codeceptjs 3.7.0-beta.8 → 3.7.0-beta.9
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/els.js +87 -106
- package/lib/helper/Playwright.js +7 -1
- package/lib/listener/store.js +9 -1
- package/lib/mocha/asyncWrapper.js +3 -3
- package/lib/mocha/cli.js +15 -7
- package/lib/output.js +1 -1
- package/lib/plugin/analyze.js +14 -17
- package/lib/plugin/pageInfo.js +0 -3
- package/lib/plugin/screenshotOnFail.js +7 -10
- package/lib/plugin/stepByStepReport.js +5 -4
- package/lib/step/func.js +46 -0
- package/lib/step.js +6 -0
- package/lib/store.js +2 -0
- package/package.json +6 -5
- package/typings/promiseBasedTypes.d.ts +610 -0
- package/typings/types.d.ts +610 -63
package/lib/els.js
CHANGED
|
@@ -1,115 +1,124 @@
|
|
|
1
|
-
const output = require('./output')
|
|
2
|
-
const store = require('./store')
|
|
3
|
-
const
|
|
4
|
-
const
|
|
5
|
-
const
|
|
6
|
-
const
|
|
7
|
-
const { truth } = require('./assert/truth')
|
|
8
|
-
const { isAsyncFunction, humanizeFunction } = require('./utils')
|
|
1
|
+
const output = require('./output')
|
|
2
|
+
const store = require('./store')
|
|
3
|
+
const container = require('./container')
|
|
4
|
+
const StepConfig = require('./step/config')
|
|
5
|
+
const recordStep = require('./step/record')
|
|
6
|
+
const FuncStep = require('./step/func')
|
|
7
|
+
const { truth } = require('./assert/truth')
|
|
8
|
+
const { isAsyncFunction, humanizeFunction } = require('./utils')
|
|
9
9
|
|
|
10
10
|
function element(purpose, locator, fn) {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
purpose = 'first element';
|
|
11
|
+
let stepConfig
|
|
12
|
+
if (arguments[arguments.length - 1] instanceof StepConfig) {
|
|
13
|
+
stepConfig = arguments[arguments.length - 1]
|
|
15
14
|
}
|
|
16
15
|
|
|
17
|
-
|
|
18
|
-
|
|
16
|
+
if (!fn || fn === stepConfig) {
|
|
17
|
+
fn = locator
|
|
18
|
+
locator = purpose
|
|
19
|
+
purpose = 'first element'
|
|
20
|
+
}
|
|
19
21
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
output.debug(`Found ${els.length} elements, using first element`);
|
|
22
|
+
const step = prepareStep(purpose, locator, fn)
|
|
23
|
+
if (!step) return
|
|
23
24
|
|
|
24
|
-
|
|
25
|
-
|
|
25
|
+
return executeStep(
|
|
26
|
+
step,
|
|
27
|
+
async () => {
|
|
28
|
+
const els = await step.helper._locate(locator)
|
|
29
|
+
output.debug(`Found ${els.length} elements, using first element`)
|
|
30
|
+
|
|
31
|
+
return fn(els[0])
|
|
32
|
+
},
|
|
33
|
+
stepConfig,
|
|
34
|
+
)
|
|
26
35
|
}
|
|
27
36
|
|
|
28
37
|
function eachElement(purpose, locator, fn) {
|
|
29
38
|
if (!fn) {
|
|
30
|
-
fn = locator
|
|
31
|
-
locator = purpose
|
|
32
|
-
purpose = 'for each element'
|
|
39
|
+
fn = locator
|
|
40
|
+
locator = purpose
|
|
41
|
+
purpose = 'for each element'
|
|
33
42
|
}
|
|
34
43
|
|
|
35
|
-
const step = prepareStep(purpose, locator, fn)
|
|
36
|
-
if (!step) return
|
|
44
|
+
const step = prepareStep(purpose, locator, fn)
|
|
45
|
+
if (!step) return
|
|
37
46
|
|
|
38
47
|
return executeStep(step, async () => {
|
|
39
|
-
const els = await step.helper._locate(locator)
|
|
40
|
-
output.debug(`Found ${els.length} elements for each elements to iterate`)
|
|
48
|
+
const els = await step.helper._locate(locator)
|
|
49
|
+
output.debug(`Found ${els.length} elements for each elements to iterate`)
|
|
41
50
|
|
|
42
|
-
const errs = []
|
|
43
|
-
let i = 0
|
|
51
|
+
const errs = []
|
|
52
|
+
let i = 0
|
|
44
53
|
for (const el of els) {
|
|
45
54
|
try {
|
|
46
|
-
await fn(el, i)
|
|
55
|
+
await fn(el, i)
|
|
47
56
|
} catch (err) {
|
|
48
|
-
output.error(`eachElement: failed operation on element #${i} ${el}`)
|
|
49
|
-
errs.push(err)
|
|
57
|
+
output.error(`eachElement: failed operation on element #${i} ${el}`)
|
|
58
|
+
errs.push(err)
|
|
50
59
|
}
|
|
51
|
-
i
|
|
60
|
+
i++
|
|
52
61
|
}
|
|
53
62
|
|
|
54
63
|
if (errs.length) {
|
|
55
|
-
throw errs[0]
|
|
64
|
+
throw errs[0]
|
|
56
65
|
}
|
|
57
|
-
})
|
|
66
|
+
})
|
|
58
67
|
}
|
|
59
68
|
|
|
60
69
|
function expectElement(locator, fn) {
|
|
61
|
-
const step = prepareStep('expect element to be', locator, fn)
|
|
62
|
-
if (!step) return
|
|
70
|
+
const step = prepareStep('expect element to be', locator, fn)
|
|
71
|
+
if (!step) return
|
|
63
72
|
|
|
64
73
|
return executeStep(step, async () => {
|
|
65
|
-
const els = await step.helper._locate(locator)
|
|
66
|
-
output.debug(`Found ${els.length} elements, first will be used for assertion`)
|
|
74
|
+
const els = await step.helper._locate(locator)
|
|
75
|
+
output.debug(`Found ${els.length} elements, first will be used for assertion`)
|
|
67
76
|
|
|
68
|
-
const result = await fn(els[0])
|
|
69
|
-
const assertion = truth(`element (${locator})`, fn.toString())
|
|
70
|
-
assertion.assert(result)
|
|
71
|
-
})
|
|
77
|
+
const result = await fn(els[0])
|
|
78
|
+
const assertion = truth(`element (${locator})`, fn.toString())
|
|
79
|
+
assertion.assert(result)
|
|
80
|
+
})
|
|
72
81
|
}
|
|
73
82
|
|
|
74
83
|
function expectAnyElement(locator, fn) {
|
|
75
|
-
const step = prepareStep('expect any element to be', locator, fn)
|
|
76
|
-
if (!step) return
|
|
84
|
+
const step = prepareStep('expect any element to be', locator, fn)
|
|
85
|
+
if (!step) return
|
|
77
86
|
|
|
78
87
|
return executeStep(step, async () => {
|
|
79
|
-
const els = await step.helper._locate(locator)
|
|
80
|
-
output.debug(`Found ${els.length} elements, at least one should pass the assertion`)
|
|
88
|
+
const els = await step.helper._locate(locator)
|
|
89
|
+
output.debug(`Found ${els.length} elements, at least one should pass the assertion`)
|
|
81
90
|
|
|
82
|
-
const assertion = truth(`any element of (${locator})`, fn.toString())
|
|
91
|
+
const assertion = truth(`any element of (${locator})`, fn.toString())
|
|
83
92
|
|
|
84
|
-
let found = false
|
|
93
|
+
let found = false
|
|
85
94
|
for (const el of els) {
|
|
86
|
-
const result = await fn(el)
|
|
95
|
+
const result = await fn(el)
|
|
87
96
|
if (result) {
|
|
88
|
-
found = true
|
|
89
|
-
break
|
|
97
|
+
found = true
|
|
98
|
+
break
|
|
90
99
|
}
|
|
91
100
|
}
|
|
92
|
-
if (!found) throw assertion.getException()
|
|
93
|
-
})
|
|
101
|
+
if (!found) throw assertion.getException()
|
|
102
|
+
})
|
|
94
103
|
}
|
|
95
104
|
|
|
96
105
|
function expectAllElements(locator, fn) {
|
|
97
|
-
const step = prepareStep('expect all elements', locator, fn)
|
|
98
|
-
if (!step) return
|
|
106
|
+
const step = prepareStep('expect all elements', locator, fn)
|
|
107
|
+
if (!step) return
|
|
99
108
|
|
|
100
109
|
return executeStep(step, async () => {
|
|
101
|
-
const els = await step.helper._locate(locator)
|
|
102
|
-
output.debug(`Found ${els.length} elements, all should pass the assertion`)
|
|
110
|
+
const els = await step.helper._locate(locator)
|
|
111
|
+
output.debug(`Found ${els.length} elements, all should pass the assertion`)
|
|
103
112
|
|
|
104
|
-
let i = 1
|
|
113
|
+
let i = 1
|
|
105
114
|
for (const el of els) {
|
|
106
|
-
output.debug(`checking element #${i}: ${el}`)
|
|
107
|
-
const result = await fn(el)
|
|
108
|
-
const assertion = truth(`element #${i} of (${locator})`, humanizeFunction(fn))
|
|
109
|
-
assertion.assert(result)
|
|
110
|
-
i
|
|
115
|
+
output.debug(`checking element #${i}: ${el}`)
|
|
116
|
+
const result = await fn(el)
|
|
117
|
+
const assertion = truth(`element #${i} of (${locator})`, humanizeFunction(fn))
|
|
118
|
+
assertion.assert(result)
|
|
119
|
+
i++
|
|
111
120
|
}
|
|
112
|
-
})
|
|
121
|
+
})
|
|
113
122
|
}
|
|
114
123
|
|
|
115
124
|
module.exports = {
|
|
@@ -118,60 +127,32 @@ module.exports = {
|
|
|
118
127
|
expectElement,
|
|
119
128
|
expectAnyElement,
|
|
120
129
|
expectAllElements,
|
|
121
|
-
}
|
|
130
|
+
}
|
|
122
131
|
|
|
123
132
|
function prepareStep(purpose, locator, fn) {
|
|
124
|
-
if (store.dryRun) return
|
|
125
|
-
const helpers = Object.values(container.helpers())
|
|
133
|
+
if (store.dryRun) return
|
|
134
|
+
const helpers = Object.values(container.helpers())
|
|
126
135
|
|
|
127
|
-
const helper = helpers.filter(h => !!h._locate)[0]
|
|
136
|
+
const helper = helpers.filter(h => !!h._locate)[0]
|
|
128
137
|
|
|
129
138
|
if (!helper) {
|
|
130
|
-
throw new Error('No helper enabled with _locate method with returns a list of elements.')
|
|
139
|
+
throw new Error('No helper enabled with _locate method with returns a list of elements.')
|
|
131
140
|
}
|
|
132
141
|
|
|
133
142
|
if (!isAsyncFunction(fn)) {
|
|
134
|
-
throw new Error('Async function should be passed into each element')
|
|
143
|
+
throw new Error('Async function should be passed into each element')
|
|
135
144
|
}
|
|
136
145
|
|
|
137
|
-
const isAssertion = purpose.startsWith('expect')
|
|
146
|
+
const isAssertion = purpose.startsWith('expect')
|
|
138
147
|
|
|
139
|
-
const step = new
|
|
140
|
-
step.
|
|
141
|
-
step.setArguments([humanizeFunction(fn)])
|
|
142
|
-
step.helperMethod = '_locate';
|
|
148
|
+
const step = new FuncStep(`${purpose} within "${locator}" ${isAssertion ? 'to be' : 'to'}`)
|
|
149
|
+
step.setHelper(helper)
|
|
150
|
+
step.setArguments([humanizeFunction(fn)]) // user defined function is a passed argument
|
|
143
151
|
|
|
144
|
-
return step
|
|
152
|
+
return step
|
|
145
153
|
}
|
|
146
154
|
|
|
147
|
-
async function executeStep(step, action) {
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
event.emit(event.step.started, step);
|
|
151
|
-
|
|
152
|
-
try {
|
|
153
|
-
await action();
|
|
154
|
-
} catch (err) {
|
|
155
|
-
recorder.throw(err);
|
|
156
|
-
event.emit(event.step.failed, step, err);
|
|
157
|
-
event.emit(event.step.finished, step);
|
|
158
|
-
// event.emit(event.step.after, step)
|
|
159
|
-
error = err;
|
|
160
|
-
// await recorder.promise();
|
|
161
|
-
return;
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
event.emit(event.step.after, step);
|
|
165
|
-
event.emit(event.step.passed, step);
|
|
166
|
-
event.emit(event.step.finished, step);
|
|
167
|
-
});
|
|
168
|
-
|
|
169
|
-
// await recorder.promise();
|
|
170
|
-
|
|
171
|
-
// if (error) {
|
|
172
|
-
// console.log('error', error.inspect())
|
|
173
|
-
// return recorder.throw(error);
|
|
174
|
-
// }
|
|
175
|
-
|
|
176
|
-
return promise;
|
|
155
|
+
async function executeStep(step, action, stepConfig = {}) {
|
|
156
|
+
step.setCallable(action)
|
|
157
|
+
return recordStep(step, [stepConfig])
|
|
177
158
|
}
|
package/lib/helper/Playwright.js
CHANGED
|
@@ -523,12 +523,17 @@ class Playwright extends Helper {
|
|
|
523
523
|
this.currentRunningTest.artifacts.har = fileName
|
|
524
524
|
contextOptions.recordHar = this.options.recordHar
|
|
525
525
|
}
|
|
526
|
+
|
|
527
|
+
// load pre-saved cookies
|
|
528
|
+
if (test?.opts?.cookies) contextOptions.storageState = { cookies: test.opts.cookies }
|
|
529
|
+
|
|
526
530
|
if (this.storageState) contextOptions.storageState = this.storageState
|
|
527
531
|
if (this.options.userAgent) contextOptions.userAgent = this.options.userAgent
|
|
528
532
|
if (this.options.locale) contextOptions.locale = this.options.locale
|
|
529
533
|
if (this.options.colorScheme) contextOptions.colorScheme = this.options.colorScheme
|
|
530
534
|
this.contextOptions = contextOptions
|
|
531
535
|
if (!this.browserContext || !restartsSession()) {
|
|
536
|
+
this.debugSection('New Session', JSON.stringify(this.contextOptions))
|
|
532
537
|
this.browserContext = await this.browser.newContext(this.contextOptions) // Adding the HTTPSError ignore in the context so that we can ignore those errors
|
|
533
538
|
}
|
|
534
539
|
}
|
|
@@ -938,7 +943,8 @@ class Playwright extends Helper {
|
|
|
938
943
|
throw new Error('Cannot open pages inside an Electron container')
|
|
939
944
|
}
|
|
940
945
|
if (!/^\w+\:(\/\/|.+)/.test(url)) {
|
|
941
|
-
url = this.options.url + (url.startsWith('/') ? url : `/${url}`)
|
|
946
|
+
url = this.options.url + (!this.options.url.endsWith('/') && url.startsWith('/') ? url : `/${url}`)
|
|
947
|
+
this.debug(`Changed URL to base url + relative path: ${url}`)
|
|
942
948
|
}
|
|
943
949
|
|
|
944
950
|
if (this.options.basicAuth && this.isAuthenticated !== true) {
|
package/lib/listener/store.js
CHANGED
|
@@ -2,11 +2,19 @@ const event = require('../event')
|
|
|
2
2
|
const store = require('../store')
|
|
3
3
|
|
|
4
4
|
module.exports = function () {
|
|
5
|
+
event.dispatcher.on(event.suite.before, suite => {
|
|
6
|
+
store.currentSuite = suite
|
|
7
|
+
})
|
|
8
|
+
|
|
9
|
+
event.dispatcher.on(event.suite.after, () => {
|
|
10
|
+
store.currentSuite = null
|
|
11
|
+
})
|
|
12
|
+
|
|
5
13
|
event.dispatcher.on(event.test.before, test => {
|
|
6
14
|
store.currentTest = test
|
|
7
15
|
})
|
|
8
16
|
|
|
9
|
-
event.dispatcher.on(event.test.finished,
|
|
17
|
+
event.dispatcher.on(event.test.finished, () => {
|
|
10
18
|
store.currentTest = null
|
|
11
19
|
})
|
|
12
20
|
}
|
|
@@ -19,10 +19,10 @@ const injectHook = function (inject, suite) {
|
|
|
19
19
|
return recorder.promise()
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
-
function suiteTestFailedHookError(suite, err) {
|
|
22
|
+
function suiteTestFailedHookError(suite, err, hookName) {
|
|
23
23
|
suite.eachTest(test => {
|
|
24
24
|
test.err = err
|
|
25
|
-
event.emit(event.test.failed, test, err)
|
|
25
|
+
event.emit(event.test.failed, test, err, ucfirst(hookName))
|
|
26
26
|
})
|
|
27
27
|
}
|
|
28
28
|
|
|
@@ -120,7 +120,7 @@ module.exports.injected = function (fn, suite, hookName) {
|
|
|
120
120
|
const errHandler = err => {
|
|
121
121
|
recorder.session.start('teardown')
|
|
122
122
|
recorder.cleanAsyncErr()
|
|
123
|
-
if (hookName == 'before' || hookName == 'beforeSuite') suiteTestFailedHookError(suite, err)
|
|
123
|
+
if (hookName == 'before' || hookName == 'beforeSuite') suiteTestFailedHookError(suite, err, hookName)
|
|
124
124
|
if (hookName === 'after') event.emit(event.test.after, suite)
|
|
125
125
|
if (hookName === 'afterSuite') event.emit(event.suite.after, suite)
|
|
126
126
|
recorder.add(() => doneFn(err))
|
package/lib/mocha/cli.js
CHANGED
|
@@ -198,17 +198,25 @@ class Cli extends Base {
|
|
|
198
198
|
// add new line before the message
|
|
199
199
|
err.message = '\n ' + err.message
|
|
200
200
|
|
|
201
|
+
// explicitly show file with error
|
|
202
|
+
if (test.file) {
|
|
203
|
+
log += `\n${output.styles.basic(figures.circle)} ${output.styles.section('File:')} ${output.styles.basic(test.file)}\n`
|
|
204
|
+
}
|
|
205
|
+
|
|
201
206
|
const steps = test.steps || (test.ctx && test.ctx.test.steps)
|
|
202
207
|
|
|
203
208
|
if (steps && steps.length) {
|
|
204
209
|
let scenarioTrace = ''
|
|
205
|
-
steps
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
210
|
+
steps
|
|
211
|
+
.reverse()
|
|
212
|
+
.slice(0, 10)
|
|
213
|
+
.forEach(step => {
|
|
214
|
+
const hasFailed = step.status === 'failed'
|
|
215
|
+
let line = `${hasFailed ? output.styles.bold(figures.cross) : figures.tick} ${step.toCode()} ${step.line()}`
|
|
216
|
+
if (hasFailed) line = output.styles.bold(line)
|
|
217
|
+
scenarioTrace += `\n${line}`
|
|
218
|
+
})
|
|
219
|
+
log += `\n${output.styles.basic(figures.circle)} ${output.styles.section('Scenario Steps')}:${scenarioTrace}\n`
|
|
212
220
|
}
|
|
213
221
|
|
|
214
222
|
// display artifacts in debug mode
|
package/lib/output.js
CHANGED
|
@@ -115,7 +115,7 @@ module.exports = {
|
|
|
115
115
|
}
|
|
116
116
|
}
|
|
117
117
|
|
|
118
|
-
let stepLine = step.toCliStyled()
|
|
118
|
+
let stepLine = step.toCliStyled ? step.toCliStyled() : step.toString()
|
|
119
119
|
if (step.metaStep && outputLevel >= 1) {
|
|
120
120
|
// this.stepShift += 2;
|
|
121
121
|
stepLine = colors.dim(truncate(stepLine, this.spaceShift))
|
package/lib/plugin/analyze.js
CHANGED
|
@@ -2,6 +2,7 @@ const debug = require('debug')('codeceptjs:analyze')
|
|
|
2
2
|
const { isMainThread } = require('node:worker_threads')
|
|
3
3
|
const { arrowRight } = require('figures')
|
|
4
4
|
const container = require('../container')
|
|
5
|
+
const store = require('../store')
|
|
5
6
|
const ai = require('../ai')
|
|
6
7
|
const colors = require('chalk')
|
|
7
8
|
const ora = require('ora-classic')
|
|
@@ -12,8 +13,8 @@ const { ansiRegExp, base64EncodeFile, markdownToAnsi } = require('../utils')
|
|
|
12
13
|
const MAX_DATA_LENGTH = 5000
|
|
13
14
|
|
|
14
15
|
const defaultConfig = {
|
|
15
|
-
clusterize:
|
|
16
|
-
analyze:
|
|
16
|
+
clusterize: 5,
|
|
17
|
+
analyze: 2,
|
|
17
18
|
vision: false,
|
|
18
19
|
categories: [
|
|
19
20
|
'Browser connection error / browser crash',
|
|
@@ -64,17 +65,19 @@ const defaultConfig = {
|
|
|
64
65
|
If you identify that all tests in the group have the same tag, add this tag to the group report, otherwise ignore TAG section.
|
|
65
66
|
If you identify that all tests in the group have the same suite, add this suite to the group report, otherwise ignore SUITE section.
|
|
66
67
|
Pick different emojis for each group.
|
|
67
|
-
|
|
68
|
+
Order groups by the number of tests in the group.
|
|
69
|
+
If group has one test, skip that group.
|
|
68
70
|
|
|
69
71
|
Provide list of groups in following format:
|
|
70
72
|
|
|
71
73
|
_______________________________
|
|
72
74
|
|
|
73
|
-
## Group <group_number>
|
|
75
|
+
## Group <group_number> <emoji>
|
|
74
76
|
|
|
77
|
+
* SUMMARY <summary_of_errors>
|
|
75
78
|
* CATEGORY <category_of_failure>
|
|
79
|
+
* URL <url_of_failure_if_any>
|
|
76
80
|
* ERROR <error_message_1>, <error_message_2>, ...
|
|
77
|
-
* SUMMARY <summary_of_errors>
|
|
78
81
|
* STEP <step_of_failure> (use CodeceptJS format I.click(), I.see(), etc; if all failures happend on the same step)
|
|
79
82
|
* SUITE <suite_title>, <suite_title> (if SUITE is present, and if all tests in the group have the same suite or suites)
|
|
80
83
|
* TAG <tag> (if TAG is present, and if all tests in the group have the same tag)
|
|
@@ -85,11 +88,6 @@ const defaultConfig = {
|
|
|
85
88
|
x ...
|
|
86
89
|
`,
|
|
87
90
|
},
|
|
88
|
-
{
|
|
89
|
-
role: 'assistant',
|
|
90
|
-
content: `## '
|
|
91
|
-
`,
|
|
92
|
-
},
|
|
93
91
|
]
|
|
94
92
|
return messages
|
|
95
93
|
},
|
|
@@ -126,14 +124,17 @@ const defaultConfig = {
|
|
|
126
124
|
Do not get to details, be concise.
|
|
127
125
|
If there is failed step, just write it in STEPS section.
|
|
128
126
|
If you have suggestions for the test, write them in SUMMARY section.
|
|
127
|
+
Do not be too technical in SUMMARY section.
|
|
129
128
|
Inside SUMMARY write exact values, if you have suggestions, explain which information you used to suggest.
|
|
130
129
|
Be concise, each section should not take more than one sentence.
|
|
131
130
|
|
|
132
131
|
Response format:
|
|
133
132
|
|
|
133
|
+
* SUMMARY <explanation_of_failure>
|
|
134
|
+
* ERROR <error_message_1>, <error_message_2>, ...
|
|
134
135
|
* CATEGORY <category_of_failure>
|
|
135
136
|
* STEPS <step_of_failure>
|
|
136
|
-
*
|
|
137
|
+
* URL <url_of_failure_if_any>
|
|
137
138
|
|
|
138
139
|
Do not add any other sections or explanations. Only CATEGORY, SUMMARY, STEPS.
|
|
139
140
|
${config.vision ? 'Also a screenshot of the page is attached to the prompt.' : ''}
|
|
@@ -153,11 +154,6 @@ const defaultConfig = {
|
|
|
153
154
|
})
|
|
154
155
|
}
|
|
155
156
|
|
|
156
|
-
messages.push({
|
|
157
|
-
role: 'assistant',
|
|
158
|
-
content: `## `,
|
|
159
|
-
})
|
|
160
|
-
|
|
161
157
|
return messages
|
|
162
158
|
},
|
|
163
159
|
},
|
|
@@ -340,12 +336,13 @@ function serializeTest(test) {
|
|
|
340
336
|
}
|
|
341
337
|
|
|
342
338
|
function formatResponse(response) {
|
|
343
|
-
if (!response.startsWith('##')) response = '## ' + response
|
|
344
339
|
return response
|
|
340
|
+
.replace(/<think>([\s\S]*?)<\/think>/g, store.debugMode ? colors.cyan('$1') : '')
|
|
345
341
|
.split('\n')
|
|
346
342
|
.map(line => line.trim())
|
|
347
343
|
.filter(line => !/^[A-Z\s]+$/.test(line))
|
|
348
344
|
.map(line => markdownToAnsi(line))
|
|
349
345
|
.map(line => line.replace(/^x /gm, ` ${colors.red.bold('x')} `))
|
|
350
346
|
.join('\n')
|
|
347
|
+
.trim()
|
|
351
348
|
}
|
package/lib/plugin/pageInfo.js
CHANGED
|
@@ -62,10 +62,7 @@ module.exports = function (config = {}) {
|
|
|
62
62
|
})
|
|
63
63
|
recorder.add('HTML snapshot failed test', async () => {
|
|
64
64
|
try {
|
|
65
|
-
const currentOutputLevel = output.level()
|
|
66
|
-
output.level(0)
|
|
67
65
|
const html = await helper.grabHTMLFrom('body')
|
|
68
|
-
output.level(currentOutputLevel)
|
|
69
66
|
|
|
70
67
|
if (!html) return
|
|
71
68
|
|
|
@@ -64,7 +64,7 @@ module.exports = function (config) {
|
|
|
64
64
|
}
|
|
65
65
|
|
|
66
66
|
if (Codeceptjs.container.mocha()) {
|
|
67
|
-
options.reportDir = Codeceptjs.container.mocha()
|
|
67
|
+
options.reportDir = Codeceptjs.container.mocha()?.options?.reporterOptions && Codeceptjs.container.mocha()?.options?.reporterOptions?.reportDir
|
|
68
68
|
}
|
|
69
69
|
|
|
70
70
|
if (options.disableScreenshots) {
|
|
@@ -72,11 +72,12 @@ module.exports = function (config) {
|
|
|
72
72
|
return
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
-
event.dispatcher.on(event.test.failed, test => {
|
|
76
|
-
if (
|
|
77
|
-
|
|
75
|
+
event.dispatcher.on(event.test.failed, (test, _err, hookName) => {
|
|
76
|
+
if (hookName === 'BeforeSuite' || hookName === 'AfterSuite') {
|
|
77
|
+
// no browser here
|
|
78
78
|
return
|
|
79
79
|
}
|
|
80
|
+
|
|
80
81
|
recorder.add(
|
|
81
82
|
'screenshot of failed test',
|
|
82
83
|
async () => {
|
|
@@ -139,12 +140,8 @@ module.exports = function (config) {
|
|
|
139
140
|
})
|
|
140
141
|
|
|
141
142
|
function _getUUID(test) {
|
|
142
|
-
if (test.
|
|
143
|
-
return test.
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
if (test.ctx && test.ctx.test.uuid) {
|
|
147
|
-
return test.ctx.test.uuid
|
|
143
|
+
if (test.uid) {
|
|
144
|
+
return test.uid
|
|
148
145
|
}
|
|
149
146
|
|
|
150
147
|
return Math.floor(new Date().getTime() / 1000)
|
|
@@ -121,12 +121,13 @@ module.exports = function (config) {
|
|
|
121
121
|
deleteDir(dir)
|
|
122
122
|
})
|
|
123
123
|
|
|
124
|
-
event.dispatcher.on(event.test.failed, (test,
|
|
125
|
-
if (
|
|
126
|
-
|
|
124
|
+
event.dispatcher.on(event.test.failed, (test, _err, hookName) => {
|
|
125
|
+
if (hookName === 'BeforeSuite' || hookName === 'AfterSuite') {
|
|
126
|
+
// no browser here
|
|
127
127
|
return
|
|
128
128
|
}
|
|
129
|
-
|
|
129
|
+
|
|
130
|
+
persist(test)
|
|
130
131
|
})
|
|
131
132
|
|
|
132
133
|
event.dispatcher.on(event.all.result, () => {
|
package/lib/step/func.js
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
const BaseStep = require('./base')
|
|
2
|
+
const store = require('../store')
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Function executed as a step
|
|
6
|
+
*/
|
|
7
|
+
class FuncStep extends BaseStep {
|
|
8
|
+
// this is actual function that should be executed within step
|
|
9
|
+
setCallable(fn) {
|
|
10
|
+
this.fn = fn
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// helper is optional, if we need to allow step to access helper methods
|
|
14
|
+
setHelper(helper) {
|
|
15
|
+
this.helper = helper
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
run() {
|
|
19
|
+
if (!this.fn) throw new Error('Function is not set')
|
|
20
|
+
|
|
21
|
+
// we wrap that function to track time and status
|
|
22
|
+
// and disable it in dry run mode
|
|
23
|
+
this.args = Array.prototype.slice.call(arguments)
|
|
24
|
+
this.startTime = +Date.now()
|
|
25
|
+
|
|
26
|
+
if (store.dryRun) {
|
|
27
|
+
this.setStatus('success')
|
|
28
|
+
// should we add Proxy and dry run resolver here?
|
|
29
|
+
return Promise.resolve(true)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
let result
|
|
33
|
+
try {
|
|
34
|
+
result = this.fn.apply(this.helper, this.args)
|
|
35
|
+
this.setStatus('success')
|
|
36
|
+
this.endTime = +Date.now()
|
|
37
|
+
} catch (err) {
|
|
38
|
+
this.endTime = +Date.now()
|
|
39
|
+
this.setStatus('failed')
|
|
40
|
+
throw err
|
|
41
|
+
}
|
|
42
|
+
return result
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
module.exports = FuncStep
|
package/lib/step.js
CHANGED
|
@@ -14,7 +14,13 @@ const Step = require('./step/helper')
|
|
|
14
14
|
*/
|
|
15
15
|
const MetaStep = require('./step/meta')
|
|
16
16
|
|
|
17
|
+
/**
|
|
18
|
+
* Step used to execute a single function
|
|
19
|
+
*/
|
|
20
|
+
const FuncStep = require('./step/func')
|
|
21
|
+
|
|
17
22
|
module.exports = Step
|
|
18
23
|
module.exports.MetaStep = MetaStep
|
|
19
24
|
module.exports.BaseStep = BaseStep
|
|
20
25
|
module.exports.StepConfig = StepConfig
|
|
26
|
+
module.exports.FuncStep = FuncStep
|
package/lib/store.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codeceptjs",
|
|
3
|
-
"version": "3.7.0-beta.
|
|
3
|
+
"version": "3.7.0-beta.9",
|
|
4
4
|
"description": "Supercharged End 2 End Testing Framework for NodeJS",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"acceptance",
|
|
@@ -79,7 +79,7 @@
|
|
|
79
79
|
"@cucumber/cucumber-expressions": "18",
|
|
80
80
|
"@cucumber/gherkin": "30",
|
|
81
81
|
"@cucumber/messages": "27.0.2",
|
|
82
|
-
"@xmldom/xmldom": "0.9.
|
|
82
|
+
"@xmldom/xmldom": "0.9.7",
|
|
83
83
|
"acorn": "8.14.0",
|
|
84
84
|
"arrify": "3.0.0",
|
|
85
85
|
"axios": "1.7.9",
|
|
@@ -93,6 +93,7 @@
|
|
|
93
93
|
"escape-string-regexp": "4.0.0",
|
|
94
94
|
"figures": "3.2.0",
|
|
95
95
|
"fn-args": "4.0.0",
|
|
96
|
+
"fs-extra": "11.3.0",
|
|
96
97
|
"fs-extra": "11.2.0",
|
|
97
98
|
"glob": "^11.0.1",
|
|
98
99
|
"fuse.js": "^7.0.0",
|
|
@@ -105,7 +106,7 @@
|
|
|
105
106
|
"lodash.merge": "4.6.2",
|
|
106
107
|
"mkdirp": "3.0.1",
|
|
107
108
|
"mocha": "11.1.0",
|
|
108
|
-
"monocart-coverage-reports": "2.
|
|
109
|
+
"monocart-coverage-reports": "2.12.0",
|
|
109
110
|
"ms": "2.1.3",
|
|
110
111
|
"ora-classic": "5.4.2",
|
|
111
112
|
"parse-function": "5.6.10",
|
|
@@ -133,7 +134,7 @@
|
|
|
133
134
|
"@wdio/sauce-service": "9.5.7",
|
|
134
135
|
"@wdio/selenium-standalone-service": "8.15.0",
|
|
135
136
|
"@wdio/utils": "9.5.0",
|
|
136
|
-
"@xmldom/xmldom": "0.9.
|
|
137
|
+
"@xmldom/xmldom": "0.9.7",
|
|
137
138
|
"chai": "^4.0.0",
|
|
138
139
|
"chai-as-promised": "7.1.2",
|
|
139
140
|
"chai-subset": "1.6.0",
|
|
@@ -154,7 +155,7 @@
|
|
|
154
155
|
"json-server": "0.17.4",
|
|
155
156
|
"playwright": "1.49.1",
|
|
156
157
|
"prettier": "^3.3.2",
|
|
157
|
-
"puppeteer": "24.
|
|
158
|
+
"puppeteer": "24.1.0",
|
|
158
159
|
"qrcode-terminal": "0.12.0",
|
|
159
160
|
"rosie": "2.1.1",
|
|
160
161
|
"runok": "0.9.3",
|