diff --git a/.github/actions/run-playwright-smoke-tests/action.yml b/.github/actions/run-playwright-smoke-tests/action.yml new file mode 100644 index 000000000..f0f8c03ef --- /dev/null +++ b/.github/actions/run-playwright-smoke-tests/action.yml @@ -0,0 +1,36 @@ +name: Run Playwright smoke tests +description: Run smoke tests using Playwright + +inputs: + webapp_url: + required: true + type: string + auth_secret: + required: true + type: string + +runs: + using: composite + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: lts/* + - name: Install dependencies + shell: bash + working-directory: ./tests/Dfe.EarlyYearsQualification.SmokeTests/playwright + run: npm ci + - name: Install Playwright Browsers + shell: bash + working-directory: ./tests/Dfe.EarlyYearsQualification.SmokeTests/playwright + run: npx playwright install --with-deps + - name: Run Playwright tests + shell: bash + working-directory: ./tests/Dfe.EarlyYearsQualification.SmokeTests/playwright + run: WEBAPP_URL=https://${{ inputs.webapp_url }}.azurewebsites.net AUTH_SECRET=${{ inputs.auth_secret }} npx playwright test + - uses: actions/upload-artifact@v4 + if: ${{ !cancelled() }} + with: + name: playwright-report + path: playwright-report/ + retention-days: 30 \ No newline at end of file diff --git a/.github/workflows/code-pr-check.yml b/.github/workflows/code-pr-check.yml index e0656a546..a636f6c2d 100644 --- a/.github/workflows/code-pr-check.yml +++ b/.github/workflows/code-pr-check.yml @@ -81,7 +81,7 @@ jobs: --ServiceAccess:IsPublic="true" & - name: ZAP Scan - uses: zaproxy/action-baseline@v0.13.0 + uses: zaproxy/action-baseline@v0.14.0 with: token: ${{ secrets.GITHUB_TOKEN }} docker_name: 'ghcr.io/zaproxy/zaproxy:stable' diff --git a/.github/workflows/dev-security-check.yml b/.github/workflows/dev-security-check.yml index 79daeff57..d94098817 100644 --- a/.github/workflows/dev-security-check.yml +++ b/.github/workflows/dev-security-check.yml @@ -36,7 +36,7 @@ jobs: # Run full ZAP scan - name: ZAP Scan - uses: zaproxy/action-full-scan@v0.11.0 + uses: zaproxy/action-full-scan@v0.12.0 with: token: ${{ secrets.GITHUB_TOKEN }} docker_name: 'ghcr.io/zaproxy/zaproxy:stable' diff --git a/.github/workflows/development-build-and-deploy.yml b/.github/workflows/development-build-and-deploy.yml index cb435a37f..898571d81 100644 --- a/.github/workflows/development-build-and-deploy.yml +++ b/.github/workflows/development-build-and-deploy.yml @@ -66,10 +66,9 @@ jobs: uses: ./.github/actions/health-check with: webapp_url: ${{ vars.WEBAPP_NAME }} - - # Run smoke test - - name: Run smoke tests - uses: ./.github/actions/run-smoke-tests + + - name: Run Playwright smoke tests + uses: ./.github/actions/run-playwright-smoke-tests with: - webapp_url: ${{ vars.WEBAPP_NAME }} + webapp_url: ${{ vars.WEBAPP_NAME }} auth_secret: ${{ secrets.WEBAPP_E2E_ACCESS_KEY }} \ No newline at end of file diff --git a/.github/workflows/release-build-and-deploy.yml b/.github/workflows/release-build-and-deploy.yml index 769bb7309..bce3138d5 100644 --- a/.github/workflows/release-build-and-deploy.yml +++ b/.github/workflows/release-build-and-deploy.yml @@ -69,8 +69,8 @@ jobs: webapp_url: "${{ vars.WEBAPP_NAME }}-${{ vars.WEBAPP_SLOT_NAME }}" # Run smoke test - - name: Run smoke tests against slot - uses: ./.github/actions/run-smoke-tests + - name: Run Playwright smoke tests against slot + uses: ./.github/actions/run-playwright-smoke-tests with: webapp_url: "${{ vars.WEBAPP_NAME }}-${{ vars.WEBAPP_SLOT_NAME }}" auth_secret: ${{ secrets.WEBAPP_E2E_ACCESS_KEY }} @@ -116,9 +116,9 @@ jobs: with: webapp_url: "${{ vars.WEBAPP_NAME }}-${{ vars.WEBAPP_SLOT_NAME }}" - # Run smoke test - - name: Run smoke tests against slot - uses: ./.github/actions/run-smoke-tests + # Run smoke test + - name: Run Playwright smoke tests against slot + uses: ./.github/actions/run-playwright-smoke-tests with: webapp_url: "${{ vars.WEBAPP_NAME }}-${{ vars.WEBAPP_SLOT_NAME }}" auth_secret: ${{ secrets.WEBAPP_E2E_ACCESS_KEY }} diff --git a/.github/workflows/terraform-pr-check.yml b/.github/workflows/terraform-pr-check.yml index 347b668a4..ca8386291 100644 --- a/.github/workflows/terraform-pr-check.yml +++ b/.github/workflows/terraform-pr-check.yml @@ -2,7 +2,7 @@ name: Terraform PR Check on: pull_request: - branches: ["release/**", "fix/**"] + branches: ["release/**", "fix/**", "feature/**"] paths: - 'terraform/**' - '.github/workflows/terraform-pr-check.yml' diff --git a/.github/workflows/tf-azure-deploy.yml b/.github/workflows/tf-azure-deploy.yml index bf5c36b40..966881a68 100644 --- a/.github/workflows/tf-azure-deploy.yml +++ b/.github/workflows/tf-azure-deploy.yml @@ -51,6 +51,11 @@ jobs: # Checkout the repository to the GitHub Actions runner - name: Checkout uses: actions/checkout@v4 + + # Checks that all Terraform configuration files adhere to a canonical format + # Will fail the build if not + - name: Terraform Format + run: terraform fmt -check # Sets an environment variable to contain the current IP address - name: IP address env @@ -84,11 +89,6 @@ jobs: -backend-config="container_name=${{ secrets.TERRAFORM_STATE_STORAGE_CONTAINER_NAME }}" -backend-config="key=${{ secrets.TERRAFORM_STATE_KEY }}" - # Checks that all Terraform configuration files adhere to a canonical format - # Will fail the build if not - - name: Terraform Format - run: terraform fmt -check - # Generates Terraform input variables - name: Generate Terraform Variables shell: bash diff --git a/.gitignore b/.gitignore index eac0a0c23..171d9295f 100644 --- a/.gitignore +++ b/.gitignore @@ -502,4 +502,7 @@ build/ tests/Dfe.EarlyYearsQualification.E2ETests/cypress/screenshots/ # Sonar -.sonarqube/ \ No newline at end of file +.sonarqube/ + +# k6 browser tests screenshots +screenshots/ diff --git a/adr/0019-license.md b/adr/0019-license.md index c8d151dd1..a9e4c62c0 100644 --- a/adr/0019-license.md +++ b/adr/0019-license.md @@ -1,6 +1,6 @@ # 0019 - Software License -* **Status**: proposed +* **Status**: accepted ## Context and Problem Statement diff --git a/adr/0020-load-test-framework.md b/adr/0020-load-test-framework.md new file mode 100644 index 000000000..bfe58eacf --- /dev/null +++ b/adr/0020-load-test-framework.md @@ -0,0 +1,30 @@ +# 0020 - Load Testing Framework + +* **Status**: accepted + +## Context and Problem Statement + +To ensure confidence in the service's performance under user load. + +## Decision Drivers + +* Ease of maintenance +* Established practice among other DfE service development teams +* Ability to use existing Cypress (or Playwright) tests + +## Considered Options + +* Azure Load Test +* JMeter +* Grafana k6 + +Azure Load Test can be fed `*.jmx` files from JMeter. However, JMeter itself seems a little +old fashioned nowadays, and was not designed with ease of development & maintenance, nor +for cloud native infrastructure. Also, Azure Load Test incurs a cost to run. + +Grafana k6 is in use by other DfE teams, and investigation showed it is flexible +and easy to use. + +## Decision Outcome + +Grafana k6. diff --git a/adr/README.md b/adr/README.md index 534d505d8..b1660d7d4 100644 --- a/adr/README.md +++ b/adr/README.md @@ -30,4 +30,5 @@ General information about architectural decision records is available at - /// Entry ID for the "Qualification Level 7" advice page + /// Entry ID for the "Level 7 qualification post-2014" advice page. /// - public const string QualificationLevel7 = "1bGiyvwsJZPd14dwrVtWCM"; - - /// - /// Entry ID for the "Level 6 qualification pre-2014" advice page. - /// - public const string Level6QualificationPre2014 = "2TT7k2J5KUNmUQ3f8A56tw"; - - /// - /// Entry ID for the "Level 6 qualification post-2014" advice page. - /// - public const string Level6QualificationPost2014 = "5aoy3C3jIEKdMo8fauvyvk"; - - /// - /// Entry ID for the TEMPORARY Privacy Policy page. - /// - public const string TemporaryPrivacyPolicy = "6VCAUoA8ERp0hkeT5JcM72"; + public const string Level7QualificationPost2014 = "4nfocBj9Alwfa15ke8MHBj"; } \ No newline at end of file diff --git a/src/Dfe.EarlyYearsQualification.Content/Dfe.EarlyYearsQualification.Content.csproj b/src/Dfe.EarlyYearsQualification.Content/Dfe.EarlyYearsQualification.Content.csproj index 23c56d4fb..332cce64d 100644 --- a/src/Dfe.EarlyYearsQualification.Content/Dfe.EarlyYearsQualification.Content.csproj +++ b/src/Dfe.EarlyYearsQualification.Content/Dfe.EarlyYearsQualification.Content.csproj @@ -9,7 +9,7 @@ - + diff --git a/src/Dfe.EarlyYearsQualification.Content/Entities/AdditionalRequirementQuestion.cs b/src/Dfe.EarlyYearsQualification.Content/Entities/AdditionalRequirementQuestion.cs index 5fdc6f0f5..cffe94364 100644 --- a/src/Dfe.EarlyYearsQualification.Content/Entities/AdditionalRequirementQuestion.cs +++ b/src/Dfe.EarlyYearsQualification.Content/Entities/AdditionalRequirementQuestion.cs @@ -4,6 +4,8 @@ namespace Dfe.EarlyYearsQualification.Content.Entities; public class AdditionalRequirementQuestion { + public SystemProperties Sys { get; init; } = new(); + public string Question { get; init; } = string.Empty; public string HintText { get; init; } = string.Empty; diff --git a/src/Dfe.EarlyYearsQualification.Content/Entities/CheckAdditionalRequirementsAnswersPage.cs b/src/Dfe.EarlyYearsQualification.Content/Entities/CheckAdditionalRequirementsAnswersPage.cs index 47b93ddc7..78bd5cec1 100644 --- a/src/Dfe.EarlyYearsQualification.Content/Entities/CheckAdditionalRequirementsAnswersPage.cs +++ b/src/Dfe.EarlyYearsQualification.Content/Entities/CheckAdditionalRequirementsAnswersPage.cs @@ -1,5 +1,3 @@ -using Contentful.Core.Models; - namespace Dfe.EarlyYearsQualification.Content.Entities; public class CheckAdditionalRequirementsAnswerPage diff --git a/src/Dfe.EarlyYearsQualification.Content/Entities/ConfirmQualificationPage.cs b/src/Dfe.EarlyYearsQualification.Content/Entities/ConfirmQualificationPage.cs index faea1e0e7..85ac4995a 100644 --- a/src/Dfe.EarlyYearsQualification.Content/Entities/ConfirmQualificationPage.cs +++ b/src/Dfe.EarlyYearsQualification.Content/Entities/ConfirmQualificationPage.cs @@ -18,4 +18,6 @@ public class ConfirmQualificationPage public string ErrorText { get; init; } = string.Empty; public string ButtonText { get; init; } = string.Empty; public NavigationLink? BackButton { get; init; } + public string AnswerDisclaimerText { get; init; } = string.Empty; + public string NoAdditionalRequirementsButtonText { get; init; } = string.Empty; } \ No newline at end of file diff --git a/src/Dfe.EarlyYearsQualification.Content/Entities/DetailsPage.cs b/src/Dfe.EarlyYearsQualification.Content/Entities/DetailsPage.cs index a3e7498fe..bf99d2c51 100644 --- a/src/Dfe.EarlyYearsQualification.Content/Entities/DetailsPage.cs +++ b/src/Dfe.EarlyYearsQualification.Content/Entities/DetailsPage.cs @@ -29,9 +29,9 @@ public class DetailsPage public Document? FurtherInfoText { get; init; } public NavigationLink? BackButton { get; init; } - - public NavigationLink? BackToAdditionalQuestionsLink { get; init; } - + + public NavigationLink? BackToConfirmAnswers { get; init; } + public NavigationLink? BackToLevelSixAdviceBefore2014 { get; init; } public NavigationLink? BackToLevelSixAdvice { get; init; } diff --git a/src/Dfe.EarlyYearsQualification.Content/Entities/Qualification.cs b/src/Dfe.EarlyYearsQualification.Content/Entities/Qualification.cs index 65c9f6b56..974b93ada 100644 --- a/src/Dfe.EarlyYearsQualification.Content/Entities/Qualification.cs +++ b/src/Dfe.EarlyYearsQualification.Content/Entities/Qualification.cs @@ -19,4 +19,5 @@ public class Qualification( public string? AdditionalRequirements { get; init; } public List? AdditionalRequirementQuestions { get; init; } public List? RatioRequirements { get; init; } + public bool IsAutomaticallyApprovedAtLevel6 { get; init; } } \ No newline at end of file diff --git a/src/Dfe.EarlyYearsQualification.Content/Entities/RatioRequirement.cs b/src/Dfe.EarlyYearsQualification.Content/Entities/RatioRequirement.cs index fcfff6843..c6033fe7b 100644 --- a/src/Dfe.EarlyYearsQualification.Content/Entities/RatioRequirement.cs +++ b/src/Dfe.EarlyYearsQualification.Content/Entities/RatioRequirement.cs @@ -63,4 +63,6 @@ public class RatioRequirement public Document? RequirementForLevel7Before2014 { get; set; } public Document? RequirementForLevel7After2014 { get; set; } + + public Document? RequirementForLevel2BetweenSept14AndAug19 { get; set; } } \ No newline at end of file diff --git a/src/Dfe.EarlyYearsQualification.Mock/Content/MockContentfulService.cs b/src/Dfe.EarlyYearsQualification.Mock/Content/MockContentfulService.cs index bb35b8bca..4f929e22f 100644 --- a/src/Dfe.EarlyYearsQualification.Mock/Content/MockContentfulService.cs +++ b/src/Dfe.EarlyYearsQualification.Mock/Content/MockContentfulService.cs @@ -59,21 +59,8 @@ await Task.FromResult(CreateAdvicePage("Qualifications achieved in Northern Irel await Task.FromResult(CreateAdvicePage("Qualification not on the list", body, QualificationsPath)), - AdvicePages.QualificationLevel7 => - await Task.FromResult(CreateAdvicePage("Qualification at Level 7", - body, - WhatLevelIsTheQualificationPath)), - - AdvicePages.Level6QualificationPre2014 => - await Task.FromResult(CreateAdvicePage("Level 6 qualification pre 2014", - body, WhatLevelIsTheQualificationPath)), - - AdvicePages.Level6QualificationPost2014 => - await Task.FromResult(CreateAdvicePage("Level 6 qualification post 2014", - body, WhatLevelIsTheQualificationPath)), - - AdvicePages.TemporaryPrivacyPolicy => - await Task.FromResult(CreateAdvicePage("Temporary privacy policy", + AdvicePages.Level7QualificationPost2014 => + await Task.FromResult(CreateAdvicePage("Level 7 qualification post 2014", body, WhatLevelIsTheQualificationPath)), _ => null }; @@ -142,12 +129,12 @@ await Task.FromResult(CreateAdvicePage("Temporary privacy policy", Href = "/confirm-qualification/eyq-240", OpenInNewTab = false }, - BackToAdditionalQuestionsLink = new NavigationLink + BackToConfirmAnswers = new NavigationLink { DisplayText = "TEST (back to additional questions)", Href = - "/qualifications/check-additional-questions", + "/qualifications/check-additional-questions/$[qualification-id]$/confirm-answers", OpenInNewTab = false }, BackToLevelSixAdvice = new NavigationLink @@ -328,7 +315,9 @@ await Task.FromResult(CreateDropdownPage()), ErrorBannerLink = "Test error banner link", VariousAwardingOrganisationsExplanation = ContentfulContentHelper - .Paragraph("Various awarding organisation explanation text") + .Paragraph("Various awarding organisation explanation text"), + AnswerDisclaimerText = "Answer disclaimer text", + NoAdditionalRequirementsButtonText = "Get result" }); } @@ -346,7 +335,7 @@ await Task.FromResult(CreateDropdownPage()), PreviousQuestionBackButton = new NavigationLink { DisplayText = "Previous", - Href = "/previous", + Href = "/qualifications/check-additional-questions", OpenInNewTab = false }, CtaButtonText = "Get result", diff --git a/src/Dfe.EarlyYearsQualification.Mock/Content/MockQualificationsRepository.cs b/src/Dfe.EarlyYearsQualification.Mock/Content/MockQualificationsRepository.cs index 24dd3ef75..62da27eda 100644 --- a/src/Dfe.EarlyYearsQualification.Mock/Content/MockQualificationsRepository.cs +++ b/src/Dfe.EarlyYearsQualification.Mock/Content/MockQualificationsRepository.cs @@ -1,6 +1,6 @@ +using Contentful.Core.Models; using Dfe.EarlyYearsQualification.Content.Constants; using Dfe.EarlyYearsQualification.Content.Entities; -using Dfe.EarlyYearsQualification.Content.Services; using Dfe.EarlyYearsQualification.Content.Services.Interfaces; using Dfe.EarlyYearsQualification.Mock.Helpers; @@ -13,10 +13,18 @@ public class MockQualificationsRepository : IQualificationsRepository return qualificationId.ToLower() switch { "eyq-250" => await Task.FromResult(CreateQualification("EYQ-250", "BTEC", - AwardingOrganisations.Various)), + AwardingOrganisations.Various, 3)), + "eyq-108" => await Task.FromResult(CreateQtsQualification("EYQ-108", "BTEC", + AwardingOrganisations.Various, 6)), + "eyq-115" => await Task.FromResult(CreateQualification("EYQ-115", "NCFE", + AwardingOrganisations.Various, 3, false)), + "eyq-114" => await Task.FromResult(CreateLevel2FurtherActionRequiredQualification("EYQ-114", "Level 2 Further Action Qualification", AwardingOrganisations.Ncfe, 3)), + + "eyq-241" => await Task.FromResult(CreateQualification("EYQ-241", "BTEC", + AwardingOrganisations.Various, 2)), _ => await Task.FromResult(CreateQualification("EYQ-240", "T Level Technical Qualification in Education and Childcare (Specialism - Early Years Educator)", - AwardingOrganisations.Ncfe)) + AwardingOrganisations.Ncfe, 3)) }; } @@ -153,12 +161,96 @@ private static Qualification CreateQualification(string qualificationId, string } private static Qualification CreateQualification(string qualificationId, string qualificationName, - string awardingOrganisation) + string awardingOrganisation, int qualificationLevel, + bool includeAdditionalRequirementQuestions = true) + { + var additionalRequirementQuestions = includeAdditionalRequirementQuestions + ? new List{ + new() + { + Question = "Test question", + HintText = + "This is the hint text: answer yes for full and relevant", + DetailsHeading = + "This is the details heading", + DetailsContent = + ContentfulContentHelper + .Paragraph("This is the details content"), + Answers = + [ + new Option + { + Label = "Yes", + Value = "yes" + }, + + new Option + { + Label = "No", + Value = "no" + } + ], + ConfirmationStatement = + "This is the confirmation statement 1", + AnswerToBeFullAndRelevant = true + }, + CreateSecondAdditionalRequirementQuestion(false) + } : null; + + return new Qualification(qualificationId, + qualificationName, + awardingOrganisation, + qualificationLevel) + { + FromWhichYear = "2020", + ToWhichYear = "2021", + QualificationNumber = "603/5829/4", + AdditionalRequirements = + "The course must be assessed within the EYFS in an Early Years setting in England. Please note that the name of this qualification changed in February 2023. Qualifications achieved under either name are full and relevant provided that the start date for the qualification aligns with the date of the name change.", + AdditionalRequirementQuestions = additionalRequirementQuestions, + RatioRequirements = + [ + new RatioRequirement + { + RatioRequirementName = + RatioRequirements + .Level2RatioRequirementName, + FullAndRelevantForLevel3After2014 = true, + RequirementForLevel2BetweenSept14AndAug19 = ContentfulContentHelper.Paragraph("Level 2 further action required text") + }, + + new RatioRequirement + { + RatioRequirementName = + RatioRequirements + .Level3RatioRequirementName, + FullAndRelevantForLevel3After2014 = true + }, + + new RatioRequirement + { + RatioRequirementName = RatioRequirements + .Level6RatioRequirementName + }, + + new RatioRequirement + { + RatioRequirementName = + RatioRequirements + .UnqualifiedRatioRequirementName, + FullAndRelevantForLevel3After2014 = true + } + ] + }; + } + + private static Qualification CreateQtsQualification(string qualificationId, string qualificationName, + string awardingOrganisation, int qualificationLevel) { return new Qualification(qualificationId, qualificationName, awardingOrganisation, - 3) + qualificationLevel) { FromWhichYear = "2020", ToWhichYear = "2021", @@ -169,14 +261,16 @@ private static Qualification CreateQualification(string qualificationId, string [ new AdditionalRequirementQuestion { - Question = "Test question", + Sys = new SystemProperties + { Id = AdditionalRequirementQuestions.QtsQuestion }, + Question = "This is the Qts question", HintText = "This is the hint text: answer yes for full and relevant", DetailsHeading = - "This is the details heading", + "Qts question heading", DetailsContent = ContentfulContentHelper - .Paragraph("This is the details content"), + .Paragraph("Qts question content"), Answers = [ new Option @@ -195,36 +289,93 @@ private static Qualification CreateQualification(string qualificationId, string "This is the confirmation statement 1", AnswerToBeFullAndRelevant = true }, + CreateSecondAdditionalRequirementQuestion(true) + ], + RatioRequirements = + [ + new RatioRequirement + { + RatioRequirementName = + RatioRequirements + .Level2RatioRequirementName, + FullAndRelevantForQtsEtcAfter2014 = true, + FullAndRelevantForLevel6After2014 = true + }, - new AdditionalRequirementQuestion + new RatioRequirement { - Question = "Test question 2", - HintText = - "This is the hint text: answer no for full and relevant", - DetailsHeading = - "This is the details heading", - DetailsContent = - ContentfulContentHelper - .Paragraph("This is the details content"), - Answers = - [ - new Option - { - Label = "Yes", - Value = "yes" - }, + RatioRequirementName = + RatioRequirements + .Level3RatioRequirementName, + FullAndRelevantForQtsEtcAfter2014 = true, + FullAndRelevantForLevel6After2014 = true + }, - new Option - { - Label = "No", - Value = "no" - } - ], - ConfirmationStatement = - "This is the confirmation statement 2", - AnswerToBeFullAndRelevant = false + new RatioRequirement + { + RatioRequirementName = RatioRequirements + .Level6RatioRequirementName, + FullAndRelevantForQtsEtcAfter2014 = true, + FullAndRelevantForLevel6After2014 = false + }, + + new RatioRequirement + { + RatioRequirementName = + RatioRequirements + .UnqualifiedRatioRequirementName, + FullAndRelevantForQtsEtcAfter2014 = true, + FullAndRelevantForLevel6After2014 = true + } + ], + IsAutomaticallyApprovedAtLevel6 = false + }; + } + + private static AdditionalRequirementQuestion CreateSecondAdditionalRequirementQuestion(bool answerToBeFullAndRelevant) + { + return new AdditionalRequirementQuestion + { + Question = "Test question 2", + HintText = + "This is the hint text: answer no for full and relevant", + DetailsHeading = + "This is the details heading", + DetailsContent = + ContentfulContentHelper + .Paragraph("This is the details content"), + Answers = + [ + new Option + { + Label = "Yes", + Value = "yes" + }, + + new Option + { + Label = "No", + Value = "no" } ], + ConfirmationStatement = + "This is the confirmation statement 2", + AnswerToBeFullAndRelevant = answerToBeFullAndRelevant + }; + } + + private static Qualification CreateLevel2FurtherActionRequiredQualification( + string qualificationId, string qualificationName, + string awardingOrganisation, int qualificationLevel) + { + return new Qualification(qualificationId, + qualificationName, + awardingOrganisation, + qualificationLevel) + { + FromWhichYear = "2014", + ToWhichYear = "2019", + QualificationNumber = "603/5829/5", RatioRequirements = [ new RatioRequirement @@ -232,7 +383,13 @@ private static Qualification CreateQualification(string qualificationId, string RatioRequirementName = RatioRequirements .Level2RatioRequirementName, - FullAndRelevantForLevel3After2014 = true + FullAndRelevantForLevel2After2014 = false, + FullAndRelevantForLevel3After2014 = false, + FullAndRelevantForLevel4After2014 = false, + FullAndRelevantForLevel5After2014 = false, + FullAndRelevantForLevel6After2014 = false, + FullAndRelevantForLevel7After2014 = false, + RequirementForLevel2BetweenSept14AndAug19 = ContentfulContentHelper.Paragraph("Level 2 further action required text") }, new RatioRequirement @@ -240,13 +397,24 @@ private static Qualification CreateQualification(string qualificationId, string RatioRequirementName = RatioRequirements .Level3RatioRequirementName, - FullAndRelevantForLevel3After2014 = true + FullAndRelevantForLevel2After2014 = false, + FullAndRelevantForLevel3After2014 = false, + FullAndRelevantForLevel4After2014 = false, + FullAndRelevantForLevel5After2014 = false, + FullAndRelevantForLevel6After2014 = false, + FullAndRelevantForLevel7After2014 = false }, new RatioRequirement { RatioRequirementName = RatioRequirements - .Level6RatioRequirementName + .Level6RatioRequirementName, + FullAndRelevantForLevel2After2014 = false, + FullAndRelevantForLevel3After2014 = false, + FullAndRelevantForLevel4After2014 = false, + FullAndRelevantForLevel5After2014 = false, + FullAndRelevantForLevel6After2014 = false, + FullAndRelevantForLevel7After2014 = false }, new RatioRequirement @@ -254,9 +422,15 @@ private static Qualification CreateQualification(string qualificationId, string RatioRequirementName = RatioRequirements .UnqualifiedRatioRequirementName, - FullAndRelevantForLevel3After2014 = true + FullAndRelevantForLevel2After2014 = true, + FullAndRelevantForLevel3After2014 = true, + FullAndRelevantForLevel4After2014 = true, + FullAndRelevantForLevel5After2014 = true, + FullAndRelevantForLevel6After2014 = true, + FullAndRelevantForLevel7After2014 = true } - ] + ], + IsAutomaticallyApprovedAtLevel6 = false }; } } \ No newline at end of file diff --git a/src/Dfe.EarlyYearsQualification.Web/Controllers/AdviceController.cs b/src/Dfe.EarlyYearsQualification.Web/Controllers/AdviceController.cs index ac8802edb..968a44e66 100644 --- a/src/Dfe.EarlyYearsQualification.Web/Controllers/AdviceController.cs +++ b/src/Dfe.EarlyYearsQualification.Web/Controllers/AdviceController.cs @@ -81,32 +81,12 @@ public async Task QualificationNotOnTheList() return await GetView(AdvicePages.QualificationNotOnTheList); } - - [HttpGet("level-6-qualification-pre-2014")] - [RedirectIfDateMissing] - public async Task Level6QualificationPre2014() - { - return await GetView(AdvicePages.Level6QualificationPre2014); - } - - [HttpGet("level-6-qualification-post-2014")] - [RedirectIfDateMissing] - public async Task Level6QualificationPost2014() - { - return await GetView(AdvicePages.Level6QualificationPost2014); - } - - [HttpGet("qualification-level-7")] + + [HttpGet("level-7-qualification-post-2014")] [RedirectIfDateMissing] - public async Task QualificationLevel7() - { - return await GetView(AdvicePages.QualificationLevel7); - } - - [HttpGet("privacy-policy")] - public async Task PrivacyPolicy() + public async Task Level7QualificationPost2014() { - return await GetView(AdvicePages.TemporaryPrivacyPolicy); + return await GetView(AdvicePages.Level7QualificationPost2014); } private async Task GetView(string advicePageId) diff --git a/src/Dfe.EarlyYearsQualification.Web/Controllers/CheckAdditionalRequirementsController.cs b/src/Dfe.EarlyYearsQualification.Web/Controllers/CheckAdditionalRequirementsController.cs index 8eb53267d..e9f646daa 100644 --- a/src/Dfe.EarlyYearsQualification.Web/Controllers/CheckAdditionalRequirementsController.cs +++ b/src/Dfe.EarlyYearsQualification.Web/Controllers/CheckAdditionalRequirementsController.cs @@ -1,4 +1,5 @@ using System.Globalization; +using Dfe.EarlyYearsQualification.Content.Constants; using Dfe.EarlyYearsQualification.Content.Entities; using Dfe.EarlyYearsQualification.Content.RichTextParsing; using Dfe.EarlyYearsQualification.Content.Services.Interfaces; @@ -55,12 +56,30 @@ public async Task Post(string qualificationId, int questionIndex, return RedirectToAction("Index", "Error"); } - if (qualification.AdditionalRequirementQuestions is not null && questionIndex < qualification.AdditionalRequirementQuestions.Count) + if (qualification.AdditionalRequirementQuestions is null) + return RedirectToAction("ConfirmAnswers", "CheckAdditionalRequirements", + new { model.QualificationId }); + + // If the user answer matches the answer to be full and relevant to the Qts question, then go straight to the confirm answers page + var modelAnswerAsBool = model.Answer == "yes"; + var question = qualification.AdditionalRequirementQuestions[questionIndex - 1]; + if (question.Sys.Id == AdditionalRequirementQuestions.QtsQuestion && question.AnswerToBeFullAndRelevant == modelAnswerAsBool) + { + var overrideAdditionalRequirementQuestion = new Dictionary + { + [question.Question] = model.Answer + }; + userJourneyCookieService.SetAdditionalQuestionsAnswers(overrideAdditionalRequirementQuestion); + return RedirectToAction("ConfirmAnswers", "CheckAdditionalRequirements", + new { model.QualificationId }); + } + + if (questionIndex < qualification.AdditionalRequirementQuestions.Count) { return RedirectToAction("Index", "CheckAdditionalRequirements", new { model.QualificationId, questionIndex = questionIndex + 1 }); } - + return RedirectToAction("ConfirmAnswers", "CheckAdditionalRequirements", new { model.QualificationId }); } @@ -98,9 +117,12 @@ public async Task ConfirmAnswers(string qualificationId) var answers = userJourneyCookieService.GetAdditionalQuestionsAnswers(); - if (answers == null || answers.Count != qualification.AdditionalRequirementQuestions!.Count) + if (answers == null || !UserAnswerMatchesQtsQuestionAnswerToBeFullAndRelevant(qualification, answers)) { - return RedirectToAction("Index", "CheckAdditionalRequirements", new { qualificationId, questionIndex = 0 }); + if (answers?.Count != qualification.AdditionalRequirementQuestions!.Count) + { + return RedirectToAction("Index", "CheckAdditionalRequirements", new { qualificationId, questionIndex = 1 }); + } } var model = MapCheckAnswers(content, answers, qualificationId); @@ -221,4 +243,22 @@ private static CheckAdditionalRequirementsAnswerPageModel MapCheckAnswers(CheckA ChangeQuestionHref = $"/qualifications/check-additional-questions/{qualificationId}/" }; } + + private static bool UserAnswerMatchesQtsQuestionAnswerToBeFullAndRelevant(Qualification qualification, + Dictionary additionalRequirementAnswers) + { + if (qualification.AdditionalRequirementQuestions is null + || qualification.AdditionalRequirementQuestions.All(x => x.Sys.Id != AdditionalRequirementQuestions.QtsQuestion)) + { + return false; + } + + var qtsQuestion = + qualification.AdditionalRequirementQuestions.First(x => x.Sys.Id == AdditionalRequirementQuestions + .QtsQuestion); + + var userAnsweredQuestion = additionalRequirementAnswers.First(x => x.Key == qtsQuestion.Question); + var answerAsBool = userAnsweredQuestion.Value == "yes"; + return qtsQuestion.AnswerToBeFullAndRelevant == answerAsBool; + } } \ No newline at end of file diff --git a/src/Dfe.EarlyYearsQualification.Web/Controllers/ConfirmQualificationController.cs b/src/Dfe.EarlyYearsQualification.Web/Controllers/ConfirmQualificationController.cs index 2dcf37574..fe5870f48 100644 --- a/src/Dfe.EarlyYearsQualification.Web/Controllers/ConfirmQualificationController.cs +++ b/src/Dfe.EarlyYearsQualification.Web/Controllers/ConfirmQualificationController.cs @@ -79,8 +79,7 @@ public async Task Confirm(ConfirmQualificationPageModel model) userJourneyCookieService.SetUserSelectedQualificationFromList(YesOrNo.Yes); userJourneyCookieService.ClearAdditionalQuestionsAnswers(); - var hasAdditionalQuestions = qualification.AdditionalRequirementQuestions is not null && - qualification.AdditionalRequirementQuestions.Count > 0; + var hasAdditionalQuestions = qualification is { IsAutomaticallyApprovedAtLevel6: false, AdditionalRequirementQuestions.Count: > 0 }; return model.ConfirmQualificationAnswer switch { diff --git a/src/Dfe.EarlyYearsQualification.Web/Controllers/QualificationDetailsController.cs b/src/Dfe.EarlyYearsQualification.Web/Controllers/QualificationDetailsController.cs index 4083f7372..e254870d3 100644 --- a/src/Dfe.EarlyYearsQualification.Web/Controllers/QualificationDetailsController.cs +++ b/src/Dfe.EarlyYearsQualification.Web/Controllers/QualificationDetailsController.cs @@ -7,6 +7,7 @@ using Dfe.EarlyYearsQualification.Web.Attributes; using Dfe.EarlyYearsQualification.Web.Controllers.Base; using Dfe.EarlyYearsQualification.Web.Mappers; +using Dfe.EarlyYearsQualification.Web.Models; using Dfe.EarlyYearsQualification.Web.Models.Content; using Dfe.EarlyYearsQualification.Web.Services.UserJourneyCookieService; using Microsoft.AspNetCore.Mvc; @@ -89,24 +90,80 @@ public async Task Index(string qualificationId) var model = await MapDetails(qualificationStartedBeforeSeptember2014, qualification, detailsPageContent); - var validateAdditionalRequirementQuestions = ValidateAdditionalQuestions(model); + var validateAdditionalRequirementQuestions = await ValidateAdditionalQuestions(model, qualification); if (!validateAdditionalRequirementQuestions.isValid) { + await QualificationLevel3OrAboveMightBeRelevantAtLevel2(model, qualification); return validateAdditionalRequirementQuestions.actionResult!; } // If all the additional requirement checks pass, then we can go to check each level individually await CheckRatioRequirements(qualificationStartedBeforeSeptember2014, qualification, model); + + await QualificationLevel3OrAboveMightBeRelevantAtLevel2(model, qualification); return View(model); } - private (bool isValid, IActionResult? actionResult) ValidateAdditionalQuestions(QualificationDetailsModel model) + private async Task QualificationLevel3OrAboveMightBeRelevantAtLevel2(QualificationDetailsModel model, Qualification qualification) + { + // Check if the qualification is not full and relevant and was started between Sept 2014 and Aug 2019 and is above a level 2 qualification + if (model.RatioRequirements.IsNotFullAndRelevant && userJourneyCookieService.WasStartedBetweenSept2014AndAug2019() && qualification.QualificationLevel > 2) + { + await QualIsNotLevel2NotApprovedAndStartedBetweenSept2014AndAug2019(model, qualification); + } + } + + private async Task<(bool isValid, IActionResult? actionResult)> ValidateAdditionalQuestions(QualificationDetailsModel model, Qualification qualification) { // If the qualification has no additional requirements then skip all checks and return. if (model.AdditionalRequirementAnswers == null) return (true, null); - + + // If qualification contains the QTS question, check the answers + if (QualificationContainsQtsQuestion(qualification)) + { + var qtsQuestion = + qualification.AdditionalRequirementQuestions!.First(x => x.Sys.Id == AdditionalRequirementQuestions + .QtsQuestion); + + if (UserAnswerMatchesQtsQuestionAnswerToBeFullAndRelevant(qualification, model.AdditionalRequirementAnswers)) + { + // Remove the additional requirements that they didn't answer following the bypass. + model.AdditionalRequirementAnswers.RemoveAll(x => x.Question != qtsQuestion.Question); + return (true, null); + } + + // Check remaining questions + var answersToCheck = new List(); + answersToCheck.AddRange(model.AdditionalRequirementAnswers); + // As L6 / L7 can potentially work at L3/2/unqualified, remove the Qts question and check answers + answersToCheck.RemoveAll(x => x.Question == qtsQuestion.Question); + + // As we know that they didn't answer the Qts question, we need to show the L6 requirements by default. + // Adding it here covers scenarios where they are OK for L2/3/Unqualified and just Unqualified. + model.RatioRequirements.ShowRequirementsForLevel6ByDefault = true; + + if (!AnswersIndicateNotFullAndRelevant(answersToCheck)) return (true, null); + + // Answers indicate not full and relevant + model.RatioRequirements = MarkAsNotFullAndRelevant(model.RatioRequirements); + // Set any content for L6 + var qualificationStartedBefore2014 = userJourneyCookieService.WasStartedBeforeSeptember2014(); + var beforeOrAfter = + qualificationStartedBefore2014 + ? "Before" + : "After"; + var additionalRequirementDetailPropertyToCheck = $"RequirementForLevel{qualification.QualificationLevel}{beforeOrAfter}2014"; + var requirementsForLevel6 = GetRatioProperty(additionalRequirementDetailPropertyToCheck, + RatioRequirements.Level6RatioRequirementName, + qualification); + model.RatioRequirements.RequirementsForLevel6 = await contentParser.ToHtml(requirementsForLevel6); + model.RatioRequirements.ShowRequirementsForLevel6ByDefault = true; + + return (false, View(model)); + } + // If there is a mismatch between the questions answered, then clear the answers and navigate back to the additional requirements check page if (model.AdditionalRequirementAnswers.Count == 0 || model.AdditionalRequirementAnswers.Exists(answer => string.IsNullOrEmpty(answer.Answer))) @@ -141,6 +198,18 @@ answer is or { AnswerToBeFullAndRelevant: false, Answer: "yes" }); } + + /// If the qualification is above a level 2 qualification, is not full and relevant and is started between Sept 2014 and Aug 2019 + /// then it will have special requirements for level 2. + private async Task QualIsNotLevel2NotApprovedAndStartedBetweenSept2014AndAug2019(QualificationDetailsModel model, Qualification qualification) + { + model.RatioRequirements.ApprovedForLevel2 = QualificationApprovalStatus.FurtherActionRequired; + var requirementsForLevel2 = GetRatioProperty("RequirementForLevel2BetweenSept14AndAug19", + RatioRequirements.Level2RatioRequirementName, + qualification); + model.RatioRequirements.RequirementsForLevel2 = await contentParser.ToHtml(requirementsForLevel2); + model.RatioRequirements.ShowRequirementsForLevel2ByDefault = true; + } private async Task CheckRatioRequirements(bool qualificationStartedBeforeSeptember2014, Qualification qualification, @@ -157,12 +226,25 @@ private async Task CheckRatioRequirements(bool qualificationStartedBeforeSeptemb var additionalRequirementDetailPropertyToCheck = $"RequirementForLevel{qualification.QualificationLevel}{beforeOrAfter}2014"; + + if (qualification.IsAutomaticallyApprovedAtLevel6 || + (QualificationContainsQtsQuestion(qualification) + && UserAnswerMatchesQtsQuestionAnswerToBeFullAndRelevant(qualification, model.AdditionalRequirementAnswers))) + { + // Check user against QTS criteria and swap to Qts Criteria if matches + fullAndRelevantPropertyToCheck = $"FullAndRelevantForQtsEtc{beforeOrAfter}2014"; + additionalRequirementDetailPropertyToCheck = $"RequirementForQtsEtc{beforeOrAfter}2014"; + } const string additionalRequirementHeading = "RequirementHeading"; - model.RatioRequirements.ApprovedForLevel2 = - GetRatioProperty(fullAndRelevantPropertyToCheck, RatioRequirements.Level2RatioRequirementName, - qualification); + var approvedForLevel2 = GetRatioProperty(fullAndRelevantPropertyToCheck, RatioRequirements.Level2RatioRequirementName, + qualification); + + model.RatioRequirements.ApprovedForLevel2 = approvedForLevel2 + ? QualificationApprovalStatus.Approved + : QualificationApprovalStatus.NotApproved; + var requirementsForLevel2 = GetRatioProperty(additionalRequirementDetailPropertyToCheck, RatioRequirements.Level2RatioRequirementName, @@ -173,9 +255,13 @@ private async Task CheckRatioRequirements(bool qualificationStartedBeforeSeptemb GetRatioProperty(additionalRequirementHeading, RatioRequirements.Level2RatioRequirementName, qualification); - model.RatioRequirements.ApprovedForLevel3 = - GetRatioProperty(fullAndRelevantPropertyToCheck, RatioRequirements.Level3RatioRequirementName, - qualification); + var approvedForLevel3 = GetRatioProperty(fullAndRelevantPropertyToCheck, RatioRequirements.Level3RatioRequirementName, + qualification); + + model.RatioRequirements.ApprovedForLevel3 = approvedForLevel3 + ? QualificationApprovalStatus.Approved + : QualificationApprovalStatus.NotApproved; + var requirementsForLevel3 = GetRatioProperty(additionalRequirementDetailPropertyToCheck, RatioRequirements.Level3RatioRequirementName, @@ -185,10 +271,13 @@ private async Task CheckRatioRequirements(bool qualificationStartedBeforeSeptemb model.RatioRequirements.RequirementsHeadingForLevel3 = GetRatioProperty(additionalRequirementHeading, RatioRequirements.Level3RatioRequirementName, qualification); + + var approvedForLevel6 = GetRatioProperty(fullAndRelevantPropertyToCheck, RatioRequirements.Level6RatioRequirementName, + qualification); - model.RatioRequirements.ApprovedForLevel6 = - GetRatioProperty(fullAndRelevantPropertyToCheck, RatioRequirements.Level6RatioRequirementName, - qualification); + model.RatioRequirements.ApprovedForLevel6 = approvedForLevel6 + ? QualificationApprovalStatus.Approved + : QualificationApprovalStatus.NotApproved; var requirementsForLevel6 = GetRatioProperty(additionalRequirementDetailPropertyToCheck, RatioRequirements.Level6RatioRequirementName, @@ -199,9 +288,12 @@ private async Task CheckRatioRequirements(bool qualificationStartedBeforeSeptemb GetRatioProperty(additionalRequirementHeading, RatioRequirements.Level6RatioRequirementName, qualification); - model.RatioRequirements.ApprovedForUnqualified = - GetRatioProperty(fullAndRelevantPropertyToCheck, RatioRequirements.UnqualifiedRatioRequirementName, - qualification); + var approvedForUnqualified = GetRatioProperty(fullAndRelevantPropertyToCheck, RatioRequirements.UnqualifiedRatioRequirementName, qualification); + + model.RatioRequirements.ApprovedForUnqualified = approvedForUnqualified + ? QualificationApprovalStatus.Approved + : QualificationApprovalStatus.NotApproved; + var requirementsForUnqualified = GetRatioProperty(additionalRequirementDetailPropertyToCheck, RatioRequirements.UnqualifiedRatioRequirementName, @@ -209,8 +301,7 @@ private async Task CheckRatioRequirements(bool qualificationStartedBeforeSeptemb model.RatioRequirements.RequirementsForUnqualified = await contentParser.ToHtml(requirementsForUnqualified); model.RatioRequirements.RequirementsHeadingForUnqualified = - GetRatioProperty(additionalRequirementHeading, RatioRequirements.UnqualifiedRatioRequirementName, - qualification); + GetRatioProperty(additionalRequirementHeading, RatioRequirements.UnqualifiedRatioRequirementName, qualification); } private T GetRatioProperty(string propertyToCheck, string ratioName, Qualification qualification) @@ -232,14 +323,39 @@ private T GetRatioProperty(string propertyToCheck, string ratioName, Qualific private static RatioRequirementModel MarkAsNotFullAndRelevant(RatioRequirementModel model) { - model.ApprovedForLevel2 = false; - model.ApprovedForLevel3 = false; - model.ApprovedForLevel6 = false; - model.ApprovedForUnqualified = true; + model.ApprovedForLevel2 = QualificationApprovalStatus.NotApproved; + model.ApprovedForLevel3 = QualificationApprovalStatus.NotApproved; + model.ApprovedForLevel6 = QualificationApprovalStatus.NotApproved; + model.ApprovedForUnqualified = QualificationApprovalStatus.Approved; return model; } + + private static bool QualificationContainsQtsQuestion(Qualification qualification) + { + return qualification.AdditionalRequirementQuestions != null + && qualification.AdditionalRequirementQuestions.Any(x => x.Sys.Id == AdditionalRequirementQuestions + .QtsQuestion); + } + private static bool UserAnswerMatchesQtsQuestionAnswerToBeFullAndRelevant(Qualification qualification, + List? + additionalRequirementAnswerModels) + { + if (additionalRequirementAnswerModels is null) + { + return false; + } + + var qtsQuestion = + qualification.AdditionalRequirementQuestions!.First(x => x.Sys.Id == AdditionalRequirementQuestions + .QtsQuestion); + + var userAnsweredQuestion = additionalRequirementAnswerModels.First(x => x.Question == qtsQuestion.Question); + var answerAsBool = userAnsweredQuestion.Answer == "yes"; + return qtsQuestion.AnswerToBeFullAndRelevant == answerAsBool; + } + private async Task> GetFilteredQualifications() { var level = userJourneyCookieService.GetLevelOfQualification(); @@ -396,17 +512,15 @@ private async Task MapDetails( private static NavigationLink? ContentBackButtonLinkForAdditionalQuestions( DetailsPage content, string qualificationId) { - var link = content.BackToAdditionalQuestionsLink; + const string placeholder = "$[qualification-id]$"; + var link = content.BackToConfirmAnswers; if (link == null) { return content.BackButton; } - if (!link.Href.EndsWith($"/{qualificationId}/1", StringComparison.OrdinalIgnoreCase)) - { - link.Href = $"{link.Href}/{qualificationId}/1"; - } + link.Href = link.Href.Replace(placeholder, qualificationId); return link; } diff --git a/src/Dfe.EarlyYearsQualification.Web/Controllers/QuestionsController.cs b/src/Dfe.EarlyYearsQualification.Web/Controllers/QuestionsController.cs index c297456d9..95c47e62c 100644 --- a/src/Dfe.EarlyYearsQualification.Web/Controllers/QuestionsController.cs +++ b/src/Dfe.EarlyYearsQualification.Web/Controllers/QuestionsController.cs @@ -161,12 +161,8 @@ public async Task WhatLevelIsTheQualification(RadioQuestionModel { "2" when userJourneyCookieService.WasStartedBetweenSeptember2014AndAugust2019() => RedirectToAction("QualificationsStartedBetweenSept2014AndAug2019", "Advice"), - "6" => - RedirectToAction(userJourneyCookieService.WasStartedBeforeSeptember2014() - ? "Level6QualificationPre2014" - : "Level6QualificationPost2014", - "Advice"), - "7" => RedirectToAction(nameof(AdviceController.QualificationLevel7), "Advice"), + "7" when userJourneyCookieService.WasStartedOnOrAfterSeptember2014() => + RedirectToAction(nameof(AdviceController.Level7QualificationPost2014), "Advice"), _ => RedirectToAction(nameof(this.WhatIsTheAwardingOrganisation)) }; } diff --git a/src/Dfe.EarlyYearsQualification.Web/Dfe.EarlyYearsQualification.Web.csproj b/src/Dfe.EarlyYearsQualification.Web/Dfe.EarlyYearsQualification.Web.csproj index d356e233c..6dfa69e21 100644 --- a/src/Dfe.EarlyYearsQualification.Web/Dfe.EarlyYearsQualification.Web.csproj +++ b/src/Dfe.EarlyYearsQualification.Web/Dfe.EarlyYearsQualification.Web.csproj @@ -17,7 +17,7 @@ - + diff --git a/src/Dfe.EarlyYearsQualification.Web/Mappers/ConfirmQualificationPageMapper.cs b/src/Dfe.EarlyYearsQualification.Web/Mappers/ConfirmQualificationPageMapper.cs index 1585b04ed..c0fadb0e4 100644 --- a/src/Dfe.EarlyYearsQualification.Web/Mappers/ConfirmQualificationPageMapper.cs +++ b/src/Dfe.EarlyYearsQualification.Web/Mappers/ConfirmQualificationPageMapper.cs @@ -11,6 +11,8 @@ public static ConfirmQualificationPageModel Map(ConfirmQualificationPage content string postHeadingContentHtml, string variousAwardingOrganisationsExplanationHtml) { + var hasAnyAdditionalRequirementQuestions = qualification.AdditionalRequirementQuestions is { Count: > 0 }; + return new ConfirmQualificationPageModel { Heading = content.Heading, @@ -23,7 +25,7 @@ public static ConfirmQualificationPageModel Map(ConfirmQualificationPage content AwardingOrganisationLabel = content.AwardingOrganisationLabel, ErrorBannerHeading = content.ErrorBannerHeading, ErrorBannerLink = content.ErrorBannerLink, - ButtonText = content.ButtonText, + ButtonText = hasAnyAdditionalRequirementQuestions ? content.ButtonText : content.NoAdditionalRequirementsButtonText, HasErrors = false, QualificationName = qualification.QualificationName, QualificationLevel = qualification.QualificationLevel.ToString(), @@ -33,7 +35,9 @@ public static ConfirmQualificationPageModel Map(ConfirmQualificationPage content BackButton = NavigationLinkMapper.Map(content.BackButton), PostHeadingContent = postHeadingContentHtml, VariousAwardingOrganisationsExplanation = - variousAwardingOrganisationsExplanationHtml + variousAwardingOrganisationsExplanationHtml, + ShowAnswerDisclaimerText = !hasAnyAdditionalRequirementQuestions, + AnswerDisclaimerText = content.AnswerDisclaimerText }; } } \ No newline at end of file diff --git a/src/Dfe.EarlyYearsQualification.Web/Models/AdditionalInformationModel.cs b/src/Dfe.EarlyYearsQualification.Web/Models/AdditionalInformationModel.cs index 0926bc111..184b48593 100644 --- a/src/Dfe.EarlyYearsQualification.Web/Models/AdditionalInformationModel.cs +++ b/src/Dfe.EarlyYearsQualification.Web/Models/AdditionalInformationModel.cs @@ -5,4 +5,6 @@ public class AdditionalInformationModel public string AdditionalInformationHeader { get; init; } = string.Empty; public string AdditionalInformationBody { get; init; } = string.Empty; + + public bool ShowAdditionalInformationBodyByDefault { get; init; } } \ No newline at end of file diff --git a/src/Dfe.EarlyYearsQualification.Web/Models/Content/ConfirmQualificationPageModel.cs b/src/Dfe.EarlyYearsQualification.Web/Models/Content/ConfirmQualificationPageModel.cs index 315036cc0..1e8054b76 100644 --- a/src/Dfe.EarlyYearsQualification.Web/Models/Content/ConfirmQualificationPageModel.cs +++ b/src/Dfe.EarlyYearsQualification.Web/Models/Content/ConfirmQualificationPageModel.cs @@ -32,4 +32,6 @@ public class ConfirmQualificationPageModel public NavigationLinkModel? BackButton { get; init; } public string PostHeadingContent { get; init; } = string.Empty; public string VariousAwardingOrganisationsExplanation { get; init; } = string.Empty; + public bool ShowAnswerDisclaimerText { get; init; } + public string AnswerDisclaimerText { get; init; } = string.Empty; } \ No newline at end of file diff --git a/src/Dfe.EarlyYearsQualification.Web/Models/Content/RatioRequirementModel.cs b/src/Dfe.EarlyYearsQualification.Web/Models/Content/RatioRequirementModel.cs index 1d74f283a..ee8cbd436 100644 --- a/src/Dfe.EarlyYearsQualification.Web/Models/Content/RatioRequirementModel.cs +++ b/src/Dfe.EarlyYearsQualification.Web/Models/Content/RatioRequirementModel.cs @@ -2,18 +2,24 @@ namespace Dfe.EarlyYearsQualification.Web.Models.Content; public class RatioRequirementModel { - public bool ApprovedForLevel2 { get; set; } + public QualificationApprovalStatus ApprovedForLevel2 { get; set; } - public bool ApprovedForLevel3 { get; set; } + public QualificationApprovalStatus ApprovedForLevel3 { get; set; } - public bool ApprovedForLevel6 { get; set; } + public QualificationApprovalStatus ApprovedForLevel6 { get; set; } - public bool ApprovedForUnqualified { get; set; } + public QualificationApprovalStatus ApprovedForUnqualified { get; set; } public bool IsNotFullAndRelevant { - get { return ApprovedForUnqualified && !ApprovedForLevel2 & !ApprovedForLevel3 && !ApprovedForLevel6; } + get + { + return ApprovedForLevel2 != QualificationApprovalStatus.Approved + && ApprovedForLevel3 != QualificationApprovalStatus.Approved + && ApprovedForLevel6 != QualificationApprovalStatus.Approved; + } } + public string RequirementsHeadingForLevel2 { get; set; } = string.Empty; @@ -27,6 +33,10 @@ public bool IsNotFullAndRelevant public string RequirementsForLevel6 { get; set; } = string.Empty; + public bool ShowRequirementsForLevel6ByDefault { get; set; } + + public bool ShowRequirementsForLevel2ByDefault { get; set; } + public string RequirementsHeadingForUnqualified { get; set; } = string.Empty; public string RequirementsForUnqualified { get; set; } = string.Empty; diff --git a/src/Dfe.EarlyYearsQualification.Web/Models/QualificationApprovalStatus.cs b/src/Dfe.EarlyYearsQualification.Web/Models/QualificationApprovalStatus.cs new file mode 100644 index 000000000..3ecaf06a0 --- /dev/null +++ b/src/Dfe.EarlyYearsQualification.Web/Models/QualificationApprovalStatus.cs @@ -0,0 +1,8 @@ +namespace Dfe.EarlyYearsQualification.Web.Models; + +public enum QualificationApprovalStatus +{ + Approved, + NotApproved, + FurtherActionRequired +} \ No newline at end of file diff --git a/src/Dfe.EarlyYearsQualification.Web/Models/RatioRowModel.cs b/src/Dfe.EarlyYearsQualification.Web/Models/RatioRowModel.cs index 9b4c6c5aa..c57c14b14 100644 --- a/src/Dfe.EarlyYearsQualification.Web/Models/RatioRowModel.cs +++ b/src/Dfe.EarlyYearsQualification.Web/Models/RatioRowModel.cs @@ -4,7 +4,15 @@ public class RatioRowModel { public string LevelText { get; init; } = string.Empty; - public bool IsApproved { get; init; } + public string RatioId + { + get + { + return string.Concat(LevelText.Where(c => !char.IsWhiteSpace(c))); + } + } + + public QualificationApprovalStatus ApprovalStatus { get; init; } public AdditionalInformationModel AdditionalInformation { get; init; } = new(); } \ No newline at end of file diff --git a/src/Dfe.EarlyYearsQualification.Web/Program.cs b/src/Dfe.EarlyYearsQualification.Web/Program.cs index dcfced46f..1b0980643 100644 --- a/src/Dfe.EarlyYearsQualification.Web/Program.cs +++ b/src/Dfe.EarlyYearsQualification.Web/Program.cs @@ -14,6 +14,7 @@ using Dfe.EarlyYearsQualification.Web.Services.DatesAndTimes; using Dfe.EarlyYearsQualification.Web.Services.UserJourneyCookieService; using GovUk.Frontend.AspNetCore; +using Microsoft.ApplicationInsights.AspNetCore.Extensions; using Microsoft.AspNetCore.DataProtection; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Infrastructure; @@ -22,7 +23,13 @@ using RobotsTxt; var builder = WebApplication.CreateBuilder(args); -builder.Services.AddApplicationInsightsTelemetry(); + +var applicationInsightsServiceOptions = new ApplicationInsightsServiceOptions + { + EnableAdaptiveSampling = false + }; + +builder.Services.AddApplicationInsightsTelemetry(applicationInsightsServiceOptions); builder.WebHost.ConfigureKestrel(serverOptions => { serverOptions.AddServerHeader = false; }); var useMockContentful = builder.Configuration.GetValue("UseMockContentful"); diff --git a/src/Dfe.EarlyYearsQualification.Web/Services/UserJourneyCookieService/IUserJourneyCookieService.cs b/src/Dfe.EarlyYearsQualification.Web/Services/UserJourneyCookieService/IUserJourneyCookieService.cs index 032954a12..e1c79b78c 100644 --- a/src/Dfe.EarlyYearsQualification.Web/Services/UserJourneyCookieService/IUserJourneyCookieService.cs +++ b/src/Dfe.EarlyYearsQualification.Web/Services/UserJourneyCookieService/IUserJourneyCookieService.cs @@ -15,6 +15,7 @@ public interface IUserJourneyCookieService string? GetWhereWasQualificationAwarded(); (int? startMonth, int? startYear) GetWhenWasQualificationStarted(); bool WasStartedBeforeSeptember2014(); + bool WasStartedOnOrAfterSeptember2014(); bool WasStartedBetweenSeptember2014AndAugust2019(); int? GetLevelOfQualification(); string? GetAwardingOrganisation(); @@ -23,4 +24,5 @@ public interface IUserJourneyCookieService Dictionary? GetAdditionalQuestionsAnswers(); bool UserHasAnsweredAdditionalQuestions(); YesOrNo GetQualificationWasSelectedFromList(); + bool WasStartedBetweenSept2014AndAug2019(); } \ No newline at end of file diff --git a/src/Dfe.EarlyYearsQualification.Web/Services/UserJourneyCookieService/UserJourneyCookieService.cs b/src/Dfe.EarlyYearsQualification.Web/Services/UserJourneyCookieService/UserJourneyCookieService.cs index b2c0a40f3..f5da5b643 100644 --- a/src/Dfe.EarlyYearsQualification.Web/Services/UserJourneyCookieService/UserJourneyCookieService.cs +++ b/src/Dfe.EarlyYearsQualification.Web/Services/UserJourneyCookieService/UserJourneyCookieService.cs @@ -195,6 +195,34 @@ public bool WasStartedBeforeSeptember2014() var date = new DateOnly(startDateYear.Value, startDateMonth.Value, 1); return date < new DateOnly(2014, 9, 1); } + + public bool WasStartedOnOrAfterSeptember2014() + { + var (startDateMonth, startDateYear) = GetWhenWasQualificationStarted(); + + if (startDateMonth is null || startDateYear is null) + { + throw new + InvalidOperationException("Unable to determine whether qualification was started on or after 09-2014"); + } + + var date = new DateOnly(startDateYear.Value, startDateMonth.Value, 1); + return date >= new DateOnly(2014, 9, 1); + } + + public bool WasStartedBetweenSept2014AndAug2019() + { + var (startDateMonth, startDateYear) = GetWhenWasQualificationStarted(); + + if (startDateMonth is null || startDateYear is null) + { + throw new + InvalidOperationException("Unable to determine whether qualification was started on or after 09-2014"); + } + + var date = new DateOnly(startDateYear.Value, startDateMonth.Value, 1); + return date >= new DateOnly(2014, 9, 1) && date <= new DateOnly(2019, 8,31); + } public int? GetLevelOfQualification() { diff --git a/src/Dfe.EarlyYearsQualification.Web/Views/CheckAdditionalRequirements/ConfirmAnswers.cshtml b/src/Dfe.EarlyYearsQualification.Web/Views/CheckAdditionalRequirements/ConfirmAnswers.cshtml index dd755c396..9d6277114 100644 --- a/src/Dfe.EarlyYearsQualification.Web/Views/CheckAdditionalRequirements/ConfirmAnswers.cshtml +++ b/src/Dfe.EarlyYearsQualification.Web/Views/CheckAdditionalRequirements/ConfirmAnswers.cshtml @@ -34,13 +34,11 @@ } -
- - - Warning - @Model.AnswerDisclaimerText - -
+ + @{ + await Html.RenderPartialAsync("Partials/WarningText", Model.AnswerDisclaimerText); + } + @Model.ButtonText diff --git a/src/Dfe.EarlyYearsQualification.Web/Views/ConfirmQualification/index.cshtml b/src/Dfe.EarlyYearsQualification.Web/Views/ConfirmQualification/Index.cshtml similarity index 93% rename from src/Dfe.EarlyYearsQualification.Web/Views/ConfirmQualification/index.cshtml rename to src/Dfe.EarlyYearsQualification.Web/Views/ConfirmQualification/Index.cshtml index 055c90b82..c0e6fc6e5 100644 --- a/src/Dfe.EarlyYearsQualification.Web/Views/ConfirmQualification/index.cshtml +++ b/src/Dfe.EarlyYearsQualification.Web/Views/ConfirmQualification/Index.cshtml @@ -98,6 +98,12 @@ } + + @if(Model.ShowAnswerDisclaimerText) + { + await Html.RenderPartialAsync("Partials/WarningText", Model.AnswerDisclaimerText); + } +

- @Model.Content.CheckAnotherQualificationLink?.DisplayText + @Model.Content.CheckAnotherQualificationLink?.DisplayText

@if (Model.Content.FeedbackBanner is not null) diff --git a/src/Dfe.EarlyYearsQualification.Web/Views/Shared/Partials/AdditionalInformation.cshtml b/src/Dfe.EarlyYearsQualification.Web/Views/Shared/Partials/AdditionalInformation.cshtml index ce0b87649..4b5aaaca7 100644 --- a/src/Dfe.EarlyYearsQualification.Web/Views/Shared/Partials/AdditionalInformation.cshtml +++ b/src/Dfe.EarlyYearsQualification.Web/Views/Shared/Partials/AdditionalInformation.cshtml @@ -1,12 +1,21 @@ @model Dfe.EarlyYearsQualification.Web.Models.AdditionalInformationModel -
- - - @Model.AdditionalInformationHeader - - -
+@if (Model.ShowAdditionalInformationBodyByDefault) +{ +
@Html.Raw(Model.AdditionalInformationBody)
-
\ No newline at end of file +} +else +{ +
+ + + @Model.AdditionalInformationHeader + + +
+ @Html.Raw(Model.AdditionalInformationBody) +
+
+} diff --git a/src/Dfe.EarlyYearsQualification.Web/Views/Shared/Partials/RatioRow.cshtml b/src/Dfe.EarlyYearsQualification.Web/Views/Shared/Partials/RatioRow.cshtml index 8fe89bbab..692f2b556 100644 --- a/src/Dfe.EarlyYearsQualification.Web/Views/Shared/Partials/RatioRow.cshtml +++ b/src/Dfe.EarlyYearsQualification.Web/Views/Shared/Partials/RatioRow.cshtml @@ -7,27 +7,34 @@
-
+
@{ - if (Model.IsApproved) - { - - Approved - - } - else + switch (Model.ApprovalStatus) { - - Not Approved - + case QualificationApprovalStatus.Approved: + + Approved + + break; + case QualificationApprovalStatus.NotApproved: + + Not approved + + break; + case QualificationApprovalStatus.FurtherActionRequired: + + Further action required + + break; } }
-@if (!string.IsNullOrEmpty(Model.AdditionalInformation.AdditionalInformationBody) && !string.IsNullOrEmpty(Model.AdditionalInformation.AdditionalInformationHeader)) +@if ((!string.IsNullOrEmpty(Model.AdditionalInformation.AdditionalInformationBody) && !string.IsNullOrEmpty(Model.AdditionalInformation.AdditionalInformationHeader)) + || Model.AdditionalInformation.ShowAdditionalInformationBodyByDefault) { -
+
@{ await Html.RenderPartialAsync("Partials/AdditionalInformation", Model.AdditionalInformation); }
diff --git a/src/Dfe.EarlyYearsQualification.Web/Views/Shared/Partials/WarningText.cshtml b/src/Dfe.EarlyYearsQualification.Web/Views/Shared/Partials/WarningText.cshtml new file mode 100644 index 000000000..8369a8e29 --- /dev/null +++ b/src/Dfe.EarlyYearsQualification.Web/Views/Shared/Partials/WarningText.cshtml @@ -0,0 +1,9 @@ +@model string + +
+ + + Warning + @Model + +
\ No newline at end of file diff --git a/src/Dfe.EarlyYearsQualification.Web/wwwroot/css/site.css b/src/Dfe.EarlyYearsQualification.Web/wwwroot/css/site.css index 6a0f0847f..943d33bec 100644 --- a/src/Dfe.EarlyYearsQualification.Web/wwwroot/css/site.css +++ b/src/Dfe.EarlyYearsQualification.Web/wwwroot/css/site.css @@ -153,4 +153,8 @@ body { .feedback-banner-container .feedback-banner-body > p { font-size: 19px !important; margin-bottom: 0; +} + +.govuk-tag { + max-width: initial !important; } \ No newline at end of file diff --git a/src/Dfe.EarlyYearsQualification.Web/wwwroot/js/gtm/form-handler.js b/src/Dfe.EarlyYearsQualification.Web/wwwroot/js/gtm/form-handler.js index 8bc4f09f1..871ee565f 100644 --- a/src/Dfe.EarlyYearsQualification.Web/wwwroot/js/gtm/form-handler.js +++ b/src/Dfe.EarlyYearsQualification.Web/wwwroot/js/gtm/form-handler.js @@ -21,15 +21,25 @@ $("#date-question-form").on("submit", function(){ }); $("#dropdown-question-form").on("submit", function(){ - let question = $("#question").text(); - let selectedAO = $("#awarding-organisation-select :selected").val(); - let isNotOnTheListChecked = $("#awarding-organisation-not-in-list").is(":checked"); - window.dataLayer.push({ - 'event': 'dropdownQuestionFormSubmission', - 'question': question, - 'selectedAwardingOrganisation': selectedAO, - 'isNotOnTheListChecked': isNotOnTheListChecked - }); + const question = $("#question").text(); + const selectedAO = $("#awarding-organisation-select :selected").val(); + const isNotOnTheListChecked = $("#awarding-organisation-not-in-list").is(":checked"); + const eventName = 'dropdownQuestionFormSubmission'; + + const payload = isNotOnTheListChecked ? + { + 'event': eventName, + 'question': question, + 'isNotOnTheListChecked': isNotOnTheListChecked + } + : + { + 'event': eventName, + 'question': question, + 'selectedAwardingOrganisation': selectedAO + }; + + window.dataLayer.push(payload); }); $("#confirm-qualification").on("submit", function(){ diff --git a/terraform/README.md b/terraform/README.md index 663cf6428..07993db0f 100644 --- a/terraform/README.md +++ b/terraform/README.md @@ -23,6 +23,7 @@ This module provisions a new Azure Resource Group that assembles together the in | Name | Source | Version | |------|--------|---------| +| [alerts](#module\_alerts) | ./modules/azure-alerts | n/a | | [network](#module\_network) | ./modules/azure-network | n/a | | [storage](#module\_storage) | ./modules/azure-storage | n/a | | [webapp](#module\_webapp) | ./modules/azure-web | n/a | @@ -38,7 +39,7 @@ This module provisions a new Azure Resource Group that assembles together the in | Name | Description | Type | Default | Required | |------|-------------|------|---------|:--------:| | [admin\_email\_address](#input\_admin\_email\_address) | Email Address of the Admin | `string` | n/a | yes | -| [asp\_sku](#input\_asp\_sku) | SKU name for the App Service Plan | `string` | `"S1"` | no | +| [asp\_sku](#input\_asp\_sku) | SKU name for the App Service Plan | `string` | n/a | yes | | [azure\_region](#input\_azure\_region) | Name of the Azure region to deploy resources | `string` | `"westeurope"` | no | | [clarity\_tag](#input\_clarity\_tag) | The Microsoft Clarity tag | `string` | `""` | no | | [contentful\_delivery\_api\_key](#input\_contentful\_delivery\_api\_key) | Contentful delivery API key | `string` | n/a | yes | @@ -65,6 +66,7 @@ This module provisions a new Azure Resource Group that assembles together the in | [webapp\_e2e\_access\_key](#input\_webapp\_e2e\_access\_key) | Web app access key for automated end-to-end tests | `string` | n/a | yes | | [webapp\_name](#input\_webapp\_name) | Name for the Web Application | `string` | n/a | yes | | [webapp\_slot\_name](#input\_webapp\_slot\_name) | Name for the slot for the Web Application | `string` | `"green"` | no | +| [webapp\_storage\_account\_name](#input\_webapp\_storage\_account\_name) | Storage Account name | `string` | n/a | yes | | [webapp\_team\_access\_key](#input\_webapp\_team\_access\_key) | Web app access key for the service team | `string` | n/a | yes | | [webapp\_worker\_count](#input\_webapp\_worker\_count) | Number of Workers for the App Service Plan | `string` | `1` | no | diff --git a/terraform/main.tf b/terraform/main.tf index ff323f33e..61447f755 100644 --- a/terraform/main.tf +++ b/terraform/main.tf @@ -114,3 +114,14 @@ module "webapp" { tags = local.common_tags depends_on = [module.network] } + +# Create service alerts +module "alerts" { + source = "./modules/azure-alerts" + + resource_group = azurerm_resource_group.rg.name + app_service_plan_id = module.webapp.app_service_plan_id + app_service_webapp_id = module.webapp.app_service_webapp_id + tags = local.common_tags + depends_on = [module.webapp] +} \ No newline at end of file diff --git a/terraform/modules/azure-alerts/README.md b/terraform/modules/azure-alerts/README.md new file mode 100644 index 000000000..9d412f29d --- /dev/null +++ b/terraform/modules/azure-alerts/README.md @@ -0,0 +1,40 @@ + +## Requirements + +No requirements. + +## Providers + +| Name | Version | +|------|---------| +| [azurerm](#provider\_azurerm) | n/a | + +## Modules + +No modules. + +## Resources + +| Name | Type | +|------|------| +| [azurerm_monitor_action_group.dev_team](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/monitor_action_group) | resource | +| [azurerm_monitor_activity_log_alert.instance_count_decrease](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/monitor_activity_log_alert) | resource | +| [azurerm_monitor_activity_log_alert.instance_count_increase](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/monitor_activity_log_alert) | resource | +| [azurerm_monitor_metric_alert.cpu_alert](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/monitor_metric_alert) | resource | +| [azurerm_monitor_metric_alert.http4xx_errors](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/monitor_metric_alert) | resource | +| [azurerm_monitor_metric_alert.http5xx_errors](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/monitor_metric_alert) | resource | +| [azurerm_monitor_metric_alert.memory_alert](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/monitor_metric_alert) | resource | + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| [app\_service\_plan\_id](#input\_app\_service\_plan\_id) | Id of the App Service Plan | `string` | n/a | yes | +| [app\_service\_webapp\_id](#input\_app\_service\_webapp\_id) | Id of the Web Application | `string` | n/a | yes | +| [resource\_group](#input\_resource\_group) | Name of the Azure Resource Group to deploy resources | `string` | n/a | yes | +| [tags](#input\_tags) | Resource tags | `map(string)` | n/a | yes | + +## Outputs + +No outputs. + \ No newline at end of file diff --git a/terraform/modules/azure-alerts/alerts.tf b/terraform/modules/azure-alerts/alerts.tf new file mode 100644 index 000000000..bd175a474 --- /dev/null +++ b/terraform/modules/azure-alerts/alerts.tf @@ -0,0 +1,193 @@ +# Supported Metrics can be found here: https://learn.microsoft.com/en-us/azure/azure-monitor/reference/metrics-index + +# Create the Dev Team action group - Manual step to add / update users in the group +resource "azurerm_monitor_action_group" "dev_team" { + name = "dev-team-action-group" + resource_group_name = var.resource_group + short_name = "Dev Team" + tags = var.tags + + # Should ignore changes made to email receivers + lifecycle { + ignore_changes = [ + email_receiver, + tags["Environment"], + tags["Product"], + tags["Service Offering"] + ] + } +} + +# Alert for CPU >= 90% +resource "azurerm_monitor_metric_alert" "cpu_alert" { + name = "cpu-alert" + resource_group_name = var.resource_group + scopes = [var.app_service_plan_id] + description = "Action will be triggered when CPU Percentage is greater than 90%" + tags = var.tags + severity = 2 # warning + + criteria { + metric_namespace = "Microsoft.Web/serverfarms" + metric_name = "CpuPercentage" + aggregation = "Average" + operator = "GreaterThanOrEqual" + threshold = 90 + } + + action { + action_group_id = azurerm_monitor_action_group.dev_team.id + } + + lifecycle { + ignore_changes = [ + tags["Environment"], + tags["Product"], + tags["Service Offering"] + ] + } +} + +# Alert for Memory >= 90% +resource "azurerm_monitor_metric_alert" "memory_alert" { + name = "memory-alert" + resource_group_name = var.resource_group + scopes = [var.app_service_plan_id] + description = "Action will be triggered when Memory Percentage is greater than 90%" + tags = var.tags + severity = 2 # warning + + criteria { + metric_namespace = "Microsoft.Web/serverfarms" + metric_name = "MemoryPercentage" + aggregation = "Average" + operator = "GreaterThanOrEqual" + threshold = 90 + } + + action { + action_group_id = azurerm_monitor_action_group.dev_team.id + } + + lifecycle { + ignore_changes = [ + tags["Environment"], + tags["Product"], + tags["Service Offering"] + ] + } +} + +# Alert for Http4xx errors Avg >= 10 +resource "azurerm_monitor_metric_alert" "http4xx_errors" { + name = "http4xx-alert" + resource_group_name = var.resource_group + scopes = [var.app_service_webapp_id] + description = "Action will be triggered when Http4xx errors occur" + tags = var.tags + severity = 2 # warning + + criteria { + metric_namespace = "Microsoft.Web/sites" + metric_name = "Http4xx" + aggregation = "Average" + operator = "GreaterThanOrEqual" + threshold = 10 + } + + action { + action_group_id = azurerm_monitor_action_group.dev_team.id + } + + lifecycle { + ignore_changes = [ + tags["Environment"], + tags["Product"], + tags["Service Offering"] + ] + } +} + +# Alert for Http5xx errors Avg >= 10 +resource "azurerm_monitor_metric_alert" "http5xx_errors" { + name = "http5xx-alert" + resource_group_name = var.resource_group + scopes = [var.app_service_webapp_id] + description = "Action will be triggered when Http5xx errors occur" + tags = var.tags + severity = 1 # error + + criteria { + metric_namespace = "Microsoft.Web/sites" + metric_name = "Http5xx" + aggregation = "Average" + operator = "GreaterThanOrEqual" + threshold = 10 + } + + action { + action_group_id = azurerm_monitor_action_group.dev_team.id + } + + lifecycle { + ignore_changes = [ + tags["Environment"], + tags["Product"], + tags["Service Offering"] + ] + } +} + +# Alert for App Service Plan Instance increase +resource "azurerm_monitor_activity_log_alert" "instance_count_increase" { + name = "instance-count-increase-alert" + resource_group_name = var.resource_group + scopes = [var.app_service_plan_id] + description = "Action will be triggered when the instance count increases" + tags = var.tags + + criteria { + category = "Autoscale" + operation_name = "Microsoft.Insights/AutoscaleSettings/ScaleupResult/Action" + status = "Succeeded" + } + + action { + action_group_id = azurerm_monitor_action_group.dev_team.id + } + + lifecycle { + ignore_changes = [ + tags["Environment"], + tags["Product"], + tags["Service Offering"] + ] + } +} + +# Alert for App Service Plan Instance decrease +resource "azurerm_monitor_activity_log_alert" "instance_count_decrease" { + name = "instance-count-decrease-alert" + resource_group_name = var.resource_group + scopes = [var.app_service_plan_id] + description = "Action will be triggered when the instance count decreases" + tags = var.tags + + criteria { + category = "Autoscale" + operation_name = "Microsoft.Insights/AutoscaleSettings/ScaledownResult/Action" + status = "Succeeded" + } + + action { + action_group_id = azurerm_monitor_action_group.dev_team.id + } + + lifecycle { + ignore_changes = [ + tags["Environment"], + tags["Product"], + tags["Service Offering"] + ] + } +} \ No newline at end of file diff --git a/terraform/modules/azure-alerts/variables.tf b/terraform/modules/azure-alerts/variables.tf new file mode 100644 index 000000000..448c1e23a --- /dev/null +++ b/terraform/modules/azure-alerts/variables.tf @@ -0,0 +1,19 @@ +variable "resource_group" { + description = "Name of the Azure Resource Group to deploy resources" + type = string +} + +variable "app_service_plan_id" { + description = "Id of the App Service Plan" + type = string +} + +variable "app_service_webapp_id" { + description = "Id of the Web Application" + type = string +} + +variable "tags" { + description = "Resource tags" + type = map(string) +} diff --git a/terraform/modules/azure-storage/README.md b/terraform/modules/azure-storage/README.md index 9fd85d589..b373bbb64 100644 --- a/terraform/modules/azure-storage/README.md +++ b/terraform/modules/azure-storage/README.md @@ -12,7 +12,6 @@ No requirements. | Name | Version | |------|---------| | [azurerm](#provider\_azurerm) | n/a | -| [random](#provider\_random) | n/a | ## Modules @@ -26,7 +25,6 @@ No modules. | [azurerm_storage_account.sa](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/storage_account) | resource | | [azurerm_storage_account_network_rules.sa_network_rules](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/storage_account_network_rules) | resource | | [azurerm_storage_container.data_protection](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/storage_container) | resource | -| [random_string.resource_code](https://registry.terraform.io/providers/hashicorp/random/latest/docs/resources/string) | resource | ## Inputs @@ -36,6 +34,7 @@ No modules. | [location](#input\_location) | Name of the Azure region to deploy resources | `string` | n/a | yes | | [resource\_group](#input\_resource\_group) | Name of the Azure Resource Group to deploy resources | `string` | n/a | yes | | [tags](#input\_tags) | Resource tags | `map(string)` | n/a | yes | +| [webapp\_storage\_account\_name](#input\_webapp\_storage\_account\_name) | Storage Account name | `string` | n/a | yes | | [webapp\_subnet\_id](#input\_webapp\_subnet\_id) | The ID of the WebApp Subnet | `string` | n/a | yes | ## Outputs diff --git a/terraform/modules/azure-web/README.md b/terraform/modules/azure-web/README.md index a1fa1e151..7c8b0e067 100644 --- a/terraform/modules/azure-web/README.md +++ b/terraform/modules/azure-web/README.md @@ -77,5 +77,8 @@ No modules. ## Outputs -No outputs. +| Name | Description | +|------|-------------| +| [app\_service\_plan\_id](#output\_app\_service\_plan\_id) | ID of the App Service Plan for the Web Application | +| [app\_service\_webapp\_id](#output\_app\_service\_webapp\_id) | ID of the Web Application | \ No newline at end of file diff --git a/terraform/modules/azure-web/outputs.tf b/terraform/modules/azure-web/outputs.tf new file mode 100644 index 000000000..b3357590f --- /dev/null +++ b/terraform/modules/azure-web/outputs.tf @@ -0,0 +1,9 @@ +output "app_service_plan_id" { + description = "ID of the App Service Plan for the Web Application" + value = azurerm_service_plan.asp.id +} + +output "app_service_webapp_id" { + description = "ID of the Web Application" + value = azurerm_linux_web_app.webapp.id +} \ No newline at end of file diff --git a/tests/Dfe.EarlyYearsQualification.E2ETests/cypress/e2e/journey/back-button-spec.cy.js b/tests/Dfe.EarlyYearsQualification.E2ETests/cypress/e2e/journey/back-button-spec.cy.js index a8ff9ef4d..c20b42207 100644 --- a/tests/Dfe.EarlyYearsQualification.E2ETests/cypress/e2e/journey/back-button-spec.cy.js +++ b/tests/Dfe.EarlyYearsQualification.E2ETests/cypress/e2e/journey/back-button-spec.cy.js @@ -87,6 +87,20 @@ describe('A spec used to test the main back button route through the journey', ( /// Time to go back through the journey! cy.get('#back-button').click(); + + // confirm answers page + cy.location().should((loc) => { + expect(loc.pathname).to.eq('/qualifications/check-additional-questions/EYQ-240/confirm-answers'); + }) + + cy.get('#back-button').click(); + + cy.location().should((loc) => { + // answered additional questions, so back to additional questions page + expect(loc.pathname).to.eq('/qualifications/check-additional-questions/EYQ-240/2') + }) + + cy.get('#back-button').click(); cy.location().should((loc) => { // answered additional questions, so back to additional questions page diff --git a/tests/Dfe.EarlyYearsQualification.E2ETests/cypress/e2e/journey/journey-spec.cy.js b/tests/Dfe.EarlyYearsQualification.E2ETests/cypress/e2e/journey/journey-spec.cy.js index 21241527d..dfbc25b7c 100644 --- a/tests/Dfe.EarlyYearsQualification.E2ETests/cypress/e2e/journey/journey-spec.cy.js +++ b/tests/Dfe.EarlyYearsQualification.E2ETests/cypress/e2e/journey/journey-spec.cy.js @@ -216,8 +216,8 @@ describe('A spec used to test the various routes through the journey', () => { expect(loc.pathname).to.eq('/qualifications'); }) }) - - it("Selecting qualification level 6 started before 1 Sept 2014 should navigate to the level 6 pre 2014 advice page", () => { + + it("Selecting qualification level 7 started after 1 Sept 2014 should navigate to the level 7 post 2014 advice page", () => { // home page cy.get('.govuk-button--start').click(); @@ -235,19 +235,19 @@ describe('A spec used to test the various routes through the journey', () => { }) cy.get('#date-started-month').type("8"); - cy.get('#date-started-year').type("2014"); + cy.get('#date-started-year').type("2015"); cy.get('button[id="question-submit"]').click(); // what-level-is-the-qualification page cy.location().should((loc) => { expect(loc.pathname).to.eq('/questions/what-level-is-the-qualification'); }) - cy.get('#6').click(); + cy.get('#7').click(); cy.get('button[id="question-submit"]').click(); - // level 6 pre 2014 advice page + // level 7 post 2014 advice page cy.location().should((loc) => { - expect(loc.pathname).to.eq('/advice/level-6-qualification-pre-2014'); + expect(loc.pathname).to.eq('/advice/level-7-qualification-post-2014'); }) // check back button goes back to the what level is the qualification page @@ -257,8 +257,25 @@ describe('A spec used to test the various routes through the journey', () => { expect(loc.pathname).to.eq('/questions/what-level-is-the-qualification'); }) }) + + it("should move the user back to the previous page when they click on the back button", () => { + // home page + cy.get('.govuk-button--start').click(); + + // where-was-the-qualification-awarded page + cy.location().should((loc) => { + expect(loc.pathname).to.eq('/questions/where-was-the-qualification-awarded'); + }) + + cy.get('.govuk-back-link').click(); - it("Selecting qualification level 6 started after 1 Sept 2014 should navigate to the level 6 post 2014 advice page", () => { + // home page + cy.location().should((loc) => { + expect(loc.pathname).to.eq('/'); + }) + }) + + it("Should remove the search criteria when a user goes to the awarding organisation page and back again", () => { // home page cy.get('.govuk-button--start').click(); @@ -275,31 +292,98 @@ describe('A spec used to test the various routes through the journey', () => { expect(loc.pathname).to.eq('/questions/when-was-the-qualification-started'); }) - cy.get('#date-started-month').type("8"); - cy.get('#date-started-year').type("2015"); + cy.get('#date-started-month').type("6"); + cy.get('#date-started-year').type("2022"); cy.get('button[id="question-submit"]').click(); // what-level-is-the-qualification page cy.location().should((loc) => { expect(loc.pathname).to.eq('/questions/what-level-is-the-qualification'); }) - cy.get('#6').click(); + cy.get('#3').click(); cy.get('button[id="question-submit"]').click(); - // level 6 post 2014 advice page + // what-is-the-awarding-organisation page cy.location().should((loc) => { - expect(loc.pathname).to.eq('/advice/level-6-qualification-post-2014'); + expect(loc.pathname).to.eq('/questions/what-is-the-awarding-organisation'); }) - // check back button goes back to the what level is the qualification page + cy.get('#awarding-organisation-select').select(1); // first no-default item in the list + cy.get('button[id="question-submit"]').click(); + + // Select a qualification page + cy.location().should((loc) => { + expect(loc.pathname).to.eq('/qualifications'); + }) + + cy.get('#refineSearch').type('test'); + cy.get('#refineSearchButton').click(); + + cy.location().should((loc) => { + expect(loc.pathname).to.eq('/qualifications'); + }) + cy.get('#back-button').click(); + // what-is-the-awarding-organisation page + cy.location().should((loc) => { + expect(loc.pathname).to.eq('/questions/what-is-the-awarding-organisation'); + }) + + cy.get('#awarding-organisation-select').select(1); // first no-default item in the list + cy.get('button[id="question-submit"]').click(); + // Select a qualification page cy.location().should((loc) => { - expect(loc.pathname).to.eq('/questions/what-level-is-the-qualification'); + expect(loc.pathname).to.eq('/qualifications'); }) + + cy.get('#refineSearch').should('have.value', ''); }) - it("Selecting qualification level 7 should navigate to the level 7 advice page", () => { + const testDates = [ + ['09', '2014'], + ['06', '2017'], + ['08', '2019'], + ]; + + testDates.forEach((date) => { + const [month, year] = date; + it(`should redirect when qualification is level 2 and startMonth is ${month} and startYear is ${year}`, () => { + // home page + cy.get('.govuk-button--start').click(); + + // where-was-the-qualification-awarded page + cy.location().should((loc) => { + expect(loc.pathname).to.eq('/questions/where-was-the-qualification-awarded'); + }) + + cy.get('#england').click(); + cy.get('button[id="question-submit"]').click(); + + // when-was-the-qualification-started page + cy.location().should((loc) => { + expect(loc.pathname).to.eq('/questions/when-was-the-qualification-started'); + }) + + cy.get('#date-started-month').type(month); + cy.get('#date-started-year').type(year); + cy.get('button[id="question-submit"]').click(); + + // what-level-is-the-qualification page + cy.location().should((loc) => { + expect(loc.pathname).to.eq('/questions/what-level-is-the-qualification'); + }) + cy.get('#2').click(); + cy.get('button[id="question-submit"]').click(); + + // level-2-qualifications-started-between-1-sept-2014-and-31-aug-2019 page + cy.location().should((loc) => { + expect(loc.pathname).to.eq('/advice/level-2-qualifications-started-between-1-sept-2014-and-31-aug-2019'); + }) + }) + }) + + it("should bypass remaining additional requirement question when answering yes to the Qts question", () => { // home page cy.get('.govuk-button--start').click(); @@ -324,40 +408,54 @@ describe('A spec used to test the various routes through the journey', () => { cy.location().should((loc) => { expect(loc.pathname).to.eq('/questions/what-level-is-the-qualification'); }) - cy.get('#7').click(); + cy.get('#6').click(); + cy.get('button[id="question-submit"]').click(); + + // what-is-the-awarding-organisation page + cy.location().should((loc) => { + expect(loc.pathname).to.eq('/questions/what-is-the-awarding-organisation'); + }) + + cy.get('#awarding-organisation-select').select(1); // first no-default item in the list cy.get('button[id="question-submit"]').click(); - // level 7 advice page + // qualifications page cy.location().should((loc) => { - expect(loc.pathname).to.eq('/advice/qualification-level-7'); + expect(loc.pathname).to.eq('/qualifications'); }) - // check back button goes back to the what level is the qualification page - cy.get('#back-button').click(); + cy.get('a[href="/confirm-qualification/EYQ-108"]').click(); + // confirm qualification page cy.location().should((loc) => { - expect(loc.pathname).to.eq('/questions/what-level-is-the-qualification'); + expect(loc.pathname).to.eq('/confirm-qualification/EYQ-108'); }) - }) - it("should move the user back to the previous page when they click on the back button", () => { - // home page - cy.get('.govuk-button--start').click(); + cy.get('#yes').click(); + cy.get('button[id="confirm-qualification-button"]').click(); - // where-was-the-qualification-awarded page + // check additional questions first page cy.location().should((loc) => { - expect(loc.pathname).to.eq('/questions/where-was-the-qualification-awarded'); + expect(loc.pathname).to.eq('/qualifications/check-additional-questions/EYQ-108/1'); }) - cy.get('.govuk-back-link').click(); + cy.get('#yes').click(); + cy.get('button[id="additional-requirement-button"]').click(); - // home page + // confirm answers page cy.location().should((loc) => { - expect(loc.pathname).to.eq('/'); + expect(loc.pathname).to.eq('/qualifications/check-additional-questions/EYQ-108/confirm-answers'); + }) + + cy.get("#confirm-answers").click(); + + // qualification details page + cy.location().should((loc) => { + expect(loc.pathname).to.eq('/qualifications/qualification-details/EYQ-108'); }) }) - it("Should remove the search criteria when a user goes to the awarding organisation page and back again", () => { + it("should not bypass remaining additional requirement question when answering no to the Qts question", () => { // home page cy.get('.govuk-button--start').click(); @@ -382,7 +480,7 @@ describe('A spec used to test the various routes through the journey', () => { cy.location().should((loc) => { expect(loc.pathname).to.eq('/questions/what-level-is-the-qualification'); }) - cy.get('#3').click(); + cy.get('#6').click(); cy.get('button[id="question-submit"]').click(); // what-is-the-awarding-organisation page @@ -393,75 +491,47 @@ describe('A spec used to test the various routes through the journey', () => { cy.get('#awarding-organisation-select').select(1); // first no-default item in the list cy.get('button[id="question-submit"]').click(); - // Select a qualification page + // qualifications page cy.location().should((loc) => { expect(loc.pathname).to.eq('/qualifications'); }) - - cy.get('#refineSearch').type('test'); - cy.get('#refineSearchButton').click(); + cy.get('a[href="/confirm-qualification/EYQ-108"]').click(); + + // confirm qualification page cy.location().should((loc) => { - expect(loc.pathname).to.eq('/qualifications'); - }) - - cy.get('#back-button').click(); - // what-is-the-awarding-organisation page - cy.location().should((loc) => { - expect(loc.pathname).to.eq('/questions/what-is-the-awarding-organisation'); + expect(loc.pathname).to.eq('/confirm-qualification/EYQ-108'); }) - cy.get('#awarding-organisation-select').select(1); // first no-default item in the list - cy.get('button[id="question-submit"]').click(); + cy.get('#yes').click(); + cy.get('button[id="confirm-qualification-button"]').click(); - // Select a qualification page + // check additional questions first page cy.location().should((loc) => { - expect(loc.pathname).to.eq('/qualifications'); + expect(loc.pathname).to.eq('/qualifications/check-additional-questions/EYQ-108/1'); }) - cy.get('#refineSearch').should('have.value', ''); - }) - - const testDates = [ - ['09', '2014'], - ['06', '2017'], - ['08', '2019'], - ]; - - testDates.forEach((date) => { - const [month, year] = date; - it(`should redirect when qualification is level 2 and startMonth is ${month} and startYear is ${year}`, () => { - // home page - cy.get('.govuk-button--start').click(); - - // where-was-the-qualification-awarded page - cy.location().should((loc) => { - expect(loc.pathname).to.eq('/questions/where-was-the-qualification-awarded'); - }) + cy.get('#no').click(); + cy.get('button[id="additional-requirement-button"]').click(); - cy.get('#england').click(); - cy.get('button[id="question-submit"]').click(); + // check second additional question + cy.location().should((loc) => { + expect(loc.pathname).to.eq('/qualifications/check-additional-questions/EYQ-108/2'); + }) - // when-was-the-qualification-started page - cy.location().should((loc) => { - expect(loc.pathname).to.eq('/questions/when-was-the-qualification-started'); - }) + cy.get('#yes').click(); + cy.get('button[id="additional-requirement-button"]').click(); - cy.get('#date-started-month').type(month); - cy.get('#date-started-year').type(year); - cy.get('button[id="question-submit"]').click(); + // confirm answers page + cy.location().should((loc) => { + expect(loc.pathname).to.eq('/qualifications/check-additional-questions/EYQ-108/confirm-answers'); + }) - // what-level-is-the-qualification page - cy.location().should((loc) => { - expect(loc.pathname).to.eq('/questions/what-level-is-the-qualification'); - }) - cy.get('#2').click(); - cy.get('button[id="question-submit"]').click(); + cy.get("#confirm-answers").click(); - // level-2-qualifications-started-between-1-sept-2014-and-31-aug-2019 page - cy.location().should((loc) => { - expect(loc.pathname).to.eq('/advice/level-2-qualifications-started-between-1-sept-2014-and-31-aug-2019'); - }) + // qualification details page + cy.location().should((loc) => { + expect(loc.pathname).to.eq('/qualifications/qualification-details/EYQ-108'); }) }) }) \ No newline at end of file diff --git a/tests/Dfe.EarlyYearsQualification.E2ETests/cypress/e2e/pages/advice-spec.cy.js b/tests/Dfe.EarlyYearsQualification.E2ETests/cypress/e2e/pages/advice-spec.cy.js index 66e1632dc..396981910 100644 --- a/tests/Dfe.EarlyYearsQualification.E2ETests/cypress/e2e/pages/advice-spec.cy.js +++ b/tests/Dfe.EarlyYearsQualification.E2ETests/cypress/e2e/pages/advice-spec.cy.js @@ -79,60 +79,12 @@ describe("A spec that tests advice pages", () => { cy.get(".govuk-notification-banner__heading").eq(1).should("contain.text", "Feedback heading"); cy.get(".govuk-notification-banner__content").eq(1).should("contain.text", "This is the body text"); }) - - it("Checks the Level 6 qualification pre 2014 details are on the page", () => { - cy.setCookie('user_journey', '%7B%22WhenWasQualificationStarted%22%3A%227%2F2015%22%7D'); - cy.visit("/advice/level-6-qualification-pre-2014"); - - cy.get("#advice-page-heading").should("contain.text", "Level 6 qualification pre 2014"); - cy.get("#advice-page-body").should("contain.text", "Test Advice Page Body"); - - cy.get(".govuk-notification-banner__title").eq(0).should("contain.text", "Test banner title"); - cy.get(".govuk-notification-banner__heading").eq(0).should("contain.text", "Feedback heading"); - cy.get(".govuk-notification-banner__content").eq(0).should("contain.text", "This is the body text"); - - cy.get(".govuk-notification-banner__title").eq(1).should("contain.text", "Test banner title"); - cy.get(".govuk-notification-banner__heading").eq(1).should("contain.text", "Feedback heading"); - cy.get(".govuk-notification-banner__content").eq(1).should("contain.text", "This is the body text"); - }) - - it("Checks the Level 6 qualification post 2014 details are on the page", () => { - cy.setCookie('user_journey', '%7B%22WhenWasQualificationStarted%22%3A%227%2F2015%22%7D'); - cy.visit("/advice/level-6-qualification-post-2014"); - - cy.get("#advice-page-heading").should("contain.text", "Level 6 qualification post 2014"); - cy.get("#advice-page-body").should("contain.text", "Test Advice Page Body"); - - cy.get(".govuk-notification-banner__title").eq(0).should("contain.text", "Test banner title"); - cy.get(".govuk-notification-banner__heading").eq(0).should("contain.text", "Feedback heading"); - cy.get(".govuk-notification-banner__content").eq(0).should("contain.text", "This is the body text"); - - cy.get(".govuk-notification-banner__title").eq(1).should("contain.text", "Test banner title"); - cy.get(".govuk-notification-banner__heading").eq(1).should("contain.text", "Feedback heading"); - cy.get(".govuk-notification-banner__content").eq(1).should("contain.text", "This is the body text"); - }) - - it("Checks the Qualifications level 7 details are on the page", () => { - cy.setCookie('user_journey', '%7B%22WhenWasQualificationStarted%22%3A%227%2F2015%22%7D'); - cy.visit("/advice/qualification-level-7"); - - cy.get("#advice-page-heading").should("contain.text", "Qualification at Level 7"); - cy.get("#advice-page-body").should("contain.text", "Test Advice Page Body"); - - cy.get(".govuk-notification-banner__title").eq(0).should("contain.text", "Test banner title"); - cy.get(".govuk-notification-banner__heading").eq(0).should("contain.text", "Feedback heading"); - cy.get(".govuk-notification-banner__content").eq(0).should("contain.text", "This is the body text"); - - cy.get(".govuk-notification-banner__title").eq(1).should("contain.text", "Test banner title"); - cy.get(".govuk-notification-banner__heading").eq(1).should("contain.text", "Feedback heading"); - cy.get(".govuk-notification-banner__content").eq(1).should("contain.text", "This is the body text"); - }) - - it("Checks the Temporary privacy policy details are on the page", () => { + + it("Checks the Level 7 qualification post 2014 details are on the page", () => { cy.setCookie('user_journey', '%7B%22WhenWasQualificationStarted%22%3A%227%2F2015%22%7D'); - cy.visit("/advice/privacy-policy"); + cy.visit("/advice/level-7-qualification-post-2014"); - cy.get("#advice-page-heading").should("contain.text", "Temporary privacy policy"); + cy.get("#advice-page-heading").should("contain.text", "Level 7 qualification post 2014"); cy.get("#advice-page-body").should("contain.text", "Test Advice Page Body"); cy.get(".govuk-notification-banner__title").eq(0).should("contain.text", "Test banner title"); diff --git a/tests/Dfe.EarlyYearsQualification.E2ETests/cypress/e2e/pages/check-additional-questions-spec.cy.js b/tests/Dfe.EarlyYearsQualification.E2ETests/cypress/e2e/pages/check-additional-questions-spec.cy.js index 4896db2e9..4edae2669 100644 --- a/tests/Dfe.EarlyYearsQualification.E2ETests/cypress/e2e/pages/check-additional-questions-spec.cy.js +++ b/tests/Dfe.EarlyYearsQualification.E2ETests/cypress/e2e/pages/check-additional-questions-spec.cy.js @@ -23,7 +23,7 @@ describe("A spec that tests the check additional questions page", () => { it("Checks the check additional questions details are on the second question page", () => { cy.visit("/qualifications/check-additional-questions/eyq-240/2"); - cy.get("#back-button").should("have.attr", "href").and("include", "/previous"); + cy.get("#back-button").should("have.attr", "href").and("include", "/qualifications/check-additional-questions"); cy.get("#question").should("contain.text", "Test question 2"); cy.get("#hint").should("contain.text", "This is the hint text"); diff --git a/tests/Dfe.EarlyYearsQualification.E2ETests/cypress/e2e/pages/confirm-additional-question-answers-spec.cy.js b/tests/Dfe.EarlyYearsQualification.E2ETests/cypress/e2e/pages/confirm-additional-question-answers-spec.cy.js index fea2d8a95..6cf2cb25b 100644 --- a/tests/Dfe.EarlyYearsQualification.E2ETests/cypress/e2e/pages/confirm-additional-question-answers-spec.cy.js +++ b/tests/Dfe.EarlyYearsQualification.E2ETests/cypress/e2e/pages/confirm-additional-question-answers-spec.cy.js @@ -20,7 +20,8 @@ describe("A spec used to test the check additional requirements answer page", () cy.get("#question-2-question").should("contain.text", "Test question"); cy.get("#question-2-answer").should("contain.text", "Yes"); cy.get("#question-2-change").should("contain.text", "Test change answer text"); - + + cy.get('#warning-text-container').should("exist"); cy.get(".govuk-warning-text__text").should("contain.text", "Test answer disclaimer text"); cy.get("#confirm-answers").should("contain.text", "Test button text"); diff --git a/tests/Dfe.EarlyYearsQualification.E2ETests/cypress/e2e/pages/confirm-qualification-spec.cy.js b/tests/Dfe.EarlyYearsQualification.E2ETests/cypress/e2e/pages/confirm-qualification-spec.cy.js index f492debfb..8610b7177 100644 --- a/tests/Dfe.EarlyYearsQualification.E2ETests/cypress/e2e/pages/confirm-qualification-spec.cy.js +++ b/tests/Dfe.EarlyYearsQualification.E2ETests/cypress/e2e/pages/confirm-qualification-spec.cy.js @@ -23,6 +23,8 @@ describe("A spec that tests the confirm qualification page", () => { cy.get('label[for="yes"]').should("contain.text", "yes"); cy.get('label[for="no"]').should("contain.text", "no"); + cy.get('#warning-text-container').should("not.exist"); + cy.get("#confirm-qualification-button").should("contain.text", "Test button text"); cy.get(".govuk-error-summary").should("not.exist"); @@ -36,6 +38,14 @@ describe("A spec that tests the confirm qualification page", () => { cy.get('#various-ao-content').should("contain.text", "Various awarding organisation explanation text"); }); + it("Checks the warning content is on the page when the qualification has no additional requirement questions", () => { + cy.visit("/confirm-qualification/eyq-115"); + + cy.get('#warning-text-container').should("exist"); + cy.get('#warning-text-container').should("contain.text", "Answer disclaimer text"); + cy.get("#confirm-qualification-button").should("contain.text", "Get result"); + }); + it("Shows errors if user does not select an option", () => { cy.visit("/confirm-qualification/eyq-240"); diff --git a/tests/Dfe.EarlyYearsQualification.E2ETests/cypress/e2e/pages/qualification-details-spec.cy.js b/tests/Dfe.EarlyYearsQualification.E2ETests/cypress/e2e/pages/qualification-details-spec.cy.js index 160024aec..99595d4cf 100644 --- a/tests/Dfe.EarlyYearsQualification.E2ETests/cypress/e2e/pages/qualification-details-spec.cy.js +++ b/tests/Dfe.EarlyYearsQualification.E2ETests/cypress/e2e/pages/qualification-details-spec.cy.js @@ -46,6 +46,53 @@ describe("A spec used to test the qualification details page", () => { cy.get(".govuk-notification-banner__content").eq(1).should("contain.text", "Test body"); }) + it("Checks the order of the ratios for a level 6 qualification when a user answers yes to the Qts Question", () => { + // Value is '{"WhereWasQualificationAwarded":"england","WhenWasQualificationStarted":"7/2015","LevelOfQualification":"6","WhatIsTheAwardingOrganisation":"NCFE","SearchCriteria":"","AdditionalQuestionsAnswers":{"This is the Qts question":"yes"}}' encoded + cy.setCookie('user_journey', '%7B%22WhereWasQualificationAwarded%22%3A%22england%22%2C%22WhenWasQualificationStarted%22%3A%227%2F2015%22%2C%22LevelOfQualification%22%3A%226%22%2C%22WhatIsTheAwardingOrganisation%22%3A%22NCFE%22%2C%22SearchCriteria%22%3A%22%22%2C%22AdditionalQuestionsAnswers%22%3A%7B%22This%20is%20the%20Qts%20question%22%3A%22yes%22%7D%7D'); + cy.visit("/qualifications/qualification-details/eyq-108"); + + cy.get(".ratio-row").should('have.length', 4); + cy.get(".ratio-heading").eq(0).should("contain.text", "Level 6"); + cy.get(".ratio-heading").eq(1).should("contain.text", "Level 3"); + cy.get(".ratio-heading").eq(2).should("contain.text", "Level 2"); + cy.get(".ratio-heading").eq(3).should("contain.text", "Unqualified"); + + + // Phase Banner uses govuk-tag also hence index starting at 1 + cy.get(".govuk-tag").eq(1).should("contain.text", "Approved"); + cy.get(".govuk-tag").eq(2).should("contain.text", "Approved"); + cy.get(".govuk-tag").eq(3).should("contain.text", "Approved"); + cy.get(".govuk-tag").eq(4).should("contain.text", "Approved"); + + cy.get(".govuk-tag").eq(1).should("have.class", "govuk-tag--green"); + cy.get(".govuk-tag").eq(2).should("have.class", "govuk-tag--green"); + cy.get(".govuk-tag").eq(3).should("have.class", "govuk-tag--green"); + cy.get(".govuk-tag").eq(4).should("have.class", "govuk-tag--green"); + }) + + it("Checks the order of the ratios for a level 6 qualification when a user answers no to the Qts Question but yes to the remaining question", () => { + // Value is '{"WhereWasQualificationAwarded":"england","WhenWasQualificationStarted":"7/2015","LevelOfQualification":"6","WhatIsTheAwardingOrganisation":"NCFE","SearchCriteria":"","AdditionalQuestionsAnswers":{"This is the Qts question":"no","Test question 2":"yes"}}' encoded + cy.setCookie('user_journey', '%7B%22WhereWasQualificationAwarded%22%3A%22england%22%2C%22WhenWasQualificationStarted%22%3A%227%2F2015%22%2C%22LevelOfQualification%22%3A%226%22%2C%22WhatIsTheAwardingOrganisation%22%3A%22NCFE%22%2C%22SearchCriteria%22%3A%22%22%2C%22AdditionalQuestionsAnswers%22%3A%7B%22This%20is%20the%20Qts%20question%22%3A%22no%22%2C%22Test%20question%202%22%3A%22yes%22%7D%7D'); + cy.visit("/qualifications/qualification-details/eyq-108"); + + cy.get(".ratio-row").should('have.length', 4); + cy.get(".ratio-heading").eq(0).should("contain.text", "Level 3"); + cy.get(".ratio-heading").eq(1).should("contain.text", "Level 2"); + cy.get(".ratio-heading").eq(2).should("contain.text", "Unqualified"); + cy.get(".ratio-heading").eq(3).should("contain.text", "Level 6"); + + // Phase Banner uses govuk-tag also hence index starting at 1 + cy.get(".govuk-tag").eq(1).should("contain.text", "Approved"); + cy.get(".govuk-tag").eq(2).should("contain.text", "Approved"); + cy.get(".govuk-tag").eq(3).should("contain.text", "Approved"); + cy.get(".govuk-tag").eq(4).should("contain.text", "Not approved"); + + cy.get(".govuk-tag").eq(1).should("have.class", "govuk-tag--green"); + cy.get(".govuk-tag").eq(2).should("have.class", "govuk-tag--green"); + cy.get(".govuk-tag").eq(3).should("have.class", "govuk-tag--green"); + cy.get(".govuk-tag").eq(4).should("have.class", "govuk-tag--red"); + }) + it("Checks the order of the ratios on the page when a user answers additional requirement questions indicating full and relevant", () => { // Value is '{"WhereWasQualificationAwarded":"england","WhenWasQualificationStarted":"7/2015","LevelOfQualification":"3","WhatIsTheAwardingOrganisation":"NCFE","SearchCriteria":"","AdditionalQuestionsAnswers":{"Test question":"yes","Test question 2":"no"}}' encoded cy.setCookie('user_journey', '%7B%22WhereWasQualificationAwarded%22%3A%22england%22%2C%22WhenWasQualificationStarted%22%3A%227%2F2015%22%2C%22LevelOfQualification%22%3A%223%22%2C%22WhatIsTheAwardingOrganisation%22%3A%22NCFE%22%2C%22SearchCriteria%22%3A%22%22%2C%22AdditionalQuestionsAnswers%22%3A%7B%22Test%20question%22%3A%22yes%22%2C%22Test%20question%202%22%3A%22no%22%7D%7D'); @@ -61,7 +108,7 @@ describe("A spec used to test the qualification details page", () => { cy.get(".govuk-tag").eq(1).should("contain.text", "Approved"); cy.get(".govuk-tag").eq(2).should("contain.text", "Approved"); cy.get(".govuk-tag").eq(3).should("contain.text", "Approved"); - cy.get(".govuk-tag").eq(4).should("contain.text", "Not Approved"); + cy.get(".govuk-tag").eq(4).should("contain.text", "Not approved"); cy.get(".govuk-tag").eq(1).should("have.class", "govuk-tag--green"); cy.get(".govuk-tag").eq(2).should("have.class", "govuk-tag--green"); @@ -81,7 +128,7 @@ describe("A spec used to test the qualification details page", () => { it("Checks the order of the ratios on the page when a user answers an additional requirement question indicating not full and relevant", () => { // Value is '{"WhereWasQualificationAwarded":"england","WhenWasQualificationStarted":"7/2015","LevelOfQualification":"3","WhatIsTheAwardingOrganisation":"NCFE","SearchCriteria":"","AdditionalQuestionsAnswers":{"Test question":"yes","Test question 2":"yes"}}' encoded cy.setCookie('user_journey', '%7B%22WhereWasQualificationAwarded%22%3A%22england%22%2C%22WhenWasQualificationStarted%22%3A%227%2F2015%22%2C%22LevelOfQualification%22%3A%223%22%2C%22WhatIsTheAwardingOrganisation%22%3A%22NCFE%22%2C%22SearchCriteria%22%3A%22%22%2C%22AdditionalQuestionsAnswers%22%3A%7B%22Test%20question%22%3A%22yes%22%2C%22Test%20question%202%22%3A%22yes%22%7D%7D'); - cy.visit("/qualifications/qualification-details/eyq-240"); + cy.visit("/qualifications/qualification-details/eyq-241"); cy.get(".ratio-row").should('have.length', 4); cy.get(".ratio-heading").eq(0).should("contain.text", "Unqualified"); @@ -91,9 +138,9 @@ describe("A spec used to test the qualification details page", () => { // Phase Banner uses govuk-tag also hence index starting at 1 cy.get(".govuk-tag").eq(1).should("contain.text", "Approved"); - cy.get(".govuk-tag").eq(2).should("contain.text", "Not Approved"); - cy.get(".govuk-tag").eq(3).should("contain.text", "Not Approved"); - cy.get(".govuk-tag").eq(4).should("contain.text", "Not Approved"); + cy.get(".govuk-tag").eq(2).should("contain.text", "Not approved"); + cy.get(".govuk-tag").eq(3).should("contain.text", "Not approved"); + cy.get(".govuk-tag").eq(4).should("contain.text", "Not approved"); cy.get(".govuk-tag").eq(1).should("have.class", "govuk-tag--green"); cy.get(".govuk-tag").eq(2).should("have.class", "govuk-tag--red"); @@ -116,4 +163,20 @@ describe("A spec used to test the qualification details page", () => { expect(printStub).to.be.calledOnce }) }); + + it("When the user selects a qualification that is above a level 2, started between Sept 2014 and Aug 2019, and is not full and relevant with no questions, they see the level 2 qualification markes as 'Further action required'", () => { + cy.setCookie('user_journey', '%7B%22WhereWasQualificationAwarded%22%3A%22england%22%2C%22WhenWasQualificationStarted%22%3A%226%2F2016%22%2C%22LevelOfQualification%22%3A%225%22%2C%22WhatIsTheAwardingOrganisation%22%3A%22NCFE%22%2C%22SelectedAwardingOrganisationNotOnTheList%22%3Afalse%2C%22SearchCriteria%22%3A%22%22%2C%22AdditionalQuestionsAnswers%22%3A%7B%7D%2C%22QualificationWasSelectedFromList%22%3A1%7D'); + cy.visit("/qualifications/qualification-details/eyq-114"); + + cy.get("#ratio-Level2-tag").should("contain.text", "Further action required"); + cy.get("#ratio-Level2-additional-info").should("contain.text", "Level 2 further action required text"); + }); + + it("When the user selects a qualification that is above a level 2, started between Sept 2014 and Aug 2019, and is not full and relevant due to their answers, they see the level 2 qualification markes as 'Further action required'", () => { + cy.setCookie('user_journey', '%7B%22WhereWasQualificationAwarded%22%3A%22england%22%2C%22WhenWasQualificationStarted%22%3A%2212%2F2016%22%2C%22LevelOfQualification%22%3A%223%22%2C%22WhatIsTheAwardingOrganisation%22%3A%22%22%2C%22SelectedAwardingOrganisationNotOnTheList%22%3Atrue%2C%22SearchCriteria%22%3A%22%22%2C%22AdditionalQuestionsAnswers%22%3A%7B%22Test%20question%22%3A%22no%22%2C%22Test%20question%202%22%3A%22yes%22%7D%2C%22QualificationWasSelectedFromList%22%3A1%7D'); + cy.visit("/qualifications/qualification-details/eyq-240"); + + cy.get("#ratio-Level2-tag").should("contain.text", "Further action required"); + cy.get("#ratio-Level2-additional-info").should("contain.text", "Level 2 further action required text"); + }); }); \ No newline at end of file diff --git a/tests/Dfe.EarlyYearsQualification.E2ETests/cypress/e2e/shared/urls-to-check.js b/tests/Dfe.EarlyYearsQualification.E2ETests/cypress/e2e/shared/urls-to-check.js index 27720e33c..618c50efe 100644 --- a/tests/Dfe.EarlyYearsQualification.E2ETests/cypress/e2e/shared/urls-to-check.js +++ b/tests/Dfe.EarlyYearsQualification.E2ETests/cypress/e2e/shared/urls-to-check.js @@ -16,9 +16,7 @@ export const pages = [ "/advice/qualifications-achieved-in-wales", "/advice/qualifications-achieved-in-northern-ireland", "/advice/qualification-not-on-the-list", - "/advice/qualification-level-7", - "/advice/level-6-qualification-pre-2014", - "/advice/level-6-qualification-post-2014" + "/advice/level-7-qualification-post-2014" ]; export const pagesWithForms = [ @@ -45,9 +43,7 @@ export const pagesWithoutFormsOrRedirects = [ export const pagesWithoutFormsWithRedirects = [ "/qualifications/qualification-details/EYQ-240", "/advice/level-2-qualifications-started-between-1-sept-2014-and-31-aug-2019", - "/advice/qualification-level-7", - "/advice/level-6-qualification-pre-2014", - "/advice/level-6-qualification-post-2014" + "/advice/level-7-qualification-post-2014" ] export const pagesThatRedirectIfDateMissing = [ @@ -58,7 +54,5 @@ export const pagesThatRedirectIfDateMissing = [ "/qualifications/qualification-details/EYQ-240", "/qualifications/check-additional-questions/EYQ-240/1", "/advice/level-2-qualifications-started-between-1-sept-2014-and-31-aug-2019", - "/advice/qualification-level-7", - "/advice/level-6-qualification-pre-2014", - "/advice/level-6-qualification-post-2014" + "/advice/level-7-qualification-post-2014" ] \ No newline at end of file diff --git a/tests/Dfe.EarlyYearsQualification.LoadTests/README.md b/tests/Dfe.EarlyYearsQualification.LoadTests/README.md new file mode 100644 index 000000000..2adc32161 --- /dev/null +++ b/tests/Dfe.EarlyYearsQualification.LoadTests/README.md @@ -0,0 +1,32 @@ +# Grafana K6 tests + +Here we have performance and load tests for the service. + +## Prerequisites + +* Install k6 locally +* Make sure the folder `TestResults` exists in your local git repository's root folder + +## How to run the tests locally + +"Locally" means against the test/staging environment, but on your local machine rather than in a pipeline. + +A `k6` command will kick off a test. For example, to kick off a quick test that runs through a level-3 user journey with 5 virtual users, +run the following in the main repo folder: + +`k6 run tests/Dfe.EarlyYearsQualification.LoadTests/run-tests.js --env CHALLENGE_PASSWORD="[Secret]" --env CUSTOM_DOMAIN="[test-env-domain]" --env OPTIONS_SET=quick` + +…replacing `[Secret]` with a valid password for the challenge page in the test environment, and +`[test-env-domain]` with the custom domain for the service in the test environment. + +If you want to run a load test, ramping up to 80 users, replace `quick` with `load`, and it will pick up that scenario. If you +don't specify an `OPTIONS_SET`, then it will default to `quick`. + +If you want the output in JSON format in a file rather than in text format to the console, add `--env JSON_RESULT=yes` +and the output report will be placed in `TestResults\k6-testResult.json`. + +Remember, we will always expect a challenge password in test/staging, even after the *production* +service is in public beta and set up for unrestricted access. + +When set up, the GitHub pipelines will always use the `test` (staging) environment, and will pass the password and the +domain from the staging environment's secrets and variables configured in GitHub. diff --git a/tests/Dfe.EarlyYearsQualification.LoadTests/browser-start-page.js b/tests/Dfe.EarlyYearsQualification.LoadTests/browser-start-page.js new file mode 100644 index 000000000..f2426cf3e --- /dev/null +++ b/tests/Dfe.EarlyYearsQualification.LoadTests/browser-start-page.js @@ -0,0 +1,56 @@ +import { browser } from 'k6/browser'; +import { sleep, check } from 'k6'; + +var configName = __ENV.CONFIG; + +export const options = { + scenarios: { + ui: { + executor: 'shared-iterations', + options: { + browser: { + type: 'chromium' + } + } + } + }, + thresholds: { + checks: ['rate==1.0'] + } +}; + +const ENVIRONMENT = { + challenge: __ENV.CHALLENGE_PASSWORD, // secret value, passed in from the CLI + customDomain: __ENV.CUSTOM_DOMAIN // custom domain, the address of the service to test, passed in from the CLI +} + +// The function that defines VU logic. +// +// See https://grafana.com/docs/k6/latest/examples/get-started-with-k6/ to learn more +// about authoring k6 scripts. +// +export default async function () { + + const address = 'https://' + ENVIRONMENT.customDomain + '/'; + + const context = await browser.newContext(); + const page = await context.newPage(); + + try { + + await context.addCookies([ + { name: 'auth-secret', value: ENVIRONMENT.challenge, sameSite: 'Strict', url: address } + ]); + + var resp = await page.goto(address,); + + await page.screenshot({ path: 'screenshots/screenshot.png' }); + + sleep(1); + } + finally { + + await page.close(); + + } +} diff --git a/tests/Dfe.EarlyYearsQualification.LoadTests/config/load.json b/tests/Dfe.EarlyYearsQualification.LoadTests/config/load.json new file mode 100644 index 000000000..514f7fab6 --- /dev/null +++ b/tests/Dfe.EarlyYearsQualification.LoadTests/config/load.json @@ -0,0 +1,44 @@ +{ + "thresholds": { + "http_req_failed": [ + "rate<0.01" + ], + "http_req_duration": [ + "p(90) < 400", + "p(95) < 1000" + ] + }, + "scenarios": { + "end_to_end": { + "executor": "ramping-vus", + "stages": [ + { + "duration": "8m", + "target": 40 + }, + { + "duration": "2m", + "target": 40 + }, + { + "duration": "8m", + "target": 80 + }, + { + "duration": "2m", + "target": 80 + }, + { + "duration": "5m", + "target": 8 + }, + { + "duration": "5m", + "target": 0 + } + ], + "gracefulStop": "5m", + "gracefulRampDown": "5m" + } + } +} \ No newline at end of file diff --git a/tests/Dfe.EarlyYearsQualification.LoadTests/config/quick.json b/tests/Dfe.EarlyYearsQualification.LoadTests/config/quick.json new file mode 100644 index 000000000..b689352e9 --- /dev/null +++ b/tests/Dfe.EarlyYearsQualification.LoadTests/config/quick.json @@ -0,0 +1,4 @@ +{ + "vus": 5, + "duration": "15s" +} \ No newline at end of file diff --git a/tests/Dfe.EarlyYearsQualification.LoadTests/http-start-page.js b/tests/Dfe.EarlyYearsQualification.LoadTests/http-start-page.js new file mode 100644 index 000000000..3a063da69 --- /dev/null +++ b/tests/Dfe.EarlyYearsQualification.LoadTests/http-start-page.js @@ -0,0 +1,79 @@ +import http from 'k6/http'; +import { sleep, check } from 'k6'; + +export const options = { + + // A number specifying the number of VUs to run concurrently. + vus: 10, + + // A string specifying the total duration of the test run. + duration: '10s', + + challengeKey: __ENV.CHALLENGE_PASSWORD, // secret value, passed in from the CLI + customDomain: __ENV.CUSTOM_DOMAIN // custom domain, the address of the service to test, passed in from the CLI + + // The following section contains configuration options for execution of this + // test script in Grafana Cloud. + // + // See https://grafana.com/docs/grafana-cloud/k6/get-started/run-cloud-tests-from-the-cli/ + // to learn about authoring and running k6 test scripts in Grafana k6 Cloud. + // + // cloud: { + // // The ID of the project to which the test is assigned in the k6 Cloud UI. + // // By default tests are executed in default project. + // projectID: "", + // // The name of the test in the k6 Cloud UI. + // // Test runs with the same name will be grouped. + // name: "main.js" + // }, + + // Uncomment this section to enable the use of Browser API in your tests. + // + // See https://grafana.com/docs/k6/latest/using-k6-browser/running-browser-tests/ to learn more + // about using Browser API in your test scripts. + // + // scenarios: { + // // The scenario name appears in the result summary, tags, and so on. + // // You can give the scenario any name, as long as each name in the script is unique. + // ui: { + // // Executor is a mandatory parameter for browser-based tests. + // // Shared iterations in this case tells k6 to reuse VUs to execute iterations. + // // + // // See https://grafana.com/docs/k6/latest/using-k6/scenarios/executors/ for other executor types. + // executor: 'shared-iterations', + // options: { + // browser: { + // // This is a mandatory parameter that instructs k6 to launch and + // // connect to a chromium-based browser, and use it to run UI-based + // // tests. + // type: 'chromium', + // }, + // }, + // }, + // } +}; + +// The function that defines VU logic. +// +// See https://grafana.com/docs/k6/latest/examples/get-started-with-k6/ to learn more +// about authoring k6 scripts. +// +export default async function () { + + const address = 'https://' + options.customDomain + '/'; + + const cookieJar = http.cookieJar(); + + cookieJar.set(address, 'auth-secret', options.challengeKey); + + const resp = http.get(address); + + const cookies = cookieJar.cookiesForURL(address); + + check(resp, { + "has cookie 'auth-secret'": (r) => cookies['auth-secret'].length > 0, + 'cookie has expected value': (r) => cookies['auth-secret'][0] === options.challengeKey + }) + + sleep(1); +} diff --git a/tests/Dfe.EarlyYearsQualification.LoadTests/run-tests.js b/tests/Dfe.EarlyYearsQualification.LoadTests/run-tests.js new file mode 100644 index 000000000..bc90ea846 --- /dev/null +++ b/tests/Dfe.EarlyYearsQualification.LoadTests/run-tests.js @@ -0,0 +1,51 @@ +import { fail } from "k6"; + +// test options set scenario: currently "load" and "quick" are supported; defaults to "quick" +const allowedOptionsSets = ['load', 'quick']; + +// Parse __ENV +const ENVIRONMENT = { + password: __ENV.CHALLENGE_PASSWORD, // secret value, passed in from the CLI + customDomain: __ENV.CUSTOM_DOMAIN, // custom domain, the address of the service to test, passed in from the CLI + optionsSet: __ENV.OPTIONS_SET, + jsonResult: __ENV.JSON_RESULT +}; + +if (!ENVIRONMENT.optionsSet) { + ENVIRONMENT.optionsSet = 'quick'; +} + +if (!allowedOptionsSets.includes(ENVIRONMENT.optionsSet)) { + fail('Environment OPTIONS_SET must be "load" or "quick"'); +} + +import level3Journey from "./tests/recorded-l3-journey.js"; + +// Add tests to run to this array +let TESTS = [level3Journey]; + +// Load test options +let optionsFile = `./config/${ENVIRONMENT.optionsSet}.json`; + +export const options = JSON.parse(open(optionsFile)); + +// Data for tests +const DATA = {} + +export default function main() { + + [...TESTS].forEach(t => { t(ENVIRONMENT, DATA); }); + +} + +// This function is called automatically by K6 after a test run. Produces a test report in the location specified +export function handleSummary(data) { + if (!ENVIRONMENT.jsonResult) { + return; + } + + console.log('Preparing the end-of-test summary...'); + return { + "./TestResults/k6-testResults.json": JSON.stringify(data) + }; +} \ No newline at end of file diff --git a/tests/Dfe.EarlyYearsQualification.LoadTests/tests/recorded-l3-journey.js b/tests/Dfe.EarlyYearsQualification.LoadTests/tests/recorded-l3-journey.js new file mode 100644 index 000000000..6d5de3e0a --- /dev/null +++ b/tests/Dfe.EarlyYearsQualification.LoadTests/tests/recorded-l3-journey.js @@ -0,0 +1,1919 @@ +// Adapted from recordings created by Grafana k6 Browser Recorder 1.0.4 +// A recorded level-3 qualification journey, end-to-end, where the qualification is found + +import { sleep, group, check } from 'k6'; +import http from 'k6/http'; + +import { getRequestVerificationTokenValue } from './support/requestVerificationToken.js'; + +export default function level3Journey(ENVIRONMENT, DATA) { + + let response; + + const cookieJar = http.cookieJar(); + const address = 'https://' + ENVIRONMENT.customDomain; + + cookieJar.set(address, 'auth-secret', ENVIRONMENT.password); + + let antiForgeryToken; + let requestVerificationToken; + + group( + `page_1 - ${address}/`, + function () { + response = http.get(`${address}/`, { + headers: { + 'upgrade-insecure-requests': '1', + 'user-agent': + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36', + 'sec-ch-ua': '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"', + 'sec-ch-ua-mobile': '?0', + 'sec-ch-ua-platform': '"Windows"', + }, + }) + + const cookies = cookieJar.cookiesForURL(address); + + antiForgeryToken = cookies['.AspNetCore.Antiforgery'][0]; + + requestVerificationToken = getRequestVerificationTokenValue(response); + + check(response, { + "has cookie 'auth-secret'": (r) => cookies['auth-secret'].length > 0, + "'auth-secret' cookie has expected value": (r) => cookies['auth-secret'][0] === ENVIRONMENT.password, + 'anti-forgery token has value': (r) => antiForgeryToken && antiForgeryToken.length > 0, + 'request not challenged': (r) => !r.url.includes('challenge'), + 'status 200': (r) => r.status == 200 + }); + + response = http.get( + `${address}/lib/bootstrap/dist/css/bootstrap.min.css`, + { + headers: { + referer: '', + 'user-agent': + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36', + 'sec-ch-ua': '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"', + 'sec-ch-ua-mobile': '?0', + 'sec-ch-ua-platform': '"Windows"', + }, + } + ) + response = http.get( + `${address}/govuk/all.min.css`, + { + headers: { + referer: '', + 'user-agent': + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36', + 'sec-ch-ua': '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"', + 'sec-ch-ua-mobile': '?0', + 'sec-ch-ua-platform': '"Windows"', + }, + } + ) + response = http.get( + `${address}/css/site.css`, + { + headers: { + referer: '', + 'user-agent': + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36', + 'sec-ch-ua': '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"', + 'sec-ch-ua-mobile': '?0', + 'sec-ch-ua-platform': '"Windows"', + }, + } + ) + response = http.get( + `${address}/lib/jquery/dist/jquery.min.js`, + { + headers: { + referer: '', + 'user-agent': + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36', + 'sec-ch-ua': '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"', + 'sec-ch-ua-mobile': '?0', + 'sec-ch-ua-platform': '"Windows"', + }, + } + ) + response = http.get( + `${address}/lib/bootstrap/dist/js/bootstrap.bundle.min.js`, + { + headers: { + referer: '', + 'user-agent': + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36', + 'sec-ch-ua': '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"', + 'sec-ch-ua-mobile': '?0', + 'sec-ch-ua-platform': '"Windows"', + }, + } + ) + response = http.get( + `${address}/js/site.js`, + { + headers: { + referer: '', + 'user-agent': + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36', + 'sec-ch-ua': '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"', + 'sec-ch-ua-mobile': '?0', + 'sec-ch-ua-platform': '"Windows"', + }, + } + ) + response = http.get( + `${address}/govuk/all.min.js`, + { + headers: { + origin: address, + referer: '', + 'user-agent': + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36', + 'sec-ch-ua': '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"', + 'sec-ch-ua-mobile': '?0', + 'sec-ch-ua-platform': '"Windows"', + }, + } + ) + response = http.get( + `${address}/assets/fonts/light-94a07e06a1-v2.woff2`, + { + headers: { + origin: address, + referer: + '${address}/govuk/all.min.css?v=GgJd433hcU0_9bAVW6i2iBx3ytWDyVLKH-9vfxG4UxI', + 'user-agent': + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36', + 'sec-ch-ua': '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"', + 'sec-ch-ua-mobile': '?0', + 'sec-ch-ua-platform': '"Windows"', + }, + } + ) + response = http.get( + `${address}/assets/images/govuk-crest.png`, + { + headers: { + referer: + '${address}/govuk/all.min.css?v=GgJd433hcU0_9bAVW6i2iBx3ytWDyVLKH-9vfxG4UxI', + 'user-agent': + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36', + 'sec-ch-ua': '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"', + 'sec-ch-ua-mobile': '?0', + 'sec-ch-ua-platform': '"Windows"', + }, + } + ) + response = http.get( + `${address}/assets/fonts/bold-b542beb274-v2.woff2`, + { + headers: { + origin: address, + referer: + '${address}/govuk/all.min.css?v=GgJd433hcU0_9bAVW6i2iBx3ytWDyVLKH-9vfxG4UxI', + 'user-agent': + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36', + 'sec-ch-ua': '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"', + 'sec-ch-ua-mobile': '?0', + 'sec-ch-ua-platform': '"Windows"', + }, + } + ) + response = http.get( + `${address}/govuk/all.min.js`, + { + headers: { + origin: address, + referer: '', + 'user-agent': + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36', + 'sec-ch-ua': '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"', + 'sec-ch-ua-mobile': '?0', + 'sec-ch-ua-platform': '"Windows"', + }, + } + ) + response = http.get( + `${address}/assets/images/favicon.svg`, + { + headers: { + referer: '', + 'user-agent': + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36', + 'sec-ch-ua': '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"', + 'sec-ch-ua-mobile': '?0', + 'sec-ch-ua-platform': '"Windows"', + }, + } + ) + } + ) + + group( + `page_2 - ${address}/questions/start-new`, + function () { + response = http.get( + `${address}/questions/start-new`, + { + headers: { + 'upgrade-insecure-requests': '1', + 'user-agent': + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36', + 'sec-ch-ua': '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"', + 'sec-ch-ua-mobile': '?0', + 'sec-ch-ua-platform': '"Windows"', + }, + } + ) + + requestVerificationToken = getRequestVerificationTokenValue(response); + + check(response, { + 'request-verification token has value': (r) => requestVerificationToken && requestVerificationToken.length > 0, + 'get status 200': (r) => r.status == 200, + 'get request not challenged': (r) => !r.url.includes('challenge') + }) + + response = http.get( + `${address}/lib/bootstrap/dist/css/bootstrap.min.css`, + { + headers: { + referer: '', + 'user-agent': + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36', + 'sec-ch-ua': '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"', + 'sec-ch-ua-mobile': '?0', + 'sec-ch-ua-platform': '"Windows"', + }, + } + ) + + response = http.get( + `${address}/govuk/all.min.css`, + { + headers: { + referer: '', + 'user-agent': + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36', + 'sec-ch-ua': '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"', + 'sec-ch-ua-mobile': '?0', + 'sec-ch-ua-platform': '"Windows"', + }, + } + ) + + response = http.get( + `${address}/css/site.css`, + { + headers: { + referer: '', + 'user-agent': + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36', + 'sec-ch-ua': '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"', + 'sec-ch-ua-mobile': '?0', + 'sec-ch-ua-platform': '"Windows"', + }, + } + ) + + response = http.get( + `${address}/lib/jquery/dist/jquery.min.js`, + { + headers: { + referer: '', + 'user-agent': + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36', + 'sec-ch-ua': '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"', + 'sec-ch-ua-mobile': '?0', + 'sec-ch-ua-platform': '"Windows"', + }, + } + ) + + response = http.get( + `${address}/lib/bootstrap/dist/js/bootstrap.bundle.min.js`, + { + headers: { + referer: '', + 'user-agent': + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36', + 'sec-ch-ua': '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"', + 'sec-ch-ua-mobile': '?0', + 'sec-ch-ua-platform': '"Windows"', + }, + } + ) + + response = http.get( + `${address}/js/site.js`, + { + headers: { + referer: '', + 'user-agent': + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36', + 'sec-ch-ua': '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"', + 'sec-ch-ua-mobile': '?0', + 'sec-ch-ua-platform': '"Windows"', + }, + } + ) + + response = http.get( + `${address}/govuk/all.min.js`, + { + headers: { + origin: address, + referer: '', + 'user-agent': + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36', + 'sec-ch-ua': '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"', + 'sec-ch-ua-mobile': '?0', + 'sec-ch-ua-platform': '"Windows"', + }, + } + ) + + response = http.get( + `${address}/assets/fonts/light-94a07e06a1-v2.woff2`, + { + headers: { + origin: address, + referer: + `${address}/govuk/all.min.css?v=GgJd433hcU0_9bAVW6i2iBx3ytWDyVLKH-9vfxG4UxI`, + 'user-agent': + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36', + 'sec-ch-ua': '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"', + 'sec-ch-ua-mobile': '?0', + 'sec-ch-ua-platform': '"Windows"', + }, + } + ) + + response = http.get( + `${address}/assets/images/govuk-crest.png`, + { + headers: { + referer: + `${address}/govuk/all.min.css?v=GgJd433hcU0_9bAVW6i2iBx3ytWDyVLKH-9vfxG4UxI`, + 'user-agent': + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36', + 'sec-ch-ua': '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"', + 'sec-ch-ua-mobile': '?0', + 'sec-ch-ua-platform': '"Windows"', + }, + } + ) + + response = http.get( + `${address}/assets/fonts/bold-b542beb274-v2.woff2`, + { + headers: { + origin: address, + referer: + `${address}/govuk/all.min.css?v=GgJd433hcU0_9bAVW6i2iBx3ytWDyVLKH-9vfxG4UxI`, + 'user-agent': + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36', + 'sec-ch-ua': '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"', + 'sec-ch-ua-mobile': '?0', + 'sec-ch-ua-platform': '"Windows"', + }, + } + ) + + response = http.get( + `${address}/govuk/all.min.js`, + { + headers: { + origin: address, + referer: '', + 'user-agent': + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36', + 'sec-ch-ua': '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"', + 'sec-ch-ua-mobile': '?0', + 'sec-ch-ua-platform': '"Windows"', + }, + } + ) + + response = http.get( + `${address}/assets/images/favicon.svg`, + { + headers: { + referer: '', + 'user-agent': + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36', + 'sec-ch-ua': '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"', + 'sec-ch-ua-mobile': '?0', + 'sec-ch-ua-platform': '"Windows"', + }, + } + ) + + response = http.post( + `${address}/questions/where-was-the-qualification-awarded`, + { + Option: 'england', + __RequestVerificationToken: requestVerificationToken, + }, + { + headers: { + 'content-type': 'application/x-www-form-urlencoded', + origin: 'null', + 'upgrade-insecure-requests': '1', + 'user-agent': + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36', + 'sec-ch-ua': '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"', + 'sec-ch-ua-mobile': '?0', + 'sec-ch-ua-platform': '"Windows"', + }, + } + ) + + check(response, { + 'post status 200': (r) => r.status == 200, + 'post request not challenged': (r) => !r.url.includes('challenge') + }); + + requestVerificationToken = getRequestVerificationTokenValue(response); + + response = http.get( + `${address}/lib/bootstrap/dist/css/bootstrap.min.css`, + { + headers: { + referer: '', + 'user-agent': + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36', + 'sec-ch-ua': '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"', + 'sec-ch-ua-mobile': '?0', + 'sec-ch-ua-platform': '"Windows"', + }, + } + ) + + response = http.get( + `${address}/govuk/all.min.css`, + { + headers: { + referer: '', + 'user-agent': + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36', + 'sec-ch-ua': '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"', + 'sec-ch-ua-mobile': '?0', + 'sec-ch-ua-platform': '"Windows"', + }, + } + ) + + response = http.get( + `${address}/css/site.css`, + { + headers: { + referer: '', + 'user-agent': + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36', + 'sec-ch-ua': '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"', + 'sec-ch-ua-mobile': '?0', + 'sec-ch-ua-platform': '"Windows"', + }, + } + ) + + response = http.get( + `${address}/lib/jquery/dist/jquery.min.js`, + { + headers: { + referer: '', + 'user-agent': + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36', + 'sec-ch-ua': '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"', + 'sec-ch-ua-mobile': '?0', + 'sec-ch-ua-platform': '"Windows"', + }, + } + ) + + response = http.get( + `${address}/lib/bootstrap/dist/js/bootstrap.bundle.min.js`, + { + headers: { + referer: '', + 'user-agent': + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36', + 'sec-ch-ua': '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"', + 'sec-ch-ua-mobile': '?0', + 'sec-ch-ua-platform': '"Windows"', + }, + } + ) + + response = http.get( + `${address}/js/site.js`, + { + headers: { + referer: '', + 'user-agent': + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36', + 'sec-ch-ua': '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"', + 'sec-ch-ua-mobile': '?0', + 'sec-ch-ua-platform': '"Windows"', + }, + } + ) + + response = http.get( + `${address}/govuk/all.min.js`, + { + headers: { + origin: address, + referer: '', + 'user-agent': + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36', + 'sec-ch-ua': '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"', + 'sec-ch-ua-mobile': '?0', + 'sec-ch-ua-platform': '"Windows"', + }, + } + ) + + response = http.get( + `${address}/assets/fonts/light-94a07e06a1-v2.woff2`, + { + headers: { + origin: address, + referer: + `${address}/govuk/all.min.css?v=GgJd433hcU0_9bAVW6i2iBx3ytWDyVLKH-9vfxG4UxI`, + 'user-agent': + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36', + 'sec-ch-ua': '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"', + 'sec-ch-ua-mobile': '?0', + 'sec-ch-ua-platform': '"Windows"', + }, + } + ) + + response = http.get( + `${address}/assets/fonts/bold-b542beb274-v2.woff2`, + { + headers: { + origin: address, + referer: + `${address}/govuk/all.min.css?v=GgJd433hcU0_9bAVW6i2iBx3ytWDyVLKH-9vfxG4UxI`, + 'user-agent': + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36', + 'sec-ch-ua': '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"', + 'sec-ch-ua-mobile': '?0', + 'sec-ch-ua-platform': '"Windows"', + }, + } + ) + + response = http.get( + `${address}/assets/images/govuk-crest.png`, + { + headers: { + referer: + `${address}/govuk/all.min.css?v=GgJd433hcU0_9bAVW6i2iBx3ytWDyVLKH-9vfxG4UxI`, + 'user-agent': + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36', + 'sec-ch-ua': '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"', + 'sec-ch-ua-mobile': '?0', + 'sec-ch-ua-platform': '"Windows"', + }, + } + ) + + response = http.get( + `${address}/govuk/all.min.js`, + { + headers: { + origin: address, + referer: '', + 'user-agent': + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36', + 'sec-ch-ua': '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"', + 'sec-ch-ua-mobile': '?0', + 'sec-ch-ua-platform': '"Windows"', + }, + } + ) + + response = http.post( + `${address}/questions/when-was-the-qualification-started`, + { + SelectedMonth: '9', + SelectedYear: '2014', + __RequestVerificationToken: requestVerificationToken, + }, + { + headers: { + 'content-type': 'application/x-www-form-urlencoded', + origin: 'null', + 'upgrade-insecure-requests': '1', + 'user-agent': + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36', + 'sec-ch-ua': '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"', + 'sec-ch-ua-mobile': '?0', + 'sec-ch-ua-platform': '"Windows"', + }, + } + ) + + check(response, { + 'post status 200': (r) => r.status == 200, + 'post request not challenged': (r) => !r.url.includes('challenge') + }); + + requestVerificationToken = getRequestVerificationTokenValue(response); + + response = http.get( + `${address}/lib/bootstrap/dist/css/bootstrap.min.css`, + { + headers: { + referer: '', + 'user-agent': + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36', + 'sec-ch-ua': '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"', + 'sec-ch-ua-mobile': '?0', + 'sec-ch-ua-platform': '"Windows"', + }, + } + ) + + response = http.get( + `${address}/govuk/all.min.css`, + { + headers: { + referer: '', + 'user-agent': + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36', + 'sec-ch-ua': '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"', + 'sec-ch-ua-mobile': '?0', + 'sec-ch-ua-platform': '"Windows"', + }, + } + ) + + response = http.get( + `${address}/css/site.css`, + { + headers: { + referer: '', + 'user-agent': + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36', + 'sec-ch-ua': '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"', + 'sec-ch-ua-mobile': '?0', + 'sec-ch-ua-platform': '"Windows"', + }, + } + ) + + response = http.get( + `${address}/lib/jquery/dist/jquery.min.js`, + { + headers: { + referer: '', + 'user-agent': + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36', + 'sec-ch-ua': '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"', + 'sec-ch-ua-mobile': '?0', + 'sec-ch-ua-platform': '"Windows"', + }, + } + ) + + response = http.get( + `${address}/lib/bootstrap/dist/js/bootstrap.bundle.min.js`, + { + headers: { + referer: '', + 'user-agent': + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36', + 'sec-ch-ua': '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"', + 'sec-ch-ua-mobile': '?0', + 'sec-ch-ua-platform': '"Windows"', + }, + } + ) + + response = http.get( + `${address}/js/site.js`, + { + headers: { + referer: '', + 'user-agent': + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36', + 'sec-ch-ua': '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"', + 'sec-ch-ua-mobile': '?0', + 'sec-ch-ua-platform': '"Windows"', + }, + } + ) + + response = http.get( + `${address}/govuk/all.min.js`, + { + headers: { + origin: address, + referer: '', + 'user-agent': + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36', + 'sec-ch-ua': '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"', + 'sec-ch-ua-mobile': '?0', + 'sec-ch-ua-platform': '"Windows"', + }, + } + ) + + response = http.get( + `${address}/assets/fonts/light-94a07e06a1-v2.woff2`, + { + headers: { + origin: address, + referer: + `${address}/govuk/all.min.css?v=GgJd433hcU0_9bAVW6i2iBx3ytWDyVLKH-9vfxG4UxI`, + 'user-agent': + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36', + 'sec-ch-ua': '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"', + 'sec-ch-ua-mobile': '?0', + 'sec-ch-ua-platform': '"Windows"', + }, + } + ) + + response = http.get( + `${address}/assets/fonts/bold-b542beb274-v2.woff2`, + { + headers: { + origin: address, + referer: + `${address}/govuk/all.min.css?v=GgJd433hcU0_9bAVW6i2iBx3ytWDyVLKH-9vfxG4UxI`, + 'user-agent': + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36', + 'sec-ch-ua': '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"', + 'sec-ch-ua-mobile': '?0', + 'sec-ch-ua-platform': '"Windows"', + }, + } + ) + + response = http.get( + `${address}/assets/images/govuk-crest.png`, + { + headers: { + referer: + `${address}/govuk/all.min.css?v=GgJd433hcU0_9bAVW6i2iBx3ytWDyVLKH-9vfxG4UxI`, + 'user-agent': + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36', + 'sec-ch-ua': '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"', + 'sec-ch-ua-mobile': '?0', + 'sec-ch-ua-platform': '"Windows"', + }, + } + ) + + response = http.get( + `${address}/govuk/all.min.js`, + { + headers: { + origin: address, + referer: '', + 'user-agent': + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36', + 'sec-ch-ua': '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"', + 'sec-ch-ua-mobile': '?0', + 'sec-ch-ua-platform': '"Windows"', + }, + } + ) + + response = http.post( + `${address}/questions/what-level-is-the-qualification`, + { + Option: '3', + __RequestVerificationToken: requestVerificationToken, + }, + { + headers: { + 'content-type': 'application/x-www-form-urlencoded', + origin: 'null', + 'upgrade-insecure-requests': '1', + 'user-agent': + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36', + 'sec-ch-ua': '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"', + 'sec-ch-ua-mobile': '?0', + 'sec-ch-ua-platform': '"Windows"', + }, + } + ) + + check(response, { + 'post status 200': (r) => r.status == 200, + 'post request not challenged': (r) => !r.url.includes('challenge') + }); + + requestVerificationToken = getRequestVerificationTokenValue(response); + + response = http.get( + `${address}/lib/bootstrap/dist/css/bootstrap.min.css`, + { + headers: { + referer: '', + 'user-agent': + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36', + 'sec-ch-ua': '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"', + 'sec-ch-ua-mobile': '?0', + 'sec-ch-ua-platform': '"Windows"', + }, + } + ) + + response = http.get( + `${address}/govuk/all.min.css`, + { + headers: { + referer: '', + 'user-agent': + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36', + 'sec-ch-ua': '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"', + 'sec-ch-ua-mobile': '?0', + 'sec-ch-ua-platform': '"Windows"', + }, + } + ) + + response = http.get( + `${address}/css/site.css`, + { + headers: { + referer: '', + 'user-agent': + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36', + 'sec-ch-ua': '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"', + 'sec-ch-ua-mobile': '?0', + 'sec-ch-ua-platform': '"Windows"', + }, + } + ) + + response = http.get( + `${address}/lib/jquery/dist/jquery.min.js`, + { + headers: { + referer: '', + 'user-agent': + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36', + 'sec-ch-ua': '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"', + 'sec-ch-ua-mobile': '?0', + 'sec-ch-ua-platform': '"Windows"', + }, + } + ) + + response = http.get( + `${address}/lib/bootstrap/dist/js/bootstrap.bundle.min.js`, + { + headers: { + referer: '', + 'user-agent': + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36', + 'sec-ch-ua': '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"', + 'sec-ch-ua-mobile': '?0', + 'sec-ch-ua-platform': '"Windows"', + }, + } + ) + + response = http.get( + `${address}/js/site.js`, + { + headers: { + referer: '', + 'user-agent': + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36', + 'sec-ch-ua': '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"', + 'sec-ch-ua-mobile': '?0', + 'sec-ch-ua-platform': '"Windows"', + }, + } + ) + + response = http.get( + `${address}/govuk/all.min.js`, + { + headers: { + origin: address, + referer: '', + 'user-agent': + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36', + 'sec-ch-ua': '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"', + 'sec-ch-ua-mobile': '?0', + 'sec-ch-ua-platform': '"Windows"', + }, + } + ) + + response = http.get( + `${address}/assets/fonts/light-94a07e06a1-v2.woff2`, + { + headers: { + origin: address, + referer: + `${address}/govuk/all.min.css?v=GgJd433hcU0_9bAVW6i2iBx3ytWDyVLKH-9vfxG4UxI`, + 'user-agent': + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36', + 'sec-ch-ua': '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"', + 'sec-ch-ua-mobile': '?0', + 'sec-ch-ua-platform': '"Windows"', + }, + } + ) + + response = http.get( + `${address}/assets/fonts/bold-b542beb274-v2.woff2`, + { + headers: { + origin: address, + referer: + `${address}/govuk/all.min.css?v=GgJd433hcU0_9bAVW6i2iBx3ytWDyVLKH-9vfxG4UxI`, + 'user-agent': + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36', + 'sec-ch-ua': '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"', + 'sec-ch-ua-mobile': '?0', + 'sec-ch-ua-platform': '"Windows"', + }, + } + ) + + response = http.get( + `${address}/assets/images/govuk-crest.png`, + { + headers: { + referer: + `${address}/govuk/all.min.css?v=GgJd433hcU0_9bAVW6i2iBx3ytWDyVLKH-9vfxG4UxI`, + 'user-agent': + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36', + 'sec-ch-ua': '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"', + 'sec-ch-ua-mobile': '?0', + 'sec-ch-ua-platform': '"Windows"', + }, + } + ) + + response = http.get( + `${address}/govuk/all.min.js`, + { + headers: { + origin: address, + referer: '', + 'user-agent': + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36', + 'sec-ch-ua': '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"', + 'sec-ch-ua-mobile': '?0', + 'sec-ch-ua-platform': '"Windows"', + }, + } + ) + + response = http.post( + `${address}/questions/what-is-the-awarding-organisation`, + { + SelectedValue: 'NCFE', + __RequestVerificationToken: requestVerificationToken, + NotInTheList: 'false', + }, + { + headers: { + 'content-type': 'application/x-www-form-urlencoded', + origin: 'null', + 'upgrade-insecure-requests': '1', + 'user-agent': + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36', + 'sec-ch-ua': '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"', + 'sec-ch-ua-mobile': '?0', + 'sec-ch-ua-platform': '"Windows"', + }, + } + ) + + check(response, { + 'post status 200': (r) => r.status == 200, + 'post request not challenged': (r) => !r.url.includes('challenge') + }); + + requestVerificationToken = getRequestVerificationTokenValue(response); + + response = http.get( + `${address}/lib/bootstrap/dist/css/bootstrap.min.css`, + { + headers: { + referer: '', + 'user-agent': + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36', + 'sec-ch-ua': '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"', + 'sec-ch-ua-mobile': '?0', + 'sec-ch-ua-platform': '"Windows"', + }, + } + ) + + response = http.get( + `${address}/govuk/all.min.css`, + { + headers: { + referer: '', + 'user-agent': + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36', + 'sec-ch-ua': '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"', + 'sec-ch-ua-mobile': '?0', + 'sec-ch-ua-platform': '"Windows"', + }, + } + ) + + response = http.get( + `${address}/css/site.css`, + { + headers: { + referer: '', + 'user-agent': + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36', + 'sec-ch-ua': '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"', + 'sec-ch-ua-mobile': '?0', + 'sec-ch-ua-platform': '"Windows"', + }, + } + ) + + response = http.get( + `${address}/lib/jquery/dist/jquery.min.js`, + { + headers: { + referer: '', + 'user-agent': + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36', + 'sec-ch-ua': '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"', + 'sec-ch-ua-mobile': '?0', + 'sec-ch-ua-platform': '"Windows"', + }, + } + ) + + response = http.get( + `${address}/lib/bootstrap/dist/js/bootstrap.bundle.min.js`, + { + headers: { + referer: '', + 'user-agent': + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36', + 'sec-ch-ua': '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"', + 'sec-ch-ua-mobile': '?0', + 'sec-ch-ua-platform': '"Windows"', + }, + } + ) + + response = http.get( + `${address}/js/site.js`, + { + headers: { + referer: '', + 'user-agent': + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36', + 'sec-ch-ua': '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"', + 'sec-ch-ua-mobile': '?0', + 'sec-ch-ua-platform': '"Windows"', + }, + } + ) + + response = http.get( + `${address}/govuk/all.min.js`, + { + headers: { + origin: address, + referer: '', + 'user-agent': + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36', + 'sec-ch-ua': '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"', + 'sec-ch-ua-mobile': '?0', + 'sec-ch-ua-platform': '"Windows"', + }, + } + ) + + response = http.get( + `${address}/assets/fonts/light-94a07e06a1-v2.woff2`, + { + headers: { + origin: address, + referer: + `${address}/govuk/all.min.css?v=GgJd433hcU0_9bAVW6i2iBx3ytWDyVLKH-9vfxG4UxI`, + 'user-agent': + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36', + 'sec-ch-ua': '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"', + 'sec-ch-ua-mobile': '?0', + 'sec-ch-ua-platform': '"Windows"', + }, + } + ) + + response = http.get( + `${address}/assets/fonts/bold-b542beb274-v2.woff2`, + { + headers: { + origin: address, + referer: + `${address}/govuk/all.min.css?v=GgJd433hcU0_9bAVW6i2iBx3ytWDyVLKH-9vfxG4UxI`, + 'user-agent': + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36', + 'sec-ch-ua': '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"', + 'sec-ch-ua-mobile': '?0', + 'sec-ch-ua-platform': '"Windows"', + }, + } + ) + + response = http.get( + `${address}/assets/images/govuk-crest.png`, + { + headers: { + referer: + `${address}/govuk/all.min.css?v=GgJd433hcU0_9bAVW6i2iBx3ytWDyVLKH-9vfxG4UxI`, + 'user-agent': + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36', + 'sec-ch-ua': '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"', + 'sec-ch-ua-mobile': '?0', + 'sec-ch-ua-platform': '"Windows"', + }, + } + ) + + response = http.get( + `${address}/govuk/all.min.js`, + { + headers: { + origin: address, + referer: '', + 'user-agent': + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36', + 'sec-ch-ua': '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"', + 'sec-ch-ua-mobile': '?0', + 'sec-ch-ua-platform': '"Windows"', + }, + } + ) + } + ) + + group( + `page_3 - ${address}/confirm-qualification/EYQ-224`, + function () { + response = http.get( + `${address}/confirm-qualification/EYQ-224`, + { + headers: { + 'upgrade-insecure-requests': '1', + 'user-agent': + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36', + 'sec-ch-ua': '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"', + 'sec-ch-ua-mobile': '?0', + 'sec-ch-ua-platform': '"Windows"', + }, + } + ) + + check(response, { + 'get status 200': (r) => r.status == 200, + 'get request not challenged': (r) => !r.url.includes('challenge') + }) + + requestVerificationToken = getRequestVerificationTokenValue(response); + + response = http.get( + `${address}/lib/bootstrap/dist/css/bootstrap.min.css`, + { + headers: { + referer: '', + 'user-agent': + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36', + 'sec-ch-ua': '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"', + 'sec-ch-ua-mobile': '?0', + 'sec-ch-ua-platform': '"Windows"', + }, + } + ) + response = http.get( + `${address}/govuk/all.min.css`, + { + headers: { + referer: '', + 'user-agent': + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36', + 'sec-ch-ua': '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"', + 'sec-ch-ua-mobile': '?0', + 'sec-ch-ua-platform': '"Windows"', + }, + } + ) + response = http.get( + `${address}/css/site.css`, + { + headers: { + referer: '', + 'user-agent': + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36', + 'sec-ch-ua': '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"', + 'sec-ch-ua-mobile': '?0', + 'sec-ch-ua-platform': '"Windows"', + }, + } + ) + response = http.get( + `${address}/lib/jquery/dist/jquery.min.js`, + { + headers: { + referer: '', + 'user-agent': + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36', + 'sec-ch-ua': '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"', + 'sec-ch-ua-mobile': '?0', + 'sec-ch-ua-platform': '"Windows"', + }, + } + ) + response = http.get( + `${address}/lib/bootstrap/dist/js/bootstrap.bundle.min.js`, + { + headers: { + referer: '', + 'user-agent': + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36', + 'sec-ch-ua': '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"', + 'sec-ch-ua-mobile': '?0', + 'sec-ch-ua-platform': '"Windows"', + }, + } + ) + response = http.get( + `${address}/js/site.js`, + { + headers: { + referer: '', + 'user-agent': + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36', + 'sec-ch-ua': '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"', + 'sec-ch-ua-mobile': '?0', + 'sec-ch-ua-platform': '"Windows"', + }, + } + ) + response = http.get( + `${address}/govuk/all.min.js`, + { + headers: { + origin: address, + referer: '', + 'user-agent': + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36', + 'sec-ch-ua': '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"', + 'sec-ch-ua-mobile': '?0', + 'sec-ch-ua-platform': '"Windows"', + }, + } + ) + response = http.get( + `${address}/assets/fonts/light-94a07e06a1-v2.woff2`, + { + headers: { + origin: address, + referer: + `${address}/govuk/all.min.css?v=GgJd433hcU0_9bAVW6i2iBx3ytWDyVLKH-9vfxG4UxI`, + 'user-agent': + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36', + 'sec-ch-ua': '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"', + 'sec-ch-ua-mobile': '?0', + 'sec-ch-ua-platform': '"Windows"', + }, + } + ) + response = http.get( + `${address}/assets/fonts/bold-b542beb274-v2.woff2`, + { + headers: { + origin: address, + referer: + `${address}/govuk/all.min.css?v=GgJd433hcU0_9bAVW6i2iBx3ytWDyVLKH-9vfxG4UxI`, + 'user-agent': + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36', + 'sec-ch-ua': '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"', + 'sec-ch-ua-mobile': '?0', + 'sec-ch-ua-platform': '"Windows"', + }, + } + ) + response = http.get( + `${address}/assets/images/govuk-crest.png`, + { + headers: { + referer: + `${address}/govuk/all.min.css?v=GgJd433hcU0_9bAVW6i2iBx3ytWDyVLKH-9vfxG4UxI`, + 'user-agent': + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36', + 'sec-ch-ua': '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"', + 'sec-ch-ua-mobile': '?0', + 'sec-ch-ua-platform': '"Windows"', + }, + } + ) + response = http.get( + `${address}/govuk/all.min.js`, + { + headers: { + origin: address, + referer: '', + 'user-agent': + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36', + 'sec-ch-ua': '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"', + 'sec-ch-ua-mobile': '?0', + 'sec-ch-ua-platform': '"Windows"', + }, + } + ) + } + ) + + group( + `page_4 - ${address}/confirm-qualification`, + function () { + response = http.post( + `${address}/confirm-qualification`, + { + qualificationId: 'EYQ-224', + ConfirmQualificationAnswer: 'yes', + __RequestVerificationToken: requestVerificationToken, + }, + { + headers: { + 'content-type': 'application/x-www-form-urlencoded', + origin: 'null', + 'upgrade-insecure-requests': '1', + 'user-agent': + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36', + 'sec-ch-ua': '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"', + 'sec-ch-ua-mobile': '?0', + 'sec-ch-ua-platform': '"Windows"', + }, + } + ) + + check(response, { + 'post status 200': (r) => r.status == 200, + 'post request not challenged': (r) => !r.url.includes('challenge') + }); + + requestVerificationToken = getRequestVerificationTokenValue(response); + + response = http.get( + `${address}/lib/bootstrap/dist/css/bootstrap.min.css`, + { + headers: { + referer: '', + 'user-agent': + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36', + 'sec-ch-ua': '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"', + 'sec-ch-ua-mobile': '?0', + 'sec-ch-ua-platform': '"Windows"', + }, + } + ) + + response = http.get( + `${address}/govuk/all.min.css`, + { + headers: { + referer: '', + 'user-agent': + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36', + 'sec-ch-ua': '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"', + 'sec-ch-ua-mobile': '?0', + 'sec-ch-ua-platform': '"Windows"', + }, + } + ) + + response = http.get( + `${address}/css/site.css`, + { + headers: { + referer: '', + 'user-agent': + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36', + 'sec-ch-ua': '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"', + 'sec-ch-ua-mobile': '?0', + 'sec-ch-ua-platform': '"Windows"', + }, + } + ) + + response = http.get( + `${address}/lib/jquery/dist/jquery.min.js`, + { + headers: { + referer: '', + 'user-agent': + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36', + 'sec-ch-ua': '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"', + 'sec-ch-ua-mobile': '?0', + 'sec-ch-ua-platform': '"Windows"', + }, + } + ) + + response = http.get( + `${address}/lib/bootstrap/dist/js/bootstrap.bundle.min.js`, + { + headers: { + referer: '', + 'user-agent': + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36', + 'sec-ch-ua': '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"', + 'sec-ch-ua-mobile': '?0', + 'sec-ch-ua-platform': '"Windows"', + }, + } + ) + + response = http.get( + `${address}/js/site.js`, + { + headers: { + referer: '', + 'user-agent': + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36', + 'sec-ch-ua': '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"', + 'sec-ch-ua-mobile': '?0', + 'sec-ch-ua-platform': '"Windows"', + }, + } + ) + + response = http.get( + `${address}/govuk/all.min.js`, + { + headers: { + origin: address, + referer: '', + 'user-agent': + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36', + 'sec-ch-ua': '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"', + 'sec-ch-ua-mobile': '?0', + 'sec-ch-ua-platform': '"Windows"', + }, + } + ) + + response = http.get( + `${address}/assets/fonts/light-94a07e06a1-v2.woff2`, + { + headers: { + origin: address, + referer: + `${address}/govuk/all.min.css?v=GgJd433hcU0_9bAVW6i2iBx3ytWDyVLKH-9vfxG4UxI`, + 'user-agent': + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36', + 'sec-ch-ua': '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"', + 'sec-ch-ua-mobile': '?0', + 'sec-ch-ua-platform': '"Windows"', + }, + } + ) + + response = http.get( + `${address}/assets/fonts/bold-b542beb274-v2.woff2`, + { + headers: { + origin: address, + referer: + `${address}/govuk/all.min.css?v=GgJd433hcU0_9bAVW6i2iBx3ytWDyVLKH-9vfxG4UxI`, + 'user-agent': + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36', + 'sec-ch-ua': '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"', + 'sec-ch-ua-mobile': '?0', + 'sec-ch-ua-platform': '"Windows"', + }, + } + ) + + response = http.get( + `${address}/assets/images/govuk-crest.png`, + { + headers: { + referer: + `${address}/govuk/all.min.css?v=GgJd433hcU0_9bAVW6i2iBx3ytWDyVLKH-9vfxG4UxI`, + 'user-agent': + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36', + 'sec-ch-ua': '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"', + 'sec-ch-ua-mobile': '?0', + 'sec-ch-ua-platform': '"Windows"', + }, + } + ) + + response = http.get( + `${address}/govuk/all.min.js`, + { + headers: { + origin: address, + referer: '', + 'user-agent': + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36', + 'sec-ch-ua': '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"', + 'sec-ch-ua-mobile': '?0', + 'sec-ch-ua-platform': '"Windows"', + }, + } + ) + + response = http.post( + `${address}/qualifications/check-additional-questions/EYQ-224/1`, + { + qualificationId: 'EYQ-224', + questionIndex: '1', + question: "Does the qualification name include 'Early Years Educator'?", + Answer: 'yes', + __RequestVerificationToken: requestVerificationToken, + }, + { + headers: { + 'content-type': 'application/x-www-form-urlencoded', + origin: 'null', + 'upgrade-insecure-requests': '1', + 'user-agent': + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36', + 'sec-ch-ua': '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"', + 'sec-ch-ua-mobile': '?0', + 'sec-ch-ua-platform': '"Windows"', + }, + } + ) + + check(response, { + 'post status 200': (r) => r.status == 200, + 'post request not challenged': (r) => !r.url.includes('challenge') + }); + + requestVerificationToken = getRequestVerificationTokenValue(response); + + response = http.get( + `${address}/lib/bootstrap/dist/css/bootstrap.min.css`, + { + headers: { + referer: '', + 'user-agent': + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36', + 'sec-ch-ua': '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"', + 'sec-ch-ua-mobile': '?0', + 'sec-ch-ua-platform': '"Windows"', + }, + } + ) + + response = http.get( + `${address}/govuk/all.min.css`, + { + headers: { + referer: '', + 'user-agent': + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36', + 'sec-ch-ua': '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"', + 'sec-ch-ua-mobile': '?0', + 'sec-ch-ua-platform': '"Windows"', + }, + } + ) + + response = http.get( + `${address}/css/site.css`, + { + headers: { + referer: '', + 'user-agent': + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36', + 'sec-ch-ua': '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"', + 'sec-ch-ua-mobile': '?0', + 'sec-ch-ua-platform': '"Windows"', + }, + } + ) + + response = http.get( + `${address}/lib/jquery/dist/jquery.min.js`, + { + headers: { + referer: '', + 'user-agent': + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36', + 'sec-ch-ua': '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"', + 'sec-ch-ua-mobile': '?0', + 'sec-ch-ua-platform': '"Windows"', + }, + } + ) + + response = http.get( + `${address}/lib/bootstrap/dist/js/bootstrap.bundle.min.js`, + { + headers: { + referer: '', + 'user-agent': + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36', + 'sec-ch-ua': '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"', + 'sec-ch-ua-mobile': '?0', + 'sec-ch-ua-platform': '"Windows"', + }, + } + ) + + response = http.get( + `${address}/js/site.js`, + { + headers: { + referer: '', + 'user-agent': + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36', + 'sec-ch-ua': '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"', + 'sec-ch-ua-mobile': '?0', + 'sec-ch-ua-platform': '"Windows"', + }, + } + ) + + response = http.get( + `${address}/govuk/all.min.js`, + { + headers: { + origin: address, + referer: '', + 'user-agent': + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36', + 'sec-ch-ua': '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"', + 'sec-ch-ua-mobile': '?0', + 'sec-ch-ua-platform': '"Windows"', + }, + } + ) + + response = http.get( + `${address}/assets/fonts/light-94a07e06a1-v2.woff2`, + { + headers: { + origin: address, + referer: + `${address}/govuk/all.min.css?v=GgJd433hcU0_9bAVW6i2iBx3ytWDyVLKH-9vfxG4UxI`, + 'user-agent': + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36', + 'sec-ch-ua': '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"', + 'sec-ch-ua-mobile': '?0', + 'sec-ch-ua-platform': '"Windows"', + }, + } + ) + + response = http.get( + `${address}/assets/fonts/bold-b542beb274-v2.woff2`, + { + headers: { + origin: address, + referer: + `${address}/govuk/all.min.css?v=GgJd433hcU0_9bAVW6i2iBx3ytWDyVLKH-9vfxG4UxI`, + 'user-agent': + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36', + 'sec-ch-ua': '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"', + 'sec-ch-ua-mobile': '?0', + 'sec-ch-ua-platform': '"Windows"', + }, + } + ) + + response = http.get( + `${address}/assets/images/govuk-crest.png`, + { + headers: { + referer: + `${address}/govuk/all.min.css?v=GgJd433hcU0_9bAVW6i2iBx3ytWDyVLKH-9vfxG4UxI`, + 'user-agent': + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36', + 'sec-ch-ua': '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"', + 'sec-ch-ua-mobile': '?0', + 'sec-ch-ua-platform': '"Windows"', + }, + } + ) + + response = http.get( + `${address}/govuk/all.min.js`, + { + headers: { + origin: address, + referer: '', + 'user-agent': + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36', + 'sec-ch-ua': '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"', + 'sec-ch-ua-mobile': '?0', + 'sec-ch-ua-platform': '"Windows"', + }, + } + ) + } + ) + + group( + `page_5 - ${address}/qualifications/qualification-details/EYQ-224`, + function () { + response = http.get( + `${address}/qualifications/qualification-details/EYQ-224`, + { + headers: { + 'upgrade-insecure-requests': '1', + 'user-agent': + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36', + 'sec-ch-ua': '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"', + 'sec-ch-ua-mobile': '?0', + 'sec-ch-ua-platform': '"Windows"', + }, + } + ) + + requestVerificationToken = getRequestVerificationTokenValue(response); + + response = http.get( + `${address}/lib/bootstrap/dist/css/bootstrap.min.css`, + { + headers: { + referer: '', + 'user-agent': + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36', + 'sec-ch-ua': '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"', + 'sec-ch-ua-mobile': '?0', + 'sec-ch-ua-platform': '"Windows"', + }, + } + ) + response = http.get( + `${address}/govuk/all.min.css`, + { + headers: { + referer: '', + 'user-agent': + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36', + 'sec-ch-ua': '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"', + 'sec-ch-ua-mobile': '?0', + 'sec-ch-ua-platform': '"Windows"', + }, + } + ) + response = http.get( + `${address}/css/site.css`, + { + headers: { + referer: '', + 'user-agent': + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36', + 'sec-ch-ua': '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"', + 'sec-ch-ua-mobile': '?0', + 'sec-ch-ua-platform': '"Windows"', + }, + } + ) + response = http.get( + `${address}/lib/jquery/dist/jquery.min.js`, + { + headers: { + referer: '', + 'user-agent': + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36', + 'sec-ch-ua': '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"', + 'sec-ch-ua-mobile': '?0', + 'sec-ch-ua-platform': '"Windows"', + }, + } + ) + response = http.get( + `${address}/lib/bootstrap/dist/js/bootstrap.bundle.min.js`, + { + headers: { + referer: '', + 'user-agent': + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36', + 'sec-ch-ua': '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"', + 'sec-ch-ua-mobile': '?0', + 'sec-ch-ua-platform': '"Windows"', + }, + } + ) + response = http.get( + `${address}/js/site.js`, + { + headers: { + referer: '', + 'user-agent': + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36', + 'sec-ch-ua': '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"', + 'sec-ch-ua-mobile': '?0', + 'sec-ch-ua-platform': '"Windows"', + }, + } + ) + response = http.get( + `${address}/govuk/all.min.js`, + { + headers: { + origin: address, + referer: '', + 'user-agent': + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36', + 'sec-ch-ua': '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"', + 'sec-ch-ua-mobile': '?0', + 'sec-ch-ua-platform': '"Windows"', + }, + } + ) + response = http.get( + `${address}/assets/fonts/light-94a07e06a1-v2.woff2`, + { + headers: { + origin: address, + referer: + `${address}/govuk/all.min.css?v=GgJd433hcU0_9bAVW6i2iBx3ytWDyVLKH-9vfxG4UxI`, + 'user-agent': + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36', + 'sec-ch-ua': '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"', + 'sec-ch-ua-mobile': '?0', + 'sec-ch-ua-platform': '"Windows"', + }, + } + ) + response = http.get( + `${address}/assets/fonts/bold-b542beb274-v2.woff2`, + { + headers: { + origin: address, + referer: + `${address}/govuk/all.min.css?v=GgJd433hcU0_9bAVW6i2iBx3ytWDyVLKH-9vfxG4UxI`, + 'user-agent': + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36', + 'sec-ch-ua': '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"', + 'sec-ch-ua-mobile': '?0', + 'sec-ch-ua-platform': '"Windows"', + }, + } + ) + response = http.get( + `${address}/govuk/all.min.js`, + { + headers: { + origin: address, + referer: '', + 'user-agent': + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36', + 'sec-ch-ua': '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"', + 'sec-ch-ua-mobile': '?0', + 'sec-ch-ua-platform': '"Windows"', + }, + } + ) + response = http.get( + `${address}/assets/images/icon-print.png`, + { + headers: { + referer: '', + 'user-agent': + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36', + 'sec-ch-ua': '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"', + 'sec-ch-ua-mobile': '?0', + 'sec-ch-ua-platform': '"Windows"', + }, + } + ) + response = http.get( + `${address}/assets/images/govuk-crest.png`, + { + headers: { + referer: + `${address}/govuk/all.min.css?v=GgJd433hcU0_9bAVW6i2iBx3ytWDyVLKH-9vfxG4UxI`, + 'user-agent': + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36', + 'sec-ch-ua': '"Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"', + 'sec-ch-ua-mobile': '?0', + 'sec-ch-ua-platform': '"Windows"', + }, + } + ) + } + ) + + sleep(1) +} \ No newline at end of file diff --git a/tests/Dfe.EarlyYearsQualification.LoadTests/tests/support/requestVerificationToken.js b/tests/Dfe.EarlyYearsQualification.LoadTests/tests/support/requestVerificationToken.js new file mode 100644 index 000000000..ae2d3f87d --- /dev/null +++ b/tests/Dfe.EarlyYearsQualification.LoadTests/tests/support/requestVerificationToken.js @@ -0,0 +1,9 @@ + +export function getRequestVerificationTokenValue(response) { + + const requestVerificationTokenInput = response.html().find('input[name=__RequestVerificationToken]'); + + const requestVerificationToken = requestVerificationTokenInput.attr('value'); + + return requestVerificationToken; +} \ No newline at end of file diff --git a/tests/Dfe.EarlyYearsQualification.SmokeTests/playwright/package-lock.json b/tests/Dfe.EarlyYearsQualification.SmokeTests/playwright/package-lock.json new file mode 100644 index 000000000..e24e4cf9d --- /dev/null +++ b/tests/Dfe.EarlyYearsQualification.SmokeTests/playwright/package-lock.json @@ -0,0 +1,97 @@ +{ + "name": "playwright", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "playwright", + "version": "1.0.0", + "license": "ISC", + "devDependencies": { + "@playwright/test": "^1.48.2", + "@types/node": "^22.9.0" + } + }, + "node_modules/@playwright/test": { + "version": "1.48.2", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.48.2.tgz", + "integrity": "sha512-54w1xCWfXuax7dz4W2M9uw0gDyh+ti/0K/MxcCUxChFh37kkdxPdfZDw5QBbuPUJHr1CiHJ1hXgSs+GgeQc5Zw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright": "1.48.2" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@types/node": { + "version": "22.9.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.9.0.tgz", + "integrity": "sha512-vuyHg81vvWA1Z1ELfvLko2c8f34gyA0zaic0+Rllc5lbCnbSyuvb2Oxpm6TAUAC/2xZN3QGqxBNggD1nNR2AfQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.19.8" + } + }, + "node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/playwright": { + "version": "1.48.2", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.48.2.tgz", + "integrity": "sha512-NjYvYgp4BPmiwfe31j4gHLa3J7bD2WiBz8Lk2RoSsmX38SVIARZ18VYjxLjAcDsAhA+F4iSEXTSGgjua0rrlgQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright-core": "1.48.2" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, + "node_modules/playwright-core": { + "version": "1.48.2", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.48.2.tgz", + "integrity": "sha512-sjjw+qrLFlriJo64du+EK0kJgZzoQPsabGF4lBvsid+3CNIZIYLgnMj9V6JY5VhM2Peh20DJWIVpVljLLnlawA==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/undici-types": { + "version": "6.19.8", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", + "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", + "dev": true, + "license": "MIT" + } + } +} diff --git a/tests/Dfe.EarlyYearsQualification.SmokeTests/playwright/package.json b/tests/Dfe.EarlyYearsQualification.SmokeTests/playwright/package.json new file mode 100644 index 000000000..ac4d7f851 --- /dev/null +++ b/tests/Dfe.EarlyYearsQualification.SmokeTests/playwright/package.json @@ -0,0 +1,14 @@ +{ + "name": "playwright", + "version": "1.0.0", + "main": "index.js", + "scripts": {}, + "keywords": [], + "author": "", + "license": "ISC", + "description": "", + "devDependencies": { + "@playwright/test": "^1.48.2", + "@types/node": "^22.9.0" + } +} diff --git a/tests/Dfe.EarlyYearsQualification.SmokeTests/playwright/playwright.config.ts b/tests/Dfe.EarlyYearsQualification.SmokeTests/playwright/playwright.config.ts new file mode 100644 index 000000000..11a608a65 --- /dev/null +++ b/tests/Dfe.EarlyYearsQualification.SmokeTests/playwright/playwright.config.ts @@ -0,0 +1,76 @@ +import { defineConfig, devices } from '@playwright/test'; + +/** + * Read environment variables from file. + * https://github.com/motdotla/dotenv + */ + +/** + * See https://playwright.dev/docs/test-configuration. + */ +export default defineConfig({ + testDir: './tests', + /* Run tests in files in parallel */ + fullyParallel: true, + /* Fail the build on CI if you accidentally left test.only in the source code. */ + forbidOnly: !!process.env.CI, + /* Retry on CI only */ + retries: process.env.CI ? 2 : 0, + /* Opt out of parallel tests on CI. */ + workers: process.env.CI ? 1 : undefined, + /* Reporter to use. See https://playwright.dev/docs/test-reporters */ + reporter: 'html', + /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */ + use: { + /* Base URL to use in actions like `await page.goto('/')`. */ + baseURL: process.env.WEBAPP_URL, + + /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ + trace: 'on-first-retry', + }, + + /* Configure projects for major browsers */ + projects: [ + { + name: 'chromium', + use: { ...devices['Desktop Chrome'] }, + }, + + { + name: 'firefox', + use: { ...devices['Desktop Firefox'] }, + }, + + // { + // name: 'webkit', + // use: { ...devices['Desktop Safari'] }, + // }, + + /* Test against mobile viewports. */ + // { + // name: 'Mobile Chrome', + // use: { ...devices['Pixel 5'] }, + // }, + // { + // name: 'Mobile Safari', + // use: { ...devices['iPhone 12'] }, + // }, + + /* Test against branded browsers. */ + { + name: 'Microsoft Edge', + use: { ...devices['Desktop Edge'], channel: 'msedge' }, + }, + // { + // name: 'Google Chrome', + // use: { ...devices['Desktop Chrome'], channel: 'chrome' }, + // }, + ], + + /* Run your local dev server before starting the tests */ + // webServer: { + // command: 'npm run start', + // url: 'http://127.0.0.1:3000', + // reuseExistingServer: !process.env.CI, + // }, +}); \ No newline at end of file diff --git a/tests/Dfe.EarlyYearsQualification.SmokeTests/playwright/tests/smoke-tests.spec.ts b/tests/Dfe.EarlyYearsQualification.SmokeTests/playwright/tests/smoke-tests.spec.ts new file mode 100644 index 000000000..f2379031a --- /dev/null +++ b/tests/Dfe.EarlyYearsQualification.SmokeTests/playwright/tests/smoke-tests.spec.ts @@ -0,0 +1,50 @@ +import { test, expect } from '@playwright/test'; + +test.describe("A spec used to smoke test the environment once a deployment has happened", () => { + test("should return search results", async ({ page, context }) => { + + // attempt to set cookie and navigate to start page + await context.addCookies([ + { name: 'auth-secret', value: process.env.AUTH_SECRET, path: '/', domain: process.env.WEBAPP_URL } + ]); + + await page.goto("/"); + + //if we end up navigated to the challenge page, then fill in the password and continue + if (page.url().includes("challenge")) { + await page.locator("#PasswordValue").type(process.env.AUTH_SECRET); + await page.locator("#question-submit").click(); + await page.waitForURL("/"); + } + + // home page + await expect(page.locator("#start-now-button")).toBeVisible(); + await page.locator("#start-now-button").click(); + + // where-was-the-qualification-awarded page + await expect(page.url()).toContain("/questions/where-was-the-qualification-awarded"); + await page.locator("#england").click(); + await page.locator("#question-submit").click(); + + // when-was-the-qualification-started page + await expect(page.url()).toContain("/questions/when-was-the-qualification-started"); + await page.locator("#date-started-month").type("7"); + await page.locator("#date-started-year").type("2015"); + await page.locator("#question-submit").click(); + + // what-level-is-the-qualification page + await expect(page.url()).toContain("/questions/what-level-is-the-qualification"); + await page.locator('input[id="0"]').click(); + await page.locator("#question-submit").click(); + + // what-is-the-awarding-organisation page + await expect(page.url()).toContain("/questions/what-is-the-awarding-organisation"); + await page.locator("#awarding-organisation-not-in-list").click(); + await page.locator("#question-submit").click(); + + // qualifications page + await expect(page.url()).toContain("/qualifications"); + // If this shows then no qualifications are getting returned indicating possible issue + await expect(page.locator("#no-result-content")).not.toBeVisible(); + }); +}); diff --git a/tests/Dfe.EarlyYearsQualification.UnitTests/Controllers/AdviceControllerTests.cs b/tests/Dfe.EarlyYearsQualification.UnitTests/Controllers/AdviceControllerTests.cs index b4ecf5520..9ce06de9b 100644 --- a/tests/Dfe.EarlyYearsQualification.UnitTests/Controllers/AdviceControllerTests.cs +++ b/tests/Dfe.EarlyYearsQualification.UnitTests/Controllers/AdviceControllerTests.cs @@ -456,250 +456,64 @@ public async Task QualificationNotOnTheList_ContentServiceReturnsAdvicePage_Retu } [TestMethod] - public async Task QualificationLevel7_ContentServiceReturnsNoAdvicePage_RedirectsToErrorPage() + public async Task Level7QualificationPost2014_ContentServiceReturnsNoAdvicePage_RedirectsToErrorPage() { var mockLogger = new Mock>(); var mockContentService = new Mock(); var mockContentParser = new Mock(); - - var controller = new AdviceController(mockLogger.Object, mockContentService.Object, mockContentParser.Object, - UserJourneyMockNoOp.Object); - - mockContentService.Setup(x => x.GetAdvicePage(AdvicePages.QualificationLevel7)) - .ReturnsAsync((AdvicePage?)default).Verifiable(); - - var result = await controller.QualificationLevel7(); - - result.Should().NotBeNull(); - - var resultType = result as RedirectToActionResult; - - resultType.Should().NotBeNull(); - - resultType!.ActionName.Should().Be("Index"); - resultType.ControllerName.Should().Be("Error"); - - mockLogger.VerifyError("No content for the advice page"); - } - - [TestMethod] - public async Task QualificationLevel7_ContentServiceReturnsAdvicePage_ReturnsAdvicePageModel() - { - var mockLogger = new Mock>(); - var mockContentService = new Mock(); - var mockContentParser = new Mock(); - - var controller = new AdviceController(mockLogger.Object, mockContentService.Object, mockContentParser.Object, - UserJourneyMockNoOp.Object); - - const string renderedHtmlBody = "Test html body (level 7)"; - - var advicePage = new AdvicePage - { Heading = "Heading (level 7)", Body = ContentfulContentHelper.Text("Anything") }; - mockContentService.Setup(x => x.GetAdvicePage(AdvicePages.QualificationLevel7)) - .ReturnsAsync(advicePage); - - mockContentParser.Setup(x => x.ToHtml(It.IsAny())).ReturnsAsync(renderedHtmlBody); - - var result = await controller.QualificationLevel7(); - - result.Should().NotBeNull(); - - var resultType = result as ViewResult; - resultType.Should().NotBeNull(); - - var model = resultType!.Model as AdvicePageModel; - model.Should().NotBeNull(); - - model!.Heading.Should().Be(advicePage.Heading); - model.BodyContent.Should().Be(renderedHtmlBody); - - mockContentParser.Verify(x => x.ToHtml(It.IsAny()), Times.Once); - } - - [TestMethod] - public async Task Level6QualificationPre2014_ContentServiceReturnsNoAdvicePage_RedirectsToErrorPage() - { - var mockLogger = new Mock>(); - var mockContentService = new Mock(); - var mockContentParser = new Mock(); - - var controller = new AdviceController(mockLogger.Object, mockContentService.Object, mockContentParser.Object, - UserJourneyMockNoOp.Object); - - mockContentService.Setup(x => x.GetAdvicePage(AdvicePages.Level6QualificationPre2014)) - .ReturnsAsync((AdvicePage?)default).Verifiable(); - - var result = await controller.Level6QualificationPre2014(); - - result.Should().NotBeNull(); - - var resultType = result as RedirectToActionResult; - - resultType.Should().NotBeNull(); - - resultType!.ActionName.Should().Be("Index"); - resultType.ControllerName.Should().Be("Error"); - - mockLogger.VerifyError("No content for the advice page"); - } - - [TestMethod] - public async Task Level6QualificationPre2014_ContentServiceReturnsAdvicePage_ReturnsAdvicePageModel() - { - var mockLogger = new Mock>(); - var mockContentService = new Mock(); - var mockContentParser = new Mock(); - - var controller = new AdviceController(mockLogger.Object, mockContentService.Object, mockContentParser.Object, - UserJourneyMockNoOp.Object); - - const string renderedHtmlBody = "Test html body (level 6 pre 2014)"; - - var advicePage = new AdvicePage - { Heading = "Heading (level 6 pre 2014)", Body = ContentfulContentHelper.Text("Anything") }; - mockContentService.Setup(x => x.GetAdvicePage(AdvicePages.Level6QualificationPre2014)) - .ReturnsAsync(advicePage); - - mockContentParser.Setup(x => x.ToHtml(It.IsAny())).ReturnsAsync(renderedHtmlBody); - - var result = await controller.Level6QualificationPre2014(); - - result.Should().NotBeNull(); - - var resultType = result as ViewResult; - resultType.Should().NotBeNull(); - - var model = resultType!.Model as AdvicePageModel; - model.Should().NotBeNull(); - - model!.Heading.Should().Be(advicePage.Heading); - model.BodyContent.Should().Be(renderedHtmlBody); - - mockContentParser.Verify(x => x.ToHtml(It.IsAny()), Times.Once); - } - - [TestMethod] - public async Task Level6QualificationPost2014_ContentServiceReturnsNoAdvicePage_RedirectsToErrorPage() - { - var mockLogger = new Mock>(); - var mockContentService = new Mock(); - var mockContentParser = new Mock(); - - var controller = new AdviceController(mockLogger.Object, mockContentService.Object, mockContentParser.Object, - UserJourneyMockNoOp.Object); - - mockContentService.Setup(x => x.GetAdvicePage(AdvicePages.Level6QualificationPost2014)) - .ReturnsAsync((AdvicePage?)default).Verifiable(); - - var result = await controller.Level6QualificationPost2014(); - - result.Should().NotBeNull(); - - var resultType = result as RedirectToActionResult; - - resultType.Should().NotBeNull(); - - resultType!.ActionName.Should().Be("Index"); - resultType.ControllerName.Should().Be("Error"); - - mockLogger.VerifyError("No content for the advice page"); - } - - [TestMethod] - public async Task Level6QualificationPost2014_ContentServiceReturnsAdvicePage_ReturnsAdvicePageModel() - { - var mockLogger = new Mock>(); - var mockContentService = new Mock(); - var mockContentParser = new Mock(); - - var controller = new AdviceController(mockLogger.Object, mockContentService.Object, mockContentParser.Object, - UserJourneyMockNoOp.Object); - - const string renderedHtmlBody = "Test html body (level 6 post 2014)"; - - var advicePage = new AdvicePage - { Heading = "Heading (level 6 post 2014)", Body = ContentfulContentHelper.Text("Anything") }; - mockContentService.Setup(x => x.GetAdvicePage(AdvicePages.Level6QualificationPost2014)) - .ReturnsAsync(advicePage); - - mockContentParser.Setup(x => x.ToHtml(It.IsAny())).ReturnsAsync(renderedHtmlBody); - - var result = await controller.Level6QualificationPost2014(); - - result.Should().NotBeNull(); - - var resultType = result as ViewResult; - resultType.Should().NotBeNull(); - - var model = resultType!.Model as AdvicePageModel; - model.Should().NotBeNull(); - - model!.Heading.Should().Be(advicePage.Heading); - model.BodyContent.Should().Be(renderedHtmlBody); - - mockContentParser.Verify(x => x.ToHtml(It.IsAny()), Times.Once); - } - - [TestMethod] - public async Task PrivacyPolicy_ContentServiceReturnsNoAdvicePage_RedirectsToErrorPage() - { - var mockLogger = new Mock>(); - var mockContentService = new Mock(); - var mockContentParser = new Mock(); - + var controller = new AdviceController(mockLogger.Object, mockContentService.Object, mockContentParser.Object, UserJourneyMockNoOp.Object); - - mockContentService.Setup(x => x.GetAdvicePage(AdvicePages.TemporaryPrivacyPolicy)) + + mockContentService.Setup(x => x.GetAdvicePage(AdvicePages.Level7QualificationPost2014)) .ReturnsAsync((AdvicePage?)default).Verifiable(); - - var result = await controller.PrivacyPolicy(); - + + var result = await controller.Level7QualificationPost2014(); + result.Should().NotBeNull(); - + var resultType = result as RedirectToActionResult; - + resultType.Should().NotBeNull(); - + resultType!.ActionName.Should().Be("Index"); resultType.ControllerName.Should().Be("Error"); - + mockLogger.VerifyError("No content for the advice page"); } - + [TestMethod] - public async Task PrivacyPolicy_ContentServiceReturnsAdvicePage_ReturnsAdvicePageModel() + public async Task Level7QualificationPost2014_ContentServiceReturnsAdvicePage_ReturnsAdvicePageModel() { var mockLogger = new Mock>(); var mockContentService = new Mock(); var mockContentParser = new Mock(); - + var controller = new AdviceController(mockLogger.Object, mockContentService.Object, mockContentParser.Object, UserJourneyMockNoOp.Object); - - const string renderedHtmlBody = "Test html body (Privacy Policy)"; - + + const string renderedHtmlBody = "Test html body (level 7 post 2014)"; + var advicePage = new AdvicePage - { Heading = "Heading (Privacy Policy)", Body = ContentfulContentHelper.Text("Anything") }; - mockContentService.Setup(x => x.GetAdvicePage(AdvicePages.TemporaryPrivacyPolicy)) + { Heading = "Heading (level 7 post 2014)", Body = ContentfulContentHelper.Text("Anything") }; + mockContentService.Setup(x => x.GetAdvicePage(AdvicePages.Level7QualificationPost2014)) .ReturnsAsync(advicePage); - + mockContentParser.Setup(x => x.ToHtml(It.IsAny())).ReturnsAsync(renderedHtmlBody); - - var result = await controller.PrivacyPolicy(); - + + var result = await controller.Level7QualificationPost2014(); + result.Should().NotBeNull(); - + var resultType = result as ViewResult; resultType.Should().NotBeNull(); - + var model = resultType!.Model as AdvicePageModel; model.Should().NotBeNull(); - + model!.Heading.Should().Be(advicePage.Heading); model.BodyContent.Should().Be(renderedHtmlBody); - + mockContentParser.Verify(x => x.ToHtml(It.IsAny()), Times.Once); } } \ No newline at end of file diff --git a/tests/Dfe.EarlyYearsQualification.UnitTests/Controllers/CheckAdditionalRequirementsControllerTests.cs b/tests/Dfe.EarlyYearsQualification.UnitTests/Controllers/CheckAdditionalRequirementsControllerTests.cs index e1c239c83..fc092458d 100644 --- a/tests/Dfe.EarlyYearsQualification.UnitTests/Controllers/CheckAdditionalRequirementsControllerTests.cs +++ b/tests/Dfe.EarlyYearsQualification.UnitTests/Controllers/CheckAdditionalRequirementsControllerTests.cs @@ -1,4 +1,5 @@ using Contentful.Core.Models; +using Dfe.EarlyYearsQualification.Content.Constants; using Dfe.EarlyYearsQualification.Content.Entities; using Dfe.EarlyYearsQualification.Content.RichTextParsing; using Dfe.EarlyYearsQualification.Content.Services.Interfaces; @@ -295,7 +296,7 @@ public async Task Post_ModelStateIsValidAndUnableToFindQualification_RedirectsTo } [TestMethod] - public async Task Post_ModelStateIsValid_RedirectsToQualificationDetails() + public async Task Post_ModelStateIsValid_RedirectsToConfirmAnswers() { var mockLogger = new Mock>(); var mockRepository = new Mock(); @@ -323,6 +324,96 @@ public async Task Post_ModelStateIsValid_RedirectsToQualificationDetails() mockUserJourneyCookieService .Verify(x => x.SetAdditionalQuestionsAnswers(It.IsAny>()), Times.Once); } + + [TestMethod] + public async Task Post_QuestionIsQtsQuestionAndAnswerMatchesAnswerToBeFullAndRelevant_RedirectsToConfirmAnswers() + { + var mockLogger = new Mock>(); + var mockRepository = new Mock(); + var mockContentParser = new Mock(); + var mockContentService = new Mock(); + var mockUserJourneyCookieService = new Mock(); + + var qtsQuestion = new AdditionalRequirementQuestion + { + Sys = new SystemProperties + { + Id = AdditionalRequirementQuestions.QtsQuestion + }, + AnswerToBeFullAndRelevant = true, + Question = "This is the qts question" + }; + var qualification = CreateQualification(new List { qtsQuestion }); + mockRepository.Setup(x => x.GetById("Test-123")) + .ReturnsAsync(qualification); + + var controller = new CheckAdditionalRequirementsController(mockLogger.Object, + mockRepository.Object, + mockContentService.Object, + mockContentParser.Object, + mockUserJourneyCookieService.Object); + + var result = await controller.Post("Test-123", 1, new CheckAdditionalRequirementsPageModel { QualificationId = "Test-123", Answer = "yes" }); + result.Should().NotBeNull(); + + var resultType = result as RedirectToActionResult; + resultType.Should().NotBeNull(); + resultType!.ActionName.Should().Be("ConfirmAnswers"); + resultType.ControllerName.Should().Be("CheckAdditionalRequirements"); + resultType.RouteValues.Should().ContainSingle("qualificationId", "Test-123"); + mockUserJourneyCookieService + .Verify(x => x.SetAdditionalQuestionsAnswers(It.IsAny>()), Times.Exactly(2)); + } + + [TestMethod] + public async Task Post_QuestionIsQtsQuestionAndAnswerDoesntMatchAnswerToBeFullAndRelevant_RedirectsToCheckAdditionalRequirements() + { + var mockLogger = new Mock>(); + var mockRepository = new Mock(); + var mockContentParser = new Mock(); + var mockContentService = new Mock(); + var mockUserJourneyCookieService = new Mock(); + + var questions = new List + { + new() { + Sys = new SystemProperties + { + Id = AdditionalRequirementQuestions.QtsQuestion + }, + AnswerToBeFullAndRelevant = true, + Question = "This is the qts question" + }, + new() { + Sys = new SystemProperties + { + Id = "Another id" + }, + AnswerToBeFullAndRelevant = true, + Question = "This is not a qts question" + } + }; + var qualification = CreateQualification(questions); + mockRepository.Setup(x => x.GetById("Test-123")) + .ReturnsAsync(qualification); + + var controller = new CheckAdditionalRequirementsController(mockLogger.Object, + mockRepository.Object, + mockContentService.Object, + mockContentParser.Object, + mockUserJourneyCookieService.Object); + + var result = await controller.Post("Test-123", 1, new CheckAdditionalRequirementsPageModel { QualificationId = "Test-123", Answer = "no" }); + result.Should().NotBeNull(); + + var resultType = result as RedirectToActionResult; + resultType.Should().NotBeNull(); + resultType!.ControllerName.Should().Be("CheckAdditionalRequirements"); + resultType.RouteValues.Should().Contain("qualificationId", "Test-123"); + resultType.RouteValues.Should().Contain("questionIndex", 2); + mockUserJourneyCookieService + .Verify(x => x.SetAdditionalQuestionsAnswers(It.IsAny>()), Times.Exactly(1)); + } [TestMethod] public async Task Post_UnableToFindQualification_RedirectsToErrorPage() @@ -598,7 +689,7 @@ public async Task ConfirmAnswers_QualHasQuestionsButNoAnswers_RedirectToFirstQue resultType!.ActionName.Should().Be("Index"); resultType.ControllerName.Should().Be("CheckAdditionalRequirements"); resultType.RouteValues.Should().Contain("qualificationId", "Test-123"); - resultType.RouteValues.Should().Contain("questionIndex", 0); + resultType.RouteValues.Should().Contain("questionIndex", 1); } [TestMethod] @@ -639,7 +730,7 @@ public async Task ConfirmAnswers_QualHasQuestionsButNotAllAreAnswered_RedirectTo resultType!.ActionName.Should().Be("Index"); resultType.ControllerName.Should().Be("CheckAdditionalRequirements"); resultType.RouteValues.Should().Contain("qualificationId", "Test-123"); - resultType.RouteValues.Should().Contain("questionIndex", 0); + resultType.RouteValues.Should().Contain("questionIndex", 1); } [TestMethod] diff --git a/tests/Dfe.EarlyYearsQualification.UnitTests/Controllers/ConfirmQualificationControllerTests.cs b/tests/Dfe.EarlyYearsQualification.UnitTests/Controllers/ConfirmQualificationControllerTests.cs index 7a9f14ee3..b2bb61956 100644 --- a/tests/Dfe.EarlyYearsQualification.UnitTests/Controllers/ConfirmQualificationControllerTests.cs +++ b/tests/Dfe.EarlyYearsQualification.UnitTests/Controllers/ConfirmQualificationControllerTests.cs @@ -80,38 +80,7 @@ public async Task Index_CantFindQualification_LogsAndReturnsError() var mockRepository = new Mock(); var mockContentService = new Mock(); - mockContentService.Setup(x => x.GetConfirmQualificationPage()).ReturnsAsync(new ConfirmQualificationPage - { - QualificationLabel = "Test qualification label", - BackButton = new NavigationLink - { - DisplayText = "Test back button", - OpenInNewTab = false, - Href = "/qualifications" - }, - ErrorText = "Test error text", - ButtonText = "Test button text", - LevelLabel = "Test level label", - DateAddedLabel = "Test date added label", - Heading = "Test heading", - Options = - [ - new Option - { - Label = "yes", - Value = "yes" - }, - new Option - { - Label = "no", - Value = "no" - } - ], - RadioHeading = "Test radio heading", - AwardingOrganisationLabel = "Test awarding organisation label", - ErrorBannerHeading = "Test error banner heading", - ErrorBannerLink = "Test error banner link" - }); + mockContentService.Setup(x => x.GetConfirmQualificationPage()).ReturnsAsync(GetConfirmQualificationPageContent()); mockRepository.Setup(x => x.GetById("Some ID")) .ReturnsAsync(default(Qualification?)); @@ -146,38 +115,9 @@ public async Task Index_PageDetailsAndQualificationFound_MapsModelAndReturnsView var mockRepository = new Mock(); var mockContentService = new Mock(); - mockContentService.Setup(x => x.GetConfirmQualificationPage()).ReturnsAsync(new ConfirmQualificationPage - { - QualificationLabel = "Test qualification label", - BackButton = new NavigationLink - { - DisplayText = "Test back button", - OpenInNewTab = false, - Href = "/qualifications" - }, - ErrorText = "Test error text", - ButtonText = "Test button text", - LevelLabel = "Test level label", - DateAddedLabel = "Test date added label", - Heading = "Test heading", - Options = - [ - new Option - { - Label = "yes", - Value = "yes" - }, - new Option - { - Label = "no", - Value = "no" - } - ], - RadioHeading = "Test radio heading", - AwardingOrganisationLabel = "Test awarding organisation label", - ErrorBannerHeading = "Test error banner heading", - ErrorBannerLink = "Test error banner link" - }); + var confirmQualificationPageContent = GetConfirmQualificationPageContent(); + + mockContentService.Setup(x => x.GetConfirmQualificationPage()).ReturnsAsync(confirmQualificationPageContent); var qualification = new Qualification("Some ID", "Qualification Name", @@ -232,24 +172,24 @@ public async Task Index_PageDetailsAndQualificationFound_MapsModelAndReturnsView Value = "no" } ]); - model.Heading.Should().Be("Test heading"); - model.ButtonText.Should().Be("Test button text"); - model.ErrorText.Should().Be("Test error text"); - model.LevelLabel.Should().Be("Test level label"); + model.Heading.Should().Be(confirmQualificationPageContent.Heading); + model.ButtonText.Should().Be(confirmQualificationPageContent.NoAdditionalRequirementsButtonText); + model.ErrorText.Should().Be(confirmQualificationPageContent.ErrorText); + model.LevelLabel.Should().Be(confirmQualificationPageContent.LevelLabel); model.QualificationId.Should().Be("Some ID"); - model.QualificationLabel.Should().Be("Test qualification label"); + model.QualificationLabel.Should().Be(confirmQualificationPageContent.QualificationLabel); model.QualificationLevel.Should().Be("2"); model.QualificationName.Should().Be("Qualification Name"); - model.RadioHeading.Should().Be("Test radio heading"); - model.AwardingOrganisationLabel.Should().Be("Test awarding organisation label"); + model.RadioHeading.Should().Be(confirmQualificationPageContent.RadioHeading); + model.AwardingOrganisationLabel.Should().Be(confirmQualificationPageContent.AwardingOrganisationLabel); model.ConfirmQualificationAnswer.Should().Be(string.Empty); - model.DateAddedLabel.Should().Be("Test date added label"); - model.ErrorBannerHeading.Should().Be("Test error banner heading"); - model.ErrorBannerLink.Should().Be("Test error banner link"); + model.DateAddedLabel.Should().Be(confirmQualificationPageContent.DateAddedLabel); + model.ErrorBannerHeading.Should().Be(confirmQualificationPageContent.ErrorBannerHeading); + model.ErrorBannerLink.Should().Be(confirmQualificationPageContent.ErrorBannerLink); model.QualificationAwardingOrganisation.Should().Be(AwardingOrganisations.Ncfe); model.QualificationDateAdded.Should().Be("2014"); } - + [TestMethod] public async Task Post_InvalidModel_CantGetPageContent_LogsAndReturnsError() { @@ -370,38 +310,9 @@ public async Task Post_InvalidModel_BuildsModelWithHasErrorsAndReturns() var mockRepository = new Mock(); var mockContentService = new Mock(); - mockContentService.Setup(x => x.GetConfirmQualificationPage()).ReturnsAsync(new ConfirmQualificationPage - { - QualificationLabel = "Test qualification label", - BackButton = new NavigationLink - { - DisplayText = "Test back button", - OpenInNewTab = false, - Href = "/qualifications" - }, - ErrorText = "Test error text", - ButtonText = "Test button text", - LevelLabel = "Test level label", - DateAddedLabel = "Test date added label", - Heading = "Test heading", - Options = - [ - new Option - { - Label = "yes", - Value = "yes" - }, - new Option - { - Label = "no", - Value = "no" - } - ], - RadioHeading = "Test radio heading", - AwardingOrganisationLabel = "Test awarding organisation label", - ErrorBannerHeading = "Test error banner heading", - ErrorBannerLink = "Test error banner link" - }); + var confirmQualificationPageContent = GetConfirmQualificationPageContent(); + + mockContentService.Setup(x => x.GetConfirmQualificationPage()).ReturnsAsync(confirmQualificationPageContent); var qualification = new Qualification("Some ID", "Qualification Name", @@ -461,20 +372,20 @@ public async Task Post_InvalidModel_BuildsModelWithHasErrorsAndReturns() Value = "no" } ]); - model.Heading.Should().Be("Test heading"); - model.ButtonText.Should().Be("Test button text"); - model.ErrorText.Should().Be("Test error text"); - model.LevelLabel.Should().Be("Test level label"); + model.Heading.Should().Be(confirmQualificationPageContent.Heading); + model.ButtonText.Should().Be(confirmQualificationPageContent.NoAdditionalRequirementsButtonText); + model.ErrorText.Should().Be(confirmQualificationPageContent.ErrorText); + model.LevelLabel.Should().Be(confirmQualificationPageContent.LevelLabel); model.QualificationId.Should().Be("Some ID"); - model.QualificationLabel.Should().Be("Test qualification label"); + model.QualificationLabel.Should().Be(confirmQualificationPageContent.QualificationLabel); model.QualificationLevel.Should().Be("2"); model.QualificationName.Should().Be("Qualification Name"); - model.RadioHeading.Should().Be("Test radio heading"); - model.AwardingOrganisationLabel.Should().Be("Test awarding organisation label"); + model.RadioHeading.Should().Be(confirmQualificationPageContent.RadioHeading); + model.AwardingOrganisationLabel.Should().Be(confirmQualificationPageContent.AwardingOrganisationLabel); model.ConfirmQualificationAnswer.Should().Be(string.Empty); - model.DateAddedLabel.Should().Be("Test date added label"); - model.ErrorBannerHeading.Should().Be("Test error banner heading"); - model.ErrorBannerLink.Should().Be("Test error banner link"); + model.DateAddedLabel.Should().Be(confirmQualificationPageContent.DateAddedLabel); + model.ErrorBannerHeading.Should().Be(confirmQualificationPageContent.ErrorBannerHeading); + model.ErrorBannerLink.Should().Be(confirmQualificationPageContent.ErrorBannerLink); model.QualificationAwardingOrganisation.Should().Be(AwardingOrganisations.Ncfe); model.QualificationDateAdded.Should().Be("2014"); } @@ -528,6 +439,56 @@ public async Task actionResult.RouteValues.Should().Contain("qualificationId", "TEST-123"); actionResult.RouteValues.Should().Contain("questionIndex", 1); } + + [TestMethod] + public async Task + Post_ValidModel_QualificationHasAdditionalRequirementsButAutomaticallyApprovedAtL6IsTrue_RedirectsToCheckAdditionalRequirements() + { + var mockLogger = new Mock>(); + var mockRepository = new Mock(); + var mockContentService = new Mock(); + var additionalRequirements = new List { new() }; + + var qualification = new Qualification("Some ID", + "Qualification Name", + AwardingOrganisations.Ncfe, + 2) + { + FromWhichYear = "2014", ToWhichYear = "2019", + QualificationNumber = "ABC/547/900", + AdditionalRequirements = "additional requirements", + AdditionalRequirementQuestions = additionalRequirements, + IsAutomaticallyApprovedAtLevel6 = true + }; + + mockRepository.Setup(x => x.GetById("TEST-123")) + .ReturnsAsync(qualification); + + var mockUserJourneyService = new Mock(); + + var mockContentParser = new Mock(); + + var controller = + new ConfirmQualificationController(mockLogger.Object, + mockRepository.Object, + mockContentService.Object, + mockUserJourneyService.Object, + mockContentParser.Object); + + var result = await controller.Confirm(new ConfirmQualificationPageModel + { + QualificationId = "TEST-123", + ConfirmQualificationAnswer = "yes" + }); + + result.Should().BeOfType(); + + var actionResult = (RedirectToActionResult)result; + + actionResult.ActionName.Should().Be("Index"); + actionResult.ControllerName.Should().Be("QualificationDetails"); + actionResult.RouteValues.Should().ContainSingle("qualificationId", "TEST-123"); + } [TestMethod] public async Task Post_ValidModel_PassedYes_RedirectsToQualificationDetailsAction() @@ -659,4 +620,42 @@ await controller.Confirm(new ConfirmQualificationPageModel mockUserJourneyService.Verify(x => x.ClearAdditionalQuestionsAnswers(), Times.Once); } + + private static ConfirmQualificationPage GetConfirmQualificationPageContent() + { + return new ConfirmQualificationPage + { + QualificationLabel = "Test qualification label", + BackButton = new NavigationLink + { + DisplayText = "Test back button", + OpenInNewTab = false, + Href = "/qualifications" + }, + ErrorText = "Test error text", + ButtonText = "Test button text", + LevelLabel = "Test level label", + DateAddedLabel = "Test date added label", + Heading = "Test heading", + Options = + [ + new Option + { + Label = "yes", + Value = "yes" + }, + new Option + { + Label = "no", + Value = "no" + } + ], + RadioHeading = "Test radio heading", + AwardingOrganisationLabel = "Test awarding organisation label", + ErrorBannerHeading = "Test error banner heading", + ErrorBannerLink = "Test error banner link", + AnswerDisclaimerText = "Answer disclaimer text", + NoAdditionalRequirementsButtonText = "Get result" + }; + } } \ No newline at end of file diff --git a/tests/Dfe.EarlyYearsQualification.UnitTests/Controllers/QualificationDetailsControllerTests.cs b/tests/Dfe.EarlyYearsQualification.UnitTests/Controllers/QualificationDetailsControllerTests.cs index 0fff7b50b..dbd64b247 100644 --- a/tests/Dfe.EarlyYearsQualification.UnitTests/Controllers/QualificationDetailsControllerTests.cs +++ b/tests/Dfe.EarlyYearsQualification.UnitTests/Controllers/QualificationDetailsControllerTests.cs @@ -1,3 +1,4 @@ +using Contentful.Core.Models; using Dfe.EarlyYearsQualification.Content.Constants; using Dfe.EarlyYearsQualification.Content.Entities; using Dfe.EarlyYearsQualification.Content.RichTextParsing; @@ -5,6 +6,7 @@ using Dfe.EarlyYearsQualification.Mock.Helpers; using Dfe.EarlyYearsQualification.UnitTests.Extensions; using Dfe.EarlyYearsQualification.Web.Controllers; +using Dfe.EarlyYearsQualification.Web.Models; using Dfe.EarlyYearsQualification.Web.Models.Content; using Dfe.EarlyYearsQualification.Web.Services.UserJourneyCookieService; using FluentAssertions; @@ -383,12 +385,12 @@ public async Task model.FromWhichYear.Should().Be(qualificationResult.FromWhichYear); model.QualificationNumber.Should().Be(qualificationResult.QualificationNumber); - model.RatioRequirements.ApprovedForLevel2.Should().BeFalse(); - model.RatioRequirements.ApprovedForLevel3.Should().BeFalse(); - model.RatioRequirements.ApprovedForLevel6.Should().BeFalse(); + model.RatioRequirements.ApprovedForLevel2.Should().Be(QualificationApprovalStatus.NotApproved); + model.RatioRequirements.ApprovedForLevel3.Should().Be(QualificationApprovalStatus.NotApproved); + model.RatioRequirements.ApprovedForLevel6.Should().Be(QualificationApprovalStatus.NotApproved); //Note unqualified ratios will always be approved no matter the answers - model.RatioRequirements.ApprovedForUnqualified.Should().BeTrue(); + model.RatioRequirements.ApprovedForUnqualified.Should().Be(QualificationApprovalStatus.Approved); } [TestMethod] @@ -567,6 +569,269 @@ public async Task Index_BuildUpRatioRequirements_CantFindLevel6_LogsAndThrows() await Assert.ThrowsExceptionAsync(() => controller.Index(qualificationId)); mockLogger.VerifyError($"Could not find property: FullAndRelevantForLevel{level}After2014 within Level 6 Ratio Requirements for qualification: {qualificationId}"); } + + [TestMethod] + [DataRow(9, 2014, 3)] + [DataRow(8, 2019, 3)] + [DataRow(9, 2014, 4)] + [DataRow(8, 2019, 4)] + [DataRow(9, 2014, 5)] + [DataRow(8, 2019, 5)] + [DataRow(9, 2014, 6)] + [DataRow(8, 2019, 6)] + [DataRow(9, 2014, 7)] + [DataRow(8, 2019, 7)] + public async Task Index_BuildUpRatioRequirements_NotRelevantButLevel3OrAboveStartedBetweenSept2014AndAug2019_MarksLevel2AsFurtherActionRequired(int startMonth, int startYear, int level) + { + var mockLogger = new Mock>(); + var mockRepository = new Mock(); + var mockContentService = new Mock(); + var mockContentParser = new Mock(); + var mockUserJourneyCookieService = new Mock(); + + const string qualificationId = "eyq-145"; + + var ratioRequirements = new List + { + new() + { + RatioRequirementName = "Level 2 Ratio Requirements", + FullAndRelevantForLevel2After2014 = false, + FullAndRelevantForLevel3After2014 = false, + FullAndRelevantForLevel4After2014 = false, + FullAndRelevantForLevel5After2014 = false, + FullAndRelevantForLevel6After2014 = false, + FullAndRelevantForLevel7After2014 = false, + RequirementForLevel2BetweenSept14AndAug19 = ContentfulContentHelper.Paragraph("Test") + }, + new() + { + RatioRequirementName = "Level 3 Ratio Requirements", + FullAndRelevantForLevel2After2014 = false, + FullAndRelevantForLevel3After2014 = false, + FullAndRelevantForLevel4After2014 = false, + FullAndRelevantForLevel5After2014 = false, + FullAndRelevantForLevel6After2014 = false, + FullAndRelevantForLevel7After2014 = false, + RequirementForLevel2After2014 = ContentfulContentHelper.Paragraph("Test") + }, + new() + { + RatioRequirementName = "Level 6 Ratio Requirements", + FullAndRelevantForLevel2After2014 = false, + FullAndRelevantForLevel3After2014 = false, + FullAndRelevantForLevel4After2014 = false, + FullAndRelevantForLevel5After2014 = false, + FullAndRelevantForLevel6After2014 = false, + FullAndRelevantForLevel7After2014 = false, + RequirementForLevel2After2014 = ContentfulContentHelper.Paragraph("Test") + }, + // Note that every qualification will be marked as relevant for unqualified work, this still counts as not F and R. + new() + { + RatioRequirementName = "Unqualified Ratio Requirements", + FullAndRelevantForLevel2After2014 = true, + FullAndRelevantForLevel3After2014 = true, + FullAndRelevantForLevel4After2014 = true, + FullAndRelevantForLevel5After2014 = true, + FullAndRelevantForLevel6After2014 = true, + FullAndRelevantForLevel7After2014 = true, + RequirementForLevel2After2014 = ContentfulContentHelper.Paragraph("Test") + } + }; + + var qualificationResult = new Qualification(qualificationId, + "Qualification Name", + AwardingOrganisations.Ncfe, + level) + { + FromWhichYear = "2014", ToWhichYear = "2019", + QualificationNumber = "ABC/547/900", + AdditionalRequirements = "additional requirements", + RatioRequirements = ratioRequirements + }; + + mockRepository.Setup(x => x.GetById(qualificationId)) + .ReturnsAsync(qualificationResult); + + mockContentService.Setup(x => x.GetDetailsPage()) + .ReturnsAsync(new DetailsPage + { + BackToConfirmAnswers = new NavigationLink + { + Href = "/qualifications/check-additional-questions/$[qualification-id]$/confirm-answers" + } + }); + + mockUserJourneyCookieService.Setup(x => x.GetWhenWasQualificationStarted()).Returns((startMonth, startYear)); + mockUserJourneyCookieService.Setup(x => x.WasStartedBetweenSept2014AndAug2019()) + .Returns(true); + + var controller = + new QualificationDetailsController(mockLogger.Object, + mockRepository.Object, + mockContentService.Object, + mockContentParser.Object, + mockUserJourneyCookieService.Object) + { + ControllerContext = new ControllerContext + { + HttpContext = new DefaultHttpContext() + } + }; + + var result = await controller.Index(qualificationId); + + result.Should().NotBeNull(); + + var resultType = result as ViewResult; + resultType.Should().NotBeNull(); + + var model = resultType!.Model as QualificationDetailsModel; + model.Should().NotBeNull(); + + model!.QualificationId.Should().Be(qualificationResult.QualificationId); + model.QualificationName.Should().Be(qualificationResult.QualificationName); + model.AwardingOrganisationTitle.Should().Be(qualificationResult.AwardingOrganisationTitle); + model.QualificationLevel.Should().Be(qualificationResult.QualificationLevel); + model.FromWhichYear.Should().Be(qualificationResult.FromWhichYear); + model.QualificationNumber.Should().Be(qualificationResult.QualificationNumber); + + model.RatioRequirements.ApprovedForLevel2.Should().Be(QualificationApprovalStatus.FurtherActionRequired); + model.RatioRequirements.ApprovedForLevel3.Should().Be(QualificationApprovalStatus.NotApproved); + model.RatioRequirements.ApprovedForLevel6.Should().Be(QualificationApprovalStatus.NotApproved); + model.RatioRequirements.ApprovedForUnqualified.Should().Be(QualificationApprovalStatus.Approved); + } + + [TestMethod] + public async Task + Index_QualIsRelevantButAdditionalAnswersMarkItAsNotRelevantAndLevel3OrAboveStartedBetweenSept2014AndAug2019_MarksLevel2AsFurtherActionRequired() + { + var mockLogger = new Mock>(); + var mockRepository = new Mock(); + var mockContentService = new Mock(); + var mockContentParser = new Mock(); + var mockUserJourneyCookieService = new Mock(); + + const string qualificationId = "eyq-145"; + const int level = 6; + const int startMonth = 6; + const int startYear = 2016; + + var additionalRequirementQuestions = new List + { + new() + { + Sys = new SystemProperties + { + Id = "Some Id" + }, + Question = "Have they got pediatric first aid?", + AnswerToBeFullAndRelevant = true + } + }; + + //Answer here makes this qual not full and relevant + var listOfAdditionalReqsAnswered = new Dictionary + { + { "Have they got pediatric first aid?", "no" } + }; + + var ratioRequirements = new List + { + new() + { + RatioRequirementName = "Level 2 Ratio Requirements", + FullAndRelevantForLevel6After2014 = true, + RequirementForLevel2BetweenSept14AndAug19 = ContentfulContentHelper.Paragraph("Test") + }, + new() + { + RatioRequirementName = "Level 3 Ratio Requirements", + FullAndRelevantForLevel6After2014 = true, + RequirementForLevel2After2014 = ContentfulContentHelper.Paragraph("Test") + }, + new() + { + RatioRequirementName = "Level 6 Ratio Requirements", + FullAndRelevantForLevel6After2014 = true, + RequirementForLevel2After2014 = ContentfulContentHelper.Paragraph("Test") + }, + // Note that every qualification will be marked as relevant for unqualified work, this still counts as not F and R. + new() + { + RatioRequirementName = "Unqualified Ratio Requirements", + FullAndRelevantForLevel6After2014 = true, + RequirementForLevel2After2014 = ContentfulContentHelper.Paragraph("Test") + } + }; + + var qualificationResult = new Qualification(qualificationId, + "Qualification Name", + AwardingOrganisations.Ncfe, + level) + { + FromWhichYear = "2014", ToWhichYear = "2019", + QualificationNumber = "ABC/547/900", + AdditionalRequirements = "additional requirements", + RatioRequirements = ratioRequirements, + AdditionalRequirementQuestions = additionalRequirementQuestions + }; + + mockRepository.Setup(x => x.GetById(qualificationId)) + .ReturnsAsync(qualificationResult); + + mockContentService.Setup(x => x.GetDetailsPage()) + .ReturnsAsync(new DetailsPage + { + BackToConfirmAnswers = new NavigationLink + { + Href = "/qualifications/check-additional-questions/$[qualification-id]$/confirm-answers" + } + }); + + mockUserJourneyCookieService.Setup(x => x.GetWhenWasQualificationStarted()).Returns((startMonth, startYear)); + mockUserJourneyCookieService.Setup(x => x.WasStartedBetweenSept2014AndAug2019()) + .Returns(true); + + mockUserJourneyCookieService.Setup(x => x.GetAdditionalQuestionsAnswers()) + .Returns(listOfAdditionalReqsAnswered); + + var controller = + new QualificationDetailsController(mockLogger.Object, + mockRepository.Object, + mockContentService.Object, + mockContentParser.Object, + mockUserJourneyCookieService.Object) + { + ControllerContext = new ControllerContext + { + HttpContext = new DefaultHttpContext() + } + }; + + var result = await controller.Index(qualificationId); + + result.Should().NotBeNull(); + + var resultType = result as ViewResult; + resultType.Should().NotBeNull(); + + var model = resultType!.Model as QualificationDetailsModel; + model.Should().NotBeNull(); + + model!.QualificationId.Should().Be(qualificationResult.QualificationId); + model.QualificationName.Should().Be(qualificationResult.QualificationName); + model.AwardingOrganisationTitle.Should().Be(qualificationResult.AwardingOrganisationTitle); + model.QualificationLevel.Should().Be(qualificationResult.QualificationLevel); + model.FromWhichYear.Should().Be(qualificationResult.FromWhichYear); + model.QualificationNumber.Should().Be(qualificationResult.QualificationNumber); + + model.RatioRequirements.ApprovedForLevel2.Should().Be(QualificationApprovalStatus.FurtherActionRequired); + model.RatioRequirements.ApprovedForLevel3.Should().Be(QualificationApprovalStatus.NotApproved); + model.RatioRequirements.ApprovedForLevel6.Should().Be(QualificationApprovalStatus.NotApproved); + model.RatioRequirements.ApprovedForUnqualified.Should().Be(QualificationApprovalStatus.Approved); + } [TestMethod] public async Task Index_AllRatiosFound_ReturnsView() @@ -622,10 +887,10 @@ public async Task Index_AllRatiosFound_ReturnsView() var detailsPage = new DetailsPage { - BackToAdditionalQuestionsLink = new NavigationLink - { - Href = "/api/qualifications" - } + BackToConfirmAnswers = new NavigationLink + { + Href = "/qualifications/check-additional-questions/$[qualification-id]$/confirm-answers" + } }; mockRepository.Setup(x => x.GetById(qualificationId)) @@ -670,10 +935,569 @@ public async Task Index_AllRatiosFound_ReturnsView() model.FromWhichYear.Should().Be(qualificationResult.FromWhichYear); model.QualificationNumber.Should().Be(qualificationResult.QualificationNumber); - model.RatioRequirements.ApprovedForLevel2.Should().BeTrue(); - model.RatioRequirements.ApprovedForLevel3.Should().BeTrue(); - model.RatioRequirements.ApprovedForLevel6.Should().BeTrue(); - model.RatioRequirements.ApprovedForUnqualified.Should().BeTrue(); + model.RatioRequirements.ApprovedForLevel2.Should().Be(QualificationApprovalStatus.Approved); + model.RatioRequirements.ApprovedForLevel3.Should().Be(QualificationApprovalStatus.Approved); + model.RatioRequirements.ApprovedForLevel6.Should().Be(QualificationApprovalStatus.Approved); + model.RatioRequirements.ApprovedForUnqualified.Should().Be(QualificationApprovalStatus.Approved); + } + + [TestMethod] + public async Task Index_QualificationContainsQts_UserAnswerDoesntMatch_NotApprovedAtL6() + { + var mockLogger = new Mock>(); + var mockRepository = new Mock(); + var mockContentService = new Mock(); + var mockContentParser = new Mock(); + var mockUserJourneyCookieService = new Mock(); + + const string qualificationId = "eyq-148"; + const int level = 6; + const int startDateYear = 2022; + const string requirementsForLevel = "Test"; + + var additionalRequirementQuestions = new List + { + new() + { + Sys = new SystemProperties + { + Id = AdditionalRequirementQuestions.QtsQuestion + }, + Question = "This is the Qts Question", + AnswerToBeFullAndRelevant = true + }, + new() + { + Sys = new SystemProperties + { + Id = "Some other Id" + }, + Question = "Have they got pediatric first aid?", + AnswerToBeFullAndRelevant = true + } + }; + + var ratioRequirements = new List + { + new() + { + RatioRequirementName = "Level 2 Ratio Requirements", + FullAndRelevantForLevel6After2014 = true, + RequirementForLevel6After2014 = ContentfulContentHelper.Paragraph(requirementsForLevel) + }, + new() + { + RatioRequirementName = "Level 3 Ratio Requirements", + FullAndRelevantForLevel6After2014 = true, + RequirementForLevel6After2014 = ContentfulContentHelper.Paragraph(requirementsForLevel) + }, + new() + { + RatioRequirementName = "Level 6 Ratio Requirements", + FullAndRelevantForLevel6After2014 = false, + RequirementForLevel6After2014 = ContentfulContentHelper.Paragraph(requirementsForLevel) + }, + new() + { + RatioRequirementName = "Unqualified Ratio Requirements", + FullAndRelevantForLevel6After2014 = true, + RequirementForLevel6After2014 = ContentfulContentHelper.Paragraph(requirementsForLevel) + } + }; + + var listOfAdditionalReqsAnswered = new Dictionary + { + { "This is the Qts Question", "no" }, + { "Have they got pediatric first aid?", "yes" } + }; + + var qualificationResult = new Qualification(qualificationId, + "Qualification Name", + AwardingOrganisations.Ncfe, + level) + { + FromWhichYear = "2014", ToWhichYear = "2019", + QualificationNumber = "ABC/547/900", + AdditionalRequirements = "additional requirements", + AdditionalRequirementQuestions = additionalRequirementQuestions, + RatioRequirements = ratioRequirements + }; + + var detailsPage = new DetailsPage + { + BackToConfirmAnswers = new NavigationLink + { + Href = "/qualifications/check-additional-questions/$[qualification-id]$/confirm-answers" + } + }; + + mockRepository.Setup(x => x.GetById(qualificationId)) + .ReturnsAsync(qualificationResult); + + mockContentService.Setup(x => x.GetDetailsPage()) + .ReturnsAsync(detailsPage); + + mockUserJourneyCookieService.Setup(x => x.GetWhenWasQualificationStarted()) + .Returns((9, startDateYear)); + + mockUserJourneyCookieService.Setup(x => x.WasStartedBeforeSeptember2014()) + .Returns(false); + + mockUserJourneyCookieService.Setup(x => x.GetAdditionalQuestionsAnswers()) + .Returns(listOfAdditionalReqsAnswered); + + mockContentParser.Setup(x => x.ToHtml(It.IsAny())).ReturnsAsync(requirementsForLevel); + + var controller = + new QualificationDetailsController(mockLogger.Object, + mockRepository.Object, + mockContentService.Object, + mockContentParser.Object, + mockUserJourneyCookieService.Object) + { + ControllerContext = new ControllerContext + { + HttpContext = new DefaultHttpContext() + } + }; + + var result = await controller.Index(qualificationId); + + result.Should().NotBeNull(); + + var resultType = result as ViewResult; + resultType.Should().NotBeNull(); + + var model = resultType!.Model as QualificationDetailsModel; + model.Should().NotBeNull(); + + model!.QualificationId.Should().Be(qualificationResult.QualificationId); + model.QualificationName.Should().Be(qualificationResult.QualificationName); + model.AwardingOrganisationTitle.Should().Be(qualificationResult.AwardingOrganisationTitle); + model.QualificationLevel.Should().Be(qualificationResult.QualificationLevel); + model.FromWhichYear.Should().Be(qualificationResult.FromWhichYear); + model.QualificationNumber.Should().Be(qualificationResult.QualificationNumber); + + model.RatioRequirements.ApprovedForLevel2.Should().Be(QualificationApprovalStatus.Approved); + model.RatioRequirements.ApprovedForLevel3.Should().Be(QualificationApprovalStatus.Approved); + model.RatioRequirements.ApprovedForLevel6.Should().Be(QualificationApprovalStatus.NotApproved); + model.RatioRequirements.ApprovedForUnqualified.Should().Be(QualificationApprovalStatus.Approved); + + model.RatioRequirements.RequirementsForLevel6.Should().Be(requirementsForLevel); + model.RatioRequirements.ShowRequirementsForLevel6ByDefault.Should().BeTrue(); + } + + [TestMethod] + public async Task Index_QualificationContainsQts_UserAnswerMatches_ApprovedAtL6() + { + var mockLogger = new Mock>(); + var mockRepository = new Mock(); + var mockContentService = new Mock(); + var mockContentParser = new Mock(); + var mockUserJourneyCookieService = new Mock(); + + const string qualificationId = "eyq-148"; + const int level = 6; + const int startDateYear = 2022; + const string requirementsForLevel = "Test"; + + var additionalRequirementQuestions = new List + { + new() + { + Sys = new SystemProperties + { + Id = AdditionalRequirementQuestions.QtsQuestion + }, + Question = "This is the Qts Question", + AnswerToBeFullAndRelevant = true + }, + new() + { + Sys = new SystemProperties + { + Id = "Some other Id" + }, + Question = "Have they got pediatric first aid?", + AnswerToBeFullAndRelevant = true + } + }; + + var ratioRequirements = new List + { + new() + { + RatioRequirementName = "Level 2 Ratio Requirements", + FullAndRelevantForQtsEtcAfter2014 = true, + RequirementForQtsEtcAfter2014 = ContentfulContentHelper.Paragraph(requirementsForLevel) + }, + new() + { + RatioRequirementName = "Level 3 Ratio Requirements", + FullAndRelevantForQtsEtcAfter2014 = true, + RequirementForQtsEtcAfter2014 = ContentfulContentHelper.Paragraph(requirementsForLevel) + }, + new() + { + RatioRequirementName = "Level 6 Ratio Requirements", + FullAndRelevantForQtsEtcAfter2014 = true, + RequirementForQtsEtcAfter2014 = ContentfulContentHelper.Paragraph(requirementsForLevel) + }, + new() + { + RatioRequirementName = "Unqualified Ratio Requirements", + FullAndRelevantForQtsEtcAfter2014 = true, + RequirementForQtsEtcAfter2014 = ContentfulContentHelper.Paragraph(requirementsForLevel) + } + }; + + var listOfAdditionalReqsAnswered = new Dictionary + { + { "This is the Qts Question", "yes" }, + { "Have they got pediatric first aid?", "yes" } + }; + + var qualificationResult = new Qualification(qualificationId, + "Qualification Name", + AwardingOrganisations.Ncfe, + level) + { + FromWhichYear = "2014", ToWhichYear = "2019", + QualificationNumber = "ABC/547/900", + AdditionalRequirements = "additional requirements", + AdditionalRequirementQuestions = additionalRequirementQuestions, + RatioRequirements = ratioRequirements + }; + + var detailsPage = new DetailsPage + { + BackToConfirmAnswers = new NavigationLink + { + Href = "/qualifications/check-additional-questions/$[qualification-id]$/confirm-answers" + } + }; + + mockRepository.Setup(x => x.GetById(qualificationId)) + .ReturnsAsync(qualificationResult); + + mockContentService.Setup(x => x.GetDetailsPage()) + .ReturnsAsync(detailsPage); + + mockUserJourneyCookieService.Setup(x => x.GetWhenWasQualificationStarted()) + .Returns((9, startDateYear)); + + mockUserJourneyCookieService.Setup(x => x.WasStartedBeforeSeptember2014()) + .Returns(false); + + mockUserJourneyCookieService.Setup(x => x.GetAdditionalQuestionsAnswers()) + .Returns(listOfAdditionalReqsAnswered); + + mockContentParser.Setup(x => x.ToHtml(It.IsAny())).ReturnsAsync(requirementsForLevel); + + var controller = + new QualificationDetailsController(mockLogger.Object, + mockRepository.Object, + mockContentService.Object, + mockContentParser.Object, + mockUserJourneyCookieService.Object) + { + ControllerContext = new ControllerContext + { + HttpContext = new DefaultHttpContext() + } + }; + + var result = await controller.Index(qualificationId); + + result.Should().NotBeNull(); + + var resultType = result as ViewResult; + resultType.Should().NotBeNull(); + + var model = resultType!.Model as QualificationDetailsModel; + model.Should().NotBeNull(); + + model!.QualificationId.Should().Be(qualificationResult.QualificationId); + model.QualificationName.Should().Be(qualificationResult.QualificationName); + model.AwardingOrganisationTitle.Should().Be(qualificationResult.AwardingOrganisationTitle); + model.QualificationLevel.Should().Be(qualificationResult.QualificationLevel); + model.FromWhichYear.Should().Be(qualificationResult.FromWhichYear); + model.QualificationNumber.Should().Be(qualificationResult.QualificationNumber); + + model.RatioRequirements.ApprovedForLevel2.Should().Be(QualificationApprovalStatus.Approved); + model.RatioRequirements.ApprovedForLevel3.Should().Be(QualificationApprovalStatus.Approved); + model.RatioRequirements.ApprovedForLevel6.Should().Be(QualificationApprovalStatus.Approved); + model.RatioRequirements.ApprovedForUnqualified.Should().Be(QualificationApprovalStatus.Approved); + + model.AdditionalRequirementAnswers.Should().NotBeNull(); + model.AdditionalRequirementAnswers!.Count.Should().Be(1); + } + + [TestMethod] + public async Task Index_QualificationIsAutomaticallyApprovedAtL6_ApprovedAtL6() + { + var mockLogger = new Mock>(); + var mockRepository = new Mock(); + var mockContentService = new Mock(); + var mockContentParser = new Mock(); + var mockUserJourneyCookieService = new Mock(); + + const string qualificationId = "eyq-148"; + const int level = 6; + const int startDateYear = 2022; + const string requirementsForLevel = "Test"; + + var ratioRequirements = new List + { + new() + { + RatioRequirementName = "Level 2 Ratio Requirements", + FullAndRelevantForQtsEtcAfter2014 = true, + RequirementForQtsEtcAfter2014 = ContentfulContentHelper.Paragraph(requirementsForLevel) + }, + new() + { + RatioRequirementName = "Level 3 Ratio Requirements", + FullAndRelevantForQtsEtcAfter2014 = true, + RequirementForQtsEtcAfter2014 = ContentfulContentHelper.Paragraph(requirementsForLevel) + }, + new() + { + RatioRequirementName = "Level 6 Ratio Requirements", + FullAndRelevantForQtsEtcAfter2014 = true, + RequirementForQtsEtcAfter2014 = ContentfulContentHelper.Paragraph(requirementsForLevel) + }, + new() + { + RatioRequirementName = "Unqualified Ratio Requirements", + FullAndRelevantForQtsEtcAfter2014 = true, + RequirementForQtsEtcAfter2014 = ContentfulContentHelper.Paragraph(requirementsForLevel) + } + }; + + var listOfAdditionalReqsAnswered = new Dictionary + { + { "This is the Qts Question", "yes" }, + { "Have they got pediatric first aid?", "yes" } + }; + + var qualificationResult = new Qualification(qualificationId, + "Qualification Name", + AwardingOrganisations.Ncfe, + level) + { + FromWhichYear = "2014", ToWhichYear = "2019", + QualificationNumber = "ABC/547/900", + AdditionalRequirements = "additional requirements", + IsAutomaticallyApprovedAtLevel6 = true, + RatioRequirements = ratioRequirements + }; + + var detailsPage = new DetailsPage + { + BackToConfirmAnswers = new NavigationLink + { + Href = "/qualifications/check-additional-questions/$[qualification-id]$/confirm-answers" + } + }; + + mockRepository.Setup(x => x.GetById(qualificationId)) + .ReturnsAsync(qualificationResult); + + mockContentService.Setup(x => x.GetDetailsPage()) + .ReturnsAsync(detailsPage); + + mockUserJourneyCookieService.Setup(x => x.GetWhenWasQualificationStarted()) + .Returns((9, startDateYear)); + + mockUserJourneyCookieService.Setup(x => x.WasStartedBeforeSeptember2014()) + .Returns(false); + + mockUserJourneyCookieService.Setup(x => x.GetAdditionalQuestionsAnswers()) + .Returns(listOfAdditionalReqsAnswered); + + mockContentParser.Setup(x => x.ToHtml(It.IsAny())).ReturnsAsync(requirementsForLevel); + + var controller = + new QualificationDetailsController(mockLogger.Object, + mockRepository.Object, + mockContentService.Object, + mockContentParser.Object, + mockUserJourneyCookieService.Object) + { + ControllerContext = new ControllerContext + { + HttpContext = new DefaultHttpContext() + } + }; + + var result = await controller.Index(qualificationId); + + result.Should().NotBeNull(); + + var resultType = result as ViewResult; + resultType.Should().NotBeNull(); + + var model = resultType!.Model as QualificationDetailsModel; + model.Should().NotBeNull(); + + model!.QualificationId.Should().Be(qualificationResult.QualificationId); + model.QualificationName.Should().Be(qualificationResult.QualificationName); + model.AwardingOrganisationTitle.Should().Be(qualificationResult.AwardingOrganisationTitle); + model.QualificationLevel.Should().Be(qualificationResult.QualificationLevel); + model.FromWhichYear.Should().Be(qualificationResult.FromWhichYear); + model.QualificationNumber.Should().Be(qualificationResult.QualificationNumber); + + model.RatioRequirements.ApprovedForLevel2.Should().Be(QualificationApprovalStatus.Approved); + model.RatioRequirements.ApprovedForLevel3.Should().Be(QualificationApprovalStatus.Approved); + model.RatioRequirements.ApprovedForLevel6.Should().Be(QualificationApprovalStatus.Approved); + model.RatioRequirements.ApprovedForUnqualified.Should().Be(QualificationApprovalStatus.Approved); + } + + [TestMethod] + public async Task Index_QualificationContainsQts_UserAnswerDoesntMatch_OnlyApprovedAtUnqualified() + { + var mockLogger = new Mock>(); + var mockRepository = new Mock(); + var mockContentService = new Mock(); + var mockContentParser = new Mock(); + var mockUserJourneyCookieService = new Mock(); + + const string qualificationId = "eyq-148"; + const int level = 6; + const int startDateYear = 2022; + const string requirementsForLevel = "Test"; + + var additionalRequirementQuestions = new List + { + new() + { + Sys = new SystemProperties + { + Id = AdditionalRequirementQuestions.QtsQuestion + }, + Question = "This is the Qts Question", + AnswerToBeFullAndRelevant = true + }, + new() + { + Sys = new SystemProperties + { + Id = "Some other Id" + }, + Question = "Have they got pediatric first aid?", + AnswerToBeFullAndRelevant = true + } + }; + + var ratioRequirements = new List + { + new() + { + RatioRequirementName = "Level 2 Ratio Requirements", + FullAndRelevantForLevel6After2014 = true, + RequirementForLevel6After2014 = ContentfulContentHelper.Paragraph(requirementsForLevel) + }, + new() + { + RatioRequirementName = "Level 3 Ratio Requirements", + FullAndRelevantForLevel6After2014 = true, + RequirementForLevel6After2014 = ContentfulContentHelper.Paragraph(requirementsForLevel) + }, + new() + { + RatioRequirementName = "Level 6 Ratio Requirements", + FullAndRelevantForLevel6After2014 = false, + RequirementForLevel6After2014 = ContentfulContentHelper.Paragraph(requirementsForLevel) + }, + new() + { + RatioRequirementName = "Unqualified Ratio Requirements", + FullAndRelevantForLevel6After2014 = true, + RequirementForLevel6After2014 = ContentfulContentHelper.Paragraph(requirementsForLevel) + } + }; + + var listOfAdditionalReqsAnswered = new Dictionary + { + { "This is the Qts Question", "no" }, + { "Have they got pediatric first aid?", "no" } + }; + + var qualificationResult = new Qualification(qualificationId, + "Qualification Name", + AwardingOrganisations.Ncfe, + level) + { + FromWhichYear = "2014", ToWhichYear = "2019", + QualificationNumber = "ABC/547/900", + AdditionalRequirements = "additional requirements", + AdditionalRequirementQuestions = additionalRequirementQuestions, + RatioRequirements = ratioRequirements + }; + + var detailsPage = new DetailsPage + { + BackToConfirmAnswers = new NavigationLink + { + Href = "/qualifications/check-additional-questions/$[qualification-id]$/confirm-answers" + } + }; + + mockRepository.Setup(x => x.GetById(qualificationId)) + .ReturnsAsync(qualificationResult); + + mockContentService.Setup(x => x.GetDetailsPage()) + .ReturnsAsync(detailsPage); + + mockUserJourneyCookieService.Setup(x => x.GetWhenWasQualificationStarted()) + .Returns((9, startDateYear)); + + mockUserJourneyCookieService.Setup(x => x.WasStartedBeforeSeptember2014()) + .Returns(false); + + mockUserJourneyCookieService.Setup(x => x.GetAdditionalQuestionsAnswers()) + .Returns(listOfAdditionalReqsAnswered); + + mockContentParser.Setup(x => x.ToHtml(It.IsAny())).ReturnsAsync(requirementsForLevel); + + var controller = + new QualificationDetailsController(mockLogger.Object, + mockRepository.Object, + mockContentService.Object, + mockContentParser.Object, + mockUserJourneyCookieService.Object) + { + ControllerContext = new ControllerContext + { + HttpContext = new DefaultHttpContext() + } + }; + + var result = await controller.Index(qualificationId); + + result.Should().NotBeNull(); + + var resultType = result as ViewResult; + resultType.Should().NotBeNull(); + + var model = resultType!.Model as QualificationDetailsModel; + model.Should().NotBeNull(); + + model!.QualificationId.Should().Be(qualificationResult.QualificationId); + model.QualificationName.Should().Be(qualificationResult.QualificationName); + model.AwardingOrganisationTitle.Should().Be(qualificationResult.AwardingOrganisationTitle); + model.QualificationLevel.Should().Be(qualificationResult.QualificationLevel); + model.FromWhichYear.Should().Be(qualificationResult.FromWhichYear); + model.QualificationNumber.Should().Be(qualificationResult.QualificationNumber); + + model.RatioRequirements.ApprovedForLevel2.Should().Be(QualificationApprovalStatus.NotApproved); + model.RatioRequirements.ApprovedForLevel3.Should().Be(QualificationApprovalStatus.NotApproved); + model.RatioRequirements.ApprovedForLevel6.Should().Be(QualificationApprovalStatus.NotApproved); + model.RatioRequirements.ApprovedForUnqualified.Should().Be(QualificationApprovalStatus.Approved); + + model.RatioRequirements.RequirementsForLevel6.Should().Be(requirementsForLevel); + model.RatioRequirements.ShowRequirementsForLevel6ByDefault.Should().BeTrue(); } [TestMethod] @@ -733,9 +1557,9 @@ public async Task Index_BackToAdditionalQuestionsLinkIncludesQualificationId() var detailsPage = new DetailsPage { - BackToAdditionalQuestionsLink = new NavigationLink + BackToConfirmAnswers = new NavigationLink { - Href = "/api/qualifications" + Href = "/qualifications/check-additional-questions/$[qualification-id]$/confirm-answers" } }; @@ -771,7 +1595,7 @@ public async Task Index_BackToAdditionalQuestionsLinkIncludesQualificationId() var model = resultType!.Model as QualificationDetailsModel; - model!.BackButton!.Href.Should().Be("/api/qualifications/eyq-145/1"); + model!.BackButton!.Href.Should().Be("/qualifications/check-additional-questions/eyq-145/confirm-answers"); } [TestMethod] diff --git a/tests/Dfe.EarlyYearsQualification.UnitTests/Controllers/QuestionsControllerTests.cs b/tests/Dfe.EarlyYearsQualification.UnitTests/Controllers/QuestionsControllerTests.cs index d8a199a3a..1f5a92111 100644 --- a/tests/Dfe.EarlyYearsQualification.UnitTests/Controllers/QuestionsControllerTests.cs +++ b/tests/Dfe.EarlyYearsQualification.UnitTests/Controllers/QuestionsControllerTests.cs @@ -681,9 +681,9 @@ public async Task Post_WhatLevelIsTheQualification_Level2StartedBetween2014And20 resultType!.ActionName.Should().Be("QualificationsStartedBetweenSept2014AndAug2019"); resultType.ControllerName.Should().Be("Advice"); } - + [TestMethod] - public async Task Post_WhatLevelIsTheQualification_Level6Pre2014_ReturnsRedirectResponse() + public async Task Post_WhatLevelIsTheQualification_Level7Post2014_ReturnsRedirectResponse() { var mockLogger = new Mock>(); var mockContentService = new Mock(); @@ -692,89 +692,28 @@ public async Task Post_WhatLevelIsTheQualification_Level6Pre2014_ReturnsRedirect var mockRepository = new Mock(); var mockQuestionModelValidator = new Mock(); var mockPlaceholderUpdater = new Mock(); - - mockUserJourneyCookieService.Setup(x => x.WasStartedBeforeSeptember2014()) + + mockUserJourneyCookieService.Setup(x => x.WasStartedOnOrAfterSeptember2014()) .Returns(true); - + var controller = new QuestionsController(mockLogger.Object, mockContentService.Object, mockContentParser.Object, mockUserJourneyCookieService.Object, mockRepository.Object, mockQuestionModelValidator.Object, mockPlaceholderUpdater.Object); - - var result = await controller.WhatLevelIsTheQualification(new RadioQuestionModel - { - Option = "6" - }); - - result.Should().NotBeNull(); - - var resultType = result as RedirectToActionResult; - resultType.Should().NotBeNull(); - - resultType!.ActionName.Should().Be("Level6QualificationPre2014"); - resultType.ControllerName.Should().Be("Advice"); - } - - [TestMethod] - public async Task Post_WhatLevelIsTheQualification_Level6Post2014_ReturnsRedirectResponse() - { - var mockLogger = new Mock>(); - var mockContentService = new Mock(); - var mockContentParser = new Mock(); - var mockUserJourneyCookieService = new Mock(); - var mockRepository = new Mock(); - var mockQuestionModelValidator = new Mock(); - var mockPlaceholderUpdater = new Mock(); - - mockUserJourneyCookieService.Setup(x => x.WasStartedBeforeSeptember2014()) - .Returns(false); - - var controller = new QuestionsController(mockLogger.Object, mockContentService.Object, mockContentParser.Object, - mockUserJourneyCookieService.Object, mockRepository.Object, - mockQuestionModelValidator.Object, mockPlaceholderUpdater.Object); - - var result = await controller.WhatLevelIsTheQualification(new RadioQuestionModel - { - Option = "6" - }); - - result.Should().NotBeNull(); - - var resultType = result as RedirectToActionResult; - resultType.Should().NotBeNull(); - - resultType!.ActionName.Should().Be("Level6QualificationPost2014"); - resultType.ControllerName.Should().Be("Advice"); - } - - [TestMethod] - public async Task Post_WhatLevelIsTheQualification_Level7_ReturnsRedirectResponse() - { - var mockLogger = new Mock>(); - var mockContentService = new Mock(); - var mockContentParser = new Mock(); - var mockUserJourneyCookieService = new Mock(); - var mockRepository = new Mock(); - var mockQuestionModelValidator = new Mock(); - var mockPlaceholderUpdater = new Mock(); - - var controller = new QuestionsController(mockLogger.Object, mockContentService.Object, mockContentParser.Object, - mockUserJourneyCookieService.Object, mockRepository.Object, - mockQuestionModelValidator.Object, mockPlaceholderUpdater.Object); - + var result = await controller.WhatLevelIsTheQualification(new RadioQuestionModel { Option = "7" }); - + result.Should().NotBeNull(); - + var resultType = result as RedirectToActionResult; resultType.Should().NotBeNull(); - - resultType!.ActionName.Should().Be(nameof(AdviceController.QualificationLevel7)); + + resultType!.ActionName.Should().Be("Level7QualificationPost2014"); resultType.ControllerName.Should().Be("Advice"); } - + [TestMethod] public async Task WhatIsTheAwardingOrganisation_ContentServiceReturnsNoQuestionPage_RedirectsToErrorPage() { diff --git a/tests/Dfe.EarlyYearsQualification.UnitTests/Dfe.EarlyYearsQualification.UnitTests.csproj b/tests/Dfe.EarlyYearsQualification.UnitTests/Dfe.EarlyYearsQualification.UnitTests.csproj index 89cea36ce..036e8b988 100644 --- a/tests/Dfe.EarlyYearsQualification.UnitTests/Dfe.EarlyYearsQualification.UnitTests.csproj +++ b/tests/Dfe.EarlyYearsQualification.UnitTests/Dfe.EarlyYearsQualification.UnitTests.csproj @@ -10,11 +10,11 @@ - - + + - - + + diff --git a/tests/Dfe.EarlyYearsQualification.UnitTests/Mappers/ConfirmQualificationPageMapperTests.cs b/tests/Dfe.EarlyYearsQualification.UnitTests/Mappers/ConfirmQualificationPageMapperTests.cs index 1473c6cd6..e9ca51443 100644 --- a/tests/Dfe.EarlyYearsQualification.UnitTests/Mappers/ConfirmQualificationPageMapperTests.cs +++ b/tests/Dfe.EarlyYearsQualification.UnitTests/Mappers/ConfirmQualificationPageMapperTests.cs @@ -8,28 +8,9 @@ namespace Dfe.EarlyYearsQualification.UnitTests.Mappers; public class ConfirmQualificationPageMapperTests { [TestMethod] - public void Map_PassInParameters_ReturnsModel() + public void Map_PassInParameters_NoAdditionalRequirementQuestions_ReturnsModel() { - var content = new ConfirmQualificationPage - { - Heading = "Heading", - Options = [new Option { Label = "Label", Value = "Value " }], - ErrorText = "Error text", - LevelLabel = "Level label", - QualificationLabel = "Qualification label", - RadioHeading = "Radio heading", - DateAddedLabel = "Date added label", - AwardingOrganisationLabel = "Awarding organisation label", - ErrorBannerHeading = "Error banner heading", - ErrorBannerLink = "Error banner link", - ButtonText = "Back button", - BackButton = new NavigationLink - { - DisplayText = "Back", - OpenInNewTab = true, - Href = "/" - }, - }; + var content = GetConfirmQualificationPageContent(); var qualification = new Qualification("Test-ABC", "QualificationName", "NCFE", 3) { @@ -39,6 +20,52 @@ public void Map_PassInParameters_ReturnsModel() const string postHeadingContentHtml = "Post heading content"; const string variousAwardingOrganisationsExplanationHtml = "Various awarding organisations explanation"; + var result = ConfirmQualificationPageMapper.Map(content, qualification, postHeadingContentHtml, + variousAwardingOrganisationsExplanationHtml); + + result.Should().NotBeNull(); + result.Heading.Should().BeSameAs(content.Heading); + result.Options.Count.Should().Be(1); + result.Options[0].Label.Should().BeSameAs(content.Options[0].Label); + result.Options[0].Value.Should().BeSameAs(content.Options[0].Value); + result.ErrorText.Should().BeSameAs(content.ErrorText); + result.LevelLabel.Should().BeSameAs(content.LevelLabel); + result.QualificationLabel.Should().BeSameAs(content.QualificationLabel); + result.RadioHeading.Should().BeSameAs(content.RadioHeading); + result.DateAddedLabel.Should().BeSameAs(content.DateAddedLabel); + result.AwardingOrganisationLabel.Should().BeSameAs(content.AwardingOrganisationLabel); + result.ErrorBannerHeading.Should().BeSameAs(content.ErrorBannerHeading); + result.ErrorBannerLink.Should().BeSameAs(content.ErrorBannerLink); + result.ButtonText.Should().BeSameAs(content.NoAdditionalRequirementsButtonText); + result.QualificationName.Should().BeSameAs(qualification.QualificationName); + result.QualificationLevel.Should().BeSameAs(qualification.QualificationLevel.ToString()); + result.QualificationId.Should().BeSameAs(qualification.QualificationId); + result.QualificationAwardingOrganisation.Should().BeSameAs(qualification.AwardingOrganisationTitle); + result.QualificationDateAdded.Should().BeSameAs(qualification.FromWhichYear); + result.BackButton.Should().BeEquivalentTo(content.BackButton, options => options.Excluding(x => x!.Sys)); + result.PostHeadingContent.Should().BeSameAs(postHeadingContentHtml); + result.VariousAwardingOrganisationsExplanation.Should().BeSameAs(variousAwardingOrganisationsExplanationHtml); + result.ShowAnswerDisclaimerText.Should().BeTrue(); + result.AnswerDisclaimerText.Should().BeSameAs(content.AnswerDisclaimerText); + } + + [TestMethod] + public void Map_PassInParameters_HasAdditionalRequirementQuestions_ReturnsModel() + { + var content = GetConfirmQualificationPageContent(); + + var qualification = new Qualification("Test-ABC", "QualificationName", "NCFE", 3) + { + FromWhichYear = "Sep-16", + AdditionalRequirementQuestions = new List + { + new () + } + }; + + const string postHeadingContentHtml = "Post heading content"; + const string variousAwardingOrganisationsExplanationHtml = "Various awarding organisations explanation"; + var result = ConfirmQualificationPageMapper.Map(content, qualification, postHeadingContentHtml, variousAwardingOrganisationsExplanationHtml); @@ -61,8 +88,36 @@ public void Map_PassInParameters_ReturnsModel() result.QualificationId.Should().BeSameAs(qualification.QualificationId); result.QualificationAwardingOrganisation.Should().BeSameAs(qualification.AwardingOrganisationTitle); result.QualificationDateAdded.Should().BeSameAs(qualification.FromWhichYear); - result.BackButton.Should().BeEquivalentTo(content.BackButton, options => options.Excluding(x => x.Sys)); + result.BackButton.Should().BeEquivalentTo(content.BackButton, options => options.Excluding(x => x!.Sys)); result.PostHeadingContent.Should().BeSameAs(postHeadingContentHtml); result.VariousAwardingOrganisationsExplanation.Should().BeSameAs(variousAwardingOrganisationsExplanationHtml); + result.ShowAnswerDisclaimerText.Should().BeFalse(); + result.AnswerDisclaimerText.Should().BeSameAs(content.AnswerDisclaimerText); + } + + private static ConfirmQualificationPage GetConfirmQualificationPageContent() + { + return new ConfirmQualificationPage + { + Heading = "Heading", + Options = [new Option { Label = "Label", Value = "Value " }], + ErrorText = "Error text", + LevelLabel = "Level label", + QualificationLabel = "Qualification label", + RadioHeading = "Radio heading", + DateAddedLabel = "Date added label", + AwardingOrganisationLabel = "Awarding organisation label", + ErrorBannerHeading = "Error banner heading", + ErrorBannerLink = "Error banner link", + ButtonText = "Back button", + BackButton = new NavigationLink + { + DisplayText = "Back", + OpenInNewTab = true, + Href = "/" + }, + NoAdditionalRequirementsButtonText = "Get result", + AnswerDisclaimerText = "Disclaimer text" + }; } } \ No newline at end of file diff --git a/tests/Dfe.EarlyYearsQualification.UnitTests/Mocks/MockContentfulServiceTests.cs b/tests/Dfe.EarlyYearsQualification.UnitTests/Mocks/MockContentfulServiceTests.cs index 9275e59dc..d8537ee4f 100644 --- a/tests/Dfe.EarlyYearsQualification.UnitTests/Mocks/MockContentfulServiceTests.cs +++ b/tests/Dfe.EarlyYearsQualification.UnitTests/Mocks/MockContentfulServiceTests.cs @@ -97,52 +97,13 @@ public async Task GetAdvicePage_QualificationNotOnTheList_ReturnsExpectedDetails result.Body!.Content[0].Should().BeAssignableTo() .Which.Content.Should().ContainSingle(x => ((Text)x).Value == "Test Advice Page Body"); } - - [TestMethod] - public async Task GetAdvicePage_QualificationLevel7_ReturnsExpectedDetails() - { - var contentfulService = new MockContentfulService(); - - var result = await contentfulService.GetAdvicePage(AdvicePages.QualificationLevel7); - result.Should().NotBeNull(); - result.Should().BeAssignableTo(); - result!.Heading.Should().Be("Qualification at Level 7"); - result.Body!.Content[0].Should().BeAssignableTo() - .Which.Content.Should().ContainSingle(x => ((Text)x).Value == "Test Advice Page Body"); - } - - [TestMethod] - public async Task GetAdvicePage_Level6QualificationPre2014_ReturnsExpectedDetails() - { - var contentfulService = new MockContentfulService(); - - var result = await contentfulService.GetAdvicePage(AdvicePages.Level6QualificationPre2014); - result.Should().NotBeNull(); - result.Should().BeAssignableTo(); - result!.Heading.Should().NotBeNullOrEmpty(); - result.Body!.Content[0].Should().BeAssignableTo() - .Which.Content.Should().ContainSingle(x => ((Text)x).Value == "Test Advice Page Body"); - } - - [TestMethod] - public async Task GetAdvicePage_Level6QualificationPost2014_ReturnsExpectedDetails() - { - var contentfulService = new MockContentfulService(); - - var result = await contentfulService.GetAdvicePage(AdvicePages.Level6QualificationPost2014); - result.Should().NotBeNull(); - result.Should().BeAssignableTo(); - result!.Heading.Should().NotBeNullOrEmpty(); - result.Body!.Content[0].Should().BeAssignableTo() - .Which.Content.Should().ContainSingle(x => ((Text)x).Value == "Test Advice Page Body"); - } - + [TestMethod] - public async Task GetAdvicePage_privacyPolicy_ReturnsExpectedDetails() + public async Task GetAdvicePage_Level7QualificationPost2014_ReturnsExpectedDetails() { var contentfulService = new MockContentfulService(); - - var result = await contentfulService.GetAdvicePage(AdvicePages.TemporaryPrivacyPolicy); + + var result = await contentfulService.GetAdvicePage(AdvicePages.Level7QualificationPost2014); result.Should().NotBeNull(); result.Should().BeAssignableTo(); result!.Heading.Should().NotBeNullOrEmpty(); diff --git a/tests/Dfe.EarlyYearsQualification.UnitTests/Mocks/MockQualificationsRepositoryTests.cs b/tests/Dfe.EarlyYearsQualification.UnitTests/Mocks/MockQualificationsRepositoryTests.cs index bc2255851..ec9344494 100644 --- a/tests/Dfe.EarlyYearsQualification.UnitTests/Mocks/MockQualificationsRepositoryTests.cs +++ b/tests/Dfe.EarlyYearsQualification.UnitTests/Mocks/MockQualificationsRepositoryTests.cs @@ -135,6 +135,256 @@ public async Task GetQualificationById_ReturnsExpectedDetails() result.RatioRequirements[0].RequirementForQtsEtcBefore2014.Should().BeNull(); result.RatioRequirements[0].RequirementForQtsEtcAfter2014.Should().BeNull(); } + + [TestMethod] + public async Task GetQualificationById_EYQ108_ReturnsExpectedDetails() + { + var repository = new MockQualificationsRepository(); + + var result = await repository.GetById("eyq-108"); + result.Should().NotBeNull(); + result.Should().BeAssignableTo(); + result!.AdditionalRequirements.Should().NotBeNullOrEmpty(); + result.AwardingOrganisationTitle.Should().NotBeNullOrEmpty(); + result.FromWhichYear.Should().NotBeNullOrEmpty(); + result.QualificationId.Should().NotBeNullOrEmpty(); + result.QualificationLevel.Should().BeGreaterThan(0); + result.QualificationName.Should().NotBeNullOrEmpty(); + result.QualificationNumber.Should().NotBeNullOrEmpty(); + result.ToWhichYear.Should().NotBeNullOrEmpty(); + result.AdditionalRequirementQuestions.Should().NotBeNull(); + result.AdditionalRequirementQuestions!.Count.Should().Be(2); + result.AdditionalRequirementQuestions[0].Question.Should().NotBeNullOrEmpty(); + result.AdditionalRequirementQuestions[0].HintText.Should().NotBeNullOrEmpty(); + result.AdditionalRequirementQuestions[0].ConfirmationStatement.Should().NotBeNullOrEmpty(); + result.AdditionalRequirementQuestions[0].DetailsHeading.Should().NotBeNullOrEmpty(); + result.AdditionalRequirementQuestions[0].DetailsHeading.Should().NotBeNullOrEmpty(); + result.AdditionalRequirementQuestions[0].Answers.Should().NotBeNull(); + result.AdditionalRequirementQuestions[0].Answers.Count.Should().Be(2); + result.AdditionalRequirementQuestions[0].Answers[0].Label.Should().NotBeNullOrEmpty(); + result.AdditionalRequirementQuestions[0].Answers[0].Value.Should().NotBeNullOrEmpty(); + result.AdditionalRequirementQuestions[0].Answers[1].Label.Should().NotBeNullOrEmpty(); + result.AdditionalRequirementQuestions[0].Answers[1].Value.Should().NotBeNullOrEmpty(); + result.AdditionalRequirementQuestions[1].Question.Should().NotBeNullOrEmpty(); + result.AdditionalRequirementQuestions[1].HintText.Should().NotBeNullOrEmpty(); + result.AdditionalRequirementQuestions[1].ConfirmationStatement.Should().NotBeNullOrEmpty(); + result.AdditionalRequirementQuestions[1].DetailsHeading.Should().NotBeNullOrEmpty(); + result.AdditionalRequirementQuestions[1].DetailsHeading.Should().NotBeNullOrEmpty(); + result.AdditionalRequirementQuestions[1].Answers.Should().NotBeNull(); + result.AdditionalRequirementQuestions[1].Answers.Count.Should().Be(2); + result.AdditionalRequirementQuestions[1].Answers[0].Label.Should().NotBeNullOrEmpty(); + result.AdditionalRequirementQuestions[1].Answers[0].Value.Should().NotBeNullOrEmpty(); + result.AdditionalRequirementQuestions[1].Answers[1].Label.Should().NotBeNullOrEmpty(); + result.AdditionalRequirementQuestions[1].Answers[1].Value.Should().NotBeNullOrEmpty(); + result.RatioRequirements.Should().NotBeNullOrEmpty(); + result.RatioRequirements!.Count.Should().Be(4); + result.RatioRequirements[0].RatioRequirementName.Should().Be(RatioRequirements.Level2RatioRequirementName); + result.RatioRequirements[0].FullAndRelevantForLevel2Before2014.Should().BeFalse(); + result.RatioRequirements[0].FullAndRelevantForLevel2After2014.Should().BeFalse(); + result.RatioRequirements[0].FullAndRelevantForLevel3Before2014.Should().BeFalse(); + result.RatioRequirements[0].FullAndRelevantForLevel3After2014.Should().BeFalse(); + result.RatioRequirements[0].FullAndRelevantForLevel4Before2014.Should().BeFalse(); + result.RatioRequirements[0].FullAndRelevantForLevel4After2014.Should().BeFalse(); + result.RatioRequirements[0].FullAndRelevantForLevel5Before2014.Should().BeFalse(); + result.RatioRequirements[0].FullAndRelevantForLevel5After2014.Should().BeFalse(); + result.RatioRequirements[0].FullAndRelevantForLevel6Before2014.Should().BeFalse(); + result.RatioRequirements[0].FullAndRelevantForLevel6After2014.Should().BeTrue(); + result.RatioRequirements[0].FullAndRelevantForLevel7Before2014.Should().BeFalse(); + result.RatioRequirements[0].FullAndRelevantForLevel7After2014.Should().BeFalse(); + result.RatioRequirements[0].FullAndRelevantForQtsEtcBefore2014.Should().BeFalse(); + result.RatioRequirements[0].FullAndRelevantForQtsEtcAfter2014.Should().BeTrue(); + result.RatioRequirements[0].RequirementForLevel2Before2014.Should().BeNull(); + result.RatioRequirements[0].RequirementForLevel2After2014.Should().BeNull(); + result.RatioRequirements[0].RequirementForLevel3Before2014.Should().BeNull(); + result.RatioRequirements[0].RequirementForLevel3After2014.Should().BeNull(); + result.RatioRequirements[0].RequirementForLevel4Before2014.Should().BeNull(); + result.RatioRequirements[0].RequirementForLevel4After2014.Should().BeNull(); + result.RatioRequirements[0].RequirementForLevel5Before2014.Should().BeNull(); + result.RatioRequirements[0].RequirementForLevel5After2014.Should().BeNull(); + result.RatioRequirements[0].RequirementForLevel6Before2014.Should().BeNull(); + result.RatioRequirements[0].RequirementForLevel6After2014.Should().BeNull(); + result.RatioRequirements[0].RequirementForLevel7Before2014.Should().BeNull(); + result.RatioRequirements[0].RequirementForLevel7After2014.Should().BeNull(); + result.RatioRequirements[0].RequirementForQtsEtcBefore2014.Should().BeNull(); + result.RatioRequirements[0].RequirementForQtsEtcAfter2014.Should().BeNull(); + } + + [TestMethod] + public async Task GetQualificationById_EYQ115_ReturnsExpectedDetails() + { + var repository = new MockQualificationsRepository(); + + var result = await repository.GetById("eyq-115"); + result.Should().NotBeNull(); + result.Should().BeAssignableTo(); + result!.AdditionalRequirements.Should().NotBeNullOrEmpty(); + result.AwardingOrganisationTitle.Should().NotBeNullOrEmpty(); + result.FromWhichYear.Should().NotBeNullOrEmpty(); + result.QualificationId.Should().NotBeNullOrEmpty(); + result.QualificationId.Should().Be("EYQ-115"); + result.QualificationLevel.Should().BeGreaterThan(0); + result.QualificationName.Should().NotBeNullOrEmpty(); + result.QualificationNumber.Should().NotBeNullOrEmpty(); + result.ToWhichYear.Should().NotBeNullOrEmpty(); + result.AdditionalRequirementQuestions.Should().BeNull(); + result.RatioRequirements.Should().NotBeNullOrEmpty(); + result.RatioRequirements!.Count.Should().Be(4); + result.RatioRequirements[0].RatioRequirementName.Should().Be(RatioRequirements.Level2RatioRequirementName); + result.RatioRequirements[0].FullAndRelevantForLevel2Before2014.Should().BeFalse(); + result.RatioRequirements[0].FullAndRelevantForLevel2After2014.Should().BeFalse(); + result.RatioRequirements[0].FullAndRelevantForLevel3Before2014.Should().BeFalse(); + result.RatioRequirements[0].FullAndRelevantForLevel3After2014.Should().BeTrue(); + result.RatioRequirements[0].FullAndRelevantForLevel4Before2014.Should().BeFalse(); + result.RatioRequirements[0].FullAndRelevantForLevel4After2014.Should().BeFalse(); + result.RatioRequirements[0].FullAndRelevantForLevel5Before2014.Should().BeFalse(); + result.RatioRequirements[0].FullAndRelevantForLevel5After2014.Should().BeFalse(); + result.RatioRequirements[0].FullAndRelevantForLevel6Before2014.Should().BeFalse(); + result.RatioRequirements[0].FullAndRelevantForLevel6After2014.Should().BeFalse(); + result.RatioRequirements[0].FullAndRelevantForLevel7Before2014.Should().BeFalse(); + result.RatioRequirements[0].FullAndRelevantForLevel7After2014.Should().BeFalse(); + result.RatioRequirements[0].FullAndRelevantForQtsEtcBefore2014.Should().BeFalse(); + result.RatioRequirements[0].FullAndRelevantForQtsEtcAfter2014.Should().BeFalse(); + result.RatioRequirements[0].RequirementForLevel2Before2014.Should().BeNull(); + result.RatioRequirements[0].RequirementForLevel2After2014.Should().BeNull(); + result.RatioRequirements[0].RequirementForLevel3Before2014.Should().BeNull(); + result.RatioRequirements[0].RequirementForLevel3After2014.Should().BeNull(); + result.RatioRequirements[0].RequirementForLevel4Before2014.Should().BeNull(); + result.RatioRequirements[0].RequirementForLevel4After2014.Should().BeNull(); + result.RatioRequirements[0].RequirementForLevel5Before2014.Should().BeNull(); + result.RatioRequirements[0].RequirementForLevel5After2014.Should().BeNull(); + result.RatioRequirements[0].RequirementForLevel6Before2014.Should().BeNull(); + result.RatioRequirements[0].RequirementForLevel6After2014.Should().BeNull(); + result.RatioRequirements[0].RequirementForLevel7Before2014.Should().BeNull(); + result.RatioRequirements[0].RequirementForLevel7After2014.Should().BeNull(); + result.RatioRequirements[0].RequirementForQtsEtcBefore2014.Should().BeNull(); + result.RatioRequirements[0].RequirementForQtsEtcAfter2014.Should().BeNull(); + } + + [TestMethod] + public async Task GetQualificationById_EYQ250_ReturnsExpectedDetails() + { + var repository = new MockQualificationsRepository(); + + var result = await repository.GetById("eyq-250"); + result.Should().NotBeNull(); + result.Should().BeAssignableTo(); + result!.AdditionalRequirements.Should().NotBeNullOrEmpty(); + result.AwardingOrganisationTitle.Should().NotBeNullOrEmpty(); + result.FromWhichYear.Should().NotBeNullOrEmpty(); + result.QualificationId.Should().NotBeNullOrEmpty(); + result.QualificationId.Should().Be("EYQ-250"); + result.QualificationLevel.Should().BeGreaterThan(0); + result.QualificationName.Should().NotBeNullOrEmpty(); + result.QualificationNumber.Should().NotBeNullOrEmpty(); + result.ToWhichYear.Should().NotBeNullOrEmpty(); + result.AdditionalRequirementQuestions.Should().NotBeNull(); + result.AdditionalRequirementQuestions!.Count.Should().Be(2); + result.AdditionalRequirementQuestions[0].Question.Should().NotBeNullOrEmpty(); + result.AdditionalRequirementQuestions[0].HintText.Should().NotBeNullOrEmpty(); + result.AdditionalRequirementQuestions[0].ConfirmationStatement.Should().NotBeNullOrEmpty(); + result.AdditionalRequirementQuestions[0].DetailsHeading.Should().NotBeNullOrEmpty(); + result.AdditionalRequirementQuestions[0].DetailsHeading.Should().NotBeNullOrEmpty(); + result.AdditionalRequirementQuestions[0].Answers.Should().NotBeNull(); + result.AdditionalRequirementQuestions[0].Answers.Count.Should().Be(2); + result.AdditionalRequirementQuestions[0].Answers[0].Label.Should().NotBeNullOrEmpty(); + result.AdditionalRequirementQuestions[0].Answers[0].Value.Should().NotBeNullOrEmpty(); + result.AdditionalRequirementQuestions[0].Answers[1].Label.Should().NotBeNullOrEmpty(); + result.AdditionalRequirementQuestions[0].Answers[1].Value.Should().NotBeNullOrEmpty(); + result.AdditionalRequirementQuestions[1].Question.Should().NotBeNullOrEmpty(); + result.AdditionalRequirementQuestions[1].HintText.Should().NotBeNullOrEmpty(); + result.AdditionalRequirementQuestions[1].ConfirmationStatement.Should().NotBeNullOrEmpty(); + result.AdditionalRequirementQuestions[1].DetailsHeading.Should().NotBeNullOrEmpty(); + result.AdditionalRequirementQuestions[1].DetailsHeading.Should().NotBeNullOrEmpty(); + result.AdditionalRequirementQuestions[1].Answers.Should().NotBeNull(); + result.AdditionalRequirementQuestions[1].Answers.Count.Should().Be(2); + result.AdditionalRequirementQuestions[1].Answers[0].Label.Should().NotBeNullOrEmpty(); + result.AdditionalRequirementQuestions[1].Answers[0].Value.Should().NotBeNullOrEmpty(); + result.AdditionalRequirementQuestions[1].Answers[1].Label.Should().NotBeNullOrEmpty(); + result.AdditionalRequirementQuestions[1].Answers[1].Value.Should().NotBeNullOrEmpty(); + result.RatioRequirements.Should().NotBeNullOrEmpty(); + result.RatioRequirements!.Count.Should().Be(4); + result.RatioRequirements[0].RatioRequirementName.Should().Be(RatioRequirements.Level2RatioRequirementName); + result.RatioRequirements[0].FullAndRelevantForLevel2Before2014.Should().BeFalse(); + result.RatioRequirements[0].FullAndRelevantForLevel2After2014.Should().BeFalse(); + result.RatioRequirements[0].FullAndRelevantForLevel3Before2014.Should().BeFalse(); + result.RatioRequirements[0].FullAndRelevantForLevel3After2014.Should().BeTrue(); + result.RatioRequirements[0].FullAndRelevantForLevel4Before2014.Should().BeFalse(); + result.RatioRequirements[0].FullAndRelevantForLevel4After2014.Should().BeFalse(); + result.RatioRequirements[0].FullAndRelevantForLevel5Before2014.Should().BeFalse(); + result.RatioRequirements[0].FullAndRelevantForLevel5After2014.Should().BeFalse(); + result.RatioRequirements[0].FullAndRelevantForLevel6Before2014.Should().BeFalse(); + result.RatioRequirements[0].FullAndRelevantForLevel6After2014.Should().BeFalse(); + result.RatioRequirements[0].FullAndRelevantForLevel7Before2014.Should().BeFalse(); + result.RatioRequirements[0].FullAndRelevantForLevel7After2014.Should().BeFalse(); + result.RatioRequirements[0].FullAndRelevantForQtsEtcBefore2014.Should().BeFalse(); + result.RatioRequirements[0].FullAndRelevantForQtsEtcAfter2014.Should().BeFalse(); + result.RatioRequirements[0].RequirementForLevel2Before2014.Should().BeNull(); + result.RatioRequirements[0].RequirementForLevel2After2014.Should().BeNull(); + result.RatioRequirements[0].RequirementForLevel3Before2014.Should().BeNull(); + result.RatioRequirements[0].RequirementForLevel3After2014.Should().BeNull(); + result.RatioRequirements[0].RequirementForLevel4Before2014.Should().BeNull(); + result.RatioRequirements[0].RequirementForLevel4After2014.Should().BeNull(); + result.RatioRequirements[0].RequirementForLevel5Before2014.Should().BeNull(); + result.RatioRequirements[0].RequirementForLevel5After2014.Should().BeNull(); + result.RatioRequirements[0].RequirementForLevel6Before2014.Should().BeNull(); + result.RatioRequirements[0].RequirementForLevel6After2014.Should().BeNull(); + result.RatioRequirements[0].RequirementForLevel7Before2014.Should().BeNull(); + result.RatioRequirements[0].RequirementForLevel7After2014.Should().BeNull(); + result.RatioRequirements[0].RequirementForQtsEtcBefore2014.Should().BeNull(); + result.RatioRequirements[0].RequirementForQtsEtcAfter2014.Should().BeNull(); + } + + [TestMethod] + public async Task GetQualificationById_EYQ114_ReturnsExpectedDetails() + { + var repository = new MockQualificationsRepository(); + + var result = await repository.GetById("eyq-114"); + result.Should().NotBeNull(); + result.Should().BeAssignableTo(); + result!.AdditionalRequirements.Should().BeNull(); + result.AwardingOrganisationTitle.Should().NotBeNullOrEmpty(); + result.FromWhichYear.Should().NotBeNullOrEmpty(); + result.QualificationId.Should().NotBeNullOrEmpty(); + result.QualificationId.Should().Be("EYQ-114"); + result.QualificationLevel.Should().BeGreaterThan(0); + result.QualificationName.Should().NotBeNullOrEmpty(); + result.QualificationNumber.Should().NotBeNullOrEmpty(); + result.ToWhichYear.Should().NotBeNullOrEmpty(); + result.AdditionalRequirementQuestions.Should().BeNull(); + result.RatioRequirements.Should().NotBeNullOrEmpty(); + result.RatioRequirements!.Count.Should().Be(4); + result.RatioRequirements[0].RatioRequirementName.Should().Be(RatioRequirements.Level2RatioRequirementName); + result.RatioRequirements[0].FullAndRelevantForLevel2Before2014.Should().BeFalse(); + result.RatioRequirements[0].FullAndRelevantForLevel2After2014.Should().BeFalse(); + result.RatioRequirements[0].FullAndRelevantForLevel3Before2014.Should().BeFalse(); + result.RatioRequirements[0].FullAndRelevantForLevel3After2014.Should().BeFalse(); + result.RatioRequirements[0].FullAndRelevantForLevel4Before2014.Should().BeFalse(); + result.RatioRequirements[0].FullAndRelevantForLevel4After2014.Should().BeFalse(); + result.RatioRequirements[0].FullAndRelevantForLevel5Before2014.Should().BeFalse(); + result.RatioRequirements[0].FullAndRelevantForLevel5After2014.Should().BeFalse(); + result.RatioRequirements[0].FullAndRelevantForLevel6Before2014.Should().BeFalse(); + result.RatioRequirements[0].FullAndRelevantForLevel6After2014.Should().BeFalse(); + result.RatioRequirements[0].FullAndRelevantForLevel7Before2014.Should().BeFalse(); + result.RatioRequirements[0].FullAndRelevantForLevel7After2014.Should().BeFalse(); + result.RatioRequirements[0].FullAndRelevantForQtsEtcBefore2014.Should().BeFalse(); + result.RatioRequirements[0].FullAndRelevantForQtsEtcAfter2014.Should().BeFalse(); + result.RatioRequirements[0].RequirementForLevel2Before2014.Should().BeNull(); + result.RatioRequirements[0].RequirementForLevel2After2014.Should().BeNull(); + result.RatioRequirements[0].RequirementForLevel3Before2014.Should().BeNull(); + result.RatioRequirements[0].RequirementForLevel3After2014.Should().BeNull(); + result.RatioRequirements[0].RequirementForLevel4Before2014.Should().BeNull(); + result.RatioRequirements[0].RequirementForLevel4After2014.Should().BeNull(); + result.RatioRequirements[0].RequirementForLevel5Before2014.Should().BeNull(); + result.RatioRequirements[0].RequirementForLevel5After2014.Should().BeNull(); + result.RatioRequirements[0].RequirementForLevel6Before2014.Should().BeNull(); + result.RatioRequirements[0].RequirementForLevel6After2014.Should().BeNull(); + result.RatioRequirements[0].RequirementForLevel7Before2014.Should().BeNull(); + result.RatioRequirements[0].RequirementForLevel7After2014.Should().BeNull(); + result.RatioRequirements[0].RequirementForQtsEtcBefore2014.Should().BeNull(); + result.RatioRequirements[0].RequirementForQtsEtcAfter2014.Should().BeNull(); + result.RatioRequirements[0].RequirementForLevel2BetweenSept14AndAug19.Should().NotBeNull(); + } [TestMethod] public async Task GetQualifications_ReturnsAListOfQualifications() diff --git a/tests/Dfe.EarlyYearsQualification.UnitTests/Services/UserJourneyCookieServiceTests.cs b/tests/Dfe.EarlyYearsQualification.UnitTests/Services/UserJourneyCookieServiceTests.cs index 802dc2804..8716a5f68 100644 --- a/tests/Dfe.EarlyYearsQualification.UnitTests/Services/UserJourneyCookieServiceTests.cs +++ b/tests/Dfe.EarlyYearsQualification.UnitTests/Services/UserJourneyCookieServiceTests.cs @@ -427,6 +427,182 @@ public void GetQualificationStartedBeforeSept2019_CookieHasValidValueIn2015_Retu service.WasStartedBeforeSeptember2014().Should().BeFalse(); } + [TestMethod] + public void WasStartedOnOrAfterSeptember2014_CookieValueIsEmpty_Throws() + { + var existingModel = new UserJourneyCookieService.UserJourneyModel + { + WhenWasQualificationStarted = string.Empty + }; + + var mockHttpContextAccessor = SetCookieManagerWithExistingCookie(existingModel); + var mockLogger = new Mock>(); + + var service = new UserJourneyCookieService(mockLogger.Object, mockHttpContextAccessor.cookieManager.Object); + + var action = () => service.WasStartedOnOrAfterSeptember2014(); + + action.Should().Throw(); + } + + [TestMethod] + public void WasStartedOnOrAfterSeptember2014_CookieHasInvalidValue_Throws() + { + var existingModel = new UserJourneyCookieService.UserJourneyModel + { + WhenWasQualificationStarted = "4" + }; + + var mockHttpContextAccessor = SetCookieManagerWithExistingCookie(existingModel); + var mockLogger = new Mock>(); + + var service = new UserJourneyCookieService(mockLogger.Object, mockHttpContextAccessor.cookieManager.Object); + + var action = () => service.WasStartedOnOrAfterSeptember2014(); + + action.Should().Throw(); + } + + [TestMethod] + public void WasStartedOnOrAfterSeptember2014_CookieHasValidValueIn2013_ReturnsFalse() + { + var existingModel = new UserJourneyCookieService.UserJourneyModel + { + WhenWasQualificationStarted = "12/2013" + }; + + var mockHttpContextAccessor = SetCookieManagerWithExistingCookie(existingModel); + var mockLogger = new Mock>(); + + var service = new UserJourneyCookieService(mockLogger.Object, mockHttpContextAccessor.cookieManager.Object); + + service.WasStartedOnOrAfterSeptember2014().Should().BeFalse(); + } + + [TestMethod] + public void WasStartedOnOrAfterSeptember2014_CookieHasValidValueInAugust2014_ReturnsFalse() + { + var existingModel = new UserJourneyCookieService.UserJourneyModel + { + WhenWasQualificationStarted = "8/2014" + }; + + var mockHttpContextAccessor = SetCookieManagerWithExistingCookie(existingModel); + var mockLogger = new Mock>(); + + var service = new UserJourneyCookieService(mockLogger.Object, mockHttpContextAccessor.cookieManager.Object); + + service.WasStartedOnOrAfterSeptember2014().Should().BeFalse(); + } + + [TestMethod] + public void WasStartedOnOrAfterSeptember2014_CookieHasValidValueInSeptember2014_ReturnsTrue() + { + var existingModel = new UserJourneyCookieService.UserJourneyModel + { + WhenWasQualificationStarted = "9/2014" + }; + + var mockHttpContextAccessor = SetCookieManagerWithExistingCookie(existingModel); + var mockLogger = new Mock>(); + + var service = new UserJourneyCookieService(mockLogger.Object, mockHttpContextAccessor.cookieManager.Object); + + service.WasStartedOnOrAfterSeptember2014().Should().BeTrue(); + } + + [TestMethod] + public void WasStartedOnOrAfterSeptember2014_CookieHasValidValueIn2015_ReturnsTrue() + { + var existingModel = new UserJourneyCookieService.UserJourneyModel + { + WhenWasQualificationStarted = "1/2015" + }; + + var mockHttpContextAccessor = SetCookieManagerWithExistingCookie(existingModel); + var mockLogger = new Mock>(); + + var service = new UserJourneyCookieService(mockLogger.Object, mockHttpContextAccessor.cookieManager.Object); + + service.WasStartedOnOrAfterSeptember2014().Should().BeTrue(); + } + + [TestMethod] + public void WasStartedBetweenSept2014AndAug2019_CookieValueIsEmpty_Throws() + { + var existingModel = new UserJourneyCookieService.UserJourneyModel + { + WhenWasQualificationStarted = string.Empty + }; + + var mockHttpContextAccessor = SetCookieManagerWithExistingCookie(existingModel); + var mockLogger = new Mock>(); + + var service = new UserJourneyCookieService(mockLogger.Object, mockHttpContextAccessor.cookieManager.Object); + + var action = () => service.WasStartedBetweenSeptember2014AndAugust2019(); + + action.Should().Throw(); + } + + [TestMethod] + public void WasStartedBetweenSept2014AndAug2019_CookieHasInvalidValue_Throws() + { + var existingModel = new UserJourneyCookieService.UserJourneyModel + { + WhenWasQualificationStarted = "4" + }; + + var mockHttpContextAccessor = SetCookieManagerWithExistingCookie(existingModel); + var mockLogger = new Mock>(); + + var service = new UserJourneyCookieService(mockLogger.Object, mockHttpContextAccessor.cookieManager.Object); + + var action = () => service.WasStartedBetweenSeptember2014AndAugust2019(); + + action.Should().Throw(); + } + + [TestMethod] + [DataRow("8/2014")] + [DataRow("9/2019")] + [DataRow("5/2002")] + [DataRow("6/2024")] + public void WasStartedBetweenSept2014AndAug2019_CookieValueOutsideOfRange_ReturnsFalse(string dateStarted) + { + var existingModel = new UserJourneyCookieService.UserJourneyModel + { + WhenWasQualificationStarted = dateStarted + }; + + var mockHttpContextAccessor = SetCookieManagerWithExistingCookie(existingModel); + var mockLogger = new Mock>(); + + var service = new UserJourneyCookieService(mockLogger.Object, mockHttpContextAccessor.cookieManager.Object); + + service.WasStartedBetweenSeptember2014AndAugust2019().Should().BeFalse(); + } + + [TestMethod] + [DataRow("9/2014")] + [DataRow("8/2019")] + [DataRow("11/2016")] + [DataRow("4/2018")] + public void WasStartedBetweenSept2014AndAug2019_CookieValueInsideOfRange_ReturnsTrue(string dateStarted) + { + var existingModel = new UserJourneyCookieService.UserJourneyModel + { + WhenWasQualificationStarted = dateStarted + }; + + var mockHttpContextAccessor = SetCookieManagerWithExistingCookie(existingModel); + var mockLogger = new Mock>(); + + var service = new UserJourneyCookieService(mockLogger.Object, mockHttpContextAccessor.cookieManager.Object); + + service.WasStartedBetweenSeptember2014AndAugust2019().Should().BeTrue(); + } + [TestMethod] public void GetQualificationStartedBetween2014And2019_CookieValueIsEmpty_Throws() {