sforkowany z mirror/soapbox
				
			EntityStore: add tests for reducer, improve types, ensure error gets added to state
							rodzic
							
								
									b93a299009
								
							
						
					
					
						commit
						9df2bb4a86
					
				| 
						 | 
				
			
			@ -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<TestEntity>;
 | 
			
		||||
 | 
			
		||||
  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<TestEntity>;
 | 
			
		||||
 | 
			
		||||
  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<TestEntity>;
 | 
			
		||||
 | 
			
		||||
  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<TestEntity>;
 | 
			
		||||
 | 
			
		||||
  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);
 | 
			
		||||
});
 | 
			
		||||
| 
						 | 
				
			
			@ -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<State> = {}, 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;
 | 
			
		||||
  }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -5,8 +5,8 @@ interface Entity {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
/** Store of entities by ID. */
 | 
			
		||||
interface EntityStore {
 | 
			
		||||
  [id: string]: Entity | undefined
 | 
			
		||||
interface EntityStore<TEntity extends Entity = Entity> {
 | 
			
		||||
  [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<TEntity extends Entity = Entity> {
 | 
			
		||||
  /** Map of entities of this type. */
 | 
			
		||||
  store: EntityStore
 | 
			
		||||
  store: EntityStore<TEntity>
 | 
			
		||||
  /** Lists of entity IDs for a particular purpose. */
 | 
			
		||||
  lists: {
 | 
			
		||||
    [listKey: string]: EntityList | undefined
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Ładowanie…
	
		Reference in New Issue