The Four Asserts
Second lap of the test recipe, against a given undo/redo editor. Same four helpers, same two-test main, different prop.
- Pattern
- Four helpers · same throw shape
- Time
- the recipe
- Space
- O(1)
Same recipe as Write the Test Runner. Different prop. TextDocument and EditorHistory are already written — you are here to produce the four helpers cold.
The recipe
Retype it. Do not paste it.
function assertEqual<T>(actual: T, expected: T, label: string): void {
if (actual !== expected) {
throw new Error(`${label}: expected ${expected}, got ${actual}`);
}
}
function assertTrue(actual: boolean, label: string): void {
if (actual !== true) {
throw new Error(`${label}: expected true, got ${actual}`);
}
}
function assertFalse(actual: boolean, label: string): void {
if (actual !== false) {
throw new Error(`${label}: expected false, got ${actual}`);
}
}
function assertDeepEqual<T>(actual: T, expected: T, label: string): void {
const actualJson = JSON.stringify(actual);
const expectedJson = JSON.stringify(expected);
if (actualJson !== expectedJson) {
throw new Error(`${label}: expected ${expectedJson}, got ${actualJson}`);
}
}
function test(name: string, callback: () => void): void {
try {
callback();
console.log(`PASS: ${name}`);
} catch (error) {
console.error(`FAIL: ${name}`);
console.error(error);
}
}
What this class is for
EditorHistory.undo() and redo() return booleans. That is why this one exists — phase two has real assertTrue / assertFalse material instead of comparing strings to strings.
function main(): void {
test("undo is a no-op on a fresh history", () => {
const doc = new TextDocument("hello");
const history = new EditorHistory(doc);
assertEqual(history.undo(), false, "undo result");
assertEqual(doc.getText(), "hello", "document text");
assertFalse(history.undo(), "still nothing to undo");
assertDeepEqual({ text: doc.getText() }, { text: "hello" }, "snapshot");
});
test("insert then undo", () => {
const doc = new TextDocument("hello");
const history = new EditorHistory(doc);
history.apply({ type: "insert", index: 5, text: " there" });
assertEqual(doc.getText(), "hello there", "after insert");
assertTrue(history.undo(), "undo after apply");
assertDeepEqual({ text: doc.getText() }, { text: "hello" }, "after undo");
});
}
main();
Two tests. A mix of all four helpers. That is a passing lap.
Note that each test builds its own doc and history. That is not style: the bench rebinds the helper names to count your assertions and replays main() to do it, so the helpers have to be function declarations rather than const arrows, and a fixture shared across tests will not survive the second run.
The three sets
Phase 1 — assertEqual, test, main. Two passing assertEqual calls.
Phase 2 — add assertTrue and assertFalse. undo() on a fresh history is false; undo() after apply is true.
Phase 3 — add assertDeepEqual. Snapshot the text. Do not invent a deep object the class does not have.
Notes
If you can type the four helpers without looking, the design problems stop spending their first five minutes on a console.log. That is the only score this extra keeps.