snapshot of https://github.com/TanStack/query.git (main, 2026-09-16; blobs > 131072B omitted)
Some checks failed
autofix.ci / autofix (push) Failing after 4s
Release / Release (push) Has been skipped
GitHub Actions Security Analysis / Run zizmor (push) Failing after 2s

This commit is contained in:
upstream-mirror
2026-09-16 01:13:08 +05:00
commit 3a013ccdfe
3570 changed files with 490194 additions and 0 deletions

View File

@@ -0,0 +1,674 @@
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import { QueryClient } from '@tanstack/query-core'
import { TestBed } from '@angular/core/testing'
import {
ENVIRONMENT_INITIALIZER,
EnvironmentInjector,
InjectionToken,
PLATFORM_ID,
createEnvironmentInjector,
isDevMode,
provideZonelessChangeDetection,
signal,
} from '@angular/core'
import { provideTanStackQuery } from '../providers'
import { withDevtools } from '../devtools'
import type {
DevtoolsButtonPosition,
DevtoolsErrorType,
DevtoolsPosition,
Theme,
} from '@tanstack/query-devtools'
import type { DevtoolsOptions } from '../devtools'
const mockDevtoolsInstance = {
mount: vi.fn(),
unmount: vi.fn(),
setClient: vi.fn(),
setPosition: vi.fn(),
setErrorTypes: vi.fn(),
setButtonPosition: vi.fn(),
setInitialIsOpen: vi.fn(),
setTheme: vi.fn(),
}
function MockTanstackQueryDevtools() {
return mockDevtoolsInstance
}
const mockTanstackQueryDevtools = vi.fn(MockTanstackQueryDevtools)
vi.mock('@tanstack/query-devtools', () => ({
TanstackQueryDevtools: mockTanstackQueryDevtools,
}))
vi.mock('@angular/core', async () => {
const actual = await vi.importActual('@angular/core')
return {
...actual,
isDevMode: vi.fn(),
}
})
const mockIsDevMode = vi.mocked(isDevMode)
describe('withDevtools feature', () => {
beforeEach(() => {
vi.clearAllMocks()
vi.useFakeTimers()
})
afterEach(() => {
vi.restoreAllMocks()
TestBed.resetTestingModule()
vi.useRealTimers()
})
it.each([
{
description: 'should load devtools in development mode',
isDevMode: true,
expectedCalled: true,
},
{
description: 'should not load devtools in production mode',
isDevMode: false,
expectedCalled: false,
},
{
description: `should load devtools in development mode when 'loadDevtools' is set to 'auto'`,
isDevMode: true,
loadDevtools: 'auto',
expectedCalled: true,
},
{
description: `should not load devtools in production mode when 'loadDevtools' is set to 'auto'`,
isDevMode: false,
loadDevtools: 'auto',
expectedCalled: false,
},
{
description:
"should load devtools in development mode when 'loadDevtools' is set to true",
isDevMode: true,
loadDevtools: true,
expectedCalled: true,
},
{
description:
"should load devtools in production mode when 'loadDevtools' is set to true",
isDevMode: false,
loadDevtools: true,
expectedCalled: true,
},
{
description:
"should not load devtools in development mode when 'loadDevtools' is set to false",
isDevMode: true,
loadDevtools: false,
expectedCalled: false,
},
{
description:
"should not load devtools in production mode when 'loadDevtools' is set to false",
isDevMode: false,
loadDevtools: false,
expectedCalled: false,
},
])(
'$description',
async ({ isDevMode: isDevModeValue, loadDevtools, expectedCalled }) => {
mockIsDevMode.mockReturnValue(isDevModeValue)
const providers = [
provideZonelessChangeDetection(),
provideTanStackQuery(
new QueryClient(),
loadDevtools !== undefined
? withDevtools(
() =>
({
loadDevtools,
}) as DevtoolsOptions,
)
: withDevtools(),
),
]
TestBed.configureTestingModule({
providers,
})
TestBed.inject(ENVIRONMENT_INITIALIZER)
await vi.advanceTimersByTimeAsync(0)
TestBed.tick()
await vi.dynamicImportSettled()
TestBed.tick()
await vi.dynamicImportSettled()
expect(mockTanstackQueryDevtools).toHaveBeenCalledTimes(
expectedCalled ? 1 : 0,
)
expect(mockDevtoolsInstance.mount).toHaveBeenCalledTimes(
expectedCalled ? 1 : 0,
)
},
)
it("should throw 'No QueryClient found' when 'loadDevtools' is 'true' and no 'QueryClient' is provided", async () => {
const consoleErrorSpy = vi
.spyOn(console, 'error')
.mockImplementation(() => undefined)
TestBed.configureTestingModule({
providers: [
provideZonelessChangeDetection(),
withDevtools(() => ({
loadDevtools: true,
})).ɵproviders,
],
})
TestBed.inject(ENVIRONMENT_INITIALIZER)
TestBed.tick()
await vi.dynamicImportSettled()
expect(mockTanstackQueryDevtools).not.toHaveBeenCalled()
expect(consoleErrorSpy).toHaveBeenCalledWith(
'Install @tanstack/query-devtools or reinstall without --omit=optional.',
expect.objectContaining({ message: 'No QueryClient found' }),
)
consoleErrorSpy.mockRestore()
})
it('should not continue loading devtools after injector is destroyed', async () => {
TestBed.configureTestingModule({
providers: [
provideZonelessChangeDetection(),
provideTanStackQuery(
new QueryClient(),
withDevtools(() => ({
loadDevtools: true,
})),
),
],
})
TestBed.inject(ENVIRONMENT_INITIALIZER)
// Destroys injector
TestBed.resetTestingModule()
await vi.advanceTimersByTimeAsync(0)
await vi.dynamicImportSettled()
expect(mockTanstackQueryDevtools).not.toHaveBeenCalled()
})
it('should not create devtools again when already provided', async () => {
TestBed.configureTestingModule({
providers: [
provideZonelessChangeDetection(),
provideTanStackQuery(
new QueryClient(),
withDevtools(() => ({
loadDevtools: true,
})),
),
],
})
TestBed.inject(ENVIRONMENT_INITIALIZER)
await vi.advanceTimersByTimeAsync(0)
await vi.dynamicImportSettled()
expect(mockTanstackQueryDevtools).toHaveBeenCalledTimes(1)
const injector = TestBed.inject(EnvironmentInjector)
createEnvironmentInjector(
[
withDevtools(() => ({
loadDevtools: true,
})).ɵproviders,
],
injector,
)
TestBed.inject(ENVIRONMENT_INITIALIZER)
await vi.advanceTimersByTimeAsync(0)
expect(mockTanstackQueryDevtools).toHaveBeenCalledTimes(1)
})
it('should not load devtools if platform is not browser', async () => {
TestBed.configureTestingModule({
providers: [
{
provide: PLATFORM_ID,
useValue: 'server',
},
provideZonelessChangeDetection(),
provideTanStackQuery(
new QueryClient(),
withDevtools(() => ({
loadDevtools: true,
})),
),
],
})
TestBed.inject(ENVIRONMENT_INITIALIZER)
await vi.runAllTimersAsync()
expect(mockTanstackQueryDevtools).not.toHaveBeenCalled()
})
it('should update error types', async () => {
const errorTypes = signal([] as Array<DevtoolsErrorType>)
TestBed.configureTestingModule({
providers: [
provideZonelessChangeDetection(),
provideTanStackQuery(
new QueryClient(),
withDevtools(() => ({
loadDevtools: true,
errorTypes: errorTypes(),
})),
),
],
})
TestBed.inject(ENVIRONMENT_INITIALIZER)
await vi.advanceTimersByTimeAsync(0)
await vi.dynamicImportSettled()
TestBed.tick()
expect(mockDevtoolsInstance.setErrorTypes).toHaveBeenCalledTimes(0)
const newErrorTypes = [
{
name: '',
initializer: () => new Error(),
},
]
errorTypes.set(newErrorTypes)
TestBed.tick()
expect(mockDevtoolsInstance.setErrorTypes).toHaveBeenCalledTimes(1)
expect(mockDevtoolsInstance.setErrorTypes).toHaveBeenCalledWith(
newErrorTypes,
)
})
it('should update client', async () => {
const client = signal(new QueryClient())
TestBed.configureTestingModule({
providers: [
provideZonelessChangeDetection(),
provideTanStackQuery(
new QueryClient(),
withDevtools(() => ({
loadDevtools: true,
client: client(),
})),
),
],
})
TestBed.inject(ENVIRONMENT_INITIALIZER)
await vi.advanceTimersByTimeAsync(0)
await vi.dynamicImportSettled()
TestBed.tick()
expect(mockDevtoolsInstance.setClient).toHaveBeenCalledTimes(0)
const newClient = new QueryClient()
client.set(newClient)
TestBed.tick()
expect(mockDevtoolsInstance.setClient).toHaveBeenCalledTimes(1)
expect(mockDevtoolsInstance.setClient).toHaveBeenCalledWith(newClient)
})
it('should update position', async () => {
const position = signal<DevtoolsPosition>('top')
TestBed.configureTestingModule({
providers: [
provideZonelessChangeDetection(),
provideTanStackQuery(
new QueryClient(),
withDevtools(() => ({
loadDevtools: true,
position: position(),
})),
),
],
})
TestBed.inject(ENVIRONMENT_INITIALIZER)
await vi.advanceTimersByTimeAsync(0)
await vi.dynamicImportSettled()
TestBed.tick()
expect(mockDevtoolsInstance.setPosition).toHaveBeenCalledTimes(0)
position.set('left')
TestBed.tick()
expect(mockDevtoolsInstance.setPosition).toHaveBeenCalledTimes(1)
expect(mockDevtoolsInstance.setPosition).toHaveBeenCalledWith('left')
})
it('should update button position', async () => {
const buttonPosition = signal<DevtoolsButtonPosition>('bottom-left')
TestBed.configureTestingModule({
providers: [
provideZonelessChangeDetection(),
provideTanStackQuery(
new QueryClient(),
withDevtools(() => ({
loadDevtools: true,
buttonPosition: buttonPosition(),
})),
),
],
})
TestBed.inject(ENVIRONMENT_INITIALIZER)
await vi.advanceTimersByTimeAsync(0)
await vi.dynamicImportSettled()
TestBed.tick()
expect(mockDevtoolsInstance.setButtonPosition).toHaveBeenCalledTimes(0)
buttonPosition.set('bottom-right')
TestBed.tick()
expect(mockDevtoolsInstance.setButtonPosition).toHaveBeenCalledTimes(1)
expect(mockDevtoolsInstance.setButtonPosition).toHaveBeenCalledWith(
'bottom-right',
)
})
it('should update initialIsOpen', async () => {
const initialIsOpen = signal(false)
TestBed.configureTestingModule({
providers: [
provideZonelessChangeDetection(),
provideTanStackQuery(
new QueryClient(),
withDevtools(() => ({
loadDevtools: true,
initialIsOpen: initialIsOpen(),
})),
),
],
})
TestBed.inject(ENVIRONMENT_INITIALIZER)
await vi.advanceTimersByTimeAsync(0)
await vi.dynamicImportSettled()
TestBed.tick()
expect(mockDevtoolsInstance.setInitialIsOpen).toHaveBeenCalledTimes(0)
initialIsOpen.set(true)
TestBed.tick()
expect(mockDevtoolsInstance.setInitialIsOpen).toHaveBeenCalledTimes(1)
expect(mockDevtoolsInstance.setInitialIsOpen).toHaveBeenCalledWith(true)
})
it('should update theme', async () => {
const theme = signal<Theme>('system')
TestBed.configureTestingModule({
providers: [
provideZonelessChangeDetection(),
provideTanStackQuery(
new QueryClient(),
withDevtools(() => ({
loadDevtools: true,
theme: theme(),
})),
),
],
})
TestBed.inject(ENVIRONMENT_INITIALIZER)
await vi.advanceTimersByTimeAsync(0)
await vi.dynamicImportSettled()
TestBed.tick()
expect(mockDevtoolsInstance.setTheme).toHaveBeenCalledTimes(0)
theme.set('dark')
TestBed.tick()
expect(mockDevtoolsInstance.setTheme).toHaveBeenCalledTimes(1)
expect(mockDevtoolsInstance.setTheme).toHaveBeenCalledWith('dark')
})
it('should destroy devtools', async () => {
const loadDevtools = signal(true)
TestBed.configureTestingModule({
providers: [
provideZonelessChangeDetection(),
provideTanStackQuery(
new QueryClient(),
withDevtools(() => ({
loadDevtools: loadDevtools(),
})),
),
],
})
TestBed.inject(ENVIRONMENT_INITIALIZER)
await vi.advanceTimersByTimeAsync(0)
await vi.dynamicImportSettled()
expect(mockDevtoolsInstance.mount).toHaveBeenCalledTimes(1)
expect(mockDevtoolsInstance.unmount).toHaveBeenCalledTimes(0)
loadDevtools.set(false)
TestBed.tick()
expect(mockDevtoolsInstance.unmount).toHaveBeenCalledTimes(1)
})
it('should unmount devtools when injector is destroyed', async () => {
TestBed.configureTestingModule({
providers: [
provideZonelessChangeDetection(),
provideTanStackQuery(
new QueryClient(),
withDevtools(() => ({
loadDevtools: true,
})),
),
],
})
TestBed.inject(ENVIRONMENT_INITIALIZER)
await vi.advanceTimersByTimeAsync(0)
TestBed.tick()
await vi.dynamicImportSettled()
expect(mockTanstackQueryDevtools).toHaveBeenCalled()
expect(mockDevtoolsInstance.mount).toHaveBeenCalledTimes(1)
expect(mockDevtoolsInstance.unmount).toHaveBeenCalledTimes(0)
// Destroy the injector
TestBed.resetTestingModule()
expect(mockDevtoolsInstance.unmount).toHaveBeenCalledTimes(1)
})
it('should remount devtools when toggled from false to true', async () => {
const loadDevtools = signal(false)
TestBed.configureTestingModule({
providers: [
provideZonelessChangeDetection(),
provideTanStackQuery(
new QueryClient(),
withDevtools(() => ({
loadDevtools: loadDevtools(),
})),
),
],
})
TestBed.inject(ENVIRONMENT_INITIALIZER)
await vi.advanceTimersByTimeAsync(0)
expect(mockTanstackQueryDevtools).not.toHaveBeenCalled()
expect(mockDevtoolsInstance.mount).not.toHaveBeenCalled()
loadDevtools.set(true)
TestBed.tick()
await vi.dynamicImportSettled()
expect(mockTanstackQueryDevtools).toHaveBeenCalledTimes(1)
expect(mockDevtoolsInstance.mount).toHaveBeenCalledTimes(1)
expect(mockDevtoolsInstance.unmount).not.toHaveBeenCalled()
loadDevtools.set(false)
TestBed.tick()
expect(mockDevtoolsInstance.unmount).toHaveBeenCalledTimes(1)
expect(mockDevtoolsInstance.mount).toHaveBeenCalledTimes(1)
loadDevtools.set(true)
TestBed.tick()
await vi.dynamicImportSettled()
// Should remount (mount called twice now)
expect(mockDevtoolsInstance.mount).toHaveBeenCalledTimes(2)
expect(mockDevtoolsInstance.unmount).toHaveBeenCalledTimes(1)
})
describe('deps parameter', () => {
it('should inject dependencies and pass them to withDevtoolsFn in correct order', async () => {
const mockService1 = { value: 'service1' }
const mockService2 = { value: 'service2' }
const mockService1Token = new InjectionToken('MockService1')
const mockService2Token = new InjectionToken('MockService2')
const withDevtoolsFn = vi.fn().mockReturnValue({ loadDevtools: true })
TestBed.configureTestingModule({
providers: [
provideZonelessChangeDetection(),
{
provide: mockService1Token,
useValue: mockService1,
},
{
provide: mockService2Token,
useValue: mockService2,
},
provideTanStackQuery(
new QueryClient(),
withDevtools(withDevtoolsFn, {
deps: [mockService1Token, mockService2Token],
}),
),
],
})
TestBed.inject(ENVIRONMENT_INITIALIZER)
await vi.advanceTimersByTimeAsync(0)
expect(withDevtoolsFn).toHaveBeenCalledWith(mockService1, mockService2)
})
it('should work with empty deps array', async () => {
const withDevtoolsFn = vi.fn().mockReturnValue({ loadDevtools: true })
TestBed.configureTestingModule({
providers: [
provideZonelessChangeDetection(),
provideTanStackQuery(
new QueryClient(),
withDevtools(withDevtoolsFn, {
deps: [],
}),
),
],
})
TestBed.inject(ENVIRONMENT_INITIALIZER)
await vi.advanceTimersByTimeAsync(0)
expect(withDevtoolsFn).toHaveBeenCalledWith()
})
it('should reactively update when injected services change', async () => {
class ReactiveService {
enabled = signal(false)
position = signal<DevtoolsPosition>('bottom')
}
const withDevtoolsFn = (service: ReactiveService) => ({
loadDevtools: service.enabled(),
position: service.position(),
})
TestBed.configureTestingModule({
providers: [
provideZonelessChangeDetection(),
ReactiveService,
provideTanStackQuery(
new QueryClient(),
withDevtools(withDevtoolsFn, {
deps: [ReactiveService],
}),
),
],
})
TestBed.inject(ENVIRONMENT_INITIALIZER)
await vi.advanceTimersByTimeAsync(0)
const service = TestBed.inject(ReactiveService)
expect(mockTanstackQueryDevtools).not.toHaveBeenCalled()
service.enabled.set(true)
TestBed.tick()
await vi.dynamicImportSettled()
expect(mockTanstackQueryDevtools).toHaveBeenCalledTimes(1)
expect(mockTanstackQueryDevtools).toHaveBeenCalledWith(
expect.objectContaining({
position: 'bottom',
}),
)
service.position.set('top')
TestBed.tick()
expect(mockDevtoolsInstance.setPosition).toHaveBeenCalledWith('top')
})
})
})