| [a762898] | 1 | import { noop } from '@internal/listenerMiddleware/utils'
|
|---|
| 2 | import { server } from '@internal/query/tests/mocks/server'
|
|---|
| 3 | import {
|
|---|
| 4 | getSerializedHeaders,
|
|---|
| 5 | setupApiStore,
|
|---|
| 6 | } from '@internal/tests/utils/helpers'
|
|---|
| 7 | import type { SerializedError } from '@reduxjs/toolkit'
|
|---|
| 8 | import { configureStore, createAction, createReducer } from '@reduxjs/toolkit'
|
|---|
| 9 | import type {
|
|---|
| 10 | DefinitionsFromApi,
|
|---|
| 11 | FetchBaseQueryError,
|
|---|
| 12 | FetchBaseQueryMeta,
|
|---|
| 13 | OverrideResultType,
|
|---|
| 14 | SchemaFailureConverter,
|
|---|
| 15 | SchemaType,
|
|---|
| 16 | SerializeQueryArgs,
|
|---|
| 17 | TagTypesFromApi,
|
|---|
| 18 | } from '@reduxjs/toolkit/query'
|
|---|
| 19 | import {
|
|---|
| 20 | createApi,
|
|---|
| 21 | fetchBaseQuery,
|
|---|
| 22 | NamedSchemaError,
|
|---|
| 23 | } from '@reduxjs/toolkit/query'
|
|---|
| 24 | import { HttpResponse, delay, http } from 'msw'
|
|---|
| 25 | import nodeFetch from 'node-fetch'
|
|---|
| 26 | import * as v from 'valibot'
|
|---|
| 27 | import type { SchemaFailureHandler } from '../endpointDefinitions'
|
|---|
| 28 |
|
|---|
| 29 | beforeAll(() => {
|
|---|
| 30 | vi.stubEnv('NODE_ENV', 'development')
|
|---|
| 31 |
|
|---|
| 32 | return vi.unstubAllEnvs
|
|---|
| 33 | })
|
|---|
| 34 |
|
|---|
| 35 | const consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation(noop)
|
|---|
| 36 |
|
|---|
| 37 | afterEach(() => {
|
|---|
| 38 | vi.clearAllMocks()
|
|---|
| 39 | server.resetHandlers()
|
|---|
| 40 | })
|
|---|
| 41 |
|
|---|
| 42 | afterAll(() => {
|
|---|
| 43 | vi.restoreAllMocks()
|
|---|
| 44 | })
|
|---|
| 45 |
|
|---|
| 46 | function paginate<T>(array: T[], page_size: number, page_number: number) {
|
|---|
| 47 | // human-readable page numbers usually start with 1, so we reduce 1 in the first argument
|
|---|
| 48 | return array.slice((page_number - 1) * page_size, page_number * page_size)
|
|---|
| 49 | }
|
|---|
| 50 |
|
|---|
| 51 | test('sensible defaults', () => {
|
|---|
| 52 | const api = createApi({
|
|---|
| 53 | baseQuery: fetchBaseQuery(),
|
|---|
| 54 | endpoints: (build) => ({
|
|---|
| 55 | getUser: build.query<unknown, void>({
|
|---|
| 56 | query(id) {
|
|---|
| 57 | return { url: `user/${id}` }
|
|---|
| 58 | },
|
|---|
| 59 | }),
|
|---|
| 60 | updateUser: build.mutation<unknown, void>({
|
|---|
| 61 | query: () => '',
|
|---|
| 62 | }),
|
|---|
| 63 | }),
|
|---|
| 64 | })
|
|---|
| 65 | configureStore({
|
|---|
| 66 | reducer: {
|
|---|
| 67 | [api.reducerPath]: api.reducer,
|
|---|
| 68 | },
|
|---|
| 69 | middleware: (gDM) => gDM().concat(api.middleware),
|
|---|
| 70 | })
|
|---|
| 71 | expect(api.reducerPath).toBe('api')
|
|---|
| 72 |
|
|---|
| 73 | expect(api.endpoints.getUser.name).toBe('getUser')
|
|---|
| 74 | expect(api.endpoints.updateUser.name).toBe('updateUser')
|
|---|
| 75 | })
|
|---|
| 76 |
|
|---|
| 77 | describe('wrong tagTypes log errors', () => {
|
|---|
| 78 | const baseQuery = vi.fn()
|
|---|
| 79 | const api = createApi({
|
|---|
| 80 | baseQuery,
|
|---|
| 81 | tagTypes: ['User'],
|
|---|
| 82 | endpoints: (build) => ({
|
|---|
| 83 | provideNothing: build.query<unknown, void>({
|
|---|
| 84 | query: () => '',
|
|---|
| 85 | }),
|
|---|
| 86 | provideTypeString: build.query<unknown, void>({
|
|---|
| 87 | query: () => '',
|
|---|
| 88 | providesTags: ['User'],
|
|---|
| 89 | }),
|
|---|
| 90 | provideTypeWithId: build.query<unknown, void>({
|
|---|
| 91 | query: () => '',
|
|---|
| 92 | providesTags: [{ type: 'User', id: 5 }],
|
|---|
| 93 | }),
|
|---|
| 94 | provideTypeWithIdAndCallback: build.query<unknown, void>({
|
|---|
| 95 | query: () => '',
|
|---|
| 96 | providesTags: () => [{ type: 'User', id: 5 }],
|
|---|
| 97 | }),
|
|---|
| 98 | provideWrongTypeString: build.query<unknown, void>({
|
|---|
| 99 | query: () => '',
|
|---|
| 100 | // @ts-expect-error
|
|---|
| 101 | providesTags: ['Users'],
|
|---|
| 102 | }),
|
|---|
| 103 | provideWrongTypeWithId: build.query<unknown, void>({
|
|---|
| 104 | query: () => '',
|
|---|
| 105 | // @ts-expect-error
|
|---|
| 106 | providesTags: [{ type: 'Users', id: 5 }],
|
|---|
| 107 | }),
|
|---|
| 108 | provideWrongTypeWithIdAndCallback: build.query<unknown, void>({
|
|---|
| 109 | query: () => '',
|
|---|
| 110 | // @ts-expect-error
|
|---|
| 111 | providesTags: () => [{ type: 'Users', id: 5 }],
|
|---|
| 112 | }),
|
|---|
| 113 | invalidateNothing: build.query<unknown, void>({
|
|---|
| 114 | query: () => '',
|
|---|
| 115 | }),
|
|---|
| 116 | invalidateTypeString: build.mutation<unknown, void>({
|
|---|
| 117 | query: () => '',
|
|---|
| 118 | invalidatesTags: ['User'],
|
|---|
| 119 | }),
|
|---|
| 120 | invalidateTypeWithId: build.mutation<unknown, void>({
|
|---|
| 121 | query: () => '',
|
|---|
| 122 | invalidatesTags: [{ type: 'User', id: 5 }],
|
|---|
| 123 | }),
|
|---|
| 124 | invalidateTypeWithIdAndCallback: build.mutation<unknown, void>({
|
|---|
| 125 | query: () => '',
|
|---|
| 126 | invalidatesTags: () => [{ type: 'User', id: 5 }],
|
|---|
| 127 | }),
|
|---|
| 128 |
|
|---|
| 129 | invalidateWrongTypeString: build.mutation<unknown, void>({
|
|---|
| 130 | query: () => '',
|
|---|
| 131 | // @ts-expect-error
|
|---|
| 132 | invalidatesTags: ['Users'],
|
|---|
| 133 | }),
|
|---|
| 134 | invalidateWrongTypeWithId: build.mutation<unknown, void>({
|
|---|
| 135 | query: () => '',
|
|---|
| 136 | // @ts-expect-error
|
|---|
| 137 | invalidatesTags: [{ type: 'Users', id: 5 }],
|
|---|
| 138 | }),
|
|---|
| 139 | invalidateWrongTypeWithIdAndCallback: build.mutation<unknown, void>({
|
|---|
| 140 | query: () => '',
|
|---|
| 141 | // @ts-expect-error
|
|---|
| 142 | invalidatesTags: () => [{ type: 'Users', id: 5 }],
|
|---|
| 143 | }),
|
|---|
| 144 | }),
|
|---|
| 145 | })
|
|---|
| 146 | const store = configureStore({
|
|---|
| 147 | reducer: {
|
|---|
| 148 | [api.reducerPath]: api.reducer,
|
|---|
| 149 | },
|
|---|
| 150 | middleware: (gDM) => gDM().concat(api.middleware),
|
|---|
| 151 | })
|
|---|
| 152 |
|
|---|
| 153 | beforeEach(() => {
|
|---|
| 154 | baseQuery.mockResolvedValue({ data: 'foo' })
|
|---|
| 155 | })
|
|---|
| 156 |
|
|---|
| 157 | test.each<[keyof typeof api.endpoints, boolean?]>([
|
|---|
| 158 | ['provideNothing', false],
|
|---|
| 159 | ['provideTypeString', false],
|
|---|
| 160 | ['provideTypeWithId', false],
|
|---|
| 161 | ['provideTypeWithIdAndCallback', false],
|
|---|
| 162 | ['provideWrongTypeString', true],
|
|---|
| 163 | ['provideWrongTypeWithId', true],
|
|---|
| 164 | ['provideWrongTypeWithIdAndCallback', true],
|
|---|
| 165 | ['invalidateNothing', false],
|
|---|
| 166 | ['invalidateTypeString', false],
|
|---|
| 167 | ['invalidateTypeWithId', false],
|
|---|
| 168 | ['invalidateTypeWithIdAndCallback', false],
|
|---|
| 169 | ['invalidateWrongTypeString', true],
|
|---|
| 170 | ['invalidateWrongTypeWithId', true],
|
|---|
| 171 | ['invalidateWrongTypeWithIdAndCallback', true],
|
|---|
| 172 | ])(`endpoint %s should log an error? %s`, async (endpoint, shouldError) => {
|
|---|
| 173 | vi.stubEnv('NODE_ENV', 'development')
|
|---|
| 174 |
|
|---|
| 175 | // @ts-ignore
|
|---|
| 176 | store.dispatch(api.endpoints[endpoint].initiate())
|
|---|
| 177 | let result: { status: string }
|
|---|
| 178 | do {
|
|---|
| 179 | await delay(5)
|
|---|
| 180 | // @ts-ignore
|
|---|
| 181 | result = api.endpoints[endpoint].select()(store.getState())
|
|---|
| 182 | } while (result.status === 'pending')
|
|---|
| 183 |
|
|---|
| 184 | if (shouldError) {
|
|---|
| 185 | expect(consoleErrorSpy).toHaveBeenLastCalledWith(
|
|---|
| 186 | "Tag type 'Users' was used, but not specified in `tagTypes`!",
|
|---|
| 187 | )
|
|---|
| 188 | } else {
|
|---|
| 189 | expect(consoleErrorSpy).not.toHaveBeenCalled()
|
|---|
| 190 | }
|
|---|
| 191 | })
|
|---|
| 192 | })
|
|---|
| 193 |
|
|---|
| 194 | describe('endpoint definition typings', () => {
|
|---|
| 195 | const api = createApi({
|
|---|
| 196 | baseQuery: (from: 'From'): { data: 'To' } | Promise<{ data: 'To' }> => ({
|
|---|
| 197 | data: 'To',
|
|---|
| 198 | }),
|
|---|
| 199 | endpoints: () => ({}),
|
|---|
| 200 | tagTypes: ['typeA', 'typeB'],
|
|---|
| 201 | })
|
|---|
| 202 | test('query: query & transformResponse types', () => {
|
|---|
| 203 | api.injectEndpoints({
|
|---|
| 204 | endpoints: (build) => ({
|
|---|
| 205 | query: build.query<'RetVal', 'Arg'>({
|
|---|
| 206 | query: (x: 'Arg') => 'From' as const,
|
|---|
| 207 | transformResponse(r: 'To') {
|
|---|
| 208 | return 'RetVal' as const
|
|---|
| 209 | },
|
|---|
| 210 | }),
|
|---|
| 211 | query1: build.query<'RetVal', 'Arg'>({
|
|---|
| 212 | // @ts-expect-error
|
|---|
| 213 | query: (x: 'Error') => 'From' as const,
|
|---|
| 214 | transformResponse(r: 'To') {
|
|---|
| 215 | return 'RetVal' as const
|
|---|
| 216 | },
|
|---|
| 217 | }),
|
|---|
| 218 | query2: build.query<'RetVal', 'Arg'>({
|
|---|
| 219 | // @ts-expect-error
|
|---|
| 220 | query: (x: 'Arg') => 'Error' as const,
|
|---|
| 221 | transformResponse(r: 'To') {
|
|---|
| 222 | return 'RetVal' as const
|
|---|
| 223 | },
|
|---|
| 224 | }),
|
|---|
| 225 | query3: build.query<'RetVal', 'Arg'>({
|
|---|
| 226 | query: (x: 'Arg') => 'From' as const,
|
|---|
| 227 | // @ts-expect-error
|
|---|
| 228 | transformResponse(r: 'Error') {
|
|---|
| 229 | return 'RetVal' as const
|
|---|
| 230 | },
|
|---|
| 231 | }),
|
|---|
| 232 | query4: build.query<'RetVal', 'Arg'>({
|
|---|
| 233 | query: (x: 'Arg') => 'From' as const,
|
|---|
| 234 | // @ts-expect-error
|
|---|
| 235 | transformResponse(r: 'To') {
|
|---|
| 236 | return 'Error' as const
|
|---|
| 237 | },
|
|---|
| 238 | }),
|
|---|
| 239 | queryInference1: build.query<'RetVal', 'Arg'>({
|
|---|
| 240 | query: (x) => {
|
|---|
| 241 | return 'From'
|
|---|
| 242 | },
|
|---|
| 243 | transformResponse(r) {
|
|---|
| 244 | return 'RetVal'
|
|---|
| 245 | },
|
|---|
| 246 | }),
|
|---|
| 247 | queryInference2: (() => {
|
|---|
| 248 | const query = build.query({
|
|---|
| 249 | query: (x: 'Arg') => 'From' as const,
|
|---|
| 250 | transformResponse(r: 'To') {
|
|---|
| 251 | return 'RetVal' as const
|
|---|
| 252 | },
|
|---|
| 253 | })
|
|---|
| 254 | return query
|
|---|
| 255 | })(),
|
|---|
| 256 | }),
|
|---|
| 257 | })
|
|---|
| 258 | })
|
|---|
| 259 | test('mutation: query & transformResponse types', () => {
|
|---|
| 260 | api.injectEndpoints({
|
|---|
| 261 | endpoints: (build) => ({
|
|---|
| 262 | query: build.mutation<'RetVal', 'Arg'>({
|
|---|
| 263 | query: (x: 'Arg') => 'From' as const,
|
|---|
| 264 | transformResponse(r: 'To') {
|
|---|
| 265 | return 'RetVal' as const
|
|---|
| 266 | },
|
|---|
| 267 | }),
|
|---|
| 268 | query1: build.mutation<'RetVal', 'Arg'>({
|
|---|
| 269 | // @ts-expect-error
|
|---|
| 270 | query: (x: 'Error') => 'From' as const,
|
|---|
| 271 | transformResponse(r: 'To') {
|
|---|
| 272 | return 'RetVal' as const
|
|---|
| 273 | },
|
|---|
| 274 | }),
|
|---|
| 275 | query2: build.mutation<'RetVal', 'Arg'>({
|
|---|
| 276 | // @ts-expect-error
|
|---|
| 277 | query: (x: 'Arg') => 'Error' as const,
|
|---|
| 278 | transformResponse(r: 'To') {
|
|---|
| 279 | return 'RetVal' as const
|
|---|
| 280 | },
|
|---|
| 281 | }),
|
|---|
| 282 | query3: build.mutation<'RetVal', 'Arg'>({
|
|---|
| 283 | query: (x: 'Arg') => 'From' as const,
|
|---|
| 284 | // @ts-expect-error
|
|---|
| 285 | transformResponse(r: 'Error') {
|
|---|
| 286 | return 'RetVal' as const
|
|---|
| 287 | },
|
|---|
| 288 | }),
|
|---|
| 289 | query4: build.mutation<'RetVal', 'Arg'>({
|
|---|
| 290 | query: (x: 'Arg') => 'From' as const,
|
|---|
| 291 | // @ts-expect-error
|
|---|
| 292 | transformResponse(r: 'To') {
|
|---|
| 293 | return 'Error' as const
|
|---|
| 294 | },
|
|---|
| 295 | }),
|
|---|
| 296 | mutationInference1: build.mutation<'RetVal', 'Arg'>({
|
|---|
| 297 | query: (x) => {
|
|---|
| 298 | return 'From'
|
|---|
| 299 | },
|
|---|
| 300 | transformResponse(r) {
|
|---|
| 301 | return 'RetVal'
|
|---|
| 302 | },
|
|---|
| 303 | }),
|
|---|
| 304 | mutationInference2: (() => {
|
|---|
| 305 | const query = build.mutation({
|
|---|
| 306 | query: (x: 'Arg') => 'From' as const,
|
|---|
| 307 | transformResponse(r: 'To') {
|
|---|
| 308 | return 'RetVal' as const
|
|---|
| 309 | },
|
|---|
| 310 | })
|
|---|
| 311 | return query
|
|---|
| 312 | })(),
|
|---|
| 313 | }),
|
|---|
| 314 | })
|
|---|
| 315 | })
|
|---|
| 316 |
|
|---|
| 317 | describe('enhancing endpoint definitions', () => {
|
|---|
| 318 | const baseQuery = vi.fn((x: string) => ({ data: 'success' }))
|
|---|
| 319 | const commonBaseQueryApi = {
|
|---|
| 320 | dispatch: expect.any(Function),
|
|---|
| 321 | endpoint: expect.any(String),
|
|---|
| 322 | abort: expect.any(Function),
|
|---|
| 323 | extra: undefined,
|
|---|
| 324 | forced: expect.any(Boolean),
|
|---|
| 325 | getState: expect.any(Function),
|
|---|
| 326 | signal: expect.any(Object),
|
|---|
| 327 | type: expect.any(String),
|
|---|
| 328 | queryCacheKey: expect.any(String),
|
|---|
| 329 | }
|
|---|
| 330 | beforeEach(() => {
|
|---|
| 331 | baseQuery.mockClear()
|
|---|
| 332 | })
|
|---|
| 333 | function getNewApi() {
|
|---|
| 334 | return createApi({
|
|---|
| 335 | baseQuery,
|
|---|
| 336 | tagTypes: ['old'],
|
|---|
| 337 | endpoints: (build) => ({
|
|---|
| 338 | query1: build.query<'out1', 'in1'>({ query: (id) => `${id}` }),
|
|---|
| 339 | query2: build.query<'out2', 'in2'>({ query: (id) => `${id}` }),
|
|---|
| 340 | mutation1: build.mutation<'out1', 'in1'>({ query: (id) => `${id}` }),
|
|---|
| 341 | mutation2: build.mutation<'out2', 'in2'>({ query: (id) => `${id}` }),
|
|---|
| 342 | }),
|
|---|
| 343 | })
|
|---|
| 344 | }
|
|---|
| 345 | let api = getNewApi()
|
|---|
| 346 | beforeEach(() => {
|
|---|
| 347 | api = getNewApi()
|
|---|
| 348 | })
|
|---|
| 349 |
|
|---|
| 350 | test('pre-modification behavior', async () => {
|
|---|
| 351 | const storeRef = setupApiStore(api, undefined, {
|
|---|
| 352 | withoutTestLifecycles: true,
|
|---|
| 353 | })
|
|---|
| 354 | storeRef.store.dispatch(api.endpoints.query1.initiate('in1'))
|
|---|
| 355 | storeRef.store.dispatch(api.endpoints.query2.initiate('in2'))
|
|---|
| 356 | storeRef.store.dispatch(api.endpoints.mutation1.initiate('in1'))
|
|---|
| 357 | storeRef.store.dispatch(api.endpoints.mutation2.initiate('in2'))
|
|---|
| 358 |
|
|---|
| 359 | expect(baseQuery.mock.calls).toEqual([
|
|---|
| 360 | [
|
|---|
| 361 | 'in1',
|
|---|
| 362 | {
|
|---|
| 363 | dispatch: expect.any(Function),
|
|---|
| 364 | endpoint: expect.any(String),
|
|---|
| 365 | getState: expect.any(Function),
|
|---|
| 366 | signal: expect.any(Object),
|
|---|
| 367 | abort: expect.any(Function),
|
|---|
| 368 | forced: expect.any(Boolean),
|
|---|
| 369 | type: expect.any(String),
|
|---|
| 370 | queryCacheKey: expect.any(String),
|
|---|
| 371 | },
|
|---|
| 372 | undefined,
|
|---|
| 373 | ],
|
|---|
| 374 | [
|
|---|
| 375 | 'in2',
|
|---|
| 376 | {
|
|---|
| 377 | dispatch: expect.any(Function),
|
|---|
| 378 | endpoint: expect.any(String),
|
|---|
| 379 | getState: expect.any(Function),
|
|---|
| 380 | signal: expect.any(Object),
|
|---|
| 381 | abort: expect.any(Function),
|
|---|
| 382 | forced: expect.any(Boolean),
|
|---|
| 383 | type: expect.any(String),
|
|---|
| 384 | queryCacheKey: expect.any(String),
|
|---|
| 385 | },
|
|---|
| 386 | undefined,
|
|---|
| 387 | ],
|
|---|
| 388 | [
|
|---|
| 389 | 'in1',
|
|---|
| 390 | {
|
|---|
| 391 | dispatch: expect.any(Function),
|
|---|
| 392 | endpoint: expect.any(String),
|
|---|
| 393 | getState: expect.any(Function),
|
|---|
| 394 | signal: expect.any(Object),
|
|---|
| 395 | abort: expect.any(Function),
|
|---|
| 396 | // forced: undefined,
|
|---|
| 397 | type: expect.any(String),
|
|---|
| 398 | },
|
|---|
| 399 | undefined,
|
|---|
| 400 | ],
|
|---|
| 401 | [
|
|---|
| 402 | 'in2',
|
|---|
| 403 | {
|
|---|
| 404 | dispatch: expect.any(Function),
|
|---|
| 405 | endpoint: expect.any(String),
|
|---|
| 406 | getState: expect.any(Function),
|
|---|
| 407 | signal: expect.any(Object),
|
|---|
| 408 | abort: expect.any(Function),
|
|---|
| 409 | // forced: undefined,
|
|---|
| 410 | type: expect.any(String),
|
|---|
| 411 | },
|
|---|
| 412 | undefined,
|
|---|
| 413 | ],
|
|---|
| 414 | ])
|
|---|
| 415 | })
|
|---|
| 416 |
|
|---|
| 417 | test('warn on wrong tagType', async () => {
|
|---|
| 418 | vi.stubEnv('NODE_ENV', 'development')
|
|---|
| 419 |
|
|---|
| 420 | const storeRef = setupApiStore(api, undefined, {
|
|---|
| 421 | withoutTestLifecycles: true,
|
|---|
| 422 | })
|
|---|
| 423 | // only type-test this part
|
|---|
| 424 | if (2 > 1) {
|
|---|
| 425 | api.enhanceEndpoints({
|
|---|
| 426 | endpoints: {
|
|---|
| 427 | query1: {
|
|---|
| 428 | // @ts-expect-error
|
|---|
| 429 | providesTags: ['new'],
|
|---|
| 430 | },
|
|---|
| 431 | query2: {
|
|---|
| 432 | // @ts-expect-error
|
|---|
| 433 | providesTags: ['missing'],
|
|---|
| 434 | },
|
|---|
| 435 | },
|
|---|
| 436 | })
|
|---|
| 437 | }
|
|---|
| 438 |
|
|---|
| 439 | const enhanced = api.enhanceEndpoints({
|
|---|
| 440 | addTagTypes: ['new'],
|
|---|
| 441 | endpoints: {
|
|---|
| 442 | query1: {
|
|---|
| 443 | providesTags: ['new'],
|
|---|
| 444 | },
|
|---|
| 445 | query2: {
|
|---|
| 446 | // @ts-expect-error
|
|---|
| 447 | providesTags: ['missing'],
|
|---|
| 448 | },
|
|---|
| 449 | },
|
|---|
| 450 | })
|
|---|
| 451 |
|
|---|
| 452 | storeRef.store.dispatch(api.endpoints.query1.initiate('in1'))
|
|---|
| 453 | await delay(1)
|
|---|
| 454 | expect(consoleErrorSpy).not.toHaveBeenCalled()
|
|---|
| 455 |
|
|---|
| 456 | storeRef.store.dispatch(api.endpoints.query2.initiate('in2'))
|
|---|
| 457 | await delay(1)
|
|---|
| 458 |
|
|---|
| 459 | expect(consoleErrorSpy).toHaveBeenCalledOnce()
|
|---|
| 460 |
|
|---|
| 461 | expect(consoleErrorSpy).toHaveBeenLastCalledWith(
|
|---|
| 462 | "Tag type 'missing' was used, but not specified in `tagTypes`!",
|
|---|
| 463 | )
|
|---|
| 464 |
|
|---|
| 465 | // only type-test this part
|
|---|
| 466 | if (2 > 1) {
|
|---|
| 467 | enhanced.enhanceEndpoints({
|
|---|
| 468 | endpoints: {
|
|---|
| 469 | query1: {
|
|---|
| 470 | // returned `enhanced` api contains "new" enitityType
|
|---|
| 471 | providesTags: ['new'],
|
|---|
| 472 | },
|
|---|
| 473 | query2: {
|
|---|
| 474 | // @ts-expect-error
|
|---|
| 475 | providesTags: ['missing'],
|
|---|
| 476 | },
|
|---|
| 477 | },
|
|---|
| 478 | })
|
|---|
| 479 | }
|
|---|
| 480 | })
|
|---|
| 481 |
|
|---|
| 482 | test('modify', () => {
|
|---|
| 483 | const storeRef = setupApiStore(api, undefined, {
|
|---|
| 484 | withoutTestLifecycles: true,
|
|---|
| 485 | })
|
|---|
| 486 | api.enhanceEndpoints({
|
|---|
| 487 | endpoints: {
|
|---|
| 488 | query1: {
|
|---|
| 489 | query: (x) => {
|
|---|
| 490 | return 'modified1'
|
|---|
| 491 | },
|
|---|
| 492 | },
|
|---|
| 493 | query2(definition) {
|
|---|
| 494 | definition.query = (x) => {
|
|---|
| 495 | return 'modified2'
|
|---|
| 496 | }
|
|---|
| 497 | },
|
|---|
| 498 | mutation1: {
|
|---|
| 499 | query: (x) => {
|
|---|
| 500 | return 'modified1'
|
|---|
| 501 | },
|
|---|
| 502 | },
|
|---|
| 503 | mutation2(definition) {
|
|---|
| 504 | definition.query = (x) => {
|
|---|
| 505 | return 'modified2'
|
|---|
| 506 | }
|
|---|
| 507 | },
|
|---|
| 508 | // @ts-expect-error
|
|---|
| 509 | nonExisting: {},
|
|---|
| 510 | },
|
|---|
| 511 | })
|
|---|
| 512 |
|
|---|
| 513 | storeRef.store.dispatch(api.endpoints.query1.initiate('in1'))
|
|---|
| 514 | storeRef.store.dispatch(api.endpoints.query2.initiate('in2'))
|
|---|
| 515 | storeRef.store.dispatch(api.endpoints.mutation1.initiate('in1'))
|
|---|
| 516 | storeRef.store.dispatch(api.endpoints.mutation2.initiate('in2'))
|
|---|
| 517 |
|
|---|
| 518 | expect(baseQuery.mock.calls).toEqual([
|
|---|
| 519 | ['modified1', commonBaseQueryApi, undefined],
|
|---|
| 520 | ['modified2', commonBaseQueryApi, undefined],
|
|---|
| 521 | [
|
|---|
| 522 | 'modified1',
|
|---|
| 523 | {
|
|---|
| 524 | ...commonBaseQueryApi,
|
|---|
| 525 | forced: undefined,
|
|---|
| 526 | queryCacheKey: undefined,
|
|---|
| 527 | },
|
|---|
| 528 | undefined,
|
|---|
| 529 | ],
|
|---|
| 530 | [
|
|---|
| 531 | 'modified2',
|
|---|
| 532 | {
|
|---|
| 533 | ...commonBaseQueryApi,
|
|---|
| 534 | forced: undefined,
|
|---|
| 535 | queryCacheKey: undefined,
|
|---|
| 536 | },
|
|---|
| 537 | undefined,
|
|---|
| 538 | ],
|
|---|
| 539 | ])
|
|---|
| 540 | })
|
|---|
| 541 |
|
|---|
| 542 | test('updated transform response types', async () => {
|
|---|
| 543 | const baseApi = createApi({
|
|---|
| 544 | baseQuery: fetchBaseQuery({ baseUrl: 'https://example.com' }),
|
|---|
| 545 | tagTypes: ['old'],
|
|---|
| 546 | endpoints: (build) => ({
|
|---|
| 547 | query1: build.query<'out1', void>({ query: () => 'success' }),
|
|---|
| 548 | mutation1: build.mutation<'out1', void>({ query: () => 'success' }),
|
|---|
| 549 | }),
|
|---|
| 550 | })
|
|---|
| 551 |
|
|---|
| 552 | type Transformed = { value: string }
|
|---|
| 553 |
|
|---|
| 554 | type Definitions = DefinitionsFromApi<typeof api>
|
|---|
| 555 | type TagTypes = TagTypesFromApi<typeof api>
|
|---|
| 556 |
|
|---|
| 557 | type Q1Definition = OverrideResultType<Definitions['query1'], Transformed>
|
|---|
| 558 | type M1Definition = OverrideResultType<
|
|---|
| 559 | Definitions['mutation1'],
|
|---|
| 560 | Transformed
|
|---|
| 561 | >
|
|---|
| 562 |
|
|---|
| 563 | type UpdatedDefitions = Omit<Definitions, 'query1' | 'mutation1'> & {
|
|---|
| 564 | query1: Q1Definition
|
|---|
| 565 | mutation1: M1Definition
|
|---|
| 566 | }
|
|---|
| 567 |
|
|---|
| 568 | const enhancedApi = baseApi.enhanceEndpoints<TagTypes, UpdatedDefitions>({
|
|---|
| 569 | endpoints: {
|
|---|
| 570 | query1: {
|
|---|
| 571 | transformResponse: (a, b, c) => ({
|
|---|
| 572 | value: 'transformed',
|
|---|
| 573 | }),
|
|---|
| 574 | },
|
|---|
| 575 | mutation1: {
|
|---|
| 576 | transformResponse: (a, b, c) => ({
|
|---|
| 577 | value: 'transformed',
|
|---|
| 578 | }),
|
|---|
| 579 | },
|
|---|
| 580 | },
|
|---|
| 581 | })
|
|---|
| 582 |
|
|---|
| 583 | const storeRef = setupApiStore(enhancedApi, undefined, {
|
|---|
| 584 | withoutTestLifecycles: true,
|
|---|
| 585 | })
|
|---|
| 586 |
|
|---|
| 587 | const queryResponse = await storeRef.store.dispatch(
|
|---|
| 588 | enhancedApi.endpoints.query1.initiate(),
|
|---|
| 589 | )
|
|---|
| 590 | expect(queryResponse.data).toEqual({ value: 'transformed' })
|
|---|
| 591 |
|
|---|
| 592 | const mutationResponse = await storeRef.store.dispatch(
|
|---|
| 593 | enhancedApi.endpoints.mutation1.initiate(),
|
|---|
| 594 | )
|
|---|
| 595 | expect('data' in mutationResponse && mutationResponse.data).toEqual({
|
|---|
| 596 | value: 'transformed',
|
|---|
| 597 | })
|
|---|
| 598 | })
|
|---|
| 599 | })
|
|---|
| 600 | })
|
|---|
| 601 |
|
|---|
| 602 | describe('additional transformResponse behaviors', () => {
|
|---|
| 603 | type SuccessResponse = { value: 'success' }
|
|---|
| 604 | type EchoResponseData = { banana: 'bread' }
|
|---|
| 605 | type ErrorResponse = { value: 'error' }
|
|---|
| 606 | const api = createApi({
|
|---|
| 607 | baseQuery: fetchBaseQuery({ baseUrl: 'https://example.com' }),
|
|---|
| 608 | endpoints: (build) => ({
|
|---|
| 609 | echo: build.mutation({
|
|---|
| 610 | query: () => ({ method: 'PUT', url: '/echo' }),
|
|---|
| 611 | }),
|
|---|
| 612 | mutation: build.mutation({
|
|---|
| 613 | query: () => ({
|
|---|
| 614 | url: '/echo',
|
|---|
| 615 | method: 'POST',
|
|---|
| 616 | body: { nested: { banana: 'bread' } },
|
|---|
| 617 | }),
|
|---|
| 618 | transformResponse: (response: { body: { nested: EchoResponseData } }) =>
|
|---|
| 619 | response.body.nested,
|
|---|
| 620 | }),
|
|---|
| 621 | mutationWithError: build.mutation({
|
|---|
| 622 | query: () => ({
|
|---|
| 623 | url: '/error',
|
|---|
| 624 | method: 'POST',
|
|---|
| 625 | }),
|
|---|
| 626 | transformErrorResponse: (response) => {
|
|---|
| 627 | const data = response.data as ErrorResponse
|
|---|
| 628 | return data.value
|
|---|
| 629 | },
|
|---|
| 630 | }),
|
|---|
| 631 | mutationWithMeta: build.mutation({
|
|---|
| 632 | query: () => ({
|
|---|
| 633 | url: '/echo',
|
|---|
| 634 | method: 'POST',
|
|---|
| 635 | body: { nested: { banana: 'bread' } },
|
|---|
| 636 | }),
|
|---|
| 637 | transformResponse: (
|
|---|
| 638 | response: { body: { nested: EchoResponseData } },
|
|---|
| 639 | meta,
|
|---|
| 640 | ) => {
|
|---|
| 641 | return {
|
|---|
| 642 | ...response.body.nested,
|
|---|
| 643 | meta: {
|
|---|
| 644 | request: { headers: getSerializedHeaders(meta?.request.headers) },
|
|---|
| 645 | response: {
|
|---|
| 646 | headers: getSerializedHeaders(meta?.response?.headers),
|
|---|
| 647 | },
|
|---|
| 648 | },
|
|---|
| 649 | }
|
|---|
| 650 | },
|
|---|
| 651 | }),
|
|---|
| 652 | query: build.query<SuccessResponse & EchoResponseData, void>({
|
|---|
| 653 | query: () => '/success',
|
|---|
| 654 | transformResponse: async (response: SuccessResponse) => {
|
|---|
| 655 | const res: any = await nodeFetch('https://example.com/echo', {
|
|---|
| 656 | method: 'POST',
|
|---|
| 657 | body: JSON.stringify({ banana: 'bread' }),
|
|---|
| 658 | }).then((res) => res.json())
|
|---|
| 659 |
|
|---|
| 660 | const additionalData = res.body as EchoResponseData
|
|---|
| 661 | return { ...response, ...additionalData }
|
|---|
| 662 | },
|
|---|
| 663 | }),
|
|---|
| 664 | queryWithMeta: build.query<SuccessResponse, void>({
|
|---|
| 665 | query: () => '/success',
|
|---|
| 666 | transformResponse: async (response: SuccessResponse, meta) => {
|
|---|
| 667 | return {
|
|---|
| 668 | ...response,
|
|---|
| 669 | meta: {
|
|---|
| 670 | request: { headers: getSerializedHeaders(meta?.request.headers) },
|
|---|
| 671 | response: {
|
|---|
| 672 | headers: getSerializedHeaders(meta?.response?.headers),
|
|---|
| 673 | },
|
|---|
| 674 | },
|
|---|
| 675 | }
|
|---|
| 676 | },
|
|---|
| 677 | }),
|
|---|
| 678 | }),
|
|---|
| 679 | })
|
|---|
| 680 |
|
|---|
| 681 | const storeRef = setupApiStore(api)
|
|---|
| 682 |
|
|---|
| 683 | test('transformResponse handles an async transformation and returns the merged data (query)', async () => {
|
|---|
| 684 | const result = await storeRef.store.dispatch(api.endpoints.query.initiate())
|
|---|
| 685 |
|
|---|
| 686 | expect(result.data).toEqual({ value: 'success', banana: 'bread' })
|
|---|
| 687 | })
|
|---|
| 688 |
|
|---|
| 689 | test('transformResponse transforms a response from a mutation', async () => {
|
|---|
| 690 | const result = await storeRef.store.dispatch(
|
|---|
| 691 | api.endpoints.mutation.initiate({}),
|
|---|
| 692 | )
|
|---|
| 693 |
|
|---|
| 694 | expect('data' in result && result.data).toEqual({ banana: 'bread' })
|
|---|
| 695 | })
|
|---|
| 696 |
|
|---|
| 697 | test('transformResponse transforms a response from a mutation with an error', async () => {
|
|---|
| 698 | const result = await storeRef.store.dispatch(
|
|---|
| 699 | api.endpoints.mutationWithError.initiate({}),
|
|---|
| 700 | )
|
|---|
| 701 |
|
|---|
| 702 | expect('error' in result && result.error).toEqual('error')
|
|---|
| 703 | })
|
|---|
| 704 |
|
|---|
| 705 | test('transformResponse can inject baseQuery meta into the end result from a mutation', async () => {
|
|---|
| 706 | const result = await storeRef.store.dispatch(
|
|---|
| 707 | api.endpoints.mutationWithMeta.initiate({}),
|
|---|
| 708 | )
|
|---|
| 709 |
|
|---|
| 710 | expect('data' in result && result.data).toEqual({
|
|---|
| 711 | banana: 'bread',
|
|---|
| 712 | meta: {
|
|---|
| 713 | request: {
|
|---|
| 714 | headers: {
|
|---|
| 715 | accept: 'application/json',
|
|---|
| 716 | 'content-type': 'application/json',
|
|---|
| 717 | },
|
|---|
| 718 | },
|
|---|
| 719 | response: {
|
|---|
| 720 | headers: {
|
|---|
| 721 | 'content-type': 'application/json',
|
|---|
| 722 | },
|
|---|
| 723 | },
|
|---|
| 724 | },
|
|---|
| 725 | })
|
|---|
| 726 | })
|
|---|
| 727 |
|
|---|
| 728 | test('transformResponse can inject baseQuery meta into the end result from a query', async () => {
|
|---|
| 729 | const result = await storeRef.store.dispatch(
|
|---|
| 730 | api.endpoints.queryWithMeta.initiate(),
|
|---|
| 731 | )
|
|---|
| 732 |
|
|---|
| 733 | expect(result.data).toEqual({
|
|---|
| 734 | value: 'success',
|
|---|
| 735 | meta: {
|
|---|
| 736 | request: {
|
|---|
| 737 | headers: {
|
|---|
| 738 | accept: 'application/json',
|
|---|
| 739 | },
|
|---|
| 740 | },
|
|---|
| 741 | response: {
|
|---|
| 742 | headers: {
|
|---|
| 743 | 'content-type': 'application/json',
|
|---|
| 744 | },
|
|---|
| 745 | },
|
|---|
| 746 | },
|
|---|
| 747 | })
|
|---|
| 748 | })
|
|---|
| 749 | })
|
|---|
| 750 |
|
|---|
| 751 | describe('query endpoint lifecycles - onStart, onSuccess, onError', () => {
|
|---|
| 752 | const initialState = {
|
|---|
| 753 | count: null as null | number,
|
|---|
| 754 | }
|
|---|
| 755 | const setCount = createAction<number>('setCount')
|
|---|
| 756 | const testReducer = createReducer(initialState, (builder) => {
|
|---|
| 757 | builder.addCase(setCount, (state, action) => {
|
|---|
| 758 | state.count = action.payload
|
|---|
| 759 | })
|
|---|
| 760 | })
|
|---|
| 761 |
|
|---|
| 762 | type SuccessResponse = { value: 'success' }
|
|---|
| 763 | const api = createApi({
|
|---|
| 764 | baseQuery: fetchBaseQuery({ baseUrl: 'https://example.com' }),
|
|---|
| 765 | endpoints: (build) => ({
|
|---|
| 766 | echo: build.mutation({
|
|---|
| 767 | query: () => ({ method: 'PUT', url: '/echo' }),
|
|---|
| 768 | }),
|
|---|
| 769 | query: build.query<SuccessResponse, void>({
|
|---|
| 770 | query: () => '/success',
|
|---|
| 771 | async onQueryStarted(_, api) {
|
|---|
| 772 | api.dispatch(setCount(0))
|
|---|
| 773 | try {
|
|---|
| 774 | await api.queryFulfilled
|
|---|
| 775 | api.dispatch(setCount(1))
|
|---|
| 776 | } catch {
|
|---|
| 777 | api.dispatch(setCount(-1))
|
|---|
| 778 | }
|
|---|
| 779 | },
|
|---|
| 780 | }),
|
|---|
| 781 | mutation: build.mutation<SuccessResponse, void>({
|
|---|
| 782 | query: () => ({ url: '/success', method: 'POST' }),
|
|---|
| 783 | async onQueryStarted(_, api) {
|
|---|
| 784 | api.dispatch(setCount(0))
|
|---|
| 785 | try {
|
|---|
| 786 | await api.queryFulfilled
|
|---|
| 787 | api.dispatch(setCount(1))
|
|---|
| 788 | } catch {
|
|---|
| 789 | api.dispatch(setCount(-1))
|
|---|
| 790 | }
|
|---|
| 791 | },
|
|---|
| 792 | }),
|
|---|
| 793 | }),
|
|---|
| 794 | })
|
|---|
| 795 |
|
|---|
| 796 | const storeRef = setupApiStore(api, { testReducer })
|
|---|
| 797 |
|
|---|
| 798 | test('query lifecycle events fire properly', async () => {
|
|---|
| 799 | // We intentionally fail the first request so we can test all lifecycles
|
|---|
| 800 | server.use(
|
|---|
| 801 | http.get(
|
|---|
| 802 | 'https://example.com/success',
|
|---|
| 803 | () => HttpResponse.json({ value: 'failed' }, { status: 500 }),
|
|---|
| 804 | { once: true },
|
|---|
| 805 | ),
|
|---|
| 806 | )
|
|---|
| 807 |
|
|---|
| 808 | expect(storeRef.store.getState().testReducer.count).toBe(null)
|
|---|
| 809 | const failAttempt = storeRef.store.dispatch(api.endpoints.query.initiate())
|
|---|
| 810 | expect(storeRef.store.getState().testReducer.count).toBe(0)
|
|---|
| 811 | await failAttempt
|
|---|
| 812 | await delay(10)
|
|---|
| 813 | expect(storeRef.store.getState().testReducer.count).toBe(-1)
|
|---|
| 814 |
|
|---|
| 815 | const successAttempt = storeRef.store.dispatch(
|
|---|
| 816 | api.endpoints.query.initiate(),
|
|---|
| 817 | )
|
|---|
| 818 | expect(storeRef.store.getState().testReducer.count).toBe(0)
|
|---|
| 819 | await successAttempt
|
|---|
| 820 | await delay(10)
|
|---|
| 821 | expect(storeRef.store.getState().testReducer.count).toBe(1)
|
|---|
| 822 | })
|
|---|
| 823 |
|
|---|
| 824 | test('mutation lifecycle events fire properly', async () => {
|
|---|
| 825 | // We intentionally fail the first request so we can test all lifecycles
|
|---|
| 826 | server.use(
|
|---|
| 827 | http.post(
|
|---|
| 828 | 'https://example.com/success',
|
|---|
| 829 | () => HttpResponse.json({ value: 'failed' }, { status: 500 }),
|
|---|
| 830 | { once: true },
|
|---|
| 831 | ),
|
|---|
| 832 | )
|
|---|
| 833 |
|
|---|
| 834 | expect(storeRef.store.getState().testReducer.count).toBe(null)
|
|---|
| 835 | const failAttempt = storeRef.store.dispatch(
|
|---|
| 836 | api.endpoints.mutation.initiate(),
|
|---|
| 837 | )
|
|---|
| 838 | expect(storeRef.store.getState().testReducer.count).toBe(0)
|
|---|
| 839 | await failAttempt
|
|---|
| 840 | expect(storeRef.store.getState().testReducer.count).toBe(-1)
|
|---|
| 841 |
|
|---|
| 842 | const successAttempt = storeRef.store.dispatch(
|
|---|
| 843 | api.endpoints.mutation.initiate(),
|
|---|
| 844 | )
|
|---|
| 845 | expect(storeRef.store.getState().testReducer.count).toBe(0)
|
|---|
| 846 | await successAttempt
|
|---|
| 847 | expect(storeRef.store.getState().testReducer.count).toBe(1)
|
|---|
| 848 | })
|
|---|
| 849 | })
|
|---|
| 850 |
|
|---|
| 851 | test('providesTags and invalidatesTags can use baseQueryMeta', async () => {
|
|---|
| 852 | let _meta: FetchBaseQueryMeta | undefined
|
|---|
| 853 |
|
|---|
| 854 | type SuccessResponse = { value: 'success' }
|
|---|
| 855 |
|
|---|
| 856 | const api = createApi({
|
|---|
| 857 | baseQuery: fetchBaseQuery({ baseUrl: 'https://example.com' }),
|
|---|
| 858 | tagTypes: ['success'],
|
|---|
| 859 | endpoints: (build) => ({
|
|---|
| 860 | query: build.query<SuccessResponse, void>({
|
|---|
| 861 | query: () => '/success',
|
|---|
| 862 | providesTags: (_result, _error, _arg, meta) => {
|
|---|
| 863 | _meta = meta
|
|---|
| 864 | return ['success']
|
|---|
| 865 | },
|
|---|
| 866 | }),
|
|---|
| 867 | mutation: build.mutation<SuccessResponse, void>({
|
|---|
| 868 | query: () => ({ url: '/success', method: 'POST' }),
|
|---|
| 869 | invalidatesTags: (_result, _error, _arg, meta) => {
|
|---|
| 870 | _meta = meta
|
|---|
| 871 | return ['success']
|
|---|
| 872 | },
|
|---|
| 873 | }),
|
|---|
| 874 | }),
|
|---|
| 875 | })
|
|---|
| 876 |
|
|---|
| 877 | const storeRef = setupApiStore(api, undefined, {
|
|---|
| 878 | withoutTestLifecycles: true,
|
|---|
| 879 | })
|
|---|
| 880 |
|
|---|
| 881 | await storeRef.store.dispatch(api.endpoints.query.initiate())
|
|---|
| 882 | expect('request' in _meta! && 'response' in _meta!).toBe(true)
|
|---|
| 883 |
|
|---|
| 884 | _meta = undefined
|
|---|
| 885 |
|
|---|
| 886 | await storeRef.store.dispatch(api.endpoints.mutation.initiate())
|
|---|
| 887 |
|
|---|
| 888 | expect('request' in _meta! && 'response' in _meta!).toBe(true)
|
|---|
| 889 | })
|
|---|
| 890 |
|
|---|
| 891 | describe('structuralSharing flag behaviors', () => {
|
|---|
| 892 | type SuccessResponse = { value: 'success' }
|
|---|
| 893 |
|
|---|
| 894 | const api = createApi({
|
|---|
| 895 | baseQuery: fetchBaseQuery({ baseUrl: 'https://example.com' }),
|
|---|
| 896 | tagTypes: ['success'],
|
|---|
| 897 | endpoints: (build) => ({
|
|---|
| 898 | enabled: build.query<SuccessResponse, void>({
|
|---|
| 899 | query: () => '/success',
|
|---|
| 900 | }),
|
|---|
| 901 | disabled: build.query<SuccessResponse, void>({
|
|---|
| 902 | query: () => ({ url: '/success' }),
|
|---|
| 903 | structuralSharing: false,
|
|---|
| 904 | }),
|
|---|
| 905 | }),
|
|---|
| 906 | })
|
|---|
| 907 |
|
|---|
| 908 | const storeRef = setupApiStore(api)
|
|---|
| 909 |
|
|---|
| 910 | it('enables structural sharing for query endpoints by default', async () => {
|
|---|
| 911 | await storeRef.store.dispatch(api.endpoints.enabled.initiate())
|
|---|
| 912 | const firstRef = api.endpoints.enabled.select()(storeRef.store.getState())
|
|---|
| 913 |
|
|---|
| 914 | await storeRef.store.dispatch(
|
|---|
| 915 | api.endpoints.enabled.initiate(undefined, { forceRefetch: true }),
|
|---|
| 916 | )
|
|---|
| 917 |
|
|---|
| 918 | const secondRef = api.endpoints.enabled.select()(storeRef.store.getState())
|
|---|
| 919 |
|
|---|
| 920 | expect(firstRef.requestId).not.toEqual(secondRef.requestId)
|
|---|
| 921 | expect(firstRef.data === secondRef.data).toBeTruthy()
|
|---|
| 922 | })
|
|---|
| 923 |
|
|---|
| 924 | it('allows a query endpoint to opt-out of structural sharing', async () => {
|
|---|
| 925 | await storeRef.store.dispatch(api.endpoints.disabled.initiate())
|
|---|
| 926 | const firstRef = api.endpoints.disabled.select()(storeRef.store.getState())
|
|---|
| 927 |
|
|---|
| 928 | await storeRef.store.dispatch(
|
|---|
| 929 | api.endpoints.disabled.initiate(undefined, { forceRefetch: true }),
|
|---|
| 930 | )
|
|---|
| 931 |
|
|---|
| 932 | const secondRef = api.endpoints.disabled.select()(storeRef.store.getState())
|
|---|
| 933 |
|
|---|
| 934 | expect(firstRef.requestId).not.toEqual(secondRef.requestId)
|
|---|
| 935 | expect(firstRef.data === secondRef.data).toBeFalsy()
|
|---|
| 936 | })
|
|---|
| 937 | })
|
|---|
| 938 |
|
|---|
| 939 | describe('custom serializeQueryArgs per endpoint', () => {
|
|---|
| 940 | const customArgsSerializer: SerializeQueryArgs<number> = ({
|
|---|
| 941 | endpointName,
|
|---|
| 942 | queryArgs,
|
|---|
| 943 | }) => `${endpointName}-${queryArgs}`
|
|---|
| 944 |
|
|---|
| 945 | type SuccessResponse = { value: 'success' }
|
|---|
| 946 |
|
|---|
| 947 | const serializer1 = vi.fn(customArgsSerializer)
|
|---|
| 948 |
|
|---|
| 949 | interface MyApiClient {
|
|---|
| 950 | fetchPost: (id: string) => Promise<SuccessResponse>
|
|---|
| 951 | }
|
|---|
| 952 |
|
|---|
| 953 | const dummyClient: MyApiClient = {
|
|---|
| 954 | async fetchPost() {
|
|---|
| 955 | return { value: 'success' }
|
|---|
| 956 | },
|
|---|
| 957 | }
|
|---|
| 958 |
|
|---|
| 959 | const api = createApi({
|
|---|
| 960 | baseQuery: fetchBaseQuery({ baseUrl: 'https://example.com' }),
|
|---|
| 961 | serializeQueryArgs: ({ endpointName, queryArgs }) =>
|
|---|
| 962 | `base-${endpointName}-${queryArgs}`,
|
|---|
| 963 | endpoints: (build) => ({
|
|---|
| 964 | queryWithNoSerializer: build.query<SuccessResponse, number>({
|
|---|
| 965 | query: (arg) => `${arg}`,
|
|---|
| 966 | }),
|
|---|
| 967 | queryWithCustomSerializer: build.query<SuccessResponse, number>({
|
|---|
| 968 | query: (arg) => `${arg}`,
|
|---|
| 969 | serializeQueryArgs: serializer1,
|
|---|
| 970 | }),
|
|---|
| 971 | queryWithCustomObjectSerializer: build.query<
|
|---|
| 972 | SuccessResponse,
|
|---|
| 973 | { id: number; client: MyApiClient }
|
|---|
| 974 | >({
|
|---|
| 975 | query: (arg) => `${arg.id}`,
|
|---|
| 976 | serializeQueryArgs: ({
|
|---|
| 977 | endpointDefinition,
|
|---|
| 978 | endpointName,
|
|---|
| 979 | queryArgs,
|
|---|
| 980 | }) => {
|
|---|
| 981 | const { id } = queryArgs
|
|---|
| 982 | return { id }
|
|---|
| 983 | },
|
|---|
| 984 | }),
|
|---|
| 985 | queryWithCustomNumberSerializer: build.query<
|
|---|
| 986 | SuccessResponse,
|
|---|
| 987 | { id: number; client: MyApiClient }
|
|---|
| 988 | >({
|
|---|
| 989 | query: (arg) => `${arg.id}`,
|
|---|
| 990 | serializeQueryArgs: ({
|
|---|
| 991 | endpointDefinition,
|
|---|
| 992 | endpointName,
|
|---|
| 993 | queryArgs,
|
|---|
| 994 | }) => {
|
|---|
| 995 | const { id } = queryArgs
|
|---|
| 996 | return id
|
|---|
| 997 | },
|
|---|
| 998 | }),
|
|---|
| 999 | listItems: build.query<string[], number>({
|
|---|
| 1000 | query: (pageNumber) => `/listItems?page=${pageNumber}`,
|
|---|
| 1001 | serializeQueryArgs: ({ endpointName }) => {
|
|---|
| 1002 | return endpointName
|
|---|
| 1003 | },
|
|---|
| 1004 | merge: (currentCache, newItems) => {
|
|---|
| 1005 | currentCache.push(...newItems)
|
|---|
| 1006 | },
|
|---|
| 1007 | forceRefetch({ currentArg, previousArg }) {
|
|---|
| 1008 | return currentArg !== previousArg
|
|---|
| 1009 | },
|
|---|
| 1010 | }),
|
|---|
| 1011 | listItems2: build.query<{ items: string[]; meta?: any }, number>({
|
|---|
| 1012 | query: (pageNumber) => `/listItems2?page=${pageNumber}`,
|
|---|
| 1013 | serializeQueryArgs: ({ endpointName }) => {
|
|---|
| 1014 | return endpointName
|
|---|
| 1015 | },
|
|---|
| 1016 | transformResponse(items: string[]) {
|
|---|
| 1017 | return { items }
|
|---|
| 1018 | },
|
|---|
| 1019 | merge: (currentCache, newData, meta) => {
|
|---|
| 1020 | currentCache.items.push(...newData.items)
|
|---|
| 1021 | currentCache.meta = meta
|
|---|
| 1022 | },
|
|---|
| 1023 | forceRefetch({ currentArg, previousArg }) {
|
|---|
| 1024 | return currentArg !== previousArg
|
|---|
| 1025 | },
|
|---|
| 1026 | }),
|
|---|
| 1027 | }),
|
|---|
| 1028 | })
|
|---|
| 1029 |
|
|---|
| 1030 | const storeRef = setupApiStore(api)
|
|---|
| 1031 |
|
|---|
| 1032 | it('Works via createApi', async () => {
|
|---|
| 1033 | await storeRef.store.dispatch(
|
|---|
| 1034 | api.endpoints.queryWithNoSerializer.initiate(99),
|
|---|
| 1035 | )
|
|---|
| 1036 |
|
|---|
| 1037 | expect(serializer1).not.toHaveBeenCalled()
|
|---|
| 1038 |
|
|---|
| 1039 | await storeRef.store.dispatch(
|
|---|
| 1040 | api.endpoints.queryWithCustomSerializer.initiate(42),
|
|---|
| 1041 | )
|
|---|
| 1042 |
|
|---|
| 1043 | expect(serializer1).toHaveBeenCalled()
|
|---|
| 1044 |
|
|---|
| 1045 | expect(
|
|---|
| 1046 | storeRef.store.getState().api.queries['base-queryWithNoSerializer-99'],
|
|---|
| 1047 | ).toBeTruthy()
|
|---|
| 1048 |
|
|---|
| 1049 | expect(
|
|---|
| 1050 | storeRef.store.getState().api.queries['queryWithCustomSerializer-42'],
|
|---|
| 1051 | ).toBeTruthy()
|
|---|
| 1052 | })
|
|---|
| 1053 |
|
|---|
| 1054 | const serializer2 = vi.fn(customArgsSerializer)
|
|---|
| 1055 |
|
|---|
| 1056 | const injectedApi = api.injectEndpoints({
|
|---|
| 1057 | endpoints: (build) => ({
|
|---|
| 1058 | injectedQueryWithCustomSerializer: build.query<SuccessResponse, number>({
|
|---|
| 1059 | query: (arg) => `${arg}`,
|
|---|
| 1060 | serializeQueryArgs: serializer2,
|
|---|
| 1061 | }),
|
|---|
| 1062 | }),
|
|---|
| 1063 | })
|
|---|
| 1064 |
|
|---|
| 1065 | it('Works via injectEndpoints', async () => {
|
|---|
| 1066 | expect(serializer2).not.toHaveBeenCalled()
|
|---|
| 1067 |
|
|---|
| 1068 | await storeRef.store.dispatch(
|
|---|
| 1069 | injectedApi.endpoints.injectedQueryWithCustomSerializer.initiate(5),
|
|---|
| 1070 | )
|
|---|
| 1071 |
|
|---|
| 1072 | expect(serializer2).toHaveBeenCalled()
|
|---|
| 1073 | expect(
|
|---|
| 1074 | storeRef.store.getState().api.queries[
|
|---|
| 1075 | 'injectedQueryWithCustomSerializer-5'
|
|---|
| 1076 | ],
|
|---|
| 1077 | ).toBeTruthy()
|
|---|
| 1078 | })
|
|---|
| 1079 |
|
|---|
| 1080 | test('Serializes a returned object for query args', async () => {
|
|---|
| 1081 | await storeRef.store.dispatch(
|
|---|
| 1082 | api.endpoints.queryWithCustomObjectSerializer.initiate({
|
|---|
| 1083 | id: 42,
|
|---|
| 1084 | client: dummyClient,
|
|---|
| 1085 | }),
|
|---|
| 1086 | )
|
|---|
| 1087 |
|
|---|
| 1088 | expect(
|
|---|
| 1089 | storeRef.store.getState().api.queries[
|
|---|
| 1090 | 'queryWithCustomObjectSerializer({"id":42})'
|
|---|
| 1091 | ],
|
|---|
| 1092 | ).toBeTruthy()
|
|---|
| 1093 | })
|
|---|
| 1094 |
|
|---|
| 1095 | test('Serializes a returned primitive for query args', async () => {
|
|---|
| 1096 | await storeRef.store.dispatch(
|
|---|
| 1097 | api.endpoints.queryWithCustomNumberSerializer.initiate({
|
|---|
| 1098 | id: 42,
|
|---|
| 1099 | client: dummyClient,
|
|---|
| 1100 | }),
|
|---|
| 1101 | )
|
|---|
| 1102 |
|
|---|
| 1103 | expect(
|
|---|
| 1104 | storeRef.store.getState().api.queries[
|
|---|
| 1105 | 'queryWithCustomNumberSerializer(42)'
|
|---|
| 1106 | ],
|
|---|
| 1107 | ).toBeTruthy()
|
|---|
| 1108 | })
|
|---|
| 1109 |
|
|---|
| 1110 | test('serializeQueryArgs + merge allows refetching as args change with same cache key', async () => {
|
|---|
| 1111 | const allItems = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'i']
|
|---|
| 1112 | const PAGE_SIZE = 3
|
|---|
| 1113 |
|
|---|
| 1114 | server.use(
|
|---|
| 1115 | http.get('https://example.com/listItems', ({ request }) => {
|
|---|
| 1116 | const url = new URL(request.url)
|
|---|
| 1117 | const pageString = url.searchParams.get('page')
|
|---|
| 1118 | const pageNum = parseInt(pageString || '0')
|
|---|
| 1119 |
|
|---|
| 1120 | const results = paginate(allItems, PAGE_SIZE, pageNum)
|
|---|
| 1121 | return HttpResponse.json(results)
|
|---|
| 1122 | }),
|
|---|
| 1123 | )
|
|---|
| 1124 |
|
|---|
| 1125 | // Page number shouldn't matter here, because the cache key ignores that.
|
|---|
| 1126 | // We just need to select the only cache entry.
|
|---|
| 1127 | const selectListItems = api.endpoints.listItems.select(0)
|
|---|
| 1128 |
|
|---|
| 1129 | await storeRef.store.dispatch(api.endpoints.listItems.initiate(1))
|
|---|
| 1130 |
|
|---|
| 1131 | const initialEntry = selectListItems(storeRef.store.getState())
|
|---|
| 1132 | expect(initialEntry.data).toEqual(['a', 'b', 'c'])
|
|---|
| 1133 |
|
|---|
| 1134 | await storeRef.store.dispatch(api.endpoints.listItems.initiate(2))
|
|---|
| 1135 | const updatedEntry = selectListItems(storeRef.store.getState())
|
|---|
| 1136 | expect(updatedEntry.data).toEqual(['a', 'b', 'c', 'd', 'e', 'f'])
|
|---|
| 1137 | })
|
|---|
| 1138 |
|
|---|
| 1139 | test('merge receives a meta object as an argument', async () => {
|
|---|
| 1140 | const allItems = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'i']
|
|---|
| 1141 | const PAGE_SIZE = 3
|
|---|
| 1142 |
|
|---|
| 1143 | server.use(
|
|---|
| 1144 | http.get('https://example.com/listItems2', ({ request }) => {
|
|---|
| 1145 | const url = new URL(request.url)
|
|---|
| 1146 | const pageString = url.searchParams.get('page')
|
|---|
| 1147 | const pageNum = parseInt(pageString || '0')
|
|---|
| 1148 |
|
|---|
| 1149 | const results = paginate(allItems, PAGE_SIZE, pageNum)
|
|---|
| 1150 | return HttpResponse.json(results)
|
|---|
| 1151 | }),
|
|---|
| 1152 | )
|
|---|
| 1153 |
|
|---|
| 1154 | const selectListItems = api.endpoints.listItems2.select(0)
|
|---|
| 1155 |
|
|---|
| 1156 | await storeRef.store.dispatch(api.endpoints.listItems2.initiate(1))
|
|---|
| 1157 | await storeRef.store.dispatch(api.endpoints.listItems2.initiate(2))
|
|---|
| 1158 | const cacheEntry = selectListItems(storeRef.store.getState())
|
|---|
| 1159 |
|
|---|
| 1160 | // Should have passed along the third arg from `merge` containing these fields
|
|---|
| 1161 | expect(cacheEntry.data?.meta).toEqual({
|
|---|
| 1162 | requestId: expect.any(String),
|
|---|
| 1163 | fulfilledTimeStamp: expect.any(Number),
|
|---|
| 1164 | arg: 2,
|
|---|
| 1165 | baseQueryMeta: expect.any(Object),
|
|---|
| 1166 | })
|
|---|
| 1167 | })
|
|---|
| 1168 | })
|
|---|
| 1169 |
|
|---|
| 1170 | describe('timeout behavior', () => {
|
|---|
| 1171 | test('triggers TIMEOUT_ERROR', async () => {
|
|---|
| 1172 | const api = createApi({
|
|---|
| 1173 | baseQuery: fetchBaseQuery({ baseUrl: 'https://example.com', timeout: 5 }),
|
|---|
| 1174 | endpoints: (build) => ({
|
|---|
| 1175 | query: build.query<unknown, void>({
|
|---|
| 1176 | query: () => '/success',
|
|---|
| 1177 | }),
|
|---|
| 1178 | }),
|
|---|
| 1179 | })
|
|---|
| 1180 |
|
|---|
| 1181 | const storeRef = setupApiStore(api, undefined, {
|
|---|
| 1182 | withoutTestLifecycles: true,
|
|---|
| 1183 | })
|
|---|
| 1184 |
|
|---|
| 1185 | server.use(
|
|---|
| 1186 | http.get(
|
|---|
| 1187 | 'https://example.com/success',
|
|---|
| 1188 | async () => {
|
|---|
| 1189 | await delay(50)
|
|---|
| 1190 | return HttpResponse.json({ value: 'failed' }, { status: 500 })
|
|---|
| 1191 | },
|
|---|
| 1192 | { once: true },
|
|---|
| 1193 | ),
|
|---|
| 1194 | )
|
|---|
| 1195 |
|
|---|
| 1196 | const result = await storeRef.store.dispatch(api.endpoints.query.initiate())
|
|---|
| 1197 |
|
|---|
| 1198 | expect(result?.error).toEqual({
|
|---|
| 1199 | status: 'TIMEOUT_ERROR',
|
|---|
| 1200 | error: expect.stringMatching(/^TimeoutError/),
|
|---|
| 1201 | })
|
|---|
| 1202 | })
|
|---|
| 1203 | })
|
|---|
| 1204 |
|
|---|
| 1205 | describe('endpoint schemas', () => {
|
|---|
| 1206 | const schemaConverter: SchemaFailureConverter<
|
|---|
| 1207 | ReturnType<typeof fetchBaseQuery>
|
|---|
| 1208 | > = (error) => {
|
|---|
| 1209 | return {
|
|---|
| 1210 | status: 'CUSTOM_ERROR',
|
|---|
| 1211 | error: error.schemaName + ' failed validation',
|
|---|
| 1212 | data: error.issues,
|
|---|
| 1213 | }
|
|---|
| 1214 | }
|
|---|
| 1215 |
|
|---|
| 1216 | const serializedSchemaError = {
|
|---|
| 1217 | name: 'SchemaError',
|
|---|
| 1218 | message: expect.any(String),
|
|---|
| 1219 | stack: expect.any(String),
|
|---|
| 1220 | } satisfies SerializedError
|
|---|
| 1221 |
|
|---|
| 1222 | const onSchemaFailureGlobal = vi.fn<SchemaFailureHandler>()
|
|---|
| 1223 | const onSchemaFailureEndpoint = vi.fn<SchemaFailureHandler>()
|
|---|
| 1224 | afterEach(() => {
|
|---|
| 1225 | onSchemaFailureGlobal.mockClear()
|
|---|
| 1226 | onSchemaFailureEndpoint.mockClear()
|
|---|
| 1227 | })
|
|---|
| 1228 |
|
|---|
| 1229 | function expectFailureHandlersToHaveBeenCalled({
|
|---|
| 1230 | schemaName,
|
|---|
| 1231 | value,
|
|---|
| 1232 | arg,
|
|---|
| 1233 | }: {
|
|---|
| 1234 | schemaName: `${SchemaType}Schema`
|
|---|
| 1235 | value: unknown
|
|---|
| 1236 | arg: unknown
|
|---|
| 1237 | }) {
|
|---|
| 1238 | for (const handler of [onSchemaFailureGlobal, onSchemaFailureEndpoint]) {
|
|---|
| 1239 | expect(handler).toHaveBeenCalledOnce()
|
|---|
| 1240 | const [namedError, info] = handler.mock.calls[0]
|
|---|
| 1241 | expect(namedError).toBeInstanceOf(NamedSchemaError)
|
|---|
| 1242 | expect(namedError.issues.length).toBeGreaterThan(0)
|
|---|
| 1243 | expect(namedError.value).toEqual(value)
|
|---|
| 1244 | expect(namedError.schemaName).toBe(schemaName)
|
|---|
| 1245 | expect(info.endpoint).toBe('query')
|
|---|
| 1246 | expect(info.type).toBe('query')
|
|---|
| 1247 | expect(info.arg).toEqual(arg)
|
|---|
| 1248 | }
|
|---|
| 1249 | }
|
|---|
| 1250 |
|
|---|
| 1251 | interface SkipApiOptions {
|
|---|
| 1252 | globalSkip?: boolean
|
|---|
| 1253 | endpointSkip?: boolean
|
|---|
| 1254 | useArray?: boolean
|
|---|
| 1255 | globalCatch?: boolean
|
|---|
| 1256 | endpointCatch?: boolean
|
|---|
| 1257 | }
|
|---|
| 1258 |
|
|---|
| 1259 | const apiOptions = (
|
|---|
| 1260 | type: SchemaType,
|
|---|
| 1261 | { useArray, globalSkip, globalCatch }: SkipApiOptions = {},
|
|---|
| 1262 | ) => ({
|
|---|
| 1263 | onSchemaFailure: onSchemaFailureGlobal,
|
|---|
| 1264 | skipSchemaValidation: useArray ? globalSkip && [type] : globalSkip,
|
|---|
| 1265 | catchSchemaFailure: globalCatch ? schemaConverter : undefined,
|
|---|
| 1266 | })
|
|---|
| 1267 |
|
|---|
| 1268 | const endpointOptions = (
|
|---|
| 1269 | type: SchemaType,
|
|---|
| 1270 | { useArray, endpointSkip, endpointCatch }: SkipApiOptions = {},
|
|---|
| 1271 | ) => ({
|
|---|
| 1272 | onSchemaFailure: onSchemaFailureEndpoint,
|
|---|
| 1273 | skipSchemaValidation: useArray ? endpointSkip && [type] : endpointSkip,
|
|---|
| 1274 | catchSchemaFailure: endpointCatch ? schemaConverter : undefined,
|
|---|
| 1275 | })
|
|---|
| 1276 |
|
|---|
| 1277 | const skipCases: [string, SkipApiOptions][] = [
|
|---|
| 1278 | ['globally', { globalSkip: true }],
|
|---|
| 1279 | ['on the endpoint', { endpointSkip: true }],
|
|---|
| 1280 | ['globally (array)', { globalSkip: true, useArray: true }],
|
|---|
| 1281 | ['on the endpoint (array)', { endpointSkip: true, useArray: true }],
|
|---|
| 1282 | ]
|
|---|
| 1283 |
|
|---|
| 1284 | describe('argSchema', () => {
|
|---|
| 1285 | const makeApi = (opts?: SkipApiOptions) =>
|
|---|
| 1286 | createApi({
|
|---|
| 1287 | baseQuery: fetchBaseQuery({ baseUrl: 'https://example.com' }),
|
|---|
| 1288 | ...apiOptions('arg', opts),
|
|---|
| 1289 | endpoints: (build) => ({
|
|---|
| 1290 | query: build.query<unknown, { id: number }>({
|
|---|
| 1291 | query: ({ id }) => `/post/${id}`,
|
|---|
| 1292 | argSchema: v.object({ id: v.number() }),
|
|---|
| 1293 | ...endpointOptions('arg', opts),
|
|---|
| 1294 | }),
|
|---|
| 1295 | }),
|
|---|
| 1296 | })
|
|---|
| 1297 | test("can be used to validate the endpoint's arguments", async () => {
|
|---|
| 1298 | const api = makeApi()
|
|---|
| 1299 |
|
|---|
| 1300 | const storeRef = setupApiStore(api, undefined, {
|
|---|
| 1301 | withoutTestLifecycles: true,
|
|---|
| 1302 | })
|
|---|
| 1303 |
|
|---|
| 1304 | const result = await storeRef.store.dispatch(
|
|---|
| 1305 | api.endpoints.query.initiate({ id: 1 }),
|
|---|
| 1306 | )
|
|---|
| 1307 |
|
|---|
| 1308 | expect(result?.error).toBeUndefined()
|
|---|
| 1309 |
|
|---|
| 1310 | const invalidResult = await storeRef.store.dispatch(
|
|---|
| 1311 | // @ts-expect-error
|
|---|
| 1312 | api.endpoints.query.initiate({ id: '1' }),
|
|---|
| 1313 | )
|
|---|
| 1314 |
|
|---|
| 1315 | expect(invalidResult?.error).toEqual(serializedSchemaError)
|
|---|
| 1316 | expectFailureHandlersToHaveBeenCalled({
|
|---|
| 1317 | schemaName: 'argSchema',
|
|---|
| 1318 | value: { id: '1' },
|
|---|
| 1319 | arg: { id: '1' },
|
|---|
| 1320 | })
|
|---|
| 1321 | })
|
|---|
| 1322 |
|
|---|
| 1323 | test.each(skipCases)('can be skipped %s', async (_, arg) => {
|
|---|
| 1324 | const api = makeApi(arg)
|
|---|
| 1325 |
|
|---|
| 1326 | const storeRef = setupApiStore(api, undefined, {
|
|---|
| 1327 | withoutTestLifecycles: true,
|
|---|
| 1328 | })
|
|---|
| 1329 |
|
|---|
| 1330 | const result = await storeRef.store.dispatch(
|
|---|
| 1331 | // @ts-expect-error
|
|---|
| 1332 | api.endpoints.query.initiate({ id: '1' }),
|
|---|
| 1333 | )
|
|---|
| 1334 |
|
|---|
| 1335 | expect(result?.error).toBeUndefined()
|
|---|
| 1336 | })
|
|---|
| 1337 | // we only need to test this once
|
|---|
| 1338 | test('endpoint overrides global skip', async () => {
|
|---|
| 1339 | const api = makeApi({ globalSkip: true, endpointSkip: false })
|
|---|
| 1340 |
|
|---|
| 1341 | const storeRef = setupApiStore(api, undefined, {
|
|---|
| 1342 | withoutTestLifecycles: true,
|
|---|
| 1343 | })
|
|---|
| 1344 |
|
|---|
| 1345 | const result = await storeRef.store.dispatch(
|
|---|
| 1346 | // @ts-expect-error
|
|---|
| 1347 | api.endpoints.query.initiate({ id: '1' }),
|
|---|
| 1348 | )
|
|---|
| 1349 |
|
|---|
| 1350 | expect(result?.error).toEqual(serializedSchemaError)
|
|---|
| 1351 | })
|
|---|
| 1352 |
|
|---|
| 1353 | test('can be converted to a standard error object at global level', async () => {
|
|---|
| 1354 | const api = makeApi({ globalCatch: true })
|
|---|
| 1355 | const storeRef = setupApiStore(api, undefined, {
|
|---|
| 1356 | withoutTestLifecycles: true,
|
|---|
| 1357 | })
|
|---|
| 1358 |
|
|---|
| 1359 | const result = await storeRef.store.dispatch(
|
|---|
| 1360 | // @ts-expect-error
|
|---|
| 1361 | api.endpoints.query.initiate({ id: '1' }),
|
|---|
| 1362 | )
|
|---|
| 1363 |
|
|---|
| 1364 | expect(result?.error).toEqual({
|
|---|
| 1365 | status: 'CUSTOM_ERROR',
|
|---|
| 1366 | error: 'argSchema failed validation',
|
|---|
| 1367 | data: expect.any(Array),
|
|---|
| 1368 | })
|
|---|
| 1369 |
|
|---|
| 1370 | expectFailureHandlersToHaveBeenCalled({
|
|---|
| 1371 | schemaName: 'argSchema',
|
|---|
| 1372 | value: { id: '1' },
|
|---|
| 1373 | arg: { id: '1' },
|
|---|
| 1374 | })
|
|---|
| 1375 | })
|
|---|
| 1376 | test('can be converted to a standard error object at endpoint level', async () => {
|
|---|
| 1377 | const api = makeApi({ endpointCatch: true })
|
|---|
| 1378 | const storeRef = setupApiStore(api, undefined, {
|
|---|
| 1379 | withoutTestLifecycles: true,
|
|---|
| 1380 | })
|
|---|
| 1381 |
|
|---|
| 1382 | const result = await storeRef.store.dispatch(
|
|---|
| 1383 | // @ts-expect-error
|
|---|
| 1384 | api.endpoints.query.initiate({ id: '1' }),
|
|---|
| 1385 | )
|
|---|
| 1386 |
|
|---|
| 1387 | expect(result?.error).toEqual({
|
|---|
| 1388 | status: 'CUSTOM_ERROR',
|
|---|
| 1389 | error: 'argSchema failed validation',
|
|---|
| 1390 | data: expect.any(Array),
|
|---|
| 1391 | })
|
|---|
| 1392 | expectFailureHandlersToHaveBeenCalled({
|
|---|
| 1393 | schemaName: 'argSchema',
|
|---|
| 1394 | value: { id: '1' },
|
|---|
| 1395 | arg: { id: '1' },
|
|---|
| 1396 | })
|
|---|
| 1397 | })
|
|---|
| 1398 | })
|
|---|
| 1399 | describe('rawResponseSchema', () => {
|
|---|
| 1400 | const makeApi = (opts?: SkipApiOptions) =>
|
|---|
| 1401 | createApi({
|
|---|
| 1402 | baseQuery: fetchBaseQuery({ baseUrl: 'https://example.com' }),
|
|---|
| 1403 | ...apiOptions('rawResponse', opts),
|
|---|
| 1404 | endpoints: (build) => ({
|
|---|
| 1405 | query: build.query<{ success: boolean }, void>({
|
|---|
| 1406 | query: () => '/success',
|
|---|
| 1407 | rawResponseSchema: v.object({ value: v.literal('success!') }),
|
|---|
| 1408 | ...endpointOptions('rawResponse', opts),
|
|---|
| 1409 | }),
|
|---|
| 1410 | }),
|
|---|
| 1411 | })
|
|---|
| 1412 | test("can be used to validate the endpoint's raw result", async () => {
|
|---|
| 1413 | const api = makeApi()
|
|---|
| 1414 | const storeRef = setupApiStore(api, undefined, {
|
|---|
| 1415 | withoutTestLifecycles: true,
|
|---|
| 1416 | })
|
|---|
| 1417 | const result = await storeRef.store.dispatch(
|
|---|
| 1418 | api.endpoints.query.initiate(),
|
|---|
| 1419 | )
|
|---|
| 1420 | expect(result?.error).toEqual(serializedSchemaError)
|
|---|
| 1421 | expectFailureHandlersToHaveBeenCalled({
|
|---|
| 1422 | schemaName: 'rawResponseSchema',
|
|---|
| 1423 | value: { value: 'success' },
|
|---|
| 1424 | arg: undefined,
|
|---|
| 1425 | })
|
|---|
| 1426 | })
|
|---|
| 1427 | test.each(skipCases)('can be skipped %s', async (_, arg) => {
|
|---|
| 1428 | const api = makeApi(arg)
|
|---|
| 1429 | const storeRef = setupApiStore(api, undefined, {
|
|---|
| 1430 | withoutTestLifecycles: true,
|
|---|
| 1431 | })
|
|---|
| 1432 | const result = await storeRef.store.dispatch(
|
|---|
| 1433 | api.endpoints.query.initiate(),
|
|---|
| 1434 | )
|
|---|
| 1435 | expect(result?.error).toBeUndefined()
|
|---|
| 1436 | })
|
|---|
| 1437 | test('can be skipped on the endpoint', async () => {
|
|---|
| 1438 | const api = makeApi({ endpointSkip: true })
|
|---|
| 1439 | const storeRef = setupApiStore(api, undefined, {
|
|---|
| 1440 | withoutTestLifecycles: true,
|
|---|
| 1441 | })
|
|---|
| 1442 | const result = await storeRef.store.dispatch(
|
|---|
| 1443 | api.endpoints.query.initiate(),
|
|---|
| 1444 | )
|
|---|
| 1445 | expect(result?.error).toBeUndefined()
|
|---|
| 1446 | })
|
|---|
| 1447 | test('can be converted to a standard error object at global level', async () => {
|
|---|
| 1448 | const api = makeApi({ globalCatch: true })
|
|---|
| 1449 | const storeRef = setupApiStore(api, undefined, {
|
|---|
| 1450 | withoutTestLifecycles: true,
|
|---|
| 1451 | })
|
|---|
| 1452 | const result = await storeRef.store.dispatch(
|
|---|
| 1453 | api.endpoints.query.initiate(),
|
|---|
| 1454 | )
|
|---|
| 1455 | expect(result?.error).toEqual({
|
|---|
| 1456 | status: 'CUSTOM_ERROR',
|
|---|
| 1457 | error: 'rawResponseSchema failed validation',
|
|---|
| 1458 | data: expect.any(Array),
|
|---|
| 1459 | })
|
|---|
| 1460 | expectFailureHandlersToHaveBeenCalled({
|
|---|
| 1461 | schemaName: 'rawResponseSchema',
|
|---|
| 1462 | value: { value: 'success' },
|
|---|
| 1463 | arg: undefined,
|
|---|
| 1464 | })
|
|---|
| 1465 | })
|
|---|
| 1466 | test('can be converted to a standard error object at endpoint level', async () => {
|
|---|
| 1467 | const api = makeApi({ endpointCatch: true })
|
|---|
| 1468 | const storeRef = setupApiStore(api, undefined, {
|
|---|
| 1469 | withoutTestLifecycles: true,
|
|---|
| 1470 | })
|
|---|
| 1471 | const result = await storeRef.store.dispatch(
|
|---|
| 1472 | api.endpoints.query.initiate(),
|
|---|
| 1473 | )
|
|---|
| 1474 | expect(result?.error).toEqual({
|
|---|
| 1475 | status: 'CUSTOM_ERROR',
|
|---|
| 1476 | error: 'rawResponseSchema failed validation',
|
|---|
| 1477 | data: expect.any(Array),
|
|---|
| 1478 | })
|
|---|
| 1479 | expectFailureHandlersToHaveBeenCalled({
|
|---|
| 1480 | schemaName: 'rawResponseSchema',
|
|---|
| 1481 | value: { value: 'success' },
|
|---|
| 1482 | arg: undefined,
|
|---|
| 1483 | })
|
|---|
| 1484 | })
|
|---|
| 1485 | })
|
|---|
| 1486 | describe('responseSchema', () => {
|
|---|
| 1487 | const makeApi = (opts?: SkipApiOptions) =>
|
|---|
| 1488 | createApi({
|
|---|
| 1489 | baseQuery: fetchBaseQuery({ baseUrl: 'https://example.com' }),
|
|---|
| 1490 | ...apiOptions('response', opts),
|
|---|
| 1491 | endpoints: (build) => ({
|
|---|
| 1492 | query: build.query<{ success: boolean }, void>({
|
|---|
| 1493 | query: () => '/success',
|
|---|
| 1494 | transformResponse: () => ({ success: false }),
|
|---|
| 1495 | responseSchema: v.object({ success: v.literal(true) }),
|
|---|
| 1496 | ...endpointOptions('response', opts),
|
|---|
| 1497 | }),
|
|---|
| 1498 | }),
|
|---|
| 1499 | })
|
|---|
| 1500 | test("can be used to validate the endpoint's final result", async () => {
|
|---|
| 1501 | const api = makeApi()
|
|---|
| 1502 | const storeRef = setupApiStore(api, undefined, {
|
|---|
| 1503 | withoutTestLifecycles: true,
|
|---|
| 1504 | })
|
|---|
| 1505 | const result = await storeRef.store.dispatch(
|
|---|
| 1506 | api.endpoints.query.initiate(),
|
|---|
| 1507 | )
|
|---|
| 1508 | expect(result?.error).toEqual(serializedSchemaError)
|
|---|
| 1509 |
|
|---|
| 1510 | expectFailureHandlersToHaveBeenCalled({
|
|---|
| 1511 | schemaName: 'responseSchema',
|
|---|
| 1512 | value: { success: false },
|
|---|
| 1513 | arg: undefined,
|
|---|
| 1514 | })
|
|---|
| 1515 | })
|
|---|
| 1516 | test.each(skipCases)('can be skipped %s', async (_, arg) => {
|
|---|
| 1517 | const api = makeApi(arg)
|
|---|
| 1518 | const storeRef = setupApiStore(api, undefined, {
|
|---|
| 1519 | withoutTestLifecycles: true,
|
|---|
| 1520 | })
|
|---|
| 1521 | const result = await storeRef.store.dispatch(
|
|---|
| 1522 | api.endpoints.query.initiate(),
|
|---|
| 1523 | )
|
|---|
| 1524 | expect(result?.error).toBeUndefined()
|
|---|
| 1525 | })
|
|---|
| 1526 | test('can be converted to a standard error object at global level', async () => {
|
|---|
| 1527 | const api = makeApi({ globalCatch: true })
|
|---|
| 1528 | const storeRef = setupApiStore(api, undefined, {
|
|---|
| 1529 | withoutTestLifecycles: true,
|
|---|
| 1530 | })
|
|---|
| 1531 | const result = await storeRef.store.dispatch(
|
|---|
| 1532 | api.endpoints.query.initiate(),
|
|---|
| 1533 | )
|
|---|
| 1534 | expect(result?.error).toEqual({
|
|---|
| 1535 | status: 'CUSTOM_ERROR',
|
|---|
| 1536 | error: 'responseSchema failed validation',
|
|---|
| 1537 | data: expect.any(Array),
|
|---|
| 1538 | })
|
|---|
| 1539 | expectFailureHandlersToHaveBeenCalled({
|
|---|
| 1540 | schemaName: 'responseSchema',
|
|---|
| 1541 | value: { success: false },
|
|---|
| 1542 | arg: undefined,
|
|---|
| 1543 | })
|
|---|
| 1544 | })
|
|---|
| 1545 | test('can be converted to a standard error object at endpoint level', async () => {
|
|---|
| 1546 | const api = makeApi({ endpointCatch: true })
|
|---|
| 1547 | const storeRef = setupApiStore(api, undefined, {
|
|---|
| 1548 | withoutTestLifecycles: true,
|
|---|
| 1549 | })
|
|---|
| 1550 | const result = await storeRef.store.dispatch(
|
|---|
| 1551 | api.endpoints.query.initiate(),
|
|---|
| 1552 | )
|
|---|
| 1553 | expect(result?.error).toEqual({
|
|---|
| 1554 | status: 'CUSTOM_ERROR',
|
|---|
| 1555 | error: 'responseSchema failed validation',
|
|---|
| 1556 | data: expect.any(Array),
|
|---|
| 1557 | })
|
|---|
| 1558 | expectFailureHandlersToHaveBeenCalled({
|
|---|
| 1559 | schemaName: 'responseSchema',
|
|---|
| 1560 | value: { success: false },
|
|---|
| 1561 | arg: undefined,
|
|---|
| 1562 | })
|
|---|
| 1563 | })
|
|---|
| 1564 | })
|
|---|
| 1565 | describe('rawErrorResponseSchema', () => {
|
|---|
| 1566 | const makeApi = (opts?: SkipApiOptions) =>
|
|---|
| 1567 | createApi({
|
|---|
| 1568 | baseQuery: fetchBaseQuery({ baseUrl: 'https://example.com' }),
|
|---|
| 1569 | ...apiOptions('rawErrorResponse', opts),
|
|---|
| 1570 | endpoints: (build) => ({
|
|---|
| 1571 | query: build.query<{ success: boolean }, void>({
|
|---|
| 1572 | query: () => '/error',
|
|---|
| 1573 | rawErrorResponseSchema: v.object({
|
|---|
| 1574 | status: v.pipe(v.number(), v.minValue(400), v.maxValue(499)),
|
|---|
| 1575 | data: v.unknown(),
|
|---|
| 1576 | }),
|
|---|
| 1577 | ...endpointOptions('rawErrorResponse', opts),
|
|---|
| 1578 | }),
|
|---|
| 1579 | }),
|
|---|
| 1580 | })
|
|---|
| 1581 | test("can be used to validate the endpoint's raw error result", async () => {
|
|---|
| 1582 | const api = makeApi()
|
|---|
| 1583 | const storeRef = setupApiStore(api, undefined, {
|
|---|
| 1584 | withoutTestLifecycles: true,
|
|---|
| 1585 | })
|
|---|
| 1586 | const result = await storeRef.store.dispatch(
|
|---|
| 1587 | api.endpoints.query.initiate(),
|
|---|
| 1588 | )
|
|---|
| 1589 | expect(result?.error).toEqual(serializedSchemaError)
|
|---|
| 1590 | expectFailureHandlersToHaveBeenCalled({
|
|---|
| 1591 | schemaName: 'rawErrorResponseSchema',
|
|---|
| 1592 | value: { status: 500, data: { value: 'error' } },
|
|---|
| 1593 | arg: undefined,
|
|---|
| 1594 | })
|
|---|
| 1595 | })
|
|---|
| 1596 | test.each(skipCases)('can be skipped %s', async (_, arg) => {
|
|---|
| 1597 | const api = makeApi(arg)
|
|---|
| 1598 | const storeRef = setupApiStore(api, undefined, {
|
|---|
| 1599 | withoutTestLifecycles: true,
|
|---|
| 1600 | })
|
|---|
| 1601 | const result = await storeRef.store.dispatch(
|
|---|
| 1602 | api.endpoints.query.initiate(),
|
|---|
| 1603 | )
|
|---|
| 1604 | expect(result?.error).not.toEqual(serializedSchemaError)
|
|---|
| 1605 | })
|
|---|
| 1606 | test('can be converted to a standard error object at global level', async () => {
|
|---|
| 1607 | const api = makeApi({ globalCatch: true })
|
|---|
| 1608 | const storeRef = setupApiStore(api, undefined, {
|
|---|
| 1609 | withoutTestLifecycles: true,
|
|---|
| 1610 | })
|
|---|
| 1611 | const result = await storeRef.store.dispatch(
|
|---|
| 1612 | api.endpoints.query.initiate(),
|
|---|
| 1613 | )
|
|---|
| 1614 | expect(result?.error).toEqual({
|
|---|
| 1615 | status: 'CUSTOM_ERROR',
|
|---|
| 1616 | error: 'rawErrorResponseSchema failed validation',
|
|---|
| 1617 | data: expect.any(Array),
|
|---|
| 1618 | })
|
|---|
| 1619 | expectFailureHandlersToHaveBeenCalled({
|
|---|
| 1620 | schemaName: 'rawErrorResponseSchema',
|
|---|
| 1621 | value: { status: 500, data: { value: 'error' } },
|
|---|
| 1622 | arg: undefined,
|
|---|
| 1623 | })
|
|---|
| 1624 | })
|
|---|
| 1625 | test('can be converted to a standard error object at endpoint level', async () => {
|
|---|
| 1626 | const api = makeApi({ endpointCatch: true })
|
|---|
| 1627 | const storeRef = setupApiStore(api, undefined, {
|
|---|
| 1628 | withoutTestLifecycles: true,
|
|---|
| 1629 | })
|
|---|
| 1630 | const result = await storeRef.store.dispatch(
|
|---|
| 1631 | api.endpoints.query.initiate(),
|
|---|
| 1632 | )
|
|---|
| 1633 | expect(result?.error).toEqual({
|
|---|
| 1634 | status: 'CUSTOM_ERROR',
|
|---|
| 1635 | error: 'rawErrorResponseSchema failed validation',
|
|---|
| 1636 | data: expect.any(Array),
|
|---|
| 1637 | })
|
|---|
| 1638 | expectFailureHandlersToHaveBeenCalled({
|
|---|
| 1639 | schemaName: 'rawErrorResponseSchema',
|
|---|
| 1640 | value: { status: 500, data: { value: 'error' } },
|
|---|
| 1641 | arg: undefined,
|
|---|
| 1642 | })
|
|---|
| 1643 | })
|
|---|
| 1644 | })
|
|---|
| 1645 | describe('errorResponseSchema', () => {
|
|---|
| 1646 | const makeApi = (opts?: SkipApiOptions) =>
|
|---|
| 1647 | createApi({
|
|---|
| 1648 | baseQuery: fetchBaseQuery({ baseUrl: 'https://example.com' }),
|
|---|
| 1649 | ...apiOptions('errorResponse', opts),
|
|---|
| 1650 | endpoints: (build) => ({
|
|---|
| 1651 | query: build.query<{ success: boolean }, void>({
|
|---|
| 1652 | query: () => '/error',
|
|---|
| 1653 | transformErrorResponse: (error): FetchBaseQueryError => ({
|
|---|
| 1654 | status: 'CUSTOM_ERROR',
|
|---|
| 1655 | data: error,
|
|---|
| 1656 | error: 'whoops',
|
|---|
| 1657 | }),
|
|---|
| 1658 | errorResponseSchema: v.object({
|
|---|
| 1659 | status: v.literal('CUSTOM_ERROR'),
|
|---|
| 1660 | error: v.literal('oh no'),
|
|---|
| 1661 | data: v.unknown(),
|
|---|
| 1662 | }),
|
|---|
| 1663 | ...endpointOptions('errorResponse', opts),
|
|---|
| 1664 | }),
|
|---|
| 1665 | }),
|
|---|
| 1666 | })
|
|---|
| 1667 | test("can be used to validate the endpoint's final error result", async () => {
|
|---|
| 1668 | const api = makeApi()
|
|---|
| 1669 | const storeRef = setupApiStore(api, undefined, {
|
|---|
| 1670 | withoutTestLifecycles: true,
|
|---|
| 1671 | })
|
|---|
| 1672 | const result = await storeRef.store.dispatch(
|
|---|
| 1673 | api.endpoints.query.initiate(),
|
|---|
| 1674 | )
|
|---|
| 1675 | expect(result?.error).toEqual(serializedSchemaError)
|
|---|
| 1676 | expectFailureHandlersToHaveBeenCalled({
|
|---|
| 1677 | schemaName: 'errorResponseSchema',
|
|---|
| 1678 | value: {
|
|---|
| 1679 | status: 'CUSTOM_ERROR',
|
|---|
| 1680 | error: 'whoops',
|
|---|
| 1681 | data: { status: 500, data: { value: 'error' } },
|
|---|
| 1682 | },
|
|---|
| 1683 | arg: undefined,
|
|---|
| 1684 | })
|
|---|
| 1685 | })
|
|---|
| 1686 | test.each(skipCases)('can be skipped %s', async (_, arg) => {
|
|---|
| 1687 | const api = makeApi(arg)
|
|---|
| 1688 | const storeRef = setupApiStore(api, undefined, {
|
|---|
| 1689 | withoutTestLifecycles: true,
|
|---|
| 1690 | })
|
|---|
| 1691 | const result = await storeRef.store.dispatch(
|
|---|
| 1692 | api.endpoints.query.initiate(),
|
|---|
| 1693 | )
|
|---|
| 1694 | expect(result?.error).not.toEqual(serializedSchemaError)
|
|---|
| 1695 | })
|
|---|
| 1696 | test('can be converted to a standard error object at global level', async () => {
|
|---|
| 1697 | const api = makeApi({ globalCatch: true })
|
|---|
| 1698 | const storeRef = setupApiStore(api, undefined, {
|
|---|
| 1699 | withoutTestLifecycles: true,
|
|---|
| 1700 | })
|
|---|
| 1701 | const result = await storeRef.store.dispatch(
|
|---|
| 1702 | api.endpoints.query.initiate(),
|
|---|
| 1703 | )
|
|---|
| 1704 | expect(result?.error).toEqual({
|
|---|
| 1705 | status: 'CUSTOM_ERROR',
|
|---|
| 1706 | error: 'errorResponseSchema failed validation',
|
|---|
| 1707 | data: expect.any(Array),
|
|---|
| 1708 | })
|
|---|
| 1709 | expectFailureHandlersToHaveBeenCalled({
|
|---|
| 1710 | schemaName: 'errorResponseSchema',
|
|---|
| 1711 | value: {
|
|---|
| 1712 | status: 'CUSTOM_ERROR',
|
|---|
| 1713 | error: 'whoops',
|
|---|
| 1714 | data: { status: 500, data: { value: 'error' } },
|
|---|
| 1715 | },
|
|---|
| 1716 | arg: undefined,
|
|---|
| 1717 | })
|
|---|
| 1718 | })
|
|---|
| 1719 | test('can be converted to a standard error object at endpoint level', async () => {
|
|---|
| 1720 | const api = makeApi({ endpointCatch: true })
|
|---|
| 1721 | const storeRef = setupApiStore(api, undefined, {
|
|---|
| 1722 | withoutTestLifecycles: true,
|
|---|
| 1723 | })
|
|---|
| 1724 | const result = await storeRef.store.dispatch(
|
|---|
| 1725 | api.endpoints.query.initiate(),
|
|---|
| 1726 | )
|
|---|
| 1727 | expect(result?.error).toEqual({
|
|---|
| 1728 | status: 'CUSTOM_ERROR',
|
|---|
| 1729 | error: 'errorResponseSchema failed validation',
|
|---|
| 1730 | data: expect.any(Array),
|
|---|
| 1731 | })
|
|---|
| 1732 | expectFailureHandlersToHaveBeenCalled({
|
|---|
| 1733 | schemaName: 'errorResponseSchema',
|
|---|
| 1734 | value: {
|
|---|
| 1735 | status: 'CUSTOM_ERROR',
|
|---|
| 1736 | error: 'whoops',
|
|---|
| 1737 | data: { status: 500, data: { value: 'error' } },
|
|---|
| 1738 | },
|
|---|
| 1739 | arg: undefined,
|
|---|
| 1740 | })
|
|---|
| 1741 | })
|
|---|
| 1742 | })
|
|---|
| 1743 | describe('metaSchema', () => {
|
|---|
| 1744 | const makeApi = (opts?: SkipApiOptions) =>
|
|---|
| 1745 | createApi({
|
|---|
| 1746 | baseQuery: fetchBaseQuery({ baseUrl: 'https://example.com' }),
|
|---|
| 1747 | ...apiOptions('meta', opts),
|
|---|
| 1748 | endpoints: (build) => ({
|
|---|
| 1749 | query: build.query<{ success: boolean }, void>({
|
|---|
| 1750 | query: () => '/success',
|
|---|
| 1751 | metaSchema: v.object({
|
|---|
| 1752 | request: v.instance(Request),
|
|---|
| 1753 | response: v.instance(Response),
|
|---|
| 1754 | timestamp: v.number(),
|
|---|
| 1755 | }),
|
|---|
| 1756 | ...endpointOptions('meta', opts),
|
|---|
| 1757 | }),
|
|---|
| 1758 | }),
|
|---|
| 1759 | })
|
|---|
| 1760 | test("can be used to validate the endpoint's meta result", async () => {
|
|---|
| 1761 | const api = makeApi()
|
|---|
| 1762 | const storeRef = setupApiStore(api, undefined, {
|
|---|
| 1763 | withoutTestLifecycles: true,
|
|---|
| 1764 | })
|
|---|
| 1765 | const result = await storeRef.store.dispatch(
|
|---|
| 1766 | api.endpoints.query.initiate(),
|
|---|
| 1767 | )
|
|---|
| 1768 | expect(result?.error).toEqual(serializedSchemaError)
|
|---|
| 1769 | expectFailureHandlersToHaveBeenCalled({
|
|---|
| 1770 | schemaName: 'metaSchema',
|
|---|
| 1771 | value: {
|
|---|
| 1772 | request: expect.any(Request),
|
|---|
| 1773 | response: expect.any(Response),
|
|---|
| 1774 | },
|
|---|
| 1775 | arg: undefined,
|
|---|
| 1776 | })
|
|---|
| 1777 | })
|
|---|
| 1778 | test.each(skipCases)('can be skipped %s', async (_, arg) => {
|
|---|
| 1779 | const api = makeApi(arg)
|
|---|
| 1780 | const storeRef = setupApiStore(api, undefined, {
|
|---|
| 1781 | withoutTestLifecycles: true,
|
|---|
| 1782 | })
|
|---|
| 1783 | const result = await storeRef.store.dispatch(
|
|---|
| 1784 | api.endpoints.query.initiate(),
|
|---|
| 1785 | )
|
|---|
| 1786 | expect(result?.error).toBeUndefined()
|
|---|
| 1787 | })
|
|---|
| 1788 | test('can be converted to a standard error object at global level', async () => {
|
|---|
| 1789 | const api = makeApi({ globalCatch: true })
|
|---|
| 1790 | const storeRef = setupApiStore(api, undefined, {
|
|---|
| 1791 | withoutTestLifecycles: true,
|
|---|
| 1792 | })
|
|---|
| 1793 | const result = await storeRef.store.dispatch(
|
|---|
| 1794 | api.endpoints.query.initiate(),
|
|---|
| 1795 | )
|
|---|
| 1796 | expect(result?.error).toEqual({
|
|---|
| 1797 | status: 'CUSTOM_ERROR',
|
|---|
| 1798 | error: 'metaSchema failed validation',
|
|---|
| 1799 | data: expect.any(Array),
|
|---|
| 1800 | })
|
|---|
| 1801 | expectFailureHandlersToHaveBeenCalled({
|
|---|
| 1802 | schemaName: 'metaSchema',
|
|---|
| 1803 | value: {
|
|---|
| 1804 | request: expect.any(Request),
|
|---|
| 1805 | response: expect.any(Response),
|
|---|
| 1806 | },
|
|---|
| 1807 | arg: undefined,
|
|---|
| 1808 | })
|
|---|
| 1809 | })
|
|---|
| 1810 | test('can be converted to a standard error object at endpoint level', async () => {
|
|---|
| 1811 | const api = makeApi({ endpointCatch: true })
|
|---|
| 1812 | const storeRef = setupApiStore(api, undefined, {
|
|---|
| 1813 | withoutTestLifecycles: true,
|
|---|
| 1814 | })
|
|---|
| 1815 | const result = await storeRef.store.dispatch(
|
|---|
| 1816 | api.endpoints.query.initiate(),
|
|---|
| 1817 | )
|
|---|
| 1818 | expect(result?.error).toEqual({
|
|---|
| 1819 | status: 'CUSTOM_ERROR',
|
|---|
| 1820 | error: 'metaSchema failed validation',
|
|---|
| 1821 | data: expect.any(Array),
|
|---|
| 1822 | })
|
|---|
| 1823 | expectFailureHandlersToHaveBeenCalled({
|
|---|
| 1824 | schemaName: 'metaSchema',
|
|---|
| 1825 | value: {
|
|---|
| 1826 | request: expect.any(Request),
|
|---|
| 1827 | response: expect.any(Response),
|
|---|
| 1828 | },
|
|---|
| 1829 | arg: undefined,
|
|---|
| 1830 | })
|
|---|
| 1831 | })
|
|---|
| 1832 | })
|
|---|
| 1833 | })
|
|---|