UNPKG

19.7 kBMarkdownView Raw
1# jest-diff
2
3Display differences clearly so people can review changes confidently.
4
5The `diff` named export serializes JavaScript **values**, compares them line-by-line, and returns a string which includes comparison lines.
6
7Two named exports compare **strings** character-by-character:
8
9- `diffStringsUnified` returns a string.
10- `diffStringsRaw` returns an array of `Diff` objects.
11
12Three named exports compare **arrays of strings** line-by-line:
13
14- `diffLinesUnified` and `diffLinesUnified2` return a string.
15- `diffLinesRaw` returns an array of `Diff` objects.
16
17## Installation
18
19To add this package as a dependency of a project, run either of the following commands:
20
21- `npm install jest-diff`
22- `yarn add jest-diff`
23
24## Usage of `diff()`
25
26Given JavaScript **values**, `diff(a, b, options?)` does the following:
27
281. **serialize** the values as strings using the `pretty-format` package
292. **compare** the strings line-by-line using the `diff-sequences` package
303. **format** the changed or common lines using the `chalk` package
31
32To use this function, write either of the following:
33
34- `const {diff} = require('jest-diff');` in CommonJS modules
35- `import {diff} from 'jest-diff';` in ECMAScript modules
36
37### Example of `diff()`
38
39```js
40const a = ['delete', 'common', 'changed from'];
41const b = ['common', 'changed to', 'insert'];
42
43const difference = diff(a, b);
44```
45
46The returned **string** consists of:
47
48- annotation lines: describe the two change indicators with labels, and a blank line
49- comparison lines: similar to “unified” view on GitHub, but `Expected` lines are green, `Received` lines are red, and common lines are dim (by default, see Options)
50
51```diff
52- Expected
53+ Received
54
55 Array [
56- "delete",
57 "common",
58- "changed from",
59+ "changed to",
60+ "insert",
61 ]
62```
63
64### Edge cases of `diff()`
65
66Here are edge cases for the return value:
67
68- `' Comparing two different types of values. …'` if the arguments have **different types** according to the `jest-get-type` package (instances of different classes have the same `'object'` type)
69- `'Compared values have no visual difference.'` if the arguments have either **referential identity** according to `Object.is` method or **same serialization** according to the `pretty-format` package
70- `null` if either argument is a so-called **asymmetric matcher** in Jasmine or Jest
71
72## Usage of diffStringsUnified
73
74Given **strings**, `diffStringsUnified(a, b, options?)` does the following:
75
761. **compare** the strings character-by-character using the `diff-sequences` package
772. **clean up** small (often coincidental) common substrings, also known as chaff
783. **format** the changed or common lines using the `chalk` package
79
80Although the function is mainly for **multiline** strings, it compares any strings.
81
82Write either of the following:
83
84- `const {diffStringsUnified} = require('jest-diff');` in CommonJS modules
85- `import {diffStringsUnified} from 'jest-diff';` in ECMAScript modules
86
87### Example of diffStringsUnified
88
89```js
90const a = 'common\nchanged from';
91const b = 'common\nchanged to';
92
93const difference = diffStringsUnified(a, b);
94```
95
96The returned **string** consists of:
97
98- annotation lines: describe the two change indicators with labels, and a blank line
99- comparison lines: similar to “unified” view on GitHub, and **changed substrings** have **inverse** foreground and background colors (that is, `from` has white-on-green and `to` has white-on-red, which the following example does not show)
100
101```diff
102- Expected
103+ Received
104
105 common
106- changed from
107+ changed to
108```
109
110### Performance of diffStringsUnified
111
112To get the benefit of **changed substrings** within the comparison lines, a character-by-character comparison has a higher computational cost (in time and space) than a line-by-line comparison.
113
114If the input strings can have **arbitrary length**, we recommend that the calling code set a limit, beyond which splits the strings, and then calls `diffLinesUnified` instead. For example, Jest falls back to line-by-line comparison if either string has length greater than 20K characters.
115
116## Usage of diffLinesUnified
117
118Given **arrays of strings**, `diffLinesUnified(aLines, bLines, options?)` does the following:
119
1201. **compare** the arrays line-by-line using the `diff-sequences` package
1212. **format** the changed or common lines using the `chalk` package
122
123You might call this function when strings have been split into lines and you do not need to see changed substrings within lines.
124
125### Example of diffLinesUnified
126
127```js
128const aLines = ['delete', 'common', 'changed from'];
129const bLines = ['common', 'changed to', 'insert'];
130
131const difference = diffLinesUnified(aLines, bLines);
132```
133
134```diff
135- Expected
136+ Received
137
138- delete
139 common
140- changed from
141+ changed to
142+ insert
143```
144
145### Edge cases of diffLinesUnified or diffStringsUnified
146
147Here are edge cases for arguments and return values:
148
149- both `a` and `b` are empty strings: no comparison lines
150- only `a` is empty string: all comparison lines have `bColor` and `bIndicator` (see Options)
151- only `b` is empty string: all comparison lines have `aColor` and `aIndicator` (see Options)
152- `a` and `b` are equal non-empty strings: all comparison lines have `commonColor` and `commonIndicator` (see Options)
153
154## Usage of diffLinesUnified2
155
156Given two **pairs** of arrays of strings, `diffLinesUnified2(aLinesDisplay, bLinesDisplay, aLinesCompare, bLinesCompare, options?)` does the following:
157
1581. **compare** the pair of `Compare` arrays line-by-line using the `diff-sequences` package
1592. **format** the corresponding lines in the pair of `Display` arrays using the `chalk` package
160
161Jest calls this function to consider lines as common instead of changed if the only difference is indentation.
162
163You might call this function for case insensitive or Unicode equivalence comparison of lines.
164
165### Example of diffLinesUnified2
166
167```js
168import {format} from 'pretty-format';
169
170const a = {
171 text: 'Ignore indentation in serialized object',
172 time: '2019-09-19T12:34:56.000Z',
173 type: 'CREATE_ITEM',
174};
175const b = {
176 payload: {
177 text: 'Ignore indentation in serialized object',
178 time: '2019-09-19T12:34:56.000Z',
179 },
180 type: 'CREATE_ITEM',
181};
182
183const difference = diffLinesUnified2(
184 // serialize with indentation to display lines
185 format(a).split('\n'),
186 format(b).split('\n'),
187 // serialize without indentation to compare lines
188 format(a, {indent: 0}).split('\n'),
189 format(b, {indent: 0}).split('\n'),
190);
191```
192
193The `text` and `time` properties are common, because their only difference is indentation:
194
195```diff
196- Expected
197+ Received
198
199 Object {
200+ payload: Object {
201 text: 'Ignore indentation in serialized object',
202 time: '2019-09-19T12:34:56.000Z',
203+ },
204 type: 'CREATE_ITEM',
205 }
206```
207
208The preceding example illustrates why (at least for indentation) it seems more intuitive that the function returns the common line from the `bLinesDisplay` array instead of from the `aLinesDisplay` array.
209
210## Usage of diffStringsRaw
211
212Given **strings** and a boolean option, `diffStringsRaw(a, b, cleanup)` does the following:
213
2141. **compare** the strings character-by-character using the `diff-sequences` package
2152. optionally **clean up** small (often coincidental) common substrings, also known as chaff
216
217Because `diffStringsRaw` returns the difference as **data** instead of a string, you can format it as your application requires (for example, enclosed in HTML markup for browser instead of escape sequences for console).
218
219The returned **array** describes substrings as instances of the `Diff` class, which calling code can access like array tuples:
220
221The value at index `0` is one of the following:
222
223| value | named export | description |
224| ----: | :------------ | :-------------------- |
225| `0` | `DIFF_EQUAL` | in `a` and in `b` |
226| `-1` | `DIFF_DELETE` | in `a` but not in `b` |
227| `1` | `DIFF_INSERT` | in `b` but not in `a` |
228
229The value at index `1` is a substring of `a` or `b` or both.
230
231### Example of diffStringsRaw with cleanup
232
233```js
234const diffs = diffStringsRaw('changed from', 'changed to', true);
235```
236
237| `i` | `diffs[i][0]` | `diffs[i][1]` |
238| --: | ------------: | :------------ |
239| `0` | `0` | `'changed '` |
240| `1` | `-1` | `'from'` |
241| `2` | `1` | `'to'` |
242
243### Example of diffStringsRaw without cleanup
244
245```js
246const diffs = diffStringsRaw('changed from', 'changed to', false);
247```
248
249| `i` | `diffs[i][0]` | `diffs[i][1]` |
250| --: | ------------: | :------------ |
251| `0` | `0` | `'changed '` |
252| `1` | `-1` | `'fr'` |
253| `2` | `1` | `'t'` |
254| `3` | `0` | `'o'` |
255| `4` | `-1` | `'m'` |
256
257### Advanced import for diffStringsRaw
258
259Here are all the named imports that you might need for the `diffStringsRaw` function:
260
261- `const {DIFF_DELETE, DIFF_EQUAL, DIFF_INSERT, Diff, diffStringsRaw} = require('jest-diff');` in CommonJS modules
262- `import {DIFF_DELETE, DIFF_EQUAL, DIFF_INSERT, Diff, diffStringsRaw} from 'jest-diff';` in ECMAScript modules
263
264To write a **formatting** function, you might need the named constants (and `Diff` in TypeScript annotations).
265
266If you write an application-specific **cleanup** algorithm, then you might need to call the `Diff` constructor:
267
268```js
269const diffCommon = new Diff(DIFF_EQUAL, 'changed ');
270const diffDelete = new Diff(DIFF_DELETE, 'from');
271const diffInsert = new Diff(DIFF_INSERT, 'to');
272```
273
274## Usage of diffLinesRaw
275
276Given **arrays of strings**, `diffLinesRaw(aLines, bLines)` does the following:
277
278- **compare** the arrays line-by-line using the `diff-sequences` package
279
280Because `diffLinesRaw` returns the difference as **data** instead of a string, you can format it as your application requires.
281
282### Example of diffLinesRaw
283
284```js
285const aLines = ['delete', 'common', 'changed from'];
286const bLines = ['common', 'changed to', 'insert'];
287
288const diffs = diffLinesRaw(aLines, bLines);
289```
290
291| `i` | `diffs[i][0]` | `diffs[i][1]` |
292| --: | ------------: | :--------------- |
293| `0` | `-1` | `'delete'` |
294| `1` | `0` | `'common'` |
295| `2` | `-1` | `'changed from'` |
296| `3` | `1` | `'changed to'` |
297| `4` | `1` | `'insert'` |
298
299### Edge case of diffLinesRaw
300
301If you call `string.split('\n')` for an empty string:
302
303- the result is `['']` an array which contains an empty string
304- instead of `[]` an empty array
305
306Depending of your application, you might call `diffLinesRaw` with either array.
307
308### Example of split method
309
310```js
311import {diffLinesRaw} from 'jest-diff';
312
313const a = 'non-empty string';
314const b = '';
315
316const diffs = diffLinesRaw(a.split('\n'), b.split('\n'));
317```
318
319| `i` | `diffs[i][0]` | `diffs[i][1]` |
320| --: | ------------: | :------------------- |
321| `0` | `-1` | `'non-empty string'` |
322| `1` | `1` | `''` |
323
324Which you might format as follows:
325
326```diff
327- Expected - 1
328+ Received + 1
329
330- non-empty string
331+
332```
333
334### Example of splitLines0 function
335
336For edge case behavior like the `diffLinesUnified` function, you might define a `splitLines0` function, which given an empty string, returns `[]` an empty array:
337
338```js
339export const splitLines0 = string =>
340 string.length === 0 ? [] : string.split('\n');
341```
342
343```js
344import {diffLinesRaw} from 'jest-diff';
345
346const a = '';
347const b = 'line 1\nline 2\nline 3';
348
349const diffs = diffLinesRaw(a.split('\n'), b.split('\n'));
350```
351
352| `i` | `diffs[i][0]` | `diffs[i][1]` |
353| --: | ------------: | :------------ |
354| `0` | `1` | `'line 1'` |
355| `1` | `1` | `'line 2'` |
356| `2` | `1` | `'line 3'` |
357
358Which you might format as follows:
359
360```diff
361- Expected - 0
362+ Received + 3
363
364+ line 1
365+ line 2
366+ line 3
367```
368
369In contrast to the `diffLinesRaw` function, the `diffLinesUnified` and `diffLinesUnified2` functions **automatically** convert array arguments computed by string `split` method, so callers do **not** need a `splitLine0` function.
370
371## Options
372
373The default options are for the report when an assertion fails from the `expect` package used by Jest.
374
375For other applications, you can provide an options object as a third argument:
376
377- `diff(a, b, options)`
378- `diffStringsUnified(a, b, options)`
379- `diffLinesUnified(aLines, bLines, options)`
380- `diffLinesUnified2(aLinesDisplay, bLinesDisplay, aLinesCompare, bLinesCompare, options)`
381
382### Properties of options object
383
384| name | default |
385| :-------------------------------- | :----------------- |
386| `aAnnotation` | `'Expected'` |
387| `aColor` | `chalk.green` |
388| `aIndicator` | `'-'` |
389| `bAnnotation` | `'Received'` |
390| `bColor` | `chalk.red` |
391| `bIndicator` | `'+'` |
392| `changeColor` | `chalk.inverse` |
393| `changeLineTrailingSpaceColor` | `string => string` |
394| `commonColor` | `chalk.dim` |
395| `commonIndicator` | `' '` |
396| `commonLineTrailingSpaceColor` | `string => string` |
397| `compareKeys` | `undefined` |
398| `contextLines` | `5` |
399| `emptyFirstOrLastLinePlaceholder` | `''` |
400| `expand` | `true` |
401| `includeChangeCounts` | `false` |
402| `omitAnnotationLines` | `false` |
403| `patchColor` | `chalk.yellow` |
404
405For more information about the options, see the following examples.
406
407### Example of options for labels
408
409If the application is code modification, you might replace the labels:
410
411```js
412const options = {
413 aAnnotation: 'Original',
414 bAnnotation: 'Modified',
415};
416```
417
418```diff
419- Original
420+ Modified
421
422 common
423- changed from
424+ changed to
425```
426
427The `jest-diff` package does not assume that the 2 labels have equal length.
428
429### Example of options for colors of changed lines
430
431For consistency with most diff tools, you might exchange the colors:
432
433```ts
434import chalk = require('chalk');
435
436const options = {
437 aColor: chalk.red,
438 bColor: chalk.green,
439};
440```
441
442### Example of option for color of changed substrings
443
444Although the default inverse of foreground and background colors is hard to beat for changed substrings **within lines**, especially because it highlights spaces, if you want bold font weight on yellow background color:
445
446```ts
447import chalk = require('chalk');
448
449const options = {
450 changeColor: chalk.bold.bgYellowBright,
451};
452```
453
454### Example of option to format trailing spaces
455
456Because `diff()` does not display substring differences within lines, formatting can help you see when lines differ by the presence or absence of trailing spaces found by `/\s+$/` regular expression.
457
458- If change lines have a background color, then you can see trailing spaces.
459- If common lines have default dim color, then you cannot see trailing spaces. You might want yellowish background color to see them.
460
461```js
462const options = {
463 aColor: chalk.rgb(128, 0, 128).bgRgb(255, 215, 255), // magenta
464 bColor: chalk.rgb(0, 95, 0).bgRgb(215, 255, 215), // green
465 commonLineTrailingSpaceColor: chalk.bgYellow,
466};
467```
468
469The value of a Color option is a function, which given a string, returns a string.
470
471If you want to replace trailing spaces with middle dot characters:
472
473```js
474const replaceSpacesWithMiddleDot = string => '·'.repeat(string.length);
475
476const options = {
477 changeLineTrailingSpaceColor: replaceSpacesWithMiddleDot,
478 commonLineTrailingSpaceColor: replaceSpacesWithMiddleDot,
479};
480```
481
482If you need the TypeScript type of a Color option:
483
484```ts
485import {DiffOptionsColor} from 'jest-diff';
486```
487
488### Example of options for no colors
489
490To store the difference in a file without escape codes for colors, provide an identity function:
491
492```js
493const noColor = string => string;
494
495const options = {
496 aColor: noColor,
497 bColor: noColor,
498 changeColor: noColor,
499 commonColor: noColor,
500 patchColor: noColor,
501};
502```
503
504### Example of options for indicators
505
506For consistency with the `diff` command, you might replace the indicators:
507
508```js
509const options = {
510 aIndicator: '<',
511 bIndicator: '>',
512};
513```
514
515The `jest-diff` package assumes (but does not enforce) that the 3 indicators have equal length.
516
517### Example of options to limit common lines
518
519By default, the output includes all common lines.
520
521To emphasize the changes, you might limit the number of common “context” lines:
522
523```js
524const options = {
525 contextLines: 1,
526 expand: false,
527};
528```
529
530A patch mark like `@@ -12,7 +12,9 @@` accounts for omitted common lines.
531
532### Example of option for color of patch marks
533
534If you want patch marks to have the same dim color as common lines:
535
536```ts
537import chalk = require('chalk');
538
539const options = {
540 expand: false,
541 patchColor: chalk.dim,
542};
543```
544
545### Example of option to include change counts
546
547To display the number of changed lines at the right of annotation lines:
548
549```js
550const a = ['common', 'changed from'];
551const b = ['common', 'changed to', 'insert'];
552
553const options = {
554 includeChangeCounts: true,
555};
556
557const difference = diff(a, b, options);
558```
559
560```diff
561- Expected - 1
562+ Received + 2
563
564 Array [
565 "common",
566- "changed from",
567+ "changed to",
568+ "insert",
569 ]
570```
571
572### Example of option to omit annotation lines
573
574To display only the comparison lines:
575
576```js
577const a = 'common\nchanged from';
578const b = 'common\nchanged to';
579
580const options = {
581 omitAnnotationLines: true,
582};
583
584const difference = diffStringsUnified(a, b, options);
585```
586
587```diff
588 common
589- changed from
590+ changed to
591```
592
593### Example of option for empty first or last lines
594
595If the **first** or **last** comparison line is **empty**, because the content is empty and the indicator is a space, you might not notice it.
596
597The replacement option is a string whose default value is `''` empty string.
598
599Because Jest trims the report when a matcher fails, it deletes an empty last line.
600
601Therefore, Jest uses as placeholder the downwards arrow with corner leftwards:
602
603```js
604const options = {
605 emptyFirstOrLastLinePlaceholder: '↵', // U+21B5
606};
607```
608
609If a content line is empty, then the corresponding comparison line is automatically trimmed to remove the margin space (represented as a middle dot below) for the default indicators:
610
611| Indicator | untrimmed | trimmed |
612| ----------------: | :-------- | :------ |
613| `aIndicator` | `'-·'` | `'-'` |
614| `bIndicator` | `'+·'` | `'+'` |
615| `commonIndicator` | `' ·'` | `''` |
616
617### Example of option for sorting object keys
618
619When two objects are compared their keys are printed in alphabetical order by default. If this was not the original order of the keys the diff becomes harder to read as the keys are not in their original position.
620
621Use `compareKeys` to pass a function which will be used when sorting the object keys.
622
623```js
624const a = {c: 'c', b: 'b1', a: 'a'};
625const b = {c: 'c', b: 'b2', a: 'a'};
626
627const options = {
628 // The keys will be in their original order
629 compareKeys: () => 0,
630};
631
632const difference = diff(a, b, options);
633```
634
635```diff
636- Expected
637+ Received
638
639 Object {
640 "c": "c",
641- "b": "b1",
642+ "b": "b2",
643 "a": "a",
644 }
645```
646
647Depending on the implementation of `compareKeys` any sort order can be used.
648
649```js
650const a = {c: 'c', b: 'b1', a: 'a'};
651const b = {c: 'c', b: 'b2', a: 'a'};
652
653const options = {
654 // The keys will be in reverse order
655 compareKeys: (a, b) => (a > b ? -1 : 1),
656};
657
658const difference = diff(a, b, options);
659```
660
661```diff
662- Expected
663+ Received
664
665 Object {
666 "a": "a",
667- "b": "b1",
668+ "b": "b2",
669 "c": "c",
670 }
671```