mirror of
https://github.com/elyby/accounts-frontend.git
synced 2025-02-23 18:57:37 +05:30
Fix the rest of the tests
This commit is contained in:
parent
f8ae8282ed
commit
5ca4c323c7
@ -71,7 +71,12 @@ class RootPage extends React.PureComponent<{
|
||||
>
|
||||
<div className={styles.header} data-testid="toolbar">
|
||||
<div className={styles.headerContent}>
|
||||
<Link to="/" className={styles.logo} onClick={onLogoClick}>
|
||||
<Link
|
||||
to="/"
|
||||
className={styles.logo}
|
||||
onClick={onLogoClick}
|
||||
data-testid="home-page"
|
||||
>
|
||||
<Message {...messages.siteName} />
|
||||
</Link>
|
||||
<div className={styles.userbar}>
|
||||
|
@ -68,7 +68,7 @@ describe('AuthFlow.functional', () => {
|
||||
it('should redirect guest / -> /login', () => {
|
||||
navigate('/');
|
||||
|
||||
expect(flow.navigate, 'was called once');
|
||||
expect(flow.navigate, 'was called twice');
|
||||
expect(flow.navigate, 'to have a call satisfying', ['/login']);
|
||||
});
|
||||
|
||||
@ -80,8 +80,12 @@ describe('AuthFlow.functional', () => {
|
||||
navigate('/login');
|
||||
navigate('/');
|
||||
|
||||
expect(flow.navigate, 'was called twice');
|
||||
expect(flow.navigate, 'to have a call satisfying', ['/login']);
|
||||
expect(flow.navigate, 'was called thrice');
|
||||
expect(flow.navigate, 'to have calls satisfying', [
|
||||
['/login'],
|
||||
['/login'],
|
||||
['/login'],
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -115,12 +115,6 @@ export default class AuthFlow implements AuthContext {
|
||||
options.replace = true;
|
||||
}
|
||||
|
||||
this.currentRequest = {
|
||||
path: route,
|
||||
params: {},
|
||||
query: new URLSearchParams(),
|
||||
};
|
||||
|
||||
if (this.replace) {
|
||||
this.replace(route);
|
||||
}
|
||||
@ -288,18 +282,18 @@ export default class AuthFlow implements AuthContext {
|
||||
*
|
||||
* @returns {bool} - whether oauth state is being restored
|
||||
*/
|
||||
private restoreOAuthState() {
|
||||
if (/^\/(register|oauth2)/.test(this.getRequest().path)) {
|
||||
// allow register or the new oauth requests
|
||||
return;
|
||||
}
|
||||
|
||||
private restoreOAuthState(): boolean {
|
||||
if (this.oAuthStateRestored) {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
this.oAuthStateRestored = true;
|
||||
|
||||
if (/^\/(register|oauth2)/.test(this.getRequest().path)) {
|
||||
// allow register or the new oauth requests
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
const data = JSON.parse(localStorage.getItem('oauthData'));
|
||||
const expirationTime = 2 * 60 * 60 * 1000; // 2h
|
||||
|
@ -105,7 +105,7 @@ export default class CompleteState extends AbstractState {
|
||||
(resp: { redirectUri: string }) => {
|
||||
// TODO: пусть в стейт попадает флаг или тип авторизации
|
||||
// вместо волшебства над редирект урлой
|
||||
if (resp.redirectUri.indexOf('static_page') === 0) {
|
||||
if (resp.redirectUri.includes('static_page')) {
|
||||
context.setState(new FinishState());
|
||||
} else {
|
||||
return context.run('redirect', resp.redirectUri);
|
||||
|
@ -12,7 +12,7 @@ export default class LoginState extends AbstractState {
|
||||
const { user } = context.getState();
|
||||
|
||||
const isUserAddsSecondAccount =
|
||||
!user.isGuest && /login|password/.test(context.getRequest().path); // TODO: improve me
|
||||
!user.isGuest && /login|password/.test(location.pathname); // TODO: improve me
|
||||
|
||||
// TODO: it may not allow user to leave password state till he click back or enters password
|
||||
if (login) {
|
||||
|
@ -15,9 +15,11 @@ import { AuthContext } from './AuthFlow';
|
||||
|
||||
export default class PasswordState extends AbstractState {
|
||||
enter(context: AuthContext) {
|
||||
const { login } = getCredentials(context.getState());
|
||||
const { login, isTotpRequired } = getCredentials(context.getState());
|
||||
|
||||
if (login) {
|
||||
if (isTotpRequired) {
|
||||
context.setState(new MfaState());
|
||||
} else if (login) {
|
||||
context.navigate('/password');
|
||||
} else {
|
||||
context.setState(new CompleteState());
|
||||
|
@ -9,31 +9,25 @@ singleAccount.accounts.available = singleAccount.accounts.available.filter(
|
||||
account => account.id === singleAccount.accounts.active,
|
||||
);
|
||||
|
||||
describe("when user's token and refreshToken are invalid", () => {
|
||||
before(() =>
|
||||
describe('User with invalid token and refreshToken', () => {
|
||||
before(() => {
|
||||
// ensure we always have one account with correct token
|
||||
cy.visit('/').then(() =>
|
||||
fetch('/api/authentication/login', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
|
||||
},
|
||||
body: `login=${account1.login}&password=${account1.password}`,
|
||||
})
|
||||
.then(resp => resp.json())
|
||||
.then(resp => {
|
||||
const account = multiAccount.accounts.available.find(
|
||||
item => item.username === account1.username,
|
||||
);
|
||||
cy.login({
|
||||
accounts: ['default'],
|
||||
updateState: false,
|
||||
rawApiResp: true,
|
||||
}).then(({ accounts: [resp] }) => {
|
||||
const account = multiAccount.accounts.available.find(
|
||||
item => item.username === account1.username,
|
||||
);
|
||||
|
||||
if (!account) {
|
||||
throw new Error('Can not find an account');
|
||||
}
|
||||
if (!account) {
|
||||
throw new Error('Can not find an account');
|
||||
}
|
||||
|
||||
account.token = resp.access_token;
|
||||
}),
|
||||
),
|
||||
);
|
||||
account.token = resp.access_token;
|
||||
});
|
||||
});
|
||||
|
||||
beforeEach(() =>
|
||||
localStorage.setItem('redux-storage', JSON.stringify(multiAccount)),
|
||||
@ -46,7 +40,7 @@ describe("when user's token and refreshToken are invalid", () => {
|
||||
|
||||
cy.get('[name="password"]').type(`${account2.password}{enter}`);
|
||||
|
||||
cy.location('pathname', { timeout: 15000 }).should('eq', '/');
|
||||
cy.location('pathname').should('eq', '/');
|
||||
cy.contains('account preferences');
|
||||
});
|
||||
|
||||
@ -60,18 +54,15 @@ describe("when user's token and refreshToken are invalid", () => {
|
||||
.contains('Ely.by')
|
||||
.click();
|
||||
|
||||
// TODO: currently we can not skip redirect to /, but we will in future
|
||||
cy.location('pathname', { timeout: 15000 }).should('eq', '/');
|
||||
cy.url({ timeout: 15000 }).should('include', '/password');
|
||||
cy.url().should('include', '/password');
|
||||
});
|
||||
|
||||
it('should allow select account', () => {
|
||||
// TODO: need a way to get valid token for one of the accounts
|
||||
cy.visit('/');
|
||||
|
||||
cy.get('[data-e2e-go-back]').click();
|
||||
|
||||
cy.url().should('include', '/choose-account');
|
||||
cy.location('pathname').should('eq', '/choose-account');
|
||||
|
||||
cy.get('[data-e2e-content]')
|
||||
.contains(account2.email)
|
||||
@ -80,8 +71,9 @@ describe("when user's token and refreshToken are invalid", () => {
|
||||
cy.get('[data-e2e-content]')
|
||||
.contains(account1.username)
|
||||
.click();
|
||||
cy.get('[name="password"]').type(`${account2.password}{enter}`);
|
||||
|
||||
cy.location('pathname', { timeout: 15000 }).should('eq', '/');
|
||||
cy.location('pathname').should('eq', '/');
|
||||
cy.contains('account preferences');
|
||||
});
|
||||
|
||||
@ -101,12 +93,20 @@ describe("when user's token and refreshToken are invalid", () => {
|
||||
});
|
||||
|
||||
it('should allow logout', () => {
|
||||
cy.server();
|
||||
cy.route({
|
||||
url: `/api/v1/accounts/${account2.id}`,
|
||||
}).as('account');
|
||||
cy.route({
|
||||
method: 'POST',
|
||||
url: '/api/authentication/logout',
|
||||
}).as('logout');
|
||||
|
||||
cy.visit('/');
|
||||
|
||||
cy.get('@fetch', { timeout: 15000 }).should(
|
||||
'be.calledWith',
|
||||
`/api/v1/accounts/${account2.id}`,
|
||||
);
|
||||
cy.wait('@account')
|
||||
.its('status')
|
||||
.should('eq', 401);
|
||||
|
||||
cy.getByTestId('toolbar')
|
||||
.contains(account2.username)
|
||||
@ -115,10 +115,7 @@ describe("when user's token and refreshToken are invalid", () => {
|
||||
.contains('Log out')
|
||||
.click();
|
||||
|
||||
cy.get('@fetch', { timeout: 15000 }).should(
|
||||
'be.calledWith',
|
||||
'/api/authentication/logout',
|
||||
);
|
||||
cy.wait('@logout');
|
||||
cy.getByTestId('toolbar')
|
||||
.contains(account2.email)
|
||||
.should('not.exist');
|
||||
@ -128,12 +125,16 @@ describe("when user's token and refreshToken are invalid", () => {
|
||||
});
|
||||
|
||||
it('should allow enter new login from choose account', () => {
|
||||
cy.server();
|
||||
cy.route({
|
||||
url: `/api/v1/accounts/${account2.id}`,
|
||||
}).as('account');
|
||||
|
||||
cy.visit('/');
|
||||
|
||||
cy.get('@fetch', { timeout: 15000 }).should(
|
||||
'be.calledWith',
|
||||
`/api/v1/accounts/${account2.id}`,
|
||||
);
|
||||
cy.wait('@account')
|
||||
.its('status')
|
||||
.should('eq', 401);
|
||||
|
||||
cy.url().should('include', '/password');
|
||||
|
||||
@ -152,9 +153,10 @@ describe("when user's token and refreshToken are invalid", () => {
|
||||
|
||||
cy.get('[name=password]').type(account1.password);
|
||||
cy.get('[name=rememberMe]').should('be.checked');
|
||||
cy.get('[type=submit]').should('have.length', 1); // wait till transition ends
|
||||
cy.get('[type=submit]').click();
|
||||
|
||||
cy.location('pathname', { timeout: 15000 }).should('eq', '/');
|
||||
cy.location('pathname').should('eq', '/');
|
||||
});
|
||||
|
||||
it('should allow logout from all accounts while choosing an account', () => {
|
||||
@ -195,7 +197,7 @@ describe("when user's token and refreshToken are invalid", () => {
|
||||
cy.wait(1000);
|
||||
cy.get('[name="password"]').type(`${account1.password}{enter}`);
|
||||
|
||||
cy.location('pathname', { timeout: 15000 }).should('eq', '/');
|
||||
cy.location('pathname').should('eq', '/');
|
||||
cy.contains('account preferences');
|
||||
});
|
||||
|
||||
@ -254,7 +256,7 @@ describe("when user's token and refreshToken are invalid", () => {
|
||||
|
||||
cy.get('[name=password]').type(`${account2.password}{enter}`);
|
||||
|
||||
cy.url({ timeout: 15000 }).should('contain', '/oauth/choose-account');
|
||||
cy.url().should('contain', '/oauth/choose-account');
|
||||
|
||||
cy.get('[data-e2e-content]')
|
||||
.contains(account2.username)
|
||||
|
@ -161,8 +161,8 @@ describe('OAuth', () => {
|
||||
...defaults,
|
||||
// suggest preferred username
|
||||
// https://docs.ely.by/ru/oauth.html#id3
|
||||
login_hint: account.id,
|
||||
})}`,
|
||||
login_hint: String(account.id),
|
||||
}).toString()}`,
|
||||
);
|
||||
|
||||
cy.url().should('equal', 'https://ely.by/');
|
||||
@ -368,6 +368,12 @@ describe('OAuth', () => {
|
||||
|
||||
describe('static pages', () => {
|
||||
it('should authenticate using static page', () => {
|
||||
cy.server();
|
||||
cy.route({
|
||||
method: 'POST',
|
||||
url: '/api/oauth2/v1/complete**',
|
||||
}).as('complete');
|
||||
|
||||
cy.login({ accounts: ['default'] });
|
||||
|
||||
cy.visit(
|
||||
@ -378,10 +384,18 @@ describe('OAuth', () => {
|
||||
})}`,
|
||||
);
|
||||
|
||||
cy.wait('@complete');
|
||||
|
||||
cy.url().should('include', 'oauth/finish#{%22auth_code%22:');
|
||||
});
|
||||
|
||||
it('should authenticate using static page with code', () => {
|
||||
cy.server();
|
||||
cy.route({
|
||||
method: 'POST',
|
||||
url: '/api/oauth2/v1/complete**',
|
||||
}).as('complete');
|
||||
|
||||
cy.login({ accounts: ['default'] });
|
||||
|
||||
cy.visit(
|
||||
@ -392,6 +406,8 @@ describe('OAuth', () => {
|
||||
})}`,
|
||||
);
|
||||
|
||||
cy.wait('@complete');
|
||||
|
||||
cy.url().should('include', 'oauth/finish#{%22auth_code%22:');
|
||||
|
||||
cy.getByTestId('oauth-code-container').should(
|
||||
@ -404,25 +420,21 @@ describe('OAuth', () => {
|
||||
// https://github.com/cypress-io/cypress/issues/2752
|
||||
cy.getByTestId('oauth-code-container')
|
||||
.contains('Copy')
|
||||
.click({
|
||||
// TODO: forcing, because currently we have needless re-renders, that causing
|
||||
// button to disappear for some time and to be unclickable
|
||||
force: true,
|
||||
});
|
||||
.click();
|
||||
});
|
||||
});
|
||||
|
||||
function assertPermissions() {
|
||||
cy.url().should('include', '/oauth/permissions');
|
||||
|
||||
cy.getByTestId('auth-header').should('contain', 'Application permissions');
|
||||
cy.getByTestId('auth-body').should(
|
||||
'contain',
|
||||
'Access to your profile data (except E‑mail)',
|
||||
);
|
||||
cy.getByTestId('auth-body').should(
|
||||
'contain',
|
||||
'Access to your E‑mail address',
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
function assertPermissions() {
|
||||
cy.url().should('include', '/oauth/permissions');
|
||||
|
||||
cy.getByTestId('auth-header').should('contain', 'Application permissions');
|
||||
cy.getByTestId('auth-body').should(
|
||||
'contain',
|
||||
'Access to your profile data (except E‑mail)',
|
||||
);
|
||||
cy.getByTestId('auth-body').should(
|
||||
'contain',
|
||||
'Access to your E‑mail address',
|
||||
);
|
||||
}
|
||||
|
@ -46,6 +46,16 @@ describe('Sign in / Log out', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('should force to /login page', () => {
|
||||
cy.visit('/');
|
||||
|
||||
cy.location('pathname').should('eq', '/login');
|
||||
|
||||
cy.getByTestId('home-page').click();
|
||||
|
||||
cy.location('pathname').should('eq', '/login');
|
||||
});
|
||||
|
||||
it('should sign in without remember me', () => {
|
||||
cy.visit('/');
|
||||
|
||||
|
14
tests-e2e/cypress/support/index.d.ts
vendored
14
tests-e2e/cypress/support/index.d.ts
vendored
@ -30,8 +30,20 @@ declare namespace Cypress {
|
||||
/**
|
||||
* Whether return raw api response without any conversion. Defaults to: `false`
|
||||
*/
|
||||
rawApiResp?: boolean;
|
||||
rawApiResp?: false;
|
||||
}): Promise<{ accounts: TAccount[] }>;
|
||||
login(options: {
|
||||
accounts: AccountAlias[];
|
||||
updateState?: boolean;
|
||||
rawApiResp: true;
|
||||
}): Promise<{
|
||||
accounts: {
|
||||
success: true;
|
||||
access_token: string;
|
||||
expires_in: number;
|
||||
refresh_token: string;
|
||||
}[];
|
||||
}>;
|
||||
|
||||
getByTestId<S = any>(
|
||||
id: string,
|
||||
|
Loading…
x
Reference in New Issue
Block a user