codeceptjs 3.7.0-beta.7 → 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.
Files changed (52) hide show
  1. package/lib/actor.js +1 -2
  2. package/lib/ai.js +130 -121
  3. package/lib/codecept.js +4 -4
  4. package/lib/command/check.js +4 -0
  5. package/lib/command/run-workers.js +1 -53
  6. package/lib/command/workers/runTests.js +25 -189
  7. package/lib/container.js +16 -0
  8. package/lib/els.js +87 -106
  9. package/lib/event.js +18 -17
  10. package/lib/helper/Playwright.js +7 -1
  11. package/lib/listener/exit.js +5 -8
  12. package/lib/listener/globalTimeout.js +26 -9
  13. package/lib/listener/result.js +12 -0
  14. package/lib/listener/steps.js +0 -6
  15. package/lib/listener/store.js +9 -1
  16. package/lib/mocha/asyncWrapper.js +12 -2
  17. package/lib/mocha/cli.js +65 -31
  18. package/lib/mocha/hooks.js +32 -3
  19. package/lib/mocha/suite.js +27 -1
  20. package/lib/mocha/test.js +91 -7
  21. package/lib/mocha/types.d.ts +5 -0
  22. package/lib/output.js +2 -1
  23. package/lib/plugin/analyze.js +348 -0
  24. package/lib/plugin/commentStep.js +5 -0
  25. package/lib/plugin/customReporter.js +52 -0
  26. package/lib/plugin/heal.js +2 -2
  27. package/lib/plugin/pageInfo.js +140 -0
  28. package/lib/plugin/retryTo.js +10 -2
  29. package/lib/plugin/screenshotOnFail.js +11 -16
  30. package/lib/plugin/stepByStepReport.js +5 -4
  31. package/lib/plugin/stepTimeout.js +1 -1
  32. package/lib/plugin/tryTo.js +9 -1
  33. package/lib/recorder.js +4 -4
  34. package/lib/rerun.js +43 -42
  35. package/lib/result.js +161 -0
  36. package/lib/step/base.js +52 -4
  37. package/lib/step/func.js +46 -0
  38. package/lib/step/helper.js +3 -0
  39. package/lib/step/meta.js +9 -1
  40. package/lib/step/record.js +5 -5
  41. package/lib/step/section.js +55 -0
  42. package/lib/step.js +6 -0
  43. package/lib/steps.js +28 -1
  44. package/lib/store.js +2 -0
  45. package/lib/{step/timeout.js → timeout.js} +24 -0
  46. package/lib/utils.js +35 -0
  47. package/lib/workers.js +28 -38
  48. package/package.json +7 -6
  49. package/typings/promiseBasedTypes.d.ts +104 -0
  50. package/typings/types.d.ts +104 -0
  51. package/lib/listener/artifacts.js +0 -19
  52. package/lib/plugin/debugErrors.js +0 -67
@@ -75,217 +75,53 @@ function filterTests() {
75
75
  }
76
76
 
