codeceptjs 3.7.6-beta.2 → 3.7.6-beta.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/event.js +10 -1
- package/lib/helper/Appium.js +145 -25
- package/lib/helper/JSONResponse.js +4 -4
- package/lib/helper/Playwright.js +2 -3
- package/lib/helper/Puppeteer.js +6 -6
- package/lib/helper/WebDriver.js +3 -3
- package/lib/listener/steps.js +2 -2
- package/lib/output.js +51 -5
- package/lib/plugin/htmlReporter.js +147 -79
- package/lib/result.js +100 -23
- package/package.json +5 -2
- package/typings/promiseBasedTypes.d.ts +37 -25
- package/typings/types.d.ts +127 -30
package/lib/event.js
CHANGED
|
@@ -2,13 +2,22 @@ const debug = require('debug')('codeceptjs:event')
|
|
|
2
2
|
const events = require('events')
|
|
3
3
|
const { error } = require('./output')
|
|
4
4
|
|
|
5
|
+
const MAX_LISTENERS = 200
|
|
6
|
+
|
|
5
7
|
const dispatcher = new events.EventEmitter()
|
|
6
8
|
|
|
7
|
-
dispatcher.setMaxListeners(
|
|
9
|
+
dispatcher.setMaxListeners(MAX_LISTENERS)
|
|
10
|
+
|
|
11
|
+
// Increase process max listeners to prevent warnings for beforeExit and other events
|
|
12
|
+
if (typeof process.setMaxListeners === 'function') {
|
|
13
|
+
process.setMaxListeners(MAX_LISTENERS)
|
|
14
|
+
}
|
|
15
|
+
|
|
8
16
|
/**
|
|
9
17
|
* @namespace
|
|
10
18
|
* @alias event
|
|
11
19
|
*/
|
|
20
|
+
|
|
12
21
|
module.exports = {
|
|
13
22
|
/**
|
|
14
23
|
* @type {NodeJS.EventEmitter}
|
package/lib/helper/Appium.js
CHANGED
|
@@ -391,6 +391,29 @@ class Appium extends Webdriver {
|
|
|
391
391
|
return `${protocol}://${hostname}:${port}${normalizedPath}/session/${this.browser.sessionId}`
|
|
392
392
|
}
|
|
393
393
|
|
|
394
|
+
/**
|
|
395
|
+
* Helper method to safely call isDisplayed() on mobile elements.
|
|
396
|
+
* Handles the case where webdriverio tries to use execute/sync which isn't supported in Appium.
|
|
397
|
+
* @private
|
|
398
|
+
*/
|
|
399
|
+
async _isDisplayedSafe(element) {
|
|
400
|
+
if (this.isWeb) {
|
|
401
|
+
// For web contexts, use the normal isDisplayed
|
|
402
|
+
return element.isDisplayed()
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
try {
|
|
406
|
+
return await element.isDisplayed()
|
|
407
|
+
} catch (err) {
|
|
408
|
+
// If isDisplayed fails due to execute/sync not being supported in native mobile contexts,
|
|
409
|
+
// fall back to assuming the element is displayed (since we found it)
|
|
410
|
+
if (err.message && err.message.includes('Method is not implemented')) {
|
|
411
|
+
return true
|
|
412
|
+
}
|
|
413
|
+
throw err
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
|
|
394
417
|
/**
|
|
395
418
|
* Execute code only on iOS
|
|
396
419
|
*
|
|
@@ -619,6 +642,7 @@ class Appium extends Webdriver {
|
|
|
619
642
|
*/
|
|
620
643
|
async resetApp() {
|
|
621
644
|
onlyForApps.call(this)
|
|
645
|
+
this.isWeb = false // Reset to native context after app reset
|
|
622
646
|
return this.axios({
|
|
623
647
|
method: 'post',
|
|
624
648
|
url: `${this._buildAppiumEndpoint()}/appium/app/reset`,
|
|
@@ -1134,7 +1158,7 @@ class Appium extends Webdriver {
|
|
|
1134
1158
|
],
|
|
1135
1159
|
},
|
|
1136
1160
|
])
|
|
1137
|
-
await this.browser.pause(
|
|
1161
|
+
await this.browser.pause(2000)
|
|
1138
1162
|
}
|
|
1139
1163
|
|
|
1140
1164
|
/**
|
|
@@ -1296,28 +1320,26 @@ class Appium extends Webdriver {
|
|
|
1296
1320
|
let currentSource
|
|
1297
1321
|
return browser
|
|
1298
1322
|
.waitUntil(
|
|
1299
|
-
() => {
|
|
1323
|
+
async () => {
|
|
1300
1324
|
if (err) {
|
|
1301
1325
|
return new Error(`Scroll to the end and element ${searchableLocator} was not found`)
|
|
1302
1326
|
}
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
})
|
|
1320
|
-
})
|
|
1327
|
+
const els = await browser.$$(parseLocator.call(this, searchableLocator))
|
|
1328
|
+
if (els.length) {
|
|
1329
|
+
const displayed = await this._isDisplayedSafe(els[0])
|
|
1330
|
+
if (displayed) {
|
|
1331
|
+
return true
|
|
1332
|
+
}
|
|
1333
|
+
}
|
|
1334
|
+
|
|
1335
|
+
await this[direction](scrollLocator, offset, speed)
|
|
1336
|
+
const source = await this.browser.getPageSource()
|
|
1337
|
+
if (source === currentSource) {
|
|
1338
|
+
err = true
|
|
1339
|
+
} else {
|
|
1340
|
+
currentSource = source
|
|
1341
|
+
return false
|
|
1342
|
+
}
|
|
1321
1343
|
},
|
|
1322
1344
|
timeout * 1000,
|
|
1323
1345
|
errorMsg,
|
|
@@ -1523,7 +1545,28 @@ class Appium extends Webdriver {
|
|
|
1523
1545
|
*/
|
|
1524
1546
|
async dontSeeElement(locator) {
|
|
1525
1547
|
if (this.isWeb) return super.dontSeeElement(locator)
|
|
1526
|
-
|
|
1548
|
+
|
|
1549
|
+
// For mobile native apps, use safe isDisplayed wrapper
|
|
1550
|
+
const parsedLocator = parseLocator.call(this, locator)
|
|
1551
|
+
const res = await this._locate(parsedLocator, false)
|
|
1552
|
+
const { truth } = require('../assert/truth')
|
|
1553
|
+
const Locator = require('../locator')
|
|
1554
|
+
|
|
1555
|
+
if (!res || res.length === 0) {
|
|
1556
|
+
return truth(`elements of ${new Locator(parsedLocator)}`, 'to be seen').negate(false)
|
|
1557
|
+
}
|
|
1558
|
+
|
|
1559
|
+
const selected = []
|
|
1560
|
+
for (const el of res) {
|
|
1561
|
+
const displayed = await this._isDisplayedSafe(el)
|
|
1562
|
+
if (displayed) selected.push(true)
|
|
1563
|
+
}
|
|
1564
|
+
|
|
1565
|
+
try {
|
|
1566
|
+
return truth(`elements of ${new Locator(parsedLocator)}`, 'to be seen').negate(selected)
|
|
1567
|
+
} catch (err) {
|
|
1568
|
+
throw err
|
|
1569
|
+
}
|
|
1527
1570
|
}
|
|
1528
1571
|
|
|
1529
1572
|
/**
|
|
@@ -1577,7 +1620,18 @@ class Appium extends Webdriver {
|
|
|
1577
1620
|
*/
|
|
1578
1621
|
async grabNumberOfVisibleElements(locator) {
|
|
1579
1622
|
if (this.isWeb) return super.grabNumberOfVisibleElements(locator)
|
|
1580
|
-
|
|
1623
|
+
|
|
1624
|
+
// For mobile native apps, use safe isDisplayed wrapper
|
|
1625
|
+
const parsedLocator = parseLocator.call(this, locator)
|
|
1626
|
+
const res = await this._locate(parsedLocator)
|
|
1627
|
+
|
|
1628
|
+
const selected = []
|
|
1629
|
+
for (const el of res) {
|
|
1630
|
+
const displayed = await this._isDisplayedSafe(el)
|
|
1631
|
+
if (displayed) selected.push(true)
|
|
1632
|
+
}
|
|
1633
|
+
|
|
1634
|
+
return selected.length
|
|
1581
1635
|
}
|
|
1582
1636
|
|
|
1583
1637
|
/**
|
|
@@ -1656,7 +1710,30 @@ class Appium extends Webdriver {
|
|
|
1656
1710
|
*/
|
|
1657
1711
|
async seeElement(locator) {
|
|
1658
1712
|
if (this.isWeb) return super.seeElement(locator)
|
|
1659
|
-
|
|
1713
|
+
|
|
1714
|
+
// For mobile native apps, use safe isDisplayed wrapper
|
|
1715
|
+
const parsedLocator = parseLocator.call(this, locator)
|
|
1716
|
+
const res = await this._locate(parsedLocator, true)
|
|
1717
|
+
const ElementNotFound = require('./errors/ElementNotFound')
|
|
1718
|
+
const { truth } = require('../assert/truth')
|
|
1719
|
+
const { dontSeeElementError } = require('./errors/ElementAssertion')
|
|
1720
|
+
const Locator = require('../locator')
|
|
1721
|
+
|
|
1722
|
+
if (!res || res.length === 0) {
|
|
1723
|
+
throw new ElementNotFound(parsedLocator)
|
|
1724
|
+
}
|
|
1725
|
+
|
|
1726
|
+
const selected = []
|
|
1727
|
+
for (const el of res) {
|
|
1728
|
+
const displayed = await this._isDisplayedSafe(el)
|
|
1729
|
+
if (displayed) selected.push(true)
|
|
1730
|
+
}
|
|
1731
|
+
|
|
1732
|
+
try {
|
|
1733
|
+
return truth(`elements of ${new Locator(parsedLocator)}`, 'to be seen').assert(selected)
|
|
1734
|
+
} catch (e) {
|
|
1735
|
+
dontSeeElementError(parsedLocator)
|
|
1736
|
+
}
|
|
1660
1737
|
}
|
|
1661
1738
|
|
|
1662
1739
|
/**
|
|
@@ -1703,7 +1780,30 @@ class Appium extends Webdriver {
|
|
|
1703
1780
|
*/
|
|
1704
1781
|
async waitForVisible(locator, sec = null) {
|
|
1705
1782
|
if (this.isWeb) return super.waitForVisible(locator, sec)
|
|
1706
|
-
|
|
1783
|
+
|
|
1784
|
+
// For mobile native apps, use safe isDisplayed wrapper
|
|
1785
|
+
const parsedLocator = parseLocator.call(this, locator)
|
|
1786
|
+
const aSec = sec || this.options.waitForTimeoutInSeconds
|
|
1787
|
+
const Locator = require('../locator')
|
|
1788
|
+
|
|
1789
|
+
return this.browser.waitUntil(
|
|
1790
|
+
async () => {
|
|
1791
|
+
const res = await this._res(parsedLocator)
|
|
1792
|
+
if (!res || res.length === 0) return false
|
|
1793
|
+
|
|
1794
|
+
const selected = []
|
|
1795
|
+
for (const el of res) {
|
|
1796
|
+
const displayed = await this._isDisplayedSafe(el)
|
|
1797
|
+
if (displayed) selected.push(true)
|
|
1798
|
+
}
|
|
1799
|
+
|
|
1800
|
+
return selected.length > 0
|
|
1801
|
+
},
|
|
1802
|
+
{
|
|
1803
|
+
timeout: aSec * 1000,
|
|
1804
|
+
timeoutMsg: `element (${new Locator(parsedLocator)}) still not visible after ${aSec} sec`,
|
|
1805
|
+
},
|
|
1806
|
+
)
|
|
1707
1807
|
}
|
|
1708
1808
|
|
|
1709
1809
|
/**
|
|
@@ -1712,7 +1812,27 @@ class Appium extends Webdriver {
|
|
|
1712
1812
|
*/
|
|
1713
1813
|
async waitForInvisible(locator, sec = null) {
|
|
1714
1814
|
if (this.isWeb) return super.waitForInvisible(locator, sec)
|
|
1715
|
-
|
|
1815
|
+
|
|
1816
|
+
// For mobile native apps, use safe isDisplayed wrapper
|
|
1817
|
+
const parsedLocator = parseLocator.call(this, locator)
|
|
1818
|
+
const aSec = sec || this.options.waitForTimeoutInSeconds
|
|
1819
|
+
const Locator = require('../locator')
|
|
1820
|
+
|
|
1821
|
+
return this.browser.waitUntil(
|
|
1822
|
+
async () => {
|
|
1823
|
+
const res = await this._res(parsedLocator)
|
|
1824
|
+
if (!res || res.length === 0) return true
|
|
1825
|
+
|
|
1826
|
+
const selected = []
|
|
1827
|
+
for (const el of res) {
|
|
1828
|
+
const displayed = await this._isDisplayedSafe(el)
|
|
1829
|
+
if (displayed) selected.push(true)
|
|
1830
|
+
}
|
|
1831
|
+
|
|
1832
|
+
return selected.length === 0
|
|
1833
|
+
},
|
|
1834
|
+
{ timeout: aSec * 1000, timeoutMsg: `element (${new Locator(parsedLocator)}) still visible after ${aSec} sec` },
|
|
1835
|
+
)
|
|
1716
1836
|
}
|
|
1717
1837
|
|
|
1718
1838
|
/**
|
|
@@ -72,11 +72,11 @@ class JSONResponse extends Helper {
|
|
|
72
72
|
if (!this.helpers[this.options.requestHelper]) {
|
|
73
73
|
throw new Error(`Error setting JSONResponse, helper ${this.options.requestHelper} is not enabled in config, helpers: ${Object.keys(this.helpers)}`)
|
|
74
74
|
}
|
|
75
|
-
const origOnResponse = this.helpers[this.options.requestHelper].config.onResponse
|
|
75
|
+
const origOnResponse = this.helpers[this.options.requestHelper].config.onResponse
|
|
76
76
|
this.helpers[this.options.requestHelper].config.onResponse = response => {
|
|
77
|
-
this.response = response
|
|
78
|
-
if (typeof origOnResponse === 'function') origOnResponse(response)
|
|
79
|
-
}
|
|
77
|
+
this.response = response
|
|
78
|
+
if (typeof origOnResponse === 'function') origOnResponse(response)
|
|
79
|
+
}
|
|
80
80
|
}
|
|
81
81
|
|
|
82
82
|
_before() {
|
package/lib/helper/Playwright.js
CHANGED
|
@@ -370,7 +370,6 @@ class Playwright extends Helper {
|
|
|
370
370
|
if (typeof config.storageState !== 'undefined') {
|
|
371
371
|
this.storageState = config.storageState
|
|
372
372
|
}
|
|
373
|
-
|
|
374
373
|
}
|
|
375
374
|
|
|
376
375
|
_validateConfig(config) {
|
|
@@ -1844,7 +1843,7 @@ class Playwright extends Helper {
|
|
|
1844
1843
|
|
|
1845
1844
|
/**
|
|
1846
1845
|
*
|
|
1847
|
-
* _Note:_ Shortcuts like `'Meta'` + `'A'` do not work on macOS ([
|
|
1846
|
+
* _Note:_ Shortcuts like `'Meta'` + `'A'` do not work on macOS ([puppeteer/puppeteer#1313](https://github.com/puppeteer/puppeteer/issues/1313)).
|
|
1848
1847
|
*
|
|
1849
1848
|
* {{> pressKeyWithKeyNormalization }}
|
|
1850
1849
|
*/
|
|
@@ -3191,7 +3190,7 @@ class Playwright extends Helper {
|
|
|
3191
3190
|
/**
|
|
3192
3191
|
* Waits for navigation to finish. By default, it takes configured `waitForNavigation` option.
|
|
3193
3192
|
*
|
|
3194
|
-
* See [Playwright's reference](https://playwright.dev/docs/api/class-page
|
|
3193
|
+
* See [Playwright's reference](https://playwright.dev/docs/api/class-page#page-wait-for-navigation)
|
|
3195
3194
|
*
|
|
3196
3195
|
* @param {*} options
|
|
3197
3196
|
*/
|
package/lib/helper/Puppeteer.js
CHANGED
|
@@ -64,7 +64,7 @@ const consoleLogStore = new Console()
|
|
|
64
64
|
* @prop {boolean} [keepBrowserState=false] - keep browser state between tests when `restart` is set to false.
|
|
65
65
|
* @prop {boolean} [keepCookies=false] - keep cookies between tests when `restart` is set to false.
|
|
66
66
|
* @prop {number} [waitForAction=100] - how long to wait after click, doubleClick or PressKey actions in ms. Default: 100.
|
|
67
|
-
* @prop {string} [waitForNavigation=load] - when to consider navigation succeeded. Possible options: `load`, `domcontentloaded`, `networkidle0`, `networkidle2`. See [Puppeteer API](https://github.com/
|
|
67
|
+
* @prop {string|string[]} [waitForNavigation=load] - when to consider navigation succeeded. Possible options: `load`, `domcontentloaded`, `networkidle0`, `networkidle2`. See [Puppeteer API](https://github.com/puppeteer/puppeteer/blob/main/docs/api/puppeteer.waitforoptions.md). Array values are accepted as well.
|
|
68
68
|
* @prop {number} [pressKeyDelay=10] - delay between key presses in ms. Used when calling Puppeteers page.type(...) in fillField/appendField
|
|
69
69
|
* @prop {number} [getPageTimeout=30000] - config option to set maximum navigation time in milliseconds. If the timeout is set to 0, then timeout will be disabled.
|
|
70
70
|
* @prop {number} [waitForTimeout=1000] - default wait* timeout in ms.
|
|
@@ -72,13 +72,13 @@ const consoleLogStore = new Console()
|
|
|
72
72
|
* @prop {string} [userAgent] - user-agent string.
|
|
73
73
|
* @prop {boolean} [manualStart=false] - do not start browser before a test, start it manually inside a helper with `this.helpers["Puppeteer"]._startBrowser()`.
|
|
74
74
|
* @prop {string} [browser=chrome] - can be changed to `firefox` when using [puppeteer-firefox](https://codecept.io/helpers/Puppeteer-firefox).
|
|
75
|
-
* @prop {object} [chrome] - pass additional [Puppeteer run options](https://github.com/
|
|
75
|
+
* @prop {object} [chrome] - pass additional [Puppeteer run options](https://github.com/puppeteer/puppeteer/blob/main/docs/api/puppeteer.launchoptions.md).
|
|
76
76
|
* @prop {boolean} [highlightElement] - highlight the interacting elements. Default: false. Note: only activate under verbose mode (--verbose).
|
|
77
77
|
*/
|
|
78
78
|
const config = {}
|
|
79
79
|
|
|
80
80
|
/**
|
|
81
|
-
* Uses [Google Chrome's Puppeteer](https://github.com/
|
|
81
|
+
* Uses [Google Chrome's Puppeteer](https://github.com/puppeteer/puppeteer) library to run tests inside headless Chrome.
|
|
82
82
|
* Browser control is executed via DevTools Protocol (instead of Selenium).
|
|
83
83
|
* This helper works with a browser out of the box with no additional tools required to install.
|
|
84
84
|
*
|
|
@@ -1349,7 +1349,7 @@ class Puppeteer extends Helper {
|
|
|
1349
1349
|
}
|
|
1350
1350
|
|
|
1351
1351
|
/**
|
|
1352
|
-
* _Note:_ Shortcuts like `'Meta'` + `'A'` do not work on macOS ([
|
|
1352
|
+
* _Note:_ Shortcuts like `'Meta'` + `'A'` do not work on macOS ([puppeteer/puppeteer#1313](https://github.com/puppeteer/puppeteer/issues/1313)).
|
|
1353
1353
|
*
|
|
1354
1354
|
* {{> pressKeyWithKeyNormalization }}
|
|
1355
1355
|
*/
|
|
@@ -2463,7 +2463,7 @@ class Puppeteer extends Helper {
|
|
|
2463
2463
|
/**
|
|
2464
2464
|
* Waits for navigation to finish. By default, takes configured `waitForNavigation` option.
|
|
2465
2465
|
*
|
|
2466
|
-
* See [Puppeteer's reference](https://github.com/
|
|
2466
|
+
* See [Puppeteer's reference](https://github.com/puppeteer/puppeteer/blob/main/docs/api/puppeteer.page.waitfornavigation.md)
|
|
2467
2467
|
*
|
|
2468
2468
|
* @param {*} opts
|
|
2469
2469
|
*/
|
|
@@ -3093,7 +3093,7 @@ async function getClickablePoint(el) {
|
|
|
3093
3093
|
}
|
|
3094
3094
|
|
|
3095
3095
|
// List of key values to key definitions
|
|
3096
|
-
// https://github.com/
|
|
3096
|
+
// https://github.com/puppeteer/puppeteer/blob/v1.20.0/lib/USKeyboardLayout.js
|
|
3097
3097
|
const keyDefinitionMap = {
|
|
3098
3098
|
0: 'Digit0',
|
|
3099
3099
|
1: 'Digit1',
|
package/lib/helper/WebDriver.js
CHANGED
|
@@ -998,7 +998,7 @@ class WebDriver extends Helper {
|
|
|
998
998
|
* {{ react }}
|
|
999
999
|
*/
|
|
1000
1000
|
async click(locator, context = null) {
|
|
1001
|
-
|
|
1001
|
+
const clickMethod = this.browser.isMobile && this.browser.capabilities.platformName !== 'android' ? 'touchClick' : 'elementClick'
|
|
1002
1002
|
const locateFn = prepareLocateFn.call(this, context)
|
|
1003
1003
|
|
|
1004
1004
|
const res = await findClickable.call(this, locator, locateFn)
|
|
@@ -1217,7 +1217,7 @@ class WebDriver extends Helper {
|
|
|
1217
1217
|
* {{> checkOption }}
|
|
1218
1218
|
*/
|
|
1219
1219
|
async checkOption(field, context = null) {
|
|
1220
|
-
|
|
1220
|
+
const clickMethod = this.browser.isMobile && this.browser.capabilities.platformName !== 'android' ? 'touchClick' : 'elementClick'
|
|
1221
1221
|
const locateFn = prepareLocateFn.call(this, context)
|
|
1222
1222
|
|
|
1223
1223
|
const res = await findCheckable.call(this, field, locateFn)
|
|
@@ -1237,7 +1237,7 @@ class WebDriver extends Helper {
|
|
|
1237
1237
|
* {{> uncheckOption }}
|
|
1238
1238
|
*/
|
|
1239
1239
|
async uncheckOption(field, context = null) {
|
|
1240
|
-
|
|
1240
|
+
const clickMethod = this.browser.isMobile && this.browser.capabilities.platformName !== 'android' ? 'touchClick' : 'elementClick'
|
|
1241
1241
|
const locateFn = prepareLocateFn.call(this, context)
|
|
1242
1242
|
|
|
1243
1243
|
const res = await findCheckable.call(this, field, locateFn)
|
package/lib/listener/steps.js
CHANGED
|
@@ -79,14 +79,14 @@ module.exports = function () {
|
|
|
79
79
|
return currentHook.steps.push(step)
|
|
80
80
|
}
|
|
81
81
|
if (!currentTest || !currentTest.steps) return
|
|
82
|
-
|
|
82
|
+
|
|
83
83
|
// Check if we're in a session that should be excluded from main test steps
|
|
84
84
|
const currentSessionId = recorder.getCurrentSessionId()
|
|
85
85
|
if (currentSessionId && EXCLUDED_SESSIONS.includes(currentSessionId)) {
|
|
86
86
|
// Skip adding this step to the main test steps
|
|
87
87
|
return
|
|
88
88
|
}
|
|
89
|
-
|
|
89
|
+
|
|
90
90
|
currentTest.steps.push(step)
|
|
91
91
|
})
|
|
92
92
|
|
package/lib/output.js
CHANGED
|
@@ -50,7 +50,40 @@ module.exports = {
|
|
|
50
50
|
*/
|
|
51
51
|
process(process) {
|
|
52
52
|
if (process === null) return (outputProcess = '')
|
|
53
|
-
if (process)
|
|
53
|
+
if (process) {
|
|
54
|
+
// Handle objects by converting to empty string or extracting properties
|
|
55
|
+
let processValue = process
|
|
56
|
+
if (typeof process === 'object') {
|
|
57
|
+
// If it's an object, try to extract a numeric value or use empty string
|
|
58
|
+
processValue = process.id || process.index || process.worker || ''
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Check if this is a run-multiple process (contains : or .)
|
|
62
|
+
// Format: "1.runName:browserName" from run-multiple
|
|
63
|
+
if (String(processValue).includes(':') || (String(processValue).includes('.') && String(processValue).split('.').length > 1)) {
|
|
64
|
+
// Keep original format for run-multiple
|
|
65
|
+
outputProcess = colors.cyan.bold(`[${processValue}]`)
|
|
66
|
+
} else {
|
|
67
|
+
// Standard worker format for run-workers
|
|
68
|
+
const processNum = parseInt(processValue, 10)
|
|
69
|
+
const processStr = !isNaN(processNum) ? String(processNum).padStart(2, '0') : String(processValue).padStart(2, '0')
|
|
70
|
+
|
|
71
|
+
// Assign different colors to different workers for better identification
|
|
72
|
+
const workerColors = [
|
|
73
|
+
colors.cyan, // Worker 01 - Cyan
|
|
74
|
+
colors.magenta, // Worker 02 - Magenta
|
|
75
|
+
colors.green, // Worker 03 - Green
|
|
76
|
+
colors.yellow, // Worker 04 - Yellow
|
|
77
|
+
colors.blue, // Worker 05 - Blue
|
|
78
|
+
colors.red, // Worker 06 - Red
|
|
79
|
+
colors.white, // Worker 07 - White
|
|
80
|
+
colors.gray, // Worker 08 - Gray
|
|
81
|
+
]
|
|
82
|
+
const workerIndex = !isNaN(processNum) ? processNum - 1 : -1
|
|
83
|
+
const colorFn = workerIndex >= 0 && workerColors[workerIndex % workerColors.length] ? workerColors[workerIndex % workerColors.length] : colors.cyan
|
|
84
|
+
outputProcess = colorFn.bold(`[Worker ${processStr}]`)
|
|
85
|
+
}
|
|
86
|
+
}
|
|
54
87
|
return outputProcess
|
|
55
88
|
},
|
|
56
89
|
|
|
@@ -149,25 +182,38 @@ module.exports = {
|
|
|
149
182
|
* @param {Mocha.Test} test
|
|
150
183
|
*/
|
|
151
184
|
started(test) {
|
|
152
|
-
|
|
185
|
+
// Only show feature name in workers mode (when outputProcess is set)
|
|
186
|
+
const featureName = outputProcess && test.parent?.title ? `${colors.cyan.bold(test.parent.title)} › ` : ''
|
|
187
|
+
print(` ${featureName}${colors.magenta.bold(test.title)}`)
|
|
153
188
|
},
|
|
154
189
|
/**
|
|
155
190
|
* @param {Mocha.Test} test
|
|
156
191
|
*/
|
|
157
192
|
passed(test) {
|
|
158
|
-
|
|
193
|
+
// Only show feature name in workers mode (when outputProcess is set)
|
|
194
|
+
const featureName = outputProcess && test.parent?.title ? `${colors.cyan(test.parent.title)} › ` : ''
|
|
195
|
+
const scenarioName = colors.bold(test.title)
|
|
196
|
+
const executionTime = colors.cyan(`in ${test.duration}ms`)
|
|
197
|
+
print(` ${colors.green.bold(figures.tick)} ${featureName}${scenarioName} ${executionTime}`)
|
|
159
198
|
},
|
|
160
199
|
/**
|
|
161
200
|
* @param {Mocha.Test} test
|
|
162
201
|
*/
|
|
163
202
|
failed(test) {
|
|
164
|
-
|
|
203
|
+
// Only show feature name in workers mode (when outputProcess is set)
|
|
204
|
+
const featureName = outputProcess && test.parent?.title ? `${colors.yellow(test.parent.title)} › ` : ''
|
|
205
|
+
const scenarioName = colors.bold(test.title)
|
|
206
|
+
const executionTime = colors.yellow(`in ${test.duration}ms`)
|
|
207
|
+
print(` ${colors.red.bold(figures.cross)} ${featureName}${scenarioName} ${executionTime}`)
|
|
165
208
|
},
|
|
166
209
|
/**
|
|
167
210
|
* @param {Mocha.Test} test
|
|
168
211
|
*/
|
|
169
212
|
skipped(test) {
|
|
170
|
-
|
|
213
|
+
// Only show feature name in workers mode (when outputProcess is set)
|
|
214
|
+
const featureName = outputProcess && test.parent?.title ? `${colors.gray(test.parent.title)} › ` : ''
|
|
215
|
+
const scenarioName = colors.bold(test.title)
|
|
216
|
+
print(` ${colors.yellow.bold('S')} ${featureName}${scenarioName}`)
|
|
171
217
|
},
|
|
172
218
|
},
|
|
173
219
|
|