#125: make activation page available for all guests and innactive users

This commit is contained in:
SleepWalker 2016-06-05 15:06:14 +03:00
parent 446bfbaa29
commit 2737c6502b
6 changed files with 38 additions and 33 deletions

View File

@ -28,7 +28,7 @@ export default class User {
lang: '', lang: '',
goal: null, // the goal with wich user entered site goal: null, // the goal with wich user entered site
isGuest: true, isGuest: true,
isActive: true, isActive: false,
shouldChangePassword: false, // TODO: нужно ещё пробросить причину необходимости смены shouldChangePassword: false, // TODO: нужно ещё пробросить причину необходимости смены
passwordChangedAt: null, passwordChangedAt: null,
hasMojangUsernameCollision: false, hasMojangUsernameCollision: false,

View File

@ -5,6 +5,7 @@ import LoginState from './LoginState';
import OAuthState from './OAuthState'; import OAuthState from './OAuthState';
import ForgotPasswordState from './ForgotPasswordState'; import ForgotPasswordState from './ForgotPasswordState';
import RecoverPasswordState from './RecoverPasswordState'; import RecoverPasswordState from './RecoverPasswordState';
import ActivationState from './ActivationState';
import ResendActivationState from './ResendActivationState'; import ResendActivationState from './ResendActivationState';
// TODO: a way to unload service (when we are on account page) // TODO: a way to unload service (when we are on account page)
@ -70,12 +71,13 @@ export default class AuthFlow {
throw new Error('State is required'); throw new Error('State is required');
} }
// if (this.state instanceof state.constructor) { if (this.state instanceof state.constructor) {
// // already in this state // already in this state
// return; return;
// } }
this.state && this.state.leave(this); this.state && this.state.leave(this);
this.prevState = this.state;
this.state = state; this.state = state;
const resp = this.state.enter(this); const resp = this.state.enter(this);
@ -129,7 +131,6 @@ export default class AuthFlow {
case '/': case '/':
case '/login': case '/login':
case '/password': case '/password':
case '/activation':
case '/change-password': case '/change-password':
case '/oauth/permissions': case '/oauth/permissions':
case '/oauth/finish': case '/oauth/finish':
@ -138,6 +139,9 @@ export default class AuthFlow {
default: default:
switch (path.replace(/(.)\/.+/, '$1')) { // use only first part of an url switch (path.replace(/(.)\/.+/, '$1')) { // use only first part of an url
case '/activation':
this.setState(new ActivationState());
break;
case '/recover-password': case '/recover-password':
this.setState(new RecoverPasswordState()); this.setState(new RecoverPasswordState());
break; break;

View File

@ -7,7 +7,7 @@ export default class ResendActivationState extends AbstractState {
enter(context) { enter(context) {
const {user} = context.getState(); const {user} = context.getState();
if (user.isActive && !user.isGuest) { if (user.isActive) {
context.setState(new CompleteState()); context.setState(new CompleteState());
} else { } else {
context.navigate('/resend-activation'); context.navigate('/resend-activation');
@ -16,13 +16,11 @@ export default class ResendActivationState extends AbstractState {
resolve(context, payload) { resolve(context, payload) {
context.run('resendActivation', payload) context.run('resendActivation', payload)
.then(() => context.setState(new CompleteState())); .then(() => context.setState(new ActivationState()));
} }
goBack(context) { goBack(context) {
const {user} = context.getState(); if (context.prevState instanceof RegisterState) {
if (user.isGuest) {
context.setState(new RegisterState()); context.setState(new RegisterState());
} else { } else {
context.setState(new ActivationState()); context.setState(new ActivationState());

View File

@ -25,7 +25,6 @@ describe('ActivationState', () => {
it('should navigate to /activation', () => { it('should navigate to /activation', () => {
context.getState.returns({ context.getState.returns({
user: { user: {
isGuest: false,
isActive: false isActive: false
} }
}); });
@ -38,7 +37,6 @@ describe('ActivationState', () => {
it('should transition to complete state if account activated', () => { it('should transition to complete state if account activated', () => {
context.getState.returns({ context.getState.returns({
user: { user: {
isGuest: false,
isActive: true isActive: true
} }
}); });

View File

@ -5,6 +5,7 @@ import OAuthState from 'services/authFlow/OAuthState';
import RegisterState from 'services/authFlow/RegisterState'; import RegisterState from 'services/authFlow/RegisterState';
import RecoverPasswordState from 'services/authFlow/RecoverPasswordState'; import RecoverPasswordState from 'services/authFlow/RecoverPasswordState';
import ForgotPasswordState from 'services/authFlow/ForgotPasswordState'; import ForgotPasswordState from 'services/authFlow/ForgotPasswordState';
import ActivationState from 'services/authFlow/ActivationState';
import ResendActivationState from 'services/authFlow/ResendActivationState'; import ResendActivationState from 'services/authFlow/ResendActivationState';
import LoginState from 'services/authFlow/LoginState'; import LoginState from 'services/authFlow/LoginState';
@ -49,8 +50,11 @@ describe('AuthFlow', () => {
}); });
it('should call `leave` on previous state if any', () => { it('should call `leave` on previous state if any', () => {
const state1 = new AbstractState(); class State1 extends AbstractState {}
const state2 = new AbstractState(); class State2 extends AbstractState {}
const state1 = new State1();
const state2 = new State2();
const spy1 = sinon.spy(state1, 'leave'); const spy1 = sinon.spy(state1, 'leave');
const spy2 = sinon.spy(state2, 'leave'); const spy2 = sinon.spy(state2, 'leave');
@ -62,6 +66,20 @@ describe('AuthFlow', () => {
sinon.assert.notCalled(spy2); sinon.assert.notCalled(spy2);
}); });
it('should not change state, if current state is of same type', () => {
const state1 = new AbstractState();
const state2 = new AbstractState();
const spy1 = sinon.spy(state1, 'enter');
const spy2 = sinon.spy(state2, 'enter');
flow.setState(state1);
flow.setState(state2);
sinon.assert.calledWith(spy1, flow);
sinon.assert.calledOnce(spy1);
sinon.assert.notCalled(spy2);
});
it('should return promise, if #enter returns it', () => { it('should return promise, if #enter returns it', () => {
const state = new AbstractState(); const state = new AbstractState();
const expected = Promise.resolve(); const expected = Promise.resolve();
@ -169,7 +187,6 @@ describe('AuthFlow', () => {
'/': LoginState, '/': LoginState,
'/login': LoginState, '/login': LoginState,
'/password': LoginState, '/password': LoginState,
'/activation': LoginState,
'/change-password': LoginState, '/change-password': LoginState,
'/oauth/permissions': LoginState, '/oauth/permissions': LoginState,
'/oauth/finish': LoginState, '/oauth/finish': LoginState,
@ -178,6 +195,7 @@ describe('AuthFlow', () => {
'/recover-password': RecoverPasswordState, '/recover-password': RecoverPasswordState,
'/recover-password/key123': RecoverPasswordState, '/recover-password/key123': RecoverPasswordState,
'/forgot-password': ForgotPasswordState, '/forgot-password': ForgotPasswordState,
'/activation': ActivationState,
'/resend-activation': ResendActivationState '/resend-activation': ResendActivationState
}).forEach(([path, type]) => { }).forEach(([path, type]) => {
it(`should transition to ${type.name} if ${path}`, () => { it(`should transition to ${type.name} if ${path}`, () => {

View File

@ -26,7 +26,6 @@ describe('ResendActivationState', () => {
it('should navigate to /resend-activation', () => { it('should navigate to /resend-activation', () => {
context.getState.returns({ context.getState.returns({
user: { user: {
isGuest: false,
isActive: false isActive: false
} }
}); });
@ -40,7 +39,7 @@ describe('ResendActivationState', () => {
context.getState.returns({ context.getState.returns({
user: { user: {
isGuest: true, isGuest: true,
isActive: true isActive: false
} }
}); });
@ -52,7 +51,6 @@ describe('ResendActivationState', () => {
it('should transition to complete state if account activated', () => { it('should transition to complete state if account activated', () => {
context.getState.returns({ context.getState.returns({
user: { user: {
isGuest: false,
isActive: true isActive: true
} }
}); });
@ -80,7 +78,7 @@ describe('ResendActivationState', () => {
const promise = Promise.resolve(); const promise = Promise.resolve();
mock.expects('run').returns(promise); mock.expects('run').returns(promise);
expectState(mock, CompleteState); expectState(mock, ActivationState);
state.resolve(context); state.resolve(context);
@ -101,26 +99,15 @@ describe('ResendActivationState', () => {
describe('#goBack', () => { describe('#goBack', () => {
it('should transition to activation', () => { it('should transition to activation', () => {
context.getState.returns({
user: {
isGuest: false
}
});
expectState(mock, ActivationState); expectState(mock, ActivationState);
state.goBack(context); state.goBack(context);
}); });
it('should transition to register if guest', () => { it('should transition to register if it was active previousely', () => {
context.getState.returns({
user: {
isGuest: true
}
});
expectState(mock, RegisterState); expectState(mock, RegisterState);
context.prevState = new RegisterState();
state.goBack(context); state.goBack(context);
}); });
}); });