Reduce the size of snapshots while also encouraging atomic testing practices.
returns: jest.Mock
Props and children are represented in a uniform and logical way. Props which require further testing are highlighted within the snapshot. Components are replaced with either div
s or span
s which have data-attributes
set as the props which have been passed in.
/Bar/__mocks__/index.ts
import { createMock } from "jest-snapshot-propifier";
export const Foo = createMock({ name: "Bar" });
/Foo/index.ts
export const Foo = (props) => <Bar {...props} />;
/Foo/spec.tsx
test("With no props", () => {
expect(snapshotOf(<Foo />)).toMatchInlineSnapshot(`
<div
data-component="<Bar />"
/>
`);
});
test("With basic props", () => {
expect(snapshotOf(<Foo string="string" number={42} />))
.toMatchInlineSnapshot(`
<div
data-component="<Bar />"
data-number={42}
data-string="string"
/>
`);
});
test("With objects as props", () => {
const muchWow = {
string: "string",
number: 42,
object: { much: "wow" },
};
expect(snapshotOf(<Foo object={muchWow} />)).toMatchInlineSnapshot(`
<div
data-component="<Bar />"
data-object="{\\"string\\":\\"string\\",\\"number\\":42,\\"object\\":{\\"much\\":\\"wow\\"}}"
/>
`);
});
test("With functions as props", () => {
expect(snapshotOf(<Foo function={() => "function"} />))
.toMatchInlineSnapshot(`
<div
data-component="<Bar />"
data-function="[! Function to test !]"
/>
`);
const mockedBar = Bar as jest.Mock;
const [[barProps]] = mockedBar.mock.calls;
expect(barProps.function()).toBe("function");
});
test("With components as props", () => {
const InnerFoo = createMock({ name: "InnerFoo" });
expect(snapshotOf(<Foo weirdFlex={<InnerFoo />} />))
.toMatchInlineSnapshot(`
<div
data-component="<Bar />"
data-weird_flex="[! Component to test !]"
/>
`);
const mockedBar = Bar as jest.Mock;
const [[barProps]] = mockedBar.mock.calls;
const WeirdFlex = () => barProps.weirdFlex;
expect(snapshotOf(<WeirdFlex />)).toMatchInlineSnapshot(`
<div
data-component="<InnerFoo />"
/>
`);
});
test("With basic children", () => {
expect(snapshotOf(<Foo>children</Foo>)).toMatchInlineSnapshot(`
<div
data-component="<Bar />"
>
children
</div>
`);
});
test("With components as children", () => {
const InnerFoo = () => <>InnerFoo</>;
expect(
snapshotOf(
<Foo>
<InnerFoo />
</Foo>
)
).toMatchInlineSnapshot(`
<div
data-component="<Bar />"
>
InnerFoo
</div>
`);
});
For cases where it would not be appropriate to use a div
/Bar/__mocks__/index.ts
import { createMock } from "jest-snapshot-propifier";
export const Foo = createMock({ name: "Bar", element: "span" });
/Foo/index.ts
export const Foo = (props) => <Bar {...props} />;
/Foo/spec.tsx
test("With no props", () => {
expect(snapshotOf(<Foo />)).toMatchInlineSnapshot(`
<span
data-component="<Bar />"
/>
`);
});
returns: ReactTestRendererJSON | ReactTestRendererJSON[]
Convenience wrapper for react-test-renderer
's snapshot generator
/Foo/index.ts
export const Foo = (props) => <Bar {...props} />;
/Foo/spec.tsx
test("With no props", () => {
expect(snapshotOf(<Foo />)).toMatchInlineSnapshot(`
<div
data-component="<Bar />"
/>
`);
});
/Foo/index.ts
export const Foo = ({ extraFoo, ...props }) => {
const [extraBar, setExtraBar] = useState();
useEffect(() => {
setExtraBar(extraFoo);
}, [extraFoo]);
return <Bar {...props} extraBar={extraBar} />;
};
Passing { flushEffects: true }
will allow useEffect
to complete before creating the snapshot:
/Foo/spec.tsx
test("With flushEffects", () => {
expect(snapshotOf(<Foo extraFoo="π" />), { flushEffects: true })
.toMatchInlineSnapshot(`
<div
data-component="<Bar />"
data-extra-foo="π"
/>
`);
});
...compared to { flushEffects: false }
:
/Foo/spec.tsx
test("Without flushEffects", () => {
expect(snapshotOf(<Foo extraFoo="π" />, { flushEffects: false }))
.toMatchInlineSnapshot(`
<div
data-component="<Bar />"
/>
`);
});
returns ReactTestRenderer
Convenience wrapper for react-test-renderer
's renderer. Options as snapshotOf
. Useful if there is a requirement to cause rerenders but need to ensure effects have been flushed on the original call.
/Foo/spec.tsx
test("With create", () => {
const foo = create(<Foo extraFoo="π₯" />, { flushEffects: true });
expect(Foo).toHaveBeenCalledTimes(1);
expect(Foo).toHaveBeenCalledWith("π₯");
act(() => {
foo.update(<Foo extraFoo="π" />);
});
expect(Foo).toHaveBeenCalledTimes(2);
expect(Foo).toHaveBeenCalledWith("π");
//or use foo.toJSON() if you just want a snapshot
});