@data-client/core
Version:
Async State Management without the Management. REST, GraphQL, SSE, Websockets, Fetch
304 lines (275 loc) • 9.06 kB
text/typescript
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 });
});
});