/* eslint no-invalid-this: "off" */ import Nightmare from 'nightmare'; import {URL} from 'url'; import config from './config.js'; let currentUser; let actions = { clearTextarea: function(selector, done) { this.wait(selector) .evaluate(inputSelector => { return document.querySelector(inputSelector).value = ''; }, selector) .then(done) .catch(done); }, clearInput: function(selector, done) { this.wait(selector) .evaluate(inputSelector => { return document.querySelector(inputSelector).closest('*[model], *[field], *[value]').$ctrl.value = ''; }, selector) .then(done) .catch(done); }, login: function(userName, done) { if (currentUser) this.waitToClick('#logout'); let doLogin = () => { this.wait(`vn-login input[name=user]`) .write(`vn-login input[name=user]`, userName) .write(`vn-login input[name=password]`, 'nightmare') .click(`vn-login input[type=submit]`) .then(() => { currentUser = userName; done(); }) .catch(done); }; this.waitForURL('#!/login') .then(doLogin) .catch(() => { this.goto(`${config.url}/#!/login`) .then(doLogin) .catch(done); }); }, waitForLogin: function(userName, done) { if (currentUser === userName) { return this.waitToClick('vn-topbar a[ui-sref="home"]') .waitForURL('#!/') .changeLanguageToEnglish() .then(done) .catch(done); } return this.login(userName) .waitForURL('#!/') .url() .changeLanguageToEnglish() .then(done) .catch(done); }, resetLogin: function(done) { this.then(() => { currentUser = undefined; done(); }) .catch(done); }, changeLanguageToEnglish: function(done) { let langSelector = 'vn-user-configuration-popover vn-autocomplete[field="$ctrl.lang"]'; this.waitToClick('#user') .wait(langSelector) .waitToGetProperty(`${langSelector} input`, 'value') .then(lang => { if (lang === 'English') { this.then(done) .catch(done); } else { this.autocompleteSearch(langSelector, 'English') .then(done) .catch(done); } }); }, selectModule: function(moduleName, done) { let snakeName = moduleName.replace(/[\w]([A-Z])/g, m => { return m[0] + '-' + m[1]; }).toLowerCase(); this.waitToClick(`vn-home a[ui-sref="${moduleName}.index"]`) .waitForURL(snakeName) .then(done) .catch(done); }, loginAndModule: function(userName, moduleName, done) { this.waitForLogin(userName) .selectModule(moduleName) .then(done) .catch(done); }, parsedUrl: function(done) { this.url() .then(url => { done(null, new URL(url)); }).catch(done); }, getProperty: function(selector, property, done) { this.evaluate_now((selector, property) => { return document.querySelector(selector)[property].replace(/\s+/g, ' ').trim(); }, done, selector, property); }, waitPropertyLength: function(selector, property, minLength, done) { this.wait((selector, property, minLength) => { const element = document.querySelector(selector); return element && element[property] != null && element[property] !== '' && element[property].length >= minLength; }, selector, property, minLength) .getProperty(selector, property) .then(result => done(null, result), done); }, waitPropertyValue: function(selector, property, status, done) { this.wait(selector) .wait((selector, property, status) => { const element = document.querySelector(selector); return element[property] === status; }, selector, property, status) .then(done) .catch(done); }, waitToGetProperty: function(selector, property, done) { this.wait((selector, property) => { const element = document.querySelector(selector); return element && element[property] != null && element[property] !== ''; }, selector, property) .getProperty(selector, property) .then(result => done(null, result), done); }, write: function(selector, text, done) { this.wait(selector) .type(selector, text) .then(done) .catch(done); }, waitToClick: function(selector, done) { this.wait(selector) .click(selector) .then(done) .catch(done); }, focusElement: function(selector, done) { this.wait(selector) .evaluate_now(elemenetSelector => { let element = document.querySelector(elemenetSelector); element.focus(); }, done, selector) .then(done) .catch(done); }, isVisible: function(selector, done) { this.wait(selector) .evaluate_now(elementSelector => { const selectorMatches = document.querySelectorAll(elementSelector); const element = selectorMatches[0]; if (selectorMatches.length > 1) throw new Error(`multiple matches of ${elementSelector} found`); let isVisible = false; if (element) { const eventHandler = event => { event.preventDefault(); isVisible = true; }; element.addEventListener('mouseover', eventHandler); const elementBoundaries = element.getBoundingClientRect(); const x = elementBoundaries.left + element.offsetWidth / 2; const y = elementBoundaries.top + element.offsetHeight / 2; const elementInCenter = document.elementFromPoint(x, y); const elementInTopLeft = document.elementFromPoint(elementBoundaries.left, elementBoundaries.top); const elementInBottomRight = document.elementFromPoint(elementBoundaries.right, elementBoundaries.bottom); const 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; }, done, selector); }, waitImgLoad: function(selector, done) { this.wait(selector) .wait(selector => { const imageReady = document.querySelector(selector).complete; return imageReady; }, selector) .then(done) .catch(() => { done(new Error(`image ${selector}, load timed out`)); }); }, clickIfVisible: function(selector, done) { this.wait(selector) .isVisible(selector) .then(visible => { if (visible) return this.click(selector); throw new Error(`invisible selector: ${selector}`); }) .then(done) .catch(done); }, countElement: function(selector, done) { this.evaluate_now(selector => { return document.querySelectorAll(selector).length; }, done, selector); }, waitForNumberOfElements: function(selector, count, done) { this.wait((selector, count) => { return document.querySelectorAll(selector).length === count; }, selector, count) .then(done) .catch(() => { done(new Error(`.waitForNumberOfElements() for ${selector}, count ${count} timed out`)); }); }, waitForClassNotPresent: function(selector, className, done) { this.wait(selector) .wait((selector, className) => { if (!document.querySelector(selector).classList.contains(className)) return true; }, selector, className) .then(done) .catch(() => { done(new Error(`.waitForClassNotPresent() for ${selector}, class ${className} timed out`)); }); }, waitForClassPresent: function(selector, className, done) { this.wait(selector) .wait((elementSelector, targetClass) => { if (document.querySelector(elementSelector).classList.contains(targetClass)) return true; }, selector, className) .then(done) .catch(() => { done(new Error(`.waitForClassPresent() for ${selector}, class ${className} timed out`)); }); }, waitForTextInElement: function(selector, text, done) { this.wait(selector) .wait((selector, text) => { return document.querySelector(selector).innerText.toLowerCase().includes(text.toLowerCase()); }, selector, text) .then(done) .catch(done); }, waitForTextInInput: function(selector, text, done) { this.wait(selector) .wait((selector, text) => { return document.querySelector(selector).value.toLowerCase().includes(text.toLowerCase()); }, selector, text) .then(done) .catch(done); }, waitForInnerText: function(selector, done) { this.wait(selector) .wait(selector => { const innerText = document.querySelector(selector).innerText; return innerText != null && innerText != ''; }, selector) .evaluate_now(selector => { return document.querySelector(selector).innerText; }, done, selector); }, waitForEmptyInnerText: function(selector, done) { this.wait(selector => { return document.querySelector(selector).innerText == ''; }, selector) .then(done) .catch(done); }, waitForURL: function(hashURL, done) { this.wait(hash => { return document.location.hash.includes(hash); }, hashURL) .then(done) .catch(done); }, waitForShapes: function(selector, done) { this.wait(selector) .evaluate_now(selector => { const shapes = document.querySelectorAll(selector); const shapesList = []; for (const shape of shapes) shapesList.push(shape.innerText); return shapesList; }, done, selector); }, waitForSnackbar: function(done) { this.wait(500).waitForShapes('vn-snackbar .shape .text') .then(shapes => { done(null, shapes); }).catch(done); }, waitForLastShape: function(selector, done) { this.wait(selector) .evaluate_now(selector => { const shape = document.querySelector(selector); return shape.innerText; }, done, selector); }, waitForLastSnackbar: function(done) { this.wait(500).waitForLastShape('vn-snackbar .shape .text') .then(shapes => { done(null, shapes); }).catch(done); }, accessToSearchResult: function(searchValue, done) { this.clearInput('vn-searchbar input') .write('vn-searchbar input', searchValue) .click('vn-searchbar vn-icon[icon="search"]') .wait(100) .waitForNumberOfElements('.searchResult', 1) .evaluate(() => { return document.querySelector('ui-view vn-card vn-table') != null; }) .then(result => { if (result) return this.waitToClick('ui-view vn-card vn-td'); return this.waitToClick('ui-view vn-card a'); }) .then(done) .catch(done); }, accessToSection: function(sectionRoute, done) { this.wait(`vn-left-menu`) .evaluate(sectionRoute => { return document.querySelector(`vn-left-menu ul li ul li > a[ui-sref="${sectionRoute}"]`) != null; }, sectionRoute) .then(nested => { if (!nested) return this.waitToClick(`vn-left-menu li > a[ui-sref="${sectionRoute}"]`); return this.waitToClick('vn-left-menu .collapsed') .wait('vn-left-menu .expanded') .waitToClick(`vn-left-menu li > a[ui-sref="${sectionRoute}"]`); }) .then(done) .catch(done); }, autocompleteSearch: function(autocompleteSelector, searchValue, done) { this.wait(`${autocompleteSelector} input`) .waitToClick(`${autocompleteSelector} input`) .write(`${autocompleteSelector} vn-drop-down input`, searchValue) .waitToClick(`${autocompleteSelector} li.active`) .wait((autocompleteSelector, searchValue) => { return document.querySelector(`${autocompleteSelector} input`).value.toLowerCase().includes(searchValue.toLowerCase()); }, autocompleteSelector, searchValue) .then(done) .catch(() => { done(new Error(`.autocompleteSearch() for ${autocompleteSelector}, timed out`)); }); }, datePicker: function(datePickerSelector, changeMonth, day, done) { this.wait(datePickerSelector) .mousedown(datePickerSelector) .wait('div.flatpickr-calendar.open'); if (changeMonth > 0) this.mousedown('body > div.flatpickr-calendar.open > div.flatpickr-months > span.flatpickr-next-month > svg'); if (changeMonth < 0) this.mousedown('body > div.flatpickr-calendar.open > div.flatpickr-months > span.flatpickr-prev-month > svg'); let daySelector; if (!day) daySelector = 'div.flatpickr-calendar.open span.flatpickr-day:nth-child(16)'; if (day) daySelector = `span.flatpickr-day[aria-label~="${day},"]:not(.prevMonthDay):not(.nextMonthDay)`; this.wait(selector => { return document.querySelector(selector); }, daySelector) .evaluate(selector => { let event = new MouseEvent('mousedown', { bubbles: true, cancelable: true, view: window }); document.querySelector(selector).dispatchEvent(event); }, daySelector) .then(done) .catch(() => { done(new Error(`.datePicker(), for ${daySelector} timed out`)); }); }, reloadSection: function(sectionRoute, done) { this.waitToClick('vn-icon[icon="desktop_windows"]') .wait('vn-card.summary') .waitToClick(`vn-left-menu li > a[ui-sref="${sectionRoute}"]`) .then(done) .catch(done); }, forceReloadSection: function(sectionRoute, done) { this.waitToClick('vn-icon[icon="desktop_windows"]') .waitToClick('button[response="ACCEPT"]') .wait('vn-card.summary') .waitToClick(`vn-left-menu li > a[ui-sref="${sectionRoute}"]`) .then(done) .catch(done); }, checkboxState: function(selector, done) { this.wait(selector) .evaluate(checkboxSelector => { return document.querySelector(checkboxSelector).getAttribute('class'); }, selector) .then(elementClass => { let classList = elementClass.split(' '); let className; if (classList.includes('md-checked')) className = 'checked'; if (classList.includes('md-intermediate')) className = 'intermediate'; if (!classList.includes('md-intermediate') && !classList.includes('md-checked')) className = 'unchecked'; if (!className) throw new Error(`selector: ${selector} is not a md-checkbox`); done(null, className); }) .then(done) .catch(done); }, waitForSpinnerLoad: function(done) { let spinnerSelector = 'vn-spinner > div'; this.waitForClassNotPresent(spinnerSelector, 'is-active') .then(done) .catch(done); }, }; Object.keys(actions).forEach(function(name) { let fn = actions[name]; Nightmare.action(name, fn); });