#169: remove unhandled promise rejections in tests. Added loging for that cases

This commit is contained in:
SleepWalker 2016-12-06 23:08:51 +02:00
parent febd148b2e
commit f9ae7053d0
18 changed files with 67 additions and 40 deletions

View File

@ -12,7 +12,7 @@ let promise;
* *
* @param {object} store - redux store * @param {object} store - redux store
* *
* @return {Promise} - a promise, that resolves in User state * @return {Promise<User>} - a promise, that resolves in User state
*/ */
export function factory(store) { export function factory(store) {
if (promise) { if (promise) {

View File

@ -1,3 +1,5 @@
import logger from 'services/logger';
import AbstractState from './AbstractState'; import AbstractState from './AbstractState';
import CompleteState from './CompleteState'; import CompleteState from './CompleteState';
@ -14,7 +16,8 @@ export default class AcceptRulesState extends AbstractState {
resolve(context) { resolve(context) {
context.run('acceptRules') context.run('acceptRules')
.then(() => context.setState(new CompleteState())); .then(() => context.setState(new CompleteState()))
.catch((err = {}) => err.errors || logger.warn(err));
} }
reject(context) { reject(context) {

View File

@ -1,3 +1,5 @@
import logger from 'services/logger';
import AbstractState from './AbstractState'; import AbstractState from './AbstractState';
import CompleteState from './CompleteState'; import CompleteState from './CompleteState';
import ResendActivationState from './ResendActivationState'; import ResendActivationState from './ResendActivationState';
@ -18,7 +20,8 @@ export default class ActivationState extends AbstractState {
resolve(context, payload) { resolve(context, payload) {
context.run('activate', payload) context.run('activate', payload)
.then(() => context.setState(new CompleteState())); .then(() => context.setState(new CompleteState()))
.catch((err = {}) => err.errors || logger.warn(err));
} }
reject(context) { reject(context) {

View File

@ -1,5 +1,7 @@
import { routeActions } from 'react-router-redux'; import { routeActions } from 'react-router-redux';
import logger from 'services/logger';
import RegisterState from './RegisterState'; import RegisterState from './RegisterState';
import LoginState from './LoginState'; import LoginState from './LoginState';
import OAuthState from './OAuthState'; import OAuthState from './OAuthState';
@ -88,7 +90,7 @@ export default class AuthFlow {
if (this.onReady) { if (this.onReady) {
const callback = this.onReady; const callback = this.onReady;
this.onReady = () => {}; this.onReady = () => {};
return resp.then(callback); return resp.then(callback, (err) => err || logger.warn(err));
} }
return resp; return resp;
@ -119,7 +121,7 @@ export default class AuthFlow {
* (state's enter function's promise resolved) * (state's enter function's promise resolved)
*/ */
handleRequest(request, replace, callback = function() {}) { handleRequest(request, replace, callback = function() {}) {
const {path, params = {}, query = {}} = request; const {path} = request;
this.replace = replace; this.replace = replace;
this.onReady = callback; this.onReady = callback;
@ -175,7 +177,6 @@ export default class AuthFlow {
break; break;
default: default:
console.log('Unsupported request', {path, query, params});
replace('/404'); replace('/404');
break; break;
} }

View File

@ -71,8 +71,11 @@ export default class CompleteState extends AbstractState {
if (resp.redirectUri.indexOf('static_page') === 0) { if (resp.redirectUri.indexOf('static_page') === 0) {
context.setState(new FinishState()); context.setState(new FinishState());
} else { } else {
context.run('redirect', resp.redirectUri); return new Promise(() => {
return Promise.reject(); // do not allow loader to be hidden and app to be rendered // do not resolve promise to make loader visible and
// overcome app rendering
context.run('redirect', resp.redirectUri);
});
} }
}, (resp) => { }, (resp) => {
if (resp.unauthorized) { if (resp.unauthorized) {

View File

@ -1,3 +1,5 @@
import logger from 'services/logger';
import AbstractState from './AbstractState'; import AbstractState from './AbstractState';
import PasswordState from './PasswordState'; import PasswordState from './PasswordState';
@ -20,6 +22,7 @@ export default class LoginState extends AbstractState {
resolve(context, payload) { resolve(context, payload) {
context.run('login', payload) context.run('login', payload)
.then(() => context.setState(new PasswordState())); .then(() => context.setState(new PasswordState()))
.catch((err = {}) => err.errors || logger.warn(err));
} }
} }

View File

@ -1,3 +1,5 @@
import logger from 'services/logger';
import AbstractState from './AbstractState'; import AbstractState from './AbstractState';
import LoginState from './LoginState'; import LoginState from './LoginState';
import CompleteState from './CompleteState'; import CompleteState from './CompleteState';
@ -18,7 +20,8 @@ export default class RecoverPasswordState extends AbstractState {
resolve(context, payload) { resolve(context, payload) {
context.run('recoverPassword', payload) context.run('recoverPassword', payload)
.then(() => context.setState(new CompleteState())); .then(() => context.setState(new CompleteState()))
.catch((err = {}) => err.errors || logger.warn(err));
} }
goBack(context) { goBack(context) {

View File

@ -1,3 +1,5 @@
import logger from 'services/logger';
import AbstractState from './AbstractState'; import AbstractState from './AbstractState';
import CompleteState from './CompleteState'; import CompleteState from './CompleteState';
import ActivationState from './ActivationState'; import ActivationState from './ActivationState';
@ -16,7 +18,8 @@ export default class RegisterState extends AbstractState {
resolve(context, payload) { resolve(context, payload) {
context.run('register', payload) context.run('register', payload)
.then(() => context.setState(new CompleteState())); .then(() => context.setState(new CompleteState()))
.catch((err = {}) => err.errors || logger.warn(err));
} }
reject(context, payload) { reject(context, payload) {

View File

@ -1,3 +1,5 @@
import logger from 'services/logger';
import AbstractState from './AbstractState'; import AbstractState from './AbstractState';
import CompleteState from './CompleteState'; import CompleteState from './CompleteState';
import ActivationState from './ActivationState'; import ActivationState from './ActivationState';
@ -16,7 +18,8 @@ export default class ResendActivationState extends AbstractState {
resolve(context, payload) { resolve(context, payload) {
context.run('resendActivation', payload) context.run('resendActivation', payload)
.then(() => context.setState(new ActivationState())); .then(() => context.setState(new ActivationState()))
.catch((err = {}) => err.errors || logger.warn(err));
} }
reject(context) { reject(context) {

View File

@ -126,7 +126,7 @@ describe('components/accounts/actions', () => {
sessionStorage.removeItem(expectedKey); sessionStorage.removeItem(expectedKey);
return authenticate(account)(dispatch).then(() => { return authenticate(account)(dispatch).then(() => {
expect(sessionStorage.getItem(expectedKey), 'not to be null') expect(sessionStorage.getItem(expectedKey), 'not to be null');
sessionStorage.removeItem(expectedKey); sessionStorage.removeItem(expectedKey);
}); });
}); });

View File

@ -1,4 +1,5 @@
import expect from 'unexpected'; import expect from 'unexpected';
import sinon from 'sinon';
import { routeActions } from 'react-router-redux'; import { routeActions } from 'react-router-redux';
@ -56,24 +57,15 @@ describe('components/user/actions', () => {
}); });
}); });
it('should post to /api/authentication/logout with user jwt', () => { it('should post to /api/authentication/logout with user jwt', () =>
// TODO: need an integration test with a middleware, because this callThunk(logout).then(() => {
// test is not reliable to check, whether logout request will have token inside
request.post.returns(new Promise((resolve) => {
setTimeout(() => {
// we must not overwrite user's token till request starts
expect(dispatch, 'was not called');
resolve();
}, 0);
}));
return callThunk(logout).then(() => {
expect(request.post, 'to have a call satisfying', [ expect(request.post, 'to have a call satisfying', [
'/api/authentication/logout', {}, {} '/api/authentication/logout', {}, {
token: expect.it('not to be empty')
}
]); ]);
}); })
}); );
testChangedToGuest(); testChangedToGuest();
testAccountsReset(); testAccountsReset();

View File

@ -2,14 +2,14 @@ import expect from 'unexpected';
describe('promise.prototype.finally', () => { describe('promise.prototype.finally', () => {
it('should be invoked after promise resolved', () => it('should be invoked after promise resolved', () =>
expect(new Promise((resolve) => { expect(new Promise((resolve) =>
Promise.resolve().finally(resolve) Promise.resolve().finally(resolve)
}), 'to be fulfilled') ), 'to be fulfilled')
); );
it('should be invoked after promise rejected', () => it('should be invoked after promise rejected', () =>
expect(new Promise((resolve) => { expect(new Promise((resolve) =>
Promise.reject().finally(resolve) expect(Promise.reject().finally(resolve), 'to be rejected')
}), 'to be fulfilled') ), 'to be fulfilled')
); );
}); });

View File

@ -1,3 +1,5 @@
import sinon from 'sinon';
import AcceptRulesState from 'services/authFlow/AcceptRulesState'; import AcceptRulesState from 'services/authFlow/AcceptRulesState';
import CompleteState from 'services/authFlow/CompleteState'; import CompleteState from 'services/authFlow/CompleteState';
@ -50,7 +52,8 @@ describe('AcceptRulesState', () => {
describe('#resolve', () => { describe('#resolve', () => {
it('should call acceptRules', () => { it('should call acceptRules', () => {
expectRun(mock, 'acceptRules').returns({then() {}}); expectRun(mock, 'acceptRules')
.returns(Promise.resolve());
state.resolve(context); state.resolve(context);
}); });

View File

@ -1,3 +1,5 @@
import sinon from 'sinon';
import ActivationState from 'services/authFlow/ActivationState'; import ActivationState from 'services/authFlow/ActivationState';
import CompleteState from 'services/authFlow/CompleteState'; import CompleteState from 'services/authFlow/CompleteState';
import ResendActivationState from 'services/authFlow/ResendActivationState'; import ResendActivationState from 'services/authFlow/ResendActivationState';
@ -73,7 +75,7 @@ describe('ActivationState', () => {
mock, mock,
'activate', 'activate',
sinon.match.same(payload) sinon.match.same(payload)
).returns({then() {}}); ).returns(Promise.resolve());
state.resolve(context, payload); state.resolve(context, payload);
}); });

View File

@ -1,3 +1,5 @@
import sinon from 'sinon';
import LoginState from 'services/authFlow/LoginState'; import LoginState from 'services/authFlow/LoginState';
import PasswordState from 'services/authFlow/PasswordState'; import PasswordState from 'services/authFlow/PasswordState';
@ -52,7 +54,7 @@ describe('LoginState', () => {
mock, mock,
'login', 'login',
sinon.match.same(payload) sinon.match.same(payload)
).returns({then() {}}); ).returns(Promise.resolve());
state.resolve(context, payload); state.resolve(context, payload);
}); });

View File

@ -1,3 +1,5 @@
import sinon from 'sinon';
import RecoverPasswordState from 'services/authFlow/RecoverPasswordState'; import RecoverPasswordState from 'services/authFlow/RecoverPasswordState';
import CompleteState from 'services/authFlow/CompleteState'; import CompleteState from 'services/authFlow/CompleteState';
import LoginState from 'services/authFlow/LoginState'; import LoginState from 'services/authFlow/LoginState';
@ -67,7 +69,7 @@ describe('RecoverPasswordState', () => {
mock, mock,
'recoverPassword', 'recoverPassword',
sinon.match(expectedPayload) sinon.match(expectedPayload)
).returns({then() {}}); ).returns(Promise.resolve());
state.resolve(context, expectedPayload); state.resolve(context, expectedPayload);
}); });

View File

@ -1,3 +1,5 @@
import sinon from 'sinon';
import RegisterState from 'services/authFlow/RegisterState'; import RegisterState from 'services/authFlow/RegisterState';
import CompleteState from 'services/authFlow/CompleteState'; import CompleteState from 'services/authFlow/CompleteState';
import ActivationState from 'services/authFlow/ActivationState'; import ActivationState from 'services/authFlow/ActivationState';
@ -52,7 +54,7 @@ describe('RegisterState', () => {
mock, mock,
'register', 'register',
sinon.match.same(payload) sinon.match.same(payload)
).returns({then() {}}); ).returns(Promise.resolve());
state.resolve(context, payload); state.resolve(context, payload);
}); });

View File

@ -1,3 +1,5 @@
import sinon from 'sinon';
import ResendActivationState from 'services/authFlow/ResendActivationState'; import ResendActivationState from 'services/authFlow/ResendActivationState';
import CompleteState from 'services/authFlow/CompleteState'; import CompleteState from 'services/authFlow/CompleteState';
import ActivationState from 'services/authFlow/ActivationState'; import ActivationState from 'services/authFlow/ActivationState';
@ -69,7 +71,7 @@ describe('ResendActivationState', () => {
mock, mock,
'resendActivation', 'resendActivation',
sinon.match.same(payload) sinon.match.same(payload)
).returns({then() {}}); ).returns(Promise.resolve());
state.resolve(context, payload); state.resolve(context, payload);
}); });