mirror of
https://github.com/openclaw/openclaw.git
synced 2026-10-03 17:53:39 +00:00
test(ui): align e2e fixtures with startup readiness
Bisecting 8b3f9bd1d..9fe93a204 identifiesd769e31af2as the introducing ordering change: foreground history now waits for stream admission. Its parent16393180bapasses the six targeted cases; the commit exposes avatar rendering and sidebar request-count failures. The mock advertised session.members.listEvidence but returned an empty object. The sharing header then crashed on members.map before avatars could render. Supply the required response through the canonical session fixture owner, alongside its existing resolve and describe projections. Force pending startup in the locked-model case, then await enabled effort controls. Force pending relay creation in the Talk case: microphone acquisition happens before admission, so wait for the input processor before emitting relay events. Keep all transcript, layout, lock, and request-count assertions. Retain main's sidebar repair6b9da9dc02. Validation on main9b730f0cfbplus this diff: - Four complete UI E2E files: 27 passed; one worker, 65.44s wall. Talk file 12.254s; partial-catalog file 8.804s test time. - Both mock Gateway/session owner files: 69 tests passed, one worker. - UI typecheck, targeted lint, formatting, and diff checks passed. - Final independent autoreview: scoped-clean through P2. - Changed checks pass conflict and line-growth gates, then encounter the pre-existing OPENCLAW_* budget failure (484/483), independently reproduced from the untouched index. The coordinator owns that separate main break and explicitly authorized this scoped repair to proceed without changing it. Release-note context: test reliability only; production behavior unchanged.
This commit is contained in:
parent
9b730f0cfb
commit
f03d64f6a5
4 changed files with 56 additions and 16 deletions
|
|
@ -688,6 +688,7 @@ suite.define(() => {
|
|||
await suite.withPage({ permissions: ["microphone"] }, async ({ page }) => {
|
||||
const relaySessionId = "relay-e2e-transcript";
|
||||
const gateway = await installMockGateway(page, {
|
||||
deferredMethods: ["talk.client.create"],
|
||||
methodResponses: {
|
||||
"talk.client.create": {
|
||||
provider: "openai",
|
||||
|
|
@ -710,20 +711,33 @@ suite.define(() => {
|
|||
await page.setViewportSize({ width: 1366, height: 900 });
|
||||
await page.getByRole("button", { name: "Start voice input" }).click();
|
||||
await gateway.waitForRequest("talk.client.create");
|
||||
// The request is recorded before its mock response is delivered. Wait for
|
||||
// microphone setup before probing relay readiness below.
|
||||
// Microphone acquisition precedes relay admission and cannot prove readiness.
|
||||
expect(
|
||||
await page.evaluate(() => {
|
||||
const state = (
|
||||
window as Window & {
|
||||
openclawTalkE2eState?: { constraints: unknown[]; inputProcessor: unknown };
|
||||
}
|
||||
).openclawTalkE2eState;
|
||||
return {
|
||||
microphoneRequests: state?.constraints.length,
|
||||
inputReady: state?.inputProcessor != null,
|
||||
};
|
||||
}),
|
||||
).toEqual({ microphoneRequests: 1, inputReady: false });
|
||||
await gateway.resolveDeferred("talk.client.create");
|
||||
await expect
|
||||
.poll(() =>
|
||||
page.evaluate(
|
||||
() =>
|
||||
(
|
||||
window as Window & {
|
||||
openclawTalkE2eState?: { constraints: unknown[] };
|
||||
openclawTalkE2eState?: { inputProcessor: unknown };
|
||||
}
|
||||
).openclawTalkE2eState?.constraints.length,
|
||||
).openclawTalkE2eState?.inputProcessor != null,
|
||||
),
|
||||
)
|
||||
.toBe(1);
|
||||
.toBe(true);
|
||||
await gateway.emitGatewayEvent("talk.event", { relaySessionId, type: "ready" });
|
||||
await expect
|
||||
.poll(() => page.locator('.agent-chat__voice-activity[data-status="listening"]').count())
|
||||
|
|
|
|||
|
|
@ -202,8 +202,9 @@ suite.define(() => {
|
|||
|
||||
it("keeps usable effort controls when the selected model is locked during a partial refresh", async () => {
|
||||
await suite.withPage({ locale: "en-US" }, async ({ page }) => {
|
||||
await installMockGateway(page, {
|
||||
const gateway = await installMockGateway(page, {
|
||||
agentModel: partialConfig.agents.defaults.model,
|
||||
heldMethods: ["chat.startup"],
|
||||
models: catalog.models,
|
||||
methodResponses: {
|
||||
"models.list": catalog,
|
||||
|
|
@ -227,8 +228,12 @@ suite.define(() => {
|
|||
},
|
||||
});
|
||||
await page.goto(`${suite.server.baseUrl}chat`);
|
||||
await gateway.waitForRequest("chat.startup");
|
||||
const effort = page.locator("[data-chat-thinking-select]");
|
||||
await expect.poll(() => effort.isVisible()).toBe(true);
|
||||
expect(await effort.getAttribute("aria-disabled")).toBe("true");
|
||||
await gateway.resolveDeferred("chat.startup");
|
||||
await page.locator('[data-chat-thinking-select][aria-disabled="false"]').waitFor();
|
||||
expect(await effort.getAttribute("aria-disabled")).toBe("false");
|
||||
expect(await page.locator(".chat-controls__effort-picker").getAttribute("aria-hidden")).toBe(
|
||||
"false",
|
||||
|
|
|
|||
|
|
@ -2097,7 +2097,9 @@ function installControlUiMockGateway(
|
|||
case "artifacts.download":
|
||||
return null;
|
||||
case "sessions.resolve":
|
||||
return sessions.resolve(isRecord(params) ? params : {});
|
||||
case "sessions.describe":
|
||||
case "session.members.listEvidence":
|
||||
return sessions.readResponse(method, params, scenario);
|
||||
case "chat.history":
|
||||
case "chat.startup": {
|
||||
const resolution =
|
||||
|
|
@ -2146,11 +2148,6 @@ function installControlUiMockGateway(
|
|||
: {}),
|
||||
};
|
||||
}
|
||||
case "sessions.describe": {
|
||||
const key =
|
||||
isRecord(params) && typeof params.key === "string" ? params.key : scenario.sessionKey;
|
||||
return { session: sessions.sessionInfo(key) ?? null };
|
||||
}
|
||||
case "chat.metadata":
|
||||
return {
|
||||
commands: [],
|
||||
|
|
|
|||
|
|
@ -492,13 +492,37 @@ export function createControlUiSessionFixtures(
|
|||
? { ok: true, ...only }
|
||||
: { ok: false, ...(matches.length ? { candidates: matches.slice(0, 10) } : {}) };
|
||||
};
|
||||
// History publishes a full row replacement. An unseeded wire-only fixture
|
||||
// has no canonical metadata to publish until its caller declares the row.
|
||||
const sessionInfo = (key: string) =>
|
||||
listed.has(canonicalKey(key)) ? sample(read(key), Date.now()) : undefined;
|
||||
return {
|
||||
read,
|
||||
resolve,
|
||||
// History publishes a full row replacement. An unseeded wire-only fixture
|
||||
// has no canonical metadata to publish until its caller declares the row.
|
||||
sessionInfo: (key: string) =>
|
||||
listed.has(canonicalKey(key)) ? sample(read(key), Date.now()) : undefined,
|
||||
sessionInfo,
|
||||
readResponse(
|
||||
method: "sessions.resolve" | "sessions.describe" | "session.members.listEvidence",
|
||||
params: unknown,
|
||||
scenario: { sessionKey: string; allowedSessionVisibilities: readonly string[] },
|
||||
) {
|
||||
if (method === "sessions.resolve") {
|
||||
return resolve(isRecord(params) ? params : {});
|
||||
}
|
||||
const field = method === "sessions.describe" ? "key" : "sessionKey";
|
||||
const requestedKey = isRecord(params) ? params[field] : undefined;
|
||||
const key = typeof requestedKey === "string" ? requestedKey : scenario.sessionKey;
|
||||
if (method === "sessions.describe") {
|
||||
return { session: sessionInfo(key) ?? null };
|
||||
}
|
||||
const row = read(key);
|
||||
return {
|
||||
sessionKey: row.key,
|
||||
members: [],
|
||||
identities: [],
|
||||
role: row.sharingRole ?? "admin",
|
||||
allowedVisibilities: scenario.allowedSessionVisibilities,
|
||||
};
|
||||
},
|
||||
patch,
|
||||
abortRuns,
|
||||
trackRun,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue