Skip to content

Commit de73d6c

Browse files
ryan-m-walkeracao
andcommitted
feat: replace use of enzyme with react-testing-library (#1144) by @ryan-m-walker
- added @testing-library/react to graphiql package - added @testing-libary/jest-dom to graphiql package - removed usage of ENZYM env variable in test script in graphiql packaget - removed enzyme adapter setup from enzyme.config.js file and renamed it to test.config.js - removed use of enzyme and replaced with react-testing-library in all graphiql package test files and updated tests to use react-testing-library patterns - moved mockStorage to own file so it can be reused in other test files and added other methods to it - added a CodeMirror mock to be used in tests - mocked out codemirror addon modules - moved QueryHistory tests from own file into GraphiQL.spec.js file - removed `cross-env` from test script - moved CodeMirror mock to __mock__ file Co-authored-by: Rikki Schulte <[email protected]> Co-authored-by: Rikki Schulte <[email protected]>
1 parent 5765394 commit de73d6c

File tree

14 files changed

+692
-586
lines changed

14 files changed

+692
-586
lines changed

jest.config.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ module.exports = {
1010
},
1111
clearMocks: true,
1212
collectCoverage: true,
13-
setupFiles: [path.join(__dirname, '/resources/enzyme.config.js')],
13+
setupFiles: [path.join(__dirname, '/resources/test.config.js')],
1414
testMatch: [
1515
'<rootDir>/packages/*/src/**/*-test.{js,ts}',
1616
'<rootDir>/packages/*/src/**/*.spec.{js,ts}',
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
function CodeMirror(node, { value, ...options }) {
2+
let _eventListeners = {};
3+
const mockTextArea = document.createElement('textarea');
4+
mockTextArea.className = 'mockCodeMirror';
5+
mockTextArea.addEventListener('change', e => {
6+
_emit('change', e);
7+
});
8+
mockTextArea.value = value;
9+
node.appendChild(mockTextArea);
10+
11+
function _emit(event, data) {
12+
if (_eventListeners[event]) {
13+
_eventListeners[event](data);
14+
}
15+
}
16+
17+
return {
18+
options: {
19+
...options,
20+
},
21+
22+
on(event, handler) {
23+
_eventListeners[event] = handler;
24+
},
25+
26+
off(event) {
27+
if (_eventListeners.hasOwnProperty(event)) {
28+
const updatedEventListeners = {};
29+
for (const e in _eventListeners) {
30+
if (e !== event) {
31+
updatedEventListeners[e] = _eventListeners[e];
32+
}
33+
}
34+
_eventListeners = updatedEventListeners;
35+
}
36+
},
37+
38+
getValue() {
39+
return mockTextArea.value;
40+
},
41+
42+
setValue(newValue) {
43+
mockTextArea.value = newValue;
44+
},
45+
46+
setSize() {},
47+
48+
emit: _emit,
49+
};
50+
}
51+
52+
CodeMirror.defineExtension = () => {};
53+
CodeMirror.registerHelper = () => {};
54+
CodeMirror.defineOption = () => {};
55+
CodeMirror.signal = (mockCodeMirror, event, ...args) => {
56+
mockCodeMirror.emit(event, ...args);
57+
};
58+
59+
module.exports = CodeMirror;

packages/graphiql/package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@
3939
"cypress-open": "yarn e2e-server 'cypress open'",
4040
"e2e": "yarn e2e-server 'cypress run'",
4141
"e2e-server": "start-server-and-test 'cross-env PORT=8080 node test/e2e-server' 'http-get://localhost:8080/graphql?query={test { id }}'",
42-
"test": "cross-env ENZYME=true node ../../resources/runTests",
42+
"test": "node ../../resources/runTests",
4343
"storybook": "start-storybook"
4444
},
4545
"dependencies": {
@@ -61,11 +61,11 @@
6161
},
6262
"devDependencies": {
6363
"@storybook/react": "^5.2.8",
64+
"@testing-library/jest-dom": "4.2.4",
65+
"@testing-library/react": "9.4.0",
6466
"cross-env": "^6.0.3",
6567
"css-loader": "3.4.0",
6668
"cssnano": "^4.1.10",
67-
"enzyme": "^3.10.0",
68-
"enzyme-adapter-react-16": "^1.15.1",
6969
"express": "4.17.1",
7070
"express-graphql": "0.9.0",
7171
"graphql": "14.5.8",

packages/graphiql/src/components/DocExplorer/__tests__/FieldDoc.spec.js

Lines changed: 34 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@
66
*/
77

88
import React from 'react';
9-
import { mount } from 'enzyme';
9+
import { render } from '@testing-library/react';
10+
import '@testing-library/jest-dom/extend-expect';
1011

1112
import FieldDoc from '../FieldDoc';
1213

@@ -35,47 +36,58 @@ const exampleObject = new GraphQLObjectType({
3536

3637
describe('FieldDoc', () => {
3738
it('should render a simple string field', () => {
38-
const W = mount(
39+
const { container } = render(
3940
<FieldDoc
4041
field={exampleObject.getFields().string}
4142
onClickType={jest.fn()}
4243
/>,
4344
);
44-
expect(W.find('MarkdownContent').text()).toEqual('No Description\n');
45-
expect(W.find('TypeLink').text()).toEqual('String');
46-
expect(W.find('Argument').length).toEqual(0);
45+
expect(container.querySelector('.doc-type-description')).toHaveTextContent(
46+
'No Description',
47+
);
48+
expect(container.querySelector('.type-name')).toHaveTextContent('String');
49+
expect(container.querySelector('.arg')).not.toBeInTheDocument();
4750
});
4851

4952
it('should re-render on field change', () => {
50-
const W = mount(
53+
const { container, rerender } = render(
5154
<FieldDoc
5255
field={exampleObject.getFields().string}
5356
onClickType={jest.fn()}
5457
/>,
5558
);
56-
expect(W.find('MarkdownContent').text()).toEqual('No Description\n');
57-
expect(W.find('TypeLink').text()).toEqual('String');
58-
expect(W.find('Argument').length).toEqual(0);
59+
expect(container.querySelector('.doc-type-description')).toHaveTextContent(
60+
'No Description',
61+
);
62+
expect(container.querySelector('.type-name')).toHaveTextContent('String');
63+
expect(container.querySelector('.arg')).not.toBeInTheDocument();
64+
65+
rerender(
66+
<FieldDoc
67+
field={exampleObject.getFields().stringWithArgs}
68+
onClickType={jest.fn()}
69+
/>,
70+
);
71+
expect(container.querySelector('.type-name')).toHaveTextContent('String');
72+
expect(container.querySelector('.doc-type-description')).toHaveTextContent(
73+
'Example String field with arguments',
74+
);
5975
});
6076

6177
it('should render a string field with arguments', () => {
62-
const W = mount(
78+
const { container } = render(
6379
<FieldDoc
6480
field={exampleObject.getFields().stringWithArgs}
6581
onClickType={jest.fn()}
6682
/>,
6783
);
68-
expect(
69-
W.find('TypeLink')
70-
.at(0)
71-
.text(),
72-
).toEqual('String');
73-
expect(
74-
W.find('.doc-type-description')
75-
.at(0)
76-
.text(),
77-
).toEqual('Example String field with arguments\n');
78-
expect(W.find('Argument').length).toEqual(1);
79-
expect(W.find('Argument').text()).toEqual('stringArg: String');
84+
expect(container.querySelector('.type-name')).toHaveTextContent('String');
85+
expect(container.querySelector('.doc-type-description')).toHaveTextContent(
86+
'Example String field with arguments',
87+
);
88+
expect(container.querySelectorAll('.arg')).toHaveLength(1);
89+
expect(container.querySelector('.arg')).toHaveTextContent(
90+
'stringArg: String',
91+
);
8092
});
8193
});

packages/graphiql/src/components/DocExplorer/__tests__/TypeDoc.spec.js

Lines changed: 55 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@
66
*/
77

88
import React from 'react';
9-
import { mount } from 'enzyme';
9+
import { render, fireEvent } from '@testing-library/react';
10+
import '@testing-library/jest-dom/extend-expect';
1011

1112
import { GraphQLString } from 'graphql';
1213

@@ -21,115 +22,107 @@ import {
2122

2223
describe('TypeDoc', () => {
2324
it('renders a top-level query object type', () => {
24-
const W = mount(
25+
const { container } = render(
2526
<TypeDoc
2627
schema={ExampleSchema}
2728
type={ExampleQuery}
2829
onClickType={jest.fn()}
2930
/>,
3031
);
31-
const cats = W.find('.doc-category-item');
32-
expect(cats.at(0).text()).toEqual('string: String');
33-
expect(cats.at(1).text()).toEqual('union: exampleUnion');
34-
expect(cats.at(2).text()).toEqual(
32+
const cats = container.querySelectorAll('.doc-category-item');
33+
expect(cats[0]).toHaveTextContent('string: String');
34+
expect(cats[1]).toHaveTextContent('union: exampleUnion');
35+
expect(cats[2]).toHaveTextContent(
3536
'fieldWithArgs(stringArg: String): String',
3637
);
3738
});
3839

3940
it('handles onClickField and onClickType', () => {
4041
const onClickType = jest.fn();
4142
const onClickField = jest.fn();
42-
const W = mount(
43+
const { container } = render(
4344
<TypeDoc
4445
schema={ExampleSchema}
4546
type={ExampleQuery}
4647
onClickType={onClickType}
4748
onClickField={onClickField}
4849
/>,
4950
);
50-
W.find('TypeLink')
51-
.at(0)
52-
.simulate('click');
51+
fireEvent.click(container.querySelector('.type-name'));
5352
expect(onClickType.mock.calls.length).toEqual(1);
5453
expect(onClickType.mock.calls[0][0]).toEqual(GraphQLString);
5554

56-
W.find('.field-name')
57-
.at(0)
58-
.simulate('click');
59-
55+
fireEvent.click(container.querySelector('.field-name'));
6056
expect(onClickField.mock.calls.length).toEqual(1);
6157
expect(onClickField.mock.calls[0][0].name).toEqual('string');
6258
expect(onClickField.mock.calls[0][0].type).toEqual(GraphQLString);
6359
expect(onClickField.mock.calls[0][1]).toEqual(ExampleQuery);
6460
});
6561

6662
it('renders deprecated fields when you click to see them', () => {
67-
const W = mount(
63+
const { container } = render(
6864
<TypeDoc
6965
schema={ExampleSchema}
7066
type={ExampleQuery}
7167
onClickType={jest.fn()}
7268
/>,
7369
);
74-
let cats = W.find('.doc-category-item');
75-
expect(cats.length).toEqual(3);
70+
let cats = container.querySelectorAll('.doc-category-item');
71+
expect(cats).toHaveLength(3);
7672

77-
W.find('.show-btn').simulate('click');
73+
fireEvent.click(container.querySelector('.show-btn'));
7874

79-
cats = W.find('.doc-category-item');
80-
expect(cats.length).toEqual(4);
81-
expect(
82-
W.find('.field-name')
83-
.at(3)
84-
.text(),
85-
).toEqual('deprecatedField');
86-
expect(
87-
W.find('.doc-deprecation')
88-
.at(0)
89-
.text(),
90-
).toEqual('example deprecation reason\n');
75+
cats = container.querySelectorAll('.doc-category-item');
76+
expect(cats).toHaveLength(4);
77+
expect(container.querySelectorAll('.field-name')[3]).toHaveTextContent(
78+
'deprecatedField',
79+
);
80+
expect(container.querySelector('.doc-deprecation')).toHaveTextContent(
81+
'example deprecation reason',
82+
);
9183
});
9284

9385
it('renders a Union type', () => {
94-
const W = mount(<TypeDoc schema={ExampleSchema} type={ExampleUnion} />);
95-
expect(
96-
W.find('.doc-category-title')
97-
.at(0)
98-
.text(),
99-
).toEqual('possible types');
86+
const { container } = render(
87+
<TypeDoc schema={ExampleSchema} type={ExampleUnion} />,
88+
);
89+
expect(container.querySelector('.doc-category-title')).toHaveTextContent(
90+
'possible types',
91+
);
10092
});
10193

10294
it('renders an Enum type', () => {
103-
const W = mount(<TypeDoc schema={ExampleSchema} type={ExampleEnum} />);
104-
expect(
105-
W.find('.doc-category-title')
106-
.at(0)
107-
.text(),
108-
).toEqual('values');
109-
const enums = W.find('EnumValue');
110-
expect(enums.at(0).props().value.value).toEqual('Value 1');
111-
expect(enums.at(1).props().value.value).toEqual('Value 2');
95+
const { container } = render(
96+
<TypeDoc schema={ExampleSchema} type={ExampleEnum} />,
97+
);
98+
expect(container.querySelector('.doc-category-title')).toHaveTextContent(
99+
'values',
100+
);
101+
const enums = container.querySelectorAll('.enum-value');
102+
expect(enums[0]).toHaveTextContent('value1');
103+
expect(enums[1]).toHaveTextContent('value2');
112104
});
113105

114106
it('shows deprecated enum values on click', () => {
115-
const W = mount(<TypeDoc schema={ExampleSchema} type={ExampleEnum} />);
116-
expect(W.state().showDeprecated).toEqual(false);
117-
const titles = W.find('.doc-category-title');
118-
expect(titles.at(0).text()).toEqual('values');
119-
expect(titles.at(1).text()).toEqual('deprecated values');
120-
let enums = W.find('EnumValue');
121-
expect(enums.length).toEqual(2);
107+
const { getByText, container } = render(
108+
<TypeDoc schema={ExampleSchema} type={ExampleEnum} />,
109+
);
110+
const showBtn = getByText('Show deprecated values...');
111+
expect(showBtn).toBeInTheDocument();
112+
const titles = container.querySelectorAll('.doc-category-title');
113+
expect(titles[0]).toHaveTextContent('values');
114+
expect(titles[1]).toHaveTextContent('deprecated values');
115+
let enums = container.querySelectorAll('.enum-value');
116+
expect(enums).toHaveLength(2);
122117

123118
// click button to show deprecated enum values
124-
W.find('.show-btn').simulate('click');
125-
expect(W.state().showDeprecated).toEqual(true);
126-
enums = W.find('EnumValue');
127-
expect(enums.length).toEqual(3);
128-
expect(enums.at(2).props().value.value).toEqual('Value 3');
129-
expect(
130-
W.find('.doc-deprecation')
131-
.at(1)
132-
.text(),
133-
).toEqual('Only two are needed\n');
119+
fireEvent.click(showBtn);
120+
expect(showBtn).not.toBeInTheDocument();
121+
enums = container.querySelectorAll('.enum-value');
122+
expect(enums).toHaveLength(3);
123+
expect(enums[2]).toHaveTextContent('value3');
124+
expect(container.querySelector('.doc-deprecation')).toHaveTextContent(
125+
'Only two are needed',
126+
);
134127
});
135128
});

0 commit comments

Comments
 (0)