@khanacademy/perseus 0.4.3 → 1.0.0

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.
Files changed (50) hide show
  1. package/.eslintrc.js +6 -1
  2. package/CHANGELOG.md +34 -0
  3. package/dist/es/index.js +1078 -1299
  4. package/dist/es/index.js.map +1 -1
  5. package/dist/index.js +1077 -1304
  6. package/dist/index.js.map +1 -1
  7. package/dist/strings.js +0 -4
  8. package/package.json +9 -8
  9. package/src/multi-items/__tests__/__snapshots__/multi-renderer_test.js.snap +110 -110
  10. package/src/perseus-api.jsx +10 -36
  11. package/src/perseus-types.js +0 -138
  12. package/src/styles/constants.js +0 -2
  13. package/src/styles/variables.less +0 -2
  14. package/src/types.js +18 -35
  15. package/src/util.js +0 -6
  16. package/src/version.js +1 -1
  17. package/src/widgets/__testdata__/base-radio_testdata.js +22 -0
  18. package/src/widgets/__tests__/__snapshots__/group_test.jsx.snap +110 -110
  19. package/src/widgets/__tests__/__snapshots__/radio_test.jsx.snap +1136 -1160
  20. package/src/widgets/__tests__/radio/base-radio_test.jsx +243 -41
  21. package/src/widgets/__tests__/radio/choice-icon_test.jsx +67 -0
  22. package/src/widgets/__tests__/radio/choice_test.jsx +201 -0
  23. package/src/widgets/__tests__/radio/focus-ring_test.jsx +23 -0
  24. package/src/widgets/__tests__/radio/option-status_test.jsx +65 -0
  25. package/src/widgets/passage/__tests__/passage-markdown_test.jsx +0 -9
  26. package/src/widgets/passage/get-line-height-for-node.js +4 -5
  27. package/src/widgets/passage/passage-markdown.jsx +176 -120
  28. package/src/widgets/passage.jsx +48 -46
  29. package/src/widgets/radio/__stories__/base-radio.stories.jsx +164 -17
  30. package/src/widgets/radio/__stories__/choice-icon.stories.jsx +202 -111
  31. package/src/widgets/radio/__stories__/choice-none-above.stories.jsx +21 -6
  32. package/src/widgets/radio/__stories__/choice.stories.jsx +138 -0
  33. package/src/widgets/radio/__stories__/focus-ring.stories.jsx +29 -23
  34. package/src/widgets/radio/__stories__/option-status.stories.jsx +93 -99
  35. package/src/widgets/radio/base-radio.jsx +483 -556
  36. package/src/widgets/radio/choice-icon/choice-icon.jsx +155 -0
  37. package/src/widgets/radio/choice-icon/cross-out-line.jsx +55 -0
  38. package/src/widgets/radio/choice-icon/library-choice-icon.jsx +219 -0
  39. package/src/widgets/radio/choice-icon/sat-choice-icon.jsx +129 -0
  40. package/src/widgets/radio/choice-icon/shared-styles.js +17 -0
  41. package/src/widgets/radio/choice-none-above.jsx +49 -40
  42. package/src/widgets/radio/choice.jsx +282 -353
  43. package/src/widgets/radio/choice.jsx.fixture.js +0 -18
  44. package/src/widgets/radio/focus-ring.jsx +29 -22
  45. package/src/widgets/radio/option-status.jsx +58 -36
  46. package/src/widgets/radio/widget.jsx +181 -136
  47. package/src/widgets/radio.jsx +16 -17
  48. package/src/widgets/radio/__stories__/cross-out-button.stories.jsx +0 -64
  49. package/src/widgets/radio/choice-icon.jsx +0 -469
  50. package/src/widgets/radio/cross-out-button.jsx +0 -134
@@ -6,72 +6,203 @@ import * as React from "react";
6
6
 
7
7
  import "@testing-library/jest-dom"; // Imports custom mathers
8
8
 
9
+ import {testDependencies} from "../../../../../../testing/test-dependencies.js";
10
+ import {setDependencies} from "../../../dependencies.js";
11
+ import {generateChoice} from "../../__testdata__/base-radio_testdata.js";
9
12
  import BaseRadio from "../../radio/base-radio.jsx";
10
13
 
11
14
  import type {APIOptions} from "../../../types.js";
12
15
 
