diff --git a/.github/workflows/build-and-deploy.yml b/.github/workflows/build-and-deploy.yml index c947aada7..1f9329263 100644 --- a/.github/workflows/build-and-deploy.yml +++ b/.github/workflows/build-and-deploy.yml @@ -153,7 +153,7 @@ jobs: - name: Configure application run: | cd usagov-2021 - sed -i "s/memory_limit = 1G/memory_limit = 500M/g" .docker/src-cms/etc/php81/php.ini + sed -i "s/memory_limit = 1G/memory_limit = 500M/g" .docker/src-cms/etc/php83/php.ini mv .docker/Dockerfile-cms . sed -i 's/80/8080/g' Dockerfile-cms sed -i 's/ENTRYPOINT/CMD/' Dockerfile-cms @@ -213,6 +213,7 @@ jobs: CR_USERNAME: "${{ secrets.CR_USERNAME }}" PROJECT: "${{ secrets.PROJECT }}" TEST_USER_PASS: "${{ secrets.TEST_USER_PASS }}" + TEST_USER_NAMES: "${{ secrets.TEST_USER_NAMES }}" run: | source ./scripts/pipeline/cloud-gov-login.sh cd usagov-2021 @@ -222,6 +223,16 @@ jobs: cf add-network-policy ${PROJECT}-waf-${BRANCH} ${PROJECT}-cms-${BRANCH} -s ${PROJECT}-${BRANCH} -o ${CF_ORG} --protocol tcp --port 61443 cf map-route benefit-finder-cms-${BRANCH} apps.internal --hostname benefit-finder-cms-${BRANCH} --app-protocol http1 cd .. - ## sed -i "s#TU_PASS#${TEST_USER_PASS}#g" ./scripts/drush-post-deploy.sh - ## cat ./scripts/drush-post-deploy.sh + # Split the USERS_LIST into an array + IFS=',' read -ra users <<< "${TEST_USER_NAMES}" + # Generate the password reset commands + password_reset_commands="" + for user in "${users[@]}"; do + password_reset_commands+="drush user:password $user \"TU_PASS\"\n" + done + # Make replacement in the drush post deploy with user change password commands + sed -i "s|### USER_PASSWORD_RESET_PLACEHOLDER ###|$password_reset_commands|" ./scripts/drush-post-deploy.sh + # Set the test user's passwords to the value of the github secret value for TEST_USER_PASS. + sed -i "s#TU_PASS#${TEST_USER_PASS}#g" ./scripts/drush-post-deploy.sh + # Execute the drush command script source ./scripts/pipeline/cloud-gov-post-deploy.sh diff --git a/.github/workflows/database-restore.yml b/.github/workflows/database-restore.yml index 8df9b15ea..2cf84e8a2 100644 --- a/.github/workflows/database-restore.yml +++ b/.github/workflows/database-restore.yml @@ -47,11 +47,25 @@ jobs: CF_ORG: "${{ secrets.CF_ORG }}" PROJECT: "${{ secrets.PROJECT }}" DATABASE_BACKUP_BASTION_NAME: "${{ secrets.DATABASE_BACKUP_BASTION_NAME }}" - TEST_USER_PASS: "{{ secrets.TEST_USER_PASS }}" + TEST_USER_PASS: "${{ secrets.TEST_USER_PASS }}" + TEST_USER_NAMES: "${{ secrets.TEST_USER_NAMES }}" run: | export S3_FILE_PATH=${{ github.event.inputs.database_file_override }} source ./scripts/pipeline/s3-backup-download.sh source ./scripts/pipeline/database-restore.sh + # Split the USERS_LIST into an array + IFS=',' read -ra users <<< "${TEST_USER_NAMES}" + # Generate the password reset commands + password_reset_commands="" + for user in "${users[@]}"; do + password_reset_commands+="drush user:password $user \"TU_PASS\"\n" + done + # Make replacement in the drush post deploy with user change password commands + sed -i "s|### USER_PASSWORD_RESET_PLACEHOLDER ###|$password_reset_commands|" ./scripts/drush-post-deploy.sh + # Set the test user's passwords to the value of the github secret value for TEST_USER_PASS. + sed -i "s#TU_PASS#${TEST_USER_PASS}#g" ./scripts/drush-post-deploy.sh + # Execute the drush command script + source ./scripts/pipeline/cloud-gov-post-deploy.sh source ./scripts/pipeline/cloud-gov-post-deploy.sh source ./scripts/pipeline/s3-backup-post-restore.sh stopBastion: diff --git a/.github/workflows/increment_sub_hash.yml b/.github/workflows/increment_sub_hash.yml index c12e5b543..a29623c45 100644 --- a/.github/workflows/increment_sub_hash.yml +++ b/.github/workflows/increment_sub_hash.yml @@ -4,14 +4,55 @@ on: workflow_dispatch: # Allows manual trigger jobs: - update-submodule: + # update-submodule-on-release: + # name: update-submodule-on-release + # runs-on: ubuntu-latest + # + # steps: + # - name: Checkout repository without submodules + # uses: actions/checkout@v4 + # with: + # submodules: false # Do not checkout submodules initially + # fetch-depth: 0 # Fetch all history + # + # - name: Set up Git + # run: | + # git config --global user.name "Xavier Metichecchia" + # git config --global user.email "Xavier.Metichecchia@gsa.gov" + # git config --global pull.rebase false # Ensure merge strategy + # git remote set-url origin https://${{ secrets.HASH_PAT }}@github.com/GSA/px-benefit-finder.git + # + # - name: Update submodules on release branch + # continue-on-error: true + # run: | + # # Checkout the release branch + # git checkout release + # + # # Update and initialize submodules + # git submodule update --init + # git submodule set-branch --branch prod usagov-2021 + # git submodule update --remote || echo "Expected error: Failed to update all submodules." + # + # # Check Git Status + # git add . + # git commit --allow-empty -m "Updated submodule to the latest commit on release branch" + # + # # Commit the changes + # git status + # git --no-pager diff + # + # # Push changes to release branch + # git push origin release + + update-submodule-on-main: + name: update-submodule-on-main runs-on: ubuntu-latest steps: - name: Checkout repository without submodules uses: actions/checkout@v4 with: - submodules: false # Do not checkout submodules initially + # submodules: false # Do not checkout submodules initially fetch-depth: 0 # Fetch all history - name: Set up Git @@ -21,46 +62,44 @@ jobs: git config --global pull.rebase false # Ensure merge strategy git remote set-url origin https://${{ secrets.HASH_PAT }}@github.com/GSA/px-benefit-finder.git - - name: Update submodules on release branch - continue-on-error: true - run: | - # Checkout the release branch - git checkout release - - # Update and initialize submodules - git submodule update --init - git submodule set-branch --branch prod usagov-2021 - git submodule update --remote || echo "Expected error: Failed to update all submodules." - - # Check Git Status - git add . - git commit --allow-empty -m "Updated submodule to the latest commit on release branch" - - # Commit the changes - git status - git --no-pager diff - - # Push changes to release branch - git push origin release - - name: Sync release to main run: | # Switch to the main branch git checkout main + git submodule update --remote + # Pull changes from release into main git pull origin release # Push updated main branch git push origin main - - name: Sync release to dev - run: | - # Switch to the dev branch - git checkout dev - - # Pull changes from release into dev - git pull origin release - - # Push updated dev branch - git push origin dev + # update-submodule-on-dev: + # name: update-submodule-on-dev + # runs-on: ubuntu-latest + # + # steps: + # - name: Checkout repository without submodules + # uses: actions/checkout@v4 + # with: + # # submodules: false # Do not checkout submodules initially + # fetch-depth: 0 # Fetch all history + # + # - name: Set up Git + # run: | + # git config --global user.name "Xavier Metichecchia" + # git config --global user.email "Xavier.Metichecchia@gsa.gov" + # git config --global pull.rebase false # Ensure merge strategy + # git remote set-url origin https://${{ secrets.HASH_PAT }}@github.com/GSA/px-benefit-finder.git + # + # - name: Sync release to dev + # run: | + # # Switch to the dev branch + # git checkout dev + # + # # Pull changes from release into dev + # git pull origin release + # + # # Push updated dev branch + # git push origin dev diff --git a/.vscode/extensions.json b/.vscode/extensions.json index e52209d2b..5c514880e 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -1,5 +1,3 @@ { - "recommendations": [ - "rvest.vs-code-prettier-eslint" - ], + "recommendations": ["github.vscode-github-actions", "esbenp.prettier-vscode"] } diff --git a/.vscode/settings.json b/.vscode/settings.json index e67357140..6317784f6 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,10 +1,9 @@ { - "debug.javascript.autoAttachFilter": "onlyWithFlag", - "editor.renderWhitespace": "none", - "diffEditor.ignoreTrimWhitespace": false, - "editor.defaultFormatter": "rvest.vs-code-prettier-eslint", - "editor.formatOnType": false, - "editor.formatOnSave": true, - "editor.formatOnSaveMode": "file", - "vs-code-prettier-eslint.prettierLast": true, + "debug.javascript.autoAttachFilter": "onlyWithFlag", + "editor.renderWhitespace": "none", + "diffEditor.ignoreTrimWhitespace": false, + "editor.formatOnType": false, + "editor.formatOnSave": true, + "editor.formatOnSaveMode": "file", + "editor.defaultFormatter": "esbenp.prettier-vscode" } diff --git a/benefit-finder/.cspell.json b/benefit-finder/.cspell.json new file mode 100644 index 000000000..3c907ab7d --- /dev/null +++ b/benefit-finder/.cspell.json @@ -0,0 +1,30 @@ +{ + "$schema": "https://raw.githubusercontent.com/streetsidesoftware/cspell/main/cspell.schema.json", + "version": "0.2", + "ignorePaths": [ + "node_modules", + "themes", + "storybook-static", + "dist", + "coverage", + "build", + "src/shared/locales/es/es.json" + ], + "language": "en", + "dictionaryDefinitions": [ + { "name": "content", "path": "./cspell-dictionary/content.txt" }, + { "name": "dev", "path": "./cspell-dictionary/dev.txt" }, + { "name": "es", "path": "./cspell-dictionary/es.txt" }, + { "name": "project", "path": "./cspell-dictionary/project.txt" } + ], + "dictionaries": [ + "html", + "bash", + "css", + "node", + "content", + "dev", + "es", + "project" + ] +} diff --git a/benefit-finder/.lintstagedrc b/benefit-finder/.lintstagedrc index cb3fd40f8..569c868ad 100644 --- a/benefit-finder/.lintstagedrc +++ b/benefit-finder/.lintstagedrc @@ -9,5 +9,8 @@ ], "src/**/*.scss": [ "stylelint --fix" + ], + "**": [ + "cspell lint" ] } diff --git a/benefit-finder/.storybook/main.js b/benefit-finder/.storybook/main.js index 9457c7cdc..11f52d16b 100644 --- a/benefit-finder/.storybook/main.js +++ b/benefit-finder/.storybook/main.js @@ -20,15 +20,16 @@ const config = { viteFinal(config) { return mergeConfig(config, { build: { - chunkSizeWarningLimit: '1000', - rollupOptions: { + chunkSizeWarningLimit: '1000', + rollupOptions: { output: { - manualChunks: (id) => id.includes('src/App/index.jsx') ? 'app-chunk' : false, + manualChunks: id => + id.includes('src/App/index.jsx') ? 'app-chunk' : false, }, }, }, - }); + }) }, -}; +} export default config diff --git a/benefit-finder/README.md b/benefit-finder/README.md index d73e3f17b..06613a032 100644 --- a/benefit-finder/README.md +++ b/benefit-finder/README.md @@ -240,7 +240,7 @@ We take a utility first approach. We do not have full control over our styles si 1. we establish uswds components with `usa-` classes. 2. this inherits global `uswds` `css` and `js` -3. IF we need to overide, we clone the uswds class `usa-` and prepend `bf-`. +3. IF we need to override, we clone the uswds class `usa-` and prepend `bf-`. ```css .bf-usa- .usa- diff --git a/benefit-finder/cspell-dictionary/content.txt b/benefit-finder/cspell-dictionary/content.txt new file mode 100644 index 000000000..fd39e8fde --- /dev/null +++ b/benefit-finder/cspell-dictionary/content.txt @@ -0,0 +1,4 @@ +DOLO +FEMA +FASS +CHAMPVA diff --git a/benefit-finder/cspell-dictionary/dev.txt b/benefit-finder/cspell-dictionary/dev.txt new file mode 100644 index 000000000..3d86d6d0f --- /dev/null +++ b/benefit-finder/cspell-dictionary/dev.txt @@ -0,0 +1,40 @@ +autodocs +camelcase +esbenp +fieldgroup +fieldset +fieldsets +firstname +lastname +stylelint +backcta +spacebar +pageup +pagedown +icongallery +skipnav +Vitest +labelledby +tabindex +stylesheet +hreflang +evenodd +checkmark +inputmode +datetype +describedby +noreferrer +noopener +lede +nbsp +httpstat +prestart +pagesurvey +Pagesub +ALLOWALL +cflinuxfs +buildpack +buildpacks +webfonts +hkshshslhshjksh +kjgljjlhkhgljgjh diff --git a/benefit-finder/cspell-dictionary/es.txt b/benefit-finder/cspell-dictionary/es.txt new file mode 100644 index 000000000..f4652b8f7 --- /dev/null +++ b/benefit-finder/cspell-dictionary/es.txt @@ -0,0 +1,16 @@ +inglés +enero +Aplicar +beneficio +beneficios +buscador +discapacidad +jubilacion +suma +muerte +elegible +resultados +selecciónes +revisar +casada +Hija diff --git a/benefit-finder/cspell-dictionary/project.txt b/benefit-finder/cspell-dictionary/project.txt new file mode 100644 index 000000000..2d2ad49f1 --- /dev/null +++ b/benefit-finder/cspell-dictionary/project.txt @@ -0,0 +1,2 @@ +usagov +uswds diff --git a/benefit-finder/cypress.config.js b/benefit-finder/cypress.config.js index f509b80bb..672f2c410 100644 --- a/benefit-finder/cypress.config.js +++ b/benefit-finder/cypress.config.js @@ -1,4 +1,3 @@ - const { defineConfig } = require('cypress') module.exports = defineConfig({ @@ -7,6 +6,7 @@ module.exports = defineConfig({ runMode: 2, openMode: 0, }, + viewportWidth: 1050, e2e: { baseUrl: 'http://localhost:6006', excludeSpecPattern: 'cypress/e2e/usagov-public-site/*.cy.js', diff --git a/benefit-finder/cypress/e2e/storybook/aria-attribute-state.cy.js b/benefit-finder/cypress/e2e/storybook/aria-attribute-state.cy.js index df7393e45..3ba09984c 100644 --- a/benefit-finder/cypress/e2e/storybook/aria-attribute-state.cy.js +++ b/benefit-finder/cypress/e2e/storybook/aria-attribute-state.cy.js @@ -3,78 +3,49 @@ import * as utils from '../../support/utils.js' import * as EN_LOCALE_DATA from '../../../src/shared/locales/en/en.json' import * as EN_DOLO_MOCK_DATA from '../../../src/shared/api/mock-data/current.json' -import { pageObjects } from '../../support/pageObjects.js' -const dob = utils.getDateByOffset(-(18 * 365.2425 - 1)) -const relation = +const dateOfBirth = utils.getDateByOffset(-(18 * 365.2425 - 1)) +const relationship = EN_DOLO_MOCK_DATA.data.lifeEventForm.sectionsEligibilityCriteria[0].section .fieldsets[1].fieldset.inputs[0].inputCriteria.values[1].value const relationshipId = EN_DOLO_MOCK_DATA.data.lifeEventForm.sectionsEligibilityCriteria[0].section .fieldsets[1].fieldset.inputs[0].inputCriteria.id -const status = +const maritalStatus = EN_DOLO_MOCK_DATA.data.lifeEventForm.sectionsEligibilityCriteria[0].section .fieldsets[2].fieldset.inputs[0].inputCriteria.values[1].value describe('Validate state of aria-invalid attribute', () => { beforeEach(() => { cy.visit(utils.storybookUri) - pageObjects.button().contains(EN_LOCALE_DATA.intro.button).click() + cy.navigateToAboutTheApplicantPage() }) it('Should have default state of "false" for select, input, and radio', () => { - pageObjects - .fieldsetById(relationshipId) - .invoke('attr', 'aria-invalid') - .should('eq', 'false') - - pageObjects - .benefitMemorableDateById('day') - .invoke('attr', 'aria-invalid') - .should('eq', 'false') - - pageObjects - .radioGroup() - .invoke('attr', 'aria-invalid') - .should('eq', 'false') + cy.validateAriaInvalid('fieldsetById', 'false', relationshipId) + cy.validateAriaInvalid('benefitMemorableDateById', 'false', 'day') + cy.validateAriaInvalid('radioGroup', 'false') }) it('Should have state of "true" when a required field has no value', () => { - pageObjects.button().contains(EN_LOCALE_DATA.buttonGroup[1].value).click() - - pageObjects - .fieldsetById(relationshipId) - .invoke('attr', 'aria-invalid') - .should('eq', 'true') - - pageObjects - .benefitMemorableDateById('day') - .invoke('attr', 'aria-invalid') - .should('eq', 'true') + cy.clickButton(EN_LOCALE_DATA.buttonGroup[1].value) + cy.validateAriaInvalid('fieldsetById', 'true', relationshipId) + cy.validateAriaInvalid('benefitMemorableDateById', 'true', 'day') }) it('Should have state of "false" when previous was true but error has been resolved', () => { - pageObjects.button().contains(EN_LOCALE_DATA.buttonGroup[1].value).click() - - pageObjects - .fieldsetById(relationshipId) - .invoke('attr', 'aria-invalid') - .should('eq', 'true') - pageObjects - .benefitMemorableDateById('day') - .invoke('attr', 'aria-invalid') - .should('eq', 'true') + cy.clickButton(EN_LOCALE_DATA.buttonGroup[1].value) - utils.dataInputs({ dob, relation, status }) + cy.validateAriaInvalid('fieldsetById', 'true', relationshipId) + cy.validateAriaInvalid('benefitMemorableDateById', 'true', 'day') - pageObjects - .fieldsetById(relationshipId) - .invoke('attr', 'aria-invalid') - .should('eq', 'false') + cy.fillDetailsAboutTheApplicant({ + dateOfBirth, + relationship, + maritalStatus, + }) - pageObjects - .benefitMemorableDateById('day') - .invoke('attr', 'aria-invalid') - .should('eq', 'false') + cy.validateAriaInvalid('fieldsetById', 'false', relationshipId) + cy.validateAriaInvalid('benefitMemorableDateById', 'false', 'day') }) }) diff --git a/benefit-finder/cypress/e2e/storybook/axe-a11y.cy.js b/benefit-finder/cypress/e2e/storybook/axe-a11y.cy.js index de5e85d66..2ef4a07e2 100644 --- a/benefit-finder/cypress/e2e/storybook/axe-a11y.cy.js +++ b/benefit-finder/cypress/e2e/storybook/axe-a11y.cy.js @@ -1,7 +1,7 @@ /// import * as utils from '../../support/utils.js' -import * as BENEFITS_ELIBILITY_DATA from '../../fixtures/benefits-eligibility.json' +import * as BENEFITS_ELIGIBILITY_DATA from '../../fixtures/benefits-eligibility.json' import * as EN_LOCALE_DATA from '../../../../benefit-finder/src/shared/locales/en/en.json' import * as EN_DOLO_MOCK_DATA from '../../../../benefit-finder/src/shared/api/mock-data/current.json' import { pageObjects } from '../../support/pageObjects' @@ -26,12 +26,12 @@ function terminalLog(violations) { cy.task('table', violationData) } -const dob = utils.getDateByOffset(-(18 * 365.2425 - 1)) -const dod = utils.getDateByOffset(-(18 * 365.2425 - 1)) -const relation = +const dateOfBirth = utils.getDateByOffset(-(18 * 365.2425 - 1)) +const dateOfDeath = utils.getDateByOffset(-30) +const relationship = EN_DOLO_MOCK_DATA.data.lifeEventForm.sectionsEligibilityCriteria[0].section .fieldsets[1].fieldset.inputs[0].inputCriteria.values[1].value -const status = +const maritalStatus = EN_DOLO_MOCK_DATA.data.lifeEventForm.sectionsEligibilityCriteria[0].section .fieldsets[2].fieldset.inputs[0].inputCriteria.values[1].value @@ -60,57 +60,62 @@ describe(`Validate code passes axe scanning`, () => { // go to first step it('Has no detectable a11y violations on step 1', () => { - pageObjects.button().contains(EN_LOCALE_DATA.intro.button).click() + cy.clickButton(EN_LOCALE_DATA.intro.button) runA11y() }) // create an error it('Has no detectable a11y violations on error state', () => { - pageObjects.button().contains(EN_LOCALE_DATA.intro.button).click() - pageObjects.button().contains(EN_LOCALE_DATA.buttonGroup[1].value).click() + cy.clickButton(EN_LOCALE_DATA.intro.button) + cy.clickButton(EN_LOCALE_DATA.buttonGroup[1].value) // Click 'Next' without filling details about the applicant runA11y() }) it('Has no detectable a11y violations on error state resolved', () => { - pageObjects.button().contains(EN_LOCALE_DATA.intro.button).click() - utils.dataInputs({ dob, relation, status }) + cy.clickButton(EN_LOCALE_DATA.intro.button) + cy.fillDetailsAboutTheApplicant({ + dateOfBirth, + relationship, + maritalStatus, + }) runA11y() }) it('Has no detectable a11y violations on step 2', () => { - pageObjects.button().contains(EN_LOCALE_DATA.intro.button).click() - utils.dataInputs({ dob, relation, status }) - pageObjects.button().contains(EN_LOCALE_DATA.buttonGroup[1].value).click() + cy.navigateToAboutTheDeceasedPage({ + dateOfBirth, + relationship, + maritalStatus, + }) + cy.clickButton(EN_LOCALE_DATA.buttonGroup[1].value) runA11y() }) it('Has no detectable a11y violations on modal launch', () => { - pageObjects.button().contains(EN_LOCALE_DATA.intro.button).click() - utils.dataInputs({ dob, relation, status }) - pageObjects.button().contains(EN_LOCALE_DATA.buttonGroup[1].value).click() - utils.dataInputs({ dod }) - pageObjects.button().contains(EN_LOCALE_DATA.buttonGroup[1].value).click() + cy.navigateToModal({ + dateOfBirth, + relationship, + maritalStatus, + dateOfDeath, + }) cy.injectAxe() cy.checkA11y('#benefit-finder-modal') }) it('Has no detectable a11y violations on modal close review selections', () => { - pageObjects.button().contains(EN_LOCALE_DATA.intro.button).click() - utils.dataInputs({ dob, relation, status }) - pageObjects.button().contains(EN_LOCALE_DATA.buttonGroup[1].value).click() - utils.dataInputs({ dod }) - pageObjects.button().contains(EN_LOCALE_DATA.buttonGroup[1].value).click() - pageObjects - .button() - .contains(EN_LOCALE_DATA.reviewSelectionModal.buttonGroup[0].value) - .click() + cy.navigateToModal({ + dateOfBirth, + relationship, + maritalStatus, + dateOfDeath, + }) + cy.clickButton(EN_LOCALE_DATA.reviewSelectionModal.buttonGroup[0].value) // Close modal runA11y() }) it('Has no detectable a11y violations on see benefits', () => { - const selectedData = BENEFITS_ELIBILITY_DATA.scenario_1_covid.en.param + const selectedData = BENEFITS_ELIGIBILITY_DATA.scenario_1_covid.en.param const scenario = utils.encodeURIFromObject(selectedData) - delete selectedData.shared // We don't want to include the "shared" param cy.visit(`${utils.storybookUri}${scenario}`) cy.injectAxe() // we inject axe again because of the reload -> visit @@ -132,19 +137,17 @@ describe(`Validate code passes axe scanning`, () => { }) it('Has no detectable a11y violations on see benefits you did not qualify for', () => { - pageObjects.button().contains(EN_LOCALE_DATA.intro.button).click() - utils.dataInputs({ dob, relation, status }) - pageObjects.button().contains(EN_LOCALE_DATA.buttonGroup[1].value).click() - utils.dataInputs({ dod }) - pageObjects.button().contains(EN_LOCALE_DATA.buttonGroup[1].value).click() - pageObjects - .button() - .contains(EN_LOCALE_DATA.reviewSelectionModal.buttonGroup[1].value) - .click() - pageObjects - .button() - .contains(EN_LOCALE_DATA.resultsView.zeroBenefits.cta) - .click() + cy.navigateToBenefitResultsPage({ + dateOfBirth, + relationship, + maritalStatus, + dateOfDeath, + }) + pageObjects.accordionHeading().should('exist') + + cy.clickButton(EN_LOCALE_DATA.resultsView.zeroBenefits.cta) + pageObjects.accordionHeading().should('exist') + // get a node list of all accordions // get the heading of the first in the list cy.get(`[data-testid="bf-usa-accordion"]`).then(accordionItems => { diff --git a/benefit-finder/cypress/e2e/storybook/benefitAccordionGroup.cy.js b/benefit-finder/cypress/e2e/storybook/benefitAccordionGroup.cy.js index c80cb37b3..d1f9054fe 100644 --- a/benefit-finder/cypress/e2e/storybook/benefitAccordionGroup.cy.js +++ b/benefit-finder/cypress/e2e/storybook/benefitAccordionGroup.cy.js @@ -1,9 +1,9 @@ import * as utils from '../../support/utils' -import * as BENEFITS_ELIBILITY_DATA from '../../fixtures/benefits-eligibility.json' +import * as BENEFITS_ELIGIBILITY_DATA from '../../fixtures/benefits-eligibility.json' import { pageObjects } from '../../support/pageObjects' beforeEach(() => { - const selectedData = BENEFITS_ELIBILITY_DATA.scenario_1_covid.en.param + const selectedData = BENEFITS_ELIGIBILITY_DATA.scenario_1_covid.en.param const scenario = utils.encodeURIFromObject(selectedData) cy.visit(`${utils.storybookUri}${scenario}`) pageObjects.accordionHeading().should('exist') diff --git a/benefit-finder/cypress/e2e/storybook/dataLayer.cy.js b/benefit-finder/cypress/e2e/storybook/dataLayer.cy.js index 34e0b6ae6..28aa12aa8 100644 --- a/benefit-finder/cypress/e2e/storybook/dataLayer.cy.js +++ b/benefit-finder/cypress/e2e/storybook/dataLayer.cy.js @@ -4,7 +4,7 @@ import * as utils from '../../support/utils.js' import { dataLayerUtils } from '../../../src/shared/utils' import { pageObjects } from '../../support/pageObjects' import * as EN_LOCALE_DATA from '../../../../benefit-finder/src/shared/locales/en/en.json' -import * as BENEFITS_ELIBILITY_DATA from '../../fixtures/benefits-eligibility.json' +import * as BENEFITS_ELIGIBILITY_DATA from '../../fixtures/benefits-eligibility.json' import content from '../../../src/shared/api/mock-data/current.json' import * as EN_DOLO_MOCK_DATA from '../../../../benefit-finder/src/shared/api/mock-data/current.json' @@ -17,12 +17,12 @@ const { openAllBenefitAccordions, } = dataLayerUtils.dataLayerStructure const { lifeEventForm } = content.data -const selectedData = BENEFITS_ELIBILITY_DATA.scenario_1_covid.en.param -const enResults = BENEFITS_ELIBILITY_DATA.scenario_1_covid.en.results -const zero_benefit_view = BENEFITS_ELIBILITY_DATA.zero_benefit_view.en.results +const selectedData = BENEFITS_ELIGIBILITY_DATA.scenario_1_covid.en.param +const enResults = BENEFITS_ELIGIBILITY_DATA.scenario_1_covid.en.results +const zero_benefit_view = BENEFITS_ELIGIBILITY_DATA.zero_benefit_view.en.results const scenario = utils.encodeURIFromObject(selectedData) -// calculate out elibibility counts we expect for our event values +// calculate out eligibility counts we expect for our event values const eligibilityCount = { eligibleBenefitCount: { number: enResults.eligible.length, @@ -146,7 +146,7 @@ const dataLayerValueFormCompletionModal = { }, } -const dataLayerValueVerifySecletions = { +const dataLayerValueVerifySelections = { event: 'bf_page_change', bfData: { pageView: 'bf-verify-selections', @@ -181,7 +181,7 @@ const dataLayerValues = [ dataLayerValueFormStepTwo, dataLayerValueFormCompletionModal, dataLayerValueFormSubmitSuccess, - dataLayerValueVerifySecletions, + dataLayerValueVerifySelections, dataLayerValueZeroResultsViewEligible, dataLayerValueZeroResultsViewNotEligible, dataLayerValueOpenAllAccordions, @@ -192,16 +192,13 @@ const dataLayerValues = [ const removeID = item => delete item['gtm.uniqueEventId'] -const relationshipId = - EN_DOLO_MOCK_DATA.data.lifeEventForm.sectionsEligibilityCriteria[0].section - .fieldsets[1].fieldset.inputs[0].inputCriteria.id -const relationshipValue = +const dateOfBirth = utils.getDateByOffset(-(18 * 365.2425 - 1)) +const dateOfDeath = utils.getDateByOffset(-30) + +const relationship = EN_DOLO_MOCK_DATA.data.lifeEventForm.sectionsEligibilityCriteria[0].section .fieldsets[1].fieldset.inputs[0].inputCriteria.values[1].value -const maritalStatusId = - EN_DOLO_MOCK_DATA.data.lifeEventForm.sectionsEligibilityCriteria[0].section - .fieldsets[2].fieldset.inputs[0].inputCriteria.id -const maritalStatusValue = +const maritalStatus = EN_DOLO_MOCK_DATA.data.lifeEventForm.sectionsEligibilityCriteria[0].section .fieldsets[2].fieldset.inputs[0].inputCriteria.values[1].value @@ -257,7 +254,8 @@ describe('Calls to Google Analytics Object', function () { it('first form step bf_page_change event', function () { cy.visit(utils.storybookUri) - pageObjects.button().contains(EN_LOCALE_DATA.intro.button).click() + + cy.navigateToAboutTheApplicantPage() cy.window().then(window => { assert.isDefined(window.dataLayer, 'window.dataLayer is defined') @@ -275,9 +273,10 @@ describe('Calls to Google Analytics Object', function () { }) }) - it('second form step bf_page_change event, asserts incrmenting values', function () { + it('second form step bf_page_change event, asserts incrementing values', function () { cy.visit(utils.storybookUri) - pageObjects.button().contains(EN_LOCALE_DATA.intro.button).click() + + cy.navigateToAboutTheApplicantPage() cy.window().then(window => { assert.isDefined(window.dataLayer, 'window.dataLayer is defined') @@ -292,37 +291,31 @@ describe('Calls to Google Analytics Object', function () { expect(ev).to.deep.equal(dataLayerValueFormStepOne) - // fill out required fields - const dateOfBirth = utils.getDateByOffset(-(18 * 365.2425 - 1)) - cy.visit('/iframe.html?args=&id=app--primary&viewMode=story') - - pageObjects.button().contains(EN_LOCALE_DATA.intro.button).click() - cy.enterDate(dateOfBirth.month, dateOfBirth.day, dateOfBirth.year) - pageObjects.fieldsetById(relationshipId).select(relationshipValue) - pageObjects.fieldsetById(maritalStatusId).select(maritalStatusValue) - - pageObjects - .button() - .contains(EN_LOCALE_DATA.buttonGroup[1].value) - .click() - .then(() => { - // get all the events in our layer that matches the event value - const ev = [ - ...window.dataLayer.filter( - x => x?.event === dataLayerValueFormStepTwo.event - ), - ] - removeID(ev[2]) + cy.fillDetailsAboutTheApplicant({ + dateOfBirth, + relationship, + maritalStatus, + }) - expect(ev[2]).to.deep.equal(dataLayerValueFormStepTwo) - }) + cy.clickButton(EN_LOCALE_DATA.buttonGroup[1].value).then(() => { + // get all the events in our layer that matches the event value + const ev = [ + ...window.dataLayer.filter( + x => x?.event === dataLayerValueFormStepTwo.event + ), + ] + removeID(ev[2]) + + expect(ev[2]).to.deep.equal(dataLayerValueFormStepTwo) + }) }) }) }) it('clicking Continue on the final form step opens a modal and triggers the modal event', function () { cy.visit(utils.storybookUri) - pageObjects.button().contains(EN_LOCALE_DATA.intro.button).click() + + cy.navigateToAboutTheApplicantPage() cy.window().then(window => { assert.isDefined(window.dataLayer, 'window.dataLayer is defined') @@ -338,49 +331,37 @@ describe('Calls to Google Analytics Object', function () { expect(ev).to.deep.equal(dataLayerValueFormStepOne) // fill out required fields - const dateOfBirth = utils.getDateByOffset(-(18 * 365.2425 - 1)) - cy.visit('/iframe.html?args=&id=app--primary&viewMode=story') + cy.fillDetailsAboutTheApplicant({ + dateOfBirth, + relationship, + maritalStatus, + }) - pageObjects.button().contains(EN_LOCALE_DATA.intro.button).click() - cy.enterDate(dateOfBirth.month, dateOfBirth.day, dateOfBirth.year) - pageObjects.fieldsetById(relationshipId).select(relationshipValue) - pageObjects.fieldsetById(maritalStatusId).select(maritalStatusValue) + cy.clickButton(EN_LOCALE_DATA.buttonGroup[1].value).then(() => { + // get all the events in our layer that matches the event value + const ev = [ + ...window.dataLayer.filter( + x => x?.event === dataLayerValueFormStepTwo.event + ), + ] + removeID(ev[2]) - pageObjects - .button() - .contains(EN_LOCALE_DATA.buttonGroup[1].value) - .click() - .then(() => { + expect(ev[2]).to.deep.equal(dataLayerValueFormStepTwo) + + cy.fillDetailsAboutTheDeceased({ dateOfDeath }) + + cy.clickButton(EN_LOCALE_DATA.buttonGroup[1].value).then(() => { // get all the events in our layer that matches the event value const ev = [ ...window.dataLayer.filter( - x => x?.event === dataLayerValueFormStepTwo.event + x => x?.event === dataLayerValueFormCompletionModal.event ), ] - removeID(ev[2]) - - expect(ev[2]).to.deep.equal(dataLayerValueFormStepTwo) + removeID(ev[3]) - // Date of death - 30 days ago - const dateOfDeath = utils.getDateByOffset(-30) - cy.enterDate(dateOfDeath.month, dateOfDeath.day, dateOfDeath.year) - - pageObjects - .button() - .contains(EN_LOCALE_DATA.buttonGroup[1].value) - .click() - .then(() => { - // get all the events in our layer that matches the event value - const ev = [ - ...window.dataLayer.filter( - x => x?.event === dataLayerValueFormCompletionModal.event - ), - ] - removeID(ev[3]) - - expect(ev[3]).to.deep.equal(dataLayerValueFormCompletionModal) - }) + expect(ev[3]).to.deep.equal(dataLayerValueFormCompletionModal) }) + }) }) }) }) @@ -580,7 +561,8 @@ describe('Calls to Google Analytics Object', function () { it('clicking Continue on the final form step opens a modal, clicking Review selections loads the verification view and a bf_page_change event', function () { cy.visit(utils.storybookUri) - pageObjects.button().contains(EN_LOCALE_DATA.intro.button).click() + + cy.navigateToAboutTheApplicantPage() cy.window().then(window => { assert.isDefined(window.dataLayer, 'window.dataLayer is defined') @@ -596,67 +578,50 @@ describe('Calls to Google Analytics Object', function () { expect(ev).to.deep.equal(dataLayerValueFormStepOne) // fill out required fields - const dateOfBirth = utils.getDateByOffset(-(18 * 365.2425 - 1)) - cy.visit('/iframe.html?args=&id=app--primary&viewMode=story') + cy.fillDetailsAboutTheApplicant({ + dateOfBirth, + relationship, + maritalStatus, + }) - pageObjects.button().contains(EN_LOCALE_DATA.intro.button).click() - cy.enterDate(dateOfBirth.month, dateOfBirth.day, dateOfBirth.year) - pageObjects.fieldsetById(relationshipId).select(relationshipValue) - pageObjects.fieldsetById(maritalStatusId).select(maritalStatusValue) + cy.clickButton(EN_LOCALE_DATA.buttonGroup[1].value).then(() => { + // get all the events in our layer that matches the event value + const ev = [ + ...window.dataLayer.filter( + x => x?.event === dataLayerValueFormStepTwo.event + ), + ] + removeID(ev[2]) - pageObjects - .button() - .contains(EN_LOCALE_DATA.buttonGroup[1].value) - .click() - .then(() => { + expect(ev[2]).to.deep.equal(dataLayerValueFormStepTwo) + + // Date of death - 30 days ago + cy.fillDetailsAboutTheDeceased({ dateOfDeath }) + + cy.clickButton(EN_LOCALE_DATA.buttonGroup[1].value).then(() => { // get all the events in our layer that matches the event value const ev = [ ...window.dataLayer.filter( - x => x?.event === dataLayerValueFormStepTwo.event + x => x?.event === dataLayerValueFormCompletionModal.event ), ] - removeID(ev[2]) - - expect(ev[2]).to.deep.equal(dataLayerValueFormStepTwo) - - // Date of death - 30 days ago - const dateOfDeath = utils.getDateByOffset(-30) - cy.enterDate(dateOfDeath.month, dateOfDeath.day, dateOfDeath.year) + removeID(ev[3]) - pageObjects - .button() - .contains(EN_LOCALE_DATA.buttonGroup[1].value) - .click() - .then(() => { - // get all the events in our layer that matches the event value - const ev = [ - ...window.dataLayer.filter( - x => x?.event === dataLayerValueFormCompletionModal.event - ), - ] - removeID(ev[3]) + expect(ev[3]).to.deep.equal(dataLayerValueFormCompletionModal) - expect(ev[3]).to.deep.equal(dataLayerValueFormCompletionModal) + cy.clickButton('Review your selections').then(() => { + // get all the events in our layer that matches the event value + const ev = [ + ...window.dataLayer.filter( + x => x?.event === dataLayerValueVerifySelections.event + ), + ] + removeID(ev[4]) - pageObjects - .button() - .contains('Review your selections') - .click() - .then(() => { - // get all the events in our layer that matches the event value - const ev = [ - ...window.dataLayer.filter( - x => x?.event === dataLayerValueVerifySecletions.event - ), - ] - removeID(ev[4]) - - expect(ev[4]).to.deep.equal( - dataLayerValueVerifySecletions - ) - }) - }) + expect(ev[4]).to.deep.equal(dataLayerValueVerifySelections) + }) }) + }) }) }) }) @@ -703,7 +668,8 @@ describe('Calls to Google Analytics Object', function () { it('navigating through all the form results in zeroBenefits views', function () { cy.visit(utils.storybookUri) - pageObjects.button().contains(EN_LOCALE_DATA.intro.button).click() + + cy.navigateToAboutTheApplicantPage() cy.window().then(window => { assert.isDefined(window.dataLayer, 'window.dataLayer is defined') @@ -719,112 +685,90 @@ describe('Calls to Google Analytics Object', function () { expect(ev).to.deep.equal(dataLayerValueFormStepOne) // fill out required fields - const dateOfBirth = utils.getDateByOffset(-(18 * 365.2425 - 1)) - cy.visit('/iframe.html?args=&id=app--primary&viewMode=story') + cy.fillDetailsAboutTheApplicant({ + dateOfBirth, + relationship, + maritalStatus, + }) - pageObjects.button().contains(EN_LOCALE_DATA.intro.button).click() - cy.enterDate(dateOfBirth.month, dateOfBirth.day, dateOfBirth.year) - pageObjects.fieldsetById(relationshipId).select(relationshipValue) - pageObjects.fieldsetById(maritalStatusId).select(maritalStatusValue) + cy.clickButton(EN_LOCALE_DATA.buttonGroup[1].value).then(() => { + // get all the events in our layer that matches the event value + const ev = [ + ...window.dataLayer.filter( + x => x?.event === dataLayerValueFormStepTwo.event + ), + ] + removeID(ev[2]) - pageObjects - .button() - .contains(EN_LOCALE_DATA.buttonGroup[1].value) - .click() - .then(() => { - // get all the events in our layer that matches the event value + expect(ev[2]).to.deep.equal(dataLayerValueFormStepTwo) + + cy.fillDetailsAboutTheDeceased({ dateOfDeath }) + + cy.clickButton(EN_LOCALE_DATA.buttonGroup[1].value).then(() => { const ev = [ ...window.dataLayer.filter( - x => x?.event === dataLayerValueFormStepTwo.event + x => x?.event === dataLayerValueFormCompletionModal.event ), ] - removeID(ev[2]) + removeID(ev[3]) - expect(ev[2]).to.deep.equal(dataLayerValueFormStepTwo) + expect(ev[3]).to.deep.equal(dataLayerValueFormCompletionModal) - // Date of death - 30 days ago - const dateOfDeath = utils.getDateByOffset(-30) - cy.enterDate(dateOfDeath.month, dateOfDeath.day, dateOfDeath.year) + cy.clickButton('Review your selections').then(() => { + // get all the events in our layer that matches the event value + const ev = [ + ...window.dataLayer.filter( + x => x?.event === dataLayerValueVerifySelections.event + ), + ] + removeID(ev[4]) - pageObjects - .button() - .contains(EN_LOCALE_DATA.buttonGroup[1].value) - .click() - .then(() => { + expect(ev[4]).to.deep.equal(dataLayerValueVerifySelections) + + cy.clickButton(EN_LOCALE_DATA.buttonGroup[1].value).then(() => { + // get all the events in our layer that matches the event value const ev = [ ...window.dataLayer.filter( - x => x?.event === dataLayerValueFormCompletionModal.event + x => + x?.event === dataLayerValueZeroResultsViewEligible.event ), ] - removeID(ev[3]) + removeID(ev[5]) - expect(ev[3]).to.deep.equal(dataLayerValueFormCompletionModal) + expect(ev[5]).to.deep.equal( + dataLayerValueZeroResultsViewEligible + ) pageObjects - .button() - .contains('Review your selections') + .seeAllBenefitsButton() .click() .then(() => { // get all the events in our layer that matches the event value const ev = [ ...window.dataLayer.filter( - x => x?.event === dataLayerValueVerifySecletions.event + x => + x?.event === + dataLayerValueZeroResultsViewNotEligible.event ), ] - removeID(ev[4]) + removeID(ev[6]) - expect(ev[4]).to.deep.equal( - dataLayerValueVerifySecletions + expect(ev[6]).to.deep.equal( + dataLayerValueZeroResultsViewNotEligible ) - - pageObjects - .button() - .contains(EN_LOCALE_DATA.buttonGroup[1].value) - .click() - .then(() => { - // get all the events in our layer that matches the event value - const ev = [ - ...window.dataLayer.filter( - x => - x?.event === - dataLayerValueZeroResultsViewEligible.event - ), - ] - removeID(ev[5]) - - expect(ev[5]).to.deep.equal( - dataLayerValueZeroResultsViewEligible - ) - - pageObjects - .seeAllBenefitsButton() - .click() - .then(() => { - // get all the events in our layer that matches the event value - const ev = [ - ...window.dataLayer.filter( - x => - x?.event === - dataLayerValueZeroResultsViewNotEligible.event - ), - ] - removeID(ev[6]) - - expect(ev[6]).to.deep.equal( - dataLayerValueZeroResultsViewNotEligible - ) - }) - }) }) }) + }) }) + }) }) }) }) it('navigating through all the test steps produces a deep equal comparison to our expected dataLayer array values', function () { cy.visit(utils.storybookUri) - pageObjects.button().contains(EN_LOCALE_DATA.intro.button).click() + + cy.navigateToAboutTheApplicantPage() cy.window().then(window => { assert.isDefined(window.dataLayer, 'window.dataLayer is defined') @@ -841,222 +785,190 @@ describe('Calls to Google Analytics Object', function () { expect(ev).to.deep.equal(dataLayerValueFormStepOne) // fill out required fields - const dateOfBirth = utils.getDateByOffset(-(18 * 365.2425 - 1)) - cy.visit('/iframe.html?args=&id=app--primary&viewMode=story') + cy.fillDetailsAboutTheApplicant({ + dateOfBirth, + relationship, + maritalStatus, + }) + + cy.clickButton(EN_LOCALE_DATA.buttonGroup[1].value).then(() => { + // get the last pushed event + const ev = [ + ...window.dataLayer.filter( + x => x?.event === dataLayerValueFormStepTwo.event + ), + ] - pageObjects.button().contains(EN_LOCALE_DATA.intro.button).click() - cy.enterDate(dateOfBirth.month, dateOfBirth.day, dateOfBirth.year) - pageObjects.fieldsetById(relationshipId).select(relationshipValue) - pageObjects.fieldsetById(maritalStatusId).select(maritalStatusValue) + // delete ev[2]['gtm.uniqueEventId'] + removeID(ev[2]) - pageObjects - .button() - .contains(EN_LOCALE_DATA.buttonGroup[1].value) - .click() - .then(() => { - // get the last pushed event + expect(ev[2]).to.deep.equal(dataLayerValueFormStepTwo) + + cy.fillDetailsAboutTheDeceased({ dateOfDeath }) + + cy.clickButton(EN_LOCALE_DATA.buttonGroup[1].value).then(() => { const ev = [ ...window.dataLayer.filter( - x => x?.event === dataLayerValueFormStepTwo.event + x => x?.event === dataLayerValueFormCompletionModal.event ), ] - // delete ev[2]['gtm.uniqueEventId'] - removeID(ev[2]) + // delete ev[3]['gtm.uniqueEventId'] + removeID(ev[3]) + + expect(ev[3]).to.deep.equal(dataLayerValueFormCompletionModal) + + cy.clickButton('Review your selections').then(() => { + const ev = [ + ...window.dataLayer.filter( + x => x?.event === dataLayerValueVerifySelections.event + ), + ] - expect(ev[2]).to.deep.equal(dataLayerValueFormStepTwo) + // delete ev[4]['gtm.uniqueEventId'] + removeID(ev[4]) - // Date of death - 30 days ago - const dateOfDeath = utils.getDateByOffset(-30) - cy.enterDate(dateOfDeath.month, dateOfDeath.day, dateOfDeath.year) + expect(ev[4]).to.deep.equal(dataLayerValueVerifySelections) - pageObjects - .button() - .contains(EN_LOCALE_DATA.buttonGroup[1].value) - .click() - .then(() => { + cy.clickButton(EN_LOCALE_DATA.buttonGroup[1].value).then(() => { const ev = [ ...window.dataLayer.filter( - x => x?.event === dataLayerValueFormCompletionModal.event + x => + x?.event === dataLayerValueZeroResultsViewEligible.event ), ] + removeID(ev[5]) - // delete ev[3]['gtm.uniqueEventId'] - removeID(ev[3]) - - expect(ev[3]).to.deep.equal(dataLayerValueFormCompletionModal) + expect(ev[5]).to.deep.equal( + dataLayerValueZeroResultsViewEligible + ) pageObjects - .button() - .contains('Review your selections') + .seeAllBenefitsButton() .click() .then(() => { const ev = [ ...window.dataLayer.filter( - x => x?.event === dataLayerValueVerifySecletions.event + x => + x?.event === + dataLayerValueZeroResultsViewNotEligible.event ), ] + removeID(ev[6]) - // delete ev[4]['gtm.uniqueEventId'] - removeID(ev[4]) - - expect(ev[4]).to.deep.equal( - dataLayerValueVerifySecletions + expect(ev[6]).to.deep.equal( + dataLayerValueZeroResultsViewNotEligible ) + }) + // confirm zero benefits event and view + // click see all benefits + pageObjects + .expandAll() + .click() + .then(() => { + // check last page change event + const ev = [ + ...window.dataLayer.filter( + x => + x?.event === dataLayerValueOpenAllAccordions.event + ), + ] + removeID(ev[0]) + expect(ev[0]).to.deep.equal( + dataLayerValueOpenAllAccordions + ) pageObjects - .button() - .contains(EN_LOCALE_DATA.buttonGroup[1].value) + .expandAll() .click() .then(() => { + // check last page change event const ev = [ ...window.dataLayer.filter( x => x?.event === - dataLayerValueZeroResultsViewEligible.event + dataLayerValueOpenAllAccordions.event ), ] - removeID(ev[5]) - expect(ev[5]).to.deep.equal( - dataLayerValueZeroResultsViewEligible + // we ignore dedupe here so there can be multiple fires + removeID(ev[1]) + + expect(ev[1].bfData.accordionsOpen).to.equal( + !dataLayerValueOpenAllAccordions.bfData + .accordionsOpen ) pageObjects - .seeAllBenefitsButton() + .accordionByTitle( + enResults.eligible.eligible_benefits[0] + ) .click() - .then(() => { - const ev = [ - ...window.dataLayer.filter( - x => - x?.event === - dataLayerValueZeroResultsViewNotEligible.event - ), - ] - removeID(ev[6]) + cy.wrap(window.dataLayer).should(dataLayer => { + const matchingEvents = dataLayer.filter( + x => + x?.event === dataLayerValueAccordionOpen.event + ) + assert.isNotEmpty( + matchingEvents, + 'bf_accordion_open event is triggered' + ) + }) + + // check last page change event + cy.wrap(window.dataLayer).then(dataLayer => { + const ev = dataLayer.filter( + x => + x?.event === dataLayerValueAccordionOpen.event + )[0] + + removeID(ev) + + expect(ev).to.deep.equal( + dataLayerValueAccordionOpen + ) + }) - expect(ev[6]).to.deep.equal( - dataLayerValueZeroResultsViewNotEligible - ) - }) - // confirm zero benefits event and view - // click see all benefits pageObjects - .expandAll() + .benefitsAccordionLink( + enResults.eligible.eligible_benefits[0] + ) + .invoke('removeAttr', 'href') .click() .then(() => { - // check last page change event const ev = [ ...window.dataLayer.filter( x => - x?.event === - dataLayerValueOpenAllAccordions.event + x?.event === dataLayerValueBenefitLink.event ), ] + // delete ev[0]['gtm.uniqueEventId'] removeID(ev[0]) - expect(ev[0]).to.deep.equal( - dataLayerValueOpenAllAccordions + dataLayerValueBenefitLink + ) + + // loop through the data layer and remove any events that are gtm + + const bfDataLayer = window.dataLayer.filter( + item => !item.event.includes('gtm') + ) + + const cleanBfDataLayer = bfDataLayer.map(item => { + removeID(item) + return item + }) + + expect(cleanBfDataLayer).to.deep.equal( + dataLayerValues ) - pageObjects - .expandAll() - .click() - .then(() => { - // check last page change event - const ev = [ - ...window.dataLayer.filter( - x => - x?.event === - dataLayerValueOpenAllAccordions.event - ), - ] - - // we ignore dedup here so there can be multiple fires - removeID(ev[1]) - - expect(ev[1].bfData.accordionsOpen).to.equal( - !dataLayerValueOpenAllAccordions.bfData - .accordionsOpen - ) - - pageObjects - .accordionByTitle( - enResults.eligible.eligible_benefits[0] - ) - .click() - cy.wrap(window.dataLayer).should( - dataLayer => { - const matchingEvents = dataLayer.filter( - x => - x?.event === - dataLayerValueAccordionOpen.event - ) - assert.isNotEmpty( - matchingEvents, - 'bf_accordion_open event is triggered' - ) - } - ) - - // check last page change event - cy.wrap(window.dataLayer).then(dataLayer => { - const ev = dataLayer.filter( - x => - x?.event === - dataLayerValueAccordionOpen.event - )[0] - - removeID(ev) - - expect(ev).to.deep.equal( - dataLayerValueAccordionOpen - ) - }) - - pageObjects - .benefitsAccordionLink( - enResults.eligible.eligible_benefits[0] - ) - .invoke('removeAttr', 'href') - .click() - .then(() => { - const ev = [ - ...window.dataLayer.filter( - x => - x?.event === - dataLayerValueBenefitLink.event - ), - ] - // delete ev[0]['gtm.uniqueEventId'] - removeID(ev[0]) - expect(ev[0]).to.deep.equal( - dataLayerValueBenefitLink - ) - - // loop through the data layer and remove any events that are gtm - - const bfDataLayer = - window.dataLayer.filter( - item => !item.event.includes('gtm') - ) - - const cleanBfDataLayer = bfDataLayer.map( - item => { - removeID(item) - return item - } - ) - - expect(cleanBfDataLayer).to.deep.equal( - dataLayerValues - ) - }) - }) }) }) }) }) + }) }) + }) }) }) }) diff --git a/benefit-finder/cypress/e2e/storybook/error-message-display.cy.js b/benefit-finder/cypress/e2e/storybook/error-message-display.cy.js index 75d79c236..01d857255 100644 --- a/benefit-finder/cypress/e2e/storybook/error-message-display.cy.js +++ b/benefit-finder/cypress/e2e/storybook/error-message-display.cy.js @@ -6,11 +6,11 @@ import * as EN_DOLO_MOCK_DATA from '../../../../benefit-finder/src/shared/api/mo import * as EN_LOCALE_DATA from '../../../../benefit-finder/src/shared/locales/en/en.json' import 'cypress-plugin-tab' -const dob = utils.getDateByOffset(-(18 * 365.2425 - 1)) -const relation = +const dateOfBirth = utils.getDateByOffset(-(18 * 365.2425 - 1)) +const relationship = EN_DOLO_MOCK_DATA.data.lifeEventForm.sectionsEligibilityCriteria[0].section .fieldsets[1].fieldset.inputs[0].inputCriteria.values[1].value -const status = +const maritalStatus = EN_DOLO_MOCK_DATA.data.lifeEventForm.sectionsEligibilityCriteria[0].section .fieldsets[2].fieldset.inputs[0].inputCriteria.values[1].value @@ -30,7 +30,7 @@ const alertDisplayState = { beforeEach(() => { cy.visit(utils.storybookUri) - pageObjects.button().contains(EN_LOCALE_DATA.intro.button).click() + cy.clickButton(EN_LOCALE_DATA.intro.button) }) describe('Validate correct error messages display for negative scenarios', () => { @@ -48,7 +48,7 @@ describe('Validate correct error messages display for negative scenarios', () => it('Should display error message when user attempts to move forward without completing required field', () => { // expect when a user selects continue the focus is made on the error notice at the top - pageObjects.button().contains(EN_LOCALE_DATA.buttonGroup[1].value).click() + cy.clickButton(EN_LOCALE_DATA.buttonGroup[1].value) cy.focused().should('have.class', 'usa-alert--error') // expect the error notice to be visible if errors are present @@ -63,7 +63,7 @@ describe('Validate correct error messages display for negative scenarios', () => }) it('Should have a list of errors', () => { - pageObjects.button().contains(EN_LOCALE_DATA.buttonGroup[1].value).click() + cy.clickButton(EN_LOCALE_DATA.buttonGroup[1].value) cy.focused().should('have.class', 'usa-alert--error').tab() pageObjects.bfAlertList().then(() => { pageObjects.bfAlertListItem().should('have.length.above', 0) @@ -71,7 +71,7 @@ describe('Validate correct error messages display for negative scenarios', () => }) it('Should have a list of errors that link to the invalid fields', () => { - pageObjects.button().contains(EN_LOCALE_DATA.buttonGroup[1].value).click() + cy.clickButton(EN_LOCALE_DATA.buttonGroup[1].value) cy.focused().should('have.class', 'usa-alert--error').tab() // expect the first tabbable item in the list to be the first error link pageObjects.bfAlertList().then(() => { @@ -88,7 +88,7 @@ describe('Validate correct error messages display for negative scenarios', () => it('Should allow tabbing to the next error message', () => { // expect when a user tabs from the focus error they can tab any other error notices in the form - pageObjects.button().contains(EN_LOCALE_DATA.buttonGroup[1].value).click() + cy.clickButton(EN_LOCALE_DATA.buttonGroup[1].value) cy.focused().should('have.class', 'usa-alert--error').tab() pageObjects.bfAlertList().then(() => { pageObjects.bfAlertListItem().then(errors => { @@ -101,7 +101,7 @@ describe('Validate correct error messages display for negative scenarios', () => it('Should hide the error message if field error is resolved', () => { // expect when a user tabs from the focus error they can tab any other error notices in the form - pageObjects.button().contains(EN_LOCALE_DATA.buttonGroup[1].value).click() + cy.clickButton(EN_LOCALE_DATA.buttonGroup[1].value) cy.focused().should('have.class', 'usa-alert--error').tab() pageObjects.bfAlertList().then(() => { pageObjects.bfAlertListItem().then(errors => { @@ -109,8 +109,11 @@ describe('Validate correct error messages display for negative scenarios', () => errorsArray.forEach(() => cy.focused().tab()) cy.focused().should('have.class', 'usa-input--error') // expect when a user has resolved all errors the top level error notices is not visible or accessible - utils.dataInputs({ dob, relation, status }) - + cy.fillDetailsAboutTheApplicant({ + dateOfBirth, + relationship, + maritalStatus, + }) // expect the error notice to be hidden if errors are present pageObjects.benefitSectionAlert().should('have.class', 'display-none') }) @@ -119,7 +122,7 @@ describe('Validate correct error messages display for negative scenarios', () => it('Should not allow moving forward by clicking continue when error banner is present', () => { // expect when a user tabs from the focus error they can tab any other error notices in the form - pageObjects.button().contains(EN_LOCALE_DATA.buttonGroup[1].value).click() + cy.clickButton(EN_LOCALE_DATA.buttonGroup[1].value) cy.focused().should('have.class', 'usa-alert--error').tab() pageObjects.bfAlertList().then(() => { pageObjects.bfAlertListItem().then(errors => { @@ -136,16 +139,15 @@ describe('Validate correct error messages display for negative scenarios', () => } // expect when a user has resolved all errors the top level error notices is not visible or accessible - utils.dataInputs({ relation }) + cy.fillDetailsAboutTheApplicant({ + relationship, + }) // expect the error notice to be visible if errors are present pageObjects .benefitSectionAlert() .should('not.have.class', 'display-none') - pageObjects - .button() - .contains(EN_LOCALE_DATA.buttonGroup[1].value) - .click() + cy.clickButton(EN_LOCALE_DATA.buttonGroup[1].value) cy.focused().should('have.class', 'usa-alert--error') // expect date DOM structure alert to be accessible for (const attr in alertDisplayState) { @@ -159,7 +161,7 @@ describe('Validate correct error messages display for negative scenarios', () => }) it('Should include error count in alert', () => { - pageObjects.button().contains(EN_LOCALE_DATA.buttonGroup[1].value).click() + cy.clickButton(EN_LOCALE_DATA.buttonGroup[1].value) pageObjects .bfAlertList() .find('li') @@ -171,7 +173,7 @@ describe('Validate correct error messages display for negative scenarios', () => }) it('Should include list of error links and clicking on a link navigates to a specific field', () => { - pageObjects.button().contains(EN_LOCALE_DATA.buttonGroup[1].value).click() + cy.clickButton(EN_LOCALE_DATA.buttonGroup[1].value) pageObjects .bfAlertList() @@ -195,29 +197,29 @@ describe('Validate correct error messages display for negative scenarios', () => }) it('Should validate error label content overrides', () => { - pageObjects.button().contains(EN_LOCALE_DATA.buttonGroup[1].value).click() + cy.clickButton(EN_LOCALE_DATA.buttonGroup[1].value) - const relationErrorMessageOvveride = + const relationErrorMessageOverride = EN_DOLO_MOCK_DATA.data.lifeEventForm.sectionsEligibilityCriteria[0] .section.fieldsets[1].fieldset.errorMessage pageObjects .errorDescription() .eq(1) .should('be.visible') - .and('contain.text', relationErrorMessageOvveride) + .and('contain.text', relationErrorMessageOverride) pageObjects .errorDescription() .eq(0) - .should('not.contain.text', relationErrorMessageOvveride) + .should('not.contain.text', relationErrorMessageOverride) pageObjects .dateOfBirthMonthError() - .should('not.contain.text', relationErrorMessageOvveride) + .should('not.contain.text', relationErrorMessageOverride) pageObjects .dateOfBirthDayError() - .should('not.contain.text', relationErrorMessageOvveride) + .should('not.contain.text', relationErrorMessageOverride) pageObjects .dateOfBirthYearError() - .should('not.contain.text', relationErrorMessageOvveride) + .should('not.contain.text', relationErrorMessageOverride) }) }) diff --git a/benefit-finder/cypress/e2e/storybook/modal.js b/benefit-finder/cypress/e2e/storybook/modal.cy.js similarity index 75% rename from benefit-finder/cypress/e2e/storybook/modal.js rename to benefit-finder/cypress/e2e/storybook/modal.cy.js index 2023ccd0f..6936b235a 100644 --- a/benefit-finder/cypress/e2e/storybook/modal.js +++ b/benefit-finder/cypress/e2e/storybook/modal.cy.js @@ -1,30 +1,29 @@ -/// +/// import * as utils from '../../support/utils.js' import * as EN_LOCALE_DATA from '../../../src/shared/locales/en/en.json' import * as EN_DOLO_MOCK_DATA from '../../../src/shared/api/mock-data/current.json' -import { pageObjects } from '../../support/pageObjects.js' -const dob = utils.getDateByOffset(-(18 * 365.2425 - 1)) -const dod = utils.getDateByOffset(-30) +const dateOfBirth = utils.getDateByOffset(-(18 * 365.2425 - 1)) +const dateOfDeath = utils.getDateByOffset(-30) -const relation = +const relationship = EN_DOLO_MOCK_DATA.data.lifeEventForm.sectionsEligibilityCriteria[0].section .fieldsets[1].fieldset.inputs[0].inputCriteria.values[1].value -const status = +const maritalStatus = EN_DOLO_MOCK_DATA.data.lifeEventForm.sectionsEligibilityCriteria[0].section .fieldsets[2].fieldset.inputs[0].inputCriteria.values[1].value describe('Validate scrolling when modal is open', () => { it('Should disable body from scrolling when model is open', () => { cy.visit(utils.storybookUri) - pageObjects.button().contains(EN_LOCALE_DATA.intro.button).click() - utils.dataInputs({ dob, relation, status }) - pageObjects.button().contains(EN_LOCALE_DATA.buttonGroup[1].value).click() - utils.dataInputs({ dod }) - // open modal - pageObjects.button().contains(EN_LOCALE_DATA.buttonGroup[1].value).click() + cy.navigateToModal({ + dateOfBirth, + relationship, + maritalStatus, + dateOfDeath, + }) // close when clicked off modal cy.get('.ReactModal__Overlay').click('topRight') @@ -35,7 +34,7 @@ describe('Validate scrolling when modal is open', () => { ) // these types run successfully but do not trigger movement in the window // open modal - pageObjects.button().contains(EN_LOCALE_DATA.buttonGroup[1].value).click() + cy.clickButton(EN_LOCALE_DATA.buttonGroup[1].value) // confirm type works for modal cy.get('#benefit-finder-modal').type( @@ -77,7 +76,7 @@ describe('Validate scrolling when modal is open', () => { expect($w.scrollY).to.be.greaterThan(scrollYPosition) }) - pageObjects.button().contains(EN_LOCALE_DATA.buttonGroup[1].value).click() + cy.clickButton(EN_LOCALE_DATA.buttonGroup[1].value) // confirm we are back at the top cy.window().then($w => { diff --git a/benefit-finder/cypress/e2e/storybook/openAllAccordions.cy.js b/benefit-finder/cypress/e2e/storybook/openAllAccordions.cy.js index 411c2b425..90ffee49c 100644 --- a/benefit-finder/cypress/e2e/storybook/openAllAccordions.cy.js +++ b/benefit-finder/cypress/e2e/storybook/openAllAccordions.cy.js @@ -1,9 +1,9 @@ import * as utils from '../../support/utils' import { pageObjects } from '../../support/pageObjects' -import * as BENEFITS_ELIBILITY_DATA from '../../fixtures/benefits-eligibility.json' +import * as BENEFITS_ELIGIBILITY_DATA from '../../fixtures/benefits-eligibility.json' beforeEach(() => { - const selectedData = BENEFITS_ELIBILITY_DATA.scenario_1_covid.en.param + const selectedData = BENEFITS_ELIGIBILITY_DATA.scenario_1_covid.en.param const scenario = utils.encodeURIFromObject(selectedData) cy.visit(`${utils.storybookUri}${scenario}`) pageObjects.accordionHeading().should('exist') diff --git a/benefit-finder/cypress/e2e/storybook/selected-criteria-eligibility-benefits.cy.js b/benefit-finder/cypress/e2e/storybook/selected-criteria-eligibility-benefits.cy.js index 60c4d1f3e..a2557049c 100644 --- a/benefit-finder/cypress/e2e/storybook/selected-criteria-eligibility-benefits.cy.js +++ b/benefit-finder/cypress/e2e/storybook/selected-criteria-eligibility-benefits.cy.js @@ -1,23 +1,23 @@ /// -import { pageObjects } from '../../support/pageObjects' import * as utils from '../../support/utils' import * as EN_DOLO_MOCK_DATA from '../../../../benefit-finder/src/shared/api/mock-data/current.json' -import * as BENEFITS_ELIBILITY_DATA from '../../fixtures/benefits-eligibility.json' +import * as BENEFITS_ELIGIBILITY_DATA from '../../fixtures/benefits-eligibility.json' import content from '../../../../benefit-finder/src/shared/api/mock-data/current.js' import * as EN_LOCALE_DATA from '../../../../benefit-finder/src/shared/locales/en/en.json' const { data } = JSON.parse(content) -const relationshipId = - EN_DOLO_MOCK_DATA.data.lifeEventForm.sectionsEligibilityCriteria[0].section - .fieldsets[1].fieldset.inputs[0].inputCriteria.id -const relationshipValue = +// 18 years ago minus one day - applicant under 18 years old +// 1 day = 365.2425 (accounts for leap year) +const dateOfBirth = utils.getDateByOffset(-(18 * 365.2425 - 1)) +// Date of death - 30 days ago +const dateOfDeath = utils.getDateByOffset(-30) + +const relationship = EN_DOLO_MOCK_DATA.data.lifeEventForm.sectionsEligibilityCriteria[0].section .fieldsets[1].fieldset.inputs[0].inputCriteria.values[1].value -const maritalStatusId = - EN_DOLO_MOCK_DATA.data.lifeEventForm.sectionsEligibilityCriteria[0].section - .fieldsets[2].fieldset.inputs[0].inputCriteria.id -const maritalStatusValue = + +const maritalStatus = EN_DOLO_MOCK_DATA.data.lifeEventForm.sectionsEligibilityCriteria[0].section .fieldsets[2].fieldset.inputs[0].inputCriteria.values[1].value const citizenshipStatusId = @@ -31,71 +31,33 @@ const publicSafetyOfficerId = .fieldsets[3].fieldset.inputs[0].inputCriteria.id describe('Validate correct eligibility benefits display based on selected criteria/options', () => { - it('Should render Survivor Benefits for Child benefit accordion correctly based on selected cretiria options', () => { - // 18 years ago minus one day - applicant under 18 years old - // 1 day = 365.2425 (accounts for leap year) - const dateOfBirth = utils.getDateByOffset(-(18 * 365.2425 - 1)) - cy.visit('/iframe.html?args=&id=app--primary&viewMode=story') - - pageObjects.button().contains(EN_LOCALE_DATA.intro.button).click() - cy.enterDate(dateOfBirth.month, dateOfBirth.day, dateOfBirth.year) - pageObjects.fieldsetById(relationshipId).select(relationshipValue) - pageObjects.fieldsetById(maritalStatusId).select(maritalStatusValue) - - pageObjects.fieldsetById(citizenshipStatusId).eq(0).click({ force: true }) - - pageObjects.button().contains(EN_LOCALE_DATA.buttonGroup[1].value).click() - - // Date of death - 30 days ago - const dateOfDeath = utils.getDateByOffset(-30) - cy.enterDate(dateOfDeath.month, dateOfDeath.day, dateOfDeath.year) - - pageObjects - .fieldsetById(paidIntoSocialSecurityId) - .eq(0) - .click({ force: true }) - - pageObjects.fieldsetById(publicSafetyOfficerId).eq(0).click({ force: true }) - - pageObjects.button().contains(EN_LOCALE_DATA.buttonGroup[1].value).click() - - pageObjects - .modalButtonGroup() - .contains(EN_LOCALE_DATA.reviewSelectionModal.buttonGroup[1].value) - .click() - - pageObjects - .accordionByTitle(EN_DOLO_MOCK_DATA.data.benefits[23].benefit.title) - .click() - .parent() - .parent() - .parent() - .find('.bf-key-eligibility-criteria-list li') - .should( - 'contain', - EN_DOLO_MOCK_DATA.data.benefits[23].benefit.eligibility[0].label - ) - .and( - 'contain', - EN_DOLO_MOCK_DATA.data.benefits[23].benefit.eligibility[1].label - ) - .and( - 'contain', - EN_DOLO_MOCK_DATA.data.benefits[23].benefit.eligibility[2].label - ) - .and( - 'contain', - EN_DOLO_MOCK_DATA.data.benefits[23].benefit.eligibility[3].label - ) - .and( - 'contain', - EN_DOLO_MOCK_DATA.data.benefits[23].benefit.eligibility[4].label - ) + it('Should render Survivor Benefits for Child benefit accordion correctly based on selected criteria options', () => { + cy.visit(utils.storybookUri) + + cy.navigateToBenefitResultsPage({ + dateOfBirth, + relationship, + maritalStatus, + optionalApplicantFields: { + [citizenshipStatusId]: 0, // Select "Yes" for citizenship + }, + dateOfDeath, + optionalDeceasedFields: { + [paidIntoSocialSecurityId]: 0, // Select "Yes" for "Did deceased ever work and pay U.S. Social Security taxes?" + [publicSafetyOfficerId]: 0, // Select "Yes" for "Was the deceased a public safety officer who died in the line of duty" + }, + }) + + const accordionTitle = EN_DOLO_MOCK_DATA.data.benefits[23].benefit.title + const eligibilityLabels = + EN_DOLO_MOCK_DATA.data.benefits[23].benefit.eligibility.map(e => e.label) + + cy.validateAccordionContent(accordionTitle, eligibilityLabels) }) it('qa scenario 1 Covid EN - Verify correct benefit results for query values that includes covid in search parameter of URL', () => { - const selectedData = BENEFITS_ELIBILITY_DATA.scenario_1_covid.en.param - const enResults = BENEFITS_ELIBILITY_DATA.scenario_1_covid.en.results + const selectedData = BENEFITS_ELIGIBILITY_DATA.scenario_1_covid.en.param + const enResults = BENEFITS_ELIGIBILITY_DATA.scenario_1_covid.en.results const scenario = utils.encodeURIFromObject(selectedData) delete selectedData.shared // We don't want to include the "shared" param const selectDataLength = Object.keys(selectedData).length @@ -103,118 +65,69 @@ describe('Validate correct eligibility benefits display based on selected criter cy.visit(`${utils.storybookUri}${scenario}`) - pageObjects - .accordionHeading() - .filter(':visible') - .should('have.length', enResults.eligible.length) - .and( - 'contain', - EN_LOCALE_DATA.resultsView.benefitAccordion.eligibleStatusLabels[0] - ) - .and('contain', enResults.eligible.eligible_benefits[0]) - .and('contain', enResults.eligible.eligible_benefits[1]) - .and('contain', enResults.eligible.eligible_benefits[2]) - - pageObjects - .benefitResultsView() - .invoke('attr', 'data-testid') - .should('eq', 'bf-result-view') - - pageObjects - .benefitResultsView() - .invoke('attr', 'data-test-results-view') - .should('eq', 'bf-eligible-view') - - pageObjects - .benefitResultsView() - .invoke('attr', 'data-test-results-view-criteria-values') - .should('eq', `${selectDataLength}`) - - pageObjects - .benefitResultsView() - .invoke('attr', 'data-test-results-view-benefits') - .should('eq', `${benefitsCount}`) - - pageObjects - .benefitResultsView() - .invoke('attr', 'data-test-results-view-eligible') - .should('eq', `${enResults.eligible.length}`) - - pageObjects - .benefitResultsView() - .invoke('attr', 'data-test-results-view-more-info') - .should('eq', `${enResults.moreInformationNeeded.length}`) - - pageObjects - .benefitResultsView() - .invoke('attr', 'data-test-results-view-not-eligible') - .should( - 'eq', - `${benefitsCount - enResults.eligible.length - enResults.moreInformationNeeded.length}` - ) + // Validate accordion headings + cy.validateAccordionHeadings( + enResults.eligible.length, + enResults.eligible.eligible_benefits, + EN_LOCALE_DATA.resultsView.benefitAccordion.eligibleStatusLabels[0] + ) + + // Validate results view attributes + cy.validateResultsViewAttributes( + selectDataLength, + benefitsCount, + enResults.eligible.length, + enResults.moreInformationNeeded.length + ) }) it('QA scenario 2 Veteran EN - Verify correct benefit results for query values that includes veteran in search parameter of URL', () => { - const selectedData = BENEFITS_ELIBILITY_DATA.scenario_2_veteran.en.param - const enResults = BENEFITS_ELIBILITY_DATA.scenario_2_veteran.en.results + const selectedData = BENEFITS_ELIGIBILITY_DATA.scenario_2_veteran.en.param + const enResults = BENEFITS_ELIGIBILITY_DATA.scenario_2_veteran.en.results const scenario = utils.encodeURIFromObject(selectedData) cy.visit(`${utils.storybookUri}${scenario}`) - pageObjects - .accordionHeading() - .filter(':visible') - .should('have.length', enResults.eligible.length) - .and( - 'contain', - EN_LOCALE_DATA.resultsView.benefitAccordion.eligibleStatusLabels[0] - ) - .and('contain', enResults.eligible.eligible_benefits[0]) + // Validate accordion headings + cy.validateAccordionHeadings( + enResults.eligible.length, + enResults.eligible.eligible_benefits, + EN_LOCALE_DATA.resultsView.benefitAccordion.eligibleStatusLabels[0] + ) }) it('QA scenario 3 Coal Miner EN - Verify correct benefit results for query values that includes Coal Miner in search parameter of URL', () => { - const selectedData = BENEFITS_ELIBILITY_DATA.scenario_3_coal_miner.en.param - const enResults = BENEFITS_ELIBILITY_DATA.scenario_3_coal_miner.en.results + const selectedData = + BENEFITS_ELIGIBILITY_DATA.scenario_3_coal_miner.en.param + const enResults = BENEFITS_ELIGIBILITY_DATA.scenario_3_coal_miner.en.results const scenario = utils.encodeURIFromObject(selectedData) cy.visit(`${utils.storybookUri}${scenario}`) - pageObjects - .accordionHeading() - .filter(':visible') - .should('have.length', enResults.eligible.length) - .and( - 'contain', - EN_LOCALE_DATA.resultsView.benefitAccordion.eligibleStatusLabels[0] - ) - .and('contain', enResults.eligible.eligible_benefits[0]) + cy.validateAccordionHeadings( + enResults.eligible.length, + enResults.eligible.eligible_benefits, + EN_LOCALE_DATA.resultsView.benefitAccordion.eligibleStatusLabels[0] + ) }) it('Should display green check icons on eligible benefits', () => { - const selectedData = BENEFITS_ELIBILITY_DATA.scenario_2_veteran.en.param + const selectedData = BENEFITS_ELIGIBILITY_DATA.scenario_2_veteran.en.param const scenario = utils.encodeURIFromObject(selectedData) cy.visit(`${utils.storybookUri}${scenario}`) - pageObjects.expandAll().click() - pageObjects.iconGreenCheck().should('exist') + cy.validateGreenCheckIcons(selectedData) }) it('Should display Zero benefit view when no benefit are eligible', () => { - const selectedData = BENEFITS_ELIBILITY_DATA.zero_benefit_view.en.param - const enResults = BENEFITS_ELIBILITY_DATA.zero_benefit_view.en.results + const selectedData = BENEFITS_ELIGIBILITY_DATA.zero_benefit_view.en.param + const enResults = BENEFITS_ELIGIBILITY_DATA.zero_benefit_view.en.results const scenario = utils.encodeURIFromObject(selectedData) cy.visit(`${utils.storybookUri}${scenario}`) - pageObjects - .zeroBenefitsViewHeading() - .should('contain', EN_LOCALE_DATA.resultsView.zeroBenefits.heading) - - pageObjects - .accordionHeading() - .filter(':visible') - .should('have.length', enResults.eligible.length) - - pageObjects - .seeAllBenefitsButton() - .should('contain', EN_LOCALE_DATA.resultsView.zeroBenefits.cta) + cy.validateZeroBenefitsView( + enResults, + EN_LOCALE_DATA.resultsView.zeroBenefits.heading, + EN_LOCALE_DATA.resultsView.zeroBenefits.cta + ) }) -}) \ No newline at end of file +}) diff --git a/benefit-finder/cypress/e2e/usagov-public-site/links.cy.js b/benefit-finder/cypress/e2e/usagov-public-site/links.cy.js index 3593e94b2..67bba145b 100644 --- a/benefit-finder/cypress/e2e/usagov-public-site/links.cy.js +++ b/benefit-finder/cypress/e2e/usagov-public-site/links.cy.js @@ -1,5 +1,5 @@ import * as utils from '../../support/utils' -import * as BENEFITS_ELIBILITY_DATA from '../../fixtures/benefits-eligibility.json' +import * as BENEFITS_ELIGIBILITY_DATA from '../../fixtures/benefits-eligibility.json' const localePaths = { en: [ @@ -14,7 +14,7 @@ const localePaths = { ], } -const handlerequest = ({ testLink, link }) => { +const handleRequest = ({ testLink, link }) => { const url = testLink || link.prop('href') return cy .request({ @@ -39,7 +39,7 @@ const handlerequest = ({ testLink, link }) => { const validateErrorCodes = () => { // we verify site is alive and fail on 404 || 503 cy.get('#benefit-finder a[href]').each(link => { - handlerequest({ link }) + handleRequest({ link }) }) } @@ -58,7 +58,6 @@ describe('Verify correct status code handling', () => { // negate validation on our functional code Cypress.on('fail', error => { if (JSON.stringify(error).includes('httpstat')) { - expect(error).to.not.be.undefined } else { throw error @@ -66,23 +65,23 @@ describe('Verify correct status code handling', () => { }) it(`handles 404 with an error`, () => { - handlerequest({ testLink: 'https://httpstat.us/404' }) + handleRequest({ testLink: 'https://httpstat.us/404' }) }) it(`handles 503 with an error`, () => { - handlerequest({ testLink: 'https://httpstat.us/503' }) + handleRequest({ testLink: 'https://httpstat.us/503' }) }) it(`handles 200 successfully`, () => { - handlerequest({ testLink: 'https://httpstat.us/200' }) + handleRequest({ testLink: 'https://httpstat.us/200' }) }) it(`handles any 403 successfully`, () => { - handlerequest({ testLink: 'https://httpstat.us/403' }) + handleRequest({ testLink: 'https://httpstat.us/403' }) }) it(`handles any other request successfully`, () => { - handlerequest({ testLink: 'https://httpstat.us/201' }) + handleRequest({ testLink: 'https://httpstat.us/201' }) }) }) @@ -90,7 +89,7 @@ describe('Verify correct status code when user navigates links in each locales', localePaths.en.forEach(location => { it(`Verify success status code response for links in ${location.key} en page`, () => { validateLinks({ - selectedData: BENEFITS_ELIBILITY_DATA[`${location.key}`].en.param, + selectedData: BENEFITS_ELIGIBILITY_DATA[`${location.key}`].en.param, path: `benefit-finder/${location.path}`, }) }) @@ -99,7 +98,7 @@ describe('Verify correct status code when user navigates links in each locales', localePaths.es.forEach(location => { it(`Verify success status code response for links in ${location.key} es page`, () => { validateLinks({ - selectedData: BENEFITS_ELIBILITY_DATA[`${location.key}`].en.param, + selectedData: BENEFITS_ELIGIBILITY_DATA[`${location.key}`].en.param, path: `es/buscador-beneficios/${location.path}`, }) }) diff --git a/benefit-finder/cypress/support/commands.js b/benefit-finder/cypress/support/commands.js index 398334da6..e6f77cf0b 100644 --- a/benefit-finder/cypress/support/commands.js +++ b/benefit-finder/cypress/support/commands.js @@ -25,9 +25,250 @@ // Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... }) import { pageObjects } from './pageObjects' +import * as EN_DOLO_MOCK_DATA from '../../../benefit-finder/src/shared/api/mock-data/current.json' +import * as EN_LOCALE_DATA from '../../../benefit-finder/src/shared/locales/en/en.json' + +const maritalStatusId = + EN_DOLO_MOCK_DATA.data.lifeEventForm.sectionsEligibilityCriteria[0].section + .fieldsets[2].fieldset.inputs[0].inputCriteria.id +const relationshipId = + EN_DOLO_MOCK_DATA.data.lifeEventForm.sectionsEligibilityCriteria[0].section + .fieldsets[1].fieldset.inputs[0].inputCriteria.id +const startFindingBenefitsButton = EN_LOCALE_DATA.intro.button +const nextButtonGroup = EN_LOCALE_DATA.buttonGroup[1].value +const getYourResultsButton = + EN_LOCALE_DATA.reviewSelectionModal.buttonGroup[1].value Cypress.Commands.add('enterDate', (month, day, year) => { pageObjects.benefitMemorableDateById('month').select(month) pageObjects.benefitMemorableDateById('day').type(day) pageObjects.benefitMemorableDateById('year').type(year) }) + +Cypress.Commands.add('clickButton', buttonText => { + pageObjects.button().contains(buttonText).click() +}) + +Cypress.Commands.add('fillDetailsAboutTheApplicant', data => { + const { dateOfBirth, relationship, maritalStatus, optionalFields = {} } = data + + // Fill mandatory fields if provided + if (dateOfBirth) { + cy.enterDate(dateOfBirth.month, dateOfBirth.day, dateOfBirth.year) + } + + if (relationship) { + cy.selectDropdownValue(relationshipId, relationship) + } + + if (maritalStatus) { + cy.selectDropdownValue(maritalStatusId, maritalStatus) + } + + // Fill optional fields dynamically + Object.entries(optionalFields).forEach(([fieldId, optionIndex]) => { + cy.selectRadioByIdAndIndex(fieldId, optionIndex) + }) +}) + +Cypress.Commands.add('fillDetailsAboutTheDeceased', data => { + const { dateOfDeath, optionalFields = {}, additionalFields = {} } = data + + // Fill mandatory fields if provided + if (dateOfDeath) { + cy.enterDate(dateOfDeath.month, dateOfDeath.day, dateOfDeath.year) + } + + // Fill optional "Yes/No" fields dynamically + Object.entries(optionalFields).forEach(([fieldId, optionIndex]) => { + cy.selectRadioByIdAndIndex(fieldId, optionIndex) + }) + + // Fill additional dropdown fields displayed on certain conditions + Object.entries(additionalFields).forEach(([fieldId, value]) => { + cy.selectDropdownValue(fieldId, value) + }) +}) + +Cypress.Commands.add('navigateToAboutTheApplicantPage', () => { + cy.clickButton(startFindingBenefitsButton) + pageObjects.benefitMemorableDateById('month').should('exist') +}) + +Cypress.Commands.add( + 'navigateToAboutTheDeceasedPage', + ({ dateOfBirth, relationship, maritalStatus, optionalFields = {} }) => { + cy.navigateToAboutTheApplicantPage() + cy.fillDetailsAboutTheApplicant({ + dateOfBirth, + relationship, + maritalStatus, + optionalFields, + }) + cy.clickButton(nextButtonGroup) // Navigate to the next step + } +) + +Cypress.Commands.add( + 'navigateToModal', + ({ + dateOfBirth, + relationship, + maritalStatus, + optionalApplicantFields = {}, + dateOfDeath, + optionalDeceasedFields = {}, + additionalDeceasedFields = {}, + }) => { + cy.navigateToAboutTheDeceasedPage({ + dateOfBirth, + relationship, + maritalStatus, + optionalFields: optionalApplicantFields, + }) + cy.fillDetailsAboutTheDeceased({ + dateOfDeath, + optionalFields: optionalDeceasedFields, + additionalFields: additionalDeceasedFields, + }) + cy.clickButton(nextButtonGroup) // Proceed to open modal + pageObjects + .button(EN_LOCALE_DATA.reviewSelectionModal.buttonGroup[0].value) + .should('exist') + } +) + +Cypress.Commands.add( + 'navigateToBenefitResultsPage', + ({ + dateOfBirth, + relationship, + maritalStatus, + optionalApplicantFields = {}, + dateOfDeath, + optionalDeceasedFields = {}, + additionalDeceasedFields = {}, + }) => { + cy.navigateToAboutTheDeceasedPage({ + dateOfBirth, + relationship, + maritalStatus, + optionalFields: optionalApplicantFields, + }) + cy.fillDetailsAboutTheDeceased({ + dateOfDeath, + optionalFields: optionalDeceasedFields, + additionalFields: additionalDeceasedFields, + }) + cy.clickButton(nextButtonGroup) // Open modal + cy.clickButton(getYourResultsButton) + } +) + +Cypress.Commands.add('selectDropdownValue', (fieldId, value) => { + pageObjects.fieldsetById(fieldId).select(value) +}) + +Cypress.Commands.add('selectRadioByIdAndIndex', (fieldId, optionIndex = 0) => { + pageObjects.fieldsetById(fieldId).eq(optionIndex).click({ force: true }) +}) + +Cypress.Commands.add('validateAccordionContent', (accordionTitle, labels) => { + pageObjects + .accordionByTitle(accordionTitle) + .click() + .parent() + .parent() + .parent() + .find('.bf-key-eligibility-criteria-list li') + .each(($li, index) => { + cy.wrap($li).should('contain', labels[index]) + }) +}) + +// Validate accordion headings +Cypress.Commands.add( + 'validateAccordionHeadings', + (visibleHeadingsLength, eligibleBenefits, eligibleLabel) => { + pageObjects + .accordionHeading() + .filter(':visible') + .should('have.length', visibleHeadingsLength) + .and('contain', eligibleLabel) + // Dynamically validate each eligible benefit + eligibleBenefits.forEach(benefit => { + pageObjects + .accordionHeading() + .filter(':visible') + .should('contain', benefit) + }) + } +) + +Cypress.Commands.add( + 'validateAriaInvalid', + (methodName, expectedValue, ...args) => { + // Dynamically call the pageObjects method and retrieve the element + pageObjects[methodName](...args) + .invoke('attr', 'aria-invalid') + .should('eq', expectedValue) + } +) + +// Validate benefit results view attributes +Cypress.Commands.add( + 'validateResultsViewAttributes', + (selectDataLength, benefitsCount, eligibleCount, moreInfoCount) => { + pageObjects + .benefitResultsView() + .should('exist') // Ensure the element exists + .then(resultsView => { + cy.wrap(resultsView) + .invoke('attr', 'data-testid') + .should('eq', 'bf-result-view') + + cy.wrap(resultsView) + .invoke('attr', 'data-test-results-view') + .should('eq', 'bf-eligible-view') + + cy.wrap(resultsView) + .invoke('attr', 'data-test-results-view-criteria-values') + .should('eq', `${selectDataLength}`) + + cy.wrap(resultsView) + .invoke('attr', 'data-test-results-view-benefits') + .should('eq', `${benefitsCount}`) + + cy.wrap(resultsView) + .invoke('attr', 'data-test-results-view-eligible') + .should('eq', `${eligibleCount}`) + + cy.wrap(resultsView) + .invoke('attr', 'data-test-results-view-more-info') + .should('eq', `${moreInfoCount}`) + + cy.wrap(resultsView) + .invoke('attr', 'data-test-results-view-not-eligible') + .should('eq', `${benefitsCount - eligibleCount - moreInfoCount}`) + }) + } +) + +Cypress.Commands.add('validateGreenCheckIcons', () => { + pageObjects.expandAll().click() + pageObjects.iconGreenCheck().should('exist') +}) + +Cypress.Commands.add( + 'validateZeroBenefitsView', + (enResults, zeroBenefitsHeading, zeroBenefitsCTA) => { + pageObjects.zeroBenefitsViewHeading().should('contain', zeroBenefitsHeading) + + pageObjects + .accordionHeading() + .filter(':visible') + .should('have.length', enResults.eligible.length) + + pageObjects.seeAllBenefitsButton().should('contain', zeroBenefitsCTA) + } +) diff --git a/benefit-finder/cypress/support/utils.js b/benefit-finder/cypress/support/utils.js index f85803edb..ab3ec5f46 100644 --- a/benefit-finder/cypress/support/utils.js +++ b/benefit-finder/cypress/support/utils.js @@ -1,6 +1,3 @@ -import { pageObjects } from '../support/pageObjects' -import * as EN_DOLO_MOCK_DATA from '../../../benefit-finder/src/shared/api/mock-data/current.json' - export function getDateByOffset(offset) { const date = new Date(Date.now()) const n = Number(offset) @@ -28,28 +25,5 @@ export const encodeURIFromObject = obj => { .join('&') } -// can be used by all the test that are visiting in storymode +// can be used by all the test that are visiting in story mode export const storybookUri = `/iframe.html?args=&id=app--primary&viewMode=story&` - -export const dataInputs = ({ dob, relation, status, dod }) => { - // input date of birth - dob && cy.enterDate(dob.month, dob.day, dob.year) - // input relation to deceased - relation && - pageObjects - .fieldsetById( - EN_DOLO_MOCK_DATA.data.lifeEventForm.sectionsEligibilityCriteria[0] - .section.fieldsets[1].fieldset.inputs[0].inputCriteria.id - ) - .select(relation) - // input marital status - status && - pageObjects - .fieldsetById( - EN_DOLO_MOCK_DATA.data.lifeEventForm.sectionsEligibilityCriteria[0] - .section.fieldsets[2].fieldset.inputs[0].inputCriteria.id - ) - .select(status) - // input date of death - dod && cy.enterDate(dod.month, dod.day, dod.year) -} diff --git a/benefit-finder/eslint.config.mjs b/benefit-finder/eslint.config.mjs index fdece7391..02464df13 100644 --- a/benefit-finder/eslint.config.mjs +++ b/benefit-finder/eslint.config.mjs @@ -5,6 +5,7 @@ import pluginCypress from 'eslint-plugin-cypress/flat' import storybook from 'eslint-plugin-storybook' import reactPlugin from 'eslint-plugin-react' import eslintConfigPrettier from 'eslint-config-prettier' +import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended' import jsxA11y from 'eslint-plugin-jsx-a11y' // error when starting dev server: @@ -19,6 +20,7 @@ export default [ js.configs.recommended, json.configs.recommended, eslintConfigPrettier, + eslintPluginPrettierRecommended, ...storybook.configs['flat/recommended'], pluginCypress.configs.recommended, reactPlugin.configs.flat.all, diff --git a/benefit-finder/index.html b/benefit-finder/index.html index f7a5bf4ca..4bc6b86fe 100644 --- a/benefit-finder/index.html +++ b/benefit-finder/index.html @@ -56,7 +56,7 @@

