Skip to content

Commit

Permalink
feat(query): add data and errors to global hooks
Browse files Browse the repository at this point in the history
  • Loading branch information
posva committed Apr 1, 2024
1 parent 2fa206c commit b4caeca
Show file tree
Hide file tree
Showing 2 changed files with 119 additions and 13 deletions.
84 changes: 84 additions & 0 deletions src/query-plugin.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import { enableAutoUnmount, mount } from '@vue/test-utils'
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import { defineComponent } from 'vue'
import { createPinia } from 'pinia'
import { runTimers } from '../test/utils'
import { useQuery } from './use-query'
import { QueryPlugin } from './query-plugin'

describe('QueryPlugin', () => {
const MyComponent = defineComponent({
template: '<div></div>',
setup() {
return {
...useQuery({
query: async () => 42,
key: ['key'],
}),
}
},
})

beforeEach(() => {
vi.useFakeTimers()
})
afterEach(() => {
vi.restoreAllMocks()
})

enableAutoUnmount(afterEach)

it('calls the hooks on success', async () => {
const onSuccess = vi.fn()
const onSettled = vi.fn()
const onError = vi.fn()
mount(MyComponent, {
global: {
plugins: [createPinia(), [QueryPlugin, { onSuccess, onSettled, onError }]],
},
})

await runTimers()

expect(onSuccess).toHaveBeenCalledTimes(1)
expect(onSettled).toHaveBeenCalledTimes(1)
expect(onError).not.toHaveBeenCalled()
expect(onSuccess).toHaveBeenCalledWith(42)
expect(onSettled).toHaveBeenCalledWith(42, null)
})

it.todo('calls the hooks on error', async () => {
const onSuccess = vi.fn()
const onSettled = vi.fn()
const onError = vi.fn()
mount(
defineComponent({
template: '<div></div>',
setup() {
return {
...useQuery({
query: async () => {
throw new Error('oops')
},
key: ['key'],
}),
}
},
}),
{
global: {
plugins: [createPinia(), [QueryPlugin, { onSuccess, onSettled, onError }]],
},
},
)

await runTimers()

expect(onSuccess).not.toHaveBeenCalled()
expect(onSettled).toHaveBeenCalledTimes(1)
expect(onError).toHaveBeenCalledTimes(1)

expect(onError).toHaveBeenCalledWith(new Error('oops'))
expect(onSettled).toHaveBeenCalledWith(undefined, new Error('oops'))
})
})
48 changes: 35 additions & 13 deletions src/query-plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,16 @@ import {
type UseQueryOptionsWithDefaults,
} from './query-options'
import { useQueryCache } from './query-store'
import { type _Simplify, noop } from './utils'
import { noop } from './utils'
import type { _Simplify } from './utils'
import type { UseQueryReturn } from './use-query'
import type { ErrorDefault } from './types-extension'

export interface QueryPluginOptions
extends Omit<UseQueryOptions, 'key' | 'query' | 'initialData' | 'transformError'> {
extends Omit<
UseQueryOptions,
'key' | 'query' | 'initialData' | 'transformError'
> {
/**
* Executes setup code inside `useQuery()` to add custom behavior to all queries. **Must be synchronous**.
*
Expand All @@ -25,10 +29,28 @@ export interface QueryPluginOptions
>,
) => void | Promise<never>

// TODO: and others
onSuccess?: () => void
onSettled?: () => void
onError?: () => void
/**
* Global handler for when a query is successful.
*
* @param data - data returned by the query
*/
onSuccess?: (data: unknown) => unknown

/**
* Global handler for when a query is settled (either successfully or with an error). Will await for the `onSuccess`
* or `onError` handlers to resolve if they return a promise.
*
* @param data - data returned by the query if any
* @param error - error thrown if any
*/
onSettled?: (data: unknown | undefined, error: unknown | null) => unknown

/**
* Global error handler for all queries.
*
* @param error - error thrown
*/
onError?: (error: unknown) => unknown

/**
* Function to ensure the `error` property is always an instance of the default global type error. Defaults to the
Expand Down Expand Up @@ -63,15 +85,15 @@ export function QueryPlugin(

const store = useQueryCache(pinia)
store.$onAction(({ name, after, onError: _onError }) => {
// FIXME: refetch / refresh
if (name === 'refetch' || name === 'refresh') {
after(() => {
onSuccess()
onSettled()
// TODO: the refetch/refresh should probably return more information so we can query the error or data here. They don't throw errors
after(async (data) => {
await onSuccess(data)
onSettled(data, null)
})
_onError(() => {
onError()
onSettled()
_onError(async (error) => {
await onError(error)
onSettled(undefined, error)
})
}
})
Expand Down

0 comments on commit b4caeca

Please sign in to comment.