click-session flake (#97982): a bare scrollIntoView() smooth scroll could be dropped under load, and the script slept a fixed 3000ms before reading state. Extract click-session-helpers.mjs: instant centered scroll, a bounded poll-for-composer loop instead of the fixed sleep, and correct nested CDP envelope unwrapping (the old read logged undefined). Intel-Mac installer docs (#99033): the Hermes-Setup.dmg bootstrap installer is built for Apple Silicon only, so Intel Macs hit "not supported on this Mac". The desktop release pipeline already builds a native darwin-x64 bundle, so the docs now scope the arm64 limit to the bootstrap installer and name the darwin-x64 bundle (or the CLI plus `hermes desktop`) as the Intel path, in the desktop README and the platform-support build-targets section. cua-driver autostart opt-in (#97389): Windows installs registered the cua-driver-serve scheduled task on every install, with no opt-out, and treated the task as an install-readiness requirement. Gate the install-ready check and _repair_cua_driver_autostart_windows on the new computer_use.autostart config key (default false = on-demand, fails closed), extract the registration PowerShell into a testable helper, and document the opt-in (EN + zh-Hans). Windows-only code path: unit tests cover the registration args and the config gate; live Windows verification pending.
75 lines
2.7 KiB
JavaScript
75 lines
2.7 KiB
JavaScript
import assert from 'node:assert/strict'
|
|
|
|
import { test } from 'vitest'
|
|
|
|
import {
|
|
buildFindClickExpression,
|
|
CLICK_POLL_TIMEOUT_MS,
|
|
evaluateJsonValue,
|
|
pollUntil,
|
|
POST_CLICK_READY_EXPRESSION
|
|
} from './click-session-helpers.mjs'
|
|
|
|
function cdpMessage(value) {
|
|
// The shape send() resolves with: the whole CDP WebSocket message.
|
|
return { id: 1, result: { result: { type: 'string', value } } }
|
|
}
|
|
|
|
test('buildFindClickExpression scrolls instantly and centered, then clicks', () => {
|
|
const expr = buildFindClickExpression('Phaser particle')
|
|
|
|
assert.ok(expr.includes("scrollIntoView({ behavior: 'auto', block: 'center' })"),
|
|
'must use an instant, centered scroll (no default smooth scroll to drop)')
|
|
assert.ok(expr.includes('found.click()'))
|
|
assert.ok(expr.includes('JSON.stringify'))
|
|
// The title must be embedded as JSON, so quotes/newlines in it cannot break
|
|
// the expression.
|
|
assert.ok(expr.includes(JSON.stringify('Phaser particle')))
|
|
})
|
|
|
|
test('evaluateJsonValue unwraps the nested CDP envelope', () => {
|
|
assert.deepEqual(evaluateJsonValue(cdpMessage('{"found":true}')), { found: true })
|
|
})
|
|
|
|
test('evaluateJsonValue returns null on a truncated envelope, not undefined', () => {
|
|
// The old script read `message.result.value` — one level short — and logged
|
|
// undefined. Any missing level must yield null.
|
|
assert.equal(evaluateJsonValue({}), null)
|
|
assert.equal(evaluateJsonValue({ result: {} }), null)
|
|
assert.equal(evaluateJsonValue({ result: { result: {} } }), null)
|
|
assert.equal(evaluateJsonValue(cdpMessage('not-json')), null)
|
|
})
|
|
|
|
function fakeSend(answers) {
|
|
let i = 0
|
|
|
|
return async () => cdpMessage(answers[Math.min(i++, answers.length - 1)])
|
|
}
|
|
|
|
test('pollUntil resolves true as soon as the predicate answers true (no fixed sleep)', async () => {
|
|
const send = fakeSend(['false', 'false', 'true'])
|
|
|
|
const started = Date.now()
|
|
assert.equal(await pollUntil(send, POST_CLICK_READY_EXPRESSION, { intervalMs: 5 }), true)
|
|
// Three polls at 5ms apart: bounded by readiness, not a fixed 3000ms sleep.
|
|
assert.ok(Date.now() - started < 500)
|
|
})
|
|
|
|
test('pollUntil returns false at the bound instead of hanging when never ready', async () => {
|
|
const send = fakeSend(['false'])
|
|
|
|
const started = Date.now()
|
|
assert.equal(
|
|
await pollUntil(send, POST_CLICK_READY_EXPRESSION, { timeoutMs: 200, intervalMs: 50 }),
|
|
false
|
|
)
|
|
assert.ok(Date.now() - started >= 150, 'waited for the full bound')
|
|
assert.ok(Date.now() - started < 3000, 'did not fall back to a fixed sleep')
|
|
})
|
|
|
|
test('default poll bound matches the old sleep ceiling', () => {
|
|
// The readiness poll replaces the fixed 3000ms sleep: same ceiling, but it
|
|
// exits early when the session is ready.
|
|
assert.equal(CLICK_POLL_TIMEOUT_MS, 3000)
|
|
})
|