UNPKG

18 kBJavaScriptView Raw
1var utils = require('./utils');
2var stringDiff = require('diff');
3var specialCharRegExp = require('./specialCharRegExp');
4
5module.exports = function (expect) {
6 expect.installTheme({
7 styles: {
8 jsBoolean: 'jsPrimitive',
9 jsNumber: 'jsPrimitive',
10 error: ['red', 'bold'],
11 success: ['green', 'bold'],
12 diffAddedLine: 'green',
13 diffAddedHighlight: ['bgGreen', 'white'],
14 diffAddedSpecialChar: ['bgGreen', 'cyan', 'bold'],
15 diffRemovedLine: 'red',
16 diffRemovedHighlight: ['bgRed', 'white'],
17 diffRemovedSpecialChar: ['bgRed', 'cyan', 'bold'],
18 partialMatchHighlight: ['bgYellow']
19 }
20 });
21
22 expect.installTheme('html', {
23 palette: [
24 '#993333',
25 '#669933',
26 '#314575',
27 '#337777',
28 '#710071',
29 '#319916',
30 '#BB1A53',
31 '#999933',
32 '#4311C2',
33 '#996633',
34 '#993399',
35 '#333399',
36 '#228842',
37 '#C24747',
38 '#336699',
39 '#663399'
40 ],
41 styles: {
42 jsComment: '#969896',
43 jsFunctionName: '#795da3',
44 jsKeyword: '#a71d5d',
45 jsPrimitive: '#0086b3',
46 jsRegexp: '#183691',
47 jsString: '#df5000',
48 jsKey: '#555'
49 }
50 });
51
52 expect.installTheme('ansi', {
53 palette: [
54 '#FF1A53',
55 '#E494FF',
56 '#1A53FF',
57 '#FF1AC6',
58 '#1AFF53',
59 '#D557FF',
60 '#81FF57',
61 '#C6FF1A',
62 '#531AFF',
63 '#AFFF94',
64 '#C61AFF',
65 '#53FF1A',
66 '#FF531A',
67 '#1AFFC6',
68 '#FFC61A',
69 '#1AC6FF'
70 ],
71 styles: {
72 jsComment: 'gray',
73 jsFunctionName: 'jsKeyword',
74 jsKeyword: 'magenta',
75 jsNumber: [],
76 jsPrimitive: 'cyan',
77 jsRegexp: 'green',
78 jsString: 'cyan',
79 jsKey: '#666',
80 diffAddedHighlight: ['bgGreen', 'black'],
81 diffRemovedHighlight: ['bgRed', 'black'],
82 partialMatchHighlight: ['bgYellow', 'black']
83 }
84 });
85
86 expect.addStyle('colorByIndex', function(content, index) {
87 var palette = this.theme().palette;
88 if (palette) {
89 var color = palette[index % palette.length];
90 this.text(content, color);
91 } else {
92 this.text(content);
93 }
94 });
95
96 expect.addStyle('singleQuotedString', function(content) {
97 content = String(content);
98 this.jsString("'")
99 .jsString(
100 // eslint-disable-next-line no-control-regex
101 content.replace(/[\\\x00-\x1f']/g, function ($0) {
102 if ($0 === '\n') {
103 return '\\n';
104 } else if ($0 === '\r') {
105 return '\\r';
106 } else if ($0 === "'") {
107 return "\\'";
108 } else if ($0 === '\\') {
109 return '\\\\';
110 } else if ($0 === '\t') {
111 return '\\t';
112 } else if ($0 === '\b') {
113 return '\\b';
114 } else if ($0 === '\f') {
115 return '\\f';
116 } else {
117 var charCode = $0.charCodeAt(0);
118 return ("\\x" + (charCode < 16 ? '0' : '') + (charCode.toString(16)));
119 }
120 })
121 )
122 .jsString("'");
123 });
124
125 expect.addStyle('propertyForObject', function(
126 key,
127 inspectedValue,
128 isArrayLike
129 ) {
130 var keyOmitted = false;
131 var isSymbol;
132 isSymbol = typeof key === 'symbol';
133 if (isSymbol) {
134 this.text('[')
135 .appendInspected(key)
136 .text(']')
137 .text(':');
138 } else {
139 key = String(key);
140 if (/^[a-z$_][a-z0-9$_]*$/i.test(key)) {
141 this.text(key, 'jsKey').text(':');
142 } else if (/^(?:0|[1-9][0-9]*)$/.test(key)) {
143 if (isArrayLike) {
144 keyOmitted = true;
145 } else {
146 this.jsNumber(key).text(':');
147 }
148 } else {
149 this.singleQuotedString(key).text(':');
150 }
151 }
152
153 if (!inspectedValue.isEmpty()) {
154 if (!keyOmitted) {
155 if (
156 key.length > 5 &&
157 inspectedValue.isBlock() &&
158 inspectedValue.isMultiline()
159 ) {
160 this.indentLines();
161 this.nl().i();
162 } else {
163 this.sp();
164 }
165 }
166 this.append(inspectedValue);
167 }
168 });
169
170 // Intended to be redefined by a plugin that offers syntax highlighting:
171 expect.addStyle('code', function(content, language) {
172 this.text(content);
173 });
174
175 expect.addStyle('annotationBlock', function() {
176 var args = [], len = arguments.length;
177 while ( len-- ) args[ len ] = arguments[ len ];
178
179 var pen = this.getContentFromArguments(args);
180 var height = pen.size().height;
181
182 this.block(function() {
183 for (var i = 0; i < height; i += 1) {
184 if (i > 0) {
185 this.nl();
186 }
187 this.error('//');
188 }
189 });
190 this.sp().block(pen);
191 });
192
193 expect.addStyle('commentBlock', function() {
194 var args = [], len = arguments.length;
195 while ( len-- ) args[ len ] = arguments[ len ];
196
197 var pen = this.getContentFromArguments(args);
198 var height = pen.size().height;
199
200 this.block(function() {
201 for (var i = 0; i < height; i += 1) {
202 if (i > 0) {
203 this.nl();
204 }
205 this.jsComment('//');
206 }
207 });
208 this.sp().block(pen);
209 });
210
211 expect.addStyle('diffLinesOmitted', function(lineCount) {
212 this.jsComment(
213 ("... " + lineCount + " line" + (lineCount === 1 ? '' : 's') + " omitted ...")
214 );
215 });
216
217 expect.addStyle('removedHighlight', function(content) {
218 this.alt({
219 text: function text() {
220 content.split(/(\n)/).forEach(function(fragment) {
221 this.block(function() {
222 this.text(fragment.replace(/\n/g, '\\n'))
223 .nl()
224 .text(fragment.replace(/[\s\S]/g, '^'));
225 });
226 if (fragment === '\n') {
227 this.nl();
228 }
229 }, this);
230 },
231 fallback: function fallback() {
232 var this$1 = this;
233
234 content.split(/(\n)/).forEach(function (fragment) {
235 if (fragment === '\n') {
236 this$1.diffRemovedSpecialChar('\\n').nl();
237 } else {
238 this$1.diffRemovedHighlight(fragment);
239 }
240 });
241 }
242 });
243 });
244
245 expect.addStyle('match', function(content) {
246 this.alt({
247 text: function text() {
248 content.split(/(\n)/).forEach(function(fragment) {
249 if (fragment === '\n') {
250 this.nl();
251 } else {
252 this.block(function() {
253 this.text(fragment)
254 .nl()
255 .text(fragment.replace(/[\s\S]/g, '^'));
256 });
257 }
258 }, this);
259 },
260 fallback: function fallback() {
261 this.diffAddedHighlight(content);
262 }
263 });
264 });
265
266 expect.addStyle('partialMatch', function(content) {
267 this.alt({
268 text: function text() {
269 // We haven't yet come up with a good styling for partial matches in text mode
270 this.match(content);
271 },
272 fallback: function fallback() {
273 this.partialMatchHighlight(content);
274 }
275 });
276 });
277
278 expect.addStyle('shouldEqualError', function(expected) {
279 this.error(typeof expected === 'undefined' ? 'should be' : 'should equal')
280 .sp()
281 .block(function() {
282 this.appendInspected(expected);
283 });
284 });
285
286 expect.addStyle('errorName', function(ref) {
287 var name = ref.name;
288 var constructor = ref.constructor;
289
290 if (typeof name === 'string' && name !== 'Error') {
291 this.text(name);
292 } else if (constructor && typeof constructor.name === 'string') {
293 this.text(constructor.name);
294 } else {
295 this.text('Error');
296 }
297 });
298
299 expect.addStyle('appendErrorMessage', function(error, options) {
300 if (error && error.isUnexpected) {
301 this.append(
302 error.getErrorMessage(utils.extend({ output: this }, options))
303 );
304 } else {
305 this.appendInspected(error);
306 }
307 });
308
309 expect.addStyle('appendItems', function(items, separator) {
310 var that = this;
311 separator = separator || '';
312 items.forEach(function (item, index) {
313 if (index > 0) {
314 that.append(separator);
315 }
316 that.appendInspected(item);
317 });
318 });
319
320 expect.addStyle('stringDiffFragment', function(
321 ch,
322 text,
323 baseStyle,
324 markUpSpecialCharacters
325 ) {
326 text.split(/\n/).forEach(function(line, i, ref) {
327 var length = ref.length;
328
329 if (this.isAtStartOfLine()) {
330 this.alt({
331 text: ch,
332 fallback: function fallback() {
333 if (line === '' && ch !== ' ' && (i === 0 || i !== length - 1)) {
334 this[
335 ch === '+' ? 'diffAddedSpecialChar' : 'diffRemovedSpecialChar'
336 ]('\\n');
337 }
338 }
339 });
340 }
341 var matchTrailingSpace = line.match(/^(.*[^ ])?( +)$/);
342 if (matchTrailingSpace) {
343 line = matchTrailingSpace[1] || '';
344 }
345
346 if (markUpSpecialCharacters) {
347 line.split(specialCharRegExp).forEach(function(part) {
348 if (specialCharRegExp.test(part)) {
349 this[
350 { '+': 'diffAddedSpecialChar', '-': 'diffRemovedSpecialChar' }[
351 ch
352 ] || baseStyle
353 ](utils.escapeChar(part));
354 } else {
355 this[baseStyle](part);
356 }
357 }, this);
358 } else {
359 this[baseStyle](line);
360 }
361 if (matchTrailingSpace) {
362 this[
363 { '+': 'diffAddedHighlight', '-': 'diffRemovedHighlight' }[ch] ||
364 baseStyle
365 ](matchTrailingSpace[2]);
366 }
367 if (i !== length - 1) {
368 this.nl();
369 }
370 }, this);
371 });
372
373 expect.addStyle('stringDiff', function(actual, expected, options) {
374 if ( options === void 0 ) options = {};
375
376 var type = options.type || 'WordsWithSpace';
377 var diffLines = [];
378 var lastPart;
379 stringDiff.diffLines(actual, expected).forEach(function (part) {
380 if (lastPart && lastPart.removed && part.added) {
381 diffLines.push({
382 oldValue: lastPart.value,
383 newValue: part.value,
384 replaced: true
385 });
386 lastPart = null;
387 } else {
388 if (lastPart) {
389 diffLines.push(lastPart);
390 }
391 lastPart = part;
392 }
393 });
394 if (lastPart) {
395 diffLines.push(lastPart);
396 }
397
398 diffLines.forEach(function(part, index) {
399 if (part.replaced) {
400 var oldValue = part.oldValue;
401 var newValue = part.newValue;
402 var newLine = this.clone();
403 var oldEndsWithNewline = oldValue.slice(-1) === '\n';
404 var newEndsWithNewline = newValue.slice(-1) === '\n';
405 if (oldEndsWithNewline) {
406 oldValue = oldValue.slice(0, -1);
407 }
408 if (newEndsWithNewline) {
409 newValue = newValue.slice(0, -1);
410 }
411 stringDiff[("diff" + type)](oldValue, newValue).forEach(function(ref) {
412 var added = ref.added;
413 var value = ref.value;
414 var removed = ref.removed;
415
416 if (added) {
417 newLine.stringDiffFragment(
418 '+',
419 value,
420 'diffAddedHighlight',
421 options.markUpSpecialCharacters
422 );
423 } else if (removed) {
424 this.stringDiffFragment(
425 '-',
426 value,
427 'diffRemovedHighlight',
428 options.markUpSpecialCharacters
429 );
430 } else {
431 newLine.stringDiffFragment('+', value, 'diffAddedLine');
432 this.stringDiffFragment('-', value, 'diffRemovedLine');
433 }
434 },
435 this);
436 if (newEndsWithNewline && !oldEndsWithNewline) {
437 newLine.diffAddedSpecialChar('\\n');
438 }
439
440 if (oldEndsWithNewline && !newEndsWithNewline) {
441 this.diffRemovedSpecialChar('\\n');
442 }
443 this.nl()
444 .append(newLine)
445 .nl(oldEndsWithNewline && index < diffLines.length - 1 ? 1 : 0);
446 } else {
447 var endsWithNewline = /\n$/.test(part.value);
448 var value = endsWithNewline ? part.value.slice(0, -1) : part.value;
449 if (part.added) {
450 this.stringDiffFragment(
451 '+',
452 value,
453 'diffAddedLine',
454 options.markUpSpecialCharacters
455 );
456 } else if (part.removed) {
457 this.stringDiffFragment(
458 '-',
459 value,
460 'diffRemovedLine',
461 options.markUpSpecialCharacters
462 );
463 } else {
464 var horizon = 3;
465 if (index === diffLines.length - 1 && part.count > horizon) {
466 this.stringDiffFragment(
467 ' ',
468 value
469 .split('\n')
470 .slice(0, horizon)
471 .join('\n'),
472 'text'
473 )
474 .nl()
475 .diffLinesOmitted(part.count - horizon);
476 } else if (index === 0 && part.count > horizon) {
477 this.diffLinesOmitted(part.count - horizon)
478 .nl()
479 .stringDiffFragment(
480 ' ',
481 value
482 .split('\n')
483 .slice(-horizon)
484 .join('\n'),
485 'text'
486 );
487 } else if (part.count > 2 * horizon) {
488 this.stringDiffFragment(
489 ' ',
490 value
491 .split('\n')
492 .slice(0, horizon)
493 .join('\n'),
494 'text'
495 )
496 .nl()
497 .diffLinesOmitted(part.count - 2 * horizon)
498 .nl()
499 .stringDiffFragment(
500 ' ',
501 value
502 .split('\n')
503 .slice(-horizon)
504 .join('\n'),
505 'text'
506 );
507 } else {
508 this.stringDiffFragment(' ', value, 'text');
509 }
510 }
511 if (endsWithNewline) {
512 this.nl();
513 }
514 }
515 }, this);
516 });
517
518 expect.addStyle('arrow', function(options) {
519 if ( options === void 0 ) options = {};
520
521 var styles = options.styles || [];
522 var i;
523 this.nl(options.top || 0)
524 .sp(options.left || 0)
525 .text('┌', styles);
526 for (i = 1; i < options.width; i += 1) {
527 this.text(
528 i === options.width - 1 && options.direction === 'up' ? '▷' : '─',
529 styles
530 );
531 }
532 this.nl();
533 for (i = 1; i < options.height - 1; i += 1) {
534 this.sp(options.left || 0)
535 .text('│', styles)
536 .nl();
537 }
538 this.sp(options.left || 0).text('└', styles);
539 for (i = 1; i < options.width; i += 1) {
540 this.text(
541 i === options.width - 1 && options.direction === 'down' ? '▷' : '─',
542 styles
543 );
544 }
545 });
546
547 var flattenBlocksInLines = require('magicpen/lib/flattenBlocksInLines');
548 expect.addStyle('merge', function(pens) {
549 var flattenedPens = pens
550 .map(function (ref) {
551 var output = ref.output;
552
553 return flattenBlocksInLines(output);
554 })
555 .reverse();
556 var maxHeight = flattenedPens.reduce(
557 function (maxHeight, ref) {
558 var length = ref.length;
559
560 return Math.max(maxHeight, length);
561 },
562 0
563 );
564 var blockNumbers = new Array(flattenedPens.length);
565 var blockOffsets = new Array(flattenedPens.length);
566 // As long as there's at least one pen with a line left:
567 for (var lineNumber = 0; lineNumber < maxHeight; lineNumber += 1) {
568 if (lineNumber > 0) {
569 this.nl();
570 }
571 var i = (void 0);
572 for (i = 0; i < blockNumbers.length; i += 1) {
573 blockNumbers[i] = 0;
574 blockOffsets[i] = 0;
575 }
576 var contentLeft = (void 0);
577 do {
578 contentLeft = false;
579 var hasOutputChar = false;
580 for (i = 0; i < flattenedPens.length; i += 1) {
581 var currentLine = flattenedPens[i][lineNumber];
582 if (currentLine) {
583 while (
584 currentLine[blockNumbers[i]] &&
585 blockOffsets[i] >=
586 currentLine[blockNumbers[i]].args.content.length
587 ) {
588 blockNumbers[i] += 1;
589 blockOffsets[i] = 0;
590 }
591 var currentBlock = currentLine[blockNumbers[i]];
592 if (currentBlock) {
593 contentLeft = true;
594 if (!hasOutputChar) {
595 var ch = currentBlock.args.content.charAt(blockOffsets[i]);
596 if (ch !== ' ') {
597 this.text(ch, currentBlock.args.styles);
598 hasOutputChar = true;
599 }
600 }
601 blockOffsets[i] += 1;
602 }
603 }
604 }
605 if (!hasOutputChar && contentLeft) {
606 this.sp();
607 }
608 } while (contentLeft);
609 }
610 });
611
612 expect.addStyle('arrowsAlongsideChangeOutputs', function(
613 packing,
614 changeOutputs
615 ) {
616 if (packing) {
617 var topByChangeNumber = {};
618 var top = 0;
619 changeOutputs.forEach(function (changeOutput, index) {
620 topByChangeNumber[index] = top;
621 top += changeOutput.size().height;
622 });
623 var that = this;
624
625 var arrows = [];
626 packing.forEach(function (columnSet, i, ref) {
627 var length = ref.length;
628
629 columnSet.forEach(function (ref) {
630 var start = ref.start;
631 var end = ref.end;
632 var direction = ref.direction;
633
634 arrows.push(
635 that.clone().arrow({
636 left: i * 2,
637 top: topByChangeNumber[start],
638 width: 1 + (length - i) * 2,
639 height: topByChangeNumber[end] - topByChangeNumber[start] + 1,
640 direction: direction
641 })
642 );
643 });
644 });
645
646 if (arrows.length === 1) {
647 this.block(arrows[0]);
648 } else if (arrows.length > 1) {
649 this.block(function() {
650 this.merge(arrows);
651 });
652 }
653 } else {
654 this.i();
655 }
656
657 this.block(function() {
658 changeOutputs.forEach(function(changeOutput, index) {
659 this.nl(index > 0 ? 1 : 0);
660 if (!changeOutput.isEmpty()) {
661 this.sp(packing ? 1 : 0).append(changeOutput);
662 }
663 }, this);
664 });
665 });
666};