Skip to content

Commit

Permalink
Merge pull request #57 from revjet-qa/feature/wait-for-loaded
Browse files Browse the repository at this point in the history
Added waitForPageToLoad function
  • Loading branch information
Marketionist authored Mar 25, 2018
2 parents 07e7eb9 + 069847e commit 3b83ae3
Show file tree
Hide file tree
Showing 27 changed files with 373 additions and 85 deletions.
2 changes: 1 addition & 1 deletion .eslintrc.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"parserOptions": {
"ecmaVersion": 6,
"ecmaVersion": 2017,
"sourceType": "module"
},
"env": {
Expand Down
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@ node_modules
!.travis.yml
!.editorconfig
!.eslintrc.*
!.vscode
*.xml
.settings
downloads
*~
.idea
.vscode
errorShots/
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ dist: trusty
# http://docs.travis-ci.com/user/languages/javascript-with-nodejs/
language: node_js
node_js:
- "6"
- "7"

# http://docs.travis-ci.com/user/gui-and-headless-browsers
before_install:
Expand Down
23 changes: 23 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"cucumberautocomplete.steps": [
"src/steps/*.js"
],
"cucumberautocomplete.syncfeatures": "test/features/*feature",
"cucumberautocomplete.strictGherkinCompletion": true,
"cucumberautocomplete.smartSnippets": true,
"cucumberautocomplete.stepsInvariants": true,
"cucumberautocomplete.customParameters": [
{
"parameter":"(_r(",
"value":"("
},
{
"parameter":"${dictionaryObject}",
"value":"([a-zA-Z0-9_-]+ from [a-zA-Z0-9_-]+ dictionary|\"[^\"]*\")"
},
{
"parameter":"${pageObject}",
"value":"([a-zA-Z0-9_-]+ from [a-zA-Z0-9_-]+ page)"
}
],
}
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,9 @@
"eslint": "^4.11.0",
"http-server": "^0.10.0",
"mocha": "^4.0.1",
"wdio-cucumber-framework": "^1.0.2",
"wdio-selenium-standalone-service": "0.0.9",
"wdio-cucumber-framework": "^1.1.0",
"wdio-selenium-standalone-service": "0.0.10",
"wdio-spec-reporter": "^0.1.2",
"webdriverio": "^4.9.8"
"webdriverio": "^4.12.0"
}
}
5 changes: 5 additions & 0 deletions src/commands/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
const waitForPageToLoad = require('./wait.for.page.to.load');

module.exports = function applyCommands () {
browser.addCommand('waitForPageToLoad', waitForPageToLoad);
};
9 changes: 9 additions & 0 deletions src/commands/wait.for.page.to.load.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/* global stepsConfig */

module.exports = function waitForPageToLoad () {
/**
* Wait for page to get fully loaded
* @param {callback} callback - A callback to run
*/
return browser.waitUntil(stepsConfig.finishedLoadingConditions);
};
27 changes: 14 additions & 13 deletions src/helpers/objects.processor.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/* eslint no-param-reassign: 0 */
/* eslint no-undef: 0 */
/* global stepsConfig */

const { _r } = require('./utils');

