Files
STCourses/packages/feature-learner/test/runner.test.ts
T

63 lines
3.2 KiB
TypeScript

import { expect, test } from "bun:test";
import { readFileSync } from "node:fs";
import { join } from "node:path";
import { createBrowserRunner } from "../src/web/runner/runner";
import { transpileTs } from "../src/server/transpile";
import { FIXTURES } from "./app";
const ex = (f: string) => readFileSync(join(FIXTURES, "sample/exercises/add", f), "utf8");
const js = createBrowserRunner("js");
test("the sample starter fails with expected and actual values", async () => {
const r = await js.run(ex("starter.js"), ex("tests.js"), 5000);
expect(r).toEqual([
{ name: "adds 2 and 3", passed: false, message: "Expected values to be strictly equal", expected: "5", actual: "-1" },
{ name: "adds -1 and 1", passed: false, message: "Expected values to be strictly equal", expected: "0", actual: "-2" },
]);
});
test("the sample exercise passes after editing", async () => {
const edited = ex("starter.js").replace("a - b", "a + b");
expect((await js.run(edited, ex("tests.js"), 5000)).every((t) => t.passed)).toBe(true);
});
test("syntax errors and missing exports are reported per test", async () => {
const r = await js.run("export function add(a, b) { return a +", ex("tests.js"), 5000);
expect(r.length).toBe(2);
expect(r[0]!.passed).toBe(false);
expect(r[0]!.message).toContain("did not load");
const r2 = await js.run("export const nope = 1", ex("tests.js"), 5000);
expect(r2[0]!.message).toContain("TypeError");
});
test("an endless loop is killed by the timeout", async () => {
const t0 = Date.now();
const r = await js.run("export function add() { while (true) {} }", ex("tests.js"), 400);
expect(Date.now() - t0).toBeLessThan(3000);
expect(r.map((x) => x.passed)).toEqual([false, false]);
expect(r[0]!.message).toContain("Timed out");
});
test("network access is blocked inside the worker", async () => {
const tests = `export const tests = [{ name: "net", run: async (m, assert) => assert.equal(await m.probe(), "blocked") }];`;
const code = `export async function probe() { try { await fetch("http://127.0.0.1:1/"); return "open"; } catch (e) { return String(e.message).includes("turned off") ? "blocked" : "other " + e.message; } }`;
expect((await js.run(code, tests, 3000))[0]!.passed).toBe(true);
});
test("deepEqual compares structures", async () => {
const tests = `export const tests = [
{ name: "same", run: (m, a) => a.deepEqual(m.v(), { x: [1, 2], y: { z: null } }) },
{ name: "diff", run: (m, a) => a.deepEqual(m.v(), { x: [1, 3] }) },
];`;
const r = await js.run("export const v = () => ({ x: [1, 2], y: { z: null } })", tests, 3000);
expect(r[0]!.passed).toBe(true);
expect(r[1]).toMatchObject({ passed: false, expected: "{x: [1, 3]}", actual: "{x: [1, 2], y: {z: null}}" });
});
test("TypeScript exercises are transpiled first", async () => {
const runner = createBrowserRunner("ts", { transpile: async (c) => transpileTs(c) });
const tests = `import type { X } from "./x"; export const tests = [{ name: "typed", run: (m: any, a: any) => a.equal(m.add(1, 2), 3) }];`;
const r = await runner.run("export function add(a: number, b: number): number { return a + b; }", tests, 3000);
expect(r).toEqual([{ name: "typed", passed: true }]);
});