-
-
Notifications
You must be signed in to change notification settings - Fork 736
/
Copy pathpageInfo.js
140 lines (119 loc) · 3.92 KB
/
pageInfo.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
const path = require('path')
const fs = require('fs')
const Container = require('../container')
const recorder = require('../recorder')
const event = require('../event')
const supportedHelpers = Container.STANDARD_ACTING_HELPERS
const { scanForErrorMessages } = require('../html')
const { output } = require('..')
const { humanizeString, ucfirst } = require('../utils')
const { testToFileName } = require('../mocha/test')
const defaultConfig = {
errorClasses: ['error', 'warning', 'alert', 'danger'],
browserLogs: ['error'],
}
/**
* Collects information from web page after each failed test and adds it to the test as an artifact.
* It is suggested to enable this plugin if you run tests on CI and you need to debug failed tests.
* This plugin can be paired with `analyze` plugin to provide more context.
*
* It collects URL, HTML errors (by classes), and browser logs.
*
* Enable this plugin in config:
*
* ```js
* plugins: {
* pageInfo: {
* enabled: true,
* }
* ```
*
* Additional config options:
*
* * `errorClasses` - list of classes to search for errors (default: `['error', 'warning', 'alert', 'danger']`)
* * `browserLogs` - list of types of errors to search for in browser logs (default: `['error']`)
*
*/
module.exports = function (config = {}) {
const helpers = Container.helpers()
let helper
config = Object.assign(defaultConfig, config)
for (const helperName of supportedHelpers) {
if (Object.keys(helpers).indexOf(helperName) > -1) {
helper = helpers[helperName]
}
}
if (!helper) return // no helpers for screenshot
event.dispatcher.on(event.test.failed, test => {
const pageState = {}
recorder.add('URL of failed test', async () => {
try {
const url = await helper.grabCurrentUrl()
pageState.url = url
} catch (err) {
// not really needed
}
})
recorder.add('HTML snapshot failed test', async () => {
try {
const html = await helper.grabHTMLFrom('body')
if (!html) return
const errors = scanForErrorMessages(html, config.errorClasses)
if (errors.length) {
output.debug('Detected errors in HTML code')
errors.forEach(error => output.debug(error))
pageState.htmlErrors = errors
}
} catch (err) {
// not really needed
}
})
recorder.add('Browser logs for failed test', async () => {
try {
const logs = await helper.grabBrowserLogs()
if (!logs) return
pageState.browserErrors = getBrowserErrors(logs, config.browserLogs)
} catch (err) {
// not really needed
}
})
recorder.add('Save page info', () => {
test.addNote('pageInfo', pageStateToMarkdown(pageState))
const pageStateFileName = path.join(global.output_dir, `${testToFileName(test)}.pageInfo.md`)
fs.writeFileSync(pageStateFileName, pageStateToMarkdown(pageState))
test.artifacts.pageInfo = pageStateFileName
return pageState
})
})
}
function pageStateToMarkdown(pageState) {
let markdown = ''
for (const [key, value] of Object.entries(pageState)) {
if (!value) continue
let result = ''
if (Array.isArray(value)) {
result = value.map(v => `- ${JSON.stringify(v, null, 2)}`).join('\n')
} else if (typeof value === 'string') {
result = `${value}`
} else {
result = JSON.stringify(value, null, 2)
}
if (!result.trim()) continue
markdown += `### ${ucfirst(humanizeString(key))}\n\n`
markdown += result
markdown += '\n\n'
}
return markdown
}
function getBrowserErrors(logs, type = ['error']) {
// Playwright & WebDriver console messages
let errors = logs
.map(log => {
if (typeof log === 'string') return log
if (!log.type) return null
return { type: log.type(), text: log.text() }
})
.filter(l => l && (typeof l === 'string' || type.includes(l.type)))
.map(l => (typeof l === 'string' ? l : l.text))
return errors
}