Skip to content

Commit

Permalink
Merge pull request #6 from KevinAst/initial
Browse files Browse the repository at this point in the history
master PR for code coverage (Take II)
  • Loading branch information
KevinAst authored Mar 1, 2018
2 parents f256948 + 6356212 commit c5d42f8
Show file tree
Hide file tree
Showing 5 changed files with 252 additions and 14 deletions.
3 changes: 1 addition & 2 deletions .babelrc
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,7 @@
},
"test": {
"plugins": [
["transform-es2015-modules-commonjs", { "loose": true }],
"istanbul"
["transform-es2015-modules-commonjs", { "loose": true }]
]
}
}
Expand Down
34 changes: 34 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ Let's see how this all works together ...
- [Interface Points](#interface-points)
- [Input](#input)
- [Exposure](#exposure)
- [Error Conditions](#error-conditions)
- [API](#api)
- [`logicAspect: Aspect`](#logicaspect-aspect)

Expand Down Expand Up @@ -274,6 +275,39 @@ process (_i.e. the inputs and outputs_) are documented here.
[feature-redux]_) that in turn manages [redux].
### Error Conditions
When **feature-redux-logic** detects that no logic modules have been
specified by any of your features, it will (by default) throw the
following exception:
```
***ERROR*** feature-redux-logic found NO logic modules within your features
... did you forget to register Feature.logic aspects in your features?
(please refer to the feature-redux-logic docs to see how to override this behavior).
```
Most likely this should in fact be considered an error _(for example
you neglected to specify the logic modules within your features)_. **The
reasoning is**: _why would you not specify any logic modules if
your using redux-logic?_
You can change this behavior through the following configuration:
```js
logicAspect.allowNoLogic$ = true;
```
With this option enabled, when no logic modules are found,
redux-logic will simply NOT be configured (accompanied with a WARNING
logging probe).
You can also specify your own array of logic modules in place of the
`true` value, which will be used ONLY in the scenario where no logic
modules were specified by your features.
## API
### logicAspect: Aspect
Expand Down
6 changes: 6 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,12 @@
"url": "https://github.com/KevinAst/feature-redux-logic/issues"
},
"homepage": "https://github.com/KevinAst/feature-redux-logic",
"jest": {
"collectCoverageFrom": [
"src/**/*.js",
"!**/spec/**"
]
},
"devDependencies": {
"babel-cli": "^6.24.1",
"babel-core": "^6.24.1",
Expand Down
112 changes: 104 additions & 8 deletions src/logicAspect.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,43 @@
import {createLogicMiddleware} from 'redux-logic'; // peerDependency
import {createAspect} from 'feature-u'; // peerDependency:
import {createAspect,
extendAspectProperty,
launchApp} from 'feature-u'; // peerDependency:

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

// NOTE: See README for complete description
export default createAspect({
name: 'logic',
genesis,
validateFeatureContent,
assembleFeatureContent,
createLogicMiddleware$,
getReduxMiddleware,
});


/**
* Register feature-redux-logic proprietary Aspect APIs (required to
* pass feature-u validation).
* This must occur early in the life-cycle (i.e. this method) to
* guarantee the new API is available during feature-u validation.
*
* @return {string} NONE FOR US ... an error message when self is in an invalid state
* (falsy when valid).
*
* @private
*/
function genesis() {
logf('genesis() registering internal Aspect properties');

extendAspectProperty('allowNoLogic$'); // Aspect.allowNoLogic$: true || [{logicModules}]
// ... AI: technically this is for reducerAspect only (if the API ever supports this)
extendAspectProperty('createLogicMiddleware$'); // Aspect.createLogicMiddleware$(app, appLogic): reduxMiddleware
// ... AI: technically this is for reducerAspect only (if the API ever supports this)
}


/**
* Validate self's aspect content on supplied feature.
*
Expand Down Expand Up @@ -48,20 +75,89 @@ function validateFeatureContent(feature) {
function assembleFeatureContent(app, activeFeatures) {

// accumulate logic modules across all features
const appLogic = activeFeatures.reduce( (accum, feature) => {
const hookSummary = [];
let appLogic = activeFeatures.reduce( (accum, feature) => {
if (feature[this.name]) {
accum = [...accum, ...feature[this.name]];
hookSummary.push(`\n Feature.name:${feature.name} <-- promotes ${this.name} AspectContent`);
}
else {
hookSummary.push(`\n Feature.name:${feature.name}`);
}
return accum;
}, []);

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

// define our redux middleware of redux-logic
this.logicMiddleware = createLogicMiddleware(appLogic,
{ // inject our app as a redux-logic dependancy
app,
});
// report the accumulation of logic modules
if (appLogic.length > 0) {
logf(`assembleFeatureContent() gathered logic modules from the following Features: ${hookSummary}`);
}

// handle special case where NO logic modules were gathered from features
else {

// by default, this is an error condition (when NOT overridden by client)
if (!this.allowNoLogic$) {
throw new Error('***ERROR*** feature-redux-logic found NO logic modules within your features ' +
`... did you forget to register Feature.${this.name} aspects in your features? ` +
'(please refer to the feature-redux-logic docs to see how to override this behavior).');
}

// when client override is an array, interpret it as logic modules
if (Array.isArray(this.allowNoLogic$)) {
logf.force('WARNING: NO logic modules were found in your Features (i.e. Feature.${this.name}), ' +
'but client override (logicAspect.allowNoLogic$=[{logicModules}];) ' +
'directed a continuation WITH specified logic modules.');
appLogic = this.allowNoLogic$;
}
// otherwise, we simply disable redux-logic and continue on
else {
logf.force('WARNING: NO logic modules were found in your Features, ' +
'but client override (logicAspect.allowNoLogic$=true;) ' +
'directed a continuation WITHOUT redux-logic.');
}
}

// define our redux middleware for redux-logic
// ... conditionally when we have logic modules
// ... retained in self for promotion to feature-redux plugin
if (appLogic.length > 0) {
// ... accomplished in internal micro method (a defensive measure to allow easier overriding by client)
this.logicMiddleware = this.createLogicMiddleware$(app, appLogic);
}
// if we have no logic ... we have no middleware
else {
this.logicMiddleware = null;
}
}


/**
* An internal micro method that creates/returns the redux middleware
* for redux-logic.
*
* This logic is broken out in this internal method as a defensive
* measure to make it easier for a client to override (if needed for
* some unknown reason).
*
* @param {App} app the App object used in feature
* cross-communication. This must be dependancy injected into
* redux-logic.
*
* @param {logicModuls[]} appLogicArr - an array of redux-logic
* modules (gaurenteed to have at least one entry).
*
* @return {reduxMiddleware} the newly created redux middleware for
* redux-logic.
*
* @private
*/
function createLogicMiddleware$(app, appLogic) {
// define our redux middleware for redux-logic
return createLogicMiddleware(appLogic,
{ // inject our app as a redux-logic dependancy
app,
});
}


Expand Down
111 changes: 107 additions & 4 deletions src/spec/logicAspect.spec.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,113 @@
import {logicAspect} from '..'; // STOP USING: '../../tooling/ModuleUnderTest';
import {createFeature} from 'feature-u';
import {logicAspect} from '..'; // modules under test

// temporarly turn on logging (just for fun)
// ... must include launchApp on this
// launchApp.diag.logf.enable();

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

test('name', () => {
expect( logicAspect.name)
.toEqual('logic');
describe('validate logicAspect.name', () => {

test('name', () => {
expect( logicAspect.name)
.toBe('logic');
});

});


describe('genesis()', () => {

logicAspect.genesis();

const noOpTest = "can't access isAspectProperty() ... just running code :-(";
test('verify extendAspectProperty()', () => {
expect(noOpTest)
.toBe(noOpTest);
});

});


describe('validateFeatureContent()', () => {

test('must be an array', () => {

const feature = createFeature({
name: 'feature1',
logic: "I'm NOT an array",
});

expect(logicAspect.validateFeatureContent(feature))
.toMatch(/must be an array/);
});

test('success', () => {

const feature = createFeature({
name: 'feature1',
logic: ['mock', 'logic', 'modules'],
});

expect(logicAspect.validateFeatureContent(feature))
.toBe(null);
});

});


describe('assembleFeatureContent()', () => {

test('no logic modules (DEFAULT)', () => {
expect(()=>logicAspect.assembleFeatureContent('simulated app', []))
.toThrow(/found NO logic modules within your features/);
});

describe('no logic modules (OVERRIDE true)', () => {
beforeEach(() => {
logicAspect.allowNoLogic$ = true;
});
afterEach(() => {
logicAspect.allowNoLogic$ = false;
});
test('expecting getReduxMiddleware() to be null', () => {
logicAspect.assembleFeatureContent('simulated app', []);
expect(logicAspect.getReduxMiddleware())
.toBe(null);
});
});

describe('no logic modules (OVERRIDE array)', () => {
beforeEach(() => {
logicAspect.allowNoLogic$ = ['simulated', 'logic'];
});
afterEach(() => {
logicAspect.allowNoLogic$ = false;
});
test('expecting getReduxMiddleware() to be non-null', () => {
logicAspect.assembleFeatureContent('simulated app', []);
expect(logicAspect.getReduxMiddleware())
.not.toBe(null);
});
});

describe('features have logic modules', () => {
test('expecting getReduxMiddleware() to be non-null', () => {
logicAspect.assembleFeatureContent('simulated app', [
createFeature({
name: 'feature1',
logic: ['simulated', 'logic']
}),
createFeature({
name: 'featureWithNoLogic',
})
]);
expect(logicAspect.getReduxMiddleware())
.not.toBe(null);
});
});

});

});

0 comments on commit c5d42f8

Please sign in to comment.