1 | 'use strict';
|
2 | const concordance = require('concordance');
|
3 | const isPromise = require('is-promise');
|
4 | const plur = require('plur');
|
5 | const assert = require('./assert');
|
6 | const nowAndTimers = require('./now-and-timers');
|
7 | const parseTestArgs = require('./parse-test-args');
|
8 | const concordanceOptions = require('./concordance-options').default;
|
9 |
|
10 | function formatErrorValue(label, error) {
|
11 | const formatted = concordance.format(error, concordanceOptions);
|
12 | return {label, formatted};
|
13 | }
|
14 |
|
15 | const captureSavedError = () => {
|
16 | const limitBefore = Error.stackTraceLimit;
|
17 | Error.stackTraceLimit = 1;
|
18 | const err = new Error();
|
19 | Error.stackTraceLimit = limitBefore;
|
20 | return err;
|
21 | };
|
22 |
|
23 | const testMap = new WeakMap();
|
24 | class ExecutionContext extends assert.Assertions {
|
25 | constructor(test) {
|
26 | super({
|
27 | pass: () => {
|
28 | test.countPassedAssertion();
|
29 | },
|
30 | pending: promise => {
|
31 | test.addPendingAssertion(promise);
|
32 | },
|
33 | fail: err => {
|
34 | test.addFailedAssertion(err);
|
35 | },
|
36 | skip: () => {
|
37 | test.countPassedAssertion();
|
38 | },
|
39 | compareWithSnapshot: options => {
|
40 | return test.compareWithSnapshot(options);
|
41 | },
|
42 | powerAssert: test.powerAssert
|
43 | });
|
44 | testMap.set(this, test);
|
45 |
|
46 | this.snapshot.skip = () => {
|
47 | test.skipSnapshot();
|
48 | };
|
49 |
|
50 | this.log = (...inputArgs) => {
|
51 | const args = inputArgs.map(value => {
|
52 | return typeof value === 'string' ?
|
53 | value :
|
54 | concordance.format(value, concordanceOptions);
|
55 | });
|
56 | if (args.length > 0) {
|
57 | test.addLog(args.join(' '));
|
58 | }
|
59 | };
|
60 |
|
61 | this.plan = count => {
|
62 | test.plan(count, captureSavedError());
|
63 | };
|
64 |
|
65 | this.plan.skip = () => {};
|
66 |
|
67 | this.timeout = ms => {
|
68 | test.timeout(ms);
|
69 | };
|
70 |
|
71 | this.teardown = callback => {
|
72 | test.addTeardown(callback);
|
73 | };
|
74 |
|
75 | this.try = async (...attemptArgs) => {
|
76 | const {args, buildTitle, implementations, receivedImplementationArray} = parseTestArgs(attemptArgs);
|
77 |
|
78 | if (implementations.length === 0) {
|
79 | throw new TypeError('Expected an implementation.');
|
80 | }
|
81 |
|
82 | const attemptPromises = implementations.map((implementation, index) => {
|
83 | let {title, isSet, isValid, isEmpty} = buildTitle(implementation);
|
84 |
|
85 | if (!isSet || isEmpty) {
|
86 | title = `${test.title} ─ attempt ${test.attemptCount + 1 + index}`;
|
87 | } else if (isValid) {
|
88 | title = `${test.title} ─ ${title}`;
|
89 | } else {
|
90 | throw new TypeError('`t.try()` titles must be strings');
|
91 | }
|
92 |
|
93 | if (!test.registerUniqueTitle(title)) {
|
94 | throw new Error(`Duplicate test title: ${title}`);
|
95 | }
|
96 |
|
97 | return {implementation, title};
|
98 | }).map(async ({implementation, title}) => {
|
99 | let committed = false;
|
100 | let discarded = false;
|
101 |
|
102 | const {assertCount, deferredSnapshotRecordings, errors, logs, passed, snapshotCount, startingSnapshotCount} = await test.runAttempt(title, t => implementation(t, ...args));
|
103 |
|
104 | return {
|
105 | errors,
|
106 | logs: [...logs],
|
107 | passed,
|
108 | title,
|
109 | commit: ({retainLogs = true} = {}) => {
|
110 | if (committed) {
|
111 | return;
|
112 | }
|
113 |
|
114 | if (discarded) {
|
115 | test.saveFirstError(new Error('Can’t commit a result that was previously discarded'));
|
116 | return;
|
117 | }
|
118 |
|
119 | committed = true;
|
120 | test.finishAttempt({
|
121 | assertCount,
|
122 | commit: true,
|
123 | deferredSnapshotRecordings,
|
124 | errors,
|
125 | logs,
|
126 | passed,
|
127 | retainLogs,
|
128 | snapshotCount,
|
129 | startingSnapshotCount
|
130 | });
|
131 | },
|
132 | discard: ({retainLogs = false} = {}) => {
|
133 | if (committed) {
|
134 | test.saveFirstError(new Error('Can’t discard a result that was previously committed'));
|
135 | return;
|
136 | }
|
137 |
|
138 | if (discarded) {
|
139 | return;
|
140 | }
|
141 |
|
142 | discarded = true;
|
143 | test.finishAttempt({
|
144 | assertCount: 0,
|
145 | commit: false,
|
146 | deferredSnapshotRecordings,
|
147 | errors,
|
148 | logs,
|
149 | passed,
|
150 | retainLogs,
|
151 | snapshotCount,
|
152 | startingSnapshotCount
|
153 | });
|
154 | }
|
155 | };
|
156 | });
|
157 |
|
158 | const results = await Promise.all(attemptPromises);
|
159 | return receivedImplementationArray ? results : results[0];
|
160 | };
|
161 | }
|
162 |
|
163 | get end() {
|
164 | const end = testMap.get(this).bindEndCallback();
|
165 | const endFn = error => end(error, captureSavedError());
|
166 | return endFn;
|
167 | }
|
168 |
|
169 | get title() {
|
170 | return testMap.get(this).title;
|
171 | }
|
172 |
|
173 | get context() {
|
174 | return testMap.get(this).contextRef.get();
|
175 | }
|
176 |
|
177 | set context(context) {
|
178 | testMap.get(this).contextRef.set(context);
|
179 | }
|
180 |
|
181 | get passed() {
|
182 | return testMap.get(this).testPassed;
|
183 | }
|
184 |
|
185 | _throwsArgStart(assertion, file, line) {
|
186 | testMap.get(this).trackThrows({assertion, file, line});
|
187 | }
|
188 |
|
189 | _throwsArgEnd() {
|
190 | testMap.get(this).trackThrows(null);
|
191 | }
|
192 | }
|
193 |
|
194 | class Test {
|
195 | constructor(options) {
|
196 | this.contextRef = options.contextRef;
|
197 | this.experiments = options.experiments || {};
|
198 | this.failWithoutAssertions = options.failWithoutAssertions;
|
199 | this.fn = options.fn;
|
200 | this.isHook = options.isHook === true;
|
201 | this.metadata = options.metadata;
|
202 | this.powerAssert = options.powerAssert;
|
203 | this.title = options.title;
|
204 | this.testPassed = options.testPassed;
|
205 | this.registerUniqueTitle = options.registerUniqueTitle;
|
206 | this.logs = [];
|
207 | this.teardowns = [];
|
208 |
|
209 | const {snapshotBelongsTo = this.title, nextSnapshotIndex = 0} = options;
|
210 | this.snapshotBelongsTo = snapshotBelongsTo;
|
211 | this.nextSnapshotIndex = nextSnapshotIndex;
|
212 | this.snapshotCount = 0;
|
213 |
|
214 | const deferRecording = this.metadata.inline;
|
215 | this.deferredSnapshotRecordings = [];
|
216 | this.compareWithSnapshot = ({expected, id, message}) => {
|
217 | this.snapshotCount++;
|
218 |
|
219 |
|
220 | const belongsTo = id || snapshotBelongsTo;
|
221 | const index = id ? 0 : this.nextSnapshotIndex++;
|
222 | const label = id ? '' : message || `Snapshot ${index + 1}`;
|
223 |
|
224 | const {record, ...result} = options.compareTestSnapshot({belongsTo, deferRecording, expected, index, label});
|
225 | if (record) {
|
226 | this.deferredSnapshotRecordings.push(record);
|
227 | }
|
228 |
|
229 | return result;
|
230 | };
|
231 |
|
232 | this.skipSnapshot = () => {
|
233 | if (options.updateSnapshots) {
|
234 | this.addFailedAssertion(new Error('Snapshot assertions cannot be skipped when updating snapshots'));
|
235 | } else {
|
236 | this.nextSnapshotIndex++;
|
237 | this.snapshotCount++;
|
238 | this.countPassedAssertion();
|
239 | }
|
240 | };
|
241 |
|
242 | this.runAttempt = async (title, fn) => {
|
243 | if (this.finishing) {
|
244 | this.saveFirstError(new Error('Running a `t.try()`, but the test has already finished'));
|
245 | }
|
246 |
|
247 | this.attemptCount++;
|
248 | this.pendingAttemptCount++;
|
249 |
|
250 | const {contextRef, snapshotBelongsTo, nextSnapshotIndex, snapshotCount: startingSnapshotCount} = this;
|
251 | const attempt = new Test({
|
252 | ...options,
|
253 | fn,
|
254 | metadata: {...options.metadata, callback: false, failing: false, inline: true},
|
255 | contextRef: contextRef.copy(),
|
256 | snapshotBelongsTo,
|
257 | nextSnapshotIndex,
|
258 | title
|
259 | });
|
260 |
|
261 | const {deferredSnapshotRecordings, error, logs, passed, assertCount, snapshotCount} = await attempt.run();
|
262 | const errors = error ? [error] : [];
|
263 | return {assertCount, deferredSnapshotRecordings, errors, logs, passed, snapshotCount, startingSnapshotCount};
|
264 | };
|
265 |
|
266 | this.assertCount = 0;
|
267 | this.assertError = undefined;
|
268 | this.attemptCount = 0;
|
269 | this.calledEnd = false;
|
270 | this.duration = null;
|
271 | this.endCallbackFinisher = null;
|
272 | this.finishDueToAttributedError = null;
|
273 | this.finishDueToInactivity = null;
|
274 | this.finishDueToTimeout = null;
|
275 | this.finishing = false;
|
276 | this.pendingAssertionCount = 0;
|
277 | this.pendingAttemptCount = 0;
|
278 | this.pendingThrowsAssertion = null;
|
279 | this.planCount = null;
|
280 | this.startedAt = 0;
|
281 | this.timeoutMs = 0;
|
282 | this.timeoutTimer = null;
|
283 | }
|
284 |
|
285 | bindEndCallback() {
|
286 | if (this.metadata.callback) {
|
287 | return (error, savedError) => {
|
288 | this.endCallback(error, savedError);
|
289 | };
|
290 | }
|
291 |
|
292 | if (this.metadata.inline) {
|
293 | throw new Error('`t.end()` is not supported inside `t.try()`');
|
294 | } else {
|
295 | throw new Error('`t.end()` is not supported in this context. To use `t.end()` as a callback, you must use "callback mode" via `test.cb(testName, fn)`');
|
296 | }
|
297 | }
|
298 |
|
299 | endCallback(error, savedError) {
|
300 | if (this.calledEnd) {
|
301 | this.saveFirstError(new Error('`t.end()` called more than once'));
|
302 | return;
|
303 | }
|
304 |
|
305 | this.calledEnd = true;
|
306 |
|
307 | if (error) {
|
308 | this.saveFirstError(new assert.AssertionError({
|
309 | actual: error,
|
310 | message: 'Callback called with an error',
|
311 | savedError,
|
312 | values: [formatErrorValue('Callback called with an error:', error)]
|
313 | }));
|
314 | }
|
315 |
|
316 | if (this.endCallbackFinisher) {
|
317 | this.endCallbackFinisher();
|
318 | }
|
319 | }
|
320 |
|
321 | createExecutionContext() {
|
322 | return new ExecutionContext(this);
|
323 | }
|
324 |
|
325 | countPassedAssertion() {
|
326 | if (this.finishing) {
|
327 | this.saveFirstError(new Error('Assertion passed, but test has already finished'));
|
328 | }
|
329 |
|
330 | if (this.pendingAttemptCount > 0) {
|
331 | this.saveFirstError(new Error('Assertion passed, but an attempt is pending. Use the attempt’s assertions instead'));
|
332 | }
|
333 |
|
334 | this.assertCount++;
|
335 | this.refreshTimeout();
|
336 | }
|
337 |
|
338 | addLog(text) {
|
339 | this.logs.push(text);
|
340 | }
|
341 |
|
342 | addPendingAssertion(promise) {
|
343 | if (this.finishing) {
|
344 | this.saveFirstError(new Error('Assertion started, but test has already finished'));
|
345 | }
|
346 |
|
347 | if (this.pendingAttemptCount > 0) {
|
348 | this.saveFirstError(new Error('Assertion started, but an attempt is pending. Use the attempt’s assertions instead'));
|
349 | }
|
350 |
|
351 | this.assertCount++;
|
352 | this.pendingAssertionCount++;
|
353 | this.refreshTimeout();
|
354 |
|
355 | promise
|
356 | .catch(error => this.saveFirstError(error))
|
357 | .then(() => {
|
358 | this.pendingAssertionCount--;
|
359 | this.refreshTimeout();
|
360 | });
|
361 | }
|
362 |
|
363 | addFailedAssertion(error) {
|
364 | if (this.finishing) {
|
365 | this.saveFirstError(new Error('Assertion failed, but test has already finished'));
|
366 | }
|
367 |
|
368 | if (this.pendingAttemptCount > 0) {
|
369 | this.saveFirstError(new Error('Assertion failed, but an attempt is pending. Use the attempt’s assertions instead'));
|
370 | }
|
371 |
|
372 | this.assertCount++;
|
373 | this.refreshTimeout();
|
374 | this.saveFirstError(error);
|
375 | }
|
376 |
|
377 | finishAttempt({commit, deferredSnapshotRecordings, errors, logs, passed, retainLogs, snapshotCount, startingSnapshotCount}) {
|
378 | if (this.finishing) {
|
379 | if (commit) {
|
380 | this.saveFirstError(new Error('`t.try()` result was committed, but the test has already finished'));
|
381 | } else {
|
382 | this.saveFirstError(new Error('`t.try()` result was discarded, but the test has already finished'));
|
383 | }
|
384 | }
|
385 |
|
386 | if (commit) {
|
387 | this.assertCount++;
|
388 |
|
389 | if (startingSnapshotCount === this.snapshotCount) {
|
390 | this.snapshotCount += snapshotCount;
|
391 | this.nextSnapshotIndex += snapshotCount;
|
392 | for (const record of deferredSnapshotRecordings) {
|
393 | record();
|
394 | }
|
395 | } else {
|
396 | this.saveFirstError(new Error('Cannot commit `t.try()` result. Do not run concurrent snapshot assertions when using `t.try()`'));
|
397 | }
|
398 | }
|
399 |
|
400 | this.pendingAttemptCount--;
|
401 |
|
402 | if (commit && !passed) {
|
403 | this.saveFirstError(errors[0]);
|
404 | }
|
405 |
|
406 | if (retainLogs) {
|
407 | for (const log of logs) {
|
408 | this.addLog(log);
|
409 | }
|
410 | }
|
411 |
|
412 | this.refreshTimeout();
|
413 | }
|
414 |
|
415 | saveFirstError(error) {
|
416 | if (!this.assertError) {
|
417 | this.assertError = error;
|
418 | }
|
419 | }
|
420 |
|
421 | plan(count, planError) {
|
422 | if (typeof count !== 'number') {
|
423 | throw new TypeError('Expected a number');
|
424 | }
|
425 |
|
426 | this.planCount = count;
|
427 |
|
428 |
|
429 |
|
430 | this.planError = planError;
|
431 | }
|
432 |
|
433 | timeout(ms) {
|
434 | if (this.finishing) {
|
435 | return;
|
436 | }
|
437 |
|
438 | this.clearTimeout();
|
439 | this.timeoutMs = ms;
|
440 | this.timeoutTimer = nowAndTimers.setTimeout(() => {
|
441 | this.saveFirstError(new Error('Test timeout exceeded'));
|
442 |
|
443 | if (this.finishDueToTimeout) {
|
444 | this.finishDueToTimeout();
|
445 | }
|
446 | }, ms);
|
447 | }
|
448 |
|
449 | refreshTimeout() {
|
450 | if (!this.timeoutTimer) {
|
451 | return;
|
452 | }
|
453 |
|
454 | if (this.timeoutTimer.refresh) {
|
455 | this.timeoutTimer.refresh();
|
456 | } else {
|
457 | this.timeout(this.timeoutMs);
|
458 | }
|
459 | }
|
460 |
|
461 | clearTimeout() {
|
462 | nowAndTimers.clearTimeout(this.timeoutTimer);
|
463 | this.timeoutTimer = null;
|
464 | }
|
465 |
|
466 | addTeardown(callback) {
|
467 | if (this.isHook) {
|
468 | this.saveFirstError(new Error('`t.teardown()` is not allowed in hooks'));
|
469 | return;
|
470 | }
|
471 |
|
472 | if (this.finishing) {
|
473 | this.saveFirstError(new Error('`t.teardown()` cannot be used during teardown'));
|
474 | return;
|
475 | }
|
476 |
|
477 | if (typeof callback !== 'function') {
|
478 | throw new TypeError('Expected a function');
|
479 | }
|
480 |
|
481 | this.teardowns.push(callback);
|
482 | }
|
483 |
|
484 | async runTeardowns() {
|
485 | for (const teardown of this.teardowns) {
|
486 | try {
|
487 | await teardown();
|
488 | } catch (error) {
|
489 | this.saveFirstError(error);
|
490 | }
|
491 | }
|
492 | }
|
493 |
|
494 | verifyPlan() {
|
495 | if (!this.assertError && this.planCount !== null && this.planCount !== this.assertCount) {
|
496 | this.saveFirstError(new assert.AssertionError({
|
497 | assertion: 'plan',
|
498 | message: `Planned for ${this.planCount} ${plur('assertion', this.planCount)}, but got ${this.assertCount}.`,
|
499 | operator: '===',
|
500 | savedError: this.planError
|
501 | }));
|
502 | }
|
503 | }
|
504 |
|
505 | verifyAssertions() {
|
506 | if (this.assertError) {
|
507 | return;
|
508 | }
|
509 |
|
510 | if (this.pendingAttemptCount > 0) {
|
511 | this.saveFirstError(new Error('Test finished, but not all attempts were committed or discarded'));
|
512 | return;
|
513 | }
|
514 |
|
515 | if (this.pendingAssertionCount > 0) {
|
516 | this.saveFirstError(new Error('Test finished, but an assertion is still pending'));
|
517 | return;
|
518 | }
|
519 |
|
520 | if (this.failWithoutAssertions) {
|
521 | if (this.planCount !== null) {
|
522 | return;
|
523 | }
|
524 |
|
525 | if (this.assertCount === 0 && !this.calledEnd) {
|
526 | this.saveFirstError(new Error('Test finished without running any assertions'));
|
527 | }
|
528 | }
|
529 | }
|
530 |
|
531 | trackThrows(pending) {
|
532 | this.pendingThrowsAssertion = pending;
|
533 | }
|
534 |
|
535 | detectImproperThrows(error) {
|
536 | if (!this.pendingThrowsAssertion) {
|
537 | return false;
|
538 | }
|
539 |
|
540 | const pending = this.pendingThrowsAssertion;
|
541 | this.pendingThrowsAssertion = null;
|
542 |
|
543 | const values = [];
|
544 | if (error) {
|
545 | values.push(formatErrorValue(`The following error was thrown, possibly before \`t.${pending.assertion}()\` could be called:`, error));
|
546 | }
|
547 |
|
548 | this.saveFirstError(new assert.AssertionError({
|
549 | assertion: pending.assertion,
|
550 | fixedSource: {file: pending.file, line: pending.line},
|
551 | improperUsage: true,
|
552 | message: `Improper usage of \`t.${pending.assertion}()\` detected`,
|
553 | savedError: error instanceof Error && error,
|
554 | values
|
555 | }));
|
556 | return true;
|
557 | }
|
558 |
|
559 | waitForPendingThrowsAssertion() {
|
560 | return new Promise(resolve => {
|
561 | this.finishDueToAttributedError = () => {
|
562 | resolve(this.finish());
|
563 | };
|
564 |
|
565 | this.finishDueToInactivity = () => {
|
566 | this.detectImproperThrows();
|
567 | resolve(this.finish());
|
568 | };
|
569 |
|
570 |
|
571 |
|
572 | nowAndTimers.setTimeout(() => this.finishDueToInactivity(), 1000).unref();
|
573 | });
|
574 | }
|
575 |
|
576 | attributeLeakedError(error) {
|
577 | if (!this.detectImproperThrows(error)) {
|
578 | return false;
|
579 | }
|
580 |
|
581 | this.finishDueToAttributedError();
|
582 | return true;
|
583 | }
|
584 |
|
585 | callFn() {
|
586 | try {
|
587 | return {
|
588 | ok: true,
|
589 | retval: this.fn.call(null, this.createExecutionContext())
|
590 | };
|
591 | } catch (error) {
|
592 | return {
|
593 | ok: false,
|
594 | error
|
595 | };
|
596 | }
|
597 | }
|
598 |
|
599 | run() {
|
600 | this.startedAt = nowAndTimers.now();
|
601 |
|
602 | const result = this.callFn();
|
603 | if (!result.ok) {
|
604 | if (!this.detectImproperThrows(result.error)) {
|
605 | this.saveFirstError(new assert.AssertionError({
|
606 | message: 'Error thrown in test',
|
607 | savedError: result.error instanceof Error && result.error,
|
608 | values: [formatErrorValue('Error thrown in test:', result.error)]
|
609 | }));
|
610 | }
|
611 |
|
612 | return this.finish();
|
613 | }
|
614 |
|
615 | const returnedObservable = result.retval !== null && typeof result.retval === 'object' && typeof result.retval.subscribe === 'function';
|
616 | const returnedPromise = isPromise(result.retval);
|
617 |
|
618 | let promise;
|
619 | if (returnedObservable) {
|
620 | promise = new Promise((resolve, reject) => {
|
621 | result.retval.subscribe({
|
622 | error: reject,
|
623 | complete: () => resolve()
|
624 | });
|
625 | });
|
626 | } else if (returnedPromise) {
|
627 |
|
628 | promise = Promise.resolve(result.retval);
|
629 | }
|
630 |
|
631 | if (this.metadata.callback) {
|
632 | if (returnedObservable || returnedPromise) {
|
633 | const asyncType = returnedObservable ? 'observables' : 'promises';
|
634 | this.saveFirstError(new Error(`Do not return ${asyncType} from tests declared via \`test.cb(…)\`. Use \`test.cb(…)\` for legacy callback APIs. When using promises, observables or async functions, use \`test(…)\`.`));
|
635 | return this.finish();
|
636 | }
|
637 |
|
638 | if (this.calledEnd) {
|
639 | return this.finish();
|
640 | }
|
641 |
|
642 | return new Promise(resolve => {
|
643 | this.endCallbackFinisher = () => {
|
644 | resolve(this.finish());
|
645 | };
|
646 |
|
647 | this.finishDueToAttributedError = () => {
|
648 | resolve(this.finish());
|
649 | };
|
650 |
|
651 | this.finishDueToTimeout = () => {
|
652 | resolve(this.finish());
|
653 | };
|
654 |
|
655 | this.finishDueToInactivity = () => {
|
656 | this.saveFirstError(new Error('`t.end()` was never called'));
|
657 | resolve(this.finish());
|
658 | };
|
659 | });
|
660 | }
|
661 |
|
662 | if (promise) {
|
663 | return new Promise(resolve => {
|
664 | this.finishDueToAttributedError = () => {
|
665 | resolve(this.finish());
|
666 | };
|
667 |
|
668 | this.finishDueToTimeout = () => {
|
669 | resolve(this.finish());
|
670 | };
|
671 |
|
672 | this.finishDueToInactivity = () => {
|
673 | const error = returnedObservable ?
|
674 | new Error('Observable returned by test never completed') :
|
675 | new Error('Promise returned by test never resolved');
|
676 | this.saveFirstError(error);
|
677 | resolve(this.finish());
|
678 | };
|
679 |
|
680 | promise
|
681 | .catch(error => {
|
682 | if (!this.detectImproperThrows(error)) {
|
683 | this.saveFirstError(new assert.AssertionError({
|
684 | message: 'Rejected promise returned by test',
|
685 | savedError: error instanceof Error && error,
|
686 | values: [formatErrorValue('Rejected promise returned by test. Reason:', error)]
|
687 | }));
|
688 | }
|
689 | })
|
690 | .then(() => resolve(this.finish()));
|
691 | });
|
692 | }
|
693 |
|
694 | return this.finish();
|
695 | }
|
696 |
|
697 | async finish() {
|
698 | this.finishing = true;
|
699 |
|
700 | if (!this.assertError && this.pendingThrowsAssertion) {
|
701 | return this.waitForPendingThrowsAssertion();
|
702 | }
|
703 |
|
704 | this.clearTimeout();
|
705 | this.verifyPlan();
|
706 | this.verifyAssertions();
|
707 | await this.runTeardowns();
|
708 |
|
709 | this.duration = nowAndTimers.now() - this.startedAt;
|
710 |
|
711 | let error = this.assertError;
|
712 | let passed = !error;
|
713 |
|
714 | if (this.metadata.failing) {
|
715 | passed = !passed;
|
716 |
|
717 | if (passed) {
|
718 | error = null;
|
719 | } else {
|
720 | error = new Error('Test was expected to fail, but succeeded, you should stop marking the test as failing');
|
721 | }
|
722 | }
|
723 |
|
724 | return {
|
725 | deferredSnapshotRecordings: this.deferredSnapshotRecordings,
|
726 | duration: this.duration,
|
727 | error,
|
728 | logs: this.logs,
|
729 | metadata: this.metadata,
|
730 | passed,
|
731 | snapshotCount: this.snapshotCount,
|
732 | assertCount: this.assertCount,
|
733 | title: this.title
|
734 | };
|
735 | }
|
736 | }
|
737 |
|
738 | module.exports = Test;
|