- Rename course/11-break-your-engine to 13-break-your-engine and update every link and mention; start-module explains that Module 13 has no tests to start. - tools/diff-tracks.mjs (npm run diff:tracks): stages both references of a module, generates a seeded random SpellGrid level and script per seed, pipes it to the Rust and TypeScript CLIs and diffs their output; crashes on one or both sides fail. stagePlay takes a module number. - register_assembled: the TypeScript track compared only the text of an existing Assembled card, so a base re-registered with a new cost (or a different split) silently rewrote the earlier card. It now compares every field the engine reads, as Rust does (Modules 5-10). New test_05_assemble_never_rewrites_an_earlier_card. - SpellGrid CLIs: coordinates and chooser numbers are 1-9 plain digits in both tracks (Rust accepted signs, TypeScript accepted "+2", "0x2", "1e0"); Rust quotes card names verbatim instead of with Debug escapes; Rust joins the minion list with "; " instead of rewriting ", ". Two new CLI tests per track. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
48 lines
2.0 KiB
TypeScript
48 lines
2.0 KiB
TypeScript
// The SpellGrid command line, played from a script (the reference's own check; not a course test).
|
|
import { readFileSync } from "node:fs";
|
|
import { expect, test } from "vitest";
|
|
import { run } from "../src/cli";
|
|
|
|
function play(level: string, input: string[]): { outcome: string; out: string } {
|
|
let out = "";
|
|
const outcome = run(readFileSync(`levels/${level}.json`, "utf8"), () => input.shift(), (s) => (out += s));
|
|
return { outcome, out };
|
|
}
|
|
|
|
test("cli_wins_level_1", () => {
|
|
const { outcome, out } = play("l1-double-tap", ["w", "w", "c Whirlwind", "s", "s", "a", "s", "s", "d", "d"]);
|
|
expect(outcome).toBe("won");
|
|
expect(out).toContain("DEA IS DOU");
|
|
expect(out).toContain("You win!");
|
|
});
|
|
|
|
test("cli_wins_level_2_with_choices_and_play", () => {
|
|
const input = ["c Graft", "Ember Moth", "Rune Scribe", "bottom", "c Ember Moth (Assembled) 4 3", "a", "a"];
|
|
const { outcome, out } = play("l2-spliced-corpse", input);
|
|
expect(outcome).toBe("won");
|
|
expect(out).toContain("Choose the base");
|
|
expect(out).toContain("Hand: Ember Moth (Assembled)");
|
|
});
|
|
|
|
test("cli_reports_errors_and_quits", () => {
|
|
const { outcome, out } = play("l3-simultaneous-paradox", ["c Spark", "x", "q"]);
|
|
expect(outcome).toBe("quit");
|
|
expect(out).toContain('"Spark" is not in the hand');
|
|
expect(out).toContain("Commands:");
|
|
});
|
|
|
|
// The two reference CLIs print the same text for the same input (tools/diff-tracks.mjs checks it).
|
|
test("cli_prints_card_names_as_typed", () => {
|
|
const { out } = play("l3-simultaneous-paradox", ['c a"b', "q"]);
|
|
expect(out).toContain('"a"b" is not in the hand');
|
|
});
|
|
|
|
test("cli_numbers_are_plain_digits", () => {
|
|
// "-1" is not a coordinate, so it is part of the card's name.
|
|
const cast = play("l1-double-tap", ["c Whirlwind -1 0", "q"]);
|
|
expect(cast.out).toContain('"Whirlwind -1 0" is not in the hand');
|
|
// "+2" is not a number, so the chooser takes the first option.
|
|
const graft = play("l2-spliced-corpse", ["c Graft", "+2", "1", "1", "q"]);
|
|
expect(graft.out).toContain("Hand: Ember Moth (Assembled)");
|
|
});
|