Skip to content

Commit ba64d2c

Browse files
DavertMikDavertMik
and
DavertMik
authored
better hooks handling by submitting where test.failed occur (#4782)
* better hooks handling by submitting where test.failed occur * reverted autologin plugin changes * small fix to screenshot failures * fixed screenshot tests * fixed PW tests --------- Co-authored-by: DavertMik <davert@testomat.io>
1 parent 2534959 commit ba64d2c

File tree

12 files changed

+72
-46
lines changed

12 files changed

+72
-46
lines changed

lib/helper/Playwright.js

+7-1
Original file line numberDiff line numberDiff line change
@@ -523,12 +523,17 @@ class Playwright extends Helper {
523523
this.currentRunningTest.artifacts.har = fileName
524524
contextOptions.recordHar = this.options.recordHar
525525
}
526+
527+
// load pre-saved cookies
528+
if (test?.opts?.cookies) contextOptions.storageState = { cookies: test.opts.cookies }
529+
526530
if (this.storageState) contextOptions.storageState = this.storageState
527531
if (this.options.userAgent) contextOptions.userAgent = this.options.userAgent
528532
if (this.options.locale) contextOptions.locale = this.options.locale
529533
if (this.options.colorScheme) contextOptions.colorScheme = this.options.colorScheme
530534
this.contextOptions = contextOptions
531535
if (!this.browserContext || !restartsSession()) {
536+
this.debugSection('New Session', JSON.stringify(this.contextOptions))
532537
this.browserContext = await this.browser.newContext(this.contextOptions) // Adding the HTTPSError ignore in the context so that we can ignore those errors
533538
}
534539
}
@@ -938,7 +943,8 @@ class Playwright extends Helper {
938943
throw new Error('Cannot open pages inside an Electron container')
939944
}
940945
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}`)
942948
}
943949

944950
if (this.options.basicAuth && this.isAuthenticated !== true) {

lib/listener/store.js

+9-1
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,19 @@ const event = require('../event')
22
const store = require('../store')
33

44
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+
513
event.dispatcher.on(event.test.before, test => {
614
store.currentTest = test
715
})
816

9-
event.dispatcher.on(event.test.finished, test => {
17+
event.dispatcher.on(event.test.finished, () => {
1018
store.currentTest = null
1119
})
1220
}

lib/mocha/asyncWrapper.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,10 @@ const injectHook = function (inject, suite) {
1919
return recorder.promise()
2020
}
2121

22-
function suiteTestFailedHookError(suite, err) {
22+
function suiteTestFailedHookError(suite, err, hookName) {
2323
suite.eachTest(test => {
2424
test.err = err
25-
event.emit(event.test.failed, test, err)
25+
event.emit(event.test.failed, test, err, ucfirst(hookName))
2626
})
2727
}
2828

@@ -120,7 +120,7 @@ module.exports.injected = function (fn, suite, hookName) {
120120
const errHandler = err => {
121121
recorder.session.start('teardown')
122122
recorder.cleanAsyncErr()
123-
if (hookName == 'before' || hookName == 'beforeSuite') suiteTestFailedHookError(suite, err)
123+
if (hookName == 'before' || hookName == 'beforeSuite') suiteTestFailedHookError(suite, err, hookName)
124124
if (hookName === 'after') event.emit(event.test.after, suite)
125125
if (hookName === 'afterSuite') event.emit(event.suite.after, suite)
126126
recorder.add(() => doneFn(err))

lib/mocha/cli.js

+15-7
Original file line numberDiff line numberDiff line change
@@ -198,17 +198,25 @@ class Cli extends Base {
198198
// add new line before the message
199199
err.message = '\n ' + err.message
200200

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+
201206
const steps = test.steps || (test.ctx && test.ctx.test.steps)
202207

203208
if (steps && steps.length) {
204209
let scenarioTrace = ''
205-
steps.reverse().forEach(step => {
206-
const hasFailed = step.status === 'failed'
207-
let line = `${hasFailed ? output.styles.bold(figures.cross) : figures.tick} ${step.toCode()} ${step.line()}`
208-
if (hasFailed) line = output.styles.bold(line)
209-
scenarioTrace += `\n${line}`
210-
})
211-
log += `${output.styles.basic(figures.circle)} ${output.styles.section('Scenario Steps')}:${scenarioTrace}\n`
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`
212220
}
213221

214222
// display artifacts in debug mode

lib/plugin/analyze.js

+9-11
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ const { ansiRegExp, base64EncodeFile, markdownToAnsi } = require('../utils')
1212
const MAX_DATA_LENGTH = 5000
1313

