draft-js
draft-js copied to clipboard
Advice on snapshot testing with Jest
Do you want to request a feature or report a bug?
I suppose this could be considered a feature request to make data-editor and data-offset-key attribute values deterministic via some seed.
What is the current behavior?
When testing a component which wraps an Editor using the snapshots feature in Jest, the test always fails because certain attributes appear to be randomly generated and unique per instance.
This is a sample of the output from the test failure:
- data-editor="4or1r"
- data-offset-key="9r3en-0-0">
+ data-editor="cbctb"
+ data-offset-key="3jgg4-0-0">
<div
className="public-DraftStyleDefault-block public-DraftStyleDefault-ltr"
- data-offset-key="9r3en-0-0">
+ data-offset-key="3jgg4-0-0">
<span
- data-offset-key="9r3en-0-0"
+ data-offset-key="3jgg4-0-0"
If the current behavior is a bug, please provide the steps to reproduce and if possible a minimal demo of the problem.
The test is very simple:
import React from 'react';
import renderer from 'react-test-renderer';
import { MyEditor } from '../src';
jest.mock('react-dom');
describe('MyEditor', () => {
it('renders as expected', () => {
const component = renderer.create(<MyEditor />);
const tree = component.toJSON();
expect(tree).toMatchSnapshot();
});
});
Where MyEditor wraps Editor and provides a default value for editorState.
What is the expected behavior?
I would hope to somehow still be able to use snapshot testing.
Which versions of Draft.js, and which browser / OS are affected by this issue? Did this work in previous versions of Draft.js?
I'm using Draft 0.9.1 and Jest 15.1.1
+1
@cpojer - would you mind chiming in here on whether consumers will need to ignore specific properties or if Draft itself should communicate that some properties should be ignored during snapshot testing
there isn't any way right now to ignore this. The right solution is usually to mock out whatever creates randomness, like Date.now = jest.fn(() => 42) for example. What provides these random IDs and can we mock them?
Perhaps I'm misunderstanding how react-test-renderer and/or snapshots work, but I thought the test renderer did a shallow render.
In that case, I don't know why I'm seeing internals of the Editor component. Ideally, I wouldn't care about the implementation details of Editor and only how my component was being rendered, which I thought was the point of shallow rendering.
(This is a obviously a separate question to the original issue)
No, it's not shallow, it's a normal render.
However, you can mock out lower level components like jest.mock('path/to/Editor', () => 'Editor').
What was the final solution to this problem?
Was there any solution to this?
Edit: Using what @cpojer recommended, I was able to stub out the component that was rendering DraftJS to render just the component name.
@kawikadkekahuna can you show your test code please ?
I'm not sure if this is the same problem other people are having, but I had an issue where EditorState was being passed into my editor component and within it contained a random key that would be different on every jest run.
Mocking this function solved it for me:
jest.mock('draft-js/lib/generateRandomKey', () => () => '123');
There are two issues happening here for snapshots:
- Not mocking the draftjs
Editor(which has random keys in data attributes) - Using
EditorState.createWithContent()in props, which has random key values in the object
Fortunately, both of these issues are solved with @neurosnap's solution of mocking generateRandomKey inside draftjs.
This is what @cpojer's meant by:
The right solution is usually to mock out whatever creates randomness
Just to make this crystal clear, both issues will affect the example component below (which is fixed with the solution above):
import { Editor, EditorState } from 'draft-js';
class MyEditor extends React.PureComponent {
constructor(props) {
super(props);
const initialState = EditorState.createEmpty(); // object contains random keys
this.state = {
initialState,
};
}
render() {
return <Editor editorState={this.state.initialState} />; // Editor contains random data attributes
}
}
export default Editor;
Hey guys, because this issue is still open I'm just going to write my proposal here. So I am doing integration tests with jest. I want to test our sign up endpoint which will return a json result that looks something like this:
{
ok: true,
token: randomTokenHere,
user_id: randomUserId
}
My test is something like this:
test('sign up new user', () => {
return rpap.post({
url: '/users.signup',
body: {
email: '[email protected]',
password: 'jestit',
first_name: 'Jest',
last_name: 'Jesting',
},
})
.then((result) => {
expect(result).toMatchSnapshot();
});
});
It would be cool if I can do that:
expect(result).ignore({
token,
user_id,
}).toMatchSnapshot();
Or because this can lead to mistakes it's going to be better if we can validate that it is at least there but with don't care what the value is because it's random.
What I have to do now is delete the values from the result before testing it. That's fine but if jest can do this for me in a nice syntax and even validate it in a non strict manner it's going to be epic.
Essentially the snapshot files will have only the deterministic values inside.
What are your thoughts about this?
I like that! Seems like a great use case for a custom matcher
Is the problem solved?
I also had to do this (entity map was still filled with uuids):
jest.mock("draft-js/lib/uuid", () => {
let idUUID = 0;
return () => (++idUUID).toString();
});
In Jest 23 we added a snapshot property matcher for this use case:
it('will check the matchers and pass', () => {
const user = {
createdAt: new Date(),
id: Math.floor(Math.random() * 20),
name: 'LeBron James',
};
expect(user).toMatchSnapshot({
createdAt: expect.any(Date),
id: expect.any(Number),
});
});
// Snapshot
exports[`will check the matchers and pass 1`] = `
Object {
"createdAt": Any<Date>,
"id": Any<Number>,
"name": "LeBron James",
}
`;
This is better than replacing or ignoring the value because you can assert that it matches some structure (like a string).
I'm not sure if this has been extended to support snapshot testing of components, but there's no reason it could be.
We encountered a similar issue, yet when instantiating the editor with a preexisting value:
describe('Editor', () => {
it('renders previously breaking markup successfully', () => {
const tree = renderer.create(<Editor value={markup} />).toJSON();
expect(tree).toMatchSnapshot();
});
});
we noticed that we needed to blend @neurosnap's suggestion with @zuffik's one as otherwise the editor would not contain the preexisting value — the deterministic "random" keys would always be the same (123) and overwrite themselves.
This is the core of the end result on our end:
jest.mock('draft-js/lib/generateRandomKey', () => {
let deterministicKey = 0;
return () => deterministicKey++;
});
Thank you all for getting us here. ✌🏼
In the latest version of DraftJS the above guidance was not working for us. We opted instead to adjust the rendered DocumentFragment after-the-fact to make it deterministic and to avoid dependence on DraftJS internals. It was more code, but it works across major & minor version changes.