Files
pbakaus_impeccable/tests/live-e2e-steer-agent.test.mjs
T
Paul Bakaus 166a78ad3d Tests: drive the live-e2e orchestrator through the engine binary
The session, fake-agent loop, steer test, and manual-edit probe spawn
<binary> <verb> (live-server, live, live-inject, live-wrap, live-insert,
live-accept, live-poll, live-complete) resolved by tests/lib/engine-bin.mjs
instead of node skill/scripts/live-*.mjs; the completion typing the agent
imported from the deleted live/completion.mjs is a small local helper. The
live-e2e helper unit tests move back into the default live suite (the steer
loop skips without a binary).

Prepared with AI assistance (Claude Code).
2026-08-31 19:59:50 -07:00

96 lines
3.1 KiB
JavaScript

/**
* Steer handler unit tests (no Playwright).
* Run with: node --test tests/live-e2e-steer-agent.test.mjs
*/
import { describe, it, before, after } from 'node:test';
import assert from 'node:assert/strict';
import { readFileSync } from 'node:fs';
import { join } from 'node:path';
import { addSteerMarkerToSource, createFakeAgent, findSteerTargetFile, runAgentLoop, STEER_MARKER_ATTR } from './live-e2e/agent.mjs';
import { stageFixture, startLiveServer, stopLiveServer, ENGINE_BIN, ENGINE_MISSING_MESSAGE, FIXTURES_DIR } from './live-e2e/session.mjs';
const FIXTURE_NAME = 'vite8-react-plain';
// The loop drives live-server and live-poll through the engine binary.
describe('live-e2e steer agent handler', { skip: ENGINE_BIN ? false : ENGINE_MISSING_MESSAGE }, () => {
let tmp;
let live;
let abort;
let loopDone;
before(async () => {
const fixture = JSON.parse(readFileSync(join(FIXTURES_DIR, FIXTURE_NAME, 'fixture.json'), 'utf-8'));
tmp = stageFixture(FIXTURE_NAME, fixture);
live = startLiveServer(tmp);
abort = new AbortController();
loopDone = runAgentLoop({
tmp,
engineBin: ENGINE_BIN,
port: live.port,
token: live.token,
agent: createFakeAgent(),
signal: abort.signal,
log: () => {},
});
});
after(async () => {
abort?.abort();
await loopDone?.catch(() => {});
if (live) stopLiveServer(tmp);
});
it('findSteerTargetFile locates the hero source file', () => {
const file = findSteerTargetFile(tmp);
assert.match(file, /App\.jsx$/);
const body = readFileSync(file, 'utf-8');
assert.match(body, /hero-title/);
});
it('marks JSX template-expression className attributes', () => {
const source = [
'export default function App() {',
' return <h1 className={`hero-title ${styles.heroTitle}`}>Fixture</h1>;',
'}',
].join('\n');
const updated = addSteerMarkerToSource(source);
assert.match(updated, new RegExp(STEER_MARKER_ATTR + '="e2e"'));
assert.match(updated, /className=\{`hero-title \$\{styles\.heroTitle\}`\}/);
});
it('agent loop handles steer POST and writes the marker', async () => {
const sourceFile = findSteerTargetFile(tmp);
const before = readFileSync(sourceFile, 'utf-8');
assert.doesNotMatch(before, new RegExp(STEER_MARKER_ATTR + '="e2e"'));
const post = await fetch(`http://127.0.0.1:${live.port}/events`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
token: live.token,
type: 'steer',
id: 'cafebabe',
message: 'steer-e2e mark hero',
pageUrl: 'http://127.0.0.1:5173/',
}),
});
assert.equal(post.status, 200);
const deadline = Date.now() + 10_000;
let updated = before;
while (Date.now() < deadline) {
updated = readFileSync(sourceFile, 'utf-8');
if (updated.includes(`${STEER_MARKER_ATTR}="e2e"`)) break;
await new Promise((r) => setTimeout(r, 50));
}
assert.match(
updated,
new RegExp(STEER_MARKER_ATTR + '="e2e"'),
'fake agent should mark hero after steer event',
);
});
});