diff --git a/packages/devtools/src/rpc/__tests__/ngrx-live-tools.test.ts b/packages/devtools/src/rpc/__tests__/ngrx-live-tools.test.ts index dc3d33cc..4e77ca9b 100644 --- a/packages/devtools/src/rpc/__tests__/ngrx-live-tools.test.ts +++ b/packages/devtools/src/rpc/__tests__/ngrx-live-tools.test.ts @@ -306,3 +306,47 @@ describe('withUntrustedPreamble', () => { expect(text.length).toBeLessThanOrEqual(NGRX_LIVE_TOOL_MAX + 200); }); }); + +describe('an unknown page', () => { + function twoPages(): NgrxPages { + const pages: NgrxPages = new Map(); + for (const [pid, at] of [ + ['p1', 1_000], + ['p2', 2_000], + ] as const) { + mergeNgrxReport( + pages, + { + pageId: pid, + session: 's', + url: `/${pid}`, + title: '', + stores: [store()], + classic: null, + log: [logEntry({ seq: 1 })], + }, + [], + at, + ); + } + return pages; + } + + const tools = [ + ['inspect-signal-store', (pages: NgrxPages) => inspectSignalStoreText(pages, 'p3')], + ['signal-store-history', (pages: NgrxPages) => signalStoreHistoryText(pages, 'p3')], + ] as const; + + for (const [name, answer] of tools) { + it(`${name} lists the pages that do report, newest first`, () => { + const text = answer(twoPages()); + expect(text).toContain('No page `p3` is reporting NgRx state.'); + expect(text).toMatch(/`p2`.*`p1`/); + expect(text).not.toMatch(/no ngrx state has been reported/i); + }); + + it(`${name} still says nothing was reported when no page reports`, () => { + expect(answer(new Map())).toMatch(/no ngrx state has been reported/i); + }); + } +}); diff --git a/packages/devtools/src/rpc/ngrx-live-tools.ts b/packages/devtools/src/rpc/ngrx-live-tools.ts index 709e5290..32aca033 100644 --- a/packages/devtools/src/rpc/ngrx-live-tools.ts +++ b/packages/devtools/src/rpc/ngrx-live-tools.ts @@ -1,5 +1,6 @@ import type { NgrxPageRecord, NgrxPages } from './ngrx-tools.ts'; import type { NgrxLogEntry, NgrxSignalStoreInfo } from '../ngrx-shared.ts'; +import { unknownPageText } from './pages.ts'; /** * `inspectSignalStoreText`/`signalStoreHistoryText` output is capped to this @@ -26,6 +27,15 @@ function pagesOf(pages: NgrxPages, pageId?: string): NgrxPageRecord[] { return pageId ? all.filter((p) => p.pageId === pageId) : all; } +/** + * The answer when no page matches. An unknown `page` lists the tabs that do report + * NgRx state, like the other page tools; with no tab reporting at all it is `NO_DATA`. + */ +function noMatchText(pages: NgrxPages, pageId?: string): string { + if (pageId && pages.size) return unknownPageText(pageId, [...pages.values()], 'NgRx state'); + return pageId ? `No page ${code(pageId)} is reporting NgRx state.\n\n${NO_DATA}` : NO_DATA; +} + function storeLabel(store: NgrxSignalStoreInfo): string { return store.name ?? store.className; } @@ -119,9 +129,7 @@ export function inspectSignalStoreText( storeId?: string, ): string { const matching = pagesOf(pages, pageId); - if (!matching.length) { - return pageId ? `No page ${code(pageId)} is reporting NgRx state.\n\n${NO_DATA}` : NO_DATA; - } + if (!matching.length) return noMatchText(pages, pageId); if (storeId) { for (const page of matching) { const store = page.stores.find((s) => s.id === storeId); @@ -181,9 +189,7 @@ export function signalStoreHistoryText( since?: number, ): string { const matching = pagesOf(pages, pageId); - if (!matching.length) { - return pageId ? `No page ${code(pageId)} is reporting NgRx state.\n\n${NO_DATA}` : NO_DATA; - } + if (!matching.length) return noMatchText(pages, pageId); if (typeof since === 'number' && matching.length > 1) { return `Pass \`page\` to identify a single page when using \`since\`; sequence numbers are per-page and cannot be compared across pages.`; }