diff --git a/.changeset/two-feet-care.md b/.changeset/two-feet-care.md new file mode 100644 index 0000000000..3a774776ad --- /dev/null +++ b/.changeset/two-feet-care.md @@ -0,0 +1,5 @@ +--- +"@khanacademy/perseus": patch +--- + +Add tests for propUpgrades functions (and remove underscore usage) diff --git a/packages/perseus/src/widgets/expression/expression.test.tsx b/packages/perseus/src/widgets/expression/expression.test.tsx index be8d76b63c..2f5ee2a239 100644 --- a/packages/perseus/src/widgets/expression/expression.test.tsx +++ b/packages/perseus/src/widgets/expression/expression.test.tsx @@ -17,7 +17,10 @@ import { expressionItemWithLabels, } from "./expression.testdata"; -import type {PerseusItem} from "../../perseus-types"; +import type { + PerseusExpressionWidgetOptions, + PerseusItem, +} from "../../perseus-types"; import type {UserEvent} from "@testing-library/user-event"; const renderAndAnswer = async ( @@ -523,4 +526,36 @@ describe("Expression Widget", function () { ).toBeNull(); }); }); + + describe("propUpgrades", () => { + it("can upgrade from v0 to v1", () => { + const v0props = { + times: false, + buttonSets: ["basic"], + functions: [], + form: false, + simplify: false, + value: "42", + }; + + const expected: PerseusExpressionWidgetOptions = { + times: false, + buttonSets: ["basic"], + functions: [], + answerForms: [ + { + considered: "correct", + form: false, + simplify: false, + value: "42", + }, + ], + }; + + const result: PerseusExpressionWidgetOptions = + ExpressionWidgetExport.propUpgrades["1"](v0props); + + expect(result).toEqual(expected); + }); + }); }); diff --git a/packages/perseus/src/widgets/measurer/measurer.test.tsx b/packages/perseus/src/widgets/measurer/measurer.test.tsx new file mode 100644 index 0000000000..6a35c0ca4c --- /dev/null +++ b/packages/perseus/src/widgets/measurer/measurer.test.tsx @@ -0,0 +1,44 @@ +import MeasurerWidgetExport from "./measurer"; + +import type {PerseusMeasurerWidgetOptions} from "../../perseus-types"; + +describe("measurer", () => { + describe("propUpgrades", () => { + it("can upgrade from v0 to v1", () => { + const v0props = { + imageUrl: "url", + imageTop: 42, + imageLeft: 42, + showProtractor: false, + showRuler: false, + rulerLabel: "test", + rulerTicks: 4, + rulerPixels: 4, + rulerLength: 4, + box: [4, 4], + static: false, + }; + + const expected: PerseusMeasurerWidgetOptions = { + image: { + url: "url", + top: 42, + left: 42, + }, + showProtractor: false, + showRuler: false, + rulerLabel: "test", + rulerTicks: 4, + rulerPixels: 4, + rulerLength: 4, + box: [4, 4], + static: false, + }; + + const result: PerseusMeasurerWidgetOptions = + MeasurerWidgetExport.propUpgrades["1"](v0props); + + expect(result).toEqual(expected); + }); + }); +}); diff --git a/packages/perseus/src/widgets/measurer/measurer.tsx b/packages/perseus/src/widgets/measurer/measurer.tsx index 4347ec677e..db4b00a07b 100644 --- a/packages/perseus/src/widgets/measurer/measurer.tsx +++ b/packages/perseus/src/widgets/measurer/measurer.tsx @@ -182,19 +182,17 @@ class Measurer extends React.Component implements Widget { } const propUpgrades = { - "1": (v0props: any): any => { - const v1props = _(v0props) - .chain() - .omit("imageUrl", "imageTop", "imageLeft") - .extend({ - image: { - url: v0props.imageUrl, - top: v0props.imageTop, - left: v0props.imageLeft, - }, - }) - .value(); - return v1props; + "1": (v0props: any): PerseusMeasurerWidgetOptions => { + const {imageUrl, imageTop, imageLeft, ...rest} = v0props; + + return { + ...rest, + image: { + url: imageUrl, + top: imageTop, + left: imageLeft, + }, + }; }, } as const; diff --git a/packages/perseus/src/widgets/radio/__tests__/radio.test.ts b/packages/perseus/src/widgets/radio/__tests__/radio.test.ts index 4eeb1de264..593f5a94c4 100644 --- a/packages/perseus/src/widgets/radio/__tests__/radio.test.ts +++ b/packages/perseus/src/widgets/radio/__tests__/radio.test.ts @@ -8,6 +8,7 @@ import * as Dependencies from "../../../dependencies"; import {mockStrings} from "../../../strings"; import {renderQuestion} from "../../__testutils__/renderQuestion"; import PassageWidget from "../../passage"; +import RadioWidgetExport from "../radio"; import scoreRadio from "../score-radio"; import { @@ -17,7 +18,10 @@ import { shuffledNoneQuestion, } from "./radio.testdata"; -import type {PerseusRenderer} from "../../../perseus-types"; +import type { + PerseusRadioWidgetOptions, + PerseusRenderer, +} from "../../../perseus-types"; import type {APIOptions} from "../../../types"; import type {PerseusRadioUserInput} from "../../../validation.types"; import type {UserEvent} from "@testing-library/user-event"; @@ -984,3 +988,32 @@ describe("scoring", () => { expect(renderer).toHaveBeenAnsweredIncorrectly(); }); }); + +describe("propsUpgrade", () => { + it("can upgrade from v0 to v1", () => { + const v0props = { + choices: [{content: "Choice 1"}, {content: "Choice 2"}], + }; + + const expected: PerseusRadioWidgetOptions = { + choices: [{content: "Choice 1"}, {content: "Choice 2"}], + hasNoneOfTheAbove: false, + }; + + const result: PerseusRadioWidgetOptions = + RadioWidgetExport.propUpgrades["1"](v0props); + + expect(result).toEqual(expected); + }); + + it("throws from noneOfTheAbove", () => { + const v0props = { + choices: [{content: "Choice 1"}, {content: "Choice 2"}], + noneOfTheAbove: true, + }; + + expect(() => RadioWidgetExport.propUpgrades["1"](v0props)).toThrow( + "radio widget v0 no longer supports auto noneOfTheAbove", + ); + }); +}); diff --git a/packages/perseus/src/widgets/radio/radio.ts b/packages/perseus/src/widgets/radio/radio.ts index 815320c5e5..528514e7c9 100644 --- a/packages/perseus/src/widgets/radio/radio.ts +++ b/packages/perseus/src/widgets/radio/radio.ts @@ -126,23 +126,19 @@ const transform = ( }; const propUpgrades = { - "1": (v0props: any): any => { - let choices; - let hasNoneOfTheAbove; - - if (!v0props.noneOfTheAbove) { - choices = v0props.choices; - hasNoneOfTheAbove = false; - } else { + "1": (v0props: any): PerseusRadioWidgetOptions => { + const {noneOfTheAbove, ...rest} = v0props; + + if (noneOfTheAbove) { throw new Error( "radio widget v0 no longer supports auto noneOfTheAbove", ); } - return _.extend(_.omit(v0props, "noneOfTheAbove"), { - choices: choices, - hasNoneOfTheAbove: hasNoneOfTheAbove, - }); + return { + ...rest, + hasNoneOfTheAbove: false, + }; }, } as const;