Skip to content

Commit

Permalink
add .toBeJsonMatching(expectation) matcher
Browse files Browse the repository at this point in the history
  • Loading branch information
Belema committed Feb 18, 2023
1 parent 5e66791 commit b6f5fcc
Show file tree
Hide file tree
Showing 10 changed files with 501 additions and 8 deletions.
1 change: 1 addition & 0 deletions src/matchers/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export { toBeFrozen } from './toBeFrozen';
export { toBeFunction } from './toBeFunction';
export { toBeHexadecimal } from './toBeHexadecimal';
export { toBeInteger } from './toBeInteger';
export { toBeJsonMatching } from './toBeJsonMatching';
export { toBeNaN } from './toBeNaN';
export { toBeNegative } from './toBeNegative';
export { toBeNil } from './toBeNil';
Expand Down
22 changes: 22 additions & 0 deletions src/matchers/toBeJsonMatching.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { matchesObject, tryParseJSON } from '../utils';

export function toBeJsonMatching(actual, expected) {
const { printExpected, printReceived, matcherHint } = this.utils;

const passMessage =
`${matcherHint('.not.toBeJsonMatching')}\n\n` +
`Expected input to not be a JSON string containing:\n ${printExpected(expected)}\n` +
`Received:\n ${printReceived(typeof tryParseJSON(actual) !== 'undefined' ? tryParseJSON(actual) : actual)}`;

const failMessage =
`${matcherHint('.toBeJsonMatching')}\n\n` +
`Expected input to be a JSON string containing:\n ${printExpected(expected)}\n` +
`Received:\n ${printReceived(typeof tryParseJSON(actual) !== 'undefined' ? tryParseJSON(actual) : actual)}`;

const pass =
typeof actual === 'string' &&
typeof tryParseJSON(actual) !== 'undefined' &&
matchesObject(this.equals, tryParseJSON(actual), expected);

return { pass, message: () => (pass ? passMessage : failMessage) };
}
9 changes: 2 additions & 7 deletions src/matchers/toPartiallyContain.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,9 @@
import { containsEntry } from '../utils';
import { partiallyContains } from '../utils';

