codeceptjs 3.6.10 → 3.7.0-beta.10

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.
Files changed (127) hide show
  1. package/README.md +89 -119
  2. package/bin/codecept.js +9 -2
  3. package/docs/webapi/clearCookie.mustache +1 -1
  4. package/lib/actor.js +66 -102
  5. package/lib/ai.js +130 -121
  6. package/lib/assert/empty.js +3 -5
  7. package/lib/assert/equal.js +4 -7
  8. package/lib/assert/include.js +4 -6
  9. package/lib/assert/throws.js +2 -4
  10. package/lib/assert/truth.js +2 -2
  11. package/lib/codecept.js +87 -83
  12. package/lib/command/check.js +186 -0
  13. package/lib/command/configMigrate.js +2 -4
  14. package/lib/command/definitions.js +8 -26
  15. package/lib/command/generate.js +10 -14
  16. package/lib/command/gherkin/snippets.js +10 -8
  17. package/lib/command/gherkin/steps.js +1 -1
  18. package/lib/command/info.js +1 -3
  19. package/lib/command/init.js +8 -12
  20. package/lib/command/interactive.js +2 -2
  21. package/lib/command/list.js +1 -1
  22. package/lib/command/run-multiple.js +12 -35
  23. package/lib/command/run-workers.js +5 -57
  24. package/lib/command/utils.js +5 -6
  25. package/lib/command/workers/runTests.js +68 -232
  26. package/lib/container.js +354 -237
  27. package/lib/data/context.js +10 -13
  28. package/lib/data/dataScenarioConfig.js +8 -8
  29. package/lib/data/dataTableArgument.js +6 -6
  30. package/lib/data/table.js +5 -11
  31. package/lib/effects.js +218 -0
  32. package/lib/els.js +158 -0
  33. package/lib/event.js +19 -17
  34. package/lib/heal.js +88 -80
  35. package/lib/helper/AI.js +2 -1
  36. package/lib/helper/ApiDataFactory.js +3 -6
  37. package/lib/helper/Appium.js +45 -51
  38. package/lib/helper/FileSystem.js +3 -3
  39. package/lib/helper/GraphQLDataFactory.js +3 -3
  40. package/lib/helper/JSONResponse.js +57 -37
  41. package/lib/helper/Nightmare.js +35 -53
  42. package/lib/helper/Playwright.js +211 -252
  43. package/lib/helper/Protractor.js +54 -77
  44. package/lib/helper/Puppeteer.js +139 -232
  45. package/lib/helper/REST.js +5 -17
  46. package/lib/helper/TestCafe.js +21 -44
  47. package/lib/helper/WebDriver.js +131 -169
  48. package/lib/helper/testcafe/testcafe-utils.js +26 -27
  49. package/lib/listener/emptyRun.js +55 -0
  50. package/lib/listener/exit.js +7 -10
  51. package/lib/listener/{retry.js → globalRetry.js} +5 -5
  52. package/lib/listener/globalTimeout.js +165 -0
  53. package/lib/listener/helpers.js +15 -15
  54. package/lib/listener/mocha.js +1 -1
  55. package/lib/listener/result.js +12 -0
  56. package/lib/listener/steps.js +20 -18
  57. package/lib/listener/store.js +20 -0
  58. package/lib/mocha/asyncWrapper.js +216 -0
  59. package/lib/{interfaces → mocha}/bdd.js +3 -3
  60. package/lib/mocha/cli.js +308 -0
  61. package/lib/mocha/factory.js +104 -0
  62. package/lib/{interfaces → mocha}/featureConfig.js +24 -12
  63. package/lib/{interfaces → mocha}/gherkin.js +26 -28
  64. package/lib/mocha/hooks.js +112 -0
  65. package/lib/mocha/index.js +12 -0
  66. package/lib/mocha/inject.js +29 -0
  67. package/lib/{interfaces → mocha}/scenarioConfig.js +21 -6
  68. package/lib/mocha/suite.js +81 -0
  69. package/lib/mocha/test.js +159 -0
  70. package/lib/mocha/types.d.ts +42 -0
  71. package/lib/mocha/ui.js +219 -0
  72. package/lib/output.js +82 -62
  73. package/lib/pause.js +155 -138
  74. package/lib/plugin/analyze.js +349 -0
  75. package/lib/plugin/autoDelay.js +6 -6
  76. package/lib/plugin/autoLogin.js +6 -7
  77. package/lib/plugin/commentStep.js +6 -1
  78. package/lib/plugin/coverage.js +10 -19
  79. package/lib/plugin/customLocator.js +3 -3
  80. package/lib/plugin/customReporter.js +52 -0
  81. package/lib/plugin/eachElement.js +1 -1
  82. package/lib/plugin/fakerTransform.js +1 -1
  83. package/lib/plugin/heal.js +36 -9
  84. package/lib/plugin/pageInfo.js +140 -0
  85. package/lib/plugin/retryFailedStep.js +4 -4
  86. package/lib/plugin/retryTo.js +18 -118
  87. package/lib/plugin/screenshotOnFail.js +17 -49
  88. package/lib/plugin/selenoid.js +15 -35
  89. package/lib/plugin/standardActingHelpers.js +4 -1
  90. package/lib/plugin/stepByStepReport.js +56 -17
  91. package/lib/plugin/stepTimeout.js +5 -12
  92. package/lib/plugin/subtitles.js +4 -4
  93. package/lib/plugin/tryTo.js +17 -107
  94. package/lib/plugin/wdio.js +8 -10
  95. package/lib/recorder.js +146 -125
  96. package/lib/rerun.js +43 -42
  97. package/lib/result.js +161 -0
  98. package/lib/secret.js +1 -1
  99. package/lib/step/base.js +228 -0
  100. package/lib/step/config.js +50 -0
  101. package/lib/step/func.js +46 -0
  102. package/lib/step/helper.js +50 -0
  103. package/lib/step/meta.js +99 -0
  104. package/lib/step/record.js +74 -0
  105. package/lib/step/retry.js +11 -0
  106. package/lib/step/section.js +55 -0
  107. package/lib/step.js +21 -332
  108. package/lib/steps.js +50 -0
  109. package/lib/store.js +10 -2
  110. package/lib/template/heal.js +2 -11
  111. package/lib/timeout.js +66 -0
  112. package/lib/utils.js +317 -216
  113. package/lib/within.js +73 -55
  114. package/lib/workers.js +259 -275
  115. package/package.json +56 -54
  116. package/typings/index.d.ts +175 -186
  117. package/typings/promiseBasedTypes.d.ts +164 -17
  118. package/typings/types.d.ts +284 -115
  119. package/lib/cli.js +0 -256
  120. package/lib/helper/ExpectHelper.js +0 -391
  121. package/lib/helper/SoftExpectHelper.js +0 -381
  122. package/lib/listener/artifacts.js +0 -19
  123. package/lib/listener/timeout.js +0 -109
  124. package/lib/mochaFactory.js +0 -113
  125. package/lib/plugin/debugErrors.js +0 -67
  126. package/lib/scenario.js +0 -224
  127. package/lib/ui.js +0 -236
