UNPKG

25 kBJavaScriptView Raw
1'use strict';
2const concordance = require('concordance');
3const isError = require('is-error');
4const isPromise = require('is-promise');
5const concordanceOptions = require('./concordance-options').default;
6const {CIRCULAR_SELECTOR, isLikeSelector, selectComparable} = require('./like-selector');
7const snapshotManager = require('./snapshot-manager');
8
9function formatDescriptorDiff(actualDescriptor, expectedDescriptor, options) {
10 options = {...options, ...concordanceOptions};
11 return {
12 label: 'Difference:',
13 formatted: concordance.diffDescriptors(actualDescriptor, expectedDescriptor, options)
14 };
15}
16
17function formatDescriptorWithLabel(label, descriptor) {
18 return {
19 label,
20 formatted: concordance.formatDescriptor(descriptor, concordanceOptions)
21 };
22}
23
24function formatWithLabel(label, value) {
25 return formatDescriptorWithLabel(label, concordance.describe(value, concordanceOptions));
26}
27
28function formatPowerAssertValue(value) {
29 return concordance.format(value, concordanceOptions);
30}
31
32const hasOwnProperty = (object, prop) => Object.prototype.hasOwnProperty.call(object, prop);
33const noop = () => {};
34const notImplemented = () => {
35 throw new Error('not implemented');
36};
37
38class AssertionError extends Error {
39 constructor(options) {
40 super(options.message || '');
41 this.name = 'AssertionError';
42
43 this.assertion = options.assertion;
44 this.fixedSource = options.fixedSource;
45 this.improperUsage = options.improperUsage || false;
46 this.actualStack = options.actualStack;
47 this.operator = options.operator;
48 this.values = options.values || [];
49
50 // Raw expected and actual objects are stored for custom reporters
51 // (such as wallaby.js), that manage worker processes directly and
52 // use the values for custom diff views
53 this.raw = options.raw;
54
55 // Reserved for power-assert statements
56 this.statements = [];
57
58 if (options.savedError) {
59 this.savedError = options.savedError;
60 } else {
61 this.savedError = getErrorWithLongStackTrace();
62 }
63 }
64}
65exports.AssertionError = AssertionError;
66
67function checkAssertionMessage(assertion, message) {
68 if (typeof message === 'undefined' || typeof message === 'string') {
69 return true;
70 }
71
72 return new AssertionError({
73 assertion,
74 improperUsage: true,
75 message: 'The assertion message must be a string',
76 values: [formatWithLabel('Called with:', message)]
77 });
78}
79
80exports.checkAssertionMessage = checkAssertionMessage;
81
82function getErrorWithLongStackTrace() {
83 const limitBefore = Error.stackTraceLimit;
84 Error.stackTraceLimit = Infinity;
85 const err = new Error();
86 Error.stackTraceLimit = limitBefore;
87 return err;
88}
89
90function validateExpectations(assertion, expectations, numberArgs) { // eslint-disable-line complexity
91 if (numberArgs === 1 || expectations === null || expectations === undefined) {
92 expectations = {};
93 } else if (
94 typeof expectations === 'function' ||
95 typeof expectations === 'string' ||
96 expectations instanceof RegExp ||
97 typeof expectations !== 'object' ||
98 Array.isArray(expectations) ||
99 Object.keys(expectations).length === 0
100 ) {
101 throw new AssertionError({
102 assertion,
103 message: `The second argument to \`t.${assertion}()\` must be an expectation object, \`null\` or \`undefined\``,
104 values: [formatWithLabel('Called with:', expectations)]
105 });
106 } else {
107 if (hasOwnProperty(expectations, 'instanceOf') && typeof expectations.instanceOf !== 'function') {
108 throw new AssertionError({
109 assertion,
110 message: `The \`instanceOf\` property of the second argument to \`t.${assertion}()\` must be a function`,
111 values: [formatWithLabel('Called with:', expectations)]
112 });
113 }
114
115 if (hasOwnProperty(expectations, 'message') && typeof expectations.message !== 'string' && !(expectations.message instanceof RegExp)) {
116 throw new AssertionError({
117 assertion,
118 message: `The \`message\` property of the second argument to \`t.${assertion}()\` must be a string or regular expression`,
119 values: [formatWithLabel('Called with:', expectations)]
120 });
121 }
122
123 if (hasOwnProperty(expectations, 'name') && typeof expectations.name !== 'string') {
124 throw new AssertionError({
125 assertion,
126 message: `The \`name\` property of the second argument to \`t.${assertion}()\` must be a string`,
127 values: [formatWithLabel('Called with:', expectations)]
128 });
129 }
130
131 if (hasOwnProperty(expectations, 'code') && typeof expectations.code !== 'string' && typeof expectations.code !== 'number') {
132 throw new AssertionError({
133 assertion,
134 message: `The \`code\` property of the second argument to \`t.${assertion}()\` must be a string or number`,
135 values: [formatWithLabel('Called with:', expectations)]
136 });
137 }
138
139 for (const key of Object.keys(expectations)) {
140 switch (key) {
141 case 'instanceOf':
142 case 'is':
143 case 'message':
144 case 'name':
145 case 'code':
146 continue;
147 default:
148 throw new AssertionError({
149 assertion,
150 message: `The second argument to \`t.${assertion}()\` contains unexpected properties`,
151 values: [formatWithLabel('Called with:', expectations)]
152 });
153 }
154 }
155 }
156
157 return expectations;
158}
159
160// Note: this function *must* throw exceptions, since it can be used
161// as part of a pending assertion for promises.
162function assertExpectations({assertion, actual, expectations, message, prefix, savedError}) {
163 if (!isError(actual)) {
164 throw new AssertionError({
165 assertion,
166 message,
167 savedError,
168 values: [formatWithLabel(`${prefix} exception that is not an error:`, actual)]
169 });
170 }
171
172 const actualStack = actual.stack;
173
174 if (hasOwnProperty(expectations, 'is') && actual !== expectations.is) {
175 throw new AssertionError({
176 assertion,
177 message,
178 savedError,
179 actualStack,
180 values: [
181 formatWithLabel(`${prefix} unexpected exception:`, actual),
182 formatWithLabel('Expected to be strictly equal to:', expectations.is)
183 ]
184 });
185 }
186
187 if (expectations.instanceOf && !(actual instanceof expectations.instanceOf)) {
188 throw new AssertionError({
189 assertion,
190 message,
191 savedError,
192 actualStack,
193 values: [
194 formatWithLabel(`${prefix} unexpected exception:`, actual),
195 formatWithLabel('Expected instance of:', expectations.instanceOf)
196 ]
197 });
198 }
199
200 if (typeof expectations.name === 'string' && actual.name !== expectations.name) {
201 throw new AssertionError({
202 assertion,
203 message,
204 savedError,
205 actualStack,
206 values: [
207 formatWithLabel(`${prefix} unexpected exception:`, actual),
208 formatWithLabel('Expected name to equal:', expectations.name)
209 ]
210 });
211 }
212
213 if (typeof expectations.message === 'string' && actual.message !== expectations.message) {
214 throw new AssertionError({
215 assertion,
216 message,
217 savedError,
218 actualStack,
219 values: [
220 formatWithLabel(`${prefix} unexpected exception:`, actual),
221 formatWithLabel('Expected message to equal:', expectations.message)
222 ]
223 });
224 }
225
226 if (expectations.message instanceof RegExp && !expectations.message.test(actual.message)) {
227 throw new AssertionError({
228 assertion,
229 message,
230 savedError,
231 actualStack,
232 values: [
233 formatWithLabel(`${prefix} unexpected exception:`, actual),
234 formatWithLabel('Expected message to match:', expectations.message)
235 ]
236 });
237 }
238
239 if (typeof expectations.code !== 'undefined' && actual.code !== expectations.code) {
240 throw new AssertionError({
241 assertion,
242 message,
243 savedError,
244 actualStack,
245 values: [
246 formatWithLabel(`${prefix} unexpected exception:`, actual),
247 formatWithLabel('Expected code to equal:', expectations.code)
248 ]
249 });
250 }
251}
252
253class Assertions {
254 constructor({
255 pass = notImplemented,
256 pending = notImplemented,
257 fail = notImplemented,
258 skip = notImplemented,
259 compareWithSnapshot = notImplemented,
260 powerAssert,
261 experiments = {},
262 disableSnapshots = false
263 } = {}) {
264 const withSkip = assertionFn => {
265 assertionFn.skip = skip;
266 return assertionFn;
267 };
268
269 // When adding new enhanced functions with new patterns, don't forget to
270 // enable the pattern in the power-assert compilation step in @ava/babel.
271 const withPowerAssert = (pattern, assertionFn) => powerAssert.empower(assertionFn, {
272 onError: event => {
273 if (event.powerAssertContext) {
274 event.error.statements = powerAssert.format(event.powerAssertContext, formatPowerAssertValue);
275 }
276
277 fail(event.error);
278 },
279 onSuccess: () => {
280 pass();
281 },
282 bindReceiver: false,
283 patterns: [pattern]
284 });
285
286 const checkMessage = (assertion, message, powerAssert = false) => {
287 const result = checkAssertionMessage(assertion, message);
288 if (result === true) {
289 return this.true;
290 }
291
292 if (powerAssert) {
293 throw result;
294 }
295
296 fail(result);
297 return false;
298 };
299
300 this.pass = withSkip(() => {
301 pass();
302 });
303
304 this.fail = withSkip(message => {
305 if (!checkMessage('fail', message)) {
306 return;
307 }
308
309 fail(new AssertionError({
310 assertion: 'fail',
311 message: message || 'Test failed via `t.fail()`'
312 }));
313 });
314
315 this.is = withSkip((actual, expected, message) => {
316 if (!checkMessage('is', message)) {
317 return;
318 }
319
320 if (Object.is(actual, expected)) {
321 pass();
322 } else {
323 const result = concordance.compare(actual, expected, concordanceOptions);
324 const actualDescriptor = result.actual || concordance.describe(actual, concordanceOptions);
325 const expectedDescriptor = result.expected || concordance.describe(expected, concordanceOptions);
326
327 if (result.pass) {
328 fail(new AssertionError({
329 assertion: 'is',
330 message,
331 raw: {actual, expected},
332 values: [formatDescriptorWithLabel('Values are deeply equal to each other, but they are not the same:', actualDescriptor)]
333 }));
334 } else {
335 fail(new AssertionError({
336 assertion: 'is',
337 message,
338 raw: {actual, expected},
339 values: [formatDescriptorDiff(actualDescriptor, expectedDescriptor)]
340 }));
341 }
342 }
343 });
344
345 this.not = withSkip((actual, expected, message) => {
346 if (!checkMessage('not', message)) {
347 return;
348 }
349
350 if (Object.is(actual, expected)) {
351 fail(new AssertionError({
352 assertion: 'not',
353 message,
354 raw: {actual, expected},
355 values: [formatWithLabel('Value is the same as:', actual)]
356 }));
357 } else {
358 pass();
359 }
360 });
361
362 this.deepEqual = withSkip((actual, expected, message) => {
363 if (!checkMessage('deepEqual', message)) {
364 return;
365 }
366
367 const result = concordance.compare(actual, expected, concordanceOptions);
368 if (result.pass) {
369 pass();
370 } else {
371 const actualDescriptor = result.actual || concordance.describe(actual, concordanceOptions);
372 const expectedDescriptor = result.expected || concordance.describe(expected, concordanceOptions);
373 fail(new AssertionError({
374 assertion: 'deepEqual',
375 message,
376 raw: {actual, expected},
377 values: [formatDescriptorDiff(actualDescriptor, expectedDescriptor)]
378 }));
379 }
380 });
381
382 this.notDeepEqual = withSkip((actual, expected, message) => {
383 if (!checkMessage('notDeepEqual', message)) {
384 return;
385 }
386
387 const result = concordance.compare(actual, expected, concordanceOptions);
388 if (result.pass) {
389 const actualDescriptor = result.actual || concordance.describe(actual, concordanceOptions);
390 fail(new AssertionError({
391 assertion: 'notDeepEqual',
392 message,
393 raw: {actual, expected},
394 values: [formatDescriptorWithLabel('Value is deeply equal:', actualDescriptor)]
395 }));
396 } else {
397 pass();
398 }
399 });
400
401 this.like = withSkip((actual, selector, message) => {
402 if (!checkMessage('like', message)) {
403 return;
404 }
405
406 if (!isLikeSelector(selector)) {
407 fail(new AssertionError({
408 assertion: 'like',
409 improperUsage: true,
410 message: '`t.like()` selector must be a non-empty object',
411 values: [formatWithLabel('Called with:', selector)]
412 }));
413 return;
414 }
415
416 let comparable;
417 try {
418 comparable = selectComparable(actual, selector);
419 } catch (error) {
420 if (error === CIRCULAR_SELECTOR) {
421 fail(new AssertionError({
422 assertion: 'like',
423 improperUsage: true,
424 message: '`t.like()` selector must not contain circular references',
425 values: [formatWithLabel('Called with:', selector)]
426 }));
427 return;
428 }
429
430 throw error;
431 }
432
433 const result = concordance.compare(comparable, selector, concordanceOptions);
434 if (result.pass) {
435 pass();
436 } else {
437 const actualDescriptor = result.actual || concordance.describe(comparable, concordanceOptions);
438 const expectedDescriptor = result.expected || concordance.describe(selector, concordanceOptions);
439 fail(new AssertionError({
440 assertion: 'like',
441 message,
442 values: [formatDescriptorDiff(actualDescriptor, expectedDescriptor)]
443 }));
444 }
445 });
446
447 this.throws = withSkip((...args) => {
448 // Since arrow functions do not support 'arguments', we are using rest
449 // operator, so we can determine the total number of arguments passed
450 // to the function.
451 let [fn, expectations, message] = args;
452
453 if (!checkMessage('throws', message)) {
454 return;
455 }
456
457 if (typeof fn !== 'function') {
458 fail(new AssertionError({
459 assertion: 'throws',
460 improperUsage: true,
461 message: '`t.throws()` must be called with a function',
462 values: [formatWithLabel('Called with:', fn)]
463 }));
464 return;
465 }
466
467 try {
468 expectations = validateExpectations('throws', expectations, args.length);
469 } catch (error) {
470 fail(error);
471 return;
472 }
473
474 let retval;
475 let actual = null;
476 try {
477 retval = fn();
478 if (isPromise(retval)) {
479 // Here isPromise() checks if something is "promise like". Cast to an actual promise.
480 Promise.resolve(retval).catch(noop);
481 fail(new AssertionError({
482 assertion: 'throws',
483 message,
484 values: [formatWithLabel('Function returned a promise. Use `t.throwsAsync()` instead:', retval)]
485 }));
486 return;
487 }
488 } catch (error) {
489 actual = error;
490 }
491
492 if (!actual) {
493 fail(new AssertionError({
494 assertion: 'throws',
495 message,
496 values: [formatWithLabel('Function returned:', retval)]
497 }));
498 return;
499 }
500
501 try {
502 assertExpectations({
503 assertion: 'throws',
504 actual,
505 expectations,
506 message,
507 prefix: 'Function threw'
508 });
509 pass();
510 return actual;
511 } catch (error) {
512 fail(error);
513 }
514 });
515
516 this.throwsAsync = withSkip((...args) => {
517 let [thrower, expectations, message] = args;
518
519 if (!checkMessage('throwsAsync', message)) {
520 return Promise.resolve();
521 }
522
523 if (typeof thrower !== 'function' && !isPromise(thrower)) {
524 fail(new AssertionError({
525 assertion: 'throwsAsync',
526 improperUsage: true,
527 message: '`t.throwsAsync()` must be called with a function or promise',
528 values: [formatWithLabel('Called with:', thrower)]
529 }));
530 return Promise.resolve();
531 }
532
533 try {
534 expectations = validateExpectations('throwsAsync', expectations, args.length);
535 } catch (error) {
536 fail(error);
537 return Promise.resolve();
538 }
539
540 const handlePromise = (promise, wasReturned) => {
541 // Create an error object to record the stack before it gets lost in the promise chain.
542 const savedError = getErrorWithLongStackTrace();
543 // Handle "promise like" objects by casting to a real Promise.
544 const intermediate = Promise.resolve(promise).then(value => { // eslint-disable-line promise/prefer-await-to-then
545 throw new AssertionError({
546 assertion: 'throwsAsync',
547 message,
548 savedError,
549 values: [formatWithLabel(`${wasReturned ? 'Returned promise' : 'Promise'} resolved with:`, value)]
550 });
551 }, error => {
552 assertExpectations({
553 assertion: 'throwsAsync',
554 actual: error,
555 expectations,
556 message,
557 prefix: `${wasReturned ? 'Returned promise' : 'Promise'} rejected with`,
558 savedError
559 });
560 return error;
561 });
562
563 pending(intermediate);
564 // Don't reject the returned promise, even if the assertion fails.
565 return intermediate.catch(noop);
566 };
567
568 if (isPromise(thrower)) {
569 return handlePromise(thrower, false);
570 }
571
572 let retval;
573 let actual = null;
574 try {
575 retval = thrower();
576 } catch (error) {
577 actual = error;
578 }
579
580 if (actual) {
581 fail(new AssertionError({
582 assertion: 'throwsAsync',
583 message,
584 actualStack: actual.stack,
585 values: [formatWithLabel('Function threw synchronously. Use `t.throws()` instead:', actual)]
586 }));
587 return Promise.resolve();
588 }
589
590 if (isPromise(retval)) {
591 return handlePromise(retval, true);
592 }
593
594 fail(new AssertionError({
595 assertion: 'throwsAsync',
596 message,
597 values: [formatWithLabel('Function returned:', retval)]
598 }));
599 return Promise.resolve();
600 });
601
602 this.notThrows = withSkip((fn, message) => {
603 if (!checkMessage('notThrows', message)) {
604 return;
605 }
606
607 if (typeof fn !== 'function') {
608 fail(new AssertionError({
609 assertion: 'notThrows',
610 improperUsage: true,
611 message: '`t.notThrows()` must be called with a function',
612 values: [formatWithLabel('Called with:', fn)]
613 }));
614 return;
615 }
616
617 try {
618 fn();
619 } catch (error) {
620 fail(new AssertionError({
621 assertion: 'notThrows',
622 message,
623 actualStack: error.stack,
624 values: [formatWithLabel('Function threw:', error)]
625 }));
626 return;
627 }
628
629 pass();
630 });
631
632 this.notThrowsAsync = withSkip((nonThrower, message) => {
633 if (!checkMessage('notThrowsAsync', message)) {
634 return Promise.resolve();
635 }
636
637 if (typeof nonThrower !== 'function' && !isPromise(nonThrower)) {
638 fail(new AssertionError({
639 assertion: 'notThrowsAsync',
640 improperUsage: true,
641 message: '`t.notThrowsAsync()` must be called with a function or promise',
642 values: [formatWithLabel('Called with:', nonThrower)]
643 }));
644 return Promise.resolve();
645 }
646
647 const handlePromise = (promise, wasReturned) => {
648 // Create an error object to record the stack before it gets lost in the promise chain.
649 const savedError = getErrorWithLongStackTrace();
650 // Handle "promise like" objects by casting to a real Promise.
651 const intermediate = Promise.resolve(promise).then(noop, error => { // eslint-disable-line promise/prefer-await-to-then
652 throw new AssertionError({
653 assertion: 'notThrowsAsync',
654 message,
655 savedError,
656 values: [formatWithLabel(`${wasReturned ? 'Returned promise' : 'Promise'} rejected with:`, error)]
657 });
658 });
659 pending(intermediate);
660 // Don't reject the returned promise, even if the assertion fails.
661 return intermediate.catch(noop);
662 };
663
664 if (isPromise(nonThrower)) {
665 return handlePromise(nonThrower, false);
666 }
667
668 let retval;
669 try {
670 retval = nonThrower();
671 } catch (error) {
672 fail(new AssertionError({
673 assertion: 'notThrowsAsync',
674 message,
675 actualStack: error.stack,
676 values: [formatWithLabel('Function threw:', error)]
677 }));
678 return Promise.resolve();
679 }
680
681 if (!isPromise(retval)) {
682 fail(new AssertionError({
683 assertion: 'notThrowsAsync',
684 message,
685 values: [formatWithLabel('Function did not return a promise. Use `t.notThrows()` instead:', retval)]
686 }));
687 return Promise.resolve();
688 }
689
690 return handlePromise(retval, true);
691 });
692
693 this.snapshot = withSkip((expected, ...rest) => {
694 if (disableSnapshots && experiments.disableSnapshotsInHooks) {
695 fail(new AssertionError({
696 assertion: 'snapshot',
697 message: '`t.snapshot()` can only be used in tests',
698 improperUsage: true
699 }));
700 return;
701 }
702
703 let message;
704 let snapshotOptions;
705 if (rest.length > 1) {
706 [snapshotOptions, message] = rest;
707 } else {
708 const [optionsOrMessage] = rest;
709 if (typeof optionsOrMessage === 'object') {
710 snapshotOptions = optionsOrMessage;
711 } else {
712 message = optionsOrMessage;
713 }
714 }
715
716 if (!checkMessage('snapshot', message)) {
717 return;
718 }
719
720 let result;
721 try {
722 result = compareWithSnapshot({
723 expected,
724 id: snapshotOptions ? snapshotOptions.id : undefined,
725 message
726 });
727 } catch (error) {
728 if (!(error instanceof snapshotManager.SnapshotError)) {
729 throw error;
730 }
731
732 const improperUsage = {name: error.name, snapPath: error.snapPath};
733 if (error instanceof snapshotManager.VersionMismatchError) {
734 improperUsage.snapVersion = error.snapVersion;
735 improperUsage.expectedVersion = error.expectedVersion;
736 }
737
738 fail(new AssertionError({
739 assertion: 'snapshot',
740 message: message || 'Could not compare snapshot',
741 improperUsage
742 }));
743 return;
744 }
745
746 if (result.pass) {
747 pass();
748 } else if (result.actual) {
749 fail(new AssertionError({
750 assertion: 'snapshot',
751 message: message || 'Did not match snapshot',
752 values: [formatDescriptorDiff(result.actual, result.expected, {invert: true})]
753 }));
754 } else {
755 // This can only occur in CI environments.
756 fail(new AssertionError({
757 assertion: 'snapshot',
758 message: message || 'No snapshot available — new snapshots are not created in CI environments'
759 }));
760 }
761 });
762
763 this.truthy = withSkip((actual, message) => {
764 if (!checkMessage('truthy', message)) {
765 return;
766 }
767
768 if (actual) {
769 pass();
770 } else {
771 fail(new AssertionError({
772 assertion: 'truthy',
773 message,
774 operator: '!!',
775 values: [formatWithLabel('Value is not truthy:', actual)]
776 }));
777 }
778 });
779
780 this.falsy = withSkip((actual, message) => {
781 if (!checkMessage('falsy', message)) {
782 return;
783 }
784
785 if (actual) {
786 fail(new AssertionError({
787 assertion: 'falsy',
788 message,
789 operator: '!',
790 values: [formatWithLabel('Value is not falsy:', actual)]
791 }));
792 } else {
793 pass();
794 }
795 });
796
797 this.true = withSkip((actual, message) => {
798 if (!checkMessage('true', message)) {
799 return;
800 }
801
802 if (actual === true) {
803 pass();
804 } else {
805 fail(new AssertionError({
806 assertion: 'true',
807 message,
808 values: [formatWithLabel('Value is not `true`:', actual)]
809 }));
810 }
811 });
812
813 this.false = withSkip((actual, message) => {
814 if (!checkMessage('false', message)) {
815 return;
816 }
817
818 if (actual === false) {
819 pass();
820 } else {
821 fail(new AssertionError({
822 assertion: 'false',
823 message,
824 values: [formatWithLabel('Value is not `false`:', actual)]
825 }));
826 }
827 });
828
829 this.regex = withSkip((string, regex, message) => {
830 if (!checkMessage('regex', message)) {
831 return;
832 }
833
834 if (typeof string !== 'string') {
835 fail(new AssertionError({
836 assertion: 'regex',
837 improperUsage: true,
838 message: '`t.regex()` must be called with a string',
839 values: [formatWithLabel('Called with:', string)]
840 }));
841 return;
842 }
843
844 if (!(regex instanceof RegExp)) {
845 fail(new AssertionError({
846 assertion: 'regex',
847 improperUsage: true,
848 message: '`t.regex()` must be called with a regular expression',
849 values: [formatWithLabel('Called with:', regex)]
850 }));
851 return;
852 }
853
854 if (!regex.test(string)) {
855 fail(new AssertionError({
856 assertion: 'regex',
857 message,
858 values: [
859 formatWithLabel('Value must match expression:', string),
860 formatWithLabel('Regular expression:', regex)
861 ]
862 }));
863 return;
864 }
865
866 pass();
867 });
868
869 this.notRegex = withSkip((string, regex, message) => {
870 if (!checkMessage('notRegex', message)) {
871 return;
872 }
873
874 if (typeof string !== 'string') {
875 fail(new AssertionError({
876 assertion: 'notRegex',
877 improperUsage: true,
878 message: '`t.notRegex()` must be called with a string',
879 values: [formatWithLabel('Called with:', string)]
880 }));
881 return;
882 }
883
884 if (!(regex instanceof RegExp)) {
885 fail(new AssertionError({
886 assertion: 'notRegex',
887 improperUsage: true,
888 message: '`t.notRegex()` must be called with a regular expression',
889 values: [formatWithLabel('Called with:', regex)]
890 }));
891 return;
892 }
893
894 if (regex.test(string)) {
895 fail(new AssertionError({
896 assertion: 'notRegex',
897 message,
898 values: [
899 formatWithLabel('Value must not match expression:', string),
900 formatWithLabel('Regular expression:', regex)
901 ]
902 }));
903 return;
904 }
905
906 pass();
907 });
908
909 if (powerAssert === undefined) {
910 this.assert = withSkip((actual, message) => {
911 if (!checkMessage('assert', message)) {
912 return;
913 }
914
915 if (!actual) {
916 fail(new AssertionError({
917 assertion: 'assert',
918 message,
919 operator: '!!',
920 values: [formatWithLabel('Value is not truthy:', actual)]
921 }));
922 return;
923 }
924
925 pass();
926 });
927 } else {
928 this.assert = withSkip(withPowerAssert(
929 'assert(value, [message])',
930 (actual, message) => {
931 checkMessage('assert', message, true);
932
933 if (!actual) {
934 throw new AssertionError({
935 assertion: 'assert',
936 message,
937 operator: '!!',
938 values: [formatWithLabel('Value is not truthy:', actual)]
939 });
940 }
941 })
942 );
943 }
944 }
945}
946exports.Assertions = Assertions;