codeceptjs 4.0.0-rc.2 → 4.0.0-rc.20
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/codecept.js +15 -2
- package/bin/codeceptq.js +49 -0
- package/bin/mcp-server.js +1187 -0
- package/docs/advanced.md +201 -0
- package/docs/agents.md +159 -0
- package/docs/ai.md +537 -0
- package/docs/aitrace.md +266 -0
- package/docs/api.md +332 -0
- package/docs/assertions.md +415 -0
- package/docs/auth.md +318 -0
- package/docs/basics.md +424 -0
- package/docs/bdd.md +539 -0
- package/docs/best.md +240 -0
- package/docs/bootstrap.md +132 -0
- package/docs/commands.md +352 -0
- package/docs/community-helpers.md +63 -0
- package/docs/configuration.md +230 -0
- package/docs/continuous-integration.md +497 -0
- package/docs/custom-helpers.md +297 -0
- package/docs/data.md +448 -0
- package/docs/debugging.md +332 -0
- package/docs/detox.md +235 -0
- package/docs/docker.md +136 -0
- package/docs/effects.md +179 -0
- package/docs/element-based-testing.md +295 -0
- package/docs/element-selection.md +125 -0
- package/docs/els.md +328 -0
- package/docs/examples.md +161 -0
- package/docs/heal.md +213 -0
- package/docs/helpers/ApiDataFactory.md +267 -0
- package/docs/helpers/Appium.md +1405 -0
- package/docs/helpers/Detox.md +665 -0
- package/docs/helpers/ExpectHelper.md +275 -0
- package/docs/helpers/FileSystem.md +152 -0
- package/docs/helpers/GraphQL.md +152 -0
- package/docs/helpers/GraphQLDataFactory.md +226 -0
- package/docs/helpers/JSONResponse.md +255 -0
- package/docs/helpers/Mochawesome.md +8 -0
- package/docs/helpers/MockRequest.md +377 -0
- package/docs/helpers/MockServer.md +212 -0
- package/docs/helpers/Playwright.md +2969 -0
- package/docs/helpers/Polly.md +44 -0
- package/docs/helpers/Protractor.md +1769 -0
- package/docs/helpers/Puppeteer-firefox.md +86 -0
- package/docs/helpers/Puppeteer.md +2690 -0
- package/docs/helpers/REST.md +289 -0
- package/docs/helpers/SoftExpectHelper.md +352 -0
- package/docs/helpers/WebDriver.md +2682 -0
- package/docs/hooks.md +339 -0
- package/docs/index.md +111 -0
- package/docs/installation.md +83 -0
- package/docs/internal-api.md +265 -0
- package/docs/internal-test-server.md +89 -0
- package/docs/locators.md +355 -0
- package/docs/mcp.md +485 -0
- package/docs/migration-4.md +556 -0
- package/docs/mobile.md +338 -0
- package/docs/pageobjects.md +399 -0
- package/docs/parallel.md +585 -0
- package/docs/playwright.md +714 -0
- package/docs/plugins.md +866 -0
- package/docs/puppeteer.md +314 -0
- package/docs/quickstart.md +120 -0
- package/docs/react.md +70 -0
- package/docs/reports.md +483 -0
- package/docs/retry.md +274 -0
- package/docs/secrets.md +150 -0
- package/docs/sessions.md +80 -0
- package/docs/shadow.md +68 -0
- package/docs/test-structure.md +275 -0
- package/docs/timeouts.md +183 -0
- package/docs/translation.md +247 -0
- package/docs/tutorial.md +271 -0
- package/docs/typescript.md +374 -0
- package/docs/web-element.md +251 -0
- package/docs/webdriver.md +708 -0
- package/docs/within.md +55 -0
- package/lib/ai.js +3 -2
- package/lib/aria.js +260 -0
- package/lib/assertions.js +18 -0
- package/lib/codecept.js +26 -23
- package/lib/command/check.js +2 -1
- package/lib/command/dryRun.js +24 -5
- package/lib/command/generate.js +2 -0
- package/lib/command/gherkin/snippets.js +5 -4
- package/lib/command/init.js +248 -269
- package/lib/command/list.js +150 -10
- package/lib/command/query.js +218 -0
- package/lib/command/run-multiple.js +2 -0
- package/lib/command/run-workers.js +2 -0
- package/lib/command/run.js +1 -1
- package/lib/command/workers/runTests.js +10 -10
- package/lib/config.js +77 -4
- package/lib/container.js +114 -17
- package/lib/effects.js +17 -0
- package/lib/element/WebElement.js +246 -2
- package/lib/els.js +12 -6
- package/lib/globals.js +32 -19
- package/lib/heal.js +4 -3
- package/lib/helper/ApiDataFactory.js +2 -1
- package/lib/helper/Appium.js +8 -8
- package/lib/helper/FileSystem.js +3 -2
- package/lib/helper/GraphQLDataFactory.js +2 -1
- package/lib/helper/Playwright.js +228 -162
- package/lib/helper/Puppeteer.js +208 -76
- package/lib/helper/WebDriver.js +173 -68
- package/lib/helper/errors/MultipleElementsFound.js +27 -110
- package/lib/helper/errors/NonFocusedType.js +8 -0
- package/lib/helper/extras/Download.js +45 -0
- package/lib/helper/extras/PlaywrightReactVueLocator.js +45 -36
- package/lib/helper/extras/elementSelection.js +58 -0
- package/lib/helper/extras/focusCheck.js +43 -0
- package/lib/helper/extras/richTextEditor.js +178 -0
- package/lib/helper/scripts/dropFile.js +11 -0
- package/lib/history.js +3 -2
- package/lib/html.js +103 -16
- package/lib/index.js +9 -1
- package/lib/listener/config.js +6 -4
- package/lib/listener/emptyRun.js +2 -1
- package/lib/listener/globalRetry.js +32 -6
- package/lib/listener/helpers.js +4 -1
- package/lib/listener/mocha.js +2 -1
- package/lib/listener/pageobjects.js +43 -0
- package/lib/listener/result.js +3 -2
- package/lib/locator.js +126 -3
- package/lib/mocha/cli.js +14 -2
- package/lib/mocha/factory.js +7 -2
- package/lib/mocha/inject.js +1 -1
- package/lib/mocha/scenarioConfig.js +2 -1
- package/lib/mocha/ui.js +5 -6
- package/lib/parser.js +2 -2
- package/lib/pause.js +38 -4
- package/lib/plugin/aiTrace.js +453 -0
- package/lib/plugin/analyze.js +1 -1
- package/lib/plugin/auth.js +3 -3
- package/lib/plugin/browser.js +77 -0
- package/lib/plugin/expose.js +159 -0
- package/lib/plugin/heal.js +44 -1
- package/lib/plugin/pageInfo.js +53 -49
- package/lib/plugin/pause.js +131 -0
- package/lib/plugin/pauseOnFail.js +10 -34
- package/lib/plugin/retryFailedStep.js +28 -19
- package/lib/plugin/screencast.js +287 -0
- package/lib/plugin/screenshot.js +563 -0
- package/lib/plugin/screenshotOnFail.js +8 -171
- package/lib/rerun.js +2 -1
- package/lib/result.js +2 -1
- package/lib/step/base.js +3 -2
- package/lib/step/config.js +15 -2
- package/lib/step/record.js +2 -2
- package/lib/store.js +72 -3
- package/lib/translation.js +2 -1
- package/lib/utils/mask_data.js +2 -1
- package/lib/utils/pluginParser.js +151 -0
- package/lib/utils/trace.js +297 -0
- package/lib/utils.js +77 -3
- package/lib/workers.js +52 -22
- package/package.json +19 -13
- package/typings/index.d.ts +19 -5
- package/docs/webapi/amOnPage.mustache +0 -11
- package/docs/webapi/appendField.mustache +0 -11
- package/docs/webapi/attachFile.mustache +0 -12
- package/docs/webapi/blur.mustache +0 -18
- package/docs/webapi/checkOption.mustache +0 -13
- package/docs/webapi/clearCookie.mustache +0 -9
- package/docs/webapi/clearField.mustache +0 -9
- package/docs/webapi/click.mustache +0 -29
- package/docs/webapi/clickLink.mustache +0 -8
- package/docs/webapi/closeCurrentTab.mustache +0 -7
- package/docs/webapi/closeOtherTabs.mustache +0 -8
- package/docs/webapi/dontSee.mustache +0 -11
- package/docs/webapi/dontSeeCheckboxIsChecked.mustache +0 -10
- package/docs/webapi/dontSeeCookie.mustache +0 -8
- package/docs/webapi/dontSeeCurrentPathEquals.mustache +0 -10
- package/docs/webapi/dontSeeCurrentUrlEquals.mustache +0 -10
- package/docs/webapi/dontSeeElement.mustache +0 -8
- package/docs/webapi/dontSeeElementInDOM.mustache +0 -8
- package/docs/webapi/dontSeeInCurrentUrl.mustache +0 -4
- package/docs/webapi/dontSeeInField.mustache +0 -11
- package/docs/webapi/dontSeeInSource.mustache +0 -8
- package/docs/webapi/dontSeeInTitle.mustache +0 -8
- package/docs/webapi/dontSeeTraffic.mustache +0 -13
- package/docs/webapi/doubleClick.mustache +0 -13
- package/docs/webapi/downloadFile.mustache +0 -12
- package/docs/webapi/dragAndDrop.mustache +0 -9
- package/docs/webapi/dragSlider.mustache +0 -11
- package/docs/webapi/executeAsyncScript.mustache +0 -24
- package/docs/webapi/executeScript.mustache +0 -26
- package/docs/webapi/fillField.mustache +0 -16
- package/docs/webapi/flushNetworkTraffics.mustache +0 -5
- package/docs/webapi/focus.mustache +0 -13
- package/docs/webapi/forceClick.mustache +0 -28
- package/docs/webapi/forceRightClick.mustache +0 -18
- package/docs/webapi/grabAllWindowHandles.mustache +0 -7
- package/docs/webapi/grabAttributeFrom.mustache +0 -10
- package/docs/webapi/grabAttributeFromAll.mustache +0 -9
- package/docs/webapi/grabBrowserLogs.mustache +0 -9
- package/docs/webapi/grabCookie.mustache +0 -11
- package/docs/webapi/grabCssPropertyFrom.mustache +0 -11
- package/docs/webapi/grabCssPropertyFromAll.mustache +0 -10
- package/docs/webapi/grabCurrentUrl.mustache +0 -9
- package/docs/webapi/grabCurrentWindowHandle.mustache +0 -6
- package/docs/webapi/grabDataFromPerformanceTiming.mustache +0 -20
- package/docs/webapi/grabElementBoundingRect.mustache +0 -20
- package/docs/webapi/grabGeoLocation.mustache +0 -8
- package/docs/webapi/grabHTMLFrom.mustache +0 -10
- package/docs/webapi/grabHTMLFromAll.mustache +0 -9
- package/docs/webapi/grabNumberOfOpenTabs.mustache +0 -8
- package/docs/webapi/grabNumberOfVisibleElements.mustache +0 -9
- package/docs/webapi/grabPageScrollPosition.mustache +0 -8
- package/docs/webapi/grabPopupText.mustache +0 -5
- package/docs/webapi/grabRecordedNetworkTraffics.mustache +0 -10
- package/docs/webapi/grabSource.mustache +0 -8
- package/docs/webapi/grabTextFrom.mustache +0 -10
- package/docs/webapi/grabTextFromAll.mustache +0 -9
- package/docs/webapi/grabTitle.mustache +0 -8
- package/docs/webapi/grabValueFrom.mustache +0 -9
- package/docs/webapi/grabValueFromAll.mustache +0 -8
- package/docs/webapi/grabWebElement.mustache +0 -9
- package/docs/webapi/grabWebElements.mustache +0 -9
- package/docs/webapi/moveCursorTo.mustache +0 -12
- package/docs/webapi/openNewTab.mustache +0 -7
- package/docs/webapi/pressKey.mustache +0 -12
- package/docs/webapi/pressKeyDown.mustache +0 -12
- package/docs/webapi/pressKeyUp.mustache +0 -12
- package/docs/webapi/pressKeyWithKeyNormalization.mustache +0 -60
- package/docs/webapi/refreshPage.mustache +0 -6
- package/docs/webapi/resizeWindow.mustache +0 -6
- package/docs/webapi/rightClick.mustache +0 -14
- package/docs/webapi/saveElementScreenshot.mustache +0 -10
- package/docs/webapi/saveScreenshot.mustache +0 -12
- package/docs/webapi/say.mustache +0 -10
- package/docs/webapi/scrollIntoView.mustache +0 -11
- package/docs/webapi/scrollPageToBottom.mustache +0 -6
- package/docs/webapi/scrollPageToTop.mustache +0 -6
- package/docs/webapi/scrollTo.mustache +0 -12
- package/docs/webapi/see.mustache +0 -11
- package/docs/webapi/seeAttributesOnElements.mustache +0 -9
- package/docs/webapi/seeCheckboxIsChecked.mustache +0 -10
- package/docs/webapi/seeCookie.mustache +0 -8
- package/docs/webapi/seeCssPropertiesOnElements.mustache +0 -9
- package/docs/webapi/seeCurrentPathEquals.mustache +0 -10
- package/docs/webapi/seeCurrentUrlEquals.mustache +0 -11
- package/docs/webapi/seeElement.mustache +0 -8
- package/docs/webapi/seeElementInDOM.mustache +0 -8
- package/docs/webapi/seeInCurrentUrl.mustache +0 -8
- package/docs/webapi/seeInField.mustache +0 -12
- package/docs/webapi/seeInPopup.mustache +0 -8
- package/docs/webapi/seeInSource.mustache +0 -7
- package/docs/webapi/seeInTitle.mustache +0 -8
- package/docs/webapi/seeNumberOfElements.mustache +0 -11
- package/docs/webapi/seeNumberOfVisibleElements.mustache +0 -10
- package/docs/webapi/seeTextEquals.mustache +0 -9
- package/docs/webapi/seeTitleEquals.mustache +0 -8
- package/docs/webapi/seeTraffic.mustache +0 -36
- package/docs/webapi/selectOption.mustache +0 -21
- package/docs/webapi/setCookie.mustache +0 -16
- package/docs/webapi/setGeoLocation.mustache +0 -12
- package/docs/webapi/startRecordingTraffic.mustache +0 -8
- package/docs/webapi/startRecordingWebSocketMessages.mustache +0 -8
- package/docs/webapi/stopRecordingTraffic.mustache +0 -5
- package/docs/webapi/stopRecordingWebSocketMessages.mustache +0 -7
- package/docs/webapi/switchTo.mustache +0 -9
- package/docs/webapi/switchToNextTab.mustache +0 -10
- package/docs/webapi/switchToPreviousTab.mustache +0 -10
- package/docs/webapi/type.mustache +0 -21
- package/docs/webapi/uncheckOption.mustache +0 -13
- package/docs/webapi/wait.mustache +0 -8
- package/docs/webapi/waitForClickable.mustache +0 -11
- package/docs/webapi/waitForCookie.mustache +0 -9
- package/docs/webapi/waitForDetached.mustache +0 -10
- package/docs/webapi/waitForDisabled.mustache +0 -6
- package/docs/webapi/waitForElement.mustache +0 -11
- package/docs/webapi/waitForEnabled.mustache +0 -6
- package/docs/webapi/waitForFunction.mustache +0 -17
- package/docs/webapi/waitForInvisible.mustache +0 -10
- package/docs/webapi/waitForNumberOfTabs.mustache +0 -9
- package/docs/webapi/waitForText.mustache +0 -13
- package/docs/webapi/waitForValue.mustache +0 -10
- package/docs/webapi/waitForVisible.mustache +0 -10
- package/docs/webapi/waitInUrl.mustache +0 -9
- package/docs/webapi/waitNumberOfVisibleElements.mustache +0 -10
- package/docs/webapi/waitToHide.mustache +0 -10
- package/docs/webapi/waitUrlEquals.mustache +0 -10
- package/lib/helper/AI.js +0 -214
- package/lib/listener/enhancedGlobalRetry.js +0 -110
- package/lib/plugin/enhancedRetryFailedStep.js +0 -99
- package/lib/plugin/htmlReporter.js +0 -3648
- package/lib/plugin/stepByStepReport.js +0 -427
- package/lib/plugin/subtitles.js +0 -89
- package/lib/retryCoordinator.js +0 -207
- package/typings/promiseBasedTypes.d.ts +0 -9469
- package/typings/types.d.ts +0 -11402
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
import crypto from 'crypto'
|
|
2
|
+
import fs from 'fs'
|
|
3
|
+
import path from 'path'
|
|
4
|
+
import { pathToFileURL } from 'url'
|
|
5
|
+
import Container from '../container.js'
|
|
6
|
+
import { clearString } from '../utils.js'
|
|
7
|
+
import { formatHtml } from '../html.js'
|
|
8
|
+
import { diffAriaSnapshots } from '../aria.js'
|
|
9
|
+
|
|
10
|
+
// ---------------------------------------------------------------------------
|
|
11
|
+
// Helper / directory naming
|
|
12
|
+
// ---------------------------------------------------------------------------
|
|
13
|
+
|
|
14
|
+
export function pickActingHelper(helpers) {
|
|
15
|
+
for (const name of Container.STANDARD_ACTING_HELPERS) {
|
|
16
|
+
if (helpers[name]) return helpers[name]
|
|
17
|
+
}
|
|
18
|
+
return null
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function traceDirFor(testFile, testTitle, baseDir) {
|
|
22
|
+
const hash = crypto.createHash('sha256').update((testFile || '') + (testTitle || '')).digest('hex').slice(0, 8)
|
|
23
|
+
const cleanTitle = clearString(testTitle || '').slice(0, 200)
|
|
24
|
+
return path.resolve(baseDir, `trace_${cleanTitle}_${hash}`)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function snapshotDirFor(baseDir) {
|
|
28
|
+
const hash = crypto.randomBytes(4).toString('hex')
|
|
29
|
+
return path.resolve(baseDir, `snapshot_${Date.now()}_${hash}`)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// ---------------------------------------------------------------------------
|
|
33
|
+
// Artifact link rendering (trace.md)
|
|
34
|
+
// ---------------------------------------------------------------------------
|
|
35
|
+
|
|
36
|
+
const ARTIFACT_LABELS = {
|
|
37
|
+
html: 'HTML',
|
|
38
|
+
aria: 'ARIA',
|
|
39
|
+
screenshot: 'Screenshot',
|
|
40
|
+
console: 'Browser Logs',
|
|
41
|
+
storage: 'Storage',
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function artifactLinks(artifacts, { indent = ' ', consoleCount } = {}) {
|
|
45
|
+
const lines = []
|
|
46
|
+
const order = ['html', 'aria', 'screenshot', 'console', 'storage']
|
|
47
|
+
|
|
48
|
+
for (const key of order) {
|
|
49
|
+
const file = artifacts[key]
|
|
50
|
+
if (!file) continue
|
|
51
|
+
const label = ARTIFACT_LABELS[key]
|
|
52
|
+
let line = `${indent}> [${label}](./${file})`
|
|
53
|
+
if (key === 'console') {
|
|
54
|
+
const count = consoleCount ?? artifacts.consoleCount ?? 0
|
|
55
|
+
line += ` (${count} entries)`
|
|
56
|
+
} else if (key === 'storage') {
|
|
57
|
+
const cookies = artifacts.cookieCount ?? 0
|
|
58
|
+
const ls = artifacts.localStorageCount ?? 0
|
|
59
|
+
line += ` (${cookies} cookies, ${ls} localStorage)`
|
|
60
|
+
}
|
|
61
|
+
lines.push(line)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return lines.join('\n')
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export function fileToUrl(dir, basename) {
|
|
68
|
+
return pathToFileURL(path.join(dir, basename)).href
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export function writeTraceMarkdown({ dir, title, file, durationMs, commands, captured, error }) {
|
|
72
|
+
let md = `file: ${file || 'mcp'}\n`
|
|
73
|
+
md += `name: ${title}\n`
|
|
74
|
+
md += `time: ${(durationMs / 1000).toFixed(2)}s\n`
|
|
75
|
+
md += `---\n\n`
|
|
76
|
+
|
|
77
|
+
if (error) md += `Error: ${error}\n\n---\n\n`
|
|
78
|
+
|
|
79
|
+
if (commands && commands.length) {
|
|
80
|
+
md += `### Commands\n`
|
|
81
|
+
for (const c of commands) md += `- ${c}\n`
|
|
82
|
+
md += `\n`
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
md += `### Final State\n`
|
|
86
|
+
if (captured.url) md += ` > URL: ${captured.url}\n`
|
|
87
|
+
const links = artifactLinks(captured)
|
|
88
|
+
if (links) md += links + '\n'
|
|
89
|
+
|
|
90
|
+
const traceFile = path.join(dir, 'trace.md')
|
|
91
|
+
fs.writeFileSync(traceFile, md)
|
|
92
|
+
return traceFile
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export function artifactsToFileUrls(captured, dir) {
|
|
96
|
+
const out = {}
|
|
97
|
+
if (captured.url) out.url = captured.url
|
|
98
|
+
if (captured.screenshot) out.screenshot = fileToUrl(dir, captured.screenshot)
|
|
99
|
+
if (captured.html) out.html = fileToUrl(dir, captured.html)
|
|
100
|
+
if (captured.aria) out.aria = fileToUrl(dir, captured.aria)
|
|
101
|
+
if (captured.console) out.console = fileToUrl(dir, captured.console)
|
|
102
|
+
if (captured.storage) out.storage = fileToUrl(dir, captured.storage)
|
|
103
|
+
if (typeof captured.consoleCount === 'number') out.consoleCount = captured.consoleCount
|
|
104
|
+
if (typeof captured.cookieCount === 'number') out.cookieCount = captured.cookieCount
|
|
105
|
+
if (typeof captured.localStorageCount === 'number') out.localStorageCount = captured.localStorageCount
|
|
106
|
+
return out
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// ---------------------------------------------------------------------------
|
|
110
|
+
// Snapshot capture (HTML / ARIA / screenshot / console / storage)
|
|
111
|
+
// ---------------------------------------------------------------------------
|
|
112
|
+
|
|
113
|
+
function normalizeBrowserLogs(logs) {
|
|
114
|
+
return (logs || []).map(l => {
|
|
115
|
+
if (typeof l === 'string') return l
|
|
116
|
+
if (l && typeof l.type === 'function' && typeof l.text === 'function') {
|
|
117
|
+
return { type: l.type(), text: l.text() }
|
|
118
|
+
}
|
|
119
|
+
return l
|
|
120
|
+
})
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
async function captureStorageState(helper) {
|
|
124
|
+
if (typeof helper.grabStorageState === 'function') {
|
|
125
|
+
try {
|
|
126
|
+
const state = await helper.grabStorageState()
|
|
127
|
+
if (state) return state
|
|
128
|
+
} catch {}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const state = { cookies: [], origins: [] }
|
|
132
|
+
|
|
133
|
+
if (typeof helper.grabCookie === 'function') {
|
|
134
|
+
try {
|
|
135
|
+
const cookies = await helper.grabCookie()
|
|
136
|
+
if (Array.isArray(cookies)) state.cookies = cookies
|
|
137
|
+
} catch {}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
if (typeof helper.executeScript === 'function') {
|
|
141
|
+
try {
|
|
142
|
+
const result = await helper.executeScript(() => {
|
|
143
|
+
const out = { origin: location.origin, items: [] }
|
|
144
|
+
for (let i = 0; i < localStorage.length; i++) {
|
|
145
|
+
const name = localStorage.key(i)
|
|
146
|
+
out.items.push({ name, value: localStorage.getItem(name) })
|
|
147
|
+
}
|
|
148
|
+
return out
|
|
149
|
+
})
|
|
150
|
+
if (result?.items?.length) {
|
|
151
|
+
state.origins.push({ origin: result.origin, localStorage: result.items })
|
|
152
|
+
}
|
|
153
|
+
} catch {}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
return state
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
export async function captureSnapshot(helper, {
|
|
160
|
+
dir,
|
|
161
|
+
prefix = 'snapshot',
|
|
162
|
+
fullPage = false,
|
|
163
|
+
captureURL = true,
|
|
164
|
+
captureScreenshot = true,
|
|
165
|
+
captureHTML = true,
|
|
166
|
+
captureARIA = true,
|
|
167
|
+
captureBrowserLogs = true,
|
|
168
|
+
captureStorage = true,
|
|
169
|
+
} = {}) {
|
|
170
|
+
if (!helper) return {}
|
|
171
|
+
const out = {}
|
|
172
|
+
|
|
173
|
+
if (captureURL) {
|
|
174
|
+
try {
|
|
175
|
+
if (helper.grabCurrentUrl) out.url = await helper.grabCurrentUrl()
|
|
176
|
+
} catch {}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
if (captureScreenshot && helper.saveScreenshot) {
|
|
180
|
+
try {
|
|
181
|
+
const file = `${prefix}_screenshot.png`
|
|
182
|
+
await helper.saveScreenshot(path.join(dir, file), fullPage)
|
|
183
|
+
out.screenshot = file
|
|
184
|
+
} catch {}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
if (captureHTML && helper.grabSource) {
|
|
188
|
+
try {
|
|
189
|
+
const html = await helper.grabSource()
|
|
190
|
+
// Universal funnel: every captured HTML snapshot flows through formatHtml
|
|
191
|
+
// (minify -> cleanHtml -> beautify). Don't add direct grabSource->writeFile
|
|
192
|
+
// paths elsewhere; route through this util so trash-class cleanup stays
|
|
193
|
+
// consistent across aiTrace, pageInfo, and MCP tools.
|
|
194
|
+
const formatted = await formatHtml(html)
|
|
195
|
+
const file = `${prefix}_page.html`
|
|
196
|
+
fs.writeFileSync(path.join(dir, file), formatted)
|
|
197
|
+
out.html = file
|
|
198
|
+
// Expose pre-cleanup HTML for consumers that need to inspect classes
|
|
199
|
+
// stripped by cleanHtml (e.g. pageInfo's error-class scan).
|
|
200
|
+
out.htmlRaw = html
|
|
201
|
+
} catch {}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
if (captureARIA && helper.grabAriaSnapshot) {
|
|
205
|
+
try {
|
|
206
|
+
const aria = await helper.grabAriaSnapshot()
|
|
207
|
+
const file = `${prefix}_aria.txt`
|
|
208
|
+
fs.writeFileSync(path.join(dir, file), aria)
|
|
209
|
+
out.aria = file
|
|
210
|
+
} catch {}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
if (captureBrowserLogs && helper.grabBrowserLogs) {
|
|
214
|
+
try {
|
|
215
|
+
const logs = await helper.grabBrowserLogs()
|
|
216
|
+
const normalized = normalizeBrowserLogs(logs)
|
|
217
|
+
const file = `${prefix}_console.json`
|
|
218
|
+
fs.writeFileSync(path.join(dir, file), JSON.stringify(normalized, null, 2))
|
|
219
|
+
out.console = file
|
|
220
|
+
out.consoleCount = normalized.length
|
|
221
|
+
} catch {}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
if (captureStorage) {
|
|
225
|
+
try {
|
|
226
|
+
const state = await captureStorageState(helper)
|
|
227
|
+
const cookieCount = state.cookies?.length || 0
|
|
228
|
+
const localStorageCount = (state.origins || [])
|
|
229
|
+
.reduce((sum, o) => sum + (o.localStorage?.length || 0), 0)
|
|
230
|
+
if (cookieCount || localStorageCount) {
|
|
231
|
+
const file = `${prefix}_storage.json`
|
|
232
|
+
fs.writeFileSync(path.join(dir, file), JSON.stringify(state, null, 2))
|
|
233
|
+
out.storage = file
|
|
234
|
+
out.cookieCount = cookieCount
|
|
235
|
+
out.localStorageCount = localStorageCount
|
|
236
|
+
}
|
|
237
|
+
} catch {}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
return out
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// ---------------------------------------------------------------------------
|
|
244
|
+
// TraceReader — read artifacts already on disk (written by aiTrace, MCP, etc.)
|
|
245
|
+
// ---------------------------------------------------------------------------
|
|
246
|
+
|
|
247
|
+
const KIND_SUFFIX = {
|
|
248
|
+
aria: '_aria.txt',
|
|
249
|
+
html: '_page.html',
|
|
250
|
+
screenshot: '_screenshot.png',
|
|
251
|
+
console: '_console.json',
|
|
252
|
+
storage: '_storage.json',
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
export class TraceReader {
|
|
256
|
+
constructor(dir) {
|
|
257
|
+
this.dir = dir
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// Filenames of a given kind, sorted in capture order. aiTrace prefixes with
|
|
261
|
+
// a zero-padded step index (`0000_`, `0001_`...), so a lexical sort is
|
|
262
|
+
// chronological.
|
|
263
|
+
list(kind) {
|
|
264
|
+
const suffix = KIND_SUFFIX[kind]
|
|
265
|
+
if (!suffix || !this.dir || !fs.existsSync(this.dir)) return []
|
|
266
|
+
let entries
|
|
267
|
+
try { entries = fs.readdirSync(this.dir) } catch { return [] }
|
|
268
|
+
return entries.filter(f => f.endsWith(suffix)).sort()
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// Path of the n-th file of `kind`, or null. Python-style indexing:
|
|
272
|
+
// 0..N-1 from the start, -1..-N from the end.
|
|
273
|
+
pathAt(n, kind) {
|
|
274
|
+
const files = this.list(kind)
|
|
275
|
+
if (!files.length) return null
|
|
276
|
+
const i = n < 0 ? files.length + n : n
|
|
277
|
+
if (i < 0 || i >= files.length) return null
|
|
278
|
+
return path.join(this.dir, files[i])
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
// Read content of the n-th file of `kind`. Binary kinds (screenshot) are
|
|
282
|
+
// returned as Buffer; text kinds as utf8 string.
|
|
283
|
+
nth(n, kind) {
|
|
284
|
+
const p = this.pathAt(n, kind)
|
|
285
|
+
if (!p) return null
|
|
286
|
+
try {
|
|
287
|
+
if (kind === 'screenshot') return fs.readFileSync(p)
|
|
288
|
+
return fs.readFileSync(p, 'utf8')
|
|
289
|
+
} catch { return null }
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
first(kind) { return this.nth(0, kind) }
|
|
293
|
+
last(kind) { return this.nth(-1, kind) }
|
|
294
|
+
count(kind) { return this.list(kind).length }
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
export const ariaDiff = diffAriaSnapshots
|
package/lib/utils.js
CHANGED
|
@@ -7,6 +7,7 @@ import getFunctionArguments from 'fn-args'
|
|
|
7
7
|
import deepClone from 'lodash.clonedeep'
|
|
8
8
|
import merge from 'lodash.merge'
|
|
9
9
|
import { convertColorToRGBA, isColorProperty } from './colorUtils.js'
|
|
10
|
+
import store from './store.js'
|
|
10
11
|
import Fuse from 'fuse.js'
|
|
11
12
|
import crypto from 'crypto'
|
|
12
13
|
import jsBeautify from 'js-beautify'
|
|
@@ -150,6 +151,24 @@ export const decodeUrl = function (url) {
|
|
|
150
151
|
return decodeURIComponent(decodeURIComponent(decodeURIComponent(url)))
|
|
151
152
|
}
|
|
152
153
|
|
|
154
|
+
export const normalizePath = function (path) {
|
|
155
|
+
if (path === '' || path === '/') return '/'
|
|
156
|
+
return path
|
|
157
|
+
.replace(/\/+/g, '/')
|
|
158
|
+
.replace(/\/$/, '') || '/'
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
export const resolveUrl = function (url, baseUrl) {
|
|
162
|
+
if (!url) return url
|
|
163
|
+
if (url.indexOf('http') === 0) return url
|
|
164
|
+
if (!baseUrl) return url
|
|
165
|
+
try {
|
|
166
|
+
return new URL(url, baseUrl).href
|
|
167
|
+
} catch (e) {
|
|
168
|
+
return url
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
153
172
|
export const xpathLocator = {
|
|
154
173
|
/**
|
|
155
174
|
* @param {string} string
|
|
@@ -317,13 +336,13 @@ export const screenshotOutputFolder = function (fileName) {
|
|
|
317
336
|
const fileSep = path.sep
|
|
318
337
|
|
|
319
338
|
if (!fileName.includes(fileSep) || fileName.includes('record_')) {
|
|
320
|
-
return path.resolve(
|
|
339
|
+
return path.resolve(store.outputDir, fileName)
|
|
321
340
|
}
|
|
322
|
-
return path.resolve(
|
|
341
|
+
return path.resolve(store.codeceptDir, fileName)
|
|
323
342
|
}
|
|
324
343
|
|
|
325
344
|
export const relativeDir = function (fileName) {
|
|
326
|
-
return fileName.replace(
|
|
345
|
+
return fileName.replace(store.codeceptDir, '').replace(/^\//, '')
|
|
327
346
|
}
|
|
328
347
|
|
|
329
348
|
export const beautify = function (code) {
|
|
@@ -598,6 +617,12 @@ function createCircularSafeReplacer(keysToSkip = []) {
|
|
|
598
617
|
return undefined
|
|
599
618
|
}
|
|
600
619
|
|
|
620
|
+
// Coerce types that JSON.stringify can't handle natively
|
|
621
|
+
if (typeof value === 'function') return `[Function: ${value.name || 'anonymous'}]`
|
|
622
|
+
if (typeof value === 'bigint') return `${value.toString()}n`
|
|
623
|
+
if (typeof value === 'symbol') return value.toString()
|
|
624
|
+
if (value instanceof Error) return { name: value.name, message: value.message, stack: value.stack }
|
|
625
|
+
|
|
601
626
|
if (value === null || typeof value !== 'object') {
|
|
602
627
|
return value
|
|
603
628
|
}
|
|
@@ -628,6 +653,25 @@ export const safeStringify = function (obj, keysToSkip = [], space = 0) {
|
|
|
628
653
|
}
|
|
629
654
|
}
|
|
630
655
|
|
|
656
|
+
/**
|
|
657
|
+
* Truncate a string at a byte cap, returning structured info.
|
|
658
|
+
* @param {string} str
|
|
659
|
+
* @param {number} maxBytes
|
|
660
|
+
* @returns {{ value: string, truncated: boolean, fullLength: number }}
|
|
661
|
+
*/
|
|
662
|
+
export const truncateString = function (str, maxBytes) {
|
|
663
|
+
if (typeof str !== 'string') str = String(str)
|
|
664
|
+
if (str.length <= maxBytes) {
|
|
665
|
+
return { value: str, truncated: false, fullLength: str.length }
|
|
666
|
+
}
|
|
667
|
+
const dropped = str.length - maxBytes
|
|
668
|
+
return {
|
|
669
|
+
value: `${str.slice(0, maxBytes)}\n...[truncated ${dropped} more chars]`,
|
|
670
|
+
truncated: true,
|
|
671
|
+
fullLength: str.length,
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
|
|
631
675
|
export const serializeError = function (error) {
|
|
632
676
|
if (error) {
|
|
633
677
|
const { stack, uncaught, message, actual, expected } = error
|
|
@@ -640,6 +684,36 @@ export const base64EncodeFile = function (filePath) {
|
|
|
640
684
|
return Buffer.from(fs.readFileSync(filePath)).toString('base64')
|
|
641
685
|
}
|
|
642
686
|
|
|
687
|
+
export const getMimeType = function (fileName) {
|
|
688
|
+
const ext = path.extname(fileName).toLowerCase()
|
|
689
|
+
const mimeTypes = {
|
|
690
|
+
'.jpg': 'image/jpeg',
|
|
691
|
+
'.jpeg': 'image/jpeg',
|
|
692
|
+
'.png': 'image/png',
|
|
693
|
+
'.gif': 'image/gif',
|
|
694
|
+
'.bmp': 'image/bmp',
|
|
695
|
+
'.svg': 'image/svg+xml',
|
|
696
|
+
'.webp': 'image/webp',
|
|
697
|
+
'.pdf': 'application/pdf',
|
|
698
|
+
'.txt': 'text/plain',
|
|
699
|
+
'.html': 'text/html',
|
|
700
|
+
'.css': 'text/css',
|
|
701
|
+
'.js': 'application/javascript',
|
|
702
|
+
'.json': 'application/json',
|
|
703
|
+
'.xml': 'application/xml',
|
|
704
|
+
'.zip': 'application/zip',
|
|
705
|
+
'.csv': 'text/csv',
|
|
706
|
+
'.doc': 'application/msword',
|
|
707
|
+
'.docx': 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
|
708
|
+
'.xls': 'application/vnd.ms-excel',
|
|
709
|
+
'.xlsx': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
|
710
|
+
'.mp3': 'audio/mpeg',
|
|
711
|
+
'.mp4': 'video/mp4',
|
|
712
|
+
'.wav': 'audio/wav',
|
|
713
|
+
}
|
|
714
|
+
return mimeTypes[ext] || 'application/octet-stream'
|
|
715
|
+
}
|
|
716
|
+
|
|
643
717
|
export const markdownToAnsi = function (markdown) {
|
|
644
718
|
return (
|
|
645
719
|
markdown
|
package/lib/workers.js
CHANGED
|
@@ -20,6 +20,7 @@ import event from './event.js'
|
|
|
20
20
|
import { deserializeTest } from './mocha/test.js'
|
|
21
21
|
import { deserializeSuite } from './mocha/suite.js'
|
|
22
22
|
import recorder from './recorder.js'
|
|
23
|
+
import store from './store.js'
|
|
23
24
|
import runHook from './hooks.js'
|
|
24
25
|
import WorkerStorage from './workerStorage.js'
|
|
25
26
|
import { createRuns } from './command/run-multiple/collection.js'
|
|
@@ -28,7 +29,7 @@ const pathToWorker = path.join(__dirname, 'command', 'workers', 'runTests.js')
|
|
|
28
29
|
|
|
29
30
|
const initializeCodecept = async (configPath, options = {}) => {
|
|
30
31
|
const config = await mainConfig.load(configPath || '.')
|
|
31
|
-
const codecept = new Codecept(config, options)
|
|
32
|
+
const codecept = new Codecept(config, { ...options, skipDefaultListeners: true })
|
|
32
33
|
await codecept.init(getTestRoot(configPath))
|
|
33
34
|
codecept.loadTests()
|
|
34
35
|
|
|
@@ -504,6 +505,7 @@ class Workers extends EventEmitter {
|
|
|
504
505
|
await this._ensureInitialized()
|
|
505
506
|
recorder.startUnlessRunning()
|
|
506
507
|
event.dispatcher.emit(event.workers.before)
|
|
508
|
+
store.workerMode = true
|
|
507
509
|
process.env.RUNS_WITH_WORKERS = 'true'
|
|
508
510
|
|
|
509
511
|
// Create workers and set up message handlers immediately (not in recorder queue)
|
|
@@ -519,22 +521,8 @@ class Workers extends EventEmitter {
|
|
|
519
521
|
// Workers are already running, this is just a placeholder step
|
|
520
522
|
})
|
|
521
523
|
|
|
522
|
-
// Add overall timeout to prevent infinite hanging
|
|
523
|
-
const overallTimeout = setTimeout(() => {
|
|
524
|
-
console.error('[Main] Overall timeout reached (10 minutes). Force terminating remaining workers...')
|
|
525
|
-
workerThreads.forEach(w => {
|
|
526
|
-
try {
|
|
527
|
-
w.terminate()
|
|
528
|
-
} catch (e) {
|
|
529
|
-
// ignore
|
|
530
|
-
}
|
|
531
|
-
})
|
|
532
|
-
this._finishRun()
|
|
533
|
-
}, 600000) // 10 minutes
|
|
534
|
-
|
|
535
524
|
return new Promise(resolve => {
|
|
536
525
|
this.on('end', () => {
|
|
537
|
-
clearTimeout(overallTimeout)
|
|
538
526
|
resolve()
|
|
539
527
|
})
|
|
540
528
|
})
|
|
@@ -563,7 +551,7 @@ class Workers extends EventEmitter {
|
|
|
563
551
|
// Track last activity time to detect hanging workers
|
|
564
552
|
let lastActivity = Date.now()
|
|
565
553
|
let currentTest = null
|
|
566
|
-
const workerTimeout =
|
|
554
|
+
const workerTimeout = process.env.CODECEPT_WORKER_TIMEOUT ? ms(process.env.CODECEPT_WORKER_TIMEOUT) : ms('5m')
|
|
567
555
|
|
|
568
556
|
const timeoutChecker = setInterval(() => {
|
|
569
557
|
const elapsed = Date.now() - lastActivity
|
|
@@ -625,13 +613,32 @@ class Workers extends EventEmitter {
|
|
|
625
613
|
|
|
626
614
|
break
|
|
627
615
|
case event.suite.before:
|
|
628
|
-
|
|
616
|
+
{
|
|
617
|
+
const suite = deserializeSuite(message.data)
|
|
618
|
+
this.emit(event.suite.before, suite)
|
|
619
|
+
event.dispatcher.emit(event.suite.before, suite)
|
|
620
|
+
}
|
|
621
|
+
break
|
|
622
|
+
case event.suite.after:
|
|
623
|
+
{
|
|
624
|
+
const suite = deserializeSuite(message.data)
|
|
625
|
+
this.emit(event.suite.after, suite)
|
|
626
|
+
event.dispatcher.emit(event.suite.after, suite)
|
|
627
|
+
}
|
|
629
628
|
break
|
|
630
629
|
case event.test.before:
|
|
631
|
-
|
|
630
|
+
{
|
|
631
|
+
const test = deserializeTest(message.data)
|
|
632
|
+
this.emit(event.test.before, test)
|
|
633
|
+
event.dispatcher.emit(event.test.before, test)
|
|
634
|
+
}
|
|
632
635
|
break
|
|
633
636
|
case event.test.started:
|
|
634
|
-
|
|
637
|
+
{
|
|
638
|
+
const test = deserializeTest(message.data)
|
|
639
|
+
this.emit(event.test.started, test)
|
|
640
|
+
event.dispatcher.emit(event.test.started, test)
|
|
641
|
+
}
|
|
635
642
|
break
|
|
636
643
|
case event.test.failed:
|
|
637
644
|
// For hook failures, emit immediately as there won't be a test.finished event
|
|
@@ -645,7 +652,11 @@ class Workers extends EventEmitter {
|
|
|
645
652
|
// Skip individual passed events - we'll emit based on finished state
|
|
646
653
|
break
|
|
647
654
|
case event.test.skipped:
|
|
648
|
-
|
|
655
|
+
{
|
|
656
|
+
const test = deserializeTest(message.data)
|
|
657
|
+
this.emit(event.test.skipped, test)
|
|
658
|
+
event.dispatcher.emit(event.test.skipped, test)
|
|
659
|
+
}
|
|
649
660
|
break
|
|
650
661
|
case event.test.finished:
|
|
651
662
|
// Handle different types of test completion properly
|
|
@@ -674,28 +685,47 @@ class Workers extends EventEmitter {
|
|
|
674
685
|
}
|
|
675
686
|
}
|
|
676
687
|
|
|
677
|
-
|
|
688
|
+
const test = deserializeTest(data)
|
|
689
|
+
this.emit(event.test.finished, test)
|
|
690
|
+
event.dispatcher.emit(event.test.finished, test)
|
|
678
691
|
}
|
|
679
692
|
break
|
|
680
693
|
case event.test.after:
|
|
681
|
-
|
|
694
|
+
{
|
|
695
|
+
const test = deserializeTest(message.data)
|
|
696
|
+
this.emit(event.test.after, test)
|
|
697
|
+
event.dispatcher.emit(event.test.after, test)
|
|
698
|
+
}
|
|
682
699
|
break
|
|
683
700
|
case event.step.finished:
|
|
684
701
|
this.emit(event.step.finished, message.data)
|
|
702
|
+
event.dispatcher.emit(event.step.finished, message.data)
|
|
685
703
|
break
|
|
686
704
|
case event.step.started:
|
|
687
705
|
this.emit(event.step.started, message.data)
|
|
706
|
+
event.dispatcher.emit(event.step.started, message.data)
|
|
688
707
|
break
|
|
689
708
|
case event.step.passed:
|
|
690
709
|
this.emit(event.step.passed, message.data)
|
|
710
|
+
event.dispatcher.emit(event.step.passed, message.data)
|
|
691
711
|
break
|
|
692
712
|
case event.step.failed:
|
|
693
713
|
this.emit(event.step.failed, message.data, message.data.error)
|
|
714
|
+
event.dispatcher.emit(event.step.failed, message.data, message.data.error)
|
|
694
715
|
break
|
|
695
716
|
case event.hook.failed:
|
|
696
717
|
// Hook failures are already reported as test failures by the worker
|
|
697
718
|
// Just emit the hook.failed event for listeners
|
|
698
719
|
this.emit(event.hook.failed, message.data)
|
|
720
|
+
event.dispatcher.emit(event.hook.failed, message.data)
|
|
721
|
+
break
|
|
722
|
+
case event.hook.passed:
|
|
723
|
+
this.emit(event.hook.passed, message.data)
|
|
724
|
+
event.dispatcher.emit(event.hook.passed, message.data)
|
|
725
|
+
break
|
|
726
|
+
case event.hook.finished:
|
|
727
|
+
this.emit(event.hook.finished, message.data)
|
|
728
|
+
event.dispatcher.emit(event.hook.finished, message.data)
|
|
699
729
|
break
|
|
700
730
|
}
|
|
701
731
|
})
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codeceptjs",
|
|
3
|
-
"version": "4.0.0-rc.
|
|
3
|
+
"version": "4.0.0-rc.20",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Supercharged End 2 End Testing Framework for NodeJS",
|
|
6
6
|
"keywords": [
|
|
@@ -26,7 +26,8 @@
|
|
|
26
26
|
"lib",
|
|
27
27
|
"translations",
|
|
28
28
|
"typings/**/*.d.ts",
|
|
29
|
-
"docs
|
|
29
|
+
"docs/*.md",
|
|
30
|
+
"docs/helpers/**"
|
|
30
31
|
],
|
|
31
32
|
"main": "lib/index.js",
|
|
32
33
|
"module": "lib/index.js",
|
|
@@ -41,12 +42,15 @@
|
|
|
41
42
|
"./els": "./lib/els.js",
|
|
42
43
|
"./effects": "./lib/effects.js",
|
|
43
44
|
"./steps": "./lib/steps.js",
|
|
44
|
-
"./store": "./lib/store.js"
|
|
45
|
+
"./store": "./lib/store.js",
|
|
46
|
+
"./assertions": "./lib/assertions.js"
|
|
45
47
|
},
|
|
46
48
|
"bin": {
|
|
47
|
-
"codeceptjs": "./bin/codecept.js"
|
|
49
|
+
"codeceptjs": "./bin/codecept.js",
|
|
50
|
+
"codeceptjs-mcp": "./bin/mcp-server.js",
|
|
51
|
+
"codeceptq": "./bin/codeceptq.js"
|
|
48
52
|
},
|
|
49
|
-
"repository": "
|
|
53
|
+
"repository": "codeceptjs/CodeceptJS",
|
|
50
54
|
"scripts": {
|
|
51
55
|
"test-server": "node bin/test-server.js test/data/rest/db.json --host 0.0.0.0 -p 8010 --read-only",
|
|
52
56
|
"test-server:writable": "node bin/test-server.js test/data/rest/db.json --host 0.0.0.0 -p 8010",
|
|
@@ -85,11 +89,12 @@
|
|
|
85
89
|
"publish-beta": "./runok.cjs publish:next-beta-version"
|
|
86
90
|
},
|
|
87
91
|
"dependencies": {
|
|
88
|
-
"@codeceptjs/configure": "
|
|
92
|
+
"@codeceptjs/configure": "^4.0.0-beta.4",
|
|
89
93
|
"@codeceptjs/helper": "2.0.4",
|
|
90
94
|
"@cucumber/cucumber-expressions": "18",
|
|
91
95
|
"@cucumber/gherkin": "38.0.0",
|
|
92
96
|
"@cucumber/messages": "32.0.1",
|
|
97
|
+
"@modelcontextprotocol/sdk": "^1.26.0",
|
|
93
98
|
"@xmldom/xmldom": "0.9.8",
|
|
94
99
|
"acorn": "8.15.0",
|
|
95
100
|
"ai": "^6.0.43",
|
|
@@ -112,7 +117,6 @@
|
|
|
112
117
|
"html-minifier-terser": "7.2.0",
|
|
113
118
|
"inquirer": "^8.2.7",
|
|
114
119
|
"invisi-data": "^1.0.0",
|
|
115
|
-
"joi": "18.0.2",
|
|
116
120
|
"js-beautify": "1.15.4",
|
|
117
121
|
"lodash.clonedeep": "4.5.0",
|
|
118
122
|
"lodash.merge": "4.6.2",
|
|
@@ -128,14 +132,16 @@
|
|
|
128
132
|
"promise-retry": "1.1.1",
|
|
129
133
|
"resq": "1.11.0",
|
|
130
134
|
"sprintf-js": "1.1.3",
|
|
131
|
-
"uuid": "11.1.0"
|
|
135
|
+
"uuid": "11.1.0",
|
|
136
|
+
"xpath": "0.0.34",
|
|
137
|
+
"zod": "^4.1.11"
|
|
132
138
|
},
|
|
133
139
|
"optionalDependencies": {
|
|
134
140
|
"@codeceptjs/detox-helper": "1.1.13"
|
|
135
141
|
},
|
|
136
142
|
"devDependencies": {
|
|
137
143
|
"@apollo/server": "^5",
|
|
138
|
-
"@codeceptjs/expect-helper": "^
|
|
144
|
+
"@codeceptjs/expect-helper": "^4.0.0-beta.5",
|
|
139
145
|
"@codeceptjs/mock-request": "0.3.1",
|
|
140
146
|
"@eslint/eslintrc": "3.3.3",
|
|
141
147
|
"@eslint/js": "9.39.2",
|
|
@@ -170,7 +176,7 @@
|
|
|
170
176
|
"jsdoc-typeof-plugin": "1.0.0",
|
|
171
177
|
"json-server": "0.17.4",
|
|
172
178
|
"mochawesome": "^7.1.3",
|
|
173
|
-
"playwright": "1.
|
|
179
|
+
"playwright": "^1.59.0",
|
|
174
180
|
"prettier": "^3.3.2",
|
|
175
181
|
"puppeteer": "24.36.0",
|
|
176
182
|
"qrcode-terminal": "0.12.0",
|
|
@@ -189,8 +195,7 @@
|
|
|
189
195
|
"typescript": "5.9.3",
|
|
190
196
|
"wdio-docker-service": "3.2.1",
|
|
191
197
|
"webdriverio": "9.23.0",
|
|
192
|
-
"xml2js": "0.6.2"
|
|
193
|
-
"xpath": "0.0.34"
|
|
198
|
+
"xml2js": "0.6.2"
|
|
194
199
|
},
|
|
195
200
|
"peerDependencies": {
|
|
196
201
|
"tsx": "^4.0.0"
|
|
@@ -212,6 +217,7 @@
|
|
|
212
217
|
}
|
|
213
218
|
},
|
|
214
219
|
"overrides": {
|
|
215
|
-
"tmp": "0.2.5"
|
|
220
|
+
"tmp": "0.2.5",
|
|
221
|
+
"js-yaml": "^4.1.1"
|
|
216
222
|
}
|
|
217
223
|
}
|