codeceptjs 3.1.2 → 3.2.2
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 +103 -0
- package/README.md +2 -3
- package/bin/codecept.js +1 -0
- package/docs/advanced.md +94 -60
- package/docs/basics.md +27 -2
- package/docs/bdd.md +57 -3
- package/docs/build/Appium.js +8 -4
- package/docs/build/FileSystem.js +1 -0
- package/docs/build/Playwright.js +39 -30
- package/docs/build/Protractor.js +9 -24
- package/docs/build/Puppeteer.js +10 -28
- package/docs/build/REST.js +1 -0
- package/docs/build/WebDriver.js +3 -25
- package/docs/changelog.md +103 -0
- package/docs/commands.md +21 -7
- package/docs/custom-helpers.md +1 -36
- package/docs/helpers/Appium.md +34 -30
- package/docs/helpers/FileSystem.md +1 -1
- package/docs/helpers/Playwright.md +16 -18
- package/docs/helpers/Puppeteer.md +1 -17
- package/docs/helpers/REST.md +3 -1
- package/docs/helpers/WebDriver.md +1 -17
- package/docs/mobile-react-native-locators.md +3 -0
- package/docs/pageobjects.md +2 -0
- package/docs/playwright.md +16 -33
- package/docs/plugins.md +128 -3
- package/docs/reports.md +23 -5
- package/lib/actor.js +20 -2
- package/lib/codecept.js +2 -0
- package/lib/command/info.js +1 -1
- package/lib/config.js +13 -1
- package/lib/container.js +3 -1
- package/lib/data/dataTableArgument.js +35 -0
- package/lib/helper/Appium.js +8 -4
- package/lib/helper/FileSystem.js +1 -0
- package/lib/helper/Playwright.js +39 -20
- package/lib/helper/Protractor.js +2 -14
- package/lib/helper/Puppeteer.js +3 -18
- package/lib/helper/REST.js +1 -0
- package/lib/helper/WebDriver.js +3 -15
- package/lib/index.js +2 -0
- package/lib/interfaces/featureConfig.js +3 -0
- package/lib/interfaces/gherkin.js +7 -1
- package/lib/interfaces/scenarioConfig.js +4 -0
- package/lib/listener/helpers.js +1 -0
- package/lib/listener/steps.js +21 -3
- package/lib/listener/timeout.js +72 -0
- package/lib/locator.js +3 -0
- package/lib/mochaFactory.js +2 -3
- package/lib/plugin/allure.js +6 -1
- package/lib/plugin/coverage.js +1 -1
- package/lib/plugin/retryFailedStep.js +4 -3
- package/lib/plugin/retryTo.js +130 -0
- package/lib/plugin/screenshotOnFail.js +1 -0
- package/lib/plugin/stepByStepReport.js +7 -0
- package/lib/plugin/stepTimeout.js +91 -0
- package/lib/recorder.js +23 -9
- package/lib/step.js +58 -0
- package/lib/store.js +2 -0
- package/lib/ui.js +2 -2
- package/package.json +4 -6
- package/typings/index.d.ts +8 -1
- package/typings/types.d.ts +103 -70
- package/docs/angular.md +0 -325
- package/docs/helpers/Protractor.md +0 -1658
- package/docs/webapi/waitUntil.mustache +0 -11
- package/typings/Protractor.d.ts +0 -16
package/docs/reports.md
CHANGED
|
@@ -7,8 +7,8 @@ title: Reporters
|
|
|
7
7
|
|
|
8
8
|
## Cli
|
|
9
9
|
|
|
10
|
-
By default CodeceptJS provides cli reporter with console output.
|
|
11
|
-
Test names and failures will be printed
|
|
10
|
+
By default, CodeceptJS provides cli reporter with console output.
|
|
11
|
+
Test names and failures will be printed out on screen.
|
|
12
12
|
|
|
13
13
|
```sh
|
|
14
14
|
GitHub --
|
|
@@ -33,8 +33,8 @@ GitHub --
|
|
|
33
33
|
Run with --verbose flag to see NodeJS stacktrace
|
|
34
34
|
|
|
35
35
|
```
|
|
36
|
-
|
|
37
|
-
|
|
36
|
+
|
|
37
|
+
output steps use `--steps` option:
|
|
38
38
|
```
|
|
39
39
|
npx codeceptjs run --steps
|
|
40
40
|
```
|
|
@@ -201,7 +201,7 @@ npx codeceptjs dry-run --debug -p allure
|
|
|
201
201
|
|
|
202
202
|
## ReportPortal
|
|
203
203
|
|
|
204
|
-
Allure is a great
|
|
204
|
+
Allure is a great reporting tool, however, if you are running tests on different machines it is hard to merge its XML result files to build a proper report. So, for enterprise grade reporting we recommend using [ReportPortal](https://reportportal.io).
|
|
205
205
|
|
|
206
206
|

|
|
207
207
|
|
|
@@ -376,3 +376,21 @@ npx codeceptjs run --reporter mocha-multi
|
|
|
376
376
|
```
|
|
377
377
|
|
|
378
378
|
This will give you cli with steps in console and HTML report in `output` directory.
|
|
379
|
+
|
|
380
|
+
|
|
381
|
+
## Testrail
|
|
382
|
+
|
|
383
|
+
Testrail integration with CodeceptJS is now so seamless. The test run is created automatically afterwards. The screenshots of failed tests are also attached to test results.
|
|
384
|
+
|
|
385
|
+
Try to use [codeceptjs-testrail](https://www.npmjs.com/package/codeceptjs-testrail) plugin
|
|
386
|
+
|
|
387
|
+
Install it via NPM:
|
|
388
|
+
|
|
389
|
+
```sh
|
|
390
|
+
npm i codeceptjs-testrail --save
|
|
391
|
+
```
|
|
392
|
+
|
|
393
|
+

|
|
394
|
+
|
|
395
|
+
Now there is new feature, add the configuration to test run of test plan
|
|
396
|
+

|
package/lib/actor.js
CHANGED
|
@@ -27,8 +27,26 @@ class Actor {
|
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
/**
|
|
30
|
+
* set the maximum execution time for the next step
|
|
30
31
|
* @function
|
|
31
|
-
* @param {
|
|
32
|
+
* @param {number} timeout - step timeout in seconds
|
|
33
|
+
* @return {this}
|
|
34
|
+
* @inner
|
|
35
|
+
*/
|
|
36
|
+
limitTime(timeout) {
|
|
37
|
+
if (!store.timeouts) return this;
|
|
38
|
+
|
|
39
|
+
event.dispatcher.prependOnceListener(event.step.before, (step) => {
|
|
40
|
+
output.log(`Timeout to ${step}: ${timeout}s`);
|
|
41
|
+
step.setTimeout(timeout * 1000, Step.TIMEOUT_ORDER.codeLimitTime);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
return this;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* @function
|
|
49
|
+
* @param {*} [opts]
|
|
32
50
|
* @return {this}
|
|
33
51
|
* @inner
|
|
34
52
|
*/
|
|
@@ -114,7 +132,7 @@ function recordStep(step, args) {
|
|
|
114
132
|
step.startTime = Date.now();
|
|
115
133
|
}
|
|
116
134
|
return val = step.run(...args);
|
|
117
|
-
});
|
|
135
|
+
}, false, undefined, step.getTimeout());
|
|
118
136
|
|
|
119
137
|
event.emit(event.step.after, step);
|
|
120
138
|
|
package/lib/codecept.js
CHANGED
|
@@ -77,6 +77,7 @@ class Codecept {
|
|
|
77
77
|
global.inject = container.support;
|
|
78
78
|
global.share = container.share;
|
|
79
79
|
global.secret = require('./secret').secret;
|
|
80
|
+
global.codecept_debug = output.debug;
|
|
80
81
|
global.codeceptjs = require('./index'); // load all objects
|
|
81
82
|
|
|
82
83
|
// BDD
|
|
@@ -95,6 +96,7 @@ class Codecept {
|
|
|
95
96
|
runHook(require('./listener/steps'));
|
|
96
97
|
runHook(require('./listener/config'));
|
|
97
98
|
runHook(require('./listener/helpers'));
|
|
99
|
+
runHook(require('./listener/timeout'));
|
|
98
100
|
runHook(require('./listener/exit'));
|
|
99
101
|
|
|
100
102
|
// custom hooks
|
package/lib/command/info.js
CHANGED
|
@@ -32,7 +32,7 @@ module.exports = async function (path) {
|
|
|
32
32
|
}
|
|
33
33
|
}
|
|
34
34
|
output.print('***************************************');
|
|
35
|
-
output.print('If you have questions ask them in our Slack:
|
|
35
|
+
output.print('If you have questions ask them in our Slack: http://bit.ly/chat-codeceptjs');
|
|
36
36
|
output.print('Or ask them on our discussion board: https://codecept.discourse.group/');
|
|
37
37
|
output.print('Please copy environment info when you report issues on GitHub: https://github.com/Codeception/CodeceptJS/issues');
|
|
38
38
|
output.print('***************************************');
|
package/lib/config.js
CHANGED
|
@@ -13,6 +13,7 @@ const defaultConfig = {
|
|
|
13
13
|
include: {},
|
|
14
14
|
mocha: {},
|
|
15
15
|
bootstrap: null,
|
|
16
|
+
timeout: null,
|
|
16
17
|
teardown: null,
|
|
17
18
|
hooks: [],
|
|
18
19
|
gherkin: {},
|
|
@@ -21,6 +22,17 @@ const defaultConfig = {
|
|
|
21
22
|
enabled: true, // will be disabled by default in 2.0
|
|
22
23
|
},
|
|
23
24
|
},
|
|
25
|
+
stepTimeout: 0,
|
|
26
|
+
stepTimeoutOverride: [
|
|
27
|
+
{
|
|
28
|
+
pattern: 'wait.*',
|
|
29
|
+
timeout: 0,
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
pattern: 'amOnPage',
|
|
33
|
+
timeout: 0,
|
|
34
|
+
},
|
|
35
|
+
],
|
|
24
36
|
};
|
|
25
37
|
|
|
26
38
|
let hooks = [];
|
|
@@ -128,7 +140,7 @@ function loadConfigFile(configFile) {
|
|
|
128
140
|
const extensionName = path.extname(configFile);
|
|
129
141
|
|
|
130
142
|
// .conf.js config file
|
|
131
|
-
if (extensionName === '.js' || extensionName === '.ts') {
|
|
143
|
+
if (extensionName === '.js' || extensionName === '.ts' || extensionName === '.cjs') {
|
|
132
144
|
return Config.create(require(configFile).config);
|
|
133
145
|
}
|
|
134
146
|
|
package/lib/container.js
CHANGED
|
@@ -7,6 +7,7 @@ const MochaFactory = require('./mochaFactory');
|
|
|
7
7
|
const recorder = require('./recorder');
|
|
8
8
|
const event = require('./event');
|
|
9
9
|
const WorkerStorage = require('./workerStorage');
|
|
10
|
+
const store = require('./store');
|
|
10
11
|
|
|
11
12
|
let container = {
|
|
12
13
|
helpers: {},
|
|
@@ -45,6 +46,7 @@ class Container {
|
|
|
45
46
|
container.support = createSupportObjects(config.include || {});
|
|
46
47
|
container.plugins = createPlugins(config.plugins || {}, opts);
|
|
47
48
|
if (config.gherkin) loadGherkinSteps(config.gherkin.steps || []);
|
|
49
|
+
if (opts && typeof opts.timeouts === 'boolean') store.timeouts = opts.timeouts;
|
|
48
50
|
}
|
|
49
51
|
|
|
50
52
|
/**
|
|
@@ -233,7 +235,7 @@ function createSupportObjects(config) {
|
|
|
233
235
|
const currentObject = objects[object];
|
|
234
236
|
Object.keys(currentObject).forEach((method) => {
|
|
235
237
|
const currentMethod = currentObject[method];
|
|
236
|
-
if (currentMethod[Symbol.toStringTag] === 'AsyncFunction') {
|
|
238
|
+
if (currentMethod && currentMethod[Symbol.toStringTag] === 'AsyncFunction') {
|
|
237
239
|
objects[object][method] = asyncWrapper(currentMethod);
|
|
238
240
|
}
|
|
239
241
|
});
|
|
@@ -1,4 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DataTableArgument class to store the Cucumber data table from
|
|
3
|
+
* a step as an object with methods that can be used to access the data.
|
|
4
|
+
*/
|
|
1
5
|
class DataTableArgument {
|
|
6
|
+
/** @param {*} gherkinDataTable */
|
|
2
7
|
constructor(gherkinDataTable) {
|
|
3
8
|
this.rawData = gherkinDataTable.rows.map((row) => {
|
|
4
9
|
return row.cells.map((cell) => {
|
|
@@ -7,16 +12,25 @@ class DataTableArgument {
|
|
|
7
12
|
});
|
|
8
13
|
}
|
|
9
14
|
|
|
15
|
+
/** Returns the table as a 2-D array
|
|
16
|
+
* @returns {string[][]}
|
|
17
|
+
*/
|
|
10
18
|
raw() {
|
|
11
19
|
return this.rawData.slice(0);
|
|
12
20
|
}
|
|
13
21
|
|
|
22
|
+
/** Returns the table as a 2-D array, without the first row
|
|
23
|
+
* @returns {string[][]}
|
|
24
|
+
*/
|
|
14
25
|
rows() {
|
|
15
26
|
const copy = this.raw();
|
|
16
27
|
copy.shift();
|
|
17
28
|
return copy;
|
|
18
29
|
}
|
|
19
30
|
|
|
31
|
+
/** Returns an array of objects where each row is converted to an object (column header is the key)
|
|
32
|
+
* @returns {any[]}
|
|
33
|
+
*/
|
|
20
34
|
hashes() {
|
|
21
35
|
const copy = this.raw();
|
|
22
36
|
const header = copy.shift();
|
|
@@ -26,6 +40,27 @@ class DataTableArgument {
|
|
|
26
40
|
return r;
|
|
27
41
|
});
|
|
28
42
|
}
|
|
43
|
+
|
|
44
|
+
/** Returns an object where each row corresponds to an entry
|
|
45
|
+
* (first column is the key, second column is the value)
|
|
46
|
+
* @returns {Record<string, string>}
|
|
47
|
+
*/
|
|
48
|
+
rowsHash() {
|
|
49
|
+
const rows = this.raw();
|
|
50
|
+
const everyRowHasTwoColumns = rows.every((row) => row.length === 2);
|
|
51
|
+
if (!everyRowHasTwoColumns) {
|
|
52
|
+
throw new Error('rowsHash can only be called on a data table where all rows have exactly two columns');
|
|
53
|
+
}
|
|
54
|
+
/** @type {Record<string, string>} */
|
|
55
|
+
const result = {};
|
|
56
|
+
rows.forEach((x) => (result[x[0]] = x[1]));
|
|
57
|
+
return result;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** Transposed the data */
|
|
61
|
+
transpose() {
|
|
62
|
+
this.rawData = this.rawData[0].map((x, i) => this.rawData.map((y) => y[i]));
|
|
63
|
+
}
|
|
29
64
|
}
|
|
30
65
|
|
|
31
66
|
module.exports = DataTableArgument;
|
package/lib/helper/Appium.js
CHANGED
|
@@ -481,10 +481,11 @@ class Appium extends Webdriver {
|
|
|
481
481
|
* ```js
|
|
482
482
|
* I.removeApp('appName', 'com.example.android.apis');
|
|
483
483
|
* ```
|
|
484
|
-
* @param {string} appId
|
|
485
|
-
* @param {string} bundleId String ID of bundle
|
|
486
484
|
*
|
|
487
485
|
* Appium: support only Android
|
|
486
|
+
*
|
|
487
|
+
* @param {string} appId
|
|
488
|
+
* @param {string} [bundleId] ID of bundle
|
|
488
489
|
*/
|
|
489
490
|
async removeApp(appId, bundleId) {
|
|
490
491
|
onlyForApps.call(this, 'Android');
|
|
@@ -820,9 +821,10 @@ class Appium extends Webdriver {
|
|
|
820
821
|
* I.hideDeviceKeyboard('pressKey', 'Done');
|
|
821
822
|
* ```
|
|
822
823
|
*
|
|
823
|
-
* @param {'tapOutside' | 'pressKey'} strategy desired strategy to close keyboard (‘tapOutside’ or ‘pressKey’)
|
|
824
|
-
*
|
|
825
824
|
* Appium: support Android and iOS
|
|
825
|
+
*
|
|
826
|
+
* @param {'tapOutside' | 'pressKey'} [strategy] Desired strategy to close keyboard (‘tapOutside’ or ‘pressKey’)
|
|
827
|
+
* @param {string} [key] Optional key
|
|
826
828
|
*/
|
|
827
829
|
async hideDeviceKeyboard(strategy, key) {
|
|
828
830
|
onlyForApps.call(this);
|
|
@@ -1162,6 +1164,8 @@ class Appium extends Webdriver {
|
|
|
1162
1164
|
* ```
|
|
1163
1165
|
*
|
|
1164
1166
|
* Appium: support Android and iOS
|
|
1167
|
+
*
|
|
1168
|
+
* @param {Array} actions Array of touch actions
|
|
1165
1169
|
*/
|
|
1166
1170
|
async touchPerform(actions) {
|
|
1167
1171
|
onlyForApps.call(this);
|
package/lib/helper/FileSystem.js
CHANGED
package/lib/helper/Playwright.js
CHANGED
|
@@ -80,6 +80,7 @@ const { createValueEngine, createDisabledEngine } = require('./extras/Playwright
|
|
|
80
80
|
* * `basicAuth`: (optional) the basic authentication to pass to base url. Example: {username: 'username', password: 'password'}
|
|
81
81
|
* * `windowSize`: (optional) default window size. Set a dimension like `640x480`.
|
|
82
82
|
* * `userAgent`: (optional) user-agent string.
|
|
83
|
+
* * `locale`: (optional) locale string. Example: 'en-GB', 'de-DE', 'fr-FR', ...
|
|
83
84
|
* * `manualStart`: (optional, default: false) - do not start browser before a test, start it manually inside a helper with `this.helpers["Playwright"]._startBrowser()`.
|
|
84
85
|
* * `chromium`: (optional) pass additional chromium options
|
|
85
86
|
* * `electron`: (optional) pass additional electron options
|
|
@@ -197,6 +198,19 @@ const { createValueEngine, createDisabledEngine } = require('./extras/Playwright
|
|
|
197
198
|
* }
|
|
198
199
|
* ```
|
|
199
200
|
*
|
|
201
|
+
* #### Example #7: Launch test with a specifc user locale
|
|
202
|
+
*
|
|
203
|
+
* ```js
|
|
204
|
+
* {
|
|
205
|
+
* helpers: {
|
|
206
|
+
* Playwright : {
|
|
207
|
+
* url: "http://localhost",
|
|
208
|
+
* locale: "fr-FR",
|
|
209
|
+
* }
|
|
210
|
+
* }
|
|
211
|
+
* }
|
|
212
|
+
* ```
|
|
213
|
+
*
|
|
200
214
|
* Note: When connecting to remote browser `show` and specific `chrome` options (e.g. `headless` or `devtools`) are ignored.
|
|
201
215
|
*
|
|
202
216
|
* ## Access From Helpers
|
|
@@ -351,9 +365,11 @@ class Playwright extends Helper {
|
|
|
351
365
|
return err.message.includes('context');
|
|
352
366
|
},
|
|
353
367
|
});
|
|
368
|
+
|
|
354
369
|
if (this.options.restart && !this.options.manualStart) await this._startBrowser();
|
|
355
370
|
if (!this.isRunning && !this.options.manualStart) await this._startBrowser();
|
|
356
371
|
|
|
372
|
+
this.isAuthenticated = false;
|
|
357
373
|
if (this.isElectron) {
|
|
358
374
|
this.browserContext = this.browser.context();
|
|
359
375
|
} else if (this.userDataDir) {
|
|
@@ -364,8 +380,14 @@ class Playwright extends Helper {
|
|
|
364
380
|
acceptDownloads: true,
|
|
365
381
|
...this.options.emulate,
|
|
366
382
|
};
|
|
383
|
+
if (this.options.basicAuth) {
|
|
384
|
+
contextOptions.httpCredentials = this.options.basicAuth;
|
|
385
|
+
this.isAuthenticated = true;
|
|
386
|
+
}
|
|
367
387
|
if (this.options.recordVideo) contextOptions.recordVideo = this.options.recordVideo;
|
|
368
388
|
if (this.storageState) contextOptions.storageState = this.storageState;
|
|
389
|
+
if (this.options.userAgent) contextOptions.userAgent = this.options.userAgent;
|
|
390
|
+
if (this.options.locale) contextOptions.locale = this.options.locale;
|
|
369
391
|
this.browserContext = await this.browser.newContext(contextOptions); // Adding the HTTPSError ignore in the context so that we can ignore those errors
|
|
370
392
|
}
|
|
371
393
|
|
|
@@ -557,9 +579,14 @@ class Playwright extends Helper {
|
|
|
557
579
|
this.page = page;
|
|
558
580
|
if (!page) return;
|
|
559
581
|
page.setDefaultNavigationTimeout(this.options.getPageTimeout);
|
|
582
|
+
|
|
583
|
+
page.on('crash', async () => {
|
|
584
|
+
console.log('ERROR: Page has crashed, closing page!');
|
|
585
|
+
await page.close();
|
|
586
|
+
});
|
|
560
587
|
this.context = await this.page;
|
|
561
588
|
this.contextLocator = null;
|
|
562
|
-
if (this.
|
|
589
|
+
if (this.options.browser === 'chrome') {
|
|
563
590
|
await page.bringToFront();
|
|
564
591
|
}
|
|
565
592
|
}
|
|
@@ -714,9 +741,9 @@ class Playwright extends Helper {
|
|
|
714
741
|
url = this.options.url + url;
|
|
715
742
|
}
|
|
716
743
|
|
|
717
|
-
if (this.
|
|
744
|
+
if (this.options.basicAuth && (this.isAuthenticated !== true)) {
|
|
718
745
|
if (url.includes(this.options.url)) {
|
|
719
|
-
await this.browserContext.setHTTPCredentials(this.
|
|
746
|
+
await this.browserContext.setHTTPCredentials(this.options.basicAuth);
|
|
720
747
|
this.isAuthenticated = true;
|
|
721
748
|
}
|
|
722
749
|
}
|
|
@@ -775,7 +802,7 @@ class Playwright extends Helper {
|
|
|
775
802
|
if (!customHeaders) {
|
|
776
803
|
throw new Error('Cannot send empty headers.');
|
|
777
804
|
}
|
|
778
|
-
return this.
|
|
805
|
+
return this.browserContext.setExtraHTTPHeaders(customHeaders);
|
|
779
806
|
}
|
|
780
807
|
|
|
781
808
|
/**
|
|
@@ -1851,6 +1878,11 @@ class Playwright extends Helper {
|
|
|
1851
1878
|
|
|
1852
1879
|
async _failed(test) {
|
|
1853
1880
|
await this._withinEnd();
|
|
1881
|
+
|
|
1882
|
+
if (!test.artifacts) {
|
|
1883
|
+
test.artifacts = {};
|
|
1884
|
+
}
|
|
1885
|
+
|
|
1854
1886
|
if (this.options.recordVideo && this.page.video()) {
|
|
1855
1887
|
test.artifacts.video = await this.page.video().path();
|
|
1856
1888
|
}
|
|
@@ -2136,11 +2168,11 @@ class Playwright extends Helper {
|
|
|
2136
2168
|
}
|
|
2137
2169
|
|
|
2138
2170
|
/**
|
|
2139
|
-
* Waits for a network
|
|
2171
|
+
* Waits for a network response.
|
|
2140
2172
|
*
|
|
2141
2173
|
* ```js
|
|
2142
2174
|
* I.waitForResponse('http://example.com/resource');
|
|
2143
|
-
* I.waitForResponse(
|
|
2175
|
+
* I.waitForResponse(response => response.url() === 'https://example.com' && response.status() === 200);
|
|
2144
2176
|
* ```
|
|
2145
2177
|
*
|
|
2146
2178
|
* @param {string|function} urlOrPredicate
|
|
@@ -2226,16 +2258,6 @@ class Playwright extends Helper {
|
|
|
2226
2258
|
return this.page.waitForNavigation(opts);
|
|
2227
2259
|
}
|
|
2228
2260
|
|
|
2229
|
-
/**
|
|
2230
|
-
* {{> waitUntil }}
|
|
2231
|
-
*/
|
|
2232
|
-
async waitUntil(fn, sec = null) {
|
|
2233
|
-
console.log('This method will remove in CodeceptJS 1.4; use `waitForFunction` instead!');
|
|
2234
|
-
const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout;
|
|
2235
|
-
const context = await this._getContext();
|
|
2236
|
-
return context.waitForFunction(fn, { timeout: waitTimeout });
|
|
2237
|
-
}
|
|
2238
|
-
|
|
2239
2261
|
async waitUntilExists(locator, sec) {
|
|
2240
2262
|
console.log(`waitUntilExists deprecated:
|
|
2241
2263
|
* use 'waitForElement' to wait for element to be attached
|
|
@@ -2652,13 +2674,10 @@ async function targetCreatedHandler(page) {
|
|
|
2652
2674
|
});
|
|
2653
2675
|
});
|
|
2654
2676
|
page.on('console', (msg) => {
|
|
2655
|
-
this.debugSection(`Browser:${ucfirst(msg.type())}`, (msg._text || '') + msg.args().join(' '));
|
|
2677
|
+
this.debugSection(`Browser:${ucfirst(msg.type())}`, (msg.text && msg.text() || msg._text || '') + msg.args().join(' '));
|
|
2656
2678
|
consoleLogStore.add(msg);
|
|
2657
2679
|
});
|
|
2658
2680
|
|
|
2659
|
-
if (this.options.userAgent) {
|
|
2660
|
-
await page.setUserAgent(this.options.userAgent);
|
|
2661
|
-
}
|
|
2662
2681
|
if (this.options.windowSize && this.options.windowSize.indexOf('x') > 0 && this._getType() === 'Browser') {
|
|
2663
2682
|
await page.setViewportSize(parseWindowSize(this.options.windowSize));
|
|
2664
2683
|
}
|
package/lib/helper/Protractor.js
CHANGED
|
@@ -838,11 +838,7 @@ class Protractor extends Helper {
|
|
|
838
838
|
}
|
|
839
839
|
|
|
840
840
|
/**
|
|
841
|
-
*
|
|
842
|
-
*
|
|
843
|
-
* ```js
|
|
844
|
-
* I.seeTitleEquals('Test title.');
|
|
845
|
-
* ```
|
|
841
|
+
* {{> seeTitleEquals }}
|
|
846
842
|
*/
|
|
847
843
|
async seeTitleEquals(text) {
|
|
848
844
|
const title = await this.browser.getTitle();
|
|
@@ -1018,7 +1014,7 @@ class Protractor extends Helper {
|
|
|
1018
1014
|
}
|
|
1019
1015
|
|
|
1020
1016
|
/**
|
|
1021
|
-
|
|
1017
|
+
* {{> seeInCurrentUrl }}
|
|
1022
1018
|
*/
|
|
1023
1019
|
async seeInCurrentUrl(url) {
|
|
1024
1020
|
return this.browser.getCurrentUrl().then(currentUrl => stringIncludes('url').assert(url, currentUrl));
|
|
@@ -1498,14 +1494,6 @@ class Protractor extends Helper {
|
|
|
1498
1494
|
return this.browser.wait(() => this.browser.executeScript.call(this.browser, fn, ...args), aSec * 1000);
|
|
1499
1495
|
}
|
|
1500
1496
|
|
|
1501
|
-
/**
|
|
1502
|
-
* {{> waitUntil }}
|
|
1503
|
-
*/
|
|
1504
|
-
async waitUntil(fn, sec = null, timeoutMsg = null) {
|
|
1505
|
-
const aSec = sec || this.options.waitForTimeout;
|
|
1506
|
-
return this.browser.wait(fn, aSec * 1000, timeoutMsg);
|
|
1507
|
-
}
|
|
1508
|
-
|
|
1509
1497
|
/**
|
|
1510
1498
|
* {{> waitInUrl }}
|
|
1511
1499
|
*/
|
package/lib/helper/Puppeteer.js
CHANGED
|
@@ -264,7 +264,7 @@ class Puppeteer extends Helper {
|
|
|
264
264
|
async _before() {
|
|
265
265
|
this.sessionPages = {};
|
|
266
266
|
recorder.retry({
|
|
267
|
-
retries:
|
|
267
|
+
retries: 3,
|
|
268
268
|
when: err => {
|
|
269
269
|
if (!err || typeof (err.message) !== 'string') {
|
|
270
270
|
return false;
|
|
@@ -554,10 +554,9 @@ class Puppeteer extends Helper {
|
|
|
554
554
|
this.context = null;
|
|
555
555
|
popupStore.clear();
|
|
556
556
|
this.isAuthenticated = false;
|
|
557
|
+
await this.browser.close();
|
|
557
558
|
if (this.isRemoteBrowser) {
|
|
558
559
|
await this.browser.disconnect();
|
|
559
|
-
} else {
|
|
560
|
-
await this.browser.close();
|
|
561
560
|
}
|
|
562
561
|
}
|
|
563
562
|
|
|
@@ -774,11 +773,7 @@ class Puppeteer extends Helper {
|
|
|
774
773
|
}
|
|
775
774
|
|
|
776
775
|
/**
|
|
777
|
-
*
|
|
778
|
-
*
|
|
779
|
-
* ```js
|
|
780
|
-
* I.seeTitleEquals('Test title.');
|
|
781
|
-
* ```
|
|
776
|
+
* {{> seeTitleEquals }}
|
|
782
777
|
*/
|
|
783
778
|
async seeTitleEquals(text) {
|
|
784
779
|
const title = await this.page.title();
|
|
@@ -2220,16 +2215,6 @@ class Puppeteer extends Helper {
|
|
|
2220
2215
|
return this.page.waitForNavigation(opts);
|
|
2221
2216
|
}
|
|
2222
2217
|
|
|
2223
|
-
/**
|
|
2224
|
-
* {{> waitUntil }}
|
|
2225
|
-
*/
|
|
2226
|
-
async waitUntil(fn, sec = null) {
|
|
2227
|
-
console.log('This method will remove in CodeceptJS 1.4; use `waitForFunction` instead!');
|
|
2228
|
-
const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout;
|
|
2229
|
-
const context = await this._getContext();
|
|
2230
|
-
return context.waitForFunction(fn, { timeout: waitTimeout });
|
|
2231
|
-
}
|
|
2232
|
-
|
|
2233
2218
|
async waitUntilExists(locator, sec) {
|
|
2234
2219
|
console.log(`waitUntilExists deprecated:
|
|
2235
2220
|
* use 'waitForElement' to wait for element to be attached
|
package/lib/helper/REST.js
CHANGED
package/lib/helper/WebDriver.js
CHANGED
|
@@ -481,7 +481,7 @@ class WebDriver extends Helper {
|
|
|
481
481
|
try {
|
|
482
482
|
require('webdriverio');
|
|
483
483
|
} catch (e) {
|
|
484
|
-
return ['webdriverio@^
|
|
484
|
+
return ['webdriverio@^6.12.1'];
|
|
485
485
|
}
|
|
486
486
|
}
|
|
487
487
|
|
|
@@ -582,7 +582,7 @@ class WebDriver extends Helper {
|
|
|
582
582
|
this.context = this.root;
|
|
583
583
|
if (this.options.restart && !this.options.manualStart) return this._startBrowser();
|
|
584
584
|
if (!this.isRunning && !this.options.manualStart) return this._startBrowser();
|
|
585
|
-
this.$$ = this.browser.$$.bind(this.browser);
|
|
585
|
+
if (this.browser) this.$$ = this.browser.$$.bind(this.browser);
|
|
586
586
|
return this.browser;
|
|
587
587
|
}
|
|
588
588
|
|
|
@@ -875,7 +875,7 @@ class WebDriver extends Helper {
|
|
|
875
875
|
* I.defineTimeout({ implicit: 10000, pageLoad: 10000, script: 5000 });
|
|
876
876
|
* ```
|
|
877
877
|
*
|
|
878
|
-
* @param {
|
|
878
|
+
* @param {*} timeouts WebDriver timeouts object.
|
|
879
879
|
*/
|
|
880
880
|
defineTimeout(timeouts) {
|
|
881
881
|
return this._defineBrowserTimeout(this.browser, timeouts);
|
|
@@ -2353,18 +2353,6 @@ class WebDriver extends Helper {
|
|
|
2353
2353
|
return this.browser.waitUntil(async () => this.browser.execute(fn, ...args), { timeout: aSec * 1000, timeoutMsg: '' });
|
|
2354
2354
|
}
|
|
2355
2355
|
|
|
2356
|
-
/**
|
|
2357
|
-
* {{> waitUntil }}
|
|
2358
|
-
*/
|
|
2359
|
-
async waitUntil(fn, sec = null, timeoutMsg = null, interval = null) {
|
|
2360
|
-
const aSec = sec || this.options.waitForTimeout;
|
|
2361
|
-
const _interval = typeof interval === 'number' ? interval * 1000 : null;
|
|
2362
|
-
if (isWebDriver5()) {
|
|
2363
|
-
return this.browser.waitUntil(fn, aSec * 1000, timeoutMsg, _interval);
|
|
2364
|
-
}
|
|
2365
|
-
return this.browser.waitUntil(fn, { timeout: aSec * 1000, timeoutMsg, interval: _interval });
|
|
2366
|
-
}
|
|
2367
|
-
|
|
2368
2356
|
/**
|
|
2369
2357
|
* {{> switchTo }}
|
|
2370
2358
|
*/
|
package/lib/index.js
CHANGED
|
@@ -32,6 +32,8 @@ module.exports = {
|
|
|
32
32
|
within: require('./within'),
|
|
33
33
|
/** @type {typeof CodeceptJS.DataTable} */
|
|
34
34
|
dataTable: require('./data/table'),
|
|
35
|
+
/** @type {typeof CodeceptJS.DataTableArgument} */
|
|
36
|
+
dataTableArgument: require('./data/dataTableArgument'),
|
|
35
37
|
/** @type {typeof CodeceptJS.store} */
|
|
36
38
|
store: require('./store'),
|
|
37
39
|
/** @type {typeof CodeceptJS.Locator} */
|
|
@@ -21,6 +21,9 @@ class FeatureConfig {
|
|
|
21
21
|
* @returns {this}
|
|
22
22
|
*/
|
|
23
23
|
timeout(timeout) {
|
|
24
|
+
console.log(`Feature('${this.suite.title}').timeout(${timeout}) is deprecated!`);
|
|
25
|
+
console.log(`Please use Feature('${this.suite.title}', { timeout: ${timeout / 1000} }) instead`);
|
|
26
|
+
console.log('Timeout should be set in seconds');
|
|
24
27
|
this.suite.timeout(timeout);
|
|
25
28
|
return this;
|
|
26
29
|
}
|
|
@@ -11,15 +11,19 @@ const transform = require('../transform');
|
|
|
11
11
|
const parser = new Parser();
|
|
12
12
|
parser.stopAtFirstError = false;
|
|
13
13
|
|
|
14
|
-
module.exports = (text) => {
|
|
14
|
+
module.exports = (text, file) => {
|
|
15
15
|
const ast = parser.parse(text);
|
|
16
16
|
|
|
17
|
+
if (!ast.feature) {
|
|
18
|
+
throw new Error(`No 'Features' available in Gherkin '${file}' provided!`);
|
|
19
|
+
}
|
|
17
20
|
const suite = new Suite(ast.feature.name, new Context());
|
|
18
21
|
const tags = ast.feature.tags.map(t => t.name);
|
|
19
22
|
suite.title = `${suite.title} ${tags.join(' ')}`.trim();
|
|
20
23
|
suite.tags = tags || [];
|
|
21
24
|
suite.comment = ast.feature.description;
|
|
22
25
|
suite.feature = ast.feature;
|
|
26
|
+
suite.file = file;
|
|
23
27
|
suite.timeout(0);
|
|
24
28
|
|
|
25
29
|
suite.beforeEach('codeceptjs.before', () => scenario.setup(suite));
|
|
@@ -95,6 +99,7 @@ module.exports = (text) => {
|
|
|
95
99
|
const title = `${child.name} ${JSON.stringify(current)} ${tags.join(' ')}`.trim();
|
|
96
100
|
const test = new Test(title, async () => runSteps(addExampleInTable(exampleSteps, current)));
|
|
97
101
|
test.tags = suite.tags.concat(tags);
|
|
102
|
+
test.file = file;
|
|
98
103
|
suite.addTest(scenario.test(test));
|
|
99
104
|
}
|
|
100
105
|
}
|
|
@@ -104,6 +109,7 @@ module.exports = (text) => {
|
|
|
104
109
|
const title = `${child.name} ${tags.join(' ')}`.trim();
|
|
105
110
|
const test = new Test(title, async () => runSteps(child.steps));
|
|
106
111
|
test.tags = suite.tags.concat(tags);
|
|
112
|
+
test.file = file;
|
|
107
113
|
suite.addTest(scenario.test(test));
|
|
108
114
|
}
|
|
109
115
|
|
|
@@ -45,6 +45,10 @@ class ScenarioConfig {
|
|
|
45
45
|
* @returns {this}
|
|
46
46
|
*/
|
|
47
47
|
timeout(timeout) {
|
|
48
|
+
console.log(`Scenario('${this.test.title}', () => {}).timeout(${timeout}) is deprecated!`);
|
|
49
|
+
console.log(`Please use Scenario('${this.test.title}', { timeout: ${timeout / 1000} }, () => {}) instead`);
|
|
50
|
+
console.log('Timeout should be set in seconds');
|
|
51
|
+
|
|
48
52
|
this.test.timeout(timeout);
|
|
49
53
|
return this;
|
|
50
54
|
}
|
package/lib/listener/helpers.js
CHANGED