1 | var utils = require('./utils');
|
2 | var stringDiff = require('diff');
|
3 | var specialCharRegExp = require('./specialCharRegExp');
|
4 |
|
5 | module.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 |
|
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 |
|
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 |
|
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 |
|
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 | };
|