codeceptjs 3.3.8-beta.1 â 3.4.0
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 +47 -0
- package/README.md +31 -32
- package/docs/advanced.md +48 -24
- package/docs/basics.md +115 -40
- package/docs/best.md +2 -2
- package/docs/build/ApiDataFactory.js +14 -9
- package/docs/build/Appium.js +2 -19
- package/docs/build/FileSystem.js +3 -3
- package/docs/build/GraphQL.js +1 -1
- package/docs/build/GraphQLDataFactory.js +3 -3
- package/docs/build/JSONResponse.js +1 -1
- package/docs/build/Mochawesome.js +1 -1
- package/docs/build/Nightmare.js +1 -1
- package/docs/build/Playwright.js +4 -3
- package/docs/build/Protractor.js +1 -1
- package/docs/build/Puppeteer.js +1 -1
- package/docs/build/REST.js +1 -1
- package/docs/build/TestCafe.js +5 -5
- package/docs/build/WebDriver.js +30 -165
- package/docs/changelog.md +49 -2
- package/docs/helpers/ApiDataFactory.md +6 -6
- package/docs/helpers/FileSystem.md +2 -2
- package/docs/helpers/GraphQLDataFactory.md +2 -2
- package/docs/helpers/Playwright.md +2 -1
- package/docs/index.md +1 -1
- package/docs/plugins.md +42 -125
- package/docs/reports.md +0 -56
- package/docs/tutorial.md +271 -0
- package/docs/typescript.md +2 -8
- package/lib/actor.js +2 -1
- package/lib/cli.js +3 -3
- package/lib/codecept.js +2 -1
- package/lib/command/generate.js +3 -1
- package/lib/command/gherkin/snippets.js +8 -4
- package/lib/command/init.js +0 -8
- package/lib/command/run-workers.js +3 -6
- package/lib/command/utils.js +0 -10
- package/lib/command/workers/runTests.js +2 -2
- package/lib/config.js +5 -1
- package/lib/helper/ApiDataFactory.js +14 -9
- package/lib/helper/Appium.js +2 -19
- package/lib/helper/FileSystem.js +3 -3
- package/lib/helper/GraphQL.js +1 -1
- package/lib/helper/GraphQLDataFactory.js +3 -3
- package/lib/helper/JSONResponse.js +1 -1
- package/lib/helper/Mochawesome.js +1 -1
- package/lib/helper/Nightmare.js +1 -1
- package/lib/helper/Playwright.js +4 -3
- package/lib/helper/Protractor.js +1 -1
- package/lib/helper/Puppeteer.js +1 -1
- package/lib/helper/REST.js +1 -1
- package/lib/helper/TestCafe.js +5 -5
- package/lib/helper/WebDriver.js +30 -165
- package/lib/helper.js +0 -2
- package/lib/interfaces/bdd.js +1 -1
- package/lib/interfaces/featureConfig.js +1 -0
- package/lib/interfaces/gherkin.js +38 -25
- package/lib/listener/exit.js +2 -2
- package/lib/listener/retry.js +67 -0
- package/lib/listener/steps.js +1 -1
- package/lib/listener/timeout.js +47 -10
- package/lib/mochaFactory.js +3 -3
- package/lib/plugin/allure.js +14 -323
- package/lib/plugin/fakerTransform.js +2 -2
- package/lib/recorder.js +1 -1
- package/lib/scenario.js +25 -18
- package/lib/utils.js +6 -0
- package/lib/workers.js +4 -7
- package/package.json +14 -18
- package/typings/index.d.ts +76 -1
- package/typings/promiseBasedTypes.d.ts +12 -12
- package/typings/types.d.ts +95 -262
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
const path = require('path');
|
|
2
2
|
|
|
3
|
-
const Helper = require('
|
|
3
|
+
const Helper = require('@codeceptjs/helper');
|
|
4
4
|
const GraphQL = require('./GraphQL');
|
|
5
5
|
|
|
6
6
|
/**
|
|
@@ -46,8 +46,8 @@ const GraphQL = require('./GraphQL');
|
|
|
46
46
|
* ```js
|
|
47
47
|
* // tests/factories/users.js
|
|
48
48
|
*
|
|
49
|
-
* const Factory = require('rosie').Factory;
|
|
50
|
-
* const faker = require('@faker-js/faker');
|
|
49
|
+
* const { Factory } = require('rosie').Factory;
|
|
50
|
+
* const { faker } = require('@faker-js/faker');
|
|
51
51
|
*
|
|
52
52
|
* // Used with a constructor function passed to Factory, so that the final build
|
|
53
53
|
* // object matches the necessary pattern to be sent as the variables object.
|
package/docs/build/Nightmare.js
CHANGED
|
@@ -2,7 +2,7 @@ const path = require('path');
|
|
|
2
2
|
|
|
3
3
|
const urlResolve = require('url').resolve;
|
|
4
4
|
|
|
5
|
-
const Helper = require('
|
|
5
|
+
const Helper = require('@codeceptjs/helper');
|
|
6
6
|
const { includes: stringIncludes } = require('../assert/include');
|
|
7
7
|
const { urlEquals } = require('../assert/equal');
|
|
8
8
|
const { equals } = require('../assert/equal');
|
package/docs/build/Playwright.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
const path = require('path');
|
|
2
2
|
const fs = require('fs');
|
|
3
3
|
|
|
4
|
-
const Helper = require('
|
|
4
|
+
const Helper = require('@codeceptjs/helper');
|
|
5
5
|
const Locator = require('../locator');
|
|
6
6
|
const recorder = require('../recorder');
|
|
7
7
|
const stringIncludes = require('../assert/include').includes;
|
|
@@ -66,6 +66,7 @@ const pathSeparator = path.sep;
|
|
|
66
66
|
* @prop {boolean} [video=false] - enables video recording for failed tests; videos are saved into `output/videos` folder
|
|
67
67
|
* @prop {boolean} [keepVideoForPassedTests=false] - save videos for passed tests; videos are saved into `output/videos` folder
|
|
68
68
|
* @prop {boolean} [trace=false] - record [tracing information](https://playwright.dev/docs/trace-viewer) with screenshots and snapshots.
|
|
69
|
+
* @prop {boolean} [keepTraceForPassedTests=false] - save trace for passed tests.
|
|
69
70
|
* @prop {boolean} [fullPageScreenshots=false] - make full page screenshots on failure.
|
|
70
71
|
* @prop {boolean} [uniqueScreenshotNames=false] - option to prevent screenshot override if you have scenarios with the same name in different suites.
|
|
71
72
|
* @prop {boolean} [keepBrowserState=false] - keep browser state between tests when `restart` is set to 'session'.
|
|
@@ -1413,7 +1414,7 @@ class Playwright extends Helper {
|
|
|
1413
1414
|
*
|
|
1414
1415
|
* ```
|
|
1415
1416
|
*
|
|
1416
|
-
* @param {string}
|
|
1417
|
+
* @param {string} fileName set filename for downloaded file
|
|
1417
1418
|
* @return {Promise<void>}
|
|
1418
1419
|
*/
|
|
1419
1420
|
async handleDownloads(fileName) {
|
|
@@ -3968,7 +3969,7 @@ async function refreshContextSession() {
|
|
|
3968
3969
|
|
|
3969
3970
|
async function saveVideoForPage(page, name) {
|
|
3970
3971
|
if (!page.video()) return null;
|
|
3971
|
-
const fileName = `${global.output_dir}${pathSeparator}videos${pathSeparator}${Date.now()}_${clearString(name)
|
|
3972
|
+
const fileName = `${`${global.output_dir}${pathSeparator}videos${pathSeparator}${Date.now()}_${clearString(name)}`.slice(0, 245)}.webm`;
|
|
3972
3973
|
page.video().saveAs(fileName).then(() => {
|
|
3973
3974
|
if (!page) return;
|
|
3974
3975
|
page.video().delete().catch(e => {});
|
package/docs/build/Protractor.js
CHANGED
|
@@ -6,7 +6,7 @@ let ProtractorExpectedConditions;
|
|
|
6
6
|
|
|
7
7
|
const path = require('path');
|
|
8
8
|
|
|
9
|
-
const Helper = require('
|
|
9
|
+
const Helper = require('@codeceptjs/helper');
|
|
10
10
|
const stringIncludes = require('../assert/include').includes;
|
|
11
11
|
const { urlEquals, equals } = require('../assert/equal');
|
|
12
12
|
const { empty } = require('../assert/empty');
|
package/docs/build/Puppeteer.js
CHANGED
|
@@ -3,7 +3,7 @@ const fs = require('fs');
|
|
|
3
3
|
const fsExtra = require('fs-extra');
|
|
4
4
|
const path = require('path');
|
|
5
5
|
|
|
6
|
-
const Helper = require('
|
|
6
|
+
const Helper = require('@codeceptjs/helper');
|
|
7
7
|
const Locator = require('../locator');
|
|
8
8
|
const recorder = require('../recorder');
|
|
9
9
|
const stringIncludes = require('../assert/include').includes;
|
package/docs/build/REST.js
CHANGED
package/docs/build/TestCafe.js
CHANGED
|
@@ -6,6 +6,7 @@ const qrcode = require('qrcode-terminal');
|
|
|
6
6
|
const createTestCafe = require('testcafe');
|
|
7
7
|
const { Selector, ClientFunction } = require('testcafe');
|
|
8
8
|
|
|
9
|
+
const Helper = require('@codeceptjs/helper');
|
|
9
10
|
const ElementNotFound = require('./errors/ElementNotFound');
|
|
10
11
|
const testControllerHolder = require('./testcafe/testControllerHolder');
|
|
11
12
|
const {
|
|
@@ -22,7 +23,6 @@ const {
|
|
|
22
23
|
xpathLocator,
|
|
23
24
|
} = require('../utils');
|
|
24
25
|
const Locator = require('../locator');
|
|
25
|
-
const Helper = require('../helper');
|
|
26
26
|
|
|
27
27
|
/**
|
|
28
28
|
* Client Functions
|
|
@@ -766,8 +766,8 @@ class TestCafe extends Helper {
|
|
|
766
766
|
|
|
767
767
|
// TODO As far as I understand the testcafe docs this should do a multi-select
|
|
768
768
|
// but it does not work
|
|
769
|
-
const clickOpts = { ctrl: option.length > 1 };
|
|
770
|
-
await this.t.click(el
|
|
769
|
+
// const clickOpts = { ctrl: option.length > 1 };
|
|
770
|
+
await this.t.click(el).catch(mapError);
|
|
771
771
|
|
|
772
772
|
for (const key of option) {
|
|
773
773
|
const opt = key;
|
|
@@ -776,7 +776,7 @@ class TestCafe extends Helper {
|
|
|
776
776
|
try {
|
|
777
777
|
optEl = el.child('option').withText(opt);
|
|
778
778
|
if (await optEl.count) {
|
|
779
|
-
await this.t.click(optEl
|
|
779
|
+
await this.t.click(optEl).catch(mapError);
|
|
780
780
|
continue;
|
|
781
781
|
}
|
|
782
782
|
// eslint-disable-next-line no-empty
|
|
@@ -787,7 +787,7 @@ class TestCafe extends Helper {
|
|
|
787
787
|
const sel = `[value="${opt}"]`;
|
|
788
788
|
optEl = el.find(sel);
|
|
789
789
|
if (await optEl.count) {
|
|
790
|
-
await this.t.click(optEl
|
|
790
|
+
await this.t.click(optEl).catch(mapError);
|
|
791
791
|
}
|
|
792
792
|
// eslint-disable-next-line no-empty
|
|
793
793
|
} catch (err) {
|
package/docs/build/WebDriver.js
CHANGED
|
@@ -4,12 +4,12 @@ const assert = require('assert');
|
|
|
4
4
|
const path = require('path');
|
|
5
5
|
const fs = require('fs');
|
|
6
6
|
|
|
7
|
-
const Helper = require('
|
|
7
|
+
const Helper = require('@codeceptjs/helper');
|
|
8
8
|
const stringIncludes = require('../assert/include').includes;
|
|
9
9
|
const { urlEquals, equals } = require('../assert/equal');
|
|
10
10
|
const { debug } = require('../output');
|
|
11
|
-
const empty = require('../assert/empty')
|
|
12
|
-
const truth = require('../assert/truth')
|
|
11
|
+
const { empty } = require('../assert/empty');
|
|
12
|
+
const { truth } = require('../assert/truth');
|
|
13
13
|
const {
|
|
14
14
|
xpathLocator,
|
|
15
15
|
fileExists,
|
|
@@ -31,8 +31,6 @@ const Locator = require('../locator');
|
|
|
31
31
|
const SHADOW = 'shadow';
|
|
32
32
|
const webRoot = 'body';
|
|
33
33
|
|
|
34
|
-
let version;
|
|
35
|
-
|
|
36
34
|
/**
|
|
37
35
|
* ## Configuration
|
|
38
36
|
*
|
|
@@ -266,7 +264,7 @@ const config = {};
|
|
|
266
264
|
* ```js
|
|
267
265
|
* plugins: {
|
|
268
266
|
* wdio: {
|
|
269
|
-
|
|
267
|
+
* enabled: true,
|
|
270
268
|
* services: ['sauce'],
|
|
271
269
|
* user: ... ,// saucelabs username
|
|
272
270
|
* key: ... // saucelabs api key
|
|
@@ -294,7 +292,7 @@ const config = {};
|
|
|
294
292
|
* ```js
|
|
295
293
|
* plugins: {
|
|
296
294
|
* wdio: {
|
|
297
|
-
|
|
295
|
+
* enabled: true,
|
|
298
296
|
* services: ['browserstack'],
|
|
299
297
|
* user: ... ,// browserstack username
|
|
300
298
|
* key: ... // browserstack api key
|
|
@@ -386,16 +384,6 @@ class WebDriver extends Helper {
|
|
|
386
384
|
super(config);
|
|
387
385
|
webdriverio = require('webdriverio');
|
|
388
386
|
|
|
389
|
-
try {
|
|
390
|
-
version = JSON.parse(fs.readFileSync(path.join(require.resolve('webdriverio'), '/../../', 'package.json')).toString()).version;
|
|
391
|
-
} catch (err) {
|
|
392
|
-
this.debug('Can\'t detect webdriverio version, assuming webdriverio v6 is used');
|
|
393
|
-
}
|
|
394
|
-
|
|
395
|
-
if (isWebDriver5()) {
|
|
396
|
-
console.log('DEPRECATION NOTICE:');
|
|
397
|
-
console.log('You are using webdriverio v5. It is recommended to update to webdriverio@6.\nSupport of webdriverio v5 is deprecated and will be removed in CodeceptJS 3.0\n');
|
|
398
|
-
}
|
|
399
387
|
// set defaults
|
|
400
388
|
this.root = webRoot;
|
|
401
389
|
this.isWeb = true;
|
|
@@ -661,23 +649,23 @@ class WebDriver extends Helper {
|
|
|
661
649
|
}
|
|
662
650
|
|
|
663
651
|
/**
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
652
|
+
* Use [webdriverio](https://webdriver.io/docs/api.html) API inside a test.
|
|
653
|
+
*
|
|
654
|
+
* First argument is a description of an action.
|
|
655
|
+
* Second argument is async function that gets this helper as parameter.
|
|
656
|
+
*
|
|
657
|
+
* { [`browser`](https://webdriver.io/docs/api.html)) } object from WebDriver API is available.
|
|
658
|
+
*
|
|
659
|
+
* ```js
|
|
660
|
+
* I.useWebDriverTo('open multiple windows', async ({ browser }) {
|
|
661
|
+
* // create new window
|
|
662
|
+
* await browser.newWindow('https://webdriver.io');
|
|
663
|
+
* });
|
|
664
|
+
* ```
|
|
665
|
+
*
|
|
666
|
+
* @param {string} description used to show in logs.
|
|
667
|
+
* @param {function} fn async functuion that executed with WebDriver helper as argument
|
|
668
|
+
*/
|
|
681
669
|
useWebDriverTo(description, fn) {
|
|
682
670
|
return this._useTo(...arguments);
|
|
683
671
|
}
|
|
@@ -2291,7 +2279,6 @@ class WebDriver extends Helper {
|
|
|
2291
2279
|
const res = await this._locate(withStrictLocator(locator), true);
|
|
2292
2280
|
assertElementExists(res, locator);
|
|
2293
2281
|
const elem = usingFirstElement(res);
|
|
2294
|
-
if (isWebDriver5()) return elem.moveTo(xOffset, yOffset);
|
|
2295
2282
|
return elem.moveTo({ xOffset, yOffset });
|
|
2296
2283
|
}
|
|
2297
2284
|
|
|
@@ -2923,19 +2910,7 @@ class WebDriver extends Helper {
|
|
|
2923
2910
|
*/
|
|
2924
2911
|
async waitForEnabled(locator, sec = null) {
|
|
2925
2912
|
const aSec = sec || this.options.waitForTimeoutInSeconds;
|
|
2926
|
-
|
|
2927
|
-
return this.browser.waitUntil(async () => {
|
|
2928
|
-
const res = await this.$$(withStrictLocator(locator));
|
|
2929
|
-
if (!res || res.length === 0) {
|
|
2930
|
-
return false;
|
|
2931
|
-
}
|
|
2932
|
-
const selected = await forEachAsync(res, async el => this.browser.isElementEnabled(getElementId(el)));
|
|
2933
|
-
if (Array.isArray(selected)) {
|
|
2934
|
-
return selected.filter(val => val === true).length > 0;
|
|
2935
|
-
}
|
|
2936
|
-
return selected;
|
|
2937
|
-
}, aSec * 1000, `element (${new Locator(locator)}) still not enabled after ${aSec} sec`);
|
|
2938
|
-
}
|
|
2913
|
+
|
|
2939
2914
|
return this.browser.waitUntil(async () => {
|
|
2940
2915
|
const res = await this._res(locator);
|
|
2941
2916
|
if (!res || res.length === 0) {
|
|
@@ -2968,12 +2943,7 @@ class WebDriver extends Helper {
|
|
|
2968
2943
|
*/
|
|
2969
2944
|
async waitForElement(locator, sec = null) {
|
|
2970
2945
|
const aSec = sec || this.options.waitForTimeoutInSeconds;
|
|
2971
|
-
|
|
2972
|
-
return this.browser.waitUntil(async () => {
|
|
2973
|
-
const res = await this.$$(withStrictLocator(locator));
|
|
2974
|
-
return res && res.length;
|
|
2975
|
-
}, aSec * 1000, `element (${(new Locator(locator))}) still not present on page after ${aSec} sec`);
|
|
2976
|
-
}
|
|
2946
|
+
|
|
2977
2947
|
return this.browser.waitUntil(async () => {
|
|
2978
2948
|
const res = await this._res(locator);
|
|
2979
2949
|
return res && res.length;
|
|
@@ -3022,20 +2992,7 @@ class WebDriver extends Helper {
|
|
|
3022
2992
|
const client = this.browser;
|
|
3023
2993
|
const aSec = sec || this.options.waitForTimeoutInSeconds;
|
|
3024
2994
|
let currUrl = '';
|
|
3025
|
-
|
|
3026
|
-
return client
|
|
3027
|
-
.waitUntil(function () {
|
|
3028
|
-
return this.getUrl().then((res) => {
|
|
3029
|
-
currUrl = decodeUrl(res);
|
|
3030
|
-
return currUrl.indexOf(urlPart) > -1;
|
|
3031
|
-
});
|
|
3032
|
-
}, aSec * 1000).catch((e) => {
|
|
3033
|
-
if (e.message.indexOf('timeout')) {
|
|
3034
|
-
throw new Error(`expected url to include ${urlPart}, but found ${currUrl}`);
|
|
3035
|
-
}
|
|
3036
|
-
throw e;
|
|
3037
|
-
});
|
|
3038
|
-
}
|
|
2995
|
+
|
|
3039
2996
|
return client
|
|
3040
2997
|
.waitUntil(function () {
|
|
3041
2998
|
return this.getUrl().then((res) => {
|
|
@@ -3103,20 +3060,6 @@ class WebDriver extends Helper {
|
|
|
3103
3060
|
async waitForText(text, sec = null, context = null) {
|
|
3104
3061
|
const aSec = sec || this.options.waitForTimeoutInSeconds;
|
|
3105
3062
|
const _context = context || this.root;
|
|
3106
|
-
if (isWebDriver5()) {
|
|
3107
|
-
return this.browser.waitUntil(
|
|
3108
|
-
async () => {
|
|
3109
|
-
const res = await this.$$(withStrictLocator.call(this, _context));
|
|
3110
|
-
if (!res || res.length === 0) return false;
|
|
3111
|
-
const selected = await forEachAsync(res, async el => this.browser.getElementText(getElementId(el)));
|
|
3112
|
-
if (Array.isArray(selected)) {
|
|
3113
|
-
return selected.filter(part => part.indexOf(text) >= 0).length > 0;
|
|
3114
|
-
}
|
|
3115
|
-
return selected.indexOf(text) >= 0;
|
|
3116
|
-
}, aSec * 1000,
|
|
3117
|
-
`element (${_context}) is not in DOM or there is no element(${_context}) with text "${text}" after ${aSec} sec`,
|
|
3118
|
-
);
|
|
3119
|
-
}
|
|
3120
3063
|
|
|
3121
3064
|
return this.browser.waitUntil(
|
|
3122
3065
|
async () => {
|
|
@@ -3150,20 +3093,7 @@ class WebDriver extends Helper {
|
|
|
3150
3093
|
async waitForValue(field, value, sec = null) {
|
|
3151
3094
|
const client = this.browser;
|
|
3152
3095
|
const aSec = sec || this.options.waitForTimeoutInSeconds;
|
|
3153
|
-
|
|
3154
|
-
return client.waitUntil(
|
|
3155
|
-
async () => {
|
|
3156
|
-
const res = await findFields.call(this, field);
|
|
3157
|
-
if (!res || res.length === 0) return false;
|
|
3158
|
-
const selected = await forEachAsync(res, async el => el.getValue());
|
|
3159
|
-
if (Array.isArray(selected)) {
|
|
3160
|
-
return selected.filter(part => part.indexOf(value) >= 0).length > 0;
|
|
3161
|
-
}
|
|
3162
|
-
return selected.indexOf(value) >= 0;
|
|
3163
|
-
}, aSec * 1000,
|
|
3164
|
-
`element (${field}) is not in DOM or there is no element(${field}) with value "${value}" after ${aSec} sec`,
|
|
3165
|
-
);
|
|
3166
|
-
}
|
|
3096
|
+
|
|
3167
3097
|
return client.waitUntil(
|
|
3168
3098
|
async () => {
|
|
3169
3099
|
const res = await findFields.call(this, field);
|
|
@@ -3196,17 +3126,7 @@ class WebDriver extends Helper {
|
|
|
3196
3126
|
*/
|
|
3197
3127
|
async waitForVisible(locator, sec = null) {
|
|
3198
3128
|
const aSec = sec || this.options.waitForTimeoutInSeconds;
|
|
3199
|
-
|
|
3200
|
-
return this.browser.waitUntil(async () => {
|
|
3201
|
-
const res = await this.$$(withStrictLocator(locator));
|
|
3202
|
-
if (!res || res.length === 0) return false;
|
|
3203
|
-
const selected = await forEachAsync(res, async el => el.isDisplayed());
|
|
3204
|
-
if (Array.isArray(selected)) {
|
|
3205
|
-
return selected.filter(val => val === true).length > 0;
|
|
3206
|
-
}
|
|
3207
|
-
return selected;
|
|
3208
|
-
}, aSec * 1000, `element (${new Locator(locator)}) still not visible after ${aSec} sec`);
|
|
3209
|
-
}
|
|
3129
|
+
|
|
3210
3130
|
return this.browser.waitUntil(async () => {
|
|
3211
3131
|
const res = await this._res(locator);
|
|
3212
3132
|
if (!res || res.length === 0) return false;
|
|
@@ -3233,17 +3153,7 @@ class WebDriver extends Helper {
|
|
|
3233
3153
|
*/
|
|
3234
3154
|
async waitNumberOfVisibleElements(locator, num, sec = null) {
|
|
3235
3155
|
const aSec = sec || this.options.waitForTimeoutInSeconds;
|
|
3236
|
-
if (isWebDriver5()) {
|
|
3237
|
-
return this.browser.waitUntil(async () => {
|
|
3238
|
-
const res = await this.$$(withStrictLocator(locator));
|
|
3239
|
-
if (!res || res.length === 0) return false;
|
|
3240
|
-
let selected = await forEachAsync(res, async el => el.isDisplayed());
|
|
3241
3156
|
|
|
3242
|
-
if (!Array.isArray(selected)) selected = [selected];
|
|
3243
|
-
selected = selected.filter(val => val === true);
|
|
3244
|
-
return selected.length === num;
|
|
3245
|
-
}, aSec * 1000, `The number of elements (${new Locator(locator)}) is not ${num} after ${aSec} sec`);
|
|
3246
|
-
}
|
|
3247
3157
|
return this.browser.waitUntil(async () => {
|
|
3248
3158
|
const res = await this._res(locator);
|
|
3249
3159
|
if (!res || res.length === 0) return false;
|
|
@@ -3270,14 +3180,7 @@ class WebDriver extends Helper {
|
|
|
3270
3180
|
*/
|
|
3271
3181
|
async waitForInvisible(locator, sec = null) {
|
|
3272
3182
|
const aSec = sec || this.options.waitForTimeoutInSeconds;
|
|
3273
|
-
|
|
3274
|
-
return this.browser.waitUntil(async () => {
|
|
3275
|
-
const res = await this.$$(withStrictLocator(locator));
|
|
3276
|
-
if (!res || res.length === 0) return true;
|
|
3277
|
-
const selected = await forEachAsync(res, async el => el.isDisplayed());
|
|
3278
|
-
return !selected.length;
|
|
3279
|
-
}, aSec * 1000, `element (${new Locator(locator)}) still visible after ${aSec} sec`);
|
|
3280
|
-
}
|
|
3183
|
+
|
|
3281
3184
|
return this.browser.waitUntil(async () => {
|
|
3282
3185
|
const res = await this._res(locator);
|
|
3283
3186
|
if (!res || res.length === 0) return true;
|
|
@@ -3318,15 +3221,7 @@ class WebDriver extends Helper {
|
|
|
3318
3221
|
*/
|
|
3319
3222
|
async waitForDetached(locator, sec = null) {
|
|
3320
3223
|
const aSec = sec || this.options.waitForTimeoutInSeconds;
|
|
3321
|
-
|
|
3322
|
-
return this.browser.waitUntil(async () => {
|
|
3323
|
-
const res = await this._res(locator);
|
|
3324
|
-
if (!res || res.length === 0) {
|
|
3325
|
-
return true;
|
|
3326
|
-
}
|
|
3327
|
-
return false;
|
|
3328
|
-
}, aSec * 1000, `element (${new Locator(locator)}) still on page after ${aSec} sec`);
|
|
3329
|
-
}
|
|
3224
|
+
|
|
3330
3225
|
return this.browser.waitUntil(async () => {
|
|
3331
3226
|
const res = await this._res(locator);
|
|
3332
3227
|
if (!res || res.length === 0) {
|
|
@@ -3367,9 +3262,7 @@ class WebDriver extends Helper {
|
|
|
3367
3262
|
}
|
|
3368
3263
|
|
|
3369
3264
|
const aSec = sec || this.options.waitForTimeoutInSeconds;
|
|
3370
|
-
|
|
3371
|
-
return this.browser.waitUntil(async () => this.browser.execute(fn, ...args), aSec * 1000, '');
|
|
3372
|
-
}
|
|
3265
|
+
|
|
3373
3266
|
return this.browser.waitUntil(async () => this.browser.execute(fn, ...args), { timeout: aSec * 1000, timeoutMsg: '' });
|
|
3374
3267
|
}
|
|
3375
3268
|
|
|
@@ -3418,18 +3311,6 @@ class WebDriver extends Helper {
|
|
|
3418
3311
|
let target;
|
|
3419
3312
|
const current = await this.browser.getWindowHandle();
|
|
3420
3313
|
|
|
3421
|
-
if (isWebDriver5()) {
|
|
3422
|
-
await this.browser.waitUntil(async () => {
|
|
3423
|
-
await this.browser.getWindowHandles().then((handles) => {
|
|
3424
|
-
if (handles.indexOf(current) + num + 1 <= handles.length) {
|
|
3425
|
-
target = handles[handles.indexOf(current) + num];
|
|
3426
|
-
}
|
|
3427
|
-
});
|
|
3428
|
-
return target;
|
|
3429
|
-
}, aSec * 1000, `There is no ability to switch to next tab with offset ${num}`);
|
|
3430
|
-
return this.browser.switchToWindow(target);
|
|
3431
|
-
}
|
|
3432
|
-
|
|
3433
3314
|
await this.browser.waitUntil(async () => {
|
|
3434
3315
|
await this.browser.getWindowHandles().then((handles) => {
|
|
3435
3316
|
if (handles.indexOf(current) + num + 1 <= handles.length) {
|
|
@@ -3459,18 +3340,6 @@ class WebDriver extends Helper {
|
|
|
3459
3340
|
const current = await this.browser.getWindowHandle();
|
|
3460
3341
|
let target;
|
|
3461
3342
|
|
|
3462
|
-
if (isWebDriver5()) {
|
|
3463
|
-
await this.browser.waitUntil(async () => {
|
|
3464
|
-
await this.browser.getWindowHandles().then((handles) => {
|
|
3465
|
-
if (handles.indexOf(current) - num > -1) {
|
|
3466
|
-
target = handles[handles.indexOf(current) - num];
|
|
3467
|
-
}
|
|
3468
|
-
});
|
|
3469
|
-
return target;
|
|
3470
|
-
}, aSec * 1000, `There is no ability to switch to previous tab with offset ${num}`);
|
|
3471
|
-
return this.browser.switchToWindow(target);
|
|
3472
|
-
}
|
|
3473
|
-
|
|
3474
3343
|
await this.browser.waitUntil(async () => {
|
|
3475
3344
|
await this.browser.getWindowHandles().then((handles) => {
|
|
3476
3345
|
if (handles.indexOf(current) - num > -1) {
|
|
@@ -4145,8 +4014,4 @@ function prepareLocateFn(context) {
|
|
|
4145
4014
|
};
|
|
4146
4015
|
}
|
|
4147
4016
|
|
|
4148
|
-
function isWebDriver5() {
|
|
4149
|
-
return version && version.indexOf('5') === 0;
|
|
4150
|
-
}
|
|
4151
|
-
|
|
4152
4017
|
module.exports = WebDriver;
|
package/docs/changelog.md
CHANGED
|
@@ -7,6 +7,53 @@ layout: Section
|
|
|
7
7
|
|
|
8
8
|
# Releases
|
|
9
9
|
|
|
10
|
+
## 3.4.0
|
|
11
|
+
|
|
12
|
+
* **Updated to latest mocha and modern Cucumber**
|
|
13
|
+
* **Allure plugin moved to [@codeceptjs/allure-legacy](https://github.com/codeceptjs/allure-legacy) package**. This happened because allure-commons package v1 was not updated and caused vulnarabilities. Fixes [#3422](https://github.com/codeceptjs/CodeceptJS/issues/3422). We don't plan to maintain allure v2 plugin so it's up to community to take this initiative. Current allure plugin will print a warning message without interfering the run, so it won't accidentally fail your builds.
|
|
14
|
+
* Added ability to **[retry Before](https://codecept.io/basics/#retry-before), BeforeSuite, After, AfterSuite** hooks by **[davertmik](https://github.com/davertmik)**:
|
|
15
|
+
```js
|
|
16
|
+
Feature('flaky Before & BeforeSuite', { retryBefore: 2, retryBeforeSuite: 3 })
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
* **Flexible [retries configuration](https://codecept.io/basics/#retry-configuration) introduced** by **[davertmik](https://github.com/davertmik)**:
|
|
20
|
+
|
|
21
|
+
```js
|
|
22
|
+
retry: [
|
|
23
|
+
{
|
|
24
|
+
// enable this config only for flaky tests
|
|
25
|
+
grep: '@flaky',
|
|
26
|
+
Before: 3 // retry Before 3 times
|
|
27
|
+
Scenario: 3 // retry Scenario 3 times
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
// retry less when running slow tests
|
|
31
|
+
grep: '@slow'
|
|
32
|
+
Scenario: 1
|
|
33
|
+
Before: 1
|
|
34
|
+
}, {
|
|
35
|
+
// retry all BeforeSuite 3 times
|
|
36
|
+
BeforeSuite: 3
|
|
37
|
+
}
|
|
38
|
+
]
|
|
39
|
+
```
|
|
40
|
+
* **Flexible [timeout configuration](https://codecept.io/advanced/#timeout-configuration)** introduced by **[davertmik](https://github.com/davertmik)**:
|
|
41
|
+
|
|
42
|
+
```js
|
|
43
|
+
timeout: [
|
|
44
|
+
10, // default timeout is 10secs
|
|
45
|
+
{ // but increase timeout for slow tests
|
|
46
|
+
grep: '@slow',
|
|
47
|
+
Feature: 50
|
|
48
|
+
},
|
|
49
|
+
]
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
* JsDoc: Removed promise from `I.say`. See [#3535](https://github.com/codeceptjs/CodeceptJS/issues/3535) by **[danielrentz](https://github.com/danielrentz)**
|
|
53
|
+
* **[Playwright]** `handleDownloads` requires now a filename param. See [#3511](https://github.com/codeceptjs/CodeceptJS/issues/3511) by **[PeterNgTr](https://github.com/PeterNgTr)**
|
|
54
|
+
* **[WebDriver]** Added support for v8, removed support for webdriverio v5 and lower. See [#3578](https://github.com/codeceptjs/CodeceptJS/issues/3578) by **[PeterNgTr](https://github.com/PeterNgTr)**
|
|
55
|
+
|
|
56
|
+
|
|
10
57
|
## 3.3.7
|
|
11
58
|
|
|
12
59
|
đŠī¸ Features
|
|
@@ -37,8 +84,8 @@ layout: Section
|
|
|
37
84
|
|
|
38
85
|
đ Documentation
|
|
39
86
|
|
|
40
|
-
* Updated [
|
|
41
|
-
* Added [Translation](/
|
|
87
|
+
* Updated [Quickstart](https://codecept.io/quickstart/) with detailed explanation of questions in init
|
|
88
|
+
* Added [Translation](/translation/) guide
|
|
42
89
|
* Updated [TypeScript](https://bit.ly/3XIMq6n) guide for promise-based typings
|
|
43
90
|
* Reordered guides list on a website
|
|
44
91
|
|
|
@@ -53,8 +53,8 @@ See the example for Posts factories:
|
|
|
53
53
|
```js
|
|
54
54
|
// tests/factories/posts.js
|
|
55
55
|
|
|
56
|
-
const Factory = require('rosie')
|
|
57
|
-
const faker = require('@faker-js/faker');
|
|
56
|
+
const { Factory } = require('rosie');
|
|
57
|
+
const { faker } = require('@faker-js/faker');
|
|
58
58
|
|
|
59
59
|
module.exports = new Factory()
|
|
60
60
|
// no need to set id, it will be set by REST API
|
|
@@ -228,8 +228,8 @@ I.have('user', { }, { age: 33, height: 55 })
|
|
|
228
228
|
#### Parameters
|
|
229
229
|
|
|
230
230
|
- `factory` **any** factory to use
|
|
231
|
-
- `params` **any
|
|
232
|
-
- `options` **any
|
|
231
|
+
- `params` **any?** predefined parameters
|
|
232
|
+
- `options` **any?** options for programmatically generate the attributes
|
|
233
233
|
|
|
234
234
|
Returns **[Promise][5]<any>**
|
|
235
235
|
|
|
@@ -252,8 +252,8 @@ I.haveMultiple('post', 3, { author: 'davert' }, { publish_date: '01.01.1997' });
|
|
|
252
252
|
|
|
253
253
|
- `factory` **any**
|
|
254
254
|
- `times` **any**
|
|
255
|
-
- `params` **any
|
|
256
|
-
- `options` **any
|
|
255
|
+
- `params` **any?**
|
|
256
|
+
- `options` **any?**
|
|
257
257
|
|
|
258
258
|
[1]: https://github.com/rosiejs/rosie
|
|
259
259
|
|
|
@@ -124,10 +124,10 @@ Checks that file found by `seeFile` includes a text.
|
|
|
124
124
|
|
|
125
125
|
### waitForFile
|
|
126
126
|
|
|
127
|
-
Waits for file to be present in current directory.
|
|
127
|
+
Waits for the file to be present in the current directory.
|
|
128
128
|
|
|
129
129
|
```js
|
|
130
|
-
I.handleDownloads();
|
|
130
|
+
I.handleDownloads('downloads/largeFilesName.txt');
|
|
131
131
|
I.click('Download large File');
|
|
132
132
|
I.amInPath('output/downloads');
|
|
133
133
|
I.waitForFile('largeFilesName.txt', 10); // wait 10 seconds for file
|
|
@@ -53,8 +53,8 @@ See the example for Users factories:
|
|
|
53
53
|
```js
|
|
54
54
|
// tests/factories/users.js
|
|
55
55
|
|
|
56
|
-
const Factory = require('rosie').Factory;
|
|
57
|
-
const faker = require('@faker-js/faker');
|
|
56
|
+
const { Factory } = require('rosie').Factory;
|
|
57
|
+
const { faker } = require('@faker-js/faker');
|
|
58
58
|
|
|
59
59
|
// Used with a constructor function passed to Factory, so that the final build
|
|
60
60
|
// object matches the necessary pattern to be sent as the variables object.
|
|
@@ -51,6 +51,7 @@ Type: [object][5]
|
|
|
51
51
|
- `video` **[boolean][26]?** enables video recording for failed tests; videos are saved into `output/videos` folder
|
|
52
52
|
- `keepVideoForPassedTests` **[boolean][26]?** save videos for passed tests; videos are saved into `output/videos` folder
|
|
53
53
|
- `trace` **[boolean][26]?** record [tracing information][35] with screenshots and snapshots.
|
|
54
|
+
- `keepTraceForPassedTests` **[boolean][26]?** save trace for passed tests.
|
|
54
55
|
- `fullPageScreenshots` **[boolean][26]?** make full page screenshots on failure.
|
|
55
56
|
- `uniqueScreenshotNames` **[boolean][26]?** option to prevent screenshot override if you have scenarios with the same name in different suites.
|
|
56
57
|
- `keepBrowserState` **[boolean][26]?** keep browser state between tests when `restart` is set to 'session'.
|
|
@@ -1138,7 +1139,7 @@ I.waitForFile('avatar.jpg', 5);
|
|
|
1138
1139
|
|
|
1139
1140
|
#### Parameters
|
|
1140
1141
|
|
|
1141
|
-
- `fileName` **[string][8]
|
|
1142
|
+
- `fileName` **[string][8]** set filename for downloaded file
|
|
1142
1143
|
|
|
1143
1144
|
Returns **[Promise][14]<void>**
|
|
1144
1145
|
|
package/docs/index.md
CHANGED
|
@@ -86,7 +86,7 @@ Scenario('Checkout test', ({ I }) => {
|
|
|
86
86
|
Can we use it for long scenarios? Sure!
|
|
87
87
|
|
|
88
88
|
```js
|
|
89
|
-
const faker = require('@faker-js/faker'); // Use 3rd-party JS code
|
|
89
|
+
const { faker } = require('@faker-js/faker'); // Use 3rd-party JS code
|
|
90
90
|
|
|
91
91
|
Feature('Store');
|
|
92
92
|
|