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