test(web): guard navigation continuity during sync reconnects

Issue #1899 reports navigation disappearing when backend health changes to sync reconnecting on v6.4.1. Protect the isolated shell transition with stable link, label, icon and focus assertions rather than assuming the badge unit test covers navigation. This does not reproduce the reporter's browser or socket environment and makes no production fix claim.

Validation: AppLayout, useAppRuntimeState and ConnectionStatusBadge focused suites pass 46 tests. Temporarily hiding desktop navigation during sync reconnecting makes the new test fail; the mutation was restored and the suites rerun successfully.
Change-source: pulse-maintainer
This commit is contained in:
pulse-triage[bot]
2026-09-05 04:03:21 +01:00
parent d60da624c8
commit ae954f776a
@@ -2,6 +2,8 @@ import { cleanup, fireEvent, render, screen, waitFor, within } from '@solidjs/te
import { Route, Router, useNavigate } from '@solidjs/router';
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
import type { State } from '@/types/api';
import { createSignal, type Accessor } from 'solid-js';
import type { AppConnectionStatus } from '@/useAppRuntimeState';
import type { Resource } from '@/types/resource';
import {
AppLayout,
@@ -72,6 +74,12 @@ const renderLayout = (
initialPath = '/settings/infrastructure',
platformVisibility?: PlatformNavigationVisibility,
tokenScopes: string[] = ['settings:read'],
connectionStatus: Accessor<AppConnectionStatus> = () => ({
kind: 'connected',
label: 'Connected',
detail: 'Backend and live data stream are connected.',
tone: 'healthy',
}),
) => {
window.history.replaceState({}, '', initialPath);
const RouteStateProbe = () => {
@@ -87,12 +95,7 @@ const renderLayout = (
};
const LayoutRoute = () => (
<AppLayout
connectionStatus={() => ({
kind: 'connected',
label: 'Connected',
detail: 'Backend and live data stream are connected.',
tone: 'healthy',
})}
connectionStatus={connectionStatus}
lastUpdateText={() => ''}
versionInfo={() =>
({
@@ -223,6 +226,47 @@ describe('AppLayout navigation icons', () => {
expect(container).toHaveTextContent('Infrastructure body');
});
it('retains navigation labels, icons and focus as backend health changes to sync reconnecting', async () => {
// #1899: isolate the shell status transition from inventory changes. This
// is not a reproduction of the reporter's socket/proxy or CSS environment.
const [connectionStatus, setConnectionStatus] = createSignal<AppConnectionStatus>({
kind: 'backend-healthy',
label: 'Backend healthy',
detail: 'Backend is healthy, but the live data stream is not connected.',
tone: 'warning',
});
renderLayout(
platformResources(),
'/settings/infrastructure',
undefined,
['settings:read'],
connectionStatus,
);
const navigation = screen.getByRole('navigation', { name: 'Primary navigation' });
const links = within(navigation).getAllByRole('link');
const labels = links.map((link) => link.textContent);
const proxmox = getInfrastructureLink('Proxmox');
proxmox.focus();
expect(proxmox).toHaveFocus();
for (const status of [
{ kind: 'sync-reconnecting', label: 'Sync reconnecting', tone: 'warning' },
{ kind: 'connected', label: 'Connected', tone: 'healthy' },
{ kind: 'sync-reconnecting', label: 'Sync reconnecting', tone: 'warning' },
] as const) {
setConnectionStatus({ ...status, detail: status.label });
await waitFor(() => expect(screen.getByText(status.label)).toBeInTheDocument());
expect(navigation).toBeInTheDocument();
const currentLinks = within(navigation).getAllByRole('link');
expect(currentLinks.map((link) => link.textContent)).toEqual(labels);
currentLinks.forEach((link, index) => {
expect(link).toBe(links[index]);
expect(link.querySelector('svg')).toBeTruthy();
});
expect(proxmox).toHaveFocus();
}
});
it('surfaces canonical Patrol attention as a count without renaming Patrol', () => {
patrolAttentionMockState.activeCount = 2;
renderLayout();