From 05c44f199faf55bb5663a3d6fb4f22ae82d4bc82 Mon Sep 17 00:00:00 2001 From: rcourtman Date: Mon, 20 Jul 2026 09:47:03 +0100 Subject: [PATCH] Stabilize populated browser fixtures Contract-Neutral: Browser-only qualification fixture synchronization; no deployment or public runtime contract changes. --- tests/integration/tests/04-mobile.spec.ts | 4 +++- .../tests/38-vmware-ai-chat-mentions.spec.ts | 4 ++-- .../tests/59-workloads-column-layout.spec.ts | 12 +++++++++++- tests/integration/tests/77-msp-isolation.spec.ts | 8 ++++---- tests/integration/tests/helpers.ts | 14 +++++++++++++- 5 files changed, 33 insertions(+), 9 deletions(-) diff --git a/tests/integration/tests/04-mobile.spec.ts b/tests/integration/tests/04-mobile.spec.ts index 3b8df9ee3..299ca055c 100644 --- a/tests/integration/tests/04-mobile.spec.ts +++ b/tests/integration/tests/04-mobile.spec.ts @@ -1,5 +1,5 @@ import { test, expect, devices } from "@playwright/test"; -import { ensureAuthenticated } from "./helpers"; +import { ensureAuthenticated, setMockMode } from "./helpers"; const getViewportWidth = async ( page: import("@playwright/test").Page, @@ -189,6 +189,8 @@ test.describe("Mobile viewport flows", () => { test("shared Workloads table preserves its mobile width and scroll contract", async ({ page, }) => { + test.setTimeout(240_000); + await setMockMode(page, true); await page.goto("/proxmox/workloads"); const table = page.locator("table.workload-table--mobile"); diff --git a/tests/integration/tests/38-vmware-ai-chat-mentions.spec.ts b/tests/integration/tests/38-vmware-ai-chat-mentions.spec.ts index 287fe81f7..0aabe6350 100644 --- a/tests/integration/tests/38-vmware-ai-chat-mentions.spec.ts +++ b/tests/integration/tests/38-vmware-ai-chat-mentions.spec.ts @@ -78,7 +78,7 @@ test.describe('VMware AI chat mentions', () => { .filter({ hasText: /esxi-01\.lab\.local/ }) .first(); await expect(hostOption).toBeVisible({ timeout: 30_000 }); - await page.keyboard.press('Enter'); + await hostOption.click(); await expect(textarea).toHaveValue('@esxi-01.lab.local '); // API-backed vSphere VMs use the same shared mention contract, carrying @@ -91,7 +91,7 @@ test.describe('VMware AI chat mentions', () => { .filter({ hasText: /warehouse-api-01/ }) .first(); await expect(vmOption).toBeVisible({ timeout: 30_000 }); - await page.keyboard.press('Enter'); + await vmOption.click(); await expect(textarea).toHaveValue('@warehouse-api-01 '); expect(unexpectedVMwareRequests).toEqual([]); diff --git a/tests/integration/tests/59-workloads-column-layout.spec.ts b/tests/integration/tests/59-workloads-column-layout.spec.ts index 7276210a1..5b37e6709 100644 --- a/tests/integration/tests/59-workloads-column-layout.spec.ts +++ b/tests/integration/tests/59-workloads-column-layout.spec.ts @@ -1,6 +1,11 @@ import { expect, test as base, type Page } from '@playwright/test'; -import { getMockMode, setMockMode } from './helpers'; +import { + ensureAuthenticated, + getMockMode, + setMockMode, + waitForDefaultMockRuntimeReady, +} from './helpers'; type ColumnHeaderMetric = { colId: string | null; @@ -45,12 +50,15 @@ test.use({ }); async function ensureMockModeEnabled(page: Page): Promise { + await ensureAuthenticated(page); const state = await getMockMode(page); if (mockModeWasEnabled === null) { mockModeWasEnabled = state.enabled; } if (!state.enabled) { await setMockMode(page, true); + } else { + await waitForDefaultMockRuntimeReady(page); } } @@ -118,6 +126,8 @@ async function readWorkloadsColumnLayout(page: Page): Promise { + test.setTimeout(240_000); + test.afterAll(async ({ browser }) => { if (mockModeWasEnabled === null) { return; diff --git a/tests/integration/tests/77-msp-isolation.spec.ts b/tests/integration/tests/77-msp-isolation.spec.ts index b9ad228c2..54cad1388 100644 --- a/tests/integration/tests/77-msp-isolation.spec.ts +++ b/tests/integration/tests/77-msp-isolation.spec.ts @@ -7,7 +7,7 @@ import { apiRequest, createOrg, deleteOrg, - ensureAuthenticated, + ensureSessionAuthenticated, isMultiTenantEnabled, } from './helpers'; @@ -88,7 +88,7 @@ test.describe('MSP isolation E2E', () => { }); test('org-bound token is scoped away from other orgs and the default org', async ({ page }) => { - await ensureAuthenticated(page); + await ensureSessionAuthenticated(page); const mtEnabled = await isMultiTenantEnabled(page); test.skip(!mtEnabled, 'Multi-tenant feature not enabled in this environment'); @@ -147,7 +147,7 @@ test.describe('MSP isolation E2E', () => { }); test('client org webhook delivery: instance-wide allowlist, tenant stamp, HMAC signature', async ({ page }) => { - await ensureAuthenticated(page); + await ensureSessionAuthenticated(page); const mtEnabled = await isMultiTenantEnabled(page); test.skip(!mtEnabled, 'Multi-tenant feature not enabled in this environment'); @@ -232,7 +232,7 @@ test.describe('MSP isolation E2E', () => { ) .toBe(200); - await ensureAuthenticated(page); + await ensureSessionAuthenticated(page); await fire(); expect(deliveries.length).toBe(2); const second = deliveries[1]; diff --git a/tests/integration/tests/helpers.ts b/tests/integration/tests/helpers.ts index ec7b0c179..6f59e3ad8 100644 --- a/tests/integration/tests/helpers.ts +++ b/tests/integration/tests/helpers.ts @@ -1171,7 +1171,7 @@ const resourceHasSource = ( source: string, ): boolean => resource.sources?.includes(source) === true; -async function waitForDefaultMockRuntimeReady(page: Page): Promise { +export async function waitForDefaultMockRuntimeReady(page: Page): Promise { if (!requiresDefaultMockRuntimeReadiness()) { return; } @@ -1207,6 +1207,18 @@ async function waitForDefaultMockRuntimeReady(page: Page): Promise { resource.name === "tank" && resourceHasSource(resource, "truenas"), ) && + resources.some( + (resource) => + (resource.type === "vm" || + resource.type === "system-container") && + resourceHasSource(resource, "proxmox"), + ) && + resources.some( + (resource) => + resource.name === "esxi-01.lab.local" && + resource.type === "agent" && + resourceHasSource(resource, "vmware"), + ) && resources.some((resource) => resource.type === "docker-host") && resources.some((resource) => resource.type === "pbs") && resources.some((resource) => resource.type === "pmg") &&