Skip to content

Commit 02950fc

Browse files
committed
fix query tests
1 parent 0698436 commit 02950fc

File tree

6 files changed

+183
-107
lines changed

6 files changed

+183
-107
lines changed

src/create-zero-composables.ts

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,22 @@
1-
import type { CustomMutatorDefs, Query, Schema, ZeroOptions } from '@rocicorp/zero'
1+
import type { CustomMutatorDefs, DefaultContext, DefaultSchema, PullRow, QueryOrQueryRequest, ReadonlyJSONValue, Schema, ZeroOptions } from '@rocicorp/zero'
22
import type { MaybeRefOrGetter, ShallowRef } from 'vue'
33
import type { QueryResult, UseQueryOptions } from './query'
44
import { Zero } from '@rocicorp/zero'
55
import { shallowRef, toValue, watch } from 'vue'
66
import { useQuery as _useQuery } from './query'
77

88
export function createZeroComposables<
9-
S extends Schema = Schema,
9+
TSchema extends Schema = DefaultSchema,
1010
MD extends CustomMutatorDefs | undefined = undefined,
11+
TContext = DefaultContext,
1112
>(
12-
optsOrZero: MaybeRefOrGetter<ZeroOptions<S, MD> | { zero: Zero<S, MD> }>,
13+
optsOrZero: MaybeRefOrGetter<ZeroOptions<TSchema, MD, TContext> | { zero: Zero<TSchema, MD, TContext> }>,
1314
) {
14-
let z: ShallowRef<Zero<S, MD>>
15+
let z: ShallowRef<Zero<TSchema, MD, TContext>>
1516

16-
function useZero(): ShallowRef<Zero<S, MD>> {
17+
function useZero(): ShallowRef<Zero<TSchema, MD, TContext>> {
1718
if (!z) {
18-
z = shallowRef() as ShallowRef<Zero<S, MD>>
19+
z = shallowRef() as ShallowRef<Zero<TSchema, MD, TContext>>
1920
}
2021

2122
if (z.value) {
@@ -34,10 +35,12 @@ export function createZeroComposables<
3435
}
3536

3637
function useQuery<
37-
TTable extends keyof S['tables'] & string,
38-
TReturn,
38+
TTable extends keyof TSchema['tables'] & string,
39+
TInput extends ReadonlyJSONValue | undefined,
40+
TOutput extends ReadonlyJSONValue | undefined,
41+
TReturn = PullRow<TTable, TSchema>,
3942
>(
40-
query: MaybeRefOrGetter<Query<S, TTable, TReturn>>,
43+
query: MaybeRefOrGetter<QueryOrQueryRequest<TTable, TInput, TOutput, TSchema, TReturn, TContext>>,
4144
options?: MaybeRefOrGetter<UseQueryOptions>,
4245
): QueryResult<TReturn> {
4346
const zero = useZero()

src/query.test.ts

Lines changed: 94 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import type { TTL } from '@rocicorp/zero'
2-
import { createBuilder, createSchema, number, string, syncedQuery, table, Zero } from '@rocicorp/zero'
2+
import { createBuilder, createCRUDBuilder, createSchema, defineMutator, defineMutators, defineQueries, defineQuery, number, string, table, Zero } from '@rocicorp/zero'
33
import { describe, expect, it, onTestFinished, vi } from 'vitest'
44
import { nextTick, ref, watchEffect } from 'vue'
5+
import z from 'zod'
56
import { createZeroComposables } from './create-zero-composables'
67
import { useQuery } from './query'
78
import { VueView, vueViewFactory } from './view'
@@ -20,44 +21,58 @@ const schema = createSchema({
2021
async function setupTestEnvironment() {
2122
const userID = ref('asdf')
2223

24+
const crud = createCRUDBuilder(schema)
25+
const mutators = defineMutators({
26+
table: {
27+
insert: defineMutator(
28+
z.object({ a: z.number(), b: z.string() }),
29+
async ({ tx, args: { a, b } }) => {
30+
return tx.mutate(crud.table.insert({ a, b }))
31+
},
32+
),
33+
update: defineMutator(
34+
z.object({ a: z.number(), b: z.string() }),
35+
async ({ tx, args: { a, b } }) => {
36+
return tx.mutate(crud.table.update({ a, b }))
37+
},
38+
),
39+
},
40+
})
41+
2342
const { useZero, useQuery } = createZeroComposables(() => ({
2443
userID: userID.value,
2544
server: null,
2645
schema,
46+
mutators,
2747
kvStore: 'mem',
2848
}))
2949

3050
const zero = useZero()
31-
await zero.value.mutate.table.insert({ a: 1, b: 'a' })
32-
await zero.value.mutate.table.insert({ a: 2, b: 'b' })
33-
34-
const builder = createBuilder(schema)
35-
const byIdQuery = syncedQuery(
36-
'byId',
37-
([id]) => {
38-
if (typeof id !== 'number') {
39-
throw new TypeError('id must be a number')
40-
}
41-
return [id] as const
42-
},
43-
(id: number) => {
44-
return builder.table.where('a', id)
45-
},
46-
)
47-
48-
const tableQuery = zero!.value.query.table
51+
await zero.value.mutate(mutators.table.insert({ a: 1, b: 'a' })).client
52+
await zero.value.mutate(mutators.table.insert({ a: 2, b: 'b' })).client
53+
54+
const zql = createBuilder(schema)
55+
const queries = defineQueries({
56+
byId: defineQuery(
57+
z.number(),
58+
({ args: a }) => zql.table.where('a', a),
59+
),
60+
table: defineQuery(
61+
() => zql.table,
62+
),
63+
})
4964

50-
onTestFinished(() => {
51-
zero.value.close()
65+
onTestFinished(async () => {
66+
await zero.value.close()
5267
})
5368

54-
return { z: zero, tableQuery, useQuery, byIdQuery, userID }
69+
return { zero, queries, useQuery, mutators, userID }
5570
}
5671

5772
describe('useQuery', () => {
5873
it('useQuery', async () => {
59-
const { z, tableQuery, useQuery } = await setupTestEnvironment()
60-
const { data: rows, status } = useQuery(() => tableQuery)
74+
const { zero, mutators, queries, useQuery } = await setupTestEnvironment()
75+
const { data: rows, status } = useQuery(() => queries.table())
6176
expect(rows.value).toMatchInlineSnapshot(`[
6277
{
6378
"a": 1,
@@ -72,7 +87,7 @@ describe('useQuery', () => {
7287
]`)
7388
expect(status.value).toEqual('unknown')
7489

75-
await z.value.mutate.table.insert({ a: 3, b: 'c' })
90+
await zero.value.mutate(mutators.table.insert({ a: 3, b: 'c' })).client
7691
await nextTick()
7792

7893
expect(rows.value).toMatchInlineSnapshot(`[
@@ -98,24 +113,45 @@ describe('useQuery', () => {
98113
})
99114

100115
it('useQuery with ttl', async () => {
101-
const { z, tableQuery, useQuery } = await setupTestEnvironment()
116+
const { zero, queries, useQuery } = await setupTestEnvironment()
102117

103118
const ttl = ref<TTL>('1m')
104119

105-
const materializeSpy = vi.spyOn(z.value, 'materialize')
106-
const queryGetter = vi.fn(() => tableQuery)
120+
const materializeSpy = vi.spyOn(zero.value, 'materialize')
121+
const queryGetter = vi.fn(() => queries.table())
107122

108123
useQuery(queryGetter, () => ({ ttl: ttl.value }))
109124
expect(queryGetter).toHaveBeenCalledTimes(1)
110125

111126
expect(materializeSpy).toHaveLastReturnedWith(expect.any(VueView))
112127
expect(materializeSpy).toHaveBeenCalledExactlyOnceWith(
113-
tableQuery,
128+
expect.any(Object),
114129
vueViewFactory,
115130
{ ttl: '1m' },
116131
)
132+
expect(materializeSpy.mock.calls[0]![0]).toMatchInlineSnapshot(`
133+
QueryImpl {
134+
"_exists": [Function],
135+
"customQueryID": {
136+
"args": [],
137+
"name": "table",
138+
},
139+
"format": {
140+
"relationships": {},
141+
"singular": false,
142+
},
143+
"limit": [Function],
144+
"one": [Function],
145+
"orderBy": [Function],
146+
"related": [Function],
147+
"start": [Function],
148+
"where": [Function],
149+
"whereExists": [Function],
150+
Symbol(): true,
151+
}
152+
`)
117153

118-
const view: VueView<unknown> = materializeSpy.mock.results[0]!.value
154+
const view: VueView = materializeSpy.mock.results[0]!.value
119155
const updateTTLSpy = vi.spyOn(view, 'updateTTL')
120156

121157
materializeSpy.mockClear()
@@ -128,12 +164,12 @@ describe('useQuery', () => {
128164
})
129165

130166
it('useQuery deps change', async () => {
131-
const { tableQuery, useQuery } = await setupTestEnvironment()
167+
const { queries, useQuery } = await setupTestEnvironment()
132168

133169
const a = ref(1)
134170

135171
const { data: rows, status } = useQuery(() =>
136-
tableQuery.where('a', a.value),
172+
queries.byId(a.value),
137173
)
138174

139175
const rowLog: unknown[] = []
@@ -187,9 +223,9 @@ describe('useQuery', () => {
187223
})
188224

189225
it('useQuery deps change watchEffect', async () => {
190-
const { z, tableQuery, useQuery } = await setupTestEnvironment()
226+
const { zero, queries, mutators, useQuery } = await setupTestEnvironment()
191227
const a = ref(1)
192-
const { data: rows } = useQuery(() => tableQuery.where('a', a.value))
228+
const { data: rows } = useQuery(() => queries.byId(a.value))
193229

194230
let run = 0
195231

@@ -205,7 +241,7 @@ describe('useQuery', () => {
205241
},
206242
]`,
207243
)
208-
z.value.mutate.table.update({ a: 1, b: 'a2' })
244+
zero.value.mutate(mutators.table.update({ a: 1, b: 'a2' }))
209245
}
210246
else if (run === 1) {
211247
expect(rows.value).toMatchInlineSnapshot(
@@ -236,37 +272,36 @@ describe('useQuery', () => {
236272
})
237273
})
238274

239-
it('useQuery with syncedQuery', async () => {
240-
const { byIdQuery, useQuery } = await setupTestEnvironment()
241-
if (!byIdQuery) {
242-
return
243-
}
244-
245-
const a = ref(1)
246-
const { data: rows, status } = useQuery(() => byIdQuery(a.value))
247-
248-
expect(rows.value).toMatchInlineSnapshot(`
249-
[
250-
{
251-
"a": 1,
252-
"b": "a",
253-
Symbol(rc): 1,
254-
},
255-
]`)
256-
expect(status.value).toEqual('unknown')
257-
})
258-
259275
it('can still be used without createZero', async () => {
276+
const crud = createCRUDBuilder(schema)
277+
const mutators = defineMutators({
278+
table: {
279+
insert: defineMutator(
280+
z.object({ a: z.number(), b: z.string() }),
281+
async ({ tx, args: { a, b } }) => {
282+
return tx.mutate(crud.table.insert({ a, b }))
283+
},
284+
),
285+
},
286+
})
260287
const zero = new Zero({
261288
userID: 'test-user',
262289
server: null,
263290
schema,
291+
mutators,
264292
kvStore: 'mem' as const,
265293
})
266-
await zero.mutate.table.insert({ a: 1, b: 'a' })
267-
await zero.mutate.table.insert({ a: 2, b: 'b' })
294+
await zero.mutate(mutators.table.insert({ a: 1, b: 'a' })).client
295+
await zero.mutate(mutators.table.insert({ a: 2, b: 'b' })).client
296+
297+
const zql = createBuilder(schema)
298+
const queries = defineQueries({
299+
table: defineQuery(
300+
() => zql.table,
301+
),
302+
})
268303

269-
const { data: rows, status } = useQuery(zero, () => zero.query.table)
304+
const { data: rows, status } = useQuery(zero, () => queries.table())
270305
expect(rows.value).toMatchInlineSnapshot(`[
271306
{
272307
"a": 1,
@@ -281,6 +316,6 @@ describe('useQuery', () => {
281316
]`)
282317
expect(status.value).toEqual('unknown')
283318

284-
zero.close()
319+
await zero.close()
285320
})
286321
})

src/query.ts

Lines changed: 39 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// based on https://github.com/rocicorp/mono/tree/main/packages/zero-solid
22

3-
import type { CustomMutatorDefs, HumanReadable, Query, Schema, TTL, Zero } from '@rocicorp/zero'
3+
import type { CustomMutatorDefs, DefaultContext, DefaultSchema, HumanReadable, PullRow, Query, QueryOrQueryRequest, ReadonlyJSONValue, Schema, TTL, Zero } from '@rocicorp/zero'
44
import type { ComputedRef, MaybeRefOrGetter } from 'vue'
55
import type { QueryError, QueryStatus, VueView } from './view'
66

@@ -26,24 +26,57 @@ export interface QueryResult<TReturn> {
2626
error: ComputedRef<QueryError & { retry: () => void } | undefined>
2727
}
2828

29-
export function useQuery<
30-
TSchema extends Schema,
29+
export function addContextToQuery<
3130
TTable extends keyof TSchema['tables'] & string,
31+
TInput extends ReadonlyJSONValue | undefined,
32+
TOutput extends ReadonlyJSONValue | undefined,
33+
TSchema extends Schema,
34+
TReturn,
35+
TContext,
36+
>(query: QueryOrQueryRequest<
37+
TTable,
38+
TInput,
39+
TOutput,
40+
TSchema,
3241
TReturn,
42+
TContext
43+
>, context: TContext): Query<TTable, TSchema, TReturn> {
44+
return 'query' in query ? query.query.fn({ ctx: context, args: query.args }) : query
45+
}
46+
47+
export function useQuery<
48+
TTable extends keyof TSchema['tables'] & string,
49+
TInput extends ReadonlyJSONValue | undefined,
50+
TOutput extends ReadonlyJSONValue | undefined,
51+
TSchema extends Schema = DefaultSchema,
52+
TReturn = PullRow<TTable, TSchema>,
53+
TContext = DefaultContext,
3354
MD extends CustomMutatorDefs | undefined = undefined,
3455
>(
35-
z: MaybeRefOrGetter<Zero<TSchema, MD>>,
36-
query: MaybeRefOrGetter<Query<TSchema, TTable, TReturn>>,
56+
z: MaybeRefOrGetter<Zero<TSchema, MD, TContext>>,
57+
query: MaybeRefOrGetter<QueryOrQueryRequest<TTable, TInput, TOutput, TSchema, TReturn, TContext>>,
3758
options?: MaybeRefOrGetter<UseQueryOptions>,
3859
): QueryResult<TReturn> {
3960
const ttl = computed(() => toValue(options)?.ttl ?? DEFAULT_TTL_MS)
40-
const view = shallowRef<VueView<HumanReadable<TReturn>> | null>(null)
61+
const view = shallowRef<VueView | null>(null)
4162
const refetchKey = shallowRef(0)
4263

64+
const q = shallowRef()
4365
watch(
4466
[
4567
() => toValue(query),
4668
() => toValue(z),
69+
],
70+
([query, z]) => {
71+
q.value = addContextToQuery(toValue(query), toValue(z).context)
72+
},
73+
{ immediate: true },
74+
)
75+
76+
watch(
77+
[
78+
() => toValue(q),
79+
() => toValue(z),
4780
refetchKey,
4881
],
4982
([q, z]) => {

0 commit comments

Comments
 (0)