diff --git a/api/lib/domain/usecases/correct-answer-then-update-assessment.js b/api/lib/domain/usecases/correct-answer-then-update-assessment.js index 5877871f027..08eb3b978f0 100644 --- a/api/lib/domain/usecases/correct-answer-then-update-assessment.js +++ b/api/lib/domain/usecases/correct-answer-then-update-assessment.js @@ -9,7 +9,13 @@ import { Examiner } from '../../../src/shared/domain/models/Examiner.js'; import { KnowledgeElement } from '../../../src/shared/domain/models/index.js'; import { logger } from '../../../src/shared/infrastructure/utils/logger.js'; -const evaluateAnswer = function ({ challenge, answer, assessment, examiner: injectedExaminer }) { +const evaluateAnswer = function ({ + challenge, + answer, + assessment, + examiner: injectedExaminer, + accessibilityAdjustmentNeeded, +}) { const examiner = injectedExaminer ?? new Examiner({ validator: challenge.validator }); try { return examiner.evaluate({ @@ -18,6 +24,7 @@ const evaluateAnswer = function ({ challenge, answer, assessment, examiner: inje isFocusedChallenge: challenge.focused, hasLastQuestionBeenFocusedOut: assessment.hasLastQuestionBeenFocusedOut, isCertificationEvaluation: assessment.isCertification(), + accessibilityAdjustmentNeeded, }); } catch (error) { throw new AnswerEvaluationError(challenge); @@ -126,6 +133,7 @@ const correctAnswerThenUpdateAssessment = async function ({ skillRepository, campaignRepository, knowledgeElementRepository, + certificationEvaluationCandidateRepository, flashAssessmentResultRepository, certificationChallengeLiveAlertRepository, flashAlgorithmService, @@ -152,17 +160,31 @@ const correctAnswerThenUpdateAssessment = async function ({ const challenge = await challengeRepository.get(answer.challengeId); - const onGoingCertificationChallengeLiveAlert = - await certificationChallengeLiveAlertRepository.getOngoingByChallengeIdAndAssessmentId({ - challengeId: challenge.id, + let certificationCandidate; + + if (assessment.isCertification()) { + const onGoingCertificationChallengeLiveAlert = + await certificationChallengeLiveAlertRepository.getOngoingByChallengeIdAndAssessmentId({ + challengeId: challenge.id, + assessmentId: assessment.id, + }); + + if (onGoingCertificationChallengeLiveAlert) { + throw new ForbiddenAccess('An alert has been set.'); + } + + certificationCandidate = await certificationEvaluationCandidateRepository.findByAssessmentId({ assessmentId: assessment.id, }); - - if (onGoingCertificationChallengeLiveAlert) { - throw new ForbiddenAccess('An alert has been set.'); } - const correctedAnswer = evaluateAnswer({ challenge, answer, assessment, examiner }); + const correctedAnswer = evaluateAnswer({ + challenge, + answer, + assessment, + examiner, + accessibilityAdjustmentNeeded: certificationCandidate?.accessibilityAdjustmentNeeded, + }); const now = dateUtils.getNowDate(); const lastQuestionDate = assessment.lastQuestionDate || now; correctedAnswer.setTimeSpentFrom({ now, lastQuestionDate }); diff --git a/api/lib/domain/usecases/index.js b/api/lib/domain/usecases/index.js index b3366796555..b771827b297 100644 --- a/api/lib/domain/usecases/index.js +++ b/api/lib/domain/usecases/index.js @@ -10,6 +10,7 @@ import * as centerRepository from '../../../src/certification/enrolment/infrastr import * as certificationCandidateRepository from '../../../src/certification/enrolment/infrastructure/repositories/certification-candidate-repository.js'; import * as certificationCpfCityRepository from '../../../src/certification/enrolment/infrastructure/repositories/certification-cpf-city-repository.js'; import * as sessionEnrolmentRepository from '../../../src/certification/enrolment/infrastructure/repositories/session-repository.js'; +import * as certificationEvaluationCandidateRepository from '../../../src/certification/evaluation/infrastructure/repositories/certification-candidate-repository.js'; import * as flashAlgorithmService from '../../../src/certification/flash-certification/domain/services/algorithm-methods/flash.js'; import * as certificationOfficerRepository from '../../../src/certification/session-management/infrastructure/repositories/certification-officer-repository.js'; import * as finalizedSessionRepository from '../../../src/certification/session-management/infrastructure/repositories/finalized-session-repository.js'; @@ -175,6 +176,7 @@ function requirePoleEmploiNotifier() { * @typedef {certificationBadgesService} CertificationBadgesService * @typedef {certificationCenterRepository} CertificationCenterRepository * @typedef {certificationRepository} CertificationRepository + * @typedef {certificationEvaluationCandidateRepository} CertificationEvaluationCandidateRepository * @typedef {complementaryCertificationRepository} ComplementaryCertificationRepository * @typedef {complementaryCertificationCourseRepository} ComplementaryCertificationCourseRepository * @typedef {finalizedSessionRepository} FinalizedSessionRepository @@ -225,6 +227,7 @@ const dependencies = { certificationAssessmentRepository, certificationBadgesService, certificationCandidateRepository, + certificationEvaluationCandidateRepository, certificationCenterForAdminRepository, certificationCenterInvitationRepository, certificationCenterInvitationService, diff --git a/api/src/certification/evaluation/domain/models/Candidate.js b/api/src/certification/evaluation/domain/models/Candidate.js new file mode 100644 index 00000000000..970ee5f3495 --- /dev/null +++ b/api/src/certification/evaluation/domain/models/Candidate.js @@ -0,0 +1,9 @@ +export class Candidate { + /** + * @param {Object} params + * @param {boolean} [params.accessibilityAdjustmentNeeded] + */ + constructor({ accessibilityAdjustmentNeeded } = {}) { + this.accessibilityAdjustmentNeeded = !!accessibilityAdjustmentNeeded; + } +} diff --git a/api/src/certification/evaluation/infrastructure/repositories/certification-candidate-repository.js b/api/src/certification/evaluation/infrastructure/repositories/certification-candidate-repository.js index baaacdedc5c..c84f0c9bcac 100644 --- a/api/src/certification/evaluation/infrastructure/repositories/certification-candidate-repository.js +++ b/api/src/certification/evaluation/infrastructure/repositories/certification-candidate-repository.js @@ -1,6 +1,6 @@ import { knex } from '../../../../../db/knex-database-connection.js'; import { CertificationCandidateNotFoundError } from '../../../../shared/domain/errors.js'; -import { Candidate } from '../../../enrolment/domain/models/Candidate.js'; +import { Candidate } from '../../domain/models/Candidate.js'; const findByAssessmentId = async function ({ assessmentId }) { const result = await knex('certification-candidates') diff --git a/api/src/shared/domain/models/Examiner.js b/api/src/shared/domain/models/Examiner.js index 4b01dd7e558..f3884d52112 100644 --- a/api/src/shared/domain/models/Examiner.js +++ b/api/src/shared/domain/models/Examiner.js @@ -10,7 +10,14 @@ class Examiner { this.validator = validator; } - evaluate({ answer, challengeFormat, isFocusedChallenge, isCertificationEvaluation, hasLastQuestionBeenFocusedOut }) { + evaluate({ + answer, + challengeFormat, + isFocusedChallenge, + isCertificationEvaluation, + hasLastQuestionBeenFocusedOut, + accessibilityAdjustmentNeeded, + }) { const correctedAnswer = new Answer(answer); if (answer.value === Answer.FAKE_VALUE_FOR_SKIPPED_QUESTIONS) { @@ -68,7 +75,7 @@ class Examiner { } if (isCorrectAnswer && isFocusedChallenge && answer.isFocusedOut && isCertificationEvaluation) { - correctedAnswer.result = AnswerStatus.FOCUSEDOUT; + correctedAnswer.result = accessibilityAdjustmentNeeded ? AnswerStatus.OK : AnswerStatus.FOCUSEDOUT; correctedAnswer.isFocusedOut = true; } diff --git a/api/tests/certification/evaluation/acceptance/answer-route_test.js b/api/tests/certification/evaluation/acceptance/answer-route_test.js new file mode 100644 index 00000000000..00f38e41c26 --- /dev/null +++ b/api/tests/certification/evaluation/acceptance/answer-route_test.js @@ -0,0 +1,169 @@ +import { + createServer, + databaseBuilder, + expect, + generateValidRequestAuthorizationHeader, + knex, + mockLearningContent, +} from '../../../test-helper.js'; + +describe('Certification | Evaluation | Acceptance | answer-route', function () { + let server; + + beforeEach(async function () { + server = await createServer(); + }); + + describe('POST /api/answers', function () { + context('when challenge is focused out and answer is correct', function () { + context('when the candidate needs an accessibility adjustment', function () { + it('should save the answer as correct', async function () { + // given + const { competenceId, challengeId } = _buildLearningContent(); + const { assessmentId, userId } = await _setupTestData(databaseBuilder, { + competenceId, + doesCandidateNeedAccessibilityAdjustment: true, + }); + const options = _setupRequestOptions({ userId, challengeId, assessmentId }); + + // when + await server.inject(options); + + // then + const [answer] = await knex('answers'); + expect(answer.result).to.equal('ok'); + expect(answer.isFocusedOut).to.equal(true); + }); + }); + + context('when the candidate does not need an accessibility adjustment', function () { + it('should save the answer as focused out', async function () { + // given + const { competenceId, challengeId } = _buildLearningContent(); + const { assessmentId, userId } = await _setupTestData(databaseBuilder, { + competenceId, + doesCandidateNeedAccessibilityAdjustment: false, + }); + const options = _setupRequestOptions({ userId, challengeId, assessmentId }); + + // when + await server.inject(options); + + // then + const [answer] = await knex('answers'); + expect(answer.result).to.equal('focusedOut'); + expect(answer.isFocusedOut).to.equal(true); + }); + }); + }); + }); +}); + +async function _setupTestData(databaseBuilder, { competenceId, doesCandidateNeedAccessibilityAdjustment }) { + const userId = databaseBuilder.factory.buildUser().id; + + const session = databaseBuilder.factory.buildSession({}); + + databaseBuilder.factory.buildCertificationCandidate({ + sessionId: session.id, + userId, + accessibilityAdjustmentNeeded: doesCandidateNeedAccessibilityAdjustment, + }); + + const certificationCourse = databaseBuilder.factory.buildCertificationCourse({ + userId, + sessionId: session.id, + }); + + const assessment = databaseBuilder.factory.buildAssessment({ + type: 'CERTIFICATION', + userId, + competenceId, + certificationCourseId: certificationCourse.id, + }); + + await databaseBuilder.commit(); + + return { assessmentId: assessment.id, userId }; +} + +function _setupRequestOptions({ userId, challengeId, assessmentId }) { + return { + method: 'POST', + url: '/api/answers', + headers: { authorization: generateValidRequestAuthorizationHeader(userId) }, + payload: { + data: { + type: 'answers', + attributes: { + value: 'correct', + 'focused-out': true, + }, + relationships: { + assessment: { + data: { + type: 'assessments', + id: assessmentId, + }, + }, + challenge: { + data: { + type: 'challenges', + id: challengeId, + }, + }, + }, + }, + }, + }; +} + +function _buildLearningContent() { + const challengeId = 'a_challenge_id'; + const competenceId = 'recCompetence'; + + const learningContent = { + areas: [{ id: 'recArea1', competenceIds: ['recCompetence'] }], + competences: [ + { + id: 'recCompetence', + areaId: 'recArea1', + skillIds: ['recSkill1'], + origin: 'Pix', + name_i18n: { + fr: 'Nom de la competence FR', + en: 'Nom de la competence EN', + }, + statue: 'active', + }, + ], + skills: [ + { + id: 'recSkill1', + name: '@recArea1_Competence1_Tube1_Skill1', + status: 'actif', + competenceId: competenceId, + pixValue: '5', + }, + ], + challenges: [ + { + id: challengeId, + competenceId: competenceId, + skillId: 'recSkill1', + status: 'validé', + solution: 'correct', + proposals: '${a}', + locales: ['fr-fr'], + type: 'QROC', + focusable: true, + }, + ], + }; + mockLearningContent(learningContent); + + return { + competenceId, + challengeId, + }; +} diff --git a/api/tests/certification/evaluation/integration/infrastructure/repositories/certification-candidate-repository_test.js b/api/tests/certification/evaluation/integration/infrastructure/repositories/certification-candidate-repository_test.js index 23326790324..24b275062dc 100644 --- a/api/tests/certification/evaluation/integration/infrastructure/repositories/certification-candidate-repository_test.js +++ b/api/tests/certification/evaluation/integration/infrastructure/repositories/certification-candidate-repository_test.js @@ -36,9 +36,8 @@ describe('Integration | Repository | certification candidate', function () { // then expect(result).to.deep.equal( - domainBuilder.certification.enrolment.buildCandidate({ + domainBuilder.certification.evaluation.buildCandidate({ ...candidate, - subscriptions: [], }), ); }); @@ -124,9 +123,8 @@ describe('Integration | Repository | certification candidate', function () { // then expect(result).to.deep.equal( - domainBuilder.certification.enrolment.buildCandidate({ + domainBuilder.certification.evaluation.buildCandidate({ ...candidate, - subscriptions: [], }), ); }); diff --git a/api/tests/shared/unit/domain/models/Examiner_test.js b/api/tests/shared/unit/domain/models/Examiner_test.js index 33f0034ecf2..632ff764815 100644 --- a/api/tests/shared/unit/domain/models/Examiner_test.js +++ b/api/tests/shared/unit/domain/models/Examiner_test.js @@ -91,50 +91,86 @@ describe('Unit | Domain | Models | Examiner', function () { }); context('and is a focused challenge', function () { - beforeEach(function () { - // given - isFocusedChallenge = true; - }); - - it('should return an answer with FOCUSEDOUT as result when the assessment is a certification, and the correct resultDetails', function () { - // given - const expectedAnswer = new Answer(uncorrectedAnswer); - expectedAnswer.result = AnswerStatus.FOCUSEDOUT; - expectedAnswer.resultDetails = validation.resultDetails; - - // when - correctedAnswer = examiner.evaluate({ - answer: uncorrectedAnswer, - challengeFormat, - isFocusedChallenge, - isCertificationEvaluation: true, + context('when the assessment is a certification', function () { + context('when the candidate needs an accessibility adjustment', function () { + it('should return an answer with OK as result, and the correct resultDetails', function () { + // given + const isFocusedChallenge = true; + const certificationCandidate = domainBuilder.certification.evaluation.buildCandidate({ + accessibilityAdjustmentNeeded: true, + }); + const expectedAnswer = new Answer(uncorrectedAnswer); + expectedAnswer.result = AnswerStatus.OK; + expectedAnswer.resultDetails = validation.resultDetails; + + // when + correctedAnswer = examiner.evaluate({ + answer: uncorrectedAnswer, + challengeFormat, + isFocusedChallenge, + isCertificationEvaluation: true, + accessibilityAdjustmentNeeded: certificationCandidate.accessibilityAdjustmentNeeded, + }); + + // then + expect(correctedAnswer).to.be.an.instanceOf(Answer); + expect(correctedAnswer).to.deep.equal(expectedAnswer); + }); }); - // then - expect(correctedAnswer).to.be.an.instanceOf(Answer); - expect(correctedAnswer).to.deep.equal(expectedAnswer); + context('when the candidate does not need an accessibility adjustment', function () { + it('should return an answer with FOCUSEDOUT as a result, and the correct resultDetails', function () { + // given + const isFocusedChallenge = true; + const certificationCandidate = domainBuilder.certification.evaluation.buildCandidate({ + accessibilityAdjustmentNeeded: false, + }); + const expectedAnswer = new Answer(uncorrectedAnswer); + expectedAnswer.result = AnswerStatus.FOCUSEDOUT; + expectedAnswer.resultDetails = validation.resultDetails; + + // when + correctedAnswer = examiner.evaluate({ + answer: uncorrectedAnswer, + challengeFormat, + isFocusedChallenge, + isCertificationEvaluation: true, + accessibilityAdjustmentNeeded: certificationCandidate.accessibilityAdjustmentNeeded, + }); + + // then + expect(correctedAnswer).to.be.an.instanceOf(Answer); + expect(correctedAnswer).to.deep.equal(expectedAnswer); + }); + }); }); - it('should return an answer with OK as result when the assessment is not a certification, and the correct resultDetails', function () { - // given - const expectedAnswer = new Answer(uncorrectedAnswer); - expectedAnswer.result = AnswerStatus.OK; - expectedAnswer.resultDetails = validation.resultDetails; - - // when - correctedAnswer = examiner.evaluate({ - answer: uncorrectedAnswer, - challengeFormat, - isFocusedChallenge, - isCertificationEvaluation: false, + context('when the assessment is not a certification', function () { + it('should return an answer with OK as result, and the correct resultDetails', function () { + // given + const isFocusedChallenge = true; + const expectedAnswer = new Answer(uncorrectedAnswer); + expectedAnswer.result = AnswerStatus.OK; + expectedAnswer.resultDetails = validation.resultDetails; + + // when + correctedAnswer = examiner.evaluate({ + answer: uncorrectedAnswer, + challengeFormat, + isFocusedChallenge, + isCertificationEvaluation: false, + }); + + // then + expect(correctedAnswer).to.be.an.instanceOf(Answer); + expect(correctedAnswer).to.deep.equal(expectedAnswer); }); - - // then - expect(correctedAnswer).to.be.an.instanceOf(Answer); - expect(correctedAnswer).to.deep.equal(expectedAnswer); }); it('should call validator.assess with answer to assess validity of answer', function () { + // given + const isFocusedChallenge = true; + // when examiner.evaluate({ answer: uncorrectedAnswer, diff --git a/api/tests/tooling/domain-builder/factory/certification/evaluation/build-candidate.js b/api/tests/tooling/domain-builder/factory/certification/evaluation/build-candidate.js new file mode 100644 index 00000000000..1262cc0763d --- /dev/null +++ b/api/tests/tooling/domain-builder/factory/certification/evaluation/build-candidate.js @@ -0,0 +1,9 @@ +import { Candidate } from '../../../../../../src/certification/evaluation/domain/models/Candidate.js'; + +const buildEvaluationCandidate = function ({ accessibilityAdjustmentNeeded } = {}) { + return new Candidate({ + accessibilityAdjustmentNeeded, + }); +}; + +export { buildEvaluationCandidate }; diff --git a/api/tests/tooling/domain-builder/factory/index.js b/api/tests/tooling/domain-builder/factory/index.js index e02d300af1f..6adeb0fe29b 100644 --- a/api/tests/tooling/domain-builder/factory/index.js +++ b/api/tests/tooling/domain-builder/factory/index.js @@ -187,6 +187,7 @@ import { import { buildUserEnrolment } from './certification/enrolment/build-user.js'; import { buildUserCertificationEligibility } from './certification/enrolment/build-user-certification-eligibility.js'; import { buildV3CertificationEligibility } from './certification/enrolment/build-v3-certification-eligibility.js'; +import { buildEvaluationCandidate } from './certification/evaluation/build-candidate.js'; import { buildFlashAssessmentAlgorithm } from './certification/flash-certification/build-flash-assessment-algorithm.js'; import { buildAssessmentResult as buildCertificationScoringAssessmentResult } from './certification/scoring/build-assessment-result.js'; import { buildCertificationAssessmentHistory } from './certification/scoring/build-certification-assessment-history.js'; @@ -234,6 +235,9 @@ const certification = { buildPixCertification, buildComplementaryCertificationBadge: buildComplementaryCertificationBadgeForEnrolment, }, + evaluation: { + buildCandidate: buildEvaluationCandidate, + }, sessionManagement: { buildCertificationSessionComplementaryCertification, buildSession: buildSessionManagement, diff --git a/api/tests/unit/domain/usecases/correct-answer-then-update-assessment_test.js b/api/tests/unit/domain/usecases/correct-answer-then-update-assessment_test.js index 3bd574f8f25..80efc8b6bdf 100644 --- a/api/tests/unit/domain/usecases/correct-answer-then-update-assessment_test.js +++ b/api/tests/unit/domain/usecases/correct-answer-then-update-assessment_test.js @@ -35,6 +35,7 @@ describe('Unit | Domain | Use Cases | correct-answer-then-update-assessment', fu scorecardService, knowledgeElementRepository, certificationChallengeLiveAlertRepository, + certificationEvaluationCandidateRepository, flashAlgorithmService, algorithmDataFetcherService; const competenceEvaluationRepository = {}; @@ -56,6 +57,7 @@ describe('Unit | Domain | Use Cases | correct-answer-then-update-assessment', fu scorecardService = { computeScorecard: sinon.stub() }; knowledgeElementRepository = { findUniqByUserIdAndAssessmentId: sinon.stub() }; certificationChallengeLiveAlertRepository = { getOngoingByChallengeIdAndAssessmentId: sinon.stub() }; + certificationEvaluationCandidateRepository = { findByAssessmentId: sinon.stub() }; flashAlgorithmService = { getCapacityAndErrorRate: sinon.stub() }; algorithmDataFetcherService = { fetchForFlashLevelEstimation: sinon.stub() }; dateUtils = { @@ -86,6 +88,7 @@ describe('Unit | Domain | Use Cases | correct-answer-then-update-assessment', fu knowledgeElementRepository, flashAssessmentResultRepository, certificationChallengeLiveAlertRepository, + certificationEvaluationCandidateRepository, scorecardService, flashAlgorithmService, algorithmDataFetcherService, @@ -718,6 +721,7 @@ describe('Unit | Domain | Use Cases | correct-answer-then-update-assessment', fu let savedAnswer; let solution; let validator; + let candidate; beforeEach(function () { // given @@ -731,6 +735,9 @@ describe('Unit | Domain | Use Cases | correct-answer-then-update-assessment', fu solution = domainBuilder.buildSolution({ id: answer.challengeId, value: correctAnswerValue }); validator = domainBuilder.buildValidator.ofTypeQCU({ solution }); challenge = domainBuilder.buildChallenge({ id: answer.challengeId, validator }); + candidate = domainBuilder.certification.evaluation.buildCandidate({ + accessibilityAdjustmentNeeded: true, + }); completedAnswer = domainBuilder.buildAnswer(answer); completedAnswer.timeSpent = 0; @@ -749,6 +756,9 @@ describe('Unit | Domain | Use Cases | correct-answer-then-update-assessment', fu assessmentRepository.get.resolves(assessment); challengeRepository.get.resolves(challenge); answerRepository.saveWithKnowledgeElements.resolves(savedAnswer); + certificationEvaluationCandidateRepository.findByAssessmentId + .withArgs({ assessmentId: assessment.id }) + .resolves(candidate); }); it('should call the answer repository to save the answer', async function () { @@ -929,65 +939,143 @@ describe('Unit | Domain | Use Cases | correct-answer-then-update-assessment', fu context('when the challenge is focused in certification', function () { let answer; let assessment; + let candidate; - beforeEach(function () { - // Given - answer = domainBuilder.buildAnswer({}); - const nonFocusedChallenge = domainBuilder.buildChallenge({ - id: answer.challengeId, - validator, - focused: true, + context('when the candidate does not need an accessibility adjustment', function () { + beforeEach(function () { + // Given + answer = domainBuilder.buildAnswer({}); + candidate = domainBuilder.certification.evaluation.buildCandidate({ + accessibilityAdjustmentNeeded: false, + }); + const nonFocusedChallenge = domainBuilder.buildChallenge({ + id: answer.challengeId, + validator, + focused: true, + }); + challengeRepository.get.resolves(nonFocusedChallenge); + assessment = domainBuilder.buildAssessment({ + userId, + lastQuestionDate: new Date('2021-03-11T11:00:00Z'), + type: Assessment.types.CERTIFICATION, + }); + assessmentRepository.get.resolves(assessment); + certificationEvaluationCandidateRepository.findByAssessmentId + .withArgs({ assessmentId: assessment.id }) + .resolves(candidate); }); - challengeRepository.get.resolves(nonFocusedChallenge); - assessment = domainBuilder.buildAssessment({ - userId, - lastQuestionDate: new Date('2021-03-11T11:00:00Z'), - type: Assessment.types.CERTIFICATION, + + // Rule disabled to allow dynamic generated tests. See https://github.com/lo1tuma/eslint-plugin-mocha/blob/master/docs/rules/no-setup-in-describe.md#disallow-setup-in-describe-blocks-mochano-setup-in-describe + // eslint-disable-next-line mocha/no-setup-in-describe + [ + { + isFocusedOut: true, + lastQuestionState: 'focusedout', + expected: { result: ANSWER_STATUS_FOCUSEDOUT, isFocusedOut: true }, + }, + { + isFocusedOut: false, + lastQuestionState: 'asked', + expected: { result: ANSWER_STATUS_OK, isFocusedOut: false }, + }, + { + isFocusedOut: false, + lastQuestionState: 'focusedout', + expected: { result: ANSWER_STATUS_FOCUSEDOUT, isFocusedOut: true }, + }, + { + isFocusedOut: true, + lastQuestionState: 'asked', + expected: { result: ANSWER_STATUS_FOCUSEDOUT, isFocusedOut: true }, + }, + ].forEach(({ isFocusedOut, lastQuestionState, expected }) => { + context(`when answer.isFocusedOut=${isFocusedOut} and lastQuestionState=${lastQuestionState}`, function () { + it(`should return result=${expected.result.status} and isFocusedOut=${expected.isFocusedOut}`, async function () { + // Given + answer.isFocusedOut = isFocusedOut; + assessment.lastQuestionState = lastQuestionState; + answerRepository.saveWithKnowledgeElements = (_) => _; + + // When + const correctedAnswer = await correctAnswerThenUpdateAssessment({ + answer: answer, + userId, + locale, + ...dependencies, + }); + + // Then + expect(correctedAnswer).to.deep.contain(expected); + }); + }); }); - assessmentRepository.get.resolves(assessment); }); - // Rule disabled to allow dynamic generated tests. See https://github.com/lo1tuma/eslint-plugin-mocha/blob/master/docs/rules/no-setup-in-describe.md#disallow-setup-in-describe-blocks-mochano-setup-in-describe - // eslint-disable-next-line mocha/no-setup-in-describe - [ - { - isFocusedOut: true, - lastQuestionState: 'focusedout', - expected: { result: ANSWER_STATUS_FOCUSEDOUT, isFocusedOut: true }, - }, - { - isFocusedOut: false, - lastQuestionState: 'asked', - expected: { result: ANSWER_STATUS_OK, isFocusedOut: false }, - }, - { - isFocusedOut: false, - lastQuestionState: 'focusedout', - expected: { result: ANSWER_STATUS_FOCUSEDOUT, isFocusedOut: true }, - }, - { - isFocusedOut: true, - lastQuestionState: 'asked', - expected: { result: ANSWER_STATUS_FOCUSEDOUT, isFocusedOut: true }, - }, - ].forEach(({ isFocusedOut, lastQuestionState, expected }) => { - context(`when answer.isFocusedOut=${isFocusedOut} and lastQuestionState=${lastQuestionState}`, function () { - it(`should return result=${expected.result.status} and isFocusedOut=${expected.isFocusedOut}`, async function () { - // Given - answer.isFocusedOut = isFocusedOut; - assessment.lastQuestionState = lastQuestionState; - answerRepository.saveWithKnowledgeElements = (_) => _; - - // When - const correctedAnswer = await correctAnswerThenUpdateAssessment({ - answer: answer, - userId, - locale, - ...dependencies, - }); + context('when the candidate needs an accessibility adjustment', function () { + beforeEach(function () { + // Given + answer = domainBuilder.buildAnswer({}); + candidate = domainBuilder.certification.evaluation.buildCandidate({ + accessibilityAdjustmentNeeded: true, + }); + const nonFocusedChallenge = domainBuilder.buildChallenge({ + id: answer.challengeId, + validator, + focused: true, + }); + challengeRepository.get.resolves(nonFocusedChallenge); + assessment = domainBuilder.buildAssessment({ + userId, + lastQuestionDate: new Date('2021-03-11T11:00:00Z'), + type: Assessment.types.CERTIFICATION, + }); + assessmentRepository.get.resolves(assessment); + certificationEvaluationCandidateRepository.findByAssessmentId + .withArgs({ assessmentId: assessment.id }) + .resolves(candidate); + }); - // Then - expect(correctedAnswer).to.deep.contain(expected); + // eslint-disable-next-line mocha/no-setup-in-describe + [ + { + isFocusedOut: true, + lastQuestionState: 'focusedout', + expected: { result: ANSWER_STATUS_OK, isFocusedOut: true }, + }, + { + isFocusedOut: false, + lastQuestionState: 'asked', + expected: { result: ANSWER_STATUS_OK, isFocusedOut: false }, + }, + { + isFocusedOut: false, + lastQuestionState: 'focusedout', + expected: { result: ANSWER_STATUS_OK, isFocusedOut: true }, + }, + { + isFocusedOut: true, + lastQuestionState: 'asked', + expected: { result: ANSWER_STATUS_OK, isFocusedOut: true }, + }, + ].forEach(({ isFocusedOut, lastQuestionState, expected }) => { + context(`when answer.isFocusedOut=${isFocusedOut} and lastQuestionState=${lastQuestionState}`, function () { + it(`should return result=${expected.result.status} and isFocusedOut=${expected.isFocusedOut}`, async function () { + // Given + answer.isFocusedOut = isFocusedOut; + assessment.lastQuestionState = lastQuestionState; + answerRepository.saveWithKnowledgeElements = (_) => _; + + // When + const correctedAnswer = await correctAnswerThenUpdateAssessment({ + answer: answer, + userId, + locale, + ...dependencies, + }); + + // Then + expect(correctedAnswer).to.deep.contain(expected); + }); }); }); });