From 02337a391cd3f8119fe9fbb04d351553be932571 Mon Sep 17 00:00:00 2001
From: LEGO Technix <109212476+lego-technix@users.noreply.github.com>
Date: Thu, 17 Oct 2024 10:21:52 +0200
Subject: [PATCH 01/10] feat(api): accept for /api/password-reset-demands a
simpler payload
as well as the previous (now deprecated) ember-data-centric value
---
.../password/password.controller.js | 2 +-
.../application/password/password.route.js | 4 +
.../password/password.route.test.js | 99 +++++++++++++------
.../password/password.controller.test.js | 7 +-
.../password/password.controller.test.js | 6 +-
5 files changed, 77 insertions(+), 41 deletions(-)
diff --git a/api/src/identity-access-management/application/password/password.controller.js b/api/src/identity-access-management/application/password/password.controller.js
index 2d588f3bc4a..de7b11c3798 100644
--- a/api/src/identity-access-management/application/password/password.controller.js
+++ b/api/src/identity-access-management/application/password/password.controller.js
@@ -9,7 +9,7 @@ const checkResetDemand = async function (request, h, dependencies = { userSerial
return dependencies.userSerializer.serialize(user);
};
const createResetPasswordDemand = async function (request, h, dependencies = { resetPasswordSerializer }) {
- const { email } = request.payload.data.attributes;
+ const email = request.payload.email;
const locale = extractLocaleFromRequest(request);
const resetPasswordDemand = await usecases.createResetPasswordDemand({
diff --git a/api/src/identity-access-management/application/password/password.route.js b/api/src/identity-access-management/application/password/password.route.js
index c25664d94cd..031f98e6401 100644
--- a/api/src/identity-access-management/application/password/password.route.js
+++ b/api/src/identity-access-management/application/password/password.route.js
@@ -11,6 +11,10 @@ export const passwordRoutes = [
handler: (request, h) => passwordController.createResetPasswordDemand(request, h),
validate: {
payload: Joi.object({
+ email: Joi.when('data.attributes.email', {
+ then: Joi.string().email().default(Joi.ref('data.attributes.email')),
+ otherwise: Joi.string().email().required(),
+ }),
data: {
attributes: {
email: Joi.string().email().required(),
diff --git a/api/tests/identity-access-management/acceptance/application/password/password.route.test.js b/api/tests/identity-access-management/acceptance/application/password/password.route.test.js
index 700b61b7a94..438d7bbcf03 100644
--- a/api/tests/identity-access-management/acceptance/application/password/password.route.test.js
+++ b/api/tests/identity-access-management/acceptance/application/password/password.route.test.js
@@ -13,44 +13,85 @@ describe('Acceptance | Identity Access Management | Application | Route | passwo
describe('POST /api/password-reset-demands', function () {
let options;
- beforeEach(async function () {
- options = {
- method: 'POST',
- url: '/api/password-reset-demands',
- payload: {
- data: {
- attributes: { email },
- },
- },
- };
+ context('with simple payload', function () {
+ beforeEach(async function () {
+ options = {
+ method: 'POST',
+ url: '/api/password-reset-demands',
+ payload: { email },
+ };
- config.mailing.enabled = false;
+ config.mailing.enabled = false;
- const userId = databaseBuilder.factory.buildUser({ email }).id;
- databaseBuilder.factory.buildAuthenticationMethod.withPixAsIdentityProviderAndHashedPassword({ userId });
- await databaseBuilder.commit();
- });
+ const userId = databaseBuilder.factory.buildUser({ email }).id;
+ databaseBuilder.factory.buildAuthenticationMethod.withPixAsIdentityProviderAndHashedPassword({ userId });
+ await databaseBuilder.commit();
+ });
+
+ context('when given email doesn’t exist', function () {
+ it('replies with 404', async function () {
+ // given
+ options.payload.email = 'unknown@example.net';
- context('when email provided is unknown', function () {
- it('replies with 404', async function () {
- // given
- options.payload.data.attributes.email = 'unknown@example.net';
+ // when
+ const response = await server.inject(options);
+
+ // then
+ expect(response.statusCode).to.equal(404);
+ });
+ });
- // when
- const response = await server.inject(options);
+ context('when given email exists', function () {
+ it('replies with 201', async function () {
+ // when
+ const response = await server.inject(options);
- // then
- expect(response.statusCode).to.equal(404);
+ // then
+ expect(response.statusCode).to.equal(201);
+ });
});
});
- context('when existing email is provided', function () {
- it('replies with 201', async function () {
- // when
- const response = await server.inject(options);
+ context('with deprecated ember-data-centric payload', function () {
+ beforeEach(async function () {
+ options = {
+ method: 'POST',
+ url: '/api/password-reset-demands',
+ payload: {
+ data: {
+ attributes: { email },
+ },
+ },
+ };
+
+ config.mailing.enabled = false;
+
+ const userId = databaseBuilder.factory.buildUser({ email }).id;
+ databaseBuilder.factory.buildAuthenticationMethod.withPixAsIdentityProviderAndHashedPassword({ userId });
+ await databaseBuilder.commit();
+ });
+
+ context('when given email doesn’t exist', function () {
+ it('replies with 404', async function () {
+ // given
+ options.payload.data.attributes.email = 'unknown@example.net';
+
+ // when
+ const response = await server.inject(options);
+
+ // then
+ expect(response.statusCode).to.equal(404);
+ });
+ });
+
+ context('when given email exists', function () {
+ it('replies with 201', async function () {
+ // when
+ const response = await server.inject(options);
- // then
- expect(response.statusCode).to.equal(201);
+ // then
+ expect(response.statusCode).to.equal(201);
+ });
});
});
});
diff --git a/api/tests/identity-access-management/integration/application/password/password.controller.test.js b/api/tests/identity-access-management/integration/application/password/password.controller.test.js
index 0ec0058d9cd..f79a3931ae2 100644
--- a/api/tests/identity-access-management/integration/application/password/password.controller.test.js
+++ b/api/tests/identity-access-management/integration/application/password/password.controller.test.js
@@ -20,12 +20,7 @@ describe('Integration | Identity Access Management | Application | Controller |
const headers = {
'accept-language': 'fr',
};
- const payload = {
- data: {
- type: 'password-reset-demands',
- attributes: { email },
- },
- };
+ const payload = { email };
it('returns a 201 HTTP status code with a response', async function () {
// given
diff --git a/api/tests/identity-access-management/unit/application/password/password.controller.test.js b/api/tests/identity-access-management/unit/application/password/password.controller.test.js
index 5f0b637ece2..211b4b15e39 100644
--- a/api/tests/identity-access-management/unit/application/password/password.controller.test.js
+++ b/api/tests/identity-access-management/unit/application/password/password.controller.test.js
@@ -41,11 +41,7 @@ describe('Unit | Identity Access Management | Application | Controller | passwor
headers: {
'accept-language': locale,
},
- payload: {
- data: {
- attributes: { email },
- },
- },
+ payload: { email },
};
const resetPasswordDemand = {
id: 1,
From dfd870d37f2fcd5089a573d0894cb30f72fac8b5 Mon Sep 17 00:00:00 2001
From: LEGO Technix <109212476+lego-technix@users.noreply.github.com>
Date: Fri, 11 Oct 2024 16:07:02 +0200
Subject: [PATCH 02/10] feat(mon-pix): add new component
authentication/password-reset-demand
---
.../password-reset-demand-form.gjs | 119 +++++++++++++
.../authentication/password-reset-demand.scss | 41 +++++
mon-pix/app/styles/app.scss | 1 +
.../password-reset-demand-form-test.gjs | 159 ++++++++++++++++++
mon-pix/translations/en.json | 12 ++
mon-pix/translations/es.json | 12 ++
mon-pix/translations/fr.json | 12 ++
mon-pix/translations/nl.json | 12 ++
8 files changed, 368 insertions(+)
create mode 100644 mon-pix/app/components/authentication/password-reset-demand-form.gjs
create mode 100644 mon-pix/app/components/authentication/password-reset-demand.scss
create mode 100644 mon-pix/tests/integration/components/authentication/password-reset-demand-form-test.gjs
diff --git a/mon-pix/app/components/authentication/password-reset-demand-form.gjs b/mon-pix/app/components/authentication/password-reset-demand-form.gjs
new file mode 100644
index 00000000000..cb1c02e3535
--- /dev/null
+++ b/mon-pix/app/components/authentication/password-reset-demand-form.gjs
@@ -0,0 +1,119 @@
+import PixButton from '@1024pix/pix-ui/components/pix-button';
+import PixButtonLink from '@1024pix/pix-ui/components/pix-button-link';
+import PixInput from '@1024pix/pix-ui/components/pix-input';
+import PixMessage from '@1024pix/pix-ui/components/pix-message';
+import { on } from '@ember/modifier';
+import { action } from '@ember/object';
+import { service } from '@ember/service';
+import Component from '@glimmer/component';
+import { tracked } from '@glimmer/tracking';
+import { t } from 'ember-intl';
+import ENV from 'mon-pix/config/environment';
+
+import isEmailValid from '../../utils/email-validator.js';
+
+export default class PasswordResetDemandForm extends Component {
+ @service intl;
+
+ @tracked isLoading = false;
+ @tracked errorMessage;
+ @tracked emailInputvalidationStatus;
+ @tracked emailInputvalidationErrorMessage;
+
+ email;
+
+ @action
+ handleEmailChange(event) {
+ this.email = event.target.value;
+ this.emailInputvalidationStatus = isEmailValid(this.email) ? 'success' : 'error';
+ this.emailInputvalidationErrorMessage = this.intl.t(
+ 'components.authentication.password-reset-demand-form.invalid-email',
+ );
+ }
+
+ @action
+ async handlePasswordResetDemand(event) {
+ if (event) event.preventDefault();
+
+ this.errorMessage = null;
+
+ const email = this.email.trim();
+ if (!email || this.emailInputvalidationStatus === 'error') {
+ return;
+ }
+
+ try {
+ this.isLoading = true;
+ const response = await window.fetch(`${ENV.APP.API_HOST}/api/password-reset-demands`, {
+ headers: {
+ 'Content-Type': 'application/json',
+ Accept: 'application/json',
+ },
+ method: 'POST',
+ body: JSON.stringify({ email }),
+ });
+ if (response.status == 404) {
+ this.errorMessage = this.intl.t('pages.password-reset-demand.error.message');
+ } else if (!response.ok) {
+ throw new Error(`Response status: ${response.status}`);
+ }
+ } catch (error) {
+ this.errorMessage = this.intl.t('common.api-error-messages.internal-server-error');
+ } finally {
+ this.isLoading = false;
+ }
+ }
+
+
+
+
+}
diff --git a/mon-pix/app/components/authentication/password-reset-demand.scss b/mon-pix/app/components/authentication/password-reset-demand.scss
new file mode 100644
index 00000000000..581c7d5de1a
--- /dev/null
+++ b/mon-pix/app/components/authentication/password-reset-demand.scss
@@ -0,0 +1,41 @@
+.authentication-password-reset-demand-form {
+ input {
+ padding: var(--pix-spacing-3x);
+ }
+
+ &__rule {
+ @extend %pix-body-xs;
+
+ color: var(--pix-neutral-500);
+ }
+
+ &__error {
+ margin-top: var(--pix-spacing-4x);
+ }
+
+ &__input-block {
+ margin-top: var(--pix-spacing-4x);
+
+ .pix-input {
+ width: 100%;
+ }
+ }
+
+ &__button {
+ width: 100%;
+ margin-top: var(--pix-spacing-4x);
+ }
+
+ &__help {
+ @extend %pix-body-s;
+
+ margin-top: var(--pix-spacing-4x);
+ color: var(--pix-neutral-70);
+ text-align: center;
+ }
+
+ &__help-contact-us-link {
+ display: inline;
+ padding: 0;
+ }
+}
\ No newline at end of file
diff --git a/mon-pix/app/styles/app.scss b/mon-pix/app/styles/app.scss
index f7fa0e40523..5cb403cc76a 100644
--- a/mon-pix/app/styles/app.scss
+++ b/mon-pix/app/styles/app.scss
@@ -138,6 +138,7 @@ of an adaptative/mobile-first approach — refactoring is welcome here */
@import 'authentication-layout';
@import 'authentication/oidc-provider-selector';
@import 'authentication/other-authentication-providers';
+@import 'authentication/password-reset-demand';
@import 'authentication/signin-form';
@import 'authentication/signup-form';
@import 'authentication/sso-selection-form';
diff --git a/mon-pix/tests/integration/components/authentication/password-reset-demand-form-test.gjs b/mon-pix/tests/integration/components/authentication/password-reset-demand-form-test.gjs
new file mode 100644
index 00000000000..8545e4a8802
--- /dev/null
+++ b/mon-pix/tests/integration/components/authentication/password-reset-demand-form-test.gjs
@@ -0,0 +1,159 @@
+import { fillByLabel, render } from '@1024pix/ember-testing-library';
+import { click } from '@ember/test-helpers';
+import { t } from 'ember-intl/test-support';
+import PasswordResetDemandForm from 'mon-pix/components/authentication/password-reset-demand-form';
+import { module, test } from 'qunit';
+import sinon from 'sinon';
+
+import setupIntlRenderingTest from '../../../helpers/setup-intl-rendering';
+
+module('Integration | Component | Authentication | password-reset-demand-form', function (hooks) {
+ setupIntlRenderingTest(hooks);
+
+ test('it displays a contact us link', async function (assert) {
+ // given
+ const screen = await render();
+
+ // then
+ const link = await screen.queryByRole('link', {
+ name: t('components.authentication.password-reset-demand-form.contact-us-link.link-text'),
+ });
+ assert.dom(link).exists();
+ assert.strictEqual(link.getAttribute('href'), 'https://pix.fr/support');
+ });
+
+ module('email input validation', function () {
+ module('when the email input is valid', function () {
+ test('it doesn’t display any error message', async function (assert) {
+ // given
+ const validEmail = 'someone@example.net';
+ const screen = await render();
+
+ // when
+ await fillByLabel(t('pages.password-reset-demand.fields.email.label'), validEmail);
+
+ // then
+ assert.dom(screen.queryByRole('alert')).doesNotExist();
+ });
+ });
+
+ module('when the email input is invalid', function () {
+ test('it displays an "invalid email" error message', async function (assert) {
+ // given
+ const invalidEmail = 'invalid email';
+ const screen = await render();
+
+ // when
+ await fillByLabel(t('pages.password-reset-demand.fields.email.label'), invalidEmail);
+
+ // then
+ assert
+ .dom(screen.queryByText(t('components.authentication.password-reset-demand-form.invalid-email')))
+ .exists();
+ });
+ });
+ });
+
+ module('password-reset-demand sending', function (hooks) {
+ hooks.beforeEach(function () {
+ sinon.stub(window, 'fetch');
+ });
+
+ hooks.afterEach(function () {
+ sinon.restore();
+ });
+
+ module('when the password-reset-demand is successful', function () {
+ test('it doesn’t display any error message', async function (assert) {
+ // given
+ window.fetch.resolves(
+ fetchMock({
+ status: 201,
+ }),
+ );
+
+ const email = 'someone@example.net';
+ const screen = await render();
+
+ // when
+ await fillByLabel(t('pages.password-reset-demand.fields.email.label'), email);
+ await click(
+ screen.getByRole('button', {
+ name: t('components.authentication.password-reset-demand-form.actions.receive-reset-button'),
+ }),
+ );
+
+ // then
+ assert.dom(screen.queryByRole('alert')).doesNotExist();
+ });
+ });
+
+ module('when there is no corresponding user account', function () {
+ test('it displays an "account not found" error message', async function (assert) {
+ // given
+ window.fetch.resolves(
+ fetchMock({
+ status: 404,
+ body: {
+ errors: [{ title: 'Not Found' }],
+ },
+ }),
+ );
+
+ const email = 'someone@example.net';
+ const screen = await render();
+
+ // when
+ await fillByLabel(t('pages.password-reset-demand.fields.email.label'), email);
+ await click(
+ screen.getByRole('button', {
+ name: t('components.authentication.password-reset-demand-form.actions.receive-reset-button'),
+ }),
+ );
+
+ // then
+ // The following doesn’t work because of a PixUi span inside the role element
+ //assert.dom(screen.queryByRole('alert', { name: t('pages.password-reset-demand.error.message') })).exists();
+ assert.dom(screen.queryByText(t('pages.password-reset-demand.error.message'))).exists();
+ });
+ });
+
+ module('when there is an unknown error', function () {
+ test('it displays an "unknown error" error message', async function (assert) {
+ // given
+ window.fetch.resolves(
+ fetchMock({
+ status: 500,
+ }),
+ );
+
+ const email = 'someone@example.net';
+ const screen = await render();
+
+ // when
+ await fillByLabel(t('pages.password-reset-demand.fields.email.label'), email);
+ await click(
+ screen.getByRole('button', {
+ name: t('components.authentication.password-reset-demand-form.actions.receive-reset-button'),
+ }),
+ );
+
+ // then
+ // The following doesn’t work because of a PixUi span inside the role element
+ //assert
+ // .dom(screen.queryByRole('alert', { name: t('common.api-error-messages.internal-server-error') }))
+ // .exists();
+ assert.dom(screen.queryByText(t('common.api-error-messages.internal-server-error'))).exists();
+ });
+ });
+ });
+});
+
+function fetchMock({ body, status }) {
+ return new window.Response(JSON.stringify(body), {
+ status,
+ headers: {
+ 'Content-type': 'application/json',
+ },
+ });
+}
diff --git a/mon-pix/translations/en.json b/mon-pix/translations/en.json
index f9a941a8bf8..c4ccc6b0835 100644
--- a/mon-pix/translations/en.json
+++ b/mon-pix/translations/en.json
@@ -176,6 +176,18 @@
},
"legend": "Information required for sign up."
}
+ },
+ "password-reset-demand-form": {
+ "invalid-email": "Your email address is invalid.",
+ "rule": "All fields are required.",
+ "no-email-question": "No email address?",
+ "contact-us-link": {
+ "link-text": "Contact us",
+ "link-url": "https://pix.org/en/support"
+ },
+ "actions": {
+ "receive-reset-button": "Receive a reset link"
+ }
}
},
"invited": {
diff --git a/mon-pix/translations/es.json b/mon-pix/translations/es.json
index 7c93d770446..3884152693f 100644
--- a/mon-pix/translations/es.json
+++ b/mon-pix/translations/es.json
@@ -169,6 +169,18 @@
},
"legend": "Información necesaria para la inscripción."
}
+ },
+ "password-reset-demand-form": {
+ "invalid-email": "Your email address is invalid.",
+ "rule": "All fields are required.",
+ "no-email-question": "No email address?",
+ "contact-us-link": {
+ "link-text": "Contact us",
+ "link-url": "https://pix.org/en/support"
+ },
+ "actions": {
+ "receive-reset-button": "Receive a reset link"
+ }
}
},
"invited": {
diff --git a/mon-pix/translations/fr.json b/mon-pix/translations/fr.json
index efea1b0a460..d3af40e83fe 100644
--- a/mon-pix/translations/fr.json
+++ b/mon-pix/translations/fr.json
@@ -176,6 +176,18 @@
},
"legend": "Information nécessaire pour l'inscription."
}
+ },
+ "password-reset-demand-form": {
+ "invalid-email": "Votre adresse e-mail n’est pas valide.",
+ "rule": "Tous les champs sont obligatoires.",
+ "no-email-question": "Pas d’adresse e-mail renseignée ?",
+ "contact-us-link": {
+ "link-text": "Contactez-nous",
+ "link-url": "https://pix.fr/support"
+ },
+ "actions": {
+ "receive-reset-button": "Recevoir un lien de réinitialisation"
+ }
}
},
"invited": {
diff --git a/mon-pix/translations/nl.json b/mon-pix/translations/nl.json
index 67abeebc12f..03385089ddd 100644
--- a/mon-pix/translations/nl.json
+++ b/mon-pix/translations/nl.json
@@ -169,6 +169,18 @@
},
"legend": "Vereiste informatie voor registratie."
}
+ },
+ "password-reset-demand-form": {
+ "invalid-email": "Your email address is invalid.",
+ "rule": "All fields are required.",
+ "no-email-question": "No email address?",
+ "contact-us-link": {
+ "link-text": "Contact us",
+ "link-url": "https://pix.org/nl-be/support"
+ },
+ "actions": {
+ "receive-reset-button": "Receive a reset link"
+ }
}
},
"invited": {
From f98a9a1914e410f07b3cd0659e2d54270edaa5f6 Mon Sep 17 00:00:00 2001
From: LEGO Technix <109212476+lego-technix@users.noreply.github.com>
Date: Fri, 11 Oct 2024 11:58:16 +0200
Subject: [PATCH 03/10] feat(mon-pix): add isNewAuthenticationDesignEnabled on
password-reset-demand.hbs
---
.../app/controllers/password-reset-demand.js | 10 +++
.../app/templates/password-reset-demand.hbs | 10 ++-
.../password-reset-demand-form-test.js | 81 ++++++++++---------
3 files changed, 62 insertions(+), 39 deletions(-)
create mode 100644 mon-pix/app/controllers/password-reset-demand.js
diff --git a/mon-pix/app/controllers/password-reset-demand.js b/mon-pix/app/controllers/password-reset-demand.js
new file mode 100644
index 00000000000..c5f4dc93c1c
--- /dev/null
+++ b/mon-pix/app/controllers/password-reset-demand.js
@@ -0,0 +1,10 @@
+import Controller from '@ember/controller';
+import { service } from '@ember/service';
+
+export default class LoginController extends Controller {
+ @service featureToggles;
+
+ get isNewAuthenticationDesignEnabled() {
+ return this.featureToggles.featureToggles.isNewAuthenticationDesignEnabled;
+ }
+}
diff --git a/mon-pix/app/templates/password-reset-demand.hbs b/mon-pix/app/templates/password-reset-demand.hbs
index 7ec2c3248f1..62822c6d429 100644
--- a/mon-pix/app/templates/password-reset-demand.hbs
+++ b/mon-pix/app/templates/password-reset-demand.hbs
@@ -1,4 +1,8 @@
{{page-title (t "pages.password-reset-demand.page-title")}}
-
\ No newline at end of file
+
+{{#if this.isNewAuthenticationDesignEnabled}}
+{{else}}
+
+{{/if}}
\ No newline at end of file
diff --git a/mon-pix/tests/acceptance/password-reset-demand-form-test.js b/mon-pix/tests/acceptance/password-reset-demand-form-test.js
index 4a5e5712b43..da47e1609d7 100644
--- a/mon-pix/tests/acceptance/password-reset-demand-form-test.js
+++ b/mon-pix/tests/acceptance/password-reset-demand-form-test.js
@@ -13,50 +13,59 @@ module('Acceptance | Password reset demand form', function (hooks) {
setupMirage(hooks);
setupIntl(hooks);
- test('can visit /mot-passe-oublie', async function (assert) {
- // when
- await visit('/mot-de-passe-oublie');
+ module('when "New authentication design" feature toggle is disabled', function (hooks) {
+ hooks.beforeEach(function () {
+ server.create('feature-toggle', {
+ id: 0,
+ isNewAuthenticationDesignEnabled: false,
+ });
+ });
- // then
- assert.strictEqual(currentURL(), '/mot-de-passe-oublie');
- });
+ test('can visit /mot-passe-oublie', async function (assert) {
+ // when
+ await visit('/mot-de-passe-oublie');
- test('should stay on mot de passe oublié page, and show success message, when email sent correspond to an existing user', async function (assert) {
- // given
- this.server.create('user', {
- id: 1,
- firstName: 'Brandone',
- lastName: 'Martins',
- email: 'brandone.martins@pix.com',
- password: '1024pix!',
+ // then
+ assert.strictEqual(currentURL(), '/mot-de-passe-oublie');
});
- await visit('/mot-de-passe-oublie');
- await fillIn('#email', 'brandone.martins@pix.com');
- // when
- await clickByLabel(t('pages.password-reset-demand.actions.reset'));
+ test('should stay on mot de passe oublié page, and show success message, when email sent correspond to an existing user', async function (assert) {
+ // given
+ this.server.create('user', {
+ id: 1,
+ firstName: 'Brandone',
+ lastName: 'Martins',
+ email: 'brandone.martins@pix.com',
+ password: '1024pix!',
+ });
+ await visit('/mot-de-passe-oublie');
+ await fillIn('#email', 'brandone.martins@pix.com');
- assert.strictEqual(currentURL(), '/mot-de-passe-oublie');
- assert.dom('.password-reset-demand-form__body').exists();
- });
+ // when
+ await clickByLabel(t('pages.password-reset-demand.actions.reset'));
- test('should stay in mot-passe-oublie page when sent email do not correspond to any existing user', async function (assert) {
- // given
- this.server.create('user', {
- id: 1,
- firstName: 'Brandone',
- lastName: 'Martins',
- email: 'brandone.martins@pix.com',
- password: '1024pix!',
+ assert.strictEqual(currentURL(), '/mot-de-passe-oublie');
+ assert.dom('.password-reset-demand-form__body').exists();
});
- const screen = await visit('/mot-de-passe-oublie');
- await fillIn('#email', 'unexisting@user.com');
- // when
- await clickByLabel(t('pages.password-reset-demand.actions.reset'));
+ test('should stay in mot-passe-oublie page when sent email do not correspond to any existing user', async function (assert) {
+ // given
+ this.server.create('user', {
+ id: 1,
+ firstName: 'Brandone',
+ lastName: 'Martins',
+ email: 'brandone.martins@pix.com',
+ password: '1024pix!',
+ });
+ const screen = await visit('/mot-de-passe-oublie');
+ await fillIn('#email', 'unexisting@user.com');
- // then
- assert.strictEqual(currentURL(), '/mot-de-passe-oublie');
- assert.dom(screen.getByText(t('pages.password-reset-demand.error.message'))).exists();
+ // when
+ await clickByLabel(t('pages.password-reset-demand.actions.reset'));
+
+ // then
+ assert.strictEqual(currentURL(), '/mot-de-passe-oublie');
+ assert.dom(screen.getByText(t('pages.password-reset-demand.error.message'))).exists();
+ });
});
});
From e1f7a109dd491b2154886d502fd4071dfa7653a5 Mon Sep 17 00:00:00 2001
From: LEGO Technix <109212476+lego-technix@users.noreply.github.com>
Date: Fri, 11 Oct 2024 16:07:17 +0200
Subject: [PATCH 04/10] feat(mon-pix): use new component
authentication/password-reset-demand
---
.../app/templates/password-reset-demand.hbs | 15 ++++++-
.../password-reset-demand-form-test.js | 42 ++++++++++++++++++-
mon-pix/translations/en.json | 3 +-
mon-pix/translations/es.json | 3 +-
mon-pix/translations/fr.json | 3 +-
mon-pix/translations/nl.json | 3 +-
6 files changed, 62 insertions(+), 7 deletions(-)
diff --git a/mon-pix/app/templates/password-reset-demand.hbs b/mon-pix/app/templates/password-reset-demand.hbs
index 62822c6d429..cb609c44000 100644
--- a/mon-pix/app/templates/password-reset-demand.hbs
+++ b/mon-pix/app/templates/password-reset-demand.hbs
@@ -1,6 +1,19 @@
-{{page-title (t "pages.password-reset-demand.page-title")}}
+{{page-title (t "pages.password-reset-demand.title")}}
{{#if this.isNewAuthenticationDesignEnabled}}
+
+ <:header>
+
+ {{t "common.actions.login"}}
+
+
+
+ <:content>
+ {{t "pages.password-reset-demand.title"}}
+
+
+
+
{{else}}
diff --git a/mon-pix/tests/acceptance/password-reset-demand-form-test.js b/mon-pix/tests/acceptance/password-reset-demand-form-test.js
index da47e1609d7..dc8f864b4af 100644
--- a/mon-pix/tests/acceptance/password-reset-demand-form-test.js
+++ b/mon-pix/tests/acceptance/password-reset-demand-form-test.js
@@ -29,7 +29,7 @@ module('Acceptance | Password reset demand form', function (hooks) {
assert.strictEqual(currentURL(), '/mot-de-passe-oublie');
});
- test('should stay on mot de passe oublié page, and show success message, when email sent correspond to an existing user', async function (assert) {
+ test('stays on mot de passe oublié page, and shows success message when email sent correspond to an existing user', async function (assert) {
// given
this.server.create('user', {
id: 1,
@@ -48,7 +48,7 @@ module('Acceptance | Password reset demand form', function (hooks) {
assert.dom('.password-reset-demand-form__body').exists();
});
- test('should stay in mot-passe-oublie page when sent email do not correspond to any existing user', async function (assert) {
+ test('stays in mot-passe-oublie page when sent email do not correspond to any existing user', async function (assert) {
// given
this.server.create('user', {
id: 1,
@@ -68,4 +68,42 @@ module('Acceptance | Password reset demand form', function (hooks) {
assert.dom(screen.getByText(t('pages.password-reset-demand.error.message'))).exists();
});
});
+
+ module('when "New authentication design" feature toggle is enabled', function (hooks) {
+ hooks.beforeEach(function () {
+ server.create('feature-toggle', {
+ id: 0,
+ isNewAuthenticationDesignEnabled: true,
+ });
+ });
+
+ test('can visit /mot-passe-oublie', async function (assert) {
+ // when
+ await visit('/mot-de-passe-oublie');
+
+ // then
+ assert.strictEqual(currentURL(), '/mot-de-passe-oublie');
+ });
+
+ test('stays on "mot de passe oublié" page, and shows success message when email sent correspond to an existing user', async function (assert) {
+ // given
+ this.server.create('user', {
+ id: 1,
+ firstName: 'Brandone',
+ lastName: 'Martins',
+ email: 'brandone.martins@pix.com',
+ password: '1024pix!',
+ });
+ const screen = await visit('/mot-de-passe-oublie');
+ await fillIn(
+ screen.getByRole('textbox', { name: t('pages.password-reset-demand.fields.email.label') }),
+ 'brandone.martins@pix.com',
+ );
+
+ // when
+ await clickByLabel(t('components.authentication.password-reset-demand-form.actions.receive-reset-button'));
+
+ assert.strictEqual(currentURL(), '/mot-de-passe-oublie');
+ });
+ });
});
diff --git a/mon-pix/translations/en.json b/mon-pix/translations/en.json
index c4ccc6b0835..564ebffe4f5 100644
--- a/mon-pix/translations/en.json
+++ b/mon-pix/translations/en.json
@@ -47,7 +47,8 @@
"show-more": "Show more",
"sign-out": "Sign out",
"stay": "Stay",
- "validate": "Validate"
+ "validate": "Validate",
+ "login": "Log in to Pix"
},
"api-error-messages": {
"bad-request-error": "The data entered was not in the correct format.",
diff --git a/mon-pix/translations/es.json b/mon-pix/translations/es.json
index 3884152693f..c8bb63397b3 100644
--- a/mon-pix/translations/es.json
+++ b/mon-pix/translations/es.json
@@ -47,7 +47,8 @@
"sign-out": "Desconectarse",
"stay": "Quedarse",
"validate": "Validar",
- "refresh-page": "Actualizar la página"
+ "refresh-page": "Actualizar la página",
+ "login": "Log in to Pix"
},
"api-error-messages": {
"bad-request-error": "Los datos que has enviado no están en el formato correcto.",
diff --git a/mon-pix/translations/fr.json b/mon-pix/translations/fr.json
index d3af40e83fe..021ee46d30a 100644
--- a/mon-pix/translations/fr.json
+++ b/mon-pix/translations/fr.json
@@ -47,7 +47,8 @@
"show-more": "Afficher plus",
"sign-out": "Se déconnecter",
"stay": "Rester",
- "validate": "Valider"
+ "validate": "Valider",
+ "login": "Se connecter sur Pix"
},
"api-error-messages": {
"bad-request-error": "Les données que vous avez soumises ne sont pas au bon format.",
diff --git a/mon-pix/translations/nl.json b/mon-pix/translations/nl.json
index 03385089ddd..19d091f6b21 100644
--- a/mon-pix/translations/nl.json
+++ b/mon-pix/translations/nl.json
@@ -47,7 +47,8 @@
"sign-out": "Log uit.",
"stay": "Blijf",
"validate": "Valideer",
- "refresh-page": "Pagina verversen"
+ "refresh-page": "Pagina verversen",
+ "login": "Log in to Pix"
},
"api-error-messages": {
"bad-request-error": "De gegevens die u hebt opgegeven, hebben niet het juiste formaat.",
From aa462292e73aa684de6259baef35e7acf03475aa Mon Sep 17 00:00:00 2001
From: LEGO Technix <109212476+lego-technix@users.noreply.github.com>
Date: Wed, 16 Oct 2024 15:47:43 +0200
Subject: [PATCH 05/10] feat(mon-pix): change page title according to design
---
.../integration/components/password-reset-demand-form-test.js | 2 +-
mon-pix/translations/en.json | 2 +-
mon-pix/translations/fr.json | 2 +-
3 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/mon-pix/tests/integration/components/password-reset-demand-form-test.js b/mon-pix/tests/integration/components/password-reset-demand-form-test.js
index c5549c6ee58..cd9b5e5ef3f 100644
--- a/mon-pix/tests/integration/components/password-reset-demand-form-test.js
+++ b/mon-pix/tests/integration/components/password-reset-demand-form-test.js
@@ -28,7 +28,7 @@ module('Integration | Component | password reset demand form', function (hooks)
// then
assert.dom(screen.getByRole('img', { name: "Page d'accueil de Pix.org" })).exists();
assert.dom(screen.getByRole('link', { name: "Page d'accueil de Pix.org" })).hasProperty('href', 'https://pix.org/');
- assert.dom(screen.getByRole('heading', { name: 'Mot de passe oublié ?' })).exists();
+ assert.dom(screen.getByRole('heading', { name: t('pages.password-reset-demand.title') })).exists();
assert.dom(screen.getByText("Entrez votre adresse e-mail ci-dessous, et c'est repartix")).exists();
assert.dom(screen.getByRole('textbox', { name: '* Adresse e-mail' })).exists();
assert.dom(screen.getByRole('button', { name: 'Réinitialiser mon mot de passe' })).exists();
diff --git a/mon-pix/translations/en.json b/mon-pix/translations/en.json
index 564ebffe4f5..4e13bbaa31d 100644
--- a/mon-pix/translations/en.json
+++ b/mon-pix/translations/en.json
@@ -1587,7 +1587,7 @@
"instructions": "An email explaining how to reset your password\n has been sent to the email address {email}.",
"subtitle": "Password reset request"
},
- "title": "Forgot your password?"
+ "title": "Forgotten password"
},
"profile": {
"accessibility": {
diff --git a/mon-pix/translations/fr.json b/mon-pix/translations/fr.json
index 021ee46d30a..90c71af6fc8 100644
--- a/mon-pix/translations/fr.json
+++ b/mon-pix/translations/fr.json
@@ -1587,7 +1587,7 @@
"instructions": "Un e-mail contenant la démarche à suivre afin de réinitialiser votre mot de passe\n vous a été envoyé à l’adresse e-mail {email}.",
"subtitle": "Demande de réinitialisation de mot de passe"
},
- "title": "Mot de passe oublié ?"
+ "title": "Mot de passe oublié"
},
"profile": {
"accessibility": {
From 9fd52e38994887b7a6faf3491b3fbaa23514d790 Mon Sep 17 00:00:00 2001
From: LEGO Technix <109212476+lego-technix@users.noreply.github.com>
Date: Fri, 18 Oct 2024 12:39:38 +0200
Subject: [PATCH 06/10] refactor(mon-pix): mark FormTextfield component as
deprecated
---
mon-pix/app/components/form-textfield.hbs | 2 ++
mon-pix/app/components/form-textfield.js | 2 ++
2 files changed, 4 insertions(+)
diff --git a/mon-pix/app/components/form-textfield.hbs b/mon-pix/app/components/form-textfield.hbs
index 299dbd29f90..fa8f2ef20bd 100644
--- a/mon-pix/app/components/form-textfield.hbs
+++ b/mon-pix/app/components/form-textfield.hbs
@@ -1,3 +1,5 @@
+{{! DEPRECATED COMPONENT: DON’T USE IT! USE COMPONENTS OF THE DESIGN SYSTEM INSTEAD!! }}
+
{{! template-lint-disable require-input-label no-unknown-arguments-for-builtin-components }}
diff --git a/mon-pix/tests/integration/components/authentication/password-reset-demand-form-test.gjs b/mon-pix/tests/integration/components/authentication/password-reset-demand-form-test.gjs
index 8545e4a8802..594fff07611 100644
--- a/mon-pix/tests/integration/components/authentication/password-reset-demand-form-test.gjs
+++ b/mon-pix/tests/integration/components/authentication/password-reset-demand-form-test.gjs
@@ -30,7 +30,7 @@ module('Integration | Component | Authentication | password-reset-demand-form',
const screen = await render(
);
// when
- await fillByLabel(t('pages.password-reset-demand.fields.email.label'), validEmail);
+ await fillByLabel(t('components.authentication.password-reset-demand-form.fields.email.label'), validEmail);
// then
assert.dom(screen.queryByRole('alert')).doesNotExist();
@@ -44,11 +44,15 @@ module('Integration | Component | Authentication | password-reset-demand-form',
const screen = await render(
);
// when
- await fillByLabel(t('pages.password-reset-demand.fields.email.label'), invalidEmail);
+ await fillByLabel(t('components.authentication.password-reset-demand-form.fields.email.label'), invalidEmail);
// then
assert
- .dom(screen.queryByText(t('components.authentication.password-reset-demand-form.invalid-email')))
+ .dom(
+ screen.queryByText(
+ t('components.authentication.password-reset-demand-form.fields.email.error-message-invalid'),
+ ),
+ )
.exists();
});
});
@@ -76,7 +80,7 @@ module('Integration | Component | Authentication | password-reset-demand-form',
const screen = await render(
);
// when
- await fillByLabel(t('pages.password-reset-demand.fields.email.label'), email);
+ await fillByLabel(t('components.authentication.password-reset-demand-form.fields.email.label'), email);
await click(
screen.getByRole('button', {
name: t('components.authentication.password-reset-demand-form.actions.receive-reset-button'),
@@ -104,7 +108,7 @@ module('Integration | Component | Authentication | password-reset-demand-form',
const screen = await render(
);
// when
- await fillByLabel(t('pages.password-reset-demand.fields.email.label'), email);
+ await fillByLabel(t('components.authentication.password-reset-demand-form.fields.email.label'), email);
await click(
screen.getByRole('button', {
name: t('components.authentication.password-reset-demand-form.actions.receive-reset-button'),
@@ -114,7 +118,7 @@ module('Integration | Component | Authentication | password-reset-demand-form',
// then
// The following doesn’t work because of a PixUi span inside the role element
//assert.dom(screen.queryByRole('alert', { name: t('pages.password-reset-demand.error.message') })).exists();
- assert.dom(screen.queryByText(t('pages.password-reset-demand.error.message'))).exists();
+ assert.dom(screen.queryByText(t('components.authentication.password-reset-demand-form.404-message'))).exists();
});
});
@@ -131,7 +135,7 @@ module('Integration | Component | Authentication | password-reset-demand-form',
const screen = await render(
);
// when
- await fillByLabel(t('pages.password-reset-demand.fields.email.label'), email);
+ await fillByLabel(t('components.authentication.password-reset-demand-form.fields.email.label'), email);
await click(
screen.getByRole('button', {
name: t('components.authentication.password-reset-demand-form.actions.receive-reset-button'),
diff --git a/mon-pix/translations/en.json b/mon-pix/translations/en.json
index 4daa11b4e9d..0ac3c40a373 100644
--- a/mon-pix/translations/en.json
+++ b/mon-pix/translations/en.json
@@ -152,6 +152,7 @@
"signup-heading": "Other ways to sign up"
},
"password-reset-demand-form": {
+ "404-message": "The email address entered does not match any Pix account",
"actions": {
"receive-reset-button": "Receive a reset link"
},
@@ -159,7 +160,13 @@
"link-text": "Contact us",
"link-url": "https://pix.org/en/support"
},
- "invalid-email": "Your email address is invalid.",
+ "fields": {
+ "email": {
+ "error-message-invalid": "Your email address is invalid.",
+ "label": "Email address",
+ "placeholder": "ex: john.doe@email.com"
+ }
+ },
"no-email-question": "No email address?",
"rule": "All fields are required."
},
diff --git a/mon-pix/translations/es.json b/mon-pix/translations/es.json
index c8bb63397b3..7b8f72964c1 100644
--- a/mon-pix/translations/es.json
+++ b/mon-pix/translations/es.json
@@ -172,13 +172,20 @@
}
},
"password-reset-demand-form": {
- "invalid-email": "Your email address is invalid.",
+ "404-message": "Esta dirección de correo electrónico no corresponde a ninguna cuenta",
"rule": "All fields are required.",
"no-email-question": "No email address?",
"contact-us-link": {
"link-text": "Contact us",
"link-url": "https://pix.org/en/support"
},
+ "fields": {
+ "email": {
+ "invalid-email": "Your email address is invalid.",
+ "label": "Email address",
+ "placeholder": "ex: john.doe@email.com"
+ }
+ },
"actions": {
"receive-reset-button": "Receive a reset link"
}
diff --git a/mon-pix/translations/fr.json b/mon-pix/translations/fr.json
index 7b26e4cdabb..e7c816612dc 100644
--- a/mon-pix/translations/fr.json
+++ b/mon-pix/translations/fr.json
@@ -152,6 +152,7 @@
"signup-heading": "Autres moyens d’inscription"
},
"password-reset-demand-form": {
+ "404-message": "Cette adresse e-mail ne correspond à aucun compte",
"actions": {
"receive-reset-button": "Recevoir un lien de réinitialisation"
},
@@ -159,7 +160,13 @@
"link-text": "Contactez-nous",
"link-url": "https://pix.fr/support"
},
- "invalid-email": "Votre adresse e-mail n’est pas valide.",
+ "fields": {
+ "email": {
+ "error-message-invalid": "Votre adresse e-mail n’est pas valide.",
+ "label": "Adresse e-mail",
+ "placeholder": "ex: jean.dupont@email.com"
+ }
+ },
"no-email-question": "Pas d’adresse e-mail renseignée ?",
"rule": "Tous les champs sont obligatoires."
},
diff --git a/mon-pix/translations/nl.json b/mon-pix/translations/nl.json
index 19d091f6b21..fc2c7a52d18 100644
--- a/mon-pix/translations/nl.json
+++ b/mon-pix/translations/nl.json
@@ -172,13 +172,20 @@
}
},
"password-reset-demand-form": {
- "invalid-email": "Your email address is invalid.",
+ "404-message": "Dit e-mailadres komt niet overeen met een account",
"rule": "All fields are required.",
"no-email-question": "No email address?",
"contact-us-link": {
"link-text": "Contact us",
"link-url": "https://pix.org/nl-be/support"
},
+ "fields": {
+ "email": {
+ "error-message-invalid": "Your email address is invalid.",
+ "label": "Email address",
+ "placeholder": "ex: john.doe@email.com"
+ }
+ },
"actions": {
"receive-reset-button": "Receive a reset link"
}