codeceptjs 3.5.11 → 3.5.12-beta.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/README.md +3 -3
- package/docs/build/Appium.js +35 -35
- package/docs/build/Nightmare.js +50 -50
- package/docs/build/Playwright.js +100 -72
- package/docs/build/Protractor.js +59 -59
- package/docs/build/Puppeteer.js +96 -69
- package/docs/build/TestCafe.js +48 -48
- package/docs/build/WebDriver.js +223 -105
- package/docs/helpers/Playwright.md +15 -0
- package/docs/helpers/Puppeteer.md +15 -0
- package/docs/helpers/WebDriver.md +340 -266
- package/docs/locators.md +9 -1
- package/docs/webapi/waitForNumberOfTabs.mustache +9 -0
- package/docs/webdriver.md +52 -6
- package/lib/command/run-multiple.js +3 -1
- package/lib/command/run-workers.js +32 -1
- package/lib/command/workers/runTests.js +2 -2
- package/lib/css2xpath/js/css_to_xpath.js +20 -0
- package/lib/css2xpath/js/expression.js +23 -0
- package/lib/css2xpath/js/renderer.js +239 -0
- package/lib/helper/Playwright.js +21 -2
- package/lib/helper/Puppeteer.js +18 -0
- package/lib/helper/WebDriver.js +140 -31
- package/lib/locator.js +31 -4
- package/lib/plugin/retryFailedStep.js +5 -1
- package/lib/plugin/retryTo.js +2 -2
- package/package.json +24 -18
- package/typings/index.d.ts +9 -6
- package/typings/promiseBasedTypes.d.ts +84 -1
- package/typings/types.d.ts +102 -2
package/lib/helper/WebDriver.js
CHANGED
|
@@ -63,6 +63,8 @@ const webRoot = 'body';
|
|
|
63
63
|
* @prop {boolean} [manualStart=false] - do not start browser before a test, start it manually inside a helper with `this.helpers["WebDriver"]._startBrowser()`.
|
|
64
64
|
* @prop {object} [timeouts] [WebDriver timeouts](http://webdriver.io/docs/timeouts.html) defined as hash.
|
|
65
65
|
* @prop {boolean} [highlightElement] - highlight the interacting elements. Default: false. Note: only activate under verbose mode (--verbose).
|
|
66
|
+
* @prop {string} [logLevel=silent] - level of logging verbosity. Default: silent. Options: trace | debug | info | warn | error | silent. More info: https://webdriver.io/docs/configuration/#loglevel
|
|
67
|
+
* @prop {boolean} [devtoolsProtocol=false] - enable devtools protocol. Default: false. More info: https://webdriver.io/docs/automationProtocols/#devtools-protocol.
|
|
66
68
|
*/
|
|
67
69
|
const config = {};
|
|
68
70
|
|
|
@@ -72,6 +74,13 @@ const config = {};
|
|
|
72
74
|
*
|
|
73
75
|
* WebDriver requires Selenium Server and ChromeDriver/GeckoDriver to be installed. Those tools can be easily installed via NPM. Please check [Testing with WebDriver](https://codecept.io/webdriver/#testing-with-webdriver) for more details.
|
|
74
76
|
*
|
|
77
|
+
* With the release of WebdriverIO version v8.14.0, and onwards, all driver management hassles are now a thing of the past 🙌. Read more [here](https://webdriver.io/blog/2023/07/31/driver-management/).
|
|
78
|
+
* One of the significant advantages of this update is that you can now get rid of any driver services you previously had to manage, such as
|
|
79
|
+
* `wdio-chromedriver-service`, `wdio-geckodriver-service`, `wdio-edgedriver-service`, `wdio-safaridriver-service`, and even `@wdio/selenium-standalone-service`.
|
|
80
|
+
*
|
|
81
|
+
* For those who require custom driver options, fear not; WebDriver Helper allows you to pass in driver options through custom WebDriver configuration.
|
|
82
|
+
* If you have a custom grid, use a cloud service, or prefer to run your own driver, there's no need to worry since WebDriver Helper will only start a driver when there are no other connection information settings like hostname or port specified.
|
|
83
|
+
*
|
|
75
84
|
* <!-- configuration -->
|
|
76
85
|
*
|
|
77
86
|
* Example:
|
|
@@ -93,6 +102,28 @@ const config = {};
|
|
|
93
102
|
* }
|
|
94
103
|
* ```
|
|
95
104
|
*
|
|
105
|
+
* Testing Chrome locally is now more convenient than ever. You can define a browser channel, and WebDriver Helper will take care of downloading the specified browser version for you.
|
|
106
|
+
* For example:
|
|
107
|
+
*
|
|
108
|
+
* ```js
|
|
109
|
+
* {
|
|
110
|
+
* helpers: {
|
|
111
|
+
* WebDriver : {
|
|
112
|
+
* smartWait: 5000,
|
|
113
|
+
* browser: "chrome",
|
|
114
|
+
* browserVersion: '116.0.5793.0', // or 'stable', 'beta', 'dev' or 'canary'
|
|
115
|
+
* restart: false,
|
|
116
|
+
* windowSize: "maximize",
|
|
117
|
+
* timeouts: {
|
|
118
|
+
* "script": 60000,
|
|
119
|
+
* "page load": 10000
|
|
120
|
+
* }
|
|
121
|
+
* }
|
|
122
|
+
* }
|
|
123
|
+
* }
|
|
124
|
+
* ```
|
|
125
|
+
*
|
|
126
|
+
*
|
|
96
127
|
* Example with basic authentication
|
|
97
128
|
* ```js
|
|
98
129
|
* {
|
|
@@ -133,6 +164,25 @@ const config = {};
|
|
|
133
164
|
* }
|
|
134
165
|
* ```
|
|
135
166
|
*
|
|
167
|
+
* ### Running with devtools protocol
|
|
168
|
+
*
|
|
169
|
+
* ```js
|
|
170
|
+
* {
|
|
171
|
+
* helpers: {
|
|
172
|
+
* WebDriver : {
|
|
173
|
+
* url: "http://localhost",
|
|
174
|
+
* browser: "chrome",
|
|
175
|
+
* devtoolsProtocol: true,
|
|
176
|
+
* desiredCapabilities: {
|
|
177
|
+
* chromeOptions: {
|
|
178
|
+
* args: [ "--headless", "--disable-gpu", "--no-sandbox" ]
|
|
179
|
+
* }
|
|
180
|
+
* }
|
|
181
|
+
* }
|
|
182
|
+
* }
|
|
183
|
+
* }
|
|
184
|
+
* ```
|
|
185
|
+
*
|
|
136
186
|
* ### Internet Explorer
|
|
137
187
|
*
|
|
138
188
|
* Additional configuration params can be used from [IE options](https://seleniumhq.github.io/selenium/docs/api/rb/Selenium/WebDriver/IE/Options.html)
|
|
@@ -415,7 +465,6 @@ class WebDriver extends Helper {
|
|
|
415
465
|
_validateConfig(config) {
|
|
416
466
|
const defaults = {
|
|
417
467
|
logLevel: 'silent',
|
|
418
|
-
path: '/wd/hub',
|
|
419
468
|
// codeceptjs
|
|
420
469
|
remoteFileUpload: true,
|
|
421
470
|
smartWait: 0,
|
|
@@ -435,12 +484,17 @@ class WebDriver extends Helper {
|
|
|
435
484
|
// override defaults with config
|
|
436
485
|
config = Object.assign(defaults, config);
|
|
437
486
|
|
|
438
|
-
if (
|
|
487
|
+
if (config.host) {
|
|
488
|
+
// webdriverio spec
|
|
489
|
+
config.hostname = config.host;
|
|
490
|
+
config.path = '/wd/hub';
|
|
491
|
+
}
|
|
439
492
|
config.baseUrl = config.url || config.baseUrl;
|
|
440
493
|
if (config.desiredCapabilities && Object.keys(config.desiredCapabilities).length) {
|
|
441
494
|
config.capabilities = config.desiredCapabilities;
|
|
442
495
|
}
|
|
443
496
|
config.capabilities.browserName = config.browser || config.capabilities.browserName;
|
|
497
|
+
config.capabilities.browserVersion = config.browserVersion || config.capabilities.browserVersion;
|
|
444
498
|
if (config.capabilities.chromeOptions) {
|
|
445
499
|
config.capabilities['goog:chromeOptions'] = config.capabilities.chromeOptions;
|
|
446
500
|
delete config.capabilities.chromeOptions;
|
|
@@ -542,6 +596,10 @@ class WebDriver extends Helper {
|
|
|
542
596
|
delete this.options.capabilities.hostname;
|
|
543
597
|
delete this.options.capabilities.port;
|
|
544
598
|
delete this.options.capabilities.path;
|
|
599
|
+
if (this.options.devtoolsProtocol) {
|
|
600
|
+
if (!['chrome', 'chromium'].includes(this.options.browser.toLowerCase())) throw Error('The devtools protocol is only working with Chrome or Chromium');
|
|
601
|
+
this.options.automationProtocol = 'devtools';
|
|
602
|
+
}
|
|
545
603
|
this.browser = await webdriverio.remote(this.options);
|
|
546
604
|
}
|
|
547
605
|
} catch (err) {
|
|
@@ -1043,7 +1101,8 @@ class WebDriver extends Helper {
|
|
|
1043
1101
|
assertElementExists(res, field, 'Field');
|
|
1044
1102
|
const elem = usingFirstElement(res);
|
|
1045
1103
|
highlightActiveElement.call(this, elem);
|
|
1046
|
-
|
|
1104
|
+
await elem.clearValue();
|
|
1105
|
+
await elem.setValue(value.toString());
|
|
1047
1106
|
}
|
|
1048
1107
|
|
|
1049
1108
|
/**
|
|
@@ -1055,6 +1114,10 @@ class WebDriver extends Helper {
|
|
|
1055
1114
|
assertElementExists(res, field, 'Field');
|
|
1056
1115
|
const elem = usingFirstElement(res);
|
|
1057
1116
|
highlightActiveElement.call(this, elem);
|
|
1117
|
+
if (this.options.automationProtocol) {
|
|
1118
|
+
const curentValue = await elem.getValue();
|
|
1119
|
+
return elem.setValue(curentValue + value.toString());
|
|
1120
|
+
}
|
|
1058
1121
|
return elem.addValue(value.toString());
|
|
1059
1122
|
}
|
|
1060
1123
|
|
|
@@ -1067,6 +1130,9 @@ class WebDriver extends Helper {
|
|
|
1067
1130
|
assertElementExists(res, field, 'Field');
|
|
1068
1131
|
const elem = usingFirstElement(res);
|
|
1069
1132
|
highlightActiveElement.call(this, elem);
|
|
1133
|
+
if (this.options.automationProtocol) {
|
|
1134
|
+
return elem.setValue('');
|
|
1135
|
+
}
|
|
1070
1136
|
return elem.clearValue(getElementId(elem));
|
|
1071
1137
|
}
|
|
1072
1138
|
|
|
@@ -1120,7 +1186,7 @@ class WebDriver extends Helper {
|
|
|
1120
1186
|
const el = usingFirstElement(res);
|
|
1121
1187
|
|
|
1122
1188
|
// Remote Upload (when running Selenium Server)
|
|
1123
|
-
if (this.options.remoteFileUpload) {
|
|
1189
|
+
if (this.options.remoteFileUpload && !this.options.automationProtocol) {
|
|
1124
1190
|
try {
|
|
1125
1191
|
this.debugSection('File', 'Uploading file to remote server');
|
|
1126
1192
|
file = await this.browser.uploadFile(file);
|
|
@@ -1498,35 +1564,33 @@ class WebDriver extends Helper {
|
|
|
1498
1564
|
async seeCssPropertiesOnElements(locator, cssProperties) {
|
|
1499
1565
|
const res = await this._locate(locator);
|
|
1500
1566
|
assertElementExists(res, locator);
|
|
1567
|
+
|
|
1568
|
+
const cssPropertiesCamelCase = convertCssPropertiesToCamelCase(cssProperties);
|
|
1501
1569
|
const elemAmount = res.length;
|
|
1570
|
+
let props = [];
|
|
1502
1571
|
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
const
|
|
1506
|
-
if (isColorProperty(prop)
|
|
1507
|
-
|
|
1572
|
+
for (const element of res) {
|
|
1573
|
+
for (const prop of Object.keys(cssProperties)) {
|
|
1574
|
+
const cssProp = await this.grabCssPropertyFrom(locator, prop);
|
|
1575
|
+
if (isColorProperty(prop)) {
|
|
1576
|
+
props.push(convertColorToRGBA(cssProp));
|
|
1577
|
+
} else {
|
|
1578
|
+
props.push(cssProp);
|
|
1508
1579
|
}
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
});
|
|
1512
|
-
|
|
1513
|
-
const cssPropertiesCamelCase = convertCssPropertiesToCamelCase(cssProperties);
|
|
1580
|
+
}
|
|
1581
|
+
}
|
|
1514
1582
|
|
|
1515
1583
|
const values = Object.keys(cssPropertiesCamelCase).map(key => cssPropertiesCamelCase[key]);
|
|
1516
1584
|
if (!Array.isArray(props)) props = [props];
|
|
1517
1585
|
let chunked = chunkArray(props, values.length);
|
|
1518
1586
|
chunked = chunked.filter((val) => {
|
|
1519
1587
|
for (let i = 0; i < val.length; ++i) {
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
if (_acutal !== _expected) return false;
|
|
1588
|
+
// eslint-disable-next-line eqeqeq
|
|
1589
|
+
if (val[i] != values[i]) return false;
|
|
1523
1590
|
}
|
|
1524
1591
|
return true;
|
|
1525
1592
|
});
|
|
1526
|
-
return assert.
|
|
1527
|
-
chunked.length === elemAmount,
|
|
1528
|
-
`expected all elements (${(new Locator(locator))}) to have CSS property ${JSON.stringify(cssProperties)}`,
|
|
1529
|
-
);
|
|
1593
|
+
return equals(`all elements (${(new Locator(locator))}) to have CSS property ${JSON.stringify(cssProperties)}`).assert(chunked.length, elemAmount);
|
|
1530
1594
|
}
|
|
1531
1595
|
|
|
1532
1596
|
/**
|
|
@@ -1546,9 +1610,9 @@ class WebDriver extends Helper {
|
|
|
1546
1610
|
let chunked = chunkArray(attrs, values.length);
|
|
1547
1611
|
chunked = chunked.filter((val) => {
|
|
1548
1612
|
for (let i = 0; i < val.length; ++i) {
|
|
1549
|
-
const
|
|
1613
|
+
const _actual = Number.isNaN(val[i]) || (typeof values[i]) === 'string' ? val[i] : Number.parseInt(val[i], 10);
|
|
1550
1614
|
const _expected = Number.isNaN(values[i]) || (typeof values[i]) === 'string' ? values[i] : Number.parseInt(values[i], 10);
|
|
1551
|
-
if (
|
|
1615
|
+
if (_actual !== _expected) return false;
|
|
1552
1616
|
}
|
|
1553
1617
|
return true;
|
|
1554
1618
|
});
|
|
@@ -1672,7 +1736,11 @@ class WebDriver extends Helper {
|
|
|
1672
1736
|
const res = await this._locate(withStrictLocator(locator), true);
|
|
1673
1737
|
assertElementExists(res, locator);
|
|
1674
1738
|
const elem = usingFirstElement(res);
|
|
1675
|
-
|
|
1739
|
+
try {
|
|
1740
|
+
await elem.moveTo({ xOffset, yOffset });
|
|
1741
|
+
} catch (e) {
|
|
1742
|
+
debug(e.message);
|
|
1743
|
+
}
|
|
1676
1744
|
}
|
|
1677
1745
|
|
|
1678
1746
|
/**
|
|
@@ -1925,7 +1993,7 @@ class WebDriver extends Helper {
|
|
|
1925
1993
|
* {{> resizeWindow }}
|
|
1926
1994
|
*/
|
|
1927
1995
|
async resizeWindow(width, height) {
|
|
1928
|
-
return this.
|
|
1996
|
+
return this.browser.setWindowSize(width, height);
|
|
1929
1997
|
}
|
|
1930
1998
|
|
|
1931
1999
|
async _resizeBrowserWindow(browser, width, height) {
|
|
@@ -2306,12 +2374,33 @@ class WebDriver extends Helper {
|
|
|
2306
2374
|
return this.browser.waitUntil(async () => this.browser.execute(fn, ...args), { timeout: aSec * 1000, timeoutMsg: '' });
|
|
2307
2375
|
}
|
|
2308
2376
|
|
|
2377
|
+
/**
|
|
2378
|
+
* {{> waitForNumberOfTabs }}
|
|
2379
|
+
*/
|
|
2380
|
+
async waitForNumberOfTabs(expectedTabs, sec) {
|
|
2381
|
+
const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeoutInSeconds;
|
|
2382
|
+
let currentTabs;
|
|
2383
|
+
let count = 0;
|
|
2384
|
+
|
|
2385
|
+
do {
|
|
2386
|
+
currentTabs = await this.grabNumberOfOpenTabs();
|
|
2387
|
+
await this.wait(1);
|
|
2388
|
+
count += 1000;
|
|
2389
|
+
if (currentTabs >= expectedTabs) return;
|
|
2390
|
+
} while (count <= waitTimeout);
|
|
2391
|
+
|
|
2392
|
+
throw new Error(`Expected ${expectedTabs} tabs are not met after ${waitTimeout / 1000} sec.`);
|
|
2393
|
+
}
|
|
2394
|
+
|
|
2309
2395
|
/**
|
|
2310
2396
|
* {{> switchTo }}
|
|
2311
2397
|
*/
|
|
2312
2398
|
async switchTo(locator) {
|
|
2313
2399
|
this.browser.isInsideFrame = true;
|
|
2314
2400
|
if (Number.isInteger(locator)) {
|
|
2401
|
+
if (this.options.automationProtocol) {
|
|
2402
|
+
return this.browser.switchToFrame(locator + 1);
|
|
2403
|
+
}
|
|
2315
2404
|
return this.browser.switchToFrame(locator);
|
|
2316
2405
|
}
|
|
2317
2406
|
if (!locator) {
|
|
@@ -2448,21 +2537,41 @@ class WebDriver extends Helper {
|
|
|
2448
2537
|
}
|
|
2449
2538
|
|
|
2450
2539
|
/**
|
|
2540
|
+
* This method is **deprecated**.
|
|
2541
|
+
*
|
|
2542
|
+
*
|
|
2451
2543
|
* {{> setGeoLocation }}
|
|
2452
2544
|
*/
|
|
2453
|
-
async setGeoLocation(latitude, longitude
|
|
2454
|
-
if (
|
|
2455
|
-
|
|
2545
|
+
async setGeoLocation(latitude, longitude) {
|
|
2546
|
+
if (!this.options.automationProtocol) {
|
|
2547
|
+
console.log(`setGeoLocation deprecated:
|
|
2548
|
+
* This command is deprecated due to using deprecated JSON Wire Protocol command. More info: https://webdriver.io/docs/api/jsonwp/#setgeolocation
|
|
2549
|
+
* Switch to devtools protocol to use this command by setting devtoolsProtocol: true in the configuration`);
|
|
2550
|
+
return;
|
|
2456
2551
|
}
|
|
2457
|
-
|
|
2552
|
+
this.geoLocation = { latitude, longitude };
|
|
2553
|
+
const puppeteerBrowser = await this.browser.getPuppeteer();
|
|
2554
|
+
await this.browser.call(async () => {
|
|
2555
|
+
const pages = await puppeteerBrowser.pages();
|
|
2556
|
+
await pages[0].setGeolocation({ latitude, longitude });
|
|
2557
|
+
});
|
|
2458
2558
|
}
|
|
2459
2559
|
|
|
2460
2560
|
/**
|
|
2561
|
+
* This method is **deprecated**.
|
|
2562
|
+
*
|
|
2461
2563
|
* {{> grabGeoLocation }}
|
|
2462
2564
|
*
|
|
2463
2565
|
*/
|
|
2464
2566
|
async grabGeoLocation() {
|
|
2465
|
-
|
|
2567
|
+
if (!this.options.automationProtocol) {
|
|
2568
|
+
console.log(`grabGeoLocation deprecated:
|
|
2569
|
+
* This command is deprecated due to using deprecated JSON Wire Protocol command. More info: https://webdriver.io/docs/api/jsonwp/#getgeolocation
|
|
2570
|
+
* Switch to devtools protocol to use this command by setting devtoolsProtocol: true in the configuration`);
|
|
2571
|
+
return;
|
|
2572
|
+
}
|
|
2573
|
+
if (!this.geoLocation) return 'No GeoLocation is set!';
|
|
2574
|
+
return this.geoLocation;
|
|
2466
2575
|
}
|
|
2467
2576
|
|
|
2468
2577
|
/**
|
|
@@ -2658,7 +2767,7 @@ async function proceedSeeField(assertType, field, value) {
|
|
|
2658
2767
|
}
|
|
2659
2768
|
};
|
|
2660
2769
|
|
|
2661
|
-
const proceedSingle = el =>
|
|
2770
|
+
const proceedSingle = el => el.getValue().then((res) => {
|
|
2662
2771
|
if (res === null) {
|
|
2663
2772
|
throw new Error(`Element ${el.selector} has no value attribute`);
|
|
2664
2773
|
}
|
package/lib/locator.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
let cssToXPath;
|
|
2
2
|
const { sprintf } = require('sprintf-js');
|
|
3
3
|
|
|
4
4
|
const { xpathLocator } = require('./utils');
|
|
@@ -158,11 +158,26 @@ class Locator {
|
|
|
158
158
|
}
|
|
159
159
|
|
|
160
160
|
/**
|
|
161
|
+
* @param {string} [pseudoSelector] CSS to XPath extension pseudo: https://www.npmjs.com/package/csstoxpath?activeTab=explore#extension-pseudos
|
|
161
162
|
* @returns {string}
|
|
162
163
|
*/
|
|
163
|
-
toXPath() {
|
|
164
|
+
toXPath(pseudoSelector = '') {
|
|
165
|
+
const limitation = [':nth-of-type', ':first-of-type', ':last-of-type', ':nth-last-child', ':nth-last-of-type', ':checked', ':disabled', ':enabled', ':required', ':lang'];
|
|
166
|
+
|
|
167
|
+
if (pseudoSelector.includes(':text-')) {
|
|
168
|
+
cssToXPath = require('csstoxpath');
|
|
169
|
+
} else if (this.value.includes('-')) {
|
|
170
|
+
if (!limitation.some(item => this.value.includes(item))) {
|
|
171
|
+
cssToXPath = require('csstoxpath');
|
|
172
|
+
}
|
|
173
|
+
} else if (limitation.some(item => this.value.includes(item))) {
|
|
174
|
+
cssToXPath = require('./css2xpath/js/css_to_xpath');
|
|
175
|
+
} else {
|
|
176
|
+
cssToXPath = require('./css2xpath/js/css_to_xpath');
|
|
177
|
+
}
|
|
178
|
+
|
|
164
179
|
if (this.isXPath()) return this.value;
|
|
165
|
-
if (this.isCSS()) return cssToXPath
|
|
180
|
+
if (this.isCSS()) return cssToXPath(`${this.value}${pseudoSelector}`);
|
|
166
181
|
|
|
167
182
|
throw new Error('Can\'t be converted to XPath');
|
|
168
183
|
}
|
|
@@ -243,12 +258,24 @@ class Locator {
|
|
|
243
258
|
}
|
|
244
259
|
|
|
245
260
|
/**
|
|
261
|
+
* Find an element containing a text
|
|
246
262
|
* @param {string} text
|
|
247
263
|
* @returns {Locator}
|
|
248
264
|
*/
|
|
249
265
|
withText(text) {
|
|
250
266
|
text = xpathLocator.literal(text);
|
|
251
|
-
const xpath =
|
|
267
|
+
const xpath = this.toXPath(`:text-contains-case(${text})`);
|
|
268
|
+
return new Locator({ xpath });
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* Find an element with exact text
|
|
273
|
+
* @param {string} text
|
|
274
|
+
* @returns {Locator}
|
|
275
|
+
*/
|
|
276
|
+
withTextEquals(text) {
|
|
277
|
+
text = xpathLocator.literal(text);
|
|
278
|
+
const xpath = this.toXPath(`:text-case(${text})`);
|
|
252
279
|
return new Locator({ xpath });
|
|
253
280
|
}
|
|
254
281
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const event = require('../event');
|
|
2
2
|
const recorder = require('../recorder');
|
|
3
3
|
const container = require('../container');
|
|
4
|
+
const { log } = require('../output');
|
|
4
5
|
|
|
5
6
|
const defaultConfig = {
|
|
6
7
|
retries: 3,
|
|
@@ -99,7 +100,10 @@ module.exports = (config) => {
|
|
|
99
100
|
config.when = when;
|
|
100
101
|
|
|
101
102
|
event.dispatcher.on(event.step.started, (step) => {
|
|
102
|
-
if (process.env.TRY_TO)
|
|
103
|
+
if (process.env.TRY_TO === 'true') {
|
|
104
|
+
log('Info: RetryFailedStep plugin is disabled inside tryTo block');
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
103
107
|
|
|
104
108
|
// if a step is ignored - return
|
|
105
109
|
for (const ignored of config.ignoredSteps) {
|
package/lib/plugin/retryTo.js
CHANGED
|
@@ -89,9 +89,9 @@ module.exports = function (config) {
|
|
|
89
89
|
let err = null;
|
|
90
90
|
|
|
91
91
|
return new Promise((done) => {
|
|
92
|
-
const tryBlock = () => {
|
|
92
|
+
const tryBlock = async () => {
|
|
93
93
|
recorder.session.start(`retryTo ${tries}`);
|
|
94
|
-
callback(tries);
|
|
94
|
+
await callback(tries);
|
|
95
95
|
recorder.add(() => {
|
|
96
96
|
recorder.session.restore(`retryTo ${tries}`);
|
|
97
97
|
done(null);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codeceptjs",
|
|
3
|
-
"version": "3.5.
|
|
3
|
+
"version": "3.5.12-beta.02",
|
|
4
4
|
"description": "Supercharged End 2 End Testing Framework for NodeJS",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"acceptance",
|
|
@@ -52,8 +52,11 @@
|
|
|
52
52
|
"test:unit:webbapi:playwright": "mocha test/helper/Playwright_test.js",
|
|
53
53
|
"test:unit:webbapi:puppeteer": "mocha test/helper/Puppeteer_test.js",
|
|
54
54
|
"test:unit:webbapi:webDriver": "mocha test/helper/WebDriver_test.js",
|
|
55
|
+
"test:unit:webbapi:webDriver:noSeleniumServer": "mocha test/helper/WebDriver.noSeleniumServer_test.js",
|
|
56
|
+
"test:unit:webbapi:webDriver:devtools": "mocha test/helper/WebDriver_devtools_test.js --exit",
|
|
55
57
|
"test:unit:webbapi:testCafe": "mocha test/helper/TestCafe_test.js",
|
|
56
58
|
"test:unit:expect": "mocha test/helper/Expect_test.js",
|
|
59
|
+
"test:plugin": "mocha test/plugin/plugin_test.js",
|
|
57
60
|
"def": "./runok.js def",
|
|
58
61
|
"dev:graphql": "node test/data/graphql/index.js",
|
|
59
62
|
"publish:site": "./runok.js publish:site",
|
|
@@ -67,29 +70,31 @@
|
|
|
67
70
|
"@codeceptjs/helper": "2.0.1",
|
|
68
71
|
"@cucumber/cucumber-expressions": "17",
|
|
69
72
|
"@cucumber/gherkin": "26",
|
|
70
|
-
"@cucumber/messages": "
|
|
73
|
+
"@cucumber/messages": "24.0.1",
|
|
71
74
|
"@xmldom/xmldom": "0.8.10",
|
|
72
75
|
"acorn": "8.11.2",
|
|
73
76
|
"arrify": "2.0.1",
|
|
74
77
|
"axios": "1.6.3",
|
|
78
|
+
"bo-selector": "0.0.10",
|
|
75
79
|
"chai": "4.3.8",
|
|
76
80
|
"chai-deep-match": "1.2.1",
|
|
77
|
-
"chai-exclude": "
|
|
78
|
-
"chai-json-schema": "
|
|
79
|
-
"chai-json-schema-ajv": "
|
|
80
|
-
"chai-match-pattern": "
|
|
81
|
-
"chai-string": "
|
|
81
|
+
"chai-exclude": "2.1.0",
|
|
82
|
+
"chai-json-schema": "1.5.1",
|
|
83
|
+
"chai-json-schema-ajv": "5.2.4",
|
|
84
|
+
"chai-match-pattern": "1.3.0",
|
|
85
|
+
"chai-string": "1.5.0",
|
|
82
86
|
"chalk": "4.1.2",
|
|
83
87
|
"commander": "11.1.0",
|
|
84
|
-
"convert-cssxpath": "1.0.2",
|
|
85
88
|
"cross-spawn": "7.0.3",
|
|
89
|
+
"csstoxpath": "1.6.0",
|
|
90
|
+
"devtools": "8.27.2",
|
|
86
91
|
"envinfo": "7.11.0",
|
|
87
92
|
"escape-string-regexp": "4.0.0",
|
|
88
93
|
"figures": "3.2.0",
|
|
89
94
|
"fn-args": "4.0.0",
|
|
90
95
|
"fs-extra": "11.2.0",
|
|
91
96
|
"glob": "6.0.1",
|
|
92
|
-
"html-minifier-terser": "
|
|
97
|
+
"html-minifier-terser": "7.2.0",
|
|
93
98
|
"inquirer": "6.5.2",
|
|
94
99
|
"joi": "17.11.0",
|
|
95
100
|
"js-beautify": "1.14.11",
|
|
@@ -105,7 +110,8 @@
|
|
|
105
110
|
"promise-retry": "1.1.1",
|
|
106
111
|
"resq": "1.11.0",
|
|
107
112
|
"sprintf-js": "1.1.1",
|
|
108
|
-
"uuid": "9.0"
|
|
113
|
+
"uuid": "9.0",
|
|
114
|
+
"xpath-builder": "0.0.7"
|
|
109
115
|
},
|
|
110
116
|
"optionalDependencies": {
|
|
111
117
|
"@codeceptjs/detox-helper": "1.0.2"
|
|
@@ -115,12 +121,13 @@
|
|
|
115
121
|
"@faker-js/faker": "7.6.0",
|
|
116
122
|
"@pollyjs/adapter-puppeteer": "6.0.6",
|
|
117
123
|
"@pollyjs/core": "5.1.0",
|
|
118
|
-
"@types/chai": "
|
|
124
|
+
"@types/chai": "4.3.7",
|
|
119
125
|
"@types/inquirer": "9.0.3",
|
|
120
|
-
"@types/node": "20.
|
|
126
|
+
"@types/node": "20.10.7",
|
|
121
127
|
"@wdio/sauce-service": "8.27.0",
|
|
122
128
|
"@wdio/selenium-standalone-service": "8.3.2",
|
|
123
|
-
"@wdio/utils": "8.27.
|
|
129
|
+
"@wdio/utils": "8.27.2",
|
|
130
|
+
"@xmldom/xmldom": "0.8.10",
|
|
124
131
|
"apollo-server-express": "2.25.3",
|
|
125
132
|
"chai-as-promised": "7.1.1",
|
|
126
133
|
"chai-subset": "1.6.0",
|
|
@@ -130,7 +137,7 @@
|
|
|
130
137
|
"electron": "28.0.0",
|
|
131
138
|
"eslint": "8.56.0",
|
|
132
139
|
"eslint-config-airbnb-base": "15.0.0",
|
|
133
|
-
"eslint-plugin-import": "2.29.
|
|
140
|
+
"eslint-plugin-import": "2.29.1",
|
|
134
141
|
"eslint-plugin-mocha": "6.3.0",
|
|
135
142
|
"expect": "29.7.0",
|
|
136
143
|
"express": "4.18.2",
|
|
@@ -143,7 +150,7 @@
|
|
|
143
150
|
"playwright": "1.40.1",
|
|
144
151
|
"puppeteer": "21.1.1",
|
|
145
152
|
"qrcode-terminal": "0.12.0",
|
|
146
|
-
"rosie": "2.1.
|
|
153
|
+
"rosie": "2.1.1",
|
|
147
154
|
"runok": "0.9.3",
|
|
148
155
|
"sinon": "17.0.1",
|
|
149
156
|
"sinon-chai": "3.7.0",
|
|
@@ -151,13 +158,12 @@
|
|
|
151
158
|
"ts-morph": "21.0.1",
|
|
152
159
|
"ts-node": "10.9.2",
|
|
153
160
|
"tsd-jsdoc": "2.5.0",
|
|
154
|
-
"typedoc": "0.25.
|
|
161
|
+
"typedoc": "0.25.7",
|
|
155
162
|
"typedoc-plugin-markdown": "3.17.1",
|
|
156
163
|
"typescript": "5.3.3",
|
|
157
164
|
"wdio-docker-service": "1.5.0",
|
|
158
|
-
"webdriverio": "8.
|
|
165
|
+
"webdriverio": "8.27.2",
|
|
159
166
|
"xml2js": "0.6.2",
|
|
160
|
-
"@xmldom/xmldom": "0.8.10",
|
|
161
167
|
"xpath": "0.0.34"
|
|
162
168
|
},
|
|
163
169
|
"engines": {
|
package/typings/index.d.ts
CHANGED
|
@@ -97,7 +97,7 @@ declare namespace CodeceptJS {
|
|
|
97
97
|
* }
|
|
98
98
|
* }
|
|
99
99
|
* ```
|
|
100
|
-
|
|
100
|
+
*/
|
|
101
101
|
helpers?: {
|
|
102
102
|
/**
|
|
103
103
|
* Run web tests controlling browsers via Playwright engine.
|
|
@@ -289,7 +289,7 @@ declare namespace CodeceptJS {
|
|
|
289
289
|
* ```js
|
|
290
290
|
* bootstrap: 'bootstrap.js',
|
|
291
291
|
* ```
|
|
292
|
-
|
|
292
|
+
*/
|
|
293
293
|
bootstrap?: (() => Promise<void>) | boolean | string;
|
|
294
294
|
/**
|
|
295
295
|
* [Execute code after tests](https://codecept.io/bootstrap/) finished.
|
|
@@ -303,7 +303,7 @@ declare namespace CodeceptJS {
|
|
|
303
303
|
* ```js
|
|
304
304
|
* teardown: 'teardown.js',
|
|
305
305
|
* ```
|
|
306
|
-
|
|
306
|
+
*/
|
|
307
307
|
teardown?: (() => Promise<void>) | boolean | string;
|
|
308
308
|
/**
|
|
309
309
|
* [Execute code before launching tests in parallel mode](https://codecept.io/bootstrap/#bootstrapall-teardownall)
|
|
@@ -312,7 +312,7 @@ declare namespace CodeceptJS {
|
|
|
312
312
|
bootstrapAll?: (() => Promise<void>) | boolean | string;
|
|
313
313
|
/**
|
|
314
314
|
* [Execute JS code after finishing tests in parallel mode](https://codecept.io/bootstrap/#bootstrapall-teardownall)
|
|
315
|
-
|
|
315
|
+
*/
|
|
316
316
|
teardownAll?: (() => Promise<void>) | boolean | string;
|
|
317
317
|
|
|
318
318
|
/** Enable [localized test commands](https://codecept.io/translation/) */
|
|
@@ -328,7 +328,7 @@ declare namespace CodeceptJS {
|
|
|
328
328
|
* ```
|
|
329
329
|
* require: ["should"]
|
|
330
330
|
* ```
|
|
331
|
-
|
|
331
|
+
*/
|
|
332
332
|
require?: Array<string>;
|
|
333
333
|
|
|
334
334
|
/**
|
|
@@ -423,15 +423,18 @@ declare namespace CodeceptJS {
|
|
|
423
423
|
| { ios: string }
|
|
424
424
|
| { android: string; ios: string }
|
|
425
425
|
| { react: string }
|
|
426
|
+
| { vue: string }
|
|
426
427
|
| { shadow: string[] }
|
|
427
428
|
| { custom: string };
|
|
428
429
|
|
|
429
430
|
interface CustomLocators {}
|
|
431
|
+
interface OtherLocators { props?: object }
|
|
430
432
|
type LocatorOrString =
|
|
431
433
|
| string
|
|
432
434
|
| ILocator
|
|
433
435
|
| Locator
|
|
434
|
-
|
|
|
436
|
+
| OtherLocators
|
|
437
|
+
| CustomLocators[keyof CustomLocators];
|
|
435
438
|
|
|
436
439
|
type StringOrSecret = string | CodeceptJS.Secret;
|
|
437
440
|
|