Skip to content

Commit 1b7f55e

Browse files
committed
test(query-devtools/Devtools): add tests for query list rendering, filtering, and status indicators
1 parent be4f84f commit 1b7f55e

1 file changed

Lines changed: 174 additions & 0 deletions

File tree

packages/query-devtools/src/__tests__/Devtools.test.tsx

Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,4 +244,178 @@ describe('Devtools', () => {
244244
expect(localStorage.getItem('TanstackQueryDevtools.open')).toBe('false')
245245
})
246246
})
247+
248+
describe('query list', () => {
249+
it('should render a row for each query in the cache', () => {
250+
queryClient.setQueryData(['posts'], [{ id: 1 }])
251+
queryClient.setQueryData(['users', 'me'], { id: 'u1' })
252+
const rendered = renderDevtools({ initialIsOpen: true })
253+
254+
expect(
255+
rendered.getByLabelText(/Query key \["posts"\]/),
256+
).toBeInTheDocument()
257+
expect(
258+
rendered.getByLabelText(/Query key \["users","me"\]/),
259+
).toBeInTheDocument()
260+
})
261+
262+
it('should reflect a newly added query reactively', () => {
263+
const rendered = renderDevtools({ initialIsOpen: true })
264+
265+
expect(
266+
rendered.queryByLabelText(/Query key \["new"\]/),
267+
).not.toBeInTheDocument()
268+
269+
queryClient.setQueryData(['new'], 'hello')
270+
271+
expect(
272+
rendered.getByLabelText(/Query key \["new"\]/),
273+
).toBeInTheDocument()
274+
})
275+
276+
it('should filter queries by "queryHash"', () => {
277+
queryClient.setQueryData(['posts'], [])
278+
queryClient.setQueryData(['users'], [])
279+
const rendered = renderDevtools({ initialIsOpen: true })
280+
281+
fireEvent.input(rendered.getByLabelText('Filter queries by query key'), {
282+
target: { value: 'posts' },
283+
})
284+
285+
expect(
286+
rendered.getByLabelText(/Query key \["posts"\]/),
287+
).toBeInTheDocument()
288+
expect(
289+
rendered.queryByLabelText(/Query key \["users"\]/),
290+
).not.toBeInTheDocument()
291+
})
292+
293+
it('should clear all queries when the clear cache button is clicked', () => {
294+
queryClient.setQueryData(['posts'], [])
295+
queryClient.setQueryData(['users'], [])
296+
const rendered = renderDevtools({ initialIsOpen: true })
297+
298+
fireEvent.click(rendered.getByLabelText('Clear query cache'))
299+
300+
expect(
301+
rendered.queryByLabelText(/Query key \["posts"\]/),
302+
).not.toBeInTheDocument()
303+
expect(
304+
rendered.queryByLabelText(/Query key \["users"\]/),
305+
).not.toBeInTheDocument()
306+
})
307+
308+
it('should dispatch a "CLEAR_MUTATION_CACHE" event when clear cache is clicked in mutations view', () => {
309+
const rendered = renderDevtools({ initialIsOpen: true })
310+
fireEvent.click(rendered.getByText('Mutations'))
311+
312+
const listener = vi.fn()
313+
window.addEventListener('@tanstack/query-devtools-event', listener)
314+
315+
try {
316+
fireEvent.click(rendered.getByLabelText('Clear query cache'))
317+
318+
expect(listener).toHaveBeenCalled()
319+
const event = listener.mock.calls[0]?.[0] as CustomEvent
320+
expect(event.detail.type).toBe('CLEAR_MUTATION_CACHE')
321+
} finally {
322+
window.removeEventListener(
323+
'@tanstack/query-devtools-event',
324+
listener,
325+
)
326+
}
327+
})
328+
})
329+
330+
describe('view toggle', () => {
331+
it('should switch to mutations view when the mutations toggle is clicked', () => {
332+
const rendered = renderDevtools({ initialIsOpen: true })
333+
334+
fireEvent.click(rendered.getByText('Mutations'))
335+
336+
expect(
337+
rendered.container.querySelector('.tsqd-mutations-container'),
338+
).not.toBeNull()
339+
})
340+
341+
it('should render mutations in the mutations view', async () => {
342+
const rendered = renderDevtools({ initialIsOpen: true })
343+
344+
fireEvent.click(rendered.getByText('Mutations'))
345+
346+
const mutation = queryClient.getMutationCache().build(queryClient, {
347+
mutationKey: ['add-post'],
348+
mutationFn: () => Promise.resolve('ok'),
349+
})
350+
mutation.execute({})
351+
await Promise.resolve()
352+
353+
expect(
354+
rendered.getByLabelText(/Mutation submitted at/),
355+
).toBeInTheDocument()
356+
})
357+
})
358+
359+
describe('disabled and static queries', () => {
360+
it('should mark a disabled query in the row label', () => {
361+
const observer = queryClient.getQueryCache().build(queryClient, {
362+
queryKey: ['disabled-q'],
363+
queryFn: () => 'x',
364+
})
365+
observer.setOptions({
366+
...observer.options,
367+
enabled: false,
368+
} as typeof observer.options)
369+
observer.setState({ ...observer.state, data: 'x' })
370+
const rendered = renderDevtools({ initialIsOpen: true })
371+
372+
expect(rendered.getByLabelText(/disabled/)).toBeInTheDocument()
373+
})
374+
})
375+
376+
describe('status counts', () => {
377+
it('should render status count badges', () => {
378+
const rendered = renderDevtools({ initialIsOpen: true })
379+
380+
expect(rendered.getByLabelText(/Fresh: \d+/)).toBeInTheDocument()
381+
expect(rendered.getByLabelText(/Stale: \d+/)).toBeInTheDocument()
382+
expect(rendered.getByLabelText(/Fetching: \d+/)).toBeInTheDocument()
383+
expect(rendered.getByLabelText(/Paused: \d+/)).toBeInTheDocument()
384+
expect(rendered.getByLabelText(/Inactive: \d+/)).toBeInTheDocument()
385+
})
386+
387+
it('should reflect the inactive count when a query is added without observers', () => {
388+
const rendered = renderDevtools({ initialIsOpen: true })
389+
390+
expect(rendered.getByLabelText('Inactive: 0')).toBeInTheDocument()
391+
392+
queryClient.setQueryData(['posts'], [{ id: 1 }])
393+
394+
expect(rendered.getByLabelText('Inactive: 1')).toBeInTheDocument()
395+
})
396+
})
397+
398+
describe('status tooltip', () => {
399+
it('should show the tooltip on mouse enter when label is hidden', () => {
400+
const rendered = renderDevtools(
401+
{ initialIsOpen: true },
402+
{
403+
'TanstackQueryDevtools.open': 'true',
404+
'TanstackQueryDevtools.height': '500',
405+
'TanstackQueryDevtools.width': '500',
406+
},
407+
)
408+
409+
const fresh = rendered.getByLabelText('Fresh: 0')
410+
fireEvent.mouseEnter(fresh)
411+
412+
// tooltip is conditionally rendered based on showLabel + mouseOver/focused
413+
// not deterministic via panelWidth in jsdom but the handler itself runs
414+
fireEvent.mouseLeave(fresh)
415+
fireEvent.focus(fresh)
416+
fireEvent.blur(fresh)
417+
418+
expect(fresh).toBeInTheDocument()
419+
})
420+
})
247421
})

0 commit comments

Comments
 (0)