summaryrefslogtreecommitdiff
path: root/web-ui
diff options
context:
space:
mode:
authorSriram Viswanathan <sriramv@thoughtworks.com>2017-04-04 17:57:48 -0300
committerSriram Viswanathan <sriramv@thoughtworks.com>2017-04-04 17:57:48 -0300
commit50d64627a924234ef5858b82bee8c9c33fb08f09 (patch)
treef2af020cd63976db53c8d2325b2b9ba4beb6063e /web-ui
parent92523b7c8ceb8f3b7adf282e1b9e573ce5b37f04 (diff)
[#938] Adds backup account step to forgot password flow
with @anikarni
Diffstat (limited to 'web-ui')
-rw-r--r--web-ui/app/locales/en_US/translation.json4
-rw-r--r--web-ui/app/locales/pt_BR/translation.json4
-rw-r--r--web-ui/src/account_recovery/backup_account_step/backup_account_step.js35
-rw-r--r--web-ui/src/account_recovery/backup_account_step/backup_account_step.spec.js17
-rw-r--r--web-ui/src/account_recovery/new_password_form/new_password_form.js4
-rw-r--r--web-ui/src/account_recovery/new_password_form/new_password_form.spec.js14
-rw-r--r--web-ui/src/account_recovery/page.js8
-rw-r--r--web-ui/src/account_recovery/page.spec.js28
8 files changed, 103 insertions, 11 deletions
diff --git a/web-ui/app/locales/en_US/translation.json b/web-ui/app/locales/en_US/translation.json
index 8607f590..4b27c9ef 100644
--- a/web-ui/app/locales/en_US/translation.json
+++ b/web-ui/app/locales/en_US/translation.json
@@ -103,6 +103,10 @@
"input-label1": "create new password",
"input-label2": "confirm your new password"
},
+ "backup-account-step": {
+ "image-description": "Backup Account - Step 4 of 4",
+ "title": "Wait! What if you forget your password again?"
+ },
"button-next": "Next",
"back": "Back to previous step"
},
diff --git a/web-ui/app/locales/pt_BR/translation.json b/web-ui/app/locales/pt_BR/translation.json
index c4b3d9e7..fe0711cb 100644
--- a/web-ui/app/locales/pt_BR/translation.json
+++ b/web-ui/app/locales/pt_BR/translation.json
@@ -103,6 +103,10 @@
"input-label1": "digite a nova senha",
"input-label2": "confirme a nova senha"
},
+ "backup-account-step": {
+ "image-description": "E-mail de Recuperação - Passo 4 de 4",
+ "title": "Opa! E se você esquecer sua senha de novo?"
+ },
"button-next": "Próximo",
"back": "Voltar ao passo anterior"
},
diff --git a/web-ui/src/account_recovery/backup_account_step/backup_account_step.js b/web-ui/src/account_recovery/backup_account_step/backup_account_step.js
new file mode 100644
index 00000000..b4dcbaaf
--- /dev/null
+++ b/web-ui/src/account_recovery/backup_account_step/backup_account_step.js
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2017 ThoughtWorks, Inc.
+ *
+ * Pixelated is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Pixelated is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with Pixelated. If not, see <http://www.gnu.org/licenses/>.
+ */
+import React from "react";
+import {translate} from "react-i18next";
+
+export const BackupAccountStep = ({ t }) => (
+ <div className='account-recovery-form backup-account'>
+ <img
+ className='account-recovery-progress'
+ src='/public/images/account-recovery/step_4.svg'
+ alt={t('account-recovery.backup-account-step.image-description')}
+ />
+ <h1>{t('account-recovery.backup-account-step.title')}</h1>
+ </div>
+);
+
+BackupAccountStep.propTypes = {
+ t: React.PropTypes.func.isRequired
+};
+
+export default translate('', { wait: true })(BackupAccountStep);
diff --git a/web-ui/src/account_recovery/backup_account_step/backup_account_step.spec.js b/web-ui/src/account_recovery/backup_account_step/backup_account_step.spec.js
new file mode 100644
index 00000000..3b27b369
--- /dev/null
+++ b/web-ui/src/account_recovery/backup_account_step/backup_account_step.spec.js
@@ -0,0 +1,17 @@
+import { shallow } from 'enzyme';
+import expect from 'expect';
+import React from 'react';
+import { BackupAccountStep } from './backup_account_step';
+
+describe('BackupAccountStep', () => {
+ let backupAccountStep;
+
+ beforeEach(() => {
+ const mockTranslations = key => key;
+ backupAccountStep = shallow(<BackupAccountStep t={mockTranslations} />);
+ });
+
+ it('renders title for backup account step', () => {
+ expect(backupAccountStep.find('h1').text()).toEqual('account-recovery.backup-account-step.title');
+ });
+});
diff --git a/web-ui/src/account_recovery/new_password_form/new_password_form.js b/web-ui/src/account_recovery/new_password_form/new_password_form.js
index 4c418900..e7f689e8 100644
--- a/web-ui/src/account_recovery/new_password_form/new_password_form.js
+++ b/web-ui/src/account_recovery/new_password_form/new_password_form.js
@@ -28,11 +28,12 @@ import './new_password_form.scss';
export class NewPasswordForm extends React.Component {
submitHandler = (event) => {
+ event.preventDefault();
submitForm(event, '/account-recovery', {
userCode: this.props.userCode,
password: this.state.password,
confirmation: this.state.confirmation
- });
+ }).then(() => this.props.next());
}
handlePasswordChange = (event) => {
@@ -72,6 +73,7 @@ export class NewPasswordForm extends React.Component {
NewPasswordForm.propTypes = {
t: React.PropTypes.func.isRequired,
+ next: React.PropTypes.func.isRequired,
previous: React.PropTypes.func.isRequired,
userCode: React.PropTypes.string.isRequired
};
diff --git a/web-ui/src/account_recovery/new_password_form/new_password_form.spec.js b/web-ui/src/account_recovery/new_password_form/new_password_form.spec.js
index 26b8651c..b57dd42e 100644
--- a/web-ui/src/account_recovery/new_password_form/new_password_form.spec.js
+++ b/web-ui/src/account_recovery/new_password_form/new_password_form.spec.js
@@ -7,9 +7,11 @@ import { NewPasswordForm } from './new_password_form';
describe('NewPasswordForm', () => {
let newPasswordForm;
let mockPrevious;
+ let mockNext;
+ let mockTranslations;
beforeEach(() => {
- const mockTranslations = key => key;
+ mockTranslations = key => key;
mockPrevious = expect.createSpy();
newPasswordForm = shallow(
<NewPasswordForm t={mockTranslations} previous={mockPrevious} userCode='def234' />
@@ -40,7 +42,11 @@ describe('NewPasswordForm', () => {
});
describe('Submit', () => {
- beforeEach(() => {
+ beforeEach((done) => {
+ mockNext = expect.createSpy().andCall(() => done());
+ newPasswordForm = shallow(
+ <NewPasswordForm t={mockTranslations} previous={mockPrevious} userCode='def234' next={mockNext} />
+ );
fetchMock.post('/account-recovery', 200);
newPasswordForm.find('InputField[name="new-password"]').simulate('change', { target: { value: '123' } });
newPasswordForm.find('InputField[name="confirm-password"]').simulate('change', { target: { value: '456' } });
@@ -62,5 +68,9 @@ describe('NewPasswordForm', () => {
it('sends password confirmation as content', () => {
expect(fetchMock.lastOptions('/account-recovery').body).toContain('"confirmation":"456"');
});
+
+ it('calls next handler on success', () => {
+ expect(mockNext).toHaveBeenCalled();
+ });
});
});
diff --git a/web-ui/src/account_recovery/page.js b/web-ui/src/account_recovery/page.js
index 2d33e2fb..89441d26 100644
--- a/web-ui/src/account_recovery/page.js
+++ b/web-ui/src/account_recovery/page.js
@@ -22,6 +22,7 @@ import Header from 'src/common/header/header';
import AdminRecoveryCodeForm from 'src/account_recovery/admin_recovery_code_form/admin_recovery_code_form';
import UserRecoveryCodeForm from 'src/account_recovery/user_recovery_code_form/user_recovery_code_form';
import NewPasswordForm from 'src/account_recovery/new_password_form/new_password_form';
+import BackupAccountStep from 'src/account_recovery/backup_account_step/backup_account_step';
import Footer from 'src/common/footer/footer';
import 'font-awesome/scss/font-awesome.scss';
@@ -36,7 +37,9 @@ export class Page extends React.Component {
}
nextStep = (event) => {
- event.preventDefault();
+ if (event) {
+ event.preventDefault();
+ }
this.setState({ step: this.state.step + 1 });
}
@@ -53,7 +56,8 @@ export class Page extends React.Component {
1: (<UserRecoveryCodeForm
previous={this.previousStep} next={this.nextStep} saveCode={this.saveUserCode}
/>),
- 2: <NewPasswordForm previous={this.previousStep} userCode={this.state.userCode} />
+ 2: <NewPasswordForm previous={this.previousStep} userCode={this.state.userCode} next={this.nextStep} />,
+ 3: <BackupAccountStep />
})
mainContent = () => this.steps()[this.state.step];
diff --git a/web-ui/src/account_recovery/page.spec.js b/web-ui/src/account_recovery/page.spec.js
index 31a748be..8e4ccc33 100644
--- a/web-ui/src/account_recovery/page.spec.js
+++ b/web-ui/src/account_recovery/page.spec.js
@@ -9,6 +9,7 @@ import Footer from 'src/common/footer/footer';
import AdminRecoveryCodeFormWrapper from './admin_recovery_code_form/admin_recovery_code_form';
import UserRecoveryCodeFormWrapper from './user_recovery_code_form/user_recovery_code_form';
import NewPasswordFormWrapper from './new_password_form/new_password_form';
+import BackupAccountStepWrapper from './backup_account_step/backup_account_step';
describe('Account Recovery Page', () => {
let page;
@@ -37,6 +38,13 @@ describe('Account Recovery Page', () => {
expect(pageInstance.state.userCode).toEqual('123');
});
+ it('prevents default event before next', () => {
+ const eventSpy = expect.createSpy();
+ pageInstance.nextStep({ preventDefault: eventSpy });
+
+ expect(eventSpy).toHaveBeenCalled();
+ });
+
context('main content', () => {
it('renders admin recovery code form as default form', () => {
expect(page.find(AdminRecoveryCodeFormWrapper).length).toEqual(1);
@@ -45,31 +53,39 @@ describe('Account Recovery Page', () => {
});
it('renders user recovery code form when admin code submitted', () => {
- pageInstance.nextStep({ preventDefault: () => {} });
+ pageInstance.nextStep();
expect(page.find(UserRecoveryCodeFormWrapper).length).toEqual(1);
});
it('returns to admin code form on user code form back link', () => {
- pageInstance.nextStep({ preventDefault: () => {} });
+ pageInstance.nextStep();
pageInstance.previousStep();
expect(page.find(AdminRecoveryCodeFormWrapper).length).toEqual(1);
});
it('renders new password form when user code submitted', () => {
- pageInstance.nextStep({ preventDefault: () => {} });
- pageInstance.nextStep({ preventDefault: () => {} });
+ pageInstance.nextStep();
+ pageInstance.nextStep();
expect(page.find(NewPasswordFormWrapper).length).toEqual(1);
});
it('returns to user code form on new password form back link', () => {
- pageInstance.nextStep({ preventDefault: () => {} });
- pageInstance.nextStep({ preventDefault: () => {} });
+ pageInstance.nextStep();
+ pageInstance.nextStep();
pageInstance.previousStep();
expect(page.find(UserRecoveryCodeFormWrapper).length).toEqual(1);
});
+
+ it('renders backup account form after submitting new password', () => {
+ pageInstance.nextStep();
+ pageInstance.nextStep();
+ pageInstance.nextStep();
+
+ expect(page.find(BackupAccountStepWrapper).length).toEqual(1);
+ });
});
});