diff --git a/src/__tests__/fetchMore.ts b/src/__tests__/fetchMore.ts index 595c68385dc..17aeecd18df 100644 --- a/src/__tests__/fetchMore.ts +++ b/src/__tests__/fetchMore.ts @@ -3,10 +3,10 @@ import gql from 'graphql-tag'; import { mockSingleLink } from '../utilities/testing/mocking/mockLink'; import subscribeAndCount from '../utilities/testing/subscribeAndCount'; -import { InMemoryCache } from '../cache/inmemory/inMemoryCache'; +import { InMemoryCache, InMemoryCacheConfig } from '../cache/inmemory/inMemoryCache'; import { ApolloClient, NetworkStatus, ObservableQuery } from '../'; import { itAsync } from '../utilities/testing/itAsync'; -import { offsetLimitPagination } from '../utilities'; +import { offsetLimitPagination, concatPagination } from '../utilities'; describe('updateQuery on a simple query', () => { const query = gql` @@ -233,82 +233,181 @@ describe('fetchMore on an observable query', () => { }); } - itAsync('triggers new result withAsync new variables', (resolve, reject) => { - const observable = setup(reject, { - request: { - query, - variables: variablesMore, - }, - result: resultMore, + function setupWithCacheConfig( + reject: (reason: any) => any, + cacheConfig: InMemoryCacheConfig, + ...mockedResponses: any[] + ) { + const client = new ApolloClient({ + link: mockSingleLink({ + request: { + query, + variables, + }, + result, + }, ...mockedResponses).setOnError(reject), + cache: new InMemoryCache(cacheConfig), }); - let latestResult: any; - observable.subscribe({ - next(result: any) { - latestResult = result; - }, + return client.watchQuery({ + query, + variables, }); + } - return observable.fetchMore({ - // Rely on the fact that the original variables had limit: 10 - variables: { start: 10 }, - updateQuery: (prev, options) => { - expect(options.variables).toEqual(variablesMore); + describe('triggers new result with async new variables', () => { + itAsync('updateQuery', (resolve, reject) => { + const observable = setup(reject, { + request: { + query, + variables: variablesMore, + }, + result: resultMore, + }); - const state = cloneDeep(prev) as any; - state.entry.comments = [ - ...state.entry.comments, - ...(options.fetchMoreResult as any).entry.comments, - ]; - return state; - }, - }).then(data => { - // This is the server result - expect(data.data.entry.comments).toHaveLength(10); - expect(data.loading).toBe(false); - const comments = latestResult.data.entry.comments; - expect(comments).toHaveLength(20); - for (let i = 1; i <= 20; i++) { - expect(comments[i - 1].text).toEqual(`comment ${i}`); - } - }).then(resolve, reject); - }); + let latestResult: any; + observable.subscribe({ + next(result: any) { + latestResult = result; + }, + }); + + return observable.fetchMore({ + // Rely on the fact that the original variables had limit: 10 + variables: { start: 10 }, + updateQuery: (prev, options) => { + expect(options.variables).toEqual(variablesMore); + + const state = cloneDeep(prev) as any; + state.entry.comments = [ + ...state.entry.comments, + ...(options.fetchMoreResult as any).entry.comments, + ]; + return state; + }, + }).then(data => { + // This is the server result + expect(data.data.entry.comments).toHaveLength(10); + expect(data.loading).toBe(false); + const comments = latestResult.data.entry.comments; + expect(comments).toHaveLength(20); + for (let i = 1; i <= 20; i++) { + expect(comments[i - 1].text).toEqual(`comment ${i}`); + } + }).then(resolve, reject); + }); - itAsync('basic fetchMore results merging', (resolve, reject) => { - const observable = setup(reject, { - request: { - query, - variables: variablesMore, - }, - result: resultMore, + itAsync('field policy', (resolve, reject) => { + const observable = setupWithCacheConfig(reject, { + typePolicies: { + Entry: { + fields: { + comments: concatPagination(), + }, + }, + }, + }, { + request: { query, variables: variablesMore }, + result: resultMore, + }); + + let latestResult: any; + observable.subscribe({ + next(result: any) { + latestResult = result; + }, + }); + + return observable.fetchMore({ + // Rely on the fact that the original variables had limit: 10 + variables: { start: 10 }, + }).then(data => { + // This is the server result + expect(data.data.entry.comments).toHaveLength(10); + expect(data.loading).toBe(false); + const comments = latestResult.data.entry.comments; + expect(comments).toHaveLength(20); + for (let i = 1; i <= 20; i++) { + expect(comments[i - 1].text).toEqual(`comment ${i}`); + } + }).then(resolve, reject); }); + }); - let latestResult: any; - observable.subscribe({ - next(result: any) { - latestResult = result; - }, + describe('basic fetchMore results merging', () => { + itAsync('updateQuery', (resolve, reject) => { + const observable = setup(reject, { + request: { + query, + variables: variablesMore, + }, + result: resultMore, + }); + + let latestResult: any; + observable.subscribe({ + next(result: any) { + latestResult = result; + }, + }); + + return observable.fetchMore({ + variables: { start: 10 }, // rely on the fact that the original variables had limit: 10 + updateQuery: (prev, options) => { + const state = cloneDeep(prev) as any; + state.entry.comments = [ + ...state.entry.comments, + ...(options.fetchMoreResult as any).entry.comments, + ]; + return state; + }, + }).then(data => { + expect(data.data.entry.comments).toHaveLength(10); // this is the server result + expect(data.loading).toBe(false); + const comments = latestResult.data.entry.comments; + expect(comments).toHaveLength(20); + for (let i = 1; i <= 20; i++) { + expect(comments[i - 1].text).toEqual(`comment ${i}`); + } + }).then(resolve, reject); }); - return observable.fetchMore({ - variables: { start: 10 }, // rely on the fact that the original variables had limit: 10 - updateQuery: (prev, options) => { - const state = cloneDeep(prev) as any; - state.entry.comments = [ - ...state.entry.comments, - ...(options.fetchMoreResult as any).entry.comments, - ]; - return state; - }, - }).then(data => { - expect(data.data.entry.comments).toHaveLength(10); // this is the server result - expect(data.loading).toBe(false); - const comments = latestResult.data.entry.comments; - expect(comments).toHaveLength(20); - for (let i = 1; i <= 20; i++) { - expect(comments[i - 1].text).toEqual(`comment ${i}`); - } - }).then(resolve, reject); + itAsync('field policy', (resolve, reject) => { + const observable = setupWithCacheConfig(reject, { + typePolicies: { + Entry: { + fields: { + comments: concatPagination(), + }, + }, + }, + }, { + request: { + query, + variables: variablesMore, + }, + result: resultMore, + }); + + let latestResult: any; + observable.subscribe({ + next(result: any) { + latestResult = result; + }, + }); + + return observable.fetchMore({ + variables: { start: 10 }, // rely on the fact that the original variables had limit: 10 + }).then(data => { + expect(data.data.entry.comments).toHaveLength(10); // this is the server result + expect(data.loading).toBe(false); + const comments = latestResult.data.entry.comments; + expect(comments).toHaveLength(20); + for (let i = 1; i <= 20; i++) { + expect(comments[i - 1].text).toEqual(`comment ${i}`); + } + }).then(resolve, reject); + }); }); itAsync('fetchMore passes new args to field merge function', (resolve, reject) => { @@ -513,57 +612,128 @@ describe('fetchMore on an observable query', () => { }).then(resolve, reject); }); - itAsync('will not get an error from `fetchMore` if thrown', (resolve, reject) => { - const fetchMoreError = new Error('Uh, oh!'); - const link = mockSingleLink({ - request: { query, variables }, - result, - delay: 5, - }, { - request: { query, variables: variablesMore }, - error: fetchMoreError, - delay: 5, - }).setOnError(reject); + describe('will not get an error from `fetchMore` if thrown', () => { + itAsync('updateQuery', (resolve, reject) => { + const fetchMoreError = new Error('Uh, oh!'); + const link = mockSingleLink({ + request: { query, variables }, + result, + delay: 5, + }, { + request: { query, variables: variablesMore }, + error: fetchMoreError, + delay: 5, + }).setOnError(reject); - const client = new ApolloClient({ - link, - cache: new InMemoryCache(), - }); + const client = new ApolloClient({ + link, + cache: new InMemoryCache(), + }); - const observable = client.watchQuery({ - query, - variables, - notifyOnNetworkStatusChange: true, + const observable = client.watchQuery({ + query, + variables, + notifyOnNetworkStatusChange: true, + }); + + let count = 0; + observable.subscribe({ + next: ({ data, networkStatus }) => { + switch (++count) { + case 1: + expect(networkStatus).toBe(NetworkStatus.ready); + expect((data as any).entry.comments.length).toBe(10); + observable.fetchMore({ + variables: { start: 10 }, + updateQuery: prev => { + reject(new Error("should not have called updateQuery")); + return prev; + }, + }).catch(e => { + expect(e.networkError).toBe(fetchMoreError); + resolve(); + }); + break; + } + }, + error: () => { + reject(new Error('`error` called when it wasn’t supposed to be.')); + }, + complete: () => { + reject( + new Error('`complete` called when it wasn’t supposed to be.'), + ); + }, + }); }); - let count = 0; - observable.subscribe({ - next: ({ data, networkStatus }) => { - switch (++count) { - case 1: - expect(networkStatus).toBe(NetworkStatus.ready); - expect((data as any).entry.comments.length).toBe(10); - observable.fetchMore({ - variables: { start: 10 }, - updateQuery: prev => { - reject(new Error("should not have called updateQuery")); - return prev; + itAsync('field policy', (resolve, reject) => { + const fetchMoreError = new Error('Uh, oh!'); + const link = mockSingleLink({ + request: { query, variables }, + result, + delay: 5, + }, { + request: { query, variables: variablesMore }, + error: fetchMoreError, + delay: 5, + }).setOnError(reject); + + let calledFetchMore = false; + + const client = new ApolloClient({ + link, + cache: new InMemoryCache({ + typePolicies: { + Entry: { + fields: { + comments: { + keyArgs: false, + merge(_, incoming) { + if (calledFetchMore) { + reject(new Error("should not have called merge")); + } + return incoming; + }, + }, + }, }, - }).catch(e => { - expect(e.networkError).toBe(fetchMoreError); - resolve(); - }); - break; - } - }, - error: () => { - reject(new Error('`error` called when it wasn’t supposed to be.')); - }, - complete: () => { - reject( - new Error('`complete` called when it wasn’t supposed to be.'), - ); - }, + }, + }), + }); + + const observable = client.watchQuery({ + query, + variables, + notifyOnNetworkStatusChange: true, + }); + + let count = 0; + observable.subscribe({ + next: ({ data, networkStatus }) => { + switch (++count) { + case 1: + expect(networkStatus).toBe(NetworkStatus.ready); + expect((data as any).entry.comments.length).toBe(10); + calledFetchMore = true; + observable.fetchMore({ + variables: { start: 10 }, + }).catch(e => { + expect(e.networkError).toBe(fetchMoreError); + resolve(); + }); + break; + } + }, + error: () => { + reject(new Error('`error` called when it wasn’t supposed to be.')); + }, + complete: () => { + reject( + new Error('`complete` called when it wasn’t supposed to be.'), + ); + }, + }); }); }); @@ -679,95 +849,213 @@ describe('fetchMore on an observable query with connection', () => { }); } - itAsync('fetchMore with connection results merging', (resolve, reject) => { - const observable = setup(reject, { - request: { - query: transformedQuery, - variables: variablesMore, - }, - result: resultMore, - }) + function setupWithCacheConfig( + reject: (reason: any) => any, + cacheConfig: InMemoryCacheConfig, + ...mockedResponses: any[] + ) { + const client = new ApolloClient({ + link: mockSingleLink({ + request: { + query: transformedQuery, + variables, + }, + result, + }, ...mockedResponses).setOnError(reject), + cache: new InMemoryCache(cacheConfig), + }); - let latestResult: any; - observable.subscribe({ - next(result: any) { - latestResult = result; - }, + return client.watchQuery({ + query, + variables, }); + } - return observable.fetchMore({ - variables: { start: 10 }, // rely on the fact that the original variables had limit: 10 - updateQuery: (prev, options) => { - const state = cloneDeep(prev) as any; - state.entry.comments = [ - ...state.entry.comments, - ...(options.fetchMoreResult as any).entry.comments, - ]; - return state; - }, - }).then(data => { - expect(data.data.entry.comments).toHaveLength(10); // this is the server result - expect(data.loading).toBe(false); - const comments = latestResult.data.entry.comments; - expect(comments).toHaveLength(20); - for (let i = 1; i <= 20; i++) { - expect(comments[i - 1].text).toBe(`comment ${i}`); - } - }).then(resolve, reject); - }); + describe('fetchMore with connection results merging', () => { + itAsync('updateQuery', (resolve, reject) => { + const observable = setup(reject, { + request: { + query: transformedQuery, + variables: variablesMore, + }, + result: resultMore, + }) - itAsync('will set the network status to `fetchMore`', (resolve, reject) => { - const link = mockSingleLink({ - request: { query: transformedQuery, variables }, - result, - delay: 5, - }, { - request: { query: transformedQuery, variables: variablesMore }, - result: resultMore, - delay: 5, - }).setOnError(reject); + let latestResult: any; + observable.subscribe({ + next(result: any) { + latestResult = result; + }, + }); + + return observable.fetchMore({ + variables: { start: 10 }, // rely on the fact that the original variables had limit: 10 + updateQuery: (prev, options) => { + const state = cloneDeep(prev) as any; + state.entry.comments = [ + ...state.entry.comments, + ...(options.fetchMoreResult as any).entry.comments, + ]; + return state; + }, + }).then(data => { + expect(data.data.entry.comments).toHaveLength(10); // this is the server result + expect(data.loading).toBe(false); + const comments = latestResult.data.entry.comments; + expect(comments).toHaveLength(20); + for (let i = 1; i <= 20; i++) { + expect(comments[i - 1].text).toBe(`comment ${i}`); + } + }).then(resolve, reject); + }); - const client = new ApolloClient({ - link, - cache: new InMemoryCache(), + itAsync('field policy', (resolve, reject) => { + const observable = setupWithCacheConfig(reject, { + typePolicies: { + Entry: { + fields: { + comments: concatPagination(), + }, + }, + }, + }, { + request: { + query: transformedQuery, + variables: variablesMore, + }, + result: resultMore, + }) + + let latestResult: any; + observable.subscribe({ + next(result: any) { + latestResult = result; + }, + }); + + return observable.fetchMore({ + variables: { start: 10 }, // rely on the fact that the original variables had limit: 10 + }).then(data => { + expect(data.data.entry.comments).toHaveLength(10); // this is the server result + expect(data.loading).toBe(false); + const comments = latestResult.data.entry.comments; + expect(comments).toHaveLength(20); + for (let i = 1; i <= 20; i++) { + expect(comments[i - 1].text).toBe(`comment ${i}`); + } + }).then(resolve, reject); }); + }); - const observable = client.watchQuery({ - query, - variables, - notifyOnNetworkStatusChange: true, + describe('will set the network status to `fetchMore`', () => { + itAsync('updateQuery', (resolve, reject) => { + const link = mockSingleLink({ + request: { query: transformedQuery, variables }, + result, + delay: 5, + }, { + request: { query: transformedQuery, variables: variablesMore }, + result: resultMore, + delay: 5, + }).setOnError(reject); + + const client = new ApolloClient({ + link, + cache: new InMemoryCache(), + }); + + const observable = client.watchQuery({ + query, + variables, + notifyOnNetworkStatusChange: true, + }); + + let count = 0; + observable.subscribe({ + next: ({ data, networkStatus }) => { + switch (count++) { + case 0: + expect(networkStatus).toBe(NetworkStatus.ready); + expect((data as any).entry.comments.length).toBe(10); + observable.fetchMore({ + variables: { start: 10 }, + updateQuery: (prev: any, options: any) => { + const state = cloneDeep(prev) as any; + state.entry.comments = [ + ...state.entry.comments, + ...(options.fetchMoreResult as any).entry.comments, + ]; + return state; + }, + }); + break; + case 1: + expect(networkStatus).toBe(NetworkStatus.ready); + expect((data as any).entry.comments.length).toBe(20); + resolve(); + break; + default: + reject(new Error('`next` called too many times')); + } + }, + error: (error: any) => reject(error), + complete: () => reject(new Error('Should not have completed')), + }); }); - let count = 0; - observable.subscribe({ - next: ({ data, networkStatus }) => { - switch (count++) { - case 0: - expect(networkStatus).toBe(NetworkStatus.ready); - expect((data as any).entry.comments.length).toBe(10); - observable.fetchMore({ - variables: { start: 10 }, - updateQuery: (prev: any, options: any) => { - const state = cloneDeep(prev) as any; - state.entry.comments = [ - ...state.entry.comments, - ...(options.fetchMoreResult as any).entry.comments, - ]; - return state; + itAsync('field policy', (resolve, reject) => { + const link = mockSingleLink({ + request: { query: transformedQuery, variables }, + result, + delay: 5, + }, { + request: { query: transformedQuery, variables: variablesMore }, + result: resultMore, + delay: 5, + }).setOnError(reject); + + const client = new ApolloClient({ + link, + cache: new InMemoryCache({ + typePolicies: { + Entry: { + fields: { + comments: concatPagination(), }, - }); - break; - case 1: - expect(networkStatus).toBe(NetworkStatus.ready); - expect((data as any).entry.comments.length).toBe(20); - resolve(); - break; - default: - reject(new Error('`next` called too many times')); - } - }, - error: (error: any) => reject(error), - complete: () => reject(new Error('Should not have completed')), + }, + }, + }), + }); + + const observable = client.watchQuery({ + query, + variables, + notifyOnNetworkStatusChange: true, + }); + + let count = 0; + observable.subscribe({ + next: ({ data, networkStatus }) => { + switch (count++) { + case 0: + expect(networkStatus).toBe(NetworkStatus.ready); + expect((data as any).entry.comments.length).toBe(10); + observable.fetchMore({ + variables: { start: 10 }, + }); + break; + case 1: + expect(networkStatus).toBe(NetworkStatus.ready); + expect((data as any).entry.comments.length).toBe(20); + resolve(); + break; + default: + reject(new Error('`next` called too many times')); + } + }, + error: (error: any) => reject(error), + complete: () => reject(new Error('Should not have completed')), + }); }); }); }); diff --git a/src/react/hooks/__tests__/useQuery.test.tsx b/src/react/hooks/__tests__/useQuery.test.tsx index 58c78775405..cb1cfa081f4 100644 --- a/src/react/hooks/__tests__/useQuery.test.tsx +++ b/src/react/hooks/__tests__/useQuery.test.tsx @@ -16,6 +16,7 @@ import { useMutation } from '../useMutation'; import { QueryFunctionOptions } from '../..'; import { NetworkStatus } from '../../../core/networkStatus'; import { Reference } from '../../../utilities/graphql/storeUtils'; +import { concatPagination } from '../../../utilities'; describe('useQuery Hook', () => { const CAR_QUERY: DocumentNode = gql` @@ -1117,57 +1118,55 @@ describe('useQuery Hook', () => { }); describe('Pagination', () => { - itAsync( - 'should render `fetchMore.updateQuery` updated results with proper ' + - 'loading status, when `notifyOnNetworkStatusChange` is true', - (resolve, reject) => { - const carQuery: DocumentNode = gql` - query cars($limit: Int) { - cars(limit: $limit) { - id - make - model - vin - __typename - } + describe('should render fetchMore-updated results with proper loading status, when `notifyOnNetworkStatusChange` is true', () => { + const carQuery: DocumentNode = gql` + query cars($limit: Int) { + cars(limit: $limit) { + id + make + model + vin + __typename } - `; - - const carResults = { - cars: [ - { - id: 1, - make: 'Audi', - model: 'RS8', - vin: 'DOLLADOLLABILL', - __typename: 'Car' - } - ] - }; - - const moreCarResults = { - cars: [ - { - id: 2, - make: 'Audi', - model: 'eTron', - vin: 'TREESRGOOD', - __typename: 'Car' - } - ] - }; + } + `; - const mocks = [ + const carResults = { + cars: [ { - request: { query: carQuery, variables: { limit: 1 } }, - result: { data: carResults } - }, + id: 1, + make: 'Audi', + model: 'RS8', + vin: 'DOLLADOLLABILL', + __typename: 'Car' + } + ] + }; + + const moreCarResults = { + cars: [ { - request: { query: carQuery, variables: { limit: 1 } }, - result: { data: moreCarResults } + id: 2, + make: 'Audi', + model: 'eTron', + vin: 'TREESRGOOD', + __typename: 'Car' } - ]; + ] + }; + + const mocks = [ + { + request: { query: carQuery, variables: { limit: 1 } }, + result: { data: carResults } + }, + { + request: { query: carQuery, variables: { limit: 1 } }, + result: { data: moreCarResults } + } + ]; + itAsync('updateQuery', (resolve, reject) => { let renderCount = 0; function App() { const { loading, data, fetchMore } = useQuery(carQuery, { @@ -1218,60 +1217,115 @@ describe('useQuery Hook', () => { return wait(() => { expect(renderCount).toBe(3); }).then(resolve, reject); - } - ); + }); - itAsync( - 'should render `fetchMore.updateQuery` updated results with no ' + - 'loading status, when `notifyOnNetworkStatusChange` is false', - (resolve, reject) => { - const carQuery: DocumentNode = gql` - query cars($limit: Int) { - cars(limit: $limit) { - id - make - model - vin - __typename - } + itAsync('field policy', (resolve, reject) => { + let renderCount = 0; + function App() { + const { loading, data, fetchMore } = useQuery(carQuery, { + variables: { limit: 1 }, + notifyOnNetworkStatusChange: true + }); + + switch (++renderCount) { + case 1: + expect(loading).toBeTruthy(); + break; + case 2: + expect(loading).toBeFalsy(); + expect(data).toEqual(carResults); + fetchMore({ + variables: { + limit: 1 + }, + }); + break; + case 3: + expect(loading).toBeFalsy(); + expect(data).toEqual({ + cars: [ + carResults.cars[0], + moreCarResults.cars[0], + ], + }); + break; + default: } - `; - const carResults = { - cars: [ - { - id: 1, - make: 'Audi', - model: 'RS8', - vin: 'DOLLADOLLABILL', - __typename: 'Car' - } - ] - }; + return null; + } - const moreCarResults = { - cars: [ - { - id: 2, - make: 'Audi', - model: 'eTron', - vin: 'TREESRGOOD', - __typename: 'Car' - } - ] - }; + const cache = new InMemoryCache({ + typePolicies: { + Query: { + fields: { + cars: concatPagination(), + }, + }, + }, + }); - const mocks = [ + render( + + + + ); + + return wait(() => { + expect(renderCount).toBe(3); + }).then(resolve, reject); + }); + }); + + describe('should render fetchMore-updated results with no loading status, when `notifyOnNetworkStatusChange` is false', () => { + const carQuery: DocumentNode = gql` + query cars($limit: Int) { + cars(limit: $limit) { + id + make + model + vin + __typename + } + } + `; + + const carResults = { + cars: [ { - request: { query: carQuery, variables: { limit: 1 } }, - result: { data: carResults } - }, + id: 1, + make: 'Audi', + model: 'RS8', + vin: 'DOLLADOLLABILL', + __typename: 'Car' + } + ] + }; + + const moreCarResults = { + cars: [ { - request: { query: carQuery, variables: { limit: 1 } }, - result: { data: moreCarResults } + id: 2, + make: 'Audi', + model: 'eTron', + vin: 'TREESRGOOD', + __typename: 'Car' } - ]; + ] + }; + + const mocks = [ + { + request: { query: carQuery, variables: { limit: 1 } }, + result: { data: carResults } + }, + { + request: { query: carQuery, variables: { limit: 1 } }, + result: { data: moreCarResults } + } + ]; + itAsync('updateQuery', (resolve, reject) => { let renderCount = 0; function App() { const { loading, data, fetchMore } = useQuery(carQuery, { @@ -1317,8 +1371,63 @@ describe('useQuery Hook', () => { return wait(() => { expect(renderCount).toBe(3); }).then(resolve, reject); - } - ); + }); + + itAsync('field policy', (resolve, reject) => { + let renderCount = 0; + function App() { + const { loading, data, fetchMore } = useQuery(carQuery, { + variables: { limit: 1 }, + notifyOnNetworkStatusChange: false + }); + + switch (renderCount) { + case 0: + expect(loading).toBeTruthy(); + break; + case 1: + expect(loading).toBeFalsy(); + expect(data).toEqual(carResults); + fetchMore({ + variables: { + limit: 1 + }, + }); + break; + case 2: + expect(loading).toBeFalsy(); + expect(data).toEqual({ + cars: [carResults.cars[0], moreCarResults.cars[0]] + }); + break; + default: + } + + renderCount += 1; + return null; + } + + const cache = new InMemoryCache({ + typePolicies: { + Query: { + fields: { + cars: concatPagination(), + }, + }, + }, + }); + + render( + + + + ); + + return wait(() => { + expect(renderCount).toBe(3); + }).then(resolve, reject); + }); + }); }); describe('Refetching', () => {