test: dedupe and optimize test suites

This commit is contained in:
Peter Steinberger
2026-02-19 15:18:50 +00:00
parent b0e55283d5
commit a1cb700a05
80 changed files with 2627 additions and 2962 deletions

View File

@@ -1,4 +1,5 @@
import { describe, expect, it } from "vitest";
import { resolveBrowserConfig } from "./config.js";
import {
allocateCdpPort,
allocateColor,
@@ -11,15 +12,12 @@ import {
} from "./profiles.js";
describe("profile name validation", () => {
it("accepts valid lowercase names", () => {
expect(isValidProfileName("openclaw")).toBe(true);
expect(isValidProfileName("work")).toBe(true);
expect(isValidProfileName("my-profile")).toBe(true);
expect(isValidProfileName("test123")).toBe(true);
expect(isValidProfileName("a")).toBe(true);
expect(isValidProfileName("a-b-c-1-2-3")).toBe(true);
expect(isValidProfileName("1test")).toBe(true);
});
it.each(["openclaw", "work", "my-profile", "test123", "a", "a-b-c-1-2-3", "1test"])(
"accepts valid lowercase name: %s",
(name) => {
expect(isValidProfileName(name)).toBe(true);
},
);
it("rejects empty or missing names", () => {
expect(isValidProfileName("")).toBe(false);
@@ -37,23 +35,19 @@ describe("profile name validation", () => {
expect(isValidProfileName(maxName)).toBe(true);
});
it("rejects uppercase letters", () => {
expect(isValidProfileName("MyProfile")).toBe(false);
expect(isValidProfileName("PROFILE")).toBe(false);
expect(isValidProfileName("Work")).toBe(false);
});
it("rejects spaces and special characters", () => {
expect(isValidProfileName("my profile")).toBe(false);
expect(isValidProfileName("my_profile")).toBe(false);
expect(isValidProfileName("my.profile")).toBe(false);
expect(isValidProfileName("my/profile")).toBe(false);
expect(isValidProfileName("my@profile")).toBe(false);
});
it("rejects names starting with hyphen", () => {
expect(isValidProfileName("-invalid")).toBe(false);
expect(isValidProfileName("--double")).toBe(false);
it.each([
"MyProfile",
"PROFILE",
"Work",
"my profile",
"my_profile",
"my.profile",
"my/profile",
"my@profile",
"-invalid",
"--double",
])("rejects invalid name: %s", (name) => {
expect(isValidProfileName(name)).toBe(false);
});
});
@@ -131,9 +125,8 @@ describe("getUsedPorts", () => {
});
describe("port collision prevention", () => {
it("raw config vs resolved config - shows the data source difference", async () => {
it("raw config vs resolved config - shows the data source difference", () => {
// This demonstrates WHY the route handler must use resolved config
const { resolveBrowserConfig } = await import("./config.js");
// Fresh config with no profiles defined (like a new install)
const rawConfigProfiles = undefined;
@@ -148,9 +141,8 @@ describe("port collision prevention", () => {
expect(usedFromResolved.has(CDP_PORT_RANGE_START)).toBe(true);
});
it("create-profile must use resolved config to avoid port collision", async () => {
it("create-profile must use resolved config to avoid port collision", () => {
// The route handler must use state.resolved.profiles, not raw config
const { resolveBrowserConfig } = await import("./config.js");
// Simulate what happens with raw config (empty) vs resolved config
const rawConfig: { browser: { profiles?: Record<string, { cdpPort?: number }> } } = {

View File

@@ -1,4 +1,4 @@
import { describe, expect, it, vi } from "vitest";
import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
let page: { evaluate: ReturnType<typeof vi.fn> } | null = null;
let locator: { evaluate: ReturnType<typeof vi.fn> } | null = null;
@@ -29,64 +29,61 @@ vi.mock("./pw-session.js", () => {
};
});
describe("evaluateViaPlaywright (abort)", () => {
it("rejects when aborted after page.evaluate starts", async () => {
vi.clearAllMocks();
const ctrl = new AbortController();
let evaluateViaPlaywright: typeof import("./pw-tools-core.interactions.js").evaluateViaPlaywright;
let evalCalled!: () => void;
const evalCalledPromise = new Promise<void>((resolve) => {
evalCalled = resolve;
});
function createPendingEval() {
let evalCalled!: () => void;
const evalCalledPromise = new Promise<void>((resolve) => {
evalCalled = resolve;
});
return {
evalCalledPromise,
resolveEvalCalled: evalCalled,
};
}
describe("evaluateViaPlaywright (abort)", () => {
beforeAll(async () => {
({ evaluateViaPlaywright } = await import("./pw-tools-core.interactions.js"));
});
beforeEach(() => {
vi.clearAllMocks();
});
it.each([
{ label: "page.evaluate", fn: "() => 1" },
{ label: "locator.evaluate", fn: "(el) => el.textContent", ref: "e1" },
])("rejects when aborted after $label starts", async ({ fn, ref }) => {
const ctrl = new AbortController();
const pending = createPendingEval();
const pendingPromise = new Promise(() => {});
page = {
evaluate: vi.fn(() => {
evalCalled();
return new Promise(() => {});
if (!ref) {
pending.resolveEvalCalled();
}
return pendingPromise;
}),
};
locator = { evaluate: vi.fn() };
const { evaluateViaPlaywright } = await import("./pw-tools-core.interactions.js");
const p = evaluateViaPlaywright({
cdpUrl: "http://127.0.0.1:9222",
fn: "() => 1",
signal: ctrl.signal,
});
await evalCalledPromise;
ctrl.abort(new Error("aborted by test"));
await expect(p).rejects.toThrow("aborted by test");
expect(forceDisconnectPlaywrightForTarget).toHaveBeenCalled();
});
it("rejects when aborted after locator.evaluate starts", async () => {
vi.clearAllMocks();
const ctrl = new AbortController();
let evalCalled!: () => void;
const evalCalledPromise = new Promise<void>((resolve) => {
evalCalled = resolve;
});
page = { evaluate: vi.fn() };
locator = {
evaluate: vi.fn(() => {
evalCalled();
return new Promise(() => {});
if (ref) {
pending.resolveEvalCalled();
}
return pendingPromise;
}),
};
const { evaluateViaPlaywright } = await import("./pw-tools-core.interactions.js");
const p = evaluateViaPlaywright({
cdpUrl: "http://127.0.0.1:9222",
fn: "(el) => el.textContent",
ref: "e1",
fn,
ref,
signal: ctrl.signal,
});
await evalCalledPromise;
await pending.evalCalledPromise;
ctrl.abort(new Error("aborted by test"));
await expect(p).rejects.toThrow("aborted by test");

View File

@@ -1,4 +1,4 @@
import { beforeEach, describe, expect, it, vi } from "vitest";
import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
import { resolveBrowserConfig } from "./config.js";
import {
refreshResolvedBrowserConfigFromDisk,
@@ -40,6 +40,12 @@ vi.mock("../config/config.js", () => ({
}));
describe("server-context hot-reload profiles", () => {
let loadConfig: typeof import("../config/config.js").loadConfig;
beforeAll(async () => {
({ loadConfig } = await import("../config/config.js"));
});
beforeEach(() => {
vi.clearAllMocks();
cfgProfiles = {
@@ -49,8 +55,6 @@ describe("server-context hot-reload profiles", () => {
});
it("forProfile hot-reloads newly added profiles from config", async () => {
const { loadConfig } = await import("../config/config.js");
// Start with only openclaw profile
// 1. Prime the cache by calling loadConfig() first
const cfg = loadConfig();
@@ -101,8 +105,6 @@ describe("server-context hot-reload profiles", () => {
});
it("forProfile still throws for profiles that don't exist in fresh config", async () => {
const { loadConfig } = await import("../config/config.js");
const cfg = loadConfig();
const resolved = resolveBrowserConfig(cfg.browser, cfg);
const state = {
@@ -123,8 +125,6 @@ describe("server-context hot-reload profiles", () => {
});
it("forProfile refreshes existing profile config after loadConfig cache updates", async () => {
const { loadConfig } = await import("../config/config.js");
const cfg = loadConfig();
const resolved = resolveBrowserConfig(cfg.browser, cfg);
const state = {
@@ -147,8 +147,6 @@ describe("server-context hot-reload profiles", () => {
});
it("listProfiles refreshes config before enumerating profiles", async () => {
const { loadConfig } = await import("../config/config.js");
const cfg = loadConfig();
const resolved = resolveBrowserConfig(cfg.browser, cfg);
const state = {