Skip to content

Commit

Permalink
[FEATURE] Enregistrer la confirmation de lecture des écrans d'instruc…
Browse files Browse the repository at this point in the history
…tions sur Pix App (PIX-12907).

 #9443
  • Loading branch information
pix-service-auto-merge committed Jul 8, 2024
2 parents f7e5a86 + 7987db4 commit 09280bc
Show file tree
Hide file tree
Showing 18 changed files with 273 additions and 50 deletions.
3 changes: 3 additions & 0 deletions api/lib/domain/models/CertificationCandidate.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ const certificationCandidateParticipationJoiSchema = Joi.object({
.empty(null),
prepaymentCode: Joi.string().allow(null).optional(),
subscriptions: Joi.array().items(subscriptionSchema).unique('type').required(),
hasSeenCertificationInstructions: Joi.boolean().optional(),
});

class CertificationCandidate {
Expand Down Expand Up @@ -84,6 +85,7 @@ class CertificationCandidate {
billingMode = null,
prepaymentCode = null,
subscriptions = [],
hasSeenCertificationInstructions = false,
} = {}) {
this.id = id;
this.firstName = firstName;
Expand All @@ -107,6 +109,7 @@ class CertificationCandidate {
this.subscriptions = subscriptions;
this.billingMode = billingMode;
this.prepaymentCode = prepaymentCode;
this.hasSeenCertificationInstructions = hasSeenCertificationInstructions;

Object.defineProperty(this, 'complementaryCertification', {
enumerable: true,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ const serialize = function (certificationCandidates) {
'complementaryCertification',
'billingMode',
'prepaymentCode',
'hasSeenCertificationInstructions',
],
}).serialize(certificationCandidates);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ describe('Unit | Domain | Models | Certification Candidate', function () {
resultRecipientEmail: undefined,
complementaryCertification: null,
subscriptions: [coreSubscription],
hasSeenCertificationInstructions: false,
};
});

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import ApplicationAdapter from './application';

export default class AccountRecoveryDemandAdapter extends ApplicationAdapter {
export default class CertificationCandidateSubscription extends ApplicationAdapter {
urlForFindRecord(id) {
return `${this.host}/${this.namespace}/certification-candidates/${id}/subscriptions`;
}
Expand Down
11 changes: 11 additions & 0 deletions mon-pix/app/adapters/certification-candidate.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,15 @@ export default class CertificationCandidate extends ApplicationAdapter {

return url;
}

urlForUpdateRecord(id, modelName, { adapterOptions }) {
const url = super.urlForUpdateRecord(...arguments);

if (adapterOptions && adapterOptions.hasSeenCertificationInstructions) {
delete adapterOptions.hasSeenCertificationInstructions;
return `${url}/validate-certification-instructions`;
}

return url;
}
}
19 changes: 13 additions & 6 deletions mon-pix/app/components/certification-instructions/steps.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,20 +43,27 @@ export default class Steps extends Component {
}

@action
nextStep() {
async nextStep() {
if (this.pageId < this.pageCount) {
this.pageId = this.pageId + 1;
}

if (this.isConfirmationCheckboxChecked) {
this.router.transitionTo('authenticated.certifications.start', this.args.candidateId, {
queryParams: {
isConfirmationCheckboxChecked: this.isConfirmationCheckboxChecked,
},
});
await this.submit();

this.router.transitionTo('authenticated.certifications.start', this.args.candidate.id);
}
}

@action
async submit() {
await this.args.candidate.save({
adapterOptions: {
hasSeenCertificationInstructions: true,
},
});
}

@action
enableNextButton(checked) {
this.isConfirmationCheckboxChecked = checked;
Expand Down
1 change: 1 addition & 0 deletions mon-pix/app/models/certification-candidate.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export default class CertificationCandidate extends Model {
@attr('string') firstName;
@attr('string') lastName;
@attr('date-only') birthdate;
@attr('boolean') hasSeenCertificationInstructions;

// references
@attr('number') sessionId;
Expand Down
12 changes: 11 additions & 1 deletion mon-pix/app/routes/authenticated/certifications/information.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,18 @@ import { service } from '@ember/service';

export default class InformationRoute extends Route {
@service store;
@service router;

async model(params) {
return await this.store.findRecord('certification-candidate-subscription', params.certification_candidate_id);
const certificationCandidate = await this.store.peekRecord(
'certification-candidate',
params.certification_candidate_id,
);

if (!certificationCandidate) {
this.router.replaceWith('authenticated.certifications.join');
}

return certificationCandidate;
}
}
14 changes: 8 additions & 6 deletions mon-pix/app/routes/authenticated/certifications/start.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,22 @@ export default class StartRoute extends Route {
@service store;
@service router;
@service featureToggles;
hasSeenCertificationInstructions = false;

beforeModel(transition) {
this.hasSeenCertificationInstructions = transition.to.queryParams.isConfirmationCheckboxChecked;
}

async model(params) {
const certificationCandidateSubscription = await this.store.findRecord(
'certification-candidate-subscription',
params.certification_candidate_id,
);

const certificationCandidate = await this.store.peekRecord(
'certification-candidate',
params.certification_candidate_id,
);

const hasSeenCertificationInstructions = certificationCandidate?.hasSeenCertificationInstructions;

if (
!this.hasSeenCertificationInstructions &&
!hasSeenCertificationInstructions &&
certificationCandidateSubscription.isSessionVersion3 &&
this.featureToggles.featureToggles.areV3InfoScreensEnabled
) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,6 @@
<h1 class="instructions-header__title">{{t "pages.certification-instructions.title"}}</h1>
</header>
<PixBlock @shadow="heavy" class="instructions-step">
<CertificationInstructions::Steps @candidateId={{this.model.id}} />
<CertificationInstructions::Steps @candidate={{this.model}} />
</PixBlock>
</main>
6 changes: 6 additions & 0 deletions mon-pix/mirage/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import putTutorialEvaluation from './routes/put-tutorial-evaluation';
import putUserSavedTutorial from './routes/put-user-saved-tutorial';
import loadScoOrganizationLearnersRoutes from './routes/sco-organization-learners/index';
import loadSupOrganizationLearnersRoutes from './routes/sup-organization-learners/index';
import updateCertificationCandidates from './routes/update-certification-candidates';
import loadUserRoutes from './routes/users/index';

export default function makeServer(config) {
Expand Down Expand Up @@ -108,4 +109,9 @@ function routes() {
this.get('/certification-courses/:id');

this.post('/feedbacks');

this.patch(
'/certification-candidates/:certificationCandidateId/validate-certification-instructions',
updateCertificationCandidates,
);
}
5 changes: 5 additions & 0 deletions mon-pix/mirage/routes/post-session-participation.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,13 @@ export default function (schema, request) {
const firstName = params.data.attributes['first-name'];
const lastName = params.data.attributes['last-name'];
const birthdate = params.data.attributes['birthdate'];
let hasSeenCertificationInstructions = false;
if (!every([firstName, lastName, birthdate, sessionId])) {
return new Response(400);
}
if (lastName === 'hasSeenCertificationInstructions') {
hasSeenCertificationInstructions = true;
}
if (lastName === 'PasInscrite') {
return new Response(404);
}
Expand All @@ -32,5 +36,6 @@ export default function (schema, request) {
lastName: 'Bravo',
sessionId: 1,
birthdate: '1990-01-04',
hasSeenCertificationInstructions,
});
}
7 changes: 7 additions & 0 deletions mon-pix/mirage/routes/update-certification-candidates.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export default function (schema, request) {
const certificationCandidateId = request.params.certificationCandidateId;
const certificationCandidate = schema.certificationCandidates.find(certificationCandidateId);
certificationCandidate.update({ hasSeenCertificationInstructions: true });

return certificationCandidate;
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { visit } from '@1024pix/ember-testing-library';
import { currentURL } from '@ember/test-helpers';
import { click, currentURL } from '@ember/test-helpers';
import { setupMirage } from 'ember-cli-mirage/test-support';
import { setupApplicationTest } from 'ember-qunit';
import { module, test } from 'qunit';
Expand All @@ -21,7 +21,7 @@ module('Acceptance | Certifications | Information', function (hooks) {

module('when certification candidate participates in a V3 session', function () {
module('when toggle areV3InfoScreensEnabled is enabled', function () {
test('should display the certification information page', async function (assert) {
test('should display the certification instructions page', async function (assert) {
// given
server.create('feature-toggle', {
id: 0,
Expand Down Expand Up @@ -56,6 +56,88 @@ module('Acceptance | Certifications | Information', function (hooks) {
assert.dom(screen.getByRole('heading', { name: 'Bienvenue à la certification Pix', level: 2 })).exists();
assert.dom(screen.getByRole('button', { name: "Continuer vers l'écran suivant" })).exists();
});

module('when user validates instructions', function () {
test('should validate checkbox and redirect to the certification start page', async function (assert) {
// given
server.create('feature-toggle', {
id: 0,
areV3InfoScreensEnabled: true,
});
server.create('certification-candidate-subscription', {
id: 2,
sessionId: 123,
eligibleSubscription: null,
nonEligibleSubscription: null,
sessionVersion: 3,
});

await authenticateByEmail(user);

const screen = await visit('/certifications');

// when
await fillCertificationJoiner({
sessionId: '123',
firstName: 'toto',
lastName: 'titi',
dayOfBirth: '01',
monthOfBirth: '01',
yearOfBirth: '2000',
intl: this.intl,
});
for (let i = 0; i < 4; i++) {
await click(screen.getByRole('button', { name: "Continuer vers l'écran suivant" }));
}

await click(
screen.getByRole('checkbox', {
name: 'En cochant cette case, je reconnais avoir pris connaissance de ces règles et je m’engage à les respecter.',
}),
);
await click(screen.getByRole('button', { name: "Continuer vers la page d'entrée en certification" }));

// then
assert.strictEqual(currentURL(), '/certifications/candidat/2');
});
});

module('when user has already validated instructions', function () {
test('should redirect to the certification start page', async function (assert) {
// given
server.create('feature-toggle', {
id: 0,
areV3InfoScreensEnabled: true,
});
server.create('certification-candidate-subscription', {
id: 2,
sessionId: 123,
eligibleSubscription: null,
nonEligibleSubscription: null,
sessionVersion: 3,
});

const candidateLastName = 'hasSeenCertificationInstructions';

await authenticateByEmail(user);

await visit('/certifications');

// when
await fillCertificationJoiner({
sessionId: '123',
firstName: 'toto',
lastName: candidateLastName,
dayOfBirth: '01',
monthOfBirth: '01',
yearOfBirth: '2000',
intl: this.intl,
});

// then
assert.strictEqual(currentURL(), '/certifications/candidat/2');
});
});
});

module('when toggle areV3InfoScreensEnabled is not enabled', function () {
Expand Down
24 changes: 24 additions & 0 deletions mon-pix/tests/unit/adapters/certification-candidate_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,28 @@ module('Unit | Adapters | certification-candidate', function (hooks) {
assert.true(url.endsWith('/sessions/456/candidate-participation'));
});
});

module('#urlForUpdateRecord', function () {
module('when hasSeenCertificationInstructions option is true', function () {
test('should redirect to session/id/certification-candidate/participation', async function (assert) {
// when
const options = { adapterOptions: { hasSeenCertificationInstructions: true } };
const url = await adapter.urlForUpdateRecord(456, 'certification-candidate', options);

// then
assert.true(url.endsWith('/certification-candidates/456/validate-certification-instructions'));
});
});

module('when hasSeenCertificationInstructions option is false', function () {
test('should build create url from certification-candidate id', async function (assert) {
// when
const options = { adapterOptions: {} };
const url = await adapter.urlForUpdateRecord(456, 'certification-candidate', options);

// then
assert.true(url.endsWith('/certification-candidates/456'));
});
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -27,32 +27,29 @@ module('Unit | Component | certification-instruction | steps', function (hooks)

module('when pageId equal pageCount', function () {
module('when confirmation checkbox is checked', function () {
test('should redirect to certificartion starter', async function (assert) {
test('should redirect to certification starter', async function (assert) {
// given
const component = createGlimmerComponent('certification-instructions/steps');

component.pageId = 2;
component.pageCount = 2;
component.args = {
candidateId: 123,
};
component.isConfirmationCheckboxChecked = true;
const transitionToStub = sinon.stub();
const saveStub = sinon.stub();
saveStub.resolves();
component.router = {
transitionTo: transitionToStub,
};
component.args.candidate = {
save: saveStub,
id: 123,
};

// when
await component.nextStep();

// then
assert.ok(
transitionToStub.calledWith('authenticated.certifications.start', 123, {
queryParams: {
isConfirmationCheckboxChecked: true,
},
}),
);
assert.ok(transitionToStub.calledWith('authenticated.certifications.start', 123));
});
});
});
Expand Down
Loading

0 comments on commit 09280bc

Please sign in to comment.