mirror of
https://github.com/elyby/accounts-frontend.git
synced 2024-11-30 02:32:58 +05:30
Тесты для стейтов AuthFlow
This commit is contained in:
parent
1e9b48bd9b
commit
21bbba399f
@ -65,7 +65,7 @@
|
||||
"less-loader": "^2.0.0",
|
||||
"mocha": "^2.2.5",
|
||||
"node-sass": "^3.4.2",
|
||||
"phantomjs": "^1.9.18",
|
||||
"phantomjs-prebuilt": "^2.0.0",
|
||||
"postcss-loader": "^0.8.0",
|
||||
"react-addons-test-utils": "^0.14.3",
|
||||
"sass-loader": "^3.1.2",
|
||||
|
@ -10,14 +10,13 @@ import ForgotPasswordState from './ForgotPasswordState';
|
||||
|
||||
const availableActions = {
|
||||
...actions,
|
||||
updateUser
|
||||
updateUser,
|
||||
redirect(url) {
|
||||
location.href = url;
|
||||
}
|
||||
};
|
||||
|
||||
export default class AuthFlow {
|
||||
constructor(states) {
|
||||
this.states = states;
|
||||
}
|
||||
|
||||
setStore(store) {
|
||||
this.navigate = (route) => {
|
||||
const {routing} = this.getState();
|
||||
|
@ -9,14 +9,11 @@ export default class CompleteState extends AbstractState {
|
||||
constructor(options = {}) {
|
||||
super(options);
|
||||
|
||||
if ('accept' in options) {
|
||||
this.isPermissionsAccepted = options.accept;
|
||||
this.isUserReviewedPermissions = true;
|
||||
}
|
||||
this.isPermissionsAccepted = options.accept;
|
||||
}
|
||||
|
||||
enter(context) {
|
||||
const {auth, user} = context.getState();
|
||||
const {auth = {}, user} = context.getState();
|
||||
|
||||
if (user.isGuest) {
|
||||
context.setState(new LoginState());
|
||||
@ -28,18 +25,19 @@ export default class CompleteState extends AbstractState {
|
||||
if (auth.oauth.code) {
|
||||
context.setState(new FinishState());
|
||||
} else {
|
||||
let data = {};
|
||||
if (this.isUserReviewedPermissions) {
|
||||
const data = {};
|
||||
if (typeof this.isPermissionsAccepted !== 'undefined') {
|
||||
data.accept = this.isPermissionsAccepted;
|
||||
}
|
||||
context.run('oAuthComplete', data).then((resp) => {
|
||||
if (resp.redirectUri.startsWith('static_page')) {
|
||||
// TODO: пусть в стейт попадает флаг или тип авторизации
|
||||
// вместо волшебства над редирект урлой
|
||||
if (resp.redirectUri.indexOf('static_page') === 0) {
|
||||
context.setState(new FinishState());
|
||||
} else {
|
||||
location.href = resp.redirectUri;
|
||||
context.run('redirect', resp.redirectUri);
|
||||
}
|
||||
}, (resp) => {
|
||||
// TODO
|
||||
if (resp.unauthorized) {
|
||||
context.setState(new LoginState());
|
||||
} else if (resp.acceptRequired) {
|
||||
|
@ -1,6 +1,6 @@
|
||||
import AbstractState from './AbstractState';
|
||||
|
||||
export default class CompleteState extends AbstractState {
|
||||
export default class FinishState extends AbstractState {
|
||||
enter(context) {
|
||||
context.navigate('/oauth/finish');
|
||||
}
|
||||
|
11
tests/.eslintrc.json
Normal file
11
tests/.eslintrc.json
Normal file
@ -0,0 +1,11 @@
|
||||
{
|
||||
"env": {
|
||||
"browser": true,
|
||||
"es6": true,
|
||||
"mocha": true
|
||||
},
|
||||
|
||||
"globals": {
|
||||
"sinon": true
|
||||
}
|
||||
}
|
4
tests/index.js
Normal file
4
tests/index.js
Normal file
@ -0,0 +1,4 @@
|
||||
// require all modules ending in "_test" from the
|
||||
// current directory and all subdirectories
|
||||
var testsContext = require.context(".", true, /\.test\.js$/);
|
||||
testsContext.keys().forEach(testsContext);
|
86
tests/services/authFlow/ActivationState.test.js
Normal file
86
tests/services/authFlow/ActivationState.test.js
Normal file
@ -0,0 +1,86 @@
|
||||
import ActivationState from 'services/authFlow/ActivationState';
|
||||
import CompleteState from 'services/authFlow/CompleteState';
|
||||
|
||||
import { bootstrap, expectState, expectNavigate, expectRun } from './helpers';
|
||||
|
||||
describe('ActivationState', () => {
|
||||
let state;
|
||||
let context;
|
||||
let mock;
|
||||
|
||||
beforeEach(() => {
|
||||
state = new ActivationState();
|
||||
|
||||
const data = bootstrap();
|
||||
context = data.context;
|
||||
mock = data.mock;
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
mock.verify();
|
||||
});
|
||||
|
||||
describe('#enter', () => {
|
||||
it('should navigate to /activation', () => {
|
||||
context.getState.returns({
|
||||
user: {
|
||||
isGuest: false,
|
||||
isActive: false
|
||||
}
|
||||
});
|
||||
|
||||
expectNavigate(mock, '/activation');
|
||||
|
||||
state.enter(context);
|
||||
});
|
||||
|
||||
it('should transition to complete state if account activated', () => {
|
||||
context.getState.returns({
|
||||
user: {
|
||||
isGuest: false,
|
||||
isActive: true
|
||||
}
|
||||
});
|
||||
|
||||
expectState(mock, CompleteState);
|
||||
|
||||
state.enter(context);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#resolve', () => {
|
||||
it('should call activate with payload', () => {
|
||||
const payload = {};
|
||||
|
||||
expectRun(
|
||||
mock,
|
||||
'activate',
|
||||
sinon.match.same(payload)
|
||||
).returns({then() {}});
|
||||
|
||||
state.resolve(context, payload);
|
||||
});
|
||||
|
||||
it('should transition to complete state on success', () => {
|
||||
const promise = Promise.resolve();
|
||||
|
||||
mock.expects('run').returns(promise);
|
||||
expectState(mock, CompleteState);
|
||||
|
||||
state.resolve(context);
|
||||
|
||||
return promise;
|
||||
});
|
||||
|
||||
it('should NOT transition to complete state on fail', () => {
|
||||
const promise = Promise.reject();
|
||||
|
||||
mock.expects('run').returns(promise);
|
||||
mock.expects('setState').never();
|
||||
|
||||
state.resolve(context);
|
||||
|
||||
return promise.catch(mock.verify.bind(mock));
|
||||
});
|
||||
});
|
||||
});
|
86
tests/services/authFlow/ChangePasswordState.test.js
Normal file
86
tests/services/authFlow/ChangePasswordState.test.js
Normal file
@ -0,0 +1,86 @@
|
||||
import ChangePasswordState from 'services/authFlow/ChangePasswordState';
|
||||
import CompleteState from 'services/authFlow/CompleteState';
|
||||
|
||||
import { bootstrap, expectState, expectNavigate, expectRun } from './helpers';
|
||||
|
||||
describe('ChangePasswordState', () => {
|
||||
let state;
|
||||
let context;
|
||||
let mock;
|
||||
|
||||
beforeEach(() => {
|
||||
state = new ChangePasswordState();
|
||||
|
||||
const data = bootstrap();
|
||||
context = data.context;
|
||||
mock = data.mock;
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
mock.verify();
|
||||
});
|
||||
|
||||
describe('#enter', () => {
|
||||
it('should navigate to /change-password', () => {
|
||||
context.getState.returns({
|
||||
user: {isGuest: true}
|
||||
});
|
||||
|
||||
expectNavigate(mock, '/change-password');
|
||||
|
||||
state.enter(context);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#resolve', () => {
|
||||
it('should call changePassword with payload', () => {
|
||||
const payload = {};
|
||||
|
||||
expectRun(
|
||||
mock,
|
||||
'changePassword',
|
||||
sinon.match.same(payload)
|
||||
).returns({then() {}});
|
||||
|
||||
state.resolve(context, payload);
|
||||
});
|
||||
|
||||
it('should transition to complete state on success', () => {
|
||||
const promise = Promise.resolve();
|
||||
|
||||
mock.expects('run').returns(promise);
|
||||
expectState(mock, CompleteState);
|
||||
|
||||
state.resolve(context);
|
||||
|
||||
return promise;
|
||||
});
|
||||
|
||||
it('should NOT transition to complete state on fail', () => {
|
||||
const promise = Promise.reject();
|
||||
|
||||
mock.expects('run').returns(promise);
|
||||
mock.expects('setState').never();
|
||||
|
||||
state.resolve(context);
|
||||
|
||||
return promise.catch(mock.verify.bind(mock));
|
||||
});
|
||||
});
|
||||
|
||||
describe('#reject', () => {
|
||||
it('should transition to complete state and mark that password should not be changed', () => {
|
||||
expectRun(
|
||||
mock,
|
||||
'updateUser',
|
||||
sinon.match({
|
||||
shouldChangePassword: false
|
||||
})
|
||||
);
|
||||
|
||||
expectState(mock, CompleteState);
|
||||
|
||||
state.reject(context);
|
||||
});
|
||||
});
|
||||
});
|
305
tests/services/authFlow/CompleteState.test.js
Normal file
305
tests/services/authFlow/CompleteState.test.js
Normal file
@ -0,0 +1,305 @@
|
||||
import CompleteState from 'services/authFlow/CompleteState';
|
||||
import LoginState from 'services/authFlow/LoginState';
|
||||
import ActivationState from 'services/authFlow/ActivationState';
|
||||
import ChangePasswordState from 'services/authFlow/ChangePasswordState';
|
||||
import FinishState from 'services/authFlow/FinishState';
|
||||
import PermissionsState from 'services/authFlow/PermissionsState';
|
||||
|
||||
import { bootstrap, expectState, expectNavigate, expectRun } from './helpers';
|
||||
|
||||
describe('CompleteState', () => {
|
||||
let state;
|
||||
let context;
|
||||
let mock;
|
||||
|
||||
beforeEach(() => {
|
||||
state = new CompleteState();
|
||||
|
||||
const data = bootstrap();
|
||||
context = data.context;
|
||||
mock = data.mock;
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
mock.verify();
|
||||
});
|
||||
|
||||
describe('#enter', () => {
|
||||
it('should navigate to / for authenticated', () => {
|
||||
context.getState.returns({
|
||||
user: {
|
||||
isActive: true,
|
||||
isGuest: false
|
||||
},
|
||||
auth: {}
|
||||
});
|
||||
|
||||
expectNavigate(mock, '/');
|
||||
|
||||
state.enter(context);
|
||||
});
|
||||
|
||||
it('should transition to login for guests', () => {
|
||||
context.getState.returns({
|
||||
user: {
|
||||
isGuest: true
|
||||
},
|
||||
auth: {}
|
||||
});
|
||||
|
||||
expectState(mock, LoginState);
|
||||
|
||||
state.enter(context);
|
||||
});
|
||||
|
||||
it('should transition to activation if account is not activated', () => {
|
||||
context.getState.returns({
|
||||
user: {
|
||||
isGuest: false
|
||||
},
|
||||
auth: {}
|
||||
});
|
||||
|
||||
expectState(mock, ActivationState);
|
||||
|
||||
state.enter(context);
|
||||
});
|
||||
|
||||
it('should transition to change-password if shouldChangePassword', () => {
|
||||
context.getState.returns({
|
||||
user: {
|
||||
shouldChangePassword: true,
|
||||
isActive: true,
|
||||
isGuest: false
|
||||
},
|
||||
auth: {}
|
||||
});
|
||||
|
||||
expectState(mock, ChangePasswordState);
|
||||
|
||||
state.enter(context);
|
||||
});
|
||||
|
||||
it('should transition to activation with higher priority than shouldChangePassword', () => {
|
||||
context.getState.returns({
|
||||
user: {
|
||||
shouldChangePassword: true,
|
||||
isGuest: false
|
||||
},
|
||||
auth: {}
|
||||
});
|
||||
|
||||
expectState(mock, ActivationState);
|
||||
|
||||
state.enter(context);
|
||||
});
|
||||
|
||||
it('should transition to finish state if code is present', () => {
|
||||
context.getState.returns({
|
||||
user: {
|
||||
isActive: true,
|
||||
isGuest: false
|
||||
},
|
||||
auth: {
|
||||
oauth: {
|
||||
clientId: 'ely.by',
|
||||
code: 'XXX'
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
expectState(mock, FinishState);
|
||||
|
||||
state.enter(context);
|
||||
});
|
||||
});
|
||||
|
||||
describe('oAuthComplete', () => {
|
||||
it('should run oAuthComplete', () => {
|
||||
context.getState.returns({
|
||||
user: {
|
||||
isActive: true,
|
||||
isGuest: false
|
||||
},
|
||||
auth: {
|
||||
oauth: {
|
||||
clientId: 'ely.by'
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
expectRun(
|
||||
mock,
|
||||
'oAuthComplete',
|
||||
sinon.match.object
|
||||
).returns({then() {}});
|
||||
|
||||
state.enter(context);
|
||||
});
|
||||
|
||||
it('should listen for auth success/failure', () => {
|
||||
context.getState.returns({
|
||||
user: {
|
||||
isActive: true,
|
||||
isGuest: false
|
||||
},
|
||||
auth: {
|
||||
oauth: {
|
||||
clientId: 'ely.by'
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
expectRun(
|
||||
mock,
|
||||
'oAuthComplete',
|
||||
sinon.match.object
|
||||
).returns({then(success, fail) {
|
||||
expect(success).to.be.a('function');
|
||||
expect(fail).to.be.a('function');
|
||||
}});
|
||||
|
||||
state.enter(context);
|
||||
});
|
||||
|
||||
it('should transition run redirect by default', () => {
|
||||
const expectedUrl = 'foo/bar';
|
||||
const promise = Promise.resolve({redirectUri: expectedUrl});
|
||||
|
||||
context.getState.returns({
|
||||
user: {
|
||||
isActive: true,
|
||||
isGuest: false
|
||||
},
|
||||
auth: {
|
||||
oauth: {
|
||||
clientId: 'ely.by'
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
expectRun(
|
||||
mock,
|
||||
'oAuthComplete',
|
||||
sinon.match.object
|
||||
).returns(promise);
|
||||
expectRun(
|
||||
mock,
|
||||
'redirect',
|
||||
expectedUrl
|
||||
);
|
||||
|
||||
state.enter(context);
|
||||
|
||||
return promise.catch(mock.verify.bind(mock));
|
||||
});
|
||||
|
||||
const testOAuth = (type, resp, expectedInstance) => {
|
||||
const promise = Promise[type](resp);
|
||||
|
||||
context.getState.returns({
|
||||
user: {
|
||||
isActive: true,
|
||||
isGuest: false
|
||||
},
|
||||
auth: {
|
||||
oauth: {
|
||||
clientId: 'ely.by'
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
expectRun(
|
||||
mock,
|
||||
'oAuthComplete',
|
||||
sinon.match.object
|
||||
).returns(promise);
|
||||
expectState(mock, expectedInstance);
|
||||
|
||||
state.enter(context);
|
||||
|
||||
return promise.catch(mock.verify.bind(mock));
|
||||
};
|
||||
|
||||
it('should transition to finish state if rejected with static_page', () => {
|
||||
return testOAuth('resolve', {redirectUri: 'static_page'}, FinishState);
|
||||
});
|
||||
|
||||
it('should transition to finish state if rejected with static_page_with_code', () => {
|
||||
return testOAuth('resolve', {redirectUri: 'static_page_with_code'}, FinishState);
|
||||
});
|
||||
|
||||
it('should transition to login state if rejected with unauthorized', () => {
|
||||
return testOAuth('reject', {unauthorized: true}, LoginState);
|
||||
});
|
||||
|
||||
it('should transition to permissions state if rejected with acceptRequired', () => {
|
||||
return testOAuth('reject', {acceptRequired: true}, PermissionsState);
|
||||
});
|
||||
})
|
||||
|
||||
describe('permissions accept', () => {
|
||||
it('should set flags, when user accepted permissions', () => {
|
||||
state = new CompleteState();
|
||||
expect(state.isPermissionsAccepted).to.be.undefined;
|
||||
|
||||
state = new CompleteState({accept: undefined});
|
||||
expect(state.isPermissionsAccepted).to.be.undefined;
|
||||
|
||||
state = new CompleteState({accept: true});
|
||||
expect(state.isPermissionsAccepted).to.be.true;
|
||||
|
||||
state = new CompleteState({accept: false});
|
||||
expect(state.isPermissionsAccepted).to.be.false;
|
||||
});
|
||||
|
||||
it('should run oAuthComplete passing accept: true', () => {
|
||||
const expected = {accept: true};
|
||||
|
||||
state = new CompleteState(expected);
|
||||
context.getState.returns({
|
||||
user: {
|
||||
isActive: true,
|
||||
isGuest: false
|
||||
},
|
||||
auth: {
|
||||
oauth: {
|
||||
clientId: 'ely.by'
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
mock.expects('run').once().withExactArgs(
|
||||
'oAuthComplete',
|
||||
sinon.match(expected)
|
||||
).returns({then() {}});
|
||||
|
||||
state.enter(context);
|
||||
});
|
||||
|
||||
it('should run oAuthComplete passing accept: false', () => {
|
||||
const expected = {accept: false};
|
||||
|
||||
state = new CompleteState(expected);
|
||||
context.getState.returns({
|
||||
user: {
|
||||
isActive: true,
|
||||
isGuest: false
|
||||
},
|
||||
auth: {
|
||||
oauth: {
|
||||
clientId: 'ely.by'
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
expectRun(
|
||||
mock,
|
||||
'oAuthComplete',
|
||||
sinon.match(expected)
|
||||
).returns({then() {}});
|
||||
|
||||
state.enter(context);
|
||||
});
|
||||
});
|
||||
});
|
29
tests/services/authFlow/FinishState.test.js
Normal file
29
tests/services/authFlow/FinishState.test.js
Normal file
@ -0,0 +1,29 @@
|
||||
import FinishState from 'services/authFlow/FinishState';
|
||||
|
||||
import { bootstrap, expectNavigate } from './helpers';
|
||||
|
||||
describe('FinishState', () => {
|
||||
let state;
|
||||
let context;
|
||||
let mock;
|
||||
|
||||
beforeEach(() => {
|
||||
state = new FinishState();
|
||||
|
||||
const data = bootstrap();
|
||||
context = data.context;
|
||||
mock = data.mock;
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
mock.verify();
|
||||
});
|
||||
|
||||
describe('#enter', () => {
|
||||
it('should navigate to /oauth/finish', () => {
|
||||
expectNavigate(mock, '/oauth/finish');
|
||||
|
||||
state.enter(context);
|
||||
});
|
||||
});
|
||||
});
|
97
tests/services/authFlow/LoginState.test.js
Normal file
97
tests/services/authFlow/LoginState.test.js
Normal file
@ -0,0 +1,97 @@
|
||||
import LoginState from 'services/authFlow/LoginState';
|
||||
import PasswordState from 'services/authFlow/PasswordState';
|
||||
import ForgotPasswordState from 'services/authFlow/ForgotPasswordState';
|
||||
|
||||
import { bootstrap, expectState, expectNavigate, expectRun } from './helpers';
|
||||
|
||||
describe('LoginState', () => {
|
||||
let state;
|
||||
let context;
|
||||
let mock;
|
||||
|
||||
beforeEach(() => {
|
||||
state = new LoginState();
|
||||
|
||||
const data = bootstrap();
|
||||
context = data.context;
|
||||
mock = data.mock;
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
mock.verify();
|
||||
});
|
||||
|
||||
describe('#enter', () => {
|
||||
it('should navigate to /login', () => {
|
||||
context.getState.returns({
|
||||
user: {isGuest: true}
|
||||
});
|
||||
|
||||
expectNavigate(mock, '/login');
|
||||
|
||||
state.enter(context);
|
||||
});
|
||||
|
||||
const testTransitionToPassword = (user) => {
|
||||
context.getState.returns({
|
||||
user: user
|
||||
});
|
||||
|
||||
expectState(mock, PasswordState);
|
||||
|
||||
state.enter(context);
|
||||
};
|
||||
|
||||
it('should transition to password if has email', () => {
|
||||
testTransitionToPassword({email: 'foo'});
|
||||
});
|
||||
|
||||
it('should transition to password if has username', () => {
|
||||
testTransitionToPassword({username: 'foo'});
|
||||
});
|
||||
});
|
||||
|
||||
describe('#resolve', () => {
|
||||
it('should call login with email or username', () => {
|
||||
const payload = {};
|
||||
|
||||
expectRun(
|
||||
mock,
|
||||
'login',
|
||||
sinon.match.same(payload)
|
||||
).returns({then() {}});
|
||||
|
||||
state.resolve(context, payload);
|
||||
});
|
||||
|
||||
it('should transition to password state on successfull login (first phase)', () => {
|
||||
const promise = Promise.resolve();
|
||||
|
||||
mock.expects('run').returns(promise);
|
||||
expectState(mock, PasswordState);
|
||||
|
||||
state.resolve(context);
|
||||
|
||||
return promise;
|
||||
});
|
||||
|
||||
it('should NOT transition to password state on fail', () => {
|
||||
const promise = Promise.reject();
|
||||
|
||||
mock.expects('run').returns(promise);
|
||||
mock.expects('setState').never();
|
||||
|
||||
state.resolve(context);
|
||||
|
||||
return promise.catch(mock.verify.bind(mock));
|
||||
});
|
||||
});
|
||||
|
||||
describe('#reject', () => {
|
||||
it('should transition to forgot password state', () => {
|
||||
expectState(mock, ForgotPasswordState);
|
||||
|
||||
state.reject(context);
|
||||
});
|
||||
});
|
||||
});
|
67
tests/services/authFlow/OAuthState.test.js
Normal file
67
tests/services/authFlow/OAuthState.test.js
Normal file
@ -0,0 +1,67 @@
|
||||
import OAuthState from 'services/authFlow/OAuthState';
|
||||
import CompleteState from 'services/authFlow/CompleteState';
|
||||
|
||||
import { bootstrap, expectState, expectRun } from './helpers';
|
||||
|
||||
describe('OAuthState', () => {
|
||||
let state;
|
||||
let context;
|
||||
let mock;
|
||||
|
||||
beforeEach(() => {
|
||||
state = new OAuthState();
|
||||
|
||||
const data = bootstrap();
|
||||
context = data.context;
|
||||
mock = data.mock;
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
mock.verify();
|
||||
});
|
||||
|
||||
describe('#enter', () => {
|
||||
it('should run oAuthValidate', () => {
|
||||
const query = {
|
||||
client_id: 'client_id',
|
||||
redirect_uri: 'redirect_uri',
|
||||
response_type: 'response_type',
|
||||
scope: 'scope',
|
||||
state: 'state'
|
||||
};
|
||||
|
||||
context.getState.returns({
|
||||
routing: {location: {query}}
|
||||
});
|
||||
|
||||
expectRun(
|
||||
mock,
|
||||
'oAuthValidate',
|
||||
sinon.match({
|
||||
clientId: query.client_id,
|
||||
redirectUrl: query.redirect_uri,
|
||||
responseType: query.response_type,
|
||||
scope: query.scope,
|
||||
state: query.state
|
||||
})
|
||||
).returns({then() {}});
|
||||
|
||||
state.enter(context);
|
||||
});
|
||||
|
||||
it('should transition to complete state on success', () => {
|
||||
const promise = Promise.resolve();
|
||||
|
||||
context.getState.returns({
|
||||
routing: {location: {query: {}}}
|
||||
});
|
||||
|
||||
mock.expects('run').returns(promise);
|
||||
expectState(mock, CompleteState);
|
||||
|
||||
state.enter(context);
|
||||
|
||||
return promise;
|
||||
});
|
||||
});
|
||||
});
|
119
tests/services/authFlow/PasswordState.test.js
Normal file
119
tests/services/authFlow/PasswordState.test.js
Normal file
@ -0,0 +1,119 @@
|
||||
import PasswordState from 'services/authFlow/PasswordState';
|
||||
import CompleteState from 'services/authFlow/CompleteState';
|
||||
import LoginState from 'services/authFlow/LoginState';
|
||||
import ForgotPasswordState from 'services/authFlow/ForgotPasswordState';
|
||||
|
||||
import { bootstrap, expectState, expectNavigate, expectRun } from './helpers';
|
||||
|
||||
describe('PasswordState', () => {
|
||||
let state;
|
||||
let context;
|
||||
let mock;
|
||||
|
||||
beforeEach(() => {
|
||||
state = new PasswordState();
|
||||
|
||||
const data = bootstrap();
|
||||
context = data.context;
|
||||
mock = data.mock;
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
mock.verify();
|
||||
});
|
||||
|
||||
describe('#enter', () => {
|
||||
it('should navigate to /password', () => {
|
||||
context.getState.returns({
|
||||
user: {isGuest: true}
|
||||
});
|
||||
|
||||
expectNavigate(mock, '/password');
|
||||
|
||||
state.enter(context);
|
||||
});
|
||||
|
||||
it('should transition to complete if not guest', () => {
|
||||
context.getState.returns({
|
||||
user: {isGuest: false}
|
||||
});
|
||||
|
||||
expectState(mock, CompleteState);
|
||||
|
||||
state.enter(context);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#resolve', () => {
|
||||
(() => {
|
||||
const expectedLogin = 'login';
|
||||
const expectedPassword = 'password';
|
||||
|
||||
const testWith = (user) => {
|
||||
it(`should call login with email or username and password. User: ${JSON.stringify(user)}`, () => {
|
||||
context.getState.returns({user});
|
||||
|
||||
expectRun(
|
||||
mock,
|
||||
'login',
|
||||
sinon.match({
|
||||
login: expectedLogin,
|
||||
password: expectedPassword
|
||||
})
|
||||
).returns({then() {}});
|
||||
|
||||
state.resolve(context, {password: expectedPassword});
|
||||
});
|
||||
};
|
||||
|
||||
testWith({
|
||||
email: expectedLogin
|
||||
});
|
||||
|
||||
testWith({
|
||||
username: expectedLogin
|
||||
});
|
||||
|
||||
testWith({
|
||||
email: expectedLogin,
|
||||
username: expectedLogin
|
||||
});
|
||||
});
|
||||
|
||||
it('should transition to complete state on successfull login', () => {
|
||||
const promise = Promise.resolve();
|
||||
const expectedLogin = 'login';
|
||||
const expectedPassword = 'password';
|
||||
|
||||
context.getState.returns({
|
||||
user: {
|
||||
email: expectedLogin
|
||||
}
|
||||
});
|
||||
|
||||
mock.expects('run').returns(promise);
|
||||
expectState(mock, CompleteState);
|
||||
|
||||
state.resolve(context, {password: expectedPassword});
|
||||
|
||||
return promise;
|
||||
});
|
||||
});
|
||||
|
||||
describe('#reject', () => {
|
||||
it('should transition to forgot password state', () => {
|
||||
expectState(mock, ForgotPasswordState);
|
||||
|
||||
state.reject(context);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#goBack', () => {
|
||||
it('should transition to forgot password state', () => {
|
||||
expectRun(mock, 'logout');
|
||||
expectState(mock, LoginState);
|
||||
|
||||
state.goBack(context);
|
||||
});
|
||||
});
|
||||
});
|
56
tests/services/authFlow/PermissionsState.test.js
Normal file
56
tests/services/authFlow/PermissionsState.test.js
Normal file
@ -0,0 +1,56 @@
|
||||
import PermissionsState from 'services/authFlow/PermissionsState';
|
||||
import CompleteState from 'services/authFlow/CompleteState';
|
||||
|
||||
import { bootstrap, expectNavigate } from './helpers';
|
||||
|
||||
describe('PermissionsState', () => {
|
||||
let state;
|
||||
let context;
|
||||
let mock;
|
||||
|
||||
beforeEach(() => {
|
||||
state = new PermissionsState();
|
||||
|
||||
const data = bootstrap();
|
||||
context = data.context;
|
||||
mock = data.mock;
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
mock.verify();
|
||||
});
|
||||
|
||||
describe('#enter', () => {
|
||||
it('should navigate to /oauth/permissions', () => {
|
||||
expectNavigate(mock, '/oauth/permissions');
|
||||
|
||||
state.enter(context);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#resolve', () => {
|
||||
it('should transition to complete state with acceptance', () => {
|
||||
mock.expects('setState').once().withExactArgs(
|
||||
sinon.match.instanceOf(CompleteState)
|
||||
.and(
|
||||
sinon.match.has('isPermissionsAccepted', true)
|
||||
)
|
||||
);
|
||||
|
||||
state.resolve(context);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#reject', () => {
|
||||
it('should transition to complete state without acceptance', () => {
|
||||
mock.expects('setState').once().withExactArgs(
|
||||
sinon.match.instanceOf(CompleteState)
|
||||
.and(
|
||||
sinon.match.has('isPermissionsAccepted', false)
|
||||
)
|
||||
);
|
||||
|
||||
state.reject(context);
|
||||
});
|
||||
});
|
||||
});
|
81
tests/services/authFlow/RegisterState.test.js
Normal file
81
tests/services/authFlow/RegisterState.test.js
Normal file
@ -0,0 +1,81 @@
|
||||
import RegisterState from 'services/authFlow/RegisterState';
|
||||
import CompleteState from 'services/authFlow/CompleteState';
|
||||
|
||||
import { bootstrap, expectState, expectNavigate, expectRun } from './helpers';
|
||||
|
||||
describe('RegisterState', () => {
|
||||
let state;
|
||||
let context;
|
||||
let mock;
|
||||
|
||||
beforeEach(() => {
|
||||
state = new RegisterState();
|
||||
|
||||
const data = bootstrap();
|
||||
context = data.context;
|
||||
mock = data.mock;
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
mock.verify();
|
||||
});
|
||||
|
||||
describe('#enter', () => {
|
||||
it('should navigate to /register', () => {
|
||||
context.getState.returns({
|
||||
user: {isGuest: true}
|
||||
});
|
||||
|
||||
expectNavigate(mock, '/register');
|
||||
|
||||
state.enter(context);
|
||||
});
|
||||
|
||||
it('should transition to complete if not guest', () => {
|
||||
context.getState.returns({
|
||||
user: {isGuest: false}
|
||||
});
|
||||
|
||||
expectState(mock, CompleteState);
|
||||
|
||||
state.enter(context);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#resolve', () => {
|
||||
it('should register on resolve', () => {
|
||||
const payload = {};
|
||||
|
||||
expectRun(
|
||||
mock,
|
||||
'register',
|
||||
sinon.match.same(payload)
|
||||
).returns({then() {}});
|
||||
|
||||
state.resolve(context, payload);
|
||||
});
|
||||
|
||||
it('should transition to complete after register', () => {
|
||||
const payload = {};
|
||||
const promise = Promise.resolve();
|
||||
|
||||
mock.expects('run').returns(promise);
|
||||
expectState(mock, CompleteState);
|
||||
|
||||
state.resolve(context, payload);
|
||||
|
||||
return promise;
|
||||
});
|
||||
|
||||
it('should NOT transition to complete if register fails', () => {
|
||||
const promise = Promise.reject();
|
||||
|
||||
mock.expects('run').returns(promise);
|
||||
mock.expects('setState').never();
|
||||
|
||||
state.resolve(context);
|
||||
|
||||
return promise.catch(mock.verify.bind(mock));
|
||||
});
|
||||
});
|
||||
});
|
29
tests/services/authFlow/helpers.js
Normal file
29
tests/services/authFlow/helpers.js
Normal file
@ -0,0 +1,29 @@
|
||||
export function bootstrap() {
|
||||
const context = {
|
||||
getState: sinon.stub(),
|
||||
run() {},
|
||||
setState() {},
|
||||
navigate() {}
|
||||
};
|
||||
|
||||
const mock = sinon.mock(context);
|
||||
mock.expects('run').never();
|
||||
mock.expects('navigate').never();
|
||||
mock.expects('setState').never();
|
||||
|
||||
return {context, mock};
|
||||
}
|
||||
|
||||
export function expectState(mock, state) {
|
||||
return mock.expects('setState').once().withExactArgs(
|
||||
sinon.match.instanceOf(state)
|
||||
);
|
||||
}
|
||||
|
||||
export function expectNavigate(mock, route) {
|
||||
return mock.expects('navigate').once().withExactArgs(route);
|
||||
}
|
||||
|
||||
export function expectRun(mock, ...args) {
|
||||
return mock.expects('run').once().withExactArgs(...args);
|
||||
}
|
@ -15,6 +15,7 @@ var iconfontImporter = require('./webpack/node-sass-iconfont-importer');
|
||||
* https://github.com/glenjamin/ultimate-hot-reloading-example ( обратить внимание на плагины babel )
|
||||
* https://github.com/gajus/react-css-modules ( + BrowserSync)
|
||||
* https://github.com/reactuate/reactuate
|
||||
* https://github.com/insin/nwb
|
||||
*
|
||||
* Inspiration projects:
|
||||
* https://github.com/davezuko/react-redux-starter-kit
|
||||
|
Loading…
Reference in New Issue
Block a user