diff --git a/packages/react/src/__tests__/__snapshots__/endpoint-types.web.tsx.snap b/packages/react/src/__tests__/__snapshots__/endpoint-types.web.tsx.snap new file mode 100644 index 00000000000..bd39b7ac6df --- /dev/null +++ b/packages/react/src/__tests__/__snapshots__/endpoint-types.web.tsx.snap @@ -0,0 +1,5 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`endpoint types CacheProvider should enforce defined types should console.error on invalid payload 1`] = `[]`; + +exports[`endpoint types ExternalCacheProvider should enforce defined types should console.error on invalid payload 1`] = `[]`; diff --git a/packages/react/src/__tests__/endpoint-types.web.tsx b/packages/react/src/__tests__/endpoint-types.web.tsx index 53d5dda4106..d4c8913f7cf 100644 --- a/packages/react/src/__tests__/endpoint-types.web.tsx +++ b/packages/react/src/__tests__/endpoint-types.web.tsx @@ -3,7 +3,7 @@ import { CacheProvider as ExternalCacheProvider } from '@data-client/redux'; import { TypedArticleResource } from '__tests__/new'; import nock from 'nock'; -import { makeRenderDataClient } from '../../../test'; +import { act, makeRenderDataClient } from '../../../test'; import { useController, useSuspense } from '../hooks'; import { payload, createPayload, users, nested } from '../test-fixtures'; @@ -55,7 +55,7 @@ describe('endpoint types', () => { .get(/article-cooler\/.*/) .reply(404, 'not found') .put(`/article-cooler/${payload.id}`) - .reply(200, (uri, body) => body) + .reply(200, (uri, body: any) => ({ ...payload, ...body })) .put(/article-cooler\/[^5].*/) .reply(404, 'not found'); @@ -72,6 +72,14 @@ describe('endpoint types', () => { nock.cleanAll(); }); + let errorSpy: jest.SpyInstance; + afterEach(() => { + errorSpy.mockRestore(); + }); + beforeEach( + () => (errorSpy = jest.spyOn(console, 'error').mockImplementation()), + ); + it('should pass with exact params', async () => { const { result, waitForNextUpdate } = renderDataClient(() => { return useSuspense(TypedArticleResource.get, { @@ -81,6 +89,7 @@ describe('endpoint types', () => { expect(result.current).toBeUndefined(); await waitForNextUpdate(); expect(result.current.title).toBe(payload.title); + expect(errorSpy.mock.calls.length).toBe(0); }); it('should fail with improperly typed param', async () => { @@ -94,17 +103,21 @@ describe('endpoint types', () => { await waitForNextUpdate(); expect(result.error).toBeDefined(); expect((result.error as any).status).toBe(404); + expect(errorSpy.mock.calls.length).toBe(0); }); it('should work with everything correct', async () => { const { result } = renderDataClient(() => { return useController().fetch; }); - const a = await result.current( - TypedArticleResource.update, - { id: payload.id }, - { title: 'hi' }, - ); + await act(async () => { + const a = await result.current( + TypedArticleResource.update, + { id: payload.id }, + { title: 'hi' }, + ); + }); + expect(errorSpy.mock.calls.length).toBe(0); }); it('types should strictly enforce with parameters that are any', async () => { @@ -119,6 +132,7 @@ describe('endpoint types', () => { { title: 'hi' }, ); () => result.current(TypedArticleResource.anyparam, { id: payload.id }); + expect(errorSpy.mock.calls.length).toBe(0); }); it('types should strictly enforce with body that are any', async () => { @@ -133,9 +147,10 @@ describe('endpoint types', () => { ); // @ts-expect-error () => result.current(TypedArticleResource.anybody(), { id: payload.id }); + expect(errorSpy.mock.calls.length).toBe(0); }); - it('should error on invalid payload', async () => { + it('should console.error on invalid payload', async () => { const { result } = renderDataClient(() => { return useController().fetch; }); @@ -151,6 +166,7 @@ describe('endpoint types', () => { // @ts-expect-error { title: 5 }, ); + expect(errorSpy.mock.calls).toMatchSnapshot(); }); it('should error on invalid params', async () => { @@ -158,14 +174,21 @@ describe('endpoint types', () => { return useController().fetch; }); - expect(() => - result.current( - TypedArticleResource.update, - // @ts-expect-error - 'hi', - { title: 'hi' }, - ), - ).toThrow(expect.any(Error)); + let caught; + await act(async () => { + try { + await result.current( + TypedArticleResource.update, + // @ts-expect-error + 'hi', + { title: 'hi' }, + ); + } catch (error) { + caught = error; + } + }); + + expect(caught).toEqual(expect.any(Error)); }); }); }); diff --git a/packages/test/src/makeRenderDataClient/renderHook.cts b/packages/test/src/makeRenderDataClient/renderHook.cts index 513c1253588..ecb6f8ca345 100644 --- a/packages/test/src/makeRenderDataClient/renderHook.cts +++ b/packages/test/src/makeRenderDataClient/renderHook.cts @@ -6,7 +6,6 @@ import type { Queries, waitForOptions, RenderHookOptions, - act as ActType, } from '@testing-library/react'; import { USE18 } from './use18.cjs'; @@ -17,8 +16,16 @@ export const renderHook: RenderHook = : (require('@testing-library/react-hooks').renderHook as any); export default renderHook; +// we declare our own type here because the one is marked as deprecated in the react library +declare const UNDEFINED_VOID_ONLY: unique symbol; +type VoidOrUndefinedOnly = void | { [UNDEFINED_VOID_ONLY]: never }; +interface ActType { + (callback: () => VoidOrUndefinedOnly): void; + (callback: () => T | Promise): Promise; +} + // this is for react native + react web compatibility, not actually 18 compatibility -export const act: typeof ActType = +export const act: ActType = USE18 ? require('./render18HookWrapped.js').act : (require('@testing-library/react-hooks').act as any);