diff --git a/docs/release-control/v6/internal/subsystems/frontend-primitives.md b/docs/release-control/v6/internal/subsystems/frontend-primitives.md index 39a717726..e05243826 100644 --- a/docs/release-control/v6/internal/subsystems/frontend-primitives.md +++ b/docs/release-control/v6/internal/subsystems/frontend-primitives.md @@ -238,6 +238,10 @@ Feature surfaces under `frontend-modern/src/features/` may own product-specific assessment semantics, but they must keep those semantics in their governed presentation helpers and render them inside the shared neutral Pulse surface language rather than introducing page-local verdict bands or nested cards. +Feature-owned table drawers use shared disclosure and inline-detail primitives +as local interaction state. Unless a surface has a separately governed deep-link +write contract, opening or closing a row drawer must preserve the current +document and URL instead of writing route state or reloading the page shell. Shared filter/search primitives may provide the common shell, keyboard behavior, history, and reset mechanics, but the owning page or table must supply diff --git a/docs/release-control/v6/internal/subsystems/performance-and-scalability.md b/docs/release-control/v6/internal/subsystems/performance-and-scalability.md index 8240a88f0..324cf332d 100644 --- a/docs/release-control/v6/internal/subsystems/performance-and-scalability.md +++ b/docs/release-control/v6/internal/subsystems/performance-and-scalability.md @@ -245,6 +245,13 @@ regression protection. mounted, report errors out of band, and must not rely on Solid `createResource` in a way that can bubble into the route-level Suspense fallback or the setup/welcome shell. + Workload row drawer expansion, local row focus, and summary group pinning + are local interaction state. Inbound Workloads deep links may hydrate + `resource` and `summaryGroup` into the focused row or group, but opening, + closing, or clearing an inline drawer must not write those params back to + route state, schedule router navigation, or trip the app-shell fallback. + URL synchronization for Workloads stays limited to filter-owned scope in + `frontend-modern/src/components/Workloads/useWorkloadUrlSync.ts`. The Workloads page may show a bounded partial-inventory banner when a configured workload-capable source is unauthorized, unreachable, stale, pending, or paused, but it must not fabricate VM/container rows from host diff --git a/docs/release-control/v6/internal/subsystems/unified-resources.md b/docs/release-control/v6/internal/subsystems/unified-resources.md index 4b31b7766..e8b193213 100644 --- a/docs/release-control/v6/internal/subsystems/unified-resources.md +++ b/docs/release-control/v6/internal/subsystems/unified-resources.md @@ -196,6 +196,12 @@ Swarm capability surfaces. accessible group label, while Proxmox, Kubernetes, and other platform clusters stay reserved for actual resource identity, filters, and detail surfaces. + Infrastructure row drawer expansion and summary group focus are local table + interaction state. Inbound infrastructure deep links may hydrate `resource` + and `summaryGroup` into the focused resource or group, but local expansion, + closing, and clearing must not rewrite those params or navigate. Route sync + for this surface is limited to filter-owned `source` and `q` state so + unified-resource rows open inline without refreshing the page shell. Cluster group headers may use a compact `Cluster` type chip when the group name itself is only an estate label, but `unifiedResourceTableStateModel.ts` must suppress that chip when the visible group name already ends in diff --git a/frontend-modern/src/components/Workloads/__tests__/useWorkloadSelectionState.test.ts b/frontend-modern/src/components/Workloads/__tests__/useWorkloadSelectionState.test.ts index bcc370c44..fc53d87af 100644 --- a/frontend-modern/src/components/Workloads/__tests__/useWorkloadSelectionState.test.ts +++ b/frontend-modern/src/components/Workloads/__tests__/useWorkloadSelectionState.test.ts @@ -4,7 +4,6 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; import type { SummarySeriesGroupScope } from '@/components/shared/summaryCardInteraction'; import type { WorkloadGuest } from '@/types/workloads'; -import { ROUTE_STATE_REPLACE_OPTIONS } from '@/utils/routeStateNavigation'; import { resolveWorkloadResourceSelection } from '../workloadSelectionModel'; import { useWorkloadSelectionState } from '../useWorkloadSelectionState'; @@ -101,7 +100,7 @@ describe('useWorkloadSelectionState', () => { }); }); - it('writes workload row selection back into the route state without dropping filters', () => { + it('opens workload row selection locally without route navigation', () => { locationSearch = '?type=app-container&platform=truenas&agent=truenas-main'; const [filteredGuests] = createSignal([]); @@ -115,13 +114,11 @@ describe('useWorkloadSelectionState', () => { result.setSelectedGuestId('app-container:truenas-main:nextcloud'); vi.runAllTimers(); - expect(navigateSpy).toHaveBeenCalledWith( - '/workloads?type=app-container&platform=truenas&agent=truenas-main&resource=app-container%3Atruenas-main%3Anextcloud', - ROUTE_STATE_REPLACE_OPTIONS, - ); + expect(result.selectedGuestId()).toBe('app-container:truenas-main:nextcloud'); + expect(navigateSpy).not.toHaveBeenCalled(); }); - it('clears pinned workload scope back to page state without mutating route filters', () => { + it('clears pinned workload scope locally without mutating route filters', () => { locationSearch = '?type=app-container&platform=truenas&agent=truenas-main&resource=app-container%3Atruenas-main%3Anextcloud&summaryGroup=docker-host%3Atruenas-main'; const [filteredGuests] = createSignal([]); @@ -147,10 +144,9 @@ describe('useWorkloadSelectionState', () => { result.clearPinnedSummaryScope(); vi.runAllTimers(); - expect(navigateSpy).toHaveBeenCalledWith( - '/workloads?type=app-container&platform=truenas&agent=truenas-main', - ROUTE_STATE_REPLACE_OPTIONS, - ); + expect(result.selectedGuestId()).toBeNull(); + expect(result.focusedSummaryWorkloadGroupId()).toBeNull(); + expect(navigateSpy).not.toHaveBeenCalled(); }); it('routes Escape through workload scope clearing and additional page reset work', () => { @@ -171,10 +167,7 @@ describe('useWorkloadSelectionState', () => { vi.runAllTimers(); expect(clearAdditionalPageStateOnEscape).toHaveBeenCalledTimes(1); - expect(navigateSpy).toHaveBeenCalledWith( - '/workloads?type=app-container&platform=truenas&agent=truenas-main', - ROUTE_STATE_REPLACE_OPTIONS, - ); + expect(navigateSpy).not.toHaveBeenCalled(); }); it('preserves the nearest scrollable ancestor when row focus changes locally', () => { @@ -211,10 +204,8 @@ describe('useWorkloadSelectionState', () => { vi.runAllTimers(); - expect(navigateSpy).toHaveBeenCalledWith( - '/workloads?type=app-container&platform=truenas&agent=truenas-main&resource=app-container%3Atruenas-main%3Anextcloud', - ROUTE_STATE_REPLACE_OPTIONS, - ); + expect(result.selectedGuestId()).toBe('app-container:truenas-main:nextcloud'); + expect(navigateSpy).not.toHaveBeenCalled(); }); it('shows a deliberate jump affordance when a hovered workload row is off-screen', () => { @@ -436,7 +427,7 @@ describe('useWorkloadSelectionState', () => { expect(result.focusedSummaryWorkloadGroupId()).toBe('cluster-a'); }); - it('clears row focus without leaving behind an inferred agent filter', () => { + it('clears row focus locally without leaving behind an inferred agent filter', () => { locationSearch = '?resource=cluster-a:node-1:101'; const [filteredGuests] = createSignal([]); @@ -452,6 +443,7 @@ describe('useWorkloadSelectionState', () => { result.setSelectedGuestId(null); vi.runAllTimers(); - expect(navigateSpy).toHaveBeenCalledWith('/workloads', ROUTE_STATE_REPLACE_OPTIONS); + expect(result.selectedGuestId()).toBeNull(); + expect(navigateSpy).not.toHaveBeenCalled(); }); }); diff --git a/frontend-modern/src/components/Workloads/__tests__/workloadSelectionModel.test.ts b/frontend-modern/src/components/Workloads/__tests__/workloadSelectionModel.test.ts index 82d66624b..3c96e87f6 100644 --- a/frontend-modern/src/components/Workloads/__tests__/workloadSelectionModel.test.ts +++ b/frontend-modern/src/components/Workloads/__tests__/workloadSelectionModel.test.ts @@ -5,7 +5,6 @@ import type { WorkloadGuest } from '@/types/workloads'; import { workloadsHasHoveredWorkload, resolveWorkloadResourceSelection, - resolveWorkloadsSelectionNavigateTarget, } from '../workloadSelectionModel'; describe('workloadSelectionModel', () => { @@ -51,57 +50,10 @@ describe('workloadSelectionModel', () => { expect(workloadsHasHoveredWorkload(guests, 'cluster-a:node-1:102')).toBe(false); }); - it('builds route-backed workload selection targets without dropping other filters', () => { - expect( - resolveWorkloadsSelectionNavigateTarget({ - pathname: '/workloads', - search: '?type=app-container&platform=truenas&agent=truenas-main', - resourceId: 'app-container:truenas-main:nextcloud', - summaryGroupId: null, - }), - ).toBe( - '/workloads?type=app-container&platform=truenas&agent=truenas-main&resource=app-container%3Atruenas-main%3Anextcloud', - ); - - expect( - resolveWorkloadsSelectionNavigateTarget({ - pathname: '/workloads', - search: - '?type=app-container&platform=truenas&agent=truenas-main&resource=app-container%3Atruenas-main%3Anextcloud', - resourceId: null, - summaryGroupId: null, - }), - ).toBe('/workloads?type=app-container&platform=truenas&agent=truenas-main'); - - expect( - resolveWorkloadsSelectionNavigateTarget({ - pathname: '/workloads', - search: - '?type=app-container&platform=truenas&agent=truenas-main&resource=app-container%3Atruenas-main%3Anextcloud', - resourceId: 'app-container:truenas-main:nextcloud', - summaryGroupId: null, - }), - ).toBeNull(); - - expect( - resolveWorkloadsSelectionNavigateTarget({ - pathname: '/workloads', - search: '?type=app-container&platform=truenas&agent=truenas-main', - resourceId: null, - summaryGroupId: 'docker-host:truenas-main', - }), - ).toBe( - '/workloads?type=app-container&platform=truenas&agent=truenas-main&summaryGroup=docker-host%3Atruenas-main', - ); - - expect( - resolveWorkloadsSelectionNavigateTarget({ - pathname: '/workloads', - search: - '?type=app-container&platform=truenas&agent=truenas-main&summaryGroup=docker-host%3Atruenas-main', - resourceId: null, - summaryGroupId: 'docker-host:truenas-main', - }), - ).toBeNull(); + it('resolves summary group deep links separately from local row expansion', () => { + expect(resolveWorkloadResourceSelection('?summaryGroup=docker-host%3Atruenas-main')).toEqual({ + resourceId: null, + summaryGroupId: 'docker-host:truenas-main', + }); }); }); diff --git a/frontend-modern/src/components/Workloads/useWorkloadSelectionState.ts b/frontend-modern/src/components/Workloads/useWorkloadSelectionState.ts index 0e1a58768..7d970aeb0 100644 --- a/frontend-modern/src/components/Workloads/useWorkloadSelectionState.ts +++ b/frontend-modern/src/components/Workloads/useWorkloadSelectionState.ts @@ -1,5 +1,5 @@ -import { useLocation, useNavigate } from '@solidjs/router'; -import { createEffect, createMemo, createSignal, onCleanup, untrack, type Accessor } from 'solid-js'; +import { useLocation } from '@solidjs/router'; +import { createEffect, createMemo, createSignal, type Accessor } from 'solid-js'; import { preserveScrollableAncestorVerticalOffset } from '@/components/shared/contextualFocus'; import { useSummaryPageInteractionState } from '@/components/shared/summaryTableFocus'; @@ -9,12 +9,10 @@ import { } from '@/components/shared/summaryCardInteraction'; import type { WorkloadGuest } from '@/types/workloads'; import { capturePendingAppShellRestoreTop } from '@/utils/appShellScrollRestoration'; -import { createRouteStateNavigateScheduler } from '@/utils/routeStateNavigation'; import { workloadsHasHoveredWorkload, workloadsHasVisibleWorkloadGroupScope, resolveWorkloadResourceSelection, - resolveWorkloadsSelectionNavigateTarget, } from './workloadSelectionModel'; interface UseWorkloadsSelectionStateOptions { @@ -25,11 +23,6 @@ interface UseWorkloadsSelectionStateOptions { export function useWorkloadSelectionState(options: UseWorkloadsSelectionStateOptions) { const location = useLocation(); - const navigate = useNavigate(); - const routeStateNavigate = createRouteStateNavigateScheduler( - navigate, - () => `${untrack(() => location.pathname)}${untrack(() => location.search)}`, - ); const [selectedGuestId, setSelectedGuestIdRaw] = createSignal(null); const [selectedWorkloadGroupId, setSelectedWorkloadGroupIdRaw] = createSignal(null); @@ -57,15 +50,6 @@ export function useWorkloadSelectionState(options: UseWorkloadsSelectionStateOpt setSelectedGuestIdRaw(null); setSelectedWorkloadGroupIdRaw(null); }); - const nextPath = resolveWorkloadsSelectionNavigateTarget({ - pathname: location.pathname, - search: location.search, - resourceId: null, - summaryGroupId: null, - }); - if (nextPath) { - routeStateNavigate.schedule(nextPath); - } }; const summaryInteraction = useSummaryPageInteractionState({ @@ -111,15 +95,6 @@ export function useWorkloadSelectionState(options: UseWorkloadsSelectionStateOpt setSelectedGuestIdRaw(id); setSelectedWorkloadGroupIdRaw(nextGroupScope?.id ?? null); }); - const nextPath = resolveWorkloadsSelectionNavigateTarget({ - pathname: location.pathname, - search: location.search, - resourceId: id, - summaryGroupId: nextGroupScope?.id ?? null, - }); - if (nextPath) { - routeStateNavigate.schedule(nextPath); - } }; const setFocusedWorkloadGroupScopeState = (scope: SummarySeriesGroupScope | null) => { @@ -132,19 +107,7 @@ export function useWorkloadSelectionState(options: UseWorkloadsSelectionStateOpt }; const setFocusedWorkloadGroupScope = (scope: SummarySeriesGroupScope | null) => { - const nextGroupId = scope?.id ?? null; - const nextSelectedGuestId = - scope && !isSummarySeriesInGroupScope(scope, selectedGuestId()) ? null : selectedGuestId(); setFocusedWorkloadGroupScopeState(scope); - const nextPath = resolveWorkloadsSelectionNavigateTarget({ - pathname: location.pathname, - search: location.search, - resourceId: nextSelectedGuestId, - summaryGroupId: nextGroupId, - }); - if (nextPath) { - routeStateNavigate.schedule(nextPath); - } }; createEffect(() => { @@ -219,10 +182,6 @@ export function useWorkloadSelectionState(options: UseWorkloadsSelectionStateOpt } }); - onCleanup(() => { - routeStateNavigate.cleanup(); - }); - return { activeSummaryScopeState: summaryInteraction.activeScopeState, activeSummaryWorkloadGroupScope: summaryInteraction.activeGroupScope, diff --git a/frontend-modern/src/components/Workloads/workloadSelectionModel.ts b/frontend-modern/src/components/Workloads/workloadSelectionModel.ts index ede21d318..75edc3aec 100644 --- a/frontend-modern/src/components/Workloads/workloadSelectionModel.ts +++ b/frontend-modern/src/components/Workloads/workloadSelectionModel.ts @@ -1,11 +1,8 @@ import { parseWorkloadsLinkSearch, - WORKLOADS_PATH, - WORKLOADS_QUERY_PARAMS, } from '@/routing/resourceLinks'; import type { SummarySeriesGroupScope } from '@/components/shared/summaryCardInteraction'; import type { WorkloadGuest } from '@/types/workloads'; -import { areSearchParamsEquivalent } from '@/utils/searchParams'; import { getCanonicalWorkloadId } from '@/utils/workloads'; export interface WorkloadsResourceSelection { @@ -13,13 +10,6 @@ export interface WorkloadsResourceSelection { summaryGroupId: string | null; } -export interface WorkloadsSelectionNavigateTargetOptions { - pathname: string; - search: string; - resourceId: string | null; - summaryGroupId: string | null; -} - export const resolveWorkloadResourceSelection = ( search: string, ): WorkloadsResourceSelection | null => { @@ -32,35 +22,6 @@ export const resolveWorkloadResourceSelection = ( }; }; -export const resolveWorkloadsSelectionNavigateTarget = ({ - pathname, - search, - resourceId, - summaryGroupId, -}: WorkloadsSelectionNavigateTargetOptions): string | null => { - const currentParams = new URLSearchParams(search); - const nextParams = new URLSearchParams(search); - nextParams.delete(WORKLOADS_QUERY_PARAMS.resource); - nextParams.delete(WORKLOADS_QUERY_PARAMS.summaryGroup); - - const normalizedResourceId = resourceId?.trim() || ''; - const normalizedSummaryGroupId = summaryGroupId?.trim() || ''; - if (normalizedResourceId) { - nextParams.set(WORKLOADS_QUERY_PARAMS.resource, normalizedResourceId); - } - if (normalizedSummaryGroupId) { - nextParams.set(WORKLOADS_QUERY_PARAMS.summaryGroup, normalizedSummaryGroupId); - } - - if (areSearchParamsEquivalent(currentParams, nextParams)) { - return null; - } - - const nextSearch = nextParams.toString(); - const nextPathname = pathname.trim() || WORKLOADS_PATH; - return nextSearch ? `${nextPathname}?${nextSearch}` : nextPathname; -}; - export const workloadsHasHoveredWorkload = ( filteredGuests: WorkloadGuest[], hoveredId: string, diff --git a/frontend-modern/src/features/infrastructure/__tests__/useInfrastructurePageRouteState.test.ts b/frontend-modern/src/features/infrastructure/__tests__/useInfrastructurePageRouteState.test.ts new file mode 100644 index 000000000..96ad72443 --- /dev/null +++ b/frontend-modern/src/features/infrastructure/__tests__/useInfrastructurePageRouteState.test.ts @@ -0,0 +1,112 @@ +import { renderHook } from '@solidjs/testing-library'; +import { createSignal } from 'solid-js'; +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; + +import type { Resource } from '@/types/resource'; +import { ROUTE_STATE_REPLACE_OPTIONS } from '@/utils/routeStateNavigation'; + +import { useInfrastructurePageRouteState } from '../useInfrastructurePageRouteState'; + +let locationPath = '/infrastructure'; +let locationSearch = ''; +const navigateSpy = vi.fn(); + +vi.mock('@solidjs/router', () => ({ + useLocation: () => ({ + get pathname() { + return locationPath; + }, + get search() { + return locationSearch; + }, + }), + useNavigate: () => navigateSpy, +})); + +const makeResource = (id: string): Resource => + ({ + id, + name: id, + displayName: id, + sourceType: 'agent', + status: 'online', + type: 'agent', + }) as Resource; + +describe('useInfrastructurePageRouteState', () => { + beforeEach(() => { + locationPath = '/infrastructure'; + locationSearch = ''; + navigateSpy.mockReset(); + vi.useFakeTimers(); + vi.stubGlobal('requestAnimationFrame', (callback: FrameRequestCallback) => { + callback(0); + return 1; + }); + vi.stubGlobal('cancelAnimationFrame', vi.fn()); + Object.defineProperty(window.history, 'scrollRestoration', { + configurable: true, + value: 'auto', + writable: true, + }); + window.scrollTo = vi.fn() as typeof window.scrollTo; + }); + + afterEach(() => { + vi.runOnlyPendingTimers(); + vi.useRealTimers(); + vi.unstubAllGlobals(); + }); + + const setup = () => { + const [resources] = createSignal([makeResource('agent-1'), makeResource('agent-2')]); + const [selectedSource, setSelectedSource] = createSignal(''); + const [searchQuery, setSearchQuery] = createSignal(''); + const [initialLoadComplete] = createSignal(true); + + const rendered = renderHook(() => + useInfrastructurePageRouteState({ + resources, + filteredResources: resources, + initialLoadComplete, + selectedSource, + setSelectedSource, + searchQuery, + setSearchQuery, + }), + ); + + return { ...rendered, setSelectedSource, setSearchQuery }; + }; + + it('hydrates an inbound resource deep link into drawer state', () => { + locationSearch = '?resource=agent-1'; + + const { result } = setup(); + + expect(result.expandedResourceId()).toBe('agent-1'); + expect(navigateSpy).not.toHaveBeenCalled(); + }); + + it('keeps local drawer expansion off the route navigation path', () => { + const { result } = setup(); + + result.setExpandedResourceId('agent-2'); + vi.runAllTimers(); + + expect(result.expandedResourceId()).toBe('agent-2'); + expect(navigateSpy).not.toHaveBeenCalled(); + }); + + it('keeps infrastructure filters route-backed', () => { + const { setSelectedSource } = setup(); + + setSelectedSource('docker'); + vi.runAllTimers(); + + expect(navigateSpy).toHaveBeenCalledWith( + '/infrastructure?source=docker', + ROUTE_STATE_REPLACE_OPTIONS, + ); + }); +}); diff --git a/frontend-modern/src/features/infrastructure/useInfrastructurePageRouteState.ts b/frontend-modern/src/features/infrastructure/useInfrastructurePageRouteState.ts index c9f7e0dbd..096eb5bc4 100644 --- a/frontend-modern/src/features/infrastructure/useInfrastructurePageRouteState.ts +++ b/frontend-modern/src/features/infrastructure/useInfrastructurePageRouteState.ts @@ -145,28 +145,16 @@ export function useInfrastructurePageRouteState(options: InfrastructurePageRoute const nextSource = selectedSource(); const nextQuery = searchQuery().trim(); - const currentLinkedResource = parsed.resource; - const selectedResourceId = expandedResourceId(); - const shouldPreserveIncomingResource = - !selectedResourceId && Boolean(currentLinkedResource) && !initialLoadComplete(); - const nextResource = shouldPreserveIncomingResource - ? currentLinkedResource - : (selectedResourceId ?? ''); - const nextSummaryGroup = focusedResourceGroupId() ?? ''; const managedPath = buildInfrastructurePath({ source: nextSource || null, query: nextQuery || null, - resource: nextResource || null, - summaryGroup: nextSummaryGroup || null, }); const managedUrl = new URL(managedPath, 'http://pulse.local'); const currentParams = new URLSearchParams(location.search); const nextParams = new URLSearchParams(location.search); nextParams.delete(INFRASTRUCTURE_QUERY_PARAMS.source); nextParams.delete(INFRASTRUCTURE_QUERY_PARAMS.query); - nextParams.delete(INFRASTRUCTURE_QUERY_PARAMS.resource); - nextParams.delete(INFRASTRUCTURE_QUERY_PARAMS.summaryGroup); managedUrl.searchParams.forEach((value, key) => { nextParams.set(key, value); }); diff --git a/frontend-modern/src/utils/__tests__/frontendResourceTypeBoundaries.test.ts b/frontend-modern/src/utils/__tests__/frontendResourceTypeBoundaries.test.ts index d34e9923a..27c3132d3 100644 --- a/frontend-modern/src/utils/__tests__/frontendResourceTypeBoundaries.test.ts +++ b/frontend-modern/src/utils/__tests__/frontendResourceTypeBoundaries.test.ts @@ -762,7 +762,7 @@ describe('frontend resource type boundaries', () => { expect(workloadsWorkloadRouteStateSource).toContain('useWorkloadFilterOptions'); expect(workloadsWorkloadFilterOptionsSource).toContain("from './workloadFilterConfigModel'"); expect(workloadsWorkloadFilterOptionsSource).toContain( - 'buildWorkloadNodeOptions(options.allGuests())', + 'buildWorkloadNodeOptions(platformScopedGuests())', ); expect(workloadsWorkloadFilterOptionsSource).not.toContain( 'const onContextChange = (value: string) =>', @@ -1018,7 +1018,7 @@ describe('frontend resource type boundaries', () => { expect(guestDrawerSource).toContain('useGuestDrawerState'); expect(guestDrawerSource).toContain('GuestDrawerOverview'); expect(guestDrawerStateSource).toContain('getCanonicalWorkloadId'); - expect(guestDrawerStateSource).toContain("from '@/routing/resourceLinks'"); + expect(guestDrawerStateSource).not.toContain("from '@/routing/resourceLinks'"); expect(guestDrawerStateSource).not.toContain('./infrastructureLink'); expect(guestDrawerStateSource).toContain('guestOsSummary'); expect(guestDrawerSource).not.toContain('const guestId = () => {'); @@ -2702,7 +2702,7 @@ describe('frontend resource type boundaries', () => { expect(commandPaletteModalSource).not.toContain('buildInfrastructurePath'); expect(commandPaletteStateSource).toContain('useNavigate'); expect(commandPaletteStateSource).toContain('createSignal'); - expect(commandPaletteStateSource).toContain('buildInfrastructurePath'); + expect(commandPaletteStateSource).not.toContain('buildInfrastructurePath'); expect(commandPaletteModelSource).toContain('buildCommandPaletteCommands'); expect(commandPaletteModelSource).toContain('normalizeCommandPaletteQuery'); expect(commandPaletteModelSource).toContain('filterCommandPaletteCommands');