mirror of
https://github.com/elyby/accounts-frontend.git
synced 2025-05-31 14:11:58 +05:30
E2e tests for mfa. Fix a bug that made mfa impossible to disable
This commit is contained in:
@@ -38,9 +38,12 @@ export default class MfaDisable extends React.Component<
|
|||||||
onSubmit = (form: FormModel) => {
|
onSubmit = (form: FormModel) => {
|
||||||
return this.props
|
return this.props
|
||||||
.onSubmit(form, () => {
|
.onSubmit(form, () => {
|
||||||
const { totp } = form.serialize() as { totp: string };
|
const { totp, password } = form.serialize() as {
|
||||||
|
totp: string;
|
||||||
|
password?: string;
|
||||||
|
};
|
||||||
|
|
||||||
return disableMFA(this.context.userId, totp);
|
return disableMFA(this.context.userId, totp, password);
|
||||||
})
|
})
|
||||||
.then(() => this.props.onComplete())
|
.then(() => this.props.onComplete())
|
||||||
.catch(resp => {
|
.catch(resp => {
|
||||||
|
@@ -77,7 +77,11 @@ const linksByOs: {
|
|||||||
|
|
||||||
export default function OsInstruction({ os }: { os: OS }) {
|
export default function OsInstruction({ os }: { os: OS }) {
|
||||||
return (
|
return (
|
||||||
<div className={styles.osInstruction}>
|
<div
|
||||||
|
className={styles.osInstruction}
|
||||||
|
data-testid="os-instruction"
|
||||||
|
data-os={os}
|
||||||
|
>
|
||||||
<h3 className={styles.instructionTitle}>
|
<h3 className={styles.instructionTitle}>
|
||||||
<Message {...messages.installOnOfTheApps} />
|
<Message {...messages.installOnOfTheApps} />
|
||||||
</h3>
|
</h3>
|
||||||
|
@@ -15,7 +15,11 @@ export default function OsInstruction({
|
|||||||
onClick: (event: React.MouseEvent<any>) => void;
|
onClick: (event: React.MouseEvent<any>) => void;
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<div className={clsx(styles.osTile, className)} onClick={onClick}>
|
<div
|
||||||
|
className={clsx(styles.osTile, className)}
|
||||||
|
onClick={onClick}
|
||||||
|
data-testid="os-tile"
|
||||||
|
>
|
||||||
<img className={styles.osLogo} src={logo} alt={label} />
|
<img className={styles.osLogo} src={logo} alt={label} />
|
||||||
<div className={styles.osName}>{label}</div>
|
<div className={styles.osName}>{label}</div>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -40,7 +40,9 @@ export default function KeyForm({
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={profileForm.formRow}>
|
<div className={profileForm.formRow}>
|
||||||
<div className={styles.key}>{formattedSecret}</div>
|
<div className={styles.key} data-testid="secret">
|
||||||
|
{formattedSecret}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={profileForm.formRow}>
|
<div className={profileForm.formRow}>
|
||||||
|
@@ -48,4 +48,42 @@ describe('Profile — Change password', () => {
|
|||||||
cy.location('pathname').should('eq', '/');
|
cy.location('pathname').should('eq', '/');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should close password popup if form has errors', () => {
|
||||||
|
cy.login({ accounts: ['default'] }).then(({ accounts: [account] }) => {
|
||||||
|
cy.server();
|
||||||
|
cy.route({
|
||||||
|
method: 'POST',
|
||||||
|
url: `/api/v1/accounts/${account.id}/password`,
|
||||||
|
response: {
|
||||||
|
success: false,
|
||||||
|
errors: {
|
||||||
|
password: 'force popup to be shown',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}).as('password');
|
||||||
|
cy.visit('/');
|
||||||
|
|
||||||
|
openSectionByName('Password');
|
||||||
|
|
||||||
|
cy.location('pathname').should('eq', '/profile/change-password');
|
||||||
|
|
||||||
|
cy.get('[name=newPassword]').type(account.password);
|
||||||
|
// make a mistake to hide confirm popup in future
|
||||||
|
cy.get('[name=newRePassword]').type(`${account.password}lol`);
|
||||||
|
cy.get('[name=logoutAll]').should('be.checked');
|
||||||
|
cy.get('[type=submit]').click();
|
||||||
|
|
||||||
|
// disable response mocks
|
||||||
|
cy.route({
|
||||||
|
method: 'POST',
|
||||||
|
url: `/api/v1/accounts/${account.id}/password`,
|
||||||
|
}).as('password');
|
||||||
|
|
||||||
|
confirmWithPassword(account.password);
|
||||||
|
|
||||||
|
cy.getByTestId('password-request-form').should('not.be.visible');
|
||||||
|
cy.contains('The passwords does not match').should('be.visible');
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
182
tests-e2e/cypress/integration/profile/mfa.test.ts
Normal file
182
tests-e2e/cypress/integration/profile/mfa.test.ts
Normal file
@@ -0,0 +1,182 @@
|
|||||||
|
import {
|
||||||
|
openSectionByName,
|
||||||
|
getSectionByName,
|
||||||
|
confirmWithPassword,
|
||||||
|
} from './utils';
|
||||||
|
|
||||||
|
describe('Profile — mfa', () => {
|
||||||
|
it('should enable mfa', () => {
|
||||||
|
const totp = 'totp123';
|
||||||
|
|
||||||
|
cy.login({ accounts: ['default'] }).then(({ accounts: [account] }) => {
|
||||||
|
cy.server();
|
||||||
|
cy.route({
|
||||||
|
method: 'POST',
|
||||||
|
url: `/api/v1/accounts/${account.id}/two-factor-auth`,
|
||||||
|
response: { success: false, errors: { password: 'foo' } },
|
||||||
|
}).as('mfaSaved');
|
||||||
|
|
||||||
|
cy.visit('/');
|
||||||
|
|
||||||
|
getSectionByName('Two‑factor auth').should('contain', 'Disabled');
|
||||||
|
openSectionByName('Two‑factor auth');
|
||||||
|
|
||||||
|
cy.location('pathname').should('eq', '/profile/mfa');
|
||||||
|
|
||||||
|
assertOs('Google Play', 'android');
|
||||||
|
assertOs('App Store', 'ios');
|
||||||
|
assertOs('Windows Store', 'windows');
|
||||||
|
|
||||||
|
cy.contains('App has been installed').click();
|
||||||
|
|
||||||
|
cy.location('pathname').should('eq', '/profile/mfa/step2');
|
||||||
|
|
||||||
|
cy.getByTestId('secret').should('not.be.empty');
|
||||||
|
|
||||||
|
cy.contains('Ready').click();
|
||||||
|
|
||||||
|
cy.location('pathname').should('eq', '/profile/mfa/step3');
|
||||||
|
|
||||||
|
cy.get('[name=totp]').type(`${totp}{enter}`);
|
||||||
|
|
||||||
|
cy.wait('@mfaSaved')
|
||||||
|
.its('requestBody')
|
||||||
|
.should(
|
||||||
|
'eq',
|
||||||
|
new URLSearchParams({
|
||||||
|
totp,
|
||||||
|
password: '',
|
||||||
|
}).toString(),
|
||||||
|
);
|
||||||
|
|
||||||
|
cy.route({
|
||||||
|
method: 'POST',
|
||||||
|
url: `/api/v1/accounts/${account.id}/two-factor-auth`,
|
||||||
|
response: { success: true },
|
||||||
|
}).as('mfaSaved');
|
||||||
|
cy.route({
|
||||||
|
method: 'GET',
|
||||||
|
url: `/api/v1/accounts/${account.id}`,
|
||||||
|
response: {
|
||||||
|
id: 7,
|
||||||
|
uuid: '522e8c19-89d8-4a6d-a2ec-72ebb58c2dbe',
|
||||||
|
username: 'SleepWalker',
|
||||||
|
isOtpEnabled: true, // fake enabled mfa
|
||||||
|
registeredAt: 1475568334,
|
||||||
|
lang: 'en',
|
||||||
|
elyProfileLink: 'http://ely.by/u7',
|
||||||
|
email: 'danilenkos@auroraglobal.com',
|
||||||
|
isActive: true,
|
||||||
|
passwordChangedAt: 1476075696,
|
||||||
|
hasMojangUsernameCollision: true,
|
||||||
|
shouldAcceptRules: false,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
confirmWithPassword(account.password);
|
||||||
|
|
||||||
|
cy.wait('@mfaSaved')
|
||||||
|
.its('requestBody')
|
||||||
|
.should(
|
||||||
|
'eq',
|
||||||
|
new URLSearchParams({
|
||||||
|
totp,
|
||||||
|
password: account.password,
|
||||||
|
}).toString(),
|
||||||
|
);
|
||||||
|
|
||||||
|
cy.location('pathname').should('eq', '/');
|
||||||
|
getSectionByName('Two‑factor auth').should('contain', 'Enabled');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should disable mfa', () => {
|
||||||
|
const totp = 'totp123';
|
||||||
|
|
||||||
|
cy.login({ accounts: ['default'] }).then(({ accounts: [account] }) => {
|
||||||
|
cy.server();
|
||||||
|
cy.route({
|
||||||
|
method: 'GET',
|
||||||
|
url: `/api/v1/accounts/${account.id}`,
|
||||||
|
response: {
|
||||||
|
id: 7,
|
||||||
|
uuid: '522e8c19-89d8-4a6d-a2ec-72ebb58c2dbe',
|
||||||
|
username: 'SleepWalker',
|
||||||
|
isOtpEnabled: true, // fake enabled mfa
|
||||||
|
registeredAt: 1475568334,
|
||||||
|
lang: 'en',
|
||||||
|
elyProfileLink: 'http://ely.by/u7',
|
||||||
|
email: 'danilenkos@auroraglobal.com',
|
||||||
|
isActive: true,
|
||||||
|
passwordChangedAt: 1476075696,
|
||||||
|
hasMojangUsernameCollision: true,
|
||||||
|
shouldAcceptRules: false,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
cy.route({
|
||||||
|
method: 'DELETE',
|
||||||
|
url: `/api/v1/accounts/${account.id}/two-factor-auth`,
|
||||||
|
response: { success: false, errors: { password: 'foo' } },
|
||||||
|
}).as('mfaSaved');
|
||||||
|
|
||||||
|
cy.visit('/');
|
||||||
|
|
||||||
|
getSectionByName('Two‑factor auth').should('contain', 'Enabled');
|
||||||
|
openSectionByName('Two‑factor auth');
|
||||||
|
|
||||||
|
cy.location('pathname').should('eq', '/profile/mfa');
|
||||||
|
|
||||||
|
cy.contains('Disable').click();
|
||||||
|
|
||||||
|
cy.get('[name=totp]').type(`${totp}{enter}`);
|
||||||
|
|
||||||
|
cy.wait('@mfaSaved')
|
||||||
|
.its('requestBody')
|
||||||
|
.should(
|
||||||
|
'eq',
|
||||||
|
new URLSearchParams({
|
||||||
|
totp,
|
||||||
|
password: '',
|
||||||
|
}).toString(),
|
||||||
|
);
|
||||||
|
|
||||||
|
cy.route({
|
||||||
|
method: 'DELETE',
|
||||||
|
url: `/api/v1/accounts/${account.id}/two-factor-auth`,
|
||||||
|
response: { success: true },
|
||||||
|
}).as('mfaSaved');
|
||||||
|
// unmock accounts route
|
||||||
|
cy.route({
|
||||||
|
method: 'GET',
|
||||||
|
url: `/api/v1/accounts/${account.id}`,
|
||||||
|
});
|
||||||
|
|
||||||
|
confirmWithPassword(account.password);
|
||||||
|
|
||||||
|
cy.wait('@mfaSaved')
|
||||||
|
.its('requestBody')
|
||||||
|
.should(
|
||||||
|
'eq',
|
||||||
|
new URLSearchParams({
|
||||||
|
totp,
|
||||||
|
password: account.password,
|
||||||
|
}).toString(),
|
||||||
|
);
|
||||||
|
|
||||||
|
cy.location('pathname').should('eq', '/');
|
||||||
|
getSectionByName('Two‑factor auth').should('contain', 'Disabled');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
function assertOs(name: string, os: string) {
|
||||||
|
cy.getByTestId('os-tile')
|
||||||
|
.contains(name)
|
||||||
|
.click();
|
||||||
|
|
||||||
|
cy.getByTestId('os-instruction').should('have.attr', 'data-os', os);
|
||||||
|
|
||||||
|
cy.getByTestId('os-tile')
|
||||||
|
.contains(name)
|
||||||
|
.click();
|
||||||
|
}
|
@@ -1,10 +1,12 @@
|
|||||||
export function getSectionByName(name: string) {
|
export function getSectionByName(name: string) {
|
||||||
return cy.getByTestId('profile-item').contains(name);
|
return cy
|
||||||
|
.getByTestId('profile-item')
|
||||||
|
.contains(name)
|
||||||
|
.closest('[data-testid="profile-item"]');
|
||||||
}
|
}
|
||||||
|
|
||||||
export function openSectionByName(name: string) {
|
export function openSectionByName(name: string) {
|
||||||
return getSectionByName(name)
|
return getSectionByName(name)
|
||||||
.closest('[data-testid="profile-item"]')
|
|
||||||
.getByTestId('profile-action')
|
.getByTestId('profile-action')
|
||||||
.click();
|
.click();
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user