| 1 | import { createDraftSafeSelectorCreator } from '../../createDraftSafeSelector'
|
|---|
| 2 | import type { EntityAdapter, EntityState } from '../index'
|
|---|
| 3 | import { createEntityAdapter } from '../index'
|
|---|
| 4 | import type { EntitySelectors } from '../models'
|
|---|
| 5 | import type { BookModel } from './fixtures/book'
|
|---|
| 6 | import { AClockworkOrange, AnimalFarm, TheGreatGatsby } from './fixtures/book'
|
|---|
| 7 | import type { Selector } from 'reselect'
|
|---|
| 8 | import { createSelector, weakMapMemoize } from 'reselect'
|
|---|
| 9 | import { vi } from 'vitest'
|
|---|
| 10 |
|
|---|
| 11 | describe('Entity State Selectors', () => {
|
|---|
| 12 | describe('Composed Selectors', () => {
|
|---|
| 13 | interface State {
|
|---|
| 14 | books: EntityState<BookModel, string>
|
|---|
| 15 | }
|
|---|
| 16 |
|
|---|
| 17 | let adapter: EntityAdapter<BookModel, string>
|
|---|
| 18 | let selectors: EntitySelectors<BookModel, State, string>
|
|---|
| 19 | let state: State
|
|---|
| 20 |
|
|---|
| 21 | beforeEach(() => {
|
|---|
| 22 | adapter = createEntityAdapter({
|
|---|
| 23 | selectId: (book: BookModel) => book.id,
|
|---|
| 24 | })
|
|---|
| 25 |
|
|---|
| 26 | state = {
|
|---|
| 27 | books: adapter.setAll(adapter.getInitialState(), [
|
|---|
| 28 | AClockworkOrange,
|
|---|
| 29 | AnimalFarm,
|
|---|
| 30 | TheGreatGatsby,
|
|---|
| 31 | ]),
|
|---|
| 32 | }
|
|---|
| 33 |
|
|---|
| 34 | selectors = adapter.getSelectors((state: State) => state.books)
|
|---|
| 35 | })
|
|---|
| 36 |
|
|---|
| 37 | it('should create a selector for selecting the ids', () => {
|
|---|
| 38 | const ids = selectors.selectIds(state)
|
|---|
| 39 |
|
|---|
| 40 | expect(ids).toEqual(state.books.ids)
|
|---|
| 41 | })
|
|---|
| 42 |
|
|---|
| 43 | it('should create a selector for selecting the entities', () => {
|
|---|
| 44 | const entities = selectors.selectEntities(state)
|
|---|
| 45 |
|
|---|
| 46 | expect(entities).toEqual(state.books.entities)
|
|---|
| 47 | })
|
|---|
| 48 |
|
|---|
| 49 | it('should create a selector for selecting the list of models', () => {
|
|---|
| 50 | const models = selectors.selectAll(state)
|
|---|
| 51 |
|
|---|
| 52 | expect(models).toEqual([AClockworkOrange, AnimalFarm, TheGreatGatsby])
|
|---|
| 53 | })
|
|---|
| 54 |
|
|---|
| 55 | it('should create a selector for selecting the count of models', () => {
|
|---|
| 56 | const total = selectors.selectTotal(state)
|
|---|
| 57 |
|
|---|
| 58 | expect(total).toEqual(3)
|
|---|
| 59 | })
|
|---|
| 60 |
|
|---|
| 61 | it('should create a selector for selecting a single item by ID', () => {
|
|---|
| 62 | const first = selectors.selectById(state, AClockworkOrange.id)
|
|---|
| 63 | expect(first).toBe(AClockworkOrange)
|
|---|
| 64 | const second = selectors.selectById(state, AnimalFarm.id)
|
|---|
| 65 | expect(second).toBe(AnimalFarm)
|
|---|
| 66 | })
|
|---|
| 67 | })
|
|---|
| 68 |
|
|---|
| 69 | describe('Uncomposed Selectors', () => {
|
|---|
| 70 | type State = EntityState<BookModel, string>
|
|---|
| 71 |
|
|---|
| 72 | let adapter: EntityAdapter<BookModel, string>
|
|---|
| 73 | let selectors: EntitySelectors<
|
|---|
| 74 | BookModel,
|
|---|
| 75 | EntityState<BookModel, string>,
|
|---|
| 76 | string
|
|---|
| 77 | >
|
|---|
| 78 | let state: State
|
|---|
| 79 |
|
|---|
| 80 | beforeEach(() => {
|
|---|
| 81 | adapter = createEntityAdapter({
|
|---|
| 82 | selectId: (book: BookModel) => book.id,
|
|---|
| 83 | })
|
|---|
| 84 |
|
|---|
| 85 | state = adapter.setAll(adapter.getInitialState(), [
|
|---|
| 86 | AClockworkOrange,
|
|---|
| 87 | AnimalFarm,
|
|---|
| 88 | TheGreatGatsby,
|
|---|
| 89 | ])
|
|---|
| 90 |
|
|---|
| 91 | selectors = adapter.getSelectors()
|
|---|
| 92 | })
|
|---|
| 93 |
|
|---|
| 94 | it('should create a selector for selecting the ids', () => {
|
|---|
| 95 | const ids = selectors.selectIds(state)
|
|---|
| 96 |
|
|---|
| 97 | expect(ids).toEqual(state.ids)
|
|---|
| 98 | })
|
|---|
| 99 |
|
|---|
| 100 | it('should create a selector for selecting the entities', () => {
|
|---|
| 101 | const entities = selectors.selectEntities(state)
|
|---|
| 102 |
|
|---|
| 103 | expect(entities).toEqual(state.entities)
|
|---|
| 104 | })
|
|---|
| 105 |
|
|---|
| 106 | it('should type single entity from Dictionary as entity type or undefined', () => {
|
|---|
| 107 | expectType<
|
|---|
| 108 | Selector<EntityState<BookModel, string>, BookModel | undefined>
|
|---|
| 109 | >(createSelector(selectors.selectEntities, (entities) => entities[0]))
|
|---|
| 110 | })
|
|---|
| 111 |
|
|---|
| 112 | it('should create a selector for selecting the list of models', () => {
|
|---|
| 113 | const models = selectors.selectAll(state)
|
|---|
| 114 |
|
|---|
| 115 | expect(models).toEqual([AClockworkOrange, AnimalFarm, TheGreatGatsby])
|
|---|
| 116 | })
|
|---|
| 117 |
|
|---|
| 118 | it('should create a selector for selecting the count of models', () => {
|
|---|
| 119 | const total = selectors.selectTotal(state)
|
|---|
| 120 |
|
|---|
| 121 | expect(total).toEqual(3)
|
|---|
| 122 | })
|
|---|
| 123 |
|
|---|
| 124 | it('should create a selector for selecting a single item by ID', () => {
|
|---|
| 125 | const first = selectors.selectById(state, AClockworkOrange.id)
|
|---|
| 126 | expect(first).toBe(AClockworkOrange)
|
|---|
| 127 | const second = selectors.selectById(state, AnimalFarm.id)
|
|---|
| 128 | expect(second).toBe(AnimalFarm)
|
|---|
| 129 | })
|
|---|
| 130 | })
|
|---|
| 131 | describe('custom createSelector instance', () => {
|
|---|
| 132 | it('should use the custom createSelector function if provided', () => {
|
|---|
| 133 | const memoizeSpy = vi.fn(
|
|---|
| 134 | // test that we're allowed to pass memoizers with different options, as long as they're optional
|
|---|
| 135 | <F extends (...args: any[]) => any>(fn: F, param?: boolean) => fn,
|
|---|
| 136 | )
|
|---|
| 137 | const createCustomSelector = createDraftSafeSelectorCreator(memoizeSpy)
|
|---|
| 138 |
|
|---|
| 139 | const adapter = createEntityAdapter({
|
|---|
| 140 | selectId: (book: BookModel) => book.id,
|
|---|
| 141 | })
|
|---|
| 142 |
|
|---|
| 143 | adapter.getSelectors(undefined, { createSelector: createCustomSelector })
|
|---|
| 144 |
|
|---|
| 145 | expect(memoizeSpy).toHaveBeenCalled()
|
|---|
| 146 |
|
|---|
| 147 | memoizeSpy.mockClear()
|
|---|
| 148 | })
|
|---|
| 149 | })
|
|---|
| 150 | })
|
|---|
| 151 |
|
|---|
| 152 | function expectType<T>(t: T) {
|
|---|
| 153 | return t
|
|---|
| 154 | }
|
|---|