codeceptjs 3.5.14 → 3.5.15
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/lib/command/dryRun.js +44 -13
- package/lib/helper/Expect.js +1 -1
- package/lib/helper/Puppeteer.js +123 -61
- package/lib/plugin/coverage.js +112 -99
- package/package.json +27 -19
- package/typings/index.d.ts +1 -0
- package/typings/promiseBasedTypes.d.ts +2 -2
- package/typings/types.d.ts +80 -15
package/lib/command/dryRun.js
CHANGED
|
@@ -59,24 +59,39 @@ function printTests(files) {
|
|
|
59
59
|
|
|
60
60
|
let numOfTests = 0;
|
|
61
61
|
let numOfSuites = 0;
|
|
62
|
-
|
|
62
|
+
let outputString = '';
|
|
63
|
+
const filterBy = process.env.grep ? process.env.grep.toLowerCase() : undefined;
|
|
63
64
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
65
|
+
if (filterBy) {
|
|
66
|
+
for (const suite of mocha.suite.suites) {
|
|
67
|
+
const currentSuite = suite.title;
|
|
68
|
+
if (suite.title.toLowerCase().includes(filterBy)) {
|
|
69
|
+
outputString += `${colors.white.bold(suite.title)} -- ${output.styles.log(suite.file || '')} -- ${mocha.suite.suites.length} tests\n`;
|
|
70
|
+
numOfSuites++;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
for (test of suite.tests) {
|
|
74
|
+
if (test.title.toLowerCase().includes(filterBy)) {
|
|
75
|
+
numOfTests++;
|
|
76
|
+
outputString += `${colors.white.bold(test.parent.title)} -- ${output.styles.log(test.parent.file || '')} -- ${mocha.suite.suites.length} tests\n`;
|
|
77
|
+
outputString += ` ${output.styles.scenario(figures.checkboxOff)} ${test.title}\n`;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
67
80
|
}
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
81
|
+
numOfSuites = countSuites(outputString);
|
|
82
|
+
} else {
|
|
83
|
+
for (const suite of mocha.suite.suites) {
|
|
84
|
+
output.print(`${colors.white.bold(suite.title)} -- ${output.styles.log(suite.file || '')} -- ${mocha.suite.suites.length} tests`);
|
|
85
|
+
numOfSuites++;
|
|
86
|
+
|
|
87
|
+
for (test of suite.tests) {
|
|
88
|
+
numOfTests++;
|
|
89
|
+
output.print(` ${output.styles.scenario(figures.checkboxOff)} ${test.title}`);
|
|
90
|
+
}
|
|
77
91
|
}
|
|
78
92
|
}
|
|
79
93
|
|
|
94
|
+
output.print(removeDuplicates(outputString));
|
|
80
95
|
output.print('');
|
|
81
96
|
output.success(` Total: ${numOfSuites} suites | ${numOfTests} tests `);
|
|
82
97
|
printFooter();
|
|
@@ -87,3 +102,19 @@ function printFooter() {
|
|
|
87
102
|
output.print();
|
|
88
103
|
output.print('--- DRY MODE: No tests were executed ---');
|
|
89
104
|
}
|
|
105
|
+
|
|
106
|
+
function removeDuplicates(inputString) {
|
|
107
|
+
const array = inputString.split('\n');
|
|
108
|
+
const uniqueLines = [...new Set(array)];
|
|
109
|
+
const resultString = uniqueLines.join('\n');
|
|
110
|
+
|
|
111
|
+
return resultString;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function countSuites(inputString) {
|
|
115
|
+
const array = inputString.split('\n');
|
|
116
|
+
|
|
117
|
+
const uniqueLines = [...new Set(array)];
|
|
118
|
+
const res = uniqueLines.filter(item => item.includes('-- '));
|
|
119
|
+
return res.length;
|
|
120
|
+
}
|
package/lib/helper/Expect.js
CHANGED
|
@@ -190,7 +190,7 @@ class ExpectHelper {
|
|
|
190
190
|
* @param {*} targetData
|
|
191
191
|
* @param {*} jsonSchema
|
|
192
192
|
* @param {*} [customErrorMsg]
|
|
193
|
-
* @param {*} ajvOptions Pass AJV options
|
|
193
|
+
* @param {*} [ajvOptions] Pass AJV options
|
|
194
194
|
*/
|
|
195
195
|
expectJsonSchemaUsingAJV(
|
|
196
196
|
targetData,
|
package/lib/helper/Puppeteer.js
CHANGED
|
@@ -78,7 +78,7 @@ const consoleLogStore = new Console();
|
|
|
78
78
|
* @prop {string} [browser=chrome] - can be changed to `firefox` when using [puppeteer-firefox](https://codecept.io/helpers/Puppeteer-firefox).
|
|
79
79
|
* @prop {object} [chrome] - pass additional [Puppeteer run options](https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md#puppeteerlaunchoptions).
|
|
80
80
|
* @prop {boolean} [highlightElement] - highlight the interacting elements. Default: false. Note: only activate under verbose mode (--verbose).
|
|
81
|
-
*/
|
|
81
|
+
*/
|
|
82
82
|
const config = {};
|
|
83
83
|
|
|
84
84
|
/**
|
|
@@ -369,7 +369,7 @@ class Puppeteer extends Helper {
|
|
|
369
369
|
this.debugSection('Incognito Tab', 'opened');
|
|
370
370
|
this.activeSessionName = name;
|
|
371
371
|
|
|
372
|
-
const bc = await this.browser.
|
|
372
|
+
const bc = await this.browser.createBrowserContext();
|
|
373
373
|
await bc.newPage();
|
|
374
374
|
|
|
375
375
|
// Create a new page inside context.
|
|
@@ -599,14 +599,7 @@ class Puppeteer extends Helper {
|
|
|
599
599
|
}
|
|
600
600
|
|
|
601
601
|
async _evaluateHandeInContext(...args) {
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
if (context.constructor.name === 'Frame') {
|
|
605
|
-
// Currently there is no evalateHandle for the Frame object
|
|
606
|
-
// https://github.com/GoogleChrome/puppeteer/issues/1051
|
|
607
|
-
context = await context.executionContext();
|
|
608
|
-
}
|
|
609
|
-
|
|
602
|
+
const context = await this._getContext();
|
|
610
603
|
return context.evaluateHandle(...args);
|
|
611
604
|
}
|
|
612
605
|
|
|
@@ -884,7 +877,8 @@ class Puppeteer extends Helper {
|
|
|
884
877
|
* {{ react }}
|
|
885
878
|
*/
|
|
886
879
|
async _locate(locator) {
|
|
887
|
-
|
|
880
|
+
const context = await this.context;
|
|
881
|
+
return findElements.call(this, context, locator);
|
|
888
882
|
}
|
|
889
883
|
|
|
890
884
|
/**
|
|
@@ -910,7 +904,7 @@ class Puppeteer extends Helper {
|
|
|
910
904
|
* ```
|
|
911
905
|
*/
|
|
912
906
|
async _locateClickable(locator) {
|
|
913
|
-
const context = await this.
|
|
907
|
+
const context = await this.context;
|
|
914
908
|
return findClickable.call(this, context, locator);
|
|
915
909
|
}
|
|
916
910
|
|
|
@@ -1687,8 +1681,8 @@ class Puppeteer extends Helper {
|
|
|
1687
1681
|
* {{> executeScript }}
|
|
1688
1682
|
*/
|
|
1689
1683
|
async executeScript(...args) {
|
|
1690
|
-
let context = this.
|
|
1691
|
-
if (this.context && this.context.constructor.name === '
|
|
1684
|
+
let context = await this._getContext();
|
|
1685
|
+
if (this.context && this.context.constructor.name === 'CdpFrame') {
|
|
1692
1686
|
context = this.context; // switching to iframe context
|
|
1693
1687
|
}
|
|
1694
1688
|
return context.evaluate.apply(context, args);
|
|
@@ -1769,7 +1763,7 @@ class Puppeteer extends Helper {
|
|
|
1769
1763
|
*/
|
|
1770
1764
|
async grabHTMLFromAll(locator) {
|
|
1771
1765
|
const els = await this._locate(locator);
|
|
1772
|
-
const values = await Promise.all(els.map(el => el.
|
|
1766
|
+
const values = await Promise.all(els.map(el => el.evaluate(element => element.innerHTML, el)));
|
|
1773
1767
|
return values;
|
|
1774
1768
|
}
|
|
1775
1769
|
|
|
@@ -1792,7 +1786,7 @@ class Puppeteer extends Helper {
|
|
|
1792
1786
|
*/
|
|
1793
1787
|
async grabCssPropertyFromAll(locator, cssProperty) {
|
|
1794
1788
|
const els = await this._locate(locator);
|
|
1795
|
-
const res = await Promise.all(els.map(el => el.
|
|
1789
|
+
const res = await Promise.all(els.map(el => el.evaluate(el => JSON.parse(JSON.stringify(getComputedStyle(el))), el)));
|
|
1796
1790
|
const cssValues = res.map(props => props[toCamelCase(cssProperty)]);
|
|
1797
1791
|
|
|
1798
1792
|
return cssValues;
|
|
@@ -1854,35 +1848,34 @@ class Puppeteer extends Helper {
|
|
|
1854
1848
|
* {{ react }}
|
|
1855
1849
|
*/
|
|
1856
1850
|
async seeAttributesOnElements(locator, attributes) {
|
|
1857
|
-
const
|
|
1858
|
-
assertElementExists(
|
|
1851
|
+
const elements = await this._locate(locator);
|
|
1852
|
+
assertElementExists(elements, locator);
|
|
1859
1853
|
|
|
1860
|
-
const
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
});
|
|
1870
|
-
let attrs = await Promise.all(commands);
|
|
1871
|
-
const values = Object.keys(attributes).map(key => attributes[key]);
|
|
1872
|
-
if (!Array.isArray(attrs)) attrs = [attrs];
|
|
1873
|
-
let chunked = chunkArray(attrs, values.length);
|
|
1874
|
-
chunked = chunked.filter((val) => {
|
|
1875
|
-
for (let i = 0; i < val.length; ++i) {
|
|
1876
|
-
const _actual = Number.isNaN(val[i]) || (typeof values[i]) === 'string' ? val[i] : Number.parseInt(values[i], 10);
|
|
1877
|
-
const _expected = Number.isNaN(values[i]) || (typeof values[i]) === 'string' ? values[i] : Number.parseInt(values[i], 10);
|
|
1878
|
-
// the attribute could be a boolean
|
|
1879
|
-
if (typeof _actual === 'boolean') return _actual === _expected;
|
|
1880
|
-
// if the attribute doesn't exist, returns false as well
|
|
1881
|
-
if (!_actual || !_actual.includes(_expected)) return false;
|
|
1882
|
-
}
|
|
1883
|
-
return true;
|
|
1854
|
+
const expectedAttributes = Object.entries(attributes);
|
|
1855
|
+
|
|
1856
|
+
const valuesPromises = elements.map(async (element) => {
|
|
1857
|
+
const elementAttributes = {};
|
|
1858
|
+
await Promise.all(expectedAttributes.map(async ([attribute, expectedValue]) => {
|
|
1859
|
+
const actualValue = await element.evaluate((el, attr) => el[attr] || el.getAttribute(attr), attribute);
|
|
1860
|
+
elementAttributes[attribute] = actualValue;
|
|
1861
|
+
}));
|
|
1862
|
+
return elementAttributes;
|
|
1884
1863
|
});
|
|
1885
|
-
|
|
1864
|
+
|
|
1865
|
+
const actualAttributes = await Promise.all(valuesPromises);
|
|
1866
|
+
|
|
1867
|
+
const matchingElements = actualAttributes.filter((attrs) => expectedAttributes.every(([attribute, expectedValue]) => {
|
|
1868
|
+
const actualValue = attrs[attribute];
|
|
1869
|
+
if (!actualValue) return false;
|
|
1870
|
+
if (actualValue.toString().match(new RegExp(expectedValue.toString()))) return true;
|
|
1871
|
+
return expectedValue === actualValue;
|
|
1872
|
+
}));
|
|
1873
|
+
|
|
1874
|
+
const elementsCount = elements.length;
|
|
1875
|
+
const matchingCount = matchingElements.length;
|
|
1876
|
+
|
|
1877
|
+
return equals(`all elements (${(new Locator(locator))}) to have attributes ${JSON.stringify(attributes)}`)
|
|
1878
|
+
.assert(matchingCount, elementsCount);
|
|
1886
1879
|
}
|
|
1887
1880
|
|
|
1888
1881
|
/**
|
|
@@ -2138,7 +2131,7 @@ class Puppeteer extends Helper {
|
|
|
2138
2131
|
if (locator.isCSS()) {
|
|
2139
2132
|
waiter = context.waitForSelector(locator.simplify(), { timeout: waitTimeout });
|
|
2140
2133
|
} else {
|
|
2141
|
-
waiter =
|
|
2134
|
+
waiter = _waitForElement.call(this, locator, { timeout: waitTimeout });
|
|
2142
2135
|
}
|
|
2143
2136
|
return waiter.catch((err) => {
|
|
2144
2137
|
throw new Error(`element (${locator.toString()}) still not present on page after ${waitTimeout / 1000} sec\n${err.message}`);
|
|
@@ -2159,7 +2152,7 @@ class Puppeteer extends Helper {
|
|
|
2159
2152
|
if (locator.isCSS()) {
|
|
2160
2153
|
waiter = context.waitForSelector(locator.simplify(), { timeout: waitTimeout, visible: true });
|
|
2161
2154
|
} else {
|
|
2162
|
-
waiter =
|
|
2155
|
+
waiter = _waitForElement.call(this, locator, { timeout: waitTimeout, visible: true });
|
|
2163
2156
|
}
|
|
2164
2157
|
return waiter.catch((err) => {
|
|
2165
2158
|
throw new Error(`element (${locator.toString()}) still not visible after ${waitTimeout / 1000} sec\n${err.message}`);
|
|
@@ -2178,7 +2171,7 @@ class Puppeteer extends Helper {
|
|
|
2178
2171
|
if (locator.isCSS()) {
|
|
2179
2172
|
waiter = context.waitForSelector(locator.simplify(), { timeout: waitTimeout, hidden: true });
|
|
2180
2173
|
} else {
|
|
2181
|
-
waiter =
|
|
2174
|
+
waiter = _waitForElement.call(this, locator, { timeout: waitTimeout, hidden: true });
|
|
2182
2175
|
}
|
|
2183
2176
|
return waiter.catch((err) => {
|
|
2184
2177
|
throw new Error(`element (${locator.toString()}) still visible after ${waitTimeout / 1000} sec\n${err.message}`);
|
|
@@ -2196,7 +2189,7 @@ class Puppeteer extends Helper {
|
|
|
2196
2189
|
if (locator.isCSS()) {
|
|
2197
2190
|
waiter = context.waitForSelector(locator.simplify(), { timeout: waitTimeout, hidden: true });
|
|
2198
2191
|
} else {
|
|
2199
|
-
waiter =
|
|
2192
|
+
waiter = _waitForElement.call(this, locator, { timeout: waitTimeout, hidden: true });
|
|
2200
2193
|
}
|
|
2201
2194
|
return waiter.catch((err) => {
|
|
2202
2195
|
throw new Error(`element (${locator.toString()}) still not hidden after ${waitTimeout / 1000} sec\n${err.message}`);
|
|
@@ -2222,7 +2215,7 @@ class Puppeteer extends Helper {
|
|
|
2222
2215
|
}
|
|
2223
2216
|
|
|
2224
2217
|
async _getContext() {
|
|
2225
|
-
if (this.context && this.context.constructor.name === '
|
|
2218
|
+
if (this.context && this.context.constructor.name === 'CdpFrame') {
|
|
2226
2219
|
return this.context;
|
|
2227
2220
|
}
|
|
2228
2221
|
return this.page;
|
|
@@ -2345,35 +2338,37 @@ class Puppeteer extends Helper {
|
|
|
2345
2338
|
async switchTo(locator) {
|
|
2346
2339
|
if (Number.isInteger(locator)) {
|
|
2347
2340
|
// Select by frame index of current context
|
|
2348
|
-
|
|
2349
|
-
let childFrames = null;
|
|
2341
|
+
let frames = [];
|
|
2350
2342
|
if (this.context && typeof this.context.childFrames === 'function') {
|
|
2351
|
-
|
|
2343
|
+
frames = await this.context.childFrames();
|
|
2352
2344
|
} else {
|
|
2353
|
-
|
|
2345
|
+
frames = await this.page.mainFrame().childFrames();
|
|
2354
2346
|
}
|
|
2355
2347
|
|
|
2356
|
-
if (locator >= 0 && locator <
|
|
2357
|
-
this.context =
|
|
2348
|
+
if (locator >= 0 && locator < frames.length) {
|
|
2349
|
+
this.context = frames[locator];
|
|
2358
2350
|
} else {
|
|
2359
|
-
throw new Error('
|
|
2351
|
+
throw new Error('Frame index out of bounds');
|
|
2360
2352
|
}
|
|
2361
2353
|
return;
|
|
2362
2354
|
}
|
|
2355
|
+
|
|
2363
2356
|
if (!locator) {
|
|
2364
|
-
this.context = await this.page.mainFrame()
|
|
2357
|
+
this.context = await this.page.mainFrame();
|
|
2365
2358
|
return;
|
|
2366
2359
|
}
|
|
2367
2360
|
|
|
2368
|
-
// iframe by selector
|
|
2361
|
+
// Select iframe by selector
|
|
2369
2362
|
const els = await this._locate(locator);
|
|
2370
2363
|
assertElementExists(els, locator);
|
|
2371
|
-
|
|
2364
|
+
|
|
2365
|
+
const iframeElement = els[0];
|
|
2366
|
+
const contentFrame = await iframeElement.contentFrame();
|
|
2372
2367
|
|
|
2373
2368
|
if (contentFrame) {
|
|
2374
2369
|
this.context = contentFrame;
|
|
2375
2370
|
} else {
|
|
2376
|
-
|
|
2371
|
+
throw new Error('Element "#invalidIframeSelector" was not found by text|CSS|XPath');
|
|
2377
2372
|
}
|
|
2378
2373
|
}
|
|
2379
2374
|
|
|
@@ -2469,7 +2464,7 @@ class Puppeteer extends Helper {
|
|
|
2469
2464
|
module.exports = Puppeteer;
|
|
2470
2465
|
|
|
2471
2466
|
async function findElements(matcher, locator) {
|
|
2472
|
-
if (locator.react) return
|
|
2467
|
+
if (locator.react) return findReactElements.call(this, locator);
|
|
2473
2468
|
locator = new Locator(locator, 'css');
|
|
2474
2469
|
if (!locator.isXPath()) return matcher.$$(locator.simplify());
|
|
2475
2470
|
// puppeteer version < 19.4.0 is no longer supported. This one is backward support.
|
|
@@ -2506,7 +2501,7 @@ async function proceedClick(locator, context = null, options = {}) {
|
|
|
2506
2501
|
}
|
|
2507
2502
|
|
|
2508
2503
|
async function findClickable(matcher, locator) {
|
|
2509
|
-
if (locator.react) return
|
|
2504
|
+
if (locator.react) return findReactElements.call(this, locator);
|
|
2510
2505
|
locator = new Locator(locator);
|
|
2511
2506
|
if (!locator.isFuzzy()) return findElements.call(this, matcher, locator);
|
|
2512
2507
|
|
|
@@ -2855,3 +2850,70 @@ function highlightActiveElement(element, context) {
|
|
|
2855
2850
|
highlightElement(element, context);
|
|
2856
2851
|
}
|
|
2857
2852
|
}
|
|
2853
|
+
|
|
2854
|
+
function _waitForElement(locator, options) {
|
|
2855
|
+
try {
|
|
2856
|
+
return this.context.waitForXPath(locator.value, options);
|
|
2857
|
+
} catch (e) {
|
|
2858
|
+
return this.context.waitForSelector(`::-p-xpath(${locator.value})`, options);
|
|
2859
|
+
}
|
|
2860
|
+
}
|
|
2861
|
+
|
|
2862
|
+
async function findReactElements(locator, props = {}, state = {}) {
|
|
2863
|
+
const resqScript = await fs.promises.readFile(require.resolve('resq'), 'utf-8');
|
|
2864
|
+
await this.page.evaluate(resqScript.toString());
|
|
2865
|
+
|
|
2866
|
+
await this.page.evaluate(() => window.resq.waitToLoadReact());
|
|
2867
|
+
const arrayHandle = await this.page.evaluateHandle((obj) => {
|
|
2868
|
+
const { selector, props, state } = obj;
|
|
2869
|
+
let elements = window.resq.resq$$(selector);
|
|
2870
|
+
if (Object.keys(props).length) {
|
|
2871
|
+
elements = elements.byProps(props);
|
|
2872
|
+
}
|
|
2873
|
+
if (Object.keys(state).length) {
|
|
2874
|
+
elements = elements.byState(state);
|
|
2875
|
+
}
|
|
2876
|
+
|
|
2877
|
+
if (!elements.length) {
|
|
2878
|
+
return [];
|
|
2879
|
+
}
|
|
2880
|
+
|
|
2881
|
+
// resq returns an array of HTMLElements if the React component is a fragment
|
|
2882
|
+
// this avoids having nested arrays of nodes which the driver does not understand
|
|
2883
|
+
// [[div, div], [div, div]] => [div, div, div, div]
|
|
2884
|
+
let nodes = [];
|
|
2885
|
+
|
|
2886
|
+
elements.forEach((element) => {
|
|
2887
|
+
let { node, isFragment } = element;
|
|
2888
|
+
|
|
2889
|
+
if (!node) {
|
|
2890
|
+
isFragment = true;
|
|
2891
|
+
node = element.children;
|
|
2892
|
+
}
|
|
2893
|
+
|
|
2894
|
+
if (isFragment) {
|
|
2895
|
+
nodes = nodes.concat(node);
|
|
2896
|
+
} else {
|
|
2897
|
+
nodes.push(node);
|
|
2898
|
+
}
|
|
2899
|
+
});
|
|
2900
|
+
|
|
2901
|
+
return [...nodes];
|
|
2902
|
+
}, {
|
|
2903
|
+
selector: locator.react,
|
|
2904
|
+
props: locator.props || {},
|
|
2905
|
+
state: locator.state || {},
|
|
2906
|
+
});
|
|
2907
|
+
|
|
2908
|
+
const properties = await arrayHandle.getProperties();
|
|
2909
|
+
const result = [];
|
|
2910
|
+
for (const property of properties.values()) {
|
|
2911
|
+
const elementHandle = property.asElement();
|
|
2912
|
+
if (elementHandle) {
|
|
2913
|
+
result.push(elementHandle);
|
|
2914
|
+
}
|
|
2915
|
+
}
|
|
2916
|
+
|
|
2917
|
+
await arrayHandle.dispose();
|
|
2918
|
+
return result;
|
|
2919
|
+
}
|
package/lib/plugin/coverage.js
CHANGED
|
@@ -1,44 +1,67 @@
|
|
|
1
1
|
const debugModule = require('debug');
|
|
2
|
-
const
|
|
3
|
-
const path = require('path');
|
|
4
|
-
|
|
2
|
+
const { CoverageReport } = require('monocart-coverage-reports');
|
|
5
3
|
const Container = require('../container');
|
|
6
4
|
const recorder = require('../recorder');
|
|
7
5
|
const event = require('../event');
|
|
8
6
|
const output = require('../output');
|
|
9
|
-
const {
|
|
7
|
+
const { deepMerge } = require('../utils');
|
|
10
8
|
|
|
11
9
|
const defaultConfig = {
|
|
12
|
-
|
|
13
|
-
|
|
10
|
+
name: 'CodeceptJS Coverage Report',
|
|
11
|
+
outputDir: 'output/coverage',
|
|
14
12
|
};
|
|
15
13
|
|
|
16
14
|
const supportedHelpers = ['Puppeteer', 'Playwright'];
|
|
17
15
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
16
|
+
const v8CoverageHelpers = {
|
|
17
|
+
Playwright: {
|
|
18
|
+
startCoverage: async (page) => {
|
|
19
|
+
await Promise.all([
|
|
20
|
+
page.coverage.startJSCoverage({
|
|
21
|
+
resetOnNavigation: false,
|
|
22
|
+
}),
|
|
23
|
+
page.coverage.startCSSCoverage({
|
|
24
|
+
resetOnNavigation: false,
|
|
25
|
+
}),
|
|
26
|
+
]);
|
|
27
|
+
},
|
|
28
|
+
takeCoverage: async (page, coverageReport) => {
|
|
29
|
+
const [jsCoverage, cssCoverage] = await Promise.all([
|
|
30
|
+
page.coverage.stopJSCoverage(),
|
|
31
|
+
page.coverage.stopCSSCoverage(),
|
|
32
|
+
]);
|
|
33
|
+
const coverageList = [...jsCoverage, ...cssCoverage];
|
|
34
|
+
await coverageReport.add(coverageList);
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
Puppeteer: {
|
|
38
|
+
startCoverage: async (page) => {
|
|
39
|
+
await Promise.all([
|
|
40
|
+
page.coverage.startJSCoverage({
|
|
41
|
+
resetOnNavigation: false,
|
|
42
|
+
includeRawScriptCoverage: true,
|
|
43
|
+
}),
|
|
44
|
+
page.coverage.startCSSCoverage({
|
|
45
|
+
resetOnNavigation: false,
|
|
46
|
+
}),
|
|
47
|
+
]);
|
|
48
|
+
},
|
|
49
|
+
takeCoverage: async (page, coverageReport) => {
|
|
50
|
+
const [jsCoverage, cssCoverage] = await Promise.all([
|
|
51
|
+
page.coverage.stopJSCoverage(),
|
|
52
|
+
page.coverage.stopCSSCoverage(),
|
|
53
|
+
]);
|
|
54
|
+
// to raw V8 script coverage
|
|
55
|
+
const coverageList = [...jsCoverage.map((it) => {
|
|
56
|
+
return {
|
|
57
|
+
source: it.text,
|
|
58
|
+
...it.rawScriptCoverage,
|
|
59
|
+
};
|
|
60
|
+
}), ...cssCoverage];
|
|
61
|
+
await coverageReport.add(coverageList);
|
|
62
|
+
},
|
|
63
|
+
},
|
|
64
|
+
};
|
|
42
65
|
|
|
43
66
|
/**
|
|
44
67
|
* Dumps code coverage from Playwright/Puppeteer after every test.
|
|
@@ -49,94 +72,84 @@ function buildFileName(test, uniqueFileName) {
|
|
|
49
72
|
* ```js
|
|
50
73
|
* plugins: {
|
|
51
74
|
* coverage: {
|
|
52
|
-
* enabled: true
|
|
75
|
+
* enabled: true,
|
|
76
|
+
* debug: true,
|
|
77
|
+
* name: 'CodeceptJS Coverage Report',
|
|
78
|
+
* outputDir: 'output/coverage'
|
|
53
79
|
* }
|
|
54
80
|
* }
|
|
55
81
|
* ```
|
|
56
82
|
*
|
|
57
|
-
* Possible config options
|
|
83
|
+
* Possible config options, More could be found at [monocart-coverage-reports](https://github.com/cenfun/monocart-coverage-reports?tab=readme-ov-file#default-options)
|
|
84
|
+
*
|
|
85
|
+
* * `debug`: debug info. By default, false.
|
|
86
|
+
* * `name`: coverage report name.
|
|
87
|
+
* * `outputDir`: path to coverage report.
|
|
88
|
+
* * `sourceFilter`: filter the source files.
|
|
89
|
+
* * `sourcePath`: option to resolve a custom path.
|
|
58
90
|
*
|
|
59
|
-
* * `coverageDir`: directory to dump coverage files
|
|
60
|
-
* * `uniqueFileName`: generate a unique filename by adding uuid
|
|
61
91
|
*/
|
|
62
92
|
module.exports = function (config) {
|
|
93
|
+
config = deepMerge(defaultConfig, config);
|
|
94
|
+
|
|
95
|
+
if (config.debug) config.logging = 'debug';
|
|
96
|
+
|
|
63
97
|
const helpers = Container.helpers();
|
|
64
98
|
let coverageRunning = false;
|
|
65
|
-
let helper;
|
|
66
99
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
100
|
+
const v8Names = Object.keys(v8CoverageHelpers);
|
|
101
|
+
const helperName = Object.keys(helpers).find((it) => v8Names.includes(it));
|
|
102
|
+
if (!helperName) {
|
|
103
|
+
console.error(`Coverage is only supported in ${supportedHelpers.join(' or ')}`);
|
|
104
|
+
// no helpers for screenshot
|
|
105
|
+
return;
|
|
73
106
|
}
|
|
74
107
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
108
|
+
config.name = `${config.name} - in ${helperName}`;
|
|
109
|
+
const debug = debugModule(`codeceptjs:plugin:${helperName.toLowerCase()}Coverage`);
|
|
110
|
+
|
|
111
|
+
const helper = helpers[helperName];
|
|
112
|
+
const v8Helper = v8CoverageHelpers[helperName];
|
|
79
113
|
|
|
80
|
-
const
|
|
114
|
+
const coverageOptions = {
|
|
115
|
+
...config,
|
|
116
|
+
};
|
|
117
|
+
const coverageReport = new CoverageReport(coverageOptions);
|
|
118
|
+
coverageReport.cleanCache();
|
|
81
119
|
|
|
82
|
-
event.dispatcher.on(event.all.
|
|
83
|
-
output.
|
|
120
|
+
event.dispatcher.on(event.all.after, async () => {
|
|
121
|
+
output.print(`writing ${coverageOptions.outputDir}`);
|
|
122
|
+
await coverageReport.generate();
|
|
84
123
|
});
|
|
85
124
|
|
|
86
|
-
//
|
|
125
|
+
// we're going to try to "start" coverage before each step because this is
|
|
87
126
|
// when the browser is already up and is ready to start coverage.
|
|
88
|
-
event.dispatcher.on(event.step.before,
|
|
89
|
-
recorder.add(
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
}
|
|
101
|
-
},
|
|
102
|
-
true,
|
|
103
|
-
);
|
|
127
|
+
event.dispatcher.on(event.step.before, () => {
|
|
128
|
+
recorder.add('start coverage', async () => {
|
|
129
|
+
if (coverageRunning) {
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
if (!helper.page || !helper.page.coverage) {
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
coverageRunning = true;
|
|
136
|
+
debug('--> starting coverage <--');
|
|
137
|
+
await v8Helper.startCoverage(helper.page);
|
|
138
|
+
}, true);
|
|
104
139
|
});
|
|
105
140
|
|
|
106
141
|
// Save coverage data after every test run
|
|
107
|
-
event.dispatcher.on(event.test.after,
|
|
108
|
-
recorder.add(
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
options.coverageDir,
|
|
120
|
-
);
|
|
121
|
-
|
|
122
|
-
// Checking if coverageDir already exists, if not, create new one
|
|
123
|
-
|
|
124
|
-
if (!fs.existsSync(coverageDir)) {
|
|
125
|
-
fs.mkdirSync(coverageDir, { recursive: true });
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
const coveragePath = path.resolve(
|
|
129
|
-
coverageDir,
|
|
130
|
-
buildFileName(test, options.uniqueFileName),
|
|
131
|
-
);
|
|
132
|
-
output.print(`writing ${coveragePath}`);
|
|
133
|
-
fs.writeFileSync(coveragePath, JSON.stringify(coverage));
|
|
134
|
-
}
|
|
135
|
-
} catch (err) {
|
|
136
|
-
console.error(err);
|
|
137
|
-
}
|
|
138
|
-
},
|
|
139
|
-
true,
|
|
140
|
-
);
|
|
142
|
+
event.dispatcher.on(event.test.after, (test) => {
|
|
143
|
+
recorder.add('take coverage', async () => {
|
|
144
|
+
if (!coverageRunning) {
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
if (!helper.page || !helper.page.coverage) {
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
coverageRunning = false;
|
|
151
|
+
debug('--> stopping coverage <--');
|
|
152
|
+
await v8Helper.takeCoverage(helper.page, coverageReport);
|
|
153
|
+
}, true);
|
|
141
154
|
});
|
|
142
155
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codeceptjs",
|
|
3
|
-
"version": "3.5.
|
|
3
|
+
"version": "3.5.15",
|
|
4
4
|
"description": "Supercharged End 2 End Testing Framework for NodeJS",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"acceptance",
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
"docs/webapi/**"
|
|
30
30
|
],
|
|
31
31
|
"main": "lib/index.js",
|
|
32
|
-
"
|
|
32
|
+
"types": "typings/index.d.ts",
|
|
33
33
|
"bin": {
|
|
34
34
|
"codeceptjs": "./bin/codecept.js"
|
|
35
35
|
},
|
|
@@ -62,12 +62,13 @@
|
|
|
62
62
|
"dev:graphql": "node test/data/graphql/index.js",
|
|
63
63
|
"publish:site": "./runok.js publish:site",
|
|
64
64
|
"update-contributor-faces": "./runok.js contributor:faces",
|
|
65
|
-
"
|
|
65
|
+
"types-fix": "node typings/fixDefFiles.js",
|
|
66
|
+
"dtslint": "npm run types-fix && tsd",
|
|
66
67
|
"prepare": "husky install",
|
|
67
68
|
"prepare-release": "./runok.js versioning && ./runok.js get:commit-log"
|
|
68
69
|
},
|
|
69
70
|
"dependencies": {
|
|
70
|
-
"@codeceptjs/configure": "0.
|
|
71
|
+
"@codeceptjs/configure": "1.0.1",
|
|
71
72
|
"@codeceptjs/helper": "2.0.1",
|
|
72
73
|
"@cucumber/cucumber-expressions": "17",
|
|
73
74
|
"@cucumber/gherkin": "26",
|
|
@@ -76,7 +77,7 @@
|
|
|
76
77
|
"acorn": "8.11.3",
|
|
77
78
|
"arrify": "2.0.1",
|
|
78
79
|
"axios": "1.6.7",
|
|
79
|
-
"chai": "5.0
|
|
80
|
+
"chai": "5.1.0",
|
|
80
81
|
"chai-deep-match": "1.2.1",
|
|
81
82
|
"chai-exclude": "2.1.0",
|
|
82
83
|
"chai-json-schema": "1.5.1",
|
|
@@ -88,7 +89,7 @@
|
|
|
88
89
|
"cross-spawn": "7.0.3",
|
|
89
90
|
"css-to-xpath": "0.1.0",
|
|
90
91
|
"csstoxpath": "1.6.0",
|
|
91
|
-
"devtools": "8.
|
|
92
|
+
"devtools": "8.33.1",
|
|
92
93
|
"envinfo": "7.11.0",
|
|
93
94
|
"escape-string-regexp": "4.0.0",
|
|
94
95
|
"figures": "3.2.0",
|
|
@@ -97,16 +98,17 @@
|
|
|
97
98
|
"glob": "6.0.1",
|
|
98
99
|
"html-minifier-terser": "7.2.0",
|
|
99
100
|
"inquirer": "6.5.2",
|
|
100
|
-
"joi": "17.12.
|
|
101
|
-
"js-beautify": "1.
|
|
101
|
+
"joi": "17.12.2",
|
|
102
|
+
"js-beautify": "1.15.1",
|
|
102
103
|
"lodash.clonedeep": "4.5.0",
|
|
103
104
|
"lodash.merge": "4.6.2",
|
|
104
105
|
"mkdirp": "1.0.4",
|
|
105
|
-
"mocha": "10.
|
|
106
|
+
"mocha": "10.3.0",
|
|
107
|
+
"monocart-coverage-reports": "2.7.1",
|
|
106
108
|
"ms": "2.1.3",
|
|
107
109
|
"openai": "3.2.1",
|
|
108
110
|
"ora-classic": "5.4.2",
|
|
109
|
-
"pactum": "3.6.
|
|
111
|
+
"pactum": "3.6.1",
|
|
110
112
|
"parse-function": "5.6.10",
|
|
111
113
|
"parse5": "7.1.2",
|
|
112
114
|
"promise-retry": "1.1.1",
|
|
@@ -115,7 +117,7 @@
|
|
|
115
117
|
"uuid": "9.0"
|
|
116
118
|
},
|
|
117
119
|
"optionalDependencies": {
|
|
118
|
-
"@codeceptjs/detox-helper": "1.0.
|
|
120
|
+
"@codeceptjs/detox-helper": "1.0.5"
|
|
119
121
|
},
|
|
120
122
|
"devDependencies": {
|
|
121
123
|
"@codeceptjs/mock-request": "0.3.1",
|
|
@@ -125,7 +127,7 @@
|
|
|
125
127
|
"@types/chai": "4.3.11",
|
|
126
128
|
"@types/inquirer": "9.0.3",
|
|
127
129
|
"@types/node": "20.11.16",
|
|
128
|
-
"@wdio/sauce-service": "8.
|
|
130
|
+
"@wdio/sauce-service": "8.32.3",
|
|
129
131
|
"@wdio/selenium-standalone-service": "8.3.2",
|
|
130
132
|
"@wdio/utils": "8.28.8",
|
|
131
133
|
"@xmldom/xmldom": "0.8.10",
|
|
@@ -134,14 +136,13 @@
|
|
|
134
136
|
"chai-subset": "1.6.0",
|
|
135
137
|
"contributor-faces": "1.1.0",
|
|
136
138
|
"documentation": "12.3.0",
|
|
137
|
-
"dtslint": "4.2.1",
|
|
138
139
|
"electron": "28.2.1",
|
|
139
140
|
"eslint": "8.56.0",
|
|
140
141
|
"eslint-config-airbnb-base": "15.0.0",
|
|
141
142
|
"eslint-plugin-import": "2.29.1",
|
|
142
143
|
"eslint-plugin-mocha": "6.3.0",
|
|
143
144
|
"expect": "29.7.0",
|
|
144
|
-
"express": "4.18.
|
|
145
|
+
"express": "4.18.3",
|
|
145
146
|
"graphql": "14.6.0",
|
|
146
147
|
"husky": "8.0.3",
|
|
147
148
|
"inquirer-test": "2.0.1",
|
|
@@ -149,7 +150,7 @@
|
|
|
149
150
|
"jsdoc-typeof-plugin": "1.0.0",
|
|
150
151
|
"json-server": "0.10.1",
|
|
151
152
|
"playwright": "1.41.1",
|
|
152
|
-
"puppeteer": "
|
|
153
|
+
"puppeteer": "22.4.1",
|
|
153
154
|
"qrcode-terminal": "0.12.0",
|
|
154
155
|
"rosie": "2.1.1",
|
|
155
156
|
"runok": "0.9.3",
|
|
@@ -158,12 +159,13 @@
|
|
|
158
159
|
"testcafe": "3.5.0",
|
|
159
160
|
"ts-morph": "21.0.1",
|
|
160
161
|
"ts-node": "10.9.2",
|
|
162
|
+
"tsd": "^0.30.7",
|
|
161
163
|
"tsd-jsdoc": "2.5.0",
|
|
162
|
-
"typedoc": "0.25.
|
|
164
|
+
"typedoc": "0.25.12",
|
|
163
165
|
"typedoc-plugin-markdown": "3.17.1",
|
|
164
166
|
"typescript": "5.3.3",
|
|
165
167
|
"wdio-docker-service": "1.5.0",
|
|
166
|
-
"webdriverio": "8.
|
|
168
|
+
"webdriverio": "8.33.1",
|
|
167
169
|
"xml2js": "0.6.2",
|
|
168
170
|
"xpath": "0.0.34"
|
|
169
171
|
},
|
|
@@ -171,5 +173,11 @@
|
|
|
171
173
|
"node": ">=16.0",
|
|
172
174
|
"npm": ">=5.6.0"
|
|
173
175
|
},
|
|
174
|
-
"es6": true
|
|
175
|
-
|
|
176
|
+
"es6": true,
|
|
177
|
+
"tsd": {
|
|
178
|
+
"directory": "typings",
|
|
179
|
+
"compilerOptions": {
|
|
180
|
+
"strict": false
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
package/typings/index.d.ts
CHANGED
|
@@ -1127,9 +1127,9 @@ declare namespace CodeceptJS {
|
|
|
1127
1127
|
expectNotEndsWith(actualValue: any, expectedValueToNotEndWith: any, customErrorMsg?: any): Promise<any>;
|
|
1128
1128
|
expectJsonSchema(targetData: any, jsonSchema: any, customErrorMsg?: any): Promise<any>;
|
|
1129
1129
|
/**
|
|
1130
|
-
* @param ajvOptions - Pass AJV options
|
|
1130
|
+
* @param [ajvOptions] - Pass AJV options
|
|
1131
1131
|
*/
|
|
1132
|
-
expectJsonSchemaUsingAJV(targetData: any, jsonSchema: any, customErrorMsg?: any, ajvOptions
|
|
1132
|
+
expectJsonSchemaUsingAJV(targetData: any, jsonSchema: any, customErrorMsg?: any, ajvOptions?: any): Promise<any>;
|
|
1133
1133
|
expectHasProperty(targetData: any, propertyName: any, customErrorMsg?: any): Promise<any>;
|
|
1134
1134
|
expectHasAProperty(targetData: any, propertyName: any, customErrorMsg?: any): Promise<any>;
|
|
1135
1135
|
expectToBeA(targetData: any, type: any, customErrorMsg?: any): Promise<any>;
|
package/typings/types.d.ts
CHANGED
|
@@ -1151,9 +1151,9 @@ declare namespace CodeceptJS {
|
|
|
1151
1151
|
expectNotEndsWith(actualValue: any, expectedValueToNotEndWith: any, customErrorMsg?: any): void;
|
|
1152
1152
|
expectJsonSchema(targetData: any, jsonSchema: any, customErrorMsg?: any): void;
|
|
1153
1153
|
/**
|
|
1154
|
-
* @param ajvOptions - Pass AJV options
|
|
1154
|
+
* @param [ajvOptions] - Pass AJV options
|
|
1155
1155
|
*/
|
|
1156
|
-
expectJsonSchemaUsingAJV(targetData: any, jsonSchema: any, customErrorMsg?: any, ajvOptions
|
|
1156
|
+
expectJsonSchemaUsingAJV(targetData: any, jsonSchema: any, customErrorMsg?: any, ajvOptions?: any): void;
|
|
1157
1157
|
expectHasProperty(targetData: any, propertyName: any, customErrorMsg?: any): void;
|
|
1158
1158
|
expectHasAProperty(targetData: any, propertyName: any, customErrorMsg?: any): void;
|
|
1159
1159
|
expectToBeA(targetData: any, type: any, customErrorMsg?: any): void;
|
|
@@ -12184,14 +12184,14 @@ declare namespace CodeceptJS {
|
|
|
12184
12184
|
* Detox provides a grey box testing for mobile applications, playing especially good for React Native apps.
|
|
12185
12185
|
*
|
|
12186
12186
|
* Detox plays quite differently from Appium. To establish detox testing you need to build a mobile application in a special way to inject Detox code.
|
|
12187
|
-
* This why **Detox is grey box testing** solution, so you need
|
|
12187
|
+
* This why **Detox is grey box testing** solution, so you need access to application source code, and a way to build and execute it on emulator.
|
|
12188
12188
|
*
|
|
12189
12189
|
* Comparing to Appium, Detox runs faster and more stable but requires an additional setup for build.
|
|
12190
12190
|
*
|
|
12191
12191
|
* ### Setup
|
|
12192
12192
|
*
|
|
12193
|
-
* 1. [Install and configure Detox
|
|
12194
|
-
* 2. [Build an application](https://github.
|
|
12193
|
+
* 1. [Install and configure Detox](https://wix.github.io/Detox/docs/introduction/project-setup)
|
|
12194
|
+
* 2. [Build an application](https://wix.github.io/Detox/docs/introduction/project-setup#step-5-build-the-app) using `detox build` command.
|
|
12195
12195
|
* 3. Install [CodeceptJS](https://codecept.io) and detox-helper:
|
|
12196
12196
|
*
|
|
12197
12197
|
* ```
|
|
@@ -12204,15 +12204,28 @@ declare namespace CodeceptJS {
|
|
|
12204
12204
|
*
|
|
12205
12205
|
* ```js
|
|
12206
12206
|
* "detox": {
|
|
12207
|
-
*
|
|
12208
|
-
*
|
|
12209
|
-
*
|
|
12210
|
-
*
|
|
12211
|
-
*
|
|
12212
|
-
*
|
|
12213
|
-
*
|
|
12214
|
-
*
|
|
12215
|
-
*
|
|
12207
|
+
* "configurations": {
|
|
12208
|
+
* "ios.sim.debug": {
|
|
12209
|
+
* "device": "simulator",
|
|
12210
|
+
* "app": "ios.debug"
|
|
12211
|
+
* }
|
|
12212
|
+
* },
|
|
12213
|
+
* "apps": {
|
|
12214
|
+
* "ios.debug": {
|
|
12215
|
+
* "type": "ios.app",
|
|
12216
|
+
* "binaryPath": "../test/ios/build/Build/Products/Debug-iphonesimulator/MyTestApp.app",
|
|
12217
|
+
* "build": "xcodebuild -workspace ../test/ios/MyTestApp.xcworkspace -scheme MyTestApp -configuration Debug -sdk iphonesimulator -derivedDataPath ../test/ios/build"
|
|
12218
|
+
* }
|
|
12219
|
+
* },
|
|
12220
|
+
* "devices": {
|
|
12221
|
+
* "simulator": {
|
|
12222
|
+
* "type": "ios.simulator",
|
|
12223
|
+
* "device": {
|
|
12224
|
+
* "type": "iPhone 15"
|
|
12225
|
+
* }
|
|
12226
|
+
* }
|
|
12227
|
+
* }
|
|
12228
|
+
* }
|
|
12216
12229
|
* ```
|
|
12217
12230
|
*
|
|
12218
12231
|
*
|
|
@@ -12307,6 +12320,14 @@ declare namespace CodeceptJS {
|
|
|
12307
12320
|
* ```
|
|
12308
12321
|
*/
|
|
12309
12322
|
setPortraitOrientation(): void;
|
|
12323
|
+
/**
|
|
12324
|
+
* Grab the device platform
|
|
12325
|
+
*
|
|
12326
|
+
* ```js
|
|
12327
|
+
* const platform = await I.grabPlatform();
|
|
12328
|
+
* ```
|
|
12329
|
+
*/
|
|
12330
|
+
grabPlatform(): void;
|
|
12310
12331
|
/**
|
|
12311
12332
|
* Execute code only on iOS
|
|
12312
12333
|
*
|
|
@@ -12396,6 +12417,19 @@ declare namespace CodeceptJS {
|
|
|
12396
12417
|
* ```
|
|
12397
12418
|
*/
|
|
12398
12419
|
click(locator: CodeceptJS.LocatorOrString, context?: CodeceptJS.LocatorOrString | null): void;
|
|
12420
|
+
/**
|
|
12421
|
+
* Clicks on an element.
|
|
12422
|
+
* Element can be located by its label
|
|
12423
|
+
*
|
|
12424
|
+
* The second parameter is a context (id | type | accessibility id) to narrow the search.
|
|
12425
|
+
*
|
|
12426
|
+
*
|
|
12427
|
+
* ```js
|
|
12428
|
+
* I.tapByLabel('Login'); // locate by text
|
|
12429
|
+
* I.tapByLabel('Login', '#nav'); // locate by text inside #nav
|
|
12430
|
+
* ```
|
|
12431
|
+
*/
|
|
12432
|
+
tapByLabel(locator: CodeceptJS.LocatorOrString, context?: CodeceptJS.LocatorOrString | null): void;
|
|
12399
12433
|
/**
|
|
12400
12434
|
* Performs click on element with horizontal and vertical offset.
|
|
12401
12435
|
* An element is located by text, id, accessibility id.
|
|
@@ -12446,6 +12480,17 @@ declare namespace CodeceptJS {
|
|
|
12446
12480
|
* @param [context = null] - context element
|
|
12447
12481
|
*/
|
|
12448
12482
|
seeElement(locator: CodeceptJS.LocatorOrString, context?: CodeceptJS.LocatorOrString | null): void;
|
|
12483
|
+
/**
|
|
12484
|
+
* Checks if an element exists.
|
|
12485
|
+
*
|
|
12486
|
+
* ```js
|
|
12487
|
+
* I.checkIfElementExists('~edit'); // located by accessibility id
|
|
12488
|
+
* I.checkIfElementExists('~edit', '#menu'); // element inside #menu
|
|
12489
|
+
* ```
|
|
12490
|
+
* @param locator - element to locate
|
|
12491
|
+
* @param [context = null] - context element
|
|
12492
|
+
*/
|
|
12493
|
+
checkIfElementExists(locator: CodeceptJS.LocatorOrString, context?: CodeceptJS.LocatorOrString | null): void;
|
|
12449
12494
|
/**
|
|
12450
12495
|
* Checks that element is not visible.
|
|
12451
12496
|
* Use second parameter to narrow down the search.
|
|
@@ -12495,6 +12540,18 @@ declare namespace CodeceptJS {
|
|
|
12495
12540
|
* @param value - value to fill
|
|
12496
12541
|
*/
|
|
12497
12542
|
fillField(field: CodeceptJS.LocatorOrString, value: string): void;
|
|
12543
|
+
/**
|
|
12544
|
+
* Taps return key.
|
|
12545
|
+
* A field can be located by text, accessibility id, id.
|
|
12546
|
+
*
|
|
12547
|
+
* ```js
|
|
12548
|
+
* I.tapReturnKey('Username');
|
|
12549
|
+
* I.tapReturnKey('~name');
|
|
12550
|
+
* I.tapReturnKey({ android: 'NAME', ios: 'name' });
|
|
12551
|
+
* ```
|
|
12552
|
+
* @param field - an input element to fill in
|
|
12553
|
+
*/
|
|
12554
|
+
tapReturnKey(field: CodeceptJS.LocatorOrString): void;
|
|
12498
12555
|
/**
|
|
12499
12556
|
* Clears a text field.
|
|
12500
12557
|
* A field can be located by text, accessibility id, id.
|
|
@@ -12620,7 +12677,7 @@ declare namespace CodeceptJS {
|
|
|
12620
12677
|
*/
|
|
12621
12678
|
waitForElementVisible(locator: CodeceptJS.LocatorOrString, sec?: number): void;
|
|
12622
12679
|
/**
|
|
12623
|
-
* Waits an
|
|
12680
|
+
* Waits an elmenet to become not visible.
|
|
12624
12681
|
*
|
|
12625
12682
|
* ```js
|
|
12626
12683
|
* I.waitToHide('#message', 2); // wait for 2 seconds
|
|
@@ -12629,6 +12686,14 @@ declare namespace CodeceptJS {
|
|
|
12629
12686
|
* @param [sec = 5] - number of seconds to wait
|
|
12630
12687
|
*/
|
|
12631
12688
|
waitToHide(locator: CodeceptJS.LocatorOrString, sec?: number): void;
|
|
12689
|
+
/**
|
|
12690
|
+
* Scrolls within a scrollable container to an element.
|
|
12691
|
+
* @param targetLocator - Locator of the element to scroll to
|
|
12692
|
+
* @param containerLocator - Locator of the scrollable container
|
|
12693
|
+
* @param direction - 'up' or 'down'
|
|
12694
|
+
* @param [offset = 100] - Offset for scroll, can be adjusted based on need
|
|
12695
|
+
*/
|
|
12696
|
+
scrollToElement(targetLocator: CodeceptJS.LocatorOrString, containerLocator: CodeceptJS.LocatorOrString, direction?: string, offset?: number): void;
|
|
12632
12697
|
}
|
|
12633
12698
|
/**
|
|
12634
12699
|
* Abstract class.
|