mirror of
https://github.com/Termix-SSH/Termix.git
synced 2026-10-09 21:32:38 +00:00
182 lines
5.8 KiB
TypeScript
182 lines
5.8 KiB
TypeScript
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
import fs from "node:fs";
|
|
import os from "node:os";
|
|
import path from "node:path";
|
|
import {
|
|
createDebouncer,
|
|
createSerialQueue,
|
|
dataDirFor,
|
|
findBuildablePlugins,
|
|
isPluginSourceChange,
|
|
needsDocker,
|
|
parseDevArgs,
|
|
parseTscStatus,
|
|
} from "./lib/dev-runner.mjs";
|
|
|
|
const cleanups: Array<() => void> = [];
|
|
|
|
afterEach(() => {
|
|
vi.useRealTimers();
|
|
while (cleanups.length) cleanups.pop()!();
|
|
});
|
|
|
|
describe("parseTscStatus", () => {
|
|
it("reads the error count from a watch summary", () => {
|
|
expect(
|
|
parseTscStatus(
|
|
"12:00:00 PM - Found 0 errors. Watching for file changes.",
|
|
),
|
|
).toBe(0);
|
|
expect(parseTscStatus("Found 1 error. Watching for file changes.")).toBe(1);
|
|
expect(parseTscStatus("src/a.ts(1,1): error TS2304: x")).toBeNull();
|
|
});
|
|
});
|
|
|
|
describe("isPluginSourceChange", () => {
|
|
it("accepts sources and skips build output", () => {
|
|
expect(
|
|
isPluginSourceChange(["src", "backend", "index.ts"].join("\\")),
|
|
).toBe(true);
|
|
expect(isPluginSourceChange("locales/en.json")).toBe(true);
|
|
expect(isPluginSourceChange("manifest.json")).toBe(true);
|
|
expect(isPluginSourceChange("dist/backend.js")).toBe(false);
|
|
expect(isPluginSourceChange("node_modules/x/index.js")).toBe(false);
|
|
expect(isPluginSourceChange("notes.txt")).toBe(false);
|
|
expect(isPluginSourceChange(null)).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe("findBuildablePlugins", () => {
|
|
it("lists repos with a manifest and installed dependencies", () => {
|
|
const dir = fs.mkdtempSync(path.join(os.tmpdir(), "termix-dev-"));
|
|
cleanups.push(() => fs.rmSync(dir, { recursive: true, force: true }));
|
|
const make = (name: string, id: string, deps = true) => {
|
|
const repo = path.join(dir, name);
|
|
fs.mkdirSync(repo);
|
|
fs.writeFileSync(
|
|
path.join(repo, "manifest.json"),
|
|
JSON.stringify({ id }),
|
|
);
|
|
if (deps) fs.mkdirSync(path.join(repo, "node_modules"));
|
|
};
|
|
make("Plugin-B", "b");
|
|
make("Plugin-A", "a");
|
|
make("Plugin-C", "c", false);
|
|
fs.mkdirSync(path.join(dir, "notes"));
|
|
|
|
expect(findBuildablePlugins(dir).map((p) => p.id)).toEqual(["a", "b"]);
|
|
expect(findBuildablePlugins(null)).toEqual([]);
|
|
});
|
|
});
|
|
|
|
describe("createSerialQueue", () => {
|
|
it("runs one task at a time and survives a failure", async () => {
|
|
const queue = createSerialQueue();
|
|
const order: string[] = [];
|
|
const first = queue(async () => {
|
|
await new Promise((r) => setTimeout(r, 10));
|
|
order.push("first");
|
|
throw new Error("boom");
|
|
});
|
|
const second = queue(async () => order.push("second"));
|
|
await expect(first).rejects.toThrow("boom");
|
|
await second;
|
|
expect(order).toEqual(["first", "second"]);
|
|
});
|
|
});
|
|
|
|
describe("createDebouncer", () => {
|
|
it("fires once per quiet key and again for a change mid-run", async () => {
|
|
vi.useFakeTimers();
|
|
let release!: () => void;
|
|
const calls: string[] = [];
|
|
const trigger = createDebouncer(100, async (key: string) => {
|
|
calls.push(key);
|
|
if (calls.length === 1) await new Promise<void>((r) => (release = r));
|
|
});
|
|
|
|
trigger("a");
|
|
trigger("a");
|
|
await vi.advanceTimersByTimeAsync(100);
|
|
expect(calls).toEqual(["a"]);
|
|
|
|
trigger("a");
|
|
await vi.advanceTimersByTimeAsync(100);
|
|
expect(calls).toEqual(["a"]);
|
|
release();
|
|
await vi.advanceTimersByTimeAsync(100);
|
|
expect(calls).toEqual(["a", "a"]);
|
|
trigger.cancel();
|
|
});
|
|
});
|
|
|
|
describe("parseDevArgs", () => {
|
|
it("defaults to the web target with sqlite and local plugins", () => {
|
|
expect(parseDevArgs([])).toMatchObject({
|
|
target: "web",
|
|
db: "sqlite",
|
|
local: true,
|
|
guacd: false,
|
|
fresh: false,
|
|
port: 8081,
|
|
});
|
|
});
|
|
|
|
it("reads a target and options in any order", () => {
|
|
expect(
|
|
parseDevArgs(["--db", "Postgres", "electron", "--guacd", "--fresh"]),
|
|
).toMatchObject({
|
|
target: "electron",
|
|
db: "postgres",
|
|
guacd: true,
|
|
fresh: true,
|
|
});
|
|
expect(
|
|
parseDevArgs(["docker", "--port", "9000", "--no-cache", "--no-local"]),
|
|
).toMatchObject({
|
|
target: "docker",
|
|
port: 9000,
|
|
noCache: true,
|
|
local: false,
|
|
});
|
|
expect(parseDevArgs(["--plugins", "../x"]).pluginsDir).toBe("../x");
|
|
});
|
|
|
|
it("refuses unknown or incomplete options", () => {
|
|
expect(() => parseDevArgs(["--bogus"])).toThrow(/Unknown option/);
|
|
expect(() => parseDevArgs(["--db", "oracle"])).toThrow(/--db/);
|
|
expect(() => parseDevArgs(["--db"])).toThrow(/needs a value/);
|
|
expect(() => parseDevArgs(["--port", "abc"])).toThrow(/port/);
|
|
expect(() => parseDevArgs(["--fresh", "--data", "x"])).toThrow(/together/);
|
|
});
|
|
});
|
|
|
|
describe("dataDirFor", () => {
|
|
it("keeps each engine and fresh runs in their own folder", () => {
|
|
expect(dataDirFor(parseDevArgs([]))).toBeNull();
|
|
expect(dataDirFor(parseDevArgs(["--db", "mysql"]))).toBe("db/data-mysql");
|
|
expect(dataDirFor(parseDevArgs(["--fresh"]))).toBe("db/data-fresh");
|
|
expect(dataDirFor(parseDevArgs(["--fresh", "--db", "postgres"]))).toBe(
|
|
"db/data-fresh-postgres",
|
|
);
|
|
expect(dataDirFor(parseDevArgs(["--data", "x"]))).toBe("x");
|
|
expect(dataDirFor(parseDevArgs(["electron"]))).toBe("db/data-desktop");
|
|
expect(dataDirFor(parseDevArgs(["electron", "--db", "mysql"]))).toBe(
|
|
"db/data-desktop-mysql",
|
|
);
|
|
expect(dataDirFor(parseDevArgs(["electron", "--fresh"]))).toBe(
|
|
"db/data-fresh-desktop",
|
|
);
|
|
});
|
|
});
|
|
|
|
describe("needsDocker", () => {
|
|
it("is only true when something runs in a container", () => {
|
|
expect(needsDocker(parseDevArgs([]))).toBe(false);
|
|
expect(needsDocker(parseDevArgs(["electron"]))).toBe(false);
|
|
expect(needsDocker(parseDevArgs(["docker"]))).toBe(true);
|
|
expect(needsDocker(parseDevArgs(["--db", "postgres"]))).toBe(true);
|
|
expect(needsDocker(parseDevArgs(["--guacd"]))).toBe(true);
|
|
});
|
|
});
|