Skip to content

Commit c5d42f8

Browse files
authored
Merge pull request #6 from KevinAst/initial
master PR for code coverage (Take II)
2 parents f256948 + 6356212 commit c5d42f8

File tree

5 files changed

+252
-14
lines changed

5 files changed

+252
-14
lines changed

.babelrc

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,7 @@
1212
},
1313
"test": {
1414
"plugins": [
15-
["transform-es2015-modules-commonjs", { "loose": true }],
16-
"istanbul"
15+
["transform-es2015-modules-commonjs", { "loose": true }]
1716
]
1817
}
1918
}

README.md

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ Let's see how this all works together ...
5858
- [Interface Points](#interface-points)
5959
- [Input](#input)
6060
- [Exposure](#exposure)
61+
- [Error Conditions](#error-conditions)
6162
- [API](#api)
6263
- [`logicAspect: Aspect`](#logicaspect-aspect)
6364

@@ -274,6 +275,39 @@ process (_i.e. the inputs and outputs_) are documented here.
274275
[feature-redux]_) that in turn manages [redux].
275276

276277

278+
### Error Conditions
279+
280+
When **feature-redux-logic** detects that no logic modules have been
281+
specified by any of your features, it will (by default) throw the
282+
following exception:
283+
284+
```
285+
***ERROR*** feature-redux-logic found NO logic modules within your features
286+
... did you forget to register Feature.logic aspects in your features?
287+
(please refer to the feature-redux-logic docs to see how to override this behavior).
288+
```
289+
290+
Most likely this should in fact be considered an error _(for example
291+
you neglected to specify the logic modules within your features)_. **The
292+
reasoning is**: _why would you not specify any logic modules if
293+
your using redux-logic?_
294+
295+
You can change this behavior through the following configuration:
296+
297+
```js
298+
logicAspect.allowNoLogic$ = true;
299+
```
300+
301+
With this option enabled, when no logic modules are found,
302+
redux-logic will simply NOT be configured (accompanied with a WARNING
303+
logging probe).
304+
305+
You can also specify your own array of logic modules in place of the
306+
`true` value, which will be used ONLY in the scenario where no logic
307+
modules were specified by your features.
308+
309+
310+
277311
## API
278312

279313
### logicAspect: Aspect

package.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,12 @@
7171
"url": "https://github.com/KevinAst/feature-redux-logic/issues"
7272
},
7373
"homepage": "https://github.com/KevinAst/feature-redux-logic",
74+
"jest": {
75+
"collectCoverageFrom": [
76+
"src/**/*.js",
77+
"!**/spec/**"
78+
]
79+
},
7480
"devDependencies": {
7581
"babel-cli": "^6.24.1",
7682
"babel-core": "^6.24.1",

src/logicAspect.js

Lines changed: 104 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,43 @@
11
import {createLogicMiddleware} from 'redux-logic'; // peerDependency
2-
import {createAspect} from 'feature-u'; // peerDependency:
2+
import {createAspect,
3+
extendAspectProperty,
4+
launchApp} from 'feature-u'; // peerDependency:
35

6+
// our logger (integrated/activated via feature-u)
7+
const logf = launchApp.diag.logf.newLogger('- ***feature-redux-logic*** logicAspect: ');
48

59
// NOTE: See README for complete description
610
export default createAspect({
711
name: 'logic',
12+
genesis,
813
validateFeatureContent,
914
assembleFeatureContent,
15+
createLogicMiddleware$,
1016
getReduxMiddleware,
1117
});
1218

1319

20+
/**
21+
* Register feature-redux-logic proprietary Aspect APIs (required to
22+
* pass feature-u validation).
23+
* This must occur early in the life-cycle (i.e. this method) to
24+
* guarantee the new API is available during feature-u validation.
25+
*
26+
* @return {string} NONE FOR US ... an error message when self is in an invalid state
27+
* (falsy when valid).
28+
*
29+
* @private
30+
*/
31+
function genesis() {
32+
logf('genesis() registering internal Aspect properties');
33+
34+
extendAspectProperty('allowNoLogic$'); // Aspect.allowNoLogic$: true || [{logicModules}]
35+
// ... AI: technically this is for reducerAspect only (if the API ever supports this)
36+
extendAspectProperty('createLogicMiddleware$'); // Aspect.createLogicMiddleware$(app, appLogic): reduxMiddleware
37+
// ... AI: technically this is for reducerAspect only (if the API ever supports this)
38+
}
39+
40+
1441
/**
1542
* Validate self's aspect content on supplied feature.
1643
*
@@ -48,20 +75,89 @@ function validateFeatureContent(feature) {
4875
function assembleFeatureContent(app, activeFeatures) {
4976

5077
// accumulate logic modules across all features
51-
const appLogic = activeFeatures.reduce( (accum, feature) => {
78+
const hookSummary = [];
79+
let appLogic = activeFeatures.reduce( (accum, feature) => {
5280
if (feature[this.name]) {
5381
accum = [...accum, ...feature[this.name]];
82+
hookSummary.push(`\n Feature.name:${feature.name} <-- promotes ${this.name} AspectContent`);
83+
}
84+
else {
85+
hookSummary.push(`\n Feature.name:${feature.name}`);
5486
}
5587
return accum;
5688
}, []);
5789

58-
// ?? how should NO logic be handled: silenty ignore, with NO redux-logic OR throw error?
5990

60-
// define our redux middleware of redux-logic
61-
this.logicMiddleware = createLogicMiddleware(appLogic,
62-
{ // inject our app as a redux-logic dependancy
63-
app,
64-
});
91+
// report the accumulation of logic modules
92+
if (appLogic.length > 0) {
93+
logf(`assembleFeatureContent() gathered logic modules from the following Features: ${hookSummary}`);
94+
}
95+
96+
// handle special case where NO logic modules were gathered from features
97+
else {
98+
99+
// by default, this is an error condition (when NOT overridden by client)
100+
if (!this.allowNoLogic$) {
101+
throw new Error('***ERROR*** feature-redux-logic found NO logic modules within your features ' +
102+
`... did you forget to register Feature.${this.name} aspects in your features? ` +
103+
'(please refer to the feature-redux-logic docs to see how to override this behavior).');
104+
}
105+
106+
// when client override is an array, interpret it as logic modules
107+
if (Array.isArray(this.allowNoLogic$)) {
108+
logf.force('WARNING: NO logic modules were found in your Features (i.e. Feature.${this.name}), ' +
109+
'but client override (logicAspect.allowNoLogic$=[{logicModules}];) ' +
110+
'directed a continuation WITH specified logic modules.');
111+
appLogic = this.allowNoLogic$;
112+
}
113+
// otherwise, we simply disable redux-logic and continue on
114+
else {
115+
logf.force('WARNING: NO logic modules were found in your Features, ' +
116+
'but client override (logicAspect.allowNoLogic$=true;) ' +
117+
'directed a continuation WITHOUT redux-logic.');
118+
}
119+
}
120+
121+
// define our redux middleware for redux-logic
122+
// ... conditionally when we have logic modules
123+
// ... retained in self for promotion to feature-redux plugin
124+
if (appLogic.length > 0) {
125+
// ... accomplished in internal micro method (a defensive measure to allow easier overriding by client)
126+
this.logicMiddleware = this.createLogicMiddleware$(app, appLogic);
127+
}
128+
// if we have no logic ... we have no middleware
129+
else {
130+
this.logicMiddleware = null;
131+
}
132+
}
133+
134+
135+
/**
136+
* An internal micro method that creates/returns the redux middleware
137+
* for redux-logic.
138+
*
139+
* This logic is broken out in this internal method as a defensive
140+
* measure to make it easier for a client to override (if needed for
141+
* some unknown reason).
142+
*
143+
* @param {App} app the App object used in feature
144+
* cross-communication. This must be dependancy injected into
145+
* redux-logic.
146+
*
147+
* @param {logicModuls[]} appLogicArr - an array of redux-logic
148+
* modules (gaurenteed to have at least one entry).
149+
*
150+
* @return {reduxMiddleware} the newly created redux middleware for
151+
* redux-logic.
152+
*
153+
* @private
154+
*/
155+
function createLogicMiddleware$(app, appLogic) {
156+
// define our redux middleware for redux-logic
157+
return createLogicMiddleware(appLogic,
158+
{ // inject our app as a redux-logic dependancy
159+
app,
160+
});
65161
}
66162

67163

src/spec/logicAspect.spec.js

Lines changed: 107 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,113 @@
1-
import {logicAspect} from '..'; // STOP USING: '../../tooling/ModuleUnderTest';
1+
import {createFeature} from 'feature-u';
2+
import {logicAspect} from '..'; // modules under test
3+
4+
// temporarly turn on logging (just for fun)
5+
// ... must include launchApp on this
6+
// launchApp.diag.logf.enable();
27

38
describe('logicAspect() tests', () => {
49

5-
test('name', () => {
6-
expect( logicAspect.name)
7-
.toEqual('logic');
10+
describe('validate logicAspect.name', () => {
11+
12+
test('name', () => {
13+
expect( logicAspect.name)
14+
.toBe('logic');
15+
});
16+
17+
});
18+
19+
20+
describe('genesis()', () => {
21+
22+
logicAspect.genesis();
23+
24+
const noOpTest = "can't access isAspectProperty() ... just running code :-(";
25+
test('verify extendAspectProperty()', () => {
26+
expect(noOpTest)
27+
.toBe(noOpTest);
28+
});
29+
30+
});
31+
32+
33+
describe('validateFeatureContent()', () => {
34+
35+
test('must be an array', () => {
36+
37+
const feature = createFeature({
38+
name: 'feature1',
39+
logic: "I'm NOT an array",
40+
});
41+
42+
expect(logicAspect.validateFeatureContent(feature))
43+
.toMatch(/must be an array/);
44+
});
45+
46+
test('success', () => {
47+
48+
const feature = createFeature({
49+
name: 'feature1',
50+
logic: ['mock', 'logic', 'modules'],
51+
});
52+
53+
expect(logicAspect.validateFeatureContent(feature))
54+
.toBe(null);
55+
});
56+
57+
});
58+
59+
60+
describe('assembleFeatureContent()', () => {
61+
62+
test('no logic modules (DEFAULT)', () => {
63+
expect(()=>logicAspect.assembleFeatureContent('simulated app', []))
64+
.toThrow(/found NO logic modules within your features/);
65+
});
66+
67+
describe('no logic modules (OVERRIDE true)', () => {
68+
beforeEach(() => {
69+
logicAspect.allowNoLogic$ = true;
70+
});
71+
afterEach(() => {
72+
logicAspect.allowNoLogic$ = false;
73+
});
74+
test('expecting getReduxMiddleware() to be null', () => {
75+
logicAspect.assembleFeatureContent('simulated app', []);
76+
expect(logicAspect.getReduxMiddleware())
77+
.toBe(null);
78+
});
79+
});
80+
81+
describe('no logic modules (OVERRIDE array)', () => {
82+
beforeEach(() => {
83+
logicAspect.allowNoLogic$ = ['simulated', 'logic'];
84+
});
85+
afterEach(() => {
86+
logicAspect.allowNoLogic$ = false;
87+
});
88+
test('expecting getReduxMiddleware() to be non-null', () => {
89+
logicAspect.assembleFeatureContent('simulated app', []);
90+
expect(logicAspect.getReduxMiddleware())
91+
.not.toBe(null);
92+
});
93+
});
94+
95+
describe('features have logic modules', () => {
96+
test('expecting getReduxMiddleware() to be non-null', () => {
97+
logicAspect.assembleFeatureContent('simulated app', [
98+
createFeature({
99+
name: 'feature1',
100+
logic: ['simulated', 'logic']
101+
}),
102+
createFeature({
103+
name: 'featureWithNoLogic',
104+
})
105+
]);
106+
expect(logicAspect.getReduxMiddleware())
107+
.not.toBe(null);
108+
});
109+
});
110+
8111
});
9112

10113
});

0 commit comments

Comments
 (0)