From 7d4d098dbdbc3f41e89d7e118c8cd83fbcaeb928 Mon Sep 17 00:00:00 2001 From: Tamara <60857422+Myranae@users.noreply.github.com> Date: Thu, 22 Feb 2024 10:14:22 -0600 Subject: [PATCH] Create a helper function for generating correctly typed Perseus test items (#980) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary: There are many places in webapp where custom types are created for Perseus items, frequently related to testing. This change adds a helper function that creates a generic Perseus test item that can have specific sections overridden with custom input. It will ensure tests are properly typed and help us know where updates need to be made if any changes happen to the types in Perseus. Issue: LC-1658 ## Test plan: - Confirm all checks pass - Test the new function in webapp with the npm snapshot - In webapp, confirm related tests pass and related stories still work Author: Myranae Reviewers: Myranae, handeyeco, nixterrimus, SonicScrewdriver Required Reviewers: Approved By: handeyeco Checks: ✅ codecov/project, ✅ codecov/patch, ✅ Upload Coverage, ✅ Publish npm snapshot (ubuntu-latest, 20.x), ✅ Lint, Typecheck, Format, and Test (ubuntu-latest, 20.x), ✅ Extract i18n strings (ubuntu-latest, 20.x), ✅ Cypress (ubuntu-latest, 20.x), ✅ Jest Coverage (ubuntu-latest, 20.x), ✅ Check for .changeset entries for all changed files (ubuntu-latest, 20.x), ✅ Check builds for changes in size (ubuntu-latest, 20.x), ✅ Publish Storybook to Chromatic (ubuntu-latest, 20.x), ✅ gerald Pull Request URL: https://github.com/Khan/perseus/pull/980 --- .changeset/sour-ways-wave.md | 5 + packages/perseus/src/index.ts | 4 + packages/perseus/src/util/test-utils.test.ts | 34 +++ .../perseus/src/util/test-utils.testdata.ts | 219 ++++++++++++++++++ packages/perseus/src/util/test-utils.ts | 42 ++++ 5 files changed, 304 insertions(+) create mode 100644 .changeset/sour-ways-wave.md create mode 100644 packages/perseus/src/util/test-utils.test.ts create mode 100644 packages/perseus/src/util/test-utils.testdata.ts create mode 100644 packages/perseus/src/util/test-utils.ts diff --git a/.changeset/sour-ways-wave.md b/.changeset/sour-ways-wave.md new file mode 100644 index 0000000000..33f6cc1eee --- /dev/null +++ b/.changeset/sour-ways-wave.md @@ -0,0 +1,5 @@ +--- +"@khanacademy/perseus": minor +--- + +Create a helper function for generating correctly typed Perseus test items diff --git a/packages/perseus/src/index.ts b/packages/perseus/src/index.ts index d6a689ab15..0f3d2147a9 100644 --- a/packages/perseus/src/index.ts +++ b/packages/perseus/src/index.ts @@ -84,6 +84,10 @@ export { injectWidgets, } from "./util/extract-perseus-data"; export {parsePerseusItem} from "./util/parse-perseus-json"; +export { + generateTestPerseusItem, + genericPerseusItemData, +} from "./util/test-utils"; /** * Mixins diff --git a/packages/perseus/src/util/test-utils.test.ts b/packages/perseus/src/util/test-utils.test.ts new file mode 100644 index 0000000000..c5905d3aa3 --- /dev/null +++ b/packages/perseus/src/util/test-utils.test.ts @@ -0,0 +1,34 @@ +import {generateTestPerseusItem} from "./test-utils"; +import { + basicObject, + customQuestionInfo, + expectedQuestionInfoAdded, + customAnswerAreaInfo, + expectedAnswerAreaInfoAdded, + customHintsInfo, + expectedHintsInfoAdded, +} from "./test-utils.testdata"; + +describe("generateTestPerseusItem", () => { + it("should provide a basic Perseus item object with no inputs", () => { + expect(generateTestPerseusItem()).toEqual(basicObject); + }); + + it("should replace question parts when given question custom info", () => { + expect(generateTestPerseusItem(customQuestionInfo)).toEqual( + expectedQuestionInfoAdded, + ); + }); + + it("should replace answer area parts when given answer area custom info", () => { + expect(generateTestPerseusItem(customAnswerAreaInfo)).toEqual( + expectedAnswerAreaInfoAdded, + ); + }); + + it("should add hints when given custom info containing hints", () => { + expect(generateTestPerseusItem(customHintsInfo)).toEqual( + expectedHintsInfoAdded, + ); + }); +}); diff --git a/packages/perseus/src/util/test-utils.testdata.ts b/packages/perseus/src/util/test-utils.testdata.ts new file mode 100644 index 0000000000..771f9a8012 --- /dev/null +++ b/packages/perseus/src/util/test-utils.testdata.ts @@ -0,0 +1,219 @@ +import type {PerseusItem} from "@khanacademy/perseus"; + +export const basicObject: PerseusItem = { + question: { + content: "", + images: {}, + widgets: {}, + }, + answerArea: { + calculator: false, + chi2Table: false, + periodicTable: false, + tTable: false, + zTable: false, + financialCalculatorMonthlyPayment: false, + financialCalculatorTotalAmount: false, + financialCalculatorTimeToPayOff: false, + periodicTableWithKey: false, + }, + itemDataVersion: { + major: 0, + minor: 1, + }, + hints: [], + _multi: null, + answer: null, +}; + +export const customQuestionInfo: Partial = { + question: { + content: "Test content string", + images: {"Test image string": {width: 200, height: 200}}, + widgets: { + "input-number 1": { + type: "input-number", + graded: true, + options: { + value: 123, + simplify: "required", + size: "small", + inexact: false, + maxError: 0.123, + answerType: "number", + }, + }, + }, + }, +}; + +export const expectedQuestionInfoAdded: PerseusItem = { + question: { + content: "Test content string", + images: {"Test image string": {width: 200, height: 200}}, + widgets: { + "input-number 1": { + type: "input-number", + graded: true, + options: { + value: 123, + simplify: "required", + size: "small", + inexact: false, + maxError: 0.123, + answerType: "number", + }, + }, + }, + }, + answerArea: { + calculator: false, + chi2Table: false, + periodicTable: false, + tTable: false, + zTable: false, + financialCalculatorMonthlyPayment: false, + financialCalculatorTotalAmount: false, + financialCalculatorTimeToPayOff: false, + periodicTableWithKey: false, + }, + itemDataVersion: { + major: 0, + minor: 1, + }, + hints: [], + _multi: null, + answer: null, +}; + +export const customAnswerAreaInfo: Partial = { + answerArea: { + calculator: true, + chi2Table: true, + periodicTable: true, + tTable: true, + zTable: true, + financialCalculatorMonthlyPayment: true, + financialCalculatorTotalAmount: true, + financialCalculatorTimeToPayOff: true, + periodicTableWithKey: true, + }, +}; + +export const expectedAnswerAreaInfoAdded: PerseusItem = { + question: { + content: "", + images: {}, + widgets: {}, + }, + answerArea: { + calculator: true, + chi2Table: true, + periodicTable: true, + tTable: true, + zTable: true, + financialCalculatorMonthlyPayment: true, + financialCalculatorTotalAmount: true, + financialCalculatorTimeToPayOff: true, + periodicTableWithKey: true, + }, + itemDataVersion: { + major: 0, + minor: 1, + }, + hints: [], + _multi: null, + answer: null, +}; + +export const customHintsInfo: Partial = { + hints: [ + { + content: "Test content string", + images: { + "Test images string": {height: 200, width: 200}, + }, + widgets: { + "radio 1": { + graded: true, + options: { + choices: [ + { + content: "Test content string", + correct: true, + }, + { + content: "Test content string 2", + correct: false, + }, + ], + deselectEnabled: false, + displayCount: null, + multipleSelect: false, + noneOfTheAbove: false, + onePerLine: true, + randomize: true, + }, + type: "radio", + }, + }, + }, + ], +}; + +export const expectedHintsInfoAdded: PerseusItem = { + question: { + content: "", + images: {}, + widgets: {}, + }, + answerArea: { + calculator: false, + chi2Table: false, + periodicTable: false, + tTable: false, + zTable: false, + financialCalculatorMonthlyPayment: false, + financialCalculatorTotalAmount: false, + financialCalculatorTimeToPayOff: false, + periodicTableWithKey: false, + }, + itemDataVersion: { + major: 0, + minor: 1, + }, + hints: [ + { + content: "Test content string", + images: { + "Test images string": {height: 200, width: 200}, + }, + widgets: { + "radio 1": { + graded: true, + options: { + choices: [ + { + content: "Test content string", + correct: true, + }, + { + content: "Test content string 2", + correct: false, + }, + ], + deselectEnabled: false, + displayCount: null, + multipleSelect: false, + noneOfTheAbove: false, + onePerLine: true, + randomize: true, + }, + type: "radio", + }, + }, + }, + ], + _multi: null, + answer: null, +}; diff --git a/packages/perseus/src/util/test-utils.ts b/packages/perseus/src/util/test-utils.ts new file mode 100644 index 0000000000..62c4c54994 --- /dev/null +++ b/packages/perseus/src/util/test-utils.ts @@ -0,0 +1,42 @@ +import type {PerseusItem} from "../perseus-types"; + +export const genericPerseusItemData: PerseusItem = { + question: { + content: "", + images: {}, + widgets: {}, + }, + answerArea: { + calculator: false, + chi2Table: false, + periodicTable: false, + tTable: false, + zTable: false, + financialCalculatorMonthlyPayment: false, + financialCalculatorTotalAmount: false, + financialCalculatorTimeToPayOff: false, + periodicTableWithKey: false, + }, + itemDataVersion: { + major: 0, + minor: 1, + }, + hints: [], + _multi: null, + answer: null, +} as const; + +/** + * Generate a Perseus item object for testing purposes. + * + * In order to better type Perseus objects used in testing, this function + * uses a basic Perseus object and updates it with custom values as needed. + * + * @param {Partial} customFields + * @returns PerseusItem + */ +export function generateTestPerseusItem( + customFields: Partial = {}, +): PerseusItem { + return {...genericPerseusItemData, ...customFields}; +}