codeceptjs 3.5.3 → 3.5.4
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 +0 -2
- package/docs/build/Appium.js +8 -6
- package/docs/build/GraphQL.js +25 -0
- package/docs/build/Nightmare.js +11 -6
- package/docs/build/Playwright.js +425 -193
- package/docs/build/Protractor.js +13 -8
- package/docs/build/Puppeteer.js +20 -14
- package/docs/build/TestCafe.js +17 -10
- package/docs/build/WebDriver.js +41 -37
- package/docs/changelog.md +170 -1
- package/docs/community-helpers.md +8 -4
- package/docs/examples.md +8 -2
- package/docs/helpers/Appium.md +2 -2
- package/docs/helpers/GraphQL.md +21 -0
- package/docs/helpers/Nightmare.md +2 -2
- package/docs/helpers/Playwright.md +239 -122
- package/docs/helpers/Protractor.md +2 -2
- package/docs/helpers/Puppeteer.md +3 -3
- package/docs/helpers/TestCafe.md +2 -2
- package/docs/helpers/WebDriver.md +3 -3
- package/docs/playwright.md +24 -1
- package/docs/webapi/dontSeeInField.mustache +1 -1
- package/docs/webapi/seeInField.mustache +1 -1
- package/docs/wiki/Books-&-Posts.md +0 -0
- package/docs/wiki/Community-Helpers-&-Plugins.md +8 -4
- package/docs/wiki/Converting-Playwright-to-Istanbul-Coverage.md +46 -14
- package/docs/wiki/Examples.md +8 -2
- package/docs/wiki/Google-Summer-of-Code-(GSoC)-2020.md +0 -0
- package/docs/wiki/Home.md +0 -0
- package/docs/wiki/Migration-to-Appium-v2---CodeceptJS.md +83 -0
- package/docs/wiki/Release-Process.md +0 -0
- package/docs/wiki/Roadmap.md +0 -0
- package/docs/wiki/Tests.md +0 -0
- package/docs/wiki/Upgrading-to-CodeceptJS-3.md +0 -0
- package/docs/wiki/Videos.md +0 -0
- package/lib/command/definitions.js +2 -7
- package/lib/command/run-multiple/collection.js +17 -5
- package/lib/helper/Appium.js +6 -4
- package/lib/helper/GraphQL.js +25 -0
- package/lib/helper/Nightmare.js +9 -4
- package/lib/helper/Playwright.js +422 -190
- package/lib/helper/Protractor.js +11 -6
- package/lib/helper/Puppeteer.js +18 -12
- package/lib/helper/TestCafe.js +15 -8
- package/lib/helper/WebDriver.js +39 -35
- package/lib/helper/errors/ElementNotFound.js +2 -1
- package/lib/helper/extras/PlaywrightReact.js +9 -0
- package/lib/helper/scripts/highlightElement.js +1 -1
- package/lib/interfaces/bdd.js +1 -1
- package/lib/mochaFactory.js +2 -1
- package/lib/pause.js +5 -4
- package/lib/plugin/heal.js +2 -3
- package/lib/plugin/selenoid.js +6 -1
- package/lib/step.js +27 -10
- package/lib/utils.js +4 -0
- package/lib/workers.js +3 -1
- package/package.json +14 -14
- package/typings/promiseBasedTypes.d.ts +145 -126
- package/typings/types.d.ts +152 -133
- package/CHANGELOG.md +0 -2563
- package/docs/build/Polly.js +0 -42
- package/docs/build/SeleniumWebdriver.js +0 -76
package/docs/build/Protractor.js
CHANGED
|
@@ -821,12 +821,13 @@ class Protractor extends Helper {
|
|
|
821
821
|
* I.seeInField('#searchform input','Search');
|
|
822
822
|
* ```
|
|
823
823
|
* @param {CodeceptJS.LocatorOrString} field located by label|name|CSS|XPath|strict locator.
|
|
824
|
-
* @param {
|
|
824
|
+
* @param {CodeceptJS.StringOrSecret} value value to check.
|
|
825
825
|
* ⚠️ returns a _promise_ which is synchronized internally by recorder
|
|
826
826
|
*
|
|
827
827
|
*/
|
|
828
828
|
async seeInField(field, value) {
|
|
829
|
-
|
|
829
|
+
const _value = (typeof value === 'boolean') ? value : value.toString();
|
|
830
|
+
return proceedSeeInField.call(this, 'assert', field, _value);
|
|
830
831
|
}
|
|
831
832
|
|
|
832
833
|
/**
|
|
@@ -839,12 +840,13 @@ class Protractor extends Helper {
|
|
|
839
840
|
* ```
|
|
840
841
|
*
|
|
841
842
|
* @param {CodeceptJS.LocatorOrString} field located by label|name|CSS|XPath|strict locator.
|
|
842
|
-
* @param {
|
|
843
|
+
* @param {CodeceptJS.StringOrSecret} value value to check.
|
|
843
844
|
* ⚠️ returns a _promise_ which is synchronized internally by recorder
|
|
844
845
|
*
|
|
845
846
|
*/
|
|
846
847
|
async dontSeeInField(field, value) {
|
|
847
|
-
|
|
848
|
+
const _value = (typeof value === 'boolean') ? value : value.toString();
|
|
849
|
+
return proceedSeeInField.call(this, 'negate', field, _value);
|
|
848
850
|
}
|
|
849
851
|
|
|
850
852
|
/**
|
|
@@ -1645,7 +1647,7 @@ class Protractor extends Helper {
|
|
|
1645
1647
|
const stream = fs.createWriteStream(outputFile);
|
|
1646
1648
|
stream.write(Buffer.from(png, 'base64'));
|
|
1647
1649
|
stream.end();
|
|
1648
|
-
return new Promise(resolve => stream.on('finish', resolve));
|
|
1650
|
+
return new Promise(resolve => stream.on('finish', resolve)); // eslint-disable-line no-promise-executor-return
|
|
1649
1651
|
};
|
|
1650
1652
|
|
|
1651
1653
|
const res = await this._locate(locator);
|
|
@@ -1680,7 +1682,7 @@ class Protractor extends Helper {
|
|
|
1680
1682
|
const stream = fs.createWriteStream(outputFile);
|
|
1681
1683
|
stream.write(Buffer.from(png, 'base64'));
|
|
1682
1684
|
stream.end();
|
|
1683
|
-
return new Promise(resolve => stream.on('finish', resolve));
|
|
1685
|
+
return new Promise(resolve => stream.on('finish', resolve)); // eslint-disable-line no-promise-executor-return
|
|
1684
1686
|
};
|
|
1685
1687
|
|
|
1686
1688
|
if (!fullPage) {
|
|
@@ -2458,8 +2460,11 @@ class Protractor extends Helper {
|
|
|
2458
2460
|
const body = document.body;
|
|
2459
2461
|
const html = document.documentElement;
|
|
2460
2462
|
window.scrollTo(0, Math.max(
|
|
2461
|
-
body.scrollHeight,
|
|
2462
|
-
|
|
2463
|
+
body.scrollHeight,
|
|
2464
|
+
body.offsetHeight,
|
|
2465
|
+
html.clientHeight,
|
|
2466
|
+
html.scrollHeight,
|
|
2467
|
+
html.offsetHeight
|
|
2463
2468
|
));
|
|
2464
2469
|
});
|
|
2465
2470
|
/* eslint-enable */
|
package/docs/build/Puppeteer.js
CHANGED
|
@@ -23,7 +23,7 @@ const {
|
|
|
23
23
|
screenshotOutputFolder,
|
|
24
24
|
getNormalizedKeyAttributeValue,
|
|
25
25
|
isModifierKey,
|
|
26
|
-
requireWithFallback,
|
|
26
|
+
requireWithFallback, normalizeSpacesInString,
|
|
27
27
|
} = require('../utils');
|
|
28
28
|
const {
|
|
29
29
|
isColorProperty,
|
|
@@ -69,7 +69,7 @@ const consoleLogStore = new Console();
|
|
|
69
69
|
* @prop {boolean} [manualStart=false] - do not start browser before a test, start it manually inside a helper with `this.helpers["Puppeteer"]._startBrowser()`.
|
|
70
70
|
* @prop {string} [browser=chrome] - can be changed to `firefox` when using [puppeteer-firefox](https://codecept.io/helpers/Puppeteer-firefox).
|
|
71
71
|
* @prop {object} [chrome] - pass additional [Puppeteer run options](https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md#puppeteerlaunchoptions).
|
|
72
|
-
* @prop {boolean} [highlightElement] - highlight the interacting elements
|
|
72
|
+
* @prop {boolean} [highlightElement] - highlight the interacting elements. Default: false
|
|
73
73
|
*/
|
|
74
74
|
const config = {};
|
|
75
75
|
|
|
@@ -231,6 +231,7 @@ class Puppeteer extends Helper {
|
|
|
231
231
|
keepBrowserState: false,
|
|
232
232
|
show: false,
|
|
233
233
|
defaultPopupAction: 'accept',
|
|
234
|
+
highlightElement: false,
|
|
234
235
|
};
|
|
235
236
|
|
|
236
237
|
return Object.assign(defaults, config);
|
|
@@ -857,8 +858,11 @@ class Puppeteer extends Helper {
|
|
|
857
858
|
const body = document.body;
|
|
858
859
|
const html = document.documentElement;
|
|
859
860
|
window.scrollTo(0, Math.max(
|
|
860
|
-
body.scrollHeight,
|
|
861
|
-
|
|
861
|
+
body.scrollHeight,
|
|
862
|
+
body.offsetHeight,
|
|
863
|
+
html.clientHeight,
|
|
864
|
+
html.scrollHeight,
|
|
865
|
+
html.offsetHeight,
|
|
862
866
|
));
|
|
863
867
|
});
|
|
864
868
|
}
|
|
@@ -1756,7 +1760,7 @@ class Puppeteer extends Helper {
|
|
|
1756
1760
|
await this._evaluateHandeInContext(el => el.innerHTML = '', el);
|
|
1757
1761
|
}
|
|
1758
1762
|
|
|
1759
|
-
highlightActiveElement.call(this, el, this.
|
|
1763
|
+
highlightActiveElement.call(this, el, await this._getContext());
|
|
1760
1764
|
await el.type(value.toString(), { delay: this.options.pressKeyDelay });
|
|
1761
1765
|
|
|
1762
1766
|
return this._waitForAction();
|
|
@@ -1797,7 +1801,7 @@ class Puppeteer extends Helper {
|
|
|
1797
1801
|
async appendField(field, value) {
|
|
1798
1802
|
const els = await findVisibleFields.call(this, field);
|
|
1799
1803
|
assertElementExists(els, field, 'Field');
|
|
1800
|
-
highlightActiveElement.call(this, els[0], this.
|
|
1804
|
+
highlightActiveElement.call(this, els[0], await this._getContext());
|
|
1801
1805
|
await els[0].press('End');
|
|
1802
1806
|
await els[0].type(value.toString(), { delay: this.options.pressKeyDelay });
|
|
1803
1807
|
return this._waitForAction();
|
|
@@ -1814,12 +1818,13 @@ class Puppeteer extends Helper {
|
|
|
1814
1818
|
* I.seeInField('#searchform input','Search');
|
|
1815
1819
|
* ```
|
|
1816
1820
|
* @param {CodeceptJS.LocatorOrString} field located by label|name|CSS|XPath|strict locator.
|
|
1817
|
-
* @param {
|
|
1821
|
+
* @param {CodeceptJS.StringOrSecret} value value to check.
|
|
1818
1822
|
* ⚠️ returns a _promise_ which is synchronized internally by recorder
|
|
1819
1823
|
*
|
|
1820
1824
|
*/
|
|
1821
1825
|
async seeInField(field, value) {
|
|
1822
|
-
|
|
1826
|
+
const _value = (typeof value === 'boolean') ? value : value.toString();
|
|
1827
|
+
return proceedSeeInField.call(this, 'assert', field, _value);
|
|
1823
1828
|
}
|
|
1824
1829
|
|
|
1825
1830
|
/**
|
|
@@ -1832,12 +1837,13 @@ class Puppeteer extends Helper {
|
|
|
1832
1837
|
* ```
|
|
1833
1838
|
*
|
|
1834
1839
|
* @param {CodeceptJS.LocatorOrString} field located by label|name|CSS|XPath|strict locator.
|
|
1835
|
-
* @param {
|
|
1840
|
+
* @param {CodeceptJS.StringOrSecret} value value to check.
|
|
1836
1841
|
* ⚠️ returns a _promise_ which is synchronized internally by recorder
|
|
1837
1842
|
*
|
|
1838
1843
|
*/
|
|
1839
1844
|
async dontSeeInField(field, value) {
|
|
1840
|
-
|
|
1845
|
+
const _value = (typeof value === 'boolean') ? value : value.toString();
|
|
1846
|
+
return proceedSeeInField.call(this, 'negate', field, _value);
|
|
1841
1847
|
}
|
|
1842
1848
|
|
|
1843
1849
|
/**
|
|
@@ -1900,7 +1906,7 @@ class Puppeteer extends Helper {
|
|
|
1900
1906
|
if (await el.getProperty('tagName').then(t => t.jsonValue()) !== 'SELECT') {
|
|
1901
1907
|
throw new Error('Element is not <select>');
|
|
1902
1908
|
}
|
|
1903
|
-
highlightActiveElement.call(this, els[0], this.
|
|
1909
|
+
highlightActiveElement.call(this, els[0], await this._getContext());
|
|
1904
1910
|
if (!Array.isArray(option)) option = [option];
|
|
1905
1911
|
|
|
1906
1912
|
for (const key in option) {
|
|
@@ -3438,7 +3444,7 @@ async function proceedClick(locator, context = null, options = {}) {
|
|
|
3438
3444
|
assertElementExists(els, locator, 'Clickable element');
|
|
3439
3445
|
}
|
|
3440
3446
|
|
|
3441
|
-
highlightActiveElement.call(this, els[0], this.
|
|
3447
|
+
highlightActiveElement.call(this, els[0], await this._getContext());
|
|
3442
3448
|
|
|
3443
3449
|
await els[0].click(options);
|
|
3444
3450
|
const promises = [];
|
|
@@ -3498,7 +3504,7 @@ async function proceedSee(assertType, text, context, strict = false) {
|
|
|
3498
3504
|
if (strict) {
|
|
3499
3505
|
return allText.map(elText => equals(description)[assertType](text, elText));
|
|
3500
3506
|
}
|
|
3501
|
-
return stringIncludes(description)[assertType](text, allText.join(' | '));
|
|
3507
|
+
return stringIncludes(description)[assertType](normalizeSpacesInString(text), normalizeSpacesInString(allText.join(' | ')));
|
|
3502
3508
|
}
|
|
3503
3509
|
|
|
3504
3510
|
async function findCheckable(locator, context) {
|
|
@@ -3793,7 +3799,7 @@ function getNormalizedKey(key) {
|
|
|
3793
3799
|
}
|
|
3794
3800
|
|
|
3795
3801
|
function highlightActiveElement(element, context) {
|
|
3796
|
-
if (!this.options.
|
|
3802
|
+
if (!this.options.highlightElement && !store.debugMode) return;
|
|
3797
3803
|
|
|
3798
3804
|
highlightElement(element, context);
|
|
3799
3805
|
}
|
package/docs/build/TestCafe.js
CHANGED
|
@@ -20,7 +20,7 @@ const { urlEquals } = require('../assert/equal');
|
|
|
20
20
|
const { empty } = require('../assert/empty');
|
|
21
21
|
const { truth } = require('../assert/truth');
|
|
22
22
|
const {
|
|
23
|
-
xpathLocator,
|
|
23
|
+
xpathLocator, normalizeSpacesInString,
|
|
24
24
|
} = require('../utils');
|
|
25
25
|
const Locator = require('../locator');
|
|
26
26
|
|
|
@@ -933,9 +933,9 @@ class TestCafe extends Helper {
|
|
|
933
933
|
async see(text, context = null) {
|
|
934
934
|
let els;
|
|
935
935
|
if (context) {
|
|
936
|
-
els = (await findElements.call(this, this.context, context)).withText(text);
|
|
936
|
+
els = (await findElements.call(this, this.context, context)).withText(normalizeSpacesInString(text));
|
|
937
937
|
} else {
|
|
938
|
-
els = (await findElements.call(this, this.context, '*')).withText(text);
|
|
938
|
+
els = (await findElements.call(this, this.context, '*')).withText(normalizeSpacesInString(text));
|
|
939
939
|
}
|
|
940
940
|
|
|
941
941
|
return this.t
|
|
@@ -1091,18 +1091,19 @@ class TestCafe extends Helper {
|
|
|
1091
1091
|
* I.seeInField('#searchform input','Search');
|
|
1092
1092
|
* ```
|
|
1093
1093
|
* @param {CodeceptJS.LocatorOrString} field located by label|name|CSS|XPath|strict locator.
|
|
1094
|
-
* @param {
|
|
1094
|
+
* @param {CodeceptJS.StringOrSecret} value value to check.
|
|
1095
1095
|
* ⚠️ returns a _promise_ which is synchronized internally by recorder
|
|
1096
1096
|
*
|
|
1097
1097
|
*/
|
|
1098
1098
|
async seeInField(field, value) {
|
|
1099
|
+
const _value = (typeof value === 'boolean') ? value : value.toString();
|
|
1099
1100
|
// const expectedValue = findElements.call(this, this.context, field).value;
|
|
1100
1101
|
const els = await findFields.call(this, field);
|
|
1101
1102
|
assertElementExists(els, field, 'Field');
|
|
1102
1103
|
const el = await els.nth(0);
|
|
1103
1104
|
|
|
1104
1105
|
return this.t
|
|
1105
|
-
.expect(await el.value).eql(
|
|
1106
|
+
.expect(await el.value).eql(_value)
|
|
1106
1107
|
.catch(mapError);
|
|
1107
1108
|
}
|
|
1108
1109
|
|
|
@@ -1116,18 +1117,19 @@ class TestCafe extends Helper {
|
|
|
1116
1117
|
* ```
|
|
1117
1118
|
*
|
|
1118
1119
|
* @param {CodeceptJS.LocatorOrString} field located by label|name|CSS|XPath|strict locator.
|
|
1119
|
-
* @param {
|
|
1120
|
+
* @param {CodeceptJS.StringOrSecret} value value to check.
|
|
1120
1121
|
* ⚠️ returns a _promise_ which is synchronized internally by recorder
|
|
1121
1122
|
*
|
|
1122
1123
|
*/
|
|
1123
1124
|
async dontSeeInField(field, value) {
|
|
1125
|
+
const _value = (typeof value === 'boolean') ? value : value.toString();
|
|
1124
1126
|
// const expectedValue = findElements.call(this, this.context, field).value;
|
|
1125
1127
|
const els = await findFields.call(this, field);
|
|
1126
1128
|
assertElementExists(els, field, 'Field');
|
|
1127
1129
|
const el = await els.nth(0);
|
|
1128
1130
|
|
|
1129
1131
|
return this.t
|
|
1130
|
-
.expect(el.value).notEql(
|
|
1132
|
+
.expect(el.value).notEql(_value)
|
|
1131
1133
|
.catch(mapError);
|
|
1132
1134
|
}
|
|
1133
1135
|
|
|
@@ -1502,8 +1504,11 @@ class TestCafe extends Helper {
|
|
|
1502
1504
|
const body = document.body;
|
|
1503
1505
|
const html = document.documentElement;
|
|
1504
1506
|
window.scrollTo(0, Math.max(
|
|
1505
|
-
body.scrollHeight,
|
|
1506
|
-
|
|
1507
|
+
body.scrollHeight,
|
|
1508
|
+
body.offsetHeight,
|
|
1509
|
+
html.clientHeight,
|
|
1510
|
+
html.scrollHeight,
|
|
1511
|
+
html.offsetHeight,
|
|
1507
1512
|
));
|
|
1508
1513
|
}).with({ boundTestRun: this.t })().catch(mapError);
|
|
1509
1514
|
}
|
|
@@ -1926,7 +1931,9 @@ class TestCafe extends Helper {
|
|
|
1926
1931
|
}
|
|
1927
1932
|
|
|
1928
1933
|
async function waitForFunction(browserFn, waitTimeout) {
|
|
1929
|
-
const pause = () => new Promise((done =>
|
|
1934
|
+
const pause = () => new Promise((done => {
|
|
1935
|
+
setTimeout(done, 50);
|
|
1936
|
+
}));
|
|
1930
1937
|
|
|
1931
1938
|
const start = Date.now();
|
|
1932
1939
|
// eslint-disable-next-line no-constant-condition
|
package/docs/build/WebDriver.js
CHANGED
|
@@ -62,7 +62,7 @@ const webRoot = 'body';
|
|
|
62
62
|
* @prop {object} [desiredCapabilities] Selenium's [desired capabilities](https://github.com/SeleniumHQ/selenium/wiki/DesiredCapabilities).
|
|
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
|
-
* @prop {boolean} [highlightElement] - highlight the interacting elements
|
|
65
|
+
* @prop {boolean} [highlightElement] - highlight the interacting elements. Default: false
|
|
66
66
|
*/
|
|
67
67
|
const config = {};
|
|
68
68
|
|
|
@@ -429,6 +429,7 @@ class WebDriver extends Helper {
|
|
|
429
429
|
keepCookies: false,
|
|
430
430
|
keepBrowserState: false,
|
|
431
431
|
deprecationWarnings: false,
|
|
432
|
+
highlightElement: false,
|
|
432
433
|
};
|
|
433
434
|
|
|
434
435
|
// override defaults with config
|
|
@@ -1713,13 +1714,14 @@ class WebDriver extends Helper {
|
|
|
1713
1714
|
* I.seeInField('#searchform input','Search');
|
|
1714
1715
|
* ```
|
|
1715
1716
|
* @param {CodeceptJS.LocatorOrString} field located by label|name|CSS|XPath|strict locator.
|
|
1716
|
-
* @param {
|
|
1717
|
+
* @param {CodeceptJS.StringOrSecret} value value to check.
|
|
1717
1718
|
* ⚠️ returns a _promise_ which is synchronized internally by recorder
|
|
1718
1719
|
*
|
|
1719
1720
|
*
|
|
1720
1721
|
*/
|
|
1721
1722
|
async seeInField(field, value) {
|
|
1722
|
-
|
|
1723
|
+
const _value = (typeof value === 'boolean') ? value : value.toString();
|
|
1724
|
+
return proceedSeeField.call(this, 'assert', field, _value);
|
|
1723
1725
|
}
|
|
1724
1726
|
|
|
1725
1727
|
/**
|
|
@@ -1732,13 +1734,14 @@ class WebDriver extends Helper {
|
|
|
1732
1734
|
* ```
|
|
1733
1735
|
*
|
|
1734
1736
|
* @param {CodeceptJS.LocatorOrString} field located by label|name|CSS|XPath|strict locator.
|
|
1735
|
-
* @param {
|
|
1737
|
+
* @param {CodeceptJS.StringOrSecret} value value to check.
|
|
1736
1738
|
* ⚠️ returns a _promise_ which is synchronized internally by recorder
|
|
1737
1739
|
*
|
|
1738
1740
|
*
|
|
1739
1741
|
*/
|
|
1740
1742
|
async dontSeeInField(field, value) {
|
|
1741
|
-
|
|
1743
|
+
const _value = (typeof value === 'boolean') ? value : value.toString();
|
|
1744
|
+
return proceedSeeField.call(this, 'negate', field, _value);
|
|
1742
1745
|
}
|
|
1743
1746
|
|
|
1744
1747
|
/**
|
|
@@ -2970,7 +2973,9 @@ class WebDriver extends Helper {
|
|
|
2970
2973
|
*
|
|
2971
2974
|
*/
|
|
2972
2975
|
async wait(sec) {
|
|
2973
|
-
return new Promise(resolve =>
|
|
2976
|
+
return new Promise(resolve => {
|
|
2977
|
+
setTimeout(resolve, sec * 1000);
|
|
2978
|
+
});
|
|
2974
2979
|
}
|
|
2975
2980
|
|
|
2976
2981
|
/**
|
|
@@ -3135,20 +3140,18 @@ class WebDriver extends Helper {
|
|
|
3135
3140
|
const aSec = sec || this.options.waitForTimeoutInSeconds;
|
|
3136
3141
|
const _context = context || this.root;
|
|
3137
3142
|
|
|
3138
|
-
return this.browser.waitUntil(
|
|
3139
|
-
|
|
3140
|
-
|
|
3141
|
-
|
|
3142
|
-
|
|
3143
|
-
|
|
3144
|
-
|
|
3145
|
-
|
|
3146
|
-
|
|
3147
|
-
|
|
3148
|
-
|
|
3149
|
-
|
|
3150
|
-
},
|
|
3151
|
-
);
|
|
3143
|
+
return this.browser.waitUntil(async () => {
|
|
3144
|
+
const res = await this.$$(withStrictLocator.call(this, _context));
|
|
3145
|
+
if (!res || res.length === 0) return false;
|
|
3146
|
+
const selected = await forEachAsync(res, async el => this.browser.getElementText(getElementId(el)));
|
|
3147
|
+
if (Array.isArray(selected)) {
|
|
3148
|
+
return selected.filter(part => part.indexOf(text) >= 0).length > 0;
|
|
3149
|
+
}
|
|
3150
|
+
return selected.indexOf(text) >= 0;
|
|
3151
|
+
}, {
|
|
3152
|
+
timeout: aSec * 1000,
|
|
3153
|
+
timeoutMsg: `element (${_context}) is not in DOM or there is no element(${_context}) with text "${text}" after ${aSec} sec`,
|
|
3154
|
+
});
|
|
3152
3155
|
}
|
|
3153
3156
|
|
|
3154
3157
|
/**
|
|
@@ -3168,20 +3171,18 @@ class WebDriver extends Helper {
|
|
|
3168
3171
|
const client = this.browser;
|
|
3169
3172
|
const aSec = sec || this.options.waitForTimeoutInSeconds;
|
|
3170
3173
|
|
|
3171
|
-
return client.waitUntil(
|
|
3172
|
-
|
|
3173
|
-
|
|
3174
|
-
|
|
3175
|
-
|
|
3176
|
-
|
|
3177
|
-
|
|
3178
|
-
|
|
3179
|
-
|
|
3180
|
-
|
|
3181
|
-
|
|
3182
|
-
|
|
3183
|
-
},
|
|
3184
|
-
);
|
|
3174
|
+
return client.waitUntil(async () => {
|
|
3175
|
+
const res = await findFields.call(this, field);
|
|
3176
|
+
if (!res || res.length === 0) return false;
|
|
3177
|
+
const selected = await forEachAsync(res, async el => el.getValue());
|
|
3178
|
+
if (Array.isArray(selected)) {
|
|
3179
|
+
return selected.filter(part => part.indexOf(value) >= 0).length > 0;
|
|
3180
|
+
}
|
|
3181
|
+
return selected.indexOf(value) >= 0;
|
|
3182
|
+
}, {
|
|
3183
|
+
timeout: aSec * 1000,
|
|
3184
|
+
timeoutMsg: `element (${field}) is not in DOM or there is no element(${field}) with value "${value}" after ${aSec} sec`,
|
|
3185
|
+
});
|
|
3185
3186
|
}
|
|
3186
3187
|
|
|
3187
3188
|
/**
|
|
@@ -3525,8 +3526,11 @@ class WebDriver extends Helper {
|
|
|
3525
3526
|
const body = document.body;
|
|
3526
3527
|
const html = document.documentElement;
|
|
3527
3528
|
window.scrollTo(0, Math.max(
|
|
3528
|
-
body.scrollHeight,
|
|
3529
|
-
|
|
3529
|
+
body.scrollHeight,
|
|
3530
|
+
body.offsetHeight,
|
|
3531
|
+
html.clientHeight,
|
|
3532
|
+
html.scrollHeight,
|
|
3533
|
+
html.offsetHeight
|
|
3530
3534
|
));
|
|
3531
3535
|
});
|
|
3532
3536
|
/* eslint-enable */
|
|
@@ -4075,7 +4079,7 @@ function isModifierKey(key) {
|
|
|
4075
4079
|
}
|
|
4076
4080
|
|
|
4077
4081
|
function highlightActiveElement(element) {
|
|
4078
|
-
if (!this.options.
|
|
4082
|
+
if (!this.options.highlightElement && !store.debugMode) return;
|
|
4079
4083
|
|
|
4080
4084
|
highlightElement(element, this.browser);
|
|
4081
4085
|
}
|
package/docs/changelog.md
CHANGED
|
@@ -7,11 +7,180 @@ layout: Section
|
|
|
7
7
|
|
|
8
8
|
# Releases
|
|
9
9
|
|
|
10
|
+
## 3.5.4
|
|
11
|
+
|
|
12
|
+
🐛 Bug Fixes:
|
|
13
|
+
* **[Playwright]** When passing `userDataDir`, it throws error after test execution ([#3814](https://github.com/codeceptjs/CodeceptJS/issues/3814)) - by **[KobeNguyenT](https://github.com/KobeNguyenT)**
|
|
14
|
+
* [CodeceptJS-CLI] Improve command to generate types ([#3788](https://github.com/codeceptjs/CodeceptJS/issues/3788)) - by **[KobeNguyenT](https://github.com/KobeNguyenT)**
|
|
15
|
+
* Heal plugin fix ([#3820](https://github.com/codeceptjs/CodeceptJS/issues/3820)) - by **[davert](https://github.com/davert)**
|
|
16
|
+
* Fix for error in using `all` with `run-workers` ([#3805](https://github.com/codeceptjs/CodeceptJS/issues/3805)) - by **[KobeNguyenT](https://github.com/KobeNguyenT)**
|
|
17
|
+
```js
|
|
18
|
+
helpers: {
|
|
19
|
+
Playwright: {
|
|
20
|
+
url: 'https://github.com',
|
|
21
|
+
show: false,
|
|
22
|
+
browser: 'chromium',
|
|
23
|
+
waitForNavigation: 'load',
|
|
24
|
+
waitForTimeout: 30_000,
|
|
25
|
+
trace: true,
|
|
26
|
+
keepTraceForPassedTests: true
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
multiple: {
|
|
30
|
+
profile1: {
|
|
31
|
+
browsers: [
|
|
32
|
+
{
|
|
33
|
+
browser: "chromium",
|
|
34
|
+
}
|
|
35
|
+
]
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
```
|
|
39
|
+
* Highlight elements issues ([#3779](https://github.com/codeceptjs/CodeceptJS/issues/3779)) ([#3778](https://github.com/codeceptjs/CodeceptJS/issues/3778)) - by **[philkas](https://github.com/philkas)**
|
|
40
|
+
* Support ` ` symbol in `I.see` method ([#3815](https://github.com/codeceptjs/CodeceptJS/issues/3815)) - by **[KobeNguyenT](https://github.com/KobeNguyenT)**
|
|
41
|
+
|
|
42
|
+
```js
|
|
43
|
+
// HTML code uses instead of space
|
|
44
|
+
<div class="dJHe_" style="color: rgb(255, 255, 255);">My Text!</div>
|
|
45
|
+
|
|
46
|
+
I.see("My Text!") // this test would work with both and space
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
📖 Documentation
|
|
50
|
+
* Improve the configuration of electron testing when the app is build with electron-forge ([#3802](https://github.com/codeceptjs/CodeceptJS/issues/3802)) - by **[KobeNguyenT](https://github.com/KobeNguyenT)**
|
|
51
|
+
|
|
52
|
+
```js
|
|
53
|
+
const path = require("path");
|
|
54
|
+
|
|
55
|
+
exports.config = {
|
|
56
|
+
helpers: {
|
|
57
|
+
Playwright: {
|
|
58
|
+
browser: "electron",
|
|
59
|
+
electron: {
|
|
60
|
+
executablePath: require("electron"),
|
|
61
|
+
args: [path.join(__dirname, ".webpack/main/index.js")],
|
|
62
|
+
},
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
// rest of config
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
🛩️ Features
|
|
70
|
+
|
|
71
|
+
#### **[Playwright]** new features and improvements
|
|
72
|
+
* Parse the response in recording network steps ([#3771](https://github.com/codeceptjs/CodeceptJS/issues/3771)) - by **[KobeNguyenT](https://github.com/KobeNguyenT)**
|
|
73
|
+
|
|
74
|
+
```js
|
|
75
|
+
const traffics = await I.grabRecordedNetworkTraffics();
|
|
76
|
+
expect(traffics[0].url).to.equal('https://reqres.in/api/comments/1');
|
|
77
|
+
expect(traffics[0].response.status).to.equal(200);
|
|
78
|
+
expect(traffics[0].response.body).to.contain({ name: 'this was mocked' });
|
|
79
|
+
|
|
80
|
+
expect(traffics[1].url).to.equal('https://reqres.in/api/comments/1');
|
|
81
|
+
expect(traffics[1].response.status).to.equal(200);
|
|
82
|
+
expect(traffics[1].response.body).to.contain({ name: 'this was another mocked' });
|
|
83
|
+
```
|
|
84
|
+
* Grab metrics ([#3809](https://github.com/codeceptjs/CodeceptJS/issues/3809)) - by **[KobeNguyenT](https://github.com/KobeNguyenT)**
|
|
85
|
+
|
|
86
|
+
```js
|
|
87
|
+
const metrics = await I.grabMetrics();
|
|
88
|
+
|
|
89
|
+
// returned metrics
|
|
90
|
+
|
|
91
|
+
[
|
|
92
|
+
{ name: 'Timestamp', value: 1584904.203473 },
|
|
93
|
+
{ name: 'AudioHandlers', value: 0 },
|
|
94
|
+
{ name: 'AudioWorkletProcessors', value: 0 },
|
|
95
|
+
{ name: 'Documents', value: 22 },
|
|
96
|
+
{ name: 'Frames', value: 10 },
|
|
97
|
+
{ name: 'JSEventListeners', value: 366 },
|
|
98
|
+
{ name: 'LayoutObjects', value: 1240 },
|
|
99
|
+
{ name: 'MediaKeySessions', value: 0 },
|
|
100
|
+
{ name: 'MediaKeys', value: 0 },
|
|
101
|
+
{ name: 'Nodes', value: 4505 },
|
|
102
|
+
{ name: 'Resources', value: 141 },
|
|
103
|
+
{ name: 'ContextLifecycleStateObservers', value: 34 },
|
|
104
|
+
{ name: 'V8PerContextDatas', value: 4 },
|
|
105
|
+
{ name: 'WorkerGlobalScopes', value: 0 },
|
|
106
|
+
{ name: 'UACSSResources', value: 0 },
|
|
107
|
+
{ name: 'RTCPeerConnections', value: 0 },
|
|
108
|
+
{ name: 'ResourceFetchers', value: 22 },
|
|
109
|
+
{ name: 'AdSubframes', value: 0 },
|
|
110
|
+
{ name: 'DetachedScriptStates', value: 2 },
|
|
111
|
+
{ name: 'ArrayBufferContents', value: 1 },
|
|
112
|
+
{ name: 'LayoutCount', value: 0 },
|
|
113
|
+
{ name: 'RecalcStyleCount', value: 0 },
|
|
114
|
+
{ name: 'LayoutDuration', value: 0 },
|
|
115
|
+
{ name: 'RecalcStyleDuration', value: 0 },
|
|
116
|
+
{ name: 'DevToolsCommandDuration', value: 0.000013 },
|
|
117
|
+
{ name: 'ScriptDuration', value: 0 },
|
|
118
|
+
{ name: 'V8CompileDuration', value: 0 },
|
|
119
|
+
{ name: 'TaskDuration', value: 0.000014 },
|
|
120
|
+
{ name: 'TaskOtherDuration', value: 0.000001 },
|
|
121
|
+
{ name: 'ThreadTime', value: 0.000046 },
|
|
122
|
+
{ name: 'ProcessTime', value: 0.616852 },
|
|
123
|
+
{ name: 'JSHeapUsedSize', value: 19004908 },
|
|
124
|
+
{ name: 'JSHeapTotalSize', value: 26820608 },
|
|
125
|
+
{ name: 'FirstMeaningfulPaint', value: 0 },
|
|
126
|
+
{ name: 'DomContentLoaded', value: 1584903.690491 },
|
|
127
|
+
{ name: 'NavigationStart', value: 1584902.841845 }
|
|
128
|
+
]
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
* Grab WebSocket (WS) messages ([#3789](https://github.com/codeceptjs/CodeceptJS/issues/3789)) - by **[KobeNguyenT](https://github.com/KobeNguyenT)**
|
|
132
|
+
* `flushWebSocketMessages`
|
|
133
|
+
* `grabWebSocketMessages`
|
|
134
|
+
* `startRecordingWebSocketMessages`
|
|
135
|
+
* `stopRecordingWebSocketMessages`
|
|
136
|
+
|
|
137
|
+
```js
|
|
138
|
+
await I.startRecordingWebSocketMessages();
|
|
139
|
+
I.amOnPage('https://websocketstest.com/');
|
|
140
|
+
I.waitForText('Work for You!');
|
|
141
|
+
I.flushNetworkTraffics();
|
|
142
|
+
const wsMessages = I.grabWebSocketMessages();
|
|
143
|
+
expect(wsMessages.length).to.equal(0);
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
```js
|
|
147
|
+
await I.startRecordingWebSocketMessages();
|
|
148
|
+
await I.amOnPage('https://websocketstest.com/');
|
|
149
|
+
I.waitForText('Work for You!');
|
|
150
|
+
const wsMessages = I.grabWebSocketMessages();
|
|
151
|
+
expect(wsMessages.length).to.greaterThan(0);
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
```js
|
|
155
|
+
await I.startRecordingWebSocketMessages();
|
|
156
|
+
await I.amOnPage('https://websocketstest.com/');
|
|
157
|
+
I.waitForText('Work for You!');
|
|
158
|
+
const wsMessages = I.grabWebSocketMessages();
|
|
159
|
+
await I.stopRecordingWebSocketMessages();
|
|
160
|
+
await I.amOnPage('https://websocketstest.com/');
|
|
161
|
+
I.waitForText('Work for You!');
|
|
162
|
+
const afterWsMessages = I.grabWebSocketMessages();
|
|
163
|
+
expect(wsMessages.length).to.equal(afterWsMessages.length);
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
* Move from `ElementHandle` to `Locator`. This change is quite major, but it happened under hood, so should not affect your code. ([#3738](https://github.com/codeceptjs/CodeceptJS/issues/3738)) - by **[KobeNguyenT](https://github.com/KobeNguyenT)**
|
|
167
|
+
|
|
10
168
|
## 3.5.3
|
|
11
169
|
|
|
12
170
|
🛩️ Features
|
|
13
171
|
|
|
14
|
-
* **[Playwright]**
|
|
172
|
+
* **[Playwright]** Added commands to check network traffic [#3748](https://github.com/codeceptjs/CodeceptJS/issues/3748) - by **[ngraf](https://github.com/ngraf)** **[KobeNguyenT](https://github.com/KobeNguyenT)**
|
|
173
|
+
* `startRecordingTraffic`
|
|
174
|
+
* `grabRecordedNetworkTraffics`
|
|
175
|
+
* `blockTraffic`
|
|
176
|
+
* `mockTraffic`
|
|
177
|
+
* `flushNetworkTraffics`
|
|
178
|
+
* `stopRecordingTraffic`
|
|
179
|
+
* `seeTraffic`
|
|
180
|
+
* `grabTrafficUrl`
|
|
181
|
+
* `dontSeeTraffic`
|
|
182
|
+
|
|
183
|
+
Examples:
|
|
15
184
|
|
|
16
185
|
```js
|
|
17
186
|
// recording traffics and verify the traffic
|
|
@@ -31,9 +31,6 @@ Please **add your own** by editing this page.
|
|
|
31
31
|
* [codeceptjs-bshelper](https://github.com/PeterNgTr/codeceptjs-bshelper) - a helper which updates `Test Names` & `Test Results` on Browserstack
|
|
32
32
|
* [codeceptjs-tbhelper](https://github.com/testingbot/codeceptjs-tbhelper) - a helper which updates `Test Names` & `Test Results` on TestingBot
|
|
33
33
|
|
|
34
|
-
## Integrations
|
|
35
|
-
* [codeceptjs-testrail](https://github.com/PeterNgTr/codeceptjs-testrail) - a plugin to integrate with [Testrail](https://www.gurock.com/testrail)
|
|
36
|
-
|
|
37
34
|
## Visual-Testing
|
|
38
35
|
* [codeceptjs-resemblehelper](https://github.com/puneet0191/codeceptjs-resemblehelper) - a helper which helps with visual testing using resemble.js.
|
|
39
36
|
* [codeceptjs-applitoolshelper](https://www.npmjs.com/package/codeceptjs-applitoolshelper) - a helper which helps interaction with [Applitools](https://applitools.com)
|
|
@@ -42,8 +39,10 @@ Please **add your own** by editing this page.
|
|
|
42
39
|
## Reporters
|
|
43
40
|
* [codeceptjs-rphelper](https://github.com/reportportal/agent-js-codecept) is a CodeceptJS helper which can publish tests results on ReportPortal after execution.
|
|
44
41
|
* [codeceptjs-xray-helper](https://www.npmjs.com/package/codeceptjs-xray-helper) is a CodeceptJS helper which can publish tests results on [XRAY](https://confluence.xpand-it.com/display/XRAYCLOUD/Import+Execution+Results+-+REST).
|
|
42
|
+
* [codeceptjs-xray-cloud-helper](https://www.npmjs.com/package/codeceptjs-xray-cloud-helper) is a helper that automatically retrieves the result of CodeceptJS tests and sends them to XRAY/JIRA(cloud version) via [XRAY Cloud API](https://docs.getxray.app/display/XRAYCLOUD/Import+Execution+Results+-+REST+v2#ImportExecutionResultsRESTv2-XrayJSONresults).
|
|
45
43
|
* [codeceptjs-slack-reporter](https://www.npmjs.com/package/codeceptjs-slack-reporter) Get a Slack notification when one or more scenarios fail.
|
|
46
44
|
* [codeceptjs-browserlogs-plugin](https://github.com/pavkam/codeceptjs-browserlogs-plugin) Record the browser logs for failed tests.
|
|
45
|
+
* [codeceptjs-testrail](https://github.com/PeterNgTr/codeceptjs-testrail) - a plugin to integrate with [Testrail](https://www.gurock.com/testrail)
|
|
47
46
|
|
|
48
47
|
## Browser request control
|
|
49
48
|
* [codeceptjs-resources-check](https://github.com/luarmr/codeceptjs-resources-check) Load a URL with Puppeteer and listen to the requests while the page is loading. Enabling count the number or check the sizes of the requests.
|
|
@@ -55,4 +54,9 @@ Please **add your own** by editing this page.
|
|
|
55
54
|
## Other
|
|
56
55
|
|
|
57
56
|
* [codeceptjs-cmdhelper](https://github.com/thiagodp/codeceptjs-cmdhelper) allows you to run commands in the terminal/console
|
|
58
|
-
* [eslint-plugin-codeceptjs](https://www.npmjs.com/package/eslint-plugin-codeceptjs) Eslint rules for CodeceptJS.
|
|
57
|
+
* [eslint-plugin-codeceptjs](https://www.npmjs.com/package/eslint-plugin-codeceptjs) Eslint rules for CodeceptJS.
|
|
58
|
+
* [codeceptjs-datalayer-helper](https://github.com/kobenguyent/codeceptjs-datalayer-helper) CodeceptJS DataLayer helper helps you to get the datalayer JavaScript array that is used to store information and send this data to the tag manager.
|
|
59
|
+
* [codeceptjs-a11y-helper](https://github.com/kobenguyent/codeceptjs-a11y-helper) accessibility tests integrated with CodeceptJS - Playwright-axe
|
|
60
|
+
* [codeceptjs-lighthouse-helper](https://github.com/kobenguyent/codeceptjs-lighthouse-helper) lighthouse audit integrated with CodeceptJS - Playwright
|
|
61
|
+
* [Snowplow Data analytics](https://www.npmjs.com/package/@viasat/codeceptjs-snowplow-helper) - Test your Snowplow events implementations with CodeceptJS and Snowplow Micro.
|
|
62
|
+
* [codeceptjs-failure-logger](https://github.com/kobenguyent/codeceptjs-failure-logger) - Log failed CodeceptJS tests to file
|
package/docs/examples.md
CHANGED
|
@@ -16,6 +16,7 @@ Playground repository where you can run tests in different helpers on a basic si
|
|
|
16
16
|
|
|
17
17
|
Tests repository demonstrate usage of
|
|
18
18
|
|
|
19
|
+
* Playwright helper
|
|
19
20
|
* Puppeteer helper
|
|
20
21
|
* WebDriver helper
|
|
21
22
|
* TestCafe plugin
|
|
@@ -28,7 +29,6 @@ Tests repository demonstrate usage of
|
|
|
28
29
|
CodeceptJS repo contains basic tests (both failing and passing) just to show how it works.
|
|
29
30
|
Our team uses it to test new features and run simple scenarios.
|
|
30
31
|
|
|
31
|
-
|
|
32
32
|
## [CodeceptJS Cucumber E2E Framework](https://github.com/gkushang/codeceptjs-e2e)
|
|
33
33
|
|
|
34
34
|
This repository contains complete E2E framework for CodeceptJS with Cucumber and SauceLabs Integration
|
|
@@ -146,4 +146,10 @@ This is necessary if all integrations with TMS and CI/CD are already configured,
|
|
|
146
146
|
* HTTP request client with session support and unit tests
|
|
147
147
|
* Exemplary code control
|
|
148
148
|
* Ready to launch in a CI/CD system as is
|
|
149
|
-
* OOP, Test data models and builders, endpoint decorators
|
|
149
|
+
* OOP, Test data models and builders, endpoint decorators
|
|
150
|
+
|
|
151
|
+
## [Playwright fun with CodeceptJS](https://github.com/PeterNgTr/codeceptjs-playwright-fun)
|
|
152
|
+
* Tests are written in TS
|
|
153
|
+
* CI/CD with Github Actions
|
|
154
|
+
* Page Object Model is applied
|
|
155
|
+
* ReportPortal Integration
|