Skip to content

Commit

Permalink
jest-diff: Remove the need to export splitLines0 function (#9151)
Browse files Browse the repository at this point in the history
* jest-diff: Remove the need to export splitLines0 function

* Edit README.md

* Update CHANGELOG.md

* Fix my favorite markdown lint mistake
  • Loading branch information
pedrottimark committed Nov 8, 2019
1 parent 85789b5 commit e00ec07
Show file tree
Hide file tree
Showing 7 changed files with 254 additions and 73 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -61,6 +61,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

0 comments on commit e00ec07

Please sign in to comment.