@@ -0,0 +1,42 @@
1
+ import { Test as MochaTest, Suite as MochaSuite } from 'mocha'
2
+
3
+ declare global {
4
+ namespace CodeceptJS {
5
+ interface Test extends MochaTest {
6
+ uid: string
7
+ title: string
8
+ tags: string[]
9
+ steps: string[]
10
+ meta: Record<string, any>
11
+ notes: Array<{
12
+ type: string
13
+ text: string
14
+ }>
15
+ state: string
16
+ err?: Error
17
+ config: Record<string, any>
18
+ artifacts: string[]
19
+ inject: Record<string, any>
20
+ opts: Record<string, any>
21
+ throws?: Error | string | RegExp | Function
22
+ totalTimeout?: number
23
+ relativeFile?: string
24
+ addToSuite(suite: Mocha.Suite): void
25
+ applyOptions(opts: Record<string, any>): void
26
+ simplify(): Record<string, any>
27
+ toFileName(): string
28
+ addNote(type: string, note: string): void
29
+ codeceptjs: boolean
30
+ }
31
+
32
+ interface Suite extends MochaSuite {
33
+ title: string
34
+ tags: string[]
35
+ opts: Record<string, any>
36
+ totalTimeout?: number
37
+ addTest(test: Test): void
38
+ applyOptions(opts: Record<string, any>): void
39
+ codeceptjs: boolean
40
+ }
41
+ }
42
+ }
@@ -0,0 +1,219 @@
1
+ const escapeRe = require('escape-string-regexp')
2
+ const { test, setup, teardown, suiteSetup, suiteTeardown, injected } = require('./asyncWrapper')
3
+ const ScenarioConfig = require('./scenarioConfig')
4
+ const FeatureConfig = require('./featureConfig')
5
+ const addDataContext = require('../data/context')
6
+ const { createTest } = require('./test')
7
+ const { createSuite } = require('./suite')
8
+ const { HookConfig, AfterSuiteHook, AfterHook, BeforeSuiteHook, BeforeHook } = require('./hooks')
9
+
10
+ const setContextTranslation = context => {
11
+ const container = require('../container')
12
+ const contexts = container.translation().value('contexts')
13
+
14
+ if (contexts) {
15
+ for (const key of Object.keys(contexts)) {
16
+ if (context[key]) {
17
+ context[contexts[key]] = context[key]
18
+ }
19
+ }
20
+ }
21
+ }
22
+
23
+ /**
24
+ * Codecept-style interface:
25
+ *
26
+ * Feature('login');
27
+ *
28
+ * Scenario('login as regular user', ({I}) {
29
+ * I.fillField();
30
+ * I.click();
31
+ * I.see('Hello, '+data.login);
32
+ * });
33
+ *
34
+ * @param {Mocha.Suite} suite Root suite.
35
+ * @ignore
36
+ */
37
+ module.exports = function (suite) {
38
+ const suites = [suite]
39
+ suite.timeout(0)
40
+ let afterAllHooks
41
+ let afterEachHooks
42
+ let afterAllHooksAreLoaded
43
+ let afterEachHooksAreLoaded
44
+
45
+ suite.on('pre-require', (context, file, mocha) => {
46
+ const common = require('mocha/lib/interfaces/common')(suites, context, mocha)
47
+
48
+ const addScenario = function (title, opts = {}, fn) {
49
+ const suite = suites[0]
50
+
51
+ if (typeof opts === 'function' && !fn) {
52
+ fn = opts
53
+ opts = {}
54
+ }
55
+ if (suite.pending) {
56
+ fn = null
57
+ }
58
+ const test = createTest(title, fn)
59
+ test.file = file
60
+ test.addToSuite(suite)
61
+ test.applyOptions(opts)
62
+
63
+ return new ScenarioConfig(test)
64
+ }
65
+
66
+ // create dispatcher
67
+
68
+ context.BeforeAll = common.before
69
+ context.AfterAll = common.after
70
+
71
+ context.run = mocha.options.delay && common.runWithSuite(suite)
72
+ /**
73
+ * Describe a "suite" with the given `title`
74
+ * and callback `fn` containing nested suites
75
+ * and/or tests.
76
+ * @global
77
+ * @param {string} title
78
+ * @param {Object<string, *>} [opts]
79
+ * @returns {FeatureConfig}
80
+ */
81
+
82
+ context.Feature = function (title, opts) {
83
+ if (suites.length > 1) {
84
+ suites.shift()
85
+ }
86
+
87
+ afterAllHooks = []
88
+ afterEachHooks = []
89
+ afterAllHooksAreLoaded = false
90
+ afterEachHooksAreLoaded = false
91
+
92
+ const suite = createSuite(suites[0], title)
93
+ suite.applyOptions(opts)
94
+
95
+ suite.file = file
96
+ suites.unshift(suite)
97
+ suite.beforeEach('codeceptjs.before', () => setup(suite))
98
+ afterEachHooks.push(['finalize codeceptjs', () => teardown(suite)])
99
+
100
+ suite.beforeAll('codeceptjs.beforeSuite', () => suiteSetup(suite))
101
+ afterAllHooks.push(['codeceptjs.afterSuite', () => suiteTeardown(suite)])
102
+
103
+ return new FeatureConfig(suite)
104
+ }
105
+
106
+ /**
107
+ * Pending test suite.
108
+ * @global
109
+ * @kind constant
110
+ * @type {CodeceptJS.IFeature}
111
+ */
112
+ context.xFeature = context.Feature.skip = function (title, opts) {
113
+ const skipInfo = {
114
+ skipped: true,
115
+ message: 'Skipped due to "skip" on Feature.',
116
+ }
117
+ return context.Feature(title, { ...opts, skipInfo })
118
+ }
119
+
120
+ context.BeforeSuite = function (fn) {
121
+ suites[0].beforeAll('BeforeSuite', injected(fn, suites[0], 'beforeSuite'))
122
+ return new HookConfig(new BeforeSuiteHook({ suite: suites[0] }))
123
+ }
124
+
125
+ context.AfterSuite = function (fn) {
126
+ afterAllHooks.unshift(['AfterSuite', injected(fn, suites[0], 'afterSuite')])
127
+ return new HookConfig(new AfterSuiteHook({ suite: suites[0] }))
128
+ }
129
+
130
+ context.Background = context.Before = function (fn) {
131
+ suites[0].beforeEach('Before', injected(fn, suites[0], 'before'))
132
+ return new HookConfig(new BeforeHook({ suite: suites[0] }))
133
+ }
134
+
135
+ context.After = function (fn) {
136
+ afterEachHooks.unshift(['After', injected(fn, suites[0], 'after')])
137
+ return new HookConfig(new AfterHook({ suite: suites[0] }))
138
+ }
139
+
140
+ /**
141
+ * Describe a specification or test-case
142
+ * with the given `title` and callback `fn`
143
+ * acting as a thunk.
144
+ * @ignore
145
+ */
146
+ context.Scenario = addScenario
147
+ /**
148
+ * Exclusive test-case.
149
+ * @ignore
150
+ */
151
+ context.Scenario.only = function (title, opts, fn) {
152
+ const reString = `^${escapeRe(`${suites[0].title}: ${title}`.replace(/( \| {.+})?$/g, ''))}`
153
+ mocha.grep(new RegExp(reString))
154
+ process.env.SCENARIO_ONLY = true
155
+ return addScenario(title, opts, fn)
156
+ }
157
+
158
+ /**
159
+ * Pending test case.
160
+ * @global
161
+ * @kind constant
162
+ * @type {CodeceptJS.IScenario}
163
+ */
164
+ context.xScenario = context.Scenario.skip = function (title, opts = {}, fn) {
165
+ if (typeof opts === 'function' && !fn) {
166
+ opts = {}
167
+ }
168
+
169
+ return context.Scenario(title, opts)
170
+ }
171
+
172
+ /**
173
+ * Pending test case with message: 'Test not implemented!'.
174
+ * @global
175
+ * @kind constant
176
+ * @type {CodeceptJS.IScenario}
177
+ */
178
+ context.Scenario.todo = function (title, opts = {}, fn) {
179
+ if (typeof opts === 'function' && !fn) {
180
+ fn = opts
181
+ opts = {}
182
+ }
183
+
184
+ const skipInfo = {
185
+ message: 'Test not implemented!',
186
+ description: fn ? fn.toString() : '',
187
+ }
188
+
189
+ return context.Scenario(title, { ...opts, skipInfo })
190
+ }
191
+
192
+ /**
193
+ * For translation
194
+ */
195
+
196
+ setContextTranslation(context)
197
+
198
+ addDataContext(context)
199
+ })
200
+
201
+ suite.on('post-require', () => {
202
+ /**
203
+ * load hooks from arrays to suite to prevent reordering
204
+ */
205
+ if (!afterEachHooksAreLoaded && Array.isArray(afterEachHooks)) {
206
+ afterEachHooks.forEach(hook => {
207
+ suites[0].afterEach(hook[0], hook[1])
208
+ })
209
+ afterEachHooksAreLoaded = true
210
+ }
211
+
212
+ if (!afterAllHooksAreLoaded && Array.isArray(afterAllHooks)) {
213
+ afterAllHooks.forEach(hook => {
214
+ suites[0].afterAll(hook[0], hook[1])
215
+ })
216
+ afterAllHooksAreLoaded = true
217
+ }
218
+ })
219
+ }
package/lib/output.js CHANGED
@@ -1,5 +1,5 @@
1
- const colors = require('chalk');
2
- const figures = require('figures');
1
+ const colors = require('chalk')
2
+ const figures = require('figures')
3
3
  const { maskSensitiveData } = require('invisi-data')
