import { describe, expect, it } from "vitest"; import { formatVariableExpressionLatex, generateVariableParenthesisProblem, generateVariableParenthesisSession, gradeVariableParenthesisAnswer, } from "./variable-parenthesis"; import type { VariableParenthesisProblem } from "./variable-types"; function findCorrectChoiceIds(problem: VariableParenthesisProblem) { return problem.correctTerms.map((term, index) => { const choices = problem.choices[index as 0 | 1]; const choice = choices.find( (item) => item.coefficient === term.coefficient && item.variable === term.variable, ); if (!choice) throw new Error("correct choice not found."); return choice.id; }) as [string, string]; } describe("variable parenthesis engine", () => { it("generates a problem with exactly one variable term inside parentheses", () => { const problem = generateVariableParenthesisProblem(); const terms = [problem.firstTerm, problem.secondTerm]; expect(terms.filter((term) => term.kind === "variable")).toHaveLength(1); expect(terms.filter((term) => term.kind === "constant")).toHaveLength(1); expect(problem.multiplier).toBeLessThan(0); expect(Number.isInteger(problem.multiplier)).toBe(true); expect(problem.firstTerm.coefficient).toBeGreaterThanOrEqual(1); expect(problem.secondTerm.coefficient).toBeGreaterThanOrEqual(1); }); it("distributes signs correctly for addition and subtraction", () => { const problems = generateVariableParenthesisSession(50); for (const problem of problems) { const secondSign = problem.operator === "-" ? -1 : 1; expect(problem.correctTerms[0].coefficient).toBe( problem.multiplier * problem.firstTerm.coefficient, ); expect(problem.correctTerms[1].coefficient).toBe( problem.multiplier * secondSign * problem.secondTerm.coefficient, ); } }); it("creates positive and negative choices for each term", () => { const problem = generateVariableParenthesisProblem(); problem.choices.forEach((choices, index) => { const correctTerm = problem.correctTerms[index as 0 | 1]; const coefficients = choices.map((choice) => choice.coefficient).sort(); expect(choices).toHaveLength(2); expect(coefficients).toEqual([ -Math.abs(correctTerm.coefficient), Math.abs(correctTerm.coefficient), ]); expect(choices.every((choice) => choice.variable === correctTerm.variable)) .toBe(true); }); }); it("grades selected choice ids", () => { const problem = generateVariableParenthesisProblem(); const correctChoiceIds = findCorrectChoiceIds(problem); const result = gradeVariableParenthesisAnswer(problem, { selectedChoiceIds: correctChoiceIds, }); expect(result.correct).toBe(true); expect(result.correctAnswer).toEqual(problem.correctTerms); const wrongFirstChoice = problem.choices[0].find( (choice) => choice.id !== correctChoiceIds[0], ); expect(wrongFirstChoice).toBeDefined(); expect( gradeVariableParenthesisAnswer(problem, { selectedChoiceIds: [wrongFirstChoice!.id, correctChoiceIds[1]], }).correct, ).toBe(false); }); it("generates sessions without duplicate keys", () => { const problems = generateVariableParenthesisSession(10); const keys = new Set(problems.map((problem) => problem.key)); expect(problems).toHaveLength(10); expect(keys.size).toBe(10); }); it("formats expressions with variable terms", () => { expect( formatVariableExpressionLatex([ { kind: "variable", coefficient: -6, variable: "x", latex: "-6x" }, { kind: "constant", coefficient: 12, latex: "12" }, ]), ).toBe("-6x + 12"); }); });