@khanacademy/wonder-blocks-form 3.1.5 → 3.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,12 @@
1
1
  # @khanacademy/wonder-blocks-form
2
2
 
3
+ ## 3.1.6
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies [4c682709]
8
+ - @khanacademy/wonder-blocks-clickable@2.4.1
9
+
3
10
  ## 3.1.5
4
11
 
5
12
  ### Patch Changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@khanacademy/wonder-blocks-form",
3
- "version": "3.1.5",
3
+ "version": "3.1.6",
4
4
  "design": "v1",
5
5
  "description": "Form components for Wonder Blocks.",
6
6
  "main": "dist/index.js",
@@ -16,7 +16,7 @@
16
16
  },
17
17
  "dependencies": {
18
18
  "@babel/runtime": "^7.18.6",
19
- "@khanacademy/wonder-blocks-clickable": "^2.4.0",
19
+ "@khanacademy/wonder-blocks-clickable": "^2.4.1",
20
20
  "@khanacademy/wonder-blocks-color": "^1.2.0",
21
21
  "@khanacademy/wonder-blocks-core": "^4.5.0",
22
22
  "@khanacademy/wonder-blocks-icon": "^1.2.32",
@@ -1,15 +1,10 @@
1
1
  // @flow
2
2
  import * as React from "react";
3
- import {mount} from "enzyme";
4
- import "jest-enzyme";
3
+ import {render, screen} from "@testing-library/react";
5
4
  import {StyleSheet} from "aphrodite";
6
5
 
7
6
  import {I18nInlineMarkup} from "@khanacademy/wonder-blocks-i18n";
8
- import {
9
- Body,
10
- LabelMedium,
11
- LabelSmall,
12
- } from "@khanacademy/wonder-blocks-typography";
7
+ import {Body} from "@khanacademy/wonder-blocks-typography";
13
8
 
14
9
  import FieldHeading from "../field-heading.js";
15
10
  import TextField from "../text-field.js";
@@ -20,7 +15,7 @@ describe("FieldHeading", () => {
20
15
  const label = "Label";
21
16
 
22
17
  // Act
23
- const wrapper = mount(
18
+ render(
24
19
  <FieldHeading
25
20
  field={<TextField id="tf-1" value="" onChange={() => {}} />}
26
21
  label={label}
@@ -28,7 +23,7 @@ describe("FieldHeading", () => {
28
23
  );
29
24
 
30
25
  // Assert
31
- expect(wrapper).toIncludeText(label);
26
+ expect(screen.getByText(label)).toBeInTheDocument();
32
27
  });
33
28
 
34
29
  it("fieldheading renders the description text", () => {
@@ -36,7 +31,7 @@ describe("FieldHeading", () => {
36
31
  const description = "Description";
37
32
 
38
33
  // Act
39
- const wrapper = mount(
34
+ render(
40
35
  <FieldHeading
41
36
  field={<TextField id="tf-1" value="" onChange={() => {}} />}
42
37
  label="Label"
@@ -45,7 +40,7 @@ describe("FieldHeading", () => {
45
40
  );
46
41
 
47
42
  // Assert
48
- expect(wrapper).toIncludeText(description);
43
+ expect(screen.getByText(description)).toBeInTheDocument();
49
44
  });
50
45
 
51
46
  it("fieldheading renders the error text", () => {
@@ -53,7 +48,7 @@ describe("FieldHeading", () => {
53
48
  const error = "Error";
54
49
 
55
50
  // Act
56
- const wrapper = mount(
51
+ render(
57
52
  <FieldHeading
58
53
  field={<TextField id="tf-1" value="" onChange={() => {}} />}
59
54
  label="Label"
@@ -62,7 +57,7 @@ describe("FieldHeading", () => {
62
57
  );
63
58
 
64
59
  // Assert
65
- expect(wrapper).toIncludeText(error);
60
+ expect(screen.getByRole("alert")).toBeInTheDocument();
66
61
  });
67
62
 
68
63
  it("fieldheading adds testId to label", () => {
@@ -70,7 +65,7 @@ describe("FieldHeading", () => {
70
65
  const testId = "testid";
71
66
 
72
67
  // Act
73
- const wrapper = mount(
68
+ render(
74
69
  <FieldHeading
75
70
  field={<TextField id="tf-1" value="" onChange={() => {}} />}
76
71
  label="Label"
@@ -79,8 +74,8 @@ describe("FieldHeading", () => {
79
74
  );
80
75
 
81
76
  // Assert
82
- const label = wrapper.find(`[data-test-id="${testId}-label"]`);
83
- expect(label).toExist();
77
+ const label = screen.getByTestId(`${testId}-label`);
78
+ expect(label).toBeInTheDocument();
84
79
  });
85
80
 
86
81
  it("fieldheading adds testId to description", () => {
@@ -88,7 +83,7 @@ describe("FieldHeading", () => {
88
83
  const testId = "testid";
89
84
 
90
85
  // Act
91
- const wrapper = mount(
86
+ render(
92
87
  <FieldHeading
93
88
  field={<TextField id="tf-1" value="" onChange={() => {}} />}
94
89
  label="Label"
@@ -98,10 +93,8 @@ describe("FieldHeading", () => {
98
93
  );
99
94
 
100
95
  // Assert
101
- const description = wrapper.find(
102
- `[data-test-id="${testId}-description"]`,
103
- );
104
- expect(description).toExist();
96
+ const description = screen.getByTestId(`${testId}-description`);
97
+ expect(description).toBeInTheDocument();
105
98
  });
106
99
 
107
100
  it("fieldheading adds testId to error", () => {
@@ -109,7 +102,7 @@ describe("FieldHeading", () => {
109
102
  const testId = "testid";
110
103
 
111
104
  // Act
112
- const wrapper = mount(
105
+ render(
113
106
  <FieldHeading
114
107
  field={<TextField id="tf-1" value="" onChange={() => {}} />}
115
108
  label="Label"
@@ -119,8 +112,8 @@ describe("FieldHeading", () => {
119
112
  );
120
113
 
121
114
  // Assert
122
- const error = wrapper.find(`[data-test-id="${testId}-error"]`);
123
- expect(error).toExist();
115
+ const error = screen.getByTestId(`${testId}-error`);
116
+ expect(error).toBeInTheDocument();
124
117
  });
125
118
 
126
119
  it("fieldheading adds the correctly formatted id to label's htmlFor", () => {
@@ -129,7 +122,7 @@ describe("FieldHeading", () => {
129
122
  const testId = "testid";
130
123
 
131
124
  // Act
132
- const wrapper = mount(
125
+ render(
133
126
  <FieldHeading
134
127
  field={<TextField id="tf-1" value="" onChange={() => {}} />}
135
128
  label="Label"
@@ -139,8 +132,8 @@ describe("FieldHeading", () => {
139
132
  );
140
133
 
141
134
  // Assert
142
- const label = wrapper.find(`[data-test-id="${testId}-label"]`);
143
- expect(label).toContainMatchingElement(`[htmlFor="${id}-field"]`);
135
+ const label = screen.getByTestId(`${testId}-label`);
136
+ expect(label).toHaveAttribute("for", `${id}-field`);
144
137
  });
145
138
 
146
139
  it("fieldheading adds the correctly formatted id to error's id", () => {
@@ -149,7 +142,7 @@ describe("FieldHeading", () => {
149
142
  const testId = "testid";
150
143
 
151
144
  // Act
152
- const wrapper = mount(
145
+ render(
153
146
  <FieldHeading
154
147
  field={<TextField id="tf-1" value="" onChange={() => {}} />}
155
148
  label="Label"
@@ -160,8 +153,8 @@ describe("FieldHeading", () => {
160
153
  );
161
154
 
162
155
  // Assert
163
- const error = wrapper.find(`[data-test-id="${testId}-error"]`);
164
- expect(error).toContainMatchingElement(`[id="${id}-error"]`);
156
+ const error = screen.getByRole("alert");
157
+ expect(error).toHaveAttribute("id", `${id}-error`);
165
158
  });
166
159
 
167
160
  it("stype prop applies to the fieldheading container", () => {
@@ -174,7 +167,7 @@ describe("FieldHeading", () => {
174
167
  });
175
168
 
176
169
  // Act
177
- const wrapper = mount(
170
+ const {container} = render(
178
171
  <FieldHeading
179
172
  field={<TextField id="tf-1" value="" onChange={() => {}} />}
180
173
  label="Label"
@@ -184,15 +177,15 @@ describe("FieldHeading", () => {
184
177
  );
185
178
 
186
179
  // Assert
187
- const container = wrapper.find("View").at(0);
188
- expect(container).toHaveStyle(styles.style1);
180
+ const fieldHeading = container.childNodes[0];
181
+ expect(fieldHeading).toHaveStyle("background: blue");
189
182
  });
190
183
 
191
- it("should render a LabelSmall when the 'label' prop is a I18nInlineMarkup", () => {
184
+ it("should render a LabelMedium when the 'label' prop is a I18nInlineMarkup", () => {
192
185
  // Arrange
193
186
 
194
187
  // Act
195
- const wrapper = mount(
188
+ render(
196
189
  <FieldHeading
197
190
  field={<TextField id="tf-1" value="" onChange={() => {}} />}
198
191
  label={<I18nInlineMarkup>Hello, world!</I18nInlineMarkup>}
@@ -200,15 +193,16 @@ describe("FieldHeading", () => {
200
193
  );
201
194
 
202
195
  // Assert
203
- const label = wrapper.find(LabelMedium);
204
- expect(label).toExist();
196
+ const label = screen.getByText("Hello, world!");
197
+ // LabelMedium has a font-size of 16px
198
+ expect(label).toHaveStyle("font-size: 16px");
205
199
  });
206
200
 
207
201
  it("should render a LabelSmall when the 'description' prop is a I18nInlineMarkup", () => {
208
202
  // Arrange
209
203
 
210
204
  // Act
211
- const wrapper = mount(
205
+ render(
212
206
  <FieldHeading
213
207
  field={<TextField id="tf-1" value="" onChange={() => {}} />}
214
208
  label={<Body>Hello, world</Body>}
@@ -217,7 +211,8 @@ describe("FieldHeading", () => {
217
211
  );
218
212
 
219
213
  // Assert
220
- const label = wrapper.find(LabelSmall);
221
- expect(label).toExist();
214
+ const description = screen.getByText("description");
215
+ // LabelSmall has a font-size of 16px
216
+ expect(description).toHaveStyle("font-size: 14px");
222
217
  });
223
218
  });
@@ -1,54 +1,37 @@
1
1
  //@flow
2
2
  import * as React from "react";
3
- import {mount} from "enzyme";
4
- import "jest-enzyme";
5
- import {render, screen} from "@testing-library/react";
3
+ import {render, screen, fireEvent} from "@testing-library/react";
6
4
  import userEvent from "@testing-library/user-event";
7
5
 
8
6
  import {StyleSheet} from "aphrodite";
9
7
  import LabeledTextField from "../labeled-text-field.js";
10
8
 
11
- const wait = (delay: number = 0) =>
12
- new Promise((resolve, reject) => {
13
- // eslint-disable-next-line no-restricted-syntax
14
- return setTimeout(resolve, delay);
15
- });
16
-
17
9
  describe("LabeledTextField", () => {
18
10
  it("labeledtextfield becomes focused", () => {
19
11
  // Arrange
20
- const wrapper = mount(
21
- <LabeledTextField label="Label" value="" onChange={() => {}} />,
22
- );
23
- const field = wrapper.find("TextFieldInternal");
12
+ render(<LabeledTextField label="Label" value="" onChange={() => {}} />);
13
+ const field = screen.getByRole("textbox");
24
14
 
25
15
  // Act
26
- field.simulate("focus");
16
+ userEvent.tab();
27
17
 
28
18
  // Assert
29
- expect(wrapper.find("LabeledTextFieldInternal")).toHaveState(
30
- "focused",
31
- true,
32
- );
19
+ expect(field).toHaveFocus();
33
20
  });
34
21
 
35
22
  it("labeledtextfield becomes blurred", async () => {
36
23
  // Arrange
37
- const wrapper = mount(
38
- <LabeledTextField label="Label" value="" onChange={() => {}} />,
39
- );
40
- const field = wrapper.find("TextFieldInternal");
24
+ render(<LabeledTextField label="Label" value="" onChange={() => {}} />);
25
+
26
+ // focus
27
+ userEvent.tab();
41
28
 
42
29
  // Act
43
- field.simulate("focus");
44
- await wait(0);
45
- field.simulate("blur");
30
+ // blur
31
+ userEvent.tab();
46
32
 
47
33
  // Assert
48
- expect(wrapper.find("LabeledTextFieldInternal")).toHaveState(
49
- "focused",
50
- false,
51
- );
34
+ expect(screen.getByRole("textbox")).not.toHaveFocus();
52
35
  });
53
36
 
54
37
  it("id prop is passed to input", () => {
@@ -56,7 +39,7 @@ describe("LabeledTextField", () => {
56
39
  const id = "exampleid";
57
40
 
58
41
  // Act
59
- const wrapper = mount(
42
+ render(
60
43
  <LabeledTextField
61
44
  id={id}
62
45
  label="Label"
@@ -67,23 +50,21 @@ describe("LabeledTextField", () => {
67
50
  );
68
51
 
69
52
  // Assert
70
- const input = wrapper.find("input");
71
- expect(input).toContainMatchingElement(`[id="${id}-field"]`);
53
+ const input = screen.getByRole("textbox");
54
+ expect(input).toHaveAttribute("id", `${id}-field`);
72
55
  });
73
56
 
74
57
  it("auto-generated id is passed to input when id prop is not set", () => {
75
58
  // Arrange
76
59
 
77
60
  // Act
78
- const wrapper = mount(
79
- <LabeledTextField label="Label" value="" onChange={() => {}} />,
80
- );
61
+ render(<LabeledTextField label="Label" value="" onChange={() => {}} />);
81
62
 
82
63
  // Assert
83
64
  // Since the generated id is unique, we cannot know what it will be.
84
65
  // We only test if the id attribute starts with "uid-" and ends with "-field".
85
- const input = wrapper.find("input");
86
- expect(input.props()["id"]).toMatch(/uid-.*-field/);
66
+ const input = screen.getByRole("textbox");
67
+ expect(input.getAttribute("id")).toMatch(/uid-.*-field/);
87
68
  });
88
69
 
89
70
  it("type prop is passed to input", () => {
@@ -91,7 +72,7 @@ describe("LabeledTextField", () => {
91
72
  const type = "email";
92
73
 
93
74
  // Act
94
- const wrapper = mount(
75
+ render(
95
76
  <LabeledTextField
96
77
  type={type}
97
78
  label="Label"
@@ -101,8 +82,8 @@ describe("LabeledTextField", () => {
101
82
  );
102
83
 
103
84
  // Assert
104
- const input = wrapper.find("input");
105
- expect(input).toContainMatchingElement(`[type="${type}"]`);
85
+ const input = screen.getByRole("textbox");
86
+ expect(input).toHaveAttribute("type", type);
106
87
  });
107
88
 
108
89
  it("label prop is rendered", () => {
@@ -110,12 +91,10 @@ describe("LabeledTextField", () => {
110
91
  const label = "Label";
111
92
 
112
93
  // Act
113
- const wrapper = mount(
114
- <LabeledTextField label={label} value="" onChange={() => {}} />,
115
- );
94
+ render(<LabeledTextField label={label} value="" onChange={() => {}} />);
116
95
 
117
96
  // Assert
118
- expect(wrapper).toIncludeText(label);
97
+ expect(screen.getByText(label)).toBeInTheDocument();
119
98
  });
120
99
 
121
100
  it("description prop is rendered", () => {
@@ -123,7 +102,7 @@ describe("LabeledTextField", () => {
123
102
  const description = "Description";
124
103
 
125
104
  // Act
126
- const wrapper = mount(
105
+ render(
127
106
  <LabeledTextField
128
107
  label="Label"
129
108
  description={description}
@@ -133,7 +112,7 @@ describe("LabeledTextField", () => {
133
112
  );
134
113
 
135
114
  // Assert
136
- expect(wrapper).toIncludeText(description);
115
+ expect(screen.getByText(description)).toBeInTheDocument();
137
116
  });
138
117
 
139
118
  it("value prop is set on mount", () => {
@@ -141,7 +120,7 @@ describe("LabeledTextField", () => {
141
120
  const value = "Value";
142
121
 
143
122
  // Act
144
- const wrapper = mount(
123
+ render(
145
124
  <LabeledTextField
146
125
  label="Label"
147
126
  value={value}
@@ -150,32 +129,38 @@ describe("LabeledTextField", () => {
150
129
  );
151
130
 
152
131
  // Assert
153
- const input = wrapper.find("input");
154
- expect(input).toHaveValue(value);
132
+ const input = screen.getByRole("textbox");
133
+ expect(input).toHaveAttribute("value", value);
155
134
  });
156
135
 
157
136
  it("value prop change from parent reflects on input value", async () => {
158
137
  // Arrange
159
138
  const handleChange = jest.fn((newValue: string) => {});
160
139
 
161
- const wrapper = mount(
140
+ const {rerender} = render(
162
141
  <LabeledTextField label="Label" value="" onChange={handleChange} />,
163
142
  );
164
143
 
165
144
  // Act
166
145
  const newValue = "new value";
167
- wrapper.setProps({value: newValue});
146
+ rerender(
147
+ <LabeledTextField
148
+ label="Label"
149
+ value={newValue}
150
+ onChange={handleChange}
151
+ />,
152
+ );
168
153
 
169
154
  // Assert
170
- const input = wrapper.find("input");
171
- expect(input).toHaveValue(newValue);
155
+ const input = screen.getByRole("textbox");
156
+ expect(input).toHaveAttribute("value", newValue);
172
157
  });
173
158
 
174
159
  it("disabled prop disables the input", () => {
175
160
  // Arrange
176
161
 
177
162
  // Act
178
- const wrapper = mount(
163
+ render(
179
164
  <LabeledTextField
180
165
  label="Label"
181
166
  value=""
@@ -185,7 +170,7 @@ describe("LabeledTextField", () => {
185
170
  );
186
171
 
187
172
  // Assert
188
- const input = wrapper.find("input");
173
+ const input = screen.getByRole("textbox");
189
174
  expect(input).toBeDisabled();
190
175
  });
191
176
 
@@ -235,7 +220,7 @@ describe("LabeledTextField", () => {
235
220
  it("validate prop is called when input changes", () => {
236
221
  // Arrange
237
222
  const validate = jest.fn((value: string): ?string => {});
238
- const wrapper = mount(
223
+ render(
239
224
  <LabeledTextField
240
225
  label="Label"
241
226
  value=""
@@ -246,8 +231,11 @@ describe("LabeledTextField", () => {
246
231
 
247
232
  // Act
248
233
  const newValue = "New Value";
249
- const input = wrapper.find("input");
250
- input.simulate("change", {target: {value: newValue}});
234
+ const input = screen.getByRole("textbox");
235
+ // @see https://testing-library.com/docs/react-testing-library/faq
236
+ // How do I test input onChange handlers?
237
+ // eslint-disable-next-line testing-library/prefer-user-event
238
+ fireEvent.change(input, {target: {value: newValue}});
251
239
 
252
240
  // Assert
253
241
  expect(validate).toHaveBeenCalledWith(newValue);
@@ -264,7 +252,7 @@ describe("LabeledTextField", () => {
264
252
  }
265
253
  };
266
254
 
267
- const wrapper = mount(
255
+ render(
268
256
  <LabeledTextField
269
257
  label="Label"
270
258
  value="LongerThan8Chars"
@@ -275,8 +263,8 @@ describe("LabeledTextField", () => {
275
263
  );
276
264
 
277
265
  // Act
278
- const input = wrapper.find("input");
279
- input.simulate("change", {target: {value: "Short"}});
266
+ // Select all text and replace it with the new value.
267
+ userEvent.type(screen.getByRole("textbox"), `{selectall}Short`);
280
268
 
281
269
  // Assert
282
270
  expect(handleValidate).toHaveBeenCalledWith(errorMessage);
@@ -286,14 +274,17 @@ describe("LabeledTextField", () => {
286
274
  // Arrange
287
275
  const handleChange = jest.fn((newValue: string) => {});
288
276
 
289
- const wrapper = mount(
277
+ render(
290
278
  <LabeledTextField label="Label" value="" onChange={handleChange} />,
291
279
  );
292
280
 
293
281
  // Act
294
282
  const newValue = "new value";
295
- const input = wrapper.find("input");
296
- input.simulate("change", {target: {value: newValue}});
283
+ const input = screen.getByRole("textbox");
284
+ // @see https://testing-library.com/docs/react-testing-library/faq
285
+ // How do I test input onChange handlers?
286
+ // eslint-disable-next-line testing-library/prefer-user-event
287
+ fireEvent.change(input, {target: {value: newValue}});
297
288
 
298
289
  // Assert
299
290
  expect(handleChange).toHaveBeenCalledWith(newValue);
@@ -307,7 +298,7 @@ describe("LabeledTextField", () => {
307
298
  },
308
299
  );
309
300
 
310
- const wrapper = mount(
301
+ render(
311
302
  <LabeledTextField
312
303
  label="Label"
313
304
  value=""
@@ -317,18 +308,16 @@ describe("LabeledTextField", () => {
317
308
  );
318
309
 
319
310
  // Act
320
- const key = "Enter";
321
- const input = wrapper.find("input");
322
- input.simulate("keyDown", {key: key});
311
+ userEvent.type(screen.getByRole("textbox"), "{enter}");
323
312
 
324
313
  // Assert
325
- expect(handleKeyDown).toHaveReturnedWith(key);
314
+ expect(handleKeyDown).toHaveReturnedWith("Enter");
326
315
  });
327
316
 
328
317
  it("onFocus prop is called when field is focused", () => {
329
318
  // Arrange
330
319
  const handleFocus = jest.fn(() => {});
331
- const wrapper = mount(
320
+ render(
332
321
  <LabeledTextField
333
322
  label="Label"
334
323
  value=""
@@ -338,8 +327,8 @@ describe("LabeledTextField", () => {
338
327
  );
339
328
 
340
329
  // Act
341
- const field = wrapper.find("TextFieldInternal");
342
- field.simulate("focus");
330
+ const field = screen.getByRole("textbox");
331
+ field.focus();
343
332
 
344
333
  // Assert
345
334
  expect(handleFocus).toHaveBeenCalled();
@@ -348,7 +337,7 @@ describe("LabeledTextField", () => {
348
337
  it("onBlur prop is called when field is blurred", async () => {
349
338
  // Arrange
350
339
  const handleBlur = jest.fn(() => {});
351
- const wrapper = mount(
340
+ render(
352
341
  <LabeledTextField
353
342
  label="Label"
354
343
  value=""
@@ -357,11 +346,12 @@ describe("LabeledTextField", () => {
357
346
  />,
358
347
  );
359
348
 
349
+ // focus
350
+ userEvent.tab();
351
+
360
352
  // Act
361
- const field = wrapper.find("TextFieldInternal");
362
- field.simulate("focus");
363
- await wait(0);
364
- field.simulate("blur");
353
+ // blur
354
+ userEvent.tab();
365
355
 
366
356
  // Assert
367
357
  expect(handleBlur).toHaveBeenCalled();
@@ -372,7 +362,7 @@ describe("LabeledTextField", () => {
372
362
  const placeholder = "Placeholder";
373
363
 
374
364
  // Act
375
- const wrapper = mount(
365
+ render(
376
366
  <LabeledTextField
377
367
  label="Label"
378
368
  value=""
@@ -382,17 +372,15 @@ describe("LabeledTextField", () => {
382
372
  );
383
373
 
384
374
  // Assert
385
- const input = wrapper.find("input");
386
- expect(input).toContainMatchingElement(
387
- `[placeholder="${placeholder}"]`,
388
- );
375
+ const input = screen.getByPlaceholderText(placeholder);
376
+ expect(input).toBeInTheDocument();
389
377
  });
390
378
 
391
379
  it("light prop is passed to textfield", async () => {
392
380
  // Arrange
393
381
 
394
382
  // Act
395
- const wrapper = mount(
383
+ render(
396
384
  <LabeledTextField
397
385
  label="Label"
398
386
  value=""
@@ -401,9 +389,11 @@ describe("LabeledTextField", () => {
401
389
  />,
402
390
  );
403
391
 
392
+ const textField = screen.getByRole("textbox");
393
+ textField.focus();
394
+
404
395
  // Assert
405
- const textField = wrapper.find("TextFieldInternal");
406
- expect(textField).toHaveProp("light", true);
396
+ expect(textField.getAttribute("class")).toMatch(/light/i);
407
397
  });
408
398
 
409
399
  it("style prop is passed to fieldheading", async () => {
@@ -416,7 +406,7 @@ describe("LabeledTextField", () => {
416
406
  });
417
407
 
418
408
  // Act
419
- const wrapper = mount(
409
+ const {container} = render(
420
410
  <LabeledTextField
421
411
  label="Label"
422
412
  value=""
@@ -426,8 +416,8 @@ describe("LabeledTextField", () => {
426
416
  );
427
417
 
428
418
  // Assert
429
- const fieldHeading = wrapper.find("FieldHeading");
430
- expect(fieldHeading).toHaveStyle(styles.style1);
419
+ const fieldHeading = container.childNodes[0];
420
+ expect(fieldHeading).toHaveStyle("min-width: 250px");
431
421
  });
432
422
 
433
423
  it("testId prop is passed to textfield", async () => {
@@ -435,7 +425,7 @@ describe("LabeledTextField", () => {
435
425
  const testId = "example-testid";
436
426
 
437
427
  // Act
438
- const wrapper = mount(
428
+ render(
439
429
  <LabeledTextField
440
430
  label="Label"
441
431
  value=""
@@ -445,15 +435,15 @@ describe("LabeledTextField", () => {
445
435
  );
446
436
 
447
437
  // Assert
448
- const textField = wrapper.find(`[data-test-id="${testId}-field"]`);
449
- expect(textField).toExist();
438
+ const input = screen.getByRole("textbox");
439
+ expect(input).toHaveAttribute("data-test-id", `${testId}-field`);
450
440
  });
451
441
 
452
442
  it("readOnly prop is passed to textfield", async () => {
453
443
  // Arrange
454
444
 
455
445
  // Act
456
- const wrapper = mount(
446
+ render(
457
447
  <LabeledTextField
458
448
  label="Label"
459
449
  value=""
@@ -463,8 +453,8 @@ describe("LabeledTextField", () => {
463
453
  );
464
454
 
465
455
  // Assert
466
- const textField = wrapper.find("TextFieldInternal");
467
- expect(textField).toHaveProp("readOnly", true);
456
+ const input = screen.getByRole("textbox");
457
+ expect(input).toHaveAttribute("readOnly");
468
458
  });
469
459
 
470
460
  it("autoComplete prop is passed to textfield", async () => {
@@ -472,7 +462,7 @@ describe("LabeledTextField", () => {
472
462
  const autoComplete = "name";
473
463
 
474
464
  // Act
475
- const wrapper = mount(
465
+ render(
476
466
  <LabeledTextField
477
467
  label="Label"
478
468
  value=""
@@ -482,8 +472,8 @@ describe("LabeledTextField", () => {
482
472
  );
483
473
 
484
474
  // Assert
485
- const textField = wrapper.find("TextFieldInternal");
486
- expect(textField).toHaveProp("autoComplete", autoComplete);
475
+ const input = screen.getByRole("textbox");
476
+ expect(input).toHaveAttribute("autoComplete", autoComplete);
487
477
  });
488
478
  });
489
479
 
@@ -1,35 +1,27 @@
1
1
  // @flow
2
2
  import * as React from "react";
3
- import {mount} from "enzyme";
4
- import "jest-enzyme";
3
+ import {fireEvent, render, screen} from "@testing-library/react";
4
+ import userEvent from "@testing-library/user-event";
5
5
 
6
6
  import TextField from "../text-field.js";
7
7
 
8
- const wait = (delay: number = 0) =>
9
- new Promise((resolve, reject) => {
10
- // eslint-disable-next-line no-restricted-syntax
11
- return setTimeout(resolve, delay);
12
- });
13
-
14
8
  describe("TextField", () => {
15
9
  it("textfield is focused", () => {
16
10
  // Arrange
17
- const wrapper = mount(
18
- <TextField id="tf-1" value="" onChange={() => {}} />,
19
- );
11
+ render(<TextField id="tf-1" value="" onChange={() => {}} />);
20
12
 
21
13
  // Act
22
- wrapper.simulate("focus");
14
+ userEvent.tab();
23
15
 
24
16
  // Assert
25
- expect(wrapper.find("TextFieldInternal")).toHaveState("focused", true);
17
+ expect(screen.getByRole("textbox")).toHaveFocus();
26
18
  });
27
19
 
28
20
  it("onFocus is called after textfield is focused", () => {
29
21
  // Arrange
30
22
  const handleOnFocus = jest.fn(() => {});
31
23
 
32
- const wrapper = mount(
24
+ render(
33
25
  <TextField
34
26
  id={"tf-1"}
35
27
  value="TextIsLongerThan8"
@@ -39,7 +31,7 @@ describe("TextField", () => {
39
31
  );
40
32
 
41
33
  // Act
42
- wrapper.simulate("focus");
34
+ userEvent.tab();
43
35
 
44
36
  // Assert
45
37
  expect(handleOnFocus).toHaveBeenCalled();
@@ -47,24 +39,24 @@ describe("TextField", () => {
47
39
 
48
40
  it("textfield is blurred", async () => {
49
41
  // Arrange
50
- const wrapper = mount(
51
- <TextField id="tf-1" value="" onChange={() => {}} />,
52
- );
42
+ render(<TextField id="tf-1" value="" onChange={() => {}} />);
43
+
44
+ // focus
45
+ userEvent.tab();
53
46
 
54
47
  // Act
55
- wrapper.simulate("focus");
56
- await wait(0);
57
- wrapper.simulate("blur");
48
+ // blur
49
+ userEvent.tab();
58
50
 
59
51
  // Assert
60
- expect(wrapper.find("TextFieldInternal")).toHaveState("focused", false);
52
+ expect(screen.getByRole("textbox")).not.toHaveFocus();
61
53
  });
62
54
 
63
55
  it("onBlur is called after textfield is blurred", async () => {
64
56
  // Arrange
65
57
  const handleOnBlur = jest.fn(() => {});
66
58
 
67
- const wrapper = mount(
59
+ render(
68
60
  <TextField
69
61
  id={"tf-1"}
70
62
  value="TextIsLongerThan8"
@@ -73,10 +65,12 @@ describe("TextField", () => {
73
65
  />,
74
66
  );
75
67
 
68
+ // focus
69
+ userEvent.tab();
70
+
76
71
  // Act
77
- wrapper.simulate("focus");
78
- await wait(0);
79
- wrapper.simulate("blur");
72
+ // blur
73
+ userEvent.tab();
80
74
 
81
75
  // Assert
82
76
  expect(handleOnBlur).toHaveBeenCalled();
@@ -87,13 +81,11 @@ describe("TextField", () => {
87
81
  const id: string = "tf-1";
88
82
 
89
83
  // Act
90
- const wrapper = mount(
91
- <TextField id={id} value="" onChange={() => {}} />,
92
- );
84
+ render(<TextField id={id} value="" onChange={() => {}} />);
93
85
 
94
86
  // Assert
95
- const input = wrapper.find("input");
96
- expect(input).toContainMatchingElement(`[id="${id}"]`);
87
+ const input = screen.getByRole("textbox");
88
+ expect(input).toHaveAttribute("id", id);
97
89
  });
98
90
 
99
91
  it("type prop is passed to the input element", () => {
@@ -101,13 +93,14 @@ describe("TextField", () => {
101
93
  const type = "number";
102
94
 
103
95
  // Act
104
- const wrapper = mount(
96
+ render(
105
97
  <TextField id={"tf-1"} type={type} value="" onChange={() => {}} />,
106
98
  );
107
99
 
108
100
  // Assert
109
- const input = wrapper.find("input");
110
- expect(input).toContainMatchingElement(`[type="${type}"]`);
101
+ // NOTE: The implicit role for input[type=number] is "spinbutton".
102
+ const input = screen.getByRole("spinbutton");
103
+ expect(input).toHaveAttribute("type", type);
111
104
  });
112
105
 
113
106
  it("value prop is passed to the input element", () => {
@@ -115,18 +108,16 @@ describe("TextField", () => {
115
108
  const value = "Text";
116
109
 
117
110
  // Act
118
- const wrapper = mount(
119
- <TextField id={"tf-1"} value={value} onChange={() => {}} />,
120
- );
111
+ render(<TextField id={"tf-1"} value={value} onChange={() => {}} />);
121
112
 
122
113
  // Assert
123
- const input = wrapper.find("input");
124
- expect(input).toContainMatchingElement(`[value="${value}"]`);
114
+ const input = screen.getByDisplayValue(value);
115
+ expect(input).toBeInTheDocument();
125
116
  });
126
117
 
127
118
  it("disabled prop disables the input element", () => {
128
119
  // Arrange
129
- const wrapper = mount(
120
+ render(
130
121
  <TextField
131
122
  id="tf-1"
132
123
  value=""
@@ -134,7 +125,7 @@ describe("TextField", () => {
134
125
  disabled={true}
135
126
  />,
136
127
  );
137
- const input = wrapper.find("input");
128
+ const input = screen.getByRole("textbox");
138
129
 
139
130
  // Act
140
131
 
@@ -146,13 +137,17 @@ describe("TextField", () => {
146
137
  // Arrange
147
138
  const handleOnChange = jest.fn();
148
139
 
149
- const wrapper = mount(
140
+ render(
150
141
  <TextField id={"tf-1"} value="Text" onChange={handleOnChange} />,
151
142
  );
152
143
 
153
144
  // Act
154
145
  const newValue = "Test2";
155
- wrapper.simulate("change", {target: {value: newValue}});
146
+ const input = screen.getByRole("textbox");
147
+ // @see https://testing-library.com/docs/react-testing-library/faq
148
+ // How do I test input onChange handlers?
149
+ // eslint-disable-next-line testing-library/prefer-user-event
150
+ fireEvent.change(input, {target: {value: newValue}});
156
151
 
157
152
  // Assert
158
153
  expect(handleOnChange).toHaveBeenCalledWith(newValue);
@@ -162,7 +157,7 @@ describe("TextField", () => {
162
157
  // Arrange
163
158
  const handleValidate = jest.fn((value: string): ?string => {});
164
159
 
165
- const wrapper = mount(
160
+ render(
166
161
  <TextField
167
162
  id={"tf-1"}
168
163
  value="Text"
@@ -173,7 +168,8 @@ describe("TextField", () => {
173
168
 
174
169
  // Act
175
170
  const newValue = "Text2";
176
- wrapper.simulate("change", {target: {value: newValue}});
171
+ // Select all text and replace it with the new value.
172
+ userEvent.type(screen.getByRole("textbox"), `{selectall}${newValue}`);
177
173
 
178
174
  // Assert
179
175
  expect(handleValidate).toHaveBeenCalledWith(newValue);
@@ -187,7 +183,7 @@ describe("TextField", () => {
187
183
  }
188
184
  });
189
185
 
190
- const wrapper = mount(
186
+ render(
191
187
  <TextField
192
188
  id={"tf-1"}
193
189
  value="TextIsLong"
@@ -198,7 +194,8 @@ describe("TextField", () => {
198
194
 
199
195
  // Act
200
196
  const newValue = "TextIsLongerThan8";
201
- wrapper.simulate("change", {target: {value: newValue}});
197
+ // Select all text and replace it with the new value.
198
+ userEvent.type(screen.getByRole("textbox"), `{selectall}${newValue}`);
202
199
 
203
200
  // Assert
204
201
  expect(handleValidate).toHaveReturnedWith(undefined);
@@ -213,7 +210,7 @@ describe("TextField", () => {
213
210
  }
214
211
  });
215
212
 
216
- const wrapper = mount(
213
+ render(
217
214
  <TextField
218
215
  id={"tf-1"}
219
216
  value="TextIsLongerThan8"
@@ -224,7 +221,8 @@ describe("TextField", () => {
224
221
 
225
222
  // Act
226
223
  const newValue = "Text";
227
- wrapper.simulate("change", {target: {value: newValue}});
224
+ // Select all text and replace it with the new value.
225
+ userEvent.type(screen.getByRole("textbox"), `{selectall}${newValue}`);
228
226
 
229
227
  // Assert
230
228
  expect(handleValidate).toHaveReturnedWith(errorMessage);
@@ -240,7 +238,7 @@ describe("TextField", () => {
240
238
  }
241
239
  });
242
240
 
243
- const wrapper = mount(
241
+ render(
244
242
  <TextField
245
243
  id={"tf-1"}
246
244
  value="TextIsLongerThan8"
@@ -252,7 +250,8 @@ describe("TextField", () => {
252
250
 
253
251
  // Act
254
252
  const newValue = "Text";
255
- wrapper.simulate("change", {target: {value: newValue}});
253
+ // Select all text and replace it with the new value.
254
+ userEvent.type(screen.getByRole("textbox"), `{selectall}${newValue}`);
256
255
 
257
256
  // Assert
258
257
  expect(handleValidate).toHaveBeenCalledWith(errorMessage);
@@ -269,7 +268,7 @@ describe("TextField", () => {
269
268
  });
270
269
 
271
270
  // Act
272
- mount(
271
+ render(
273
272
  <TextField
274
273
  id={"tf-1"}
275
274
  value="Short"
@@ -291,7 +290,7 @@ describe("TextField", () => {
291
290
  },
292
291
  );
293
292
 
294
- const wrapper = mount(
293
+ render(
295
294
  <TextField
296
295
  id={"tf-1"}
297
296
  value="TextIsLongerThan8"
@@ -301,12 +300,10 @@ describe("TextField", () => {
301
300
  );
302
301
 
303
302
  // Act
304
- const key = "Enter";
305
- const input = wrapper.find("input");
306
- input.simulate("keyDown", {key: key});
303
+ userEvent.type(screen.getByRole("textbox"), "{enter}");
307
304
 
308
305
  // Assert
309
- expect(handleOnKeyDown).toHaveReturnedWith(key);
306
+ expect(handleOnKeyDown).toHaveReturnedWith("Enter");
310
307
  });
311
308
 
312
309
  it("placeholder prop is passed to the input element", () => {
@@ -314,7 +311,7 @@ describe("TextField", () => {
314
311
  const placeholder = "Placeholder";
315
312
 
316
313
  // Act
317
- const wrapper = mount(
314
+ render(
318
315
  <TextField
319
316
  id={"tf-1"}
320
317
  value="Text"
@@ -324,16 +321,14 @@ describe("TextField", () => {
324
321
  );
325
322
 
326
323
  // Assert
327
- const input = wrapper.find("input");
328
- expect(input).toContainMatchingElement(
329
- `[placeholder="${placeholder}"]`,
330
- );
324
+ const input = screen.getByPlaceholderText(placeholder);
325
+ expect(input).toBeInTheDocument();
331
326
  });
332
327
 
333
328
  it("testId is passed to the input element", () => {
334
329
  // Arrange
335
330
  const testId = "some-test-id";
336
- const wrapper = mount(
331
+ render(
337
332
  <TextField
338
333
  id={"tf-1"}
339
334
  value="Text"
@@ -345,14 +340,14 @@ describe("TextField", () => {
345
340
  // Act
346
341
 
347
342
  // Assert
348
- const input = wrapper.find("input");
349
- expect(input).toContainMatchingElement(`[data-test-id="${testId}"]`);
343
+ const input = screen.getByRole("textbox");
344
+ expect(input).toHaveAttribute("data-test-id", testId);
350
345
  });
351
346
 
352
347
  it("aria props are passed to the input element", () => {
353
348
  // Arrange
354
349
  const ariaLabel = "example-text-field";
355
- const wrapper = mount(
350
+ render(
356
351
  <TextField
357
352
  id={"tf-1"}
358
353
  value="Text"
@@ -364,15 +359,15 @@ describe("TextField", () => {
364
359
  // Act
365
360
 
366
361
  // Assert
367
- const input = wrapper.find("input");
368
- expect(input).toContainMatchingElement(`[aria-label="${ariaLabel}"]`);
362
+ const input = screen.getByRole("textbox");
363
+ expect(input).toHaveAttribute("aria-label", ariaLabel);
369
364
  });
370
365
 
371
366
  it("readOnly prop is passed to the input element", async () => {
372
367
  // Arrange
373
368
 
374
369
  // Act
375
- const wrapper = mount(
370
+ render(
376
371
  <TextField
377
372
  id={"tf-1"}
378
373
  value={"Text"}
@@ -382,8 +377,8 @@ describe("TextField", () => {
382
377
  );
383
378
 
384
379
  // Assert
385
- const input = wrapper.find("input");
386
- expect(input).toHaveProp("readOnly");
380
+ const input = screen.getByRole("textbox");
381
+ expect(input).toHaveAttribute("readOnly");
387
382
  });
388
383
 
389
384
  it("autoComplete prop is passed to the input element", async () => {
@@ -391,7 +386,7 @@ describe("TextField", () => {
391
386
  const autoComplete = "name";
392
387
 
393
388
  // Act
394
- const wrapper = mount(
389
+ render(
395
390
  <TextField
396
391
  id={"tf-1"}
397
392
  value={"Text"}
@@ -401,7 +396,7 @@ describe("TextField", () => {
401
396
  );
402
397
 
403
398
  // Assert
404
- const input = wrapper.find("input");
405
- expect(input).toHaveProp("autoComplete", autoComplete);
399
+ const input = screen.getByRole("textbox");
400
+ expect(input).toHaveAttribute("autoComplete", autoComplete);
406
401
  });
407
402
  });