Harden browser release qualification

Contract-Neutral: Release qualification reliability fixes preserve existing runtime, route, and deployment contracts
This commit is contained in:
rcourtman 2026-07-20 03:15:13 +01:00
parent 12eef62f76
commit eeab775445
6 changed files with 38 additions and 21 deletions

View file

@ -1,4 +1,5 @@
import { Navigate, useLocation } from '@solidjs/router';
import { Show } from 'solid-js';
import { DockerPageSurface } from '@/features/docker/DockerPageSurface';
import { buildDockerPath, DOCKER_PATH } from '@/routing/resourceLinks';
@ -6,11 +7,14 @@ export function Docker() {
const location = useLocation();
const pathname = () => location.pathname.replace(/\/+$/, '');
if (pathname() === `${DOCKER_PATH}/workloads`) {
return <Navigate href={buildDockerPath()} />;
}
return <DockerPageSurface />;
return (
<Show
when={pathname() !== `${DOCKER_PATH}/workloads`}
fallback={<Navigate href={buildDockerPath()} />}
>
<DockerPageSurface />
</Show>
);
}
export default Docker;

View file

@ -1,6 +1,7 @@
import { test, expect, type Locator } from '@playwright/test';
import {
E2E_CREDENTIALS,
apiRequest,
ensureAuthenticated,
getMockMode,
login,
@ -81,7 +82,7 @@ test.describe.serial('Core E2E flows', () => {
test.skip(true, `Unable to read CPU default threshold (value="${cpuDefaultValueRaw}")`);
}
const configResBeforeCreate = await page.request.get('/api/alerts/config');
const configResBeforeCreate = await apiRequest(page, '/api/alerts/config');
expect(configResBeforeCreate.ok()).toBeTruthy();
const configBeforeCreate = (await configResBeforeCreate.json()) as {
overrides?: Record<string, unknown>;
@ -166,7 +167,7 @@ test.describe.serial('Core E2E flows', () => {
});
await expect(revertOverride).toBeVisible();
const configResAfterCreate = await page.request.get('/api/alerts/config');
const configResAfterCreate = await apiRequest(page, '/api/alerts/config');
expect(configResAfterCreate.ok()).toBeTruthy();
const configAfterCreate = (await configResAfterCreate.json()) as { overrides?: Record<string, unknown> };
const previousOverrideIds = new Set(Object.keys(configBeforeCreate.overrides ?? {}));
@ -181,7 +182,7 @@ test.describe.serial('Core E2E flows', () => {
await page.getByRole('button', { name: 'Save Changes' }).click();
await expect(unsaved).not.toBeVisible();
const configResAfterDelete = await page.request.get('/api/alerts/config');
const configResAfterDelete = await apiRequest(page, '/api/alerts/config');
expect(configResAfterDelete.ok()).toBeTruthy();
const configAfterDelete = (await configResAfterDelete.json()) as { overrides?: Record<string, unknown> };
expect(

View file

@ -279,21 +279,29 @@ test.describe.serial("First-session experience", () => {
route: "/settings/support/reporting",
expectedURL: /\/settings\/support\/reporting/,
feature: "advanced_reporting",
paywallIndicator: /Advanced Reporting/i,
},
{
route: "/settings/security-webhooks",
expectedURL: /\/settings\/security-webhooks/,
feature: "audit_logging",
paywallIndicator: /Audit (Logging|Webhooks)/i,
},
{
route: "/settings/system-relay",
expectedURL: /\/settings\/system-relay/,
feature: "relay",
paywallIndicator: /Remote Access \(Relay\)/i,
},
] as const;
try {
for (const { route, expectedURL, feature } of gatedRoutes) {
for (const {
route,
expectedURL,
feature,
paywallIndicator: paywallPattern,
} of gatedRoutes) {
const hasFeature = features.has(feature);
if (!hasFeature) {
@ -346,9 +354,8 @@ test.describe.serial("First-session experience", () => {
// Paywall indicator should be visible.
const paywallIndicator = page
.locator(
"text=/Upgrade|Pro Feature|Requires Pro|Requires Relay|Start.*Trial|Advanced Reporting|Audit Logging|Audit Webhooks|Pulse Relay/i",
)
.getByRole("main")
.getByText(paywallPattern)
.first();
const isPaywallVisible = await paywallIndicator
.isVisible({ timeout: 5_000 })

View file

@ -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 hostOption.click();
await textarea.press('Enter');
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 vmOption.click();
await textarea.press('Enter');
await expect(textarea).toHaveValue('@warehouse-api-01 ');
expect(unexpectedVMwareRequests).toEqual([]);

View file

@ -65,15 +65,16 @@ test.describe('VMware AI chat read recovery', () => {
await expect(textarea).toBeVisible();
// First websocket state frame can lag on a freshly booted backend.
await textarea.click();
await textarea.pressSequentially('@warehouse');
await textarea.focus();
await textarea.fill('@warehouse');
const mentionListbox = page.getByRole('listbox', { name: 'Assistant resources' });
await expect(mentionListbox).toBeVisible({ timeout: 30_000 });
await mentionListbox
const vmOption = mentionListbox
.getByRole('option')
.filter({ hasText: /warehouse-api-01/ })
.first()
.click();
.first();
await expect(vmOption).toBeVisible({ timeout: 30_000 });
await textarea.press('Enter');
await expect(textarea).toHaveValue('@warehouse-api-01 ');
await textarea.fill('@warehouse-api-01 show me recent status');

View file

@ -1161,7 +1161,7 @@ async function waitForDefaultMockRuntimeReady(page: Page): Promise<void> {
await expect
.poll(
async () => {
const response = await page.request.get("/api/state").catch(() => null);
const response = await apiRequest(page, "/api/state").catch(() => null);
if (!response?.ok()) {
return false;
}
@ -1272,7 +1272,11 @@ export async function setMockMode(page: Page, enabled: boolean) {
}
if (res.ok()) {
return (await res.json()) as { enabled: boolean };
const result = (await res.json()) as { enabled: boolean };
if (enabled) {
await waitForDefaultMockRuntimeReady(page);
}
return result;
}
lastError = new Error(`HTTP ${res.status()}: ${await res.text()}`);