@khanacademy/perseus-editor 2.15.3 → 2.15.4
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 +29 -0
- package/dist/editor-page.d.ts +4 -10
- package/dist/editor.d.ts +2 -2
- package/dist/es/index.js +25 -14
- package/dist/es/index.js.map +1 -1
- package/dist/index.js +25 -14
- package/dist/index.js.map +1 -1
- package/dist/widgets/interactive-graph-editor.d.ts +92 -92
- package/package.json +2 -2
- package/src/__stories__/editor-page.stories.tsx +65 -0
- package/src/__stories__/editor.stories.tsx +2 -4
- package/src/editor-page.tsx +5 -11
- package/src/editor.tsx +2 -2
- package/src/widgets/__stories__/categorizer-editor.stories.tsx +24 -0
- package/src/widgets/__stories__/definition-editor.stories.tsx +18 -0
- package/src/widgets/__stories__/dropdown-editor.stories.tsx +18 -0
- package/src/widgets/__stories__/explanation-editor.stories.tsx +18 -0
- package/src/widgets/__stories__/input-number-editor.stories.tsx +18 -0
- package/src/widgets/__stories__/matcher-editor.stories.tsx +18 -0
- package/src/widgets/__stories__/number-line-editor.stories.tsx +18 -0
- package/src/widgets/__stories__/numeric-input-editor.stories.tsx +18 -0
- package/src/widgets/__stories__/radio-editor.stories.tsx +3 -3
- package/src/widgets/__stories__/sorter-editor.stories.tsx +18 -0
- package/src/widgets/__tests__/categorizer-editor.test.tsx +51 -0
- package/src/widgets/__tests__/definition-editor.test.tsx +41 -0
- package/src/widgets/__tests__/dropdown-editor.test.tsx +57 -0
- package/src/widgets/__tests__/explanation-editor.test.tsx +59 -0
- package/src/widgets/__tests__/input-number-editor.test.tsx +128 -0
- package/src/widgets/__tests__/matcher-editor.test.tsx +47 -0
- package/src/widgets/__tests__/number-line-editor.test.tsx +144 -0
- package/src/widgets/__tests__/numeric-input-editor.test.tsx +177 -0
- package/src/widgets/__tests__/radio-editor.test.tsx +118 -0
- package/src/widgets/__tests__/sorter-editor.test.tsx +55 -0
- package/src/widgets/dropdown-editor.tsx +1 -0
- package/src/widgets/input-number-editor.tsx +2 -0
- package/src/widgets/number-line-editor.tsx +16 -5
- package/src/widgets/numeric-input-editor.tsx +2 -0
- package/tsconfig-build.tsbuildinfo +1 -1
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import {Dependencies} from "@khanacademy/perseus";
|
|
2
|
+
import {render, screen} from "@testing-library/react";
|
|
3
|
+
import userEvent from "@testing-library/user-event";
|
|
4
|
+
import * as React from "react";
|
|
5
|
+
|
|
6
|
+
import "@testing-library/jest-dom";
|
|
7
|
+
|
|
8
|
+
import {testDependencies} from "../../../../../testing/test-dependencies";
|
|
9
|
+
import InputNumberEditor from "../input-number-editor";
|
|
10
|
+
|
|
11
|
+
describe("input-number-editor", () => {
|
|
12
|
+
beforeEach(() => {
|
|
13
|
+
jest.spyOn(Dependencies, "getDependencies").mockReturnValue(
|
|
14
|
+
testDependencies,
|
|
15
|
+
);
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it("should render", async () => {
|
|
19
|
+
render(<InputNumberEditor onChange={() => undefined} />);
|
|
20
|
+
|
|
21
|
+
expect(await screen.findByText("Correct answer:")).toBeInTheDocument();
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it("should be possible to change the correct answer", async () => {
|
|
25
|
+
const onChangeMock = jest.fn();
|
|
26
|
+
|
|
27
|
+
render(<InputNumberEditor onChange={onChangeMock} />);
|
|
28
|
+
|
|
29
|
+
const input = screen.getByRole("textbox", {name: "Correct answer:"});
|
|
30
|
+
userEvent.type(input, "1");
|
|
31
|
+
input.blur();
|
|
32
|
+
|
|
33
|
+
expect(onChangeMock).toBeCalledWith(
|
|
34
|
+
expect.objectContaining({value: 1}),
|
|
35
|
+
);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it("should be possible to change allow inexact answers", async () => {
|
|
39
|
+
const onChangeMock = jest.fn();
|
|
40
|
+
|
|
41
|
+
render(<InputNumberEditor onChange={onChangeMock} />);
|
|
42
|
+
|
|
43
|
+
userEvent.click(
|
|
44
|
+
screen.getByRole("checkbox", {name: "Allow inexact answers"}),
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
expect(onChangeMock).toBeCalledWith(
|
|
48
|
+
expect.objectContaining({inexact: true}),
|
|
49
|
+
);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it("should be possible to change right alignment", async () => {
|
|
53
|
+
const onChangeMock = jest.fn();
|
|
54
|
+
|
|
55
|
+
render(<InputNumberEditor onChange={onChangeMock} />);
|
|
56
|
+
|
|
57
|
+
userEvent.click(
|
|
58
|
+
screen.getByRole("checkbox", {name: "Right alignment"}),
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
expect(onChangeMock).toBeCalledWith(
|
|
62
|
+
expect.objectContaining({rightAlign: true}),
|
|
63
|
+
);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
const unsimplifiedOptions = ["required", "optional", "enforced"];
|
|
67
|
+
unsimplifiedOptions.forEach((opt) => {
|
|
68
|
+
it(`should be possible to set unsimplified answers to: ${opt}`, async () => {
|
|
69
|
+
const onChangeMock = jest.fn();
|
|
70
|
+
|
|
71
|
+
render(<InputNumberEditor onChange={onChangeMock} />);
|
|
72
|
+
|
|
73
|
+
const select = screen.getByRole("combobox", {
|
|
74
|
+
name: "Unsimplified answers",
|
|
75
|
+
});
|
|
76
|
+
userEvent.selectOptions(select, opt);
|
|
77
|
+
|
|
78
|
+
expect(onChangeMock).toBeCalledWith(
|
|
79
|
+
expect.objectContaining({simplify: opt}),
|
|
80
|
+
);
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
const answerTypeOptions = [
|
|
85
|
+
"number",
|
|
86
|
+
"decimal",
|
|
87
|
+
"integer",
|
|
88
|
+
"rational",
|
|
89
|
+
"improper",
|
|
90
|
+
"mixed",
|
|
91
|
+
"percent",
|
|
92
|
+
"pi",
|
|
93
|
+
];
|
|
94
|
+
answerTypeOptions.forEach((opt) => {
|
|
95
|
+
it(`should be possible to set answer type to: ${opt}`, async () => {
|
|
96
|
+
const onChangeMock = jest.fn();
|
|
97
|
+
|
|
98
|
+
render(<InputNumberEditor onChange={onChangeMock} />);
|
|
99
|
+
|
|
100
|
+
const select = screen.getByRole("combobox", {
|
|
101
|
+
name: "Answer type",
|
|
102
|
+
});
|
|
103
|
+
userEvent.selectOptions(select, opt);
|
|
104
|
+
|
|
105
|
+
expect(onChangeMock).toBeCalledWith(
|
|
106
|
+
expect.objectContaining({answerType: opt}),
|
|
107
|
+
);
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
const sizeOptions = ["normal", "small"];
|
|
112
|
+
sizeOptions.forEach((opt) => {
|
|
113
|
+
it(`should be possible to set unsimplified answers to: ${opt}`, async () => {
|
|
114
|
+
const onChangeMock = jest.fn();
|
|
115
|
+
|
|
116
|
+
render(<InputNumberEditor onChange={onChangeMock} />);
|
|
117
|
+
|
|
118
|
+
const select = screen.getByRole("combobox", {
|
|
119
|
+
name: "Width",
|
|
120
|
+
});
|
|
121
|
+
userEvent.selectOptions(select, opt);
|
|
122
|
+
|
|
123
|
+
expect(onChangeMock).toBeCalledWith(
|
|
124
|
+
expect.objectContaining({size: opt}),
|
|
125
|
+
);
|
|
126
|
+
});
|
|
127
|
+
});
|
|
128
|
+
});
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import {Dependencies} from "@khanacademy/perseus";
|
|
2
|
+
import {render, screen} from "@testing-library/react";
|
|
3
|
+
import userEvent from "@testing-library/user-event";
|
|
4
|
+
import * as React from "react";
|
|
5
|
+
|
|
6
|
+
import "@testing-library/jest-dom";
|
|
7
|
+
|
|
8
|
+
import {testDependencies} from "../../../../../testing/test-dependencies";
|
|
9
|
+
import MatcherEditor from "../matcher-editor";
|
|
10
|
+
|
|
11
|
+
describe("matcher-editor", () => {
|
|
12
|
+
beforeEach(() => {
|
|
13
|
+
jest.spyOn(Dependencies, "getDependencies").mockReturnValue(
|
|
14
|
+
testDependencies,
|
|
15
|
+
);
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it("renders", async () => {
|
|
19
|
+
render(<MatcherEditor onChange={() => {}} />);
|
|
20
|
+
|
|
21
|
+
expect(screen.getByText("Correct answer:")).toBeInTheDocument();
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it("is possible to change option: order of matched pairs matters", async () => {
|
|
25
|
+
const onChangeMock = jest.fn();
|
|
26
|
+
|
|
27
|
+
render(<MatcherEditor onChange={onChangeMock} />);
|
|
28
|
+
|
|
29
|
+
userEvent.click(
|
|
30
|
+
screen.getByRole("checkbox", {
|
|
31
|
+
name: "Order of the matched pairs matters:",
|
|
32
|
+
}),
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
expect(onChangeMock).toBeCalledWith({orderMatters: true});
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it("should be possible to change padding", async () => {
|
|
39
|
+
const onChangeMock = jest.fn();
|
|
40
|
+
|
|
41
|
+
render(<MatcherEditor onChange={onChangeMock} />);
|
|
42
|
+
|
|
43
|
+
userEvent.click(screen.getByRole("checkbox", {name: "Padding:"}));
|
|
44
|
+
|
|
45
|
+
expect(onChangeMock).toBeCalledWith({padding: false});
|
|
46
|
+
});
|
|
47
|
+
});
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import {Dependencies} from "@khanacademy/perseus";
|
|
2
|
+
import {render, screen} from "@testing-library/react";
|
|
3
|
+
import userEvent from "@testing-library/user-event";
|
|
4
|
+
import * as React from "react";
|
|
5
|
+
|
|
6
|
+
import "@testing-library/jest-dom";
|
|
7
|
+
|
|
8
|
+
import {testDependencies} from "../../../../../testing/test-dependencies";
|
|
9
|
+
import NumberLineEditor from "../number-line-editor";
|
|
10
|
+
|
|
11
|
+
describe("number-line-editor", () => {
|
|
12
|
+
beforeEach(() => {
|
|
13
|
+
jest.spyOn(Dependencies, "getDependencies").mockReturnValue(
|
|
14
|
+
testDependencies,
|
|
15
|
+
);
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it("should render", async () => {
|
|
19
|
+
render(<NumberLineEditor onChange={() => undefined} />);
|
|
20
|
+
|
|
21
|
+
expect(await screen.findByText("Correct x")).toBeInTheDocument();
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
const relationships = ["lt", "gt", "le", "ge", "eq"];
|
|
25
|
+
relationships.forEach((rel) => {
|
|
26
|
+
it(`should be possible to set relationship to: ${rel}`, async () => {
|
|
27
|
+
const onChangeMock = jest.fn();
|
|
28
|
+
|
|
29
|
+
render(<NumberLineEditor onChange={onChangeMock} />);
|
|
30
|
+
|
|
31
|
+
const select = screen.getByRole("combobox", {
|
|
32
|
+
name: "Select relationship",
|
|
33
|
+
});
|
|
34
|
+
userEvent.selectOptions(select, rel);
|
|
35
|
+
|
|
36
|
+
expect(onChangeMock).toBeCalledWith(
|
|
37
|
+
expect.objectContaining({correctRel: rel}),
|
|
38
|
+
);
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it("should be possible to update the answer", async () => {
|
|
43
|
+
const onChangeMock = jest.fn();
|
|
44
|
+
|
|
45
|
+
render(<NumberLineEditor onChange={onChangeMock} />);
|
|
46
|
+
|
|
47
|
+
const input = screen.getByPlaceholderText("answer");
|
|
48
|
+
userEvent.type(input, "1");
|
|
49
|
+
|
|
50
|
+
expect(onChangeMock).toBeCalledWith({correctX: 1});
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it("should be possible to update position", async () => {
|
|
54
|
+
const onChangeMock = jest.fn();
|
|
55
|
+
|
|
56
|
+
render(<NumberLineEditor onChange={onChangeMock} />);
|
|
57
|
+
|
|
58
|
+
const input = screen.getByRole("textbox", {name: "Position: ∈"});
|
|
59
|
+
userEvent.type(input, "1");
|
|
60
|
+
|
|
61
|
+
expect(onChangeMock).toBeCalledWith({initialX: 1});
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
const styles = {
|
|
65
|
+
decimal: "Decimals",
|
|
66
|
+
improper: "Improper fractions",
|
|
67
|
+
mixed: "Mixed numbers",
|
|
68
|
+
"non-reduced": "Non-reduced",
|
|
69
|
+
};
|
|
70
|
+
Object.entries(styles).forEach(([key, title]) => {
|
|
71
|
+
it(`should be possible to update style: ${title}`, async () => {
|
|
72
|
+
const onChangeMock = jest.fn();
|
|
73
|
+
|
|
74
|
+
render(<NumberLineEditor onChange={onChangeMock} />);
|
|
75
|
+
|
|
76
|
+
userEvent.click(screen.getByTitle(title));
|
|
77
|
+
|
|
78
|
+
expect(onChangeMock).toBeCalledWith({labelStyle: key});
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it("should be possible to change show tick controller", async () => {
|
|
83
|
+
const onChangeMock = jest.fn();
|
|
84
|
+
|
|
85
|
+
render(<NumberLineEditor onChange={onChangeMock} />);
|
|
86
|
+
|
|
87
|
+
userEvent.click(
|
|
88
|
+
screen.getByRole("checkbox", {name: "Show tick controller"}),
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
expect(onChangeMock).toBeCalledWith({isTickCtrl: true});
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it("should be possible to change show label tickets", async () => {
|
|
95
|
+
const onChangeMock = jest.fn();
|
|
96
|
+
|
|
97
|
+
render(<NumberLineEditor onChange={onChangeMock} />);
|
|
98
|
+
|
|
99
|
+
userEvent.click(
|
|
100
|
+
screen.getByRole("checkbox", {name: "Show label ticks"}),
|
|
101
|
+
);
|
|
102
|
+
|
|
103
|
+
expect(onChangeMock).toBeCalledWith({labelTicks: false});
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
it("should be possible to change show tooltips", async () => {
|
|
107
|
+
const onChangeMock = jest.fn();
|
|
108
|
+
|
|
109
|
+
render(<NumberLineEditor onChange={onChangeMock} />);
|
|
110
|
+
|
|
111
|
+
userEvent.click(screen.getByRole("checkbox", {name: "Show tooltips"}));
|
|
112
|
+
|
|
113
|
+
expect(onChangeMock).toBeCalledWith({showTooltips: true});
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
it("should be possible to update tick steps", async () => {
|
|
117
|
+
const onChangeMock = jest.fn();
|
|
118
|
+
|
|
119
|
+
render(<NumberLineEditor onChange={onChangeMock} />);
|
|
120
|
+
|
|
121
|
+
const input = screen.getByRole("textbox", {
|
|
122
|
+
name: "or tick step:",
|
|
123
|
+
});
|
|
124
|
+
userEvent.type(input, "6");
|
|
125
|
+
|
|
126
|
+
expect(onChangeMock).toBeCalledWith({
|
|
127
|
+
numDivisions: null,
|
|
128
|
+
tickStep: 6,
|
|
129
|
+
});
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
it("should be possible to update snap divisions", async () => {
|
|
133
|
+
const onChangeMock = jest.fn();
|
|
134
|
+
|
|
135
|
+
render(<NumberLineEditor onChange={onChangeMock} />);
|
|
136
|
+
|
|
137
|
+
const input = screen.getByRole("textbox", {
|
|
138
|
+
name: "Snap increments per tick:",
|
|
139
|
+
});
|
|
140
|
+
userEvent.type(input, "6");
|
|
141
|
+
|
|
142
|
+
expect(onChangeMock).toBeCalledWith({snapDivisions: 26});
|
|
143
|
+
});
|
|
144
|
+
});
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import {Dependencies} from "@khanacademy/perseus";
|
|
2
|
+
import {render, screen} from "@testing-library/react";
|
|
3
|
+
import userEvent from "@testing-library/user-event";
|
|
4
|
+
import * as React from "react";
|
|
5
|
+
|
|
6
|
+
import "@testing-library/jest-dom";
|
|
7
|
+
|
|
8
|
+
import {testDependencies} from "../../../../../testing/test-dependencies";
|
|
9
|
+
import NumericInputEditor from "../numeric-input-editor";
|
|
10
|
+
|
|
11
|
+
describe("numeric-input-editor", () => {
|
|
12
|
+
beforeEach(() => {
|
|
13
|
+
jest.spyOn(Dependencies, "getDependencies").mockReturnValue(
|
|
14
|
+
testDependencies,
|
|
15
|
+
);
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it("should render", async () => {
|
|
19
|
+
render(<NumericInputEditor onChange={() => undefined} />);
|
|
20
|
+
|
|
21
|
+
expect(await screen.findByText(/Add new answer/)).toBeInTheDocument();
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it("should be possible to select normal width", async () => {
|
|
25
|
+
const onChangeMock = jest.fn();
|
|
26
|
+
|
|
27
|
+
render(<NumericInputEditor onChange={onChangeMock} />);
|
|
28
|
+
|
|
29
|
+
userEvent.click(screen.getByRole("button", {name: "Normal (80px)"}));
|
|
30
|
+
|
|
31
|
+
expect(onChangeMock).toBeCalledWith(
|
|
32
|
+
expect.objectContaining({size: "normal"}),
|
|
33
|
+
undefined,
|
|
34
|
+
);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it("should be possible to select small width", async () => {
|
|
38
|
+
const onChangeMock = jest.fn();
|
|
39
|
+
|
|
40
|
+
render(<NumericInputEditor onChange={onChangeMock} />);
|
|
41
|
+
|
|
42
|
+
userEvent.click(screen.getByRole("button", {name: "Small (40px)"}));
|
|
43
|
+
|
|
44
|
+
expect(onChangeMock).toBeCalledWith(
|
|
45
|
+
expect.objectContaining({size: "small"}),
|
|
46
|
+
undefined,
|
|
47
|
+
);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it("should be possible to select right alignment", async () => {
|
|
51
|
+
const onChangeMock = jest.fn();
|
|
52
|
+
|
|
53
|
+
render(<NumericInputEditor onChange={onChangeMock} />);
|
|
54
|
+
|
|
55
|
+
userEvent.click(
|
|
56
|
+
screen.getByRole("checkbox", {name: "Right alignment"}),
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
expect(onChangeMock).toBeCalledWith({rightAlign: true});
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it("should be possible to select coefficient", async () => {
|
|
63
|
+
const onChangeMock = jest.fn();
|
|
64
|
+
|
|
65
|
+
render(<NumericInputEditor onChange={onChangeMock} />);
|
|
66
|
+
|
|
67
|
+
userEvent.click(screen.getByRole("checkbox", {name: "Coefficient"}));
|
|
68
|
+
|
|
69
|
+
expect(onChangeMock).toBeCalledWith({coefficient: true});
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it("should be possible to update label text", async () => {
|
|
73
|
+
const onChangeMock = jest.fn();
|
|
74
|
+
|
|
75
|
+
render(<NumericInputEditor onChange={onChangeMock} />);
|
|
76
|
+
|
|
77
|
+
const input = screen.getByRole("textbox", {
|
|
78
|
+
name: "Label text:",
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
userEvent.type(input, "a");
|
|
82
|
+
|
|
83
|
+
expect(onChangeMock).toBeCalledWith(
|
|
84
|
+
expect.objectContaining({labelText: "a"}),
|
|
85
|
+
undefined,
|
|
86
|
+
);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it("should be possible to toggle options", async () => {
|
|
90
|
+
render(<NumericInputEditor onChange={() => {}} />);
|
|
91
|
+
|
|
92
|
+
userEvent.click(screen.getByRole("link", {name: "Toggle options"}));
|
|
93
|
+
|
|
94
|
+
expect(
|
|
95
|
+
screen.getByText("Unsimplified answers are"),
|
|
96
|
+
).toBeInTheDocument();
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it("should be possible to set unsimplified answers to ungraded", async () => {
|
|
100
|
+
const onChangeMock = jest.fn();
|
|
101
|
+
|
|
102
|
+
render(<NumericInputEditor onChange={onChangeMock} />);
|
|
103
|
+
|
|
104
|
+
userEvent.click(screen.getByRole("link", {name: "Toggle options"}));
|
|
105
|
+
userEvent.click(screen.getByRole("button", {name: "ungraded"}));
|
|
106
|
+
|
|
107
|
+
expect(onChangeMock).toBeCalledWith(
|
|
108
|
+
expect.objectContaining({
|
|
109
|
+
answers: expect.arrayContaining([
|
|
110
|
+
expect.objectContaining({simplify: "required"}),
|
|
111
|
+
]),
|
|
112
|
+
}),
|
|
113
|
+
);
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
it("should be possible to set unsimplified answers to accepted", async () => {
|
|
117
|
+
const onChangeMock = jest.fn();
|
|
118
|
+
|
|
119
|
+
render(<NumericInputEditor onChange={onChangeMock} />);
|
|
120
|
+
|
|
121
|
+
userEvent.click(screen.getByRole("link", {name: "Toggle options"}));
|
|
122
|
+
userEvent.click(screen.getByRole("button", {name: "accepted"}));
|
|
123
|
+
|
|
124
|
+
expect(onChangeMock).toBeCalledWith(
|
|
125
|
+
expect.objectContaining({
|
|
126
|
+
answers: expect.arrayContaining([
|
|
127
|
+
expect.objectContaining({simplify: "optional"}),
|
|
128
|
+
]),
|
|
129
|
+
}),
|
|
130
|
+
);
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
it("should be possible to set unsimplified answers to wrong", async () => {
|
|
134
|
+
const onChangeMock = jest.fn();
|
|
135
|
+
|
|
136
|
+
render(<NumericInputEditor onChange={onChangeMock} />);
|
|
137
|
+
|
|
138
|
+
userEvent.click(screen.getByRole("link", {name: "Toggle options"}));
|
|
139
|
+
userEvent.click(screen.getByRole("button", {name: "wrong"}));
|
|
140
|
+
|
|
141
|
+
expect(onChangeMock).toBeCalledWith(
|
|
142
|
+
expect.objectContaining({
|
|
143
|
+
answers: expect.arrayContaining([
|
|
144
|
+
expect.objectContaining({simplify: "enforced"}),
|
|
145
|
+
]),
|
|
146
|
+
}),
|
|
147
|
+
);
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
const answerFormats = {
|
|
151
|
+
integer: "Integers",
|
|
152
|
+
decimal: "Decimals",
|
|
153
|
+
proper: "Proper fractions",
|
|
154
|
+
improper: "Improper fractions",
|
|
155
|
+
mixed: "Mixed numbers",
|
|
156
|
+
pi: "Numbers with π",
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
Object.entries(answerFormats).forEach(([key, name]) => {
|
|
160
|
+
it(`should be possible to set suggested answer format to: ${name}`, async () => {
|
|
161
|
+
const onChangeMock = jest.fn();
|
|
162
|
+
|
|
163
|
+
render(<NumericInputEditor onChange={onChangeMock} />);
|
|
164
|
+
|
|
165
|
+
userEvent.click(screen.getByRole("link", {name: "Toggle options"}));
|
|
166
|
+
userEvent.click(screen.getByTitle(name));
|
|
167
|
+
|
|
168
|
+
expect(onChangeMock).toBeCalledWith(
|
|
169
|
+
expect.objectContaining({
|
|
170
|
+
answers: expect.arrayContaining([
|
|
171
|
+
expect.objectContaining({answerForms: [key]}),
|
|
172
|
+
]),
|
|
173
|
+
}),
|
|
174
|
+
);
|
|
175
|
+
});
|
|
176
|
+
});
|
|
177
|
+
});
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import {Dependencies, ApiOptions} from "@khanacademy/perseus";
|
|
2
|
+
import {render, screen} from "@testing-library/react";
|
|
3
|
+
import userEvent from "@testing-library/user-event";
|
|
4
|
+
import * as React from "react";
|
|
5
|
+
|
|
6
|
+
import "@testing-library/jest-dom";
|
|
7
|
+
|
|
8
|
+
import {testDependencies} from "../../../../../testing/test-dependencies";
|
|
9
|
+
import RadioEditor from "../radio/editor";
|
|
10
|
+
|
|
11
|
+
describe("radio-editor", () => {
|
|
12
|
+
beforeEach(() => {
|
|
13
|
+
jest.spyOn(Dependencies, "getDependencies").mockReturnValue(
|
|
14
|
+
testDependencies,
|
|
15
|
+
);
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it("should render", async () => {
|
|
19
|
+
render(
|
|
20
|
+
<RadioEditor
|
|
21
|
+
onChange={() => undefined}
|
|
22
|
+
apiOptions={ApiOptions.defaults}
|
|
23
|
+
/>,
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
expect(
|
|
27
|
+
await screen.findByText(/Multiple selections/),
|
|
28
|
+
).toBeInTheDocument();
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it("should toggle multiple select checkbox", () => {
|
|
32
|
+
const onChangeMock = jest.fn();
|
|
33
|
+
|
|
34
|
+
render(
|
|
35
|
+
<RadioEditor
|
|
36
|
+
onChange={onChangeMock}
|
|
37
|
+
apiOptions={ApiOptions.defaults}
|
|
38
|
+
/>,
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
userEvent.click(
|
|
42
|
+
screen.getByRole("checkbox", {
|
|
43
|
+
name: "Multiple selections",
|
|
44
|
+
}),
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
expect(onChangeMock).toBeCalledWith({multipleSelect: true});
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it("should toggle randomize order checkbox", () => {
|
|
51
|
+
const onChangeMock = jest.fn();
|
|
52
|
+
|
|
53
|
+
render(
|
|
54
|
+
<RadioEditor
|
|
55
|
+
onChange={onChangeMock}
|
|
56
|
+
apiOptions={ApiOptions.defaults}
|
|
57
|
+
/>,
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
userEvent.click(
|
|
61
|
+
screen.getByRole("checkbox", {
|
|
62
|
+
name: "Randomize order",
|
|
63
|
+
}),
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
expect(onChangeMock).toBeCalledWith({randomize: true});
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it("should be possible to add answer", () => {
|
|
70
|
+
const onChangeMock = jest.fn();
|
|
71
|
+
|
|
72
|
+
render(
|
|
73
|
+
<RadioEditor
|
|
74
|
+
onChange={onChangeMock}
|
|
75
|
+
apiOptions={ApiOptions.defaults}
|
|
76
|
+
/>,
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
userEvent.click(
|
|
80
|
+
screen.getAllByRole("link", {
|
|
81
|
+
name: "Add a choice",
|
|
82
|
+
})[0],
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
expect(onChangeMock).toBeCalledWith(
|
|
86
|
+
expect.objectContaining({
|
|
87
|
+
choices: [{}, {}, {isNoneOfTheAbove: false}],
|
|
88
|
+
hasNoneOfTheAbove: false,
|
|
89
|
+
}),
|
|
90
|
+
// there's some anonymous function that's also passed
|
|
91
|
+
expect.anything(),
|
|
92
|
+
);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
it("should be possible to delete answer", () => {
|
|
96
|
+
const onChangeMock = jest.fn();
|
|
97
|
+
|
|
98
|
+
render(
|
|
99
|
+
<RadioEditor
|
|
100
|
+
onChange={onChangeMock}
|
|
101
|
+
apiOptions={ApiOptions.defaults}
|
|
102
|
+
/>,
|
|
103
|
+
);
|
|
104
|
+
|
|
105
|
+
userEvent.click(
|
|
106
|
+
screen.getAllByRole("link", {
|
|
107
|
+
name: "Remove this choice",
|
|
108
|
+
})[0],
|
|
109
|
+
);
|
|
110
|
+
|
|
111
|
+
expect(onChangeMock).toBeCalledWith(
|
|
112
|
+
expect.objectContaining({
|
|
113
|
+
choices: [{}],
|
|
114
|
+
hasNoneOfTheAbove: false,
|
|
115
|
+
}),
|
|
116
|
+
);
|
|
117
|
+
});
|
|
118
|
+
});
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import {Dependencies} from "@khanacademy/perseus";
|
|
2
|
+
import {render, screen} from "@testing-library/react";
|
|
3
|
+
import userEvent from "@testing-library/user-event";
|
|
4
|
+
import * as React from "react";
|
|
5
|
+
|
|
6
|
+
import "@testing-library/jest-dom";
|
|
7
|
+
|
|
8
|
+
import {testDependencies} from "../../../../../testing/test-dependencies";
|
|
9
|
+
import SorterEditor from "../sorter-editor";
|
|
10
|
+
|
|
11
|
+
describe("sorter-editor", () => {
|
|
12
|
+
beforeEach(() => {
|
|
13
|
+
jest.spyOn(Dependencies, "getDependencies").mockReturnValue(
|
|
14
|
+
testDependencies,
|
|
15
|
+
);
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it("renders", async () => {
|
|
19
|
+
render(<SorterEditor onChange={() => {}} />);
|
|
20
|
+
|
|
21
|
+
expect(screen.getByText("Correct answer:")).toBeInTheDocument();
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it("should be possible to change layout to vertical", async () => {
|
|
25
|
+
const onChangeMock = jest.fn();
|
|
26
|
+
|
|
27
|
+
render(<SorterEditor onChange={onChangeMock} />);
|
|
28
|
+
|
|
29
|
+
const select = screen.getByRole("combobox", {name: "Layout:"});
|
|
30
|
+
userEvent.selectOptions(select, "vertical");
|
|
31
|
+
|
|
32
|
+
expect(onChangeMock).toBeCalledWith({layout: "vertical"});
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it("should be possible to change layout to horizontal", async () => {
|
|
36
|
+
const onChangeMock = jest.fn();
|
|
37
|
+
|
|
38
|
+
render(<SorterEditor onChange={onChangeMock} />);
|
|
39
|
+
|
|
40
|
+
const select = screen.getByRole("combobox", {name: "Layout:"});
|
|
41
|
+
userEvent.selectOptions(select, "horizontal");
|
|
42
|
+
|
|
43
|
+
expect(onChangeMock).toBeCalledWith({layout: "horizontal"});
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it("should be possible to change padding", async () => {
|
|
47
|
+
const onChangeMock = jest.fn();
|
|
48
|
+
|
|
49
|
+
render(<SorterEditor onChange={onChangeMock} />);
|
|
50
|
+
|
|
51
|
+
userEvent.click(screen.getByRole("checkbox", {name: "Padding:"}));
|
|
52
|
+
|
|
53
|
+
expect(onChangeMock).toBeCalledWith({padding: false});
|
|
54
|
+
});
|
|
55
|
+
});
|
|
@@ -174,6 +174,7 @@ class DropdownEditor extends React.Component<Props> {
|
|
|
174
174
|
<a
|
|
175
175
|
href="#"
|
|
176
176
|
className="simple-button orange"
|
|
177
|
+
aria-label="Delete choice"
|
|
177
178
|
// eslint-disable-next-line react/jsx-no-bind
|
|
178
179
|
// @ts-expect-error - TS2683 - 'this' implicitly has type 'any' because it does not have a type annotation.
|
|
179
180
|
onClick={this.removeChoice.bind(
|
|
@@ -186,6 +186,7 @@ class InputNumberEditor extends React.Component<Props> {
|
|
|
186
186
|
type="text"
|
|
187
187
|
disabled={!this.props.inexact}
|
|
188
188
|
defaultValue={this.props.maxError}
|
|
189
|
+
aria-label="Max error"
|
|
189
190
|
onBlur={(e) => {
|
|
190
191
|
const ans =
|
|
191
192
|
"" +
|
|
@@ -206,6 +207,7 @@ class InputNumberEditor extends React.Component<Props> {
|
|
|
206
207
|
// @ts-expect-error - TS2322 - Type 'string' is not assignable to type '"number" | "integer" | "mixed" | "decimal" | "improper" | "percent" | "pi" | "rational" | undefined'.
|
|
207
208
|
this.props.onChange({answerType: e.target.value});
|
|
208
209
|
}}
|
|
210
|
+
aria-label="Answer type"
|
|
209
211
|
>
|
|
210
212
|
{answerTypeOptions}
|
|
211
213
|
</select>
|