@khanacademy/perseus 12.1.0 → 12.3.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.
- package/CHANGELOG.md +31 -0
- package/dist/article-renderer.d.ts +5 -2
- package/dist/error-boundary.d.ts +1 -0
- package/dist/es/index.css +4 -66
- package/dist/es/index.js +112 -87
- package/dist/es/index.js.map +1 -1
- package/dist/index.css +4 -66
- package/dist/index.js +103 -75
- package/dist/index.js.map +1 -1
- package/dist/renderer.d.ts +4 -1
- package/dist/styles/constants.d.ts +2 -2
- package/dist/widget-container.d.ts +2 -0
- package/dist/widgets/expression.d.ts +5 -4
- package/package.json +8 -8
- package/src/__stories__/article-renderer.stories.tsx +28 -2
- package/src/__testdata__/article-renderer.testdata.ts +85 -0
- package/src/__tests__/article-renderer.test.tsx +131 -0
- package/src/__tests__/renderer-api.test.tsx +18 -23
- package/src/article-renderer.tsx +73 -58
- package/src/error-boundary.tsx +3 -0
- package/src/multi-items/__tests__/__snapshots__/multi-renderer.test.tsx.snap +5 -5
- package/src/renderer.tsx +29 -27
- package/src/server-item-renderer.tsx +1 -0
- package/src/styles/constants.ts +4 -4
- package/src/styles/perseus-renderer.less +1 -10
- package/src/styles/variables.less +2 -2
- package/src/styles/variables.shared.less +2 -2
- package/src/styles/widgets/expression.less +0 -77
- package/src/styles/widgets/passage.less +1 -19
- package/src/widget-container.tsx +32 -10
- package/src/widgets/__stories__/label-image.stories.tsx +30 -0
- package/src/widgets/__testdata__/label-image.testdata.ts +63 -0
- package/src/widgets/__tests__/__snapshots__/group.test.tsx.snap +5 -5
- package/src/widgets/__tests__/__snapshots__/radio.test.ts.snap +28 -28
- package/src/widgets/__tests__/expression.test.tsx +12 -9
- package/src/widgets/__tests__/renderQuestion.tsx +30 -13
- package/src/widgets/expression.tsx +19 -25
- package/src/widgets/transformer.tsx +1 -2
- package/tsconfig-build.tsbuildinfo +1 -1
package/dist/renderer.d.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
import InteractionTracker from "./interaction-tracker";
|
|
3
3
|
import TranslationLinter from "./translation-linter";
|
|
4
|
+
import WidgetContainer from "./widget-container";
|
|
5
|
+
import type { DependenciesContext } from "./dependencies";
|
|
4
6
|
import type { PerseusRenderer, PerseusWidgetOptions } from "./perseus-types";
|
|
5
7
|
import type { APIOptions, APIOptionsWithDefaults, FilterCriterion, FocusPath, PerseusScore, WidgetInfo, WidgetProps } from "./types";
|
|
6
8
|
import type { LinterContextProps } from "@khanacademy/perseus-linter";
|
|
@@ -37,7 +39,7 @@ export type Widget = {
|
|
|
37
39
|
showRationalesForCurrentlySelectedChoices?: (options?: any) => void;
|
|
38
40
|
examples?: () => ReadonlyArray<string>;
|
|
39
41
|
};
|
|
40
|
-
type Props = {
|
|
42
|
+
type Props = Partial<React.ContextType<typeof DependenciesContext>> & {
|
|
41
43
|
apiOptions?: APIOptions;
|
|
42
44
|
alwaysUpdate?: boolean;
|
|
43
45
|
findExternalWidgets: any;
|
|
@@ -93,6 +95,7 @@ declare class Renderer extends React.Component<Props, State> {
|
|
|
93
95
|
_isMounted: boolean;
|
|
94
96
|
_isTwoColumn: boolean;
|
|
95
97
|
_translationLinter: TranslationLinter;
|
|
98
|
+
_widgetContainers: Map<string, WidgetContainer>;
|
|
96
99
|
lastRenderedMarkdown: React.ReactNode;
|
|
97
100
|
reuseMarkdown: boolean;
|
|
98
101
|
translationIndex: number;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
export declare const baseFontFamily = "'
|
|
2
|
-
export declare const boldFontFamily = "'
|
|
1
|
+
export declare const baseFontFamily = "'Lato', sans-serif";
|
|
2
|
+
export declare const boldFontFamily = "'Lato-Bold', 'Lato', sans-serif";
|
|
3
3
|
export declare const green = "#76A005";
|
|
4
4
|
export declare const kaGreen = "#71B307";
|
|
5
5
|
export declare const blue = "#1C758A";
|
|
@@ -5,6 +5,7 @@ import type { LinterContextProps } from "@khanacademy/perseus-linter";
|
|
|
5
5
|
type Props = {
|
|
6
6
|
shouldHighlight: boolean;
|
|
7
7
|
type: string;
|
|
8
|
+
id: string;
|
|
8
9
|
initialProps: WidgetProps<any, PerseusWidgetOptions>;
|
|
9
10
|
linterContext: LinterContextProps;
|
|
10
11
|
};
|
|
@@ -16,6 +17,7 @@ type State = {
|
|
|
16
17
|
widgetProps: WidgetProps<any, PerseusWidgetOptions>;
|
|
17
18
|
};
|
|
18
19
|
declare class WidgetContainer extends React.Component<Props, State> {
|
|
20
|
+
widgetRef: React.RefObject<React.ComponentType<any>>;
|
|
19
21
|
static defaultProps: DefaultProps;
|
|
20
22
|
state: any;
|
|
21
23
|
componentDidMount(): void;
|
|
@@ -51,7 +51,9 @@ export declare class Expression extends React.Component<Props, ExpressionState>
|
|
|
51
51
|
componentDidUpdate: (prevProps: Props) => void;
|
|
52
52
|
componentWillUnmount: () => void;
|
|
53
53
|
_handleMouseDown: () => void;
|
|
54
|
-
simpleValidate: (rubric: Rubric
|
|
54
|
+
simpleValidate: (rubric: Rubric & {
|
|
55
|
+
scoring?: boolean;
|
|
56
|
+
}, onInputError: OnInputErrorFunctionType) => PerseusScore;
|
|
55
57
|
getUserInput: () => string;
|
|
56
58
|
change: (...args: any) => any | undefined;
|
|
57
59
|
parse: (value: string, props: Props) => any;
|
|
@@ -65,7 +67,6 @@ export declare class Expression extends React.Component<Props, ExpressionState>
|
|
|
65
67
|
getInputPaths: () => ReadonlyArray<ReadonlyArray<any>>;
|
|
66
68
|
getGrammarTypeForPath: (inputPath: string) => string;
|
|
67
69
|
setInputValue: (path: string, newValue: string, cb: () => void) => void;
|
|
68
|
-
sendExpressionEvaluatedEvent(result: "correct" | "incorrect" | "invalid"): void;
|
|
69
70
|
render(): React.ReactNode | React.ReactElement<React.ComponentProps<"div">>;
|
|
70
71
|
}
|
|
71
72
|
/**
|
|
@@ -213,7 +214,7 @@ declare const _default: Readonly<{
|
|
|
213
214
|
onFocus: (blurPath: import("../types").FocusPath) => void;
|
|
214
215
|
times: boolean;
|
|
215
216
|
value: string;
|
|
216
|
-
}, "static" | "trackInteraction" | "onChange" | "
|
|
217
|
+
}, "static" | "trackInteraction" | "onChange" | "analytics" | "problemNum" | "questionCompleted" | "keypadElement" | "alignment" | "buttonsVisible" | "containerSizeClass" | "widgetId" | "findWidgets" | "keypadConfiguration" | "reviewModeRubric" | "isLastUsedWidget"> & {
|
|
217
218
|
value?: string | undefined;
|
|
218
219
|
onFocus?: ((blurPath: import("../types").FocusPath) => void) | undefined;
|
|
219
220
|
onBlur?: ((blurPath: import("../types").FocusPath) => void) | undefined;
|
|
@@ -419,7 +420,7 @@ declare const _default: Readonly<{
|
|
|
419
420
|
onFocus: (blurPath: import("../types").FocusPath) => void;
|
|
420
421
|
times: boolean;
|
|
421
422
|
value: string;
|
|
422
|
-
}, "static" | "trackInteraction" | "onChange" | "
|
|
423
|
+
}, "static" | "trackInteraction" | "onChange" | "analytics" | "problemNum" | "questionCompleted" | "keypadElement" | "alignment" | "buttonsVisible" | "containerSizeClass" | "widgetId" | "findWidgets" | "keypadConfiguration" | "reviewModeRubric" | "isLastUsedWidget"> & {
|
|
423
424
|
value?: string | undefined;
|
|
424
425
|
onFocus?: ((blurPath: import("../types").FocusPath) => void) | undefined;
|
|
425
426
|
onBlur?: ((blurPath: import("../types").FocusPath) => void) | undefined;
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"description": "Core Perseus API (includes renderers and widgets)",
|
|
4
4
|
"author": "Khan Academy",
|
|
5
5
|
"license": "MIT",
|
|
6
|
-
"version": "12.
|
|
6
|
+
"version": "12.3.0",
|
|
7
7
|
"publishConfig": {
|
|
8
8
|
"access": "public"
|
|
9
9
|
},
|
|
@@ -22,13 +22,13 @@
|
|
|
22
22
|
"test": "bash -c 'yarn --silent --cwd \"../..\" test ${@:0} $($([[ ${@: -1} = -* ]] || [[ ${@: -1} = bash ]]) && echo $PWD)'"
|
|
23
23
|
},
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"@khanacademy/kas": "^0.3.
|
|
26
|
-
"@khanacademy/kmath": "^0.1.
|
|
27
|
-
"@khanacademy/math-input": "^14.2.
|
|
28
|
-
"@khanacademy/perseus-core": "1.
|
|
29
|
-
"@khanacademy/perseus-linter": "^0.3.
|
|
30
|
-
"@khanacademy/pure-markdown": "^0.2.
|
|
31
|
-
"@khanacademy/simple-markdown": "^0.10.
|
|
25
|
+
"@khanacademy/kas": "^0.3.4",
|
|
26
|
+
"@khanacademy/kmath": "^0.1.5",
|
|
27
|
+
"@khanacademy/math-input": "^14.2.1",
|
|
28
|
+
"@khanacademy/perseus-core": "1.2.0",
|
|
29
|
+
"@khanacademy/perseus-linter": "^0.3.7",
|
|
30
|
+
"@khanacademy/pure-markdown": "^0.2.9",
|
|
31
|
+
"@khanacademy/simple-markdown": "^0.10.3"
|
|
32
32
|
},
|
|
33
33
|
"devDependencies": {
|
|
34
34
|
"@khanacademy/wonder-blocks-button": "^4.1.3",
|
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
multiSectionArticle,
|
|
8
8
|
passageArticle,
|
|
9
9
|
articleWithExpression,
|
|
10
|
+
multiSectionArticleWithExpression,
|
|
10
11
|
} from "../__testdata__/article-renderer.testdata";
|
|
11
12
|
import ArticleRenderer from "../article-renderer";
|
|
12
13
|
import TestKeypadContextWrapper from "../widgets/__stories__/test-keypad-context-wrapper";
|
|
@@ -55,7 +56,7 @@ export const PassageArticle = ({useNewStyles}): any => (
|
|
|
55
56
|
export const ExpressionArticle = ({useNewStyles}): any => (
|
|
56
57
|
<TestKeypadContextWrapper>
|
|
57
58
|
<KeypadContext.Consumer>
|
|
58
|
-
{({keypadElement, setRenderer
|
|
59
|
+
{({keypadElement, setRenderer}) => (
|
|
59
60
|
<ArticleRenderer
|
|
60
61
|
ref={(node) => {
|
|
61
62
|
setRenderer(node);
|
|
@@ -63,7 +64,32 @@ export const ExpressionArticle = ({useNewStyles}): any => (
|
|
|
63
64
|
json={articleWithExpression}
|
|
64
65
|
dependencies={storybookDependenciesV2}
|
|
65
66
|
useNewStyles={useNewStyles}
|
|
66
|
-
apiOptions={{
|
|
67
|
+
apiOptions={{
|
|
68
|
+
isMobile: true,
|
|
69
|
+
customKeypad: true,
|
|
70
|
+
}}
|
|
71
|
+
keypadElement={keypadElement}
|
|
72
|
+
/>
|
|
73
|
+
)}
|
|
74
|
+
</KeypadContext.Consumer>
|
|
75
|
+
</TestKeypadContextWrapper>
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
export const MultiSectionedExpressionArticle = ({useNewStyles}): any => (
|
|
79
|
+
<TestKeypadContextWrapper>
|
|
80
|
+
<KeypadContext.Consumer>
|
|
81
|
+
{({keypadElement, setRenderer}) => (
|
|
82
|
+
<ArticleRenderer
|
|
83
|
+
ref={(node) => {
|
|
84
|
+
setRenderer(node);
|
|
85
|
+
}}
|
|
86
|
+
json={multiSectionArticleWithExpression}
|
|
87
|
+
dependencies={storybookDependenciesV2}
|
|
88
|
+
useNewStyles={useNewStyles}
|
|
89
|
+
apiOptions={{
|
|
90
|
+
isMobile: true,
|
|
91
|
+
customKeypad: true,
|
|
92
|
+
}}
|
|
67
93
|
keypadElement={keypadElement}
|
|
68
94
|
/>
|
|
69
95
|
)}
|
|
@@ -125,3 +125,88 @@ export const multiSectionArticle: ReadonlyArray<PerseusRenderer> = [
|
|
|
125
125
|
widgets: {},
|
|
126
126
|
},
|
|
127
127
|
];
|
|
128
|
+
|
|
129
|
+
export const multiSectionArticleWithExpression: ReadonlyArray<PerseusRenderer> =
|
|
130
|
+
[
|
|
131
|
+
{
|
|
132
|
+
content:
|
|
133
|
+
"### Practice Problem\n\n$8\\cdot(11i+2)=$ [[☃ expression 1]] \n*Your answer should be a complex number in the form $a+bi$ where $a$ and $b$ are real numbers.*",
|
|
134
|
+
images: {},
|
|
135
|
+
widgets: {
|
|
136
|
+
"expression 1": {
|
|
137
|
+
alignment: "default",
|
|
138
|
+
graded: true,
|
|
139
|
+
options: {
|
|
140
|
+
answerForms: [
|
|
141
|
+
{
|
|
142
|
+
considered: "correct",
|
|
143
|
+
form: true,
|
|
144
|
+
simplify: false,
|
|
145
|
+
value: "16+88i",
|
|
146
|
+
},
|
|
147
|
+
],
|
|
148
|
+
buttonSets: ["basic"],
|
|
149
|
+
functions: ["f", "g", "h"],
|
|
150
|
+
times: false,
|
|
151
|
+
},
|
|
152
|
+
static: false,
|
|
153
|
+
type: "expression",
|
|
154
|
+
version: {major: 1, minor: 0},
|
|
155
|
+
},
|
|
156
|
+
},
|
|
157
|
+
},
|
|
158
|
+
{
|
|
159
|
+
content:
|
|
160
|
+
"### Practice Problem\n\n$8\\cdot(11i+2)=$ [[☃ expression 2]] \n*Your answer should be a complex number in the form $a+bi$ where $a$ and $b$ are real numbers.*",
|
|
161
|
+
images: {},
|
|
162
|
+
widgets: {
|
|
163
|
+
"expression 2": {
|
|
164
|
+
alignment: "default",
|
|
165
|
+
graded: true,
|
|
166
|
+
options: {
|
|
167
|
+
answerForms: [
|
|
168
|
+
{
|
|
169
|
+
considered: "correct",
|
|
170
|
+
form: true,
|
|
171
|
+
simplify: false,
|
|
172
|
+
value: "16+88i",
|
|
173
|
+
},
|
|
174
|
+
],
|
|
175
|
+
buttonSets: ["basic"],
|
|
176
|
+
functions: ["f", "g", "h"],
|
|
177
|
+
times: false,
|
|
178
|
+
},
|
|
179
|
+
static: false,
|
|
180
|
+
type: "expression",
|
|
181
|
+
version: {major: 1, minor: 0},
|
|
182
|
+
},
|
|
183
|
+
},
|
|
184
|
+
},
|
|
185
|
+
{
|
|
186
|
+
content:
|
|
187
|
+
"### Practice Problem\n\n$8\\cdot(11i+2)=$ [[☃ expression 3]] \n*Your answer should be a complex number in the form $a+bi$ where $a$ and $b$ are real numbers.*",
|
|
188
|
+
images: {},
|
|
189
|
+
widgets: {
|
|
190
|
+
"expression 3": {
|
|
191
|
+
alignment: "default",
|
|
192
|
+
graded: true,
|
|
193
|
+
options: {
|
|
194
|
+
answerForms: [
|
|
195
|
+
{
|
|
196
|
+
considered: "correct",
|
|
197
|
+
form: true,
|
|
198
|
+
simplify: false,
|
|
199
|
+
value: "16+88i",
|
|
200
|
+
},
|
|
201
|
+
],
|
|
202
|
+
buttonSets: ["basic"],
|
|
203
|
+
functions: ["f", "g", "h"],
|
|
204
|
+
times: false,
|
|
205
|
+
},
|
|
206
|
+
static: false,
|
|
207
|
+
type: "expression",
|
|
208
|
+
version: {major: 1, minor: 0},
|
|
209
|
+
},
|
|
210
|
+
},
|
|
211
|
+
},
|
|
212
|
+
];
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import {
|
|
2
|
+
StatefulKeypadContextProvider,
|
|
3
|
+
KeypadContext,
|
|
4
|
+
} from "@khanacademy/math-input";
|
|
5
|
+
import {RenderStateRoot} from "@khanacademy/wonder-blocks-core";
|
|
6
|
+
import {screen, render, fireEvent, waitFor} from "@testing-library/react";
|
|
7
|
+
import * as React from "react";
|
|
8
|
+
import "@testing-library/jest-dom"; // Imports custom matchers
|
|
9
|
+
|
|
10
|
+
import {
|
|
11
|
+
testDependencies,
|
|
12
|
+
testDependenciesV2,
|
|
13
|
+
} from "../../../../testing/test-dependencies";
|
|
14
|
+
import KeypadSwitch from "../../../math-input/src/components/keypad-switch";
|
|
15
|
+
import {articleWithExpression} from "../__testdata__/article-renderer.testdata";
|
|
16
|
+
import ArticleRenderer from "../article-renderer";
|
|
17
|
+
import * as Dependencies from "../dependencies";
|
|
18
|
+
import {ApiOptions} from "../perseus-api";
|
|
19
|
+
|
|
20
|
+
import type {APIOptions} from "../types";
|
|
21
|
+
|
|
22
|
+
function KeypadWithContext() {
|
|
23
|
+
return (
|
|
24
|
+
<KeypadContext.Consumer>
|
|
25
|
+
{({setKeypadElement}) => {
|
|
26
|
+
return (
|
|
27
|
+
<KeypadSwitch
|
|
28
|
+
onElementMounted={setKeypadElement}
|
|
29
|
+
onDismiss={() => {}}
|
|
30
|
+
onAnalyticsEvent={async () => {}}
|
|
31
|
+
useV2Keypad
|
|
32
|
+
/>
|
|
33
|
+
);
|
|
34
|
+
}}
|
|
35
|
+
</KeypadContext.Consumer>
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
// This looks alot like `widgets/__tests__/renderQuestion.jsx', except we use
|
|
39
|
+
// the ArticleRenderer instead of Renderer
|
|
40
|
+
export const RenderArticle = (
|
|
41
|
+
apiOptions: APIOptions = Object.freeze({}),
|
|
42
|
+
): {
|
|
43
|
+
container: HTMLElement;
|
|
44
|
+
renderer: ArticleRenderer;
|
|
45
|
+
} => {
|
|
46
|
+
let renderer: ArticleRenderer | null = null;
|
|
47
|
+
const {container} = render(
|
|
48
|
+
<RenderStateRoot>
|
|
49
|
+
<StatefulKeypadContextProvider>
|
|
50
|
+
<KeypadContext.Consumer>
|
|
51
|
+
{({keypadElement, setRenderer}) => (
|
|
52
|
+
<ArticleRenderer
|
|
53
|
+
ref={(node) => {
|
|
54
|
+
renderer = node;
|
|
55
|
+
setRenderer(node);
|
|
56
|
+
}}
|
|
57
|
+
json={articleWithExpression}
|
|
58
|
+
dependencies={testDependenciesV2}
|
|
59
|
+
apiOptions={{...apiOptions}}
|
|
60
|
+
keypadElement={keypadElement}
|
|
61
|
+
/>
|
|
62
|
+
)}
|
|
63
|
+
</KeypadContext.Consumer>
|
|
64
|
+
<KeypadWithContext />
|
|
65
|
+
</StatefulKeypadContextProvider>
|
|
66
|
+
</RenderStateRoot>,
|
|
67
|
+
);
|
|
68
|
+
if (!renderer) {
|
|
69
|
+
throw new Error(`Failed to render!`);
|
|
70
|
+
}
|
|
71
|
+
return {container, renderer};
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
describe("article renderer", () => {
|
|
75
|
+
beforeEach(() => {
|
|
76
|
+
// Mock ResizeObserver used by the mobile keypad
|
|
77
|
+
window.ResizeObserver = jest.fn().mockImplementation(() => ({
|
|
78
|
+
observe: jest.fn(),
|
|
79
|
+
unobserve: jest.fn(),
|
|
80
|
+
disconnect: jest.fn(),
|
|
81
|
+
}));
|
|
82
|
+
|
|
83
|
+
jest.spyOn(Dependencies, "getDependencies").mockReturnValue(
|
|
84
|
+
testDependencies,
|
|
85
|
+
);
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
afterEach(() => {
|
|
89
|
+
// The Renderer uses a timer to wait for widgets to complete rendering.
|
|
90
|
+
// If we don't spin the timers here, then the timer fires in the test
|
|
91
|
+
// _after_ and breaks it because we do setState() in the callback,
|
|
92
|
+
// and by that point the component has been unmounted.
|
|
93
|
+
jest.runOnlyPendingTimers();
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it("should render the content", () => {
|
|
97
|
+
// Arrange and Act
|
|
98
|
+
RenderArticle({
|
|
99
|
+
...ApiOptions.defaults,
|
|
100
|
+
isMobile: false,
|
|
101
|
+
customKeypad: false,
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
// Assert
|
|
105
|
+
expect(screen.getByRole("textbox")).toBeInTheDocument();
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
it("should call the onFocusChanged callback when an input is focused", async () => {
|
|
109
|
+
// Arrange
|
|
110
|
+
const answerableCallback = jest.fn();
|
|
111
|
+
|
|
112
|
+
// Act
|
|
113
|
+
RenderArticle({
|
|
114
|
+
...ApiOptions.defaults,
|
|
115
|
+
onFocusChange: answerableCallback,
|
|
116
|
+
isMobile: true,
|
|
117
|
+
customKeypad: true,
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
const input = screen.getByLabelText(
|
|
121
|
+
"Math input box Tap with one or two fingers to open keyboard",
|
|
122
|
+
);
|
|
123
|
+
|
|
124
|
+
fireEvent.touchStart(input);
|
|
125
|
+
|
|
126
|
+
await waitFor(() => {
|
|
127
|
+
expect(screen.getByRole("button", {name: "4"})).toBeVisible();
|
|
128
|
+
});
|
|
129
|
+
expect(answerableCallback).toHaveBeenCalled();
|
|
130
|
+
});
|
|
131
|
+
});
|
|
@@ -8,9 +8,10 @@ import TestUtils from "react-dom/test-utils";
|
|
|
8
8
|
import _ from "underscore";
|
|
9
9
|
import "jest-enzyme";
|
|
10
10
|
|
|
11
|
-
import {ClassNames, Dependencies, Renderer} from "@khanacademy/perseus";
|
|
12
|
-
|
|
13
11
|
import {testDependencies} from "../../../../testing/test-dependencies";
|
|
12
|
+
import * as Dependencies from "../dependencies";
|
|
13
|
+
import {ClassNames} from "../perseus-api";
|
|
14
|
+
import Renderer from "../renderer";
|
|
14
15
|
import {registerAllWidgetsForTesting} from "../util/register-all-widgets-for-testing";
|
|
15
16
|
|
|
16
17
|
import imageItem from "./test-items/image-item";
|
|
@@ -18,6 +19,8 @@ import inputNumber1Item from "./test-items/input-number-1-item";
|
|
|
18
19
|
import inputNumber2Item from "./test-items/input-number-2-item";
|
|
19
20
|
import tableItem from "./test-items/table-item";
|
|
20
21
|
|
|
22
|
+
import type {APIOptions} from "../types";
|
|
23
|
+
|
|
21
24
|
const itemWidget = inputNumber1Item;
|
|
22
25
|
|
|
23
26
|
// Returns a promise that will resolve shortly after the end of this
|
|
@@ -30,16 +33,20 @@ const delayedPromise = (value: undefined) => {
|
|
|
30
33
|
return deferred.promise();
|
|
31
34
|
};
|
|
32
35
|
|
|
33
|
-
const renderQuestionArea = function (item, apiOptions): any {
|
|
36
|
+
const renderQuestionArea = function (item, apiOptions?: APIOptions): any {
|
|
34
37
|
const wrapper = mount(
|
|
35
38
|
<RenderStateRoot>
|
|
36
|
-
<
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
39
|
+
<Dependencies.DependenciesContext.Provider
|
|
40
|
+
value={{analytics: {onAnalyticsEvent: async () => undefined}}}
|
|
41
|
+
>
|
|
42
|
+
<Renderer
|
|
43
|
+
content={item.question.content}
|
|
44
|
+
images={item.question.images}
|
|
45
|
+
widgets={item.question.widgets}
|
|
46
|
+
problemNum={0}
|
|
47
|
+
apiOptions={apiOptions}
|
|
48
|
+
/>
|
|
49
|
+
</Dependencies.DependenciesContext.Provider>
|
|
43
50
|
</RenderStateRoot>,
|
|
44
51
|
{includeDefaultTestHarness: false},
|
|
45
52
|
);
|
|
@@ -56,7 +63,6 @@ describe("Perseus API", function () {
|
|
|
56
63
|
|
|
57
64
|
describe("setInputValue", function () {
|
|
58
65
|
it("should be able to produce a correctly graded value", function () {
|
|
59
|
-
// @ts-expect-error - TS2554 - Expected 2 arguments, but got 1.
|
|
60
66
|
const renderer = renderQuestionArea(inputNumber1Item);
|
|
61
67
|
renderer.setInputValue(["input-number 1"], "5");
|
|
62
68
|
const score = renderer.guessAndScore()[1];
|
|
@@ -65,7 +71,6 @@ describe("Perseus API", function () {
|
|
|
65
71
|
});
|
|
66
72
|
|
|
67
73
|
it("should be able to produce a wrong value", function () {
|
|
68
|
-
// @ts-expect-error - TS2554 - Expected 2 arguments, but got 1.
|
|
69
74
|
const renderer = renderQuestionArea(inputNumber1Item);
|
|
70
75
|
renderer.setInputValue(["input-number 1"], "3", () => {});
|
|
71
76
|
const score = renderer.guessAndScore()[1];
|
|
@@ -74,7 +79,6 @@ describe("Perseus API", function () {
|
|
|
74
79
|
});
|
|
75
80
|
|
|
76
81
|
it("should be able to produce an empty score", function () {
|
|
77
|
-
// @ts-expect-error - TS2554 - Expected 2 arguments, but got 1.
|
|
78
82
|
const renderer = renderQuestionArea(inputNumber1Item);
|
|
79
83
|
renderer.setInputValue(["input-number 1"], "3");
|
|
80
84
|
let score = renderer.guessAndScore()[1];
|
|
@@ -86,7 +90,6 @@ describe("Perseus API", function () {
|
|
|
86
90
|
});
|
|
87
91
|
|
|
88
92
|
it("should be able to accept a callback", function (done) {
|
|
89
|
-
// @ts-expect-error - TS2554 - Expected 2 arguments, but got 1.
|
|
90
93
|
const renderer = renderQuestionArea(inputNumber1Item);
|
|
91
94
|
renderer.setInputValue(["input-number 1"], "3", function () {
|
|
92
95
|
const guess = renderer.getUserInput()[0];
|
|
@@ -99,14 +102,12 @@ describe("Perseus API", function () {
|
|
|
99
102
|
|
|
100
103
|
describe("getInputPaths", function () {
|
|
101
104
|
it("should be able to find all the input widgets", function () {
|
|
102
|
-
// @ts-expect-error - TS2554 - Expected 2 arguments, but got 1.
|
|
103
105
|
const renderer = renderQuestionArea(inputNumber2Item);
|
|
104
106
|
const numPaths = renderer.getInputPaths().length;
|
|
105
107
|
expect(numPaths).toBe(2);
|
|
106
108
|
});
|
|
107
109
|
|
|
108
110
|
it("should be able to find all inputs within widgets", function () {
|
|
109
|
-
// @ts-expect-error - TS2554 - Expected 2 arguments, but got 1.
|
|
110
111
|
const renderer = renderQuestionArea(tableItem);
|
|
111
112
|
const numPaths = renderer.getInputPaths().length;
|
|
112
113
|
expect(numPaths).toBe(8);
|
|
@@ -115,7 +116,6 @@ describe("Perseus API", function () {
|
|
|
115
116
|
|
|
116
117
|
describe("getDOMNodeForPath", function () {
|
|
117
118
|
it("should find one DOM node per <input>", function () {
|
|
118
|
-
// @ts-expect-error - TS2554 - Expected 2 arguments, but got 1.
|
|
119
119
|
const renderer = renderQuestionArea(inputNumber2Item);
|
|
120
120
|
const inputPaths = renderer.getInputPaths();
|
|
121
121
|
const allInputs = TestUtils.scryRenderedDOMComponentsWithTag(
|
|
@@ -126,7 +126,6 @@ describe("Perseus API", function () {
|
|
|
126
126
|
});
|
|
127
127
|
|
|
128
128
|
it("should find the right DOM nodes for the <input>s", function () {
|
|
129
|
-
// @ts-expect-error - TS2554 - Expected 2 arguments, but got 1.
|
|
130
129
|
const renderer = renderQuestionArea(inputNumber2Item);
|
|
131
130
|
const inputPaths = renderer.getInputPaths();
|
|
132
131
|
const allInputs = TestUtils.scryRenderedDOMComponentsWithTag(
|
|
@@ -165,11 +164,7 @@ describe("Perseus API", function () {
|
|
|
165
164
|
// version
|
|
166
165
|
expect(ClassNames.FOCUSED).toBe("perseus-focused");
|
|
167
166
|
|
|
168
|
-
const renderer = renderQuestionArea(inputNumber1Item
|
|
169
|
-
interceptInputFocus: function (widgetId) {
|
|
170
|
-
renderer.setInputValue(widgetId, "5");
|
|
171
|
-
},
|
|
172
|
-
});
|
|
167
|
+
const renderer = renderQuestionArea(inputNumber1Item);
|
|
173
168
|
|
|
174
169
|
const input =
|
|
175
170
|
// @ts-expect-error - TS2531 - Object is possibly 'null'. | TS2339 - Property 'querySelector' does not exist on type 'Element | Text'.
|