-
-
Notifications
You must be signed in to change notification settings - Fork 2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(signals): add entities subpackage (#4090)
- Loading branch information
1 parent
73fda59
commit f01bcd1
Showing
35 changed files
with
2,663 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export * from './src/index'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
{ | ||
"lib": { | ||
"entryFile": "index.ts" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
export type User = { id: number; firstName: string; lastName: string }; | ||
export type Todo = { _id: string; text: string; completed: boolean }; | ||
|
||
export const user1: User = { id: 1, firstName: 'John', lastName: 'Doe' }; | ||
export const user2: User = { id: 2, firstName: 'Jane', lastName: 'Smith' }; | ||
export const user3: User = { id: 3, firstName: 'Joe', lastName: 'Johnson' }; | ||
|
||
export const todo1: Todo = { _id: 'x', text: 'Buy milk', completed: true }; | ||
export const todo2: Todo = { _id: 'y', text: 'Buy eggs', completed: false }; | ||
export const todo3: Todo = { _id: 'z', text: 'Make bread', completed: true }; |
248 changes: 248 additions & 0 deletions
248
modules/signals/entities/spec/updaters/add-entities.spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,248 @@ | ||
import { patchState, signalStore, type } from '@ngrx/signals'; | ||
import { addEntities, withEntities } from '../../src'; | ||
import { Todo, todo1, todo2, todo3, User, user1, user2, user3 } from '../mocks'; | ||
|
||
describe('addEntities', () => { | ||
it('adds entities if they do not exist', () => { | ||
const Store = signalStore(withEntities<User>()); | ||
const store = new Store(); | ||
|
||
patchState(store, addEntities([user1])); | ||
|
||
expect(store.entityMap()).toEqual({ 1: user1 }); | ||
expect(store.ids()).toEqual([1]); | ||
expect(store.entities()).toEqual([user1]); | ||
|
||
patchState(store, addEntities([user2, user3])); | ||
|
||
expect(store.entityMap()).toEqual({ 1: user1, 2: user2, 3: user3 }); | ||
expect(store.ids()).toEqual([1, 2, 3]); | ||
expect(store.entities()).toEqual([user1, user2, user3]); | ||
}); | ||
|
||
it('does not add entities if they already exist', () => { | ||
const Store = signalStore(withEntities<User>()); | ||
const store = new Store(); | ||
|
||
patchState(store, addEntities([user1, user2])); | ||
|
||
const entityMap = store.entityMap(); | ||
const ids = store.ids(); | ||
const entities = store.entities(); | ||
|
||
patchState( | ||
store, | ||
addEntities([user2, { ...user2, firstName: 'Jack' }, user1]), | ||
addEntities([] as User[]) | ||
); | ||
|
||
expect(store.entityMap()).toBe(entityMap); | ||
expect(store.ids()).toBe(ids); | ||
expect(store.entities()).toBe(entities); | ||
|
||
expect(store.entityMap()).toEqual({ 1: user1, 2: user2 }); | ||
expect(store.ids()).toEqual([1, 2]); | ||
expect(store.entities()).toEqual([user1, user2]); | ||
|
||
patchState(store, addEntities([user1, user3, user2])); | ||
|
||
expect(store.entityMap()).toEqual({ 1: user1, 2: user2, 3: user3 }); | ||
expect(store.ids()).toEqual([1, 2, 3]); | ||
expect(store.entities()).toEqual([user1, user2, user3]); | ||
}); | ||
|
||
it('adds entities to the specified collection if they do not exist', () => { | ||
const Store = signalStore( | ||
withEntities({ | ||
entity: type<User>(), | ||
collection: 'user', | ||
}) | ||
); | ||
const store = new Store(); | ||
|
||
patchState( | ||
store, | ||
addEntities([user1, user2], { collection: 'user' }), | ||
addEntities([user3], { collection: 'user' }) | ||
); | ||
|
||
expect(store.userEntityMap()).toEqual({ 1: user1, 2: user2, 3: user3 }); | ||
expect(store.userIds()).toEqual([1, 2, 3]); | ||
expect(store.userEntities()).toEqual([user1, user2, user3]); | ||
}); | ||
|
||
it('does not add entities to the specified collection if they already exist', () => { | ||
const Store = signalStore( | ||
withEntities({ | ||
entity: type<User>(), | ||
collection: 'user', | ||
}) | ||
); | ||
const store = new Store(); | ||
|
||
patchState( | ||
store, | ||
addEntities([user1, { ...user1, lastName: 'Hendrix' }, user3, user1], { | ||
collection: 'user', | ||
}) | ||
); | ||
|
||
const userEntityMap = store.userEntityMap(); | ||
const userIds = store.userIds(); | ||
const userEntities = store.userEntities(); | ||
|
||
patchState( | ||
store, | ||
addEntities([] as User[], { collection: 'user' }), | ||
addEntities([user3, { ...user3, firstName: 'Jimmy' }, user1], { | ||
collection: 'user', | ||
}) | ||
); | ||
|
||
expect(store.userEntityMap()).toBe(userEntityMap); | ||
expect(store.userIds()).toBe(userIds); | ||
expect(store.userEntities()).toBe(userEntities); | ||
expect(store.userEntityMap()).toEqual({ 1: user1, 3: user3 }); | ||
expect(store.userIds()).toEqual([1, 3]); | ||
expect(store.userEntities()).toEqual([user1, user3]); | ||
|
||
patchState( | ||
store, | ||
addEntities([user1, user2, user3], { collection: 'user' }) | ||
); | ||
expect(store.userEntityMap()).toEqual({ 1: user1, 3: user3, 2: user2 }); | ||
expect(store.userIds()).toEqual([1, 3, 2]); | ||
expect(store.userEntities()).toEqual([user1, user3, user2]); | ||
}); | ||
|
||
it('adds entities with the specified idKey if they do not exist', () => { | ||
const Store = signalStore(withEntities<Todo>()); | ||
const store = new Store(); | ||
|
||
patchState(store, addEntities([todo2, todo3], { idKey: '_id' })); | ||
|
||
expect(store.entityMap()).toEqual({ y: todo2, z: todo3 }); | ||
expect(store.ids()).toEqual(['y', 'z']); | ||
expect(store.entities()).toEqual([todo2, todo3]); | ||
|
||
patchState( | ||
store, | ||
addEntities([todo1], { idKey: '_id' }), | ||
addEntities([] as Todo[], { idKey: '_id' }) | ||
); | ||
|
||
expect(store.entityMap()).toEqual({ y: todo2, z: todo3, x: todo1 }); | ||
expect(store.ids()).toEqual(['y', 'z', 'x']); | ||
expect(store.entities()).toEqual([todo2, todo3, todo1]); | ||
}); | ||
|
||
it('does not add entities with the specified idKey if they already exist', () => { | ||
const Store = signalStore(withEntities<Todo>()); | ||
const store = new Store(); | ||
|
||
patchState( | ||
store, | ||
addEntities([todo1], { idKey: '_id' }), | ||
addEntities([todo2, todo1], { idKey: '_id' }), | ||
addEntities([] as Todo[], { idKey: '_id' }) | ||
); | ||
|
||
const entityMap = store.entityMap(); | ||
const ids = store.ids(); | ||
const entities = store.entities(); | ||
|
||
patchState( | ||
store, | ||
addEntities([] as Todo[], { idKey: '_id' }), | ||
addEntities([todo2, { ...todo2, text: 'NgRx' }, todo1], { idKey: '_id' }) | ||
); | ||
|
||
expect(store.entityMap()).toBe(entityMap); | ||
expect(store.ids()).toBe(ids); | ||
expect(store.entities()).toBe(entities); | ||
expect(store.entityMap()).toEqual({ x: todo1, y: todo2 }); | ||
expect(store.ids()).toEqual(['x', 'y']); | ||
expect(store.entities()).toEqual([todo1, todo2]); | ||
|
||
patchState(store, addEntities([todo1, todo3, todo2], { idKey: '_id' })); | ||
|
||
expect(store.entityMap()).toEqual({ x: todo1, y: todo2, z: todo3 }); | ||
expect(store.ids()).toEqual(['x', 'y', 'z']); | ||
expect(store.entities()).toEqual([todo1, todo2, todo3]); | ||
}); | ||
|
||
it('adds entities with the specified idKey to the specified collection if they do not exist', () => { | ||
const Store = signalStore( | ||
withEntities({ | ||
entity: type<Todo>(), | ||
collection: 'todo', | ||
}) | ||
); | ||
const store = new Store(); | ||
|
||
patchState( | ||
store, | ||
addEntities([todo3, todo2], { | ||
collection: 'todo', | ||
idKey: '_id', | ||
}) | ||
); | ||
|
||
expect(store.todoEntityMap()).toEqual({ z: todo3, y: todo2 }); | ||
expect(store.todoIds()).toEqual(['z', 'y']); | ||
expect(store.todoEntities()).toEqual([todo3, todo2]); | ||
|
||
patchState( | ||
store, | ||
addEntities([todo1], { collection: 'todo', idKey: '_id' }), | ||
addEntities([] as Todo[], { collection: 'todo', idKey: '_id' }) | ||
); | ||
|
||
expect(store.todoEntityMap()).toEqual({ z: todo3, y: todo2, x: todo1 }); | ||
expect(store.todoIds()).toEqual(['z', 'y', 'x']); | ||
expect(store.todoEntities()).toEqual([todo3, todo2, todo1]); | ||
}); | ||
|
||
it('does not add entities with the specified idKey to the specified collection if they already exist', () => { | ||
const todoMeta = { | ||
entity: type<Todo>(), | ||
collection: 'todo', | ||
idKey: '_id', | ||
} as const; | ||
|
||
const Store = signalStore(withEntities(todoMeta)); | ||
const store = new Store(); | ||
|
||
patchState( | ||
store, | ||
addEntities([todo2, { ...todo2, text: 'NgRx' }, todo3, todo2], todoMeta) | ||
); | ||
|
||
const todoEntityMap = store.todoEntityMap(); | ||
const todoIds = store.todoIds(); | ||
const todoEntities = store.todoEntities(); | ||
|
||
patchState( | ||
store, | ||
addEntities([] as Todo[], todoMeta), | ||
addEntities([todo3, todo2, { ...todo3, text: 'NgRx' }], todoMeta) | ||
); | ||
|
||
expect(store.todoEntityMap()).toBe(todoEntityMap); | ||
expect(store.todoIds()).toBe(todoIds); | ||
expect(store.todoEntities()).toBe(todoEntities); | ||
expect(store.todoEntityMap()).toEqual({ y: todo2, z: todo3 }); | ||
expect(store.todoIds()).toEqual(['y', 'z']); | ||
expect(store.todoEntities()).toEqual([todo2, todo3]); | ||
|
||
patchState( | ||
store, | ||
addEntities([todo1, todo2, todo3], todoMeta), | ||
addEntities([todo2, todo3, todo1], todoMeta) | ||
); | ||
|
||
expect(store.todoEntityMap()).toEqual({ y: todo2, z: todo3, x: todo1 }); | ||
expect(store.todoIds()).toEqual(['y', 'z', 'x']); | ||
expect(store.todoEntities()).toEqual([todo2, todo3, todo1]); | ||
}); | ||
}); |
Oops, something went wrong.