Skip to content

Commit 865b8c8

Browse files
committed
Merge branch 'master' of https://github.com/a-marquez/redux-logic into a-marquez-master
2 parents ffd2857 + 2feb72c commit 865b8c8

6 files changed

+178
-5
lines changed

src/createLogic.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { stringifyType } from './utils';
12

23
const allowedOptions = [
34
'name',
@@ -188,7 +189,7 @@ export default function createLogic(logicOptions = {}) {
188189
typeof processOptions.dispatchMultiple !== 'undefined' &&
189190
warnTimeout !== 0) {
190191
// eslint-disable-next-line no-console
191-
console.error(`warning: in logic for type(s): ${type} - dispatchMultiple is always true in next version. For non-ending logic, set warnTimeout to 0`);
192+
console.error(`warning: in logic for type(s): ${stringifyType(type)} - dispatchMultiple is always true in next version. For non-ending logic, set warnTimeout to 0`);
192193
}
193194

194195
// use process fn signature to determine some processOption defaults
@@ -203,7 +204,7 @@ export default function createLogic(logicOptions = {}) {
203204
!processOptions.dispatchMultiple
204205
&& warnTimeout !== 0) {
205206
// eslint-disable-next-line no-console
206-
console.error(`warning: in logic for type(s): ${type} - single-dispatch mode is deprecated, call done when finished dispatching. For non-ending logic, set warnTimeout: 0`);
207+
console.error(`warning: in logic for type(s): ${stringifyType(type)} - single-dispatch mode is deprecated, call done when finished dispatching. For non-ending logic, set warnTimeout: 0`);
207208
}
208209
// nothing to do, defaults are fine
209210
break;

src/createLogicMiddleware.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import 'rxjs/add/operator/scan';
77
import 'rxjs/add/operator/takeWhile';
88
import 'rxjs/add/operator/toPromise';
99
import wrapper from './logicWrapper';
10-
import { confirmProps } from './utils';
10+
import { confirmProps, stringifyType } from './utils';
1111

1212
// confirm custom Rx build imports
1313
confirmProps(Observable.prototype, [
@@ -258,7 +258,7 @@ function naming(logic, idx) {
258258
if (logic.name) { return logic; }
259259
return {
260260
...logic,
261-
name: `L(${logic.type.toString()})-${idx}`
261+
name: `L(${stringifyType(logic.type)})-${idx}`
262262
};
263263
}
264264

src/logicWrapper.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,9 @@ export default function logicWrapper(logic, store, deps, monitor$) {
6262
function matchesType(tStrArrRe, type) {
6363
/* istanbul ignore if */
6464
if (!tStrArrRe) { return false; } // nothing matches none
65+
if (typeof tStrArrRe === 'symbol') {
66+
return (tStrArrRe === type);
67+
}
6568
if (typeof tStrArrRe === 'string') {
6669
return (tStrArrRe === type || tStrArrRe === '*');
6770
}

src/utils.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,12 @@ export function confirmProps(obj, arrProps, objName = '') {
77
}
88
});
99
}
10+
11+
// Symbols and Arrays containing Symbols cannot be interpolated in template strings,
12+
// they must be explicitly converted with toString()
13+
// eslint-disable-next-line import/prefer-default-export
14+
export function stringifyType(type) {
15+
return Array.isArray(type) ?
16+
type.map(type => type.toString()) :
17+
type.toString();
18+
}

test/createLogicMiddleware.spec.js

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,148 @@ describe('createLogicMiddleware', () => {
327327
});
328328
});
329329

