From 421541df032069e3018c1f622936601e6b6f766b Mon Sep 17 00:00:00 2001 From: Jerel Miller Date: Tue, 28 Jul 2026 22:01:30 -0600 Subject: [PATCH] Reduce duplicate custom scalars tests between core APIs and React hooks --- .../client.mutate/customScalars.test.ts | 131 +- .../useBackgroundQuery/customScalars.test.tsx | 826 +-------- .../useLazyQuery/customScalars.test.tsx | 1372 +-------------- .../useLoadableQuery/customScalars.test.tsx | 896 +--------- .../useMutation/customScalars.test.tsx | 1521 +---------------- .../__tests__/useQuery/customScalars.test.tsx | 1313 +------------- .../customScalars.test.tsx | 2 +- .../useSubscription/customScalars.test.tsx | 279 +-- .../useSuspenseQuery/customScalars.test.tsx | 1242 +------------- .../customScalars.test.tsx | 689 +------- 10 files changed, 362 insertions(+), 7909 deletions(-) diff --git a/src/core/__tests__/client.mutate/customScalars.test.ts b/src/core/__tests__/client.mutate/customScalars.test.ts index 8f9210fc56b..754d408143f 100644 --- a/src/core/__tests__/client.mutate/customScalars.test.ts +++ b/src/core/__tests__/client.mutate/customScalars.test.ts @@ -9,8 +9,17 @@ import { NetworkStatus, } from "@apollo/client"; import { InMemoryCache } from "@apollo/client/cache"; +import { + Defer20220824Handler, + GraphQL17Alpha9Handler, +} from "@apollo/client/incremental"; import { MockLink } from "@apollo/client/testing"; -import { dateScalar, ObservableStream } from "@apollo/client/testing/internal"; +import { + dateScalar, + mockDefer20220824, + mockDeferStreamGraphQL17Alpha9, + ObservableStream, +} from "@apollo/client/testing/internal"; test("serializes scalar variables used in field arguments", async () => { let requestVariables!: OperationVariables; @@ -857,3 +866,123 @@ test("parses custom scalar fields with an `ignore` error policy", async () => { }, }); }); + +test("parses custom scalar fields across `@defer` payloads (defer20220824)", async () => { + const link = mockDefer20220824(); + const client = new ApolloClient({ + cache: new InMemoryCache({ + scalars: { Date: dateScalar }, + typePolicies: { + Event: { + fields: { + startDate: { scalar: "Date" }, + endDate: { scalar: "Date" }, + }, + }, + }, + }), + link: link.httpLink, + incrementalHandler: new Defer20220824Handler(), + }); + const mutation = gql` + mutation CreateEvent { + createEvent { + id + startDate + ... @defer { + endDate + } + } + } + `; + + const promise = client.mutate({ mutation }); + + link.enqueueInitialChunk({ + data: { + createEvent: { + __typename: "Event", + id: "1", + startDate: "2026-01-01", + }, + }, + hasNext: true, + }); + + link.enqueueSubsequentChunk({ + incremental: [{ data: { endDate: "2026-02-02" }, path: ["createEvent"] }], + hasNext: false, + }); + + await expect(promise).resolves.toStrictEqualTyped({ + data: { + createEvent: { + __typename: "Event", + id: "1", + startDate: new Date(2026, 0, 1), + endDate: new Date(2026, 1, 2), + }, + }, + }); +}); + +test("parses custom scalar fields across `@defer` payloads (graphql17Alpha9)", async () => { + const link = mockDeferStreamGraphQL17Alpha9(); + const client = new ApolloClient({ + cache: new InMemoryCache({ + scalars: { Date: dateScalar }, + typePolicies: { + Event: { + fields: { + startDate: { scalar: "Date" }, + endDate: { scalar: "Date" }, + }, + }, + }, + }), + link: link.httpLink, + incrementalHandler: new GraphQL17Alpha9Handler(), + }); + const mutation = gql` + mutation CreateEvent { + createEvent { + id + startDate + ... @defer { + endDate + } + } + } + `; + + const promise = client.mutate({ mutation }); + + link.enqueueInitialChunk({ + data: { + createEvent: { + __typename: "Event", + id: "1", + startDate: "2026-01-01", + }, + }, + pending: [{ id: "0", path: ["createEvent"] }], + hasNext: true, + }); + + link.enqueueSubsequentChunk({ + incremental: [{ data: { endDate: "2026-02-02" }, id: "0" }], + completed: [{ id: "0" }], + hasNext: false, + }); + + await expect(promise).resolves.toStrictEqualTyped({ + data: { + createEvent: { + __typename: "Event", + id: "1", + startDate: new Date(2026, 0, 1), + endDate: new Date(2026, 1, 2), + }, + }, + }); +}); diff --git a/src/react/hooks/__tests__/useBackgroundQuery/customScalars.test.tsx b/src/react/hooks/__tests__/useBackgroundQuery/customScalars.test.tsx index 9d6bcf7d21e..fda2490d042 100644 --- a/src/react/hooks/__tests__/useBackgroundQuery/customScalars.test.tsx +++ b/src/react/hooks/__tests__/useBackgroundQuery/customScalars.test.tsx @@ -4,31 +4,16 @@ import { disableActEnvironment } from "@testing-library/react-render-stream"; import { delay, of } from "rxjs"; import type { OperationVariables } from "@apollo/client"; -import { - ApolloClient, - ApolloLink, - CombinedGraphQLErrors, - gql, - NetworkStatus, -} from "@apollo/client"; +import { ApolloClient, ApolloLink, gql, NetworkStatus } from "@apollo/client"; import { InMemoryCache } from "@apollo/client/cache"; -import { - Defer20220824Handler, - GraphQL17Alpha9Handler, -} from "@apollo/client/incremental"; import { useBackgroundQuery } from "@apollo/client/react"; import { createClientWrapper, dateScalar, - markAsStreaming, - mockDefer20220824, - mockDeferStreamGraphQL17Alpha9, - spyOnConsole, } from "@apollo/client/testing/internal"; import { renderUseBackgroundQuery } from "./testUtils.js"; - -test("serializes scalar variables used in field arguments", async () => { +test("serializes parsed scalar values in variables", async () => { let requestVariables!: OperationVariables; const client = new ApolloClient({ @@ -89,104 +74,42 @@ test("serializes scalar variables used in field arguments", async () => { expect(requestVariables).toStrictEqualTyped({ date: "2026-01-01" }); }); -test("serializes scalar variables used in directive arguments", async () => { - let requestVariables!: OperationVariables; - - const client = new ApolloClient({ - cache: new InMemoryCache({ - scalars: { Date: dateScalar }, - }), - link: new ApolloLink((operation) => { - requestVariables = operation.variables; - - return of({ - data: { event: { __typename: "Event", name: "GraphQL Summit" } }, - }); - }), - }); - +test("returns parsed scalar fields", async () => { const query = gql` - query Event($date: Date!) { - event @on(date: $date) { - name + query Event { + event { + id + startDate } } `; - - using _disabledAct = disableActEnvironment(); - const { takeRender } = await renderUseBackgroundQuery( - () => - useBackgroundQuery(query, { variables: { date: new Date(2026, 0, 1) } }), - { wrapper: createClientWrapper(client) } - ); - - { - const { renderedComponents } = await takeRender(); - - expect(renderedComponents).toStrictEqual([ - "useBackgroundQuery", - "", - ]); - } - - { - const { snapshot, renderedComponents } = await takeRender(); - - expect(renderedComponents).toStrictEqual(["useReadQuery"]); - expect(snapshot).toStrictEqualTyped({ - data: { - event: { - __typename: "Event", - name: "GraphQL Summit", - }, - }, - dataState: "complete", - networkStatus: NetworkStatus.ready, - error: undefined, - }); - } - - await expect(takeRender).not.toRerender(); - expect(requestVariables).toStrictEqualTyped({ date: "2026-01-01" }); -}); - -test("serializes scalar fields in input object variables", async () => { - let requestVariables!: OperationVariables; - const client = new ApolloClient({ cache: new InMemoryCache({ scalars: { Date: dateScalar }, - inputObjects: { - EventFilter: { + typePolicies: { + Event: { fields: { - date: "Date", + startDate: { scalar: "Date" }, }, }, }, }), - link: new ApolloLink((operation) => { - requestVariables = operation.variables; - - return of({ - data: { event: { __typename: "Event", name: "GraphQL Summit" } }, - }); - }), + link: new ApolloLink(() => + of({ + data: { + event: { + __typename: "Event", + id: "1", + startDate: "2026-01-01", + }, + }, + }).pipe(delay(20)) + ), }); - const query = gql` - query Event($filter: EventFilter!) { - event(filter: $filter) { - name - } - } - `; - using _disabledAct = disableActEnvironment(); const { takeRender } = await renderUseBackgroundQuery( - () => - useBackgroundQuery(query, { - variables: { filter: { date: new Date(2026, 0, 1) } }, - }), + () => useBackgroundQuery(query), { wrapper: createClientWrapper(client) } ); @@ -207,7 +130,8 @@ test("serializes scalar fields in input object variables", async () => { data: { event: { __typename: "Event", - name: "GraphQL Summit", + id: "1", + startDate: new Date(2026, 0, 1), }, }, dataState: "complete", @@ -217,12 +141,9 @@ test("serializes scalar fields in input object variables", async () => { } await expect(takeRender).not.toRerender(); - expect(requestVariables).toStrictEqualTyped({ - filter: { date: "2026-01-01" }, - }); }); -test("preserves referential identity when refetching identical serialized scalar values", async () => { +test("preserves referential identity when refetching identical scalar values", async () => { const query = gql` query Event { event { @@ -337,700 +258,3 @@ test("preserves referential identity when refetching identical serialized scalar await expect(takeRender).not.toRerender(); }); - -test("serializes scalar fields in the error with a `none` error policy", async () => { - using _consoleSpy = spyOnConsole("error"); - const query = gql` - query Event { - event { - id - startDate - endDate - } - } - `; - const client = new ApolloClient({ - cache: new InMemoryCache({ - scalars: { Date: dateScalar }, - typePolicies: { - Event: { - fields: { - startDate: { scalar: "Date" }, - endDate: { scalar: "Date" }, - }, - }, - }, - }), - link: new ApolloLink(() => - of({ - data: { - event: { - __typename: "Event", - id: "1", - startDate: "2026-01-01", - endDate: null, - }, - }, - errors: [ - { - message: "Could not resolve endDate", - path: ["event", "endDate"], - }, - ], - }).pipe(delay(20)) - ), - }); - - using _disabledAct = disableActEnvironment(); - const { takeRender } = await renderUseBackgroundQuery( - () => useBackgroundQuery(query, { errorPolicy: "none" }), - { wrapper: createClientWrapper(client) } - ); - - { - const { renderedComponents } = await takeRender(); - - expect(renderedComponents).toStrictEqual([ - "useBackgroundQuery", - "", - ]); - } - - { - const { snapshot, renderedComponents } = await takeRender(); - - expect(renderedComponents).toStrictEqual([""]); - expect(snapshot).toStrictEqualTyped({ - error: new CombinedGraphQLErrors({ - data: { - event: { - __typename: "Event", - id: "1", - startDate: "2026-01-01", - endDate: null, - }, - }, - errors: [ - { - message: "Could not resolve endDate", - path: ["event", "endDate"], - }, - ], - }), - }); - } - - await expect(takeRender).not.toRerender(); -}); - -test("parses scalar fields in the result and serializes them in the error with an `all` error policy", async () => { - const query = gql` - query Event { - event { - id - startDate - endDate - } - } - `; - const client = new ApolloClient({ - cache: new InMemoryCache({ - scalars: { Date: dateScalar }, - typePolicies: { - Event: { - fields: { - startDate: { scalar: "Date" }, - endDate: { scalar: "Date" }, - }, - }, - }, - }), - link: new ApolloLink(() => - of({ - data: { - event: { - __typename: "Event", - id: "1", - startDate: "2026-01-01", - endDate: null, - }, - }, - errors: [ - { - message: "Could not resolve endDate", - path: ["event", "endDate"], - }, - ], - }).pipe(delay(20)) - ), - }); - - using _disabledAct = disableActEnvironment(); - const { takeRender } = await renderUseBackgroundQuery( - () => useBackgroundQuery(query, { errorPolicy: "all" }), - { wrapper: createClientWrapper(client) } - ); - - { - const { renderedComponents } = await takeRender(); - - expect(renderedComponents).toStrictEqual([ - "useBackgroundQuery", - "", - ]); - } - - { - const { snapshot, renderedComponents } = await takeRender(); - - expect(renderedComponents).toStrictEqual(["useReadQuery"]); - expect(snapshot).toStrictEqualTyped({ - data: { - event: { - __typename: "Event", - id: "1", - startDate: new Date(2026, 0, 1), - endDate: null, - }, - }, - dataState: "complete", - networkStatus: NetworkStatus.error, - error: new CombinedGraphQLErrors({ - data: { - event: { - __typename: "Event", - id: "1", - // TODO: Determine if this is correct - startDate: new Date(2026, 0, 1), - endDate: null, - }, - }, - errors: [ - { - message: "Could not resolve endDate", - path: ["event", "endDate"], - }, - ], - }), - }); - } - - await expect(takeRender).not.toRerender(); -}); - -test("parses custom scalar fields with an `ignore` error policy", async () => { - const query = gql` - query Event { - event { - id - startDate - endDate - } - } - `; - const client = new ApolloClient({ - cache: new InMemoryCache({ - scalars: { Date: dateScalar }, - typePolicies: { - Event: { - fields: { - startDate: { scalar: "Date" }, - endDate: { scalar: "Date" }, - }, - }, - }, - }), - link: new ApolloLink(() => - of({ - data: { - event: { - __typename: "Event", - id: "1", - startDate: "2026-01-01", - endDate: null, - }, - }, - errors: [ - { - message: "Could not resolve endDate", - path: ["event", "endDate"], - }, - ], - }).pipe(delay(20)) - ), - }); - - using _disabledAct = disableActEnvironment(); - const { takeRender } = await renderUseBackgroundQuery( - () => useBackgroundQuery(query, { errorPolicy: "ignore" }), - { wrapper: createClientWrapper(client) } - ); - - { - const { renderedComponents } = await takeRender(); - - expect(renderedComponents).toStrictEqual([ - "useBackgroundQuery", - "", - ]); - } - - { - const { snapshot, renderedComponents } = await takeRender(); - - expect(renderedComponents).toStrictEqual(["useReadQuery"]); - expect(snapshot).toStrictEqualTyped({ - data: { - event: { - __typename: "Event", - id: "1", - startDate: new Date(2026, 0, 1), - endDate: null, - }, - }, - dataState: "complete", - networkStatus: NetworkStatus.ready, - error: undefined, - }); - } - - await expect(takeRender).not.toRerender(); -}); - -test("parses custom scalar fields across `@defer` payloads (defer20220824)", async () => { - const link = mockDefer20220824(); - const client = new ApolloClient({ - cache: new InMemoryCache({ - scalars: { Date: dateScalar }, - typePolicies: { - Event: { - fields: { - startDate: { scalar: "Date" }, - endDate: { scalar: "Date" }, - }, - }, - }, - }), - link: link.httpLink, - incrementalHandler: new Defer20220824Handler(), - }); - const query = gql` - query Event { - event { - id - startDate - ... @defer { - endDate - } - } - } - `; - - using _disabledAct = disableActEnvironment(); - const { takeRender } = await renderUseBackgroundQuery( - () => useBackgroundQuery(query), - { wrapper: createClientWrapper(client) } - ); - - { - const { renderedComponents } = await takeRender(); - - expect(renderedComponents).toStrictEqual([ - "useBackgroundQuery", - "", - ]); - } - - link.enqueueInitialChunk({ - data: { - event: { - __typename: "Event", - id: "1", - startDate: "2026-01-01", - }, - }, - hasNext: true, - }); - - { - const { snapshot, renderedComponents } = await takeRender(); - - expect(renderedComponents).toStrictEqual(["useReadQuery"]); - expect(snapshot).toStrictEqualTyped({ - data: markAsStreaming({ - event: { - __typename: "Event", - id: "1", - startDate: new Date(2026, 0, 1), - }, - }), - dataState: "streaming", - networkStatus: NetworkStatus.streaming, - error: undefined, - }); - } - - link.enqueueSubsequentChunk({ - incremental: [{ data: { endDate: "2026-02-02" }, path: ["event"] }], - hasNext: false, - }); - - { - const { snapshot, renderedComponents } = await takeRender(); - - expect(renderedComponents).toStrictEqual(["useReadQuery"]); - expect(snapshot).toStrictEqualTyped({ - data: { - event: { - __typename: "Event", - id: "1", - startDate: new Date(2026, 0, 1), - endDate: new Date(2026, 1, 2), - }, - }, - dataState: "complete", - networkStatus: NetworkStatus.ready, - error: undefined, - }); - } - - await expect(takeRender).not.toRerender(); -}); - -test("parses custom scalar fields across `@defer` payloads (graphql17Alpha9)", async () => { - const link = mockDeferStreamGraphQL17Alpha9(); - const client = new ApolloClient({ - cache: new InMemoryCache({ - scalars: { Date: dateScalar }, - typePolicies: { - Event: { - fields: { - startDate: { scalar: "Date" }, - endDate: { scalar: "Date" }, - }, - }, - }, - }), - link: link.httpLink, - incrementalHandler: new GraphQL17Alpha9Handler(), - }); - const query = gql` - query Event { - event { - id - startDate - ... @defer { - endDate - } - } - } - `; - - using _disabledAct = disableActEnvironment(); - const { takeRender } = await renderUseBackgroundQuery( - () => useBackgroundQuery(query), - { wrapper: createClientWrapper(client) } - ); - - { - const { renderedComponents } = await takeRender(); - - expect(renderedComponents).toStrictEqual([ - "useBackgroundQuery", - "", - ]); - } - - link.enqueueInitialChunk({ - data: { - event: { - __typename: "Event", - id: "1", - startDate: "2026-01-01", - }, - }, - pending: [{ id: "0", path: ["event"] }], - hasNext: true, - }); - - { - const { snapshot, renderedComponents } = await takeRender(); - - expect(renderedComponents).toStrictEqual(["useReadQuery"]); - expect(snapshot).toStrictEqualTyped({ - data: markAsStreaming({ - event: { - __typename: "Event", - id: "1", - startDate: new Date(2026, 0, 1), - }, - }), - dataState: "streaming", - networkStatus: NetworkStatus.streaming, - error: undefined, - }); - } - - link.enqueueSubsequentChunk({ - incremental: [{ data: { endDate: "2026-02-02" }, id: "0" }], - completed: [{ id: "0" }], - hasNext: false, - }); - - { - const { snapshot, renderedComponents } = await takeRender(); - - expect(renderedComponents).toStrictEqual(["useReadQuery"]); - expect(snapshot).toStrictEqualTyped({ - data: { - event: { - __typename: "Event", - id: "1", - startDate: new Date(2026, 0, 1), - endDate: new Date(2026, 1, 2), - }, - }, - dataState: "complete", - networkStatus: NetworkStatus.ready, - error: undefined, - }); - } - - await expect(takeRender).not.toRerender(); -}); - -test("parses custom scalar fields across `@stream` payloads (defer20220824)", async () => { - const link = mockDefer20220824(); - const client = new ApolloClient({ - cache: new InMemoryCache({ - scalars: { Date: dateScalar }, - typePolicies: { - Event: { - fields: { - startDate: { scalar: "Date" }, - }, - }, - }, - }), - link: link.httpLink, - incrementalHandler: new Defer20220824Handler(), - }); - const query = gql` - query Events { - events @stream(initialCount: 1) { - id - startDate - } - } - `; - - using _disabledAct = disableActEnvironment(); - const { takeRender } = await renderUseBackgroundQuery( - () => useBackgroundQuery(query), - { wrapper: createClientWrapper(client) } - ); - - { - const { renderedComponents } = await takeRender(); - - expect(renderedComponents).toStrictEqual([ - "useBackgroundQuery", - "", - ]); - } - - link.enqueueInitialChunk({ - data: { - events: [ - { - __typename: "Event", - id: "1", - startDate: "2026-01-01", - }, - ], - }, - hasNext: true, - }); - - { - const { snapshot, renderedComponents } = await takeRender(); - - expect(renderedComponents).toStrictEqual(["useReadQuery"]); - expect(snapshot).toStrictEqualTyped({ - data: { - events: [ - { - __typename: "Event", - id: "1", - startDate: new Date(2026, 0, 1), - }, - ], - }, - dataState: "complete", - networkStatus: NetworkStatus.streaming, - error: undefined, - }); - } - - link.enqueueSubsequentChunk({ - incremental: [ - { - items: [ - { - __typename: "Event", - id: "2", - startDate: "2026-02-02", - }, - ] as any, - path: ["events", 1], - }, - ], - hasNext: false, - }); - - { - const { snapshot, renderedComponents } = await takeRender(); - - expect(renderedComponents).toStrictEqual(["useReadQuery"]); - expect(snapshot).toStrictEqualTyped({ - data: { - events: [ - { - __typename: "Event", - id: "1", - startDate: new Date(2026, 0, 1), - }, - { - __typename: "Event", - id: "2", - startDate: new Date(2026, 1, 2), - }, - ], - }, - dataState: "complete", - networkStatus: NetworkStatus.ready, - error: undefined, - }); - } - - await expect(takeRender).not.toRerender(); -}); - -test("parses custom scalar fields across `@stream` payloads (graphql17Alpha9)", async () => { - const link = mockDeferStreamGraphQL17Alpha9(); - const client = new ApolloClient({ - cache: new InMemoryCache({ - scalars: { Date: dateScalar }, - typePolicies: { - Event: { - fields: { - startDate: { scalar: "Date" }, - }, - }, - }, - }), - link: link.httpLink, - incrementalHandler: new GraphQL17Alpha9Handler(), - }); - const query = gql` - query Events { - events @stream(initialCount: 1) { - id - startDate - } - } - `; - - using _disabledAct = disableActEnvironment(); - const { takeRender } = await renderUseBackgroundQuery( - () => useBackgroundQuery(query), - { wrapper: createClientWrapper(client) } - ); - - { - const { renderedComponents } = await takeRender(); - - expect(renderedComponents).toStrictEqual([ - "useBackgroundQuery", - "", - ]); - } - - link.enqueueInitialChunk({ - data: { - events: [ - { - __typename: "Event", - id: "1", - startDate: "2026-01-01", - }, - ], - }, - pending: [{ id: "0", path: ["events"] }], - hasNext: true, - }); - - { - const { snapshot, renderedComponents } = await takeRender(); - - expect(renderedComponents).toStrictEqual(["useReadQuery"]); - expect(snapshot).toStrictEqualTyped({ - data: { - events: [ - { - __typename: "Event", - id: "1", - startDate: new Date(2026, 0, 1), - }, - ], - }, - dataState: "complete", - networkStatus: NetworkStatus.streaming, - error: undefined, - }); - } - - link.enqueueSubsequentChunk({ - incremental: [ - { - items: [ - { - __typename: "Event", - id: "2", - startDate: "2026-02-02", - }, - ] as any, - id: "0", - }, - ], - completed: [{ id: "0" }], - hasNext: false, - }); - - { - const { snapshot, renderedComponents } = await takeRender(); - - expect(renderedComponents).toStrictEqual(["useReadQuery"]); - expect(snapshot).toStrictEqualTyped({ - data: { - events: [ - { - __typename: "Event", - id: "1", - startDate: new Date(2026, 0, 1), - }, - { - __typename: "Event", - id: "2", - startDate: new Date(2026, 1, 2), - }, - ], - }, - dataState: "complete", - networkStatus: NetworkStatus.ready, - error: undefined, - }); - } - - await expect(takeRender).not.toRerender(); -}); diff --git a/src/react/hooks/__tests__/useLazyQuery/customScalars.test.tsx b/src/react/hooks/__tests__/useLazyQuery/customScalars.test.tsx index 2a30cfdf851..200a19e5b8a 100644 --- a/src/react/hooks/__tests__/useLazyQuery/customScalars.test.tsx +++ b/src/react/hooks/__tests__/useLazyQuery/customScalars.test.tsx @@ -5,13 +5,7 @@ import { import { delay, of } from "rxjs"; import type { OperationVariables } from "@apollo/client"; -import { - ApolloClient, - ApolloLink, - CombinedGraphQLErrors, - gql, - NetworkStatus, -} from "@apollo/client"; +import { ApolloClient, ApolloLink, gql, NetworkStatus } from "@apollo/client"; import { InMemoryCache } from "@apollo/client/cache"; import { useLazyQuery } from "@apollo/client/react"; import { @@ -19,1086 +13,36 @@ import { dateScalar, } from "@apollo/client/testing/internal"; -test("serializes scalar variables used in field arguments", async () => { - let requestVariables!: OperationVariables; - - const link = new ApolloLink((operation) => { - requestVariables = operation.variables; - - return of({ - data: { event: { __typename: "Event", name: "GraphQL Summit" } }, - }).pipe(delay(20)); - }); - - const client = new ApolloClient({ - cache: new InMemoryCache({ - scalars: { - Date: dateScalar, - }, - }), - link, - }); - const query = gql` - query Event($date: Date!) { - event(date: $date) { - name - } - } - `; - - using _disabledAct = disableActEnvironment(); - const { takeSnapshot, getCurrentSnapshot } = await renderHookToSnapshotStream( - () => useLazyQuery(query), - { wrapper: createClientWrapper(client) } - ); - - { - const [, result] = await takeSnapshot(); - - expect(result).toStrictEqualTyped({ - data: undefined, - dataState: "empty", - called: false, - loading: false, - networkStatus: NetworkStatus.ready, - previousData: undefined, - variables: {}, - }); - } - - const [execute] = getCurrentSnapshot(); - - await expect( - execute({ - variables: { - date: new Date(2026, 0, 1), - }, - }) - ).resolves.toStrictEqualTyped({ - data: { - event: { - __typename: "Event", - name: "GraphQL Summit", - }, - }, - }); - - { - const [, result] = await takeSnapshot(); - - expect(result).toStrictEqualTyped({ - data: undefined, - dataState: "empty", - called: true, - loading: true, - networkStatus: NetworkStatus.setVariables, - previousData: undefined, - variables: { - date: new Date(2026, 0, 1), - }, - }); - } - - { - const [, result] = await takeSnapshot(); - - expect(result).toStrictEqualTyped({ - data: { - event: { - __typename: "Event", - name: "GraphQL Summit", - }, - }, - dataState: "complete", - called: true, - loading: false, - networkStatus: NetworkStatus.ready, - previousData: undefined, - variables: { - date: new Date(2026, 0, 1), - }, - }); - } - - await expect(takeSnapshot).not.toRerender(); - - expect(requestVariables).toStrictEqualTyped({ date: "2026-01-01" }); -}); - -test("serializes scalar variables used in directive arguments", async () => { - let requestVariables!: OperationVariables; - - const link = new ApolloLink((operation) => { - requestVariables = operation.variables; - - return of({ - data: { event: { __typename: "Event", name: "GraphQL Summit" } }, - }).pipe(delay(20)); - }); - - const client = new ApolloClient({ - cache: new InMemoryCache({ - scalars: { - Date: dateScalar, - }, - }), - link, - }); - const query = gql` - query Event($date: Date!) { - event @on(date: $date) { - name - } - } - `; - - using _disabledAct = disableActEnvironment(); - const { takeSnapshot, getCurrentSnapshot } = await renderHookToSnapshotStream( - () => useLazyQuery(query), - { wrapper: createClientWrapper(client) } - ); - - { - const [, result] = await takeSnapshot(); - - expect(result).toStrictEqualTyped({ - data: undefined, - dataState: "empty", - called: false, - loading: false, - networkStatus: NetworkStatus.ready, - previousData: undefined, - variables: {}, - }); - } - - const [execute] = getCurrentSnapshot(); - - await expect( - execute({ - variables: { - date: new Date(2026, 0, 1), - }, - }) - ).resolves.toStrictEqualTyped({ - data: { - event: { - __typename: "Event", - name: "GraphQL Summit", - }, - }, - }); - - { - const [, result] = await takeSnapshot(); - - expect(result).toStrictEqualTyped({ - data: undefined, - dataState: "empty", - called: true, - loading: true, - networkStatus: NetworkStatus.setVariables, - previousData: undefined, - variables: { - date: new Date(2026, 0, 1), - }, - }); - } - - { - const [, result] = await takeSnapshot(); - - expect(result).toStrictEqualTyped({ - data: { - event: { - __typename: "Event", - name: "GraphQL Summit", - }, - }, - dataState: "complete", - called: true, - loading: false, - networkStatus: NetworkStatus.ready, - previousData: undefined, - variables: { - date: new Date(2026, 0, 1), - }, - }); - } - - await expect(takeSnapshot).not.toRerender(); - - expect(requestVariables).toStrictEqualTyped({ date: "2026-01-01" }); -}); - -test("serializes scalar fields in input object variables", async () => { - let requestVariables!: OperationVariables; - - const link = new ApolloLink((operation) => { - requestVariables = operation.variables; - - return of({ - data: { event: { __typename: "Event", name: "GraphQL Summit" } }, - }).pipe(delay(20)); - }); - - const client = new ApolloClient({ - cache: new InMemoryCache({ - scalars: { - Date: dateScalar, - }, - inputObjects: { - EventFilter: { - fields: { - date: "Date", - }, - }, - }, - }), - link, - }); - const query = gql` - query Event($filter: EventFilter!) { - event(filter: $filter) { - name - } - } - `; - - using _disabledAct = disableActEnvironment(); - const { takeSnapshot, getCurrentSnapshot } = await renderHookToSnapshotStream( - () => useLazyQuery(query), - { wrapper: createClientWrapper(client) } - ); - - { - const [, result] = await takeSnapshot(); - - expect(result).toStrictEqualTyped({ - data: undefined, - dataState: "empty", - called: false, - loading: false, - networkStatus: NetworkStatus.ready, - previousData: undefined, - variables: {}, - }); - } - - const [execute] = getCurrentSnapshot(); - - await expect( - execute({ - variables: { - filter: { - date: new Date(2026, 0, 1), - }, - }, - }) - ).resolves.toStrictEqualTyped({ - data: { - event: { - __typename: "Event", - name: "GraphQL Summit", - }, - }, - }); - - { - const [, result] = await takeSnapshot(); - - expect(result).toStrictEqualTyped({ - data: undefined, - dataState: "empty", - called: true, - loading: true, - networkStatus: NetworkStatus.setVariables, - previousData: undefined, - variables: { - filter: { - date: new Date(2026, 0, 1), - }, - }, - }); - } - - { - const [, result] = await takeSnapshot(); - - expect(result).toStrictEqualTyped({ - data: { - event: { - __typename: "Event", - name: "GraphQL Summit", - }, - }, - dataState: "complete", - called: true, - loading: false, - networkStatus: NetworkStatus.ready, - previousData: undefined, - variables: { - filter: { - date: new Date(2026, 0, 1), - }, - }, - }); - } - - await expect(takeSnapshot).not.toRerender(); - - expect(requestVariables).toStrictEqualTyped({ - filter: { - date: "2026-01-01", - }, - }); -}); - -test("parses cached custom scalar fields with a cache-only fetch policy", async () => { - const query = gql` - query Event { - event { - id - startDate - } - } - `; - const client = new ApolloClient({ - cache: new InMemoryCache({ - scalars: { Date: dateScalar }, - typePolicies: { - Event: { - fields: { - startDate: { scalar: "Date" }, - }, - }, - }, - }), - link: ApolloLink.empty(), - }); - client.writeQuery({ - query, - data: { - event: { - __typename: "Event", - id: "1", - startDate: "2026-01-01", - }, - }, - }); - - using _disabledAct = disableActEnvironment(); - const { takeSnapshot, getCurrentSnapshot } = await renderHookToSnapshotStream( - () => useLazyQuery(query, { fetchPolicy: "cache-only" }), - { wrapper: createClientWrapper(client) } - ); - - { - const [, result] = await takeSnapshot(); - - expect(result).toStrictEqualTyped({ - data: undefined, - dataState: "empty", - called: false, - loading: false, - networkStatus: NetworkStatus.ready, - previousData: undefined, - variables: {}, - }); - } - - const [execute] = getCurrentSnapshot(); - - await expect(execute()).resolves.toStrictEqualTyped({ - data: { - event: { - __typename: "Event", - id: "1", - startDate: new Date(2026, 0, 1), - }, - }, - }); - - { - const [, result] = await takeSnapshot(); - - expect(result).toStrictEqualTyped({ - data: { - event: { - __typename: "Event", - id: "1", - startDate: new Date(2026, 0, 1), - }, - }, - dataState: "complete", - called: true, - loading: false, - networkStatus: NetworkStatus.ready, - previousData: undefined, - variables: {}, - }); - } - - await expect(takeSnapshot).not.toRerender(); -}); - -test("parses cached custom scalar fields with a cache-first fetch policy", async () => { - const query = gql` - query Event { - event { - id - startDate - } - } - `; - const client = new ApolloClient({ - cache: new InMemoryCache({ - scalars: { Date: dateScalar }, - typePolicies: { - Event: { - fields: { - startDate: { scalar: "Date" }, - }, - }, - }, - }), - link: ApolloLink.empty(), - }); - client.writeQuery({ - query, - data: { - event: { - __typename: "Event", - id: "1", - startDate: "2026-01-01", - }, - }, - }); - - using _disabledAct = disableActEnvironment(); - const { takeSnapshot, getCurrentSnapshot } = await renderHookToSnapshotStream( - () => useLazyQuery(query, { fetchPolicy: "cache-first" }), - { wrapper: createClientWrapper(client) } - ); - - { - const [, result] = await takeSnapshot(); - - expect(result).toStrictEqualTyped({ - data: undefined, - dataState: "empty", - called: false, - loading: false, - networkStatus: NetworkStatus.ready, - previousData: undefined, - variables: {}, - }); - } - - const [execute] = getCurrentSnapshot(); - - await expect(execute()).resolves.toStrictEqualTyped({ - data: { - event: { - __typename: "Event", - id: "1", - startDate: new Date(2026, 0, 1), - }, - }, - }); - - { - const [, result] = await takeSnapshot(); - - expect(result).toStrictEqualTyped({ - data: { - event: { - __typename: "Event", - id: "1", - startDate: new Date(2026, 0, 1), - }, - }, - dataState: "complete", - called: true, - loading: false, - networkStatus: NetworkStatus.ready, - previousData: undefined, - variables: {}, - }); - } - - await expect(takeSnapshot).not.toRerender(); -}); - -test("parses network custom scalar fields with a cache-first fetch policy", async () => { - const query = gql` - query Event { - event { - id - startDate - } - } - `; - const client = new ApolloClient({ - cache: new InMemoryCache({ - scalars: { Date: dateScalar }, - typePolicies: { - Event: { - fields: { - startDate: { scalar: "Date" }, - }, - }, - }, - }), - link: new ApolloLink(() => - of({ - data: { - event: { - __typename: "Event", - id: "1", - startDate: "2026-01-01", - }, - }, - }).pipe(delay(20)) - ), - }); - - using _disabledAct = disableActEnvironment(); - const { takeSnapshot, getCurrentSnapshot } = await renderHookToSnapshotStream( - () => useLazyQuery(query, { fetchPolicy: "cache-first" }), - { wrapper: createClientWrapper(client) } - ); - - { - const [, result] = await takeSnapshot(); - - expect(result).toStrictEqualTyped({ - data: undefined, - dataState: "empty", - called: false, - loading: false, - networkStatus: NetworkStatus.ready, - previousData: undefined, - variables: {}, - }); - } - - const [execute] = getCurrentSnapshot(); - - await expect(execute()).resolves.toStrictEqualTyped({ - data: { - event: { - __typename: "Event", - id: "1", - startDate: new Date(2026, 0, 1), - }, - }, - }); - - { - const [, result] = await takeSnapshot(); - - expect(result).toStrictEqualTyped({ - data: undefined, - dataState: "empty", - called: true, - loading: true, - networkStatus: NetworkStatus.loading, - previousData: undefined, - variables: {}, - }); - } - - { - const [, result] = await takeSnapshot(); - - expect(result).toStrictEqualTyped({ - data: { - event: { - __typename: "Event", - id: "1", - startDate: new Date(2026, 0, 1), - }, - }, - dataState: "complete", - called: true, - loading: false, - networkStatus: NetworkStatus.ready, - previousData: undefined, - variables: {}, - }); - } - - await expect(takeSnapshot).not.toRerender(); -}); - -test("parses cached and network custom scalar fields with a cache-and-network fetch policy", async () => { - const query = gql` - query Event { - event { - id - startDate - } - } - `; - const client = new ApolloClient({ - cache: new InMemoryCache({ - scalars: { Date: dateScalar }, - typePolicies: { - Event: { - fields: { - startDate: { scalar: "Date" }, - }, - }, - }, - }), - link: new ApolloLink(() => - of({ - data: { - event: { - __typename: "Event", - id: "1", - startDate: "2026-02-02", - }, - }, - }).pipe(delay(20)) - ), - }); - client.writeQuery({ - query, - data: { - event: { - __typename: "Event", - id: "1", - startDate: "2026-01-01", - }, - }, - }); - - using _disabledAct = disableActEnvironment(); - const { takeSnapshot, getCurrentSnapshot } = await renderHookToSnapshotStream( - () => useLazyQuery(query, { fetchPolicy: "cache-and-network" }), - { wrapper: createClientWrapper(client) } - ); - - { - const [, result] = await takeSnapshot(); - - expect(result).toStrictEqualTyped({ - data: undefined, - dataState: "empty", - called: false, - loading: false, - networkStatus: NetworkStatus.ready, - previousData: undefined, - variables: {}, - }); - } - - const [execute] = getCurrentSnapshot(); - - await expect(execute()).resolves.toStrictEqualTyped({ - data: { - event: { - __typename: "Event", - id: "1", - startDate: new Date(2026, 1, 2), - }, - }, - }); - - { - const [, result] = await takeSnapshot(); - - expect(result).toStrictEqualTyped({ - data: { - event: { - __typename: "Event", - id: "1", - startDate: new Date(2026, 0, 1), - }, - }, - dataState: "complete", - called: true, - loading: true, - networkStatus: NetworkStatus.loading, - previousData: undefined, - variables: {}, - }); - } - - { - const [, result] = await takeSnapshot(); - - expect(result).toStrictEqualTyped({ - data: { - event: { - __typename: "Event", - id: "1", - startDate: new Date(2026, 1, 2), - }, - }, - dataState: "complete", - called: true, - loading: false, - networkStatus: NetworkStatus.ready, - previousData: { - event: { - __typename: "Event", - id: "1", - startDate: new Date(2026, 0, 1), - }, - }, - variables: {}, - }); - } - - await expect(takeSnapshot).not.toRerender(); -}); - -test("parses network custom scalar fields with a cache-and-network fetch policy", async () => { - const query = gql` - query Event { - event { - id - startDate - } - } - `; - const client = new ApolloClient({ - cache: new InMemoryCache({ - scalars: { Date: dateScalar }, - typePolicies: { - Event: { - fields: { - startDate: { scalar: "Date" }, - }, - }, - }, - }), - link: new ApolloLink(() => - of({ - data: { - event: { - __typename: "Event", - id: "1", - startDate: "2026-01-01", - }, - }, - }).pipe(delay(20)) - ), - }); - - using _disabledAct = disableActEnvironment(); - const { takeSnapshot, getCurrentSnapshot } = await renderHookToSnapshotStream( - () => useLazyQuery(query, { fetchPolicy: "cache-and-network" }), - { wrapper: createClientWrapper(client) } - ); - - { - const [, result] = await takeSnapshot(); - - expect(result).toStrictEqualTyped({ - data: undefined, - dataState: "empty", - called: false, - loading: false, - networkStatus: NetworkStatus.ready, - previousData: undefined, - variables: {}, - }); - } - - const [execute] = getCurrentSnapshot(); - - await expect(execute()).resolves.toStrictEqualTyped({ - data: { - event: { - __typename: "Event", - id: "1", - startDate: new Date(2026, 0, 1), - }, - }, - }); - - { - const [, result] = await takeSnapshot(); - - expect(result).toStrictEqualTyped({ - data: undefined, - dataState: "empty", - called: true, - loading: true, - networkStatus: NetworkStatus.loading, - previousData: undefined, - variables: {}, - }); - } - - { - const [, result] = await takeSnapshot(); - - expect(result).toStrictEqualTyped({ - data: { - event: { - __typename: "Event", - id: "1", - startDate: new Date(2026, 0, 1), - }, - }, - dataState: "complete", - called: true, - loading: false, - networkStatus: NetworkStatus.ready, - previousData: undefined, - variables: {}, - }); - } - - await expect(takeSnapshot).not.toRerender(); -}); - -test("parses network custom scalar fields with a network-only fetch policy", async () => { - const query = gql` - query Event { - event { - id - startDate - } - } - `; - const client = new ApolloClient({ - cache: new InMemoryCache({ - scalars: { Date: dateScalar }, - typePolicies: { - Event: { - fields: { - startDate: { scalar: "Date" }, - }, - }, - }, - }), - link: new ApolloLink(() => - of({ - data: { - event: { - __typename: "Event", - id: "1", - startDate: "2026-01-01", - }, - }, - }).pipe(delay(20)) - ), - }); - - using _disabledAct = disableActEnvironment(); - const { takeSnapshot, getCurrentSnapshot } = await renderHookToSnapshotStream( - () => useLazyQuery(query, { fetchPolicy: "network-only" }), - { wrapper: createClientWrapper(client) } - ); - - { - const [, result] = await takeSnapshot(); - - expect(result).toStrictEqualTyped({ - data: undefined, - dataState: "empty", - called: false, - loading: false, - networkStatus: NetworkStatus.ready, - previousData: undefined, - variables: {}, - }); - } - - const [execute] = getCurrentSnapshot(); - - await expect(execute()).resolves.toStrictEqualTyped({ - data: { - event: { - __typename: "Event", - id: "1", - startDate: new Date(2026, 0, 1), - }, - }, - }); - - { - const [, result] = await takeSnapshot(); - - expect(result).toStrictEqualTyped({ - data: undefined, - dataState: "empty", - called: true, - loading: true, - networkStatus: NetworkStatus.loading, - previousData: undefined, - variables: {}, - }); - } - - { - const [, result] = await takeSnapshot(); - - expect(result).toStrictEqualTyped({ - data: { - event: { - __typename: "Event", - id: "1", - startDate: new Date(2026, 0, 1), - }, - }, - dataState: "complete", - called: true, - loading: false, - networkStatus: NetworkStatus.ready, - previousData: undefined, - variables: {}, - }); - } - - await expect(takeSnapshot).not.toRerender(); -}); - -test.failing( - "parses custom scalar fields with a no-cache fetch policy", - async () => { - const query = gql` - query Event { - event { - id - startDate - } - } - `; - const client = new ApolloClient({ - cache: new InMemoryCache({ - scalars: { Date: dateScalar }, - typePolicies: { - Event: { - fields: { - startDate: { scalar: "Date" }, - }, - }, - }, - }), - link: new ApolloLink(() => - of({ - data: { - event: { - __typename: "Event", - id: "1", - startDate: "2026-01-01", - }, - }, - }).pipe(delay(20)) - ), - }); - - using _disabledAct = disableActEnvironment(); - const { takeSnapshot, getCurrentSnapshot } = - await renderHookToSnapshotStream( - () => useLazyQuery(query, { fetchPolicy: "no-cache" }), - { wrapper: createClientWrapper(client) } - ); - - { - const [, result] = await takeSnapshot(); - - expect(result).toStrictEqualTyped({ - data: undefined, - dataState: "empty", - called: false, - loading: false, - networkStatus: NetworkStatus.ready, - previousData: undefined, - variables: {}, - }); - } - const [execute] = getCurrentSnapshot(); - - await expect(execute()).resolves.toStrictEqualTyped({ - data: { - event: { - __typename: "Event", - id: "1", - startDate: new Date(2026, 0, 1), - }, - }, - }); - - { - const [, result] = await takeSnapshot(); - - expect(result).toStrictEqualTyped({ - data: undefined, - dataState: "empty", - called: true, - loading: true, - networkStatus: NetworkStatus.loading, - previousData: undefined, - variables: {}, - }); - } +test("serializes parsed scalar values in variables", async () => { + let requestVariables!: OperationVariables; - { - const [, result] = await takeSnapshot(); + const link = new ApolloLink((operation) => { + requestVariables = operation.variables; - expect(result).toStrictEqualTyped({ - data: { - event: { - __typename: "Event", - id: "1", - startDate: new Date(2026, 0, 1), - }, - }, - dataState: "complete", - called: true, - loading: false, - networkStatus: NetworkStatus.ready, - previousData: undefined, - variables: {}, - }); - } - } -); + return of({ + data: { event: { __typename: "Event", name: "GraphQL Summit" } }, + }).pipe(delay(20)); + }); -test("preserves referential identity when re-executing with identical serialized scalar values", async () => { - const query = gql` - query Event { - event { - id - startDate - } - } - `; const client = new ApolloClient({ cache: new InMemoryCache({ - scalars: { Date: dateScalar }, - typePolicies: { - Event: { - fields: { - startDate: { scalar: "Date" }, - }, - }, + scalars: { + Date: dateScalar, }, }), - link: new ApolloLink(() => - of({ - data: { - event: { - __typename: "Event", - id: "1", - startDate: "2026-01-01", - }, - }, - }).pipe(delay(20)) - ), + link, }); + const query = gql` + query Event($date: Date!) { + event(date: $date) { + name + } + } + `; using _disabledAct = disableActEnvironment(); const { takeSnapshot, getCurrentSnapshot } = await renderHookToSnapshotStream( - () => useLazyQuery(query, { fetchPolicy: "network-only" }), + () => useLazyQuery(query), { wrapper: createClientWrapper(client) } ); @@ -1118,12 +62,17 @@ test("preserves referential identity when re-executing with identical serialized const [execute] = getCurrentSnapshot(); - await expect(execute()).resolves.toStrictEqualTyped({ + await expect( + execute({ + variables: { + date: new Date(2026, 0, 1), + }, + }) + ).resolves.toStrictEqualTyped({ data: { event: { __typename: "Event", - id: "1", - startDate: new Date(2026, 0, 1), + name: "GraphQL Summit", }, }, }); @@ -1136,62 +85,11 @@ test("preserves referential identity when re-executing with identical serialized dataState: "empty", called: true, loading: true, - networkStatus: NetworkStatus.loading, - previousData: undefined, - variables: {}, - }); - } - - let previousData: unknown; - { - const [, result] = await takeSnapshot(); - - expect(result).toStrictEqualTyped({ - data: { - event: { - __typename: "Event", - id: "1", - startDate: new Date(2026, 0, 1), - }, - }, - dataState: "complete", - called: true, - loading: false, - networkStatus: NetworkStatus.ready, + networkStatus: NetworkStatus.setVariables, previousData: undefined, - variables: {}, - }); - - previousData = result.data; - } - - await expect(execute()).resolves.toStrictEqualTyped({ - data: { - event: { - __typename: "Event", - id: "1", - startDate: new Date(2026, 0, 1), - }, - }, - }); - - { - const [, result] = await takeSnapshot(); - - expect(result).toStrictEqualTyped({ - data: { - event: { - __typename: "Event", - id: "1", - startDate: new Date(2026, 0, 1), - }, + variables: { + date: new Date(2026, 0, 1), }, - dataState: "complete", - called: true, - loading: true, - networkStatus: NetworkStatus.loading, - previousData: undefined, - variables: {}, }); } @@ -1202,8 +100,7 @@ test("preserves referential identity when re-executing with identical serialized data: { event: { __typename: "Event", - id: "1", - startDate: new Date(2026, 0, 1), + name: "GraphQL Summit", }, }, dataState: "complete", @@ -1211,22 +108,23 @@ test("preserves referential identity when re-executing with identical serialized loading: false, networkStatus: NetworkStatus.ready, previousData: undefined, - variables: {}, + variables: { + date: new Date(2026, 0, 1), + }, }); - - expect(result.data).toBe(previousData); } await expect(takeSnapshot).not.toRerender(); + + expect(requestVariables).toStrictEqualTyped({ date: "2026-01-01" }); }); -test("serializes scalar fields in the error with a `none` error policy", async () => { +test("returns parsed scalar fields", async () => { const query = gql` query Event { event { id startDate - endDate } } `; @@ -1237,7 +135,6 @@ test("serializes scalar fields in the error with a `none` error policy", async ( Event: { fields: { startDate: { scalar: "Date" }, - endDate: { scalar: "Date" }, }, }, }, @@ -1249,22 +146,15 @@ test("serializes scalar fields in the error with a `none` error policy", async ( __typename: "Event", id: "1", startDate: "2026-01-01", - endDate: null, }, }, - errors: [ - { - message: "Could not resolve endDate", - path: ["event", "endDate"], - }, - ], }).pipe(delay(20)) ), }); using _disabledAct = disableActEnvironment(); const { takeSnapshot, getCurrentSnapshot } = await renderHookToSnapshotStream( - () => useLazyQuery(query, { errorPolicy: "none" }), + () => useLazyQuery(query), { wrapper: createClientWrapper(client) } ); @@ -1284,24 +174,15 @@ test("serializes scalar fields in the error with a `none` error policy", async ( const [execute] = getCurrentSnapshot(); - await expect(execute()).rejects.toEqual( - new CombinedGraphQLErrors({ - data: { - event: { - __typename: "Event", - id: "1", - startDate: "2026-01-01", - endDate: null, - }, + await expect(execute()).resolves.toStrictEqualTyped({ + data: { + event: { + __typename: "Event", + id: "1", + startDate: new Date(2026, 0, 1), }, - errors: [ - { - message: "Could not resolve endDate", - path: ["event", "endDate"], - }, - ], - }) - ); + }, + }); { const [, result] = await takeSnapshot(); @@ -1321,27 +202,17 @@ test("serializes scalar fields in the error with a `none` error policy", async ( const [, result] = await takeSnapshot(); expect(result).toStrictEqualTyped({ - data: undefined, - dataState: "empty", - error: new CombinedGraphQLErrors({ - data: { - event: { - __typename: "Event", - id: "1", - startDate: "2026-01-01", - endDate: null, - }, + data: { + event: { + __typename: "Event", + id: "1", + startDate: new Date(2026, 0, 1), }, - errors: [ - { - message: "Could not resolve endDate", - path: ["event", "endDate"], - }, - ], - }), + }, + dataState: "complete", called: true, loading: false, - networkStatus: NetworkStatus.error, + networkStatus: NetworkStatus.ready, previousData: undefined, variables: {}, }); @@ -1350,13 +221,12 @@ test("serializes scalar fields in the error with a `none` error policy", async ( await expect(takeSnapshot).not.toRerender(); }); -test("parses scalar fields in the result and serializes them in the error with an `all` error policy", async () => { +test("preserves referential identity when re-executing with identical scalar values", async () => { const query = gql` query Event { event { id startDate - endDate } } `; @@ -1367,7 +237,6 @@ test("parses scalar fields in the result and serializes them in the error with a Event: { fields: { startDate: { scalar: "Date" }, - endDate: { scalar: "Date" }, }, }, }, @@ -1379,22 +248,15 @@ test("parses scalar fields in the result and serializes them in the error with a __typename: "Event", id: "1", startDate: "2026-01-01", - endDate: null, }, }, - errors: [ - { - message: "Could not resolve endDate", - path: ["event", "endDate"], - }, - ], }).pipe(delay(20)) ), }); using _disabledAct = disableActEnvironment(); const { takeSnapshot, getCurrentSnapshot } = await renderHookToSnapshotStream( - () => useLazyQuery(query, { errorPolicy: "all" }), + () => useLazyQuery(query, { fetchPolicy: "network-only" }), { wrapper: createClientWrapper(client) } ); @@ -1420,26 +282,8 @@ test("parses scalar fields in the result and serializes them in the error with a __typename: "Event", id: "1", startDate: new Date(2026, 0, 1), - endDate: null, }, }, - error: new CombinedGraphQLErrors({ - data: { - event: { - __typename: "Event", - id: "1", - // TODO: Determine if this is correct - startDate: new Date(2026, 0, 1), - endDate: null, - }, - }, - errors: [ - { - message: "Could not resolve endDate", - path: ["event", "endDate"], - }, - ], - }), }); { @@ -1456,6 +300,7 @@ test("parses scalar fields in the result and serializes them in the error with a }); } + let previousData: unknown; { const [, result] = await takeSnapshot(); @@ -1465,101 +310,18 @@ test("parses scalar fields in the result and serializes them in the error with a __typename: "Event", id: "1", startDate: new Date(2026, 0, 1), - endDate: null, }, }, dataState: "complete", - error: new CombinedGraphQLErrors({ - data: { - event: { - __typename: "Event", - id: "1", - // TODO: Determine if this is correct - startDate: new Date(2026, 0, 1), - endDate: null, - }, - }, - errors: [ - { - message: "Could not resolve endDate", - path: ["event", "endDate"], - }, - ], - }), called: true, loading: false, - networkStatus: NetworkStatus.error, - previousData: undefined, - variables: {}, - }); - } - - await expect(takeSnapshot).not.toRerender(); -}); - -test("parses custom scalar fields with an `ignore` error policy", async () => { - const query = gql` - query Event { - event { - id - startDate - endDate - } - } - `; - const client = new ApolloClient({ - cache: new InMemoryCache({ - scalars: { Date: dateScalar }, - typePolicies: { - Event: { - fields: { - startDate: { scalar: "Date" }, - endDate: { scalar: "Date" }, - }, - }, - }, - }), - link: new ApolloLink(() => - of({ - data: { - event: { - __typename: "Event", - id: "1", - startDate: "2026-01-01", - endDate: null, - }, - }, - errors: [ - { - message: "Could not resolve endDate", - path: ["event", "endDate"], - }, - ], - }).pipe(delay(20)) - ), - }); - - using _disabledAct = disableActEnvironment(); - const { takeSnapshot, getCurrentSnapshot } = await renderHookToSnapshotStream( - () => useLazyQuery(query, { errorPolicy: "ignore" }), - { wrapper: createClientWrapper(client) } - ); - - { - const [, result] = await takeSnapshot(); - - expect(result).toStrictEqualTyped({ - data: undefined, - dataState: "empty", - called: false, - loading: false, networkStatus: NetworkStatus.ready, previousData: undefined, variables: {}, }); - } - const [execute] = getCurrentSnapshot(); + previousData = result.data; + } await expect(execute()).resolves.toStrictEqualTyped({ data: { @@ -1567,7 +329,6 @@ test("parses custom scalar fields with an `ignore` error policy", async () => { __typename: "Event", id: "1", startDate: new Date(2026, 0, 1), - endDate: null, }, }, }); @@ -1576,8 +337,14 @@ test("parses custom scalar fields with an `ignore` error policy", async () => { const [, result] = await takeSnapshot(); expect(result).toStrictEqualTyped({ - data: undefined, - dataState: "empty", + data: { + event: { + __typename: "Event", + id: "1", + startDate: new Date(2026, 0, 1), + }, + }, + dataState: "complete", called: true, loading: true, networkStatus: NetworkStatus.loading, @@ -1595,7 +362,6 @@ test("parses custom scalar fields with an `ignore` error policy", async () => { __typename: "Event", id: "1", startDate: new Date(2026, 0, 1), - endDate: null, }, }, dataState: "complete", @@ -1605,6 +371,8 @@ test("parses custom scalar fields with an `ignore` error policy", async () => { previousData: undefined, variables: {}, }); + + expect(result.data).toBe(previousData); } await expect(takeSnapshot).not.toRerender(); diff --git a/src/react/hooks/__tests__/useLoadableQuery/customScalars.test.tsx b/src/react/hooks/__tests__/useLoadableQuery/customScalars.test.tsx index f06d1a9df92..d657835799f 100644 --- a/src/react/hooks/__tests__/useLoadableQuery/customScalars.test.tsx +++ b/src/react/hooks/__tests__/useLoadableQuery/customScalars.test.tsx @@ -2,32 +2,18 @@ import { disableActEnvironment } from "@testing-library/react-render-stream"; import { delay, of } from "rxjs"; import type { OperationVariables } from "@apollo/client"; -import { - ApolloClient, - ApolloLink, - CombinedGraphQLErrors, - gql, - NetworkStatus, -} from "@apollo/client"; +import { ApolloClient, ApolloLink, gql, NetworkStatus } from "@apollo/client"; import { InMemoryCache } from "@apollo/client/cache"; -import { - Defer20220824Handler, - GraphQL17Alpha9Handler, -} from "@apollo/client/incremental"; import { useLoadableQuery } from "@apollo/client/react"; import { createClientWrapper, dateScalar, - markAsStreaming, - mockDefer20220824, - mockDeferStreamGraphQL17Alpha9, - spyOnConsole, } from "@apollo/client/testing/internal"; import { invariant } from "@apollo/client/utilities/invariant"; import { renderUseLoadableQueryHook } from "./testUtils.js"; -test("serializes scalar variables used in field arguments", async () => { +test("serializes parsed scalar values in variables", async () => { let requestVariables!: OperationVariables; const client = new ApolloClient({ @@ -96,106 +82,39 @@ test("serializes scalar variables used in field arguments", async () => { expect(requestVariables).toStrictEqualTyped({ date: "2026-01-01" }); }); -test("serializes scalar variables used in directive arguments", async () => { - let requestVariables!: OperationVariables; - - const client = new ApolloClient({ - cache: new InMemoryCache({ - scalars: { Date: dateScalar }, - }), - link: new ApolloLink((operation) => { - requestVariables = operation.variables; - - return of({ - data: { event: { __typename: "Event", name: "GraphQL Summit" } }, - }).pipe(delay(20)); - }), - }); - +test("returns parsed scalar fields", async () => { const query = gql` - query Event($date: Date!) { - event @on(date: $date) { - name + query Event { + event { + id + startDate } } `; - - using _disabledAct = disableActEnvironment(); - const { takeRender, loadQuery } = await renderUseLoadableQueryHook( - () => useLoadableQuery(query), - { wrapper: createClientWrapper(client) } - ); - - { - const { renderedComponents } = await takeRender(); - - expect(renderedComponents).toStrictEqual(["useLoadableQuery"]); - } - - loadQuery({ date: new Date(2026, 0, 1) }); - - { - const { renderedComponents } = await takeRender(); - - expect(renderedComponents).toStrictEqual([ - "useLoadableQuery", - "", - ]); - } - - { - const { snapshot, renderedComponents } = await takeRender(); - - invariant("result" in snapshot); - expect(renderedComponents).toStrictEqual(["useReadQuery"]); - expect(snapshot.result).toStrictEqualTyped({ - data: { - event: { - __typename: "Event", - name: "GraphQL Summit", - }, - }, - dataState: "complete", - networkStatus: NetworkStatus.ready, - error: undefined, - }); - } - - await expect(takeRender).not.toRerender(); - expect(requestVariables).toStrictEqualTyped({ date: "2026-01-01" }); -}); - -test("serializes scalar fields in input object variables", async () => { - let requestVariables!: OperationVariables; - const client = new ApolloClient({ cache: new InMemoryCache({ scalars: { Date: dateScalar }, - inputObjects: { - EventFilter: { + typePolicies: { + Event: { fields: { - date: "Date", + startDate: { scalar: "Date" }, }, }, }, }), - link: new ApolloLink((operation) => { - requestVariables = operation.variables; - - return of({ - data: { event: { __typename: "Event", name: "GraphQL Summit" } }, - }).pipe(delay(20)); - }), + link: new ApolloLink(() => + of({ + data: { + event: { + __typename: "Event", + id: "1", + startDate: "2026-01-01", + }, + }, + }).pipe(delay(20)) + ), }); - const query = gql` - query Event($filter: EventFilter!) { - event(filter: $filter) { - name - } - } - `; - using _disabledAct = disableActEnvironment(); const { takeRender, loadQuery } = await renderUseLoadableQueryHook( () => useLoadableQuery(query), @@ -208,7 +127,7 @@ test("serializes scalar fields in input object variables", async () => { expect(renderedComponents).toStrictEqual(["useLoadableQuery"]); } - loadQuery({ filter: { date: new Date(2026, 0, 1) } }); + loadQuery(); { const { renderedComponents } = await takeRender(); @@ -228,7 +147,8 @@ test("serializes scalar fields in input object variables", async () => { data: { event: { __typename: "Event", - name: "GraphQL Summit", + id: "1", + startDate: new Date(2026, 0, 1), }, }, dataState: "complete", @@ -238,12 +158,9 @@ test("serializes scalar fields in input object variables", async () => { } await expect(takeRender).not.toRerender(); - expect(requestVariables).toStrictEqualTyped({ - filter: { date: "2026-01-01" }, - }); }); -test("preserves referential identity when refetching identical serialized scalar values", async () => { +test("preserves referential identity when refetching identical scalar values", async () => { const query = gql` query Event { event { @@ -363,766 +280,3 @@ test("preserves referential identity when refetching identical serialized scalar await expect(takeRender).not.toRerender(); }); - -test("serializes scalar fields in the error with a `none` error policy", async () => { - using _consoleSpy = spyOnConsole("error"); - const query = gql` - query Event { - event { - id - startDate - endDate - } - } - `; - const client = new ApolloClient({ - cache: new InMemoryCache({ - scalars: { Date: dateScalar }, - typePolicies: { - Event: { - fields: { - startDate: { scalar: "Date" }, - endDate: { scalar: "Date" }, - }, - }, - }, - }), - link: new ApolloLink(() => - of({ - data: { - event: { - __typename: "Event", - id: "1", - startDate: "2026-01-01", - endDate: null, - }, - }, - errors: [ - { - message: "Could not resolve endDate", - path: ["event", "endDate"], - }, - ], - }).pipe(delay(20)) - ), - }); - - using _disabledAct = disableActEnvironment(); - const { takeRender, loadQuery } = await renderUseLoadableQueryHook( - () => useLoadableQuery(query, { errorPolicy: "none" }), - { wrapper: createClientWrapper(client) } - ); - - { - const { renderedComponents } = await takeRender(); - - expect(renderedComponents).toStrictEqual(["useLoadableQuery"]); - } - - loadQuery(); - - { - const { renderedComponents } = await takeRender(); - - expect(renderedComponents).toStrictEqual([ - "useLoadableQuery", - "", - ]); - } - - { - const { snapshot, renderedComponents } = await takeRender(); - - expect(renderedComponents).toStrictEqual([""]); - expect(snapshot).toStrictEqualTyped({ - error: new CombinedGraphQLErrors({ - data: { - event: { - __typename: "Event", - id: "1", - startDate: "2026-01-01", - endDate: null, - }, - }, - errors: [ - { - message: "Could not resolve endDate", - path: ["event", "endDate"], - }, - ], - }), - }); - } - - await expect(takeRender).not.toRerender(); -}); - -test("parses scalar fields in the result and serializes them in the error with an `all` error policy", async () => { - const query = gql` - query Event { - event { - id - startDate - endDate - } - } - `; - const client = new ApolloClient({ - cache: new InMemoryCache({ - scalars: { Date: dateScalar }, - typePolicies: { - Event: { - fields: { - startDate: { scalar: "Date" }, - endDate: { scalar: "Date" }, - }, - }, - }, - }), - link: new ApolloLink(() => - of({ - data: { - event: { - __typename: "Event", - id: "1", - startDate: "2026-01-01", - endDate: null, - }, - }, - errors: [ - { - message: "Could not resolve endDate", - path: ["event", "endDate"], - }, - ], - }).pipe(delay(20)) - ), - }); - - using _disabledAct = disableActEnvironment(); - const { takeRender, loadQuery } = await renderUseLoadableQueryHook( - () => useLoadableQuery(query, { errorPolicy: "all" }), - { wrapper: createClientWrapper(client) } - ); - - { - const { renderedComponents } = await takeRender(); - - expect(renderedComponents).toStrictEqual(["useLoadableQuery"]); - } - - loadQuery(); - - { - const { renderedComponents } = await takeRender(); - - expect(renderedComponents).toStrictEqual([ - "useLoadableQuery", - "", - ]); - } - - { - const { snapshot, renderedComponents } = await takeRender(); - - invariant("result" in snapshot); - expect(renderedComponents).toStrictEqual(["useReadQuery"]); - expect(snapshot.result).toStrictEqualTyped({ - data: { - event: { - __typename: "Event", - id: "1", - startDate: new Date(2026, 0, 1), - endDate: null, - }, - }, - dataState: "complete", - networkStatus: NetworkStatus.error, - error: new CombinedGraphQLErrors({ - data: { - event: { - __typename: "Event", - id: "1", - // TODO: Determine if this is correct - startDate: new Date(2026, 0, 1), - endDate: null, - }, - }, - errors: [ - { - message: "Could not resolve endDate", - path: ["event", "endDate"], - }, - ], - }), - }); - } - - await expect(takeRender).not.toRerender(); -}); - -test("parses custom scalar fields with an `ignore` error policy", async () => { - const query = gql` - query Event { - event { - id - startDate - endDate - } - } - `; - const client = new ApolloClient({ - cache: new InMemoryCache({ - scalars: { Date: dateScalar }, - typePolicies: { - Event: { - fields: { - startDate: { scalar: "Date" }, - endDate: { scalar: "Date" }, - }, - }, - }, - }), - link: new ApolloLink(() => - of({ - data: { - event: { - __typename: "Event", - id: "1", - startDate: "2026-01-01", - endDate: null, - }, - }, - errors: [ - { - message: "Could not resolve endDate", - path: ["event", "endDate"], - }, - ], - }).pipe(delay(20)) - ), - }); - - using _disabledAct = disableActEnvironment(); - const { takeRender, loadQuery } = await renderUseLoadableQueryHook( - () => useLoadableQuery(query, { errorPolicy: "ignore" }), - { wrapper: createClientWrapper(client) } - ); - - { - const { renderedComponents } = await takeRender(); - - expect(renderedComponents).toStrictEqual(["useLoadableQuery"]); - } - - loadQuery(); - - { - const { renderedComponents } = await takeRender(); - - expect(renderedComponents).toStrictEqual([ - "useLoadableQuery", - "", - ]); - } - - { - const { snapshot, renderedComponents } = await takeRender(); - - invariant("result" in snapshot); - expect(renderedComponents).toStrictEqual(["useReadQuery"]); - expect(snapshot.result).toStrictEqualTyped({ - data: { - event: { - __typename: "Event", - id: "1", - startDate: new Date(2026, 0, 1), - endDate: null, - }, - }, - dataState: "complete", - networkStatus: NetworkStatus.ready, - error: undefined, - }); - } - - await expect(takeRender).not.toRerender(); -}); - -test("parses custom scalar fields across `@defer` payloads (defer20220824)", async () => { - const link = mockDefer20220824(); - const client = new ApolloClient({ - cache: new InMemoryCache({ - scalars: { Date: dateScalar }, - typePolicies: { - Event: { - fields: { - startDate: { scalar: "Date" }, - endDate: { scalar: "Date" }, - }, - }, - }, - }), - link: link.httpLink, - incrementalHandler: new Defer20220824Handler(), - }); - const query = gql` - query Event { - event { - id - startDate - ... @defer { - endDate - } - } - } - `; - - using _disabledAct = disableActEnvironment(); - const { takeRender, loadQuery } = await renderUseLoadableQueryHook( - () => useLoadableQuery(query), - { wrapper: createClientWrapper(client) } - ); - - { - const { renderedComponents } = await takeRender(); - - expect(renderedComponents).toStrictEqual(["useLoadableQuery"]); - } - - loadQuery(); - - { - const { renderedComponents } = await takeRender(); - - expect(renderedComponents).toStrictEqual([ - "useLoadableQuery", - "", - ]); - } - - link.enqueueInitialChunk({ - data: { - event: { - __typename: "Event", - id: "1", - startDate: "2026-01-01", - }, - }, - hasNext: true, - }); - - { - const { snapshot, renderedComponents } = await takeRender(); - - invariant("result" in snapshot); - expect(renderedComponents).toStrictEqual(["useReadQuery"]); - expect(snapshot.result).toStrictEqualTyped({ - data: markAsStreaming({ - event: { - __typename: "Event", - id: "1", - startDate: new Date(2026, 0, 1), - }, - }), - dataState: "streaming", - networkStatus: NetworkStatus.streaming, - error: undefined, - }); - } - - link.enqueueSubsequentChunk({ - incremental: [{ data: { endDate: "2026-02-02" }, path: ["event"] }], - hasNext: false, - }); - - { - const { snapshot, renderedComponents } = await takeRender(); - - invariant("result" in snapshot); - expect(renderedComponents).toStrictEqual(["useReadQuery"]); - expect(snapshot.result).toStrictEqualTyped({ - data: { - event: { - __typename: "Event", - id: "1", - startDate: new Date(2026, 0, 1), - endDate: new Date(2026, 1, 2), - }, - }, - dataState: "complete", - networkStatus: NetworkStatus.ready, - error: undefined, - }); - } - - await expect(takeRender).not.toRerender(); -}); - -test("parses custom scalar fields across `@defer` payloads (graphql17Alpha9)", async () => { - const link = mockDeferStreamGraphQL17Alpha9(); - const client = new ApolloClient({ - cache: new InMemoryCache({ - scalars: { Date: dateScalar }, - typePolicies: { - Event: { - fields: { - startDate: { scalar: "Date" }, - endDate: { scalar: "Date" }, - }, - }, - }, - }), - link: link.httpLink, - incrementalHandler: new GraphQL17Alpha9Handler(), - }); - const query = gql` - query Event { - event { - id - startDate - ... @defer { - endDate - } - } - } - `; - - using _disabledAct = disableActEnvironment(); - const { takeRender, loadQuery } = await renderUseLoadableQueryHook( - () => useLoadableQuery(query), - { wrapper: createClientWrapper(client) } - ); - - { - const { renderedComponents } = await takeRender(); - - expect(renderedComponents).toStrictEqual(["useLoadableQuery"]); - } - - loadQuery(); - - { - const { renderedComponents } = await takeRender(); - - expect(renderedComponents).toStrictEqual([ - "useLoadableQuery", - "", - ]); - } - - link.enqueueInitialChunk({ - data: { - event: { - __typename: "Event", - id: "1", - startDate: "2026-01-01", - }, - }, - pending: [{ id: "0", path: ["event"] }], - hasNext: true, - }); - - { - const { snapshot, renderedComponents } = await takeRender(); - - invariant("result" in snapshot); - expect(renderedComponents).toStrictEqual(["useReadQuery"]); - expect(snapshot.result).toStrictEqualTyped({ - data: markAsStreaming({ - event: { - __typename: "Event", - id: "1", - startDate: new Date(2026, 0, 1), - }, - }), - dataState: "streaming", - networkStatus: NetworkStatus.streaming, - error: undefined, - }); - } - - link.enqueueSubsequentChunk({ - incremental: [{ data: { endDate: "2026-02-02" }, id: "0" }], - completed: [{ id: "0" }], - hasNext: false, - }); - - { - const { snapshot, renderedComponents } = await takeRender(); - - invariant("result" in snapshot); - expect(renderedComponents).toStrictEqual(["useReadQuery"]); - expect(snapshot.result).toStrictEqualTyped({ - data: { - event: { - __typename: "Event", - id: "1", - startDate: new Date(2026, 0, 1), - endDate: new Date(2026, 1, 2), - }, - }, - dataState: "complete", - networkStatus: NetworkStatus.ready, - error: undefined, - }); - } - - await expect(takeRender).not.toRerender(); -}); - -test("parses custom scalar fields across `@stream` payloads (defer20220824)", async () => { - const link = mockDefer20220824(); - const client = new ApolloClient({ - cache: new InMemoryCache({ - scalars: { Date: dateScalar }, - typePolicies: { - Event: { - fields: { - startDate: { scalar: "Date" }, - }, - }, - }, - }), - link: link.httpLink, - incrementalHandler: new Defer20220824Handler(), - }); - const query = gql` - query Events { - events @stream(initialCount: 1) { - id - startDate - } - } - `; - - using _disabledAct = disableActEnvironment(); - const { takeRender, loadQuery } = await renderUseLoadableQueryHook( - () => useLoadableQuery(query), - { wrapper: createClientWrapper(client) } - ); - - { - const { renderedComponents } = await takeRender(); - - expect(renderedComponents).toStrictEqual(["useLoadableQuery"]); - } - - loadQuery(); - - { - const { renderedComponents } = await takeRender(); - - expect(renderedComponents).toStrictEqual([ - "useLoadableQuery", - "", - ]); - } - - link.enqueueInitialChunk({ - data: { - events: [ - { - __typename: "Event", - id: "1", - startDate: "2026-01-01", - }, - ], - }, - hasNext: true, - }); - - { - const { snapshot, renderedComponents } = await takeRender(); - - invariant("result" in snapshot); - expect(renderedComponents).toStrictEqual(["useReadQuery"]); - expect(snapshot.result).toStrictEqualTyped({ - data: { - events: [ - { - __typename: "Event", - id: "1", - startDate: new Date(2026, 0, 1), - }, - ], - }, - dataState: "complete", - networkStatus: NetworkStatus.streaming, - error: undefined, - }); - } - - link.enqueueSubsequentChunk({ - incremental: [ - { - items: [ - { - __typename: "Event", - id: "2", - startDate: "2026-02-02", - }, - ] as any, - path: ["events", 1], - }, - ], - hasNext: false, - }); - - { - const { snapshot, renderedComponents } = await takeRender(); - - invariant("result" in snapshot); - expect(renderedComponents).toStrictEqual(["useReadQuery"]); - expect(snapshot.result).toStrictEqualTyped({ - data: { - events: [ - { - __typename: "Event", - id: "1", - startDate: new Date(2026, 0, 1), - }, - { - __typename: "Event", - id: "2", - startDate: new Date(2026, 1, 2), - }, - ], - }, - dataState: "complete", - networkStatus: NetworkStatus.ready, - error: undefined, - }); - } - - await expect(takeRender).not.toRerender(); -}); - -test("parses custom scalar fields across `@stream` payloads (graphql17Alpha9)", async () => { - const link = mockDeferStreamGraphQL17Alpha9(); - const client = new ApolloClient({ - cache: new InMemoryCache({ - scalars: { Date: dateScalar }, - typePolicies: { - Event: { - fields: { - startDate: { scalar: "Date" }, - }, - }, - }, - }), - link: link.httpLink, - incrementalHandler: new GraphQL17Alpha9Handler(), - }); - const query = gql` - query Events { - events @stream(initialCount: 1) { - id - startDate - } - } - `; - - using _disabledAct = disableActEnvironment(); - const { takeRender, loadQuery } = await renderUseLoadableQueryHook( - () => useLoadableQuery(query), - { wrapper: createClientWrapper(client) } - ); - - { - const { renderedComponents } = await takeRender(); - - expect(renderedComponents).toStrictEqual(["useLoadableQuery"]); - } - - loadQuery(); - - { - const { renderedComponents } = await takeRender(); - - expect(renderedComponents).toStrictEqual([ - "useLoadableQuery", - "", - ]); - } - - link.enqueueInitialChunk({ - data: { - events: [ - { - __typename: "Event", - id: "1", - startDate: "2026-01-01", - }, - ], - }, - pending: [{ id: "0", path: ["events"] }], - hasNext: true, - }); - - { - const { snapshot, renderedComponents } = await takeRender(); - - invariant("result" in snapshot); - expect(renderedComponents).toStrictEqual(["useReadQuery"]); - expect(snapshot.result).toStrictEqualTyped({ - data: { - events: [ - { - __typename: "Event", - id: "1", - startDate: new Date(2026, 0, 1), - }, - ], - }, - dataState: "complete", - networkStatus: NetworkStatus.streaming, - error: undefined, - }); - } - - link.enqueueSubsequentChunk({ - incremental: [ - { - items: [ - { - __typename: "Event", - id: "2", - startDate: "2026-02-02", - }, - ] as any, - id: "0", - }, - ], - completed: [{ id: "0" }], - hasNext: false, - }); - - { - const { snapshot, renderedComponents } = await takeRender(); - - invariant("result" in snapshot); - expect(renderedComponents).toStrictEqual(["useReadQuery"]); - expect(snapshot.result).toStrictEqualTyped({ - data: { - events: [ - { - __typename: "Event", - id: "1", - startDate: new Date(2026, 0, 1), - }, - { - __typename: "Event", - id: "2", - startDate: new Date(2026, 1, 2), - }, - ], - }, - dataState: "complete", - networkStatus: NetworkStatus.ready, - error: undefined, - }); - } - - await expect(takeRender).not.toRerender(); -}); diff --git a/src/react/hooks/__tests__/useMutation/customScalars.test.tsx b/src/react/hooks/__tests__/useMutation/customScalars.test.tsx index 8a3612f66ee..35cd21d0259 100644 --- a/src/react/hooks/__tests__/useMutation/customScalars.test.tsx +++ b/src/react/hooks/__tests__/useMutation/customScalars.test.tsx @@ -4,29 +4,16 @@ import { } from "@testing-library/react-render-stream"; import { delay, of } from "rxjs"; -import type { OperationVariables, TypedDocumentNode } from "@apollo/client"; -import { - ApolloClient, - ApolloLink, - CombinedGraphQLErrors, - gql, - NetworkStatus, -} from "@apollo/client"; +import type { OperationVariables } from "@apollo/client"; +import { ApolloClient, ApolloLink, gql } from "@apollo/client"; import { InMemoryCache } from "@apollo/client/cache"; -import { - Defer20220824Handler, - GraphQL17Alpha9Handler, -} from "@apollo/client/incremental"; -import { useMutation, useQuery } from "@apollo/client/react"; -import { MockLink } from "@apollo/client/testing"; +import { useMutation } from "@apollo/client/react"; import { createClientWrapper, dateScalar, - mockDefer20220824, - mockDeferStreamGraphQL17Alpha9, } from "@apollo/client/testing/internal"; -test("serializes scalar variables used in field arguments", async () => { +test("serializes parsed scalar values in variables", async () => { let requestVariables!: OperationVariables; const client = new ApolloClient({ @@ -116,196 +103,7 @@ test("serializes scalar variables used in field arguments", async () => { expect(requestVariables).toStrictEqualTyped({ date: "2026-01-01" }); }); -test("serializes scalar variables used in directive arguments", async () => { - let requestVariables!: OperationVariables; - - const client = new ApolloClient({ - cache: new InMemoryCache({ - scalars: { Date: dateScalar }, - }), - link: new ApolloLink((operation) => { - requestVariables = operation.variables; - - return of({ - data: { - createEvent: { - __typename: "Event", - name: "GraphQL Summit", - }, - }, - }).pipe(delay(20)); - }), - }); - - const mutation = gql` - mutation CreateEvent($date: Date!) { - createEvent @on(date: $date) { - name - } - } - `; - - using _disabledAct = disableActEnvironment(); - const { takeSnapshot, getCurrentSnapshot } = await renderHookToSnapshotStream( - () => useMutation(mutation), - { wrapper: createClientWrapper(client) } - ); - - { - const [, result] = await takeSnapshot(); - - expect(result).toStrictEqualTyped({ - data: undefined, - error: undefined, - called: false, - loading: false, - }); - } - - const [mutate] = getCurrentSnapshot(); - - await expect( - mutate({ variables: { date: new Date(2026, 0, 1) } }) - ).resolves.toStrictEqualTyped({ - data: { - createEvent: { - __typename: "Event", - name: "GraphQL Summit", - }, - }, - }); - - { - const [, result] = await takeSnapshot(); - - expect(result).toStrictEqualTyped({ - data: undefined, - error: undefined, - called: true, - loading: true, - }); - } - - { - const [, result] = await takeSnapshot(); - - expect(result).toStrictEqualTyped({ - data: { - createEvent: { - __typename: "Event", - name: "GraphQL Summit", - }, - }, - error: undefined, - called: true, - loading: false, - }); - } - - await expect(takeSnapshot).not.toRerender(); - expect(requestVariables).toStrictEqualTyped({ date: "2026-01-01" }); -}); - -test("serializes scalar fields in input object variables", async () => { - let requestVariables!: OperationVariables; - - const client = new ApolloClient({ - cache: new InMemoryCache({ - scalars: { Date: dateScalar }, - inputObjects: { - EventInput: { - fields: { - date: "Date", - }, - }, - }, - }), - link: new ApolloLink((operation) => { - requestVariables = operation.variables; - - return of({ - data: { - createEvent: { - __typename: "Event", - name: "GraphQL Summit", - }, - }, - }).pipe(delay(20)); - }), - }); - - const mutation = gql` - mutation CreateEvent($input: EventInput!) { - createEvent(input: $input) { - name - } - } - `; - - using _disabledAct = disableActEnvironment(); - const { takeSnapshot, getCurrentSnapshot } = await renderHookToSnapshotStream( - () => useMutation(mutation), - { wrapper: createClientWrapper(client) } - ); - - { - const [, result] = await takeSnapshot(); - - expect(result).toStrictEqualTyped({ - data: undefined, - error: undefined, - called: false, - loading: false, - }); - } - - const [mutate] = getCurrentSnapshot(); - - await expect( - mutate({ variables: { input: { date: new Date(2026, 0, 1) } } }) - ).resolves.toStrictEqualTyped({ - data: { - createEvent: { - __typename: "Event", - name: "GraphQL Summit", - }, - }, - }); - - { - const [, result] = await takeSnapshot(); - - expect(result).toStrictEqualTyped({ - data: undefined, - error: undefined, - called: true, - loading: true, - }); - } - - { - const [, result] = await takeSnapshot(); - - expect(result).toStrictEqualTyped({ - data: { - createEvent: { - __typename: "Event", - name: "GraphQL Summit", - }, - }, - error: undefined, - called: true, - loading: false, - }); - } - - await expect(takeSnapshot).not.toRerender(); - expect(requestVariables).toStrictEqualTyped({ - input: { date: "2026-01-01" }, - }); -}); - -test("parses custom scalar fields with a network-only fetch policy", async () => { +test("returns parsed scalar fields", async () => { const mutation = gql` mutation CreateEvent { createEvent { @@ -357,9 +155,7 @@ test("parses custom scalar fields with a network-only fetch policy", async () => const [mutate] = getCurrentSnapshot(); - await expect( - mutate({ fetchPolicy: "network-only" }) - ).resolves.toStrictEqualTyped({ + await expect(mutate()).resolves.toStrictEqualTyped({ data: { createEvent: { __typename: "Event", @@ -399,1308 +195,3 @@ test("parses custom scalar fields with a network-only fetch policy", async () => await expect(takeSnapshot).not.toRerender(); }); - -test.failing( - "parses custom scalar fields with a no-cache fetch policy", - async () => { - const mutation = gql` - mutation CreateEvent { - createEvent { - id - startDate - } - } - `; - const client = new ApolloClient({ - cache: new InMemoryCache({ - scalars: { Date: dateScalar }, - typePolicies: { - Event: { - fields: { - startDate: { scalar: "Date" }, - }, - }, - }, - }), - link: new ApolloLink(() => - of({ - data: { - createEvent: { - __typename: "Event", - id: "1", - startDate: "2026-01-01", - }, - }, - }).pipe(delay(20)) - ), - }); - - using _disabledAct = disableActEnvironment(); - const { takeSnapshot, getCurrentSnapshot } = - await renderHookToSnapshotStream(() => useMutation(mutation), { - wrapper: createClientWrapper(client), - }); - - await takeSnapshot(); - const [mutate] = getCurrentSnapshot(); - - await expect( - mutate({ fetchPolicy: "no-cache" }) - ).resolves.toStrictEqualTyped({ - data: { - createEvent: { - __typename: "Event", - id: "1", - startDate: new Date(2026, 0, 1), - }, - }, - }); - } -); - -test("parses parsed custom scalar fields in optimistic responses", async () => { - const mutation = gql` - mutation CreateEvent { - createEvent { - id - startDate - } - } - `; - const fragment = gql` - fragment EventFragment on Event { - id - startDate - } - `; - const client = new ApolloClient({ - cache: new InMemoryCache({ - scalars: { Date: dateScalar }, - typePolicies: { - Event: { - fields: { - startDate: { scalar: "Date" }, - }, - }, - }, - }), - link: new ApolloLink(() => - of({ - data: { - createEvent: { - __typename: "Event", - id: "1", - startDate: "2026-02-02", - }, - }, - }).pipe(delay(20)) - ), - }); - - using _disabledAct = disableActEnvironment(); - const { takeSnapshot, getCurrentSnapshot } = await renderHookToSnapshotStream( - () => useMutation(mutation), - { wrapper: createClientWrapper(client) } - ); - - { - const [, result] = await takeSnapshot(); - - expect(result).toStrictEqualTyped({ - data: undefined, - error: undefined, - called: false, - loading: false, - }); - } - - const [mutate] = getCurrentSnapshot(); - const promise = mutate({ - optimisticResponse: { - createEvent: { - __typename: "Event", - id: "1", - startDate: new Date(2026, 0, 1), - }, - }, - }); - - expect( - client.cache.readFragment({ - id: "Event:1", - fragment, - optimistic: true, - }) - ).toStrictEqualTyped({ - __typename: "Event", - id: "1", - startDate: new Date(2026, 0, 1), - }); - - await expect(promise).resolves.toStrictEqualTyped({ - data: { - createEvent: { - __typename: "Event", - id: "1", - startDate: new Date(2026, 1, 2), - }, - }, - }); - - { - const [, result] = await takeSnapshot(); - - expect(result).toStrictEqualTyped({ - data: undefined, - error: undefined, - called: true, - loading: true, - }); - } - - { - const [, result] = await takeSnapshot(); - - expect(result).toStrictEqualTyped({ - data: { - createEvent: { - __typename: "Event", - id: "1", - startDate: new Date(2026, 1, 2), - }, - }, - error: undefined, - called: true, - loading: false, - }); - } - - await expect(takeSnapshot).not.toRerender(); -}); - -test("parses serialized custom scalar fields in optimistic responses", async () => { - const mutation = gql` - mutation CreateEvent { - createEvent { - id - startDate - } - } - `; - const fragment = gql` - fragment EventFragment on Event { - id - startDate - } - `; - const client = new ApolloClient({ - cache: new InMemoryCache({ - scalars: { Date: dateScalar }, - typePolicies: { - Event: { - fields: { - startDate: { scalar: "Date" }, - }, - }, - }, - }), - link: new ApolloLink(() => - of({ - data: { - createEvent: { - __typename: "Event", - id: "1", - startDate: "2026-02-02", - }, - }, - }).pipe(delay(20)) - ), - }); - - using _disabledAct = disableActEnvironment(); - const { takeSnapshot, getCurrentSnapshot } = await renderHookToSnapshotStream( - () => useMutation(mutation), - { wrapper: createClientWrapper(client) } - ); - - { - const [, result] = await takeSnapshot(); - - expect(result).toStrictEqualTyped({ - data: undefined, - error: undefined, - called: false, - loading: false, - }); - } - - const [mutate] = getCurrentSnapshot(); - const promise = mutate({ - optimisticResponse: { - createEvent: { - __typename: "Event", - id: "1", - startDate: "2026-01-01", - }, - }, - }); - - expect( - client.cache.readFragment({ - id: "Event:1", - fragment, - optimistic: true, - }) - ).toStrictEqualTyped({ - __typename: "Event", - id: "1", - startDate: new Date(2026, 0, 1), - }); - - await expect(promise).resolves.toStrictEqualTyped({ - data: { - createEvent: { - __typename: "Event", - id: "1", - startDate: new Date(2026, 1, 2), - }, - }, - }); - - { - const [, result] = await takeSnapshot(); - - expect(result).toStrictEqualTyped({ - data: undefined, - error: undefined, - called: true, - loading: true, - }); - } - - { - const [, result] = await takeSnapshot(); - - expect(result).toStrictEqualTyped({ - data: { - createEvent: { - __typename: "Event", - id: "1", - startDate: new Date(2026, 1, 2), - }, - }, - error: undefined, - called: true, - loading: false, - }); - } - - await expect(takeSnapshot).not.toRerender(); -}); - -test("passes parsed custom scalar fields to mutation updater callbacks", async () => { - const mutation: TypedDocumentNode<{ - createEvent: { __typename: "Event"; id: string; startDate: Date }; - }> = gql` - mutation CreateEvent { - createEvent { - id - startDate - } - } - `; - const query = gql` - query LastCreatedEvent { - lastCreatedEvent { - id - startDate - } - } - `; - const client = new ApolloClient({ - cache: new InMemoryCache({ - scalars: { Date: dateScalar }, - typePolicies: { - Event: { - fields: { - startDate: { scalar: "Date" }, - }, - }, - }, - }), - link: new ApolloLink(() => - of({ - data: { - createEvent: { - __typename: "Event", - id: "1", - startDate: "2026-01-01", - }, - }, - }).pipe(delay(20)) - ), - }); - - using _disabledAct = disableActEnvironment(); - const { takeSnapshot, getCurrentSnapshot } = await renderHookToSnapshotStream( - () => useMutation(mutation), - { wrapper: createClientWrapper(client) } - ); - - { - const [, result] = await takeSnapshot(); - - expect(result).toStrictEqualTyped({ - data: undefined, - error: undefined, - called: false, - loading: false, - }); - } - - const [mutate] = getCurrentSnapshot(); - - await expect( - mutate({ - update(cache, result) { - expect(result).toStrictEqualTyped({ - data: { - createEvent: { - __typename: "Event", - id: "1", - startDate: new Date(2026, 0, 1), - }, - }, - }); - - cache.writeQuery({ - query, - data: { - lastCreatedEvent: result.data!.createEvent, - }, - }); - }, - }) - ).resolves.toStrictEqualTyped({ - data: { - createEvent: { - __typename: "Event", - id: "1", - startDate: new Date(2026, 0, 1), - }, - }, - }); - - { - const [, result] = await takeSnapshot(); - - expect(result).toStrictEqualTyped({ - data: undefined, - error: undefined, - called: true, - loading: true, - }); - } - - { - const [, result] = await takeSnapshot(); - - expect(result).toStrictEqualTyped({ - data: { - createEvent: { - __typename: "Event", - id: "1", - startDate: new Date(2026, 0, 1), - }, - }, - error: undefined, - called: true, - loading: false, - }); - } - - await expect(takeSnapshot).not.toRerender(); - - expect(client.readQuery({ query })).toStrictEqualTyped({ - lastCreatedEvent: { - __typename: "Event", - id: "1", - startDate: new Date(2026, 0, 1), - }, - }); -}); - -test("parses custom scalar fields in queries triggered by refetchQueries", async () => { - const query = gql` - query Event { - event { - id - startDate - } - } - `; - const mutation = gql` - mutation CreateEvent { - createEvent { - id - startDate - } - } - `; - let requestCount = 0; - const link = new MockLink([ - { - request: { query }, - maxUsageCount: 2, - delay: 20, - result: () => { - requestCount++; - - return { - data: { - event: { - __typename: "Event", - id: "1", - startDate: requestCount === 1 ? "2026-01-01" : "2026-03-03", - }, - }, - }; - }, - }, - { - request: { query: mutation }, - delay: 20, - result: { - data: { - createEvent: { - __typename: "Event", - id: "2", - startDate: "2026-02-02", - }, - }, - }, - }, - ]); - const client = new ApolloClient({ - cache: new InMemoryCache({ - scalars: { Date: dateScalar }, - typePolicies: { - Event: { - fields: { - startDate: { scalar: "Date" }, - }, - }, - }, - }), - link, - }); - - using _disabledAct = disableActEnvironment(); - const { takeSnapshot, getCurrentSnapshot } = await renderHookToSnapshotStream( - () => ({ - query: useQuery(query), - mutation: useMutation(mutation), - }), - { wrapper: createClientWrapper(client) } - ); - - { - const { - query, - mutation: [, mutation], - } = await takeSnapshot(); - - expect(query).toStrictEqualTyped({ - data: undefined, - dataState: "empty", - loading: true, - networkStatus: NetworkStatus.loading, - previousData: undefined, - variables: {}, - }); - - expect(mutation).toStrictEqualTyped({ - data: undefined, - error: undefined, - called: false, - loading: false, - }); - } - - { - const { - query, - mutation: [, mutation], - } = await takeSnapshot(); - - expect(query).toStrictEqualTyped({ - data: { - event: { - __typename: "Event", - id: "1", - startDate: new Date(2026, 0, 1), - }, - }, - dataState: "complete", - loading: false, - networkStatus: NetworkStatus.ready, - previousData: undefined, - variables: {}, - }); - - expect(mutation).toStrictEqualTyped({ - data: undefined, - error: undefined, - called: false, - loading: false, - }); - } - - const { - mutation: [mutate], - } = getCurrentSnapshot(); - - await expect( - mutate({ - refetchQueries: [query], - awaitRefetchQueries: true, - }) - ).resolves.toStrictEqualTyped({ - data: { - createEvent: { - __typename: "Event", - id: "2", - startDate: new Date(2026, 1, 2), - }, - }, - }); - - { - const { - query, - mutation: [, mutation], - } = await takeSnapshot(); - - expect(query).toStrictEqualTyped({ - data: { - event: { - __typename: "Event", - id: "1", - startDate: new Date(2026, 0, 1), - }, - }, - dataState: "complete", - loading: false, - networkStatus: NetworkStatus.ready, - previousData: undefined, - variables: {}, - }); - - expect(mutation).toStrictEqualTyped({ - data: undefined, - error: undefined, - called: true, - loading: true, - }); - } - - { - const { - query, - mutation: [, mutation], - } = await takeSnapshot(); - - expect(query).toStrictEqualTyped({ - data: { - event: { - __typename: "Event", - id: "1", - startDate: new Date(2026, 0, 1), - }, - }, - dataState: "complete", - loading: true, - networkStatus: NetworkStatus.refetch, - previousData: undefined, - variables: {}, - }); - - expect(mutation).toStrictEqualTyped({ - data: undefined, - error: undefined, - called: true, - loading: true, - }); - } - - { - const { - query, - mutation: [, mutation], - } = await takeSnapshot(); - - expect(query).toStrictEqualTyped({ - data: { - event: { - __typename: "Event", - id: "1", - startDate: new Date(2026, 2, 3), - }, - }, - dataState: "complete", - loading: false, - networkStatus: NetworkStatus.ready, - previousData: { - event: { - __typename: "Event", - id: "1", - startDate: new Date(2026, 0, 1), - }, - }, - variables: {}, - }); - - expect(mutation).toStrictEqualTyped({ - data: undefined, - error: undefined, - called: true, - loading: true, - }); - } - - { - const { - query, - mutation: [, mutation], - } = await takeSnapshot(); - - expect(query).toStrictEqualTyped({ - data: { - event: { - __typename: "Event", - id: "1", - startDate: new Date(2026, 2, 3), - }, - }, - dataState: "complete", - loading: false, - networkStatus: NetworkStatus.ready, - previousData: { - event: { - __typename: "Event", - id: "1", - startDate: new Date(2026, 0, 1), - }, - }, - variables: {}, - }); - - expect(mutation).toStrictEqualTyped({ - data: { - createEvent: { - __typename: "Event", - id: "2", - startDate: new Date(2026, 1, 2), - }, - }, - error: undefined, - called: true, - loading: false, - }); - } - - await expect(takeSnapshot).not.toRerender(); - - expect(client.readQuery({ query })).toStrictEqualTyped({ - event: { - __typename: "Event", - id: "1", - startDate: new Date(2026, 2, 3), - }, - }); -}); - -test("serializes scalar fields in the error with a `none` error policy", async () => { - const mutation = gql` - mutation CreateEvent { - createEvent { - id - startDate - endDate - } - } - `; - const client = new ApolloClient({ - cache: new InMemoryCache({ - scalars: { Date: dateScalar }, - typePolicies: { - Event: { - fields: { - startDate: { scalar: "Date" }, - endDate: { scalar: "Date" }, - }, - }, - }, - }), - link: new ApolloLink(() => - of({ - data: { - createEvent: { - __typename: "Event", - id: "1", - startDate: "2026-01-01", - endDate: null, - }, - }, - errors: [ - { - message: "Could not resolve endDate", - path: ["createEvent", "endDate"], - }, - ], - }).pipe(delay(20)) - ), - }); - - using _disabledAct = disableActEnvironment(); - const { takeSnapshot, getCurrentSnapshot } = await renderHookToSnapshotStream( - () => useMutation(mutation, { errorPolicy: "none" }), - { wrapper: createClientWrapper(client) } - ); - - { - const [, result] = await takeSnapshot(); - - expect(result).toStrictEqualTyped({ - data: undefined, - error: undefined, - called: false, - loading: false, - }); - } - - const [mutate] = getCurrentSnapshot(); - - await expect(mutate()).rejects.toEqual( - new CombinedGraphQLErrors({ - data: { - createEvent: { - __typename: "Event", - id: "1", - startDate: "2026-01-01", - endDate: null, - }, - }, - errors: [ - { - message: "Could not resolve endDate", - path: ["createEvent", "endDate"], - }, - ], - }) - ); - - { - const [, result] = await takeSnapshot(); - - expect(result).toStrictEqualTyped({ - data: undefined, - error: undefined, - called: true, - loading: true, - }); - } - - { - const [, result] = await takeSnapshot(); - - expect(result).toStrictEqualTyped({ - data: undefined, - error: new CombinedGraphQLErrors({ - data: { - createEvent: { - __typename: "Event", - id: "1", - startDate: "2026-01-01", - endDate: null, - }, - }, - errors: [ - { - message: "Could not resolve endDate", - path: ["createEvent", "endDate"], - }, - ], - }), - called: true, - loading: false, - }); - } - - await expect(takeSnapshot).not.toRerender(); -}); - -test("parses scalar fields in the result and serializes them in the error with an `all` error policy", async () => { - const mutation = gql` - mutation CreateEvent { - createEvent { - id - startDate - endDate - } - } - `; - const client = new ApolloClient({ - cache: new InMemoryCache({ - scalars: { Date: dateScalar }, - typePolicies: { - Event: { - fields: { - startDate: { scalar: "Date" }, - endDate: { scalar: "Date" }, - }, - }, - }, - }), - link: new ApolloLink(() => - of({ - data: { - createEvent: { - __typename: "Event", - id: "1", - startDate: "2026-01-01", - endDate: null, - }, - }, - errors: [ - { - message: "Could not resolve endDate", - path: ["createEvent", "endDate"], - }, - ], - }).pipe(delay(20)) - ), - }); - - using _disabledAct = disableActEnvironment(); - const { takeSnapshot, getCurrentSnapshot } = await renderHookToSnapshotStream( - () => useMutation(mutation, { errorPolicy: "all" }), - { wrapper: createClientWrapper(client) } - ); - - { - const [, result] = await takeSnapshot(); - - expect(result).toStrictEqualTyped({ - data: undefined, - error: undefined, - called: false, - loading: false, - }); - } - - const [mutate] = getCurrentSnapshot(); - - await expect(mutate()).resolves.toStrictEqualTyped({ - data: { - createEvent: { - __typename: "Event", - id: "1", - startDate: new Date(2026, 0, 1), - endDate: null, - }, - }, - error: new CombinedGraphQLErrors({ - data: { - createEvent: { - __typename: "Event", - id: "1", - // TODO: Determine if this is correct - startDate: new Date(2026, 0, 1), - endDate: null, - }, - }, - errors: [ - { - message: "Could not resolve endDate", - path: ["createEvent", "endDate"], - }, - ], - }), - }); - - { - const [, result] = await takeSnapshot(); - - expect(result).toStrictEqualTyped({ - data: undefined, - error: undefined, - called: true, - loading: true, - }); - } - - { - const [, result] = await takeSnapshot(); - - expect(result).toStrictEqualTyped({ - data: { - createEvent: { - __typename: "Event", - id: "1", - startDate: new Date(2026, 0, 1), - endDate: null, - }, - }, - error: new CombinedGraphQLErrors({ - data: { - createEvent: { - __typename: "Event", - id: "1", - // TODO: Determine if this is correct - startDate: new Date(2026, 0, 1), - endDate: null, - }, - }, - errors: [ - { - message: "Could not resolve endDate", - path: ["createEvent", "endDate"], - }, - ], - }), - called: true, - loading: false, - }); - } - - await expect(takeSnapshot).not.toRerender(); -}); - -test("parses custom scalar fields with an `ignore` error policy", async () => { - const mutation = gql` - mutation CreateEvent { - createEvent { - id - startDate - endDate - } - } - `; - const client = new ApolloClient({ - cache: new InMemoryCache({ - scalars: { Date: dateScalar }, - typePolicies: { - Event: { - fields: { - startDate: { scalar: "Date" }, - endDate: { scalar: "Date" }, - }, - }, - }, - }), - link: new ApolloLink(() => - of({ - data: { - createEvent: { - __typename: "Event", - id: "1", - startDate: "2026-01-01", - endDate: null, - }, - }, - errors: [ - { - message: "Could not resolve endDate", - path: ["createEvent", "endDate"], - }, - ], - }).pipe(delay(20)) - ), - }); - - using _disabledAct = disableActEnvironment(); - const { takeSnapshot, getCurrentSnapshot } = await renderHookToSnapshotStream( - () => useMutation(mutation, { errorPolicy: "ignore" }), - { wrapper: createClientWrapper(client) } - ); - - { - const [, result] = await takeSnapshot(); - - expect(result).toStrictEqualTyped({ - data: undefined, - error: undefined, - called: false, - loading: false, - }); - } - - const [mutate] = getCurrentSnapshot(); - - await expect(mutate()).resolves.toStrictEqualTyped({ - data: { - createEvent: { - __typename: "Event", - id: "1", - startDate: new Date(2026, 0, 1), - endDate: null, - }, - }, - }); - - { - const [, result] = await takeSnapshot(); - - expect(result).toStrictEqualTyped({ - data: undefined, - error: undefined, - called: true, - loading: true, - }); - } - - { - const [, result] = await takeSnapshot(); - - expect(result).toStrictEqualTyped({ - data: { - createEvent: { - __typename: "Event", - id: "1", - startDate: new Date(2026, 0, 1), - endDate: null, - }, - }, - error: undefined, - called: true, - loading: false, - }); - } - - await expect(takeSnapshot).not.toRerender(); -}); - -test("parses custom scalar fields across `@defer` payloads (defer20220824)", async () => { - const link = mockDefer20220824(); - const client = new ApolloClient({ - cache: new InMemoryCache({ - scalars: { Date: dateScalar }, - typePolicies: { - Event: { - fields: { - startDate: { scalar: "Date" }, - endDate: { scalar: "Date" }, - }, - }, - }, - }), - link: link.httpLink, - incrementalHandler: new Defer20220824Handler(), - }); - const mutation = gql` - mutation CreateEvent { - createEvent { - id - startDate - ... @defer { - endDate - } - } - } - `; - - using _disabledAct = disableActEnvironment(); - const { takeSnapshot, getCurrentSnapshot } = await renderHookToSnapshotStream( - () => useMutation(mutation), - { wrapper: createClientWrapper(client) } - ); - - { - const [, result] = await takeSnapshot(); - - expect(result).toStrictEqualTyped({ - data: undefined, - error: undefined, - called: false, - loading: false, - }); - } - - const [mutate] = getCurrentSnapshot(); - - const promise = mutate(); - - { - const [, result] = await takeSnapshot(); - - expect(result).toStrictEqualTyped({ - data: undefined, - error: undefined, - called: true, - loading: true, - }); - } - - link.enqueueInitialChunk({ - data: { - createEvent: { - __typename: "Event", - id: "1", - startDate: "2026-01-01", - }, - }, - hasNext: true, - }); - - await expect(takeSnapshot).not.toRerender(); - - link.enqueueSubsequentChunk({ - incremental: [{ data: { endDate: "2026-02-02" }, path: ["createEvent"] }], - hasNext: false, - }); - - { - const [, result] = await takeSnapshot(); - - expect(result).toStrictEqualTyped({ - data: { - createEvent: { - __typename: "Event", - id: "1", - startDate: new Date(2026, 0, 1), - endDate: new Date(2026, 1, 2), - }, - }, - error: undefined, - called: true, - loading: false, - }); - } - - await expect(promise).resolves.toStrictEqualTyped({ - data: { - createEvent: { - __typename: "Event", - id: "1", - startDate: new Date(2026, 0, 1), - endDate: new Date(2026, 1, 2), - }, - }, - }); - - await expect(takeSnapshot).not.toRerender(); -}); - -test("parses custom scalar fields across `@defer` payloads (graphql17Alpha9)", async () => { - const link = mockDeferStreamGraphQL17Alpha9(); - const client = new ApolloClient({ - cache: new InMemoryCache({ - scalars: { Date: dateScalar }, - typePolicies: { - Event: { - fields: { - startDate: { scalar: "Date" }, - endDate: { scalar: "Date" }, - }, - }, - }, - }), - link: link.httpLink, - incrementalHandler: new GraphQL17Alpha9Handler(), - }); - const mutation = gql` - mutation CreateEvent { - createEvent { - id - startDate - ... @defer { - endDate - } - } - } - `; - - using _disabledAct = disableActEnvironment(); - const { takeSnapshot, getCurrentSnapshot } = await renderHookToSnapshotStream( - () => useMutation(mutation), - { wrapper: createClientWrapper(client) } - ); - - { - const [, result] = await takeSnapshot(); - - expect(result).toStrictEqualTyped({ - data: undefined, - error: undefined, - called: false, - loading: false, - }); - } - - const [mutate] = getCurrentSnapshot(); - - const promise = mutate(); - - { - const [, result] = await takeSnapshot(); - - expect(result).toStrictEqualTyped({ - data: undefined, - error: undefined, - called: true, - loading: true, - }); - } - - link.enqueueInitialChunk({ - data: { - createEvent: { - __typename: "Event", - id: "1", - startDate: "2026-01-01", - }, - }, - pending: [{ id: "0", path: ["createEvent"] }], - hasNext: true, - }); - - await expect(takeSnapshot).not.toRerender(); - - link.enqueueSubsequentChunk({ - incremental: [{ data: { endDate: "2026-02-02" }, id: "0" }], - completed: [{ id: "0" }], - hasNext: false, - }); - - { - const [, result] = await takeSnapshot(); - - expect(result).toStrictEqualTyped({ - data: { - createEvent: { - __typename: "Event", - id: "1", - startDate: new Date(2026, 0, 1), - endDate: new Date(2026, 1, 2), - }, - }, - error: undefined, - called: true, - loading: false, - }); - } - - await expect(promise).resolves.toStrictEqualTyped({ - data: { - createEvent: { - __typename: "Event", - id: "1", - startDate: new Date(2026, 0, 1), - endDate: new Date(2026, 1, 2), - }, - }, - }); - - await expect(takeSnapshot).not.toRerender(); -}); diff --git a/src/react/hooks/__tests__/useQuery/customScalars.test.tsx b/src/react/hooks/__tests__/useQuery/customScalars.test.tsx index afa599abec3..95f021102d4 100644 --- a/src/react/hooks/__tests__/useQuery/customScalars.test.tsx +++ b/src/react/hooks/__tests__/useQuery/customScalars.test.tsx @@ -5,28 +5,15 @@ import { import { delay, of } from "rxjs"; import type { OperationVariables } from "@apollo/client"; -import { - ApolloClient, - ApolloLink, - CombinedGraphQLErrors, - gql, - NetworkStatus, -} from "@apollo/client"; +import { ApolloClient, ApolloLink, gql, NetworkStatus } from "@apollo/client"; import { InMemoryCache } from "@apollo/client/cache"; -import { - Defer20220824Handler, - GraphQL17Alpha9Handler, -} from "@apollo/client/incremental"; import { useQuery } from "@apollo/client/react"; import { createClientWrapper, dateScalar, - markAsStreaming, - mockDefer20220824, - mockDeferStreamGraphQL17Alpha9, } from "@apollo/client/testing/internal"; -test("serializes scalar variables used in field arguments", async () => { +test("serializes parsed scalar values in variables", async () => { let requestVariables!: OperationVariables; const link = new ApolloLink((operation) => { @@ -96,164 +83,7 @@ test("serializes scalar variables used in field arguments", async () => { expect(requestVariables).toStrictEqualTyped({ date: "2026-01-01" }); }); -test("serializes scalar variables used in directive arguments", async () => { - let requestVariables!: OperationVariables; - - const link = new ApolloLink((operation) => { - requestVariables = operation.variables; - - return of({ - data: { event: { __typename: "Event", name: "GraphQL Summit" } }, - }); - }); - - const client = new ApolloClient({ - cache: new InMemoryCache({ - scalars: { - Date: dateScalar, - }, - }), - link, - }); - const query = gql` - query Event($date: Date!) { - event @on(date: $date) { - name - } - } - `; - - using _disabledAct = disableActEnvironment(); - const { takeSnapshot } = await renderHookToSnapshotStream( - () => - useQuery(query, { - variables: { - date: new Date(2026, 0, 1), - }, - }), - { wrapper: createClientWrapper(client) } - ); - - await expect(takeSnapshot()).resolves.toStrictEqualTyped({ - data: undefined, - dataState: "empty", - loading: true, - networkStatus: NetworkStatus.loading, - previousData: undefined, - variables: { - date: new Date(2026, 0, 1), - }, - }); - - await expect(takeSnapshot()).resolves.toStrictEqualTyped({ - data: { - event: { - __typename: "Event", - name: "GraphQL Summit", - }, - }, - dataState: "complete", - loading: false, - networkStatus: NetworkStatus.ready, - previousData: undefined, - variables: { - date: new Date(2026, 0, 1), - }, - }); - - await expect(takeSnapshot).not.toRerender(); - - expect(requestVariables).toStrictEqualTyped({ date: "2026-01-01" }); -}); - -test("serializes scalar fields in input object variables", async () => { - let requestVariables!: OperationVariables; - - const link = new ApolloLink((operation) => { - requestVariables = operation.variables; - - return of({ - data: { event: { __typename: "Event", name: "GraphQL Summit" } }, - }); - }); - - const client = new ApolloClient({ - cache: new InMemoryCache({ - scalars: { - Date: dateScalar, - }, - inputObjects: { - EventFilter: { - fields: { - date: "Date", - }, - }, - }, - }), - link, - }); - const query = gql` - query Event($filter: EventFilter!) { - event(filter: $filter) { - name - } - } - `; - - using _disabledAct = disableActEnvironment(); - const { takeSnapshot } = await renderHookToSnapshotStream( - () => - useQuery(query, { - variables: { - filter: { - date: new Date(2026, 0, 1), - }, - }, - }), - { wrapper: createClientWrapper(client) } - ); - - await expect(takeSnapshot()).resolves.toStrictEqualTyped({ - data: undefined, - dataState: "empty", - loading: true, - networkStatus: NetworkStatus.loading, - previousData: undefined, - variables: { - filter: { - date: new Date(2026, 0, 1), - }, - }, - }); - - await expect(takeSnapshot()).resolves.toStrictEqualTyped({ - data: { - event: { - __typename: "Event", - name: "GraphQL Summit", - }, - }, - dataState: "complete", - loading: false, - networkStatus: NetworkStatus.ready, - previousData: undefined, - variables: { - filter: { - date: new Date(2026, 0, 1), - }, - }, - }); - - await expect(takeSnapshot).not.toRerender(); - - expect(requestVariables).toStrictEqualTyped({ - filter: { - date: "2026-01-01", - }, - }); -}); - -test("parses cached custom scalar fields with a cache-only fetch policy", async () => { +test("returns parsed scalar fields", async () => { const query = gql` query Event { event { @@ -273,81 +103,33 @@ test("parses cached custom scalar fields with a cache-only fetch policy", async }, }, }), - link: ApolloLink.empty(), - }); - client.writeQuery({ - query, - data: { - event: { - __typename: "Event", - id: "1", - startDate: "2026-01-01", - }, - }, + link: new ApolloLink(() => + of({ + data: { + event: { + __typename: "Event", + id: "1", + startDate: "2026-01-01", + }, + }, + }).pipe(delay(20)) + ), }); using _disabledAct = disableActEnvironment(); const { takeSnapshot } = await renderHookToSnapshotStream( - () => useQuery(query, { fetchPolicy: "cache-only" }), + () => useQuery(query), { wrapper: createClientWrapper(client) } ); await expect(takeSnapshot()).resolves.toStrictEqualTyped({ - data: { - event: { - __typename: "Event", - id: "1", - startDate: new Date(2026, 0, 1), - }, - }, - dataState: "complete", - loading: false, - networkStatus: NetworkStatus.ready, + data: undefined, + dataState: "empty", + loading: true, + networkStatus: NetworkStatus.loading, previousData: undefined, variables: {}, }); - await expect(takeSnapshot).not.toRerender(); -}); - -test("parses cached custom scalar fields with a cache-first fetch policy", async () => { - const query = gql` - query Event { - event { - id - startDate - } - } - `; - const client = new ApolloClient({ - cache: new InMemoryCache({ - scalars: { Date: dateScalar }, - typePolicies: { - Event: { - fields: { - startDate: { scalar: "Date" }, - }, - }, - }, - }), - link: ApolloLink.empty(), - }); - client.writeQuery({ - query, - data: { - event: { - __typename: "Event", - id: "1", - startDate: "2026-01-01", - }, - }, - }); - - using _disabledAct = disableActEnvironment(); - const { takeSnapshot } = await renderHookToSnapshotStream( - () => useQuery(query, { fetchPolicy: "cache-first" }), - { wrapper: createClientWrapper(client) } - ); - await expect(takeSnapshot()).resolves.toStrictEqualTyped({ data: { event: { @@ -365,7 +147,7 @@ test("parses cached custom scalar fields with a cache-first fetch policy", async await expect(takeSnapshot).not.toRerender(); }); -test("parses network custom scalar fields with a cache-first fetch policy", async () => { +test("preserves referential identity when refetching identical scalar values", async () => { const query = gql` query Event { event { @@ -399,8 +181,8 @@ test("parses network custom scalar fields with a cache-first fetch policy", asyn }); using _disabledAct = disableActEnvironment(); - const { takeSnapshot } = await renderHookToSnapshotStream( - () => useQuery(query, { fetchPolicy: "cache-first" }), + const { takeSnapshot, getCurrentSnapshot } = await renderHookToSnapshotStream( + () => useQuery(query), { wrapper: createClientWrapper(client) } ); @@ -412,6 +194,7 @@ test("parses network custom scalar fields with a cache-first fetch policy", asyn previousData: undefined, variables: {}, }); + await expect(takeSnapshot()).resolves.toStrictEqualTyped({ data: { event: { @@ -426,361 +209,15 @@ test("parses network custom scalar fields with a cache-first fetch policy", asyn previousData: undefined, variables: {}, }); - await expect(takeSnapshot).not.toRerender(); -}); -test("parses cached and network custom scalar fields with a cache-and-network fetch policy", async () => { - const query = gql` - query Event { - event { - id - startDate - } - } - `; - const client = new ApolloClient({ - cache: new InMemoryCache({ - scalars: { Date: dateScalar }, - typePolicies: { - Event: { - fields: { - startDate: { scalar: "Date" }, - }, - }, - }, - }), - link: new ApolloLink(() => - of({ - data: { - event: { - __typename: "Event", - id: "1", - startDate: "2026-02-02", - }, - }, - }).pipe(delay(20)) - ), - }); - client.writeQuery({ - query, + const { data: previousData, refetch } = getCurrentSnapshot(); + + await expect(refetch()).resolves.toStrictEqualTyped({ data: { event: { __typename: "Event", id: "1", - startDate: "2026-01-01", - }, - }, - }); - - using _disabledAct = disableActEnvironment(); - const { takeSnapshot } = await renderHookToSnapshotStream( - () => useQuery(query, { fetchPolicy: "cache-and-network" }), - { wrapper: createClientWrapper(client) } - ); - - await expect(takeSnapshot()).resolves.toStrictEqualTyped({ - data: { - event: { - __typename: "Event", - id: "1", - startDate: new Date(2026, 0, 1), - }, - }, - dataState: "complete", - loading: true, - networkStatus: NetworkStatus.loading, - previousData: undefined, - variables: {}, - }); - await expect(takeSnapshot()).resolves.toStrictEqualTyped({ - data: { - event: { - __typename: "Event", - id: "1", - startDate: new Date(2026, 1, 2), - }, - }, - dataState: "complete", - loading: false, - networkStatus: NetworkStatus.ready, - previousData: { - event: { - __typename: "Event", - id: "1", - startDate: new Date(2026, 0, 1), - }, - }, - variables: {}, - }); - await expect(takeSnapshot).not.toRerender(); -}); - -test("parses network custom scalar fields with a cache-and-network fetch policy", async () => { - const query = gql` - query Event { - event { - id - startDate - } - } - `; - const client = new ApolloClient({ - cache: new InMemoryCache({ - scalars: { Date: dateScalar }, - typePolicies: { - Event: { - fields: { - startDate: { scalar: "Date" }, - }, - }, - }, - }), - link: new ApolloLink(() => - of({ - data: { - event: { - __typename: "Event", - id: "1", - startDate: "2026-01-01", - }, - }, - }).pipe(delay(20)) - ), - }); - - using _disabledAct = disableActEnvironment(); - const { takeSnapshot } = await renderHookToSnapshotStream( - () => useQuery(query, { fetchPolicy: "cache-and-network" }), - { wrapper: createClientWrapper(client) } - ); - - await expect(takeSnapshot()).resolves.toStrictEqualTyped({ - data: undefined, - dataState: "empty", - loading: true, - networkStatus: NetworkStatus.loading, - previousData: undefined, - variables: {}, - }); - await expect(takeSnapshot()).resolves.toStrictEqualTyped({ - data: { - event: { - __typename: "Event", - id: "1", - startDate: new Date(2026, 0, 1), - }, - }, - dataState: "complete", - loading: false, - networkStatus: NetworkStatus.ready, - previousData: undefined, - variables: {}, - }); - await expect(takeSnapshot).not.toRerender(); -}); - -test("parses network custom scalar fields with a network-only fetch policy", async () => { - const query = gql` - query Event { - event { - id - startDate - } - } - `; - const client = new ApolloClient({ - cache: new InMemoryCache({ - scalars: { Date: dateScalar }, - typePolicies: { - Event: { - fields: { - startDate: { scalar: "Date" }, - }, - }, - }, - }), - link: new ApolloLink(() => - of({ - data: { - event: { - __typename: "Event", - id: "1", - startDate: "2026-01-01", - }, - }, - }).pipe(delay(20)) - ), - }); - - using _disabledAct = disableActEnvironment(); - const { takeSnapshot } = await renderHookToSnapshotStream( - () => useQuery(query, { fetchPolicy: "network-only" }), - { wrapper: createClientWrapper(client) } - ); - - await expect(takeSnapshot()).resolves.toStrictEqualTyped({ - data: undefined, - dataState: "empty", - loading: true, - networkStatus: NetworkStatus.loading, - previousData: undefined, - variables: {}, - }); - await expect(takeSnapshot()).resolves.toStrictEqualTyped({ - data: { - event: { - __typename: "Event", - id: "1", - startDate: new Date(2026, 0, 1), - }, - }, - dataState: "complete", - loading: false, - networkStatus: NetworkStatus.ready, - previousData: undefined, - variables: {}, - }); - await expect(takeSnapshot).not.toRerender(); -}); - -test.failing( - "parses custom scalar fields with a no-cache fetch policy", - async () => { - const query = gql` - query Event { - event { - id - startDate - } - } - `; - const client = new ApolloClient({ - cache: new InMemoryCache({ - scalars: { Date: dateScalar }, - typePolicies: { - Event: { - fields: { - startDate: { scalar: "Date" }, - }, - }, - }, - }), - link: new ApolloLink(() => - of({ - data: { - event: { - __typename: "Event", - id: "1", - startDate: "2026-01-01", - }, - }, - }).pipe(delay(20)) - ), - }); - - using _disabledAct = disableActEnvironment(); - const { takeSnapshot } = await renderHookToSnapshotStream( - () => useQuery(query, { fetchPolicy: "no-cache" }), - { wrapper: createClientWrapper(client) } - ); - - await expect(takeSnapshot()).resolves.toStrictEqualTyped({ - data: undefined, - dataState: "empty", - loading: true, - networkStatus: NetworkStatus.loading, - previousData: undefined, - variables: {}, - }); - - await expect(takeSnapshot()).resolves.toStrictEqualTyped({ - data: { - event: { - __typename: "Event", - id: "1", - startDate: new Date(2026, 0, 1), - }, - }, - dataState: "complete", - loading: false, - networkStatus: NetworkStatus.ready, - previousData: undefined, - variables: {}, - }); - } -); - -test("preserves referential identity when refetching identical serialized scalar values", async () => { - const query = gql` - query Event { - event { - id - startDate - } - } - `; - const client = new ApolloClient({ - cache: new InMemoryCache({ - scalars: { Date: dateScalar }, - typePolicies: { - Event: { - fields: { - startDate: { scalar: "Date" }, - }, - }, - }, - }), - link: new ApolloLink(() => - of({ - data: { - event: { - __typename: "Event", - id: "1", - startDate: "2026-01-01", - }, - }, - }).pipe(delay(20)) - ), - }); - - using _disabledAct = disableActEnvironment(); - const { takeSnapshot, getCurrentSnapshot } = await renderHookToSnapshotStream( - () => useQuery(query), - { wrapper: createClientWrapper(client) } - ); - - await expect(takeSnapshot()).resolves.toStrictEqualTyped({ - data: undefined, - dataState: "empty", - loading: true, - networkStatus: NetworkStatus.loading, - previousData: undefined, - variables: {}, - }); - - await expect(takeSnapshot()).resolves.toStrictEqualTyped({ - data: { - event: { - __typename: "Event", - id: "1", - startDate: new Date(2026, 0, 1), - }, - }, - dataState: "complete", - loading: false, - networkStatus: NetworkStatus.ready, - previousData: undefined, - variables: {}, - }); - - const { data: previousData, refetch } = getCurrentSnapshot(); - - await expect(refetch()).resolves.toStrictEqualTyped({ - data: { - event: { - __typename: "Event", - id: "1", - startDate: new Date(2026, 0, 1), + startDate: new Date(2026, 0, 1), }, }, }); @@ -821,699 +258,3 @@ test("preserves referential identity when refetching identical serialized scalar await expect(takeSnapshot).not.toRerender(); }); - -test("serializes scalar fields in the error with a `none` error policy", async () => { - const query = gql` - query Event { - event { - id - startDate - endDate - } - } - `; - const client = new ApolloClient({ - cache: new InMemoryCache({ - scalars: { Date: dateScalar }, - typePolicies: { - Event: { - fields: { - startDate: { scalar: "Date" }, - endDate: { scalar: "Date" }, - }, - }, - }, - }), - link: new ApolloLink(() => - of({ - data: { - event: { - __typename: "Event", - id: "1", - startDate: "2026-01-01", - endDate: null, - }, - }, - errors: [ - { - message: "Could not resolve endDate", - path: ["event", "endDate"], - }, - ], - }).pipe(delay(20)) - ), - }); - - using _disabledAct = disableActEnvironment(); - const { takeSnapshot } = await renderHookToSnapshotStream( - () => useQuery(query, { errorPolicy: "none" }), - { wrapper: createClientWrapper(client) } - ); - - await expect(takeSnapshot()).resolves.toStrictEqualTyped({ - data: undefined, - dataState: "empty", - loading: true, - networkStatus: NetworkStatus.loading, - previousData: undefined, - variables: {}, - }); - - await expect(takeSnapshot()).resolves.toStrictEqualTyped({ - data: undefined, - dataState: "empty", - error: new CombinedGraphQLErrors({ - data: { - event: { - __typename: "Event", - id: "1", - startDate: "2026-01-01", - endDate: null, - }, - }, - errors: [ - { - message: "Could not resolve endDate", - path: ["event", "endDate"], - }, - ], - }), - loading: false, - networkStatus: NetworkStatus.error, - previousData: undefined, - variables: {}, - }); - - await expect(takeSnapshot).not.toRerender(); -}); - -test("parses scalar fields in the result and serializes them in the error with an `all` error policy", async () => { - const query = gql` - query Event { - event { - id - startDate - endDate - } - } - `; - const client = new ApolloClient({ - cache: new InMemoryCache({ - scalars: { Date: dateScalar }, - typePolicies: { - Event: { - fields: { - startDate: { scalar: "Date" }, - endDate: { scalar: "Date" }, - }, - }, - }, - }), - link: new ApolloLink(() => - of({ - data: { - event: { - __typename: "Event", - id: "1", - startDate: "2026-01-01", - endDate: null, - }, - }, - errors: [ - { - message: "Could not resolve endDate", - path: ["event", "endDate"], - }, - ], - }).pipe(delay(20)) - ), - }); - - using _disabledAct = disableActEnvironment(); - const { takeSnapshot } = await renderHookToSnapshotStream( - () => useQuery(query, { errorPolicy: "all" }), - { wrapper: createClientWrapper(client) } - ); - - await expect(takeSnapshot()).resolves.toStrictEqualTyped({ - data: undefined, - dataState: "empty", - loading: true, - networkStatus: NetworkStatus.loading, - previousData: undefined, - variables: {}, - }); - - await expect(takeSnapshot()).resolves.toStrictEqualTyped({ - data: { - event: { - __typename: "Event", - id: "1", - startDate: new Date(2026, 0, 1), - endDate: null, - }, - }, - dataState: "complete", - error: new CombinedGraphQLErrors({ - data: { - event: { - __typename: "Event", - id: "1", - // TODO: Determine if this is correct - startDate: new Date(2026, 0, 1), - endDate: null, - }, - }, - errors: [ - { - message: "Could not resolve endDate", - path: ["event", "endDate"], - }, - ], - }), - loading: false, - networkStatus: NetworkStatus.error, - previousData: undefined, - variables: {}, - }); - - await expect(takeSnapshot).not.toRerender(); -}); - -test("parses custom scalar fields with an `ignore` error policy", async () => { - const query = gql` - query Event { - event { - id - startDate - endDate - } - } - `; - const client = new ApolloClient({ - cache: new InMemoryCache({ - scalars: { Date: dateScalar }, - typePolicies: { - Event: { - fields: { - startDate: { scalar: "Date" }, - endDate: { scalar: "Date" }, - }, - }, - }, - }), - link: new ApolloLink(() => - of({ - data: { - event: { - __typename: "Event", - id: "1", - startDate: "2026-01-01", - endDate: null, - }, - }, - errors: [ - { - message: "Could not resolve endDate", - path: ["event", "endDate"], - }, - ], - }).pipe(delay(20)) - ), - }); - - using _disabledAct = disableActEnvironment(); - const { takeSnapshot } = await renderHookToSnapshotStream( - () => useQuery(query, { errorPolicy: "ignore" }), - { wrapper: createClientWrapper(client) } - ); - - await expect(takeSnapshot()).resolves.toStrictEqualTyped({ - data: undefined, - dataState: "empty", - loading: true, - networkStatus: NetworkStatus.loading, - previousData: undefined, - variables: {}, - }); - - await expect(takeSnapshot()).resolves.toStrictEqualTyped({ - data: { - event: { - __typename: "Event", - id: "1", - startDate: new Date(2026, 0, 1), - endDate: null, - }, - }, - dataState: "complete", - loading: false, - networkStatus: NetworkStatus.ready, - previousData: undefined, - variables: {}, - }); - - await expect(takeSnapshot).not.toRerender(); -}); - -test("parses custom scalar fields across `@defer` payloads (defer20220824)", async () => { - const link = mockDefer20220824(); - const client = new ApolloClient({ - cache: new InMemoryCache({ - scalars: { Date: dateScalar }, - typePolicies: { - Event: { - fields: { - startDate: { scalar: "Date" }, - endDate: { scalar: "Date" }, - }, - }, - }, - }), - link: link.httpLink, - incrementalHandler: new Defer20220824Handler(), - }); - const query = gql` - query Event { - event { - id - startDate - ... @defer { - endDate - } - } - } - `; - - using _disabledAct = disableActEnvironment(); - const { takeSnapshot } = await renderHookToSnapshotStream( - () => useQuery(query), - { wrapper: createClientWrapper(client) } - ); - - await expect(takeSnapshot()).resolves.toStrictEqualTyped({ - data: undefined, - dataState: "empty", - loading: true, - networkStatus: NetworkStatus.loading, - previousData: undefined, - variables: {}, - }); - - link.enqueueInitialChunk({ - data: { - event: { - __typename: "Event", - id: "1", - startDate: "2026-01-01", - }, - }, - hasNext: true, - }); - - await expect(takeSnapshot()).resolves.toStrictEqualTyped({ - data: markAsStreaming({ - event: { - __typename: "Event", - id: "1", - startDate: new Date(2026, 0, 1), - }, - }), - dataState: "streaming", - loading: true, - networkStatus: NetworkStatus.streaming, - previousData: undefined, - variables: {}, - }); - - link.enqueueSubsequentChunk({ - incremental: [{ data: { endDate: "2026-02-02" }, path: ["event"] }], - hasNext: false, - }); - - await expect(takeSnapshot()).resolves.toStrictEqualTyped({ - data: { - event: { - __typename: "Event", - id: "1", - startDate: new Date(2026, 0, 1), - endDate: new Date(2026, 1, 2), - }, - }, - dataState: "complete", - loading: false, - networkStatus: NetworkStatus.ready, - previousData: { - event: { - __typename: "Event", - id: "1", - startDate: new Date(2026, 0, 1), - }, - }, - variables: {}, - }); - - await expect(takeSnapshot).not.toRerender(); -}); - -test("parses custom scalar fields across `@defer` payloads (graphql17Alpha9)", async () => { - const link = mockDeferStreamGraphQL17Alpha9(); - const client = new ApolloClient({ - cache: new InMemoryCache({ - scalars: { Date: dateScalar }, - typePolicies: { - Event: { - fields: { - startDate: { scalar: "Date" }, - endDate: { scalar: "Date" }, - }, - }, - }, - }), - link: link.httpLink, - incrementalHandler: new GraphQL17Alpha9Handler(), - }); - const query = gql` - query Event { - event { - id - startDate - ... @defer { - endDate - } - } - } - `; - - using _disabledAct = disableActEnvironment(); - const { takeSnapshot } = await renderHookToSnapshotStream( - () => useQuery(query), - { wrapper: createClientWrapper(client) } - ); - - await expect(takeSnapshot()).resolves.toStrictEqualTyped({ - data: undefined, - dataState: "empty", - loading: true, - networkStatus: NetworkStatus.loading, - previousData: undefined, - variables: {}, - }); - - link.enqueueInitialChunk({ - data: { - event: { - __typename: "Event", - id: "1", - startDate: "2026-01-01", - }, - }, - pending: [{ id: "0", path: ["event"] }], - hasNext: true, - }); - - await expect(takeSnapshot()).resolves.toStrictEqualTyped({ - data: markAsStreaming({ - event: { - __typename: "Event", - id: "1", - startDate: new Date(2026, 0, 1), - }, - }), - dataState: "streaming", - loading: true, - networkStatus: NetworkStatus.streaming, - previousData: undefined, - variables: {}, - }); - - link.enqueueSubsequentChunk({ - incremental: [{ data: { endDate: "2026-02-02" }, id: "0" }], - completed: [{ id: "0" }], - hasNext: false, - }); - - await expect(takeSnapshot()).resolves.toStrictEqualTyped({ - data: { - event: { - __typename: "Event", - id: "1", - startDate: new Date(2026, 0, 1), - endDate: new Date(2026, 1, 2), - }, - }, - dataState: "complete", - loading: false, - networkStatus: NetworkStatus.ready, - previousData: { - event: { - __typename: "Event", - id: "1", - startDate: new Date(2026, 0, 1), - }, - }, - variables: {}, - }); - - await expect(takeSnapshot).not.toRerender(); -}); - -test("parses custom scalar fields across `@stream` payloads (defer20220824)", async () => { - const link = mockDefer20220824(); - const client = new ApolloClient({ - cache: new InMemoryCache({ - scalars: { Date: dateScalar }, - typePolicies: { - Event: { - fields: { - startDate: { scalar: "Date" }, - }, - }, - }, - }), - link: link.httpLink, - incrementalHandler: new Defer20220824Handler(), - }); - const query = gql` - query Events { - events @stream(initialCount: 1) { - id - startDate - } - } - `; - - using _disabledAct = disableActEnvironment(); - const { takeSnapshot } = await renderHookToSnapshotStream( - () => useQuery(query), - { wrapper: createClientWrapper(client) } - ); - - await expect(takeSnapshot()).resolves.toStrictEqualTyped({ - data: undefined, - dataState: "empty", - loading: true, - networkStatus: NetworkStatus.loading, - previousData: undefined, - variables: {}, - }); - - link.enqueueInitialChunk({ - data: { - events: [ - { - __typename: "Event", - id: "1", - startDate: "2026-01-01", - }, - ], - }, - hasNext: true, - }); - - await expect(takeSnapshot()).resolves.toStrictEqualTyped({ - data: { - events: [ - { - __typename: "Event", - id: "1", - startDate: new Date(2026, 0, 1), - }, - ], - }, - dataState: "complete", - loading: true, - networkStatus: NetworkStatus.streaming, - previousData: undefined, - variables: {}, - }); - - link.enqueueSubsequentChunk({ - incremental: [ - { - items: [ - { - __typename: "Event", - id: "2", - startDate: "2026-02-02", - }, - ] as any, - path: ["events", 1], - }, - ], - hasNext: false, - }); - - await expect(takeSnapshot()).resolves.toStrictEqualTyped({ - data: { - events: [ - { - __typename: "Event", - id: "1", - startDate: new Date(2026, 0, 1), - }, - { - __typename: "Event", - id: "2", - startDate: new Date(2026, 1, 2), - }, - ], - }, - dataState: "complete", - loading: false, - networkStatus: NetworkStatus.ready, - previousData: { - events: [ - { - __typename: "Event", - id: "1", - startDate: new Date(2026, 0, 1), - }, - ], - }, - variables: {}, - }); - - await expect(takeSnapshot).not.toRerender(); -}); - -test("parses custom scalar fields across `@stream` payloads (graphql17Alpha9)", async () => { - const link = mockDeferStreamGraphQL17Alpha9(); - const client = new ApolloClient({ - cache: new InMemoryCache({ - scalars: { Date: dateScalar }, - typePolicies: { - Event: { - fields: { - startDate: { scalar: "Date" }, - }, - }, - }, - }), - link: link.httpLink, - incrementalHandler: new GraphQL17Alpha9Handler(), - }); - const query = gql` - query Events { - events @stream(initialCount: 1) { - id - startDate - } - } - `; - - using _disabledAct = disableActEnvironment(); - const { takeSnapshot } = await renderHookToSnapshotStream( - () => useQuery(query), - { wrapper: createClientWrapper(client) } - ); - - await expect(takeSnapshot()).resolves.toStrictEqualTyped({ - data: undefined, - dataState: "empty", - loading: true, - networkStatus: NetworkStatus.loading, - previousData: undefined, - variables: {}, - }); - - link.enqueueInitialChunk({ - data: { - events: [ - { - __typename: "Event", - id: "1", - startDate: "2026-01-01", - }, - ], - }, - pending: [{ id: "0", path: ["events"] }], - hasNext: true, - }); - - await expect(takeSnapshot()).resolves.toStrictEqualTyped({ - data: { - events: [ - { - __typename: "Event", - id: "1", - startDate: new Date(2026, 0, 1), - }, - ], - }, - dataState: "complete", - loading: true, - networkStatus: NetworkStatus.streaming, - previousData: undefined, - variables: {}, - }); - - link.enqueueSubsequentChunk({ - incremental: [ - { - items: [ - { - __typename: "Event", - id: "2", - startDate: "2026-02-02", - }, - ] as any, - id: "0", - }, - ], - completed: [{ id: "0" }], - hasNext: false, - }); - - await expect(takeSnapshot()).resolves.toStrictEqualTyped({ - data: { - events: [ - { - __typename: "Event", - id: "1", - startDate: new Date(2026, 0, 1), - }, - { - __typename: "Event", - id: "2", - startDate: new Date(2026, 1, 2), - }, - ], - }, - dataState: "complete", - loading: false, - networkStatus: NetworkStatus.ready, - previousData: { - events: [ - { - __typename: "Event", - id: "1", - startDate: new Date(2026, 0, 1), - }, - ], - }, - variables: {}, - }); - - await expect(takeSnapshot).not.toRerender(); -}); diff --git a/src/react/hooks/__tests__/useQueryRefHandlers/customScalars.test.tsx b/src/react/hooks/__tests__/useQueryRefHandlers/customScalars.test.tsx index 9d4f6078f1f..39313afd7a5 100644 --- a/src/react/hooks/__tests__/useQueryRefHandlers/customScalars.test.tsx +++ b/src/react/hooks/__tests__/useQueryRefHandlers/customScalars.test.tsx @@ -471,7 +471,7 @@ test("serializes scalar variables passed to subscribeToMore", async () => { }); }); -test("preserves referential identity when refetching identical serialized scalar values", async () => { +test("preserves referential identity when refetching identical scalar values", async () => { let refetchPromise!: ReturnType< ReturnType["refetch"] >; diff --git a/src/react/hooks/__tests__/useSubscription/customScalars.test.tsx b/src/react/hooks/__tests__/useSubscription/customScalars.test.tsx index 5f9741aef45..f9b34c1bcff 100644 --- a/src/react/hooks/__tests__/useSubscription/customScalars.test.tsx +++ b/src/react/hooks/__tests__/useSubscription/customScalars.test.tsx @@ -3,6 +3,7 @@ import { renderHookToSnapshotStream, } from "@testing-library/react-render-stream"; +import type { TypedDocumentNode } from "@apollo/client"; import { ApolloClient, gql } from "@apollo/client"; import { InMemoryCache } from "@apollo/client/cache"; import { useSubscription } from "@apollo/client/react"; @@ -12,7 +13,7 @@ import { dateScalar, } from "@apollo/client/testing/internal"; -test("serializes scalar variables used in field arguments", async () => { +test("serializes parsed scalar values in variables", async () => { const link = new MockSubscriptionLink(); const client = new ApolloClient({ cache: new InMemoryCache({ @@ -73,138 +74,7 @@ test("serializes scalar variables used in field arguments", async () => { expect(link.operation?.variables).toStrictEqualTyped({ date: "2026-01-01" }); }); -test("serializes scalar variables used in directive arguments", async () => { - const link = new MockSubscriptionLink(); - const client = new ApolloClient({ - cache: new InMemoryCache({ - scalars: { Date: dateScalar }, - }), - link, - }); - - const subscription = gql` - subscription EventCreated($date: Date!) { - eventCreated @on(date: $date) { - name - } - } - `; - - using _disabledAct = disableActEnvironment(); - const { takeSnapshot } = await renderHookToSnapshotStream( - () => - useSubscription(subscription, { - variables: { date: new Date(2026, 0, 1) }, - }), - { wrapper: createClientWrapper(client) } - ); - - await expect(takeSnapshot()).resolves.toStrictEqualTyped({ - data: undefined, - error: undefined, - loading: true, - }); - - link.simulateResult( - { - result: { - data: { - eventCreated: { - __typename: "Event", - name: "GraphQL Summit", - }, - }, - }, - }, - true - ); - - await expect(takeSnapshot()).resolves.toStrictEqualTyped({ - data: { - eventCreated: { - __typename: "Event", - name: "GraphQL Summit", - }, - }, - error: undefined, - loading: false, - }); - - await expect(takeSnapshot).not.toRerender(); - expect(link.operation?.variables).toStrictEqualTyped({ date: "2026-01-01" }); -}); - -test("serializes scalar fields in input object variables", async () => { - const link = new MockSubscriptionLink(); - const client = new ApolloClient({ - cache: new InMemoryCache({ - scalars: { Date: dateScalar }, - inputObjects: { - EventFilter: { - fields: { - date: "Date", - }, - }, - }, - }), - link, - }); - - const subscription = gql` - subscription EventCreated($filter: EventFilter!) { - eventCreated(filter: $filter) { - name - } - } - `; - - using _disabledAct = disableActEnvironment(); - const { takeSnapshot } = await renderHookToSnapshotStream( - () => - useSubscription(subscription, { - variables: { filter: { date: new Date(2026, 0, 1) } }, - }), - { wrapper: createClientWrapper(client) } - ); - - await expect(takeSnapshot()).resolves.toStrictEqualTyped({ - data: undefined, - error: undefined, - loading: true, - }); - - link.simulateResult( - { - result: { - data: { - eventCreated: { - __typename: "Event", - name: "GraphQL Summit", - }, - }, - }, - }, - true - ); - - await expect(takeSnapshot()).resolves.toStrictEqualTyped({ - data: { - eventCreated: { - __typename: "Event", - name: "GraphQL Summit", - }, - }, - error: undefined, - loading: false, - }); - - await expect(takeSnapshot).not.toRerender(); - expect(link.operation?.variables).toStrictEqualTyped({ - filter: { date: "2026-01-01" }, - }); -}); - -test("parses custom scalar fields with a cache-only fetch policy", async () => { +test("returns parsed scalar fields", async () => { const subscription = gql` subscription EventCreated { eventCreated { @@ -230,7 +100,7 @@ test("parses custom scalar fields with a cache-only fetch policy", async () => { using _disabledAct = disableActEnvironment(); const { takeSnapshot } = await renderHookToSnapshotStream( - () => useSubscription(subscription, { fetchPolicy: "cache-only" }), + () => useSubscription(subscription), { wrapper: createClientWrapper(client) } ); @@ -270,8 +140,10 @@ test("parses custom scalar fields with a cache-only fetch policy", async () => { await expect(takeSnapshot).not.toRerender(); }); -test("parses custom scalar fields with a cache-first fetch policy", async () => { - const subscription = gql` +test("preserves referential identity when a subscription emits identical scalar values", async () => { + const subscription: TypedDocumentNode<{ + eventCreated: { __typename: "Event"; id: string; startDate: Date }; + }> = gql` subscription EventCreated { eventCreated { id @@ -295,8 +167,8 @@ test("parses custom scalar fields with a cache-first fetch policy", async () => }); using _disabledAct = disableActEnvironment(); - const { takeSnapshot } = await renderHookToSnapshotStream( - () => useSubscription(subscription, { fetchPolicy: "cache-first" }), + const { takeSnapshot, getCurrentSnapshot } = await renderHookToSnapshotStream( + () => useSubscription(subscription), { wrapper: createClientWrapper(client) } ); @@ -306,20 +178,17 @@ test("parses custom scalar fields with a cache-first fetch policy", async () => loading: true, }); - link.simulateResult( - { - result: { - data: { - eventCreated: { - __typename: "Event", - id: "1", - startDate: "2026-01-01", - }, + link.simulateResult({ + result: { + data: { + eventCreated: { + __typename: "Event", + id: "1", + startDate: "2026-01-01", }, }, }, - true - ); + }); await expect(takeSnapshot()).resolves.toStrictEqualTyped({ data: { @@ -333,44 +202,7 @@ test("parses custom scalar fields with a cache-first fetch policy", async () => loading: false, }); - await expect(takeSnapshot).not.toRerender(); -}); - -test("parses custom scalar fields with a network-only fetch policy", async () => { - const subscription = gql` - subscription EventCreated { - eventCreated { - id - startDate - } - } - `; - const link = new MockSubscriptionLink(); - const client = new ApolloClient({ - cache: new InMemoryCache({ - scalars: { Date: dateScalar }, - typePolicies: { - Event: { - fields: { - startDate: { scalar: "Date" }, - }, - }, - }, - }), - link, - }); - - using _disabledAct = disableActEnvironment(); - const { takeSnapshot } = await renderHookToSnapshotStream( - () => useSubscription(subscription, { fetchPolicy: "network-only" }), - { wrapper: createClientWrapper(client) } - ); - - await expect(takeSnapshot()).resolves.toStrictEqualTyped({ - data: undefined, - error: undefined, - loading: true, - }); + const previousData = getCurrentSnapshot().data; link.simulateResult( { @@ -399,74 +231,9 @@ test("parses custom scalar fields with a network-only fetch policy", async () => loading: false, }); + expect(getCurrentSnapshot().data!.eventCreated).toBe( + previousData!.eventCreated + ); + await expect(takeSnapshot).not.toRerender(); }); - -test.failing( - "parses custom scalar fields with a no-cache fetch policy", - async () => { - const subscription = gql` - subscription EventCreated { - eventCreated { - id - startDate - } - } - `; - const link = new MockSubscriptionLink(); - const client = new ApolloClient({ - cache: new InMemoryCache({ - scalars: { Date: dateScalar }, - typePolicies: { - Event: { - fields: { - startDate: { scalar: "Date" }, - }, - }, - }, - }), - link, - }); - - using _disabledAct = disableActEnvironment(); - const { takeSnapshot } = await renderHookToSnapshotStream( - () => useSubscription(subscription, { fetchPolicy: "no-cache" }), - { wrapper: createClientWrapper(client) } - ); - - await expect(takeSnapshot()).resolves.toStrictEqualTyped({ - data: undefined, - error: undefined, - loading: true, - }); - - link.simulateResult( - { - result: { - data: { - eventCreated: { - __typename: "Event", - id: "1", - startDate: "2026-01-01", - }, - }, - }, - }, - true - ); - - await expect(takeSnapshot()).resolves.toStrictEqualTyped({ - data: { - eventCreated: { - __typename: "Event", - id: "1", - startDate: new Date(2026, 0, 1), - }, - }, - error: undefined, - loading: false, - }); - - await expect(takeSnapshot).not.toRerender(); - } -); diff --git a/src/react/hooks/__tests__/useSuspenseQuery/customScalars.test.tsx b/src/react/hooks/__tests__/useSuspenseQuery/customScalars.test.tsx index 17154023002..a91b46fe98a 100644 --- a/src/react/hooks/__tests__/useSuspenseQuery/customScalars.test.tsx +++ b/src/react/hooks/__tests__/useSuspenseQuery/customScalars.test.tsx @@ -2,31 +2,17 @@ import { disableActEnvironment } from "@testing-library/react-render-stream"; import { delay, of } from "rxjs"; import type { OperationVariables } from "@apollo/client"; -import { - ApolloClient, - ApolloLink, - CombinedGraphQLErrors, - gql, - NetworkStatus, -} from "@apollo/client"; +import { ApolloClient, ApolloLink, gql, NetworkStatus } from "@apollo/client"; import { InMemoryCache } from "@apollo/client/cache"; -import { - Defer20220824Handler, - GraphQL17Alpha9Handler, -} from "@apollo/client/incremental"; import { useSuspenseQuery } from "@apollo/client/react"; import { createClientWrapper, dateScalar, - markAsStreaming, - mockDefer20220824, - mockDeferStreamGraphQL17Alpha9, - spyOnConsole, } from "@apollo/client/testing/internal"; import { renderUseSuspenseQuery } from "./testUtils.js"; -test("serializes scalar variables used in field arguments", async () => { +test("serializes parsed scalar values in variables", async () => { let requestVariables!: OperationVariables; const link = new ApolloLink((operation) => { @@ -90,148 +76,7 @@ test("serializes scalar variables used in field arguments", async () => { expect(requestVariables).toStrictEqualTyped({ date: "2026-01-01" }); }); -test("serializes scalar variables used in directive arguments", async () => { - let requestVariables!: OperationVariables; - - const link = new ApolloLink((operation) => { - requestVariables = operation.variables; - - return of({ - data: { event: { __typename: "Event", name: "GraphQL Summit" } }, - }); - }); - - const client = new ApolloClient({ - cache: new InMemoryCache({ - scalars: { - Date: dateScalar, - }, - }), - link, - }); - const query = gql` - query Event($date: Date!) { - event @on(date: $date) { - name - } - } - `; - - using _disabledAct = disableActEnvironment(); - const { takeRender } = await renderUseSuspenseQuery( - () => - useSuspenseQuery(query, { - variables: { - date: new Date(2026, 0, 1), - }, - }), - { wrapper: createClientWrapper(client) } - ); - - await expect(takeRender()).resolves.toMatchObject({ - renderedComponents: [""], - }); - - { - const { snapshot, renderedComponents } = await takeRender(); - - expect(renderedComponents).toStrictEqual(["useSuspenseQuery"]); - expect(snapshot).toStrictEqualTyped({ - data: { - event: { - __typename: "Event", - name: "GraphQL Summit", - }, - }, - dataState: "complete", - networkStatus: NetworkStatus.ready, - error: undefined, - }); - } - - await expect(takeRender).not.toRerender(); - - expect(requestVariables).toStrictEqualTyped({ date: "2026-01-01" }); -}); - -test("serializes scalar fields in input object variables", async () => { - let requestVariables!: OperationVariables; - - const link = new ApolloLink((operation) => { - requestVariables = operation.variables; - - return of({ - data: { event: { __typename: "Event", name: "GraphQL Summit" } }, - }); - }); - - const client = new ApolloClient({ - cache: new InMemoryCache({ - scalars: { - Date: dateScalar, - }, - inputObjects: { - EventFilter: { - fields: { - date: "Date", - }, - }, - }, - }), - link, - }); - const query = gql` - query Event($filter: EventFilter!) { - event(filter: $filter) { - name - } - } - `; - - using _disabledAct = disableActEnvironment(); - const { takeRender } = await renderUseSuspenseQuery( - () => - useSuspenseQuery(query, { - variables: { - filter: { - date: new Date(2026, 0, 1), - }, - }, - }), - { wrapper: createClientWrapper(client) } - ); - - await expect(takeRender()).resolves.toMatchObject({ - renderedComponents: [""], - }); - - { - const { snapshot, renderedComponents } = await takeRender(); - - expect(renderedComponents).toStrictEqual(["useSuspenseQuery"]); - expect(snapshot).toStrictEqualTyped({ - data: { - event: { - __typename: "Event", - name: "GraphQL Summit", - }, - }, - dataState: "complete", - networkStatus: NetworkStatus.ready, - error: undefined, - }); - } - - await expect(takeRender).not.toRerender(); - - expect(requestVariables).toStrictEqualTyped({ - filter: { - date: "2026-01-01", - }, - }); -}); - -test("parses cached custom scalar fields with a cache-first fetch policy", async () => { +test("returns parsed scalar fields", async () => { const query = gql` query Event { event { @@ -251,25 +96,31 @@ test("parses cached custom scalar fields with a cache-first fetch policy", async }, }, }), - link: ApolloLink.empty(), - }); - client.writeQuery({ - query, - data: { - event: { - __typename: "Event", - id: "1", - startDate: "2026-01-01", - }, - }, + link: new ApolloLink(() => + of({ + data: { + event: { + __typename: "Event", + id: "1", + startDate: "2026-01-01", + }, + }, + }).pipe(delay(20)) + ), }); using _disabledAct = disableActEnvironment(); const { takeRender } = await renderUseSuspenseQuery( - () => useSuspenseQuery(query, { fetchPolicy: "cache-first" }), + () => useSuspenseQuery(query), { wrapper: createClientWrapper(client) } ); + { + const { renderedComponents } = await takeRender(); + + expect(renderedComponents).toStrictEqual([""]); + } + { const { snapshot, renderedComponents } = await takeRender(); @@ -291,7 +142,7 @@ test("parses cached custom scalar fields with a cache-first fetch policy", async await expect(takeRender).not.toRerender(); }); -test("parses network custom scalar fields with a cache-first fetch policy", async () => { +test("preserves referential identity when refetching identical scalar values", async () => { const query = gql` query Event { event { @@ -325,10 +176,10 @@ test("parses network custom scalar fields with a cache-first fetch policy", asyn }); using _disabledAct = disableActEnvironment(); - const { takeRender } = await renderUseSuspenseQuery( - () => useSuspenseQuery(query, { fetchPolicy: "cache-first" }), - { wrapper: createClientWrapper(client) } - ); + const { takeRender, getCurrentSnapshot, refetch } = + await renderUseSuspenseQuery(() => useSuspenseQuery(query), { + wrapper: createClientWrapper(client), + }); { const { renderedComponents } = await takeRender(); @@ -354,74 +205,22 @@ test("parses network custom scalar fields with a cache-first fetch policy", asyn }); } - await expect(takeRender).not.toRerender(); -}); + const { data: previousData } = getCurrentSnapshot(); -test("parses cached and network custom scalar fields with a cache-and-network fetch policy", async () => { - const query = gql` - query Event { - event { - id - startDate - } - } - `; - const client = new ApolloClient({ - cache: new InMemoryCache({ - scalars: { Date: dateScalar }, - typePolicies: { - Event: { - fields: { - startDate: { scalar: "Date" }, - }, - }, - }, - }), - link: new ApolloLink(() => - of({ - data: { - event: { - __typename: "Event", - id: "1", - startDate: "2026-02-02", - }, - }, - }).pipe(delay(20)) - ), - }); - client.writeQuery({ - query, + await expect(refetch()).resolves.toStrictEqualTyped({ data: { event: { __typename: "Event", id: "1", - startDate: "2026-01-01", + startDate: new Date(2026, 0, 1), }, }, }); - using _disabledAct = disableActEnvironment(); - const { takeRender } = await renderUseSuspenseQuery( - () => useSuspenseQuery(query, { fetchPolicy: "cache-and-network" }), - { wrapper: createClientWrapper(client) } - ); - { - const { snapshot, renderedComponents } = await takeRender(); + const { renderedComponents } = await takeRender(); - expect(renderedComponents).toStrictEqual(["useSuspenseQuery"]); - expect(snapshot).toStrictEqualTyped({ - data: { - event: { - __typename: "Event", - id: "1", - startDate: new Date(2026, 0, 1), - }, - }, - dataState: "complete", - networkStatus: NetworkStatus.loading, - error: undefined, - }); + expect(renderedComponents).toStrictEqual([""]); } { @@ -433,308 +232,7 @@ test("parses cached and network custom scalar fields with a cache-and-network fe event: { __typename: "Event", id: "1", - startDate: new Date(2026, 1, 2), - }, - }, - dataState: "complete", - networkStatus: NetworkStatus.ready, - error: undefined, - }); - } - - await expect(takeRender).not.toRerender(); -}); - -test("parses network custom scalar fields with a cache-and-network fetch policy", async () => { - const query = gql` - query Event { - event { - id - startDate - } - } - `; - const client = new ApolloClient({ - cache: new InMemoryCache({ - scalars: { Date: dateScalar }, - typePolicies: { - Event: { - fields: { - startDate: { scalar: "Date" }, - }, - }, - }, - }), - link: new ApolloLink(() => - of({ - data: { - event: { - __typename: "Event", - id: "1", - startDate: "2026-01-01", - }, - }, - }).pipe(delay(20)) - ), - }); - - using _disabledAct = disableActEnvironment(); - const { takeRender } = await renderUseSuspenseQuery( - () => useSuspenseQuery(query, { fetchPolicy: "cache-and-network" }), - { wrapper: createClientWrapper(client) } - ); - - { - const { renderedComponents } = await takeRender(); - - expect(renderedComponents).toStrictEqual([""]); - } - - { - const { snapshot, renderedComponents } = await takeRender(); - - expect(renderedComponents).toStrictEqual(["useSuspenseQuery"]); - expect(snapshot).toStrictEqualTyped({ - data: { - event: { - __typename: "Event", - id: "1", - startDate: new Date(2026, 0, 1), - }, - }, - dataState: "complete", - networkStatus: NetworkStatus.ready, - error: undefined, - }); - } - - await expect(takeRender).not.toRerender(); -}); - -test("parses network custom scalar fields with a network-only fetch policy", async () => { - const query = gql` - query Event { - event { - id - startDate - } - } - `; - const client = new ApolloClient({ - cache: new InMemoryCache({ - scalars: { Date: dateScalar }, - typePolicies: { - Event: { - fields: { - startDate: { scalar: "Date" }, - }, - }, - }, - }), - link: new ApolloLink(() => - of({ - data: { - event: { - __typename: "Event", - id: "1", - startDate: "2026-01-01", - }, - }, - }).pipe(delay(20)) - ), - }); - - using _disabledAct = disableActEnvironment(); - const { takeRender } = await renderUseSuspenseQuery( - () => useSuspenseQuery(query, { fetchPolicy: "network-only" }), - { wrapper: createClientWrapper(client) } - ); - - { - const { renderedComponents } = await takeRender(); - - expect(renderedComponents).toStrictEqual([""]); - } - - { - const { snapshot, renderedComponents } = await takeRender(); - - expect(renderedComponents).toStrictEqual(["useSuspenseQuery"]); - expect(snapshot).toStrictEqualTyped({ - data: { - event: { - __typename: "Event", - id: "1", - startDate: new Date(2026, 0, 1), - }, - }, - dataState: "complete", - networkStatus: NetworkStatus.ready, - error: undefined, - }); - } - - await expect(takeRender).not.toRerender(); -}); - -test.failing( - "parses custom scalar fields with a no-cache fetch policy", - async () => { - const query = gql` - query Event { - event { - id - startDate - } - } - `; - const client = new ApolloClient({ - cache: new InMemoryCache({ - scalars: { Date: dateScalar }, - typePolicies: { - Event: { - fields: { - startDate: { scalar: "Date" }, - }, - }, - }, - }), - link: new ApolloLink(() => - of({ - data: { - event: { - __typename: "Event", - id: "1", - startDate: "2026-01-01", - }, - }, - }).pipe(delay(20)) - ), - }); - - using _disabledAct = disableActEnvironment(); - const { takeRender } = await renderUseSuspenseQuery( - () => useSuspenseQuery(query, { fetchPolicy: "no-cache" }), - { wrapper: createClientWrapper(client) } - ); - - { - const { renderedComponents } = await takeRender(); - - expect(renderedComponents).toStrictEqual([""]); - } - - { - const { snapshot, renderedComponents } = await takeRender(); - - expect(renderedComponents).toStrictEqual(["useSuspenseQuery"]); - expect(snapshot).toStrictEqualTyped({ - data: { - event: { - __typename: "Event", - id: "1", - startDate: new Date(2026, 0, 1), - }, - }, - dataState: "complete", - networkStatus: NetworkStatus.ready, - error: undefined, - }); - } - } -); - -test("preserves referential identity when refetching identical serialized scalar values", async () => { - const query = gql` - query Event { - event { - id - startDate - } - } - `; - const client = new ApolloClient({ - cache: new InMemoryCache({ - scalars: { Date: dateScalar }, - typePolicies: { - Event: { - fields: { - startDate: { scalar: "Date" }, - }, - }, - }, - }), - link: new ApolloLink(() => - of({ - data: { - event: { - __typename: "Event", - id: "1", - startDate: "2026-01-01", - }, - }, - }).pipe(delay(20)) - ), - }); - - using _disabledAct = disableActEnvironment(); - const { takeRender, getCurrentSnapshot, refetch } = - await renderUseSuspenseQuery(() => useSuspenseQuery(query), { - wrapper: createClientWrapper(client), - }); - - { - const { renderedComponents } = await takeRender(); - - expect(renderedComponents).toStrictEqual([""]); - } - - { - const { snapshot, renderedComponents } = await takeRender(); - - expect(renderedComponents).toStrictEqual(["useSuspenseQuery"]); - expect(snapshot).toStrictEqualTyped({ - data: { - event: { - __typename: "Event", - id: "1", - startDate: new Date(2026, 0, 1), - }, - }, - dataState: "complete", - networkStatus: NetworkStatus.ready, - error: undefined, - }); - } - - const { data: previousData } = getCurrentSnapshot(); - - await expect(refetch()).resolves.toStrictEqualTyped({ - data: { - event: { - __typename: "Event", - id: "1", - startDate: new Date(2026, 0, 1), - }, - }, - }); - - { - const { renderedComponents } = await takeRender(); - - expect(renderedComponents).toStrictEqual([""]); - } - - { - const { snapshot, renderedComponents } = await takeRender(); - - expect(renderedComponents).toStrictEqual(["useSuspenseQuery"]); - expect(snapshot).toStrictEqualTyped({ - data: { - event: { - __typename: "Event", - id: "1", - startDate: new Date(2026, 0, 1), + startDate: new Date(2026, 0, 1), }, }, dataState: "complete", @@ -747,679 +245,3 @@ test("preserves referential identity when refetching identical serialized scalar await expect(takeRender).not.toRerender(); }); - -test("serializes scalar fields in the error with a `none` error policy", async () => { - using _consoleSpy = spyOnConsole("error"); - const query = gql` - query Event { - event { - id - startDate - endDate - } - } - `; - const client = new ApolloClient({ - cache: new InMemoryCache({ - scalars: { Date: dateScalar }, - typePolicies: { - Event: { - fields: { - startDate: { scalar: "Date" }, - endDate: { scalar: "Date" }, - }, - }, - }, - }), - link: new ApolloLink(() => - of({ - data: { - event: { - __typename: "Event", - id: "1", - startDate: "2026-01-01", - endDate: null, - }, - }, - errors: [ - { - message: "Could not resolve endDate", - path: ["event", "endDate"], - }, - ], - }).pipe(delay(20)) - ), - }); - - using _disabledAct = disableActEnvironment(); - const { takeRender } = await renderUseSuspenseQuery( - () => useSuspenseQuery(query, { errorPolicy: "none" }), - { wrapper: createClientWrapper(client) } - ); - - { - const { renderedComponents } = await takeRender(); - - expect(renderedComponents).toStrictEqual([""]); - } - - { - const { snapshot, renderedComponents } = await takeRender(); - - expect(renderedComponents).toStrictEqual([""]); - expect(snapshot).toStrictEqualTyped({ - error: new CombinedGraphQLErrors({ - data: { - event: { - __typename: "Event", - id: "1", - startDate: "2026-01-01", - endDate: null, - }, - }, - errors: [ - { - message: "Could not resolve endDate", - path: ["event", "endDate"], - }, - ], - }), - }); - } - - await expect(takeRender).not.toRerender(); -}); - -test("parses scalar fields in the result and serializes them in the error with an `all` error policy", async () => { - const query = gql` - query Event { - event { - id - startDate - endDate - } - } - `; - const client = new ApolloClient({ - cache: new InMemoryCache({ - scalars: { Date: dateScalar }, - typePolicies: { - Event: { - fields: { - startDate: { scalar: "Date" }, - endDate: { scalar: "Date" }, - }, - }, - }, - }), - link: new ApolloLink(() => - of({ - data: { - event: { - __typename: "Event", - id: "1", - startDate: "2026-01-01", - endDate: null, - }, - }, - errors: [ - { - message: "Could not resolve endDate", - path: ["event", "endDate"], - }, - ], - }).pipe(delay(20)) - ), - }); - - using _disabledAct = disableActEnvironment(); - const { takeRender } = await renderUseSuspenseQuery( - () => useSuspenseQuery(query, { errorPolicy: "all" }), - { wrapper: createClientWrapper(client) } - ); - - { - const { renderedComponents } = await takeRender(); - - expect(renderedComponents).toStrictEqual([""]); - } - - { - const { snapshot, renderedComponents } = await takeRender(); - - expect(renderedComponents).toStrictEqual(["useSuspenseQuery"]); - expect(snapshot).toStrictEqualTyped({ - data: { - event: { - __typename: "Event", - id: "1", - startDate: new Date(2026, 0, 1), - endDate: null, - }, - }, - dataState: "complete", - networkStatus: NetworkStatus.error, - error: new CombinedGraphQLErrors({ - data: { - event: { - __typename: "Event", - id: "1", - // TODO: Determine if this is correct - startDate: new Date(2026, 0, 1), - endDate: null, - }, - }, - errors: [ - { - message: "Could not resolve endDate", - path: ["event", "endDate"], - }, - ], - }), - }); - } - - await expect(takeRender).not.toRerender(); -}); - -test("parses custom scalar fields with an `ignore` error policy", async () => { - const query = gql` - query Event { - event { - id - startDate - endDate - } - } - `; - const client = new ApolloClient({ - cache: new InMemoryCache({ - scalars: { Date: dateScalar }, - typePolicies: { - Event: { - fields: { - startDate: { scalar: "Date" }, - endDate: { scalar: "Date" }, - }, - }, - }, - }), - link: new ApolloLink(() => - of({ - data: { - event: { - __typename: "Event", - id: "1", - startDate: "2026-01-01", - endDate: null, - }, - }, - errors: [ - { - message: "Could not resolve endDate", - path: ["event", "endDate"], - }, - ], - }).pipe(delay(20)) - ), - }); - - using _disabledAct = disableActEnvironment(); - const { takeRender } = await renderUseSuspenseQuery( - () => useSuspenseQuery(query, { errorPolicy: "ignore" }), - { wrapper: createClientWrapper(client) } - ); - - { - const { renderedComponents } = await takeRender(); - - expect(renderedComponents).toStrictEqual([""]); - } - - { - const { snapshot, renderedComponents } = await takeRender(); - - expect(renderedComponents).toStrictEqual(["useSuspenseQuery"]); - expect(snapshot).toStrictEqualTyped({ - data: { - event: { - __typename: "Event", - id: "1", - startDate: new Date(2026, 0, 1), - endDate: null, - }, - }, - dataState: "complete", - networkStatus: NetworkStatus.ready, - error: undefined, - }); - } - - await expect(takeRender).not.toRerender(); -}); - -test("parses custom scalar fields across `@defer` payloads (defer20220824)", async () => { - const link = mockDefer20220824(); - const client = new ApolloClient({ - cache: new InMemoryCache({ - scalars: { Date: dateScalar }, - typePolicies: { - Event: { - fields: { - startDate: { scalar: "Date" }, - endDate: { scalar: "Date" }, - }, - }, - }, - }), - link: link.httpLink, - incrementalHandler: new Defer20220824Handler(), - }); - const query = gql` - query Event { - event { - id - startDate - ... @defer { - endDate - } - } - } - `; - - using _disabledAct = disableActEnvironment(); - const { takeRender } = await renderUseSuspenseQuery( - () => useSuspenseQuery(query), - { wrapper: createClientWrapper(client) } - ); - - { - const { renderedComponents } = await takeRender(); - - expect(renderedComponents).toStrictEqual([""]); - } - - link.enqueueInitialChunk({ - data: { - event: { - __typename: "Event", - id: "1", - startDate: "2026-01-01", - }, - }, - hasNext: true, - }); - - { - const { snapshot, renderedComponents } = await takeRender(); - - expect(renderedComponents).toStrictEqual(["useSuspenseQuery"]); - expect(snapshot).toStrictEqualTyped({ - data: markAsStreaming({ - event: { - __typename: "Event", - id: "1", - startDate: new Date(2026, 0, 1), - }, - }), - dataState: "streaming", - networkStatus: NetworkStatus.streaming, - error: undefined, - }); - } - - link.enqueueSubsequentChunk({ - incremental: [{ data: { endDate: "2026-02-02" }, path: ["event"] }], - hasNext: false, - }); - - { - const { snapshot, renderedComponents } = await takeRender(); - - expect(renderedComponents).toStrictEqual(["useSuspenseQuery"]); - expect(snapshot).toStrictEqualTyped({ - data: { - event: { - __typename: "Event", - id: "1", - startDate: new Date(2026, 0, 1), - endDate: new Date(2026, 1, 2), - }, - }, - dataState: "complete", - networkStatus: NetworkStatus.ready, - error: undefined, - }); - } - - await expect(takeRender).not.toRerender(); -}); - -test("parses custom scalar fields across `@defer` payloads (graphql17Alpha9)", async () => { - const link = mockDeferStreamGraphQL17Alpha9(); - const client = new ApolloClient({ - cache: new InMemoryCache({ - scalars: { Date: dateScalar }, - typePolicies: { - Event: { - fields: { - startDate: { scalar: "Date" }, - endDate: { scalar: "Date" }, - }, - }, - }, - }), - link: link.httpLink, - incrementalHandler: new GraphQL17Alpha9Handler(), - }); - const query = gql` - query Event { - event { - id - startDate - ... @defer { - endDate - } - } - } - `; - - using _disabledAct = disableActEnvironment(); - const { takeRender } = await renderUseSuspenseQuery( - () => useSuspenseQuery(query), - { wrapper: createClientWrapper(client) } - ); - - { - const { renderedComponents } = await takeRender(); - - expect(renderedComponents).toStrictEqual([""]); - } - - link.enqueueInitialChunk({ - data: { - event: { - __typename: "Event", - id: "1", - startDate: "2026-01-01", - }, - }, - pending: [{ id: "0", path: ["event"] }], - hasNext: true, - }); - - { - const { snapshot, renderedComponents } = await takeRender(); - - expect(renderedComponents).toStrictEqual(["useSuspenseQuery"]); - expect(snapshot).toStrictEqualTyped({ - data: markAsStreaming({ - event: { - __typename: "Event", - id: "1", - startDate: new Date(2026, 0, 1), - }, - }), - dataState: "streaming", - networkStatus: NetworkStatus.streaming, - error: undefined, - }); - } - - link.enqueueSubsequentChunk({ - incremental: [{ data: { endDate: "2026-02-02" }, id: "0" }], - completed: [{ id: "0" }], - hasNext: false, - }); - - { - const { snapshot, renderedComponents } = await takeRender(); - - expect(renderedComponents).toStrictEqual(["useSuspenseQuery"]); - expect(snapshot).toStrictEqualTyped({ - data: { - event: { - __typename: "Event", - id: "1", - startDate: new Date(2026, 0, 1), - endDate: new Date(2026, 1, 2), - }, - }, - dataState: "complete", - networkStatus: NetworkStatus.ready, - error: undefined, - }); - } - - await expect(takeRender).not.toRerender(); -}); - -test("parses custom scalar fields across `@stream` payloads (defer20220824)", async () => { - const link = mockDefer20220824(); - const client = new ApolloClient({ - cache: new InMemoryCache({ - scalars: { Date: dateScalar }, - typePolicies: { - Event: { - fields: { - startDate: { scalar: "Date" }, - }, - }, - }, - }), - link: link.httpLink, - incrementalHandler: new Defer20220824Handler(), - }); - const query = gql` - query Events { - events @stream(initialCount: 1) { - id - startDate - } - } - `; - - using _disabledAct = disableActEnvironment(); - const { takeRender } = await renderUseSuspenseQuery( - () => useSuspenseQuery(query), - { wrapper: createClientWrapper(client) } - ); - - { - const { renderedComponents } = await takeRender(); - - expect(renderedComponents).toStrictEqual([""]); - } - - link.enqueueInitialChunk({ - data: { - events: [ - { - __typename: "Event", - id: "1", - startDate: "2026-01-01", - }, - ], - }, - hasNext: true, - }); - - { - const { snapshot, renderedComponents } = await takeRender(); - - expect(renderedComponents).toStrictEqual(["useSuspenseQuery"]); - expect(snapshot).toStrictEqualTyped({ - data: { - events: [ - { - __typename: "Event", - id: "1", - startDate: new Date(2026, 0, 1), - }, - ], - }, - dataState: "complete", - networkStatus: NetworkStatus.streaming, - error: undefined, - }); - } - - link.enqueueSubsequentChunk({ - incremental: [ - { - items: [ - { - __typename: "Event", - id: "2", - startDate: "2026-02-02", - }, - ] as any, - path: ["events", 1], - }, - ], - hasNext: false, - }); - - { - const { snapshot, renderedComponents } = await takeRender(); - - expect(renderedComponents).toStrictEqual(["useSuspenseQuery"]); - expect(snapshot).toStrictEqualTyped({ - data: { - events: [ - { - __typename: "Event", - id: "1", - startDate: new Date(2026, 0, 1), - }, - { - __typename: "Event", - id: "2", - startDate: new Date(2026, 1, 2), - }, - ], - }, - dataState: "complete", - networkStatus: NetworkStatus.ready, - error: undefined, - }); - } - - await expect(takeRender).not.toRerender(); -}); - -test("parses custom scalar fields across `@stream` payloads (graphql17Alpha9)", async () => { - const link = mockDeferStreamGraphQL17Alpha9(); - const client = new ApolloClient({ - cache: new InMemoryCache({ - scalars: { Date: dateScalar }, - typePolicies: { - Event: { - fields: { - startDate: { scalar: "Date" }, - }, - }, - }, - }), - link: link.httpLink, - incrementalHandler: new GraphQL17Alpha9Handler(), - }); - const query = gql` - query Events { - events @stream(initialCount: 1) { - id - startDate - } - } - `; - - using _disabledAct = disableActEnvironment(); - const { takeRender } = await renderUseSuspenseQuery( - () => useSuspenseQuery(query), - { wrapper: createClientWrapper(client) } - ); - - { - const { renderedComponents } = await takeRender(); - - expect(renderedComponents).toStrictEqual([""]); - } - - link.enqueueInitialChunk({ - data: { - events: [ - { - __typename: "Event", - id: "1", - startDate: "2026-01-01", - }, - ], - }, - pending: [{ id: "0", path: ["events"] }], - hasNext: true, - }); - - { - const { snapshot, renderedComponents } = await takeRender(); - - expect(renderedComponents).toStrictEqual(["useSuspenseQuery"]); - expect(snapshot).toStrictEqualTyped({ - data: { - events: [ - { - __typename: "Event", - id: "1", - startDate: new Date(2026, 0, 1), - }, - ], - }, - dataState: "complete", - networkStatus: NetworkStatus.streaming, - error: undefined, - }); - } - - link.enqueueSubsequentChunk({ - incremental: [ - { - items: [ - { - __typename: "Event", - id: "2", - startDate: "2026-02-02", - }, - ] as any, - id: "0", - }, - ], - completed: [{ id: "0" }], - hasNext: false, - }); - - { - const { snapshot, renderedComponents } = await takeRender(); - - expect(renderedComponents).toStrictEqual(["useSuspenseQuery"]); - expect(snapshot).toStrictEqualTyped({ - data: { - events: [ - { - __typename: "Event", - id: "1", - startDate: new Date(2026, 0, 1), - }, - { - __typename: "Event", - id: "2", - startDate: new Date(2026, 1, 2), - }, - ], - }, - dataState: "complete", - networkStatus: NetworkStatus.ready, - error: undefined, - }); - } - - await expect(takeRender).not.toRerender(); -}); diff --git a/src/react/query-preloader/__tests__/createQueryPreloader/customScalars.test.tsx b/src/react/query-preloader/__tests__/createQueryPreloader/customScalars.test.tsx index a89e9bb7d51..bda8a08a153 100644 --- a/src/react/query-preloader/__tests__/createQueryPreloader/customScalars.test.tsx +++ b/src/react/query-preloader/__tests__/createQueryPreloader/customScalars.test.tsx @@ -2,30 +2,14 @@ import { disableActEnvironment } from "@testing-library/react-render-stream"; import { delay, of } from "rxjs"; import type { OperationVariables } from "@apollo/client"; -import { - ApolloClient, - ApolloLink, - CombinedGraphQLErrors, - gql, - NetworkStatus, -} from "@apollo/client"; +import { ApolloClient, ApolloLink, gql, NetworkStatus } from "@apollo/client"; import { InMemoryCache } from "@apollo/client/cache"; -import { - Defer20220824Handler, - GraphQL17Alpha9Handler, -} from "@apollo/client/incremental"; import { createQueryPreloader } from "@apollo/client/react"; -import { - dateScalar, - markAsStreaming, - mockDefer20220824, - mockDeferStreamGraphQL17Alpha9, - spyOnConsole, -} from "@apollo/client/testing/internal"; +import { dateScalar } from "@apollo/client/testing/internal"; import { renderDefaultTestApp } from "./testUtils.js"; -test("serializes scalar variables used in field arguments", async () => { +test("serializes parsed scalar values in variables", async () => { let requestVariables!: OperationVariables; const client = new ApolloClient({ @@ -72,117 +56,12 @@ test("serializes scalar variables used in field arguments", async () => { expect(requestVariables).toStrictEqualTyped({ date: "2026-01-01" }); }); -test("serializes scalar variables used in directive arguments", async () => { - let requestVariables!: OperationVariables; - - const client = new ApolloClient({ - cache: new InMemoryCache({ - scalars: { Date: dateScalar }, - }), - link: new ApolloLink((operation) => { - requestVariables = operation.variables; - - return of({ - data: { event: { __typename: "Event", name: "GraphQL Summit" } }, - }); - }), - }); - - const query = gql` - query Event($date: Date!) { - event @on(date: $date) { - name - } - } - `; - - const preloadQuery = createQueryPreloader(client); - const queryRef = preloadQuery(query, { - variables: { date: new Date(2026, 0, 1) }, - }); - - using _disabledAct = disableActEnvironment(); - const { renderStream } = await renderDefaultTestApp({ client, queryRef }); - - { - const { renderedComponents, snapshot } = await renderStream.takeRender(); - - expect(renderedComponents).toStrictEqual(["App", "useReadQuery"]); - expect(snapshot.result).toStrictEqualTyped({ - data: { event: { __typename: "Event", name: "GraphQL Summit" } }, - dataState: "complete", - error: undefined, - networkStatus: NetworkStatus.ready, - }); - } - - expect(requestVariables).toStrictEqualTyped({ date: "2026-01-01" }); -}); - -test("serializes scalar fields in input object variables", async () => { - let requestVariables!: OperationVariables; - - const client = new ApolloClient({ - cache: new InMemoryCache({ - scalars: { Date: dateScalar }, - inputObjects: { - EventFilter: { - fields: { - date: "Date", - }, - }, - }, - }), - link: new ApolloLink((operation) => { - requestVariables = operation.variables; - - return of({ - data: { event: { __typename: "Event", name: "GraphQL Summit" } }, - }); - }), - }); - - const query = gql` - query Event($filter: EventFilter!) { - event(filter: $filter) { - name - } - } - `; - - const preloadQuery = createQueryPreloader(client); - const queryRef = preloadQuery(query, { - variables: { filter: { date: new Date(2026, 0, 1) } }, - }); - - using _disabledAct = disableActEnvironment(); - const { renderStream } = await renderDefaultTestApp({ client, queryRef }); - - { - const { renderedComponents, snapshot } = await renderStream.takeRender(); - - expect(renderedComponents).toStrictEqual(["App", "useReadQuery"]); - expect(snapshot.result).toStrictEqualTyped({ - data: { event: { __typename: "Event", name: "GraphQL Summit" } }, - dataState: "complete", - error: undefined, - networkStatus: NetworkStatus.ready, - }); - } - - expect(requestVariables).toStrictEqualTyped({ - filter: { date: "2026-01-01" }, - }); -}); - -test("serializes scalar fields in the error with a `none` error policy", async () => { - using _consoleSpy = spyOnConsole("error"); +test("returns parsed scalar fields", async () => { const query = gql` query Event { event { id startDate - endDate } } `; @@ -193,7 +72,6 @@ test("serializes scalar fields in the error with a `none` error policy", async ( Event: { fields: { startDate: { scalar: "Date" }, - endDate: { scalar: "Date" }, }, }, }, @@ -205,102 +83,14 @@ test("serializes scalar fields in the error with a `none` error policy", async ( __typename: "Event", id: "1", startDate: "2026-01-01", - endDate: null, }, }, - errors: [ - { - message: "Could not resolve endDate", - path: ["event", "endDate"], - }, - ], }).pipe(delay(20)) ), }); const preloadQuery = createQueryPreloader(client); - const queryRef = preloadQuery(query, { errorPolicy: "none" }); - - using _disabledAct = disableActEnvironment(); - const { renderStream } = await renderDefaultTestApp({ client, queryRef }); - - { - const { renderedComponents } = await renderStream.takeRender(); - - expect(renderedComponents).toStrictEqual(["App", ""]); - } - - { - const { snapshot, renderedComponents } = await renderStream.takeRender(); - - expect(renderedComponents).toStrictEqual(["Error"]); - expect(snapshot.error).toEqual( - new CombinedGraphQLErrors({ - data: { - event: { - __typename: "Event", - id: "1", - startDate: "2026-01-01", - endDate: null, - }, - }, - errors: [ - { - message: "Could not resolve endDate", - path: ["event", "endDate"], - }, - ], - }) - ); - } - - await expect(renderStream).not.toRerender(); -}); - -test("parses scalar fields in the result and serializes them in the error with an `all` error policy", async () => { - const query = gql` - query Event { - event { - id - startDate - endDate - } - } - `; - const client = new ApolloClient({ - cache: new InMemoryCache({ - scalars: { Date: dateScalar }, - typePolicies: { - Event: { - fields: { - startDate: { scalar: "Date" }, - endDate: { scalar: "Date" }, - }, - }, - }, - }), - link: new ApolloLink(() => - of({ - data: { - event: { - __typename: "Event", - id: "1", - startDate: "2026-01-01", - endDate: null, - }, - }, - errors: [ - { - message: "Could not resolve endDate", - path: ["event", "endDate"], - }, - ], - }).pipe(delay(20)) - ), - }); - - const preloadQuery = createQueryPreloader(client); - const queryRef = preloadQuery(query, { errorPolicy: "all" }); + const queryRef = preloadQuery(query); using _disabledAct = disableActEnvironment(); const { renderStream } = await renderDefaultTestApp({ client, queryRef }); @@ -312,7 +102,7 @@ test("parses scalar fields in the result and serializes them in the error with a } { - const { snapshot, renderedComponents } = await renderStream.takeRender(); + const { renderedComponents, snapshot } = await renderStream.takeRender(); expect(renderedComponents).toStrictEqual(["useReadQuery"]); expect(snapshot.result).toStrictEqualTyped({ @@ -321,41 +111,23 @@ test("parses scalar fields in the result and serializes them in the error with a __typename: "Event", id: "1", startDate: new Date(2026, 0, 1), - endDate: null, }, }, dataState: "complete", - networkStatus: NetworkStatus.error, - error: new CombinedGraphQLErrors({ - data: { - event: { - __typename: "Event", - id: "1", - // TODO: Determine if this is correct - startDate: new Date(2026, 0, 1), - endDate: null, - }, - }, - errors: [ - { - message: "Could not resolve endDate", - path: ["event", "endDate"], - }, - ], - }), + error: undefined, + networkStatus: NetworkStatus.ready, }); } - await expect(renderStream).not.toRerender(); + await expect(renderStream.takeRender).not.toRerender(); }); -test("parses custom scalar fields with an `ignore` error policy", async () => { +test("preserves referential identity when refetching identical scalar values", async () => { const query = gql` query Event { event { id startDate - endDate } } `; @@ -366,7 +138,6 @@ test("parses custom scalar fields with an `ignore` error policy", async () => { Event: { fields: { startDate: { scalar: "Date" }, - endDate: { scalar: "Date" }, }, }, }, @@ -378,130 +149,28 @@ test("parses custom scalar fields with an `ignore` error policy", async () => { __typename: "Event", id: "1", startDate: "2026-01-01", - endDate: null, }, }, - errors: [ - { - message: "Could not resolve endDate", - path: ["event", "endDate"], - }, - ], }).pipe(delay(20)) ), }); - const preloadQuery = createQueryPreloader(client); - const queryRef = preloadQuery(query, { errorPolicy: "ignore" }); - - using _disabledAct = disableActEnvironment(); - const { renderStream } = await renderDefaultTestApp({ client, queryRef }); - - { - const { renderedComponents } = await renderStream.takeRender(); - - expect(renderedComponents).toStrictEqual(["App", ""]); - } - - { - const { snapshot, renderedComponents } = await renderStream.takeRender(); - - expect(renderedComponents).toStrictEqual(["useReadQuery"]); - expect(snapshot.result).toStrictEqualTyped({ - data: { - event: { - __typename: "Event", - id: "1", - startDate: new Date(2026, 0, 1), - endDate: null, - }, - }, - dataState: "complete", - networkStatus: NetworkStatus.ready, - error: undefined, - }); - } - - await expect(renderStream).not.toRerender(); -}); - -test("parses custom scalar fields across `@defer` payloads (defer20220824)", async () => { - const link = mockDefer20220824(); - const client = new ApolloClient({ - cache: new InMemoryCache({ - scalars: { Date: dateScalar }, - typePolicies: { - Event: { - fields: { - startDate: { scalar: "Date" }, - endDate: { scalar: "Date" }, - }, - }, - }, - }), - link: link.httpLink, - incrementalHandler: new Defer20220824Handler(), - }); - const query = gql` - query Event { - event { - id - startDate - ... @defer { - endDate - } - } - } - `; - const preloadQuery = createQueryPreloader(client); const queryRef = preloadQuery(query); using _disabledAct = disableActEnvironment(); const { renderStream } = await renderDefaultTestApp({ client, queryRef }); + let previousData: unknown; + { const { renderedComponents } = await renderStream.takeRender(); expect(renderedComponents).toStrictEqual(["App", ""]); } - link.enqueueInitialChunk({ - data: { - event: { - __typename: "Event", - id: "1", - startDate: "2026-01-01", - }, - }, - hasNext: true, - }); - - { - const { snapshot, renderedComponents } = await renderStream.takeRender(); - - expect(renderedComponents).toStrictEqual(["useReadQuery"]); - expect(snapshot.result).toStrictEqualTyped({ - data: markAsStreaming({ - event: { - __typename: "Event", - id: "1", - startDate: new Date(2026, 0, 1), - }, - }), - dataState: "streaming", - networkStatus: NetworkStatus.streaming, - error: undefined, - }); - } - - link.enqueueSubsequentChunk({ - incremental: [{ data: { endDate: "2026-02-02" }, path: ["event"] }], - hasNext: false, - }); - { - const { snapshot, renderedComponents } = await renderStream.takeRender(); + const { renderedComponents, snapshot } = await renderStream.takeRender(); expect(renderedComponents).toStrictEqual(["useReadQuery"]); expect(snapshot.result).toStrictEqualTyped({ @@ -510,343 +179,31 @@ test("parses custom scalar fields across `@defer` payloads (defer20220824)", asy __typename: "Event", id: "1", startDate: new Date(2026, 0, 1), - endDate: new Date(2026, 1, 2), }, }, dataState: "complete", - networkStatus: NetworkStatus.ready, error: undefined, + networkStatus: NetworkStatus.ready, }); - } - - await expect(renderStream).not.toRerender(); -}); - -test("parses custom scalar fields across `@defer` payloads (graphql17Alpha9)", async () => { - const link = mockDeferStreamGraphQL17Alpha9(); - const client = new ApolloClient({ - cache: new InMemoryCache({ - scalars: { Date: dateScalar }, - typePolicies: { - Event: { - fields: { - startDate: { scalar: "Date" }, - endDate: { scalar: "Date" }, - }, - }, - }, - }), - link: link.httpLink, - incrementalHandler: new GraphQL17Alpha9Handler(), - }); - const query = gql` - query Event { - event { - id - startDate - ... @defer { - endDate - } - } - } - `; - const preloadQuery = createQueryPreloader(client); - const queryRef = preloadQuery(query); - - using _disabledAct = disableActEnvironment(); - const { renderStream } = await renderDefaultTestApp({ client, queryRef }); - - { - const { renderedComponents } = await renderStream.takeRender(); - - expect(renderedComponents).toStrictEqual(["App", ""]); + previousData = snapshot.result!.data; } - link.enqueueInitialChunk({ + await expect( + client.query({ query, fetchPolicy: "network-only" }) + ).resolves.toStrictEqualTyped({ data: { event: { __typename: "Event", id: "1", - startDate: "2026-01-01", + startDate: new Date(2026, 0, 1), }, }, - pending: [{ id: "0", path: ["event"] }], - hasNext: true, }); - { - const { snapshot, renderedComponents } = await renderStream.takeRender(); - - expect(renderedComponents).toStrictEqual(["useReadQuery"]); - expect(snapshot.result).toStrictEqualTyped({ - data: markAsStreaming({ - event: { - __typename: "Event", - id: "1", - startDate: new Date(2026, 0, 1), - }, - }), - dataState: "streaming", - networkStatus: NetworkStatus.streaming, - error: undefined, - }); - } - - link.enqueueSubsequentChunk({ - incremental: [{ data: { endDate: "2026-02-02" }, id: "0" }], - completed: [{ id: "0" }], - hasNext: false, - }); - - { - const { snapshot, renderedComponents } = await renderStream.takeRender(); - - expect(renderedComponents).toStrictEqual(["useReadQuery"]); - expect(snapshot.result).toStrictEqualTyped({ - data: { - event: { - __typename: "Event", - id: "1", - startDate: new Date(2026, 0, 1), - endDate: new Date(2026, 1, 2), - }, - }, - dataState: "complete", - networkStatus: NetworkStatus.ready, - error: undefined, - }); - } - - await expect(renderStream).not.toRerender(); -}); - -test("parses custom scalar fields across `@stream` payloads (defer20220824)", async () => { - const link = mockDefer20220824(); - const client = new ApolloClient({ - cache: new InMemoryCache({ - scalars: { Date: dateScalar }, - typePolicies: { - Event: { - fields: { - startDate: { scalar: "Date" }, - }, - }, - }, - }), - link: link.httpLink, - incrementalHandler: new Defer20220824Handler(), - }); - const query = gql` - query Events { - events @stream(initialCount: 1) { - id - startDate - } - } - `; - - const preloadQuery = createQueryPreloader(client); - const queryRef = preloadQuery(query); - - using _disabledAct = disableActEnvironment(); - const { renderStream } = await renderDefaultTestApp({ client, queryRef }); - - { - const { renderedComponents } = await renderStream.takeRender(); - - expect(renderedComponents).toStrictEqual(["App", ""]); - } - - link.enqueueInitialChunk({ - data: { - events: [ - { - __typename: "Event", - id: "1", - startDate: "2026-01-01", - }, - ], - }, - hasNext: true, - }); - - { - const { snapshot, renderedComponents } = await renderStream.takeRender(); - - expect(renderedComponents).toStrictEqual(["useReadQuery"]); - expect(snapshot.result).toStrictEqualTyped({ - data: { - events: [ - { - __typename: "Event", - id: "1", - startDate: new Date(2026, 0, 1), - }, - ], - }, - dataState: "complete", - networkStatus: NetworkStatus.streaming, - error: undefined, - }); - } - - link.enqueueSubsequentChunk({ - incremental: [ - { - items: [ - { - __typename: "Event", - id: "2", - startDate: "2026-02-02", - }, - ] as any, - path: ["events", 1], - }, - ], - hasNext: false, - }); - - { - const { snapshot, renderedComponents } = await renderStream.takeRender(); - - expect(renderedComponents).toStrictEqual(["useReadQuery"]); - expect(snapshot.result).toStrictEqualTyped({ - data: { - events: [ - { - __typename: "Event", - id: "1", - startDate: new Date(2026, 0, 1), - }, - { - __typename: "Event", - id: "2", - startDate: new Date(2026, 1, 2), - }, - ], - }, - dataState: "complete", - networkStatus: NetworkStatus.ready, - error: undefined, - }); - } - - await expect(renderStream).not.toRerender(); -}); - -test("parses custom scalar fields across `@stream` payloads (graphql17Alpha9)", async () => { - const link = mockDeferStreamGraphQL17Alpha9(); - const client = new ApolloClient({ - cache: new InMemoryCache({ - scalars: { Date: dateScalar }, - typePolicies: { - Event: { - fields: { - startDate: { scalar: "Date" }, - }, - }, - }, - }), - link: link.httpLink, - incrementalHandler: new GraphQL17Alpha9Handler(), - }); - const query = gql` - query Events { - events @stream(initialCount: 1) { - id - startDate - } - } - `; - - const preloadQuery = createQueryPreloader(client); - const queryRef = preloadQuery(query); - - using _disabledAct = disableActEnvironment(); - const { renderStream } = await renderDefaultTestApp({ client, queryRef }); - - { - const { renderedComponents } = await renderStream.takeRender(); - - expect(renderedComponents).toStrictEqual(["App", ""]); - } - - link.enqueueInitialChunk({ - data: { - events: [ - { - __typename: "Event", - id: "1", - startDate: "2026-01-01", - }, - ], - }, - pending: [{ id: "0", path: ["events"] }], - hasNext: true, - }); - - { - const { snapshot, renderedComponents } = await renderStream.takeRender(); - - expect(renderedComponents).toStrictEqual(["useReadQuery"]); - expect(snapshot.result).toStrictEqualTyped({ - data: { - events: [ - { - __typename: "Event", - id: "1", - startDate: new Date(2026, 0, 1), - }, - ], - }, - dataState: "complete", - networkStatus: NetworkStatus.streaming, - error: undefined, - }); - } - - link.enqueueSubsequentChunk({ - incremental: [ - { - items: [ - { - __typename: "Event", - id: "2", - startDate: "2026-02-02", - }, - ] as any, - id: "0", - }, - ], - completed: [{ id: "0" }], - hasNext: false, - }); - - { - const { snapshot, renderedComponents } = await renderStream.takeRender(); - - expect(renderedComponents).toStrictEqual(["useReadQuery"]); - expect(snapshot.result).toStrictEqualTyped({ - data: { - events: [ - { - __typename: "Event", - id: "1", - startDate: new Date(2026, 0, 1), - }, - { - __typename: "Event", - id: "2", - startDate: new Date(2026, 1, 2), - }, - ], - }, - dataState: "complete", - networkStatus: NetworkStatus.ready, - error: undefined, - }); - } + await expect(renderStream.takeRender).not.toRerender(); - await expect(renderStream).not.toRerender(); + expect(renderStream.getCurrentRender().snapshot.result!.data).toBe( + previousData + ); });