77
77
  function initializeListeners() {
78
- function simplifyError(error) {
79
- if (error) {
80
- const { stack, uncaught, message, actual, expected } = error
81
-
82
- return {
83
- stack,
84
- uncaught,
85
- message,
86
- actual,
87
- expected,
88
- }
89
- }
90
-
91
- return null
92
- }
93
- function simplifyTest(test, err = null) {
94
- test = { ...test }
95
-
96
- if (test.start && !test.duration) {
97
- const end = new Date()
98
- test.duration = end - test.start
99
- }
100
-
101
- if (test.err) {
102
- err = simplifyError(test.err)
103
- test.status = 'failed'
104
- } else if (err) {
105
- err = simplifyError(err)
106
- test.status = 'failed'
107
- }
108
- const parent = {}
109
- if (test.parent) {
110
- parent.title = test.parent.title
111
- }
112
-
113
- if (test.opts) {
114
- Object.keys(test.opts).forEach(k => {
115
- if (typeof test.opts[k] === 'object') delete test.opts[k]
116
- if (typeof test.opts[k] === 'function') delete test.opts[k]
117
- })
118
- }
119
-
120
- return {
121
- opts: test.opts || {},
122
- tags: test.tags || [],
123
- uid: test.uid,
124
- workerIndex,
125
- retries: test._retries,
126
- title: test.title,
127
- status: test.status,
128
- notes: test.notes || [],
129
- meta: test.meta || {},
130
- artifacts: test.artifacts || [],
131
- duration: test.duration || 0,
132
- err,
133
- parent,
134
- steps: test.steps && test.steps.length > 0 ? simplifyStepsInTestObject(test.steps, err) : [],
135
- }
136
- }
137
-
138
- function simplifyStepsInTestObject(steps, err) {
139
- steps = [...steps]
140
- const _steps = []
141
-
142
- for (step of steps) {
143
- const _args = []
144
-
145
- if (step.args) {
146
- for (const arg of step.args) {
147
- // check if arg is a JOI object
148
- if (arg && arg.$_root) {
149
- _args.push(JSON.stringify(arg).slice(0, 300))
150
- // check if arg is a function
151
- } else if (arg && typeof arg === 'function') {
152
- _args.push(arg.name)
153
- } else {
154
- _args.push(arg)
155
- }
156
- }
157
- }
158
-
159
- _steps.push({
160
- actor: step.actor,
161
- name: step.name,
162
- status: step.status,
163
- args: JSON.stringify(_args),
164
- startedAt: step.startedAt,
165
- startTime: step.startTime,
166
- endTime: step.endTime,
167
- finishedAt: step.finishedAt,
168
- duration: step.duration,
169
- err,
170
- })
171
- }
172
-
173
- return _steps
174
- }
175
-
176
- function simplifyStep(step, err = null) {
177
- step = { ...step }
178
-
179
- if (step.startTime && !step.duration) {
180
- const end = new Date()
181
- step.duration = end - step.startTime
182
- }
183
-
184
- if (step.err) {
185
- err = simplifyError(step.err)
186
- step.status = 'failed'
187
- } else if (err) {
188
- err = simplifyError(err)
189
- step.status = 'failed'
190
- }
191
-
192
- const parent = {}
193
- if (step.metaStep) {
194
- parent.title = step.metaStep.actor
195
- }
196
-
197
- if (step.opts) {
198
- Object.keys(step.opts).forEach(k => {
199
- if (typeof step.opts[k] === 'object') delete step.opts[k]
200
- if (typeof step.opts[k] === 'function') delete step.opts[k]
201
- })
202
- }
203
-
204
- return {
205
- opts: step.opts || {},
206
- workerIndex,
207
- title: step.name,
208
- status: step.status,
209
- duration: step.duration || 0,
210
- err,
211
- parent,
212
- test: simplifyTest(step.test),
213
- }
214
- }
215
-
216
- collectStats()
217
78
  // suite
218
- event.dispatcher.on(event.suite.before, suite => sendToParentThread({ event: event.suite.before, workerIndex, data: simplifyTest(suite) }))
219
- event.dispatcher.on(event.suite.after, suite => sendToParentThread({ event: event.suite.after, workerIndex, data: simplifyTest(suite) }))
79
+ event.dispatcher.on(event.suite.before, suite => sendToParentThread({ event: event.suite.before, workerIndex, data: suite.simplify() }))
80
+ event.dispatcher.on(event.suite.after, suite => sendToParentThread({ event: event.suite.after, workerIndex, data: suite.simplify() }))
220
81
 
221
82
  // calculate duration
222
83
  event.dispatcher.on(event.test.started, test => (test.start = new Date()))
223
84
 
224
85
  // tests
225
- event.dispatcher.on(event.test.before, test => sendToParentThread({ event: event.test.before, workerIndex, data: simplifyTest(test) }))
226
- event.dispatcher.on(event.test.after, test => sendToParentThread({ event: event.test.after, workerIndex, data: simplifyTest(test) }))
86
+ event.dispatcher.on(event.test.before, test => sendToParentThread({ event: event.test.before, workerIndex, data: test.simplify() }))
87
+ event.dispatcher.on(event.test.after, test => sendToParentThread({ event: event.test.after, workerIndex, data: test.simplify() }))
227
88
  // we should force-send correct errors to prevent race condition
228
- event.dispatcher.on(event.test.finished, (test, err) => sendToParentThread({ event: event.test.finished, workerIndex, data: simplifyTest(test, err) }))
229
- event.dispatcher.on(event.test.failed, (test, err) => sendToParentThread({ event: event.test.failed, workerIndex, data: simplifyTest(test, err) }))
230
- event.dispatcher.on(event.test.passed, (test, err) => sendToParentThread({ event: event.test.passed, workerIndex, data: simplifyTest(test, err) }))
231
- event.dispatcher.on(event.test.started, test => sendToParentThread({ event: event.test.started, workerIndex, data: simplifyTest(test) }))
232
- event.dispatcher.on(event.test.skipped, test => sendToParentThread({ event: event.test.skipped, workerIndex, data: simplifyTest(test) }))
89
+ event.dispatcher.on(event.test.finished, (test, err) => sendToParentThread({ event: event.test.finished, workerIndex, data: { ...test.simplify(), err } }))
90
+ event.dispatcher.on(event.test.failed, (test, err) => sendToParentThread({ event: event.test.failed, workerIndex, data: { ...test.simplify(), err } }))
91
+ event.dispatcher.on(event.test.passed, (test, err) => sendToParentThread({ event: event.test.passed, workerIndex, data: { ...test.simplify(), err } }))
92
+ event.dispatcher.on(event.test.started, test => sendToParentThread({ event: event.test.started, workerIndex, data: test.simplify() }))
93
+ event.dispatcher.on(event.test.skipped, test => sendToParentThread({ event: event.test.skipped, workerIndex, data: test.simplify() }))
233
94
 
234
95
  // steps
235
- event.dispatcher.on(event.step.finished, step => sendToParentThread({ event: event.step.finished, workerIndex, data: simplifyStep(step) }))
236
- event.dispatcher.on(event.step.started, step => sendToParentThread({ event: event.step.started, workerIndex, data: simplifyStep(step) }))
237
- event.dispatcher.on(event.step.passed, step => sendToParentThread({ event: event.step.passed, workerIndex, data: simplifyStep(step) }))
238
- event.dispatcher.on(event.step.failed, step => sendToParentThread({ event: event.step.failed, workerIndex, data: simplifyStep(step) }))
96
+ event.dispatcher.on(event.step.finished, step => sendToParentThread({ event: event.step.finished, workerIndex, data: step.simplify() }))
97
+ event.dispatcher.on(event.step.started, step => sendToParentThread({ event: event.step.started, workerIndex, data: step.simplify() }))
98
+ event.dispatcher.on(event.step.passed, step => sendToParentThread({ event: event.step.passed, workerIndex, data: step.simplify() }))
99
+ event.dispatcher.on(event.step.failed, step => sendToParentThread({ event: event.step.failed, workerIndex, data: step.simplify() }))
239
100
 
240
- event.dispatcher.on(event.hook.failed, (test, err) => sendToParentThread({ event: event.hook.failed, workerIndex, data: simplifyTest(test, err) }))
241
- event.dispatcher.on(event.hook.passed, (test, err) => sendToParentThread({ event: event.hook.passed, workerIndex, data: simplifyTest(test, err) }))
242
- event.dispatcher.on(event.all.failures, data => sendToParentThread({ event: event.all.failures, workerIndex, data }))
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 => sendToParentThread({ event: event.hook.passed, workerIndex, data: hook.simplify() }))
103
+ event.dispatcher.on(event.hook.finished, hook => sendToParentThread({ event: event.hook.finished, workerIndex, data: hook.simplify() }))
243
104
 
