mirror of
https://github.com/deepseek-ai/deepseek-harness.git
synced 2026-08-29 04:26:38 +00:00
test(web): gate composer gestures on the editable attribute
A running turn disables the composer by flipping contenteditable to false on the same element. fill() throws there immediately — a disabled textarea used to hold it back through actionability — and isEnabled() reports true for a div regardless, so the permission-policy scenario's post-settle wait never waited and its next gesture raced the re-enable render. The window is a few frames wide; #3083's Remote-routed subagent control stretches settle enough to hit it on CI. writeComposerDraft now waits for contenteditable="true" before acting, and the permission-policy scenario drives all four sends through it with the settle wait pinned to the attribute.
This commit is contained in:
@@ -15,7 +15,7 @@ import {
|
||||
assertFinalWorkspaceSnapshot, assertFixtureInventory, fixtureUserPrompts, launchWebScaffold, recordFixture,
|
||||
watchConsole, webSnapshotMode, type WebScaffold,
|
||||
} from './scaffold.ts'
|
||||
import { connectFreshWorkspace, newEnglishPage, saveFailureShot } from './support.ts'
|
||||
import { connectFreshWorkspace, newEnglishPage, saveFailureShot, writeComposerDraft } from './support.ts'
|
||||
|
||||
const SNAPSHOT_DIR = fileURLToPath(new URL('../../../snapshots/web/permission-policy-context', import.meta.url))
|
||||
const FIXTURE = fileURLToPath(new URL('../../../snapshots/web/permission-policy-context/session.jsonl', import.meta.url))
|
||||
@@ -97,23 +97,23 @@ describe('web e2e: current sandbox policy reaches the model before tools', () =>
|
||||
const input = page.locator('[data-composer-input]').first()
|
||||
let sessionId: Awaited<ReturnType<WebScaffold['whenTurnSettled']>> | undefined
|
||||
for (const [index, preset] of ['read-only', 'danger-full-access', 'workspace-write'].entries()) {
|
||||
await input.fill(`/permission ${preset}`)
|
||||
await writeComposerDraft(page, input, `/permission ${preset}`)
|
||||
await input.press('Enter')
|
||||
await page.getByRole('button', { name: `Access mode, current: ${PRESET_LABELS[index]}` })
|
||||
.waitFor({ timeout: 10_000 })
|
||||
|
||||
const settled = scaffold.whenTurnSettled()
|
||||
await input.fill(PROMPTS[index] as string)
|
||||
await writeComposerDraft(page, input, PROMPTS[index] as string)
|
||||
await input.press('Enter')
|
||||
sessionId = await settled
|
||||
await expect.poll(() => input.isEnabled(), { timeout: 10_000 }).toBe(true)
|
||||
await expect.poll(() => input.getAttribute('contenteditable'), { timeout: 10_000 }).toBe('true')
|
||||
}
|
||||
|
||||
await input.fill('/permission read-only')
|
||||
await writeComposerDraft(page, input, '/permission read-only')
|
||||
await input.press('Enter')
|
||||
await page.getByRole('button', { name: 'Access mode, current: Read Only' }).waitFor({ timeout: 10_000 })
|
||||
const settled = scaffold.whenTurnSettled()
|
||||
await input.fill(PROMPTS[3])
|
||||
await writeComposerDraft(page, input, PROMPTS[3])
|
||||
await input.press('Enter')
|
||||
sessionId = await settled
|
||||
|
||||
|
||||
@@ -117,6 +117,13 @@ export async function connectFreshWorkspaceZh(page: Page, root: string, name = '
|
||||
* selection, and the batched edit lands on a null selection and is silently
|
||||
* dropped, leaving the previous draft in place. Real keystrokes leave room for
|
||||
* `selectionchange` between keys, which is also what a user's typing does.
|
||||
*
|
||||
* Waits for the surface to be editable first. A running turn disables the
|
||||
* composer by setting `contenteditable="false"` on the same element, where
|
||||
* `fill()` throws immediately instead of waiting (a disabled `<textarea>`
|
||||
* held it back via actionability) and `isEnabled()` reports `true` for a
|
||||
* `<div>` regardless — so a gesture directly after a turn settles must gate
|
||||
* on the attribute, not on enablement.
|
||||
* @param page - the page under test.
|
||||
* @param input - the `[data-composer-input]` surface locator.
|
||||
* @param text - the replacement draft; `''` clears the draft. Must not
|
||||
@@ -127,6 +134,7 @@ export async function writeComposerDraft(
|
||||
input: ReturnType<Page['locator']>,
|
||||
text: string,
|
||||
): Promise<void> {
|
||||
await page.locator('[data-composer-input][contenteditable="true"]').first().waitFor({ timeout: 15_000 })
|
||||
await input.click()
|
||||
await page.keyboard.press('ControlOrMeta+A')
|
||||
if (text === '') await page.keyboard.press('Backspace')
|
||||
|
||||
Reference in New Issue
Block a user