4
4
 
5
5
  const styles = {
@@ -10,11 +10,12 @@ const styles = {
10
10
  debug: colors.cyan,
11
11
  log: colors.grey,
12
12
  bold: colors.bold,
13
- };
13
+ section: colors.white.dim.bold,
14
+ }
14
15
 
15
- let outputLevel = 0;
16
- let outputProcess = '';
17
- let newline = true;
16
+ let outputLevel = 0
17
+ let outputProcess = ''
18
+ let newline = true
18
19
 
19
20
  /**
20
21
  * @alias output
@@ -28,7 +29,7 @@ module.exports = {
28
29
  stepShift: 0,
29
30
 
30
31
  standWithUkraine() {
31
- return `#${colors.bold.yellow('StandWith')}${colors.bold.cyan('Ukraine')}`;
32
+ return `#${colors.bold.yellow('StandWith')}${colors.bold.cyan('Ukraine')}`
32
33
  },
33
34
 
34
35
  /**
@@ -37,8 +38,8 @@ module.exports = {
37
38
  * @returns {number}
38
39
  */
39
40
  level(level) {
40
- if (level !== undefined) outputLevel = level;
41
- return outputLevel;
41
+ if (level !== undefined) outputLevel = level
42
+ return outputLevel
42
43
  },
43
44
 
