Skip to content

Commit 31ddf71

Browse files
authored
Merge pull request #121 from simonsobs/dev
Update tests, clean code
2 parents ea4ba13 + 5523131 commit 31ddf71

14 files changed

+220
-207
lines changed

src/api/__tests__/fc.ts

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import { expect, it } from "vitest";
2+
import fc from "fast-check";
3+
4+
export const fcUndefinedOr = <T>(arb: fc.Arbitrary<T>) =>
5+
fc.oneof(fc.constant(undefined), arb);
6+
7+
const fcError = fc.string().map((msg) => new Error(msg));
8+
9+
export const fcMockUseQueryResponseArg = <T>(arbData: fc.Arbitrary<T>) =>
10+
fc.record({
11+
data: fcUndefinedOr(arbData),
12+
error: fcUndefinedOr(fcError),
13+
});
14+
15+
export const fcMockUseSubscriptionResponseArg = <T>(arbData: fc.Arbitrary<T>) =>
16+
fc.array(fcMockUseQueryResponseArg(arbData));
17+
18+
if (import.meta.vitest) {
19+
it("fcUndefinedOr()", () => {
20+
fc.assert(
21+
fc.property(fcUndefinedOr(fc.integer()), (v) => {
22+
expect(v === undefined || typeof v === "number").toBe(true);
23+
}),
24+
);
25+
});
26+
27+
it("fcMockUseQueryResponse()", () => {
28+
const fcData = fc.record({ a: fc.integer() });
29+
const fcArg = fcMockUseQueryResponseArg(fcData);
30+
fc.assert(
31+
fc.property(fcArg, (v) => {
32+
expect(v.data === undefined || typeof v.data.a === "number").toBe(true);
33+
expect(v.error === undefined || v.error instanceof Error).toBe(true);
34+
}),
35+
);
36+
});
37+
38+
it("fcMockUseSubscriptionResponse()", () => {
39+
const fcData = fc.record({ a: fc.integer() });
40+
const fcArg = fcMockUseSubscriptionResponseArg(fcData);
41+
fc.assert(
42+
fc.property(fcArg, (v) => {
43+
expect(
44+
v.every(
45+
(e) =>
46+
(e.data === undefined || typeof e.data.a === "number") &&
47+
(e.error === undefined || e.error instanceof Error),
48+
),
49+
).toBe(true);
50+
}),
51+
);
52+
});
53+
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import { vi, expect } from "vitest";
2+
import type { ComputedRef } from "vue";
3+
import { useQuery, useSubscription } from "@urql/vue";
4+
import type { UseQueryResponse, UseSubscriptionResponse } from "@urql/vue";
5+
import fc from "fast-check";
6+
7+
import { fcMockUseQueryResponseArg, fcMockUseSubscriptionResponseArg } from "./fc";
8+
import { mockUseQueryResponse } from "./mock-use-query-response";
9+
import { mockUseSubscriptionResponse } from "./mock-use-subscription-response";
10+
11+
interface _UseSubscribeXXXReturn<R> {
12+
data: ComputedRef<R | undefined>;
13+
error: ComputedRef<Error | undefined>;
14+
}
15+
16+
type UseSubscribeXXXReturn<R> = _UseSubscribeXXXReturn<R> &
17+
PromiseLike<_UseSubscribeXXXReturn<R>>;
18+
19+
export async function runPropertyTest<QueryData, SubData, R>(
20+
useSubscribeXXX: () => UseSubscribeXXXReturn<R>,
21+
mapQuery: (d: QueryData | undefined) => R | undefined,
22+
mapSub: (d: SubData | undefined) => R | undefined,
23+
fcQueryData: fc.Arbitrary<QueryData>,
24+
fcSubData: fc.Arbitrary<SubData>,
25+
) {
26+
type QueryRes = UseQueryResponse<QueryData, any>;
27+
type SubRes = UseSubscriptionResponse<SubData, SubData, any>;
28+
29+
const fcQueryArg = fcMockUseQueryResponseArg(fcQueryData);
30+
const fcSubArg = fcMockUseSubscriptionResponseArg(fcSubData);
31+
32+
await fc.assert(
33+
fc.asyncProperty(fcQueryArg, fcSubArg, async (queryArg, subArg) => {
34+
// Mock useGenQuery()
35+
const queryRes = mockUseQueryResponse<QueryData>(queryArg);
36+
vi.mocked(useQuery<QueryData>).mockReturnValue(queryRes as QueryRes);
37+
38+
// Mock useGenSub()
39+
const { response: subRes, issue } = mockUseSubscriptionResponse<SubData>(subArg);
40+
vi.mocked(useSubscription<SubData>).mockReturnValue(subRes as SubRes);
41+
42+
const { data, error } = await useSubscribeXXX();
43+
44+
// Assert initial values are from query.
45+
expect(error.value).toBe(queryArg.error);
46+
expect(data.value).toBe(queryArg.error ? undefined : mapQuery(queryArg.data));
47+
48+
// Assert the subsequent values are issued from subscription backed up by query.
49+
for (const issued of issue) {
50+
const expectedError = issued.error || queryArg.error;
51+
const expectedData = expectedError
52+
? undefined
53+
: (mapSub(issued.data) ?? mapQuery(queryArg.data));
54+
expect(error.value).toBe(expectedError);
55+
expect(data.value).toBe(expectedData);
56+
}
57+
}),
58+
);
59+
}

src/api/__tests__/use-run-no-subscription.spec.ts

Lines changed: 0 additions & 79 deletions
This file was deleted.

src/api/__tests__/use-state-subscription.spec.ts

Lines changed: 0 additions & 79 deletions
This file was deleted.
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import { it, vi } from "vitest";
2+
import fc from "fast-check";
3+
4+
import type {
5+
CtrlStateQuery,
6+
CtrlStateSSubscription,
7+
CtrlRunNoQuery,
8+
CtrlRunNoSSubscription,
9+
} from "@/graphql/codegen/generated";
10+
11+
import { useSubscribeRunNo } from "../use-run-no-subscription";
12+
import { useSubscribeState } from "../use-state-subscription";
13+
14+
import { runPropertyTest } from "./run-property-test";
15+
16+
// Mock functions used in runPropertyTest()
17+
vi.mock("@urql/vue", () => ({
18+
useQuery: vi.fn(),
19+
useSubscription: vi.fn(),
20+
}));
21+
22+
it("useSubscribeState", async () => {
23+
type QueryData = CtrlStateQuery;
24+
type SubData = CtrlStateSSubscription;
25+
26+
const mapQuery = (d: QueryData | undefined) => d?.ctrl.state;
27+
const mapSub = (d: SubData | undefined) => d?.ctrlState;
28+
29+
const fcCtrlState = fc.string();
30+
const fcQueryData: fc.Arbitrary<QueryData> = fc.record({
31+
ctrl: fc.record({ state: fcCtrlState }),
32+
});
33+
const fcSubData: fc.Arbitrary<SubData> = fc.record({ ctrlState: fcCtrlState });
34+
35+
await runPropertyTest(useSubscribeState, mapQuery, mapSub, fcQueryData, fcSubData);
36+
});
37+
38+
it("useSubscribeRunNo", async () => {
39+
type QueryData = CtrlRunNoQuery;
40+
type SubData = CtrlRunNoSSubscription;
41+
42+
const mapQuery = (d: QueryData | undefined) => d?.ctrl.runNo;
43+
const mapSub = (d: SubData | undefined) => d?.ctrlRunNo;
44+
45+
const fcCtrlRunNo = fc.integer();
46+
const fcQueryData: fc.Arbitrary<QueryData> = fc.record({
47+
ctrl: fc.record({ runNo: fcCtrlRunNo }),
48+
});
49+
const fcSubData: fc.Arbitrary<SubData> = fc.record({ ctrlRunNo: fcCtrlRunNo });
50+
51+
await runPropertyTest(useSubscribeRunNo, mapQuery, mapSub, fcQueryData, fcSubData);
52+
});
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import type { ComputedRef, Ref } from "vue";
2+
import { UseQueryResponse, UseSubscriptionResponse } from "@urql/vue";
3+
4+
import { onReady } from "@/utils/on-ready";
5+
import type { OnReady } from "@/utils/on-ready";
6+
7+
import { useMappedWithFallback } from "./use-mapped-with-fallback";
8+
9+
interface UseQueryBackedSubscriptionOptions<T, Q, S> {
10+
query: UseQueryResponse<Q>;
11+
subscription: UseSubscriptionResponse<S>;
12+
mapQueryData: (d: Ref<Q | undefined>) => T | undefined;
13+
mapSubscriptionData: (d: Ref<S | undefined>) => T | undefined;
14+
}
15+
16+
type UseQueryBackedSubscriptionReturn<T> = OnReady<{
17+
data: ComputedRef<T | undefined>;
18+
error: ComputedRef<Error | undefined>;
19+
}>;
20+
21+
export function useQueryBackedSubscription<T, Q, S>(
22+
options: UseQueryBackedSubscriptionOptions<T, Q, S>,
23+
): UseQueryBackedSubscriptionReturn<T> {
24+
const ret = useMappedWithFallback({
25+
response1: options.subscription,
26+
response2: options.query,
27+
map1: options.mapSubscriptionData,
28+
map2: options.mapQueryData,
29+
});
30+
return onReady(ret, options.query);
31+
}

0 commit comments

Comments
 (0)