Skip to content

Commit

Permalink
Fix reducing similar action names (#22)
Browse files Browse the repository at this point in the history
* [fix] dont reduce similar action names

* [fix] more test case hardening

* [nit] typo

* [fix] escape api names for RegExp
  • Loading branch information
kinetifex committed Jul 28, 2020
1 parent e276a0e commit 9955c21
Show file tree
Hide file tree
Showing 4 changed files with 48 additions and 65 deletions.
14 changes: 9 additions & 5 deletions src/reducers.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { startsWith, endsWith } from './utils';
import { escapeRegExp } from './utils';

export const handlers = {};

Expand Down Expand Up @@ -59,11 +59,15 @@ handlers.onComplete = function (state, action) {
* @private
*/
export default function createReducer(apiName) {
const safeName = escapeRegExp(apiName);
const reApiAction = new RegExp(`^${safeName}_(RESET|START|SUCCESS|FAIL)$`);
return function reducer(state = {}, action) {
if (startsWith(action.type, apiName)) {
if (endsWith(action.type, 'RESET')) return handlers.onReset(state, action);
if (endsWith(action.type, 'START')) return handlers.onStart(state, action);
if (endsWith(action.type, 'SUCCESS') || endsWith(action.type, 'FAIL')) return handlers.onComplete(state, action);
const match = reApiAction.exec(action.type);
if (match) {
const type = match[1];
if (type === 'RESET') return handlers.onReset(state, action);
if (type === 'START') return handlers.onStart(state, action);
return handlers.onComplete(state, action);
}
return state;
};
Expand Down
25 changes: 6 additions & 19 deletions src/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -105,25 +105,12 @@ export function getUrlTemplate(url, getState) {
}

/**
* Determines whether a string begins with the characters of a specified string
* Escape special characters for RegExp
*
* @param {String} str - String to check
* @param {String} search - String to search for at the start
* @returns {Boolean} result
* @private
*/
export function startsWith(str, search) {
return str.indexOf(search) === 0;
}

/**
* Determines whether a string ends with the characters of a specified string
*
* @param {String} str - String to check
* @param {String} search - String to search for at the end
* @returns {Boolean} result
* @private
* @see: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions#Escaping
* @param {string} string - Intended for expression
* @returns {string} escaped
*/
export function endsWith(str, search) {
return str.lastIndexOf(search) === (str.length - search.length);
export function escapeRegExp(string) {
return string.replace(/[.*+\-?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string
}
29 changes: 29 additions & 0 deletions tests/reducers.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,35 @@ describe('Reducers', () => {
expect(handlers.onComplete).not.toHaveBeenCalled();
expect(handlers.onReset).toHaveBeenCalled();
});

it('handles special characters in api names', () => {
const inState = { bogus: 'bogus' };
reducer = createReducer('a*b+c');
reducer(inState, { type: 'a*b+c_START' });
expect(handlers.onStart).toHaveBeenCalled();

reducer = createReducer('api(v2)');
reducer(inState, { type: 'api(v2)_SUCCESS' });
expect(handlers.onComplete).toHaveBeenCalled();
});

it('does not handle for similarly named apis', () => {
const inState = { bogus: 'bogus' };
reducer(inState, { type: '2mockApi_START' });
reducer(inState, { type: 'mockApi_2_START' });
reducer(inState, { type: 'mockApi2_START' });
reducer(inState, { type: 'MOCKAPI_START' });
expect(handlers.onStart).not.toHaveBeenCalled();
});

it('does not handle for unknown actions', () => {
const inState = { bogus: 'bogus' };
reducer(inState, { type: 'mockApi_2START' });
reducer(inState, { type: 'mockApi_START2' });
reducer(inState, { type: 'mockApi__START' });
reducer(inState, { type: 'mockApi_start' });
expect(handlers.onStart).not.toHaveBeenCalled();
});
});
describe('handleStart', () => {

Expand Down
45 changes: 4 additions & 41 deletions tests/utils.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,7 @@ import {
getUrlTemplate,
parseReqDesc,
parseApiDesc,
startsWith,
endsWith
escapeRegExp
} from '../src/utils';
import * as utils from '../src/utils';

Expand Down Expand Up @@ -219,46 +218,10 @@ describe('Utils', () => {
});
});

describe('startsWith', () => {
describe('escapeRegExp', () => {

it('returns true if search string is at start', () => {
expect(startsWith('example', 'ex')).toBe(true);
});

it('returns false if search string is not at start', () => {
expect(startsWith('example', 'no')).toBe(false);
});

it('returns false if search string is not at start, even if present', () => {
expect(startsWith('example', 'amp')).toBe(false);
it('escaped special characters', () => {
expect(escapeRegExp('a*b+c')).toEqual('a\\*b\\+c');
});
});

describe('endsWith', () => {

it('returns true is search string is at end', () => {
expect(endsWith('example', 'le')).toBe(true);
});

it('handles a string with multiple occurences', () => {
expect(endsWith('siryessir', 'sir')).toBe(true);
});

it('handles an empty search string', () => {
expect(endsWith('example', '')).toBe(true);
});

it('returns false if search string is not at end', () => {
expect(endsWith('example', 'no')).toBe(false);
});

it('returns false if search string is not at end, even if present', () => {
expect(endsWith('example', 'amp')).toBe(false);
});

it('handles an empty string', () => {
expect(endsWith('', 'amp')).toBe(false);
});
});

});

0 comments on commit 9955c21

Please sign in to comment.