Skip to content

Commit 851e690

Browse files
author
DavertMik
committed
improved handling information between workers and main process
1 parent 5b4b9a6 commit 851e690

File tree

11 files changed

+97
-51
lines changed

11 files changed

+97
-51
lines changed

lib/command/run-workers.js

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -51,15 +51,6 @@ module.exports = async function (workerCount, selectedRuns, options) {
5151
})
5252

5353
workers.on(event.all.result, result => {
54-
event.dispatcher.emit(event.workers.result, {
55-
...result?.simplify(),
56-
suites: suiteArr,
57-
tests: {
58-
passed: passedTestArr,
59-
failed: failedTestArr,
60-
skipped: skippedTestArr,
61-
},
62-
})
6354
workers.printResults()
6455
})
6556

lib/command/workers/runTests.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -98,8 +98,8 @@ function initializeListeners() {
9898
event.dispatcher.on(event.step.passed, step => sendToParentThread({ event: event.step.passed, workerIndex, data: step.simplify() }))
9999
event.dispatcher.on(event.step.failed, step => sendToParentThread({ event: event.step.failed, workerIndex, data: step.simplify() }))
100100

101-
event.dispatcher.on(event.hook.failed, (test, err) => sendToParentThread({ event: event.hook.failed, workerIndex, data: { ...test.simplify(), err } }))
102-
event.dispatcher.on(event.hook.passed, (test, err) => sendToParentThread({ event: event.hook.passed, workerIndex, data: { ...test.simplify(), err } }))
101+
event.dispatcher.on(event.hook.failed, (hook, err) => sendToParentThread({ event: event.hook.failed, workerIndex, data: { ...hook.simplify(), err } }))
102+
event.dispatcher.on(event.hook.passed, (hook, err) => sendToParentThread({ event: event.hook.passed, workerIndex, data: { ...hook.simplify(), err } }))
103103

104104
event.dispatcher.once(event.all.after, () => {
105105
sendToParentThread({ event: event.all.after, workerIndex, data: container.result().simplify() })

lib/listener/result.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ module.exports = function () {
77
})
88

99
event.dispatcher.on(event.test.started, test => {
10-
container.result().addStats({ tests: 1 })
1110
container.result().addTest(test)
1211
})
1312
}

lib/mocha/hooks.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
const event = require('../event')
2+
const { serializeError } = require('../utils')
3+
// const { serializeTest } = require('./test')
24

35
class Hook {
46
constructor(context, error) {
@@ -13,6 +15,16 @@ class Hook {
1315
return this.constructor.name.replace('Hook', '')
1416
}
1517

18+
simplify() {
19+
return {
20+
hookName: this.hookName,
21+
title: this.title,
22+
// test: this.test ? serializeTest(this.test) : null,
23+
// suite: this.suite ? serializeSuite(this.suite) : null,
24+
error: this.error ? serializeError(this.error) : null,
25+
}
26+
}
27+
1628
toString() {
1729
return this.hookName
1830
}

lib/mocha/test.js

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
const Test = require('mocha/lib/test')
22
const Suite = require('mocha/lib/suite')
33
const { test: testWrapper } = require('./asyncWrapper')
4-
const { enhanceMochaSuite } = require('./suite')
4+
const { enhanceMochaSuite, createSuite } = require('./suite')
55
const { genTestId, serializeError, clearString } = require('../utils')
6-
6+
const Step = require('../step/base')
77
/**
88
* Factory function to create enhanced tests
99
* @param {string} title - Test title
@@ -67,15 +67,18 @@ function enhanceMochaTest(test) {
6767
}
6868

6969
function deserializeTest(test) {
70-
test = Object.assign(new Test(test.title || '', () => {}), test)
70+
test = Object.assign(
71+
createTest(test.title || '', () => {}),
72+
test,
73+
)
7174
test.parent = Object.assign(new Suite(test.parent.title), test.parent)
72-
enhanceMochaTest(test)
7375
enhanceMochaSuite(test.parent)
76+
test.steps = test.steps.map(step => Object.assign(new Step(step.title), step))
7477
return test
7578
}
7679

7780
function serializeTest(test, err = null) {
78-
test = { ...test }
81+
// test = { ...test }
7982

8083
if (test.start && !test.duration) {
8184
const end = +new Date()
@@ -107,14 +110,14 @@ function serializeTest(test, err = null) {
107110
uid: test.uid,
108111
retries: test._retries,
109112
title: test.title,
110-
status: test.status,
113+
state: test.state,
111114
notes: test.notes || [],
112115
meta: test.meta || {},
113-
artifacts: test.artifacts || [],
116+
artifacts: test.artifacts || {},
114117
duration: test.duration || 0,
115118
err,
116119
parent,
117-
steps: [...test.steps].map(step => step.simplify()),
120+
steps: [...test.steps].map(step => (step.simplify ? step.simplify() : step)),
118121
}
119122
}
120123

lib/plugin/analyze.js

Lines changed: 36 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
const debug = require('debug')('codeceptjs:analyze')
22
const { isMainThread } = require('node:worker_threads')
3+
const { arrowRight } = require('figures')
34
const container = require('../container')
45
const ai = require('../ai')
56
const colors = require('chalk')
@@ -16,10 +17,11 @@ const defaultConfig = {
1617
prompts: {
1718
clusterize: testsAndErrors => {
1819
const serializedFailedTests = testsAndErrors
19-
.map(({ test, error }, index) => {
20+
.map((test, index) => {
21+
if (!test || !test.err) return
2022
return `
2123
TEST #${index + 1}: ${serializeTest(test)}
22-
ERROR: ${serializeError(error).slice(0, MAX_DATA_LENGTH / testsAndErrors.length)}`
24+
ERROR: ${serializeError(test.err).slice(0, MAX_DATA_LENGTH / testsAndErrors.length)}`
2325
})
2426
.join('\n\n---\n\n')
2527

@@ -96,7 +98,7 @@ const defaultConfig = {
9698
9799
Pick one of the categories of failures and explain it.
98100
99-
Common causes of failures:
101+
Common causes of failures in order of priority:
100102
101103
* Browser connection error / browser crash
102104
* Network errors (server error, timeout, etc)
@@ -118,9 +120,9 @@ const defaultConfig = {
118120
If you have suggestions for the test, write them in SUMMARY section.
119121
Inside SUMMARY write exact values, if you have suggestions, explain which information you used to suggest.
120122
Be concise, each section should not take more than one sentence.
121-
123+
122124
Response format:
123-
125+
124126
CATEGORY <category_of_failure>
125127
STEPS <step_of_failure>
126128
SUMMARY <explanation_of_failure>
@@ -141,38 +143,46 @@ const defaultConfig = {
141143
module.exports = function (config = {}) {
142144
config = Object.assign(defaultConfig, config)
143145

144-
let failedTestsAndErrors = []
146+
event.dispatcher.on(event.all.result, async result => {
147+
if (!isMainThread) return // run only on main thread
148+
if (!ai.isEnabled) {
149+
console.log('AI is disabled, no analysis will be performed. Run tests with --ai flag to enable it.')
150+
return
151+
}
145152

146-
event.dispatcher.on(event.test.failed, (test, error) => {
147-
if (!ai.isEnabled) return
148-
failedTestsAndErrors.push({ test, error })
153+
printReport(result)
149154
})
150155

151-
event.dispatcher.on(event.all.result, async () => {
156+
event.dispatcher.on(event.workers.result, async result => {
152157
if (!ai.isEnabled) {
153158
console.log('AI is disabled, no analysis will be performed. Run tests with --ai flag to enable it.')
154159
return
155160
}
156161

162+
printReport(result)
163+
})
164+
165+
async function printReport(result) {
166+
const failedTestsAndErrors = result.tests.filter(t => t.state === 'failed' && t.err)
167+
157168
if (!failedTestsAndErrors.length) return
158-
if (!isMainThread) return // run only on main thread
159169

160-
debug(failedTestsAndErrors.map(e => serializeTest(e.test) + '\n' + serializeError(e.error)))
170+
debug(failedTestsAndErrors.map(t => serializeTest(t) + '\n' + serializeError(t.err)))
161171

162172
console.log()
163173
console.log(colors.bold.white('🪄 AI REPORT:'))
164174

165175
try {
166176
if (failedTestsAndErrors.length >= config.clusterize) {
167-
const response = await clusterize()
177+
const response = await clusterize(failedTestsAndErrors)
168178
console.log(response)
169179
return
170180
}
171181

172182
output.plugin('analyze', `Analyzing first ${config.analyze} failed tests...`)
173183

174184
const uniqueErrors = failedTestsAndErrors.filter((item, index, array) => {
175-
return array.findIndex(t => serializeError(t.error) === serializeError(item.error)) === index
185+
return array.findIndex(t => serializeError(t.err) === serializeError(item.err)) === index
176186
})
177187

178188
for (let i = 0; i < config.analyze; i++) {
@@ -185,19 +195,20 @@ module.exports = function (config = {}) {
185195

186196
console.log()
187197
console.log('--------------------------------')
188-
console.log(colors.bold.white(uniqueErrors[i].test.fullTitle()))
198+
console.log(arrowRight, colors.bold.white(uniqueErrors[i].fullTitle()))
199+
console.log()
189200
console.log(response)
190201
}
191202
} catch (err) {
192203
console.error('Error analyzing failed tests', err)
193204
}
194205

195-
if (!container.plugins('pageInfo')) {
206+
if (!Object.keys(container.plugins()).includes('pageInfo')) {
196207
console.log('To improve analysis, enable pageInfo plugin to get more context for failed tests.')
197208
}
198-
})
209+
}
199210

200-
async function clusterize() {
211+
async function clusterize(failedTestsAndErrors) {
201212
const spinner = ora('Clusterizing failures...').start()
202213
const prompt = config.prompts.clusterize(failedTestsAndErrors)
203214
try {
@@ -212,7 +223,7 @@ module.exports = function (config = {}) {
212223

213224
async function analyze(failedTestAndError) {
214225
const spinner = ora('Analyzing failure...').start()
215-
const prompt = config.prompts.analyze(failedTestAndError.test, failedTestAndError.error)
226+
const prompt = config.prompts.analyze(failedTestAndError, failedTestAndError.err)
216227
try {
217228
const response = await ai.createCompletion(prompt)
218229
spinner.stop()
@@ -225,6 +236,12 @@ module.exports = function (config = {}) {
225236
}
226237

227238
function serializeError(error) {
239+
if (typeof error === 'string') {
240+
return error
241+
}
242+
243+
if (!error) return
244+
228245
let errorMessage = 'ERROR: ' + error.message
229246

230247
if (error.inspect) {

lib/plugin/heal.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -117,10 +117,10 @@ module.exports = function (config = {}) {
117117
}
118118
})
119119

120-
event.dispatcher.on(event.workers.result, ({ tests }) => {
120+
event.dispatcher.on(event.workers.result, result => {
121121
const { print } = output
122122

123-
const healedTests = Object.values(tests)
123+
const healedTests = Object.values(result.tests)
124124
.flat()
125125
.filter(test => test.notes.some(note => note.type === 'heal'))
126126
if (!healedTests.length) return

lib/plugin/pageInfo.js

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -125,8 +125,6 @@ function pageStateToMarkdown(pageState) {
125125
markdown += '\n\n'
126126
}
127127

128-
markdown += `### ${ucfirst(humanizeString(key))}\n\n`
129-
130128
return markdown
131129
}
132130

lib/result.js

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
const fs = require('fs')
22
const path = require('path')
3+
const { serializeTest } = require('./mocha/test')
34

45
class Result {
56
constructor() {
@@ -52,6 +53,12 @@ class Result {
5253
}
5354

5455
addTest(test) {
56+
const existingTestIndex = this._tests.findIndex(t => !!t.uid && t.uid === test.uid)
57+
if (existingTestIndex >= 0) {
58+
this._tests[existingTestIndex] = test
59+
return
60+
}
61+
5562
this._tests.push(test)
5663
}
5764

@@ -72,12 +79,24 @@ class Result {
7279
return this._endTime ? +this._endTime - +this._startTime : 0
7380
}
7481

82+
get failedTests() {
83+
return this._tests.filter(test => test.state === 'failed')
84+
}
85+
86+
get passedTests() {
87+
return this._tests.filter(test => test.state === 'passed')
88+
}
89+
90+
get skippedTests() {
91+
return this._tests.filter(test => test.state === 'skipped' || test.state === 'pending')
92+
}
93+
7594
simplify() {
7695
return {
7796
hasFailed: this.hasFailed,
7897
stats: this.stats,
7998
duration: this.duration,
80-
tests: this._tests.map(test => test.simplify()),
99+
tests: this._tests.map(test => serializeTest(test)),
81100
failures: this._failures,
82101
}
83102
}

lib/step/base.js

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -185,22 +185,23 @@ class Step {
185185
if (step.args) {
186186
for (const arg of step.args) {
187187
// check if arg is a JOI object
188-
if (arg && arg.$_root) {
189-
args.push(JSON.stringify(arg).slice(0, 300))
190-
} else if (arg && typeof arg === 'function') {
188+
if (arg && typeof arg === 'function') {
191189
args.push(arg.name)
192-
} else {
190+
} else if (typeof arg == 'string') {
193191
args.push(arg)
192+
} else {
193+
args.push(JSON.stringify(arg).slice(0, 300))
194194
}
195195
}
196196
}
197197

198198
return {
199199
opts: step.opts || {},
200200
title: step.name,
201-
args: JSON.stringify(args),
201+
args: args,
202202
status: step.status,
203-
duration: step.duration || 0,
203+
startTime: step.startTime,
204+
endTime: step.endTime,
204205
parent,
205206
}
206207
}

0 commit comments

Comments
 (0)