diff --git a/packages/angular-db/tests/inject-live-query.test-d.ts b/packages/angular-db/tests/inject-live-query.test-d.ts new file mode 100644 index 000000000..667e11314 --- /dev/null +++ b/packages/angular-db/tests/inject-live-query.test-d.ts @@ -0,0 +1,138 @@ +import { describe, expectTypeOf, it } from 'vitest' +import { createCollection } from '../../db/src/collection/index' +import { mockSyncCollectionOptions } from '../../db/tests/utils' +import { + createLiveQueryCollection, + eq, + liveQueryCollectionOptions, +} from '../../db/src/query/index' +import { injectLiveQuery } from '../src/index' +import type { SingleResult } from '../../db/src/types' + +type Person = { + id: string + name: string + age: number + email: string + isActive: boolean + team: string +} + +describe(`injectLiveQuery type assertions`, () => { + it(`should type findOne query builder to return a single row`, () => { + const collection = createCollection( + mockSyncCollectionOptions({ + id: `test-persons-findone-angular`, + getKey: (person: Person) => person.id, + initialData: [], + }), + ) + + const { data } = injectLiveQuery((q) => + q + .from({ collection }) + .where(({ collection: c }) => eq(c.id, `3`)) + .findOne(), + ) + + // findOne returns a single result or undefined + expectTypeOf(data()).toEqualTypeOf() + }) + + it(`should type findOne config object to return a single row`, () => { + const collection = createCollection( + mockSyncCollectionOptions({ + id: `test-persons-findone-config-angular`, + getKey: (person: Person) => person.id, + initialData: [], + }), + ) + + const { data } = injectLiveQuery({ + params: () => ({ id: `3` }), + query: ({ params, q }) => + q + .from({ collection }) + .where(({ collection: c }) => eq(c.id, params.id)) + .findOne(), + }) + + // findOne returns a single result or undefined + expectTypeOf(data()).toEqualTypeOf() + }) + + it(`should type findOne collection using liveQueryCollectionOptions to return a single row`, () => { + const collection = createCollection( + mockSyncCollectionOptions({ + id: `test-persons-findone-options-angular`, + getKey: (person: Person) => person.id, + initialData: [], + }), + ) + + const options = liveQueryCollectionOptions({ + query: (q) => + q + .from({ collection }) + .where(({ collection: c }) => eq(c.id, `3`)) + .findOne(), + }) + + const liveQueryCollection = createCollection(options) + + expectTypeOf(liveQueryCollection).toExtend() + + const { data } = injectLiveQuery(liveQueryCollection) + + // findOne returns a single result or undefined + expectTypeOf(data()).toEqualTypeOf() + }) + + it(`should type findOne collection using createLiveQueryCollection to return a single row`, () => { + const collection = createCollection( + mockSyncCollectionOptions({ + id: `test-persons-findone-create-angular`, + getKey: (person: Person) => person.id, + initialData: [], + }), + ) + + const liveQueryCollection = createLiveQueryCollection({ + query: (q) => + q + .from({ collection }) + .where(({ collection: c }) => eq(c.id, `3`)) + .findOne(), + }) + + expectTypeOf(liveQueryCollection).toExtend() + + const { data } = injectLiveQuery(liveQueryCollection) + + // findOne returns a single result or undefined + expectTypeOf(data()).toEqualTypeOf() + }) + + it(`should type regular query to return an array`, () => { + const collection = createCollection( + mockSyncCollectionOptions({ + id: `test-persons-array-angular`, + getKey: (person: Person) => person.id, + initialData: [], + }), + ) + + const { data } = injectLiveQuery((q) => + q + .from({ collection }) + .where(({ collection: c }) => eq(c.isActive, true)) + .select(({ collection: c }) => ({ + id: c.id, + name: c.name, + })), + ) + + // Regular queries should return an array + expectTypeOf(data()).toEqualTypeOf>() + }) +}) diff --git a/packages/angular-db/tests/inject-live-query.test.ts b/packages/angular-db/tests/inject-live-query.test.ts index 2f4db01ca..81fbb12a9 100644 --- a/packages/angular-db/tests/inject-live-query.test.ts +++ b/packages/angular-db/tests/inject-live-query.test.ts @@ -14,6 +14,7 @@ import type { CollectionStatus, Context, LiveQueryCollectionConfig, + NonSingleResult, QueryBuilder, } from '@tanstack/db' @@ -66,12 +67,13 @@ async function waitForAngularUpdate() { function createMockCollection( initial: Array> = [], initialStatus: CollectionStatus = `ready`, -): Collection> & { - __setStatus: (s: CollectionStatus) => void - __replaceAll: (rows: Array>) => void - __upsert: (row: T & Record<`id`, K>) => void - __delete: (key: K) => void -} { +): Collection> & + NonSingleResult & { + __setStatus: (s: CollectionStatus) => void + __replaceAll: (rows: Array>) => void + __upsert: (row: T & Record<`id`, K>) => void + __delete: (key: K) => void + } { const map = new Map() for (const r of initial) { map.set(r.id, r) @@ -532,7 +534,7 @@ describe(`injectLiveQuery`, () => { await waitForAngularUpdate() - expect(res.collection().id).toEqual(expect.any(String)) + expect(res.collection()!.id).toEqual(expect.any(String)) expect(res.status()).toBe(`ready`) expect(Array.isArray(res.data())).toBe(true) expect(res.state() instanceof Map).toBe(true) @@ -565,7 +567,7 @@ describe(`injectLiveQuery`, () => { const res = injectLiveQuery(config) await waitForAngularUpdate() - expect(res.collection().id).toEqual(expect.any(String)) + expect(res.collection()!.id).toEqual(expect.any(String)) expect(res.isReady()).toBe(true) }) }) @@ -611,7 +613,7 @@ describe(`injectLiveQuery`, () => { await waitForAngularUpdate() - expect(res.collection().id).toEqual(expect.any(String)) + expect(res.collection()!.id).toEqual(expect.any(String)) expect(res.status()).toBe(`ready`) expect(Array.isArray(res.data())).toBe(true) expect(res.state() instanceof Map).toBe(true) @@ -752,6 +754,40 @@ describe(`injectLiveQuery`, () => { }) }) + it(`should return a single object for findOne query`, async () => { + await TestBed.runInInjectionContext(async () => { + const collection = createCollection( + mockSyncCollectionOptions({ + id: `test-persons-findone-angular`, + getKey: (person: Person) => person.id, + initialData: initialPersons, + }), + ) + + const { state, data } = injectLiveQuery((q) => + q + .from({ collection }) + .where(({ collection: c }) => eq(c.id, `3`)) + .findOne(), + ) + + await waitForAngularUpdate() + + expect(state().size).toBe(1) + expect(state().get(`3`)).toMatchObject({ + id: `3`, + name: `John Smith`, + }) + + // findOne should return a single object, not an array + expect(Array.isArray(data())).toBe(false) + expect(data()).toMatchObject({ + id: `3`, + name: `John Smith`, + }) + }) + }) + describe(`eager execution during sync`, () => { it(`should show state while isLoading is true during sync`, async () => { await TestBed.runInInjectionContext(async () => { @@ -795,7 +831,7 @@ describe(`injectLiveQuery`, () => { }) // Start the live query sync manually - liveQueryCollection().preload() + liveQueryCollection()!.preload() await waitForAngularUpdate() @@ -907,7 +943,7 @@ describe(`injectLiveQuery`, () => { }) // Start the live query sync manually - liveQueryCollection().preload() + liveQueryCollection()!.preload() await waitForAngularUpdate() @@ -1007,7 +1043,7 @@ describe(`injectLiveQuery`, () => { }) // Start the live query sync manually - liveQueryCollection().preload() + liveQueryCollection()!.preload() await waitForAngularUpdate()