105
+ event.dispatcher.once(event.all.after, () => {
106
+ sendToParentThread({ event: event.all.after, workerIndex, data: container.result().simplify() })
107
+ })
244
108
  // all
245
- event.dispatcher.once(event.all.result, () => parentPort.close())
109
+ event.dispatcher.once(event.all.result, () => {
110
+ sendToParentThread({ event: event.all.result, workerIndex, data: container.result().simplify() })
111
+ parentPort?.close()
112
+ })
246
113
  }
247
114
 
248
115
  function disablePause() {
249
116
  global.pause = () => {}
250
117
  }
251
118
 
252
- function collectStats() {
253
- const stats = {
254
- passes: 0,
255
- failures: 0,
256
- skipped: 0,
257
- tests: 0,
258
- pending: 0,
259
- }
260
- event.dispatcher.on(event.test.skipped, () => {
261
- stats.skipped++
262
- })
263
- event.dispatcher.on(event.test.passed, () => {
264
- stats.passes++
265
- })
266
- event.dispatcher.on(event.test.failed, test => {
267
- if (test.ctx._runnable.title.includes('hook: AfterSuite')) {
268
- stats.failedHooks += 1
269
- }
270
- stats.failures++
271
- })
272
- event.dispatcher.on(event.test.skipped, () => {
273
- stats.pending++
274
- })
275
- event.dispatcher.on(event.test.finished, () => {
276
- stats.tests++
277
- })
278
- event.dispatcher.once(event.all.after, () => {
279
- sendToParentThread({ event: event.all.after, data: stats })
280
- })
281
- }
282
-
283
119
  function sendToParentThread(data) {
284
- parentPort.postMessage(data)
120
+ parentPort?.postMessage(data)
285
121
  }