44
45
  /**
@@ -48,9 +49,9 @@ module.exports = {
48
49
  * @returns {string}
49
50
  */
50
51
  process(process) {
51
- if (process === null) return outputProcess = '';
52
- if (process) outputProcess = String(process).length === 1 ? `[0${process}]` : `[${process}]`;
53
- return outputProcess;
52
+ if (process === null) return (outputProcess = '')
53
+ if (process) outputProcess = String(process).length === 1 ? `[0${process}]` : `[${process}]`
54
+ return outputProcess
54
55
  },
55
56
 
56
57
  /**
@@ -60,7 +61,7 @@ module.exports = {
60
61
  debug(msg) {
61
62
  const _msg = isMaskedData() ? maskSensitiveData(msg) : msg
62
63
  if (outputLevel >= 2) {
63
- print(' '.repeat(this.stepShift), styles.debug(`${figures.pointerSmall} ${_msg}`));
64
+ print(' '.repeat(this.stepShift), styles.debug(`${figures.pointerSmall} ${_msg}`))
64
65
  }
65
66
  },
66
67
 
@@ -71,7 +72,7 @@ module.exports = {
71
72
  log(msg) {
72
73
  const _msg = isMaskedData() ? maskSensitiveData(msg) : msg
73
74
  if (outputLevel >= 3) {
74
- print(' '.repeat(this.stepShift), styles.log(truncate(` ${_msg}`, this.spaceShift)));
75
+ print(' '.repeat(this.stepShift), styles.log(truncate(` ${_msg}`, this.spaceShift)))
75
76
  }
76
77
  },
77
78
 
@@ -80,7 +81,7 @@ module.exports = {
80
81
  * @param {string} msg
81
82
  */
