diff --git a/packages/enty/.eslintrc.js b/packages/enty/.eslintrc.js new file mode 100644 index 00000000..797555a3 --- /dev/null +++ b/packages/enty/.eslintrc.js @@ -0,0 +1,5 @@ +module.exports = { + rules: { + 'prettier/prettier': 0 + } +}; diff --git a/packages/react-enty/src/EntityApi.ts b/packages/react-enty/src/EntityApi.ts index 3fa15765..0df9b96d 100644 --- a/packages/react-enty/src/EntityApi.ts +++ b/packages/react-enty/src/EntityApi.ts @@ -16,7 +16,7 @@ export type Request = { type RequestFunction = (variables?: any, meta?: any) => Promise | AsyncIterator; type MappedTransform = { - [K in keyof T]: T[K] extends RequestFunction + [K in keyof T]: T[K] extends SideEffect ? Request< Awaited>, Parameters[0] extends undefined ? void : Parameters[0] diff --git a/packages/react-enty/src/__tests__/EntityApi-test.tsx b/packages/react-enty/src/__tests__/EntityApi-test.tsx index e4644253..f4724fbf 100644 --- a/packages/react-enty/src/__tests__/EntityApi-test.tsx +++ b/packages/react-enty/src/__tests__/EntityApi-test.tsx @@ -4,7 +4,6 @@ import {ObjectSchema} from 'enty'; import {useEffect} from 'react'; import {asyncUpdate, ExpectsMessage} from './RequestSuite'; import {mount} from 'enzyme'; -import Hash from '../util/Hash'; describe('exports', () => { const api = EntityApi( diff --git a/packages/react-enty/src/__tests__/EntityReducerFactory-test.ts b/packages/react-enty/src/__tests__/EntityReducerFactory-test.ts index 84d09d75..babfc056 100644 --- a/packages/react-enty/src/__tests__/EntityReducerFactory-test.ts +++ b/packages/react-enty/src/__tests__/EntityReducerFactory-test.ts @@ -10,7 +10,6 @@ import resetAction from '../api/resetAction'; // const baseState = { - baseSchema: new ObjectSchema({}), schemas: {}, response: {}, error: {}, @@ -67,7 +66,7 @@ test('EntityReducerFactory normalizes a reuslt', () => { const exampleReceiveAction = { type: 'ENTY_RECEIVE' as const, - meta: {responseKey: 'TEST'}, + meta: {responseKey: 'TEST', name: 'foo'}, payload: examplePayload }; @@ -80,7 +79,7 @@ describe('EntityReducer requestState', () => { const data = EntityReducer(null, { type: 'ENTY_FETCH' as const, payload: null, - meta: {responseKey: 'TEST'} + meta: {responseKey: 'TEST', name: 'foo'} }).requestState.TEST; expect(data.isFetching).toBe(true); }); @@ -92,7 +91,7 @@ describe('EntityReducer requestState', () => { requestState: {TEST: RequestState.fetching()}, response: {TEST: {}} }, - {type: 'ENTY_FETCH' as const, payload: null, meta: {responseKey: 'TEST'}} + {type: 'ENTY_FETCH' as const, payload: null, meta: {responseKey: 'TEST', name: 'foo'}} ).requestState.TEST; expect(data.isRefetching).toBe(true); }); @@ -101,12 +100,12 @@ describe('EntityReducer requestState', () => { let stateA = EntityReducer(null, { type: 'ENTY_FETCH' as const, payload: null, - meta: {responseKey: 'foo'} + meta: {responseKey: 'foo', name: 'foo'} }); let stateB = EntityReducer(stateA, { type: 'ENTY_FETCH' as const, payload: null, - meta: {responseKey: 'foo'} + meta: {responseKey: 'foo', name: 'foo'} }); expect(stateB.requestState.foo.isRefetching).toBe(undefined); expect(stateB.requestState.foo.isFetching).toBe(true); @@ -118,7 +117,7 @@ describe('EntityReducer requestState', () => { // @ts-ignore - intentionally bad types type: 'nothing', payload: null, - meta: {responseKey: 'nothing'} + meta: {responseKey: 'nothing', name: 'foo'} }).requestState.nothing ).toBeUndefined(); }); @@ -130,7 +129,7 @@ describe('EntityReducer requestState', () => { EntityReducer(null, { type: 'ENTY_ERROR', payload: 'errorPayload', - meta: {responseKey: 'TEST'} + meta: {responseKey: 'TEST', name: 'foo'} }).requestState.TEST.value() ).toBe('errorPayload'); }); @@ -138,11 +137,7 @@ describe('EntityReducer requestState', () => { describe('EntityReducer Config', () => { const action = (type: Action['type']) => - ({type, payload: null, meta: {responseKey: type}} as const); - test('the supplied schema is not mutated when reducing', () => { - // @ts-ignore - intentionally bad types - expect(EntityReducer(null, action('nothing')).baseSchema).toBe(schema); - }); + ({type, payload: null, meta: {responseKey: type, name: 'foo'}} as const); test('response starts with an empty object', () => { // @ts-ignore - intentionally bad types @@ -177,7 +172,7 @@ describe('EntityReducer Normalizing', () => { test('it will store normalized results on _result.responseKey', () => { const action = { type: 'ENTY_RECEIVE', - meta: {responseKey: 'TEST'}, + meta: {responseKey: 'TEST', name: 'foo'}, payload: { subreddit: { fullnameId: 'MK', @@ -192,7 +187,7 @@ describe('EntityReducer Normalizing', () => { test('it will store normalized data on _entities.type.id', () => { const action = { type: 'ENTY_RECEIVE', - meta: {responseKey: 'TEST'}, + meta: {responseKey: 'TEST', name: 'foo'}, payload: { subreddit: { fullnameId: 'MK', @@ -209,7 +204,7 @@ describe('EntityReducer Normalizing', () => { test('it will store deep entities', () => { const action = { type: 'ENTY_RECEIVE', - meta: {responseKey: 'TEST'}, + meta: {responseKey: 'TEST', name: 'foo'}, payload: { subreddit: { fullnameId: 'MK', @@ -227,7 +222,7 @@ describe('EntityReducer Normalizing', () => { const action = (payload: any) => ({ type: 'ENTY_RECEIVE', - meta: {responseKey: 'TEST'}, + meta: {responseKey: 'TEST', name: 'foo'}, payload } as const); @@ -286,7 +281,7 @@ describe('no schema reducer', () => { const stateA = reducer(null, { type: 'ENTY_RECEIVE', payload: 'FOO', - meta: {responseKey: '123'} + meta: {responseKey: '123', name: 'foo'} }); expect(stateA.entities).toEqual({}); @@ -297,7 +292,7 @@ describe('no schema reducer', () => { const stateB = reducer(stateA, { type: 'ENTY_RECEIVE', payload: 'BAR', - meta: {responseKey: '456'} + meta: {responseKey: '456', name: 'foo'} }); expect(stateB.response['123']).toBe('FOO'); expect(stateB.response['456']).toBe('BAR'); @@ -318,7 +313,7 @@ describe('remove entity', () => { const state = reducer(initialState, { type: 'ENTY_REMOVE' as const, payload: ['foo', 'bar'], - meta: {responseKey: ''} + meta: {responseKey: '', name: 'foo'} }); expect(state.entities.foo.bar).toBe(REMOVED_ENTITY); expect(state.entities.baz.qux).not.toBe(REMOVED_ENTITY); @@ -332,7 +327,7 @@ describe('ENTY_RESET', () => { const stateA = reducer(null, { type: 'ENTY_RECEIVE', payload: 'FOO', - meta: {responseKey: '123'} + meta: {responseKey: '123', name: 'foo'} }); expect(stateA.response['123']).toBe('FOO'); expect(stateA.requestState['123'].isSuccess).toBe(true); diff --git a/packages/react-enty/src/__tests__/ProviderFactory-test.tsx b/packages/react-enty/src/__tests__/ProviderFactory-test.tsx index 47ab345c..e4cd3fc4 100644 --- a/packages/react-enty/src/__tests__/ProviderFactory-test.tsx +++ b/packages/react-enty/src/__tests__/ProviderFactory-test.tsx @@ -39,7 +39,6 @@ describe('Component', () => { ); }).toMatchObject([ { - baseSchema: undefined, entities: {foo: 'component'}, error: {}, requestState: {}, @@ -63,8 +62,6 @@ describe('Component', () => { ); }).toMatchObject([ { - //baseMeta: {foo: 'bar'}, - baseSchema: undefined, error: {}, requestState: {}, response: {}, @@ -142,7 +139,6 @@ describe('Hoc', () => { ); }).toMatchObject([ { - baseSchema: undefined, entities: {foo: 'hoc'}, error: {}, requestState: {}, @@ -158,15 +154,12 @@ describe('Hoc', () => { describe('Debugging', () => { it('will log reducer state if debug prop is provided', () => { - const group = jest.spyOn(console, 'groupCollapsed').mockImplementation(() => {}); - const groupEnd = jest.spyOn(console, 'groupEnd').mockImplementation(() => {}); + const log = jest.spyOn(console, 'log').mockImplementation(() => {}); const {Provider} = ProviderFactory({}); - mount(); + mount(); - expect(group).toHaveBeenCalled(); - expect(groupEnd).toHaveBeenCalled(); + expect(log).toHaveBeenCalled(); - group.mockRestore(); - groupEnd.mockRestore(); + log.mockRestore(); }); }); diff --git a/packages/react-enty/src/__tests__/RequestHook-test.tsx b/packages/react-enty/src/__tests__/RequestHook-test.tsx index 7d3c5374..e2b15573 100644 --- a/packages/react-enty/src/__tests__/RequestHook-test.tsx +++ b/packages/react-enty/src/__tests__/RequestHook-test.tsx @@ -23,7 +23,6 @@ import { badEntity, bar, baz, - obs, entity } from './RequestSuite'; @@ -62,19 +61,6 @@ describe('config', () => { return null; }); }); - - it('request will return undefined for observables', async () => { - expect.assertions(1); - mountWithProvider(() => () => { - const message = obs.useRequest(); - useEffect(() => { - var pending = message.request(); - expect(pending).toBeUndefined(); - }, []); - - return null; - }); - }); }); describe('usage', () => { diff --git a/packages/react-enty/src/__tests__/RequestSuite.tsx b/packages/react-enty/src/__tests__/RequestSuite.tsx index ae8113ce..81dab6a7 100644 --- a/packages/react-enty/src/__tests__/RequestSuite.tsx +++ b/packages/react-enty/src/__tests__/RequestSuite.tsx @@ -12,23 +12,22 @@ import {mount} from 'enzyme'; function setupTests() { const fooEntity = new EntitySchema('foo'); - //const {Provider, foo, fooError, badEntity, bar, baz, obs, entity} = EntityApi( const api = EntityApi( { - foo: (data: string = 'foo') => Promise.resolve({data}), - entity: (foo: {id: string; name: string} = {id: '123', name: 'foo'}) => - Promise.resolve({foo}), - badEntity: (foo: {name: string} = {name: 'foo'}) => Promise.resolve({foo}), - fooError: () => Promise.reject('ouch!'), - bar: (data: string = 'bar') => Promise.resolve({data}), - baz: (data: string = 'requested-baz') => Promise.resolve({data}), - obs: () => ({subscribe: () => {}}) + foo: async (data: string = 'foo') => ({data}), + entity: async (foo: {id: string; name: string} = {id: '123', name: 'foo'}) => ({foo}), + badEntity: async (foo: {name: string} = {name: 'foo'}) => ({foo}), + fooError: async () => { + throw new Error('ouch!'); + }, + bar: async (data: string = 'bar') => ({data}), + baz: async (data: string = 'requested-baz') => ({data}) }, new ObjectSchema({ foo: fooEntity }) ); - const {Provider, foo, fooError, badEntity, bar, baz, obs, entity} = api; + const {Provider, foo, fooError, badEntity, bar, baz, entity} = api; function ExpectsMessage(props: { message: Message; @@ -73,15 +72,14 @@ function setupTests() { foo, bar, baz, - obs, fooError }; } -export const {mountWithProvider, foo, bar, baz, obs, badEntity, entity, fooError, ExpectsMessage} = +export const {mountWithProvider, foo, bar, baz, badEntity, entity, fooError, ExpectsMessage} = setupTests(); -export function asyncUpdate(wrapper: any) { +export async function asyncUpdate(wrapper: any) { return new Promise((resolve) => setTimeout(resolve, 0)).then(() => wrapper.update()); } @@ -145,7 +143,7 @@ export async function errorOnLoad(testFn: TestFn) { let wrapper = mountWithProvider(testFn); expect(wrapper).toBeFetching(); await asyncUpdate(wrapper); - expect(wrapper).toBeError('ouch!'); + expect(wrapper).toBeError(new Error('ouch!')); } export async function fetchBadEntity(testFn: TestFn) { diff --git a/packages/react-enty/src/api/__tests__/createRequestAction-test.ts b/packages/react-enty/src/api/__tests__/createRequestAction-test.ts index 63759bcc..7fe44cc8 100644 --- a/packages/react-enty/src/api/__tests__/createRequestAction-test.ts +++ b/packages/react-enty/src/api/__tests__/createRequestAction-test.ts @@ -1,7 +1,7 @@ import createRequestAction from '../createRequestAction'; const payload = 'PAYLOAD'; -const meta = {responseKey: 'foo'}; +const meta = {responseKey: 'foo', name: 'foo'}; describe('observable support', () => { const observable = (fn: any) => ({ @@ -118,7 +118,7 @@ describe('promise support', () => { const dispatch = jest.fn(); const getState = jest.fn(); const request = createRequestAction(() => Promise.reject('BORKD')); - const meta = {responseKey: '123'}; + const meta = {responseKey: '123', name: 'foo'}; try { await request(payload, meta)(dispatch, getState); } catch (e) { @@ -200,7 +200,7 @@ describe('async generators', () => { describe('general', () => { it('returns a function accepts payload/meta that returns a redux thunk', () => { const payloadFunction = createRequestAction(() => Promise.resolve()); - const thunk = payloadFunction('bar', {responseKey: '123'}); + const thunk = payloadFunction('bar', {responseKey: '123', name: 'foo'}); expect(typeof payloadFunction).toBe('function'); expect(typeof thunk).toBe('function'); @@ -212,11 +212,11 @@ describe('general', () => { const getState = jest.fn(); const payload = createRequestAction(async () => 'foo'); - const payloadA = payload('foo', {responseKey: '', returnResponse: true})( + const payloadA = payload('foo', {responseKey: '', name: 'foo', returnResponse: true})( dispatch, getState ); - const payloadB = payload('foo', {responseKey: '', returnResponse: false})( + const payloadB = payload('foo', {responseKey: '', name: 'foo', returnResponse: false})( dispatch, getState ); diff --git a/packages/react-enty/src/util/__tests__/useReducerThunk-test.ts b/packages/react-enty/src/util/__tests__/useReducerThunk-test.ts index 13c794ee..c7551041 100644 --- a/packages/react-enty/src/util/__tests__/useReducerThunk-test.ts +++ b/packages/react-enty/src/util/__tests__/useReducerThunk-test.ts @@ -3,7 +3,7 @@ import {act, renderHook} from '@testing-library/react-hooks'; import {ObjectSchema} from 'enty'; const action = (payload: T) => { - return {type: 'ENTY_RECEIVE', payload, meta: {responseKey: ''}} as const; + return {type: 'ENTY_RECEIVE', payload, meta: {name: 'foo', responseKey: ''}} as const; }; const initialState = { baseSchema: new ObjectSchema({}), diff --git a/packages/react-enty/yarn.lock b/packages/react-enty/yarn.lock index c98c7532..5cb2b066 100644 --- a/packages/react-enty/yarn.lock +++ b/packages/react-enty/yarn.lock @@ -2867,9 +2867,9 @@ caniuse-api@^3.0.0: lodash.uniq "^4.5.0" caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001219: - version "1.0.30001402" - resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001402.tgz" - integrity sha512-Mx4MlhXO5NwuvXGgVb+hg65HZ+bhUYsz8QtDGDo2QmaJS2GBX47Xfi2koL86lc8K+l+htXeTEB/Aeqvezoo6Ew== + version "1.0.30001546" + resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001546.tgz" + integrity sha512-zvtSJwuQFpewSyRrI3AsftF6rM0X80mZkChIt1spBGEvRglCrjTniXvinc8JKRoqTwXAgvqTImaN9igfSMtUBw== capture-exit@^2.0.0: version "2.0.0" @@ -3825,10 +3825,10 @@ entities@^2.0.0: resolved "https://registry.yarnpkg.com/entities/-/entities-2.2.0.tgz#098dc90ebb83d8dffa089d55256b351d34c4da55" integrity sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A== -enty@^2.0.0-alpha.13: - version "2.0.0-alpha.13" - resolved "https://registry.yarnpkg.com/enty/-/enty-2.0.0-alpha.13.tgz#64f480e748c0037cb559e32068996d913ac88df4" - integrity sha512-pvtmAdOJh/BDZByKuOMpfFQZ4dTXBFin8QfTOvWMDH5DUhbIqrZxBFzXuI8tezoN6ozF7NdKaVdH1H8h/c8KeQ== +enty@^2.0.0-alpha.16: + version "2.0.0-alpha.16" + resolved "https://registry.yarnpkg.com/enty/-/enty-2.0.0-alpha.16.tgz#4dc2185466d1732a119f7b1463a5bdf6c706c183" + integrity sha512-huWNHs30cRrDvg1+j3gGOMHth2QayssZLYHwhnBCfHLr14AOKirlkAvqdBKq6FbBvbb2qROjtW23zukqL8SwhQ== enzyme-matchers@^7.1.2: version "7.1.2"