/* eslint no-invalid-this: "off" */ 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; }, parsedUrl: async function() { return new URL(await this.url()); }, waitUntilNotPresent: async function(selector) { await this.wait(selector => { return document.querySelector(selector) == null; }, selector); }, changeLanguageToEnglish: async function() { let langSelector = '.user-popover vn-autocomplete[ng-model="$ctrl.lang"]'; await this.waitToClick('#user'); await this.wait(langSelector); let lang = await this.waitToGetProperty(`${langSelector} input`, 'value'); if (lang !== 'English') await this.autocompleteSearch(langSelector, 'English'); await this.keyboard.press('Escape'); await this.waitForSelector(langSelector, {hidden: true}); }, doLogin: async function(userName, password = 'nightmare') { await this.wait(`vn-login [ng-model="$ctrl.user"]`); await this.clearInput(`vn-login [ng-model="$ctrl.user"]`); await this.write(`vn-login [ng-model="$ctrl.user"]`, userName); await this.clearInput(`vn-login [ng-model="$ctrl.password"]`); await this.write(`vn-login [ng-model="$ctrl.password"]`, password); await this.click('vn-login button[type=submit]'); }, login: async function(userName) { try { await this.waitForURL('#!/login'); } catch (e) { await this.goto(`${defaultURL}/#!/login`); } await this.doLogin(userName); await this.wait(() => { return document.location.hash === '#!/'; }, {}); await this.changeLanguageToEnglish(); }, 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.waitForURL(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.wait(selector); await this.input.evaluate(inputSelector => { return document.querySelector(inputSelector).value = ''; }, selector); }, clearInput: async function(selector) { await this.wait(selector); 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.wait((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); }, waitPropertyValue: async function(selector, property, status) { await this.wait(selector); return await this.wait((selector, property, status) => { const element = document.querySelector(selector); return element[property] === status; }, {}, selector, property, status); }, waitToGetProperty: async function(selector, property) { await this.wait((selector, property) => { const element = document.querySelector(selector); return element && element[property] != null && element[property] !== ''; }, {}, selector, property); return await this.getProperty(selector, property); }, write: async function(selector, text) { await this.waitForSelector(selector, {}); await this.type(`${selector} input`, text); await this.waitForTextInInput(selector, text); }, waitToClick: async function(selector) { await this.wait(selector); await this.click(selector); }, 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.wait((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.wait(selector); return await this.wait((selector, text) => { return document.querySelector(selector).innerText.toLowerCase().includes(text.toLowerCase()); }, {}, selector, text); }, waitForTextInInput: async function(selector, text) { await this.wait(selector); return await this.wait((selector, text) => { return document.querySelector(`${selector} input`).value.toLowerCase().includes(text.toLowerCase()); }, {}, selector, text); }, waitForInnerText: async function(selector) { await this.wait(selector); await this.wait(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); }, waitForURL: async function(hashURL) { await this.waitForFunction(expectedHash => { return document.location.hash.includes(expectedHash); }, {}, hashURL); }, hideSnackbar: async function() { await this.waitToClick('#shapes .shown button'); }, waitForLastShape: async function(selector) { await this.wait(selector); let snackBarText = await this.evaluate(selector => { const shape = document.querySelector(selector); return shape.innerText; }, selector); await this.hideSnackbar(); return snackBarText; }, waitForLastSnackbar: async function() { await this.wait(2000); // this needs a refactor to be somehow dynamic ie: page.waitForResponse(urlOrPredicate[, options]) or something to fire waitForLastShape once the request is completed await this.waitForSpinnerLoad(); return await this.waitForLastShape('vn-snackbar .shown .text'); }, 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); return await this.waitToClick('ui-view vn-card a'); }, accessToSection: async function(sectionRoute) { await this.wait(`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']}); }, autocompleteSearch: async function(selector, searchValue) { try { await this.waitToClick(`${selector} input`), await this.waitForSelector(selector => { document .querySelector(`${selector} vn-drop-down`).$ctrl.content .querySelectorAll('li'); }, selector); await this.write(`.vn-drop-down.shown`, searchValue), await this.waitForFunction(selector => { return document .querySelector(`${selector} vn-drop-down`).$ctrl.content .querySelector('li.active'); }, {}, selector); await this.keyboard.press('Enter'); await this.waitForFunction((selector, searchValue) => { return document.querySelector(`${selector} input`).value.toLowerCase() .includes(searchValue.toLowerCase()); }, {}, selector, searchValue); } catch (error) { throw new Error(`${selector} failed to autocomplete ${searchValue}! ${error}`); } await this.waitForMutation(`.vn-drop-down`, 'childList'); }, reloadSection: async function(sectionRoute) { await this.evaluate(() => { let summayButton = document.querySelector('vn-icon[icon="desktop_windows"]'); summayButton.scrollIntoViewIfNeeded(); return summayButton.click(); }); await this.wait('vn-card.summary'); await this.waitToClick(`vn-left-menu li > a[ui-sref="${sectionRoute}"]`); }, 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.wait(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); }, waitForContentLoaded: async function() { await this.evaluate(() => { return new Promise(resolve => { const $rootScope = angular.element(document.body).injector().get('$rootScope'); $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;