diff --git a/test-home-coverage-e2e.js b/test-home-coverage-e2e.js index 4436930a..20968767 100644 --- a/test-home-coverage-e2e.js +++ b/test-home-coverage-e2e.js @@ -100,20 +100,51 @@ async function pickAnyPubkey(page) { }); // ── 2. Search flow ── + // Mock /api/nodes/search so the suggestion dropdown render is exercised + // against a deterministic response (issue #1313 — the live fetch path + // flakes intermittently on cold CI; home.js's try/catch swallows the + // error and the dropdown never opens, hanging the test). + // Use a REAL fixture pubkey (so downstream /api/nodes//health calls + // succeed) but pin a sentinel display name we can assert on. + const _fixtureNode = await pickAnyPubkey(page); + assert(_fixtureNode && _fixtureNode.public_key, + 'fixture must expose at least one node with a public_key (got ' + JSON.stringify(_fixtureNode) + ')'); + const FIXTURE_PUBKEY = _fixtureNode.public_key; + const FIXTURE_NAME = 'HomeFlakeFix-1313'; + // Register the route BEFORE the typing step (and BEFORE we even reach + // the step body, so there is zero chance the keyup→debounce→fetch + // beats the route handler being attached). PR #1584 originally + // registered this inside the step; under cold-CI load the + // page.route() promise occasionally lost the race with the input + // event, leaving the fetch to hit the real /api/nodes/search and + // depending on whether the fixture had a match for 'h' the dropdown + // could end up empty or never open — see #1313. + await page.route('**/api/nodes/search**', (route) => route.fulfill({ + status: 200, + contentType: 'application/json', + body: JSON.stringify({ + nodes: [{ public_key: FIXTURE_PUBKEY, name: FIXTURE_NAME, role: 'companion' }], + }), + })); let pickedPubkey = null; let pickedName = null; await step('search input renders suggestions for a 1-char query', async () => { - // Populate pickedPubkey/pickedName so later steps (claim, My Mesh, etc.) - // still have a node to work with. The actual UI assertion is skipped — - // the /api/nodes/search fetch path is flaky in CI (home.js's try/catch - // swallows errors and never adds `.home-suggest.open`, causing the wait - // to time out). Proper fix needs Playwright-level page.route() mocking. - // See issue #1313. - const node = await pickAnyPubkey(page); - assert(node, 'fixture must have at least one node'); - pickedPubkey = node.public_key; - pickedName = node.name || ''; - console.log('SKIP: search test — flaky API fetch path, see issue #1313'); + // Make sure home.js has finished its async init (loadStats fetch + // resolves, setupSearch has bound the input listener) before we + // type. Otherwise the very first keystroke can fire before the + // 'input' handler is attached and the debounce timer never starts. + await page.waitForLoadState('networkidle'); + const input = await page.waitForSelector('#homeSearch', { timeout: 5000 }); + await input.click(); + await input.type('h', { delay: 20 }); + await page.waitForSelector('.home-suggest.open', { timeout: 5000 }); + const items = await page.$$('.suggest-item'); + assert(items.length >= 1, 'expected >=1 .suggest-item, got ' + items.length); + const names = await page.$$eval('.suggest-item .suggest-name', els => els.map(e => e.textContent)); + assert(names.includes(FIXTURE_NAME), + 'expected suggestion list to include mocked name "' + FIXTURE_NAME + '", got ' + JSON.stringify(names)); + pickedPubkey = FIXTURE_PUBKEY; + pickedName = FIXTURE_NAME; }); await step('claim button adds a node to My Mesh (localStorage)', async () => {