-
-
Notifications
You must be signed in to change notification settings - Fork 3.3k
test(solid-query): use fake timers for useMutation.test.tsx #9081
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,11 +1,11 @@ | ||
import { describe, expect, it, vi } from 'vitest' | ||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' | ||
import { | ||
ErrorBoundary, | ||
createEffect, | ||
createRenderEffect, | ||
createSignal, | ||
} from 'solid-js' | ||
import { fireEvent, render, waitFor } from '@solidjs/testing-library' | ||
import { fireEvent, render } from '@solidjs/testing-library' | ||
import { MutationCache, QueryCache, QueryClientProvider, useMutation } from '..' | ||
import { | ||
createQueryClient, | ||
|
@@ -17,6 +17,14 @@ import { | |
import type { UseMutationResult } from '../types' | ||
|
||
describe('useMutation', () => { | ||
beforeEach(() => { | ||
vi.useFakeTimers() | ||
}) | ||
|
||
afterEach(() => { | ||
vi.useRealTimers() | ||
}) | ||
|
||
const queryCache = new QueryCache() | ||
const mutationCache = new MutationCache() | ||
const queryClient = createQueryClient({ queryCache, mutationCache }) | ||
|
@@ -46,15 +54,14 @@ describe('useMutation', () => { | |
|
||
fireEvent.click(rendered.getByRole('button', { name: /mutate/i })) | ||
|
||
await waitFor(() => { | ||
await vi.waitFor(() => { | ||
expect(rendered.getByRole('heading').textContent).toBe('mutation') | ||
}) | ||
|
||
fireEvent.click(rendered.getByRole('button', { name: /reset/i })) | ||
|
||
await waitFor(() => { | ||
expect(rendered.getByRole('heading').textContent).toBe('empty') | ||
}) | ||
await vi.waitFor(() => {}) | ||
expect(rendered.getByRole('heading').textContent).toBe('empty') | ||
}) | ||
|
||
it('should be able to reset `error`', async () => { | ||
|
@@ -86,21 +93,19 @@ describe('useMutation', () => { | |
</QueryClientProvider> | ||
)) | ||
|
||
await waitFor(() => { | ||
expect(rendered.queryByRole('heading')).toBeNull() | ||
}) | ||
expect(rendered.queryByRole('heading')).toBeNull() | ||
|
||
fireEvent.click(rendered.getByRole('button', { name: /mutate/i })) | ||
|
||
await waitFor(() => { | ||
await vi.waitFor(() => { | ||
expect(rendered.getByRole('heading').textContent).toBe( | ||
'Expected mock error. All is well!', | ||
) | ||
}) | ||
|
||
fireEvent.click(rendered.getByRole('button', { name: /reset/i })) | ||
|
||
await waitFor(() => { | ||
await vi.waitFor(() => { | ||
expect(rendered.queryByRole('heading')).toBeNull() | ||
}) | ||
|
||
|
@@ -150,19 +155,19 @@ describe('useMutation', () => { | |
fireEvent.click(rendered.getByRole('button', { name: /mutate/i })) | ||
fireEvent.click(rendered.getByRole('button', { name: /mutate/i })) | ||
|
||
await waitFor(() => { | ||
await vi.waitFor(() => { | ||
expect(rendered.getByRole('heading').textContent).toBe('3') | ||
}) | ||
|
||
await waitFor(() => { | ||
await vi.waitFor(() => { | ||
expect(onSuccessMock).toHaveBeenCalledTimes(3) | ||
}) | ||
|
||
expect(onSuccessMock).toHaveBeenCalledWith(1) | ||
expect(onSuccessMock).toHaveBeenCalledWith(2) | ||
expect(onSuccessMock).toHaveBeenCalledWith(3) | ||
|
||
await waitFor(() => { | ||
await vi.waitFor(() => { | ||
expect(onSettledMock).toHaveBeenCalledTimes(3) | ||
}) | ||
|
||
|
@@ -177,14 +182,11 @@ describe('useMutation', () => { | |
|
||
const mutateFn = vi.fn<(value: Value) => Promise<Value>>() | ||
|
||
mutateFn.mockImplementationOnce(() => { | ||
return Promise.reject(new Error('Error test Jonas')) | ||
}) | ||
mutateFn.mockImplementationOnce(() => | ||
Promise.reject(new Error('Error test Jonas')), | ||
) | ||
|
||
mutateFn.mockImplementation(async (value) => { | ||
await sleep(10) | ||
return Promise.resolve(value) | ||
}) | ||
mutateFn.mockImplementation((value) => sleep(10).then(() => value)) | ||
|
||
function Page() { | ||
const mutation = useMutation(() => ({ | ||
|
@@ -215,20 +217,22 @@ describe('useMutation', () => { | |
</QueryClientProvider> | ||
)) | ||
|
||
await waitFor(() => rendered.getByText('Data')) | ||
rendered.getByText('Data') | ||
|
||
fireEvent.click(rendered.getByRole('button', { name: /mutate/i })) | ||
await waitFor(() => rendered.getByText('Data')) | ||
await waitFor(() => rendered.getByText('Status error')) | ||
await waitFor(() => rendered.getByText('Failed 1 times')) | ||
await waitFor(() => rendered.getByText('Failed because Error test Jonas')) | ||
rendered.getByText('Data') | ||
await vi.waitFor(() => rendered.getByText('Status error')) | ||
await vi.waitFor(() => rendered.getByText('Failed 1 times')) | ||
await vi.waitFor(() => | ||
rendered.getByText('Failed because Error test Jonas'), | ||
) | ||
|
||
fireEvent.click(rendered.getByRole('button', { name: /mutate/i })) | ||
await waitFor(() => rendered.getByText('Status pending')) | ||
await waitFor(() => rendered.getByText('Status success')) | ||
await waitFor(() => rendered.getByText('Data 2')) | ||
await waitFor(() => rendered.getByText('Failed 0 times')) | ||
await waitFor(() => rendered.getByText('Failed because null')) | ||
await vi.waitFor(() => rendered.getByText('Status pending')) | ||
await vi.waitFor(() => rendered.getByText('Status success')) | ||
await vi.waitFor(() => rendered.getByText('Data 2')) | ||
await vi.waitFor(() => rendered.getByText('Failed 0 times')) | ||
await vi.waitFor(() => rendered.getByText('Failed because null')) | ||
}) | ||
|
||
it('should be able to call `onError` and `onSettled` after each failed mutate', async () => { | ||
|
@@ -280,11 +284,11 @@ describe('useMutation', () => { | |
fireEvent.click(rendered.getByRole('button', { name: /mutate/i })) | ||
fireEvent.click(rendered.getByRole('button', { name: /mutate/i })) | ||
|
||
await waitFor(() => { | ||
await vi.waitFor(() => { | ||
expect(rendered.getByRole('heading').textContent).toBe('3') | ||
}) | ||
|
||
await waitFor(() => { | ||
await vi.waitFor(() => { | ||
expect(onErrorMock).toHaveBeenCalledTimes(3) | ||
}) | ||
expect(onErrorMock).toHaveBeenCalledWith( | ||
|
@@ -297,7 +301,7 @@ describe('useMutation', () => { | |
'Expected mock error. All is well! 3', | ||
) | ||
|
||
await waitFor(() => { | ||
await vi.waitFor(() => { | ||
expect(onSettledMock).toHaveBeenCalledTimes(3) | ||
}) | ||
expect(onSettledMock).toHaveBeenCalledWith( | ||
|
@@ -351,7 +355,7 @@ describe('useMutation', () => { | |
</QueryClientProvider> | ||
)) | ||
|
||
await sleep(100) | ||
await vi.advanceTimersByTimeAsync(10) | ||
|
||
expect(callbacks).toEqual([ | ||
'useMutation.onSuccess', | ||
|
@@ -367,7 +371,7 @@ describe('useMutation', () => { | |
|
||
function Page() { | ||
const mutation = useMutation(() => ({ | ||
mutationFn: async (_text: string) => Promise.reject(new Error('oops')), | ||
mutationFn: (_text: string) => Promise.reject(new Error('oops')), | ||
|
||
onError: () => { | ||
callbacks.push('useMutation.onError') | ||
|
@@ -404,7 +408,7 @@ describe('useMutation', () => { | |
</QueryClientProvider> | ||
)) | ||
|
||
await sleep(100) | ||
await vi.advanceTimersByTimeAsync(10) | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Strictly but not flaky |
||
expect(callbacks).toEqual([ | ||
'useMutation.onError', | ||
|
@@ -419,10 +423,7 @@ describe('useMutation', () => { | |
const key = queryKey() | ||
|
||
queryClient.setMutationDefaults(key, { | ||
mutationFn: async (text: string) => { | ||
await sleep(10) | ||
return text | ||
}, | ||
mutationFn: (text: string) => sleep(10).then(() => text), | ||
}) | ||
|
||
const states: Array<UseMutationResult<any, any, any, any>> = [] | ||
|
@@ -452,7 +453,7 @@ describe('useMutation', () => { | |
</QueryClientProvider> | ||
)) | ||
|
||
await sleep(100) | ||
await vi.advanceTimersByTimeAsync(20) | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Strictly but not flaky |
||
expect(states.length).toBe(3) | ||
expect(states[0]).toMatchObject({ data: undefined, isPending: false }) | ||
|
@@ -489,7 +490,7 @@ describe('useMutation', () => { | |
</QueryClientProvider> | ||
)) | ||
|
||
await sleep(100) | ||
await vi.advanceTimersByTimeAsync(20) | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Strictly but not flaky |
||
expect(count).toBe(2) | ||
}) | ||
|
@@ -529,34 +530,27 @@ describe('useMutation', () => { | |
</QueryClientProvider> | ||
)) | ||
|
||
await waitFor(() => { | ||
expect( | ||
rendered.getByText('error: null, status: idle, isPaused: false'), | ||
).toBeInTheDocument() | ||
}) | ||
expect( | ||
rendered.getByText('error: null, status: idle, isPaused: false'), | ||
).toBeInTheDocument() | ||
|
||
window.dispatchEvent(new Event('offline')) | ||
|
||
fireEvent.click(rendered.getByRole('button', { name: /mutate/i })) | ||
|
||
await waitFor(() => { | ||
expect( | ||
rendered.getByText('error: null, status: pending, isPaused: true'), | ||
).toBeInTheDocument() | ||
}) | ||
expect( | ||
rendered.getByText('error: null, status: pending, isPaused: true'), | ||
).toBeInTheDocument() | ||
|
||
expect(count).toBe(0) | ||
|
||
onlineMock.mockRestore() | ||
window.dispatchEvent(new Event('online')) | ||
|
||
await sleep(100) | ||
|
||
await waitFor(() => { | ||
expect( | ||
rendered.getByText('error: oops, status: error, isPaused: false'), | ||
).toBeInTheDocument() | ||
}) | ||
await vi.advanceTimersByTimeAsync(5) | ||
expect( | ||
rendered.getByText('error: oops, status: error, isPaused: false'), | ||
).toBeInTheDocument() | ||
|
||
expect(count).toBe(2) | ||
}) | ||
|
@@ -607,6 +601,7 @@ describe('useMutation', () => { | |
onlineMock.mockRestore() | ||
window.dispatchEvent(new Event('online')) | ||
|
||
await vi.advanceTimersByTimeAsync(10) | ||
await rendered.findByText('data: 1, status: success, isPaused: false') | ||
|
||
expect(onMutate).toHaveBeenCalledTimes(1) | ||
|
@@ -661,6 +656,7 @@ describe('useMutation', () => { | |
onlineMock.mockReturnValue(true) | ||
window.dispatchEvent(new Event('online')) | ||
|
||
await vi.advanceTimersByTimeAsync(10) | ||
await rendered.findByText('data: 1, status: success, isPaused: false') | ||
|
||
onlineMock.mockRestore() | ||
|
@@ -707,7 +703,7 @@ describe('useMutation', () => { | |
</QueryClientProvider> | ||
)) | ||
|
||
await sleep(50) | ||
await vi.advanceTimersByTimeAsync(16) | ||
|
||
expect(states.length).toBe(4) | ||
expect(states[0]).toMatchObject({ | ||
|
@@ -738,7 +734,7 @@ describe('useMutation', () => { | |
onlineMock.mockRestore() | ||
window.dispatchEvent(new Event('online')) | ||
|
||
await sleep(50) | ||
await vi.advanceTimersByTimeAsync(1) | ||
|
||
expect(states.length).toBe(6) | ||
expect(states[4]).toMatchObject({ | ||
|
@@ -818,7 +814,7 @@ describe('useMutation', () => { | |
|
||
fireEvent.click(rendered.getByText('mutate')) | ||
|
||
await waitFor(() => { | ||
await vi.waitFor(() => { | ||
expect(rendered.queryByText('error')).not.toBeNull() | ||
}) | ||
|
||
|
@@ -868,13 +864,13 @@ describe('useMutation', () => { | |
|
||
// first error goes to component | ||
fireEvent.click(rendered.getByText('mutate')) | ||
await waitFor(() => { | ||
await vi.waitFor(() => { | ||
expect(rendered.queryByText('mock error')).not.toBeNull() | ||
}) | ||
|
||
// second error goes to boundary | ||
fireEvent.click(rendered.getByText('mutate')) | ||
await waitFor(() => { | ||
await vi.waitFor(() => { | ||
expect(rendered.queryByText('error boundary')).not.toBeNull() | ||
}) | ||
|
||
|
@@ -930,7 +926,7 @@ describe('useMutation', () => { | |
fireEvent.click(rendered.getByText('succeed')) | ||
fireEvent.click(rendered.getByText('error')) | ||
|
||
await waitFor(() => { | ||
await vi.waitFor(() => { | ||
expect(rendered.queryByText('successTest')).not.toBeNull() | ||
expect(rendered.queryByText('errorTest')).not.toBeNull() | ||
}) | ||
|
@@ -1003,7 +999,7 @@ describe('useMutation', () => { | |
fireEvent.click(rendered.getByRole('button', { name: /mutate/i })) | ||
fireEvent.click(rendered.getByRole('button', { name: /hide/i })) | ||
|
||
await waitFor(() => { | ||
await vi.waitFor(() => { | ||
expect( | ||
queryClient.getMutationCache().findAll({ mutationKey: mutationKey }), | ||
).toHaveLength(0) | ||
|
@@ -1065,6 +1061,7 @@ describe('useMutation', () => { | |
fireEvent.click(rendered.getByRole('button', { name: /mutate/i })) | ||
fireEvent.click(rendered.getByRole('button', { name: /mutate/i })) | ||
|
||
await vi.advanceTimersByTimeAsync(10) | ||
await rendered.findByText('data: result2, status: success') | ||
|
||
expect(count).toBe(2) | ||
|
@@ -1114,6 +1111,7 @@ describe('useMutation', () => { | |
|
||
rendered.getByRole('button', { name: /mutate/i }).click() | ||
|
||
await vi.advanceTimersByTimeAsync(10) | ||
await rendered.findByText('status: error') | ||
|
||
expect(onError).toHaveBeenCalledWith(error, 'todo', undefined) | ||
|
@@ -1154,6 +1152,7 @@ describe('useMutation', () => { | |
|
||
rendered.getByRole('button', { name: /mutate/i }).click() | ||
|
||
await vi.advanceTimersByTimeAsync(10) | ||
await rendered.findByText('error: mutateFnError, status: error') | ||
}) | ||
|
||
|
@@ -1194,6 +1193,7 @@ describe('useMutation', () => { | |
|
||
rendered.getByRole('button', { name: /mutate/i }).click() | ||
|
||
await vi.advanceTimersByTimeAsync(10) | ||
await rendered.findByText('error: mutateFnError, status: error') | ||
|
||
expect(onError).toHaveBeenCalledWith(mutateFnError, 'todo', undefined) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Strictly but not flaky