import { describe, it, expect } from "vitest"; import type { Difficulty, MathProblem } from "../types"; import { generateFractionBodmas, generateSignedFraction } from "./fraction-problems"; import { generateDefineRatio, generateFractionsAndRatios } from "./ratio-problems"; import { generateIntegerOrderCompare, generateIntegerAddSubtractMixed } from "./integer-problems"; import { generateQuaternaryToDenary } from "./number-system-problems"; function assertWellFormed(p: MathProblem) { expect(typeof p.prompt).toBe("string"); expect(p.prompt.length).toBeGreaterThan(0); expect(Array.isArray(p.hints)).toBe(true); expect(Array.isArray(p.steps)).toBe(true); const a = p.answer; if (a.kind === "integer") { expect(Number.isFinite(a.value)).toBe(true); } else if (a.kind === "fraction") { expect(Number.isInteger(a.numerator)).toBe(true); expect(Number.isInteger(a.denominator)).toBe(true); expect(a.denominator).not.toBe(0); } else if (a.kind === "ratio") { expect(a.parts.length).toBeGreaterThan(0); a.parts.forEach((v) => expect(Number.isFinite(v)).toBe(true)); } } const NEW_GENERATORS: Record MathProblem> = { "fraction BODMAS": generateFractionBodmas, "signed fraction": generateSignedFraction, "define ratio": generateDefineRatio, "fractions and ratios": generateFractionsAndRatios, "integer order/compare": generateIntegerOrderCompare, "integer add/subtract (mixed)": generateIntegerAddSubtractMixed, "quaternary to denary": generateQuaternaryToDenary, }; describe("new practice generators produce well-formed problems", () => { for (const [name, gen] of Object.entries(NEW_GENERATORS)) { it(name, () => { for (const d of [1, 2, 3] as Difficulty[]) { for (let i = 0; i < 40; i++) assertWellFormed(gen(d)); } }); } }); describe("quaternary answer matches its base-4 prompt", () => { it("converts base 4 correctly", () => { for (const d of [1, 2, 3] as Difficulty[]) { for (let i = 0; i < 40; i++) { const p = generateQuaternaryToDenary(d); const digits = p.prompt.match(/^(\d+)_\{4\}/)?.[1]; expect(digits).toBeTruthy(); if (p.answer.kind === "integer") { expect(p.answer.value).toBe(parseInt(digits!, 4)); } } } }); }); describe("order/compare answer is one of the numbers asked about", () => { it("picks a value from the prompt", () => { for (let i = 0; i < 60; i++) { const p = generateIntegerOrderCompare(1); const nums = (p.prompt.match(/-?\d+/g) ?? []).map(Number); if (p.answer.kind === "integer") { expect(nums).toContain(p.answer.value); } } }); });