Skip to content

Commit

Permalink
behavior unit specs
Browse files Browse the repository at this point in the history
  • Loading branch information
Christopher Turner committed Jul 5, 2016
1 parent 64e8f28 commit 5b2897a
Show file tree
Hide file tree
Showing 4 changed files with 319 additions and 15 deletions.
32 changes: 17 additions & 15 deletions polymer-redux.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,22 +22,24 @@
var prevArrays = {}; // tracks array splices

// property bindings
Object.keys(element.properties).forEach(function(name) {
prop = element.properties[name];
if (prop.hasOwnProperty('statePath')) {
// notify flag, warn against two-way bindings
if (prop.notify && !prop.readOnly) {
console.warn(warning, element.is, name);
if (element.properties != null) {
Object.keys(element.properties).forEach(function(name) {
prop = element.properties[name];
if (prop.hasOwnProperty('statePath')) {
// notify flag, warn against two-way bindings
if (prop.notify && !prop.readOnly) {
console.warn(warning, element.is, name);
}
props.push({
name: name,
// Empty statePath return state
path: prop.statePath || store.getState,
readOnly: prop.readOnly,
type: prop.type
});
}
props.push({
name: name,
// Empty statePath return state
path: prop.statePath || store.getState,
readOnly: prop.readOnly,
type: prop.type
});
}
});
});
}

// redux listener
return function() {
Expand Down
15 changes: 15 additions & 0 deletions test/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<!doctype html>
<html>
<head>
<title>Polymer Redux Test Suite</title>
<meta charset="utf-8">
<script src="../../web-component-tester/browser.js"></script>
</head>
<body>
<script>
WCT.loadSuites([
'polymer-redux.unit-spec.html'
]);
</script>
</body>
</html>
263 changes: 263 additions & 0 deletions test/polymer-redux.unit-spec.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,263 @@
<!doctype html>
<html>
<head>
<title>Polymer Redux Unit Specs</title>
<meta charset="utf-8">
<script src="../../webcomponentsjs/webcomponents-lite.js"></script>
<script src="../../web-component-tester/browser.js"></script>
<link rel="import" href="../polymer-redux.html">
<link rel="import" href="./support/redux-stub.html">
</head>
<body>
<script>
describe('PolymerRedux', function() {
it('should throw TypeError when no store given', function() {
assert.throws(function() {
PolymerRedux( /* no store */ );
}, /missing redux store/);
});

describe('Behavior instance', function() {
before(function() {
this.state = {};
this.element = {
is: 'fake-element',
actions: {
test: this.testAction
}
};
this.store = ReduxStub.createStore();
this.behavior = PolymerRedux(this.store);
});

it('should have ready method', function() {
assert.typeOf(this.behavior.ready, 'function');
});

it('should have attached method', function() {
assert.typeOf(this.behavior.attached, 'function');
});

it('should have detached method', function() {
assert.typeOf(this.behavior.detached, 'function');
});

it('should have getState method', function() {
assert.typeOf(this.behavior.getState, 'function');
});

describe('#ready', function() {
before(function() {
this.readyUnsubscribe = function() {};
this.fireReadyStub = sinon.stub();
this.elementReady = {
_reduxUnsubscribe: null,
fire: this.fireReadyStub
};
this.store.resetStub();
this.store.subscribe.returns(this.readyUnsubscribe);
this.store.getState.returns(this.state);
this.behavior.ready.apply(this.elementReady);
});

it('should call redux subscribe with listener', function() {
sinon.assert.calledWithMatch(
this.store.subscribe,
sinon.match.func
);
});

it('should have unsubscribe function on element', function() {
assert.strictEqual(this.elementReady._reduxUnsubscribe, this.readyUnsubscribe);
});
});

describe('#attached', function() {
before(function() {
this.attachedUnsubscribe = function() {};
this.fireAttachedStub = sinon.stub();
this.elementAttached = {
_reduxUnsubscribe: null,
fire: this.fireAttachedStub
};
this.store.resetStub();
this.store.subscribe.returns(this.attachedUnsubscribe);
this.store.getState.returns(this.state);
this.behavior.attached.apply(this.elementAttached);
});

it('should call redux subscribe with listener', function() {
sinon.assert.calledWithMatch(
this.store.subscribe,
sinon.match.func
);
});

it('should have unsubscribe function on element', function() {
assert.strictEqual(this.elementAttached._reduxUnsubscribe, this.attachedUnsubscribe);
});

it('should not subscribe if redux listener already added', function() {
this.store.subscribe.reset();
this.behavior.attached.apply(this.elementAttached);

sinon.assert.notCalled(this.store.subscribe);
});
});

describe('#detached', function() {
before(function() {
this.detachedUnsubscribe = sinon.stub();
this.elementDetached = {
_reduxUnsubscribe: this.detachedUnsubscribe
};
this.behavior.detached.apply(this.elementDetached);
});

it('should call unsubscribe whenever detached', function() {
sinon.assert.calledOnce(this.detachedUnsubscribe);
});

it('should remove unsubscribe from element', function() {
assert.notProperty(this.elementDetached, '_reduxUnsubscribe');
});

it('should not throw when detached without listener', function() {
var detached = this.behavior.detached;
var element = this.elementDetached;

delete this.elementDetached._reduxUnsubscribe;
assert.doesNotThrow(function() {
detached.apply(element);
});
});
});

describe('#dispatch', function() {
it('should return store\'s dispatch', function() {
var returnAction = {};

this.store.resetStub();
this.store.dispatch.returns(returnAction);

assert.strictEqual(this.behavior.dispatch(), returnAction);
});

describe('action name', function() {
before(function() {
var returnAction = this.nameAction = {};
this.testAction = sinon.spy(function() {
return returnAction;
});
this.actionElement = {
is: 'fake-element',
actions: {
test: this.testAction
}
};
this.store.resetStub();
});

describe('action method', function() {
before(function() {
this.behavior.dispatch.apply(this.actionElement, ['test', 'foo']);
});

it('should call action method', function() {
sinon.assert.calledOnce(this.testAction);
});

it('should be called with element context', function() {
sinon.assert.calledOn(this.testAction, this.actionElement);
});

it('should be pass args to action method', function() {
sinon.assert.calledWithExactly(this.testAction, 'foo');
});

it('should pass returned action to redux', function() {
sinon.assert.calledWithMatch(
this.store.dispatch,
sinon.match.same(this.nameAction)
);
});
});

it('should throw TypeError if no action available', function() {
var dispatch = this.behavior.dispatch;
var element = this.actionElement;

assert.throws(function() {
dispatch.apply(element, ['nothing']);
}, /<fake-element> has no action "nothing"/)
});
});

describe('action creator', function() {
before(function() {
var returnAction = {};
this.creatorAction = returnAction;
this.store.resetStub();
this.creatorSpy = sinon.spy(function() {
return returnAction;
});
this.behavior.dispatch(this.creatorSpy);
});

it('should pass redux given returning action creator', function() {
sinon.assert.calledWithMatch(
this.store.dispatch,
sinon.match.same(this.creatorAction)
);
});

it('should call action creator', function() {
sinon.assert.calledOnce(this.creatorSpy);
});
});

describe('action', function() {
it('should pass redux given action', function() {
var reduxAction = {};

this.store.resetStub();
this.behavior.dispatch(reduxAction);

sinon.assert.calledWithMatch(
this.store.dispatch,
sinon.match.same(reduxAction)
);
});

it('should not call middleware within behavior', function() {
var middleware = sinon.spy(function(dispatch) {});

this.store.resetStub();
this.behavior.dispatch(middleware);

sinon.assert.notCalled(middleware);
});
});
});

describe('#getState', function() {
before(function() {
this.store.resetStub();
this.store.getState.returns(this.state);

this.returnedState = this.behavior.getState();
});

it('should called store getState', function() {
sinon.assert.calledOnce(this.store.getState);
});

it('should return store\'s state', function() {
assert.strictEqual(this.returnedState, this.state);
});
});
});
});
</script>
</body>
</html>
24 changes: 24 additions & 0 deletions test/support/redux-stub.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<!doctype html>
<html>
<head>
<title>Redux Stub</title>
<meta charset="utf-8">
</head>
<body>
<script>
ReduxStub = {
createStore: function() {
var sandbox = sinon.sandbox.create();
return {
getState: sandbox.stub(),
dispatch: sandbox.stub(),
subscribe: sandbox.stub(),
resetStub: function() {
sandbox.reset();
}
};
}
};
</script>
</body>
</html>

0 comments on commit 5b2897a

Please sign in to comment.