From 3ed5d63a7b0513d5e7a15606590016ae7eca583d Mon Sep 17 00:00:00 2001 From: Wonsuk Choi Date: Mon, 18 May 2026 09:11:24 +0900 Subject: [PATCH 1/2] test(query-devtools/Explorer): add tests for primitive values and basic collection rendering (#10714) --- .../src/__tests__/Explorer.test.tsx | 123 ++++++++++++++++++ 1 file changed, 123 insertions(+) create mode 100644 packages/query-devtools/src/__tests__/Explorer.test.tsx diff --git a/packages/query-devtools/src/__tests__/Explorer.test.tsx b/packages/query-devtools/src/__tests__/Explorer.test.tsx new file mode 100644 index 0000000000..30bc632b31 --- /dev/null +++ b/packages/query-devtools/src/__tests__/Explorer.test.tsx @@ -0,0 +1,123 @@ +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' +import { fireEvent, render } from '@solidjs/testing-library' +import { QueryClient, onlineManager } from '@tanstack/query-core' +import Explorer from '../Explorer' +import { QueryDevtoolsContext, ThemeContext } from '../contexts' + +// `goober` compiles every `css\`...\`` template literal at mount time; +// replace it with a no-op factory so label/role-based assertions stay fast. +vi.mock('goober', () => { + let counter = 0 + const css = Object.assign(() => `tsqd-${++counter}`, { + bind: () => css, + }) + return { css, glob: () => {}, setup: () => {} } +}) + +describe('Explorer', () => { + let queryClient: QueryClient + + beforeEach(() => { + queryClient = new QueryClient() + }) + + afterEach(() => { + queryClient.clear() + }) + + function renderExplorer(props: Parameters[0]) { + return render(() => ( + + 'dark'}> + + + + )) + } + + describe('primitive values', () => { + it('should render a "label: value" row for a string value', () => { + const rendered = renderExplorer({ label: 'name', value: 'Anna' }) + + expect(rendered.getByText('name:')).toBeInTheDocument() + expect(rendered.getByText('"Anna"')).toBeInTheDocument() + }) + + it('should render a "label: value" row for a number value', () => { + const rendered = renderExplorer({ label: 'count', value: 42 }) + + expect(rendered.getByText('count:')).toBeInTheDocument() + expect(rendered.getByText('42')).toBeInTheDocument() + }) + + it('should render a "label: value" row for a boolean value', () => { + const rendered = renderExplorer({ label: 'active', value: true }) + + expect(rendered.getByText('active:')).toBeInTheDocument() + expect(rendered.getByText('true')).toBeInTheDocument() + }) + + it('should render a "label: value" row for a "null" value', () => { + const rendered = renderExplorer({ label: 'missing', value: null }) + + expect(rendered.getByText('missing:')).toBeInTheDocument() + expect(rendered.getByText('null')).toBeInTheDocument() + }) + }) + + describe('arrays and objects', () => { + it('should render an empty object as a primitive row (no expander)', () => { + const rendered = renderExplorer({ label: 'data', value: {} }) + + expect(rendered.getByText('data:')).toBeInTheDocument() + expect(rendered.queryByRole('button', { expanded: false })).toBeNull() + }) + + it('should render an array with an expander showing the item count', () => { + const rendered = renderExplorer({ + label: 'list', + value: ['a', 'b', 'c'], + }) + + const expander = rendered.getByRole('button', { expanded: false }) + expect(expander).toBeInTheDocument() + expect(expander.textContent).toContain('list') + expect(expander.textContent).toContain('3 items') + }) + + it('should render children under their index labels when the array expander is clicked', () => { + const rendered = renderExplorer({ + label: 'list', + value: ['a', 'b'], + }) + + fireEvent.click(rendered.getByRole('button', { expanded: false })) + + expect(rendered.getByText('0:')).toBeInTheDocument() + expect(rendered.getByText('1:')).toBeInTheDocument() + expect(rendered.getByText('"a"')).toBeInTheDocument() + expect(rendered.getByText('"b"')).toBeInTheDocument() + }) + + it('should render object entries under their keys when expanded', () => { + const rendered = renderExplorer({ + label: 'user', + value: { name: 'Anna', age: 30 }, + }) + + fireEvent.click(rendered.getByRole('button', { expanded: false })) + + expect(rendered.getByText('name:')).toBeInTheDocument() + expect(rendered.getByText('"Anna"')).toBeInTheDocument() + expect(rendered.getByText('age:')).toBeInTheDocument() + expect(rendered.getByText('30')).toBeInTheDocument() + }) + }) +}) From 334161f2f511689b3998c6e041d9c25f792a2721 Mon Sep 17 00:00:00 2001 From: Wonsuk Choi Date: Mon, 18 May 2026 09:38:20 +0900 Subject: [PATCH 2/2] test(query-devtools/Explorer): add tests for 'Map' and iterable rendering branches (#10721) --- .../src/__tests__/Explorer.test.tsx | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/packages/query-devtools/src/__tests__/Explorer.test.tsx b/packages/query-devtools/src/__tests__/Explorer.test.tsx index 30bc632b31..a242cc19cb 100644 --- a/packages/query-devtools/src/__tests__/Explorer.test.tsx +++ b/packages/query-devtools/src/__tests__/Explorer.test.tsx @@ -120,4 +120,44 @@ describe('Explorer', () => { expect(rendered.getByText('30')).toBeInTheDocument() }) }) + + describe('Map and iterable values', () => { + it('should preserve "Map" keys as labels when expanded', () => { + const rendered = renderExplorer({ + label: 'm', + value: new Map([ + ['first', 1], + ['second', 2], + ]), + }) + + fireEvent.click(rendered.getByRole('button', { expanded: false })) + + expect(rendered.getByText('first:')).toBeInTheDocument() + expect(rendered.getByText('second:')).toBeInTheDocument() + }) + + it('should mark an iterable value with an "(Iterable)" prefix on the expander', () => { + const rendered = renderExplorer({ + label: 's', + value: new Set(['x', 'y']), + }) + + expect( + rendered.getByRole('button', { expanded: false }).textContent, + ).toContain('(Iterable)') + }) + + it('should render iterable children under their numeric index when expanded', () => { + const rendered = renderExplorer({ + label: 's', + value: new Set(['x', 'y']), + }) + + fireEvent.click(rendered.getByRole('button', { expanded: false })) + + expect(rendered.getByText('0:')).toBeInTheDocument() + expect(rendered.getByText('1:')).toBeInTheDocument() + }) + }) })