Skip to content

Commit

Permalink
chore: cover reactivity with test cases
Browse files Browse the repository at this point in the history
  • Loading branch information
yurks committed Jun 19, 2024
1 parent 1a3ebd3 commit 02be203
Showing 1 changed file with 269 additions and 34 deletions.
303 changes: 269 additions & 34 deletions packages/vue-urql/src/useQuery.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import { OperationResult, OperationResultSource } from '@urql/core';
import { nextTick, reactive, ref } from 'vue';
import {
OperationResult,
OperationResultSource,
RequestPolicy,
} from '@urql/core';
import { computed, nextTick, reactive, ref } from 'vue';
import { vi, expect, it, describe } from 'vitest';

vi.mock('./useClient.ts', async () => ({
Expand All @@ -10,10 +14,28 @@ vi.mock('./useClient.ts', async () => ({

import { pipe, makeSubject, fromValue, delay } from 'wonka';
import { createClient } from '@urql/core';
import { useQuery } from './useQuery';
import { useQuery, UseQueryArgs } from './useQuery';

const client = createClient({ url: '/graphql', exchanges: [] });

const createQuery = (args: UseQueryArgs) => {
const executeQuery = vi
.spyOn(client, 'executeQuery')
.mockImplementation(request => {
return pipe(
fromValue({ operation: request, data: { test: true } }),
delay(1)
) as any;
});

const query$ = useQuery(args);

return {
query$,
executeQuery,
};
};

describe('useQuery', () => {
it('runs a query and updates data', async () => {
const subject = makeSubject<any>();
Expand Down Expand Up @@ -79,18 +101,9 @@ describe('useQuery', () => {
});

it('runs queries as a promise-like that resolves even when the query changes', async () => {
const executeQuery = vi
.spyOn(client, 'executeQuery')
.mockImplementation(request => {
return pipe(
fromValue({ operation: request, data: { test: true } }),
delay(1)
) as any;
});

const doc = ref('{ test }');

const query$ = useQuery({
const { executeQuery, query$ } = createQuery({
query: doc,
});

Expand All @@ -108,36 +121,194 @@ describe('useQuery', () => {
);
});

it('reacts to variables changing', async () => {
const executeQuery = vi
.spyOn(client, 'executeQuery')
.mockImplementation(request => {
return pipe(
fromValue({ operation: request, data: { test: true } }),
delay(1)
) as any;
});
it('reacts to ref variables changing', async () => {
const variables = ref({ prop: 1 });

const { executeQuery, query$ } = createQuery({
query: ref('{ test }'),
variables,
});

await query$;
expect(executeQuery).toHaveBeenCalledTimes(1);
expect(query$.operation.value).toHaveProperty('variables.prop', 1);

variables.value.prop++;
await query$;
expect(executeQuery).toHaveBeenCalledTimes(2);
expect(query$.operation.value).toHaveProperty('variables.prop', 2);

variables.value = { prop: 3 };
await query$;
expect(executeQuery).toHaveBeenCalledTimes(3);
expect(query$.operation.value).toHaveProperty('variables.prop', 3);
});

it('reacts to nested ref variables changing', async () => {
const prop = ref(1);

const { executeQuery, query$ } = createQuery({
query: ref('{ test }'),
variables: { prop },
});

await query$;
expect(executeQuery).toHaveBeenCalledTimes(1);
expect(query$.operation.value).toHaveProperty('variables.prop', 1);

prop.value++;
await query$;
expect(executeQuery).toHaveBeenCalledTimes(2);
expect(query$.operation.value).toHaveProperty('variables.prop', 2);
});

it('reacts to deep nested ref variables changing', async () => {
const prop = ref(1);

const { executeQuery, query$ } = createQuery({
query: ref('{ test }'),
variables: { deep: { nested: { prop } } },
});

await query$;
expect(executeQuery).toHaveBeenCalledTimes(1);
expect(query$.operation.value).toHaveProperty(
'variables.deep.nested.prop',
1
);

prop.value++;
await query$;
expect(executeQuery).toHaveBeenCalledTimes(2);
expect(query$.operation.value).toHaveProperty(
'variables.deep.nested.prop',
2
);
});

it('reacts to reactive variables changing', async () => {
const prop = ref(1);
const variables = reactive({ prop: 1, deep: { nested: { prop } } });

const { executeQuery, query$ } = createQuery({
query: ref('{ test }'),
variables,
});

await query$;
expect(executeQuery).toHaveBeenCalledTimes(1);
expect(query$.operation.value).toHaveProperty('variables.prop', 1);

variables.prop++;
await query$;
expect(executeQuery).toHaveBeenCalledTimes(2);
expect(query$.operation.value).toHaveProperty('variables.prop', 2);

prop.value++;
await query$;
expect(executeQuery).toHaveBeenCalledTimes(3);
expect(query$.operation.value).toHaveProperty(
'variables.deep.nested.prop',
2
);
});

it('reacts to computed variables changing', async () => {
const prop = ref(1);
const prop2 = ref(1);
const variables = computed(() => ({
prop: prop.value,
deep: { nested: { prop2 } },
}));

const { executeQuery, query$ } = createQuery({
query: ref('{ test }'),
variables,
});

await query$;
expect(executeQuery).toHaveBeenCalledTimes(1);
expect(query$.operation.value).toHaveProperty('variables.prop', 1);

prop.value++;
await query$;
expect(executeQuery).toHaveBeenCalledTimes(2);
expect(query$.operation.value).toHaveProperty('variables.prop', 2);

prop2.value++;
await query$;
expect(executeQuery).toHaveBeenCalledTimes(3);
expect(query$.operation.value).toHaveProperty(
'variables.deep.nested.prop2',
2
);
});

const variables = {
test: ref(1),
};
const query$ = useQuery({
query: '{ test }',
it('reacts to callback variables changing', async () => {
const prop = ref(1);
const prop2 = ref(1);
const variables = () => ({
prop: prop.value,
deep: { nested: { prop2 } },
});

const { executeQuery, query$ } = createQuery({
query: ref('{ test }'),
variables,
});

await query$;
expect(executeQuery).toHaveBeenCalledTimes(1);
expect(query$.operation.value).toHaveProperty('variables.prop', 1);

prop.value++;
await query$;
expect(executeQuery).toHaveBeenCalledTimes(2);
expect(query$.operation.value).toHaveProperty('variables.prop', 2);

prop2.value++;
await query$;
expect(executeQuery).toHaveBeenCalledTimes(3);
expect(query$.operation.value).toHaveProperty(
'variables.deep.nested.prop2',
2
);
});

it('reacts to reactive context argument', async () => {
const context = ref<{ requestPolicy: RequestPolicy }>({
requestPolicy: 'cache-only',
});

const { executeQuery, query$ } = createQuery({
query: ref('{ test }'),
context,
});

await query$;
expect(executeQuery).toHaveBeenCalledTimes(1);

expect(query$.operation.value).toHaveProperty('variables.test', 1);
context.value.requestPolicy = 'network-only';
await query$;
expect(executeQuery).toHaveBeenCalledTimes(2);
});

it('reacts to callback context argument', async () => {
const requestPolicy = ref<RequestPolicy>('cache-only');

variables.test.value = 2;
const { executeQuery, query$ } = createQuery({
query: ref('{ test }'),
context: () => ({
requestPolicy: requestPolicy.value,
}),
});

await query$;
expect(executeQuery).toHaveBeenCalledTimes(1);

requestPolicy.value = 'network-only';
await query$;
expect(executeQuery).toHaveBeenCalledTimes(2);
expect(query$.operation.value).toHaveProperty('variables.test', 2);
});

it('pauses query when asked to do so', async () => {
Expand All @@ -148,17 +319,15 @@ describe('useQuery', () => {
() => subject.source as OperationResultSource<OperationResult>
);

const pause = ref(true);

const _query = useQuery({
query: `{ test }`,
pause: () => pause.value,
pause: true,
});
const query = reactive(_query);

expect(executeQuery).not.toHaveBeenCalled();

pause.value = false;
query.resume();
await nextTick();
expect(query.fetching).toBe(true);

Expand All @@ -167,4 +336,70 @@ describe('useQuery', () => {
expect(query.fetching).toBe(false);
expect(query.data).toEqual({ test: true });
});

it('pauses query with ref variable', async () => {
const pause = ref(true);

const { executeQuery, query$ } = createQuery({
query: ref('{ test }'),
pause,
});

await query$;
expect(executeQuery).not.toHaveBeenCalled();

pause.value = false;
await query$;
expect(executeQuery).toHaveBeenCalledTimes(1);

query$.pause();
query$.resume();
await query$;
expect(executeQuery).toHaveBeenCalledTimes(2);
});

it('pauses query with computed variable', async () => {
const pause = ref(true);

const { executeQuery, query$ } = createQuery({
query: ref('{ test }'),
pause: computed(() => pause.value),
});

await query$;
expect(executeQuery).not.toHaveBeenCalled();

pause.value = false;
await query$;
expect(executeQuery).toHaveBeenCalledTimes(1);

query$.pause();
query$.resume();
await query$;
// this shouldn't be called, as pause/resume functionality should works in sync with passed `pause` variable, e.g.:
// if we pass readonly computed variable, then we want to make sure that its value fully controls the state of the request.
expect(executeQuery).toHaveBeenCalledTimes(1);
});

it('pauses query with callback', async () => {
const pause = ref(true);

const { executeQuery, query$ } = createQuery({
query: ref('{ test }'),
pause: () => pause.value,
});

await query$;
expect(executeQuery).not.toHaveBeenCalled();

pause.value = false;
await query$;
expect(executeQuery).toHaveBeenCalledTimes(1);

query$.pause();
query$.resume();
await query$;
// the same as computed variable example - user has full control over the request state if using callback
expect(executeQuery).toHaveBeenCalledTimes(1);
});
});

0 comments on commit 02be203

Please sign in to comment.