mirror of
https://github.com/elyby/accounts-frontend.git
synced 2024-10-01 15:57:46 +05:30
#245: cover loginHint in CompleteState with tests
This commit is contained in:
parent
acf912d979
commit
7178ac0b88
@ -17,7 +17,7 @@ export default class CompleteState extends AbstractState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
enter(context) {
|
enter(context) {
|
||||||
const {auth = {}, user, accounts} = context.getState();
|
const {auth = {}, user} = context.getState();
|
||||||
|
|
||||||
if (user.isGuest) {
|
if (user.isGuest) {
|
||||||
context.setState(new LoginState());
|
context.setState(new LoginState());
|
||||||
@ -26,67 +26,75 @@ export default class CompleteState extends AbstractState {
|
|||||||
} else if (user.shouldAcceptRules) {
|
} else if (user.shouldAcceptRules) {
|
||||||
context.setState(new AcceptRulesState());
|
context.setState(new AcceptRulesState());
|
||||||
} else if (auth.oauth && auth.oauth.clientId) {
|
} else if (auth.oauth && auth.oauth.clientId) {
|
||||||
let isSwitcherEnabled = auth.isSwitcherEnabled;
|
return this.processOAuth(context);
|
||||||
|
|
||||||
if (auth.oauth.loginHint) {
|
|
||||||
const account = accounts.available.filter((account) =>
|
|
||||||
account.id === auth.oauth.loginHint * 1
|
|
||||||
|| account.email === auth.oauth.loginHint
|
|
||||||
|| account.username === auth.oauth.loginHint
|
|
||||||
)[0];
|
|
||||||
|
|
||||||
if (account) {
|
|
||||||
// disable switching, because we are know the account, user must be authorized with
|
|
||||||
context.run('setAccountSwitcher', false);
|
|
||||||
isSwitcherEnabled = false;
|
|
||||||
|
|
||||||
if (account.id !== accounts.active.id) {
|
|
||||||
// lets switch user to an account, that is needed for auth
|
|
||||||
return context.run('authenticate', account)
|
|
||||||
.then(() => context.setState(new CompleteState()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isSwitcherEnabled
|
|
||||||
&& (accounts.available.length > 1
|
|
||||||
|| auth.oauth.prompt.includes(PROMPT_ACCOUNT_CHOOSE)
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
context.setState(new ChooseAccountState());
|
|
||||||
} else if (auth.oauth.code) {
|
|
||||||
context.setState(new FinishState());
|
|
||||||
} else {
|
|
||||||
const data = {};
|
|
||||||
if (typeof this.isPermissionsAccepted !== 'undefined') {
|
|
||||||
data.accept = this.isPermissionsAccepted;
|
|
||||||
} else if (auth.oauth.acceptRequired || auth.oauth.prompt.includes(PROMPT_PERMISSIONS)) {
|
|
||||||
context.setState(new PermissionsState());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// TODO: it seams that oAuthComplete may be a separate state
|
|
||||||
return context.run('oAuthComplete', data).then((resp) => {
|
|
||||||
// TODO: пусть в стейт попадает флаг или тип авторизации
|
|
||||||
// вместо волшебства над редирект урлой
|
|
||||||
if (resp.redirectUri.indexOf('static_page') === 0) {
|
|
||||||
context.setState(new FinishState());
|
|
||||||
} else {
|
|
||||||
return new Promise(() => {
|
|
||||||
// do not resolve promise to make loader visible and
|
|
||||||
// overcome app rendering
|
|
||||||
context.run('redirect', resp.redirectUri);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}, (resp) => {
|
|
||||||
if (resp.unauthorized) {
|
|
||||||
context.setState(new LoginState());
|
|
||||||
} else if (resp.acceptRequired) {
|
|
||||||
context.setState(new PermissionsState());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
context.navigate('/');
|
context.navigate('/');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
processOAuth(context) {
|
||||||
|
const {auth = {}, accounts} = context.getState();
|
||||||
|
|
||||||
|
let isSwitcherEnabled = auth.isSwitcherEnabled;
|
||||||
|
const loginHint = auth.oauth.loginHint;
|
||||||
|
|
||||||
|
if (loginHint) {
|
||||||
|
const account = accounts.available.filter((account) =>
|
||||||
|
account.id === loginHint * 1
|
||||||
|
|| account.email === loginHint
|
||||||
|
|| account.username === loginHint
|
||||||
|
)[0];
|
||||||
|
|
||||||
|
if (account) {
|
||||||
|
// disable switching, because we are know the account, user must be authorized with
|
||||||
|
context.run('setAccountSwitcher', false);
|
||||||
|
isSwitcherEnabled = false;
|
||||||
|
|
||||||
|
if (account.id !== accounts.active.id) {
|
||||||
|
// lets switch user to an account, that is needed for auth
|
||||||
|
return context.run('authenticate', account)
|
||||||
|
.then(() => context.setState(new CompleteState()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isSwitcherEnabled
|
||||||
|
&& (accounts.available.length > 1
|
||||||
|
|| auth.oauth.prompt.includes(PROMPT_ACCOUNT_CHOOSE)
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
context.setState(new ChooseAccountState());
|
||||||
|
} else if (auth.oauth.code) {
|
||||||
|
context.setState(new FinishState());
|
||||||
|
} else {
|
||||||
|
const data = {};
|
||||||
|
if (typeof this.isPermissionsAccepted !== 'undefined') {
|
||||||
|
data.accept = this.isPermissionsAccepted;
|
||||||
|
} else if (auth.oauth.acceptRequired || auth.oauth.prompt.includes(PROMPT_PERMISSIONS)) {
|
||||||
|
context.setState(new PermissionsState());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: it seams that oAuthComplete may be a separate state
|
||||||
|
return context.run('oAuthComplete', data).then((resp) => {
|
||||||
|
// TODO: пусть в стейт попадает флаг или тип авторизации
|
||||||
|
// вместо волшебства над редирект урлой
|
||||||
|
if (resp.redirectUri.indexOf('static_page') === 0) {
|
||||||
|
context.setState(new FinishState());
|
||||||
|
} else {
|
||||||
|
return new Promise(() => {
|
||||||
|
// do not resolve promise to make loader visible and
|
||||||
|
// overcome app rendering
|
||||||
|
context.run('redirect', resp.redirectUri);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, (resp) => {
|
||||||
|
if (resp.unauthorized) {
|
||||||
|
context.setState(new LoginState());
|
||||||
|
} else if (resp.acceptRequired) {
|
||||||
|
context.setState(new PermissionsState());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import expect from 'unexpected';
|
import expect from 'unexpected';
|
||||||
|
import sinon from 'sinon';
|
||||||
|
|
||||||
import CompleteState from 'services/authFlow/CompleteState';
|
import CompleteState from 'services/authFlow/CompleteState';
|
||||||
import LoginState from 'services/authFlow/LoginState';
|
import LoginState from 'services/authFlow/LoginState';
|
||||||
@ -135,7 +136,7 @@ describe('CompleteState', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('oAuthComplete', () => {
|
describe('when user completes oauth', () => {
|
||||||
it('should run oAuthComplete', () => {
|
it('should run oAuthComplete', () => {
|
||||||
context.getState.returns({
|
context.getState.returns({
|
||||||
user: {
|
user: {
|
||||||
@ -185,7 +186,7 @@ describe('CompleteState', () => {
|
|||||||
state.enter(context);
|
state.enter(context);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should transition run redirect by default', () => {
|
it('should run redirect by default', () => {
|
||||||
const expectedUrl = 'foo/bar';
|
const expectedUrl = 'foo/bar';
|
||||||
const promise = Promise.resolve({redirectUri: expectedUrl});
|
const promise = Promise.resolve({redirectUri: expectedUrl});
|
||||||
|
|
||||||
@ -261,6 +262,122 @@ describe('CompleteState', () => {
|
|||||||
it('should transition to permissions state if rejected with acceptRequired', () =>
|
it('should transition to permissions state if rejected with acceptRequired', () =>
|
||||||
testOAuth('reject', {acceptRequired: true}, PermissionsState)
|
testOAuth('reject', {acceptRequired: true}, PermissionsState)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
describe('when loginHint is set', () => {
|
||||||
|
const testSuccessLoginHint = (field) => {
|
||||||
|
const account = {
|
||||||
|
id: 9,
|
||||||
|
email: 'some@email.com',
|
||||||
|
username: 'thatUsername'
|
||||||
|
};
|
||||||
|
|
||||||
|
context.getState.returns({
|
||||||
|
user: {
|
||||||
|
isActive: true,
|
||||||
|
isGuest: false
|
||||||
|
},
|
||||||
|
accounts: {
|
||||||
|
available: [
|
||||||
|
account
|
||||||
|
],
|
||||||
|
active: {
|
||||||
|
id: 100
|
||||||
|
}
|
||||||
|
},
|
||||||
|
auth: {
|
||||||
|
oauth: {
|
||||||
|
clientId: 'ely.by',
|
||||||
|
loginHint: account[field],
|
||||||
|
prompt: []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
expectRun(mock, 'setAccountSwitcher', false);
|
||||||
|
expectRun(mock, 'authenticate', account)
|
||||||
|
.returns(Promise.resolve());
|
||||||
|
expectState(mock, CompleteState);
|
||||||
|
|
||||||
|
return expect(state.enter(context), 'to be fulfilled');
|
||||||
|
};
|
||||||
|
|
||||||
|
it('should authenticate account if id matches', () =>
|
||||||
|
testSuccessLoginHint('id')
|
||||||
|
);
|
||||||
|
|
||||||
|
it('should authenticate account if email matches', () =>
|
||||||
|
testSuccessLoginHint('email')
|
||||||
|
);
|
||||||
|
|
||||||
|
it('should authenticate account if username matches', () =>
|
||||||
|
testSuccessLoginHint('username')
|
||||||
|
);
|
||||||
|
|
||||||
|
it('should not authenticate if account is already authenticated', () => {
|
||||||
|
const account = {
|
||||||
|
id: 9,
|
||||||
|
email: 'some@email.com',
|
||||||
|
username: 'thatUsername'
|
||||||
|
};
|
||||||
|
|
||||||
|
context.getState.returns({
|
||||||
|
user: {
|
||||||
|
isActive: true,
|
||||||
|
isGuest: false
|
||||||
|
},
|
||||||
|
accounts: {
|
||||||
|
available: [
|
||||||
|
account
|
||||||
|
],
|
||||||
|
active: account
|
||||||
|
},
|
||||||
|
auth: {
|
||||||
|
oauth: {
|
||||||
|
clientId: 'ely.by',
|
||||||
|
loginHint: account.id,
|
||||||
|
prompt: []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
expectRun(mock, 'setAccountSwitcher', false);
|
||||||
|
expectRun(mock, 'oAuthComplete', {})
|
||||||
|
.returns({then: () => Promise.resolve()});
|
||||||
|
|
||||||
|
return expect(state.enter(context), 'to be fulfilled');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not authenticate if account was not found and continue auth', () => {
|
||||||
|
const account = {
|
||||||
|
id: 9,
|
||||||
|
email: 'some@email.com',
|
||||||
|
username: 'thatUsername'
|
||||||
|
};
|
||||||
|
|
||||||
|
context.getState.returns({
|
||||||
|
user: {
|
||||||
|
isActive: true,
|
||||||
|
isGuest: false
|
||||||
|
},
|
||||||
|
accounts: {
|
||||||
|
available: [{id: 1}],
|
||||||
|
active: {id: 1}
|
||||||
|
},
|
||||||
|
auth: {
|
||||||
|
oauth: {
|
||||||
|
clientId: 'ely.by',
|
||||||
|
loginHint: account.id,
|
||||||
|
prompt: []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
expectRun(mock, 'oAuthComplete', {})
|
||||||
|
.returns({then: () => Promise.resolve()});
|
||||||
|
|
||||||
|
return expect(state.enter(context), 'to be fulfilled');
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('permissions accept', () => {
|
describe('permissions accept', () => {
|
||||||
|
@ -2,6 +2,8 @@
|
|||||||
* A helpers for testing states in isolation from AuthFlow
|
* A helpers for testing states in isolation from AuthFlow
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import sinon from 'sinon';
|
||||||
|
|
||||||
export function bootstrap() {
|
export function bootstrap() {
|
||||||
const context = {
|
const context = {
|
||||||
getState: sinon.stub(),
|
getState: sinon.stub(),
|
||||||
@ -28,9 +30,9 @@ export function expectState(mock, state) {
|
|||||||
export function expectNavigate(mock, route, options) {
|
export function expectNavigate(mock, route, options) {
|
||||||
if (options) {
|
if (options) {
|
||||||
return mock.expects('navigate').once().withExactArgs(route, sinon.match(options));
|
return mock.expects('navigate').once().withExactArgs(route, sinon.match(options));
|
||||||
} else {
|
|
||||||
return mock.expects('navigate').once().withExactArgs(route);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return mock.expects('navigate').once().withExactArgs(route);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function expectRun(mock, ...args) {
|
export function expectRun(mock, ...args) {
|
||||||
|
Loading…
Reference in New Issue
Block a user