mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-25 20:05:46 -06:00
4fac990321
* fix(test): run the shared Control UI lane on the cross-file cleanup runner ui/vitest.config.ts drives CI's checks-ui job (pnpm --dir ui test). Its unit project sets isolate:false but never wired runner: nonIsolatedRunnerPath, so the per-file cleanup in test/non-isolated-runner.ts — module-graph reset, repo-owned custom element dropping, DOM body reset, timer and spy restoration — never ran in the lane CI actually uses. Only the repo-root lane behind scripts/run-vitest.mjs loaded it. Files sharing a worker therefore kept the previous file's evaluated modules, so whichever file imported a component first pinned it to the real dependency and a later file's vi.mock factory never reached production code, surfacing as "expected 0 to be 1" in whichever sibling the size sequencer happened to pack alongside it. This is the class PR #123512 diagnosed and fixed at the runner; the fix never reached this lane, so the repo kept absorbing it one uiIsolatedTestFiles entry at a time. browser stays exempt (the runner imports node:fs and server modules that cannot load in browser mode) and unit-node stays exempt (it carries the Playwright-driven layout tests whose browser lives in module scope, which per-file module resets churn). The config test asserted runner was undefined for every project, pinning the broken wiring; it now asserts the invariant and fails on the pre-fix config. * fix(agents): stop passing an ignored resolver to instance-bound announce dispatch check-prod-types is red on main: #126062 threaded resolveGatewayContext into the announce dispatch call, but that call now goes through dispatchGatewayLifecycleMethod, whose options type does not carry the field. The type checker is right that it does not belong there. That dispatcher hands work to runtime.dispatchAgent, which resolves context from the Gateway instance it is bound to and forwards a fixed option allowlist, so a caller-supplied resolver was already being ignored. Dropping it is behavior-preserving. The delivery test asserted the resolver was forwarded, but production now binds to the instance dispatcher while the test injects a mock, so that assertion only proved the mock. It now asserts the resolver is deliberately not forwarded. Left for the owner of #126062: sendSubagentAnnounceDirectly and its callers still accept and thread resolveGatewayContext, which is now vestigial on this path. Deleting that chain or teaching the instance runtime to honor the resolver is a design call on a just-landed change.
109 lines
3.9 KiB
TypeScript
109 lines
3.9 KiB
TypeScript
// Vitest UI package config tests validate UI package test project settings.
|
|
import path from "node:path";
|
|
import { describe, expect, it } from "vitest";
|
|
import uiConfig from "../ui/vitest.config.ts";
|
|
import uiNodeConfig from "../ui/vitest.node.config.ts";
|
|
import { normalizeConfigPath } from "./helpers/vitest-config-paths.js";
|
|
|
|
type ExpectedTestConfig = {
|
|
browser?: { enabled?: boolean };
|
|
isolate?: boolean;
|
|
name?: string;
|
|
pool?: string;
|
|
projects?: unknown[];
|
|
runner?: string;
|
|
};
|
|
|
|
function requireTestConfig(config: unknown): ExpectedTestConfig {
|
|
if (!config || typeof config !== "object" || !("test" in config) || !config.test) {
|
|
throw new Error("expected ui package vitest test config");
|
|
}
|
|
return config.test as ExpectedTestConfig;
|
|
}
|
|
|
|
function requireAlias(config: unknown, specifier: string): { find: string; replacement: string } {
|
|
const aliases = (config as { resolve?: { alias?: unknown } }).resolve?.alias;
|
|
if (!Array.isArray(aliases)) {
|
|
throw new Error("expected ui package vitest aliases");
|
|
}
|
|
const alias = aliases.find((candidate): candidate is { find: string; replacement: string } =>
|
|
Boolean(
|
|
candidate &&
|
|
typeof candidate === "object" &&
|
|
"find" in candidate &&
|
|
candidate.find === specifier &&
|
|
"replacement" in candidate &&
|
|
typeof candidate.replacement === "string",
|
|
),
|
|
);
|
|
if (!alias) {
|
|
throw new Error(`missing ui package vitest alias ${specifier}`);
|
|
}
|
|
return alias;
|
|
}
|
|
|
|
describe("ui package vitest config", () => {
|
|
it("keeps the standalone ui package on thread workers without broad isolation", () => {
|
|
const testConfig = requireTestConfig(uiConfig);
|
|
|
|
expect(testConfig.pool).toBe("threads");
|
|
expect(testConfig.isolate).toBe(false);
|
|
expect(testConfig.projects).toHaveLength(4);
|
|
|
|
for (const project of testConfig.projects ?? []) {
|
|
const projectTestConfig = requireTestConfig(project);
|
|
expect(projectTestConfig.pool).toBe("threads");
|
|
expect(projectTestConfig.isolate).toBe(projectTestConfig.name === "unit-mock-registry");
|
|
}
|
|
});
|
|
|
|
// The invariant, not a snapshot: `unit` shares one module graph and jsdom
|
|
// window across files, so without the cleanup runner the first file to
|
|
// evaluate a component owns it for the whole worker and a later file's
|
|
// vi.mock never reaches production. Two projects are exempt for stated
|
|
// reasons: `browser` runs in browser mode, where the runner's node:fs and
|
|
// server-module imports cannot load, and `unit-node` carries the
|
|
// Playwright-driven layout tests whose browser lives in module scope, which
|
|
// per-file module resets churn.
|
|
it("runs the shared jsdom ui project on the cross-file cleanup runner", () => {
|
|
const projects = requireTestConfig(uiConfig).projects ?? [];
|
|
const wiring = projects.map((project) => {
|
|
const projectTestConfig = requireTestConfig(project);
|
|
return {
|
|
name: projectTestConfig.name,
|
|
runner: projectTestConfig.runner
|
|
? normalizeConfigPath(projectTestConfig.runner)
|
|
: undefined,
|
|
};
|
|
});
|
|
|
|
expect(wiring).toEqual([
|
|
{ name: "unit", runner: "test/non-isolated-runner.ts" },
|
|
{ name: "unit-mock-registry", runner: undefined },
|
|
{ name: "unit-node", runner: undefined },
|
|
{ name: "browser", runner: undefined },
|
|
]);
|
|
});
|
|
|
|
it("keeps the standalone ui node config on thread workers without isolation", () => {
|
|
const testConfig = requireTestConfig(uiNodeConfig);
|
|
|
|
expect(testConfig.pool).toBe("threads");
|
|
expect(testConfig.isolate).toBe(false);
|
|
expect(testConfig.runner).toBeUndefined();
|
|
});
|
|
|
|
it("aliases the scope-upgrade workspace subpath for clean browser test checkouts", () => {
|
|
expect(requireAlias(uiConfig, "@openclaw/gateway-client/scope-upgrade")).toEqual({
|
|
find: "@openclaw/gateway-client/scope-upgrade",
|
|
replacement: path.join(
|
|
process.cwd(),
|
|
"packages",
|
|
"gateway-client",
|
|
"src",
|
|
"scope-upgrade.ts",
|
|
),
|
|
});
|
|
});
|
|
});
|