0
# Diff Generation
1
2
Functions for comparing values and generating visual diffs for test output, helping developers understand exactly how expected and received values differ.
3
4
## Capabilities
5
6
### Diff Function
7
8
Generates diff output between two values when appropriate, with automatic detection of diffable types.
9
10
```typescript { .api }
11
/**
12
* Generate diff between two values
13
* @param a - First value to compare
14
* @param b - Second value to compare
15
* @param options - Diff generation options
16
* @returns Diff output string or null if not diffable
17
*/
18
function diff(
19
a: unknown,
20
b: unknown,
21
options?: DiffOptions
22
): string | null;
23
24
type DiffOptions = {
25
aAnnotation?: string;
26
bAnnotation?: string;
27
changeColor?: (arg: string) => string;
28
changeLineTrailingSpaceColor?: (arg: string) => string;
29
commonColor?: (arg: string) => string;
30
commonLineTrailingSpaceColor?: (arg: string) => string;
31
contextLines?: number;
32
expand?: boolean;
33
includeChangeCounts?: boolean;
34
omitAnnotationLines?: boolean;
35
patchColor?: (arg: string) => string;
36
};
37
```
38
39
**Usage Examples:**
40
41
```typescript
42
import { diff } from "jest-matcher-utils";
43
44
// Basic object diff
45
const expected = { name: "Alice", age: 25, city: "NYC" };
46
const received = { name: "Alice", age: 30, city: "LA" };
47
const diffResult = diff(expected, received);
48
// Result: Colorized diff showing changed properties
49
50
// Array diff
51
const expectedArray = [1, 2, 3, 4];
52
const receivedArray = [1, 2, 5, 4];
53
const arrayDiff = diff(expectedArray, receivedArray);
54
// Result: Shows element differences
55
56
// String diff
57
const expectedText = "Hello world";
58
const receivedText = "Hello earth";
59
const textDiff = diff(expectedText, receivedText);
60
// Result: Character-level diff highlighting changes
61
62
// Returns null for non-diffable types
63
const numDiff = diff(5, 10);
64
// Result: null (numbers don't generate useful diffs)
65
66
// With options
67
const customDiff = diff(expected, received, {
68
expand: true,
69
includeChangeCounts: true
70
});
71
```
72
73
### Print Diff Or Stringify
74
75
Generates comprehensive diff output or falls back to stringified comparison when diff is not useful.
76
77
```typescript { .api }
78
/**
79
* Generate diff output or stringify values for comparison
80
* @param expected - Expected value
81
* @param received - Received value
82
* @param expectedLabel - Label for expected value
83
* @param receivedLabel - Label for received value
84
* @param expand - Whether to expand diff output
85
* @returns Formatted diff or comparison string
86
*/
87
function printDiffOrStringify(
88
expected: unknown,
89
received: unknown,
90
expectedLabel: string,
91
receivedLabel: string,
92
expand: boolean
93
): string;
94
```
95
96
**Usage Examples:**
97
98
```typescript
99
import { printDiffOrStringify } from "jest-matcher-utils";
100
101
// Object comparison with labels
102
const expected = { user: "Alice", role: "admin" };
103
const received = { user: "Alice", role: "user" };
104
const comparison = printDiffOrStringify(
105
expected,
106
received,
107
"Expected",
108
"Received",
109
false
110
);
111
// Result: Labeled diff output showing role change
112
113
// String comparison with multiline
114
const expectedText = `Line 1
115
Line 2
116
Line 3`;
117
const receivedText = `Line 1
118
Modified Line 2
119
Line 3`;
120
const textComparison = printDiffOrStringify(
121
expectedText,
122
receivedText,
123
"Expected",
124
"Received",
125
true // expanded output
126
);
127
// Result: Unified diff format with line numbers
128
129
// Falls back to stringify for incomparable types
130
const numComparison = printDiffOrStringify(
131
42,
132
43,
133
"Expected",
134
"Received",
135
false
136
);
137
// Result: 'Expected: 42\nReceived: 43'
138
139
// Handles identical values that serialize differently
140
const date1 = new Date('2024-01-01');
141
const date2 = new Date('2024-01-01');
142
const sameComparison = printDiffOrStringify(
143
date1,
144
date2,
145
"Expected",
146
"Received",
147
false
148
);
149
// Result: 'Expected: ...\nReceived: serializes to the same string'
150
```
151
152
### Replace Matched To Asymmetric Matcher
153
154
Advanced function for replacing matched values with asymmetric matchers during comparison, handling Jest's expect.any(), expect.objectContaining(), etc.
155
156
```typescript { .api }
157
/**
158
* Replace matched values with asymmetric matchers for comparison
159
* @param replacedExpected - Expected value to process
160
* @param replacedReceived - Received value to process
161
* @param expectedCycles - Cycle detection for expected value
162
* @param receivedCycles - Cycle detection for received value
163
* @returns Object with processed expected and received values
164
*/
165
function replaceMatchedToAsymmetricMatcher(
166
replacedExpected: unknown,
167
replacedReceived: unknown,
168
expectedCycles: Array<unknown>,
169
receivedCycles: Array<unknown>
170
): {replacedExpected: unknown; replacedReceived: unknown};
171
```
172
173
**Usage Examples:**
174
175
```typescript
176
import { replaceMatchedToAsymmetricMatcher } from "jest-matcher-utils";
177
178
// Handle asymmetric matchers in expected values
179
const expected = {
180
id: expect.any(Number),
181
name: "Alice",
182
metadata: expect.objectContaining({ version: "1.0" })
183
};
184
185
const received = {
186
id: 123,
187
name: "Alice",
188
metadata: { version: "1.0", extra: "data" }
189
};
190
191
const {replacedExpected, replacedReceived} = replaceMatchedToAsymmetricMatcher(
192
expected,
193
received,
194
[], // expectedCycles
195
[] // receivedCycles
196
);
197
198
// Result: Asymmetric matchers that match are replaced in received,
199
// making diff output cleaner by showing what actually differs
200
201
// Handles circular references
202
const circular = { prop: null };
203
circular.prop = circular;
204
205
const processed = replaceMatchedToAsymmetricMatcher(
206
circular,
207
{ prop: { prop: "different" } },
208
[circular], // Track circular reference
209
[]
210
);
211
// Prevents infinite recursion during processing
212
213
// Used internally by Jest matchers
214
function customMatcher(received: unknown, expected: unknown) {
215
const {replacedExpected, replacedReceived} = replaceMatchedToAsymmetricMatcher(
216
expected,
217
received,
218
[],
219
[]
220
);
221
222
// Generate cleaner diff with processed values
223
const diffOutput = diff(replacedExpected, replacedReceived);
224
return diffOutput;
225
}
226
```
227
228
## Diff Strategy
229
230
The diff generation system uses intelligent strategies:
231
232
1. **String Diffs**: Character-level and line-level diffs for strings
233
- Short strings: Character highlighting
234
- Multi-line strings: Unified diff format
235
- Trailing whitespace visualization
236
237
2. **Object/Array Diffs**: Structured comparison
238
- Property-level changes highlighted
239
- Nested object traversal
240
- Array element comparison
241
242
3. **Type-Based Logic**: Different approaches per type
243
- Primitives: Simple before/after display
244
- Complex objects: Recursive property comparison
245
- Functions/Dates/RegExp: String representation fallback
246
247
4. **Asymmetric Matcher Support**:
248
- Replaces matching asymmetric matchers
249
- Cleaner diff output focusing on actual differences
250
- Circular reference handling
251
252
5. **Performance Limits**:
253
- Maximum string length limits (20,000 chars)
254
- Automatic fallback to stringify for large objects
255
- Cycle detection prevents infinite recursion
256
257
The system automatically chooses the most informative representation for any value comparison, ensuring developers get the clearest possible view of test failures.