export function toPartiallyContain(actual, expected) {
const { printReceived, printExpected, matcherHint } = this.utils;

const pass =
Array.isArray(actual) &&
Array.isArray([expected]) &&
[expected].every(partial =>
actual.some(value => Object.entries(partial).every(entry => containsEntry(this.equals, value, entry))),
);
const pass = partiallyContains(this.equals, actual, [expected]);

return {
pass,
Expand Down
47 changes: 47 additions & 0 deletions src/utils/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,50 @@ export const isJestMockOrSpy = value => {

export const containsEntry = (equals, obj, [key, value]) =>
obj.hasOwnProperty && Object.prototype.hasOwnProperty.call(obj, key) && equals(obj[key], value);

export const partiallyContains = (equals, actual, expected) =>
Array.isArray(actual) &&
Array.isArray(expected) &&
expected.every(partial =>
actual.some(value => {
if (typeof partial !== 'object' || partial === null) {
return equals(value, partial);
}
if (Array.isArray(partial)) {
return partiallyContains(equals, value, partial);
}
return Object.entries(partial).every(entry => containsEntry(equals, value, entry));
}),
);

export const matchesObject = (equals, actual, expected) => {
if (equals(actual, expected)) {
return true;
}
if (Array.isArray(actual) || Array.isArray(expected)) {
return partiallyContains(equals, actual, expected);
}
if (typeof actual === 'object' && typeof expected === 'object' && expected !== null) {
return Object.getOwnPropertyNames(expected).every(name => {
if (equals(actual[name], expected[name])) {
return true;
}
if (Array.isArray(actual[name]) || Array.isArray(expected[name])) {
return partiallyContains(equals, actual[name], expected[name]);
}
if (typeof actual[name] === 'object' && typeof expected[name] === 'object' && expected[name] !== null) {
return matchesObject(equals, actual[name], expected[name]);
}
return false;
});
}
return false;
};

export const tryParseJSON = input => {
try {
return JSON.parse(input);
} catch {
return undefined;
}
};
298 changes: 298 additions & 0 deletions test/matchers/__snapshots__/toBeJsonMatching.test.js.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,298 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`.not.toBeJsonMatching fails when given JSON string matches expectation 1`] = `
"<dim>expect(</intensity><red>received</color><dim>).not.toBeJsonMatching(</intensity><green>expected</color><dim>)</intensity>
Expected input to not be a JSON string containing:
<green>{}</color>
Received:
<red>{"a": 42, "b": "foo", "c": {"d": "hello", "e": "world"}, "f": [7, {"g": "bar", "h": "baz"}]}</color>"
`;

exports[`.not.toBeJsonMatching fails when given JSON string matches expectation 2`] = `
"<dim>expect(</intensity><red>received</color><dim>).not.toBeJsonMatching(</intensity><green>expected</color><dim>)</intensity>
Expected input to not be a JSON string containing:
<green>{"a": 42}</color>
Received:
<red>{"a": 42, "b": "foo", "c": {"d": "hello", "e": "world"}, "f": [7, {"g": "bar", "h": "baz"}]}</color>"
`;

exports[`.not.toBeJsonMatching fails when given JSON string matches expectation 3`] = `
"<dim>expect(</intensity><red>received</color><dim>).not.toBeJsonMatching(</intensity><green>expected</color><dim>)</intensity>
Expected input to not be a JSON string containing:
<green>{"a": 42, "b": "foo"}</color>
Received:
<red>{"a": 42, "b": "foo", "c": {"d": "hello", "e": "world"}, "f": [7, {"g": "bar", "h": "baz"}]}</color>"
`;

exports[`.not.toBeJsonMatching fails when given JSON string matches expectation 4`] = `
"<dim>expect(</intensity><red>received</color><dim>).not.toBeJsonMatching(</intensity><green>expected</color><dim>)</intensity>
Expected input to not be a JSON string containing:
<green>{"a": 42, "b": "foo", "c": {}}</color>
Received:
<red>{"a": 42, "b": "foo", "c": {"d": "hello", "e": "world"}, "f": [7, {"g": "bar", "h": "baz"}]}</color>"
`;

exports[`.not.toBeJsonMatching fails when given JSON string matches expectation 5`] = `
"<dim>expect(</intensity><red>received</color><dim>).not.toBeJsonMatching(</intensity><green>expected</color><dim>)</intensity>
Expected input to not be a JSON string containing:
<green>{"a": 42, "b": "foo", "c": {"d": "hello"}}</color>
Received:
<red>{"a": 42, "b": "foo", "c": {"d": "hello", "e": "world"}, "f": [7, {"g": "bar", "h": "baz"}]}</color>"
`;

exports[`.not.toBeJsonMatching fails when given JSON string matches expectation 6`] = `
"<dim>expect(</intensity><red>received</color><dim>).not.toBeJsonMatching(</intensity><green>expected</color><dim>)</intensity>
Expected input to not be a JSON string containing:
<green>{"a": 42, "b": "foo", "c": {"d": "hello", "e": "world"}}</color>
Received:
<red>{"a": 42, "b": "foo", "c": {"d": "hello", "e": "world"}, "f": [7, {"g": "bar", "h": "baz"}]}</color>"
`;

exports[`.not.toBeJsonMatching fails when given JSON string matches expectation 7`] = `
"<dim>expect(</intensity><red>received</color><dim>).not.toBeJsonMatching(</intensity><green>expected</color><dim>)</intensity>
Expected input to not be a JSON string containing:
<green>{"a": 42, "b": "foo", "c": {"d": "hello", "e": "world"}, "f": []}</color>
Received:
<red>{"a": 42, "b": "foo", "c": {"d": "hello", "e": "world"}, "f": [7, {"g": "bar", "h": "baz"}]}</color>"
`;

exports[`.not.toBeJsonMatching fails when given JSON string matches expectation 8`] = `
"<dim>expect(</intensity><red>received</color><dim>).not.toBeJsonMatching(</intensity><green>expected</color><dim>)</intensity>
Expected input to not be a JSON string containing:
<green>{"a": 42, "b": "foo", "c": {"d": "hello", "e": "world"}, "f": [7]}</color>
Received:
<red>{"a": 42, "b": "foo", "c": {"d": "hello", "e": "world"}, "f": [7, {"g": "bar", "h": "baz"}]}</color>"
`;

exports[`.not.toBeJsonMatching fails when given JSON string matches expectation 9`] = `
"<dim>expect(</intensity><red>received</color><dim>).not.toBeJsonMatching(</intensity><green>expected</color><dim>)</intensity>
Expected input to not be a JSON string containing:
<green>{"a": 42, "b": "foo", "c": {"d": "hello", "e": "world"}, "f": [7, {}]}</color>
Received:
<red>{"a": 42, "b": "foo", "c": {"d": "hello", "e": "world"}, "f": [7, {"g": "bar", "h": "baz"}]}</color>"
`;

exports[`.not.toBeJsonMatching fails when given JSON string matches expectation 10`] = `
"<dim>expect(</intensity><red>received</color><dim>).not.toBeJsonMatching(</intensity><green>expected</color><dim>)</intensity>
Expected input to not be a JSON string containing:
<green>{"a": 42, "b": "foo", "c": {"d": "hello", "e": "world"}, "f": [7, {"g": "bar"}]}</color>
Received:
<red>{"a": 42, "b": "foo", "c": {"d": "hello", "e": "world"}, "f": [7, {"g": "bar", "h": "baz"}]}</color>"
`;

exports[`.not.toBeJsonMatching fails when given JSON string matches expectation 11`] = `
"<dim>expect(</intensity><red>received</color><dim>).not.toBeJsonMatching(</intensity><green>expected</color><dim>)</intensity>
Expected input to not be a JSON string containing:
<green>{"a": 42, "b": "foo", "c": {"d": "hello", "e": "world"}, "f": [7, {"g": "bar", "h": "baz"}]}</color>
Received:
<red>{"a": 42, "b": "foo", "c": {"d": "hello", "e": "world"}, "f": [7, {"g": "bar", "h": "baz"}]}</color>"
`;

exports[`.toBeJsonMatching fails when given JSON string does not match expectation 1`] = `
"<dim>expect(</intensity><red>received</color><dim>).toBeJsonMatching(</intensity><green>expected</color><dim>)</intensity>
Expected input to be a JSON string containing:
<green>null</color>
Received:
<red>{"a": 42, "b": "foo", "c": {"d": "hello", "e": "world"}, "f": [7, {"g": "bar", "h": "baz"}]}</color>"
`;

exports[`.toBeJsonMatching fails when given JSON string does not match expectation 2`] = `
"<dim>expect(</intensity><red>received</color><dim>).toBeJsonMatching(</intensity><green>expected</color><dim>)</intensity>
Expected input to be a JSON string containing:
<green>[]</color>
Received:
<red>{"a": 42, "b": "foo", "c": {"d": "hello", "e": "world"}, "f": [7, {"g": "bar", "h": "baz"}]}</color>"
`;

exports[`.toBeJsonMatching fails when given JSON string does not match expectation 3`] = `
"<dim>expect(</intensity><red>received</color><dim>).toBeJsonMatching(</intensity><green>expected</color><dim>)</intensity>
Expected input to be a JSON string containing:
<green>{"a": "42"}</color>
Received:
<red>{"a": 42, "b": "foo", "c": {"d": "hello", "e": "world"}, "f": [7, {"g": "bar", "h": "baz"}]}</color>"
`;

exports[`.toBeJsonMatching fails when given JSON string does not match expectation 4`] = `
"<dim>expect(</intensity><red>received</color><dim>).toBeJsonMatching(</intensity><green>expected</color><dim>)</intensity>
Expected input to be a JSON string containing:
<green>{"a": 41}</color>
Received:
<red>{"a": 42, "b": "foo", "c": {"d": "hello", "e": "world"}, "f": [7, {"g": "bar", "h": "baz"}]}</color>"
`;

exports[`.toBeJsonMatching fails when given JSON string does not match expectation 5`] = `
"<dim>expect(</intensity><red>received</color><dim>).toBeJsonMatching(</intensity><green>expected</color><dim>)</intensity>
Expected input to be a JSON string containing:
<green>{"a": []}</color>
Received:
<red>{"a": 42, "b": "foo", "c": {"d": "hello", "e": "world"}, "f": [7, {"g": "bar", "h": "baz"}]}</color>"
`;

exports[`.toBeJsonMatching fails when given JSON string does not match expectation 6`] = `
"<dim>expect(</intensity><red>received</color><dim>).toBeJsonMatching(</intensity><green>expected</color><dim>)</intensity>
Expected input to be a JSON string containing:
<green>{"a": {}}</color>
Received:
<red>{"a": 42, "b": "foo", "c": {"d": "hello", "e": "world"}, "f": [7, {"g": "bar", "h": "baz"}]}</color>"
`;

exports[`.toBeJsonMatching fails when given JSON string does not match expectation 7`] = `
"<dim>expect(</intensity><red>received</color><dim>).toBeJsonMatching(</intensity><green>expected</color><dim>)</intensity>
Expected input to be a JSON string containing:
<green>{"a": null}</color>
Received:
<red>{"a": 42, "b": "foo", "c": {"d": "hello", "e": "world"}, "f": [7, {"g": "bar", "h": "baz"}]}</color>"
`;

exports[`.toBeJsonMatching fails when given JSON string does not match expectation 8`] = `
"<dim>expect(</intensity><red>received</color><dim>).toBeJsonMatching(</intensity><green>expected</color><dim>)</intensity>
Expected input to be a JSON string containing:
<green>{"a": 42, "b": "bar"}</color>
Received:
<red>{"a": 42, "b": "foo", "c": {"d": "hello", "e": "world"}, "f": [7, {"g": "bar", "h": "baz"}]}</color>"
`;

exports[`.toBeJsonMatching fails when given JSON string does not match expectation 9`] = `
"<dim>expect(</intensity><red>received</color><dim>).toBeJsonMatching(</intensity><green>expected</color><dim>)</intensity>
Expected input to be a JSON string containing:
<green>{"a": 42, "b": "foo", "c": 7}</color>
Received:
<red>{"a": 42, "b": "foo", "c": {"d": "hello", "e": "world"}, "f": [7, {"g": "bar", "h": "baz"}]}</color>"
`;

exports[`.toBeJsonMatching fails when given JSON string does not match expectation 10`] = `
"<dim>expect(</intensity><red>received</color><dim>).toBeJsonMatching(</intensity><green>expected</color><dim>)</intensity>
Expected input to be a JSON string containing:
<green>{"a": 42, "b": "foo", "c": []}</color>
Received:
<red>{"a": 42, "b": "foo", "c": {"d": "hello", "e": "world"}, "f": [7, {"g": "bar", "h": "baz"}]}</color>"
`;

exports[`.toBeJsonMatching fails when given JSON string does not match expectation 11`] = `
"<dim>expect(</intensity><red>received</color><dim>).toBeJsonMatching(</intensity><green>expected</color><dim>)</intensity>
Expected input to be a JSON string containing:
<green>{"a": 42, "b": "foo", "c": {"d": "bonjour"}}</color>
Received:
<red>{"a": 42, "b": "foo", "c": {"d": "hello", "e": "world"}, "f": [7, {"g": "bar", "h": "baz"}]}</color>"
`;

exports[`.toBeJsonMatching fails when given JSON string does not match expectation 12`] = `
"<dim>expect(</intensity><red>received</color><dim>).toBeJsonMatching(</intensity><green>expected</color><dim>)</intensity>
Expected input to be a JSON string containing:
<green>{"a": 42, "b": "foo", "c": {"d": "hello", "e": "dolly"}}</color>
Received:
<red>{"a": 42, "b": "foo", "c": {"d": "hello", "e": "world"}, "f": [7, {"g": "bar", "h": "baz"}]}</color>"
`;

exports[`.toBeJsonMatching fails when given JSON string does not match expectation 13`] = `
"<dim>expect(</intensity><red>received</color><dim>).toBeJsonMatching(</intensity><green>expected</color><dim>)</intensity>
Expected input to be a JSON string containing:
<green>{"a": 42, "b": "foo", "c": {"d": "hello", "e": 7}}</color>
Received:
<red>{"a": 42, "b": "foo", "c": {"d": "hello", "e": "world"}, "f": [7, {"g": "bar", "h": "baz"}]}</color>"
`;

exports[`.toBeJsonMatching fails when given JSON string does not match expectation 14`] = `
"<dim>expect(</intensity><red>received</color><dim>).toBeJsonMatching(</intensity><green>expected</color><dim>)</intensity>
Expected input to be a JSON string containing:
<green>{"a": 42, "b": "foo", "c": {"d": "hello", "e": []}}</color>
Received:
<red>{"a": 42, "b": "foo", "c": {"d": "hello", "e": "world"}, "f": [7, {"g": "bar", "h": "baz"}]}</color>"
`;

exports[`.toBeJsonMatching fails when given JSON string does not match expectation 15`] = `
"<dim>expect(</intensity><red>received</color><dim>).toBeJsonMatching(</intensity><green>expected</color><dim>)</intensity>
Expected input to be a JSON string containing:
<green>{"a": 42, "b": "foo", "c": {"d": "hello", "e": {}}}</color>
Received:
<red>{"a": 42, "b": "foo", "c": {"d": "hello", "e": "world"}, "f": [7, {"g": "bar", "h": "baz"}]}</color>"
`;

exports[`.toBeJsonMatching fails when given JSON string does not match expectation 16`] = `
"<dim>expect(</intensity><red>received</color><dim>).toBeJsonMatching(</intensity><green>expected</color><dim>)</intensity>
Expected input to be a JSON string containing:
<green>{"a": 42, "b": "foo", "c": {"d": "hello", "e": null}}</color>
Received:
<red>{"a": 42, "b": "foo", "c": {"d": "hello", "e": "world"}, "f": [7, {"g": "bar", "h": "baz"}]}</color>"
`;

exports[`.toBeJsonMatching fails when given JSON string does not match expectation 17`] = `
"<dim>expect(</intensity><red>received</color><dim>).toBeJsonMatching(</intensity><green>expected</color><dim>)</intensity>
Expected input to be a JSON string containing:
<green>{"a": 42, "b": "foo", "c": {"d": "hello", "e": "world"}, "f": 7}</color>
Received:
<red>{"a": 42, "b": "foo", "c": {"d": "hello", "e": "world"}, "f": [7, {"g": "bar", "h": "baz"}]}</color>"
`;

exports[`.toBeJsonMatching fails when given JSON string does not match expectation 18`] = `
"<dim>expect(</intensity><red>received</color><dim>).toBeJsonMatching(</intensity><green>expected</color><dim>)</intensity>
Expected input to be a JSON string containing:
<green>{"a": 42, "b": "foo", "c": {"d": "hello", "e": "world"}, "f": {}}</color>
Received:
<red>{"a": 42, "b": "foo", "c": {"d": "hello", "e": "world"}, "f": [7, {"g": "bar", "h": "baz"}]}</color>"
`;

exports[`.toBeJsonMatching fails when given JSON string does not match expectation 19`] = `
"<dim>expect(</intensity><red>received</color><dim>).toBeJsonMatching(</intensity><green>expected</color><dim>)</intensity>
Expected input to be a JSON string containing:
<green>{"a": 42, "b": "foo", "c": {"d": "hello", "e": "world"}, "f": [8]}</color>
Received:
<red>{"a": 42, "b": "foo", "c": {"d": "hello", "e": "world"}, "f": [7, {"g": "bar", "h": "baz"}]}</color>"
`;

exports[`.toBeJsonMatching fails when given JSON string does not match expectation 20`] = `
"<dim>expect(</intensity><red>received</color><dim>).toBeJsonMatching(</intensity><green>expected</color><dim>)</intensity>
Expected input to be a JSON string containing:
<green>{"a": 42, "b": "foo", "c": {"d": "hello", "e": "world"}, "f": [7, []]}</color>
Received:
<red>{"a": 42, "b": "foo", "c": {"d": "hello", "e": "world"}, "f": [7, {"g": "bar", "h": "baz"}]}</color>"
`;

exports[`.toBeJsonMatching fails when given JSON string does not match expectation 21`] = `
"<dim>expect(</intensity><red>received</color><dim>).toBeJsonMatching(</intensity><green>expected</color><dim>)</intensity>
Expected input to be a JSON string containing:
<green>{"a": 42, "b": "foo", "c": {"d": "hello", "e": "world"}, "f": [7, {"g": "baz"}]}</color>
Received:
<red>{"a": 42, "b": "foo", "c": {"d": "hello", "e": "world"}, "f": [7, {"g": "bar", "h": "baz"}]}</color>"
`;

exports[`.toBeJsonMatching fails when given JSON string does not match expectation 22`] = `
"<dim>expect(</intensity><red>received</color><dim>).toBeJsonMatching(</intensity><green>expected</color><dim>)</intensity>
Expected input to be a JSON string containing:
<green>{"a": 42, "b": "foo", "c": {"d": "hello", "e": "world"}, "f": [7, {"g": "bar", "h": "baz", "i": "buzz"}]}</color>
Received:
<red>{"a": 42, "b": "foo", "c": {"d": "hello", "e": "world"}, "f": [7, {"g": "bar", "h": "baz"}]}</color>"
`;
Loading

0 comments on commit b6f5fcc

Please sign in to comment.