| 1 | import type { EntityAdapter } from '../index'
|
|---|
| 2 | import { createEntityAdapter } from '../index'
|
|---|
| 3 | import type { PayloadAction } from '../../createAction'
|
|---|
| 4 | import { createAction } from '../../createAction'
|
|---|
| 5 | import { createSlice } from '../../createSlice'
|
|---|
| 6 | import type { BookModel } from './fixtures/book'
|
|---|
| 7 |
|
|---|
| 8 | describe('Entity State', () => {
|
|---|
| 9 | let adapter: EntityAdapter<BookModel, string>
|
|---|
| 10 |
|
|---|
| 11 | beforeEach(() => {
|
|---|
| 12 | adapter = createEntityAdapter({
|
|---|
| 13 | selectId: (book: BookModel) => book.id,
|
|---|
| 14 | })
|
|---|
| 15 | })
|
|---|
| 16 |
|
|---|
| 17 | it('should let you get the initial state', () => {
|
|---|
| 18 | const initialState = adapter.getInitialState()
|
|---|
| 19 |
|
|---|
| 20 | expect(initialState).toEqual({
|
|---|
| 21 | ids: [],
|
|---|
| 22 | entities: {},
|
|---|
| 23 | })
|
|---|
| 24 | })
|
|---|
| 25 |
|
|---|
| 26 | it('should let you provide additional initial state properties', () => {
|
|---|
| 27 | const additionalProperties = { isHydrated: true }
|
|---|
| 28 |
|
|---|
| 29 | const initialState = adapter.getInitialState(additionalProperties)
|
|---|
| 30 |
|
|---|
| 31 | expect(initialState).toEqual({
|
|---|
| 32 | ...additionalProperties,
|
|---|
| 33 | ids: [],
|
|---|
| 34 | entities: {},
|
|---|
| 35 | })
|
|---|
| 36 | })
|
|---|
| 37 |
|
|---|
| 38 | it('should let you provide initial entities', () => {
|
|---|
| 39 | const book1: BookModel = { id: 'a', title: 'First' }
|
|---|
| 40 |
|
|---|
| 41 | const initialState = adapter.getInitialState(undefined, [book1])
|
|---|
| 42 |
|
|---|
| 43 | expect(initialState).toEqual({
|
|---|
| 44 | ids: [book1.id],
|
|---|
| 45 | entities: { [book1.id]: book1 },
|
|---|
| 46 | })
|
|---|
| 47 |
|
|---|
| 48 | const additionalProperties = { isHydrated: true }
|
|---|
| 49 |
|
|---|
| 50 | const initialState2 = adapter.getInitialState(additionalProperties, [book1])
|
|---|
| 51 |
|
|---|
| 52 | expect(initialState2).toEqual({
|
|---|
| 53 | ...additionalProperties,
|
|---|
| 54 | ids: [book1.id],
|
|---|
| 55 | entities: { [book1.id]: book1 },
|
|---|
| 56 | })
|
|---|
| 57 | })
|
|---|
| 58 |
|
|---|
| 59 | it('should allow methods to be passed as reducers', () => {
|
|---|
| 60 | const upsertBook = createAction<BookModel>('otherBooks/upsert')
|
|---|
| 61 |
|
|---|
| 62 | const booksSlice = createSlice({
|
|---|
| 63 | name: 'books',
|
|---|
| 64 | initialState: adapter.getInitialState(),
|
|---|
| 65 | reducers: {
|
|---|
| 66 | addOne: adapter.addOne,
|
|---|
| 67 | removeOne(state, action: PayloadAction<string>) {
|
|---|
| 68 | // TODO The nested `produce` calls don't mutate `state` here as I would have expected.
|
|---|
| 69 | // TODO (note that `state` here is actually an Immer Draft<S>, from `createReducer`)
|
|---|
| 70 | // TODO However, this works if we _return_ the new plain result value instead
|
|---|
| 71 | // TODO See https://github.com/immerjs/immer/issues/533
|
|---|
| 72 | const result = adapter.removeOne(state, action)
|
|---|
| 73 | return result
|
|---|
| 74 | },
|
|---|
| 75 | },
|
|---|
| 76 | extraReducers: (builder) => {
|
|---|
| 77 | builder.addCase(upsertBook, (state, action) => {
|
|---|
| 78 | return adapter.upsertOne(state, action)
|
|---|
| 79 | })
|
|---|
| 80 | },
|
|---|
| 81 | })
|
|---|
| 82 |
|
|---|
| 83 | const { addOne, removeOne } = booksSlice.actions
|
|---|
| 84 | const { reducer } = booksSlice
|
|---|
| 85 |
|
|---|
| 86 | const selectors = adapter.getSelectors()
|
|---|
| 87 |
|
|---|
| 88 | const book1: BookModel = { id: 'a', title: 'First' }
|
|---|
| 89 | const book1a: BookModel = { id: 'a', title: 'Second' }
|
|---|
| 90 |
|
|---|
| 91 | const afterAddOne = reducer(undefined, addOne(book1))
|
|---|
| 92 | expect(afterAddOne.entities[book1.id]).toBe(book1)
|
|---|
| 93 |
|
|---|
| 94 | const afterRemoveOne = reducer(afterAddOne, removeOne(book1.id))
|
|---|
| 95 | expect(afterRemoveOne.entities[book1.id]).toBeUndefined()
|
|---|
| 96 | expect(selectors.selectTotal(afterRemoveOne)).toBe(0)
|
|---|
| 97 |
|
|---|
| 98 | const afterUpsertFirst = reducer(afterRemoveOne, upsertBook(book1))
|
|---|
| 99 | const afterUpsertSecond = reducer(afterUpsertFirst, upsertBook(book1a))
|
|---|
| 100 |
|
|---|
| 101 | expect(afterUpsertSecond.entities[book1.id]).toEqual(book1a)
|
|---|
| 102 | expect(selectors.selectTotal(afterUpsertSecond)).toBe(1)
|
|---|
| 103 | })
|
|---|
| 104 | })
|
|---|