codeceptjs 4.0.0-rc.23 → 4.0.0-rc.25
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 +9 -10
- package/docs/ai.md +3 -51
- package/docs/architecture.md +16 -0
- package/docs/bootstrap.md +1 -1
- package/docs/continuous-integration.md +16 -44
- package/docs/custom-helpers.md +1 -1
- package/docs/detox.md +1 -1
- package/docs/docker.md +1 -30
- package/docs/examples.md +0 -1
- package/docs/helpers/Appium.md +16 -2
- package/docs/helpers/Playwright.md +161 -160
- package/docs/helpers/Puppeteer.md +143 -250
- package/docs/helpers/WebDriver.md +134 -177
- package/docs/hooks.md +11 -1
- package/docs/index.md +1 -1
- package/docs/installation.md +2 -19
- package/docs/locators.md +1 -1
- package/docs/migrate-from-cypress.md +98 -0
- package/docs/migrate-from-java.md +108 -0
- package/docs/migrate-from-protractor.md +101 -0
- package/docs/migrate-from-testcafe.md +99 -0
- package/docs/migration-4.md +195 -8
- package/docs/plugins/aiTrace.md +49 -0
- package/docs/plugins/analyze.md +66 -0
- package/docs/plugins/auth.md +241 -0
- package/docs/plugins/autoDelay.md +48 -0
- package/docs/plugins/browser.md +41 -0
- package/docs/plugins/coverage.md +39 -0
- package/docs/plugins/customLocator.md +119 -0
- package/docs/plugins/customReporter.md +16 -0
- package/docs/plugins/expose.md +75 -0
- package/docs/plugins/heal.md +44 -0
- package/docs/plugins/junitReporter.md +51 -0
- package/docs/plugins/pageInfo.md +34 -0
- package/docs/plugins/pause.md +43 -0
- package/docs/plugins/pauseOnFail.md +18 -0
- package/docs/plugins/retryFailedStep.md +75 -0
- package/docs/plugins/screencast.md +55 -0
- package/docs/plugins/screenshot.md +58 -0
- package/docs/plugins/screenshotOnFail.md +18 -0
- package/docs/plugins/stepTimeout.md +65 -0
- package/docs/plugins.md +40 -862
- package/docs/reports.md +18 -4
- package/docs/retry.md +48 -18
- package/docs/store.md +94 -0
- package/docs/timeouts.md +1 -1
- package/docs/tutorial.md +207 -155
- package/docs/webdriver.md +6 -73
- package/lib/actor.js +0 -35
- package/lib/command/run-multiple.js +1 -2
- package/lib/helper/Playwright.js +1 -15
- package/lib/helper/Puppeteer.js +0 -103
- package/lib/helper/WebDriver.js +1 -28
- package/lib/helper/extras/PlaywrightLocator.js +10 -0
- package/lib/locator.js +0 -13
- package/lib/plugin/analyze.js +3 -4
- package/lib/plugin/pauseOnFail.js +3 -1
- package/lib/plugin/retryFailedStep.js +7 -7
- package/lib/plugin/screenshot.js +0 -5
- package/lib/plugin/screenshotOnFail.js +3 -1
- package/lib/plugin/stepTimeout.js +1 -1
- package/lib/recorder.js +1 -1
- package/lib/workers.js +0 -4
- package/package.json +3 -4
- package/docs/helpers/Mochawesome.md +0 -8
- package/docs/helpers/MockServer.md +0 -212
- package/docs/helpers/Polly.md +0 -44
- package/docs/helpers/Protractor.md +0 -1769
- package/docs/helpers/SoftExpectHelper.md +0 -352
- package/docs/react.md +0 -70
- package/lib/helper/Mochawesome.js +0 -96
- package/lib/helper/extras/PlaywrightReactVueLocator.js +0 -61
- package/lib/helper/extras/React.js +0 -65
|
@@ -138,9 +138,8 @@ function executeRun(runName, runConfig) {
|
|
|
138
138
|
|
|
139
139
|
outputDir = clearString(outputDir)
|
|
140
140
|
|
|
141
|
-
// tweaking default output directories
|
|
141
|
+
// tweaking default output directories
|
|
142
142
|
overriddenConfig = replaceValueDeep(overriddenConfig, 'output', path.join(config.output, outputDir))
|
|
143
|
-
overriddenConfig = replaceValueDeep(overriddenConfig, 'reportDir', path.join(config.output, outputDir))
|
|
144
143
|
overriddenConfig = replaceValueDeep(overriddenConfig, 'mochaFile', path.join(config.output, outputDir, `${browserName}_report.xml`))
|
|
145
144
|
|
|
146
145
|
// override tests configuration
|
package/lib/helper/Playwright.js
CHANGED
|
@@ -36,7 +36,7 @@ import MultipleElementsFound from './errors/MultipleElementsFound.js'
|
|
|
36
36
|
import RemoteBrowserConnectionRefused from './errors/RemoteBrowserConnectionRefused.js'
|
|
37
37
|
import Popup from './extras/Popup.js'
|
|
38
38
|
import Console from './extras/Console.js'
|
|
39
|
-
import {
|
|
39
|
+
import { findByPlaywrightLocator } from './extras/PlaywrightLocator.js'
|
|
40
40
|
import { dropFile } from './scripts/dropFile.js'
|
|
41
41
|
import WebElement from '../element/WebElement.js'
|
|
42
42
|
import { selectElement } from './extras/elementSelection.js'
|
|
@@ -2081,15 +2081,6 @@ class Playwright extends Helper {
|
|
|
2081
2081
|
return proceedClick.call(this, locator, context, options)
|
|
2082
2082
|
}
|
|
2083
2083
|
|
|
2084
|
-
/**
|
|
2085
|
-
* Clicks link and waits for navigation (deprecated)
|
|
2086
|
-
*/
|
|
2087
|
-
async clickLink(locator, context = null) {
|
|
2088
|
-
console.log('clickLink deprecated: Playwright automatically waits for navigation to happen.')
|
|
2089
|
-
console.log('Replace I.clickLink with I.click')
|
|
2090
|
-
return this.click(locator, context)
|
|
2091
|
-
}
|
|
2092
|
-
|
|
2093
2084
|
/**
|
|
2094
2085
|
* {{> forceClick }}
|
|
2095
2086
|
*/
|
|
@@ -3306,8 +3297,6 @@ class Playwright extends Helper {
|
|
|
3306
3297
|
}
|
|
3307
3298
|
|
|
3308
3299
|
/**
|
|
3309
|
-
* This method accepts [React selectors](https://codecept.io/react).
|
|
3310
|
-
*
|
|
3311
3300
|
* {{> waitForVisible }}
|
|
3312
3301
|
*/
|
|
3313
3302
|
async waitForVisible(locator, sec) {
|
|
@@ -4231,10 +4220,8 @@ async function findByRole(context, locator) {
|
|
|
4231
4220
|
}
|
|
4232
4221
|
|
|
4233
4222
|
async function findElements(matcher, locator) {
|
|
4234
|
-
const isReactLocator = locator.type === 'react' || (locator.locator && locator.locator.react) || locator.react
|
|
4235
4223
|
const isPwLocator = locator.type === 'pw' || (locator.locator && locator.locator.pw) || locator.pw
|
|
4236
4224
|
|
|
4237
|
-
if (isReactLocator) return findReact(matcher, locator)
|
|
4238
4225
|
if (isPwLocator) return findByPlaywrightLocator.call(this, matcher, locator)
|
|
4239
4226
|
|
|
4240
4227
|
// Handle role locators with text/exact options (e.g., {role: 'button', text: 'Submit', exact: true})
|
|
@@ -4249,7 +4236,6 @@ async function findElements(matcher, locator) {
|
|
|
4249
4236
|
}
|
|
4250
4237
|
|
|
4251
4238
|
async function findElement(matcher, locator) {
|
|
4252
|
-
if (locator.react) return findReact(matcher, locator)
|
|
4253
4239
|
if (locator.pw) return findByPlaywrightLocator.call(this, matcher, locator)
|
|
4254
4240
|
|
|
4255
4241
|
locator = new Locator(locator, 'css')
|
package/lib/helper/Puppeteer.js
CHANGED
|
@@ -821,7 +821,6 @@ class Puppeteer extends Helper {
|
|
|
821
821
|
|
|
822
822
|
/**
|
|
823
823
|
* {{> moveCursorTo }}
|
|
824
|
-
* {{ react }}
|
|
825
824
|
*/
|
|
826
825
|
async moveCursorTo(locator, offsetX = 0, offsetY = 0) {
|
|
827
826
|
let context = null
|
|
@@ -992,7 +991,6 @@ class Puppeteer extends Helper {
|
|
|
992
991
|
* const elements = await this.helpers['Puppeteer']._locate({name: 'password'});
|
|
993
992
|
* ```
|
|
994
993
|
*
|
|
995
|
-
* {{ react }}
|
|
996
994
|
*/
|
|
997
995
|
async _locate(locator) {
|
|
998
996
|
const context = await this.context
|
|
@@ -1007,7 +1005,6 @@ class Puppeteer extends Helper {
|
|
|
1007
1005
|
* const element = await this.helpers['Puppeteer']._locateElement({name: 'password'});
|
|
1008
1006
|
* ```
|
|
1009
1007
|
*
|
|
1010
|
-
* {{ react }}
|
|
1011
1008
|
*/
|
|
1012
1009
|
async _locateElement(locator) {
|
|
1013
1010
|
const context = await this.context
|
|
@@ -1191,7 +1188,6 @@ class Puppeteer extends Helper {
|
|
|
1191
1188
|
|
|
1192
1189
|
/**
|
|
1193
1190
|
* {{> seeElement }}
|
|
1194
|
-
* {{ react }}
|
|
1195
1191
|
*/
|
|
1196
1192
|
async seeElement(locator, context = null) {
|
|
1197
1193
|
let els
|
|
@@ -1215,7 +1211,6 @@ class Puppeteer extends Helper {
|
|
|
1215
1211
|
|
|
1216
1212
|
/**
|
|
1217
1213
|
* {{> dontSeeElement }}
|
|
1218
|
-
* {{ react }}
|
|
1219
1214
|
*/
|
|
1220
1215
|
async dontSeeElement(locator, context = null) {
|
|
1221
1216
|
let els
|
|
@@ -1264,7 +1259,6 @@ class Puppeteer extends Helper {
|
|
|
1264
1259
|
/**
|
|
1265
1260
|
* {{> click }}
|
|
1266
1261
|
*
|
|
1267
|
-
* {{ react }}
|
|
1268
1262
|
*/
|
|
1269
1263
|
async click(locator = '//body', context = null) {
|
|
1270
1264
|
return proceedClick.call(this, locator, context)
|
|
@@ -1273,7 +1267,6 @@ class Puppeteer extends Helper {
|
|
|
1273
1267
|
/**
|
|
1274
1268
|
* {{> forceClick }}
|
|
1275
1269
|
*
|
|
1276
|
-
* {{ react }}
|
|
1277
1270
|
*/
|
|
1278
1271
|
async forceClick(locator, context = null) {
|
|
1279
1272
|
let matcher = await this.context
|
|
@@ -1303,7 +1296,6 @@ class Puppeteer extends Helper {
|
|
|
1303
1296
|
/**
|
|
1304
1297
|
* {{> clickLink }}
|
|
1305
1298
|
*
|
|
1306
|
-
* {{ react }}
|
|
1307
1299
|
*/
|
|
1308
1300
|
async clickLink(locator, context = null) {
|
|
1309
1301
|
return proceedClick.call(this, locator, context, { waitForNavigation: true })
|
|
@@ -1411,7 +1403,6 @@ class Puppeteer extends Helper {
|
|
|
1411
1403
|
/**
|
|
1412
1404
|
* {{> doubleClick }}
|
|
1413
1405
|
*
|
|
1414
|
-
* {{ react }}
|
|
1415
1406
|
*/
|
|
1416
1407
|
async doubleClick(locator, context = null) {
|
|
1417
1408
|
return proceedClick.call(this, locator, context, { clickCount: 2 })
|
|
@@ -1420,7 +1411,6 @@ class Puppeteer extends Helper {
|
|
|
1420
1411
|
/**
|
|
1421
1412
|
* {{> rightClick }}
|
|
1422
1413
|
*
|
|
1423
|
-
* {{ react }}
|
|
1424
1414
|
*/
|
|
1425
1415
|
async rightClick(locator, context = null) {
|
|
1426
1416
|
return proceedClick.call(this, locator, context, { button: 'right' })
|
|
@@ -1593,7 +1583,6 @@ class Puppeteer extends Helper {
|
|
|
1593
1583
|
|
|
1594
1584
|
/**
|
|
1595
1585
|
* {{> fillField }}
|
|
1596
|
-
* {{ react }}
|
|
1597
1586
|
*/
|
|
1598
1587
|
async fillField(field, value, context = null) {
|
|
1599
1588
|
let els = await findVisibleFields.call(this, field, context)
|
|
@@ -1632,7 +1621,6 @@ class Puppeteer extends Helper {
|
|
|
1632
1621
|
/**
|
|
1633
1622
|
* {{> appendField }}
|
|
1634
1623
|
*
|
|
1635
|
-
* {{ react }}
|
|
1636
1624
|
*/
|
|
1637
1625
|
async appendField(field, value, context = null) {
|
|
1638
1626
|
const els = await findVisibleFields.call(this, field, context)
|
|
@@ -1734,7 +1722,6 @@ class Puppeteer extends Helper {
|
|
|
1734
1722
|
|
|
1735
1723
|
/**
|
|
1736
1724
|
* {{> grabNumberOfVisibleElements }}
|
|
1737
|
-
* {{ react }}
|
|
1738
1725
|
*/
|
|
1739
1726
|
async grabNumberOfVisibleElements(locator) {
|
|
1740
1727
|
let els = await this._locate(locator)
|
|
@@ -1796,7 +1783,6 @@ class Puppeteer extends Helper {
|
|
|
1796
1783
|
/**
|
|
1797
1784
|
* {{> see }}
|
|
1798
1785
|
*
|
|
1799
|
-
* {{ react }}
|
|
1800
1786
|
*/
|
|
1801
1787
|
async see(text, context = null) {
|
|
1802
1788
|
return proceedSee.call(this, 'assert', text, context)
|
|
@@ -1812,7 +1798,6 @@ class Puppeteer extends Helper {
|
|
|
1812
1798
|
/**
|
|
1813
1799
|
* {{> dontSee }}
|
|
1814
1800
|
*
|
|
1815
|
-
* {{ react }}
|
|
1816
1801
|
*/
|
|
1817
1802
|
async dontSee(text, context = null) {
|
|
1818
1803
|
return proceedSee.call(this, 'negate', text, context)
|
|
@@ -1866,7 +1851,6 @@ class Puppeteer extends Helper {
|
|
|
1866
1851
|
/**
|
|
1867
1852
|
* {{> seeNumberOfElements }}
|
|
1868
1853
|
*
|
|
1869
|
-
* {{ react }}
|
|
1870
1854
|
*/
|
|
1871
1855
|
async seeNumberOfElements(locator, num) {
|
|
1872
1856
|
const elements = await this._locate(locator)
|
|
@@ -1876,7 +1860,6 @@ class Puppeteer extends Helper {
|
|
|
1876
1860
|
/**
|
|
1877
1861
|
* {{> seeNumberOfVisibleElements }}
|
|
1878
1862
|
*
|
|
1879
|
-
* {{ react }}
|
|
1880
1863
|
*/
|
|
1881
1864
|
async seeNumberOfVisibleElements(locator, num) {
|
|
1882
1865
|
const res = await this.grabNumberOfVisibleElements(locator)
|
|
@@ -2003,7 +1986,6 @@ class Puppeteer extends Helper {
|
|
|
2003
1986
|
|
|
2004
1987
|
/**
|
|
2005
1988
|
* {{> grabTextFromAll }}
|
|
2006
|
-
* {{ react }}
|
|
2007
1989
|
*/
|
|
2008
1990
|
async grabTextFromAll(locator) {
|
|
2009
1991
|
const els = await this._locate(locator)
|
|
@@ -2016,7 +1998,6 @@ class Puppeteer extends Helper {
|
|
|
2016
1998
|
|
|
2017
1999
|
/**
|
|
2018
2000
|
* {{> grabTextFrom }}
|
|
2019
|
-
* {{ react }}
|
|
2020
2001
|
*/
|
|
2021
2002
|
async grabTextFrom(locator) {
|
|
2022
2003
|
const texts = await this.grabTextFromAll(locator)
|
|
@@ -2077,7 +2058,6 @@ class Puppeteer extends Helper {
|
|
|
2077
2058
|
|
|
2078
2059
|
/**
|
|
2079
2060
|
* {{> grabCssPropertyFromAll }}
|
|
2080
|
-
* {{ react }}
|
|
2081
2061
|
*/
|
|
2082
2062
|
async grabCssPropertyFromAll(locator, cssProperty) {
|
|
2083
2063
|
const els = await this._locate(locator)
|
|
@@ -2089,7 +2069,6 @@ class Puppeteer extends Helper {
|
|
|
2089
2069
|
|
|
2090
2070
|
/**
|
|
2091
2071
|
* {{> grabCssPropertyFrom }}
|
|
2092
|
-
* {{ react }}
|
|
2093
2072
|
*/
|
|
2094
2073
|
async grabCssPropertyFrom(locator, cssProperty) {
|
|
2095
2074
|
const cssValues = await this.grabCssPropertyFromAll(locator, cssProperty)
|
|
@@ -2104,7 +2083,6 @@ class Puppeteer extends Helper {
|
|
|
2104
2083
|
|
|
2105
2084
|
/**
|
|
2106
2085
|
* {{> seeCssPropertiesOnElements }}
|
|
2107
|
-
* {{ react }}
|
|
2108
2086
|
*/
|
|
2109
2087
|
async seeCssPropertiesOnElements(locator, cssProperties) {
|
|
2110
2088
|
const res = await this._locate(locator)
|
|
@@ -2139,7 +2117,6 @@ class Puppeteer extends Helper {
|
|
|
2139
2117
|
|
|
2140
2118
|
/**
|
|
2141
2119
|
* {{> seeAttributesOnElements }}
|
|
2142
|
-
* {{ react }}
|
|
2143
2120
|
*/
|
|
2144
2121
|
async seeAttributesOnElements(locator, attributes) {
|
|
2145
2122
|
const elements = await this._locate(locator)
|
|
@@ -2177,7 +2154,6 @@ class Puppeteer extends Helper {
|
|
|
2177
2154
|
|
|
2178
2155
|
/**
|
|
2179
2156
|
* {{> dragSlider }}
|
|
2180
|
-
* {{ react }}
|
|
2181
2157
|
*/
|
|
2182
2158
|
async dragSlider(locator, offsetX = 0) {
|
|
2183
2159
|
const src = await this._locate(locator)
|
|
@@ -2199,7 +2175,6 @@ class Puppeteer extends Helper {
|
|
|
2199
2175
|
|
|
2200
2176
|
/**
|
|
2201
2177
|
* {{> grabAttributeFromAll }}
|
|
2202
|
-
* {{ react }}
|
|
2203
2178
|
*/
|
|
2204
2179
|
async grabAttributeFromAll(locator, attr) {
|
|
2205
2180
|
const els = await this._locate(locator)
|
|
@@ -2213,7 +2188,6 @@ class Puppeteer extends Helper {
|
|
|
2213
2188
|
|
|
2214
2189
|
/**
|
|
2215
2190
|
* {{> grabAttributeFrom }}
|
|
2216
|
-
* {{ react }}
|
|
2217
2191
|
*/
|
|
2218
2192
|
async grabAttributeFrom(locator, attr) {
|
|
2219
2193
|
const attrs = await this.grabAttributeFromAll(locator, attr)
|
|
@@ -2376,7 +2350,6 @@ class Puppeteer extends Helper {
|
|
|
2376
2350
|
|
|
2377
2351
|
/**
|
|
2378
2352
|
* {{> waitNumberOfVisibleElements }}
|
|
2379
|
-
* {{ react }}
|
|
2380
2353
|
*/
|
|
2381
2354
|
async waitNumberOfVisibleElements(locator, num, sec) {
|
|
2382
2355
|
const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout
|
|
@@ -2424,7 +2397,6 @@ class Puppeteer extends Helper {
|
|
|
2424
2397
|
|
|
2425
2398
|
/**
|
|
2426
2399
|
* {{> waitForElement }}
|
|
2427
|
-
* {{ react }}
|
|
2428
2400
|
*/
|
|
2429
2401
|
async waitForElement(locator, sec) {
|
|
2430
2402
|
const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout
|
|
@@ -2445,7 +2417,6 @@ class Puppeteer extends Helper {
|
|
|
2445
2417
|
/**
|
|
2446
2418
|
* {{> waitForVisible }}
|
|
2447
2419
|
*
|
|
2448
|
-
* {{ react }}
|
|
2449
2420
|
*/
|
|
2450
2421
|
async waitForVisible(locator, sec) {
|
|
2451
2422
|
const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout
|
|
@@ -3022,10 +2993,6 @@ export default Puppeteer
|
|
|
3022
2993
|
* @returns {Promise<Array>} Array of ElementHandle objects
|
|
3023
2994
|
*/
|
|
3024
2995
|
async function findElements(matcher, locator) {
|
|
3025
|
-
// Check if locator is a Locator object with react type, or a raw object with react property
|
|
3026
|
-
const isReactLocator = locator.type === 'react' || (locator.locator && locator.locator.react) || locator.react
|
|
3027
|
-
if (isReactLocator) return findReactElements.call(this, locator)
|
|
3028
|
-
|
|
3029
2996
|
locator = new Locator(locator, 'css')
|
|
3030
2997
|
|
|
3031
2998
|
// Check if locator is a role locator and call findByRole
|
|
@@ -3092,7 +3059,6 @@ async function findElements(matcher, locator) {
|
|
|
3092
3059
|
* @returns {Promise<Object>} Single ElementHandle object
|
|
3093
3060
|
*/
|
|
3094
3061
|
async function findElement(matcher, locator) {
|
|
3095
|
-
if (locator.react) return findReactElements.call(this, locator)
|
|
3096
3062
|
locator = new Locator(locator, 'css')
|
|
3097
3063
|
|
|
3098
3064
|
// Check if locator is a role locator and call findByRole
|
|
@@ -3591,75 +3557,6 @@ function _waitForElement(locator, options) {
|
|
|
3591
3557
|
}
|
|
3592
3558
|
}
|
|
3593
3559
|
|
|
3594
|
-
async function findReactElements(locator) {
|
|
3595
|
-
// Handle both Locator objects and raw locator objects
|
|
3596
|
-
const resolved = locator.locator ? locator.locator : toLocatorConfig(locator, 'react')
|
|
3597
|
-
this.debug(`Finding React elements: ${JSON.stringify(resolved)}`)
|
|
3598
|
-
|
|
3599
|
-
// Use createRequire to access require.resolve in ESM
|
|
3600
|
-
const { createRequire } = await import('module')
|
|
3601
|
-
const require = createRequire(import.meta.url)
|
|
3602
|
-
const resqScript = await fs.promises.readFile(require.resolve('resq'), 'utf-8')
|
|
3603
|
-
await this.page.evaluate(resqScript.toString())
|
|
3604
|
-
|
|
3605
|
-
await this.page.evaluate(() => window.resq.waitToLoadReact())
|
|
3606
|
-
const arrayHandle = await this.page.evaluateHandle(
|
|
3607
|
-
obj => {
|
|
3608
|
-
const { selector, props, state } = obj
|
|
3609
|
-
let elements = window.resq.resq$$(selector)
|
|
3610
|
-
if (Object.keys(props).length) {
|
|
3611
|
-
elements = elements.byProps(props)
|
|
3612
|
-
}
|
|
3613
|
-
if (Object.keys(state).length) {
|
|
3614
|
-
elements = elements.byState(state)
|
|
3615
|
-
}
|
|
3616
|
-
|
|
3617
|
-
if (!elements.length) {
|
|
3618
|
-
return []
|
|
3619
|
-
}
|
|
3620
|
-
|
|
3621
|
-
// resq returns an array of HTMLElements if the React component is a fragment
|
|
3622
|
-
// this avoids having nested arrays of nodes which the driver does not understand
|
|
3623
|
-
// [[div, div], [div, div]] => [div, div, div, div]
|
|
3624
|
-
let nodes = []
|
|
3625
|
-
|
|
3626
|
-
elements.forEach(element => {
|
|
3627
|
-
let { node, isFragment } = element
|
|
3628
|
-
|
|
3629
|
-
if (!node) {
|
|
3630
|
-
isFragment = true
|
|
3631
|
-
node = element.children
|
|
3632
|
-
}
|
|
3633
|
-
|
|
3634
|
-
if (isFragment) {
|
|
3635
|
-
nodes = nodes.concat(node)
|
|
3636
|
-
} else {
|
|
3637
|
-
nodes.push(node)
|
|
3638
|
-
}
|
|
3639
|
-
})
|
|
3640
|
-
|
|
3641
|
-
return [...nodes]
|
|
3642
|
-
},
|
|
3643
|
-
{
|
|
3644
|
-
selector: resolved.react,
|
|
3645
|
-
props: resolved.props || {},
|
|
3646
|
-
state: resolved.state || {},
|
|
3647
|
-
},
|
|
3648
|
-
)
|
|
3649
|
-
|
|
3650
|
-
const properties = await arrayHandle.getProperties()
|
|
3651
|
-
const result = []
|
|
3652
|
-
for (const property of properties.values()) {
|
|
3653
|
-
const elementHandle = property.asElement()
|
|
3654
|
-
if (elementHandle) {
|
|
3655
|
-
result.push(elementHandle)
|
|
3656
|
-
}
|
|
3657
|
-
}
|
|
3658
|
-
|
|
3659
|
-
await arrayHandle.dispose()
|
|
3660
|
-
return result
|
|
3661
|
-
}
|
|
3662
|
-
|
|
3663
3560
|
async function findByRole(matcher, locator) {
|
|
3664
3561
|
const resolved = toLocatorConfig(locator, 'role')
|
|
3665
3562
|
const roleSelector = buildRoleSelector(resolved)
|
package/lib/helper/WebDriver.js
CHANGED
|
@@ -97,11 +97,7 @@ const config = {}
|
|
|
97
97
|
* WebDriver helper which wraps [webdriverio](http://webdriver.io/) library to
|
|
98
98
|
* manipulate browser using Selenium WebDriver or PhantomJS.
|
|
99
99
|
*
|
|
100
|
-
*
|
|
101
|
-
*
|
|
102
|
-
* 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/).
|
|
103
|
-
* 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
|
|
104
|
-
* `wdio-chromedriver-service`, `wdio-geckodriver-service`, `wdio-edgedriver-service`, `wdio-safaridriver-service`, and even `@wdio/selenium-standalone-service`.
|
|
100
|
+
* No Selenium Server, ChromeDriver, or GeckoDriver to install or start. Since WebdriverIO 9, driver management is fully automatic — WebdriverIO downloads and starts the matching driver for you. Read more [here](https://webdriver.io/blog/2023/07/31/driver-management/). Please check [Testing with WebDriver](https://codecept.io/webdriver/#testing-with-webdriver) for more details.
|
|
105
101
|
*
|
|
106
102
|
* For those who require custom driver options, fear not; WebDriver Helper allows you to pass in driver options through custom WebDriver configuration.
|
|
107
103
|
* 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.
|
|
@@ -908,13 +904,6 @@ class WebDriver extends Helper {
|
|
|
908
904
|
return els
|
|
909
905
|
}
|
|
910
906
|
|
|
911
|
-
// special locator type for React
|
|
912
|
-
if (locator.react) {
|
|
913
|
-
const els = await this.browser.react$$(locator.react, locator.props || undefined, locator.state || undefined)
|
|
914
|
-
this.debugSection('Elements', `Found ${els.length} react components`)
|
|
915
|
-
return els
|
|
916
|
-
}
|
|
917
|
-
|
|
918
907
|
// special locator type for ARIA roles
|
|
919
908
|
if (locator.role) {
|
|
920
909
|
return this._locateByRole(locator)
|
|
@@ -1084,7 +1073,6 @@ class WebDriver extends Helper {
|
|
|
1084
1073
|
/**
|
|
1085
1074
|
* {{> click }}
|
|
1086
1075
|
*
|
|
1087
|
-
* {{ react }}
|
|
1088
1076
|
*/
|
|
1089
1077
|
async click(locator, context = null) {
|
|
1090
1078
|
const clickMethod = this.browser.isMobile && this.browser.capabilities.platformName !== 'android' ? 'touchClick' : 'elementClick'
|
|
@@ -1104,7 +1092,6 @@ class WebDriver extends Helper {
|
|
|
1104
1092
|
/**
|
|
1105
1093
|
* {{> forceClick }}
|
|
1106
1094
|
*
|
|
1107
|
-
* {{ react }}
|
|
1108
1095
|
*/
|
|
1109
1096
|
async forceClick(locator, context = null) {
|
|
1110
1097
|
const locateFn = prepareLocateFn.call(this, context)
|
|
@@ -1131,7 +1118,6 @@ class WebDriver extends Helper {
|
|
|
1131
1118
|
/**
|
|
1132
1119
|
* {{> doubleClick }}
|
|
1133
1120
|
*
|
|
1134
|
-
* {{ react }}
|
|
1135
1121
|
*/
|
|
1136
1122
|
async doubleClick(locator, context = null) {
|
|
1137
1123
|
const locateFn = prepareLocateFn.call(this, context)
|
|
@@ -1151,7 +1137,6 @@ class WebDriver extends Helper {
|
|
|
1151
1137
|
/**
|
|
1152
1138
|
* {{> rightClick }}
|
|
1153
1139
|
*
|
|
1154
|
-
* {{ react }}
|
|
1155
1140
|
*/
|
|
1156
1141
|
async rightClick(locator, context) {
|
|
1157
1142
|
const locateFn = prepareLocateFn.call(this, context)
|
|
@@ -1246,7 +1231,6 @@ class WebDriver extends Helper {
|
|
|
1246
1231
|
/**
|
|
1247
1232
|
* {{> forceRightClick }}
|
|
1248
1233
|
*
|
|
1249
|
-
* {{ react }}
|
|
1250
1234
|
*/
|
|
1251
1235
|
async forceRightClick(locator, context = null) {
|
|
1252
1236
|
const locateFn = prepareLocateFn.call(this, context)
|
|
@@ -1271,7 +1255,6 @@ class WebDriver extends Helper {
|
|
|
1271
1255
|
|
|
1272
1256
|
/**
|
|
1273
1257
|
* {{> fillField }}
|
|
1274
|
-
* {{ react }}
|
|
1275
1258
|
* {{ custom }}
|
|
1276
1259
|
*
|
|
1277
1260
|
*/
|
|
@@ -1301,7 +1284,6 @@ class WebDriver extends Helper {
|
|
|
1301
1284
|
|
|
1302
1285
|
/**
|
|
1303
1286
|
* {{> appendField }}
|
|
1304
|
-
* {{ react }}
|
|
1305
1287
|
*/
|
|
1306
1288
|
async appendField(field, value, context = null) {
|
|
1307
1289
|
const res = await findFields.call(this, field, context)
|
|
@@ -1599,7 +1581,6 @@ class WebDriver extends Helper {
|
|
|
1599
1581
|
/**
|
|
1600
1582
|
* {{> see }}
|
|
1601
1583
|
*
|
|
1602
|
-
* {{ react }}
|
|
1603
1584
|
*/
|
|
1604
1585
|
async see(text, context = null) {
|
|
1605
1586
|
return proceedSee.call(this, 'assert', text, context)
|
|
@@ -1615,7 +1596,6 @@ class WebDriver extends Helper {
|
|
|
1615
1596
|
/**
|
|
1616
1597
|
* {{> dontSee }}
|
|
1617
1598
|
*
|
|
1618
|
-
* {{ react }}
|
|
1619
1599
|
*/
|
|
1620
1600
|
async dontSee(text, context = null) {
|
|
1621
1601
|
return proceedSee.call(this, 'negate', text, context)
|
|
@@ -1657,7 +1637,6 @@ class WebDriver extends Helper {
|
|
|
1657
1637
|
|
|
1658
1638
|
/**
|
|
1659
1639
|
* {{> seeElement }}
|
|
1660
|
-
* {{ react }}
|
|
1661
1640
|
*
|
|
1662
1641
|
*/
|
|
1663
1642
|
async seeElement(locator, context = null) {
|
|
@@ -1674,7 +1653,6 @@ class WebDriver extends Helper {
|
|
|
1674
1653
|
|
|
1675
1654
|
/**
|
|
1676
1655
|
* {{> dontSeeElement }}
|
|
1677
|
-
* {{ react }}
|
|
1678
1656
|
*/
|
|
1679
1657
|
async dontSeeElement(locator, context = null) {
|
|
1680
1658
|
const locateFn = prepareLocateFn.call(this, context)
|
|
@@ -1759,7 +1737,6 @@ class WebDriver extends Helper {
|
|
|
1759
1737
|
|
|
1760
1738
|
/**
|
|
1761
1739
|
* {{> seeNumberOfElements }}
|
|
1762
|
-
* {{ react }}
|
|
1763
1740
|
*/
|
|
1764
1741
|
async seeNumberOfElements(locator, num) {
|
|
1765
1742
|
const res = await this._locate(locator)
|
|
@@ -1768,7 +1745,6 @@ class WebDriver extends Helper {
|
|
|
1768
1745
|
|
|
1769
1746
|
/**
|
|
1770
1747
|
* {{> seeNumberOfVisibleElements }}
|
|
1771
|
-
* {{ react }}
|
|
1772
1748
|
*/
|
|
1773
1749
|
async seeNumberOfVisibleElements(locator, num) {
|
|
1774
1750
|
const res = await this.grabNumberOfVisibleElements(locator)
|
|
@@ -3501,9 +3477,6 @@ function prepareLocateFn(context) {
|
|
|
3501
3477
|
l = new Locator(l, 'css')
|
|
3502
3478
|
return this._locate(context, true).then(async res => {
|
|
3503
3479
|
assertElementExists(res, context, 'Context element')
|
|
3504
|
-
if (l.react) {
|
|
3505
|
-
return res[0].react$$(l.react, l.props || undefined)
|
|
3506
|
-
}
|
|
3507
3480
|
return res[0].$$(l.simplify())
|
|
3508
3481
|
})
|
|
3509
3482
|
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
async function findByPlaywrightLocator(matcher, locator) {
|
|
2
|
+
const pwLocator = locator.locator || locator
|
|
3
|
+
if (pwLocator && pwLocator.toString && pwLocator.toString().includes(process.env.testIdAttribute)) {
|
|
4
|
+
return matcher.getByTestId(pwLocator.pw.value.split('=')[1])
|
|
5
|
+
}
|
|
6
|
+
const pwValue = typeof pwLocator.pw === 'string' ? pwLocator.pw : pwLocator.pw
|
|
7
|
+
return matcher.locator(pwValue).all()
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export { findByPlaywrightLocator }
|
package/lib/locator.js
CHANGED
|
@@ -58,9 +58,6 @@ class Locator {
|
|
|
58
58
|
if (isShadow(locator)) {
|
|
59
59
|
this.type = 'shadow'
|
|
60
60
|
}
|
|
61
|
-
if (isPlaywrightLocator(locator)) {
|
|
62
|
-
this.type = 'pw'
|
|
63
|
-
}
|
|
64
61
|
|
|
65
62
|
Locator.filters.forEach(f => f(locator, this))
|
|
66
63
|
}
|
|
@@ -753,16 +750,6 @@ function removePrefix(xpath) {
|
|
|
753
750
|
return xpath.replace(/^(\.|\/)+/, '')
|
|
754
751
|
}
|
|
755
752
|
|
|
756
|
-
/**
|
|
757
|
-
* @private
|
|
758
|
-
* check if the locator is a Playwright locator
|
|
759
|
-
* @param {string} locator
|
|
760
|
-
* @returns {boolean}
|
|
761
|
-
*/
|
|
762
|
-
function isPlaywrightLocator(locator) {
|
|
763
|
-
return locator.includes('_react') || locator.includes('_vue')
|
|
764
|
-
}
|
|
765
|
-
|
|
766
753
|
/**
|
|
767
754
|
* @private
|
|
768
755
|
* check if the locator is a role locator
|
package/lib/plugin/analyze.js
CHANGED
|
@@ -155,10 +155,9 @@ const defaultConfig = {
|
|
|
155
155
|
if (config.vision && test.artifacts.screenshot) {
|
|
156
156
|
debug('Adding screenshot to prompt')
|
|
157
157
|
messages[0].content.push({
|
|
158
|
-
type: '
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
},
|
|
158
|
+
type: 'image',
|
|
159
|
+
image: base64EncodeFile(test.artifacts.screenshot),
|
|
160
|
+
mediaType: 'image/png',
|
|
162
161
|
})
|
|
163
162
|
}
|
|
164
163
|
|
|
@@ -4,7 +4,9 @@ import pause from './pause.js'
|
|
|
4
4
|
let warned = false
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
|
-
*
|
|
7
|
+
* Starts an interactive pause when a test fails.
|
|
8
|
+
*
|
|
9
|
+
* **Deprecated:** use the `pause` plugin with `on: 'fail'`, which is the default behavior.
|
|
8
10
|
*/
|
|
9
11
|
export default function (config = {}) {
|
|
10
12
|
if (!warned) {
|
|
@@ -8,7 +8,10 @@ const debug = debugModule('codeceptjs:retryFailedStep')
|
|
|
8
8
|
const defaultConfig = {
|
|
9
9
|
retries: 3,
|
|
10
10
|
defaultIgnoredSteps: ['amOnPage', 'wait*', 'send*', 'execute*', 'run*', 'have*'],
|
|
11
|
+
minTimeout: 150,
|
|
12
|
+
maxTimeout: 10000,
|
|
11
13
|
factor: 1.5,
|
|
14
|
+
randomize: false,
|
|
12
15
|
ignoredSteps: [],
|
|
13
16
|
deferToScenarioRetries: true,
|
|
14
17
|
}
|
|
@@ -44,10 +47,9 @@ const RETRY_PRIORITIES = {
|
|
|
44
47
|
* #### Configuration:
|
|
45
48
|
*
|
|
46
49
|
* * `retries` - number of retries (by default 3),
|
|
47
|
-
* * `when` - function, when to perform a retry (accepts error as parameter)
|
|
48
50
|
* * `factor` - The exponential factor to use. Default is 1.5.
|
|
49
|
-
* * `minTimeout` - The number of milliseconds before starting the first retry. Default is
|
|
50
|
-
* * `maxTimeout` - The maximum number of milliseconds between two retries. Default is
|
|
51
|
+
* * `minTimeout` - The number of milliseconds before starting the first retry. Default is 150.
|
|
52
|
+
* * `maxTimeout` - The maximum number of milliseconds between two retries. Default is 10000.
|
|
51
53
|
* * `randomize` - Randomizes the timeouts by multiplying with a factor from 1 to 2. Default is false.
|
|
52
54
|
* * `defaultIgnoredSteps` - an array of steps to be ignored for retry. Includes:
|
|
53
55
|
* * `amOnPage`
|
|
@@ -77,7 +79,7 @@ const RETRY_PRIORITIES = {
|
|
|
77
79
|
*
|
|
78
80
|
* #### Disable Per Test
|
|
79
81
|
*
|
|
80
|
-
* This plugin can be disabled per test. In this case you will need to
|
|
82
|
+
* This plugin can be disabled per test. In this case you will need to add `step.retry()` to all flaky steps:
|
|
81
83
|
*
|
|
82
84
|
* Use scenario configuration to disable plugin for a test
|
|
83
85
|
*
|
|
@@ -89,9 +91,8 @@ const RETRY_PRIORITIES = {
|
|
|
89
91
|
*
|
|
90
92
|
*/
|
|
91
93
|
export default function (config) {
|
|
92
|
-
config = Object.assign(defaultConfig, config)
|
|
94
|
+
config = Object.assign({}, defaultConfig, config)
|
|
93
95
|
config.ignoredSteps = config.ignoredSteps.concat(config.defaultIgnoredSteps)
|
|
94
|
-
const customWhen = config.when
|
|
95
96
|
|
|
96
97
|
let enableRetry = false
|
|
97
98
|
|
|
@@ -101,7 +102,6 @@ export default function (config) {
|
|
|
101
102
|
if (!store.autoRetries) return false
|
|
102
103
|
if (err && err.isTerminal) return false
|
|
103
104
|
if (err && err.message && (err.message.includes('ERR_ABORTED') || err.message.includes('frame was detached') || err.message.includes('Target page, context or browser has been closed'))) return false
|
|
104
|
-
if (customWhen) return customWhen(err)
|
|
105
105
|
return true
|
|
106
106
|
}
|
|
107
107
|
config.when = when
|
package/lib/plugin/screenshot.js
CHANGED
|
@@ -86,14 +86,9 @@ export default function (config = {}) {
|
|
|
86
86
|
const trigger = resolveTrigger(cliArgs, config, { on: defaultConfig.on }, { name: 'screenshot' })
|
|
87
87
|
if (!trigger) return
|
|
88
88
|
|
|
89
|
-
const helpers = Container.helpers()
|
|
90
89
|
const options = Object.assign({}, defaultConfig, helper.options, config)
|
|
91
90
|
options.slides = cliArgs.slides ?? config.slides ?? defaultConfig.slides
|
|
92
91
|
|
|
93
|
-
if (helpers.Mochawesome?.config) {
|
|
94
|
-
options.uniqueScreenshotNames = helpers.Mochawesome.config.uniqueScreenshotNames
|
|
95
|
-
}
|
|
96
|
-
|
|
97
92
|
if (Codeceptjs.container.mocha()) {
|
|
98
93
|
options.reportDir = Codeceptjs.container.mocha()?.options?.reporterOptions
|
|
99
94
|
&& Codeceptjs.container.mocha()?.options?.reporterOptions?.reportDir
|
|
@@ -4,7 +4,9 @@ import screenshot from './screenshot.js'
|
|
|
4
4
|
let warned = false
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
|
-
*
|
|
7
|
+
* Saves a screenshot when a test fails.
|
|
8
|
+
*
|
|
9
|
+
* **Deprecated:** use the `screenshot` plugin with `on: 'fail'`, which is the default behavior.
|
|
8
10
|
*/
|
|
9
11
|
export default function (config = {}) {
|
|
10
12
|
if (!warned) {
|
|
@@ -32,7 +32,7 @@ const defaultConfig = {
|
|
|
32
32
|
* #### Configuration:
|
|
33
33
|
*
|
|
34
34
|
* * `timeout` - global step timeout, default 150 seconds
|
|
35
|
-
* * `overrideStepLimits` - whether to use timeouts set in plugin config to override step timeouts set in code with I.
|
|
35
|
+
* * `overrideStepLimits` - whether to use timeouts set in plugin config to override step timeouts set in code with `I.action(..., step.timeout(x))`, default false
|
|
36
36
|
* * `noTimeoutSteps` - an array of steps with no timeout. Default:
|
|
37
37
|
* * `amOnPage`
|
|
38
38
|
* * `wait*`
|
package/lib/recorder.js
CHANGED
|
@@ -217,7 +217,7 @@ export default {
|
|
|
217
217
|
}
|
|
218
218
|
|
|
219
219
|
const retryRules = this.retries.slice().reverse()
|
|
220
|
-
return promiseRetry(Object.assign(defaultRetryOptions, retryOpts), (retry, number) => {
|
|
220
|
+
return promiseRetry(Object.assign({}, defaultRetryOptions, retryOpts), (retry, number) => {
|
|
221
221
|
if (number > 1) output.log(`${currentQueue()}Retrying... Attempt #${number}`)
|
|
222
222
|
const [promise, timer] = getTimeoutPromise(timeout, taskName)
|
|
223
223
|
return Promise.race([promise, Promise.resolve(res).then(fn)])
|