import {url as defaultURL} from './config'; let actions = { clickIfExists: async function(selector) { let exists; try { exists = await this.waitForSelector(selector, {timeout: 500}); } catch (error) { exists = false; } if (exists) await this.waitToClick(selector); return exists; }, expectURL: async function(expectedHash) { try { await this.waitForFunction(expectedHash => { return document.location.hash.includes(expectedHash); }, {}, expectedHash); } catch (error) { throw new Error(`failed to reach URL containing: ${expectedHash}`); } await this.waitForContentLoaded(); return true; }, waitUntilNotPresent: async function(selector) { await this.wait(selector => { return document.querySelector(selector) == null; }, selector); }, doLogin: async function(userName, password = 'nightmare') { await this.waitForSelector(`vn-login vn-textfield[ng-model="$ctrl.user"]`, {visible: true}); await this.clearInput(`vn-login vn-textfield[ng-model="$ctrl.user"]`); await this.write(`vn-login vn-textfield[ng-model="$ctrl.user"]`, userName); await this.clearInput(`vn-login vn-textfield[ng-model="$ctrl.password"]`); await this.write(`vn-login vn-textfield[ng-model="$ctrl.password"]`, password); await this.waitToClick('vn-login button[type=submit]'); }, login: async function(userName) { await this.goto(`${defaultURL}/#!/login`); let dialog = await this.evaluate(() => { return document.querySelector('button[response="accept"]'); }); if (dialog) await this.waitToClick('button[response="accept"]'); await this.doLogin(userName); await this.waitForFunction(() => { return document.location.hash === '#!/'; }, {}); }, selectModule: async function(moduleName) { let snakeName = moduleName.replace(/[\w]([A-Z])/g, m => { return m[0] + '-' + m[1]; }).toLowerCase(); let selector = `vn-home a[ui-sref="${moduleName}.index"]`; await this.waitToClick(selector); await this.expectURL(snakeName); }, loginAndModule: async function(userName, moduleName) { await this.login(userName); await this.selectModule(moduleName); }, datePicker: async function(selector, changeMonth, day) { let date = new Date(); if (changeMonth) date.setMonth(date.getMonth() + changeMonth); date.setDate(day ? day : 16); date = date.toISOString().substr(0, 10); await this.wait(selector); await this.evaluate((selector, date) => { let input = document.querySelector(selector).$ctrl.input; input.value = date; input.dispatchEvent(new Event('change')); }, selector, date); }, pickTime: async function(selector, time) { await this.wait(selector); await this.evaluate((selector, time) => { let input = document.querySelector(selector).$ctrl.input; input.value = time; input.dispatchEvent(new Event('change')); }, selector, time); }, clearTextarea: async function(selector) { await this.waitForSelector(selector, {visible: true}); await this.evaluate(inputSelector => { return document.querySelector(`${inputSelector} textarea`).value = ''; }, selector); }, clearInput: async function(selector) { await this.waitForSelector(selector, {visible: true}); let field = await this.evaluate(selector => { return document.querySelector(`${selector} input`).closest('.vn-field').$ctrl.field; }, selector); if ((field != null && field != '') || field == '0') { let coords = await this.evaluate(selector => { let rect = document.querySelector(selector).getBoundingClientRect(); return {x: rect.x + (rect.width / 2), y: rect.y + (rect.height / 2), width: rect.width}; }, selector); await this.mouse.move(coords.x, coords.y); await this.waitForSelector(`${selector} [icon="clear"]`, {visible: true}); await this.waitToClick(`${selector} [icon="clear"]`); } await this.evaluate(selector => { return document.querySelector(`${selector} input`).closest('.vn-field').$ctrl.field == ''; }, selector); }, getProperty: async function(selector, property) { return await this.evaluate((selector, property) => { return document.querySelector(selector)[property].replace(/\s+/g, ' ').trim(); }, selector, property); }, waitPropertyLength: async function(selector, property, minLength) { await this.waitForFunction((selector, property, minLength) => { const element = document.querySelector(selector); return element && element[property] != null && element[property] !== '' && element[property].length >= minLength; }, {}, selector, property, minLength); return await this.getProperty(selector, property); }, expectPropertyValue: async function(selector, property, value) { let builtSelector = selector; if (property != 'innerText') builtSelector = await this.selectorFormater(selector); try { return await this.waitForFunction((selector, property, value) => { const element = document.querySelector(selector); return element[property] == value; }, {}, builtSelector, property, value); } catch (error) { throw new Error(`${value} wasn't the value of ${builtSelector}, ${error}`); } }, waitToGetProperty: async function(selector, property) { let builtSelector = selector; if (selector.includes('vn-input-file') || property != 'innerText') builtSelector = await this.selectorFormater(selector); try { await this.waitForFunction((selector, property) => { const element = document.querySelector(selector); return element && element[property] != null && element[property] !== ''; }, {}, builtSelector, property); return await this.getProperty(builtSelector, property); } catch (error) { throw new Error(`couldn't get property: ${property} of ${builtSelector}, ${error}`); } }, write: async function(selector, text) { let builtSelector = await this.selectorFormater(selector); await this.waitForSelector(selector, {}); await this.type(builtSelector, text); await this.waitForTextInField(selector, text); }, waitToClick: async function(selector) { await this.waitForSelector(selector); await this.click(selector); }, writeOnEditableTD: async function(selector, text) { let builtSelector = await this.selectorFormater(selector); await this.waitToClick(selector); await this.type(builtSelector, text); await this.keyboard.press('Enter'); }, focusElement: async function(selector) { await this.wait(selector); return await this.evaluate(selector => { let element = document.querySelector(selector); element.focus(); }, selector); }, isVisible: async function(selector) { await this.wait(selector); return await this.evaluate(elementSelector => { let selectorMatches = document.querySelectorAll(elementSelector); let element = selectorMatches[0]; if (selectorMatches.length > 1) throw new Error(`Multiple matches of ${elementSelector} found`); let isVisible = false; if (element) { let eventHandler = event => { event.preventDefault(); isVisible = true; }; element.addEventListener('mouseover', eventHandler); let rect = element.getBoundingClientRect(); let x = rect.left + rect.width / 2; let y = rect.top + rect.height / 2; let elementInCenter = document.elementFromPoint(x, y); let elementInTopLeft = document.elementFromPoint(rect.left, rect.top); let elementInBottomRight = document.elementFromPoint(rect.right, rect.bottom); let e = new MouseEvent('mouseover', { view: window, bubbles: true, cancelable: true, }); if (elementInCenter) elementInCenter.dispatchEvent(e); if (elementInTopLeft) elementInTopLeft.dispatchEvent(e); if (elementInBottomRight) elementInBottomRight.dispatchEvent(e); element.removeEventListener('mouseover', eventHandler); } return isVisible; }, selector); }, waitImgLoad: async function(selector) { await this.wait(selector); return await this.wait(selector => { const imageReady = document.querySelector(selector).complete; return imageReady; }, {}, selector); }, clickIfVisible: async function(selector) { await this.wait(selector); let isVisible = await this.isVisible(selector); if (isVisible) return await this.click(selector); throw new Error(`invisible selector: ${selector}`); }, countElement: async function(selector) { return await this.evaluate(selector => { return document.querySelectorAll(selector).length; }, selector); }, waitForNumberOfElements: async function(selector, count) { return await this.waitForFunction((selector, count) => { return document.querySelectorAll(selector).length == count; }, {}, selector, count); }, waitForClassNotPresent: async function(selector, className) { await this.wait(selector); return await this.wait((selector, className) => { if (!document.querySelector(selector).classList.contains(className)) return true; }, {}, selector, className); }, waitForClassPresent: async function(selector, className) { await this.wait(selector); return await this.wait((elementSelector, targetClass) => { if (document.querySelector(elementSelector).classList.contains(targetClass)) return true; }, {}, selector, className); }, waitForTextInElement: async function(selector, text) { await this.waitForSelector(selector); return await this.waitForFunction((selector, text) => { return document.querySelector(selector).innerText.toLowerCase().includes(text.toLowerCase()); }, {}, selector, text); }, selectorFormater: function(selector) { if (selector.includes('vn-textarea')) return `${selector} textarea`; if (selector.includes('vn-input-file')) return `${selector} section`; return `${selector} input`; }, waitForTextInField: async function(selector, text) { let builtSelector = await this.selectorFormater(selector); await this.waitForSelector(builtSelector); return await this.waitForFunction((selector, text) => { return document.querySelector(selector).value.toLowerCase().includes(text.toLowerCase()); }, {}, builtSelector, text); }, waitForInnerText: async function(selector) { await this.waitForSelector(selector, {}); await this.waitForFunction(selector => { const innerText = document.querySelector(selector).innerText; return innerText != null && innerText != ''; }, {}, selector); return await this.evaluate(selector => { return document.querySelector(selector).innerText; }, selector); }, waitForEmptyInnerText: async function(selector) { return await this.wait(selector => { return document.querySelector(selector).innerText == ''; }, selector); }, hideSnackbar: async function() { await this.waitFor(750); // holds up for the snackbar to be visible for a small period of time. await this.evaluate(() => { let hideButton = document.querySelector('#shapes .shown button'); if (hideButton) return document.querySelector('#shapes .shown button').click(); }); }, waitForLastSnackbar: async function() { const selector = 'vn-snackbar .shown .text'; await this.waitForSelector(selector); let snackBarText = await this.evaluate(selector => { const shape = document.querySelector(selector); return shape.innerText; }, selector); await this.hideSnackbar(); return snackBarText; }, accessToSearchResult: async function(searchValue) { await this.clearInput('vn-searchbar'); await this.write('vn-searchbar', searchValue); await this.waitToClick('vn-searchbar vn-icon[icon="search"]'); await this.waitForNumberOfElements('.search-result', 1); await this.waitForContentLoaded(); await this.evaluate(() => { return document.querySelector('.search-result').click(); }); await this.waitForContentLoaded(); }, accessToSection: async function(sectionRoute) { await this.waitForSelector('vn-left-menu'); let nested = await this.evaluate(sectionRoute => { return document.querySelector(`vn-left-menu li li > a[ui-sref="${sectionRoute}"]`) != null; }, sectionRoute); if (nested) { await this.waitToClick('vn-left-menu vn-item-section > vn-icon[icon=keyboard_arrow_down]'); await this.wait('vn-left-menu .expanded'); } await this.evaluate(sectionRoute => { let navButton = document.querySelector(`vn-left-menu li > a[ui-sref="${sectionRoute}"]`); navButton.scrollIntoViewIfNeeded(); return navButton.click(); }, sectionRoute); await this.waitForNavigation({waitUntil: ['networkidle0']}); await this.waitForContentLoaded(); }, autocompleteSearch: async function(selector, searchValue) { let builtSelector = await this.selectorFormater(selector); await this.waitForContentLoaded(); await this.waitToClick(selector); await this.waitForSelector(selector => { document .querySelector(`${selector} vn-drop-down`).$ctrl.content .querySelectorAll('li'); }, selector); await this.write('.vn-drop-down.vn-popover.vn-popup.shown vn-textfield', searchValue); try { await this.waitForFunction((selector, searchValue) => { let element = document .querySelector(`${selector} vn-drop-down`).$ctrl.content .querySelector('li.active'); if (element) return element.innerText.toLowerCase().includes(searchValue.toLowerCase()); }, {}, selector, searchValue); } catch (error) { let inputValue = await this.evaluate(() => { return document.querySelector('.vn-drop-down.vn-popover.vn-popup.shown vn-textfield input').value; }); throw new Error(`${builtSelector} value is ${inputValue}! ${error}`); } await this.keyboard.press('Enter'); await this.waitForFunction((selector, searchValue) => { return document.querySelector(selector).value.toLowerCase() .includes(searchValue.toLowerCase()); }, {}, builtSelector, searchValue); await this.waitForMutation('.vn-drop-down', 'childList'); await this.waitForContentLoaded(); }, reloadSection: async function(sectionRoute) { await this.waitForContentLoaded(); await Promise.all([ this.waitForNavigation({waitUntil: 'networkidle0'}), this.click('vn-icon[icon="desktop_windows"]', {}), ]); await Promise.all([ this.waitForNavigation({waitUntil: 'networkidle0'}), this.click(`vn-left-menu li > a[ui-sref="${sectionRoute}"]`, {}), ]); await this.waitForContentLoaded(); }, forceReloadSection: async function(sectionRoute) { await this.waitToClick('vn-icon[icon="desktop_windows"]'); await this.waitToClick('button[response="accept"]'); await this.wait('vn-card.summary'); await this.waitToClick(`vn-left-menu li > a[ui-sref="${sectionRoute}"]`); }, checkboxState: async function(selector) { await this.wait(selector); return await this.evaluate(selector => { let checkbox = document.querySelector(selector); switch (checkbox.$ctrl.field) { case null: return 'intermediate'; case true: return 'checked'; default: return 'unchecked'; } }, selector); }, isDisabled: async function(selector) { await this.waitForSelector(selector); return await this.evaluate(selector => { let element = document.querySelector(selector); return element.$ctrl.disabled; }, selector); }, waitForStylePresent: async function(selector, property, value) { return await this.wait((selector, property, value) => { const element = document.querySelector(selector); return element.style[property] == value; }, {}, selector, property, value); }, waitForSpinnerLoad: async function() { await this.waitUntilNotPresent('vn-topbar vn-spinner'); }, waitForWatcherData: async function(selector) { await this.wait(selector); await this.wait(selector => { let watcher = document.querySelector(selector); let orgData = watcher.$ctrl.orgData; return !angular.equals({}, orgData) && orgData != null; }, {}, selector); await this.waitForSpinnerLoad(); }, waitForMutation: async function(selector, type) { try { await this.evaluate((selector, type) => { return new Promise(resolve => { const config = {attributes: true, childList: true, subtree: true}; const target = document.querySelector(selector); const onEnd = function(mutationsList, observer) { resolve(); observer.disconnect(); }; const observer = new MutationObserver(onEnd); observer.expectedType = type; observer.observe(target, config); }); }, selector, type); } catch (error) { throw new Error(`failed to wait for mutation type: ${type}`); } }, waitForTransitionEnd: async function(selector) { await this.evaluate(selector => { return new Promise(resolve => { const transition = document.querySelector(selector); const onEnd = function() { transition.removeEventListener('transitionend', onEnd); resolve(); }; transition.addEventListener('transitionend', onEnd); }); }, selector); }, closePopup: async function(selector) { await Promise.all([ this.keyboard.press('Escape'), this.waitForSelector('.vn-popup', {hidden: true}), ]); }, waitForContentLoaded: async function() { await this.waitFor(1000); // to be implemented in base of a directive loaded once al modules are done loading, further investigation required. // await this.waitForFunction(() => { // return new Promise(resolve => { // angular.element(document).ready(() => resolve()); // const $rootScope = angular.element(document).find('ui-view').injector().get('$rootScope'); // $rootScope.$$postDigest(resolve()); // $rootScope.$on('$viewContentLoaded', resolve()); // }); // }); } }; export function extendPage(page) { for (let name in actions) { page[name] = async(...args) => { return await actions[name].call(page, ...args); }; } page.wait = page.waitFor; return page; } export default actions;