UNPKG

@data-client/core

Version:

Async State Management without the Management. REST, GraphQL, SSE, Websockets, Fetch

304 lines (275 loc) 9.06 kB
import { Entity, schema } from '@data-client/endpoint'; import { initialState } from '../../state/reducer/createReducer'; import Controller from '../Controller'; class Tacos extends Entity { type = ''; id = ''; } const TacoList = new schema.Collection([Tacos]); const entities = { Tacos: { 1: { id: '1', type: 'foo' }, 2: { id: '2', type: 'bar' }, }, [TacoList.key]: { [TacoList.pk(undefined, undefined, '', [{ type: 'foo' }])]: ['1'], [TacoList.pk(undefined, undefined, '', [{ type: 'bar' }])]: ['2'], [TacoList.pk(undefined, undefined, '', [])]: ['1', '2'], }, }; describe('Controller.get()', () => { it('query Entity based on pk', () => { const controller = new Controller(); const state = { ...initialState, entities, }; const taco = controller.get(Tacos, { id: '1' }, state); expect(taco).toBeDefined(); expect(taco).toBeInstanceOf(Tacos); expect(taco).toMatchSnapshot(); const taco2 = controller.get(Tacos, { id: '2' }, state); expect(taco2).toBeDefined(); expect(taco2).toBeInstanceOf(Tacos); expect(taco2).not.toEqual(taco); // should maintain referential equality expect(taco).toBe(controller.get(Tacos, { id: '1' }, state)); // @ts-expect-error () => controller.get(Tacos, { id: { bob: 5 } }, state); // @ts-expect-error expect(controller.get(Tacos, 5, state)).toBeUndefined(); // @ts-expect-error () => controller.get(Tacos, { doesnotexist: 5 }, state); }); it('query Entity based on index', () => { class User extends Entity { id = ''; username = ''; static indexes = ['username'] as const; } const controller = new Controller(); const state = { ...initialState, entities: { User: { '1': { id: '1', username: 'bob' }, }, }, indexes: { User: { username: { bob: '1', }, }, }, }; const bob = controller.get(User, { username: 'bob' }, state); expect(bob).toBeDefined(); expect(bob).toBeInstanceOf(User); expect(bob).toMatchSnapshot(); // should be same as id lookup expect(bob).toBe(controller.get(User, { id: '1' }, state)); }); it('query Collection based on args', () => { const controller = new Controller(); const state = { ...initialState, entities, }; const tacos = controller.get(TacoList, { type: 'foo' }, state); expect(tacos).toBeDefined(); expect(tacos?.[0]).toBeInstanceOf(Tacos); expect(tacos).toMatchSnapshot(); const tacosBars = controller.get(TacoList, { type: 'bar' }, state); expect(tacosBars).toBeDefined(); expect(tacosBars?.[0]).toBeInstanceOf(Tacos); expect(tacosBars).not.toEqual(tacos); // should maintain referential equality expect(tacos).toBe(controller.get(TacoList, { type: 'foo' }, state)); const allTacos = controller.get(TacoList, state); expect(allTacos).toBeDefined(); expect(allTacos).toHaveLength(2); expect(allTacos).toMatchSnapshot(); // @ts-expect-error () => controller.get(TacoList, 5, state); }); describe('query All', () => { const controller = new Controller(); const state = { ...initialState, entities, }; const AllTacos = new schema.All(Tacos); it('should get all entities', () => { const allTacos = controller.get(AllTacos, state); expect(allTacos).toBeDefined(); expect(allTacos).toHaveLength(2); expect(allTacos).toMatchSnapshot(); // TODO: @ts-expect-error (we have a hack to make this not break other things now) () => controller.get(AllTacos, 5, state); }); it('should maintain referential equality', () => { const allTacos = controller.get(AllTacos, state); expect(allTacos).toStrictEqual(controller.get(TacoList, state)); }); it('should include new entities when added', () => { controller.get(AllTacos, state); const tacosWithExtra = controller.get(AllTacos, { ...state, entities: { ...entities, Tacos: { ...entities.Tacos, 3: { id: '3', type: 'extra' }, }, }, }); expect(tacosWithExtra).toBeDefined(); expect(tacosWithExtra).toHaveLength(3); expect(tacosWithExtra).toMatchSnapshot(); }); }); it('Query+All based on args', () => { const controller = new Controller(); const state = { ...initialState, entities, }; const queryTacos = new schema.Query( new schema.All(Tacos), (tacos, { type }: { type?: string } = {}) => { if (!type) return tacos; return tacos.filter(taco => taco.type === type); }, ); const tacos = controller.get(queryTacos, { type: 'foo' }, state); expect(tacos).toBeDefined(); expect(tacos?.[0]).toBeInstanceOf(Tacos); expect(tacos).toMatchSnapshot(); const tacosBars = controller.get(queryTacos, { type: 'bar' }, state); expect(tacosBars).toBeDefined(); expect(tacosBars?.[0]).toBeInstanceOf(Tacos); expect(tacosBars).not.toEqual(tacos); // should maintain referential equality expect(tacos).toBe(controller.get(queryTacos, { type: 'foo' }, state)); const allTacos = controller.get(queryTacos, state); expect(allTacos).toBeDefined(); expect(allTacos).toHaveLength(2); expect(allTacos).toMatchSnapshot(); // @ts-expect-error () => controller.get(queryTacos, 5, state); const tacosWithExtra = controller.get( queryTacos, { type: 'bar' }, { ...state, entities: { ...entities, Tacos: { ...entities.Tacos, 3: { id: '3', type: 'bar', name: 'thing3' }, }, }, }, ); expect(tacosWithExtra).toBeDefined(); expect(tacosWithExtra).toHaveLength(2); expect(tacosWithExtra).toMatchSnapshot(); }); it('Query+Collection based on args', () => { const controller = new Controller(); const state = { ...initialState, entities, }; const tacoCount = new schema.Query(TacoList, tacos => { return tacos.length ?? 0; }); expect(controller.get(tacoCount, { type: 'foo' }, state)).toBe(1); expect(controller.get(tacoCount, { type: 'bar' }, state)).toBe(1); expect(controller.get(tacoCount, state)).toBe(2); }); it('Union based on args', () => { class IDEntity extends Entity { id: string = ''; } class User extends IDEntity { type = 'user'; username: string = ''; } class Group extends IDEntity { type = 'group'; groupname: string = ''; memberCount = 0; } const queryPerson = new schema.Union( { users: User, groups: Group, }, 'type', ); const controller = new Controller(); const state = { ...initialState, entities: { User: { '1': { id: '1', type: 'users', username: 'bob' }, }, Group: { '2': { id: '2', type: 'groups', groupname: 'fast', memberCount: 5 }, }, }, }; const user = controller.get(queryPerson, { id: '1', type: 'users' }, state); expect(user).toBeDefined(); expect(user).toBeInstanceOf(User); expect(user).toMatchSnapshot(); const group = controller.get( queryPerson, { id: '2', type: 'groups' }, state, ); expect(group).toBeDefined(); expect(group).toBeInstanceOf(Group); expect(group).toMatchSnapshot(); // should maintain referential equality expect(user).toBe( controller.get(queryPerson, { id: '1', type: 'users' }, state), ); // @ts-expect-error () => controller.get(queryPerson, { id: { bob: 5 } }, state); // @ts-expect-error expect(controller.get(queryPerson, 5, state)).toBeUndefined(); // @ts-expect-error () => controller.get(queryPerson, { doesnotexist: 5 }, state); // @ts-expect-error () => controller.get(queryPerson, { id: '1', doesnotexist: 5 }, state); }); }); describe('Snapshot.getQueryMeta()', () => { it('query Entity based on pk', () => { const controller = new Controller(); const state = { ...initialState, entities, }; const snapshot = controller.snapshot(state); const taco = snapshot.getQueryMeta(Tacos, { id: '1' }).data; expect(taco).toBeDefined(); expect(taco).toBeInstanceOf(Tacos); expect(taco).toMatchSnapshot(); const taco2 = snapshot.getQueryMeta(Tacos, { id: '2' }).data; expect(taco2).toBeDefined(); expect(taco2).toBeInstanceOf(Tacos); expect(taco2).not.toEqual(taco); // should maintain referential equality expect(taco).toBe(snapshot.getQueryMeta(Tacos, { id: '1' }).data); // @ts-expect-error () => snapshot.getQueryMeta(Tacos, { id: { bob: 5 } }); // @ts-expect-error expect(snapshot.getQueryMeta(Tacos, 5).data).toBeUndefined(); // @ts-expect-error () => snapshot.getQueryMeta(Tacos, { doesnotexist: 5 }); }); });