16
+ function renderBaseRadio(props) {
17
+ const apiOptions: APIOptions = Object.freeze({});
18
+
19
+ const baseProps = {
20
+ apiOptions,
21
+ choices: [],
22
+ deselectEnabled: false,
23
+ editMode: false,
24
+ labelWrap: false,
25
+ countChoices: false,
26
+ numCorrect: 1,
27
+ multipleSelect: false,
28
+
29
+ // A callback indicating that this choice has changed. Its argument is
30
+ // an object with two keys: `checked` and `crossedOut`. Each contains
31
+ // an array of boolean values, specifying the new checked and
32
+ // crossed-out value of each choice.
33
+ onChange: ({checked, crossedOut}) => {},
34
+
35
+ // Whether this widget was the most recently used widget in this
36
+ // Renderer. Determines whether we'll auto-scroll the page upon
37
+ // entering review mode.
38
+ isLastUsedWidget: false,
39
+ };
40
+
41
+ const overwrittenProps = {...baseProps, ...props};
42
+
43
+ return render(<BaseRadio {...overwrittenProps} />);
44
+ }
45
+
13
46
  describe("base-radio", () => {
47
+ beforeEach(() => {
48
+ // because choice-none-above uses a Renderer
49
+ // we need to set dependencies when it's rendered
50
+ // (this probably needs to be fixed)
51
+ setDependencies(testDependencies);
52
+ });
53
+
54
+ it("renders a none of the above choice", () => {
55
+ // Arrange / Act
56
+ renderBaseRadio({
57
+ editMode: true,
58
+ choices: [
59
+ generateChoice({content: "Option 1", correct: false}),
60
+ generateChoice({content: "Option B", correct: false}),
61
+ generateChoice({content: "Option Gamma", correct: false}),
62
+ generateChoice({content: "Option Delta", correct: false}),
63
+ generateChoice({
64
+ isNoneOfTheAbove: true,
65
+ correct: true,
66
+ }),
67
+ ],
68
+ });
69
+
70
+ // Assert
71
+ expect(
72
+ screen.getByRole("checkbox", {name: "Select Choice E"}),
73
+ ).toBeInTheDocument();
74
+ expect(screen.getByText("None of the above")).toBeInTheDocument();
75
+ });
76
+
14
77
  describe("edit mode", () => {
15
78
  it("should render <li>'s for each choice", () => {
79
+ // Arrange / Act
80
+ renderBaseRadio({
81
+ editMode: true,
82
+ choices: [
83
+ generateChoice({content: "Option 1", correct: false}),
84
+ generateChoice({content: "Option B", correct: false}),
85
+ generateChoice({content: "Option Gamma", correct: true}),
86
+ generateChoice({content: "Option Delta", correct: false}),
87
+ ],
88
+ });
89
+
90
+ // Assert
91
+ expect(screen.getAllByRole("listitem")).toHaveLength(4);
92
+ });
93
+
94
+ it("should toggle choice when inner element clicked", () => {
16
95
  // Arrange
17
- const apiOptions: APIOptions = {
18
- styling: {radioStyleVersion: "final"},
96
+ let updatedValues = null;
97
+ const onChangeHandler = (newValues) => {
98
+ updatedValues = newValues;
19
99
  };
20
- const onChangeHandler = () => {};
100
+
101
+ renderBaseRadio({
102
+ editMode: true,
103
+ choices: [
104
+ generateChoice({content: "Option 1", correct: false}),
105
+ generateChoice({content: "Option B", correct: false}),
106
+ generateChoice({content: "Option Gamma", correct: true}),
107
+ generateChoice({content: "Option Delta", correct: false}),
108
+ ],
109
+ onChange: onChangeHandler,
110
+ });
21
111
 
22
112
  // Act
23
- render(
24
- <BaseRadio
25
- multipleSelect={false}
26
- countChoices={false}
27
- numCorrect={1}
28
- editMode={true}
29
- labelWrap={false}
30
- apiOptions={apiOptions}
31
- choices={[
32
- {content: "Option 1", correct: false},
33
- {content: "Option B", correct: false},
34
- {content: "Option Gamma", correct: true},
35
- {content: "Option Delta", correct: false},
36
- ]}
37
- onChange={onChangeHandler}
38
- />,
113
+ userEvent.click(
114
+ screen.getByRole("checkbox", {name: "Select Choice C"}),
39
115
  );
40
116
 
41
117
  // Assert
42
- expect(screen.getAllByRole("listitem")).toHaveLength(4);
118
+ expect(updatedValues).toMatchObject({
119
+ checked: [false, false, true, false],
120
+ });
43
121
  });
122
+ });
44
123
 
45
- it("should toggle choice when inner element clicked", () => {
124
+ describe("selecting and deselecting options", () => {
125
+ it("deselects multi-select choices", () => {
46
126
  // Arrange
47
- const apiOptions: APIOptions = {
48
- styling: {radioStyleVersion: "final"},
127
+ let updatedValues = null;
128
+ const onChangeHandler = (newValues) => {
129
+ updatedValues = newValues;
49
130
  };
131
+
132
+ renderBaseRadio({
133
+ multipleSelect: true,
134
+ choices: [
135
+ generateChoice({
136
+ content: "Option 1",
137
+ correct: false,
138
+ checked: false,
139
+ }),
140
+ generateChoice({
141
+ content: "Option B",
142
+ correct: false,
143
+ checked: false,
144
+ }),
145
+ generateChoice({
146
+ content: "Option Gamma",
147
+ correct: true,
148
+ checked: true,
149
+ }),
150
+ generateChoice({
151
+ content: "Option Delta",
152
+ correct: false,
153
+ checked: false,
154
+ }),
155
+ ],
156
+ onChange: onChangeHandler,
157
+ });
158
+
159
+ // Act
160
+ const radioButton = screen.getByRole("checkbox", {
161
+ name: "Select Choice C",
162
+ });
163
+ userEvent.click(radioButton);
164
+
165
+ // Assert
166
+ expect(updatedValues).toMatchObject({
167
+ checked: [false, false, false, false],
168
+ });
169
+ });
170
+
171
+ // Equivalent to "should toggle choice when inner element clicked" but with editMode set to false
172
+ it("select single select choices", () => {
173
+ // Arrange
50
174
  let updatedValues = null;
51
175
  const onChangeHandler = (newValues) => {
52
176
  updatedValues = newValues;
53
177
  };
54
178
 
55
- render(
56
- <BaseRadio
57
- multipleSelect={false}
58
- countChoices={false}
59
- numCorrect={1}
60
- editMode={true}
61
- labelWrap={false}
62
- apiOptions={apiOptions}
63
- choices={[
64
- {content: "Option 1", correct: false},
65
- {content: "Option B", correct: false},
66
- {content: "Option Gamma", correct: true},
67
- {content: "Option Delta", correct: false},
68
- ]}
69
- onChange={onChangeHandler}
70
- />,
71
- );
179
+ renderBaseRadio({
180
+ multipleSelect: true,
181
+ choices: [
182
+ generateChoice({
183
+ content: "Option 1",
184
+ correct: false,
185
+ }),
186
+ generateChoice({
187
+ content: "Option B",
188
+ correct: false,
189
+ }),
190
+ generateChoice({
191
+ content: "Option Gamma",
192
+ correct: true,
193
+ }),
194
+ generateChoice({
195
+ content: "Option Delta",
196
+ correct: false,
197
+ }),
198
+ ],
199
+ onChange: onChangeHandler,
200
+ });
72
201
 
73
202
  // Act
74
- userEvent.click(screen.getByText("(Choice C)", {selector: "div"}));
203
+ userEvent.click(
204
+ screen.getByRole("checkbox", {name: "Select Choice C"}),
205
+ );
75
206
 
76
207
  // Assert
77
208
  expect(updatedValues).toMatchObject({
@@ -79,4 +210,75 @@ describe("base-radio", () => {
79
210
  });
80
211
  });
81
212
  });
213
+
214
+ describe("instruction text", () => {
215
+ it("displays correct text for single select", () => {
216
+ // Arrange / Act
217
+ renderBaseRadio({
218
+ multipleSelect: false,
219
+ choices: [
220
+ generateChoice({content: "Option 1", correct: false}),
221
+ generateChoice({content: "Option B", correct: false}),
222
+ generateChoice({content: "Option Gamma", correct: true}),
223
+ generateChoice({content: "Option Delta", correct: false}),
224
+ ],
225
+ });
226
+
227
+ // Assert
228
+ // note(matthewc) role=group is for the fieldset/legend
229
+ // https://www.w3.org/TR/html-aria/
230
+ const group = screen.getByRole("group", {
231
+ name: "Choose 1 answer:",
232
+ });
233
+ expect(group).toBeInTheDocument();
234
+ expect(group).toBeVisible();
235
+ });
236
+
237
+ it("displays correct text for multi select (count choices off)", () => {
238
+ // Arrange / Act
239
+ renderBaseRadio({
240
+ multipleSelect: true,
241
+ countChoices: false,
242
+ choices: [
243
+ generateChoice({content: "Option 1", correct: false}),
244
+ generateChoice({content: "Option B", correct: false}),
245
+ generateChoice({content: "Option Gamma", correct: true}),
246
+ generateChoice({content: "Option Delta", correct: true}),
247
+ ],
248
+ });
249
+
250
+ // Assert
251
+ // note(matthewc) role=group is for the fieldset/legend
252
+ // https://www.w3.org/TR/html-aria/
253
+ const group = screen.getByRole("group", {
254
+ name: "Choose all answers that apply:",
255
+ });
256
+ expect(group).toBeInTheDocument();
257
+ expect(group).toBeVisible();
258
+ });
259
+
260
+ it("displays correct text for multi select (count choices on)", () => {
261
+ // Arrange / Act
262
+ renderBaseRadio({
263
+ multipleSelect: true,
264
+ countChoices: true,
265
+ numCorrect: 2,
266
+ choices: [
267
+ generateChoice({content: "Option 1", correct: false}),
268
+ generateChoice({content: "Option B", correct: false}),
269
+ generateChoice({content: "Option Gamma", correct: true}),
270
+ generateChoice({content: "Option Delta", correct: true}),
271
+ ],
272
+ });
273
+
274
+ // Assert
275
+ // note(matthewc) role=group is for the fieldset/legend
276
+ // https://www.w3.org/TR/html-aria/
277
+ const group = screen.getByRole("group", {
278
+ name: "Choose 2 answers:",
279
+ });
280
+ expect(group).toBeInTheDocument();
281
+ expect(group).toBeVisible();
282
+ });
283
+ });
82
284
  });
@@ -0,0 +1,67 @@
1
+ // @flow
2
+
3
+ import {render, screen} from "@testing-library/react";
4
+ import * as React from "react";
5
+
6
+ import "@testing-library/jest-dom"; // Imports custom mathers
7
+
8
+ import ChoiceIcon from "../../radio/choice-icon/choice-icon.jsx";
9
+ import {
10
+ SAT_ICON_SIZE,
11
+ LIBRARY_ICON_SIZE,
12
+ } from "../../radio/choice-icon/shared-styles.js";
13
+
14
+ function renderChoiceIcon(options) {
15
+ const defaultOptions = {
16
+ pos: 0,
17
+ checked: false,
18
+ crossedOut: false,
19
+ focused: false,
20
+ hovered: false,
21
+ pressed: false,
22
+ correct: false,
23
+ showCorrectness: false,
24
+ product: "library",
25
+ multipleSelect: false,
26
+ reviewMode: false,
27
+ previouslyAnswered: false,
28
+ transparentBackground: false,
29
+ };
30
+
31
+ const overwrittenOptions = {...defaultOptions, ...options};
32
+
33
+ return render(<ChoiceIcon {...overwrittenOptions} />);
34
+ }
35
+
36
+ describe("choice icon", () => {
37
+ describe.each([["sat"], ["library"]])("product: %s", (product) => {
38
+ describe.each([[true], [false]])(
39
+ "multipleSelect: %s",
40
+ (multipleSelect) => {
41
+ it("renders with the correct border radius", () => {
42
+ // Arrange
43
+ let expectedRadius;
44
+ if (multipleSelect) {
45
+ expectedRadius = 3;
46
+ } else if (product === "sat") {
47
+ expectedRadius = SAT_ICON_SIZE;
48
+ } else {
49
+ expectedRadius = LIBRARY_ICON_SIZE;
50
+ }
51
+
52
+ // Act
53
+ renderChoiceIcon({product, multipleSelect});
54
+
55
+ const choiceWrapper = screen.getByTestId(
56
+ `choice-icon__${product}-choice-icon`,
57
+ );
58
+
59
+ // Assert
60
+ expect(choiceWrapper).toHaveStyle(
61
+ `border-radius: ${expectedRadius}px`,
62
+ );
63
+ });
64
+ },
65
+ );
66
+ });
67
+ });
@@ -0,0 +1,201 @@
1
+ // @flow
2
+
3
+ import {render, screen} from "@testing-library/react";
4
+ import userEvent from "@testing-library/user-event";
5
+ import * as React from "react";
6
+
7
+ import "@testing-library/jest-dom"; // Imports custom mathers
8
+
9
+ import Choice from "../../radio/choice.jsx";
10
+
11
+ function renderChoice(options) {
12
+ const defaultOptions = {
13
+ checked: false,
14
+ rationale: "This is a good rationale",
15
+ content: "This is a possible choice",
16
+ correct: true,
17
+ disabled: false,
18
+ pos: 0,
19
+ reviewMode: false,
20
+ showRationale: false,
21
+ showCorrectness: false,
22
+ multipleSelect: false,
23
+ crossedOut: false,
24
+ previouslyAnswered: false,
25
+ ref: {current: null},
26
+ };
27
+
28
+ const overwrittenOptions = {...defaultOptions, ...options};
29
+
30
+ return render(<Choice {...overwrittenOptions} />);
31
+ }
32
+
33
+ describe("choice", () => {
34
+ it("renders choice content", () => {
35
+ // Arrange / Act
36
+ renderChoice();
37
+
38
+ // Assert
39
+ expect(screen.getByText("This is a possible choice")).toBeVisible();
40
+ });
41
+
42
+ it("renders rationale", () => {
43
+ // Arrange / Act
44
+ renderChoice({showRationale: true});
45
+
46
+ // Assert
47
+ expect(screen.getByText("This is a good rationale")).toBeVisible();
48
+ });
49
+
50
+ it("renders choice button", () => {
51
+ // Arrange / Act
52
+ renderChoice();
53
+
54
+ const button = screen.getByRole("checkbox", {
55
+ name: "Select Choice A",
56
+ });
57
+
58
+ // Assert
59
+ expect(button).toBeVisible();
60
+ });
61
+
62
+ it("has correct aria-checked when checked", () => {
63
+ // Arrange / Act
64
+ renderChoice({checked: true});
65
+
66
+ const button = screen.getByRole("checkbox", {
67
+ name: "Select Choice A",
68
+ });
69
+
70
+ // Assert
71
+ expect(button).toBeChecked();
72
+ });
73
+
74
+ it("has correct aria-checked when unchecked", () => {
75
+ // Arrange / Act
76
+ renderChoice({checked: false});
77
+
78
+ const button = screen.getByRole("checkbox", {
79
+ name: "Select Choice A",
80
+ });
81
+
82
+ // Assert
83
+ expect(button).not.toBeChecked();
84
+ });
85
+
86
+ it("has correct aria-disabled when disabled", () => {
87
+ // Arrange / Act
88
+ renderChoice({disabled: true});
89
+
90
+ const button = screen.getByRole("checkbox", {
91
+ name: "Select Choice A",
92
+ });
93
+
94
+ // Assert
95
+ expect(button.getAttribute("aria-disabled")).toBe("true");
96
+ });
97
+
98
+ it("has correct aria-disabled when not disabled", () => {
99
+ // Arrange / Act
100
+ renderChoice({disabled: false});
101
+
102
+ const button = screen.getByRole("checkbox", {
103
+ name: "Select Choice A",
104
+ });
105
+
106
+ // Assert
107
+ expect(button.getAttribute("aria-disabled")).toBe("false");
108
+ });
109
+
110
+ it("shows correct when in review mode", () => {
111
+ // Arrange / Act
112
+ renderChoice({
113
+ checked: false,
114
+ correct: true,
115
+ showCorrectness: true,
116
+ reviewMode: true,
117
+ });
118
+
119
+ // Assert
120
+ expect(screen.getByText("Correct")).toBeVisible();
121
+ });
122
+
123
+ it("shows correct selected when in review mode", () => {
124
+ // Arrange / Act
125
+ renderChoice({
126
+ checked: true,
127
+ correct: true,
128
+ showCorrectness: true,
129
+ reviewMode: true,
130
+ });
131
+
132
+ // Assert
133
+ expect(screen.getByText("Correct (selected)")).toBeVisible();
134
+ });
135
+
136
+ it("shows incorrect when in review mode", () => {
137
+ // Arrange / Act
138
+ renderChoice({
139
+ checked: false,
140
+ correct: false,
141
+ showCorrectness: true,
142
+ reviewMode: true,
143
+ });
144
+
145
+ // Assert
146
+ expect(screen.getByText("Incorrect")).toBeVisible();
147
+ });
148
+
149
+ it("shows incorrect selected when in review mode", () => {
150
+ // Arrange / Act
151
+ renderChoice({
152
+ checked: true,
153
+ correct: false,
154
+ showCorrectness: true,
155
+ reviewMode: true,
156
+ });
157
+
158
+ // Assert
159
+ expect(screen.getByText("Incorrect (selected)")).toBeVisible();
160
+ });
161
+
162
+ it("can be checked", () => {
163
+ // Arrange
164
+ let checked = false;
165
+ renderChoice({
166
+ checked,
167
+ onChange: (next) => {
168
+ checked = next.checked;
169
+ },
170
+ });
171
+
172
+ // Act
173
+ const button = screen.getByRole("checkbox", {
174
+ name: "Select Choice A",
175
+ });
176
+ userEvent.click(button);
177
+
178
+ // Assert
179
+ expect(checked).toBeTrue();
180
+ });
181
+
182
+ it("can be unchecked", () => {
183
+ // Arrange
184
+ let checked = true;
185
+ renderChoice({
186
+ checked,
187
+ onChange: (next) => {
188
+ checked = next.checked;
189
+ },
190
+ });
191
+
192
+ // Act
193
+ const button = screen.getByRole("checkbox", {
194
+ name: "Select Choice A",
195
+ });
196
+ userEvent.click(button);
197
+
198
+ // Assert
199
+ expect(checked).toBeFalse();
200
+ });
201
+ });
@@ -0,0 +1,23 @@
1
+ // @flow
2
+
3
+ import {render, screen} from "@testing-library/react";
4
+ import * as React from "react";
5
+
6
+ import "@testing-library/jest-dom"; // Imports custom mathers
7
+
8
+ import FocusRing from "../../radio/focus-ring.jsx";
9
+
10
+ describe("choice icon", () => {
11
+ describe.each([[true], [false]])("multipleSelect: %s", (multipleSelect) => {
12
+ it("renders with the correct border radius", () => {
13
+ // Arrange/Act
14
+ render(<FocusRing multipleSelect={multipleSelect} />);
15
+
16
+ const focusRing = screen.getByTestId("focus-ring");
17
+
18
+ // Assert
19
+ const expectedRadius = multipleSelect ? "5px" : "50%";
20
+ expect(focusRing).toHaveStyle(`border-radius: ${expectedRadius}`);
21
+ });
22
+ });
23
+ });
@@ -0,0 +1,65 @@
1
+ // @flow
2
+
3
+ import {render, screen} from "@testing-library/react";
4
+ import * as React from "react";
5
+
6
+ import "@testing-library/jest-dom"; // Imports custom mathers
7
+
8
+ import OptionStatus from "../../radio/option-status.jsx";
9
+
10
+ function renderOptionStatus(options) {
11
+ const defaultOptions = {
12
+ correct: false,
13
+ checked: false,
14
+ crossedOut: false,
15
+ previouslyAnswered: false,
16
+ reviewMode: true,
17
+ };
18
+
19
+ const overwrittenOptions = {...defaultOptions, ...options};
20
+
21
+ return render(<OptionStatus {...overwrittenOptions} />);
22
+ }
23
+
24
+ describe("answer text", () => {
25
+ it.each([
26
+ [true, "Correct"],
27
+ [false, "Incorrect"],
28
+ ])(
29
+ "renders with the correct text based on 'correct' prop: %s => %s",
30
+ (correct, expectedText) => {
31
+ // Arrange/Act
32
+ renderOptionStatus({correct: correct});
33
+ const optionStatus = screen.getByText(expectedText);
34
+
35
+ // Assert
36
+ expect(optionStatus).toBeVisible();
37
+ },
38
+ );
39
+
40
+ it.each([
41
+ [true, "Incorrect (selected)"],
42
+ [false, "Incorrect"],
43
+ ])(
44
+ "renders with the correct text based on 'checked' prop: %s => %s",
45
+ (checked, expectedText) => {
46
+ // Arrange/Act
47
+ renderOptionStatus({correct: false, checked: checked});
48
+ const optionStatus = screen.getByText(expectedText);
49
+
50
+ // Assert
51
+ expect(optionStatus).toBeVisible();
52
+ },
53
+ );
54
+
55
+ it("renders correctly based on 'crossedOut' and 'correct'", () => {
56
+ // Arrange/Act
57
+ renderOptionStatus({crossedOut: true, correct: true});
58
+ const optionStatus = screen.getByText(
59
+ "Correct (but you crossed it out)",
60
+ );
61
+
62
+ // Assert
63
+ expect(optionStatus).toBeVisible();
64
+ });
65
+ });