resource-toolkit
Version:
Async and RESTful resource management tool.
535 lines (449 loc) • 15.7 kB
JavaScript
import { makeReducerAssets } from '../src';
import { ResourceToolkitError } from '../src/utils';
const defaultState = {
items: [],
isCreating: false,
isReadingBlindly: false,
isReadingAll: false,
reading: [],
updating: [],
deleting: [],
isLoading: false,
finishingLogs: [],
currentMessage: null,
relatedsTo: {},
meta: {},
};
describe('reducer factory: runtime validation', () => {
it('fails to be created if no options are provided', () => {
expect(() => makeReducerAssets()).toThrow(
'Expected params object on assets factory, got something else of type undefined.',
);
});
it('fails to be created if no truthy name string is provided', () => {
expect(() => makeReducerAssets({})).toThrow(
'Expected truthy "name" string on assets factory, got something else of type undefined.',
);
expect(() => makeReducerAssets({ name: null })).toThrow(
'Expected truthy "name" string on assets factory, got something else of type object.',
);
expect(() => makeReducerAssets({ name: '' })).toThrow(
'Expected truthy "name" string on assets factory, got something else of type string.',
);
});
it('fails to be created if no truthy idKey string is provided', () => {
expect(() => makeReducerAssets({ name: 'sample' })).toThrow(
'Expected truthy "idKey" string on assets factory, got something else of type undefined.',
);
expect(() => makeReducerAssets({ name: 'sample', idKey: null })).toThrow(
'Expected truthy "idKey" string on assets factory, got something else of type object.',
);
expect(() => makeReducerAssets({ name: 'sample', idKey: '' })).toThrow(
'Expected truthy "idKey" string on assets factory, got something else of type string.',
);
});
});
describe('reducer factory: commons', () => {
const userResource = makeReducerAssets({ name: 'USER', idKey: 'id' });
it('has initial state', () => {
expect(userResource.reducer()).toEqual(defaultState);
});
it('fallbacks as no-op if action is unknown by the resource lib', () => {
const action = {
type: 'invalid random type',
};
expect(userResource.reducer(defaultState, action)).toEqual(defaultState);
});
it('handles some error action by at least storing its log', () => {
const error = new ResourceToolkitError('Weird random programming error at reading');
const action = userResource.actions.setReadError(null, error);
const existingMessage = {
text: 'Existing success message',
isError: false,
causedByError: null,
};
const expectedErrorMessage = {
text: 'Failed to fetch data.',
causedByError: error,
isError: true,
};
const previousState = {
...defaultState,
isReadingBlindly: true,
finishingLogs: [existingMessage],
};
const expectedCurrentState = {
...defaultState,
isReadingBlindly: false,
finishingLogs: [existingMessage, expectedErrorMessage],
currentMessage: expectedErrorMessage,
};
expect(userResource.reducer(previousState, action)).toEqual(expectedCurrentState);
});
it('handles error action, defensively rejects non-Error types', () => {
const error = { message: 'am I really an error? my programmer should explicit throw so' };
const expectedMessage = {
causedByError: null,
isError: true,
text: 'Failed to update.',
};
const action = userResource.actions.setUpdateError(42, error);
const previousState = {
...defaultState,
};
const expectedCurrentState = {
...defaultState,
finishingLogs: [expectedMessage],
currentMessage: expectedMessage,
};
expect(userResource.reducer(previousState, action)).toEqual(expectedCurrentState);
});
it('supports adapter of error messages (thus adapter must be aware of error instance)', () => {
const adapter = jest.fn(() => 'Any custom message here!');
const customResource = makeReducerAssets({
name: 'USER',
idKey: 'id',
makeMessageText: adapter,
});
const error = new ResourceToolkitError('Weird random programming error at reading');
const action = customResource.actions.setReadError([1, 2], error);
const expectedErrorMessage = {
text: 'Any custom message here!',
causedByError: error,
isError: true,
};
const previousState = {
...defaultState,
reading: [1, 2],
};
const expectedCurrentState = {
...defaultState,
reading: [],
finishingLogs: [expectedErrorMessage],
currentMessage: expectedErrorMessage,
};
expect(customResource.reducer(previousState, action)).toEqual(expectedCurrentState);
expect(adapter).toBeCalledWith(error, 'READ', error);
});
it('handles clear message action', () => {
const action = userResource.actions.clearCurrentMessage();
const previousState = {
...defaultState,
finishingLogs: ['One', 'Two', 'Three'],
currentMessage: 'Three',
};
const expectedCurrentState = {
...defaultState,
finishingLogs: ['One', 'Two', 'Three'],
currentMessage: null,
};
expect(userResource.reducer(previousState, action)).toEqual(expectedCurrentState);
});
it('handles truthy flag for reading', () => {
const action = userResource.actions.setIsReadingAll(true);
const previousState = { ...defaultState, isReadingAll: false };
const expectedCurrentState = { ...defaultState, isReadingAll: true };
expect(userResource.reducer(previousState, action)).toEqual(expectedCurrentState);
});
it('handles falsy flag for reading', () => {
const action = userResource.actions.setIsReadingAll(false);
const previousState = { ...defaultState, isReadingAll: true };
const expectedCurrentState = { ...defaultState, isReadingAll: false };
expect(userResource.reducer(previousState, action)).toEqual(expectedCurrentState);
});
it('handles meta set', () => {
const action = userResource.actions.setMeta({ page: 1, for: 'example' });
const previousState = { ...defaultState, meta: { outdated: 'stuff' } };
const expectedCurrentState = { ...defaultState, meta: { page: 1, for: 'example' } };
expect(userResource.reducer(previousState, action)).toEqual(expectedCurrentState);
});
it('handles items clearing', () => {
const action = userResource.actions.clearItems();
const previousState = { ...defaultState, items: ['a', 'b', 'c'], meta: { some: 'thing' } };
const expectedCurrentState = { ...defaultState, items: [], meta: {} };
expect(userResource.reducer(previousState, action)).toEqual(expectedCurrentState);
});
});
describe('reducer factory: create', () => {
const userResource = makeReducerAssets({ name: 'USER', idKey: 'id' });
it('handles loading action for creating', () => {
const action = userResource.actions.setCreating();
const previousState = { ...defaultState };
const expectedCurrentState = {
...defaultState,
isCreating: true,
isLoading: true,
};
expect(userResource.reducer(previousState, action)).toEqual(expectedCurrentState);
});
it('handles success action for creating', () => {
const expectedMessage = {
causedByError: null,
isError: false,
text: 'Successfully created.',
};
const action = userResource.actions.setCreated({
id: 42,
name: 'Marcell',
lastName: 'Guilherme',
});
const previousState = {
...defaultState,
isCreating: true,
items: [{ id: 23, name: 'Jane', lastName: 'Doe' }],
};
const expectedCurrentState = {
...defaultState,
isCreating: false,
items: [
{ id: 23, name: 'Jane', lastName: 'Doe' },
{ id: 42, name: 'Marcell', lastName: 'Guilherme' },
],
currentMessage: expectedMessage,
finishingLogs: [expectedMessage],
};
expect(userResource.reducer(previousState, action)).toEqual(expectedCurrentState);
});
it('handles error action for creating', () => {
const error = new ResourceToolkitError('No creation here');
const expectedMessage = {
causedByError: error,
isError: true,
text: 'Failed to create.',
};
const action = userResource.actions.setCreateError(error);
const previousState = {
...defaultState,
isCreating: true,
items: [{ id: 23, name: 'Jane', lastName: 'Doe' }],
};
const expectedCurrentState = {
...defaultState,
isCreating: false,
items: [{ id: 23, name: 'Jane', lastName: 'Doe' }],
currentMessage: expectedMessage,
finishingLogs: [expectedMessage],
};
expect(userResource.reducer(previousState, action)).toEqual(expectedCurrentState);
});
});
describe('reducer factory: read', () => {
const userResource = makeReducerAssets({ name: 'USER', idKey: 'id' });
it('handles loading action for reading blindly', () => {
const action = userResource.actions.setReading();
const previousState = { ...defaultState };
const expectedCurrentState = {
...defaultState,
isReadingBlindly: true,
isLoading: true,
};
expect(userResource.reducer(previousState, action)).toEqual(expectedCurrentState);
});
it('handles success action for creating many', () => {
const expectedMessage = {
causedByError: null,
isError: false,
text: 'Successfully created. Related to 2 items.',
};
const action = userResource.actions.setCreated([
{
id: 42,
name: 'Marcell',
lastName: 'Guilherme',
},
{
id: 12,
name: 'Wallys',
lastName: 'Lima',
},
]);
const previousState = {
...defaultState,
isCreating: true,
isLoading: true,
items: [{ id: 23, name: 'Jane', lastName: 'Doe' }],
};
const expectedCurrentState = {
...defaultState,
isCreating: false,
isLoading: false,
items: [
{ id: 23, name: 'Jane', lastName: 'Doe' },
{ id: 42, name: 'Marcell', lastName: 'Guilherme' },
{ id: 12, name: 'Wallys', lastName: 'Lima' },
],
currentMessage: expectedMessage,
finishingLogs: [expectedMessage],
};
expect(userResource.reducer(previousState, action)).toEqual(expectedCurrentState);
});
it('handles success action for reading blindly', () => {
const expectedReadData = [
{
id: 69,
firstName: 'Marcell',
lastName: 'Guilherme',
},
{
id: 42,
firstName: 'Crash',
lastName: 'Bandicoot',
},
];
const expectedMessage = {
causedByError: null,
isError: false,
text: 'Successfully fetched data. Related to 2 items.',
};
const action = userResource.actions.setRead(null, expectedReadData);
const previousState = {
...defaultState,
isReadingBlindly: true,
};
const expectedCurrentState = {
...defaultState,
isReadingBlindly: false,
items: expectedReadData,
currentMessage: expectedMessage,
finishingLogs: [expectedMessage],
};
expect(userResource.reducer(previousState, action)).toEqual(expectedCurrentState);
});
});
describe('reducer factory: update', () => {
const userResource = makeReducerAssets({ name: 'USER', idKey: 'id' });
it('handles loading action for updating', () => {
const action = userResource.actions.setUpdating(42);
const previousState = {
...defaultState,
updating: [3],
isLoading: true,
};
const expectedCurrentState = {
...defaultState,
updating: [3, 42],
isLoading: true,
};
expect(userResource.reducer(previousState, action)).toEqual(expectedCurrentState);
});
it('handles success action for updating', () => {
const expectedMessage = {
causedByError: null,
isError: false,
text: 'Successfully updated.',
};
const action = userResource.actions.setUpdated(42, {
id: 42,
name: 'Marcell',
lastName: 'Guilherme',
});
const previousState = {
...defaultState,
updating: [23, 42],
isLoading: true,
items: [
{ id: 23, name: 'Jane', lastName: 'Doe' },
{ id: 42, name: 'John', lastName: 'Doe' },
],
};
const expectedCurrentState = {
...defaultState,
updating: [23],
isLoading: true,
items: [
{ id: 23, name: 'Jane', lastName: 'Doe' },
{ id: 42, name: 'Marcell', lastName: 'Guilherme' },
],
currentMessage: expectedMessage,
finishingLogs: [expectedMessage],
};
expect(userResource.reducer(previousState, action)).toEqual(expectedCurrentState);
});
it('handles error action for updating', () => {
const error = new ResourceToolkitError('Weird random programming error at reading');
const expectedMessage = {
causedByError: error,
isError: true,
text: 'Failed to update.',
};
const action = userResource.actions.setUpdateError(42, error);
const previousState = {
...defaultState,
updating: [23, 42],
isLoading: true,
};
const expectedCurrentState = {
...defaultState,
updating: [23],
isLoading: true,
finishingLogs: [expectedMessage],
currentMessage: expectedMessage,
};
expect(userResource.reducer(previousState, action)).toEqual(expectedCurrentState);
});
});
describe('reducer factory: delete', () => {
const userResource = makeReducerAssets({ name: 'USER', idKey: 'id' });
it('handles loading action for deleting', () => {
const action = userResource.actions.setDeleting(3);
const previousState = { ...defaultState, deleting: [9, 4], isLoading: true };
const expectedCurrentState = { ...defaultState, deleting: [9, 4, 3], isLoading: true };
expect(userResource.reducer(previousState, action)).toEqual(expectedCurrentState);
});
it('handles success action for deleting', () => {
const expectedMessage = {
causedByError: null,
isError: false,
text: 'Successfully deleted.',
};
const action = userResource.actions.setDeleted(42);
const previousState = {
...defaultState,
deleting: [23, 42],
isLoading: true,
items: [
{ id: 23, name: 'Jane', lastName: 'Doe' },
{ id: 42, name: 'John', lastName: 'Doe' },
],
};
const expectedCurrentState = {
...defaultState,
deleting: [23],
isLoading: true,
items: [{ id: 23, name: 'Jane', lastName: 'Doe' }],
currentMessage: expectedMessage,
finishingLogs: [expectedMessage],
};
expect(userResource.reducer(previousState, action)).toEqual(expectedCurrentState);
});
it('handles error action for deleting', () => {
const error = new ResourceToolkitError('No deleting here');
const expectedMessage = {
causedByError: error,
isError: true,
text: 'Failed to delete.',
};
const action = userResource.actions.setDeleteError(42, error);
const previousState = {
...defaultState,
deleting: [23, 42],
isLoading: true,
items: [
{ id: 23, name: 'Jane', lastName: 'Doe' },
{ id: 42, name: 'John', lastName: 'Doe' },
],
};
const expectedCurrentState = {
...defaultState,
isLoading: true,
deleting: [23],
items: [
{ id: 23, name: 'Jane', lastName: 'Doe' },
{ id: 42, name: 'John', lastName: 'Doe' },
],
currentMessage: expectedMessage,
finishingLogs: [expectedMessage],
};
expect(userResource.reducer(previousState, action)).toEqual(expectedCurrentState);
});
});