test: replace ui prototype method patches with instance stubs

This commit is contained in:
Peter Steinberger
2026-02-17 01:57:42 +01:00
parent c20ef582cb
commit dcdbbd8b3b
3 changed files with 8 additions and 31 deletions

View File

@@ -73,6 +73,7 @@
- Never add `@ts-nocheck` and do not disable `no-explicit-any`; fix root causes and update Oxlint/Oxfmt config only when required.
- Never share class behavior via prototype mutation (`applyPrototypeMixins`, `Object.defineProperty` on `.prototype`, or exporting `Class.prototype` for merges). Use explicit inheritance/composition (`A extends B extends C`) or helper composition so TypeScript can typecheck.
- If this pattern is needed, stop and get explicit approval before shipping; default behavior is to split/refactor into an explicit class hierarchy and keep members strongly typed.
- In tests, prefer per-instance stubs over prototype mutation (`SomeClass.prototype.method = ...`) unless a test explicitly documents why prototype-level patching is required.
- Add brief code comments for tricky or non-obvious logic.
- Keep files concise; extract helpers instead of “V2” copies. Use existing patterns for CLI options and dependency injection via `createDefaultDeps`.
- Aim to keep files under ~700 LOC; guideline only (not a hard guardrail). Split/refactor when it improves clarity or testability.

View File

@@ -1,15 +1,11 @@
import { afterEach, beforeEach, describe, expect, it } from "vitest";
import { OpenClawApp } from "./app.ts";
import { describe, expect, it } from "vitest";
import "../styles.css";
import { mountApp as mountTestApp, registerAppMountHooks } from "./test-helpers/app-mount.ts";
// oxlint-disable-next-line typescript/unbound-method
const originalConnect = OpenClawApp.prototype.connect;
registerAppMountHooks();
function mountApp(pathname: string) {
window.history.replaceState({}, "", pathname);
const app = document.createElement("openclaw-app") as OpenClawApp;
document.body.append(app);
return app;
return mountTestApp(pathname);
}
function nextFrame() {
@@ -18,22 +14,6 @@ function nextFrame() {
});
}
beforeEach(() => {
OpenClawApp.prototype.connect = () => {
// no-op: avoid real gateway WS connections in browser tests
};
window.__OPENCLAW_CONTROL_UI_BASE_PATH__ = undefined;
localStorage.clear();
document.body.innerHTML = "";
});
afterEach(() => {
OpenClawApp.prototype.connect = originalConnect;
window.__OPENCLAW_CONTROL_UI_BASE_PATH__ = undefined;
localStorage.clear();
document.body.innerHTML = "";
});
describe("control UI routing", () => {
it("hydrates the tab from the location", async () => {
const app = mountApp("/sessions");

View File

@@ -1,28 +1,24 @@
import { afterEach, beforeEach } from "vitest";
import { OpenClawApp } from "../app.ts";
// oxlint-disable-next-line typescript/unbound-method
const originalConnect = OpenClawApp.prototype.connect;
export function mountApp(pathname: string) {
window.history.replaceState({}, "", pathname);
const app = document.createElement("openclaw-app") as OpenClawApp;
app.connect = () => {
// no-op: avoid real gateway WS connections in browser tests
};
document.body.append(app);
return app;
}
export function registerAppMountHooks() {
beforeEach(() => {
OpenClawApp.prototype.connect = () => {
// no-op: avoid real gateway WS connections in browser tests
};
window.__OPENCLAW_CONTROL_UI_BASE_PATH__ = undefined;
localStorage.clear();
document.body.innerHTML = "";
});
afterEach(() => {
OpenClawApp.prototype.connect = originalConnect;
window.__OPENCLAW_CONTROL_UI_BASE_PATH__ = undefined;
localStorage.clear();
document.body.innerHTML = "";