codeceptjs 4.0.0-beta.2 → 4.0.0-beta.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 +133 -120
- package/bin/codecept.js +107 -96
- package/bin/test-server.js +64 -0
- package/docs/webapi/clearCookie.mustache +1 -1
- package/docs/webapi/click.mustache +5 -1
- package/lib/actor.js +71 -103
- package/lib/ai.js +159 -188
- package/lib/assert/empty.js +22 -24
- package/lib/assert/equal.js +30 -37
- package/lib/assert/error.js +14 -14
- package/lib/assert/include.js +43 -48
- package/lib/assert/throws.js +11 -11
- package/lib/assert/truth.js +22 -22
- package/lib/assert.js +20 -18
- package/lib/codecept.js +262 -162
- package/lib/colorUtils.js +50 -52
- package/lib/command/check.js +206 -0
- package/lib/command/configMigrate.js +56 -51
- package/lib/command/definitions.js +96 -109
- package/lib/command/dryRun.js +77 -79
- package/lib/command/generate.js +234 -194
- package/lib/command/gherkin/init.js +42 -33
- package/lib/command/gherkin/snippets.js +76 -74
- package/lib/command/gherkin/steps.js +20 -17
- package/lib/command/info.js +74 -38
- package/lib/command/init.js +301 -290
- package/lib/command/interactive.js +41 -32
- package/lib/command/list.js +28 -27
- package/lib/command/run-multiple/chunk.js +51 -48
- package/lib/command/run-multiple/collection.js +5 -5
- package/lib/command/run-multiple/run.js +5 -1
- package/lib/command/run-multiple.js +97 -97
- package/lib/command/run-rerun.js +19 -25
- package/lib/command/run-workers.js +68 -92
- package/lib/command/run.js +39 -27
- package/lib/command/utils.js +80 -64
- package/lib/command/workers/runTests.js +388 -226
- package/lib/config.js +109 -50
- package/lib/container.js +641 -261
- package/lib/data/context.js +60 -61
- package/lib/data/dataScenarioConfig.js +47 -47
- package/lib/data/dataTableArgument.js +32 -32
- package/lib/data/table.js +22 -22
- package/lib/effects.js +307 -0
- package/lib/element/WebElement.js +327 -0
- package/lib/els.js +160 -0
- package/lib/event.js +173 -163
- package/lib/globals.js +141 -0
- package/lib/heal.js +89 -85
- package/lib/helper/AI.js +131 -41
- package/lib/helper/ApiDataFactory.js +107 -75
- package/lib/helper/Appium.js +542 -404
- package/lib/helper/FileSystem.js +100 -79
- package/lib/helper/GraphQL.js +44 -43
- package/lib/helper/GraphQLDataFactory.js +52 -52
- package/lib/helper/JSONResponse.js +126 -88
- package/lib/helper/Mochawesome.js +54 -29
- package/lib/helper/Playwright.js +2547 -1316
- package/lib/helper/Puppeteer.js +1578 -1181
- package/lib/helper/REST.js +209 -68
- package/lib/helper/WebDriver.js +1482 -1342
- package/lib/helper/errors/ConnectionRefused.js +6 -6
- package/lib/helper/errors/ElementAssertion.js +11 -16
- package/lib/helper/errors/ElementNotFound.js +5 -9
- package/lib/helper/errors/RemoteBrowserConnectionRefused.js +5 -5
- package/lib/helper/extras/Console.js +11 -11
- package/lib/helper/extras/PlaywrightLocator.js +110 -0
- package/lib/helper/extras/PlaywrightPropEngine.js +18 -18
- package/lib/helper/extras/PlaywrightReactVueLocator.js +17 -8
- package/lib/helper/extras/PlaywrightRestartOpts.js +25 -11
- package/lib/helper/extras/Popup.js +22 -22
- package/lib/helper/extras/React.js +27 -28
- package/lib/helper/network/actions.js +36 -42
- package/lib/helper/network/utils.js +78 -84
- package/lib/helper/scripts/blurElement.js +5 -5
- package/lib/helper/scripts/focusElement.js +5 -5
- package/lib/helper/scripts/highlightElement.js +8 -8
- package/lib/helper/scripts/isElementClickable.js +34 -34
- package/lib/helper.js +2 -3
- package/lib/history.js +23 -19
- package/lib/hooks.js +8 -8
- package/lib/html.js +94 -104
- package/lib/index.js +38 -27
- package/lib/listener/config.js +30 -23
- package/lib/listener/emptyRun.js +54 -0
- package/lib/listener/enhancedGlobalRetry.js +110 -0
- package/lib/listener/exit.js +16 -18
- package/lib/listener/globalRetry.js +70 -0
- package/lib/listener/globalTimeout.js +181 -0
- package/lib/listener/helpers.js +76 -51
- package/lib/listener/mocha.js +10 -11
- package/lib/listener/result.js +11 -0
- package/lib/listener/retryEnhancer.js +85 -0
- package/lib/listener/steps.js +71 -59
- package/lib/listener/store.js +20 -0
- package/lib/locator.js +214 -197
- package/lib/mocha/asyncWrapper.js +274 -0
- package/lib/mocha/bdd.js +167 -0
- package/lib/mocha/cli.js +341 -0
- package/lib/mocha/factory.js +163 -0
- package/lib/mocha/featureConfig.js +89 -0
- package/lib/mocha/gherkin.js +231 -0
- package/lib/mocha/hooks.js +121 -0
- package/lib/mocha/index.js +21 -0
- package/lib/mocha/inject.js +46 -0
- package/lib/{interfaces → mocha}/scenarioConfig.js +58 -34
- package/lib/mocha/suite.js +89 -0
- package/lib/mocha/test.js +184 -0
- package/lib/mocha/types.d.ts +42 -0
- package/lib/mocha/ui.js +242 -0
- package/lib/output.js +141 -71
- package/lib/parser.js +47 -44
- package/lib/pause.js +173 -145
- package/lib/plugin/analyze.js +403 -0
- package/lib/plugin/{autoLogin.js → auth.js} +178 -79
- package/lib/plugin/autoDelay.js +36 -40
- package/lib/plugin/coverage.js +131 -78
- package/lib/plugin/customLocator.js +22 -21
- package/lib/plugin/customReporter.js +53 -0
- package/lib/plugin/enhancedRetryFailedStep.js +99 -0
- package/lib/plugin/heal.js +101 -110
- package/lib/plugin/htmlReporter.js +3648 -0
- package/lib/plugin/pageInfo.js +140 -0
- package/lib/plugin/pauseOnFail.js +12 -11
- package/lib/plugin/retryFailedStep.js +82 -47
- package/lib/plugin/screenshotOnFail.js +111 -92
- package/lib/plugin/stepByStepReport.js +159 -101
- package/lib/plugin/stepTimeout.js +20 -25
- package/lib/plugin/subtitles.js +38 -38
- package/lib/recorder.js +193 -130
- package/lib/rerun.js +94 -49
- package/lib/result.js +238 -0
- package/lib/retryCoordinator.js +207 -0
- package/lib/secret.js +20 -18
- package/lib/session.js +95 -89
- package/lib/step/base.js +239 -0
- package/lib/step/comment.js +10 -0
- package/lib/step/config.js +50 -0
- package/lib/step/func.js +46 -0
- package/lib/step/helper.js +50 -0
- package/lib/step/meta.js +99 -0
- package/lib/step/record.js +74 -0
- package/lib/step/retry.js +11 -0
- package/lib/step/section.js +55 -0
- package/lib/step.js +18 -329
- package/lib/steps.js +54 -0
- package/lib/store.js +38 -7
- package/lib/template/heal.js +3 -12
- package/lib/template/prompts/generatePageObject.js +31 -0
- package/lib/template/prompts/healStep.js +13 -0
- package/lib/template/prompts/writeStep.js +9 -0
- package/lib/test-server.js +334 -0
- package/lib/timeout.js +60 -0
- package/lib/transform.js +8 -8
- package/lib/translation.js +34 -21
- package/lib/utils/loaderCheck.js +124 -0
- package/lib/utils/mask_data.js +47 -0
- package/lib/utils/typescript.js +237 -0
- package/lib/utils.js +411 -228
- package/lib/workerStorage.js +37 -34
- package/lib/workers.js +532 -296
- package/package.json +124 -95
- package/translations/de-DE.js +5 -3
- package/translations/fr-FR.js +5 -4
- package/translations/index.js +22 -12
- package/translations/it-IT.js +4 -3
- package/translations/ja-JP.js +4 -3
- package/translations/nl-NL.js +76 -0
- package/translations/pl-PL.js +4 -3
- package/translations/pt-BR.js +4 -3
- package/translations/ru-RU.js +4 -3
- package/translations/utils.js +10 -0
- package/translations/zh-CN.js +4 -3
- package/translations/zh-TW.js +4 -3
- package/typings/index.d.ts +546 -185
- package/typings/promiseBasedTypes.d.ts +150 -875
- package/typings/types.d.ts +547 -992
- package/lib/cli.js +0 -249
- package/lib/dirname.js +0 -5
- package/lib/helper/Expect.js +0 -425
- package/lib/helper/ExpectHelper.js +0 -399
- package/lib/helper/MockServer.js +0 -223
- package/lib/helper/Nightmare.js +0 -1411
- package/lib/helper/Protractor.js +0 -1835
- package/lib/helper/SoftExpectHelper.js +0 -381
- package/lib/helper/TestCafe.js +0 -1410
- package/lib/helper/clientscripts/nightmare.js +0 -213
- package/lib/helper/testcafe/testControllerHolder.js +0 -42
- package/lib/helper/testcafe/testcafe-utils.js +0 -63
- package/lib/interfaces/bdd.js +0 -98
- package/lib/interfaces/featureConfig.js +0 -69
- package/lib/interfaces/gherkin.js +0 -195
- package/lib/listener/artifacts.js +0 -19
- package/lib/listener/retry.js +0 -68
- package/lib/listener/timeout.js +0 -109
- package/lib/mochaFactory.js +0 -110
- package/lib/plugin/allure.js +0 -15
- package/lib/plugin/commentStep.js +0 -136
- package/lib/plugin/debugErrors.js +0 -67
- package/lib/plugin/eachElement.js +0 -127
- package/lib/plugin/fakerTransform.js +0 -49
- package/lib/plugin/retryTo.js +0 -121
- package/lib/plugin/selenoid.js +0 -371
- package/lib/plugin/standardActingHelpers.js +0 -9
- package/lib/plugin/tryTo.js +0 -105
- package/lib/plugin/wdio.js +0 -246
- package/lib/scenario.js +0 -222
- package/lib/ui.js +0 -238
- package/lib/within.js +0 -70
|
@@ -1,12 +1,6 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
3
|
-
|
|
4
|
-
let expect;
|
|
5
|
-
|
|
6
|
-
import('chai').then(chai => {
|
|
7
|
-
expect = chai.expect;
|
|
8
|
-
chai.use(require('chai-deep-match'));
|
|
9
|
-
});
|
|
1
|
+
import Helper from '@codeceptjs/helper'
|
|
2
|
+
import assert from 'assert'
|
|
3
|
+
import { z } from 'zod'
|
|
10
4
|
|
|
11
5
|
/**
|
|
12
6
|
* This helper allows performing assertions on JSON responses paired with following helpers:
|
|
@@ -66,36 +60,30 @@ import('chai').then(chai => {
|
|
|
66
60
|
*/
|
|
67
61
|
class JSONResponse extends Helper {
|
|
68
62
|
constructor(config = {}) {
|
|
69
|
-
super(config)
|
|
63
|
+
super(config)
|
|
70
64
|
this.options = {
|
|
71
65
|
requestHelper: 'REST',
|
|
72
|
-
}
|
|
73
|
-
this.options = { ...this.options, ...config }
|
|
66
|
+
}
|
|
67
|
+
this.options = { ...this.options, ...config }
|
|
74
68
|
}
|
|
75
69
|
|
|
76
70
|
_beforeSuite() {
|
|
77
|
-
this.response = null
|
|
71
|
+
this.response = null
|
|
78
72
|
if (!this.helpers[this.options.requestHelper]) {
|
|
79
|
-
throw new Error(`Error setting JSONResponse, helper ${this.options.requestHelper} is not enabled in config, helpers: ${Object.keys(this.helpers)}`)
|
|
73
|
+
throw new Error(`Error setting JSONResponse, helper ${this.options.requestHelper} is not enabled in config, helpers: ${Object.keys(this.helpers)}`)
|
|
74
|
+
}
|
|
75
|
+
const origOnResponse = this.helpers[this.options.requestHelper].config.onResponse
|
|
76
|
+
this.helpers[this.options.requestHelper].config.onResponse = response => {
|
|
77
|
+
this.response = response
|
|
78
|
+
if (typeof origOnResponse === 'function') origOnResponse(response)
|
|
80
79
|
}
|
|
81
|
-
// connect to REST helper
|
|
82
|
-
this.helpers[this.options.requestHelper].config.onResponse = (response) => {
|
|
83
|
-
this.response = response;
|
|
84
|
-
};
|
|
85
80
|
}
|
|
86
81
|
|
|
87
82
|
_before() {
|
|
88
|
-
this.response = null
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
static _checkRequirements() {
|
|
92
|
-
try {
|
|
93
|
-
require('joi');
|
|
94
|
-
} catch (e) {
|
|
95
|
-
return ['joi'];
|
|
96
|
-
}
|
|
83
|
+
this.response = null
|
|
97
84
|
}
|
|
98
85
|
|
|
86
|
+
|
|
99
87
|
/**
|
|
100
88
|
* Checks that response code is equal to the provided one
|
|
101
89
|
*
|
|
@@ -106,8 +94,8 @@ class JSONResponse extends Helper {
|
|
|
106
94
|
* @param {number} code
|
|
107
95
|
*/
|
|
108
96
|
seeResponseCodeIs(code) {
|
|
109
|
-
this._checkResponseReady()
|
|
110
|
-
|
|
97
|
+
this._checkResponseReady()
|
|
98
|
+
assert.strictEqual(this.response.status, code, 'Response code is not the same as expected')
|
|
111
99
|
}
|
|
112
100
|
|
|
113
101
|
/**
|
|
@@ -120,35 +108,32 @@ class JSONResponse extends Helper {
|
|
|
120
108
|
* @param {number} code
|
|
121
109
|
*/
|
|
122
110
|
dontSeeResponseCodeIs(code) {
|
|
123
|
-
this._checkResponseReady()
|
|
124
|
-
|
|
111
|
+
this._checkResponseReady()
|
|
112
|
+
assert.notStrictEqual(this.response.status, code)
|
|
125
113
|
}
|
|
126
114
|
|
|
127
115
|
/**
|
|
128
116
|
* Checks that the response code is 4xx
|
|
129
117
|
*/
|
|
130
118
|
seeResponseCodeIsClientError() {
|
|
131
|
-
this._checkResponseReady()
|
|
132
|
-
|
|
133
|
-
expect(this.response.status).to.be.lt(500);
|
|
119
|
+
this._checkResponseReady()
|
|
120
|
+
assert(this.response.status >= 400 && this.response.status < 500)
|
|
134
121
|
}
|
|
135
122
|
|
|
136
123
|
/**
|
|
137
124
|
* Checks that the response code is 3xx
|
|
138
125
|
*/
|
|
139
126
|
seeResponseCodeIsRedirection() {
|
|
140
|
-
this._checkResponseReady()
|
|
141
|
-
|
|
142
|
-
expect(this.response.status).to.be.lt(400);
|
|
127
|
+
this._checkResponseReady()
|
|
128
|
+
assert(this.response.status >= 300 && this.response.status < 400)
|
|
143
129
|
}
|
|
144
130
|
|
|
145
131
|
/**
|
|
146
132
|
* Checks that the response code is 5xx
|
|
147
133
|
*/
|
|
148
134
|
seeResponseCodeIsServerError() {
|
|
149
|
-
this._checkResponseReady()
|
|
150
|
-
|
|
151
|
-
expect(this.response.status).to.be.lt(600);
|
|
135
|
+
this._checkResponseReady()
|
|
136
|
+
assert(this.response.status >= 500 && this.response.status < 600)
|
|
152
137
|
}
|
|
153
138
|
|
|
154
139
|
/**
|
|
@@ -160,9 +145,8 @@ class JSONResponse extends Helper {
|
|
|
160
145
|
* ```
|
|
161
146
|
*/
|
|
162
147
|
seeResponseCodeIsSuccessful() {
|
|
163
|
-
this._checkResponseReady()
|
|
164
|
-
|
|
165
|
-
expect(this.response.status).to.be.lt(300);
|
|
148
|
+
this._checkResponseReady()
|
|
149
|
+
assert(this.response.status >= 200 && this.response.status < 300)
|
|
166
150
|
}
|
|
167
151
|
|
|
168
152
|
/**
|
|
@@ -183,19 +167,21 @@ class JSONResponse extends Helper {
|
|
|
183
167
|
* @param {object} json
|
|
184
168
|
*/
|
|
185
169
|
seeResponseContainsJson(json = {}) {
|
|
186
|
-
this._checkResponseReady()
|
|
170
|
+
this._checkResponseReady()
|
|
187
171
|
if (Array.isArray(this.response.data)) {
|
|
188
|
-
let
|
|
172
|
+
let found = false
|
|
189
173
|
for (const el of this.response.data) {
|
|
190
174
|
try {
|
|
191
|
-
|
|
175
|
+
this._assertContains(el, json)
|
|
176
|
+
found = true
|
|
177
|
+
break
|
|
192
178
|
} catch (err) {
|
|
193
|
-
|
|
179
|
+
continue
|
|
194
180
|
}
|
|
195
181
|
}
|
|
196
|
-
|
|
182
|
+
assert(found, `No elements in array matched ${JSON.stringify(json)}`)
|
|
197
183
|
} else {
|
|
198
|
-
|
|
184
|
+
this._assertContains(this.response.data, json)
|
|
199
185
|
}
|
|
200
186
|
}
|
|
201
187
|
|
|
@@ -217,11 +203,24 @@ class JSONResponse extends Helper {
|
|
|
217
203
|
* @param {object} json
|
|
218
204
|
*/
|
|
219
205
|
dontSeeResponseContainsJson(json = {}) {
|
|
220
|
-
this._checkResponseReady()
|
|
206
|
+
this._checkResponseReady()
|
|
221
207
|
if (Array.isArray(this.response.data)) {
|
|
222
|
-
|
|
208
|
+
for (const data of this.response.data) {
|
|
209
|
+
try {
|
|
210
|
+
this._assertContains(data, json)
|
|
211
|
+
assert.fail(`Found matching element: ${JSON.stringify(data)}`)
|
|
212
|
+
} catch (err) {
|
|
213
|
+
// expected to fail
|
|
214
|
+
continue
|
|
215
|
+
}
|
|
216
|
+
}
|
|
223
217
|
} else {
|
|
224
|
-
|
|
218
|
+
try {
|
|
219
|
+
this._assertContains(this.response.data, json)
|
|
220
|
+
assert.fail('Response contains the JSON')
|
|
221
|
+
} catch (err) {
|
|
222
|
+
// expected to fail
|
|
223
|
+
}
|
|
225
224
|
}
|
|
226
225
|
}
|
|
227
226
|
|
|
@@ -245,32 +244,39 @@ class JSONResponse extends Helper {
|
|
|
245
244
|
* @param {array} keys
|
|
246
245
|
*/
|
|
247
246
|
seeResponseContainsKeys(keys = []) {
|
|
248
|
-
this._checkResponseReady()
|
|
247
|
+
this._checkResponseReady()
|
|
249
248
|
if (Array.isArray(this.response.data)) {
|
|
250
|
-
|
|
249
|
+
for (const data of this.response.data) {
|
|
250
|
+
for (const key of keys) {
|
|
251
|
+
assert(key in data, `Key "${key}" is not found in ${JSON.stringify(data)}`)
|
|
252
|
+
}
|
|
253
|
+
}
|
|
251
254
|
} else {
|
|
252
|
-
|
|
255
|
+
for (const key of keys) {
|
|
256
|
+
assert(key in this.response.data, `Key "${key}" is not found in ${JSON.stringify(this.response.data)}`)
|
|
257
|
+
}
|
|
253
258
|
}
|
|
254
259
|
}
|
|
255
260
|
|
|
256
261
|
/**
|
|
257
|
-
* Executes a callback function passing in `response` object and
|
|
262
|
+
* Executes a callback function passing in `response` object and assert
|
|
258
263
|
* Use it to perform custom checks of response data
|
|
259
264
|
*
|
|
260
265
|
* ```js
|
|
261
|
-
* I.seeResponseValidByCallback(({ data, status
|
|
262
|
-
*
|
|
263
|
-
*
|
|
266
|
+
* I.seeResponseValidByCallback(({ data, status }) => {
|
|
267
|
+
* assert.strictEqual(status, 200);
|
|
268
|
+
* assert('user' in data);
|
|
269
|
+
* assert('company' in data);
|
|
264
270
|
* });
|
|
265
271
|
* ```
|
|
266
272
|
*
|
|
267
273
|
* @param {function} fn
|
|
268
274
|
*/
|
|
269
275
|
seeResponseValidByCallback(fn) {
|
|
270
|
-
this._checkResponseReady()
|
|
271
|
-
fn({ ...this.response,
|
|
272
|
-
const body = fn.toString()
|
|
273
|
-
fn.toString = () => `${body.split('\n')[1]}
|
|
276
|
+
this._checkResponseReady()
|
|
277
|
+
fn({ ...this.response, assert })
|
|
278
|
+
const body = fn.toString()
|
|
279
|
+
fn.toString = () => `${body.split('\n')[1]}...`
|
|
274
280
|
}
|
|
275
281
|
|
|
276
282
|
/**
|
|
@@ -284,54 +290,86 @@ class JSONResponse extends Helper {
|
|
|
284
290
|
* @param {object} resp
|
|
285
291
|
*/
|
|
286
292
|
seeResponseEquals(resp) {
|
|
287
|
-
this._checkResponseReady()
|
|
288
|
-
|
|
293
|
+
this._checkResponseReady()
|
|
294
|
+
assert.deepStrictEqual(this.response.data, resp)
|
|
289
295
|
}
|
|
290
296
|
|
|
291
297
|
/**
|
|
292
|
-
* Validates JSON structure of response using [
|
|
293
|
-
* See [
|
|
298
|
+
* Validates JSON structure of response using [Zod library](https://zod.dev).
|
|
299
|
+
* See [Zod API](https://zod.dev/) for complete reference on usage.
|
|
294
300
|
*
|
|
295
|
-
* Use pre-initialized
|
|
301
|
+
* Use pre-initialized Zod instance by passing function callback:
|
|
296
302
|
*
|
|
297
303
|
* ```js
|
|
298
304
|
* // response.data is { name: 'jon', id: 1 }
|
|
299
305
|
*
|
|
300
|
-
* I.seeResponseMatchesJsonSchema(
|
|
301
|
-
* return
|
|
302
|
-
* name:
|
|
303
|
-
* id:
|
|
306
|
+
* I.seeResponseMatchesJsonSchema(z => {
|
|
307
|
+
* return z.object({
|
|
308
|
+
* name: z.string(),
|
|
309
|
+
* id: z.number()
|
|
304
310
|
* })
|
|
305
311
|
* });
|
|
306
312
|
*
|
|
307
313
|
* // or pass a valid schema
|
|
308
|
-
*
|
|
314
|
+
* import { z } from 'zod';
|
|
309
315
|
*
|
|
310
|
-
* I.seeResponseMatchesJsonSchema(
|
|
311
|
-
* name:
|
|
312
|
-
* id:
|
|
313
|
-
* });
|
|
316
|
+
* I.seeResponseMatchesJsonSchema(z.object({
|
|
317
|
+
* name: z.string(),
|
|
318
|
+
* id: z.number()
|
|
319
|
+
* }));
|
|
314
320
|
* ```
|
|
315
321
|
*
|
|
316
322
|
* @param {any} fnOrSchema
|
|
317
323
|
*/
|
|
318
324
|
seeResponseMatchesJsonSchema(fnOrSchema) {
|
|
319
|
-
this._checkResponseReady()
|
|
320
|
-
let schema = fnOrSchema
|
|
325
|
+
this._checkResponseReady()
|
|
326
|
+
let schema = fnOrSchema
|
|
321
327
|
if (typeof fnOrSchema === 'function') {
|
|
322
|
-
schema = fnOrSchema(
|
|
323
|
-
const body = fnOrSchema.toString()
|
|
324
|
-
fnOrSchema.toString = () => `${body.split('\n')[1]}
|
|
328
|
+
schema = fnOrSchema(z)
|
|
329
|
+
const body = fnOrSchema.toString()
|
|
330
|
+
fnOrSchema.toString = () => `${body.split('\n')[1]}...`
|
|
331
|
+
}
|
|
332
|
+
if (!schema) throw new Error('Empty Zod schema provided, see https://zod.dev/ for details')
|
|
333
|
+
if (!(schema instanceof z.ZodType)) throw new Error('Invalid Zod schema provided, see https://zod.dev/ for details')
|
|
334
|
+
schema.toString = () => schema._def.description || JSON.stringify(schema._def)
|
|
335
|
+
const result = schema.parse(this.response.data)
|
|
336
|
+
if (!result) {
|
|
337
|
+
throw new Error('Schema validation failed')
|
|
325
338
|
}
|
|
326
|
-
if (!schema) throw new Error('Empty Joi schema provided, see https://joi.dev/ for details');
|
|
327
|
-
if (!joi.isSchema(schema)) throw new Error('Invalid Joi schema provided, see https://joi.dev/ for details');
|
|
328
|
-
schema.toString = () => schema.describe();
|
|
329
|
-
joi.assert(this.response.data, schema);
|
|
330
339
|
}
|
|
331
340
|
|
|
332
341
|
_checkResponseReady() {
|
|
333
|
-
if (!this.response) throw new Error('Response is not available')
|
|
342
|
+
if (!this.response) throw new Error('Response is not available')
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
_assertContains(actual, expected) {
|
|
346
|
+
for (const key in expected) {
|
|
347
|
+
assert(key in actual, `Key "${key}" not found in ${JSON.stringify(actual)}`)
|
|
348
|
+
if (typeof expected[key] === 'object' && expected[key] !== null) {
|
|
349
|
+
if (Array.isArray(expected[key])) {
|
|
350
|
+
// Handle array comparison: each expected element should have a match in actual array
|
|
351
|
+
assert(Array.isArray(actual[key]), `Expected array for key "${key}", but got ${typeof actual[key]}`)
|
|
352
|
+
for (const expectedItem of expected[key]) {
|
|
353
|
+
let found = false
|
|
354
|
+
for (const actualItem of actual[key]) {
|
|
355
|
+
try {
|
|
356
|
+
this._assertContains(actualItem, expectedItem)
|
|
357
|
+
found = true
|
|
358
|
+
break
|
|
359
|
+
} catch (err) {
|
|
360
|
+
continue
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
assert(found, `No matching element found in array for ${JSON.stringify(expectedItem)}`)
|
|
364
|
+
}
|
|
365
|
+
} else {
|
|
366
|
+
this._assertContains(actual[key], expected[key])
|
|
367
|
+
}
|
|
368
|
+
} else {
|
|
369
|
+
assert.deepStrictEqual(actual[key], expected[key], `Values for key "${key}" don't match`)
|
|
370
|
+
}
|
|
371
|
+
}
|
|
334
372
|
}
|
|
335
373
|
}
|
|
336
374
|
|
|
337
|
-
export default
|
|
375
|
+
export { JSONResponse as default }
|
|
@@ -1,71 +1,96 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
let currentTest
|
|
2
|
+
let currentSuite
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
import Helper from '@codeceptjs/helper'
|
|
5
|
+
import { createRequire } from 'module'
|
|
6
|
+
import { clearString } from '../utils.js'
|
|
7
|
+
import { testToFileName } from '../mocha/test.js'
|
|
7
8
|
|
|
8
9
|
class Mochawesome extends Helper {
|
|
9
10
|
constructor(config) {
|
|
10
|
-
super(config)
|
|
11
|
+
super(config)
|
|
11
12
|
|
|
12
13
|
// set defaults
|
|
13
14
|
this.options = {
|
|
14
15
|
uniqueScreenshotNames: false,
|
|
15
16
|
disableScreenshots: false,
|
|
16
|
-
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// ESM-compatible require for CommonJS module
|
|
20
|
+
const require = createRequire(import.meta.url)
|
|
21
|
+
this._addContext = require('mochawesome/addContext')
|
|
17
22
|
|
|
18
|
-
|
|
19
|
-
this._createConfig(config);
|
|
23
|
+
this._createConfig(config)
|
|
20
24
|
}
|
|
21
25
|
|
|
22
26
|
_createConfig(config) {
|
|
23
27
|
// override defaults with config
|
|
24
|
-
Object.assign(this.options, config)
|
|
28
|
+
Object.assign(this.options, config)
|
|
25
29
|
}
|
|
26
30
|
|
|
27
31
|
_beforeSuite(suite) {
|
|
28
|
-
currentSuite = suite
|
|
29
|
-
currentTest = ''
|
|
32
|
+
currentSuite = suite
|
|
33
|
+
currentTest = ''
|
|
30
34
|
}
|
|
31
35
|
|
|
32
36
|
_before() {
|
|
33
37
|
if (currentSuite && currentSuite.ctx) {
|
|
34
|
-
currentTest = { test: currentSuite.ctx.currentTest }
|
|
38
|
+
currentTest = { test: currentSuite.ctx.currentTest }
|
|
35
39
|
}
|
|
36
40
|
}
|
|
37
41
|
|
|
38
42
|
_test(test) {
|
|
39
|
-
|
|
43
|
+
// If this is a retried test, we want to add context to the retried test
|
|
44
|
+
// but also potentially preserve context from the original test
|
|
45
|
+
const originalTest = test.retriedTest && test.retriedTest()
|
|
46
|
+
if (originalTest) {
|
|
47
|
+
// This is a retried test - use the retried test for context
|
|
48
|
+
currentTest = { test }
|
|
49
|
+
|
|
50
|
+
// Optionally copy context from original test if it exists
|
|
51
|
+
// Note: mochawesome context is stored in test.ctx, but we need to be careful
|
|
52
|
+
// not to break the mocha context structure
|
|
53
|
+
} else {
|
|
54
|
+
// Normal test (not a retry)
|
|
55
|
+
currentTest = { test }
|
|
56
|
+
}
|
|
40
57
|
}
|
|
41
58
|
|
|
42
59
|
_failed(test) {
|
|
43
|
-
if (this.options.disableScreenshots) return
|
|
44
|
-
let fileName
|
|
60
|
+
if (this.options.disableScreenshots) return
|
|
61
|
+
let fileName
|
|
45
62
|
// Get proper name if we are fail on hook
|
|
46
|
-
if (test.ctx
|
|
47
|
-
currentTest = { test: test.ctx.test }
|
|
63
|
+
if (test.ctx?.test?.type === 'hook') {
|
|
64
|
+
currentTest = { test: test.ctx.test }
|
|
48
65
|
// ignore retries if we are in hook
|
|
49
|
-
test._retries = -1
|
|
50
|
-
fileName = clearString(`${test.title}_${currentTest.test.title}`)
|
|
66
|
+
test._retries = -1
|
|
67
|
+
fileName = clearString(`${test.title}_${currentTest.test.title}`)
|
|
51
68
|
} else {
|
|
52
|
-
currentTest = { test }
|
|
53
|
-
fileName =
|
|
69
|
+
currentTest = { test }
|
|
70
|
+
fileName = testToFileName(test)
|
|
54
71
|
}
|
|
55
72
|
if (this.options.uniqueScreenshotNames) {
|
|
56
|
-
|
|
57
|
-
fileName = `${fileName.substring(0, 10)}_${uuid}`;
|
|
73
|
+
fileName = testToFileName(test, { unique: true })
|
|
58
74
|
}
|
|
59
75
|
if (test._retries < 1 || test._retries === test.retryNum) {
|
|
60
|
-
fileName = `${fileName}.failed.png
|
|
61
|
-
return
|
|
76
|
+
fileName = `${fileName}.failed.png`
|
|
77
|
+
return this._addContext(currentTest, fileName)
|
|
62
78
|
}
|
|
63
79
|
}
|
|
64
80
|
|
|
65
81
|
addMochawesomeContext(context) {
|
|
66
|
-
if (currentTest === '') currentTest = { test: currentSuite.ctx.test }
|
|
67
|
-
|
|
82
|
+
if (currentTest === '') currentTest = { test: currentSuite.ctx.test }
|
|
83
|
+
|
|
84
|
+
// For retried tests, make sure we're adding context to the current (retried) test
|
|
85
|
+
// not the original test
|
|
86
|
+
let targetTest = currentTest
|
|
87
|
+
if (currentTest.test && currentTest.test.retriedTest && currentTest.test.retriedTest()) {
|
|
88
|
+
// This test has been retried, make sure we're using the current test for context
|
|
89
|
+
targetTest = { test: currentTest.test }
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return this._addContext(targetTest, context)
|
|
68
93
|
}
|
|
69
94
|
}
|
|
70
95
|
|
|
71
|
-
export default Mochawesome
|
|
96
|
+
export default Mochawesome
|