forked from Khan/perseus
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
21 changed files
with
512 additions
and
848 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
--- | ||
"@khanacademy/perseus": patch | ||
--- | ||
|
||
Remove unused code related to KaTeX and MathJax 2. It's no longer needed | ||
because all callers have upgraded to MathJax 3. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
"@khanacademy/perseus": patch | ||
--- | ||
|
||
Upgrade MathJax |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -37,6 +37,10 @@ jobs: | |
matchAllGlobs: true # Default is to match any of the globs, which ends up matching all files | ||
conjunctive: true # Only match files that match all of the above | ||
|
||
- uses: webfactory/[email protected] | ||
with: | ||
ssh-private-key: ${{ secrets.KHAN_ACTIONS_BOT_SSH_PRIVATE_KEY }} | ||
|
||
- name: Verify changeset entries | ||
uses: Khan/[email protected] | ||
with: | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,120 +1,142 @@ | ||
import {ApiOptions, Dependencies, Widgets} from "@khanacademy/perseus"; | ||
import { | ||
ApiOptions, | ||
Dependencies, | ||
Widgets, | ||
widgets, | ||
Util, | ||
} from "@khanacademy/perseus"; | ||
import {render, screen} from "@testing-library/react"; | ||
import userEvent from "@testing-library/user-event"; | ||
import * as React from "react"; | ||
import "@testing-library/jest-dom"; // Imports custom mathers | ||
|
||
import {testDependencies} from "../../../../testing/test-dependencies"; | ||
import {wait} from "../../../../testing/wait"; | ||
import {question1} from "../__testdata__/input-number.testdata"; | ||
import Editor from "../editor"; | ||
import ImageEditor from "../widgets/image-editor"; | ||
|
||
import type {PropsFor} from "@khanacademy/wonder-blocks-core"; | ||
|
||
const Harnessed = (props: Partial<PropsFor<typeof Editor>>) => { | ||
return ( | ||
<Editor | ||
apiOptions={ApiOptions.defaults} | ||
onChange={() => {}} | ||
content="[[☃ image 1]]" | ||
widgets={{ | ||
"image 1": { | ||
type: "image", | ||
options: { | ||
backgroundImage: { | ||
url: "http://placekitten.com/200/300", | ||
}, | ||
}, | ||
}, | ||
}} | ||
{...props} | ||
/> | ||
); | ||
}; | ||
|
||
describe("Editor", () => { | ||
beforeAll(() => { | ||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion | ||
const ImageWidget = widgets.find((w) => w.name === "image")!; | ||
expect(ImageWidget).toBeDefined(); | ||
Widgets.registerWidget("image", ImageWidget); | ||
Widgets.registerEditors([ImageEditor]); | ||
}); | ||
|
||
beforeEach(() => { | ||
jest.spyOn(Dependencies, "getDependencies").mockReturnValue( | ||
testDependencies, | ||
); | ||
}); | ||
|
||
describe("input-number widget", () => { | ||
beforeEach(async () => { | ||
const [InputNumberWidget, InputNumberEditor] = await Promise.all([ | ||
import("@khanacademy/perseus").then((m) => m.InputNumber), | ||
import("../widgets/input-number-editor").then((m) => m.default), | ||
]); | ||
Widgets.registerWidgets([InputNumberWidget]); | ||
Widgets.registerEditors([InputNumberEditor]); | ||
jest.useRealTimers(); | ||
it("should delete widget if confirmed", async () => { | ||
const onChangeMock = jest.fn(); | ||
jest.spyOn(window, "confirm").mockReturnValue(true); | ||
|
||
// Arrange | ||
render(<Harnessed onChange={onChangeMock} />); | ||
|
||
// Act | ||
userEvent.click( | ||
screen.getByRole("button", {name: "Remove image widget"}), | ||
); | ||
|
||
// Assert | ||
expect(onChangeMock).toHaveBeenCalledWith({content: ""}); | ||
}); | ||
|
||
it("should NOT delete widget if not confirmed", async () => { | ||
const onChangeMock = jest.fn(); | ||
jest.spyOn(window, "confirm").mockReturnValue(false); | ||
|
||
// Arrange | ||
render(<Harnessed onChange={onChangeMock} />); | ||
|
||
// Act | ||
userEvent.click( | ||
screen.getByRole("button", {name: "Remove image widget"}), | ||
); | ||
|
||
// Assert | ||
expect(onChangeMock).not.toHaveBeenCalled(); | ||
}); | ||
|
||
test("clicking on the widget editor should open it", async () => { | ||
// Arrange | ||
render(<Harnessed />); | ||
|
||
// Act | ||
const widgetDisclosure = screen.getByRole("link", { | ||
name: "image 1", | ||
}); | ||
userEvent.click(widgetDisclosure); | ||
|
||
// Assert | ||
const previewImage = screen.getByAltText("Editor preview of image"); | ||
expect(previewImage).toHaveAttribute( | ||
"src", | ||
"http://placekitten.com/200/300", | ||
); | ||
}); | ||
|
||
it("should update values", async () => { | ||
// Arrange | ||
jest.spyOn(Util, "getImageSizeModern").mockResolvedValue([200, 200]); | ||
|
||
test("clicking on the widget editor should open it", async () => { | ||
// Arrange | ||
render( | ||
<Editor | ||
apiOptions={ApiOptions.defaults} | ||
content={question1.content} | ||
placeholder="" | ||
widgets={question1.widgets} | ||
images={question1.images} | ||
disabled={false} | ||
widgetEnabled={true} | ||
immutableWidgets={false} | ||
showWordCount={true} | ||
warnNoPrompt={true} | ||
warnNoWidgets={true} | ||
onChange={(props) => {}} | ||
/>, | ||
); | ||
await wait(); | ||
|
||
// Act | ||
const widgetDisclosure = screen.getByRole("link", { | ||
name: "input-number 1", | ||
}); | ||
userEvent.click(widgetDisclosure); | ||
const correctAnswerInput = screen.getByLabelText("Correct answer:"); | ||
|
||
// Assert | ||
expect(correctAnswerInput).toHaveValue("0.5"); | ||
const changeFn = jest.fn(); | ||
render(<Harnessed onChange={changeFn} />); | ||
|
||
// Act | ||
const widgetDisclosure = screen.getByRole("link", { | ||
name: "image 1", | ||
}); | ||
userEvent.click(widgetDisclosure); | ||
|
||
it("should update values", async () => { | ||
// Arrange | ||
const changeFn = jest.fn(); | ||
render( | ||
<Editor | ||
apiOptions={ApiOptions.defaults} | ||
content={question1.content} | ||
placeholder="" | ||
widgets={question1.widgets} | ||
images={question1.images} | ||
disabled={false} | ||
widgetEnabled={true} | ||
immutableWidgets={false} | ||
showWordCount={true} | ||
warnNoPrompt={true} | ||
warnNoWidgets={true} | ||
onChange={changeFn} | ||
/>, | ||
); | ||
await wait(); | ||
|
||
// Act | ||
const widgetDisclosure = screen.getByRole("link", { | ||
name: "input-number 1", | ||
}); | ||
userEvent.click(widgetDisclosure); | ||
const correctAnswerInput = screen.getByLabelText("Correct answer:"); | ||
|
||
userEvent.clear(correctAnswerInput); | ||
userEvent.paste(correctAnswerInput, "0.75"); | ||
userEvent.tab(); // blurring the input triggers onChange to be called | ||
|
||
// Assert | ||
expect(changeFn).toHaveBeenCalledWith( | ||
{ | ||
widgets: { | ||
"input-number 1": { | ||
graded: true, | ||
version: {major: 0, minor: 0}, | ||
static: false, | ||
type: "input-number", | ||
options: { | ||
value: 0.75, | ||
simplify: "required", | ||
size: "normal", | ||
inexact: false, | ||
maxError: 0.1, | ||
answerType: "number", | ||
rightAlign: false, | ||
}, | ||
alignment: "default", | ||
}, | ||
}, | ||
const captionInput = screen.getByLabelText(/Caption:/); | ||
|
||
userEvent.clear(captionInput); | ||
userEvent.type(captionInput, "A picture of kittens"); | ||
userEvent.tab(); // blurring the input triggers onChange to be called | ||
jest.runOnlyPendingTimers(); | ||
|
||
// Assert | ||
expect(changeFn).toHaveBeenCalledWith( | ||
{ | ||
widgets: { | ||
"image 1": expect.objectContaining({ | ||
type: "image", | ||
graded: true, | ||
options: expect.objectContaining({ | ||
caption: "A picture of kittens", | ||
}), | ||
}), | ||
}, | ||
undefined, | ||
undefined, | ||
); | ||
}); | ||
}, | ||
undefined, | ||
undefined, | ||
); | ||
}); | ||
}); |
83 changes: 83 additions & 0 deletions
83
packages/perseus-editor/src/__tests__/hint-editor.test.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
import "@testing-library/jest-dom"; | ||
import {render, screen} from "@testing-library/react"; | ||
import userEvent from "@testing-library/user-event"; | ||
import * as React from "react"; | ||
|
||
import CombinedHintsEditor from "../hint-editor"; | ||
|
||
describe("CombinedHintsEditor", () => { | ||
it("should render", () => { | ||
render( | ||
<CombinedHintsEditor deviceType="phone" previewURL="about:blank" />, | ||
); | ||
}); | ||
|
||
it("should confirm before removing a hint", () => { | ||
// Arrange | ||
const comfirmSpy = jest.spyOn(window, "confirm").mockReturnValue(false); | ||
render( | ||
<CombinedHintsEditor | ||
deviceType="phone" | ||
previewURL="about:blank" | ||
hints={[ | ||
{content: "You know this one!", widgets: {}, images: {}}, | ||
{content: "Ok, the answer is 3", widgets: {}, images: {}}, | ||
]} | ||
/>, | ||
); | ||
|
||
// Act | ||
userEvent.click(screen.getAllByText("Remove this hint")[0]); | ||
|
||
// Assert | ||
expect(comfirmSpy).toHaveBeenCalled(); | ||
}); | ||
|
||
it("should not remove the hint if not confirmed", () => { | ||
// Arrange | ||
jest.spyOn(window, "confirm").mockReturnValue(false); | ||
const onChangeMock = jest.fn(); | ||
render( | ||
<CombinedHintsEditor | ||
deviceType="phone" | ||
previewURL="about:blank" | ||
hints={[ | ||
{content: "You know this one!", widgets: {}, images: {}}, | ||
{content: "Ok, the answer is 3", widgets: {}, images: {}}, | ||
]} | ||
onChange={onChangeMock} | ||
/>, | ||
); | ||
|
||
// Act | ||
userEvent.click(screen.getAllByText("Remove this hint")[0]); | ||
|
||
// Assert | ||
expect(onChangeMock).not.toHaveBeenCalled(); | ||
}); | ||
|
||
it("should remove the hint if confirmed", () => { | ||
// Arrange | ||
jest.spyOn(window, "confirm").mockReturnValue(true); | ||
const onChangeMock = jest.fn(); | ||
render( | ||
<CombinedHintsEditor | ||
deviceType="phone" | ||
previewURL="about:blank" | ||
hints={[ | ||
{content: "You know this one!", widgets: {}, images: {}}, | ||
{content: "Ok, the answer is 3", widgets: {}, images: {}}, | ||
]} | ||
onChange={onChangeMock} | ||
/>, | ||
); | ||
|
||
// Act | ||
userEvent.click(screen.getAllByText("Remove this hint")[0]); | ||
|
||
// Assert | ||
expect(onChangeMock).toHaveBeenCalledWith({ | ||
hints: [{content: "Ok, the answer is 3", widgets: {}, images: {}}], | ||
}); | ||
}); | ||
}); |
Oops, something went wrong.