Expand All @@ -16,17 +17,17 @@ const dictionaryObjectsParts = '^(?:([a-zA-Z0-9_-]+) from ([a-zA-Z0-9_-]+) dicti
// Todo do we need this in csp-qa
function injectInto (locator, injection) {
const lastInjectionSymbol = injection.slice(-1);
const lastLocatorSumbol = locator.slice(-1);
const lastLocatorSymbol = locator.slice(-1);

if (lastInjectionSymbol !== ']') {
// Add ']' to the end of injection only if missing (for backward compatibility)
injection += ']';
}
if (lastLocatorSumbol === ')') {
if (lastLocatorSymbol === ')') {
// If our locator ends with round brackets
return injectInto(locator.replace(/\)$/, ''), injection) + ')';
}
if (lastLocatorSumbol === ']') {
if (lastLocatorSymbol === ']') {
if (locator.match(/\[[0-9]+\]$/)) {
// Locator ends with brackets, which contains some xpath num
const nums = locator.match(/\[[0-9]+\]$/)[0];
Expand All @@ -49,21 +50,21 @@ function pageObjectGetter (str) {
const page = match[2];
const object = match[1];

if (!pages[page]) {
if (!stepsConfig.pages[page]) {
throw new Error(`"${page}" page is missing`);
}
if (!pages[page][object]) {
if (!stepsConfig.pages[page][object]) {
throw new Error(`"${object}" page object is missing for the "${page}" page`);
}
return pages[page][object];
return stepsConfig.pages[page][object];
}
throw new Error(`Unknown Page Object type for "${str}"`);
}

function getPageObject (str) {
const pageObjectGetterFunc = objectsProcessor.pageObjectGetter || pageObjectGetter;
const pageObjectGetterFunc = stepsConfig.objectsProcessor.pageObjectGetter || pageObjectGetter;
const value = pageObjectGetterFunc(str);
const idValue = value.replace(_r(regDynamicId, 'g'), id.getId());
const idValue = value.replace(_r(regDynamicId, 'g'), stepsConfig.id.idValue);
const injection = `not(ancestor-or-self::*[contains(@style,"visibility: hidden;")
or contains(@style,"display: none") or contains(@class,"x-hide-offsets")])`;
const injectedValue = injectInto(idValue, injection);
Expand All @@ -81,13 +82,13 @@ function dictionaryGetter (str) {
const dictionary = match[2];
const object = match[1];

if (!pages[dictionary]) {
if (!stepsConfig.pages[dictionary]) {
throw new Error(`"${dictionary}" page is missing`);
}
if (!pages[dictionary][object]) {
if (!stepsConfig.pages[dictionary][object]) {
throw new Error(`"${object}" page object is missing for the "${dictionary}" page`);
}
return pages[dictionary][object];
return stepsConfig.pages[dictionary][object];
}
if (match[3] !== undefined) {
return match[3];
Expand All @@ -96,9 +97,9 @@ function dictionaryGetter (str) {
}

function getDictionaryObject (str) {
const dictionaryGetterFunc = objectsProcessor.dictionaryGetter || dictionaryGetter;
const dictionaryGetterFunc = stepsConfig.objectsProcessor.dictionaryGetter || dictionaryGetter;
const value = dictionaryGetterFunc(str);
const idValue = value.replace(_r(regDynamicId, 'g'), id.getId());
const idValue = value.replace(_r(regDynamicId, 'g'), stepsConfig.id.idValue);

return idValue;
}
Expand Down
45 changes: 45 additions & 0 deletions src/steps.conf.generator.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
const defaultFinishedLoadingConditions = (loaderSelectors) => {
// Check if jQuery is present on the page and if any XMLHttpRequests (AJAX) are in progress
if (typeof $ !== 'undefined' && $.active) {
return false;
}

// Check if any loaders are still present on the page
return browser.executeAsync((selectors, done) => {
done(!selectors.some((selector) => {
return document.querySelector(selector);
}));
}, loaderSelectors).value;

};

const defaultIdGenerator = () => (new Date()).getTime();

module.exports = function ({
loaderSelectors = [], // <string>[] - array of xpath selectors, that will be used by finishedLoadingConditions.
finishedLoadingConditions = defaultFinishedLoadingConditions, // <(loaderSelectors) => <boolean>>.
pages = {}, // <{[key: string]: {[key: string]: <string>}}> - object, that contains "key" -> "Page object" pairs.
defaultIdValue = '', // Default value of steps config Id.
idGenerator = defaultIdGenerator, // <() => <string|number>> - function, that will return new generated id.
objectsProcessor = {} // objectsProcessor childs could be previded here - see objects.processor.js for more details.
}) {
const Id = class {
constructor () {
this.value = defaultIdValue || idGenerator.call(this);
}
get idValue () {
return this.value;
}
regenerate () {
this.value = idGenerator.call(this);
}
};

return {
loaderSelectors,
finishedLoadingConditions: finishedLoadingConditions.bind(this, loaderSelectors),
pages,
id: new Id(),
objectsProcessor
};
};
8 changes: 6 additions & 2 deletions src/steps/given.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,18 @@ const { _r } = require('../helpers/utils');
module.exports = function () {
defineSupportCode(({ Given }) => {

Given(_r(`^I open ${dictionaryObject}$`), (urlDictionary) => {
Given(_r(`^I open ${dictionaryObject}$`), async function async (urlDictionary) {
/**
* The URL to navigate to
* @type {String} or {DictionaryObject}
*/
const url = getDictionaryObject.call(this, urlDictionary);

browser.url(url);
try {
return await browser.url(url);
} catch (err) {
throw new Error(err);
}
});

});
Expand Down
16 changes: 12 additions & 4 deletions src/steps/then.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,24 +7,32 @@ const { _r } = require('../helpers/utils');
module.exports = function () {
defineSupportCode(({ Then }) => {

Then(_r(`^${pageObject} should be present$`), (element) => {
Then(_r(`^${pageObject} should be present$`), async function async (element) {
/**
* The element should be present
* @param {pageObject} element - String or "page"."object" to select the element
*/
const locator = getPageObject(element);

browser.$(locator).waitForExist();
try {
return await browser.waitForExist(locator);
} catch (err) {
throw new Error(err);
}
});

Then(_r(`^${pageObject} should not be present$`), (element) => {
Then(_r(`^${pageObject} should not be present$`), async function async (element) {
/**
* The element should not be present
* @param {pageObject} element - String or "page"."object" to select the element
*/
const locator = getPageObject(element);

browser.$(locator).waitForExist(null, true);
try {
return await browser.waitForExist(locator, null, true);
} catch (err) {
throw new Error(err);
}
});

});
Expand Down
61 changes: 53 additions & 8 deletions src/steps/when.js
Original file line number Diff line number Diff line change
@@ -1,36 +1,81 @@
/* eslint new-cap: 0 */

const { defineSupportCode } = require('cucumber');
const { dictionaryObject, getDictionaryObject } = require('../helpers/objects.processor');
const { pageObject, getPageObject, dictionaryObject, getDictionaryObject } = require('../helpers/objects.processor');
const { _r, getInteger } = require('../helpers/utils');


module.exports = function () {
defineSupportCode(({ When }) => {

When(_r(`I wait ${dictionaryObject} ms$`), (timeObject) => {
When(_r(`I click ${pageObject}$`), async function async (element) {
/**
* Click on the element
* @param {pageObject} element - String or "page"."object" to select the element
*/
const locator = getPageObject(element);

try {
await browser.waitForExist(locator);
await browser.waitForPageToLoad();
await browser.moveToObject(locator);
return await browser.click(locator);
} catch (err) {
throw new Error(err);
}
});

When(_r(`I doubleclick ${pageObject}$`), async function async (element) {
/**
* Double-click on the element
* @param {pageObject} element - String or "page"."object" to select the element
*/
const locator = getPageObject(element);

try {
await browser.waitForExist(locator);
await browser.waitForPageToLoad();
await browser.moveToObject(locator);
return await browser.doubleClick(locator);
} catch (err) {
throw new Error(err);
}
});

When(_r(`I wait ${dictionaryObject} ms$`), async function async (timeObject) {
/**
* Wait for specified amount of milliseconds
* @param {String} timeObject - String with specified amount of milliseconds
*/
const timeValue = getDictionaryObject.call(this, timeObject);
const time = getInteger(timeValue);

browser.pause(time);
try {
return await browser.pause(time);
} catch (err) {
throw new Error(err);
}
});

When(_r('I close current tab$'), () => {
When(_r('I close current tab$'), async function async () {
/**
* Close current tab
*/
browser.close();
try {
return await browser.close();
} catch (err) {
throw new Error(err);
}
});

When(_r('I open new window$'), () => {
When(_r('I open new window$'), async function async () {
/**
* Open default empty ('about:blank') tab
*/
browser.newWindow('about:blank');
try {
return await browser.newWindow('about:blank');
} catch (err) {
throw new Error(err);
}
});

});
Expand Down
Binary file added test/demo-app/images/loader1.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added test/demo-app/images/loader2.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit 3b83ae3

Please sign in to comment.