import { expect, test } from '@playwright/test'; import { loginToDashboard } from './fixtures'; import { ensureLiveStubAgent, isLiveStubReady } from './live-stub'; import { ensureDiscoverSpreadStub, E2E_DISCOVER_AGENT_HOSTNAME, E2E_DISCOVER_AGENT_ID, E2E_DISCOVER_JOIN_LABEL, isDiscoverSpreadStubReady, } from './discover-spread-stub'; import { E2E_STUB_AGENT_HOSTNAME, E2E_STUB_AGENT_ID } from './stub-agent'; async function openCrucibleSpreadTab(page: import('@playwright/test').Page, hostname: string) { await page.getByRole('link', { name: /Crucible/i }).click(); await expect(page.getByRole('heading', { name: 'Crucible' })).toBeVisible({ timeout: 10_000 }); const card = page.locator('.crucible-node-card').filter({ hasText: hostname }); await expect(card).toBeVisible({ timeout: 15_000 }); await expect(card.locator('.cn-status-dot.on')).toBeVisible({ timeout: 15_000 }); await card.click(); await expect( page.locator('.crucible-actions-card').getByText(new RegExp(`→ ${hostname}`)), ).toBeVisible({ timeout: 10_000 }); await page.getByRole('button', { name: 'LATERAL / SPREAD' }).click(); await expect(page.getByRole('button', { name: 'Probe & Join' })).toBeVisible({ timeout: 10_000, }); } test.describe('Crucible discover and spread E2E', () => { test.describe('Probe & Join command wiring', () => { test.beforeAll(async ({ request }) => { await ensureLiveStubAgent(request); }); test.beforeEach(async ({ page }) => { test.skip( !isLiveStubReady(), 'Requires live E2E server (test-suite phase 8 on :18989 or AETHERFORGE_URL)', ); await loginToDashboard(page); }); test('Probe & Join fires POST discover_and_join for selected online node', async ({ page }) => { await openCrucibleSpreadTab(page, E2E_STUB_AGENT_HOSTNAME); const commandRequest = page.waitForRequest( (req) => req.method() === 'POST' && req.url().includes(`/api/v1/agents/${E2E_STUB_AGENT_ID}/command`) && req.postDataJSON()?.action === 'discover_and_join', ); await page.getByRole('button', { name: 'Probe & Join' }).click(); const request = await commandRequest; expect(request.postDataJSON()).toMatchObject({ action: 'discover_and_join' }); await expect(page.locator('.crucible-terminal')).toContainText('discover_and_join → 1 node(s)', { timeout: 10_000, }); }); }); test.describe('Multi-hop discover→spread acknowledgment', () => { test.beforeAll(async ({ request }) => { await ensureDiscoverSpreadStub(request); }); test.beforeEach(async ({ page }) => { test.skip( !isDiscoverSpreadStubReady(), 'Requires live E2E server (test-suite phase 8 on :18989 or AETHERFORGE_URL)', ); await loginToDashboard(page); }); test('discover_and_join stub ack updates join lane in Access Depth', async ({ page }) => { await openCrucibleSpreadTab(page, E2E_DISCOVER_AGENT_HOSTNAME); const commandRequest = page.waitForRequest( (req) => req.method() === 'POST' && req.url().includes(`/api/v1/agents/${E2E_DISCOVER_AGENT_ID}/command`) && req.postDataJSON()?.action === 'discover_and_join', ); await page.getByRole('button', { name: 'Probe & Join' }).click(); await commandRequest; await expect(page.locator('.crucible-terminal')).toContainText('discover_and_join → 1 node(s)', { timeout: 10_000, }); await expect(page.locator('.access-depth-panel')).toContainText(E2E_DISCOVER_JOIN_LABEL, { timeout: 15_000, }); }); }); });