1414
const defaultConfig = {
15-
clusterize: 2,
16-
analyze: 3,
15+
clusterize: 5,
16+
analyze: 2,
1717
vision: false,
1818
categories: [
1919
'Browser connection error / browser crash',
@@ -64,17 +64,18 @@ const defaultConfig = {
6464
If you identify that all tests in the group have the same tag, add this tag to the group report, otherwise ignore TAG section.
6565
If you identify that all tests in the group have the same suite, add this suite to the group report, otherwise ignore SUITE section.
6666
Pick different emojis for each group.
67-
Do not include group into report if it has only one test in affected tests section.
67+
Order groups by the number of tests in the group.
68+
If group has one test, skip that group.
6869
6970
Provide list of groups in following format:
7071
7172
_______________________________
7273
73-
## Group <group_number>
74+
## Group <group_number> <emoji>
7475
76+
* SUMMARY <summary_of_errors>
7577
* CATEGORY <category_of_failure>
7678
* ERROR <error_message_1>, <error_message_2>, ...
77-
* SUMMARY <summary_of_errors>
7879
* STEP <step_of_failure> (use CodeceptJS format I.click(), I.see(), etc; if all failures happend on the same step)
7980
* SUITE <suite_title>, <suite_title> (if SUITE is present, and if all tests in the group have the same suite or suites)
8081
* TAG <tag> (if TAG is present, and if all tests in the group have the same tag)
@@ -126,14 +127,16 @@ const defaultConfig = {
126127
Do not get to details, be concise.
127128
If there is failed step, just write it in STEPS section.
128129
If you have suggestions for the test, write them in SUMMARY section.
130+
Do not be too technical in SUMMARY section.
129131
Inside SUMMARY write exact values, if you have suggestions, explain which information you used to suggest.
130132
Be concise, each section should not take more than one sentence.
131133
132134
Response format:
133135
136+
* SUMMARY <explanation_of_failure>
137+
* ERROR <error_message_1>, <error_message_2>, ...
134138
* CATEGORY <category_of_failure>
135139
* STEPS <step_of_failure>
136-
* SUMMARY <explanation_of_failure>
137140
138141
Do not add any other sections or explanations. Only CATEGORY, SUMMARY, STEPS.
139142
${config.vision ? 'Also a screenshot of the page is attached to the prompt.' : ''}
@@ -153,11 +156,6 @@ const defaultConfig = {
153156
})
154157
}
155158

156-
messages.push({
157-
role: 'assistant',
158-
content: `## `,
159-
})
160-
161159
return messages
162160
},
163161
},

lib/plugin/pageInfo.js

