源码快照 @ 2026-09-02 (source-only, 剔除非源码素材)
This commit is contained in:
@@ -0,0 +1,89 @@
|
||||
import { expect } from "vitest";
|
||||
import { control, createAndSelectWorkspace, evalIn, waitFor } from "@openwork/behaviors";
|
||||
import { desktop } from "@openwork/hosts";
|
||||
import { needs, test } from "@openwork/testkit";
|
||||
|
||||
const e2eTestsEnabled = process.env.OPENWORK_EVAL_E2E_TESTS === "1";
|
||||
const title = e2eTestsEnabled
|
||||
? "same-session refresh preserves composer focus and uninterrupted typing"
|
||||
: "composer focus continuity skipped — needs: set OPENWORK_EVAL_E2E_TESTS=1";
|
||||
|
||||
test.skipIf(!e2eTestsEnabled)(title, async ({ evidence }) => {
|
||||
needs({ optIn: ["OPENWORK_EVAL_E2E_TESTS"] });
|
||||
|
||||
await using app = await desktop({ name: "composer-focus-continuity" });
|
||||
await createAndSelectWorkspace(app, {
|
||||
path: `/tmp/openwork-composer-focus-continuity-${Date.now()}`,
|
||||
});
|
||||
await waitFor(
|
||||
app,
|
||||
`window.__openworkControl.listActions().some((action) => action.id === "session.create_task" && !action.disabled)`,
|
||||
{ timeoutMs: 30_000, label: "new task control ready" },
|
||||
);
|
||||
await control(app, "session.create_task");
|
||||
await waitFor(
|
||||
app,
|
||||
`window.__openworkControl.listActions().some((action) => action.id === "eval.composer_focus.refresh_current_session" && !action.disabled)`,
|
||||
{ timeoutMs: 30_000, label: "composer refresh proof control ready" },
|
||||
);
|
||||
await waitFor(
|
||||
app,
|
||||
`Boolean(document.querySelector('[contenteditable="true"][data-lexical-editor="true"]'))`,
|
||||
{ timeoutMs: 30_000, label: "composer editor ready" },
|
||||
);
|
||||
|
||||
const firstDraft = "Keep this draft while the task finishes";
|
||||
const suffix = " and let me keep typing.";
|
||||
const focused = await evalIn(app, `(() => {
|
||||
const editor = document.querySelector('[contenteditable="true"][data-lexical-editor="true"]');
|
||||
if (!(editor instanceof HTMLElement)) return false;
|
||||
editor.focus();
|
||||
return document.activeElement === editor;
|
||||
})()`);
|
||||
expect(focused).toBe(true);
|
||||
await app.client.send("Input.insertText", { text: firstDraft });
|
||||
await waitFor(
|
||||
app,
|
||||
`(document.querySelector('[contenteditable="true"][data-lexical-editor="true"]')?.innerText ?? "").trim() === ${JSON.stringify(firstDraft)}`,
|
||||
{ timeoutMs: 10_000, label: "initial composer draft" },
|
||||
);
|
||||
|
||||
const refresh = await control(app, "eval.composer_focus.refresh_current_session");
|
||||
expect(refresh).toMatchObject({
|
||||
ok: true,
|
||||
wasFocused: true,
|
||||
remainsFocused: true,
|
||||
editable: true,
|
||||
});
|
||||
|
||||
const stateAfterRefresh = await evalIn(app, `(() => {
|
||||
const editor = document.querySelector('[contenteditable="true"][data-lexical-editor="true"]');
|
||||
return {
|
||||
focused: editor instanceof HTMLElement && document.activeElement === editor,
|
||||
editable: editor?.getAttribute('contenteditable') === 'true',
|
||||
text: editor instanceof HTMLElement ? editor.innerText.trim() : '',
|
||||
};
|
||||
})()`);
|
||||
expect(stateAfterRefresh).toEqual({
|
||||
focused: true,
|
||||
editable: true,
|
||||
text: firstDraft,
|
||||
});
|
||||
evidence.recordAssertionEvidence(
|
||||
"A same-session completion refresh preserves composer focus and editability",
|
||||
"The active Lexical editor remained focused and contenteditable after its session snapshot refreshed.",
|
||||
true,
|
||||
);
|
||||
|
||||
await app.client.send("Input.insertText", { text: suffix });
|
||||
await waitFor(
|
||||
app,
|
||||
`(document.querySelector('[contenteditable="true"][data-lexical-editor="true"]')?.innerText ?? "").trim() === ${JSON.stringify(firstDraft + suffix)}`,
|
||||
{ timeoutMs: 10_000, label: "continued composer draft" },
|
||||
);
|
||||
evidence.recordAssertionEvidence(
|
||||
"The existing draft remains intact and typing continues without another click",
|
||||
`The composer preserved “${firstDraft}” and accepted the suffix immediately after refresh.`,
|
||||
true,
|
||||
);
|
||||
});
|
||||
Reference in New Issue
Block a user