Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

jest-diff: Remove the need to export splitLines0 function #9151

Merged
merged 4 commits into from Nov 8, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -59,6 +59,7 @@
- `[jest]` [**BREAKING**] Use ESM exports ([#8874](https://github.com/facebook/jest/pull/8874))
- `[jest-cli]` [**BREAKING**] Use ESM exports ([#8874](https://github.com/facebook/jest/pull/8874))
- `[jest-cli]` [**BREAKING**] Remove re-exports from `@jest/core` ([#8874](https://github.com/facebook/jest/pull/8874))
- `[jest-diff]` Remove the need to export `splitLines0` function ([#9151](https://github.com/facebook/jest/pull/9151))
- `[jest-environment-jsdom]` [**BREAKING**] Upgrade JSDOM from v11 to v15 ([#8851](https://github.com/facebook/jest/pull/8851))
- `[jest-util]` [**BREAKING**] Remove deprecated exports ([#8863](https://github.com/facebook/jest/pull/8863))
- `[jest-validate]` [**BREAKING**] Use ESM exports ([#8874](https://github.com/facebook/jest/pull/8874))
Expand Down
124 changes: 72 additions & 52 deletions packages/jest-diff/README.md
Expand Up @@ -151,58 +151,6 @@ Here are edge cases for arguments and return values:
- only `b` is empty string: all comparison lines have `aColor` and `aIndicator` (see Options)
- `a` and `b` are equal non-empty strings: all comparison lines have `commonColor` and `commonIndicator` (see Options)

To get the comparison lines described above from the `diffLineUnified` function, call `splitLines0(string)` instead of `string.split('\n')`

```js
export const splitLines0 = string =>
string.length === 0 ? [] : string.split('\n');
```

### Example of splitLines0 function

```js
import {diffLinesUnified, splitLines0} from 'jest-diff';

const a = 'multi\nline\nstring';
const b = '';
const options = {includeChangeCounts: true}; // see Options

const difference = diffLinesUnified(splitLines0(a), splitLines0(b), options);
```

Given an empty string, `splitLines0(b)` returns `[]` an empty array, formatted as no `Received` lines:

```diff
- Expected - 3
+ Received + 0

- multi
- line
- string
```

### Example of split method

```js
const a = 'multi\nline\nstring';
const b = '';
const options = {includeChangeCounts: true}; // see Options

const difference = diffLinesUnified(a.split('\n'), b.split('\n'), options);
```

Given an empty string, `b.split('\n')` returns `['']` an array that contains an empty string, formatted as one empty `Received` line, which is **ambiguous** with an empty line:

```diff
- Expected - 3
+ Received + 1

- multi
- line
- string
+
```

## Usage of diffLinesUnified2

Given two **pairs** of arrays of strings, `diffLinesUnified2(aLinesDisplay, bLinesDisplay, aLinesCompare, bLinesCompare, options?)` does the following:
Expand Down Expand Up @@ -348,6 +296,78 @@ const diffs = diffLinesRaw(aLines, bLines);
| `3` | `1` | `'changed to'` |
| `4` | `1` | `'insert'` |

### Edge case of diffLinesRaw

If you call `string.split('\n')` for an empty string:

- the result is `['']` an array which contains an empty string
- instead of `[]` an empty array

Depending of your application, you might call `diffLinesRaw` with either array.

### Example of split method

```js
import {diffLinesRaw} from 'jest-diff';

const a = 'non-empty string';
const b = '';

const diffs = diffLinesRaw(a.split('\n'), b.split('\n'));
```

| `i` | `diffs[i][0]` | `diffs[i][1]` |
| --: | ------------: | :------------------- |
| `0` | `-1` | `'non-empty string'` |
| `1` | `1` | `''` |

Which you might format as follows:

```diff
- Expected - 1
+ Received + 1

- non-empty string
+
```

### Example of splitLines0 function

For edge case behavior like the `diffLinesUnified` function, you might define a `splitLines0` function, which given an empty string, returns `[]` an empty array:

```js
export const splitLines0 = string =>
string.length === 0 ? [] : string.split('\n');
```

```js
import {diffLinesRaw} from 'jest-diff';

const a = '';
const b = 'line 1\nline 2\nline 3';

const diffs = diffLinesRaw(a.split('\n'), b.split('\n'));
```

| `i` | `diffs[i][0]` | `diffs[i][1]` |
| --: | ------------: | :------------ |
| `0` | `1` | `'line 1'` |
| `1` | `1` | `'line 2'` |
| `2` | `1` | `'line 3'` |

Which you might format as follows:

```diff
- Expected - 0
+ Received + 3

+ line 1
+ line 2
+ line 3
```

In 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.

## Options

The default options are for the report when an assertion fails from the `expect` package used by Jest.
Expand Down
160 changes: 153 additions & 7 deletions packages/jest-diff/src/__tests__/diff.test.ts
Expand Up @@ -10,6 +10,7 @@ import stripAnsi from 'strip-ansi';
import {alignedAnsiStyleSerializer} from '@jest/test-utils';

import diff from '../';
import {diffLinesUnified, diffLinesUnified2} from '../diffLines';
import {noColor} from '../normalizeDiffOptions';
import {diffStringsUnified} from '../printDiffs';
import {DiffOptions} from '../types';
Expand Down Expand Up @@ -728,6 +729,156 @@ describe('context', () => {
testDiffContextLines(); // (5 default)
});

describe('diffLinesUnified edge cases', () => {
test('a empty string b empty string', () => {
const a = '';
const b = '';

const received = diffLinesUnified(a.split('\n'), b.split('\n'), optionsBe);
const expected = '';

expect(received).toBe(expected);
});

test('a empty string b one line', () => {
const a = '';
const b = 'line 1';

const received = diffLinesUnified(a.split('\n'), b.split('\n'), optionsBe);
const expected = '+ line 1';

expect(received).toBe(expected);
});

test('a multiple lines b empty string', () => {
const a = 'line 1\n\nline 3';
const b = '';

const received = diffLinesUnified(a.split('\n'), b.split('\n'), optionsBe);
const expected = '- line 1\n-\n- line 3';

expect(received).toBe(expected);
});

test('a one line b multiple lines', () => {
const a = 'line 2';
const b = 'line 1\nline 2\nline 3';

const received = diffLinesUnified(a.split('\n'), b.split('\n'), optionsBe);
const expected = '+ line 1\n line 2\n+ line 3';

expect(received).toBe(expected);
});
});

describe('diffLinesUnified2 edge cases', () => {
test('a empty string b empty string', () => {
const a = '';
const b = '';

const received = diffLinesUnified2(
a.split('\n'),
b.split('\n'),
a.split('\n'),
b.split('\n'),
optionsBe,
);
const expected = '';

expect(received).toBe(expected);
});

test('a empty string b one line', () => {
const a = '';
const b = 'line 1';

const received = diffLinesUnified2(
a.split('\n'),
b.split('\n'),
a.split('\n'),
b.split('\n'),
optionsBe,
);
const expected = '+ line 1';

expect(received).toBe(expected);
});

test('a multiple lines b empty string', () => {
const a = 'line 1\n\nline 3';
const b = '';

const received = diffLinesUnified2(
a.split('\n'),
b.split('\n'),
a.split('\n'),
b.split('\n'),
optionsBe,
);
const expected = '- line 1\n-\n- line 3';

expect(received).toBe(expected);
});

test('a one line b multiple lines', () => {
const aDisplay = 'LINE 2';
const bDisplay = 'Line 1\nLine 2\nLine 3';
const aCompare = aDisplay.toLowerCase();
const bCompare = bDisplay.toLowerCase();

const received = diffLinesUnified2(
aDisplay.split('\n'),
bDisplay.split('\n'),
aCompare.split('\n'),
bCompare.split('\n'),
optionsBe,
);
const expected = '+ Line 1\n Line 2\n+ Line 3';

expect(received).toBe(expected);
});

describe('lengths not equal', () => {
// Fall back to diff of display lines.

test('a', () => {
const aDisplay = 'MiXeD cAsE';
const bDisplay = 'Mixed case\nUPPER CASE';
const aCompare = aDisplay.toLowerCase() + '\nlower case';
const bCompare = bDisplay.toLowerCase();

const received = diffLinesUnified2(
aDisplay.split('\n'),
bDisplay.split('\n'),
aCompare.split('\n'),
bCompare.split('\n'),
optionsBe,
);
const expected = '- MiXeD cAsE\n+ Mixed case\n+ UPPER CASE';

expect(received).toBe(expected);
});

test('b', () => {
const aDisplay = '{\n "key": "value",\n}';
const bDisplay = '{\n}';
const aCompare = '{\n"key": "value",\n}';
const bCompare = '{}';

const expected = ' {\n- "key": "value",\n }';
const received = diffLinesUnified2(
aDisplay.split('\n'),
bDisplay.split('\n'),
aCompare.split('\n'),
bCompare.split('\n'),
optionsBe,
);

expect(received).toBe(expected);
});
});
});

describe('diffStringsUnified edge cases', () => {
test('empty both a and b', () => {
const a = '';
Expand Down Expand Up @@ -928,15 +1079,10 @@ describe('options', () => {
});
});

describe('firstOrLastEmptyLineReplacement', () => {
const noColor = (string: string) => string;
describe('emptyFirstOrLastLinePlaceholder default empty string', () => {
const options = {
aColor: noColor,
bColor: noColor,
...optionsBe,
changeColor: noColor,
commonColor: noColor,
firstOrLastEmptyLineReplacement: '',
omitAnnotationLines: true,
};

const aEmpty = '\ncommon\nchanged from\n';
Expand Down
20 changes: 19 additions & 1 deletion packages/jest-diff/src/diffLines.ts
Expand Up @@ -11,13 +11,22 @@ import {normalizeDiffOptions} from './normalizeDiffOptions';
import {printDiffLines} from './printDiffs';
import {DiffOptions} from './types';

const isEmptyString = (lines: Array<string>) =>
lines.length === 1 && lines[0].length === 0;

// Compare two arrays of strings line-by-line. Format as comparison lines.
export const diffLinesUnified = (
aLines: Array<string>,
bLines: Array<string>,
options?: DiffOptions,
): string =>
printDiffLines(diffLinesRaw(aLines, bLines), normalizeDiffOptions(options));
printDiffLines(
diffLinesRaw(
isEmptyString(aLines) ? [] : aLines,
isEmptyString(bLines) ? [] : bLines,
),
normalizeDiffOptions(options),
);

// Given two pairs of arrays of strings:
// Compare the pair of comparison arrays line-by-line.
Expand All @@ -29,6 +38,15 @@ export const diffLinesUnified2 = (
bLinesCompare: Array<string>,
options?: DiffOptions,
): string => {
if (isEmptyString(aLinesDisplay) && isEmptyString(aLinesCompare)) {
aLinesDisplay = [];
aLinesCompare = [];
}
if (isEmptyString(bLinesDisplay) && isEmptyString(bLinesCompare)) {
bLinesDisplay = [];
bLinesCompare = [];
}

if (
aLinesDisplay.length !== aLinesCompare.length ||
bLinesDisplay.length !== bLinesCompare.length
Expand Down