82
83
  error(msg) {
83
- print(styles.error(msg));
84
+ print(styles.error(msg))
84
85
  },
85
86
 
86
87
  /**
@@ -88,7 +89,7 @@ module.exports = {
88
89
  * @param {string} msg
89
90
  */
90
91
  success(msg) {
91
- print(styles.success(msg));
92
+ print(styles.success(msg))
92
93
  },
93
94
 
94
95
  /**
@@ -97,7 +98,7 @@ module.exports = {
97
98
  * @param {string} msg
98
99
  */
99
100
  plugin(pluginName, msg = '') {
100
- this.debug(`<${pluginName}> ${msg}`);
101
+ this.debug(`<${pluginName}> ${msg}`)
101
102
  },
102
103
 
103
104
  /**
@@ -105,26 +106,26 @@ module.exports = {
105
106
  * @param {CodeceptJS.Step} step
106
107
  */
107
108
  step(step) {
108
- if (outputLevel === 0) return;
109
- if (!step) return;
109
+ if (outputLevel === 0) return
110
+ if (!step) return
110
111
  // Avoid to print non-gherkin steps, when gherkin is running for --steps mode
111
112
  if (outputLevel === 1) {
112
113
  if (typeof step === 'object' && step.hasBDDAncestor()) {
113
- return;
114
+ return
114
115
  }
115
116
  }
116
117
 
117
- let stepLine = step.toString();
118
+ let stepLine = step.toCliStyled()
118
119
  if (step.metaStep && outputLevel >= 1) {
119
120
  // this.stepShift += 2;
120
- stepLine = colors.green(truncate(stepLine, this.spaceShift));
121
+ stepLine = colors.dim(truncate(stepLine, this.spaceShift))
121
122
  }
122
123
  if (step.comment) {
123
- stepLine += colors.grey(step.comment.split('\n').join('\n' + ' '.repeat(4)));
124
+ stepLine += colors.grey(step.comment.split('\n').join('\n' + ' '.repeat(4)))
124
125
  }
125
126
 
126
127
  const _stepLine = isMaskedData() ? maskSensitiveData(stepLine) : stepLine
127
- print(' '.repeat(this.stepShift), truncate(_stepLine, this.spaceShift));
128
+ print(' '.repeat(this.stepShift), truncate(_stepLine, this.spaceShift))
128
129
  },
