Skip to content

Commit

Permalink
Feature: toEqualIgnoringWhitespace. Check if two strings are equal ir…
Browse files Browse the repository at this point in the history
…respective of white-spaces and provide hints if not (#266)
  • Loading branch information
ppiyush13 authored Oct 18, 2021
1 parent 8505445 commit 8977bca
Show file tree
Hide file tree
Showing 11 changed files with 363 additions and 0 deletions.
23 changes: 23 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ If you've come here to help contribute - Thanks! Take a look at the [contributin
- [.toInclude(substring)](#toincludesubstring)
- [.toIncludeRepeated(substring, times)](#toincluderepeatedsubstring-times)
- [.toIncludeMultiple([substring])](#toincludemultiplesubstring)
- [.toEqualIgnoringWhitespace(string)](#toequalignoringwhitespacestring)
- [Symbol](#symbol)
- [.toBeSymbol()](#tobesymbol)
- [LICENSE](#license)
Expand Down Expand Up @@ -1088,6 +1089,28 @@ test('passes when value includes all substrings', () => {
});
```
#### .toEqualIgnoringWhitespace(string)
Use `.toEqualIgnoringWhitespace` when checking if a `String` is equal to another `String` ignoring white-space.
```js
test('passes if strings are equal ignoring white-space', () => {
expect('hello world').toEqualIgnoringWhitespace(`
hello
world
`);
expect('SELECT * FROM TABLE WHERE CONDITION').toEqualIgnoringWhitespace(`
SELECT * FROM TABLE
WHERE CONDITION
`);
expect('.class { cssRule: value }').not.toEqualIgnoringWhitespace(`
#id {
cssRule: value
}
`);
});
```
### Symbol
#### .toBeSymbol()
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
},
"dependencies": {
"expect": "^26.6.2",
"jest-diff": "^27.2.5",
"jest-get-type": "^27.0.6",
"jest-matcher-utils": "^27.2.4"
},
Expand Down
1 change: 1 addition & 0 deletions src/matchers/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,3 +65,4 @@ export { toSatisfyAll } from './toSatisfyAll';
export { toSatisfyAny } from './toSatisfyAny';
export { toStartWith } from './toStartWith';
export { toThrowWithMessage } from './toThrowWithMessage';
export { toEqualIgnoringWhitespace } from './toEqualIgnoringWhitespace';
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`.toEqualIgnoringWhitespace should not pass if strings are not equal, ignoring white-space 1`] = `
"<dim>expect(</><red>received</><dim>).not.toEqualIgnoringWhitespace(</><green>expected</><dim>)</>
Expected values to not be equal while ignoring white-space (using ===):
Expected: not <green></>
<green> .class { </>
<green> cssRule: value; </>
<green> }</>
<green> </>
"
`;
exports[`.toEqualIgnoringWhitespace should pass if strings are equal ignoring white-space 1`] = `
"<dim>expect(</><red>received</><dim>).toEqualIgnoringWhitespace(</><green>expected</><dim>)</>
Expected values to be equal while ignoring white-space (using ===):
Expected:
<inverse><green>#id</></> <green>{</> <green>cssRule:</> <green>value;</> <green>}</>
Received:
<inverse><red>.class</></> <red>{</> <red>cssRule:</> <red>value;</> <red>}</>"
`;
26 changes: 26 additions & 0 deletions src/matchers/toEqualIgnoringWhitespace/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { EXPECTED_COLOR, matcherHint } from 'jest-matcher-utils';
import predicate from './predicate';
import { printExpected, printReceived } from './print-util';

const passMessage = expected => () =>
matcherHint('.not.toEqualIgnoringWhitespace') +
'\n\n' +
'Expected values to not be equal while ignoring white-space (using ===):\n' +
`Expected: not ${EXPECTED_COLOR(expected)}\n\n`;

const failMessage = diff => () =>
matcherHint('.toEqualIgnoringWhitespace') +
'\n\n' +
'Expected values to be equal while ignoring white-space (using ===):\n' +
`Expected:\n ${printExpected(diff)}\n\n` +
`Received:\n ${printReceived(diff)}`;

export function toEqualIgnoringWhitespace(received, expected) {
const { pass, diff } = predicate(received, expected);

return {
pass: pass,
message: pass ? passMessage(expected) : failMessage(diff),
actual: received,
};
}
73 changes: 73 additions & 0 deletions src/matchers/toEqualIgnoringWhitespace/index.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import * as matcher from '.';

expect.extend(matcher);

describe('.toEqualIgnoringWhitespace', () => {
it('should pass if strings are equal ignoring white-space', () => {
expect('SELECT * from TABLE WHERE CONDITION = "5"').toEqualIgnoringWhitespace(`
SELECT * from TABLE
WHERE CONDITION = "5"
`);

expect('SELECT * from TABLE WHERE CONDITION = "5"').toEqualIgnoringWhitespace(`
SELECT
*
from
TABLE
WHERE
CONDITION="5"
`);

expect(`
diff.forEach((diffObject) => {
if(diffObject.value.trim())
return;
diffObject.added = diffObject.removed = undefined;
});
`).toEqualIgnoringWhitespace(`
diff.forEach((diffObject) => {
if(diffObject.value.trim()) return;
diffObject.added = diffObject.removed = undefined;
});
`);

expect(() =>
expect('.class { cssRule: value; }').toEqualIgnoringWhitespace('#id { cssRule: value; }'),
).toThrowErrorMatchingSnapshot();
});

it('should not pass if strings are not equal, ignoring white-space', () => {
expect('SELECT * from TABLE WHERE CONDITION = "5"').not.toEqualIgnoringWhitespace(`
WHERE CONDITION = "5"
SELECT * from TABLE
`);

expect('SELECT * from TABLE WHERE CONDITION = "5"').not.toEqualIgnoringWhitespace(`
SELECT * from TABLE
WHERE CONDITION = "555"
`);

expect('SELECT * from TABLE WHERE CONDITION = "5"').not.toEqualIgnoringWhitespace(`
WHERE CONDITION = "5"
`);

expect('SELECT * from TABLE WHERE CONDITION = "5"').not.toEqualIgnoringWhitespace(`
select * from table
where condition="5"
`);

expect(`
import React from 'react';
`).not.toEqualIgnoringWhitespace(`
import {Component} from 'react';
`);

expect(() =>
expect('.class { cssRule: value; }').not.toEqualIgnoringWhitespace(`
.class {
cssRule: value;
}
`),
).toThrowErrorMatchingSnapshot();
});
});
22 changes: 22 additions & 0 deletions src/matchers/toEqualIgnoringWhitespace/predicate.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { diffStringsRaw, DIFF_EQUAL } from 'jest-diff';

const removeWhitespace = str => str.trim().replace(/\s+/g, '');

export default (received, expected) => {
/* calculate diff of received w.r.t expected string */
const diff = diffStringsRaw(expected, received);

/* mark every diff result object with value of white-space as DIFF_EQUAL */
diff.forEach(diffObject => {
if (diffObject[1].trim()) return;
diffObject[0] = DIFF_EQUAL;
});

/* determine whether strings are equal after removing white-space */
const pass = removeWhitespace(received) === removeWhitespace(expected);

return {
diff,
pass,
};
};
55 changes: 55 additions & 0 deletions src/matchers/toEqualIgnoringWhitespace/predicate.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import predicate from './predicate';

describe('toEqualIgnoringWhitespace predicate', () => {
it('should generate correct pass and diff for inputs without white-space', () => {
const { pass, diff } = predicate('inputs A', 'inputs B');

expect(pass).toEqual(false);
expect(diff).toEqual([
{
0: 0,
1: 'inputs ',
},
{
0: -1,
1: 'B',
},
{
0: 1,
1: 'A',
},
]);
});

it('should generate correct pass and diff for inputs with white-space', () => {
const { pass, diff } = predicate(' inputs A', 'input B ');

expect(pass).toEqual(false);
expect(diff).toEqual([
{
0: 0,
1: ' ',
},
{
0: 0,
1: 'input',
},
{
0: 1,
1: 's',
},
{
0: 0,
1: ' ',
},
{
0: -1,
1: 'B ',
},
{
0: 1,
1: 'A',
},
]);
});
});
61 changes: 61 additions & 0 deletions src/matchers/toEqualIgnoringWhitespace/print-util.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { DIFF_DELETE, DIFF_EQUAL, DIFF_INSERT } from 'jest-diff';
import { EXPECTED_COLOR, INVERTED_COLOR, RECEIVED_COLOR } from 'jest-matcher-utils';

export const tokenize = str => {
const isWhitespace = char => /\s/.test(char);
const tokens = [];
let idx = 0;
let token;

while (idx < str.length) {
const char = str.charAt(idx);
const isCurrentCharWhitespace = isWhitespace(char);

if (token) {
if (token.isWhitespace === isCurrentCharWhitespace) {
token.value += char;
} else {
tokens.push(token);
token = undefined;
continue;
}
} else {
token = {
value: char,
isWhitespace: isCurrentCharWhitespace,
};
}

idx += 1;
}

/* push last token */
tokens.push(token);

return tokens;
};

const colorTokens = (str, color) => {
const tokens = tokenize(str);
return tokens.reduce((acc, { value, isWhitespace }) => acc + (isWhitespace ? value : color(value)), '');
};

export const printExpected = diff =>
diff.reduce((acc, diffObject) => {
const operation = diffObject[0];
const value = diffObject[1];

if (operation === DIFF_EQUAL) return acc + colorTokens(value, EXPECTED_COLOR);
if (operation === DIFF_DELETE) return acc + colorTokens(value, str => INVERTED_COLOR(EXPECTED_COLOR(str)));
return acc;
}, '');

export const printReceived = diff =>
diff.reduce((acc, diffObject) => {
const operation = diffObject[0];
const value = diffObject[1];

if (operation === DIFF_EQUAL) return acc + colorTokens(value, RECEIVED_COLOR);
if (operation === DIFF_INSERT) return acc + colorTokens(value, str => INVERTED_COLOR(RECEIVED_COLOR(str)));
return acc;
}, '');
62 changes: 62 additions & 0 deletions src/matchers/toEqualIgnoringWhitespace/print-util.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { tokenize } from './print-util';

describe('print-util module', () => {
it('should tokenize given string', () => {
const tokens = tokenize('This function \n creates tokens \t keeping white-space intact.');

expect(tokens).toEqual([
{
isWhitespace: false,
value: 'This',
},
{
isWhitespace: true,
value: ' ',
},
{
isWhitespace: false,
value: 'function',
},
{
isWhitespace: true,
value: ' \n ',
},
{
isWhitespace: false,
value: 'creates',
},
{
isWhitespace: true,
value: ' ',
},
{
isWhitespace: false,
value: 'tokens',
},
{
isWhitespace: true,
value: ' \t ',
},
{
isWhitespace: false,
value: 'keeping',
},
{
isWhitespace: true,
value: ' ',
},
{
isWhitespace: false,
value: 'white-space',
},
{
isWhitespace: true,
value: ' ',
},
{
isWhitespace: false,
value: 'intact.',
},
]);
});
});
Loading

0 comments on commit 8977bca

Please sign in to comment.