import http from 'node:http'; import test from 'node:test'; import assert from 'node:assert/strict'; import { executeTestPlan } from './test-executor.js'; import type { ExecutionStep } from '@detiq/agents'; async function startFixtureServer(html: string): Promise<{ url: string; close: () => Promise }> { const server = http.createServer((_req, res) => { res.writeHead(200, { 'Content-Type': 'text/html' }); res.end(html); }); await new Promise((resolve) => server.listen(0, '127.0.0.1', resolve)); const address = server.address(); const port = typeof address === 'object' && address ? address.port : 0; return { url: `http://127.0.0.1:${port}`, close: () => new Promise((resolve) => server.close(() => resolve())), }; } // No AI healing context is passed in these tests (healCtx omitted), so the AI tier // (which needs a real LLM call) never engages — this is regression coverage for the // pre-existing mechanical strategy-fallback tier that self-healing wiring must not break. test('resolveLocator: primary strategy succeeds when the selector/strategy pair is correct', async () => { const fixture = await startFixtureServer(''); try { const plan: ExecutionStep[] = [ { stepNumber: 1, description: 'navigate', action: 'navigate', url: fixture.url }, { stepNumber: 2, description: 'click Submit', action: 'click', selector: 'Submit', selectorStrategy: 'text' }, ]; const result = await executeTestPlan(plan, { screenshotDir: '/tmp', testRunId: 'test-primary-ok' }); assert.equal(result.status, 'PASSED'); assert.equal(result.stepResults[1].healed, false); } finally { await fixture.close(); } }); test('resolveLocator: mechanical tier heals when the strategy is wrong but the text still matches', async () => { // "label" strategy looks for a form control associated with a