pagesurvey-qual-container

This HTML file is a template. If you open it directly in the browser, you will see an empty page. - You can add webfonts, meta tags, or analytics to this file. + You can add web fonts, meta tags, or analytics to this file. The build step will place the bundled scripts into the tag. To begin the development, run `npm start` or `yarn start`. diff --git a/benefit-finder/package-lock.json b/benefit-finder/package-lock.json index 8e3a31a29..a796914bf 100644 --- a/benefit-finder/package-lock.json +++ b/benefit-finder/package-lock.json @@ -11,8 +11,7 @@ "react": "^18.3.1", "react-dom": "^18.3.1", "react-modal": "^3.16.1", - "react-router": "^6.26.2", - "react-router-dom": "^6.26.2" + "react-router": "^7.0.2" }, "devDependencies": { "@babel/preset-env": "^7.25.3", @@ -35,6 +34,7 @@ "autoprefixer": "^10.4.20", "concurrently": "^9.0.1", "cross-env": "^7.0.3", + "cspell": "^8.16.1", "cypress": "^13.14.1", "cypress-axe": "^1.5.0", "cypress-plugin-tab": "^1.0.5", @@ -43,6 +43,7 @@ "eslint-plugin-cypress": "^3.6.0", "eslint-plugin-json": "^4.0.1", "eslint-plugin-jsx-a11y": "^6.10.2", + "eslint-plugin-prettier": "^5.2.1", "eslint-plugin-react": "^7.37.2", "eslint-plugin-storybook": "^0.10.0", "http-server": "^14.1.1", @@ -1753,6 +1754,573 @@ "node": ">=0.1.90" } }, + "node_modules/@cspell/cspell-bundled-dicts": { + "version": "8.16.1", + "resolved": "https://registry.npmjs.org/@cspell/cspell-bundled-dicts/-/cspell-bundled-dicts-8.16.1.tgz", + "integrity": "sha512-EkbtoYpmiN9YPfcOoPcMnIrJBZh13mun64jPyyaYhrPPToiU5+CisZ7ZKUBGnqNaatuciMUxwIudhanQJ7Yhnw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@cspell/dict-ada": "^4.0.5", + "@cspell/dict-al": "^1.0.3", + "@cspell/dict-aws": "^4.0.7", + "@cspell/dict-bash": "^4.1.8", + "@cspell/dict-companies": "^3.1.7", + "@cspell/dict-cpp": "^6.0.2", + "@cspell/dict-cryptocurrencies": "^5.0.3", + "@cspell/dict-csharp": "^4.0.5", + "@cspell/dict-css": "^4.0.16", + "@cspell/dict-dart": "^2.2.4", + "@cspell/dict-django": "^4.1.3", + "@cspell/dict-docker": "^1.1.11", + "@cspell/dict-dotnet": "^5.0.8", + "@cspell/dict-elixir": "^4.0.6", + "@cspell/dict-en_us": "^4.3.28", + "@cspell/dict-en-common-misspellings": "^2.0.7", + "@cspell/dict-en-gb": "1.1.33", + "@cspell/dict-filetypes": "^3.0.8", + "@cspell/dict-flutter": "^1.0.3", + "@cspell/dict-fonts": "^4.0.3", + "@cspell/dict-fsharp": "^1.0.4", + "@cspell/dict-fullstack": "^3.2.3", + "@cspell/dict-gaming-terms": "^1.0.8", + "@cspell/dict-git": "^3.0.3", + "@cspell/dict-golang": "^6.0.17", + "@cspell/dict-google": "^1.0.4", + "@cspell/dict-haskell": "^4.0.4", + "@cspell/dict-html": "^4.0.10", + "@cspell/dict-html-symbol-entities": "^4.0.3", + "@cspell/dict-java": "^5.0.10", + "@cspell/dict-julia": "^1.0.4", + "@cspell/dict-k8s": "^1.0.9", + "@cspell/dict-latex": "^4.0.3", + "@cspell/dict-lorem-ipsum": "^4.0.3", + "@cspell/dict-lua": "^4.0.6", + "@cspell/dict-makefile": "^1.0.3", + "@cspell/dict-markdown": "^2.0.7", + "@cspell/dict-monkeyc": "^1.0.9", + "@cspell/dict-node": "^5.0.5", + "@cspell/dict-npm": "^5.1.14", + "@cspell/dict-php": "^4.0.13", + "@cspell/dict-powershell": "^5.0.13", + "@cspell/dict-public-licenses": "^2.0.11", + "@cspell/dict-python": "^4.2.12", + "@cspell/dict-r": "^2.0.4", + "@cspell/dict-ruby": "^5.0.7", + "@cspell/dict-rust": "^4.0.10", + "@cspell/dict-scala": "^5.0.6", + "@cspell/dict-software-terms": "^4.1.17", + "@cspell/dict-sql": "^2.1.8", + "@cspell/dict-svelte": "^1.0.5", + "@cspell/dict-swift": "^2.0.4", + "@cspell/dict-terraform": "^1.0.6", + "@cspell/dict-typescript": "^3.1.11", + "@cspell/dict-vue": "^3.0.3" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@cspell/cspell-json-reporter": { + "version": "8.16.1", + "resolved": "https://registry.npmjs.org/@cspell/cspell-json-reporter/-/cspell-json-reporter-8.16.1.tgz", + "integrity": "sha512-ue1paJ2OE2BjIBQHXFMHnFqJL5xMrE/TLveOntDSCKJw7edCGP4XJA6Q0ZfUgR/ZAP3SYKNPkajEWbDTMfG+XA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@cspell/cspell-types": "8.16.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@cspell/cspell-pipe": { + "version": "8.16.1", + "resolved": "https://registry.npmjs.org/@cspell/cspell-pipe/-/cspell-pipe-8.16.1.tgz", + "integrity": "sha512-6N+QZ3y65JRgGrQhZHmaBHESR+nC0J8nySGaYKclit8yk3jLZ/ORw9aoSGIj+dMPzImkNEDh+C1B1zdV4X8W6A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@cspell/cspell-resolver": { + "version": "8.16.1", + "resolved": "https://registry.npmjs.org/@cspell/cspell-resolver/-/cspell-resolver-8.16.1.tgz", + "integrity": "sha512-CfVI2JFMwh9/n1QuU9niEONbYCX1XGKqmyCcHQUzAapSqGzbAmFrRFnvyKwNL+mmy1bxli9EZV8f5vBco26f9Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "global-directory": "^4.0.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@cspell/cspell-service-bus": { + "version": "8.16.1", + "resolved": "https://registry.npmjs.org/@cspell/cspell-service-bus/-/cspell-service-bus-8.16.1.tgz", + "integrity": "sha512-URaralJKcdHZH/Lr25L28GJo2Ub07adHPPhOL83BvmPyGkboehmz5arjNrgQFwS+IvGjHLdp5uzEJd0xyeHGdw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@cspell/cspell-types": { + "version": "8.16.1", + "resolved": "https://registry.npmjs.org/@cspell/cspell-types/-/cspell-types-8.16.1.tgz", + "integrity": "sha512-B8bHlBaDSMDMEq++H8qO9osKUkzWUrP4CgWQyRqlXZ9EOdnJ469Tp1wghcQ7DezII3aXYrHiVKsUYY9VvjkhIg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@cspell/dict-ada": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@cspell/dict-ada/-/dict-ada-4.0.5.tgz", + "integrity": "sha512-6/RtZ/a+lhFVmrx/B7bfP7rzC4yjEYe8o74EybXcvu4Oue6J4Ey2WSYj96iuodloj1LWrkNCQyX5h4Pmcj0Iag==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-al": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@cspell/dict-al/-/dict-al-1.0.3.tgz", + "integrity": "sha512-V1HClwlfU/qwSq2Kt+MkqRAsonNu3mxjSCDyGRecdLGIHmh7yeEeaxqRiO/VZ4KP+eVSiSIlbwrb5YNFfxYZbw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-aws": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/@cspell/dict-aws/-/dict-aws-4.0.7.tgz", + "integrity": "sha512-PoaPpa2NXtSkhGIMIKhsJUXB6UbtTt6Ao3x9JdU9kn7fRZkwD4RjHDGqulucIOz7KeEX/dNRafap6oK9xHe4RA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-bash": { + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@cspell/dict-bash/-/dict-bash-4.1.8.tgz", + "integrity": "sha512-I2CM2pTNthQwW069lKcrVxchJGMVQBzru2ygsHCwgidXRnJL/NTjAPOFTxN58Jc1bf7THWghfEDyKX/oyfc0yg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-companies": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/@cspell/dict-companies/-/dict-companies-3.1.7.tgz", + "integrity": "sha512-ncVs/efuAkP1/tLDhWbXukBjgZ5xOUfe03neHMWsE8zvXXc5+Lw6TX5jaJXZLOoES/f4j4AhRE20jsPCF5pm+A==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-cpp": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@cspell/dict-cpp/-/dict-cpp-6.0.2.tgz", + "integrity": "sha512-yw5eejWvY4bAnc6LUA44m4WsFwlmgPt2uMSnO7QViGMBDuoeopMma4z9XYvs4lSjTi8fIJs/A1YDfM9AVzb8eg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-cryptocurrencies": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/@cspell/dict-cryptocurrencies/-/dict-cryptocurrencies-5.0.3.tgz", + "integrity": "sha512-bl5q+Mk+T3xOZ12+FG37dB30GDxStza49Rmoax95n37MTLksk9wBo1ICOlPJ6PnDUSyeuv4SIVKgRKMKkJJglA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-csharp": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@cspell/dict-csharp/-/dict-csharp-4.0.5.tgz", + "integrity": "sha512-c/sFnNgtRwRJxtC3JHKkyOm+U3/sUrltFeNwml9VsxKBHVmvlg4tk4ar58PdpW9/zTlGUkWi2i85//DN1EsUCA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-css": { + "version": "4.0.16", + "resolved": "https://registry.npmjs.org/@cspell/dict-css/-/dict-css-4.0.16.tgz", + "integrity": "sha512-70qu7L9z/JR6QLyJPk38fNTKitlIHnfunx0wjpWQUQ8/jGADIhMCrz6hInBjqPNdtGpYm8d1dNFyF8taEkOgrQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-dart": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/@cspell/dict-dart/-/dict-dart-2.2.4.tgz", + "integrity": "sha512-of/cVuUIZZK/+iqefGln8G3bVpfyN6ZtH+LyLkHMoR5tEj+2vtilGNk9ngwyR8L4lEqbKuzSkOxgfVjsXf5PsQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-data-science": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@cspell/dict-data-science/-/dict-data-science-2.0.5.tgz", + "integrity": "sha512-nNSILXmhSJox9/QoXICPQgm8q5PbiSQP4afpbkBqPi/u/b3K9MbNH5HvOOa6230gxcGdbZ9Argl2hY/U8siBlg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-django": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/@cspell/dict-django/-/dict-django-4.1.3.tgz", + "integrity": "sha512-yBspeL3roJlO0a1vKKNaWABURuHdHZ9b1L8d3AukX0AsBy9snSggc8xCavPmSzNfeMDXbH+1lgQiYBd3IW03fg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-docker": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/@cspell/dict-docker/-/dict-docker-1.1.11.tgz", + "integrity": "sha512-s0Yhb16/R+UT1y727ekbR/itWQF3Qz275DR1ahOa66wYtPjHUXmhM3B/LT3aPaX+hD6AWmK23v57SuyfYHUjsw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-dotnet": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/@cspell/dict-dotnet/-/dict-dotnet-5.0.8.tgz", + "integrity": "sha512-MD8CmMgMEdJAIPl2Py3iqrx3B708MbCIXAuOeZ0Mzzb8YmLmiisY7QEYSZPg08D7xuwARycP0Ki+bb0GAkFSqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-elixir": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@cspell/dict-elixir/-/dict-elixir-4.0.6.tgz", + "integrity": "sha512-TfqSTxMHZ2jhiqnXlVKM0bUADtCvwKQv2XZL/DI0rx3doG8mEMS8SGPOmiyyGkHpR/pGOq18AFH3BEm4lViHIw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-en_us": { + "version": "4.3.28", + "resolved": "https://registry.npmjs.org/@cspell/dict-en_us/-/dict-en_us-4.3.28.tgz", + "integrity": "sha512-BN1PME7cOl7DXRQJ92pEd1f0Xk5sqjcDfThDGkKcsgwbSOY7KnTc/czBW6Pr3WXIchIm6cT12KEfjNqx7U7Rrw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-en-common-misspellings": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@cspell/dict-en-common-misspellings/-/dict-en-common-misspellings-2.0.7.tgz", + "integrity": "sha512-qNFo3G4wyabcwnM+hDrMYKN9vNVg/k9QkhqSlSst6pULjdvPyPs1mqz1689xO/v9t8e6sR4IKc3CgUXDMTYOpA==", + "dev": true, + "license": "CC BY-SA 4.0" + }, + "node_modules/@cspell/dict-en-gb": { + "version": "1.1.33", + "resolved": "https://registry.npmjs.org/@cspell/dict-en-gb/-/dict-en-gb-1.1.33.tgz", + "integrity": "sha512-tKSSUf9BJEV+GJQAYGw5e+ouhEe2ZXE620S7BLKe3ZmpnjlNG9JqlnaBhkIMxKnNFkLY2BP/EARzw31AZnOv4g==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-filetypes": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/@cspell/dict-filetypes/-/dict-filetypes-3.0.9.tgz", + "integrity": "sha512-U7ycC1cE32A5aEgwzp/iE0TVabonUFnVt+Ygbf6NsIWqEuFWZgZChC7gfztA4T1fpuj602nFdp7eOnTWKORsnQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-flutter": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@cspell/dict-flutter/-/dict-flutter-1.0.3.tgz", + "integrity": "sha512-52C9aUEU22ptpgYh6gQyIdA4MP6NPwzbEqndfgPh3Sra191/kgs7CVqXiO1qbtZa9gnYHUoVApkoxRE7mrXHfg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-fonts": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@cspell/dict-fonts/-/dict-fonts-4.0.3.tgz", + "integrity": "sha512-sPd17kV5qgYXLteuHFPn5mbp/oCHKgitNfsZLFC3W2fWEgZlhg4hK+UGig3KzrYhhvQ8wBnmZrAQm0TFKCKzsA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-fsharp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@cspell/dict-fsharp/-/dict-fsharp-1.0.4.tgz", + "integrity": "sha512-G5wk0o1qyHUNi9nVgdE1h5wl5ylq7pcBjX8vhjHcO4XBq20D5eMoXjwqMo/+szKAqzJ+WV3BgAL50akLKrT9Rw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-fullstack": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/@cspell/dict-fullstack/-/dict-fullstack-3.2.3.tgz", + "integrity": "sha512-62PbndIyQPH11mAv0PyiyT0vbwD0AXEocPpHlCHzfb5v9SspzCCbzQ/LIBiFmyRa+q5LMW35CnSVu6OXdT+LKg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-gaming-terms": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@cspell/dict-gaming-terms/-/dict-gaming-terms-1.0.8.tgz", + "integrity": "sha512-7OL0zTl93WFWhhtpXFrtm9uZXItC3ncAs8d0iQDMMFVNU1rBr6raBNxJskxE5wx2Ant12fgI66ZGVagXfN+yfA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-git": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@cspell/dict-git/-/dict-git-3.0.3.tgz", + "integrity": "sha512-LSxB+psZ0qoj83GkyjeEH/ZViyVsGEF/A6BAo8Nqc0w0HjD2qX/QR4sfA6JHUgQ3Yi/ccxdK7xNIo67L2ScW5A==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-golang": { + "version": "6.0.17", + "resolved": "https://registry.npmjs.org/@cspell/dict-golang/-/dict-golang-6.0.17.tgz", + "integrity": "sha512-uDDLEJ/cHdLiqPw4+5BnmIo2i/TSR+uDvYd6JlBjTmjBKpOCyvUgYRztH7nv5e7virsN5WDiUWah4/ATQGz4Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-google": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@cspell/dict-google/-/dict-google-1.0.4.tgz", + "integrity": "sha512-JThUT9eiguCja1mHHLwYESgxkhk17Gv7P3b1S7ZJzXw86QyVHPrbpVoMpozHk0C9o+Ym764B7gZGKmw9uMGduQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-haskell": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@cspell/dict-haskell/-/dict-haskell-4.0.4.tgz", + "integrity": "sha512-EwQsedEEnND/vY6tqRfg9y7tsnZdxNqOxLXSXTsFA6JRhUlr8Qs88iUUAfsUzWc4nNmmzQH2UbtT25ooG9x4nA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-html": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/@cspell/dict-html/-/dict-html-4.0.10.tgz", + "integrity": "sha512-I9uRAcdtHbh0wEtYZlgF0TTcgH0xaw1B54G2CW+tx4vHUwlde/+JBOfIzird4+WcMv4smZOfw+qHf7puFUbI5g==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-html-symbol-entities": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@cspell/dict-html-symbol-entities/-/dict-html-symbol-entities-4.0.3.tgz", + "integrity": "sha512-aABXX7dMLNFdSE8aY844X4+hvfK7977sOWgZXo4MTGAmOzR8524fjbJPswIBK7GaD3+SgFZ2yP2o0CFvXDGF+A==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-java": { + "version": "5.0.10", + "resolved": "https://registry.npmjs.org/@cspell/dict-java/-/dict-java-5.0.10.tgz", + "integrity": "sha512-pVNcOnmoGiNL8GSVq4WbX/Vs2FGS0Nej+1aEeGuUY9CU14X8yAVCG+oih5ZoLt1jaR8YfR8byUF8wdp4qG4XIw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-julia": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@cspell/dict-julia/-/dict-julia-1.0.4.tgz", + "integrity": "sha512-bFVgNX35MD3kZRbXbJVzdnN7OuEqmQXGpdOi9jzB40TSgBTlJWA4nxeAKV4CPCZxNRUGnLH0p05T/AD7Aom9/w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-k8s": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@cspell/dict-k8s/-/dict-k8s-1.0.9.tgz", + "integrity": "sha512-Q7GELSQIzo+BERl2ya/nBEnZeQC+zJP19SN1pI6gqDYraM51uYJacbbcWLYYO2Y+5joDjNt/sd/lJtLaQwoSlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-latex": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@cspell/dict-latex/-/dict-latex-4.0.3.tgz", + "integrity": "sha512-2KXBt9fSpymYHxHfvhUpjUFyzrmN4c4P8mwIzweLyvqntBT3k0YGZJSriOdjfUjwSygrfEwiuPI1EMrvgrOMJw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-lorem-ipsum": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@cspell/dict-lorem-ipsum/-/dict-lorem-ipsum-4.0.3.tgz", + "integrity": "sha512-WFpDi/PDYHXft6p0eCXuYnn7mzMEQLVeqpO+wHSUd+kz5ADusZ4cpslAA4wUZJstF1/1kMCQCZM6HLZic9bT8A==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-lua": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@cspell/dict-lua/-/dict-lua-4.0.6.tgz", + "integrity": "sha512-Jwvh1jmAd9b+SP9e1GkS2ACbqKKRo9E1f9GdjF/ijmooZuHU0hPyqvnhZzUAxO1egbnNjxS/J2T6iUtjAUK2KQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-makefile": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@cspell/dict-makefile/-/dict-makefile-1.0.3.tgz", + "integrity": "sha512-R3U0DSpvTs6qdqfyBATnePj9Q/pypkje0Nj26mQJ8TOBQutCRAJbr2ZFAeDjgRx5EAJU/+8txiyVF97fbVRViw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-markdown": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@cspell/dict-markdown/-/dict-markdown-2.0.7.tgz", + "integrity": "sha512-F9SGsSOokFn976DV4u/1eL4FtKQDSgJHSZ3+haPRU5ki6OEqojxKa8hhj4AUrtNFpmBaJx/WJ4YaEzWqG7hgqg==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@cspell/dict-css": "^4.0.16", + "@cspell/dict-html": "^4.0.10", + "@cspell/dict-html-symbol-entities": "^4.0.3", + "@cspell/dict-typescript": "^3.1.11" + } + }, + "node_modules/@cspell/dict-monkeyc": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@cspell/dict-monkeyc/-/dict-monkeyc-1.0.9.tgz", + "integrity": "sha512-Jvf6g5xlB4+za3ThvenYKREXTEgzx5gMUSzrAxIiPleVG4hmRb/GBSoSjtkGaibN3XxGx5x809gSTYCA/IHCpA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-node": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/@cspell/dict-node/-/dict-node-5.0.5.tgz", + "integrity": "sha512-7NbCS2E8ZZRZwlLrh2sA0vAk9n1kcTUiRp/Nia8YvKaItGXLfxYqD2rMQ3HpB1kEutal6hQLVic3N2Yi1X7AaA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-npm": { + "version": "5.1.16", + "resolved": "https://registry.npmjs.org/@cspell/dict-npm/-/dict-npm-5.1.16.tgz", + "integrity": "sha512-9z0YLQIu88pC6BOMbHUYksXoSyhzwWvZa9XVvN/1rGMov1kTgStr2QDvsBpECk3AIamzZnahKIyUvJrr+rOkYg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-php": { + "version": "4.0.13", + "resolved": "https://registry.npmjs.org/@cspell/dict-php/-/dict-php-4.0.13.tgz", + "integrity": "sha512-P6sREMZkhElzz/HhXAjahnICYIqB/HSGp1EhZh+Y6IhvC15AzgtDP8B8VYCIsQof6rPF1SQrFwunxOv8H1e2eg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-powershell": { + "version": "5.0.13", + "resolved": "https://registry.npmjs.org/@cspell/dict-powershell/-/dict-powershell-5.0.13.tgz", + "integrity": "sha512-0qdj0XZIPmb77nRTynKidRJKTU0Fl+10jyLbAhFTuBWKMypVY06EaYFnwhsgsws/7nNX8MTEQuewbl9bWFAbsg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-public-licenses": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/@cspell/dict-public-licenses/-/dict-public-licenses-2.0.11.tgz", + "integrity": "sha512-rR5KjRUSnVKdfs5G+gJ4oIvQvm8+NJ6cHWY2N+GE69/FSGWDOPHxulCzeGnQU/c6WWZMSimG9o49i9r//lUQyA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-python": { + "version": "4.2.13", + "resolved": "https://registry.npmjs.org/@cspell/dict-python/-/dict-python-4.2.13.tgz", + "integrity": "sha512-mZIcmo9qif8LkJ6N/lqTZawcOk2kVTcuWIUOSbMcjyomO0XZ7iWz15TfONyr03Ea/l7o5ULV+MZ4vx76bAUb7w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@cspell/dict-data-science": "^2.0.5" + } + }, + "node_modules/@cspell/dict-r": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@cspell/dict-r/-/dict-r-2.0.4.tgz", + "integrity": "sha512-cBpRsE/U0d9BRhiNRMLMH1PpWgw+N+1A2jumgt1if9nBGmQw4MUpg2u9I0xlFVhstTIdzXiLXMxP45cABuiUeQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-ruby": { + "version": "5.0.7", + "resolved": "https://registry.npmjs.org/@cspell/dict-ruby/-/dict-ruby-5.0.7.tgz", + "integrity": "sha512-4/d0hcoPzi5Alk0FmcyqlzFW9lQnZh9j07MJzPcyVO62nYJJAGKaPZL2o4qHeCS/od/ctJC5AHRdoUm0ktsw6Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-rust": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/@cspell/dict-rust/-/dict-rust-4.0.10.tgz", + "integrity": "sha512-6o5C8566VGTTctgcwfF3Iy7314W0oMlFFSQOadQ0OEdJ9Z9ERX/PDimrzP3LGuOrvhtEFoK8pj+BLnunNwRNrw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-scala": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/@cspell/dict-scala/-/dict-scala-5.0.6.tgz", + "integrity": "sha512-tl0YWAfjUVb4LyyE4JIMVE8DlLzb1ecHRmIWc4eT6nkyDqQgHKzdHsnusxFEFMVLIQomgSg0Zz6hJ5S1E4W4ww==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-software-terms": { + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/@cspell/dict-software-terms/-/dict-software-terms-4.1.18.tgz", + "integrity": "sha512-QhOQ3qVFr2Y+uDw2SH15klVNU2S07ecFhG+2gpTO/K4Kuaui3INbVrzHOKW41ofP3ghv9y2TkUUtOP5KfddP8A==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-sql": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/@cspell/dict-sql/-/dict-sql-2.1.8.tgz", + "integrity": "sha512-dJRE4JV1qmXTbbGm6WIcg1knmR6K5RXnQxF4XHs5HA3LAjc/zf77F95i5LC+guOGppVF6Hdl66S2UyxT+SAF3A==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-svelte": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@cspell/dict-svelte/-/dict-svelte-1.0.5.tgz", + "integrity": "sha512-sseHlcXOqWE4Ner9sg8KsjxwSJ2yssoJNqFHR9liWVbDV+m7kBiUtn2EB690TihzVsEmDr/0Yxrbb5Bniz70mA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-swift": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@cspell/dict-swift/-/dict-swift-2.0.4.tgz", + "integrity": "sha512-CsFF0IFAbRtYNg0yZcdaYbADF5F3DsM8C4wHnZefQy8YcHP/qjAF/GdGfBFBLx+XSthYuBlo2b2XQVdz3cJZBw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-terraform": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@cspell/dict-terraform/-/dict-terraform-1.0.6.tgz", + "integrity": "sha512-Sqm5vGbXuI9hCFcr4w6xWf4Y25J9SdleE/IqfM6RySPnk8lISEmVdax4k6+Kinv9qaxyvnIbUUN4WFLWcBPQAg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-typescript": { + "version": "3.1.11", + "resolved": "https://registry.npmjs.org/@cspell/dict-typescript/-/dict-typescript-3.1.11.tgz", + "integrity": "sha512-FwvK5sKbwrVpdw0e9+1lVTl8FPoHYvfHRuQRQz2Ql5XkC0gwPPkpoyD1zYImjIyZRoYXk3yp9j8ss4iz7A7zoQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-vue": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@cspell/dict-vue/-/dict-vue-3.0.3.tgz", + "integrity": "sha512-akmYbrgAGumqk1xXALtDJcEcOMYBYMnkjpmGzH13Ozhq1mkPF4VgllFQlm1xYde+BUKNnzMgPEzxrL2qZllgYA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dynamic-import": { + "version": "8.16.1", + "resolved": "https://registry.npmjs.org/@cspell/dynamic-import/-/dynamic-import-8.16.1.tgz", + "integrity": "sha512-mEfdeS1kFKpJoDsQ8wW6PxO3+ncYuZCWCASR0trbzZDduzO2RcogMUgzP99obHtYbgXadw94qcQWXB8OYTPSwg==", + "dev": true, + "license": "MIT", + "dependencies": { + "import-meta-resolve": "^4.1.0" + }, + "engines": { + "node": ">=18.0" + } + }, + "node_modules/@cspell/filetypes": { + "version": "8.16.1", + "resolved": "https://registry.npmjs.org/@cspell/filetypes/-/filetypes-8.16.1.tgz", + "integrity": "sha512-zpbNg3n26muR1jdMbylw5YsaVGyS9LU5Lfy20gU7RygAk6kFyx3Yz4C84EihBGQHy2gVEsEeyCCxk+R8RXuPZA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@cspell/strong-weak-map": { + "version": "8.16.1", + "resolved": "https://registry.npmjs.org/@cspell/strong-weak-map/-/strong-weak-map-8.16.1.tgz", + "integrity": "sha512-jJQS05wg2iUkLKnPR8NEq3LqvqHWKnvUDFoPwaJzYw6ol/O4yi/lv+Me9+XCPrgjpnAz+8APhWkhrR/O71R1Bw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@cspell/url": { + "version": "8.16.1", + "resolved": "https://registry.npmjs.org/@cspell/url/-/url-8.16.1.tgz", + "integrity": "sha512-kGlr7Wdo4xJpXKal/Gqo3Ll5Is7ptlIlLZOB/hzR6R53Fw4N6SdipTDIeHHqC15p2AXTEG6TSNdhk9dA50LY6w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.0" + } + }, "node_modules/@csstools/css-parser-algorithms": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.4.tgz", @@ -2852,13 +3420,17 @@ "node": ">=14" } }, - "node_modules/@remix-run/router": { - "version": "1.21.0", - "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.21.0.tgz", - "integrity": "sha512-xfSkCAchbdG5PnbrKqFWwia4Bi61nH+wm8wLEqfHDyp7Y3dZzgqS2itV8i4gAq9pC2HsTpwyBC6Ds8VHZ96JlA==", + "node_modules/@pkgr/core": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.1.1.tgz", + "integrity": "sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==", + "dev": true, "license": "MIT", "engines": { - "node": ">=14.0.0" + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts" } }, "node_modules/@rollup/pluginutils": { @@ -3977,6 +4549,12 @@ "@babel/types": "^7.20.7" } }, + "node_modules/@types/cookie": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==", + "license": "MIT" + }, "node_modules/@types/debug": { "version": "4.1.12", "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", @@ -4818,6 +5396,13 @@ "node": ">=0.10.0" } }, + "node_modules/array-timsort": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/array-timsort/-/array-timsort-1.0.3.tgz", + "integrity": "sha512-/+3GRL7dDAGEfM6TseQk/U+mi18TU2Ms9I3UlLdUMhz2hbvGNTKdj9xniwXfUqgYhHxRx0+8UnKkvlNwVU+cWQ==", + "dev": true, + "license": "MIT" + }, "node_modules/array-union": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", @@ -5531,18 +6116,47 @@ "node": ">=12" } }, - "node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chalk-template": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/chalk-template/-/chalk-template-1.1.0.tgz", + "integrity": "sha512-T2VJbcDuZQ0Tb2EWwSotMPJjgpy1/tGee1BTpUNsGZ/qgNjV2t7Mvu+d4600U564nbLesN1x2dPL+xii174Ekg==", "dev": true, "license": "MIT", "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "chalk": "^5.2.0" }, "engines": { - "node": ">=10" + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/chalk/chalk-template?sponsor=1" + } + }, + "node_modules/chalk-template/node_modules/chalk": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", + "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" }, "funding": { "url": "https://github.com/chalk/chalk?sponsor=1" @@ -5671,6 +6285,46 @@ "node": ">=6" } }, + "node_modules/clear-module": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/clear-module/-/clear-module-4.1.2.tgz", + "integrity": "sha512-LWAxzHqdHsAZlPlEyJ2Poz6AIs384mPeqLVCru2p0BrP9G/kVGuhNyZYClLO6cXlnuJjzC8xtsJIuMjKqLXoAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "parent-module": "^2.0.0", + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/clear-module/node_modules/parent-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-2.0.0.tgz", + "integrity": "sha512-uo0Z9JJeWzv8BG+tRcapBKNJ0dro9cLyczGzulS6EfeyAdeC9sbojtW6XwvYxJkEne9En+J2XEl4zyglVeIwFg==", + "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^3.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/clear-module/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/cli-cursor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", @@ -5829,6 +6483,30 @@ "node": ">= 6" } }, + "node_modules/comment-json": { + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/comment-json/-/comment-json-4.2.5.tgz", + "integrity": "sha512-bKw/r35jR3HGt5PEPm1ljsQQGyCrR8sFGNiN5L+ykDHdpO8Smxkrkla9Yi6NkQyUrb8V54PGhfMs6NrIwtxtdw==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-timsort": "^1.0.3", + "core-util-is": "^1.0.3", + "esprima": "^4.0.1", + "has-own-prop": "^2.0.0", + "repeat-string": "^1.6.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/comment-json/node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "dev": true, + "license": "MIT" + }, "node_modules/common-tags": { "version": "1.8.2", "resolved": "https://registry.npmjs.org/common-tags/-/common-tags-1.8.2.tgz", @@ -5907,6 +6585,15 @@ "dev": true, "license": "MIT" }, + "node_modules/cookie": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.0.2.tgz", + "integrity": "sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/core-js-compat": { "version": "3.39.0", "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.39.0.tgz", @@ -5914,89 +6601,371 @@ "dev": true, "license": "MIT", "dependencies": { - "browserslist": "^4.24.2" + "browserslist": "^4.24.2" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "node_modules/core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/corser": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/corser/-/corser-2.0.1.tgz", + "integrity": "sha512-utCYNzRSQIZNPIcGZdQc92UVJYAhtGAteCFg0yRaFm8f0P+CPtyGyHXJcGXnffjCybUCEx3FQ2G7U3/o9eIkVQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/cosmiconfig": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.0.tgz", + "integrity": "sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==", + "dev": true, + "license": "MIT", + "dependencies": { + "env-paths": "^2.2.1", + "import-fresh": "^3.3.0", + "js-yaml": "^4.1.0", + "parse-json": "^5.2.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/d-fischer" + }, + "peerDependencies": { + "typescript": ">=4.9.5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/cross-env": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz", + "integrity": "sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.1" + }, + "bin": { + "cross-env": "src/bin/cross-env.js", + "cross-env-shell": "src/bin/cross-env-shell.js" + }, + "engines": { + "node": ">=10.14", + "npm": ">=6", + "yarn": ">=1" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/cspell": { + "version": "8.16.1", + "resolved": "https://registry.npmjs.org/cspell/-/cspell-8.16.1.tgz", + "integrity": "sha512-ILuCjnY3JPY2oO62PodTQD6n3DGTKTwB+IU1tE9EC6EP2Xw6z3Ir+hO2DO6QlRUmZlGrkGMek5U06nNmztt4eA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@cspell/cspell-json-reporter": "8.16.1", + "@cspell/cspell-pipe": "8.16.1", + "@cspell/cspell-types": "8.16.1", + "@cspell/dynamic-import": "8.16.1", + "@cspell/url": "8.16.1", + "chalk": "^5.3.0", + "chalk-template": "^1.1.0", + "commander": "^12.1.0", + "cspell-dictionary": "8.16.1", + "cspell-gitignore": "8.16.1", + "cspell-glob": "8.16.1", + "cspell-io": "8.16.1", + "cspell-lib": "8.16.1", + "fast-json-stable-stringify": "^2.1.0", + "file-entry-cache": "^9.1.0", + "get-stdin": "^9.0.0", + "semver": "^7.6.3", + "tinyglobby": "^0.2.10" + }, + "bin": { + "cspell": "bin.mjs", + "cspell-esm": "bin.mjs" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/streetsidesoftware/cspell?sponsor=1" + } + }, + "node_modules/cspell-config-lib": { + "version": "8.16.1", + "resolved": "https://registry.npmjs.org/cspell-config-lib/-/cspell-config-lib-8.16.1.tgz", + "integrity": "sha512-ohbSi9sI14rMdFc2g17ogObGGkd/x6zUVOzCH1nEOefC9yJYYfsvaMHqdhk0rOjvmF95j5OK4dm5oid+DKQcpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@cspell/cspell-types": "8.16.1", + "comment-json": "^4.2.5", + "yaml": "^2.6.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/cspell-config-lib/node_modules/yaml": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.6.1.tgz", + "integrity": "sha512-7r0XPzioN/Q9kXBro/XPnA6kznR73DHq+GXh5ON7ZozRO6aMjbmiBuKste2wslTFkC5d1dw0GooOCepZXJ2SAg==", + "dev": true, + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/cspell-dictionary": { + "version": "8.16.1", + "resolved": "https://registry.npmjs.org/cspell-dictionary/-/cspell-dictionary-8.16.1.tgz", + "integrity": "sha512-NL/vwf5SjtkWWaEUh+0dogKdEU4UuepJaNh36FX8W1CFtQXj7yEs45x4K7/Fp+pn/4AT7Qe7WpSSWi9z5GcqKg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@cspell/cspell-pipe": "8.16.1", + "@cspell/cspell-types": "8.16.1", + "cspell-trie-lib": "8.16.1", + "fast-equals": "^5.0.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/cspell-gitignore": { + "version": "8.16.1", + "resolved": "https://registry.npmjs.org/cspell-gitignore/-/cspell-gitignore-8.16.1.tgz", + "integrity": "sha512-Gg8qvFc8wr1D7TvB+GSfT1jyrUoUmPiG3WdOnQnxOSYKJesOiVvNxLv7YXRFkcUKG1VU6XDUkpb/uzKh3k2rKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@cspell/url": "8.16.1", + "cspell-glob": "8.16.1", + "cspell-io": "8.16.1", + "find-up-simple": "^1.0.0" + }, + "bin": { + "cspell-gitignore": "bin.mjs" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/cspell-glob": { + "version": "8.16.1", + "resolved": "https://registry.npmjs.org/cspell-glob/-/cspell-glob-8.16.1.tgz", + "integrity": "sha512-EukaXFaUrgrY9G4bB2PguzpkAoOq6ai9acLl6gWD+6DgVEwkLqPmCWjsFJA0MaqVp9QvPsIfCy4KCnx35csG/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@cspell/url": "8.16.1", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/cspell-grammar": { + "version": "8.16.1", + "resolved": "https://registry.npmjs.org/cspell-grammar/-/cspell-grammar-8.16.1.tgz", + "integrity": "sha512-7IRYa0O1xfK2HVbhGSpOPPt5HlP2ZHRHtdLU2iOvMSCkh0cSPERu++kdprvcaOf7E7koo0P+bxHSprcYbU/agg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@cspell/cspell-pipe": "8.16.1", + "@cspell/cspell-types": "8.16.1" + }, + "bin": { + "cspell-grammar": "bin.mjs" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/cspell-io": { + "version": "8.16.1", + "resolved": "https://registry.npmjs.org/cspell-io/-/cspell-io-8.16.1.tgz", + "integrity": "sha512-25MOQfy7EhdVeoNUW/+jyb5ArDYSLbaFwVToakHtLGuYk9cW8q8MAHq1W9GzW06wXswT2sQsRvaozmIOTDIOnw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@cspell/cspell-service-bus": "8.16.1", + "@cspell/url": "8.16.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/cspell-lib": { + "version": "8.16.1", + "resolved": "https://registry.npmjs.org/cspell-lib/-/cspell-lib-8.16.1.tgz", + "integrity": "sha512-Gn1vJcyhYe78iB+9dms8rnfgDEfJgYocXapFPTOcZV3EUWKcV4wyCiHdbK3j2ElLXmPuSPg4eZSlxxk8ITD0Aw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@cspell/cspell-bundled-dicts": "8.16.1", + "@cspell/cspell-pipe": "8.16.1", + "@cspell/cspell-resolver": "8.16.1", + "@cspell/cspell-types": "8.16.1", + "@cspell/dynamic-import": "8.16.1", + "@cspell/filetypes": "8.16.1", + "@cspell/strong-weak-map": "8.16.1", + "@cspell/url": "8.16.1", + "clear-module": "^4.1.2", + "comment-json": "^4.2.5", + "cspell-config-lib": "8.16.1", + "cspell-dictionary": "8.16.1", + "cspell-glob": "8.16.1", + "cspell-grammar": "8.16.1", + "cspell-io": "8.16.1", + "cspell-trie-lib": "8.16.1", + "env-paths": "^3.0.0", + "fast-equals": "^5.0.1", + "gensequence": "^7.0.0", + "import-fresh": "^3.3.0", + "resolve-from": "^5.0.0", + "vscode-languageserver-textdocument": "^1.0.12", + "vscode-uri": "^3.0.8", + "xdg-basedir": "^5.1.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/cspell-lib/node_modules/env-paths": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-3.0.0.tgz", + "integrity": "sha512-dtJUTepzMW3Lm/NPxRf3wP4642UWhjL2sQxc+ym2YMj1m/H2zDNQOlezafzkHwn6sMstjHTwG6iQQsctDW/b1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cspell-lib/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/cspell-trie-lib": { + "version": "8.16.1", + "resolved": "https://registry.npmjs.org/cspell-trie-lib/-/cspell-trie-lib-8.16.1.tgz", + "integrity": "sha512-T86nszsjQjyZ35dOWk7qN17Hem0cVeXJ4D1v/gIG+Y0Umo7dBW7AwmTvUy8iMFAra29cSdgRH+yk6q1qdpA+ZA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@cspell/cspell-pipe": "8.16.1", + "@cspell/cspell-types": "8.16.1", + "gensequence": "^7.0.0" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/core-js" + "engines": { + "node": ">=18" } }, - "node_modules/core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==", + "node_modules/cspell/node_modules/chalk": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", + "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", "dev": true, - "license": "MIT" + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } }, - "node_modules/corser": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/corser/-/corser-2.0.1.tgz", - "integrity": "sha512-utCYNzRSQIZNPIcGZdQc92UVJYAhtGAteCFg0yRaFm8f0P+CPtyGyHXJcGXnffjCybUCEx3FQ2G7U3/o9eIkVQ==", + "node_modules/cspell/node_modules/commander": { + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", + "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", "dev": true, "license": "MIT", "engines": { - "node": ">= 0.4.0" + "node": ">=18" } }, - "node_modules/cosmiconfig": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.0.tgz", - "integrity": "sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==", + "node_modules/cspell/node_modules/file-entry-cache": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-9.1.0.tgz", + "integrity": "sha512-/pqPFG+FdxWQj+/WSuzXSDaNzxgTLr/OrR1QuqfEZzDakpdYE70PwUxL7BPUa8hpjbvY1+qvCl8k+8Tq34xJgg==", "dev": true, "license": "MIT", "dependencies": { - "env-paths": "^2.2.1", - "import-fresh": "^3.3.0", - "js-yaml": "^4.1.0", - "parse-json": "^5.2.0" + "flat-cache": "^5.0.0" }, "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/d-fischer" - }, - "peerDependencies": { - "typescript": ">=4.9.5" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "node": ">=18" } }, - "node_modules/cross-env": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz", - "integrity": "sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==", + "node_modules/cspell/node_modules/flat-cache": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-5.0.0.tgz", + "integrity": "sha512-JrqFmyUl2PnPi1OvLyTVHnQvwQ0S+e6lGSwu8OkAZlSaNIZciTY2H/cOOROxsBA1m/LZNHDsqAgDZt6akWcjsQ==", "dev": true, "license": "MIT", "dependencies": { - "cross-spawn": "^7.0.1" - }, - "bin": { - "cross-env": "src/bin/cross-env.js", - "cross-env-shell": "src/bin/cross-env-shell.js" + "flatted": "^3.3.1", + "keyv": "^4.5.4" }, "engines": { - "node": ">=10.14", - "npm": ">=6", - "yarn": ">=1" + "node": ">=18" } }, - "node_modules/cross-spawn": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", - "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "node_modules/cspell/node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", "dev": true, - "license": "MIT", - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" + "license": "ISC", + "bin": { + "semver": "bin/semver.js" }, "engines": { - "node": ">= 8" + "node": ">=10" } }, "node_modules/css-functions-list": { @@ -7135,6 +8104,37 @@ "node": ">= 0.4" } }, + "node_modules/eslint-plugin-prettier": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.2.1.tgz", + "integrity": "sha512-gH3iR3g4JfF+yYPaJYkN7jEl9QbweL/YfkoRlNnuIEHEz1vHVlCmWOS+eGGiRuzHQXdJFCOTxRgvju9b8VUmrw==", + "dev": true, + "license": "MIT", + "dependencies": { + "prettier-linter-helpers": "^1.0.0", + "synckit": "^0.9.1" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint-plugin-prettier" + }, + "peerDependencies": { + "@types/eslint": ">=8.0.0", + "eslint": ">=8.0.0", + "eslint-config-prettier": "*", + "prettier": ">=3.0.0" + }, + "peerDependenciesMeta": { + "@types/eslint": { + "optional": true + }, + "eslint-config-prettier": { + "optional": true + } + } + }, "node_modules/eslint-plugin-react": { "version": "7.37.2", "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.2.tgz", @@ -7472,6 +8472,23 @@ "dev": true, "license": "MIT" }, + "node_modules/fast-diff": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", + "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/fast-equals": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/fast-equals/-/fast-equals-5.0.1.tgz", + "integrity": "sha512-WF1Wi8PwwSY7/6Kx0vKXtw8RwuSGoM1bvDaJbu7MxDlR1vovZjIAKrnzyrThgAjm6JDTu0fVgWXDlMGspodfoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/fast-glob": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", @@ -7553,6 +8570,21 @@ "pend": "~1.2.0" } }, + "node_modules/fdir": { + "version": "6.4.2", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.2.tgz", + "integrity": "sha512-KnhMXsKSPZlAhp7+IjUkRZKPb4fUyccpDrdFXbi4QL1qkmFh9kVY09Yox+n4MaOb3lHZ1Tv829C3oaaXoMYPDQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, "node_modules/figures": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", @@ -7622,6 +8654,19 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/find-up-simple": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/find-up-simple/-/find-up-simple-1.0.0.tgz", + "integrity": "sha512-q7Us7kcjj2VMePAa02hDAF6d+MzsdsAWEwYyOpwUtlerRBkOEPBCRZrAV4XfcSN8fHAgaD0hP7miwoay6DCprw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/findup-sync": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-5.0.0.tgz", @@ -7886,6 +8931,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/gensequence": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/gensequence/-/gensequence-7.0.0.tgz", + "integrity": "sha512-47Frx13aZh01afHJTB3zTtKIlFI6vWY+MYCN9Qpew6i52rfKjnhCF/l1YlC8UmEMvvntZZ6z4PiCcmyuedR2aQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/gensync": { "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", @@ -7942,6 +8997,19 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/get-stdin": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-9.0.0.tgz", + "integrity": "sha512-dVKBjfWisLAicarI2Sf+JuBE/DghV4UzNAVe9yhEJuzeREd3JhOTE9cUaJTeSa77fsbQUK3pcOpJfM59+VKZaA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/get-stream": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", @@ -8031,6 +9099,32 @@ "node": ">=10.13.0" } }, + "node_modules/global-directory": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/global-directory/-/global-directory-4.0.1.tgz", + "integrity": "sha512-wHTUcDUoZ1H5/0iVqEudYW4/kAlN5cZ3j/bXn0Dpbizl9iaUVeWSHqiOjsgk6OW2bkLclbBjzewBz6weQ1zA2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ini": "4.1.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/global-directory/node_modules/ini": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ini/-/ini-4.1.1.tgz", + "integrity": "sha512-QQnnxNyfvmHFIsj7gkPcYymR8Jdw/o7mp5ZFihxn6h8Ci6fh3Dx4E1gPjpQEpIuPo9XVNY/ZUwh4BPMjGyL01g==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, "node_modules/global-dirs": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-3.0.1.tgz", @@ -8245,6 +9339,16 @@ "node": ">=8" } }, + "node_modules/has-own-prop": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-own-prop/-/has-own-prop-2.0.0.tgz", + "integrity": "sha512-Pq0h+hvsVm6dDEa8x82GnLSYHOzNDt7f0ddFa3FqcQlgzEiptPqL+XrOJNavjOzSYiYWIrgeVYYgGlLmnxwilQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/has-property-descriptors": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", @@ -8547,6 +9651,17 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/import-meta-resolve": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-4.1.0.tgz", + "integrity": "sha512-I6fiaX09Xivtk+THaMfAwnA3MVA5Big1WHF1Dfx9hFuvNIWpXnorlkzhcQf6ehrqQiiZECRt1poOAkPmer3ruw==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", @@ -16123,6 +17238,19 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/prettier-linter-helpers": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", + "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-diff": "^1.1.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/pretty-bytes": { "version": "5.6.0", "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz", @@ -16369,35 +17497,27 @@ } }, "node_modules/react-router": { - "version": "6.28.0", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.28.0.tgz", - "integrity": "sha512-HrYdIFqdrnhDw0PqG/AKjAqEqM7AvxCz0DQ4h2W8k6nqmc5uRBYDag0SBxx9iYz5G8gnuNVLzUe13wl9eAsXXg==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.0.2.tgz", + "integrity": "sha512-m5AcPfTRUcjwmhBzOJGEl6Y7+Crqyju0+TgTQxoS4SO+BkWbhOrcfZNq6wSWdl2BBbJbsAoBUb8ZacOFT+/JlA==", "license": "MIT", "dependencies": { - "@remix-run/router": "1.21.0" + "@types/cookie": "^0.6.0", + "cookie": "^1.0.1", + "set-cookie-parser": "^2.6.0", + "turbo-stream": "2.4.0" }, "engines": { - "node": ">=14.0.0" + "node": ">=20.0.0" }, "peerDependencies": { - "react": ">=16.8" - } - }, - "node_modules/react-router-dom": { - "version": "6.28.0", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.28.0.tgz", - "integrity": "sha512-kQ7Unsl5YdyOltsPGl31zOjLrDv+m2VcIEcIHqYYD3Lp0UppLjrzcfJqDJwXxFw3TH/yvapbnUvPlAj7Kx5nbg==", - "license": "MIT", - "dependencies": { - "@remix-run/router": "1.21.0", - "react-router": "6.28.0" + "react": ">=18", + "react-dom": ">=18" }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "react": ">=16.8", - "react-dom": ">=16.8" + "peerDependenciesMeta": { + "react-dom": { + "optional": true + } } }, "node_modules/readable-stream": { @@ -16680,6 +17800,16 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10" + } + }, "node_modules/request-progress": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/request-progress/-/request-progress-3.0.0.tgz", @@ -17555,6 +18685,12 @@ "upper-case-first": "^2.0.2" } }, + "node_modules/set-cookie-parser": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.1.tgz", + "integrity": "sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==", + "license": "MIT" + }, "node_modules/set-function-length": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", @@ -18447,6 +19583,23 @@ "node": ">=16.0.0" } }, + "node_modules/synckit": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.9.2.tgz", + "integrity": "sha512-vrozgXDQwYO72vHjUb/HnFbQx1exDjoKzqx23aXEg2a9VIg2TSFZ8FmeZpTjUCFMYw7mpX4BE2SFu8wI7asYsw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@pkgr/core": "^0.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts" + } + }, "node_modules/table": { "version": "6.9.0", "resolved": "https://registry.npmjs.org/table/-/table-6.9.0.tgz", @@ -18613,6 +19766,20 @@ "dev": true, "license": "MIT" }, + "node_modules/tinyglobby": { + "version": "0.2.10", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.10.tgz", + "integrity": "sha512-Zc+8eJlFMvgatPZTl6A9L/yht8QqdmUNtURHaKZLmKBE12hNPSrqNkUp2cs3M/UKmNVVAMFQYSjYIVHDjW5zew==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.4.2", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/tinypool": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.0.2.tgz", @@ -18801,6 +19968,12 @@ "node": "*" } }, + "node_modules/turbo-stream": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/turbo-stream/-/turbo-stream-2.4.0.tgz", + "integrity": "sha512-FHncC10WpBd2eOmGwpmQsWLDoK4cqsA/UT/GqNoaKOQnT8uzhtCbg3EoUDMvqpOSAI0S26mr0rkjzbOO6S3v1g==", + "license": "ISC" + }, "node_modules/tweetnacl": { "version": "0.14.5", "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", @@ -20456,6 +21629,19 @@ } } }, + "node_modules/xdg-basedir": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-5.1.0.tgz", + "integrity": "sha512-GCPAHLvrIH13+c0SuacwvRYj2SxJXQ4kaVTT5xgL3kPrz56XxkF21IGhjSE1+W0aw7gpBWRGXLCPnPby6lSpmQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/xml-name-validator": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz", diff --git a/benefit-finder/package.json b/benefit-finder/package.json index a6e603bf5..6bef1e1b4 100644 --- a/benefit-finder/package.json +++ b/benefit-finder/package.json @@ -19,6 +19,7 @@ "lint:scss:fix": "stylelint 'src/**/*.scss' --fix", "lint:js": "eslint . --ext .js,.jsx,.json", "lint:js:fix": "npm run lint:js -- --fix", + "lint:cspell:dev": "cspell lint '**'", "lint-staged": "lint-staged", "generate:component": "plop", "prebuild:storybook": "npm run mv:uswds:usagov && npm run build", @@ -36,8 +37,7 @@ "react": "^18.3.1", "react-dom": "^18.3.1", "react-modal": "^3.16.1", - "react-router": "^6.26.2", - "react-router-dom": "^6.26.2" + "react-router": "^7.0.2" }, "devDependencies": { "@babel/preset-env": "^7.25.3", @@ -60,6 +60,7 @@ "autoprefixer": "^10.4.20", "concurrently": "^9.0.1", "cross-env": "^7.0.3", + "cspell": "^8.16.1", "cypress": "^13.14.1", "cypress-axe": "^1.5.0", "cypress-plugin-tab": "^1.0.5", @@ -68,6 +69,7 @@ "eslint-plugin-cypress": "^3.6.0", "eslint-plugin-json": "^4.0.1", "eslint-plugin-jsx-a11y": "^6.10.2", + "eslint-plugin-prettier": "^5.2.1", "eslint-plugin-react": "^7.37.2", "eslint-plugin-storybook": "^0.10.0", "http-server": "^14.1.1", diff --git a/benefit-finder/src/App/__tests__/__snapshots__/index.spec.jsx.snap b/benefit-finder/src/App/__tests__/__snapshots__/index.spec.jsx.snap index a7f5bf281..fc95a33b2 100644 --- a/benefit-finder/src/App/__tests__/__snapshots__/index.spec.jsx.snap +++ b/benefit-finder/src/App/__tests__/__snapshots__/index.spec.jsx.snap @@ -166,7 +166,10 @@ exports[`loads intro 1`] = ` with each agency.

- We do not share, save, or submit your information. + + We do not share, save, or submit + + your information.

diff --git a/benefit-finder/src/App/_index.scss b/benefit-finder/src/App/_index.scss index 69251a7dc..1a3c14592 100644 --- a/benefit-finder/src/App/_index.scss +++ b/benefit-finder/src/App/_index.scss @@ -5,12 +5,3 @@ html { height: 100%; min-height: 100vh; } - -// try some usa-gov overides -.main-content { - padding-bottom: 0 !important; -} - -.usa-footer { - padding-top: 0 !important; -} diff --git a/benefit-finder/src/App/index.jsx b/benefit-finder/src/App/index.jsx index 475a9f0d7..6d0db9b2f 100644 --- a/benefit-finder/src/App/index.jsx +++ b/benefit-finder/src/App/index.jsx @@ -1,5 +1,5 @@ import { useState, createContext, useEffect, useMemo } from 'react' -import { BrowserRouter, Routes, Route } from 'react-router-dom' +import { BrowserRouter, Routes, Route } from 'react-router' import { version } from '../../package.json' import { useResetElement } from '@hooks' import * as apiCalls from '@api/apiCalls' @@ -65,7 +65,7 @@ function App({ testAppContent, testQuery }) { // set data state const [stepDataArray, setStepDataArray] = useState() - const [benfitsArray, setBenefitsArray] = useState() + const [benefitsArray, setBenefitsArray] = useState() useEffect(() => { content && setBenefitsArray([...content.benefits]) @@ -74,9 +74,9 @@ function App({ testAppContent, testQuery }) { }, [content]) // state - const [t] = useState(language === 'es' ? es : en) // tranlations + const [t] = useState(language === 'es' ? es : en) // translations - // update data basd on passed query paramaters + // update data based on passed query parameters useEffect(() => { if (hasQueryParams) { stepDataArray && @@ -84,7 +84,7 @@ function App({ testAppContent, testQuery }) { windowQuery, stepDataArray, setBenefitsArray, - benfitsArray, + benefitsArray, sharedToken ) } @@ -170,7 +170,7 @@ function App({ testAppContent, testQuery }) { relevantBenefits={ content?.lifeEventForm?.relevantBenefits } - data={benfitsArray} + data={benefitsArray} setBenefitsArray={() => setBenefitsArray()} ui={t.resultsView} notEligibleView={i !== 0} diff --git a/benefit-finder/src/Routes/Intro/__tests__/__snapshots__/index.spec.jsx.snap b/benefit-finder/src/Routes/Intro/__tests__/__snapshots__/index.spec.jsx.snap index 8eb8970d3..e308c488e 100644 --- a/benefit-finder/src/Routes/Intro/__tests__/__snapshots__/index.spec.jsx.snap +++ b/benefit-finder/src/Routes/Intro/__tests__/__snapshots__/index.spec.jsx.snap @@ -151,7 +151,10 @@ exports[`Intro > renders a match to the previous snapshot 1`] = ` with each agency.

- We do not share, save, or submit your information. + + We do not share, save, or submit + + your information.

diff --git a/benefit-finder/src/Routes/Intro/__tests__/index.spec.jsx b/benefit-finder/src/Routes/Intro/__tests__/index.spec.jsx index 4c57dba3e..8b55475ec 100644 --- a/benefit-finder/src/Routes/Intro/__tests__/index.spec.jsx +++ b/benefit-finder/src/Routes/Intro/__tests__/index.spec.jsx @@ -1,5 +1,5 @@ import { render } from '@testing-library/react' -import { BrowserRouter } from 'react-router-dom' +import { BrowserRouter } from 'react-router' import Intro from '../index.jsx' import * as en from '@locales/en/en.json' import content from '@api/mock-data/current.js' diff --git a/benefit-finder/src/Routes/Intro/_index.scss b/benefit-finder/src/Routes/Intro/_index.scss index 1db312294..228546993 100644 --- a/benefit-finder/src/Routes/Intro/_index.scss +++ b/benefit-finder/src/Routes/Intro/_index.scss @@ -3,7 +3,7 @@ @use '@styles/functions' as *; .bf-intro { - padding-bottom: rem(32px); + padding-bottom: rem(36px); > .bf-grid-container.grid-container { padding: 0 rem(36px); @@ -12,7 +12,7 @@ .bf-cta-wrapper { display: flex; justify-content: center; - margin: rem(20px) 0 rem(56px); + margin: rem(20px) 0 rem(24px); @media (width >= $desktop) { margin: rem(32px) rem(32px) rem(64px); @@ -32,7 +32,7 @@ margin-left: rem(16px); } - .bf-intro-process-notices-heading { + .bf-intro-process-notices-heading { width: 100; margin-bottom: rem(30px); } @@ -59,14 +59,14 @@ .bf-usa-process-list { margin-left: 0; margin-right: 0; - padding-top:rem(8px); + padding-top: rem(8px); .bf-usa-process-list__heading { font-size: rem(16px); - @media (width >= $desktop) { - font-size: rem(21px); - } + @media (width >= $desktop) { + font-size: rem(21px); + } } } } diff --git a/benefit-finder/src/Routes/Intro/index.jsx b/benefit-finder/src/Routes/Intro/index.jsx index f0e5d7340..7d9a901b4 100644 --- a/benefit-finder/src/Routes/Intro/index.jsx +++ b/benefit-finder/src/Routes/Intro/index.jsx @@ -1,5 +1,5 @@ import { useEffect, useContext } from 'react' -import { useNavigate, useLocation } from 'react-router-dom' +import { useNavigate, useLocation } from 'react-router' import { RouteContext } from '@/App' import { dataLayerUtils } from '@utils' import { useResetElement } from '@hooks' @@ -16,7 +16,7 @@ import { import './_index.scss' /** - * a compound component that renders the introductional start of the form process + * a compound component that renders the introduction start of the form process * @component * @param {object} content - inherited life event content * @param {object} ui - life event form ui translations @@ -36,7 +36,7 @@ const Intro = ({ content, ui, hasQueryParams }) => { resetElement.current.focus() } - // if we have query paramters direct user to the results page + // if we have query parameters direct user to the results page useEffect(() => { hasQueryParams && navigate( diff --git a/benefit-finder/src/Routes/LifeEventSection/__tests__/__snapshots__/index.spec.jsx.snap b/benefit-finder/src/Routes/LifeEventSection/__tests__/__snapshots__/index.spec.jsx.snap index fc2999746..4228392ff 100644 --- a/benefit-finder/src/Routes/LifeEventSection/__tests__/__snapshots__/index.spec.jsx.snap +++ b/benefit-finder/src/Routes/LifeEventSection/__tests__/__snapshots__/index.spec.jsx.snap @@ -307,7 +307,7 @@ exports[`LifeEventSection > renders a match to the previous snapshot 1`] = ` >
renders a match to the previous snapshot 1`] = ` aria-errormessage="error-description-applicant_relationship_to_the_deceased_0" aria-invalid="false" class="bf-usa-select usa-select " - data-errormessage="Test overide error label" + data-errormessage="Test override error label" data-testid="applicant_relationship_to_the_deceased_0" id="applicant_relationship_to_the_deceased_0" name="applicant_relationship_to_the_deceased_0" diff --git a/benefit-finder/src/Routes/LifeEventSection/__tests__/index.spec.jsx b/benefit-finder/src/Routes/LifeEventSection/__tests__/index.spec.jsx index 383cc9d38..6cfed6fc4 100644 --- a/benefit-finder/src/Routes/LifeEventSection/__tests__/index.spec.jsx +++ b/benefit-finder/src/Routes/LifeEventSection/__tests__/index.spec.jsx @@ -1,5 +1,5 @@ import { render, screen } from '@testing-library/react' -import { BrowserRouter } from 'react-router-dom' +import { BrowserRouter } from 'react-router' import { RouteContext } from '@/App' import { cleanString } from '@utils' import LifeEventSection from '../index.jsx' diff --git a/benefit-finder/src/Routes/LifeEventSection/_index.scss b/benefit-finder/src/Routes/LifeEventSection/_index.scss index 01407f339..af95097c2 100644 --- a/benefit-finder/src/Routes/LifeEventSection/_index.scss +++ b/benefit-finder/src/Routes/LifeEventSection/_index.scss @@ -11,7 +11,7 @@ .bf-grid-container.grid-container { max-width: $form-container-max-width; - padding: 0 rem(36px); + padding: 0 rem(20px); @media (width >= calc($form-container-max-width + rem(24px))) { padding-left: 0; diff --git a/benefit-finder/src/Routes/LifeEventSection/index.jsx b/benefit-finder/src/Routes/LifeEventSection/index.jsx index b3a14d14f..1272c0cac 100644 --- a/benefit-finder/src/Routes/LifeEventSection/index.jsx +++ b/benefit-finder/src/Routes/LifeEventSection/index.jsx @@ -1,6 +1,6 @@ import { useState, useEffect, useRef, useContext, Fragment } from 'react' import { RouteContext } from '@/App' -import { useNavigate, useLocation } from 'react-router-dom' +import { useNavigate, useLocation } from 'react-router' import PropTypes from 'prop-types' import { dateInputValidation, @@ -28,7 +28,7 @@ import './_index.scss' /** * a compound component that renders the main conditional view of the form * @component - * @param {object} data - inherieted life event step data + * @param {object} data - inherited life event step data * @param {func} handleData - inherited state manager * @param {object} ui - inherited ui translations * @return {html} returns a semantic html component that displays a form step @@ -46,7 +46,7 @@ const LifeEventSection = ({ data, handleData, ui }) => { const [submissionCount, setSubmissionCount] = useState(0) const { lifeEventSection } = dataLayerUtils.dataLayerStructure const { buttonGroup, reviewSelectionModal, requiredLabel, sectionHeadings } = - ui // desctructure data + ui // destructure data useHandleUnload(hasData) // alert the user if they try to go back in browser const resetElement = useResetElement() const ROUTES = useContext(RouteContext) @@ -125,7 +125,7 @@ const LifeEventSection = ({ data, handleData, ui }) => { * @function * @return {func} either success or alert handler */ - const handleCheckRequriedFields = () => { + const handleCheckRequiredFields = () => { // collect all the required fields in the current step return errorHandling .handleCheckForRequiredValues(requiredFieldsets, setHasError) @@ -146,7 +146,7 @@ const LifeEventSection = ({ data, handleData, ui }) => { * @return {null} only executes inherited functions */ const handleForwardUpdate = updateIndex => { - handleCheckRequriedFields().then(valid => { + handleCheckRequiredFields().then(valid => { if (valid === true) { // handle dataLayer const { errors } = dataLayerUtils.dataLayerStructure @@ -520,7 +520,7 @@ const LifeEventSection = ({ data, handleData, ui }) => { child => child.fieldsets.length && child.fieldsets.map((childItem, childItemIndex) => { - // if a parent determines the children inputs are hidden, we expect that the seleted values in child items are de-selected + // if a parent determines the children inputs are hidden, we expect that the selected values in child items are de-selected const selectedValue = childItem && apiCalls.GET.SelectedValue(childItem) @@ -587,7 +587,7 @@ const LifeEventSection = ({ data, handleData, ui }) => { ) } triggerLabel={buttonGroup[1].value} - handleCheckRequriedFields={handleCheckRequriedFields} + handleCheckRequiredFields={handleCheckRequiredFields} completed={currentData.completed} /> )} diff --git a/benefit-finder/src/Routes/ResultsView/__tests__/__snapshots__/index.spec.jsx.snap b/benefit-finder/src/Routes/ResultsView/__tests__/__snapshots__/index.spec.jsx.snap index 9f3f00aee..e69461b19 100644 --- a/benefit-finder/src/Routes/ResultsView/__tests__/__snapshots__/index.spec.jsx.snap +++ b/benefit-finder/src/Routes/ResultsView/__tests__/__snapshots__/index.spec.jsx.snap @@ -457,7 +457,7 @@ exports[`loads view 1`] = ` class="bf-usa-detail-summary" >

- Compensation to surviving spouses of coal minerstotally disabled by or whose deaths are attributable to pneumoconiosis. + Compensation to surviving spouses of coal miners totally disabled by or whose deaths are attributable to pneumoconiosis.

- Explore benefits you did not qualify for + See benefits you didn't qualify for

More benefits @@ -6303,7 +6303,12 @@ exports[`loads view 1`] = ` Share results

- Copy or email these results. Your answers will be visible. Only share with those you trust. + Copy or email these results. Your answers will be visible. + + Only share with those you trust + + . +

    Share link @@ -6322,7 +6328,8 @@ exports[`loads view 1`] = ` Email results @@ -6796,7 +6803,7 @@ exports[`scenario 1 loads in view with the correct amount of likely eligible ite class="bf-usa-detail-summary" >

    - Compensation to surviving spouses of coal minerstotally disabled by or whose deaths are attributable to pneumoconiosis. + Compensation to surviving spouses of coal miners totally disabled by or whose deaths are attributable to pneumoconiosis.

- Explore benefits you did not qualify for + See benefits you didn't qualify for

More benefits @@ -12642,7 +12649,12 @@ exports[`scenario 1 loads in view with the correct amount of likely eligible ite Share results

- Copy or email these results. Your answers will be visible. Only share with those you trust. + Copy or email these results. Your answers will be visible. + + Only share with those you trust + + . +