Files
math-quiz/lib/engine/variable-parenthesis.test.ts
T

123 lines
4.1 KiB
TypeScript

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];
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;
});
}
describe("variable parenthesis engine", () => {
it("generates a problem with exactly one variable term inside parentheses", () => {
const problem = generateVariableParenthesisProblem();
expect(problem.terms.filter((term) => term.kind === "variable")).toHaveLength(1);
expect(problem.terms.filter((term) => term.kind === "constant")).toHaveLength(1);
expect(problem.multiplier).toBeLessThan(0);
expect(Number.isInteger(problem.multiplier)).toBe(true);
expect(
problem.terms.every((term) => term.coefficient >= 1 && term.coefficient <= 9),
).toBe(true);
});
it("distributes signs correctly for addition and subtraction", () => {
const problems = generateVariableParenthesisSession(50);
for (const problem of problems) {
expect(problem.correctTerms.map((term) => term.coefficient)).toEqual(
problem.terms.map((term, index) => {
const sign = index === 0 || problem.operators[index - 1] === "+" ? 1 : -1;
return problem.multiplier * sign * term.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("generates the selected number of variable choices", () => {
const problem = generateVariableParenthesisProblem(4);
expect(problem.terms).toHaveLength(4);
expect(problem.operators).toHaveLength(3);
expect(problem.correctTerms).toHaveLength(4);
expect(problem.choices).toHaveLength(4);
expect(problem.terms.filter((term) => term.kind === "variable")).toHaveLength(1);
expect(
gradeVariableParenthesisAnswer(problem, {
selectedChoiceIds: findCorrectChoiceIds(problem),
}).correct,
).toBe(true);
});
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");
});
});