330+
describe('[logicA] type is symbol, match only', () => {
331+
let monArr = [];
332+
let mw;
333+
let logicA;
334+
let next;
335+
let dispatch;
336+
const FOO = Symbol('FOO');
337+
const BAR = Symbol('BAR');
338+
const CAT = Symbol('CAT');
339+
const actionA = { type: FOO };
340+
const actionAResult = { type: FOO, allowed: ['a'] };
341+
const actionADispatch = { type: BAR, allowed: ['a'] };
342+
const actionIgnore = { type: CAT };
343+
beforeEach(done => {
344+
monArr = [];
345+
next = expect.createSpy();
346+
dispatch = expect.createSpy();
347+
logicA = createLogic({
348+
type: FOO,
349+
validate({ action }, allow) {
350+
allow({
351+
...action,
352+
allowed: ['a']
353+
});
354+
},
355+
process({ action }, dispatch) {
356+
dispatch({
357+
...action,
358+
type: BAR
359+
});
360+
}
361+
});
362+
mw = createLogicMiddleware([logicA]);
363+
mw.monitor$.subscribe(x => monArr.push(x));
364+
const storeFn = mw({ dispatch })(next);
365+
storeFn(actionIgnore);
366+
storeFn(actionA);
367+
mw.whenComplete(done);
368+
});
369+
370+
it('both messages hit next, one bypassed validation/transform', () => {
371+
expect(next.calls.length).toBe(2);
372+
expect(next.calls[0].arguments[0]).toEqual(actionIgnore);
373+
expect(next.calls[1].arguments[0]).toEqual(actionAResult);
374+
});
375+
376+
it('only matching is processed and dispatched', () => {
377+
expect(dispatch.calls.length).toBe(1);
378+
expect(dispatch.calls[0].arguments[0]).toEqual(actionADispatch);
379+
});
380+
381+
it('mw.monitor$ should track flow', () => {
382+
expect(monArr).toEqual([
383+
{ action: { type: CAT }, op: 'top' },
384+
{ nextAction: { type: CAT }, op: 'bottom' },
385+
{ action: { type: FOO }, op: 'top' },
386+
{ action: { type: FOO }, name: 'L(Symbol(FOO))-0', op: 'begin' },
387+
{ action: { type: FOO },
388+
nextAction: { type: FOO, allowed: ['a'] },
389+
name: 'L(Symbol(FOO))-0',
390+
shouldProcess: true,
391+
op: 'next' },
392+
{ nextAction: { type: FOO, allowed: ['a'] }, op: 'bottom' },
393+
{ action: { type: FOO },
394+
dispAction: { type: BAR, allowed: ['a'] },
395+
op: 'dispatch' },
396+
{ action: { type: FOO },
397+
name: 'L(Symbol(FOO))-0',
398+
op: 'end'
399+
}
400+
]);
401+
});
402+
403+
it('mw.whenComplete(fn) should be called when complete', (done) => {
404+
mw.whenComplete(done);
405+
});
406+
407+
it('mw.whenComplete(fn) should resolve to promise', (done) => {
408+
function fn() { }
409+
mw.whenComplete(fn).then(done);
410+
});
411+
412+
it('mw.whenComplete() should resolve to promise', (done) => {
413+
mw.whenComplete().then(done);
414+
});
415+
416+
it('delayed mw.whenComplete() should still resolve to promise', (done) => {
417+
setTimeout(() => {
418+
mw.whenComplete().then(done);
419+
}, 100);
420+
});
421+
});
422+
423+
describe('[logicA] type is arr of Symbols, match any', () => {
424+
let mw;
425+
let logicA;
426+
let next;
427+
let dispatch;
428+
const FOO = Symbol('FOO');
429+
const BAR = Symbol('BAR');
430+
const CAT = Symbol('CAT');
431+
const DOG = Symbol('DOG');
432+
const actionA = { type: FOO };
433+
const actionAResult = { type: FOO, allowed: ['a'] };
434+
const actionADispatch = { type: BAR, allowed: ['a'] };
435+
const actionIgnore = { type: CAT };
436+
beforeEach(done => {
437+
next = expect.createSpy();
438+
dispatch = expect.createSpy().andCall(() => done());
439+
logicA = createLogic({
440+
type: [DOG, FOO],
441+
validate({ action }, allow) {
442+
allow({
443+
...action,
444+
allowed: ['a']
445+
});
446+
},
447+
process({ action }, dispatch) {
448+
dispatch({
449+
...action,
450+
type: BAR
451+
});
452+
}
453+
});
454+
mw = createLogicMiddleware([logicA]);
455+
const storeFn = mw({ dispatch })(next);
456+
storeFn(actionIgnore);
457+
storeFn(actionA);
458+
});
459+
460+
it('both messages hit next, one bypassed validation/transform', () => {
461+
expect(next.calls.length).toBe(2);
462+
expect(next.calls[0].arguments[0]).toEqual(actionIgnore);
463+
expect(next.calls[1].arguments[0]).toEqual(actionAResult);
464+
});
465+
466+
it('only matching is processed and dispatched', () => {
467+
expect(dispatch.calls.length).toBe(1);
468+
expect(dispatch.calls[0].arguments[0]).toEqual(actionADispatch);
469+
});
470+
});
471+
330472
describe('[logicA] type is regex, match', () => {
331473
let mw;
332474
let logicA;

test/utils.spec.js

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import expect from 'expect';
2-
import { confirmProps } from '../src/utils';
2+
import { confirmProps, stringifyType } from '../src/utils';
33

44

55
describe('confirmProps', () => {
@@ -41,3 +41,21 @@ describe('confirmProps', () => {
4141
});
4242

4343
});
44+
45+
describe('stringifyType', () => {
46+
it('should stringify a single type of string|symbol|regex', () => {
47+
[
48+
['FOO', 'FOO'],
49+
[Symbol('BAR'), 'Symbol(BAR)'],
50+
[/CAT/, '/CAT/']
51+
].forEach(([type, string]) => {
52+
expect(stringifyType(type)).toEqual(string);
53+
});
54+
});
55+
56+
it('should stringify contents of an arr type of string|symbol|regex', () => {
57+
const type = ['FOO', Symbol('BAR'), /CAT/];
58+
const string = ['FOO', 'Symbol(BAR)', '/CAT/'];
59+
expect(stringifyType(type)).toEqual(string);
60+
});
61+
});

0 commit comments

Comments
 (0)