129
130
 
130
131
  /** @namespace */
@@ -132,10 +133,11 @@ module.exports = {
132
133
  /**
133
134
  * @param {Mocha.Suite} suite
134
135
  */
135
- started: (suite) => {
136
- if (!suite.title) return;
137
- print(`${colors.bold(suite.title)} --`);
138
- if (suite.comment) print(suite.comment);
136
+ started: suite => {
137
+ if (!suite.title) return
138
+ print(`${colors.bold(suite.title)} --`)
139
+ if (suite.file && outputLevel >= 1) print(colors.underline.grey(suite.file))
140
+ if (suite.comment) print(suite.comment)
139
141
  },
140
142
  },
141
143
 
@@ -145,25 +147,25 @@ module.exports = {
145
147
  * @param {Mocha.Test} test
146
148
  */
147
149
  started(test) {
148
- print(` ${colors.magenta.bold(test.title)}`);
150
+ print(` ${colors.magenta.bold(test.title)}`)
149
151
  },
150
152
  /**
151
153
  * @param {Mocha.Test} test
152
154
  */
153
155
  passed(test) {
154
- print(` ${colors.green.bold(figures.tick)} ${test.title} ${colors.grey(`in ${test.duration}ms`)}`);
156
+ print(` ${colors.green.bold(figures.tick)} ${test.title} ${colors.grey(`in ${test.duration}ms`)}`)
155
157
  },
156
158
  /**
157
159
  * @param {Mocha.Test} test
158
160
  */
159
161
  failed(test) {
160
- print(` ${colors.red.bold(figures.cross)} ${test.title} ${colors.grey(`in ${test.duration}ms`)}`);
162
+ print(` ${colors.red.bold(figures.cross)} ${test.title} ${colors.grey(`in ${test.duration}ms`)}`)
161
163
  },
162
164
  /**
163
165
  * @param {Mocha.Test} test
164
166
  */
165
167
  skipped(test) {
166
- print(` ${colors.yellow.bold('S')} ${test.title}`);
168
+ print(` ${colors.yellow.bold('S')} ${test.title}`)
167
169
  },
168
170
  },
169
171
 
@@ -173,21 +175,39 @@ module.exports = {
173
175
  * @param {Mocha.Test} test
174
176
  */
175
177
 
176
- started(test) {},
178
+ started(test) {
179
+ if (outputLevel < 1) return
180
+ print(` ${colors.dim.bold('Scenario()')}`)
181
+ },
177
182
 
178
183
  /**
179
184
  * @param {Mocha.Test} test
180
185
  */
181
186
  passed(test) {
182
- print(` ${colors.green.bold(`${figures.tick} OK`)} ${colors.grey(`in ${test.duration}ms`)}`);
183
- print();
187
+ print(` ${colors.green.bold(`${figures.tick} OK`)} ${colors.grey(`in ${test.duration}ms`)}`)
188
+ print()
184
189
  },
185
190
  /**
186
191
  * @param {Mocha.Test} test
187
192
  */
188
193
  failed(test) {
189
- print(` ${colors.red.bold(`${figures.cross} FAILED`)} ${colors.grey(`in ${test.duration}ms`)}`);
190
- print();
194
+ print(` ${colors.red.bold(`${figures.cross} FAILED`)} ${colors.grey(`in ${test.duration}ms`)}`)
195
+ print()
196
+ },
197
+ },
198
+
199
+ hook: {
200
+ started(hook) {
201
+ if (outputLevel < 1) return
202
+ print(` ${colors.dim.bold(hook.toCode())}`)
203
+ },
204
+ passed(hook) {
205
+ if (outputLevel < 1) return
206
+ print()
207
+ },
208
+ failed(hook) {
209
+ if (outputLevel < 1) return
210
+ print(` ${colors.red.bold(hook.toCode())}`)
191
211
  },
192
212
  },
193
213
 
@@ -199,9 +219,9 @@ module.exports = {
199
219
  */
200
220
  say(message, color = 'cyan') {
201
221
  if (colors[color] === undefined) {
202
- color = 'cyan';
222
+ color = 'cyan'
203
223
  }
204
- if (outputLevel >= 1) print(` ${colors[color].bold(message)}`);
224
+ if (outputLevel >= 1) print(` ${colors[color].bold(message)}`)
205
225
  },
206
226
 
207
227
  /**
@@ -211,52 +231,52 @@ module.exports = {
211
231
  * @param {number|string} duration
212
232
  */
213
233
  result(passed, failed, skipped, duration, failedHooks = 0) {
214
- let style = colors.bgGreen;
215
- let msg = ` ${passed || 0} passed`;
216
- let status = style.bold(' OK ');
234
+ let style = colors.bgGreen
235
+ let msg = ` ${passed || 0} passed`
236
+ let status = style.bold(' OK ')
217
237
  if (failed) {
218
- style = style.bgRed;
219
- status = style.bold(' FAIL ');
220
- msg += `, ${failed} failed`;
238
+ style = style.bgRed
239
+ status = style.bold(' FAIL ')
240
+ msg += `, ${failed} failed`
221
241
  }
222
242
 
223
243
  if (failedHooks > 0) {
224
- style = style.bgRed;
225
- status = style.bold(' FAIL ');
226
- msg += `, ${failedHooks} failedHooks`;
244
+ style = style.bgRed
245
+ status = style.bold(' FAIL ')
246
+ msg += `, ${failedHooks} failedHooks`
227
247
  }
228
- status += style.grey(' |');
248
+ status += style.grey(' |')
229
249
 
230
250
  if (skipped) {
231
- if (!failed) style = style.bgYellow;
232
- msg += `, ${skipped} skipped`;
251
+ if (!failed) style = style.bgYellow
252
+ msg += `, ${skipped} skipped`
233
253
  }
234
- msg += ' ';
235
- print(status + style(msg) + colors.grey(` // ${duration}`));
254
+ msg += ' '
255
+ print(status + style(msg) + colors.grey(` // ${duration}`))
236
256
  },
237
- };
257
+ }
238
258
 
239
259
  function print(...msg) {
240
260
  if (outputProcess) {
241
- msg.unshift(outputProcess);
261
+ msg.unshift(outputProcess)
242
262
  }
243
263
  if (!newline) {
244
- console.log();
245
- newline = true;
264
+ console.log()
265
+ newline = true
246
266
  }
247
267
 
248
- console.log.apply(this, msg);
268
+ console.log.apply(this, msg)
249
269
  }
250
270
 
251
271
  function truncate(msg, gap = 0) {
252
272
  if (msg.indexOf('\n') > 0 || outputLevel >= 3) {
253
- return msg; // don't cut multi line steps or on verbose log level
273
+ return msg // don't cut multi line steps or on verbose log level
254
274
  }
255
- const width = (process.stdout.columns || 200) - gap - 4;
275
+ const width = (process.stdout.columns || 200) - gap - 4
256
276
  if (msg.length > width) {
257
- msg = msg.substr(0, width - 1) + figures.ellipsis;
277
+ msg = msg.substr(0, width - 1) + figures.ellipsis
258
278
  }
259
- return msg;
279
+ return msg
260
280
  }
261
281
 
262
282
  function isMaskedData() {