diff --git a/app/soapbox/entity-store/__tests__/reducer.test.ts b/app/soapbox/entity-store/__tests__/reducer.test.ts new file mode 100644 index 0000000000..0da25f25e5 --- /dev/null +++ b/app/soapbox/entity-store/__tests__/reducer.test.ts @@ -0,0 +1,79 @@ +import { entitiesFetchFail, entitiesFetchRequest, importEntities } from '../actions'; +import reducer from '../reducer'; + +import type { EntityCache } from '../types'; + +interface TestEntity { + id: string + msg: string +} + +test('import entities', () => { + const entities: TestEntity[] = [ + { id: '1', msg: 'yolo' }, + { id: '2', msg: 'benis' }, + { id: '3', msg: 'boop' }, + ]; + + const action = importEntities(entities, 'TestEntity'); + const result = reducer(undefined, action); + const cache = result.TestEntity as EntityCache; + + expect(cache.store['1']!.msg).toBe('yolo'); + expect(Object.values(cache.lists).length).toBe(0); +}); + +test('import entities into a list', () => { + const entities: TestEntity[] = [ + { id: '1', msg: 'yolo' }, + { id: '2', msg: 'benis' }, + { id: '3', msg: 'boop' }, + ]; + + const action = importEntities(entities, 'TestEntity', 'thingies'); + const result = reducer(undefined, action); + const cache = result.TestEntity as EntityCache; + + expect(cache.store['2']!.msg).toBe('benis'); + expect(cache.lists.thingies?.ids.size).toBe(3); + + // Now try adding an additional item. + const entities2: TestEntity[] = [ + { id: '4', msg: 'hehe' }, + ]; + + const action2 = importEntities(entities2, 'TestEntity', 'thingies'); + const result2 = reducer(result, action2); + const cache2 = result2.TestEntity as EntityCache; + + expect(cache2.store['4']!.msg).toBe('hehe'); + expect(cache2.lists.thingies?.ids.size).toBe(4); + + // Finally, update an item. + const entities3: TestEntity[] = [ + { id: '2', msg: 'yolofam' }, + ]; + + const action3 = importEntities(entities3, 'TestEntity', 'thingies'); + const result3 = reducer(result2, action3); + const cache3 = result3.TestEntity as EntityCache; + + expect(cache3.store['2']!.msg).toBe('yolofam'); + expect(cache3.lists.thingies?.ids.size).toBe(4); // unchanged +}); + +test('fetching updates the list state', () => { + const action = entitiesFetchRequest('TestEntity', 'thingies'); + const result = reducer(undefined, action); + + expect(result.TestEntity!.lists.thingies!.state.fetching).toBe(true); +}); + +test('failure adds the error to the state', () => { + const error = new Error('whoopsie'); + + const action = entitiesFetchFail('TestEntity', 'thingies', error); + const result = reducer(undefined, action); + + expect(result.TestEntity!.lists.thingies!.state.error).toBe(error); +}); \ No newline at end of file diff --git a/app/soapbox/entity-store/reducer.ts b/app/soapbox/entity-store/reducer.ts index f2af680a18..bb1dcdd1d7 100644 --- a/app/soapbox/entity-store/reducer.ts +++ b/app/soapbox/entity-store/reducer.ts @@ -48,6 +48,7 @@ const setFetching = ( entityType: string, listKey: string | undefined, isFetching: boolean, + error?: any, ) => { return produce(state, draft => { const cache = draft[entityType] ?? createCache(); @@ -55,6 +56,7 @@ const setFetching = ( if (typeof listKey === 'string') { const list = cache.lists[listKey] ?? createList(); list.state.fetching = isFetching; + list.state.error = error; cache.lists[listKey] = list; } @@ -72,7 +74,7 @@ function reducer(state: Readonly = {}, action: EntityAction): State { case ENTITIES_FETCH_REQUEST: return setFetching(state, action.entityType, action.listKey, true); case ENTITIES_FETCH_FAIL: - return setFetching(state, action.entityType, action.listKey, false); + return setFetching(state, action.entityType, action.listKey, false, action.error); default: return state; } diff --git a/app/soapbox/entity-store/types.ts b/app/soapbox/entity-store/types.ts index efec97df13..eb2a306a09 100644 --- a/app/soapbox/entity-store/types.ts +++ b/app/soapbox/entity-store/types.ts @@ -5,8 +5,8 @@ interface Entity { } /** Store of entities by ID. */ -interface EntityStore { - [id: string]: Entity | undefined +interface EntityStore { + [id: string]: TEntity | undefined } /** List of entity IDs and fetch state. */ @@ -32,9 +32,9 @@ interface EntityListState { } /** Cache data pertaining to a paritcular entity type.. */ -interface EntityCache { +interface EntityCache { /** Map of entities of this type. */ - store: EntityStore + store: EntityStore /** Lists of entity IDs for a particular purpose. */ lists: { [listKey: string]: EntityList | undefined