codeceptjs 2.2.0 → 2.2.1
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 +30 -1
- package/README.md +15 -22
- package/bin/codecept.js +3 -1
- package/docs/advanced.md +1 -1
- package/docs/angular.md +6 -9
- package/docs/basics.md +388 -86
- package/docs/bdd.md +4 -3
- package/docs/build/Nightmare.js +3 -0
- package/docs/build/Polly.js +26 -12
- package/docs/build/Puppeteer.js +14 -13
- package/docs/build/TestCafe.js +101 -2
- package/docs/build/WebDriver.js +53 -52
- package/docs/changelog.md +86 -57
- package/docs/detox.md +235 -0
- package/docs/helpers/Detox.md +579 -0
- package/docs/helpers/Polly.md +13 -3
- package/docs/helpers/Puppeteer.md +155 -156
- package/docs/helpers/TestCafe.md +53 -0
- package/docs/helpers/WebDriver.md +209 -204
- package/docs/locators.md +2 -0
- package/docs/mobile.md +5 -1
- package/docs/puppeteer.md +59 -13
- package/docs/quickstart.md +47 -12
- package/docs/testcafe.md +157 -0
- package/docs/webdriver.md +453 -0
- package/lib/command/definitions.js +152 -7
- package/lib/command/gherkin/snippets.js +19 -8
- package/lib/command/init.js +30 -22
- package/lib/command/utils.js +1 -1
- package/lib/container.js +36 -10
- package/lib/data/dataScenarioConfig.js +18 -0
- package/lib/helper/Nightmare.js +3 -0
- package/lib/helper/Polly.js +26 -12
- package/lib/helper/Puppeteer.js +14 -13
- package/lib/helper/TestCafe.js +72 -2
- package/lib/helper/WebDriver.js +53 -52
- package/lib/helper/testcafe/testcafe-utils.js +3 -2
- package/lib/interfaces/scenarioConfig.js +2 -2
- package/lib/listener/config.js +2 -2
- package/lib/plugin/allure.js +3 -0
- package/lib/step.js +5 -2
- package/lib/ui.js +1 -1
- package/lib/utils.js +13 -21
- package/package.json +14 -12
package/docs/build/Puppeteer.js
CHANGED
|
@@ -57,12 +57,13 @@ const consoleLogStore = new Console();
|
|
|
57
57
|
* * `keepCookies`: (optional, default: false) - keep cookies between tests when `restart` is set to false.
|
|
58
58
|
* * `waitForAction`: (optional) how long to wait after click, doubleClick or PressKey actions in ms. Default: 100.
|
|
59
59
|
* * `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.
|
|
60
|
+
* * `pressKeyDelay`: (optional, default: '10'). Delay between key presses in ms. Used when calling Puppeteers page.type(...) in fillField/appendField
|
|
60
61
|
* * `getPageTimeout` (optional, default: '0') config option to set maximum navigation time in milliseconds.
|
|
61
62
|
* * `waitForTimeout`: (optional) default wait* timeout in ms. Default: 1000.
|
|
62
63
|
* * `windowSize`: (optional) default window size. Set a dimension like `640x480`.
|
|
63
64
|
* * `userAgent`: (optional) user-agent string.
|
|
64
65
|
* * `manualStart`: (optional, default: false) - do not start browser before a test, start it manually inside a helper with `this.helpers["Puppeteer"]._startBrowser()`.
|
|
65
|
-
* * `browser`: (optional, default: chrome) - can be changed to `firefox` when using [puppeteer-firefox](
|
|
66
|
+
* * `browser`: (optional, default: chrome) - can be changed to `firefox` when using [puppeteer-firefox](https://codecept.io/helpers/Puppeteer-firefox).
|
|
66
67
|
* * `chrome`: (optional) pass additional [Puppeteer run options](https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md#puppeteerlaunchoptions).
|
|
67
68
|
*
|
|
68
69
|
*
|
|
@@ -159,6 +160,7 @@ class Puppeteer extends Helper {
|
|
|
159
160
|
browser: 'chrome',
|
|
160
161
|
waitForAction: 100,
|
|
161
162
|
waitForTimeout: 1000,
|
|
163
|
+
pressKeyDelay: 10,
|
|
162
164
|
fullPageScreenshots: false,
|
|
163
165
|
disableScreenshots: false,
|
|
164
166
|
uniqueScreenshotNames: false,
|
|
@@ -189,6 +191,9 @@ class Puppeteer extends Helper {
|
|
|
189
191
|
static _config() {
|
|
190
192
|
return [
|
|
191
193
|
{ name: 'url', message: 'Base url of site to be tested', default: 'http://localhost' },
|
|
194
|
+
{
|
|
195
|
+
name: 'show', message: 'Show browser window', default: true, type: 'confirm',
|
|
196
|
+
},
|
|
192
197
|
];
|
|
193
198
|
}
|
|
194
199
|
|
|
@@ -643,7 +648,7 @@ class Puppeteer extends Helper {
|
|
|
643
648
|
* {--end--}
|
|
644
649
|
*/
|
|
645
650
|
scrollPageToTop() {
|
|
646
|
-
return this.
|
|
651
|
+
return this.executeScript(() => {
|
|
647
652
|
window.scrollTo(0, 0);
|
|
648
653
|
});
|
|
649
654
|
}
|
|
@@ -657,7 +662,7 @@ class Puppeteer extends Helper {
|
|
|
657
662
|
* {--end--}
|
|
658
663
|
*/
|
|
659
664
|
scrollPageToBottom() {
|
|
660
|
-
return this.
|
|
665
|
+
return this.executeScript(() => {
|
|
661
666
|
const body = document.body;
|
|
662
667
|
const html = document.documentElement;
|
|
663
668
|
window.scrollTo(0, Math.max(
|
|
@@ -687,20 +692,16 @@ class Puppeteer extends Helper {
|
|
|
687
692
|
offsetX = locator;
|
|
688
693
|
locator = null;
|
|
689
694
|
}
|
|
690
|
-
|
|
691
|
-
let y = 0;
|
|
695
|
+
|
|
692
696
|
if (locator) {
|
|
693
697
|
const els = await this._locate(locator);
|
|
694
698
|
assertElementExists(els, locator, 'Element');
|
|
695
699
|
await els[0]._scrollIntoViewIfNeeded();
|
|
696
700
|
const elementCoordinates = await els[0]._clickablePoint();
|
|
697
|
-
x
|
|
698
|
-
|
|
701
|
+
await this.executeScript((x, y) => window.scrollBy(x, y), elementCoordinates.x + offsetX, elementCoordinates.y + offsetY);
|
|
702
|
+
} else {
|
|
703
|
+
await this.executeScript((x, y) => window.scrollTo(x, y), offsetX, offsetY);
|
|
699
704
|
}
|
|
700
|
-
|
|
701
|
-
await this.page.evaluate((x, y) => {
|
|
702
|
-
window.scrollTo(x, y);
|
|
703
|
-
}, x + offsetX, y + offsetY);
|
|
704
705
|
return this._waitForAction();
|
|
705
706
|
}
|
|
706
707
|
|
|
@@ -1335,7 +1336,7 @@ class Puppeteer extends Helper {
|
|
|
1335
1336
|
} else if (editable) {
|
|
1336
1337
|
await this._evaluateHandeInContext(el => el.innerHTML = '', el);
|
|
1337
1338
|
}
|
|
1338
|
-
await el.type(value.toString(), { delay:
|
|
1339
|
+
await el.type(value.toString(), { delay: this.options.pressKeyDelay });
|
|
1339
1340
|
return this._waitForAction();
|
|
1340
1341
|
}
|
|
1341
1342
|
|
|
@@ -1372,7 +1373,7 @@ class Puppeteer extends Helper {
|
|
|
1372
1373
|
const els = await findFields.call(this, field);
|
|
1373
1374
|
assertElementExists(els, field, 'Field');
|
|
1374
1375
|
await els[0].press('End');
|
|
1375
|
-
await els[0].type(value, { delay:
|
|
1376
|
+
await els[0].type(value, { delay: this.options.pressKeyDelay });
|
|
1376
1377
|
return this._waitForAction();
|
|
1377
1378
|
}
|
|
1378
1379
|
|
package/docs/build/TestCafe.js
CHANGED
|
@@ -124,6 +124,10 @@ class TestCafe extends Helper {
|
|
|
124
124
|
static _config() {
|
|
125
125
|
return [
|
|
126
126
|
{ name: 'url', message: 'Base url of site to be tested', default: 'http://localhost' },
|
|
127
|
+
{ name: 'browser', message: 'Browser to be used', default: 'chrome' },
|
|
128
|
+
{
|
|
129
|
+
name: 'show', message: 'Show browser window', default: true, type: 'confirm',
|
|
130
|
+
},
|
|
127
131
|
];
|
|
128
132
|
}
|
|
129
133
|
|
|
@@ -248,7 +252,7 @@ class TestCafe extends Helper {
|
|
|
248
252
|
async _withinBegin(locator) {
|
|
249
253
|
const els = await this._locate(locator);
|
|
250
254
|
assertElementExists(els, locator);
|
|
251
|
-
this.context = els.nth(0);
|
|
255
|
+
this.context = await els.nth(0);
|
|
252
256
|
}
|
|
253
257
|
|
|
254
258
|
async _withinEnd() {
|
|
@@ -943,7 +947,7 @@ class TestCafe extends Helper {
|
|
|
943
947
|
const el = await els.nth(0);
|
|
944
948
|
|
|
945
949
|
return this.t
|
|
946
|
-
.expect(el.value).eql(value)
|
|
950
|
+
.expect(await el.value).eql(value)
|
|
947
951
|
.catch(mapError);
|
|
948
952
|
}
|
|
949
953
|
|
|
@@ -1198,6 +1202,94 @@ class TestCafe extends Helper {
|
|
|
1198
1202
|
return ClientFunction(() => document.location.href).with({ boundTestRun: this.t })();
|
|
1199
1203
|
}
|
|
1200
1204
|
|
|
1205
|
+
/**
|
|
1206
|
+
* Retrieves a page scroll position and returns it to test.
|
|
1207
|
+
* Resumes test execution, so **should be used inside an async function with `await`** operator.
|
|
1208
|
+
*
|
|
1209
|
+
* ```js
|
|
1210
|
+
* let { x, y } = await I.grabPageScrollPosition();
|
|
1211
|
+
* ```
|
|
1212
|
+
*
|
|
1213
|
+
* @returns {Promise<object>} scroll position
|
|
1214
|
+
* {--end--}
|
|
1215
|
+
*/
|
|
1216
|
+
async grabPageScrollPosition() {
|
|
1217
|
+
return ClientFunction(() => ({ x: window.pageXOffset, y: window.pageYOffset })).with({ boundTestRun: this.t })();
|
|
1218
|
+
}
|
|
1219
|
+
|
|
1220
|
+
/**
|
|
1221
|
+
* Scroll page to the top.
|
|
1222
|
+
*
|
|
1223
|
+
* ```js
|
|
1224
|
+
* I.scrollPageToTop();
|
|
1225
|
+
* ```
|
|
1226
|
+
* {--end--}
|
|
1227
|
+
*/
|
|
1228
|
+
scrollPageToTop() {
|
|
1229
|
+
return ClientFunction(() => window.scrollTo(0, 0)).with({ boundTestRun: this.t })().catch(mapError);
|
|
1230
|
+
}
|
|
1231
|
+
|
|
1232
|
+
/**
|
|
1233
|
+
* Scroll page to the bottom.
|
|
1234
|
+
*
|
|
1235
|
+
* ```js
|
|
1236
|
+
* I.scrollPageToBottom();
|
|
1237
|
+
* ```
|
|
1238
|
+
* {--end--}
|
|
1239
|
+
*/
|
|
1240
|
+
scrollPageToBottom() {
|
|
1241
|
+
return ClientFunction(() => {
|
|
1242
|
+
const body = document.body;
|
|
1243
|
+
const html = document.documentElement;
|
|
1244
|
+
window.scrollTo(0, Math.max(
|
|
1245
|
+
body.scrollHeight, body.offsetHeight,
|
|
1246
|
+
html.clientHeight, html.scrollHeight, html.offsetHeight,
|
|
1247
|
+
));
|
|
1248
|
+
}).with({ boundTestRun: this.t })().catch(mapError);
|
|
1249
|
+
}
|
|
1250
|
+
|
|
1251
|
+
/**
|
|
1252
|
+
* Scrolls to element matched by locator.
|
|
1253
|
+
* Extra shift can be set with offsetX and offsetY options.
|
|
1254
|
+
*
|
|
1255
|
+
* ```js
|
|
1256
|
+
* I.scrollTo('footer');
|
|
1257
|
+
* I.scrollTo('#submit', 5, 5);
|
|
1258
|
+
* ```
|
|
1259
|
+
*
|
|
1260
|
+
* @param {string|object} locator located by CSS|XPath|strict locator.
|
|
1261
|
+
* @param {number} offsetX (optional, `0` by default) X-axis offset.
|
|
1262
|
+
* @param {number} offsetY (optional, `0` by default) Y-axis offset.
|
|
1263
|
+
* {--end--}
|
|
1264
|
+
*/
|
|
1265
|
+
async scrollTo(locator, offsetX = 0, offsetY = 0) {
|
|
1266
|
+
if (typeof locator === 'number' && typeof offsetX === 'number') {
|
|
1267
|
+
offsetY = offsetX;
|
|
1268
|
+
offsetX = locator;
|
|
1269
|
+
locator = null;
|
|
1270
|
+
}
|
|
1271
|
+
|
|
1272
|
+
const scrollBy = ClientFunction((offset) => {
|
|
1273
|
+
if (window && window.scrollBy && offset) {
|
|
1274
|
+
window.scrollBy(offset.x, offset.y);
|
|
1275
|
+
}
|
|
1276
|
+
}).with({ boundTestRun: this.t });
|
|
1277
|
+
|
|
1278
|
+
if (locator) {
|
|
1279
|
+
const els = await this._locate(locator);
|
|
1280
|
+
assertElementExists(els, locator, 'Element');
|
|
1281
|
+
const el = await els.nth(0);
|
|
1282
|
+
const x = (await el.offsetLeft) + offsetX;
|
|
1283
|
+
const y = (await el.offsetTop) + offsetY;
|
|
1284
|
+
|
|
1285
|
+
return scrollBy({ x, y }).catch(mapError);
|
|
1286
|
+
}
|
|
1287
|
+
|
|
1288
|
+
const x = offsetX;
|
|
1289
|
+
const y = offsetY;
|
|
1290
|
+
return scrollBy({ x, y }).catch(mapError);
|
|
1291
|
+
}
|
|
1292
|
+
|
|
1201
1293
|
/**
|
|
1202
1294
|
* Switches frame or in case of null locator reverts to parent.
|
|
1203
1295
|
*
|
|
@@ -1210,6 +1302,13 @@ class TestCafe extends Helper {
|
|
|
1210
1302
|
* {--end--}
|
|
1211
1303
|
*/
|
|
1212
1304
|
async switchTo(locator) {
|
|
1305
|
+
if (Number.isInteger(locator)) {
|
|
1306
|
+
throw new Error('Not supported switching to iframe by number');
|
|
1307
|
+
}
|
|
1308
|
+
|
|
1309
|
+
if (!locator) {
|
|
1310
|
+
return this.t.switchToMainWindow();
|
|
1311
|
+
}
|
|
1213
1312
|
return this.t.switchToIframe(findElements.call(this, this.context, locator));
|
|
1214
1313
|
}
|
|
1215
1314
|
|
package/docs/build/WebDriver.js
CHANGED
|
@@ -15,7 +15,6 @@ const {
|
|
|
15
15
|
chunkArray,
|
|
16
16
|
convertCssPropertiesToCamelCase,
|
|
17
17
|
screenshotOutputFolder,
|
|
18
|
-
fileToBase64Zip,
|
|
19
18
|
} = require('../utils');
|
|
20
19
|
const {
|
|
21
20
|
isColorProperty,
|
|
@@ -61,15 +60,15 @@ const webRoot = 'body';
|
|
|
61
60
|
*
|
|
62
61
|
* Example:
|
|
63
62
|
*
|
|
64
|
-
* ```
|
|
63
|
+
* ```js
|
|
65
64
|
* {
|
|
66
|
-
*
|
|
67
|
-
*
|
|
68
|
-
*
|
|
69
|
-
*
|
|
70
|
-
*
|
|
71
|
-
*
|
|
72
|
-
*
|
|
65
|
+
* helpers: {
|
|
66
|
+
* WebDriver : {
|
|
67
|
+
* smartWait: 5000,
|
|
68
|
+
* browser: "chrome",
|
|
69
|
+
* restart: false,
|
|
70
|
+
* windowSize: "maximize",
|
|
71
|
+
* timeouts: {
|
|
73
72
|
* "script": 60000,
|
|
74
73
|
* "page load": 10000
|
|
75
74
|
* }
|
|
@@ -83,15 +82,15 @@ const webRoot = 'body';
|
|
|
83
82
|
*
|
|
84
83
|
* ### Headless Chrome
|
|
85
84
|
*
|
|
86
|
-
* ```
|
|
85
|
+
* ```js
|
|
87
86
|
* {
|
|
88
|
-
*
|
|
89
|
-
*
|
|
90
|
-
*
|
|
91
|
-
*
|
|
92
|
-
*
|
|
93
|
-
*
|
|
94
|
-
*
|
|
87
|
+
* helpers: {
|
|
88
|
+
* WebDriver : {
|
|
89
|
+
* url: "http://localhost",
|
|
90
|
+
* browser: "chrome",
|
|
91
|
+
* desiredCapabilities: {
|
|
92
|
+
* chromeOptions: {
|
|
93
|
+
* args: [ "--headless", "--disable-gpu", "--window-size=800,600" ]
|
|
95
94
|
* }
|
|
96
95
|
* }
|
|
97
96
|
* }
|
|
@@ -103,14 +102,14 @@ const webRoot = 'body';
|
|
|
103
102
|
*
|
|
104
103
|
* Additional configuration params can be used from [IE options](https://seleniumhq.github.io/selenium/docs/api/rb/Selenium/WebDriver/IE/Options.html)
|
|
105
104
|
*
|
|
106
|
-
* ```
|
|
105
|
+
* ```js
|
|
107
106
|
* {
|
|
108
|
-
*
|
|
109
|
-
*
|
|
110
|
-
*
|
|
111
|
-
*
|
|
112
|
-
*
|
|
113
|
-
*
|
|
107
|
+
* helpers: {
|
|
108
|
+
* WebDriver : {
|
|
109
|
+
* url: "http://localhost",
|
|
110
|
+
* browser: "internet explorer",
|
|
111
|
+
* desiredCapabilities: {
|
|
112
|
+
* ieOptions: {
|
|
114
113
|
* "ie.browserCommandLineSwitches": "-private",
|
|
115
114
|
* "ie.usePerProcessProxy": true,
|
|
116
115
|
* "ie.ensureCleanSession": true,
|
|
@@ -123,15 +122,18 @@ const webRoot = 'body';
|
|
|
123
122
|
*
|
|
124
123
|
* ### Selenoid Options
|
|
125
124
|
*
|
|
126
|
-
*
|
|
125
|
+
* [Selenoid](https://aerokube.com/selenoid/latest/) is a modern way to run Selenium inside Docker containers.
|
|
126
|
+
* Selenoid is easy to set up and provides more features than original Selenium Server. Use `selenoidOptions` to set Selenoid capabilities
|
|
127
|
+
*
|
|
128
|
+
* ```js
|
|
127
129
|
* {
|
|
128
|
-
*
|
|
129
|
-
*
|
|
130
|
-
*
|
|
131
|
-
*
|
|
132
|
-
*
|
|
133
|
-
*
|
|
134
|
-
*
|
|
130
|
+
* helpers: {
|
|
131
|
+
* WebDriver : {
|
|
132
|
+
* url: "http://localhost",
|
|
133
|
+
* browser: "chrome",
|
|
134
|
+
* desiredCapabilities: {
|
|
135
|
+
* selenoidOptions: {
|
|
136
|
+
* enableVNC: true,
|
|
135
137
|
* }
|
|
136
138
|
* }
|
|
137
139
|
* }
|
|
@@ -139,17 +141,17 @@ const webRoot = 'body';
|
|
|
139
141
|
* }
|
|
140
142
|
* ```
|
|
141
143
|
*
|
|
142
|
-
* ### Connect
|
|
144
|
+
* ### Connect Through proxy
|
|
143
145
|
*
|
|
144
146
|
* CodeceptJS also provides flexible options when you want to execute tests to Selenium servers through proxy. You will
|
|
145
147
|
* need to update the `helpers.WebDriver.capabilities.proxy` key.
|
|
146
148
|
*
|
|
147
149
|
* ```js
|
|
148
150
|
* {
|
|
149
|
-
*
|
|
150
|
-
*
|
|
151
|
-
*
|
|
152
|
-
*
|
|
151
|
+
* helpers: {
|
|
152
|
+
* WebDriver: {
|
|
153
|
+
* capabilities: {
|
|
154
|
+
* proxy: {
|
|
153
155
|
* "proxyType": "manual|pac",
|
|
154
156
|
* "proxyAutoconfigUrl": "URL TO PAC FILE",
|
|
155
157
|
* "httpProxy": "PROXY SERVER",
|
|
@@ -169,10 +171,10 @@ const webRoot = 'body';
|
|
|
169
171
|
*
|
|
170
172
|
* ```js
|
|
171
173
|
* {
|
|
172
|
-
*
|
|
173
|
-
*
|
|
174
|
-
*
|
|
175
|
-
*
|
|
174
|
+
* helpers: {
|
|
175
|
+
* WebDriver: {
|
|
176
|
+
* capabilities: {
|
|
177
|
+
* proxy: {
|
|
176
178
|
* "proxyType": "manual",
|
|
177
179
|
* "httpProxy": "http://corporate.proxy:8080",
|
|
178
180
|
* "socksUsername": "codeceptjs",
|
|
@@ -199,12 +201,12 @@ const webRoot = 'body';
|
|
|
199
201
|
*
|
|
200
202
|
* ```js
|
|
201
203
|
* {
|
|
202
|
-
*
|
|
203
|
-
*
|
|
204
|
-
*
|
|
205
|
-
*
|
|
206
|
-
*
|
|
207
|
-
*
|
|
204
|
+
* helpers:{
|
|
205
|
+
* WebDriver: {
|
|
206
|
+
* url: "YOUR_DESIRED_HOST",
|
|
207
|
+
* user: "YOUR_BROWSERSTACK_USER",
|
|
208
|
+
* key: "YOUR_BROWSERSTACK_KEY",
|
|
209
|
+
* capabilities: {
|
|
208
210
|
* "browserName": "chrome",
|
|
209
211
|
*
|
|
210
212
|
* // only set this if you're using BrowserStackLocal to test a local domain
|
|
@@ -318,8 +320,8 @@ const webRoot = 'body';
|
|
|
318
320
|
*
|
|
319
321
|
* ```js
|
|
320
322
|
* {
|
|
321
|
-
*
|
|
322
|
-
*
|
|
323
|
+
* helpers: {
|
|
324
|
+
* WebDriver: {
|
|
323
325
|
* "multiremote": {
|
|
324
326
|
* "MyChrome": {
|
|
325
327
|
* "desiredCapabilities": {
|
|
@@ -973,12 +975,11 @@ class WebDriver extends Helper {
|
|
|
973
975
|
assertElementExists(res, locator, 'File field');
|
|
974
976
|
const el = usingFirstElement(res);
|
|
975
977
|
|
|
976
|
-
// Remote
|
|
978
|
+
// Remote Upload (when running Selenium Server)
|
|
977
979
|
if (this.options.remoteFileUpload) {
|
|
978
|
-
const fileCompressed = await fileToBase64Zip(file);
|
|
979
980
|
try {
|
|
980
981
|
this.debugSection('File', 'Uploading file to remote server');
|
|
981
|
-
file = await this.browser.uploadFile(
|
|
982
|
+
file = await this.browser.uploadFile(file);
|
|
982
983
|
} catch (err) {
|
|
983
984
|
throw new Error(`File can't be transferred to remote server. Set \`remoteFileUpload: false\` in config to upload file locally.\n${err.message}`);
|
|
984
985
|
}
|