From f77040d64bf02db1a6546bb2a42a2444164ad7fd Mon Sep 17 00:00:00 2001 From: Jerel Miller Date: Fri, 9 Feb 2024 09:33:28 -0700 Subject: [PATCH 1/7] Add failing test for changing variables with observableQuery --- src/core/__tests__/ObservableQuery.ts | 78 +++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/core/__tests__/ObservableQuery.ts b/src/core/__tests__/ObservableQuery.ts index 96ef174df4b..85eb406932c 100644 --- a/src/core/__tests__/ObservableQuery.ts +++ b/src/core/__tests__/ObservableQuery.ts @@ -3512,3 +3512,81 @@ test("handles changing variables in rapid succession before other request is com networkStatus: NetworkStatus.ready, }); }); + +test("does not return partial cache data when `returnPartialData` is false and new variables are passed in", async () => { + const cache = new InMemoryCache(); + const client = new ApolloClient({ + cache, + link: ApolloLink.empty(), + }); + + const query = gql` + query MyCar($id: ID) { + car(id: $id) { + id + make + } + } + `; + + const partialQuery = gql` + query MyCar($id: ID) { + car(id: $id) { + id + make + model + } + } + `; + + cache.writeQuery({ + query, + variables: { id: 1 }, + data: { + car: { + __typename: "Car", + id: 1, + make: "Ford", + model: "Pinto", + }, + }, + }); + + cache.writeQuery({ + query: partialQuery, + variables: { id: 2 }, + data: { + car: { + __typename: "Car", + id: 2, + make: "Ford", + model: "Bronco", + }, + }, + }); + + const observable = client.watchQuery({ + query: partialQuery, + variables: { id: 2 }, + returnPartialData: false, + notifyOnNetworkStatusChange: true, + }); + + const stream = new ObservableStream(observable); + + expect(await stream.takeNext()).toEqual({ + loading: false, + networkStatus: NetworkStatus.ready, + data: { + car: { __typename: "Car", id: 2, make: "Ford", model: "Bronco" }, + }, + }); + + observable.reobserve({ variables: { id: 1 } }); + + expect(await stream.takeNext()).toEqual({ + loading: true, + networkStatus: NetworkStatus.loading, + data: undefined, + }); +}); From c422bd1b183d523187720343287cdac655f2d9e1 Mon Sep 17 00:00:00 2001 From: Jerel Miller Date: Mon, 12 Feb 2024 11:36:24 -0700 Subject: [PATCH 2/7] Add test to ensure multiple calls to `getCurrentResult` don't lose data --- src/core/__tests__/ObservableQuery.ts | 123 ++++++++++++++++++++++++++ 1 file changed, 123 insertions(+) diff --git a/src/core/__tests__/ObservableQuery.ts b/src/core/__tests__/ObservableQuery.ts index 85eb406932c..97c7adb0eeb 100644 --- a/src/core/__tests__/ObservableQuery.ts +++ b/src/core/__tests__/ObservableQuery.ts @@ -2553,6 +2553,129 @@ describe("ObservableQuery", () => { }); }); + it("handles multiple calls to getCurrentResult without losing data while using `@defer`", async () => { + const query = gql` + { + greeting { + message + ... on Greeting @defer { + recipient { + name + } + } + } + } + `; + + const link = new MockSubscriptionLink(); + + const client = new ApolloClient({ + link, + cache: new InMemoryCache(), + }); + + const obs = client.watchQuery({ query }); + const stream = new ObservableStream(obs); + + setTimeout(() => { + link.simulateResult({ + result: { + data: { + greeting: { + message: "Hello world", + __typename: "Greeting", + }, + }, + hasNext: true, + }, + }); + }); + + { + const result = await stream.takeNext(); + expect(result.data).toEqual({ + greeting: { + message: "Hello world", + __typename: "Greeting", + }, + }); + } + + expect(obs.getCurrentResult().data).toEqual({ + greeting: { + message: "Hello world", + __typename: "Greeting", + }, + }); + // second call to `getCurrentResult` + expect(obs.getCurrentResult().data).toEqual({ + greeting: { + message: "Hello world", + __typename: "Greeting", + }, + }); + + setTimeout(() => { + link.simulateResult( + { + result: { + incremental: [ + { + data: { + recipient: { + name: "Alice", + __typename: "Person", + }, + __typename: "Greeting", + }, + path: ["greeting"], + }, + ], + hasNext: false, + }, + }, + true + ); + }); + + { + const result = await stream.takeNext(); + + expect(result.data).toEqual({ + greeting: { + message: "Hello world", + recipient: { + name: "Alice", + __typename: "Person", + }, + __typename: "Greeting", + }, + }); + } + + expect(obs.getCurrentResult().data).toEqual({ + greeting: { + message: "Hello world", + recipient: { + name: "Alice", + __typename: "Person", + }, + __typename: "Greeting", + }, + }); + + expect(obs.getCurrentResult().data).toEqual({ + greeting: { + message: "Hello world", + recipient: { + name: "Alice", + __typename: "Person", + }, + __typename: "Greeting", + }, + }); + }); + { type Result = Partial>; From 21ee3e093e8e4bafc6f8aea92537f08779d8cecb Mon Sep 17 00:00:00 2001 From: Jerel Miller Date: Mon, 12 Feb 2024 11:48:09 -0700 Subject: [PATCH 3/7] Swap names of queries for clarity --- src/core/__tests__/ObservableQuery.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/core/__tests__/ObservableQuery.ts b/src/core/__tests__/ObservableQuery.ts index 97c7adb0eeb..9d23d5711e3 100644 --- a/src/core/__tests__/ObservableQuery.ts +++ b/src/core/__tests__/ObservableQuery.ts @@ -3643,7 +3643,7 @@ test("does not return partial cache data when `returnPartialData` is false and n link: ApolloLink.empty(), }); - const query = gql` + const partialQuery = gql` query MyCar($id: ID) { car(id: $id) { id @@ -3652,7 +3652,7 @@ test("does not return partial cache data when `returnPartialData` is false and n } `; - const partialQuery = gql` + const query = gql` query MyCar($id: ID) { car(id: $id) { id @@ -3663,7 +3663,7 @@ test("does not return partial cache data when `returnPartialData` is false and n `; cache.writeQuery({ - query, + query: partialQuery, variables: { id: 1 }, data: { car: { @@ -3676,7 +3676,7 @@ test("does not return partial cache data when `returnPartialData` is false and n }); cache.writeQuery({ - query: partialQuery, + query, variables: { id: 2 }, data: { car: { @@ -3689,7 +3689,7 @@ test("does not return partial cache data when `returnPartialData` is false and n }); const observable = client.watchQuery({ - query: partialQuery, + query, variables: { id: 2 }, returnPartialData: false, notifyOnNetworkStatusChange: true, @@ -3709,7 +3709,7 @@ test("does not return partial cache data when `returnPartialData` is false and n expect(await stream.takeNext()).toEqual({ loading: true, - networkStatus: NetworkStatus.loading, + networkStatus: NetworkStatus.setVariables, data: undefined, }); }); From 2d3f8da258d1abffdd26e8b2ded10a6647fe920e Mon Sep 17 00:00:00 2001 From: Jerel Miller Date: Mon, 12 Feb 2024 11:51:59 -0700 Subject: [PATCH 4/7] Use mocks to test full result in failing test --- src/core/__tests__/ObservableQuery.ts | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/src/core/__tests__/ObservableQuery.ts b/src/core/__tests__/ObservableQuery.ts index 9d23d5711e3..b75d248dfc8 100644 --- a/src/core/__tests__/ObservableQuery.ts +++ b/src/core/__tests__/ObservableQuery.ts @@ -3637,12 +3637,6 @@ test("handles changing variables in rapid succession before other request is com }); test("does not return partial cache data when `returnPartialData` is false and new variables are passed in", async () => { - const cache = new InMemoryCache(); - const client = new ApolloClient({ - cache, - link: ApolloLink.empty(), - }); - const partialQuery = gql` query MyCar($id: ID) { car(id: $id) { @@ -3662,6 +3656,21 @@ test("does not return partial cache data when `returnPartialData` is false and n } `; + const cache = new InMemoryCache(); + const client = new ApolloClient({ + cache, + link: new MockLink([ + { + request: { query, variables: { id: 2 } }, + result: { + data: { + car: { __typename: "Car", id: 2, make: "Ford", model: "Bronco" }, + }, + }, + }, + ]), + }); + cache.writeQuery({ query: partialQuery, variables: { id: 1 }, @@ -3712,4 +3721,10 @@ test("does not return partial cache data when `returnPartialData` is false and n networkStatus: NetworkStatus.setVariables, data: undefined, }); + + expect(await stream.takeNext()).toEqual({ + loading: false, + networkStatus: NetworkStatus.ready, + data: { __typename: "Car", id: 1, make: "Ford", model: "Pinto" }, + }); }); From 29343c12134080f2ee9e0d766e93b5d66ebbdc71 Mon Sep 17 00:00:00 2001 From: Jerel Miller Date: Mon, 12 Feb 2024 11:53:06 -0700 Subject: [PATCH 5/7] Test calls to getCurrentResult as well --- src/core/__tests__/ObservableQuery.ts | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/core/__tests__/ObservableQuery.ts b/src/core/__tests__/ObservableQuery.ts index b75d248dfc8..acb57833b5e 100644 --- a/src/core/__tests__/ObservableQuery.ts +++ b/src/core/__tests__/ObservableQuery.ts @@ -3722,9 +3722,21 @@ test("does not return partial cache data when `returnPartialData` is false and n data: undefined, }); + expect(observable.getCurrentResult()).toEqual({ + loading: true, + networkStatus: NetworkStatus.setVariables, + data: undefined, + }); + expect(await stream.takeNext()).toEqual({ loading: false, networkStatus: NetworkStatus.ready, data: { __typename: "Car", id: 1, make: "Ford", model: "Pinto" }, }); + + expect(observable.getCurrentResult()).toEqual({ + loading: false, + networkStatus: NetworkStatus.ready, + data: { __typename: "Car", id: 1, make: "Ford", model: "Pinto" }, + }); }); From 3c9ce678365adfc5aacc3f26636f414845c42624 Mon Sep 17 00:00:00 2001 From: Jerel Miller Date: Thu, 5 Dec 2024 10:10:32 -0700 Subject: [PATCH 6/7] Use stream assertions --- src/core/__tests__/ObservableQuery.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/core/__tests__/ObservableQuery.ts b/src/core/__tests__/ObservableQuery.ts index acb57833b5e..7b923ed45e3 100644 --- a/src/core/__tests__/ObservableQuery.ts +++ b/src/core/__tests__/ObservableQuery.ts @@ -3706,7 +3706,7 @@ test("does not return partial cache data when `returnPartialData` is false and n const stream = new ObservableStream(observable); - expect(await stream.takeNext()).toEqual({ + await expect(stream).toEmitValue({ loading: false, networkStatus: NetworkStatus.ready, data: { @@ -3714,9 +3714,9 @@ test("does not return partial cache data when `returnPartialData` is false and n }, }); - observable.reobserve({ variables: { id: 1 } }); + await observable.reobserve({ variables: { id: 1 } }); - expect(await stream.takeNext()).toEqual({ + await expect(stream).toEmitValue({ loading: true, networkStatus: NetworkStatus.setVariables, data: undefined, @@ -3728,7 +3728,7 @@ test("does not return partial cache data when `returnPartialData` is false and n data: undefined, }); - expect(await stream.takeNext()).toEqual({ + await expect(stream).toEmitValue({ loading: false, networkStatus: NetworkStatus.ready, data: { __typename: "Car", id: 1, make: "Ford", model: "Pinto" }, From 2dbaaf66986f6a92ba346d18df47dbd332f5605f Mon Sep 17 00:00:00 2001 From: Jerel Miller Date: Thu, 5 Dec 2024 10:16:22 -0700 Subject: [PATCH 7/7] Swap variables used in test for clarity --- src/core/__tests__/ObservableQuery.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/core/__tests__/ObservableQuery.ts b/src/core/__tests__/ObservableQuery.ts index 7b923ed45e3..8cb498bb4db 100644 --- a/src/core/__tests__/ObservableQuery.ts +++ b/src/core/__tests__/ObservableQuery.ts @@ -3672,7 +3672,7 @@ test("does not return partial cache data when `returnPartialData` is false and n }); cache.writeQuery({ - query: partialQuery, + query, variables: { id: 1 }, data: { car: { @@ -3685,7 +3685,7 @@ test("does not return partial cache data when `returnPartialData` is false and n }); cache.writeQuery({ - query, + query: partialQuery, variables: { id: 2 }, data: { car: { @@ -3699,7 +3699,7 @@ test("does not return partial cache data when `returnPartialData` is false and n const observable = client.watchQuery({ query, - variables: { id: 2 }, + variables: { id: 1 }, returnPartialData: false, notifyOnNetworkStatusChange: true, }); @@ -3710,11 +3710,11 @@ test("does not return partial cache data when `returnPartialData` is false and n loading: false, networkStatus: NetworkStatus.ready, data: { - car: { __typename: "Car", id: 2, make: "Ford", model: "Bronco" }, + car: { __typename: "Car", id: 1, make: "Ford", model: "Pinto" }, }, }); - await observable.reobserve({ variables: { id: 1 } }); + await observable.reobserve({ variables: { id: 2 } }); await expect(stream).toEmitValue({ loading: true, @@ -3731,12 +3731,12 @@ test("does not return partial cache data when `returnPartialData` is false and n await expect(stream).toEmitValue({ loading: false, networkStatus: NetworkStatus.ready, - data: { __typename: "Car", id: 1, make: "Ford", model: "Pinto" }, + data: { __typename: "Car", id: 2, make: "Ford", model: "Bronco" }, }); expect(observable.getCurrentResult()).toEqual({ loading: false, networkStatus: NetworkStatus.ready, - data: { __typename: "Car", id: 1, make: "Ford", model: "Pinto" }, + data: { __typename: "Car", id: 2, make: "Ford", model: "Bronco" }, }); });