codeceptjs 4.0.0-rc.2 → 4.0.0-rc.7
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 +39 -27
- package/bin/mcp-server.js +610 -0
- package/docs/webapi/appendField.mustache +5 -0
- package/docs/webapi/attachFile.mustache +12 -0
- package/docs/webapi/checkOption.mustache +1 -1
- package/docs/webapi/clearField.mustache +5 -0
- package/docs/webapi/dontSeeElement.mustache +4 -0
- package/docs/webapi/dontSeeInField.mustache +5 -0
- package/docs/webapi/fillField.mustache +5 -0
- package/docs/webapi/seeElement.mustache +4 -0
- package/docs/webapi/seeInField.mustache +5 -0
- package/docs/webapi/selectOption.mustache +5 -0
- package/docs/webapi/uncheckOption.mustache +1 -1
- package/lib/codecept.js +20 -17
- package/lib/command/init.js +0 -3
- package/lib/command/run-workers.js +1 -0
- package/lib/container.js +19 -4
- package/lib/helper/Appium.js +8 -8
- package/lib/helper/Playwright.js +145 -72
- package/lib/helper/Puppeteer.js +147 -61
- package/lib/helper/WebDriver.js +116 -51
- package/lib/listener/globalRetry.js +32 -6
- package/lib/plugin/aiTrace.js +464 -0
- package/lib/plugin/retryFailedStep.js +28 -19
- package/lib/plugin/stepByStepReport.js +5 -1
- package/lib/utils.js +48 -0
- package/lib/workers.js +49 -7
- package/package.json +5 -3
- package/lib/listener/enhancedGlobalRetry.js +0 -110
- package/lib/plugin/enhancedRetryFailedStep.js +0 -99
- package/lib/plugin/htmlReporter.js +0 -3648
- package/lib/retryCoordinator.js +0 -207
- package/typings/promiseBasedTypes.d.ts +0 -9469
- package/typings/types.d.ts +0 -11402
package/lib/helper/WebDriver.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
let webdriverio
|
|
2
2
|
|
|
3
|
+
import fs from 'fs'
|
|
3
4
|
import assert from 'assert'
|
|
4
5
|
import path from 'path'
|
|
5
6
|
import crypto from 'crypto'
|
|
@@ -13,7 +14,20 @@ import output from '../output.js'
|
|
|
13
14
|
const { debug } = output
|
|
14
15
|
import { empty } from '../assert/empty.js'
|
|
15
16
|
import { truth } from '../assert/truth.js'
|
|
16
|
-
import {
|
|
17
|
+
import {
|
|
18
|
+
xpathLocator,
|
|
19
|
+
fileExists,
|
|
20
|
+
decodeUrl,
|
|
21
|
+
chunkArray,
|
|
22
|
+
convertCssPropertiesToCamelCase,
|
|
23
|
+
screenshotOutputFolder,
|
|
24
|
+
getNormalizedKeyAttributeValue,
|
|
25
|
+
modifierKeys,
|
|
26
|
+
normalizePath,
|
|
27
|
+
resolveUrl,
|
|
28
|
+
getMimeType,
|
|
29
|
+
base64EncodeFile,
|
|
30
|
+
} from '../utils.js'
|
|
17
31
|
import { isColorProperty, convertColorToRGBA } from '../colorUtils.js'
|
|
18
32
|
import ElementNotFound from './errors/ElementNotFound.js'
|
|
19
33
|
import ConnectionRefused from './errors/ConnectionRefused.js'
|
|
@@ -1255,8 +1269,8 @@ class WebDriver extends Helper {
|
|
|
1255
1269
|
* {{ custom }}
|
|
1256
1270
|
*
|
|
1257
1271
|
*/
|
|
1258
|
-
async fillField(field, value) {
|
|
1259
|
-
const res = await findFields.call(this, field)
|
|
1272
|
+
async fillField(field, value, context = null) {
|
|
1273
|
+
const res = await findFields.call(this, field, context)
|
|
1260
1274
|
assertElementExists(res, field, 'Field')
|
|
1261
1275
|
const elem = usingFirstElement(res)
|
|
1262
1276
|
highlightActiveElement.call(this, elem)
|
|
@@ -1278,8 +1292,8 @@ class WebDriver extends Helper {
|
|
|
1278
1292
|
* {{> appendField }}
|
|
1279
1293
|
* {{ react }}
|
|
1280
1294
|
*/
|
|
1281
|
-
async appendField(field, value) {
|
|
1282
|
-
const res = await findFields.call(this, field)
|
|
1295
|
+
async appendField(field, value, context = null) {
|
|
1296
|
+
const res = await findFields.call(this, field, context)
|
|
1283
1297
|
assertElementExists(res, field, 'Field')
|
|
1284
1298
|
const elem = usingFirstElement(res)
|
|
1285
1299
|
highlightActiveElement.call(this, elem)
|
|
@@ -1290,8 +1304,8 @@ class WebDriver extends Helper {
|
|
|
1290
1304
|
* {{> clearField }}
|
|
1291
1305
|
*
|
|
1292
1306
|
*/
|
|
1293
|
-
async clearField(field) {
|
|
1294
|
-
const res = await findFields.call(this, field)
|
|
1307
|
+
async clearField(field, context = null) {
|
|
1308
|
+
const res = await findFields.call(this, field, context)
|
|
1295
1309
|
assertElementExists(res, field, 'Field')
|
|
1296
1310
|
const elem = usingFirstElement(res)
|
|
1297
1311
|
highlightActiveElement.call(this, elem)
|
|
@@ -1301,13 +1315,14 @@ class WebDriver extends Helper {
|
|
|
1301
1315
|
/**
|
|
1302
1316
|
* {{> selectOption }}
|
|
1303
1317
|
*/
|
|
1304
|
-
async selectOption(select, option) {
|
|
1318
|
+
async selectOption(select, option, context = null) {
|
|
1319
|
+
const locateFn = prepareLocateFn.call(this, context)
|
|
1305
1320
|
const matchedLocator = new Locator(select)
|
|
1306
1321
|
|
|
1307
1322
|
// Strict locator
|
|
1308
1323
|
if (!matchedLocator.isFuzzy()) {
|
|
1309
1324
|
this.debugSection('SelectOption', `Strict: ${JSON.stringify(select)}`)
|
|
1310
|
-
const els = await
|
|
1325
|
+
const els = await locateFn(select)
|
|
1311
1326
|
assertElementExists(els, select, 'Selectable element')
|
|
1312
1327
|
return proceedSelectOption.call(this, usingFirstElement(els), option)
|
|
1313
1328
|
}
|
|
@@ -1322,7 +1337,7 @@ class WebDriver extends Helper {
|
|
|
1322
1337
|
if (els?.length) return proceedSelectOption.call(this, usingFirstElement(els), option)
|
|
1323
1338
|
|
|
1324
1339
|
// Fuzzy: try native select
|
|
1325
|
-
const res = await findFields.call(this, select)
|
|
1340
|
+
const res = await findFields.call(this, select, context)
|
|
1326
1341
|
assertElementExists(res, select, 'Selectable field')
|
|
1327
1342
|
return proceedSelectOption.call(this, usingFirstElement(res), option)
|
|
1328
1343
|
}
|
|
@@ -1332,28 +1347,49 @@ class WebDriver extends Helper {
|
|
|
1332
1347
|
*
|
|
1333
1348
|
* {{> attachFile }}
|
|
1334
1349
|
*/
|
|
1335
|
-
async attachFile(locator, pathToFile) {
|
|
1350
|
+
async attachFile(locator, pathToFile, context = null) {
|
|
1336
1351
|
let file = path.join(global.codecept_dir, pathToFile)
|
|
1337
1352
|
if (!fileExists(file)) {
|
|
1338
1353
|
throw new Error(`File at ${file} can not be found on local system`)
|
|
1339
1354
|
}
|
|
1340
1355
|
|
|
1341
|
-
const res = await findFields.call(this, locator)
|
|
1356
|
+
const res = await findFields.call(this, locator, context)
|
|
1342
1357
|
this.debug(`Uploading ${file}`)
|
|
1343
|
-
assertElementExists(res, locator, 'File field')
|
|
1344
|
-
const el = usingFirstElement(res)
|
|
1345
1358
|
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1359
|
+
if (res.length) {
|
|
1360
|
+
const el = usingFirstElement(res)
|
|
1361
|
+
const tag = await this.browser.execute(function (elem) { return elem.tagName }, el)
|
|
1362
|
+
const type = await this.browser.execute(function (elem) { return elem.type }, el)
|
|
1363
|
+
if (tag === 'INPUT' && type === 'file') {
|
|
1364
|
+
if (this.options.remoteFileUpload) {
|
|
1365
|
+
try {
|
|
1366
|
+
this.debugSection('File', 'Uploading file to remote server')
|
|
1367
|
+
file = await this.browser.uploadFile(file)
|
|
1368
|
+
} catch (err) {
|
|
1369
|
+
throw new Error(`File can't be transferred to remote server. Set \`remoteFileUpload: false\` in config to upload file locally.\n${err.message}`)
|
|
1370
|
+
}
|
|
1371
|
+
}
|
|
1372
|
+
return el.addValue(file)
|
|
1353
1373
|
}
|
|
1354
1374
|
}
|
|
1355
1375
|
|
|
1356
|
-
|
|
1376
|
+
const targetRes = res.length ? res : await this._locate(locator)
|
|
1377
|
+
assertElementExists(targetRes, locator, 'Element')
|
|
1378
|
+
const targetEl = usingFirstElement(targetRes)
|
|
1379
|
+
const base64Content = base64EncodeFile(file)
|
|
1380
|
+
const fileName = path.basename(file)
|
|
1381
|
+
const mimeType = getMimeType(fileName)
|
|
1382
|
+
return this.browser.execute(function (el, data) {
|
|
1383
|
+
var binaryStr = atob(data.base64Content)
|
|
1384
|
+
var bytes = new Uint8Array(binaryStr.length)
|
|
1385
|
+
for (var i = 0; i < binaryStr.length; i++) bytes[i] = binaryStr.charCodeAt(i)
|
|
1386
|
+
var fileObj = new File([bytes], data.fileName, { type: data.mimeType })
|
|
1387
|
+
var dataTransfer = new DataTransfer()
|
|
1388
|
+
dataTransfer.items.add(fileObj)
|
|
1389
|
+
el.dispatchEvent(new DragEvent('dragenter', { dataTransfer: dataTransfer, bubbles: true }))
|
|
1390
|
+
el.dispatchEvent(new DragEvent('dragover', { dataTransfer: dataTransfer, bubbles: true }))
|
|
1391
|
+
el.dispatchEvent(new DragEvent('drop', { dataTransfer: dataTransfer, bubbles: true }))
|
|
1392
|
+
}, targetEl, { base64Content, fileName, mimeType })
|
|
1357
1393
|
}
|
|
1358
1394
|
|
|
1359
1395
|
/**
|
|
@@ -1586,18 +1622,18 @@ class WebDriver extends Helper {
|
|
|
1586
1622
|
* {{> seeInField }}
|
|
1587
1623
|
*
|
|
1588
1624
|
*/
|
|
1589
|
-
async seeInField(field, value) {
|
|
1625
|
+
async seeInField(field, value, context = null) {
|
|
1590
1626
|
const _value = typeof value === 'boolean' ? value : value.toString()
|
|
1591
|
-
return proceedSeeField.call(this, 'assert', field, _value)
|
|
1627
|
+
return proceedSeeField.call(this, 'assert', field, _value, context)
|
|
1592
1628
|
}
|
|
1593
1629
|
|
|
1594
1630
|
/**
|
|
1595
1631
|
* {{> dontSeeInField }}
|
|
1596
1632
|
*
|
|
1597
1633
|
*/
|
|
1598
|
-
async dontSeeInField(field, value) {
|
|
1634
|
+
async dontSeeInField(field, value, context = null) {
|
|
1599
1635
|
const _value = typeof value === 'boolean' ? value : value.toString()
|
|
1600
|
-
return proceedSeeField.call(this, 'negate', field, _value)
|
|
1636
|
+
return proceedSeeField.call(this, 'negate', field, _value, context)
|
|
1601
1637
|
}
|
|
1602
1638
|
|
|
1603
1639
|
/**
|
|
@@ -1621,8 +1657,9 @@ class WebDriver extends Helper {
|
|
|
1621
1657
|
* {{ react }}
|
|
1622
1658
|
*
|
|
1623
1659
|
*/
|
|
1624
|
-
async seeElement(locator) {
|
|
1625
|
-
const
|
|
1660
|
+
async seeElement(locator, context = null) {
|
|
1661
|
+
const locateFn = prepareLocateFn.call(this, context)
|
|
1662
|
+
const res = context ? await locateFn(locator) : await this._locate(locator, true)
|
|
1626
1663
|
assertElementExists(res, locator)
|
|
1627
1664
|
const selected = await forEachAsync(res, async el => el.isDisplayed())
|
|
1628
1665
|
try {
|
|
@@ -1636,8 +1673,9 @@ class WebDriver extends Helper {
|
|
|
1636
1673
|
* {{> dontSeeElement }}
|
|
1637
1674
|
* {{ react }}
|
|
1638
1675
|
*/
|
|
1639
|
-
async dontSeeElement(locator) {
|
|
1640
|
-
const
|
|
1676
|
+
async dontSeeElement(locator, context = null) {
|
|
1677
|
+
const locateFn = prepareLocateFn.call(this, context)
|
|
1678
|
+
const res = context ? await locateFn(locator) : await this._locate(locator, false)
|
|
1641
1679
|
if (!res || res.length === 0) {
|
|
1642
1680
|
return truth(`elements of ${new Locator(locator)}`, 'to be seen').negate(false)
|
|
1643
1681
|
}
|
|
@@ -1851,7 +1889,7 @@ class WebDriver extends Helper {
|
|
|
1851
1889
|
const currentUrl = await this.browser.getUrl()
|
|
1852
1890
|
const baseUrl = this.options.url || 'http://localhost'
|
|
1853
1891
|
const actualPath = new URL(currentUrl, baseUrl).pathname
|
|
1854
|
-
return equals('url path').assert(path, actualPath)
|
|
1892
|
+
return equals('url path').assert(normalizePath(path), normalizePath(actualPath))
|
|
1855
1893
|
}
|
|
1856
1894
|
|
|
1857
1895
|
/**
|
|
@@ -1861,7 +1899,7 @@ class WebDriver extends Helper {
|
|
|
1861
1899
|
const currentUrl = await this.browser.getUrl()
|
|
1862
1900
|
const baseUrl = this.options.url || 'http://localhost'
|
|
1863
1901
|
const actualPath = new URL(currentUrl, baseUrl).pathname
|
|
1864
|
-
return equals('url path').negate(path, actualPath)
|
|
1902
|
+
return equals('url path').negate(normalizePath(path), normalizePath(actualPath))
|
|
1865
1903
|
}
|
|
1866
1904
|
|
|
1867
1905
|
/**
|
|
@@ -2487,6 +2525,7 @@ class WebDriver extends Helper {
|
|
|
2487
2525
|
async waitInUrl(urlPart, sec = null) {
|
|
2488
2526
|
const client = this.browser
|
|
2489
2527
|
const aSec = sec || this.options.waitForTimeoutInSeconds
|
|
2528
|
+
const expectedUrl = resolveUrl(urlPart, this.options.url)
|
|
2490
2529
|
let currUrl = ''
|
|
2491
2530
|
|
|
2492
2531
|
return client
|
|
@@ -2494,7 +2533,7 @@ class WebDriver extends Helper {
|
|
|
2494
2533
|
function () {
|
|
2495
2534
|
return this.getUrl().then(res => {
|
|
2496
2535
|
currUrl = decodeUrl(res)
|
|
2497
|
-
return currUrl.indexOf(
|
|
2536
|
+
return currUrl.indexOf(expectedUrl) > -1
|
|
2498
2537
|
})
|
|
2499
2538
|
},
|
|
2500
2539
|
{ timeout: aSec * 1000 },
|
|
@@ -2502,7 +2541,7 @@ class WebDriver extends Helper {
|
|
|
2502
2541
|
.catch(e => {
|
|
2503
2542
|
e = wrapError(e)
|
|
2504
2543
|
if (e.message.indexOf('timeout')) {
|
|
2505
|
-
throw new Error(`expected url to include ${
|
|
2544
|
+
throw new Error(`expected url to include ${expectedUrl}, but found ${currUrl}`)
|
|
2506
2545
|
}
|
|
2507
2546
|
throw e
|
|
2508
2547
|
})
|
|
@@ -2513,22 +2552,47 @@ class WebDriver extends Helper {
|
|
|
2513
2552
|
*/
|
|
2514
2553
|
async waitUrlEquals(urlPart, sec = null) {
|
|
2515
2554
|
const aSec = sec || this.options.waitForTimeoutInSeconds
|
|
2516
|
-
const
|
|
2517
|
-
if (urlPart.indexOf('http') < 0) {
|
|
2518
|
-
urlPart = baseUrl + urlPart
|
|
2519
|
-
}
|
|
2555
|
+
const expectedUrl = resolveUrl(urlPart, this.options.url)
|
|
2520
2556
|
let currUrl = ''
|
|
2521
2557
|
return this.browser
|
|
2522
2558
|
.waitUntil(function () {
|
|
2523
2559
|
return this.getUrl().then(res => {
|
|
2524
2560
|
currUrl = decodeUrl(res)
|
|
2525
|
-
return currUrl ===
|
|
2561
|
+
return currUrl === expectedUrl
|
|
2526
2562
|
})
|
|
2527
2563
|
}, aSec * 1000)
|
|
2528
2564
|
.catch(e => {
|
|
2529
2565
|
e = wrapError(e)
|
|
2530
2566
|
if (e.message.indexOf('timeout')) {
|
|
2531
|
-
throw new Error(`expected url to be ${
|
|
2567
|
+
throw new Error(`expected url to be ${expectedUrl}, but found ${currUrl}`)
|
|
2568
|
+
}
|
|
2569
|
+
throw e
|
|
2570
|
+
})
|
|
2571
|
+
}
|
|
2572
|
+
|
|
2573
|
+
/**
|
|
2574
|
+
* {{> waitCurrentPathEquals }}
|
|
2575
|
+
*/
|
|
2576
|
+
async waitCurrentPathEquals(path, sec = null) {
|
|
2577
|
+
const aSec = sec || this.options.waitForTimeoutInSeconds
|
|
2578
|
+
const normalizedPath = normalizePath(path)
|
|
2579
|
+
const baseUrl = this.options.url || 'http://localhost'
|
|
2580
|
+
let actualPath = ''
|
|
2581
|
+
|
|
2582
|
+
return this.browser
|
|
2583
|
+
.waitUntil(
|
|
2584
|
+
async () => {
|
|
2585
|
+
const currUrl = await this.browser.getUrl()
|
|
2586
|
+
const url = new URL(currUrl, baseUrl)
|
|
2587
|
+
actualPath = url.pathname
|
|
2588
|
+
return normalizePath(actualPath) === normalizedPath
|
|
2589
|
+
},
|
|
2590
|
+
{ timeout: aSec * 1000 },
|
|
2591
|
+
)
|
|
2592
|
+
.catch(e => {
|
|
2593
|
+
e = wrapError(e)
|
|
2594
|
+
if (e.message.indexOf('timeout')) {
|
|
2595
|
+
throw new Error(`expected path to be ${normalizedPath}, but found ${normalizePath(actualPath)}`)
|
|
2532
2596
|
}
|
|
2533
2597
|
throw e
|
|
2534
2598
|
})
|
|
@@ -3010,32 +3074,33 @@ async function findClickable(locator, locateFn) {
|
|
|
3010
3074
|
return await locateFn(locator.value) // by css or xpath
|
|
3011
3075
|
}
|
|
3012
3076
|
|
|
3013
|
-
async function findFields(locator) {
|
|
3077
|
+
async function findFields(locator, context = null) {
|
|
3078
|
+
const locateFn = prepareLocateFn.call(this, context)
|
|
3014
3079
|
locator = new Locator(locator)
|
|
3015
3080
|
|
|
3016
3081
|
if (this._isCustomLocator(locator)) {
|
|
3017
|
-
return
|
|
3082
|
+
return locateFn(locator)
|
|
3018
3083
|
}
|
|
3019
3084
|
|
|
3020
|
-
if (locator.isAccessibilityId() && !this.isWeb) return
|
|
3021
|
-
if (locator.isRole()) return
|
|
3022
|
-
if (!locator.isFuzzy()) return
|
|
3085
|
+
if (locator.isAccessibilityId() && !this.isWeb) return locateFn(locator)
|
|
3086
|
+
if (locator.isRole()) return locateFn(locator)
|
|
3087
|
+
if (!locator.isFuzzy()) return locateFn(locator)
|
|
3023
3088
|
|
|
3024
3089
|
const literal = xpathLocator.literal(locator.value)
|
|
3025
|
-
let els = await
|
|
3090
|
+
let els = await locateFn(Locator.field.labelEquals(literal))
|
|
3026
3091
|
if (els.length) return els
|
|
3027
3092
|
|
|
3028
|
-
els = await
|
|
3093
|
+
els = await locateFn(Locator.field.labelContains(literal))
|
|
3029
3094
|
if (els.length) return els
|
|
3030
3095
|
|
|
3031
|
-
els = await
|
|
3096
|
+
els = await locateFn(Locator.field.byName(literal))
|
|
3032
3097
|
if (els.length) return els
|
|
3033
3098
|
|
|
3034
|
-
return await
|
|
3099
|
+
return await locateFn(locator.value) // by css or xpath
|
|
3035
3100
|
}
|
|
3036
3101
|
|
|
3037
|
-
async function proceedSeeField(assertType, field, value) {
|
|
3038
|
-
const res = await findFields.call(this, field)
|
|
3102
|
+
async function proceedSeeField(assertType, field, value, context) {
|
|
3103
|
+
const res = await findFields.call(this, field, context)
|
|
3039
3104
|
assertElementExists(res, field, 'Field')
|
|
3040
3105
|
const elem = usingFirstElement(res)
|
|
3041
3106
|
const elemId = getElementId(elem)
|
|
@@ -5,16 +5,27 @@ import { isNotSet } from '../utils.js'
|
|
|
5
5
|
|
|
6
6
|
const hooks = ['Before', 'After', 'BeforeSuite', 'AfterSuite']
|
|
7
7
|
|
|
8
|
+
const RETRY_PRIORITIES = {
|
|
9
|
+
MANUAL_STEP: 100,
|
|
10
|
+
STEP_PLUGIN: 50,
|
|
11
|
+
SCENARIO_CONFIG: 30,
|
|
12
|
+
FEATURE_CONFIG: 20,
|
|
13
|
+
HOOK_CONFIG: 10,
|
|
14
|
+
}
|
|
15
|
+
|
|
8
16
|
export default function () {
|
|
9
17
|
event.dispatcher.on(event.suite.before, suite => {
|
|
10
18
|
let retryConfig = Config.get('retry')
|
|
11
19
|
if (!retryConfig) return
|
|
12
20
|
|
|
13
21
|
if (Number.isInteger(+retryConfig)) {
|
|
14
|
-
// is number
|
|
15
22
|
const retryNum = +retryConfig
|
|
16
23
|
output.log(`Retries: ${retryNum}`)
|
|
17
|
-
|
|
24
|
+
|
|
25
|
+
if (suite.retries() === -1 || (suite.opts.retryPriority || 0) <= RETRY_PRIORITIES.FEATURE_CONFIG) {
|
|
26
|
+
suite.retries(retryNum)
|
|
27
|
+
suite.opts.retryPriority = RETRY_PRIORITIES.FEATURE_CONFIG
|
|
28
|
+
}
|
|
18
29
|
return
|
|
19
30
|
}
|
|
20
31
|
|
|
@@ -30,11 +41,18 @@ export default function () {
|
|
|
30
41
|
hooks
|
|
31
42
|
.filter(hook => !!config[hook])
|
|
32
43
|
.forEach(hook => {
|
|
33
|
-
|
|
44
|
+
const retryKey = `retry${hook}`
|
|
45
|
+
if (isNotSet(suite.opts[retryKey])) {
|
|
46
|
+
suite.opts[retryKey] = config[hook]
|
|
47
|
+
suite.opts[`${retryKey}Priority`] = RETRY_PRIORITIES.HOOK_CONFIG
|
|
48
|
+
}
|
|
34
49
|
})
|
|
35
50
|
|
|
36
51
|
if (config.Feature) {
|
|
37
|
-
if (
|
|
52
|
+
if (suite.retries() === -1 || (suite.opts.retryPriority || 0) <= RETRY_PRIORITIES.FEATURE_CONFIG) {
|
|
53
|
+
suite.retries(config.Feature)
|
|
54
|
+
suite.opts.retryPriority = RETRY_PRIORITIES.FEATURE_CONFIG
|
|
55
|
+
}
|
|
38
56
|
}
|
|
39
57
|
|
|
40
58
|
output.log(`Retries: ${JSON.stringify(config)}`)
|
|
@@ -46,7 +64,10 @@ export default function () {
|
|
|
46
64
|
if (!retryConfig) return
|
|
47
65
|
|
|
48
66
|
if (Number.isInteger(+retryConfig)) {
|
|
49
|
-
if (test.retries() === -1)
|
|
67
|
+
if (test.retries() === -1) {
|
|
68
|
+
test.retries(retryConfig)
|
|
69
|
+
test.opts.retryPriority = RETRY_PRIORITIES.SCENARIO_CONFIG
|
|
70
|
+
}
|
|
50
71
|
return
|
|
51
72
|
}
|
|
52
73
|
|
|
@@ -62,9 +83,14 @@ export default function () {
|
|
|
62
83
|
}
|
|
63
84
|
|
|
64
85
|
if (config.Scenario) {
|
|
65
|
-
if (test.retries() === -1
|
|
86
|
+
if (test.retries() === -1 || (test.opts.retryPriority || 0) <= RETRY_PRIORITIES.SCENARIO_CONFIG) {
|
|
87
|
+
test.retries(config.Scenario)
|
|
88
|
+
test.opts.retryPriority = RETRY_PRIORITIES.SCENARIO_CONFIG
|
|
89
|
+
}
|
|
66
90
|
output.log(`Retries: ${config.Scenario}`)
|
|
67
91
|
}
|
|
68
92
|
}
|
|
69
93
|
})
|
|
70
94
|
}
|
|
95
|
+
|
|
96
|
+
export { RETRY_PRIORITIES }
|