Skip to content

Commit

Permalink
[TECH] Affichage d'une erreur spécifique à l'enregistrement d'un cand…
Browse files Browse the repository at this point in the history
…idat lorsque la session est finalisée (PIX-13862) (#10248)
  • Loading branch information
mcampourcy authored Oct 8, 2024
1 parent 82e5ad8 commit b97aa51
Show file tree
Hide file tree
Showing 8 changed files with 54 additions and 102 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ export async function addCandidateToSession({
} catch {
throw new CertificationCandidatesError({
code: CERTIFICATION_CANDIDATES_ERRORS.CANDIDATE_RESULT_RECIPIENT_EMAIL_NOT_VALID.code,
meta: { email: candidate.resultRecipientEmail },
meta: { value: candidate.resultRecipientEmail },
});
}
}
Expand All @@ -111,7 +111,7 @@ export async function addCandidateToSession({
} catch {
throw new CertificationCandidatesError({
code: CERTIFICATION_CANDIDATES_ERRORS.CANDIDATE_EMAIL_NOT_VALID.code,
meta: { email: candidate.email },
meta: { value: candidate.email },
});
}
}
Expand Down
2 changes: 1 addition & 1 deletion api/src/shared/application/error-manager.js
Original file line number Diff line number Diff line change
Expand Up @@ -469,7 +469,7 @@ function _mapToHttpError(error) {
}

if (error instanceof DomainErrors.CertificationCandidateOnFinalizedSessionError) {
return new HttpErrors.ForbiddenError(error.message);
return new HttpErrors.ForbiddenError(error.message, error.code);
}

if (error instanceof DomainErrors.OrganizationLearnerCannotBeDissociatedError) {
Expand Down
1 change: 1 addition & 0 deletions api/src/shared/domain/errors.js
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ class CertificationEndedByFinalizationError extends DomainError {
class CertificationCandidateOnFinalizedSessionError extends DomainError {
constructor(message = "Cette session a déjà été finalisée, l'ajout de candidat n'est pas autorisé") {
super(message);
this.code = 'CANDIDATE_NOT_ALLOWED_FOR_FINALIZED_SESSION';
}
}

Expand Down
2 changes: 1 addition & 1 deletion api/tests/shared/unit/application/error-manager_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -661,7 +661,7 @@ describe('Shared | Unit | Application | ErrorManager', function () {
await handle(params.request, params.h, params.error);

// then
expect(HttpErrors.ForbiddenError).to.have.been.calledWithExactly(error.message);
expect(HttpErrors.ForbiddenError).to.have.been.calledWithExactly(error.message, error.code);
});

it('should instantiate ConflictError when CertificationEndedByFinalizationError', async function () {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,8 @@ export default class EnrolledCandidates extends Component {
return this._handleEntityValidationError(errorResponse);
case '400':
return this._handleMissingQueryParamError(errorResponse);
case '403':
return this._handleApiError(errorResponse);
default:
return this.intl.t(`${TRANSLATE_PREFIX}.add-modal.notifications.error-add-unknown`);
}
Expand All @@ -248,6 +250,15 @@ export default class EnrolledCandidates extends Component {
}
}

_handleApiError(errorResponse) {
const error = errorResponse?.errors?.[0];
if (error?.code) {
return this.intl.t(`common.api-error-messages.${error.code}`, {
...error?.meta,
});
}
}

_handleMissingQueryParamError(errorResponse) {
const error = errorResponse?.errors?.[0];
if (error?.detail === 'CANDIDATE_BIRTHDATE_FORMAT_NOT_VALID') {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,7 @@ module('Integration | Component | Sessions | SessionDetails | EnrolledCandidates

test('it should have an accessible table description', async function (assert) {
//given
const candidate = _buildCertificationCandidate({
birthdate: new Date('2019-04-28'),
subscriptions: [],
});
const candidate = _buildCertificationCandidate({ subscriptions: [] });

const certificationCandidates = [store.createRecord('certification-candidate', candidate)];
const countries = [store.createRecord('country', { name: 'CANADA', code: 99401 })];
Expand Down Expand Up @@ -89,7 +86,6 @@ module('Integration | Component | Sessions | SessionDetails | EnrolledCandidates
complementaryCertificationId,
});
const candidate = _buildCertificationCandidate({
birthdate: new Date('2019-04-28'),
accessibilityAdjustmentNeeded: true,
subscriptions: [coreSubscription, complementarySubscription],
});
Expand Down Expand Up @@ -131,9 +127,7 @@ module('Integration | Component | Sessions | SessionDetails | EnrolledCandidates

test('it should display details button', async function (assert) {
// given
const candidate = _buildCertificationCandidate({
subscriptions: [],
});
const candidate = _buildCertificationCandidate({ subscriptions: [] });
const certificationCandidates = [store.createRecord('certification-candidate', candidate)];
const countries = [store.createRecord('country', { name: 'CANADA', code: 99401 })];

Expand All @@ -158,9 +152,7 @@ module('Integration | Component | Sessions | SessionDetails | EnrolledCandidates

test('it should display details modal', async function (assert) {
// given
const candidate = _buildCertificationCandidate({
subscriptions: [],
});
const candidate = _buildCertificationCandidate({ subscriptions: [] });
const certificationCandidates = [store.createRecord('certification-candidate', candidate)];
const countries = [store.createRecord('country', { name: 'CANADA', code: 99401 })];

Expand Down Expand Up @@ -190,19 +182,9 @@ module('Integration | Component | Sessions | SessionDetails | EnrolledCandidates
test('it should be possible to delete the candidate', async function (assert) {
// given
const certificationCandidates = [
_buildCertificationCandidate({
id: 1,
firstName: 'Riri',
lastName: 'Duck',
subscriptions: [],
}),
_buildCertificationCandidate({ id: 2, firstName: 'Fifi', lastName: 'Duck', subscriptions: [] }),
_buildCertificationCandidate({
id: 3,
firstName: 'Loulou',
lastName: 'Duck',
subscriptions: [],
}),
_buildCertificationCandidate({ id: '1' }),
_buildCertificationCandidate({ id: '2', firstName: 'Lara', lastName: 'Pafromage' }),
_buildCertificationCandidate({ id: '3', firstName: 'Jean', lastName: 'Registre' }),
].map((candidateData) => store.createRecord('certification-candidate', candidateData));
const countries = [store.createRecord('country', { name: 'CANADA', code: 99401 })];

Expand All @@ -221,7 +203,7 @@ module('Integration | Component | Sessions | SessionDetails | EnrolledCandidates
</template>,
);

await click(screen.getByRole('button', { name: 'Supprimer le candidat Riri Duck' }));
await click(screen.getByRole('button', { name: 'Supprimer le candidat Eddy Taurial' }));

// then
sinon.assert.calledOnce(certificationCandidates[0].destroyRecord);
Expand All @@ -235,21 +217,9 @@ module('Integration | Component | Sessions | SessionDetails | EnrolledCandidates
test('it display candidates with delete button disabled', async function (assert) {
// given
const certificationCandidates = [
_buildCertificationCandidate({
id: 1,
firstName: 'Riri',
lastName: 'Duck',
isLinked: false,
subscriptions: [],
}),
_buildCertificationCandidate({ id: 2, firstName: 'Fifi', lastName: 'Duck', isLinked: true, subscriptions: [] }),
_buildCertificationCandidate({
id: 3,
firstName: 'Loulou',
lastName: 'Duck',
isLinked: false,
subscriptions: [],
}),
_buildCertificationCandidate({ id: '1' }),
_buildCertificationCandidate({ id: '2', firstName: 'Lara', lastName: 'Pafromage', isLinked: true }),
_buildCertificationCandidate({ id: '3', firstName: 'Jean', lastName: 'Registre' }),
].map((candidateData) => store.createRecord('certification-candidate', candidateData));
const countries = [store.createRecord('country', { name: 'CANADA', code: 99401 })];

Expand All @@ -266,13 +236,13 @@ module('Integration | Component | Sessions | SessionDetails | EnrolledCandidates

// then
assert
.dom(screen.getByRole('button', { name: 'Supprimer le candidat Riri Duck' }))
.dom(screen.getByRole('button', { name: 'Supprimer le candidat Eddy Taurial' }))
.hasClass(DELETE_BUTTON_SELECTOR);
assert
.dom(screen.getByRole('button', { name: 'Supprimer le candidat Fifi Duck' }))
.dom(screen.getByRole('button', { name: 'Supprimer le candidat Lara Pafromage' }))
.hasClass(DELETE_BUTTON_DISABLED_SELECTOR);
assert
.dom(screen.getByRole('button', { name: 'Supprimer le candidat Loulou Duck' }))
.dom(screen.getByRole('button', { name: 'Supprimer le candidat Jean Registre' }))
.hasClass(DELETE_BUTTON_SELECTOR);
});
});
Expand Down Expand Up @@ -308,27 +278,9 @@ module('Integration | Component | Sessions | SessionDetails | EnrolledCandidates
test('it display candidates with an edit button', async function (assert) {
// given
const certificationCandidates = [
_buildCertificationCandidate({
id: 1,
firstName: 'Riri',
lastName: 'Duck',
isLinked: false,
subscriptions: [],
}),
_buildCertificationCandidate({
id: 2,
firstName: 'Fifi',
lastName: 'Duck',
isLinked: true,
subscriptions: [],
}),
_buildCertificationCandidate({
id: 3,
firstName: 'Loulou',
lastName: 'Duck',
isLinked: false,
subscriptions: [],
}),
_buildCertificationCandidate({ id: '1' }),
_buildCertificationCandidate({ id: '2', firstName: 'Lara', lastName: 'Pafromage', isLinked: true }),
_buildCertificationCandidate({ id: '3', firstName: 'Jean', lastName: 'Registre' }),
].map((candidateData) => store.createRecord('certification-candidate', candidateData));
const countries = [store.createRecord('country', { name: 'CANADA', code: 99401 })];

Expand All @@ -346,12 +298,14 @@ module('Integration | Component | Sessions | SessionDetails | EnrolledCandidates
// then

// then
assert.dom(screen.getByRole('button', { name: 'Editer le candidat Riri Duck' })).hasClass(EDIT_BUTTON_SELECTOR);
assert
.dom(screen.getByRole('button', { name: 'Editer le candidat Fifi Duck' }))
.dom(screen.getByRole('button', { name: 'Editer le candidat Eddy Taurial' }))
.hasClass(EDIT_BUTTON_SELECTOR);
assert
.dom(screen.getByRole('button', { name: 'Editer le candidat Lara Pafromage' }))
.hasClass(EDIT_BUTTON_DISABLED_SELECTOR);
assert
.dom(screen.getByRole('button', { name: 'Editer le candidat Loulou Duck' }))
.dom(screen.getByRole('button', { name: 'Editer le candidat Jean Registre' }))
.hasClass(EDIT_BUTTON_SELECTOR);
assert.strictEqual(
screen.getAllByText("Ce candidat a déjà rejoint la session. Vous ne pouvez pas l'éditer.").length,
Expand All @@ -363,7 +317,6 @@ module('Integration | Component | Sessions | SessionDetails | EnrolledCandidates
test('should display candidate needs accessibility adjusted certification', async function (assert) {
// given
const candidate = _buildCertificationCandidate({
birthdate: new Date('2019-04-28'),
accessibilityAdjustmentNeeded: true,
});

Expand Down Expand Up @@ -391,7 +344,6 @@ module('Integration | Component | Sessions | SessionDetails | EnrolledCandidates
test('should display candidate doesnt need accessibility adjusted certification', async function (assert) {
// given
const candidate = _buildCertificationCandidate({
birthdate: new Date('2019-04-28'),
accessibilityAdjustmentNeeded: false,
});

Expand Down Expand Up @@ -420,7 +372,6 @@ module('Integration | Component | Sessions | SessionDetails | EnrolledCandidates
test('should not display accessibility adjusted certification needed information', async function (assert) {
// given
const candidate = _buildCertificationCandidate({
birthdate: new Date('2019-04-28'),
accessibilityAdjustmentNeeded: true,
});

Expand Down Expand Up @@ -450,20 +401,8 @@ module('Integration | Component | Sessions | SessionDetails | EnrolledCandidates
test('it does not display candidates with an edit button', async function (assert) {
// given
const certificationCandidates = [
_buildCertificationCandidate({
id: 1,
firstName: 'Riri',
lastName: 'Duck',
isLinked: false,
subscriptions: [],
}),
_buildCertificationCandidate({
id: 2,
firstName: 'Fifi',
lastName: 'Duck',
isLinked: true,
subscriptions: [],
}),
_buildCertificationCandidate({ id: '1' }),
_buildCertificationCandidate({ id: '2', firstName: 'Lara', lastName: 'Pafromage', isLinked: true }),
].map((candidateData) => store.createRecord('certification-candidate', candidateData));
const countries = [store.createRecord('country', { name: 'CANADA', code: 99401 })];

Expand All @@ -479,8 +418,8 @@ module('Integration | Component | Sessions | SessionDetails | EnrolledCandidates
);

// then
assert.dom(screen.queryByRole('button', { name: 'Editer le candidat Riri Duck' })).doesNotExist();
assert.dom(screen.queryByRole('button', { name: 'Editer le candidat Fifi Duck' })).doesNotExist();
assert.dom(screen.queryByRole('button', { name: 'Editer le candidat Eddy Taurial' })).doesNotExist();
assert.dom(screen.queryByRole('button', { name: 'Editer le candidat Lara Pafromage' })).doesNotExist();
});
});
});
Expand All @@ -498,7 +437,6 @@ module('Integration | Component | Sessions | SessionDetails | EnrolledCandidates
complementaryCertificationId: cleaCertificationId,
});
const candidate = _buildCertificationCandidate({
birthdate: new Date('2019-04-28'),
subscriptions: [coreSubscription, complementarySubscription],
});
const complementaryCertifications = [
Expand Down Expand Up @@ -728,18 +666,18 @@ module('Integration | Component | Sessions | SessionDetails | EnrolledCandidates

function _buildCertificationCandidate({
id = '12345',
firstName = 'Bob',
lastName = 'Leponge',
birthdate = new Date(),
birthCity = 'Marseille',
birthProvinceCode = '',
birthCountry = '',
email = '[email protected]',
resultRecipientEmail = '[email protected]',
firstName = 'Eddy',
lastName = 'Taurial',
birthdate = '1990-03-22',
birthCity = 'Sainte-Anne',
birthProvinceCode = '01',
birthCountry = 'France',
email = '[email protected]',
resultRecipientEmail = '[email protected]',
externalId = 'an external id',
extraTimePercentage = 0.3,
isLinked = false,
billingMode = null,
billingMode = 'FREE',
prepaymentCode = null,
accessibilityAdjustmentNeeded = false,
subscriptions = [],
Expand Down
3 changes: 2 additions & 1 deletion certif/translations/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
"validate": "Validate"
},
"api-error-messages": {
"CANDIDATE_NOT_ALLOWED_FOR_FINALIZED_SESSION": "This session has already been finalised and no new candidates may be added.",
"CANDIDATE_NOT_FOUND": "An error has occurred, the candidate {firstName} {lastName} hasn't been found.",
"INVALID_DOCUMENT": "This version of the document is unknown.<br />Please download and import the candidate list template again",
"SESSION_ALREADY_FINALIZED": "Cannot finalize session more than once.",
Expand Down Expand Up @@ -1029,4 +1030,4 @@
"page-title": "Terms and conditions"
}
}
}
}
3 changes: 2 additions & 1 deletion certif/translations/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
"validate": "Valider"
},
"api-error-messages": {
"CANDIDATE_NOT_ALLOWED_FOR_FINALIZED_SESSION": "Cette session a déjà été finalisée, l'ajout de candidat n'est pas autorisé.",
"CANDIDATE_NOT_FOUND": "Une erreur est survenue, le candidat {firstName} {lastName} n'a pas été trouvé.",
"INVALID_DOCUMENT": "La version du document est inconnue.<br />Veuillez télécharger à nouveau le modèle de liste des candidats et l'importer à nouveau.",
"SESSION_ALREADY_FINALIZED": "La session a déjà été finalisée.",
Expand Down Expand Up @@ -1029,4 +1030,4 @@
"page-title": "Conditions générales"
}
}
}
}

0 comments on commit b97aa51

Please sign in to comment.