diff --git a/e2e/deploy-log-panel.spec.ts b/e2e/deploy-log-panel.spec.ts index 251e65191..86ebbe1b4 100644 --- a/e2e/deploy-log-panel.spec.ts +++ b/e2e/deploy-log-panel.spec.ts @@ -26,6 +26,30 @@ const FAIL_COMPOSE = `services: image: nginnnnx:notexist `; +/** + * Surface React errors and network failures from the browser so test + * failures arrive with diagnostic context instead of just "modal not + * visible". Skip noisy info/log/debug messages. + */ +function logBrowserErrors(page: Page, title: string): void { + page.on('console', (msg) => { + if (msg.type() === 'error' || msg.type() === 'warning') { + // eslint-disable-next-line no-console + console.log(`[browser ${msg.type()}] [${title}] ${msg.text()}`); + } + }); + page.on('pageerror', (err) => { + // eslint-disable-next-line no-console + console.log(`[browser pageerror] [${title}] ${err.message}`); + }); + // Aborted requests (for example net::ERR_NETWORK_CHANGED during login) + // never reach the console as a status line, so log them explicitly. + page.on('requestfailed', (req) => { + // eslint-disable-next-line no-console + console.log(`[browser requestfailed] [${title}] ${req.method()} ${req.url()} ${req.failure()?.errorText ?? ''}`); + }); +} + async function createStackViaApi(page: Page, name: string, composeContent: string): Promise { await page.evaluate( async ({ stackName, content }: { stackName: string; content: string }) => { @@ -188,19 +212,7 @@ async function setupDeployStack(page: Page, name: string, composeContent: string test.describe('Deploy feedback modal', () => { test.beforeEach(async ({ page }, testInfo) => { - // Surface React errors and network failures from the browser so test - // failures arrive with diagnostic context instead of just "modal not - // visible". Skip noisy info/log/debug messages. - page.on('console', (msg) => { - if (msg.type() === 'error' || msg.type() === 'warning') { - // eslint-disable-next-line no-console - console.log(`[browser ${msg.type()}] [${testInfo.title}] ${msg.text()}`); - } - }); - page.on('pageerror', (err) => { - // eslint-disable-next-line no-console - console.log(`[browser pageerror] [${testInfo.title}] ${err.message}`); - }); + logBrowserErrors(page, testInfo.title); await loginAs(page); await waitForStacksLoaded(page); }); @@ -407,7 +419,8 @@ test.describe('Deploy feedback modal', () => { }); test.describe('Inline deploy progress', () => { - test.beforeEach(async ({ page }) => { + test.beforeEach(async ({ page }, testInfo) => { + logBrowserErrors(page, testInfo.title); await loginAs(page); await waitForStacksLoaded(page); }); diff --git a/e2e/stack-file-explorer.spec.ts b/e2e/stack-file-explorer.spec.ts index 41f9e1ccb..222fee9b3 100644 --- a/e2e/stack-file-explorer.spec.ts +++ b/e2e/stack-file-explorer.spec.ts @@ -519,7 +519,9 @@ test.describe('Stack file explorer: UI lifecycle', () => { // Right-click near the right edge, well past the filename text: the whole // row is the trigger, so the Sencho menu (not the native one) must open. await row.click({ button: 'right', position: { x: box.width - 6, y: box.height / 2 } }); - await expect(page.getByText('Rename')).toBeVisible({ timeout: 5_000 }); + // Match the menu item by role: an earlier test leaves a file named + // lifecycle-rename-dst.txt in the tree, which a bare text match also hits. + await expect(page.getByRole('menuitem', { name: 'Rename', exact: true })).toBeVisible({ timeout: 5_000 }); }); test('a long filename overflows the pane horizontally instead of being clipped', async ({ page }) => {