codeceptjs 2.6.7 → 2.6.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +33 -6
- package/README.md +11 -11
- package/docs/advanced.md +21 -0
- package/docs/basics.md +6 -5
- package/docs/bdd.md +1 -2
- package/docs/books.md +1 -1
- package/docs/build/Appium.js +1 -2
- package/docs/build/FileSystem.js +3 -3
- package/docs/build/Mochawesome.js +1 -1
- package/docs/build/Nightmare.js +81 -5
- package/docs/build/Playwright.js +100 -17
- package/docs/build/Protractor.js +34 -2
- package/docs/build/Puppeteer.js +59 -2
- package/docs/build/TestCafe.js +23 -0
- package/docs/build/WebDriver.js +62 -16
- package/docs/changelog.md +152 -125
- package/docs/community-helpers.md +7 -3
- package/docs/configuration.md +1 -1
- package/docs/custom-helpers.md +2 -2
- package/docs/data.md +1 -1
- package/docs/detox.md +2 -2
- package/docs/email.md +1 -1
- package/docs/examples.md +12 -2
- package/docs/helpers/Appium.md +24 -5
- package/docs/helpers/Nightmare.md +42 -0
- package/docs/helpers/Playwright.md +41 -4
- package/docs/helpers/Protractor.md +14 -0
- package/docs/helpers/Puppeteer.md +38 -1
- package/docs/helpers/TestCafe.md +14 -0
- package/docs/helpers/WebDriver.md +24 -5
- package/docs/hooks.md +14 -14
- package/docs/locators.md +1 -1
- package/docs/playwright.md +13 -0
- package/docs/translation.md +21 -1
- package/docs/ui.md +2 -2
- package/docs/videos.md +4 -4
- package/docs/webapi/saveElementScreenshot.mustache +9 -0
- package/docs/webapi/type.mustache +11 -6
- package/docs/wiki/{Community-Helpers.md → Community-Helpers-&-Plugins.md} +6 -2
- package/docs/wiki/Examples.md +11 -1
- package/docs/wiki/Google-Summer-of-Code-(GSoC)-2020.md +68 -0
- package/docs/wiki/Home.md +9 -4
- package/docs/wiki/Release-Process.md +24 -0
- package/docs/wiki/Tests.md +1391 -0
- package/docs/wiki/Upgrading-to-CodeceptJS-3.md +153 -0
- package/docs/wiki/Videos.md +3 -3
- package/lib/actor.js +1 -1
- package/lib/assert/empty.js +1 -1
- package/lib/assert/equal.js +1 -1
- package/lib/assert/include.js +1 -1
- package/lib/assert/truth.js +1 -1
- package/lib/codecept.js +2 -3
- package/lib/command/configMigrate.js +3 -5
- package/lib/command/definitions.js +1 -2
- package/lib/command/dryRun.js +1 -2
- package/lib/command/gherkin/init.js +1 -1
- package/lib/command/gherkin/snippets.js +3 -3
- package/lib/command/gherkin/steps.js +2 -3
- package/lib/command/info.js +1 -2
- package/lib/command/init.js +2 -2
- package/lib/command/interactive.js +1 -2
- package/lib/command/list.js +3 -4
- package/lib/command/run-multiple.js +2 -3
- package/lib/command/run-rerun.js +2 -4
- package/lib/command/run.js +1 -2
- package/lib/container.js +2 -2
- package/lib/data/context.js +1 -1
- package/lib/event.js +1 -1
- package/lib/helper/Appium.js +1 -2
- package/lib/helper/FileSystem.js +3 -3
- package/lib/helper/Mochawesome.js +1 -1
- package/lib/helper/Nightmare.js +54 -5
- package/lib/helper/Playwright.js +75 -17
- package/lib/helper/Protractor.js +26 -2
- package/lib/helper/Puppeteer.js +34 -2
- package/lib/helper/TestCafe.js +15 -0
- package/lib/helper/WebDriver.js +43 -11
- package/lib/helper/clientscripts/PollyWebDriverExt.js +1 -1
- package/lib/hooks.js +1 -2
- package/lib/interfaces/gherkin.js +0 -1
- package/lib/listener/helpers.js +1 -2
- package/lib/listener/mocha.js +0 -1
- package/lib/locator.js +2 -2
- package/lib/pause.js +1 -1
- package/lib/plugin/allure.js +1 -1
- package/lib/plugin/autoDelay.js +3 -3
- package/lib/plugin/autoLogin.js +1 -1
- package/lib/plugin/screenshotOnFail.js +2 -1
- package/lib/plugin/standardActingHelpers.js +0 -3
- package/lib/recorder.js +1 -1
- package/lib/step.js +3 -0
- package/lib/ui.js +1 -0
- package/package.json +3 -2
- package/translations/fr-FR.js +63 -0
- package/translations/index.js +5 -4
- package/typings/types.d.ts +140 -8
- package/docs/wiki/Release-process.md +0 -25
package/lib/helper/Playwright.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
const requireg = require('requireg');
|
|
2
2
|
const path = require('path');
|
|
3
3
|
const fs = require('fs');
|
|
4
|
-
const fsExtra = require('fs-extra');
|
|
5
4
|
|
|
6
5
|
const Helper = require('../helper');
|
|
7
6
|
const Locator = require('../locator');
|
|
@@ -11,7 +10,7 @@ const { urlEquals } = require('../assert/equal');
|
|
|
11
10
|
const { equals } = require('../assert/equal');
|
|
12
11
|
const { empty } = require('../assert/empty');
|
|
13
12
|
const { truth } = require('../assert/truth');
|
|
14
|
-
|
|
13
|
+
|
|
15
14
|
const {
|
|
16
15
|
xpathLocator,
|
|
17
16
|
ucfirst,
|
|
@@ -160,7 +159,7 @@ const { createValueEngine, createDisabledEngine } = require('./extras/Playwright
|
|
|
160
159
|
* }
|
|
161
160
|
* ```
|
|
162
161
|
*
|
|
163
|
-
* #### Example #6:
|
|
162
|
+
* #### Example #6: Launch tests emulating iPhone 6
|
|
164
163
|
*
|
|
165
164
|
*
|
|
166
165
|
*
|
|
@@ -234,6 +233,7 @@ class Playwright extends Helper {
|
|
|
234
233
|
keepBrowserState: false,
|
|
235
234
|
show: false,
|
|
236
235
|
defaultPopupAction: 'accept',
|
|
236
|
+
ignoreHTTPSErrors: false, // Adding it here o that context can be set up to ignore the SSL errors
|
|
237
237
|
};
|
|
238
238
|
|
|
239
239
|
config = Object.assign(defaults, config);
|
|
@@ -348,6 +348,9 @@ class Playwright extends Helper {
|
|
|
348
348
|
await this.executeScript('localStorage.clear();').catch((err) => {
|
|
349
349
|
if (!(err.message.indexOf("Storage is disabled inside 'data:' URLs.") > -1)) throw err;
|
|
350
350
|
});
|
|
351
|
+
await this.executeScript('sessionStorage.clear();').catch((err) => {
|
|
352
|
+
if (!(err.message.indexOf("Storage is disabled inside 'data:' URLs.") > -1)) throw err;
|
|
353
|
+
});
|
|
351
354
|
}
|
|
352
355
|
// await this.closeOtherTabs();
|
|
353
356
|
return this.browser;
|
|
@@ -539,7 +542,7 @@ class Playwright extends Helper {
|
|
|
539
542
|
this.browser.on('targetchanged', (target) => {
|
|
540
543
|
this.debugSection('Url', target.url());
|
|
541
544
|
});
|
|
542
|
-
this.browserContext = await this.browser.newContext({ acceptDownloads: true, ...this.options.emulate })
|
|
545
|
+
this.browserContext = await this.browser.newContext({ ignoreHTTPSErrors: this.options.ignoreHTTPSErrors, acceptDownloads: true, ...this.options.emulate });// Adding the HTTPSError ignore in the context so that we can ignore those errors
|
|
543
546
|
|
|
544
547
|
const existingPages = await this.browserContext.pages();
|
|
545
548
|
|
|
@@ -691,7 +694,7 @@ class Playwright extends Helper {
|
|
|
691
694
|
assertElementExists(els);
|
|
692
695
|
|
|
693
696
|
// Use manual mouse.move instead of .hover() so the offset can be added to the coordinates
|
|
694
|
-
const { x, y } = await els[0]
|
|
697
|
+
const { x, y } = await clickablePoint(els[0]);
|
|
695
698
|
await this.page.mouse.move(x + offsetX, y + offsetY);
|
|
696
699
|
return this._waitForAction();
|
|
697
700
|
}
|
|
@@ -747,7 +750,7 @@ class Playwright extends Helper {
|
|
|
747
750
|
const els = await this._locate(locator);
|
|
748
751
|
assertElementExists(els, locator, 'Element');
|
|
749
752
|
await els[0].scrollIntoViewIfNeeded();
|
|
750
|
-
const elementCoordinates = await els[0]
|
|
753
|
+
const elementCoordinates = await clickablePoint(els[0]);
|
|
751
754
|
await this.executeScript((offsetX, offsetY) => window.scrollBy(offsetX, offsetY), { offsetX: elementCoordinates.x + offsetX, offsetY: elementCoordinates.y + offsetY });
|
|
752
755
|
} else {
|
|
753
756
|
await this.executeScript(({ offsetX, offsetY }) => window.scrollTo(offsetX, offsetY), { offsetX, offsetY });
|
|
@@ -1141,7 +1144,7 @@ class Playwright extends Helper {
|
|
|
1141
1144
|
/**
|
|
1142
1145
|
* {{> pressKeyWithKeyNormalization }}
|
|
1143
1146
|
*
|
|
1144
|
-
* _Note:_ Shortcuts like `'Meta'` + `'A'` do not work on macOS ([GoogleChrome/
|
|
1147
|
+
* _Note:_ Shortcuts like `'Meta'` + `'A'` do not work on macOS ([GoogleChrome/Puppeteer#1313](https://github.com/GoogleChrome/puppeteer/issues/1313)).
|
|
1145
1148
|
*/
|
|
1146
1149
|
async pressKey(key) {
|
|
1147
1150
|
const modifiers = [];
|
|
@@ -1168,6 +1171,20 @@ class Playwright extends Helper {
|
|
|
1168
1171
|
return this._waitForAction();
|
|
1169
1172
|
}
|
|
1170
1173
|
|
|
1174
|
+
/**
|
|
1175
|
+
* {{> type }}
|
|
1176
|
+
*/
|
|
1177
|
+
async type(keys, delay = null) {
|
|
1178
|
+
if (!Array.isArray(keys)) {
|
|
1179
|
+
keys = keys.split('');
|
|
1180
|
+
}
|
|
1181
|
+
|
|
1182
|
+
for (const key of keys) {
|
|
1183
|
+
await this.page.keyboard.press(key);
|
|
1184
|
+
if (delay) await this.wait(delay / 1000);
|
|
1185
|
+
}
|
|
1186
|
+
}
|
|
1187
|
+
|
|
1171
1188
|
/**
|
|
1172
1189
|
* {{> fillField }}
|
|
1173
1190
|
*
|
|
@@ -1600,8 +1617,8 @@ class Playwright extends Helper {
|
|
|
1600
1617
|
const src = await this._locate(locator);
|
|
1601
1618
|
assertElementExists(src, locator, 'Slider Element');
|
|
1602
1619
|
|
|
1603
|
-
// Note: Using private api
|
|
1604
|
-
const sliderSource = await src[0]
|
|
1620
|
+
// Note: Using clickablePoint private api because the .BoundingBox does not take into account iframe offsets!
|
|
1621
|
+
const sliderSource = await clickablePoint(src[0]);
|
|
1605
1622
|
|
|
1606
1623
|
// Drag start point
|
|
1607
1624
|
await this.page.mouse.move(sliderSource.x, sliderSource.y, { steps: 5 });
|
|
@@ -1631,6 +1648,21 @@ class Playwright extends Helper {
|
|
|
1631
1648
|
return array.length === 1 ? array[0] : array;
|
|
1632
1649
|
}
|
|
1633
1650
|
|
|
1651
|
+
/**
|
|
1652
|
+
* {{> saveElementScreenshot }}
|
|
1653
|
+
*
|
|
1654
|
+
*/
|
|
1655
|
+
async saveElementScreenshot(locator, fileName) {
|
|
1656
|
+
const outputFile = screenshotOutputFolder(fileName);
|
|
1657
|
+
|
|
1658
|
+
const res = await this._locate(locator);
|
|
1659
|
+
assertElementExists(res, locator);
|
|
1660
|
+
if (res.length > 1) this.debug(`[Elements] Using first element out of ${res.length}`);
|
|
1661
|
+
const elem = res[0];
|
|
1662
|
+
this.debug(`Screenshot of ${locator} element has been saved to ${outputFile}`);
|
|
1663
|
+
return elem.screenshot({ path: outputFile, type: 'png' });
|
|
1664
|
+
}
|
|
1665
|
+
|
|
1634
1666
|
/**
|
|
1635
1667
|
* {{> saveScreenshot }}
|
|
1636
1668
|
*/
|
|
@@ -1962,7 +1994,7 @@ class Playwright extends Helper {
|
|
|
1962
1994
|
/**
|
|
1963
1995
|
* Waits for navigation to finish. By default takes configured `waitForNavigation` option.
|
|
1964
1996
|
*
|
|
1965
|
-
* See [Pupeteer's reference](https://github.com/
|
|
1997
|
+
* See [Pupeteer's reference](https://github.com/microsoft/Playwright/blob/master/docs/api.md#pagewaitfornavigationoptions)
|
|
1966
1998
|
*
|
|
1967
1999
|
* @param {*} opts
|
|
1968
2000
|
*/
|
|
@@ -2068,7 +2100,14 @@ async function proceedClick(locator, context = null, options = {}) {
|
|
|
2068
2100
|
} else {
|
|
2069
2101
|
assertElementExists(els, locator, 'Clickable element');
|
|
2070
2102
|
}
|
|
2071
|
-
|
|
2103
|
+
/*
|
|
2104
|
+
using the force true options itself but instead dispatching a click
|
|
2105
|
+
*/
|
|
2106
|
+
if (options.force) {
|
|
2107
|
+
await els[0].dispatchEvent('click');
|
|
2108
|
+
} else {
|
|
2109
|
+
await els[0].click(options);
|
|
2110
|
+
}
|
|
2072
2111
|
const promises = [];
|
|
2073
2112
|
if (options.waitForNavigation) {
|
|
2074
2113
|
promises.push(this.waitForNavigation());
|
|
@@ -2190,9 +2229,9 @@ async function proceedDragAndDrop(sourceLocator, destinationLocator, options = {
|
|
|
2190
2229
|
const dst = await this._locate(destinationLocator);
|
|
2191
2230
|
assertElementExists(dst, destinationLocator, 'Destination Element');
|
|
2192
2231
|
|
|
2193
|
-
// Note: Using private api
|
|
2194
|
-
const dragSource = await src[0]
|
|
2195
|
-
const dragDestination = await dst[0]
|
|
2232
|
+
// Note: Using clickablePoint private api becaues the .BoundingBox does not take into account iframe offsets!
|
|
2233
|
+
const dragSource = await clickablePoint(src[0]);
|
|
2234
|
+
const dragDestination = await clickablePoint(dst[0]);
|
|
2196
2235
|
|
|
2197
2236
|
// Drag start point
|
|
2198
2237
|
await this.page.mouse.move(dragSource.x, dragSource.y, { steps: 5 });
|
|
@@ -2317,10 +2356,20 @@ function $XPath(element, selector) {
|
|
|
2317
2356
|
async function targetCreatedHandler(page) {
|
|
2318
2357
|
if (!page) return;
|
|
2319
2358
|
this.withinLocator = null;
|
|
2320
|
-
page.on('load', (
|
|
2359
|
+
page.on('load', () => {
|
|
2321
2360
|
page.$('body')
|
|
2322
2361
|
.catch(() => null)
|
|
2323
|
-
.then(context =>
|
|
2362
|
+
.then(async context => {
|
|
2363
|
+
if (this.context._type === 'Frame') {
|
|
2364
|
+
// we are inside iframe?
|
|
2365
|
+
const frameEl = await this.context.frameElement();
|
|
2366
|
+
this.context = await frameEl.contentFrame();
|
|
2367
|
+
return;
|
|
2368
|
+
}
|
|
2369
|
+
// if context element was in iframe - keep it
|
|
2370
|
+
// if (await this.context.ownerFrame()) return;
|
|
2371
|
+
this.context = context;
|
|
2372
|
+
});
|
|
2324
2373
|
});
|
|
2325
2374
|
page.on('console', (msg) => {
|
|
2326
2375
|
this.debugSection(`Browser:${ucfirst(msg.type())}`, (msg._text || '') + msg.args().join(' '));
|
|
@@ -2339,7 +2388,7 @@ async function targetCreatedHandler(page) {
|
|
|
2339
2388
|
}
|
|
2340
2389
|
|
|
2341
2390
|
// List of key values to key definitions
|
|
2342
|
-
// https://github.com/
|
|
2391
|
+
// https://github.com/puppeteer/puppeteer/blob/v1.20.0/lib/USKeyboardLayout.js
|
|
2343
2392
|
const keyDefinitionMap = {
|
|
2344
2393
|
/* eslint-disable quote-props */
|
|
2345
2394
|
'0': 'Digit0',
|
|
@@ -2403,3 +2452,12 @@ function getNormalizedKey(key) {
|
|
|
2403
2452
|
}
|
|
2404
2453
|
return normalizedKey;
|
|
2405
2454
|
}
|
|
2455
|
+
|
|
2456
|
+
async function clickablePoint(el) {
|
|
2457
|
+
const rect = await el.boundingBox();
|
|
2458
|
+
if (!rect) throw new ElementNotFound(el);
|
|
2459
|
+
const {
|
|
2460
|
+
x, y, width, height,
|
|
2461
|
+
} = rect;
|
|
2462
|
+
return { x: x + width / 2, y: y + height / 2 };
|
|
2463
|
+
}
|
package/lib/helper/Protractor.js
CHANGED
|
@@ -10,8 +10,8 @@ const requireg = require('requireg');
|
|
|
10
10
|
const Helper = require('../helper');
|
|
11
11
|
const stringIncludes = require('../assert/include').includes;
|
|
12
12
|
const { urlEquals, equals } = require('../assert/equal');
|
|
13
|
-
const empty = require('../assert/empty')
|
|
14
|
-
const truth = require('../assert/truth')
|
|
13
|
+
const { empty } = require('../assert/empty');
|
|
14
|
+
const { truth } = require('../assert/truth');
|
|
15
15
|
const {
|
|
16
16
|
xpathLocator,
|
|
17
17
|
fileExists,
|
|
@@ -967,6 +967,30 @@ class Protractor extends Helper {
|
|
|
967
967
|
return this.browser.getCurrentUrl().then(currentUrl => urlEquals(this.options.url).negate(url, currentUrl));
|
|
968
968
|
}
|
|
969
969
|
|
|
970
|
+
/**
|
|
971
|
+
* {{> saveElementScreenshot }}
|
|
972
|
+
*
|
|
973
|
+
*/
|
|
974
|
+
async saveElementScreenshot(locator, fileName) {
|
|
975
|
+
const outputFile = screenshotOutputFolder(fileName);
|
|
976
|
+
|
|
977
|
+
const writeFile = (png, outputFile) => {
|
|
978
|
+
const fs = require('fs');
|
|
979
|
+
const stream = fs.createWriteStream(outputFile);
|
|
980
|
+
stream.write(Buffer.from(png, 'base64'));
|
|
981
|
+
stream.end();
|
|
982
|
+
return new Promise(resolve => stream.on('finish', resolve));
|
|
983
|
+
};
|
|
984
|
+
|
|
985
|
+
const res = await this._locate(locator);
|
|
986
|
+
assertElementExists(res, locator);
|
|
987
|
+
if (res.length > 1) this.debug(`[Elements] Using first element out of ${res.length}`);
|
|
988
|
+
const elem = res[0];
|
|
989
|
+
this.debug(`Screenshot of ${locator} element has been saved to ${outputFile}`);
|
|
990
|
+
const png = await elem.takeScreenshot();
|
|
991
|
+
return writeFile(png, outputFile);
|
|
992
|
+
}
|
|
993
|
+
|
|
970
994
|
/**
|
|
971
995
|
* {{> saveScreenshot }}
|
|
972
996
|
*/
|
package/lib/helper/Puppeteer.js
CHANGED
|
@@ -64,7 +64,7 @@ const consoleLogStore = new Console();
|
|
|
64
64
|
* * `waitForAction`: (optional) how long to wait after click, doubleClick or PressKey actions in ms. Default: 100.
|
|
65
65
|
* * `waitForNavigation`: (optional, default: 'load'). When to consider navigation succeeded. Possible options: `load`, `domcontentloaded`, `networkidle0`, `networkidle2`. See [Puppeteer API](https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md#pagewaitfornavigationoptions). Array values are accepted as well.
|
|
66
66
|
* * `pressKeyDelay`: (optional, default: '10'). Delay between key presses in ms. Used when calling Puppeteers page.type(...) in fillField/appendField
|
|
67
|
-
* * `getPageTimeout` (optional, default: '
|
|
67
|
+
* * `getPageTimeout` (optional, default: '30000') config option to set maximum navigation time in milliseconds. If the timeout is set to 0, then timeout will be disabled.
|
|
68
68
|
* * `waitForTimeout`: (optional) default wait* timeout in ms. Default: 1000.
|
|
69
69
|
* * `windowSize`: (optional) default window size. Set a dimension like `640x480`.
|
|
70
70
|
* * `userAgent`: (optional) user-agent string.
|
|
@@ -190,7 +190,7 @@ class Puppeteer extends Helper {
|
|
|
190
190
|
disableScreenshots: false,
|
|
191
191
|
uniqueScreenshotNames: false,
|
|
192
192
|
manualStart: false,
|
|
193
|
-
getPageTimeout:
|
|
193
|
+
getPageTimeout: 30000,
|
|
194
194
|
waitForNavigation: 'load',
|
|
195
195
|
restart: true,
|
|
196
196
|
keepCookies: false,
|
|
@@ -295,6 +295,9 @@ class Puppeteer extends Helper {
|
|
|
295
295
|
await this.executeScript('localStorage.clear();').catch((err) => {
|
|
296
296
|
if (!(err.message.indexOf("Storage is disabled inside 'data:' URLs.") > -1)) throw err;
|
|
297
297
|
});
|
|
298
|
+
await this.executeScript('sessionStorage.clear();').catch((err) => {
|
|
299
|
+
if (!(err.message.indexOf("Storage is disabled inside 'data:' URLs.") > -1)) throw err;
|
|
300
|
+
});
|
|
298
301
|
}
|
|
299
302
|
await this.closeOtherTabs();
|
|
300
303
|
return this.browser;
|
|
@@ -1195,6 +1198,20 @@ class Puppeteer extends Helper {
|
|
|
1195
1198
|
return this._waitForAction();
|
|
1196
1199
|
}
|
|
1197
1200
|
|
|
1201
|
+
/**
|
|
1202
|
+
* {{> type }}
|
|
1203
|
+
*/
|
|
1204
|
+
async type(keys, delay = null) {
|
|
1205
|
+
if (!Array.isArray(keys)) {
|
|
1206
|
+
keys = keys.split('');
|
|
1207
|
+
}
|
|
1208
|
+
|
|
1209
|
+
for (const key of keys) {
|
|
1210
|
+
await this.page.keyboard.press(key);
|
|
1211
|
+
if (delay) await this.wait(delay / 1000);
|
|
1212
|
+
}
|
|
1213
|
+
}
|
|
1214
|
+
|
|
1198
1215
|
/**
|
|
1199
1216
|
* {{> fillField }}
|
|
1200
1217
|
* {{ react }}
|
|
@@ -1671,6 +1688,21 @@ class Puppeteer extends Helper {
|
|
|
1671
1688
|
return array.length === 1 ? array[0] : array;
|
|
1672
1689
|
}
|
|
1673
1690
|
|
|
1691
|
+
/**
|
|
1692
|
+
* {{> saveElementScreenshot }}
|
|
1693
|
+
*
|
|
1694
|
+
*/
|
|
1695
|
+
async saveElementScreenshot(locator, fileName) {
|
|
1696
|
+
const outputFile = screenshotOutputFolder(fileName);
|
|
1697
|
+
|
|
1698
|
+
const res = await this._locate(locator);
|
|
1699
|
+
assertElementExists(res, locator);
|
|
1700
|
+
if (res.length > 1) this.debug(`[Elements] Using first element out of ${res.length}`);
|
|
1701
|
+
const elem = res[0];
|
|
1702
|
+
this.debug(`Screenshot of ${locator} element has been saved to ${outputFile}`);
|
|
1703
|
+
return elem.screenshot({ path: outputFile, type: 'png' });
|
|
1704
|
+
}
|
|
1705
|
+
|
|
1674
1706
|
/**
|
|
1675
1707
|
* {{> saveScreenshot }}
|
|
1676
1708
|
*/
|
package/lib/helper/TestCafe.js
CHANGED
|
@@ -731,6 +731,21 @@ class TestCafe extends Helper {
|
|
|
731
731
|
stringIncludes('HTML source of a page').negate(text, source);
|
|
732
732
|
}
|
|
733
733
|
|
|
734
|
+
/**
|
|
735
|
+
* {{> saveElementScreenshot }}
|
|
736
|
+
*
|
|
737
|
+
*/
|
|
738
|
+
async saveElementScreenshot(locator, fileName) {
|
|
739
|
+
const outputFile = path.join(global.output_dir, fileName);
|
|
740
|
+
|
|
741
|
+
const sel = await findElements.call(this, this.context, locator);
|
|
742
|
+
assertElementExists(sel);
|
|
743
|
+
const firstElement = await sel.filterVisible().nth(0);
|
|
744
|
+
|
|
745
|
+
this.debug(`Screenshot of ${locator} element has been saved to ${outputFile}`);
|
|
746
|
+
return this.t.takeElementScreenshot(firstElement, fileName);
|
|
747
|
+
}
|
|
748
|
+
|
|
734
749
|
/**
|
|
735
750
|
* {{> saveScreenshot }}
|
|
736
751
|
*/
|
package/lib/helper/WebDriver.js
CHANGED
|
@@ -1530,6 +1530,21 @@ class WebDriver extends Helper {
|
|
|
1530
1530
|
return elem.moveTo({ xOffset, yOffset });
|
|
1531
1531
|
}
|
|
1532
1532
|
|
|
1533
|
+
/**
|
|
1534
|
+
* {{> saveElementScreenshot }}
|
|
1535
|
+
*
|
|
1536
|
+
*/
|
|
1537
|
+
async saveElementScreenshot(locator, fileName) {
|
|
1538
|
+
const outputFile = screenshotOutputFolder(fileName);
|
|
1539
|
+
|
|
1540
|
+
const res = await this._locate(withStrictLocator(locator), true);
|
|
1541
|
+
assertElementExists(res, locator);
|
|
1542
|
+
const elem = usingFirstElement(res);
|
|
1543
|
+
|
|
1544
|
+
this.debug(`Screenshot of ${locator} element has been saved to ${outputFile}`);
|
|
1545
|
+
return elem.saveScreenshot(outputFile);
|
|
1546
|
+
}
|
|
1547
|
+
|
|
1533
1548
|
/**
|
|
1534
1549
|
* {{> saveScreenshot }}
|
|
1535
1550
|
*
|
|
@@ -1755,14 +1770,19 @@ class WebDriver extends Helper {
|
|
|
1755
1770
|
|
|
1756
1771
|
/**
|
|
1757
1772
|
* {{> type }}
|
|
1758
|
-
* Type out given array of keys or a string of text
|
|
1759
1773
|
*/
|
|
1760
|
-
async type(keys) {
|
|
1761
|
-
if (Array.isArray(keys)) {
|
|
1762
|
-
|
|
1774
|
+
async type(keys, delay = null) {
|
|
1775
|
+
if (!Array.isArray(keys)) {
|
|
1776
|
+
keys = keys.split('');
|
|
1777
|
+
}
|
|
1778
|
+
if (delay) {
|
|
1779
|
+
for (const key of keys) {
|
|
1780
|
+
await this.browser.keys(key);
|
|
1781
|
+
await this.wait(delay / 1000);
|
|
1782
|
+
}
|
|
1763
1783
|
return;
|
|
1764
1784
|
}
|
|
1765
|
-
await this.browser.keys(keys
|
|
1785
|
+
await this.browser.keys(keys);
|
|
1766
1786
|
}
|
|
1767
1787
|
|
|
1768
1788
|
/**
|
|
@@ -1819,12 +1839,24 @@ class WebDriver extends Helper {
|
|
|
1819
1839
|
|
|
1820
1840
|
// for chrome
|
|
1821
1841
|
if (browser.isW3C) {
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1842
|
+
const xOffset = await this.grabElementBoundingRect(locator, 'x');
|
|
1843
|
+
const yOffset = await this.grabElementBoundingRect(locator, 'y');
|
|
1844
|
+
|
|
1845
|
+
return browser.performActions([{
|
|
1846
|
+
type: 'pointer',
|
|
1847
|
+
id: 'pointer1',
|
|
1848
|
+
parameters: { pointerType: 'mouse' },
|
|
1849
|
+
actions: [
|
|
1850
|
+
{
|
|
1851
|
+
type: 'pointerMove', origin: 'pointer', duration: 1000, x: xOffset, y: yOffset,
|
|
1852
|
+
},
|
|
1853
|
+
{ type: 'pointerDown', button: 0 },
|
|
1854
|
+
{
|
|
1855
|
+
type: 'pointerMove', origin: 'pointer', duration: 1000, x: offsetX, y: 0,
|
|
1856
|
+
},
|
|
1857
|
+
{ type: 'pointerUp', button: 0 },
|
|
1858
|
+
],
|
|
1859
|
+
},
|
|
1828
1860
|
]);
|
|
1829
1861
|
}
|
|
1830
1862
|
|
|
@@ -55,7 +55,7 @@ const pollyWebDriver = {
|
|
|
55
55
|
throw new Error(`Invalid value for baseUrl: ${baseUrl}`);
|
|
56
56
|
}
|
|
57
57
|
if (!(typeof oneOrMoreUrls === 'string' || Array.isArray(oneOrMoreUrls))) {
|
|
58
|
-
throw new Error(`Expected type of
|
|
58
|
+
throw new Error(`Expected type of URLs is 'string' or 'array', found '${typeof oneOrMoreUrls}'.`);
|
|
59
59
|
}
|
|
60
60
|
// Remove '/' if it's at the end of baseUrl
|
|
61
61
|
const lastChar = baseUrl.substr(-1);
|
package/lib/hooks.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
const fsPath = require('path');
|
|
2
2
|
|
|
3
|
-
const getParamNames = require('./utils')
|
|
4
|
-
const fileExists = require('./utils').fileExists;
|
|
3
|
+
const { getParamNames, fileExists } = require('./utils');
|
|
5
4
|
const output = require('./output');
|
|
6
5
|
|
|
7
6
|
module.exports = function (hook, done, stage) {
|
|
@@ -2,7 +2,6 @@ const { Parser } = require('gherkin');
|
|
|
2
2
|
const { Context, Suite, Test } = require('mocha');
|
|
3
3
|
|
|
4
4
|
const { matchStep } = require('./bdd');
|
|
5
|
-
const { isAsyncFunction } = require('../utils');
|
|
6
5
|
const event = require('../event');
|
|
7
6
|
const scenario = require('../scenario');
|
|
8
7
|
const Step = require('../step');
|
package/lib/listener/helpers.js
CHANGED
|
@@ -2,8 +2,7 @@ const event = require('../event');
|
|
|
2
2
|
const container = require('../container');
|
|
3
3
|
const recorder = require('../recorder');
|
|
4
4
|
const store = require('../store');
|
|
5
|
-
const error = require('../output')
|
|
6
|
-
const { deepMerge } = require('../utils');
|
|
5
|
+
const { error } = require('../output');
|
|
7
6
|
/**
|
|
8
7
|
* Enable Helpers to listen to test events
|
|
9
8
|
*/
|
package/lib/listener/mocha.js
CHANGED
package/lib/locator.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
const cssToXPath = require('css-to-xpath');
|
|
2
|
-
const sprintf = require('sprintf-js')
|
|
2
|
+
const { sprintf } = require('sprintf-js');
|
|
3
3
|
|
|
4
|
-
const xpathLocator = require('./utils')
|
|
4
|
+
const { xpathLocator } = require('./utils');
|
|
5
5
|
|
|
6
6
|
const locatorTypes = ['css', 'by', 'xpath', 'id', 'name', 'fuzzy', 'frame'];
|
|
7
7
|
/** @class */
|
package/lib/pause.js
CHANGED
|
@@ -7,7 +7,7 @@ const store = require('./store');
|
|
|
7
7
|
const recorder = require('./recorder');
|
|
8
8
|
const event = require('./event');
|
|
9
9
|
const output = require('./output');
|
|
10
|
-
const methodsOfObject = require('./utils')
|
|
10
|
+
const { methodsOfObject } = require('./utils');
|
|
11
11
|
|
|
12
12
|
// npm install colors
|
|
13
13
|
let rl;
|
package/lib/plugin/allure.js
CHANGED
|
@@ -2,7 +2,7 @@ const Allure = require('allure-js-commons');
|
|
|
2
2
|
|
|
3
3
|
const event = require('../event');
|
|
4
4
|
const logger = require('../output');
|
|
5
|
-
const ansiRegExp = require('../utils')
|
|
5
|
+
const { ansiRegExp } = require('../utils');
|
|
6
6
|
|
|
7
7
|
const defaultConfig = {
|
|
8
8
|
outputDir: global.output_dir,
|
package/lib/plugin/autoDelay.js
CHANGED
|
@@ -2,9 +2,8 @@ const Container = require('../container');
|
|
|
2
2
|
const store = require('../store');
|
|
3
3
|
const recorder = require('../recorder');
|
|
4
4
|
const event = require('../event');
|
|
5
|
-
const log = require('../output')
|
|
6
|
-
|
|
7
|
-
const supportedHelpers = require('./standardActingHelpers');
|
|
5
|
+
const { log } = require('../output');
|
|
6
|
+
const supportedHelpers = require('./standardActingHelpers').slice();
|
|
8
7
|
|
|
9
8
|
const methodsToDelay = [
|
|
10
9
|
'click',
|
|
@@ -60,6 +59,7 @@ const defaultConfig = {
|
|
|
60
59
|
*
|
|
61
60
|
*/
|
|
62
61
|
module.exports = function (config) {
|
|
62
|
+
supportedHelpers.push('REST');
|
|
63
63
|
const helpers = Container.helpers();
|
|
64
64
|
let helper;
|
|
65
65
|
|
package/lib/plugin/autoLogin.js
CHANGED
|
@@ -4,7 +4,7 @@ const { fileExists } = require('../utils');
|
|
|
4
4
|
const container = require('../container');
|
|
5
5
|
const store = require('../store');
|
|
6
6
|
const recorder = require('../recorder');
|
|
7
|
-
const debug = require('../output')
|
|
7
|
+
const { debug } = require('../output');
|
|
8
8
|
const isAsyncFunction = require('../utils').isAsyncFunction;
|
|
9
9
|
|
|
10
10
|
const defaultUser = {
|
|
@@ -86,7 +86,7 @@ module.exports = function (config) {
|
|
|
86
86
|
} else {
|
|
87
87
|
fileName += '.failed.png';
|
|
88
88
|
}
|
|
89
|
-
output.plugin('screenshotOnFail', 'Test failed,
|
|
89
|
+
output.plugin('screenshotOnFail', 'Test failed, try to save a screenshot');
|
|
90
90
|
|
|
91
91
|
try {
|
|
92
92
|
if (options.reportDir) {
|
|
@@ -107,6 +107,7 @@ module.exports = function (config) {
|
|
|
107
107
|
allureReporter.addAttachment('Last Seen Screenshot', fs.readFileSync(path.join(global.output_dir, fileName)), 'image/png');
|
|
108
108
|
}
|
|
109
109
|
} catch (err) {
|
|
110
|
+
output.plugin(err);
|
|
110
111
|
if (
|
|
111
112
|
err
|
|
112
113
|
&& err.type
|
package/lib/recorder.js
CHANGED
package/lib/step.js
CHANGED
package/lib/ui.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codeceptjs",
|
|
3
|
-
"version": "2.6.
|
|
3
|
+
"version": "2.6.11",
|
|
4
4
|
"description": "Supercharged End 2 End Testing Framework for NodeJS",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"acceptance",
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
"puppeteer",
|
|
10
10
|
"webdriver",
|
|
11
11
|
"testcafe",
|
|
12
|
+
"playwright",
|
|
12
13
|
"protractor",
|
|
13
14
|
"bdd",
|
|
14
15
|
"tdd",
|
|
@@ -110,7 +111,7 @@
|
|
|
110
111
|
"mocha-parallel-tests": "^2.3.0",
|
|
111
112
|
"nightmare": "^3.0.2",
|
|
112
113
|
"nodemon": "^1.19.4",
|
|
113
|
-
"playwright": "^1.
|
|
114
|
+
"playwright": "^1.4.0",
|
|
114
115
|
"protractor": "^5.4.4",
|
|
115
116
|
"puppeteer": "^4.0.0",
|
|
116
117
|
"qrcode-terminal": "^0.12.0",
|