From e0e5a9440248479d3620e11149bfc0ad0e0e5274 Mon Sep 17 00:00:00 2001 From: David Goss Date: Tue, 18 Aug 2026 21:54:29 +0100 Subject: [PATCH] Improve useFilteredTestCases --- src/hooks/useFilteredTestCases.spec.tsx | 67 ++++++++++++++++++++++--- src/hooks/useFilteredTestCases.ts | 14 ++++-- 2 files changed, 71 insertions(+), 10 deletions(-) diff --git a/src/hooks/useFilteredTestCases.spec.tsx b/src/hooks/useFilteredTestCases.spec.tsx index 5ea17fc8..9376361f 100644 --- a/src/hooks/useFilteredTestCases.spec.tsx +++ b/src/hooks/useFilteredTestCases.spec.tsx @@ -17,12 +17,8 @@ interface ProviderProps { defaultHideStatuses?: readonly TestStepResultStatus[] } -function renderAndExtractPickleNames({ - envelopes, - defaultQuery, - defaultHideStatuses, -}: ProviderProps) { - return renderHook(() => useFilteredTestCases().map(({ pickle }) => pickle.name), { +function renderFilteredTestCases({ envelopes, defaultQuery, defaultHideStatuses }: ProviderProps) { + return renderHook(useFilteredTestCases, { wrapper: ({ children }) => ( pickle.name) + }, + }, + ...rest, + } +} + describe('useFilteredTestCases', () => { describe('with no filters', () => { it('returns a test case for every finished scenario', async () => { @@ -148,4 +156,51 @@ describe('useFilteredTestCases', () => { expect(result.current).to.include('another scenario') }) }) + + describe('readiness', () => { + it('leaves results undefined while the search is still running', () => { + const { result } = renderFilteredTestCases({ envelopes: retry, defaultQuery: 'third' }) + + expect(result.current.results).to.equal(undefined) + }) + + it('populates results once the search resolves', async () => { + const { result } = renderFilteredTestCases({ envelopes: retry, defaultQuery: 'third' }) + + await waitFor(() => expect(result.current.results).not.to.equal(undefined)) + }) + }) + + describe('the filtered flag', () => { + it('is false when no criteria are active', async () => { + const { result } = renderFilteredTestCases({ envelopes: hooksConditional }) + + await waitFor(() => expect(result.current.results).to.have.lengthOf(3)) + expect(result.current.filtered).to.equal(false) + }) + + it('is true when a tag expression is active', async () => { + const { result } = renderFilteredTestCases({ + envelopes: hooksConditional, + defaultQuery: '@fail-before', + }) + + await waitFor(() => expect(result.current.filtered).to.equal(true)) + }) + + it('is true when a text search is active', async () => { + const { result } = renderFilteredTestCases({ envelopes: retry, defaultQuery: 'third' }) + + await waitFor(() => expect(result.current.filtered).to.equal(true)) + }) + + it('is true when statuses are hidden', async () => { + const { result } = renderFilteredTestCases({ + envelopes: retry, + defaultHideStatuses: [TestStepResultStatus.FAILED], + }) + + await waitFor(() => expect(result.current.filtered).to.equal(true)) + }) + }) }) diff --git a/src/hooks/useFilteredTestCases.ts b/src/hooks/useFilteredTestCases.ts index d80add38..473b290c 100644 --- a/src/hooks/useFilteredTestCases.ts +++ b/src/hooks/useFilteredTestCases.ts @@ -9,13 +9,16 @@ import { useQueries } from './useQueries.js' import { useSearch } from './useSearch.js' import { useSearchResult } from './useSearchResult.js' -export function useFilteredTestCases(): ReadonlyArray> { +export function useFilteredTestCases(): { + results?: ReadonlyArray> + filtered: boolean +} { const { cucumberQuery } = useQueries() const allTestCasesFinished = useMemo( () => cucumberQuery.findAllTestCaseFinished(), [cucumberQuery] ) - const { hideStatuses, tagExpression } = useSearch() + const { hideStatuses, tagExpression, unchanged } = useSearch() const candidates = useMemo( () => filterAndExpandTestCaseEvents(cucumberQuery, allTestCasesFinished, { @@ -25,7 +28,7 @@ export function useFilteredTestCases(): ReadonlyArray>>([]) + const [results, setResults] = useState>>() useEffect(() => { switch (searchResult.status) { @@ -43,5 +46,8 @@ export function useFilteredTestCases(): ReadonlyArray