mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-08-21 06:53:29 +00:00
Harden browser release qualification
Contract-Neutral: Release qualification reliability fixes preserve existing runtime, route, and deployment contracts
This commit is contained in:
parent
12eef62f76
commit
eeab775445
6 changed files with 38 additions and 21 deletions
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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 })
|
||||
|
|
|
|||
|
|
@ -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([]);
|
||||
|
|
|
|||
|
|
@ -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');
|
||||
|
|
|
|||
|
|
@ -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()}`);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue