Skip to content

Commit 9ca2797

Browse files
committed
checkpoint
1 parent 046ad7d commit 9ca2797

File tree

1 file changed

+347
-0
lines changed

1 file changed

+347
-0
lines changed
Lines changed: 347 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,347 @@
1+
import { describe, expectTypeOf, test } from "vitest"
2+
import {
3+
createLiveQueryCollection,
4+
eq,
5+
gt,
6+
and,
7+
or,
8+
} from "../../src/query/index.js"
9+
import { createCollection } from "../../src/collection.js"
10+
import { mockSyncCollectionOptions } from "../utls.js"
11+
12+
// Complex nested type for testing with optional properties
13+
type Person = {
14+
id: string
15+
name: string
16+
age: number
17+
email: string
18+
isActive: boolean
19+
team: string
20+
profile?: {
21+
bio: string
22+
score: number
23+
stats: {
24+
tasksCompleted: number
25+
rating: number
26+
}
27+
}
28+
address?: {
29+
city: string
30+
country: string
31+
coordinates: {
32+
lat: number
33+
lng: number
34+
}
35+
}
36+
}
37+
38+
// Sample data
39+
const samplePersons: Array<Person> = [
40+
{
41+
id: "1",
42+
name: "John Doe",
43+
age: 30,
44+
45+
isActive: true,
46+
team: "team1",
47+
profile: {
48+
bio: "Senior developer",
49+
score: 85,
50+
stats: {
51+
tasksCompleted: 120,
52+
rating: 4.5,
53+
},
54+
},
55+
address: {
56+
city: "New York",
57+
country: "USA",
58+
coordinates: {
59+
lat: 40.7128,
60+
lng: -74.006,
61+
},
62+
},
63+
},
64+
]
65+
66+
function createPersonsCollection() {
67+
return createCollection(
68+
mockSyncCollectionOptions<Person>({
69+
id: "test-persons",
70+
getKey: (person) => person.id,
71+
initialData: samplePersons,
72+
})
73+
)
74+
}
75+
76+
describe("Nested Properties Types", () => {
77+
const personsCollection = createPersonsCollection()
78+
79+
test("select with nested properties returns correct types", () => {
80+
const collection = createLiveQueryCollection({
81+
query: (q) =>
82+
q.from({ persons: personsCollection }).select(({ persons }) => ({
83+
id: persons.id,
84+
name: persons.name,
85+
// Level 1 nesting
86+
bio: persons.profile?.bio,
87+
score: persons.profile?.score,
88+
// Level 2 nesting
89+
tasksCompleted: persons.profile?.stats.tasksCompleted,
90+
rating: persons.profile?.stats.rating,
91+
// Address nesting
92+
city: persons.address?.city,
93+
country: persons.address?.country,
94+
// Coordinates (level 2 nesting under address)
95+
lat: persons.address?.coordinates.lat,
96+
lng: persons.address?.coordinates.lng,
97+
})),
98+
})
99+
100+
const results = collection.toArray
101+
expectTypeOf(results).toEqualTypeOf<
102+
Array<{
103+
id: string
104+
name: string
105+
bio: string | undefined // Now optional because profile is optional
106+
score: number | undefined // Now optional because profile is optional
107+
tasksCompleted: number | undefined // Now optional because profile?.stats is optional
108+
rating: number | undefined // Now optional because profile?.stats is optional
109+
city: string | undefined // Now optional because address is optional
110+
country: string | undefined // Now optional because address is optional
111+
lat: number | undefined // Now optional because address?.coordinates is optional
112+
lng: number | undefined // Now optional because address?.coordinates is optional
113+
}>
114+
>()
115+
})
116+
117+
test("where clause with nested properties", () => {
118+
const collection = createLiveQueryCollection({
119+
query: (q) =>
120+
q
121+
.from({ persons: personsCollection })
122+
// Test various nested property comparisons
123+
.where(({ persons }) => gt(persons.profile?.score, 80))
124+
.where(({ persons }) => eq(persons.address?.country, "USA"))
125+
.where(({ persons }) => gt(persons.address?.coordinates.lat, 35))
126+
.where(({ persons }) => gt(persons.profile?.stats.rating, 4.0))
127+
.select(({ persons }) => ({
128+
id: persons.id,
129+
name: persons.name,
130+
})),
131+
})
132+
133+
const results = collection.toArray
134+
expectTypeOf(results).toEqualTypeOf<
135+
Array<{
136+
id: string
137+
name: string
138+
}>
139+
>()
140+
})
141+
142+
test("where clause with complex nested expressions", () => {
143+
const collection = createLiveQueryCollection({
144+
query: (q) =>
145+
q
146+
.from({ persons: personsCollection })
147+
.where(({ persons }) =>
148+
or(
149+
gt(persons.profile?.score, 90),
150+
and(
151+
gt(persons.profile?.stats.tasksCompleted, 100),
152+
gt(persons.profile?.stats.rating, 4.5)
153+
)
154+
)
155+
)
156+
.select(({ persons }) => ({
157+
id: persons.id,
158+
name: persons.name,
159+
score: persons.profile?.score,
160+
})),
161+
})
162+
163+
const results = collection.toArray
164+
expectTypeOf(results).toEqualTypeOf<
165+
Array<{
166+
id: string
167+
name: string
168+
score: number | undefined // Optional because profile is optional
169+
}>
170+
>()
171+
})
172+
173+
test("orderBy with nested properties", () => {
174+
const collection = createLiveQueryCollection({
175+
query: (q) =>
176+
q
177+
.from({ persons: personsCollection })
178+
.orderBy(({ persons }) => persons.profile?.score, "desc")
179+
.orderBy(({ persons }) => persons.address?.coordinates.lat, "asc")
180+
.orderBy(({ persons }) => persons.profile?.stats.rating, "desc")
181+
.select(({ persons }) => ({
182+
id: persons.id,
183+
name: persons.name,
184+
score: persons.profile?.score,
185+
lat: persons.address?.coordinates.lat,
186+
rating: persons.profile?.stats.rating,
187+
})),
188+
})
189+
190+
const results = collection.toArray
191+
expectTypeOf(results).toEqualTypeOf<
192+
Array<{
193+
id: string
194+
name: string
195+
score: number | undefined // Optional because profile is optional
196+
lat: number | undefined // Optional because address?.coordinates is optional
197+
rating: number | undefined // Optional because profile?.stats is optional
198+
}>
199+
>()
200+
})
201+
202+
test("orderBy with multiple nested properties and options", () => {
203+
const collection = createLiveQueryCollection({
204+
query: (q) =>
205+
q
206+
.from({ persons: personsCollection })
207+
.orderBy(({ persons }) => persons.profile?.score, {
208+
direction: "desc",
209+
nulls: "last",
210+
})
211+
.orderBy(({ persons }) => persons.address?.city, {
212+
direction: "asc",
213+
nulls: "first",
214+
stringSort: "locale",
215+
})
216+
.select(({ persons }) => ({
217+
id: persons.id,
218+
name: persons.name,
219+
})),
220+
})
221+
222+
const results = collection.toArray
223+
expectTypeOf(results).toEqualTypeOf<
224+
Array<{
225+
id: string
226+
name: string
227+
}>
228+
>()
229+
})
230+
231+
test("deeply nested property access in all methods", () => {
232+
const collection = createLiveQueryCollection({
233+
query: (q) =>
234+
q
235+
.from({ persons: personsCollection })
236+
// WHERE with deeply nested
237+
.where(({ persons }) => gt(persons.profile?.stats.tasksCompleted, 50))
238+
// ORDER BY with deeply nested
239+
.orderBy(({ persons }) => persons.profile?.stats.rating, "desc")
240+
.orderBy(({ persons }) => persons.address?.coordinates.lng, "asc")
241+
// SELECT with deeply nested
242+
.select(({ persons }) => ({
243+
id: persons.id,
244+
name: persons.name,
245+
// Level 1
246+
team: persons.team,
247+
// Level 2 under profile
248+
bio: persons.profile?.bio,
249+
score: persons.profile?.score,
250+
// Level 3 under profile?.stats
251+
tasksCompleted: persons.profile?.stats.tasksCompleted,
252+
rating: persons.profile?.stats.rating,
253+
// Level 2 under address
254+
city: persons.address?.city,
255+
country: persons.address?.country,
256+
// Level 3 under address?.coordinates
257+
lat: persons.address?.coordinates.lat,
258+
lng: persons.address?.coordinates.lng,
259+
})),
260+
})
261+
262+
const results = collection.toArray
263+
expectTypeOf(results).toEqualTypeOf<
264+
Array<{
265+
id: string
266+
name: string
267+
team: string
268+
bio: string | undefined
269+
score: number | undefined
270+
tasksCompleted: number | undefined
271+
rating: number | undefined
272+
city: string | undefined
273+
country: string | undefined
274+
lat: number | undefined
275+
lng: number | undefined
276+
}>
277+
>()
278+
})
279+
280+
test("direct nested object access", () => {
281+
const collection = createLiveQueryCollection({
282+
query: (q) =>
283+
q.from({ persons: personsCollection }).select(({ persons }) => ({
284+
// These should be properly typed without optionality issues
285+
profileExists: persons.profile,
286+
addressExists: persons.address,
287+
// Direct access works fine with required properties
288+
score: persons.profile?.score,
289+
coordinates: persons.address?.coordinates,
290+
})),
291+
})
292+
293+
const results = collection.toArray
294+
expectTypeOf(results).toEqualTypeOf<
295+
Array<{
296+
profileExists:
297+
| {
298+
bio: string
299+
score: number
300+
stats: {
301+
tasksCompleted: number
302+
rating: number
303+
}
304+
}
305+
| undefined
306+
addressExists:
307+
| {
308+
city: string
309+
country: string
310+
coordinates: {
311+
lat: number
312+
lng: number
313+
}
314+
}
315+
| undefined
316+
score: number | undefined
317+
coordinates:
318+
| {
319+
lat: number
320+
lng: number
321+
}
322+
| undefined
323+
}>
324+
>()
325+
})
326+
327+
test("nested properties work at runtime with correct types", () => {
328+
// Test that nested properties work correctly at runtime
329+
const collection = createLiveQueryCollection({
330+
query: (q) =>
331+
q.from({ persons: personsCollection }).select(({ persons }) => ({
332+
id: persons.id,
333+
profileScore: persons.profile?.score,
334+
coordinatesLat: persons.address?.coordinates.lat,
335+
})),
336+
})
337+
338+
const results = collection.toArray
339+
expectTypeOf(results).toEqualTypeOf<
340+
Array<{
341+
id: string
342+
profileScore: number | undefined
343+
coordinatesLat: number | undefined
344+
}>
345+
>()
346+
})
347+
})

0 commit comments

Comments
 (0)