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