Compare commits

...

2 Commits

Author SHA1 Message Date
Bartłomiej Głownia
b80e5aca22 chore: improve generic data provider unit test 2023-02-08 15:41:45 +01:00
Bartłomiej Głownia
dada8521e2 chore: add generic data provider unit test 2023-02-08 15:41:45 +01:00
2 changed files with 78 additions and 29 deletions

View File

@ -19,7 +19,10 @@ import type {
OperationVariables,
ApolloQueryResult,
QueryOptions,
ApolloError,
} from '@apollo/client';
import { GraphQLError } from 'graphql';
import type { Subscription, Observable } from 'zen-observable-ts';
type Item = {
@ -58,12 +61,16 @@ const query: Query<QueryData> = {
};
const subscriptionQuery: Query<SubscriptionData> = query;
const getData = jest.fn((r: QueryData | null) => r?.data || null);
const getDelta = jest.fn((r: SubscriptionData) => r.data);
const subscribe = makeDataProvider<QueryData, Data, SubscriptionData, Delta>({
query,
subscriptionQuery,
update,
getData: (r) => r?.data || null,
getDelta: (r) => r.data,
getData,
getDelta,
});
const combineData = jest.fn<
@ -91,8 +98,8 @@ const paginatedSubscribe = makeDataProvider<
query,
subscriptionQuery,
update,
getData: (r) => r?.data || null,
getDelta: (r) => r.data,
getData,
getDelta,
pagination: {
first,
append: defaultAppend,
@ -186,9 +193,20 @@ const clearPendingQueries = () => {
};
describe('data provider', () => {
beforeEach(() => {
clearPendingQueries();
callback.mockClear();
getData.mockClear();
clientQuery.mockClear();
clientSubscribeUnsubscribe.mockClear();
clientSubscribeSubscribe.mockClear();
});
it('memoize instance and unsubscribe if no subscribers', () => {
const subscription1 = subscribe(jest.fn(), client);
const subscription2 = subscribe(jest.fn(), client);
const variables = { var: 'val' };
const subscription1 = subscribe(jest.fn(), client, variables);
const subscription2 = subscribe(jest.fn(), client, { ...variables });
// const subscription1 = subscribe(jest.fn(), client);
// const subscription2 = subscribe(jest.fn(), client);
expect(clientSubscribeSubscribe.mock.calls.length).toEqual(1);
subscription1.unsubscribe();
expect(clientSubscribeUnsubscribe.mock.calls.length).toEqual(0);
@ -197,7 +215,6 @@ describe('data provider', () => {
});
it('calls callback before and after initial fetch', async () => {
callback.mockClear();
const data: Item[] = [];
const subscription = subscribe(callback, client);
expect(callback.mock.calls.length).toBe(1);
@ -210,6 +227,49 @@ describe('data provider', () => {
subscription.unsubscribe();
});
it('calls callback on error', async () => {
const subscription = subscribe(callback, client);
expect(callback.mock.calls.length).toBe(1);
expect(callback.mock.calls[0][0].data).toBe(null);
expect(callback.mock.calls[0][0].loading).toBe(true);
const error = new Error('Rejected by unit test');
await rejectQuery(error);
expect(getData).not.toBeCalled();
expect(callback.mock.calls.length).toBe(2);
expect(callback.mock.calls[1][0].error).toEqual(error);
expect(callback.mock.calls[1][0].loading).toBe(false);
subscription.unsubscribe();
});
it('calls successful callback on NotFound error on party path', async () => {
const subscription = subscribe(callback, client);
expect(callback.mock.calls.length).toBe(1);
expect(callback.mock.calls[0][0].data).toBe(null);
expect(callback.mock.calls[0][0].loading).toBe(true);
const error = new Error() as ApolloError;
const graphQLError = new GraphQLError(
'',
undefined,
undefined,
undefined,
['party'],
undefined,
{
type: 'NotFound',
}
);
error.graphQLErrors = [graphQLError];
const data: Data = [];
getData.mockReturnValueOnce(data);
await rejectQuery(error);
expect(getData).toHaveBeenCalledWith(null, undefined);
expect(callback.mock.calls.length).toBe(2);
expect(callback.mock.calls[1][0].data).toEqual(data);
expect(callback.mock.calls[1][0].error).toEqual(undefined);
expect(callback.mock.calls[1][0].loading).toBe(false);
subscription.unsubscribe();
});
it('calls update and callback on each update', async () => {
const data: Item[] = [];
const subscription = subscribe(callback, client);
@ -228,8 +288,7 @@ describe('data provider', () => {
subscription.unsubscribe();
});
it("don't calls callback on update if data doesn't", async () => {
callback.mockClear();
it("don't calls callback on update if data doesn't change", async () => {
const data: Item[] = [];
const subscription = subscribe(callback, client);
await resolveQuery({ data });
@ -247,10 +306,6 @@ describe('data provider', () => {
});
it('refetch data on reload', async () => {
clearPendingQueries();
clientQuery.mockClear();
clientSubscribeUnsubscribe.mockClear();
clientSubscribeSubscribe.mockClear();
const data: Item[] = [];
const subscription = subscribe(callback, client);
await resolveQuery({ data });
@ -263,9 +318,6 @@ describe('data provider', () => {
});
it('refetch data and restart subscription on reload with force', async () => {
clientQuery.mockClear();
clientSubscribeUnsubscribe.mockClear();
clientSubscribeSubscribe.mockClear();
const data: Item[] = [];
const subscription = subscribe(callback, client);
await resolveQuery({ data });
@ -278,7 +330,6 @@ describe('data provider', () => {
});
it('calls callback on flush', async () => {
callback.mockClear();
const data: Item[] = [];
const subscription = subscribe(callback, client);
await resolveQuery({ data });
@ -289,8 +340,6 @@ describe('data provider', () => {
});
it('fills data with nulls if pagination is enabled', async () => {
callback.mockClear();
clearPendingQueries();
const totalCount = 1000;
const data: Item[] = new Array(first).fill(null).map((v, i) => ({
cursor: i.toString(),
@ -311,7 +360,6 @@ describe('data provider', () => {
});
it('loads requested data blocks and inserts data with total count', async () => {
callback.mockClear();
const totalCount = 1000;
const subscription = paginatedSubscribe(callback, client);
await resolveQuery({
@ -436,7 +484,6 @@ describe('data provider', () => {
});
it('loads requested data blocks and inserts data without totalCount', async () => {
callback.mockClear();
const totalCount = undefined;
const subscription = paginatedSubscribe(callback, client);
await resolveQuery({

View File

@ -407,6 +407,15 @@ function makeDataProviderInternal<
}
};
const onError = (e: Error) => {
error = e;
if (subscription) {
subscription.unsubscribe();
subscription = undefined;
}
notifyAll();
};
const initialize = async () => {
if (subscription) {
if (resetTimer) {
@ -427,14 +436,7 @@ function makeDataProviderInternal<
variables,
fetchPolicy,
})
.subscribe(onNext, (e) => {
error = e as Error;
if (subscription) {
subscription.unsubscribe();
subscription = undefined;
}
notifyAll();
});
.subscribe(onNext, onError);
}
await initialFetch();
};