Add more auth/oauth/multi-acc related test cases

This commit is contained in:
SleepWalker 2019-12-28 11:28:25 +02:00
parent 0325f0aac4
commit 951f538ee5
8 changed files with 267 additions and 18 deletions

View File

@ -66,9 +66,10 @@ export class AccountSwitcher extends React.Component<Props> {
styles.accountSwitcher,
styles[`${skin}AccountSwitcher`],
)}
data-testid="account-switcher"
>
{highlightActiveAccount ? (
<div className={styles.item}>
{highlightActiveAccount && (
<div className={styles.item} data-testid="active-account">
<div
className={clsx(
styles.accountIcon,
@ -97,6 +98,7 @@ export class AccountSwitcher extends React.Component<Props> {
<div className={styles.link}>
<a
className={styles.link}
data-testid="logout-account"
onClick={this.onRemove(activeAccount)}
href="#"
>
@ -106,11 +108,13 @@ export class AccountSwitcher extends React.Component<Props> {
</div>
</div>
</div>
) : null}
)}
{available.map((account, index) => (
<div
className={clsx(styles.item, styles.accountSwitchItem)}
key={account.id}
data-e2e-account-id={account.id}
onClick={this.onSwitch(account)}
>
<div
@ -125,6 +129,7 @@ export class AccountSwitcher extends React.Component<Props> {
{allowLogout ? (
<div
className={styles.logoutIcon}
data-testid="logout-account"
onClick={this.onRemove(account)}
/>
) : (
@ -141,6 +146,7 @@ export class AccountSwitcher extends React.Component<Props> {
<Link to="/login" onClick={this.props.onAfterAction}>
<Button
color={COLOR_WHITE}
data-testid="add-account"
block
small
className={styles.addAccount}

View File

@ -89,10 +89,12 @@ class RootPage extends React.PureComponent<{
<Route path="/rules" component={RulesPage} />
<Route path="/dev" component={DevPage} />
{!user.isGuest && (
<PrivateRoute exact path="/" component={ProfilePage} />
)}
<AuthFlowRoute
exact
path="/"
key="indexPage"
component={user.isGuest ? AuthPage : ProfilePage}
/>
<AuthFlowRoute path="/" component={AuthPage} />
<Route component={PageNotFound} />

View File

@ -77,6 +77,7 @@ export default class AuthFlow implements AuthContext {
onReady: () => void;
navigate: (route: string, options: { replace?: boolean }) => void;
currentRequest: Request;
oAuthStateRestored = false;
dispatch: (action: { [key: string]: any }) => void;
getState: () => RootState;
@ -184,9 +185,6 @@ export default class AuthFlow implements AuthContext {
}
}
/**
* @returns {object} - current request object
*/
getRequest() {
return {
path: '',
@ -296,6 +294,12 @@ export default class AuthFlow implements AuthContext {
return;
}
if (this.oAuthStateRestored) {
return;
}
this.oAuthStateRestored = true;
try {
const data = JSON.parse(localStorage.getItem('oauthData'));
const expirationTime = 2 * 60 * 60 * 1000; // 2h

View File

@ -1,6 +1,6 @@
{
"account1": {
"id": "7",
"id": 7,
"username": "SleepWalker",
"email": "danilenkos@auroraglobal.com",
"login": "SleepWalker",

View File

@ -1,4 +1,6 @@
/* eslint-disable @typescript-eslint/camelcase */
import { account1 } from '../../fixtures/accounts.json';
const defaults = {
client_id: 'ely',
redirect_uri: 'http://ely.by/authorization/oauth',
@ -14,6 +16,29 @@ it('should complete oauth', () => {
cy.url().should('equal', 'https://ely.by/');
});
it('should restore previous oauthData if any', () => {
localStorage.setItem(
'oauthData',
JSON.stringify({
timestamp: Date.now() - 3600,
payload: {
clientId: 'ely',
redirectUrl: 'http://ely.by/authorization/oauth',
responseType: 'code',
description: null,
scope: 'account_info account_email',
loginHint: null,
state: null,
},
}),
);
cy.login({ accounts: ['default'] });
cy.visit('/');
cy.url().should('equal', 'https://ely.by/');
});
it('should ask to choose an account if user has multiple', () => {
cy.login({ accounts: ['default', 'default2'] }).then(
({ accounts: [account] }) => {
@ -72,6 +97,20 @@ it('should prompt for permissions', () => {
cy.url().should('match', /^http:\/\/localhost:8080\/?\?code=[^&]+&state=$/);
});
it('should allow sign in during oauth (guest oauth)', () => {
cy.visit(`/oauth2/v1/ely?${new URLSearchParams(defaults)}`);
cy.url().should('include', '/login');
cy.get('[name=login]').type(`${account1.login}{enter}`);
cy.url().should('include', '/password');
cy.get('[name=password]').type(`${account1.password}{enter}`);
cy.url().should('equal', 'https://ely.by/');
});
// TODO: enable, when backend api will return correct response on auth decline
xit('should redirect to error page, when permission request declined', () => {
cy.server();
@ -170,7 +209,28 @@ describe('login_hint', () => {
describe('prompts', () => {
it('should prompt for account', () => {
cy.login({ accounts: ['default'] });
cy.login({ accounts: ['default'] }).then(({ accounts: [account] }) => {
cy.visit(
`/oauth2/v1/ely?${new URLSearchParams({
...defaults,
prompt: 'select_account',
})}`,
);
cy.url().should('include', '/oauth/choose-account');
cy.getByTestId('auth-header').should('contain', 'Choose an account');
cy.getByTestId('auth-body')
.contains(account.email)
.click();
cy.url().should('equal', 'https://ely.by/');
});
});
it('should allow sign in with another account', () => {
cy.login({ accounts: ['default2'] });
cy.visit(
`/oauth2/v1/ely?${new URLSearchParams({
@ -181,7 +241,19 @@ describe('prompts', () => {
cy.url().should('include', '/oauth/choose-account');
cy.getByTestId('auth-header').should('contain', 'Choose an account');
cy.getByTestId('auth-controls')
.contains('another account')
.click();
cy.url().should('include', '/login');
cy.get('[name=login]').type(`${account1.login}{enter}`);
cy.url().should('include', '/password');
cy.get('[name=password]').type(`${account1.password}{enter}`);
cy.url().should('equal', 'https://ely.by/');
});
it('should prompt for permissions', () => {
@ -258,6 +330,33 @@ describe('prompts', () => {
);
});
});
it('should allow sign in during oauth (guest oauth)', () => {
cy.visit(
`/oauth2/v1/ely?${new URLSearchParams({
...defaults,
client_id: 'tlauncher',
redirect_uri: 'http://localhost:8080',
prompt: 'select_account,consent',
})}`,
);
cy.url().should('include', '/login');
cy.get('[name=login]').type(`${account1.login}{enter}`);
cy.url().should('include', '/password');
cy.get('[name=password]').type(`${account1.password}{enter}`);
assertPermissions();
cy.getByTestId('auth-controls')
.contains('Approve')
.click();
cy.url().should('match', /^http:\/\/localhost:8080\/?\?code=[^&]+&state=$/);
});
});
describe('static pages', () => {

View File

@ -1,8 +1,10 @@
import { account1 } from '../../fixtures/accounts.json';
import { account1, account2 } from '../../fixtures/accounts.json';
it('should sign in', () => {
cy.visit('/');
cy.url().should('include', '/login');
cy.get('[name=login]').type(`${account1.login}{enter}`);
cy.url().should('include', '/password');
@ -122,3 +124,129 @@ it('should sign in with totp', () => {
cy.location('pathname').should('eq', '/');
});
it('should allow logout', () => {
cy.login({ accounts: ['default'] });
cy.visit('/');
cy.getByTestId('toolbar')
.contains(account1.username)
.click();
cy.getByTestId('active-account')
.getByTestId('logout-account')
.click();
cy.location('pathname').should('eq', '/login');
cy.getByTestId('toolbar').should('contain', 'Join');
});
describe('multi account', () => {
it('should allow sign in with another account', () => {
cy.login({ accounts: ['default2'] });
cy.visit('/');
cy.getByTestId('toolbar')
.contains(account2.username)
.click();
cy.getByTestId('active-account').should('have.length', 1);
cy.get('[data-e2e-account-id]').should('have.length', 0);
cy.getByTestId('add-account').click();
cy.location('pathname').should('eq', '/login');
cy.get('[data-e2e-go-back]').should('exist');
cy.get('[name=login]').type(`${account1.login}{enter}`);
cy.url().should('include', '/password');
cy.get('[name=password]').type(`${account1.password}{enter}`);
cy.location('pathname').should('eq', '/');
cy.getByTestId('toolbar')
.contains(account1.username)
.click();
cy.getByTestId('active-account').should('have.length', 1);
cy.get('[data-e2e-account-id]').should('have.length', 1);
cy.get('[data-e2e-account-id]')
.getByTestId('logout-account')
.click();
});
it('should go back to profile from login screen', () => {
cy.login({ accounts: ['default'] });
cy.visit('/');
cy.getByTestId('toolbar')
.contains(account1.username)
.click();
cy.getByTestId('add-account').click();
cy.location('pathname').should('eq', '/login');
cy.get('[data-e2e-go-back]').click();
cy.location('pathname').should('eq', '/');
});
it('should allow logout active account', () => {
cy.login({ accounts: ['default', 'default2'] });
cy.visit('/');
cy.getByTestId('toolbar')
.contains(account1.username)
.click();
cy.getByTestId('active-account')
.getByTestId('logout-account')
.click();
cy.getByTestId('toolbar')
.contains(account2.username)
.click();
cy.get('[data-e2e-account-id]').should('have.length', 0);
});
it('should not allow log in the same account twice', () => {
cy.login({ accounts: ['default'] });
cy.visit('/');
cy.getByTestId('toolbar')
.contains(account1.username)
.click();
cy.getByTestId('active-account').should('have.length', 1);
cy.get('[data-e2e-account-id]').should('have.length', 0);
cy.getByTestId('add-account').click();
cy.location('pathname').should('eq', '/login');
cy.get('[data-e2e-go-back]').should('exist');
cy.get('[name=login]').type(`${account1.login}{enter}`);
cy.url().should('include', '/password');
cy.get('[name=password]').type(`${account1.password}{enter}`);
cy.location('pathname').should('eq', '/');
cy.getByTestId('toolbar')
.contains(account1.username)
.click();
cy.get('[data-e2e-account-id]').should('have.length', 0);
});
});

View File

@ -27,8 +27,8 @@ import { account1, account2 } from '../fixtures/accounts';
// Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... })
const accountsMap = {
default: account2,
default2: account1,
default: account1,
default2: account2,
};
Cypress.Commands.add(
@ -82,8 +82,18 @@ Cypress.Commands.add(
},
);
Cypress.Commands.add('getByTestId', (id, options) =>
cy.get(`[data-testid=${id}]`, options),
Cypress.Commands.add(
'getByTestId',
{ prevSubject: 'optional' },
(subject, id, options) => {
const selector = `[data-testid=${id}]`;
if (subject) {
return cy.wrap(subject.find(selector));
}
return cy.get(selector, options);
},
);
function createState(accounts) {