UNPKG

@gjv/redux-slice-factory

Version:

Generic factory functions for common slice data structures

293 lines (251 loc) 10.9 kB
import { describe, beforeEach, it, expect } from '@jest/globals' import { miniSerializeError, SerializedError } from '@reduxjs/toolkit' import StatusEnum from '../../constants/StatusEnum' import EntityState, { IEntityState } from '../../models/entity-state' import { getISOString } from '../../utilities' import createEntitySlice, { IEntitySlice } from './CreateEntitySlice' enum UsersSliceStatusEnum { Settled = 'Settled', Requesting = 'Requesting', Failed = 'Failed', Saving = 'Saving', Saved = 'Saved', } interface ITestUserModel { id: string; name: string; age: string; } const alice: ITestUserModel = { id: '123abc', name: 'alice', age: '25', } const bob: ITestUserModel = { id: '456def', name: 'bob', age: '30', } const carl: ITestUserModel = { id: '789ghi', name: 'carl', age: '35', } type SliceState = IEntityState<ITestUserModel, keyof typeof UsersSliceStatusEnum, SerializedError> type IGlobalState = { FooBarThing: SliceState } type EntitySlice = IEntitySlice<IGlobalState, ITestUserModel, keyof typeof UsersSliceStatusEnum, SerializedError> describe('createEntitySlice', () => { const testName = 'FooBarThing' let sliceState: SliceState let slice: EntitySlice // eslint-disable-next-line no-void void beforeEach(() => { sliceState = EntityState.create<ITestUserModel>() slice = createEntitySlice<IGlobalState, ITestUserModel, keyof typeof UsersSliceStatusEnum, SerializedError>({ name: testName, selectSliceState: () => sliceState, selectId: (model) => model.id, sortComparer: (a, b) => a.name.localeCompare(b.name), }) }) it('initializes', () => { expect(slice.name).toEqual(testName) expect(typeof slice.reducer).toEqual('function') expect(Object.values(slice.actions)).toHaveLength(17) expect(Object.values(slice.selectors)).toHaveLength(12) }) it('does not affect state with unregistered action types', () => { // GIVEN const previousState = sliceState // WHEN const nextState = slice.reducer(sliceState, { type: 'not_an_action' }) // THEN expect(nextState).toEqual(previousState) }) it('sets error property in state', () => { // GIVEN const previousState = sliceState const error = new Error('Whoops.') // WHEN const nextState = slice.reducer(sliceState, slice.actions.setError(miniSerializeError(error))) // THEN expect(nextState.ids).toEqual(previousState.ids) // should be unaffected expect(nextState.entities).toEqual(previousState.entities) // should be unaffected expect(nextState.error).toEqual(miniSerializeError(error)) expect(nextState.status).toEqual(previousState.status) // should be unaffected expect(nextState.lastModified).toEqual(previousState.lastModified) // should be unaffected expect(nextState.lastHydrated).toEqual(previousState.lastHydrated) // should be unaffected }) it('sets status property in state', () => { // GIVEN const previousState = sliceState const status = StatusEnum.Requesting // WHEN const nextState = slice.reducer(sliceState, slice.actions.setStatus(status)) // THEN expect(nextState.ids).toEqual(previousState.ids) // should be unaffected expect(nextState.entities).toEqual(previousState.entities) // should be unaffected expect(nextState.error).toEqual(previousState.error) // should be unaffected expect(nextState.status).toEqual(status) expect(nextState.lastModified).toEqual(previousState.lastModified) // should be unaffected expect(nextState.lastHydrated).toEqual(previousState.lastHydrated) // should be unaffected }) it('sets status property in state to custom value', () => { // GIVEN const previousState = sliceState const status = UsersSliceStatusEnum.Saving // WHEN const nextState = slice.reducer(sliceState, slice.actions.setStatus(status)) // THEN expect(nextState.ids).toEqual(previousState.ids) // should be unaffected expect(nextState.entities).toEqual(previousState.entities) // should be unaffected expect(nextState.error).toEqual(previousState.error) // should be unaffected expect(nextState.status).toEqual(status) expect(nextState.lastModified).toEqual(previousState.lastModified) // should be unaffected expect(nextState.lastHydrated).toEqual(previousState.lastHydrated) // should be unaffected }) it('hydrates state with data', () => { // GIVEN const previousData = { [alice.id]: alice, [bob.id]: { id: bob.id, name: 'bobby', age: bob.age, }, } const previousStateWithData = EntityState.create({ ...sliceState, ids: Object.keys(previousData), entities: previousData, }) const data = { [alice.id]: alice, [bob.id]: bob, [carl.id]: carl, } // WHEN const nextState = slice.reducer(previousStateWithData, slice.actions.hydrateAll(data)) // THEN expect(nextState.ids).toEqual(Object.keys(data)) expect(nextState.entities).toEqual(data) expect(nextState.error).toEqual(previousStateWithData.error) // should be unaffected expect(nextState.status).toEqual(previousStateWithData.status) // should be unaffected expect(nextState.lastModified).toEqual(null) expect(nextState.lastHydrated).toBeTruthy() }) it('modifies state with data', () => { // GIVEN const previousData = { [alice.id]: alice, [bob.id]: { id: bob.id, name: 'bobby', age: bob.age, }, } const previousStateWithData = EntityState.create({ ...sliceState, ids: Object.keys(previousData), entities: previousData, }) const data = { [alice.id]: alice, [bob.id]: bob, [carl.id]: carl, } // WHEN const nextState = slice.reducer(previousStateWithData, slice.actions.setAll(data)) // THEN expect(nextState.ids).toEqual(Object.keys(data)) expect(nextState.entities).toEqual(data) expect(nextState.error).toEqual(previousStateWithData.error) // should be unaffected expect(nextState.status).toEqual(previousStateWithData.status) // should be unaffected expect(nextState.lastModified).toBeTruthy() expect(nextState.lastHydrated).toEqual(previousStateWithData.lastHydrated) // should be unaffected }) it('resets state', () => { // GIVEN const initialState = sliceState const previousData = { [alice.id]: alice, [bob.id]: { id: bob.id, name: 'bobby', age: bob.age, }, } const previousStateWithData = EntityState.create({ ids: Object.keys(previousData), entities: previousData, status: StatusEnum.Failed, error: new Error('Whoops.'), lastHydrated: getISOString(), lastModified: getISOString(), }) // WHEN const nextState = slice.reducer(previousStateWithData, slice.actions.reset()) // THEN expect(nextState).toEqual(initialState) expect(nextState.ids).toEqual(initialState.ids) expect(nextState.entities).toEqual(initialState.entities) expect(nextState.error).toEqual(initialState.error) expect(nextState.status).toEqual(initialState.status) expect(nextState.lastModified).toEqual(initialState.lastModified) expect(nextState.lastHydrated).toEqual(initialState.lastHydrated) }) it('sets meta state to request', () => { // GIVEN const previousState = sliceState const status = StatusEnum.Requesting // WHEN const nextState = slice.reducer(previousState, slice.actions.setMetaState({ status: status, })) // THEN expect(nextState.ids).toEqual(previousState.ids) // should be unaffected expect(nextState.entities).toEqual(previousState.entities) // should be unaffected expect(nextState.error).toEqual(previousState.error) // should be unaffected expect(nextState.status).toEqual(status) expect(nextState.lastModified).toEqual(previousState.lastModified) // should be unaffected expect(nextState.lastHydrated).toEqual(previousState.lastHydrated) // should be unaffected }) it('sets meta state to succeed', () => { // GIVEN const previousState = sliceState const status = StatusEnum.Settled const error = null // WHEN const nextState = slice.reducer(previousState, slice.actions.setMetaState({ status: status, error: error, })) // THEN expect(nextState.ids).toEqual(previousState.ids) // should be unaffected expect(nextState.entities).toEqual(previousState.entities) // should be unaffected expect(nextState.error).toEqual(error) expect(nextState.status).toEqual(status) expect(nextState.lastModified).toEqual(previousState.lastModified) // should be unaffected expect(nextState.lastHydrated).toEqual(previousState.lastHydrated) // should be unaffected }) it('sets meta state to fail', () => { // GIVEN const previousState = sliceState const status = StatusEnum.Failed const error = new Error('Whoops.') // WHEN const nextState = slice.reducer(previousState, slice.actions.setMetaState({ status: status, error: miniSerializeError(error), })) // THEN expect(nextState.ids).toEqual(previousState.ids) // should be unaffected expect(nextState.entities).toEqual(previousState.entities) // should be unaffected expect(nextState.error).toEqual(miniSerializeError(error)) expect(nextState.status).toEqual(status) expect(nextState.lastModified).toEqual(previousState.lastModified) // should be unaffected expect(nextState.lastHydrated).toEqual(previousState.lastHydrated) // should be unaffected }) })