-3
Original file line numberDiff line numberDiff line change
@@ -62,10 +62,7 @@ module.exports = function (config = {}) {
6262
})
6363
recorder.add('HTML snapshot failed test', async () => {
6464
try {
65-
const currentOutputLevel = output.level()
66-
output.level(0)
6765
const html = await helper.grabHTMLFrom('body')
68-
output.level(currentOutputLevel)
6966

7067
if (!html) return
7168

lib/plugin/screenshotOnFail.js

+7-10
Original file line numberDiff line numberDiff line change
@@ -64,19 +64,20 @@ module.exports = function (config) {
6464
}
6565

6666
if (Codeceptjs.container.mocha()) {
67-
options.reportDir = Codeceptjs.container.mocha().options.reporterOptions && Codeceptjs.container.mocha().options.reporterOptions.reportDir
67+
options.reportDir = Codeceptjs.container.mocha()?.options?.reporterOptions && Codeceptjs.container.mocha()?.options?.reporterOptions?.reportDir
6868
}
6969

7070
if (options.disableScreenshots) {
7171
// old version of disabling screenshots
7272
return
7373
}
7474

75-
event.dispatcher.on(event.test.failed, test => {
76-
if (test.ctx?._runnable.title.includes('hook: ')) {
77-
output.plugin('screenshotOnFail', 'BeforeSuite/AfterSuite do not have any access to the browser, hence it could not take screenshot.')
75+
event.dispatcher.on(event.test.failed, (test, _err, hookName) => {
76+
if (hookName === 'BeforeSuite' || hookName === 'AfterSuite') {
77+
// no browser here
7878
return
7979
}
80+
8081
recorder.add(
8182
'screenshot of failed test',
8283
async () => {
@@ -139,12 +140,8 @@ module.exports = function (config) {
139140
})
140141

141142
function _getUUID(test) {
142-
if (test.uuid) {
143-
return test.uuid
144-
}
145-
146-
if (test.ctx && test.ctx.test.uuid) {
147-
return test.ctx.test.uuid
143+
if (test.uid) {
144+
return test.uid
148145
}
149146

150147
return Math.floor(new Date().getTime() / 1000)

lib/plugin/stepByStepReport.js

+5-4
Original file line numberDiff line numberDiff line change
@@ -121,12 +121,13 @@ module.exports = function (config) {
121121
deleteDir(dir)
122122
})
123123

124-
event.dispatcher.on(event.test.failed, (test, err) => {
125-
if (test.ctx._runnable.title.includes('hook: ')) {
126-
output.plugin('stepByStepReport', 'BeforeSuite/AfterSuite do not have any access to the browser, hence it could not take screenshot.')
124+
event.dispatcher.on(event.test.failed, (test, _err, hookName) => {
125+
if (hookName === 'BeforeSuite' || hookName === 'AfterSuite') {
126+
// no browser here
127127
return
128128
}
129-
persist(test, err)
129+
130+
persist(test)
130131
})
131132

132133
event.dispatcher.on(event.all.result, () => {

lib/step.js

+6
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,13 @@ const Step = require('./step/helper')
1414
*/
1515
const MetaStep = require('./step/meta')
1616

17+
/**
18+
* Step used to execute a single function
19+
*/
20+
const FuncStep = require('./step/func')
21+
1722
module.exports = Step
1823
module.exports.MetaStep = MetaStep
1924
module.exports.BaseStep = BaseStep
2025
module.exports.StepConfig = StepConfig
26+
module.exports.FuncStep = FuncStep

lib/store.js

+2
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ const store = {
1515
currentTest: null,
1616
/** @type {any} */
1717
currentStep: null,
18+
/** @type {CodeceptJS.Suite | null} */
19+
currentSuite: null,
1820
}
1921

2022
module.exports = store

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "codeceptjs",
3-
"version": "3.7.0-beta.1",
3+
"version": "3.7.0-beta.8",
44
"description": "Supercharged End 2 End Testing Framework for NodeJS",
55
"keywords": [
66
"acceptance",

test/unit/plugin/screenshotOnFail_test.js

+8-5
Original file line numberDiff line numberDiff line change
@@ -49,14 +49,17 @@ describe('screenshotOnFail', () => {
4949

5050
it('should create screenshot with unique name', async () => {
5151
screenshotOnFail({ uniqueScreenshotNames: true })
52-
event.dispatcher.emit(event.test.failed, { title: 'test1', uuid: 1 })
52+
53+
const test = { title: 'test1', uid: 1 }
54+
event.dispatcher.emit(event.test.failed, test)
5355
await recorder.promise()
5456
expect(screenshotSaved.called).is.ok
55-
expect('test1_1.failed.png').is.equal(screenshotSaved.getCall(0).args[0])
57+
expect(`test1_${test.uid}.failed.png`).is.equal(screenshotSaved.getCall(0).args[0])
5658
})
5759

58-
it('should create screenshot with unique name when uuid is null', async () => {
60+
it('should create screenshot with unique name when uid is null', async () => {
5961
screenshotOnFail({ uniqueScreenshotNames: true })
62+
6063
event.dispatcher.emit(event.test.failed, { title: 'test1' })
6164
await recorder.promise()
6265
expect(screenshotSaved.called).is.ok
@@ -67,14 +70,14 @@ describe('screenshotOnFail', () => {
6770

6871
it('should not save screenshot in BeforeSuite', async () => {
6972
screenshotOnFail({ uniqueScreenshotNames: true })
70-
event.dispatcher.emit(event.test.failed, { title: 'test1', ctx: { _runnable: { title: 'hook: BeforeSuite' } } })
73+
event.dispatcher.emit(event.test.failed, { title: 'test1' }, null, 'BeforeSuite')
7174
await recorder.promise()
7275
expect(!screenshotSaved.called).is.ok
7376
})
7477

7578
it('should not save screenshot in AfterSuite', async () => {
7679
screenshotOnFail({ uniqueScreenshotNames: true })
77-
event.dispatcher.emit(event.test.failed, { title: 'test1', ctx: { _runnable: { title: 'hook: AfterSuite' } } })
80+
event.dispatcher.emit(event.test.failed, { title: 'test1' }, null, 'AfterSuite')
7881
await recorder.promise()
7982
expect(!screenshotSaved.called).is.ok
8083
})

0 commit comments

Comments
 (0)