UNPKG

56.2 kBJavaScriptView Raw
1var createStandardErrorMessage = require('./createStandardErrorMessage');
2var utils = require('./utils');
3var magicpen = require('magicpen');
4var extend = utils.extend;
5var leven = require('leven');
6var makePromise = require('./makePromise');
7var addAdditionalPromiseMethods = require('./addAdditionalPromiseMethods');
8var wrapPromiseIfNecessary = require('./wrapPromiseIfNecessary');
9var oathbreaker = require('./oathbreaker');
10var UnexpectedError = require('./UnexpectedError');
11var notifyPendingPromise = require('./notifyPendingPromise');
12var defaultDepth = require('./defaultDepth');
13var AssertionString = require('./AssertionString');
14var Context = require('./Context');
15var throwIfNonUnexpectedError = require('./throwIfNonUnexpectedError');
16var ensureValidUseOfParenthesesOrBrackets = require('./ensureValidUseOfParenthesesOrBrackets');
17var expandAssertion = require('./expandAssertion');
18var nodeJsCustomInspect = require('./nodeJsCustomInspect');
19
20function isAssertionArg(ref) {
21 var type = ref.type;
22
23 return type.is('assertion');
24}
25
26var anyType = {
27 _unexpectedType: true,
28 name: 'any',
29 level: 0,
30 identify: function identify() {
31 return true;
32 },
33 equal: utils.objectIs,
34 inspect: function inspect(value, depth, output) {
35 if (output && output.isMagicPen) {
36 return output.text(value);
37 } else {
38 // Guard against node.js' require('util').inspect eagerly calling .inspect() on objects
39 return ("type: " + (this.name));
40 }
41 },
42 diff: function diff(actual, expected, output, diff$1, inspect) {
43 return null;
44 },
45 typeEqualityCache: {},
46 is: function is(typeOrTypeName) {
47 var typeName;
48 if (typeof typeOrTypeName === 'string') {
49 typeName = typeOrTypeName;
50 } else {
51 typeName = typeOrTypeName.name;
52 }
53
54 var cachedValue = this.typeEqualityCache[typeName];
55 if (typeof cachedValue !== 'undefined') {
56 return cachedValue;
57 }
58
59 var result = false;
60 if (this.name === typeName) {
61 result = true;
62 } else if (this.baseType) {
63 result = this.baseType.is(typeName);
64 }
65 this.typeEqualityCache[typeName] = result;
66 return result;
67 }
68};
69
70if (nodeJsCustomInspect !== 'inspect') {
71 anyType[nodeJsCustomInspect] = function() {
72 return ("type: " + (this.name));
73 };
74}
75
76var OR = {};
77function getOrGroups(expectations) {
78 var orGroups = [[]];
79 expectations.forEach(function (expectation) {
80 if (expectation === OR) {
81 orGroups.push([]);
82 } else {
83 orGroups[orGroups.length - 1].push(expectation);
84 }
85 });
86 return orGroups;
87}
88
89function evaluateGroup(unexpected, context, subject, orGroup) {
90 return orGroup.map(function (expectation) {
91 var args = Array.prototype.slice.call(expectation);
92 args.unshift(subject);
93 return {
94 expectation: args,
95 promise: makePromise(function () {
96 if (typeof args[1] === 'function') {
97 if (args.length > 2) {
98 throw new Error(
99 'expect.it(<function>) does not accept additional arguments'
100 );
101 } else {
102 // expect.it(function (value) { ... })
103 return args[1](args[0]);
104 }
105 } else {
106 return unexpected._expect(context.child(), args);
107 }
108 })
109 };
110 });
111}
112
113function writeGroupEvaluationsToOutput(output, groupEvaluations) {
114 var hasOrClauses = groupEvaluations.length > 1;
115 var hasAndClauses = groupEvaluations.some(function (ref) {
116 var length = ref.length;
117
118 return length > 1;
119 });
120 groupEvaluations.forEach(function (groupEvaluation, i) {
121 if (i > 0) {
122 if (hasAndClauses) {
123 output.nl();
124 } else {
125 output.sp();
126 }
127 output.jsComment('or').nl();
128 }
129
130 var groupFailed = false;
131 groupEvaluation.forEach(function (evaluation, j) {
132 if (j > 0) {
133 output.jsComment(' and').nl();
134 }
135 var isRejected = evaluation.promise.isRejected();
136 if (isRejected && !groupFailed) {
137 groupFailed = true;
138 var err = evaluation.promise.reason();
139
140 if (hasAndClauses || hasOrClauses) {
141 output.error('⨯ ');
142 }
143
144 output.block(function (output) {
145 output.append(err.getErrorMessage(output));
146 });
147 } else {
148 if (isRejected) {
149 output.error('⨯ ');
150 } else {
151 output.success('✓ ');
152 }
153
154 var expectation = evaluation.expectation;
155 output.block(function (output) {
156 var subject = expectation[0];
157 var subjectOutput = function (output) {
158 output.appendInspected(subject);
159 };
160 var args = expectation.slice(2);
161 var argsOutput = args.map(function (arg) { return function (output) {
162 output.appendInspected(arg);
163 }; });
164 var testDescription = expectation[1];
165 createStandardErrorMessage(
166 output,
167 subjectOutput,
168 testDescription,
169 argsOutput,
170 {
171 subject: subject
172 }
173 );
174 });
175 }
176 });
177 });
178}
179
180function createExpectIt(expect, expectations) {
181 var orGroups = getOrGroups(expectations);
182
183 function expectIt(subject, context) {
184 context =
185 context && typeof context === 'object' && context instanceof Context
186 ? context
187 : new Context(expect);
188
189 if (
190 orGroups.length === 1 &&
191 orGroups[0].length === 1 &&
192 orGroups[0][0].length === 1 &&
193 typeof orGroups[0][0][0] === 'function'
194 ) {
195 // expect.it(subject => ...)
196 return oathbreaker(orGroups[0][0][0](subject));
197 }
198
199 var groupEvaluations = [];
200 var promises = [];
201 orGroups.forEach(function (orGroup) {
202 var evaluations = evaluateGroup(expect, context, subject, orGroup);
203 evaluations.forEach(function (ref) {
204 var promise = ref.promise;
205
206 promises.push(promise);
207 });
208 groupEvaluations.push(evaluations);
209 });
210
211 return oathbreaker(
212 makePromise.settle(promises).then(function () {
213 groupEvaluations.forEach(function (groupEvaluation) {
214 groupEvaluation.forEach(function (ref) {
215 var promise = ref.promise;
216
217 if (
218 promise.isRejected() &&
219 promise.reason().errorMode === 'bubbleThrough'
220 ) {
221 throw promise.reason();
222 }
223 });
224 });
225
226 if (
227 !groupEvaluations.some(function (groupEvaluation) { return groupEvaluation.every(function (ref) {
228 var promise = ref.promise;
229
230 return promise.isFulfilled();
231 }); }
232 )
233 ) {
234 expect.fail(function (output) {
235 writeGroupEvaluationsToOutput(output, groupEvaluations);
236 });
237 }
238 })
239 );
240 }
241 expectIt._expectIt = true;
242 expectIt._expectations = expectations;
243 expectIt._OR = OR;
244 expectIt.and = function() {
245 var args = [], len = arguments.length;
246 while ( len-- ) args[ len ] = arguments[ len ];
247
248 var copiedExpectations = expectations.slice();
249 copiedExpectations.push(args);
250 return createExpectIt(expect, copiedExpectations);
251 };
252 expectIt.or = function() {
253 var args = [], len = arguments.length;
254 while ( len-- ) args[ len ] = arguments[ len ];
255
256 var copiedExpectations = expectations.slice();
257 copiedExpectations.push(OR, args);
258 return createExpectIt(expect, copiedExpectations);
259 };
260 return expectIt;
261}
262
263var expectPrototype = {
264 promise: makePromise,
265 notifyPendingPromise: notifyPendingPromise,
266 errorMode: 'default'
267};
268utils.setPrototypeOfOrExtend(expectPrototype, Function.prototype);
269
270expectPrototype.it = function() {
271 var args = [], len = arguments.length;
272 while ( len-- ) args[ len ] = arguments[ len ];
273
274 if (this.flags && typeof args[0] === 'string') {
275 args[0] = utils.forwardFlags(args[0], this.flags);
276 }
277 return createExpectIt(this._topLevelExpect, [args]);
278};
279
280expectPrototype.equal = function(actual, expected, depth, seen) {
281 var this$1 = this;
282
283 depth = typeof depth === 'number' ? depth : 100;
284 if (depth <= 0) {
285 // detect recursive loops in the structure
286 seen = seen || [];
287 if (seen.indexOf(actual) !== -1) {
288 throw new Error('Cannot compare circular structures');
289 }
290 seen.push(actual);
291 }
292
293 return this.findCommonType(actual, expected).equal(actual, expected, function (a, b) { return this$1.equal(a, b, depth - 1, seen); }
294 );
295};
296
297expectPrototype.inspect = function(obj, depth, outputOrFormat) {
298 var this$1 = this;
299
300 var seen = [];
301 var printOutput = function (obj, currentDepth, output) {
302 var objType = this$1.findTypeOf(obj);
303 if (currentDepth <= 0 && objType.is('object') && !objType.is('expect.it')) {
304 return output.text('...');
305 }
306
307 seen = seen || [];
308 if (seen.indexOf(obj) !== -1) {
309 return output.text('[Circular]');
310 }
311
312 return objType.inspect(obj, currentDepth, output, function (v, childDepth) {
313 output = output.clone();
314 seen.push(obj);
315 if (typeof childDepth === 'undefined') {
316 childDepth = currentDepth - 1;
317 }
318 output = printOutput(v, childDepth, output) || output;
319 seen.pop();
320 return output;
321 });
322 };
323
324 var output =
325 typeof outputOrFormat === 'string'
326 ? this.createOutput(outputOrFormat)
327 : outputOrFormat;
328 output = output || this.createOutput();
329 return (
330 printOutput(
331 obj,
332 typeof depth === 'number' ? depth : defaultDepth,
333 output
334 ) || output
335 );
336};
337
338if (nodeJsCustomInspect !== 'inspect') {
339 expectPrototype[nodeJsCustomInspect] = expectPrototype.inspect;
340}
341
342expectPrototype.expandTypeAlternations = function(assertion) {
343 var this$1 = this;
344
345 var createPermutations = function (args, i) {
346 if (i === args.length) {
347 return [];
348 }
349
350 var result = [];
351 args[i].forEach(function (arg) {
352 var tails = createPermutations(args, i + 1);
353 if (tails.length) {
354 tails.forEach(function (tail) {
355 result.push([arg].concat(tail));
356 });
357 } else if (arg.type.is('assertion')) {
358 result.push([
359 { type: arg.type, minimum: 1, maximum: 1 },
360 { type: this$1.getType('any'), minimum: 0, maximum: Infinity }
361 ]);
362 result.push([
363 { type: this$1.getType('expect.it'), minimum: 1, maximum: 1 }
364 ]);
365 if (arg.minimum === 0) {
366 // <assertion?>
367 result.push([]);
368 }
369 } else {
370 result.push([arg]);
371 }
372 });
373 return result;
374 };
375 var result = [];
376 assertion.subject.forEach(function (subjectRequirement) {
377 if (assertion.args.length) {
378 createPermutations(assertion.args, 0).forEach(function (args) {
379 result.push(
380 extend({}, assertion, {
381 subject: subjectRequirement,
382 args: args
383 })
384 );
385 });
386 } else {
387 result.push(
388 extend({}, assertion, {
389 subject: subjectRequirement,
390 args: []
391 })
392 );
393 }
394 });
395 return result;
396};
397
398expectPrototype.parseAssertion = function(assertionString) {
399 var this$1 = this;
400
401 var tokens = [];
402 var nextIndex = 0;
403
404 var parseTypeToken = function (typeToken) {
405 return typeToken.split('|').map(function (typeDeclaration) {
406 var matchNameAndOperator = typeDeclaration.match(
407 /^([a-z_](?:|[a-z0-9_.-]*[_a-z0-9]))([+*?]|)$/i
408 );
409 if (!matchNameAndOperator) {
410 throw new SyntaxError(
411 ("Cannot parse type declaration:" + typeDeclaration)
412 );
413 }
414 var type = this$1.getType(matchNameAndOperator[1]);
415 if (!type) {
416 throw new Error(
417 ("Unknown type: " + (matchNameAndOperator[1]) + " in " + assertionString)
418 );
419 }
420 var operator = matchNameAndOperator[2];
421 return {
422 minimum: !operator || operator === '+' ? 1 : 0,
423 maximum: operator === '*' || operator === '+' ? Infinity : 1,
424 type: type
425 };
426 });
427 };
428
429 function hasVarargs(types) {
430 return types.some(function (ref) {
431 var minimum = ref.minimum;
432 var maximum = ref.maximum;
433
434 return minimum !== 1 || maximum !== 1;
435 });
436 }
437 assertionString.replace(
438 /\s*<((?:[a-z_](?:|[a-z0-9_.-]*[_a-z0-9])[?*+]?)(?:\|(?:[a-z_](?:|[a-z0-9_.-]*[_a-z0-9])[?*+]?))*)>|\s*([^<]+)/gi,
439 function (ref, $1, $2, index) {
440 var length = ref.length;
441
442 if (index !== nextIndex) {
443 throw new SyntaxError(
444 ("Cannot parse token at index " + nextIndex + " in " + assertionString)
445 );
446 }
447 if ($1) {
448 tokens.push(parseTypeToken($1));
449 } else {
450 tokens.push($2.trim());
451 }
452 nextIndex += length;
453 }
454 );
455
456 var assertion;
457 if (tokens.length === 1 && typeof tokens[0] === 'string') {
458 if (!this._legacyTypelessAssertionWarned) {
459 console.warn(
460 'The typeless expect.addAssertion syntax is deprecated and will be removed in a future update\n' +
461 'Please refer to http://unexpected.js.org/api/addAssertion/'
462 );
463 this._legacyTypelessAssertionWarned = true;
464 }
465 assertion = {
466 subject: parseTypeToken('any'),
467 assertion: tokens[0],
468 args: [parseTypeToken('any*')]
469 };
470 } else {
471 assertion = {
472 subject: tokens[0],
473 assertion: tokens[1],
474 args: tokens.slice(2)
475 };
476 }
477
478 if (!Array.isArray(assertion.subject)) {
479 throw new SyntaxError(("Missing subject type in " + assertionString));
480 }
481 if (typeof assertion.assertion !== 'string') {
482 throw new SyntaxError(("Missing assertion in " + assertionString));
483 }
484 if (hasVarargs(assertion.subject)) {
485 throw new SyntaxError(
486 ("The subject type cannot have varargs: " + assertionString)
487 );
488 }
489 if (assertion.args.some(function (arg) { return typeof arg === 'string'; })) {
490 throw new SyntaxError('Only one assertion string is supported (see #225)');
491 }
492
493 if (assertion.args.slice(0, -1).some(hasVarargs)) {
494 throw new SyntaxError(
495 ("Only the last argument type can have varargs: " + assertionString)
496 );
497 }
498 if (
499 [assertion.subject]
500 .concat(assertion.args.slice(0, -1))
501 .some(function (argRequirements) { return argRequirements.some(function (ref) {
502 var type = ref.type;
503
504 return type.is('assertion');
505 }); }
506 )
507 ) {
508 throw new SyntaxError(
509 ("Only the last argument type can be <assertion>: " + assertionString)
510 );
511 }
512
513 var lastArgRequirements = assertion.args[assertion.args.length - 1] || [];
514 var assertionRequirements = lastArgRequirements.filter(function (ref) {
515 var type = ref.type;
516
517 return type.is('assertion');
518 }
519 );
520
521 if (assertionRequirements.length > 0 && lastArgRequirements.length > 1) {
522 throw new SyntaxError(
523 ("<assertion> cannot be alternated with other types: " + assertionString)
524 );
525 }
526
527 if (assertionRequirements.some(function (ref) {
528 var maximum = ref.maximum;
529
530 return maximum !== 1;
531 })) {
532 throw new SyntaxError(
533 ("<assertion+> and <assertion*> are not allowed: " + assertionString)
534 );
535 }
536 return this.expandTypeAlternations(assertion);
537};
538
539var placeholderSplitRegexp = /(\{(?:\d+)\})/g;
540var placeholderRegexp = /\{(\d+)\}/;
541expectPrototype._fail = function(arg) {
542 var arguments$1 = arguments;
543
544 if (arg instanceof UnexpectedError) {
545 arg._hasSerializedErrorMessage = false;
546 throw arg;
547 }
548
549 if (utils.isError(arg)) {
550 throw arg;
551 }
552
553 var error = new UnexpectedError(this);
554 if (typeof arg === 'function') {
555 error.errorMode = 'bubble';
556 error.output = arg;
557 } else if (arg && typeof arg === 'object') {
558 if (typeof arg.message !== 'undefined') {
559 error.errorMode = 'bubble';
560 }
561 error.output = function (output) {
562 if (typeof arg.message !== 'undefined') {
563 if (arg.message.isMagicPen) {
564 output.append(arg.message);
565 } else if (typeof arg.message === 'function') {
566 arg.message.call(output, output);
567 } else {
568 output.text(String(arg.message));
569 }
570 } else {
571 output.error('Explicit failure');
572 }
573 };
574 Object.keys(arg).forEach(function(key) {
575 var this$1 = this;
576
577 var value = arg[key];
578 if (key === 'diff') {
579 if (typeof value === 'function' && this.parent) {
580 error.createDiff = function (output, diff, inspect, equal) {
581 var childOutput = this$1.createOutput(output.format);
582 childOutput.inline = output.inline;
583 childOutput.output = output.output;
584 return value(
585 childOutput,
586 function (actual, expected) {
587 return this$1.diff(actual, expected, childOutput.clone());
588 },
589 function (v, depth) { return childOutput
590 .clone()
591 .appendInspected(v, (depth || defaultDepth) - 1); },
592 function (actual, expected) { return this$1.equal(actual, expected); }
593 );
594 };
595 } else {
596 error.createDiff = value;
597 }
598 } else if (key !== 'message') {
599 error[key] = value;
600 }
601 }, this);
602 } else {
603 var placeholderArgs;
604 if (arguments.length > 0) {
605 placeholderArgs = new Array(arguments.length - 1);
606 for (var i = 1; i < arguments.length; i += 1) {
607 placeholderArgs[i - 1] = arguments$1[i];
608 }
609 }
610 error.errorMode = 'bubble';
611 error.output = function (output) {
612 var message = arg ? String(arg) : 'Explicit failure';
613 var tokens = message.split(placeholderSplitRegexp);
614 tokens.forEach(function (token) {
615 var match = placeholderRegexp.exec(token);
616 if (match) {
617 var index = match[1];
618 if (index in placeholderArgs) {
619 var placeholderArg = placeholderArgs[index];
620 if (placeholderArg && placeholderArg.isMagicPen) {
621 output.append(placeholderArg);
622 } else {
623 output.appendInspected(placeholderArg);
624 }
625 } else {
626 output.text(match[0]);
627 }
628 } else {
629 output.error(token);
630 }
631 });
632 };
633 }
634
635 throw error;
636};
637
638function compareSpecificities(a, b) {
639 for (var i = 0; i < Math.min(a.length, b.length); i += 1) {
640 var c = b[i] - a[i];
641 if (c !== 0) {
642 return c;
643 }
644 }
645 return b.length - a.length;
646}
647
648function calculateAssertionSpecificity(ref) {
649 var subject = ref.subject;
650 var args = ref.args;
651
652 return [subject.type.level].concat(
653 args.map(function (ref) {
654 var minimum = ref.minimum;
655 var maximum = ref.maximum;
656 var type = ref.type;
657
658 var bonus = minimum === 1 && maximum === 1 ? 0.5 : 0;
659 return bonus + type.level;
660 })
661 );
662}
663
664expectPrototype.addAssertion = function(
665 patternOrPatterns,
666 handler,
667 childExpect
668) {
669 var this$1 = this;
670
671 if (this._frozen) {
672 throw new Error(
673 'Cannot add an assertion to a frozen instance, please run .clone() first'
674 );
675 }
676 var maxArguments;
677 if (typeof childExpect === 'function') {
678 maxArguments = 3;
679 } else {
680 maxArguments = 2;
681 }
682 if (
683 arguments.length > maxArguments ||
684 typeof handler !== 'function' ||
685 (typeof patternOrPatterns !== 'string' && !Array.isArray(patternOrPatterns))
686 ) {
687 var errorMessage =
688 'Syntax: expect.addAssertion(<string|array[string]>, function (expect, subject, ...) { ... });';
689 if (
690 (typeof handler === 'string' || Array.isArray(handler)) &&
691 typeof arguments[2] === 'function'
692 ) {
693 errorMessage +=
694 '\nAs of Unexpected 10, the syntax for adding assertions that apply only to specific\n' +
695 'types has changed. See http://unexpected.js.org/api/addAssertion/';
696 }
697 throw new Error(errorMessage);
698 }
699 var patterns = Array.isArray(patternOrPatterns)
700 ? patternOrPatterns
701 : [patternOrPatterns];
702 patterns.forEach(function (pattern) {
703 if (typeof pattern !== 'string' || pattern === '') {
704 throw new Error('Assertion patterns must be a non-empty string');
705 } else {
706 if (pattern !== pattern.trim()) {
707 throw new Error(
708 ("Assertion patterns can't start or end with whitespace:\n\n " + (JSON.stringify(
709 pattern
710 )))
711 );
712 }
713 }
714 });
715
716 var assertions = this.assertions;
717
718 var defaultValueByFlag = {};
719 var assertionHandlers = [];
720 var maxNumberOfArgs = 0;
721 patterns.forEach(function (pattern) {
722 var assertionDeclarations = this$1.parseAssertion(pattern);
723 assertionDeclarations.forEach(function (ref) {
724 var assertion = ref.assertion;
725 var args = ref.args;
726 var subject = ref.subject;
727
728 ensureValidUseOfParenthesesOrBrackets(assertion);
729 var expandedAssertions = expandAssertion(assertion);
730 expandedAssertions.forEach(function (ref) {
731 var flags = ref.flags;
732 var alternations = ref.alternations;
733 var text = ref.text;
734
735 Object.keys(flags).forEach(function (flag) {
736 defaultValueByFlag[flag] = false;
737 });
738 maxNumberOfArgs = Math.max(
739 maxNumberOfArgs,
740 args.reduce(
741 function (previous, ref) {
742 var maximum = ref.maximum;
743
744 return previous + (maximum === null ? Infinity : maximum);
745 },
746 0
747 )
748 );
749 assertionHandlers.push({
750 handler: handler,
751 alternations: alternations,
752 flags: flags,
753 subject: subject,
754 args: args,
755 testDescriptionString: text,
756 declaration: pattern,
757 expect: childExpect
758 });
759 });
760 });
761 });
762 if (handler.length - 2 > maxNumberOfArgs) {
763 throw new Error(
764 ("The provided assertion handler takes " + (handler.length -
765 2) + " parameters, but the type signature specifies a maximum of " + maxNumberOfArgs + ":\n\n " + (JSON.stringify(
766 patterns
767 )))
768 );
769 }
770
771 assertionHandlers.forEach(function (handler) {
772 // Make sure that all flags are defined.
773 handler.flags = extend({}, defaultValueByFlag, handler.flags);
774
775 var assertionHandlers = assertions[handler.testDescriptionString];
776 handler.specificity = calculateAssertionSpecificity(handler);
777 if (!assertionHandlers) {
778 assertions[handler.testDescriptionString] = [handler];
779 } else {
780 var i = 0;
781 while (
782 i < assertionHandlers.length &&
783 compareSpecificities(
784 handler.specificity,
785 assertionHandlers[i].specificity
786 ) > 0
787 ) {
788 i += 1;
789 }
790 assertionHandlers.splice(i, 0, handler);
791 }
792 });
793
794 return this; // for chaining
795};
796
797expectPrototype.addType = function(type, childExpect) {
798 var this$1 = this;
799
800 if (this._frozen) {
801 throw new Error(
802 'Cannot add a type to a frozen instance, please run .clone() first'
803 );
804 }
805
806 var baseType;
807 if (
808 typeof type.name !== 'string' ||
809 !/^[a-z_](?:|[a-z0-9_.-]*[_a-z0-9])$/i.test(type.name)
810 ) {
811 throw new Error(
812 'A type must be given a non-empty name and must match ^[a-z_](?:|[a-z0-9_.-]*[_a-z0-9])$'
813 );
814 }
815
816 if (typeof type.identify !== 'function' && type.identify !== false) {
817 throw new Error(
818 ("Type " + (type.name) + " must specify an identify function or be declared abstract by setting identify to false")
819 );
820 }
821
822 if (this.typeByName[type.name]) {
823 throw new Error(("The type with the name " + (type.name) + " already exists"));
824 }
825
826 if (type.base) {
827 baseType = this.getType(type.base);
828
829 if (!baseType) {
830 throw new Error(("Unknown base type: " + (type.base)));
831 }
832 } else {
833 baseType = anyType;
834 }
835
836 var extendedBaseType = Object.create(baseType);
837 extendedBaseType.inspect = function (value, depth, output) {
838 if (!output || !output.isMagicPen) {
839 throw new Error(
840 'You need to pass the output to baseType.inspect() as the third parameter'
841 );
842 }
843
844 return baseType.inspect(value, depth, output, function (value, depth) { return output.clone().appendInspected(value, depth); }
845 );
846 };
847
848 if (nodeJsCustomInspect !== 'inspect') {
849 extendedBaseType[nodeJsCustomInspect] = extendedBaseType.inspect;
850 }
851
852 extendedBaseType.diff = function (actual, expected, output) {
853 if (!output || !output.isMagicPen) {
854 throw new Error(
855 'You need to pass the output to baseType.diff() as the third parameter'
856 );
857 }
858
859 return baseType.diff(
860 actual,
861 expected,
862 output.clone(),
863 function (actual, expected) { return this$1.diff(actual, expected, output.clone()); },
864 function (value, depth) { return output.clone().appendInspected(value, depth); },
865 this$1.equal.bind(this$1)
866 );
867 };
868
869 extendedBaseType.equal = function (actual, expected) { return baseType.equal(actual, expected, this$1.equal.bind(this$1)); };
870
871 var extendedType = extend({}, baseType, type, {
872 baseType: extendedBaseType
873 });
874 var originalInspect = extendedType.inspect;
875
876 // Prevent node.js' util.inspect from complaining about our inspect method:
877 if (nodeJsCustomInspect !== 'inspect') {
878 extendedType[nodeJsCustomInspect] = function() {
879 return ("type: " + (type.name));
880 };
881 }
882
883 extendedType.inspect = function(obj, depth, output, inspect) {
884 if (arguments.length < 2 || (!output || !output.isMagicPen)) {
885 return ("type: " + (type.name));
886 } else if (childExpect) {
887 var childOutput = childExpect.createOutput(output.format);
888 return (
889 originalInspect.call(this, obj, depth, childOutput, inspect) ||
890 childOutput
891 );
892 } else {
893 return originalInspect.call(this, obj, depth, output, inspect) || output;
894 }
895 };
896
897 if (childExpect) {
898 extendedType.childExpect = childExpect;
899 var originalDiff = extendedType.diff;
900 extendedType.diff = function(
901 actual,
902 expected,
903 output,
904 inspect,
905 diff,
906 equal
907 ) {
908 var childOutput = childExpect.createOutput(output.format);
909 // Make sure that already buffered up output is preserved:
910 childOutput.output = output.output;
911 return (
912 originalDiff.call(
913 this,
914 actual,
915 expected,
916 childOutput,
917 inspect,
918 diff,
919 equal
920 ) || output
921 );
922 };
923 }
924
925 if (extendedType.identify === false) {
926 this.types.push(extendedType);
927 } else {
928 this.types.unshift(extendedType);
929 }
930
931 extendedType.level = baseType.level + 1;
932 extendedType.typeEqualityCache = {};
933 this.typeByName[extendedType.name] = extendedType;
934
935 return this;
936};
937
938expectPrototype.getType = function(typeName) {
939 return (
940 this.typeByName[typeName] || (this.parent && this.parent.getType(typeName))
941 );
942};
943
944expectPrototype.findTypeOf = function(obj) {
945 return (
946 utils.findFirst(
947 this.types || [],
948 function (type) { return type.identify && type.identify(obj); }
949 ) ||
950 (this.parent && this.parent.findTypeOf(obj))
951 );
952};
953
954expectPrototype.findTypeOfWithParentType = function(obj, requiredParentType) {
955 return (
956 utils.findFirst(
957 this.types || [],
958 function (type) { return type.identify &&
959 type.identify(obj) &&
960 (!requiredParentType || type.is(requiredParentType)); }
961 ) ||
962 (this.parent &&
963 this.parent.findTypeOfWithParentType(obj, requiredParentType))
964 );
965};
966
967expectPrototype.findCommonType = function(a, b) {
968 var aAncestorIndex = {};
969 var current = this.findTypeOf(a);
970 while (current) {
971 aAncestorIndex[current.name] = current;
972 current = current.baseType;
973 }
974 current = this.findTypeOf(b);
975 while (current) {
976 if (aAncestorIndex[current.name]) {
977 return current;
978 }
979 current = current.baseType;
980 }
981};
982
983expectPrototype.addStyle = function() {
984 var ref;
985
986 var args = [], len = arguments.length;
987 while ( len-- ) args[ len ] = arguments[ len ];
988 if (this._frozen) {
989 throw new Error(
990 'Cannot add a style to a frozen instance, please run .clone() first'
991 );
992 }
993 (ref = this.output).addStyle.apply(ref, args);
994 return this;
995};
996
997expectPrototype.installTheme = function() {
998 var ref;
999
1000 var args = [], len = arguments.length;
1001 while ( len-- ) args[ len ] = arguments[ len ];
1002 if (this._frozen) {
1003 throw new Error(
1004 'Cannot install a theme into a frozen instance, please run .clone() first'
1005 );
1006 }
1007 (ref = this.output).installTheme.apply(ref, args);
1008 return this;
1009};
1010
1011function getPluginName(plugin) {
1012 if (typeof plugin === 'function') {
1013 return utils.getFunctionName(plugin);
1014 } else {
1015 return plugin.name;
1016 }
1017}
1018
1019expectPrototype.use = function(plugin) {
1020 this._assertTopLevelExpect();
1021 if (this._frozen) {
1022 throw new Error(
1023 'Cannot install a plugin into a frozen instance, please run .clone() first'
1024 );
1025 }
1026 if (
1027 (typeof plugin !== 'function' &&
1028 (typeof plugin !== 'object' ||
1029 typeof plugin.installInto !== 'function')) ||
1030 (typeof plugin.name !== 'undefined' && typeof plugin.name !== 'string')
1031 ) {
1032 throw new Error(
1033 'Plugins must be functions or adhere to the following interface\n' +
1034 '{\n' +
1035 ' name: <an optional plugin name>,\n' +
1036 ' version: <an optional semver version string>,\n' +
1037 ' installInto: <a function that will update the given expect instance>\n' +
1038 '}'
1039 );
1040 }
1041
1042 var pluginName = getPluginName(plugin);
1043
1044 var existingPlugin = utils.findFirst(
1045 this.installedPlugins,
1046 function (installedPlugin) {
1047 if (installedPlugin === plugin) {
1048 return true;
1049 } else {
1050 return pluginName && pluginName === getPluginName(installedPlugin);
1051 }
1052 }
1053 );
1054
1055 if (existingPlugin) {
1056 if (
1057 existingPlugin === plugin ||
1058 (typeof plugin.version !== 'undefined' &&
1059 plugin.version === existingPlugin.version)
1060 ) {
1061 // No-op
1062 return this;
1063 } else {
1064 throw new Error(
1065 ("Another instance of the plugin '" + pluginName + "' is already installed" + (typeof existingPlugin.version !== 'undefined'
1066 ? (" (version " + (existingPlugin.version) + (typeof plugin.version !== 'undefined'
1067 ? (", trying to install " + (plugin.version))
1068 : '') + ")")
1069 : '') + ". Please check your node_modules folder for unmet peerDependencies.")
1070 );
1071 }
1072 }
1073
1074 if (pluginName === 'unexpected-promise') {
1075 throw new Error(
1076 'The unexpected-promise plugin was pulled into Unexpected as of 8.5.0. This means that the plugin is no longer supported.'
1077 );
1078 }
1079
1080 this.installedPlugins.push(plugin);
1081 if (typeof plugin === 'function') {
1082 plugin(this);
1083 } else {
1084 plugin.installInto(this);
1085 }
1086
1087 return this; // for chaining
1088};
1089
1090expectPrototype.withError = function (body, handler) { return oathbreaker(
1091 makePromise(body).caught(function (e) {
1092 throwIfNonUnexpectedError(e);
1093 return handler(e);
1094 })
1095 ); };
1096
1097expectPrototype.installPlugin = expectPrototype.use; // Legacy alias
1098
1099function calculateLimits(items) {
1100 return items.reduce(
1101 function (result, ref) {
1102 var minimum = ref.minimum;
1103 var maximum = ref.maximum;
1104
1105 result.minimum += minimum;
1106 result.maximum += maximum;
1107 return result;
1108 },
1109 { minimum: 0, maximum: 0 }
1110 );
1111}
1112
1113expectPrototype.throwAssertionNotFoundError = function(
1114 subject,
1115 testDescriptionString,
1116 args
1117) {
1118 var this$1 = this;
1119
1120 var candidateHandlers = this.assertions[testDescriptionString];
1121
1122 var instance = this;
1123 while (instance && !candidateHandlers) {
1124 candidateHandlers = instance.assertions[testDescriptionString];
1125 instance = instance.parent;
1126 }
1127
1128 if (candidateHandlers) {
1129 this.fail({
1130 message: function (output) {
1131 var subjectOutput = function (output) {
1132 output.appendInspected(subject);
1133 };
1134 var argsOutput = function (output) {
1135 output.appendItems(args, ', ');
1136 };
1137 output
1138 .append(
1139 createStandardErrorMessage(
1140 output.clone(),
1141 subjectOutput,
1142 testDescriptionString,
1143 argsOutput
1144 )
1145 )
1146 .nl()
1147 .indentLines();
1148 output
1149 .i()
1150 .error('The assertion does not have a matching signature for:')
1151 .nl()
1152 .indentLines()
1153 .i()
1154 .text('<')
1155 .text(this$1.findTypeOf(subject).name)
1156 .text('>')
1157 .sp()
1158 .text(testDescriptionString);
1159
1160 args.forEach(function (arg, i) {
1161 output
1162 .sp()
1163 .text('<')
1164 .text(this$1.findTypeOf(arg).name)
1165 .text('>');
1166 });
1167
1168 output
1169 .outdentLines()
1170 .nl()
1171 .i()
1172 .text('did you mean:')
1173 .indentLines()
1174 .nl();
1175 var assertionDeclarations = Object.keys(
1176 candidateHandlers.reduce(function (result, ref) {
1177 var declaration = ref.declaration;
1178
1179 result[declaration] = true;
1180 return result;
1181 }, {})
1182 ).sort();
1183 assertionDeclarations.forEach(function (declaration, i) {
1184 output
1185 .nl(i > 0 ? 1 : 0)
1186 .i()
1187 .text(declaration);
1188 });
1189 output.outdentLines();
1190 }
1191 });
1192 }
1193
1194 var assertionsWithScore = [];
1195 var assertionStrings = [];
1196 instance = this;
1197 while (instance) {
1198 assertionStrings.push.apply(assertionStrings, Object.keys(instance.assertions));
1199 instance = instance.parent;
1200 }
1201
1202 var compareAssertions = function (a, b) {
1203 var aAssertion = this$1.lookupAssertionRule(subject, a, args);
1204 var bAssertion = this$1.lookupAssertionRule(subject, b, args);
1205 if (!aAssertion && !bAssertion) {
1206 return 0;
1207 }
1208 if (aAssertion && !bAssertion) {
1209 return -1;
1210 }
1211 if (!aAssertion && bAssertion) {
1212 return 1;
1213 }
1214
1215 return compareSpecificities(aAssertion.specificity, bAssertion.specificity);
1216 };
1217
1218 assertionStrings.forEach(function (assertionString) {
1219 var score = leven(testDescriptionString, assertionString);
1220
1221 assertionsWithScore.push({
1222 assertion: assertionString,
1223 score: score
1224 });
1225 });
1226
1227 var bestMatch = assertionsWithScore
1228 .sort(function (a, b) {
1229 var c = a.score - b.score;
1230 if (c !== 0) {
1231 return c;
1232 }
1233
1234 if (a.assertion < b.assertion) {
1235 return -1;
1236 } else {
1237 return 1;
1238 }
1239 })
1240 .slice(0, 10)
1241 .filter(function (ref, i, arr) {
1242 var score = ref.score;
1243
1244 return Math.abs(score - arr[0].score) <= 2;
1245 })
1246 .sort(function (a, b) {
1247 var c = compareAssertions(a.assertion, b.assertion);
1248 if (c !== 0) {
1249 return c;
1250 }
1251
1252 return a.score - b.score;
1253 })[0];
1254
1255 this.fail({
1256 errorMode: 'bubbleThrough',
1257 message: function message(output) {
1258 output
1259 .error("Unknown assertion '")
1260 .jsString(testDescriptionString)
1261 .error("', did you mean: '")
1262 .jsString(bestMatch.assertion)
1263 .error("'");
1264 }
1265 });
1266};
1267
1268expectPrototype.lookupAssertionRule = function(
1269 subject,
1270 testDescriptionString,
1271 args,
1272 requireAssertionSuffix
1273) {
1274 var this$1 = this;
1275
1276 if (typeof testDescriptionString !== 'string') {
1277 throw new Error(
1278 'The expect function requires the second parameter to be a string or an expect.it.'
1279 );
1280 }
1281 var handlers;
1282 var instance = this;
1283 while (instance) {
1284 var instanceHandlers = instance.assertions[testDescriptionString];
1285 if (instanceHandlers) {
1286 handlers = handlers
1287 ? handlers.concat(instanceHandlers)
1288 : instanceHandlers;
1289 }
1290 instance = instance.parent;
1291 }
1292 if (!handlers) {
1293 return null;
1294 }
1295 var cachedTypes = {};
1296
1297 var findTypeOf = function (value, key) {
1298 var type = cachedTypes[key];
1299 if (!type) {
1300 type = this$1.findTypeOf(value);
1301 cachedTypes[key] = type;
1302 }
1303 return type;
1304 };
1305
1306 var matches = function (value, assertionType, key, relaxed) {
1307 if (assertionType.is('assertion') && typeof value === 'string') {
1308 return true;
1309 }
1310
1311 if (relaxed) {
1312 if (assertionType.identify === false) {
1313 return this$1.types.some(
1314 function (type) { return type.identify && type.is(assertionType) && type.identify(value); }
1315 );
1316 }
1317 return assertionType.identify(value);
1318 } else {
1319 return findTypeOf(value, key).is(assertionType);
1320 }
1321 };
1322
1323 function matchesHandler(handler, relaxed) {
1324 if (!matches(subject, handler.subject.type, 'subject', relaxed)) {
1325 return false;
1326 }
1327 if (requireAssertionSuffix && !handler.args.some(isAssertionArg)) {
1328 return false;
1329 }
1330
1331 var requireArgumentsLength = calculateLimits(handler.args);
1332
1333 if (
1334 args.length < requireArgumentsLength.minimum ||
1335 requireArgumentsLength.maximum < args.length
1336 ) {
1337 return false;
1338 } else if (args.length === 0 && requireArgumentsLength.maximum === 0) {
1339 return true;
1340 }
1341
1342 var lastRequirement = handler.args[handler.args.length - 1];
1343 return args.every(function (arg, i) {
1344 if (i < handler.args.length - 1) {
1345 return matches(arg, handler.args[i].type, i, relaxed);
1346 } else {
1347 return matches(arg, lastRequirement.type, i, relaxed);
1348 }
1349 });
1350 }
1351
1352 var j, handler;
1353 for (j = 0; j < handlers.length; j += 1) {
1354 handler = handlers[j];
1355 if (matchesHandler(handler)) {
1356 return handler;
1357 }
1358 }
1359 for (j = 0; j < handlers.length; j += 1) {
1360 handler = handlers[j];
1361 if (matchesHandler(handler, true)) {
1362 return handler;
1363 }
1364 }
1365
1366 return null;
1367};
1368
1369expectPrototype._assertTopLevelExpect = function() {
1370 if (this !== this._topLevelExpect) {
1371 throw new Error('This method only works on the top level expect function');
1372 }
1373};
1374
1375expectPrototype._assertWrappedExpect = function() {
1376 if (this === this._topLevelExpect) {
1377 throw new Error(
1378 'This method only works on the expect function handed to an assertion'
1379 );
1380 }
1381};
1382
1383expectPrototype.setErrorMessage = function(err) {
1384 err.serializeMessage(this.outputFormat());
1385};
1386
1387expectPrototype._createWrappedExpect = function(
1388 assertionRule,
1389 subject,
1390 args,
1391 testDescriptionString,
1392 context
1393) {
1394 var flags = extend({}, assertionRule.flags);
1395 var parentExpect = this;
1396
1397 function wrappedExpect(subject, testDescriptionString) {
1398 var arguments$1 = arguments;
1399
1400 if (arguments.length === 0) {
1401 throw new Error('The expect function requires at least one parameter.');
1402 } else if (arguments.length === 1) {
1403 return addAdditionalPromiseMethods(
1404 makePromise.resolve(subject),
1405 wrappedExpect,
1406 subject
1407 );
1408 } else if (typeof testDescriptionString === 'function') {
1409 wrappedExpect.errorMode = 'nested';
1410 return wrappedExpect.withError(
1411 function () { return testDescriptionString(subject); },
1412 function (err) {
1413 wrappedExpect.fail(err);
1414 }
1415 );
1416 }
1417 testDescriptionString = utils.forwardFlags(testDescriptionString, flags);
1418
1419 var args = new Array(arguments.length - 2);
1420 for (var i = 2; i < arguments.length; i += 1) {
1421 args[i - 2] = arguments$1[i];
1422 }
1423 return wrappedExpect._callInNestedContext(function () { return parentExpect._executeExpect(
1424 context.child(),
1425 subject,
1426 testDescriptionString,
1427 args
1428 ); }
1429 );
1430 }
1431
1432 utils.setPrototypeOfOrExtend(wrappedExpect, this);
1433
1434 wrappedExpect.context = context;
1435 wrappedExpect.execute = wrappedExpect;
1436 wrappedExpect.alternations = assertionRule.alternations;
1437 wrappedExpect.flags = flags;
1438 wrappedExpect.subject = subject;
1439 wrappedExpect.testDescription = testDescriptionString;
1440 wrappedExpect.args = args;
1441 wrappedExpect.assertionRule = assertionRule;
1442
1443 wrappedExpect.subjectOutput = function (output) {
1444 output.appendInspected(subject);
1445 };
1446 wrappedExpect.argsOutput = args.map(function (arg, i) {
1447 var argRule = wrappedExpect.assertionRule.args[i];
1448 if (
1449 typeof arg === 'string' &&
1450 ((argRule && argRule.type.is('assertion')) ||
1451 wrappedExpect._getAssertionIndices().indexOf(i) >= 0)
1452 ) {
1453 return new AssertionString(arg);
1454 }
1455
1456 return function (output) {
1457 output.appendInspected(arg);
1458 };
1459 });
1460
1461 return wrappedExpect;
1462};
1463
1464expectPrototype._executeExpect = function(
1465 context,
1466 subject,
1467 testDescriptionString,
1468 args
1469) {
1470 var assertionRule = this.lookupAssertionRule(
1471 subject,
1472 testDescriptionString,
1473 args
1474 );
1475
1476 if (!assertionRule) {
1477 var tokens = testDescriptionString.split(' ');
1478 // eslint-disable-next-line no-labels
1479 OUTER: for (var n = tokens.length - 1; n > 0; n -= 1) {
1480 var prefix = tokens.slice(0, n).join(' ');
1481 var remainingTokens = tokens.slice(n);
1482 var argsWithAssertionPrepended = [remainingTokens.join(' ')].concat(
1483 args
1484 );
1485 assertionRule = this.lookupAssertionRule(
1486 subject,
1487 prefix,
1488 argsWithAssertionPrepended,
1489 true
1490 );
1491 if (assertionRule) {
1492 // Found the longest prefix of the string that yielded a suitable assertion for the given subject and args
1493 // To avoid bogus error messages when shifting later (#394) we require some prefix of the remaining tokens
1494 // to be a valid assertion name:
1495 for (var i = 1; i < remainingTokens.length; i += 1) {
1496 if (
1497 this.assertions.hasOwnProperty(
1498 remainingTokens.slice(0, i + 1).join(' ')
1499 )
1500 ) {
1501 testDescriptionString = prefix;
1502 args = argsWithAssertionPrepended;
1503 // eslint-disable-next-line no-labels
1504 break OUTER;
1505 }
1506 }
1507 }
1508 }
1509 if (!assertionRule) {
1510 this.throwAssertionNotFoundError(subject, testDescriptionString, args);
1511 }
1512 }
1513
1514 if (assertionRule.expect && assertionRule.expect !== this) {
1515 return assertionRule.expect.apply(assertionRule, [ subject, testDescriptionString ].concat( args ));
1516 }
1517
1518 var wrappedExpect = this._createWrappedExpect(
1519 assertionRule,
1520 subject,
1521 args,
1522 testDescriptionString,
1523 context
1524 );
1525
1526 return oathbreaker(assertionRule.handler.apply(assertionRule, [ wrappedExpect, subject ].concat( args )));
1527};
1528
1529expectPrototype._expect = function expect(context, args) {
1530 var this$1 = this;
1531
1532 var subject = args[0];
1533 var testDescriptionString = args[1];
1534
1535 if (args.length < 2) {
1536 throw new Error('The expect function requires at least two parameters.');
1537 } else if (typeof testDescriptionString === 'function') {
1538 return this.withError(
1539 function () { return testDescriptionString(subject); },
1540 function (err) {
1541 this$1.fail(err);
1542 }
1543 );
1544 }
1545
1546 try {
1547 var result = this._executeExpect(
1548 context,
1549 subject,
1550 testDescriptionString,
1551 Array.prototype.slice.call(args, 2)
1552 );
1553 if (utils.isPromise(result)) {
1554 result = wrapPromiseIfNecessary(result);
1555 if (result.isPending()) {
1556 result = result.then(undefined, function (e) {
1557 if (e && e._isUnexpected && context.level === 0) {
1558 this$1.setErrorMessage(e);
1559 }
1560 throw e;
1561 });
1562 this.notifyPendingPromise(result);
1563 }
1564 } else {
1565 result = makePromise.resolve(result);
1566 }
1567 return addAdditionalPromiseMethods(result, this, subject);
1568 } catch (e) {
1569 if (e && e._isUnexpected) {
1570 var newError = e;
1571 if (typeof mochaPhantomJS !== 'undefined') {
1572 newError = e.clone();
1573 }
1574 if (context.level === 0) {
1575 this.setErrorMessage(newError);
1576 }
1577 throw newError;
1578 }
1579 throw e;
1580 }
1581};
1582
1583expectPrototype.diff = function(
1584 a,
1585 b,
1586 output,
1587 recursions,
1588 seen
1589) {
1590 var this$1 = this;
1591 if ( output === void 0 ) output = this.createOutput();
1592
1593 var maxRecursions = 100;
1594 recursions = typeof recursions === 'number' ? recursions : maxRecursions;
1595 if (recursions <= 0) {
1596 // detect recursive loops in the structure
1597 seen = seen || [];
1598 if (seen.indexOf(a) !== -1) {
1599 throw new Error('Cannot compare circular structures');
1600 }
1601 seen.push(a);
1602 }
1603
1604 return this.findCommonType(a, b).diff(
1605 a,
1606 b,
1607 output,
1608 function (actual, expected) { return this$1.diff(actual, expected, output.clone(), recursions - 1, seen); },
1609 function (v, depth) { return output.clone().appendInspected(v, depth); },
1610 function (actual, expected) { return this$1.equal(actual, expected); }
1611 );
1612};
1613
1614expectPrototype.toString = function() {
1615 var assertions = this.assertions;
1616
1617 var seen = {};
1618 var declarations = [];
1619 var pen = magicpen();
1620 Object.keys(assertions)
1621 .sort()
1622 .forEach(function (key) {
1623 assertions[key].forEach(function (ref) {
1624 var declaration = ref.declaration;
1625
1626 if (!seen[declaration]) {
1627 declarations.push(declaration);
1628 seen[declaration] = true;
1629 }
1630 });
1631 });
1632
1633 declarations.forEach(function (declaration) {
1634 pen.text(declaration).nl();
1635 });
1636 return pen.toString();
1637};
1638
1639expectPrototype.clone = function() {
1640 this._assertTopLevelExpect();
1641 var clonedAssertions = {};
1642 Object.keys(this.assertions).forEach(function(assertion) {
1643 clonedAssertions[assertion] = [].concat(this.assertions[assertion]);
1644 }, this);
1645 var expect = createTopLevelExpect({
1646 assertions: clonedAssertions,
1647 types: [].concat(this.types),
1648 typeByName: extend({}, this.typeByName),
1649 output: this.output.clone(),
1650 format: this.outputFormat(),
1651 installedPlugins: [].concat(this.installedPlugins)
1652 });
1653 // Install the hooks:
1654 expect._expect = this._expect;
1655 // Make sure that changes to the parent's preferredWidth doesn't propagate:
1656 expect.output.preferredWidth = this.output.preferredWidth;
1657 return expect;
1658};
1659
1660expectPrototype.child = function() {
1661 this._assertTopLevelExpect();
1662 var childExpect = createTopLevelExpect({
1663 assertions: {},
1664 types: [],
1665 typeByName: {},
1666 output: this.output.clone(),
1667 format: this.outputFormat(),
1668 installedPlugins: []
1669 });
1670 var parent = (childExpect.parent = this);
1671
1672 childExpect.exportAssertion = function(testDescription, handler) {
1673 parent.addAssertion(testDescription, handler, childExpect);
1674 return this;
1675 };
1676 childExpect.exportType = function(type) {
1677 if (childExpect.getType(type.name) !== type) {
1678 childExpect.addType(type);
1679 }
1680
1681 parent.addType(type, childExpect);
1682 return this;
1683 };
1684 childExpect.exportStyle = function(name, handler) {
1685 parent.addStyle(name, function() {
1686 var args = [], len = arguments.length;
1687 while ( len-- ) args[ len ] = arguments[ len ];
1688
1689 var childOutput = childExpect.createOutput(this.format);
1690 this.append(handler.call.apply(handler, [ childOutput ].concat( args )) || childOutput);
1691 });
1692 return this;
1693 };
1694 return childExpect;
1695};
1696
1697expectPrototype.freeze = function() {
1698 this._assertTopLevelExpect();
1699 this._frozen = true;
1700 return this;
1701};
1702
1703expectPrototype.outputFormat = function(format) {
1704 this._assertTopLevelExpect();
1705 if (typeof format === 'undefined') {
1706 return this._outputFormat;
1707 } else {
1708 this._outputFormat = format;
1709 return this;
1710 }
1711};
1712
1713expectPrototype.createOutput = function(format) {
1714 var that = this;
1715 var output = this.output.clone(format || 'text');
1716 output.addStyle('appendInspected', function(value, depth) {
1717 this.append(that.inspect(value, depth, this.clone()));
1718 });
1719 return output;
1720};
1721
1722expectPrototype.hook = function(fn) {
1723 this._assertTopLevelExpect();
1724 if (this._frozen) {
1725 throw new Error(
1726 'Cannot install a hook into a frozen instance, please run .clone() first'
1727 );
1728 }
1729 this._expect = fn(this._expect.bind(this));
1730};
1731
1732// This is not super elegant, but wrappedExpect.fail was different:
1733expectPrototype.fail = function() {
1734 var this$1 = this;
1735 var ref;
1736
1737 var args = [], len = arguments.length;
1738 while ( len-- ) args[ len ] = arguments[ len ];
1739 if (this === this._topLevelExpect) {
1740 try {
1741 (ref = this)._fail.apply(ref, args);
1742 } catch (e) {
1743 if (e && e._isUnexpected) {
1744 this.setErrorMessage(e);
1745 }
1746 throw e;
1747 }
1748 } else {
1749 this._callInNestedContext(function () {
1750 var ref;
1751
1752 (ref = this$1.context.expect).fail.apply(ref, args);
1753 });
1754 }
1755};
1756
1757function lookupAssertionsInParentChain(assertionString, expect) {
1758 var assertions = [];
1759 for (var instance = expect; instance; instance = instance.parent) {
1760 if (instance.assertions[assertionString]) {
1761 assertions.push.apply(assertions, instance.assertions[assertionString]);
1762 }
1763 }
1764 return assertions;
1765}
1766
1767function findSuffixAssertions(assertionString, expect) {
1768 if (typeof assertionString !== 'string') {
1769 return null;
1770 }
1771 var straightforwardAssertions = lookupAssertionsInParentChain(
1772 assertionString,
1773 expect
1774 );
1775 if (straightforwardAssertions.length > 0) {
1776 return straightforwardAssertions;
1777 }
1778 var tokens = assertionString.split(' ');
1779 for (var n = tokens.length - 1; n > 0; n -= 1) {
1780 var suffix = tokens.slice(n).join(' ');
1781 var suffixAssertions = lookupAssertionsInParentChain(suffix, expect);
1782 if (
1783 findSuffixAssertions(tokens.slice(0, n).join(' '), expect) &&
1784 suffixAssertions.length > 0
1785 ) {
1786 return suffixAssertions;
1787 }
1788 }
1789 return null;
1790}
1791
1792expectPrototype.standardErrorMessage = function(output, options) {
1793 var this$1 = this;
1794
1795 this._assertWrappedExpect();
1796 options = typeof options === 'object' ? options : {};
1797
1798 if ('omitSubject' in output) {
1799 options.subject = this.subject;
1800 }
1801
1802 if (options && options.compact) {
1803 options.compactSubject = function (output) {
1804 output.jsFunctionName(this$1.subjectType.name);
1805 };
1806 }
1807 return createStandardErrorMessage(
1808 output,
1809 this.subjectOutput,
1810 this.testDescription,
1811 this.argsOutput,
1812 options
1813 );
1814};
1815
1816expectPrototype._callInNestedContext = function(callback) {
1817 var this$1 = this;
1818
1819 this._assertWrappedExpect();
1820 try {
1821 var result = oathbreaker(callback());
1822 if (utils.isPromise(result)) {
1823 result = wrapPromiseIfNecessary(result);
1824 if (result.isPending()) {
1825 result = result.then(undefined, function (e) {
1826 if (e && e._isUnexpected) {
1827 var wrappedError = new UnexpectedError(this$1, e);
1828 wrappedError.originalError = e.originalError;
1829 throw wrappedError;
1830 }
1831 throw e;
1832 });
1833 }
1834 } else {
1835 result = makePromise.resolve(result);
1836 }
1837 return addAdditionalPromiseMethods(result, this.execute, this.subject);
1838 } catch (e) {
1839 if (e && e._isUnexpected) {
1840 var wrappedError = new UnexpectedError(this, e);
1841 wrappedError.originalError = e.originalError;
1842 throw wrappedError;
1843 }
1844 throw e;
1845 }
1846};
1847
1848expectPrototype.shift = function(subject, assertionIndex) {
1849 var this$1 = this;
1850 var ref;
1851
1852 this._assertWrappedExpect();
1853 if (arguments.length <= 1) {
1854 if (arguments.length === 0) {
1855 subject = this.subject;
1856 }
1857 assertionIndex = -1;
1858 for (var i = 0; i < this.assertionRule.args.length; i += 1) {
1859 var type = this.assertionRule.args[i].type;
1860 if (type.is('assertion') || type.is('expect.it')) {
1861 assertionIndex = i;
1862 break;
1863 }
1864 }
1865 } else if (arguments.length === 3) {
1866 // The 3-argument syntax for wrappedExpect.shift is deprecated, please omit the first (expect) arg
1867 subject = arguments[1];
1868 assertionIndex = arguments[2];
1869 }
1870
1871 if (assertionIndex !== -1) {
1872 var args = this.args.slice(0, assertionIndex);
1873 var rest = this.args.slice(assertionIndex);
1874 var nextArgumentType = this.findTypeOf(rest[0]);
1875 if (arguments.length > 1) {
1876 // Legacy
1877 this.argsOutput = function (output) {
1878 args.forEach(function (arg, index) {
1879 if (index > 0) {
1880 output.text(', ');
1881 }
1882 output.appendInspected(arg);
1883 });
1884
1885 if (args.length > 0) {
1886 output.sp();
1887 }
1888 if (nextArgumentType.is('string')) {
1889 output.error(rest[0]);
1890 } else if (rest.length > 0) {
1891 output.appendInspected(rest[0]);
1892 }
1893 if (rest.length > 1) {
1894 output.sp();
1895 }
1896 rest.slice(1).forEach(function (arg, index) {
1897 if (index > 0) {
1898 output.text(', ');
1899 }
1900 output.appendInspected(arg);
1901 });
1902 };
1903 }
1904 if (nextArgumentType.is('expect.it')) {
1905 return this.withError(
1906 function () { return rest[0](subject); },
1907 function (err) {
1908 this$1.fail(err);
1909 }
1910 );
1911 } else if (nextArgumentType.is('string')) {
1912 return (ref = this).execute.apply(ref, [ subject ].concat( rest ));
1913 } else {
1914 return subject;
1915 }
1916 } else {
1917 // No assertion to delegate to. Provide the new subject as the fulfillment value:
1918 return subject;
1919 }
1920};
1921
1922expectPrototype._getSubjectType = function() {
1923 this._assertWrappedExpect();
1924 return this.findTypeOfWithParentType(
1925 this.subject,
1926 this.assertionRule.subject.type
1927 );
1928};
1929
1930expectPrototype._getArgTypes = function(index) {
1931 var this$1 = this;
1932
1933 this._assertWrappedExpect();
1934 var lastIndex = this.assertionRule.args.length - 1;
1935 return this.args.map(function (arg, index) {
1936 return this$1.findTypeOfWithParentType(
1937 arg,
1938 this$1.assertionRule.args[Math.min(index, lastIndex)].type
1939 );
1940 });
1941};
1942
1943expectPrototype._getAssertionIndices = function() {
1944 this._assertWrappedExpect();
1945 if (!this._assertionIndices) {
1946 var assertionIndices = [];
1947 var args = this.args;
1948 var currentAssertionRule = this.assertionRule;
1949 var offset = 0;
1950 // eslint-disable-next-line no-labels
1951 OUTER: while (true) {
1952 if (
1953 currentAssertionRule.args.length > 1 &&
1954 isAssertionArg(
1955 currentAssertionRule.args[currentAssertionRule.args.length - 2]
1956 )
1957 ) {
1958 assertionIndices.push(offset + currentAssertionRule.args.length - 2);
1959 var suffixAssertions = findSuffixAssertions(
1960 args[offset + currentAssertionRule.args.length - 2],
1961 this
1962 );
1963 if (suffixAssertions) {
1964 for (var i = 0; i < suffixAssertions.length; i += 1) {
1965 if (suffixAssertions[i].args.some(isAssertionArg)) {
1966 offset += currentAssertionRule.args.length - 1;
1967 currentAssertionRule = suffixAssertions[i];
1968 // eslint-disable-next-line no-labels
1969 continue OUTER;
1970 }
1971 }
1972 }
1973 }
1974 // No further assertions found,
1975 break;
1976 }
1977 this._assertionIndices = assertionIndices;
1978 }
1979 return this._assertionIndices;
1980};
1981
1982Object.defineProperty(expectPrototype, 'subjectType', {
1983 enumerable: true,
1984 get: function get() {
1985 this._assertWrappedExpect();
1986 return this._getSubjectType();
1987 }
1988});
1989
1990Object.defineProperty(expectPrototype, 'argTypes', {
1991 enumerable: true,
1992 get: function get() {
1993 this._assertWrappedExpect();
1994 return this._getArgTypes();
1995 }
1996});
1997
1998function createTopLevelExpect(ref) {
1999 if ( ref === void 0 ) ref = {};
2000 var assertions = ref.assertions; if ( assertions === void 0 ) assertions = {};
2001 var typeByName = ref.typeByName; if ( typeByName === void 0 ) typeByName = { any: anyType };
2002 var types = ref.types; if ( types === void 0 ) types = [anyType];
2003 var output = ref.output;
2004 var format = ref.format; if ( format === void 0 ) format = magicpen.defaultFormat;
2005 var installedPlugins = ref.installedPlugins; if ( installedPlugins === void 0 ) installedPlugins = [];
2006
2007 var expect = function() {
2008 var args = [], len = arguments.length;
2009 while ( len-- ) args[ len ] = arguments[ len ];
2010
2011 return expect._expect(new Context(expect), args);
2012 };
2013 utils.setPrototypeOfOrExtend(expect, expectPrototype);
2014
2015 if (!output) {
2016 output = magicpen();
2017 output.inline = false;
2018 output.diff = false;
2019 }
2020 return extend(expect, {
2021 _topLevelExpect: expect,
2022 _outputFormat: format,
2023 assertions: assertions,
2024 typeByName: typeByName,
2025 installedPlugins: installedPlugins,
2026 types: types,
2027 output: output
2028 });
2029}
2030
2031module.exports = createTopLevelExpect;