docs: enterprise refactor plan with ralph specs

This commit is contained in:
debian
2026-03-04 16:17:03 -05:00
parent 4c92712d20
commit f8191133c8
204 changed files with 32722 additions and 422 deletions

View File

@@ -0,0 +1,143 @@
/**
* PlaywrightAgent integration test.
* Uses a base64 data: URL so no external server is needed.
*/
import { PlaywrightAgent } from '../../../src/plugins/agents/PlaywrightAgent';
import { NullLogger } from '../../../src/core/Logger';
const HTML_CONTENT = `<!DOCTYPE html>
<html>
<head><title>Test Page</title></head>
<body>
<a href="#section" id="nav-link">Go to section</a>
<button id="submit-btn">Submit</button>
<input type="text" id="name-input" name="name" />
<input type="email" id="email-input" name="email" />
</body>
</html>`;
const TEST_URL = `data:text/html;base64,${Buffer.from(HTML_CONTENT).toString('base64')}`;
describe('PlaywrightAgent', () => {
jest.setTimeout(30000);
let agent: PlaywrightAgent;
beforeEach(() => {
agent = new PlaywrightAgent({ seed: 42, headless: true, logger: new NullLogger() });
});
afterEach(async () => {
await agent.close();
});
it('launches and captures initial state', async () => {
await agent.launch(TEST_URL);
const state = await agent.captureState();
expect(state.id).toBeTruthy();
expect(state.title).toBe('Test Page');
expect(state.domSnapshot).toContain('submit-btn');
expect(state.visitCount).toBe(0);
expect(typeof state.timestamp).toBe('number');
});
it('discovers clickable and fillable actions', async () => {
await agent.launch(TEST_URL);
const state = await agent.captureState();
const actions = await agent.discoverActions(state);
expect(actions.length).toBeGreaterThan(0);
const clicks = actions.filter((a) => a.type === 'click');
const fills = actions.filter((a) => a.type === 'fill');
expect(clicks.length).toBeGreaterThan(0);
expect(fills.length).toBeGreaterThan(0);
// All actions must have required fields
for (const action of actions) {
expect(action.id).toBeTruthy();
expect(action.seed).toBeDefined();
expect(action.stateId).toBe(state.id);
expect(action.timestamp).toBeGreaterThan(0);
}
});
it('executes a click action and returns an observation', async () => {
await agent.launch(TEST_URL);
const state = await agent.captureState();
const actions = await agent.discoverActions(state);
const clickAction = actions.find((a) => a.type === 'click');
expect(clickAction).toBeDefined();
const observation = await agent.executeAction(clickAction!);
expect(observation.id).toBeTruthy();
expect(observation.actionId).toBe(clickAction!.id);
expect(observation.newStateId).toBeTruthy();
expect(Array.isArray(observation.httpResponses)).toBe(true);
expect(Array.isArray(observation.consoleErrors)).toBe(true);
expect(Array.isArray(observation.jsExceptions)).toBe(true);
});
it('executes a fill action and returns an observation', async () => {
await agent.launch(TEST_URL);
const state = await agent.captureState();
const actions = await agent.discoverActions(state);
const fillAction = actions.find((a) => a.type === 'fill');
expect(fillAction).toBeDefined();
const observation = await agent.executeAction(fillAction!);
expect(observation.actionId).toBe(fillAction!.id);
expect(observation.jsExceptions).toHaveLength(0);
});
it('uses deterministic seed for action discovery (same seed = same order)', async () => {
await agent.launch(TEST_URL);
const state = await agent.captureState();
const actions1 = await agent.discoverActions(state);
// Skip if no actions found (page didn't load elements)
if (actions1.length === 0) return;
await agent.close();
const agent2 = new PlaywrightAgent({ seed: 42, headless: true, logger: new NullLogger() });
await agent2.launch(TEST_URL);
const state2 = await agent2.captureState();
const actions2 = await agent2.discoverActions(state2);
await agent2.close();
// Same seed → same seeds on actions in same order
const seeds1 = actions1.map((a) => a.seed);
const seeds2 = actions2.map((a) => a.seed);
expect(seeds1).toEqual(seeds2);
});
it('two instances with different seeds produce different action seeds', async () => {
const agent2 = new PlaywrightAgent({ seed: 99, headless: true, logger: new NullLogger() });
await agent.launch(TEST_URL);
await agent2.launch(TEST_URL);
const state1 = await agent.captureState();
const state2 = await agent2.captureState();
const actions1 = await agent.discoverActions(state1);
const actions2 = await agent2.discoverActions(state2);
await agent2.close();
// Only test if actions were found
if (actions1.length === 0) {
// If no actions, pass — the test validates seed differences when actions exist
return;
}
const seeds1 = actions1.map((a) => a.seed);
const seeds2 = actions2.map((a) => a.seed);
expect(seeds1).not.toEqual(seeds2);
});
});