Zum Inhalt springen

Blöcke testen

Deine Definitionen und Behaviors sind Code, also verdienen sie Tests wie jeder andere Code. Zwei Prüfungen decken das meiste ab: Laden meine Definitionen, und erzeugt ein Programm aus meinen Blöcken den erwarteten Code. Diese Anleitung richtet beides mit Vitest ein.

Morphic Blocks läuft auf Blockly, und Blockly braucht einen Browser. In Tests springt jsdom dafür ein.

Terminal window
npm install --save-dev vitest jsdom

Sag Vitest, dass es jsdom nutzen und eine kleine Setup-Datei laden soll:

vitest.config.ts
import { defineConfig } from "vitest/config";
export default defineConfig({
test: {
environment: "jsdom",
setupFiles: ["./test/setup.ts"],
},
});

jsdom berechnet kein Layout und kann daher keinen Text messen. Blockly misst Text beim Zeichnen von Blöcken, deshalb beantwortet die Setup-Datei diese Aufrufe mit festen Größen. Deine Tests prüfen den erzeugten Code, nicht das Aussehen der Blöcke, also spielen die Größen keine Rolle:

test/setup.ts
HTMLCanvasElement.prototype.getContext = (() => ({
font: "",
measureText: (text: string) => ({ width: text.length * 8 }),
})) as unknown as HTMLCanvasElement["getContext"];
Object.assign(SVGElement.prototype, {
getBBox: () => ({ x: 0, y: 0, width: 0, height: 0 }),
getComputedTextLength: () => 0,
});

mount() validiert deine Definitionen und wirft einen Fehler, der jedes gefundene Problem auflistet. Ein Test, der einen Editor mountet, findet kaputte Definitionen also, bevor es deine Nutzer tun:

test/definitions.test.ts
import { expect, test } from "vitest";
import { MorphicBlocks } from "morphic-blocks";
import definitions from "../src/definitions.json";
import { behaviors } from "../src/behaviors";
test("definitions load", () => {
const engine = new MorphicBlocks(definitions, behaviors);
expect(() =>
engine.mount({
workspaceContainer: document.createElement("div"),
codespaceContainer: document.createElement("div"),
}),
).not.toThrow();
});

Übergib einen codespaceContainer, sobald eines deiner Presets einen Codespace zeigt, genau wie in deiner App.

Am einfachsten kommt ein Programm in einen Test, wenn du es in deiner App baust und speicherst:

// in deiner App, z. B. über die Browser-Konsole
JSON.stringify(engine.serializeWorkspace());

Speichere das Ergebnis als Datei, etwa test/programs/hello.json, und prüfe, was es erzeugt und ausgibt:

test/programs.test.ts
import { expect, test } from "vitest";
import { MorphicBlocks } from "morphic-blocks";
import definitions from "../src/definitions.json";
import { behaviors } from "../src/behaviors";
import hello from "./programs/hello.json";
test("hello prints a greeting", () => {
const engine = new MorphicBlocks(definitions, behaviors);
engine.mount({
workspaceContainer: document.createElement("div"),
codespaceContainer: document.createElement("div"),
});
engine.loadWorkspace(hello);
expect(engine.generateJavaScript()).toContain("console.log");
expect(engine.runJavaScript().output).toEqual([{ level: "log", text: "Hello, world!" }]);
});

runJavaScript() sammelt jede ausgegebene Zeile samt Stufe (siehe Behaviors & Code-Erzeugung). Eine Prüfung auf output testet also, was das Programm tatsächlich tut, nicht nur, welchen Code es erzeugt.

  • Halte ein gespeichertes Programm pro Behavior, das dir wichtig ist. Ändert sich ein Behavior, zeigt der Test, welche Programme jetzt anderen Code erzeugen.
  • Ein gespeichertes Programm enthält nur die Blöcke, nicht die Modes, und auch der erzeugte Code hängt nicht vom Mode ab. Eine Datei genügt, egal in welchem Mode sie gespeichert wurde. Siehe Speichern & Laden.
  • Während der Tests wird nichts auf dem Bildschirm gezeichnet, deshalb laufen sie in deutlich unter einer Sekunde.