diff --git a/src/services/authFlow/AuthFlow.js b/src/services/authFlow/AuthFlow.js index a505d76..3d2ef97 100644 --- a/src/services/authFlow/AuthFlow.js +++ b/src/services/authFlow/AuthFlow.js @@ -82,13 +82,25 @@ export default class AuthFlow { if (resp && resp.then) { // this is a state with an async enter phase // block route components from mounting, till promise will be resolved - const callback = this.onReady; - this.onReady = () => {}; - return resp.then(callback); + if (this.onReady) { + const callback = this.onReady; + this.onReady = () => {}; + return resp.then(callback); + } else { + return resp; + } } } - handleRequest(path, replace, callback) { + /** + * This should be called from onEnter prop of react-router Route component + * + * @param {string} path + * @param {function} replace + * @param {function} [callback = function() {}] - an optional callback function to be called, when state will be stabilized + * (state's enter function's promise resolved) + */ + handleRequest(path, replace, callback = function() {}) { this.replace = replace; this.onReady = callback; diff --git a/tests/services/authFlow/AuthFlow.test.js b/tests/services/authFlow/AuthFlow.test.js index 51ecd4d..1f7d7e8 100644 --- a/tests/services/authFlow/AuthFlow.test.js +++ b/tests/services/authFlow/AuthFlow.test.js @@ -38,7 +38,7 @@ describe('AuthFlow', () => { expect(flow.state).to.be.equal(state); }); - it('should call `enter` on new state and pass reference to itself', () => { + it('should call #enter() on new state and pass reference to itself', () => { const state = new AbstractState(); const spy = sinon.spy(state, 'enter'); @@ -62,6 +62,17 @@ describe('AuthFlow', () => { sinon.assert.notCalled(spy2); }); + it('should return promise, if #enter returns it', () => { + const state = new AbstractState(); + const expected = Promise.resolve(); + + state.enter = () => expected; + + const actual = flow.setState(state); + + expect(actual).to.be.equal(expected); + }); + it('should throw if no state', () => { expect(() => flow.setState()).to.throw('State is required'); }); @@ -184,6 +195,32 @@ describe('AuthFlow', () => { sinon.assert.calledWithExactly(flow.run, 'setOAuthRequest', {}); }); + it('should call callback', () => { + const callback = sinon.stub(); + + flow.handleRequest('/', function() {}, callback); + + sinon.assert.calledOnce(callback); + }); + + it('should not call callback till returned from #enter() promise will be resolved', () => { + let resolve; + const promise = {then: (cb) => {resolve = cb}}; + const callback = sinon.stub(); + const state = new AbstractState(); + state.enter = () => promise; + + flow.setState = AuthFlow.prototype.setState.bind(flow, state); + + flow.handleRequest('/', function() {}, callback); + + expect(resolve).to.be.a('function'); + + sinon.assert.notCalled(callback); + resolve(); + sinon.assert.calledOnce(callback); + }); + it('throws if unsupported request', () => { expect(() => flow.handleRequest('/foo/bar')).to.throw('Unsupported request: /foo/bar'); });