286
122
 
287
123
  function listenToParentThread() {
288
- parentPort.on('message', eventData => {
124
+ parentPort?.on('message', eventData => {
289
125
  container.append({ support: eventData.data })
290
126
  })
291
127
  }
package/lib/container.js CHANGED
@@ -9,6 +9,7 @@ const recorder = require('./recorder')
9
9
  const event = require('./event')
10
10
  const WorkerStorage = require('./workerStorage')
11
11
  const store = require('./store')
12
+ const Result = require('./result')
12
13
  const ai = require('./ai')
13
14
 
14
15
  let asyncHelperPromise
@@ -25,6 +26,8 @@ let container = {
25
26
  */
26
27
  mocha: {},
27
28
  translation: {},
29
+ /** @type {Result | null} */
30
+ result: null,
28
31
  }
29
32
 
30
33
  /**
@@ -54,6 +57,7 @@ class Container {
54
57
  container.translation = loadTranslation(config.translation || null, config.vocabularies || [])
55
58
  container.proxySupport = createSupportObjects(config.include || {})
56
59
  container.plugins = createPlugins(config.plugins || {}, opts)
60
+ container.result = new Result()
57
61
 
58
62
  createActor(config.include?.I)
59
63
 
@@ -127,6 +131,18 @@ class Container {
127
131
  return container.mocha
128
132
  }
129
133
 
134
+ /**
135
+ * Get result
136
+ *
137
+ * @returns {Result}
138
+ */
139
+ static result() {
140
+ if (!container.result) {
141
+ container.result = new Result()
142
+ }
143
+ return container.result
144
+ }
145
+
130
146
  /**
131
147
  * Append new services to container
132
148
  *
package/lib/els.js CHANGED
@@ -1,115 +1,124 @@
1
- const output = require('./output');
2
- const store = require('./store');
3
- const recorder = require('./recorder');
4
- const container = require('./container');
5
- const event = require('./event');
6
- const Step = require('./step');
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
- if (!fn) {
12
- fn = locator;
13
- locator = purpose;
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
- const step = prepareStep(purpose, locator, fn);
18
- if (!step) return;
16
+ if (!fn || fn === stepConfig) {
17
+ fn = locator
18
+ locator = purpose
19
+ purpose = 'first element'
20
+ }
19
21
 
20
- return executeStep(step, async () => {
21
- const els = await step.helper._locate(locator);
22
- output.debug(`Found ${els.length} elements, using first element`);
22
+ const step = prepareStep(purpose, locator, fn)
23
+ if (!step) return
23
24
 
24
- return fn(els[0]);
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 Step(helper, `${purpose} within "${locator}" ${isAssertion ? 'to be' : 'to'}`);
140
- step.setActor('EL');
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
- let error;
149
- const promise = recorder.add('register element wrapper', async () => {
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/event.js CHANGED
@@ -1,10 +1,10 @@
1
- const debug = require('debug')('codeceptjs:event');
2
- const events = require('events');
3
- const { error } = require('./output');
1
+ const debug = require('debug')('codeceptjs:event')
2
+ const events = require('events')
3
+ const { error } = require('./output')
4
4
 
5
- const dispatcher = new events.EventEmitter();
5
+ const dispatcher = new events.EventEmitter()
6
6
 
7
- dispatcher.setMaxListeners(50);
7
+ dispatcher.setMaxListeners(50)
8
8
  /**
9
9
  * @namespace
10
10
  * @alias event
@@ -59,6 +59,7 @@ module.exports = {
59
59
  started: 'hook.start',
60
60
  passed: 'hook.passed',
61
61
  failed: 'hook.failed',
62
+ finished: 'hook.finished',
62
63
  },
63
64
 
64
65
  /**
@@ -141,33 +142,33 @@ module.exports = {
141
142
  * @param {*} [param]
142
143
  */
143
144
  emit(event, param) {
144
- let msg = `Emitted | ${event}`;
145
+ let msg = `Emitted | ${event}`
145
146
  if (param && param.toString()) {
146
- msg += ` (${param.toString()})`;
147
+ msg += ` (${param.toString()})`
147
148
  }
148
- debug(msg);
149
+ debug(msg)
149
150
  try {
150
- this.dispatcher.emit.apply(this.dispatcher, arguments);
151
+ this.dispatcher.emit.apply(this.dispatcher, arguments)
151
152
  } catch (err) {
152
- error(`Error processing ${event} event:`);
153
- error(err.stack);
153
+ error(`Error processing ${event} event:`)
154
+ error(err.stack)
154
155
  }
155
156
  },
156
157
 
157
158
  /** for testing only! */
158
159
  cleanDispatcher: () => {
159
- let event;
160
+ let event
160
161
  for (event in this.test) {
161
- this.dispatcher.removeAllListeners(this.test[event]);
162
+ this.dispatcher.removeAllListeners(this.test[event])
162
163
  }
163
164
  for (event in this.suite) {
164
- this.dispatcher.removeAllListeners(this.test[event]);
165
+ this.dispatcher.removeAllListeners(this.test[event])
165
166
  }
166
167
  for (event in this.step) {
167
- this.dispatcher.removeAllListeners(this.test[event]);
168
+ this.dispatcher.removeAllListeners(this.test[event])
168
169
  }
169
170
  for (event in this.all) {
170
- this.dispatcher.removeAllListeners(this.test[event]);
171
+ this.dispatcher.removeAllListeners(this.test[event])
171
172
  }
172
173
  },
173
- };
174
+ }
@@ -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) {
@@ -1,20 +1,17 @@
1
1
  const event = require('../event')
2
+ const debug = require('debug')('codeceptjs:exit')
2
3
 
3
4
  module.exports = function () {
4
5
  let failedTests = []
5
6
 
6
- event.dispatcher.on(event.test.failed, testOrSuite => {
7
- // NOTE When an error happens in one of the hooks (BeforeAll/BeforeEach...) the event object
8
- // is a suite and not a test
9
- const id = testOrSuite.uid || (testOrSuite.ctx && testOrSuite.ctx.test.uid) || 'empty'
7
+ event.dispatcher.on(event.test.failed, test => {
8
+ const id = test.uid || (test.ctx && test.ctx.test.uid) || 'empty'
10
9
  failedTests.push(id)
11
10
  })
12
11
 
13
12
  // if test was successful after retries
14
- event.dispatcher.on(event.test.passed, testOrSuite => {
15
- // NOTE When an error happens in one of the hooks (BeforeAll/BeforeEach...) the event object
16
- // is a suite and not a test
17
- const id = testOrSuite.uid || (testOrSuite.ctx && testOrSuite.ctx.test.uid) || 'empty'
13
+ event.dispatcher.on(event.test.passed, test => {
14
+ const id = test.uid || (test.ctx && test.ctx.test.uid) || 'empty'
18
15
  failedTests = failedTests.filter(failed => id !== failed)
19
16
  })
20
17