diff --git a/.eslintignore b/.eslintignore index 77c09bb8bc..0940554472 100644 --- a/.eslintignore +++ b/.eslintignore @@ -18,13 +18,11 @@ jest.config.js protractor.conf.js /src/app/api/* /mock-api -/cypress /.vscode /.storybook /.husky /.github setenv.ts setup-jest.ts -cypress.config.ts jest-global-mocks.ts environment.deploy.ts diff --git a/.github/workflows/deploy-prod.yml b/.github/workflows/deploy-prod.yml index d49e2f6e1a..9b40bdef3b 100644 --- a/.github/workflows/deploy-prod.yml +++ b/.github/workflows/deploy-prod.yml @@ -8,7 +8,7 @@ jobs: vtm-app: uses: ./.github/workflows/deploy.yml with: - branch: 'master' + branch: ${{ github.ref_name }} environment: 'prod' bucket: '.prod' secrets: diff --git a/.github/workflows/pr-checks.yml b/.github/workflows/pr-checks.yml index 41c88c0670..e516ff443f 100644 --- a/.github/workflows/pr-checks.yml +++ b/.github/workflows/pr-checks.yml @@ -12,18 +12,12 @@ on: jobs: build: runs-on: ubuntu-latest - - strategy: - matrix: - node-version: [18.18.0] - # See supported Node.js release schedule at https://nodejs.org/en/about/releases/ - steps: - - uses: actions/checkout@v3 - - name: Setup Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v3 + - uses: actions/checkout@v4 + - name: Setup Node.js + uses: actions/setup-node@v4 with: - node-version: ${{ matrix.node-version }} + node-version-file: '.nvmrc' cache: 'npm' - name: Install dependencies run: npm ci --include=optional @@ -31,19 +25,14 @@ jobs: run: npm run build --ignore-scripts -- --configuration production test: - runs-on: ubuntu-latest - - strategy: - matrix: - node-version: [18.18.0] - # See supported Node.js release schedule at https://nodejs.org/en/about/releases/ - + runs-on: + - ARM64 steps: - - uses: actions/checkout@v3 - - name: Setup Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v3 + - uses: actions/checkout@v4 + - name: Setup Node.js + uses: actions/setup-node@v4 with: - node-version: ${{ matrix.node-version }} + node-version-file: '.nvmrc' cache: 'npm' - name: Install dependencies run: npm ci --include=optional @@ -52,18 +41,12 @@ jobs: lint: runs-on: ubuntu-latest - - strategy: - matrix: - node-version: [18.18.0] - # See supported Node.js release schedule at https://nodejs.org/en/about/releases/ - steps: - - uses: actions/checkout@v3 - - name: Setup Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v3 + - uses: actions/checkout@v4 + - name: Setup Node.js + uses: actions/setup-node@v4 with: - node-version: ${{ matrix.node-version }} + node-version-file: '.nvmrc' cache: 'npm' - name: Install dependencies run: npm ci --include=optional @@ -72,45 +55,12 @@ jobs: audit: runs-on: ubuntu-latest - - strategy: - matrix: - node-version: [18.18.0] steps: - - uses: actions/checkout@v3 - - name: Setup Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v3 + - uses: actions/checkout@v4 + - name: Setup Node.js + uses: actions/setup-node@v4 with: - node-version: ${{ matrix.node-version }} + node-version-file: '.nvmrc' cache: 'npm' - name: Run audit run: npm audit --audit-level=critical - - scanner: - permissions: - id-token: write - runs-on: X64 - steps: - - uses: actions/checkout@v4 - - uses: actions/setup-node@v4 - with: - node-version-file: '.nvmrc' - - uses: aws-actions/configure-aws-credentials@v4 - with: - role-to-assume: ${{ secrets.AWS_MGMT_ROLE }} - aws-region: ${{ secrets.DVSA_AWS_REGION }} - role-session-name: 'cvs-app-vtm' - - uses: aws-actions/aws-secretsmanager-get-secrets@v1 - with: - secret-ids: sonarqube-gha - parse-json-secrets: true - - name: Install dependencies - run: npm ci --include=optional - - name: Run SonarQube scanner - run: | - npm run sonar-scanner -- \ - -Dsonar.host.url=${{ env.SONARQUBE_GHA_URL }} \ - -Dsonar.token=${{ env.SONARQUBE_GHA_TOKEN }} \ - -Dsonar.login=${{ env.SONARQUBE_GHA_TOKEN }} \ - -Dsonar.projectName=${{ github.repository }} \ - -Dsonar.projectVersion=1.0.${{ github.run_id }} \ No newline at end of file diff --git a/.github/workflows/sonar-scan.yml b/.github/workflows/sonar-scan.yml new file mode 100644 index 0000000000..4d256aa0b9 --- /dev/null +++ b/.github/workflows/sonar-scan.yml @@ -0,0 +1,40 @@ +name: Sonar scan + +on: + push: + branches: [ 'develop' ] + +jobs: + scanner: + permissions: + id-token: write + runs-on: X64 + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-node@v4 + with: + node-version-file: '.nvmrc' + + - uses: aws-actions/configure-aws-credentials@v4 + with: + role-to-assume: ${{ secrets.AWS_MGMT_ROLE }} + aws-region: ${{ secrets.DVSA_AWS_REGION }} + role-session-name: 'cvs-app-vtm' + + - uses: aws-actions/aws-secretsmanager-get-secrets@v2 + with: + secret-ids: sonarqube-gha + parse-json-secrets: true + + - name: Install dependencies + run: npm ci --include=optional + + - name: Run SonarQube scanner + run: | + npm run sonar-scanner -- \ + -Dsonar.host.url=${{ env.SONARQUBE_GHA_URL }} \ + -Dsonar.token=${{ env.SONARQUBE_GHA_TOKEN }} \ + -Dsonar.login=${{ env.SONARQUBE_GHA_TOKEN }} \ + -Dsonar.projectName=${{ github.repository }} \ + -Dsonar.projectVersion=1.0.${{ github.run_id }} diff --git a/.gitignore b/.gitignore index f0f66a5d5e..a11a0b13c5 100644 --- a/.gitignore +++ b/.gitignore @@ -45,21 +45,12 @@ yarn-error.log testem.log /typings .reports/ -{} .scannerwork # System files .DS_Store Thumbs.db -# Cypress -cypress.env.json -cypress/videos -cypress/screenshots -runner-results -multi-reporter-config.json -cypress/parallel-weights.json - report.json # Sentry Config File .sentryclirc diff --git a/.nvmrc b/.nvmrc index 02c8b485ed..8ce7030825 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -18.18.0 +20.16.0 diff --git a/.snyk b/.snyk new file mode 100644 index 0000000000..233f15f89f --- /dev/null +++ b/.snyk @@ -0,0 +1,23 @@ +# Snyk (https://snyk.io) policy file, patches or ignores known vulnerabilities. +version: v1.25.0 + +# TEMPLATE that can be used inside the `ignore section` +# SNYK-LANG-PKG-NUM: +# - '*': +# reason: Remediation not yet available +# expires: 2023-08-27T09:01:15.119Z +# created: 2022-08-08T09:01:15.122Z + +# ignores vulnerabilities until expiry date; change duration by modifying expiry date +ignore: + SNYK-JS-BRACES-6838727: + - '*': + reason: Remediation not yet available + expires: 2024-11-24T09:01:15.119Z + created: 2024-05-24T09:01:15.122Z + SNYK-JS-MICROMATCH-6838728: + - '*': + reason: Remediation not yet available + expires: 2024-11-24T09:01:15.119Z + created: 2024-05-24T09:01:15.122Z +patch: {} diff --git a/angular.json b/angular.json index c645771e63..cc3ac32aec 100644 --- a/angular.json +++ b/angular.json @@ -24,12 +24,15 @@ "prefix": "app", "architect": { "build": { - "builder": "@angular-devkit/build-angular:browser", + "builder": "@angular/build:application", "options": { - "outputPath": "dist/cvs-app-vtm", + "outputPath": { + "base": "dist/cvs-app-vtm" + }, "index": "src/index.html", - "main": "src/main.ts", - "polyfills": "src/polyfills.ts", + "polyfills": [ + "src/polyfills.ts" + ], "tsConfig": "tsconfig.app.json", "inlineStyleLanguage": "scss", "sourceMap": true, @@ -46,7 +49,15 @@ "src/global-styles.scss" ], "scripts": [], - "allowedCommonJsDependencies": ["validate-govuk-date"] + "allowedCommonJsDependencies": [ + "validate-govuk-date" + ], + "browser": "src/main.ts", + "stylePreprocessorOptions": { + "includePaths": [ + "." + ] + } }, "configurations": { "production": { @@ -60,10 +71,7 @@ "outputHashing": "all", "sourceMap": false, "namedChunks": false, - "aot": true, - "extractLicenses": true, - "vendorChunk": false, - "buildOptimizer": true + "extractLicenses": true }, "deploy": { "fileReplacements": [ @@ -76,30 +84,27 @@ "outputHashing": "all", "sourceMap": true, "namedChunks": false, - "aot": true, - "extractLicenses": true, - "vendorChunk": true, - "buildOptimizer": true + "extractLicenses": true } }, "defaultConfiguration": "deploy" }, "serve": { - "builder": "@angular-devkit/build-angular:dev-server", + "builder": "@angular/build:dev-server", "configurations": { "production": { - "browserTarget": "cvs-app-vtm:build:production" + "buildTarget": "cvs-app-vtm:build:production" }, "deploy": { - "browserTarget": "cvs-app-vtm:build:deploy" + "buildTarget": "cvs-app-vtm:build:deploy" } }, "defaultConfiguration": "deploy" }, "extract-i18n": { - "builder": "@angular-devkit/build-angular:extract-i18n", + "builder": "@angular/build:extract-i18n", "options": { - "browserTarget": "cvs-app-vtm:build" + "buildTarget": "cvs-app-vtm:build" } }, "lint": { @@ -114,9 +119,7 @@ } } }, - "defaultProject": "cvs-app-vtm", "cli": { - "analytics": false, - "defaultCollection": "@angular-eslint/schematics" + "analytics": false } } diff --git a/biome.json b/biome.json new file mode 100644 index 0000000000..0298ee8771 --- /dev/null +++ b/biome.json @@ -0,0 +1,47 @@ +{ + "extends": ["@dvsa/biome-config/biome"], + "files": { + "ignore": ["src/app/api/**"] + }, + "overrides": [ + { + "include": ["**/*.mock.ts", "**/*.spec.ts", "**/*.ts"], + "linter": { + "rules": { + "complexity": { + "noBannedTypes": "off", + "noForEach": "off", + "noThisInStatic": "off", + "noUselessConstructor": "off", + "useLiteralKeys": "off", + "useOptionalChain": "off", + "useRegexLiterals": "off" + }, + "correctness": { + "noInvalidUseBeforeDeclaration": "off", + "noVoidTypeReturn": "off", + "noSwitchDeclarations": "off" + }, + "performance": { + "noAccumulatingSpread": "off", + "noDelete": "off" + }, + "suspicious": { + "noAssignInExpressions": "off", + "noDoubleEquals": "off", + "noDuplicateTestHooks": "off", + "noExplicitAny": "off", + "noImplicitAnyLet": "off", + "noPrototypeBuiltins": "off", + "noShadowRestrictedNames": "off", + "useValidTypeof": "off" + }, + "style": { + "noNonNullAssertion": "off", + "noParameterAssign": "off" + } + } + } + } + ] +} diff --git a/cypress.config.ts b/cypress.config.ts deleted file mode 100644 index ea575f0997..0000000000 --- a/cypress.config.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { defineConfig } from 'cypress'; - -export default defineConfig({ - e2e: { - setupNodeEvents(on, config) { - // implement node event listeners here - }, - baseUrl: 'http://localhost:4200/', - experimentalModifyObstructiveThirdPartyCode: true, - defaultCommandTimeout: 30000, - videoCompression: false, - videoUploadOnPasses: false - } -}); diff --git a/cypress/e2e/techRecord/HGV/hgv.cy.ts b/cypress/e2e/techRecord/HGV/hgv.cy.ts deleted file mode 100644 index 4e6222dfa5..0000000000 --- a/cypress/e2e/techRecord/HGV/hgv.cy.ts +++ /dev/null @@ -1,66 +0,0 @@ -import { randomString } from '../../../support/functions'; - -describe('HGV technical record', () => { - beforeEach(() => { - cy.loginToAAD(); - cy.visit(''); - cy.get('#search-for-technical-record-link', { timeout: 10000 }).should('be.visible'); - }); - - it('should amend the vehicle', () => { - const vin = randomString(10); - cy.createVehicle('hgv-testable', vin, 'current', randomString(7)); - cy.get('#search-for-technical-record-link').click(); - cy.get('#search-term').type(vin + '{enter}'); - cy.get('a').contains('Select technical record').click(); - cy.get('.govuk-accordion__show-all-text').click(); - cy.get('#test-approvalType').should('have.text', ' - '); - cy.get('#test-approvalType').should('not.have.text', ' NTA '); - cy.get('app-button > #edit').click(); - cy.get('#reason-correcting-an-error-radio').click(); - cy.get('#submit').click(); - cy.get('.govuk-accordion__show-all-text').click(); - cy.get('#reasonForCreation').type('amending approval type'); - cy.get('#approvalType').select('1: NTA'); - cy.get('#dtpNumber').type('00'); - cy.wait(400); - cy.get('.govuk-button-group > :nth-child(1) > #submit').click(); - cy.intercept('GET', '/develop/vehicles/**').as('get-vehicle'); - cy.wait('@get-vehicle'); - cy.get('.govuk-accordion__show-all-text').click(); - cy.get('#test-approvalType').should('have.text', ' NTA '); - }); - - describe('Create HGV technical record', () => { - it('should display the create vehicle page with details filled in', () => { - const vin = randomString(10); - const vrm = randomString(7); - cy.get('#create-new-technical-record-link').click(); - cy.get('#input-vin').type(vin); - cy.get('#input-vrm-or-trailer-id').type(vrm); - cy.get('#change-vehicle-type-select').select('1: hgv'); - cy.get('#create-record-continue').click(); - cy.get('#vehicleType').should('have.text', ' HGV '); - cy.get('#vin').should('have.text', ` ${vin.toUpperCase()} `); - cy.get('#current-vrm-span').should(vrmDisplay => { - const vrmSplit = vrm.slice(0, vrm.length - 3) + ' ' + vrm.slice(vrm.length - 3); - expect(vrmDisplay).to.contain(vrmSplit.toUpperCase()); - }); - }); - - it('should navigate back on browser refresh', () => { - const vin = randomString(10); - const vrm = randomString(7); - cy.get('#create-new-technical-record-link').click(); - cy.get('#input-vin').type(vin); - cy.get('#input-vrm-or-trailer-id').type(vrm); - cy.get('#change-vehicle-type-select').select('1: hgv'); - cy.get('#create-record-continue').click(); - cy.get('#vehicleType').should('have.text', ' HGV '); - cy.get('#vin').should('have.text', ` ${vin.toUpperCase()} `); - cy.reload(); - cy.location('pathname').should('include', 'create'); - cy.location('pathname').should('not.contain', 'new-record-details'); - }); - }); -}); diff --git a/cypress/e2e/techRecord/LGV/lgv.cy.ts b/cypress/e2e/techRecord/LGV/lgv.cy.ts deleted file mode 100644 index 1ca57b7066..0000000000 --- a/cypress/e2e/techRecord/LGV/lgv.cy.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { randomString } from '../../../support/functions'; - -describe('LGV technical record', () => { - beforeEach(() => { - cy.loginToAAD(); - cy.visit(''); - cy.get('#search-for-technical-record-link', { timeout: 10000 }).should('be.visible'); - }); - it('should create a new LGV', () => { - const vin = randomString(10); - cy.intercept('POST', '/develop/vehicles').as('create-vehicle'); - cy.get('#create-new-technical-record-link').click(); - cy.get('#input-vin').type(vin); - cy.get('#generate-c-t-z-num-true-checkbox').click(); - cy.get('#change-vehicle-type-select').select('2: lgv'); - cy.get(':nth-child(6) > #create-record-continue').click(); - cy.get('.govuk-accordion__show-all-text').click(); - cy.get('#reasonForCreation').type('testing create'); - cy.get('#vehicleSubclass-l-checkbox').click(); - cy.wait(400); - cy.get('app-button > #submit-record-continue').click(); - cy.get('@create-vehicle').its('response', { timeout: 12000 }).should('have.property', 'statusCode', 201); - }); -}); diff --git a/cypress/e2e/techRecord/PSV/psv.cy.ts b/cypress/e2e/techRecord/PSV/psv.cy.ts deleted file mode 100644 index 2513beae36..0000000000 --- a/cypress/e2e/techRecord/PSV/psv.cy.ts +++ /dev/null @@ -1,103 +0,0 @@ -import { randomString } from '../../../support/functions'; - -describe('PSV technical record', () => { - beforeEach(() => { - cy.loginToAAD(); - cy.visit(''); - cy.get('#search-for-technical-record-link', { timeout: 10000 }).should('be.visible'); - }); - - it('should create a new PSV', () => { - const vin = randomString(10); - cy.intercept('POST', '/develop/vehicles').as('create-vehicle'); - cy.get('#create-new-technical-record-link').click(); - cy.get('#input-vin').type(vin); - cy.get('#generate-c-t-z-num-true-checkbox').click(); - cy.get('#change-vehicle-type-select').select('3: psv'); - cy.get(':nth-child(6) > #create-record-continue').click(); - cy.get('.govuk-accordion__show-all-text').click(); - cy.get('#reasonForCreation').type('testing create'); - cy.get('#vehicleClassDescription').select('2: not applicable'); - cy.get('#dtpNumber').type('00'); - cy.get('#dtpNumber__option--1').click(); - cy.wait(400); - cy.get('app-button > #submit-record-continue').click(); - cy.get('@create-vehicle').its('response', { timeout: 12000 }).should('have.property', 'statusCode', 201); - }); - - it('should amend the vehicle type', () => { - const vin = randomString(10); - cy.createVehicle('psv', vin, 'provisional', randomString(7)); - cy.get('#search-for-technical-record-link').click(); - cy.get('#search-term').type(vin + '{enter}'); - cy.get('a').contains('Select technical record').click(); - cy.get('#vehicleType').should('have.text', ' PSV '); - cy.get('app-button > #change-type-link').click(); - cy.get('#change-vehicle-type-select').select('1: hgv'); - cy.get('button').contains('Confirm and continue').click(); - cy.get('.govuk-accordion__show-all-text').click(); - cy.get('#reasonForCreation').type('amending vehicle type'); - cy.get('.govuk-button-group > :nth-child(1) > #submit').click(); - cy.intercept('GET', '/develop/vehicles/**').as('get-vehicle'); - cy.wait('@get-vehicle'); - cy.get('#vehicleType').should('have.text', ' HGV '); - }); - - it('should amend the vrm', () => { - const newVrm = randomString(7); - const vin = randomString(10); - cy.createVehicle('psv', vin, 'current', randomString(7)); - cy.get('#search-for-technical-record-link').click(); - cy.get('#search-term').type(vin + '{enter}'); - cy.get('a').contains('Select technical record').click(); - cy.get('#current-vrm-span').should(vrm => { - const vrmSplit = newVrm.slice(0, newVrm.length - 3) + ' ' + newVrm.slice(newVrm.length - 3); - expect(vrm).not.to.contain(vrmSplit.toUpperCase()); - }); - cy.get('app-button > #change-vrm-link').click(); - cy.get('.govuk-input').type(newVrm); - cy.get('#-true-radio').click(); - cy.get('#submit-vrm').click(); - cy.get('#current-vrm-span').should(vrm => { - const vrmSplit = newVrm.slice(0, newVrm.length - 3) + ' ' + newVrm.slice(newVrm.length - 3); - expect(vrm).to.contain(vrmSplit.toUpperCase()); - }); - }); - - it('should promote the record', () => { - const vin = randomString(10); - cy.createVehicle('psv', vin, 'provisional', randomString(7)); - cy.get('#search-for-technical-record-link').click(); - cy.get('#search-term').type(vin + '{enter}'); - cy.get('a').contains('Select technical record').click(); - cy.get(':nth-child(2) > #promote-link').click(); - cy.get('#reason').type('testing promote'); - cy.get('#submit-change-status').click(); - cy.get('#status-code').should('have.text', 'Current'); - }); - - it('should amend the vehicle', () => { - const vin = randomString(10); - cy.createVehicle('psv', vin, 'current', randomString(7)); - cy.get('#search-for-technical-record-link').click(); - cy.get('#search-term').type(vin + '{enter}'); - cy.get('a').contains('Select technical record').click(); - cy.get('.govuk-accordion__show-all-text').click(); - cy.get('#test-approvalType').should('have.text', ' - '); - cy.get('#test-approvalType').should('not.have.text', ' NTA '); - cy.get('app-button > #edit').click(); - cy.get('#reason-correcting-an-error-radio').click(); - cy.get('#submit').click(); - cy.get('.govuk-accordion__show-all-text').click(); - cy.get('#reasonForCreation').type('amending approval type'); - cy.get('#approvalType').select('1: NTA'); - cy.get('#dtpNumber').type('00'); - cy.get('#dtpNumber__option--1').click(); - cy.wait(400); - cy.get('.govuk-button-group > :nth-child(1) > #submit').click(); - cy.intercept('GET', '/develop/vehicles/**').as('get-vehicle'); - cy.wait('@get-vehicle'); - cy.get('.govuk-accordion__show-all-text').click(); - cy.get('#test-approvalType').should('have.text', ' NTA '); - }); -}); diff --git a/cypress/e2e/techRecord/TRL/trl.cy.ts b/cypress/e2e/techRecord/TRL/trl.cy.ts deleted file mode 100644 index bbc3893680..0000000000 --- a/cypress/e2e/techRecord/TRL/trl.cy.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { randomString } from '../../../support/functions'; - -describe('TRL technical record', () => { - beforeEach(() => { - cy.loginToAAD(); - cy.visit(''); - cy.get('#search-for-technical-record-link', { timeout: 10000 }).should('be.visible'); - }); - it('should create a new TRL', () => { - const vin = randomString(10); - cy.intercept('POST', '/develop/vehicles').as('create-vehicle'); - cy.get('#create-new-technical-record-link').click(); - cy.get('#input-vin').type(vin); - cy.get('#generate-c-t-z-num-true-checkbox').click(); - cy.get('#change-vehicle-type-select').select('4: trl'); - cy.get(':nth-child(6) > #create-record-continue').click(); - cy.get('.govuk-accordion__show-all-text').click(); - cy.get('#reasonForCreation').type('testing create'); - cy.get('#vehicleClassDescription').select('5: trailer'); - cy.get('#bodyType').select('6: flat'); - cy.wait(400); - cy.get('app-button > #submit-record-continue').click(); - cy.get('@create-vehicle').its('response', { timeout: 12000 }).should('have.property', 'statusCode', 201); - }); -}); diff --git a/cypress/e2e/testResults/HGV/tests.cy.ts b/cypress/e2e/testResults/HGV/tests.cy.ts deleted file mode 100644 index 1526308460..0000000000 --- a/cypress/e2e/testResults/HGV/tests.cy.ts +++ /dev/null @@ -1,81 +0,0 @@ -import { randomString } from '../../../support/functions'; - -describe('HGV tests', () => { - beforeEach(() => { - cy.loginToAAD(); - cy.visit(''); - cy.get('#search-for-technical-record-link', { timeout: 10000 }).should('be.visible'); - }); - - it('should create an annual test', () => { - const vin = randomString(10); - cy.createVehicle('hgv-testable', vin, 'current', randomString(7)); - cy.get('#search-for-technical-record-link').click(); - cy.get('#search-term').type(vin + '{enter}'); - cy.get('a').contains('Select technical record').click(); - cy.get('#create-test').click(); - cy.get('#test-list-item-94').click(); - cy.get('#countryOfRegistration').type('g'); - cy.get('#countryOfRegistration__option--1').click(); - cy.get('#odometerReading').type('123'); - cy.get('#odometerReadingUnits-miles-radio').click(); - cy.get('#contingencyTestNumber').type('123456'); - cy.get('#testTypeStartTimestamp-day').type('14'); - cy.get('#testTypeStartTimestamp-month').type('02'); - cy.get('#testTypeStartTimestamp-year').type(new Date().getFullYear().toString()); - cy.get('#testTypeStartTimestamp-hour').type('14'); - cy.get('#testTypeStartTimestamp-minute').type('14'); - cy.get('#testTypeEndTimestamp-day').type('14'); - cy.get('#testTypeEndTimestamp-month').type('02'); - cy.get('#testTypeEndTimestamp-year').type(new Date().getFullYear().toString()); - cy.get('#testTypeEndTimestamp-hour').type('14'); - cy.get('#testTypeEndTimestamp-minute').type('15'); - cy.get('#testStationPNumber').type('a'); - cy.get('#testStationPNumber__option--1').click(); - cy.get('#testerStaffId').type('a'); - cy.get('#testerStaffId__option--1').click(); - cy.get('#reasonForCreation').type('testing create annual hgv test'); - cy.wait(400); - cy.get('#review-test-result').click(); - cy.get('#submit-test-result').click(); - cy.get('#test-record-summary-name-0').should('have.text', 'Annual test'); - }); - - it('should create an abandoned annual test', () => { - const vin = randomString(10); - cy.createVehicle('hgv-testable', vin, 'current', randomString(7)); - cy.get('#search-for-technical-record-link').click(); - cy.get('#search-term').type(vin + '{enter}'); - cy.get('a').contains('Select technical record').click(); - cy.get('#create-test').click(); - cy.get('#test-list-item-94').click(); - cy.get('#countryOfRegistration').type('g'); - cy.get('#countryOfRegistration__option--1').click(); - cy.get('#odometerReading').type('123'); - cy.get('#odometerReadingUnits-miles-radio').click(); - cy.get('#contingencyTestNumber').type('123456'); - cy.get('#testTypeStartTimestamp-day').type('14'); - cy.get('#testTypeStartTimestamp-month').type('02'); - cy.get('#testTypeStartTimestamp-year').type(new Date().getFullYear().toString()); - cy.get('#testTypeStartTimestamp-hour').type('14'); - cy.get('#testTypeStartTimestamp-minute').type('14'); - cy.get('#testTypeEndTimestamp-day').type('14'); - cy.get('#testTypeEndTimestamp-month').type('02'); - cy.get('#testTypeEndTimestamp-year').type(new Date().getFullYear().toString()); - cy.get('#testTypeEndTimestamp-hour').type('14'); - cy.get('#testTypeEndTimestamp-minute').type('15'); - cy.get('#testStationPNumber').type('a'); - cy.get('#testStationPNumber__option--1').click(); - cy.get('#testerStaffId').type('a'); - cy.get('#testerStaffId__option--1').click(); - cy.get('#reasonForCreation').type('testing create an abandoned annual hgv test'); - cy.wait(400); - cy.get('[type="button"] > #abandon-test-result').click(); - cy.get('input[type="checkbox"]').first().click(); - cy.get('#additionalCommentsForAbandon').type('Cypress testing abandoning'); - cy.wait(400); - cy.get('.govuk-button-group > :nth-child(1) > #confirm').click(); - cy.get('#test-record-summary-name-0').should('have.text', 'Annual test'); - cy.get('#test-record-summary-result-0').should('have.text', 'abandoned'); - }); -}); diff --git a/cypress/e2e/testResults/LGV/DBA.cy.ts b/cypress/e2e/testResults/LGV/DBA.cy.ts deleted file mode 100644 index abe0106ac1..0000000000 --- a/cypress/e2e/testResults/LGV/DBA.cy.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { randomString } from '../../../support/functions'; - -describe('LGV DBAs', () => { - beforeEach(() => { - cy.loginToAAD(); - cy.visit(''); - cy.get('#search-for-technical-record-link', { timeout: 10000 }).should('be.visible'); - }); - - it('should create a DBA', () => { - const vin = randomString(10); - cy.createVehicle('lgv', vin, 'current', randomString(7)); - cy.get('#search-for-technical-record-link').click(); - cy.get('#search-term').type(vin + '{enter}'); - cy.get('a').contains('Select technical record').click(); - cy.get('#create-test', { timeout: 12000 }).click(); - cy.get('#test-list-item-400', { timeout: 20000 }).click(); - cy.get('#browse-list-item-413').click(); - cy.get('#browse-list-item-433').click(); - cy.get('#reasonForCreation', { timeout: 15000 }).type('testing create DBA'); - cy.get('#certificateNumber').type('123456'); - cy.wait(400); - cy.get('#review-test-result').click(); - cy.get('#submit-test-result').click(); - cy.get('#test-record-summary-name-0', { timeout: 10000 }).should('have.text', 'IVA17 Appeal for IVA'); - }); - - it('should amend the test result', () => { - const vin = randomString(10); - cy.createVehicleAndTest('lgv', vin, randomString(7), '433'); - cy.get('#search-for-technical-record-link').click(); - cy.get('#search-term').type(vin + '{enter}'); - cy.get('a').contains('Select technical record').click(); - cy.get('#view-test-1').click(); - cy.get('.govuk-tag--green', { timeout: 10000 }).should('have.text', 'Pass'); - cy.get('#amend-test').click(); - cy.get('#submit').click(); - cy.get('.govuk-accordion__show-all-text').click(); - cy.get('#reasonForCreation', { timeout: 15000 }).type('testing amend DBA'); - cy.get('#testResult-fail-radio').click(); - cy.wait(400); - cy.get('#review-test-result').click(); - cy.get('#save-test-result').click(); - cy.get('.govuk-tag--red', { timeout: 10000 }).should('have.text', 'Fail'); - }); -}); diff --git a/cypress/e2e/testResults/TRL/tests.cy.ts b/cypress/e2e/testResults/TRL/tests.cy.ts deleted file mode 100644 index 09234d4da4..0000000000 --- a/cypress/e2e/testResults/TRL/tests.cy.ts +++ /dev/null @@ -1,79 +0,0 @@ -import { randomString } from '../../../support/functions'; - -describe('TRL tests', () => { - beforeEach(() => { - cy.loginToAAD(); - cy.visit(''); - cy.get('#search-for-technical-record-link', { timeout: 10000 }).should('be.visible'); - }); - - it('should create an annual test', () => { - const vin = randomString(10); - cy.createVehicle('trl-testable', vin, 'current', randomString(7)); - cy.get('#search-for-technical-record-link').click(); - cy.get('#search-term').type(vin + '{enter}'); - cy.get('a').contains('Select technical record').click(); - cy.get('#create-test').click(); - cy.get('#test-list-item-94').click(); - cy.get('#countryOfRegistration').type('g'); - cy.get('#countryOfRegistration__option--1').click(); - cy.get('#euVehicleCategory').select('9: o3'); - cy.get('#contingencyTestNumber').type('123456'); - cy.get('#testTypeStartTimestamp-day').type('14'); - cy.get('#testTypeStartTimestamp-month').type('02'); - cy.get('#testTypeStartTimestamp-year').type(new Date().getFullYear().toString()); - cy.get('#testTypeStartTimestamp-hour').type('14'); - cy.get('#testTypeStartTimestamp-minute').type('14'); - cy.get('#testTypeEndTimestamp-day').type('14'); - cy.get('#testTypeEndTimestamp-month').type('02'); - cy.get('#testTypeEndTimestamp-year').type(new Date().getFullYear().toString()); - cy.get('#testTypeEndTimestamp-hour').type('14'); - cy.get('#testTypeEndTimestamp-minute').type('15'); - cy.get('#testStationPNumber').type('a'); - cy.get('#testStationPNumber__option--1').click(); - cy.get('#testerStaffId').type('a'); - cy.get('#testerStaffId__option--1').click(); - cy.get('#reasonForCreation').type('testing create annual trl test'); - cy.wait(400); - cy.get('#review-test-result').click(); - cy.get('#submit-test-result').click(); - cy.get('#test-record-summary-name-0').should('have.text', 'Annual test'); - }); - - it('should create an abandoned annual test', () => { - const vin = randomString(10); - cy.createVehicle('trl-testable', vin, 'current', randomString(7)); - cy.get('#search-for-technical-record-link').click(); - cy.get('#search-term').type(vin + '{enter}'); - cy.get('a').contains('Select technical record').click(); - cy.get('#create-test').click(); - cy.get('#test-list-item-94').click(); - cy.get('#countryOfRegistration').type('g'); - cy.get('#countryOfRegistration__option--1').click(); - cy.get('#euVehicleCategory').select('9: o3'); - cy.get('#contingencyTestNumber').type('123456'); - cy.get('#testTypeStartTimestamp-day').type('14'); - cy.get('#testTypeStartTimestamp-month').type('02'); - cy.get('#testTypeStartTimestamp-year').type(new Date().getFullYear().toString()); - cy.get('#testTypeStartTimestamp-hour').type('14'); - cy.get('#testTypeStartTimestamp-minute').type('14'); - cy.get('#testTypeEndTimestamp-day').type('14'); - cy.get('#testTypeEndTimestamp-month').type('02'); - cy.get('#testTypeEndTimestamp-year').type(new Date().getFullYear().toString()); - cy.get('#testTypeEndTimestamp-hour').type('14'); - cy.get('#testTypeEndTimestamp-minute').type('15'); - cy.get('#testStationPNumber').type('a'); - cy.get('#testStationPNumber__option--1').click(); - cy.get('#testerStaffId').type('a'); - cy.get('#testerStaffId__option--1').click(); - cy.get('#reasonForCreation').type('testing create an abandoned annual trl test'); - cy.wait(400); - cy.get('[type="button"] > #abandon-test-result').click(); - cy.get('input[type="checkbox"]').first().click(); - cy.get('#additionalCommentsForAbandon').type('Cypress testing abandoning'); - cy.wait(400); - cy.get('.govuk-button-group > :nth-child(1) > #confirm').click(); - cy.get('#test-record-summary-name-0').should('have.text', 'Annual test'); - cy.get('#test-record-summary-result-0').should('have.text', 'abandoned'); - }); -}); diff --git a/cypress/fixtures/433.json b/cypress/fixtures/433.json deleted file mode 100644 index 816fed0fec..0000000000 --- a/cypress/fixtures/433.json +++ /dev/null @@ -1,43 +0,0 @@ -{ - "vehicleType": "lgv", - "typeOfTest": "desk-based", - "source": "vtm", - "testStatus": "submitted", - "vehicleClass": null, - "vehicleSubclass": ["c"], - "noOfAxles": 2, - "numberOfWheelsDriven": null, - "firstUseDate": null, - "createdByName": "CVS Cypress automation", - "lastUpdatedByName": "Tester", - "vehicleConfiguration": "other", - "reasonForCancellation": "", - "testTypes": [ - { - "testTypeId": "433", - "testTypeName": "IVA17 Appeal for IVA", - "prohibitionIssued": null, - "name": "IVA17 Appeal for IVA", - "reasonForAbandoning": null, - "additionalCommentsForAbandon": null, - "secondaryCertificateNumber": null, - "testResult": "pass", - "certificateNumber": "123456", - "additionalNotesRecorded": "", - "customDefects": [] - } - ], - "vrm": "PG4F03V", - "countryOfRegistration": "bg", - "euVehicleCategory": null, - "odometerReading": null, - "odometerReadingUnits": null, - "preparerName": "", - "preparerId": "", - "testStationName": "Swansea Test HQ", - "testStationPNumber": "123456", - "testStationType": "hq", - "testerName": "Tester", - "testerEmailAddress": "Tester", - "testerStaffId": "CVS cypress automation" -} diff --git a/cypress/fixtures/hgv-testable.json b/cypress/fixtures/hgv-testable.json deleted file mode 100644 index 638ea082e0..0000000000 --- a/cypress/fixtures/hgv-testable.json +++ /dev/null @@ -1,85 +0,0 @@ -{ - "bodyType": { "description": "other", "code": "x" }, - "grossKerbWeight": 2500, - "brakeCode": "178202", - "drawbarCouplingFitted": true, - "notes": "test note", - "functionCode": "A", - "brakes": { - "brakeCodeOriginal": "12412", - "brakeCode": "123", - "dtpNumber": "1234", - "loadSensingValve": true, - "brakeForceWheelsNotLocked": { "parkingBrakeForceA": 2332, "serviceBrakeForceA": 6424, "secondaryBrakeForceA": 2512 }, - "retarderBrakeOne": "electric", - "dataTrBrakeTwo": "None", - "antilockBrakingSystem": true, - "retarderBrakeTwo": "exhaust", - "dataTrBrakeOne": "None", - "brakeForceWheelsUpToHalfLocked": { "secondaryBrakeForceB": 2512, "parkingBrakeForceB": 3512, "serviceBrakeForceB": 5521 }, - "dataTrBrakeThree": "None" - }, - "conversionRefNo": "7891234", - "frontVehicleTo5thWheelCouplingMax": 1900, - "grossLadenWeight": 3000, - "axles": [ - { - "axleNumber": 1, - "tyres": { - "speedCategorySymbol": "a7", - "tyreSize": "295/80-22.5", - "fitmentCode": "single", - "dataTrAxles": 345, - "plyRating": "AB", - "tyreCode": 1234 - }, - "parkingBrakeMrk": true, - "weights": { "designWeight": 1800, "gbWeight": 1400 } - }, - { - "axleNumber": 2, - "tyres": { - "speedCategorySymbol": "a7", - "tyreSize": "295/80-22.5", - "fitmentCode": "double", - "dataTrAxles": 345, - "plyRating": "AB", - "tyreCode": 5678 - }, - "parkingBrakeMrk": true, - "weights": { "designWeight": 1900, "gbWeight": 1600 } - } - ], - "maxTrainDesignWeight": 500, - "euroStandard": "9", - "vehicleClass": { "description": "heavy goods vehicle", "code": "v" }, - "model": "FM", - "frontVehicleTo5thWheelCouplingMin": 1700, - "noOfAxles": 2, - "make": "Isuzu", - "maxTrainGbWeight": 1000, - "vehicleType": "hgv", - "speedLimiterMrk": true, - "recordCompleteness": "testable", - "vehicleConfiguration": "semi-car transporter", - "regnDate": "2019-06-25", - "tachoExemptMrk": true, - "frontAxleTo5thWheelMax": 1500, - "ntaNumber": "123456", - "trainGbWeight": 1500, - "reasonForCreation": "CVS Cypress testing", - "trainDesignWeight": 2000, - "roadFriendly": true, - "tyreUseCode": "2B", - "frontAxleTo5thWheelMin": 1200, - "euVehicleCategory": "n2", - "manufactureYear": 2018, - "dimensions": { "length": 7500, "width": 2200, "axleSpacing": [{ "axles": "1-2", "value": null }] }, - "statusCode": "current", - "numberOfWheelsDriven": null, - "fuelPropulsionSystem": "", - "offRoad": "", - "emissionsLimit": null, - "departmentalVehicleMarker": "", - "alterationMarker": "" -} diff --git a/cypress/fixtures/lgv.json b/cypress/fixtures/lgv.json deleted file mode 100644 index e6ef579434..0000000000 --- a/cypress/fixtures/lgv.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "reasonForCreation": "Cypress auto testing", - "vehicleType": "lgv", - "noOfAxles": 2, - "vehicleSubclass": ["c"], - "vehicleConfiguration": "other" -} diff --git a/cypress/fixtures/psv.json b/cypress/fixtures/psv.json deleted file mode 100644 index 14fc6caf73..0000000000 --- a/cypress/fixtures/psv.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "vehicleType": "psv", - "vehicleClass": { "code": "4", "description": "MOT class 4" }, - "bodyType": { "code": "b", "description": "box" }, - "reasonForCreation": "Cypress auto testing", - "brakes": { "brakeCode": "123" } -} diff --git a/cypress/fixtures/trl-testable.json b/cypress/fixtures/trl-testable.json deleted file mode 100644 index 8944fb3114..0000000000 --- a/cypress/fixtures/trl-testable.json +++ /dev/null @@ -1,179 +0,0 @@ -{ - "bodyType": { - "description": "flat", - "code": "f" - }, - "frontAxleToRearAxle": 0, - "suspensionType": "A", - "notes": "T/S 33 1/9/06", - "couplingType": "F", - "brakes": { - "antilockBrakingSystem": true, - "dtpNumber": "326577", - "loadSensingValve": true - }, - "conversionRefNo": " ", - "rearAxleToRearTrl": 1935, - "axles": [ - { - "axleNumber": 1, - "tyres": { - "tyreSize": "385/65-22.5", - "fitmentCode": "single", - "dataTrAxles": 158, - "plyRating": " ", - "tyreCode": 383 - }, - "parkingBrakeMrk": true, - "weights": { - "designWeight": 8250, - "gbWeight": 8000 - }, - "brakes": { - "leverLength": 135, - "springBrakeParking": true, - "brakeActuator": 24 - } - }, - { - "axleNumber": 2, - "tyres": { - "tyreSize": "385/65-22.5", - "fitmentCode": "single", - "dataTrAxles": 158, - "plyRating": " ", - "tyreCode": 383 - }, - "parkingBrakeMrk": true, - "weights": { - "designWeight": 8250, - "gbWeight": 8000 - }, - "brakes": { - "leverLength": 135, - "springBrakeParking": true, - "brakeActuator": 24 - } - }, - { - "axleNumber": 3, - "tyres": { - "tyreSize": "385/65-22.5", - "fitmentCode": "single", - "dataTrAxles": 158, - "plyRating": " ", - "tyreCode": 383 - }, - "parkingBrakeMrk": true, - "weights": { - "designWeight": 8250, - "gbWeight": 8000 - }, - "brakes": { - "leverLength": 135, - "springBrakeParking": true, - "brakeActuator": 24 - } - } - ], - "couplingCenterToRearAxleMax": 9430, - "maxLoadOnCoupling": 15000, - "grossGbWeight": 39000, - "couplingCenterToRearTrlMin": 11365, - "vehicleClass": { - "description": "trailer", - "code": "t" - }, - "model": " ", - "noOfAxles": 3, - "make": "DENNISON TRAILERS", - "grossDesignWeight": 39000, - "vehicleType": "trl", - "speedLimiterMrk": false, - "recordCompleteness": "complete", - "couplingCenterToRearAxleMin": 9430, - "vehicleConfiguration": "semi-trailer", - "regnDate": "2005-01-01", - "couplingCenterToRearTrlMax": 11365, - "firstUseDate": "0001-01-01", - "ntaNumber": "N000394760", - "adrDetails": { - "batteryListNumber": null, - "declarationsSeen": true, - "brakeEndurance": false, - "brakeDeclarationIssuer": null, - "vehicleDetails": { - "type": "SEMI TRAILER TANK", - "approvalDate": "2016-11-09" - }, - "documents": [], - "additionalExaminerNotes": "Additional Examiner Notes", - "adrTypeApprovalNo": null, - "permittedDangerousGoods": ["FP <61 (FL)", "AT"], - "weight": null, - "memosApply": [], - "applicantDetails": { - "name": "***", - "postcode": "********", - "town": "*****", - "city": "**", - "street": "********" - }, - "compatibilityGroupJ": null, - "brakeDeclarationsSeen": true, - "additionalNotes": { - "number": ["3"], - "guidanceNotes": [] - }, - "tank": { - "tankDetails": { - "yearOfManufacture": "2006", - "tc2Details": { - "tc2IntermediateApprovalNo": "HSB/5640", - "tc2IntermediateExpiryDate": null, - "tc2Type": null - }, - "tc3Details": [ - { - "tc3PeriodicNumber": "GB/M/14/008953", - "tc3PeriodicExpiryDate": "2017-12-11", - "tc3Type": null - } - ], - "specialProvisions": null, - "tankManufacturerSerialNo": "CTB296ST0644", - "tankCode": "L4BN", - "tankTypeAppNo": "GB/FT/TCHSB/5055", - "tankManufacturer": "CROSSLAND TANKERS" - }, - "tankStatement": { - "statement": "B", - "productListRefNo": null, - "productListUnNo": [null], - "substancesPermitted": "B", - "productList": "TARS LIQUID CLASS 3 UN NO. 1999 ELEVATED TEMPERATURE LIQUID FLAMMABLE NOS CLASS 3 UN NO. 3256 ELEVATED TEMPERATURE LIQUID NOS CLASS 9 UN NO. 3257" - } - }, - "listStatementApplicable": false - }, - "reasonForCreation": " ", - "roadFriendly": true, - "tyreUseCode": "2B", - "manufactureYear": 2005, - "dimensions": { - "axleSpacing": [ - { - "axles": "1", - "value": 1310 - }, - { - "axles": "2", - "value": 1310 - } - ], - "length": 12271, - "width": 2438 - }, - "statusCode": "current", - "numberOfWheelsDriven": null -} diff --git a/cypress/support/commands.ts b/cypress/support/commands.ts deleted file mode 100644 index f9fe3f3b38..0000000000 --- a/cypress/support/commands.ts +++ /dev/null @@ -1,123 +0,0 @@ -/// -declare namespace Cypress { - interface Chainable { - loginToAAD(): Chainable>; - createVehicle(vehicleType: string, vin: string, statusCode: string, primaryVrm: string): Chainable>; - createVehicleAndTest(vehicleType: string, vin: string, primaryVrm: string, testTypeId: string): Chainable>; - } -} - -function loginViaAAD(username: string, password: string) { - //Login to your AAD tenant. - cy.visit(''); - cy.origin( - 'login.microsoftonline.com', - { - args: { - username, - password - } - }, - ({ username, password }) => { - cy.get('input[type="email"]').type(username, { - log: false - }); - cy.get('input[type="submit"]').click(); - cy.get('input[type="password"]').type(password, { - log: false - }); - cy.get('input[type="submit"]').click(); - } - ); - - cy.url().should('equal', 'http://localhost:4200/'); -} - -function createVehicle(vehicleType: string, vin: string, statusCode: string, primaryVrm: string) { - cy.fixture(vehicleType).then(techRecord => { - techRecord.statusCode = statusCode; - const body = { - vin, - primaryVrm, - msUserDetails: { msUser: '123', msOid: '123' }, - techRecord: [techRecord] - }; - const headers = { - authorization: 'Bearer ' + window.localStorage.getItem('accessToken') - }; - cy.request({ method: 'POST', url: Cypress.env('vtm_api_uri') + '/vehicles', headers, body }).then(response => { - expect(response.body).to.have.property('systemNumber'); - }); - }); -} - -function createTest(vehicleType: string, vin: string, primaryVrm: string, testTypeId: string) { - cy.fixture(vehicleType).then(techRecord => { - techRecord.statusCode = 'current'; - const body = { - vin, - primaryVrm, - msUserDetails: { msUser: '123', msOid: '123' }, - techRecord: [techRecord] - }; - const headers = { - authorization: 'Bearer ' + window.localStorage.getItem('accessToken') - }; - cy.request({ method: 'POST', url: Cypress.env('vtm_api_uri') + '/vehicles', headers, body }).then(response => { - cy.fixture(testTypeId).then(testRecord => { - testRecord.systemNumber = response.body.systemNumber; - testRecord.testResultId = randomString(15); - testRecord.testStartTimestamp = new Date().toISOString(); - testRecord.testEndTimestamp = new Date().toISOString(); - testRecord.testTypes[0].testTypeStartTimestamp = new Date().toISOString(); - testRecord.testTypes[0].testTypeEndTimestamp = new Date().toISOString(); - testRecord.vin = vin; - const testBody = testRecord; - cy.request({ method: 'POST', url: Cypress.env('vtm_api_uri') + '/test-results', headers, body: testBody }).then(response => { - expect(response.status).to.equal(201); - }); - }); - }); - }); -} - -Cypress.Commands.add('loginToAAD', () => { - const username = Cypress.env('aad_username'); - const password = Cypress.env('aad_password'); - cy.session( - `loginSession`, - () => { - const log = Cypress.log({ - displayName: 'Azure Active Directory Login', - message: [`🔐 Authenticating | ${username}`], - // @ts-ignore - autoEnd: false - }); - - log.snapshot('before'); - - loginViaAAD(username, password); - - log.snapshot('after'); - log.end(); - }, - { - validate: () => { - cy.visit('http://localhost:4200'); - }, - cacheAcrossSpecs: true - } - ); -}); - -Cypress.Commands.add('createVehicle', (vehicleType: string, vin: string, statusCode: string, primaryVrm: string) => { - createVehicle(vehicleType, vin, statusCode, primaryVrm); -}); -Cypress.Commands.add('createVehicleAndTest', (vehicleType: string, vin: string, primaryVrm: string, testTypeId: string) => { - createTest(vehicleType, vin, primaryVrm, testTypeId); -}); - -const randomString = (length: number = 5) => { - const randomString = (Math.random() + 1).toString(36); - return randomString.substring(randomString.length - length); -}; diff --git a/cypress/support/e2e.ts b/cypress/support/e2e.ts deleted file mode 100644 index 1221b17e09..0000000000 --- a/cypress/support/e2e.ts +++ /dev/null @@ -1 +0,0 @@ -import './commands'; diff --git a/cypress/support/functions.ts b/cypress/support/functions.ts deleted file mode 100644 index efa859f17e..0000000000 --- a/cypress/support/functions.ts +++ /dev/null @@ -1,4 +0,0 @@ -export const randomString = (length: number = 5) => { - const randomString = (Math.random() + 1).toString(36); - return randomString.substring(randomString.length - length); -}; diff --git a/cypress/tsconfig.json b/cypress/tsconfig.json deleted file mode 100644 index 1daa4db3e5..0000000000 --- a/cypress/tsconfig.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "compilerOptions": { - "noEmit": true, - "target": "es5", - "lib": ["es5", "dom"], - "types": ["cypress", "node"] - }, - "include": ["./**/*.ts"], -} diff --git a/jest.config.js b/jest.config.js index dd7875c255..1e70394042 100644 --- a/jest.config.js +++ b/jest.config.js @@ -1,18 +1,31 @@ const { pathsToModuleNameMapper } = require('ts-jest'); const { compilerOptions } = require('./tsconfig.json'); + +const fileListToExclude = [ + '/src/app/.*\\.html$', + '/src/app/.*\\.index\\.ts$', + '/src/app/.*\\.enum\\.ts$', + '/src/app/.*\\.module\\.ts$', + '/src/app/.*\\.actions\\.ts$', + '/src/app/.*\\.template\\.ts$', +]; + module.exports = { - preset: 'jest-preset-angular', - roots: ['/src/'], - testMatch: ['**/+(*.)+(spec).+(ts)'], - setupFilesAfterEnv: ['/setup-jest.ts'], - collectCoverage: true, - transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'], - coverageDirectory: 'coverage/cvs-app-vtm', - testPathIgnorePatterns: ['/node_modules/', '/archive/', '/dist/'], - coveragePathIgnorePatterns: ['/src/mocks/', '/src/app/api/'], - testResultsProcessor: 'jest-sonar-reporter', - workerThreads: true, - moduleNameMapper: {...pathsToModuleNameMapper(compilerOptions.paths || {}, { - prefix: '/' - }), "@sentry/angular-ivy": "/node_modules/@sentry/angular-ivy/bundles/sentry-angular-ivy.umd.js"}, + preset: 'jest-preset-angular', + roots: ['/src/'], + testMatch: ['**/+(*.)+(spec).+(ts)'], + setupFilesAfterEnv: ['/setup-jest.ts'], + collectCoverage: true, + transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'], + coverageDirectory: 'coverage/cvs-app-vtm', + testPathIgnorePatterns: ['/node_modules/', '/archive/', '/dist/', ...fileListToExclude], + coveragePathIgnorePatterns: ['/src/mocks/', '/src/app/api/', ...fileListToExclude], + testResultsProcessor: 'jest-sonar-reporter', + workerThreads: true, + moduleNameMapper: { + ...pathsToModuleNameMapper(compilerOptions.paths || {}, { + prefix: '/', + }), + '@sentry/angular-ivy': '/node_modules/@sentry/angular-ivy/bundles/sentry-angular-ivy.umd.js', + }, }; diff --git a/package-lock.json b/package-lock.json index 1be11df960..b1b1466a79 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,107 +1,84 @@ { "name": "cvs-app-vtm", - "version": "1.20", + "version": "1.25", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "cvs-app-vtm", - "version": "1.20", + "version": "1.25", "license": "MIT", "dependencies": { - "@angular/animations": "^17.2.1", - "@angular/common": "^17.2.1", - "@angular/compiler": "^17.2.1", - "@angular/core": "^17.2.1", - "@angular/forms": "^17.2.1", - "@angular/platform-browser": "^17.2.1", - "@angular/platform-browser-dynamic": "^17.2.1", - "@angular/router": "^17.2.1", - "@azure/msal-angular": "^3.0.13", + "@angular/animations": "^18.2.0", + "@angular/common": "^18.2.0", + "@angular/compiler": "^18.2.0", + "@angular/core": "^18.2.0", + "@angular/forms": "^18.2.0", + "@angular/platform-browser": "^18.2.0", + "@angular/platform-browser-dynamic": "^18.2.0", + "@angular/router": "^18.2.0", + "@azure/msal-angular": "^3.0.20", "@azure/msal-browser": "^3.10.0", "@dvsa/cvs-type-definitions": "^6.3.0", - "@ngrx/effects": "^17.1.0", - "@ngrx/entity": "^17.1.0", - "@ngrx/router-store": "^17.1.0", - "@ngrx/store": "^17.1.0", - "@ngrx/store-devtools": "^17.1.0", - "@sentry/angular-ivy": "^7.102.0", - "@sentry/cli": "^2.28.6", - "@sentry/types": "^7.102.0", + "@ngrx/effects": "^18.0.2", + "@ngrx/entity": "^18.0.2", + "@ngrx/operators": "^18.0.0", + "@ngrx/router-store": "^18.0.2", + "@ngrx/store": "^18.0.2", + "@ngrx/store-devtools": "^18.0.2", + "@sentry/angular": "^8.26.0", + "@sentry/types": "^8.26.0", "accessible-autocomplete": "^2.0.4", + "angular-google-tag-manager": "^1.10.0", "deep-object-diff": "^1.1.9", - "eslint-import-resolver-typescript": "^3.6.1", "govuk-frontend": "^4.7.0", "jwt-decode": "^4.0.0", "lodash": "^4.17.21", "lodash.clonedeep": "^4.5.0", "lodash.merge": "^4.6.2", - "ngrx-store-localstorage": "17.0.0", + "ngrx-store-localstorage": "18.0.0", "rxjs": "^7.8.1", "tslib": "^2.6.2", "uuid": "^9.0.1", "validate-govuk-date": "^1.0.2", - "zone.js": "^0.14.4" + "zone.js": "^0.14.10" }, "devDependencies": { - "@angular-devkit/build-angular": "^17.2.0", - "@angular-eslint/builder": "^17.2.1", - "@angular-eslint/eslint-plugin": "^17.2.1", - "@angular-eslint/eslint-plugin-template": "^17.2.1", - "@angular-eslint/schematics": "^17.2.1", - "@angular-eslint/template-parser": "^17.2.1", - "@angular/cli": "^17.2.0", - "@angular/compiler-cli": "^17.2.1", + "@angular/build": "^18.2.0", + "@angular/cli": "^18.2.0", + "@angular/compiler-cli": "^18.2.0", "@babel/core": "^7.23.9", + "@biomejs/biome": "1.8.3", "@commitlint/cli": "^18.6.1", "@commitlint/config-conventional": "^18.6.2", "@compodoc/compodoc": "^1.1.23", - "@dvsa/eslint-config-ts": "^3.0.1", - "@ngrx/schematics": "^17.1.0", - "@storybook/addon-actions": "^7.6.17", - "@storybook/addon-controls": "^7.6.17", - "@storybook/addon-essentials": "^7.6.17", - "@storybook/addon-interactions": "^7.6.17", - "@storybook/addon-links": "^7.6.17", - "@storybook/angular": "^7.6.17", - "@storybook/cli": "^7.6.17", - "@storybook/testing-library": "^0.2.2", + "@dvsa/biome-config": "^0.1.0", + "@ngrx/schematics": "^18.0.2", + "@sentry/cli": "^2.32.2", "@types/jest": "^29.5.12", "@types/json-server": "^0.14.7", "@types/lodash.clonedeep": "^4.5.9", "@types/lodash.merge": "^4.6.9", - "@types/node": "^18.18.0", + "@types/node": "^20.14.8", "@types/uuid": "^9.0.8", - "@typescript-eslint/eslint-plugin": "^6.0.0", - "@typescript-eslint/parser": "^6.0.0", "babel-loader": "^9.1.3", "commitlint-plugin-function-rules": "^3.1.0", - "cypress": "^13.6.5", - "cypress-parallel": "^0.14.0", "dotenv": "^16.4.5", - "eslint": "^8.56.0", - "eslint-config-prettier": "^9.1.0", - "eslint-plugin-import": "^2.29.1", - "eslint-plugin-ngrx": "^2.1.4", - "eslint-plugin-prettier": "^5.1.3", - "eslint-plugin-storybook": "^0.8.0", "husky": "^9.0.11", "jest": "^29.7.0", - "jest-preset-angular": "^14.0.3", + "jest-preset-angular": "^14.2.2", "jest-sonar-reporter": "^2.0.0", "json-server": "^0.17.3", - "prettier": "^3.2.5", "sonarqube-scanner": "^3.3.0", - "storybook": "^7.6.17", "ts-jest": "^29.1.2", "ts-node": "^10.9.2", "ts-patch": "^3.1.2", - "typescript": "^5.3.3", + "typescript": "^5.5.4", "yargs": "^17.7.2" }, "engines": { - "node": "18.18.0", - "npm": "9.8.1" + "node": "20.16.*", + "npm": "10.8.*" }, "optionalDependencies": { "@nx/nx-darwin-arm64": "18.0.4", @@ -110,14 +87,6 @@ "@nx/nx-win32-x64-msvc": "18.0.4" } }, - "node_modules/@aashutoshrathi/word-wrap": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", - "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/@aduh95/viz.js": { "version": "3.4.0", "resolved": "https://registry.npmjs.org/@aduh95/viz.js/-/viz.js-3.4.0.tgz", @@ -125,125 +94,125 @@ "dev": true }, "node_modules/@ampproject/remapping": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", - "integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", "dev": true, "dependencies": { - "@jridgewell/gen-mapping": "^0.3.0", - "@jridgewell/trace-mapping": "^0.3.9" + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" }, "engines": { "node": ">=6.0.0" } }, "node_modules/@angular-devkit/architect": { - "version": "0.1702.2", - "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1702.2.tgz", - "integrity": "sha512-qBvif8/NquFUqVQgs4U+8wXh/rQZv+YlYwg6eDZly1bIaTd/k9spko/seTtNT1OpK/Be+GLo5IbiQ7i2SON3iQ==", + "version": "0.1802.0", + "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1802.0.tgz", + "integrity": "sha512-s1atTSL98XLUUxfWEQAnhFaOFIJZDLWjSqec+Sb+f4iZFj+hOFejzJxPVnHMIJudOzn0Lqjk3t987KG/zNEGdg==", "dev": true, "dependencies": { - "@angular-devkit/core": "17.2.2", + "@angular-devkit/core": "18.2.0", "rxjs": "7.8.1" }, "engines": { - "node": "^18.13.0 || >=20.9.0", + "node": "^18.19.1 || ^20.11.1 || >=22.0.0", "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", "yarn": ">= 1.13.0" } }, "node_modules/@angular-devkit/build-angular": { - "version": "17.2.2", - "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-17.2.2.tgz", - "integrity": "sha512-K55xBiWBfxD4wmxLR2viOPbBryOk6YaZeNr72IMkp1yIrIy1BES6LDJi7R9fDW7+TprqZdM4B91Tkc+BCwYQzQ==", - "dev": true, - "dependencies": { - "@ampproject/remapping": "2.2.1", - "@angular-devkit/architect": "0.1702.2", - "@angular-devkit/build-webpack": "0.1702.2", - "@angular-devkit/core": "17.2.2", - "@babel/core": "7.23.9", - "@babel/generator": "7.23.6", - "@babel/helper-annotate-as-pure": "7.22.5", - "@babel/helper-split-export-declaration": "7.22.6", - "@babel/plugin-transform-async-generator-functions": "7.23.9", - "@babel/plugin-transform-async-to-generator": "7.23.3", - "@babel/plugin-transform-runtime": "7.23.9", - "@babel/preset-env": "7.23.9", - "@babel/runtime": "7.23.9", - "@discoveryjs/json-ext": "0.5.7", - "@ngtools/webpack": "17.2.2", + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-18.2.0.tgz", + "integrity": "sha512-V0XKT7xt6d6duXqmDAQEQgEJNXuWAekpHEDxafvBT0MTxcEhu0ozQVwI4oAekiKOz+APIcAZyMzvw3Tlzog5cw==", + "dev": true, + "peer": true, + "dependencies": { + "@ampproject/remapping": "2.3.0", + "@angular-devkit/architect": "0.1802.0", + "@angular-devkit/build-webpack": "0.1802.0", + "@angular-devkit/core": "18.2.0", + "@angular/build": "18.2.0", + "@babel/core": "7.25.2", + "@babel/generator": "7.25.0", + "@babel/helper-annotate-as-pure": "7.24.7", + "@babel/helper-split-export-declaration": "7.24.7", + "@babel/plugin-transform-async-generator-functions": "7.25.0", + "@babel/plugin-transform-async-to-generator": "7.24.7", + "@babel/plugin-transform-runtime": "7.24.7", + "@babel/preset-env": "7.25.3", + "@babel/runtime": "7.25.0", + "@discoveryjs/json-ext": "0.6.1", + "@ngtools/webpack": "18.2.0", "@vitejs/plugin-basic-ssl": "1.1.0", "ansi-colors": "4.1.3", - "autoprefixer": "10.4.17", + "autoprefixer": "10.4.20", "babel-loader": "9.1.3", - "babel-plugin-istanbul": "6.1.1", "browserslist": "^4.21.5", - "copy-webpack-plugin": "11.0.0", - "critters": "0.0.20", - "css-loader": "6.10.0", - "esbuild-wasm": "0.20.0", + "copy-webpack-plugin": "12.0.2", + "critters": "0.0.24", + "css-loader": "7.1.2", + "esbuild-wasm": "0.23.0", "fast-glob": "3.3.2", - "http-proxy-middleware": "2.0.6", - "https-proxy-agent": "7.0.2", - "inquirer": "9.2.14", - "jsonc-parser": "3.2.1", + "http-proxy-middleware": "3.0.0", + "https-proxy-agent": "7.0.5", + "istanbul-lib-instrument": "6.0.3", + "jsonc-parser": "3.3.1", "karma-source-map-support": "1.4.0", "less": "4.2.0", - "less-loader": "11.1.0", + "less-loader": "12.2.0", "license-webpack-plugin": "4.0.2", - "loader-utils": "3.2.1", - "magic-string": "0.30.7", - "mini-css-extract-plugin": "2.8.0", + "loader-utils": "3.3.1", + "magic-string": "0.30.11", + "mini-css-extract-plugin": "2.9.0", "mrmime": "2.0.0", - "open": "8.4.2", + "open": "10.1.0", "ora": "5.4.1", "parse5-html-rewriting-stream": "7.0.0", - "picomatch": "4.0.1", - "piscina": "4.3.1", - "postcss": "8.4.35", - "postcss-loader": "8.1.0", + "picomatch": "4.0.2", + "piscina": "4.6.1", + "postcss": "8.4.41", + "postcss-loader": "8.1.1", "resolve-url-loader": "5.0.0", "rxjs": "7.8.1", - "sass": "1.70.0", - "sass-loader": "14.1.0", - "semver": "7.6.0", + "sass": "1.77.8", + "sass-loader": "16.0.0", + "semver": "7.6.3", "source-map-loader": "5.0.0", "source-map-support": "0.5.21", - "terser": "5.27.0", + "terser": "5.31.6", "tree-kill": "1.2.2", - "tslib": "2.6.2", - "undici": "6.6.2", - "vite": "5.0.12", - "watchpack": "2.4.0", - "webpack": "5.90.1", - "webpack-dev-middleware": "6.1.1", - "webpack-dev-server": "4.15.1", - "webpack-merge": "5.10.0", + "tslib": "2.6.3", + "vite": "5.4.0", + "watchpack": "2.4.1", + "webpack": "5.93.0", + "webpack-dev-middleware": "7.3.0", + "webpack-dev-server": "5.0.4", + "webpack-merge": "6.0.1", "webpack-subresource-integrity": "5.1.0" }, "engines": { - "node": "^18.13.0 || >=20.9.0", + "node": "^18.19.1 || ^20.11.1 || >=22.0.0", "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", "yarn": ">= 1.13.0" }, "optionalDependencies": { - "esbuild": "0.20.0" + "esbuild": "0.23.0" }, "peerDependencies": { - "@angular/compiler-cli": "^17.0.0", - "@angular/localize": "^17.0.0", - "@angular/platform-server": "^17.0.0", - "@angular/service-worker": "^17.0.0", + "@angular/compiler-cli": "^18.0.0", + "@angular/localize": "^18.0.0", + "@angular/platform-server": "^18.0.0", + "@angular/service-worker": "^18.0.0", "@web/test-runner": "^0.18.0", "browser-sync": "^3.0.2", "jest": "^29.5.0", "jest-environment-jsdom": "^29.5.0", "karma": "^6.3.0", - "ng-packagr": "^17.0.0", + "ng-packagr": "^18.0.0", "protractor": "^7.0.0", "tailwindcss": "^2.0.0 || ^3.0.0", - "typescript": ">=5.2 <5.4" + "typescript": ">=5.4 <5.6" }, "peerDependenciesMeta": { "@angular/localize": { @@ -281,209 +250,71 @@ } } }, - "node_modules/@angular-devkit/build-angular/node_modules/@babel/core": { - "version": "7.23.9", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.23.9.tgz", - "integrity": "sha512-5q0175NOjddqpvvzU+kDiSOAk4PfdO6FvwCWoQ6RO7rTzEe8vlo+4HVfcnAREhD4npMs0e9uZypjTwzZPCf/cw==", + "node_modules/@angular-devkit/build-angular/node_modules/istanbul-lib-instrument": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz", + "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==", "dev": true, + "peer": true, "dependencies": { - "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.23.5", - "@babel/generator": "^7.23.6", - "@babel/helper-compilation-targets": "^7.23.6", - "@babel/helper-module-transforms": "^7.23.3", - "@babel/helpers": "^7.23.9", + "@babel/core": "^7.23.9", "@babel/parser": "^7.23.9", - "@babel/template": "^7.23.9", - "@babel/traverse": "^7.23.9", - "@babel/types": "^7.23.9", - "convert-source-map": "^2.0.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.3", - "semver": "^6.3.1" + "@istanbuljs/schema": "^0.1.3", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^7.5.4" }, "engines": { - "node": ">=6.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/babel" + "node": ">=10" } }, - "node_modules/@angular-devkit/build-angular/node_modules/@babel/core/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "node_modules/@angular-devkit/build-angular/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, + "peer": true, "bin": { "semver": "bin/semver.js" - } - }, - "node_modules/@angular-devkit/build-angular/node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/@angular-devkit/build-angular/node_modules/ajv-keywords": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "dev": true, - "peerDependencies": { - "ajv": "^6.9.1" - } - }, - "node_modules/@angular-devkit/build-angular/node_modules/convert-source-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "dev": true - }, - "node_modules/@angular-devkit/build-angular/node_modules/eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", - "dev": true, - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/@angular-devkit/build-angular/node_modules/estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/@angular-devkit/build-angular/node_modules/json-parse-even-better-errors": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "dev": true - }, - "node_modules/@angular-devkit/build-angular/node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - }, - "node_modules/@angular-devkit/build-angular/node_modules/schema-utils": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", - "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", - "dev": true, - "dependencies": { - "@types/json-schema": "^7.0.8", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/@angular-devkit/build-angular/node_modules/webpack": { - "version": "5.90.1", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.90.1.tgz", - "integrity": "sha512-SstPdlAC5IvgFnhiRok8hqJo/+ArAbNv7rhU4fnWGHNVfN59HSQFaxZDSAL3IFG2YmqxuRs+IU33milSxbPlog==", - "dev": true, - "dependencies": { - "@types/eslint-scope": "^3.7.3", - "@types/estree": "^1.0.5", - "@webassemblyjs/ast": "^1.11.5", - "@webassemblyjs/wasm-edit": "^1.11.5", - "@webassemblyjs/wasm-parser": "^1.11.5", - "acorn": "^8.7.1", - "acorn-import-assertions": "^1.9.0", - "browserslist": "^4.21.10", - "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.15.0", - "es-module-lexer": "^1.2.1", - "eslint-scope": "5.1.1", - "events": "^3.2.0", - "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.2.9", - "json-parse-even-better-errors": "^2.3.1", - "loader-runner": "^4.2.0", - "mime-types": "^2.1.27", - "neo-async": "^2.6.2", - "schema-utils": "^3.2.0", - "tapable": "^2.1.1", - "terser-webpack-plugin": "^5.3.10", - "watchpack": "^2.4.0", - "webpack-sources": "^3.2.3" - }, - "bin": { - "webpack": "bin/webpack.js" }, "engines": { - "node": ">=10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependenciesMeta": { - "webpack-cli": { - "optional": true - } + "node": ">=10" } }, "node_modules/@angular-devkit/build-webpack": { - "version": "0.1702.2", - "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.1702.2.tgz", - "integrity": "sha512-+c7rHD2Se1VD9i9uPEYHqhq8hTnsUAn5LfeJCLS8g7FU8T42tDSC/k1qWxHp7d99kf7ecg2BvYcZDlYaBUnl3A==", + "version": "0.1802.0", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.1802.0.tgz", + "integrity": "sha512-bU7AxlI/avnlOLrgE195cokrOA1ayT6JjRv8Hxzh1bIOa1jE87HsyjxvQhOLcdEb97zwHqMqbntcgUNBgsegJQ==", "dev": true, + "peer": true, "dependencies": { - "@angular-devkit/architect": "0.1702.2", + "@angular-devkit/architect": "0.1802.0", "rxjs": "7.8.1" }, "engines": { - "node": "^18.13.0 || >=20.9.0", + "node": "^18.19.1 || ^20.11.1 || >=22.0.0", "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", "yarn": ">= 1.13.0" }, "peerDependencies": { "webpack": "^5.30.0", - "webpack-dev-server": "^4.0.0" + "webpack-dev-server": "^5.0.2" } }, "node_modules/@angular-devkit/core": { - "version": "17.2.2", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-17.2.2.tgz", - "integrity": "sha512-bKMi6bBkEeN4a3qTxCykhrAvE0ESHhKO38Qh1bN/8QSyvKVAEyVAVls5W9IN5GKRHvXgEn9aw+DSzRnPpy9nyw==", + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-18.2.0.tgz", + "integrity": "sha512-8SOopyUKUMqAq2rj32XkTIfr79Y274k4uglxxRtzHYoWwHlLdG0KrA+wCcsh/FU9PyR4dA+5dcDAApn358ZF+Q==", "dev": true, "dependencies": { - "ajv": "8.12.0", - "ajv-formats": "2.1.1", - "jsonc-parser": "3.2.1", - "picomatch": "4.0.1", + "ajv": "8.17.1", + "ajv-formats": "3.0.1", + "jsonc-parser": "3.3.1", + "picomatch": "4.0.2", "rxjs": "7.8.1", "source-map": "0.7.4" }, "engines": { - "node": "^18.13.0 || >=20.9.0", + "node": "^18.19.1 || ^20.11.1 || >=22.0.0", "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", "yarn": ">= 1.13.0" }, @@ -496,158 +327,156 @@ } } }, + "node_modules/@angular-devkit/core/node_modules/ajv-formats": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", + "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", + "dev": true, + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, "node_modules/@angular-devkit/schematics": { - "version": "17.2.2", - "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-17.2.2.tgz", - "integrity": "sha512-t6dBhHvto9BEIo+Kew0+YyIS3TV1SEd4MActUk+zF4NNQyJ8wRUHL+8glUKB6ZWPyCTYSinJ+QKn/3yytELTHg==", + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-18.2.0.tgz", + "integrity": "sha512-WWKwz2RKMVI6T25JFwOSSfRLB+anNzabVIRwf85R/YMIo34BUk777f2JU/7cCKlxSpQu39TqIfMQZAyzAD1z0A==", "dev": true, "dependencies": { - "@angular-devkit/core": "17.2.2", - "jsonc-parser": "3.2.1", - "magic-string": "0.30.7", + "@angular-devkit/core": "18.2.0", + "jsonc-parser": "3.3.1", + "magic-string": "0.30.11", "ora": "5.4.1", "rxjs": "7.8.1" }, "engines": { - "node": "^18.13.0 || >=20.9.0", + "node": "^18.19.1 || ^20.11.1 || >=22.0.0", "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", "yarn": ">= 1.13.0" } }, - "node_modules/@angular-eslint/builder": { - "version": "17.2.1", - "resolved": "https://registry.npmjs.org/@angular-eslint/builder/-/builder-17.2.1.tgz", - "integrity": "sha512-O30eaR0wCPiP+zKWvXj2JM8hVq30Wok2rp7zJMFm3PurjF9nWIIyexXkE5fa538DYZYxu8N3gQRqhpv5jvTXCg==", - "dev": true, + "node_modules/@angular/animations": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-18.2.0.tgz", + "integrity": "sha512-BFAfqDDjsa0Q91F4s33pFcBG+ydFDurEmwYGG1gmO7UXZJI6ZbRVdULaZHz75M+Bf3hJkzVB05pesvfbK+Fg/g==", "dependencies": { - "@nx/devkit": "17.2.8", - "nx": "17.2.8" + "tslib": "^2.3.0" }, - "peerDependencies": { - "eslint": "^7.20.0 || ^8.0.0", - "typescript": "*" - } - }, - "node_modules/@angular-eslint/bundled-angular-compiler": { - "version": "17.2.1", - "resolved": "https://registry.npmjs.org/@angular-eslint/bundled-angular-compiler/-/bundled-angular-compiler-17.2.1.tgz", - "integrity": "sha512-puC0itsZv2QlrDOCcWtq1KZH+DvfrpV+mV78HHhi6+h25R5iIhr8ARKcl3EQxFjvrFq34jhG8pSupxKvFbHVfA==", - "dev": true - }, - "node_modules/@angular-eslint/eslint-plugin": { - "version": "17.2.1", - "resolved": "https://registry.npmjs.org/@angular-eslint/eslint-plugin/-/eslint-plugin-17.2.1.tgz", - "integrity": "sha512-9yA81BHpsaCUKRBtHGN3ieAy8HpIoffzPQMu34lYqZFT4yGHGhYmhQjNSQGBRbV2LD9dVv2U35rMHNmUcozXpw==", - "dev": true, - "dependencies": { - "@angular-eslint/utils": "17.2.1", - "@typescript-eslint/utils": "6.19.0" + "engines": { + "node": "^18.19.1 || ^20.11.1 || >=22.0.0" }, "peerDependencies": { - "eslint": "^7.20.0 || ^8.0.0", - "typescript": "*" + "@angular/core": "18.2.0" } }, - "node_modules/@angular-eslint/eslint-plugin-template": { - "version": "17.2.1", - "resolved": "https://registry.npmjs.org/@angular-eslint/eslint-plugin-template/-/eslint-plugin-template-17.2.1.tgz", - "integrity": "sha512-hl1hcHtcm90wyVL1OQGTz16oA0KHon+FFb3Qg0fLXObaXxA495Ecefd9ub5Xxg4JEOPRDi29bF1Y3YKpwflgeg==", + "node_modules/@angular/build": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/@angular/build/-/build-18.2.0.tgz", + "integrity": "sha512-LvNJ2VOEVy3N1tGzt+xnKyweRBuUE1NsnuEEWAb9Y+V1cyRgj0s7FX2+IQZZQhP+W/pXfbsKaByOAbJ5KWV85Q==", "dev": true, "dependencies": { - "@angular-eslint/bundled-angular-compiler": "17.2.1", - "@angular-eslint/utils": "17.2.1", - "@typescript-eslint/type-utils": "6.19.0", - "@typescript-eslint/utils": "6.19.0", - "aria-query": "5.3.0", - "axobject-query": "4.0.0" + "@ampproject/remapping": "2.3.0", + "@angular-devkit/architect": "0.1802.0", + "@babel/core": "7.25.2", + "@babel/helper-annotate-as-pure": "7.24.7", + "@babel/helper-split-export-declaration": "7.24.7", + "@babel/plugin-syntax-import-attributes": "7.24.7", + "@inquirer/confirm": "3.1.22", + "@vitejs/plugin-basic-ssl": "1.1.0", + "browserslist": "^4.23.0", + "critters": "0.0.24", + "esbuild": "0.23.0", + "fast-glob": "3.3.2", + "https-proxy-agent": "7.0.5", + "listr2": "8.2.4", + "lmdb": "3.0.13", + "magic-string": "0.30.11", + "mrmime": "2.0.0", + "parse5-html-rewriting-stream": "7.0.0", + "picomatch": "4.0.2", + "piscina": "4.6.1", + "rollup": "4.20.0", + "sass": "1.77.8", + "semver": "7.6.3", + "vite": "5.4.0", + "watchpack": "2.4.1" }, - "peerDependencies": { - "eslint": "^7.20.0 || ^8.0.0", - "typescript": "*" - } - }, - "node_modules/@angular-eslint/schematics": { - "version": "17.2.1", - "resolved": "https://registry.npmjs.org/@angular-eslint/schematics/-/schematics-17.2.1.tgz", - "integrity": "sha512-7ldtIePI4ZTp/TBpeOZkzfv30HSAn//4TgtFuqvojudI8n8batV5FqQ0VNm1e0zitl75t8Zwtr0KYT4I6vh59g==", - "dev": true, - "dependencies": { - "@angular-eslint/eslint-plugin": "17.2.1", - "@angular-eslint/eslint-plugin-template": "17.2.1", - "@nx/devkit": "17.2.8", - "ignore": "5.3.0", - "nx": "17.2.8", - "strip-json-comments": "3.1.1", - "tmp": "0.2.1" + "engines": { + "node": "^18.19.1 || ^20.11.1 || >=22.0.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" }, "peerDependencies": { - "@angular/cli": ">= 17.0.0 < 18.0.0" - } - }, - "node_modules/@angular-eslint/template-parser": { - "version": "17.2.1", - "resolved": "https://registry.npmjs.org/@angular-eslint/template-parser/-/template-parser-17.2.1.tgz", - "integrity": "sha512-WPQYFvRju0tCDXQ/pwrzC911pE07JvpeDgcN2elhzV6lxDHJEZpA5O9pnW9qgNA6J6XM9Q7dBkJ22ztAzC4WFw==", - "dev": true, - "dependencies": { - "@angular-eslint/bundled-angular-compiler": "17.2.1", - "eslint-scope": "^8.0.0" + "@angular/compiler-cli": "^18.0.0", + "@angular/localize": "^18.0.0", + "@angular/platform-server": "^18.0.0", + "@angular/service-worker": "^18.0.0", + "less": "^4.2.0", + "postcss": "^8.4.0", + "tailwindcss": "^2.0.0 || ^3.0.0", + "typescript": ">=5.4 <5.6" }, - "peerDependencies": { - "eslint": "^7.20.0 || ^8.0.0", - "typescript": "*" + "peerDependenciesMeta": { + "@angular/localize": { + "optional": true + }, + "@angular/platform-server": { + "optional": true + }, + "@angular/service-worker": { + "optional": true + }, + "less": { + "optional": true + }, + "postcss": { + "optional": true + }, + "tailwindcss": { + "optional": true + } } }, - "node_modules/@angular-eslint/utils": { - "version": "17.2.1", - "resolved": "https://registry.npmjs.org/@angular-eslint/utils/-/utils-17.2.1.tgz", - "integrity": "sha512-qQYTBXy90dWM7fhhpa5i9lTtqqhJisvRa+naCrQx9kBgR458JScLdkVIdcZ9D/rPiDCmKiVUfgcDISnjUeqTqg==", + "node_modules/@angular/build/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, - "dependencies": { - "@angular-eslint/bundled-angular-compiler": "17.2.1", - "@typescript-eslint/utils": "6.19.0" - }, - "peerDependencies": { - "eslint": "^7.20.0 || ^8.0.0", - "typescript": "*" - } - }, - "node_modules/@angular/animations": { - "version": "17.2.3", - "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-17.2.3.tgz", - "integrity": "sha512-eQcN6hC/dXISEYC/TjRuQJgfdZieBROBlXrS+BxRbsy9T4/QeKxChC3yiNxTmdxl5mvjLKvQTXHR8X0AWc07/Q==", - "dependencies": { - "tslib": "^2.3.0" + "bin": { + "semver": "bin/semver.js" }, "engines": { - "node": "^18.13.0 || >=20.9.0" - }, - "peerDependencies": { - "@angular/core": "17.2.3" + "node": ">=10" } }, "node_modules/@angular/cli": { - "version": "17.2.2", - "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-17.2.2.tgz", - "integrity": "sha512-cGGOnOTjU1bHBAU+5LMR1vfjUSmIY204pUcRAHu6xq1Qp8jm0Wf1lYOG1KrzpDezKa8d0WZe6FIVlxsCZRRYSw==", + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-18.2.0.tgz", + "integrity": "sha512-hA60QIA7Dh8LQxM42wqd7WrhwQjbjB8oIRH5Slgbiv8iocAo76scp1/qyZo2SSzjfkB7jxUiao5L4ckiJ/hvZg==", "dev": true, "dependencies": { - "@angular-devkit/architect": "0.1702.2", - "@angular-devkit/core": "17.2.2", - "@angular-devkit/schematics": "17.2.2", - "@schematics/angular": "17.2.2", + "@angular-devkit/architect": "0.1802.0", + "@angular-devkit/core": "18.2.0", + "@angular-devkit/schematics": "18.2.0", + "@inquirer/prompts": "5.3.8", + "@listr2/prompt-adapter-inquirer": "2.0.15", + "@schematics/angular": "18.2.0", "@yarnpkg/lockfile": "1.1.0", - "ansi-colors": "4.1.3", - "ini": "4.1.1", - "inquirer": "9.2.14", - "jsonc-parser": "3.2.1", - "npm-package-arg": "11.0.1", - "npm-pick-manifest": "9.0.0", - "open": "8.4.2", - "ora": "5.4.1", - "pacote": "17.0.6", + "ini": "4.1.3", + "jsonc-parser": "3.3.1", + "listr2": "8.2.4", + "npm-package-arg": "11.0.3", + "npm-pick-manifest": "9.1.0", + "pacote": "18.0.6", "resolve": "1.22.8", - "semver": "7.6.0", + "semver": "7.6.3", "symbol-observable": "4.0.0", "yargs": "17.7.2" }, @@ -655,38 +484,50 @@ "ng": "bin/ng.js" }, "engines": { - "node": "^18.13.0 || >=20.9.0", + "node": "^18.19.1 || ^20.11.1 || >=22.0.0", "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", "yarn": ">= 1.13.0" } }, + "node_modules/@angular/cli/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, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/@angular/common": { - "version": "17.2.3", - "resolved": "https://registry.npmjs.org/@angular/common/-/common-17.2.3.tgz", - "integrity": "sha512-XR3rWS4W7/+RknyJMUUo9E81mSeyUznpclqTZ+Hy7+i4Naeso0qcRaIyr6JJmB5UGvlnfT1MlH9Fj78Dc80NEw==", + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/@angular/common/-/common-18.2.0.tgz", + "integrity": "sha512-DELx/QYNqqjmiM+kE5PoVmyG4gPw5WB1bDDeg3hEuBCK3eS2KosgQH0/MQo3OSBZHOcAMFjfHMGqKgxndmYixQ==", "dependencies": { "tslib": "^2.3.0" }, "engines": { - "node": "^18.13.0 || >=20.9.0" + "node": "^18.19.1 || ^20.11.1 || >=22.0.0" }, "peerDependencies": { - "@angular/core": "17.2.3", + "@angular/core": "18.2.0", "rxjs": "^6.5.3 || ^7.4.0" } }, "node_modules/@angular/compiler": { - "version": "17.2.3", - "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-17.2.3.tgz", - "integrity": "sha512-U2okLZ+4ipD5zTv32pMp+RsrM3kkP0XneSsIMPRpYZZfKgfnGLIwkRx6FoVoBwByugng6lBG/PiIe8DhRU/HFg==", + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-18.2.0.tgz", + "integrity": "sha512-RmGwQ7jRzotUKKMk0CwxTcIXFr5mRxSbJf9o5S3ujuIOo1lYop8SQZvjq67a5BuoYyD+1pX6iMmxZqlbKoihBQ==", "dependencies": { "tslib": "^2.3.0" }, "engines": { - "node": "^18.13.0 || >=20.9.0" + "node": "^18.19.1 || ^20.11.1 || >=22.0.0" }, "peerDependencies": { - "@angular/core": "17.2.3" + "@angular/core": "18.2.0" }, "peerDependenciesMeta": { "@angular/core": { @@ -695,12 +536,12 @@ } }, "node_modules/@angular/compiler-cli": { - "version": "17.2.3", - "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-17.2.3.tgz", - "integrity": "sha512-mATybangypneXwO270VQeIw3N0avzc2Lpvdb8nm9WZYj23AcTUzpUUKOn63HtJdwMT5J2GjkyZFSRXisiPmpkA==", + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-18.2.0.tgz", + "integrity": "sha512-pPBFjMqNTNettrleLtEc6a/ysOZjG3F0Z5lyKYePcyNQmn2rpa9XmD2y6PhmzTmIhxeXrogWA84Xgg/vK5wBNw==", "dev": true, "dependencies": { - "@babel/core": "7.23.9", + "@babel/core": "7.25.2", "@jridgewell/sourcemap-codec": "^1.4.14", "chokidar": "^3.0.0", "convert-source-map": "^1.5.1", @@ -715,104 +556,59 @@ "ngcc": "bundles/ngcc/index.js" }, "engines": { - "node": "^18.13.0 || >=20.9.0" + "node": "^18.19.1 || ^20.11.1 || >=22.0.0" }, "peerDependencies": { - "@angular/compiler": "17.2.3", - "typescript": ">=5.2 <5.4" + "@angular/compiler": "18.2.0", + "typescript": ">=5.4 <5.6" } }, - "node_modules/@angular/compiler-cli/node_modules/@babel/core": { - "version": "7.23.9", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.23.9.tgz", - "integrity": "sha512-5q0175NOjddqpvvzU+kDiSOAk4PfdO6FvwCWoQ6RO7rTzEe8vlo+4HVfcnAREhD4npMs0e9uZypjTwzZPCf/cw==", - "dev": true, + "node_modules/@angular/core": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/@angular/core/-/core-18.2.0.tgz", + "integrity": "sha512-7+4wXfeAk1TnG3MGho2gpBI5XHxeSRWzLK2rC5qyyRbmMV+GrIgf1HqFjQ4S02rydkQvGpjqQHtO1PYJnyn4bg==", "dependencies": { - "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.23.5", - "@babel/generator": "^7.23.6", - "@babel/helper-compilation-targets": "^7.23.6", - "@babel/helper-module-transforms": "^7.23.3", - "@babel/helpers": "^7.23.9", - "@babel/parser": "^7.23.9", - "@babel/template": "^7.23.9", - "@babel/traverse": "^7.23.9", - "@babel/types": "^7.23.9", - "convert-source-map": "^2.0.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.3", - "semver": "^6.3.1" + "tslib": "^2.3.0" }, "engines": { - "node": ">=6.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/babel" - } - }, - "node_modules/@angular/compiler-cli/node_modules/@babel/core/node_modules/convert-source-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "dev": true - }, - "node_modules/@angular/compiler-cli/node_modules/@babel/core/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@angular/core": { - "version": "17.2.3", - "resolved": "https://registry.npmjs.org/@angular/core/-/core-17.2.3.tgz", - "integrity": "sha512-DU+RdUB4E4I489R2P2hOrgkCDJNXlVaTzYixpgeDnuldCIYM0MatEzjor9DYNL3EDCayHF+M4HlVOcn6T/IVPQ==", - "dependencies": { - "tslib": "^2.3.0" - }, - "engines": { - "node": "^18.13.0 || >=20.9.0" + "node": "^18.19.1 || ^20.11.1 || >=22.0.0" }, "peerDependencies": { "rxjs": "^6.5.3 || ^7.4.0", - "zone.js": "~0.14.0" + "zone.js": "~0.14.10" } }, "node_modules/@angular/forms": { - "version": "17.2.3", - "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-17.2.3.tgz", - "integrity": "sha512-v+/6pimht808F5XpmVTNV4/109s+A7m3nadQP97qvIDsrtwrPPZR7cST+DRioG2C41VwtjXM0HVbIon/3ydo6A==", + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-18.2.0.tgz", + "integrity": "sha512-G+4BjNCUo4cRwg9NwisGLbtG/1AbIJNOO3RUejPJJbCcAkYMSzmDWSQ+LQEGW4vC/1xaDKO8AT71DI/I09bOxA==", "dependencies": { "tslib": "^2.3.0" }, "engines": { - "node": "^18.13.0 || >=20.9.0" + "node": "^18.19.1 || ^20.11.1 || >=22.0.0" }, "peerDependencies": { - "@angular/common": "17.2.3", - "@angular/core": "17.2.3", - "@angular/platform-browser": "17.2.3", + "@angular/common": "18.2.0", + "@angular/core": "18.2.0", + "@angular/platform-browser": "18.2.0", "rxjs": "^6.5.3 || ^7.4.0" } }, "node_modules/@angular/platform-browser": { - "version": "17.2.3", - "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-17.2.3.tgz", - "integrity": "sha512-bFi+H8avyCjwSBy+zpOKmqx852MRH8fkuZa4XgwKCPJRay8BfSCjHdtIo3eokUNPMu9JsyXM7HYKIfzLu5y6LA==", + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-18.2.0.tgz", + "integrity": "sha512-yhj281TuPz5a8CehwucwIVl29Oqte9KS4X/VQkMV++GpYQE2KKKcoff4FXSdF5RUcUYkK2li4IvawIqPmUSagg==", "dependencies": { "tslib": "^2.3.0" }, "engines": { - "node": "^18.13.0 || >=20.9.0" + "node": "^18.19.1 || ^20.11.1 || >=22.0.0" }, "peerDependencies": { - "@angular/animations": "17.2.3", - "@angular/common": "17.2.3", - "@angular/core": "17.2.3" + "@angular/animations": "18.2.0", + "@angular/common": "18.2.0", + "@angular/core": "18.2.0" }, "peerDependenciesMeta": { "@angular/animations": { @@ -821,120 +617,111 @@ } }, "node_modules/@angular/platform-browser-dynamic": { - "version": "17.2.3", - "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-17.2.3.tgz", - "integrity": "sha512-K8CsHbmG2nvV1jrNN9PYxyA0zJNoIWp+qf2udvPhG8rJ+Pyw61qmptrarpQUUkr8ONOtjwtOsnKa9/w+15nExw==", + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-18.2.0.tgz", + "integrity": "sha512-izfaXKNC/kqOEzJG8eTnFu39VLI3vv3dTKoYOdEKRB7MTI0t0x66oZmABnHcihtkTSvXs/is+7lA5HmH2ZuIEQ==", "dependencies": { "tslib": "^2.3.0" }, "engines": { - "node": "^18.13.0 || >=20.9.0" + "node": "^18.19.1 || ^20.11.1 || >=22.0.0" }, "peerDependencies": { - "@angular/common": "17.2.3", - "@angular/compiler": "17.2.3", - "@angular/core": "17.2.3", - "@angular/platform-browser": "17.2.3" + "@angular/common": "18.2.0", + "@angular/compiler": "18.2.0", + "@angular/core": "18.2.0", + "@angular/platform-browser": "18.2.0" } }, "node_modules/@angular/router": { - "version": "17.2.3", - "resolved": "https://registry.npmjs.org/@angular/router/-/router-17.2.3.tgz", - "integrity": "sha512-8UPjMzI98xZ6cDNm0MzHd9hFq6aOQJGmgxKDUPIG2h74glRwwbiewpo5hPo2EGIF8BLvQmmAm9ytr5zesHu0cg==", + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/@angular/router/-/router-18.2.0.tgz", + "integrity": "sha512-6/462hvC3HSwbps8VItECHpkdkiFqRmTKdn1Trik+FjnvdupYrKB6kBsveM3eP+gZD4zyMBMKzBWB9N/xA1clw==", "dependencies": { "tslib": "^2.3.0" }, "engines": { - "node": "^18.13.0 || >=20.9.0" + "node": "^18.19.1 || ^20.11.1 || >=22.0.0" }, "peerDependencies": { - "@angular/common": "17.2.3", - "@angular/core": "17.2.3", - "@angular/platform-browser": "17.2.3", + "@angular/common": "18.2.0", + "@angular/core": "18.2.0", + "@angular/platform-browser": "18.2.0", "rxjs": "^6.5.3 || ^7.4.0" } }, - "node_modules/@aw-web-design/x-default-browser": { - "version": "1.4.126", - "resolved": "https://registry.npmjs.org/@aw-web-design/x-default-browser/-/x-default-browser-1.4.126.tgz", - "integrity": "sha512-Xk1sIhyNC/esHGGVjL/niHLowM0csl/kFO5uawBy4IrWwy0o1G8LGt3jP6nmWGz+USxeeqbihAmp/oVZju6wug==", - "dev": true, - "dependencies": { - "default-browser-id": "3.0.0" - }, - "bin": { - "x-default-browser": "bin/x-default-browser.js" - } - }, "node_modules/@azure/msal-angular": { - "version": "3.0.13", - "resolved": "https://registry.npmjs.org/@azure/msal-angular/-/msal-angular-3.0.13.tgz", - "integrity": "sha512-HqXwgERyQZ9yYjTeSsVX8aP9vEmRt6blYSMcAzUxaatFV8++T6m4ByBySZdSV2LLdEdCXtAeB1dEDT68rElo/Q==", + "version": "3.0.20", + "resolved": "https://registry.npmjs.org/@azure/msal-angular/-/msal-angular-3.0.20.tgz", + "integrity": "sha512-7pMPtQQjxnqeYrt2ziYQ7cO6K+EFeZfxjCR3NdLH/1702dkSvS7CB2ERjGQPUshmMfEiNRdw/kwqvN6rEB8Flg==", + "license": "MIT", "dependencies": { "tslib": "^2.3.0" }, "peerDependencies": { - "@azure/msal-browser": "^3.10.0", + "@azure/msal-browser": "^3.17.0", "rxjs": "^7.0.0" } }, "node_modules/@azure/msal-browser": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/@azure/msal-browser/-/msal-browser-3.10.0.tgz", - "integrity": "sha512-mnmi8dCXVNZI+AGRq0jKQ3YiodlIC4W9npr6FCB9WN6NQT+6rq+cIlxgUb//BjLyzKsnYo+i4LROGeMyU+6v1A==", + "version": "3.19.1", + "resolved": "https://registry.npmjs.org/@azure/msal-browser/-/msal-browser-3.19.1.tgz", + "integrity": "sha512-pqYP2gK0GCEa4OxtOqlS+EdFQqhXV6ZuESgSTYWq2ABXyxBVVdd5KNuqgR5SU0OwI2V1YWdFVvLDe1487dyQ0g==", + "license": "MIT", "dependencies": { - "@azure/msal-common": "14.7.1" + "@azure/msal-common": "14.13.1" }, "engines": { "node": ">=0.8.0" } }, "node_modules/@azure/msal-common": { - "version": "14.7.1", - "resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-14.7.1.tgz", - "integrity": "sha512-v96btzjM7KrAu4NSEdOkhQSTGOuNUIIsUdB8wlyB9cdgl5KqEKnTonHUZ8+khvZ6Ap542FCErbnTyDWl8lZ2rA==", + "version": "14.13.1", + "resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-14.13.1.tgz", + "integrity": "sha512-iUp3BYrsRZ4X3EiaZ2fDjNFjmtYMv9rEQd6c1op6ULn0HWk4ACvDmosL6NaBgWOhl1BAblIbd9vmB5/ilF8d4A==", + "license": "MIT", "engines": { "node": ">=0.8.0" } }, "node_modules/@babel/code-frame": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.23.5.tgz", - "integrity": "sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.7.tgz", + "integrity": "sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==", "dev": true, "dependencies": { - "@babel/highlight": "^7.23.4", - "chalk": "^2.4.2" + "@babel/highlight": "^7.24.7", + "picocolors": "^1.0.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/compat-data": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.23.5.tgz", - "integrity": "sha512-uU27kfDRlhfKl+w1U6vp16IuvSLtjAxdArVXPa9BvLkrr7CYIsxH5adpHObeAGY/41+syctUWOZ140a2Rvkgjw==", + "version": "7.25.2", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.25.2.tgz", + "integrity": "sha512-bYcppcpKBvX4znYaPEeFau03bp89ShqNMLs+rmdptMw+heSZh9+z84d2YG+K7cYLbWwzdjtDoW/uqZmPjulClQ==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/core": { - "version": "7.24.0", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.0.tgz", - "integrity": "sha512-fQfkg0Gjkza3nf0c7/w6Xf34BW4YvzNfACRLmmb7XRLa6XHdR+K9AlJlxneFfWYf6uhOzuzZVTjF/8KfndZANw==", + "version": "7.25.2", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.25.2.tgz", + "integrity": "sha512-BBt3opiCOxUr9euZ5/ro/Xv8/V7yJ5bjYMqG/C1YAo8MIKAnumZalCN+msbci3Pigy4lIQfPUpfMM27HMGaYEA==", "dev": true, "dependencies": { "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.23.5", - "@babel/generator": "^7.23.6", - "@babel/helper-compilation-targets": "^7.23.6", - "@babel/helper-module-transforms": "^7.23.3", - "@babel/helpers": "^7.24.0", - "@babel/parser": "^7.24.0", - "@babel/template": "^7.24.0", - "@babel/traverse": "^7.24.0", - "@babel/types": "^7.24.0", + "@babel/code-frame": "^7.24.7", + "@babel/generator": "^7.25.0", + "@babel/helper-compilation-targets": "^7.25.2", + "@babel/helper-module-transforms": "^7.25.2", + "@babel/helpers": "^7.25.0", + "@babel/parser": "^7.25.0", + "@babel/template": "^7.25.0", + "@babel/traverse": "^7.25.2", + "@babel/types": "^7.25.2", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -965,14 +752,14 @@ } }, "node_modules/@babel/generator": { - "version": "7.23.6", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.6.tgz", - "integrity": "sha512-qrSfCYxYQB5owCmGLbl8XRpX1ytXlpueOb0N0UmQwA073KZxejgQTzAmJezxvpwQD9uGtK2shHdi55QT+MbjIw==", + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.25.0.tgz", + "integrity": "sha512-3LEEcj3PVW8pW2R1SR1M89g/qrYk/m/mB/tLqn7dn4sbBUQyTqnlod+II2U4dqiGtUmkcnAmkMDralTFZttRiw==", "dev": true, "dependencies": { - "@babel/types": "^7.23.6", - "@jridgewell/gen-mapping": "^0.3.2", - "@jridgewell/trace-mapping": "^0.3.17", + "@babel/types": "^7.25.0", + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25", "jsesc": "^2.5.1" }, "engines": { @@ -980,38 +767,39 @@ } }, "node_modules/@babel/helper-annotate-as-pure": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.22.5.tgz", - "integrity": "sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.24.7.tgz", + "integrity": "sha512-BaDeOonYvhdKw+JoMVkAixAAJzG2jVPIwWoKBPdYuY9b452e2rPuI9QPYh3KpofZ3pW2akOmwZLOiOsHMiqRAg==", "dev": true, "dependencies": { - "@babel/types": "^7.22.5" + "@babel/types": "^7.24.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-builder-binary-assignment-operator-visitor": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.22.15.tgz", - "integrity": "sha512-QkBXwGgaoC2GtGZRoma6kv7Szfv06khvhFav67ZExau2RaXzy8MpHSMO2PNoP2XtmQphJQRHFfg77Bq731Yizw==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.24.7.tgz", + "integrity": "sha512-xZeCVVdwb4MsDBkkyZ64tReWYrLRHlMN72vP7Bdm3OUOuyFZExhsHUUnuWnm2/XOlAJzR0LfPpB56WXZn0X/lA==", "dev": true, "dependencies": { - "@babel/types": "^7.22.15" + "@babel/traverse": "^7.24.7", + "@babel/types": "^7.24.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.23.6", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.23.6.tgz", - "integrity": "sha512-9JB548GZoQVmzrFgp8o7KxdgkTGm6xs9DW0o/Pim72UDjzr5ObUQ6ZzYPqA+g9OTS2bBQoctLJrky0RDCAWRgQ==", + "version": "7.25.2", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.2.tgz", + "integrity": "sha512-U2U5LsSaZ7TAt3cfaymQ8WHh0pxvdHoEk6HVpaexxixjyEquMh0L0YNJNM6CTGKMXV1iksi0iZkGw4AcFkPaaw==", "dev": true, "dependencies": { - "@babel/compat-data": "^7.23.5", - "@babel/helper-validator-option": "^7.23.5", - "browserslist": "^4.22.2", + "@babel/compat-data": "^7.25.2", + "@babel/helper-validator-option": "^7.24.8", + "browserslist": "^4.23.1", "lru-cache": "^5.1.1", "semver": "^6.3.1" }, @@ -1029,19 +817,17 @@ } }, "node_modules/@babel/helper-create-class-features-plugin": { - "version": "7.24.0", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.24.0.tgz", - "integrity": "sha512-QAH+vfvts51BCsNZ2PhY6HAggnlS6omLLFTsIpeqZk/MmJ6cW7tgz5yRv0fMJThcr6FmbMrENh1RgrWPTYA76g==", - "dev": true, - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-function-name": "^7.23.0", - "@babel/helper-member-expression-to-functions": "^7.23.0", - "@babel/helper-optimise-call-expression": "^7.22.5", - "@babel/helper-replace-supers": "^7.22.20", - "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.6", + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.25.0.tgz", + "integrity": "sha512-GYM6BxeQsETc9mnct+nIIpf63SAyzvyYN7UB/IlTyd+MBg06afFGp0mIeUqGyWgS2mxad6vqbMrHVlaL3m70sQ==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.24.7", + "@babel/helper-member-expression-to-functions": "^7.24.8", + "@babel/helper-optimise-call-expression": "^7.24.7", + "@babel/helper-replace-supers": "^7.25.0", + "@babel/helper-skip-transparent-expression-wrappers": "^7.24.7", + "@babel/traverse": "^7.25.0", "semver": "^6.3.1" }, "engines": { @@ -1061,12 +847,12 @@ } }, "node_modules/@babel/helper-create-regexp-features-plugin": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.22.15.tgz", - "integrity": "sha512-29FkPLFjn4TPEa3RE7GpW+qbE8tlsu3jntNYNfcGsc49LphF1PQIiD+vMZ1z1xVOKt+93khA9tc2JBs3kBjA7w==", + "version": "7.25.2", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.25.2.tgz", + "integrity": "sha512-+wqVGP+DFmqwFD3EH6TMTfUNeqDehV3E/dl+Sd54eaXqm17tEUNbEIn4sVivVowbvUpOtIGxdo3GoXyDH9N/9g==", "dev": true, "dependencies": { - "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-annotate-as-pure": "^7.24.7", "regexpu-core": "^5.3.1", "semver": "^6.3.1" }, @@ -1087,9 +873,9 @@ } }, "node_modules/@babel/helper-define-polyfill-provider": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.5.0.tgz", - "integrity": "sha512-NovQquuQLAQ5HuyjCz7WQP9MjRj7dx++yspwiyUiGl9ZyadHRSql1HZh5ogRd8W8w6YM6EQ/NTB8rgjLt5W65Q==", + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.2.tgz", + "integrity": "sha512-LV76g+C502biUK6AyZ3LK10vDpDyCzZnhZFXkH1L75zHPj68+qc8Zfpx2th+gzwA2MzyK+1g/3EPl62yFnVttQ==", "dev": true, "dependencies": { "@babel/helper-compilation-targets": "^7.22.6", @@ -1102,75 +888,42 @@ "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, - "node_modules/@babel/helper-environment-visitor": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", - "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-function-name": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", - "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", - "dev": true, - "dependencies": { - "@babel/template": "^7.22.15", - "@babel/types": "^7.23.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-hoist-variables": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", - "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", - "dev": true, - "dependencies": { - "@babel/types": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/helper-member-expression-to-functions": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.23.0.tgz", - "integrity": "sha512-6gfrPwh7OuT6gZyJZvd6WbTfrqAo7vm4xCzAXOusKqq/vWdKXphTpj5klHKNmRUU6/QRGlBsyU9mAIPaWHlqJA==", + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.24.8.tgz", + "integrity": "sha512-LABppdt+Lp/RlBxqrh4qgf1oEH/WxdzQNDJIu5gC/W1GyvPVrOBiItmmM8wan2fm4oYqFuFfkXmlGpLQhPY8CA==", "dev": true, "dependencies": { - "@babel/types": "^7.23.0" + "@babel/traverse": "^7.24.8", + "@babel/types": "^7.24.8" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-imports": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz", - "integrity": "sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.24.7.tgz", + "integrity": "sha512-8AyH3C+74cgCVVXow/myrynrAGv+nTVg5vKu2nZph9x7RcRwzmh0VFallJuFTZ9mx6u4eSdXZfcOzSqTUm0HCA==", "dev": true, "dependencies": { - "@babel/types": "^7.22.15" + "@babel/traverse": "^7.24.7", + "@babel/types": "^7.24.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.23.3.tgz", - "integrity": "sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==", + "version": "7.25.2", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.25.2.tgz", + "integrity": "sha512-BjyRAbix6j/wv83ftcVJmBt72QtHI56C7JXZoG2xATiLpmoC7dpd8WnkikExHDVPpi/3qCmO6WY1EaXOluiecQ==", "dev": true, "dependencies": { - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-module-imports": "^7.22.15", - "@babel/helper-simple-access": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/helper-validator-identifier": "^7.22.20" + "@babel/helper-module-imports": "^7.24.7", + "@babel/helper-simple-access": "^7.24.7", + "@babel/helper-validator-identifier": "^7.24.7", + "@babel/traverse": "^7.25.2" }, "engines": { "node": ">=6.9.0" @@ -1180,35 +933,35 @@ } }, "node_modules/@babel/helper-optimise-call-expression": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.22.5.tgz", - "integrity": "sha512-HBwaojN0xFRx4yIvpwGqxiV2tUfl7401jlok564NgB9EHS1y6QT17FmKWm4ztqjeVdXLuC4fSvHc5ePpQjoTbw==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.24.7.tgz", + "integrity": "sha512-jKiTsW2xmWwxT1ixIdfXUZp+P5yURx2suzLZr5Hi64rURpDYdMW0pv+Uf17EYk2Rd428Lx4tLsnjGJzYKDM/6A==", "dev": true, "dependencies": { - "@babel/types": "^7.22.5" + "@babel/types": "^7.24.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-plugin-utils": { - "version": "7.24.0", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.0.tgz", - "integrity": "sha512-9cUznXMG0+FxRuJfvL82QlTqIzhVW9sL0KjMPHhAOOvpQGL8QtdxnBKILjBqxlHyliz0yCa1G903ZXI/FuHy2w==", + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.8.tgz", + "integrity": "sha512-FFWx5142D8h2Mgr/iPVGH5G7w6jDn4jUSpZTyDnQO0Yn7Ks2Kuz6Pci8H6MPCoUJegd/UZQ3tAvfLCxQSnWWwg==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-remap-async-to-generator": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.22.20.tgz", - "integrity": "sha512-pBGyV4uBqOns+0UvhsTO8qgl8hO89PmiDYv+/COyp1aeMcmfrfruz+/nCMFiYyFF/Knn0yfrC85ZzNFjembFTw==", + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.25.0.tgz", + "integrity": "sha512-NhavI2eWEIz/H9dbrG0TuOicDhNexze43i5z7lEqwYm0WEZVTwnPpA0EafUTP7+6/W79HWIP2cTe3Z5NiSTVpw==", "dev": true, "dependencies": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-wrap-function": "^7.22.20" + "@babel/helper-annotate-as-pure": "^7.24.7", + "@babel/helper-wrap-function": "^7.25.0", + "@babel/traverse": "^7.25.0" }, "engines": { "node": ">=6.9.0" @@ -1218,14 +971,14 @@ } }, "node_modules/@babel/helper-replace-supers": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.22.20.tgz", - "integrity": "sha512-qsW0In3dbwQUbK8kejJ4R7IHVGwHJlV6lpG6UA7a9hSa2YEiAib+N1T2kr6PEeUT+Fl7najmSOS6SmAwCHK6Tw==", + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.25.0.tgz", + "integrity": "sha512-q688zIvQVYtZu+i2PsdIu/uWGRpfxzr5WESsfpShfZECkO+d2o+WROWezCi/Q6kJ0tfPa5+pUGUlfx2HhrA3Bg==", "dev": true, "dependencies": { - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-member-expression-to-functions": "^7.22.15", - "@babel/helper-optimise-call-expression": "^7.22.5" + "@babel/helper-member-expression-to-functions": "^7.24.8", + "@babel/helper-optimise-call-expression": "^7.24.7", + "@babel/traverse": "^7.25.0" }, "engines": { "node": ">=6.9.0" @@ -1235,115 +988,120 @@ } }, "node_modules/@babel/helper-simple-access": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz", - "integrity": "sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.24.7.tgz", + "integrity": "sha512-zBAIvbCMh5Ts+b86r/CjU+4XGYIs+R1j951gxI3KmmxBMhCg4oQMsv6ZXQ64XOm/cvzfU1FmoCyt6+owc5QMYg==", "dev": true, "dependencies": { - "@babel/types": "^7.22.5" + "@babel/traverse": "^7.24.7", + "@babel/types": "^7.24.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-skip-transparent-expression-wrappers": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.22.5.tgz", - "integrity": "sha512-tK14r66JZKiC43p8Ki33yLBVJKlQDFoA8GYN67lWCDCqoL6EMMSuM9b+Iff2jHaM/RRFYl7K+iiru7hbRqNx8Q==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.24.7.tgz", + "integrity": "sha512-IO+DLT3LQUElMbpzlatRASEyQtfhSE0+m465v++3jyyXeBTBUjtVZg28/gHeV5mrTJqvEKhKroBGAvhW+qPHiQ==", "dev": true, "dependencies": { - "@babel/types": "^7.22.5" + "@babel/traverse": "^7.24.7", + "@babel/types": "^7.24.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-split-export-declaration": { - "version": "7.22.6", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", - "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.24.7.tgz", + "integrity": "sha512-oy5V7pD+UvfkEATUKvIjvIAH/xCzfsFVw7ygW2SI6NClZzquT+mwdTfgfdbUiceh6iQO0CHtCPsyze/MZ2YbAA==", "dev": true, "dependencies": { - "@babel/types": "^7.22.5" + "@babel/types": "^7.24.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-string-parser": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.23.4.tgz", - "integrity": "sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==", + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.8.tgz", + "integrity": "sha512-pO9KhhRcuUyGnJWwyEgnRJTSIZHiT+vMD0kPeD+so0l7mxkMT19g3pjY9GTnHySck/hDzq+dtW/4VgnMkippsQ==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", - "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.7.tgz", + "integrity": "sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-option": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.23.5.tgz", - "integrity": "sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw==", + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.24.8.tgz", + "integrity": "sha512-xb8t9tD1MHLungh/AIoWYN+gVHaB9kwlu8gffXGSt3FFEIT7RjS+xWbc2vUD1UTZdIpKj/ab3rdqJ7ufngyi2Q==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-wrap-function": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.22.20.tgz", - "integrity": "sha512-pms/UwkOpnQe/PDAEdV/d7dVCoBbB+R4FvYoHGZz+4VPcg7RtYy2KP7S2lbuWM6FCSgob5wshfGESbC/hzNXZw==", + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.25.0.tgz", + "integrity": "sha512-s6Q1ebqutSiZnEjaofc/UKDyC4SbzV5n5SrA2Gq8UawLycr3i04f1dX4OzoQVnexm6aOCh37SQNYlJ/8Ku+PMQ==", "dev": true, "dependencies": { - "@babel/helper-function-name": "^7.22.5", - "@babel/template": "^7.22.15", - "@babel/types": "^7.22.19" + "@babel/template": "^7.25.0", + "@babel/traverse": "^7.25.0", + "@babel/types": "^7.25.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helpers": { - "version": "7.24.0", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.24.0.tgz", - "integrity": "sha512-ulDZdc0Aj5uLc5nETsa7EPx2L7rM0YJM8r7ck7U73AXi7qOV44IHHRAYZHY6iU1rr3C5N4NtTmMRUJP6kwCWeA==", + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.25.0.tgz", + "integrity": "sha512-MjgLZ42aCm0oGjJj8CtSM3DB8NOOf8h2l7DCTePJs29u+v7yO/RBX9nShlKMgFnRks/Q4tBAe7Hxnov9VkGwLw==", "dev": true, "dependencies": { - "@babel/template": "^7.24.0", - "@babel/traverse": "^7.24.0", - "@babel/types": "^7.24.0" + "@babel/template": "^7.25.0", + "@babel/types": "^7.25.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/highlight": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.23.4.tgz", - "integrity": "sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.7.tgz", + "integrity": "sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==", "dev": true, "dependencies": { - "@babel/helper-validator-identifier": "^7.22.20", + "@babel/helper-validator-identifier": "^7.24.7", "chalk": "^2.4.2", - "js-tokens": "^4.0.0" + "js-tokens": "^4.0.0", + "picocolors": "^1.0.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/parser": { - "version": "7.24.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.0.tgz", - "integrity": "sha512-QuP/FxEAzMSjXygs8v4N9dvdXzEHN4W1oF3PxuWAtPo08UdM17u89RDMgjLn/mlc56iM0HlLmVkO/wgR+rDgHg==", + "version": "7.25.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.25.3.tgz", + "integrity": "sha512-iLTJKDbJ4hMvFPgQwwsVoxtHyWpKKPBrxkANrSYewDPaPpT5py5yeVkgPIJ7XYXhndxJpaA3PyALSXQ7u8e/Dw==", "dev": true, + "dependencies": { + "@babel/types": "^7.25.2" + }, "bin": { "parser": "bin/babel-parser.js" }, @@ -1351,13 +1109,44 @@ "node": ">=6.0.0" } }, + "node_modules/@babel/plugin-bugfix-firefox-class-in-computed-class-key": { + "version": "7.25.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.25.3.tgz", + "integrity": "sha512-wUrcsxZg6rqBXG05HG1FPYgsP6EvwF4WpBbxIpWIIYnH8wG0gzx3yZY3dtEHas4sTAOGkbTsc9EGPxwff8lRoA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.8", + "@babel/traverse": "^7.25.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-safari-class-field-initializer-scope": { + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-class-field-initializer-scope/-/plugin-bugfix-safari-class-field-initializer-scope-7.25.0.tgz", + "integrity": "sha512-Bm4bH2qsX880b/3ziJ8KD711LT7z4u8CFudmjqle65AZj/HNUFhEf90dqYv6O86buWvSBmeQDjv0Tn2aF/bIBA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.8" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.23.3.tgz", - "integrity": "sha512-iRkKcCqb7iGnq9+3G6rZ+Ciz5VywC4XNRHe57lKM+jOeYAoR0lVqdeeDRfh0tQcTfw/+vBhHn926FmQhLtlFLQ==", + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.25.0.tgz", + "integrity": "sha512-lXwdNZtTmeVOOFtwM/WDe7yg1PL8sYhRk/XH0FzbR2HDQ0xC+EnQ/JHeoMYSavtU115tnUk0q9CDyq8si+LMAA==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.24.8" }, "engines": { "node": ">=6.9.0" @@ -1367,14 +1156,14 @@ } }, "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.23.3.tgz", - "integrity": "sha512-WwlxbfMNdVEpQjZmK5mhm7oSwD3dS6eU+Iwsi4Knl9wAletWem7kaRsGOG+8UEbRyqxY4SS5zvtfXwX+jMxUwQ==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.24.7.tgz", + "integrity": "sha512-+izXIbke1T33mY4MSNnrqhPXDz01WYhEf3yF5NbnUtkiNnm+XBZJl3kNfoK6NKmYlz/D07+l2GWVK/QfDkNCuQ==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", - "@babel/plugin-transform-optional-chaining": "^7.23.3" + "@babel/helper-plugin-utils": "^7.24.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.24.7", + "@babel/plugin-transform-optional-chaining": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -1384,13 +1173,13 @@ } }, "node_modules/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": { - "version": "7.23.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.23.7.tgz", - "integrity": "sha512-LlRT7HgaifEpQA1ZgLVOIJZZFVPWN5iReq/7/JixwBtwcoeVGDBD53ZV28rrsLYOZs1Y/EHhA8N/Z6aazHR8cw==", + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.25.0.tgz", + "integrity": "sha512-tggFrk1AIShG/RUQbEwt2Tr/E+ObkfwrPjR6BjbRvsx24+PSjK8zrq0GWPNCjo8qpRx4DuJzlcvWJqlm+0h3kw==", "dev": true, "dependencies": { - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.24.8", + "@babel/traverse": "^7.25.0" }, "engines": { "node": ">=6.9.0" @@ -1503,28 +1292,13 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-syntax-flow": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.23.3.tgz", - "integrity": "sha512-YZiAIpkJAwQXBJLIQbRFayR5c+gJ35Vcz3bg954k7cd73zqjvhacJuL9RbrzPz8qPmZdgqP6EUKwy0PCNhaaPA==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, "node_modules/@babel/plugin-syntax-import-assertions": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.23.3.tgz", - "integrity": "sha512-lPgDSU+SJLK3xmFDTV2ZRQAiM7UuUjGidwBywFavObCiZc1BeAAcMtHJKUya92hPHO+at63JJPLygilZard8jw==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.24.7.tgz", + "integrity": "sha512-Ec3NRUMoi8gskrkBe3fNmEQfxDvY8bgfQpz6jlk/41kX9eUjvpyqWU7PBP/pLAvMaSQjbMNKJmvX57jP+M6bPg==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -1534,12 +1308,12 @@ } }, "node_modules/@babel/plugin-syntax-import-attributes": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.23.3.tgz", - "integrity": "sha512-pawnE0P9g10xgoP7yKr6CK63K2FMsTE+FZidZO/1PwRdzmAPVs+HS1mAURUsgaoxammTJvULUdIkEK0gOcU2tA==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.24.7.tgz", + "integrity": "sha512-hbX+lKKeUMGihnK8nvKqmXBInriT3GVjzXKFriV3YC6APGxMbP8RZNFwy91+hocLXq90Mta+HshoB31802bb8A==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -1721,12 +1495,12 @@ } }, "node_modules/@babel/plugin-transform-arrow-functions": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.23.3.tgz", - "integrity": "sha512-NzQcQrzaQPkaEwoTm4Mhyl8jI1huEL/WWIEvudjTCMJ9aBZNpsJbMASx7EQECtQQPS/DcnFpo0FIh3LvEO9cxQ==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.24.7.tgz", + "integrity": "sha512-Dt9LQs6iEY++gXUwY03DNFat5C2NbO48jj+j/bSAz6b3HgPs39qcPiYt77fDObIcFwj3/C2ICX9YMwGflUoSHQ==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -1736,15 +1510,15 @@ } }, "node_modules/@babel/plugin-transform-async-generator-functions": { - "version": "7.23.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.23.9.tgz", - "integrity": "sha512-8Q3veQEDGe14dTYuwagbRtwxQDnytyg1JFu4/HwEMETeofocrB0U0ejBJIXoeG/t2oXZ8kzCyI0ZZfbT80VFNQ==", + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.25.0.tgz", + "integrity": "sha512-uaIi2FdqzjpAMvVqvB51S42oC2JEVgh0LDsGfZVDysWE8LrJtQC2jvKmOqEYThKyB7bDEb7BP1GYWDm7tABA0Q==", "dev": true, "dependencies": { - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-remap-async-to-generator": "^7.22.20", - "@babel/plugin-syntax-async-generators": "^7.8.4" + "@babel/helper-plugin-utils": "^7.24.8", + "@babel/helper-remap-async-to-generator": "^7.25.0", + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/traverse": "^7.25.0" }, "engines": { "node": ">=6.9.0" @@ -1754,14 +1528,14 @@ } }, "node_modules/@babel/plugin-transform-async-to-generator": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.23.3.tgz", - "integrity": "sha512-A7LFsKi4U4fomjqXJlZg/u0ft/n8/7n7lpffUP/ZULx/DtV9SGlNKZolHH6PE8Xl1ngCc0M11OaeZptXVkfKSw==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.24.7.tgz", + "integrity": "sha512-SQY01PcJfmQ+4Ash7NE+rpbLFbmqA2GPIgqzxfFTL4t1FKRq4zTms/7htKpoCUI9OcFYgzqfmCdH53s6/jn5fA==", "dev": true, "dependencies": { - "@babel/helper-module-imports": "^7.22.15", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-remap-async-to-generator": "^7.22.20" + "@babel/helper-module-imports": "^7.24.7", + "@babel/helper-plugin-utils": "^7.24.7", + "@babel/helper-remap-async-to-generator": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -1771,12 +1545,12 @@ } }, "node_modules/@babel/plugin-transform-block-scoped-functions": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.23.3.tgz", - "integrity": "sha512-vI+0sIaPIO6CNuM9Kk5VmXcMVRiOpDh7w2zZt9GXzmE/9KD70CUEVhvPR/etAeNK/FAEkhxQtXOzVF3EuRL41A==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.24.7.tgz", + "integrity": "sha512-yO7RAz6EsVQDaBH18IDJcMB1HnrUn2FJ/Jslc/WtPPWcjhpUJXU/rjbwmluzp7v/ZzWcEhTMXELnnsz8djWDwQ==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -1786,12 +1560,12 @@ } }, "node_modules/@babel/plugin-transform-block-scoping": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.23.4.tgz", - "integrity": "sha512-0QqbP6B6HOh7/8iNR4CQU2Th/bbRtBp4KS9vcaZd1fZ0wSh5Fyssg0UCIHwxh+ka+pNDREbVLQnHCMHKZfPwfw==", + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.25.0.tgz", + "integrity": "sha512-yBQjYoOjXlFv9nlXb3f1casSHOZkWr29NX+zChVanLg5Nc157CrbEX9D7hxxtTpuFy7Q0YzmmWfJxzvps4kXrQ==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.24.8" }, "engines": { "node": ">=6.9.0" @@ -1801,13 +1575,13 @@ } }, "node_modules/@babel/plugin-transform-class-properties": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.23.3.tgz", - "integrity": "sha512-uM+AN8yCIjDPccsKGlw271xjJtGii+xQIF/uMPS8H15L12jZTsLfF4o5vNO7d/oUguOyfdikHGc/yi9ge4SGIg==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.24.7.tgz", + "integrity": "sha512-vKbfawVYayKcSeSR5YYzzyXvsDFWU2mD8U5TFeXtbCPLFUqe7GyCgvO6XDHzje862ODrOwy6WCPmKeWHbCFJ4w==", "dev": true, "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.22.15", - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-create-class-features-plugin": "^7.24.7", + "@babel/helper-plugin-utils": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -1817,13 +1591,13 @@ } }, "node_modules/@babel/plugin-transform-class-static-block": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.23.4.tgz", - "integrity": "sha512-nsWu/1M+ggti1SOALj3hfx5FXzAY06fwPJsUZD4/A5e1bWi46VUIWtD+kOX6/IdhXGsXBWllLFDSnqSCdUNydQ==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.24.7.tgz", + "integrity": "sha512-HMXK3WbBPpZQufbMG4B46A90PkuuhN9vBCb5T8+VAHqvAqvcLi+2cKoukcpmUYkszLhScU3l1iudhrks3DggRQ==", "dev": true, "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.22.15", - "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-create-class-features-plugin": "^7.24.7", + "@babel/helper-plugin-utils": "^7.24.7", "@babel/plugin-syntax-class-static-block": "^7.14.5" }, "engines": { @@ -1834,18 +1608,16 @@ } }, "node_modules/@babel/plugin-transform-classes": { - "version": "7.23.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.23.8.tgz", - "integrity": "sha512-yAYslGsY1bX6Knmg46RjiCiNSwJKv2IUC8qOdYKqMMr0491SXFhcHqOdRDeCRohOOIzwN/90C6mQ9qAKgrP7dg==", + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.25.0.tgz", + "integrity": "sha512-xyi6qjr/fYU304fiRwFbekzkqVJZ6A7hOjWZd+89FVcBqPV3S9Wuozz82xdpLspckeaafntbzglaW4pqpzvtSw==", "dev": true, "dependencies": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-compilation-targets": "^7.23.6", - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-function-name": "^7.23.0", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-replace-supers": "^7.22.20", - "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/helper-annotate-as-pure": "^7.24.7", + "@babel/helper-compilation-targets": "^7.24.8", + "@babel/helper-plugin-utils": "^7.24.8", + "@babel/helper-replace-supers": "^7.25.0", + "@babel/traverse": "^7.25.0", "globals": "^11.1.0" }, "engines": { @@ -1856,13 +1628,13 @@ } }, "node_modules/@babel/plugin-transform-computed-properties": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.23.3.tgz", - "integrity": "sha512-dTj83UVTLw/+nbiHqQSFdwO9CbTtwq1DsDqm3CUEtDrZNET5rT5E6bIdTlOftDTDLMYxvxHNEYO4B9SLl8SLZw==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.24.7.tgz", + "integrity": "sha512-25cS7v+707Gu6Ds2oY6tCkUwsJ9YIDbggd9+cu9jzzDgiNq7hR/8dkzxWfKWnTic26vsI3EsCXNd4iEB6e8esQ==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/template": "^7.22.15" + "@babel/helper-plugin-utils": "^7.24.7", + "@babel/template": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -1872,12 +1644,12 @@ } }, "node_modules/@babel/plugin-transform-destructuring": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.23.3.tgz", - "integrity": "sha512-n225npDqjDIr967cMScVKHXJs7rout1q+tt50inyBCPkyZ8KxeI6d+GIbSBTT/w/9WdlWDOej3V9HE5Lgk57gw==", + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.24.8.tgz", + "integrity": "sha512-36e87mfY8TnRxc7yc6M9g9gOB7rKgSahqkIKwLpz4Ppk2+zC2Cy1is0uwtuSG6AE4zlTOUa+7JGz9jCJGLqQFQ==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.24.8" }, "engines": { "node": ">=6.9.0" @@ -1887,13 +1659,13 @@ } }, "node_modules/@babel/plugin-transform-dotall-regex": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.23.3.tgz", - "integrity": "sha512-vgnFYDHAKzFaTVp+mneDsIEbnJ2Np/9ng9iviHw3P/KVcgONxpNULEW/51Z/BaFojG2GI2GwwXck5uV1+1NOYQ==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.24.7.tgz", + "integrity": "sha512-ZOA3W+1RRTSWvyqcMJDLqbchh7U4NRGqwRfFSVbOLS/ePIP4vHB5e8T8eXcuqyN1QkgKyj5wuW0lcS85v4CrSw==", "dev": true, "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.22.15", - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-create-regexp-features-plugin": "^7.24.7", + "@babel/helper-plugin-utils": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -1903,12 +1675,12 @@ } }, "node_modules/@babel/plugin-transform-duplicate-keys": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.23.3.tgz", - "integrity": "sha512-RrqQ+BQmU3Oyav3J+7/myfvRCq7Tbz+kKLLshUmMwNlDHExbGL7ARhajvoBJEvc+fCguPPu887N+3RRXBVKZUA==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.24.7.tgz", + "integrity": "sha512-JdYfXyCRihAe46jUIliuL2/s0x0wObgwwiGxw/UbgJBr20gQBThrokO4nYKgWkD7uBaqM7+9x5TU7NkExZJyzw==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -1917,30 +1689,30 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-dynamic-import": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.23.4.tgz", - "integrity": "sha512-V6jIbLhdJK86MaLh4Jpghi8ho5fGzt3imHOBu/x0jlBaPYqDoWz4RDXjmMOfnh+JWNaQleEAByZLV0QzBT4YQQ==", + "node_modules/@babel/plugin-transform-duplicate-named-capturing-groups-regex": { + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.25.0.tgz", + "integrity": "sha512-YLpb4LlYSc3sCUa35un84poXoraOiQucUTTu8X1j18JV+gNa8E0nyUf/CjZ171IRGr4jEguF+vzJU66QZhn29g==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-dynamic-import": "^7.8.3" + "@babel/helper-create-regexp-features-plugin": "^7.25.0", + "@babel/helper-plugin-utils": "^7.24.8" }, "engines": { "node": ">=6.9.0" }, "peerDependencies": { - "@babel/core": "^7.0.0-0" + "@babel/core": "^7.0.0" } }, - "node_modules/@babel/plugin-transform-exponentiation-operator": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.23.3.tgz", - "integrity": "sha512-5fhCsl1odX96u7ILKHBj4/Y8vipoqwsJMh4csSA8qFfxrZDEA4Ssku2DyNvMJSmZNOEBT750LfFPbtrnTP90BQ==", + "node_modules/@babel/plugin-transform-dynamic-import": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.24.7.tgz", + "integrity": "sha512-sc3X26PhZQDb3JhORmakcbvkeInvxz+A8oda99lj7J60QRuPZvNAk9wQlTBS1ZynelDrDmTU4pw1tyc5d5ZMUg==", "dev": true, "dependencies": { - "@babel/helper-builder-binary-assignment-operator-visitor": "^7.22.15", - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.24.7", + "@babel/plugin-syntax-dynamic-import": "^7.8.3" }, "engines": { "node": ">=6.9.0" @@ -1949,14 +1721,14 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-export-namespace-from": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.23.4.tgz", - "integrity": "sha512-GzuSBcKkx62dGzZI1WVgTWvkkz84FZO5TC5T8dl/Tht/rAla6Dg/Mz9Yhypg+ezVACf/rgDuQt3kbWEv7LdUDQ==", + "node_modules/@babel/plugin-transform-exponentiation-operator": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.24.7.tgz", + "integrity": "sha512-Rqe/vSc9OYgDajNIK35u7ot+KeCoetqQYFXM4Epf7M7ez3lWlOjrDjrwMei6caCVhfdw+mIKD4cgdGNy5JQotQ==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-export-namespace-from": "^7.8.3" + "@babel/helper-builder-binary-assignment-operator-visitor": "^7.24.7", + "@babel/helper-plugin-utils": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -1965,14 +1737,14 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-flow-strip-types": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.23.3.tgz", - "integrity": "sha512-26/pQTf9nQSNVJCrLB1IkHUKyPxR+lMrH2QDPG89+Znu9rAMbtrybdbWeE9bb7gzjmE5iXHEY+e0HUwM6Co93Q==", + "node_modules/@babel/plugin-transform-export-namespace-from": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.24.7.tgz", + "integrity": "sha512-v0K9uNYsPL3oXZ/7F9NNIbAj2jv1whUEtyA6aujhekLs56R++JDQuzRcP2/z4WX5Vg/c5lE9uWZA0/iUoFhLTA==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-flow": "^7.23.3" + "@babel/helper-plugin-utils": "^7.24.7", + "@babel/plugin-syntax-export-namespace-from": "^7.8.3" }, "engines": { "node": ">=6.9.0" @@ -1982,13 +1754,13 @@ } }, "node_modules/@babel/plugin-transform-for-of": { - "version": "7.23.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.23.6.tgz", - "integrity": "sha512-aYH4ytZ0qSuBbpfhuofbg/e96oQ7U2w1Aw/UQmKT+1l39uEhUPoFS3fHevDc1G0OvewyDudfMKY1OulczHzWIw==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.24.7.tgz", + "integrity": "sha512-wo9ogrDG1ITTTBsy46oGiN1dS9A7MROBTcYsfS8DtsImMkHk9JXJ3EWQM6X2SUw4x80uGPlwj0o00Uoc6nEE3g==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5" + "@babel/helper-plugin-utils": "^7.24.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -1998,14 +1770,14 @@ } }, "node_modules/@babel/plugin-transform-function-name": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.23.3.tgz", - "integrity": "sha512-I1QXp1LxIvt8yLaib49dRW5Okt7Q4oaxao6tFVKS/anCdEOMtYwWVKoiOA1p34GOWIZjUK0E+zCp7+l1pfQyiw==", + "version": "7.25.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.25.1.tgz", + "integrity": "sha512-TVVJVdW9RKMNgJJlLtHsKDTydjZAbwIsn6ySBPQaEAUU5+gVvlJt/9nRmqVbsV/IBanRjzWoaAQKLoamWVOUuA==", "dev": true, "dependencies": { - "@babel/helper-compilation-targets": "^7.22.15", - "@babel/helper-function-name": "^7.23.0", - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-compilation-targets": "^7.24.8", + "@babel/helper-plugin-utils": "^7.24.8", + "@babel/traverse": "^7.25.1" }, "engines": { "node": ">=6.9.0" @@ -2015,12 +1787,12 @@ } }, "node_modules/@babel/plugin-transform-json-strings": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.23.4.tgz", - "integrity": "sha512-81nTOqM1dMwZ/aRXQ59zVubN9wHGqk6UtqRK+/q+ciXmRy8fSolhGVvG09HHRGo4l6fr/c4ZhXUQH0uFW7PZbg==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.24.7.tgz", + "integrity": "sha512-2yFnBGDvRuxAaE/f0vfBKvtnvvqU8tGpMHqMNpTN2oWMKIR3NqFkjaAgGwawhqK/pIN2T3XdjGPdaG0vDhOBGw==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-plugin-utils": "^7.24.7", "@babel/plugin-syntax-json-strings": "^7.8.3" }, "engines": { @@ -2031,12 +1803,12 @@ } }, "node_modules/@babel/plugin-transform-literals": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.23.3.tgz", - "integrity": "sha512-wZ0PIXRxnwZvl9AYpqNUxpZ5BiTGrYt7kueGQ+N5FiQ7RCOD4cm8iShd6S6ggfVIWaJf2EMk8eRzAh52RfP4rQ==", + "version": "7.25.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.25.2.tgz", + "integrity": "sha512-HQI+HcTbm9ur3Z2DkO+jgESMAMcYLuN/A7NRw9juzxAezN9AvqvUTnpKP/9kkYANz6u7dFlAyOu44ejuGySlfw==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.24.8" }, "engines": { "node": ">=6.9.0" @@ -2046,12 +1818,12 @@ } }, "node_modules/@babel/plugin-transform-logical-assignment-operators": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.23.4.tgz", - "integrity": "sha512-Mc/ALf1rmZTP4JKKEhUwiORU+vcfarFVLfcFiolKUo6sewoxSEgl36ak5t+4WamRsNr6nzjZXQjM35WsU+9vbg==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.24.7.tgz", + "integrity": "sha512-4D2tpwlQ1odXmTEIFWy9ELJcZHqrStlzK/dAOWYyxX3zT0iXQB6banjgeOJQXzEc4S0E0a5A+hahxPaEFYftsw==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-plugin-utils": "^7.24.7", "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" }, "engines": { @@ -2062,12 +1834,12 @@ } }, "node_modules/@babel/plugin-transform-member-expression-literals": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.23.3.tgz", - "integrity": "sha512-sC3LdDBDi5x96LA+Ytekz2ZPk8i/Ck+DEuDbRAll5rknJ5XRTSaPKEYwomLcs1AA8wg9b3KjIQRsnApj+q51Ag==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.24.7.tgz", + "integrity": "sha512-T/hRC1uqrzXMKLQ6UCwMT85S3EvqaBXDGf0FaMf4446Qx9vKwlghvee0+uuZcDUCZU5RuNi4781UQ7R308zzBw==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -2077,13 +1849,13 @@ } }, "node_modules/@babel/plugin-transform-modules-amd": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.23.3.tgz", - "integrity": "sha512-vJYQGxeKM4t8hYCKVBlZX/gtIY2I7mRGFNcm85sgXGMTBcoV3QdVtdpbcWEbzbfUIUZKwvgFT82mRvaQIebZzw==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.24.7.tgz", + "integrity": "sha512-9+pB1qxV3vs/8Hdmz/CulFB8w2tuu6EB94JZFsjdqxQokwGa9Unap7Bo2gGBGIvPmDIVvQrom7r5m/TCDMURhg==", "dev": true, "dependencies": { - "@babel/helper-module-transforms": "^7.23.3", - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-module-transforms": "^7.24.7", + "@babel/helper-plugin-utils": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -2093,14 +1865,14 @@ } }, "node_modules/@babel/plugin-transform-modules-commonjs": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.23.3.tgz", - "integrity": "sha512-aVS0F65LKsdNOtcz6FRCpE4OgsP2OFnW46qNxNIX9h3wuzaNcSQsJysuMwqSibC98HPrf2vCgtxKNwS0DAlgcA==", + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.24.8.tgz", + "integrity": "sha512-WHsk9H8XxRs3JXKWFiqtQebdh9b/pTk4EgueygFzYlTKAg0Ud985mSevdNjdXdFBATSKVJGQXP1tv6aGbssLKA==", "dev": true, "dependencies": { - "@babel/helper-module-transforms": "^7.23.3", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-simple-access": "^7.22.5" + "@babel/helper-module-transforms": "^7.24.8", + "@babel/helper-plugin-utils": "^7.24.8", + "@babel/helper-simple-access": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -2110,15 +1882,15 @@ } }, "node_modules/@babel/plugin-transform-modules-systemjs": { - "version": "7.23.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.23.9.tgz", - "integrity": "sha512-KDlPRM6sLo4o1FkiSlXoAa8edLXFsKKIda779fbLrvmeuc3itnjCtaO6RrtoaANsIJANj+Vk1zqbZIMhkCAHVw==", + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.25.0.tgz", + "integrity": "sha512-YPJfjQPDXxyQWg/0+jHKj1llnY5f/R6a0p/vP4lPymxLu7Lvl4k2WMitqi08yxwQcCVUUdG9LCUj4TNEgAp3Jw==", "dev": true, "dependencies": { - "@babel/helper-hoist-variables": "^7.22.5", - "@babel/helper-module-transforms": "^7.23.3", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-validator-identifier": "^7.22.20" + "@babel/helper-module-transforms": "^7.25.0", + "@babel/helper-plugin-utils": "^7.24.8", + "@babel/helper-validator-identifier": "^7.24.7", + "@babel/traverse": "^7.25.0" }, "engines": { "node": ">=6.9.0" @@ -2128,13 +1900,13 @@ } }, "node_modules/@babel/plugin-transform-modules-umd": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.23.3.tgz", - "integrity": "sha512-zHsy9iXX2nIsCBFPud3jKn1IRPWg3Ing1qOZgeKV39m1ZgIdpJqvlWVeiHBZC6ITRG0MfskhYe9cLgntfSFPIg==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.24.7.tgz", + "integrity": "sha512-3aytQvqJ/h9z4g8AsKPLvD4Zqi2qT+L3j7XoFFu1XBlZWEl2/1kWnhmAbxpLgPrHSY0M6UA02jyTiwUVtiKR6A==", "dev": true, "dependencies": { - "@babel/helper-module-transforms": "^7.23.3", - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-module-transforms": "^7.24.7", + "@babel/helper-plugin-utils": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -2144,13 +1916,13 @@ } }, "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.22.5.tgz", - "integrity": "sha512-YgLLKmS3aUBhHaxp5hi1WJTgOUb/NCuDHzGT9z9WTt3YG+CPRhJs6nprbStx6DnWM4dh6gt7SU3sZodbZ08adQ==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.24.7.tgz", + "integrity": "sha512-/jr7h/EWeJtk1U/uz2jlsCioHkZk1JJZVcc8oQsJ1dUlaJD83f4/6Zeh2aHt9BIFokHIsSeDfhUmju0+1GPd6g==", "dev": true, "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-create-regexp-features-plugin": "^7.24.7", + "@babel/helper-plugin-utils": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -2160,12 +1932,12 @@ } }, "node_modules/@babel/plugin-transform-new-target": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.23.3.tgz", - "integrity": "sha512-YJ3xKqtJMAT5/TIZnpAR3I+K+WaDowYbN3xyxI8zxx/Gsypwf9B9h0VB+1Nh6ACAAPRS5NSRje0uVv5i79HYGQ==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.24.7.tgz", + "integrity": "sha512-RNKwfRIXg4Ls/8mMTza5oPF5RkOW8Wy/WgMAp1/F1yZ8mMbtwXW+HDoJiOsagWrAhI5f57Vncrmr9XeT4CVapA==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -2175,12 +1947,12 @@ } }, "node_modules/@babel/plugin-transform-nullish-coalescing-operator": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.23.4.tgz", - "integrity": "sha512-jHE9EVVqHKAQx+VePv5LLGHjmHSJR76vawFPTdlxR/LVJPfOEGxREQwQfjuZEOPTwG92X3LINSh3M40Rv4zpVA==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.24.7.tgz", + "integrity": "sha512-Ts7xQVk1OEocqzm8rHMXHlxvsfZ0cEF2yomUqpKENHWMF4zKk175Y4q8H5knJes6PgYad50uuRmt3UJuhBw8pQ==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-plugin-utils": "^7.24.7", "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" }, "engines": { @@ -2191,12 +1963,12 @@ } }, "node_modules/@babel/plugin-transform-numeric-separator": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.23.4.tgz", - "integrity": "sha512-mps6auzgwjRrwKEZA05cOwuDc9FAzoyFS4ZsG/8F43bTLf/TgkJg7QXOrPO1JO599iA3qgK9MXdMGOEC8O1h6Q==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.24.7.tgz", + "integrity": "sha512-e6q1TiVUzvH9KRvicuxdBTUj4AdKSRwzIyFFnfnezpCfP2/7Qmbb8qbU2j7GODbl4JMkblitCQjKYUaX/qkkwA==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-plugin-utils": "^7.24.7", "@babel/plugin-syntax-numeric-separator": "^7.10.4" }, "engines": { @@ -2207,16 +1979,15 @@ } }, "node_modules/@babel/plugin-transform-object-rest-spread": { - "version": "7.24.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.24.0.tgz", - "integrity": "sha512-y/yKMm7buHpFFXfxVFS4Vk1ToRJDilIa6fKRioB9Vjichv58TDGXTvqV0dN7plobAmTW5eSEGXDngE+Mm+uO+w==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.24.7.tgz", + "integrity": "sha512-4QrHAr0aXQCEFni2q4DqKLD31n2DL+RxcwnNjDFkSG0eNQ/xCavnRkfCUjsyqGC2OviNJvZOF/mQqZBw7i2C5Q==", "dev": true, "dependencies": { - "@babel/compat-data": "^7.23.5", - "@babel/helper-compilation-targets": "^7.23.6", - "@babel/helper-plugin-utils": "^7.24.0", + "@babel/helper-compilation-targets": "^7.24.7", + "@babel/helper-plugin-utils": "^7.24.7", "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-transform-parameters": "^7.23.3" + "@babel/plugin-transform-parameters": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -2226,13 +1997,13 @@ } }, "node_modules/@babel/plugin-transform-object-super": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.23.3.tgz", - "integrity": "sha512-BwQ8q0x2JG+3lxCVFohg+KbQM7plfpBwThdW9A6TMtWwLsbDA01Ek2Zb/AgDN39BiZsExm4qrXxjk+P1/fzGrA==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.24.7.tgz", + "integrity": "sha512-A/vVLwN6lBrMFmMDmPPz0jnE6ZGx7Jq7d6sT/Ev4H65RER6pZ+kczlf1DthF5N0qaPHBsI7UXiE8Zy66nmAovg==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-replace-supers": "^7.22.20" + "@babel/helper-plugin-utils": "^7.24.7", + "@babel/helper-replace-supers": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -2242,12 +2013,12 @@ } }, "node_modules/@babel/plugin-transform-optional-catch-binding": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.23.4.tgz", - "integrity": "sha512-XIq8t0rJPHf6Wvmbn9nFxU6ao4c7WhghTR5WyV8SrJfUFzyxhCm4nhC+iAp3HFhbAKLfYpgzhJ6t4XCtVwqO5A==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.24.7.tgz", + "integrity": "sha512-uLEndKqP5BfBbC/5jTwPxLh9kqPWWgzN/f8w6UwAIirAEqiIVJWWY312X72Eub09g5KF9+Zn7+hT7sDxmhRuKA==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-plugin-utils": "^7.24.7", "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" }, "engines": { @@ -2258,13 +2029,13 @@ } }, "node_modules/@babel/plugin-transform-optional-chaining": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.23.4.tgz", - "integrity": "sha512-ZU8y5zWOfjM5vZ+asjgAPwDaBjJzgufjES89Rs4Lpq63O300R/kOz30WCLo6BxxX6QVEilwSlpClnG5cZaikTA==", + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.24.8.tgz", + "integrity": "sha512-5cTOLSMs9eypEy8JUVvIKOu6NgvbJMnpG62VpIHrTmROdQ+L5mDAaI40g25k5vXti55JWNX5jCkq3HZxXBQANw==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", + "@babel/helper-plugin-utils": "^7.24.8", + "@babel/helper-skip-transparent-expression-wrappers": "^7.24.7", "@babel/plugin-syntax-optional-chaining": "^7.8.3" }, "engines": { @@ -2275,12 +2046,12 @@ } }, "node_modules/@babel/plugin-transform-parameters": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.23.3.tgz", - "integrity": "sha512-09lMt6UsUb3/34BbECKVbVwrT9bO6lILWln237z7sLaWnMsTi7Yc9fhX5DLpkJzAGfaReXI22wP41SZmnAA3Vw==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.24.7.tgz", + "integrity": "sha512-yGWW5Rr+sQOhK0Ot8hjDJuxU3XLRQGflvT4lhlSY0DFvdb3TwKaY26CJzHtYllU0vT9j58hc37ndFPsqT1SrzA==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -2290,13 +2061,13 @@ } }, "node_modules/@babel/plugin-transform-private-methods": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.23.3.tgz", - "integrity": "sha512-UzqRcRtWsDMTLrRWFvUBDwmw06tCQH9Rl1uAjfh6ijMSmGYQ+fpdB+cnqRC8EMh5tuuxSv0/TejGL+7vyj+50g==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.24.7.tgz", + "integrity": "sha512-COTCOkG2hn4JKGEKBADkA8WNb35TGkkRbI5iT845dB+NyqgO8Hn+ajPbSnIQznneJTa3d30scb6iz/DhH8GsJQ==", "dev": true, "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.22.15", - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-create-class-features-plugin": "^7.24.7", + "@babel/helper-plugin-utils": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -2306,14 +2077,14 @@ } }, "node_modules/@babel/plugin-transform-private-property-in-object": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.23.4.tgz", - "integrity": "sha512-9G3K1YqTq3F4Vt88Djx1UZ79PDyj+yKRnUy7cZGSMe+a7jkwD259uKKuUzQlPkGam7R+8RJwh5z4xO27fA1o2A==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.24.7.tgz", + "integrity": "sha512-9z76mxwnwFxMyxZWEgdgECQglF2Q7cFLm0kMf8pGwt+GSJsY0cONKj/UuO4bOH0w/uAel3ekS4ra5CEAyJRmDA==", "dev": true, "dependencies": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-create-class-features-plugin": "^7.22.15", - "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-annotate-as-pure": "^7.24.7", + "@babel/helper-create-class-features-plugin": "^7.24.7", + "@babel/helper-plugin-utils": "^7.24.7", "@babel/plugin-syntax-private-property-in-object": "^7.14.5" }, "engines": { @@ -2324,12 +2095,12 @@ } }, "node_modules/@babel/plugin-transform-property-literals": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.23.3.tgz", - "integrity": "sha512-jR3Jn3y7cZp4oEWPFAlRsSWjxKe4PZILGBSd4nis1TsC5qeSpb+nrtihJuDhNI7QHiVbUaiXa0X2RZY3/TI6Nw==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.24.7.tgz", + "integrity": "sha512-EMi4MLQSHfd2nrCqQEWxFdha2gBCqU4ZcCng4WBGZ5CJL4bBRW0ptdqqDdeirGZcpALazVVNJqRmsO8/+oNCBA==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -2339,12 +2110,12 @@ } }, "node_modules/@babel/plugin-transform-regenerator": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.23.3.tgz", - "integrity": "sha512-KP+75h0KghBMcVpuKisx3XTu9Ncut8Q8TuvGO4IhY+9D5DFEckQefOuIsB/gQ2tG71lCke4NMrtIPS8pOj18BQ==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.24.7.tgz", + "integrity": "sha512-lq3fvXPdimDrlg6LWBoqj+r/DEWgONuwjuOuQCSYgRroXDH/IdM1C0IZf59fL5cHLpjEH/O6opIRBbqv7ELnuA==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-plugin-utils": "^7.24.7", "regenerator-transform": "^0.15.2" }, "engines": { @@ -2355,12 +2126,12 @@ } }, "node_modules/@babel/plugin-transform-reserved-words": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.23.3.tgz", - "integrity": "sha512-QnNTazY54YqgGxwIexMZva9gqbPa15t/x9VS+0fsEFWplwVpXYZivtgl43Z1vMpc1bdPP2PP8siFeVcnFvA3Cg==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.24.7.tgz", + "integrity": "sha512-0DUq0pHcPKbjFZCfTss/pGkYMfy3vFWydkUBd9r0GHpIyfs2eCDENvqadMycRS9wZCXR41wucAfJHJmwA0UmoQ==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -2370,16 +2141,17 @@ } }, "node_modules/@babel/plugin-transform-runtime": { - "version": "7.23.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.23.9.tgz", - "integrity": "sha512-A7clW3a0aSjm3ONU9o2HAILSegJCYlEZmOhmBRReVtIpY/Z/p7yIZ+wR41Z+UipwdGuqwtID/V/dOdZXjwi9gQ==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.24.7.tgz", + "integrity": "sha512-YqXjrk4C+a1kZjewqt+Mmu2UuV1s07y8kqcUf4qYLnoqemhR4gRQikhdAhSVJioMjVTu6Mo6pAbaypEA3jY6fw==", "dev": true, + "peer": true, "dependencies": { - "@babel/helper-module-imports": "^7.22.15", - "@babel/helper-plugin-utils": "^7.22.5", - "babel-plugin-polyfill-corejs2": "^0.4.8", - "babel-plugin-polyfill-corejs3": "^0.9.0", - "babel-plugin-polyfill-regenerator": "^0.5.5", + "@babel/helper-module-imports": "^7.24.7", + "@babel/helper-plugin-utils": "^7.24.7", + "babel-plugin-polyfill-corejs2": "^0.4.10", + "babel-plugin-polyfill-corejs3": "^0.10.1", + "babel-plugin-polyfill-regenerator": "^0.6.1", "semver": "^6.3.1" }, "engines": { @@ -2394,17 +2166,18 @@ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, + "peer": true, "bin": { "semver": "bin/semver.js" } }, "node_modules/@babel/plugin-transform-shorthand-properties": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.23.3.tgz", - "integrity": "sha512-ED2fgqZLmexWiN+YNFX26fx4gh5qHDhn1O2gvEhreLW2iI63Sqm4llRLCXALKrCnbN4Jy0VcMQZl/SAzqug/jg==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.24.7.tgz", + "integrity": "sha512-KsDsevZMDsigzbA09+vacnLpmPH4aWjcZjXdyFKGzpplxhbeB4wYtury3vglQkg6KM/xEPKt73eCjPPf1PgXBA==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -2414,13 +2187,13 @@ } }, "node_modules/@babel/plugin-transform-spread": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.23.3.tgz", - "integrity": "sha512-VvfVYlrlBVu+77xVTOAoxQ6mZbnIq5FM0aGBSFEcIh03qHf+zNqA4DC/3XMUozTg7bZV3e3mZQ0i13VB6v5yUg==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.24.7.tgz", + "integrity": "sha512-x96oO0I09dgMDxJaANcRyD4ellXFLLiWhuwDxKZX5g2rWP1bTPkBSwCYv96VDXVT1bD9aPj8tppr5ITIh8hBng==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5" + "@babel/helper-plugin-utils": "^7.24.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -2430,12 +2203,12 @@ } }, "node_modules/@babel/plugin-transform-sticky-regex": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.23.3.tgz", - "integrity": "sha512-HZOyN9g+rtvnOU3Yh7kSxXrKbzgrm5X4GncPY1QOquu7epga5MxKHVpYu2hvQnry/H+JjckSYRb93iNfsioAGg==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.24.7.tgz", + "integrity": "sha512-kHPSIJc9v24zEml5geKg9Mjx5ULpfncj0wRpYtxbvKyTtHCYDkVE3aHQ03FrpEo4gEe2vrJJS1Y9CJTaThA52g==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -2445,12 +2218,12 @@ } }, "node_modules/@babel/plugin-transform-template-literals": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.23.3.tgz", - "integrity": "sha512-Flok06AYNp7GV2oJPZZcP9vZdszev6vPBkHLwxwSpaIqx75wn6mUd3UFWsSsA0l8nXAKkyCmL/sR02m8RYGeHg==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.24.7.tgz", + "integrity": "sha512-AfDTQmClklHCOLxtGoP7HkeMw56k1/bTQjwsfhL6pppo/M4TOBSq+jjBUBLmV/4oeFg4GWMavIl44ZeCtmmZTw==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -2460,30 +2233,12 @@ } }, "node_modules/@babel/plugin-transform-typeof-symbol": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.23.3.tgz", - "integrity": "sha512-4t15ViVnaFdrPC74be1gXBSMzXk3B4Us9lP7uLRQHTFpV5Dvt33pn+2MyyNxmN3VTTm3oTrZVMUmuw3oBnQ2oQ==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-typescript": { - "version": "7.23.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.23.6.tgz", - "integrity": "sha512-6cBG5mBvUu4VUD04OHKnYzbuHNP8huDsD3EDqqpIpsswTDoqHCjLoHb6+QgsV1WsT2nipRqCPgxD3LXnEO7XfA==", + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.24.8.tgz", + "integrity": "sha512-adNTUpDCVnmAE58VEqKlAA6ZBlNkMnWD0ZcW76lyNFN3MJniyGFZfNwERVk8Ap56MCnXztmDr19T4mPTztcuaw==", "dev": true, "dependencies": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-create-class-features-plugin": "^7.23.6", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-typescript": "^7.23.3" + "@babel/helper-plugin-utils": "^7.24.8" }, "engines": { "node": ">=6.9.0" @@ -2493,12 +2248,12 @@ } }, "node_modules/@babel/plugin-transform-unicode-escapes": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.23.3.tgz", - "integrity": "sha512-OMCUx/bU6ChE3r4+ZdylEqAjaQgHAgipgW8nsCfu5pGqDcFytVd91AwRvUJSBZDz0exPGgnjoqhgRYLRjFZc9Q==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.24.7.tgz", + "integrity": "sha512-U3ap1gm5+4edc2Q/P+9VrBNhGkfnf+8ZqppY71Bo/pzZmXhhLdqgaUl6cuB07O1+AQJtCLfaOmswiNbSQ9ivhw==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -2508,13 +2263,13 @@ } }, "node_modules/@babel/plugin-transform-unicode-property-regex": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.23.3.tgz", - "integrity": "sha512-KcLIm+pDZkWZQAFJ9pdfmh89EwVfmNovFBcXko8szpBeF8z68kWIPeKlmSOkT9BXJxs2C0uk+5LxoxIv62MROA==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.24.7.tgz", + "integrity": "sha512-uH2O4OV5M9FZYQrwc7NdVmMxQJOCCzFeYudlZSzUAHRFeOujQefa92E74TQDVskNHCzOXoigEuoyzHDhaEaK5w==", "dev": true, "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.22.15", - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-create-regexp-features-plugin": "^7.24.7", + "@babel/helper-plugin-utils": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -2524,13 +2279,13 @@ } }, "node_modules/@babel/plugin-transform-unicode-regex": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.23.3.tgz", - "integrity": "sha512-wMHpNA4x2cIA32b/ci3AfwNgheiva2W0WUKWTK7vBHBhDKfPsc5cFGNWm69WBqpwd86u1qwZ9PWevKqm1A3yAw==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.24.7.tgz", + "integrity": "sha512-hlQ96MBZSAXUq7ltkjtu3FJCCSMx/j629ns3hA3pXnBXjanNP0LHi+JpPeA81zaWgVK1VGH95Xuy7u0RyQ8kMg==", "dev": true, "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.22.15", - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-create-regexp-features-plugin": "^7.24.7", + "@babel/helper-plugin-utils": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -2540,13 +2295,13 @@ } }, "node_modules/@babel/plugin-transform-unicode-sets-regex": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.23.3.tgz", - "integrity": "sha512-W7lliA/v9bNR83Qc3q1ip9CQMZ09CcHDbHfbLRDNuAhn1Mvkr1ZNF7hPmztMQvtTGVLJ9m8IZqWsTkXOml8dbw==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.24.7.tgz", + "integrity": "sha512-2G8aAvF4wy1w/AGZkemprdGMRg5o6zPNhbHVImRz3lss55TYCBd6xStN19rt8XJHq20sqV0JbyWjOWwQRwV/wg==", "dev": true, "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.22.15", - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-create-regexp-features-plugin": "^7.24.7", + "@babel/helper-plugin-utils": "^7.24.7" }, "engines": { "node": ">=6.9.0" @@ -2556,26 +2311,28 @@ } }, "node_modules/@babel/preset-env": { - "version": "7.23.9", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.23.9.tgz", - "integrity": "sha512-3kBGTNBBk9DQiPoXYS0g0BYlwTQYUTifqgKTjxUwEUkduRT2QOa0FPGBJ+NROQhGyYO5BuTJwGvBnqKDykac6A==", - "dev": true, - "dependencies": { - "@babel/compat-data": "^7.23.5", - "@babel/helper-compilation-targets": "^7.23.6", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-validator-option": "^7.23.5", - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.23.3", - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.23.3", - "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.23.7", + "version": "7.25.3", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.25.3.tgz", + "integrity": "sha512-QsYW7UeAaXvLPX9tdVliMJE7MD7M6MLYVTovRTIwhoYQVFHR1rM4wO8wqAezYi3/BpSD+NzVCZ69R6smWiIi8g==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.25.2", + "@babel/helper-compilation-targets": "^7.25.2", + "@babel/helper-plugin-utils": "^7.24.8", + "@babel/helper-validator-option": "^7.24.8", + "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.25.3", + "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.25.0", + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.25.0", + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.24.7", + "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.25.0", "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", "@babel/plugin-syntax-async-generators": "^7.8.4", "@babel/plugin-syntax-class-properties": "^7.12.13", "@babel/plugin-syntax-class-static-block": "^7.14.5", "@babel/plugin-syntax-dynamic-import": "^7.8.3", "@babel/plugin-syntax-export-namespace-from": "^7.8.3", - "@babel/plugin-syntax-import-assertions": "^7.23.3", - "@babel/plugin-syntax-import-attributes": "^7.23.3", + "@babel/plugin-syntax-import-assertions": "^7.24.7", + "@babel/plugin-syntax-import-attributes": "^7.24.7", "@babel/plugin-syntax-import-meta": "^7.10.4", "@babel/plugin-syntax-json-strings": "^7.8.3", "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", @@ -2587,59 +2344,60 @@ "@babel/plugin-syntax-private-property-in-object": "^7.14.5", "@babel/plugin-syntax-top-level-await": "^7.14.5", "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", - "@babel/plugin-transform-arrow-functions": "^7.23.3", - "@babel/plugin-transform-async-generator-functions": "^7.23.9", - "@babel/plugin-transform-async-to-generator": "^7.23.3", - "@babel/plugin-transform-block-scoped-functions": "^7.23.3", - "@babel/plugin-transform-block-scoping": "^7.23.4", - "@babel/plugin-transform-class-properties": "^7.23.3", - "@babel/plugin-transform-class-static-block": "^7.23.4", - "@babel/plugin-transform-classes": "^7.23.8", - "@babel/plugin-transform-computed-properties": "^7.23.3", - "@babel/plugin-transform-destructuring": "^7.23.3", - "@babel/plugin-transform-dotall-regex": "^7.23.3", - "@babel/plugin-transform-duplicate-keys": "^7.23.3", - "@babel/plugin-transform-dynamic-import": "^7.23.4", - "@babel/plugin-transform-exponentiation-operator": "^7.23.3", - "@babel/plugin-transform-export-namespace-from": "^7.23.4", - "@babel/plugin-transform-for-of": "^7.23.6", - "@babel/plugin-transform-function-name": "^7.23.3", - "@babel/plugin-transform-json-strings": "^7.23.4", - "@babel/plugin-transform-literals": "^7.23.3", - "@babel/plugin-transform-logical-assignment-operators": "^7.23.4", - "@babel/plugin-transform-member-expression-literals": "^7.23.3", - "@babel/plugin-transform-modules-amd": "^7.23.3", - "@babel/plugin-transform-modules-commonjs": "^7.23.3", - "@babel/plugin-transform-modules-systemjs": "^7.23.9", - "@babel/plugin-transform-modules-umd": "^7.23.3", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.22.5", - "@babel/plugin-transform-new-target": "^7.23.3", - "@babel/plugin-transform-nullish-coalescing-operator": "^7.23.4", - "@babel/plugin-transform-numeric-separator": "^7.23.4", - "@babel/plugin-transform-object-rest-spread": "^7.23.4", - "@babel/plugin-transform-object-super": "^7.23.3", - "@babel/plugin-transform-optional-catch-binding": "^7.23.4", - "@babel/plugin-transform-optional-chaining": "^7.23.4", - "@babel/plugin-transform-parameters": "^7.23.3", - "@babel/plugin-transform-private-methods": "^7.23.3", - "@babel/plugin-transform-private-property-in-object": "^7.23.4", - "@babel/plugin-transform-property-literals": "^7.23.3", - "@babel/plugin-transform-regenerator": "^7.23.3", - "@babel/plugin-transform-reserved-words": "^7.23.3", - "@babel/plugin-transform-shorthand-properties": "^7.23.3", - "@babel/plugin-transform-spread": "^7.23.3", - "@babel/plugin-transform-sticky-regex": "^7.23.3", - "@babel/plugin-transform-template-literals": "^7.23.3", - "@babel/plugin-transform-typeof-symbol": "^7.23.3", - "@babel/plugin-transform-unicode-escapes": "^7.23.3", - "@babel/plugin-transform-unicode-property-regex": "^7.23.3", - "@babel/plugin-transform-unicode-regex": "^7.23.3", - "@babel/plugin-transform-unicode-sets-regex": "^7.23.3", + "@babel/plugin-transform-arrow-functions": "^7.24.7", + "@babel/plugin-transform-async-generator-functions": "^7.25.0", + "@babel/plugin-transform-async-to-generator": "^7.24.7", + "@babel/plugin-transform-block-scoped-functions": "^7.24.7", + "@babel/plugin-transform-block-scoping": "^7.25.0", + "@babel/plugin-transform-class-properties": "^7.24.7", + "@babel/plugin-transform-class-static-block": "^7.24.7", + "@babel/plugin-transform-classes": "^7.25.0", + "@babel/plugin-transform-computed-properties": "^7.24.7", + "@babel/plugin-transform-destructuring": "^7.24.8", + "@babel/plugin-transform-dotall-regex": "^7.24.7", + "@babel/plugin-transform-duplicate-keys": "^7.24.7", + "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.25.0", + "@babel/plugin-transform-dynamic-import": "^7.24.7", + "@babel/plugin-transform-exponentiation-operator": "^7.24.7", + "@babel/plugin-transform-export-namespace-from": "^7.24.7", + "@babel/plugin-transform-for-of": "^7.24.7", + "@babel/plugin-transform-function-name": "^7.25.1", + "@babel/plugin-transform-json-strings": "^7.24.7", + "@babel/plugin-transform-literals": "^7.25.2", + "@babel/plugin-transform-logical-assignment-operators": "^7.24.7", + "@babel/plugin-transform-member-expression-literals": "^7.24.7", + "@babel/plugin-transform-modules-amd": "^7.24.7", + "@babel/plugin-transform-modules-commonjs": "^7.24.8", + "@babel/plugin-transform-modules-systemjs": "^7.25.0", + "@babel/plugin-transform-modules-umd": "^7.24.7", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.24.7", + "@babel/plugin-transform-new-target": "^7.24.7", + "@babel/plugin-transform-nullish-coalescing-operator": "^7.24.7", + "@babel/plugin-transform-numeric-separator": "^7.24.7", + "@babel/plugin-transform-object-rest-spread": "^7.24.7", + "@babel/plugin-transform-object-super": "^7.24.7", + "@babel/plugin-transform-optional-catch-binding": "^7.24.7", + "@babel/plugin-transform-optional-chaining": "^7.24.8", + "@babel/plugin-transform-parameters": "^7.24.7", + "@babel/plugin-transform-private-methods": "^7.24.7", + "@babel/plugin-transform-private-property-in-object": "^7.24.7", + "@babel/plugin-transform-property-literals": "^7.24.7", + "@babel/plugin-transform-regenerator": "^7.24.7", + "@babel/plugin-transform-reserved-words": "^7.24.7", + "@babel/plugin-transform-shorthand-properties": "^7.24.7", + "@babel/plugin-transform-spread": "^7.24.7", + "@babel/plugin-transform-sticky-regex": "^7.24.7", + "@babel/plugin-transform-template-literals": "^7.24.7", + "@babel/plugin-transform-typeof-symbol": "^7.24.8", + "@babel/plugin-transform-unicode-escapes": "^7.24.7", + "@babel/plugin-transform-unicode-property-regex": "^7.24.7", + "@babel/plugin-transform-unicode-regex": "^7.24.7", + "@babel/plugin-transform-unicode-sets-regex": "^7.24.7", "@babel/preset-modules": "0.1.6-no-external-plugins", - "babel-plugin-polyfill-corejs2": "^0.4.8", - "babel-plugin-polyfill-corejs3": "^0.9.0", - "babel-plugin-polyfill-regenerator": "^0.5.5", - "core-js-compat": "^3.31.0", + "babel-plugin-polyfill-corejs2": "^0.4.10", + "babel-plugin-polyfill-corejs3": "^0.10.4", + "babel-plugin-polyfill-regenerator": "^0.6.1", + "core-js-compat": "^3.37.1", "semver": "^6.3.1" }, "engines": { @@ -2658,23 +2416,6 @@ "semver": "bin/semver.js" } }, - "node_modules/@babel/preset-flow": { - "version": "7.24.0", - "resolved": "https://registry.npmjs.org/@babel/preset-flow/-/preset-flow-7.24.0.tgz", - "integrity": "sha512-cum/nSi82cDaSJ21I4PgLTVlj0OXovFk6GRguJYe/IKg6y6JHLTbJhybtX4k35WT9wdeJfEVjycTixMhBHd0Dg==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0", - "@babel/helper-validator-option": "^7.23.5", - "@babel/plugin-transform-flow-strip-types": "^7.23.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, "node_modules/@babel/preset-modules": { "version": "0.1.6-no-external-plugins", "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz", @@ -2689,242 +2430,229 @@ "@babel/core": "^7.0.0-0 || ^8.0.0-0 <8.0.0" } }, - "node_modules/@babel/preset-typescript": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.23.3.tgz", - "integrity": "sha512-17oIGVlqz6CchO9RFYn5U6ZpWRZIngayYCtrPRSgANSwC2V1Jb+iP74nVxzzXJte8b8BYxrL1yY96xfhTBrNNQ==", + "node_modules/@babel/regjsgen": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@babel/regjsgen/-/regjsgen-0.8.0.tgz", + "integrity": "sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==", + "dev": true + }, + "node_modules/@babel/runtime": { + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.25.0.tgz", + "integrity": "sha512-7dRy4DwXwtzBrPbZflqxnvfxLF8kdZXPkhymtDeFoFqE6ldzjQFgYTtYIFARcLEYDrqfBfYcZt1WqFxRoyC9Rw==", "dev": true, "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-validator-option": "^7.22.15", - "@babel/plugin-syntax-jsx": "^7.23.3", - "@babel/plugin-transform-modules-commonjs": "^7.23.3", - "@babel/plugin-transform-typescript": "^7.23.3" + "regenerator-runtime": "^0.14.0" }, "engines": { "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/register": { - "version": "7.23.7", - "resolved": "https://registry.npmjs.org/@babel/register/-/register-7.23.7.tgz", - "integrity": "sha512-EjJeB6+kvpk+Y5DAkEAmbOBEFkh9OASx0huoEkqYTFxAZHzOAX2Oh5uwAUuL2rUddqfM0SA+KPXV2TbzoZ2kvQ==", + "node_modules/@babel/template": { + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.0.tgz", + "integrity": "sha512-aOOgh1/5XzKvg1jvVz7AVrx2piJ2XBi227DHmbY6y+bM9H2FlN+IfecYu4Xl0cNiiVejlsCri89LUsbj8vJD9Q==", "dev": true, "dependencies": { - "clone-deep": "^4.0.1", - "find-cache-dir": "^2.0.0", - "make-dir": "^2.1.0", - "pirates": "^4.0.6", - "source-map-support": "^0.5.16" + "@babel/code-frame": "^7.24.7", + "@babel/parser": "^7.25.0", + "@babel/types": "^7.25.0" }, "engines": { "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/register/node_modules/find-cache-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz", - "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==", + "node_modules/@babel/traverse": { + "version": "7.25.3", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.3.tgz", + "integrity": "sha512-HefgyP1x754oGCsKmV5reSmtV7IXj/kpaE1XYY+D9G5PvKKoFfSbiS4M77MdjuwlZKDIKFCffq9rPU+H/s3ZdQ==", "dev": true, "dependencies": { - "commondir": "^1.0.1", - "make-dir": "^2.0.0", - "pkg-dir": "^3.0.0" + "@babel/code-frame": "^7.24.7", + "@babel/generator": "^7.25.0", + "@babel/parser": "^7.25.3", + "@babel/template": "^7.25.0", + "@babel/types": "^7.25.2", + "debug": "^4.3.1", + "globals": "^11.1.0" }, "engines": { - "node": ">=6" + "node": ">=6.9.0" } }, - "node_modules/@babel/register/node_modules/find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "node_modules/@babel/types": { + "version": "7.25.2", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.25.2.tgz", + "integrity": "sha512-YTnYtra7W9e6/oAZEHj0bJehPRUlLH9/fbpT5LfB0NhQXyALCRkRs3zH9v07IYhkgpqX6Z78FnuccZr/l4Fs4Q==", "dev": true, "dependencies": { - "locate-path": "^3.0.0" + "@babel/helper-string-parser": "^7.24.8", + "@babel/helper-validator-identifier": "^7.24.7", + "to-fast-properties": "^2.0.0" }, "engines": { - "node": ">=6" + "node": ">=6.9.0" } }, - "node_modules/@babel/register/node_modules/locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "dev": true, - "dependencies": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@babel/register/node_modules/make-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", - "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", - "dev": true, - "dependencies": { - "pify": "^4.0.1", - "semver": "^5.6.0" - }, - "engines": { - "node": ">=6" - } + "node_modules/@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "dev": true }, - "node_modules/@babel/register/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "node_modules/@biomejs/biome": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-1.8.3.tgz", + "integrity": "sha512-/uUV3MV+vyAczO+vKrPdOW0Iaet7UnJMU4bNMinggGJTAnBPjCoLEYcyYtYHNnUNYlv4xZMH6hVIQCAozq8d5w==", "dev": true, - "dependencies": { - "p-try": "^2.0.0" + "hasInstallScript": true, + "bin": { + "biome": "bin/biome" }, "engines": { - "node": ">=6" + "node": ">=14.21.3" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@babel/register/node_modules/p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "dev": true, - "dependencies": { - "p-limit": "^2.0.0" + "type": "opencollective", + "url": "https://opencollective.com/biome" }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@babel/register/node_modules/path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", + "optionalDependencies": { + "@biomejs/cli-darwin-arm64": "1.8.3", + "@biomejs/cli-darwin-x64": "1.8.3", + "@biomejs/cli-linux-arm64": "1.8.3", + "@biomejs/cli-linux-arm64-musl": "1.8.3", + "@biomejs/cli-linux-x64": "1.8.3", + "@biomejs/cli-linux-x64-musl": "1.8.3", + "@biomejs/cli-win32-arm64": "1.8.3", + "@biomejs/cli-win32-x64": "1.8.3" + } + }, + "node_modules/@biomejs/cli-darwin-arm64": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-1.8.3.tgz", + "integrity": "sha512-9DYOjclFpKrH/m1Oz75SSExR8VKvNSSsLnVIqdnKexj6NwmiMlKk94Wa1kZEdv6MCOHGHgyyoV57Cw8WzL5n3A==", + "cpu": [ + "arm64" + ], "dev": true, + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": ">=4" + "node": ">=14.21.3" } }, - "node_modules/@babel/register/node_modules/pify": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "node_modules/@biomejs/cli-darwin-x64": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-x64/-/cli-darwin-x64-1.8.3.tgz", + "integrity": "sha512-UeW44L/AtbmOF7KXLCoM+9PSgPo0IDcyEUfIoOXYeANaNXXf9mLUwV1GeF2OWjyic5zj6CnAJ9uzk2LT3v/wAw==", + "cpu": [ + "x64" + ], "dev": true, + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": ">=6" + "node": ">=14.21.3" } }, - "node_modules/@babel/register/node_modules/pkg-dir": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", - "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", + "node_modules/@biomejs/cli-linux-arm64": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64/-/cli-linux-arm64-1.8.3.tgz", + "integrity": "sha512-fed2ji8s+I/m8upWpTJGanqiJ0rnlHOK3DdxsyVLZQ8ClY6qLuPc9uehCREBifRJLl/iJyQpHIRufLDeotsPtw==", + "cpu": [ + "arm64" + ], "dev": true, - "dependencies": { - "find-up": "^3.0.0" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=6" - } - }, - "node_modules/@babel/register/node_modules/semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", - "dev": true, - "bin": { - "semver": "bin/semver" + "node": ">=14.21.3" } }, - "node_modules/@babel/regjsgen": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@babel/regjsgen/-/regjsgen-0.8.0.tgz", - "integrity": "sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==", - "dev": true - }, - "node_modules/@babel/runtime": { - "version": "7.23.9", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.9.tgz", - "integrity": "sha512-0CX6F+BI2s9dkUqr08KFrAIZgNFj75rdBU/DjCyYLIaV/quFjkk6T+EJ2LkZHyZTbEV4L5p97mNkUsHl2wLFAw==", + "node_modules/@biomejs/cli-linux-arm64-musl": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-1.8.3.tgz", + "integrity": "sha512-9yjUfOFN7wrYsXt/T/gEWfvVxKlnh3yBpnScw98IF+oOeCYb5/b/+K7YNqKROV2i1DlMjg9g/EcN9wvj+NkMuQ==", + "cpu": [ + "arm64" + ], "dev": true, - "dependencies": { - "regenerator-runtime": "^0.14.0" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=6.9.0" + "node": ">=14.21.3" } }, - "node_modules/@babel/template": { - "version": "7.24.0", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.24.0.tgz", - "integrity": "sha512-Bkf2q8lMB0AFpX0NFEqSbx1OkTHf0f+0j82mkw+ZpzBnkk7e9Ql0891vlfgi+kHwOk8tQjiQHpqh4LaSa0fKEA==", + "node_modules/@biomejs/cli-linux-x64": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64/-/cli-linux-x64-1.8.3.tgz", + "integrity": "sha512-I8G2QmuE1teISyT8ie1HXsjFRz9L1m5n83U1O6m30Kw+kPMPSKjag6QGUn+sXT8V+XWIZxFFBoTDEDZW2KPDDw==", + "cpu": [ + "x64" + ], "dev": true, - "dependencies": { - "@babel/code-frame": "^7.23.5", - "@babel/parser": "^7.24.0", - "@babel/types": "^7.24.0" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=6.9.0" + "node": ">=14.21.3" } }, - "node_modules/@babel/traverse": { - "version": "7.24.0", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.24.0.tgz", - "integrity": "sha512-HfuJlI8qq3dEDmNU5ChzzpZRWq+oxCZQyMzIMEqLho+AQnhMnKQUzH6ydo3RBl/YjPCuk68Y6s0Gx0AeyULiWw==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.23.5", - "@babel/generator": "^7.23.6", - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-function-name": "^7.23.0", - "@babel/helper-hoist-variables": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/parser": "^7.24.0", - "@babel/types": "^7.24.0", - "debug": "^4.3.1", - "globals": "^11.1.0" - }, + "node_modules/@biomejs/cli-linux-x64-musl": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-1.8.3.tgz", + "integrity": "sha512-UHrGJX7PrKMKzPGoEsooKC9jXJMa28TUSMjcIlbDnIO4EAavCoVmNQaIuUSH0Ls2mpGMwUIf+aZJv657zfWWjA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=6.9.0" + "node": ">=14.21.3" } }, - "node_modules/@babel/types": { - "version": "7.24.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.0.tgz", - "integrity": "sha512-+j7a5c253RfKh8iABBhywc8NSfP5LURe7Uh4qpsh6jc+aLJguvmIUBdjSdEMQv2bENrCR5MfRdjGo7vzS/ob7w==", + "node_modules/@biomejs/cli-win32-arm64": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-arm64/-/cli-win32-arm64-1.8.3.tgz", + "integrity": "sha512-J+Hu9WvrBevfy06eU1Na0lpc7uR9tibm9maHynLIoAjLZpQU3IW+OKHUtyL8p6/3pT2Ju5t5emReeIS2SAxhkQ==", + "cpu": [ + "arm64" + ], "dev": true, - "dependencies": { - "@babel/helper-string-parser": "^7.23.4", - "@babel/helper-validator-identifier": "^7.22.20", - "to-fast-properties": "^2.0.0" - }, + "optional": true, + "os": [ + "win32" + ], "engines": { - "node": ">=6.9.0" + "node": ">=14.21.3" } }, - "node_modules/@bcoe/v8-coverage": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", - "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", - "dev": true - }, - "node_modules/@colors/colors": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", - "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", + "node_modules/@biomejs/cli-win32-x64": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-x64/-/cli-win32-x64-1.8.3.tgz", + "integrity": "sha512-/PJ59vA1pnQeKahemaQf4Nyj7IKUvGQSc3Ze1uIGi+Wvr1xF7rGobSrAAG01T/gUDG21vkDsZYM03NAmPiVkqg==", + "cpu": [ + "x64" + ], "dev": true, + "optional": true, + "os": [ + "win32" + ], "engines": { - "node": ">=0.1.90" + "node": ">=14.21.3" } }, "node_modules/@commitlint/cli": { @@ -3627,101 +3355,22 @@ "@jridgewell/sourcemap-codec": "^1.4.10" } }, - "node_modules/@cypress/request": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@cypress/request/-/request-3.0.1.tgz", - "integrity": "sha512-TWivJlJi8ZDx2wGOw1dbLuHJKUYX7bWySw377nlnGOW3hP9/MUKIsEdXT/YngWxVdgNCHRBmFlBipE+5/2ZZlQ==", - "dev": true, - "dependencies": { - "aws-sign2": "~0.7.0", - "aws4": "^1.8.0", - "caseless": "~0.12.0", - "combined-stream": "~1.0.6", - "extend": "~3.0.2", - "forever-agent": "~0.6.1", - "form-data": "~2.3.2", - "http-signature": "~1.3.6", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.19", - "performance-now": "^2.1.0", - "qs": "6.10.4", - "safe-buffer": "^5.1.2", - "tough-cookie": "^4.1.3", - "tunnel-agent": "^0.6.0", - "uuid": "^8.3.2" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/@cypress/request/node_modules/form-data": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", - "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", - "dev": true, - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 0.12" - } - }, - "node_modules/@cypress/request/node_modules/qs": { - "version": "6.10.4", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.4.tgz", - "integrity": "sha512-OQiU+C+Ds5qiH91qh/mg0w+8nwQuLjM4F4M/PbmhDOoYehPh+Fb0bDjtR1sOvy7YKxvj28Y/M0PhP5uVX0kB+g==", - "dev": true, - "dependencies": { - "side-channel": "^1.0.4" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/@cypress/request/node_modules/uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "dev": true, - "bin": { - "uuid": "dist/bin/uuid" - } - }, - "node_modules/@cypress/xvfb": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@cypress/xvfb/-/xvfb-1.2.4.tgz", - "integrity": "sha512-skbBzPggOVYCbnGgV+0dmBdW/s77ZkAOXIC1knS8NagwDjBrNC1LuXtQJeiN6l+m7lzmHtaoUw/ctJKdqkG57Q==", - "dev": true, - "dependencies": { - "debug": "^3.1.0", - "lodash.once": "^4.1.1" - } - }, - "node_modules/@cypress/xvfb/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "dependencies": { - "ms": "^2.1.1" - } - }, "node_modules/@discoveryjs/json-ext": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz", - "integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==", + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.6.1.tgz", + "integrity": "sha512-boghen8F0Q8D+0/Q1/1r6DUEieUJ8w2a1gIknExMSHBsJFOr2+0KUfHiVYBvucPwl3+RU5PFBK833FjFCh3BhA==", "dev": true, + "peer": true, "engines": { - "node": ">=10.0.0" + "node": ">=14.17.0" } }, + "node_modules/@dvsa/biome-config": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@dvsa/biome-config/-/biome-config-0.1.0.tgz", + "integrity": "sha512-eHJ5UN+klqWgwyHrcuIXQpNy0YsP8AELR8UhLaIdMr0/sPZgyZBD5YRhv54GFjh94UYWKhEbc1qyltW7imupwg==", + "dev": true + }, "node_modules/@dvsa/cvs-type-definitions": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/@dvsa/cvs-type-definitions/-/cvs-type-definitions-6.3.0.tgz", @@ -3733,38 +3382,10 @@ "util": "^0.12.5" } }, - "node_modules/@dvsa/eslint-config-ts": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@dvsa/eslint-config-ts/-/eslint-config-ts-3.0.1.tgz", - "integrity": "sha512-kvXp2oD9o8RfvcIrxHH5Dp2cWxZ65D6POslJZ+tnyExPVhWyxFDJjGehkpsJHPawkpp6/SsXtjv9Qe76XbDk9w==", - "dev": true, - "peerDependencies": { - "@typescript-eslint/eslint-plugin": ">=4.33.0", - "@typescript-eslint/typescript-estree": ">=4.33.0", - "eslint": ">=7.32.0", - "eslint-config-airbnb-base": ">=14.2.0", - "eslint-config-airbnb-typescript": ">=12.3.1", - "eslint-plugin-import": ">=2.24.2", - "eslint-plugin-jest": ">=25.2.4", - "eslint-plugin-jsx-a11y": ">=6.4.1", - "eslint-plugin-react": ">=7.26.1", - "eslint-plugin-react-hooks": ">=4.2.0", - "eslint-plugin-security": ">=2.1.0" - } - }, - "node_modules/@emotion/use-insertion-effect-with-fallbacks": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.0.1.tgz", - "integrity": "sha512-jT/qyKZ9rzLErtrjGgdkMBn2OP8wl0G3sQlBb3YPryvKHsjvINUhVaPFfP+fpBcOkmrVOVEEHQFJ7nbj2TH2gw==", - "dev": true, - "peerDependencies": { - "react": ">=16.8.0" - } - }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.20.0", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.20.0.tgz", - "integrity": "sha512-fGFDEctNh0CcSwsiRPxiaqX0P5rq+AqE0SRhYGZ4PX46Lg1FNR6oCxJghf8YgY0WQEgQuh3lErUFE4KxLeRmmw==", + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.23.0.tgz", + "integrity": "sha512-3sG8Zwa5fMcA9bgqB8AfWPQ+HFke6uD3h1s3RIwUNK8EG7a4buxvuFTs3j1IMs2NXAk9F30C/FF4vxRgQCcmoQ==", "cpu": [ "ppc64" ], @@ -3773,15 +3394,14 @@ "os": [ "aix" ], - "peer": true, "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/android-arm": { - "version": "0.20.0", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.20.0.tgz", - "integrity": "sha512-3bMAfInvByLHfJwYPJRlpTeaQA75n8C/QKpEaiS4HrFWFiJlNI0vzq/zCjBrhAYcPyVPG7Eo9dMrcQXuqmNk5g==", + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.23.0.tgz", + "integrity": "sha512-+KuOHTKKyIKgEEqKbGTK8W7mPp+hKinbMBeEnNzjJGyFcWsfrXjSTNluJHCY1RqhxFurdD8uNXQDei7qDlR6+g==", "cpu": [ "arm" ], @@ -3790,15 +3410,14 @@ "os": [ "android" ], - "peer": true, "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/android-arm64": { - "version": "0.20.0", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.20.0.tgz", - "integrity": "sha512-aVpnM4lURNkp0D3qPoAzSG92VXStYmoVPOgXveAUoQBWRSuQzt51yvSju29J6AHPmwY1BjH49uR29oyfH1ra8Q==", + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.23.0.tgz", + "integrity": "sha512-EuHFUYkAVfU4qBdyivULuu03FhJO4IJN9PGuABGrFy4vUuzk91P2d+npxHcFdpUnfYKy0PuV+n6bKIpHOB3prQ==", "cpu": [ "arm64" ], @@ -3807,15 +3426,14 @@ "os": [ "android" ], - "peer": true, "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/android-x64": { - "version": "0.20.0", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.20.0.tgz", - "integrity": "sha512-uK7wAnlRvjkCPzh8jJ+QejFyrP8ObKuR5cBIsQZ+qbMunwR8sbd8krmMbxTLSrDhiPZaJYKQAU5Y3iMDcZPhyQ==", + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.23.0.tgz", + "integrity": "sha512-WRrmKidLoKDl56LsbBMhzTTBxrsVwTKdNbKDalbEZr0tcsBgCLbEtoNthOW6PX942YiYq8HzEnb4yWQMLQuipQ==", "cpu": [ "x64" ], @@ -3824,15 +3442,14 @@ "os": [ "android" ], - "peer": true, "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.20.0", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.20.0.tgz", - "integrity": "sha512-AjEcivGAlPs3UAcJedMa9qYg9eSfU6FnGHJjT8s346HSKkrcWlYezGE8VaO2xKfvvlZkgAhyvl06OJOxiMgOYQ==", + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.23.0.tgz", + "integrity": "sha512-YLntie/IdS31H54Ogdn+v50NuoWF5BDkEUFpiOChVa9UnKpftgwzZRrI4J132ETIi+D8n6xh9IviFV3eXdxfow==", "cpu": [ "arm64" ], @@ -3841,15 +3458,14 @@ "os": [ "darwin" ], - "peer": true, "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.20.0", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.20.0.tgz", - "integrity": "sha512-bsgTPoyYDnPv8ER0HqnJggXK6RyFy4PH4rtsId0V7Efa90u2+EifxytE9pZnsDgExgkARy24WUQGv9irVbTvIw==", + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.23.0.tgz", + "integrity": "sha512-IMQ6eme4AfznElesHUPDZ+teuGwoRmVuuixu7sv92ZkdQcPbsNHzutd+rAfaBKo8YK3IrBEi9SLLKWJdEvJniQ==", "cpu": [ "x64" ], @@ -3858,15 +3474,14 @@ "os": [ "darwin" ], - "peer": true, "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.20.0", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.20.0.tgz", - "integrity": "sha512-kQ7jYdlKS335mpGbMW5tEe3IrQFIok9r84EM3PXB8qBFJPSc6dpWfrtsC/y1pyrz82xfUIn5ZrnSHQQsd6jebQ==", + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.23.0.tgz", + "integrity": "sha512-0muYWCng5vqaxobq6LB3YNtevDFSAZGlgtLoAc81PjUfiFz36n4KMpwhtAd4he8ToSI3TGyuhyx5xmiWNYZFyw==", "cpu": [ "arm64" ], @@ -3875,15 +3490,14 @@ "os": [ "freebsd" ], - "peer": true, "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.20.0", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.20.0.tgz", - "integrity": "sha512-uG8B0WSepMRsBNVXAQcHf9+Ko/Tr+XqmK7Ptel9HVmnykupXdS4J7ovSQUIi0tQGIndhbqWLaIL/qO/cWhXKyQ==", + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.23.0.tgz", + "integrity": "sha512-XKDVu8IsD0/q3foBzsXGt/KjD/yTKBCIwOHE1XwiXmrRwrX6Hbnd5Eqn/WvDekddK21tfszBSrE/WMaZh+1buQ==", "cpu": [ "x64" ], @@ -3892,15 +3506,14 @@ "os": [ "freebsd" ], - "peer": true, "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-arm": { - "version": "0.20.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.20.0.tgz", - "integrity": "sha512-2ezuhdiZw8vuHf1HKSf4TIk80naTbP9At7sOqZmdVwvvMyuoDiZB49YZKLsLOfKIr77+I40dWpHVeY5JHpIEIg==", + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.23.0.tgz", + "integrity": "sha512-SEELSTEtOFu5LPykzA395Mc+54RMg1EUgXP+iw2SJ72+ooMwVsgfuwXo5Fn0wXNgWZsTVHwY2cg4Vi/bOD88qw==", "cpu": [ "arm" ], @@ -3909,15 +3522,14 @@ "os": [ "linux" ], - "peer": true, "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.20.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.20.0.tgz", - "integrity": "sha512-uTtyYAP5veqi2z9b6Gr0NUoNv9F/rOzI8tOD5jKcCvRUn7T60Bb+42NDBCWNhMjkQzI0qqwXkQGo1SY41G52nw==", + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.23.0.tgz", + "integrity": "sha512-j1t5iG8jE7BhonbsEg5d9qOYcVZv/Rv6tghaXM/Ug9xahM0nX/H2gfu6X6z11QRTMT6+aywOMA8TDkhPo8aCGw==", "cpu": [ "arm64" ], @@ -3926,15 +3538,14 @@ "os": [ "linux" ], - "peer": true, "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.20.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.20.0.tgz", - "integrity": "sha512-c88wwtfs8tTffPaoJ+SQn3y+lKtgTzyjkD8NgsyCtCmtoIC8RDL7PrJU05an/e9VuAke6eJqGkoMhJK1RY6z4w==", + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.23.0.tgz", + "integrity": "sha512-P7O5Tkh2NbgIm2R6x1zGJJsnacDzTFcRWZyTTMgFdVit6E98LTxO+v8LCCLWRvPrjdzXHx9FEOA8oAZPyApWUA==", "cpu": [ "ia32" ], @@ -3943,15 +3554,14 @@ "os": [ "linux" ], - "peer": true, "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.20.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.20.0.tgz", - "integrity": "sha512-lR2rr/128/6svngnVta6JN4gxSXle/yZEZL3o4XZ6esOqhyR4wsKyfu6qXAL04S4S5CgGfG+GYZnjFd4YiG3Aw==", + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.23.0.tgz", + "integrity": "sha512-InQwepswq6urikQiIC/kkx412fqUZudBO4SYKu0N+tGhXRWUqAx+Q+341tFV6QdBifpjYgUndV1hhMq3WeJi7A==", "cpu": [ "loong64" ], @@ -3960,15 +3570,14 @@ "os": [ "linux" ], - "peer": true, "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.20.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.20.0.tgz", - "integrity": "sha512-9Sycc+1uUsDnJCelDf6ZNqgZQoK1mJvFtqf2MUz4ujTxGhvCWw+4chYfDLPepMEvVL9PDwn6HrXad5yOrNzIsQ==", + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.23.0.tgz", + "integrity": "sha512-J9rflLtqdYrxHv2FqXE2i1ELgNjT+JFURt/uDMoPQLcjWQA5wDKgQA4t/dTqGa88ZVECKaD0TctwsUfHbVoi4w==", "cpu": [ "mips64el" ], @@ -3977,15 +3586,14 @@ "os": [ "linux" ], - "peer": true, "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.20.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.20.0.tgz", - "integrity": "sha512-CoWSaaAXOZd+CjbUTdXIJE/t7Oz+4g90A3VBCHLbfuc5yUQU/nFDLOzQsN0cdxgXd97lYW/psIIBdjzQIwTBGw==", + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.23.0.tgz", + "integrity": "sha512-cShCXtEOVc5GxU0fM+dsFD10qZ5UpcQ8AM22bYj0u/yaAykWnqXJDpd77ublcX6vdDsWLuweeuSNZk4yUxZwtw==", "cpu": [ "ppc64" ], @@ -3994,15 +3602,14 @@ "os": [ "linux" ], - "peer": true, "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.20.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.20.0.tgz", - "integrity": "sha512-mlb1hg/eYRJUpv8h/x+4ShgoNLL8wgZ64SUr26KwglTYnwAWjkhR2GpoKftDbPOCnodA9t4Y/b68H4J9XmmPzA==", + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.23.0.tgz", + "integrity": "sha512-HEtaN7Y5UB4tZPeQmgz/UhzoEyYftbMXrBCUjINGjh3uil+rB/QzzpMshz3cNUxqXN7Vr93zzVtpIDL99t9aRw==", "cpu": [ "riscv64" ], @@ -4011,15 +3618,14 @@ "os": [ "linux" ], - "peer": true, "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.20.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.20.0.tgz", - "integrity": "sha512-fgf9ubb53xSnOBqyvWEY6ukBNRl1mVX1srPNu06B6mNsNK20JfH6xV6jECzrQ69/VMiTLvHMicQR/PgTOgqJUQ==", + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.23.0.tgz", + "integrity": "sha512-WDi3+NVAuyjg/Wxi+o5KPqRbZY0QhI9TjrEEm+8dmpY9Xir8+HE/HNx2JoLckhKbFopW0RdO2D72w8trZOV+Wg==", "cpu": [ "s390x" ], @@ -4028,15 +3634,14 @@ "os": [ "linux" ], - "peer": true, "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-x64": { - "version": "0.20.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.20.0.tgz", - "integrity": "sha512-H9Eu6MGse++204XZcYsse1yFHmRXEWgadk2N58O/xd50P9EvFMLJTQLg+lB4E1cF2xhLZU5luSWtGTb0l9UeSg==", + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.23.0.tgz", + "integrity": "sha512-a3pMQhUEJkITgAw6e0bWA+F+vFtCciMjW/LPtoj99MhVt+Mfb6bbL9hu2wmTZgNd994qTAEw+U/r6k3qHWWaOQ==", "cpu": [ "x64" ], @@ -4045,15 +3650,14 @@ "os": [ "linux" ], - "peer": true, "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.20.0", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.20.0.tgz", - "integrity": "sha512-lCT675rTN1v8Fo+RGrE5KjSnfY0x9Og4RN7t7lVrN3vMSjy34/+3na0q7RIfWDAj0e0rCh0OL+P88lu3Rt21MQ==", + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.23.0.tgz", + "integrity": "sha512-cRK+YDem7lFTs2Q5nEv/HHc4LnrfBCbH5+JHu6wm2eP+d8OZNoSMYgPZJq78vqQ9g+9+nMuIsAO7skzphRXHyw==", "cpu": [ "x64" ], @@ -4062,15 +3666,30 @@ "os": [ "netbsd" ], - "peer": true, "engines": { - "node": ">=12" + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.23.0.tgz", + "integrity": "sha512-suXjq53gERueVWu0OKxzWqk7NxiUWSUlrxoZK7usiF50C6ipColGR5qie2496iKGYNLhDZkPxBI3erbnYkU0rQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.20.0", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.20.0.tgz", - "integrity": "sha512-HKoUGXz/TOVXKQ+67NhxyHv+aDSZf44QpWLa3I1lLvAwGq8x1k0T+e2HHSRvxWhfJrFxaaqre1+YyzQ99KixoA==", + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.23.0.tgz", + "integrity": "sha512-6p3nHpby0DM/v15IFKMjAaayFhqnXV52aEmv1whZHX56pdkK+MEaLoQWj+H42ssFarP1PcomVhbsR4pkz09qBg==", "cpu": [ "x64" ], @@ -4079,15 +3698,14 @@ "os": [ "openbsd" ], - "peer": true, "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.20.0", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.20.0.tgz", - "integrity": "sha512-GDwAqgHQm1mVoPppGsoq4WJwT3vhnz/2N62CzhvApFD1eJyTroob30FPpOZabN+FgCjhG+AgcZyOPIkR8dfD7g==", + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.23.0.tgz", + "integrity": "sha512-BFelBGfrBwk6LVrmFzCq1u1dZbG4zy/Kp93w2+y83Q5UGYF1d8sCzeLI9NXjKyujjBBniQa8R8PzLFAUrSM9OA==", "cpu": [ "x64" ], @@ -4096,15 +3714,14 @@ "os": [ "sunos" ], - "peer": true, "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.20.0", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.20.0.tgz", - "integrity": "sha512-0vYsP8aC4TvMlOQYozoksiaxjlvUcQrac+muDqj1Fxy6jh9l9CZJzj7zmh8JGfiV49cYLTorFLxg7593pGldwQ==", + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.23.0.tgz", + "integrity": "sha512-lY6AC8p4Cnb7xYHuIxQ6iYPe6MfO2CC43XXKo9nBXDb35krYt7KGhQnOkRGar5psxYkircpCqfbNDB4uJbS2jQ==", "cpu": [ "arm64" ], @@ -4113,15 +3730,14 @@ "os": [ "win32" ], - "peer": true, "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.20.0", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.20.0.tgz", - "integrity": "sha512-p98u4rIgfh4gdpV00IqknBD5pC84LCub+4a3MO+zjqvU5MVXOc3hqR2UgT2jI2nh3h8s9EQxmOsVI3tyzv1iFg==", + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.23.0.tgz", + "integrity": "sha512-7L1bHlOTcO4ByvI7OXVI5pNN6HSu6pUQq9yodga8izeuB1KcT2UkHaH6118QJwopExPn0rMHIseCTx1CRo/uNA==", "cpu": [ "ia32" ], @@ -4130,15 +3746,14 @@ "os": [ "win32" ], - "peer": true, "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/win32-x64": { - "version": "0.20.0", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.20.0.tgz", - "integrity": "sha512-NgJnesu1RtWihtTtXGFMU5YSE6JyyHPMxCwBZK7a6/8d31GuSo9l0Ss7w1Jw5QnKUawG6UEehs883kcXf5fYwg==", + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.23.0.tgz", + "integrity": "sha512-Arm+WgUFLUATuoxCJcahGuk6Yj9Pzxd6l11Zb/2aAuv5kWWvvfhLFo2fni4uSK5vzlUdCGZ/BdV5tH8klj8p8g==", "cpu": [ "x64" ], @@ -4147,197 +3762,10 @@ "os": [ "win32" ], - "peer": true, - "engines": { - "node": ">=12" - } - }, - "node_modules/@eslint-community/eslint-utils": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", - "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", - "dependencies": { - "eslint-visitor-keys": "^3.3.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" - } - }, - "node_modules/@eslint-community/regexpp": { - "version": "4.10.0", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz", - "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==", - "engines": { - "node": "^12.0.0 || ^14.0.0 || >=16.0.0" - } - }, - "node_modules/@eslint/eslintrc": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", - "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", - "dependencies": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^9.6.0", - "globals": "^13.19.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.1.2", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@eslint/eslintrc/node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/@eslint/eslintrc/node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" - }, - "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/@eslint/eslintrc/node_modules/globals": { - "version": "13.24.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", - "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", - "dependencies": { - "type-fest": "^0.20.2" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@eslint/eslintrc/node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/@eslint/eslintrc/node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" - }, - "node_modules/@eslint/eslintrc/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/@eslint/eslintrc/node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@eslint/js": { - "version": "8.57.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.0.tgz", - "integrity": "sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==", - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, - "node_modules/@fal-works/esbuild-plugin-global-externals": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@fal-works/esbuild-plugin-global-externals/-/esbuild-plugin-global-externals-2.1.2.tgz", - "integrity": "sha512-cEee/Z+I12mZcFJshKcCqC8tuX5hG3s+d+9nZ3LabqKF1vKdF41B92pJVCBggjAGORAeOzyyDDKrZwIkLffeOQ==", - "dev": true - }, - "node_modules/@fastify/busboy": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.1.1.tgz", - "integrity": "sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA==", - "dev": true, "engines": { - "node": ">=14" - } - }, - "node_modules/@floating-ui/core": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.0.tgz", - "integrity": "sha512-PcF++MykgmTj3CIyOQbKA/hDzOAiqI3mhuoN44WRCopIs1sgoDoU4oty4Jtqaj/y3oDU6fnVSm4QG0a3t5i0+g==", - "dev": true, - "dependencies": { - "@floating-ui/utils": "^0.2.1" - } - }, - "node_modules/@floating-ui/dom": { - "version": "1.6.3", - "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.3.tgz", - "integrity": "sha512-RnDthu3mzPlQ31Ss/BTwQ1zjzIhr3lk1gZB1OC56h/1vEtaXkESrOqL5fQVMfXpwGtRwX+YsZBdyHtJMQnkArw==", - "dev": true, - "dependencies": { - "@floating-ui/core": "^1.0.0", - "@floating-ui/utils": "^0.2.0" - } - }, - "node_modules/@floating-ui/react-dom": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.0.8.tgz", - "integrity": "sha512-HOdqOt3R3OGeTKidaLvJKcgg75S6tibQ3Tif4eyd91QnIJWr0NLvoXFpJA/j8HqkFSL68GDca9AuyWEHlhyClw==", - "dev": true, - "dependencies": { - "@floating-ui/dom": "^1.6.1" - }, - "peerDependencies": { - "react": ">=16.8.0", - "react-dom": ">=16.8.0" + "node": ">=18" } }, - "node_modules/@floating-ui/utils": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.1.tgz", - "integrity": "sha512-9TANp6GPoMtYzQdt54kfAyMmz1+osLlXdg2ENroU7zzrtflTLrrC/lgrIfaSe+Wu0b89GKccT7vxXA0MoAIO+Q==", - "dev": true - }, "node_modules/@foliojs-fork/fontkit": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/@foliojs-fork/fontkit/-/fontkit-1.9.1.tgz", @@ -4400,112 +3828,297 @@ "integrity": "sha512-59SgoZ3EXbkfSX7b63tsou/SDGzwUEK6MuB5sKqgVK1/XE0fxmpsOb9DQI8LXW3KfGnAjImCGhhEb7uPPAUVNA==", "dev": true }, - "node_modules/@humanwhocodes/config-array": { - "version": "0.11.14", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", - "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==", + "node_modules/@inquirer/checkbox": { + "version": "2.4.7", + "resolved": "https://registry.npmjs.org/@inquirer/checkbox/-/checkbox-2.4.7.tgz", + "integrity": "sha512-5YwCySyV1UEgqzz34gNsC38eKxRBtlRDpJLlKcRtTjlYA/yDKuc1rfw+hjw+2WJxbAZtaDPsRl5Zk7J14SBoBw==", + "dev": true, "dependencies": { - "@humanwhocodes/object-schema": "^2.0.2", - "debug": "^4.3.1", - "minimatch": "^3.0.5" + "@inquirer/core": "^9.0.10", + "@inquirer/figures": "^1.0.5", + "@inquirer/type": "^1.5.2", + "ansi-escapes": "^4.3.2", + "yoctocolors-cjs": "^2.1.2" }, "engines": { - "node": ">=10.10.0" + "node": ">=18" } }, - "node_modules/@humanwhocodes/config-array/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "node_modules/@inquirer/confirm": { + "version": "3.1.22", + "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-3.1.22.tgz", + "integrity": "sha512-gsAKIOWBm2Q87CDfs9fEo7wJT3fwWIJfnDGMn9Qy74gBnNFOACDNfhUzovubbJjWnKLGBln7/NcSmZwj5DuEXg==", + "dev": true, "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "@inquirer/core": "^9.0.10", + "@inquirer/type": "^1.5.2" + }, + "engines": { + "node": ">=18" } }, - "node_modules/@humanwhocodes/config-array/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "node_modules/@inquirer/core": { + "version": "9.0.10", + "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-9.0.10.tgz", + "integrity": "sha512-TdESOKSVwf6+YWDz8GhS6nKscwzkIyakEzCLJ5Vh6O3Co2ClhCJ0A4MG909MUWfaWdpJm7DE45ii51/2Kat9tA==", + "dev": true, "dependencies": { - "brace-expansion": "^1.1.7" + "@inquirer/figures": "^1.0.5", + "@inquirer/type": "^1.5.2", + "@types/mute-stream": "^0.0.4", + "@types/node": "^22.1.0", + "@types/wrap-ansi": "^3.0.0", + "ansi-escapes": "^4.3.2", + "cli-spinners": "^2.9.2", + "cli-width": "^4.1.0", + "mute-stream": "^1.0.0", + "signal-exit": "^4.1.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^6.2.0", + "yoctocolors-cjs": "^2.1.2" }, "engines": { - "node": "*" + "node": ">=18" } }, - "node_modules/@humanwhocodes/module-importer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", - "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "node_modules/@inquirer/core/node_modules/@types/node": { + "version": "22.3.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.3.0.tgz", + "integrity": "sha512-nrWpWVaDZuaVc5X84xJ0vNrLvomM205oQyLsRt7OHNZbSHslcWsvgFR7O7hire2ZonjLrWBbedmotmIlJDVd6g==", + "dev": true, + "dependencies": { + "undici-types": "~6.18.2" + } + }, + "node_modules/@inquirer/core/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, "engines": { - "node": ">=12.22" + "node": ">=14" }, "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/@humanwhocodes/object-schema": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.2.tgz", - "integrity": "sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==" + "node_modules/@inquirer/core/node_modules/undici-types": { + "version": "6.18.2", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.18.2.tgz", + "integrity": "sha512-5ruQbENj95yDYJNS3TvcaxPMshV7aizdv/hWYjGIKoANWKjhWNBsr2YEuYZKodQulB1b8l7ILOuDQep3afowQQ==", + "dev": true }, - "node_modules/@isaacs/cliui": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", - "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "node_modules/@inquirer/editor": { + "version": "2.1.22", + "resolved": "https://registry.npmjs.org/@inquirer/editor/-/editor-2.1.22.tgz", + "integrity": "sha512-K1QwTu7GCK+nKOVRBp5HY9jt3DXOfPGPr6WRDrPImkcJRelG9UTx2cAtK1liXmibRrzJlTWOwqgWT3k2XnS62w==", "dev": true, "dependencies": { - "string-width": "^5.1.2", - "string-width-cjs": "npm:string-width@^4.2.0", - "strip-ansi": "^7.0.1", - "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", - "wrap-ansi": "^8.1.0", - "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + "@inquirer/core": "^9.0.10", + "@inquirer/type": "^1.5.2", + "external-editor": "^3.1.0" }, "engines": { - "node": ">=12" + "node": ">=18" } }, - "node_modules/@isaacs/cliui/node_modules/ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "node_modules/@inquirer/expand": { + "version": "2.1.22", + "resolved": "https://registry.npmjs.org/@inquirer/expand/-/expand-2.1.22.tgz", + "integrity": "sha512-wTZOBkzH+ItPuZ3ZPa9lynBsdMp6kQ9zbjVPYEtSBG7UulGjg2kQiAnUjgyG4SlntpTce5bOmXAPvE4sguXjpA==", "dev": true, - "engines": { - "node": ">=12" + "dependencies": { + "@inquirer/core": "^9.0.10", + "@inquirer/type": "^1.5.2", + "yoctocolors-cjs": "^2.1.2" }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" + "engines": { + "node": ">=18" } }, - "node_modules/@isaacs/cliui/node_modules/ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "node_modules/@inquirer/figures": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-1.0.5.tgz", + "integrity": "sha512-79hP/VWdZ2UVc9bFGJnoQ/lQMpL74mGgzSYX1xUqCVk7/v73vJCMw1VuyWN1jGkZ9B3z7THAbySqGbCNefcjfA==", "dev": true, "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "node": ">=18" } }, - "node_modules/@isaacs/cliui/node_modules/string-width": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "node_modules/@inquirer/input": { + "version": "2.2.9", + "resolved": "https://registry.npmjs.org/@inquirer/input/-/input-2.2.9.tgz", + "integrity": "sha512-7Z6N+uzkWM7+xsE+3rJdhdG/+mQgejOVqspoW+w0AbSZnL6nq5tGMEVASaYVWbkoSzecABWwmludO2evU3d31g==", "dev": true, "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" + "@inquirer/core": "^9.0.10", + "@inquirer/type": "^1.5.2" }, "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=18" + } + }, + "node_modules/@inquirer/number": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/@inquirer/number/-/number-1.0.10.tgz", + "integrity": "sha512-kWTxRF8zHjQOn2TJs+XttLioBih6bdc5CcosXIzZsrTY383PXI35DuhIllZKu7CdXFi2rz2BWPN9l0dPsvrQOA==", + "dev": true, + "dependencies": { + "@inquirer/core": "^9.0.10", + "@inquirer/type": "^1.5.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/password": { + "version": "2.1.22", + "resolved": "https://registry.npmjs.org/@inquirer/password/-/password-2.1.22.tgz", + "integrity": "sha512-5Fxt1L9vh3rAKqjYwqsjU4DZsEvY/2Gll+QkqR4yEpy6wvzLxdSgFhUcxfDAOtO4BEoTreWoznC0phagwLU5Kw==", + "dev": true, + "dependencies": { + "@inquirer/core": "^9.0.10", + "@inquirer/type": "^1.5.2", + "ansi-escapes": "^4.3.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/prompts": { + "version": "5.3.8", + "resolved": "https://registry.npmjs.org/@inquirer/prompts/-/prompts-5.3.8.tgz", + "integrity": "sha512-b2BudQY/Si4Y2a0PdZZL6BeJtl8llgeZa7U2j47aaJSCeAl1e4UI7y8a9bSkO3o/ZbZrgT5muy/34JbsjfIWxA==", + "dev": true, + "dependencies": { + "@inquirer/checkbox": "^2.4.7", + "@inquirer/confirm": "^3.1.22", + "@inquirer/editor": "^2.1.22", + "@inquirer/expand": "^2.1.22", + "@inquirer/input": "^2.2.9", + "@inquirer/number": "^1.0.10", + "@inquirer/password": "^2.1.22", + "@inquirer/rawlist": "^2.2.4", + "@inquirer/search": "^1.0.7", + "@inquirer/select": "^2.4.7" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/rawlist": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/@inquirer/rawlist/-/rawlist-2.2.4.tgz", + "integrity": "sha512-pb6w9pWrm7EfnYDgQObOurh2d2YH07+eDo3xQBsNAM2GRhliz6wFXGi1thKQ4bN6B0xDd6C3tBsjdr3obsCl3Q==", + "dev": true, + "dependencies": { + "@inquirer/core": "^9.0.10", + "@inquirer/type": "^1.5.2", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/search": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@inquirer/search/-/search-1.0.7.tgz", + "integrity": "sha512-p1wpV+3gd1eST/o5N3yQpYEdFNCzSP0Klrl+5bfD3cTTz8BGG6nf4Z07aBW0xjlKIj1Rp0y3x/X4cZYi6TfcLw==", + "dev": true, + "dependencies": { + "@inquirer/core": "^9.0.10", + "@inquirer/figures": "^1.0.5", + "@inquirer/type": "^1.5.2", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/select": { + "version": "2.4.7", + "resolved": "https://registry.npmjs.org/@inquirer/select/-/select-2.4.7.tgz", + "integrity": "sha512-JH7XqPEkBpNWp3gPCqWqY8ECbyMoFcCZANlL6pV9hf59qK6dGmkOlx1ydyhY+KZ0c5X74+W6Mtp+nm2QX0/MAQ==", + "dev": true, + "dependencies": { + "@inquirer/core": "^9.0.10", + "@inquirer/figures": "^1.0.5", + "@inquirer/type": "^1.5.2", + "ansi-escapes": "^4.3.2", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/type": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-1.5.2.tgz", + "integrity": "sha512-w9qFkumYDCNyDZmNQjf/n6qQuvQ4dMC3BJesY4oF+yr0CxR5vxujflAVeIcS6U336uzi9GM0kAfZlLrZ9UTkpA==", + "dev": true, + "dependencies": { + "mute-stream": "^1.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/@isaacs/cliui/node_modules/strip-ansi": { @@ -5294,14 +4907,14 @@ } }, "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.4.tgz", - "integrity": "sha512-Oud2QPM5dHviZNn4y/WhhYKSXksv+1xLEIsNrAbGcFzUN3ubqWRFT5gwPchNc5NuzILOU4tPBDTZ4VwhL8Y7cw==", + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", + "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", "dev": true, "dependencies": { - "@jridgewell/set-array": "^1.0.1", + "@jridgewell/set-array": "^1.2.1", "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" + "@jridgewell/trace-mapping": "^0.3.24" }, "engines": { "node": ">=6.0.0" @@ -5330,252 +4943,437 @@ "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.5.tgz", "integrity": "sha512-UTYAUj/wviwdsMfzoSJspJxbkH5o1snzwX0//0ENX1u/55kkZZkcTZP6u9bwKGkv+dkk9at4m1Cpt0uY80kcpQ==", "dev": true, + "peer": true, "dependencies": { "@jridgewell/gen-mapping": "^0.3.0", "@jridgewell/trace-mapping": "^0.3.9" } }, "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.15", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", - "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", "dev": true }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.23", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.23.tgz", - "integrity": "sha512-9/4foRoUKp8s96tSkh8DlAAc5A0Ty8vLXld+l9gjKKY6ckwI8G15f0hskGmuLZu78ZlGa1vtsfOa+lnB4vG6Jg==", + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", "dev": true, "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, - "node_modules/@juggle/resize-observer": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@juggle/resize-observer/-/resize-observer-3.4.0.tgz", - "integrity": "sha512-dfLbk+PwWvFzSxwk3n5ySL0hfBog779o8h68wK/7/APo/7cgyWp5jcXockbxdk5kFRkbeXWm4Fbi9FrdN381sA==", - "dev": true - }, - "node_modules/@leichtgewicht/ip-codec": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz", - "integrity": "sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A==", - "dev": true - }, - "node_modules/@ljharb/through": { - "version": "2.3.12", - "resolved": "https://registry.npmjs.org/@ljharb/through/-/through-2.3.12.tgz", - "integrity": "sha512-ajo/heTlG3QgC8EGP6APIejksVAYt4ayz4tqoP3MolFELzcH1x1fzwEYRJTPO0IELutZ5HQ0c26/GqAYy79u3g==", + "node_modules/@jsonjoy.com/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-q6XAnWQDIMA3+FTiOYajoYqySkO+JSat0ytXGSuRdq9uXE7o92gzuQwQM14xaCRlBLGq3v5miDGC4vkVTn54xA==", "dev": true, - "dependencies": { - "call-bind": "^1.0.5" - }, + "peer": true, "engines": { - "node": ">= 0.4" - } - }, - "node_modules/@mdx-js/react": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@mdx-js/react/-/react-2.3.0.tgz", - "integrity": "sha512-zQH//gdOmuu7nt2oJR29vFhDv88oGPmVw6BggmrHeMI+xgEkp1B2dX9/bMBSYtK0dyLX/aOmesKS09g222K1/g==", - "dev": true, - "dependencies": { - "@types/mdx": "^2.0.0", - "@types/react": ">=16" + "node": ">=10.0" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "type": "github", + "url": "https://github.com/sponsors/streamich" }, "peerDependencies": { - "react": ">=16" + "tslib": "2" } }, - "node_modules/@ndelangen/get-tarball": { - "version": "3.0.9", - "resolved": "https://registry.npmjs.org/@ndelangen/get-tarball/-/get-tarball-3.0.9.tgz", - "integrity": "sha512-9JKTEik4vq+yGosHYhZ1tiH/3WpUS0Nh0kej4Agndhox8pAdWhEx5knFVRcb/ya9knCRCs1rPxNrSXTDdfVqpA==", + "node_modules/@jsonjoy.com/json-pack": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/json-pack/-/json-pack-1.1.0.tgz", + "integrity": "sha512-zlQONA+msXPPwHWZMKFVS78ewFczIll5lXiVPwFPCZUsrOKdxc2AvxU1HoNBmMRhqDZUR9HkC3UOm+6pME6Xsg==", "dev": true, + "peer": true, "dependencies": { - "gunzip-maybe": "^1.4.2", - "pump": "^3.0.0", - "tar-fs": "^2.1.1" - } - }, - "node_modules/@ngrx/effects": { - "version": "17.1.1", - "resolved": "https://registry.npmjs.org/@ngrx/effects/-/effects-17.1.1.tgz", - "integrity": "sha512-VDNVI70wfEwqoNliffAiMhsPry0CWKkifCLmfzr+SZEEdAaPEBr4FtRrrdcdq/ovmkcgoWukkH2MBljbCyHwtA==", - "dependencies": { - "@ngrx/operators": "17.0.0-beta.0", - "tslib": "^2.0.0" - }, - "peerDependencies": { - "@angular/core": "^17.0.0", - "@ngrx/store": "17.1.1", - "rxjs": "^6.5.3 || ^7.5.0" - } - }, - "node_modules/@ngrx/entity": { - "version": "17.1.1", - "resolved": "https://registry.npmjs.org/@ngrx/entity/-/entity-17.1.1.tgz", - "integrity": "sha512-MNjJY2K0Ci6RS64AEEy4P8nYWVP8EQnt5/Rjy4IQ8IAjw2zFlDSiOJA5xmFcWTk9v0m8x9YZ/7clVGA4Es69kA==", - "dependencies": { - "tslib": "^2.0.0" - }, - "peerDependencies": { - "@angular/core": "^17.0.0", - "@ngrx/store": "17.1.1", - "rxjs": "^6.5.3 || ^7.5.0" - } - }, - "node_modules/@ngrx/operators": { - "version": "17.0.0-beta.0", - "resolved": "https://registry.npmjs.org/@ngrx/operators/-/operators-17.0.0-beta.0.tgz", - "integrity": "sha512-EbO8AONuQ6zo2v/mPyBOi4y0CTAp1x4Z+bx7ZF+Pd8BL5ma53BTCL1TmzaeK5zPUe0yApudLk9/ZbHXPnVox5A==", - "dependencies": { - "tslib": "^2.3.0" - }, - "peerDependencies": { - "rxjs": "^6.5.3 || ^7.4.0" - } - }, - "node_modules/@ngrx/router-store": { - "version": "17.1.1", - "resolved": "https://registry.npmjs.org/@ngrx/router-store/-/router-store-17.1.1.tgz", - "integrity": "sha512-DcCIgtaryYdSREcO9Mgc2Xup9fb9gyB98jB2hAQX2oWVSpwA/4hU6/WGU1mtHyQF0JqmiE2517JBLeqA3owSwQ==", - "dependencies": { - "tslib": "^2.0.0" + "@jsonjoy.com/base64": "^1.1.1", + "@jsonjoy.com/util": "^1.1.2", + "hyperdyperid": "^1.2.0", + "thingies": "^1.20.0" }, - "peerDependencies": { - "@angular/common": "^17.0.0", - "@angular/core": "^17.0.0", - "@angular/router": "^17.0.0", - "@ngrx/store": "17.1.1", - "rxjs": "^6.5.3 || ^7.5.0" - } - }, - "node_modules/@ngrx/schematics": { - "version": "17.1.1", - "resolved": "https://registry.npmjs.org/@ngrx/schematics/-/schematics-17.1.1.tgz", - "integrity": "sha512-DaBU97P+lCRWnphvYkE0qvZW16oCpn43/IA8XktFJa6hJP/JVlqk6cLR5fhfoLL2zdhpPkj9w5vsdqIlXZYXgw==", - "dev": true - }, - "node_modules/@ngrx/store": { - "version": "17.1.1", - "resolved": "https://registry.npmjs.org/@ngrx/store/-/store-17.1.1.tgz", - "integrity": "sha512-MGbKLTcl4uq2Uzx+qbMYNy6xW+JnkpRiznaGFX2/NFflq/zNZsjbxZrvn4/Z6ClVYIjj3uadjM1fupwMYMJxVA==", - "dependencies": { - "tslib": "^2.0.0" + "engines": { + "node": ">=10.0" }, - "peerDependencies": { - "@angular/core": "^17.0.0", - "rxjs": "^6.5.3 || ^7.5.0" - } - }, - "node_modules/@ngrx/store-devtools": { - "version": "17.1.1", - "resolved": "https://registry.npmjs.org/@ngrx/store-devtools/-/store-devtools-17.1.1.tgz", - "integrity": "sha512-MCzM44wHzc0eY6FSvPQwuXn1HFst8Y4Ckq7jvlPA202nJYwMZQQJReI5KQNHfGiqe0RpZKov0FUmy6sIyOOt2A==", - "dependencies": { - "tslib": "^2.0.0" + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" }, "peerDependencies": { - "@ngrx/store": "17.1.1", - "rxjs": "^6.5.3 || ^7.5.0" + "tslib": "2" } }, - "node_modules/@ngtools/webpack": { - "version": "17.2.2", - "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-17.2.2.tgz", - "integrity": "sha512-HgvClGO6WVq4VA5d0ZvlDG5hrj8lQzRH99Gt87URm7G8E5XkatysdOsMqUQsJz+OwFWhP4PvTRWVblpBDiDl/A==", + "node_modules/@jsonjoy.com/util": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/util/-/util-1.3.0.tgz", + "integrity": "sha512-Cebt4Vk7k1xHy87kHY7KSPLT77A7Ev7IfOblyLZhtYEhrdQ6fX4EoLq3xOQ3O/DRMEh2ok5nyC180E+ABS8Wmw==", "dev": true, + "peer": true, "engines": { - "node": "^18.13.0 || >=20.9.0", - "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", - "yarn": ">= 1.13.0" + "node": ">=10.0" }, - "peerDependencies": { - "@angular/compiler-cli": "^17.0.0", - "typescript": ">=5.2 <5.4", - "webpack": "^5.54.0" - } - }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" }, - "engines": { - "node": ">= 8" + "peerDependencies": { + "tslib": "2" } }, - "node_modules/@nodelib/fs.stat": { + "node_modules/@leichtgewicht/ip-codec": { "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "engines": { - "node": ">= 8" - } + "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.5.tgz", + "integrity": "sha512-Vo+PSpZG2/fmgmiNzYK9qWRh8h/CHrwD0mo1h1DzL4yzHNSfWYujGTYsWGreD000gcgmZ7K4Ys6Tx9TxtsKdDw==", + "dev": true, + "peer": true }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "node_modules/@listr2/prompt-adapter-inquirer": { + "version": "2.0.15", + "resolved": "https://registry.npmjs.org/@listr2/prompt-adapter-inquirer/-/prompt-adapter-inquirer-2.0.15.tgz", + "integrity": "sha512-MZrGem/Ujjd4cPTLYDfCZK2iKKeiO/8OX13S6jqxldLs0Prf2aGqVlJ77nMBqMv7fzqgXEgjrNHLXcKR8l9lOg==", + "dev": true, "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" + "@inquirer/type": "^1.5.1" }, "engines": { - "node": ">= 8" + "node": ">=18.0.0" + }, + "peerDependencies": { + "@inquirer/prompts": ">= 3 < 6" } }, - "node_modules/@npmcli/agent": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/@npmcli/agent/-/agent-2.2.1.tgz", - "integrity": "sha512-H4FrOVtNyWC8MUwL3UfjOsAihHvT1Pe8POj3JvjXhSTJipsZMtgUALCT4mGyYZNxymkUfOw3PUj6dE4QPp6osQ==", + "node_modules/@lmdb/lmdb-darwin-arm64": { + "version": "3.0.13", + "resolved": "https://registry.npmjs.org/@lmdb/lmdb-darwin-arm64/-/lmdb-darwin-arm64-3.0.13.tgz", + "integrity": "sha512-uiKPB0Fv6WEEOZjruu9a6wnW/8jrjzlZbxXscMB8kuCJ1k6kHpcBnuvaAWcqhbI7rqX5GKziwWEdD+wi2gNLfA==", + "cpu": [ + "arm64" + ], "dev": true, - "dependencies": { - "agent-base": "^7.1.0", - "http-proxy-agent": "^7.0.0", - "https-proxy-agent": "^7.0.1", - "lru-cache": "^10.0.1", - "socks-proxy-agent": "^8.0.1" - }, - "engines": { - "node": "^16.14.0 || >=18.0.0" - } + "optional": true, + "os": [ + "darwin" + ] }, - "node_modules/@npmcli/agent/node_modules/http-proxy-agent": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", - "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "node_modules/@lmdb/lmdb-darwin-x64": { + "version": "3.0.13", + "resolved": "https://registry.npmjs.org/@lmdb/lmdb-darwin-x64/-/lmdb-darwin-x64-3.0.13.tgz", + "integrity": "sha512-bEVIIfK5mSQoG1R19qA+fJOvCB+0wVGGnXHT3smchBVahYBdlPn2OsZZKzlHWfb1E+PhLBmYfqB5zQXFP7hJig==", + "cpu": [ + "x64" + ], "dev": true, - "dependencies": { + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@lmdb/lmdb-linux-arm": { + "version": "3.0.13", + "resolved": "https://registry.npmjs.org/@lmdb/lmdb-linux-arm/-/lmdb-linux-arm-3.0.13.tgz", + "integrity": "sha512-Yml1KlMzOnXj/tnW7yX8U78iAzTk39aILYvCPbqeewAq1kSzl+w59k/fiVkTBfvDi/oW/5YRxL+Fq+Y1Fr1r2Q==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@lmdb/lmdb-linux-arm64": { + "version": "3.0.13", + "resolved": "https://registry.npmjs.org/@lmdb/lmdb-linux-arm64/-/lmdb-linux-arm64-3.0.13.tgz", + "integrity": "sha512-afbVrsMgZ9dUTNUchFpj5VkmJRxvht/u335jUJ7o23YTbNbnpmXif3VKQGCtnjSh+CZaqm6N3CPG8KO3zwyZ1Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@lmdb/lmdb-linux-x64": { + "version": "3.0.13", + "resolved": "https://registry.npmjs.org/@lmdb/lmdb-linux-x64/-/lmdb-linux-x64-3.0.13.tgz", + "integrity": "sha512-vOtxu0xC0SLdQ2WRXg8Qgd8T32ak4SPqk5zjItRszrJk2BdeXqfGxBJbP7o4aOvSPSmSSv46Lr1EP4HXU8v7Kg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@lmdb/lmdb-win32-x64": { + "version": "3.0.13", + "resolved": "https://registry.npmjs.org/@lmdb/lmdb-win32-x64/-/lmdb-win32-x64-3.0.13.tgz", + "integrity": "sha512-UCrMJQY/gJnOl3XgbWRZZUvGGBuKy6i0YNSptgMzHBjs+QYDYR1Mt/RLTOPy4fzzves65O1EDmlL//OzEqoLlA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-darwin-arm64": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-darwin-arm64/-/msgpackr-extract-darwin-arm64-3.0.3.tgz", + "integrity": "sha512-QZHtlVgbAdy2zAqNA9Gu1UpIuI8Xvsd1v8ic6B2pZmeFnFcMWiPLfWXh7TVw4eGEZ/C9TH281KwhVoeQUKbyjw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-darwin-x64": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-darwin-x64/-/msgpackr-extract-darwin-x64-3.0.3.tgz", + "integrity": "sha512-mdzd3AVzYKuUmiWOQ8GNhl64/IoFGol569zNRdkLReh6LRLHOXxU4U8eq0JwaD8iFHdVGqSy4IjFL4reoWCDFw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-linux-arm": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-arm/-/msgpackr-extract-linux-arm-3.0.3.tgz", + "integrity": "sha512-fg0uy/dG/nZEXfYilKoRe7yALaNmHoYeIoJuJ7KJ+YyU2bvY8vPv27f7UKhGRpY6euFYqEVhxCFZgAUNQBM3nw==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-linux-arm64": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-arm64/-/msgpackr-extract-linux-arm64-3.0.3.tgz", + "integrity": "sha512-YxQL+ax0XqBJDZiKimS2XQaf+2wDGVa1enVRGzEvLLVFeqa5kx2bWbtcSXgsxjQB7nRqqIGFIcLteF/sHeVtQg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-linux-x64": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-x64/-/msgpackr-extract-linux-x64-3.0.3.tgz", + "integrity": "sha512-cvwNfbP07pKUfq1uH+S6KJ7dT9K8WOE4ZiAcsrSes+UY55E/0jLYc+vq+DO7jlmqRb5zAggExKm0H7O/CBaesg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-win32-x64": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-win32-x64/-/msgpackr-extract-win32-x64-3.0.3.tgz", + "integrity": "sha512-x0fWaQtYp4E6sktbsdAqnehxDgEc/VwM7uLsRCYWaiGu0ykYdZPiS8zCWdnjHwyiumousxfBm4SO31eXqwEZhQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@ngrx/effects": { + "version": "18.0.2", + "resolved": "https://registry.npmjs.org/@ngrx/effects/-/effects-18.0.2.tgz", + "integrity": "sha512-YojXcOD9Lsq4kl2HCjENccyUM/mOlgBdtddsg9j/ojzSUgu3ZuBVKLN3atrL2TJYkbMX1MN0RzafSkL3TPGFIA==", + "dependencies": { + "@ngrx/operators": "18.0.1", + "tslib": "^2.0.0" + }, + "peerDependencies": { + "@angular/core": "^18.0.0", + "@ngrx/store": "18.0.2", + "rxjs": "^6.5.3 || ^7.5.0" + } + }, + "node_modules/@ngrx/entity": { + "version": "18.0.2", + "resolved": "https://registry.npmjs.org/@ngrx/entity/-/entity-18.0.2.tgz", + "integrity": "sha512-wPV7MK0RnTx//hliCwmWXFaE2qvUbK32uzoElLiBBaLzwFTxeddqAlVrqOw7u15iPeVbPM92Y3pkbavH8f04uw==", + "dependencies": { + "tslib": "^2.0.0" + }, + "peerDependencies": { + "@angular/core": "^18.0.0", + "@ngrx/store": "18.0.2", + "rxjs": "^6.5.3 || ^7.5.0" + } + }, + "node_modules/@ngrx/operators": { + "version": "18.0.1", + "resolved": "https://registry.npmjs.org/@ngrx/operators/-/operators-18.0.1.tgz", + "integrity": "sha512-M+QMrHNKgcuiLaRGZxJ4aQi5/OCRfKC4+T/63dsHyLFZ53/FFpF6a/ytSO1Q+tzOplZ5o99S+i8FVaZqNQ3LmQ==", + "dependencies": { + "tslib": "^2.3.0" + }, + "peerDependencies": { + "rxjs": "^6.5.3 || ^7.4.0" + } + }, + "node_modules/@ngrx/router-store": { + "version": "18.0.2", + "resolved": "https://registry.npmjs.org/@ngrx/router-store/-/router-store-18.0.2.tgz", + "integrity": "sha512-jUrQ/uJJ53x8O1XbN2YxH2GpRREZlwS5gRxlCoc4fWL4Us/uS1/K6+QfRmKBPtpTKBIixqsOb+dIUV5iwBrivA==", + "dependencies": { + "tslib": "^2.0.0" + }, + "peerDependencies": { + "@angular/common": "^18.0.0", + "@angular/core": "^18.0.0", + "@angular/router": "^18.0.0", + "@ngrx/store": "18.0.2", + "rxjs": "^6.5.3 || ^7.5.0" + } + }, + "node_modules/@ngrx/schematics": { + "version": "18.0.2", + "resolved": "https://registry.npmjs.org/@ngrx/schematics/-/schematics-18.0.2.tgz", + "integrity": "sha512-vJZyH6ngJuGL/kd6XBRmp5hh1ik3KiE12CkRWSi7ha7fDNaXIdfGNj02oaXlRG/GvJFQOG/BQlxKkMTUviuaGw==", + "dev": true + }, + "node_modules/@ngrx/store": { + "version": "18.0.2", + "resolved": "https://registry.npmjs.org/@ngrx/store/-/store-18.0.2.tgz", + "integrity": "sha512-ajwv0+njsO4vzArp9esnFvs1wyUb1U1W8E8LSCKrcW2hWWo9o1Pezj+JRsdQwatxHfrrPFuTDyajsl6GQM/JSA==", + "dependencies": { + "tslib": "^2.0.0" + }, + "peerDependencies": { + "@angular/core": "^18.0.0", + "rxjs": "^6.5.3 || ^7.5.0" + } + }, + "node_modules/@ngrx/store-devtools": { + "version": "18.0.2", + "resolved": "https://registry.npmjs.org/@ngrx/store-devtools/-/store-devtools-18.0.2.tgz", + "integrity": "sha512-NcfVGUOGNvl1luV+Lt8KDqqwuwNYYaSbEuGi4mq70Hb4ws3sO6BFL9v+AeHLCQ51vKccL/cYqDNMYe4QMD7UaA==", + "dependencies": { + "tslib": "^2.0.0" + }, + "peerDependencies": { + "@ngrx/store": "18.0.2", + "rxjs": "^6.5.3 || ^7.5.0" + } + }, + "node_modules/@ngtools/webpack": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-18.2.0.tgz", + "integrity": "sha512-a6hbkYzh/KUlI52huiU4vztqIuxzyddg6kJGcelUJx3Ju6MJeziu+XmJ6wqFRvfH89zmJeaSADKsGFQaBHtJLg==", + "dev": true, + "peer": true, + "engines": { + "node": "^18.19.1 || ^20.11.1 || >=22.0.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + }, + "peerDependencies": { + "@angular/compiler-cli": "^18.0.0", + "typescript": ">=5.4 <5.6", + "webpack": "^5.54.0" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@npmcli/agent": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/@npmcli/agent/-/agent-2.2.2.tgz", + "integrity": "sha512-OrcNPXdpSl9UX7qPVRWbmWMCSXrcDa2M9DvrbOTj7ao1S4PlqVFYv9/yLKMkrJKZ/V5A/kDBC690or307i26Og==", + "dev": true, + "dependencies": { "agent-base": "^7.1.0", - "debug": "^4.3.4" + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.1", + "lru-cache": "^10.0.1", + "socks-proxy-agent": "^8.0.3" }, "engines": { - "node": ">= 14" + "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/@npmcli/agent/node_modules/lru-cache": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.0.tgz", - "integrity": "sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==", + "node_modules/@npmcli/agent/node_modules/http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", "dev": true, + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, "engines": { - "node": "14 || >=16.14" + "node": ">= 14" } }, + "node_modules/@npmcli/agent/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true + }, "node_modules/@npmcli/fs": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-3.1.0.tgz", - "integrity": "sha512-7kZUAaLscfgbwBQRbvdMYaZOWyMEcPTH/tJjnyAWJ/dvvs9Ef+CERx/qJb9GExJpl1qipaDGn7KqHnFGGixd0w==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-3.1.1.tgz", + "integrity": "sha512-q9CRWjpHCMIh5sVyefoD1cA7PkvILqCZsnSOEUUivORLjxCO/Irmue2DprETiNgEqktDBZaM1Bi+jrarx1XdCg==", "dev": true, "dependencies": { "semver": "^7.3.5" @@ -5585,15 +5383,16 @@ } }, "node_modules/@npmcli/git": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/@npmcli/git/-/git-5.0.4.tgz", - "integrity": "sha512-nr6/WezNzuYUppzXRaYu/W4aT5rLxdXqEFupbh6e/ovlYFQ8hpu1UUPV3Ir/YTl+74iXl2ZOMlGzudh9ZPUchQ==", + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/@npmcli/git/-/git-5.0.8.tgz", + "integrity": "sha512-liASfw5cqhjNW9UFd+ruwwdEf/lbOAQjLL2XY2dFW/bkJheXDYZgOyul/4gVvEV4BWkTXjYGmDqMw9uegdbJNQ==", "dev": true, "dependencies": { "@npmcli/promise-spawn": "^7.0.0", + "ini": "^4.1.3", "lru-cache": "^10.0.1", "npm-pick-manifest": "^9.0.0", - "proc-log": "^3.0.0", + "proc-log": "^4.0.0", "promise-inflight": "^1.0.1", "promise-retry": "^2.0.1", "semver": "^7.3.5", @@ -5613,13 +5412,10 @@ } }, "node_modules/@npmcli/git/node_modules/lru-cache": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.0.tgz", - "integrity": "sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==", - "dev": true, - "engines": { - "node": "14 || >=16.14" - } + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true }, "node_modules/@npmcli/git/node_modules/which": { "version": "4.0.0", @@ -5637,16 +5433,16 @@ } }, "node_modules/@npmcli/installed-package-contents": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@npmcli/installed-package-contents/-/installed-package-contents-2.0.2.tgz", - "integrity": "sha512-xACzLPhnfD51GKvTOOuNX2/V4G4mz9/1I2MfDoye9kBM3RYe5g2YbscsaGoTlaWqkxeiapBWyseULVKpSVHtKQ==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@npmcli/installed-package-contents/-/installed-package-contents-2.1.0.tgz", + "integrity": "sha512-c8UuGLeZpm69BryRykLuKRyKFZYJsZSCT4aVY5ds4omyZqJ172ApzgfKJ5eV/r3HgLdUYgFVe54KSFVjKoe27w==", "dev": true, "dependencies": { "npm-bundled": "^3.0.0", "npm-normalize-package-bin": "^3.0.0" }, "bin": { - "installed-package-contents": "lib/index.js" + "installed-package-contents": "bin/index.js" }, "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" @@ -5662,9 +5458,9 @@ } }, "node_modules/@npmcli/package-json": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@npmcli/package-json/-/package-json-5.0.0.tgz", - "integrity": "sha512-OI2zdYBLhQ7kpNPaJxiflofYIpkNLi+lnGdzqUOfRmCF3r2l1nadcjtCYMJKv/Utm/ZtlffaUuTiAktPHbc17g==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@npmcli/package-json/-/package-json-5.2.0.tgz", + "integrity": "sha512-qe/kiqqkW0AGtvBjL8TJKZk/eBBSpnJkUWvHdQ9jM2lKHXRYYJuyNpJPlJw3c8QjC2ow6NZYiLExhUaeJelbxQ==", "dev": true, "dependencies": { "@npmcli/git": "^5.0.0", @@ -5672,7 +5468,7 @@ "hosted-git-info": "^7.0.0", "json-parse-even-better-errors": "^3.0.0", "normalize-package-data": "^6.0.0", - "proc-log": "^3.0.0", + "proc-log": "^4.0.0", "semver": "^7.5.3" }, "engines": { @@ -5680,9 +5476,9 @@ } }, "node_modules/@npmcli/promise-spawn": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/@npmcli/promise-spawn/-/promise-spawn-7.0.1.tgz", - "integrity": "sha512-P4KkF9jX3y+7yFUxgcUdDtLy+t4OlDGuEBLNs57AZsfSfg+uV6MLndqGpnl4831ggaEdXwR50XFoZP4VFtHolg==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@npmcli/promise-spawn/-/promise-spawn-7.0.2.tgz", + "integrity": "sha512-xhfYPXoV5Dy4UkY0D+v2KkwvnDfiA/8Mt3sWCGI/hM03NsYIH8ZaG6QzS9x7pje5vHZBZJ2v6VRFVTWACnqcmQ==", "dev": true, "dependencies": { "which": "^4.0.0" @@ -5715,16 +5511,26 @@ "node": "^16.13.0 || >=18.0.0" } }, + "node_modules/@npmcli/redact": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@npmcli/redact/-/redact-2.0.1.tgz", + "integrity": "sha512-YgsR5jCQZhVmTJvjduTOIHph0L73pK8xwMVaDY0PatySqVM9AZj93jpoXYSJqfHFxFkN9dmqTw6OiqExsS3LPw==", + "dev": true, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, "node_modules/@npmcli/run-script": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/@npmcli/run-script/-/run-script-7.0.4.tgz", - "integrity": "sha512-9ApYM/3+rBt9V80aYg6tZfzj3UWdiYyCt7gJUD1VJKvWF5nwKDSICXbYIQbspFTq6TOpbsEtIC0LArB8d9PFmg==", + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@npmcli/run-script/-/run-script-8.1.0.tgz", + "integrity": "sha512-y7efHHwghQfk28G2z3tlZ67pLG0XdfYbcVG26r7YIXALRsrVQcTq4/tdenSmdOrEsNahIYA/eh8aEVROWGFUDg==", "dev": true, "dependencies": { "@npmcli/node-gyp": "^3.0.0", "@npmcli/package-json": "^5.0.0", "@npmcli/promise-spawn": "^7.0.0", "node-gyp": "^10.0.0", + "proc-log": "^4.0.0", "which": "^4.0.0" }, "engines": { @@ -5755,79 +5561,6 @@ "node": "^16.13.0 || >=18.0.0" } }, - "node_modules/@nrwl/devkit": { - "version": "17.2.8", - "resolved": "https://registry.npmjs.org/@nrwl/devkit/-/devkit-17.2.8.tgz", - "integrity": "sha512-l2dFy5LkWqSA45s6pee6CoqJeluH+sjRdVnAAQfjLHRNSx6mFAKblyzq5h1f4P0EUCVVVqLs+kVqmNx5zxYqvw==", - "dev": true, - "dependencies": { - "@nx/devkit": "17.2.8" - } - }, - "node_modules/@nrwl/tao": { - "version": "17.2.8", - "resolved": "https://registry.npmjs.org/@nrwl/tao/-/tao-17.2.8.tgz", - "integrity": "sha512-Qpk5YKeJ+LppPL/wtoDyNGbJs2MsTi6qyX/RdRrEc8lc4bk6Cw3Oul1qTXCI6jT0KzTz+dZtd0zYD/G7okkzvg==", - "dev": true, - "dependencies": { - "nx": "17.2.8", - "tslib": "^2.3.0" - }, - "bin": { - "tao": "index.js" - } - }, - "node_modules/@nx/devkit": { - "version": "17.2.8", - "resolved": "https://registry.npmjs.org/@nx/devkit/-/devkit-17.2.8.tgz", - "integrity": "sha512-6LtiQihtZwqz4hSrtT5cCG5XMCWppG6/B8c1kNksg97JuomELlWyUyVF+sxmeERkcLYFaKPTZytP0L3dmCFXaw==", - "dev": true, - "dependencies": { - "@nrwl/devkit": "17.2.8", - "ejs": "^3.1.7", - "enquirer": "~2.3.6", - "ignore": "^5.0.4", - "semver": "7.5.3", - "tmp": "~0.2.1", - "tslib": "^2.3.0" - }, - "peerDependencies": { - "nx": ">= 16 <= 18" - } - }, - "node_modules/@nx/devkit/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@nx/devkit/node_modules/semver": { - "version": "7.5.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.3.tgz", - "integrity": "sha512-QBlUtyVk/5EeHbi7X0fw6liDZc7BBmEaSYn01fMU1OUYbf6GPsbTtd8WmnqbI20SeycoHSeiybkE/q1Q+qlThQ==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@nx/devkit/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, "node_modules/@nx/nx-darwin-arm64": { "version": "18.0.4", "resolved": "https://registry.npmjs.org/@nx/nx-darwin-arm64/-/nx-darwin-arm64-18.0.4.tgz", @@ -5858,58 +5591,128 @@ "node": ">= 10" } }, - "node_modules/@nx/nx-freebsd-x64": { - "version": "17.2.8", - "resolved": "https://registry.npmjs.org/@nx/nx-freebsd-x64/-/nx-freebsd-x64-17.2.8.tgz", - "integrity": "sha512-YFMgx5Qpp2btCgvaniDGdu7Ctj56bfFvbbaHQWmOeBPK1krNDp2mqp8HK6ZKOfEuDJGOYAp7HDtCLvdZKvJxzA==", + "node_modules/@nx/nx-linux-x64-gnu": { + "version": "18.0.4", + "resolved": "https://registry.npmjs.org/@nx/nx-linux-x64-gnu/-/nx-linux-x64-gnu-18.0.4.tgz", + "integrity": "sha512-BVLkegIwxHnEB64VBraBxyC01D3C3dVNxq2b4iNaqr4mpWNmos+G/mvcTU3NS7W8ZjpBjlXgdEkpgkl2hMKTEA==", "cpu": [ "x64" ], - "dev": true, "optional": true, "os": [ - "freebsd" + "linux" ], "engines": { "node": ">= 10" } }, - "node_modules/@nx/nx-linux-arm-gnueabihf": { - "version": "17.2.8", - "resolved": "https://registry.npmjs.org/@nx/nx-linux-arm-gnueabihf/-/nx-linux-arm-gnueabihf-17.2.8.tgz", - "integrity": "sha512-iN2my6MrhLRkVDtdivQHugK8YmR7URo1wU9UDuHQ55z3tEcny7LV3W9NSsY9UYPK/FrxdDfevj0r2hgSSdhnzA==", + "node_modules/@nx/nx-win32-x64-msvc": { + "version": "18.0.4", + "resolved": "https://registry.npmjs.org/@nx/nx-win32-x64-msvc/-/nx-win32-x64-msvc-18.0.4.tgz", + "integrity": "sha512-FdAdl5buvtUXp8hZVRkK0AZeiCu35l0u+yHsulNViYdh3OXRT1hYJ0CeqpxlLfvbHqB9JzDPtJtG0dpKHH/O0Q==", "cpu": [ - "arm" + "x64" ], - "dev": true, "optional": true, "os": [ - "linux" + "win32" ], "engines": { "node": ">= 10" } }, - "node_modules/@nx/nx-linux-arm64-gnu": { - "version": "17.2.8", - "resolved": "https://registry.npmjs.org/@nx/nx-linux-arm64-gnu/-/nx-linux-arm64-gnu-17.2.8.tgz", - "integrity": "sha512-Iy8BjoW6mOKrSMiTGujUcNdv+xSM1DALTH6y3iLvNDkGbjGK1Re6QNnJAzqcXyDpv32Q4Fc57PmuexyysZxIGg==", + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.20.0.tgz", + "integrity": "sha512-TSpWzflCc4VGAUJZlPpgAJE1+V60MePDQnBd7PPkpuEmOy8i87aL6tinFGKBFKuEDikYpig72QzdT3QPYIi+oA==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.20.0.tgz", + "integrity": "sha512-u00Ro/nok7oGzVuh/FMYfNoGqxU5CPWz1mxV85S2w9LxHR8OoMQBuSk+3BKVIDYgkpeOET5yXkx90OYFc+ytpQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.20.0.tgz", + "integrity": "sha512-uFVfvzvsdGtlSLuL0ZlvPJvl6ZmrH4CBwLGEFPe7hUmf7htGAN+aXo43R/V6LATyxlKVC/m6UsLb7jbG+LG39Q==", "cpu": [ "arm64" ], "dev": true, "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.20.0.tgz", + "integrity": "sha512-xbrMDdlev53vNXexEa6l0LffojxhqDTBeL+VUxuuIXys4x6xyvbKq5XqTXBCEUA8ty8iEJblHvFaWRJTk/icAQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.20.0.tgz", + "integrity": "sha512-jMYvxZwGmoHFBTbr12Xc6wOdc2xA5tF5F2q6t7Rcfab68TT0n+r7dgawD4qhPEvasDsVpQi+MgDzj2faOLsZjA==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, "os": [ "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.20.0.tgz", + "integrity": "sha512-1asSTl4HKuIHIB1GcdFHNNZhxAYEdqML/MW4QmPS4G0ivbEcBr1JKlFLKsIRqjSwOBkdItn3/ZDlyvZ/N6KPlw==", + "cpu": [ + "arm" ], - "engines": { - "node": ">= 10" - } + "dev": true, + "optional": true, + "os": [ + "linux" + ] }, - "node_modules/@nx/nx-linux-arm64-musl": { - "version": "17.2.8", - "resolved": "https://registry.npmjs.org/@nx/nx-linux-arm64-musl/-/nx-linux-arm64-musl-17.2.8.tgz", - "integrity": "sha512-9wkAxWzknjpzdofL1xjtU6qPFF1PHlvKCZI3hgEYJDo4mQiatGI+7Ttko+lx/ZMP6v4+Umjtgq7+qWrApeKamQ==", + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.20.0.tgz", + "integrity": "sha512-COBb8Bkx56KldOYJfMf6wKeYJrtJ9vEgBRAOkfw6Ens0tnmzPqvlpjZiLgkhg6cA3DGzCmLmmd319pmHvKWWlQ==", "cpu": [ "arm64" ], @@ -5917,30 +5720,64 @@ "optional": true, "os": [ "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.20.0.tgz", + "integrity": "sha512-+it+mBSyMslVQa8wSPvBx53fYuZK/oLTu5RJoXogjk6x7Q7sz1GNRsXWjn6SwyJm8E/oMjNVwPhmNdIjwP135Q==", + "cpu": [ + "arm64" ], - "engines": { - "node": ">= 10" - } + "dev": true, + "optional": true, + "os": [ + "linux" + ] }, - "node_modules/@nx/nx-linux-x64-gnu": { - "version": "18.0.4", - "resolved": "https://registry.npmjs.org/@nx/nx-linux-x64-gnu/-/nx-linux-x64-gnu-18.0.4.tgz", - "integrity": "sha512-BVLkegIwxHnEB64VBraBxyC01D3C3dVNxq2b4iNaqr4mpWNmos+G/mvcTU3NS7W8ZjpBjlXgdEkpgkl2hMKTEA==", + "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.20.0.tgz", + "integrity": "sha512-yAMvqhPfGKsAxHN8I4+jE0CpLWD8cv4z7CK7BMmhjDuz606Q2tFKkWRY8bHR9JQXYcoLfopo5TTqzxgPUjUMfw==", "cpu": [ - "x64" + "ppc64" ], + "dev": true, "optional": true, "os": [ "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.20.0.tgz", + "integrity": "sha512-qmuxFpfmi/2SUkAw95TtNq/w/I7Gpjurx609OOOV7U4vhvUhBcftcmXwl3rqAek+ADBwSjIC4IVNLiszoj3dPA==", + "cpu": [ + "riscv64" ], - "engines": { - "node": ">= 10" - } + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.20.0.tgz", + "integrity": "sha512-I0BtGXddHSHjV1mqTNkgUZLnS3WtsqebAXv11D5BZE/gfw5KoyXSAXVqyJximQXNvNzUo4GKlCK/dIwXlz+jlg==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] }, - "node_modules/@nx/nx-linux-x64-musl": { - "version": "17.2.8", - "resolved": "https://registry.npmjs.org/@nx/nx-linux-x64-musl/-/nx-linux-x64-musl-17.2.8.tgz", - "integrity": "sha512-QiakXZ1xBCIptmkGEouLHQbcM4klQkcr+kEaz2PlNwy/sW3gH1b/1c0Ed5J1AN9xgQxWspriAONpScYBRgxdhA==", + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.20.0.tgz", + "integrity": "sha512-y+eoL2I3iphUg9tN9GB6ku1FA8kOfmF4oUEWhztDJ4KXJy1agk/9+pejOuZkNFhRwHAOxMsBPLbXPd6mJiCwew==", "cpu": [ "x64" ], @@ -5948,15 +5785,25 @@ "optional": true, "os": [ "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.20.0.tgz", + "integrity": "sha512-hM3nhW40kBNYUkZb/r9k2FKK+/MnKglX7UYd4ZUy5DJs8/sMsIbqWK2piZtVGE3kcXVNj3B2IrUYROJMMCikNg==", + "cpu": [ + "x64" ], - "engines": { - "node": ">= 10" - } + "dev": true, + "optional": true, + "os": [ + "linux" + ] }, - "node_modules/@nx/nx-win32-arm64-msvc": { - "version": "17.2.8", - "resolved": "https://registry.npmjs.org/@nx/nx-win32-arm64-msvc/-/nx-win32-arm64-msvc-17.2.8.tgz", - "integrity": "sha512-XBWUY/F/GU3vKN9CAxeI15gM4kr3GOBqnzFZzoZC4qJt2hKSSUEWsMgeZtsMgeqEClbi4ZyCCkY7YJgU32WUGA==", + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.20.0.tgz", + "integrity": "sha512-psegMvP+Ik/Bg7QRJbv8w8PAytPA7Uo8fpFjXyCRHWm6Nt42L+JtoqH8eDQ5hRP7/XW2UiIriy1Z46jf0Oa1kA==", "cpu": [ "arm64" ], @@ -5964,741 +5811,564 @@ "optional": true, "os": [ "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.20.0.tgz", + "integrity": "sha512-GabekH3w4lgAJpVxkk7hUzUf2hICSQO0a/BLFA11/RMxQT92MabKAqyubzDZmMOC/hcJNlc+rrypzNzYl4Dx7A==", + "cpu": [ + "ia32" ], - "engines": { - "node": ">= 10" - } + "dev": true, + "optional": true, + "os": [ + "win32" + ] }, - "node_modules/@nx/nx-win32-x64-msvc": { - "version": "18.0.4", - "resolved": "https://registry.npmjs.org/@nx/nx-win32-x64-msvc/-/nx-win32-x64-msvc-18.0.4.tgz", - "integrity": "sha512-FdAdl5buvtUXp8hZVRkK0AZeiCu35l0u+yHsulNViYdh3OXRT1hYJ0CeqpxlLfvbHqB9JzDPtJtG0dpKHH/O0Q==", + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.20.0.tgz", + "integrity": "sha512-aJ1EJSuTdGnM6qbVC4B5DSmozPTqIag9fSzXRNNo+humQLG89XpPgdt16Ia56ORD7s+H8Pmyx44uczDQ0yDzpg==", "cpu": [ "x64" ], + "dev": true, "optional": true, "os": [ "win32" - ], - "engines": { - "node": ">= 10" - } + ] }, - "node_modules/@pkgjs/parseargs": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", - "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "node_modules/@schematics/angular": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-18.2.0.tgz", + "integrity": "sha512-XePvx2ZnxCcAQw5lHVMUrJvm8MXqAWGcMyJDAuQUqNZrPCk3GpCaplWx2n+nPkinYVX2Q2v/DqtvWStQwgU4nA==", "dev": true, - "optional": true, + "dependencies": { + "@angular-devkit/core": "18.2.0", + "@angular-devkit/schematics": "18.2.0", + "jsonc-parser": "3.3.1" + }, "engines": { - "node": ">=14" + "node": "^18.19.1 || ^20.11.1 || >=22.0.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" } }, - "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, - "engines": { - "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + "node_modules/@sentry-internal/browser-utils": { + "version": "8.26.0", + "resolved": "https://registry.npmjs.org/@sentry-internal/browser-utils/-/browser-utils-8.26.0.tgz", + "integrity": "sha512-O2Tj+WK33/ZVp5STnz6ZL0OO+/Idk2KqsH0ITQkQmyZ2z0kdzWOeqK7s7q3/My6rB1GfPcyqPcBBv4dVv92FYQ==", + "license": "MIT", + "dependencies": { + "@sentry/core": "8.26.0", + "@sentry/types": "8.26.0", + "@sentry/utils": "8.26.0" }, - "funding": { - "url": "https://opencollective.com/unts" + "engines": { + "node": ">=14.18" } }, - "node_modules/@radix-ui/number": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@radix-ui/number/-/number-1.0.1.tgz", - "integrity": "sha512-T5gIdVO2mmPW3NNhjNgEP3cqMXjXL9UbO0BzWcXfvdBs+BohbQxvd/K5hSVKmn9/lbTdsQVKbUcP5WLCwvUbBg==", - "dev": true, + "node_modules/@sentry-internal/browser-utils/node_modules/@sentry/core": { + "version": "8.26.0", + "resolved": "https://registry.npmjs.org/@sentry/core/-/core-8.26.0.tgz", + "integrity": "sha512-g/tVmTZD4GNbLFf++hKJfBpcCAtduFEMLnbfa9iT/QEZjlmP+EzY+GsH9bafM5VsNe8DiOUp+kJKWtShzlVdBA==", + "license": "MIT", "dependencies": { - "@babel/runtime": "^7.13.10" + "@sentry/types": "8.26.0", + "@sentry/utils": "8.26.0" + }, + "engines": { + "node": ">=14.18" } }, - "node_modules/@radix-ui/primitive": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.0.1.tgz", - "integrity": "sha512-yQ8oGX2GVsEYMWGxcovu1uGWPCxV5BFfeeYxqPmuAzUyLT9qmaMXSAhXpb0WrspIeqYzdJpkh2vHModJPgRIaw==", - "dev": true, + "node_modules/@sentry-internal/browser-utils/node_modules/@sentry/utils": { + "version": "8.26.0", + "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-8.26.0.tgz", + "integrity": "sha512-xvlPU9Hd2BlyT+FhWHGNwnxWqdVRk2AHnDtVcW4Ma0Ri5EwS+uy4Jeik5UkSv8C5RVb9VlxFmS8LN3I1MPJsLw==", + "license": "MIT", "dependencies": { - "@babel/runtime": "^7.13.10" + "@sentry/types": "8.26.0" + }, + "engines": { + "node": ">=14.18" } }, - "node_modules/@radix-ui/react-arrow": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.0.3.tgz", - "integrity": "sha512-wSP+pHsB/jQRaL6voubsQ/ZlrGBHHrOjmBnr19hxYgtS0WvAFwZhK2WP/YY5yF9uKECCEEDGxuLxq1NBK51wFA==", - "dev": true, + "node_modules/@sentry-internal/replay": { + "version": "8.26.0", + "resolved": "https://registry.npmjs.org/@sentry-internal/replay/-/replay-8.26.0.tgz", + "integrity": "sha512-JDY7W2bswlp5c3483lKP4kcb75fHNwGNfwD8x8FsY9xMjv7nxeXjLpR5cCEk1XqPq2+n6w4j7mJOXhEXGiUIKg==", + "license": "MIT", "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-primitive": "1.0.3" + "@sentry-internal/browser-utils": "8.26.0", + "@sentry/core": "8.26.0", + "@sentry/types": "8.26.0", + "@sentry/utils": "8.26.0" }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0", - "react-dom": "^16.8 || ^17.0 || ^18.0" + "engines": { + "node": ">=14.18" + } + }, + "node_modules/@sentry-internal/replay/node_modules/@sentry/core": { + "version": "8.26.0", + "resolved": "https://registry.npmjs.org/@sentry/core/-/core-8.26.0.tgz", + "integrity": "sha512-g/tVmTZD4GNbLFf++hKJfBpcCAtduFEMLnbfa9iT/QEZjlmP+EzY+GsH9bafM5VsNe8DiOUp+kJKWtShzlVdBA==", + "license": "MIT", + "dependencies": { + "@sentry/types": "8.26.0", + "@sentry/utils": "8.26.0" }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } + "engines": { + "node": ">=14.18" } }, - "node_modules/@radix-ui/react-collection": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.0.3.tgz", - "integrity": "sha512-3SzW+0PW7yBBoQlT8wNcGtaxaD0XSu0uLUFgrtHY08Acx05TaHaOmVLR73c0j/cqpDy53KBMO7s0dx2wmOIDIA==", - "dev": true, + "node_modules/@sentry-internal/replay/node_modules/@sentry/utils": { + "version": "8.26.0", + "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-8.26.0.tgz", + "integrity": "sha512-xvlPU9Hd2BlyT+FhWHGNwnxWqdVRk2AHnDtVcW4Ma0Ri5EwS+uy4Jeik5UkSv8C5RVb9VlxFmS8LN3I1MPJsLw==", + "license": "MIT", "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-compose-refs": "1.0.1", - "@radix-ui/react-context": "1.0.1", - "@radix-ui/react-primitive": "1.0.3", - "@radix-ui/react-slot": "1.0.2" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0", - "react-dom": "^16.8 || ^17.0 || ^18.0" + "@sentry/types": "8.26.0" }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } + "engines": { + "node": ">=14.18" } }, - "node_modules/@radix-ui/react-compose-refs": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.0.1.tgz", - "integrity": "sha512-fDSBgd44FKHa1FRMU59qBMPFcl2PZE+2nmqunj+BWFyYYjnhIDWL2ItDs3rrbJDQOtzt5nIebLCQc4QRfz6LJw==", - "dev": true, + "node_modules/@sentry/angular": { + "version": "8.26.0", + "resolved": "https://registry.npmjs.org/@sentry/angular/-/angular-8.26.0.tgz", + "integrity": "sha512-9YolcJMdEzS6hbImal3jrAbzGZGM7DpmfSOfzt1Cs4bYTD9gCxKRkLyUgiNRIlrIBO7CkdpMrCSY+nEohvCw7A==", + "license": "MIT", "dependencies": { - "@babel/runtime": "^7.13.10" + "@sentry/browser": "8.26.0", + "@sentry/core": "8.26.0", + "@sentry/types": "8.26.0", + "@sentry/utils": "8.26.0", + "tslib": "^2.4.1" }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0" + "engines": { + "node": ">=14.18" }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } + "peerDependencies": { + "@angular/common": ">= 14.x <= 18.x", + "@angular/core": ">= 14.x <= 18.x", + "@angular/router": ">= 14.x <= 18.x", + "rxjs": "^6.5.5 || ^7.x" } }, - "node_modules/@radix-ui/react-context": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.0.1.tgz", - "integrity": "sha512-ebbrdFoYTcuZ0v4wG5tedGnp9tzcV8awzsxYph7gXUyvnNLuTIcCk1q17JEbnVhXAKG9oX3KtchwiMIAYp9NLg==", - "dev": true, + "node_modules/@sentry/angular/node_modules/@sentry-internal/feedback": { + "version": "8.26.0", + "resolved": "https://registry.npmjs.org/@sentry-internal/feedback/-/feedback-8.26.0.tgz", + "integrity": "sha512-hQtw1gg8n6ERK1UH47F7ZI1zOsbhu0J2VX+TrnkpaQR2FgxDW1oe9Ja6oCV4CQKuR4w+1ZI/Kj4imSt0K33kEw==", + "license": "MIT", "dependencies": { - "@babel/runtime": "^7.13.10" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0" + "@sentry/core": "8.26.0", + "@sentry/types": "8.26.0", + "@sentry/utils": "8.26.0" }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } + "engines": { + "node": ">=14.18" } }, - "node_modules/@radix-ui/react-direction": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.0.1.tgz", - "integrity": "sha512-RXcvnXgyvYvBEOhCBuddKecVkoMiI10Jcm5cTI7abJRAHYfFxeu+FBQs/DvdxSYucxR5mna0dNsL6QFlds5TMA==", - "dev": true, + "node_modules/@sentry/angular/node_modules/@sentry-internal/replay-canvas": { + "version": "8.26.0", + "resolved": "https://registry.npmjs.org/@sentry-internal/replay-canvas/-/replay-canvas-8.26.0.tgz", + "integrity": "sha512-2CFQW6f9aJHIo/DqmqYa9PaYoLn1o36ywc0h8oyGrD4oPCbrnE5F++PmTdc71GBODu41HBn/yoCTLmxOD+UjpA==", + "license": "MIT", "dependencies": { - "@babel/runtime": "^7.13.10" + "@sentry-internal/replay": "8.26.0", + "@sentry/core": "8.26.0", + "@sentry/types": "8.26.0", + "@sentry/utils": "8.26.0" }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } + "engines": { + "node": ">=14.18" } }, - "node_modules/@radix-ui/react-dismissable-layer": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.0.4.tgz", - "integrity": "sha512-7UpBa/RKMoHJYjie1gkF1DlK8l1fdU/VKDpoS3rCCo8YBJR294GwcEHyxHw72yvphJ7ld0AXEcSLAzY2F/WyCg==", - "dev": true, + "node_modules/@sentry/angular/node_modules/@sentry/browser": { + "version": "8.26.0", + "resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-8.26.0.tgz", + "integrity": "sha512-e5s6eKlwLZWzTwQcBwqyAGZMMuQROW9Z677VzwkSyREWAIkKjfH2VBxHATnNGc0IVkNHjD7iH3ixo3C0rLKM3w==", + "license": "MIT", "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/primitive": "1.0.1", - "@radix-ui/react-compose-refs": "1.0.1", - "@radix-ui/react-primitive": "1.0.3", - "@radix-ui/react-use-callback-ref": "1.0.1", - "@radix-ui/react-use-escape-keydown": "1.0.3" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0", - "react-dom": "^16.8 || ^17.0 || ^18.0" + "@sentry-internal/browser-utils": "8.26.0", + "@sentry-internal/feedback": "8.26.0", + "@sentry-internal/replay": "8.26.0", + "@sentry-internal/replay-canvas": "8.26.0", + "@sentry/core": "8.26.0", + "@sentry/types": "8.26.0", + "@sentry/utils": "8.26.0" }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } + "engines": { + "node": ">=14.18" } }, - "node_modules/@radix-ui/react-focus-guards": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.0.1.tgz", - "integrity": "sha512-Rect2dWbQ8waGzhMavsIbmSVCgYxkXLxxR3ZvCX79JOglzdEy4JXMb98lq4hPxUbLr77nP0UOGf4rcMU+s1pUA==", - "dev": true, + "node_modules/@sentry/angular/node_modules/@sentry/core": { + "version": "8.26.0", + "resolved": "https://registry.npmjs.org/@sentry/core/-/core-8.26.0.tgz", + "integrity": "sha512-g/tVmTZD4GNbLFf++hKJfBpcCAtduFEMLnbfa9iT/QEZjlmP+EzY+GsH9bafM5VsNe8DiOUp+kJKWtShzlVdBA==", + "license": "MIT", "dependencies": { - "@babel/runtime": "^7.13.10" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0" + "@sentry/types": "8.26.0", + "@sentry/utils": "8.26.0" }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } + "engines": { + "node": ">=14.18" } }, - "node_modules/@radix-ui/react-focus-scope": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.0.3.tgz", - "integrity": "sha512-upXdPfqI4islj2CslyfUBNlaJCPybbqRHAi1KER7Isel9Q2AtSJ0zRBZv8mWQiFXD2nyAJ4BhC3yXgZ6kMBSrQ==", - "dev": true, + "node_modules/@sentry/angular/node_modules/@sentry/utils": { + "version": "8.26.0", + "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-8.26.0.tgz", + "integrity": "sha512-xvlPU9Hd2BlyT+FhWHGNwnxWqdVRk2AHnDtVcW4Ma0Ri5EwS+uy4Jeik5UkSv8C5RVb9VlxFmS8LN3I1MPJsLw==", + "license": "MIT", "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-compose-refs": "1.0.1", - "@radix-ui/react-primitive": "1.0.3", - "@radix-ui/react-use-callback-ref": "1.0.1" + "@sentry/types": "8.26.0" }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0", - "react-dom": "^16.8 || ^17.0 || ^18.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } + "engines": { + "node": ">=14.18" } }, - "node_modules/@radix-ui/react-id": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.0.1.tgz", - "integrity": "sha512-tI7sT/kqYp8p96yGWY1OAnLHrqDgzHefRBKQ2YAkBS5ja7QLcZ9Z/uY7bEjPUatf8RomoXM8/1sMj1IJaE5UzQ==", + "node_modules/@sentry/cli": { + "version": "2.33.1", + "resolved": "https://registry.npmjs.org/@sentry/cli/-/cli-2.33.1.tgz", + "integrity": "sha512-dUlZ4EFh98VFRPJ+f6OW3JEYQ7VvqGNMa0AMcmvk07ePNeK/GicAWmSQE4ZfJTTl80ul6HZw1kY01fGQOQlVRA==", "dev": true, + "hasInstallScript": true, + "license": "BSD-3-Clause", "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-use-layout-effect": "1.0.1" + "https-proxy-agent": "^5.0.0", + "node-fetch": "^2.6.7", + "progress": "^2.0.3", + "proxy-from-env": "^1.1.0", + "which": "^2.0.2" }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0" + "bin": { + "sentry-cli": "bin/sentry-cli" }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } + "engines": { + "node": ">= 10" + }, + "optionalDependencies": { + "@sentry/cli-darwin": "2.33.1", + "@sentry/cli-linux-arm": "2.33.1", + "@sentry/cli-linux-arm64": "2.33.1", + "@sentry/cli-linux-i686": "2.33.1", + "@sentry/cli-linux-x64": "2.33.1", + "@sentry/cli-win32-i686": "2.33.1", + "@sentry/cli-win32-x64": "2.33.1" } }, - "node_modules/@radix-ui/react-popper": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.1.2.tgz", - "integrity": "sha512-1CnGGfFi/bbqtJZZ0P/NQY20xdG3E0LALJaLUEoKwPLwl6PPPfbeiCqMVQnhoFRAxjJj4RpBRJzDmUgsex2tSg==", - "dev": true, - "dependencies": { - "@babel/runtime": "^7.13.10", - "@floating-ui/react-dom": "^2.0.0", - "@radix-ui/react-arrow": "1.0.3", - "@radix-ui/react-compose-refs": "1.0.1", - "@radix-ui/react-context": "1.0.1", - "@radix-ui/react-primitive": "1.0.3", - "@radix-ui/react-use-callback-ref": "1.0.1", - "@radix-ui/react-use-layout-effect": "1.0.1", - "@radix-ui/react-use-rect": "1.0.1", - "@radix-ui/react-use-size": "1.0.1", - "@radix-ui/rect": "1.0.1" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0", - "react-dom": "^16.8 || ^17.0 || ^18.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } + "node_modules/@sentry/cli-darwin": { + "version": "2.33.1", + "resolved": "https://registry.npmjs.org/@sentry/cli-darwin/-/cli-darwin-2.33.1.tgz", + "integrity": "sha512-+4/VIx/E1L2hChj5nGf5MHyEPHUNHJ/HoG5RY+B+vyEutGily1c1+DM2bum7RbD0xs6wKLIyup5F02guzSzG8A==", + "dev": true, + "license": "BSD-3-Clause", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=10" } }, - "node_modules/@radix-ui/react-portal": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.0.3.tgz", - "integrity": "sha512-xLYZeHrWoPmA5mEKEfZZevoVRK/Q43GfzRXkWV6qawIWWK8t6ifIiLQdd7rmQ4Vk1bmI21XhqF9BN3jWf+phpA==", + "node_modules/@sentry/cli-linux-arm": { + "version": "2.33.1", + "resolved": "https://registry.npmjs.org/@sentry/cli-linux-arm/-/cli-linux-arm-2.33.1.tgz", + "integrity": "sha512-zbxEvQju+tgNvzTOt635le4kS/Fbm2XC2RtYbCTs034Vb8xjrAxLnK0z1bQnStUV8BkeBHtsNVrG+NSQDym2wg==", + "cpu": [ + "arm" + ], "dev": true, - "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-primitive": "1.0.3" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0", - "react-dom": "^16.8 || ^17.0 || ^18.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } + "license": "BSD-3-Clause", + "optional": true, + "os": [ + "linux", + "freebsd" + ], + "engines": { + "node": ">=10" } }, - "node_modules/@radix-ui/react-primitive": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-1.0.3.tgz", - "integrity": "sha512-yi58uVyoAcK/Nq1inRY56ZSjKypBNKTa/1mcL8qdl6oJeEaDbOldlzrGn7P6Q3Id5d+SYNGc5AJgc4vGhjs5+g==", + "node_modules/@sentry/cli-linux-arm64": { + "version": "2.33.1", + "resolved": "https://registry.npmjs.org/@sentry/cli-linux-arm64/-/cli-linux-arm64-2.33.1.tgz", + "integrity": "sha512-DbGV56PRKOLsAZJX27Jt2uZ11QfQEMmWB4cIvxkKcFVE+LJP4MVA+MGGRUL6p+Bs1R9ZUuGbpKGtj0JiG6CoXw==", + "cpu": [ + "arm64" + ], "dev": true, - "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-slot": "1.0.2" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0", - "react-dom": "^16.8 || ^17.0 || ^18.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } + "license": "BSD-3-Clause", + "optional": true, + "os": [ + "linux", + "freebsd" + ], + "engines": { + "node": ">=10" } }, - "node_modules/@radix-ui/react-roving-focus": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.0.4.tgz", - "integrity": "sha512-2mUg5Mgcu001VkGy+FfzZyzbmuUWzgWkj3rvv4yu+mLw03+mTzbxZHvfcGyFp2b8EkQeMkpRQ5FiA2Vr2O6TeQ==", + "node_modules/@sentry/cli-linux-i686": { + "version": "2.33.1", + "resolved": "https://registry.npmjs.org/@sentry/cli-linux-i686/-/cli-linux-i686-2.33.1.tgz", + "integrity": "sha512-g2LS4oPXkPWOfKWukKzYp4FnXVRRSwBxhuQ9eSw2peeb58ZIObr4YKGOA/8HJRGkooBJIKGaAR2mH2Pk1TKaiA==", + "cpu": [ + "x86", + "ia32" + ], "dev": true, - "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/primitive": "1.0.1", - "@radix-ui/react-collection": "1.0.3", - "@radix-ui/react-compose-refs": "1.0.1", - "@radix-ui/react-context": "1.0.1", - "@radix-ui/react-direction": "1.0.1", - "@radix-ui/react-id": "1.0.1", - "@radix-ui/react-primitive": "1.0.3", - "@radix-ui/react-use-callback-ref": "1.0.1", - "@radix-ui/react-use-controllable-state": "1.0.1" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0", - "react-dom": "^16.8 || ^17.0 || ^18.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } + "license": "BSD-3-Clause", + "optional": true, + "os": [ + "linux", + "freebsd" + ], + "engines": { + "node": ">=10" } }, - "node_modules/@radix-ui/react-select": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-select/-/react-select-1.2.2.tgz", - "integrity": "sha512-zI7McXr8fNaSrUY9mZe4x/HC0jTLY9fWNhO1oLWYMQGDXuV4UCivIGTxwioSzO0ZCYX9iSLyWmAh/1TOmX3Cnw==", - "dev": true, - "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/number": "1.0.1", - "@radix-ui/primitive": "1.0.1", - "@radix-ui/react-collection": "1.0.3", - "@radix-ui/react-compose-refs": "1.0.1", - "@radix-ui/react-context": "1.0.1", - "@radix-ui/react-direction": "1.0.1", - "@radix-ui/react-dismissable-layer": "1.0.4", - "@radix-ui/react-focus-guards": "1.0.1", - "@radix-ui/react-focus-scope": "1.0.3", - "@radix-ui/react-id": "1.0.1", - "@radix-ui/react-popper": "1.1.2", - "@radix-ui/react-portal": "1.0.3", - "@radix-ui/react-primitive": "1.0.3", - "@radix-ui/react-slot": "1.0.2", - "@radix-ui/react-use-callback-ref": "1.0.1", - "@radix-ui/react-use-controllable-state": "1.0.1", - "@radix-ui/react-use-layout-effect": "1.0.1", - "@radix-ui/react-use-previous": "1.0.1", - "@radix-ui/react-visually-hidden": "1.0.3", - "aria-hidden": "^1.1.1", - "react-remove-scroll": "2.5.5" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0", - "react-dom": "^16.8 || ^17.0 || ^18.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } + "node_modules/@sentry/cli-linux-x64": { + "version": "2.33.1", + "resolved": "https://registry.npmjs.org/@sentry/cli-linux-x64/-/cli-linux-x64-2.33.1.tgz", + "integrity": "sha512-IV3dcYV/ZcvO+VGu9U6kuxSdbsV2kzxaBwWUQxtzxJ+cOa7J8Hn1t0koKGtU53JVZNBa06qJWIcqgl4/pCuKIg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "BSD-3-Clause", + "optional": true, + "os": [ + "linux", + "freebsd" + ], + "engines": { + "node": ">=10" } }, - "node_modules/@radix-ui/react-separator": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-separator/-/react-separator-1.0.3.tgz", - "integrity": "sha512-itYmTy/kokS21aiV5+Z56MZB54KrhPgn6eHDKkFeOLR34HMN2s8PaN47qZZAGnvupcjxHaFZnW4pQEh0BvvVuw==", + "node_modules/@sentry/cli-win32-i686": { + "version": "2.33.1", + "resolved": "https://registry.npmjs.org/@sentry/cli-win32-i686/-/cli-win32-i686-2.33.1.tgz", + "integrity": "sha512-F7cJySvkpzIu7fnLKNHYwBzZYYwlhoDbAUnaFX0UZCN+5DNp/5LwTp37a5TWOsmCaHMZT4i9IO4SIsnNw16/zQ==", + "cpu": [ + "x86", + "ia32" + ], "dev": true, - "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-primitive": "1.0.3" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0", - "react-dom": "^16.8 || ^17.0 || ^18.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } + "license": "BSD-3-Clause", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" } }, - "node_modules/@radix-ui/react-slot": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.0.2.tgz", - "integrity": "sha512-YeTpuq4deV+6DusvVUW4ivBgnkHwECUu0BiN43L5UCDFgdhsRUWAghhTF5MbvNTPzmiFOx90asDSUjWuCNapwg==", + "node_modules/@sentry/cli-win32-x64": { + "version": "2.33.1", + "resolved": "https://registry.npmjs.org/@sentry/cli-win32-x64/-/cli-win32-x64-2.33.1.tgz", + "integrity": "sha512-8VyRoJqtb2uQ8/bFRKNuACYZt7r+Xx0k2wXRGTyH05lCjAiVIXn7DiS2BxHFty7M1QEWUCMNsb/UC/x/Cu2wuA==", + "cpu": [ + "x64" + ], "dev": true, - "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-compose-refs": "1.0.1" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } + "license": "BSD-3-Clause", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" } }, - "node_modules/@radix-ui/react-toggle": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-toggle/-/react-toggle-1.0.3.tgz", - "integrity": "sha512-Pkqg3+Bc98ftZGsl60CLANXQBBQ4W3mTFS9EJvNxKMZ7magklKV69/id1mlAlOFDDfHvlCms0fx8fA4CMKDJHg==", + "node_modules/@sentry/cli/node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", "dev": true, "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/primitive": "1.0.1", - "@radix-ui/react-primitive": "1.0.3", - "@radix-ui/react-use-controllable-state": "1.0.1" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0", - "react-dom": "^16.8 || ^17.0 || ^18.0" + "debug": "4" }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } + "engines": { + "node": ">= 6.0.0" } }, - "node_modules/@radix-ui/react-toggle-group": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@radix-ui/react-toggle-group/-/react-toggle-group-1.0.4.tgz", - "integrity": "sha512-Uaj/M/cMyiyT9Bx6fOZO0SAG4Cls0GptBWiBmBxofmDbNVnYYoyRWj/2M/6VCi/7qcXFWnHhRUfdfZFvvkuu8A==", + "node_modules/@sentry/cli/node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", "dev": true, "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/primitive": "1.0.1", - "@radix-ui/react-context": "1.0.1", - "@radix-ui/react-direction": "1.0.1", - "@radix-ui/react-primitive": "1.0.3", - "@radix-ui/react-roving-focus": "1.0.4", - "@radix-ui/react-toggle": "1.0.3", - "@radix-ui/react-use-controllable-state": "1.0.1" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0", - "react-dom": "^16.8 || ^17.0 || ^18.0" + "agent-base": "6", + "debug": "4" }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } + "engines": { + "node": ">= 6" } }, - "node_modules/@radix-ui/react-toolbar": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@radix-ui/react-toolbar/-/react-toolbar-1.0.4.tgz", - "integrity": "sha512-tBgmM/O7a07xbaEkYJWYTXkIdU/1pW4/KZORR43toC/4XWyBCURK0ei9kMUdp+gTPPKBgYLxXmRSH1EVcIDp8Q==", + "node_modules/@sentry/types": { + "version": "8.26.0", + "resolved": "https://registry.npmjs.org/@sentry/types/-/types-8.26.0.tgz", + "integrity": "sha512-zKmh6SWsJh630rpt7a9vP4Cm4m1C2gDTUqUiH565CajCL/4cePpNWYrNwalSqsOSL7B9OrczA1+n6a6XvND+ng==", + "license": "MIT", + "engines": { + "node": ">=14.18" + } + }, + "node_modules/@sigstore/bundle": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@sigstore/bundle/-/bundle-2.3.2.tgz", + "integrity": "sha512-wueKWDk70QixNLB363yHc2D2ItTgYiMTdPwK8D9dKQMR3ZQ0c35IxP5xnwQ8cNLoCgCRcHf14kE+CLIvNX1zmA==", "dev": true, "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/primitive": "1.0.1", - "@radix-ui/react-context": "1.0.1", - "@radix-ui/react-direction": "1.0.1", - "@radix-ui/react-primitive": "1.0.3", - "@radix-ui/react-roving-focus": "1.0.4", - "@radix-ui/react-separator": "1.0.3", - "@radix-ui/react-toggle-group": "1.0.4" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0", - "react-dom": "^16.8 || ^17.0 || ^18.0" + "@sigstore/protobuf-specs": "^0.3.2" }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } + "engines": { + "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/@radix-ui/react-use-callback-ref": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.0.1.tgz", - "integrity": "sha512-D94LjX4Sp0xJFVaoQOd3OO9k7tpBYNOXdVhkltUbGv2Qb9OXdrg/CpsjlZv7ia14Sylv398LswWBVVu5nqKzAQ==", + "node_modules/@sigstore/core": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@sigstore/core/-/core-1.1.0.tgz", + "integrity": "sha512-JzBqdVIyqm2FRQCulY6nbQzMpJJpSiJ8XXWMhtOX9eKgaXXpfNOF53lzQEjIydlStnd/eFtuC1dW4VYdD93oRg==", "dev": true, - "dependencies": { - "@babel/runtime": "^7.13.10" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } + "engines": { + "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/@radix-ui/react-use-controllable-state": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.0.1.tgz", - "integrity": "sha512-Svl5GY5FQeN758fWKrjM6Qb7asvXeiZltlT4U2gVfl8Gx5UAv2sMR0LWo8yhsIZh2oQ0eFdZ59aoOOMV7b47VA==", + "node_modules/@sigstore/protobuf-specs": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@sigstore/protobuf-specs/-/protobuf-specs-0.3.2.tgz", + "integrity": "sha512-c6B0ehIWxMI8wiS/bj6rHMPqeFvngFV7cDU/MY+B16P9Z3Mp9k8L93eYZ7BYzSickzuqAQqAq0V956b3Ju6mLw==", "dev": true, - "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-use-callback-ref": "1.0.1" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } + "engines": { + "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/@radix-ui/react-use-escape-keydown": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.0.3.tgz", - "integrity": "sha512-vyL82j40hcFicA+M4Ex7hVkB9vHgSse1ZWomAqV2Je3RleKGO5iM8KMOEtfoSB0PnIelMd2lATjTGMYqN5ylTg==", + "node_modules/@sigstore/sign": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@sigstore/sign/-/sign-2.3.2.tgz", + "integrity": "sha512-5Vz5dPVuunIIvC5vBb0APwo7qKA4G9yM48kPWJT+OEERs40md5GoUR1yedwpekWZ4m0Hhw44m6zU+ObsON+iDA==", "dev": true, "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-use-callback-ref": "1.0.1" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0" + "@sigstore/bundle": "^2.3.2", + "@sigstore/core": "^1.0.0", + "@sigstore/protobuf-specs": "^0.3.2", + "make-fetch-happen": "^13.0.1", + "proc-log": "^4.2.0", + "promise-retry": "^2.0.1" }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } + "engines": { + "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/@radix-ui/react-use-layout-effect": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.0.1.tgz", - "integrity": "sha512-v/5RegiJWYdoCvMnITBkNNx6bCj20fiaJnWtRkU18yITptraXjffz5Qbn05uOiQnOvi+dbkznkoaMltz1GnszQ==", + "node_modules/@sigstore/tuf": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/@sigstore/tuf/-/tuf-2.3.4.tgz", + "integrity": "sha512-44vtsveTPUpqhm9NCrbU8CWLe3Vck2HO1PNLw7RIajbB7xhtn5RBPm1VNSCMwqGYHhDsBJG8gDF0q4lgydsJvw==", "dev": true, "dependencies": { - "@babel/runtime": "^7.13.10" + "@sigstore/protobuf-specs": "^0.3.2", + "tuf-js": "^2.2.1" }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } + "engines": { + "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/@radix-ui/react-use-previous": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-previous/-/react-use-previous-1.0.1.tgz", - "integrity": "sha512-cV5La9DPwiQ7S0gf/0qiD6YgNqM5Fk97Kdrlc5yBcrF3jyEZQwm7vYFqMo4IfeHgJXsRaMvLABFtd0OVEmZhDw==", + "node_modules/@sigstore/verify": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@sigstore/verify/-/verify-1.2.1.tgz", + "integrity": "sha512-8iKx79/F73DKbGfRf7+t4dqrc0bRr0thdPrxAtCKWRm/F0tG71i6O1rvlnScncJLLBZHn3h8M3c1BSUAb9yu8g==", "dev": true, "dependencies": { - "@babel/runtime": "^7.13.10" + "@sigstore/bundle": "^2.3.2", + "@sigstore/core": "^1.1.0", + "@sigstore/protobuf-specs": "^0.3.2" }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0" + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/@sinclair/typebox": { + "version": "0.27.8", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", + "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", + "dev": true + }, + "node_modules/@sindresorhus/merge-streams": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-2.3.0.tgz", + "integrity": "sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg==", + "dev": true, + "peer": true, + "engines": { + "node": ">=18" }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@radix-ui/react-use-rect": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-rect/-/react-use-rect-1.0.1.tgz", - "integrity": "sha512-Cq5DLuSiuYVKNU8orzJMbl15TXilTnJKUCltMVQg53BQOF1/C5toAaGrowkgksdBQ9H+SRL23g0HDmg9tvmxXw==", + "node_modules/@sinonjs/commons": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", + "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", "dev": true, "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/rect": "1.0.1" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } + "type-detect": "4.0.8" } }, - "node_modules/@radix-ui/react-use-size": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-size/-/react-use-size-1.0.1.tgz", - "integrity": "sha512-ibay+VqrgcaI6veAojjofPATwledXiSmX+C0KrBk/xgpX9rBzPV3OsfwlhQdUOFbh+LKQorLYT+xTXW9V8yd0g==", + "node_modules/@sinonjs/fake-timers": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", + "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", "dev": true, "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-use-layout-effect": "1.0.1" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } + "@sinonjs/commons": "^3.0.0" } }, - "node_modules/@radix-ui/react-visually-hidden": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.0.3.tgz", - "integrity": "sha512-D4w41yN5YRKtu464TLnByKzMDG/JlMPHtfZgQAu9v6mNakUqGUI9vUrfQKz8NK41VMm/xbZbh76NUTVtIYqOMA==", + "node_modules/@swc/core": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.4.2.tgz", + "integrity": "sha512-vWgY07R/eqj1/a0vsRKLI9o9klGZfpLNOVEnrv4nrccxBgYPjcf22IWwAoaBJ+wpA7Q4fVjCUM8lP0m01dpxcg==", "dev": true, + "hasInstallScript": true, + "optional": true, + "peer": true, "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-primitive": "1.0.3" + "@swc/counter": "^0.1.2", + "@swc/types": "^0.1.5" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/swc" + }, + "optionalDependencies": { + "@swc/core-darwin-arm64": "1.4.2", + "@swc/core-darwin-x64": "1.4.2", + "@swc/core-linux-arm-gnueabihf": "1.4.2", + "@swc/core-linux-arm64-gnu": "1.4.2", + "@swc/core-linux-arm64-musl": "1.4.2", + "@swc/core-linux-x64-gnu": "1.4.2", + "@swc/core-linux-x64-musl": "1.4.2", + "@swc/core-win32-arm64-msvc": "1.4.2", + "@swc/core-win32-ia32-msvc": "1.4.2", + "@swc/core-win32-x64-msvc": "1.4.2" }, "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0", - "react-dom": "^16.8 || ^17.0 || ^18.0" + "@swc/helpers": "^0.5.0" }, "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { + "@swc/helpers": { "optional": true } } }, - "node_modules/@radix-ui/rect": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.0.1.tgz", - "integrity": "sha512-fyrgCaedtvMg9NK3en0pnOYJdtfwxUcNolezkNPUsoX57X8oQk+NkqcvzHXD2uKNij6GXmWU9NDru2IWjrO4BQ==", - "dev": true, - "dependencies": { - "@babel/runtime": "^7.13.10" - } - }, - "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.12.0.tgz", - "integrity": "sha512-+ac02NL/2TCKRrJu2wffk1kZ+RyqxVUlbjSagNgPm94frxtr+XDL12E5Ll1enWskLrtrZ2r8L3wED1orIibV/w==", - "cpu": [ - "arm" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-android-arm64": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.12.0.tgz", - "integrity": "sha512-OBqcX2BMe6nvjQ0Nyp7cC90cnumt8PXmO7Dp3gfAju/6YwG0Tj74z1vKrfRz7qAv23nBcYM8BCbhrsWqO7PzQQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.12.0.tgz", - "integrity": "sha512-X64tZd8dRE/QTrBIEs63kaOBG0b5GVEd3ccoLtyf6IdXtHdh8h+I56C2yC3PtC9Ucnv0CpNFJLqKFVgCYe0lOQ==", + "node_modules/@swc/core-darwin-arm64": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.4.2.tgz", + "integrity": "sha512-1uSdAn1MRK5C1m/TvLZ2RDvr0zLvochgrZ2xL+lRzugLlCTlSA+Q4TWtrZaOz+vnnFVliCpw7c7qu0JouhgQIw==", "cpu": [ "arm64" ], @@ -6706,12 +6376,16 @@ "optional": true, "os": [ "darwin" - ] + ], + "peer": true, + "engines": { + "node": ">=10" + } }, - "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.12.0.tgz", - "integrity": "sha512-cc71KUZoVbUJmGP2cOuiZ9HSOP14AzBAThn3OU+9LcA1+IUqswJyR1cAJj3Mg55HbjZP6OLAIscbQsQLrpgTOg==", + "node_modules/@swc/core-darwin-x64": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.4.2.tgz", + "integrity": "sha512-TYD28+dCQKeuxxcy7gLJUCFLqrwDZnHtC2z7cdeGfZpbI2mbfppfTf2wUPzqZk3gEC96zHd4Yr37V3Tvzar+lQ==", "cpu": [ "x64" ], @@ -6719,12 +6393,16 @@ "optional": true, "os": [ "darwin" - ] + ], + "peer": true, + "engines": { + "node": ">=10" + } }, - "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.12.0.tgz", - "integrity": "sha512-a6w/Y3hyyO6GlpKL2xJ4IOh/7d+APaqLYdMf86xnczU3nurFTaVN9s9jOXQg97BE4nYm/7Ga51rjec5nfRdrvA==", + "node_modules/@swc/core-linux-arm-gnueabihf": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.4.2.tgz", + "integrity": "sha512-Eyqipf7ZPGj0vplKHo8JUOoU1un2sg5PjJMpEesX0k+6HKE2T8pdyeyXODN0YTFqzndSa/J43EEPXm+rHAsLFQ==", "cpu": [ "arm" ], @@ -6732,12 +6410,16 @@ "optional": true, "os": [ "linux" - ] + ], + "peer": true, + "engines": { + "node": ">=10" + } }, - "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.12.0.tgz", - "integrity": "sha512-0fZBq27b+D7Ar5CQMofVN8sggOVhEtzFUwOwPppQt0k+VR+7UHMZZY4y+64WJ06XOhBTKXtQB/Sv0NwQMXyNAA==", + "node_modules/@swc/core-linux-arm64-gnu": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.4.2.tgz", + "integrity": "sha512-wZn02DH8VYPv3FC0ub4my52Rttsus/rFw+UUfzdb3tHMHXB66LqN+rR0ssIOZrH6K+VLN6qpTw9VizjyoH0BxA==", "cpu": [ "arm64" ], @@ -6745,12 +6427,16 @@ "optional": true, "os": [ "linux" - ] + ], + "peer": true, + "engines": { + "node": ">=10" + } }, - "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.12.0.tgz", - "integrity": "sha512-eTvzUS3hhhlgeAv6bfigekzWZjaEX9xP9HhxB0Dvrdbkk5w/b+1Sxct2ZuDxNJKzsRStSq1EaEkVSEe7A7ipgQ==", + "node_modules/@swc/core-linux-arm64-musl": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.4.2.tgz", + "integrity": "sha512-3G0D5z9hUj9bXNcwmA1eGiFTwe5rWkuL3DsoviTj73TKLpk7u64ND0XjEfO0huVv4vVu9H1jodrKb7nvln/dlw==", "cpu": [ "arm64" ], @@ -6758,25 +6444,16 @@ "optional": true, "os": [ "linux" - ] - }, - "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.12.0.tgz", - "integrity": "sha512-ix+qAB9qmrCRiaO71VFfY8rkiAZJL8zQRXveS27HS+pKdjwUfEhqo2+YF2oI+H/22Xsiski+qqwIBxVewLK7sw==", - "cpu": [ - "riscv64" ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] + "peer": true, + "engines": { + "node": ">=10" + } }, - "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.12.0.tgz", - "integrity": "sha512-TenQhZVOtw/3qKOPa7d+QgkeM6xY0LtwzR8OplmyL5LrgTWIXpTQg2Q2ycBf8jm+SFW2Wt/DTn1gf7nFp3ssVA==", + "node_modules/@swc/core-linux-x64-gnu": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.4.2.tgz", + "integrity": "sha512-LFxn9U8cjmYHw3jrdPNqPAkBGglKE3tCZ8rA7hYyp0BFxuo7L2ZcEnPm4RFpmSCCsExFH+LEJWuMGgWERoktvg==", "cpu": [ "x64" ], @@ -6784,12 +6461,16 @@ "optional": true, "os": [ "linux" - ] - }, - "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.12.0.tgz", - "integrity": "sha512-LfFdRhNnW0zdMvdCb5FNuWlls2WbbSridJvxOvYWgSBOYZtgBfW9UGNJG//rwMqTX1xQE9BAodvMH9tAusKDUw==", + ], + "peer": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-x64-musl": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.4.2.tgz", + "integrity": "sha512-dp0fAmreeVVYTUcb4u9njTPrYzKnbIH0EhH2qvC9GOYNNREUu2GezSIDgonjOXkHiTCvopG4xU7y56XtXj4VrQ==", "cpu": [ "x64" ], @@ -6797,12 +6478,16 @@ "optional": true, "os": [ "linux" - ] + ], + "peer": true, + "engines": { + "node": ">=10" + } }, - "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.12.0.tgz", - "integrity": "sha512-JPDxovheWNp6d7AHCgsUlkuCKvtu3RB55iNEkaQcf0ttsDU/JZF+iQnYcQJSk/7PtT4mjjVG8N1kpwnI9SLYaw==", + "node_modules/@swc/core-win32-arm64-msvc": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.4.2.tgz", + "integrity": "sha512-HlVIiLMQkzthAdqMslQhDkoXJ5+AOLUSTV6fm6shFKZKqc/9cJvr4S8UveNERL9zUficA36yM3bbfo36McwnvQ==", "cpu": [ "arm64" ], @@ -6810,12 +6495,16 @@ "optional": true, "os": [ "win32" - ] + ], + "peer": true, + "engines": { + "node": ">=10" + } }, - "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.12.0.tgz", - "integrity": "sha512-fjtuvMWRGJn1oZacG8IPnzIV6GF2/XG+h71FKn76OYFqySXInJtseAqdprVTDTyqPxQOG9Exak5/E9Z3+EJ8ZA==", + "node_modules/@swc/core-win32-ia32-msvc": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.4.2.tgz", + "integrity": "sha512-WCF8faPGjCl4oIgugkp+kL9nl3nUATlzKXCEGFowMEmVVCFM0GsqlmGdPp1pjZoWc9tpYanoXQDnp5IvlDSLhA==", "cpu": [ "ia32" ], @@ -6823,12 +6512,16 @@ "optional": true, "os": [ "win32" - ] + ], + "peer": true, + "engines": { + "node": ">=10" + } }, - "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.12.0.tgz", - "integrity": "sha512-ZYmr5mS2wd4Dew/JjT0Fqi2NPB/ZhZ2VvPp7SmvPZb4Y1CG/LRcS6tcRo2cYU7zLK5A7cdbhWnnWmUjoI4qapg==", + "node_modules/@swc/core-win32-x64-msvc": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.4.2.tgz", + "integrity": "sha512-oV71rwiSpA5xre2C5570BhCsg1HF97SNLsZ/12xv7zayGzqr3yvFALFJN8tHKpqUdCB4FGPjoP3JFdV3i+1wUw==", "cpu": [ "x64" ], @@ -6836,10982 +6529,2774 @@ "optional": true, "os": [ "win32" - ] - }, - "node_modules/@schematics/angular": { - "version": "17.2.2", - "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-17.2.2.tgz", - "integrity": "sha512-Q3VAQ/S4gj8D1JPWgWG4enDdDZUu8mUXWVRG1rOi4sHgOF5zgPieQFp3LXqMUgOncmzbXrctkbO6NKc4N2FAag==", - "dev": true, - "dependencies": { - "@angular-devkit/core": "17.2.2", - "@angular-devkit/schematics": "17.2.2", - "jsonc-parser": "3.2.1" - }, + ], + "peer": true, "engines": { - "node": "^18.13.0 || >=20.9.0", - "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", - "yarn": ">= 1.13.0" + "node": ">=10" } }, - "node_modules/@sentry-internal/feedback": { - "version": "7.103.0", - "resolved": "https://registry.npmjs.org/@sentry-internal/feedback/-/feedback-7.103.0.tgz", - "integrity": "sha512-2nYoCfP7FpiUR+xxO5y5BL2ajHrhM4fL7HSup6QKNn7gI7vLyllYOOuYFNHhSmsXCD0i00U8DBClGLcn+6DQqw==", - "dependencies": { - "@sentry/core": "7.103.0", - "@sentry/types": "7.103.0", - "@sentry/utils": "7.103.0" - }, + "node_modules/@swc/counter": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz", + "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==", + "dev": true, + "optional": true, + "peer": true + }, + "node_modules/@swc/types": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.5.tgz", + "integrity": "sha512-myfUej5naTBWnqOCc/MdVOLVjXUXtIA+NpDrDBKJtLLg2shUjBu3cZmB/85RyitKc55+lUUyl7oRfLOvkr2hsw==", + "dev": true, + "optional": true, + "peer": true + }, + "node_modules/@thednp/event-listener": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@thednp/event-listener/-/event-listener-2.0.4.tgz", + "integrity": "sha512-sc4B7AzYAIvnGnivirq0XyR7LfzEDhGiiB70Q0qdNn8wSJ2pL1buVAsEZxrlc47qRJiBV4YIP+BFkyMm2r3NLg==", + "dev": true, "engines": { - "node": ">=12" + "node": ">=16", + "pnpm": ">=8.6.0" } }, - "node_modules/@sentry-internal/replay-canvas": { - "version": "7.103.0", - "resolved": "https://registry.npmjs.org/@sentry-internal/replay-canvas/-/replay-canvas-7.103.0.tgz", - "integrity": "sha512-EyDRMdlSqtwY8zGFhOWwl+nwwo98hlhJz+bpF5PQ6VmFpbplh6Wqfx2p+cPXQr40TGMMC4+vPFlSWTOMjcO9zQ==", - "dependencies": { - "@sentry/core": "7.103.0", - "@sentry/replay": "7.103.0", - "@sentry/types": "7.103.0", - "@sentry/utils": "7.103.0" - }, + "node_modules/@thednp/shorty": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@thednp/shorty/-/shorty-2.0.0.tgz", + "integrity": "sha512-kwtLivCxYIoFfGIVU4NlZtfdA/zxZ6X8UcWaJrb7XqU3WQ4Q1p5IaZlLBfOVAO06WH5oWE87QUdK/dS56Wnfjg==", + "dev": true, "engines": { - "node": ">=12" + "node": ">=16", + "pnpm": ">=8.6.0" } }, - "node_modules/@sentry-internal/tracing": { - "version": "7.103.0", - "resolved": "https://registry.npmjs.org/@sentry-internal/tracing/-/tracing-7.103.0.tgz", - "integrity": "sha512-sZ/Wao8HOvGaBs7WlOdflMpHGAFkOBWL6hBiirHaOy5d+IDm7n7et5U6zhvcfiyYBO4nY36gy1Tg5mw+aNO0Vw==", - "dependencies": { - "@sentry/core": "7.103.0", - "@sentry/types": "7.103.0", - "@sentry/utils": "7.103.0" - }, + "node_modules/@tootallnate/once": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", + "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", + "dev": true, "engines": { - "node": ">=8" + "node": ">= 10" } }, - "node_modules/@sentry/angular-ivy": { - "version": "7.103.0", - "resolved": "https://registry.npmjs.org/@sentry/angular-ivy/-/angular-ivy-7.103.0.tgz", - "integrity": "sha512-PpcOZ2ZjZDU+zSUeXyfAuxpOBbO8SntbFh+ksk881dMJFeehejhTJvftAFHCeA4Mm3x79ZS/w1UDfgWUPYGrFg==", + "node_modules/@ts-morph/common": { + "version": "0.21.0", + "resolved": "https://registry.npmjs.org/@ts-morph/common/-/common-0.21.0.tgz", + "integrity": "sha512-ES110Mmne5Vi4ypUKrtVQfXFDtCsDXiUiGxF6ILVlE90dDD4fdpC1LSjydl/ml7xJWKSDZwUYD2zkOePMSrPBA==", + "dev": true, "dependencies": { - "@sentry/browser": "7.103.0", - "@sentry/core": "7.103.0", - "@sentry/types": "7.103.0", - "@sentry/utils": "7.103.0", - "tslib": "^2.4.1" - }, - "engines": { - "node": ">=12" - }, - "peerDependencies": { - "@angular/common": ">= 12.x <= 17.x", - "@angular/core": ">= 12.x <= 17.x", - "@angular/router": ">= 12.x <= 17.x", - "rxjs": "^6.5.5 || ^7.x" + "fast-glob": "^3.2.12", + "minimatch": "^7.4.3", + "mkdirp": "^2.1.6", + "path-browserify": "^1.0.1" } }, - "node_modules/@sentry/browser": { - "version": "7.103.0", - "resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-7.103.0.tgz", - "integrity": "sha512-lP3Oplnwo1lY8ltk8SWzQURbxnSfVhYA099mVs1T95sdwXS16Za6SX7Ld/9T506ZW/WyoU4VCq7eKtG2kPFhMQ==", + "node_modules/@ts-morph/common/node_modules/minimatch": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-7.4.6.tgz", + "integrity": "sha512-sBz8G/YjVniEz6lKPNpKxXwazJe4c19fEfV2GDMX6AjFz+MX9uDWIZW8XreVhkFW3fkIdTv/gxWr/Kks5FFAVw==", + "dev": true, "dependencies": { - "@sentry-internal/feedback": "7.103.0", - "@sentry-internal/replay-canvas": "7.103.0", - "@sentry-internal/tracing": "7.103.0", - "@sentry/core": "7.103.0", - "@sentry/replay": "7.103.0", - "@sentry/types": "7.103.0", - "@sentry/utils": "7.103.0" + "brace-expansion": "^2.0.1" }, "engines": { - "node": ">=8" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/@sentry/cli": { - "version": "2.28.6", - "resolved": "https://registry.npmjs.org/@sentry/cli/-/cli-2.28.6.tgz", - "integrity": "sha512-o2Ngz7xXuhwHxMi+4BFgZ4qjkX0tdZeOSIZkFAGnTbRhQe5T8bxq6CcQRLdPhqMgqvDn7XuJ3YlFtD3ZjHvD7g==", - "hasInstallScript": true, - "dependencies": { - "https-proxy-agent": "^5.0.0", - "node-fetch": "^2.6.7", - "progress": "^2.0.3", - "proxy-from-env": "^1.1.0", - "which": "^2.0.2" - }, + "node_modules/@ts-morph/common/node_modules/mkdirp": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-2.1.6.tgz", + "integrity": "sha512-+hEnITedc8LAtIP9u3HJDFIdcLV2vXP33sqLLIzkv1Db1zO/1OxbvYf0Y1OC/S/Qo5dxHXepofhmxL02PsKe+A==", + "dev": true, "bin": { - "sentry-cli": "bin/sentry-cli" + "mkdirp": "dist/cjs/src/bin.js" }, "engines": { - "node": ">= 10" + "node": ">=10" }, - "optionalDependencies": { - "@sentry/cli-darwin": "2.28.6", - "@sentry/cli-linux-arm": "2.28.6", - "@sentry/cli-linux-arm64": "2.28.6", - "@sentry/cli-linux-i686": "2.28.6", - "@sentry/cli-linux-x64": "2.28.6", - "@sentry/cli-win32-i686": "2.28.6", - "@sentry/cli-win32-x64": "2.28.6" + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/@sentry/cli-darwin": { - "version": "2.28.6", - "resolved": "https://registry.npmjs.org/@sentry/cli-darwin/-/cli-darwin-2.28.6.tgz", - "integrity": "sha512-KRf0VvTltHQ5gA7CdbUkaIp222LAk/f1+KqpDzO6nB/jC/tL4sfiy6YyM4uiH6IbVEudB8WpHCECiatmyAqMBA==", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=10" - } + "node_modules/@tsconfig/node10": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", + "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", + "dev": true }, - "node_modules/@sentry/cli-linux-arm": { - "version": "2.28.6", - "resolved": "https://registry.npmjs.org/@sentry/cli-linux-arm/-/cli-linux-arm-2.28.6.tgz", - "integrity": "sha512-ANG7U47yEHD1g3JrfhpT4/MclEvmDZhctWgSP5gVw5X4AlcI87E6dTqccnLgvZjiIAQTaJJAZuSHVVF3Jk403w==", - "cpu": [ - "arm" - ], - "optional": true, - "os": [ - "linux", - "freebsd" - ], - "engines": { - "node": ">=10" - } + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "dev": true }, - "node_modules/@sentry/cli-linux-arm64": { - "version": "2.28.6", - "resolved": "https://registry.npmjs.org/@sentry/cli-linux-arm64/-/cli-linux-arm64-2.28.6.tgz", - "integrity": "sha512-caMDt37FI752n4/3pVltDjlrRlPFCOxK4PHvoZGQ3KFMsai0ZhE/0CLBUMQqfZf0M0r8KB2x7wqLm7xSELjefQ==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "linux", - "freebsd" - ], - "engines": { - "node": ">=10" - } + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true }, - "node_modules/@sentry/cli-linux-i686": { - "version": "2.28.6", - "resolved": "https://registry.npmjs.org/@sentry/cli-linux-i686/-/cli-linux-i686-2.28.6.tgz", - "integrity": "sha512-Tj1+GMc6lFsDRquOqaGKXFpW9QbmNK4TSfynkWKiJxdTEn5jSMlXXfr0r9OQrxu3dCCqEHkhEyU63NYVpgxIPw==", - "cpu": [ - "x86", - "ia32" - ], - "optional": true, - "os": [ - "linux", - "freebsd" - ], + "node_modules/@tsconfig/node16": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", + "dev": true + }, + "node_modules/@tufjs/canonical-json": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@tufjs/canonical-json/-/canonical-json-2.0.0.tgz", + "integrity": "sha512-yVtV8zsdo8qFHe+/3kw81dSLyF7D576A5cCFCi4X7B39tWT7SekaEFUnvnWJHz+9qO7qJTah1JbrDjWKqFtdWA==", + "dev": true, "engines": { - "node": ">=10" + "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/@sentry/cli-linux-x64": { - "version": "2.28.6", - "resolved": "https://registry.npmjs.org/@sentry/cli-linux-x64/-/cli-linux-x64-2.28.6.tgz", - "integrity": "sha512-Dt/Xz784w/z3tEObfyJEMmRIzn0D5qoK53H9kZ6e0yNvJOSKNCSOq5cQk4n1/qeG0K/6SU9dirmvHwFUiVNyYg==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "linux", - "freebsd" - ], + "node_modules/@tufjs/models": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@tufjs/models/-/models-2.0.1.tgz", + "integrity": "sha512-92F7/SFyufn4DXsha9+QfKnN03JGqtMFMXgSHbZOo8JG59WkTni7UzAouNQDf7AuP9OAMxVOPQcqG3sB7w+kkg==", + "dev": true, + "dependencies": { + "@tufjs/canonical-json": "2.0.0", + "minimatch": "^9.0.4" + }, "engines": { - "node": ">=10" + "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/@sentry/cli-win32-i686": { - "version": "2.28.6", - "resolved": "https://registry.npmjs.org/@sentry/cli-win32-i686/-/cli-win32-i686-2.28.6.tgz", - "integrity": "sha512-zkpWtvY3kt+ogVaAbfFr2MEkgMMHJNJUnNMO8Ixce9gh38sybIkDkZNFnVPBXMClJV0APa4QH0EwumYBFZUMuQ==", - "cpu": [ - "x86", - "ia32" - ], - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=10" + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" } }, - "node_modules/@sentry/cli-win32-x64": { - "version": "2.28.6", - "resolved": "https://registry.npmjs.org/@sentry/cli-win32-x64/-/cli-win32-x64-2.28.6.tgz", - "integrity": "sha512-TG2YzZ9JMeNFzbicdr5fbtsusVGACbrEfHmPgzWGDeLUP90mZxiMTjkXsE1X/5jQEQjB2+fyfXloba/Ugo51hA==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@sentry/cli/node_modules/agent-base": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", - "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", - "dependencies": { - "debug": "4" - }, - "engines": { - "node": ">= 6.0.0" - } - }, - "node_modules/@sentry/cli/node_modules/https-proxy-agent": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", - "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", - "dependencies": { - "agent-base": "6", - "debug": "4" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/@sentry/core": { - "version": "7.103.0", - "resolved": "https://registry.npmjs.org/@sentry/core/-/core-7.103.0.tgz", - "integrity": "sha512-LCI+PIDoF/RLqN41fNXum3ilmS6ukni6L7t38vSdibbe2G0804EbPLtOIpv2PkS8E6CFuRW5zOb+8OwEAAtZWw==", + "node_modules/@types/babel__generator": { + "version": "7.6.8", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.8.tgz", + "integrity": "sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==", + "dev": true, "dependencies": { - "@sentry/types": "7.103.0", - "@sentry/utils": "7.103.0" - }, - "engines": { - "node": ">=8" + "@babel/types": "^7.0.0" } }, - "node_modules/@sentry/replay": { - "version": "7.103.0", - "resolved": "https://registry.npmjs.org/@sentry/replay/-/replay-7.103.0.tgz", - "integrity": "sha512-I37komyb+DruQG8lPPPOFxLLbOijNXeTxiWLsIn+KFZqRtKqxxQWdNnk56V4YSTpFzxnMEFMRCpXhncuTWu4LA==", + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, "dependencies": { - "@sentry-internal/tracing": "7.103.0", - "@sentry/core": "7.103.0", - "@sentry/types": "7.103.0", - "@sentry/utils": "7.103.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@sentry/types": { - "version": "7.103.0", - "resolved": "https://registry.npmjs.org/@sentry/types/-/types-7.103.0.tgz", - "integrity": "sha512-NCvKyx8d2AGBQKPARrJemZmZ16DiMo688OEikZg4BbvFNDUzK5Egm2BH0vfLDhbNkU19o3maJowrYo42m8r9Zw==", - "engines": { - "node": ">=8" + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" } }, - "node_modules/@sentry/utils": { - "version": "7.103.0", - "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-7.103.0.tgz", - "integrity": "sha512-phkUJt3F0UOkVq+M4GfdAh2ewI3ASrNiJddx9aO7GnT0aDwwVBHZltnqt95qgAB8W+BipTSt1dAh8yUbbq1Ceg==", + "node_modules/@types/babel__traverse": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.5.tgz", + "integrity": "sha512-WXCyOcRtH37HAUkpXhUduaxdm82b4GSlyTqajXviN4EfiuPgNYR109xMCKvpl6zPIpua0DGlMEDCq+g8EdoheQ==", + "dev": true, "dependencies": { - "@sentry/types": "7.103.0" - }, - "engines": { - "node": ">=8" + "@babel/types": "^7.20.7" } }, - "node_modules/@sigstore/bundle": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@sigstore/bundle/-/bundle-2.2.0.tgz", - "integrity": "sha512-5VI58qgNs76RDrwXNhpmyN/jKpq9evV/7f1XrcqcAfvxDl5SeVY/I5Rmfe96ULAV7/FK5dge9RBKGBJPhL1WsQ==", + "node_modules/@types/body-parser": { + "version": "1.19.5", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz", + "integrity": "sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==", "dev": true, "dependencies": { - "@sigstore/protobuf-specs": "^0.3.0" - }, - "engines": { - "node": "^16.14.0 || >=18.0.0" + "@types/connect": "*", + "@types/node": "*" } }, - "node_modules/@sigstore/core": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@sigstore/core/-/core-1.0.0.tgz", - "integrity": "sha512-dW2qjbWLRKGu6MIDUTBuJwXCnR8zivcSpf5inUzk7y84zqy/dji0/uahppoIgMoKeR+6pUZucrwHfkQQtiG9Rw==", + "node_modules/@types/bonjour": { + "version": "3.5.13", + "resolved": "https://registry.npmjs.org/@types/bonjour/-/bonjour-3.5.13.tgz", + "integrity": "sha512-z9fJ5Im06zvUL548KvYNecEVlA7cVDkGUi6kZusb04mpyEFKCIZJvloCcmpmLaIahDpOQGHaHmG6imtPMmPXGQ==", "dev": true, - "engines": { - "node": "^16.14.0 || >=18.0.0" + "peer": true, + "dependencies": { + "@types/node": "*" } }, - "node_modules/@sigstore/protobuf-specs": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@sigstore/protobuf-specs/-/protobuf-specs-0.3.0.tgz", - "integrity": "sha512-zxiQ66JFOjVvP9hbhGj/F/qNdsZfkGb/dVXSanNRNuAzMlr4MC95voPUBX8//ZNnmv3uSYzdfR/JSkrgvZTGxA==", + "node_modules/@types/connect": { + "version": "3.4.38", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", + "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", "dev": true, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "dependencies": { + "@types/node": "*" } }, - "node_modules/@sigstore/sign": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/@sigstore/sign/-/sign-2.2.3.tgz", - "integrity": "sha512-LqlA+ffyN02yC7RKszCdMTS6bldZnIodiox+IkT8B2f8oRYXCB3LQ9roXeiEL21m64CVH1wyveYAORfD65WoSw==", + "node_modules/@types/connect-history-api-fallback": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.5.4.tgz", + "integrity": "sha512-n6Cr2xS1h4uAulPRdlw6Jl6s1oG8KrVilPN2yUITEs+K48EzMJJ3W1xy8K5eWuFvjp3R74AOIGSmp2UfBJ8HFw==", "dev": true, + "peer": true, "dependencies": { - "@sigstore/bundle": "^2.2.0", - "@sigstore/core": "^1.0.0", - "@sigstore/protobuf-specs": "^0.3.0", - "make-fetch-happen": "^13.0.0" - }, - "engines": { - "node": "^16.14.0 || >=18.0.0" + "@types/express-serve-static-core": "*", + "@types/node": "*" } }, - "node_modules/@sigstore/tuf": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/@sigstore/tuf/-/tuf-2.3.1.tgz", - "integrity": "sha512-9Iv40z652td/QbV0o5n/x25H9w6IYRt2pIGbTX55yFDYlApDQn/6YZomjz6+KBx69rXHLzHcbtTS586mDdFD+Q==", + "node_modules/@types/eslint": { + "version": "8.56.5", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.5.tgz", + "integrity": "sha512-u5/YPJHo1tvkSF2CE0USEkxon82Z5DBy2xR+qfyYNszpX9qcs4sT6uq2kBbj4BXY1+DBGDPnrhMZV3pKWGNukw==", "dev": true, + "peer": true, "dependencies": { - "@sigstore/protobuf-specs": "^0.3.0", - "tuf-js": "^2.2.0" - }, - "engines": { - "node": "^16.14.0 || >=18.0.0" + "@types/estree": "*", + "@types/json-schema": "*" } }, - "node_modules/@sigstore/verify": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@sigstore/verify/-/verify-1.1.0.tgz", - "integrity": "sha512-1fTqnqyTBWvV7cftUUFtDcHPdSox0N3Ub7C0lRyReYx4zZUlNTZjCV+HPy4Lre+r45dV7Qx5JLKvqqsgxuyYfg==", + "node_modules/@types/eslint-scope": { + "version": "3.7.7", + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz", + "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==", "dev": true, + "peer": true, "dependencies": { - "@sigstore/bundle": "^2.2.0", - "@sigstore/core": "^1.0.0", - "@sigstore/protobuf-specs": "^0.3.0" - }, - "engines": { - "node": "^16.14.0 || >=18.0.0" + "@types/eslint": "*", + "@types/estree": "*" } }, - "node_modules/@sinclair/typebox": { - "version": "0.27.8", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", - "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", + "node_modules/@types/estree": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", + "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", "dev": true }, - "node_modules/@sinonjs/commons": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", - "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", + "node_modules/@types/express": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz", + "integrity": "sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==", "dev": true, "dependencies": { - "type-detect": "4.0.8" + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.33", + "@types/qs": "*", + "@types/serve-static": "*" } }, - "node_modules/@sinonjs/fake-timers": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", - "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", + "node_modules/@types/express-serve-static-core": { + "version": "4.17.43", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.43.tgz", + "integrity": "sha512-oaYtiBirUOPQGSWNGPWnzyAFJ0BP3cwvN4oWZQY+zUBwpVIGsKUkpBpSztp74drYcjavs7SKFZ4DX1V2QeN8rg==", "dev": true, "dependencies": { - "@sinonjs/commons": "^3.0.0" + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" } }, - "node_modules/@storybook/addon-actions": { - "version": "7.6.17", - "resolved": "https://registry.npmjs.org/@storybook/addon-actions/-/addon-actions-7.6.17.tgz", - "integrity": "sha512-TBphs4v6LRfyTpFo/WINF0TkMaE3rrNog7wW5mbz6n0j8o53kDN4o9ZEcygSL5zQX43CAaghQTeDCss7ueG7ZQ==", + "node_modules/@types/graceful-fs": { + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", + "integrity": "sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==", "dev": true, "dependencies": { - "@storybook/core-events": "7.6.17", - "@storybook/global": "^5.0.0", - "@types/uuid": "^9.0.1", - "dequal": "^2.0.2", - "polished": "^4.2.2", - "uuid": "^9.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" + "@types/node": "*" } }, - "node_modules/@storybook/addon-backgrounds": { - "version": "7.6.17", - "resolved": "https://registry.npmjs.org/@storybook/addon-backgrounds/-/addon-backgrounds-7.6.17.tgz", - "integrity": "sha512-7dize7x8+37PH77kmt69b0xSaeDqOcZ4fpzW6+hk53hIaCVU26eGs4+j+743Xva31eOgZWNLupUhOpUDc6SqZw==", + "node_modules/@types/html-minifier-terser": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", + "integrity": "sha512-oh/6byDPnL1zeNXFrDXFLyZjkr1MsBG667IM792caf1L2UPOOMf65NFzjUH/ltyfwjAGfs1rsX1eftK0jC/KIg==", + "dev": true, + "optional": true, + "peer": true + }, + "node_modules/@types/http-errors": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz", + "integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==", + "dev": true + }, + "node_modules/@types/http-proxy": { + "version": "1.17.15", + "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.15.tgz", + "integrity": "sha512-25g5atgiVNTIv0LBDTg1H74Hvayx0ajtJPLLcYE3whFv75J0pWNtOBzaXJQgDTmrX1bx5U9YC2w/n65BN1HwRQ==", "dev": true, + "peer": true, "dependencies": { - "@storybook/global": "^5.0.0", - "memoizerific": "^1.11.3", - "ts-dedent": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" + "@types/node": "*" } }, - "node_modules/@storybook/addon-controls": { - "version": "7.6.17", - "resolved": "https://registry.npmjs.org/@storybook/addon-controls/-/addon-controls-7.6.17.tgz", - "integrity": "sha512-zR0aLaUF7FtV/nMRyfniFbCls/e0DAAoXACuOAUAwNAv0lbIS8AyZZiHSmKucCvziUQ6WceeCC7+du3C+9y0rQ==", + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", + "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", + "dev": true + }, + "node_modules/@types/istanbul-lib-report": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", + "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", "dev": true, "dependencies": { - "@storybook/blocks": "7.6.17", - "lodash": "^4.17.21", - "ts-dedent": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "node_modules/@storybook/addon-docs": { - "version": "7.6.17", - "resolved": "https://registry.npmjs.org/@storybook/addon-docs/-/addon-docs-7.6.17.tgz", - "integrity": "sha512-FKa4Mdy7nhgvEVZJHpMkHriDzpVHbohn87zv9NCL+Ctjs1iAmzGwxEm0culszyDS1HN2ToVoY0h8CSi2RSSZqA==", - "dev": true, - "dependencies": { - "@jest/transform": "^29.3.1", - "@mdx-js/react": "^2.1.5", - "@storybook/blocks": "7.6.17", - "@storybook/client-logger": "7.6.17", - "@storybook/components": "7.6.17", - "@storybook/csf-plugin": "7.6.17", - "@storybook/csf-tools": "7.6.17", - "@storybook/global": "^5.0.0", - "@storybook/mdx2-csf": "^1.0.0", - "@storybook/node-logger": "7.6.17", - "@storybook/postinstall": "7.6.17", - "@storybook/preview-api": "7.6.17", - "@storybook/react-dom-shim": "7.6.17", - "@storybook/theming": "7.6.17", - "@storybook/types": "7.6.17", - "fs-extra": "^11.1.0", - "remark-external-links": "^8.0.0", - "remark-slug": "^6.0.0", - "ts-dedent": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" + "@types/istanbul-lib-coverage": "*" } }, - "node_modules/@storybook/addon-essentials": { - "version": "7.6.17", - "resolved": "https://registry.npmjs.org/@storybook/addon-essentials/-/addon-essentials-7.6.17.tgz", - "integrity": "sha512-qlSpamxuYfT2taF953nC9QijGF2pSbg1ewMNpdwLTj16PTZvR/d8NCDMTJujI1bDwM2m18u8Yc43ibh5LEmxCw==", + "node_modules/@types/istanbul-reports": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", + "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", "dev": true, "dependencies": { - "@storybook/addon-actions": "7.6.17", - "@storybook/addon-backgrounds": "7.6.17", - "@storybook/addon-controls": "7.6.17", - "@storybook/addon-docs": "7.6.17", - "@storybook/addon-highlight": "7.6.17", - "@storybook/addon-measure": "7.6.17", - "@storybook/addon-outline": "7.6.17", - "@storybook/addon-toolbars": "7.6.17", - "@storybook/addon-viewport": "7.6.17", - "@storybook/core-common": "7.6.17", - "@storybook/manager-api": "7.6.17", - "@storybook/node-logger": "7.6.17", - "@storybook/preview-api": "7.6.17", - "ts-dedent": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" + "@types/istanbul-lib-report": "*" } }, - "node_modules/@storybook/addon-highlight": { - "version": "7.6.17", - "resolved": "https://registry.npmjs.org/@storybook/addon-highlight/-/addon-highlight-7.6.17.tgz", - "integrity": "sha512-R1yBPUUqGn+60aJakn8q+5Zt34E/gU3n3VmgPdryP0LJUdZ5q1/RZShoVDV+yYQ40htMH6oaCv3OyyPzFAGJ6A==", + "node_modules/@types/jest": { + "version": "29.5.12", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.12.tgz", + "integrity": "sha512-eDC8bTvT/QhYdxJAulQikueigY5AsdBRH2yDKW3yveW7svY3+DzN84/2NUgkw10RTiJbWqZrTtoGVdYlvFJdLw==", "dev": true, "dependencies": { - "@storybook/global": "^5.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" + "expect": "^29.0.0", + "pretty-format": "^29.0.0" } }, - "node_modules/@storybook/addon-interactions": { - "version": "7.6.17", - "resolved": "https://registry.npmjs.org/@storybook/addon-interactions/-/addon-interactions-7.6.17.tgz", - "integrity": "sha512-6zlX+RDQ1PlA6fp7C+hun8t7h2RXfCGs5dGrhEenp2lqnR/rYuUJRC0tmKpkZBb8kZVcbSChzkB/JYkBjBCzpQ==", + "node_modules/@types/jest/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", "dev": true, - "dependencies": { - "@storybook/global": "^5.0.0", - "@storybook/types": "7.6.17", - "jest-mock": "^27.0.6", - "polished": "^4.2.2", - "ts-dedent": "^2.2.0" + "engines": { + "node": ">=10" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/@storybook/addon-links": { - "version": "7.6.17", - "resolved": "https://registry.npmjs.org/@storybook/addon-links/-/addon-links-7.6.17.tgz", - "integrity": "sha512-iFUwKObRn0EKI0zMETsil2p9a/81rCuSMEWECsi+khkCAs1FUnD2cT6Ag5ydcNcBXsdtdfDJdtXQrkw+TSoStQ==", + "node_modules/@types/jest/node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", "dev": true, "dependencies": { - "@storybook/csf": "^0.1.2", - "@storybook/global": "^5.0.0", - "ts-dedent": "^2.0.0" + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0" - }, - "peerDependenciesMeta": { - "react": { - "optional": true - } + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/@storybook/addon-measure": { - "version": "7.6.17", - "resolved": "https://registry.npmjs.org/@storybook/addon-measure/-/addon-measure-7.6.17.tgz", - "integrity": "sha512-O5vnHZNkduvZ95jf1UssbOl6ivIxzl5tv+4EpScPYId7w700bxWsJH+QX7ip6KlrCf2o3iUhmPe8bm05ghG2KA==", + "node_modules/@types/jest/node_modules/react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", + "dev": true + }, + "node_modules/@types/jsdom": { + "version": "20.0.1", + "resolved": "https://registry.npmjs.org/@types/jsdom/-/jsdom-20.0.1.tgz", + "integrity": "sha512-d0r18sZPmMQr1eG35u12FZfhIXNrnsPU/g5wvRKCUf/tOGilKKwYMYGqh33BNR6ba+2gkHw1EUiHoN3mn7E5IQ==", "dev": true, "dependencies": { - "@storybook/global": "^5.0.0", - "tiny-invariant": "^1.3.1" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" + "@types/node": "*", + "@types/tough-cookie": "*", + "parse5": "^7.0.0" } }, - "node_modules/@storybook/addon-outline": { - "version": "7.6.17", - "resolved": "https://registry.npmjs.org/@storybook/addon-outline/-/addon-outline-7.6.17.tgz", - "integrity": "sha512-9o9JXDsYjNaDgz/cY5+jv694+aik/1aiRGGvsCv68e1p/ob0glkGKav4lnJe2VJqD+gCmaARoD8GOJlhoQl8JQ==", + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true + }, + "node_modules/@types/json-server": { + "version": "0.14.7", + "resolved": "https://registry.npmjs.org/@types/json-server/-/json-server-0.14.7.tgz", + "integrity": "sha512-aE1pKwYE3Y3vtj1IHrZVMP+BSeyP+BFgjGAC+iMTFV/yyLnYRFeX1yqTY1Xh0FyTu+xEpRVpeJupdRACuvi1eg==", "dev": true, "dependencies": { - "@storybook/global": "^5.0.0", - "ts-dedent": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" + "@types/connect": "*", + "@types/express": "*", + "@types/lowdb": "*" } }, - "node_modules/@storybook/addon-toolbars": { - "version": "7.6.17", - "resolved": "https://registry.npmjs.org/@storybook/addon-toolbars/-/addon-toolbars-7.6.17.tgz", - "integrity": "sha512-UMrchbUHiyWrh6WuGnpy34Jqzkx/63B+MSgb3CW7YsQaXz64kE0Rol0TNSznnB+mYXplcqH+ndI4r4kFsmgwDg==", - "dev": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } + "node_modules/@types/lodash": { + "version": "4.14.202", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.202.tgz", + "integrity": "sha512-OvlIYQK9tNneDlS0VN54LLd5uiPCBOp7gS5Z0f1mjoJYBrtStzgmJBxONW3U6OZqdtNzZPmn9BS/7WI7BFFcFQ==", + "dev": true }, - "node_modules/@storybook/addon-viewport": { - "version": "7.6.17", - "resolved": "https://registry.npmjs.org/@storybook/addon-viewport/-/addon-viewport-7.6.17.tgz", - "integrity": "sha512-sA0QCcf4QAMixWvn8uvRYPfkKCSl6JajJaAspoPqXSxHEpK7uwOlpg3kqFU5XJJPXD0X957M+ONgNvBzYqSpEw==", + "node_modules/@types/lodash.clonedeep": { + "version": "4.5.9", + "resolved": "https://registry.npmjs.org/@types/lodash.clonedeep/-/lodash.clonedeep-4.5.9.tgz", + "integrity": "sha512-19429mWC+FyaAhOLzsS8kZUsI+/GmBAQ0HFiCPsKGU+7pBXOQWhyrY6xNNDwUSX8SMZMJvuFVMF9O5dQOlQK9Q==", "dev": true, "dependencies": { - "memoizerific": "^1.11.3" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "node_modules/@storybook/angular": { - "version": "7.6.17", - "resolved": "https://registry.npmjs.org/@storybook/angular/-/angular-7.6.17.tgz", - "integrity": "sha512-dKhY7YQrJTitdKfusxBqJHMPezq++vTwdWtKmFHveU8ALS9PJbmMUriuoBQRll9VIwS3E3Y5CyLc7524tYT+3A==", - "dev": true, - "dependencies": { - "@storybook/builder-webpack5": "7.6.17", - "@storybook/cli": "7.6.17", - "@storybook/client-logger": "7.6.17", - "@storybook/core-common": "7.6.17", - "@storybook/core-events": "7.6.17", - "@storybook/core-server": "7.6.17", - "@storybook/core-webpack": "7.6.17", - "@storybook/docs-tools": "7.6.17", - "@storybook/global": "^5.0.0", - "@storybook/node-logger": "7.6.17", - "@storybook/preview-api": "7.6.17", - "@storybook/telemetry": "7.6.17", - "@storybook/types": "7.6.17", - "@types/node": "^18.0.0", - "@types/react": "^16.14.34", - "@types/react-dom": "^16.9.14", - "@types/semver": "^7.3.4", - "@types/webpack-env": "^1.18.0", - "find-up": "^5.0.0", - "read-pkg-up": "^7.0.1", - "semver": "^7.3.7", - "telejson": "^7.2.0", - "ts-dedent": "^2.0.0", - "tsconfig-paths-webpack-plugin": "^4.0.1", - "util-deprecate": "^1.0.2", - "webpack": "5" - }, - "engines": { - "node": ">=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - }, - "peerDependencies": { - "@angular-devkit/architect": ">=0.1400.0 < 0.1800.0", - "@angular-devkit/build-angular": ">=14.1.0 < 18.0.0", - "@angular-devkit/core": ">=14.1.0 < 18.0.0", - "@angular/cli": ">=14.1.0 < 18.0.0", - "@angular/common": ">=14.1.0 < 18.0.0", - "@angular/compiler": ">=14.1.0 < 18.0.0", - "@angular/compiler-cli": ">=14.1.0 < 18.0.0", - "@angular/core": ">=14.1.0 < 18.0.0", - "@angular/forms": ">=14.1.0 < 18.0.0", - "@angular/platform-browser": ">=14.1.0 < 18.0.0", - "@angular/platform-browser-dynamic": ">=14.1.0 < 18.0.0", - "@babel/core": "*", - "rxjs": "^6.0.0 || ^7.4.0", - "typescript": "^4.0.0 || ^5.0.0", - "zone.js": ">= 0.11.1 < 1.0.0" - }, - "peerDependenciesMeta": { - "@angular/cli": { - "optional": true - } + "@types/lodash": "*" } }, - "node_modules/@storybook/angular/node_modules/@types/react": { - "version": "16.14.57", - "resolved": "https://registry.npmjs.org/@types/react/-/react-16.14.57.tgz", - "integrity": "sha512-fuNq/GV1a6GgqSuVuC457vYeTbm4E1CUBQVZwSPxqYnRhIzSXCJ1gGqyv+PKhqLyfbKCga9dXHJDzv+4XE41fw==", + "node_modules/@types/lodash.merge": { + "version": "4.6.9", + "resolved": "https://registry.npmjs.org/@types/lodash.merge/-/lodash.merge-4.6.9.tgz", + "integrity": "sha512-23sHDPmzd59kUgWyKGiOMO2Qb9YtqRO/x4IhkgNUiPQ1+5MUVqi6bCZeq9nBJ17msjIMbEIO5u+XW4Kz6aGUhQ==", "dev": true, "dependencies": { - "@types/prop-types": "*", - "@types/scheduler": "*", - "csstype": "^3.0.2" + "@types/lodash": "*" } }, - "node_modules/@storybook/blocks": { - "version": "7.6.17", - "resolved": "https://registry.npmjs.org/@storybook/blocks/-/blocks-7.6.17.tgz", - "integrity": "sha512-PsNVoe0bX1mMn4Kk3nbKZ0ItDZZ0YJnYAFJ6toAbsyBAbgzg1sce88sQinzvbn58/RT9MPKeWMPB45ZS7ggiNg==", + "node_modules/@types/lowdb": { + "version": "1.0.15", + "resolved": "https://registry.npmjs.org/@types/lowdb/-/lowdb-1.0.15.tgz", + "integrity": "sha512-xaMNIveDCryK4UvnUJOc2BCOH0lPivdvWHrutsLryo9r9Id3RqZq2RDmT4eddiEPYzu7nJMw6nFIcVifcqjWqg==", "dev": true, "dependencies": { - "@storybook/channels": "7.6.17", - "@storybook/client-logger": "7.6.17", - "@storybook/components": "7.6.17", - "@storybook/core-events": "7.6.17", - "@storybook/csf": "^0.1.2", - "@storybook/docs-tools": "7.6.17", - "@storybook/global": "^5.0.0", - "@storybook/manager-api": "7.6.17", - "@storybook/preview-api": "7.6.17", - "@storybook/theming": "7.6.17", - "@storybook/types": "7.6.17", - "@types/lodash": "^4.14.167", - "color-convert": "^2.0.1", - "dequal": "^2.0.2", - "lodash": "^4.17.21", - "markdown-to-jsx": "^7.1.8", - "memoizerific": "^1.11.3", - "polished": "^4.2.2", - "react-colorful": "^5.1.2", - "telejson": "^7.2.0", - "tocbot": "^4.20.1", - "ts-dedent": "^2.0.0", - "util-deprecate": "^1.0.2" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" - } - }, - "node_modules/@storybook/builder-manager": { - "version": "7.6.17", - "resolved": "https://registry.npmjs.org/@storybook/builder-manager/-/builder-manager-7.6.17.tgz", - "integrity": "sha512-Sj8hcDYiPCCMfeLzus37czl0zdrAxAz4IyYam2jBjVymrIrcDAFyL1OCZvnq33ft179QYQWhUs9qwzVmlR/ZWg==", - "dev": true, - "dependencies": { - "@fal-works/esbuild-plugin-global-externals": "^2.1.2", - "@storybook/core-common": "7.6.17", - "@storybook/manager": "7.6.17", - "@storybook/node-logger": "7.6.17", - "@types/ejs": "^3.1.1", - "@types/find-cache-dir": "^3.2.1", - "@yarnpkg/esbuild-plugin-pnp": "^3.0.0-rc.10", - "browser-assert": "^1.2.1", - "ejs": "^3.1.8", - "esbuild": "^0.18.0", - "esbuild-plugin-alias": "^0.2.1", - "express": "^4.17.3", - "find-cache-dir": "^3.0.0", - "fs-extra": "^11.1.0", - "process": "^0.11.10", - "util": "^0.12.4" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" + "@types/lodash": "*" } }, - "node_modules/@storybook/builder-manager/node_modules/@esbuild/android-arm": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.20.tgz", - "integrity": "sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==", - "cpu": [ - "arm" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } + "node_modules/@types/mime": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", + "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", + "dev": true }, - "node_modules/@storybook/builder-manager/node_modules/@esbuild/android-arm64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.18.20.tgz", - "integrity": "sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==", - "cpu": [ - "arm64" - ], + "node_modules/@types/minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-hov8bUuiLiyFPGyFPE1lwWhmzYbirOXQNNo40+y3zow8aFVTeyn3VWL0VFFfdNddA8S4Vf0Tc062rzyNr7Paag==", + "dev": true + }, + "node_modules/@types/mute-stream": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/@types/mute-stream/-/mute-stream-0.0.4.tgz", + "integrity": "sha512-CPM9nzrCPPJHQNA9keH9CVkVI+WR5kMa+7XEs5jcGQ0VoAGnLv242w8lIVgwAEfmE4oufJRaTc9PNLQl0ioAow==", "dev": true, - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" + "dependencies": { + "@types/node": "*" } }, - "node_modules/@storybook/builder-manager/node_modules/@esbuild/android-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.18.20.tgz", - "integrity": "sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==", - "cpu": [ - "x64" - ], + "node_modules/@types/node": { + "version": "20.14.8", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.8.tgz", + "integrity": "sha512-DO+2/jZinXfROG7j7WKFn/3C6nFwxy2lLpgLjEXJz+0XKphZlTLJ14mo8Vfg8X5BWN6XjyESXq+LcYdT7tR3bA==", "dev": true, - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" + "dependencies": { + "undici-types": "~5.26.4" } }, - "node_modules/@storybook/builder-manager/node_modules/@esbuild/darwin-arm64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.18.20.tgz", - "integrity": "sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==", - "cpu": [ - "arm64" - ], + "node_modules/@types/node-forge": { + "version": "1.3.11", + "resolved": "https://registry.npmjs.org/@types/node-forge/-/node-forge-1.3.11.tgz", + "integrity": "sha512-FQx220y22OKNTqaByeBGqHWYz4cl94tpcxeFdvBo3wjG6XPBuZ0BNgNZRV5J5TFmmcsJ4IzsLkmGRiQbnYsBEQ==", "dev": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=12" + "peer": true, + "dependencies": { + "@types/node": "*" } }, - "node_modules/@storybook/builder-manager/node_modules/@esbuild/darwin-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.18.20.tgz", - "integrity": "sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==", - "cpu": [ - "x64" - ], + "node_modules/@types/normalize-package-data": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz", + "integrity": "sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==", + "dev": true + }, + "node_modules/@types/qs": { + "version": "6.9.12", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.12.tgz", + "integrity": "sha512-bZcOkJ6uWrL0Qb2NAWKa7TBU+mJHPzhx9jjLL1KHF+XpzEcR7EXHvjbHlGtR/IsP1vyPrehuS6XqkmaePy//mg==", + "dev": true + }, + "node_modules/@types/range-parser": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", + "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", + "dev": true + }, + "node_modules/@types/retry": { + "version": "0.12.2", + "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.2.tgz", + "integrity": "sha512-XISRgDJ2Tc5q4TRqvgJtzsRkFYNJzZrhTdtMoGVBttwzzQJkPnS3WWTFc7kuDRoPtPakl+T+OfdEUjYJj7Jbow==", "dev": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=12" - } + "peer": true }, - "node_modules/@storybook/builder-manager/node_modules/@esbuild/freebsd-arm64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.20.tgz", - "integrity": "sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==", - "cpu": [ - "arm64" - ], + "node_modules/@types/send": { + "version": "0.17.4", + "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz", + "integrity": "sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==", "dev": true, - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=12" + "dependencies": { + "@types/mime": "^1", + "@types/node": "*" } }, - "node_modules/@storybook/builder-manager/node_modules/@esbuild/freebsd-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.18.20.tgz", - "integrity": "sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==", - "cpu": [ - "x64" - ], + "node_modules/@types/serve-index": { + "version": "1.9.4", + "resolved": "https://registry.npmjs.org/@types/serve-index/-/serve-index-1.9.4.tgz", + "integrity": "sha512-qLpGZ/c2fhSs5gnYsQxtDEq3Oy8SXPClIXkW5ghvAvsNuVSA8k+gCONcUCS/UjLEYvYps+e8uBtfgXgvhwfNug==", "dev": true, - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=12" + "peer": true, + "dependencies": { + "@types/express": "*" } }, - "node_modules/@storybook/builder-manager/node_modules/@esbuild/linux-arm": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.18.20.tgz", - "integrity": "sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==", - "cpu": [ - "arm" - ], + "node_modules/@types/serve-static": { + "version": "1.15.5", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.5.tgz", + "integrity": "sha512-PDRk21MnK70hja/YF8AHfC7yIsiQHn1rcXx7ijCFBX/k+XQJhQT/gw3xekXKJvx+5SXaMMS8oqQy09Mzvz2TuQ==", "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" + "dependencies": { + "@types/http-errors": "*", + "@types/mime": "*", + "@types/node": "*" } }, - "node_modules/@storybook/builder-manager/node_modules/@esbuild/linux-arm64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.18.20.tgz", - "integrity": "sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==", - "cpu": [ - "arm64" - ], + "node_modules/@types/sockjs": { + "version": "0.3.36", + "resolved": "https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.36.tgz", + "integrity": "sha512-MK9V6NzAS1+Ud7JV9lJLFqW85VbC9dq3LmwZCuBe4wBDgKC0Kj/jd8Xl+nSviU+Qc3+m7umHHyHg//2KSa0a0Q==", "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" + "peer": true, + "dependencies": { + "@types/node": "*" } }, - "node_modules/@storybook/builder-manager/node_modules/@esbuild/linux-ia32": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.18.20.tgz", - "integrity": "sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==", - "cpu": [ - "ia32" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@storybook/builder-manager/node_modules/@esbuild/linux-loong64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.18.20.tgz", - "integrity": "sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==", - "cpu": [ - "loong64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@storybook/builder-manager/node_modules/@esbuild/linux-mips64el": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.18.20.tgz", - "integrity": "sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==", - "cpu": [ - "mips64el" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@storybook/builder-manager/node_modules/@esbuild/linux-ppc64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.18.20.tgz", - "integrity": "sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==", - "cpu": [ - "ppc64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@storybook/builder-manager/node_modules/@esbuild/linux-riscv64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.18.20.tgz", - "integrity": "sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==", - "cpu": [ - "riscv64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@storybook/builder-manager/node_modules/@esbuild/linux-s390x": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.18.20.tgz", - "integrity": "sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==", - "cpu": [ - "s390x" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@storybook/builder-manager/node_modules/@esbuild/linux-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.18.20.tgz", - "integrity": "sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@storybook/builder-manager/node_modules/@esbuild/netbsd-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.18.20.tgz", - "integrity": "sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@storybook/builder-manager/node_modules/@esbuild/openbsd-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.18.20.tgz", - "integrity": "sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@storybook/builder-manager/node_modules/@esbuild/sunos-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.18.20.tgz", - "integrity": "sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@storybook/builder-manager/node_modules/@esbuild/win32-arm64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.18.20.tgz", - "integrity": "sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@storybook/builder-manager/node_modules/@esbuild/win32-ia32": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.18.20.tgz", - "integrity": "sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==", - "cpu": [ - "ia32" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@storybook/builder-manager/node_modules/@esbuild/win32-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.18.20.tgz", - "integrity": "sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@storybook/builder-manager/node_modules/esbuild": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.18.20.tgz", - "integrity": "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==", - "dev": true, - "hasInstallScript": true, - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=12" - }, - "optionalDependencies": { - "@esbuild/android-arm": "0.18.20", - "@esbuild/android-arm64": "0.18.20", - "@esbuild/android-x64": "0.18.20", - "@esbuild/darwin-arm64": "0.18.20", - "@esbuild/darwin-x64": "0.18.20", - "@esbuild/freebsd-arm64": "0.18.20", - "@esbuild/freebsd-x64": "0.18.20", - "@esbuild/linux-arm": "0.18.20", - "@esbuild/linux-arm64": "0.18.20", - "@esbuild/linux-ia32": "0.18.20", - "@esbuild/linux-loong64": "0.18.20", - "@esbuild/linux-mips64el": "0.18.20", - "@esbuild/linux-ppc64": "0.18.20", - "@esbuild/linux-riscv64": "0.18.20", - "@esbuild/linux-s390x": "0.18.20", - "@esbuild/linux-x64": "0.18.20", - "@esbuild/netbsd-x64": "0.18.20", - "@esbuild/openbsd-x64": "0.18.20", - "@esbuild/sunos-x64": "0.18.20", - "@esbuild/win32-arm64": "0.18.20", - "@esbuild/win32-ia32": "0.18.20", - "@esbuild/win32-x64": "0.18.20" - } - }, - "node_modules/@storybook/builder-webpack5": { - "version": "7.6.17", - "resolved": "https://registry.npmjs.org/@storybook/builder-webpack5/-/builder-webpack5-7.6.17.tgz", - "integrity": "sha512-GMaBd8/RzivuAmWrYSt9Rga3j8WLcu5LCMYiPVs+XKXsKAC8lTkV0WRWh8Nk6wTmfzsRQ2acwFjSG5oE4ClZKA==", - "dev": true, - "dependencies": { - "@babel/core": "^7.23.2", - "@storybook/channels": "7.6.17", - "@storybook/client-logger": "7.6.17", - "@storybook/core-common": "7.6.17", - "@storybook/core-events": "7.6.17", - "@storybook/core-webpack": "7.6.17", - "@storybook/node-logger": "7.6.17", - "@storybook/preview": "7.6.17", - "@storybook/preview-api": "7.6.17", - "@swc/core": "^1.3.82", - "@types/node": "^18.0.0", - "@types/semver": "^7.3.4", - "babel-loader": "^9.0.0", - "browser-assert": "^1.2.1", - "case-sensitive-paths-webpack-plugin": "^2.4.0", - "cjs-module-lexer": "^1.2.3", - "constants-browserify": "^1.0.0", - "css-loader": "^6.7.1", - "es-module-lexer": "^1.4.1", - "express": "^4.17.3", - "fork-ts-checker-webpack-plugin": "^8.0.0", - "fs-extra": "^11.1.0", - "html-webpack-plugin": "^5.5.0", - "magic-string": "^0.30.5", - "path-browserify": "^1.0.1", - "process": "^0.11.10", - "semver": "^7.3.7", - "style-loader": "^3.3.1", - "swc-loader": "^0.2.3", - "terser-webpack-plugin": "^5.3.1", - "ts-dedent": "^2.0.0", - "url": "^0.11.0", - "util": "^0.12.4", - "util-deprecate": "^1.0.2", - "webpack": "5", - "webpack-dev-middleware": "^6.1.1", - "webpack-hot-middleware": "^2.25.1", - "webpack-virtual-modules": "^0.5.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@storybook/channels": { - "version": "7.6.17", - "resolved": "https://registry.npmjs.org/@storybook/channels/-/channels-7.6.17.tgz", - "integrity": "sha512-GFG40pzaSxk1hUr/J/TMqW5AFDDPUSu+HkeE/oqSWJbOodBOLJzHN6CReJS6y1DjYSZLNFt1jftPWZZInG/XUA==", - "dev": true, - "dependencies": { - "@storybook/client-logger": "7.6.17", - "@storybook/core-events": "7.6.17", - "@storybook/global": "^5.0.0", - "qs": "^6.10.0", - "telejson": "^7.2.0", - "tiny-invariant": "^1.3.1" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "node_modules/@storybook/cli": { - "version": "7.6.17", - "resolved": "https://registry.npmjs.org/@storybook/cli/-/cli-7.6.17.tgz", - "integrity": "sha512-1sCo+nCqyR+nKfTcEidVu8XzNoECC7Y1l+uW38/r7s2f/TdDorXaIGAVrpjbSaXSoQpx5DxYJVaKCcQuOgqwcA==", - "dev": true, - "dependencies": { - "@babel/core": "^7.23.2", - "@babel/preset-env": "^7.23.2", - "@babel/types": "^7.23.0", - "@ndelangen/get-tarball": "^3.0.7", - "@storybook/codemod": "7.6.17", - "@storybook/core-common": "7.6.17", - "@storybook/core-events": "7.6.17", - "@storybook/core-server": "7.6.17", - "@storybook/csf-tools": "7.6.17", - "@storybook/node-logger": "7.6.17", - "@storybook/telemetry": "7.6.17", - "@storybook/types": "7.6.17", - "@types/semver": "^7.3.4", - "@yarnpkg/fslib": "2.10.3", - "@yarnpkg/libzip": "2.3.0", - "chalk": "^4.1.0", - "commander": "^6.2.1", - "cross-spawn": "^7.0.3", - "detect-indent": "^6.1.0", - "envinfo": "^7.7.3", - "execa": "^5.0.0", - "express": "^4.17.3", - "find-up": "^5.0.0", - "fs-extra": "^11.1.0", - "get-npm-tarball-url": "^2.0.3", - "get-port": "^5.1.1", - "giget": "^1.0.0", - "globby": "^11.0.2", - "jscodeshift": "^0.15.1", - "leven": "^3.1.0", - "ora": "^5.4.1", - "prettier": "^2.8.0", - "prompts": "^2.4.0", - "puppeteer-core": "^2.1.1", - "read-pkg-up": "^7.0.1", - "semver": "^7.3.7", - "strip-json-comments": "^3.0.1", - "tempy": "^1.0.1", - "ts-dedent": "^2.0.0", - "util-deprecate": "^1.0.2" - }, - "bin": { - "getstorybook": "bin/index.js", - "sb": "bin/index.js" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "node_modules/@storybook/cli/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@storybook/cli/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, - "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/@storybook/cli/node_modules/commander": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz", - "integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==", - "dev": true, - "engines": { - "node": ">= 6" - } - }, - "node_modules/@storybook/cli/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/@storybook/cli/node_modules/prettier": { - "version": "2.8.8", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", - "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", - "dev": true, - "bin": { - "prettier": "bin-prettier.js" - }, - "engines": { - "node": ">=10.13.0" - }, - "funding": { - "url": "https://github.com/prettier/prettier?sponsor=1" - } - }, - "node_modules/@storybook/cli/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@storybook/client-logger": { - "version": "7.6.17", - "resolved": "https://registry.npmjs.org/@storybook/client-logger/-/client-logger-7.6.17.tgz", - "integrity": "sha512-6WBYqixAXNAXlSaBWwgljWpAu10tPRBJrcFvx2gPUne58EeMM20Gi/iHYBz2kMCY+JLAgeIH7ZxInqwO8vDwiQ==", - "dev": true, - "dependencies": { - "@storybook/global": "^5.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "node_modules/@storybook/codemod": { - "version": "7.6.17", - "resolved": "https://registry.npmjs.org/@storybook/codemod/-/codemod-7.6.17.tgz", - "integrity": "sha512-JuTmf2u3C4fCnjO7o3dqRgrq3ozNYfWlrRP8xuIdvT7niMap7a396hJtSKqS10FxCgKFcMAOsRgrCalH1dWxUg==", - "dev": true, - "dependencies": { - "@babel/core": "^7.23.2", - "@babel/preset-env": "^7.23.2", - "@babel/types": "^7.23.0", - "@storybook/csf": "^0.1.2", - "@storybook/csf-tools": "7.6.17", - "@storybook/node-logger": "7.6.17", - "@storybook/types": "7.6.17", - "@types/cross-spawn": "^6.0.2", - "cross-spawn": "^7.0.3", - "globby": "^11.0.2", - "jscodeshift": "^0.15.1", - "lodash": "^4.17.21", - "prettier": "^2.8.0", - "recast": "^0.23.1" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "node_modules/@storybook/codemod/node_modules/prettier": { - "version": "2.8.8", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", - "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", - "dev": true, - "bin": { - "prettier": "bin-prettier.js" - }, - "engines": { - "node": ">=10.13.0" - }, - "funding": { - "url": "https://github.com/prettier/prettier?sponsor=1" - } - }, - "node_modules/@storybook/components": { - "version": "7.6.17", - "resolved": "https://registry.npmjs.org/@storybook/components/-/components-7.6.17.tgz", - "integrity": "sha512-lbh7GynMidA+CZcJnstVku6Nhs+YkqjYaZ+mKPugvlVhGVWv0DaaeQFVuZ8cJtUGJ/5FFU4Y+n+gylYUHkGBMA==", - "dev": true, - "dependencies": { - "@radix-ui/react-select": "^1.2.2", - "@radix-ui/react-toolbar": "^1.0.4", - "@storybook/client-logger": "7.6.17", - "@storybook/csf": "^0.1.2", - "@storybook/global": "^5.0.0", - "@storybook/theming": "7.6.17", - "@storybook/types": "7.6.17", - "memoizerific": "^1.11.3", - "use-resize-observer": "^9.1.0", - "util-deprecate": "^1.0.2" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" - } - }, - "node_modules/@storybook/core-common": { - "version": "7.6.17", - "resolved": "https://registry.npmjs.org/@storybook/core-common/-/core-common-7.6.17.tgz", - "integrity": "sha512-me2TP3Q9/qzqCLoDHUSsUF+VS1MHxfHbTVF6vAz0D/COTxzsxLpu9TxTbzJoBCxse6XRb6wWI1RgF1mIcjic7g==", - "dev": true, - "dependencies": { - "@storybook/core-events": "7.6.17", - "@storybook/node-logger": "7.6.17", - "@storybook/types": "7.6.17", - "@types/find-cache-dir": "^3.2.1", - "@types/node": "^18.0.0", - "@types/node-fetch": "^2.6.4", - "@types/pretty-hrtime": "^1.0.0", - "chalk": "^4.1.0", - "esbuild": "^0.18.0", - "esbuild-register": "^3.5.0", - "file-system-cache": "2.3.0", - "find-cache-dir": "^3.0.0", - "find-up": "^5.0.0", - "fs-extra": "^11.1.0", - "glob": "^10.0.0", - "handlebars": "^4.7.7", - "lazy-universal-dotenv": "^4.0.0", - "node-fetch": "^2.0.0", - "picomatch": "^2.3.0", - "pkg-dir": "^5.0.0", - "pretty-hrtime": "^1.0.3", - "resolve-from": "^5.0.0", - "ts-dedent": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "node_modules/@storybook/core-common/node_modules/@esbuild/android-arm": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.20.tgz", - "integrity": "sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==", - "cpu": [ - "arm" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@storybook/core-common/node_modules/@esbuild/android-arm64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.18.20.tgz", - "integrity": "sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@storybook/core-common/node_modules/@esbuild/android-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.18.20.tgz", - "integrity": "sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@storybook/core-common/node_modules/@esbuild/darwin-arm64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.18.20.tgz", - "integrity": "sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@storybook/core-common/node_modules/@esbuild/darwin-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.18.20.tgz", - "integrity": "sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@storybook/core-common/node_modules/@esbuild/freebsd-arm64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.20.tgz", - "integrity": "sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@storybook/core-common/node_modules/@esbuild/freebsd-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.18.20.tgz", - "integrity": "sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@storybook/core-common/node_modules/@esbuild/linux-arm": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.18.20.tgz", - "integrity": "sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==", - "cpu": [ - "arm" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@storybook/core-common/node_modules/@esbuild/linux-arm64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.18.20.tgz", - "integrity": "sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@storybook/core-common/node_modules/@esbuild/linux-ia32": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.18.20.tgz", - "integrity": "sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==", - "cpu": [ - "ia32" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@storybook/core-common/node_modules/@esbuild/linux-loong64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.18.20.tgz", - "integrity": "sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==", - "cpu": [ - "loong64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@storybook/core-common/node_modules/@esbuild/linux-mips64el": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.18.20.tgz", - "integrity": "sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==", - "cpu": [ - "mips64el" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@storybook/core-common/node_modules/@esbuild/linux-ppc64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.18.20.tgz", - "integrity": "sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==", - "cpu": [ - "ppc64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@storybook/core-common/node_modules/@esbuild/linux-riscv64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.18.20.tgz", - "integrity": "sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==", - "cpu": [ - "riscv64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@storybook/core-common/node_modules/@esbuild/linux-s390x": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.18.20.tgz", - "integrity": "sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==", - "cpu": [ - "s390x" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@storybook/core-common/node_modules/@esbuild/linux-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.18.20.tgz", - "integrity": "sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@storybook/core-common/node_modules/@esbuild/netbsd-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.18.20.tgz", - "integrity": "sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@storybook/core-common/node_modules/@esbuild/openbsd-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.18.20.tgz", - "integrity": "sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@storybook/core-common/node_modules/@esbuild/sunos-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.18.20.tgz", - "integrity": "sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@storybook/core-common/node_modules/@esbuild/win32-arm64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.18.20.tgz", - "integrity": "sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@storybook/core-common/node_modules/@esbuild/win32-ia32": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.18.20.tgz", - "integrity": "sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==", - "cpu": [ - "ia32" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@storybook/core-common/node_modules/@esbuild/win32-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.18.20.tgz", - "integrity": "sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@storybook/core-common/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@storybook/core-common/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, - "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/@storybook/core-common/node_modules/esbuild": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.18.20.tgz", - "integrity": "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==", - "dev": true, - "hasInstallScript": true, - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=12" - }, - "optionalDependencies": { - "@esbuild/android-arm": "0.18.20", - "@esbuild/android-arm64": "0.18.20", - "@esbuild/android-x64": "0.18.20", - "@esbuild/darwin-arm64": "0.18.20", - "@esbuild/darwin-x64": "0.18.20", - "@esbuild/freebsd-arm64": "0.18.20", - "@esbuild/freebsd-x64": "0.18.20", - "@esbuild/linux-arm": "0.18.20", - "@esbuild/linux-arm64": "0.18.20", - "@esbuild/linux-ia32": "0.18.20", - "@esbuild/linux-loong64": "0.18.20", - "@esbuild/linux-mips64el": "0.18.20", - "@esbuild/linux-ppc64": "0.18.20", - "@esbuild/linux-riscv64": "0.18.20", - "@esbuild/linux-s390x": "0.18.20", - "@esbuild/linux-x64": "0.18.20", - "@esbuild/netbsd-x64": "0.18.20", - "@esbuild/openbsd-x64": "0.18.20", - "@esbuild/sunos-x64": "0.18.20", - "@esbuild/win32-arm64": "0.18.20", - "@esbuild/win32-ia32": "0.18.20", - "@esbuild/win32-x64": "0.18.20" - } - }, - "node_modules/@storybook/core-common/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/@storybook/core-common/node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/@storybook/core-common/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@storybook/core-events": { - "version": "7.6.17", - "resolved": "https://registry.npmjs.org/@storybook/core-events/-/core-events-7.6.17.tgz", - "integrity": "sha512-AriWMCm/k1cxlv10f+jZ1wavThTRpLaN3kY019kHWbYT9XgaSuLU67G7GPr3cGnJ6HuA6uhbzu8qtqVCd6OfXA==", - "dev": true, - "dependencies": { - "ts-dedent": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "node_modules/@storybook/core-server": { - "version": "7.6.17", - "resolved": "https://registry.npmjs.org/@storybook/core-server/-/core-server-7.6.17.tgz", - "integrity": "sha512-KWGhTTaL1Q14FolcoKKZgytlPJUbH6sbJ1Ptj/84EYWFewcnEgVs0Zlnh1VStRZg+Rd1WC1V4yVd/bbDzxrvQA==", - "dev": true, - "dependencies": { - "@aw-web-design/x-default-browser": "1.4.126", - "@discoveryjs/json-ext": "^0.5.3", - "@storybook/builder-manager": "7.6.17", - "@storybook/channels": "7.6.17", - "@storybook/core-common": "7.6.17", - "@storybook/core-events": "7.6.17", - "@storybook/csf": "^0.1.2", - "@storybook/csf-tools": "7.6.17", - "@storybook/docs-mdx": "^0.1.0", - "@storybook/global": "^5.0.0", - "@storybook/manager": "7.6.17", - "@storybook/node-logger": "7.6.17", - "@storybook/preview-api": "7.6.17", - "@storybook/telemetry": "7.6.17", - "@storybook/types": "7.6.17", - "@types/detect-port": "^1.3.0", - "@types/node": "^18.0.0", - "@types/pretty-hrtime": "^1.0.0", - "@types/semver": "^7.3.4", - "better-opn": "^3.0.2", - "chalk": "^4.1.0", - "cli-table3": "^0.6.1", - "compression": "^1.7.4", - "detect-port": "^1.3.0", - "express": "^4.17.3", - "fs-extra": "^11.1.0", - "globby": "^11.0.2", - "ip": "^2.0.1", - "lodash": "^4.17.21", - "open": "^8.4.0", - "pretty-hrtime": "^1.0.3", - "prompts": "^2.4.0", - "read-pkg-up": "^7.0.1", - "semver": "^7.3.7", - "telejson": "^7.2.0", - "tiny-invariant": "^1.3.1", - "ts-dedent": "^2.0.0", - "util": "^0.12.4", - "util-deprecate": "^1.0.2", - "watchpack": "^2.2.0", - "ws": "^8.2.3" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "node_modules/@storybook/core-server/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@storybook/core-server/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, - "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/@storybook/core-server/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/@storybook/core-server/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@storybook/core-webpack": { - "version": "7.6.17", - "resolved": "https://registry.npmjs.org/@storybook/core-webpack/-/core-webpack-7.6.17.tgz", - "integrity": "sha512-PyGrFhRM8sTONGwwLWLqBQ1HO+LBnVZ+5TOQO7ejQfdV2FWyNOzjBXm2e5jL/C6XlqiEhmL5pyHEyDBaQJQ3KA==", - "dev": true, - "dependencies": { - "@storybook/core-common": "7.6.17", - "@storybook/node-logger": "7.6.17", - "@storybook/types": "7.6.17", - "@types/node": "^18.0.0", - "ts-dedent": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "node_modules/@storybook/csf": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/@storybook/csf/-/csf-0.1.2.tgz", - "integrity": "sha512-ePrvE/pS1vsKR9Xr+o+YwdqNgHUyXvg+1Xjx0h9LrVx7Zq4zNe06pd63F5EvzTbCbJsHj7GHr9tkiaqm7U8WRA==", - "dev": true, - "dependencies": { - "type-fest": "^2.19.0" - } - }, - "node_modules/@storybook/csf-plugin": { - "version": "7.6.17", - "resolved": "https://registry.npmjs.org/@storybook/csf-plugin/-/csf-plugin-7.6.17.tgz", - "integrity": "sha512-xTHv9BUh3bkDVCvcbmdfVF0/e96BdrEgqPJ3G3RmKbSzWLOkQ2U9yiPfHzT0KJWPhVwj12fjfZp0zunu+pcS6Q==", - "dev": true, - "dependencies": { - "@storybook/csf-tools": "7.6.17", - "unplugin": "^1.3.1" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "node_modules/@storybook/csf-tools": { - "version": "7.6.17", - "resolved": "https://registry.npmjs.org/@storybook/csf-tools/-/csf-tools-7.6.17.tgz", - "integrity": "sha512-dAQtam0EBPeTJYcQPLxXgz4L9JFqD+HWbLFG9CmNIhMMjticrB0mpk1EFIS6vPXk/VsVWpBgMLD7dZlD6YMKcQ==", - "dev": true, - "dependencies": { - "@babel/generator": "^7.23.0", - "@babel/parser": "^7.23.0", - "@babel/traverse": "^7.23.2", - "@babel/types": "^7.23.0", - "@storybook/csf": "^0.1.2", - "@storybook/types": "7.6.17", - "fs-extra": "^11.1.0", - "recast": "^0.23.1", - "ts-dedent": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "node_modules/@storybook/docs-mdx": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/@storybook/docs-mdx/-/docs-mdx-0.1.0.tgz", - "integrity": "sha512-JDaBR9lwVY4eSH5W8EGHrhODjygPd6QImRbwjAuJNEnY0Vw4ie3bPkeGfnacB3OBW6u/agqPv2aRlR46JcAQLg==", - "dev": true - }, - "node_modules/@storybook/docs-tools": { - "version": "7.6.17", - "resolved": "https://registry.npmjs.org/@storybook/docs-tools/-/docs-tools-7.6.17.tgz", - "integrity": "sha512-bYrLoj06adqklyLkEwD32C0Ww6t+9ZVvrJHiVT42bIhTRpFiFPAetl1a9KPHtFLnfduh4n2IxIr1jv32ThPDTA==", - "dev": true, - "dependencies": { - "@storybook/core-common": "7.6.17", - "@storybook/preview-api": "7.6.17", - "@storybook/types": "7.6.17", - "@types/doctrine": "^0.0.3", - "assert": "^2.1.0", - "doctrine": "^3.0.0", - "lodash": "^4.17.21" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "node_modules/@storybook/global": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@storybook/global/-/global-5.0.0.tgz", - "integrity": "sha512-FcOqPAXACP0I3oJ/ws6/rrPT9WGhu915Cg8D02a9YxLo0DE9zI+a9A5gRGvmQ09fiWPukqI8ZAEoQEdWUKMQdQ==", - "dev": true - }, - "node_modules/@storybook/manager": { - "version": "7.6.17", - "resolved": "https://registry.npmjs.org/@storybook/manager/-/manager-7.6.17.tgz", - "integrity": "sha512-A1LDDIqMpwRzq/dqkbbiza0QI04o4ZHCl2a3UMDZUV/+QLc2nsr2DAaLk4CVL4/cIc5zGqmIcaOTvprx2YKVBw==", - "dev": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "node_modules/@storybook/manager-api": { - "version": "7.6.17", - "resolved": "https://registry.npmjs.org/@storybook/manager-api/-/manager-api-7.6.17.tgz", - "integrity": "sha512-IJIV1Yc6yw1dhCY4tReHCfBnUKDqEBnMyHp3mbXpsaHxnxJZrXO45WjRAZIKlQKhl/Ge1CrnznmHRCmYgqmrWg==", - "dev": true, - "dependencies": { - "@storybook/channels": "7.6.17", - "@storybook/client-logger": "7.6.17", - "@storybook/core-events": "7.6.17", - "@storybook/csf": "^0.1.2", - "@storybook/global": "^5.0.0", - "@storybook/router": "7.6.17", - "@storybook/theming": "7.6.17", - "@storybook/types": "7.6.17", - "dequal": "^2.0.2", - "lodash": "^4.17.21", - "memoizerific": "^1.11.3", - "store2": "^2.14.2", - "telejson": "^7.2.0", - "ts-dedent": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "node_modules/@storybook/mdx2-csf": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@storybook/mdx2-csf/-/mdx2-csf-1.1.0.tgz", - "integrity": "sha512-TXJJd5RAKakWx4BtpwvSNdgTDkKM6RkXU8GK34S/LhidQ5Pjz3wcnqb0TxEkfhK/ztbP8nKHqXFwLfa2CYkvQw==", - "dev": true - }, - "node_modules/@storybook/node-logger": { - "version": "7.6.17", - "resolved": "https://registry.npmjs.org/@storybook/node-logger/-/node-logger-7.6.17.tgz", - "integrity": "sha512-w59MQuXhhUNrUVmVkXhMwIg2nvFWjdDczLTwYLorhfsE36CWeUOY5QCZWQy0Qf/h+jz8Uo7Evy64qn18v9C4wA==", - "dev": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "node_modules/@storybook/postinstall": { - "version": "7.6.17", - "resolved": "https://registry.npmjs.org/@storybook/postinstall/-/postinstall-7.6.17.tgz", - "integrity": "sha512-WaWqB8o9vUc9aaVls+povQSVirf1Xd1LZcVhUKfAocAF3mzYUsnJsVqvnbjRj/F96UFVihOyDt9Zjl/9OvrCvQ==", - "dev": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "node_modules/@storybook/preview": { - "version": "7.6.17", - "resolved": "https://registry.npmjs.org/@storybook/preview/-/preview-7.6.17.tgz", - "integrity": "sha512-LvkMYK/y6alGjwRVNDIKL1lFlbyZ0H0c8iAbcQkiMoaFiujMQyVswMDKlWcj42Upfr/B1igydiruomc+eUt0mw==", - "dev": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "node_modules/@storybook/preview-api": { - "version": "7.6.17", - "resolved": "https://registry.npmjs.org/@storybook/preview-api/-/preview-api-7.6.17.tgz", - "integrity": "sha512-wLfDdI9RWo1f2zzFe54yRhg+2YWyxLZvqdZnSQ45mTs4/7xXV5Wfbv3QNTtcdw8tT3U5KRTrN1mTfTCiRJc0Kw==", - "dev": true, - "dependencies": { - "@storybook/channels": "7.6.17", - "@storybook/client-logger": "7.6.17", - "@storybook/core-events": "7.6.17", - "@storybook/csf": "^0.1.2", - "@storybook/global": "^5.0.0", - "@storybook/types": "7.6.17", - "@types/qs": "^6.9.5", - "dequal": "^2.0.2", - "lodash": "^4.17.21", - "memoizerific": "^1.11.3", - "qs": "^6.10.0", - "synchronous-promise": "^2.0.15", - "ts-dedent": "^2.0.0", - "util-deprecate": "^1.0.2" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "node_modules/@storybook/react-dom-shim": { - "version": "7.6.17", - "resolved": "https://registry.npmjs.org/@storybook/react-dom-shim/-/react-dom-shim-7.6.17.tgz", - "integrity": "sha512-32Sa/G+WnvaPiQ1Wvjjw5UM9rr2c4GDohwCcWVv3/LJuiFPqNS6zglAtmnsrlIBnUwRBMLMh/ekCTdqMiUmfDw==", - "dev": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" - } - }, - "node_modules/@storybook/router": { - "version": "7.6.17", - "resolved": "https://registry.npmjs.org/@storybook/router/-/router-7.6.17.tgz", - "integrity": "sha512-GnyC0j6Wi5hT4qRhSyT8NPtJfGmf82uZw97LQRWeyYu5gWEshUdM7aj40XlNiScd5cZDp0owO1idduVF2k2l2A==", - "dev": true, - "dependencies": { - "@storybook/client-logger": "7.6.17", - "memoizerific": "^1.11.3", - "qs": "^6.10.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "node_modules/@storybook/telemetry": { - "version": "7.6.17", - "resolved": "https://registry.npmjs.org/@storybook/telemetry/-/telemetry-7.6.17.tgz", - "integrity": "sha512-WOcOAmmengYnGInH98Px44F47DSpLyk20BM+Z/IIQDzfttGOLlxNqBBG1XTEhNRn+AYuk4aZ2JEed2lCjVIxcA==", - "dev": true, - "dependencies": { - "@storybook/client-logger": "7.6.17", - "@storybook/core-common": "7.6.17", - "@storybook/csf-tools": "7.6.17", - "chalk": "^4.1.0", - "detect-package-manager": "^2.0.1", - "fetch-retry": "^5.0.2", - "fs-extra": "^11.1.0", - "read-pkg-up": "^7.0.1" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "node_modules/@storybook/telemetry/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@storybook/telemetry/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, - "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/@storybook/telemetry/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/@storybook/telemetry/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@storybook/testing-library": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/@storybook/testing-library/-/testing-library-0.2.2.tgz", - "integrity": "sha512-L8sXFJUHmrlyU2BsWWZGuAjv39Jl1uAqUHdxmN42JY15M4+XCMjGlArdCCjDe1wpTSW6USYISA9axjZojgtvnw==", - "dev": true, - "dependencies": { - "@testing-library/dom": "^9.0.0", - "@testing-library/user-event": "^14.4.0", - "ts-dedent": "^2.2.0" - } - }, - "node_modules/@storybook/theming": { - "version": "7.6.17", - "resolved": "https://registry.npmjs.org/@storybook/theming/-/theming-7.6.17.tgz", - "integrity": "sha512-ZbaBt3KAbmBtfjNqgMY7wPMBshhSJlhodyMNQypv+95xLD/R+Az6aBYbpVAOygLaUQaQk4ar7H/Ww6lFIoiFbA==", - "dev": true, - "dependencies": { - "@emotion/use-insertion-effect-with-fallbacks": "^1.0.0", - "@storybook/client-logger": "7.6.17", - "@storybook/global": "^5.0.0", - "memoizerific": "^1.11.3" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" - } - }, - "node_modules/@storybook/types": { - "version": "7.6.17", - "resolved": "https://registry.npmjs.org/@storybook/types/-/types-7.6.17.tgz", - "integrity": "sha512-GRY0xEJQ0PrL7DY2qCNUdIfUOE0Gsue6N+GBJw9ku1IUDFLJRDOF+4Dx2BvYcVCPI5XPqdWKlEyZdMdKjiQN7Q==", - "dev": true, - "dependencies": { - "@storybook/channels": "7.6.17", - "@types/babel__core": "^7.0.0", - "@types/express": "^4.7.0", - "file-system-cache": "2.3.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, - "node_modules/@swc/core": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.4.2.tgz", - "integrity": "sha512-vWgY07R/eqj1/a0vsRKLI9o9klGZfpLNOVEnrv4nrccxBgYPjcf22IWwAoaBJ+wpA7Q4fVjCUM8lP0m01dpxcg==", - "dev": true, - "hasInstallScript": true, - "dependencies": { - "@swc/counter": "^0.1.2", - "@swc/types": "^0.1.5" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/swc" - }, - "optionalDependencies": { - "@swc/core-darwin-arm64": "1.4.2", - "@swc/core-darwin-x64": "1.4.2", - "@swc/core-linux-arm-gnueabihf": "1.4.2", - "@swc/core-linux-arm64-gnu": "1.4.2", - "@swc/core-linux-arm64-musl": "1.4.2", - "@swc/core-linux-x64-gnu": "1.4.2", - "@swc/core-linux-x64-musl": "1.4.2", - "@swc/core-win32-arm64-msvc": "1.4.2", - "@swc/core-win32-ia32-msvc": "1.4.2", - "@swc/core-win32-x64-msvc": "1.4.2" - }, - "peerDependencies": { - "@swc/helpers": "^0.5.0" - }, - "peerDependenciesMeta": { - "@swc/helpers": { - "optional": true - } - } - }, - "node_modules/@swc/core-darwin-arm64": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.4.2.tgz", - "integrity": "sha512-1uSdAn1MRK5C1m/TvLZ2RDvr0zLvochgrZ2xL+lRzugLlCTlSA+Q4TWtrZaOz+vnnFVliCpw7c7qu0JouhgQIw==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-darwin-x64": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.4.2.tgz", - "integrity": "sha512-TYD28+dCQKeuxxcy7gLJUCFLqrwDZnHtC2z7cdeGfZpbI2mbfppfTf2wUPzqZk3gEC96zHd4Yr37V3Tvzar+lQ==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-linux-arm-gnueabihf": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.4.2.tgz", - "integrity": "sha512-Eyqipf7ZPGj0vplKHo8JUOoU1un2sg5PjJMpEesX0k+6HKE2T8pdyeyXODN0YTFqzndSa/J43EEPXm+rHAsLFQ==", - "cpu": [ - "arm" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-linux-arm64-gnu": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.4.2.tgz", - "integrity": "sha512-wZn02DH8VYPv3FC0ub4my52Rttsus/rFw+UUfzdb3tHMHXB66LqN+rR0ssIOZrH6K+VLN6qpTw9VizjyoH0BxA==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-linux-arm64-musl": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.4.2.tgz", - "integrity": "sha512-3G0D5z9hUj9bXNcwmA1eGiFTwe5rWkuL3DsoviTj73TKLpk7u64ND0XjEfO0huVv4vVu9H1jodrKb7nvln/dlw==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-linux-x64-gnu": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.4.2.tgz", - "integrity": "sha512-LFxn9U8cjmYHw3jrdPNqPAkBGglKE3tCZ8rA7hYyp0BFxuo7L2ZcEnPm4RFpmSCCsExFH+LEJWuMGgWERoktvg==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-linux-x64-musl": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.4.2.tgz", - "integrity": "sha512-dp0fAmreeVVYTUcb4u9njTPrYzKnbIH0EhH2qvC9GOYNNREUu2GezSIDgonjOXkHiTCvopG4xU7y56XtXj4VrQ==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-win32-arm64-msvc": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.4.2.tgz", - "integrity": "sha512-HlVIiLMQkzthAdqMslQhDkoXJ5+AOLUSTV6fm6shFKZKqc/9cJvr4S8UveNERL9zUficA36yM3bbfo36McwnvQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-win32-ia32-msvc": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.4.2.tgz", - "integrity": "sha512-WCF8faPGjCl4oIgugkp+kL9nl3nUATlzKXCEGFowMEmVVCFM0GsqlmGdPp1pjZoWc9tpYanoXQDnp5IvlDSLhA==", - "cpu": [ - "ia32" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-win32-x64-msvc": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.4.2.tgz", - "integrity": "sha512-oV71rwiSpA5xre2C5570BhCsg1HF97SNLsZ/12xv7zayGzqr3yvFALFJN8tHKpqUdCB4FGPjoP3JFdV3i+1wUw==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/counter": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz", - "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==", - "dev": true - }, - "node_modules/@swc/types": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.5.tgz", - "integrity": "sha512-myfUej5naTBWnqOCc/MdVOLVjXUXtIA+NpDrDBKJtLLg2shUjBu3cZmB/85RyitKc55+lUUyl7oRfLOvkr2hsw==", - "dev": true - }, - "node_modules/@testing-library/dom": { - "version": "9.3.4", - "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-9.3.4.tgz", - "integrity": "sha512-FlS4ZWlp97iiNWig0Muq8p+3rVDjRiYE+YKGbAqXOu9nwJFFOdL00kFpz42M+4huzYi86vAK1sOOfyOG45muIQ==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.10.4", - "@babel/runtime": "^7.12.5", - "@types/aria-query": "^5.0.1", - "aria-query": "5.1.3", - "chalk": "^4.1.0", - "dom-accessibility-api": "^0.5.9", - "lz-string": "^1.5.0", - "pretty-format": "^27.0.2" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/@testing-library/dom/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@testing-library/dom/node_modules/aria-query": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.1.3.tgz", - "integrity": "sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ==", - "dev": true, - "dependencies": { - "deep-equal": "^2.0.5" - } - }, - "node_modules/@testing-library/dom/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, - "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/@testing-library/dom/node_modules/deep-equal": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-2.2.3.tgz", - "integrity": "sha512-ZIwpnevOurS8bpT4192sqAowWM76JDKSHYzMLty3BZGSswgq6pBaH3DhCSW5xVAZICZyKdOBPjwww5wfgT/6PA==", - "dev": true, - "dependencies": { - "array-buffer-byte-length": "^1.0.0", - "call-bind": "^1.0.5", - "es-get-iterator": "^1.1.3", - "get-intrinsic": "^1.2.2", - "is-arguments": "^1.1.1", - "is-array-buffer": "^3.0.2", - "is-date-object": "^1.0.5", - "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.2", - "isarray": "^2.0.5", - "object-is": "^1.1.5", - "object-keys": "^1.1.1", - "object.assign": "^4.1.4", - "regexp.prototype.flags": "^1.5.1", - "side-channel": "^1.0.4", - "which-boxed-primitive": "^1.0.2", - "which-collection": "^1.0.1", - "which-typed-array": "^1.1.13" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/@testing-library/dom/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/@testing-library/dom/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@testing-library/user-event": { - "version": "14.5.2", - "resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-14.5.2.tgz", - "integrity": "sha512-YAh82Wh4TIrxYLmfGcixwD18oIjyC1pFQC2Y01F2lzV2HTMiYrI0nze0FD0ocB//CKS/7jIUgae+adPqxK5yCQ==", - "dev": true, - "engines": { - "node": ">=12", - "npm": ">=6" - }, - "peerDependencies": { - "@testing-library/dom": ">=7.21.4" - } - }, - "node_modules/@thednp/event-listener": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@thednp/event-listener/-/event-listener-2.0.4.tgz", - "integrity": "sha512-sc4B7AzYAIvnGnivirq0XyR7LfzEDhGiiB70Q0qdNn8wSJ2pL1buVAsEZxrlc47qRJiBV4YIP+BFkyMm2r3NLg==", - "dev": true, - "engines": { - "node": ">=16", - "pnpm": ">=8.6.0" - } - }, - "node_modules/@thednp/shorty": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@thednp/shorty/-/shorty-2.0.0.tgz", - "integrity": "sha512-kwtLivCxYIoFfGIVU4NlZtfdA/zxZ6X8UcWaJrb7XqU3WQ4Q1p5IaZlLBfOVAO06WH5oWE87QUdK/dS56Wnfjg==", - "dev": true, - "engines": { - "node": ">=16", - "pnpm": ">=8.6.0" - } - }, - "node_modules/@tootallnate/once": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", - "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", - "dev": true, - "engines": { - "node": ">= 10" - } - }, - "node_modules/@ts-morph/common": { - "version": "0.21.0", - "resolved": "https://registry.npmjs.org/@ts-morph/common/-/common-0.21.0.tgz", - "integrity": "sha512-ES110Mmne5Vi4ypUKrtVQfXFDtCsDXiUiGxF6ILVlE90dDD4fdpC1LSjydl/ml7xJWKSDZwUYD2zkOePMSrPBA==", - "dev": true, - "dependencies": { - "fast-glob": "^3.2.12", - "minimatch": "^7.4.3", - "mkdirp": "^2.1.6", - "path-browserify": "^1.0.1" - } - }, - "node_modules/@ts-morph/common/node_modules/minimatch": { - "version": "7.4.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-7.4.6.tgz", - "integrity": "sha512-sBz8G/YjVniEz6lKPNpKxXwazJe4c19fEfV2GDMX6AjFz+MX9uDWIZW8XreVhkFW3fkIdTv/gxWr/Kks5FFAVw==", - "dev": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@ts-morph/common/node_modules/mkdirp": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-2.1.6.tgz", - "integrity": "sha512-+hEnITedc8LAtIP9u3HJDFIdcLV2vXP33sqLLIzkv1Db1zO/1OxbvYf0Y1OC/S/Qo5dxHXepofhmxL02PsKe+A==", - "dev": true, - "bin": { - "mkdirp": "dist/cjs/src/bin.js" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@tsconfig/node10": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", - "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", - "dev": true - }, - "node_modules/@tsconfig/node12": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", - "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", - "dev": true - }, - "node_modules/@tsconfig/node14": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", - "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", - "dev": true - }, - "node_modules/@tsconfig/node16": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", - "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", - "dev": true - }, - "node_modules/@tufjs/canonical-json": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@tufjs/canonical-json/-/canonical-json-2.0.0.tgz", - "integrity": "sha512-yVtV8zsdo8qFHe+/3kw81dSLyF7D576A5cCFCi4X7B39tWT7SekaEFUnvnWJHz+9qO7qJTah1JbrDjWKqFtdWA==", - "dev": true, - "engines": { - "node": "^16.14.0 || >=18.0.0" - } - }, - "node_modules/@tufjs/models": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@tufjs/models/-/models-2.0.0.tgz", - "integrity": "sha512-c8nj8BaOExmZKO2DXhDfegyhSGcG9E/mPN3U13L+/PsoWm1uaGiHHjxqSHQiasDBQwDA3aHuw9+9spYAP1qvvg==", - "dev": true, - "dependencies": { - "@tufjs/canonical-json": "2.0.0", - "minimatch": "^9.0.3" - }, - "engines": { - "node": "^16.14.0 || >=18.0.0" - } - }, - "node_modules/@types/aria-query": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz", - "integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==", - "dev": true - }, - "node_modules/@types/babel__core": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", - "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", - "dev": true, - "dependencies": { - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7", - "@types/babel__generator": "*", - "@types/babel__template": "*", - "@types/babel__traverse": "*" - } - }, - "node_modules/@types/babel__generator": { - "version": "7.6.8", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.8.tgz", - "integrity": "sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==", - "dev": true, - "dependencies": { - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__template": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", - "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", - "dev": true, - "dependencies": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__traverse": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.5.tgz", - "integrity": "sha512-WXCyOcRtH37HAUkpXhUduaxdm82b4GSlyTqajXviN4EfiuPgNYR109xMCKvpl6zPIpua0DGlMEDCq+g8EdoheQ==", - "dev": true, - "dependencies": { - "@babel/types": "^7.20.7" - } - }, - "node_modules/@types/body-parser": { - "version": "1.19.5", - "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz", - "integrity": "sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==", - "dev": true, - "dependencies": { - "@types/connect": "*", - "@types/node": "*" - } - }, - "node_modules/@types/bonjour": { - "version": "3.5.13", - "resolved": "https://registry.npmjs.org/@types/bonjour/-/bonjour-3.5.13.tgz", - "integrity": "sha512-z9fJ5Im06zvUL548KvYNecEVlA7cVDkGUi6kZusb04mpyEFKCIZJvloCcmpmLaIahDpOQGHaHmG6imtPMmPXGQ==", - "dev": true, - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/connect": { - "version": "3.4.38", - "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", - "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", - "dev": true, - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/connect-history-api-fallback": { - "version": "1.5.4", - "resolved": "https://registry.npmjs.org/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.5.4.tgz", - "integrity": "sha512-n6Cr2xS1h4uAulPRdlw6Jl6s1oG8KrVilPN2yUITEs+K48EzMJJ3W1xy8K5eWuFvjp3R74AOIGSmp2UfBJ8HFw==", - "dev": true, - "dependencies": { - "@types/express-serve-static-core": "*", - "@types/node": "*" - } - }, - "node_modules/@types/cross-spawn": { - "version": "6.0.6", - "resolved": "https://registry.npmjs.org/@types/cross-spawn/-/cross-spawn-6.0.6.tgz", - "integrity": "sha512-fXRhhUkG4H3TQk5dBhQ7m/JDdSNHKwR2BBia62lhwEIq9xGiQKLxd6LymNhn47SjXhsUEPmxi+PKw2OkW4LLjA==", - "dev": true, - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/detect-port": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/@types/detect-port/-/detect-port-1.3.5.tgz", - "integrity": "sha512-Rf3/lB9WkDfIL9eEKaSYKc+1L/rNVYBjThk22JTqQw0YozXarX8YljFAz+HCoC6h4B4KwCMsBPZHaFezwT4BNA==", - "dev": true - }, - "node_modules/@types/doctrine": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/@types/doctrine/-/doctrine-0.0.3.tgz", - "integrity": "sha512-w5jZ0ee+HaPOaX25X2/2oGR/7rgAQSYII7X7pp0m9KgBfMP7uKfMfTvcpl5Dj+eDBbpxKGiqE+flqDr6XTd2RA==", - "dev": true - }, - "node_modules/@types/ejs": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/@types/ejs/-/ejs-3.1.5.tgz", - "integrity": "sha512-nv+GSx77ZtXiJzwKdsASqi+YQ5Z7vwHsTP0JY2SiQgjGckkBRKZnk8nIM+7oUZ1VCtuTz0+By4qVR7fqzp/Dfg==", - "dev": true - }, - "node_modules/@types/emscripten": { - "version": "1.39.10", - "resolved": "https://registry.npmjs.org/@types/emscripten/-/emscripten-1.39.10.tgz", - "integrity": "sha512-TB/6hBkYQJxsZHSqyeuO1Jt0AB/bW6G7rHt9g7lML7SOF6lbgcHvw/Lr+69iqN0qxgXLhWKScAon73JNnptuDw==", - "dev": true - }, - "node_modules/@types/eslint": { - "version": "8.56.5", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.5.tgz", - "integrity": "sha512-u5/YPJHo1tvkSF2CE0USEkxon82Z5DBy2xR+qfyYNszpX9qcs4sT6uq2kBbj4BXY1+DBGDPnrhMZV3pKWGNukw==", - "dev": true, - "dependencies": { - "@types/estree": "*", - "@types/json-schema": "*" - } - }, - "node_modules/@types/eslint-scope": { - "version": "3.7.7", - "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz", - "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==", - "dev": true, - "dependencies": { - "@types/eslint": "*", - "@types/estree": "*" - } - }, - "node_modules/@types/estree": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", - "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", - "dev": true - }, - "node_modules/@types/express": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz", - "integrity": "sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==", - "dev": true, - "dependencies": { - "@types/body-parser": "*", - "@types/express-serve-static-core": "^4.17.33", - "@types/qs": "*", - "@types/serve-static": "*" - } - }, - "node_modules/@types/express-serve-static-core": { - "version": "4.17.43", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.43.tgz", - "integrity": "sha512-oaYtiBirUOPQGSWNGPWnzyAFJ0BP3cwvN4oWZQY+zUBwpVIGsKUkpBpSztp74drYcjavs7SKFZ4DX1V2QeN8rg==", - "dev": true, - "dependencies": { - "@types/node": "*", - "@types/qs": "*", - "@types/range-parser": "*", - "@types/send": "*" - } - }, - "node_modules/@types/find-cache-dir": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/@types/find-cache-dir/-/find-cache-dir-3.2.1.tgz", - "integrity": "sha512-frsJrz2t/CeGifcu/6uRo4b+SzAwT4NYCVPu1GN8IB9XTzrpPkGuV0tmh9mN+/L0PklAlsC3u5Fxt0ju00LXIw==", - "dev": true - }, - "node_modules/@types/graceful-fs": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", - "integrity": "sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==", - "dev": true, - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/html-minifier-terser": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", - "integrity": "sha512-oh/6byDPnL1zeNXFrDXFLyZjkr1MsBG667IM792caf1L2UPOOMf65NFzjUH/ltyfwjAGfs1rsX1eftK0jC/KIg==", - "dev": true - }, - "node_modules/@types/http-errors": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz", - "integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==", - "dev": true - }, - "node_modules/@types/http-proxy": { - "version": "1.17.14", - "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.14.tgz", - "integrity": "sha512-SSrD0c1OQzlFX7pGu1eXxSEjemej64aaNPRhhVYUGqXh0BtldAAx37MG8btcumvpgKyZp1F5Gn3JkktdxiFv6w==", - "dev": true, - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/istanbul-lib-coverage": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", - "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", - "dev": true - }, - "node_modules/@types/istanbul-lib-report": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", - "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", - "dev": true, - "dependencies": { - "@types/istanbul-lib-coverage": "*" - } - }, - "node_modules/@types/istanbul-reports": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", - "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", - "dev": true, - "dependencies": { - "@types/istanbul-lib-report": "*" - } - }, - "node_modules/@types/jest": { - "version": "29.5.12", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.12.tgz", - "integrity": "sha512-eDC8bTvT/QhYdxJAulQikueigY5AsdBRH2yDKW3yveW7svY3+DzN84/2NUgkw10RTiJbWqZrTtoGVdYlvFJdLw==", - "dev": true, - "dependencies": { - "expect": "^29.0.0", - "pretty-format": "^29.0.0" - } - }, - "node_modules/@types/jest/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@types/jest/node_modules/pretty-format": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", - "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", - "dev": true, - "dependencies": { - "@jest/schemas": "^29.6.3", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@types/jest/node_modules/react-is": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", - "dev": true - }, - "node_modules/@types/jsdom": { - "version": "20.0.1", - "resolved": "https://registry.npmjs.org/@types/jsdom/-/jsdom-20.0.1.tgz", - "integrity": "sha512-d0r18sZPmMQr1eG35u12FZfhIXNrnsPU/g5wvRKCUf/tOGilKKwYMYGqh33BNR6ba+2gkHw1EUiHoN3mn7E5IQ==", - "dev": true, - "dependencies": { - "@types/node": "*", - "@types/tough-cookie": "*", - "parse5": "^7.0.0" - } - }, - "node_modules/@types/json-schema": { - "version": "7.0.15", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", - "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", - "dev": true - }, - "node_modules/@types/json-server": { - "version": "0.14.7", - "resolved": "https://registry.npmjs.org/@types/json-server/-/json-server-0.14.7.tgz", - "integrity": "sha512-aE1pKwYE3Y3vtj1IHrZVMP+BSeyP+BFgjGAC+iMTFV/yyLnYRFeX1yqTY1Xh0FyTu+xEpRVpeJupdRACuvi1eg==", - "dev": true, - "dependencies": { - "@types/connect": "*", - "@types/express": "*", - "@types/lowdb": "*" - } - }, - "node_modules/@types/json5": { - "version": "0.0.29", - "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", - "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==" - }, - "node_modules/@types/lodash": { - "version": "4.14.202", - "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.202.tgz", - "integrity": "sha512-OvlIYQK9tNneDlS0VN54LLd5uiPCBOp7gS5Z0f1mjoJYBrtStzgmJBxONW3U6OZqdtNzZPmn9BS/7WI7BFFcFQ==", - "dev": true - }, - "node_modules/@types/lodash.clonedeep": { - "version": "4.5.9", - "resolved": "https://registry.npmjs.org/@types/lodash.clonedeep/-/lodash.clonedeep-4.5.9.tgz", - "integrity": "sha512-19429mWC+FyaAhOLzsS8kZUsI+/GmBAQ0HFiCPsKGU+7pBXOQWhyrY6xNNDwUSX8SMZMJvuFVMF9O5dQOlQK9Q==", - "dev": true, - "dependencies": { - "@types/lodash": "*" - } - }, - "node_modules/@types/lodash.merge": { - "version": "4.6.9", - "resolved": "https://registry.npmjs.org/@types/lodash.merge/-/lodash.merge-4.6.9.tgz", - "integrity": "sha512-23sHDPmzd59kUgWyKGiOMO2Qb9YtqRO/x4IhkgNUiPQ1+5MUVqi6bCZeq9nBJ17msjIMbEIO5u+XW4Kz6aGUhQ==", - "dev": true, - "dependencies": { - "@types/lodash": "*" - } - }, - "node_modules/@types/lowdb": { - "version": "1.0.15", - "resolved": "https://registry.npmjs.org/@types/lowdb/-/lowdb-1.0.15.tgz", - "integrity": "sha512-xaMNIveDCryK4UvnUJOc2BCOH0lPivdvWHrutsLryo9r9Id3RqZq2RDmT4eddiEPYzu7nJMw6nFIcVifcqjWqg==", - "dev": true, - "dependencies": { - "@types/lodash": "*" - } - }, - "node_modules/@types/mdx": { - "version": "2.0.11", - "resolved": "https://registry.npmjs.org/@types/mdx/-/mdx-2.0.11.tgz", - "integrity": "sha512-HM5bwOaIQJIQbAYfax35HCKxx7a3KrK3nBtIqJgSOitivTD1y3oW9P3rxY9RkXYPUk7y/AjAohfHKmFpGE79zw==", - "dev": true - }, - "node_modules/@types/mime": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", - "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", - "dev": true - }, - "node_modules/@types/mime-types": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@types/mime-types/-/mime-types-2.1.4.tgz", - "integrity": "sha512-lfU4b34HOri+kAY5UheuFMWPDOI+OPceBSHZKp69gEyTL/mmJ4cnU6Y/rlme3UL3GyOn6Y42hyIEw0/q8sWx5w==", - "dev": true - }, - "node_modules/@types/minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-hov8bUuiLiyFPGyFPE1lwWhmzYbirOXQNNo40+y3zow8aFVTeyn3VWL0VFFfdNddA8S4Vf0Tc062rzyNr7Paag==", - "dev": true - }, - "node_modules/@types/node": { - "version": "18.19.21", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.21.tgz", - "integrity": "sha512-2Q2NeB6BmiTFQi4DHBzncSoq/cJMLDdhPaAoJFnFCyD9a8VPZRf7a1GAwp1Edb7ROaZc5Jz/tnZyL6EsWMRaqw==", - "dev": true, - "dependencies": { - "undici-types": "~5.26.4" - } - }, - "node_modules/@types/node-fetch": { - "version": "2.6.11", - "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.11.tgz", - "integrity": "sha512-24xFj9R5+rfQJLRyM56qh+wnVSYhyXC2tkoBndtY0U+vubqNsYXGjufB2nn8Q6gt0LrARwL6UBtMCSVCwl4B1g==", - "dev": true, - "dependencies": { - "@types/node": "*", - "form-data": "^4.0.0" - } - }, - "node_modules/@types/node-forge": { - "version": "1.3.11", - "resolved": "https://registry.npmjs.org/@types/node-forge/-/node-forge-1.3.11.tgz", - "integrity": "sha512-FQx220y22OKNTqaByeBGqHWYz4cl94tpcxeFdvBo3wjG6XPBuZ0BNgNZRV5J5TFmmcsJ4IzsLkmGRiQbnYsBEQ==", - "dev": true, - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/normalize-package-data": { - "version": "2.4.4", - "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz", - "integrity": "sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==", - "dev": true - }, - "node_modules/@types/parse-json": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz", - "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==", - "dev": true - }, - "node_modules/@types/pretty-hrtime": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@types/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz", - "integrity": "sha512-nj39q0wAIdhwn7DGUyT9irmsKK1tV0bd5WFEhgpqNTMFZ8cE+jieuTphCW0tfdm47S2zVT5mr09B28b1chmQMA==", - "dev": true - }, - "node_modules/@types/prop-types": { - "version": "15.7.11", - "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.11.tgz", - "integrity": "sha512-ga8y9v9uyeiLdpKddhxYQkxNDrfvuPrlFb0N1qnZZByvcElJaXthF1UhvCh9TLWJBEHeNtdnbysW7Y6Uq8CVng==", - "dev": true - }, - "node_modules/@types/qs": { - "version": "6.9.12", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.12.tgz", - "integrity": "sha512-bZcOkJ6uWrL0Qb2NAWKa7TBU+mJHPzhx9jjLL1KHF+XpzEcR7EXHvjbHlGtR/IsP1vyPrehuS6XqkmaePy//mg==", - "dev": true - }, - "node_modules/@types/range-parser": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", - "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", - "dev": true - }, - "node_modules/@types/react": { - "version": "18.2.61", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.61.tgz", - "integrity": "sha512-NURTN0qNnJa7O/k4XUkEW2yfygA+NxS0V5h1+kp9jPwhzZy95q3ADoGMP0+JypMhrZBTTgjKAUlTctde1zzeQA==", - "dev": true, - "dependencies": { - "@types/prop-types": "*", - "@types/scheduler": "*", - "csstype": "^3.0.2" - } - }, - "node_modules/@types/react-dom": { - "version": "16.9.24", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-16.9.24.tgz", - "integrity": "sha512-Gcmq2JTDheyWn/1eteqyzzWKSqDjYU6KYsIvH7thb7CR5OYInAWOX+7WnKf6PaU/cbdOc4szJItcDEJO7UGmfA==", - "dev": true, - "dependencies": { - "@types/react": "^16" - } - }, - "node_modules/@types/react-dom/node_modules/@types/react": { - "version": "16.14.57", - "resolved": "https://registry.npmjs.org/@types/react/-/react-16.14.57.tgz", - "integrity": "sha512-fuNq/GV1a6GgqSuVuC457vYeTbm4E1CUBQVZwSPxqYnRhIzSXCJ1gGqyv+PKhqLyfbKCga9dXHJDzv+4XE41fw==", - "dev": true, - "dependencies": { - "@types/prop-types": "*", - "@types/scheduler": "*", - "csstype": "^3.0.2" - } - }, - "node_modules/@types/retry": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz", - "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==", - "dev": true - }, - "node_modules/@types/scheduler": { - "version": "0.16.8", - "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.8.tgz", - "integrity": "sha512-WZLiwShhwLRmeV6zH+GkbOFT6Z6VklCItrDioxUnv+u4Ll+8vKeFySoFyK/0ctcRpOmwAicELfmys1sDc/Rw+A==", - "dev": true - }, - "node_modules/@types/semver": { - "version": "7.5.8", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz", - "integrity": "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==", - "dev": true - }, - "node_modules/@types/send": { - "version": "0.17.4", - "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz", - "integrity": "sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==", - "dev": true, - "dependencies": { - "@types/mime": "^1", - "@types/node": "*" - } - }, - "node_modules/@types/serve-index": { - "version": "1.9.4", - "resolved": "https://registry.npmjs.org/@types/serve-index/-/serve-index-1.9.4.tgz", - "integrity": "sha512-qLpGZ/c2fhSs5gnYsQxtDEq3Oy8SXPClIXkW5ghvAvsNuVSA8k+gCONcUCS/UjLEYvYps+e8uBtfgXgvhwfNug==", - "dev": true, - "dependencies": { - "@types/express": "*" - } - }, - "node_modules/@types/serve-static": { - "version": "1.15.5", - "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.5.tgz", - "integrity": "sha512-PDRk21MnK70hja/YF8AHfC7yIsiQHn1rcXx7ijCFBX/k+XQJhQT/gw3xekXKJvx+5SXaMMS8oqQy09Mzvz2TuQ==", - "dev": true, - "dependencies": { - "@types/http-errors": "*", - "@types/mime": "*", - "@types/node": "*" - } - }, - "node_modules/@types/sinonjs__fake-timers": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-8.1.1.tgz", - "integrity": "sha512-0kSuKjAS0TrGLJ0M/+8MaFkGsQhZpB6pxOmvS3K8FYI72K//YmdfoW9X2qPsAKh1mkwxGD5zib9s1FIFed6E8g==", - "dev": true - }, - "node_modules/@types/sizzle": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/@types/sizzle/-/sizzle-2.3.8.tgz", - "integrity": "sha512-0vWLNK2D5MT9dg0iOo8GlKguPAU02QjmZitPEsXRuJXU/OGIOt9vT9Fc26wtYuavLxtO45v9PGleoL9Z0k1LHg==", - "dev": true - }, - "node_modules/@types/sockjs": { - "version": "0.3.36", - "resolved": "https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.36.tgz", - "integrity": "sha512-MK9V6NzAS1+Ud7JV9lJLFqW85VbC9dq3LmwZCuBe4wBDgKC0Kj/jd8Xl+nSviU+Qc3+m7umHHyHg//2KSa0a0Q==", - "dev": true, - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/stack-utils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", - "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", - "dev": true - }, - "node_modules/@types/tough-cookie": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.5.tgz", - "integrity": "sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==", - "dev": true - }, - "node_modules/@types/unist": { - "version": "2.0.10", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.10.tgz", - "integrity": "sha512-IfYcSBWE3hLpBg8+X2SEa8LVkJdJEkT2Ese2aaLs3ptGdVtABxndrMaxuFlQ1qdFf9Q5rDvDpxI3WwgvKFAsQA==", - "dev": true - }, - "node_modules/@types/uuid": { - "version": "9.0.8", - "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.8.tgz", - "integrity": "sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==", - "dev": true - }, - "node_modules/@types/webpack-env": { - "version": "1.18.4", - "resolved": "https://registry.npmjs.org/@types/webpack-env/-/webpack-env-1.18.4.tgz", - "integrity": "sha512-I6e+9+HtWADAWeeJWDFQtdk4EVSAbj6Rtz4q8fJ7mSr1M0jzlFcs8/HZ+Xb5SHzVm1dxH7aUiI+A8kA8Gcrm0A==", - "dev": true - }, - "node_modules/@types/ws": { - "version": "8.5.10", - "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.10.tgz", - "integrity": "sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A==", - "dev": true, - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/yargs": { - "version": "17.0.32", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.32.tgz", - "integrity": "sha512-xQ67Yc/laOG5uMfX/093MRlGGCIBzZMarVa+gfNKJxWAIgykYpVGkBdbqEzGDDfCrVUj6Hiff4mTZ5BA6TmAog==", - "dev": true, - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/@types/yargs-parser": { - "version": "21.0.3", - "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", - "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", - "dev": true - }, - "node_modules/@types/yauzl": { - "version": "2.10.3", - "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.3.tgz", - "integrity": "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==", - "dev": true, - "optional": true, - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@typescript-eslint/eslint-plugin": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.21.0.tgz", - "integrity": "sha512-oy9+hTPCUFpngkEZUSzbf9MxI65wbKFoQYsgPdILTfbUldp5ovUuphZVe4i30emU9M/kP+T64Di0mxl7dSw3MA==", - "dev": true, - "dependencies": { - "@eslint-community/regexpp": "^4.5.1", - "@typescript-eslint/scope-manager": "6.21.0", - "@typescript-eslint/type-utils": "6.21.0", - "@typescript-eslint/utils": "6.21.0", - "@typescript-eslint/visitor-keys": "6.21.0", - "debug": "^4.3.4", - "graphemer": "^1.4.0", - "ignore": "^5.2.4", - "natural-compare": "^1.4.0", - "semver": "^7.5.4", - "ts-api-utils": "^1.0.1" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "@typescript-eslint/parser": "^6.0.0 || ^6.0.0-alpha", - "eslint": "^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/type-utils": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.21.0.tgz", - "integrity": "sha512-rZQI7wHfao8qMX3Rd3xqeYSMCL3SoiSQLBATSiVKARdFGCYSRvmViieZjqc58jKgs8Y8i9YvVVhRbHSTA4VBag==", - "dev": true, - "dependencies": { - "@typescript-eslint/typescript-estree": "6.21.0", - "@typescript-eslint/utils": "6.21.0", - "debug": "^4.3.4", - "ts-api-utils": "^1.0.1" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/typescript-estree": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.21.0.tgz", - "integrity": "sha512-6npJTkZcO+y2/kr+z0hc4HwNfrrP4kNYh57ek7yCNlrBjWQ1Y0OS7jiZTkgumrvkX5HkEKXFZkkdFNkaW2wmUQ==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "6.21.0", - "@typescript-eslint/visitor-keys": "6.21.0", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "minimatch": "9.0.3", - "semver": "^7.5.4", - "ts-api-utils": "^1.0.1" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/utils": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.21.0.tgz", - "integrity": "sha512-NfWVaC8HP9T8cbKQxHcsJBY5YE1O33+jpMwN45qzWWaPDZgLIbo12toGMWnmhvCpd3sIxkpDw3Wv1B3dYrbDQQ==", - "dev": true, - "dependencies": { - "@eslint-community/eslint-utils": "^4.4.0", - "@types/json-schema": "^7.0.12", - "@types/semver": "^7.5.0", - "@typescript-eslint/scope-manager": "6.21.0", - "@typescript-eslint/types": "6.21.0", - "@typescript-eslint/typescript-estree": "6.21.0", - "semver": "^7.5.4" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0" - } - }, - "node_modules/@typescript-eslint/experimental-utils": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-5.62.0.tgz", - "integrity": "sha512-RTXpeB3eMkpoclG3ZHft6vG/Z30azNHuqY6wKPBHlVMZFuEvrtlEDe8gMqDb+SO+9hjC/pLekeSCryf9vMZlCw==", - "dev": true, - "dependencies": { - "@typescript-eslint/utils": "5.62.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/@typescript-eslint/experimental-utils/node_modules/@typescript-eslint/scope-manager": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.62.0.tgz", - "integrity": "sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "5.62.0", - "@typescript-eslint/visitor-keys": "5.62.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/experimental-utils/node_modules/@typescript-eslint/types": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.62.0.tgz", - "integrity": "sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==", - "dev": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/experimental-utils/node_modules/@typescript-eslint/typescript-estree": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz", - "integrity": "sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "5.62.0", - "@typescript-eslint/visitor-keys": "5.62.0", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "semver": "^7.3.7", - "tsutils": "^3.21.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/experimental-utils/node_modules/@typescript-eslint/utils": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.62.0.tgz", - "integrity": "sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==", - "dev": true, - "dependencies": { - "@eslint-community/eslint-utils": "^4.2.0", - "@types/json-schema": "^7.0.9", - "@types/semver": "^7.3.12", - "@typescript-eslint/scope-manager": "5.62.0", - "@typescript-eslint/types": "5.62.0", - "@typescript-eslint/typescript-estree": "5.62.0", - "eslint-scope": "^5.1.1", - "semver": "^7.3.7" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/@typescript-eslint/experimental-utils/node_modules/@typescript-eslint/visitor-keys": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz", - "integrity": "sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "5.62.0", - "eslint-visitor-keys": "^3.3.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/experimental-utils/node_modules/eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", - "dev": true, - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/@typescript-eslint/experimental-utils/node_modules/estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/@typescript-eslint/parser": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.21.0.tgz", - "integrity": "sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ==", - "dev": true, - "dependencies": { - "@typescript-eslint/scope-manager": "6.21.0", - "@typescript-eslint/types": "6.21.0", - "@typescript-eslint/typescript-estree": "6.21.0", - "@typescript-eslint/visitor-keys": "6.21.0", - "debug": "^4.3.4" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/typescript-estree": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.21.0.tgz", - "integrity": "sha512-6npJTkZcO+y2/kr+z0hc4HwNfrrP4kNYh57ek7yCNlrBjWQ1Y0OS7jiZTkgumrvkX5HkEKXFZkkdFNkaW2wmUQ==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "6.21.0", - "@typescript-eslint/visitor-keys": "6.21.0", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "minimatch": "9.0.3", - "semver": "^7.5.4", - "ts-api-utils": "^1.0.1" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/scope-manager": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.21.0.tgz", - "integrity": "sha512-OwLUIWZJry80O99zvqXVEioyniJMa+d2GrqpUTqi5/v5D5rOrppJVBPa0yKCblcigC0/aYAzxxqQ1B+DS2RYsg==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "6.21.0", - "@typescript-eslint/visitor-keys": "6.21.0" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/type-utils": { - "version": "6.19.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.19.0.tgz", - "integrity": "sha512-mcvS6WSWbjiSxKCwBcXtOM5pRkPQ6kcDds/juxcy/727IQr3xMEcwr/YLHW2A2+Fp5ql6khjbKBzOyjuPqGi/w==", - "dev": true, - "dependencies": { - "@typescript-eslint/typescript-estree": "6.19.0", - "@typescript-eslint/utils": "6.19.0", - "debug": "^4.3.4", - "ts-api-utils": "^1.0.1" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/types": { - "version": "6.19.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.19.0.tgz", - "integrity": "sha512-lFviGV/vYhOy3m8BJ/nAKoAyNhInTdXpftonhWle66XHAtT1ouBlkjL496b5H5hb8dWXHwtypTqgtb/DEa+j5A==", - "dev": true, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/typescript-estree": { - "version": "6.19.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.19.0.tgz", - "integrity": "sha512-o/zefXIbbLBZ8YJ51NlkSAt2BamrK6XOmuxSR3hynMIzzyMY33KuJ9vuMdFSXW+H0tVvdF9qBPTHA91HDb4BIQ==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "6.19.0", - "@typescript-eslint/visitor-keys": "6.19.0", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "minimatch": "9.0.3", - "semver": "^7.5.4", - "ts-api-utils": "^1.0.1" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/visitor-keys": { - "version": "6.19.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.19.0.tgz", - "integrity": "sha512-hZaUCORLgubBvtGpp1JEFEazcuEdfxta9j4iUwdSAr7mEsYYAp3EAUyCZk3VEEqGj6W+AV4uWyrDGtrlawAsgQ==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "6.19.0", - "eslint-visitor-keys": "^3.4.1" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/types": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.21.0.tgz", - "integrity": "sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg==", - "dev": true, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/typescript-estree": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.1.0.tgz", - "integrity": "sha512-k7MyrbD6E463CBbSpcOnwa8oXRdHzH1WiVzOipK3L5KSML92ZKgUBrTlehdi7PEIMT8k0bQixHUGXggPAlKnOQ==", - "dev": true, - "peer": true, - "dependencies": { - "@typescript-eslint/types": "7.1.0", - "@typescript-eslint/visitor-keys": "7.1.0", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "minimatch": "9.0.3", - "semver": "^7.5.4", - "ts-api-utils": "^1.0.1" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/@typescript-eslint/types": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.1.0.tgz", - "integrity": "sha512-qTWjWieJ1tRJkxgZYXx6WUYtWlBc48YRxgY2JN1aGeVpkhmnopq+SUC8UEVGNXIvWH7XyuTjwALfG6bFEgCkQA==", - "dev": true, - "peer": true, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/@typescript-eslint/visitor-keys": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.1.0.tgz", - "integrity": "sha512-FhUqNWluiGNzlvnDZiXad4mZRhtghdoKW6e98GoEOYSu5cND+E39rG5KwJMUzeENwm1ztYBRqof8wMLP+wNPIA==", - "dev": true, - "peer": true, - "dependencies": { - "@typescript-eslint/types": "7.1.0", - "eslint-visitor-keys": "^3.4.1" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/utils": { - "version": "6.19.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.19.0.tgz", - "integrity": "sha512-QR41YXySiuN++/dC9UArYOg4X86OAYP83OWTewpVx5ct1IZhjjgTLocj7QNxGhWoTqknsgpl7L+hGygCO+sdYw==", - "dev": true, - "dependencies": { - "@eslint-community/eslint-utils": "^4.4.0", - "@types/json-schema": "^7.0.12", - "@types/semver": "^7.5.0", - "@typescript-eslint/scope-manager": "6.19.0", - "@typescript-eslint/types": "6.19.0", - "@typescript-eslint/typescript-estree": "6.19.0", - "semver": "^7.5.4" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0" - } - }, - "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/scope-manager": { - "version": "6.19.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.19.0.tgz", - "integrity": "sha512-dO1XMhV2ehBI6QN8Ufi7I10wmUovmLU0Oru3n5LVlM2JuzB4M+dVphCPLkVpKvGij2j/pHBWuJ9piuXx+BhzxQ==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "6.19.0", - "@typescript-eslint/visitor-keys": "6.19.0" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/types": { - "version": "6.19.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.19.0.tgz", - "integrity": "sha512-lFviGV/vYhOy3m8BJ/nAKoAyNhInTdXpftonhWle66XHAtT1ouBlkjL496b5H5hb8dWXHwtypTqgtb/DEa+j5A==", - "dev": true, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/typescript-estree": { - "version": "6.19.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.19.0.tgz", - "integrity": "sha512-o/zefXIbbLBZ8YJ51NlkSAt2BamrK6XOmuxSR3hynMIzzyMY33KuJ9vuMdFSXW+H0tVvdF9qBPTHA91HDb4BIQ==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "6.19.0", - "@typescript-eslint/visitor-keys": "6.19.0", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "minimatch": "9.0.3", - "semver": "^7.5.4", - "ts-api-utils": "^1.0.1" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/visitor-keys": { - "version": "6.19.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.19.0.tgz", - "integrity": "sha512-hZaUCORLgubBvtGpp1JEFEazcuEdfxta9j4iUwdSAr7mEsYYAp3EAUyCZk3VEEqGj6W+AV4uWyrDGtrlawAsgQ==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "6.19.0", - "eslint-visitor-keys": "^3.4.1" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/visitor-keys": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.21.0.tgz", - "integrity": "sha512-JJtkDduxLi9bivAB+cYOVMtbkqdPOhZ+ZI5LC47MIRrDV4Yn2o+ZnW10Nkmr28xRpSpdJ6Sm42Hjf2+REYXm0A==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "6.21.0", - "eslint-visitor-keys": "^3.4.1" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@ungap/promise-all-settled": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz", - "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==", - "dev": true - }, - "node_modules/@ungap/structured-clone": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", - "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==" - }, - "node_modules/@vitejs/plugin-basic-ssl": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-basic-ssl/-/plugin-basic-ssl-1.1.0.tgz", - "integrity": "sha512-wO4Dk/rm8u7RNhOf95ZzcEmC9rYOncYgvq4z3duaJrCgjN8BxAnDVyndanfcJZ0O6XZzHz6Q0hTimxTg8Y9g/A==", - "dev": true, - "engines": { - "node": ">=14.6.0" - }, - "peerDependencies": { - "vite": "^3.0.0 || ^4.0.0 || ^5.0.0" - } - }, - "node_modules/@webassemblyjs/ast": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.6.tgz", - "integrity": "sha512-IN1xI7PwOvLPgjcf180gC1bqn3q/QaOCwYUahIOhbYUu8KA/3tw2RT/T0Gidi1l7Hhj5D/INhJxiICObqpMu4Q==", - "dev": true, - "dependencies": { - "@webassemblyjs/helper-numbers": "1.11.6", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6" - } - }, - "node_modules/@webassemblyjs/floating-point-hex-parser": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.6.tgz", - "integrity": "sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw==", - "dev": true - }, - "node_modules/@webassemblyjs/helper-api-error": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.6.tgz", - "integrity": "sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q==", - "dev": true - }, - "node_modules/@webassemblyjs/helper-buffer": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.6.tgz", - "integrity": "sha512-z3nFzdcp1mb8nEOFFk8DrYLpHvhKC3grJD2ardfKOzmbmJvEf/tPIqCY+sNcwZIY8ZD7IkB2l7/pqhUhqm7hLA==", - "dev": true - }, - "node_modules/@webassemblyjs/helper-numbers": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.6.tgz", - "integrity": "sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g==", - "dev": true, - "dependencies": { - "@webassemblyjs/floating-point-hex-parser": "1.11.6", - "@webassemblyjs/helper-api-error": "1.11.6", - "@xtuc/long": "4.2.2" - } - }, - "node_modules/@webassemblyjs/helper-wasm-bytecode": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.6.tgz", - "integrity": "sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA==", - "dev": true - }, - "node_modules/@webassemblyjs/helper-wasm-section": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.6.tgz", - "integrity": "sha512-LPpZbSOwTpEC2cgn4hTydySy1Ke+XEu+ETXuoyvuyezHO3Kjdu90KK95Sh9xTbmjrCsUwvWwCOQQNta37VrS9g==", - "dev": true, - "dependencies": { - "@webassemblyjs/ast": "1.11.6", - "@webassemblyjs/helper-buffer": "1.11.6", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6", - "@webassemblyjs/wasm-gen": "1.11.6" - } - }, - "node_modules/@webassemblyjs/ieee754": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.6.tgz", - "integrity": "sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg==", - "dev": true, - "dependencies": { - "@xtuc/ieee754": "^1.2.0" - } - }, - "node_modules/@webassemblyjs/leb128": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.6.tgz", - "integrity": "sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ==", - "dev": true, - "dependencies": { - "@xtuc/long": "4.2.2" - } - }, - "node_modules/@webassemblyjs/utf8": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.6.tgz", - "integrity": "sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA==", - "dev": true - }, - "node_modules/@webassemblyjs/wasm-edit": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.6.tgz", - "integrity": "sha512-Ybn2I6fnfIGuCR+Faaz7YcvtBKxvoLV3Lebn1tM4o/IAJzmi9AWYIPWpyBfU8cC+JxAO57bk4+zdsTjJR+VTOw==", - "dev": true, - "dependencies": { - "@webassemblyjs/ast": "1.11.6", - "@webassemblyjs/helper-buffer": "1.11.6", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6", - "@webassemblyjs/helper-wasm-section": "1.11.6", - "@webassemblyjs/wasm-gen": "1.11.6", - "@webassemblyjs/wasm-opt": "1.11.6", - "@webassemblyjs/wasm-parser": "1.11.6", - "@webassemblyjs/wast-printer": "1.11.6" - } - }, - "node_modules/@webassemblyjs/wasm-gen": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.6.tgz", - "integrity": "sha512-3XOqkZP/y6B4F0PBAXvI1/bky7GryoogUtfwExeP/v7Nzwo1QLcq5oQmpKlftZLbT+ERUOAZVQjuNVak6UXjPA==", - "dev": true, - "dependencies": { - "@webassemblyjs/ast": "1.11.6", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6", - "@webassemblyjs/ieee754": "1.11.6", - "@webassemblyjs/leb128": "1.11.6", - "@webassemblyjs/utf8": "1.11.6" - } - }, - "node_modules/@webassemblyjs/wasm-opt": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.6.tgz", - "integrity": "sha512-cOrKuLRE7PCe6AsOVl7WasYf3wbSo4CeOk6PkrjS7g57MFfVUF9u6ysQBBODX0LdgSvQqRiGz3CXvIDKcPNy4g==", - "dev": true, - "dependencies": { - "@webassemblyjs/ast": "1.11.6", - "@webassemblyjs/helper-buffer": "1.11.6", - "@webassemblyjs/wasm-gen": "1.11.6", - "@webassemblyjs/wasm-parser": "1.11.6" - } - }, - "node_modules/@webassemblyjs/wasm-parser": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.6.tgz", - "integrity": "sha512-6ZwPeGzMJM3Dqp3hCsLgESxBGtT/OeCvCZ4TA1JUPYgmhAx38tTPR9JaKy0S5H3evQpO/h2uWs2j6Yc/fjkpTQ==", - "dev": true, - "dependencies": { - "@webassemblyjs/ast": "1.11.6", - "@webassemblyjs/helper-api-error": "1.11.6", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6", - "@webassemblyjs/ieee754": "1.11.6", - "@webassemblyjs/leb128": "1.11.6", - "@webassemblyjs/utf8": "1.11.6" - } - }, - "node_modules/@webassemblyjs/wast-printer": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.6.tgz", - "integrity": "sha512-JM7AhRcE+yW2GWYaKeHL5vt4xqee5N2WcezptmgyhNS+ScggqcT1OtXykhAb13Sn5Yas0j2uv9tHgrjwvzAP4A==", - "dev": true, - "dependencies": { - "@webassemblyjs/ast": "1.11.6", - "@xtuc/long": "4.2.2" - } - }, - "node_modules/@xtuc/ieee754": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", - "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", - "dev": true - }, - "node_modules/@xtuc/long": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", - "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", - "dev": true - }, - "node_modules/@yarnpkg/esbuild-plugin-pnp": { - "version": "3.0.0-rc.15", - "resolved": "https://registry.npmjs.org/@yarnpkg/esbuild-plugin-pnp/-/esbuild-plugin-pnp-3.0.0-rc.15.tgz", - "integrity": "sha512-kYzDJO5CA9sy+on/s2aIW0411AklfCi8Ck/4QDivOqsMKpStZA2SsR+X27VTggGwpStWaLrjJcDcdDMowtG8MA==", - "dev": true, - "dependencies": { - "tslib": "^2.4.0" - }, - "engines": { - "node": ">=14.15.0" - }, - "peerDependencies": { - "esbuild": ">=0.10.0" - } - }, - "node_modules/@yarnpkg/fslib": { - "version": "2.10.3", - "resolved": "https://registry.npmjs.org/@yarnpkg/fslib/-/fslib-2.10.3.tgz", - "integrity": "sha512-41H+Ga78xT9sHvWLlFOZLIhtU6mTGZ20pZ29EiZa97vnxdohJD2AF42rCoAoWfqUz486xY6fhjMH+DYEM9r14A==", - "dev": true, - "dependencies": { - "@yarnpkg/libzip": "^2.3.0", - "tslib": "^1.13.0" - }, - "engines": { - "node": ">=12 <14 || 14.2 - 14.9 || >14.10.0" - } - }, - "node_modules/@yarnpkg/fslib/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - }, - "node_modules/@yarnpkg/libzip": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@yarnpkg/libzip/-/libzip-2.3.0.tgz", - "integrity": "sha512-6xm38yGVIa6mKm/DUCF2zFFJhERh/QWp1ufm4cNUvxsONBmfPg8uZ9pZBdOmF6qFGr/HlT6ABBkCSx/dlEtvWg==", - "dev": true, - "dependencies": { - "@types/emscripten": "^1.39.6", - "tslib": "^1.13.0" - }, - "engines": { - "node": ">=12 <14 || 14.2 - 14.9 || >14.10.0" - } - }, - "node_modules/@yarnpkg/libzip/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - }, - "node_modules/@yarnpkg/lockfile": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz", - "integrity": "sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==", - "dev": true - }, - "node_modules/@yarnpkg/parsers": { - "version": "3.0.0-rc.46", - "resolved": "https://registry.npmjs.org/@yarnpkg/parsers/-/parsers-3.0.0-rc.46.tgz", - "integrity": "sha512-aiATs7pSutzda/rq8fnuPwTglyVwjM22bNnK2ZgjrpAjQHSSl3lztd2f9evst1W/qnC58DRz7T7QndUDumAR4Q==", - "dev": true, - "dependencies": { - "js-yaml": "^3.10.0", - "tslib": "^2.4.0" - }, - "engines": { - "node": ">=14.15.0" - } - }, - "node_modules/@zkochan/js-yaml": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/@zkochan/js-yaml/-/js-yaml-0.0.6.tgz", - "integrity": "sha512-nzvgl3VfhcELQ8LyVrYOru+UtAy1nrygk2+AGbTm8a5YcO6o8lSjAT+pfg3vJWxIoZKOUhrK6UU7xW/+00kQrg==", - "dev": true, - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/@zkochan/js-yaml/node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - }, - "node_modules/abab": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz", - "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==", - "deprecated": "Use your platform's native atob() and btoa() methods instead", - "dev": true - }, - "node_modules/abbrev": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-2.0.0.tgz", - "integrity": "sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==", - "dev": true, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/accepts": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", - "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", - "dev": true, - "dependencies": { - "mime-types": "~2.1.34", - "negotiator": "0.6.3" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/accessible-autocomplete": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/accessible-autocomplete/-/accessible-autocomplete-2.0.4.tgz", - "integrity": "sha512-2p0txrSpvs5wXFUeQJHMheDPTZVSEmiUHWlEPb7vJnv2Dd1xPfoLnBQQMfNbTSit2pL/9sSQYESuD2Yyohd4Yw==", - "dependencies": { - "preact": "^8.3.1" - } - }, - "node_modules/acorn": { - "version": "8.11.3", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", - "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-globals": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-7.0.1.tgz", - "integrity": "sha512-umOSDSDrfHbTNPuNpC2NSnnA3LUrqpevPb4T9jRx4MagXNS0rs+gwiTcAvqCRmsD6utzsrzNt+ebm00SNWiC3Q==", - "dev": true, - "dependencies": { - "acorn": "^8.1.0", - "acorn-walk": "^8.0.2" - } - }, - "node_modules/acorn-import-assertions": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz", - "integrity": "sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==", - "dev": true, - "peerDependencies": { - "acorn": "^8" - } - }, - "node_modules/acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/acorn-node": { - "version": "1.8.2", - "resolved": "https://registry.npmjs.org/acorn-node/-/acorn-node-1.8.2.tgz", - "integrity": "sha512-8mt+fslDufLYntIoPAaIMUe/lrbrehIiwmR3t2k9LljIzoigEPF27eLk2hy8zSGzmR/ogr7zbRKINMo1u0yh5A==", - "dev": true, - "dependencies": { - "acorn": "^7.0.0", - "acorn-walk": "^7.0.0", - "xtend": "^4.0.2" - } - }, - "node_modules/acorn-node/node_modules/acorn": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", - "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", - "dev": true, - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-node/node_modules/acorn-walk": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz", - "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==", - "dev": true, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-walk": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.2.tgz", - "integrity": "sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==", - "dev": true, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/address": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/address/-/address-1.2.2.tgz", - "integrity": "sha512-4B/qKCfeE/ODUaAUpSwfzazo5x29WD4r3vXiWsB7I2mSDAihwEqKO+g8GELZUQSSAo5e1XTYh3ZVfLyxBc12nA==", - "dev": true, - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/adjust-sourcemap-loader": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/adjust-sourcemap-loader/-/adjust-sourcemap-loader-4.0.0.tgz", - "integrity": "sha512-OXwN5b9pCUXNQHJpwwD2qP40byEmSgzj8B4ydSN0uMNYWiFmJ6x6KwUllMmfk8Rwu/HJDFR7U8ubsWBoN0Xp0A==", - "dev": true, - "dependencies": { - "loader-utils": "^2.0.0", - "regex-parser": "^2.2.11" - }, - "engines": { - "node": ">=8.9" - } - }, - "node_modules/adjust-sourcemap-loader/node_modules/loader-utils": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", - "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", - "dev": true, - "dependencies": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^2.1.2" - }, - "engines": { - "node": ">=8.9.0" - } - }, - "node_modules/adm-zip": { - "version": "0.5.10", - "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.5.10.tgz", - "integrity": "sha512-x0HvcHqVJNTPk/Bw8JbLWlWoo6Wwnsug0fnYYro1HBrjxZ3G7/AZk7Ahv8JwDe1uIcz8eBqvu86FuF1POiG7vQ==", - "dev": true, - "engines": { - "node": ">=6.0" - } - }, - "node_modules/agent-base": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.0.tgz", - "integrity": "sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg==", - "dev": true, - "dependencies": { - "debug": "^4.3.4" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/aggregate-error": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", - "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", - "dev": true, - "dependencies": { - "clean-stack": "^2.0.0", - "indent-string": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/ajv": { - "version": "8.12.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", - "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ajv-formats": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", - "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", - "dev": true, - "dependencies": { - "ajv": "^8.0.0" - }, - "peerDependencies": { - "ajv": "^8.0.0" - }, - "peerDependenciesMeta": { - "ajv": { - "optional": true - } - } - }, - "node_modules/ajv-keywords": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", - "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", - "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.3" - }, - "peerDependencies": { - "ajv": "^8.8.2" - } - }, - "node_modules/amdefine": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", - "integrity": "sha512-S2Hw0TtNkMJhIabBwIojKL9YHO5T0n5eNqWJ7Lrlel/zDbftQpxpapi8tZs3X1HWa+u+QeydGmzzNU0m09+Rcg==", - "dev": true, - "optional": true, - "engines": { - "node": ">=0.4.2" - } - }, - "node_modules/ansi-colors": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", - "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/ansi-escapes": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", - "dev": true, - "dependencies": { - "type-fest": "^0.21.3" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ansi-escapes/node_modules/type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ansi-html-community": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/ansi-html-community/-/ansi-html-community-0.0.8.tgz", - "integrity": "sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw==", - "dev": true, - "engines": [ - "node >= 0.8.0" - ], - "bin": { - "ansi-html": "bin/ansi-html" - } - }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/ansi-styles/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/ansi-styles/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true - }, - "node_modules/anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "dev": true, - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/anymatch/node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/apache-crypt": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/apache-crypt/-/apache-crypt-1.2.6.tgz", - "integrity": "sha512-072WetlM4blL8PREJVeY+WHiUh1R5VNt2HfceGS8aKqttPHcmqE5pkKuXPz/ULmJOFkc8Hw3kfKl6vy7Qka6DA==", - "dev": true, - "dependencies": { - "unix-crypt-td-js": "^1.1.4" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/apache-md5": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/apache-md5/-/apache-md5-1.1.8.tgz", - "integrity": "sha512-FCAJojipPn0bXjuEpjOOOMN8FZDkxfWWp4JGN9mifU2IhxvKyXZYqpzPHdnTSUpmPDy+tsslB6Z1g+Vg6nVbYA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/app-root-dir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/app-root-dir/-/app-root-dir-1.0.2.tgz", - "integrity": "sha512-jlpIfsOoNoafl92Sz//64uQHGSyMrD2vYG5d8o2a4qGvyNCvXur7bzIsWtAC/6flI2RYAp3kv8rsfBtaLm7w0g==", - "dev": true - }, - "node_modules/arch": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/arch/-/arch-2.2.0.tgz", - "integrity": "sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/arg": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", - "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "dev": true - }, - "node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "dependencies": { - "sprintf-js": "~1.0.2" - } - }, - "node_modules/aria-hidden": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.3.tgz", - "integrity": "sha512-xcLxITLe2HYa1cnYnwCjkOO1PqUHQpozB8x9AR0OgWN2woOBi5kSDVxKfd0b7sb1hw5qFeJhXm9H1nu3xSfLeQ==", - "dev": true, - "dependencies": { - "tslib": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/aria-query": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz", - "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", - "dev": true, - "dependencies": { - "dequal": "^2.0.3" - } - }, - "node_modules/array-buffer-byte-length": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz", - "integrity": "sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==", - "dependencies": { - "call-bind": "^1.0.5", - "is-array-buffer": "^3.0.4" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", - "dev": true - }, - "node_modules/array-from": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/array-from/-/array-from-2.1.1.tgz", - "integrity": "sha512-GQTc6Uupx1FCavi5mPzBvVT7nEOeWMmUA9P95wpfpW1XwMSKs+KaymD5C2Up7KAUKg/mYwbsUYzdZWcoajlNZg==", - "dev": true - }, - "node_modules/array-ify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/array-ify/-/array-ify-1.0.0.tgz", - "integrity": "sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng==", - "dev": true - }, - "node_modules/array-includes": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.7.tgz", - "integrity": "sha512-dlcsNBIiWhPkHdOEEKnehA+RNUWDc4UqFtnIXU4uuYDPtA4LDkr7qip2p0VvFAEXNDr0yWZ9PJyIRiGjRLQzwQ==", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "get-intrinsic": "^1.2.1", - "is-string": "^1.0.7" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/array.prototype.filter": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/array.prototype.filter/-/array.prototype.filter-1.0.3.tgz", - "integrity": "sha512-VizNcj/RGJiUyQBgzwxzE5oHdeuXY5hSbbmKMlphj1cy1Vl7Pn2asCGbSrru6hSQjmCzqTBPVWAF/whmEOVHbw==", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "es-array-method-boxes-properly": "^1.0.0", - "is-string": "^1.0.7" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array.prototype.findlastindex": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.4.tgz", - "integrity": "sha512-hzvSHUshSpCflDR1QMUBLHGHP1VIEBegT4pix9H/Z92Xw3ySoy6c2qh7lJWTJnRJ8JCZ9bJNCgTyYaJGcJu6xQ==", - "dependencies": { - "call-bind": "^1.0.5", - "define-properties": "^1.2.1", - "es-abstract": "^1.22.3", - "es-errors": "^1.3.0", - "es-shim-unscopables": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array.prototype.flat": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.2.tgz", - "integrity": "sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "es-shim-unscopables": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array.prototype.flatmap": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.2.tgz", - "integrity": "sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "es-shim-unscopables": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array.prototype.tosorted": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.3.tgz", - "integrity": "sha512-/DdH4TiTmOKzyQbp/eadcCVexiCb36xJg7HshYOYJnNZFDj33GEv0P7GxsynpShhq4OLYJzbGcBDkLsDt7MnNg==", - "dev": true, - "peer": true, - "dependencies": { - "call-bind": "^1.0.5", - "define-properties": "^1.2.1", - "es-abstract": "^1.22.3", - "es-errors": "^1.1.0", - "es-shim-unscopables": "^1.0.2" - } - }, - "node_modules/arraybuffer.prototype.slice": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.3.tgz", - "integrity": "sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==", - "dependencies": { - "array-buffer-byte-length": "^1.0.1", - "call-bind": "^1.0.5", - "define-properties": "^1.2.1", - "es-abstract": "^1.22.3", - "es-errors": "^1.2.1", - "get-intrinsic": "^1.2.3", - "is-array-buffer": "^3.0.4", - "is-shared-array-buffer": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/arrify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", - "integrity": "sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/asn1": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", - "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", - "dev": true, - "dependencies": { - "safer-buffer": "~2.1.0" - } - }, - "node_modules/assert": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/assert/-/assert-2.1.0.tgz", - "integrity": "sha512-eLHpSK/Y4nhMJ07gDaAzoX/XAKS8PSaojml3M0DM4JpV1LAi5JOJ/p6H/XWrl8L+DzVEvVCW1z3vWAaB9oTsQw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "is-nan": "^1.3.2", - "object-is": "^1.1.5", - "object.assign": "^4.1.4", - "util": "^0.12.5" - } - }, - "node_modules/assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==", - "dev": true, - "engines": { - "node": ">=0.8" - } - }, - "node_modules/ast-transform": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/ast-transform/-/ast-transform-0.0.0.tgz", - "integrity": "sha512-e/JfLiSoakfmL4wmTGPjv0HpTICVmxwXgYOB8x+mzozHL8v+dSfCbrJ8J8hJ0YBP0XcYu1aLZ6b/3TnxNK3P2A==", - "dev": true, - "dependencies": { - "escodegen": "~1.2.0", - "esprima": "~1.0.4", - "through": "~2.3.4" - } - }, - "node_modules/ast-transform/node_modules/escodegen": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.2.0.tgz", - "integrity": "sha512-yLy3Cc+zAC0WSmoT2fig3J87TpQ8UaZGx8ahCAs9FL8qNbyV7CVyPKS74DG4bsHiL5ew9sxdYx131OkBQMFnvA==", - "dev": true, - "dependencies": { - "esprima": "~1.0.4", - "estraverse": "~1.5.0", - "esutils": "~1.0.0" - }, - "bin": { - "escodegen": "bin/escodegen.js", - "esgenerate": "bin/esgenerate.js" - }, - "engines": { - "node": ">=0.4.0" - }, - "optionalDependencies": { - "source-map": "~0.1.30" - } - }, - "node_modules/ast-transform/node_modules/esprima": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-1.0.4.tgz", - "integrity": "sha512-rp5dMKN8zEs9dfi9g0X1ClLmV//WRyk/R15mppFNICIFRG5P92VP7Z04p8pk++gABo9W2tY+kHyu6P1mEHgmTA==", - "dev": true, - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/ast-transform/node_modules/estraverse": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-1.5.1.tgz", - "integrity": "sha512-FpCjJDfmo3vsc/1zKSeqR5k42tcIhxFIlvq+h9j0fO2q/h2uLKyweq7rYJ+0CoVvrGQOxIS5wyBrW/+vF58BUQ==", - "dev": true, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/ast-transform/node_modules/esutils": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-1.0.0.tgz", - "integrity": "sha512-x/iYH53X3quDwfHRz4y8rn4XcEwwCJeWsul9pF1zldMbGtgOtMNBEOuYWwB1EQlK2LRa1fev3YAgym/RElp5Cg==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/ast-transform/node_modules/source-map": { - "version": "0.1.43", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.1.43.tgz", - "integrity": "sha512-VtCvB9SIQhk3aF6h+N85EaqIaBFIAfZ9Cu+NJHHVvc8BbEcnvDcFw6sqQ2dQrT6SlOrZq3tIvyD9+EGq/lJryQ==", - "dev": true, - "optional": true, - "dependencies": { - "amdefine": ">=0.0.4" - }, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/ast-types": { - "version": "0.7.8", - "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.7.8.tgz", - "integrity": "sha512-RIOpVnVlltB6PcBJ5BMLx+H+6JJ/zjDGU0t7f0L6c2M1dqcK92VQopLBlPQ9R80AVXelfqYgjcPLtHtDbNFg0Q==", - "dev": true, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/ast-types-flow": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.8.tgz", - "integrity": "sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==", - "dev": true, - "peer": true - }, - "node_modules/astral-regex": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", - "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/async": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz", - "integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==", - "dev": true - }, - "node_modules/async-limiter": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", - "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==", - "dev": true - }, - "node_modules/asynciterator.prototype": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/asynciterator.prototype/-/asynciterator.prototype-1.0.0.tgz", - "integrity": "sha512-wwHYEIS0Q80f5mosx3L/dfG5t5rjEa9Ft51GTaNt862EnpyGHpgz2RkZvLPp1oF5TnAiTohkEKVEu8pQPJI7Vg==", - "dev": true, - "peer": true, - "dependencies": { - "has-symbols": "^1.0.3" - } - }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", - "dev": true - }, - "node_modules/at-least-node": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", - "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", - "dev": true, - "engines": { - "node": ">= 4.0.0" - } - }, - "node_modules/autoprefixer": { - "version": "10.4.17", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.17.tgz", - "integrity": "sha512-/cpVNRLSfhOtcGflT13P2794gVSgmPgTR+erw5ifnMLZb0UnSlkK4tquLmkd3BhA+nLo5tX8Cu0upUsGKvKbmg==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/autoprefixer" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "dependencies": { - "browserslist": "^4.22.2", - "caniuse-lite": "^1.0.30001578", - "fraction.js": "^4.3.7", - "normalize-range": "^0.1.2", - "picocolors": "^1.0.0", - "postcss-value-parser": "^4.2.0" - }, - "bin": { - "autoprefixer": "bin/autoprefixer" - }, - "engines": { - "node": "^10 || ^12 || >=14" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/available-typed-arrays": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", - "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", - "dependencies": { - "possible-typed-array-names": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/aws-sign2": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==", - "dev": true, - "engines": { - "node": "*" - } - }, - "node_modules/aws4": { - "version": "1.12.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.12.0.tgz", - "integrity": "sha512-NmWvPnx0F1SfrQbYwOi7OeaNGokp9XhzNioJ/CSBs8Qa4vxug81mhJEAVZwxXuBmYB5KDRfMq/F3RR0BIU7sWg==", - "dev": true - }, - "node_modules/axe-core": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.7.0.tgz", - "integrity": "sha512-M0JtH+hlOL5pLQwHOLNYZaXuhqmvS8oExsqB1SBYgA4Dk7u/xx+YdGHXaK5pyUfed5mYXdlYiphWq3G8cRi5JQ==", - "dev": true, - "peer": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/axios": { - "version": "1.6.7", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.7.tgz", - "integrity": "sha512-/hDJGff6/c7u0hDkvkGxR/oy6CbCs8ziCsC7SqmhjfozqiJGc8Z11wrv9z9lYfY4K8l+H9TpjcMDX0xOZmx+RA==", - "dev": true, - "dependencies": { - "follow-redirects": "^1.15.4", - "form-data": "^4.0.0", - "proxy-from-env": "^1.1.0" - } - }, - "node_modules/axobject-query": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.0.0.tgz", - "integrity": "sha512-+60uv1hiVFhHZeO+Lz0RYzsVHy5Wr1ayX0mwda9KPDVLNJgZ1T9Ny7VmFbLDzxsH0D87I86vgj3gFrjTJUYznw==", - "dev": true, - "dependencies": { - "dequal": "^2.0.3" - } - }, - "node_modules/babel-core": { - "version": "7.0.0-bridge.0", - "resolved": "https://registry.npmjs.org/babel-core/-/babel-core-7.0.0-bridge.0.tgz", - "integrity": "sha512-poPX9mZH/5CSanm50Q+1toVci6pv5KSRv/5TWCwtzQS5XEwn40BcCrgIeMFWP9CKKIniKXNxoIOnOq4VVlGXhg==", - "dev": true, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/babel-jest": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", - "integrity": "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==", - "dev": true, - "dependencies": { - "@jest/transform": "^29.7.0", - "@types/babel__core": "^7.1.14", - "babel-plugin-istanbul": "^6.1.1", - "babel-preset-jest": "^29.6.3", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "@babel/core": "^7.8.0" - } - }, - "node_modules/babel-jest/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/babel-jest/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, - "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/babel-jest/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/babel-jest/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/babel-loader": { - "version": "9.1.3", - "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-9.1.3.tgz", - "integrity": "sha512-xG3ST4DglodGf8qSwv0MdeWLhrDsw/32QMdTO5T1ZIp9gQur0HkCyFs7Awskr10JKXFXwpAhiCuYX5oGXnRGbw==", - "dev": true, - "dependencies": { - "find-cache-dir": "^4.0.0", - "schema-utils": "^4.0.0" - }, - "engines": { - "node": ">= 14.15.0" - }, - "peerDependencies": { - "@babel/core": "^7.12.0", - "webpack": ">=5" - } - }, - "node_modules/babel-loader/node_modules/find-cache-dir": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-4.0.0.tgz", - "integrity": "sha512-9ZonPT4ZAK4a+1pUPVPZJapbi7O5qbbJPdYw/NOQWZZbVLdDTYM3A4R9z/DpAM08IDaFGsvPgiGZ82WEwUDWjg==", - "dev": true, - "dependencies": { - "common-path-prefix": "^3.0.0", - "pkg-dir": "^7.0.0" - }, - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/babel-loader/node_modules/find-up": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-6.3.0.tgz", - "integrity": "sha512-v2ZsoEuVHYy8ZIlYqwPe/39Cy+cFDzp4dXPaxNvkEuouymu+2Jbz0PxpKarJHYJTmv2HWT3O382qY8l4jMWthw==", - "dev": true, - "dependencies": { - "locate-path": "^7.1.0", - "path-exists": "^5.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/babel-loader/node_modules/locate-path": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-7.2.0.tgz", - "integrity": "sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==", - "dev": true, - "dependencies": { - "p-locate": "^6.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/babel-loader/node_modules/p-limit": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-4.0.0.tgz", - "integrity": "sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==", - "dev": true, - "dependencies": { - "yocto-queue": "^1.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/babel-loader/node_modules/p-locate": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-6.0.0.tgz", - "integrity": "sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==", - "dev": true, - "dependencies": { - "p-limit": "^4.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/babel-loader/node_modules/path-exists": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-5.0.0.tgz", - "integrity": "sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==", - "dev": true, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - } - }, - "node_modules/babel-loader/node_modules/pkg-dir": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-7.0.0.tgz", - "integrity": "sha512-Ie9z/WINcxxLp27BKOCHGde4ITq9UklYKDzVo1nhk5sqGEXU3FpkwP5GM2voTGJkGd9B3Otl+Q4uwSOeSUtOBA==", - "dev": true, - "dependencies": { - "find-up": "^6.3.0" - }, - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/babel-loader/node_modules/yocto-queue": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.0.0.tgz", - "integrity": "sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==", - "dev": true, - "engines": { - "node": ">=12.20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/babel-plugin-istanbul": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", - "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.0.0", - "@istanbuljs/load-nyc-config": "^1.0.0", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-instrument": "^5.0.4", - "test-exclude": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/babel-plugin-jest-hoist": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz", - "integrity": "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==", - "dev": true, - "dependencies": { - "@babel/template": "^7.3.3", - "@babel/types": "^7.3.3", - "@types/babel__core": "^7.1.14", - "@types/babel__traverse": "^7.0.6" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/babel-plugin-polyfill-corejs2": { - "version": "0.4.8", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.8.tgz", - "integrity": "sha512-OtIuQfafSzpo/LhnJaykc0R/MMnuLSSVjVYy9mHArIZ9qTCSZ6TpWCuEKZYVoN//t8HqBNScHrOtCrIK5IaGLg==", - "dev": true, - "dependencies": { - "@babel/compat-data": "^7.22.6", - "@babel/helper-define-polyfill-provider": "^0.5.0", - "semver": "^6.3.1" - }, - "peerDependencies": { - "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" - } - }, - "node_modules/babel-plugin-polyfill-corejs2/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/babel-plugin-polyfill-corejs3": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.9.0.tgz", - "integrity": "sha512-7nZPG1uzK2Ymhy/NbaOWTg3uibM2BmGASS4vHS4szRZAIR8R6GwA/xAujpdrXU5iyklrimWnLWU+BLF9suPTqg==", - "dev": true, - "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.5.0", - "core-js-compat": "^3.34.0" - }, - "peerDependencies": { - "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" - } - }, - "node_modules/babel-plugin-polyfill-regenerator": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.5.5.tgz", - "integrity": "sha512-OJGYZlhLqBh2DDHeqAxWB1XIvr49CxiJ2gIt61/PU55CQK4Z58OzMqjDe1zwQdQk+rBYsRc+1rJmdajM3gimHg==", - "dev": true, - "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.5.0" - }, - "peerDependencies": { - "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" - } - }, - "node_modules/babel-preset-current-node-syntax": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz", - "integrity": "sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==", - "dev": true, - "dependencies": { - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/plugin-syntax-bigint": "^7.8.3", - "@babel/plugin-syntax-class-properties": "^7.8.3", - "@babel/plugin-syntax-import-meta": "^7.8.3", - "@babel/plugin-syntax-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.8.3", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-top-level-await": "^7.8.3" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/babel-preset-jest": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz", - "integrity": "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==", - "dev": true, - "dependencies": { - "babel-plugin-jest-hoist": "^29.6.3", - "babel-preset-current-node-syntax": "^1.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" - }, - "node_modules/base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/basic-auth": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", - "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==", - "dev": true, - "dependencies": { - "safe-buffer": "5.1.2" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/batch": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", - "integrity": "sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==", - "dev": true - }, - "node_modules/bcrypt-pbkdf": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", - "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==", - "dev": true, - "dependencies": { - "tweetnacl": "^0.14.3" - } - }, - "node_modules/bcryptjs": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-2.4.3.tgz", - "integrity": "sha512-V/Hy/X9Vt7f3BbPJEi8BdVFMByHi+jNXrYkW3huaybV/kQ0KJg0Y6PkEMbn+zeT+i+SiKZ/HMqJGIIt4LZDqNQ==", - "dev": true - }, - "node_modules/better-opn": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/better-opn/-/better-opn-3.0.2.tgz", - "integrity": "sha512-aVNobHnJqLiUelTaHat9DZ1qM2w0C0Eym4LPI/3JxOnSokGVdsl1T1kN7TFvsEAD8G47A6VKQ0TVHqbBnYMJlQ==", - "dev": true, - "dependencies": { - "open": "^8.0.4" - }, - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/big-integer": { - "version": "1.6.52", - "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.52.tgz", - "integrity": "sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg==", - "dev": true, - "engines": { - "node": ">=0.6" - } - }, - "node_modules/big.js": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", - "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", - "dev": true, - "engines": { - "node": "*" - } - }, - "node_modules/binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/bl": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", - "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", - "dev": true, - "dependencies": { - "buffer": "^5.5.0", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" - } - }, - "node_modules/blob-util": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/blob-util/-/blob-util-2.0.2.tgz", - "integrity": "sha512-T7JQa+zsXXEa6/8ZhHcQEW1UFfVM49Ts65uBkFL6fz2QmrElqmbajIDJvuA0tEhRe5eIjpV9ZF+0RfZR9voJFQ==", - "dev": true - }, - "node_modules/bluebird": { - "version": "3.7.2", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", - "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", - "dev": true - }, - "node_modules/body-parser": { - "version": "1.20.2", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz", - "integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==", - "dev": true, - "dependencies": { - "bytes": "3.1.2", - "content-type": "~1.0.5", - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "on-finished": "2.4.1", - "qs": "6.11.0", - "raw-body": "2.5.2", - "type-is": "~1.6.18", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } - }, - "node_modules/body-parser/node_modules/bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", - "dev": true, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/body-parser/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/body-parser/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true - }, - "node_modules/body-parser/node_modules/qs": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", - "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", - "dev": true, - "dependencies": { - "side-channel": "^1.0.4" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/bonjour-service": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/bonjour-service/-/bonjour-service-1.2.1.tgz", - "integrity": "sha512-oSzCS2zV14bh2kji6vNe7vrpJYCHGvcZnlffFQ1MEoX/WOeQ/teD8SYWKR942OI3INjq8OMNJlbPK5LLLUxFDw==", - "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.3", - "multicast-dns": "^7.2.5" - } - }, - "node_modules/boolbase": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", - "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", - "dev": true - }, - "node_modules/bootstrap.native": { - "version": "5.0.11", - "resolved": "https://registry.npmjs.org/bootstrap.native/-/bootstrap.native-5.0.11.tgz", - "integrity": "sha512-bk2i4sQcQk2KuCTs1yygTa+JGjZOpKzIZ/It6TZZOO/Q+PmVGuKuIbrznXF64BUFxXaPNy7gO9LnE7vjGdauSQ==", - "dev": true, - "dependencies": { - "@thednp/event-listener": "^2.0.4", - "@thednp/shorty": "^2.0.0" - }, - "engines": { - "node": ">=16", - "pnpm": ">=8.6.0" - } - }, - "node_modules/bplist-parser": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/bplist-parser/-/bplist-parser-0.2.0.tgz", - "integrity": "sha512-z0M+byMThzQmD9NILRniCUXYsYpjwnlO8N5uCFaCqIOpqRsJCrQL9NK3JsD67CN5a08nF5oIL2bD6loTdHOuKw==", - "dev": true, - "dependencies": { - "big-integer": "^1.6.44" - }, - "engines": { - "node": ">= 5.10.0" - } - }, - "node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dependencies": { - "fill-range": "^7.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/brfs": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brfs/-/brfs-2.0.2.tgz", - "integrity": "sha512-IrFjVtwu4eTJZyu8w/V2gxU7iLTtcHih67sgEdzrhjLBMHp2uYefUBfdM4k2UvcuWMgV7PQDZHSLeNWnLFKWVQ==", - "dev": true, - "dependencies": { - "quote-stream": "^1.0.1", - "resolve": "^1.1.5", - "static-module": "^3.0.2", - "through2": "^2.0.0" - }, - "bin": { - "brfs": "bin/cmd.js" - } - }, - "node_modules/brfs/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "dev": true - }, - "node_modules/brfs/node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "dev": true, - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/brfs/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, - "node_modules/brfs/node_modules/through2": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", - "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", - "dev": true, - "dependencies": { - "readable-stream": "~2.3.6", - "xtend": "~4.0.1" - } - }, - "node_modules/brotli": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/brotli/-/brotli-1.3.3.tgz", - "integrity": "sha512-oTKjJdShmDuGW94SyyaoQvAjf30dZaHnjJ8uAF+u2/vGJkJbJPJAT1gDiOJP5v1Zb6f9KEyW/1HpuaWIXtGHPg==", - "dev": true, - "dependencies": { - "base64-js": "^1.1.2" - } - }, - "node_modules/browser-assert": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/browser-assert/-/browser-assert-1.2.1.tgz", - "integrity": "sha512-nfulgvOR6S4gt9UKCeGJOuSGBPGiFT6oQ/2UBnvTY/5aQ1PnksW72fhZkM30DzoRRv2WpwZf1vHHEr3mtuXIWQ==", - "dev": true - }, - "node_modules/browser-resolve": { - "version": "1.11.3", - "resolved": "https://registry.npmjs.org/browser-resolve/-/browser-resolve-1.11.3.tgz", - "integrity": "sha512-exDi1BYWB/6raKHmDTCicQfTkqwN5fioMFV4j8BsfMU4R2DK/QfZfK7kOVkmWCNANf0snkBzqGqAJBao9gZMdQ==", - "dev": true, - "dependencies": { - "resolve": "1.1.7" - } - }, - "node_modules/browser-resolve/node_modules/resolve": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", - "integrity": "sha512-9znBF0vBcaSN3W2j7wKvdERPwqTxSpCq+if5C0WoTCyV9n24rua28jeuQ2pL/HOf+yUe/Mef+H/5p60K0Id3bg==", - "dev": true - }, - "node_modules/browser-stdout": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", - "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", - "dev": true - }, - "node_modules/browserify-optional": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/browserify-optional/-/browserify-optional-1.0.1.tgz", - "integrity": "sha512-VrhjbZ+Ba5mDiSYEuPelekQMfTbhcA2DhLk2VQWqdcCROWeFqlTcXZ7yfRkXCIl8E+g4gINJYJiRB7WEtfomAQ==", - "dev": true, - "dependencies": { - "ast-transform": "0.0.0", - "ast-types": "^0.7.0", - "browser-resolve": "^1.8.1" - } - }, - "node_modules/browserify-zlib": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.1.4.tgz", - "integrity": "sha512-19OEpq7vWgsH6WkvkBJQDFvJS1uPcbFOQ4v9CU839dO+ZZXUZO6XpE6hNCqvlIIj+4fZvRiJ6DsAQ382GwiyTQ==", - "dev": true, - "dependencies": { - "pako": "~0.2.0" - } - }, - "node_modules/browserslist": { - "version": "4.23.0", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.0.tgz", - "integrity": "sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "dependencies": { - "caniuse-lite": "^1.0.30001587", - "electron-to-chromium": "^1.4.668", - "node-releases": "^2.0.14", - "update-browserslist-db": "^1.0.13" - }, - "bin": { - "browserslist": "cli.js" - }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - } - }, - "node_modules/bs-logger": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz", - "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==", - "dev": true, - "dependencies": { - "fast-json-stable-stringify": "2.x" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/bser": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", - "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", - "dev": true, - "dependencies": { - "node-int64": "^0.4.0" - } - }, - "node_modules/buffer": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.1.13" - } - }, - "node_modules/buffer-crc32": { - "version": "0.2.13", - "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", - "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", - "dev": true, - "engines": { - "node": "*" - } - }, - "node_modules/buffer-equal": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/buffer-equal/-/buffer-equal-0.0.1.tgz", - "integrity": "sha512-RgSV6InVQ9ODPdLWJ5UAqBqJBOg370Nz6ZQtRzpt6nUjc8v0St97uJ4PYC6NztqIScrAXafKM3mZPMygSe1ggA==", - "dev": true, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "dev": true - }, - "node_modules/builtins": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/builtins/-/builtins-5.0.1.tgz", - "integrity": "sha512-qwVpFEHNfhYJIzNRBvd2C1kyo6jz3ZSMPyyuR47OPdiKWlbYnZNyDWuyR175qDnAJLiCo5fBBqPb3RiXgWlkOQ==", - "dev": true, - "dependencies": { - "semver": "^7.0.0" - } - }, - "node_modules/bytes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", - "integrity": "sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==", - "dev": true, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/cacache": { - "version": "18.0.2", - "resolved": "https://registry.npmjs.org/cacache/-/cacache-18.0.2.tgz", - "integrity": "sha512-r3NU8h/P+4lVUHfeRw1dtgQYar3DZMm4/cm2bZgOvrFC/su7budSOeqh52VJIC4U4iG1WWwV6vRW0znqBvxNuw==", - "dev": true, - "dependencies": { - "@npmcli/fs": "^3.1.0", - "fs-minipass": "^3.0.0", - "glob": "^10.2.2", - "lru-cache": "^10.0.1", - "minipass": "^7.0.3", - "minipass-collect": "^2.0.1", - "minipass-flush": "^1.0.5", - "minipass-pipeline": "^1.2.4", - "p-map": "^4.0.0", - "ssri": "^10.0.0", - "tar": "^6.1.11", - "unique-filename": "^3.0.0" - }, - "engines": { - "node": "^16.14.0 || >=18.0.0" - } - }, - "node_modules/cacache/node_modules/lru-cache": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.0.tgz", - "integrity": "sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==", - "dev": true, - "engines": { - "node": "14 || >=16.14" - } - }, - "node_modules/cachedir": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/cachedir/-/cachedir-2.4.0.tgz", - "integrity": "sha512-9EtFOZR8g22CL7BWjJ9BUx1+A/djkofnyW3aOXZORNW2kxoUpx2h+uN2cOqwPmFhnpVmxg+KW2OjOSgChTEvsQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/call-bind": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", - "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", - "dependencies": { - "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.4", - "set-function-length": "^1.2.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/callsite": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/callsite/-/callsite-1.0.0.tgz", - "integrity": "sha512-0vdNRFXn5q+dtOqjfFtmtlI9N2eVZ7LMyEV2iKC5mEEFvSg/69Ml6b/WU2qF8W1nLRa0wiSrDT3Y5jOHZCwKPQ==", - "dev": true, - "engines": { - "node": "*" - } - }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "engines": { - "node": ">=6" - } - }, - "node_modules/camel-case": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz", - "integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==", - "dev": true, - "dependencies": { - "pascal-case": "^3.1.2", - "tslib": "^2.0.3" - } - }, - "node_modules/camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/camelcase-keys": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-6.2.2.tgz", - "integrity": "sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==", - "dev": true, - "dependencies": { - "camelcase": "^5.3.1", - "map-obj": "^4.0.0", - "quick-lru": "^4.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/caniuse-lite": { - "version": "1.0.30001591", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001591.tgz", - "integrity": "sha512-PCzRMei/vXjJyL5mJtzNiUCKP59dm8Apqc3PH8gJkMnMXZGox93RbE76jHsmLwmIo6/3nsYIpJtx0O7u5PqFuQ==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ] - }, - "node_modules/case-sensitive-paths-webpack-plugin": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/case-sensitive-paths-webpack-plugin/-/case-sensitive-paths-webpack-plugin-2.4.0.tgz", - "integrity": "sha512-roIFONhcxog0JSSWbvVAh3OocukmSgpqOH6YpMkCvav/ySIV3JKg4Dc8vYtQjYi/UxpNE36r/9v+VqTQqgkYmw==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/caseless": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==", - "dev": true - }, - "node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/char-regex": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", - "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/chardet": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", - "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", - "dev": true - }, - "node_modules/charenc": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz", - "integrity": "sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==", - "engines": { - "node": "*" - } - }, - "node_modules/check-more-types": { - "version": "2.24.0", - "resolved": "https://registry.npmjs.org/check-more-types/-/check-more-types-2.24.0.tgz", - "integrity": "sha512-Pj779qHxV2tuapviy1bSZNEL1maXr13bPYpsvSDB68HlYcYuhlDrmGd63i0JHMCLKzc7rUSNIrpdJlhVlNwrxA==", - "dev": true, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/cheerio": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.12.tgz", - "integrity": "sha512-VqR8m68vM46BNnuZ5NtnGBKIE/DfN0cRIzg9n40EIq9NOv90ayxLBXA8fXC5gquFRGJSTRqBq25Jt2ECLR431Q==", - "dev": true, - "dependencies": { - "cheerio-select": "^2.1.0", - "dom-serializer": "^2.0.0", - "domhandler": "^5.0.3", - "domutils": "^3.0.1", - "htmlparser2": "^8.0.1", - "parse5": "^7.0.0", - "parse5-htmlparser2-tree-adapter": "^7.0.0" - }, - "engines": { - "node": ">= 6" - }, - "funding": { - "url": "https://github.com/cheeriojs/cheerio?sponsor=1" - } - }, - "node_modules/cheerio-select": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cheerio-select/-/cheerio-select-2.1.0.tgz", - "integrity": "sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==", - "dev": true, - "dependencies": { - "boolbase": "^1.0.0", - "css-select": "^5.1.0", - "css-what": "^6.1.0", - "domelementtype": "^2.3.0", - "domhandler": "^5.0.3", - "domutils": "^3.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/fb55" - } - }, - "node_modules/chokidar": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", - "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", - "dev": true, - "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, - "engines": { - "node": ">= 8.10.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, - "node_modules/chownr": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", - "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/chrome-trace-event": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", - "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==", - "dev": true, - "engines": { - "node": ">=6.0" - } - }, - "node_modules/ci-info": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", - "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/sibiraj-s" - } - ], - "engines": { - "node": ">=8" - } - }, - "node_modules/citty": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/citty/-/citty-0.1.6.tgz", - "integrity": "sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ==", - "dev": true, - "dependencies": { - "consola": "^3.2.3" - } - }, - "node_modules/cjs-module-lexer": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.3.tgz", - "integrity": "sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ==", - "dev": true - }, - "node_modules/clean-css": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.3.tgz", - "integrity": "sha512-D5J+kHaVb/wKSFcyyV75uCn8fiY4sV38XJoe4CUyGQ+mOU/fMVYUdH1hJC+CJQ5uY3EnW27SbJYS4X8BiLrAFg==", - "dev": true, - "dependencies": { - "source-map": "~0.6.0" - }, - "engines": { - "node": ">= 10.0" - } - }, - "node_modules/clean-css/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/clean-stack": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", - "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/cli-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", - "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", - "dev": true, - "dependencies": { - "restore-cursor": "^3.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cli-spinners": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.6.1.tgz", - "integrity": "sha512-x/5fWmGMnbKQAaNwN+UZlV79qBLM9JFnJuJ03gIi5whrob0xV0ofNVHy9DhwGdsMJQc2OKv0oGmLzvaqvAVv+g==", - "dev": true, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/cli-table3": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.3.tgz", - "integrity": "sha512-w5Jac5SykAeZJKntOxJCrm63Eg5/4dhMWIcuTbo9rpE+brgaSZo0RuNJZeOyMgsUdhDeojvgyQLmjI+K50ZGyg==", - "dev": true, - "dependencies": { - "string-width": "^4.2.0" - }, - "engines": { - "node": "10.* || >= 12.*" - }, - "optionalDependencies": { - "@colors/colors": "1.5.0" - } - }, - "node_modules/cli-truncate": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-2.1.0.tgz", - "integrity": "sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==", - "dev": true, - "dependencies": { - "slice-ansi": "^3.0.0", - "string-width": "^4.2.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/cli-width": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz", - "integrity": "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==", - "dev": true, - "engines": { - "node": ">= 12" - } - }, - "node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "dev": true, - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/cliui/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/cliui/node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/clone": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", - "integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==", - "engines": { - "node": ">=0.8" - } - }, - "node_modules/clone-deep": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", - "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", - "dev": true, - "dependencies": { - "is-plain-object": "^2.0.4", - "kind-of": "^6.0.2", - "shallow-clone": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/co": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", - "dev": true, - "engines": { - "iojs": ">= 1.0.0", - "node": ">= 0.12.0" - } - }, - "node_modules/code-block-writer": { - "version": "12.0.0", - "resolved": "https://registry.npmjs.org/code-block-writer/-/code-block-writer-12.0.0.tgz", - "integrity": "sha512-q4dMFMlXtKR3XNBHyMHt/3pwYNA69EDk00lloMOaaUMKPUXBw6lpXtbu3MMVG6/uOihGnRDOlkyqsONEUj60+w==", - "dev": true - }, - "node_modules/collect-v8-coverage": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz", - "integrity": "sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==", - "dev": true - }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "node_modules/color-support": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", - "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", - "dev": true, - "bin": { - "color-support": "bin.js" - } - }, - "node_modules/colorette": { - "version": "2.0.20", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", - "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", - "dev": true - }, - "node_modules/colors": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", - "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", - "dev": true, - "engines": { - "node": ">=0.1.90" - } - }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dev": true, - "dependencies": { - "delayed-stream": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/commander": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz", - "integrity": "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==", - "dev": true, - "engines": { - "node": ">=16" - } - }, - "node_modules/commitlint-plugin-function-rules": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/commitlint-plugin-function-rules/-/commitlint-plugin-function-rules-3.1.0.tgz", - "integrity": "sha512-K44912/g7ZZ0bEawrsJTn3giDEwDn6T18g/UcimUblv8hcSIoIxMX5uk6LY+uYO9cb/+3Suilbp7XoxP53Nk9g==", - "dev": true, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@commitlint/lint": ">=9.1.2 <19" - } - }, - "node_modules/common-path-prefix": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/common-path-prefix/-/common-path-prefix-3.0.0.tgz", - "integrity": "sha512-QE33hToZseCH3jS0qN96O/bSh3kaw/h+Tq7ngyY9eWDUnTlTNUyqfqvCXioLe5Na5jFsL78ra/wuBU4iuEgd4w==", - "dev": true - }, - "node_modules/common-tags": { - "version": "1.8.2", - "resolved": "https://registry.npmjs.org/common-tags/-/common-tags-1.8.2.tgz", - "integrity": "sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA==", - "dev": true, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/commondir": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", - "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", - "dev": true - }, - "node_modules/compare-func": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/compare-func/-/compare-func-2.0.0.tgz", - "integrity": "sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA==", - "dev": true, - "dependencies": { - "array-ify": "^1.0.0", - "dot-prop": "^5.1.0" - } - }, - "node_modules/complexion": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/complexion/-/complexion-0.2.2.tgz", - "integrity": "sha512-YAfDsYc27CsPQPzZU4i1OklMCyigN7VHPylWba0AWf5VmVr34NpLhHjJm0gumFh9m2aX8fYGCTTgne7oLfXXug==" - }, - "node_modules/complexion-js": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/complexion-js/-/complexion-js-0.2.2.tgz", - "integrity": "sha512-qxqJq0nEBwSEZtoqK8bHIR2VwzbPatjtlU+ZH9IPObitTQqa2xMEryoAlCsjOH+ATgIScHt0aiCUBabPG2Mmcg==", - "peerDependencies": { - "complexion": "^0.2.2" - } - }, - "node_modules/compressible": { - "version": "2.0.18", - "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", - "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", - "dev": true, - "dependencies": { - "mime-db": ">= 1.43.0 < 2" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/compression": { - "version": "1.7.4", - "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz", - "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==", - "dev": true, - "dependencies": { - "accepts": "~1.3.5", - "bytes": "3.0.0", - "compressible": "~2.0.16", - "debug": "2.6.9", - "on-headers": "~1.0.2", - "safe-buffer": "5.1.2", - "vary": "~1.1.2" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/compression/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/compression/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" - }, - "node_modules/concat-stream": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", - "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", - "dev": true, - "engines": [ - "node >= 0.8" - ], - "dependencies": { - "buffer-from": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^2.2.2", - "typedarray": "^0.0.6" - } - }, - "node_modules/concat-stream/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "dev": true - }, - "node_modules/concat-stream/node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "dev": true, - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/concat-stream/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, - "node_modules/confusing-browser-globals": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/confusing-browser-globals/-/confusing-browser-globals-1.0.11.tgz", - "integrity": "sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA==", - "dev": true, - "peer": true - }, - "node_modules/connect": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/connect/-/connect-3.7.0.tgz", - "integrity": "sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ==", - "dev": true, - "dependencies": { - "debug": "2.6.9", - "finalhandler": "1.1.2", - "parseurl": "~1.3.3", - "utils-merge": "1.0.1" - }, - "engines": { - "node": ">= 0.10.0" - } - }, - "node_modules/connect-history-api-fallback": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz", - "integrity": "sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA==", - "dev": true, - "engines": { - "node": ">=0.8" - } - }, - "node_modules/connect-pause": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/connect-pause/-/connect-pause-0.1.1.tgz", - "integrity": "sha512-a1gSWQBQD73krFXdUEYJom2RTFrWUL3YvXDCRkyv//GVXc79cdW9MngtRuN9ih4FDKBtfJAJId+BbDuX+1rh2w==", - "dev": true, - "engines": { - "node": "*" - } - }, - "node_modules/connect/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/connect/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true - }, - "node_modules/consola": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/consola/-/consola-3.2.3.tgz", - "integrity": "sha512-I5qxpzLv+sJhTVEoLYNcTW+bThDCPsit0vLNKShZx6rLtpilNpmmeTPaeqJb9ZE9dV3DGaeby6Vuhrw38WjeyQ==", - "dev": true, - "engines": { - "node": "^14.18.0 || >=16.10.0" - } - }, - "node_modules/constants-browserify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", - "integrity": "sha512-xFxOwqIzR/e1k1gLiWEophSCMqXcwVHIH7akf7b/vxcUeGunlj3hvZaaqxwHsTgn+IndtkQJgSztIDWeumWJDQ==", - "dev": true - }, - "node_modules/content-disposition": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", - "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", - "dev": true, - "dependencies": { - "safe-buffer": "5.2.1" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/content-disposition/node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/content-type": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", - "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", - "dev": true, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/conventional-changelog-angular": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-7.0.0.tgz", - "integrity": "sha512-ROjNchA9LgfNMTTFSIWPzebCwOGFdgkEq45EnvvrmSLvCtAw0HSmrCs7/ty+wAeYUZyNay0YMUNYFTRL72PkBQ==", - "dev": true, - "dependencies": { - "compare-func": "^2.0.0" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/conventional-changelog-conventionalcommits": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/conventional-changelog-conventionalcommits/-/conventional-changelog-conventionalcommits-7.0.2.tgz", - "integrity": "sha512-NKXYmMR/Hr1DevQegFB4MwfM5Vv0m4UIxKZTTYuD98lpTknaZlSRrDOG4X7wIXpGkfsYxZTghUN+Qq+T0YQI7w==", - "dev": true, - "dependencies": { - "compare-func": "^2.0.0" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/conventional-commits-parser": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-5.0.0.tgz", - "integrity": "sha512-ZPMl0ZJbw74iS9LuX9YIAiW8pfM5p3yh2o/NbXHbkFuZzY5jvdi5jFycEOkmBW5H5I7nA+D6f3UcsCLP2vvSEA==", - "dev": true, - "dependencies": { - "is-text-path": "^2.0.0", - "JSONStream": "^1.3.5", - "meow": "^12.0.1", - "split2": "^4.0.0" - }, - "bin": { - "conventional-commits-parser": "cli.mjs" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/convert-source-map": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", - "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", - "dev": true - }, - "node_modules/cookie": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", - "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", - "dev": true, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/cookie-signature": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", - "dev": true - }, - "node_modules/copy-anything": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-2.0.6.tgz", - "integrity": "sha512-1j20GZTsvKNkc4BY3NpMOM8tt///wY3FpIzozTOFO2ffuZcV61nojHXVKIy3WM+7ADCy5FVhdZYHYDdgTU0yJw==", - "dev": true, - "dependencies": { - "is-what": "^3.14.1" - }, - "funding": { - "url": "https://github.com/sponsors/mesqueeb" - } - }, - "node_modules/copy-webpack-plugin": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-11.0.0.tgz", - "integrity": "sha512-fX2MWpamkW0hZxMEg0+mYnA40LTosOSa5TqZ9GYIBzyJa9C3QUaMPSE2xAi/buNr8u89SfD9wHSQVBzrRa/SOQ==", - "dev": true, - "dependencies": { - "fast-glob": "^3.2.11", - "glob-parent": "^6.0.1", - "globby": "^13.1.1", - "normalize-path": "^3.0.0", - "schema-utils": "^4.0.0", - "serialize-javascript": "^6.0.0" - }, - "engines": { - "node": ">= 14.15.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^5.1.0" - } - }, - "node_modules/copy-webpack-plugin/node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dev": true, - "dependencies": { - "is-glob": "^4.0.3" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/copy-webpack-plugin/node_modules/globby": { - "version": "13.2.2", - "resolved": "https://registry.npmjs.org/globby/-/globby-13.2.2.tgz", - "integrity": "sha512-Y1zNGV+pzQdh7H39l9zgB4PJqjRNqydvdYCDG4HFXM4XuvSaQQlEc91IU1yALL8gUTDomgBAfz3XJdmUS+oo0w==", - "dev": true, - "dependencies": { - "dir-glob": "^3.0.1", - "fast-glob": "^3.3.0", - "ignore": "^5.2.4", - "merge2": "^1.4.1", - "slash": "^4.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/copy-webpack-plugin/node_modules/slash": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", - "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/core-js-compat": { - "version": "3.36.0", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.36.0.tgz", - "integrity": "sha512-iV9Pd/PsgjNWBXeq8XRtWVSgz2tKAfhfvBs7qxYty+RlRd+OCksaWmOnc4JKrTc1cToXL1N0s3l/vwlxPtdElw==", - "dev": true, - "dependencies": { - "browserslist": "^4.22.3" - }, - "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 - }, - "node_modules/cors": { - "version": "2.8.5", - "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", - "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", - "dev": true, - "dependencies": { - "object-assign": "^4", - "vary": "^1" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/cosmiconfig": { - "version": "8.3.6", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.3.6.tgz", - "integrity": "sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==", - "dev": true, - "dependencies": { - "import-fresh": "^3.3.0", - "js-yaml": "^4.1.0", - "parse-json": "^5.2.0", - "path-type": "^4.0.0" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/d-fischer" - }, - "peerDependencies": { - "typescript": ">=4.9.5" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/cosmiconfig-typescript-loader": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/cosmiconfig-typescript-loader/-/cosmiconfig-typescript-loader-5.0.0.tgz", - "integrity": "sha512-+8cK7jRAReYkMwMiG+bxhcNKiHJDM6bR9FD/nGBXOWdMLuYawjF5cGrtLilJ+LGd3ZjCXnJjR5DkfWPoIVlqJA==", - "dev": true, - "dependencies": { - "jiti": "^1.19.1" - }, - "engines": { - "node": ">=v16" - }, - "peerDependencies": { - "@types/node": "*", - "cosmiconfig": ">=8.2", - "typescript": ">=4" - } - }, - "node_modules/cosmiconfig/node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - }, - "node_modules/cosmiconfig/node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/create-jest": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz", - "integrity": "sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==", - "dev": true, - "dependencies": { - "@jest/types": "^29.6.3", - "chalk": "^4.0.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.9", - "jest-config": "^29.7.0", - "jest-util": "^29.7.0", - "prompts": "^2.0.1" - }, - "bin": { - "create-jest": "bin/create-jest.js" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/create-jest/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/create-jest/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, - "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/create-jest/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/create-jest/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/create-require": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", - "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", - "dev": true - }, - "node_modules/critters": { - "version": "0.0.20", - "resolved": "https://registry.npmjs.org/critters/-/critters-0.0.20.tgz", - "integrity": "sha512-CImNRorKOl5d8TWcnAz5n5izQ6HFsvz29k327/ELy6UFcmbiZNOsinaKvzv16WZR0P6etfSWYzE47C4/56B3Uw==", - "dev": true, - "dependencies": { - "chalk": "^4.1.0", - "css-select": "^5.1.0", - "dom-serializer": "^2.0.0", - "domhandler": "^5.0.2", - "htmlparser2": "^8.0.2", - "postcss": "^8.4.23", - "pretty-bytes": "^5.3.0" - } - }, - "node_modules/critters/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/critters/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, - "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/critters/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/critters/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/crypt": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz", - "integrity": "sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==", - "engines": { - "node": "*" - } - }, - "node_modules/crypto-js": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz", - "integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==", - "dev": true - }, - "node_modules/crypto-random-string": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", - "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/css-loader": { - "version": "6.10.0", - "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.10.0.tgz", - "integrity": "sha512-LTSA/jWbwdMlk+rhmElbDR2vbtQoTBPr7fkJE+mxrHj+7ru0hUmHafDRzWIjIHTwpitWVaqY2/UWGRca3yUgRw==", - "dev": true, - "dependencies": { - "icss-utils": "^5.1.0", - "postcss": "^8.4.33", - "postcss-modules-extract-imports": "^3.0.0", - "postcss-modules-local-by-default": "^4.0.4", - "postcss-modules-scope": "^3.1.1", - "postcss-modules-values": "^4.0.0", - "postcss-value-parser": "^4.2.0", - "semver": "^7.5.4" - }, - "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "@rspack/core": "0.x || 1.x", - "webpack": "^5.0.0" - }, - "peerDependenciesMeta": { - "@rspack/core": { - "optional": true - }, - "webpack": { - "optional": true - } - } - }, - "node_modules/css-select": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz", - "integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==", - "dev": true, - "dependencies": { - "boolbase": "^1.0.0", - "css-what": "^6.1.0", - "domhandler": "^5.0.2", - "domutils": "^3.0.1", - "nth-check": "^2.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/fb55" - } - }, - "node_modules/css-what": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", - "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", - "dev": true, - "engines": { - "node": ">= 6" - }, - "funding": { - "url": "https://github.com/sponsors/fb55" - } - }, - "node_modules/cssesc": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", - "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", - "dev": true, - "bin": { - "cssesc": "bin/cssesc" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/cssom": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.5.0.tgz", - "integrity": "sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw==", - "dev": true - }, - "node_modules/cssstyle": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz", - "integrity": "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==", - "dev": true, - "dependencies": { - "cssom": "~0.3.6" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cssstyle/node_modules/cssom": { - "version": "0.3.8", - "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", - "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==", - "dev": true - }, - "node_modules/csstype": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", - "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", - "dev": true - }, - "node_modules/cypress": { - "version": "13.6.6", - "resolved": "https://registry.npmjs.org/cypress/-/cypress-13.6.6.tgz", - "integrity": "sha512-S+2S9S94611hXimH9a3EAYt81QM913ZVA03pUmGDfLTFa5gyp85NJ8dJGSlEAEmyRsYkioS1TtnWtbv/Fzt11A==", - "dev": true, - "hasInstallScript": true, - "dependencies": { - "@cypress/request": "^3.0.0", - "@cypress/xvfb": "^1.2.4", - "@types/sinonjs__fake-timers": "8.1.1", - "@types/sizzle": "^2.3.2", - "arch": "^2.2.0", - "blob-util": "^2.0.2", - "bluebird": "^3.7.2", - "buffer": "^5.7.1", - "cachedir": "^2.3.0", - "chalk": "^4.1.0", - "check-more-types": "^2.24.0", - "cli-cursor": "^3.1.0", - "cli-table3": "~0.6.1", - "commander": "^6.2.1", - "common-tags": "^1.8.0", - "dayjs": "^1.10.4", - "debug": "^4.3.4", - "enquirer": "^2.3.6", - "eventemitter2": "6.4.7", - "execa": "4.1.0", - "executable": "^4.1.1", - "extract-zip": "2.0.1", - "figures": "^3.2.0", - "fs-extra": "^9.1.0", - "getos": "^3.2.1", - "is-ci": "^3.0.1", - "is-installed-globally": "~0.4.0", - "lazy-ass": "^1.6.0", - "listr2": "^3.8.3", - "lodash": "^4.17.21", - "log-symbols": "^4.0.0", - "minimist": "^1.2.8", - "ospath": "^1.2.2", - "pretty-bytes": "^5.6.0", - "process": "^0.11.10", - "proxy-from-env": "1.0.0", - "request-progress": "^3.0.0", - "semver": "^7.5.3", - "supports-color": "^8.1.1", - "tmp": "~0.2.1", - "untildify": "^4.0.0", - "yauzl": "^2.10.0" - }, - "bin": { - "cypress": "bin/cypress" - }, - "engines": { - "node": "^16.0.0 || ^18.0.0 || >=20.0.0" - } - }, - "node_modules/cypress-multi-reporters": { - "version": "1.6.4", - "resolved": "https://registry.npmjs.org/cypress-multi-reporters/-/cypress-multi-reporters-1.6.4.tgz", - "integrity": "sha512-3xU2t6pZjZy/ORHaCvci5OT1DAboS4UuMMM8NBAizeb2C9qmHt+cgAjXgurazkwkPRdO7ccK39M5ZaPCju0r6A==", - "dev": true, - "peer": true, - "dependencies": { - "debug": "^4.3.4", - "lodash": "^4.17.21" - }, - "engines": { - "node": ">=6.0.0" - }, - "peerDependencies": { - "mocha": ">=3.1.2" - } - }, - "node_modules/cypress-parallel": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/cypress-parallel/-/cypress-parallel-0.14.0.tgz", - "integrity": "sha512-Lsh28G70vxjL0cjR820BdaVQHnGc17Vvb+tYmjbRPmfC+XEzwvUzhcaD0E1zCztBSYhw+b1/1JLmW4Y0qE/EDA==", - "dev": true, - "dependencies": { - "@colors/colors": "^1.5.0", - "cli-table3": "^0.6.0", - "cross-spawn": "^7.0.3", - "fs-extra": "^10.0.0", - "glob-escape": "^0.0.2", - "is-npm": "^5.0.0", - "lodash.camelcase": "^4.3.0", - "mocha": "~9.2.0", - "yargs": "15.3.1" - }, - "bin": { - "cypress-parallel": "cli.js" - }, - "peerDependencies": { - "cypress-multi-reporters": "^1.5.0" - } - }, - "node_modules/cypress-parallel/node_modules/ansi-colors": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", - "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/cypress-parallel/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/cypress-parallel/node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - }, - "node_modules/cypress-parallel/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/cypress-parallel/node_modules/chokidar": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", - "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - ], - "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, - "engines": { - "node": ">= 8.10.0" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, - "node_modules/cypress-parallel/node_modules/cliui": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", - "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", - "dev": true, - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^6.2.0" - } - }, - "node_modules/cypress-parallel/node_modules/debug": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", - "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", - "dev": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/cypress-parallel/node_modules/decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/cypress-parallel/node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/cypress-parallel/node_modules/fs-extra": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", - "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", - "dev": true, - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/cypress-parallel/node_modules/glob": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", - "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", - "dev": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/cypress-parallel/node_modules/glob/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/cypress-parallel/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/cypress-parallel/node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/cypress-parallel/node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cypress-parallel/node_modules/minimatch": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-4.2.1.tgz", - "integrity": "sha512-9Uq1ChtSZO+Mxa/CL1eGizn2vRn3MlLgzhT0Iz8zaY8NdvxvB0d5QdPFmCKf7JKA9Lerx5vRrnwO03jsSfGG9g==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/cypress-parallel/node_modules/mocha": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.2.2.tgz", - "integrity": "sha512-L6XC3EdwT6YrIk0yXpavvLkn8h+EU+Y5UcCHKECyMbdUIxyMuZj4bX4U9e1nvnvUUvQVsV2VHQr5zLdcUkhW/g==", - "dev": true, - "dependencies": { - "@ungap/promise-all-settled": "1.1.2", - "ansi-colors": "4.1.1", - "browser-stdout": "1.3.1", - "chokidar": "3.5.3", - "debug": "4.3.3", - "diff": "5.0.0", - "escape-string-regexp": "4.0.0", - "find-up": "5.0.0", - "glob": "7.2.0", - "growl": "1.10.5", - "he": "1.2.0", - "js-yaml": "4.1.0", - "log-symbols": "4.1.0", - "minimatch": "4.2.1", - "ms": "2.1.3", - "nanoid": "3.3.1", - "serialize-javascript": "6.0.0", - "strip-json-comments": "3.1.1", - "supports-color": "8.1.1", - "which": "2.0.2", - "workerpool": "6.2.0", - "yargs": "16.2.0", - "yargs-parser": "20.2.4", - "yargs-unparser": "2.0.0" - }, - "bin": { - "_mocha": "bin/_mocha", - "mocha": "bin/mocha" - }, - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mochajs" - } - }, - "node_modules/cypress-parallel/node_modules/mocha/node_modules/cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", - "dev": true, - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" - } - }, - "node_modules/cypress-parallel/node_modules/mocha/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true - }, - "node_modules/cypress-parallel/node_modules/mocha/node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/cypress-parallel/node_modules/mocha/node_modules/yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", - "dev": true, - "dependencies": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/cypress-parallel/node_modules/nanoid": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.1.tgz", - "integrity": "sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw==", - "dev": true, - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, - "node_modules/cypress-parallel/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/cypress-parallel/node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cypress-parallel/node_modules/serialize-javascript": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", - "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", - "dev": true, - "dependencies": { - "randombytes": "^2.1.0" - } - }, - "node_modules/cypress-parallel/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "node_modules/cypress-parallel/node_modules/workerpool": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.0.tgz", - "integrity": "sha512-Rsk5qQHJ9eowMH28Jwhe8HEbmdYDX4lwoMWshiCXugjtHqMD9ZbiqSDLxcsfdqsETPzVUtX5s1Z5kStiIM6l4A==", - "dev": true - }, - "node_modules/cypress-parallel/node_modules/yargs": { - "version": "15.3.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.3.1.tgz", - "integrity": "sha512-92O1HWEjw27sBfgmXiixJWT5hRBp2eobqXicLtPBIDBhYB+1HpwZlXmbW2luivBJHBzki+7VyCLRtAkScbTBQA==", - "dev": true, - "dependencies": { - "cliui": "^6.0.0", - "decamelize": "^1.2.0", - "find-up": "^4.1.0", - "get-caller-file": "^2.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^4.2.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^18.1.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cypress-parallel/node_modules/yargs/node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cypress-parallel/node_modules/yargs/node_modules/y18n": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", - "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", - "dev": true - }, - "node_modules/cypress-parallel/node_modules/yargs/node_modules/yargs-parser": { - "version": "18.1.3", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", - "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", - "dev": true, - "dependencies": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/cypress/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/cypress/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, - "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/cypress/node_modules/chalk/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cypress/node_modules/commander": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz", - "integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==", - "dev": true, - "engines": { - "node": ">= 6" - } - }, - "node_modules/cypress/node_modules/execa": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-4.1.0.tgz", - "integrity": "sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==", - "dev": true, - "dependencies": { - "cross-spawn": "^7.0.0", - "get-stream": "^5.0.0", - "human-signals": "^1.1.1", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.0", - "onetime": "^5.1.0", - "signal-exit": "^3.0.2", - "strip-final-newline": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" - } - }, - "node_modules/cypress/node_modules/fs-extra": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", - "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", - "dev": true, - "dependencies": { - "at-least-node": "^1.0.0", - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/cypress/node_modules/get-stream": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", - "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", - "dev": true, - "dependencies": { - "pump": "^3.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/cypress/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/cypress/node_modules/human-signals": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz", - "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==", - "dev": true, - "engines": { - "node": ">=8.12.0" - } - }, - "node_modules/cypress/node_modules/proxy-from-env": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.0.0.tgz", - "integrity": "sha512-F2JHgJQ1iqwnHDcQjVBsq3n/uoaFL+iPW/eAeL7kVxy/2RrWaN4WroKjjvbsoRtv0ftelNyC01bjRhn/bhcf4A==", - "dev": true - }, - "node_modules/cypress/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "node_modules/d": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", - "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==", - "dev": true, - "dependencies": { - "es5-ext": "^0.10.50", - "type": "^1.0.1" - } - }, - "node_modules/dag-map": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/dag-map/-/dag-map-1.0.2.tgz", - "integrity": "sha512-+LSAiGFwQ9dRnRdOeaj7g47ZFJcOUPukAP8J3A3fuZ1g9Y44BG+P1sgApjLXTQPOzC4+7S9Wr8kXsfpINM4jpw==" - }, - "node_modules/damerau-levenshtein": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", - "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==", - "dev": true, - "peer": true - }, - "node_modules/dargs": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/dargs/-/dargs-7.0.0.tgz", - "integrity": "sha512-2iy1EkLdlBzQGvbweYRFxmFath8+K7+AKB0TlhHWkNuH+TmovaMH/Wp7V7R4u7f4SnX3OgLsU9t1NI9ioDnUpg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/dash-ast": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/dash-ast/-/dash-ast-2.0.1.tgz", - "integrity": "sha512-5TXltWJGc+RdnabUGzhRae1TRq6m4gr+3K2wQX0is5/F2yS6MJXJvLyI3ErAnsAXuJoGqvfVD5icRgim07DrxQ==", - "dev": true - }, - "node_modules/dashdash": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", - "integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==", - "dev": true, - "dependencies": { - "assert-plus": "^1.0.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/data-urls": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-3.0.2.tgz", - "integrity": "sha512-Jy/tj3ldjZJo63sVAvg6LHt2mHvl4V6AgRAmNDtLdm7faqtsx+aJG42rsyCo9JCoRVKwPFzKlIPx3DIibwSIaQ==", - "dev": true, - "dependencies": { - "abab": "^2.0.6", - "whatwg-mimetype": "^3.0.0", - "whatwg-url": "^11.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/dayjs": { - "version": "1.11.10", - "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.10.tgz", - "integrity": "sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ==", - "dev": true - }, - "node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/decache": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/decache/-/decache-4.6.2.tgz", - "integrity": "sha512-2LPqkLeu8XWHU8qNCS3kcF6sCcb5zIzvWaAHYSvPfwhdd7mHuah29NssMzrTYyHN4F5oFy2ko9OBYxegtU0FEw==", - "dev": true, - "dependencies": { - "callsite": "^1.0.0" - } - }, - "node_modules/decamelize": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", - "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/decamelize-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/decamelize-keys/-/decamelize-keys-1.1.1.tgz", - "integrity": "sha512-WiPxgEirIV0/eIOMcnFBA3/IJZAZqKnwAwWyvvdi4lsr1WCN22nhdf/3db3DoZcUjTV2SqfzIwNyp6y2xs3nmg==", - "dev": true, - "dependencies": { - "decamelize": "^1.1.0", - "map-obj": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/decamelize-keys/node_modules/decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/decamelize-keys/node_modules/map-obj": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", - "integrity": "sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/decimal.js": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.3.tgz", - "integrity": "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==", - "dev": true - }, - "node_modules/dedent": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.5.1.tgz", - "integrity": "sha512-+LxW+KLWxu3HW3M2w2ympwtqPrqYRzU8fqi6Fhd18fBALe15blJPI/I4+UHveMVG6lJqB4JNd4UG0S5cnVHwIg==", - "dev": true, - "peerDependencies": { - "babel-plugin-macros": "^3.1.0" - }, - "peerDependenciesMeta": { - "babel-plugin-macros": { - "optional": true - } - } - }, - "node_modules/deep-equal": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.2.tgz", - "integrity": "sha512-5tdhKF6DbU7iIzrIOa1AOUt39ZRm13cmL1cGEh//aqR8x9+tNfbywRf0n5FD/18OKMdo7DNEtrX2t22ZAkI+eg==", - "dev": true, - "dependencies": { - "is-arguments": "^1.1.1", - "is-date-object": "^1.0.5", - "is-regex": "^1.1.4", - "object-is": "^1.1.5", - "object-keys": "^1.1.1", - "regexp.prototype.flags": "^1.5.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==" - }, - "node_modules/deep-object-diff": { - "version": "1.1.9", - "resolved": "https://registry.npmjs.org/deep-object-diff/-/deep-object-diff-1.1.9.tgz", - "integrity": "sha512-Rn+RuwkmkDwCi2/oXOFS9Gsr5lJZu/yTGpK7wAaAIE75CC+LCGEZHpY6VQJa/RoJcrmaA/docWJZvYohlNkWPA==" - }, - "node_modules/deepmerge": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", - "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/default-browser-id": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-3.0.0.tgz", - "integrity": "sha512-OZ1y3y0SqSICtE8DE4S8YOE9UZOJ8wO16fKWVP5J1Qz42kV9jcnMVFrEE/noXb/ss3Q4pZIH79kxofzyNNtUNA==", - "dev": true, - "dependencies": { - "bplist-parser": "^0.2.0", - "untildify": "^4.0.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/default-gateway": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-6.0.3.tgz", - "integrity": "sha512-fwSOJsbbNzZ/CUFpqFBqYfYNLj1NbMPm8MMCIzHjC83iSJRBEGmDUxU+WP661BaBQImeC2yHwXtz+P/O9o+XEg==", - "dev": true, - "dependencies": { - "execa": "^5.0.0" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/defaults": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", - "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", - "dev": true, - "dependencies": { - "clone": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/defaults/node_modules/clone": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", - "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", - "dev": true, - "engines": { - "node": ">=0.8" - } - }, - "node_modules/define-data-property": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", - "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", - "dependencies": { - "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "gopd": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/define-lazy-prop": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", - "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/define-properties": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", - "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", - "dependencies": { - "define-data-property": "^1.0.1", - "has-property-descriptors": "^1.0.0", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/defu": { - "version": "6.1.4", - "resolved": "https://registry.npmjs.org/defu/-/defu-6.1.4.tgz", - "integrity": "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==", - "dev": true - }, - "node_modules/del": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/del/-/del-6.1.1.tgz", - "integrity": "sha512-ua8BhapfP0JUJKC/zV9yHHDW/rDoDxP4Zhn3AkA6/xT6gY7jYXJiaeyBZznYVujhZZET+UgcbZiQ7sN3WqcImg==", - "dev": true, - "dependencies": { - "globby": "^11.0.1", - "graceful-fs": "^4.2.4", - "is-glob": "^4.0.1", - "is-path-cwd": "^2.2.0", - "is-path-inside": "^3.0.2", - "p-map": "^4.0.0", - "rimraf": "^3.0.2", - "slash": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "dev": true, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", - "dev": true, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/dequal": { + "node_modules/@types/stack-utils": { "version": "2.0.3", - "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", - "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/destroy": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", - "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", - "dev": true, - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } - }, - "node_modules/detect-indent": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-6.1.0.tgz", - "integrity": "sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/detect-newline": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", - "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/detect-node": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", - "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", + "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", "dev": true }, - "node_modules/detect-node-es": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz", - "integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==", + "node_modules/@types/tough-cookie": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.5.tgz", + "integrity": "sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==", "dev": true }, - "node_modules/detect-package-manager": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/detect-package-manager/-/detect-package-manager-2.0.1.tgz", - "integrity": "sha512-j/lJHyoLlWi6G1LDdLgvUtz60Zo5GEj+sVYtTVXnYLDPuzgC3llMxonXym9zIwhhUII8vjdw0LXxavpLqTbl1A==", - "dev": true, - "dependencies": { - "execa": "^5.1.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/detect-port": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/detect-port/-/detect-port-1.5.1.tgz", - "integrity": "sha512-aBzdj76lueB6uUst5iAs7+0H/oOjqI5D16XUWxlWMIMROhcM0rfsNVk93zTngq1dDNpoXRr++Sus7ETAExppAQ==", - "dev": true, - "dependencies": { - "address": "^1.0.1", - "debug": "4" - }, - "bin": { - "detect": "bin/detect-port.js", - "detect-port": "bin/detect-port.js" - } - }, - "node_modules/dfa": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/dfa/-/dfa-1.2.0.tgz", - "integrity": "sha512-ED3jP8saaweFTjeGX8HQPjeC1YYyZs98jGNZx6IiBvxW7JG5v492kamAQB3m2wop07CvU/RQmzcKr6bgcC5D/Q==", + "node_modules/@types/uuid": { + "version": "9.0.8", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.8.tgz", + "integrity": "sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==", "dev": true }, - "node_modules/diff": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", - "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", - "dev": true, - "engines": { - "node": ">=0.3.1" - } - }, - "node_modules/diff-sequences": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", - "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", - "dev": true, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", - "dev": true, - "dependencies": { - "path-type": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/dns-packet": { - "version": "5.6.1", - "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-5.6.1.tgz", - "integrity": "sha512-l4gcSouhcgIKRvyy99RNVOgxXiicE+2jZoNmaNmZ6JXiGajBOJAesk1OBlJuM5k2c+eudGdLxDqXuPCKIj6kpw==", - "dev": true, - "dependencies": { - "@leichtgewicht/ip-codec": "^2.0.1" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/doctrine": { + "node_modules/@types/wrap-ansi": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/dom-accessibility-api": { - "version": "0.5.16", - "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz", - "integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==", + "resolved": "https://registry.npmjs.org/@types/wrap-ansi/-/wrap-ansi-3.0.0.tgz", + "integrity": "sha512-ltIpx+kM7g/MLRZfkbL7EsCEjfzCcScLpkg37eXEtx5kmrAKBkTJwd1GIAjDSL8wTpM6Hzn5YO4pSb91BEwu1g==", "dev": true }, - "node_modules/dom-converter": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.2.0.tgz", - "integrity": "sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA==", + "node_modules/@types/ws": { + "version": "8.5.12", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.12.tgz", + "integrity": "sha512-3tPRkv1EtkDpzlgyKyI8pGsGZAGPEaXeu0DOj5DI25Ja91bdAYddYHbADRYVrZMRbfW+1l5YwXVDKohDJNQxkQ==", "dev": true, + "peer": true, "dependencies": { - "utila": "~0.4" + "@types/node": "*" } }, - "node_modules/dom-serializer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", - "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "node_modules/@types/yargs": { + "version": "17.0.32", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.32.tgz", + "integrity": "sha512-xQ67Yc/laOG5uMfX/093MRlGGCIBzZMarVa+gfNKJxWAIgykYpVGkBdbqEzGDDfCrVUj6Hiff4mTZ5BA6TmAog==", "dev": true, "dependencies": { - "domelementtype": "^2.3.0", - "domhandler": "^5.0.2", - "entities": "^4.2.0" - }, - "funding": { - "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + "@types/yargs-parser": "*" } }, - "node_modules/domelementtype": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", - "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ] - }, - "node_modules/domexception": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/domexception/-/domexception-4.0.0.tgz", - "integrity": "sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw==", - "deprecated": "Use your platform's native DOMException instead", - "dev": true, - "dependencies": { - "webidl-conversions": "^7.0.0" - }, - "engines": { - "node": ">=12" - } + "node_modules/@types/yargs-parser": { + "version": "21.0.3", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", + "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", + "dev": true }, - "node_modules/domhandler": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", - "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "node_modules/@vitejs/plugin-basic-ssl": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-basic-ssl/-/plugin-basic-ssl-1.1.0.tgz", + "integrity": "sha512-wO4Dk/rm8u7RNhOf95ZzcEmC9rYOncYgvq4z3duaJrCgjN8BxAnDVyndanfcJZ0O6XZzHz6Q0hTimxTg8Y9g/A==", "dev": true, - "dependencies": { - "domelementtype": "^2.3.0" - }, "engines": { - "node": ">= 4" - }, - "funding": { - "url": "https://github.com/fb55/domhandler?sponsor=1" - } - }, - "node_modules/domutils": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz", - "integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==", - "dev": true, - "dependencies": { - "dom-serializer": "^2.0.0", - "domelementtype": "^2.3.0", - "domhandler": "^5.0.3" + "node": ">=14.6.0" }, - "funding": { - "url": "https://github.com/fb55/domutils?sponsor=1" + "peerDependencies": { + "vite": "^3.0.0 || ^4.0.0 || ^5.0.0" } }, - "node_modules/dot": { - "version": "2.0.0-beta.1", - "resolved": "https://registry.npmjs.org/dot/-/dot-2.0.0-beta.1.tgz", - "integrity": "sha512-kxM7fSnNQTXOmaeGuBSXM8O3fEsBb7XSDBllkGbRwa0lJSJTxxDE/4eSNGLKZUmlFw0f1vJ5qSV2BljrgQtgIA==", - "dev": true - }, - "node_modules/dot-case": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", - "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==", + "node_modules/@webassemblyjs/ast": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.12.1.tgz", + "integrity": "sha512-EKfMUOPRRUTy5UII4qJDGPpqfwjOmZ5jeGFwid9mnoqIFK+e0vqoi1qH56JpmZSzEL53jKnNzScdmftJyG5xWg==", "dev": true, + "peer": true, "dependencies": { - "no-case": "^3.0.4", - "tslib": "^2.0.3" + "@webassemblyjs/helper-numbers": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6" } }, - "node_modules/dot-prop": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", - "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==", + "node_modules/@webassemblyjs/floating-point-hex-parser": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.6.tgz", + "integrity": "sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw==", "dev": true, - "dependencies": { - "is-obj": "^2.0.0" - }, - "engines": { - "node": ">=8" - } + "peer": true }, - "node_modules/dotenv": { - "version": "16.4.5", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz", - "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==", + "node_modules/@webassemblyjs/helper-api-error": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.6.tgz", + "integrity": "sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q==", "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://dotenvx.com" - } + "peer": true }, - "node_modules/dotenv-expand": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-10.0.0.tgz", - "integrity": "sha512-GopVGCpVS1UKH75VKHGuQFqS1Gusej0z4FyQkPdwjil2gNIv+LNsqBlboOzpJFZKVT95GkCyWJbBSdFEFUWI2A==", + "node_modules/@webassemblyjs/helper-buffer": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.12.1.tgz", + "integrity": "sha512-nzJwQw99DNDKr9BVCOZcLuJJUlqkJh+kVzVl6Fmq/tI5ZtEyWT1KZMyOXltXLZJmDtvLCDgwsyrkohEtopTXCw==", "dev": true, - "engines": { - "node": ">=12" - } - }, - "node_modules/duplexer": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", - "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==", - "dev": true - }, - "node_modules/duplexer2": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", - "integrity": "sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA==", + "peer": true + }, + "node_modules/@webassemblyjs/helper-numbers": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.6.tgz", + "integrity": "sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g==", "dev": true, + "peer": true, "dependencies": { - "readable-stream": "^2.0.2" + "@webassemblyjs/floating-point-hex-parser": "1.11.6", + "@webassemblyjs/helper-api-error": "1.11.6", + "@xtuc/long": "4.2.2" } }, - "node_modules/duplexer2/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "dev": true + "node_modules/@webassemblyjs/helper-wasm-bytecode": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.6.tgz", + "integrity": "sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA==", + "dev": true, + "peer": true }, - "node_modules/duplexer2/node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "node_modules/@webassemblyjs/helper-wasm-section": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.12.1.tgz", + "integrity": "sha512-Jif4vfB6FJlUlSbgEMHUyk1j234GTNG9dBJ4XJdOySoj518Xj0oGsNi59cUQF4RRMS9ouBUxDDdyBVfPTypa5g==", "dev": true, + "peer": true, "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" + "@webassemblyjs/ast": "1.12.1", + "@webassemblyjs/helper-buffer": "1.12.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/wasm-gen": "1.12.1" } }, - "node_modules/duplexer2/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "node_modules/@webassemblyjs/ieee754": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.6.tgz", + "integrity": "sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg==", "dev": true, + "peer": true, "dependencies": { - "safe-buffer": "~5.1.0" + "@xtuc/ieee754": "^1.2.0" } }, - "node_modules/duplexify": { - "version": "3.7.1", - "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz", - "integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==", + "node_modules/@webassemblyjs/leb128": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.6.tgz", + "integrity": "sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ==", "dev": true, + "peer": true, "dependencies": { - "end-of-stream": "^1.0.0", - "inherits": "^2.0.1", - "readable-stream": "^2.0.0", - "stream-shift": "^1.0.0" + "@xtuc/long": "4.2.2" } }, - "node_modules/duplexify/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "dev": true + "node_modules/@webassemblyjs/utf8": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.6.tgz", + "integrity": "sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA==", + "dev": true, + "peer": true }, - "node_modules/duplexify/node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "node_modules/@webassemblyjs/wasm-edit": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.12.1.tgz", + "integrity": "sha512-1DuwbVvADvS5mGnXbE+c9NfA8QRcZ6iKquqjjmR10k6o+zzsRVesil54DKexiowcFCPdr/Q0qaMgB01+SQ1u6g==", "dev": true, + "peer": true, "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" + "@webassemblyjs/ast": "1.12.1", + "@webassemblyjs/helper-buffer": "1.12.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/helper-wasm-section": "1.12.1", + "@webassemblyjs/wasm-gen": "1.12.1", + "@webassemblyjs/wasm-opt": "1.12.1", + "@webassemblyjs/wasm-parser": "1.12.1", + "@webassemblyjs/wast-printer": "1.12.1" } }, - "node_modules/duplexify/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "node_modules/@webassemblyjs/wasm-gen": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.12.1.tgz", + "integrity": "sha512-TDq4Ojh9fcohAw6OIMXqiIcTq5KUXTGRkVxbSo1hQnSy6lAM5GSdfwWeSxpAo0YzgsgF182E/U0mDNhuA0tW7w==", "dev": true, + "peer": true, "dependencies": { - "safe-buffer": "~5.1.0" + "@webassemblyjs/ast": "1.12.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/ieee754": "1.11.6", + "@webassemblyjs/leb128": "1.11.6", + "@webassemblyjs/utf8": "1.11.6" } }, - "node_modules/eastasianwidth": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", - "dev": true - }, - "node_modules/ecc-jsbn": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", - "integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==", + "node_modules/@webassemblyjs/wasm-opt": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.12.1.tgz", + "integrity": "sha512-Jg99j/2gG2iaz3hijw857AVYekZe2SAskcqlWIZXjji5WStnOpVoat3gQfT/Q5tb2djnCjBtMocY/Su1GfxPBg==", "dev": true, + "peer": true, "dependencies": { - "jsbn": "~0.1.0", - "safer-buffer": "^2.1.0" + "@webassemblyjs/ast": "1.12.1", + "@webassemblyjs/helper-buffer": "1.12.1", + "@webassemblyjs/wasm-gen": "1.12.1", + "@webassemblyjs/wasm-parser": "1.12.1" } }, - "node_modules/ecc-jsbn/node_modules/jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==", - "dev": true - }, - "node_modules/ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", - "dev": true - }, - "node_modules/ejs": { - "version": "3.1.9", - "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.9.tgz", - "integrity": "sha512-rC+QVNMJWv+MtPgkt0y+0rVEIdbtxVADApW9JXrUVlzHetgcyczP/E7DJmWJ4fJCZF2cPcBk0laWO9ZHMG3DmQ==", + "node_modules/@webassemblyjs/wasm-parser": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.12.1.tgz", + "integrity": "sha512-xikIi7c2FHXysxXe3COrVUPSheuBtpcfhbpFj4gmu7KRLYOzANztwUU0IbsqvMqzuNK2+glRGWCEqZo1WCLyAQ==", "dev": true, + "peer": true, "dependencies": { - "jake": "^10.8.5" - }, - "bin": { - "ejs": "bin/cli.js" - }, - "engines": { - "node": ">=0.10.0" + "@webassemblyjs/ast": "1.12.1", + "@webassemblyjs/helper-api-error": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/ieee754": "1.11.6", + "@webassemblyjs/leb128": "1.11.6", + "@webassemblyjs/utf8": "1.11.6" } }, - "node_modules/electron-to-chromium": { - "version": "1.4.687", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.687.tgz", - "integrity": "sha512-Ic85cOuXSP6h7KM0AIJ2hpJ98Bo4hyTUjc4yjMbkvD+8yTxEhfK9+8exT2KKYsSjnCn2tGsKVSZwE7ZgTORQCw==", - "dev": true - }, - "node_modules/emitter-component": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/emitter-component/-/emitter-component-1.1.2.tgz", - "integrity": "sha512-QdXO3nXOzZB4pAjM0n6ZE+R9/+kPpECA/XSELIcc54NeYVnBqIk+4DFiBgK+8QbV3mdvTG6nedl7dTYgO+5wDw==", + "node_modules/@webassemblyjs/wast-printer": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.12.1.tgz", + "integrity": "sha512-+X4WAlOisVWQMikjbcvY2e0rwPsKQ9F688lksZhBcPycBBuii3O7m8FACbDMWDojpAqvjIncrG8J0XHKyQfVeA==", "dev": true, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "peer": true, + "dependencies": { + "@webassemblyjs/ast": "1.12.1", + "@xtuc/long": "4.2.2" } }, - "node_modules/emittery": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", - "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", + "node_modules/@xtuc/ieee754": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sindresorhus/emittery?sponsor=1" - } + "peer": true }, - "node_modules/emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "node_modules/@xtuc/long": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", + "dev": true, + "peer": true + }, + "node_modules/@yarnpkg/lockfile": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz", + "integrity": "sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==", "dev": true }, - "node_modules/emojis-list": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", - "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", + "node_modules/abab": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz", + "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==", + "deprecated": "Use your platform's native atob() and btoa() methods instead", + "dev": true + }, + "node_modules/abbrev": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-2.0.0.tgz", + "integrity": "sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==", "dev": true, "engines": { - "node": ">= 4" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", "dev": true, + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, "engines": { - "node": ">= 0.8" + "node": ">= 0.6" } }, - "node_modules/encoding": { - "version": "0.1.13", - "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", - "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", - "optional": true, + "node_modules/accessible-autocomplete": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/accessible-autocomplete/-/accessible-autocomplete-2.0.4.tgz", + "integrity": "sha512-2p0txrSpvs5wXFUeQJHMheDPTZVSEmiUHWlEPb7vJnv2Dd1xPfoLnBQQMfNbTSit2pL/9sSQYESuD2Yyohd4Yw==", "dependencies": { - "iconv-lite": "^0.6.2" + "preact": "^8.3.1" } }, - "node_modules/encoding/node_modules/iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "optional": true, - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" + "node_modules/acorn": { + "version": "8.11.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", + "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", + "dev": true, + "bin": { + "acorn": "bin/acorn" }, "engines": { - "node": ">=0.10.0" + "node": ">=0.4.0" } }, - "node_modules/end-of-stream": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", - "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "node_modules/acorn-globals": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-7.0.1.tgz", + "integrity": "sha512-umOSDSDrfHbTNPuNpC2NSnnA3LUrqpevPb4T9jRx4MagXNS0rs+gwiTcAvqCRmsD6utzsrzNt+ebm00SNWiC3Q==", "dev": true, "dependencies": { - "once": "^1.4.0" + "acorn": "^8.1.0", + "acorn-walk": "^8.0.2" } }, - "node_modules/enhanced-resolve": { - "version": "5.15.1", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.15.1.tgz", - "integrity": "sha512-3d3JRbwsCLJsYgvb6NuWEG44jjPSOMuS73L/6+7BZuoKm3W+qXnSoIYVHi8dG7Qcg4inAY4jbzkZ7MnskePeDg==", - "dependencies": { - "graceful-fs": "^4.2.4", - "tapable": "^2.2.0" - }, - "engines": { - "node": ">=10.13.0" + "node_modules/acorn-import-attributes": { + "version": "1.9.5", + "resolved": "https://registry.npmjs.org/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz", + "integrity": "sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==", + "dev": true, + "peer": true, + "peerDependencies": { + "acorn": "^8" } }, - "node_modules/enquirer": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", - "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", + "node_modules/acorn-node": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/acorn-node/-/acorn-node-1.8.2.tgz", + "integrity": "sha512-8mt+fslDufLYntIoPAaIMUe/lrbrehIiwmR3t2k9LljIzoigEPF27eLk2hy8zSGzmR/ogr7zbRKINMo1u0yh5A==", "dev": true, "dependencies": { - "ansi-colors": "^4.1.1" + "acorn": "^7.0.0", + "acorn-walk": "^7.0.0", + "xtend": "^4.0.2" + } + }, + "node_modules/acorn-node/node_modules/acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "dev": true, + "bin": { + "acorn": "bin/acorn" }, "engines": { - "node": ">=8.6" + "node": ">=0.4.0" } }, - "node_modules/entities": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", - "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "node_modules/acorn-node/node_modules/acorn-walk": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz", + "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==", "dev": true, "engines": { - "node": ">=0.12" - }, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" + "node": ">=0.4.0" } }, - "node_modules/env-paths": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", - "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "node_modules/acorn-walk": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.2.tgz", + "integrity": "sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==", "dev": true, "engines": { - "node": ">=6" + "node": ">=0.4.0" } }, - "node_modules/envinfo": { - "version": "7.11.1", - "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.11.1.tgz", - "integrity": "sha512-8PiZgZNIB4q/Lw4AhOvAfB/ityHAd2bli3lESSWmWSzSsl5dKpy5N1d1Rfkd2teq/g9xN90lc6o98DOjMeYHpg==", + "node_modules/adjust-sourcemap-loader": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/adjust-sourcemap-loader/-/adjust-sourcemap-loader-4.0.0.tgz", + "integrity": "sha512-OXwN5b9pCUXNQHJpwwD2qP40byEmSgzj8B4ydSN0uMNYWiFmJ6x6KwUllMmfk8Rwu/HJDFR7U8ubsWBoN0Xp0A==", "dev": true, - "bin": { - "envinfo": "dist/cli.js" + "peer": true, + "dependencies": { + "loader-utils": "^2.0.0", + "regex-parser": "^2.2.11" }, "engines": { - "node": ">=4" + "node": ">=8.9" } }, - "node_modules/err-code": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz", - "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==", - "dev": true - }, - "node_modules/errno": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz", - "integrity": "sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==", + "node_modules/adjust-sourcemap-loader/node_modules/loader-utils": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", + "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", "dev": true, - "optional": true, + "peer": true, "dependencies": { - "prr": "~1.0.1" + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" }, - "bin": { - "errno": "cli.js" + "engines": { + "node": ">=8.9.0" } }, - "node_modules/error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "node_modules/adm-zip": { + "version": "0.5.10", + "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.5.10.tgz", + "integrity": "sha512-x0HvcHqVJNTPk/Bw8JbLWlWoo6Wwnsug0fnYYro1HBrjxZ3G7/AZk7Ahv8JwDe1uIcz8eBqvu86FuF1POiG7vQ==", "dev": true, - "dependencies": { - "is-arrayish": "^0.2.1" + "engines": { + "node": ">=6.0" } }, - "node_modules/errorhandler": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/errorhandler/-/errorhandler-1.5.1.tgz", - "integrity": "sha512-rcOwbfvP1WTViVoUjcfZicVzjhjTuhSMntHh6mW3IrEiyE6mJyXvsToJUJGlGlw/2xU9P5whlWNGlIDVeCiT4A==", + "node_modules/agent-base": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz", + "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==", "dev": true, "dependencies": { - "accepts": "~1.3.7", - "escape-html": "~1.0.3" + "debug": "^4.3.4" }, "engines": { - "node": ">= 0.8" + "node": ">= 14" } }, - "node_modules/es-abstract": { - "version": "1.22.5", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.5.tgz", - "integrity": "sha512-oW69R+4q2wG+Hc3KZePPZxOiisRIqfKBVo/HLx94QcJeWGU/8sZhCvc829rd1kS366vlJbzBfXf9yWwf0+Ko7w==", + "node_modules/aggregate-error": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "dev": true, "dependencies": { - "array-buffer-byte-length": "^1.0.1", - "arraybuffer.prototype.slice": "^1.0.3", - "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.7", - "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "es-set-tostringtag": "^2.0.3", - "es-to-primitive": "^1.2.1", - "function.prototype.name": "^1.1.6", - "get-intrinsic": "^1.2.4", - "get-symbol-description": "^1.0.2", - "globalthis": "^1.0.3", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.2", - "has-proto": "^1.0.3", - "has-symbols": "^1.0.3", - "hasown": "^2.0.1", - "internal-slot": "^1.0.7", - "is-array-buffer": "^3.0.4", - "is-callable": "^1.2.7", - "is-negative-zero": "^2.0.3", - "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.3", - "is-string": "^1.0.7", - "is-typed-array": "^1.1.13", - "is-weakref": "^1.0.2", - "object-inspect": "^1.13.1", - "object-keys": "^1.1.1", - "object.assign": "^4.1.5", - "regexp.prototype.flags": "^1.5.2", - "safe-array-concat": "^1.1.0", - "safe-regex-test": "^1.0.3", - "string.prototype.trim": "^1.2.8", - "string.prototype.trimend": "^1.0.7", - "string.prototype.trimstart": "^1.0.7", - "typed-array-buffer": "^1.0.2", - "typed-array-byte-length": "^1.0.1", - "typed-array-byte-offset": "^1.0.2", - "typed-array-length": "^1.0.5", - "unbox-primitive": "^1.0.2", - "which-typed-array": "^1.1.14" + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=8" } }, - "node_modules/es-array-method-boxes-properly": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz", - "integrity": "sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA==" - }, - "node_modules/es-define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", - "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", + "node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", "dependencies": { - "get-intrinsic": "^1.2.4" + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-errors": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "engines": { - "node": ">= 0.4" + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/es-get-iterator": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.3.tgz", - "integrity": "sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw==", + "node_modules/ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.3", - "has-symbols": "^1.0.3", - "is-arguments": "^1.1.1", - "is-map": "^2.0.2", - "is-set": "^2.0.2", - "is-string": "^1.0.7", - "isarray": "^2.0.5", - "stop-iteration-iterator": "^1.0.0" + "ajv": "^8.0.0" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } } }, - "node_modules/es-iterator-helpers": { - "version": "1.0.17", - "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.0.17.tgz", - "integrity": "sha512-lh7BsUqelv4KUbR5a/ZTaGGIMLCjPGPqJ6q+Oq24YP0RdyptX1uzm4vvaqzk7Zx3bpl/76YLTTDj9L7uYQ92oQ==", + "node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", "dev": true, - "peer": true, "dependencies": { - "asynciterator.prototype": "^1.0.0", - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.22.4", - "es-errors": "^1.3.0", - "es-set-tostringtag": "^2.0.2", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.4", - "globalthis": "^1.0.3", - "has-property-descriptors": "^1.0.2", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "internal-slot": "^1.0.7", - "iterator.prototype": "^1.1.2", - "safe-array-concat": "^1.1.0" + "fast-deep-equal": "^3.1.3" }, - "engines": { - "node": ">= 0.4" + "peerDependencies": { + "ajv": "^8.8.2" } }, - "node_modules/es-module-lexer": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.4.1.tgz", - "integrity": "sha512-cXLGjP0c4T3flZJKQSuziYoq7MlT+rnvfZjfp7h+I7K9BNX54kP9nyWvdbwjQ4u1iWbOL4u96fgeZLToQlZC7w==", - "dev": true + "node_modules/amdefine": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", + "integrity": "sha512-S2Hw0TtNkMJhIabBwIojKL9YHO5T0n5eNqWJ7Lrlel/zDbftQpxpapi8tZs3X1HWa+u+QeydGmzzNU0m09+Rcg==", + "dev": true, + "optional": true, + "engines": { + "node": ">=0.4.2" + } }, - "node_modules/es-set-tostringtag": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.3.tgz", - "integrity": "sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==", + "node_modules/angular-google-tag-manager": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/angular-google-tag-manager/-/angular-google-tag-manager-1.10.0.tgz", + "integrity": "sha512-6yqa2iupJqTKc31ub1ggVpRFqFQ/H3RCXxav6qFgN9eh/uTKQ7RaH18/sEfKjPwHWuD8IxHMh5tkgjWoJ2Nx0g==", + "license": "MIT", "dependencies": { - "get-intrinsic": "^1.2.4", - "has-tostringtag": "^1.0.2", - "hasown": "^2.0.1" + "tslib": "^2.5.0" }, - "engines": { - "node": ">= 0.4" + "peerDependencies": { + "@angular/common": "^18.0.0", + "@angular/compiler": "^18.0.0" } }, - "node_modules/es-shim-unscopables": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.2.tgz", - "integrity": "sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==", - "dependencies": { - "hasown": "^2.0.0" + "node_modules/ansi-colors": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", + "dev": true, + "engines": { + "node": ">=6" } }, - "node_modules/es-to-primitive": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", - "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, "dependencies": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" + "type-fest": "^0.21.3" }, "engines": { - "node": ">= 0.4" + "node": ">=8" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/es5-ext": { - "version": "0.10.64", - "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.64.tgz", - "integrity": "sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg==", + "node_modules/ansi-escapes/node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", "dev": true, - "hasInstallScript": true, - "dependencies": { - "es6-iterator": "^2.0.3", - "es6-symbol": "^3.1.3", - "esniff": "^2.0.1", - "next-tick": "^1.1.0" - }, "engines": { - "node": ">=0.10" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/es6-iterator": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", - "integrity": "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==", + "node_modules/ansi-html-community": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/ansi-html-community/-/ansi-html-community-0.0.8.tgz", + "integrity": "sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw==", "dev": true, - "dependencies": { - "d": "1", - "es5-ext": "^0.10.35", - "es6-symbol": "^3.1.1" + "engines": [ + "node >= 0.8.0" + ], + "peer": true, + "bin": { + "ansi-html": "bin/ansi-html" } }, - "node_modules/es6-map": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/es6-map/-/es6-map-0.1.5.tgz", - "integrity": "sha512-mz3UqCh0uPCIqsw1SSAkB/p0rOzF/M0V++vyN7JqlPtSW/VsYgQBvVvqMLmfBuyMzTpLnNqi6JmcSizs4jy19A==", - "dev": true, - "dependencies": { - "d": "1", - "es5-ext": "~0.10.14", - "es6-iterator": "~2.0.1", - "es6-set": "~0.1.5", - "es6-symbol": "~3.1.1", - "event-emitter": "~0.3.5" + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" } }, - "node_modules/es6-set": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/es6-set/-/es6-set-0.1.6.tgz", - "integrity": "sha512-TE3LgGLDIBX332jq3ypv6bcOpkLO0AslAQo7p2VqX/1N46YNsvIWgvjojjSEnWEGWMhr1qUbYeTSir5J6mFHOw==", + "node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dev": true, "dependencies": { - "d": "^1.0.1", - "es5-ext": "^0.10.62", - "es6-iterator": "~2.0.3", - "es6-symbol": "^3.1.3", - "event-emitter": "^0.3.5", - "type": "^2.7.2" + "color-convert": "^1.9.0" }, "engines": { - "node": ">=0.12" + "node": ">=4" } }, - "node_modules/es6-set/node_modules/type": { - "version": "2.7.2", - "resolved": "https://registry.npmjs.org/type/-/type-2.7.2.tgz", - "integrity": "sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw==", - "dev": true + "node_modules/ansi-styles/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } }, - "node_modules/es6-shim": { - "version": "0.35.8", - "resolved": "https://registry.npmjs.org/es6-shim/-/es6-shim-0.35.8.tgz", - "integrity": "sha512-Twf7I2v4/1tLoIXMT8HlqaBSS5H2wQTs2wx3MNYCI8K1R1/clXyCazrcVCPm/FuO9cyV8+leEaZOWD5C253NDg==", + "node_modules/ansi-styles/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", "dev": true }, - "node_modules/es6-symbol": { + "node_modules/anymatch": { "version": "3.1.3", - "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz", - "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", "dev": true, "dependencies": { - "d": "^1.0.1", - "ext": "^1.1.2" + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" } }, - "node_modules/esbuild": { - "version": "0.20.0", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.20.0.tgz", - "integrity": "sha512-6iwE3Y2RVYCME1jLpBqq7LQWK3MW6vjV2bZy6gt/WrqkY+WE74Spyc0ThAOYpMtITvnjX09CrC6ym7A/m9mebA==", + "node_modules/anymatch/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true, - "hasInstallScript": true, - "bin": { - "esbuild": "bin/esbuild" - }, "engines": { - "node": ">=12" + "node": ">=8.6" }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.20.0", - "@esbuild/android-arm": "0.20.0", - "@esbuild/android-arm64": "0.20.0", - "@esbuild/android-x64": "0.20.0", - "@esbuild/darwin-arm64": "0.20.0", - "@esbuild/darwin-x64": "0.20.0", - "@esbuild/freebsd-arm64": "0.20.0", - "@esbuild/freebsd-x64": "0.20.0", - "@esbuild/linux-arm": "0.20.0", - "@esbuild/linux-arm64": "0.20.0", - "@esbuild/linux-ia32": "0.20.0", - "@esbuild/linux-loong64": "0.20.0", - "@esbuild/linux-mips64el": "0.20.0", - "@esbuild/linux-ppc64": "0.20.0", - "@esbuild/linux-riscv64": "0.20.0", - "@esbuild/linux-s390x": "0.20.0", - "@esbuild/linux-x64": "0.20.0", - "@esbuild/netbsd-x64": "0.20.0", - "@esbuild/openbsd-x64": "0.20.0", - "@esbuild/sunos-x64": "0.20.0", - "@esbuild/win32-arm64": "0.20.0", - "@esbuild/win32-ia32": "0.20.0", - "@esbuild/win32-x64": "0.20.0" - } - }, - "node_modules/esbuild-plugin-alias": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/esbuild-plugin-alias/-/esbuild-plugin-alias-0.2.1.tgz", - "integrity": "sha512-jyfL/pwPqaFXyKnj8lP8iLk6Z0m099uXR45aSN8Av1XD4vhvQutxxPzgA2bTcAwQpa1zCXDcWOlhFgyP3GKqhQ==", - "dev": true + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } }, - "node_modules/esbuild-register": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/esbuild-register/-/esbuild-register-3.5.0.tgz", - "integrity": "sha512-+4G/XmakeBAsvJuDugJvtyF1x+XJT4FMocynNpxrvEBViirpfUn2PgNpCHedfWhF4WokNsO/OvMKrmJOIJsI5A==", + "node_modules/apache-crypt": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/apache-crypt/-/apache-crypt-1.2.6.tgz", + "integrity": "sha512-072WetlM4blL8PREJVeY+WHiUh1R5VNt2HfceGS8aKqttPHcmqE5pkKuXPz/ULmJOFkc8Hw3kfKl6vy7Qka6DA==", "dev": true, "dependencies": { - "debug": "^4.3.4" + "unix-crypt-td-js": "^1.1.4" }, - "peerDependencies": { - "esbuild": ">=0.12 <1" + "engines": { + "node": ">=8" } }, - "node_modules/esbuild-wasm": { - "version": "0.20.0", - "resolved": "https://registry.npmjs.org/esbuild-wasm/-/esbuild-wasm-0.20.0.tgz", - "integrity": "sha512-Lc9KeQCg1Zf8kCtfDXgy29rx0x8dOuhDWbkP76Wc64q7ctOOc1Zv1C39AxiE+y4N6ONyXtJk4HKpM7jlU7/jSA==", + "node_modules/apache-md5": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/apache-md5/-/apache-md5-1.1.8.tgz", + "integrity": "sha512-FCAJojipPn0bXjuEpjOOOMN8FZDkxfWWp4JGN9mifU2IhxvKyXZYqpzPHdnTSUpmPDy+tsslB6Z1g+Vg6nVbYA==", "dev": true, - "bin": { - "esbuild": "bin/esbuild" - }, "engines": { - "node": ">=12" + "node": ">=8" } }, - "node_modules/escalade": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", - "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", + "node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true + }, + "node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", "dev": true, - "engines": { - "node": ">=6" + "dependencies": { + "sprintf-js": "~1.0.2" } }, - "node_modules/escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", "dev": true }, - "node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "node_modules/array-from": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/array-from/-/array-from-2.1.1.tgz", + "integrity": "sha512-GQTc6Uupx1FCavi5mPzBvVT7nEOeWMmUA9P95wpfpW1XwMSKs+KaymD5C2Up7KAUKg/mYwbsUYzdZWcoajlNZg==", + "dev": true + }, + "node_modules/array-ify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-ify/-/array-ify-1.0.0.tgz", + "integrity": "sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng==", + "dev": true + }, + "node_modules/arrify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", + "integrity": "sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==", "dev": true, "engines": { - "node": ">=0.8.0" + "node": ">=0.10.0" } }, - "node_modules/escodegen": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz", - "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", + "node_modules/ast-transform": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/ast-transform/-/ast-transform-0.0.0.tgz", + "integrity": "sha512-e/JfLiSoakfmL4wmTGPjv0HpTICVmxwXgYOB8x+mzozHL8v+dSfCbrJ8J8hJ0YBP0XcYu1aLZ6b/3TnxNK3P2A==", "dev": true, "dependencies": { - "esprima": "^4.0.1", - "estraverse": "^5.2.0", - "esutils": "^2.0.2" + "escodegen": "~1.2.0", + "esprima": "~1.0.4", + "through": "~2.3.4" + } + }, + "node_modules/ast-transform/node_modules/escodegen": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.2.0.tgz", + "integrity": "sha512-yLy3Cc+zAC0WSmoT2fig3J87TpQ8UaZGx8ahCAs9FL8qNbyV7CVyPKS74DG4bsHiL5ew9sxdYx131OkBQMFnvA==", + "dev": true, + "dependencies": { + "esprima": "~1.0.4", + "estraverse": "~1.5.0", + "esutils": "~1.0.0" }, "bin": { "escodegen": "bin/escodegen.js", "esgenerate": "bin/esgenerate.js" }, "engines": { - "node": ">=6.0" + "node": ">=0.4.0" }, "optionalDependencies": { - "source-map": "~0.6.1" + "source-map": "~0.1.30" } }, - "node_modules/escodegen/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "node_modules/ast-transform/node_modules/esprima": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-1.0.4.tgz", + "integrity": "sha512-rp5dMKN8zEs9dfi9g0X1ClLmV//WRyk/R15mppFNICIFRG5P92VP7Z04p8pk++gABo9W2tY+kHyu6P1mEHgmTA==", "dev": true, - "optional": true, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, "engines": { - "node": ">=0.10.0" + "node": ">=0.4.0" } }, - "node_modules/eslint": { - "version": "8.57.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz", - "integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==", - "dependencies": { - "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.6.1", - "@eslint/eslintrc": "^2.1.4", - "@eslint/js": "8.57.0", - "@humanwhocodes/config-array": "^0.11.14", - "@humanwhocodes/module-importer": "^1.0.1", - "@nodelib/fs.walk": "^1.2.8", - "@ungap/structured-clone": "^1.2.0", - "ajv": "^6.12.4", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", - "debug": "^4.3.2", - "doctrine": "^3.0.0", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.2.2", - "eslint-visitor-keys": "^3.4.3", - "espree": "^9.6.1", - "esquery": "^1.4.2", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", - "find-up": "^5.0.0", - "glob-parent": "^6.0.2", - "globals": "^13.19.0", - "graphemer": "^1.4.0", - "ignore": "^5.2.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "is-path-inside": "^3.0.3", - "js-yaml": "^4.1.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.1.2", - "natural-compare": "^1.4.0", - "optionator": "^0.9.3", - "strip-ansi": "^6.0.1", - "text-table": "^0.2.0" - }, - "bin": { - "eslint": "bin/eslint.js" - }, + "node_modules/ast-transform/node_modules/estraverse": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-1.5.1.tgz", + "integrity": "sha512-FpCjJDfmo3vsc/1zKSeqR5k42tcIhxFIlvq+h9j0fO2q/h2uLKyweq7rYJ+0CoVvrGQOxIS5wyBrW/+vF58BUQ==", + "dev": true, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" + "node": ">=0.4.0" } }, - "node_modules/eslint-config-airbnb-base": { - "version": "15.0.0", - "resolved": "https://registry.npmjs.org/eslint-config-airbnb-base/-/eslint-config-airbnb-base-15.0.0.tgz", - "integrity": "sha512-xaX3z4ZZIcFLvh2oUNvcX5oEofXda7giYmuplVxoOg5A7EXJMrUyqRgR+mhDhPK8LZ4PttFOBvCYDbX3sUoUig==", + "node_modules/ast-transform/node_modules/esutils": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-1.0.0.tgz", + "integrity": "sha512-x/iYH53X3quDwfHRz4y8rn4XcEwwCJeWsul9pF1zldMbGtgOtMNBEOuYWwB1EQlK2LRa1fev3YAgym/RElp5Cg==", "dev": true, - "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ast-transform/node_modules/source-map": { + "version": "0.1.43", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.1.43.tgz", + "integrity": "sha512-VtCvB9SIQhk3aF6h+N85EaqIaBFIAfZ9Cu+NJHHVvc8BbEcnvDcFw6sqQ2dQrT6SlOrZq3tIvyD9+EGq/lJryQ==", + "dev": true, + "optional": true, "dependencies": { - "confusing-browser-globals": "^1.0.10", - "object.assign": "^4.1.2", - "object.entries": "^1.1.5", - "semver": "^6.3.0" + "amdefine": ">=0.0.4" }, "engines": { - "node": "^10.12.0 || >=12.0.0" - }, - "peerDependencies": { - "eslint": "^7.32.0 || ^8.2.0", - "eslint-plugin-import": "^2.25.2" + "node": ">=0.8.0" } }, - "node_modules/eslint-config-airbnb-base/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "node_modules/ast-types": { + "version": "0.7.8", + "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.7.8.tgz", + "integrity": "sha512-RIOpVnVlltB6PcBJ5BMLx+H+6JJ/zjDGU0t7f0L6c2M1dqcK92VQopLBlPQ9R80AVXelfqYgjcPLtHtDbNFg0Q==", "dev": true, - "peer": true, - "bin": { - "semver": "bin/semver.js" + "engines": { + "node": ">= 0.6" } }, - "node_modules/eslint-config-airbnb-typescript": { - "version": "17.1.0", - "resolved": "https://registry.npmjs.org/eslint-config-airbnb-typescript/-/eslint-config-airbnb-typescript-17.1.0.tgz", - "integrity": "sha512-GPxI5URre6dDpJ0CtcthSZVBAfI+Uw7un5OYNVxP2EYi3H81Jw701yFP7AU+/vCE7xBtFmjge7kfhhk4+RAiig==", + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "dev": true + }, + "node_modules/autoprefixer": { + "version": "10.4.20", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.20.tgz", + "integrity": "sha512-XY25y5xSv/wEoqzDyXXME4AFfkZI0P23z6Fs3YgymDnKJkCGOnkL0iTxCa85UTqaSgfcqyf3UA6+c7wUvx/16g==", "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/autoprefixer" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], "peer": true, "dependencies": { - "eslint-config-airbnb-base": "^15.0.0" + "browserslist": "^4.23.3", + "caniuse-lite": "^1.0.30001646", + "fraction.js": "^4.3.7", + "normalize-range": "^0.1.2", + "picocolors": "^1.0.1", + "postcss-value-parser": "^4.2.0" }, - "peerDependencies": { - "@typescript-eslint/eslint-plugin": "^5.13.0 || ^6.0.0", - "@typescript-eslint/parser": "^5.0.0 || ^6.0.0", - "eslint": "^7.32.0 || ^8.2.0", - "eslint-plugin-import": "^2.25.3" - } - }, - "node_modules/eslint-config-prettier": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.1.0.tgz", - "integrity": "sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==", - "dev": true, "bin": { - "eslint-config-prettier": "bin/cli.js" + "autoprefixer": "bin/autoprefixer" }, - "peerDependencies": { - "eslint": ">=7.0.0" - } - }, - "node_modules/eslint-etc": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/eslint-etc/-/eslint-etc-5.2.1.tgz", - "integrity": "sha512-lFJBSiIURdqQKq9xJhvSJFyPA+VeTh5xvk24e8pxVL7bwLBtGF60C/KRkLTMrvCZ6DA3kbPuYhLWY0TZMlqTsg==", - "dev": true, - "dependencies": { - "@typescript-eslint/experimental-utils": "^5.0.0", - "tsutils": "^3.17.1", - "tsutils-etc": "^1.4.1" + "engines": { + "node": "^10 || ^12 || >=14" }, "peerDependencies": { - "eslint": "^8.0.0", - "typescript": ">=4.0.0" - } - }, - "node_modules/eslint-import-resolver-node": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz", - "integrity": "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==", - "dependencies": { - "debug": "^3.2.7", - "is-core-module": "^2.13.0", - "resolve": "^1.22.4" - } - }, - "node_modules/eslint-import-resolver-node/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dependencies": { - "ms": "^2.1.1" + "postcss": "^8.1.0" } }, - "node_modules/eslint-import-resolver-typescript": { - "version": "3.6.1", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-3.6.1.tgz", - "integrity": "sha512-xgdptdoi5W3niYeuQxKmzVDTATvLYqhpwmykwsh7f6HIOStGWEIL9iqZgQDF9u9OEzrRwR8no5q2VT+bjAujTg==", + "node_modules/available-typed-arrays": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", "dependencies": { - "debug": "^4.3.4", - "enhanced-resolve": "^5.12.0", - "eslint-module-utils": "^2.7.4", - "fast-glob": "^3.3.1", - "get-tsconfig": "^4.5.0", - "is-core-module": "^2.11.0", - "is-glob": "^4.0.3" + "possible-typed-array-names": "^1.0.0" }, "engines": { - "node": "^14.18.0 || >=16.0.0" + "node": ">= 0.4" }, "funding": { - "url": "https://opencollective.com/unts/projects/eslint-import-resolver-ts" - }, - "peerDependencies": { - "eslint": "*", - "eslint-plugin-import": "*" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/eslint-module-utils": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.8.1.tgz", - "integrity": "sha512-rXDXR3h7cs7dy9RNpUlQf80nX31XWJEyGq1tRMo+6GsO5VmTe4UTwtmonAD4ZkAsrfMVDA2wlGJ3790Ys+D49Q==", + "node_modules/babel-jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", + "integrity": "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==", + "dev": true, "dependencies": { - "debug": "^3.2.7" + "@jest/transform": "^29.7.0", + "@types/babel__core": "^7.1.14", + "babel-plugin-istanbul": "^6.1.1", + "babel-preset-jest": "^29.6.3", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "slash": "^3.0.0" }, "engines": { - "node": ">=4" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" }, - "peerDependenciesMeta": { - "eslint": { - "optional": true - } - } - }, - "node_modules/eslint-module-utils/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dependencies": { - "ms": "^2.1.1" + "peerDependencies": { + "@babel/core": "^7.8.0" } }, - "node_modules/eslint-plugin-import": { - "version": "2.29.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.29.1.tgz", - "integrity": "sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw==", + "node_modules/babel-jest/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, "dependencies": { - "array-includes": "^3.1.7", - "array.prototype.findlastindex": "^1.2.3", - "array.prototype.flat": "^1.3.2", - "array.prototype.flatmap": "^1.3.2", - "debug": "^3.2.7", - "doctrine": "^2.1.0", - "eslint-import-resolver-node": "^0.3.9", - "eslint-module-utils": "^2.8.0", - "hasown": "^2.0.0", - "is-core-module": "^2.13.1", - "is-glob": "^4.0.3", - "minimatch": "^3.1.2", - "object.fromentries": "^2.0.7", - "object.groupby": "^1.0.1", - "object.values": "^1.1.7", - "semver": "^6.3.1", - "tsconfig-paths": "^3.15.0" + "color-convert": "^2.0.1" }, "engines": { - "node": ">=4" + "node": ">=8" }, - "peerDependencies": { - "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8" + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/eslint-plugin-import/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "node_modules/babel-jest/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, "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/eslint-plugin-import/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dependencies": { - "ms": "^2.1.1" + "node_modules/babel-jest/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" } }, - "node_modules/eslint-plugin-import/node_modules/doctrine": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", - "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "node_modules/babel-jest/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, "dependencies": { - "esutils": "^2.0.2" + "has-flag": "^4.0.0" }, "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, - "node_modules/eslint-plugin-import/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "node_modules/babel-loader": { + "version": "9.1.3", + "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-9.1.3.tgz", + "integrity": "sha512-xG3ST4DglodGf8qSwv0MdeWLhrDsw/32QMdTO5T1ZIp9gQur0HkCyFs7Awskr10JKXFXwpAhiCuYX5oGXnRGbw==", + "dev": true, "dependencies": { - "brace-expansion": "^1.1.7" + "find-cache-dir": "^4.0.0", + "schema-utils": "^4.0.0" }, "engines": { - "node": "*" - } - }, - "node_modules/eslint-plugin-import/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "bin": { - "semver": "bin/semver.js" + "node": ">= 14.15.0" + }, + "peerDependencies": { + "@babel/core": "^7.12.0", + "webpack": ">=5" } }, - "node_modules/eslint-plugin-jest": { - "version": "27.9.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-27.9.0.tgz", - "integrity": "sha512-QIT7FH7fNmd9n4se7FFKHbsLKGQiw885Ds6Y/sxKgCZ6natwCsXdgPOADnYVxN2QrRweF0FZWbJ6S7Rsn7llug==", + "node_modules/babel-loader/node_modules/find-cache-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-4.0.0.tgz", + "integrity": "sha512-9ZonPT4ZAK4a+1pUPVPZJapbi7O5qbbJPdYw/NOQWZZbVLdDTYM3A4R9z/DpAM08IDaFGsvPgiGZ82WEwUDWjg==", "dev": true, - "peer": true, "dependencies": { - "@typescript-eslint/utils": "^5.10.0" + "common-path-prefix": "^3.0.0", + "pkg-dir": "^7.0.0" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "@typescript-eslint/eslint-plugin": "^5.0.0 || ^6.0.0 || ^7.0.0", - "eslint": "^7.0.0 || ^8.0.0", - "jest": "*" + "node": ">=14.16" }, - "peerDependenciesMeta": { - "@typescript-eslint/eslint-plugin": { - "optional": true - }, - "jest": { - "optional": true - } + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/eslint-plugin-jest/node_modules/@typescript-eslint/scope-manager": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.62.0.tgz", - "integrity": "sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w==", + "node_modules/babel-loader/node_modules/find-up": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-6.3.0.tgz", + "integrity": "sha512-v2ZsoEuVHYy8ZIlYqwPe/39Cy+cFDzp4dXPaxNvkEuouymu+2Jbz0PxpKarJHYJTmv2HWT3O382qY8l4jMWthw==", "dev": true, - "peer": true, "dependencies": { - "@typescript-eslint/types": "5.62.0", - "@typescript-eslint/visitor-keys": "5.62.0" + "locate-path": "^7.1.0", + "path-exists": "^5.0.0" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/eslint-plugin-jest/node_modules/@typescript-eslint/types": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.62.0.tgz", - "integrity": "sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==", + "node_modules/babel-loader/node_modules/locate-path": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-7.2.0.tgz", + "integrity": "sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==", "dev": true, - "peer": true, + "dependencies": { + "p-locate": "^6.0.0" + }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/eslint-plugin-jest/node_modules/@typescript-eslint/typescript-estree": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz", - "integrity": "sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==", + "node_modules/babel-loader/node_modules/p-limit": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-4.0.0.tgz", + "integrity": "sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==", "dev": true, - "peer": true, "dependencies": { - "@typescript-eslint/types": "5.62.0", - "@typescript-eslint/visitor-keys": "5.62.0", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "semver": "^7.3.7", - "tsutils": "^3.21.0" + "yocto-queue": "^1.0.0" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/eslint-plugin-jest/node_modules/@typescript-eslint/utils": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.62.0.tgz", - "integrity": "sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==", + "node_modules/babel-loader/node_modules/p-locate": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-6.0.0.tgz", + "integrity": "sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==", "dev": true, - "peer": true, "dependencies": { - "@eslint-community/eslint-utils": "^4.2.0", - "@types/json-schema": "^7.0.9", - "@types/semver": "^7.3.12", - "@typescript-eslint/scope-manager": "5.62.0", - "@typescript-eslint/types": "5.62.0", - "@typescript-eslint/typescript-estree": "5.62.0", - "eslint-scope": "^5.1.1", - "semver": "^7.3.7" + "p-limit": "^4.0.0" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/eslint-plugin-jest/node_modules/@typescript-eslint/visitor-keys": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz", - "integrity": "sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==", + "node_modules/babel-loader/node_modules/path-exists": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-5.0.0.tgz", + "integrity": "sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } + }, + "node_modules/babel-loader/node_modules/pkg-dir": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-7.0.0.tgz", + "integrity": "sha512-Ie9z/WINcxxLp27BKOCHGde4ITq9UklYKDzVo1nhk5sqGEXU3FpkwP5GM2voTGJkGd9B3Otl+Q4uwSOeSUtOBA==", "dev": true, - "peer": true, "dependencies": { - "@typescript-eslint/types": "5.62.0", - "eslint-visitor-keys": "^3.3.0" + "find-up": "^6.3.0" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": ">=14.16" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/eslint-plugin-jest/node_modules/eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "node_modules/babel-loader/node_modules/yocto-queue": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.0.0.tgz", + "integrity": "sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==", "dev": true, - "peer": true, - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - }, "engines": { - "node": ">=8.0.0" + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/eslint-plugin-jest/node_modules/estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "node_modules/babel-plugin-istanbul": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", + "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", "dev": true, - "peer": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^5.0.4", + "test-exclude": "^6.0.0" + }, "engines": { - "node": ">=4.0" + "node": ">=8" } }, - "node_modules/eslint-plugin-jsx-a11y": { - "version": "6.8.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.8.0.tgz", - "integrity": "sha512-Hdh937BS3KdwwbBaKd5+PLCOmYY6U4f2h9Z2ktwtNKvIdIEu137rjYbcb9ApSbVJfWxANNuiKTD/9tOKjK9qOA==", + "node_modules/babel-plugin-jest-hoist": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz", + "integrity": "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==", "dev": true, - "peer": true, "dependencies": { - "@babel/runtime": "^7.23.2", - "aria-query": "^5.3.0", - "array-includes": "^3.1.7", - "array.prototype.flatmap": "^1.3.2", - "ast-types-flow": "^0.0.8", - "axe-core": "=4.7.0", - "axobject-query": "^3.2.1", - "damerau-levenshtein": "^1.0.8", - "emoji-regex": "^9.2.2", - "es-iterator-helpers": "^1.0.15", - "hasown": "^2.0.0", - "jsx-ast-utils": "^3.3.5", - "language-tags": "^1.0.9", - "minimatch": "^3.1.2", - "object.entries": "^1.1.7", - "object.fromentries": "^2.0.7" + "@babel/template": "^7.3.3", + "@babel/types": "^7.3.3", + "@types/babel__core": "^7.1.14", + "@types/babel__traverse": "^7.0.6" }, "engines": { - "node": ">=4.0" - }, - "peerDependencies": { - "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/eslint-plugin-jsx-a11y/node_modules/axobject-query": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-3.2.1.tgz", - "integrity": "sha512-jsyHu61e6N4Vbz/v18DHwWYKK0bSWLqn47eeDSKPB7m8tqMHF9YJ+mhIk2lVteyZrY8tnSj/jHOv4YiTCuCJgg==", + "node_modules/babel-plugin-polyfill-corejs2": { + "version": "0.4.11", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.11.tgz", + "integrity": "sha512-sMEJ27L0gRHShOh5G54uAAPaiCOygY/5ratXuiyb2G46FmlSpc9eFCzYVyDiPxfNbwzA7mYahmjQc5q+CZQ09Q==", "dev": true, - "peer": true, "dependencies": { - "dequal": "^2.0.3" + "@babel/compat-data": "^7.22.6", + "@babel/helper-define-polyfill-provider": "^0.6.2", + "semver": "^6.3.1" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, - "node_modules/eslint-plugin-jsx-a11y/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "node_modules/babel-plugin-polyfill-corejs2/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, - "peer": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "bin": { + "semver": "bin/semver.js" } }, - "node_modules/eslint-plugin-jsx-a11y/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "node_modules/babel-plugin-polyfill-corejs3": { + "version": "0.10.6", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.10.6.tgz", + "integrity": "sha512-b37+KR2i/khY5sKmWNVQAnitvquQbNdWy6lJdsr0kmquCKEEUgMKK4SboVM3HtfnZilfjr4MMQ7vY58FVWDtIA==", "dev": true, - "peer": true, "dependencies": { - "brace-expansion": "^1.1.7" + "@babel/helper-define-polyfill-provider": "^0.6.2", + "core-js-compat": "^3.38.0" }, - "engines": { - "node": "*" + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, - "node_modules/eslint-plugin-ngrx": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/eslint-plugin-ngrx/-/eslint-plugin-ngrx-2.1.4.tgz", - "integrity": "sha512-ynPPqguIpeiRPcqmkUBx+cZxzMKpx1W2T3bvraai+KZ+LZnkRouWhnr/9L0buN/Efrk7BkpAFKGy+OojPHo+ug==", + "node_modules/babel-plugin-polyfill-regenerator": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.2.tgz", + "integrity": "sha512-2R25rQZWP63nGwaAswvDazbPXfrM3HwVoBXK6HcqeKrSrL/JqcC/rDcf95l4r7LXLyxDXc8uQDa064GubtCABg==", "dev": true, "dependencies": { - "@angular-devkit/schematics": "^13.0.3", - "@typescript-eslint/experimental-utils": "^5.4.0", - "eslint-etc": "^5.1.0", - "semver": "^7.3.5", - "strip-json-comments": "3.1.1" + "@babel/helper-define-polyfill-provider": "^0.6.2" }, "peerDependencies": { - "eslint": ">=8.0.0", - "typescript": ">=4.3.5" + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, - "node_modules/eslint-plugin-ngrx/node_modules/@angular-devkit/core": { - "version": "13.3.11", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-13.3.11.tgz", - "integrity": "sha512-rfqoLMRYhlz0wzKlHx7FfyIyQq8dKTsmbCoIVU1cEIH0gyTMVY7PbVzwRRcO6xp5waY+0hA+0Brriujpuhkm4w==", + "node_modules/babel-preset-current-node-syntax": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz", + "integrity": "sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==", "dev": true, "dependencies": { - "ajv": "8.9.0", - "ajv-formats": "2.1.1", - "fast-json-stable-stringify": "2.1.0", - "magic-string": "0.25.7", - "rxjs": "6.6.7", - "source-map": "0.7.3" - }, - "engines": { - "node": "^12.20.0 || ^14.15.0 || >=16.10.0", - "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", - "yarn": ">= 1.13.0" + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-bigint": "^7.8.3", + "@babel/plugin-syntax-class-properties": "^7.8.3", + "@babel/plugin-syntax-import-meta": "^7.8.3", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.8.3", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-top-level-await": "^7.8.3" }, "peerDependencies": { - "chokidar": "^3.5.2" - }, - "peerDependenciesMeta": { - "chokidar": { - "optional": true - } + "@babel/core": "^7.0.0" } }, - "node_modules/eslint-plugin-ngrx/node_modules/@angular-devkit/schematics": { - "version": "13.3.11", - "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-13.3.11.tgz", - "integrity": "sha512-ben+EGXpCrClnIVAAnEQmhQdKmnnqFhMp5BqMxgOslSYBAmCutLA6rBu5vsc8kZcGian1wt+lueF7G1Uk5cGBg==", + "node_modules/babel-preset-jest": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz", + "integrity": "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==", "dev": true, "dependencies": { - "@angular-devkit/core": "13.3.11", - "jsonc-parser": "3.0.0", - "magic-string": "0.25.7", - "ora": "5.4.1", - "rxjs": "6.6.7" + "babel-plugin-jest-hoist": "^29.6.3", + "babel-preset-current-node-syntax": "^1.0.0" }, "engines": { - "node": "^12.20.0 || ^14.15.0 || >=16.10.0", - "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", - "yarn": ">= 1.13.0" - } - }, - "node_modules/eslint-plugin-ngrx/node_modules/ajv": { - "version": "8.9.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.9.0.tgz", - "integrity": "sha512-qOKJyNj/h+OWx7s5DePL6Zu1KeM9jPZhwBqs+7DzP6bGOvqzVCSf0xueYmVuaC/oQ/VtS2zLMLHdQFbkka+XDQ==", - "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" + "peerDependencies": { + "@babel/core": "^7.0.0" } }, - "node_modules/eslint-plugin-ngrx/node_modules/jsonc-parser": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.0.0.tgz", - "integrity": "sha512-fQzRfAbIBnR0IQvftw9FJveWiHp72Fg20giDrHz6TdfB12UH/uue0D3hm57UB5KgAVuniLMCaS8P1IMj9NR7cA==", + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, - "node_modules/eslint-plugin-ngrx/node_modules/magic-string": { - "version": "0.25.7", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.7.tgz", - "integrity": "sha512-4CrMT5DOHTDk4HYDlzmwu4FVCcIYI8gauveasrdCu2IKIFOJ3f0v/8MDGJCDL9oD2ppz/Av1b0Nj345H9M+XIA==", + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", "dev": true, - "dependencies": { - "sourcemap-codec": "^1.4.4" - } + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] }, - "node_modules/eslint-plugin-ngrx/node_modules/rxjs": { - "version": "6.6.7", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", - "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", + "node_modules/basic-auth": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", + "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==", "dev": true, "dependencies": { - "tslib": "^1.9.0" + "safe-buffer": "5.1.2" }, "engines": { - "npm": ">=2.0.0" - } - }, - "node_modules/eslint-plugin-ngrx/node_modules/source-map": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", - "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", - "dev": true, - "engines": { - "node": ">= 8" + "node": ">= 0.8" } }, - "node_modules/eslint-plugin-ngrx/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "node_modules/batch": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", + "integrity": "sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==", "dev": true }, - "node_modules/eslint-plugin-prettier": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.1.3.tgz", - "integrity": "sha512-C9GCVAs4Eq7ZC/XFQHITLiHJxQngdtraXaM+LoUFoFp/lHNl2Zn8f3WQbe9HvTBBQ9YnKFB0/2Ajdqwo5D1EAw==", - "dev": true, - "dependencies": { - "prettier-linter-helpers": "^1.0.0", - "synckit": "^0.8.6" - }, - "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/bcryptjs": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-2.4.3.tgz", + "integrity": "sha512-V/Hy/X9Vt7f3BbPJEi8BdVFMByHi+jNXrYkW3huaybV/kQ0KJg0Y6PkEMbn+zeT+i+SiKZ/HMqJGIIt4LZDqNQ==", + "dev": true }, - "node_modules/eslint-plugin-react": { - "version": "7.33.2", - "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.33.2.tgz", - "integrity": "sha512-73QQMKALArI8/7xGLNI/3LylrEYrlKZSb5C9+q3OtOewTnMQi5cT+aE9E41sLCmli3I9PGGmD1yiZydyo4FEPw==", + "node_modules/big.js": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", "dev": true, "peer": true, - "dependencies": { - "array-includes": "^3.1.6", - "array.prototype.flatmap": "^1.3.1", - "array.prototype.tosorted": "^1.1.1", - "doctrine": "^2.1.0", - "es-iterator-helpers": "^1.0.12", - "estraverse": "^5.3.0", - "jsx-ast-utils": "^2.4.1 || ^3.0.0", - "minimatch": "^3.1.2", - "object.entries": "^1.1.6", - "object.fromentries": "^2.0.6", - "object.hasown": "^1.1.2", - "object.values": "^1.1.6", - "prop-types": "^15.8.1", - "resolve": "^2.0.0-next.4", - "semver": "^6.3.1", - "string.prototype.matchall": "^4.0.8" - }, "engines": { - "node": ">=4" - }, - "peerDependencies": { - "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8" + "node": "*" } }, - "node_modules/eslint-plugin-react-hooks": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.0.tgz", - "integrity": "sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==", + "node_modules/binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", "dev": true, - "peer": true, "engines": { - "node": ">=10" - }, - "peerDependencies": { - "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0" + "node": ">=8" } }, - "node_modules/eslint-plugin-react/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", "dev": true, - "peer": true, "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" } }, - "node_modules/eslint-plugin-react/node_modules/doctrine": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", - "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "node_modules/body-parser": { + "version": "1.20.2", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz", + "integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==", "dev": true, - "peer": true, "dependencies": { - "esutils": "^2.0.2" + "bytes": "3.1.2", + "content-type": "~1.0.5", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.11.0", + "raw-body": "2.5.2", + "type-is": "~1.6.18", + "unpipe": "1.0.0" }, "engines": { - "node": ">=0.10.0" + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" } }, - "node_modules/eslint-plugin-react/node_modules/minimatch": { + "node_modules/body-parser/node_modules/bytes": { "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", "dev": true, - "peer": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, "engines": { - "node": "*" + "node": ">= 0.8" } }, - "node_modules/eslint-plugin-react/node_modules/resolve": { - "version": "2.0.0-next.5", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.5.tgz", - "integrity": "sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==", + "node_modules/body-parser/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "dev": true, - "peer": true, "dependencies": { - "is-core-module": "^2.13.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" + "ms": "2.0.0" + } + }, + "node_modules/body-parser/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "node_modules/body-parser/node_modules/qs": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "dev": true, + "dependencies": { + "side-channel": "^1.0.4" }, - "bin": { - "resolve": "bin/resolve" + "engines": { + "node": ">=0.6" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/eslint-plugin-react/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "peer": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/eslint-plugin-security": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-security/-/eslint-plugin-security-2.1.1.tgz", - "integrity": "sha512-7cspIGj7WTfR3EhaILzAPcfCo5R9FbeWvbgsPYWivSurTBKW88VQxtP3c4aWMG9Hz/GfJlJVdXEJ3c8LqS+u2w==", + "node_modules/bonjour-service": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/bonjour-service/-/bonjour-service-1.2.1.tgz", + "integrity": "sha512-oSzCS2zV14bh2kji6vNe7vrpJYCHGvcZnlffFQ1MEoX/WOeQ/teD8SYWKR942OI3INjq8OMNJlbPK5LLLUxFDw==", "dev": true, "peer": true, "dependencies": { - "safe-regex": "^2.1.1" + "fast-deep-equal": "^3.1.3", + "multicast-dns": "^7.2.5" } }, - "node_modules/eslint-plugin-storybook": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-storybook/-/eslint-plugin-storybook-0.8.0.tgz", - "integrity": "sha512-CZeVO5EzmPY7qghO2t64oaFM+8FTaD4uzOEjHKp516exyTKo+skKAL9GI3QALS2BXhyALJjNtwbmr1XinGE8bA==", + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", + "dev": true + }, + "node_modules/bootstrap.native": { + "version": "5.0.11", + "resolved": "https://registry.npmjs.org/bootstrap.native/-/bootstrap.native-5.0.11.tgz", + "integrity": "sha512-bk2i4sQcQk2KuCTs1yygTa+JGjZOpKzIZ/It6TZZOO/Q+PmVGuKuIbrznXF64BUFxXaPNy7gO9LnE7vjGdauSQ==", "dev": true, "dependencies": { - "@storybook/csf": "^0.0.1", - "@typescript-eslint/utils": "^5.62.0", - "requireindex": "^1.2.0", - "ts-dedent": "^2.2.0" + "@thednp/event-listener": "^2.0.4", + "@thednp/shorty": "^2.0.0" }, "engines": { - "node": ">= 18" - }, - "peerDependencies": { - "eslint": ">=6" + "node": ">=16", + "pnpm": ">=8.6.0" } }, - "node_modules/eslint-plugin-storybook/node_modules/@storybook/csf": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/@storybook/csf/-/csf-0.0.1.tgz", - "integrity": "sha512-USTLkZze5gkel8MYCujSRBVIrUQ3YPBrLOx7GNk/0wttvVtlzWXAq9eLbQ4p/NicGxP+3T7KPEMVV//g+yubpw==", + "node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", "dev": true, "dependencies": { - "lodash": "^4.17.15" + "balanced-match": "^1.0.0" } }, - "node_modules/eslint-plugin-storybook/node_modules/@typescript-eslint/scope-manager": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.62.0.tgz", - "integrity": "sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w==", + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.62.0", - "@typescript-eslint/visitor-keys": "5.62.0" + "fill-range": "^7.0.1" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "node": ">=8" } }, - "node_modules/eslint-plugin-storybook/node_modules/@typescript-eslint/types": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.62.0.tgz", - "integrity": "sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==", + "node_modules/brfs": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brfs/-/brfs-2.0.2.tgz", + "integrity": "sha512-IrFjVtwu4eTJZyu8w/V2gxU7iLTtcHih67sgEdzrhjLBMHp2uYefUBfdM4k2UvcuWMgV7PQDZHSLeNWnLFKWVQ==", "dev": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "dependencies": { + "quote-stream": "^1.0.1", + "resolve": "^1.1.5", + "static-module": "^3.0.2", + "through2": "^2.0.0" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "bin": { + "brfs": "bin/cmd.js" } }, - "node_modules/eslint-plugin-storybook/node_modules/@typescript-eslint/typescript-estree": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz", - "integrity": "sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==", + "node_modules/brfs/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.62.0", - "@typescript-eslint/visitor-keys": "5.62.0", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "semver": "^7.3.7", - "tsutils": "^3.21.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" } }, - "node_modules/eslint-plugin-storybook/node_modules/@typescript-eslint/utils": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.62.0.tgz", - "integrity": "sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==", + "node_modules/brfs/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "dependencies": { - "@eslint-community/eslint-utils": "^4.2.0", - "@types/json-schema": "^7.0.9", - "@types/semver": "^7.3.12", - "@typescript-eslint/scope-manager": "5.62.0", - "@typescript-eslint/types": "5.62.0", - "@typescript-eslint/typescript-estree": "5.62.0", - "eslint-scope": "^5.1.1", - "semver": "^7.3.7" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + "safe-buffer": "~5.1.0" } }, - "node_modules/eslint-plugin-storybook/node_modules/@typescript-eslint/visitor-keys": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz", - "integrity": "sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==", + "node_modules/brfs/node_modules/through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.62.0", - "eslint-visitor-keys": "^3.3.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" } }, - "node_modules/eslint-plugin-storybook/node_modules/eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "node_modules/brotli": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/brotli/-/brotli-1.3.3.tgz", + "integrity": "sha512-oTKjJdShmDuGW94SyyaoQvAjf30dZaHnjJ8uAF+u2/vGJkJbJPJAT1gDiOJP5v1Zb6f9KEyW/1HpuaWIXtGHPg==", "dev": true, "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - }, - "engines": { - "node": ">=8.0.0" + "base64-js": "^1.1.2" } }, - "node_modules/eslint-plugin-storybook/node_modules/estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "node_modules/browser-resolve": { + "version": "1.11.3", + "resolved": "https://registry.npmjs.org/browser-resolve/-/browser-resolve-1.11.3.tgz", + "integrity": "sha512-exDi1BYWB/6raKHmDTCicQfTkqwN5fioMFV4j8BsfMU4R2DK/QfZfK7kOVkmWCNANf0snkBzqGqAJBao9gZMdQ==", "dev": true, - "engines": { - "node": ">=4.0" + "dependencies": { + "resolve": "1.1.7" } }, - "node_modules/eslint-scope": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.0.0.tgz", - "integrity": "sha512-zj3Byw6jX4TcFCJmxOzLt6iol5FAr9xQyZZSQjEzW2UiCJXLwXdRIKCYVFftnpZckaC9Ps9xlC7jB8tSeWWOaw==", + "node_modules/browser-resolve/node_modules/resolve": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", + "integrity": "sha512-9znBF0vBcaSN3W2j7wKvdERPwqTxSpCq+if5C0WoTCyV9n24rua28jeuQ2pL/HOf+yUe/Mef+H/5p60K0Id3bg==", + "dev": true + }, + "node_modules/browserify-optional": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/browserify-optional/-/browserify-optional-1.0.1.tgz", + "integrity": "sha512-VrhjbZ+Ba5mDiSYEuPelekQMfTbhcA2DhLk2VQWqdcCROWeFqlTcXZ7yfRkXCIl8E+g4gINJYJiRB7WEtfomAQ==", "dev": true, "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" + "ast-transform": "0.0.0", + "ast-types": "^0.7.0", + "browser-resolve": "^1.8.1" } }, - "node_modules/eslint/node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "node_modules/browserslist": { + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.3.tgz", + "integrity": "sha512-btwCFJVjI4YWDNfau8RhZ+B1Q/VLoUITrm3RlP6y1tYGWIOa+InuYiRGXUBXo8nA1qKmHMyLB/iVQg5TT4eFoA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" + "caniuse-lite": "^1.0.30001646", + "electron-to-chromium": "^1.5.4", + "node-releases": "^2.0.18", + "update-browserslist-db": "^1.1.0" }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, - "node_modules/eslint/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/bs-logger": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz", + "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==", + "dev": true, "dependencies": { - "color-convert": "^2.0.1" + "fast-json-stable-stringify": "2.x" }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "node": ">= 6" } }, - "node_modules/eslint/node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" - }, - "node_modules/eslint/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "node_modules/bser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", + "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", + "dev": true, "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "node-int64": "^0.4.0" } }, - "node_modules/eslint/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/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/buffer-equal": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal/-/buffer-equal-0.0.1.tgz", + "integrity": "sha512-RgSV6InVQ9ODPdLWJ5UAqBqJBOg370Nz6ZQtRzpt6nUjc8v0St97uJ4PYC6NztqIScrAXafKM3mZPMygSe1ggA==", + "dev": true, + "engines": { + "node": ">=0.4.0" } }, - "node_modules/eslint/node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true + }, + "node_modules/bundle-name": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-4.1.0.tgz", + "integrity": "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==", + "dev": true, + "peer": true, + "dependencies": { + "run-applescript": "^7.0.0" + }, "engines": { - "node": ">=10" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/eslint/node_modules/eslint-scope": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", - "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - }, + "node_modules/bytes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==", + "dev": true, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" + "node": ">= 0.8" } }, - "node_modules/eslint/node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "node_modules/cacache": { + "version": "18.0.4", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-18.0.4.tgz", + "integrity": "sha512-B+L5iIa9mgcjLbliir2th36yEwPftrzteHYujzsx3dFP/31GCHcIeS8f5MGd80odLOjaOvSpU3EEAmRQptkxLQ==", + "dev": true, "dependencies": { - "is-glob": "^4.0.3" + "@npmcli/fs": "^3.1.0", + "fs-minipass": "^3.0.0", + "glob": "^10.2.2", + "lru-cache": "^10.0.1", + "minipass": "^7.0.3", + "minipass-collect": "^2.0.1", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "p-map": "^4.0.0", + "ssri": "^10.0.0", + "tar": "^6.1.11", + "unique-filename": "^3.0.0" }, "engines": { - "node": ">=10.13.0" + "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/eslint/node_modules/globals": { - "version": "13.24.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", - "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "node_modules/cacache/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true + }, + "node_modules/call-bind": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", + "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", "dependencies": { - "type-fest": "^0.20.2" + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.1" }, "engines": { - "node": ">=8" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/eslint/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "node_modules/callsite": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/callsite/-/callsite-1.0.0.tgz", + "integrity": "sha512-0vdNRFXn5q+dtOqjfFtmtlI9N2eVZ7LMyEV2iKC5mEEFvSg/69Ml6b/WU2qF8W1nLRa0wiSrDT3Y5jOHZCwKPQ==", + "dev": true, "engines": { - "node": ">=8" + "node": "*" } }, - "node_modules/eslint/node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "engines": { + "node": ">=6" } }, - "node_modules/eslint/node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" - }, - "node_modules/eslint/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "node_modules/camel-case": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz", + "integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==", + "dev": true, + "optional": true, + "peer": true, "dependencies": { - "brace-expansion": "^1.1.7" - }, + "pascal-case": "^3.1.2", + "tslib": "^2.0.3" + } + }, + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, "engines": { - "node": "*" + "node": ">=6" } }, - "node_modules/eslint/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "node_modules/camelcase-keys": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-6.2.2.tgz", + "integrity": "sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==", + "dev": true, "dependencies": { - "has-flag": "^4.0.0" + "camelcase": "^5.3.1", + "map-obj": "^4.0.0", + "quick-lru": "^4.0.1" }, "engines": { "node": ">=8" - } - }, - "node_modules/eslint/node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "engines": { - "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/esniff": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/esniff/-/esniff-2.0.1.tgz", - "integrity": "sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg==", + "node_modules/caniuse-lite": { + "version": "1.0.30001651", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001651.tgz", + "integrity": "sha512-9Cf+Xv1jJNe1xPZLGuUXLNkE1BoDkqRqYyFJ9TDYSqhduqA4hu4oR9HluGoWYQC/aj8WHjsGVV+bwkh0+tegRg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ] + }, + "node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", "dev": true, "dependencies": { - "d": "^1.0.1", - "es5-ext": "^0.10.62", - "event-emitter": "^0.3.5", - "type": "^2.7.2" + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" }, "engines": { - "node": ">=0.10" + "node": ">=4" } }, - "node_modules/esniff/node_modules/type": { - "version": "2.7.2", - "resolved": "https://registry.npmjs.org/type/-/type-2.7.2.tgz", - "integrity": "sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw==", + "node_modules/char-regex": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", + "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/chardet": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", "dev": true }, - "node_modules/espree": { - "version": "9.6.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", - "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", - "dependencies": { - "acorn": "^8.9.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.4.1" - }, + "node_modules/charenc": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz", + "integrity": "sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==", "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" + "node": "*" } }, - "node_modules/esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "node_modules/cheerio": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.12.tgz", + "integrity": "sha512-VqR8m68vM46BNnuZ5NtnGBKIE/DfN0cRIzg9n40EIq9NOv90ayxLBXA8fXC5gquFRGJSTRqBq25Jt2ECLR431Q==", "dev": true, - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" + "dependencies": { + "cheerio-select": "^2.1.0", + "dom-serializer": "^2.0.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1", + "htmlparser2": "^8.0.1", + "parse5": "^7.0.0", + "parse5-htmlparser2-tree-adapter": "^7.0.0" }, "engines": { - "node": ">=4" + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/cheeriojs/cheerio?sponsor=1" } }, - "node_modules/esquery": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", - "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", + "node_modules/cheerio-select": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cheerio-select/-/cheerio-select-2.1.0.tgz", + "integrity": "sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==", + "dev": true, "dependencies": { - "estraverse": "^5.1.0" + "boolbase": "^1.0.0", + "css-select": "^5.1.0", + "css-what": "^6.1.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1" }, - "engines": { - "node": ">=0.10" + "funding": { + "url": "https://github.com/sponsors/fb55" } }, - "node_modules/esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, "dependencies": { - "estraverse": "^5.2.0" + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" }, "engines": { - "node": ">=4.0" + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" } }, - "node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "node_modules/chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "dev": true, "engines": { - "node": ">=4.0" + "node": ">=10" } }, - "node_modules/estree-is-function": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/estree-is-function/-/estree-is-function-1.0.0.tgz", - "integrity": "sha512-nSCWn1jkSq2QAtkaVLJZY2ezwcFO161HVc174zL1KPW3RJ+O6C3eJb8Nx7OXzvhoEv+nLgSR1g71oWUHUDTrJA==", - "dev": true - }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "node_modules/chrome-trace-event": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", + "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==", + "dev": true, + "peer": true, "engines": { - "node": ">=0.10.0" + "node": ">=6.0" } }, - "node_modules/etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "node_modules/ci-info": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", + "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], "engines": { - "node": ">= 0.6" + "node": ">=8" } }, - "node_modules/event-emitter": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", - "integrity": "sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==", + "node_modules/cjs-module-lexer": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.3.tgz", + "integrity": "sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ==", + "dev": true + }, + "node_modules/clean-css": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.3.tgz", + "integrity": "sha512-D5J+kHaVb/wKSFcyyV75uCn8fiY4sV38XJoe4CUyGQ+mOU/fMVYUdH1hJC+CJQ5uY3EnW27SbJYS4X8BiLrAFg==", "dev": true, + "optional": true, + "peer": true, "dependencies": { - "d": "1", - "es5-ext": "~0.10.14" + "source-map": "~0.6.0" + }, + "engines": { + "node": ">= 10.0" } }, - "node_modules/event-stream": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/event-stream/-/event-stream-4.0.1.tgz", - "integrity": "sha512-qACXdu/9VHPBzcyhdOWR5/IahhGMf0roTeZJfzz077GwylcDd90yOHLouhmv7GJ5XzPi6ekaQWd8AvPP2nOvpA==", + "node_modules/clean-css/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true, - "dependencies": { - "duplexer": "^0.1.1", - "from": "^0.1.7", - "map-stream": "0.0.7", - "pause-stream": "^0.0.11", - "split": "^1.0.1", - "stream-combiner": "^0.2.2", - "through": "^2.3.8" + "optional": true, + "peer": true, + "engines": { + "node": ">=0.10.0" } }, - "node_modules/eventemitter2": { - "version": "6.4.7", - "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-6.4.7.tgz", - "integrity": "sha512-tYUSVOGeQPKt/eC1ABfhHy5Xd96N3oIijJvN3O9+TsC28T5V9yX9oEfEK5faP0EFSNVOG97qtAS68GBrQB2hDg==", - "dev": true - }, - "node_modules/eventemitter3": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", - "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", - "dev": true - }, - "node_modules/events": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", - "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "node_modules/clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", "dev": true, "engines": { - "node": ">=0.8.x" + "node": ">=6" } }, - "node_modules/execa": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "node_modules/cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", "dev": true, "dependencies": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.0", - "human-signals": "^2.1.0", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.1", - "onetime": "^5.1.2", - "signal-exit": "^3.0.3", - "strip-final-newline": "^2.0.0" + "restore-cursor": "^3.1.0" }, "engines": { - "node": ">=10" + "node": ">=8" + } + }, + "node_modules/cli-spinners": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", + "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", + "dev": true, + "engines": { + "node": ">=6" }, "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/executable": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/executable/-/executable-4.1.1.tgz", - "integrity": "sha512-8iA79xD3uAch729dUG8xaaBBFGaEa0wdD2VkYLFHwlqosEj/jT66AzcreRDSgV7ehnNLBW2WR5jIXwGKjVdTLg==", + "node_modules/cli-truncate": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-4.0.0.tgz", + "integrity": "sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA==", "dev": true, "dependencies": { - "pify": "^2.2.0" + "slice-ansi": "^5.0.0", + "string-width": "^7.0.0" }, "engines": { - "node": ">=4" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/exit": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", - "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", + "node_modules/cli-truncate/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", "dev": true, "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/expect": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz", - "integrity": "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==", - "dev": true, - "dependencies": { - "@jest/expect-utils": "^29.7.0", - "jest-get-type": "^29.6.3", - "jest-matcher-utils": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0" + "node": ">=12" }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, - "node_modules/exponential-backoff": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/exponential-backoff/-/exponential-backoff-3.1.1.tgz", - "integrity": "sha512-dX7e/LHVJ6W3DE1MHWi9S1EYzDESENfLrYohG2G++ovZrYOkm4Knwa0mc1cn84xJOR4KEU0WSchhLbd0UklbHw==", + "node_modules/cli-truncate/node_modules/emoji-regex": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.3.0.tgz", + "integrity": "sha512-QpLs9D9v9kArv4lfDEgg1X/gN5XLnf/A6l9cs8SPZLRZR3ZkY9+kwIQTxm+fsSej5UMYGE8fdoaZVIBlqG0XTw==", "dev": true }, - "node_modules/express": { - "version": "4.18.3", - "resolved": "https://registry.npmjs.org/express/-/express-4.18.3.tgz", - "integrity": "sha512-6VyCijWQ+9O7WuVMTRBTl+cjNNIzD5cY5mQ1WM8r/LEkI2u8EYpOotESNwzNlyCn3g+dmjKYI6BmNneSr/FSRw==", + "node_modules/cli-truncate/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", "dev": true, "dependencies": { - "accepts": "~1.3.8", - "array-flatten": "1.1.1", - "body-parser": "1.20.2", - "content-disposition": "0.5.4", - "content-type": "~1.0.4", - "cookie": "0.5.0", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "2.0.0", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "1.2.0", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "merge-descriptors": "1.0.1", - "methods": "~1.1.2", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "path-to-regexp": "0.1.7", - "proxy-addr": "~2.0.7", - "qs": "6.11.0", - "range-parser": "~1.2.1", - "safe-buffer": "5.2.1", - "send": "0.18.0", - "serve-static": "1.15.0", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "type-is": "~1.6.18", - "utils-merge": "1.0.1", - "vary": "~1.1.2" + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" }, "engines": { - "node": ">= 0.10.0" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/express-urlrewrite": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/express-urlrewrite/-/express-urlrewrite-1.4.0.tgz", - "integrity": "sha512-PI5h8JuzoweS26vFizwQl6UTF25CAHSggNv0J25Dn/IKZscJHWZzPrI5z2Y2jgOzIaw2qh8l6+/jUcig23Z2SA==", + "node_modules/cli-truncate/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", "dev": true, "dependencies": { - "debug": "*", - "path-to-regexp": "^1.0.3" + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, - "node_modules/express-urlrewrite/node_modules/isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==", - "dev": true - }, - "node_modules/express-urlrewrite/node_modules/path-to-regexp": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz", - "integrity": "sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==", + "node_modules/cli-width": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz", + "integrity": "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==", "dev": true, - "dependencies": { - "isarray": "0.0.1" + "engines": { + "node": ">= 12" } }, - "node_modules/express/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", "dev": true, "dependencies": { - "ms": "2.0.0" + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" } }, - "node_modules/express/node_modules/finalhandler": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", - "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", + "node_modules/cliui/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, "dependencies": { - "debug": "2.6.9", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "statuses": "2.0.1", - "unpipe": "~1.0.0" + "color-convert": "^2.0.1" }, "engines": { - "node": ">= 0.8" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/express/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true - }, - "node_modules/express/node_modules/qs": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", - "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "node_modules/cliui/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dev": true, "dependencies": { - "side-channel": "^1.0.4" + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" }, "engines": { - "node": ">=0.6" + "node": ">=10" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/express/node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] + "node_modules/clone": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", + "integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==", + "engines": { + "node": ">=0.8" + } }, - "node_modules/ext": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/ext/-/ext-1.7.0.tgz", - "integrity": "sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==", + "node_modules/clone-deep": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", + "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", "dev": true, + "peer": true, "dependencies": { - "type": "^2.7.2" + "is-plain-object": "^2.0.4", + "kind-of": "^6.0.2", + "shallow-clone": "^3.0.0" + }, + "engines": { + "node": ">=6" } }, - "node_modules/ext/node_modules/type": { - "version": "2.7.2", - "resolved": "https://registry.npmjs.org/type/-/type-2.7.2.tgz", - "integrity": "sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw==", + "node_modules/co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", + "dev": true, + "engines": { + "iojs": ">= 1.0.0", + "node": ">= 0.12.0" + } + }, + "node_modules/code-block-writer": { + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/code-block-writer/-/code-block-writer-12.0.0.tgz", + "integrity": "sha512-q4dMFMlXtKR3XNBHyMHt/3pwYNA69EDk00lloMOaaUMKPUXBw6lpXtbu3MMVG6/uOihGnRDOlkyqsONEUj60+w==", "dev": true }, - "node_modules/extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "node_modules/collect-v8-coverage": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz", + "integrity": "sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==", "dev": true }, - "node_modules/external-editor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", - "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, "dependencies": { - "chardet": "^0.7.0", - "iconv-lite": "^0.4.24", - "tmp": "^0.0.33" + "color-name": "~1.1.4" }, "engines": { - "node": ">=4" + "node": ">=7.0.0" } }, - "node_modules/external-editor/node_modules/tmp": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", - "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/color-support": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", "dev": true, - "dependencies": { - "os-tmpdir": "~1.0.2" - }, - "engines": { - "node": ">=0.6.0" + "bin": { + "color-support": "bin.js" } }, - "node_modules/extract-zip": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", - "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", + "node_modules/colorette": { + "version": "2.0.20", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", + "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", + "dev": true + }, + "node_modules/colors": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", + "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", "dev": true, - "dependencies": { - "debug": "^4.1.1", - "get-stream": "^5.1.0", - "yauzl": "^2.10.0" - }, - "bin": { - "extract-zip": "cli.js" - }, "engines": { - "node": ">= 10.17.0" - }, - "optionalDependencies": { - "@types/yauzl": "^2.9.1" + "node": ">=0.1.90" } }, - "node_modules/extract-zip/node_modules/get-stream": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", - "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", "dev": true, "dependencies": { - "pump": "^3.0.0" + "delayed-stream": "~1.0.0" }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">= 0.8" } }, - "node_modules/extsprintf": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==", + "node_modules/commander": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz", + "integrity": "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==", "dev": true, - "engines": [ - "node >=0.6.0" - ] + "engines": { + "node": ">=16" + } }, - "node_modules/fancy-log": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/fancy-log/-/fancy-log-2.0.0.tgz", - "integrity": "sha512-9CzxZbACXMUXW13tS0tI8XsGGmxWzO2DmYrGuBJOJ8k8q2K7hwfJA5qHjuPPe8wtsco33YR9wc+Rlr5wYFvhSA==", + "node_modules/commitlint-plugin-function-rules": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/commitlint-plugin-function-rules/-/commitlint-plugin-function-rules-3.1.0.tgz", + "integrity": "sha512-K44912/g7ZZ0bEawrsJTn3giDEwDn6T18g/UcimUblv8hcSIoIxMX5uk6LY+uYO9cb/+3Suilbp7XoxP53Nk9g==", "dev": true, - "dependencies": { - "color-support": "^1.1.3" - }, "engines": { - "node": ">=10.13.0" + "node": ">=18" + }, + "peerDependencies": { + "@commitlint/lint": ">=9.1.2 <19" } }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" - }, - "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==", + "node_modules/common-path-prefix": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/common-path-prefix/-/common-path-prefix-3.0.0.tgz", + "integrity": "sha512-QE33hToZseCH3jS0qN96O/bSh3kaw/h+Tq7ngyY9eWDUnTlTNUyqfqvCXioLe5Na5jFsL78ra/wuBU4iuEgd4w==", "dev": true }, - "node_modules/fast-glob": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", - "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "node_modules/compare-func": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/compare-func/-/compare-func-2.0.0.tgz", + "integrity": "sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA==", + "dev": true, "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.4" - }, - "engines": { - "node": ">=8.6.0" + "array-ify": "^1.0.0", + "dot-prop": "^5.1.0" } }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" - }, - "node_modules/fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==" + "node_modules/complexion": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/complexion/-/complexion-0.2.2.tgz", + "integrity": "sha512-YAfDsYc27CsPQPzZU4i1OklMCyigN7VHPylWba0AWf5VmVr34NpLhHjJm0gumFh9m2aX8fYGCTTgne7oLfXXug==" }, - "node_modules/fastq": { - "version": "1.17.1", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", - "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", - "dependencies": { - "reusify": "^1.0.4" + "node_modules/complexion-js": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/complexion-js/-/complexion-js-0.2.2.tgz", + "integrity": "sha512-qxqJq0nEBwSEZtoqK8bHIR2VwzbPatjtlU+ZH9IPObitTQqa2xMEryoAlCsjOH+ATgIScHt0aiCUBabPG2Mmcg==", + "peerDependencies": { + "complexion": "^0.2.2" } }, - "node_modules/faye-websocket": { - "version": "0.11.4", - "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz", - "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==", + "node_modules/compressible": { + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", + "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", "dev": true, "dependencies": { - "websocket-driver": ">=0.5.1" + "mime-db": ">= 1.43.0 < 2" }, "engines": { - "node": ">=0.8.0" + "node": ">= 0.6" } }, - "node_modules/fb-watchman": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", - "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", + "node_modules/compression": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz", + "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==", "dev": true, "dependencies": { - "bser": "2.1.1" + "accepts": "~1.3.5", + "bytes": "3.0.0", + "compressible": "~2.0.16", + "debug": "2.6.9", + "on-headers": "~1.0.2", + "safe-buffer": "5.1.2", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.8.0" } }, - "node_modules/fd-slicer": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", - "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", + "node_modules/compression/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "dev": true, "dependencies": { - "pend": "~1.2.0" + "ms": "2.0.0" } }, - "node_modules/fetch-retry": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/fetch-retry/-/fetch-retry-5.0.6.tgz", - "integrity": "sha512-3yurQZ2hD9VISAhJJP9bpYFNQrHHBXE2JxxjY5aLEcDi46RmAzJE2OC9FAde0yis5ElW0jTTzs0zfg/Cca4XqQ==", + "node_modules/compression/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "dev": true }, - "node_modules/figures": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", - "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", - "dev": true, - "dependencies": { - "escape-string-regexp": "^1.0.5" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/file-entry-cache": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", - "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", - "dependencies": { - "flat-cache": "^3.0.4" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - } + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true }, - "node_modules/file-system-cache": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/file-system-cache/-/file-system-cache-2.3.0.tgz", - "integrity": "sha512-l4DMNdsIPsVnKrgEXbJwDJsA5mB8rGwHYERMgqQx/xAUtChPJMre1bXBzDEqqVbWv9AIbFezXMxeEkZDSrXUOQ==", + "node_modules/concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", "dev": true, + "engines": [ + "node >= 0.8" + ], "dependencies": { - "fs-extra": "11.1.1", - "ramda": "0.29.0" + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" } }, - "node_modules/file-system-cache/node_modules/fs-extra": { - "version": "11.1.1", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.1.1.tgz", - "integrity": "sha512-MGIE4HOvQCeUCzmlHs0vXpih4ysz4wg9qiSAu6cd42lVwPbTM1TjV7RusoyQqMmk/95gdQZX72u+YW+c3eEpFQ==", + "node_modules/concat-stream/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", "dev": true, "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=14.14" + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" } }, - "node_modules/filelist": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", - "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", + "node_modules/concat-stream/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "dependencies": { - "minimatch": "^5.0.1" + "safe-buffer": "~5.1.0" } }, - "node_modules/filelist/node_modules/minimatch": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", - "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "node_modules/connect": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/connect/-/connect-3.7.0.tgz", + "integrity": "sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ==", "dev": true, "dependencies": { - "brace-expansion": "^2.0.1" + "debug": "2.6.9", + "finalhandler": "1.1.2", + "parseurl": "~1.3.3", + "utils-merge": "1.0.1" }, "engines": { - "node": ">=10" + "node": ">= 0.10.0" } }, - "node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dependencies": { - "to-regex-range": "^5.0.1" - }, + "node_modules/connect-history-api-fallback": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz", + "integrity": "sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA==", + "dev": true, + "peer": true, "engines": { - "node": ">=8" + "node": ">=0.8" } }, - "node_modules/finalhandler": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", - "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "node_modules/connect-pause": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/connect-pause/-/connect-pause-0.1.1.tgz", + "integrity": "sha512-a1gSWQBQD73krFXdUEYJom2RTFrWUL3YvXDCRkyv//GVXc79cdW9MngtRuN9ih4FDKBtfJAJId+BbDuX+1rh2w==", "dev": true, - "dependencies": { - "debug": "2.6.9", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "on-finished": "~2.3.0", - "parseurl": "~1.3.3", - "statuses": "~1.5.0", - "unpipe": "~1.0.0" - }, "engines": { - "node": ">= 0.8" + "node": "*" } }, - "node_modules/finalhandler/node_modules/debug": { + "node_modules/connect/node_modules/debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", @@ -17820,284 +9305,282 @@ "ms": "2.0.0" } }, - "node_modules/finalhandler/node_modules/ms": { + "node_modules/connect/node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "dev": true }, - "node_modules/finalhandler/node_modules/on-finished": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", - "integrity": "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==", + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", "dev": true, "dependencies": { - "ee-first": "1.1.1" + "safe-buffer": "5.2.1" }, "engines": { - "node": ">= 0.8" + "node": ">= 0.6" } }, - "node_modules/finalhandler/node_modules/statuses": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", - "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", + "node_modules/content-disposition/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", "dev": true, "engines": { "node": ">= 0.6" } }, - "node_modules/find-cache-dir": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz", - "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==", + "node_modules/conventional-changelog-angular": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-7.0.0.tgz", + "integrity": "sha512-ROjNchA9LgfNMTTFSIWPzebCwOGFdgkEq45EnvvrmSLvCtAw0HSmrCs7/ty+wAeYUZyNay0YMUNYFTRL72PkBQ==", "dev": true, "dependencies": { - "commondir": "^1.0.1", - "make-dir": "^3.0.2", - "pkg-dir": "^4.1.0" + "compare-func": "^2.0.0" }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/avajs/find-cache-dir?sponsor=1" + "node": ">=16" } }, - "node_modules/find-cache-dir/node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "node_modules/conventional-changelog-conventionalcommits": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/conventional-changelog-conventionalcommits/-/conventional-changelog-conventionalcommits-7.0.2.tgz", + "integrity": "sha512-NKXYmMR/Hr1DevQegFB4MwfM5Vv0m4UIxKZTTYuD98lpTknaZlSRrDOG4X7wIXpGkfsYxZTghUN+Qq+T0YQI7w==", "dev": true, "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" + "compare-func": "^2.0.0" }, "engines": { - "node": ">=8" + "node": ">=16" } }, - "node_modules/find-cache-dir/node_modules/locate-path": { + "node_modules/conventional-commits-parser": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-5.0.0.tgz", + "integrity": "sha512-ZPMl0ZJbw74iS9LuX9YIAiW8pfM5p3yh2o/NbXHbkFuZzY5jvdi5jFycEOkmBW5H5I7nA+D6f3UcsCLP2vvSEA==", "dev": true, "dependencies": { - "p-locate": "^4.1.0" + "is-text-path": "^2.0.0", + "JSONStream": "^1.3.5", + "meow": "^12.0.1", + "split2": "^4.0.0" }, - "engines": { - "node": ">=8" - } - }, - "node_modules/find-cache-dir/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "dependencies": { - "p-try": "^2.0.0" + "bin": { + "conventional-commits-parser": "cli.mjs" }, "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=16" } }, - "node_modules/find-cache-dir/node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "node_modules/convert-source-map": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", + "dev": true + }, + "node_modules/cookie": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", + "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", "dev": true, - "dependencies": { - "p-limit": "^2.2.0" - }, "engines": { - "node": ">=8" + "node": ">= 0.6" } }, - "node_modules/find-cache-dir/node_modules/pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", + "dev": true + }, + "node_modules/copy-anything": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-2.0.6.tgz", + "integrity": "sha512-1j20GZTsvKNkc4BY3NpMOM8tt///wY3FpIzozTOFO2ffuZcV61nojHXVKIy3WM+7ADCy5FVhdZYHYDdgTU0yJw==", "dev": true, + "peer": true, "dependencies": { - "find-up": "^4.0.0" + "is-what": "^3.14.1" }, - "engines": { - "node": ">=8" + "funding": { + "url": "https://github.com/sponsors/mesqueeb" } }, - "node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "node_modules/copy-webpack-plugin": { + "version": "12.0.2", + "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-12.0.2.tgz", + "integrity": "sha512-SNwdBeHyII+rWvee/bTnAYyO8vfVdcSTud4EIb6jcZ8inLeWucJE0DnxXQBjlQ5zlteuuvooGQy3LIyGxhvlOA==", + "dev": true, + "peer": true, "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" + "fast-glob": "^3.3.2", + "glob-parent": "^6.0.1", + "globby": "^14.0.0", + "normalize-path": "^3.0.0", + "schema-utils": "^4.2.0", + "serialize-javascript": "^6.0.2" }, "engines": { - "node": ">=10" + "node": ">= 18.12.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.1.0" } }, - "node_modules/flat": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", - "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "node_modules/copy-webpack-plugin/node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", "dev": true, - "bin": { - "flat": "cli.js" - } - }, - "node_modules/flat-cache": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", - "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", + "peer": true, "dependencies": { - "flatted": "^3.2.9", - "keyv": "^4.5.3", - "rimraf": "^3.0.2" + "is-glob": "^4.0.3" }, "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "node_modules/flatted": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", - "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==" - }, - "node_modules/flow-parser": { - "version": "0.229.2", - "resolved": "https://registry.npmjs.org/flow-parser/-/flow-parser-0.229.2.tgz", - "integrity": "sha512-T72XV2Izvl7yV6dhHhLaJ630Y6vOZJl6dnOS6dN0bPW9ExuREu7xGAf3omtcxX76POTuux9TJPu9ZpS48a/rdw==", - "dev": true, - "engines": { - "node": ">=0.4.0" + "node": ">=10.13.0" } }, - "node_modules/follow-redirects": { - "version": "1.15.5", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.5.tgz", - "integrity": "sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw==", + "node_modules/core-js-compat": { + "version": "3.38.0", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.38.0.tgz", + "integrity": "sha512-75LAicdLa4OJVwFxFbQR3NdnZjNgX6ILpVcVzcC4T2smerB5lELMrJQQQoWV6TiuC/vlaFqgU2tKQx9w5s0e0A==", "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/RubenVerborgh" - } - ], - "engines": { - "node": ">=4.0" + "dependencies": { + "browserslist": "^4.23.3" }, - "peerDependenciesMeta": { - "debug": { - "optional": true - } + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" } }, - "node_modules/for-each": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", - "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", - "dependencies": { - "is-callable": "^1.1.3" - } + "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 }, - "node_modules/foreground-child": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz", - "integrity": "sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==", + "node_modules/cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", "dev": true, "dependencies": { - "cross-spawn": "^7.0.0", - "signal-exit": "^4.0.1" + "object-assign": "^4", + "vary": "^1" }, "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": ">= 0.10" } }, - "node_modules/foreground-child/node_modules/signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "node_modules/cosmiconfig": { + "version": "8.3.6", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.3.6.tgz", + "integrity": "sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==", "dev": true, + "dependencies": { + "import-fresh": "^3.3.0", + "js-yaml": "^4.1.0", + "parse-json": "^5.2.0", + "path-type": "^4.0.0" + }, "engines": { "node": ">=14" }, "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/forever-agent": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==", - "dev": true, - "engines": { - "node": "*" + "url": "https://github.com/sponsors/d-fischer" + }, + "peerDependencies": { + "typescript": ">=4.9.5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/fork-ts-checker-webpack-plugin": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-8.0.0.tgz", - "integrity": "sha512-mX3qW3idpueT2klaQXBzrIM/pHw+T0B/V9KHEvNrqijTq9NFnMZU6oreVxDYcf33P8a5cW+67PjodNHthGnNVg==", + "node_modules/cosmiconfig-typescript-loader": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig-typescript-loader/-/cosmiconfig-typescript-loader-5.0.0.tgz", + "integrity": "sha512-+8cK7jRAReYkMwMiG+bxhcNKiHJDM6bR9FD/nGBXOWdMLuYawjF5cGrtLilJ+LGd3ZjCXnJjR5DkfWPoIVlqJA==", "dev": true, "dependencies": { - "@babel/code-frame": "^7.16.7", - "chalk": "^4.1.2", - "chokidar": "^3.5.3", - "cosmiconfig": "^7.0.1", - "deepmerge": "^4.2.2", - "fs-extra": "^10.0.0", - "memfs": "^3.4.1", - "minimatch": "^3.0.4", - "node-abort-controller": "^3.0.1", - "schema-utils": "^3.1.1", - "semver": "^7.3.5", - "tapable": "^2.2.1" + "jiti": "^1.19.1" }, "engines": { - "node": ">=12.13.0", - "yarn": ">=1.0.0" + "node": ">=v16" }, "peerDependencies": { - "typescript": ">3.6.0", - "webpack": "^5.11.0" + "@types/node": "*", + "cosmiconfig": ">=8.2", + "typescript": ">=4" } }, - "node_modules/fork-ts-checker-webpack-plugin/node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "node_modules/cosmiconfig/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/cosmiconfig/node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "dev": true, "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" + "argparse": "^2.0.1" }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" + "bin": { + "js-yaml": "bin/js-yaml.js" } }, - "node_modules/fork-ts-checker-webpack-plugin/node_modules/ajv-keywords": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "node_modules/create-jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz", + "integrity": "sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==", "dev": true, - "peerDependencies": { - "ajv": "^6.9.1" + "dependencies": { + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-config": "^29.7.0", + "jest-util": "^29.7.0", + "prompts": "^2.0.1" + }, + "bin": { + "create-jest": "bin/create-jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/fork-ts-checker-webpack-plugin/node_modules/ansi-styles": { + "node_modules/create-jest/node_modules/ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", @@ -18112,17 +9595,7 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/fork-ts-checker-webpack-plugin/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/fork-ts-checker-webpack-plugin/node_modules/chalk": { + "node_modules/create-jest/node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", @@ -18138,82 +9611,89 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/fork-ts-checker-webpack-plugin/node_modules/cosmiconfig": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", - "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==", + "node_modules/create-jest/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, - "dependencies": { - "@types/parse-json": "^4.0.0", - "import-fresh": "^3.2.1", - "parse-json": "^5.0.0", - "path-type": "^4.0.0", - "yaml": "^1.10.0" - }, "engines": { - "node": ">=10" + "node": ">=8" } }, - "node_modules/fork-ts-checker-webpack-plugin/node_modules/fs-extra": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", - "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "node_modules/create-jest/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" + "has-flag": "^4.0.0" }, - "engines": { - "node": ">=12" - } - }, - "node_modules/fork-ts-checker-webpack-plugin/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, "engines": { "node": ">=8" } }, - "node_modules/fork-ts-checker-webpack-plugin/node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", "dev": true }, - "node_modules/fork-ts-checker-webpack-plugin/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "node_modules/critters": { + "version": "0.0.24", + "resolved": "https://registry.npmjs.org/critters/-/critters-0.0.24.tgz", + "integrity": "sha512-Oyqew0FGM0wYUSNqR0L6AteO5MpMoUU0rhKRieXeiKs+PmRTxiJMyaunYB2KF6fQ3dzChXKCpbFOEJx3OQ1v/Q==", + "dev": true, + "dependencies": { + "chalk": "^4.1.0", + "css-select": "^5.1.0", + "dom-serializer": "^2.0.0", + "domhandler": "^5.0.2", + "htmlparser2": "^8.0.2", + "postcss": "^8.4.23", + "postcss-media-query-parser": "^0.2.3" + } + }, + "node_modules/critters/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, "dependencies": { - "brace-expansion": "^1.1.7" + "color-convert": "^2.0.1" }, "engines": { - "node": "*" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/fork-ts-checker-webpack-plugin/node_modules/schema-utils": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", - "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", + "node_modules/critters/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, "dependencies": { - "@types/json-schema": "^7.0.8", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" }, "engines": { - "node": ">= 10.13.0" + "node": ">=10" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/critters/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" } }, - "node_modules/fork-ts-checker-webpack-plugin/node_modules/supports-color": { + "node_modules/critters/node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", @@ -18225,248 +9705,271 @@ "node": ">=8" } }, - "node_modules/form-data": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", - "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", "dev": true, "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" }, "engines": { - "node": ">= 6" + "node": ">= 8" } }, - "node_modules/forwarded": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", - "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", - "dev": true, + "node_modules/crypt": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz", + "integrity": "sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==", "engines": { - "node": ">= 0.6" + "node": "*" } }, - "node_modules/fraction.js": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", - "integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==", + "node_modules/crypto-js": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz", + "integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==", + "dev": true + }, + "node_modules/css-loader": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-7.1.2.tgz", + "integrity": "sha512-6WvYYn7l/XEGN8Xu2vWFt9nVzrCn39vKyTEFf/ExEyoksJjjSZV/0/35XPlMbpnr6VGhZIUg5yJrL8tGfes/FA==", "dev": true, + "peer": true, + "dependencies": { + "icss-utils": "^5.1.0", + "postcss": "^8.4.33", + "postcss-modules-extract-imports": "^3.1.0", + "postcss-modules-local-by-default": "^4.0.5", + "postcss-modules-scope": "^3.2.0", + "postcss-modules-values": "^4.0.0", + "postcss-value-parser": "^4.2.0", + "semver": "^7.5.4" + }, "engines": { - "node": "*" + "node": ">= 18.12.0" }, "funding": { - "type": "patreon", - "url": "https://github.com/sponsors/rawify" + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "@rspack/core": "0.x || 1.x", + "webpack": "^5.27.0" + }, + "peerDependenciesMeta": { + "@rspack/core": { + "optional": true + }, + "webpack": { + "optional": true + } } }, - "node_modules/fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "node_modules/css-select": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz", + "integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==", "dev": true, - "engines": { - "node": ">= 0.6" + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^6.1.0", + "domhandler": "^5.0.2", + "domutils": "^3.0.1", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" } }, - "node_modules/from": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/from/-/from-0.1.7.tgz", - "integrity": "sha512-twe20eF1OxVxp/ML/kq2p1uc6KvFK/+vs8WjEbeKmV2He22MKm7YF2ANIt+EOqhJ5L3K/SuuPhk0hWQDjOM23g==", - "dev": true - }, - "node_modules/fs-constants": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", - "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", - "dev": true - }, - "node_modules/fs-extra": { - "version": "11.2.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz", - "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==", + "node_modules/css-what": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", + "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", "dev": true, - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, "engines": { - "node": ">=14.14" + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" } }, - "node_modules/fs-minipass": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-3.0.3.tgz", - "integrity": "sha512-XUBA9XClHbnJWSfBzjkm6RvPsyg3sryZt06BEQoXcF7EK/xpGaQYJgQKDJSUH5SGZ76Y7pFx1QBnXz09rU5Fbw==", + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", "dev": true, - "dependencies": { - "minipass": "^7.0.3" + "peer": true, + "bin": { + "cssesc": "bin/cssesc" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">=4" } }, - "node_modules/fs-monkey": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.5.tgz", - "integrity": "sha512-8uMbBjrhzW76TYgEV27Y5E//W2f/lTFmx78P2w19FZSxarhI/798APGQyuGCwmkNxgwGRhrLfvWyLBvNtuOmew==", + "node_modules/cssom": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.5.0.tgz", + "integrity": "sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw==", "dev": true }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" - }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "node_modules/cssstyle": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz", + "integrity": "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==", "dev": true, - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], + "dependencies": { + "cssom": "~0.3.6" + }, "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + "node": ">=8" } }, - "node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } + "node_modules/cssstyle/node_modules/cssom": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", + "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==", + "dev": true }, - "node_modules/function.prototype.name": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.6.tgz", - "integrity": "sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==", + "node_modules/d": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", + "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==", + "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "functions-have-names": "^1.2.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "es5-ext": "^0.10.50", + "type": "^1.0.1" } }, - "node_modules/functions-have-names": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", - "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } + "node_modules/dag-map": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/dag-map/-/dag-map-1.0.2.tgz", + "integrity": "sha512-+LSAiGFwQ9dRnRdOeaj7g47ZFJcOUPukAP8J3A3fuZ1g9Y44BG+P1sgApjLXTQPOzC4+7S9Wr8kXsfpINM4jpw==" }, - "node_modules/gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "node_modules/dargs": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/dargs/-/dargs-7.0.0.tgz", + "integrity": "sha512-2iy1EkLdlBzQGvbweYRFxmFath8+K7+AKB0TlhHWkNuH+TmovaMH/Wp7V7R4u7f4SnX3OgLsU9t1NI9ioDnUpg==", "dev": true, "engines": { - "node": ">=6.9.0" + "node": ">=8" } }, - "node_modules/get-assigned-identifiers": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/get-assigned-identifiers/-/get-assigned-identifiers-1.2.0.tgz", - "integrity": "sha512-mBBwmeGTrxEMO4pMaaf/uUEFHnYtwr8FTe8Y/mer4rcV/bye0qGm6pw1bGZFGStxC5O76c5ZAVBGnqHmOaJpdQ==", + "node_modules/dash-ast": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/dash-ast/-/dash-ast-2.0.1.tgz", + "integrity": "sha512-5TXltWJGc+RdnabUGzhRae1TRq6m4gr+3K2wQX0is5/F2yS6MJXJvLyI3ErAnsAXuJoGqvfVD5icRgim07DrxQ==", "dev": true }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "node_modules/data-urls": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-3.0.2.tgz", + "integrity": "sha512-Jy/tj3ldjZJo63sVAvg6LHt2mHvl4V6AgRAmNDtLdm7faqtsx+aJG42rsyCo9JCoRVKwPFzKlIPx3DIibwSIaQ==", "dev": true, + "dependencies": { + "abab": "^2.0.6", + "whatwg-mimetype": "^3.0.0", + "whatwg-url": "^11.0.0" + }, "engines": { - "node": "6.* || 8.* || >= 10.*" + "node": ">=12" } }, - "node_modules/get-intrinsic": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", - "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, "dependencies": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "hasown": "^2.0.0" + "ms": "2.1.2" }, "engines": { - "node": ">= 0.4" + "node": ">=6.0" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, - "node_modules/get-nonce": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/get-nonce/-/get-nonce-1.0.1.tgz", - "integrity": "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==", + "node_modules/decache": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/decache/-/decache-4.6.2.tgz", + "integrity": "sha512-2LPqkLeu8XWHU8qNCS3kcF6sCcb5zIzvWaAHYSvPfwhdd7mHuah29NssMzrTYyHN4F5oFy2ko9OBYxegtU0FEw==", "dev": true, - "engines": { - "node": ">=6" + "dependencies": { + "callsite": "^1.0.0" } }, - "node_modules/get-npm-tarball-url": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/get-npm-tarball-url/-/get-npm-tarball-url-2.1.0.tgz", - "integrity": "sha512-ro+DiMu5DXgRBabqXupW38h7WPZ9+Ad8UjwhvsmmN8w1sU7ab0nzAXvVZ4kqYg57OrqomRtJvepX5/xvFKNtjA==", + "node_modules/decamelize-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/decamelize-keys/-/decamelize-keys-1.1.1.tgz", + "integrity": "sha512-WiPxgEirIV0/eIOMcnFBA3/IJZAZqKnwAwWyvvdi4lsr1WCN22nhdf/3db3DoZcUjTV2SqfzIwNyp6y2xs3nmg==", "dev": true, + "dependencies": { + "decamelize": "^1.1.0", + "map-obj": "^1.0.0" + }, "engines": { - "node": ">=12.17" + "node": ">=0.10.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/get-package-type": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", - "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "node_modules/decamelize-keys/node_modules/decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", "dev": true, "engines": { - "node": ">=8.0.0" + "node": ">=0.10.0" } }, - "node_modules/get-port": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/get-port/-/get-port-5.1.1.tgz", - "integrity": "sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ==", + "node_modules/decamelize-keys/node_modules/map-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", + "integrity": "sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg==", "dev": true, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=0.10.0" } }, - "node_modules/get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "node_modules/decimal.js": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.3.tgz", + "integrity": "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==", + "dev": true + }, + "node_modules/dedent": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.5.1.tgz", + "integrity": "sha512-+LxW+KLWxu3HW3M2w2ympwtqPrqYRzU8fqi6Fhd18fBALe15blJPI/I4+UHveMVG6lJqB4JNd4UG0S5cnVHwIg==", "dev": true, - "engines": { - "node": ">=10" + "peerDependencies": { + "babel-plugin-macros": "^3.1.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "peerDependenciesMeta": { + "babel-plugin-macros": { + "optional": true + } } }, - "node_modules/get-symbol-description": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.2.tgz", - "integrity": "sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==", + "node_modules/deep-equal": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.2.tgz", + "integrity": "sha512-5tdhKF6DbU7iIzrIOa1AOUt39ZRm13cmL1cGEh//aqR8x9+tNfbywRf0n5FD/18OKMdo7DNEtrX2t22ZAkI+eg==", + "dev": true, "dependencies": { - "call-bind": "^1.0.5", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.4" + "is-arguments": "^1.1.1", + "is-date-object": "^1.0.5", + "is-regex": "^1.1.4", + "object-is": "^1.1.5", + "object-keys": "^1.1.1", + "regexp.prototype.flags": "^1.5.1" }, "engines": { "node": ">= 0.4" @@ -18475,379 +9978,349 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/get-tsconfig": { - "version": "4.7.2", - "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.7.2.tgz", - "integrity": "sha512-wuMsz4leaj5hbGgg4IvDU0bqJagpftG5l5cXIAvo8uZrqn0NJqwtfupTN00VnkQJPcIRrxYrm1Ue24btpCha2A==", - "dependencies": { - "resolve-pkg-maps": "^1.0.0" - }, - "funding": { - "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" - } - }, - "node_modules/getos": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/getos/-/getos-3.2.1.tgz", - "integrity": "sha512-U56CfOK17OKgTVqozZjUKNdkfEv6jk5WISBJ8SHoagjE6L69zOwl3Z+O8myjY9MEW3i2HPWQBt/LTbCgcC973Q==", - "dev": true, - "dependencies": { - "async": "^3.2.0" - } + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true }, - "node_modules/getpass": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==", - "dev": true, - "dependencies": { - "assert-plus": "^1.0.0" - } + "node_modules/deep-object-diff": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/deep-object-diff/-/deep-object-diff-1.1.9.tgz", + "integrity": "sha512-Rn+RuwkmkDwCi2/oXOFS9Gsr5lJZu/yTGpK7wAaAIE75CC+LCGEZHpY6VQJa/RoJcrmaA/docWJZvYohlNkWPA==" }, - "node_modules/giget": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/giget/-/giget-1.2.1.tgz", - "integrity": "sha512-4VG22mopWtIeHwogGSy1FViXVo0YT+m6BrqZfz0JJFwbSsePsCdOzdLIIli5BtMp7Xe8f/o2OmBpQX2NBOC24g==", - "dev": true, - "dependencies": { - "citty": "^0.1.5", - "consola": "^3.2.3", - "defu": "^6.1.3", - "node-fetch-native": "^1.6.1", - "nypm": "^0.3.3", - "ohash": "^1.1.3", - "pathe": "^1.1.1", - "tar": "^6.2.0" - }, - "bin": { - "giget": "dist/cli.mjs" + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "engines": { + "node": ">=0.10.0" } }, - "node_modules/git-raw-commits": { - "version": "2.0.11", - "resolved": "https://registry.npmjs.org/git-raw-commits/-/git-raw-commits-2.0.11.tgz", - "integrity": "sha512-VnctFhw+xfj8Va1xtfEqCUD2XDrbAPSJx+hSrE5K7fGdjZruW7XV+QOrN7LF/RJyvspRiD2I0asWsxFp0ya26A==", + "node_modules/default-browser": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-5.2.1.tgz", + "integrity": "sha512-WY/3TUME0x3KPYdRRxEJJvXRHV4PyPoUsxtZa78lwItwRQRHhd2U9xOscaT/YTf8uCXIAjeJOFBVEh/7FtD8Xg==", "dev": true, + "peer": true, "dependencies": { - "dargs": "^7.0.0", - "lodash": "^4.17.15", - "meow": "^8.0.0", - "split2": "^3.0.0", - "through2": "^4.0.0" - }, - "bin": { - "git-raw-commits": "cli.js" + "bundle-name": "^4.1.0", + "default-browser-id": "^5.0.0" }, "engines": { - "node": ">=10" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/git-raw-commits/node_modules/hosted-git-info": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", - "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==", + "node_modules/default-browser-id": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-5.0.0.tgz", + "integrity": "sha512-A6p/pu/6fyBcA1TRz/GqWYPViplrftcW2gZC9q79ngNCKAeR/X3gcEdXQHl4KNXV+3wgIJ1CPkJQ3IHM6lcsyA==", "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, + "peer": true, "engines": { - "node": ">=10" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/git-raw-commits/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "node_modules/default-gateway": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-6.0.3.tgz", + "integrity": "sha512-fwSOJsbbNzZ/CUFpqFBqYfYNLj1NbMPm8MMCIzHjC83iSJRBEGmDUxU+WP661BaBQImeC2yHwXtz+P/O9o+XEg==", "dev": true, + "peer": true, "dependencies": { - "yallist": "^4.0.0" + "execa": "^5.0.0" }, "engines": { - "node": ">=10" + "node": ">= 10" } }, - "node_modules/git-raw-commits/node_modules/meow": { - "version": "8.1.2", - "resolved": "https://registry.npmjs.org/meow/-/meow-8.1.2.tgz", - "integrity": "sha512-r85E3NdZ+mpYk1C6RjPFEMSE+s1iZMuHtsHAqY0DT3jZczl0diWUZ8g6oU7h0M9cD2EL+PzaYghhCLzR0ZNn5Q==", + "node_modules/defaults": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", + "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", "dev": true, "dependencies": { - "@types/minimist": "^1.2.0", - "camelcase-keys": "^6.2.2", - "decamelize-keys": "^1.1.0", - "hard-rejection": "^2.1.0", - "minimist-options": "4.1.0", - "normalize-package-data": "^3.0.0", - "read-pkg-up": "^7.0.1", - "redent": "^3.0.0", - "trim-newlines": "^3.0.0", - "type-fest": "^0.18.0", - "yargs-parser": "^20.2.3" - }, - "engines": { - "node": ">=10" + "clone": "^1.0.2" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/git-raw-commits/node_modules/normalize-package-data": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.3.tgz", - "integrity": "sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==", + "node_modules/defaults/node_modules/clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", "dev": true, - "dependencies": { - "hosted-git-info": "^4.0.1", - "is-core-module": "^2.5.0", - "semver": "^7.3.4", - "validate-npm-package-license": "^3.0.1" - }, "engines": { - "node": ">=10" + "node": ">=0.8" } }, - "node_modules/git-raw-commits/node_modules/split2": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/split2/-/split2-3.2.2.tgz", - "integrity": "sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg==", - "dev": true, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", "dependencies": { - "readable-stream": "^3.0.0" - } - }, - "node_modules/git-raw-commits/node_modules/type-fest": { - "version": "0.18.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.18.1.tgz", - "integrity": "sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==", - "dev": true, + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, "engines": { - "node": ">=10" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/git-raw-commits/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, - "node_modules/github-slugger": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/github-slugger/-/github-slugger-1.5.0.tgz", - "integrity": "sha512-wIh+gKBI9Nshz2o46B0B3f5k/W+WI9ZAv6y5Dn5WJ5SK1t0TnDimB4WE5rmTD05ZAIn8HALCZVmCsvj0w0v0lw==", - "dev": true + "node_modules/define-lazy-prop": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", + "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", + "dev": true, + "engines": { + "node": ">=8" + } }, - "node_modules/glob": { - "version": "10.3.10", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz", - "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==", + "node_modules/define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", "dev": true, "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^2.3.5", - "minimatch": "^9.0.1", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0", - "path-scurry": "^1.10.1" - }, - "bin": { - "glob": "dist/esm/bin.mjs" + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/glob-escape": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/glob-escape/-/glob-escape-0.0.2.tgz", - "integrity": "sha512-L/cXYz8x7qer1HAyUQ+mbjcUsJVdpRxpAf7CwqHoNBs9vTpABlGfNN4tzkDxt+u3Z7ZncVyKlCNPtzb0R/7WbA==", + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", "dev": true, "engines": { - "node": ">= 0.10" + "node": ">=0.4.0" } }, - "node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dependencies": { - "is-glob": "^4.0.1" - }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "dev": true, "engines": { - "node": ">= 6" + "node": ">= 0.8" } }, - "node_modules/glob-to-regexp": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", - "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", - "dev": true + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "dev": true, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } }, - "node_modules/global-dirs": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-3.0.1.tgz", - "integrity": "sha512-NBcGGFbBA9s1VzD41QXDG+3++t9Mn5t1FpLdhESY6oKY4gYTFpX4wO3sqGUa0Srjtbfj3szX0RnemmrVRUdULA==", + "node_modules/detect-libc": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz", + "integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==", "dev": true, - "dependencies": { - "ini": "2.0.0" - }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=8" } }, - "node_modules/global-dirs/node_modules/ini": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ini/-/ini-2.0.0.tgz", - "integrity": "sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==", + "node_modules/detect-newline": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", + "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", "dev": true, "engines": { - "node": ">=10" + "node": ">=8" } }, - "node_modules/global-prefix": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-3.0.0.tgz", - "integrity": "sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==", + "node_modules/detect-node": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", + "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", + "dev": true, + "peer": true + }, + "node_modules/dfa": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/dfa/-/dfa-1.2.0.tgz", + "integrity": "sha512-ED3jP8saaweFTjeGX8HQPjeC1YYyZs98jGNZx6IiBvxW7JG5v492kamAQB3m2wop07CvU/RQmzcKr6bgcC5D/Q==", + "dev": true + }, + "node_modules/diff-sequences": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", + "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", + "dev": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/dns-packet": { + "version": "5.6.1", + "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-5.6.1.tgz", + "integrity": "sha512-l4gcSouhcgIKRvyy99RNVOgxXiicE+2jZoNmaNmZ6JXiGajBOJAesk1OBlJuM5k2c+eudGdLxDqXuPCKIj6kpw==", "dev": true, + "peer": true, "dependencies": { - "ini": "^1.3.5", - "kind-of": "^6.0.2", - "which": "^1.3.1" + "@leichtgewicht/ip-codec": "^2.0.1" }, "engines": { "node": ">=6" } }, - "node_modules/global-prefix/node_modules/ini": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", - "dev": true + "node_modules/dom-converter": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.2.0.tgz", + "integrity": "sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "utila": "~0.4" + } }, - "node_modules/global-prefix/node_modules/which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "node_modules/dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", "dev": true, "dependencies": { - "isexe": "^2.0.0" + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" }, - "bin": { - "which": "bin/which" + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" } }, - "node_modules/globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", "dev": true, - "engines": { - "node": ">=4" - } + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ] }, - "node_modules/globalthis": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz", - "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==", + "node_modules/domexception": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/domexception/-/domexception-4.0.0.tgz", + "integrity": "sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw==", + "deprecated": "Use your platform's native DOMException instead", + "dev": true, "dependencies": { - "define-properties": "^1.1.3" + "webidl-conversions": "^7.0.0" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=12" } }, - "node_modules/globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "node_modules/domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", "dev": true, "dependencies": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" + "domelementtype": "^2.3.0" }, "engines": { - "node": ">=10" + "node": ">= 4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/fb55/domhandler?sponsor=1" } }, - "node_modules/gopd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", - "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "node_modules/domutils": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz", + "integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==", + "dev": true, "dependencies": { - "get-intrinsic": "^1.1.3" + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3" }, "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/govuk-frontend": { - "version": "4.8.0", - "resolved": "https://registry.npmjs.org/govuk-frontend/-/govuk-frontend-4.8.0.tgz", - "integrity": "sha512-NOmPJxL8IYq1HSNHYKx9XY2LLTxuwb+IFASiGQO4sgJ8K7AG66SlSeqARrcetevV8zOf+i1z+MbJJ2O7//OxAw==", - "engines": { - "node": ">= 4.2.0" + "url": "https://github.com/fb55/domutils?sponsor=1" } }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" + "node_modules/dot": { + "version": "2.0.0-beta.1", + "resolved": "https://registry.npmjs.org/dot/-/dot-2.0.0-beta.1.tgz", + "integrity": "sha512-kxM7fSnNQTXOmaeGuBSXM8O3fEsBb7XSDBllkGbRwa0lJSJTxxDE/4eSNGLKZUmlFw0f1vJ5qSV2BljrgQtgIA==", + "dev": true }, - "node_modules/graphemer": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", - "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==" + "node_modules/dot-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", + "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "no-case": "^3.0.4", + "tslib": "^2.0.3" + } }, - "node_modules/growl": { - "version": "1.10.5", - "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", - "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", + "node_modules/dot-prop": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", + "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==", "dev": true, + "dependencies": { + "is-obj": "^2.0.0" + }, "engines": { - "node": ">=4.x" + "node": ">=8" } }, - "node_modules/gunzip-maybe": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/gunzip-maybe/-/gunzip-maybe-1.4.2.tgz", - "integrity": "sha512-4haO1M4mLO91PW57BMsDFf75UmwoRX0GkdD+Faw+Lr+r/OZrOCS0pIBwOL1xCKQqnQzbNFGgK2V2CpBUPeFNTw==", + "node_modules/dotenv": { + "version": "16.4.5", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz", + "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==", "dev": true, - "dependencies": { - "browserify-zlib": "^0.1.4", - "is-deflate": "^1.0.0", - "is-gzip": "^1.0.0", - "peek-stream": "^1.1.0", - "pumpify": "^1.3.3", - "through2": "^2.0.3" + "engines": { + "node": ">=12" }, - "bin": { - "gunzip-maybe": "bin.js" + "funding": { + "url": "https://dotenvx.com" } }, - "node_modules/gunzip-maybe/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "node_modules/duplexer": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", + "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==", "dev": true }, - "node_modules/gunzip-maybe/node_modules/readable-stream": { + "node_modules/duplexer2": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", + "integrity": "sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA==", + "dev": true, + "dependencies": { + "readable-stream": "^2.0.2" + } + }, + "node_modules/duplexer2/node_modules/readable-stream": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", @@ -18862,7 +10335,7 @@ "util-deprecate": "~1.0.1" } }, - "node_modules/gunzip-maybe/node_modules/string_decoder": { + "node_modules/duplexer2/node_modules/string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", @@ -18871,574 +10344,698 @@ "safe-buffer": "~5.1.0" } }, - "node_modules/gunzip-maybe/node_modules/through2": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", - "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "dev": true + }, + "node_modules/electron-to-chromium": { + "version": "1.5.8", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.8.tgz", + "integrity": "sha512-4Nx0gP2tPNBLTrFxBMHpkQbtn2hidPVr/+/FTtcCiBYTucqc70zRyVZiOLj17Ui3wTO7SQ1/N+hkHYzJjBzt6A==", + "dev": true + }, + "node_modules/emitter-component": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/emitter-component/-/emitter-component-1.1.2.tgz", + "integrity": "sha512-QdXO3nXOzZB4pAjM0n6ZE+R9/+kPpECA/XSELIcc54NeYVnBqIk+4DFiBgK+8QbV3mdvTG6nedl7dTYgO+5wDw==", "dev": true, - "dependencies": { - "readable-stream": "~2.3.6", - "xtend": "~4.0.1" + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/hammerjs": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/hammerjs/-/hammerjs-2.0.8.tgz", - "integrity": "sha512-tSQXBXS/MWQOn/RKckawJ61vvsDpCom87JgxiYdGwHdOa0ht0vzUWDlfioofFCRU0L+6NGDt6XzbgoJvZkMeRQ==", + "node_modules/emittery": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", + "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", "dev": true, "engines": { - "node": ">=0.8.0" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sindresorhus/emittery?sponsor=1" } }, - "node_modules/handle-thing": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", - "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==", + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", "dev": true }, - "node_modules/handlebars": { - "version": "4.7.8", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", - "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==", + "node_modules/emojis-list": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", + "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", "dev": true, - "dependencies": { - "minimist": "^1.2.5", - "neo-async": "^2.6.2", - "source-map": "^0.6.1", - "wordwrap": "^1.0.0" - }, - "bin": { - "handlebars": "bin/handlebars" - }, + "peer": true, "engines": { - "node": ">=0.4.7" - }, - "optionalDependencies": { - "uglify-js": "^3.1.4" + "node": ">= 4" } }, - "node_modules/handlebars/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", "dev": true, "engines": { - "node": ">=0.10.0" + "node": ">= 0.8" } }, - "node_modules/hard-rejection": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/hard-rejection/-/hard-rejection-2.1.0.tgz", - "integrity": "sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==", + "node_modules/encoding": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", + "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", "dev": true, - "engines": { - "node": ">=6" + "optional": true, + "dependencies": { + "iconv-lite": "^0.6.2" } }, - "node_modules/has": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.4.tgz", - "integrity": "sha512-qdSAmqLF6209RFj4VVItywPMbm3vWylknmB3nvNiUIs72xAimcM8nVYxYr7ncvZq5qzk9MKIZR8ijqD/1QuYjQ==", + "node_modules/encoding/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", "dev": true, + "optional": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/has-bigints": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", - "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=0.10.0" } }, - "node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "node_modules/end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", "dev": true, - "engines": { - "node": ">=4" + "dependencies": { + "once": "^1.4.0" } }, - "node_modules/has-property-descriptors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", - "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "node_modules/enhanced-resolve": { + "version": "5.17.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.17.1.tgz", + "integrity": "sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg==", + "dev": true, + "peer": true, "dependencies": { - "es-define-property": "^1.0.0" + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">=10.13.0" } }, - "node_modules/has-proto": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", - "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "dev": true, "engines": { - "node": ">= 0.4" + "node": ">=0.12" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/fb55/entities?sponsor=1" } }, - "node_modules/has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "node_modules/env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "dev": true, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=6" } }, - "node_modules/has-tostringtag": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", - "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", - "dependencies": { - "has-symbols": "^1.0.3" - }, + "node_modules/environment": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/environment/-/environment-1.1.0.tgz", + "integrity": "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==", + "dev": true, "engines": { - "node": ">= 0.4" + "node": ">=18" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/hasown": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.1.tgz", - "integrity": "sha512-1/th4MHjnwncwXsIW6QMzlvYL9kG5e/CpVvLRZe4XPa8TOUNbCELqmvhDmnkNsAjwaG4+I8gJJL0JBvTTLO9qA==", + "node_modules/err-code": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz", + "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==", + "dev": true + }, + "node_modules/errno": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz", + "integrity": "sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==", + "dev": true, + "optional": true, + "peer": true, "dependencies": { - "function-bind": "^1.1.2" + "prr": "~1.0.1" }, - "engines": { - "node": ">= 0.4" + "bin": { + "errno": "cli.js" } }, - "node_modules/he": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", - "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", "dev": true, - "bin": { - "he": "bin/he" + "dependencies": { + "is-arrayish": "^0.2.1" } }, - "node_modules/hosted-git-info": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-7.0.1.tgz", - "integrity": "sha512-+K84LB1DYwMHoHSgaOY/Jfhw3ucPmSET5v98Ke/HdNSw4a0UktWzyW1mjhjpuxxTqOOsfWT/7iVshHmVZ4IpOA==", + "node_modules/errorhandler": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/errorhandler/-/errorhandler-1.5.1.tgz", + "integrity": "sha512-rcOwbfvP1WTViVoUjcfZicVzjhjTuhSMntHh6mW3IrEiyE6mJyXvsToJUJGlGlw/2xU9P5whlWNGlIDVeCiT4A==", "dev": true, "dependencies": { - "lru-cache": "^10.0.1" + "accepts": "~1.3.7", + "escape-html": "~1.0.3" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/es-define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", + "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", + "dependencies": { + "get-intrinsic": "^1.2.4" }, "engines": { - "node": "^16.14.0 || >=18.0.0" + "node": ">= 0.4" } }, - "node_modules/hosted-git-info/node_modules/lru-cache": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.0.tgz", - "integrity": "sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==", - "dev": true, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", "engines": { - "node": "14 || >=16.14" + "node": ">= 0.4" } }, - "node_modules/hpack.js": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", - "integrity": "sha512-zJxVehUdMGIKsRaNt7apO2Gqp0BdqW5yaiGHXXmbpvxgBYVZnAql+BJb4RO5ad2MgpbZKn5G6nMnegrH1FcNYQ==", + "node_modules/es-module-lexer": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.4.1.tgz", + "integrity": "sha512-cXLGjP0c4T3flZJKQSuziYoq7MlT+rnvfZjfp7h+I7K9BNX54kP9nyWvdbwjQ4u1iWbOL4u96fgeZLToQlZC7w==", + "dev": true, + "peer": true + }, + "node_modules/es5-ext": { + "version": "0.10.64", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.64.tgz", + "integrity": "sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg==", "dev": true, + "hasInstallScript": true, "dependencies": { - "inherits": "^2.0.1", - "obuf": "^1.0.0", - "readable-stream": "^2.0.1", - "wbuf": "^1.1.0" + "es6-iterator": "^2.0.3", + "es6-symbol": "^3.1.3", + "esniff": "^2.0.1", + "next-tick": "^1.1.0" + }, + "engines": { + "node": ">=0.10" } }, - "node_modules/hpack.js/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "dev": true - }, - "node_modules/hpack.js/node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "node_modules/es6-iterator": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", + "integrity": "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==", "dev": true, "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" + "d": "1", + "es5-ext": "^0.10.35", + "es6-symbol": "^3.1.1" } }, - "node_modules/hpack.js/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "node_modules/es6-map": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/es6-map/-/es6-map-0.1.5.tgz", + "integrity": "sha512-mz3UqCh0uPCIqsw1SSAkB/p0rOzF/M0V++vyN7JqlPtSW/VsYgQBvVvqMLmfBuyMzTpLnNqi6JmcSizs4jy19A==", "dev": true, "dependencies": { - "safe-buffer": "~5.1.0" + "d": "1", + "es5-ext": "~0.10.14", + "es6-iterator": "~2.0.1", + "es6-set": "~0.1.5", + "es6-symbol": "~3.1.1", + "event-emitter": "~0.3.5" } }, - "node_modules/html-encoding-sniffer": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz", - "integrity": "sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==", + "node_modules/es6-set": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/es6-set/-/es6-set-0.1.6.tgz", + "integrity": "sha512-TE3LgGLDIBX332jq3ypv6bcOpkLO0AslAQo7p2VqX/1N46YNsvIWgvjojjSEnWEGWMhr1qUbYeTSir5J6mFHOw==", "dev": true, "dependencies": { - "whatwg-encoding": "^2.0.0" + "d": "^1.0.1", + "es5-ext": "^0.10.62", + "es6-iterator": "~2.0.3", + "es6-symbol": "^3.1.3", + "event-emitter": "^0.3.5", + "type": "^2.7.2" }, "engines": { - "node": ">=12" + "node": ">=0.12" } }, - "node_modules/html-entities": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.4.0.tgz", - "integrity": "sha512-igBTJcNNNhvZFRtm8uA6xMY6xYleeDwn3PeBCkDz7tHttv4F2hsDI2aPgNERWzvRcNYHNT3ymRaQzllmXj4YsQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/mdevils" - }, - { - "type": "patreon", - "url": "https://patreon.com/mdevils" - } - ] + "node_modules/es6-set/node_modules/type": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/type/-/type-2.7.2.tgz", + "integrity": "sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw==", + "dev": true }, - "node_modules/html-escaper": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", - "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "node_modules/es6-shim": { + "version": "0.35.8", + "resolved": "https://registry.npmjs.org/es6-shim/-/es6-shim-0.35.8.tgz", + "integrity": "sha512-Twf7I2v4/1tLoIXMT8HlqaBSS5H2wQTs2wx3MNYCI8K1R1/clXyCazrcVCPm/FuO9cyV8+leEaZOWD5C253NDg==", "dev": true }, - "node_modules/html-minifier-terser": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", - "integrity": "sha512-YXxSlJBZTP7RS3tWnQw74ooKa6L9b9i9QYXY21eUEvhZ3u9XLfv6OnFsQq6RxkhHygsaUMvYsZRV5rU/OVNZxw==", + "node_modules/es6-symbol": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz", + "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==", "dev": true, "dependencies": { - "camel-case": "^4.1.2", - "clean-css": "^5.2.2", - "commander": "^8.3.0", - "he": "^1.2.0", - "param-case": "^3.0.4", - "relateurl": "^0.2.7", - "terser": "^5.10.0" + "d": "^1.0.1", + "ext": "^1.1.2" + } + }, + "node_modules/esbuild": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.23.0.tgz", + "integrity": "sha512-1lvV17H2bMYda/WaFb2jLPeHU3zml2k4/yagNMG8Q/YtfMjCwEUZa2eXXMgZTVSL5q1n4H7sQ0X6CdJDqqeCFA==", + "dev": true, + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.23.0", + "@esbuild/android-arm": "0.23.0", + "@esbuild/android-arm64": "0.23.0", + "@esbuild/android-x64": "0.23.0", + "@esbuild/darwin-arm64": "0.23.0", + "@esbuild/darwin-x64": "0.23.0", + "@esbuild/freebsd-arm64": "0.23.0", + "@esbuild/freebsd-x64": "0.23.0", + "@esbuild/linux-arm": "0.23.0", + "@esbuild/linux-arm64": "0.23.0", + "@esbuild/linux-ia32": "0.23.0", + "@esbuild/linux-loong64": "0.23.0", + "@esbuild/linux-mips64el": "0.23.0", + "@esbuild/linux-ppc64": "0.23.0", + "@esbuild/linux-riscv64": "0.23.0", + "@esbuild/linux-s390x": "0.23.0", + "@esbuild/linux-x64": "0.23.0", + "@esbuild/netbsd-x64": "0.23.0", + "@esbuild/openbsd-arm64": "0.23.0", + "@esbuild/openbsd-x64": "0.23.0", + "@esbuild/sunos-x64": "0.23.0", + "@esbuild/win32-arm64": "0.23.0", + "@esbuild/win32-ia32": "0.23.0", + "@esbuild/win32-x64": "0.23.0" + } + }, + "node_modules/esbuild-wasm": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/esbuild-wasm/-/esbuild-wasm-0.23.0.tgz", + "integrity": "sha512-6jP8UmWy6R6TUUV8bMuC3ZyZ6lZKI56x0tkxyCIqWwRRJ/DgeQKneh/Oid5EoGoPFLrGNkz47ZEtWAYuiY/u9g==", + "dev": true, "bin": { - "html-minifier-terser": "cli.js" + "esbuild": "bin/esbuild" }, "engines": { - "node": ">=12" + "node": ">=18" } }, - "node_modules/html-minifier-terser/node_modules/commander": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", - "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", + "node_modules/escalade": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", + "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", "dev": true, "engines": { - "node": ">= 12" + "node": ">=6" } }, - "node_modules/html-webpack-plugin": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.6.0.tgz", - "integrity": "sha512-iwaY4wzbe48AfKLZ/Cc8k0L+FKG6oSNRaZ8x5A/T/IVDGyXcbHncM9TdDa93wn0FsSm82FhTKW7f3vS61thXAw==", + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "dev": true + }, + "node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", "dev": true, - "dependencies": { - "@types/html-minifier-terser": "^6.0.0", - "html-minifier-terser": "^6.0.2", - "lodash": "^4.17.21", - "pretty-error": "^4.0.0", - "tapable": "^2.0.0" - }, "engines": { - "node": ">=10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/html-webpack-plugin" - }, - "peerDependencies": { - "@rspack/core": "0.x || 1.x", - "webpack": "^5.20.0" - }, - "peerDependenciesMeta": { - "@rspack/core": { - "optional": true - }, - "webpack": { - "optional": true - } + "node": ">=0.8.0" } }, - "node_modules/htmlparser2": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz", - "integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==", + "node_modules/escodegen": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz", + "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", "dev": true, - "funding": [ - "https://github.com/fb55/htmlparser2?sponsor=1", - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ], "dependencies": { - "domelementtype": "^2.3.0", - "domhandler": "^5.0.3", - "domutils": "^3.0.1", - "entities": "^4.4.0" + "esprima": "^4.0.1", + "estraverse": "^5.2.0", + "esutils": "^2.0.2" + }, + "bin": { + "escodegen": "bin/escodegen.js", + "esgenerate": "bin/esgenerate.js" + }, + "engines": { + "node": ">=6.0" + }, + "optionalDependencies": { + "source-map": "~0.6.1" } }, - "node_modules/http-auth": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/http-auth/-/http-auth-4.1.9.tgz", - "integrity": "sha512-kvPYxNGc9EKGTXvOMnTBQw2RZfuiSihK/mLw/a4pbtRueTE45S55Lw/3k5CktIf7Ak0veMKEIteDj4YkNmCzmQ==", + "node_modules/escodegen/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/esniff": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/esniff/-/esniff-2.0.1.tgz", + "integrity": "sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg==", "dev": true, "dependencies": { - "apache-crypt": "^1.1.2", - "apache-md5": "^1.0.6", - "bcryptjs": "^2.4.3", - "uuid": "^8.3.2" + "d": "^1.0.1", + "es5-ext": "^0.10.62", + "event-emitter": "^0.3.5", + "type": "^2.7.2" }, "engines": { - "node": ">=8" + "node": ">=0.10" } }, - "node_modules/http-auth-connect": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/http-auth-connect/-/http-auth-connect-1.0.6.tgz", - "integrity": "sha512-yaO0QSCPqGCjPrl3qEEHjJP+lwZ6gMpXLuCBE06eWwcXomkI5TARtu0kxf9teFuBj6iaV3Ybr15jaWUvbzNzHw==", + "node_modules/esniff/node_modules/type": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/type/-/type-2.7.2.tgz", + "integrity": "sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw==", + "dev": true + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", "dev": true, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, "engines": { - "node": ">=8" + "node": ">=4" } }, - "node_modules/http-auth/node_modules/uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", "dev": true, - "bin": { - "uuid": "dist/bin/uuid" + "peer": true, + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" } }, - "node_modules/http-cache-semantics": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", - "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==", - "dev": true + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } }, - "node_modules/http-deceiver": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", - "integrity": "sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw==", + "node_modules/estree-is-function": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/estree-is-function/-/estree-is-function-1.0.0.tgz", + "integrity": "sha512-nSCWn1jkSq2QAtkaVLJZY2ezwcFO161HVc174zL1KPW3RJ+O6C3eJb8Nx7OXzvhoEv+nLgSR1g71oWUHUDTrJA==", "dev": true }, - "node_modules/http-errors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", "dev": true, - "dependencies": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" - }, "engines": { - "node": ">= 0.8" + "node": ">=0.10.0" } }, - "node_modules/http-parser-js": { - "version": "0.5.8", - "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.8.tgz", - "integrity": "sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q==", - "dev": true - }, - "node_modules/http-proxy": { - "version": "1.18.1", - "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", - "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", "dev": true, - "dependencies": { - "eventemitter3": "^4.0.0", - "follow-redirects": "^1.0.0", - "requires-port": "^1.0.0" - }, "engines": { - "node": ">=8.0.0" + "node": ">= 0.6" } }, - "node_modules/http-proxy-agent": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", - "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", + "node_modules/event-emitter": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", + "integrity": "sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==", "dev": true, "dependencies": { - "@tootallnate/once": "2", - "agent-base": "6", - "debug": "4" - }, - "engines": { - "node": ">= 6" + "d": "1", + "es5-ext": "~0.10.14" } }, - "node_modules/http-proxy-agent/node_modules/agent-base": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", - "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "node_modules/event-stream": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/event-stream/-/event-stream-4.0.1.tgz", + "integrity": "sha512-qACXdu/9VHPBzcyhdOWR5/IahhGMf0roTeZJfzz077GwylcDd90yOHLouhmv7GJ5XzPi6ekaQWd8AvPP2nOvpA==", "dev": true, "dependencies": { - "debug": "4" - }, + "duplexer": "^0.1.1", + "from": "^0.1.7", + "map-stream": "0.0.7", + "pause-stream": "^0.0.11", + "split": "^1.0.1", + "stream-combiner": "^0.2.2", + "through": "^2.3.8" + } + }, + "node_modules/eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", + "dev": true, + "peer": true + }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "dev": true, + "peer": true, "engines": { - "node": ">= 6.0.0" + "node": ">=0.8.x" } }, - "node_modules/http-proxy-middleware": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.6.tgz", - "integrity": "sha512-ya/UeJ6HVBYxrgYotAZo1KvPWlgB48kUJLDePFeneHsVujFaW5WNj2NgWCAE//B1Dl02BIfYlpNgBy8Kf8Rjmw==", + "node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", "dev": true, "dependencies": { - "@types/http-proxy": "^1.17.8", - "http-proxy": "^1.18.1", - "is-glob": "^4.0.1", - "is-plain-obj": "^3.0.0", - "micromatch": "^4.0.2" + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" }, "engines": { - "node": ">=12.0.0" - }, - "peerDependencies": { - "@types/express": "^4.17.13" + "node": ">=10" }, - "peerDependenciesMeta": { - "@types/express": { - "optional": true - } + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" } }, - "node_modules/http-signature": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.3.6.tgz", - "integrity": "sha512-3adrsD6zqo4GsTqtO7FyrejHNv+NgiIfAfv68+jVlFmSr9OGy7zrxONceFRLKvnnZA5jbxQBX1u9PpB6Wi32Gw==", + "node_modules/exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/expect": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz", + "integrity": "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==", "dev": true, "dependencies": { - "assert-plus": "^1.0.0", - "jsprim": "^2.0.2", - "sshpk": "^1.14.1" + "@jest/expect-utils": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0" }, "engines": { - "node": ">=0.10" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/https-proxy-agent": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.2.tgz", - "integrity": "sha512-NmLNjm6ucYwtcUmL7JQC1ZQ57LmHP4lT15FQ8D61nak1rO6DH+fz5qNK2Ap5UN4ZapYICE3/0KodcLYSPsPbaA==", + "node_modules/exponential-backoff": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/exponential-backoff/-/exponential-backoff-3.1.1.tgz", + "integrity": "sha512-dX7e/LHVJ6W3DE1MHWi9S1EYzDESENfLrYohG2G++ovZrYOkm4Knwa0mc1cn84xJOR4KEU0WSchhLbd0UklbHw==", + "dev": true + }, + "node_modules/express": { + "version": "4.18.3", + "resolved": "https://registry.npmjs.org/express/-/express-4.18.3.tgz", + "integrity": "sha512-6VyCijWQ+9O7WuVMTRBTl+cjNNIzD5cY5mQ1WM8r/LEkI2u8EYpOotESNwzNlyCn3g+dmjKYI6BmNneSr/FSRw==", "dev": true, "dependencies": { - "agent-base": "^7.0.2", - "debug": "4" + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.2", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.5.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.2.0", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.7", + "qs": "6.11.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.18.0", + "serve-static": "1.15.0", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" }, "engines": { - "node": ">= 14" + "node": ">= 0.10.0" } }, - "node_modules/human-signals": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", - "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "node_modules/express-urlrewrite": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/express-urlrewrite/-/express-urlrewrite-1.4.0.tgz", + "integrity": "sha512-PI5h8JuzoweS26vFizwQl6UTF25CAHSggNv0J25Dn/IKZscJHWZzPrI5z2Y2jgOzIaw2qh8l6+/jUcig23Z2SA==", "dev": true, - "engines": { - "node": ">=10.17.0" + "dependencies": { + "debug": "*", + "path-to-regexp": "^1.0.3" } }, - "node_modules/husky": { - "version": "9.0.11", - "resolved": "https://registry.npmjs.org/husky/-/husky-9.0.11.tgz", - "integrity": "sha512-AB6lFlbwwyIqMdHYhwPe+kjOC3Oc5P3nThEoW/AaO2BX3vJDjWPFxYLxokUZOo6RNX20He3AaT8sESs9NJcmEw==", + "node_modules/express-urlrewrite/node_modules/isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==", + "dev": true + }, + "node_modules/express-urlrewrite/node_modules/path-to-regexp": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz", + "integrity": "sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==", "dev": true, - "bin": { - "husky": "bin.mjs" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/typicode" + "dependencies": { + "isarray": "0.0.1" } }, - "node_modules/i18next": { - "version": "23.10.0", - "resolved": "https://registry.npmjs.org/i18next/-/i18next-23.10.0.tgz", - "integrity": "sha512-/TgHOqsa7/9abUKJjdPeydoyDc0oTi/7u9F8lMSj6ufg4cbC1Oj3f/Jja7zj7WRIhEQKB7Q4eN6y68I9RDxxGQ==", + "node_modules/express/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://locize.com" - }, - { - "type": "individual", - "url": "https://locize.com/i18next.html" - }, - { - "type": "individual", - "url": "https://www.i18next.com/how-to/faq#i18next-is-awesome.-how-can-i-support-the-project" - } - ], "dependencies": { - "@babel/runtime": "^7.23.2" + "ms": "2.0.0" } }, - "node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "node_modules/express/node_modules/finalhandler": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", + "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", "dev": true, "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" }, "engines": { - "node": ">=0.10.0" + "node": ">= 0.8" } }, - "node_modules/icss-utils": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz", - "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==", + "node_modules/express/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "node_modules/express/node_modules/qs": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", "dev": true, + "dependencies": { + "side-channel": "^1.0.4" + }, "engines": { - "node": "^10 || ^12 || >= 14" + "node": ">=0.6" }, - "peerDependencies": { - "postcss": "^8.1.0" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "node_modules/express/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", "dev": true, "funding": [ { @@ -19455,540 +11052,447 @@ } ] }, - "node_modules/ignore": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.0.tgz", - "integrity": "sha512-g7dmpshy+gD7mh88OC9NwSGTKoc3kyLAZQRU1mt53Aw/vnvfXnbC+F/7F7QoYVKbV+KNvJx8wArewKy1vXMtlg==", - "engines": { - "node": ">= 4" - } - }, - "node_modules/ignore-walk": { - "version": "6.0.4", - "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-6.0.4.tgz", - "integrity": "sha512-t7sv42WkwFkyKbivUCglsQW5YWMskWtbEf4MNKX5u/CCWHKSPzN4FtBQGsQZgCLbxOzpVlcbWVK5KB3auIOjSw==", + "node_modules/ext": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/ext/-/ext-1.7.0.tgz", + "integrity": "sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==", "dev": true, "dependencies": { - "minimatch": "^9.0.0" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/image-size": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/image-size/-/image-size-0.5.5.tgz", - "integrity": "sha512-6TDAlDPZxUFCv+fuOkIoXT/V/f3Qbq8e37p+YOiYrUv3v9cc3/6x78VdfPgFVaB9dZYeLUfKgHRebpkm/oP2VQ==", - "dev": true, - "optional": true, - "bin": { - "image-size": "bin/image-size.js" - }, - "engines": { - "node": ">=0.10.0" + "type": "^2.7.2" } }, - "node_modules/immutable": { - "version": "4.3.5", - "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.5.tgz", - "integrity": "sha512-8eabxkth9gZatlwl5TBuJnCsoTADlL6ftEr7A4qgdaTsPyreilDSnUk57SO+jfKcNtxPa22U5KK6DSeAYhpBJw==", + "node_modules/ext/node_modules/type": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/type/-/type-2.7.2.tgz", + "integrity": "sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw==", "dev": true }, - "node_modules/import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/import-fresh/node_modules/resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "engines": { - "node": ">=4" - } - }, - "node_modules/import-local": { + "node_modules/external-editor": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", - "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", - "dev": true, - "dependencies": { - "pkg-dir": "^4.2.0", - "resolve-cwd": "^3.0.0" - }, - "bin": { - "import-local-fixture": "fixtures/cli.js" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/import-local/node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/import-local/node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/import-local/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", + "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", "dev": true, "dependencies": { - "p-try": "^2.0.0" + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" }, "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=4" } }, - "node_modules/import-local/node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "node_modules/fancy-log": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fancy-log/-/fancy-log-2.0.0.tgz", + "integrity": "sha512-9CzxZbACXMUXW13tS0tI8XsGGmxWzO2DmYrGuBJOJ8k8q2K7hwfJA5qHjuPPe8wtsco33YR9wc+Rlr5wYFvhSA==", "dev": true, "dependencies": { - "p-limit": "^2.2.0" + "color-support": "^1.1.3" }, "engines": { - "node": ">=8" + "node": ">=10.13.0" } }, - "node_modules/import-local/node_modules/pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + }, + "node_modules/fast-glob": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", "dev": true, "dependencies": { - "find-up": "^4.0.0" + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" }, "engines": { - "node": ">=8" + "node": ">=8.6.0" } }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "engines": { - "node": ">=0.8.19" - } + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true }, - "node_modules/indent-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", - "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", - "dev": true, - "engines": { - "node": ">=8" - } + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "node_modules/fast-uri": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.1.tgz", + "integrity": "sha512-MWipKbbYiYI0UC7cl8m/i/IWTqfC8YXsqjzybjddLsFjStroQzsHXkc73JutMvBiXmOvapk+axIl79ig5t55Bw==" + }, + "node_modules/fastq": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", + "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", + "dev": true, "dependencies": { - "once": "^1.3.0", - "wrappy": "1" + "reusify": "^1.0.4" } }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "node_modules/ini": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ini/-/ini-4.1.1.tgz", - "integrity": "sha512-QQnnxNyfvmHFIsj7gkPcYymR8Jdw/o7mp5ZFihxn6h8Ci6fh3Dx4E1gPjpQEpIuPo9XVNY/ZUwh4BPMjGyL01g==", + "node_modules/faye-websocket": { + "version": "0.11.4", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz", + "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==", "dev": true, + "dependencies": { + "websocket-driver": ">=0.5.1" + }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">=0.8.0" } }, - "node_modules/inquirer": { - "version": "9.2.14", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-9.2.14.tgz", - "integrity": "sha512-4ByIMt677Iz5AvjyKrDpzaepIyMewNvDcvwpVVRZNmy9dLakVoVgdCHZXbK1SlVJra1db0JZ6XkJyHsanpdrdQ==", + "node_modules/fb-watchman": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", + "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", "dev": true, "dependencies": { - "@ljharb/through": "^2.3.12", - "ansi-escapes": "^4.3.2", - "chalk": "^5.3.0", - "cli-cursor": "^3.1.0", - "cli-width": "^4.1.0", - "external-editor": "^3.1.0", - "figures": "^3.2.0", - "lodash": "^4.17.21", - "mute-stream": "1.0.0", - "ora": "^5.4.1", - "run-async": "^3.0.0", - "rxjs": "^7.8.1", - "string-width": "^4.2.3", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^6.2.0" - }, - "engines": { - "node": ">=18" + "bser": "2.1.1" } }, - "node_modules/inquirer/node_modules/chalk": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", - "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", "dev": true, - "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" + "dependencies": { + "to-regex-range": "^5.0.1" }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "engines": { + "node": ">=8" } }, - "node_modules/internal-slot": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.7.tgz", - "integrity": "sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==", + "node_modules/finalhandler": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", + "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "dev": true, "dependencies": { - "es-errors": "^1.3.0", - "hasown": "^2.0.0", - "side-channel": "^1.0.4" + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "statuses": "~1.5.0", + "unpipe": "~1.0.0" }, "engines": { - "node": ">= 0.4" + "node": ">= 0.8" } }, - "node_modules/invariant": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", - "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", + "node_modules/finalhandler/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "dev": true, "dependencies": { - "loose-envify": "^1.0.0" + "ms": "2.0.0" } }, - "node_modules/ip": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/ip/-/ip-2.0.1.tgz", - "integrity": "sha512-lJUL9imLTNi1ZfXT+DU6rBBdbiKGBuay9B6xGSPVjUeQwaH1RIGqef8RZkUtHioLmSNpPR5M4HVKJGm1j8FWVQ==", + "node_modules/finalhandler/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "dev": true }, - "node_modules/ip-address": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-9.0.5.tgz", - "integrity": "sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==", + "node_modules/finalhandler/node_modules/on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==", "dev": true, "dependencies": { - "jsbn": "1.1.0", - "sprintf-js": "^1.1.3" + "ee-first": "1.1.1" }, "engines": { - "node": ">= 12" + "node": ">= 0.8" } }, - "node_modules/ip-address/node_modules/sprintf-js": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", - "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==", - "dev": true - }, - "node_modules/ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "node_modules/finalhandler/node_modules/statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", "dev": true, "engines": { - "node": ">= 0.10" + "node": ">= 0.6" } }, - "node_modules/is-absolute-url": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-3.0.3.tgz", - "integrity": "sha512-opmNIX7uFnS96NtPmhWQgQx6/NYFgsUXYMllcfzwWKUMwfo8kku1TvE6hkNcH+Q1ts5cMVrsY7j0bxXQDciu9Q==", + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-arguments": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", - "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" }, "engines": { - "node": ">= 0.4" + "node": ">=10" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/is-array-buffer": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.4.tgz", - "integrity": "sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==", - "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.2.1" - }, + "node_modules/flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "dev": true, + "peer": true, + "bin": { + "flat": "cli.js" + } + }, + "node_modules/follow-redirects": { + "version": "1.15.6", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", + "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "peer": true, "engines": { - "node": ">= 0.4" + "node": ">=4.0" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "peerDependenciesMeta": { + "debug": { + "optional": true + } } }, - "node_modules/is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", - "dev": true + "node_modules/for-each": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "dependencies": { + "is-callable": "^1.1.3" + } }, - "node_modules/is-async-function": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.0.0.tgz", - "integrity": "sha512-Y1JXKrfykRJGdlDwdKlLpLyMIiWqWvuSd17TvZk68PLAOGOoF4Xyav1z0Xhoi+gCYjZVeC5SI+hYFOfvXmGRCA==", + "node_modules/foreground-child": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz", + "integrity": "sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==", "dev": true, - "peer": true, "dependencies": { - "has-tostringtag": "^1.0.0" + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" }, "engines": { - "node": ">= 0.4" + "node": ">=14" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/is-bigint": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", - "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", - "dependencies": { - "has-bigints": "^1.0.1" + "node_modules/foreground-child/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "engines": { + "node": ">=14" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", "dev": true, "dependencies": { - "binary-extensions": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-boolean-object": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", - "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", - "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">= 6" } }, - "node_modules/is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "dev": true, + "engines": { + "node": ">= 0.6" + } }, - "node_modules/is-callable": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", - "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "node_modules/fraction.js": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", + "integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==", + "dev": true, + "peer": true, "engines": { - "node": ">= 0.4" + "node": "*" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "type": "patreon", + "url": "https://github.com/sponsors/rawify" } }, - "node_modules/is-ci": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-3.0.1.tgz", - "integrity": "sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ==", + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", "dev": true, - "dependencies": { - "ci-info": "^3.2.0" - }, - "bin": { - "is-ci": "bin.js" + "engines": { + "node": ">= 0.6" } }, - "node_modules/is-core-module": { - "version": "2.13.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", - "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", + "node_modules/from": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/from/-/from-0.1.7.tgz", + "integrity": "sha512-twe20eF1OxVxp/ML/kq2p1uc6KvFK/+vs8WjEbeKmV2He22MKm7YF2ANIt+EOqhJ5L3K/SuuPhk0hWQDjOM23g==", + "dev": true + }, + "node_modules/fs-extra": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz", + "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==", + "dev": true, "dependencies": { - "hasown": "^2.0.0" + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">=14.14" } }, - "node_modules/is-date-object": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", - "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", + "node_modules/fs-minipass": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-3.0.3.tgz", + "integrity": "sha512-XUBA9XClHbnJWSfBzjkm6RvPsyg3sryZt06BEQoXcF7EK/xpGaQYJgQKDJSUH5SGZ76Y7pFx1QBnXz09rU5Fbw==", + "dev": true, "dependencies": { - "has-tostringtag": "^1.0.0" + "minipass": "^7.0.3" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/is-deflate": { + "node_modules/fs.realpath": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-deflate/-/is-deflate-1.0.0.tgz", - "integrity": "sha512-YDoFpuZWu1VRXlsnlYMzKyVRITXj7Ej/V9gXQ2/pAe7X1J7M/RNOqaIYi6qUn+B7nGyB9pDXrv02dsB58d2ZAQ==", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", "dev": true }, - "node_modules/is-docker": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", - "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", "dev": true, - "bin": { - "is-docker": "cli.js" - }, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "engines": { - "node": ">=0.10.0" + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-finalizationregistry": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.0.2.tgz", - "integrity": "sha512-0by5vtUJs8iFQb5TYUHHPudOR+qXYIMKtiUzvLIZITZUjknFmziyBJuLhVRc+Ds0dREFlskDNJKYIdIzu/9pfw==", + "node_modules/functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", "dev": true, - "peer": true, - "dependencies": { - "call-bind": "^1.0.2" - }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", "dev": true, "engines": { - "node": ">=8" + "node": ">=6.9.0" } }, - "node_modules/is-generator-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", - "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", + "node_modules/get-assigned-identifiers": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/get-assigned-identifiers/-/get-assigned-identifiers-1.2.0.tgz", + "integrity": "sha512-mBBwmeGTrxEMO4pMaaf/uUEFHnYtwr8FTe8Y/mer4rcV/bye0qGm6pw1bGZFGStxC5O76c5ZAVBGnqHmOaJpdQ==", + "dev": true + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", "dev": true, "engines": { - "node": ">=6" + "node": "6.* || 8.* || >= 10.*" } }, - "node_modules/is-generator-function": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", - "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", - "dependencies": { - "has-tostringtag": "^1.0.0" - }, + "node_modules/get-east-asian-width": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.2.0.tgz", + "integrity": "sha512-2nk+7SIVb14QrgXFHcm84tD4bKQz0RxPuMT8Ag5KPOq7J5fEmAg0UbXdTOSHqNuHSU28k55qnceesxXRZGzKWA==", + "dev": true, "engines": { - "node": ">= 0.4" + "node": ">=18" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "node_modules/get-intrinsic": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", + "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", "dependencies": { - "is-extglob": "^2.1.1" + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "hasown": "^2.0.0" }, "engines": { - "node": ">=0.10.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-gzip": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-gzip/-/is-gzip-1.0.0.tgz", - "integrity": "sha512-rcfALRIb1YewtnksfRIHGcIY93QnK8BIQ/2c9yDYcG/Y6+vRoJuTWBmmSEbyLLYtXm7q35pHOHbZFQBaLrhlWQ==", + "node_modules/get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", "dev": true, "engines": { - "node": ">=0.10.0" + "node": ">=8.0.0" } }, - "node_modules/is-installed-globally": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.4.0.tgz", - "integrity": "sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ==", + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", "dev": true, - "dependencies": { - "global-dirs": "^3.0.0", - "is-path-inside": "^3.0.2" - }, "engines": { "node": ">=10" }, @@ -19996,91 +11500,102 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/is-interactive": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", - "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", + "node_modules/git-raw-commits": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/git-raw-commits/-/git-raw-commits-2.0.11.tgz", + "integrity": "sha512-VnctFhw+xfj8Va1xtfEqCUD2XDrbAPSJx+hSrE5K7fGdjZruW7XV+QOrN7LF/RJyvspRiD2I0asWsxFp0ya26A==", "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-invalid-path": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-invalid-path/-/is-invalid-path-0.1.0.tgz", - "integrity": "sha512-aZMG0T3F34mTg4eTdszcGXx54oiZ4NtHSft3hWNJMGJXUUqdIj3cOZuHcU0nCWWcY3jd7yRe/3AEm3vSNTpBGQ==", "dependencies": { - "is-glob": "^2.0.0" + "dargs": "^7.0.0", + "lodash": "^4.17.15", + "meow": "^8.0.0", + "split2": "^3.0.0", + "through2": "^4.0.0" + }, + "bin": { + "git-raw-commits": "cli.js" }, "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-invalid-path/node_modules/is-extglob": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", - "integrity": "sha512-7Q+VbVafe6x2T+Tu6NcOf6sRklazEPmBoB3IWk3WdGZM2iGUwU/Oe3Wtq5lSEkDTTlpp8yx+5t4pzO/i9Ty1ww==", - "engines": { - "node": ">=0.10.0" + "node": ">=10" } }, - "node_modules/is-invalid-path/node_modules/is-glob": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", - "integrity": "sha512-a1dBeB19NXsf/E0+FHqkagizel/LQw2DjSQpvQrj3zT+jYPpaUCryPnrQajXKFLCMuf4I6FhRpaGtw4lPrG6Eg==", + "node_modules/git-raw-commits/node_modules/hosted-git-info": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", + "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==", + "dev": true, "dependencies": { - "is-extglob": "^1.0.0" + "lru-cache": "^6.0.0" }, "engines": { - "node": ">=0.10.0" + "node": ">=10" } }, - "node_modules/is-lambda": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-lambda/-/is-lambda-1.0.1.tgz", - "integrity": "sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==", - "dev": true - }, - "node_modules/is-map": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.2.tgz", - "integrity": "sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg==", + "node_modules/git-raw-commits/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" } }, - "node_modules/is-nan": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/is-nan/-/is-nan-1.3.2.tgz", - "integrity": "sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w==", + "node_modules/git-raw-commits/node_modules/meow": { + "version": "8.1.2", + "resolved": "https://registry.npmjs.org/meow/-/meow-8.1.2.tgz", + "integrity": "sha512-r85E3NdZ+mpYk1C6RjPFEMSE+s1iZMuHtsHAqY0DT3jZczl0diWUZ8g6oU7h0M9cD2EL+PzaYghhCLzR0ZNn5Q==", "dev": true, "dependencies": { - "call-bind": "^1.0.0", - "define-properties": "^1.1.3" + "@types/minimist": "^1.2.0", + "camelcase-keys": "^6.2.2", + "decamelize-keys": "^1.1.0", + "hard-rejection": "^2.1.0", + "minimist-options": "4.1.0", + "normalize-package-data": "^3.0.0", + "read-pkg-up": "^7.0.1", + "redent": "^3.0.0", + "trim-newlines": "^3.0.0", + "type-fest": "^0.18.0", + "yargs-parser": "^20.2.3" }, "engines": { - "node": ">= 0.4" + "node": ">=10" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/is-negative-zero": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", - "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", - "engines": { - "node": ">= 0.4" + "node_modules/git-raw-commits/node_modules/normalize-package-data": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.3.tgz", + "integrity": "sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==", + "dev": true, + "dependencies": { + "hosted-git-info": "^4.0.1", + "is-core-module": "^2.5.0", + "semver": "^7.3.4", + "validate-npm-package-license": "^3.0.1" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">=10" } }, - "node_modules/is-npm": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-5.0.0.tgz", - "integrity": "sha512-WW/rQLOazUq+ST/bCAVBp/2oMERWLsR7OrKyt052dNDk4DHcDE0/7QSXITlmi+VBcV13DfIbysG3tZJm5RfdBA==", + "node_modules/git-raw-commits/node_modules/split2": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/split2/-/split2-3.2.2.tgz", + "integrity": "sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg==", + "dev": true, + "dependencies": { + "readable-stream": "^3.0.0" + } + }, + "node_modules/git-raw-commits/node_modules/type-fest": { + "version": "0.18.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.18.1.tgz", + "integrity": "sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==", "dev": true, "engines": { "node": ">=10" @@ -20089,1404 +11604,1432 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "engines": { - "node": ">=0.12.0" - } + "node_modules/git-raw-commits/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true }, - "node_modules/is-number-object": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", - "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", + "node_modules/glob": { + "version": "10.3.10", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz", + "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==", + "dev": true, "dependencies": { - "has-tostringtag": "^1.0.0" + "foreground-child": "^3.1.0", + "jackspeak": "^2.3.5", + "minimatch": "^9.0.1", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0", + "path-scurry": "^1.10.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" }, "engines": { - "node": ">= 0.4" + "node": ">=16 || 14 >=14.17" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/is-obj": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", - "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, "engines": { - "node": ">=8" + "node": ">= 6" } }, - "node_modules/is-path-cwd": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz", - "integrity": "sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==", + "node_modules/glob-to-regexp": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", + "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", + "dev": true + }, + "node_modules/global-prefix": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-3.0.0.tgz", + "integrity": "sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==", "dev": true, + "dependencies": { + "ini": "^1.3.5", + "kind-of": "^6.0.2", + "which": "^1.3.1" + }, "engines": { "node": ">=6" } }, - "node_modules/is-path-inside": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", - "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", - "engines": { - "node": ">=8" + "node_modules/global-prefix/node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "dev": true + }, + "node_modules/global-prefix/node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" } }, - "node_modules/is-plain-obj": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-3.0.0.tgz", - "integrity": "sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==", + "node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", "dev": true, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=4" } }, - "node_modules/is-plain-object": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", - "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "node_modules/globby": { + "version": "14.0.2", + "resolved": "https://registry.npmjs.org/globby/-/globby-14.0.2.tgz", + "integrity": "sha512-s3Fq41ZVh7vbbe2PN3nrW7yC7U7MFVc5c98/iTl9c2GawNMKx/J648KQRW6WKkuU8GIbbh2IXfIRQjOZnXcTnw==", "dev": true, + "peer": true, "dependencies": { - "isobject": "^3.0.1" + "@sindresorhus/merge-streams": "^2.1.0", + "fast-glob": "^3.3.2", + "ignore": "^5.2.4", + "path-type": "^5.0.0", + "slash": "^5.1.0", + "unicorn-magic": "^0.1.0" }, "engines": { - "node": ">=0.10.0" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/is-potential-custom-element-name": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", - "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", - "dev": true - }, - "node_modules/is-promise": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.2.2.tgz", - "integrity": "sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==", - "dev": true - }, - "node_modules/is-regex": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", - "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", - "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - }, + "node_modules/globby/node_modules/path-type": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-5.0.0.tgz", + "integrity": "sha512-5HviZNaZcfqP95rwpv+1HDgUamezbqdSYTyzjTvwtJSnIH+3vnbmWsItli8OFEndS984VT55M3jduxZbX351gg==", + "dev": true, + "peer": true, "engines": { - "node": ">= 0.4" + "node": ">=12" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/is-set": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.2.tgz", - "integrity": "sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g==", + "node_modules/globby/node_modules/slash": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-5.1.0.tgz", + "integrity": "sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==", "dev": true, + "peer": true, + "engines": { + "node": ">=14.16" + }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/is-shared-array-buffer": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.3.tgz", - "integrity": "sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==", + "node_modules/gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", "dependencies": { - "call-bind": "^1.0.7" - }, - "engines": { - "node": ">= 0.4" + "get-intrinsic": "^1.1.3" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "node_modules/govuk-frontend": { + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/govuk-frontend/-/govuk-frontend-4.8.0.tgz", + "integrity": "sha512-NOmPJxL8IYq1HSNHYKx9XY2LLTxuwb+IFASiGQO4sgJ8K7AG66SlSeqARrcetevV8zOf+i1z+MbJJ2O7//OxAw==", + "license": "MIT", + "engines": { + "node": ">= 4.2.0" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true + }, + "node_modules/hammerjs": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/hammerjs/-/hammerjs-2.0.8.tgz", + "integrity": "sha512-tSQXBXS/MWQOn/RKckawJ61vvsDpCom87JgxiYdGwHdOa0ht0vzUWDlfioofFCRU0L+6NGDt6XzbgoJvZkMeRQ==", "dev": true, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=0.8.0" } }, - "node_modules/is-string": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", - "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "node_modules/handle-thing": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", + "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==", + "dev": true, + "peer": true + }, + "node_modules/handlebars": { + "version": "4.7.8", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", + "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==", + "dev": true, "dependencies": { - "has-tostringtag": "^1.0.0" + "minimist": "^1.2.5", + "neo-async": "^2.6.2", + "source-map": "^0.6.1", + "wordwrap": "^1.0.0" + }, + "bin": { + "handlebars": "bin/handlebars" }, "engines": { - "node": ">= 0.4" + "node": ">=0.4.7" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "optionalDependencies": { + "uglify-js": "^3.1.4" } }, - "node_modules/is-symbol": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", - "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", - "dependencies": { - "has-symbols": "^1.0.2" - }, + "node_modules/handlebars/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=0.10.0" } }, - "node_modules/is-text-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-text-path/-/is-text-path-2.0.0.tgz", - "integrity": "sha512-+oDTluR6WEjdXEJMnC2z6A4FRwFoYuvShVVEGsS7ewc0UTi2QtAKMDJuL4BDEVt+5T7MjFo12RP8ghOM75oKJw==", + "node_modules/hard-rejection": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/hard-rejection/-/hard-rejection-2.1.0.tgz", + "integrity": "sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==", "dev": true, - "dependencies": { - "text-extensions": "^2.0.0" - }, "engines": { - "node": ">=8" + "node": ">=6" } }, - "node_modules/is-typed-array": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.13.tgz", - "integrity": "sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==", - "dependencies": { - "which-typed-array": "^1.1.14" - }, + "node_modules/has": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.4.tgz", + "integrity": "sha512-qdSAmqLF6209RFj4VVItywPMbm3vWylknmB3nvNiUIs72xAimcM8nVYxYr7ncvZq5qzk9MKIZR8ijqD/1QuYjQ==", + "dev": true, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">= 0.4.0" } }, - "node_modules/is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", - "dev": true - }, - "node_modules/is-unicode-supported": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", - "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", "dev": true, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=4" } }, - "node_modules/is-valid-path": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-valid-path/-/is-valid-path-0.1.1.tgz", - "integrity": "sha512-+kwPrVDu9Ms03L90Qaml+79+6DZHqHyRoANI6IsZJ/g8frhnfchDOBCa0RbQ6/kdHt5CS5OeIEyrYznNuVN+8A==", + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", "dependencies": { - "is-invalid-path": "^0.1.0" + "es-define-property": "^1.0.0" }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-weakmap": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.1.tgz", - "integrity": "sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA==", - "dev": true, "funding": { "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-weakref": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", - "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", - "dependencies": { - "call-bind": "^1.0.2" + "node_modules/has-proto": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", + "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-weakset": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.2.tgz", - "integrity": "sha512-t2yVvttHkQktwnNNmBQ98AhENLdPUTDTE21uPqAQ0ARwQfGeQKRVS0NNurH7bTf7RrvcVn1OOge45CnBeHCSmg==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.1" + "node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-what": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/is-what/-/is-what-3.14.1.tgz", - "integrity": "sha512-sNxgpk9793nzSs7bA6JQJGeIuRBQhAaNGG77kzYQgMkrID+lS6SlK07K5LaptscDlSaIgH+GPFzf+d75FVxozA==", - "dev": true - }, - "node_modules/is-wsl": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", - "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", - "dev": true, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", "dependencies": { - "is-docker": "^2.0.0" + "has-symbols": "^1.0.3" }, "engines": { - "node": ">=8" - } - }, - "node_modules/isarray": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", - "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==" - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" - }, - "node_modules/isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/isstream": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==", - "dev": true - }, - "node_modules/istanbul-lib-coverage": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", - "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", - "dev": true, - "engines": { - "node": ">=8" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/istanbul-lib-instrument": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", - "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", - "dev": true, + "node_modules/hasown": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.1.tgz", + "integrity": "sha512-1/th4MHjnwncwXsIW6QMzlvYL9kG5e/CpVvLRZe4XPa8TOUNbCELqmvhDmnkNsAjwaG4+I8gJJL0JBvTTLO9qA==", "dependencies": { - "@babel/core": "^7.12.3", - "@babel/parser": "^7.14.7", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.2.0", - "semver": "^6.3.0" + "function-bind": "^1.1.2" }, "engines": { - "node": ">=8" + "node": ">= 0.4" } }, - "node_modules/istanbul-lib-instrument/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", "dev": true, + "optional": true, + "peer": true, "bin": { - "semver": "bin/semver.js" + "he": "bin/he" } }, - "node_modules/istanbul-lib-report": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", - "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", + "node_modules/hosted-git-info": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-7.0.2.tgz", + "integrity": "sha512-puUZAUKT5m8Zzvs72XWy3HtvVbTWljRE66cP60bxJzAqf2DgICo7lYTY2IHUmLnNpjYvw5bvmoHvPc0QO2a62w==", "dev": true, "dependencies": { - "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^4.0.0", - "supports-color": "^7.1.0" + "lru-cache": "^10.0.1" }, "engines": { - "node": ">=10" + "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/istanbul-lib-report/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "node_modules/hosted-git-info/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true + }, + "node_modules/hpack.js": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", + "integrity": "sha512-zJxVehUdMGIKsRaNt7apO2Gqp0BdqW5yaiGHXXmbpvxgBYVZnAql+BJb4RO5ad2MgpbZKn5G6nMnegrH1FcNYQ==", "dev": true, - "engines": { - "node": ">=8" + "peer": true, + "dependencies": { + "inherits": "^2.0.1", + "obuf": "^1.0.0", + "readable-stream": "^2.0.1", + "wbuf": "^1.1.0" } }, - "node_modules/istanbul-lib-report/node_modules/make-dir": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", - "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "node_modules/hpack.js/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", "dev": true, + "peer": true, "dependencies": { - "semver": "^7.5.3" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" } }, - "node_modules/istanbul-lib-report/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "node_modules/hpack.js/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, + "peer": true, "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" + "safe-buffer": "~5.1.0" } }, - "node_modules/istanbul-lib-source-maps": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", - "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", + "node_modules/html-encoding-sniffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz", + "integrity": "sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==", "dev": true, "dependencies": { - "debug": "^4.1.1", - "istanbul-lib-coverage": "^3.0.0", - "source-map": "^0.6.1" + "whatwg-encoding": "^2.0.0" }, "engines": { - "node": ">=10" + "node": ">=12" } }, - "node_modules/istanbul-lib-source-maps/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "node_modules/html-entities": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.4.0.tgz", + "integrity": "sha512-igBTJcNNNhvZFRtm8uA6xMY6xYleeDwn3PeBCkDz7tHttv4F2hsDI2aPgNERWzvRcNYHNT3ymRaQzllmXj4YsQ==", "dev": true, - "engines": { - "node": ">=0.10.0" - } + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/mdevils" + }, + { + "type": "patreon", + "url": "https://patreon.com/mdevils" + } + ] }, - "node_modules/istanbul-reports": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz", - "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==", + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true + }, + "node_modules/html-minifier-terser": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", + "integrity": "sha512-YXxSlJBZTP7RS3tWnQw74ooKa6L9b9i9QYXY21eUEvhZ3u9XLfv6OnFsQq6RxkhHygsaUMvYsZRV5rU/OVNZxw==", "dev": true, + "optional": true, + "peer": true, "dependencies": { - "html-escaper": "^2.0.0", - "istanbul-lib-report": "^3.0.0" + "camel-case": "^4.1.2", + "clean-css": "^5.2.2", + "commander": "^8.3.0", + "he": "^1.2.0", + "param-case": "^3.0.4", + "relateurl": "^0.2.7", + "terser": "^5.10.0" + }, + "bin": { + "html-minifier-terser": "cli.js" }, "engines": { - "node": ">=8" + "node": ">=12" } }, - "node_modules/iterator.prototype": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.2.tgz", - "integrity": "sha512-DR33HMMr8EzwuRL8Y9D3u2BMj8+RqSE850jfGu59kS7tbmPLzGkZmVSfyCFSDxuZiEY6Rzt3T2NA/qU+NwVj1w==", + "node_modules/html-minifier-terser/node_modules/commander": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", + "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", "dev": true, + "optional": true, "peer": true, - "dependencies": { - "define-properties": "^1.2.1", - "get-intrinsic": "^1.2.1", - "has-symbols": "^1.0.3", - "reflect.getprototypeof": "^1.0.4", - "set-function-name": "^2.0.1" + "engines": { + "node": ">= 12" } }, - "node_modules/jackspeak": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz", - "integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==", + "node_modules/html-webpack-plugin": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.6.0.tgz", + "integrity": "sha512-iwaY4wzbe48AfKLZ/Cc8k0L+FKG6oSNRaZ8x5A/T/IVDGyXcbHncM9TdDa93wn0FsSm82FhTKW7f3vS61thXAw==", "dev": true, + "optional": true, + "peer": true, "dependencies": { - "@isaacs/cliui": "^8.0.2" + "@types/html-minifier-terser": "^6.0.0", + "html-minifier-terser": "^6.0.2", + "lodash": "^4.17.21", + "pretty-error": "^4.0.0", + "tapable": "^2.0.0" }, "engines": { - "node": ">=14" + "node": ">=10.13.0" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "type": "opencollective", + "url": "https://opencollective.com/html-webpack-plugin" }, - "optionalDependencies": { - "@pkgjs/parseargs": "^0.11.0" + "peerDependencies": { + "@rspack/core": "0.x || 1.x", + "webpack": "^5.20.0" + }, + "peerDependenciesMeta": { + "@rspack/core": { + "optional": true + }, + "webpack": { + "optional": true + } } }, - "node_modules/jake": { - "version": "10.8.7", - "resolved": "https://registry.npmjs.org/jake/-/jake-10.8.7.tgz", - "integrity": "sha512-ZDi3aP+fG/LchyBzUM804VjddnwfSfsdeYkwt8NcbKRvo4rFkjhs456iLFn3k2ZUWvNe4i48WACDbza8fhq2+w==", + "node_modules/htmlparser2": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz", + "integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==", "dev": true, + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], "dependencies": { - "async": "^3.2.3", - "chalk": "^4.0.2", - "filelist": "^1.0.4", - "minimatch": "^3.1.2" - }, - "bin": { - "jake": "bin/cli.js" - }, - "engines": { - "node": ">=10" + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1", + "entities": "^4.4.0" } }, - "node_modules/jake/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/http-auth": { + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/http-auth/-/http-auth-4.1.9.tgz", + "integrity": "sha512-kvPYxNGc9EKGTXvOMnTBQw2RZfuiSihK/mLw/a4pbtRueTE45S55Lw/3k5CktIf7Ak0veMKEIteDj4YkNmCzmQ==", "dev": true, "dependencies": { - "color-convert": "^2.0.1" + "apache-crypt": "^1.1.2", + "apache-md5": "^1.0.6", + "bcryptjs": "^2.4.3", + "uuid": "^8.3.2" }, "engines": { "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/jake/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "node_modules/http-auth-connect": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/http-auth-connect/-/http-auth-connect-1.0.6.tgz", + "integrity": "sha512-yaO0QSCPqGCjPrl3qEEHjJP+lwZ6gMpXLuCBE06eWwcXomkI5TARtu0kxf9teFuBj6iaV3Ybr15jaWUvbzNzHw==", "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "engines": { + "node": ">=8" } }, - "node_modules/jake/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/http-auth/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "bin": { + "uuid": "dist/bin/uuid" } }, - "node_modules/jake/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "node_modules/http-cache-semantics": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", + "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==", + "dev": true + }, + "node_modules/http-deceiver": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", + "integrity": "sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw==", "dev": true, - "engines": { - "node": ">=8" - } + "peer": true }, - "node_modules/jake/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", "dev": true, "dependencies": { - "brace-expansion": "^1.1.7" + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" }, "engines": { - "node": "*" + "node": ">= 0.8" } }, - "node_modules/jake/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "node_modules/http-parser-js": { + "version": "0.5.8", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.8.tgz", + "integrity": "sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q==", + "dev": true + }, + "node_modules/http-proxy": { + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", + "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", "dev": true, + "peer": true, "dependencies": { - "has-flag": "^4.0.0" + "eventemitter3": "^4.0.0", + "follow-redirects": "^1.0.0", + "requires-port": "^1.0.0" }, "engines": { - "node": ">=8" + "node": ">=8.0.0" } }, - "node_modules/jest": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz", - "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==", + "node_modules/http-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", + "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", "dev": true, "dependencies": { - "@jest/core": "^29.7.0", - "@jest/types": "^29.6.3", - "import-local": "^3.0.2", - "jest-cli": "^29.7.0" - }, - "bin": { - "jest": "bin/jest.js" + "@tootallnate/once": "2", + "agent-base": "6", + "debug": "4" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } + "node": ">= 6" } }, - "node_modules/jest-changed-files": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.7.0.tgz", - "integrity": "sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==", + "node_modules/http-proxy-agent/node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", "dev": true, "dependencies": { - "execa": "^5.0.0", - "jest-util": "^29.7.0", - "p-limit": "^3.1.0" + "debug": "4" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">= 6.0.0" } }, - "node_modules/jest-circus": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.7.0.tgz", - "integrity": "sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==", + "node_modules/http-proxy-middleware": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-3.0.0.tgz", + "integrity": "sha512-36AV1fIaI2cWRzHo+rbcxhe3M3jUDCNzc4D5zRl57sEWRAxdXYtw7FSQKYY6PDKssiAKjLYypbssHk+xs/kMXw==", "dev": true, + "peer": true, "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/expect": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "co": "^4.6.0", - "dedent": "^1.0.0", - "is-generator-fn": "^2.0.0", - "jest-each": "^29.7.0", - "jest-matcher-utils": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-runtime": "^29.7.0", - "jest-snapshot": "^29.7.0", - "jest-util": "^29.7.0", - "p-limit": "^3.1.0", - "pretty-format": "^29.7.0", - "pure-rand": "^6.0.0", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" + "@types/http-proxy": "^1.17.10", + "debug": "^4.3.4", + "http-proxy": "^1.18.1", + "is-glob": "^4.0.1", + "is-plain-obj": "^3.0.0", + "micromatch": "^4.0.5" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/jest-circus/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/https-proxy-agent": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz", + "integrity": "sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==", "dev": true, "dependencies": { - "color-convert": "^2.0.1" + "agent-base": "^7.0.2", + "debug": "4" }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "node": ">= 14" } }, - "node_modules/jest-circus/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/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/husky": { + "version": "9.0.11", + "resolved": "https://registry.npmjs.org/husky/-/husky-9.0.11.tgz", + "integrity": "sha512-AB6lFlbwwyIqMdHYhwPe+kjOC3Oc5P3nThEoW/AaO2BX3vJDjWPFxYLxokUZOo6RNX20He3AaT8sESs9NJcmEw==", + "dev": true, + "bin": { + "husky": "bin.mjs" }, "engines": { - "node": ">=10" + "node": ">=18" }, "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "url": "https://github.com/sponsors/typicode" } }, - "node_modules/jest-circus/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "node_modules/hyperdyperid": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/hyperdyperid/-/hyperdyperid-1.2.0.tgz", + "integrity": "sha512-Y93lCzHYgGWdrJ66yIktxiaGULYc6oGiABxhcO5AufBeOyoIdZF7bIfLaOrbM0iGIOXQQgxxRrFEnb+Y6w1n4A==", "dev": true, + "peer": true, "engines": { - "node": ">=8" + "node": ">=10.18" } }, - "node_modules/jest-circus/node_modules/pretty-format": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", - "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "node_modules/i18next": { + "version": "23.10.0", + "resolved": "https://registry.npmjs.org/i18next/-/i18next-23.10.0.tgz", + "integrity": "sha512-/TgHOqsa7/9abUKJjdPeydoyDc0oTi/7u9F8lMSj6ufg4cbC1Oj3f/Jja7zj7WRIhEQKB7Q4eN6y68I9RDxxGQ==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://locize.com" + }, + { + "type": "individual", + "url": "https://locize.com/i18next.html" + }, + { + "type": "individual", + "url": "https://www.i18next.com/how-to/faq#i18next-is-awesome.-how-can-i-support-the-project" + } + ], + "dependencies": { + "@babel/runtime": "^7.23.2" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", "dev": true, "dependencies": { - "@jest/schemas": "^29.6.3", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" + "safer-buffer": ">= 2.1.2 < 3" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=0.10.0" } }, - "node_modules/jest-circus/node_modules/pretty-format/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "node_modules/icss-utils": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz", + "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==", "dev": true, + "peer": true, "engines": { - "node": ">=10" + "node": "^10 || ^12 || >= 14" }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "peerDependencies": { + "postcss": "^8.1.0" } }, - "node_modules/jest-circus/node_modules/react-is": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", - "dev": true + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] }, - "node_modules/jest-circus/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, + "peer": true, "engines": { - "node": ">=8" + "node": ">= 4" } }, - "node_modules/jest-cli": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.7.0.tgz", - "integrity": "sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==", + "node_modules/ignore-walk": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-6.0.5.tgz", + "integrity": "sha512-VuuG0wCnjhnylG1ABXT3dAuIpTNDs/G8jlpmwXY03fXoXy/8ZK8/T+hMzt8L4WnrLCJgdybqgPagnF/f97cg3A==", "dev": true, "dependencies": { - "@jest/core": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/types": "^29.6.3", - "chalk": "^4.0.0", - "create-jest": "^29.7.0", - "exit": "^0.1.2", - "import-local": "^3.0.2", - "jest-config": "^29.7.0", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "yargs": "^17.3.1" + "minimatch": "^9.0.0" }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/image-size": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/image-size/-/image-size-0.5.5.tgz", + "integrity": "sha512-6TDAlDPZxUFCv+fuOkIoXT/V/f3Qbq8e37p+YOiYrUv3v9cc3/6x78VdfPgFVaB9dZYeLUfKgHRebpkm/oP2VQ==", + "dev": true, + "optional": true, + "peer": true, "bin": { - "jest": "bin/jest.js" + "image-size": "bin/image-size.js" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } + "node": ">=0.10.0" } }, - "node_modules/jest-cli/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/immutable": { + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.5.tgz", + "integrity": "sha512-8eabxkth9gZatlwl5TBuJnCsoTADlL6ftEr7A4qgdaTsPyreilDSnUk57SO+jfKcNtxPa22U5KK6DSeAYhpBJw==", + "dev": true + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", "dev": true, "dependencies": { - "color-convert": "^2.0.1" + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" }, "engines": { - "node": ">=8" + "node": ">=6" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/jest-cli/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/import-fresh/node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/import-local": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", + "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", "dev": true, "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + }, + "bin": { + "import-local-fixture": "fixtures/cli.js" }, "engines": { - "node": ">=10" + "node": ">=8" }, "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/jest-cli/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "node_modules/import-local/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, "engines": { "node": ">=8" } }, - "node_modules/jest-cli/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "node_modules/import-local/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", "dev": true, "dependencies": { - "has-flag": "^4.0.0" + "p-locate": "^4.1.0" }, "engines": { "node": ">=8" } }, - "node_modules/jest-config": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.7.0.tgz", - "integrity": "sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==", + "node_modules/import-local/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", "dev": true, "dependencies": { - "@babel/core": "^7.11.6", - "@jest/test-sequencer": "^29.7.0", - "@jest/types": "^29.6.3", - "babel-jest": "^29.7.0", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "deepmerge": "^4.2.2", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "jest-circus": "^29.7.0", - "jest-environment-node": "^29.7.0", - "jest-get-type": "^29.6.3", - "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.7.0", - "jest-runner": "^29.7.0", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "micromatch": "^4.0.4", - "parse-json": "^5.2.0", - "pretty-format": "^29.7.0", - "slash": "^3.0.0", - "strip-json-comments": "^3.1.1" + "p-try": "^2.0.0" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "@types/node": "*", - "ts-node": ">=9.0.0" + "node": ">=6" }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "ts-node": { - "optional": true - } + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/jest-config/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/import-local/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", "dev": true, "dependencies": { - "color-convert": "^2.0.1" + "p-limit": "^2.2.0" }, "engines": { "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-config/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" } }, - "node_modules/jest-config/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/import-local/node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", "dev": true, "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "find-up": "^4.0.0" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "node": ">=8" } }, - "node_modules/jest-config/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", "dev": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": ">=0.8.19" } }, - "node_modules/jest-config/node_modules/has-flag": { + "node_modules/indent-string": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", "dev": true, "engines": { "node": ">=8" } }, - "node_modules/jest-config/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", "dev": true, "dependencies": { - "brace-expansion": "^1.1.7" - }, + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/ini": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ini/-/ini-4.1.3.tgz", + "integrity": "sha512-X7rqawQBvfdjS10YU1y1YVreA3SsLrW9dX2CewP2EbBJM4ypVNLDkO5y04gejPwKIY9lR+7r9gn3rFPt/kmWFg==", + "dev": true, "engines": { - "node": "*" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/jest-config/node_modules/pretty-format": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", - "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "node_modules/ip-address": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-9.0.5.tgz", + "integrity": "sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==", "dev": true, "dependencies": { - "@jest/schemas": "^29.6.3", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" + "jsbn": "1.1.0", + "sprintf-js": "^1.1.3" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">= 12" } }, - "node_modules/jest-config/node_modules/pretty-format/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "node_modules/ip-address/node_modules/sprintf-js": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", + "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==", + "dev": true + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", "dev": true, "engines": { - "node": ">=10" + "node": ">= 0.10" + } + }, + "node_modules/is-arguments": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", + "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/jest-config/node_modules/react-is": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", "dev": true }, - "node_modules/jest-config/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", "dev": true, "dependencies": { - "has-flag": "^4.0.0" + "binary-extensions": "^2.0.0" }, "engines": { "node": ">=8" } }, - "node_modules/jest-diff": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", - "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", - "dev": true, - "dependencies": { - "chalk": "^4.0.0", - "diff-sequences": "^29.6.3", - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" - }, + "node_modules/is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" + }, + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } - }, - "node_modules/jest-diff/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + }, + "node_modules/is-core-module": { + "version": "2.13.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", + "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", "dev": true, "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" + "hasown": "^2.0.0" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/jest-diff/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/is-date-object": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", + "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", "dev": true, "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "has-tostringtag": "^1.0.0" }, "engines": { - "node": ">=10" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/jest-diff/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "node_modules/is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", "dev": true, + "bin": { + "is-docker": "cli.js" + }, "engines": { "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/jest-diff/node_modules/pretty-format": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", - "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", "dev": true, - "dependencies": { - "@jest/schemas": "^29.6.3", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=0.10.0" } }, - "node_modules/jest-diff/node_modules/pretty-format/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "dev": true, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "node": ">=8" } }, - "node_modules/jest-diff/node_modules/react-is": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", - "dev": true - }, - "node_modules/jest-diff/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "node_modules/is-generator-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", + "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, "engines": { - "node": ">=8" + "node": ">=6" } }, - "node_modules/jest-docblock": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.7.0.tgz", - "integrity": "sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==", - "dev": true, + "node_modules/is-generator-function": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", + "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", "dependencies": { - "detect-newline": "^3.0.0" + "has-tostringtag": "^1.0.0" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/jest-each": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.7.0.tgz", - "integrity": "sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==", + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "dev": true, "dependencies": { - "@jest/types": "^29.6.3", - "chalk": "^4.0.0", - "jest-get-type": "^29.6.3", - "jest-util": "^29.7.0", - "pretty-format": "^29.7.0" + "is-extglob": "^2.1.1" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=0.10.0" } }, - "node_modules/jest-each/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/is-inside-container": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", + "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", "dev": true, + "peer": true, "dependencies": { - "color-convert": "^2.0.1" + "is-docker": "^3.0.0" + }, + "bin": { + "is-inside-container": "cli.js" }, "engines": { - "node": ">=8" + "node": ">=14.16" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/jest-each/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/is-inside-container/node_modules/is-docker": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", + "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "peer": true, + "bin": { + "is-docker": "cli.js" }, "engines": { - "node": ">=10" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" }, "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/jest-each/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "node_modules/is-interactive": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", + "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", "dev": true, "engines": { "node": ">=8" } }, - "node_modules/jest-each/node_modules/pretty-format": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", - "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", - "dev": true, + "node_modules/is-invalid-path": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-invalid-path/-/is-invalid-path-0.1.0.tgz", + "integrity": "sha512-aZMG0T3F34mTg4eTdszcGXx54oiZ4NtHSft3hWNJMGJXUUqdIj3cOZuHcU0nCWWcY3jd7yRe/3AEm3vSNTpBGQ==", "dependencies": { - "@jest/schemas": "^29.6.3", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" + "is-glob": "^2.0.0" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=0.10.0" } }, - "node_modules/jest-each/node_modules/pretty-format/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, + "node_modules/is-invalid-path/node_modules/is-extglob": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", + "integrity": "sha512-7Q+VbVafe6x2T+Tu6NcOf6sRklazEPmBoB3IWk3WdGZM2iGUwU/Oe3Wtq5lSEkDTTlpp8yx+5t4pzO/i9Ty1ww==", "engines": { - "node": ">=10" + "node": ">=0.10.0" + } + }, + "node_modules/is-invalid-path/node_modules/is-glob": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", + "integrity": "sha512-a1dBeB19NXsf/E0+FHqkagizel/LQw2DjSQpvQrj3zT+jYPpaUCryPnrQajXKFLCMuf4I6FhRpaGtw4lPrG6Eg==", + "dependencies": { + "is-extglob": "^1.0.0" }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "engines": { + "node": ">=0.10.0" } }, - "node_modules/jest-each/node_modules/react-is": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", + "node_modules/is-lambda": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-lambda/-/is-lambda-1.0.1.tgz", + "integrity": "sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==", "dev": true }, - "node_modules/jest-each/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "node_modules/is-network-error": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-network-error/-/is-network-error-1.1.0.tgz", + "integrity": "sha512-tUdRRAnhT+OtCZR/LxZelH/C7QtjtFrTu5tXCA8pl55eTUElUHT+GPYV8MBMBvea/j+NxQqVt3LbWMRir7Gx9g==", "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, + "peer": true, "engines": { - "node": ">=8" + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/jest-environment-jsdom": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-29.7.0.tgz", - "integrity": "sha512-k9iQbsf9OyOfdzWH8HDmrRT0gSIcX+FLNW7IQq94tFX0gynPwqDTW0Ho6iMVNjGz/nb+l/vW3dWM2bbLLpkbXA==", + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true, - "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/fake-timers": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/jsdom": "^20.0.0", - "@types/node": "*", - "jest-mock": "^29.7.0", - "jest-util": "^29.7.0", - "jsdom": "^20.0.0" - }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "canvas": "^2.5.0" - }, - "peerDependenciesMeta": { - "canvas": { - "optional": true - } + "node": ">=0.12.0" } }, - "node_modules/jest-environment-jsdom/node_modules/jest-mock": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz", - "integrity": "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==", + "node_modules/is-obj": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", + "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-plain-obj": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-3.0.0.tgz", + "integrity": "sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==", "dev": true, - "dependencies": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-util": "^29.7.0" - }, + "peer": true, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/jest-environment-node": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz", - "integrity": "sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==", + "node_modules/is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", "dev": true, + "peer": true, "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/fake-timers": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-mock": "^29.7.0", - "jest-util": "^29.7.0" + "isobject": "^3.0.1" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=0.10.0" } }, - "node_modules/jest-environment-node/node_modules/jest-mock": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz", - "integrity": "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==", + "node_modules/is-potential-custom-element-name": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", + "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", + "dev": true + }, + "node_modules/is-promise": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.2.2.tgz", + "integrity": "sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==", + "dev": true + }, + "node_modules/is-regex": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", + "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", "dev": true, "dependencies": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-util": "^29.7.0" + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/jest-get-type": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", - "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", "dev": true, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/jest-haste-map": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", - "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", + "node_modules/is-text-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-text-path/-/is-text-path-2.0.0.tgz", + "integrity": "sha512-+oDTluR6WEjdXEJMnC2z6A4FRwFoYuvShVVEGsS7ewc0UTi2QtAKMDJuL4BDEVt+5T7MjFo12RP8ghOM75oKJw==", "dev": true, "dependencies": { - "@jest/types": "^29.6.3", - "@types/graceful-fs": "^4.1.3", - "@types/node": "*", - "anymatch": "^3.0.3", - "fb-watchman": "^2.0.0", - "graceful-fs": "^4.2.9", - "jest-regex-util": "^29.6.3", - "jest-util": "^29.7.0", - "jest-worker": "^29.7.0", - "micromatch": "^4.0.4", - "walker": "^1.0.8" + "text-extensions": "^2.0.0" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "optionalDependencies": { - "fsevents": "^2.3.2" + "node": ">=8" } }, - "node_modules/jest-leak-detector": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz", - "integrity": "sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==", - "dev": true, + "node_modules/is-typed-array": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.13.tgz", + "integrity": "sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==", "dependencies": { - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" + "which-typed-array": "^1.1.14" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/jest-leak-detector/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", "dev": true, "engines": { "node": ">=10" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/jest-leak-detector/node_modules/pretty-format": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", - "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", - "dev": true, + "node_modules/is-valid-path": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-valid-path/-/is-valid-path-0.1.1.tgz", + "integrity": "sha512-+kwPrVDu9Ms03L90Qaml+79+6DZHqHyRoANI6IsZJ/g8frhnfchDOBCa0RbQ6/kdHt5CS5OeIEyrYznNuVN+8A==", "dependencies": { - "@jest/schemas": "^29.6.3", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" + "is-invalid-path": "^0.1.0" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=0.10.0" } }, - "node_modules/jest-leak-detector/node_modules/react-is": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", - "dev": true + "node_modules/is-what": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/is-what/-/is-what-3.14.1.tgz", + "integrity": "sha512-sNxgpk9793nzSs7bA6JQJGeIuRBQhAaNGG77kzYQgMkrID+lS6SlK07K5LaptscDlSaIgH+GPFzf+d75FVxozA==", + "dev": true, + "peer": true }, - "node_modules/jest-matcher-utils": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz", - "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==", + "node_modules/is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", "dev": true, "dependencies": { - "chalk": "^4.0.0", - "jest-diff": "^29.7.0", - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" + "is-docker": "^2.0.0" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=8" } }, - "node_modules/jest-matcher-utils/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", + "dev": true, + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", + "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", "dev": true, "dependencies": { - "color-convert": "^2.0.1" + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^6.3.0" }, "engines": { "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/jest-matcher-utils/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/istanbul-lib-instrument/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", "dev": true, "dependencies": { - "ansi-styles": "^4.1.0", + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", "supports-color": "^7.1.0" }, "engines": { "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/jest-matcher-utils/node_modules/has-flag": { + "node_modules/istanbul-lib-report/node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, "engines": { - "node": ">=8" - } - }, - "node_modules/jest-matcher-utils/node_modules/pretty-format": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", - "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", - "dev": true, - "dependencies": { - "@jest/schemas": "^29.6.3", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=8" } }, - "node_modules/jest-matcher-utils/node_modules/pretty-format/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "node_modules/istanbul-lib-report/node_modules/make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", "dev": true, + "dependencies": { + "semver": "^7.5.3" + }, "engines": { "node": ">=10" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/jest-matcher-utils/node_modules/react-is": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", - "dev": true - }, - "node_modules/jest-matcher-utils/node_modules/supports-color": { + "node_modules/istanbul-lib-report/node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", @@ -21498,149 +13041,132 @@ "node": ">=8" } }, - "node_modules/jest-message-util": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz", - "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", + "node_modules/istanbul-lib-source-maps": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", + "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", "dev": true, "dependencies": { - "@babel/code-frame": "^7.12.13", - "@jest/types": "^29.6.3", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "micromatch": "^4.0.4", - "pretty-format": "^29.7.0", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=10" } }, - "node_modules/jest-message-util/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/istanbul-lib-source-maps/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "node": ">=0.10.0" } }, - "node_modules/jest-message-util/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/istanbul-reports": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz", + "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==", "dev": true, "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-message-util/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, "engines": { "node": ">=8" } }, - "node_modules/jest-message-util/node_modules/pretty-format": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", - "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "node_modules/jackspeak": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz", + "integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==", "dev": true, "dependencies": { - "@jest/schemas": "^29.6.3", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" + "@isaacs/cliui": "^8.0.2" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-message-util/node_modules/pretty-format/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, - "engines": { - "node": ">=10" + "node": ">=14" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" } }, - "node_modules/jest-message-util/node_modules/react-is": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", - "dev": true - }, - "node_modules/jest-message-util/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "node_modules/jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz", + "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==", "dev": true, "dependencies": { - "has-flag": "^4.0.0" + "@jest/core": "^29.7.0", + "@jest/types": "^29.6.3", + "import-local": "^3.0.2", + "jest-cli": "^29.7.0" + }, + "bin": { + "jest": "bin/jest.js" }, "engines": { - "node": ">=8" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } } }, - "node_modules/jest-mock": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-27.5.1.tgz", - "integrity": "sha512-K4jKbY1d4ENhbrG2zuPWaQBvDly+iZ2yAW+T1fATN78hc0sInwn7wZB8XtlNnvHug5RMwV897Xm4LqmPM4e2Og==", + "node_modules/jest-changed-files": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.7.0.tgz", + "integrity": "sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==", "dev": true, "dependencies": { - "@jest/types": "^27.5.1", - "@types/node": "*" + "execa": "^5.0.0", + "jest-util": "^29.7.0", + "p-limit": "^3.1.0" }, "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/jest-mock/node_modules/@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", + "node_modules/jest-circus": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.7.0.tgz", + "integrity": "sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==", "dev": true, "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", + "@jest/environment": "^29.7.0", + "@jest/expect": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" + "chalk": "^4.0.0", + "co": "^4.6.0", + "dedent": "^1.0.0", + "is-generator-fn": "^2.0.0", + "jest-each": "^29.7.0", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "p-limit": "^3.1.0", + "pretty-format": "^29.7.0", + "pure-rand": "^6.0.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" }, "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/jest-mock/node_modules/@types/yargs": { - "version": "16.0.9", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.9.tgz", - "integrity": "sha512-tHhzvkFXZQeTECenFoRljLBYPZJ7jAVxqqtEI0qTLOmuultnFp4I9yKE17vTuhf7BkhCu7I4XuemPgikDVuYqA==", - "dev": true, - "dependencies": { - "@types/yargs-parser": "*" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/jest-mock/node_modules/ansi-styles": { + "node_modules/jest-circus/node_modules/ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", @@ -21655,7 +13181,7 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/jest-mock/node_modules/chalk": { + "node_modules/jest-circus/node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", @@ -21671,7 +13197,7 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/jest-mock/node_modules/has-flag": { + "node_modules/jest-circus/node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", @@ -21680,138 +13206,84 @@ "node": ">=8" } }, - "node_modules/jest-mock/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-pnp-resolver": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", - "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", - "dev": true, - "engines": { - "node": ">=6" - }, - "peerDependencies": { - "jest-resolve": "*" - }, - "peerDependenciesMeta": { - "jest-resolve": { - "optional": true - } - } - }, - "node_modules/jest-preset-angular": { - "version": "14.0.3", - "resolved": "https://registry.npmjs.org/jest-preset-angular/-/jest-preset-angular-14.0.3.tgz", - "integrity": "sha512-usgBL7x0rXMnMSx8iEFeOozj50W6fp+YAmQcQBUdAXhN+PAXRy4UXL6I/rfcAOU09rnnq7RKsLsmhpp/fFEuag==", + "node_modules/jest-circus/node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", "dev": true, "dependencies": { - "bs-logger": "^0.2.6", - "esbuild-wasm": ">=0.15.13", - "jest-environment-jsdom": "^29.0.0", - "jest-util": "^29.0.0", - "pretty-format": "^29.0.0", - "ts-jest": "^29.0.0" + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" }, "engines": { - "node": "^14.15.0 || >=16.10.0" - }, - "optionalDependencies": { - "esbuild": ">=0.15.13" - }, - "peerDependencies": { - "@angular-devkit/build-angular": ">=15.0.0 <18.0.0", - "@angular/compiler-cli": ">=15.0.0 <18.0.0", - "@angular/core": ">=15.0.0 <18.0.0", - "@angular/platform-browser-dynamic": ">=15.0.0 <18.0.0", - "jest": "^29.0.0", - "typescript": ">=4.8" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/jest-preset-angular/node_modules/ansi-styles": { + "node_modules/jest-circus/node_modules/pretty-format/node_modules/ansi-styles": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", "dev": true, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-preset-angular/node_modules/pretty-format": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", - "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", - "dev": true, - "dependencies": { - "@jest/schemas": "^29.6.3", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/jest-preset-angular/node_modules/react-is": { + "node_modules/jest-circus/node_modules/react-is": { "version": "18.2.0", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", "dev": true }, - "node_modules/jest-regex-util": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", - "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", + "node_modules/jest-circus/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=8" } }, - "node_modules/jest-resolve": { + "node_modules/jest-cli": { "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz", - "integrity": "sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.7.0.tgz", + "integrity": "sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==", "dev": true, "dependencies": { + "@jest/core": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "jest-pnp-resolver": "^1.2.2", + "create-jest": "^29.7.0", + "exit": "^0.1.2", + "import-local": "^3.0.2", + "jest-config": "^29.7.0", "jest-util": "^29.7.0", "jest-validate": "^29.7.0", - "resolve": "^1.20.0", - "resolve.exports": "^2.0.0", - "slash": "^3.0.0" + "yargs": "^17.3.1" }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-resolve-dependencies": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz", - "integrity": "sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==", - "dev": true, - "dependencies": { - "jest-regex-util": "^29.6.3", - "jest-snapshot": "^29.7.0" + "bin": { + "jest": "bin/jest.js" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } } }, - "node_modules/jest-resolve/node_modules/ansi-styles": { + "node_modules/jest-cli/node_modules/ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", @@ -21826,7 +13298,7 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/jest-resolve/node_modules/chalk": { + "node_modules/jest-cli/node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", @@ -21842,7 +13314,7 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/jest-resolve/node_modules/has-flag": { + "node_modules/jest-cli/node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", @@ -21851,7 +13323,7 @@ "node": ">=8" } }, - "node_modules/jest-resolve/node_modules/supports-color": { + "node_modules/jest-cli/node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", @@ -21863,39 +13335,52 @@ "node": ">=8" } }, - "node_modules/jest-runner": { + "node_modules/jest-config": { "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.7.0.tgz", - "integrity": "sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.7.0.tgz", + "integrity": "sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==", "dev": true, "dependencies": { - "@jest/console": "^29.7.0", - "@jest/environment": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", + "@babel/core": "^7.11.6", + "@jest/test-sequencer": "^29.7.0", "@jest/types": "^29.6.3", - "@types/node": "*", + "babel-jest": "^29.7.0", "chalk": "^4.0.0", - "emittery": "^0.13.1", + "ci-info": "^3.2.0", + "deepmerge": "^4.2.2", + "glob": "^7.1.3", "graceful-fs": "^4.2.9", - "jest-docblock": "^29.7.0", + "jest-circus": "^29.7.0", "jest-environment-node": "^29.7.0", - "jest-haste-map": "^29.7.0", - "jest-leak-detector": "^29.7.0", - "jest-message-util": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-regex-util": "^29.6.3", "jest-resolve": "^29.7.0", - "jest-runtime": "^29.7.0", + "jest-runner": "^29.7.0", "jest-util": "^29.7.0", - "jest-watcher": "^29.7.0", - "jest-worker": "^29.7.0", - "p-limit": "^3.1.0", - "source-map-support": "0.5.13" + "jest-validate": "^29.7.0", + "micromatch": "^4.0.4", + "parse-json": "^5.2.0", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "strip-json-comments": "^3.1.1" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@types/node": "*", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "ts-node": { + "optional": true + } } }, - "node_modules/jest-runner/node_modules/ansi-styles": { + "node_modules/jest-config/node_modules/ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", @@ -21910,7 +13395,17 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/jest-runner/node_modules/chalk": { + "node_modules/jest-config/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/jest-config/node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", @@ -21926,7 +13421,27 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/jest-runner/node_modules/has-flag": { + "node_modules/jest-config/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/jest-config/node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", @@ -21935,26 +13450,51 @@ "node": ">=8" } }, - "node_modules/jest-runner/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "node_modules/jest-config/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, "engines": { - "node": ">=0.10.0" + "node": "*" } }, - "node_modules/jest-runner/node_modules/source-map-support": { - "version": "0.5.13", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", - "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", + "node_modules/jest-config/node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", "dev": true, "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/jest-runner/node_modules/supports-color": { + "node_modules/jest-config/node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-config/node_modules/react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", + "dev": true + }, + "node_modules/jest-config/node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", @@ -21966,40 +13506,22 @@ "node": ">=8" } }, - "node_modules/jest-runtime": { + "node_modules/jest-diff": { "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.7.0.tgz", - "integrity": "sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", + "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", "dev": true, "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/fake-timers": "^29.7.0", - "@jest/globals": "^29.7.0", - "@jest/source-map": "^29.6.3", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", "chalk": "^4.0.0", - "cjs-module-lexer": "^1.0.0", - "collect-v8-coverage": "^1.0.0", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-mock": "^29.7.0", - "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.7.0", - "jest-snapshot": "^29.7.0", - "jest-util": "^29.7.0", - "slash": "^3.0.0", - "strip-bom": "^4.0.0" + "diff-sequences": "^29.6.3", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/jest-runtime/node_modules/ansi-styles": { + "node_modules/jest-diff/node_modules/ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", @@ -22007,24 +13529,14 @@ "dependencies": { "color-convert": "^2.0.1" }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-runtime/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/jest-runtime/node_modules/chalk": { + "node_modules/jest-diff/node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", @@ -22040,27 +13552,7 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/jest-runtime/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/jest-runtime/node_modules/has-flag": { + "node_modules/jest-diff/node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", @@ -22069,33 +13561,39 @@ "node": ">=8" } }, - "node_modules/jest-runtime/node_modules/jest-mock": { + "node_modules/jest-diff/node_modules/pretty-format": { "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz", - "integrity": "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", "dev": true, "dependencies": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-util": "^29.7.0" + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/jest-runtime/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "node_modules/jest-diff/node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, "engines": { - "node": "*" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/jest-runtime/node_modules/supports-color": { + "node_modules/jest-diff/node_modules/react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", + "dev": true + }, + "node_modules/jest-diff/node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", @@ -22107,38 +13605,35 @@ "node": ">=8" } }, - "node_modules/jest-snapshot": { + "node_modules/jest-docblock": { "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.7.0.tgz", - "integrity": "sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.7.0.tgz", + "integrity": "sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==", + "dev": true, + "dependencies": { + "detect-newline": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-each": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.7.0.tgz", + "integrity": "sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==", "dev": true, "dependencies": { - "@babel/core": "^7.11.6", - "@babel/generator": "^7.7.2", - "@babel/plugin-syntax-jsx": "^7.7.2", - "@babel/plugin-syntax-typescript": "^7.7.2", - "@babel/types": "^7.3.3", - "@jest/expect-utils": "^29.7.0", - "@jest/transform": "^29.7.0", "@jest/types": "^29.6.3", - "babel-preset-current-node-syntax": "^1.0.0", "chalk": "^4.0.0", - "expect": "^29.7.0", - "graceful-fs": "^4.2.9", - "jest-diff": "^29.7.0", "jest-get-type": "^29.6.3", - "jest-matcher-utils": "^29.7.0", - "jest-message-util": "^29.7.0", "jest-util": "^29.7.0", - "natural-compare": "^1.4.0", - "pretty-format": "^29.7.0", - "semver": "^7.5.3" + "pretty-format": "^29.7.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/jest-snapshot/node_modules/ansi-styles": { + "node_modules/jest-each/node_modules/ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", @@ -22153,7 +13648,7 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/jest-snapshot/node_modules/chalk": { + "node_modules/jest-each/node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", @@ -22169,7 +13664,7 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/jest-snapshot/node_modules/has-flag": { + "node_modules/jest-each/node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", @@ -22178,7 +13673,7 @@ "node": ">=8" } }, - "node_modules/jest-snapshot/node_modules/pretty-format": { + "node_modules/jest-each/node_modules/pretty-format": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", @@ -22192,7 +13687,7 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/jest-snapshot/node_modules/pretty-format/node_modules/ansi-styles": { + "node_modules/jest-each/node_modules/pretty-format/node_modules/ansi-styles": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", @@ -22204,13 +13699,13 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/jest-snapshot/node_modules/react-is": { + "node_modules/jest-each/node_modules/react-is": { "version": "18.2.0", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", "dev": true }, - "node_modules/jest-snapshot/node_modules/supports-color": { + "node_modules/jest-each/node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", @@ -22222,36 +13717,173 @@ "node": ">=8" } }, - "node_modules/jest-sonar-reporter": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/jest-sonar-reporter/-/jest-sonar-reporter-2.0.0.tgz", - "integrity": "sha512-ZervDCgEX5gdUbdtWsjdipLN3bKJwpxbvhkYNXTAYvAckCihobSLr9OT/IuyNIRT1EZMDDwR6DroWtrq+IL64w==", + "node_modules/jest-environment-jsdom": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-29.7.0.tgz", + "integrity": "sha512-k9iQbsf9OyOfdzWH8HDmrRT0gSIcX+FLNW7IQq94tFX0gynPwqDTW0Ho6iMVNjGz/nb+l/vW3dWM2bbLLpkbXA==", "dev": true, "dependencies": { - "xml": "^1.0.1" + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/jsdom": "^20.0.0", + "@types/node": "*", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0", + "jsdom": "^20.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "canvas": "^2.5.0" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } + } + }, + "node_modules/jest-environment-jsdom/node_modules/jest-mock": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz", + "integrity": "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-environment-node": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz", + "integrity": "sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==", + "dev": true, + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-environment-node/node_modules/jest-mock": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz", + "integrity": "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-get-type": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", + "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", + "dev": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-haste-map": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", + "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/graceful-fs": "^4.1.3", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "graceful-fs": "^4.2.9", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", + "micromatch": "^4.0.4", + "walker": "^1.0.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "optionalDependencies": { + "fsevents": "^2.3.2" + } + }, + "node_modules/jest-leak-detector": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz", + "integrity": "sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==", + "dev": true, + "dependencies": { + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-leak-detector/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-leak-detector/node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" }, "engines": { - "node": ">=8.0.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/jest-util": { + "node_modules/jest-leak-detector/node_modules/react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", + "dev": true + }, + "node_modules/jest-matcher-utils": { "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", - "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz", + "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==", "dev": true, "dependencies": { - "@jest/types": "^29.6.3", - "@types/node": "*", "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/jest-util/node_modules/ansi-styles": { + "node_modules/jest-matcher-utils/node_modules/ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", @@ -22266,7 +13898,7 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/jest-util/node_modules/chalk": { + "node_modules/jest-matcher-utils/node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", @@ -22282,7 +13914,7 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/jest-util/node_modules/has-flag": { + "node_modules/jest-matcher-utils/node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", @@ -22291,19 +13923,39 @@ "node": ">=8" } }, - "node_modules/jest-util/node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "node_modules/jest-matcher-utils/node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, "engines": { - "node": ">=8.6" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-matcher-utils/node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "engines": { + "node": ">=10" }, "funding": { - "url": "https://github.com/sponsors/jonschlinkert" + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/jest-util/node_modules/supports-color": { + "node_modules/jest-matcher-utils/node_modules/react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", + "dev": true + }, + "node_modules/jest-matcher-utils/node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", @@ -22315,24 +13967,27 @@ "node": ">=8" } }, - "node_modules/jest-validate": { + "node_modules/jest-message-util": { "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", - "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz", + "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", "dev": true, "dependencies": { + "@babel/code-frame": "^7.12.13", "@jest/types": "^29.6.3", - "camelcase": "^6.2.0", + "@types/stack-utils": "^2.0.0", "chalk": "^4.0.0", - "jest-get-type": "^29.6.3", - "leven": "^3.1.0", - "pretty-format": "^29.7.0" + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/jest-validate/node_modules/ansi-styles": { + "node_modules/jest-message-util/node_modules/ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", @@ -22347,19 +14002,7 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/jest-validate/node_modules/camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/jest-validate/node_modules/chalk": { + "node_modules/jest-message-util/node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", @@ -22375,7 +14018,7 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/jest-validate/node_modules/has-flag": { + "node_modules/jest-message-util/node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", @@ -22384,7 +14027,7 @@ "node": ">=8" } }, - "node_modules/jest-validate/node_modules/pretty-format": { + "node_modules/jest-message-util/node_modules/pretty-format": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", @@ -22398,7 +14041,7 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/jest-validate/node_modules/pretty-format/node_modules/ansi-styles": { + "node_modules/jest-message-util/node_modules/pretty-format/node_modules/ansi-styles": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", @@ -22410,13 +14053,13 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/jest-validate/node_modules/react-is": { + "node_modules/jest-message-util/node_modules/react-is": { "version": "18.2.0", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", "dev": true }, - "node_modules/jest-validate/node_modules/supports-color": { + "node_modules/jest-message-util/node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", @@ -22428,93 +14071,157 @@ "node": ">=8" } }, - "node_modules/jest-watcher": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.7.0.tgz", - "integrity": "sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==", + "node_modules/jest-pnp-resolver": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", + "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", "dev": true, - "dependencies": { - "@jest/test-result": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "emittery": "^0.13.1", - "jest-util": "^29.7.0", - "string-length": "^4.0.1" - }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=6" + }, + "peerDependencies": { + "jest-resolve": "*" + }, + "peerDependenciesMeta": { + "jest-resolve": { + "optional": true + } } }, - "node_modules/jest-watcher/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/jest-preset-angular": { + "version": "14.2.2", + "resolved": "https://registry.npmjs.org/jest-preset-angular/-/jest-preset-angular-14.2.2.tgz", + "integrity": "sha512-vdpv1DV4yJMMoBRbTdwRA16Es0UU+8CuOHsV2vfUL0LOy69anvq2RUawh07EyTWSVxko838jOC146jlnCkWOOw==", "dev": true, "dependencies": { - "color-convert": "^2.0.1" + "bs-logger": "^0.2.6", + "esbuild-wasm": ">=0.15.13", + "jest-environment-jsdom": "^29.0.0", + "jest-util": "^29.0.0", + "pretty-format": "^29.0.0", + "ts-jest": "^29.0.0" }, "engines": { - "node": ">=8" + "node": "^14.15.0 || >=16.10.0" + }, + "optionalDependencies": { + "esbuild": ">=0.15.13" + }, + "peerDependencies": { + "@angular-devkit/build-angular": ">=15.0.0 <19.0.0", + "@angular/compiler-cli": ">=15.0.0 <19.0.0", + "@angular/core": ">=15.0.0 <19.0.0", + "@angular/platform-browser-dynamic": ">=15.0.0 <19.0.0", + "jest": "^29.0.0", + "typescript": ">=4.8" + } + }, + "node_modules/jest-preset-angular/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "engines": { + "node": ">=10" }, "funding": { "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/jest-watcher/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/jest-preset-angular/node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", "dev": true, "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" }, "engines": { - "node": ">=10" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-preset-angular/node_modules/react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", + "dev": true + }, + "node_modules/jest-regex-util": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", + "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", + "dev": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-resolve": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz", + "integrity": "sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-pnp-resolver": "^1.2.2", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "resolve": "^1.20.0", + "resolve.exports": "^2.0.0", + "slash": "^3.0.0" }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/jest-watcher/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "node_modules/jest-resolve-dependencies": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz", + "integrity": "sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==", "dev": true, + "dependencies": { + "jest-regex-util": "^29.6.3", + "jest-snapshot": "^29.7.0" + }, "engines": { - "node": ">=8" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/jest-watcher/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "node_modules/jest-resolve/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, "dependencies": { - "has-flag": "^4.0.0" + "color-convert": "^2.0.1" }, "engines": { "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/jest-worker": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", - "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", + "node_modules/jest-resolve/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, "dependencies": { - "@types/node": "*", - "jest-util": "^29.7.0", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/jest-worker/node_modules/has-flag": { + "node_modules/jest-resolve/node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", @@ -22523,101 +14230,51 @@ "node": ">=8" } }, - "node_modules/jest-worker/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "node_modules/jest-resolve/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "dependencies": { "has-flag": "^4.0.0" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "node_modules/jiti": { - "version": "1.21.0", - "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.0.tgz", - "integrity": "sha512-gFqAIbuKyyso/3G2qhiO2OM6shY6EPP/R0+mkDbyspxKazh8BXDC5FiFsUjlczgdNz/vfra0da2y+aHrusLG/Q==", - "dev": true, - "bin": { - "jiti": "bin/jiti.js" + "node": ">=8" } }, - "node_modules/jju": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/jju/-/jju-1.4.0.tgz", - "integrity": "sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA==", - "dev": true - }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true - }, - "node_modules/js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "node_modules/jest-runner": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.7.0.tgz", + "integrity": "sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==", "dev": true, "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/jsbn": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz", - "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==", - "dev": true - }, - "node_modules/jscodeshift": { - "version": "0.15.2", - "resolved": "https://registry.npmjs.org/jscodeshift/-/jscodeshift-0.15.2.tgz", - "integrity": "sha512-FquR7Okgmc4Sd0aEDwqho3rEiKR3BdvuG9jfdHjLJ6JQoWSMpavug3AoIfnfWhxFlf+5pzQh8qjqz0DWFrNQzA==", - "dev": true, - "dependencies": { - "@babel/core": "^7.23.0", - "@babel/parser": "^7.23.0", - "@babel/plugin-transform-class-properties": "^7.22.5", - "@babel/plugin-transform-modules-commonjs": "^7.23.0", - "@babel/plugin-transform-nullish-coalescing-operator": "^7.22.11", - "@babel/plugin-transform-optional-chaining": "^7.23.0", - "@babel/plugin-transform-private-methods": "^7.22.5", - "@babel/preset-flow": "^7.22.15", - "@babel/preset-typescript": "^7.23.0", - "@babel/register": "^7.22.15", - "babel-core": "^7.0.0-bridge.0", - "chalk": "^4.1.2", - "flow-parser": "0.*", - "graceful-fs": "^4.2.4", - "micromatch": "^4.0.4", - "neo-async": "^2.5.0", - "node-dir": "^0.1.17", - "recast": "^0.23.3", - "temp": "^0.8.4", - "write-file-atomic": "^2.3.0" - }, - "bin": { - "jscodeshift": "bin/jscodeshift.js" - }, - "peerDependencies": { - "@babel/preset-env": "^7.1.6" + "@jest/console": "^29.7.0", + "@jest/environment": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "graceful-fs": "^4.2.9", + "jest-docblock": "^29.7.0", + "jest-environment-node": "^29.7.0", + "jest-haste-map": "^29.7.0", + "jest-leak-detector": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-resolve": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-util": "^29.7.0", + "jest-watcher": "^29.7.0", + "jest-worker": "^29.7.0", + "p-limit": "^3.1.0", + "source-map-support": "0.5.13" }, - "peerDependenciesMeta": { - "@babel/preset-env": { - "optional": true - } + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/jscodeshift/node_modules/ansi-styles": { + "node_modules/jest-runner/node_modules/ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", @@ -22632,7 +14289,7 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/jscodeshift/node_modules/chalk": { + "node_modules/jest-runner/node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", @@ -22648,7 +14305,7 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/jscodeshift/node_modules/has-flag": { + "node_modules/jest-runner/node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", @@ -22657,7 +14314,26 @@ "node": ">=8" } }, - "node_modules/jscodeshift/node_modules/supports-color": { + "node_modules/jest-runner/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/jest-runner/node_modules/source-map-support": { + "version": "0.5.13", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", + "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", + "dev": true, + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/jest-runner/node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", @@ -22669,185 +14345,179 @@ "node": ">=8" } }, - "node_modules/jscodeshift/node_modules/write-file-atomic": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.4.3.tgz", - "integrity": "sha512-GaETH5wwsX+GcnzhPgKcKjJ6M2Cq3/iZp1WyY/X1CSqrW+jVNM9Y7D8EC2sM4ZG/V8wZlSniJnCKWPmBYAucRQ==", + "node_modules/jest-runtime": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.7.0.tgz", + "integrity": "sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==", "dev": true, "dependencies": { - "graceful-fs": "^4.1.11", - "imurmurhash": "^0.1.4", - "signal-exit": "^3.0.2" + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/globals": "^29.7.0", + "@jest/source-map": "^29.6.3", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "cjs-module-lexer": "^1.0.0", + "collect-v8-coverage": "^1.0.0", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "slash": "^3.0.0", + "strip-bom": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/jsdom": { - "version": "20.0.3", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-20.0.3.tgz", - "integrity": "sha512-SYhBvTh89tTfCD/CRdSOm13mOBa42iTaTyfyEWBdKcGdPxPtLFBXuHR8XHb33YNYaP+lLbmSvBTsnoesCNJEsQ==", + "node_modules/jest-runtime/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, "dependencies": { - "abab": "^2.0.6", - "acorn": "^8.8.1", - "acorn-globals": "^7.0.0", - "cssom": "^0.5.0", - "cssstyle": "^2.3.0", - "data-urls": "^3.0.2", - "decimal.js": "^10.4.2", - "domexception": "^4.0.0", - "escodegen": "^2.0.0", - "form-data": "^4.0.0", - "html-encoding-sniffer": "^3.0.0", - "http-proxy-agent": "^5.0.0", - "https-proxy-agent": "^5.0.1", - "is-potential-custom-element-name": "^1.0.1", - "nwsapi": "^2.2.2", - "parse5": "^7.1.1", - "saxes": "^6.0.0", - "symbol-tree": "^3.2.4", - "tough-cookie": "^4.1.2", - "w3c-xmlserializer": "^4.0.0", - "webidl-conversions": "^7.0.0", - "whatwg-encoding": "^2.0.0", - "whatwg-mimetype": "^3.0.0", - "whatwg-url": "^11.0.0", - "ws": "^8.11.0", - "xml-name-validator": "^4.0.0" + "color-convert": "^2.0.1" }, "engines": { - "node": ">=14" - }, - "peerDependencies": { - "canvas": "^2.5.0" + "node": ">=8" }, - "peerDependenciesMeta": { - "canvas": { - "optional": true - } + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/jsdom/node_modules/agent-base": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", - "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "node_modules/jest-runtime/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, "dependencies": { - "debug": "4" + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/jest-runtime/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, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" }, "engines": { - "node": ">= 6.0.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/jsdom/node_modules/https-proxy-agent": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", - "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "node_modules/jest-runtime/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", "dev": true, "dependencies": { - "agent-base": "6", - "debug": "4" + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" }, "engines": { - "node": ">= 6" + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "node_modules/jest-runtime/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, - "bin": { - "jsesc": "bin/jsesc" - }, "engines": { - "node": ">=4" + "node": ">=8" } }, - "node_modules/json-buffer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==" - }, - "node_modules/json-parse-even-better-errors": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-3.0.1.tgz", - "integrity": "sha512-aatBvbL26wVUCLmbWdCpeu9iF5wOyWpagiKkInA+kfws3sWdBrTnsvN2CKcyCYyUrc7rebNBlK6+kteg7ksecg==", + "node_modules/jest-runtime/node_modules/jest-mock": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz", + "integrity": "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==", "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-util": "^29.7.0" + }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/json-parse-helpfulerror": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/json-parse-helpfulerror/-/json-parse-helpfulerror-1.0.3.tgz", - "integrity": "sha512-XgP0FGR77+QhUxjXkwOMkC94k3WtqEBfcnjWqhRd82qTat4SWKRE+9kUnynz/shm3I4ea2+qISvTIeGTNU7kJg==", + "node_modules/jest-runtime/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, "dependencies": { - "jju": "^1.1.0" + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" } }, - "node_modules/json-schema": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", - "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==", - "dev": true - }, - "node_modules/json-schema-deref-sync": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/json-schema-deref-sync/-/json-schema-deref-sync-0.14.0.tgz", - "integrity": "sha512-yGR1xmhdiD6R0MSrwWcFxQzAj5b3i5Gb/mt5tvQKgFMMeNe0KZYNEN/jWr7G+xn39Azqgcvk4ZKMs8dQl8e4wA==", + "node_modules/jest-runtime/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, "dependencies": { - "clone": "^2.1.2", - "dag-map": "~1.0.0", - "is-valid-path": "^0.1.1", - "lodash": "^4.17.13", - "md5": "~2.2.0", - "memory-cache": "~0.2.0", - "traverse": "~0.6.6", - "valid-url": "~1.0.9" + "has-flag": "^4.0.0" }, "engines": { - "node": ">=6.0.0" + "node": ">=8" } }, - "node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" - }, - "node_modules/json-server": { - "version": "0.17.4", - "resolved": "https://registry.npmjs.org/json-server/-/json-server-0.17.4.tgz", - "integrity": "sha512-bGBb0WtFuAKbgI7JV3A864irWnMZSvBYRJbohaOuatHwKSRFUfqtQlrYMrB6WbalXy/cJabyjlb7JkHli6dYjQ==", + "node_modules/jest-snapshot": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.7.0.tgz", + "integrity": "sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==", "dev": true, "dependencies": { - "body-parser": "^1.19.0", - "chalk": "^4.1.2", - "compression": "^1.7.4", - "connect-pause": "^0.1.1", - "cors": "^2.8.5", - "errorhandler": "^1.5.1", - "express": "^4.17.1", - "express-urlrewrite": "^1.4.0", - "json-parse-helpfulerror": "^1.0.3", - "lodash": "^4.17.21", - "lodash-id": "^0.14.1", - "lowdb": "^1.0.0", - "method-override": "^3.0.0", - "morgan": "^1.10.0", - "nanoid": "^3.1.23", - "please-upgrade-node": "^3.2.0", - "pluralize": "^8.0.0", - "server-destroy": "^1.0.1", - "yargs": "^17.0.1" - }, - "bin": { - "json-server": "lib/cli/bin.js" + "@babel/core": "^7.11.6", + "@babel/generator": "^7.7.2", + "@babel/plugin-syntax-jsx": "^7.7.2", + "@babel/plugin-syntax-typescript": "^7.7.2", + "@babel/types": "^7.3.3", + "@jest/expect-utils": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "babel-preset-current-node-syntax": "^1.0.0", + "chalk": "^4.0.0", + "expect": "^29.7.0", + "graceful-fs": "^4.2.9", + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "natural-compare": "^1.4.0", + "pretty-format": "^29.7.0", + "semver": "^7.5.3" }, "engines": { - "node": ">=12" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/json-server/node_modules/ansi-styles": { + "node_modules/jest-snapshot/node_modules/ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", @@ -22862,7 +14532,7 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/json-server/node_modules/chalk": { + "node_modules/jest-snapshot/node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", @@ -22878,7 +14548,7 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/json-server/node_modules/has-flag": { + "node_modules/jest-snapshot/node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", @@ -22887,7 +14557,39 @@ "node": ">=8" } }, - "node_modules/json-server/node_modules/supports-color": { + "node_modules/jest-snapshot/node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-snapshot/node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-snapshot/node_modules/react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", + "dev": true + }, + "node_modules/jest-snapshot/node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", @@ -22899,555 +14601,519 @@ "node": ">=8" } }, - "node_modules/json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==" - }, - "node_modules/json-stringify-safe": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", - "dev": true - }, - "node_modules/json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "node_modules/jest-sonar-reporter": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/jest-sonar-reporter/-/jest-sonar-reporter-2.0.0.tgz", + "integrity": "sha512-ZervDCgEX5gdUbdtWsjdipLN3bKJwpxbvhkYNXTAYvAckCihobSLr9OT/IuyNIRT1EZMDDwR6DroWtrq+IL64w==", "dev": true, - "bin": { - "json5": "lib/cli.js" + "dependencies": { + "xml": "^1.0.1" }, "engines": { - "node": ">=6" + "node": ">=8.0.0" } }, - "node_modules/jsonc-parser": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.1.tgz", - "integrity": "sha512-AilxAyFOAcK5wA1+LeaySVBrHsGQvUFCDWXKpZjzaL0PqW+xfBOttn8GNtWKFWqneyMZj41MWF9Kl6iPWLwgOA==", - "dev": true - }, - "node_modules/jsonfile": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "node_modules/jest-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", "dev": true, "dependencies": { - "universalify": "^2.0.0" + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/jsonparse": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", - "integrity": "sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==", + "node_modules/jest-util/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, - "engines": [ - "node >= 0.2.0" - ] + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } }, - "node_modules/JSONStream": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz", - "integrity": "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==", + "node_modules/jest-util/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, "dependencies": { - "jsonparse": "^1.2.0", - "through": ">=2.2.7 <3" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" }, - "bin": { - "JSONStream": "bin.js" + "engines": { + "node": ">=10" }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-util/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, "engines": { - "node": "*" + "node": ">=8" } }, - "node_modules/jsprim": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-2.0.2.tgz", - "integrity": "sha512-gqXddjPqQ6G40VdnI6T6yObEC+pDNvyP95wdQhkWkg7crHH3km5qP1FsOXEkzEQwnz6gz5qGTn1c2Y52wP3OyQ==", + "node_modules/jest-util/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true, - "engines": [ - "node >=0.6.0" - ], - "dependencies": { - "assert-plus": "1.0.0", - "extsprintf": "1.3.0", - "json-schema": "0.4.0", - "verror": "1.10.0" + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/jsx-ast-utils": { - "version": "3.3.5", - "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", - "integrity": "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==", + "node_modules/jest-util/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, - "peer": true, "dependencies": { - "array-includes": "^3.1.6", - "array.prototype.flat": "^1.3.1", - "object.assign": "^4.1.4", - "object.values": "^1.1.6" + "has-flag": "^4.0.0" }, "engines": { - "node": ">=4.0" - } - }, - "node_modules/jwt-decode": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/jwt-decode/-/jwt-decode-4.0.0.tgz", - "integrity": "sha512-+KJGIyHgkGuIq3IEBNftfhW/LfWhXUIY6OmyVWjliu5KH1y0fw7VQ8YndE2O4qZdMSd9SqbnC8GOcZEy0Om7sA==", - "engines": { - "node": ">=18" + "node": ">=8" } }, - "node_modules/karma-source-map-support": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/karma-source-map-support/-/karma-source-map-support-1.4.0.tgz", - "integrity": "sha512-RsBECncGO17KAoJCYXjv+ckIz+Ii9NCi+9enk+rq6XC81ezYkb4/RHE6CTXdA7IOJqoF3wcaLfVG0CPmE5ca6A==", + "node_modules/jest-validate": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", + "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", "dev": true, "dependencies": { - "source-map-support": "^0.5.5" + "@jest/types": "^29.6.3", + "camelcase": "^6.2.0", + "chalk": "^4.0.0", + "jest-get-type": "^29.6.3", + "leven": "^3.1.0", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/keycharm": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/keycharm/-/keycharm-0.2.0.tgz", - "integrity": "sha512-i/XBRTiLqRConPKioy2oq45vbv04e8x59b0mnsIRQM+7Ec/8BC7UcL5pnC4FMeGb8KwG7q4wOMw7CtNZf5tiIg==", - "dev": true - }, - "node_modules/keyv": { - "version": "4.5.4", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", - "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "node_modules/jest-validate/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, "dependencies": { - "json-buffer": "3.0.1" + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "node_modules/jest-validate/node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", "dev": true, "engines": { - "node": ">=0.10.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/kleur": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", - "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "node_modules/jest-validate/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, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, "engines": { - "node": ">=6" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/klona": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/klona/-/klona-2.0.6.tgz", - "integrity": "sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA==", + "node_modules/jest-validate/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, "engines": { - "node": ">= 8" + "node": ">=8" } }, - "node_modules/language-subtag-registry": { - "version": "0.3.22", - "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.22.tgz", - "integrity": "sha512-tN0MCzyWnoz/4nHS6uxdlFWoUZT7ABptwKPQ52Ea7URk6vll88bWBVhodtnlfEuCcKWNGoc+uGbw1cwa9IKh/w==", - "dev": true, - "peer": true - }, - "node_modules/language-tags": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/language-tags/-/language-tags-1.0.9.tgz", - "integrity": "sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==", + "node_modules/jest-validate/node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", "dev": true, - "peer": true, "dependencies": { - "language-subtag-registry": "^0.3.20" + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" }, "engines": { - "node": ">=0.10" - } - }, - "node_modules/launch-editor": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/launch-editor/-/launch-editor-2.6.1.tgz", - "integrity": "sha512-eB/uXmFVpY4zezmGp5XtU21kwo7GBbKB+EQ+UZeWtGb9yAM5xt/Evk+lYH3eRNAtId+ej4u7TYPFZ07w4s7rRw==", - "dev": true, - "dependencies": { - "picocolors": "^1.0.0", - "shell-quote": "^1.8.1" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/lazy-ass": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/lazy-ass/-/lazy-ass-1.6.0.tgz", - "integrity": "sha512-cc8oEVoctTvsFZ/Oje/kGnHbpWHYBe8IAJe4C0QNc3t8uM/0Y8+erSz/7Y1ALuXTEZTMvxXwO6YbX1ey3ujiZw==", + "node_modules/jest-validate/node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", "dev": true, "engines": { - "node": "> 0.8" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/lazy-universal-dotenv": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/lazy-universal-dotenv/-/lazy-universal-dotenv-4.0.0.tgz", - "integrity": "sha512-aXpZJRnTkpK6gQ/z4nk+ZBLd/Qdp118cvPruLSIQzQNRhKwEcdXCOzXuF55VDqIiuAaY3UGZ10DJtvZzDcvsxg==", + "node_modules/jest-validate/node_modules/react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", + "dev": true + }, + "node_modules/jest-validate/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "dependencies": { - "app-root-dir": "^1.0.2", - "dotenv": "^16.0.0", - "dotenv-expand": "^10.0.0" + "has-flag": "^4.0.0" }, "engines": { - "node": ">=14.0.0" + "node": ">=8" } }, - "node_modules/less": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/less/-/less-4.2.0.tgz", - "integrity": "sha512-P3b3HJDBtSzsXUl0im2L7gTO5Ubg8mEN6G8qoTS77iXxXX4Hvu4Qj540PZDvQ8V6DmX6iXo98k7Md0Cm1PrLaA==", + "node_modules/jest-watcher": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.7.0.tgz", + "integrity": "sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==", "dev": true, "dependencies": { - "copy-anything": "^2.0.1", - "parse-node-version": "^1.0.1", - "tslib": "^2.3.0" - }, - "bin": { - "lessc": "bin/lessc" + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "jest-util": "^29.7.0", + "string-length": "^4.0.1" }, "engines": { - "node": ">=6" - }, - "optionalDependencies": { - "errno": "^0.1.1", - "graceful-fs": "^4.1.2", - "image-size": "~0.5.0", - "make-dir": "^2.1.0", - "mime": "^1.4.1", - "needle": "^3.1.0", - "source-map": "~0.6.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/less-loader": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/less-loader/-/less-loader-11.1.0.tgz", - "integrity": "sha512-C+uDBV7kS7W5fJlUjq5mPBeBVhYpTIm5gB09APT9o3n/ILeaXVsiSFTbZpTJCJwQ/Crczfn3DmfQFwxYusWFug==", + "node_modules/jest-watcher/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, "dependencies": { - "klona": "^2.0.4" + "color-convert": "^2.0.1" }, "engines": { - "node": ">= 14.15.0" + "node": ">=8" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "less": "^3.5.0 || ^4.0.0", - "webpack": "^5.0.0" + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/less/node_modules/make-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", - "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "node_modules/jest-watcher/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, - "optional": true, "dependencies": { - "pify": "^4.0.1", - "semver": "^5.6.0" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" }, "engines": { - "node": ">=6" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/less/node_modules/pify": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "node_modules/jest-watcher/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, - "optional": true, "engines": { - "node": ">=6" + "node": ">=8" } }, - "node_modules/less/node_modules/semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "node_modules/jest-watcher/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, - "optional": true, - "bin": { - "semver": "bin/semver" + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" } }, - "node_modules/less/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "node_modules/jest-worker": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", + "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", "dev": true, - "optional": true, + "dependencies": { + "@types/node": "*", + "jest-util": "^29.7.0", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, "engines": { - "node": ">=0.10.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/leven": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", - "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "node_modules/jest-worker/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, "engines": { - "node": ">=6" + "node": ">=8" } }, - "node_modules/levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, "dependencies": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" + "has-flag": "^4.0.0" }, "engines": { - "node": ">= 0.8.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" } }, - "node_modules/license-webpack-plugin": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/license-webpack-plugin/-/license-webpack-plugin-4.0.2.tgz", - "integrity": "sha512-771TFWFD70G1wLTC4oU2Cw4qvtmNrIw+wRvBtn+okgHl7slJVi7zfNcdmqDL72BojM30VNJ2UHylr1o77U37Jw==", + "node_modules/jiti": { + "version": "1.21.0", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.0.tgz", + "integrity": "sha512-gFqAIbuKyyso/3G2qhiO2OM6shY6EPP/R0+mkDbyspxKazh8BXDC5FiFsUjlczgdNz/vfra0da2y+aHrusLG/Q==", "dev": true, - "dependencies": { - "webpack-sources": "^3.0.0" - }, - "peerDependenciesMeta": { - "webpack": { - "optional": true - }, - "webpack-sources": { - "optional": true - } + "bin": { + "jiti": "bin/jiti.js" } }, - "node_modules/lines-and-columns": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-2.0.4.tgz", - "integrity": "sha512-wM1+Z03eypVAVUCE7QdSqpVIvelbOakn1M0bPDoA4SGWPx3sNDVUiMo3L6To6WWGClB7VyXnhQ4Sn7gxiJbE6A==", + "node_modules/jju": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/jju/-/jju-1.4.0.tgz", + "integrity": "sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA==", + "dev": true + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", "dev": true, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" } }, - "node_modules/listr2": { - "version": "3.14.0", - "resolved": "https://registry.npmjs.org/listr2/-/listr2-3.14.0.tgz", - "integrity": "sha512-TyWI8G99GX9GjE54cJ+RrNMcIFBfwMPxc3XTFiAYGN4s10hWROGtOg7+O6u6LE3mNkyld7RSLE6nrKBvTfcs3g==", + "node_modules/jsbn": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz", + "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==", + "dev": true + }, + "node_modules/jsdom": { + "version": "20.0.3", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-20.0.3.tgz", + "integrity": "sha512-SYhBvTh89tTfCD/CRdSOm13mOBa42iTaTyfyEWBdKcGdPxPtLFBXuHR8XHb33YNYaP+lLbmSvBTsnoesCNJEsQ==", "dev": true, "dependencies": { - "cli-truncate": "^2.1.0", - "colorette": "^2.0.16", - "log-update": "^4.0.0", - "p-map": "^4.0.0", - "rfdc": "^1.3.0", - "rxjs": "^7.5.1", - "through": "^2.3.8", - "wrap-ansi": "^7.0.0" + "abab": "^2.0.6", + "acorn": "^8.8.1", + "acorn-globals": "^7.0.0", + "cssom": "^0.5.0", + "cssstyle": "^2.3.0", + "data-urls": "^3.0.2", + "decimal.js": "^10.4.2", + "domexception": "^4.0.0", + "escodegen": "^2.0.0", + "form-data": "^4.0.0", + "html-encoding-sniffer": "^3.0.0", + "http-proxy-agent": "^5.0.0", + "https-proxy-agent": "^5.0.1", + "is-potential-custom-element-name": "^1.0.1", + "nwsapi": "^2.2.2", + "parse5": "^7.1.1", + "saxes": "^6.0.0", + "symbol-tree": "^3.2.4", + "tough-cookie": "^4.1.2", + "w3c-xmlserializer": "^4.0.0", + "webidl-conversions": "^7.0.0", + "whatwg-encoding": "^2.0.0", + "whatwg-mimetype": "^3.0.0", + "whatwg-url": "^11.0.0", + "ws": "^8.11.0", + "xml-name-validator": "^4.0.0" }, "engines": { - "node": ">=10.0.0" + "node": ">=14" }, "peerDependencies": { - "enquirer": ">= 2.3.0 < 3" + "canvas": "^2.5.0" }, "peerDependenciesMeta": { - "enquirer": { + "canvas": { "optional": true } } }, - "node_modules/listr2/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/jsdom/node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", "dev": true, "dependencies": { - "color-convert": "^2.0.1" + "debug": "4" }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "node": ">= 6.0.0" } }, - "node_modules/listr2/node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "node_modules/jsdom/node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", "dev": true, "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" + "agent-base": "6", + "debug": "4" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + "node": ">= 6" } }, - "node_modules/loader-runner": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", - "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", + "node_modules/jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", "dev": true, + "bin": { + "jsesc": "bin/jsesc" + }, "engines": { - "node": ">=6.11.5" + "node": ">=4" } }, - "node_modules/loader-utils": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-3.2.1.tgz", - "integrity": "sha512-ZvFw1KWS3GVyYBYb7qkmRM/WwL2TQQBxgCK62rlvm4WpVQ23Nb4tYjApUlfjrEGvOs7KHEsmyUn75OHZrJMWPw==", + "node_modules/json-parse-even-better-errors": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-3.0.2.tgz", + "integrity": "sha512-fi0NG4bPjCHunUJffmLd0gxssIgkNmArMvis4iNah6Owg1MCJjWhEcDLmsK6iGkJq3tHwbDkTlce70/tmXN4cQ==", "dev": true, "engines": { - "node": ">= 12.13.0" - } - }, - "node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dependencies": { - "p-locate": "^5.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" - }, - "node_modules/lodash-id": { - "version": "0.14.1", - "resolved": "https://registry.npmjs.org/lodash-id/-/lodash-id-0.14.1.tgz", - "integrity": "sha512-ikQPBTiq/d5m6dfKQlFdIXFzvThPi2Be9/AHxktOnDSfSxE1j9ICbBT5Elk1ke7HSTgM38LHTpmJovo9/klnLg==", + "node_modules/json-parse-helpfulerror": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/json-parse-helpfulerror/-/json-parse-helpfulerror-1.0.3.tgz", + "integrity": "sha512-XgP0FGR77+QhUxjXkwOMkC94k3WtqEBfcnjWqhRd82qTat4SWKRE+9kUnynz/shm3I4ea2+qISvTIeGTNU7kJg==", "dev": true, - "engines": { - "node": ">= 4" + "dependencies": { + "jju": "^1.1.0" } }, - "node_modules/lodash.camelcase": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", - "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==", - "dev": true - }, - "node_modules/lodash.clonedeep": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", - "integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==" - }, - "node_modules/lodash.debounce": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", - "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", - "dev": true - }, - "node_modules/lodash.isfunction": { - "version": "3.0.9", - "resolved": "https://registry.npmjs.org/lodash.isfunction/-/lodash.isfunction-3.0.9.tgz", - "integrity": "sha512-AirXNj15uRIMMPihnkInB4i3NHeb4iBtNg9WRWuK2o31S+ePwwNmDPaTL3o7dTJ+VXNZim7rFs4rxN4YU1oUJw==", - "dev": true - }, - "node_modules/lodash.isplainobject": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", - "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", - "dev": true - }, - "node_modules/lodash.kebabcase": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lodash.kebabcase/-/lodash.kebabcase-4.1.1.tgz", - "integrity": "sha512-N8XRTIMMqqDgSy4VLKPnJ/+hpGZN+PHQiJnSenYqPaVV/NCqEogTnAdZLQiGKhxX+JCs8waWq2t1XHWKOmlY8g==", - "dev": true - }, - "node_modules/lodash.memoize": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", - "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", - "dev": true - }, - "node_modules/lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" - }, - "node_modules/lodash.mergewith": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz", - "integrity": "sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==", - "dev": true - }, - "node_modules/lodash.once": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", - "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", - "dev": true - }, - "node_modules/lodash.snakecase": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz", - "integrity": "sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw==", - "dev": true - }, - "node_modules/lodash.startcase": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.startcase/-/lodash.startcase-4.4.0.tgz", - "integrity": "sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg==", - "dev": true - }, - "node_modules/lodash.uniq": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", - "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==", - "dev": true + "node_modules/json-schema-deref-sync": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/json-schema-deref-sync/-/json-schema-deref-sync-0.14.0.tgz", + "integrity": "sha512-yGR1xmhdiD6R0MSrwWcFxQzAj5b3i5Gb/mt5tvQKgFMMeNe0KZYNEN/jWr7G+xn39Azqgcvk4ZKMs8dQl8e4wA==", + "dependencies": { + "clone": "^2.1.2", + "dag-map": "~1.0.0", + "is-valid-path": "^0.1.1", + "lodash": "^4.17.13", + "md5": "~2.2.0", + "memory-cache": "~0.2.0", + "traverse": "~0.6.6", + "valid-url": "~1.0.9" + }, + "engines": { + "node": ">=6.0.0" + } }, - "node_modules/lodash.upperfirst": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/lodash.upperfirst/-/lodash.upperfirst-4.3.1.tgz", - "integrity": "sha512-sReKOYJIJf74dhJONhU4e0/shzi1trVbSWDOhKYE5XV2O+H7Sb2Dihwuc7xWxVl+DgFPyTqIN3zMfT9cq5iWDg==", - "dev": true + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" }, - "node_modules/log-symbols": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", - "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "node_modules/json-server": { + "version": "0.17.4", + "resolved": "https://registry.npmjs.org/json-server/-/json-server-0.17.4.tgz", + "integrity": "sha512-bGBb0WtFuAKbgI7JV3A864irWnMZSvBYRJbohaOuatHwKSRFUfqtQlrYMrB6WbalXy/cJabyjlb7JkHli6dYjQ==", "dev": true, "dependencies": { - "chalk": "^4.1.0", - "is-unicode-supported": "^0.1.0" + "body-parser": "^1.19.0", + "chalk": "^4.1.2", + "compression": "^1.7.4", + "connect-pause": "^0.1.1", + "cors": "^2.8.5", + "errorhandler": "^1.5.1", + "express": "^4.17.1", + "express-urlrewrite": "^1.4.0", + "json-parse-helpfulerror": "^1.0.3", + "lodash": "^4.17.21", + "lodash-id": "^0.14.1", + "lowdb": "^1.0.0", + "method-override": "^3.0.0", + "morgan": "^1.10.0", + "nanoid": "^3.1.23", + "please-upgrade-node": "^3.2.0", + "pluralize": "^8.0.0", + "server-destroy": "^1.0.1", + "yargs": "^17.0.1" }, - "engines": { - "node": ">=10" + "bin": { + "json-server": "lib/cli/bin.js" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "engines": { + "node": ">=12" } }, - "node_modules/log-symbols/node_modules/ansi-styles": { + "node_modules/json-server/node_modules/ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", @@ -23462,7 +15128,7 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/log-symbols/node_modules/chalk": { + "node_modules/json-server/node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", @@ -23478,7 +15144,7 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/log-symbols/node_modules/has-flag": { + "node_modules/json-server/node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", @@ -23487,7 +15153,7 @@ "node": ">=8" } }, - "node_modules/log-symbols/node_modules/supports-color": { + "node_modules/json-server/node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", @@ -23499,2078 +15165,1969 @@ "node": ">=8" } }, - "node_modules/log-update": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/log-update/-/log-update-4.0.0.tgz", - "integrity": "sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg==", - "dev": true, - "dependencies": { - "ansi-escapes": "^4.3.0", - "cli-cursor": "^3.1.0", - "slice-ansi": "^4.0.0", - "wrap-ansi": "^6.2.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/log-update/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/log-update/node_modules/slice-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", - "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" - }, - "engines": { - "node": ">=10" + "bin": { + "json5": "lib/cli.js" }, - "funding": { - "url": "https://github.com/chalk/slice-ansi?sponsor=1" - } - }, - "node_modules/loglevel": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.9.1.tgz", - "integrity": "sha512-hP3I3kCrDIMuRwAwHltphhDM1r8i55H33GgqjXbrisuJhF4kRhW1dNuxsRklp4bXl8DSdLaNLuiL4A/LWRfxvg==", - "dev": true, "engines": { - "node": ">= 0.6.0" - }, - "funding": { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/loglevel" + "node": ">=6" } }, - "node_modules/loglevel-plugin-prefix": { - "version": "0.8.4", - "resolved": "https://registry.npmjs.org/loglevel-plugin-prefix/-/loglevel-plugin-prefix-0.8.4.tgz", - "integrity": "sha512-WpG9CcFAOjz/FtNht+QJeGpvVl/cdR6P0z6OcXSkr8wFJOsV2GRj2j10JLfjuA4aYkcKCNIEqRGCyTife9R8/g==", + "node_modules/jsonc-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz", + "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==", "dev": true }, - "node_modules/loose-envify": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", - "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "dev": true, - "dependencies": { - "js-tokens": "^3.0.0 || ^4.0.0" - }, - "bin": { - "loose-envify": "cli.js" - } - }, - "node_modules/lowdb": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/lowdb/-/lowdb-1.0.0.tgz", - "integrity": "sha512-2+x8esE/Wb9SQ1F9IHaYWfsC9FIecLOPrK4g17FGEayjUWH172H6nwicRovGvSE2CPZouc2MCIqCI7h9d+GftQ==", + "node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", "dev": true, "dependencies": { - "graceful-fs": "^4.1.3", - "is-promise": "^2.1.0", - "lodash": "4", - "pify": "^3.0.0", - "steno": "^0.4.1" + "universalify": "^2.0.0" }, - "engines": { - "node": ">=4" - } - }, - "node_modules/lowdb/node_modules/pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/lower-case": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", - "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", - "dev": true, - "dependencies": { - "tslib": "^2.0.3" - } - }, - "node_modules/lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, - "dependencies": { - "yallist": "^3.0.2" - } - }, - "node_modules/lunr": { - "version": "2.3.9", - "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz", - "integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==", - "dev": true - }, - "node_modules/lz-string": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz", - "integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==", - "dev": true, - "bin": { - "lz-string": "bin/bin.js" + "optionalDependencies": { + "graceful-fs": "^4.1.6" } }, - "node_modules/macos-release": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/macos-release/-/macos-release-2.5.1.tgz", - "integrity": "sha512-DXqXhEM7gW59OjZO8NIjBCz9AQ1BEMrfiOAl4AYByHCtVHRF4KoGNO8mqQeM8lRCtQe/UnJ4imO/d2HdkKsd+A==", + "node_modules/jsonparse": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", + "integrity": "sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==", "dev": true, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } + "engines": [ + "node >= 0.2.0" + ] }, - "node_modules/magic-string": { - "version": "0.30.7", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.7.tgz", - "integrity": "sha512-8vBuFF/I/+OSLRmdf2wwFCJCz+nSn0m6DPvGH1fS/KiQoSaR+sETbov0eIk9KhEKy8CYqIkIAnbohxT/4H0kuA==", + "node_modules/JSONStream": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz", + "integrity": "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==", "dev": true, "dependencies": { - "@jridgewell/sourcemap-codec": "^1.4.15" + "jsonparse": "^1.2.0", + "through": ">=2.2.7 <3" }, - "engines": { - "node": ">=12" - } - }, - "node_modules/make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", - "dev": true, - "dependencies": { - "semver": "^6.0.0" + "bin": { + "JSONStream": "bin.js" }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/make-dir/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "bin": { - "semver": "bin/semver.js" + "node": "*" } }, - "node_modules/make-error": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true - }, - "node_modules/make-fetch-happen": { - "version": "13.0.0", - "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-13.0.0.tgz", - "integrity": "sha512-7ThobcL8brtGo9CavByQrQi+23aIfgYU++wg4B87AIS8Rb2ZBt/MEaDqzA00Xwv/jUjAjYkLHjVolYuTLKda2A==", - "dev": true, - "dependencies": { - "@npmcli/agent": "^2.0.0", - "cacache": "^18.0.0", - "http-cache-semantics": "^4.1.1", - "is-lambda": "^1.0.1", - "minipass": "^7.0.2", - "minipass-fetch": "^3.0.0", - "minipass-flush": "^1.0.5", - "minipass-pipeline": "^1.2.4", - "negotiator": "^0.6.3", - "promise-retry": "^2.0.1", - "ssri": "^10.0.0" - }, + "node_modules/jwt-decode": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jwt-decode/-/jwt-decode-4.0.0.tgz", + "integrity": "sha512-+KJGIyHgkGuIq3IEBNftfhW/LfWhXUIY6OmyVWjliu5KH1y0fw7VQ8YndE2O4qZdMSd9SqbnC8GOcZEy0Om7sA==", "engines": { - "node": "^16.14.0 || >=18.0.0" + "node": ">=18" } }, - "node_modules/makeerror": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", - "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", + "node_modules/karma-source-map-support": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/karma-source-map-support/-/karma-source-map-support-1.4.0.tgz", + "integrity": "sha512-RsBECncGO17KAoJCYXjv+ckIz+Ii9NCi+9enk+rq6XC81ezYkb4/RHE6CTXdA7IOJqoF3wcaLfVG0CPmE5ca6A==", "dev": true, + "peer": true, "dependencies": { - "tmpl": "1.0.5" - } - }, - "node_modules/map-obj": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.3.0.tgz", - "integrity": "sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "source-map-support": "^0.5.5" } }, - "node_modules/map-or-similar": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/map-or-similar/-/map-or-similar-1.5.0.tgz", - "integrity": "sha512-0aF7ZmVon1igznGI4VS30yugpduQW3y3GkcgGJOp7d8x8QrizhigUxjI/m2UojsXXto+jLAH3KSz+xOJTiORjg==", - "dev": true - }, - "node_modules/map-stream": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.0.7.tgz", - "integrity": "sha512-C0X0KQmGm3N2ftbTGBhSyuydQ+vV1LC3f3zPvT3RXHXNZrvfPZcoXp/N5DOa8vedX/rTMm2CjTtivFg2STJMRQ==", + "node_modules/keycharm": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/keycharm/-/keycharm-0.2.0.tgz", + "integrity": "sha512-i/XBRTiLqRConPKioy2oq45vbv04e8x59b0mnsIRQM+7Ec/8BC7UcL5pnC4FMeGb8KwG7q4wOMw7CtNZf5tiIg==", "dev": true }, - "node_modules/markdown-to-jsx": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/markdown-to-jsx/-/markdown-to-jsx-7.4.1.tgz", - "integrity": "sha512-GbrbkTnHp9u6+HqbPRFJbObi369AgJNXi/sGqq5HRsoZW063xR1XDCaConqq+whfEIAlzB1YPnOgsPc7B7bc/A==", + "node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", "dev": true, "engines": { - "node": ">= 10" - }, - "peerDependencies": { - "react": ">= 0.14.0" + "node": ">=0.10.0" } }, - "node_modules/marked": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/marked/-/marked-7.0.3.tgz", - "integrity": "sha512-ev2uM40p0zQ/GbvqotfKcSWEa59fJwluGZj5dcaUOwDRrB1F3dncdXy8NWUApk4fi8atU3kTBOwjyjZ0ud0dxw==", + "node_modules/kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", "dev": true, - "bin": { - "marked": "bin/marked.js" - }, "engines": { - "node": ">= 16" + "node": ">=6" } }, - "node_modules/md5": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/md5/-/md5-2.2.1.tgz", - "integrity": "sha512-PlGG4z5mBANDGCKsYQe0CaUYHdZYZt8ZPZLmEt+Urf0W4GlpTX4HescwHU+dc9+Z/G/vZKYZYFrwgm9VxK6QOQ==", + "node_modules/launch-editor": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/launch-editor/-/launch-editor-2.8.1.tgz", + "integrity": "sha512-elBx2l/tp9z99X5H/qev8uyDywVh0VXAwEbjk8kJhnc5grOFkGh7aW6q55me9xnYbss261XtnUrysZ+XvGbhQA==", + "dev": true, + "peer": true, "dependencies": { - "charenc": "~0.0.1", - "crypt": "~0.0.1", - "is-buffer": "~1.1.1" + "picocolors": "^1.0.0", + "shell-quote": "^1.8.1" } }, - "node_modules/mdast-util-definitions": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-definitions/-/mdast-util-definitions-4.0.0.tgz", - "integrity": "sha512-k8AJ6aNnUkB7IE+5azR9h81O5EQ/cTDXtWdMq9Kk5KcEW/8ritU5CeLg/9HhOC++nALHBlaogJ5jz0Ybk3kPMQ==", + "node_modules/less": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/less/-/less-4.2.0.tgz", + "integrity": "sha512-P3b3HJDBtSzsXUl0im2L7gTO5Ubg8mEN6G8qoTS77iXxXX4Hvu4Qj540PZDvQ8V6DmX6iXo98k7Md0Cm1PrLaA==", "dev": true, + "peer": true, "dependencies": { - "unist-util-visit": "^2.0.0" + "copy-anything": "^2.0.1", + "parse-node-version": "^1.0.1", + "tslib": "^2.3.0" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "bin": { + "lessc": "bin/lessc" + }, + "engines": { + "node": ">=6" + }, + "optionalDependencies": { + "errno": "^0.1.1", + "graceful-fs": "^4.1.2", + "image-size": "~0.5.0", + "make-dir": "^2.1.0", + "mime": "^1.4.1", + "needle": "^3.1.0", + "source-map": "~0.6.0" } }, - "node_modules/mdast-util-to-string": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-1.1.0.tgz", - "integrity": "sha512-jVU0Nr2B9X3MU4tSK7JP1CMkSvOj7X5l/GboG1tKRw52lLF1x2Ju92Ms9tNetCcbfX3hzlM73zYo2NKkWSfF/A==", + "node_modules/less-loader": { + "version": "12.2.0", + "resolved": "https://registry.npmjs.org/less-loader/-/less-loader-12.2.0.tgz", + "integrity": "sha512-MYUxjSQSBUQmowc0l5nPieOYwMzGPUaTzB6inNW/bdPEG9zOL3eAAD1Qw5ZxSPk7we5dMojHwNODYMV1hq4EVg==", "dev": true, + "peer": true, + "engines": { + "node": ">= 18.12.0" + }, "funding": { "type": "opencollective", - "url": "https://opencollective.com/unified" + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "@rspack/core": "0.x || 1.x", + "less": "^3.5.0 || ^4.0.0", + "webpack": "^5.0.0" + }, + "peerDependenciesMeta": { + "@rspack/core": { + "optional": true + }, + "webpack": { + "optional": true + } } }, - "node_modules/media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "node_modules/less/node_modules/make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "pify": "^4.0.1", + "semver": "^5.6.0" + }, "engines": { - "node": ">= 0.6" + "node": ">=6" } }, - "node_modules/memfs": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.5.3.tgz", - "integrity": "sha512-UERzLsxzllchadvbPs5aolHh65ISpKpM+ccLbOJ8/vvpBKmAWf+la7dXFy7Mr0ySHbdHrFv5kGFCUHHe6GFEmw==", + "node_modules/less/node_modules/pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", "dev": true, - "dependencies": { - "fs-monkey": "^1.0.4" - }, + "optional": true, + "peer": true, "engines": { - "node": ">= 4.0.0" + "node": ">=6" } }, - "node_modules/memoizerific": { - "version": "1.11.3", - "resolved": "https://registry.npmjs.org/memoizerific/-/memoizerific-1.11.3.tgz", - "integrity": "sha512-/EuHYwAPdLtXwAwSZkh/Gutery6pD2KYd44oQLhAvQp/50mpyduZh8Q7PYHXTCJ+wuXxt7oij2LXyIJOOYFPog==", + "node_modules/less/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", "dev": true, - "dependencies": { - "map-or-similar": "^1.5.0" + "optional": true, + "peer": true, + "bin": { + "semver": "bin/semver" } }, - "node_modules/memory-cache": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/memory-cache/-/memory-cache-0.2.0.tgz", - "integrity": "sha512-OcjA+jzjOYzKmKS6IQVALHLVz+rNTMPoJvCztFaZxwG14wtAW7VRZjwTQu06vKCYOxh4jVnik7ya0SXTB0W+xA==" - }, - "node_modules/meow": { - "version": "12.1.1", - "resolved": "https://registry.npmjs.org/meow/-/meow-12.1.1.tgz", - "integrity": "sha512-BhXM0Au22RwUneMPwSCnyhTOizdWoIEPU9sp0Aqa1PnDMR5Wv2FGXYDjuzJEIX+Eo2Rb8xuYe5jrnm5QowQFkw==", + "node_modules/less/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true, + "optional": true, + "peer": true, "engines": { - "node": ">=16.10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=0.10.0" } }, - "node_modules/merge-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==", - "dev": true - }, - "node_modules/merge-source-map": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/merge-source-map/-/merge-source-map-1.0.4.tgz", - "integrity": "sha512-PGSmS0kfnTnMJCzJ16BLLCEe6oeYCamKFFdQKshi4BmM6FUwipjVOcBFGxqtQtirtAG4iZvHlqST9CpZKqlRjA==", + "node_modules/leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", "dev": true, - "dependencies": { - "source-map": "^0.5.6" + "engines": { + "node": ">=6" } }, - "node_modules/merge-source-map/node_modules/source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", + "node_modules/license-webpack-plugin": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/license-webpack-plugin/-/license-webpack-plugin-4.0.2.tgz", + "integrity": "sha512-771TFWFD70G1wLTC4oU2Cw4qvtmNrIw+wRvBtn+okgHl7slJVi7zfNcdmqDL72BojM30VNJ2UHylr1o77U37Jw==", "dev": true, - "engines": { - "node": ">=0.10.0" + "peer": true, + "dependencies": { + "webpack-sources": "^3.0.0" + }, + "peerDependenciesMeta": { + "webpack": { + "optional": true + }, + "webpack-sources": { + "optional": true + } } }, - "node_modules/merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true - }, - "node_modules/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "node_modules/listr2": { + "version": "8.2.4", + "resolved": "https://registry.npmjs.org/listr2/-/listr2-8.2.4.tgz", + "integrity": "sha512-opevsywziHd3zHCVQGAj8zu+Z3yHNkkoYhWIGnq54RrCVwLz0MozotJEDnKsIBLvkfLGN6BLOyAeRrYI0pKA4g==", + "dev": true, + "dependencies": { + "cli-truncate": "^4.0.0", + "colorette": "^2.0.20", + "eventemitter3": "^5.0.1", + "log-update": "^6.1.0", + "rfdc": "^1.4.1", + "wrap-ansi": "^9.0.0" + }, "engines": { - "node": ">= 8" + "node": ">=18.0.0" } }, - "node_modules/method-override": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/method-override/-/method-override-3.0.0.tgz", - "integrity": "sha512-IJ2NNN/mSl9w3kzWB92rcdHpz+HjkxhDJWNDBqSlas+zQdP8wBiJzITPg08M/k2uVvMow7Sk41atndNtt/PHSA==", + "node_modules/listr2/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", "dev": true, - "dependencies": { - "debug": "3.1.0", - "methods": "~1.1.2", - "parseurl": "~1.3.2", - "vary": "~1.1.2" - }, "engines": { - "node": ">= 0.10" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, - "node_modules/method-override/node_modules/debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "node_modules/listr2/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", "dev": true, - "dependencies": { - "ms": "2.0.0" + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/method-override/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "node_modules/listr2/node_modules/emoji-regex": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.3.0.tgz", + "integrity": "sha512-QpLs9D9v9kArv4lfDEgg1X/gN5XLnf/A6l9cs8SPZLRZR3ZkY9+kwIQTxm+fsSej5UMYGE8fdoaZVIBlqG0XTw==", "dev": true }, - "node_modules/methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "node_modules/listr2/node_modules/eventemitter3": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", + "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", + "dev": true + }, + "node_modules/listr2/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", "dev": true, + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, "engines": { - "node": ">= 0.6" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "node_modules/listr2/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, "dependencies": { - "braces": "^3.0.2", - "picomatch": "^2.3.1" + "ansi-regex": "^6.0.1" }, "engines": { - "node": ">=8.6" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, - "node_modules/micromatch/node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "node_modules/listr2/node_modules/wrap-ansi": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.0.tgz", + "integrity": "sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^6.2.1", + "string-width": "^7.0.0", + "strip-ansi": "^7.1.0" + }, "engines": { - "node": ">=8.6" + "node": ">=18" }, "funding": { - "url": "https://github.com/sponsors/jonschlinkert" + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "node_modules/lmdb": { + "version": "3.0.13", + "resolved": "https://registry.npmjs.org/lmdb/-/lmdb-3.0.13.tgz", + "integrity": "sha512-UGe+BbaSUQtAMZobTb4nHvFMrmvuAQKSeaqAX2meTEQjfsbpl5sxdHD8T72OnwD4GU9uwNhYXIVe4QGs8N9Zyw==", "dev": true, + "hasInstallScript": true, + "dependencies": { + "msgpackr": "^1.10.2", + "node-addon-api": "^6.1.0", + "node-gyp-build-optional-packages": "5.2.2", + "ordered-binary": "^1.4.1", + "weak-lru-cache": "^1.2.2" + }, "bin": { - "mime": "cli.js" + "download-lmdb-prebuilds": "bin/download-prebuilds.js" }, + "optionalDependencies": { + "@lmdb/lmdb-darwin-arm64": "3.0.13", + "@lmdb/lmdb-darwin-x64": "3.0.13", + "@lmdb/lmdb-linux-arm": "3.0.13", + "@lmdb/lmdb-linux-arm64": "3.0.13", + "@lmdb/lmdb-linux-x64": "3.0.13", + "@lmdb/lmdb-win32-x64": "3.0.13" + } + }, + "node_modules/loader-runner": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", + "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", + "dev": true, + "peer": true, "engines": { - "node": ">=4" + "node": ">=6.11.5" } }, - "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "node_modules/loader-utils": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-3.3.1.tgz", + "integrity": "sha512-FMJTLMXfCLMLfJxcX9PFqX5qD88Z5MRGaZCVzfuqeZSPsyiBzs+pahDQjbIWz2QIzPZz0NX9Zy4FX3lmK6YHIg==", "dev": true, + "peer": true, "engines": { - "node": ">= 0.6" + "node": ">= 12.13.0" } }, - "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", "dev": true, "dependencies": { - "mime-db": "1.52.0" + "p-locate": "^5.0.0" }, "engines": { - "node": ">= 0.6" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, + "node_modules/lodash-id": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/lodash-id/-/lodash-id-0.14.1.tgz", + "integrity": "sha512-ikQPBTiq/d5m6dfKQlFdIXFzvThPi2Be9/AHxktOnDSfSxE1j9ICbBT5Elk1ke7HSTgM38LHTpmJovo9/klnLg==", "dev": true, "engines": { - "node": ">=6" + "node": ">= 4" } }, - "node_modules/min-indent": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", - "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", + "node_modules/lodash.camelcase": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", + "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==", + "dev": true + }, + "node_modules/lodash.clonedeep": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", + "integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==" + }, + "node_modules/lodash.debounce": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", + "dev": true + }, + "node_modules/lodash.isfunction": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/lodash.isfunction/-/lodash.isfunction-3.0.9.tgz", + "integrity": "sha512-AirXNj15uRIMMPihnkInB4i3NHeb4iBtNg9WRWuK2o31S+ePwwNmDPaTL3o7dTJ+VXNZim7rFs4rxN4YU1oUJw==", + "dev": true + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", + "dev": true + }, + "node_modules/lodash.kebabcase": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.kebabcase/-/lodash.kebabcase-4.1.1.tgz", + "integrity": "sha512-N8XRTIMMqqDgSy4VLKPnJ/+hpGZN+PHQiJnSenYqPaVV/NCqEogTnAdZLQiGKhxX+JCs8waWq2t1XHWKOmlY8g==", + "dev": true + }, + "node_modules/lodash.memoize": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", + "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", + "dev": true + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" + }, + "node_modules/lodash.mergewith": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz", + "integrity": "sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==", + "dev": true + }, + "node_modules/lodash.snakecase": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz", + "integrity": "sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw==", + "dev": true + }, + "node_modules/lodash.startcase": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.startcase/-/lodash.startcase-4.4.0.tgz", + "integrity": "sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg==", + "dev": true + }, + "node_modules/lodash.uniq": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", + "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==", + "dev": true + }, + "node_modules/lodash.upperfirst": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/lodash.upperfirst/-/lodash.upperfirst-4.3.1.tgz", + "integrity": "sha512-sReKOYJIJf74dhJONhU4e0/shzi1trVbSWDOhKYE5XV2O+H7Sb2Dihwuc7xWxVl+DgFPyTqIN3zMfT9cq5iWDg==", + "dev": true + }, + "node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", "dev": true, + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, "engines": { - "node": ">=4" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/mini-css-extract-plugin": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.8.0.tgz", - "integrity": "sha512-CxmUYPFcTgET1zImteG/LZOy/4T5rTojesQXkSNBiquhydn78tfbCE9sjIjnJ/UcjNjOC1bphTCCW5rrS7cXAg==", + "node_modules/log-symbols/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, "dependencies": { - "schema-utils": "^4.0.0", - "tapable": "^2.2.1" + "color-convert": "^2.0.1" }, "engines": { - "node": ">= 12.13.0" + "node": ">=8" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/log-symbols/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, + "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/log-symbols/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/log-symbols/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" }, - "peerDependencies": { - "webpack": "^5.0.0" + "engines": { + "node": ">=8" } }, - "node_modules/minimalistic-assert": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", - "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", - "dev": true - }, - "node_modules/minimatch": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", - "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "node_modules/log-update": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/log-update/-/log-update-6.1.0.tgz", + "integrity": "sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==", "dev": true, "dependencies": { - "brace-expansion": "^2.0.1" + "ansi-escapes": "^7.0.0", + "cli-cursor": "^5.0.0", + "slice-ansi": "^7.1.0", + "strip-ansi": "^7.1.0", + "wrap-ansi": "^9.0.0" }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": ">=18" }, "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/minimist-options": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-4.1.0.tgz", - "integrity": "sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==", + "node_modules/log-update/node_modules/ansi-escapes": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.0.0.tgz", + "integrity": "sha512-GdYO7a61mR0fOlAsvC9/rIHf7L96sBc6dEWzeOu+KAea5bZyQRPIpojrVoI4AXGJS/ycu/fBTdLrUkA4ODrvjw==", "dev": true, "dependencies": { - "arrify": "^1.0.1", - "is-plain-obj": "^1.1.0", - "kind-of": "^6.0.3" + "environment": "^1.0.0" }, "engines": { - "node": ">= 6" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/minimist-options/node_modules/is-plain-obj": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", - "integrity": "sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==", + "node_modules/log-update/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", "dev": true, "engines": { - "node": ">=0.10.0" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, - "node_modules/minipass": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz", - "integrity": "sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==", + "node_modules/log-update/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", "dev": true, "engines": { - "node": ">=16 || 14 >=14.17" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/minipass-collect": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-2.0.1.tgz", - "integrity": "sha512-D7V8PO9oaz7PWGLbCACuI1qEOsq7UKfLotx/C0Aet43fCUB/wfQ7DYeq2oR/svFJGYDHPr38SHATeaj/ZoKHKw==", + "node_modules/log-update/node_modules/cli-cursor": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz", + "integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==", "dev": true, "dependencies": { - "minipass": "^7.0.3" + "restore-cursor": "^5.0.0" }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/minipass-fetch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-3.0.4.tgz", - "integrity": "sha512-jHAqnA728uUpIaFm7NWsCnqKT6UqZz7GcI/bDpPATuwYyKwJwW0remxSCxUlKiEty+eopHGa3oc8WxgQ1FFJqg==", + "node_modules/log-update/node_modules/emoji-regex": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.3.0.tgz", + "integrity": "sha512-QpLs9D9v9kArv4lfDEgg1X/gN5XLnf/A6l9cs8SPZLRZR3ZkY9+kwIQTxm+fsSej5UMYGE8fdoaZVIBlqG0XTw==", + "dev": true + }, + "node_modules/log-update/node_modules/is-fullwidth-code-point": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-5.0.0.tgz", + "integrity": "sha512-OVa3u9kkBbw7b8Xw5F9P+D/T9X+Z4+JruYVNapTjPYZYUznQ5YfWeFkOj606XYYW8yugTfC8Pj0hYqvi4ryAhA==", "dev": true, "dependencies": { - "minipass": "^7.0.3", - "minipass-sized": "^1.0.3", - "minizlib": "^2.1.2" + "get-east-asian-width": "^1.0.0" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">=18" }, - "optionalDependencies": { - "encoding": "^0.1.13" + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/minipass-flush": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz", - "integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==", + "node_modules/log-update/node_modules/onetime": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz", + "integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==", "dev": true, "dependencies": { - "minipass": "^3.0.0" + "mimic-function": "^5.0.0" }, "engines": { - "node": ">= 8" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/minipass-flush/node_modules/minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "node_modules/log-update/node_modules/restore-cursor": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz", + "integrity": "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==", "dev": true, "dependencies": { - "yallist": "^4.0.0" + "onetime": "^7.0.0", + "signal-exit": "^4.1.0" }, "engines": { - "node": ">=8" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/minipass-flush/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, - "node_modules/minipass-json-stream": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minipass-json-stream/-/minipass-json-stream-1.0.1.tgz", - "integrity": "sha512-ODqY18UZt/I8k+b7rl2AENgbWE8IDYam+undIJONvigAz8KR5GWblsFTEfQs0WODsjbSXWlm+JHEv8Gr6Tfdbg==", + "node_modules/log-update/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", "dev": true, - "dependencies": { - "jsonparse": "^1.3.1", - "minipass": "^3.0.0" + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/minipass-json-stream/node_modules/minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "node_modules/log-update/node_modules/slice-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-7.1.0.tgz", + "integrity": "sha512-bSiSngZ/jWeX93BqeIAbImyTbEihizcwNjFoRUIY/T1wWQsfsm2Vw1agPKylXvQTU7iASGdHhyqRlqQzfz+Htg==", "dev": true, "dependencies": { - "yallist": "^4.0.0" + "ansi-styles": "^6.2.1", + "is-fullwidth-code-point": "^5.0.0" }, "engines": { - "node": ">=8" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" } }, - "node_modules/minipass-json-stream/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, - "node_modules/minipass-pipeline": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz", - "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==", + "node_modules/log-update/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", "dev": true, "dependencies": { - "minipass": "^3.0.0" + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" }, "engines": { - "node": ">=8" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/minipass-pipeline/node_modules/minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "node_modules/log-update/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", "dev": true, "dependencies": { - "yallist": "^4.0.0" + "ansi-regex": "^6.0.1" }, "engines": { - "node": ">=8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, - "node_modules/minipass-pipeline/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, - "node_modules/minipass-sized": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/minipass-sized/-/minipass-sized-1.0.3.tgz", - "integrity": "sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==", + "node_modules/log-update/node_modules/wrap-ansi": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.0.tgz", + "integrity": "sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==", "dev": true, "dependencies": { - "minipass": "^3.0.0" + "ansi-styles": "^6.2.1", + "string-width": "^7.0.0", + "strip-ansi": "^7.1.0" }, "engines": { - "node": ">=8" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/minipass-sized/node_modules/minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "node_modules/loglevel": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.9.1.tgz", + "integrity": "sha512-hP3I3kCrDIMuRwAwHltphhDM1r8i55H33GgqjXbrisuJhF4kRhW1dNuxsRklp4bXl8DSdLaNLuiL4A/LWRfxvg==", "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, "engines": { - "node": ">=8" + "node": ">= 0.6.0" + }, + "funding": { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/loglevel" } }, - "node_modules/minipass-sized/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "node_modules/loglevel-plugin-prefix": { + "version": "0.8.4", + "resolved": "https://registry.npmjs.org/loglevel-plugin-prefix/-/loglevel-plugin-prefix-0.8.4.tgz", + "integrity": "sha512-WpG9CcFAOjz/FtNht+QJeGpvVl/cdR6P0z6OcXSkr8wFJOsV2GRj2j10JLfjuA4aYkcKCNIEqRGCyTife9R8/g==", "dev": true }, - "node_modules/minizlib": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", - "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "node_modules/lowdb": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lowdb/-/lowdb-1.0.0.tgz", + "integrity": "sha512-2+x8esE/Wb9SQ1F9IHaYWfsC9FIecLOPrK4g17FGEayjUWH172H6nwicRovGvSE2CPZouc2MCIqCI7h9d+GftQ==", "dev": true, "dependencies": { - "minipass": "^3.0.0", - "yallist": "^4.0.0" + "graceful-fs": "^4.1.3", + "is-promise": "^2.1.0", + "lodash": "4", + "pify": "^3.0.0", + "steno": "^0.4.1" }, "engines": { - "node": ">= 8" + "node": ">=4" } }, - "node_modules/minizlib/node_modules/minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "node_modules/lowdb/node_modules/pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/lower-case": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", + "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "tslib": "^2.0.3" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", "dev": true, "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" + "yallist": "^3.0.2" } }, - "node_modules/minizlib/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "node_modules/lunr": { + "version": "2.3.9", + "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz", + "integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==", "dev": true }, - "node_modules/mkdirp": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz", - "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==", + "node_modules/macos-release": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/macos-release/-/macos-release-2.5.1.tgz", + "integrity": "sha512-DXqXhEM7gW59OjZO8NIjBCz9AQ1BEMrfiOAl4AYByHCtVHRF4KoGNO8mqQeM8lRCtQe/UnJ4imO/d2HdkKsd+A==", "dev": true, - "bin": { - "mkdirp": "dist/cjs/src/bin.js" - }, "engines": { - "node": ">=10" + "node": ">=6" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/magic-string": { + "version": "0.30.11", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.11.tgz", + "integrity": "sha512-+Wri9p0QHMy+545hKww7YAu5NyzF8iomPL/RQazugQ9+Ez4Ic3mERMd8ZTX5rfK944j+560ZJi8iAwgak1Ac7A==", + "dev": true, + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0" } }, - "node_modules/mkdirp-classic": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", - "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", "dev": true }, - "node_modules/mocha": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.3.0.tgz", - "integrity": "sha512-uF2XJs+7xSLsrmIvn37i/wnc91nw7XjOQB8ccyx5aEgdnohr7n+rEiZP23WkCYHjilR6+EboEnbq/ZQDz4LSbg==", + "node_modules/make-fetch-happen": { + "version": "13.0.1", + "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-13.0.1.tgz", + "integrity": "sha512-cKTUFc/rbKUd/9meOvgrpJ2WrNzymt6jfRDdwg5UCnVzv9dTpEj9JS5m3wtziXVCjluIXyL8pcaukYqezIzZQA==", "dev": true, - "peer": true, "dependencies": { - "ansi-colors": "4.1.1", - "browser-stdout": "1.3.1", - "chokidar": "3.5.3", - "debug": "4.3.4", - "diff": "5.0.0", - "escape-string-regexp": "4.0.0", - "find-up": "5.0.0", - "glob": "8.1.0", - "he": "1.2.0", - "js-yaml": "4.1.0", - "log-symbols": "4.1.0", - "minimatch": "5.0.1", - "ms": "2.1.3", - "serialize-javascript": "6.0.0", - "strip-json-comments": "3.1.1", - "supports-color": "8.1.1", - "workerpool": "6.2.1", - "yargs": "16.2.0", - "yargs-parser": "20.2.4", - "yargs-unparser": "2.0.0" - }, - "bin": { - "_mocha": "bin/_mocha", - "mocha": "bin/mocha.js" + "@npmcli/agent": "^2.0.0", + "cacache": "^18.0.0", + "http-cache-semantics": "^4.1.1", + "is-lambda": "^1.0.1", + "minipass": "^7.0.2", + "minipass-fetch": "^3.0.0", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^0.6.3", + "proc-log": "^4.2.0", + "promise-retry": "^2.0.1", + "ssri": "^10.0.0" }, "engines": { - "node": ">= 14.0.0" + "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/mocha/node_modules/ansi-colors": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", - "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "node_modules/makeerror": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", + "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", "dev": true, - "peer": true, - "engines": { - "node": ">=6" + "dependencies": { + "tmpl": "1.0.5" } }, - "node_modules/mocha/node_modules/ansi-styles": { + "node_modules/map-obj": { "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.3.0.tgz", + "integrity": "sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==", "dev": true, - "peer": true, - "dependencies": { - "color-convert": "^2.0.1" - }, "engines": { "node": ">=8" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/mocha/node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true, - "peer": true + "node_modules/map-stream": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.0.7.tgz", + "integrity": "sha512-C0X0KQmGm3N2ftbTGBhSyuydQ+vV1LC3f3zPvT3RXHXNZrvfPZcoXp/N5DOa8vedX/rTMm2CjTtivFg2STJMRQ==", + "dev": true }, - "node_modules/mocha/node_modules/chokidar": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", - "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "node_modules/marked": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/marked/-/marked-7.0.3.tgz", + "integrity": "sha512-ev2uM40p0zQ/GbvqotfKcSWEa59fJwluGZj5dcaUOwDRrB1F3dncdXy8NWUApk4fi8atU3kTBOwjyjZ0ud0dxw==", "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - ], - "peer": true, - "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" + "bin": { + "marked": "bin/marked.js" }, "engines": { - "node": ">= 8.10.0" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" + "node": ">= 16" } }, - "node_modules/mocha/node_modules/cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", - "dev": true, - "peer": true, + "node_modules/md5": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/md5/-/md5-2.2.1.tgz", + "integrity": "sha512-PlGG4z5mBANDGCKsYQe0CaUYHdZYZt8ZPZLmEt+Urf0W4GlpTX4HescwHU+dc9+Z/G/vZKYZYFrwgm9VxK6QOQ==", "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" + "charenc": "~0.0.1", + "crypt": "~0.0.1", + "is-buffer": "~1.1.1" } }, - "node_modules/mocha/node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", "dev": true, - "peer": true, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">= 0.6" } }, - "node_modules/mocha/node_modules/glob": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", - "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", + "node_modules/memfs": { + "version": "4.11.1", + "resolved": "https://registry.npmjs.org/memfs/-/memfs-4.11.1.tgz", + "integrity": "sha512-LZcMTBAgqUUKNXZagcZxvXXfgF1bHX7Y7nQ0QyEiNbRJgE29GhgPd8Yna1VQcLlPiHt/5RFJMWYN9Uv/VPNvjQ==", "dev": true, "peer": true, "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^5.0.1", - "once": "^1.3.0" + "@jsonjoy.com/json-pack": "^1.0.3", + "@jsonjoy.com/util": "^1.3.0", + "tree-dump": "^1.0.1", + "tslib": "^2.0.0" }, "engines": { - "node": ">=12" + "node": ">= 4.0.0" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "type": "github", + "url": "https://github.com/sponsors/streamich" } }, - "node_modules/mocha/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "node_modules/memory-cache": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/memory-cache/-/memory-cache-0.2.0.tgz", + "integrity": "sha512-OcjA+jzjOYzKmKS6IQVALHLVz+rNTMPoJvCztFaZxwG14wtAW7VRZjwTQu06vKCYOxh4jVnik7ya0SXTB0W+xA==" + }, + "node_modules/meow": { + "version": "12.1.1", + "resolved": "https://registry.npmjs.org/meow/-/meow-12.1.1.tgz", + "integrity": "sha512-BhXM0Au22RwUneMPwSCnyhTOizdWoIEPU9sp0Aqa1PnDMR5Wv2FGXYDjuzJEIX+Eo2Rb8xuYe5jrnm5QowQFkw==", "dev": true, - "peer": true, "engines": { - "node": ">=8" + "node": ">=16.10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/mocha/node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "node_modules/merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==", + "dev": true + }, + "node_modules/merge-source-map": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/merge-source-map/-/merge-source-map-1.0.4.tgz", + "integrity": "sha512-PGSmS0kfnTnMJCzJ16BLLCEe6oeYCamKFFdQKshi4BmM6FUwipjVOcBFGxqtQtirtAG4iZvHlqST9CpZKqlRjA==", "dev": true, - "peer": true, "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" + "source-map": "^0.5.6" } }, - "node_modules/mocha/node_modules/minimatch": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz", - "integrity": "sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==", + "node_modules/merge-source-map/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", "dev": true, - "peer": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, "engines": { - "node": ">=10" + "node": ">=0.10.0" } }, - "node_modules/mocha/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, - "peer": true + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true }, - "node_modules/mocha/node_modules/serialize-javascript": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", - "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", "dev": true, - "peer": true, - "dependencies": { - "randombytes": "^2.1.0" + "engines": { + "node": ">= 8" } }, - "node_modules/mocha/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "node_modules/method-override": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/method-override/-/method-override-3.0.0.tgz", + "integrity": "sha512-IJ2NNN/mSl9w3kzWB92rcdHpz+HjkxhDJWNDBqSlas+zQdP8wBiJzITPg08M/k2uVvMow7Sk41atndNtt/PHSA==", "dev": true, - "peer": true, "dependencies": { - "has-flag": "^4.0.0" + "debug": "3.1.0", + "methods": "~1.1.2", + "parseurl": "~1.3.2", + "vary": "~1.1.2" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" + "node": ">= 0.10" } }, - "node_modules/mocha/node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "node_modules/method-override/node_modules/debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/method-override/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", "dev": true, - "peer": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + "node": ">= 0.6" } }, - "node_modules/mocha/node_modules/yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "node_modules/micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", "dev": true, - "peer": true, "dependencies": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" + "braces": "^3.0.2", + "picomatch": "^2.3.1" }, "engines": { - "node": ">=10" + "node": ">=8.6" } }, - "node_modules/moment": { - "version": "2.30.1", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz", - "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==", + "node_modules/micromatch/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true, "engines": { - "node": "*" + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/morgan": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.10.0.tgz", - "integrity": "sha512-AbegBVI4sh6El+1gNwvD5YIck7nSA36weD7xvIxG4in80j/UoK8AEGaWnnz8v1GxonMCltmlNs5ZKbGvl9b1XQ==", + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", "dev": true, - "dependencies": { - "basic-auth": "~2.0.1", - "debug": "2.6.9", - "depd": "~2.0.0", - "on-finished": "~2.3.0", - "on-headers": "~1.0.2" + "bin": { + "mime": "cli.js" }, "engines": { - "node": ">= 0.8.0" + "node": ">=4" } }, - "node_modules/morgan/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", "dev": true, - "dependencies": { - "ms": "2.0.0" + "engines": { + "node": ">= 0.6" } }, - "node_modules/morgan/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true - }, - "node_modules/morgan/node_modules/on-finished": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", - "integrity": "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==", + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", "dev": true, "dependencies": { - "ee-first": "1.1.1" + "mime-db": "1.52.0" }, "engines": { - "node": ">= 0.8" + "node": ">= 0.6" } }, - "node_modules/mrmime": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.0.tgz", - "integrity": "sha512-eu38+hdgojoyq63s+yTpN4XMBdt5l8HhMhc4VKLO9KM5caLIBvUm4thi7fFaxyTmCKeNnXZ5pAlBwCUnhA09uw==", + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", "dev": true, "engines": { - "node": ">=10" + "node": ">=6" } }, - "node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "node_modules/multicast-dns": { - "version": "7.2.5", - "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-7.2.5.tgz", - "integrity": "sha512-2eznPJP8z2BFLX50tf0LuODrpINqP1RVIm/CObbTcBRITQgmC/TjcREF1NeTBzIcR5XO/ukWo+YHOjBbFwIupg==", + "node_modules/mimic-function": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz", + "integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==", "dev": true, - "dependencies": { - "dns-packet": "^5.2.2", - "thunky": "^1.0.2" + "engines": { + "node": ">=18" }, - "bin": { - "multicast-dns": "cli.js" + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/mute-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-1.0.0.tgz", - "integrity": "sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA==", + "node_modules/min-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", + "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", "dev": true, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": ">=4" } }, - "node_modules/nanoid": { - "version": "3.3.7", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", - "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", + "node_modules/mini-css-extract-plugin": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.9.0.tgz", + "integrity": "sha512-Zs1YsZVfemekSZG+44vBsYTLQORkPMwnlv+aehcxK/NLKC+EGhDB39/YePYYqx/sTk6NnYpuqikhSn7+JIevTA==", "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "bin": { - "nanoid": "bin/nanoid.cjs" + "peer": true, + "dependencies": { + "schema-utils": "^4.0.0", + "tapable": "^2.2.1" }, "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" } }, - "node_modules/natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==" + "node_modules/minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", + "dev": true, + "peer": true }, - "node_modules/needle": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/needle/-/needle-3.3.1.tgz", - "integrity": "sha512-6k0YULvhpw+RoLNiQCRKOl09Rv1dPLr8hHnVjHqdolKwDrdNyk+Hmrthi4lIGPPz3r39dLx0hsF5s40sZ3Us4Q==", + "node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", "dev": true, - "optional": true, "dependencies": { - "iconv-lite": "^0.6.3", - "sax": "^1.2.4" - }, - "bin": { - "needle": "bin/needle" + "brace-expansion": "^2.0.1" }, "engines": { - "node": ">= 4.4.x" + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/needle/node_modules/iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", "dev": true, - "optional": true, - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - }, - "engines": { - "node": ">=0.10.0" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/negotiator": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", - "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "node_modules/minimist-options": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-4.1.0.tgz", + "integrity": "sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==", "dev": true, + "dependencies": { + "arrify": "^1.0.1", + "is-plain-obj": "^1.1.0", + "kind-of": "^6.0.3" + }, "engines": { - "node": ">= 0.6" + "node": ">= 6" } }, - "node_modules/neo-async": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", - "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", - "dev": true - }, - "node_modules/next-tick": { + "node_modules/minimist-options/node_modules/is-plain-obj": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz", - "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==", - "dev": true - }, - "node_modules/ngrx-store-localstorage": { - "version": "17.0.0", - "resolved": "https://registry.npmjs.org/ngrx-store-localstorage/-/ngrx-store-localstorage-17.0.0.tgz", - "integrity": "sha512-tub6zwYsU+5aG8y2pV30C9Gz7ff2nlmUDOB4O9JVxU6ZrgogDzDR/+FpY5BgPv/1KNYD0aNXCfv0iCuxDVMBEQ==", - "dependencies": { - "deepmerge": "^4.2.2", - "tslib": "^2.3.0" - }, - "peerDependencies": { - "@angular/common": "^17.0.4", - "@angular/core": "^17.0.4", - "@ngrx/store": "^17.0.0" + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", + "integrity": "sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==", + "dev": true, + "engines": { + "node": ">=0.10.0" } }, - "node_modules/nice-napi": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/nice-napi/-/nice-napi-1.0.2.tgz", - "integrity": "sha512-px/KnJAJZf5RuBGcfD+Sp2pAKq0ytz8j+1NehvgIGFkvtvFrDM3T8E4x/JJODXK9WZow8RRGrbA9QQ3hs+pDhA==", + "node_modules/minipass": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz", + "integrity": "sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==", "dev": true, - "hasInstallScript": true, - "optional": true, - "os": [ - "!win32" - ], - "dependencies": { - "node-addon-api": "^3.0.0", - "node-gyp-build": "^4.2.2" + "engines": { + "node": ">=16 || 14 >=14.17" } }, - "node_modules/no-case": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", - "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", + "node_modules/minipass-collect": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-2.0.1.tgz", + "integrity": "sha512-D7V8PO9oaz7PWGLbCACuI1qEOsq7UKfLotx/C0Aet43fCUB/wfQ7DYeq2oR/svFJGYDHPr38SHATeaj/ZoKHKw==", "dev": true, "dependencies": { - "lower-case": "^2.0.2", - "tslib": "^2.0.3" + "minipass": "^7.0.3" + }, + "engines": { + "node": ">=16 || 14 >=14.17" } }, - "node_modules/node-abort-controller": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/node-abort-controller/-/node-abort-controller-3.1.1.tgz", - "integrity": "sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==", - "dev": true - }, - "node_modules/node-addon-api": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.2.1.tgz", - "integrity": "sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A==", - "dev": true, - "optional": true - }, - "node_modules/node-dir": { - "version": "0.1.17", - "resolved": "https://registry.npmjs.org/node-dir/-/node-dir-0.1.17.tgz", - "integrity": "sha512-tmPX422rYgofd4epzrNoOXiE8XFZYOcCq1vD7MAXCDO+O+zndlA2ztdKKMa+EeuBG5tHETpr4ml4RGgpqDCCAg==", + "node_modules/minipass-fetch": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-3.0.5.tgz", + "integrity": "sha512-2N8elDQAtSnFV0Dk7gt15KHsS0Fyz6CbYZ360h0WTYV1Ty46li3rAXVOQj1THMNLdmrD9Vt5pBPtWtVkpwGBqg==", "dev": true, "dependencies": { - "minimatch": "^3.0.2" + "minipass": "^7.0.3", + "minipass-sized": "^1.0.3", + "minizlib": "^2.1.2" }, "engines": { - "node": ">= 0.10.5" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + }, + "optionalDependencies": { + "encoding": "^0.1.13" } }, - "node_modules/node-dir/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "node_modules/minipass-flush": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz", + "integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==", "dev": true, "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" } }, - "node_modules/node-dir/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "node_modules/minipass-flush/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", "dev": true, "dependencies": { - "brace-expansion": "^1.1.7" + "yallist": "^4.0.0" }, "engines": { - "node": "*" + "node": ">=8" } }, - "node_modules/node-downloader-helper": { - "version": "2.1.9", - "resolved": "https://registry.npmjs.org/node-downloader-helper/-/node-downloader-helper-2.1.9.tgz", - "integrity": "sha512-FSvAol2Z8UP191sZtsUZwHIN0eGoGue3uEXGdWIH5228e9KH1YHXT7fN8Oa33UGf+FbqGTQg3sJfrRGzmVCaJA==", + "node_modules/minipass-flush/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/minipass-pipeline": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz", + "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==", "dev": true, - "bin": { - "ndh": "bin/ndh" + "dependencies": { + "minipass": "^3.0.0" }, "engines": { - "node": ">=14.18" + "node": ">=8" } }, - "node_modules/node-fetch": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", - "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "node_modules/minipass-pipeline/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dev": true, "dependencies": { - "whatwg-url": "^5.0.0" + "yallist": "^4.0.0" }, "engines": { - "node": "4.x || >=6.0.0" - }, - "peerDependencies": { - "encoding": "^0.1.0" - }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } + "node": ">=8" } }, - "node_modules/node-fetch-native": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/node-fetch-native/-/node-fetch-native-1.6.2.tgz", - "integrity": "sha512-69mtXOFZ6hSkYiXAVB5SqaRvrbITC/NPyqv7yuu/qw0nmgPyYbIMYYNIDhNtwPrzk0ptrimrLz/hhjvm4w5Z+w==", + "node_modules/minipass-pipeline/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "dev": true }, - "node_modules/node-fetch/node_modules/tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" - }, - "node_modules/node-fetch/node_modules/webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" - }, - "node_modules/node-fetch/node_modules/whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "node_modules/minipass-sized": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/minipass-sized/-/minipass-sized-1.0.3.tgz", + "integrity": "sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==", + "dev": true, "dependencies": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" } }, - "node_modules/node-forge": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", - "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==", + "node_modules/minipass-sized/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, "engines": { - "node": ">= 6.13.0" + "node": ">=8" } }, - "node_modules/node-gyp": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-10.0.1.tgz", - "integrity": "sha512-gg3/bHehQfZivQVfqIyy8wTdSymF9yTyP4CJifK73imyNMU8AIGQE2pUa7dNWfmMeG9cDVF2eehiRMv0LC1iAg==", + "node_modules/minipass-sized/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", "dev": true, "dependencies": { - "env-paths": "^2.2.0", - "exponential-backoff": "^3.1.1", - "glob": "^10.3.10", - "graceful-fs": "^4.2.6", - "make-fetch-happen": "^13.0.0", - "nopt": "^7.0.0", - "proc-log": "^3.0.0", - "semver": "^7.3.5", - "tar": "^6.1.2", - "which": "^4.0.0" + "minipass": "^3.0.0", + "yallist": "^4.0.0" }, - "bin": { - "node-gyp": "bin/node-gyp.js" + "engines": { + "node": ">= 8" + } + }, + "node_modules/minizlib/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" }, "engines": { - "node": "^16.14.0 || >=18.0.0" + "node": ">=8" } }, - "node_modules/node-gyp-build": { - "version": "4.8.0", - "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.0.tgz", - "integrity": "sha512-u6fs2AEUljNho3EYTJNBfImO5QTo/J/1Etd+NVdCj7qWKUSN/bSLkZwhDv7I+w/MSC6qJ4cknepkAYykDdK8og==", + "node_modules/minizlib/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/mkdirp": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz", + "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==", "dev": true, - "optional": true, "bin": { - "node-gyp-build": "bin.js", - "node-gyp-build-optional": "optional.js", - "node-gyp-build-test": "build-test.js" + "mkdirp": "dist/cjs/src/bin.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/node-gyp/node_modules/isexe": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz", - "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==", + "node_modules/moment": { + "version": "2.30.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz", + "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==", "dev": true, "engines": { - "node": ">=16" + "node": "*" } }, - "node_modules/node-gyp/node_modules/which": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz", - "integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==", + "node_modules/morgan": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.10.0.tgz", + "integrity": "sha512-AbegBVI4sh6El+1gNwvD5YIck7nSA36weD7xvIxG4in80j/UoK8AEGaWnnz8v1GxonMCltmlNs5ZKbGvl9b1XQ==", "dev": true, "dependencies": { - "isexe": "^3.1.1" - }, - "bin": { - "node-which": "bin/which.js" + "basic-auth": "~2.0.1", + "debug": "2.6.9", + "depd": "~2.0.0", + "on-finished": "~2.3.0", + "on-headers": "~1.0.2" }, "engines": { - "node": "^16.13.0 || >=18.0.0" + "node": ">= 0.8.0" } }, - "node_modules/node-int64": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", - "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", - "dev": true - }, - "node_modules/node-machine-id": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/node-machine-id/-/node-machine-id-1.1.12.tgz", - "integrity": "sha512-QNABxbrPa3qEIfrE6GOJ7BYIuignnJw7iQ2YPbc3Nla1HzRJjXzZOiikfF8m7eAMfichLt3M4VgLOetqgDmgGQ==", - "dev": true - }, - "node_modules/node-releases": { - "version": "2.0.14", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", - "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==", - "dev": true - }, - "node_modules/nopt": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-7.2.0.tgz", - "integrity": "sha512-CVDtwCdhYIvnAzFoJ6NJ6dX3oga9/HyciQDnG1vQDjSLMeKLJ4A93ZqYKDrgYSr1FBY5/hMYC+2VCi24pgpkGA==", + "node_modules/morgan/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "dev": true, "dependencies": { - "abbrev": "^2.0.0" - }, - "bin": { - "nopt": "bin/nopt.js" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "ms": "2.0.0" } }, - "node_modules/normalize-package-data": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-6.0.0.tgz", - "integrity": "sha512-UL7ELRVxYBHBgYEtZCXjxuD5vPxnmvMGq0jp/dGPKKrN7tfsBh2IY7TlJ15WWwdjRWD3RJbnsygUurTK3xkPkg==", + "node_modules/morgan/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "node_modules/morgan/node_modules/on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==", "dev": true, "dependencies": { - "hosted-git-info": "^7.0.0", - "is-core-module": "^2.8.1", - "semver": "^7.3.5", - "validate-npm-package-license": "^3.0.4" + "ee-first": "1.1.1" }, "engines": { - "node": "^16.14.0 || >=18.0.0" + "node": ">= 0.8" } }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "node_modules/mrmime": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.0.tgz", + "integrity": "sha512-eu38+hdgojoyq63s+yTpN4XMBdt5l8HhMhc4VKLO9KM5caLIBvUm4thi7fFaxyTmCKeNnXZ5pAlBwCUnhA09uw==", "dev": true, "engines": { - "node": ">=0.10.0" + "node": ">=10" } }, - "node_modules/normalize-range": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", - "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/msgpackr": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/msgpackr/-/msgpackr-1.11.0.tgz", + "integrity": "sha512-I8qXuuALqJe5laEBYoFykChhSXLikZmUhccjGsPuSJ/7uPip2TJ7lwdIQwWSAi0jGZDXv4WOP8Qg65QZRuXxXw==", "dev": true, - "engines": { - "node": ">=0.10.0" + "optionalDependencies": { + "msgpackr-extract": "^3.0.2" } }, - "node_modules/npm-bundled": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-3.0.0.tgz", - "integrity": "sha512-Vq0eyEQy+elFpzsKjMss9kxqb9tG3YHg4dsyWuUENuzvSUWe1TCnW/vV9FkhvBk/brEDoDiVd+M1Btosa6ImdQ==", + "node_modules/msgpackr-extract": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/msgpackr-extract/-/msgpackr-extract-3.0.3.tgz", + "integrity": "sha512-P0efT1C9jIdVRefqjzOQ9Xml57zpOXnIuS+csaB4MdZbTdmGDLo8XhzBG1N7aO11gKDDkJvBLULeFTo46wwreA==", "dev": true, + "hasInstallScript": true, + "optional": true, "dependencies": { - "npm-normalize-package-bin": "^3.0.0" + "node-gyp-build-optional-packages": "5.2.2" }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "bin": { + "download-msgpackr-prebuilds": "bin/download-prebuilds.js" + }, + "optionalDependencies": { + "@msgpackr-extract/msgpackr-extract-darwin-arm64": "3.0.3", + "@msgpackr-extract/msgpackr-extract-darwin-x64": "3.0.3", + "@msgpackr-extract/msgpackr-extract-linux-arm": "3.0.3", + "@msgpackr-extract/msgpackr-extract-linux-arm64": "3.0.3", + "@msgpackr-extract/msgpackr-extract-linux-x64": "3.0.3", + "@msgpackr-extract/msgpackr-extract-win32-x64": "3.0.3" } }, - "node_modules/npm-install-checks": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/npm-install-checks/-/npm-install-checks-6.3.0.tgz", - "integrity": "sha512-W29RiK/xtpCGqn6f3ixfRYGk+zRyr+Ew9F2E20BfXxT5/euLdA/Nm7fO7OeTGuAmTs30cpgInyJ0cYe708YTZw==", + "node_modules/multicast-dns": { + "version": "7.2.5", + "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-7.2.5.tgz", + "integrity": "sha512-2eznPJP8z2BFLX50tf0LuODrpINqP1RVIm/CObbTcBRITQgmC/TjcREF1NeTBzIcR5XO/ukWo+YHOjBbFwIupg==", "dev": true, + "peer": true, "dependencies": { - "semver": "^7.1.1" + "dns-packet": "^5.2.2", + "thunky": "^1.0.2" }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "bin": { + "multicast-dns": "cli.js" } }, - "node_modules/npm-normalize-package-bin": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-3.0.1.tgz", - "integrity": "sha512-dMxCf+zZ+3zeQZXKxmyuCKlIDPGuv8EF940xbkC4kQVDTtqoh6rJFO+JTKSA6/Rwi0getWmtuy4Itup0AMcaDQ==", + "node_modules/mute-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-1.0.0.tgz", + "integrity": "sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA==", "dev": true, "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/npm-package-arg": { - "version": "11.0.1", - "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-11.0.1.tgz", - "integrity": "sha512-M7s1BD4NxdAvBKUPqqRW957Xwcl/4Zvo8Aj+ANrzvIPzGJZElrH7Z//rSaec2ORcND6FHHLnZeY8qgTpXDMFQQ==", + "node_modules/nanoid": { + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", + "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", "dev": true, - "dependencies": { - "hosted-git-info": "^7.0.0", - "proc-log": "^3.0.0", - "semver": "^7.3.5", - "validate-npm-package-name": "^5.0.0" + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "bin": { + "nanoid": "bin/nanoid.cjs" }, "engines": { - "node": "^16.14.0 || >=18.0.0" + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, - "node_modules/npm-packlist": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-8.0.2.tgz", - "integrity": "sha512-shYrPFIS/JLP4oQmAwDyk5HcyysKW8/JLTEA32S0Z5TzvpaeeX2yMFfoK1fjEBnCBvVyIB/Jj/GBFdm0wsgzbA==", - "dev": true, - "dependencies": { - "ignore-walk": "^6.0.4" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true }, - "node_modules/npm-pick-manifest": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-9.0.0.tgz", - "integrity": "sha512-VfvRSs/b6n9ol4Qb+bDwNGUXutpy76x6MARw/XssevE0TnctIKcmklJZM5Z7nqs5z5aW+0S63pgCNbpkUNNXBg==", + "node_modules/needle": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/needle/-/needle-3.3.1.tgz", + "integrity": "sha512-6k0YULvhpw+RoLNiQCRKOl09Rv1dPLr8hHnVjHqdolKwDrdNyk+Hmrthi4lIGPPz3r39dLx0hsF5s40sZ3Us4Q==", "dev": true, + "optional": true, + "peer": true, "dependencies": { - "npm-install-checks": "^6.0.0", - "npm-normalize-package-bin": "^3.0.0", - "npm-package-arg": "^11.0.0", - "semver": "^7.3.5" + "iconv-lite": "^0.6.3", + "sax": "^1.2.4" + }, + "bin": { + "needle": "bin/needle" }, "engines": { - "node": "^16.14.0 || >=18.0.0" + "node": ">= 4.4.x" } }, - "node_modules/npm-registry-fetch": { - "version": "16.1.0", - "resolved": "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-16.1.0.tgz", - "integrity": "sha512-PQCELXKt8Azvxnt5Y85GseQDJJlglTFM9L9U9gkv2y4e9s0k3GVDdOx3YoB6gm2Do0hlkzC39iCGXby+Wve1Bw==", + "node_modules/needle/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", "dev": true, + "optional": true, + "peer": true, "dependencies": { - "make-fetch-happen": "^13.0.0", - "minipass": "^7.0.2", - "minipass-fetch": "^3.0.0", - "minipass-json-stream": "^1.0.1", - "minizlib": "^2.1.2", - "npm-package-arg": "^11.0.0", - "proc-log": "^3.0.0" + "safer-buffer": ">= 2.1.2 < 3.0.0" }, "engines": { - "node": "^16.14.0 || >=18.0.0" + "node": ">=0.10.0" } }, - "node_modules/npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", "dev": true, - "dependencies": { - "path-key": "^3.0.0" - }, "engines": { - "node": ">=8" + "node": ">= 0.6" } }, - "node_modules/nth-check": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", - "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", - "dev": true, - "dependencies": { - "boolbase": "^1.0.0" - }, - "funding": { - "url": "https://github.com/fb55/nth-check?sponsor=1" - } + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "dev": true }, - "node_modules/nwsapi": { - "version": "2.2.7", - "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.7.tgz", - "integrity": "sha512-ub5E4+FBPKwAZx0UwIQOjYWGHTEq5sPqHQNRN8Z9e4A7u3Tj1weLJsL59yH9vmvqEtBHaOmT6cYQKIZOxp35FQ==", + "node_modules/next-tick": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz", + "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==", "dev": true }, - "node_modules/nx": { - "version": "17.2.8", - "resolved": "https://registry.npmjs.org/nx/-/nx-17.2.8.tgz", - "integrity": "sha512-rM5zXbuXLEuqQqcjVjClyvHwRJwt+NVImR2A6KFNG40Z60HP6X12wAxxeLHF5kXXTDRU0PFhf/yACibrpbPrAw==", - "dev": true, - "hasInstallScript": true, + "node_modules/ngrx-store-localstorage": { + "version": "18.0.0", + "resolved": "https://registry.npmjs.org/ngrx-store-localstorage/-/ngrx-store-localstorage-18.0.0.tgz", + "integrity": "sha512-WoDePvMWiWF9LQHe+dTqbpm8lxoKCPoIvA0/1enIPTmdLQsOpdDKhMSD5YgwuqDusNfEik3QslProTFGyXZwtw==", "dependencies": { - "@nrwl/tao": "17.2.8", - "@yarnpkg/lockfile": "^1.1.0", - "@yarnpkg/parsers": "3.0.0-rc.46", - "@zkochan/js-yaml": "0.0.6", - "axios": "^1.5.1", - "chalk": "^4.1.0", - "cli-cursor": "3.1.0", - "cli-spinners": "2.6.1", - "cliui": "^8.0.1", - "dotenv": "~16.3.1", - "dotenv-expand": "~10.0.0", - "enquirer": "~2.3.6", - "figures": "3.2.0", - "flat": "^5.0.2", - "fs-extra": "^11.1.0", - "glob": "7.1.4", - "ignore": "^5.0.4", - "jest-diff": "^29.4.1", - "js-yaml": "4.1.0", - "jsonc-parser": "3.2.0", - "lines-and-columns": "~2.0.3", - "minimatch": "3.0.5", - "node-machine-id": "1.1.12", - "npm-run-path": "^4.0.1", - "open": "^8.4.0", - "semver": "7.5.3", - "string-width": "^4.2.3", - "strong-log-transformer": "^2.1.0", - "tar-stream": "~2.2.0", - "tmp": "~0.2.1", - "tsconfig-paths": "^4.1.2", - "tslib": "^2.3.0", - "yargs": "^17.6.2", - "yargs-parser": "21.1.1" - }, - "bin": { - "nx": "bin/nx.js", - "nx-cloud": "bin/nx-cloud.js" - }, - "optionalDependencies": { - "@nx/nx-darwin-arm64": "17.2.8", - "@nx/nx-darwin-x64": "17.2.8", - "@nx/nx-freebsd-x64": "17.2.8", - "@nx/nx-linux-arm-gnueabihf": "17.2.8", - "@nx/nx-linux-arm64-gnu": "17.2.8", - "@nx/nx-linux-arm64-musl": "17.2.8", - "@nx/nx-linux-x64-gnu": "17.2.8", - "@nx/nx-linux-x64-musl": "17.2.8", - "@nx/nx-win32-arm64-msvc": "17.2.8", - "@nx/nx-win32-x64-msvc": "17.2.8" + "deepmerge": "^4.2.2", + "tslib": "^2.3.0" }, "peerDependencies": { - "@swc-node/register": "^1.6.7", - "@swc/core": "^1.3.85" - }, - "peerDependenciesMeta": { - "@swc-node/register": { - "optional": true - }, - "@swc/core": { - "optional": true - } + "@angular/common": "^18.0.1", + "@angular/core": "^18.0.1", + "@ngrx/store": "^18.0.0" } }, - "node_modules/nx/node_modules/@nx/nx-darwin-arm64": { - "version": "17.2.8", - "resolved": "https://registry.npmjs.org/@nx/nx-darwin-arm64/-/nx-darwin-arm64-17.2.8.tgz", - "integrity": "sha512-dMb0uxug4hM7tusISAU1TfkDK3ixYmzc1zhHSZwpR7yKJIyKLtUpBTbryt8nyso37AS1yH+dmfh2Fj2WxfBHTg==", - "cpu": [ - "arm64" - ], + "node_modules/nice-napi": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/nice-napi/-/nice-napi-1.0.2.tgz", + "integrity": "sha512-px/KnJAJZf5RuBGcfD+Sp2pAKq0ytz8j+1NehvgIGFkvtvFrDM3T8E4x/JJODXK9WZow8RRGrbA9QQ3hs+pDhA==", "dev": true, + "hasInstallScript": true, "optional": true, "os": [ - "darwin" + "!win32" ], - "engines": { - "node": ">= 10" + "dependencies": { + "node-addon-api": "^3.0.0", + "node-gyp-build": "^4.2.2" } }, - "node_modules/nx/node_modules/@nx/nx-darwin-x64": { - "version": "17.2.8", - "resolved": "https://registry.npmjs.org/@nx/nx-darwin-x64/-/nx-darwin-x64-17.2.8.tgz", - "integrity": "sha512-0cXzp1tGr7/6lJel102QiLA4NkaLCkQJj6VzwbwuvmuCDxPbpmbz7HC1tUteijKBtOcdXit1/MEoEU007To8Bw==", - "cpu": [ - "x64" - ], + "node_modules/nice-napi/node_modules/node-addon-api": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.2.1.tgz", + "integrity": "sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A==", "dev": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } + "optional": true }, - "node_modules/nx/node_modules/@nx/nx-linux-x64-gnu": { - "version": "17.2.8", - "resolved": "https://registry.npmjs.org/@nx/nx-linux-x64-gnu/-/nx-linux-x64-gnu-17.2.8.tgz", - "integrity": "sha512-sjG1bwGsjLxToasZ3lShildFsF0eyeGu+pOQZIp9+gjFbeIkd19cTlCnHrOV9hoF364GuKSXQyUlwtFYFR4VTQ==", - "cpu": [ - "x64" - ], + "node_modules/no-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", + "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", "dev": true, "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" + "peer": true, + "dependencies": { + "lower-case": "^2.0.2", + "tslib": "^2.0.3" } }, - "node_modules/nx/node_modules/@nx/nx-win32-x64-msvc": { - "version": "17.2.8", - "resolved": "https://registry.npmjs.org/@nx/nx-win32-x64-msvc/-/nx-win32-x64-msvc-17.2.8.tgz", - "integrity": "sha512-HTqDv+JThlLzbcEm/3f+LbS5/wYQWzb5YDXbP1wi7nlCTihNZOLNqGOkEmwlrR5tAdNHPRpHSmkYg4305W0CtA==", - "cpu": [ - "x64" - ], + "node_modules/node-addon-api": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-6.1.0.tgz", + "integrity": "sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA==", + "dev": true + }, + "node_modules/node-downloader-helper": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/node-downloader-helper/-/node-downloader-helper-2.1.9.tgz", + "integrity": "sha512-FSvAol2Z8UP191sZtsUZwHIN0eGoGue3uEXGdWIH5228e9KH1YHXT7fN8Oa33UGf+FbqGTQg3sJfrRGzmVCaJA==", "dev": true, - "optional": true, - "os": [ - "win32" - ], + "bin": { + "ndh": "bin/ndh" + }, "engines": { - "node": ">= 10" + "node": ">=14.18" } }, - "node_modules/nx/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", "dev": true, "dependencies": { - "color-convert": "^2.0.1" + "whatwg-url": "^5.0.0" }, "engines": { - "node": ">=8" + "node": "4.x || >=6.0.0" }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } } }, - "node_modules/nx/node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "node_modules/node-fetch/node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", "dev": true }, - "node_modules/nx/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } + "node_modules/node-fetch/node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "dev": true }, - "node_modules/nx/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/node-fetch/node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", "dev": true, "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" } }, - "node_modules/nx/node_modules/dotenv": { - "version": "16.3.2", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.3.2.tgz", - "integrity": "sha512-HTlk5nmhkm8F6JcdXvHIzaorzCoziNQT9mGxLPVXW8wJF1TiGSL60ZGB4gHWabHOaMmWmhvk2/lPHfnBiT78AQ==", + "node_modules/node-forge": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", + "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==", "dev": true, + "peer": true, "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/motdotla/dotenv?sponsor=1" + "node": ">= 6.13.0" } }, - "node_modules/nx/node_modules/glob": { - "version": "7.1.4", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz", - "integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==", + "node_modules/node-gyp": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-10.2.0.tgz", + "integrity": "sha512-sp3FonBAaFe4aYTcFdZUn2NYkbP7xroPGYvQmP4Nl5PxamznItBnNCgjrVTKrEfQynInMsJvZrdmqUnysCJ8rw==", "dev": true, "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "env-paths": "^2.2.0", + "exponential-backoff": "^3.1.1", + "glob": "^10.3.10", + "graceful-fs": "^4.2.6", + "make-fetch-happen": "^13.0.0", + "nopt": "^7.0.0", + "proc-log": "^4.1.0", + "semver": "^7.3.5", + "tar": "^6.2.1", + "which": "^4.0.0" + }, + "bin": { + "node-gyp": "bin/node-gyp.js" }, "engines": { - "node": "*" + "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/nx/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "node_modules/node-gyp-build": { + "version": "4.8.1", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.1.tgz", + "integrity": "sha512-OSs33Z9yWr148JZcbZd5WiAXhh/n9z8TxQcdMhIOlpN9AhWpLfvVFO73+m77bBABQMaY9XSvIa+qk0jlI7Gcaw==", "dev": true, - "engines": { - "node": ">=8" + "optional": true, + "bin": { + "node-gyp-build": "bin.js", + "node-gyp-build-optional": "optional.js", + "node-gyp-build-test": "build-test.js" } }, - "node_modules/nx/node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "node_modules/node-gyp-build-optional-packages": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-5.2.2.tgz", + "integrity": "sha512-s+w+rBWnpTMwSFbaE0UXsRlg7hU4FjekKU4eyAih5T8nJuNZT1nNsskXpxmeqSK9UzkBl6UgRlnKc8hz8IEqOw==", "dev": true, "dependencies": { - "argparse": "^2.0.1" + "detect-libc": "^2.0.1" }, "bin": { - "js-yaml": "bin/js-yaml.js" + "node-gyp-build-optional-packages": "bin.js", + "node-gyp-build-optional-packages-optional": "optional.js", + "node-gyp-build-optional-packages-test": "build-test.js" } }, - "node_modules/nx/node_modules/jsonc-parser": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.0.tgz", - "integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==", - "dev": true - }, - "node_modules/nx/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "node_modules/node-gyp/node_modules/isexe": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz", + "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==", "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, "engines": { - "node": ">=10" + "node": ">=16" } }, - "node_modules/nx/node_modules/minimatch": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.5.tgz", - "integrity": "sha512-tUpxzX0VAzJHjLu0xUfFv1gwVp9ba3IOuRAVH2EGuRW8a5emA2FlACLqiT/lDVtS1W+TGNwqz3sWaNyLgDJWuw==", + "node_modules/node-gyp/node_modules/which": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz", + "integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==", "dev": true, "dependencies": { - "brace-expansion": "^1.1.7" + "isexe": "^3.1.1" + }, + "bin": { + "node-which": "bin/which.js" }, "engines": { - "node": "*" + "node": "^16.13.0 || >=18.0.0" } }, - "node_modules/nx/node_modules/semver": { - "version": "7.5.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.3.tgz", - "integrity": "sha512-QBlUtyVk/5EeHbi7X0fw6liDZc7BBmEaSYn01fMU1OUYbf6GPsbTtd8WmnqbI20SeycoHSeiybkE/q1Q+qlThQ==", + "node_modules/node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", + "dev": true + }, + "node_modules/node-releases": { + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz", + "integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==", + "dev": true + }, + "node_modules/nopt": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-7.2.1.tgz", + "integrity": "sha512-taM24ViiimT/XntxbPyJQzCG+p4EKOpgD3mxFwW38mGjVUrfERQOeY4EDHjdnptttfHuHQXFx+lTP08Q+mLa/w==", "dev": true, "dependencies": { - "lru-cache": "^6.0.0" + "abbrev": "^2.0.0" }, "bin": { - "semver": "bin/semver.js" + "nopt": "bin/nopt.js" }, "engines": { - "node": ">=10" - } - }, - "node_modules/nx/node_modules/strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", - "dev": true, - "engines": { - "node": ">=4" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/nx/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "node_modules/normalize-package-data": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-6.0.2.tgz", + "integrity": "sha512-V6gygoYb/5EmNI+MEGrWkC+e6+Rr7mTmfHrxDbLzxQogBkgzo76rkok0Am6thgSF7Mv2nLOajAJj5vDJZEFn7g==", "dev": true, "dependencies": { - "has-flag": "^4.0.0" + "hosted-git-info": "^7.0.0", + "semver": "^7.3.5", + "validate-npm-package-license": "^3.0.4" }, "engines": { - "node": ">=8" + "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/nx/node_modules/tsconfig-paths": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz", - "integrity": "sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==", + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", "dev": true, - "dependencies": { - "json5": "^2.2.2", - "minimist": "^1.2.6", - "strip-bom": "^3.0.0" - }, "engines": { - "node": ">=6" + "node": ">=0.10.0" } }, - "node_modules/nx/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, - "node_modules/nx/node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "node_modules/normalize-range": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", + "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", "dev": true, + "peer": true, "engines": { - "node": ">=12" + "node": ">=0.10.0" } }, - "node_modules/nypm": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/nypm/-/nypm-0.3.6.tgz", - "integrity": "sha512-2CATJh3pd6CyNfU5VZM7qSwFu0ieyabkEdnogE30Obn1czrmOYiZ8DOZLe1yBdLKWoyD3Mcy2maUs+0MR3yVjQ==", + "node_modules/npm-bundled": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-3.0.1.tgz", + "integrity": "sha512-+AvaheE/ww1JEwRHOrn4WHNzOxGtVp+adrg2AeZS/7KuxGUYFuBta98wYpfHBbJp6Tg6j1NKSEVHNcfZzJHQwQ==", "dev": true, "dependencies": { - "citty": "^0.1.5", - "execa": "^8.0.1", - "pathe": "^1.1.2", - "ufo": "^1.3.2" - }, - "bin": { - "nypm": "dist/cli.mjs" + "npm-normalize-package-bin": "^3.0.0" }, "engines": { - "node": "^14.16.0 || >=16.10.0" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/nypm/node_modules/execa": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz", - "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==", + "node_modules/npm-install-checks": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/npm-install-checks/-/npm-install-checks-6.3.0.tgz", + "integrity": "sha512-W29RiK/xtpCGqn6f3ixfRYGk+zRyr+Ew9F2E20BfXxT5/euLdA/Nm7fO7OeTGuAmTs30cpgInyJ0cYe708YTZw==", "dev": true, "dependencies": { - "cross-spawn": "^7.0.3", - "get-stream": "^8.0.1", - "human-signals": "^5.0.0", - "is-stream": "^3.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^5.1.0", - "onetime": "^6.0.0", - "signal-exit": "^4.1.0", - "strip-final-newline": "^3.0.0" + "semver": "^7.1.1" }, "engines": { - "node": ">=16.17" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/nypm/node_modules/get-stream": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", - "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==", + "node_modules/npm-normalize-package-bin": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-3.0.1.tgz", + "integrity": "sha512-dMxCf+zZ+3zeQZXKxmyuCKlIDPGuv8EF940xbkC4kQVDTtqoh6rJFO+JTKSA6/Rwi0getWmtuy4Itup0AMcaDQ==", "dev": true, "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/nypm/node_modules/human-signals": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz", - "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==", + "node_modules/npm-package-arg": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-11.0.3.tgz", + "integrity": "sha512-sHGJy8sOC1YraBywpzQlIKBE4pBbGbiF95U6Auspzyem956E0+FtDtsx1ZxlOJkQCZ1AFXAY/yuvtFYrOxF+Bw==", "dev": true, + "dependencies": { + "hosted-git-info": "^7.0.0", + "proc-log": "^4.0.0", + "semver": "^7.3.5", + "validate-npm-package-name": "^5.0.0" + }, "engines": { - "node": ">=16.17.0" + "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/nypm/node_modules/is-stream": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", - "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", + "node_modules/npm-packlist": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-8.0.2.tgz", + "integrity": "sha512-shYrPFIS/JLP4oQmAwDyk5HcyysKW8/JLTEA32S0Z5TzvpaeeX2yMFfoK1fjEBnCBvVyIB/Jj/GBFdm0wsgzbA==", "dev": true, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + "dependencies": { + "ignore-walk": "^6.0.4" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/nypm/node_modules/mimic-fn": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", - "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", - "dev": true, "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/nypm/node_modules/npm-run-path": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz", - "integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==", + "node_modules/npm-pick-manifest": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-9.1.0.tgz", + "integrity": "sha512-nkc+3pIIhqHVQr085X9d2JzPzLyjzQS96zbruppqC9aZRm/x8xx6xhI98gHtsfELP2bE+loHq8ZaHFHhe+NauA==", "dev": true, "dependencies": { - "path-key": "^4.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + "npm-install-checks": "^6.0.0", + "npm-normalize-package-bin": "^3.0.0", + "npm-package-arg": "^11.0.0", + "semver": "^7.3.5" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "engines": { + "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/nypm/node_modules/onetime": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", - "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", + "node_modules/npm-registry-fetch": { + "version": "17.1.0", + "resolved": "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-17.1.0.tgz", + "integrity": "sha512-5+bKQRH0J1xG1uZ1zMNvxW0VEyoNWgJpY9UDuluPFLKDfJ9u2JmmjmTJV1srBGQOROfdBMiVvnH2Zvpbm+xkVA==", "dev": true, "dependencies": { - "mimic-fn": "^4.0.0" + "@npmcli/redact": "^2.0.0", + "jsonparse": "^1.3.1", + "make-fetch-happen": "^13.0.0", + "minipass": "^7.0.2", + "minipass-fetch": "^3.0.0", + "minizlib": "^2.1.2", + "npm-package-arg": "^11.0.0", + "proc-log": "^4.0.0" }, "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/nypm/node_modules/path-key": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", - "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", "dev": true, - "engines": { - "node": ">=12" + "dependencies": { + "path-key": "^3.0.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/nypm/node_modules/signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "dev": true, "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": ">=8" } }, - "node_modules/nypm/node_modules/strip-final-newline": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", - "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", + "node_modules/nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", "dev": true, - "engines": { - "node": ">=12" + "dependencies": { + "boolbase": "^1.0.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/fb55/nth-check?sponsor=1" } }, + "node_modules/nwsapi": { + "version": "2.2.7", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.7.tgz", + "integrity": "sha512-ub5E4+FBPKwAZx0UwIQOjYWGHTEq5sPqHQNRN8Z9e4A7u3Tj1weLJsL59yH9vmvqEtBHaOmT6cYQKIZOxp35FQ==", + "dev": true + }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -25584,6 +17141,7 @@ "version": "1.13.1", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==", + "dev": true, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -25608,111 +17166,17 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/object.assign": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.5.tgz", - "integrity": "sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==", - "dependencies": { - "call-bind": "^1.0.5", - "define-properties": "^1.2.1", - "has-symbols": "^1.0.3", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object.entries": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.7.tgz", - "integrity": "sha512-jCBs/0plmPsOnrKAfFQXRG2NFjlhZgjjcBLSmTnEhU8U6vVTsVe8ANeQJCHTl3gSsI4J+0emOoCgoKlmQPMgmA==", - "dev": true, - "peer": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/object.fromentries": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.7.tgz", - "integrity": "sha512-UPbPHML6sL8PI/mOqPwsH4G6iyXcCGzLin8KvEPenOZN5lpCNBZZQ+V62vdjB1mQHrmqGQt5/OJzemUA+KJmEA==", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object.groupby": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.2.tgz", - "integrity": "sha512-bzBq58S+x+uo0VjurFT0UktpKHOZmv4/xePiOA1nbB9pMqpGK7rUPNgf+1YC+7mE+0HzhTMqNUuCqvKhj6FnBw==", - "dependencies": { - "array.prototype.filter": "^1.0.3", - "call-bind": "^1.0.5", - "define-properties": "^1.2.1", - "es-abstract": "^1.22.3", - "es-errors": "^1.0.0" - } - }, - "node_modules/object.hasown": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/object.hasown/-/object.hasown-1.1.3.tgz", - "integrity": "sha512-fFI4VcYpRHvSLXxP7yiZOMAd331cPfd2p7PFDVbgUsYOfCT3tICVqXWngbjr4m49OvsBwUBQ6O2uQoJvy3RexA==", "dev": true, - "peer": true, - "dependencies": { - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object.values": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.7.tgz", - "integrity": "sha512-aU6xnDFYT3x17e/f0IiiwlGPTy2jzMySGfUB4fq6z7CV8l85CWHDk5ErhyhpfDHhrOMwGFhSQkhMGHaIotA6Ng==", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" - }, "engines": { "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" } }, "node_modules/obuf": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==", - "dev": true - }, - "node_modules/ohash": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/ohash/-/ohash-1.1.3.tgz", - "integrity": "sha512-zuHHiGTYTA1sYJ/wZN+t5HKZaH23i4yI1HMwbuXm24Nid7Dv0KcuRlKoNKS9UNfAVSBlnGLcuQrnOKWOZoEGaw==", - "dev": true + "dev": true, + "peer": true }, "node_modules/on-finished": { "version": "2.4.1", @@ -25739,6 +17203,7 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, "dependencies": { "wrappy": "1" } @@ -25759,15 +17224,30 @@ } }, "node_modules/open": { - "version": "8.4.2", - "resolved": "https://registry.npmjs.org/open/-/open-8.4.2.tgz", - "integrity": "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==", + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/open/-/open-10.1.0.tgz", + "integrity": "sha512-mnkeQ1qP5Ue2wd+aivTD3NHd/lZ96Lu0jgf0pwktLPtx6cTZiH7tyeGRRHs0zX0rbrahXPnXlUnbeXyaBBuIaw==", "dev": true, + "peer": true, "dependencies": { - "define-lazy-prop": "^2.0.0", - "is-docker": "^2.1.1", - "is-wsl": "^2.2.0" + "default-browser": "^5.2.1", + "define-lazy-prop": "^3.0.0", + "is-inside-container": "^1.0.0", + "is-wsl": "^3.1.0" + }, + "engines": { + "node": ">=18" }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/open/node_modules/define-lazy-prop": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", + "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==", + "dev": true, + "peer": true, "engines": { "node": ">=12" }, @@ -25775,6 +17255,22 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/open/node_modules/is-wsl": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.0.tgz", + "integrity": "sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw==", + "dev": true, + "peer": true, + "dependencies": { + "is-inside-container": "^1.0.0" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/opencollective-postinstall": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/opencollective-postinstall/-/opencollective-postinstall-2.0.3.tgz", @@ -25789,22 +17285,6 @@ "resolved": "https://registry.npmjs.org/option-parser/-/option-parser-1.0.2.tgz", "integrity": "sha512-Q56FmRi6TZX+S9jAl9f0Tnrk7W8faAZULf6Y0IgJR6Cy7/MxhzvBFoyL1Sz2DD7WbOY2Fxtkz2S/3kzlbn/VsQ==" }, - "node_modules/optionator": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", - "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", - "dependencies": { - "@aashutoshrathi/word-wrap": "^1.2.3", - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, "node_modules/ora": { "version": "5.4.1", "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", @@ -25880,6 +17360,12 @@ "node": ">=8" } }, + "node_modules/ordered-binary": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/ordered-binary/-/ordered-binary-1.5.1.tgz", + "integrity": "sha512-5VyHfHY3cd0iza71JepYG50My+YUbrFtGoUz2ooEydPyPM7Aai/JW098juLr+RG6+rDJuzNNTsEQu2DZa1A41A==", + "dev": true + }, "node_modules/os-name": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/os-name/-/os-name-4.0.1.tgz", @@ -25905,16 +17391,11 @@ "node": ">=0.10.0" } }, - "node_modules/ospath": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/ospath/-/ospath-1.2.2.tgz", - "integrity": "sha512-o6E5qJV5zkAbIDNhGSIlyOhScKXgQrSRMilfph0clDfM0nEnBOlKlH4sWDmG95BW/CvwNz0vmm7dJVtU2KlMiA==", - "dev": true - }, "node_modules/p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, "dependencies": { "yocto-queue": "^0.1.0" }, @@ -25929,6 +17410,7 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, "dependencies": { "p-limit": "^3.0.2" }, @@ -25955,16 +17437,21 @@ } }, "node_modules/p-retry": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz", - "integrity": "sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-6.2.0.tgz", + "integrity": "sha512-JA6nkq6hKyWLLasXQXUrO4z8BUZGUt/LjlJxx8Gb2+2ntodU/SS63YZ8b0LUTbQ8ZB9iwOfhEPhg4ykKnn2KsA==", "dev": true, + "peer": true, "dependencies": { - "@types/retry": "0.12.0", + "@types/retry": "0.12.2", + "is-network-error": "^1.0.0", "retry": "^0.13.1" }, "engines": { - "node": ">=8" + "node": ">=16.17" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/p-retry/node_modules/retry": { @@ -25972,6 +17459,7 @@ "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", "dev": true, + "peer": true, "engines": { "node": ">= 4" } @@ -25986,32 +17474,31 @@ } }, "node_modules/pacote": { - "version": "17.0.6", - "resolved": "https://registry.npmjs.org/pacote/-/pacote-17.0.6.tgz", - "integrity": "sha512-cJKrW21VRE8vVTRskJo78c/RCvwJCn1f4qgfxL4w77SOWrTCRcmfkYHlHtS0gqpgjv3zhXflRtgsrUCX5xwNnQ==", + "version": "18.0.6", + "resolved": "https://registry.npmjs.org/pacote/-/pacote-18.0.6.tgz", + "integrity": "sha512-+eK3G27SMwsB8kLIuj4h1FUhHtwiEUo21Tw8wNjmvdlpOEr613edv+8FUsTj/4F/VN5ywGE19X18N7CC2EJk6A==", "dev": true, "dependencies": { "@npmcli/git": "^5.0.0", "@npmcli/installed-package-contents": "^2.0.1", + "@npmcli/package-json": "^5.1.0", "@npmcli/promise-spawn": "^7.0.0", - "@npmcli/run-script": "^7.0.0", + "@npmcli/run-script": "^8.0.0", "cacache": "^18.0.0", "fs-minipass": "^3.0.0", "minipass": "^7.0.2", "npm-package-arg": "^11.0.0", "npm-packlist": "^8.0.0", "npm-pick-manifest": "^9.0.0", - "npm-registry-fetch": "^16.0.0", - "proc-log": "^3.0.0", + "npm-registry-fetch": "^17.0.0", + "proc-log": "^4.0.0", "promise-retry": "^2.0.1", - "read-package-json": "^7.0.0", - "read-package-json-fast": "^3.0.0", "sigstore": "^2.2.0", "ssri": "^10.0.0", "tar": "^6.1.11" }, "bin": { - "pacote": "lib/bin.js" + "pacote": "bin/index.js" }, "engines": { "node": "^16.14.0 || >=18.0.0" @@ -26028,6 +17515,8 @@ "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz", "integrity": "sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==", "dev": true, + "optional": true, + "peer": true, "dependencies": { "dot-case": "^3.0.4", "tslib": "^2.0.3" @@ -26037,6 +17526,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, "dependencies": { "callsites": "^3.0.0" }, @@ -26079,6 +17569,7 @@ "resolved": "https://registry.npmjs.org/parse-node-version/-/parse-node-version-1.0.1.tgz", "integrity": "sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==", "dev": true, + "peer": true, "engines": { "node": ">= 0.10" } @@ -26148,6 +17639,8 @@ "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz", "integrity": "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==", "dev": true, + "optional": true, + "peer": true, "dependencies": { "no-case": "^3.0.4", "tslib": "^2.0.3" @@ -26163,6 +17656,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, "engines": { "node": ">=8" } @@ -26171,6 +17665,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, "engines": { "node": ">=0.10.0" } @@ -26179,6 +17674,7 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, "engines": { "node": ">=8" } @@ -26186,7 +17682,8 @@ "node_modules/path-parse": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true }, "node_modules/path-scurry": { "version": "1.10.1", @@ -26228,12 +17725,6 @@ "node": ">=8" } }, - "node_modules/pathe": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz", - "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==", - "dev": true - }, "node_modules/pause-stream": { "version": "0.0.11", "resolved": "https://registry.npmjs.org/pause-stream/-/pause-stream-0.0.11.tgz", @@ -26284,79 +17775,16 @@ "node": ">=0.10.0" } }, - "node_modules/peek-stream": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/peek-stream/-/peek-stream-1.1.3.tgz", - "integrity": "sha512-FhJ+YbOSBb9/rIl2ZeE/QHEsWn7PqNYt8ARAY3kIgNGOk13g9FGyIY6JIl/xB/3TFRVoTv5as0l11weORrTekA==", - "dev": true, - "dependencies": { - "buffer-from": "^1.0.0", - "duplexify": "^3.5.0", - "through2": "^2.0.3" - } - }, - "node_modules/peek-stream/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "dev": true - }, - "node_modules/peek-stream/node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "dev": true, - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/peek-stream/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, - "node_modules/peek-stream/node_modules/through2": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", - "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", - "dev": true, - "dependencies": { - "readable-stream": "~2.3.6", - "xtend": "~4.0.1" - } - }, - "node_modules/pend": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", - "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", - "dev": true - }, - "node_modules/performance-now": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==", - "dev": true - }, "node_modules/picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", + "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==", "dev": true }, "node_modules/picomatch": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.1.tgz", - "integrity": "sha512-xUXwsxNjwTQ8K3GnT4pCJm+xq3RUPQbmkYJTP5aFIfNIvbcc/4MUxgBaaRSZJ6yGJZiGSyYlM6MzwTsRk8SYCg==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", "dev": true, "engines": { "node": ">=12" @@ -26365,15 +17793,6 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/pirates": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", @@ -26384,24 +17803,12 @@ } }, "node_modules/piscina": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/piscina/-/piscina-4.3.1.tgz", - "integrity": "sha512-MBj0QYm3hJQ/C/wIXTN1OCYC8uQ4BBJ4LVele2P4ZwVQAH04vkk8E1SpDbuemLAL1dZorbuOob9rYqJeWCcCRg==", - "dev": true, - "optionalDependencies": { - "nice-napi": "^1.0.2" - } - }, - "node_modules/pkg-dir": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-5.0.0.tgz", - "integrity": "sha512-NPE8TDbzl/3YQYY7CSS228s3g2ollTFnc+Qi3tqmqJp9Vg2ovUpixcJEo2HJScN2Ez+kEaal6y70c0ehqJBJeA==", - "dev": true, - "dependencies": { - "find-up": "^5.0.0" - }, - "engines": { - "node": ">=10" + "version": "4.6.1", + "resolved": "https://registry.npmjs.org/piscina/-/piscina-4.6.1.tgz", + "integrity": "sha512-z30AwWGtQE+Apr+2WBZensP2lIvwoaMcOPkQlIEmSGMJNUvaYACylPYrQM6wSdUNJlnDVMSpLv7xTMJqlVshOA==", + "dev": true, + "optionalDependencies": { + "nice-napi": "^1.0.2" } }, "node_modules/please-upgrade-node": { @@ -26428,18 +17835,6 @@ "integrity": "sha512-k+YsbhpA9e+EFfKjTCH3VW6aoKlyNYI6NYdTfDL4CIvFnvsuO84ttonmZE7rc+v23SLTH8XX+5w/Ak9v0xGY4g==", "dev": true }, - "node_modules/polished": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/polished/-/polished-4.3.1.tgz", - "integrity": "sha512-OBatVyC/N7SCW/FaDHrSd+vn0o5cS855TOmYi4OkdWUMSJCET/xip//ch8xGUvtr3i44X9LVyWwQlRMTN3pwSA==", - "dev": true, - "dependencies": { - "@babel/runtime": "^7.17.8" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/possible-typed-array-names": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", @@ -26449,9 +17844,9 @@ } }, "node_modules/postcss": { - "version": "8.4.35", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.35.tgz", - "integrity": "sha512-u5U8qYpBCpN13BsiEB0CbR1Hhh4Gc0zLFuedrHJKMctHCHAGrMdG0PRM/KErzAL3CU6/eckEtmHNB3x6e3c0vA==", + "version": "8.4.41", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.41.tgz", + "integrity": "sha512-TesUflQ0WKZqAvg52PWL6kHgLKP6xB6heTOdoYM0Wt2UHyxNa4K25EZZMgKns3BH1RLVbZCREPpLY0rhnNoHVQ==", "dev": true, "funding": [ { @@ -26469,18 +17864,19 @@ ], "dependencies": { "nanoid": "^3.3.7", - "picocolors": "^1.0.0", - "source-map-js": "^1.0.2" + "picocolors": "^1.0.1", + "source-map-js": "^1.2.0" }, "engines": { "node": "^10 || ^12 || >=14" } }, "node_modules/postcss-loader": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-8.1.0.tgz", - "integrity": "sha512-AbperNcX3rlob7Ay7A/HQcrofug1caABBkopoFeOQMspZBqcqj6giYn1Bwey/0uiOPAcR+NQD0I2HC7rXzk91w==", + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-8.1.1.tgz", + "integrity": "sha512-0IeqyAsG6tYiDRCYKQJLAmgQr47DX6N7sFSWvQxt6AcupX8DIdmykuk/o/tx0Lze3ErGHJEp5OSRxrelC6+NdQ==", "dev": true, + "peer": true, "dependencies": { "cosmiconfig": "^9.0.0", "jiti": "^1.20.0", @@ -26511,13 +17907,15 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true + "dev": true, + "peer": true }, "node_modules/postcss-loader/node_modules/cosmiconfig": { "version": "9.0.0", "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.0.tgz", "integrity": "sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==", "dev": true, + "peer": true, "dependencies": { "env-paths": "^2.2.1", "import-fresh": "^3.3.0", @@ -26544,6 +17942,7 @@ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "dev": true, + "peer": true, "dependencies": { "argparse": "^2.0.1" }, @@ -26551,11 +17950,18 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/postcss-media-query-parser": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/postcss-media-query-parser/-/postcss-media-query-parser-0.2.3.tgz", + "integrity": "sha512-3sOlxmbKcSHMjlUXQZKQ06jOswE7oVkXPxmZdoB1r5l0q6gTFTQSHxNxOrCccElbW7dxNytifNEo8qidX2Vsig==", + "dev": true + }, "node_modules/postcss-modules-extract-imports": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz", - "integrity": "sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.1.0.tgz", + "integrity": "sha512-k3kNe0aNFQDAZGbin48pL2VNidTF0w4/eASDsxlyspobzU3wZQLOGj7L9gfRe0Jo9/4uud09DsjFNH7winGv8Q==", "dev": true, + "peer": true, "engines": { "node": "^10 || ^12 || >= 14" }, @@ -26564,10 +17970,11 @@ } }, "node_modules/postcss-modules-local-by-default": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.4.tgz", - "integrity": "sha512-L4QzMnOdVwRm1Qb8m4x8jsZzKAaPAgrUF1r/hjDR2Xj7R+8Zsf97jAlSQzWtKx5YNiNGN8QxmPFIc/sh+RQl+Q==", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.5.tgz", + "integrity": "sha512-6MieY7sIfTK0hYfafw1OMEG+2bg8Q1ocHCpoWLqOKj3JXlKu4G7btkmM/B7lFubYkYWmRSPLZi5chid63ZaZYw==", "dev": true, + "peer": true, "dependencies": { "icss-utils": "^5.0.0", "postcss-selector-parser": "^6.0.2", @@ -26581,10 +17988,11 @@ } }, "node_modules/postcss-modules-scope": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.1.1.tgz", - "integrity": "sha512-uZgqzdTleelWjzJY+Fhti6F3C9iF1JR/dODLs/JDefozYcKTBCdD8BIl6nNPbTbcLnGrk56hzwZC2DaGNvYjzA==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.2.0.tgz", + "integrity": "sha512-oq+g1ssrsZOsx9M96c5w8laRmvEu9C3adDSjI8oTcbfkrTE8hx/zfyobUoWIxaKPO8bt6S62kxpw5GqypEw1QQ==", "dev": true, + "peer": true, "dependencies": { "postcss-selector-parser": "^6.0.4" }, @@ -26600,6 +18008,7 @@ "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz", "integrity": "sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==", "dev": true, + "peer": true, "dependencies": { "icss-utils": "^5.0.0" }, @@ -26611,10 +18020,11 @@ } }, "node_modules/postcss-selector-parser": { - "version": "6.0.15", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.15.tgz", - "integrity": "sha512-rEYkQOMUCEMhsKbK66tbEU9QVIxbhN18YiniAwA7XQYTVBqrBy+P2p5JcdqsHgKM2zWylp8d7J6eszocfds5Sw==", + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", + "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", "dev": true, + "peer": true, "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" @@ -26627,7 +18037,8 @@ "version": "4.2.0", "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", - "dev": true + "dev": true, + "peer": true }, "node_modules/preact": { "version": "8.5.3", @@ -26635,98 +18046,18 @@ "integrity": "sha512-O3kKP+1YdgqHOFsZF2a9JVdtqD+RPzCQc3rP+Ualf7V6rmRDchZ9MJbiGTT7LuyqFKZqlHSOyO/oMFmI2lVTsw==", "hasInstallScript": true }, - "node_modules/prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/prettier": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.2.5.tgz", - "integrity": "sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A==", - "dev": true, - "bin": { - "prettier": "bin/prettier.cjs" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/prettier/prettier?sponsor=1" - } - }, - "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, - "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", - "integrity": "sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==", - "dev": true, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/pretty-error": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-4.0.0.tgz", "integrity": "sha512-AoJ5YMAcXKYxKhuJGdcvse+Voc6v1RgnsR3nWcYU7q4t6z0Q6T86sv5Zq8VIRbOWWFpvdGE83LtdSMNd+6Y0xw==", "dev": true, + "optional": true, + "peer": true, "dependencies": { "lodash": "^4.17.20", "renderkid": "^3.0.0" } }, - "node_modules/pretty-format": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", - "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1", - "ansi-styles": "^5.0.0", - "react-is": "^17.0.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/pretty-format/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/pretty-hrtime": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz", - "integrity": "sha512-66hKPCr+72mlfiSjlEB1+45IjXSqvVAIy6mocupoww4tBFE9R9IhwwUGoI4G++Tc9Aq+2rxOt0RFU6gPcrte0A==", - "dev": true, - "engines": { - "node": ">= 0.8" - } - }, "node_modules/pretty-js": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/pretty-js/-/pretty-js-0.2.2.tgz", @@ -26751,23 +18082,14 @@ } }, "node_modules/proc-log": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-3.0.0.tgz", - "integrity": "sha512-++Vn7NS4Xf9NacaU9Xq3URUuqZETPsf8L4j5/ckhaRYsfPeRyzGw+iDjFhV/Jr3uNmTvvddEJFWh5R1gRgUH8A==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-4.2.0.tgz", + "integrity": "sha512-g8+OnU/L2v+wyiVK+D5fA34J7EH8jZ8DDlvwhRCMxmMj7UCBvxiO1mGeN+36JXIKF4zevU4kRBd8lVgG9vLelA==", "dev": true, "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/process": { - "version": "0.11.10", - "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", - "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", - "dev": true, - "engines": { - "node": ">= 0.6.0" - } - }, "node_modules/process-files": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/process-files/-/process-files-1.0.0.tgz", @@ -26783,6 +18105,7 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true, "engines": { "node": ">=0.4.0" } @@ -26790,300 +18113,103 @@ "node_modules/promise-inflight": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", - "integrity": "sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==", - "dev": true - }, - "node_modules/promise-retry": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz", - "integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==", - "dev": true, - "dependencies": { - "err-code": "^2.0.2", - "retry": "^0.12.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/prompts": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", - "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", - "dev": true, - "dependencies": { - "kleur": "^3.0.3", - "sisteransi": "^1.0.5" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/prop-types": { - "version": "15.8.1", - "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", - "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", - "dev": true, - "peer": true, - "dependencies": { - "loose-envify": "^1.4.0", - "object-assign": "^4.1.1", - "react-is": "^16.13.1" - } - }, - "node_modules/prop-types/node_modules/react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", - "dev": true, - "peer": true - }, - "node_modules/propagating-hammerjs": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/propagating-hammerjs/-/propagating-hammerjs-1.5.0.tgz", - "integrity": "sha512-3PUXWmomwutoZfydC+lJwK1bKCh6sK6jZGB31RUX6+4EXzsbkDZrK4/sVR7gBrvJaEIwpTVyxQUAd29FKkmVdw==", - "dev": true, - "dependencies": { - "hammerjs": "^2.0.8" - } - }, - "node_modules/proxy-addr": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", - "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", - "dev": true, - "dependencies": { - "forwarded": "0.2.0", - "ipaddr.js": "1.9.1" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/proxy-from-env": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" - }, - "node_modules/proxy-middleware": { - "version": "0.15.0", - "resolved": "https://registry.npmjs.org/proxy-middleware/-/proxy-middleware-0.15.0.tgz", - "integrity": "sha512-EGCG8SeoIRVMhsqHQUdDigB2i7qU7fCsWASwn54+nPutYO8n4q6EiwMzyfWlC+dzRFExP+kvcnDFdBDHoZBU7Q==", - "dev": true, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/prr": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", - "integrity": "sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==", - "dev": true, - "optional": true - }, - "node_modules/psl": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", - "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==", - "dev": true - }, - "node_modules/pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "dev": true, - "dependencies": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "node_modules/pumpify": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.5.1.tgz", - "integrity": "sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==", - "dev": true, - "dependencies": { - "duplexify": "^3.6.0", - "inherits": "^2.0.3", - "pump": "^2.0.0" - } - }, - "node_modules/pumpify/node_modules/pump": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz", - "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==", - "dev": true, - "dependencies": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "node_modules/punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "engines": { - "node": ">=6" - } - }, - "node_modules/puppeteer-core": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-2.1.1.tgz", - "integrity": "sha512-n13AWriBMPYxnpbb6bnaY5YoY6rGj8vPLrz6CZF3o0qJNEwlcfJVxBzYZ0NJsQ21UbdJoijPCDrM++SUVEz7+w==", - "dev": true, - "dependencies": { - "@types/mime-types": "^2.1.0", - "debug": "^4.1.0", - "extract-zip": "^1.6.6", - "https-proxy-agent": "^4.0.0", - "mime": "^2.0.3", - "mime-types": "^2.1.25", - "progress": "^2.0.1", - "proxy-from-env": "^1.0.0", - "rimraf": "^2.6.1", - "ws": "^6.1.0" - }, - "engines": { - "node": ">=8.16.0" - } - }, - "node_modules/puppeteer-core/node_modules/agent-base": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-5.1.1.tgz", - "integrity": "sha512-TMeqbNl2fMW0nMjTEPOwe3J/PRFP4vqeoNuQMG0HlMrtm5QxKqdvAkZ1pRBQ/ulIyDD5Yq0nJ7YbdD8ey0TO3g==", - "dev": true, - "engines": { - "node": ">= 6.0.0" - } - }, - "node_modules/puppeteer-core/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/puppeteer-core/node_modules/extract-zip": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-1.7.0.tgz", - "integrity": "sha512-xoh5G1W/PB0/27lXgMQyIhP5DSY/LhoCsOyZgb+6iMmRtCwVBo55uKaMoEYrDCKQhWvqEip5ZPKAc6eFNyf/MA==", - "dev": true, - "dependencies": { - "concat-stream": "^1.6.2", - "debug": "^2.6.9", - "mkdirp": "^0.5.4", - "yauzl": "^2.10.0" - }, - "bin": { - "extract-zip": "cli.js" - } - }, - "node_modules/puppeteer-core/node_modules/extract-zip/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "dependencies": { - "ms": "2.0.0" - } + "integrity": "sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==", + "dev": true }, - "node_modules/puppeteer-core/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "node_modules/promise-retry": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz", + "integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==", "dev": true, "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "err-code": "^2.0.2", + "retry": "^0.12.0" }, "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": ">=10" } }, - "node_modules/puppeteer-core/node_modules/https-proxy-agent": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-4.0.0.tgz", - "integrity": "sha512-zoDhWrkR3of1l9QAL8/scJZyLu8j/gBkcwcaQOZh7Gyh/+uJQzGVETdgT30akuwkpL8HTRfssqI3BZuV18teDg==", + "node_modules/prompts": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", "dev": true, "dependencies": { - "agent-base": "5", - "debug": "4" + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" }, "engines": { - "node": ">= 6.0.0" + "node": ">= 6" } }, - "node_modules/puppeteer-core/node_modules/mime": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", - "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", + "node_modules/propagating-hammerjs": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/propagating-hammerjs/-/propagating-hammerjs-1.5.0.tgz", + "integrity": "sha512-3PUXWmomwutoZfydC+lJwK1bKCh6sK6jZGB31RUX6+4EXzsbkDZrK4/sVR7gBrvJaEIwpTVyxQUAd29FKkmVdw==", "dev": true, - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=4.0.0" + "dependencies": { + "hammerjs": "^2.0.8" } }, - "node_modules/puppeteer-core/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", "dev": true, "dependencies": { - "brace-expansion": "^1.1.7" + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" }, "engines": { - "node": "*" + "node": ">= 0.10" } }, - "node_modules/puppeteer-core/node_modules/mkdirp": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", - "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "dev": true + }, + "node_modules/proxy-middleware": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/proxy-middleware/-/proxy-middleware-0.15.0.tgz", + "integrity": "sha512-EGCG8SeoIRVMhsqHQUdDigB2i7qU7fCsWASwn54+nPutYO8n4q6EiwMzyfWlC+dzRFExP+kvcnDFdBDHoZBU7Q==", "dev": true, - "dependencies": { - "minimist": "^1.2.6" - }, - "bin": { - "mkdirp": "bin/cmd.js" + "engines": { + "node": ">=0.8.0" } }, - "node_modules/puppeteer-core/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "node_modules/prr": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", + "integrity": "sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==", + "dev": true, + "optional": true, + "peer": true + }, + "node_modules/psl": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", + "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==", "dev": true }, - "node_modules/puppeteer-core/node_modules/rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "node_modules/pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", "dev": true, "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" + "end-of-stream": "^1.1.0", + "once": "^1.3.1" } }, - "node_modules/puppeteer-core/node_modules/ws": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.2.tgz", - "integrity": "sha512-zmhltoSR8u1cnDsD43TX59mzoMZsLKqUweyYBAIvTngR3shc0W6aOZylZmq/7hqyVxPdi+5Ud2QInblgyE72fw==", + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", "dev": true, - "dependencies": { - "async-limiter": "~1.0.0" + "engines": { + "node": ">=6" } }, "node_modules/pure-rand": { @@ -27102,21 +18228,6 @@ } ] }, - "node_modules/qs": { - "version": "6.11.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.2.tgz", - "integrity": "sha512-tDNIz22aBzCDxLtVH++VnTfzxlfeK5CbqohpSqpJgj1Wg/cQbStNAz3NuqCs5vV+pjBsK4x4pN9HlVh7rcYRiA==", - "dev": true, - "dependencies": { - "side-channel": "^1.0.4" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/querystringify": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", @@ -27127,6 +18238,7 @@ "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, "funding": [ { "type": "github", @@ -27165,12 +18277,6 @@ "quote-stream": "bin/cmd.js" } }, - "node_modules/quote-stream/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "dev": true - }, "node_modules/quote-stream/node_modules/readable-stream": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", @@ -27205,21 +18311,12 @@ "xtend": "~4.0.1" } }, - "node_modules/ramda": { - "version": "0.29.0", - "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.29.0.tgz", - "integrity": "sha512-BBea6L67bYLtdbOqfp8f58fPMqEwx0doL+pAi8TZyp2YWz8R9G8z9x75CZI8W+ftqhFHCpEX2cRnUUXK130iKA==", - "dev": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/ramda" - } - }, "node_modules/randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", "dev": true, + "peer": true, "dependencies": { "safe-buffer": "^5.1.0" } @@ -27257,147 +18354,6 @@ "node": ">= 0.8" } }, - "node_modules/react": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", - "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", - "dev": true, - "peer": true, - "dependencies": { - "loose-envify": "^1.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/react-colorful": { - "version": "5.6.1", - "resolved": "https://registry.npmjs.org/react-colorful/-/react-colorful-5.6.1.tgz", - "integrity": "sha512-1exovf0uGTGyq5mXQT0zgQ80uvj2PCwvF8zY1RN9/vbJVSjSo3fsB/4L3ObbF7u70NduSiK4xu4Y6q1MHoUGEw==", - "dev": true, - "peerDependencies": { - "react": ">=16.8.0", - "react-dom": ">=16.8.0" - } - }, - "node_modules/react-dom": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", - "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==", - "dev": true, - "peer": true, - "dependencies": { - "loose-envify": "^1.1.0", - "scheduler": "^0.23.0" - }, - "peerDependencies": { - "react": "^18.2.0" - } - }, - "node_modules/react-is": { - "version": "17.0.2", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", - "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", - "dev": true - }, - "node_modules/react-remove-scroll": { - "version": "2.5.5", - "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.5.5.tgz", - "integrity": "sha512-ImKhrzJJsyXJfBZ4bzu8Bwpka14c/fQt0k+cyFp/PBhTfyDnU5hjOtM4AG/0AMyy8oKzOTR0lDgJIM7pYXI0kw==", - "dev": true, - "dependencies": { - "react-remove-scroll-bar": "^2.3.3", - "react-style-singleton": "^2.2.1", - "tslib": "^2.1.0", - "use-callback-ref": "^1.3.0", - "use-sidecar": "^1.1.2" - }, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/react-remove-scroll-bar": { - "version": "2.3.5", - "resolved": "https://registry.npmjs.org/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.5.tgz", - "integrity": "sha512-3cqjOqg6s0XbOjWvmasmqHch+RLxIEk2r/70rzGXuz3iIGQsQheEQyqYCBb5EECoD01Vo2SIbDqW4paLeLTASw==", - "dev": true, - "dependencies": { - "react-style-singleton": "^2.2.1", - "tslib": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/react-style-singleton": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.1.tgz", - "integrity": "sha512-ZWj0fHEMyWkHzKYUr2Bs/4zU6XLmq9HsgBURm7g5pAVfyn49DgUiNgY2d4lXRlYSiCif9YBGpQleewkcqddc7g==", - "dev": true, - "dependencies": { - "get-nonce": "^1.0.0", - "invariant": "^2.2.4", - "tslib": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/read-package-json": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/read-package-json/-/read-package-json-7.0.0.tgz", - "integrity": "sha512-uL4Z10OKV4p6vbdvIXB+OzhInYtIozl/VxUBPgNkBuUi2DeRonnuspmaVAMcrkmfjKGNmRndyQAbE7/AmzGwFg==", - "dev": true, - "dependencies": { - "glob": "^10.2.2", - "json-parse-even-better-errors": "^3.0.0", - "normalize-package-data": "^6.0.0", - "npm-normalize-package-bin": "^3.0.0" - }, - "engines": { - "node": "^16.14.0 || >=18.0.0" - } - }, - "node_modules/read-package-json-fast": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/read-package-json-fast/-/read-package-json-fast-3.0.2.tgz", - "integrity": "sha512-0J+Msgym3vrLOUB3hzQCuZHII0xkNGCtz/HJH9xZshwv9DbDwkw1KaE3gx/e2J5rpEY5rtOy6cyhKOPrkP7FZw==", - "dev": true, - "dependencies": { - "json-parse-even-better-errors": "^3.0.0", - "npm-normalize-package-bin": "^3.0.0" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, "node_modules/read-pkg": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", @@ -27565,43 +18521,6 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/recast": { - "version": "0.23.4", - "resolved": "https://registry.npmjs.org/recast/-/recast-0.23.4.tgz", - "integrity": "sha512-qtEDqIZGVcSZCHniWwZWbRy79Dc6Wp3kT/UmDA2RJKBPg7+7k51aQBZirHmUGn5uvHf2rg8DkjizrN26k61ATw==", - "dev": true, - "dependencies": { - "assert": "^2.0.0", - "ast-types": "^0.16.1", - "esprima": "~4.0.0", - "source-map": "~0.6.1", - "tslib": "^2.0.1" - }, - "engines": { - "node": ">= 4" - } - }, - "node_modules/recast/node_modules/ast-types": { - "version": "0.16.1", - "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.16.1.tgz", - "integrity": "sha512-6t10qk83GOG8p0vKmaCr8eiilZwO171AvbROMtvvNiwrTly62t+7XkA8RdIIVbpMhCASAsxgAzdRSwh6nw/5Dg==", - "dev": true, - "dependencies": { - "tslib": "^2.0.1" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/recast/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/redent": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", @@ -27612,37 +18531,15 @@ "strip-indent": "^3.0.0" }, "engines": { - "node": ">=8" - } - }, - "node_modules/reflect-metadata": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.1.tgz", - "integrity": "sha512-i5lLI6iw9AU3Uu4szRNPPEkomnkjRTaVt9hy/bn5g/oSzekBSMeLZblcjP74AW0vBabqERLLIrz+gR8QYR54Tw==", - "dev": true - }, - "node_modules/reflect.getprototypeof": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.5.tgz", - "integrity": "sha512-62wgfC8dJWrmxv44CA36pLDnP6KKl3Vhxb7PL+8+qrrFMMoJij4vgiMP8zV4O8+CBMXY1mHxI5fITGHXFHVmQQ==", - "dev": true, - "peer": true, - "dependencies": { - "call-bind": "^1.0.5", - "define-properties": "^1.2.1", - "es-abstract": "^1.22.3", - "es-errors": "^1.0.0", - "get-intrinsic": "^1.2.3", - "globalthis": "^1.0.3", - "which-builtin-type": "^1.1.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=8" } }, + "node_modules/reflect-metadata": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.1.tgz", + "integrity": "sha512-i5lLI6iw9AU3Uu4szRNPPEkomnkjRTaVt9hy/bn5g/oSzekBSMeLZblcjP74AW0vBabqERLLIrz+gR8QYR54Tw==", + "dev": true + }, "node_modules/regenerate": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", @@ -27680,22 +18577,14 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/regex-parser/-/regex-parser-2.3.0.tgz", "integrity": "sha512-TVILVSz2jY5D47F4mA4MppkBrafEaiUWJO/TcZHEIuI13AqoZMkK1WMA4Om1YkYbTx+9Ki1/tSUXbceyr9saRg==", - "dev": true - }, - "node_modules/regexp-tree": { - "version": "0.1.27", - "resolved": "https://registry.npmjs.org/regexp-tree/-/regexp-tree-0.1.27.tgz", - "integrity": "sha512-iETxpjK6YoRWJG5o6hXLwvjYAoW+FEZn9os0PD/b6AP6xQwsa/Y7lCVgIixBbUPMfhu+i2LtdeAqVTgGlQarfA==", "dev": true, - "peer": true, - "bin": { - "regexp-tree": "bin/regexp-tree" - } + "peer": true }, "node_modules/regexp.prototype.flags": { "version": "1.5.2", "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.2.tgz", "integrity": "sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==", + "dev": true, "dependencies": { "call-bind": "^1.0.6", "define-properties": "^1.2.1", @@ -27752,47 +18641,19 @@ "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", "integrity": "sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==", "dev": true, + "optional": true, + "peer": true, "engines": { "node": ">= 0.10" } }, - "node_modules/remark-external-links": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/remark-external-links/-/remark-external-links-8.0.0.tgz", - "integrity": "sha512-5vPSX0kHoSsqtdftSHhIYofVINC8qmp0nctkeU9YoJwV3YfiBRiI6cbFRJ0oI/1F9xS+bopXG0m2KS8VFscuKA==", - "dev": true, - "dependencies": { - "extend": "^3.0.0", - "is-absolute-url": "^3.0.0", - "mdast-util-definitions": "^4.0.0", - "space-separated-tokens": "^1.0.0", - "unist-util-visit": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/remark-slug": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/remark-slug/-/remark-slug-6.1.0.tgz", - "integrity": "sha512-oGCxDF9deA8phWvxFuyr3oSJsdyUAxMFbA0mZ7Y1Sas+emILtO+e5WutF9564gDsEN4IXaQXm5pFo6MLH+YmwQ==", - "dev": true, - "dependencies": { - "github-slugger": "^1.0.0", - "mdast-util-to-string": "^1.0.0", - "unist-util-visit": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, "node_modules/renderkid": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-3.0.0.tgz", "integrity": "sha512-q/7VIQA8lmM1hF+jn+sFSPWGlMkSAeNYcPLmDQx2zzuiDfaLrOmumR8iaUKlenFgh0XRPIUeSPlH3A+AW3Z5pg==", "dev": true, + "optional": true, + "peer": true, "dependencies": { "css-select": "^4.1.3", "dom-converter": "^0.2.0", @@ -27806,6 +18667,8 @@ "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz", "integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==", "dev": true, + "optional": true, + "peer": true, "dependencies": { "boolbase": "^1.0.0", "css-what": "^6.0.1", @@ -27822,6 +18685,8 @@ "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", "dev": true, + "optional": true, + "peer": true, "dependencies": { "domelementtype": "^2.0.1", "domhandler": "^4.2.0", @@ -27836,6 +18701,8 @@ "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", "dev": true, + "optional": true, + "peer": true, "dependencies": { "domelementtype": "^2.2.0" }, @@ -27851,6 +18718,8 @@ "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", "dev": true, + "optional": true, + "peer": true, "dependencies": { "dom-serializer": "^1.0.1", "domelementtype": "^2.2.0", @@ -27865,6 +18734,8 @@ "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", "dev": true, + "optional": true, + "peer": true, "funding": { "url": "https://github.com/fb55/entities?sponsor=1" } @@ -27881,6 +18752,8 @@ "url": "https://github.com/sponsors/fb55" } ], + "optional": true, + "peer": true, "dependencies": { "domelementtype": "^2.0.1", "domhandler": "^4.0.0", @@ -27888,15 +18761,6 @@ "entities": "^2.0.0" } }, - "node_modules/request-progress": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/request-progress/-/request-progress-3.0.0.tgz", - "integrity": "sha512-MnWzEHHaxHO2iWiQuHrUPBi/1WeBf5PkxQqNyNvLl9VAYSdXkP8tQ3pBSeCPD+yw0v0Aq1zosWLz0BdeXpWwZg==", - "dev": true, - "dependencies": { - "throttleit": "^1.0.0" - } - }, "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -27914,21 +18778,6 @@ "node": ">=0.10.0" } }, - "node_modules/require-main-filename": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", - "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", - "dev": true - }, - "node_modules/requireindex": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/requireindex/-/requireindex-1.2.0.tgz", - "integrity": "sha512-L9jEkOi3ASd9PYit2cwRfyppc9NoABujTP8/5gFcbERmo5jUoAKovIC3fsF17pkTnGsrByysqX+Kxd2OTNI1ww==", - "dev": true, - "engines": { - "node": ">=0.10.5" - } - }, "node_modules/requires-port": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", @@ -27939,6 +18788,7 @@ "version": "1.22.8", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", + "dev": true, "dependencies": { "is-core-module": "^2.13.0", "path-parse": "^1.0.7", @@ -28002,19 +18852,12 @@ "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", "dev": true }, - "node_modules/resolve-pkg-maps": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", - "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", - "funding": { - "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" - } - }, "node_modules/resolve-url-loader": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/resolve-url-loader/-/resolve-url-loader-5.0.0.tgz", "integrity": "sha512-uZtduh8/8srhBoMx//5bwqjQ+rfYOUq8zC9NrMUGtjBiGTtFJM42s58/36+hTqeqINcnYe08Nj3LkK9lW4N8Xg==", "dev": true, + "peer": true, "dependencies": { "adjust-sourcemap-loader": "^4.0.0", "convert-source-map": "^1.7.0", @@ -28031,6 +18874,7 @@ "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", "dev": true, + "peer": true, "dependencies": { "big.js": "^5.2.2", "emojis-list": "^3.0.0", @@ -28045,6 +18889,7 @@ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true, + "peer": true, "engines": { "node": ">=0.10.0" } @@ -28084,74 +18929,38 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, "engines": { "iojs": ">=1.0.0", "node": ">=0.10.0" } }, "node_modules/rfdc": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.1.tgz", - "integrity": "sha512-r5a3l5HzYlIC68TpmYKlxWjmOP6wiPJ1vWv2HeLhNsRZMrCkxeqxiHlQ21oXmQ4F3SiryXBHhAD7JZqvOJjFmg==", + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", + "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", "dev": true }, "node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "version": "5.0.10", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.10.tgz", + "integrity": "sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ==", + "dev": true, + "peer": true, "dependencies": { - "glob": "^7.1.3" + "glob": "^10.3.7" }, "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/rimraf/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/rimraf/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" + "rimraf": "dist/esm/bin.mjs" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/rimraf/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, "node_modules/rollup": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.12.0.tgz", - "integrity": "sha512-wz66wn4t1OHIJw3+XU7mJJQV/2NAfw5OAk6G6Hoo3zcvz/XOfQ52Vgi+AN4Uxoxi0KBBwk2g8zPrTDA4btSB/Q==", + "version": "4.20.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.20.0.tgz", + "integrity": "sha512-6rbWBChcnSGzIlXeIdNIZTopKYad8ZG8ajhl78lGRLsI2rX8IkaotQhVas2Ma+GPxJav19wrSzvRvuiv0YKzWw==", "dev": true, "dependencies": { "@types/estree": "1.0.5" @@ -28164,35 +18973,43 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.12.0", - "@rollup/rollup-android-arm64": "4.12.0", - "@rollup/rollup-darwin-arm64": "4.12.0", - "@rollup/rollup-darwin-x64": "4.12.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.12.0", - "@rollup/rollup-linux-arm64-gnu": "4.12.0", - "@rollup/rollup-linux-arm64-musl": "4.12.0", - "@rollup/rollup-linux-riscv64-gnu": "4.12.0", - "@rollup/rollup-linux-x64-gnu": "4.12.0", - "@rollup/rollup-linux-x64-musl": "4.12.0", - "@rollup/rollup-win32-arm64-msvc": "4.12.0", - "@rollup/rollup-win32-ia32-msvc": "4.12.0", - "@rollup/rollup-win32-x64-msvc": "4.12.0", + "@rollup/rollup-android-arm-eabi": "4.20.0", + "@rollup/rollup-android-arm64": "4.20.0", + "@rollup/rollup-darwin-arm64": "4.20.0", + "@rollup/rollup-darwin-x64": "4.20.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.20.0", + "@rollup/rollup-linux-arm-musleabihf": "4.20.0", + "@rollup/rollup-linux-arm64-gnu": "4.20.0", + "@rollup/rollup-linux-arm64-musl": "4.20.0", + "@rollup/rollup-linux-powerpc64le-gnu": "4.20.0", + "@rollup/rollup-linux-riscv64-gnu": "4.20.0", + "@rollup/rollup-linux-s390x-gnu": "4.20.0", + "@rollup/rollup-linux-x64-gnu": "4.20.0", + "@rollup/rollup-linux-x64-musl": "4.20.0", + "@rollup/rollup-win32-arm64-msvc": "4.20.0", + "@rollup/rollup-win32-ia32-msvc": "4.20.0", + "@rollup/rollup-win32-x64-msvc": "4.20.0", "fsevents": "~2.3.2" } }, - "node_modules/run-async": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/run-async/-/run-async-3.0.0.tgz", - "integrity": "sha512-540WwVDOMxA6dN6We19EcT9sc3hkXPw5mzRNGM3FkdN/vtE9NFvj5lFAPNwUDmJjXidm3v7TC1cTE7t17Ulm1Q==", + "node_modules/run-applescript": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-7.0.0.tgz", + "integrity": "sha512-9by4Ij99JUr/MCFBUkDKLWK3G9HVXmabKz9U5MlIAIuvuzkiOicRYs8XJLxX+xahD+mLiiCYDqF9dKAgtzKP1A==", "dev": true, + "peer": true, "engines": { - "node": ">=0.12.0" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, "funding": [ { "type": "github", @@ -28219,65 +19036,22 @@ "tslib": "^2.1.0" } }, - "node_modules/safe-array-concat": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.0.tgz", - "integrity": "sha512-ZdQ0Jeb9Ofti4hbt5lX3T2JcAamT9hfzYU1MNB+z/jaEbB6wfFfPIR/zEORmZqobkCCJhSjodobH6WHNmJ97dg==", - "dependencies": { - "call-bind": "^1.0.5", - "get-intrinsic": "^1.2.2", - "has-symbols": "^1.0.3", - "isarray": "^2.0.5" - }, - "engines": { - "node": ">=0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", "dev": true }, - "node_modules/safe-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-2.1.1.tgz", - "integrity": "sha512-rx+x8AMzKb5Q5lQ95Zoi6ZbJqwCLkqi3XuJXp5P3rT8OEc6sZCJG5AE5dU3lsgRr/F4Bs31jSlVN+j5KrsGu9A==", - "dev": true, - "peer": true, - "dependencies": { - "regexp-tree": "~0.1.1" - } - }, - "node_modules/safe-regex-test": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.3.tgz", - "integrity": "sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==", - "dependencies": { - "call-bind": "^1.0.6", - "es-errors": "^1.3.0", - "is-regex": "^1.1.4" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "devOptional": true + "dev": true }, "node_modules/sass": { - "version": "1.70.0", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.70.0.tgz", - "integrity": "sha512-uUxNQ3zAHeAx5nRFskBnrWzDUJrrvpCPD5FNAoRvTi0WwremlheES3tg+56PaVtCs5QDRX5CBLxxKMDJMEa1WQ==", + "version": "1.77.8", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.77.8.tgz", + "integrity": "sha512-4UHg6prsrycW20fqLGPShtEvo/WyHRVRHwOP4DzkUrObWoWI05QBSfzU71TVB7PFaL104TwNaHpjlWXAZbQiNQ==", "dev": true, "dependencies": { "chokidar": ">=3.0.0 <4.0.0", @@ -28292,10 +19066,11 @@ } }, "node_modules/sass-loader": { - "version": "14.1.0", - "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-14.1.0.tgz", - "integrity": "sha512-LS2mLeFWA+orYxHNu+O18Xe4jR0kyamNOOUsE3NyBP4DvIL+8stHpNX0arYTItdPe80kluIiJ7Wfe/9iHSRO0Q==", + "version": "16.0.0", + "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-16.0.0.tgz", + "integrity": "sha512-n13Z+3rU9A177dk4888czcVFiC8CL9dii4qpXWUg3YIIgZEvi9TCFKjOQcbK0kJM7DJu9VucrZFddvNfYCPwtw==", "dev": true, + "peer": true, "dependencies": { "neo-async": "^2.6.2" }, @@ -28349,16 +19124,6 @@ "node": ">=v12.22.7" } }, - "node_modules/scheduler": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", - "integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==", - "dev": true, - "peer": true, - "dependencies": { - "loose-envify": "^1.1.0" - } - }, "node_modules/schema-utils": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.2.0.tgz", @@ -28397,13 +19162,15 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", "integrity": "sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg==", - "dev": true + "dev": true, + "peer": true }, "node_modules/selfsigned": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-2.4.1.tgz", "integrity": "sha512-th5B4L2U+eGLq1TVh7zNRGBapioSORUeymIydxgFpwww9d2qyKvtuPU2jJuHvYAwwqi2Y596QBL3eEqcPEYL8Q==", "dev": true, + "peer": true, "dependencies": { "@types/node-forge": "^1.3.0", "node-forge": "^1" @@ -28501,6 +19268,7 @@ "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", "dev": true, + "peer": true, "dependencies": { "randombytes": "^2.1.0" } @@ -28604,12 +19372,6 @@ "integrity": "sha512-rb+9B5YBIEzYcD6x2VKidaa+cqYBJQKnU4oe4E3ANwRRN56yk/ua1YCJT1n21NTS8w6CcOclAKNP3PhdCXKYtQ==", "dev": true }, - "node_modules/set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", - "dev": true - }, "node_modules/set-function-length": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.1.tgz", @@ -28630,6 +19392,7 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", + "dev": true, "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", @@ -28651,6 +19414,7 @@ "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", "dev": true, + "peer": true, "dependencies": { "kind-of": "^6.0.2" }, @@ -28668,6 +19432,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, "dependencies": { "shebang-regex": "^3.0.0" }, @@ -28679,6 +19444,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, "engines": { "node": ">=8" } @@ -28696,6 +19462,7 @@ "version": "1.0.5", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.5.tgz", "integrity": "sha512-QcgiIWV4WV7qWExbN5llt6frQB/lBven9pqliLXfGPB+K9ZYXxDozp0wLkHS24kWCm+6YXH/f0HhnObZnZOBnQ==", + "dev": true, "dependencies": { "call-bind": "^1.0.6", "es-errors": "^1.3.0", @@ -28716,17 +19483,17 @@ "dev": true }, "node_modules/sigstore": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/sigstore/-/sigstore-2.2.2.tgz", - "integrity": "sha512-2A3WvXkQurhuMgORgT60r6pOWiCOO5LlEqY2ADxGBDGVYLSo5HN0uLtb68YpVpuL/Vi8mLTe7+0Dx2Fq8lLqEg==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/sigstore/-/sigstore-2.3.1.tgz", + "integrity": "sha512-8G+/XDU8wNsJOQS5ysDVO0Etg9/2uA5gR9l4ZwijjlwxBcrU6RPfwi2+jJmbP+Ap1Hlp/nVAaEO4Fj22/SL2gQ==", "dev": true, "dependencies": { - "@sigstore/bundle": "^2.2.0", + "@sigstore/bundle": "^2.3.2", "@sigstore/core": "^1.0.0", - "@sigstore/protobuf-specs": "^0.3.0", - "@sigstore/sign": "^2.2.3", - "@sigstore/tuf": "^2.3.1", - "@sigstore/verify": "^1.1.0" + "@sigstore/protobuf-specs": "^0.3.2", + "@sigstore/sign": "^2.3.2", + "@sigstore/tuf": "^2.3.4", + "@sigstore/verify": "^1.2.1" }, "engines": { "node": "^16.14.0 || >=18.0.0" @@ -28748,34 +19515,45 @@ } }, "node_modules/slice-ansi": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz", - "integrity": "sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-5.0.0.tgz", + "integrity": "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==", "dev": true, "dependencies": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" + "ansi-styles": "^6.0.0", + "is-fullwidth-code-point": "^4.0.0" }, "engines": { - "node": ">=8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" } }, "node_modules/slice-ansi/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, "engines": { - "node": ">=8" + "node": ">=12" }, "funding": { "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/slice-ansi/node_modules/is-fullwidth-code-point": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz", + "integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/slugify": { "version": "1.6.6", "resolved": "https://registry.npmjs.org/slugify/-/slugify-1.6.6.tgz", @@ -28800,6 +19578,7 @@ "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz", "integrity": "sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ==", "dev": true, + "peer": true, "dependencies": { "faye-websocket": "^0.11.3", "uuid": "^8.3.2", @@ -28811,14 +19590,15 @@ "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", "dev": true, + "peer": true, "bin": { "uuid": "dist/bin/uuid" } }, "node_modules/socks": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.1.tgz", - "integrity": "sha512-B6w7tkwNid7ToxjZ08rQMT8M9BJAf8DKx8Ft4NivzH0zBUfd6jldGcisJn/RLgxcX3FPNDdNQCUEMMT79b+oCQ==", + "version": "2.8.3", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.3.tgz", + "integrity": "sha512-l5x7VUUWbjVFbafGLxPWkYsHIhEvmF85tbIeFZWc8ZPtoMyybuEhL7Jye/ooC4/d48FgOjSJXgsF/AJPYCW8Zw==", "dev": true, "dependencies": { "ip-address": "^9.0.5", @@ -28830,14 +19610,14 @@ } }, "node_modules/socks-proxy-agent": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.2.tgz", - "integrity": "sha512-8zuqoLv1aP/66PHF5TqwJ7Czm3Yv32urJQHrVyhD7mmA6d61Zv8cIXQYPTWwmg6qlupnPvs/QKDmfa4P/qct2g==", + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.4.tgz", + "integrity": "sha512-GNAq/eg8Udq2x0eNiFkr9gRg5bA7PXEWagQdeRX4cPSG+X/8V38v637gim9bjFptMk1QWsCTr0ttrJEiXbNnRw==", "dev": true, "dependencies": { - "agent-base": "^7.0.2", + "agent-base": "^7.1.1", "debug": "^4.3.4", - "socks": "^2.7.1" + "socks": "^2.8.3" }, "engines": { "node": ">= 14" @@ -28876,9 +19656,9 @@ } }, "node_modules/source-map-js": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", - "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz", + "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==", "dev": true, "engines": { "node": ">=0.10.0" @@ -28889,6 +19669,7 @@ "resolved": "https://registry.npmjs.org/source-map-loader/-/source-map-loader-5.0.0.tgz", "integrity": "sha512-k2Dur7CbSLcAH73sBcIkV5xjPV4SzqO1NJ7+XaQl8if3VODDUj3FNchNGpqgJSKbvUfJuhVdv8K2Eu8/TNl2eA==", "dev": true, + "peer": true, "dependencies": { "iconv-lite": "^0.6.3", "source-map-js": "^1.0.2" @@ -28909,6 +19690,7 @@ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", "dev": true, + "peer": true, "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" }, @@ -28921,6 +19703,7 @@ "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", "dev": true, + "peer": true, "dependencies": { "buffer-from": "^1.0.0", "source-map": "^0.6.0" @@ -28931,6 +19714,7 @@ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true, + "peer": true, "engines": { "node": ">=0.10.0" } @@ -28942,16 +19726,6 @@ "deprecated": "Please use @jridgewell/sourcemap-codec instead", "dev": true }, - "node_modules/space-separated-tokens": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-1.1.5.tgz", - "integrity": "sha512-q/JSVd1Lptzhf5bkYm4ob4iWPjx0KiRe3sRFBNrVqbJkFaBm5vbbowy1mymoPNLRa52+oadOhJ+K49wsSeSjTA==", - "dev": true, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, "node_modules/spdx-correct": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", @@ -28989,6 +19763,7 @@ "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz", "integrity": "sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==", "dev": true, + "peer": true, "dependencies": { "debug": "^4.1.0", "handle-thing": "^2.0.0", @@ -29005,6 +19780,7 @@ "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz", "integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==", "dev": true, + "peer": true, "dependencies": { "debug": "^4.1.0", "detect-node": "^2.0.4", @@ -29041,41 +19817,10 @@ "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", "dev": true }, - "node_modules/sshpk": { - "version": "1.18.0", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.18.0.tgz", - "integrity": "sha512-2p2KJZTSqQ/I3+HX42EpYOa2l3f8Erv8MWKsy2I9uf4wA7yFIkXRffYdsx86y6z4vHtV8u7g+pPlr8/4ouAxsQ==", - "dev": true, - "dependencies": { - "asn1": "~0.2.3", - "assert-plus": "^1.0.0", - "bcrypt-pbkdf": "^1.0.0", - "dashdash": "^1.12.0", - "ecc-jsbn": "~0.1.1", - "getpass": "^0.1.1", - "jsbn": "~0.1.0", - "safer-buffer": "^2.0.2", - "tweetnacl": "~0.14.0" - }, - "bin": { - "sshpk-conv": "bin/sshpk-conv", - "sshpk-sign": "bin/sshpk-sign", - "sshpk-verify": "bin/sshpk-verify" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/sshpk/node_modules/jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==", - "dev": true - }, "node_modules/ssri": { - "version": "10.0.5", - "resolved": "https://registry.npmjs.org/ssri/-/ssri-10.0.5.tgz", - "integrity": "sha512-bSf16tAFkGeRlUNDjXu8FzaMQt6g2HZJrun7mtMbIPOddxt3GLMSz5VWUWcqTJUPfLEaDIepGxv+bYQW49596A==", + "version": "10.0.6", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-10.0.6.tgz", + "integrity": "sha512-MGrFH9Z4NP9Iyhqn16sDtBpRRNJ0Y2hNa6D65h736fVSaPCHr4DM4sWUNvVaSuC+0OBGhwsrydQwmgfg5LncqQ==", "dev": true, "dependencies": { "minipass": "^7.0.3" @@ -29167,12 +19912,6 @@ "node": ">=4.0" } }, - "node_modules/static-module/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "dev": true - }, "node_modules/static-module/node_modules/levn": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", @@ -29295,41 +20034,6 @@ "graceful-fs": "^4.1.3" } }, - "node_modules/stop-iteration-iterator": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.0.0.tgz", - "integrity": "sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ==", - "dev": true, - "dependencies": { - "internal-slot": "^1.0.4" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/store2": { - "version": "2.14.3", - "resolved": "https://registry.npmjs.org/store2/-/store2-2.14.3.tgz", - "integrity": "sha512-4QcZ+yx7nzEFiV4BMLnr/pRa5HYzNITX2ri0Zh6sT9EyQHbBHacC6YigllUPU9X3D0f/22QCgfokpKs52YRrUg==", - "dev": true - }, - "node_modules/storybook": { - "version": "7.6.17", - "resolved": "https://registry.npmjs.org/storybook/-/storybook-7.6.17.tgz", - "integrity": "sha512-8+EIo91bwmeFWPg1eysrxXlhIYv3OsXrznTr4+4Eq0NikqAoq6oBhtlN5K2RGS2lBVF537eN+9jTCNbR+WrzDA==", - "dev": true, - "dependencies": { - "@storybook/cli": "7.6.17" - }, - "bin": { - "sb": "index.js", - "storybook": "index.js" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - } - }, "node_modules/stream-combiner": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.2.2.tgz", @@ -29340,12 +20044,6 @@ "through": "~2.3.4" } }, - "node_modules/stream-shift": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.3.tgz", - "integrity": "sha512-76ORR0DO1o1hlKwTbi/DM3EXWGf3ZJYO8cXX5RJwnul2DEg2oyoZyjLNoQM8WsvZiFKCRfC1O0J7iCvie3RZmQ==", - "dev": true - }, "node_modules/string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", @@ -29429,73 +20127,11 @@ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "dev": true }, - "node_modules/string.prototype.matchall": { - "version": "4.0.10", - "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.10.tgz", - "integrity": "sha512-rGXbGmOEosIQi6Qva94HUjgPs9vKW+dkG7Y8Q5O2OYkWL6wFaTRZO8zM4mhP94uX55wgyrXzfS2aGtGzUL7EJQ==", - "dev": true, - "peer": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "get-intrinsic": "^1.2.1", - "has-symbols": "^1.0.3", - "internal-slot": "^1.0.5", - "regexp.prototype.flags": "^1.5.0", - "set-function-name": "^2.0.0", - "side-channel": "^1.0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/string.prototype.trim": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.8.tgz", - "integrity": "sha512-lfjY4HcixfQXOfaqCvcBuOIapyaroTXhbkfJN3gcB1OtyupngWK4sEET9Knd0cXd28kTUqu/kHoV4HKSJdnjiQ==", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/string.prototype.trimend": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.7.tgz", - "integrity": "sha512-Ni79DqeB72ZFq1uH/L6zJ+DKZTkOtPIHovb3YZHQViE+HDouuU4mBrLOLDn5Dde3RF8qw5qVETEjhu9locMLvA==", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/string.prototype.trimstart": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.7.tgz", - "integrity": "sha512-NGhtDFu3jCEm7B4Fy0DpLewdJQOZcQ0rGbwQ/+stjnrp2i+rlKeCvos9hOIeCmqwratM47OBxY7uFZzjxHXmrg==", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, "dependencies": { "ansi-regex": "^5.0.1" }, @@ -29550,6 +20186,7 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, "engines": { "node": ">=8" }, @@ -29557,39 +20194,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/strong-log-transformer": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/strong-log-transformer/-/strong-log-transformer-2.1.0.tgz", - "integrity": "sha512-B3Hgul+z0L9a236FAUC9iZsL+nVHgoCJnqCbN588DjYxvGXaXaaFbfmQ/JhvKjZwsOukuR72XbHv71Qkug0HxA==", - "dev": true, - "dependencies": { - "duplexer": "^0.1.1", - "minimist": "^1.2.0", - "through": "^2.3.4" - }, - "bin": { - "sl-log-transformer": "bin/sl-log-transformer.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/style-loader": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-3.3.4.tgz", - "integrity": "sha512-0WqXzrsMTyb8yjZJHDqwmnwRJvhALK9LfRtRc6B4UTWe8AijYLZYZ9thuJTZc2VfQWINADW/j+LiJnfy2RoC1w==", - "dev": true, - "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^5.0.0" - } - }, "node_modules/supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", @@ -29606,6 +20210,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, "engines": { "node": ">= 0.4" }, @@ -29619,19 +20224,6 @@ "integrity": "sha512-JaKkGHHfGvRrcMPdJWkssLBeWqM+Isg/a09H7kgNNajT1cX5AztDTNs+C8UzpCxjCTRrG34WbquwaovZbmSk9g==", "dev": true }, - "node_modules/swc-loader": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/swc-loader/-/swc-loader-0.2.6.tgz", - "integrity": "sha512-9Zi9UP2YmDpgmQVbyOPJClY0dwf58JDyDMQ7uRc4krmc72twNI2fvlBWHLqVekBpPc7h5NJkGVT1zNDxFrqhvg==", - "dev": true, - "dependencies": { - "@swc/counter": "^0.1.3" - }, - "peerDependencies": { - "@swc/core": "^1.2.147", - "webpack": ">=2" - } - }, "node_modules/symbol-observable": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-4.0.0.tgz", @@ -29647,28 +20239,6 @@ "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", "dev": true }, - "node_modules/synchronous-promise": { - "version": "2.0.17", - "resolved": "https://registry.npmjs.org/synchronous-promise/-/synchronous-promise-2.0.17.tgz", - "integrity": "sha512-AsS729u2RHUfEra9xJrE39peJcc2stq2+poBXX8bcM08Y6g9j/i/PUzwNQqkaJde7Ntg1TO7bSREbR5sdosQ+g==", - "dev": true - }, - "node_modules/synckit": { - "version": "0.8.8", - "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.8.8.tgz", - "integrity": "sha512-HwOKAP7Wc5aRGYdKH+dw0PRRpbO841v2DENBtjnR5HFWoiNByAl7vrx3p0G/rCyYXQsrxqtX48TImFtPcIHSpQ==", - "dev": true, - "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/tablesort": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/tablesort/-/tablesort-5.3.0.tgz", @@ -29679,14 +20249,16 @@ "version": "2.2.1", "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", + "dev": true, + "peer": true, "engines": { "node": ">=6" } }, "node_modules/tar": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.0.tgz", - "integrity": "sha512-/Wo7DcT0u5HUV486xg675HtjNd3BXZ6xDbzsCUZPt5iw8bTQ63bP0Raut3mvro9u+CUyq7YQd8Cx55fsZXxqLQ==", + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", + "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", "dev": true, "dependencies": { "chownr": "^2.0.0", @@ -29700,40 +20272,6 @@ "node": ">=10" } }, - "node_modules/tar-fs": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz", - "integrity": "sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==", - "dev": true, - "dependencies": { - "chownr": "^1.1.1", - "mkdirp-classic": "^0.5.2", - "pump": "^3.0.0", - "tar-stream": "^2.1.4" - } - }, - "node_modules/tar-fs/node_modules/chownr": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", - "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", - "dev": true - }, - "node_modules/tar-stream": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", - "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", - "dev": true, - "dependencies": { - "bl": "^4.0.3", - "end-of-stream": "^1.4.1", - "fs-constants": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^3.1.1" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/tar/node_modules/fs-minipass": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", @@ -29785,126 +20323,12 @@ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "dev": true }, - "node_modules/telejson": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/telejson/-/telejson-7.2.0.tgz", - "integrity": "sha512-1QTEcJkJEhc8OnStBx/ILRu5J2p0GjvWsBx56bmZRqnrkdBMUe+nX92jxV+p3dB4CP6PZCdJMQJwCggkNBMzkQ==", - "dev": true, - "dependencies": { - "memoizerific": "^1.11.3" - } - }, - "node_modules/temp": { - "version": "0.8.4", - "resolved": "https://registry.npmjs.org/temp/-/temp-0.8.4.tgz", - "integrity": "sha512-s0ZZzd0BzYv5tLSptZooSjK8oj6C+c19p7Vqta9+6NPOf7r+fxq0cJe6/oN4LTC79sy5NY8ucOJNgwsKCSbfqg==", - "dev": true, - "dependencies": { - "rimraf": "~2.6.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/temp-dir": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-2.0.0.tgz", - "integrity": "sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/temp/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/temp/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/temp/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/temp/node_modules/rimraf": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", - "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", - "dev": true, - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - } - }, - "node_modules/tempy": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/tempy/-/tempy-1.0.1.tgz", - "integrity": "sha512-biM9brNqxSc04Ee71hzFbryD11nX7VPhQQY32AdDmjFvodsRFz/3ufeoTZ6uYkRFfGo188tENcASNs3vTdsM0w==", - "dev": true, - "dependencies": { - "del": "^6.0.0", - "is-stream": "^2.0.0", - "temp-dir": "^2.0.0", - "type-fest": "^0.16.0", - "unique-string": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/tempy/node_modules/type-fest": { - "version": "0.16.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.16.0.tgz", - "integrity": "sha512-eaBzG6MxNzEn9kiwvtre90cXaNLkmadMWa1zQMs3XORCXNbsH/OewwbxC5ia9dCxIxnTAsSxXJaa/p5y8DlvJg==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/terser": { - "version": "5.27.0", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.27.0.tgz", - "integrity": "sha512-bi1HRwVRskAjheeYl291n3JC4GgO/Ty4z1nVs5AAsmonJulGxpSektecnNedrwK9C7vpvVtcX3cw00VSLt7U2A==", + "version": "5.31.6", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.31.6.tgz", + "integrity": "sha512-PQ4DAriWzKj+qgehQ7LK5bQqCFNMmlhjR2PFFLuqGCpuCAauxemVBWwWOxo3UIwWQx8+Pr61Df++r76wDmkQBg==", "dev": true, + "peer": true, "dependencies": { "@jridgewell/source-map": "^0.3.3", "acorn": "^8.8.2", @@ -29923,6 +20347,7 @@ "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.10.tgz", "integrity": "sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w==", "dev": true, + "peer": true, "dependencies": { "@jridgewell/trace-mapping": "^0.3.20", "jest-worker": "^27.4.5", @@ -29957,6 +20382,7 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -29973,6 +20399,7 @@ "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", "dev": true, + "peer": true, "peerDependencies": { "ajv": "^6.9.1" } @@ -29982,6 +20409,7 @@ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, + "peer": true, "engines": { "node": ">=8" } @@ -29991,6 +20419,7 @@ "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", "dev": true, + "peer": true, "dependencies": { "@types/node": "*", "merge-stream": "^2.0.0", @@ -30004,13 +20433,15 @@ "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true + "dev": true, + "peer": true }, "node_modules/terser-webpack-plugin/node_modules/schema-utils": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", "dev": true, + "peer": true, "dependencies": { "@types/json-schema": "^7.0.8", "ajv": "^6.12.5", @@ -30029,6 +20460,7 @@ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", "dev": true, + "peer": true, "dependencies": { "has-flag": "^4.0.0" }, @@ -30043,7 +20475,8 @@ "version": "2.20.3", "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "dev": true + "dev": true, + "peer": true }, "node_modules/test-exclude": { "version": "6.0.0", @@ -30113,18 +20546,17 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==" - }, - "node_modules/throttleit": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/throttleit/-/throttleit-1.0.1.tgz", - "integrity": "sha512-vDZpf9Chs9mAdfY046mcPt8fg5QSZr37hEH4TXYBnDF+izxgrbRGUAAaBvIk/fJm9aOFCGFd1EsNg5AZCbnQCQ==", + "node_modules/thingies": { + "version": "1.21.0", + "resolved": "https://registry.npmjs.org/thingies/-/thingies-1.21.0.tgz", + "integrity": "sha512-hsqsJsFMsV+aD4s3CWKk85ep/3I9XzYV/IXaSouJMYIoDlgyi11cBhsqYe9/geRfB0YIikBQg6raRaM+nIMP9g==", "dev": true, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "peer": true, + "engines": { + "node": ">=10.18" + }, + "peerDependencies": { + "tslib": "^2" } }, "node_modules/through": { @@ -30146,7 +20578,8 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==", - "dev": true + "dev": true, + "peer": true }, "node_modules/tiny-inflate": { "version": "1.0.3", @@ -30154,22 +20587,16 @@ "integrity": "sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw==", "dev": true }, - "node_modules/tiny-invariant": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz", - "integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==", - "dev": true - }, "node_modules/tmp": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", - "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", "dev": true, "dependencies": { - "rimraf": "^3.0.0" + "os-tmpdir": "~1.0.2" }, "engines": { - "node": ">=8.17.0" + "node": ">=0.6.0" } }, "node_modules/tmpl": { @@ -30191,6 +20618,7 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, "dependencies": { "is-number": "^7.0.0" }, @@ -30198,12 +20626,6 @@ "node": ">=8.0" } }, - "node_modules/tocbot": { - "version": "4.25.0", - "resolved": "https://registry.npmjs.org/tocbot/-/tocbot-4.25.0.tgz", - "integrity": "sha512-kE5wyCQJ40hqUaRVkyQ4z5+4juzYsv/eK+aqD97N62YH0TxFhzJvo22RUQQZdO3YnXAk42ZOfOpjVdy+Z0YokA==", - "dev": true - }, "node_modules/toidentifier": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", @@ -30260,43 +20682,40 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/tree-dump": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/tree-dump/-/tree-dump-1.0.2.tgz", + "integrity": "sha512-dpev9ABuLWdEubk+cIaI9cHwRNNDjkBBLXTwI4UCUFdQ5xXKqNXoK4FEciw/vxf+NQ7Cb7sGUyeUtORvHIdRXQ==", + "dev": true, + "peer": true, + "engines": { + "node": ">=10.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/streamich" + }, + "peerDependencies": { + "tslib": "2" + } + }, "node_modules/tree-kill": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", "dev": true, + "peer": true, "bin": { "tree-kill": "cli.js" } }, - "node_modules/trim-newlines": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.1.tgz", - "integrity": "sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/ts-api-utils": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.2.1.tgz", - "integrity": "sha512-RIYA36cJn2WiH9Hy77hdF9r7oEwxAtB/TS9/S4Qd90Ap4z5FSiin5zEiTL44OII1Y3IIlEvxwxFUVgrHSZ/UpA==", - "dev": true, - "engines": { - "node": ">=16" - }, - "peerDependencies": { - "typescript": ">=4.2.0" - } - }, - "node_modules/ts-dedent": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/ts-dedent/-/ts-dedent-2.2.0.tgz", - "integrity": "sha512-q5W7tVM71e2xjHZTlgfTDoPF/SmqKG5hddq9SzR49CH2hayqRKJtQ4mtRlSxKaJlR/+9rEM+mnBHf7I2/BQcpQ==", + "node_modules/trim-newlines": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.1.tgz", + "integrity": "sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==", "dev": true, "engines": { - "node": ">=6.10" + "node": ">=8" } }, "node_modules/ts-jest": { @@ -30483,218 +20902,31 @@ "node": ">=8" } }, - "node_modules/tsconfig-paths": { - "version": "3.15.0", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", - "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==", - "dependencies": { - "@types/json5": "^0.0.29", - "json5": "^1.0.2", - "minimist": "^1.2.6", - "strip-bom": "^3.0.0" - } - }, - "node_modules/tsconfig-paths-webpack-plugin": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/tsconfig-paths-webpack-plugin/-/tsconfig-paths-webpack-plugin-4.1.0.tgz", - "integrity": "sha512-xWFISjviPydmtmgeUAuXp4N1fky+VCtfhOkDUFIv5ea7p4wuTomI4QTrXvFBX2S4jZsmyTSrStQl+E+4w+RzxA==", - "dev": true, - "dependencies": { - "chalk": "^4.1.0", - "enhanced-resolve": "^5.7.0", - "tsconfig-paths": "^4.1.2" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/tsconfig-paths-webpack-plugin/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/tsconfig-paths-webpack-plugin/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, - "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/tsconfig-paths-webpack-plugin/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/tsconfig-paths-webpack-plugin/node_modules/strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/tsconfig-paths-webpack-plugin/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/tsconfig-paths-webpack-plugin/node_modules/tsconfig-paths": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz", - "integrity": "sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==", - "dev": true, - "dependencies": { - "json5": "^2.2.2", - "minimist": "^1.2.6", - "strip-bom": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/tsconfig-paths/node_modules/json5": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", - "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", - "dependencies": { - "minimist": "^1.2.0" - }, - "bin": { - "json5": "lib/cli.js" - } - }, - "node_modules/tsconfig-paths/node_modules/strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", - "engines": { - "node": ">=4" - } - }, "node_modules/tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" - }, - "node_modules/tsutils": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", - "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", - "dev": true, - "dependencies": { - "tslib": "^1.8.1" - }, - "engines": { - "node": ">= 6" - }, - "peerDependencies": { - "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" - } - }, - "node_modules/tsutils-etc": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/tsutils-etc/-/tsutils-etc-1.4.2.tgz", - "integrity": "sha512-2Dn5SxTDOu6YWDNKcx1xu2YUy6PUeKrWZB/x2cQ8vY2+iz3JRembKn/iZ0JLT1ZudGNwQQvtFX9AwvRHbXuPUg==", - "dev": true, - "dependencies": { - "@types/yargs": "^17.0.0", - "yargs": "^17.0.0" - }, - "bin": { - "ts-flags": "bin/ts-flags", - "ts-kind": "bin/ts-kind" - }, - "peerDependencies": { - "tsutils": "^3.0.0", - "typescript": ">=4.0.0" - } - }, - "node_modules/tsutils/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" }, "node_modules/tuf-js": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/tuf-js/-/tuf-js-2.2.0.tgz", - "integrity": "sha512-ZSDngmP1z6zw+FIkIBjvOp/II/mIub/O7Pp12j1WNsiCpg5R5wAc//i555bBQsE44O94btLt0xM/Zr2LQjwdCg==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tuf-js/-/tuf-js-2.2.1.tgz", + "integrity": "sha512-GwIJau9XaA8nLVbUXsN3IlFi7WmQ48gBUrl3FTkkL/XLu/POhBzfmX9hd33FNMX1qAsfl6ozO1iMmW9NC8YniA==", "dev": true, "dependencies": { - "@tufjs/models": "2.0.0", + "@tufjs/models": "2.0.1", "debug": "^4.3.4", - "make-fetch-happen": "^13.0.0" + "make-fetch-happen": "^13.0.1" }, "engines": { "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", - "dev": true, - "dependencies": { - "safe-buffer": "^5.0.1" - }, - "engines": { - "node": "*" - } - }, - "node_modules/tweetnacl": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==", - "dev": true - }, "node_modules/type": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz", "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==", "dev": true }, - "node_modules/type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "dependencies": { - "prelude-ls": "^1.2.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, "node_modules/type-detect": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", @@ -30704,18 +20936,6 @@ "node": ">=4" } }, - "node_modules/type-fest": { - "version": "2.19.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", - "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==", - "dev": true, - "engines": { - "node": ">=12.20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/type-is": { "version": "1.6.18", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", @@ -30729,80 +20949,12 @@ "node": ">= 0.6" } }, - "node_modules/typed-array-buffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.2.tgz", - "integrity": "sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==", - "dependencies": { - "call-bind": "^1.0.7", - "es-errors": "^1.3.0", - "is-typed-array": "^1.1.13" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/typed-array-byte-length": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.1.tgz", - "integrity": "sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==", - "dependencies": { - "call-bind": "^1.0.7", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-proto": "^1.0.3", - "is-typed-array": "^1.1.13" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/typed-array-byte-offset": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.2.tgz", - "integrity": "sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA==", - "dependencies": { - "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.7", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-proto": "^1.0.3", - "is-typed-array": "^1.1.13" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/typed-array-length": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.5.tgz", - "integrity": "sha512-yMi0PlwuznKHxKmcpoOdeLwxBoVPkqZxd7q2FgMkmD3bNwvF5VW0+UlUQ1k1vmktTu4Yu13Q0RIxEP8+B+wloA==", - "dependencies": { - "call-bind": "^1.0.7", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-proto": "^1.0.3", - "is-typed-array": "^1.1.13", - "possible-typed-array-names": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/typed-assert": { "version": "1.0.9", "resolved": "https://registry.npmjs.org/typed-assert/-/typed-assert-1.0.9.tgz", "integrity": "sha512-KNNZtayBCtmnNmbo5mG47p1XsCyrx6iVqomjcZnec/1Y5GGARaxPs6r49RnSPeUP3YjNYiU9sQHAtY4BBvnZwg==", - "dev": true + "dev": true, + "peer": true }, "node_modules/typedarray": { "version": "0.0.6", @@ -30811,9 +20963,9 @@ "dev": true }, "node_modules/typescript": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", - "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==", + "version": "5.5.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.4.tgz", + "integrity": "sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==", "dev": true, "bin": { "tsc": "bin/tsc", @@ -30823,12 +20975,6 @@ "node": ">=14.17" } }, - "node_modules/ufo": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.4.0.tgz", - "integrity": "sha512-Hhy+BhRBleFjpJ2vchUNN40qgkh0366FWJGqVLYBHev0vpHTrXSA0ryT+74UiW6KWsldNurQMKGqCm1M2zBciQ==", - "dev": true - }, "node_modules/uglify-js": { "version": "3.17.4", "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.17.4.tgz", @@ -30842,32 +20988,6 @@ "node": ">=0.8.0" } }, - "node_modules/unbox-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", - "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", - "dependencies": { - "call-bind": "^1.0.2", - "has-bigints": "^1.0.2", - "has-symbols": "^1.0.3", - "which-boxed-primitive": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/undici": { - "version": "6.6.2", - "resolved": "https://registry.npmjs.org/undici/-/undici-6.6.2.tgz", - "integrity": "sha512-vSqvUE5skSxQJ5sztTZ/CdeJb1Wq0Hf44hlYMciqHghvz+K88U0l7D6u1VsndoFgskDcnU+nG3gYmMzJVzd9Qg==", - "dev": true, - "dependencies": { - "@fastify/busboy": "^2.0.0" - }, - "engines": { - "node": ">=18.0" - } - }, "node_modules/undici-types": { "version": "5.26.5", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", @@ -30934,6 +21054,19 @@ "tiny-inflate": "^1.0.0" } }, + "node_modules/unicorn-magic": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.1.0.tgz", + "integrity": "sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==", + "dev": true, + "peer": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/unique-filename": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-3.0.0.tgz", @@ -30952,61 +21085,10 @@ "integrity": "sha512-WrcA6AyEfqDX5bWige/4NQfPZMtASNVxdmWR76WESYQVAACSgWcR6e9i0mofqqBxYFtL4oAxPIptY73/0YE1DQ==", "dev": true, "dependencies": { - "imurmurhash": "^0.1.4" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/unique-string": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", - "integrity": "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==", - "dev": true, - "dependencies": { - "crypto-random-string": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/unist-util-is": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-4.1.0.tgz", - "integrity": "sha512-ZOQSsnce92GrxSqlnEEseX0gi7GH9zTJZ0p9dtu87WRb/37mMPO2Ilx1s/t9vBHrFhbgweUwb+t7cIn5dxPhZg==", - "dev": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-visit": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-2.0.3.tgz", - "integrity": "sha512-iJ4/RczbJMkD0712mGktuGpm/U4By4FfDonL7N/9tATGIF4imikjOuagyMY53tnZq3NP6BcmlrHhEKAfGWjh7Q==", - "dev": true, - "dependencies": { - "@types/unist": "^2.0.0", - "unist-util-is": "^4.0.0", - "unist-util-visit-parents": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-visit-parents": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-3.1.1.tgz", - "integrity": "sha512-1KROIZWo6bcMrZEwiH2UrXDyalAa0uqzWCxCJj6lPOvTve2WkfgCytoDTPaMnodXh1WrXOq0haVYHj99ynJlsg==", - "dev": true, - "dependencies": { - "@types/unist": "^2.0.0", - "unist-util-is": "^4.0.0" + "imurmurhash": "^0.1.4" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/universalify": { @@ -31033,37 +21115,10 @@ "node": ">= 0.8" } }, - "node_modules/unplugin": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/unplugin/-/unplugin-1.7.1.tgz", - "integrity": "sha512-JqzORDAPxxs8ErLV4x+LL7bk5pk3YlcWqpSNsIkAZj972KzFZLClc/ekppahKkOczGkwIG6ElFgdOgOlK4tXZw==", - "dev": true, - "dependencies": { - "acorn": "^8.11.3", - "chokidar": "^3.5.3", - "webpack-sources": "^3.2.3", - "webpack-virtual-modules": "^0.6.1" - } - }, - "node_modules/unplugin/node_modules/webpack-virtual-modules": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/webpack-virtual-modules/-/webpack-virtual-modules-0.6.1.tgz", - "integrity": "sha512-poXpCylU7ExuvZK8z+On3kX+S8o/2dQ/SVYueKA0D4WEMXROXgY8Ez50/bQEUmvoSMMrWcrJqCHuhAbsiwg7Dg==", - "dev": true - }, - "node_modules/untildify": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz", - "integrity": "sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/update-browserslist-db": { - "version": "1.0.13", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", - "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.0.tgz", + "integrity": "sha512-EdRAaAyk2cUE1wOf2DkEhzxqOQvFOoRJFNS6NeyJ01Gp2beMRpBAINjM2iDXE3KCuKhwnvHIQCJm6ThL2Z+HzQ==", "dev": true, "funding": [ { @@ -31080,8 +21135,8 @@ } ], "dependencies": { - "escalade": "^3.1.1", - "picocolors": "^1.0.0" + "escalade": "^3.1.2", + "picocolors": "^1.0.1" }, "bin": { "update-browserslist-db": "cli.js" @@ -31094,18 +21149,9 @@ "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dependencies": { - "punycode": "^2.1.0" - } - }, - "node_modules/url": { - "version": "0.11.3", - "resolved": "https://registry.npmjs.org/url/-/url-0.11.3.tgz", - "integrity": "sha512-6hxOLGfZASQK/cijlZnZJTq8OXAkt/3YGfQX45vvMYXpZoo8NdWZcY73K108Jf759lS1Bv/8wXnHDTSz17dSRw==", "dev": true, "dependencies": { - "punycode": "^1.4.1", - "qs": "^6.11.2" + "punycode": "^2.1.0" } }, "node_modules/url-parse": { @@ -31118,68 +21164,6 @@ "requires-port": "^1.0.0" } }, - "node_modules/url/node_modules/punycode": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "integrity": "sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==", - "dev": true - }, - "node_modules/use-callback-ref": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.1.tgz", - "integrity": "sha512-Lg4Vx1XZQauB42Hw3kK7JM6yjVjgFmFC5/Ab797s79aARomD2nEErc4mCgM8EZrARLmmbWpi5DGCadmK50DcAQ==", - "dev": true, - "dependencies": { - "tslib": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/use-resize-observer": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/use-resize-observer/-/use-resize-observer-9.1.0.tgz", - "integrity": "sha512-R25VqO9Wb3asSD4eqtcxk8sJalvIOYBqS8MNZlpDSQ4l4xMQxC/J7Id9HoTqPq8FwULIn0PVW+OAqF2dyYbjow==", - "dev": true, - "dependencies": { - "@juggle/resize-observer": "^3.3.1" - }, - "peerDependencies": { - "react": "16.8.0 - 18", - "react-dom": "16.8.0 - 18" - } - }, - "node_modules/use-sidecar": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.2.tgz", - "integrity": "sha512-epTbsLuzZ7lPClpz2TyryBfztm7m+28DlEv2ZCQ3MDr5ssiwyOwGH/e5F9CkfWjJ1t4clvI58yF822/GUkjjhw==", - "dev": true, - "dependencies": { - "detect-node-es": "^1.1.0", - "tslib": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "@types/react": "^16.9.0 || ^17.0.0 || ^18.0.0", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, "node_modules/util": { "version": "0.12.5", "resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz", @@ -31202,7 +21186,9 @@ "version": "0.4.0", "resolved": "https://registry.npmjs.org/utila/-/utila-0.4.0.tgz", "integrity": "sha512-Z0DbgELS9/L/75wZbro8xAnT50pBVFQZ+hUEueGDU5FN51YSCYM+jdxsfCiHjwNP/4LCDD0i/graKpeBnOXKRA==", - "dev": true + "dev": true, + "optional": true, + "peer": true }, "node_modules/utils-merge": { "version": "1.0.1", @@ -31272,13 +21258,10 @@ } }, "node_modules/validate-npm-package-name": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-5.0.0.tgz", - "integrity": "sha512-YuKoXDAhBYxY7SfOKxHBDoSyENFeW5VvIIQp2TGQuit8gpK6MnWaQelBKxso72DoxTZfZdcP3W90LqpSkgPzLQ==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-5.0.1.tgz", + "integrity": "sha512-OljLrQ9SQdOUqTaQxqL5dEfZWrXExyyWsozYlAWFawPVNuD83igl7uJD2RTkNMbniIYgt8l81eCJGIdQF7avLQ==", "dev": true, - "dependencies": { - "builtins": "^5.0.0" - }, "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } @@ -31292,20 +21275,6 @@ "node": ">= 0.8" } }, - "node_modules/verror": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", - "integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==", - "dev": true, - "engines": [ - "node >=0.6.0" - ], - "dependencies": { - "assert-plus": "^1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "^1.2.0" - } - }, "node_modules/vis": { "version": "4.21.0-EOL", "resolved": "https://registry.npmjs.org/vis/-/vis-4.21.0-EOL.tgz", @@ -31321,14 +21290,14 @@ } }, "node_modules/vite": { - "version": "5.0.12", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.0.12.tgz", - "integrity": "sha512-4hsnEkG3q0N4Tzf1+t6NdN9dg/L3BM+q8SWgbSPnJvrgH2kgdyzfVJwbR1ic69/4uMJJ/3dqDZZE5/WwqW8U1w==", + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.0.tgz", + "integrity": "sha512-5xokfMX0PIiwCMCMb9ZJcMyh5wbBun0zUzKib+L65vAZ8GY9ePZMXxFrHbr/Kyll2+LSCY7xtERPpxkBDKngwg==", "dev": true, "dependencies": { - "esbuild": "^0.19.3", - "postcss": "^8.4.32", - "rollup": "^4.2.0" + "esbuild": "^0.21.3", + "postcss": "^8.4.40", + "rollup": "^4.13.0" }, "bin": { "vite": "bin/vite.js" @@ -31347,6 +21316,7 @@ "less": "*", "lightningcss": "^1.21.0", "sass": "*", + "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.4.0" @@ -31364,6 +21334,9 @@ "sass": { "optional": true }, + "sass-embedded": { + "optional": true + }, "stylus": { "optional": true }, @@ -31376,9 +21349,9 @@ } }, "node_modules/vite/node_modules/@esbuild/aix-ppc64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.19.12.tgz", - "integrity": "sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", "cpu": [ "ppc64" ], @@ -31392,9 +21365,9 @@ } }, "node_modules/vite/node_modules/@esbuild/android-arm": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.12.tgz", - "integrity": "sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", "cpu": [ "arm" ], @@ -31408,9 +21381,9 @@ } }, "node_modules/vite/node_modules/@esbuild/android-arm64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.12.tgz", - "integrity": "sha512-P0UVNGIienjZv3f5zq0DP3Nt2IE/3plFzuaS96vihvD0Hd6H/q4WXUGpCxD/E8YrSXfNyRPbpTq+T8ZQioSuPA==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", "cpu": [ "arm64" ], @@ -31424,9 +21397,9 @@ } }, "node_modules/vite/node_modules/@esbuild/android-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.12.tgz", - "integrity": "sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", "cpu": [ "x64" ], @@ -31440,9 +21413,9 @@ } }, "node_modules/vite/node_modules/@esbuild/darwin-arm64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.12.tgz", - "integrity": "sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", "cpu": [ "arm64" ], @@ -31456,9 +21429,9 @@ } }, "node_modules/vite/node_modules/@esbuild/darwin-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.12.tgz", - "integrity": "sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", "cpu": [ "x64" ], @@ -31472,9 +21445,9 @@ } }, "node_modules/vite/node_modules/@esbuild/freebsd-arm64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.12.tgz", - "integrity": "sha512-4aRvFIXmwAcDBw9AueDQ2YnGmz5L6obe5kmPT8Vd+/+x/JMVKCgdcRwH6APrbpNXsPz+K653Qg8HB/oXvXVukA==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", "cpu": [ "arm64" ], @@ -31488,9 +21461,9 @@ } }, "node_modules/vite/node_modules/@esbuild/freebsd-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.12.tgz", - "integrity": "sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", "cpu": [ "x64" ], @@ -31504,9 +21477,9 @@ } }, "node_modules/vite/node_modules/@esbuild/linux-arm": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.12.tgz", - "integrity": "sha512-J5jPms//KhSNv+LO1S1TX1UWp1ucM6N6XuL6ITdKWElCu8wXP72l9MM0zDTzzeikVyqFE6U8YAV9/tFyj0ti+w==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", "cpu": [ "arm" ], @@ -31520,9 +21493,9 @@ } }, "node_modules/vite/node_modules/@esbuild/linux-arm64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.12.tgz", - "integrity": "sha512-EoTjyYyLuVPfdPLsGVVVC8a0p1BFFvtpQDB/YLEhaXyf/5bczaGeN15QkR+O4S5LeJ92Tqotve7i1jn35qwvdA==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", "cpu": [ "arm64" ], @@ -31536,9 +21509,9 @@ } }, "node_modules/vite/node_modules/@esbuild/linux-ia32": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.12.tgz", - "integrity": "sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", "cpu": [ "ia32" ], @@ -31552,9 +21525,9 @@ } }, "node_modules/vite/node_modules/@esbuild/linux-loong64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.12.tgz", - "integrity": "sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", "cpu": [ "loong64" ], @@ -31568,9 +21541,9 @@ } }, "node_modules/vite/node_modules/@esbuild/linux-mips64el": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.12.tgz", - "integrity": "sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", "cpu": [ "mips64el" ], @@ -31584,9 +21557,9 @@ } }, "node_modules/vite/node_modules/@esbuild/linux-ppc64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.12.tgz", - "integrity": "sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", "cpu": [ "ppc64" ], @@ -31600,9 +21573,9 @@ } }, "node_modules/vite/node_modules/@esbuild/linux-riscv64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.12.tgz", - "integrity": "sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", "cpu": [ "riscv64" ], @@ -31616,9 +21589,9 @@ } }, "node_modules/vite/node_modules/@esbuild/linux-s390x": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.12.tgz", - "integrity": "sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", "cpu": [ "s390x" ], @@ -31632,9 +21605,9 @@ } }, "node_modules/vite/node_modules/@esbuild/linux-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.12.tgz", - "integrity": "sha512-B71g1QpxfwBvNrfyJdVDexenDIt1CiDN1TIXLbhOw0KhJzE78KIFGX6OJ9MrtC0oOqMWf+0xop4qEU8JrJTwCg==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", "cpu": [ "x64" ], @@ -31648,9 +21621,9 @@ } }, "node_modules/vite/node_modules/@esbuild/netbsd-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.12.tgz", - "integrity": "sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", "cpu": [ "x64" ], @@ -31664,9 +21637,9 @@ } }, "node_modules/vite/node_modules/@esbuild/openbsd-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.12.tgz", - "integrity": "sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", "cpu": [ "x64" ], @@ -31680,9 +21653,9 @@ } }, "node_modules/vite/node_modules/@esbuild/sunos-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.12.tgz", - "integrity": "sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", "cpu": [ "x64" ], @@ -31696,9 +21669,9 @@ } }, "node_modules/vite/node_modules/@esbuild/win32-arm64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.12.tgz", - "integrity": "sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", "cpu": [ "arm64" ], @@ -31712,9 +21685,9 @@ } }, "node_modules/vite/node_modules/@esbuild/win32-ia32": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.12.tgz", - "integrity": "sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", "cpu": [ "ia32" ], @@ -31728,9 +21701,9 @@ } }, "node_modules/vite/node_modules/@esbuild/win32-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.12.tgz", - "integrity": "sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", "cpu": [ "x64" ], @@ -31744,9 +21717,9 @@ } }, "node_modules/vite/node_modules/esbuild": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.12.tgz", - "integrity": "sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", "dev": true, "hasInstallScript": true, "bin": { @@ -31756,29 +21729,29 @@ "node": ">=12" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.19.12", - "@esbuild/android-arm": "0.19.12", - "@esbuild/android-arm64": "0.19.12", - "@esbuild/android-x64": "0.19.12", - "@esbuild/darwin-arm64": "0.19.12", - "@esbuild/darwin-x64": "0.19.12", - "@esbuild/freebsd-arm64": "0.19.12", - "@esbuild/freebsd-x64": "0.19.12", - "@esbuild/linux-arm": "0.19.12", - "@esbuild/linux-arm64": "0.19.12", - "@esbuild/linux-ia32": "0.19.12", - "@esbuild/linux-loong64": "0.19.12", - "@esbuild/linux-mips64el": "0.19.12", - "@esbuild/linux-ppc64": "0.19.12", - "@esbuild/linux-riscv64": "0.19.12", - "@esbuild/linux-s390x": "0.19.12", - "@esbuild/linux-x64": "0.19.12", - "@esbuild/netbsd-x64": "0.19.12", - "@esbuild/openbsd-x64": "0.19.12", - "@esbuild/sunos-x64": "0.19.12", - "@esbuild/win32-arm64": "0.19.12", - "@esbuild/win32-ia32": "0.19.12", - "@esbuild/win32-x64": "0.19.12" + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" } }, "node_modules/w3c-xmlserializer": { @@ -31803,9 +21776,9 @@ } }, "node_modules/watchpack": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz", - "integrity": "sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.1.tgz", + "integrity": "sha512-8wrBCMtVhqcXP2Sup1ctSkga6uc2Bx0IIvKyT7yTFier5AXHooSI+QyQQAtTb7+E0IUCCKyTFmXqdqgum2XWGg==", "dev": true, "dependencies": { "glob-to-regexp": "^0.4.1", @@ -31820,6 +21793,7 @@ "resolved": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz", "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==", "dev": true, + "peer": true, "dependencies": { "minimalistic-assert": "^1.0.0" } @@ -31833,6 +21807,12 @@ "defaults": "^1.0.3" } }, + "node_modules/weak-lru-cache": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/weak-lru-cache/-/weak-lru-cache-1.2.2.tgz", + "integrity": "sha512-DEAoo25RfSYMuTGc9vPJzZcZullwIqRDSI9LOy+fkCJPi6hykCnfKaXTuPBDuXAUcqHXyOgFtHNp/kB2FjYHbw==", + "dev": true + }, "node_modules/webidl-conversions": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", @@ -31843,26 +21823,27 @@ } }, "node_modules/webpack": { - "version": "5.90.3", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.90.3.tgz", - "integrity": "sha512-h6uDYlWCctQRuXBs1oYpVe6sFcWedl0dpcVaTf/YF67J9bKvwJajFulMVSYKHrksMB3I/pIagRzDxwxkebuzKA==", + "version": "5.93.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.93.0.tgz", + "integrity": "sha512-Y0m5oEY1LRuwly578VqluorkXbvXKh7U3rLoQCEO04M97ScRr44afGVkI0FQFsXzysk5OgFAxjZAb9rsGQVihA==", "dev": true, + "peer": true, "dependencies": { "@types/eslint-scope": "^3.7.3", "@types/estree": "^1.0.5", - "@webassemblyjs/ast": "^1.11.5", - "@webassemblyjs/wasm-edit": "^1.11.5", - "@webassemblyjs/wasm-parser": "^1.11.5", + "@webassemblyjs/ast": "^1.12.1", + "@webassemblyjs/wasm-edit": "^1.12.1", + "@webassemblyjs/wasm-parser": "^1.12.1", "acorn": "^8.7.1", - "acorn-import-assertions": "^1.9.0", + "acorn-import-attributes": "^1.9.5", "browserslist": "^4.21.10", "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.15.0", + "enhanced-resolve": "^5.17.0", "es-module-lexer": "^1.2.1", "eslint-scope": "5.1.1", "events": "^3.2.0", "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.2.9", + "graceful-fs": "^4.2.11", "json-parse-even-better-errors": "^2.3.1", "loader-runner": "^4.2.0", "mime-types": "^2.1.27", @@ -31870,7 +21851,7 @@ "schema-utils": "^3.2.0", "tapable": "^2.1.1", "terser-webpack-plugin": "^5.3.10", - "watchpack": "^2.4.0", + "watchpack": "^2.4.1", "webpack-sources": "^3.2.3" }, "bin": { @@ -31890,19 +21871,21 @@ } }, "node_modules/webpack-dev-middleware": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-6.1.1.tgz", - "integrity": "sha512-y51HrHaFeeWir0YO4f0g+9GwZawuigzcAdRNon6jErXy/SqV/+O6eaVAzDqE6t3e3NpGeR5CS+cCDaTC+V3yEQ==", + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-7.3.0.tgz", + "integrity": "sha512-xD2qnNew+F6KwOGZR7kWdbIou/ud7cVqLEXeK1q0nHcNsX/u7ul/fSdlOTX4ntSL5FNFy7ZJJXbf0piF591JYw==", "dev": true, + "peer": true, "dependencies": { "colorette": "^2.0.10", - "memfs": "^3.4.12", + "memfs": "^4.6.0", "mime-types": "^2.1.31", + "on-finished": "^2.4.1", "range-parser": "^1.2.1", "schema-utils": "^4.0.0" }, "engines": { - "node": ">= 14.15.0" + "node": ">= 18.12.0" }, "funding": { "type": "opencollective", @@ -31918,54 +21901,55 @@ } }, "node_modules/webpack-dev-server": { - "version": "4.15.1", - "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.15.1.tgz", - "integrity": "sha512-5hbAst3h3C3L8w6W4P96L5vaV0PxSmJhxZvWKYIdgxOQm8pNZ5dEOmmSLBVpP85ReeyRt6AS1QJNyo/oFFPeVA==", - "dev": true, - "dependencies": { - "@types/bonjour": "^3.5.9", - "@types/connect-history-api-fallback": "^1.3.5", - "@types/express": "^4.17.13", - "@types/serve-index": "^1.9.1", - "@types/serve-static": "^1.13.10", - "@types/sockjs": "^0.3.33", - "@types/ws": "^8.5.5", + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-5.0.4.tgz", + "integrity": "sha512-dljXhUgx3HqKP2d8J/fUMvhxGhzjeNVarDLcbO/EWMSgRizDkxHQDZQaLFL5VJY9tRBj2Gz+rvCEYYvhbqPHNA==", + "dev": true, + "peer": true, + "dependencies": { + "@types/bonjour": "^3.5.13", + "@types/connect-history-api-fallback": "^1.5.4", + "@types/express": "^4.17.21", + "@types/serve-index": "^1.9.4", + "@types/serve-static": "^1.15.5", + "@types/sockjs": "^0.3.36", + "@types/ws": "^8.5.10", "ansi-html-community": "^0.0.8", - "bonjour-service": "^1.0.11", - "chokidar": "^3.5.3", + "bonjour-service": "^1.2.1", + "chokidar": "^3.6.0", "colorette": "^2.0.10", "compression": "^1.7.4", "connect-history-api-fallback": "^2.0.0", "default-gateway": "^6.0.3", "express": "^4.17.3", "graceful-fs": "^4.2.6", - "html-entities": "^2.3.2", + "html-entities": "^2.4.0", "http-proxy-middleware": "^2.0.3", - "ipaddr.js": "^2.0.1", - "launch-editor": "^2.6.0", - "open": "^8.0.9", - "p-retry": "^4.5.0", - "rimraf": "^3.0.2", - "schema-utils": "^4.0.0", - "selfsigned": "^2.1.1", + "ipaddr.js": "^2.1.0", + "launch-editor": "^2.6.1", + "open": "^10.0.3", + "p-retry": "^6.2.0", + "rimraf": "^5.0.5", + "schema-utils": "^4.2.0", + "selfsigned": "^2.4.1", "serve-index": "^1.9.1", "sockjs": "^0.3.24", "spdy": "^4.0.2", - "webpack-dev-middleware": "^5.3.1", - "ws": "^8.13.0" + "webpack-dev-middleware": "^7.1.0", + "ws": "^8.16.0" }, "bin": { "webpack-dev-server": "bin/webpack-dev-server.js" }, "engines": { - "node": ">= 12.13.0" + "node": ">= 18.12.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/webpack" }, "peerDependencies": { - "webpack": "^4.37.0 || ^5.0.0" + "webpack": "^5.0.0" }, "peerDependenciesMeta": { "webpack": { @@ -31976,61 +21960,54 @@ } } }, - "node_modules/webpack-dev-server/node_modules/ipaddr.js": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.1.0.tgz", - "integrity": "sha512-LlbxQ7xKzfBusov6UMi4MFpEg0m+mAm9xyNGEduwXMEDuf4WfzB/RZwMVYEd7IKGvh4IUkEXYxtAVu9T3OelJQ==", - "dev": true, - "engines": { - "node": ">= 10" - } - }, - "node_modules/webpack-dev-server/node_modules/webpack-dev-middleware": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.3.tgz", - "integrity": "sha512-hj5CYrY0bZLB+eTO+x/j67Pkrquiy7kWepMHmUMoPsmcUaeEnQJqFzHJOyxgWlq746/wUuA64p9ta34Kyb01pA==", + "node_modules/webpack-dev-server/node_modules/http-proxy-middleware": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.6.tgz", + "integrity": "sha512-ya/UeJ6HVBYxrgYotAZo1KvPWlgB48kUJLDePFeneHsVujFaW5WNj2NgWCAE//B1Dl02BIfYlpNgBy8Kf8Rjmw==", "dev": true, + "peer": true, "dependencies": { - "colorette": "^2.0.10", - "memfs": "^3.4.3", - "mime-types": "^2.1.31", - "range-parser": "^1.2.1", - "schema-utils": "^4.0.0" + "@types/http-proxy": "^1.17.8", + "http-proxy": "^1.18.1", + "is-glob": "^4.0.1", + "is-plain-obj": "^3.0.0", + "micromatch": "^4.0.2" }, "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" + "node": ">=12.0.0" }, "peerDependencies": { - "webpack": "^4.0.0 || ^5.0.0" + "@types/express": "^4.17.13" + }, + "peerDependenciesMeta": { + "@types/express": { + "optional": true + } } }, - "node_modules/webpack-hot-middleware": { - "version": "2.26.1", - "resolved": "https://registry.npmjs.org/webpack-hot-middleware/-/webpack-hot-middleware-2.26.1.tgz", - "integrity": "sha512-khZGfAeJx6I8K9zKohEWWYN6KDlVw2DHownoe+6Vtwj1LP9WFgegXnVMSkZ/dBEBtXFwrkkydsaPFlB7f8wU2A==", + "node_modules/webpack-dev-server/node_modules/ipaddr.js": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.2.0.tgz", + "integrity": "sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA==", "dev": true, - "dependencies": { - "ansi-html-community": "0.0.8", - "html-entities": "^2.1.0", - "strip-ansi": "^6.0.0" + "peer": true, + "engines": { + "node": ">= 10" } }, "node_modules/webpack-merge": { - "version": "5.10.0", - "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.10.0.tgz", - "integrity": "sha512-+4zXKdx7UnO+1jaN4l2lHVD+mFvnlZQP/6ljaJVb4SZiwIKeUnrT5l0gkT8z+n4hKpC+jpOv6O9R+gLtag7pSA==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-6.0.1.tgz", + "integrity": "sha512-hXXvrjtx2PLYx4qruKl+kyRSLc52V+cCvMxRjmKwoA+CBbbF5GfIBtR6kCvl0fYGqTUPKB+1ktVmTHqMOzgCBg==", "dev": true, + "peer": true, "dependencies": { "clone-deep": "^4.0.1", "flat": "^5.0.2", - "wildcard": "^2.0.0" + "wildcard": "^2.0.1" }, "engines": { - "node": ">=10.0.0" + "node": ">=18.0.0" } }, "node_modules/webpack-sources": { @@ -32038,6 +22015,7 @@ "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", "dev": true, + "peer": true, "engines": { "node": ">=10.13.0" } @@ -32047,6 +22025,7 @@ "resolved": "https://registry.npmjs.org/webpack-subresource-integrity/-/webpack-subresource-integrity-5.1.0.tgz", "integrity": "sha512-sacXoX+xd8r4WKsy9MvH/q/vBtEHr86cpImXwyg74pFIpERKt6FmB8cXpeuh0ZLgclOlHI4Wcll7+R5L02xk9Q==", "dev": true, + "peer": true, "dependencies": { "typed-assert": "^1.0.8" }, @@ -32063,17 +22042,12 @@ } } }, - "node_modules/webpack-virtual-modules": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/webpack-virtual-modules/-/webpack-virtual-modules-0.5.0.tgz", - "integrity": "sha512-kyDivFZ7ZM0BVOUteVbDFhlRt7Ah/CSPwJdi8hBpkK7QLumUqdLtVfm/PX/hkcnrvr0i77fO5+TjZ94Pe+C9iw==", - "dev": true - }, "node_modules/webpack/node_modules/ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -32090,6 +22064,7 @@ "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", "dev": true, + "peer": true, "peerDependencies": { "ajv": "^6.9.1" } @@ -32099,6 +22074,7 @@ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", "dev": true, + "peer": true, "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^4.1.1" @@ -32112,6 +22088,7 @@ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", "dev": true, + "peer": true, "engines": { "node": ">=4.0" } @@ -32120,19 +22097,22 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "dev": true + "dev": true, + "peer": true }, "node_modules/webpack/node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true + "dev": true, + "peer": true }, "node_modules/webpack/node_modules/schema-utils": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", "dev": true, + "peer": true, "dependencies": { "@types/json-schema": "^7.0.8", "ajv": "^6.12.5", @@ -32219,6 +22199,7 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, "dependencies": { "isexe": "^2.0.0" }, @@ -32229,69 +22210,6 @@ "node": ">= 8" } }, - "node_modules/which-boxed-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", - "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", - "dependencies": { - "is-bigint": "^1.0.1", - "is-boolean-object": "^1.1.0", - "is-number-object": "^1.0.4", - "is-string": "^1.0.5", - "is-symbol": "^1.0.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/which-builtin-type": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.1.3.tgz", - "integrity": "sha512-YmjsSMDBYsM1CaFiayOVT06+KJeXf0o5M/CAd4o1lTadFAtacTUM49zoYxr/oroopFDfhvN6iEcBxUyc3gvKmw==", - "dev": true, - "peer": true, - "dependencies": { - "function.prototype.name": "^1.1.5", - "has-tostringtag": "^1.0.0", - "is-async-function": "^2.0.0", - "is-date-object": "^1.0.5", - "is-finalizationregistry": "^1.0.2", - "is-generator-function": "^1.0.10", - "is-regex": "^1.1.4", - "is-weakref": "^1.0.2", - "isarray": "^2.0.5", - "which-boxed-primitive": "^1.0.2", - "which-collection": "^1.0.1", - "which-typed-array": "^1.1.9" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/which-collection": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.1.tgz", - "integrity": "sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A==", - "dev": true, - "dependencies": { - "is-map": "^2.0.1", - "is-set": "^2.0.1", - "is-weakmap": "^2.0.1", - "is-weakset": "^2.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/which-module": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz", - "integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==", - "dev": true - }, "node_modules/which-typed-array": { "version": "1.1.14", "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.14.tgz", @@ -32314,7 +22232,8 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.1.tgz", "integrity": "sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ==", - "dev": true + "dev": true, + "peer": true }, "node_modules/windows-release": { "version": "4.0.0", @@ -32393,13 +22312,6 @@ "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", "dev": true }, - "node_modules/workerpool": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz", - "integrity": "sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==", - "dev": true, - "peer": true - }, "node_modules/wrap-ansi": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", @@ -32465,7 +22377,8 @@ "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true }, "node_modules/write-file-atomic": { "version": "4.0.2", @@ -32555,15 +22468,6 @@ "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", "dev": true }, - "node_modules/yaml": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", - "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", - "dev": true, - "engines": { - "node": ">= 6" - } - }, "node_modules/yargs": { "version": "17.7.2", "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", @@ -32591,42 +22495,6 @@ "node": ">=10" } }, - "node_modules/yargs-unparser": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", - "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", - "dev": true, - "dependencies": { - "camelcase": "^6.0.0", - "decamelize": "^4.0.0", - "flat": "^5.0.2", - "is-plain-obj": "^2.1.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/yargs-unparser/node_modules/camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/yargs-unparser/node_modules/is-plain-obj": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", - "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/yargs/node_modules/yargs-parser": { "version": "21.1.1", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", @@ -32636,16 +22504,6 @@ "node": ">=12" } }, - "node_modules/yauzl": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", - "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", - "dev": true, - "dependencies": { - "buffer-crc32": "~0.2.3", - "fd-slicer": "~1.1.0" - } - }, "node_modules/yn": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", @@ -32659,6 +22517,7 @@ "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, "engines": { "node": ">=10" }, @@ -32666,6 +22525,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/yoctocolors-cjs": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yoctocolors-cjs/-/yoctocolors-cjs-2.1.2.tgz", + "integrity": "sha512-cYVsTjKl8b+FrnidjibDWskAv7UKOfcwaVZdp/it9n1s9fU3IkgDbhdIRKCW4JDsAlECJY0ytoVPT3sK6kideA==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/zepto": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/zepto/-/zepto-1.2.0.tgz", @@ -32673,12 +22544,9 @@ "dev": true }, "node_modules/zone.js": { - "version": "0.14.4", - "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.14.4.tgz", - "integrity": "sha512-NtTUvIlNELez7Q1DzKVIFZBzNb646boQMgpATo9z3Ftuu/gWvzxCW7jdjcUDoRGxRikrhVHB/zLXh1hxeJawvw==", - "dependencies": { - "tslib": "^2.3.0" - } + "version": "0.14.10", + "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.14.10.tgz", + "integrity": "sha512-YGAhaO7J5ywOXW6InXNlLmfU194F8lVgu7bRntUF3TiG8Y3nBK0x1UJJuHUP/e8IyihkjCYqhCScpSwnlaSRkQ==" } } } diff --git a/package.json b/package.json index 01bfaf4df4..62203937c6 100644 --- a/package.json +++ b/package.json @@ -1,11 +1,11 @@ { "name": "cvs-app-vtm", - "version": "1.20", + "version": "1.25", "description": "DVSA CVS Vehicle Testing Management Application", "main": "index.js", "engines": { - "node": "18.18.0", - "npm": "9.8.1" + "node": "20.16.*", + "npm": "10.8.*" }, "scripts": { "ng": "ng", @@ -13,118 +13,89 @@ "start": "npm run config -- --environment=dev && ng serve", "start:dev1": "npm run config -- --env=dev1 && ng serve", "start:dev2": "npm run config -- --env=dev2 && ng serve", - "build": "ng build --build-optimizer --delete-output-path --optimization --progress --vendor-chunk --aot", + "build": "ng build --optimization --progress --aot", "postbuild": "npm run sentry:sourcemaps", "watch": "ng build --watch", "sonar-scanner": "npm run test:ci && sonar-scanner", "security-checks": "git secrets --scan", - "lint": "eslint --ext .ts .", - "lint:fix": "eslint --ext .ts . --fix", - "format": "npx prettier '!src/app/api/**/*.{js,ts}' 'src/**/*.{js,jsx,ts,tsx,html,css,scss}' --write", + "lint": "biome check src", + "lint:fix": "npm run lint -- --write", "docs:json": "compodoc -p ./tsconfig.json -e json -d .", - "storybook": "npm run docs:json && storybook dev -p 6006", - "build-storybook": "npm run docs:json && storybook build", "test": "jest --colors --maxWorkers=50% --coverage", "test:workflow": "jest --maxWorkers=50%", "test:watch": "jest --watch", "test:ci": "jest --ci --runInBand --watchAll=false --reporters=default", "mock-api": "npx ts-node ./mock-api/mock-api-server.ts", - "prepare": "husky install && ts-patch install -s", - "e2e": "npm run config && cypress run", - "e2e:open": "npm run config && cypress open", - "e2e:parallel": "cypress-parallel -s e2e -t 3 -d cypress/e2e", + "prepare": "husky && ts-patch install -s", "sentry:sourcemaps": "sentry-cli sourcemaps inject --org driver-and-vehicle-standards-a --project vehicle-test-management ./dist && sentry-cli sourcemaps upload --org driver-and-vehicle-standards-a --project vehicle-test-management ./dist" }, "private": true, "dependencies": { - "@angular/animations": "^17.2.1", - "@angular/common": "^17.2.1", - "@angular/compiler": "^17.2.1", - "@angular/core": "^17.2.1", - "@angular/forms": "^17.2.1", - "@angular/platform-browser": "^17.2.1", - "@angular/platform-browser-dynamic": "^17.2.1", - "@angular/router": "^17.2.1", - "@azure/msal-angular": "^3.0.13", + "@angular/animations": "^18.2.0", + "@angular/common": "^18.2.0", + "@angular/compiler": "^18.2.0", + "@angular/core": "^18.2.0", + "@angular/forms": "^18.2.0", + "@angular/platform-browser": "^18.2.0", + "@angular/platform-browser-dynamic": "^18.2.0", + "@angular/router": "^18.2.0", + "@azure/msal-angular": "^3.0.20", "@azure/msal-browser": "^3.10.0", "@dvsa/cvs-type-definitions": "^6.3.0", - "@ngrx/effects": "^17.1.0", - "@ngrx/entity": "^17.1.0", - "@ngrx/router-store": "^17.1.0", - "@ngrx/store": "^17.1.0", - "@ngrx/store-devtools": "^17.1.0", - "@sentry/angular-ivy": "^7.102.0", - "@sentry/cli": "^2.28.6", - "@sentry/types": "^7.102.0", + "@ngrx/effects": "^18.0.2", + "@ngrx/entity": "^18.0.2", + "@ngrx/operators": "^18.0.0", + "@ngrx/router-store": "^18.0.2", + "@ngrx/store": "^18.0.2", + "@ngrx/store-devtools": "^18.0.2", + "@sentry/angular": "^8.26.0", + "@sentry/types": "^8.26.0", "accessible-autocomplete": "^2.0.4", + "angular-google-tag-manager": "^1.10.0", "deep-object-diff": "^1.1.9", - "eslint-import-resolver-typescript": "^3.6.1", "govuk-frontend": "^4.7.0", "jwt-decode": "^4.0.0", "lodash": "^4.17.21", "lodash.clonedeep": "^4.5.0", "lodash.merge": "^4.6.2", - "ngrx-store-localstorage": "17.0.0", + "ngrx-store-localstorage": "18.0.0", "rxjs": "^7.8.1", "tslib": "^2.6.2", "uuid": "^9.0.1", "validate-govuk-date": "^1.0.2", - "zone.js": "^0.14.4" + "zone.js": "^0.14.10" }, "devDependencies": { - "@angular-devkit/build-angular": "^17.2.0", - "@angular-eslint/builder": "^17.2.1", - "@angular-eslint/eslint-plugin": "^17.2.1", - "@angular-eslint/eslint-plugin-template": "^17.2.1", - "@angular-eslint/schematics": "^17.2.1", - "@angular-eslint/template-parser": "^17.2.1", - "@angular/cli": "^17.2.0", - "@angular/compiler-cli": "^17.2.1", + "@angular/build": "^18.2.0", + "@angular/cli": "^18.2.0", + "@angular/compiler-cli": "^18.2.0", "@babel/core": "^7.23.9", + "@biomejs/biome": "1.8.3", "@commitlint/cli": "^18.6.1", "@commitlint/config-conventional": "^18.6.2", "@compodoc/compodoc": "^1.1.23", - "@dvsa/eslint-config-ts": "^3.0.1", - "@ngrx/schematics": "^17.1.0", - "@storybook/addon-actions": "^7.6.17", - "@storybook/addon-controls": "^7.6.17", - "@storybook/addon-essentials": "^7.6.17", - "@storybook/addon-interactions": "^7.6.17", - "@storybook/addon-links": "^7.6.17", - "@storybook/angular": "^7.6.17", - "@storybook/cli": "^7.6.17", - "@storybook/testing-library": "^0.2.2", + "@dvsa/biome-config": "^0.1.0", + "@ngrx/schematics": "^18.0.2", + "@sentry/cli": "^2.32.2", "@types/jest": "^29.5.12", "@types/json-server": "^0.14.7", "@types/lodash.clonedeep": "^4.5.9", "@types/lodash.merge": "^4.6.9", - "@types/node": "^18.18.0", + "@types/node": "^20.14.8", "@types/uuid": "^9.0.8", - "@typescript-eslint/eslint-plugin": "^6.0.0", - "@typescript-eslint/parser": "^6.0.0", "babel-loader": "^9.1.3", "commitlint-plugin-function-rules": "^3.1.0", - "cypress": "^13.6.5", - "cypress-parallel": "^0.14.0", "dotenv": "^16.4.5", - "eslint": "^8.56.0", - "eslint-config-prettier": "^9.1.0", - "eslint-plugin-import": "^2.29.1", - "eslint-plugin-ngrx": "^2.1.4", - "eslint-plugin-prettier": "^5.1.3", - "eslint-plugin-storybook": "^0.8.0", "husky": "^9.0.11", "jest": "^29.7.0", - "jest-preset-angular": "^14.0.3", + "jest-preset-angular": "^14.2.2", "jest-sonar-reporter": "^2.0.0", "json-server": "^0.17.3", - "prettier": "^3.2.5", "sonarqube-scanner": "^3.3.0", - "storybook": "^7.6.17", "ts-jest": "^29.1.2", "ts-node": "^10.9.2", "ts-patch": "^3.1.2", - "typescript": "^5.3.3", + "typescript": "^5.5.4", "yargs": "^17.7.2" }, "optionalDependencies": { diff --git a/setenv.ts b/setenv.ts index ae4afcaa5c..0ce16acde0 100644 --- a/setenv.ts +++ b/setenv.ts @@ -12,7 +12,6 @@ require('dotenv').config({ const environment = argv.environment; const isProduction = environment === 'prod'; const targetPath = isProduction ? `./src/environments/environment.prod.ts` : `./src/environments/environment.deploy.ts`; -const cypressPath = 'cypress.env.json'; // we have access to our environment variables // in the process.env object thanks to dotenv @@ -34,20 +33,10 @@ const environmentFileContent = `export const environment = { }; `; -const cypressCredsFile = JSON.stringify({ - aad_username: process.env['AAD_USER'], - aad_password: process.env['AAD_PASSWORD'], - vtm_api_uri: process.env['VTM_API_URI'] -}); - const filesToWrite = [ { path: targetPath, contents: environmentFileContent - }, - { - path: cypressPath, - contents: cypressCredsFile } ]; diff --git a/sonar-project.properties b/sonar-project.properties index dba1bc6a03..35a3bdbbd8 100644 --- a/sonar-project.properties +++ b/sonar-project.properties @@ -13,7 +13,7 @@ sonar.sourceEncoding=UTF-8 # Path is relative to the sonar-project.properties file. Replace “\” by “/” on Windows. # This property is optional if sonar.modules is set. sonar.sources=src/app -sonar.exclusions=test-config/**, cypress/**, dist/**, .nyc_output/**, .scannerwork/**, coverage/**, **/*.module.ts, **/*.routes.ts, **/*.state.ts, **/utils.ts, src/app/api/** +sonar.exclusions=test-config/**, dist/**, .nyc_output/**, .scannerwork/**, coverage/**, **/*.module.ts, **/*.routes.ts, **/*.state.ts, **/utils.ts, src/app/api/** sonar.test.inclusions=**/*.spec.ts sonar.cpd.exclusions=src/app/forms/templates/**, src/app/api/** sonar.tslint.reportPaths=.reports/lint_issues.json diff --git a/src/app/accessible-autocomplete.d.ts b/src/app/accessible-autocomplete.d.ts index d0950ffeaf..8975a41770 100644 --- a/src/app/accessible-autocomplete.d.ts +++ b/src/app/accessible-autocomplete.d.ts @@ -1,25 +1,25 @@ declare module 'accessible-autocomplete/dist/accessible-autocomplete.min' { - interface ElementParam { - element: HTMLElement; - } - interface EnhancedElementParam { - selectElement: HTMLElement | null; - } - interface DefaultParams { - id?: string; - source?: Array; - onConfirm?: (value: string) => void; - confirmOnBlur?: boolean; - required?: true; - defaultValue?: string; - autoselect?: boolean; - showAllValues?: boolean; - dropdownArrow?: Function; - } + interface ElementParam { + element: HTMLElement; + } + interface EnhancedElementParam { + selectElement: HTMLElement | null; + } + interface DefaultParams { + id?: string; + source?: Array; + onConfirm?: (value: string) => void; + confirmOnBlur?: boolean; + required?: true; + defaultValue?: string; + autoselect?: boolean; + showAllValues?: boolean; + dropdownArrow?: Function; + } - export interface AutocompleteParams extends DefaultParams, ElementParam {} - export interface AutocompleteEnhanceParams extends DefaultParams, EnhancedElementParam {} + export interface AutocompleteParams extends DefaultParams, ElementParam {} + export interface AutocompleteEnhanceParams extends DefaultParams, EnhancedElementParam {} - export default function accesibleAutocomplete(params: AutocompleteParams): void; - export function enhanceSelectElement(params: AutocompleteEnhanceParams): void; + export default function accesibleAutocomplete(params: AutocompleteParams): void; + export function enhanceSelectElement(params: AutocompleteEnhanceParams): void; } diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts index 3f609e8a19..cd088077ed 100644 --- a/src/app/app-routing.module.ts +++ b/src/app/app-routing.module.ts @@ -12,76 +12,81 @@ import { techRecordViewResolver } from './resolvers/tech-record-view/tech-record import { titleResolver } from './resolvers/title/title.resolver'; const routes: Routes = [ - { - path: RootRoutes.ROOT, - resolve: { title: titleResolver }, - children: [ - { - path: RootRoutes.ROOT, - data: { title: 'Home', roles: Roles.TechRecordView }, - canActivate: [MsalGuard, RoleGuard], - canDeactivate: [CancelEditTechGuard], - loadChildren: () => import('./features/home/home.module').then((m) => m.HomeModule), - }, - { - path: RootRoutes.SEARCH_TECHNICAL_RECORD, - data: { title: 'Technical record search', roles: Roles.TechRecordView }, - canActivate: [MsalGuard, RoleGuard], - canDeactivate: [CancelEditTechGuard], - loadChildren: () => import('./features/search/search.module').then((m) => m.SearchModule), - }, - { - path: RootRoutes.CREATE_TECHNICAL_RECORD, - data: { title: 'Create new technical record', roles: Roles.TechRecordCreate }, - canActivate: [MsalGuard, RoleGuard], - loadChildren: () => import('./features/tech-record/create/create-tech-records.module').then((m) => m.CreateTechRecordsModule), - }, - { - path: RootRoutes.BATCH_CREATE_TECHNICAL_RECORD, - data: { title: 'Select Vehicle Type', roles: Roles.TechRecordCreate }, - canActivate: [MsalGuard, RoleGuard], - loadChildren: () => import('./features/tech-record/create-batch/create-batch.module').then((m) => m.CreateBatchModule), - }, - { - path: RootRoutes.CURRENT_TEST_RESULT, - data: { title: 'Test Result', roles: Roles.TestResultView }, - canActivate: [MsalGuard, RoleGuard], - resolve: { techRecord: techRecordViewResolver }, - loadChildren: () => import('./features/test-records/amend/amend-test-records.module').then((m) => m.AmendTestRecordsModule), - }, - { - path: RootRoutes.CURRENT_TECH_RECORD, - data: { title: 'Tech Record', roles: Roles.TechRecordView }, - canActivate: [MsalGuard, RoleGuard], - loadChildren: () => import('./features/tech-record/tech-record.module').then((m) => m.TechRecordsModule), - }, - { - path: RootRoutes.REFERENCE_DATA, - data: { title: 'Select Reference Data Type', roles: Roles.ReferenceDataView }, - canActivate: [MsalGuard, RoleGuard], - loadChildren: () => import('./features/reference-data/reference-data.module').then((m) => m.ReferenceDataModule), - }, - { - path: RootRoutes.FEATURE_TOGGLE, - data: { title: 'Feature Toggle', featureToggleName: 'testToggle' }, - canActivate: [MsalGuard, FeatureToggleGuard], - loadChildren: () => import('./features/feature-toggle/feature-toggle.module').then((m) => m.FeatureToggleModule), - }, - { - path: RootRoutes.ERROR, - pathMatch: 'full', - component: ServerErrorComponent, - }, - ], - }, - { - path: RootRoutes.WILDCARD, - pathMatch: 'full', - component: PageNotFoundComponent, - }, + { + path: RootRoutes.ROOT, + resolve: { title: titleResolver }, + children: [ + { + path: RootRoutes.ROOT, + data: { title: 'Home', roles: Roles.TechRecordView }, + canActivate: [MsalGuard, RoleGuard], + canDeactivate: [CancelEditTechGuard], + loadChildren: () => import('./features/home/home.module').then((m) => m.HomeModule), + }, + { + path: RootRoutes.SEARCH_TECHNICAL_RECORD, + data: { title: 'Technical record search', roles: Roles.TechRecordView }, + canActivate: [MsalGuard, RoleGuard], + canDeactivate: [CancelEditTechGuard], + loadChildren: () => import('./features/search/search.module').then((m) => m.SearchModule), + }, + { + path: RootRoutes.CREATE_TECHNICAL_RECORD, + data: { title: 'Create new technical record', roles: Roles.TechRecordCreate }, + canActivate: [MsalGuard, RoleGuard], + loadChildren: () => + import('./features/tech-record/create/create-tech-records.module').then((m) => m.CreateTechRecordsModule), + }, + { + path: RootRoutes.BATCH_CREATE_TECHNICAL_RECORD, + data: { title: 'Select Vehicle Type', roles: Roles.TechRecordCreate }, + canActivate: [MsalGuard, RoleGuard], + loadChildren: () => + import('./features/tech-record/create-batch/create-batch.module').then((m) => m.CreateBatchModule), + }, + { + path: RootRoutes.CURRENT_TEST_RESULT, + data: { title: 'Test Result', roles: Roles.TestResultView }, + canActivate: [MsalGuard, RoleGuard], + resolve: { techRecord: techRecordViewResolver }, + loadChildren: () => + import('./features/test-records/amend/amend-test-records.module').then((m) => m.AmendTestRecordsModule), + }, + { + path: RootRoutes.CURRENT_TECH_RECORD, + data: { title: 'Tech Record', roles: Roles.TechRecordView }, + canActivate: [MsalGuard, RoleGuard], + loadChildren: () => import('./features/tech-record/tech-record.module').then((m) => m.TechRecordsModule), + }, + { + path: RootRoutes.REFERENCE_DATA, + data: { title: 'Select Reference Data Type', roles: Roles.ReferenceDataView }, + canActivate: [MsalGuard, RoleGuard], + loadChildren: () => + import('./features/reference-data/reference-data.module').then((m) => m.ReferenceDataModule), + }, + { + path: RootRoutes.FEATURE_TOGGLE, + data: { title: 'Feature Toggle', featureToggleName: 'testToggle' }, + canActivate: [MsalGuard, FeatureToggleGuard], + loadChildren: () => + import('./features/feature-toggle/feature-toggle.module').then((m) => m.FeatureToggleModule), + }, + { + path: RootRoutes.ERROR, + pathMatch: 'full', + component: ServerErrorComponent, + }, + ], + }, + { + path: RootRoutes.WILDCARD, + pathMatch: 'full', + component: PageNotFoundComponent, + }, ]; @NgModule({ - imports: [RouterModule.forRoot(routes, { onSameUrlNavigation: 'reload' })], - exports: [RouterModule], + imports: [RouterModule.forRoot(routes, { onSameUrlNavigation: 'reload' })], + exports: [RouterModule], }) export class AppRoutingModule {} diff --git a/src/app/app.component.spec.ts b/src/app/app.component.spec.ts index c35d51c562..2a11b0340c 100644 --- a/src/app/app.component.spec.ts +++ b/src/app/app.component.spec.ts @@ -1,132 +1,132 @@ import { TestBed } from '@angular/core/testing'; +import { Router } from '@angular/router'; import { RouterTestingModule } from '@angular/router/testing'; import { MsalModule } from '@azure/msal-angular'; +import { PageNotFoundComponent } from '@core/components/page-not-found/page-not-found.component'; import { CoreModule } from '@core/core.module'; +import { GoogleAnalyticsServiceMock } from '@mocks/google-analytics-service.mock'; import { StoreModule } from '@ngrx/store'; import { provideMockStore } from '@ngrx/store/testing'; import { LoadingService } from '@services/loading/loading.service'; import { UserService } from '@services/user-service/user-service'; +// eslint-disable-next-line import/no-extraneous-dependencies +import { GoogleTagManagerService } from 'angular-google-tag-manager'; import { Observable, of } from 'rxjs'; -import { Router } from '@angular/router'; -import { PageNotFoundComponent } from '@core/components/page-not-found/page-not-found.component'; +import { AppRoutingModule } from './app-routing.module'; import { AppComponent } from './app.component'; import { State, initialAppState } from './store'; -import { AppRoutingModule } from './app-routing.module'; - -declare global { - function gtag(): void; -} describe('AppComponent', () => { - const MockUserService = { - getUserName$: jest.fn().mockReturnValue(new Observable()), - }; - let router: Router; - - window.gtag = jest.fn(); - - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [CoreModule, MsalModule, RouterTestingModule, AppRoutingModule, StoreModule.forRoot({})], - declarations: [PageNotFoundComponent, AppComponent], - providers: [ - provideMockStore({ initialState: initialAppState }), - { provide: LoadingService, useValue: { showSpinner$: of(false) } }, - { provide: UserService, useValue: MockUserService }, - PageNotFoundComponent, - ], - }).compileComponents(); - router = TestBed.inject(Router); - }); - - it('should create the app', () => { - const fixture = TestBed.createComponent(AppComponent); - const app = fixture.componentInstance; - fixture.detectChanges(); - expect(app).toBeTruthy(); - }); - - describe('router.initialNavigation', () => { - it('should init at default page', () => { - router.initialNavigation(); - expect(router.url).toBe('/'); - }); - }); - - describe('router.navigateByUrl', () => { - it('should navigate to search page', () => { - const navigateSpy = jest.spyOn(router, 'navigateByUrl'); - void router.navigateByUrl('/search'); - - expect(navigateSpy).toHaveBeenCalledWith('/search'); - }); - - it('should navigate to create page', () => { - const navigateSpy = jest.spyOn(router, 'navigateByUrl'); - void router.navigateByUrl('/create'); - - expect(navigateSpy).toHaveBeenCalledWith('/create'); - }); - - it('should navigate to create-batch page', () => { - const navigateSpy = jest.spyOn(router, 'navigateByUrl'); - void router.navigateByUrl('/create-batch'); - - expect(navigateSpy).toHaveBeenCalledWith('/create-batch'); - }); - - it('should navigate to test-records/:systemNumber/test-result/:testResultId/:testNumber', () => { - const navigateSpy = jest.spyOn(router, 'navigateByUrl'); - const systemNumber = '123'; - const testResultId = '456'; - const testNumber = '789'; - - void router.navigateByUrl(`test-records/${systemNumber}/test-result/${testResultId}/${testNumber}`); - - expect(navigateSpy).toHaveBeenCalledWith(`test-records/${systemNumber}/test-result/${testResultId}/${testNumber}`); - }); - - it('should navigate to tech-records/:systemNumber/:createdTimestamp', () => { - const navigateSpy = jest.spyOn(router, 'navigateByUrl'); - const systemNumber = '123'; - const createdTimestamp = '2024-02-23T09:56:12.872Z'; - - void router.navigateByUrl(`tech-records/${systemNumber}/${createdTimestamp}`); - - expect(navigateSpy).toHaveBeenCalledWith(`tech-records/${systemNumber}/${createdTimestamp}`); - }); - - it('should navigate to reference-data page', () => { - const navigateSpy = jest.spyOn(router, 'navigateByUrl'); - void router.navigateByUrl('/reference-data'); - - expect(navigateSpy).toHaveBeenCalledWith('/reference-data'); - }); - - it('should navigate to feature-toggle page', () => { - const navigateSpy = jest.spyOn(router, 'navigateByUrl'); - void router.navigateByUrl('/feature-toggle'); - - expect(navigateSpy).toHaveBeenCalledWith('/feature-toggle'); - }); - - it('should navigate to error page', () => { - const navigateSpy = jest.spyOn(router, 'navigateByUrl'); - void router.navigateByUrl('/error'); - - expect(navigateSpy).toHaveBeenCalledWith('/error'); - }); - - it('should navigate to PageNotFoundComponent page', () => { - const navigateSpy = jest.spyOn(router, 'navigateByUrl'); - const invalidRoute = 'invalid-url'; - void router.navigateByUrl(invalidRoute); - - expect(navigateSpy).toHaveBeenCalledWith(invalidRoute); - - const currentComponent = TestBed.inject(PageNotFoundComponent); - - expect(currentComponent).toBeTruthy(); - }); - }); + const MockUserService = { + getUserName$: jest.fn().mockReturnValue(new Observable()), + }; + let router: Router; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [CoreModule, MsalModule, RouterTestingModule, AppRoutingModule, StoreModule.forRoot({})], + declarations: [PageNotFoundComponent, AppComponent], + providers: [ + provideMockStore({ initialState: initialAppState }), + { provide: LoadingService, useValue: { showSpinner$: of(false) } }, + { provide: UserService, useValue: MockUserService }, + PageNotFoundComponent, + { provide: GoogleTagManagerService, useClass: GoogleAnalyticsServiceMock }, + ], + }).compileComponents(); + router = TestBed.inject(Router); + }); + + it('should create the app', () => { + const fixture = TestBed.createComponent(AppComponent); + const app = fixture.componentInstance; + fixture.detectChanges(); + expect(app).toBeTruthy(); + }); + + describe('router.initialNavigation', () => { + it('should init at default page', () => { + router.initialNavigation(); + expect(router.url).toBe('/'); + }); + }); + + describe('router.navigateByUrl', () => { + it('should navigate to search page', () => { + const navigateSpy = jest.spyOn(router, 'navigateByUrl'); + void router.navigateByUrl('/search'); + + expect(navigateSpy).toHaveBeenCalledWith('/search'); + }); + + it('should navigate to create page', () => { + const navigateSpy = jest.spyOn(router, 'navigateByUrl'); + void router.navigateByUrl('/create'); + + expect(navigateSpy).toHaveBeenCalledWith('/create'); + }); + + it('should navigate to create-batch page', () => { + const navigateSpy = jest.spyOn(router, 'navigateByUrl'); + void router.navigateByUrl('/create-batch'); + + expect(navigateSpy).toHaveBeenCalledWith('/create-batch'); + }); + + it('should navigate to test-records/:systemNumber/test-result/:testResultId/:testNumber', () => { + const navigateSpy = jest.spyOn(router, 'navigateByUrl'); + const systemNumber = '123'; + const testResultId = '456'; + const testNumber = '789'; + + void router.navigateByUrl(`test-records/${systemNumber}/test-result/${testResultId}/${testNumber}`); + + expect(navigateSpy).toHaveBeenCalledWith( + `test-records/${systemNumber}/test-result/${testResultId}/${testNumber}` + ); + }); + + it('should navigate to tech-records/:systemNumber/:createdTimestamp', () => { + const navigateSpy = jest.spyOn(router, 'navigateByUrl'); + const systemNumber = '123'; + const createdTimestamp = '2024-02-23T09:56:12.872Z'; + + void router.navigateByUrl(`tech-records/${systemNumber}/${createdTimestamp}`); + + expect(navigateSpy).toHaveBeenCalledWith(`tech-records/${systemNumber}/${createdTimestamp}`); + }); + + it('should navigate to reference-data page', () => { + const navigateSpy = jest.spyOn(router, 'navigateByUrl'); + void router.navigateByUrl('/reference-data'); + + expect(navigateSpy).toHaveBeenCalledWith('/reference-data'); + }); + + it('should navigate to feature-toggle page', () => { + const navigateSpy = jest.spyOn(router, 'navigateByUrl'); + void router.navigateByUrl('/feature-toggle'); + + expect(navigateSpy).toHaveBeenCalledWith('/feature-toggle'); + }); + + it('should navigate to error page', () => { + const navigateSpy = jest.spyOn(router, 'navigateByUrl'); + void router.navigateByUrl('/error'); + + expect(navigateSpy).toHaveBeenCalledWith('/error'); + }); + + it('should navigate to PageNotFoundComponent page', () => { + const navigateSpy = jest.spyOn(router, 'navigateByUrl'); + const invalidRoute = 'invalid-url'; + void router.navigateByUrl(invalidRoute); + + expect(navigateSpy).toHaveBeenCalledWith(invalidRoute); + + const currentComponent = TestBed.inject(PageNotFoundComponent); + + expect(currentComponent).toBeTruthy(); + }); + }); }); diff --git a/src/app/app.component.ts b/src/app/app.component.ts index c668defb5c..5cb9dfcec7 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -3,91 +3,76 @@ import { Component, OnDestroy, OnInit } from '@angular/core'; import { Event, NavigationEnd, Router } from '@angular/router'; import { Store, select } from '@ngrx/store'; -import * as Sentry from '@sentry/angular-ivy'; -import { GoogleAnalyticsService } from '@services/google-analytics/google-analytics.service'; +import * as Sentry from '@sentry/angular'; import { LoadingService } from '@services/loading/loading.service'; import { UserService } from '@services/user-service/user-service'; import { selectRouteData } from '@store/router/selectors/router.selectors'; +// eslint-disable-next-line import/no-extraneous-dependencies +import { GoogleTagManagerService } from 'angular-google-tag-manager'; import { initAll } from 'govuk-frontend/govuk/all'; -import { - Subject, - map, - take, - takeUntil, -} from 'rxjs'; +import { Subject, map, take, takeUntil } from 'rxjs'; import packageInfo from '../../package.json'; import { environment } from '../environments/environment'; import { State } from './store'; -declare const gtag: Function; @Component({ - selector: 'app-root', - templateUrl: './app.component.html', - styleUrls: ['app.component.scss'], + selector: 'app-root', + templateUrl: './app.component.html', + styleUrls: ['app.component.scss'], }) export class AppComponent implements OnInit, OnDestroy { - private destroy$ = new Subject(); + private destroy$ = new Subject(); - constructor( - public userService: UserService, - private loadingService: LoadingService, - private router: Router, - private store: Store, - private googleAnalyticsService: GoogleAnalyticsService, - ) { - this.router.events.pipe(takeUntil(this.destroy$)).subscribe((event: Event) => { - if (event instanceof NavigationEnd) { - this.googleAnalyticsService.pageView(document.title, event.urlAfterRedirects); - } - }); - } + constructor( + public userService: UserService, + private loadingService: LoadingService, + private router: Router, + private gtmService: GoogleTagManagerService, + private store: Store + ) {} - ngOnInit() { - this.startSentry(); - this.initGoogleTagManager(); - initAll(); - } + async ngOnInit() { + this.startSentry(); + this.router.events.pipe(takeUntil(this.destroy$)).subscribe((event: Event) => { + if (event instanceof NavigationEnd) { + const gtmTag = { + event: document.title, + pageName: event.urlAfterRedirects, + }; + void this.gtmService.pushTag(gtmTag); + } + }); + await this.gtmService.addGtmToDom(); + initAll(); + } - initGoogleTagManager() { - const scriptElement = document.createElement('script'); - scriptElement.async = true; - scriptElement.src = `https://www.googletagmanager.com/gtag/js?id=${environment.VTM_GTM_MEASUREMENT_ID}`; - document.head.appendChild(scriptElement); - gtag('config', environment.VTM_GTM_MEASUREMENT_ID); - } + ngOnDestroy(): void { + this.destroy$.next(); + this.destroy$.complete(); + } - ngOnDestroy(): void { - this.destroy$.next(); - this.destroy$.complete(); - } + get isStandardLayout() { + return this.store.pipe( + take(1), + select(selectRouteData), + map((routeData) => routeData && !routeData['isCustomLayout']) + ); + } - get isStandardLayout() { - return this.store.pipe( - take(1), - select(selectRouteData), - map((routeData) => routeData && !routeData['isCustomLayout']), - ); - } + get loading() { + return this.loadingService.showSpinner$; + } - get loading() { - return this.loadingService.showSpinner$; - } - - startSentry() { - Sentry.init({ - dsn: environment.SENTRY_DSN, - environment: environment.production ? 'production' : 'development', - release: packageInfo.version, - replaysSessionSampleRate: 0.1, - tracesSampleRate: 0.025, - replaysOnErrorSampleRate: 1.0, - enableTracing: false, - integrations: [ - new Sentry.BrowserTracing({ - routingInstrumentation: Sentry.routingInstrumentation, - }), - new Sentry.Replay(), - ], - }); - } + startSentry() { + Sentry.init({ + dsn: environment.SENTRY_DSN, + environment: environment.production ? 'production' : 'development', + release: packageInfo.version, + replaysSessionSampleRate: 0.1, + tracesSampleRate: 0.025, + replaysOnErrorSampleRate: 1.0, + enableTracing: false, + integrations: [Sentry.browserTracingIntegration(), Sentry.replayIntegration()], + }); + } } diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 3aff8c29de..7f8063591e 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -1,9 +1,5 @@ -import { HTTP_INTERCEPTORS, HttpClientModule } from '@angular/common/http'; -import { - APP_INITIALIZER, - ErrorHandler, - LOCALE_ID, NgModule, -} from '@angular/core'; +import { HTTP_INTERCEPTORS, provideHttpClient, withInterceptorsFromDi } from '@angular/common/http'; +import { APP_INITIALIZER, ErrorHandler, LOCALE_ID, NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { Router } from '@angular/router'; import { DocumentRetrievalApiModule, Configuration as DocumentRetrievalConfiguration } from '@api/document-retrieval'; @@ -11,26 +7,28 @@ import { ApiModule as ReferenceDataApiModule, Configuration as ReferenceDataConf import { Configuration as TestResultsApiConfiguration, ApiModule as TestResultsApiModule } from '@api/test-results'; import { Configuration as TestTypesApiConfiguration, ApiModule as TestTypesApiModule } from '@api/test-types'; import { - MSAL_GUARD_CONFIG, - MSAL_INSTANCE, - MSAL_INTERCEPTOR_CONFIG, - MsalBroadcastService, - MsalGuard, - MsalGuardConfiguration, - MsalInterceptor, - MsalInterceptorConfiguration, - MsalModule, - MsalRedirectComponent, - MsalService, + MSAL_GUARD_CONFIG, + MSAL_INSTANCE, + MSAL_INTERCEPTOR_CONFIG, + MsalBroadcastService, + MsalGuard, + MsalGuardConfiguration, + MsalInterceptor, + MsalInterceptorConfiguration, + MsalModule, + MsalRedirectComponent, + MsalService, } from '@azure/msal-angular'; import { - BrowserCacheLocation, - IPublicClientApplication, - InteractionType, - PublicClientApplication, + BrowserCacheLocation, + IPublicClientApplication, + InteractionType, + PublicClientApplication, } from '@azure/msal-browser'; -import * as Sentry from '@sentry/angular-ivy'; +import * as Sentry from '@sentry/angular'; import { FeatureToggleService } from '@services/feature-toggle-service/feature-toggle-service'; +// eslint-disable-next-line import/no-extraneous-dependencies +import { GoogleTagManagerModule } from 'angular-google-tag-manager'; import { environment } from '../environments/environment'; import { AppRoutingModule } from './app-routing.module'; import { AppComponent } from './app.component'; @@ -40,122 +38,123 @@ import { UserService } from './services/user-service/user-service'; import { AppStoreModule } from './store/app-store.module'; export function MSALInstanceFactory(): IPublicClientApplication { - return new PublicClientApplication({ - auth: { - clientId: environment.VTM_CLIENT_ID, - authority: environment.VTM_AUTHORITY_ID, - redirectUri: environment.VTM_REDIRECT_URI, - }, - cache: { - cacheLocation: BrowserCacheLocation.LocalStorage, - storeAuthStateInCookie: true, - }, - }); + return new PublicClientApplication({ + auth: { + clientId: environment.VTM_CLIENT_ID, + authority: environment.VTM_AUTHORITY_ID, + redirectUri: environment.VTM_REDIRECT_URI, + }, + cache: { + cacheLocation: BrowserCacheLocation.LocalStorage, + storeAuthStateInCookie: true, + }, + }); } export function MSALInterceptorConfigFactory(): MsalInterceptorConfiguration { - const protectedResourceMap = new Map>(); - protectedResourceMap.set(environment.VTM_API_URI, [`${environment.VTM_API_CLIENT_ID}/user_impersonation`, 'email']); + const protectedResourceMap = new Map>(); + protectedResourceMap.set(environment.VTM_API_URI, [`${environment.VTM_API_CLIENT_ID}/user_impersonation`, 'email']); - return { - interactionType: InteractionType.Redirect, - protectedResourceMap, - }; + return { + interactionType: InteractionType.Redirect, + protectedResourceMap, + }; } export function MSALGuardConfigFactory(): MsalGuardConfiguration { - return { - interactionType: InteractionType.Redirect, - authRequest: { - scopes: [`${environment.VTM_API_CLIENT_ID}/user_impersonation`, 'email'], - }, - loginFailedRoute: '', - }; + return { + interactionType: InteractionType.Redirect, + authRequest: { + scopes: [`${environment.VTM_API_CLIENT_ID}/user_impersonation`, 'email'], + }, + loginFailedRoute: '', + }; } -const featureFactory = (featureFlagsService: FeatureToggleService) => () => - featureFlagsService.loadConfig(); +const featureFactory = (featureFlagsService: FeatureToggleService) => () => featureFlagsService.loadConfig(); @NgModule({ - declarations: [AppComponent], - imports: [ - BrowserModule, - AppRoutingModule, - MsalModule, - HttpClientModule, - AppStoreModule, - InterceptorModule, - CoreModule, - TestResultsApiModule.forRoot(() => new TestResultsApiConfiguration({ basePath: environment.VTM_API_URI })), - TestTypesApiModule.forRoot(() => new TestTypesApiConfiguration({ basePath: environment.VTM_API_URI })), - ReferenceDataApiModule.forRoot(() => new ReferenceDataConfiguration({ basePath: environment.VTM_API_URI })), - DocumentRetrievalApiModule.forRoot( - () => - new DocumentRetrievalConfiguration({ - basePath: environment.VTM_API_URI, - apiKeys: { - 'X-Api-Key': environment.DOCUMENT_RETRIEVAL_API_KEY, - }, - }), - ), - ], - providers: [ - { - provide: LOCALE_ID, - useValue: 'en', - }, - { - provide: HTTP_INTERCEPTORS, - useClass: MsalInterceptor, - multi: true, - }, - { - provide: MSAL_INSTANCE, - useFactory: MSALInstanceFactory, - }, - { - provide: MSAL_GUARD_CONFIG, - useFactory: MSALGuardConfigFactory, - }, - { - provide: MSAL_INTERCEPTOR_CONFIG, - useFactory: MSALInterceptorConfigFactory, - }, - { - provide: APP_INITIALIZER, - useFactory: featureFactory, - deps: [FeatureToggleService], - multi: true, - }, - { - provide: ErrorHandler, - useValue: Sentry.createErrorHandler({ - showDialog: true, - }), - }, - { - provide: Sentry.TraceService, - deps: [Router], - }, - { - provide: APP_INITIALIZER, - useFactory: () => () => {}, - deps: [Sentry.TraceService], - multi: true, - }, - { - provide: ErrorHandler, - useValue: Sentry.createErrorHandler({ - showDialog: false, - }), - }, - MsalService, - MsalGuard, - MsalBroadcastService, - UserService, - ], - exports: [], - bootstrap: [AppComponent, MsalRedirectComponent], + declarations: [AppComponent], + exports: [], + bootstrap: [AppComponent, MsalRedirectComponent], + imports: [ + BrowserModule, + AppRoutingModule, + MsalModule, + AppStoreModule, + InterceptorModule, + CoreModule, + TestResultsApiModule.forRoot(() => new TestResultsApiConfiguration({ basePath: environment.VTM_API_URI })), + TestTypesApiModule.forRoot(() => new TestTypesApiConfiguration({ basePath: environment.VTM_API_URI })), + ReferenceDataApiModule.forRoot(() => new ReferenceDataConfiguration({ basePath: environment.VTM_API_URI })), + DocumentRetrievalApiModule.forRoot( + () => + new DocumentRetrievalConfiguration({ + basePath: environment.VTM_API_URI, + apiKeys: { + 'X-Api-Key': environment.DOCUMENT_RETRIEVAL_API_KEY, + }, + }) + ), + GoogleTagManagerModule.forRoot({ + id: environment.VTM_GTM_CONTAINER_ID, + }), + ], + providers: [ + { + provide: LOCALE_ID, + useValue: 'en', + }, + { + provide: HTTP_INTERCEPTORS, + useClass: MsalInterceptor, + multi: true, + }, + { + provide: MSAL_INSTANCE, + useFactory: MSALInstanceFactory, + }, + { + provide: MSAL_GUARD_CONFIG, + useFactory: MSALGuardConfigFactory, + }, + { + provide: MSAL_INTERCEPTOR_CONFIG, + useFactory: MSALInterceptorConfigFactory, + }, + { + provide: APP_INITIALIZER, + useFactory: featureFactory, + deps: [FeatureToggleService], + multi: true, + }, + { + provide: ErrorHandler, + useValue: Sentry.createErrorHandler({ + showDialog: true, + }), + }, + { + provide: Sentry.TraceService, + deps: [Router], + }, + { + provide: APP_INITIALIZER, + useFactory: () => () => {}, + deps: [Sentry.TraceService], + multi: true, + }, + { + provide: ErrorHandler, + useValue: Sentry.createErrorHandler({ + showDialog: false, + }), + }, + MsalService, + MsalGuard, + MsalBroadcastService, + UserService, + provideHttpClient(withInterceptorsFromDi()), + ], }) -export class AppModule { -} +export class AppModule {} diff --git a/src/app/app.stories.ts b/src/app/app.stories.ts index bc53031a78..b52efe0a28 100644 --- a/src/app/app.stories.ts +++ b/src/app/app.stories.ts @@ -3,10 +3,10 @@ import { Meta, Story } from '@storybook/angular'; import { AppComponent } from './app.component'; export default { - title: 'App', - component: AppComponent + title: 'App', + component: AppComponent, } as Meta; export const Primary: Story = () => ({ - props: {} + props: {}, }); diff --git a/src/app/core/components/breadcrumbs/breadcrumbs.component.spec.ts b/src/app/core/components/breadcrumbs/breadcrumbs.component.spec.ts index dfd2ced1fb..9e23418c03 100644 --- a/src/app/core/components/breadcrumbs/breadcrumbs.component.spec.ts +++ b/src/app/core/components/breadcrumbs/breadcrumbs.component.spec.ts @@ -9,85 +9,102 @@ import { firstValueFrom } from 'rxjs'; import { BreadcrumbsComponent } from './breadcrumbs.component'; describe('BreadcrumbsComponent', () => { - let component: BreadcrumbsComponent; - let fixture: ComponentFixture; - let store: MockStore; + let component: BreadcrumbsComponent; + let fixture: ComponentFixture; + let store: MockStore; - beforeEach(async () => { - await TestBed.configureTestingModule({ - declarations: [BreadcrumbsComponent], - imports: [RouterTestingModule], - providers: [RouterService, provideMockStore({ initialState: initialAppState })], - }).compileComponents(); - }); + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [BreadcrumbsComponent], + imports: [RouterTestingModule], + providers: [RouterService, provideMockStore({ initialState: initialAppState })], + }).compileComponents(); + }); - beforeEach(() => { - fixture = TestBed.createComponent(BreadcrumbsComponent); - component = fixture.componentInstance; - store = TestBed.inject(MockStore); - fixture.detectChanges(); - }); + beforeEach(() => { + fixture = TestBed.createComponent(BreadcrumbsComponent); + component = fixture.componentInstance; + store = TestBed.inject(MockStore); + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); - it.each([ - [[], { state: { root: { firstChild: { data: { title: 'Path1' }, url: [{ path: 'path1' }] } } } } as unknown as RouterReducerState], + it.each([ + [ + [], + { + state: { root: { firstChild: { data: { title: 'Path1' }, url: [{ path: 'path1' }] } } }, + } as unknown as RouterReducerState, + ], - [[{ label: 'Path1', path: '', preserveQueryParams: false }], - { state: { root: { firstChild: { data: { title: 'Path1' }, routeConfig: { path: 'path1' }, url: [] } } } } as unknown as RouterReducerState], + [ + [{ label: 'Path1', path: '', preserveQueryParams: false }], + { + state: { root: { firstChild: { data: { title: 'Path1' }, routeConfig: { path: 'path1' }, url: [] } } }, + } as unknown as RouterReducerState, + ], - [ - [{ label: 'Path1', path: 'path1', preserveQueryParams: false }], - { - state: - { root: { firstChild: { data: { title: 'Path1' }, routeConfig: { path: 'path1' }, url: [{ path: 'path1' }] } } }, - } as unknown as RouterReducerState, - ], + [ + [{ label: 'Path1', path: 'path1', preserveQueryParams: false }], + { + state: { + root: { firstChild: { data: { title: 'Path1' }, routeConfig: { path: 'path1' }, url: [{ path: 'path1' }] } }, + }, + } as unknown as RouterReducerState, + ], - [ - [ - { label: 'Path1', path: 'path1', preserveQueryParams: false }, - { label: 'Path2', path: 'path1/path2', preserveQueryParams: false }, - ], - { - state: { - root: { - firstChild: { - data: { title: 'Path1' }, - routeConfig: { path: 'path1' }, - url: [{ path: 'path1' }], - firstChild: { data: { title: 'Path2' }, routeConfig: { path: 'path2' }, url: [{ path: 'path2' }] }, - }, - }, - }, - } as unknown as RouterReducerState, - ], - [ - [ - { label: 'Path1', path: 'path1', preserveQueryParams: false }, - { label: 'Path2', path: 'path1/path2', preserveQueryParams: true }, - ], - { - navigationId: 'foo', - state: { - root: { - firstChild: { - data: { title: 'Path1' }, - routeConfig: { path: 'path1' }, - url: [{ path: 'path1' }], - firstChild: { data: { title: 'Path2', breadcrumbPreserveQueryParams: true }, routeConfig: { path: 'path2' }, url: [{ path: 'path2' }] }, - }, - }, - }, - } as unknown as RouterReducerState, - ], - ])( - 'should return %o when router state is %o', - async (expected: { label: string; path: string, preserveQueryParams: boolean }[], routeState: RouterReducerState) => { - store.overrideSelector(routerState, routeState); - expect(await firstValueFrom(component.breadcrumbs$)).toEqual(expected); - }, - ); + [ + [ + { label: 'Path1', path: 'path1', preserveQueryParams: false }, + { label: 'Path2', path: 'path1/path2', preserveQueryParams: false }, + ], + { + state: { + root: { + firstChild: { + data: { title: 'Path1' }, + routeConfig: { path: 'path1' }, + url: [{ path: 'path1' }], + firstChild: { data: { title: 'Path2' }, routeConfig: { path: 'path2' }, url: [{ path: 'path2' }] }, + }, + }, + }, + } as unknown as RouterReducerState, + ], + [ + [ + { label: 'Path1', path: 'path1', preserveQueryParams: false }, + { label: 'Path2', path: 'path1/path2', preserveQueryParams: true }, + ], + { + navigationId: 'foo', + state: { + root: { + firstChild: { + data: { title: 'Path1' }, + routeConfig: { path: 'path1' }, + url: [{ path: 'path1' }], + firstChild: { + data: { title: 'Path2', breadcrumbPreserveQueryParams: true }, + routeConfig: { path: 'path2' }, + url: [{ path: 'path2' }], + }, + }, + }, + }, + } as unknown as RouterReducerState, + ], + ])( + 'should return %o when router state is %o', + async ( + expected: { label: string; path: string; preserveQueryParams: boolean }[], + routeState: RouterReducerState + ) => { + store.overrideSelector(routerState, routeState); + expect(await firstValueFrom(component.breadcrumbs$)).toEqual(expected); + } + ); }); diff --git a/src/app/core/components/breadcrumbs/breadcrumbs.component.ts b/src/app/core/components/breadcrumbs/breadcrumbs.component.ts index 67f6d938ba..ff11ec3c95 100644 --- a/src/app/core/components/breadcrumbs/breadcrumbs.component.ts +++ b/src/app/core/components/breadcrumbs/breadcrumbs.component.ts @@ -3,39 +3,39 @@ import { RouterService } from '@services/router/router.service'; import { distinctUntilChanged, map } from 'rxjs'; @Component({ - selector: 'app-breadcrumbs', - templateUrl: './breadcrumbs.component.html', - styleUrls: ['./breadcrumbs.component.scss'], + selector: 'app-breadcrumbs', + templateUrl: './breadcrumbs.component.html', + styleUrls: ['./breadcrumbs.component.scss'], }) export class BreadcrumbsComponent { - constructor(private routerService: RouterService) {} + constructor(private routerService: RouterService) {} - get breadcrumbs$() { - return this.routerService.router$.pipe( - distinctUntilChanged(), - map((router) => { - let currentRoute = router?.state?.root; - const breadcrumbs: Array<{ label: string; path: string; preserveQueryParams: boolean }> = []; + get breadcrumbs$() { + return this.routerService.router$.pipe( + distinctUntilChanged(), + map((router) => { + let currentRoute = router?.state?.root; + const breadcrumbs: Array<{ label: string; path: string; preserveQueryParams: boolean }> = []; - while (currentRoute?.firstChild) { - const { routeConfig, data, url } = currentRoute.firstChild; + while (currentRoute?.firstChild) { + const { routeConfig, data, url } = currentRoute.firstChild; - if (data['title'] && routeConfig?.path && !breadcrumbs.some((b) => b.label === data['title'])) { - breadcrumbs.push({ - label: data['title'], - path: [...breadcrumbs.slice(-1).map((b) => b.path), ...url.map((urlValue) => urlValue.path)].join('/'), - preserveQueryParams: !!data['breadcrumbPreserveQueryParams'], - }); - } + if (data['title'] && routeConfig?.path && !breadcrumbs.some((b) => b.label === data['title'])) { + breadcrumbs.push({ + label: data['title'], + path: [...breadcrumbs.slice(-1).map((b) => b.path), ...url.map((urlValue) => urlValue.path)].join('/'), + preserveQueryParams: !!data['breadcrumbPreserveQueryParams'], + }); + } - currentRoute = currentRoute.firstChild; - } - return breadcrumbs; - }), - ); - } + currentRoute = currentRoute.firstChild; + } + return breadcrumbs; + }) + ); + } - trackByFn(index: number, breadcrumb: { label: string; path: string }) { - return breadcrumb.path || index; - } + trackByFn(index: number, breadcrumb: { label: string; path: string }) { + return breadcrumb.path || index; + } } diff --git a/src/app/core/components/footer/footer.component.spec.ts b/src/app/core/components/footer/footer.component.spec.ts index 36c31ab3d0..6c48f38b17 100644 --- a/src/app/core/components/footer/footer.component.spec.ts +++ b/src/app/core/components/footer/footer.component.spec.ts @@ -3,22 +3,22 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { FooterComponent } from './footer.component'; describe('FooterComponent', () => { - let component: FooterComponent; - let fixture: ComponentFixture; + let component: FooterComponent; + let fixture: ComponentFixture; - beforeEach(async () => { - await TestBed.configureTestingModule({ - declarations: [FooterComponent], - }).compileComponents(); - }); + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [FooterComponent], + }).compileComponents(); + }); - beforeEach(() => { - fixture = TestBed.createComponent(FooterComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); + beforeEach(() => { + fixture = TestBed.createComponent(FooterComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); }); diff --git a/src/app/core/components/footer/footer.component.ts b/src/app/core/components/footer/footer.component.ts index 669de37488..a07c1f15dd 100644 --- a/src/app/core/components/footer/footer.component.ts +++ b/src/app/core/components/footer/footer.component.ts @@ -1,7 +1,7 @@ import { Component } from '@angular/core'; @Component({ - selector: 'app-footer', - templateUrl: './footer.component.html', + selector: 'app-footer', + templateUrl: './footer.component.html', }) export class FooterComponent {} diff --git a/src/app/core/components/footer/footer.stories.ts b/src/app/core/components/footer/footer.stories.ts index 4bc3ed48ad..0361eabdc0 100644 --- a/src/app/core/components/footer/footer.stories.ts +++ b/src/app/core/components/footer/footer.stories.ts @@ -3,10 +3,10 @@ import { Meta, Story } from '@storybook/angular'; import { FooterComponent } from './footer.component'; export default { - title: 'Footer', - component: FooterComponent + title: 'Footer', + component: FooterComponent, } as Meta; export const Primary: Story = () => ({ - props: {} + props: {}, }); diff --git a/src/app/core/components/global-error/global-error.component.spec.ts b/src/app/core/components/global-error/global-error.component.spec.ts index d4c0d183fc..fb2ebddc04 100644 --- a/src/app/core/components/global-error/global-error.component.spec.ts +++ b/src/app/core/components/global-error/global-error.component.spec.ts @@ -7,40 +7,40 @@ import { GlobalErrorComponent } from './global-error.component'; import { GlobalErrorService } from './global-error.service'; @Component({ - selector: 'app-mock-component', - template: '\n', - styles: [], + selector: 'app-mock-component', + template: '\n', + styles: [], }) class MockComponent {} describe('GlobalErrorComponent', () => { - let component: GlobalErrorComponent; - let fixture: ComponentFixture; + let component: GlobalErrorComponent; + let fixture: ComponentFixture; - beforeEach(async () => { - await TestBed.configureTestingModule({ - declarations: [GlobalErrorComponent, MockComponent], - imports: [], - providers: [GlobalErrorService, provideMockStore({ initialState: initialAppState })], - }).compileComponents(); - }); + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [GlobalErrorComponent, MockComponent], + imports: [], + providers: [GlobalErrorService, provideMockStore({ initialState: initialAppState })], + }).compileComponents(); + }); - beforeEach(() => { - fixture = TestBed.createComponent(MockComponent); - component = fixture.debugElement.query(By.directive(GlobalErrorComponent)).componentInstance; - fixture.detectChanges(); - }); + beforeEach(() => { + fixture = TestBed.createComponent(MockComponent); + component = fixture.debugElement.query(By.directive(GlobalErrorComponent)).componentInstance; + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); - describe('goto', () => { - it('should focus element', () => { - const input: HTMLInputElement = fixture.debugElement.query(By.css('#test-input')).nativeElement; - component.goto({ error: 'navigate', anchorLink: 'test-input' }); + describe('goto', () => { + it('should focus element', () => { + const input: HTMLInputElement = fixture.debugElement.query(By.css('#test-input')).nativeElement; + component.goto({ error: 'navigate', anchorLink: 'test-input' }); - expect(document.activeElement).toBe(input); - }); - }); + expect(document.activeElement).toBe(input); + }); + }); }); diff --git a/src/app/core/components/global-error/global-error.component.ts b/src/app/core/components/global-error/global-error.component.ts index d8dc2e79f6..be51a141ae 100644 --- a/src/app/core/components/global-error/global-error.component.ts +++ b/src/app/core/components/global-error/global-error.component.ts @@ -3,17 +3,18 @@ import { GlobalError } from './global-error.interface'; import { GlobalErrorService } from './global-error.service'; @Component({ - selector: 'app-global-error', - templateUrl: './global-error.component.html', + selector: 'app-global-error', + templateUrl: './global-error.component.html', }) export class GlobalErrorComponent { - constructor(public globalErrorService: GlobalErrorService) {} + constructor(public globalErrorService: GlobalErrorService) {} - goto(error: GlobalError) { - if (error.anchorLink) { - let focusCount = 0; + goto(error: GlobalError) { + if (error.anchorLink) { + let focusCount = 0; - document.querySelectorAll(` + document + .querySelectorAll(` #${error.anchorLink}, #${error.anchorLink} a[href]:not([tabindex='-1']), #${error.anchorLink} area[href]:not([tabindex='-1']), @@ -25,12 +26,12 @@ export class GlobalErrorComponent { #${error.anchorLink} [tabindex]:not([tabindex='-1']), #${error.anchorLink} [contentEditable=true]:not([tabindex='-1']) `) - .forEach((el) => { - if (el instanceof HTMLElement && focusCount < 2) { - focusCount++; - el.focus({ preventScroll: false }); - } - }); - } - } + .forEach((el) => { + if (el instanceof HTMLElement && focusCount < 2) { + focusCount++; + el.focus({ preventScroll: false }); + } + }); + } + } } diff --git a/src/app/core/components/global-error/global-error.interface.ts b/src/app/core/components/global-error/global-error.interface.ts index 21eb1e69c9..2d754bd2cf 100644 --- a/src/app/core/components/global-error/global-error.interface.ts +++ b/src/app/core/components/global-error/global-error.interface.ts @@ -1,4 +1,4 @@ export interface GlobalError { - error: string; - anchorLink?: string; + error: string; + anchorLink?: string; } diff --git a/src/app/core/components/global-error/global-error.service.spec.ts b/src/app/core/components/global-error/global-error.service.spec.ts index a3927d2f4a..8f0250c737 100644 --- a/src/app/core/components/global-error/global-error.service.spec.ts +++ b/src/app/core/components/global-error/global-error.service.spec.ts @@ -1,59 +1,57 @@ import { TestBed } from '@angular/core/testing'; import { MockStore, provideMockStore } from '@ngrx/store/testing'; import { State, initialAppState } from '@store/.'; -import { - addError, clearError, patchErrors, setErrors, -} from '@store/global-error/actions/global-error.actions'; +import { addError, clearError, patchErrors, setErrors } from '@store/global-error/actions/global-error.actions'; import { GlobalError } from './global-error.interface'; import { GlobalErrorService } from './global-error.service'; describe('GlobalErrorService', () => { - let service: GlobalErrorService; - let store: MockStore; - beforeEach(() => { - TestBed.configureTestingModule({ providers: [provideMockStore({ initialState: initialAppState })] }); - service = TestBed.inject(GlobalErrorService); - store = TestBed.inject(MockStore); - }); + let service: GlobalErrorService; + let store: MockStore; + beforeEach(() => { + TestBed.configureTestingModule({ providers: [provideMockStore({ initialState: initialAppState })] }); + service = TestBed.inject(GlobalErrorService); + store = TestBed.inject(MockStore); + }); - it('should be created', () => { - expect(service).toBeTruthy(); - }); + it('should be created', () => { + expect(service).toBeTruthy(); + }); - it('should dispatch action addError', () => { - const expectedError: GlobalError = { error: 'erro 2', anchorLink: '' }; - const dispatchSpy = jest.spyOn(store, 'dispatch'); - service.addError(expectedError); - expect(dispatchSpy).toHaveBeenCalledTimes(1); - expect(dispatchSpy).toHaveBeenCalledWith(addError(expectedError)); - }); + it('should dispatch action addError', () => { + const expectedError: GlobalError = { error: 'erro 2', anchorLink: '' }; + const dispatchSpy = jest.spyOn(store, 'dispatch'); + service.addError(expectedError); + expect(dispatchSpy).toHaveBeenCalledTimes(1); + expect(dispatchSpy).toHaveBeenCalledWith(addError(expectedError)); + }); - it('should dispatch action patchErrors', () => { - const expectedErrors: GlobalError[] = [{ error: 'erro 2', anchorLink: '' }]; - const dispatchSpy = jest.spyOn(store, 'dispatch'); - service.patchErrors(expectedErrors); - expect(dispatchSpy).toHaveBeenCalledTimes(1); - expect(dispatchSpy).toHaveBeenCalledWith(patchErrors({ errors: expectedErrors })); - }); + it('should dispatch action patchErrors', () => { + const expectedErrors: GlobalError[] = [{ error: 'erro 2', anchorLink: '' }]; + const dispatchSpy = jest.spyOn(store, 'dispatch'); + service.patchErrors(expectedErrors); + expect(dispatchSpy).toHaveBeenCalledTimes(1); + expect(dispatchSpy).toHaveBeenCalledWith(patchErrors({ errors: expectedErrors })); + }); - it('should dispatch action clearError', () => { - const dispatchSpy = jest.spyOn(store, 'dispatch'); - service.clearErrors(); - expect(dispatchSpy).toHaveBeenCalledTimes(1); - expect(dispatchSpy).toHaveBeenCalledWith(clearError()); - }); + it('should dispatch action clearError', () => { + const dispatchSpy = jest.spyOn(store, 'dispatch'); + service.clearErrors(); + expect(dispatchSpy).toHaveBeenCalledTimes(1); + expect(dispatchSpy).toHaveBeenCalledWith(clearError()); + }); - it('should dispatch action setErrors', () => { - const expectedErrors: GlobalError[] = [{ error: 'erro 2', anchorLink: '' }]; - const dispatchSpy = jest.spyOn(store, 'dispatch'); - service.setErrors(expectedErrors); - expect(dispatchSpy).toHaveBeenCalledTimes(1); - expect(dispatchSpy).toHaveBeenCalledWith(setErrors({ errors: expectedErrors })); - }); + it('should dispatch action setErrors', () => { + const expectedErrors: GlobalError[] = [{ error: 'erro 2', anchorLink: '' }]; + const dispatchSpy = jest.spyOn(store, 'dispatch'); + service.setErrors(expectedErrors); + expect(dispatchSpy).toHaveBeenCalledTimes(1); + expect(dispatchSpy).toHaveBeenCalledWith(setErrors({ errors: expectedErrors })); + }); - it('should attempt to focus all focusable controls', () => { - const spy = jest.spyOn(document, 'querySelectorAll'); - service.focusAllControls(); - expect(spy).toHaveBeenCalled(); - }); + it('should attempt to focus all focusable controls', () => { + const spy = jest.spyOn(document, 'querySelectorAll'); + service.focusAllControls(); + expect(spy).toHaveBeenCalled(); + }); }); diff --git a/src/app/core/components/global-error/global-error.service.ts b/src/app/core/components/global-error/global-error.service.ts index a0014d152a..a89c349632 100644 --- a/src/app/core/components/global-error/global-error.service.ts +++ b/src/app/core/components/global-error/global-error.service.ts @@ -1,45 +1,44 @@ import { Injectable } from '@angular/core'; -import { select, Store } from '@ngrx/store'; +import { Store, select } from '@ngrx/store'; import { State } from '@store/.'; -import { - addError, clearError, patchErrors, setErrors, -} from '@store/global-error/actions/global-error.actions'; +import { addError, clearError, patchErrors, setErrors } from '@store/global-error/actions/global-error.actions'; import { globalErrorState } from '@store/global-error/reducers/global-error-service.reducer'; import { Observable } from 'rxjs'; import { GlobalError } from './global-error.interface'; @Injectable({ - providedIn: 'root', + providedIn: 'root', }) export class GlobalErrorService { - private errors: Observable; + private errors: Observable; - constructor(private store: Store) { - this.errors = this.store.pipe(select(globalErrorState)); - } + constructor(private store: Store) { + this.errors = this.store.pipe(select(globalErrorState)); + } - get errors$() { - return this.errors; - } + get errors$() { + return this.errors; + } - addError(error: GlobalError) { - this.store.dispatch(addError(error)); - } + addError(error: GlobalError) { + this.store.dispatch(addError(error)); + } - setErrors(errors: GlobalError[]) { - this.store.dispatch(setErrors({ errors })); - } + setErrors(errors: GlobalError[]) { + this.store.dispatch(setErrors({ errors })); + } - patchErrors(errors: GlobalError[]) { - this.store.dispatch(patchErrors({ errors })); - } + patchErrors(errors: GlobalError[]) { + this.store.dispatch(patchErrors({ errors })); + } - clearErrors(): void { - this.store.dispatch(clearError()); - } + clearErrors(): void { + this.store.dispatch(clearError()); + } - focusAllControls() { - document.querySelectorAll(` + focusAllControls() { + document + .querySelectorAll(` a[href]:not([tabindex='-1']), area[href]:not([tabindex='-1']), input:not([disabled]):not([tabindex='-1']), @@ -50,11 +49,11 @@ export class GlobalErrorService { [tabindex]:not([tabindex='-1']), [contentEditable=true]:not([tabindex='-1']) `) - .forEach((element) => { - if (element instanceof HTMLElement) { - element.focus(); - element.blur(); - } - }); - } + .forEach((element) => { + if (element instanceof HTMLElement) { + element.focus(); + element.blur(); + } + }); + } } diff --git a/src/app/core/components/global-error/global-error.stories.ts b/src/app/core/components/global-error/global-error.stories.ts index 10a4768ef2..6430a0a5e1 100644 --- a/src/app/core/components/global-error/global-error.stories.ts +++ b/src/app/core/components/global-error/global-error.stories.ts @@ -2,14 +2,14 @@ import { Meta, Story } from '@storybook/angular'; import { GlobalErrorComponent } from './global-error.component'; export default { - title: 'Global Error Component', - component: GlobalErrorComponent + title: 'Global Error Component', + component: GlobalErrorComponent, } as Meta; export const NoErrors: Story = () => ({ - props: {} + props: {}, }); export const Error: Story = () => ({ - props: { errorMessage$: 'error' } + props: { errorMessage$: 'error' }, }); diff --git a/src/app/core/components/global-warning/global-warning.component.spec.ts b/src/app/core/components/global-warning/global-warning.component.spec.ts index f26c795b53..65bc2ae2d8 100644 --- a/src/app/core/components/global-warning/global-warning.component.spec.ts +++ b/src/app/core/components/global-warning/global-warning.component.spec.ts @@ -7,46 +7,46 @@ import { GlobalWarningComponent } from './global-warning.component'; import { GlobalWarningService } from './global-warning.service'; @Component({ - selector: 'app-mock-component', - template: '\n', - styles: [], + selector: 'app-mock-component', + template: '\n', + styles: [], }) class MockComponent {} describe('GlobalWarningComponent', () => { - let component: GlobalWarningComponent; - let fixture: ComponentFixture; - - beforeEach(async () => { - await TestBed.configureTestingModule({ - declarations: [GlobalWarningComponent, MockComponent], - imports: [], - providers: [GlobalWarningService, provideMockStore({ initialState: initialAppState })], - }).compileComponents(); - }); - - beforeEach(() => { - fixture = TestBed.createComponent(MockComponent); - component = fixture.debugElement.query(By.directive(GlobalWarningComponent)).componentInstance; - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); - - describe('goto', () => { - it('should focus element', () => { - const input: HTMLInputElement = fixture.debugElement.query(By.css('#test-input')).nativeElement; - component.goto({ warning: 'navigate', anchorLink: 'test-input' }); - - expect(document.activeElement).toBe(input); - }); - - it('should do nothing if no anchor link is provided', () => { - const spy = jest.spyOn(document, 'getElementById'); - component.goto({ warning: 'navigate', anchorLink: undefined }); - expect(spy).not.toHaveBeenCalled(); - }); - }); + let component: GlobalWarningComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [GlobalWarningComponent, MockComponent], + imports: [], + providers: [GlobalWarningService, provideMockStore({ initialState: initialAppState })], + }).compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(MockComponent); + component = fixture.debugElement.query(By.directive(GlobalWarningComponent)).componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + describe('goto', () => { + it('should focus element', () => { + const input: HTMLInputElement = fixture.debugElement.query(By.css('#test-input')).nativeElement; + component.goto({ warning: 'navigate', anchorLink: 'test-input' }); + + expect(document.activeElement).toBe(input); + }); + + it('should do nothing if no anchor link is provided', () => { + const spy = jest.spyOn(document, 'getElementById'); + component.goto({ warning: 'navigate', anchorLink: undefined }); + expect(spy).not.toHaveBeenCalled(); + }); + }); }); diff --git a/src/app/core/components/global-warning/global-warning.component.ts b/src/app/core/components/global-warning/global-warning.component.ts index 0770edc945..d34809d08e 100644 --- a/src/app/core/components/global-warning/global-warning.component.ts +++ b/src/app/core/components/global-warning/global-warning.component.ts @@ -3,16 +3,16 @@ import { GlobalWarning } from './global-warning.interface'; import { GlobalWarningService } from './global-warning.service'; @Component({ - selector: 'app-global-warning', - templateUrl: './global-warning.component.html', + selector: 'app-global-warning', + templateUrl: './global-warning.component.html', }) export class GlobalWarningComponent { - constructor(public globalWarningService: GlobalWarningService) {} + constructor(public globalWarningService: GlobalWarningService) {} - goto(warning: GlobalWarning) { - if (warning.anchorLink) { - const el = document.getElementById(warning.anchorLink); - el?.focus({ preventScroll: false }); - } - } + goto(warning: GlobalWarning) { + if (warning.anchorLink) { + const el = document.getElementById(warning.anchorLink); + el?.focus({ preventScroll: false }); + } + } } diff --git a/src/app/core/components/global-warning/global-warning.interface.ts b/src/app/core/components/global-warning/global-warning.interface.ts index 9da5102102..9ae047e5d7 100644 --- a/src/app/core/components/global-warning/global-warning.interface.ts +++ b/src/app/core/components/global-warning/global-warning.interface.ts @@ -1,4 +1,4 @@ export interface GlobalWarning { - warning: string; - anchorLink?: string; + warning: string; + anchorLink?: string; } diff --git a/src/app/core/components/global-warning/global-warning.service.spec.ts b/src/app/core/components/global-warning/global-warning.service.spec.ts index 8bc261ee31..1dc9ed2f04 100644 --- a/src/app/core/components/global-warning/global-warning.service.spec.ts +++ b/src/app/core/components/global-warning/global-warning.service.spec.ts @@ -1,35 +1,35 @@ import { TestBed } from '@angular/core/testing'; import { MockStore, provideMockStore } from '@ngrx/store/testing'; -import { initialAppState, State } from '@store/.'; -import { setWarnings, clearWarning } from '@store/global-warning/actions/global-warning.actions'; +import { State, initialAppState } from '@store/.'; +import { clearWarning, setWarnings } from '@store/global-warning/actions/global-warning.actions'; import { GlobalWarning } from './global-warning.interface'; import { GlobalWarningService } from './global-warning.service'; describe('GlobalWarningService', () => { - let service: GlobalWarningService; - let store: MockStore; - beforeEach(() => { - TestBed.configureTestingModule({ providers: [provideMockStore({ initialState: initialAppState })] }); - service = TestBed.inject(GlobalWarningService); - store = TestBed.inject(MockStore); - }); + let service: GlobalWarningService; + let store: MockStore; + beforeEach(() => { + TestBed.configureTestingModule({ providers: [provideMockStore({ initialState: initialAppState })] }); + service = TestBed.inject(GlobalWarningService); + store = TestBed.inject(MockStore); + }); - it('should be created', () => { - expect(service).toBeTruthy(); - }); + it('should be created', () => { + expect(service).toBeTruthy(); + }); - it('should dispatch action setWarnings', () => { - const expectedWarning: GlobalWarning = { warning: 'warn 2', anchorLink: '' }; - const dispatchSpy = jest.spyOn(store, 'dispatch'); - service.setWarnings([expectedWarning]); - expect(dispatchSpy).toHaveBeenCalledTimes(1); - expect(dispatchSpy).toHaveBeenCalledWith(setWarnings({ warnings: [expectedWarning] })); - }); + it('should dispatch action setWarnings', () => { + const expectedWarning: GlobalWarning = { warning: 'warn 2', anchorLink: '' }; + const dispatchSpy = jest.spyOn(store, 'dispatch'); + service.setWarnings([expectedWarning]); + expect(dispatchSpy).toHaveBeenCalledTimes(1); + expect(dispatchSpy).toHaveBeenCalledWith(setWarnings({ warnings: [expectedWarning] })); + }); - it('should dispatch action clearError', () => { - const dispatchSpy = jest.spyOn(store, 'dispatch'); - service.clearWarnings(); - expect(dispatchSpy).toHaveBeenCalledTimes(1); - expect(dispatchSpy).toHaveBeenCalledWith(clearWarning()); - }); + it('should dispatch action clearError', () => { + const dispatchSpy = jest.spyOn(store, 'dispatch'); + service.clearWarnings(); + expect(dispatchSpy).toHaveBeenCalledTimes(1); + expect(dispatchSpy).toHaveBeenCalledWith(clearWarning()); + }); }); diff --git a/src/app/core/components/global-warning/global-warning.service.ts b/src/app/core/components/global-warning/global-warning.service.ts index f0a7b61ba4..76d3445767 100644 --- a/src/app/core/components/global-warning/global-warning.service.ts +++ b/src/app/core/components/global-warning/global-warning.service.ts @@ -1,30 +1,30 @@ import { Injectable } from '@angular/core'; -import { select, Store } from '@ngrx/store'; +import { Store, select } from '@ngrx/store'; import { State } from '@store/.'; import { clearWarning, setWarnings } from '@store/global-warning/actions/global-warning.actions'; -import { Observable } from 'rxjs'; import { globalWarningState } from '@store/global-warning/reducers/global-warning-service.reducers'; +import { Observable } from 'rxjs'; import { GlobalWarning } from './global-warning.interface'; @Injectable({ - providedIn: 'root', + providedIn: 'root', }) export class GlobalWarningService { - private warnings: Observable; + private warnings: Observable; - constructor(private store: Store) { - this.warnings = this.store.pipe(select(globalWarningState)); - } + constructor(private store: Store) { + this.warnings = this.store.pipe(select(globalWarningState)); + } - get warnings$() { - return this.warnings; - } + get warnings$() { + return this.warnings; + } - setWarnings(warnings: GlobalWarning[]) { - this.store.dispatch(setWarnings({ warnings })); - } + setWarnings(warnings: GlobalWarning[]) { + this.store.dispatch(setWarnings({ warnings })); + } - clearWarnings(): void { - this.store.dispatch(clearWarning()); - } + clearWarnings(): void { + this.store.dispatch(clearWarning()); + } } diff --git a/src/app/core/components/header/header.component.spec.ts b/src/app/core/components/header/header.component.spec.ts index bc67bbde09..36543cc334 100644 --- a/src/app/core/components/header/header.component.spec.ts +++ b/src/app/core/components/header/header.component.spec.ts @@ -4,42 +4,42 @@ import { By } from '@angular/platform-browser'; import { HeaderComponent } from './header.component'; describe('HeaderComponent', () => { - let component: HeaderComponent; - let fixture: ComponentFixture; - let userNameText: HTMLElement; - let logOutButton: DebugElement; - - beforeEach(async () => { - await TestBed.configureTestingModule({ - declarations: [HeaderComponent], - }).compileComponents(); - }); - - beforeEach(() => { - fixture = TestBed.createComponent(HeaderComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - - userNameText = fixture.debugElement.nativeElement.querySelector('#username'); - logOutButton = fixture.debugElement.query(By.css('#sign-out')); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); - - it('Change username updates page', () => { - component.username = 'Test'; - fixture.detectChanges(); - expect(userNameText.innerHTML).toBe('Test'); - }); - - it('Clicking logout fires off event', (done) => { - component.logOutEvent.subscribe(() => { - done(); - expect(done).toHaveBeenCalled(); - }); - - logOutButton.triggerEventHandler('click', null); - }); + let component: HeaderComponent; + let fixture: ComponentFixture; + let userNameText: HTMLElement; + let logOutButton: DebugElement; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [HeaderComponent], + }).compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(HeaderComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + + userNameText = fixture.debugElement.nativeElement.querySelector('#username'); + logOutButton = fixture.debugElement.query(By.css('#sign-out')); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + it('Change username updates page', () => { + component.username = 'Test'; + fixture.detectChanges(); + expect(userNameText.innerHTML).toBe('Test'); + }); + + it('Clicking logout fires off event', (done) => { + component.logOutEvent.subscribe(() => { + done(); + expect(done).toHaveBeenCalled(); + }); + + logOutButton.triggerEventHandler('click', null); + }); }); diff --git a/src/app/core/components/header/header.component.ts b/src/app/core/components/header/header.component.ts index 2abccae52d..a918db42a6 100644 --- a/src/app/core/components/header/header.component.ts +++ b/src/app/core/components/header/header.component.ts @@ -1,17 +1,15 @@ -import { - Component, EventEmitter, Input, Output, -} from '@angular/core'; +import { Component, EventEmitter, Input, Output } from '@angular/core'; @Component({ - selector: 'app-header', - templateUrl: './header.component.html', - styleUrls: ['./header.component.scss'], + selector: 'app-header', + templateUrl: './header.component.html', + styleUrls: ['./header.component.scss'], }) export class HeaderComponent { - @Output() logOutEvent = new EventEmitter(); - @Input() username: string | null = ''; + @Output() logOutEvent = new EventEmitter(); + @Input() username: string | null = ''; - logout() { - this.logOutEvent.emit(); - } + logout() { + this.logOutEvent.emit(); + } } diff --git a/src/app/core/components/header/header.stories.ts b/src/app/core/components/header/header.stories.ts index 65c88b90ad..41e063e96c 100644 --- a/src/app/core/components/header/header.stories.ts +++ b/src/app/core/components/header/header.stories.ts @@ -3,17 +3,17 @@ import { Meta, Story } from '@storybook/angular'; import { HeaderComponent } from './header.component'; export default { - title: 'Header', - component: HeaderComponent, - argTypes: { logOut: { action: 'clicked' } } + title: 'Header', + component: HeaderComponent, + argTypes: { logOut: { action: 'clicked' } }, } as Meta; const Template: Story = () => ({ - props: {} + props: {}, }); export const Primary: Story = () => ({ - props: { - username: 'username' - } + props: { + username: 'username', + }, }); diff --git a/src/app/core/components/page-not-found/page-not-found.component.spec.ts b/src/app/core/components/page-not-found/page-not-found.component.spec.ts index 70b685ca88..c1d2ff83e5 100644 --- a/src/app/core/components/page-not-found/page-not-found.component.spec.ts +++ b/src/app/core/components/page-not-found/page-not-found.component.spec.ts @@ -2,22 +2,22 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { PageNotFoundComponent } from './page-not-found.component'; describe('PageNotFoundComponent', () => { - let component: PageNotFoundComponent; - let fixture: ComponentFixture; + let component: PageNotFoundComponent; + let fixture: ComponentFixture; - beforeEach(async () => { - await TestBed.configureTestingModule({ - declarations: [PageNotFoundComponent], - }).compileComponents(); - }); + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [PageNotFoundComponent], + }).compileComponents(); + }); - beforeEach(() => { - fixture = TestBed.createComponent(PageNotFoundComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); + beforeEach(() => { + fixture = TestBed.createComponent(PageNotFoundComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); }); diff --git a/src/app/core/components/page-not-found/page-not-found.component.ts b/src/app/core/components/page-not-found/page-not-found.component.ts index c2b9a0c43f..84b644b2e8 100644 --- a/src/app/core/components/page-not-found/page-not-found.component.ts +++ b/src/app/core/components/page-not-found/page-not-found.component.ts @@ -1,8 +1,8 @@ import { ChangeDetectionStrategy, Component } from '@angular/core'; @Component({ - selector: 'app-page-not-found', - templateUrl: './page-not-found.component.html', - changeDetection: ChangeDetectionStrategy.OnPush, + selector: 'app-page-not-found', + templateUrl: './page-not-found.component.html', + changeDetection: ChangeDetectionStrategy.OnPush, }) export class PageNotFoundComponent {} diff --git a/src/app/core/components/phase-banner/phase-banner.component.spec.ts b/src/app/core/components/phase-banner/phase-banner.component.spec.ts index 9589c37477..779d4b39f0 100644 --- a/src/app/core/components/phase-banner/phase-banner.component.spec.ts +++ b/src/app/core/components/phase-banner/phase-banner.component.spec.ts @@ -3,22 +3,22 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { PhaseBannerComponent } from './phase-banner.component'; describe('PhaseBannerComponent', () => { - let component: PhaseBannerComponent; - let fixture: ComponentFixture; + let component: PhaseBannerComponent; + let fixture: ComponentFixture; - beforeEach(async () => { - await TestBed.configureTestingModule({ - declarations: [PhaseBannerComponent], - }).compileComponents(); - }); + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [PhaseBannerComponent], + }).compileComponents(); + }); - beforeEach(() => { - fixture = TestBed.createComponent(PhaseBannerComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); + beforeEach(() => { + fixture = TestBed.createComponent(PhaseBannerComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); }); diff --git a/src/app/core/components/phase-banner/phase-banner.component.ts b/src/app/core/components/phase-banner/phase-banner.component.ts index 794aa3501d..4d1ed4b6de 100644 --- a/src/app/core/components/phase-banner/phase-banner.component.ts +++ b/src/app/core/components/phase-banner/phase-banner.component.ts @@ -2,13 +2,13 @@ import { ChangeDetectionStrategy, Component } from '@angular/core'; import { environment } from '../../../../environments/environment'; @Component({ - selector: 'app-phase-banner', - templateUrl: './phase-banner.component.html', - styleUrls: ['./phase-banner.component.scss'], - changeDetection: ChangeDetectionStrategy.OnPush, + selector: 'app-phase-banner', + templateUrl: './phase-banner.component.html', + styleUrls: ['./phase-banner.component.scss'], + changeDetection: ChangeDetectionStrategy.OnPush, }) export class PhaseBannerComponent { - get feedbackUri(): string { - return environment.FEEDBACK_URI; - } + get feedbackUri(): string { + return environment.FEEDBACK_URI; + } } diff --git a/src/app/core/components/server-error/server-error.component.spec.ts b/src/app/core/components/server-error/server-error.component.spec.ts index a2a2b5c421..10a1576600 100644 --- a/src/app/core/components/server-error/server-error.component.spec.ts +++ b/src/app/core/components/server-error/server-error.component.spec.ts @@ -2,22 +2,22 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ServerErrorComponent } from './server-error.component'; describe('ServerErrorComponent', () => { - let component: ServerErrorComponent; - let fixture: ComponentFixture; + let component: ServerErrorComponent; + let fixture: ComponentFixture; - beforeEach(async () => { - await TestBed.configureTestingModule({ - declarations: [ServerErrorComponent], - }).compileComponents(); - }); + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ServerErrorComponent], + }).compileComponents(); + }); - beforeEach(() => { - fixture = TestBed.createComponent(ServerErrorComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); + beforeEach(() => { + fixture = TestBed.createComponent(ServerErrorComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); }); diff --git a/src/app/core/components/server-error/server-error.component.ts b/src/app/core/components/server-error/server-error.component.ts index 858e30837f..ed6c5fa435 100644 --- a/src/app/core/components/server-error/server-error.component.ts +++ b/src/app/core/components/server-error/server-error.component.ts @@ -1,7 +1,7 @@ import { Component } from '@angular/core'; @Component({ - selector: 'app-server-error', - templateUrl: './server-error.component.html', + selector: 'app-server-error', + templateUrl: './server-error.component.html', }) export class ServerErrorComponent {} diff --git a/src/app/core/components/spinner/spinner.component.spec.ts b/src/app/core/components/spinner/spinner.component.spec.ts index 42bf126ea1..e006a08470 100644 --- a/src/app/core/components/spinner/spinner.component.spec.ts +++ b/src/app/core/components/spinner/spinner.component.spec.ts @@ -2,37 +2,37 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { SpinnerComponent } from './spinner.component'; describe('SpinnerComponent', () => { - let component: SpinnerComponent; - let fixture: ComponentFixture; - let spinner: HTMLElement; + let component: SpinnerComponent; + let fixture: ComponentFixture; + let spinner: HTMLElement; - beforeEach(async () => { - await TestBed.configureTestingModule({ - declarations: [SpinnerComponent], - }).compileComponents(); + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [SpinnerComponent], + }).compileComponents(); - fixture = TestBed.createComponent(SpinnerComponent); - component = fixture.componentInstance; - }); + fixture = TestBed.createComponent(SpinnerComponent); + component = fixture.componentInstance; + }); - it('should create', () => { - expect(component).toBeTruthy(); - // Default should not show - spinner = fixture.debugElement.nativeElement.querySelector('.spinner'); - expect(spinner).toBeNull(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + // Default should not show + spinner = fixture.debugElement.nativeElement.querySelector('.spinner'); + expect(spinner).toBeNull(); + }); - it('should show', () => { - component.loading = true; - fixture.detectChanges(); - spinner = fixture.debugElement.nativeElement.querySelector('.spinner'); - expect(spinner).toBeTruthy(); - }); + it('should show', () => { + component.loading = true; + fixture.detectChanges(); + spinner = fixture.debugElement.nativeElement.querySelector('.spinner'); + expect(spinner).toBeTruthy(); + }); - it('should NOT show', () => { - component.loading = false; - fixture.detectChanges(); - spinner = fixture.debugElement.nativeElement.querySelector('.spinner'); - expect(spinner).toBeNull(); - }); + it('should NOT show', () => { + component.loading = false; + fixture.detectChanges(); + spinner = fixture.debugElement.nativeElement.querySelector('.spinner'); + expect(spinner).toBeNull(); + }); }); diff --git a/src/app/core/components/spinner/spinner.component.ts b/src/app/core/components/spinner/spinner.component.ts index 2b8ad6e335..aab8faeb28 100644 --- a/src/app/core/components/spinner/spinner.component.ts +++ b/src/app/core/components/spinner/spinner.component.ts @@ -1,11 +1,11 @@ import { ChangeDetectionStrategy, Component, Input } from '@angular/core'; @Component({ - selector: 'app-spinner', - templateUrl: './spinner.component.html', - styleUrls: ['./spinner.component.scss'], - changeDetection: ChangeDetectionStrategy.OnPush, + selector: 'app-spinner', + templateUrl: './spinner.component.html', + styleUrls: ['./spinner.component.scss'], + changeDetection: ChangeDetectionStrategy.OnPush, }) export class SpinnerComponent { - @Input() loading: boolean | null = false; + @Input() loading: boolean | null = false; } diff --git a/src/app/core/core.module.ts b/src/app/core/core.module.ts index e33d2c0891..7df1a16490 100644 --- a/src/app/core/core.module.ts +++ b/src/app/core/core.module.ts @@ -1,37 +1,37 @@ -import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; +import { NgModule } from '@angular/core'; import { RouterModule } from '@angular/router'; +import { BreadcrumbsComponent } from './components/breadcrumbs/breadcrumbs.component'; +import { FooterComponent } from './components/footer/footer.component'; import { GlobalErrorComponent } from './components/global-error/global-error.component'; import { GlobalWarningComponent } from './components/global-warning/global-warning.component'; -import { SpinnerComponent } from './components/spinner/spinner.component'; -import { FooterComponent } from './components/footer/footer.component'; import { HeaderComponent } from './components/header/header.component'; -import { BreadcrumbsComponent } from './components/breadcrumbs/breadcrumbs.component'; import { PageNotFoundComponent } from './components/page-not-found/page-not-found.component'; -import { ServerErrorComponent } from './components/server-error/server-error.component'; import { PhaseBannerComponent } from './components/phase-banner/phase-banner.component'; +import { ServerErrorComponent } from './components/server-error/server-error.component'; +import { SpinnerComponent } from './components/spinner/spinner.component'; @NgModule({ - declarations: [ - FooterComponent, - HeaderComponent, - GlobalErrorComponent, - GlobalWarningComponent, - SpinnerComponent, - BreadcrumbsComponent, - PageNotFoundComponent, - ServerErrorComponent, - PhaseBannerComponent, - ], - imports: [CommonModule, RouterModule], - exports: [ - FooterComponent, - HeaderComponent, - GlobalErrorComponent, - GlobalWarningComponent, - SpinnerComponent, - BreadcrumbsComponent, - PhaseBannerComponent, - ], + declarations: [ + FooterComponent, + HeaderComponent, + GlobalErrorComponent, + GlobalWarningComponent, + SpinnerComponent, + BreadcrumbsComponent, + PageNotFoundComponent, + ServerErrorComponent, + PhaseBannerComponent, + ], + imports: [CommonModule, RouterModule], + exports: [ + FooterComponent, + HeaderComponent, + GlobalErrorComponent, + GlobalWarningComponent, + SpinnerComponent, + BreadcrumbsComponent, + PhaseBannerComponent, + ], }) export class CoreModule {} diff --git a/src/app/directives/app-role-required.directive.spec.ts b/src/app/directives/app-role-required.directive.spec.ts index 141702b734..a588898f5b 100644 --- a/src/app/directives/app-role-required.directive.spec.ts +++ b/src/app/directives/app-role-required.directive.spec.ts @@ -9,7 +9,7 @@ import { of } from 'rxjs'; import { RoleRequiredDirective } from './app-role-required.directive'; @Component({ - template: ` + template: `

This can display

@@ -25,64 +25,76 @@ import { RoleRequiredDirective } from './app-role-required.directive'; `, }) class TestComponent { - public get Roles() { - return Roles; - } + public get Roles() { + return Roles; + } } describe('RoleRequiredDirective', () => { - const userRoles: string[] = ['TechRecord.View']; - let fixture: ComponentFixture; + const userRoles: string[] = ['TechRecord.View']; + let fixture: ComponentFixture; - beforeEach(() => { - fixture = TestBed.configureTestingModule({ - declarations: [RoleRequiredDirective, TestComponent], - providers: [provideMockStore({ initialState: initialAppState }), { provide: UserService, useValue: { roles$: of(userRoles) } }], - }).createComponent(TestComponent); + beforeEach(() => { + fixture = TestBed.configureTestingModule({ + declarations: [RoleRequiredDirective, TestComponent], + providers: [ + provideMockStore({ initialState: initialAppState }), + { provide: UserService, useValue: { roles$: of(userRoles) } }, + ], + }).createComponent(TestComponent); - fixture.detectChanges(); // initial binding - }); + fixture.detectChanges(); // initial binding + }); - it('should display the element', () => { - const seenBox = fixture.debugElement.queryAll(By.css('#displayBox')); - expect(seenBox).toHaveLength(1); - }); + it('should display the element', () => { + const seenBox = fixture.debugElement.queryAll(By.css('#displayBox')); + expect(seenBox).toHaveLength(1); + }); - it('should hide the element', () => { - const hiddenBox = fixture.debugElement.queryAll(By.css('#hiddenBox')); - expect(hiddenBox).toHaveLength(0); - }); + it('should hide the element', () => { + const hiddenBox = fixture.debugElement.queryAll(By.css('#hiddenBox')); + expect(hiddenBox).toHaveLength(0); + }); - it('should hide the element if the role needed is invalid (i.e. not from the Roles enum)', () => { - const errorBox = fixture.debugElement.queryAll(By.css('#errorBox')); - expect(errorBox).toHaveLength(0); - }); + it('should hide the element if the role needed is invalid (i.e. not from the Roles enum)', () => { + const errorBox = fixture.debugElement.queryAll(By.css('#errorBox')); + expect(errorBox).toHaveLength(0); + }); }); describe('RoleRequiredDirective with multiple optional roles', () => { - it.each([[['TestResult.View']], [['TechRecord.Amend']]])('should show the element when either role is present', (user) => { - const fixture: ComponentFixture = TestBed.configureTestingModule({ - declarations: [RoleRequiredDirective, TestComponent], - providers: [provideMockStore({ initialState: initialAppState }), { provide: UserService, useValue: { roles$: of(user) } }], - }).createComponent(TestComponent); + it.each([[['TestResult.View']], [['TechRecord.Amend']]])( + 'should show the element when either role is present', + (user) => { + const fixture: ComponentFixture = TestBed.configureTestingModule({ + declarations: [RoleRequiredDirective, TestComponent], + providers: [ + provideMockStore({ initialState: initialAppState }), + { provide: UserService, useValue: { roles$: of(user) } }, + ], + }).createComponent(TestComponent); - fixture.detectChanges(); // initial binding + fixture.detectChanges(); // initial binding - const seenBox = fixture.debugElement.queryAll(By.css('#displayEitherRoleBox')); - expect(seenBox).toHaveLength(1); - }); + const seenBox = fixture.debugElement.queryAll(By.css('#displayEitherRoleBox')); + expect(seenBox).toHaveLength(1); + } + ); }); describe('RoleRequiredDirective without roles', () => { - it('should hide the element when no roles are available', () => { - const fixture: ComponentFixture = TestBed.configureTestingModule({ - declarations: [RoleRequiredDirective, TestComponent], - providers: [provideMockStore({ initialState: initialAppState }), { provide: UserService, useValue: { roles$: of(null) } }], - }).createComponent(TestComponent); + it('should hide the element when no roles are available', () => { + const fixture: ComponentFixture = TestBed.configureTestingModule({ + declarations: [RoleRequiredDirective, TestComponent], + providers: [ + provideMockStore({ initialState: initialAppState }), + { provide: UserService, useValue: { roles$: of(null) } }, + ], + }).createComponent(TestComponent); - fixture.detectChanges(); // initial binding + fixture.detectChanges(); // initial binding - const seenBox = fixture.debugElement.queryAll(By.css('#errorBox')); - expect(seenBox).toHaveLength(0); - }); + const seenBox = fixture.debugElement.queryAll(By.css('#errorBox')); + expect(seenBox).toHaveLength(0); + }); }); diff --git a/src/app/directives/app-role-required.directive.ts b/src/app/directives/app-role-required.directive.ts index 6635e00a94..4af54d36b4 100644 --- a/src/app/directives/app-role-required.directive.ts +++ b/src/app/directives/app-role-required.directive.ts @@ -1,33 +1,34 @@ -import { - Directive, - Input, - OnInit, - TemplateRef, ViewContainerRef, -} from '@angular/core'; +import { Directive, Input, OnInit, TemplateRef, ViewContainerRef } from '@angular/core'; import { Roles } from '@models/roles.enum'; import { UserService } from '@services/user-service/user-service'; import { take } from 'rxjs'; @Directive({ selector: '[appRoleRequired]' }) export class RoleRequiredDirective implements OnInit { - constructor(private templateRef: TemplateRef, private userService: UserService, private viewContainer: ViewContainerRef) {} + constructor( + private templateRef: TemplateRef, + private userService: UserService, + private viewContainer: ViewContainerRef + ) {} - userRolesRequired: string[] | undefined; + userRolesRequired: string[] | undefined; - @Input() - set appRoleRequired(roles: Roles | Roles[]) { - this.userRolesRequired = Array.isArray(roles) ? [...new Set(roles.flatMap((role) => role.split(',')))] : roles?.split(','); - } + @Input() + set appRoleRequired(roles: Roles | Roles[]) { + this.userRolesRequired = Array.isArray(roles) + ? [...new Set(roles.flatMap((role) => role.split(',')))] + : roles?.split(','); + } - ngOnInit() { - this.userService.roles$.pipe(take(1)).subscribe((storedRoles) => { - const hasAccess = this.userRolesRequired?.some((role) => storedRoles?.includes(role)); + ngOnInit() { + this.userService.roles$.pipe(take(1)).subscribe((storedRoles) => { + const hasAccess = this.userRolesRequired?.some((role) => storedRoles?.includes(role)); - if (hasAccess) { - this.viewContainer.createEmbeddedView(this.templateRef); - } else { - this.viewContainer.clear(); - } - }); - } + if (hasAccess) { + this.viewContainer.createEmbeddedView(this.templateRef); + } else { + this.viewContainer.clear(); + } + }); + } } diff --git a/src/app/directives/feature-toggle.directive.spec.ts b/src/app/directives/feature-toggle.directive.spec.ts index 6125da2394..a740497a80 100644 --- a/src/app/directives/feature-toggle.directive.spec.ts +++ b/src/app/directives/feature-toggle.directive.spec.ts @@ -6,7 +6,7 @@ import { FeatureToggleService } from '@services/feature-toggle-service/feature-t import { FeatureToggleDirective } from './feature-toggle.directive'; @Component({ - template: ` + template: `

This does display

@@ -24,38 +24,38 @@ import { FeatureToggleDirective } from './feature-toggle.directive'; class TestComponent {} describe('FeatureToggleDirective', () => { - let fixture: ComponentFixture; - let service: FeatureToggleService; - - beforeEach(() => { - fixture = TestBed.configureTestingModule({ - declarations: [FeatureToggleDirective, TestComponent], - providers: [FeatureToggleService, HttpClient, HttpHandler], - }).createComponent(TestComponent); - - service = TestBed.inject(FeatureToggleService); - service.config = { testToggleEnabled: true, testToggleDisabled: false }; - - fixture.detectChanges(); // initial binding - }); - - it('should be able to see the enabled toggled state', () => { - const seenBox = fixture.debugElement.queryAll(By.css('#testToggleEnabled')); - expect(seenBox).toHaveLength(1); - }); - - it('should not be able to see the disabled toggled state', () => { - const seenBox = fixture.debugElement.queryAll(By.css('#testToggleDisabled')); - expect(seenBox).toEqual([]); - }); - - it('should not be able to see the random key state', () => { - const seenBox = fixture.debugElement.queryAll(By.css('#randomKey')); - expect(seenBox).toEqual([]); - }); - - it('should be able to see with no directive', () => { - const seenBox = fixture.debugElement.queryAll(By.css('#noToggle')); - expect(seenBox).toHaveLength(1); - }); + let fixture: ComponentFixture; + let service: FeatureToggleService; + + beforeEach(() => { + fixture = TestBed.configureTestingModule({ + declarations: [FeatureToggleDirective, TestComponent], + providers: [FeatureToggleService, HttpClient, HttpHandler], + }).createComponent(TestComponent); + + service = TestBed.inject(FeatureToggleService); + service.config = { testToggleEnabled: true, testToggleDisabled: false }; + + fixture.detectChanges(); // initial binding + }); + + it('should be able to see the enabled toggled state', () => { + const seenBox = fixture.debugElement.queryAll(By.css('#testToggleEnabled')); + expect(seenBox).toHaveLength(1); + }); + + it('should not be able to see the disabled toggled state', () => { + const seenBox = fixture.debugElement.queryAll(By.css('#testToggleDisabled')); + expect(seenBox).toEqual([]); + }); + + it('should not be able to see the random key state', () => { + const seenBox = fixture.debugElement.queryAll(By.css('#randomKey')); + expect(seenBox).toEqual([]); + }); + + it('should be able to see with no directive', () => { + const seenBox = fixture.debugElement.queryAll(By.css('#noToggle')); + expect(seenBox).toHaveLength(1); + }); }); diff --git a/src/app/directives/feature-toggle.directive.ts b/src/app/directives/feature-toggle.directive.ts index 9708b9787d..bc1d9c3b4d 100644 --- a/src/app/directives/feature-toggle.directive.ts +++ b/src/app/directives/feature-toggle.directive.ts @@ -1,22 +1,20 @@ -import { - Directive, Input, OnInit, TemplateRef, ViewContainerRef, -} from '@angular/core'; +import { Directive, Input, OnInit, TemplateRef, ViewContainerRef } from '@angular/core'; import { FeatureToggleService } from '@services/feature-toggle-service/feature-toggle-service'; @Directive({ selector: '[featureToggleName]' }) export class FeatureToggleDirective implements OnInit { - @Input() featureToggleName!: string; + @Input() featureToggleName!: string; - constructor( - private templateRef: TemplateRef, - private featureToggleService: FeatureToggleService, - private viewContainer: ViewContainerRef, - ) {} + constructor( + private templateRef: TemplateRef, + private featureToggleService: FeatureToggleService, + private viewContainer: ViewContainerRef + ) {} - ngOnInit() { - const isEnabled = this.featureToggleService.isFeatureEnabled(this.featureToggleName); - if (isEnabled) { - this.viewContainer.createEmbeddedView(this.templateRef); - } - } + ngOnInit() { + const isEnabled = this.featureToggleService.isFeatureEnabled(this.featureToggleName); + if (isEnabled) { + this.viewContainer.createEmbeddedView(this.templateRef); + } + } } diff --git a/src/app/features/feature-toggle/feature-toggle-routing.module.ts b/src/app/features/feature-toggle/feature-toggle-routing.module.ts index 5452f68002..ae9465ab06 100644 --- a/src/app/features/feature-toggle/feature-toggle-routing.module.ts +++ b/src/app/features/feature-toggle/feature-toggle-routing.module.ts @@ -5,16 +5,16 @@ import { FeatureToggleGuard } from '@guards/feature-toggle-guard/feature-toggle. import { FeatureToggleComponent } from './feature-toggle/feature-toggle.component'; const routes = [ - { - path: '', - component: FeatureToggleComponent, - data: { title: 'Feature Toggle', featureToggleName: 'testToggle' }, - canActivate: [MsalGuard, FeatureToggleGuard], - }, + { + path: '', + component: FeatureToggleComponent, + data: { title: 'Feature Toggle', featureToggleName: 'testToggle' }, + canActivate: [MsalGuard, FeatureToggleGuard], + }, ]; @NgModule({ - imports: [RouterModule.forChild(routes)], - exports: [RouterModule], + imports: [RouterModule.forChild(routes)], + exports: [RouterModule], }) export class FeatureToggleRoutingModule {} diff --git a/src/app/features/feature-toggle/feature-toggle.module.ts b/src/app/features/feature-toggle/feature-toggle.module.ts index f1637312e7..49cd43249c 100644 --- a/src/app/features/feature-toggle/feature-toggle.module.ts +++ b/src/app/features/feature-toggle/feature-toggle.module.ts @@ -5,9 +5,7 @@ import { FeatureToggleRoutingModule } from './feature-toggle-routing.module'; import { FeatureToggleComponent } from './feature-toggle/feature-toggle.component'; @NgModule({ - declarations: [FeatureToggleComponent], - imports: [ - CommonModule, SharedModule, FeatureToggleRoutingModule, - ], + declarations: [FeatureToggleComponent], + imports: [CommonModule, SharedModule, FeatureToggleRoutingModule], }) -export class FeatureToggleModule { } +export class FeatureToggleModule {} diff --git a/src/app/features/feature-toggle/feature-toggle/feature-toggle.component.ts b/src/app/features/feature-toggle/feature-toggle/feature-toggle.component.ts index 7e5f4b5eb4..97df34703e 100644 --- a/src/app/features/feature-toggle/feature-toggle/feature-toggle.component.ts +++ b/src/app/features/feature-toggle/feature-toggle/feature-toggle.component.ts @@ -1,8 +1,7 @@ import { Component } from '@angular/core'; @Component({ - selector: 'feature-toggle', - templateUrl: './feature-toggle.component.html', + selector: 'feature-toggle', + templateUrl: './feature-toggle.component.html', }) -export class FeatureToggleComponent { -} +export class FeatureToggleComponent {} diff --git a/src/app/features/home/components/home-button/home-button.component.spec.ts b/src/app/features/home/components/home-button/home-button.component.spec.ts index 1331308687..e7d4916c58 100644 --- a/src/app/features/home/components/home-button/home-button.component.spec.ts +++ b/src/app/features/home/components/home-button/home-button.component.spec.ts @@ -3,23 +3,23 @@ import { RouterTestingModule } from '@angular/router/testing'; import { HomeButtonComponent } from './home-button.component'; describe('HomeButtonComponent', () => { - let component: HomeButtonComponent; - let fixture: ComponentFixture; + let component: HomeButtonComponent; + let fixture: ComponentFixture; - beforeEach(async () => { - await TestBed.configureTestingModule({ - declarations: [HomeButtonComponent], - imports: [RouterTestingModule], - }).compileComponents(); - }); + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [HomeButtonComponent], + imports: [RouterTestingModule], + }).compileComponents(); + }); - beforeEach(() => { - fixture = TestBed.createComponent(HomeButtonComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); + beforeEach(() => { + fixture = TestBed.createComponent(HomeButtonComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); }); diff --git a/src/app/features/home/components/home-button/home-button.component.ts b/src/app/features/home/components/home-button/home-button.component.ts index 49ade3c0e7..5cdefb047d 100644 --- a/src/app/features/home/components/home-button/home-button.component.ts +++ b/src/app/features/home/components/home-button/home-button.component.ts @@ -1,13 +1,13 @@ import { Component, Input } from '@angular/core'; @Component({ - selector: 'app-home-button', - templateUrl: './home-button.component.html', - styleUrls: ['./home-button.component.scss'], + selector: 'app-home-button', + templateUrl: './home-button.component.html', + styleUrls: ['./home-button.component.scss'], }) export class HomeButtonComponent { - @Input() url = ''; - @Input() linkText = ''; - @Input() description = ''; - @Input() linkId = ''; + @Input() url = ''; + @Input() linkText = ''; + @Input() description = ''; + @Input() linkId = ''; } diff --git a/src/app/features/home/components/home-button/home-button.stories.ts b/src/app/features/home/components/home-button/home-button.stories.ts index 5819289225..193ae30a66 100644 --- a/src/app/features/home/components/home-button/home-button.stories.ts +++ b/src/app/features/home/components/home-button/home-button.stories.ts @@ -3,10 +3,10 @@ import { Meta, Story } from '@storybook/angular'; import { HomeButtonComponent } from './home-button.component'; export default { - title: 'Home Button', - component: HomeButtonComponent + title: 'Home Button', + component: HomeButtonComponent, } as Meta; export const Primary: Story = () => ({ - props: {} + props: {}, }); diff --git a/src/app/features/home/home-routing.module.ts b/src/app/features/home/home-routing.module.ts index a0f5430231..8150ff3cbf 100644 --- a/src/app/features/home/home-routing.module.ts +++ b/src/app/features/home/home-routing.module.ts @@ -3,15 +3,15 @@ import { RouterModule, Routes } from '@angular/router'; import { HomeComponent } from './home.component'; const routes: Routes = [ - { - path: '', - pathMatch: 'prefix', - component: HomeComponent, - }, + { + path: '', + pathMatch: 'prefix', + component: HomeComponent, + }, ]; @NgModule({ - imports: [RouterModule.forChild(routes)], - exports: [RouterModule], + imports: [RouterModule.forChild(routes)], + exports: [RouterModule], }) export class HomeRoutingModule {} diff --git a/src/app/features/home/home.component.spec.ts b/src/app/features/home/home.component.spec.ts index 31eee3e014..3d3e56ae45 100644 --- a/src/app/features/home/home.component.spec.ts +++ b/src/app/features/home/home.component.spec.ts @@ -12,34 +12,34 @@ import { HomeButtonComponent } from './components/home-button/home-button.compon import { HomeComponent } from './home.component'; describe('HomeComponent', () => { - let component: HomeComponent; - let fixture: ComponentFixture; - const actions$ = new ReplaySubject(); + let component: HomeComponent; + let fixture: ComponentFixture; + const actions$ = new ReplaySubject(); - beforeEach(async () => { - await TestBed.configureTestingModule({ - declarations: [HomeComponent, HomeButtonComponent, RoleRequiredDirective], - imports: [RouterTestingModule, SharedModule, HttpClientModule], - providers: [ - FormBuilder, - provideMockActions(() => actions$), - { - provide: UserService, - useValue: { - roles$: of(['TechRecord.View']), - }, - }, - ], - }).compileComponents(); - }); + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [HomeComponent, HomeButtonComponent, RoleRequiredDirective], + imports: [RouterTestingModule, SharedModule, HttpClientModule], + providers: [ + FormBuilder, + provideMockActions(() => actions$), + { + provide: UserService, + useValue: { + roles$: of(['TechRecord.View']), + }, + }, + ], + }).compileComponents(); + }); - beforeEach(() => { - fixture = TestBed.createComponent(HomeComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); + beforeEach(() => { + fixture = TestBed.createComponent(HomeComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); }); diff --git a/src/app/features/home/home.component.ts b/src/app/features/home/home.component.ts index 52e87cf706..f44b128606 100644 --- a/src/app/features/home/home.component.ts +++ b/src/app/features/home/home.component.ts @@ -2,11 +2,11 @@ import { Component } from '@angular/core'; import { Roles } from '@models/roles.enum'; @Component({ - selector: 'app-home', - templateUrl: './home.component.html', + selector: 'app-home', + templateUrl: './home.component.html', }) export class HomeComponent { - public get Roles() { - return Roles; - } + public get Roles() { + return Roles; + } } diff --git a/src/app/features/home/home.module.ts b/src/app/features/home/home.module.ts index 42ba21d5f6..a129d43e69 100644 --- a/src/app/features/home/home.module.ts +++ b/src/app/features/home/home.module.ts @@ -6,7 +6,7 @@ import { HomeRoutingModule } from './home-routing.module'; import { HomeComponent } from './home.component'; @NgModule({ - declarations: [HomeComponent, HomeButtonComponent], - imports: [CommonModule, HomeRoutingModule, SharedModule], + declarations: [HomeComponent, HomeButtonComponent], + imports: [CommonModule, HomeRoutingModule, SharedModule], }) export class HomeModule {} diff --git a/src/app/features/home/home.stories.ts b/src/app/features/home/home.stories.ts index ddd825dcc9..d6aaa85fd3 100644 --- a/src/app/features/home/home.stories.ts +++ b/src/app/features/home/home.stories.ts @@ -3,10 +3,10 @@ import { Meta, Story } from '@storybook/angular'; import { HomeComponent } from './home.component'; export default { - title: 'Home Page', - component: HomeComponent + title: 'Home Page', + component: HomeComponent, } as Meta; export const Primary: Story = () => ({ - props: {} + props: {}, }); diff --git a/src/app/features/reference-data/reference-data-add/reference-data-add.component.spec.ts b/src/app/features/reference-data/reference-data-add/reference-data-add.component.spec.ts index c5509678e8..5ef1ceacce 100644 --- a/src/app/features/reference-data/reference-data-add/reference-data-add.component.spec.ts +++ b/src/app/features/reference-data/reference-data-add/reference-data-add.component.spec.ts @@ -11,109 +11,109 @@ import { of } from 'rxjs'; import { ReferenceDataCreateComponent } from './reference-data-add.component'; const mockRefDataService = { - loadReferenceData: jest.fn(), - loadReferenceDataByKey: jest.fn(), - fetchReferenceDataByKey: jest.fn(), + loadReferenceData: jest.fn(), + loadReferenceDataByKey: jest.fn(), + fetchReferenceDataByKey: jest.fn(), }; describe('ReferenceDataCreateComponent', () => { - let component: ReferenceDataCreateComponent; - let fixture: ComponentFixture; - let store: MockStore; - let router: Router; - let route: ActivatedRoute; - let errorService: GlobalErrorService; - - beforeEach(async () => { - await TestBed.configureTestingModule({ - declarations: [ReferenceDataCreateComponent], - imports: [RouterTestingModule, HttpClientTestingModule], - providers: [ - GlobalErrorService, - provideMockStore({ initialState: initialAppState }), - ReferenceDataService, - { provide: UserService, useValue: {} }, - { provide: ReferenceDataService, useValue: mockRefDataService }, - ], - }).compileComponents(); - }); - - beforeEach(() => { - store = TestBed.inject(MockStore); - fixture = TestBed.createComponent(ReferenceDataCreateComponent); - component = fixture.componentInstance; - router = TestBed.inject(Router); - fixture.detectChanges(); - errorService = TestBed.inject(GlobalErrorService); - route = TestBed.inject(ActivatedRoute); - fixture.detectChanges(); - component.checkForms = jest.fn(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); - - describe('navigateBack', () => { - it('should clear all errors', () => { - jest.spyOn(router, 'navigate').mockImplementation(); - - const clearErrorsSpy = jest.spyOn(errorService, 'clearErrors'); - - component.navigateBack(); - - expect(clearErrorsSpy).toHaveBeenCalledTimes(1); - }); - - it('should navigate back to the previous page', () => { - const navigateSpy = jest.spyOn(router, 'navigate').mockImplementation(() => Promise.resolve(true)); - - component.navigateBack(); - - expect(navigateSpy).toHaveBeenCalledWith(['..'], { relativeTo: route }); - }); - }); - - describe('handleFormChange', () => { - it('should set newRefData', () => { - component.handleFormChange({ foo: 'bar' }); - - expect(component.newRefData).toEqual({ foo: 'bar' }); - }); - }); - - describe('handleSubmit', () => { - it('should dispatch if form is valid', () => { - fixture.ngZone?.run(() => { - component.newRefData = { description: 'test' }; - jest.spyOn(component, 'checkForms').mockImplementationOnce(() => { - component.isFormInvalid = false; - }); - const dispatch = jest.spyOn(store, 'dispatch'); - - component.handleSubmit(); - - expect(dispatch).toHaveBeenCalled(); - }); - }); - it('should not dispatch if form is invalid', () => { - jest.spyOn(component, 'checkForms').mockImplementationOnce(() => { - component.isFormInvalid = true; - }); - const dispatch = jest.spyOn(store, 'dispatch'); - - component.handleSubmit(); - - expect(dispatch).not.toHaveBeenCalled(); - }); - it('should not dispatch if ref data already exists', () => { - jest.spyOn(mockRefDataService, 'fetchReferenceDataByKey').mockReturnValueOnce(of({ foo: 'bar' })); - - const dispatch = jest.spyOn(store, 'dispatch'); - - component.handleSubmit(); - - expect(dispatch).not.toHaveBeenCalled(); - }); - }); + let component: ReferenceDataCreateComponent; + let fixture: ComponentFixture; + let store: MockStore; + let router: Router; + let route: ActivatedRoute; + let errorService: GlobalErrorService; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ReferenceDataCreateComponent], + imports: [RouterTestingModule, HttpClientTestingModule], + providers: [ + GlobalErrorService, + provideMockStore({ initialState: initialAppState }), + ReferenceDataService, + { provide: UserService, useValue: {} }, + { provide: ReferenceDataService, useValue: mockRefDataService }, + ], + }).compileComponents(); + }); + + beforeEach(() => { + store = TestBed.inject(MockStore); + fixture = TestBed.createComponent(ReferenceDataCreateComponent); + component = fixture.componentInstance; + router = TestBed.inject(Router); + fixture.detectChanges(); + errorService = TestBed.inject(GlobalErrorService); + route = TestBed.inject(ActivatedRoute); + fixture.detectChanges(); + component.checkForms = jest.fn(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + describe('navigateBack', () => { + it('should clear all errors', () => { + jest.spyOn(router, 'navigate').mockImplementation(); + + const clearErrorsSpy = jest.spyOn(errorService, 'clearErrors'); + + component.navigateBack(); + + expect(clearErrorsSpy).toHaveBeenCalledTimes(1); + }); + + it('should navigate back to the previous page', () => { + const navigateSpy = jest.spyOn(router, 'navigate').mockImplementation(() => Promise.resolve(true)); + + component.navigateBack(); + + expect(navigateSpy).toHaveBeenCalledWith(['..'], { relativeTo: route }); + }); + }); + + describe('handleFormChange', () => { + it('should set newRefData', () => { + component.handleFormChange({ foo: 'bar' }); + + expect(component.newRefData).toEqual({ foo: 'bar' }); + }); + }); + + describe('handleSubmit', () => { + it('should dispatch if form is valid', () => { + fixture.ngZone?.run(() => { + component.newRefData = { description: 'test' }; + jest.spyOn(component, 'checkForms').mockImplementationOnce(() => { + component.isFormInvalid = false; + }); + const dispatch = jest.spyOn(store, 'dispatch'); + + component.handleSubmit(); + + expect(dispatch).toHaveBeenCalled(); + }); + }); + it('should not dispatch if form is invalid', () => { + jest.spyOn(component, 'checkForms').mockImplementationOnce(() => { + component.isFormInvalid = true; + }); + const dispatch = jest.spyOn(store, 'dispatch'); + + component.handleSubmit(); + + expect(dispatch).not.toHaveBeenCalled(); + }); + it('should not dispatch if ref data already exists', () => { + jest.spyOn(mockRefDataService, 'fetchReferenceDataByKey').mockReturnValueOnce(of({ foo: 'bar' })); + + const dispatch = jest.spyOn(store, 'dispatch'); + + component.handleSubmit(); + + expect(dispatch).not.toHaveBeenCalled(); + }); + }); }); diff --git a/src/app/features/reference-data/reference-data-add/reference-data-add.component.ts b/src/app/features/reference-data/reference-data-add/reference-data-add.component.ts index 8a4e09d012..c1624909a5 100644 --- a/src/app/features/reference-data/reference-data-add/reference-data-add.component.ts +++ b/src/app/features/reference-data/reference-data-add/reference-data-add.component.ts @@ -1,6 +1,4 @@ -import { - Component, OnInit, QueryList, ViewChildren, -} from '@angular/core'; +import { Component, OnInit, QueryList, ViewChildren } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; import { GlobalError } from '@core/components/global-error/global-error.interface'; import { GlobalErrorService } from '@core/components/global-error/global-error.service'; @@ -12,127 +10,131 @@ import { Roles } from '@models/roles.enum'; import { Store, select } from '@ngrx/store'; import { ReferenceDataService } from '@services/reference-data/reference-data.service'; import { ReferenceDataState, createReferenceDataItem, selectReferenceDataByResourceKey } from '@store/reference-data'; -import { - Observable, catchError, filter, of, switchMap, take, throwError, -} from 'rxjs'; +import { Observable, catchError, filter, of, switchMap, take, throwError } from 'rxjs'; @Component({ - selector: 'app-reference-data-add', - templateUrl: './reference-data-add.component.html', + selector: 'app-reference-data-add', + templateUrl: './reference-data-add.component.html', }) export class ReferenceDataCreateComponent implements OnInit { - type: ReferenceDataResourceType = ReferenceDataResourceType.Brakes; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - newRefData: any; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - data: any = {}; - isFormDirty = false; - isFormInvalid = true; - - @ViewChildren(DynamicFormGroupComponent) sections!: QueryList; - - constructor( - public globalErrorService: GlobalErrorService, - public dfs: DynamicFormService, - private referenceDataService: ReferenceDataService, - private route: ActivatedRoute, - private router: Router, - private store: Store, - ) {} - - ngOnInit(): void { - this.route.parent?.params.pipe(take(1)).subscribe((params) => { - this.type = params['type']; - this.referenceDataService.loadReferenceDataByKey(ReferenceDataResourceType.ReferenceDataAdminType, this.type); - }); - } - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - get refDataAdminType$(): Observable { - return this.store.pipe(select(selectReferenceDataByResourceKey(ReferenceDataResourceType.ReferenceDataAdminType, this.type))); - } - - get roles(): typeof Roles { - return Roles; - } - - get widths(): typeof FormNodeWidth { - return FormNodeWidth; - } - - navigateBack() { - this.globalErrorService.clearErrors(); - void this.router.navigate(['..'], { relativeTo: this.route }); - } - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - handleFormChange(event: any) { - this.newRefData = event; - } - - handleSubmit() { - this.checkForms(); - - if (this.isFormInvalid) return; - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const referenceData: any = {}; - - Object.keys(this.newRefData) - .filter((newRefDataKey) => newRefDataKey !== 'resourceKey') - .forEach((dataKey) => { - referenceData[`${dataKey}`] = this.newRefData[`${dataKey}`]; - }); - - this.globalErrorService.errors$ - .pipe( - take(1), - filter((errors) => !errors.length), - switchMap(() => this.referenceDataService.fetchReferenceDataByKey(this.type, this.newRefData.resourceKey)), - take(1), - catchError((error) => (error.status === 200 ? of(true) : throwError(() => new Error('Error')))), - ) - .subscribe({ - next: (res) => { - if (res) return this.globalErrorService.addError({ error: 'Resource Key already exists', anchorLink: 'newReferenceData' }); - }, - error: (e) => { - if (e.status === 404) { - of(true); - } else { - this.store.dispatch( - createReferenceDataItem({ - resourceType: this.type, - resourceKey: encodeURIComponent(String(this.newRefData.resourceKey)), - payload: referenceData, - }), - ); - this.navigateBack(); - } - }, - }); - } - - checkForms(): void { - const forms = this.sections.map((section) => section.form) as Array; - - this.isFormDirty = forms.some((form) => form.dirty); - - this.setErrors(forms); - - this.isFormInvalid = forms.some((form) => form.invalid); - } - - setErrors(forms: Array): void { - const errors: GlobalError[] = []; - - forms.forEach((form) => DynamicFormService.validate(form, errors)); - - if (errors.length) { - this.globalErrorService.setErrors(errors); - return; - } - - this.globalErrorService.clearErrors(); - } + type: ReferenceDataResourceType = ReferenceDataResourceType.Brakes; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + newRefData: any; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + data: any = {}; + isFormDirty = false; + isFormInvalid = true; + + @ViewChildren(DynamicFormGroupComponent) sections!: QueryList; + + constructor( + public globalErrorService: GlobalErrorService, + public dfs: DynamicFormService, + private referenceDataService: ReferenceDataService, + private route: ActivatedRoute, + private router: Router, + private store: Store + ) {} + + ngOnInit(): void { + this.route.parent?.params.pipe(take(1)).subscribe((params) => { + this.type = params['type']; + this.referenceDataService.loadReferenceDataByKey(ReferenceDataResourceType.ReferenceDataAdminType, this.type); + }); + } + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + get refDataAdminType$(): Observable { + return this.store.pipe( + select(selectReferenceDataByResourceKey(ReferenceDataResourceType.ReferenceDataAdminType, this.type)) + ); + } + + get roles(): typeof Roles { + return Roles; + } + + get widths(): typeof FormNodeWidth { + return FormNodeWidth; + } + + navigateBack() { + this.globalErrorService.clearErrors(); + void this.router.navigate(['..'], { relativeTo: this.route }); + } + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + handleFormChange(event: any) { + this.newRefData = event; + } + + handleSubmit() { + this.checkForms(); + + if (this.isFormInvalid) return; + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const referenceData: any = {}; + + Object.keys(this.newRefData) + .filter((newRefDataKey) => newRefDataKey !== 'resourceKey') + .forEach((dataKey) => { + referenceData[`${dataKey}`] = this.newRefData[`${dataKey}`]; + }); + + this.globalErrorService.errors$ + .pipe( + take(1), + filter((errors) => !errors.length), + switchMap(() => this.referenceDataService.fetchReferenceDataByKey(this.type, this.newRefData.resourceKey)), + take(1), + catchError((error) => (error.status === 200 ? of(true) : throwError(() => new Error('Error')))) + ) + .subscribe({ + next: (res) => { + if (res) + return this.globalErrorService.addError({ + error: 'Resource Key already exists', + anchorLink: 'newReferenceData', + }); + }, + error: (e) => { + if (e.status === 404) { + of(true); + } else { + this.store.dispatch( + createReferenceDataItem({ + resourceType: this.type, + resourceKey: encodeURIComponent(String(this.newRefData.resourceKey)), + payload: referenceData, + }) + ); + this.navigateBack(); + } + }, + }); + } + + checkForms(): void { + const forms = this.sections.map((section) => section.form) as Array; + + this.isFormDirty = forms.some((form) => form.dirty); + + this.setErrors(forms); + + this.isFormInvalid = forms.some((form) => form.invalid); + } + + setErrors(forms: Array): void { + const errors: GlobalError[] = []; + + forms.forEach((form) => DynamicFormService.validate(form, errors)); + + if (errors.length) { + this.globalErrorService.setErrors(errors); + return; + } + + this.globalErrorService.clearErrors(); + } } diff --git a/src/app/features/reference-data/reference-data-amend-history/reference-data-amend-history.component.spec.ts b/src/app/features/reference-data/reference-data-amend-history/reference-data-amend-history.component.spec.ts index 32488da71e..2b271cda86 100644 --- a/src/app/features/reference-data/reference-data-amend-history/reference-data-amend-history.component.spec.ts +++ b/src/app/features/reference-data/reference-data-amend-history/reference-data-amend-history.component.spec.ts @@ -8,33 +8,37 @@ import { initialAppState } from '@store/.'; import { ReferenceDataAmendHistoryComponent } from './reference-data-amend-history.component'; describe('ReferenceDataAmendHistoryComponent', () => { - let component: ReferenceDataAmendHistoryComponent; - let fixture: ComponentFixture; + let component: ReferenceDataAmendHistoryComponent; + let fixture: ComponentFixture; - beforeEach(async () => { - await TestBed.configureTestingModule({ - declarations: [ReferenceDataAmendHistoryComponent], - imports: [RouterTestingModule, HttpClientTestingModule], - providers: [provideMockStore({ initialState: initialAppState }), ReferenceDataService, { provide: UserService, useValue: {} }], - }).compileComponents(); - }); + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ReferenceDataAmendHistoryComponent], + imports: [RouterTestingModule, HttpClientTestingModule], + providers: [ + provideMockStore({ initialState: initialAppState }), + ReferenceDataService, + { provide: UserService, useValue: {} }, + ], + }).compileComponents(); + }); - beforeEach(() => { - fixture = TestBed.createComponent(ReferenceDataAmendHistoryComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); + beforeEach(() => { + fixture = TestBed.createComponent(ReferenceDataAmendHistoryComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); - describe('handlePaginationChange', () => { - it('should set pageStart and pageEnd', () => { - component.handlePaginationChange({ start: 7, end: 20 }); + describe('handlePaginationChange', () => { + it('should set pageStart and pageEnd', () => { + component.handlePaginationChange({ start: 7, end: 20 }); - expect(component.pageStart).toBe(7); - expect(component.pageEnd).toBe(20); - }); - }); + expect(component.pageStart).toBe(7); + expect(component.pageEnd).toBe(20); + }); + }); }); diff --git a/src/app/features/reference-data/reference-data-amend-history/reference-data-amend-history.component.ts b/src/app/features/reference-data/reference-data-amend-history/reference-data-amend-history.component.ts index 17ccaa9da8..3c4c1d6456 100644 --- a/src/app/features/reference-data/reference-data-amend-history/reference-data-amend-history.component.ts +++ b/src/app/features/reference-data/reference-data-amend-history/reference-data-amend-history.component.ts @@ -1,52 +1,57 @@ +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnInit } from '@angular/core'; import { - ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnInit, -} from '@angular/core'; -import { ReferenceDataAdminColumn, ReferenceDataModelBase, ReferenceDataResourceType } from '@models/reference-data.model'; + ReferenceDataAdminColumn, + ReferenceDataModelBase, + ReferenceDataResourceType, +} from '@models/reference-data.model'; import { Store, select } from '@ngrx/store'; import { ReferenceDataState, fetchReferenceDataByKeySearch, selectSearchReturn } from '@store/reference-data'; import { Observable, map } from 'rxjs'; @Component({ - selector: 'app-reference-data-amend-history', - templateUrl: './reference-data-amend-history.component.html', - changeDetection: ChangeDetectionStrategy.OnPush, + selector: 'app-reference-data-amend-history', + templateUrl: './reference-data-amend-history.component.html', + changeDetection: ChangeDetectionStrategy.OnPush, }) export class ReferenceDataAmendHistoryComponent implements OnInit { - @Input() type = ''; - @Input() key = ''; - @Input() title = ''; - @Input() columns: ReferenceDataAdminColumn[] = []; - - pageStart?: number; - pageEnd?: number; - - constructor(private cdr: ChangeDetectorRef, private store: Store) {} - - ngOnInit(): void { - // load the audit history - this.store.dispatch( - fetchReferenceDataByKeySearch({ - resourceType: `${this.type}#AUDIT` as ReferenceDataResourceType, - resourceKey: `${decodeURIComponent(this.key)}#`, - }), - ); - } - - get history$(): Observable { - return this.store.pipe(select(selectSearchReturn(`${this.type}#AUDIT` as ReferenceDataResourceType))); - } - - get numberOfRecords$(): Observable { - return this.history$.pipe(map((items) => items?.length ?? 0)); - } - - get paginatedItems$() { - return this.history$.pipe(map((items) => items?.slice(this.pageStart, this.pageEnd) ?? [])); - } - - handlePaginationChange({ start, end }: { start: number; end: number }) { - this.pageStart = start; - this.pageEnd = end; - this.cdr.detectChanges(); - } + @Input() type = ''; + @Input() key = ''; + @Input() title = ''; + @Input() columns: ReferenceDataAdminColumn[] = []; + + pageStart?: number; + pageEnd?: number; + + constructor( + private cdr: ChangeDetectorRef, + private store: Store + ) {} + + ngOnInit(): void { + // load the audit history + this.store.dispatch( + fetchReferenceDataByKeySearch({ + resourceType: `${this.type}#AUDIT` as ReferenceDataResourceType, + resourceKey: `${decodeURIComponent(this.key)}#`, + }) + ); + } + + get history$(): Observable { + return this.store.pipe(select(selectSearchReturn(`${this.type}#AUDIT` as ReferenceDataResourceType))); + } + + get numberOfRecords$(): Observable { + return this.history$.pipe(map((items) => items?.length ?? 0)); + } + + get paginatedItems$() { + return this.history$.pipe(map((items) => items?.slice(this.pageStart, this.pageEnd) ?? [])); + } + + handlePaginationChange({ start, end }: { start: number; end: number }) { + this.pageStart = start; + this.pageEnd = end; + this.cdr.detectChanges(); + } } diff --git a/src/app/features/reference-data/reference-data-amend/reference-data-amend.component.spec.ts b/src/app/features/reference-data/reference-data-amend/reference-data-amend.component.spec.ts index 8d47f7f92b..7f50174b69 100644 --- a/src/app/features/reference-data/reference-data-amend/reference-data-amend.component.spec.ts +++ b/src/app/features/reference-data/reference-data-amend/reference-data-amend.component.spec.ts @@ -10,98 +10,98 @@ import { State, initialAppState } from '@store/.'; import { ReferenceDataAmendComponent } from './reference-data-amend.component'; const mockRefDataService = { - loadReferenceData: jest.fn(), - loadReferenceDataByKey: jest.fn(), - fetchReferenceDataByKey: jest.fn(), + loadReferenceData: jest.fn(), + loadReferenceDataByKey: jest.fn(), + fetchReferenceDataByKey: jest.fn(), }; describe('ReferenceDataAmendComponent', () => { - let component: ReferenceDataAmendComponent; - let fixture: ComponentFixture; - let store: MockStore; - let router: Router; - let route: ActivatedRoute; - let errorService: GlobalErrorService; - - beforeEach(async () => { - await TestBed.configureTestingModule({ - declarations: [ReferenceDataAmendComponent], - imports: [RouterTestingModule, HttpClientTestingModule], - providers: [ - provideMockStore({ initialState: initialAppState }), - ReferenceDataService, - { provide: UserService, useValue: {} }, - { provide: ReferenceDataService, useValue: mockRefDataService }, - ], - }).compileComponents(); - }); - - beforeEach(() => { - store = TestBed.inject(MockStore); - fixture = TestBed.createComponent(ReferenceDataAmendComponent); - component = fixture.componentInstance; - router = TestBed.inject(Router); - errorService = TestBed.inject(GlobalErrorService); - route = TestBed.inject(ActivatedRoute); - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); - - describe('navigateBack', () => { - it('should clear all errors', () => { - jest.spyOn(router, 'navigate').mockImplementation(); - - const clearErrorsSpy = jest.spyOn(errorService, 'clearErrors'); - - component.navigateBack(); - - expect(clearErrorsSpy).toHaveBeenCalledTimes(1); - }); - - it('should navigate back to the previous page', () => { - const navigateSpy = jest.spyOn(router, 'navigate').mockImplementation(() => Promise.resolve(true)); - - component.navigateBack(); - - expect(navigateSpy).toHaveBeenCalledWith(['..'], { relativeTo: route }); - }); - }); - - describe('handleFormChange', () => { - it('should set amendedData', () => { - component.handleFormChange({ foo: 'bar' }); - - expect(component.amendedData).toEqual({ foo: 'bar' }); - }); - }); - - describe('handleSubmit', () => { - it('should dispatch if form is valid', () => { - fixture.ngZone?.run(() => { - component.amendedData = { description: 'testing' }; - jest.spyOn(component, 'checkForms').mockImplementationOnce(() => { - component.isFormInvalid = false; - }); - const dispatch = jest.spyOn(store, 'dispatch'); - - component.handleSubmit(); - - expect(dispatch).toHaveBeenCalled(); - }); - }); - - it('should not dispatch if form is invalid', () => { - jest.spyOn(component, 'checkForms').mockImplementationOnce(() => { - component.isFormInvalid = true; - }); - const dispatch = jest.spyOn(store, 'dispatch'); - - component.handleSubmit(); - - expect(dispatch).not.toHaveBeenCalled(); - }); - }); + let component: ReferenceDataAmendComponent; + let fixture: ComponentFixture; + let store: MockStore; + let router: Router; + let route: ActivatedRoute; + let errorService: GlobalErrorService; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ReferenceDataAmendComponent], + imports: [RouterTestingModule, HttpClientTestingModule], + providers: [ + provideMockStore({ initialState: initialAppState }), + ReferenceDataService, + { provide: UserService, useValue: {} }, + { provide: ReferenceDataService, useValue: mockRefDataService }, + ], + }).compileComponents(); + }); + + beforeEach(() => { + store = TestBed.inject(MockStore); + fixture = TestBed.createComponent(ReferenceDataAmendComponent); + component = fixture.componentInstance; + router = TestBed.inject(Router); + errorService = TestBed.inject(GlobalErrorService); + route = TestBed.inject(ActivatedRoute); + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + describe('navigateBack', () => { + it('should clear all errors', () => { + jest.spyOn(router, 'navigate').mockImplementation(); + + const clearErrorsSpy = jest.spyOn(errorService, 'clearErrors'); + + component.navigateBack(); + + expect(clearErrorsSpy).toHaveBeenCalledTimes(1); + }); + + it('should navigate back to the previous page', () => { + const navigateSpy = jest.spyOn(router, 'navigate').mockImplementation(() => Promise.resolve(true)); + + component.navigateBack(); + + expect(navigateSpy).toHaveBeenCalledWith(['..'], { relativeTo: route }); + }); + }); + + describe('handleFormChange', () => { + it('should set amendedData', () => { + component.handleFormChange({ foo: 'bar' }); + + expect(component.amendedData).toEqual({ foo: 'bar' }); + }); + }); + + describe('handleSubmit', () => { + it('should dispatch if form is valid', () => { + fixture.ngZone?.run(() => { + component.amendedData = { description: 'testing' }; + jest.spyOn(component, 'checkForms').mockImplementationOnce(() => { + component.isFormInvalid = false; + }); + const dispatch = jest.spyOn(store, 'dispatch'); + + component.handleSubmit(); + + expect(dispatch).toHaveBeenCalled(); + }); + }); + + it('should not dispatch if form is invalid', () => { + jest.spyOn(component, 'checkForms').mockImplementationOnce(() => { + component.isFormInvalid = true; + }); + const dispatch = jest.spyOn(store, 'dispatch'); + + component.handleSubmit(); + + expect(dispatch).not.toHaveBeenCalled(); + }); + }); }); diff --git a/src/app/features/reference-data/reference-data-amend/reference-data-amend.component.ts b/src/app/features/reference-data/reference-data-amend/reference-data-amend.component.ts index f3fef4e437..3a7bb8346d 100644 --- a/src/app/features/reference-data/reference-data-amend/reference-data-amend.component.ts +++ b/src/app/features/reference-data/reference-data-amend/reference-data-amend.component.ts @@ -1,6 +1,4 @@ -import { - Component, OnInit, QueryList, ViewChildren, -} from '@angular/core'; +import { Component, OnInit, QueryList, ViewChildren } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; import { GlobalError } from '@core/components/global-error/global-error.interface'; import { GlobalErrorService } from '@core/components/global-error/global-error.service'; @@ -15,110 +13,112 @@ import { ReferenceDataState, amendReferenceDataItem, selectReferenceDataByResour import { Observable, first } from 'rxjs'; @Component({ - selector: 'app-reference-data-amend', - templateUrl: './reference-data-amend.component.html', + selector: 'app-reference-data-amend', + templateUrl: './reference-data-amend.component.html', }) export class ReferenceDataAmendComponent implements OnInit { - type!: ReferenceDataResourceType; - key!: string; - isFormDirty = false; - isFormInvalid = true; - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - amendedData: any; - - @ViewChildren(DynamicFormGroupComponent) sections!: QueryList; - - constructor( - public globalErrorService: GlobalErrorService, - public dfs: DynamicFormService, - private referenceDataService: ReferenceDataService, - private route: ActivatedRoute, - private router: Router, - private store: Store, - ) {} - - ngOnInit(): void { - this.route.parent?.params.pipe(first()).subscribe((params) => { - this.type = params['type']; - // load the reference data admin type - this.referenceDataService.loadReferenceDataByKey(ReferenceDataResourceType.ReferenceDataAdminType, this.type); - }); - - this.route.params.pipe(first()).subscribe((params) => { - this.key = decodeURIComponent(params['key']); - - if (this.type && this.key) { - // load the current item - this.referenceDataService.loadReferenceDataByKey(this.type, this.key); - } - }); - } - - get roles(): typeof Roles { - return Roles; - } - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - get refDataAdminType$(): Observable { - return this.store.pipe(select(selectReferenceDataByResourceKey(ReferenceDataResourceType.ReferenceDataAdminType, this.type))); - } - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - get data$(): Observable { - return this.store.pipe(select(selectReferenceDataByResourceKey(this.type, this.key))); - } - - navigateBack() { - this.globalErrorService.clearErrors(); - void this.router.navigate(['..'], { relativeTo: this.route }); - } - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - handleFormChange(event: any) { - this.amendedData = event; - } - - checkForms(): void { - const forms = this.sections.map((section) => section.form) as Array; - - this.isFormDirty = forms.some((form) => form.dirty); - - this.setErrors(forms); - - this.isFormInvalid = forms.some((form) => form.invalid); - } - - setErrors(forms: Array): void { - const errors: GlobalError[] = []; - - forms.forEach((form) => DynamicFormService.validate(form, errors)); - - if (errors.length) { - this.globalErrorService.setErrors(errors); - return; - } - - this.globalErrorService.clearErrors(); - } - - handleSubmit() { - this.checkForms(); - - if (this.isFormInvalid) return; - - this.store.dispatch( - amendReferenceDataItem({ - resourceType: this.type, - resourceKey: encodeURIComponent(String(this.key)), - payload: this.amendedData, - }), - ); - - this.sections.forEach((form) => { - form.ngOnDestroy(); - }); - - this.navigateBack(); - } + type!: ReferenceDataResourceType; + key!: string; + isFormDirty = false; + isFormInvalid = true; + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + amendedData: any; + + @ViewChildren(DynamicFormGroupComponent) sections!: QueryList; + + constructor( + public globalErrorService: GlobalErrorService, + public dfs: DynamicFormService, + private referenceDataService: ReferenceDataService, + private route: ActivatedRoute, + private router: Router, + private store: Store + ) {} + + ngOnInit(): void { + this.route.parent?.params.pipe(first()).subscribe((params) => { + this.type = params['type']; + // load the reference data admin type + this.referenceDataService.loadReferenceDataByKey(ReferenceDataResourceType.ReferenceDataAdminType, this.type); + }); + + this.route.params.pipe(first()).subscribe((params) => { + this.key = decodeURIComponent(params['key']); + + if (this.type && this.key) { + // load the current item + this.referenceDataService.loadReferenceDataByKey(this.type, this.key); + } + }); + } + + get roles(): typeof Roles { + return Roles; + } + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + get refDataAdminType$(): Observable { + return this.store.pipe( + select(selectReferenceDataByResourceKey(ReferenceDataResourceType.ReferenceDataAdminType, this.type)) + ); + } + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + get data$(): Observable { + return this.store.pipe(select(selectReferenceDataByResourceKey(this.type, this.key))); + } + + navigateBack() { + this.globalErrorService.clearErrors(); + void this.router.navigate(['..'], { relativeTo: this.route }); + } + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + handleFormChange(event: any) { + this.amendedData = event; + } + + checkForms(): void { + const forms = this.sections.map((section) => section.form) as Array; + + this.isFormDirty = forms.some((form) => form.dirty); + + this.setErrors(forms); + + this.isFormInvalid = forms.some((form) => form.invalid); + } + + setErrors(forms: Array): void { + const errors: GlobalError[] = []; + + forms.forEach((form) => DynamicFormService.validate(form, errors)); + + if (errors.length) { + this.globalErrorService.setErrors(errors); + return; + } + + this.globalErrorService.clearErrors(); + } + + handleSubmit() { + this.checkForms(); + + if (this.isFormInvalid) return; + + this.store.dispatch( + amendReferenceDataItem({ + resourceType: this.type, + resourceKey: encodeURIComponent(String(this.key)), + payload: this.amendedData, + }) + ); + + this.sections.forEach((form) => { + form.ngOnDestroy(); + }); + + this.navigateBack(); + } } diff --git a/src/app/features/reference-data/reference-data-delete/reference-data-delete.component.spec.ts b/src/app/features/reference-data/reference-data-delete/reference-data-delete.component.spec.ts index 1f3c01b783..de0205b757 100644 --- a/src/app/features/reference-data/reference-data-delete/reference-data-delete.component.spec.ts +++ b/src/app/features/reference-data/reference-data-delete/reference-data-delete.component.spec.ts @@ -11,96 +11,96 @@ import { State, initialAppState } from '@store/.'; import { ReferenceDataDeleteComponent } from './reference-data-delete.component'; describe('ReferenceDataAddComponent', () => { - let component: ReferenceDataDeleteComponent; - let fixture: ComponentFixture; - let store: MockStore; - let router: Router; - let route: ActivatedRoute; - let errorService: GlobalErrorService; + let component: ReferenceDataDeleteComponent; + let fixture: ComponentFixture; + let store: MockStore; + let router: Router; + let route: ActivatedRoute; + let errorService: GlobalErrorService; - beforeEach(async () => { - await TestBed.configureTestingModule({ - declarations: [ReferenceDataDeleteComponent], - imports: [RouterTestingModule, HttpClientTestingModule], - providers: [ - GlobalErrorService, - provideMockStore({ initialState: initialAppState }), - ReferenceDataService, - { provide: UserService, useValue: {} }, - ], - }).compileComponents(); - }); + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ReferenceDataDeleteComponent], + imports: [RouterTestingModule, HttpClientTestingModule], + providers: [ + GlobalErrorService, + provideMockStore({ initialState: initialAppState }), + ReferenceDataService, + { provide: UserService, useValue: {} }, + ], + }).compileComponents(); + }); - beforeEach(() => { - store = TestBed.inject(MockStore); - fixture = TestBed.createComponent(ReferenceDataDeleteComponent); - component = fixture.componentInstance; - router = TestBed.inject(Router); - route = TestBed.inject(ActivatedRoute); - errorService = TestBed.inject(GlobalErrorService); - fixture.detectChanges(); - }); + beforeEach(() => { + store = TestBed.inject(MockStore); + fixture = TestBed.createComponent(ReferenceDataDeleteComponent); + component = fixture.componentInstance; + router = TestBed.inject(Router); + route = TestBed.inject(ActivatedRoute); + errorService = TestBed.inject(GlobalErrorService); + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); - describe('navigateBack', () => { - it('should clear all errors', () => { - jest.spyOn(router, 'navigate').mockImplementation(); + it('should create', () => { + expect(component).toBeTruthy(); + }); + describe('navigateBack', () => { + it('should clear all errors', () => { + jest.spyOn(router, 'navigate').mockImplementation(); - const clearErrorsSpy = jest.spyOn(errorService, 'clearErrors'); + const clearErrorsSpy = jest.spyOn(errorService, 'clearErrors'); - component.navigateBack(); + component.navigateBack(); - expect(clearErrorsSpy).toHaveBeenCalledTimes(1); - }); + expect(clearErrorsSpy).toHaveBeenCalledTimes(1); + }); - it('should navigate back to the previous page', () => { - const navigateSpy = jest.spyOn(router, 'navigate').mockImplementation(() => Promise.resolve(true)); + it('should navigate back to the previous page', () => { + const navigateSpy = jest.spyOn(router, 'navigate').mockImplementation(() => Promise.resolve(true)); - component.navigateBack(); + component.navigateBack(); - expect(navigateSpy).toHaveBeenCalledWith(['../..'], { relativeTo: route }); - }); - }); - describe('handleFormChange', () => { - it('should change reason for deletion to the form value', () => { - component.handleFormChange({ reason: 'test reason' }); + expect(navigateSpy).toHaveBeenCalledWith(['../..'], { relativeTo: route }); + }); + }); + describe('handleFormChange', () => { + it('should change reason for deletion to the form value', () => { + component.handleFormChange({ reason: 'test reason' }); - expect(component.reasonForDeletion).toEqual({ reason: 'test reason' }); - }); - }); - describe('handleSubmit', () => { - it('will not dispatch there is no reason for deletion', () => { - component.type = ReferenceDataResourceType.CountryOfRegistration; - const dispatch = jest.spyOn(store, 'dispatch'); + expect(component.reasonForDeletion).toEqual({ reason: 'test reason' }); + }); + }); + describe('handleSubmit', () => { + it('will not dispatch there is no reason for deletion', () => { + component.type = ReferenceDataResourceType.CountryOfRegistration; + const dispatch = jest.spyOn(store, 'dispatch'); - component.handleSubmit(); + component.handleSubmit(); - expect(dispatch).not.toHaveBeenCalledWith({ - reason: 'test reason', - resourceKey: 'testkey', - resourceType: 'COUNTRY_OF_REGISTRATION', - type: '[API/reference-data] deleteReferenceDataItem', - }); - }); - it('will dispatches if there is a reason and type defined', () => { - fixture.ngZone?.run(() => { - component.type = ReferenceDataResourceType.CountryOfRegistration; - component.key = 'testkey'; - component.handleFormChange({ reason: 'test reason' }); - const dispatch = jest.spyOn(store, 'dispatch'); + expect(dispatch).not.toHaveBeenCalledWith({ + reason: 'test reason', + resourceKey: 'testkey', + resourceType: 'COUNTRY_OF_REGISTRATION', + type: '[API/reference-data] deleteReferenceDataItem', + }); + }); + it('will dispatches if there is a reason and type defined', () => { + fixture.ngZone?.run(() => { + component.type = ReferenceDataResourceType.CountryOfRegistration; + component.key = 'testkey'; + component.handleFormChange({ reason: 'test reason' }); + const dispatch = jest.spyOn(store, 'dispatch'); - component.handleSubmit(); + component.handleSubmit(); - expect(dispatch).toHaveBeenCalled(); - expect(dispatch).toHaveBeenCalledWith({ - reason: 'test reason', - resourceKey: 'testkey', - resourceType: 'COUNTRY_OF_REGISTRATION', - type: '[API/reference-data] deleteReferenceDataItem', - }); - }); - }); - }); + expect(dispatch).toHaveBeenCalled(); + expect(dispatch).toHaveBeenCalledWith({ + reason: 'test reason', + resourceKey: 'testkey', + resourceType: 'COUNTRY_OF_REGISTRATION', + type: '[API/reference-data] deleteReferenceDataItem', + }); + }); + }); + }); }); diff --git a/src/app/features/reference-data/reference-data-delete/reference-data-delete.component.ts b/src/app/features/reference-data/reference-data-delete/reference-data-delete.component.ts index 420d2f97bc..5b6dc49978 100644 --- a/src/app/features/reference-data/reference-data-delete/reference-data-delete.component.ts +++ b/src/app/features/reference-data/reference-data-delete/reference-data-delete.component.ts @@ -1,144 +1,145 @@ -import { - Component, OnInit, QueryList, ViewChildren, -} from '@angular/core'; +import { Component, OnInit, QueryList, ViewChildren } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; import { GlobalError } from '@core/components/global-error/global-error.interface'; import { GlobalErrorService } from '@core/components/global-error/global-error.service'; import { DynamicFormGroupComponent } from '@forms/components/dynamic-form-group/dynamic-form-group.component'; import { ValidatorNames } from '@forms/models/validators.enum'; import { DynamicFormService } from '@forms/services/dynamic-form.service'; -import { - CustomFormGroup, FormNodeEditTypes, FormNodeTypes, FormNodeWidth, -} from '@forms/services/dynamic-form.types'; +import { CustomFormGroup, FormNodeEditTypes, FormNodeTypes, FormNodeWidth } from '@forms/services/dynamic-form.types'; import { ReferenceDataResourceType } from '@models/reference-data.model'; import { Roles } from '@models/roles.enum'; import { Store, select } from '@ngrx/store'; import { ReferenceDataService } from '@services/reference-data/reference-data.service'; import { - ReferenceDataState, deleteReferenceDataItem, fetchReferenceDataByKey, selectReferenceDataByResourceKey, + ReferenceDataState, + deleteReferenceDataItem, + fetchReferenceDataByKey, + selectReferenceDataByResourceKey, } from '@store/reference-data'; import { Observable, take } from 'rxjs'; @Component({ - selector: 'app-reference-data-delete', - templateUrl: './reference-data-delete.component.html', + selector: 'app-reference-data-delete', + templateUrl: './reference-data-delete.component.html', }) export class ReferenceDataDeleteComponent implements OnInit { - type!: ReferenceDataResourceType; - key!: string; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - reasonForDeletion: any; - isFormDirty = false; - isFormInvalid = true; - - reasonTemplate = { - name: 'reason-for-deletion', - type: FormNodeTypes.GROUP, - label: 'reason', - children: [ - { - name: 'reason', - label: 'Reason for Deletion', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.TEXTAREA, - validators: [ - { - name: ValidatorNames.Required, - }, - ], - }, - ], - }; - - @ViewChildren(DynamicFormGroupComponent) sections!: QueryList; - - constructor( - public globalErrorService: GlobalErrorService, - private referenceDataService: ReferenceDataService, - private route: ActivatedRoute, - private router: Router, - private store: Store, - ) {} - - ngOnInit(): void { - this.route.parent?.params.pipe(take(1)).subscribe((params) => { - this.type = params['type']; - - this.referenceDataService.loadReferenceDataByKey(ReferenceDataResourceType.ReferenceDataAdminType, this.type); - }); - - this.route.params.pipe(take(1)).subscribe((params) => { - this.key = decodeURIComponent(params['key']); - - if (this.type && this.key) { - this.store.dispatch(fetchReferenceDataByKey({ resourceType: this.type, resourceKey: this.key })); - } - }); - } - - get roles(): typeof Roles { - return Roles; - } - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - get refData$(): Observable { - return this.store.pipe(select(selectReferenceDataByResourceKey(this.type, decodeURIComponent(this.key)))); - } - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - get refDataAdminType$(): Observable { - return this.store.pipe(select(selectReferenceDataByResourceKey(ReferenceDataResourceType.ReferenceDataAdminType, this.type))); - } - - get widths(): typeof FormNodeWidth { - return FormNodeWidth; - } - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - handleFormChange(event: any) { - this.reasonForDeletion = event; - } - - checkForms(): void { - const forms = this.sections.map((section) => section.form) as Array; - - this.isFormDirty = forms.some((form) => form.dirty); - - this.setErrors(forms); - - this.isFormInvalid = forms.some((form) => form.invalid); - } - - setErrors(forms: Array): void { - const errors: GlobalError[] = []; - - forms.forEach((form) => DynamicFormService.validate(form, errors)); - - if (errors.length) { - this.globalErrorService.setErrors(errors); - return; - } - - this.globalErrorService.clearErrors(); - } - - navigateBack() { - this.globalErrorService.clearErrors(); - void this.router.navigate(['../..'], { relativeTo: this.route }); - } - - handleSubmit() { - this.checkForms(); - - if (this.isFormInvalid || !this.reasonForDeletion) return; - - this.store.dispatch( - deleteReferenceDataItem({ - resourceType: this.type, - resourceKey: this.key, - reason: this.reasonForDeletion.reason, - }), - ); - this.navigateBack(); - } + type!: ReferenceDataResourceType; + key!: string; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + reasonForDeletion: any; + isFormDirty = false; + isFormInvalid = true; + + reasonTemplate = { + name: 'reason-for-deletion', + type: FormNodeTypes.GROUP, + label: 'reason', + children: [ + { + name: 'reason', + label: 'Reason for Deletion', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.TEXTAREA, + validators: [ + { + name: ValidatorNames.Required, + }, + ], + }, + ], + }; + + @ViewChildren(DynamicFormGroupComponent) sections!: QueryList; + + constructor( + public globalErrorService: GlobalErrorService, + private referenceDataService: ReferenceDataService, + private route: ActivatedRoute, + private router: Router, + private store: Store + ) {} + + ngOnInit(): void { + this.route.parent?.params.pipe(take(1)).subscribe((params) => { + this.type = params['type']; + + this.referenceDataService.loadReferenceDataByKey(ReferenceDataResourceType.ReferenceDataAdminType, this.type); + }); + + this.route.params.pipe(take(1)).subscribe((params) => { + this.key = decodeURIComponent(params['key']); + + if (this.type && this.key) { + this.store.dispatch(fetchReferenceDataByKey({ resourceType: this.type, resourceKey: this.key })); + } + }); + } + + get roles(): typeof Roles { + return Roles; + } + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + get refData$(): Observable { + return this.store.pipe(select(selectReferenceDataByResourceKey(this.type, decodeURIComponent(this.key)))); + } + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + get refDataAdminType$(): Observable { + return this.store.pipe( + select(selectReferenceDataByResourceKey(ReferenceDataResourceType.ReferenceDataAdminType, this.type)) + ); + } + + get widths(): typeof FormNodeWidth { + return FormNodeWidth; + } + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + handleFormChange(event: any) { + this.reasonForDeletion = event; + } + + checkForms(): void { + const forms = this.sections.map((section) => section.form) as Array; + + this.isFormDirty = forms.some((form) => form.dirty); + + this.setErrors(forms); + + this.isFormInvalid = forms.some((form) => form.invalid); + } + + setErrors(forms: Array): void { + const errors: GlobalError[] = []; + + forms.forEach((form) => DynamicFormService.validate(form, errors)); + + if (errors.length) { + this.globalErrorService.setErrors(errors); + return; + } + + this.globalErrorService.clearErrors(); + } + + navigateBack() { + this.globalErrorService.clearErrors(); + void this.router.navigate(['../..'], { relativeTo: this.route }); + } + + handleSubmit() { + this.checkForms(); + + if (this.isFormInvalid || !this.reasonForDeletion) return; + + this.store.dispatch( + deleteReferenceDataItem({ + resourceType: this.type, + resourceKey: this.key, + reason: this.reasonForDeletion.reason, + }) + ); + this.navigateBack(); + } } diff --git a/src/app/features/reference-data/reference-data-deleted-list/reference-data-deleted-list.component.spec.ts b/src/app/features/reference-data/reference-data-deleted-list/reference-data-deleted-list.component.spec.ts index adc3a94fea..8ea4c02b9e 100644 --- a/src/app/features/reference-data/reference-data-deleted-list/reference-data-deleted-list.component.spec.ts +++ b/src/app/features/reference-data/reference-data-deleted-list/reference-data-deleted-list.component.spec.ts @@ -13,29 +13,33 @@ import { of } from 'rxjs'; import { ReferenceDataDeletedListComponent } from './reference-data-deleted-list.component'; describe('DataTypeListComponent', () => { - let component: ReferenceDataDeletedListComponent; - let fixture: ComponentFixture; + let component: ReferenceDataDeletedListComponent; + let fixture: ComponentFixture; - beforeEach(async () => { - await TestBed.configureTestingModule({ - declarations: [ReferenceDataDeletedListComponent, RoleRequiredDirective, PaginationComponent], - imports: [RouterTestingModule, HttpClientTestingModule, HttpClientModule], - providers: [provideMockStore({ initialState: initialAppState }), ReferenceDataService, { - provide: UserService, - useValue: { - roles$: of([Roles.ReferenceDataView]), - }, - }], - }).compileComponents(); - }); + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ReferenceDataDeletedListComponent, RoleRequiredDirective, PaginationComponent], + imports: [RouterTestingModule, HttpClientTestingModule, HttpClientModule], + providers: [ + provideMockStore({ initialState: initialAppState }), + ReferenceDataService, + { + provide: UserService, + useValue: { + roles$: of([Roles.ReferenceDataView]), + }, + }, + ], + }).compileComponents(); + }); - beforeEach(() => { - fixture = TestBed.createComponent(ReferenceDataDeletedListComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); + beforeEach(() => { + fixture = TestBed.createComponent(ReferenceDataDeletedListComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); }); diff --git a/src/app/features/reference-data/reference-data-deleted-list/reference-data-deleted-list.component.ts b/src/app/features/reference-data/reference-data-deleted-list/reference-data-deleted-list.component.ts index f986c65371..2427f4a3ab 100644 --- a/src/app/features/reference-data/reference-data-deleted-list/reference-data-deleted-list.component.ts +++ b/src/app/features/reference-data/reference-data-deleted-list/reference-data-deleted-list.component.ts @@ -8,55 +8,57 @@ import { fetchReferenceDataAudit, selectReferenceDataByResourceKey, selectSearch import { Observable, map, take } from 'rxjs'; @Component({ - selector: 'app-reference-data-deleted-list', - templateUrl: './reference-data-deleted-list.component.html', + selector: 'app-reference-data-deleted-list', + templateUrl: './reference-data-deleted-list.component.html', }) export class ReferenceDataDeletedListComponent implements OnInit { - type!: ReferenceDataResourceType; - pageStart?: number; - pageEnd?: number; - - constructor( - private referenceDataService: ReferenceDataService, - private route: ActivatedRoute, - private router: Router, - private store: Store, - private cdr: ChangeDetectorRef, - ) {} - - ngOnInit(): void { - this.route.parent?.params.pipe(take(1)).subscribe((params) => { - this.type = params['type']; - this.referenceDataService.loadReferenceDataByKey(ReferenceDataResourceType.ReferenceDataAdminType, this.type); - this.store.dispatch(fetchReferenceDataAudit({ resourceType: (`${this.type}#AUDIT`) as ReferenceDataResourceType })); - }); - } - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - get refDataAdminType$(): Observable { - return this.store.pipe(select(selectReferenceDataByResourceKey(ReferenceDataResourceType.ReferenceDataAdminType, this.type))); - } - - get data$(): Observable { - return this.store.pipe(select(selectSearchReturn((`${this.type}#AUDIT`) as ReferenceDataResourceType))); - } - - get roles(): typeof Roles { - return Roles; - } - - handlePaginationChange({ start, end }: { start: number; end: number }) { - this.pageStart = start; - this.pageEnd = end; - this.cdr.detectChanges(); - } - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - get paginatedItems$(): Observable { - return this.data$.pipe(map((items) => items?.slice(this.pageStart, this.pageEnd) ?? [])); - } - - get numberOfRecords$(): Observable { - return this.data$.pipe(map((items) => items?.length ?? 0)); - } + type!: ReferenceDataResourceType; + pageStart?: number; + pageEnd?: number; + + constructor( + private referenceDataService: ReferenceDataService, + private route: ActivatedRoute, + private router: Router, + private store: Store, + private cdr: ChangeDetectorRef + ) {} + + ngOnInit(): void { + this.route.parent?.params.pipe(take(1)).subscribe((params) => { + this.type = params['type']; + this.referenceDataService.loadReferenceDataByKey(ReferenceDataResourceType.ReferenceDataAdminType, this.type); + this.store.dispatch(fetchReferenceDataAudit({ resourceType: `${this.type}#AUDIT` as ReferenceDataResourceType })); + }); + } + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + get refDataAdminType$(): Observable { + return this.store.pipe( + select(selectReferenceDataByResourceKey(ReferenceDataResourceType.ReferenceDataAdminType, this.type)) + ); + } + + get data$(): Observable { + return this.store.pipe(select(selectSearchReturn(`${this.type}#AUDIT` as ReferenceDataResourceType))); + } + + get roles(): typeof Roles { + return Roles; + } + + handlePaginationChange({ start, end }: { start: number; end: number }) { + this.pageStart = start; + this.pageEnd = end; + this.cdr.detectChanges(); + } + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + get paginatedItems$(): Observable { + return this.data$.pipe(map((items) => items?.slice(this.pageStart, this.pageEnd) ?? [])); + } + + get numberOfRecords$(): Observable { + return this.data$.pipe(map((items) => items?.length ?? 0)); + } } diff --git a/src/app/features/reference-data/reference-data-list/reference-data-list.component.spec.ts b/src/app/features/reference-data/reference-data-list/reference-data-list.component.spec.ts index 650be5e0f0..ca02ef1826 100644 --- a/src/app/features/reference-data/reference-data-list/reference-data-list.component.spec.ts +++ b/src/app/features/reference-data/reference-data-list/reference-data-list.component.spec.ts @@ -17,145 +17,151 @@ import * as refSelectors from '../../../store/reference-data/selectors/reference import { ReferenceDataListComponent } from './reference-data-list.component'; describe('DataTypeListComponent', () => { - let component: ReferenceDataListComponent; - let fixture: ComponentFixture; - let router: Router; - let errorService: GlobalErrorService; - let route: ActivatedRoute; - - beforeEach(async () => { - await TestBed.configureTestingModule({ - declarations: [ReferenceDataListComponent, RoleRequiredDirective], - imports: [RouterTestingModule, HttpClientTestingModule], - providers: [ - provideMockStore({ initialState: initialAppState }), - ReferenceDataService, - GlobalErrorService, - { provide: UserService, useValue: { roles$: of([Roles.ReferenceDataView]) } }, - ], - }).compileComponents(); - }); - - beforeEach(() => { - fixture = TestBed.createComponent(ReferenceDataListComponent); - component = fixture.componentInstance; - route = TestBed.inject(ActivatedRoute); - router = TestBed.inject(Router); - errorService = TestBed.inject(GlobalErrorService); - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); - describe('amend', () => { - it('should navigate to the selected items key', () => { - fixture.ngZone?.run(() => { - const navigateSpy = jest.spyOn(router, 'navigate'); - - component.amend({ resourceKey: 'foo', resourceType: ReferenceDataResourceType.CountryOfRegistration }); - - expect(navigateSpy).toHaveBeenCalledWith(['foo'], { relativeTo: route }); - }); - }); - }); - describe('delete', () => { - it('should navigate to the selected items :key/delete', () => { - fixture.ngZone?.run(() => { - const navigateSpy = jest.spyOn(router, 'navigate'); - - component.delete({ resourceKey: 'foo', resourceType: ReferenceDataResourceType.CountryOfRegistration }); - - expect(navigateSpy).toHaveBeenCalledWith(['foo/delete'], { relativeTo: route }); - }); - }); - }); - describe('addNew', () => { - it('should navigate to the selected items create', () => { - fixture.ngZone?.run(() => { - const navigateSpy = jest.spyOn(router, 'navigate'); - - component.addNew(); - - expect(navigateSpy).toHaveBeenCalledWith(['create'], { relativeTo: route }); - }); - }); - }); - describe('navigateToDeletedItems', () => { - it('should navigate to the selected items create', () => { - fixture.ngZone?.run(() => { - const navigateSpy = jest.spyOn(router, 'navigate'); - - component.navigateToDeletedItems(); - - expect(navigateSpy).toHaveBeenCalledWith(['deleted-items'], { relativeTo: route }); - }); - }); - }); - describe('handlePaginationChange', () => { - it('should set the start and end pages', () => { - component.handlePaginationChange({ start: 0, end: 24 }); - - expect(component.pageStart).toBe(0); - expect(component.pageEnd).toBe(24); - }); - }); - describe('clear', () => { - it('should reset the form', () => { - component.form.controls['term'].patchValue('foo'); - expect(component.form.controls['term'].value).toBe('foo'); - - component.clear(); - expect(component.form.controls['term'].value).toBeNull(); - }); - it('should navigate to page 1 of pagination', () => { - fixture.ngZone?.run(() => { - const navigateSpy = jest.spyOn(router, 'navigate'); - component.type = ReferenceDataResourceType.Tyres; - component.searchReturned = true; - component.clear(); - - expect(navigateSpy).toHaveBeenCalledWith(['../TYRES'], { relativeTo: route, queryParams: { 'reference-data-items-page': 1 } }); - }); - }); - }); - - describe('search', () => { - it('should call add error if there is no search term', () => { - const errorSpy = jest.spyOn(errorService, 'addError'); - component.search('', 'tyreCode'); - - expect(errorSpy).toHaveBeenCalled(); - }); - it('should call add error if there is no filter', () => { - const errorSpy = jest.spyOn(errorService, 'addError'); - component.search('term', ''); - - expect(errorSpy).toHaveBeenCalled(); - }); - it('should call add error if there are no items returned', () => { - const errorSpy = jest.spyOn(errorService, 'addError'); - component.type = ReferenceDataResourceType.Tyres; - - component.search('term', 'brakeCode'); - - expect(errorSpy).toHaveBeenCalled(); - }); - it('should navigate to page 1 of pagination', () => { - fixture.ngZone?.run(() => { - const navigateSpy = jest.spyOn(router, 'navigate'); - component.type = ReferenceDataResourceType.Tyres; - component.searchReturned = true; - jest.spyOn(refSelectors, 'selectRefDataBySearchTerm').mockReturnValue( - createSelector( - (v) => v, - () => [{ resourceKey: 'foo', resourceType: 'bar' }] as ReferenceDataItem[] | undefined, - ), - ); - component.search('foo', 'bar'); - - expect(navigateSpy).toHaveBeenCalledWith(['../TYRES'], { relativeTo: route, queryParams: { 'reference-data-items-page': 1 } }); - }); - }); - }); + let component: ReferenceDataListComponent; + let fixture: ComponentFixture; + let router: Router; + let errorService: GlobalErrorService; + let route: ActivatedRoute; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ReferenceDataListComponent, RoleRequiredDirective], + imports: [RouterTestingModule, HttpClientTestingModule], + providers: [ + provideMockStore({ initialState: initialAppState }), + ReferenceDataService, + GlobalErrorService, + { provide: UserService, useValue: { roles$: of([Roles.ReferenceDataView]) } }, + ], + }).compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(ReferenceDataListComponent); + component = fixture.componentInstance; + route = TestBed.inject(ActivatedRoute); + router = TestBed.inject(Router); + errorService = TestBed.inject(GlobalErrorService); + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + describe('amend', () => { + it('should navigate to the selected items key', () => { + fixture.ngZone?.run(() => { + const navigateSpy = jest.spyOn(router, 'navigate'); + + component.amend({ resourceKey: 'foo', resourceType: ReferenceDataResourceType.CountryOfRegistration }); + + expect(navigateSpy).toHaveBeenCalledWith(['foo'], { relativeTo: route }); + }); + }); + }); + describe('delete', () => { + it('should navigate to the selected items :key/delete', () => { + fixture.ngZone?.run(() => { + const navigateSpy = jest.spyOn(router, 'navigate'); + + component.delete({ resourceKey: 'foo', resourceType: ReferenceDataResourceType.CountryOfRegistration }); + + expect(navigateSpy).toHaveBeenCalledWith(['foo/delete'], { relativeTo: route }); + }); + }); + }); + describe('addNew', () => { + it('should navigate to the selected items create', () => { + fixture.ngZone?.run(() => { + const navigateSpy = jest.spyOn(router, 'navigate'); + + component.addNew(); + + expect(navigateSpy).toHaveBeenCalledWith(['create'], { relativeTo: route }); + }); + }); + }); + describe('navigateToDeletedItems', () => { + it('should navigate to the selected items create', () => { + fixture.ngZone?.run(() => { + const navigateSpy = jest.spyOn(router, 'navigate'); + + component.navigateToDeletedItems(); + + expect(navigateSpy).toHaveBeenCalledWith(['deleted-items'], { relativeTo: route }); + }); + }); + }); + describe('handlePaginationChange', () => { + it('should set the start and end pages', () => { + component.handlePaginationChange({ start: 0, end: 24 }); + + expect(component.pageStart).toBe(0); + expect(component.pageEnd).toBe(24); + }); + }); + describe('clear', () => { + it('should reset the form', () => { + component.form.controls['term'].patchValue('foo'); + expect(component.form.controls['term'].value).toBe('foo'); + + component.clear(); + expect(component.form.controls['term'].value).toBeNull(); + }); + it('should navigate to page 1 of pagination', () => { + fixture.ngZone?.run(() => { + const navigateSpy = jest.spyOn(router, 'navigate'); + component.type = ReferenceDataResourceType.Tyres; + component.searchReturned = true; + component.clear(); + + expect(navigateSpy).toHaveBeenCalledWith(['../TYRES'], { + relativeTo: route, + queryParams: { 'reference-data-items-page': 1 }, + }); + }); + }); + }); + + describe('search', () => { + it('should call add error if there is no search term', () => { + const errorSpy = jest.spyOn(errorService, 'addError'); + component.search('', 'tyreCode'); + + expect(errorSpy).toHaveBeenCalled(); + }); + it('should call add error if there is no filter', () => { + const errorSpy = jest.spyOn(errorService, 'addError'); + component.search('term', ''); + + expect(errorSpy).toHaveBeenCalled(); + }); + it('should call add error if there are no items returned', () => { + const errorSpy = jest.spyOn(errorService, 'addError'); + component.type = ReferenceDataResourceType.Tyres; + + component.search('term', 'brakeCode'); + + expect(errorSpy).toHaveBeenCalled(); + }); + it('should navigate to page 1 of pagination', () => { + fixture.ngZone?.run(() => { + const navigateSpy = jest.spyOn(router, 'navigate'); + component.type = ReferenceDataResourceType.Tyres; + component.searchReturned = true; + jest.spyOn(refSelectors, 'selectRefDataBySearchTerm').mockReturnValue( + createSelector( + (v) => v, + () => [{ resourceKey: 'foo', resourceType: 'bar' }] as ReferenceDataItem[] | undefined + ) + ); + component.search('foo', 'bar'); + + expect(navigateSpy).toHaveBeenCalledWith(['../TYRES'], { + relativeTo: route, + queryParams: { 'reference-data-items-page': 1 }, + }); + }); + }); + }); }); diff --git a/src/app/features/reference-data/reference-data-list/reference-data-list.component.ts b/src/app/features/reference-data/reference-data-list/reference-data-list.component.ts index d5627500fd..5dd21cbc39 100644 --- a/src/app/features/reference-data/reference-data-list/reference-data-list.component.ts +++ b/src/app/features/reference-data/reference-data-list/reference-data-list.component.ts @@ -1,6 +1,4 @@ -import { - ChangeDetectorRef, Component, OnDestroy, OnInit, -} from '@angular/core'; +import { ChangeDetectorRef, Component, OnDestroy, OnInit } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; import { GlobalErrorService } from '@core/components/global-error/global-error.service'; import { DynamicFormService } from '@forms/services/dynamic-form.service'; @@ -9,178 +7,186 @@ import { ReferenceDataModelBase, ReferenceDataResourceType } from '@models/refer import { Roles } from '@models/roles.enum'; import { Store, select } from '@ngrx/store'; import { ReferenceDataService } from '@services/reference-data/reference-data.service'; -import { selectAllReferenceDataByResourceType, selectRefDataBySearchTerm, selectReferenceDataByResourceKey } from '@store/reference-data'; import { - Observable, Subject, catchError, filter, map, of, switchMap, take, -} from 'rxjs'; + selectAllReferenceDataByResourceType, + selectRefDataBySearchTerm, + selectReferenceDataByResourceKey, +} from '@store/reference-data'; +import { Observable, Subject, catchError, filter, map, of, switchMap, take } from 'rxjs'; @Component({ - selector: 'app-reference-data-list', - templateUrl: './reference-data-list.component.html', - styleUrls: ['./reference-data-list.component.scss'], + selector: 'app-reference-data-list', + templateUrl: './reference-data-list.component.html', + styleUrls: ['./reference-data-list.component.scss'], }) export class ReferenceDataListComponent implements OnInit, OnDestroy { - type!: ReferenceDataResourceType; - disabled = true; - pageStart?: number; - pageEnd?: number; - currentPage?: number; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - data?: Observable | undefined>; - private destroy$ = new Subject(); - - public form!: CustomFormGroup; - public searchReturned = false; - - public searchTemplate: FormNode = { - name: 'criteria', - type: FormNodeTypes.GROUP, - children: [ - { - name: 'filter', - label: 'Search filter', - value: '', - type: FormNodeTypes.CONTROL, - }, - { - name: 'term', - value: '', - type: FormNodeTypes.CONTROL, - }, - ], - }; - - constructor( - private referenceDataService: ReferenceDataService, - private route: ActivatedRoute, - private router: Router, - private store: Store, - private cdr: ChangeDetectorRef, - private globalErrorService: GlobalErrorService, - public dfs: DynamicFormService, - ) {} - - ngOnInit(): void { - this.form = this.dfs.createForm(this.searchTemplate) as CustomFormGroup; - this.route.params.pipe(take(1)).subscribe((params) => { - this.type = params['type']; - this.referenceDataService.loadReferenceData(this.type); - this.referenceDataService.loadReferenceDataByKey(ReferenceDataResourceType.ReferenceDataAdminType, this.type); - }); - - this.globalErrorService.errors$ - .pipe( - take(1), - filter((errors) => !errors.length), - switchMap(() => - this.referenceDataService.fetchReferenceDataAudit(`${this.type}#AUDIT` as ReferenceDataResourceType).pipe( - map((array) => - array.data.map((item) => { - if (item.reason !== undefined) { - this.disabled = false; - } - })), - )), - catchError((error) => { - if (error.status === 404) { - this.disabled = true; - return of(true); - } - return of(false); - }), - ) - .subscribe({ - next: (res) => of(!!res), - }); - this.data = this.store.pipe(select(selectAllReferenceDataByResourceType(this.type))); - } - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - get refDataAdminType$(): Observable { - return this.store.pipe(select(selectReferenceDataByResourceKey(ReferenceDataResourceType.ReferenceDataAdminType, this.type))); - } - - public get roles(): typeof Roles { - return Roles; - } - - addNew(): void { - void this.router.navigate(['create'], { relativeTo: this.route }).then(() => { - window.location.reload(); - }); - } - - navigateToDeletedItems(): void { - void this.router.navigate(['deleted-items'], { relativeTo: this.route }); - } - - amend(item: ReferenceDataModelBase): void { - const key = encodeURIComponent(String(item.resourceKey)); - void this.router.navigate([key], { relativeTo: this.route }).then(() => { - window.location.reload(); - }); - } - - delete(item: ReferenceDataModelBase): void { - const key = encodeURIComponent(String(item.resourceKey)); - void this.router.navigate([`${key}/delete`], { relativeTo: this.route }); - } - - handlePaginationChange({ start, end }: { start: number; end: number }) { - this.pageStart = start; - this.pageEnd = end; - this.cdr.detectChanges(); - } - - get paginatedItems$() { - return (this.data ?? of([])).pipe(map((items) => items?.slice(this.pageStart, this.pageEnd) ?? [])); - } - - get numberOfRecords$() { - return (this.data ?? of([])).pipe(map((items) => items?.length ?? 0)); - } - - search(term: string, filterterm: string) { - this.globalErrorService.clearErrors(); - const trimmedTerm = term?.trim(); - if (!trimmedTerm || !filterterm) { - const error = !trimmedTerm - ? { error: 'You must provide a search term', anchorLink: 'refSearch' } - : { error: 'You must select a valid search filter', anchorLink: 'filter' }; - this.globalErrorService.addError(error); - return; - } - - this.store.pipe(select(selectRefDataBySearchTerm(trimmedTerm, this.type, filterterm)), take(1)).subscribe((items) => { - if (!items?.length) { - this.globalErrorService.addError({ error: 'Your search returned no results', anchorLink: 'refSearch' }); - this.data = of([]); - } else { - this.data = of(items); - void this.router.navigate([`../${this.type}`], { - relativeTo: this.route, - queryParams: { 'reference-data-items-page': 1 }, - }); - } - this.searchReturned = true; - }); - } - - clear() { - this.form.reset(); - this.globalErrorService.clearErrors(); - if (this.searchReturned) { - this.data = this.store.pipe(select(selectAllReferenceDataByResourceType(this.type))); - void this.router.navigate([`../${this.type}`], { - relativeTo: this.route, - queryParams: { 'reference-data-items-page': 1 }, - }); - this.searchReturned = false; - } - } - - ngOnDestroy(): void { - this.destroy$.next(); - this.destroy$.complete(); - } + type!: ReferenceDataResourceType; + disabled = true; + pageStart?: number; + pageEnd?: number; + currentPage?: number; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + data?: Observable | undefined>; + private destroy$ = new Subject(); + + public form!: CustomFormGroup; + public searchReturned = false; + + public searchTemplate: FormNode = { + name: 'criteria', + type: FormNodeTypes.GROUP, + children: [ + { + name: 'filter', + label: 'Search filter', + value: '', + type: FormNodeTypes.CONTROL, + }, + { + name: 'term', + value: '', + type: FormNodeTypes.CONTROL, + }, + ], + }; + + constructor( + private referenceDataService: ReferenceDataService, + private route: ActivatedRoute, + private router: Router, + private store: Store, + private cdr: ChangeDetectorRef, + private globalErrorService: GlobalErrorService, + public dfs: DynamicFormService + ) {} + + ngOnInit(): void { + this.form = this.dfs.createForm(this.searchTemplate) as CustomFormGroup; + this.route.params.pipe(take(1)).subscribe((params) => { + this.type = params['type']; + this.referenceDataService.loadReferenceData(this.type); + this.referenceDataService.loadReferenceDataByKey(ReferenceDataResourceType.ReferenceDataAdminType, this.type); + }); + + this.globalErrorService.errors$ + .pipe( + take(1), + filter((errors) => !errors.length), + switchMap(() => + this.referenceDataService.fetchReferenceDataAudit(`${this.type}#AUDIT` as ReferenceDataResourceType).pipe( + map((array) => + array.data.map((item) => { + if (item.reason !== undefined) { + this.disabled = false; + } + }) + ) + ) + ), + catchError((error) => { + if (error.status === 404) { + this.disabled = true; + return of(true); + } + return of(false); + }) + ) + .subscribe({ + next: (res) => of(!!res), + }); + this.data = this.store.pipe(select(selectAllReferenceDataByResourceType(this.type))); + } + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + get refDataAdminType$(): Observable { + return this.store.pipe( + select(selectReferenceDataByResourceKey(ReferenceDataResourceType.ReferenceDataAdminType, this.type)) + ); + } + + public get roles(): typeof Roles { + return Roles; + } + + addNew(): void { + void this.router.navigate(['create'], { relativeTo: this.route }).then(() => { + window.location.reload(); + }); + } + + navigateToDeletedItems(): void { + void this.router.navigate(['deleted-items'], { relativeTo: this.route }); + } + + amend(item: ReferenceDataModelBase): void { + const key = encodeURIComponent(String(item.resourceKey)); + void this.router.navigate([key], { relativeTo: this.route }).then(() => { + window.location.reload(); + }); + } + + delete(item: ReferenceDataModelBase): void { + const key = encodeURIComponent(String(item.resourceKey)); + void this.router.navigate([`${key}/delete`], { relativeTo: this.route }); + } + + handlePaginationChange({ start, end }: { start: number; end: number }) { + this.pageStart = start; + this.pageEnd = end; + this.cdr.detectChanges(); + } + + get paginatedItems$() { + return (this.data ?? of([])).pipe(map((items) => items?.slice(this.pageStart, this.pageEnd) ?? [])); + } + + get numberOfRecords$() { + return (this.data ?? of([])).pipe(map((items) => items?.length ?? 0)); + } + + search(term: string, filterterm: string) { + this.globalErrorService.clearErrors(); + const trimmedTerm = term?.trim(); + if (!trimmedTerm || !filterterm) { + const error = !trimmedTerm + ? { error: 'You must provide a search term', anchorLink: 'refSearch' } + : { error: 'You must select a valid search filter', anchorLink: 'filter' }; + this.globalErrorService.addError(error); + return; + } + + this.store + .pipe(select(selectRefDataBySearchTerm(trimmedTerm, this.type, filterterm)), take(1)) + .subscribe((items) => { + if (!items?.length) { + this.globalErrorService.addError({ error: 'Your search returned no results', anchorLink: 'refSearch' }); + this.data = of([]); + } else { + this.data = of(items); + void this.router.navigate([`../${this.type}`], { + relativeTo: this.route, + queryParams: { 'reference-data-items-page': 1 }, + }); + } + this.searchReturned = true; + }); + } + + clear() { + this.form.reset(); + this.globalErrorService.clearErrors(); + if (this.searchReturned) { + this.data = this.store.pipe(select(selectAllReferenceDataByResourceType(this.type))); + void this.router.navigate([`../${this.type}`], { + relativeTo: this.route, + queryParams: { 'reference-data-items-page': 1 }, + }); + this.searchReturned = false; + } + } + + ngOnDestroy(): void { + this.destroy$.next(); + this.destroy$.complete(); + } } diff --git a/src/app/features/reference-data/reference-data-routing.module.ts b/src/app/features/reference-data/reference-data-routing.module.ts index 3210bdf8a7..d04c2c1002 100644 --- a/src/app/features/reference-data/reference-data-routing.module.ts +++ b/src/app/features/reference-data/reference-data-routing.module.ts @@ -3,8 +3,8 @@ import { RouterModule, Routes } from '@angular/router'; import { MsalGuard } from '@azure/msal-angular'; import { RoleGuard } from '@guards/role-guard/roles.guard'; import { Roles } from '@models/roles.enum'; -import { RouterOutletComponent } from '@shared/components/router-outlet/router-outlet.component'; import { ReferenceDataRoutes } from '@models/routes.enum'; +import { RouterOutletComponent } from '@shared/components/router-outlet/router-outlet.component'; import { ReferenceDataCreateComponent } from './reference-data-add/reference-data-add.component'; import { ReferenceDataAmendComponent } from './reference-data-amend/reference-data-amend.component'; import { ReferenceDataDeleteComponent } from './reference-data-delete/reference-data-delete.component'; @@ -13,61 +13,61 @@ import { ReferenceDataListComponent } from './reference-data-list/reference-data import { ReferenceDataSelectTypeComponent } from './reference-data-select-type/reference-data-select-type.component'; const routes: Routes = [ - { - path: '', - component: RouterOutletComponent, - data: { title: 'Select Reference Data Type', roles: Roles.ReferenceDataView }, - canActivate: [MsalGuard, RoleGuard], - children: [ - { - path: '', - component: ReferenceDataSelectTypeComponent, - data: { title: 'Select Reference Data Type', roles: Roles.ReferenceDataView }, - canActivate: [MsalGuard, RoleGuard], - }, - { - path: ReferenceDataRoutes.TYPE, - component: RouterOutletComponent, - data: { title: 'Search Reference Data', roles: Roles.ReferenceDataView }, - canActivate: [MsalGuard, RoleGuard], - children: [ - { - path: '', - component: ReferenceDataListComponent, - data: { title: 'Search Reference Data', roles: Roles.ReferenceDataView }, - canActivate: [MsalGuard, RoleGuard], - }, - { - path: ReferenceDataRoutes.CREATE, - component: ReferenceDataCreateComponent, - data: { title: 'Add Reference Data', roles: Roles.ReferenceDataAmend }, - canActivate: [MsalGuard, RoleGuard], - }, - { - path: ReferenceDataRoutes.DELETED_ITEMS, - component: ReferenceDataDeletedListComponent, - data: { title: 'View deleted Reference Data', roles: Roles.ReferenceDataView }, - canActivate: [MsalGuard, RoleGuard], - }, - { - path: ReferenceDataRoutes.KEY, - component: ReferenceDataAmendComponent, - data: { title: 'Amend Reference Data', roles: Roles.ReferenceDataAmend }, - canActivate: [MsalGuard, RoleGuard], - }, - { - path: ReferenceDataRoutes.DELETE, - component: ReferenceDataDeleteComponent, - data: { title: 'Delete Reference Data', roles: Roles.ReferenceDataAmend }, - canActivate: [MsalGuard, RoleGuard], - }, - ], - }, - ], - }, + { + path: '', + component: RouterOutletComponent, + data: { title: 'Select Reference Data Type', roles: Roles.ReferenceDataView }, + canActivate: [MsalGuard, RoleGuard], + children: [ + { + path: '', + component: ReferenceDataSelectTypeComponent, + data: { title: 'Select Reference Data Type', roles: Roles.ReferenceDataView }, + canActivate: [MsalGuard, RoleGuard], + }, + { + path: ReferenceDataRoutes.TYPE, + component: RouterOutletComponent, + data: { title: 'Search Reference Data', roles: Roles.ReferenceDataView }, + canActivate: [MsalGuard, RoleGuard], + children: [ + { + path: '', + component: ReferenceDataListComponent, + data: { title: 'Search Reference Data', roles: Roles.ReferenceDataView }, + canActivate: [MsalGuard, RoleGuard], + }, + { + path: ReferenceDataRoutes.CREATE, + component: ReferenceDataCreateComponent, + data: { title: 'Add Reference Data', roles: Roles.ReferenceDataAmend }, + canActivate: [MsalGuard, RoleGuard], + }, + { + path: ReferenceDataRoutes.DELETED_ITEMS, + component: ReferenceDataDeletedListComponent, + data: { title: 'View deleted Reference Data', roles: Roles.ReferenceDataView }, + canActivate: [MsalGuard, RoleGuard], + }, + { + path: ReferenceDataRoutes.KEY, + component: ReferenceDataAmendComponent, + data: { title: 'Amend Reference Data', roles: Roles.ReferenceDataAmend }, + canActivate: [MsalGuard, RoleGuard], + }, + { + path: ReferenceDataRoutes.DELETE, + component: ReferenceDataDeleteComponent, + data: { title: 'Delete Reference Data', roles: Roles.ReferenceDataAmend }, + canActivate: [MsalGuard, RoleGuard], + }, + ], + }, + ], + }, ]; @NgModule({ - imports: [RouterModule.forChild(routes)], - exports: [RouterModule], + imports: [RouterModule.forChild(routes)], + exports: [RouterModule], }) export class ReferenceDataRoutingModule {} diff --git a/src/app/features/reference-data/reference-data-select-type/reference-data-select-type.component.spec.ts b/src/app/features/reference-data/reference-data-select-type/reference-data-select-type.component.spec.ts index 68287d3444..b118e4e9e1 100644 --- a/src/app/features/reference-data/reference-data-select-type/reference-data-select-type.component.spec.ts +++ b/src/app/features/reference-data/reference-data-select-type/reference-data-select-type.component.spec.ts @@ -17,64 +17,65 @@ import { of } from 'rxjs'; import { ReferenceDataSelectTypeComponent } from './reference-data-select-type.component'; describe('ReferenceDataComponent', () => { - let component: ReferenceDataSelectTypeComponent; - let fixture: ComponentFixture; - let router: Router; - let route: ActivatedRoute; + let component: ReferenceDataSelectTypeComponent; + let fixture: ComponentFixture; + let router: Router; + let route: ActivatedRoute; - beforeEach(async () => { - await TestBed.configureTestingModule({ - declarations: [ReferenceDataSelectTypeComponent, RoleRequiredDirective, RadioGroupComponent, ButtonComponent], - imports: [HttpClientTestingModule, RouterTestingModule, FormsModule], - providers: [ - provideMockStore({ initialState: initialAppState }), - ReferenceDataService, { provide: UserService, useValue: { roles$: of([Roles.ReferenceDataView]) } }, - ], - }).compileComponents(); - }); + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ReferenceDataSelectTypeComponent, RoleRequiredDirective, RadioGroupComponent, ButtonComponent], + imports: [HttpClientTestingModule, RouterTestingModule, FormsModule], + providers: [ + provideMockStore({ initialState: initialAppState }), + ReferenceDataService, + { provide: UserService, useValue: { roles$: of([Roles.ReferenceDataView]) } }, + ], + }).compileComponents(); + }); - beforeEach(() => { - fixture = TestBed.createComponent(ReferenceDataSelectTypeComponent); - component = fixture.componentInstance; - router = TestBed.inject(Router); - route = TestBed.inject(ActivatedRoute); - fixture.detectChanges(); - }); + beforeEach(() => { + fixture = TestBed.createComponent(ReferenceDataSelectTypeComponent); + component = fixture.componentInstance; + router = TestBed.inject(Router); + route = TestBed.inject(ActivatedRoute); + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); - describe('cancel', () => { - it('should navigate back relative to the route', () => { - jest.spyOn(router, 'navigate').mockImplementation(); + describe('cancel', () => { + it('should navigate back relative to the route', () => { + jest.spyOn(router, 'navigate').mockImplementation(); - const navigateSpy = jest.spyOn(router, 'navigate').mockImplementation(); + const navigateSpy = jest.spyOn(router, 'navigate').mockImplementation(); - component.cancel(); + component.cancel(); - expect(navigateSpy).toHaveBeenCalledWith(['..'], { relativeTo: route }); - }); - }); + expect(navigateSpy).toHaveBeenCalledWith(['..'], { relativeTo: route }); + }); + }); - describe('navigateTo', () => { - it('should navigate to the reference data resource type', () => { - jest.spyOn(router, 'navigate').mockImplementation(); - jest.spyOn(component, 'isFormValid', 'get').mockReturnValueOnce(true); + describe('navigateTo', () => { + it('should navigate to the reference data resource type', () => { + jest.spyOn(router, 'navigate').mockImplementation(); + jest.spyOn(component, 'isFormValid', 'get').mockReturnValueOnce(true); - const navigateSpy = jest.spyOn(router, 'navigate').mockImplementation(); + const navigateSpy = jest.spyOn(router, 'navigate').mockImplementation(); - component.navigateTo(ReferenceDataResourceType.CountryOfRegistration); + component.navigateTo(ReferenceDataResourceType.CountryOfRegistration); - expect(navigateSpy).toHaveBeenCalledWith(['COUNTRY_OF_REGISTRATION'], { relativeTo: route }); - }); - }); + expect(navigateSpy).toHaveBeenCalledWith(['COUNTRY_OF_REGISTRATION'], { relativeTo: route }); + }); + }); - describe('isFormValid', () => { - it('checks the form is valid', () => { - jest.spyOn(DynamicFormService, 'validate').mockReturnValueOnce(); - component.form.setValue({ referenceType: 'COUNTRY_OF_REGISTRATION' }); - expect(component.isFormValid).toBe(true); - }); - }); + describe('isFormValid', () => { + it('checks the form is valid', () => { + jest.spyOn(DynamicFormService, 'validate').mockReturnValueOnce(); + component.form.setValue({ referenceType: 'COUNTRY_OF_REGISTRATION' }); + expect(component.isFormValid).toBe(true); + }); + }); }); diff --git a/src/app/features/reference-data/reference-data-select-type/reference-data-select-type.component.ts b/src/app/features/reference-data/reference-data-select-type/reference-data-select-type.component.ts index b60a4d4796..97a3cd732a 100644 --- a/src/app/features/reference-data/reference-data-select-type/reference-data-select-type.component.ts +++ b/src/app/features/reference-data/reference-data-select-type/reference-data-select-type.component.ts @@ -4,9 +4,7 @@ import { ActivatedRoute, Router } from '@angular/router'; import { GlobalError } from '@core/components/global-error/global-error.interface'; import { GlobalErrorService } from '@core/components/global-error/global-error.service'; import { DynamicFormService } from '@forms/services/dynamic-form.service'; -import { - CustomFormControl, CustomFormGroup, FormNodeOption, FormNodeTypes, -} from '@forms/services/dynamic-form.types'; +import { CustomFormControl, CustomFormGroup, FormNodeOption, FormNodeTypes } from '@forms/services/dynamic-form.types'; import { ReferenceDataResourceType } from '@models/reference-data.model'; import { Roles } from '@models/roles.enum'; import { Store, select } from '@ngrx/store'; @@ -15,58 +13,63 @@ import { ReferenceDataState, selectAllReferenceDataByResourceType } from '@store import { Observable, map, take } from 'rxjs'; @Component({ - selector: 'app-reference-data-select-type', - templateUrl: './reference-data-select-type.component.html', + selector: 'app-reference-data-select-type', + templateUrl: './reference-data-select-type.component.html', }) export class ReferenceDataSelectTypeComponent { - form: CustomFormGroup = new CustomFormGroup( - { name: 'form-group', type: FormNodeTypes.GROUP }, - { - referenceType: new CustomFormControl({ name: 'referenceType', type: FormNodeTypes.CONTROL }, undefined, [Validators.required]), - }, - ); + form: CustomFormGroup = new CustomFormGroup( + { name: 'form-group', type: FormNodeTypes.GROUP }, + { + referenceType: new CustomFormControl({ name: 'referenceType', type: FormNodeTypes.CONTROL }, undefined, [ + Validators.required, + ]), + } + ); - constructor( - private globalErrorService: GlobalErrorService, - private referenceDataService: ReferenceDataService, - private route: ActivatedRoute, - private router: Router, - private store: Store, - ) { - this.referenceDataService.loadReferenceData(ReferenceDataResourceType.ReferenceDataAdminType); - } + constructor( + private globalErrorService: GlobalErrorService, + private referenceDataService: ReferenceDataService, + private route: ActivatedRoute, + private router: Router, + private store: Store + ) { + this.referenceDataService.loadReferenceData(ReferenceDataResourceType.ReferenceDataAdminType); + } - get options$(): Observable>> { - return this.store.pipe( - select(selectAllReferenceDataByResourceType(ReferenceDataResourceType.ReferenceDataAdminType)), - take(1), - map( - (types) => - types - ?.sort((a, b) => (a.label ?? a.resourceType).localeCompare(b.label ?? b.resourceType)) - .map((type) => ({ label: type.label ?? type.resourceKey.toString(), value: type.resourceKey.toString() })) ?? [], - ), - ); - } + get options$(): Observable>> { + return this.store.pipe( + select(selectAllReferenceDataByResourceType(ReferenceDataResourceType.ReferenceDataAdminType)), + take(1), + map( + (types) => + types + ?.sort((a, b) => (a.label ?? a.resourceType).localeCompare(b.label ?? b.resourceType)) + .map((type) => ({ + label: type.label ?? type.resourceKey.toString(), + value: type.resourceKey.toString(), + })) ?? [] + ) + ); + } - get roles(): typeof Roles { - return Roles; - } + get roles(): typeof Roles { + return Roles; + } - get isFormValid(): boolean { - const errors: GlobalError[] = []; - DynamicFormService.validate(this.form, errors); - this.globalErrorService.setErrors(errors); - return this.form.valid; - } + get isFormValid(): boolean { + const errors: GlobalError[] = []; + DynamicFormService.validate(this.form, errors); + this.globalErrorService.setErrors(errors); + return this.form.valid; + } - cancel(): void { - void this.router.navigate(['..'], { relativeTo: this.route }); - } + cancel(): void { + void this.router.navigate(['..'], { relativeTo: this.route }); + } - navigateTo(type: string): void { - if (this.isFormValid) { - void this.router.navigate([type], { relativeTo: this.route }); - } - } + navigateTo(type: string): void { + if (this.isFormValid) { + void this.router.navigate([type], { relativeTo: this.route }); + } + } } diff --git a/src/app/features/reference-data/reference-data.module.ts b/src/app/features/reference-data/reference-data.module.ts index 886d529a9d..83752cf8be 100644 --- a/src/app/features/reference-data/reference-data.module.ts +++ b/src/app/features/reference-data/reference-data.module.ts @@ -14,15 +14,22 @@ import { ReferenceDataRoutingModule } from './reference-data-routing.module'; import { ReferenceDataSelectTypeComponent } from './reference-data-select-type/reference-data-select-type.component'; @NgModule({ - declarations: [ - ReferenceDataSelectTypeComponent, - ReferenceDataListComponent, - ReferenceDataDeletedListComponent, - ReferenceDataCreateComponent, - ReferenceDataAmendComponent, - ReferenceDataDeleteComponent, - ReferenceDataAmendHistoryComponent, - ], - imports: [CommonModule, DynamicFormsModule, RouterModule, ReactiveFormsModule, ReferenceDataRoutingModule, SharedModule], + declarations: [ + ReferenceDataSelectTypeComponent, + ReferenceDataListComponent, + ReferenceDataDeletedListComponent, + ReferenceDataCreateComponent, + ReferenceDataAmendComponent, + ReferenceDataDeleteComponent, + ReferenceDataAmendHistoryComponent, + ], + imports: [ + CommonModule, + DynamicFormsModule, + RouterModule, + ReactiveFormsModule, + ReferenceDataRoutingModule, + SharedModule, + ], }) export class ReferenceDataModule {} diff --git a/src/app/features/search/multiple-search-results/multiple-search-results.component.spec.ts b/src/app/features/search/multiple-search-results/multiple-search-results.component.spec.ts index 9b80fb0603..3b0983a7a0 100644 --- a/src/app/features/search/multiple-search-results/multiple-search-results.component.spec.ts +++ b/src/app/features/search/multiple-search-results/multiple-search-results.component.spec.ts @@ -1,137 +1,133 @@ import { HttpClientTestingModule } from '@angular/common/http/testing'; -import { - ComponentFixture, fakeAsync, TestBed, tick, -} from '@angular/core/testing'; +import { ComponentFixture, TestBed, fakeAsync, tick } from '@angular/core/testing'; import { By } from '@angular/platform-browser'; import { RouterTestingModule } from '@angular/router/testing'; import { RoleRequiredDirective } from '@directives/app-role-required.directive'; +import { TechRecordSearchSchema } from '@dvsa/cvs-type-definitions/types/v3/tech-record/get/search'; import { DynamicFormsModule } from '@forms/dynamic-forms.module'; import { provideMockActions } from '@ngrx/effects/testing'; import { Action } from '@ngrx/store'; import { MockStore, provideMockStore } from '@ngrx/store/testing'; import { TechnicalRecordHttpService } from '@services/technical-record-http/technical-record-http.service'; import { UserService } from '@services/user-service/user-service'; -import { initialAppState, State } from '@store/.'; +import { State, initialAppState } from '@store/.'; import { selectQueryParams } from '@store/router/selectors/router.selectors'; -import { - firstValueFrom, of, ReplaySubject, BehaviorSubject, -} from 'rxjs'; -import { TechRecordSearchSchema } from '@dvsa/cvs-type-definitions/types/v3/tech-record/get/search'; +import { BehaviorSubject, ReplaySubject, firstValueFrom, of } from 'rxjs'; import { SingleSearchResultComponent } from '../single-search-result/single-search-result.component'; import { MultipleSearchResultsComponent } from './multiple-search-results.component'; describe('MultipleSearchResultsComponent', () => { - let component: MultipleSearchResultsComponent; - let fixture: ComponentFixture; - let store: MockStore; - const actions$ = new ReplaySubject(); - - beforeEach(async () => { - await TestBed.configureTestingModule({ - declarations: [SingleSearchResultComponent, MultipleSearchResultsComponent, RoleRequiredDirective], - imports: [DynamicFormsModule, HttpClientTestingModule, RouterTestingModule], - providers: [ - provideMockStore({ initialState: initialAppState }), - provideMockActions(() => actions$), - { - provide: UserService, - useValue: { - roles$: of(['TechRecord.View', 'TechRecord.Create']), - }, - }, - TechnicalRecordHttpService, - ], - }).compileComponents(); - }); - - describe('default tests', () => { - beforeEach(() => { - store = TestBed.inject(MockStore); - store.overrideSelector(selectQueryParams, { vin: '123456' }); - - fixture = TestBed.createComponent(MultipleSearchResultsComponent); - component = fixture.componentInstance; - - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); - - it('should show create link when searchResults is null', () => { - const button = fixture.debugElement.query(By.css('.govuk-link')); - expect(button).toBeTruthy(); - }); - - it('should navigate back when searchResults not null', fakeAsync(() => { - const navigateBackSpy = jest.spyOn(component, 'navigateBack'); - const newData: TechRecordSearchSchema[] = [ - { - vin: '1B7GG36N12S678410', - techRecord_statusCode: 'provisional', - techRecord_vehicleType: 'psv', - createdTimestamp: '2023-09-27T12:00:00Z', - systemNumber: '12345', - techRecord_manufactureYear: 2013, - }, - ]; - component.searchResults$ = new BehaviorSubject(newData); - fixture.detectChanges(); - - const button = fixture.debugElement.query(By.css('.govuk-back-link')); - expect(button).toBeTruthy(); - (button.nativeElement as HTMLButtonElement).click(); - - tick(); - - expect(navigateBackSpy).toHaveBeenCalled(); - })); - }); - - describe('searching', () => { - beforeEach(() => { - store = TestBed.inject(MockStore); - }); - - it('should search using a vin', async () => { - store.overrideSelector(selectQueryParams, { vin: '123456' }); - - fixture = TestBed.createComponent(MultipleSearchResultsComponent); - component = fixture.componentInstance; - const searchResult = await firstValueFrom(component.searchResults$); - - expect(searchResult).toBeDefined(); - }); - - it('should search using a partial vin', async () => { - store.overrideSelector(selectQueryParams, { partialVin: '123456' }); - - fixture = TestBed.createComponent(MultipleSearchResultsComponent); - component = fixture.componentInstance; - const searchResult = await firstValueFrom(component.searchResults$); - - expect(searchResult).toBeDefined(); - }); - - it('should search using a vrm', async () => { - store.overrideSelector(selectQueryParams, { vrm: '123456' }); - - fixture = TestBed.createComponent(MultipleSearchResultsComponent); - component = fixture.componentInstance; - const searchResult = await firstValueFrom(component.searchResults$); - - expect(searchResult).toBeDefined(); - }); - - it('should search using a trailer id', async () => { - store.overrideSelector(selectQueryParams, { trailerId: '123456' }); - - fixture = TestBed.createComponent(MultipleSearchResultsComponent); - component = fixture.componentInstance; - const searchResult = await firstValueFrom(component.searchResults$); - - expect(searchResult).toBeDefined(); - }); - }); + let component: MultipleSearchResultsComponent; + let fixture: ComponentFixture; + let store: MockStore; + const actions$ = new ReplaySubject(); + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [SingleSearchResultComponent, MultipleSearchResultsComponent, RoleRequiredDirective], + imports: [DynamicFormsModule, HttpClientTestingModule, RouterTestingModule], + providers: [ + provideMockStore({ initialState: initialAppState }), + provideMockActions(() => actions$), + { + provide: UserService, + useValue: { + roles$: of(['TechRecord.View', 'TechRecord.Create']), + }, + }, + TechnicalRecordHttpService, + ], + }).compileComponents(); + }); + + describe('default tests', () => { + beforeEach(() => { + store = TestBed.inject(MockStore); + store.overrideSelector(selectQueryParams, { vin: '123456' }); + + fixture = TestBed.createComponent(MultipleSearchResultsComponent); + component = fixture.componentInstance; + + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + it('should show create link when searchResults is null', () => { + const button = fixture.debugElement.query(By.css('.govuk-link')); + expect(button).toBeTruthy(); + }); + + it('should navigate back when searchResults not null', fakeAsync(() => { + const navigateBackSpy = jest.spyOn(component, 'navigateBack'); + const newData: TechRecordSearchSchema[] = [ + { + vin: '1B7GG36N12S678410', + techRecord_statusCode: 'provisional', + techRecord_vehicleType: 'psv', + createdTimestamp: '2023-09-27T12:00:00Z', + systemNumber: '12345', + techRecord_manufactureYear: 2013, + }, + ]; + component.searchResults$ = new BehaviorSubject(newData); + fixture.detectChanges(); + + const button = fixture.debugElement.query(By.css('.govuk-back-link')); + expect(button).toBeTruthy(); + (button.nativeElement as HTMLButtonElement).click(); + + tick(); + + expect(navigateBackSpy).toHaveBeenCalled(); + })); + }); + + describe('searching', () => { + beforeEach(() => { + store = TestBed.inject(MockStore); + }); + + it('should search using a vin', async () => { + store.overrideSelector(selectQueryParams, { vin: '123456' }); + + fixture = TestBed.createComponent(MultipleSearchResultsComponent); + component = fixture.componentInstance; + const searchResult = await firstValueFrom(component.searchResults$); + + expect(searchResult).toBeDefined(); + }); + + it('should search using a partial vin', async () => { + store.overrideSelector(selectQueryParams, { partialVin: '123456' }); + + fixture = TestBed.createComponent(MultipleSearchResultsComponent); + component = fixture.componentInstance; + const searchResult = await firstValueFrom(component.searchResults$); + + expect(searchResult).toBeDefined(); + }); + + it('should search using a vrm', async () => { + store.overrideSelector(selectQueryParams, { vrm: '123456' }); + + fixture = TestBed.createComponent(MultipleSearchResultsComponent); + component = fixture.componentInstance; + const searchResult = await firstValueFrom(component.searchResults$); + + expect(searchResult).toBeDefined(); + }); + + it('should search using a trailer id', async () => { + store.overrideSelector(selectQueryParams, { trailerId: '123456' }); + + fixture = TestBed.createComponent(MultipleSearchResultsComponent); + component = fixture.componentInstance; + const searchResult = await firstValueFrom(component.searchResults$); + + expect(searchResult).toBeDefined(); + }); + }); }); diff --git a/src/app/features/search/multiple-search-results/multiple-search-results.component.ts b/src/app/features/search/multiple-search-results/multiple-search-results.component.ts index d62cdb34a2..4a3a181da7 100644 --- a/src/app/features/search/multiple-search-results/multiple-search-results.component.ts +++ b/src/app/features/search/multiple-search-results/multiple-search-results.component.ts @@ -3,56 +3,56 @@ import { Component, OnDestroy } from '@angular/core'; import { GlobalErrorService } from '@core/components/global-error/global-error.service'; import { TechRecordSearchSchema } from '@dvsa/cvs-type-definitions/types/v3/tech-record/get/search'; import { Roles } from '@models/roles.enum'; +import { SEARCH_TYPES } from '@models/search-types-enum'; import { Store, select } from '@ngrx/store'; import { TechnicalRecordHttpService } from '@services/technical-record-http/technical-record-http.service'; -import { SEARCH_TYPES } from '@models/search-types-enum'; import { TechnicalRecordService } from '@services/technical-record/technical-record.service'; import { selectQueryParams } from '@store/router/selectors/router.selectors'; import { Observable, Subject, takeUntil } from 'rxjs'; @Component({ - selector: 'app-multiple-search-results', - templateUrl: './multiple-search-results.component.html', - styleUrls: ['multiple-search-results.component.scss'], + selector: 'app-multiple-search-results', + templateUrl: './multiple-search-results.component.html', + styleUrls: ['multiple-search-results.component.scss'], }) export class MultipleSearchResultsComponent implements OnDestroy { - searchResults$: Observable; - ngDestroy$ = new Subject(); + searchResults$: Observable; + ngDestroy$ = new Subject(); - constructor( - public globalErrorService: GlobalErrorService, - private technicalRecordService: TechnicalRecordService, - private technicalRecordHttpService: TechnicalRecordHttpService, - private store: Store, - private location: Location, - ) { - this.store.pipe(select(selectQueryParams), takeUntil(this.ngDestroy$)).subscribe((params) => { - if (Object.keys(params).length === 1) { - const type = Object.keys(params)[0] as SEARCH_TYPES; - // eslint-disable-next-line security/detect-object-injection - const searchTerm = params[type] as string; + constructor( + public globalErrorService: GlobalErrorService, + private technicalRecordService: TechnicalRecordService, + private technicalRecordHttpService: TechnicalRecordHttpService, + private store: Store, + private location: Location + ) { + this.store.pipe(select(selectQueryParams), takeUntil(this.ngDestroy$)).subscribe((params) => { + if (Object.keys(params).length === 1) { + const type = Object.keys(params)[0] as SEARCH_TYPES; + // eslint-disable-next-line security/detect-object-injection + const searchTerm = params[type] as string; - if (searchTerm && Object.values(SEARCH_TYPES).includes(type)) { - this.globalErrorService.clearErrors(); - this.technicalRecordHttpService.searchBy(type, searchTerm); - } - } - }); + if (searchTerm && Object.values(SEARCH_TYPES).includes(type)) { + this.globalErrorService.clearErrors(); + this.technicalRecordHttpService.searchBy(type, searchTerm); + } + } + }); - this.searchResults$ = this.technicalRecordService.searchResultsWithUniqueSystemNumbers$; - } + this.searchResults$ = this.technicalRecordService.searchResultsWithUniqueSystemNumbers$; + } - navigateBack() { - this.globalErrorService.clearErrors(); - this.location.back(); - } + navigateBack() { + this.globalErrorService.clearErrors(); + this.location.back(); + } - ngOnDestroy() { - this.ngDestroy$.next(true); - this.ngDestroy$.complete(); - } + ngOnDestroy() { + this.ngDestroy$.next(true); + this.ngDestroy$.complete(); + } - public get Roles() { - return Roles; - } + public get Roles() { + return Roles; + } } diff --git a/src/app/features/search/search-routing.module.ts b/src/app/features/search/search-routing.module.ts index f65f369045..d1cfbbf88d 100644 --- a/src/app/features/search/search-routing.module.ts +++ b/src/app/features/search/search-routing.module.ts @@ -6,21 +6,21 @@ import { MultipleSearchResultsComponent } from './multiple-search-results/multip import { SearchComponent } from './search.component'; const routes: Routes = [ - { - path: '', - pathMatch: 'prefix', - component: SearchComponent, - }, - { - path: SearchRoutes.SEARCH_RESULT, - component: MultipleSearchResultsComponent, - canActivate: [NoQueryParamsGuard], - data: { title: 'Search Results' }, - }, + { + path: '', + pathMatch: 'prefix', + component: SearchComponent, + }, + { + path: SearchRoutes.SEARCH_RESULT, + component: MultipleSearchResultsComponent, + canActivate: [NoQueryParamsGuard], + data: { title: 'Search Results' }, + }, ]; @NgModule({ - imports: [RouterModule.forChild(routes)], - exports: [RouterModule], + imports: [RouterModule.forChild(routes)], + exports: [RouterModule], }) export class SearchRoutingModule {} diff --git a/src/app/features/search/search.component.spec.ts b/src/app/features/search/search.component.spec.ts index 76324f6ba7..6949a51442 100644 --- a/src/app/features/search/search.component.spec.ts +++ b/src/app/features/search/search.component.spec.ts @@ -5,124 +5,136 @@ import { RouterTestingModule } from '@angular/router/testing'; import { GlobalError } from '@core/components/global-error/global-error.interface'; import { GlobalErrorService } from '@core/components/global-error/global-error.service'; import { Roles } from '@models/roles.enum'; -import { MockStore, provideMockStore } from '@ngrx/store/testing'; import { SEARCH_TYPES } from '@models/search-types-enum'; +import { MockStore, provideMockStore } from '@ngrx/store/testing'; import { TechnicalRecordService } from '@services/technical-record/technical-record.service'; -import { initialAppState, State } from '@store/.'; +import { State, initialAppState } from '@store/.'; import { globalErrorState } from '@store/global-error/reducers/global-error-service.reducer'; import { of } from 'rxjs'; import { SearchComponent } from './search.component'; describe('SearchComponent', () => { - let component: SearchComponent; - let fixture: ComponentFixture; - let globalErrorService: GlobalErrorService; - let router: Router; - let store: MockStore; - const expectedError: GlobalError = { error: 'some-error', anchorLink: 'some-link' }; - const expectedErrors: GlobalError[] = [expectedError]; - - beforeEach(async () => { - await TestBed.configureTestingModule({ - declarations: [SearchComponent], - imports: [HttpClientTestingModule, RouterTestingModule], - providers: [GlobalErrorService, TechnicalRecordService, provideMockStore({ initialState: initialAppState })], - }).compileComponents(); - }); - - beforeEach(() => { - store = TestBed.inject(MockStore); - store.overrideSelector(globalErrorState, expectedErrors); - fixture = TestBed.createComponent(SearchComponent); - component = fixture.componentInstance; - globalErrorService = TestBed.inject(GlobalErrorService); - router = TestBed.inject(Router); - - jest.clearAllMocks(); - }); - - it('should create', () => expect(component).toBeTruthy()); - - describe('searching', () => { - describe('navigateSearch', () => { - it('should navigate to vin search result', () => { - const navigateSpy = jest.spyOn(router, 'navigate').mockImplementation(() => Promise.resolve(true)); - - const expectedVin = 'someVin'; - component.navigateSearch(expectedVin, SEARCH_TYPES.VIN); - - expect(navigateSpy).toHaveBeenCalledWith(['/search/results'], { queryParams: { vin: expectedVin } }); - }); - - it('should navigate to partialVin search result', () => { - const navigateSpy = jest.spyOn(router, 'navigate').mockImplementation(() => Promise.resolve(true)); - - const expectedPartialVin = 'somePartialVin'; - component.navigateSearch(expectedPartialVin, SEARCH_TYPES.PARTIAL_VIN); - - expect(navigateSpy).toHaveBeenCalledWith(['/search/results'], { queryParams: { partialVin: expectedPartialVin } }); - }); - - it('should navigate to vrm search result', () => { - const navigateSpy = jest.spyOn(router, 'navigate').mockImplementation(() => Promise.resolve(true)); - - const expectedVrm = 'someVrm'; - component.navigateSearch(expectedVrm, SEARCH_TYPES.VRM); - - expect(navigateSpy).toHaveBeenCalledWith(['/search/results'], { queryParams: { primaryVrm: expectedVrm } }); - }); - - it('should navigate to trailerId search result', () => { - const navigateSpy = jest.spyOn(router, 'navigate').mockImplementation(() => Promise.resolve(true)); - - const expectedTrailerId = 'someTrailerId'; - component.navigateSearch(expectedTrailerId, SEARCH_TYPES.TRAILER_ID); - - expect(navigateSpy).toHaveBeenCalledWith(['/search/results'], { queryParams: { trailerId: expectedTrailerId } }); - }); - }); - - describe('invalid input', () => { - it('should add search-term error', () => { - const addErrorSpy = jest.spyOn(globalErrorService, 'addError').mockImplementation(() => {}); - - component.navigateSearch('', ''); - - expect(addErrorSpy).toHaveBeenCalledWith({ error: component.missingTermErrorMessage, anchorLink: 'search-term' }); - }); - - it('should add search-type error', () => { - const addErrorSpy = jest.spyOn(globalErrorService, 'addError').mockImplementation(() => {}); - - component.navigateSearch('some term', ''); - - expect(addErrorSpy).toHaveBeenCalledWith({ error: component.missingTypeErrorMessage, anchorLink: 'search-type' }); - }); - }); - - describe('helper methods', () => { - it('should get inline error message', (done) => { - const addErrorSpy = jest.spyOn(globalErrorService, 'errors$', 'get').mockImplementation(() => of(expectedErrors)); - - component.getInlineErrorMessage(expectedError.anchorLink ?? '').subscribe((response) => { - expect(response).toBeTruthy(); - done(); - }); // subscribe to activate the map inside 'getInlineErrorMessage()' + let component: SearchComponent; + let fixture: ComponentFixture; + let globalErrorService: GlobalErrorService; + let router: Router; + let store: MockStore; + const expectedError: GlobalError = { error: 'some-error', anchorLink: 'some-link' }; + const expectedErrors: GlobalError[] = [expectedError]; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [SearchComponent], + imports: [HttpClientTestingModule, RouterTestingModule], + providers: [GlobalErrorService, TechnicalRecordService, provideMockStore({ initialState: initialAppState })], + }).compileComponents(); + }); + + beforeEach(() => { + store = TestBed.inject(MockStore); + store.overrideSelector(globalErrorState, expectedErrors); + fixture = TestBed.createComponent(SearchComponent); + component = fixture.componentInstance; + globalErrorService = TestBed.inject(GlobalErrorService); + router = TestBed.inject(Router); + + jest.clearAllMocks(); + }); + + it('should create', () => expect(component).toBeTruthy()); + + describe('searching', () => { + describe('navigateSearch', () => { + it('should navigate to vin search result', () => { + const navigateSpy = jest.spyOn(router, 'navigate').mockImplementation(() => Promise.resolve(true)); + + const expectedVin = 'someVin'; + component.navigateSearch(expectedVin, SEARCH_TYPES.VIN); + + expect(navigateSpy).toHaveBeenCalledWith(['/search/results'], { queryParams: { vin: expectedVin } }); + }); + + it('should navigate to partialVin search result', () => { + const navigateSpy = jest.spyOn(router, 'navigate').mockImplementation(() => Promise.resolve(true)); + + const expectedPartialVin = 'somePartialVin'; + component.navigateSearch(expectedPartialVin, SEARCH_TYPES.PARTIAL_VIN); + + expect(navigateSpy).toHaveBeenCalledWith(['/search/results'], { + queryParams: { partialVin: expectedPartialVin }, + }); + }); + + it('should navigate to vrm search result', () => { + const navigateSpy = jest.spyOn(router, 'navigate').mockImplementation(() => Promise.resolve(true)); + + const expectedVrm = 'someVrm'; + component.navigateSearch(expectedVrm, SEARCH_TYPES.VRM); + + expect(navigateSpy).toHaveBeenCalledWith(['/search/results'], { queryParams: { primaryVrm: expectedVrm } }); + }); + + it('should navigate to trailerId search result', () => { + const navigateSpy = jest.spyOn(router, 'navigate').mockImplementation(() => Promise.resolve(true)); + + const expectedTrailerId = 'someTrailerId'; + component.navigateSearch(expectedTrailerId, SEARCH_TYPES.TRAILER_ID); + + expect(navigateSpy).toHaveBeenCalledWith(['/search/results'], { + queryParams: { trailerId: expectedTrailerId }, + }); + }); + }); + + describe('invalid input', () => { + it('should add search-term error', () => { + const addErrorSpy = jest.spyOn(globalErrorService, 'addError').mockImplementation(() => {}); + + component.navigateSearch('', ''); + + expect(addErrorSpy).toHaveBeenCalledWith({ + error: component.missingTermErrorMessage, + anchorLink: 'search-term', + }); + }); + + it('should add search-type error', () => { + const addErrorSpy = jest.spyOn(globalErrorService, 'addError').mockImplementation(() => {}); + + component.navigateSearch('some term', ''); + + expect(addErrorSpy).toHaveBeenCalledWith({ + error: component.missingTypeErrorMessage, + anchorLink: 'search-type', + }); + }); + }); + + describe('helper methods', () => { + it('should get inline error message', (done) => { + const addErrorSpy = jest + .spyOn(globalErrorService, 'errors$', 'get') + .mockImplementation(() => of(expectedErrors)); + + component.getInlineErrorMessage(expectedError.anchorLink ?? '').subscribe((response) => { + expect(response).toBeTruthy(); + done(); + }); // subscribe to activate the map inside 'getInlineErrorMessage()' - expect(addErrorSpy).toHaveBeenCalled(); - }); + expect(addErrorSpy).toHaveBeenCalled(); + }); - it('should get error by name', () => { - const error = component.getErrorByName(expectedErrors, expectedError.anchorLink ?? ''); + it('should get error by name', () => { + const error = component.getErrorByName(expectedErrors, expectedError.anchorLink ?? ''); - expect(error).toEqual(expectedError); - }); + expect(error).toEqual(expectedError); + }); - it('should return roles', () => { - const { roles } = component; + it('should return roles', () => { + const { roles } = component; - expect(roles).toBe(Roles); - }); - }); - }); + expect(roles).toBe(Roles); + }); + }); + }); }); diff --git a/src/app/features/search/search.component.ts b/src/app/features/search/search.component.ts index e571720163..cba92aacbd 100644 --- a/src/app/features/search/search.component.ts +++ b/src/app/features/search/search.component.ts @@ -9,39 +9,44 @@ import { clearAllSectionStates, clearScrollPosition } from '@store/technical-rec import { Observable, map } from 'rxjs'; @Component({ - selector: 'app-search', - templateUrl: './search.component.html', + selector: 'app-search', + templateUrl: './search.component.html', }) export class SearchComponent { - missingTermErrorMessage = 'You must provide a vehicle registration mark, trailer ID or vehicle identification number.'; - missingTypeErrorMessage = 'You must select a valid search criteria'; + missingTermErrorMessage = + 'You must provide a vehicle registration mark, trailer ID or vehicle identification number.'; + missingTypeErrorMessage = 'You must select a valid search criteria'; - constructor(public globalErrorService: GlobalErrorService, private router: Router, private store: Store) {} + constructor( + public globalErrorService: GlobalErrorService, + private router: Router, + private store: Store + ) {} - navigateSearch(term: string, type: string): void { - this.globalErrorService.clearErrors(); - this.store.dispatch(clearAllSectionStates()); - this.store.dispatch(clearScrollPosition()); - term = term.trim(); + navigateSearch(term: string, type: string): void { + this.globalErrorService.clearErrors(); + this.store.dispatch(clearAllSectionStates()); + this.store.dispatch(clearScrollPosition()); + term = term.trim(); - if (!term) { - this.globalErrorService.addError({ error: this.missingTermErrorMessage, anchorLink: 'search-term' }); - } else if (!Object.values(SEARCH_TYPES).includes(type as SEARCH_TYPES)) { - this.globalErrorService.addError({ error: this.missingTypeErrorMessage, anchorLink: 'search-type' }); - } else { - void this.router.navigate(['/search/results'], { queryParams: { [type]: term } }); - } - } + if (!term) { + this.globalErrorService.addError({ error: this.missingTermErrorMessage, anchorLink: 'search-term' }); + } else if (!Object.values(SEARCH_TYPES).includes(type as SEARCH_TYPES)) { + this.globalErrorService.addError({ error: this.missingTypeErrorMessage, anchorLink: 'search-type' }); + } else { + void this.router.navigate(['/search/results'], { queryParams: { [type]: term } }); + } + } - getInlineErrorMessage(name: string): Observable { - return this.globalErrorService.errors$.pipe(map((errors) => errors.some((error) => error.anchorLink === name))); - } + getInlineErrorMessage(name: string): Observable { + return this.globalErrorService.errors$.pipe(map((errors) => errors.some((error) => error.anchorLink === name))); + } - getErrorByName(errors: GlobalError[], name: string): GlobalError | undefined { - return errors.find((error) => error.anchorLink === name); - } + getErrorByName(errors: GlobalError[], name: string): GlobalError | undefined { + return errors.find((error) => error.anchorLink === name); + } - public get roles() { - return Roles; - } + public get roles() { + return Roles; + } } diff --git a/src/app/features/search/search.module.ts b/src/app/features/search/search.module.ts index 2f71da2d2b..bd3df06de8 100644 --- a/src/app/features/search/search.module.ts +++ b/src/app/features/search/search.module.ts @@ -3,13 +3,13 @@ import { NgModule } from '@angular/core'; import { RouterModule } from '@angular/router'; import { DynamicFormsModule } from '@forms/dynamic-forms.module'; import { SharedModule } from '@shared/shared.module'; +import { MultipleSearchResultsComponent } from './multiple-search-results/multiple-search-results.component'; import { SearchRoutingModule } from './search-routing.module'; import { SearchComponent } from './search.component'; import { SingleSearchResultComponent } from './single-search-result/single-search-result.component'; -import { MultipleSearchResultsComponent } from './multiple-search-results/multiple-search-results.component'; @NgModule({ - declarations: [SearchComponent, SingleSearchResultComponent, MultipleSearchResultsComponent], - imports: [CommonModule, DynamicFormsModule, RouterModule, SearchRoutingModule, SharedModule], + declarations: [SearchComponent, SingleSearchResultComponent, MultipleSearchResultsComponent], + imports: [CommonModule, DynamicFormsModule, RouterModule, SearchRoutingModule, SharedModule], }) export class SearchModule {} diff --git a/src/app/features/search/search.stories.ts b/src/app/features/search/search.stories.ts index b4f29e1394..f28c11f69e 100644 --- a/src/app/features/search/search.stories.ts +++ b/src/app/features/search/search.stories.ts @@ -1,20 +1,20 @@ -import { moduleMetadata, Meta, Story } from '@storybook/angular'; import { HttpClientModule } from '@angular/common/http'; +import { Meta, Story, moduleMetadata } from '@storybook/angular'; import { SearchComponent } from './search.component'; export default { - title: 'Search Page', - component: SearchComponent, - decorators: [ - moduleMetadata({ - declarations: [SearchComponent], - imports: [HttpClientModule] - }) - ] + title: 'Search Page', + component: SearchComponent, + decorators: [ + moduleMetadata({ + declarations: [SearchComponent], + imports: [HttpClientModule], + }), + ], } as Meta; -const Template: Story = args => ({ - props: args +const Template: Story = (args) => ({ + props: args, }); export const Initial = Template.bind({}); @@ -22,10 +22,10 @@ Initial.args = {}; export const Loading = Template.bind({}); Loading.args = { - isLoading: true + isLoading: true, }; export const Error = Template.bind({}); Error.args = { - searchError: 'This is an error' + searchError: 'This is an error', }; diff --git a/src/app/features/search/single-search-result/single-search-result.component.spec.ts b/src/app/features/search/single-search-result/single-search-result.component.spec.ts index e8c9d70cc1..8218f51eff 100644 --- a/src/app/features/search/single-search-result/single-search-result.component.spec.ts +++ b/src/app/features/search/single-search-result/single-search-result.component.spec.ts @@ -12,42 +12,42 @@ import { ReplaySubject, of } from 'rxjs'; import { SingleSearchResultComponent } from './single-search-result.component'; describe('SingleSearchResultComponent', () => { - let component: SingleSearchResultComponent; - let fixture: ComponentFixture; - const actions$ = new ReplaySubject(); + let component: SingleSearchResultComponent; + let fixture: ComponentFixture; + const actions$ = new ReplaySubject(); - beforeEach(async () => { - await TestBed.configureTestingModule({ - declarations: [SingleSearchResultComponent, RoleRequiredDirective], - imports: [DynamicFormsModule, HttpClientTestingModule, RouterTestingModule], - providers: [ - provideMockStore({ initialState: initialAppState }), - provideMockActions(() => actions$), - { - provide: UserService, - useValue: { - roles$: of(['TechRecord.View']), - }, - }, - ], - }).compileComponents(); - }); + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [SingleSearchResultComponent, RoleRequiredDirective], + imports: [DynamicFormsModule, HttpClientTestingModule, RouterTestingModule], + providers: [ + provideMockStore({ initialState: initialAppState }), + provideMockActions(() => actions$), + { + provide: UserService, + useValue: { + roles$: of(['TechRecord.View']), + }, + }, + ], + }).compileComponents(); + }); - beforeEach(() => { - fixture = TestBed.createComponent(SingleSearchResultComponent); - component = fixture.componentInstance; - component.searchResult = { - systemNumber: '123', - createdTimestamp: '123', - vin: '76890', - techRecord_vehicleType: 'psv', - techRecord_statusCode: 'current', - techRecord_manufactureYear: 1998, - }; - }); + beforeEach(() => { + fixture = TestBed.createComponent(SingleSearchResultComponent); + component = fixture.componentInstance; + component.searchResult = { + systemNumber: '123', + createdTimestamp: '123', + vin: '76890', + techRecord_vehicleType: 'psv', + techRecord_statusCode: 'current', + techRecord_manufactureYear: 1998, + }; + }); - it('should create', () => { - fixture.detectChanges(); - expect(component).toBeTruthy(); - }); + it('should create', () => { + fixture.detectChanges(); + expect(component).toBeTruthy(); + }); }); diff --git a/src/app/features/search/single-search-result/single-search-result.component.ts b/src/app/features/search/single-search-result/single-search-result.component.ts index 9cbfa2ca50..205aee4a80 100644 --- a/src/app/features/search/single-search-result/single-search-result.component.ts +++ b/src/app/features/search/single-search-result/single-search-result.component.ts @@ -1,46 +1,50 @@ -import { - ChangeDetectionStrategy, Component, Input, OnInit, -} from '@angular/core'; +import { ChangeDetectionStrategy, Component, Input, OnInit } from '@angular/core'; import { TechRecordSearchSchema } from '@dvsa/cvs-type-definitions/types/v3/tech-record/get/search'; import { FormNode } from '@forms/services/dynamic-form.types'; import { createSingleSearchResult } from '@forms/templates/search/single-search-result.template'; import { Roles } from '@models/roles.enum'; @Component({ - selector: 'app-single-search-result[searchResult]', - templateUrl: './single-search-result.component.html', - changeDetection: ChangeDetectionStrategy.OnPush, + selector: 'app-single-search-result[searchResult]', + templateUrl: './single-search-result.component.html', + changeDetection: ChangeDetectionStrategy.OnPush, }) export class SingleSearchResultComponent implements OnInit { - @Input() searchResult!: TechRecordSearchSchema; - vehicleDisplayData?: VehicleDisplayData; - template?: FormNode; + @Input() searchResult!: TechRecordSearchSchema; + vehicleDisplayData?: VehicleDisplayData; + template?: FormNode; - ngOnInit(): void { - this.vehicleDisplayData = { - vin: this.searchResult.vin, - vrm: this.searchResult.primaryVrm, - trailerId: this.searchResult.trailerId, - make: this.searchResult.techRecord_vehicleType === 'psv' ? this.searchResult.techRecord_chassisMake : this.searchResult.techRecord_make, - model: this.searchResult.techRecord_vehicleType === 'psv' ? this.searchResult.techRecord_chassisModel : this.searchResult.techRecord_model, - manufactureYear: this.searchResult.techRecord_manufactureYear, - vehicleType: this.searchResult.techRecord_vehicleType.toUpperCase(), - }; + ngOnInit(): void { + this.vehicleDisplayData = { + vin: this.searchResult.vin, + vrm: this.searchResult.primaryVrm, + trailerId: this.searchResult.trailerId, + make: + this.searchResult.techRecord_vehicleType === 'psv' + ? this.searchResult.techRecord_chassisMake + : this.searchResult.techRecord_make, + model: + this.searchResult.techRecord_vehicleType === 'psv' + ? this.searchResult.techRecord_chassisModel + : this.searchResult.techRecord_model, + manufactureYear: this.searchResult.techRecord_manufactureYear, + vehicleType: this.searchResult.techRecord_vehicleType.toUpperCase(), + }; - this.template = createSingleSearchResult(this.searchResult.systemNumber, this.searchResult.createdTimestamp); - } + this.template = createSingleSearchResult(this.searchResult.systemNumber, this.searchResult.createdTimestamp); + } - public get roles() { - return Roles; - } + public get roles() { + return Roles; + } } interface VehicleDisplayData { - vin?: string; - vrm?: string; - trailerId?: string; - make?: string | null; - model?: string | null; - manufactureYear?: number | null; - vehicleType?: string; + vin?: string; + vrm?: string; + trailerId?: string; + make?: string | null; + model?: string | null; + manufactureYear?: number | null; + vehicleType?: string; } diff --git a/src/app/features/tech-record/components/adr-generate-certificate/adr-generate-certificate.component.spec.ts b/src/app/features/tech-record/components/adr-generate-certificate/adr-generate-certificate.component.spec.ts index 1aa04bbc4d..8db81d1221 100644 --- a/src/app/features/tech-record/components/adr-generate-certificate/adr-generate-certificate.component.spec.ts +++ b/src/app/features/tech-record/components/adr-generate-certificate/adr-generate-certificate.component.spec.ts @@ -1,7 +1,5 @@ import { HttpClientTestingModule } from '@angular/common/http/testing'; -import { - ComponentFixture, fakeAsync, TestBed, tick, -} from '@angular/core/testing'; +import { ComponentFixture, TestBed, fakeAsync, tick } from '@angular/core/testing'; import { ReactiveFormsModule } from '@angular/forms'; import { ActivatedRoute, Router } from '@angular/router'; import { RouterTestingModule } from '@angular/router/testing'; @@ -17,146 +15,151 @@ import { UserService } from '@services/user-service/user-service'; import { SharedModule } from '@shared/shared.module'; import { initialAppState } from '@store/index'; import { generateADRCertificate, generateADRCertificateSuccess } from '@store/technical-records'; -import { of, ReplaySubject } from 'rxjs'; +import { ReplaySubject, of } from 'rxjs'; import { AdrGenerateCertificateComponent } from './adr-generate-certificate.component'; const mockDynamicFormService = { - createForm: jest.fn(), + createForm: jest.fn(), }; describe('AdrGenerateCertificateComponent', () => { - let component: AdrGenerateCertificateComponent; - let fixture: ComponentFixture; - const actions$ = new ReplaySubject(); - let errorService: GlobalErrorService; - let route: ActivatedRoute; - let router: Router; - let store: MockStore; - let technicalRecordService: TechnicalRecordService; - - beforeEach(async () => { - await TestBed.configureTestingModule({ - declarations: [AdrGenerateCertificateComponent], - providers: [ - GlobalErrorService, - provideMockActions(() => actions$), - provideMockStore({ initialState: initialAppState }), - { provide: ActivatedRoute, useValue: { params: of([{ id: 1 }]) } }, - { provide: DynamicFormService, useValue: mockDynamicFormService }, - TechnicalRecordService, - { - provide: UserService, - useValue: { - roles$: of(['TechRecord.Amend']), - }, - }, - ], - imports: [RouterTestingModule, SharedModule, ReactiveFormsModule, DynamicFormsModule, HttpClientTestingModule], - }).compileComponents(); - }); - beforeEach(() => { - fixture = TestBed.createComponent(AdrGenerateCertificateComponent); - errorService = TestBed.inject(GlobalErrorService); - route = TestBed.inject(ActivatedRoute); - router = TestBed.inject(Router); - store = TestBed.inject(MockStore); - technicalRecordService = TestBed.inject(TechnicalRecordService); - component = fixture.componentInstance; - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); - - describe('navigateBack', () => { - beforeEach(() => { - jest - .spyOn(technicalRecordService, 'techRecord$', 'get') - .mockReturnValue(of({ systemNumber: 'foo', createdTimestamp: 'bar', vin: 'testVin' } as V3TechRecordModel)); - }); - it('should clear all errors', () => { - jest.spyOn(router, 'navigate').mockImplementation(); - - const clearErrorsSpy = jest.spyOn(errorService, 'clearErrors'); - - component.navigateBack(); - - expect(clearErrorsSpy).toHaveBeenCalledTimes(1); - }); - - it('should navigate back to the previous page', () => { - const navigateSpy = jest.spyOn(router, 'navigate').mockImplementation(() => Promise.resolve(true)); - - component.navigateBack(); - - expect(navigateSpy).toHaveBeenCalledWith(['..'], { relativeTo: route }); - }); - - it('should navigate back on generateADRCertificateSuccess', fakeAsync(() => { - fixture.ngZone?.run(() => { - component.ngOnInit(); - component.form.get('certificateType')?.setValue('PASS'); - - const navigateBackSpy = jest.spyOn(component, 'navigateBack').mockImplementation(); - - component.handleSubmit(); - - actions$.next(generateADRCertificateSuccess({ id: '' })); - tick(); - - expect(navigateBackSpy).toHaveBeenCalled(); - }); - })); - }); - - describe('handleSubmit', () => { - beforeEach(() => { - jest - .spyOn(technicalRecordService, 'techRecord$', 'get') - .mockReturnValue(of({ systemNumber: 'foo', createdTimestamp: 'bar', vin: 'testVin' } as V3TechRecordModel)); - }); - it('should add an error when the field is not filled out', () => { - const addErrorSpy = jest.spyOn(errorService, 'addError'); - - component.handleSubmit(); - - expect(addErrorSpy).toHaveBeenCalledWith({ error: 'ADR Certificate Type is required', anchorLink: 'certificateType' }); - }); - - it('should dispatch the generateADRCertificate action', () => { - const dispatchSpy = jest.spyOn(store, 'dispatch'); - - component.form.get('certificateType')?.setValue('PASS'); - - component.handleSubmit(); - expect(dispatchSpy).toHaveBeenCalledTimes(2); - expect(dispatchSpy).toHaveBeenLastCalledWith(generateADRCertificate({ systemNumber: '', createdTimestamp: '', certificateType: 'PASS' })); - }); - - it('should dispatch action with default values when systemNumber and createdTimestamp are null', () => { - const dispatchSpy = jest.spyOn(store, 'dispatch'); - - component.form.get('certificateType')?.setValue('PASS'); - component.systemNumber = '123'; - component.createdTimestamp = '2021'; - - component.handleSubmit(); - expect(dispatchSpy).toHaveBeenCalled(); - expect(dispatchSpy).toHaveBeenLastCalledWith( - generateADRCertificate({ systemNumber: '123', createdTimestamp: '2021', certificateType: 'PASS' }), - ); - }); - }); - - describe('certificateTypes', () => { - it('should get correct value when call get functions', () => { - const expectedValue = [ - { label: 'New ADR Certificate', value: 'PASS' }, - { label: 'Replacement ADR Certificate', value: 'REPLACEMENT' }, - ]; - expect(component.width).toBe(10); - expect(component.certificateTypes).toEqual(expectedValue); - }); - }); + let component: AdrGenerateCertificateComponent; + let fixture: ComponentFixture; + const actions$ = new ReplaySubject(); + let errorService: GlobalErrorService; + let route: ActivatedRoute; + let router: Router; + let store: MockStore; + let technicalRecordService: TechnicalRecordService; + + beforeEach(() => { + TestBed.configureTestingModule({ + declarations: [AdrGenerateCertificateComponent], + providers: [ + GlobalErrorService, + provideMockActions(() => actions$), + provideMockStore({ initialState: initialAppState }), + { provide: ActivatedRoute, useValue: { params: of([{ id: 1 }]) } }, + { provide: DynamicFormService, useValue: mockDynamicFormService }, + TechnicalRecordService, + { + provide: UserService, + useValue: { + roles$: of(['TechRecord.Amend']), + }, + }, + ], + imports: [RouterTestingModule, SharedModule, ReactiveFormsModule, DynamicFormsModule, HttpClientTestingModule], + }).compileComponents(); + }); + beforeEach(() => { + fixture = TestBed.createComponent(AdrGenerateCertificateComponent); + errorService = TestBed.inject(GlobalErrorService); + route = TestBed.inject(ActivatedRoute); + router = TestBed.inject(Router); + store = TestBed.inject(MockStore); + technicalRecordService = TestBed.inject(TechnicalRecordService); + component = fixture.componentInstance; + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + describe('navigateBack', () => { + beforeEach(() => { + jest + .spyOn(technicalRecordService, 'techRecord$', 'get') + .mockReturnValue(of({ systemNumber: 'foo', createdTimestamp: 'bar', vin: 'testVin' } as V3TechRecordModel)); + }); + it('should clear all errors', () => { + jest.spyOn(router, 'navigate').mockImplementation(); + + const clearErrorsSpy = jest.spyOn(errorService, 'clearErrors'); + + component.navigateBack(); + + expect(clearErrorsSpy).toHaveBeenCalledTimes(1); + }); + + it('should navigate back to the previous page', () => { + const navigateSpy = jest.spyOn(router, 'navigate').mockImplementation(() => Promise.resolve(true)); + + component.navigateBack(); + + expect(navigateSpy).toHaveBeenCalledWith(['..'], { relativeTo: route }); + }); + + it('should navigate back on generateADRCertificateSuccess', fakeAsync(() => { + fixture.ngZone?.run(() => { + component.ngOnInit(); + component.form.get('certificateType')?.setValue('PASS'); + + const navigateBackSpy = jest.spyOn(component, 'navigateBack').mockImplementation(); + + component.handleSubmit(); + + actions$.next(generateADRCertificateSuccess({ id: '' })); + tick(); + + expect(navigateBackSpy).toHaveBeenCalled(); + }); + })); + }); + + describe('handleSubmit', () => { + beforeEach(() => { + jest + .spyOn(technicalRecordService, 'techRecord$', 'get') + .mockReturnValue(of({ systemNumber: 'foo', createdTimestamp: 'bar', vin: 'testVin' } as V3TechRecordModel)); + }); + it('should add an error when the field is not filled out', () => { + const addErrorSpy = jest.spyOn(errorService, 'addError'); + + component.handleSubmit(); + + expect(addErrorSpy).toHaveBeenCalledWith({ + error: 'ADR Certificate Type is required', + anchorLink: 'certificateType', + }); + }); + + it('should dispatch the generateADRCertificate action', () => { + const dispatchSpy = jest.spyOn(store, 'dispatch'); + + component.form.get('certificateType')?.setValue('PASS'); + + component.handleSubmit(); + expect(dispatchSpy).toHaveBeenCalledTimes(2); + expect(dispatchSpy).toHaveBeenLastCalledWith( + generateADRCertificate({ systemNumber: '', createdTimestamp: '', certificateType: 'PASS' }) + ); + }); + + it('should dispatch action with default values when systemNumber and createdTimestamp are null', () => { + const dispatchSpy = jest.spyOn(store, 'dispatch'); + + component.form.get('certificateType')?.setValue('PASS'); + component.systemNumber = '123'; + component.createdTimestamp = '2021'; + + component.handleSubmit(); + expect(dispatchSpy).toHaveBeenCalled(); + expect(dispatchSpy).toHaveBeenLastCalledWith( + generateADRCertificate({ systemNumber: '123', createdTimestamp: '2021', certificateType: 'PASS' }) + ); + }); + }); + + describe('certificateTypes', () => { + it('should get correct value when call get functions', () => { + const expectedValue = [ + { label: 'New ADR Certificate', value: 'PASS' }, + { label: 'Replacement ADR Certificate', value: 'REPLACEMENT' }, + ]; + expect(component.width).toBe(10); + expect(component.certificateTypes).toEqual(expectedValue); + }); + }); }); diff --git a/src/app/features/tech-record/components/adr-generate-certificate/adr-generate-certificate.component.ts b/src/app/features/tech-record/components/adr-generate-certificate/adr-generate-certificate.component.ts index 12c729915c..3bfad24248 100644 --- a/src/app/features/tech-record/components/adr-generate-certificate/adr-generate-certificate.component.ts +++ b/src/app/features/tech-record/components/adr-generate-certificate/adr-generate-certificate.component.ts @@ -2,85 +2,91 @@ import { Component, OnDestroy, OnInit } from '@angular/core'; import { FormGroup, Validators } from '@angular/forms'; import { ActivatedRoute, Router } from '@angular/router'; import { GlobalErrorService } from '@core/components/global-error/global-error.service'; -import { - CustomFormControl, FormNodeOption, FormNodeTypes, FormNodeWidth, -} from '@forms/services/dynamic-form.types'; -import { UserService } from '@services/user-service/user-service'; import { ADRCertificateTypes } from '@dvsa/cvs-type-definitions/types/v3/tech-record/enums/adrCertificateTypes.enum.js'; -import { Subject, takeUntil, take } from 'rxjs'; +import { CustomFormControl, FormNodeOption, FormNodeTypes, FormNodeWidth } from '@forms/services/dynamic-form.types'; +import { Actions, ofType } from '@ngrx/effects'; import { Store } from '@ngrx/store'; +import { UserService } from '@services/user-service/user-service'; import { State } from '@store/index'; import { generateADRCertificate, generateADRCertificateSuccess } from '@store/technical-records'; -import { Actions, ofType } from '@ngrx/effects'; +import { Subject, take, takeUntil } from 'rxjs'; @Component({ - selector: 'app-adr-generate-certificate', - templateUrl: './adr-generate-certificate.component.html', + selector: 'app-adr-generate-certificate', + templateUrl: './adr-generate-certificate.component.html', }) export class AdrGenerateCertificateComponent implements OnInit, OnDestroy { - systemNumber?: string; - createdTimestamp?: string; - - form = new FormGroup({ - certificateType: new CustomFormControl({ - name: 'certificateType', label: 'Select certificate type', type: FormNodeTypes.CONTROL, - }, '', [Validators.required]), - }); + systemNumber?: string; + createdTimestamp?: string; - private destroy$ = new Subject(); + form = new FormGroup({ + certificateType: new CustomFormControl( + { + name: 'certificateType', + label: 'Select certificate type', + type: FormNodeTypes.CONTROL, + }, + '', + [Validators.required] + ), + }); - constructor( - private actions$: Actions, - private globalErrorService: GlobalErrorService, - private route: ActivatedRoute, - private router: Router, - public userService: UserService, - private store: Store, - ) {} + private destroy$ = new Subject(); - ngOnInit(): void { - this.route.params.pipe(takeUntil(this.destroy$)).subscribe((params) => { - this.systemNumber = params['systemNumber']; - this.createdTimestamp = params['createdTimestamp']; - }); - this.actions$.pipe(ofType(generateADRCertificateSuccess), take(1)).subscribe(() => { - this.navigateBack(); - }); - } + constructor( + private actions$: Actions, + private globalErrorService: GlobalErrorService, + private route: ActivatedRoute, + private router: Router, + public userService: UserService, + private store: Store + ) {} - ngOnDestroy(): void { - this.destroy$.next(); - this.destroy$.complete(); - } + ngOnInit(): void { + this.route.params.pipe(takeUntil(this.destroy$)).subscribe((params) => { + this.systemNumber = params['systemNumber']; + this.createdTimestamp = params['createdTimestamp']; + }); + this.actions$.pipe(ofType(generateADRCertificateSuccess), take(1)).subscribe(() => { + this.navigateBack(); + }); + } - get width(): FormNodeWidth { - return FormNodeWidth.L; - } + ngOnDestroy(): void { + this.destroy$.next(); + this.destroy$.complete(); + } - get certificateTypes(): Array> { - return [ - { label: 'New ADR Certificate', value: ADRCertificateTypes.PASS }, - { label: 'Replacement ADR Certificate', value: ADRCertificateTypes.REPLACEMENT }, + get width(): FormNodeWidth { + return FormNodeWidth.L; + } - ]; - } + get certificateTypes(): Array> { + return [ + { label: 'New ADR Certificate', value: ADRCertificateTypes.PASS }, + { label: 'Replacement ADR Certificate', value: ADRCertificateTypes.REPLACEMENT }, + ]; + } - navigateBack() { - this.globalErrorService.clearErrors(); - void this.router.navigate(['..'], { relativeTo: this.route }); - } + navigateBack() { + this.globalErrorService.clearErrors(); + void this.router.navigate(['..'], { relativeTo: this.route }); + } - handleSubmit(): void { - this.globalErrorService.clearErrors(); - if (!this.form.value.certificateType) { - return this.globalErrorService.addError({ error: 'ADR Certificate Type is required', anchorLink: 'certificateType' }); - } - this.store.dispatch(generateADRCertificate( - { - systemNumber: this.systemNumber ?? '', - createdTimestamp: this.createdTimestamp ?? '', - certificateType: this.form.value.certificateType, - }, - )); - } + handleSubmit(): void { + this.globalErrorService.clearErrors(); + if (!this.form.value.certificateType) { + return this.globalErrorService.addError({ + error: 'ADR Certificate Type is required', + anchorLink: 'certificateType', + }); + } + this.store.dispatch( + generateADRCertificate({ + systemNumber: this.systemNumber ?? '', + createdTimestamp: this.createdTimestamp ?? '', + certificateType: this.form.value.certificateType, + }) + ); + } } diff --git a/src/app/features/tech-record/components/edit-tech-record-button/edit-tech-record-button.component.spec.ts b/src/app/features/tech-record/components/edit-tech-record-button/edit-tech-record-button.component.spec.ts index 66080fdfa0..1dd1330d5e 100644 --- a/src/app/features/tech-record/components/edit-tech-record-button/edit-tech-record-button.component.spec.ts +++ b/src/app/features/tech-record/components/edit-tech-record-button/edit-tech-record-button.component.spec.ts @@ -1,8 +1,6 @@ import { APP_BASE_HREF } from '@angular/common'; import { HttpClientTestingModule } from '@angular/common/http/testing'; -import { - ComponentFixture, discardPeriodicTasks, fakeAsync, TestBed, -} from '@angular/core/testing'; +import { ComponentFixture, TestBed, discardPeriodicTasks, fakeAsync } from '@angular/core/testing'; import { By } from '@angular/platform-browser'; import { Router } from '@angular/router'; import { RouterTestingModule } from '@angular/router/testing'; @@ -18,17 +16,17 @@ import { SharedModule } from '@shared/shared.module'; import { initialAppState } from '@store/.'; import { clearError } from '@store/global-error/actions/global-error.actions'; import { updateEditingTechRecordCancel } from '@store/technical-records'; -import { BehaviorSubject, of, ReplaySubject } from 'rxjs'; +import { BehaviorSubject, ReplaySubject, of } from 'rxjs'; import { EditTechRecordButtonComponent } from './edit-tech-record-button.component'; const mockTechRecordService = { - techRecord$: of({ - systemNumber: 'foo', - createdTimestamp: 'bar', - vin: 'testVin', - techRecord_statusCode: StatusCodes.CURRENT, - } as V3TechRecordModel), - clearReasonForCreation: jest.fn(), + techRecord$: of({ + systemNumber: 'foo', + createdTimestamp: 'bar', + vin: 'testVin', + techRecord_statusCode: StatusCodes.CURRENT, + } as V3TechRecordModel), + clearReasonForCreation: jest.fn(), }; let component: EditTechRecordButtonComponent; @@ -36,271 +34,273 @@ let fixture: ComponentFixture; let router: Router; let store: MockStore; let actions$: ReplaySubject; -const mockTechnicalRecordObservable = new BehaviorSubject({ techRecord_statusCode: StatusCodes.CURRENT } as V3TechRecordModel); +const mockTechnicalRecordObservable = new BehaviorSubject({ + techRecord_statusCode: StatusCodes.CURRENT, +} as V3TechRecordModel); const updateMockTechnicalRecord = (techRecord_statusCode: StatusCodes) => - mockTechnicalRecordObservable.next({ techRecord_statusCode } as V3TechRecordModel); + mockTechnicalRecordObservable.next({ techRecord_statusCode } as V3TechRecordModel); const mockRouterService = { - getRouteNestedParam$: () => '1', - getRouteDataProperty$: () => false, + getRouteNestedParam$: () => '1', + getRouteDataProperty$: () => false, }; describe('EditTechRecordButtonComponent', () => { - beforeEach(async () => { - actions$ = new ReplaySubject(); - - jest.clearAllMocks(); - - await TestBed.configureTestingModule({ - declarations: [EditTechRecordButtonComponent], - providers: [ - { provide: RouterService, useValue: mockRouterService }, - GlobalErrorService, - provideMockActions(() => actions$), - provideMockStore({ initialState: initialAppState }), - { provide: APP_BASE_HREF, useValue: '/' }, - { provide: TechnicalRecordService, useValue: mockTechRecordService }, - ], - imports: [DynamicFormsModule, HttpClientTestingModule, RouterTestingModule, SharedModule], - }).compileComponents(); - }); - - beforeEach(() => { - fixture = TestBed.createComponent(EditTechRecordButtonComponent); - router = TestBed.inject(Router); - store = TestBed.inject(MockStore); - component = fixture.componentInstance; - - fixture.detectChanges(); - - jest.spyOn(window, 'confirm'); - }); - - describe('component', () => { - it('should create', () => { - expect(component).toBeTruthy(); - }); - }); - - describe('when viewing a tech record', () => { - afterAll(() => { - mockTechRecordService.techRecord$ = of({ - systemNumber: 'foo', - createdTimestamp: 'bar', - vin: 'testVin', - techRecord_statusCode: StatusCodes.CURRENT, - } as unknown as V3TechRecordModel); - }); - it.each([ - [ - 'should be viewable', - true, - { - systemNumber: 'foo', - createdTimestamp: 'bar', - vin: 'testVin', - techRecord_statusCode: StatusCodes.PROVISIONAL, - } as V3TechRecordModel, - ], - [ - 'should be viewable', - true, - { - systemNumber: 'foo', - createdTimestamp: 'bar', - vin: 'testVin', - techRecord_statusCode: StatusCodes.CURRENT, - } as V3TechRecordModel, - ], - [ - 'should not be viewable', - false, - { - systemNumber: 'foo', - createdTimestamp: 'bar', - vin: 'testVin', - techRecord_statusCode: StatusCodes.ARCHIVED, - } as V3TechRecordModel, - ], - ])('edit button %s for %s record', (isViewable: string, expected: boolean, record: V3TechRecordModel) => { - mockTechRecordService.techRecord$ = of(record); - fixture.detectChanges(); - - const button = fixture.debugElement.query(By.css('#edit')); - expect(Boolean(button)).toEqual(expected); - }); - }); - - describe('when user clicks edit button', () => { - it('component should navigate away for current amendments', () => { - mockTechRecordService.techRecord$ = of({ - systemNumber: 'foo', - createdTimestamp: 'bar', - vin: 'testVin', - techRecord_statusCode: StatusCodes.PROVISIONAL, - } as V3TechRecordModel); - const navigateSpy = jest.spyOn(router, 'navigate'); - jest.spyOn(window, 'scrollTo').mockImplementation(() => {}); - - fixture.detectChanges(); - fixture.debugElement.query(By.css('button#edit')).nativeElement.click(); - - expect(navigateSpy).toHaveBeenCalledWith(['notifiable-alteration-needed'], { relativeTo: expect.anything() }); - }); - it('component should navigate away for notifiable alterations', () => { - mockTechRecordService.techRecord$ = of({ - systemNumber: 'foo', - createdTimestamp: 'bar', - vin: 'testVin', - techRecord_statusCode: StatusCodes.CURRENT, - } as V3TechRecordModel); - const navigateSpy = jest.spyOn(router, 'navigate'); - - fixture.detectChanges(); - fixture.debugElement.query(By.css('button#edit')).nativeElement.click(); - - expect(navigateSpy).toHaveBeenCalledWith(['amend-reason'], { relativeTo: expect.anything() }); - }); - }); - - describe('when amending a provisional tech record', () => { - beforeEach(() => { - component.isEditing = true; - }); - describe('and the user submits their changes', () => { - it('component should emit event', fakeAsync(() => { - const sumbitChangeSpy = jest.spyOn(component.submitChange, 'emit'); - - fixture.detectChanges(); - fixture.debugElement.query(By.css('button#submit')).nativeElement.click(); - discardPeriodicTasks(); - - expect(sumbitChangeSpy).toHaveBeenCalledTimes(1); - })); - }); - }); - - describe('when amending a current tech record', () => { - beforeEach(() => { - updateMockTechnicalRecord(StatusCodes.CURRENT); - component.isEditing = true; - }); - describe('and the user submits their changes', () => { - it('component should emit event', fakeAsync(() => { - const sumbitChangeSpy = jest.spyOn(component.submitChange, 'emit'); - - fixture.detectChanges(); - fixture.debugElement.query(By.css('button#submit')).nativeElement.click(); - discardPeriodicTasks(); - - expect(sumbitChangeSpy).toHaveBeenCalledTimes(1); - })); - }); - - describe('and the user cancels their changes', () => { - describe('and the form is dirty', () => { - beforeEach(() => { - component.isDirty = true; - jest.resetAllMocks(); - }); - - it('should prompt user if they wish to cancel', () => { - component.isEditing = true; - jest.spyOn(window, 'confirm').mockImplementation(() => true); - - fixture.detectChanges(); - - fixture.debugElement.query(By.css('button#cancel')).nativeElement.click(); - - expect(window.confirm).toHaveBeenCalledWith('Your changes will not be saved. Are you sure?'); - }); - - describe('and the user cancels cancelling an amendment', () => { - it('should keep user in edit view', fakeAsync(() => { - component.isEditing = true; - jest.spyOn(window, 'confirm').mockImplementation(() => false); - const dispatchSpy = jest.spyOn(store, 'dispatch'); - const cancelSpy = jest.spyOn(component, 'cancel'); - const toggleEditModeSpy = jest.spyOn(component, 'toggleEditMode'); - const navigateSpy = jest.spyOn(router, 'navigate'); - - fixture.detectChanges(); - fixture.debugElement.query(By.css('button#cancel')).nativeElement.click(); - - discardPeriodicTasks(); - - expect(cancelSpy).toHaveBeenCalled(); - expect(toggleEditModeSpy).not.toHaveBeenCalled(); - expect(component.isEditingChange).toBeTruthy(); - expect(window.confirm).toHaveBeenCalledTimes(1); - expect(window.confirm).toHaveBeenCalledWith('Your changes will not be saved. Are you sure?'); - expect(navigateSpy).not.toHaveBeenCalled(); - expect(dispatchSpy).not.toHaveBeenCalled(); - expect(navigateSpy).not.toHaveBeenCalled(); - })); - }); - - describe('and the user confirms cancelling the amendment', () => { - it('should return user back to non-edit view', fakeAsync(() => { - component.isEditing = true; - jest.spyOn(window, 'confirm').mockImplementation(() => true); - const dispatchSpy = jest.spyOn(store, 'dispatch'); - const cancelSpy = jest.spyOn(component, 'cancel'); - const toggleEditModeSpy = jest.spyOn(component, 'toggleEditMode'); - const navigateSpy = jest.spyOn(router, 'navigate'); - - fixture.detectChanges(); - fixture.debugElement.query(By.css('button#cancel')).nativeElement.click(); - - discardPeriodicTasks(); - - expect(navigateSpy).toHaveBeenCalled(); - expect(cancelSpy).toHaveBeenCalled(); - expect(toggleEditModeSpy).toHaveBeenCalled(); - expect(component.isEditing).toBeFalsy(); - expect(window.confirm).toHaveBeenCalledTimes(1); - expect(window.confirm).toHaveBeenCalledWith('Your changes will not be saved. Are you sure?'); - expect(dispatchSpy).toHaveBeenNthCalledWith(1, clearError()); - expect(dispatchSpy).toHaveBeenNthCalledWith(2, updateEditingTechRecordCancel()); - })); - }); - }); - - describe('and the form is not dirty', () => { - beforeEach(() => { - component.isDirty = false; - }); - - it('should not prompt user if they wish to cancel', fakeAsync(() => { - component.isEditing = true; - jest.spyOn(window, 'confirm'); - fixture.detectChanges(); - - fixture.debugElement.query(By.css('#cancel')).nativeElement.click(); - discardPeriodicTasks(); - expect(window.confirm).not.toHaveBeenCalled(); - })); - - it('should return user to non-edit view', fakeAsync(() => { - component.isEditing = true; - jest.spyOn(window, 'confirm'); - const cancelSpy = jest.spyOn(component, 'cancel'); - const toggleSpy = jest.spyOn(component, 'toggleEditMode'); - const dispatchSpy = jest.spyOn(store, 'dispatch'); - - fixture.detectChanges(); - - fixture.debugElement.query(By.css('button#cancel')).nativeElement.click(); - - discardPeriodicTasks(); - - expect(cancelSpy).toHaveBeenCalled(); - expect(toggleSpy).toHaveBeenCalled(); - expect(component.isEditing).toBeFalsy(); - expect(dispatchSpy).toHaveBeenNthCalledWith(1, clearError()); - expect(dispatchSpy).toHaveBeenNthCalledWith(2, updateEditingTechRecordCancel()); - })); - }); - }); - }); + beforeEach(async () => { + actions$ = new ReplaySubject(); + + jest.clearAllMocks(); + + await TestBed.configureTestingModule({ + declarations: [EditTechRecordButtonComponent], + providers: [ + { provide: RouterService, useValue: mockRouterService }, + GlobalErrorService, + provideMockActions(() => actions$), + provideMockStore({ initialState: initialAppState }), + { provide: APP_BASE_HREF, useValue: '/' }, + { provide: TechnicalRecordService, useValue: mockTechRecordService }, + ], + imports: [DynamicFormsModule, HttpClientTestingModule, RouterTestingModule, SharedModule], + }).compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(EditTechRecordButtonComponent); + router = TestBed.inject(Router); + store = TestBed.inject(MockStore); + component = fixture.componentInstance; + + fixture.detectChanges(); + + jest.spyOn(window, 'confirm'); + }); + + describe('component', () => { + it('should create', () => { + expect(component).toBeTruthy(); + }); + }); + + describe('when viewing a tech record', () => { + afterAll(() => { + mockTechRecordService.techRecord$ = of({ + systemNumber: 'foo', + createdTimestamp: 'bar', + vin: 'testVin', + techRecord_statusCode: StatusCodes.CURRENT, + } as unknown as V3TechRecordModel); + }); + it.each([ + [ + 'should be viewable', + true, + { + systemNumber: 'foo', + createdTimestamp: 'bar', + vin: 'testVin', + techRecord_statusCode: StatusCodes.PROVISIONAL, + } as V3TechRecordModel, + ], + [ + 'should be viewable', + true, + { + systemNumber: 'foo', + createdTimestamp: 'bar', + vin: 'testVin', + techRecord_statusCode: StatusCodes.CURRENT, + } as V3TechRecordModel, + ], + [ + 'should not be viewable', + false, + { + systemNumber: 'foo', + createdTimestamp: 'bar', + vin: 'testVin', + techRecord_statusCode: StatusCodes.ARCHIVED, + } as V3TechRecordModel, + ], + ])('edit button %s for %s record', (isViewable: string, expected: boolean, record: V3TechRecordModel) => { + mockTechRecordService.techRecord$ = of(record); + fixture.detectChanges(); + + const button = fixture.debugElement.query(By.css('#edit')); + expect(Boolean(button)).toEqual(expected); + }); + }); + + describe('when user clicks edit button', () => { + it('component should navigate away for current amendments', () => { + mockTechRecordService.techRecord$ = of({ + systemNumber: 'foo', + createdTimestamp: 'bar', + vin: 'testVin', + techRecord_statusCode: StatusCodes.PROVISIONAL, + } as V3TechRecordModel); + const navigateSpy = jest.spyOn(router, 'navigate'); + jest.spyOn(window, 'scrollTo').mockImplementation(() => {}); + + fixture.detectChanges(); + fixture.debugElement.query(By.css('button#edit')).nativeElement.click(); + + expect(navigateSpy).toHaveBeenCalledWith(['notifiable-alteration-needed'], { relativeTo: expect.anything() }); + }); + it('component should navigate away for notifiable alterations', () => { + mockTechRecordService.techRecord$ = of({ + systemNumber: 'foo', + createdTimestamp: 'bar', + vin: 'testVin', + techRecord_statusCode: StatusCodes.CURRENT, + } as V3TechRecordModel); + const navigateSpy = jest.spyOn(router, 'navigate'); + + fixture.detectChanges(); + fixture.debugElement.query(By.css('button#edit')).nativeElement.click(); + + expect(navigateSpy).toHaveBeenCalledWith(['amend-reason'], { relativeTo: expect.anything() }); + }); + }); + + describe('when amending a provisional tech record', () => { + beforeEach(() => { + component.isEditing = true; + }); + describe('and the user submits their changes', () => { + it('component should emit event', fakeAsync(() => { + const sumbitChangeSpy = jest.spyOn(component.submitChange, 'emit'); + + fixture.detectChanges(); + fixture.debugElement.query(By.css('button#submit')).nativeElement.click(); + discardPeriodicTasks(); + + expect(sumbitChangeSpy).toHaveBeenCalledTimes(1); + })); + }); + }); + + describe('when amending a current tech record', () => { + beforeEach(() => { + updateMockTechnicalRecord(StatusCodes.CURRENT); + component.isEditing = true; + }); + describe('and the user submits their changes', () => { + it('component should emit event', fakeAsync(() => { + const sumbitChangeSpy = jest.spyOn(component.submitChange, 'emit'); + + fixture.detectChanges(); + fixture.debugElement.query(By.css('button#submit')).nativeElement.click(); + discardPeriodicTasks(); + + expect(sumbitChangeSpy).toHaveBeenCalledTimes(1); + })); + }); + + describe('and the user cancels their changes', () => { + describe('and the form is dirty', () => { + beforeEach(() => { + component.isDirty = true; + jest.resetAllMocks(); + }); + + it('should prompt user if they wish to cancel', () => { + component.isEditing = true; + jest.spyOn(window, 'confirm').mockImplementation(() => true); + + fixture.detectChanges(); + + fixture.debugElement.query(By.css('button#cancel')).nativeElement.click(); + + expect(window.confirm).toHaveBeenCalledWith('Your changes will not be saved. Are you sure?'); + }); + + describe('and the user cancels cancelling an amendment', () => { + it('should keep user in edit view', fakeAsync(() => { + component.isEditing = true; + jest.spyOn(window, 'confirm').mockImplementation(() => false); + const dispatchSpy = jest.spyOn(store, 'dispatch'); + const cancelSpy = jest.spyOn(component, 'cancel'); + const toggleEditModeSpy = jest.spyOn(component, 'toggleEditMode'); + const navigateSpy = jest.spyOn(router, 'navigate'); + + fixture.detectChanges(); + fixture.debugElement.query(By.css('button#cancel')).nativeElement.click(); + + discardPeriodicTasks(); + + expect(cancelSpy).toHaveBeenCalled(); + expect(toggleEditModeSpy).not.toHaveBeenCalled(); + expect(component.isEditingChange).toBeTruthy(); + expect(window.confirm).toHaveBeenCalledTimes(1); + expect(window.confirm).toHaveBeenCalledWith('Your changes will not be saved. Are you sure?'); + expect(navigateSpy).not.toHaveBeenCalled(); + expect(dispatchSpy).not.toHaveBeenCalled(); + expect(navigateSpy).not.toHaveBeenCalled(); + })); + }); + + describe('and the user confirms cancelling the amendment', () => { + it('should return user back to non-edit view', fakeAsync(() => { + component.isEditing = true; + jest.spyOn(window, 'confirm').mockImplementation(() => true); + const dispatchSpy = jest.spyOn(store, 'dispatch'); + const cancelSpy = jest.spyOn(component, 'cancel'); + const toggleEditModeSpy = jest.spyOn(component, 'toggleEditMode'); + const navigateSpy = jest.spyOn(router, 'navigate'); + + fixture.detectChanges(); + fixture.debugElement.query(By.css('button#cancel')).nativeElement.click(); + + discardPeriodicTasks(); + + expect(navigateSpy).toHaveBeenCalled(); + expect(cancelSpy).toHaveBeenCalled(); + expect(toggleEditModeSpy).toHaveBeenCalled(); + expect(component.isEditing).toBeFalsy(); + expect(window.confirm).toHaveBeenCalledTimes(1); + expect(window.confirm).toHaveBeenCalledWith('Your changes will not be saved. Are you sure?'); + expect(dispatchSpy).toHaveBeenNthCalledWith(1, clearError()); + expect(dispatchSpy).toHaveBeenNthCalledWith(2, updateEditingTechRecordCancel()); + })); + }); + }); + + describe('and the form is not dirty', () => { + beforeEach(() => { + component.isDirty = false; + }); + + it('should not prompt user if they wish to cancel', fakeAsync(() => { + component.isEditing = true; + jest.spyOn(window, 'confirm'); + fixture.detectChanges(); + + fixture.debugElement.query(By.css('#cancel')).nativeElement.click(); + discardPeriodicTasks(); + expect(window.confirm).not.toHaveBeenCalled(); + })); + + it('should return user to non-edit view', fakeAsync(() => { + component.isEditing = true; + jest.spyOn(window, 'confirm'); + const cancelSpy = jest.spyOn(component, 'cancel'); + const toggleSpy = jest.spyOn(component, 'toggleEditMode'); + const dispatchSpy = jest.spyOn(store, 'dispatch'); + + fixture.detectChanges(); + + fixture.debugElement.query(By.css('button#cancel')).nativeElement.click(); + + discardPeriodicTasks(); + + expect(cancelSpy).toHaveBeenCalled(); + expect(toggleSpy).toHaveBeenCalled(); + expect(component.isEditing).toBeFalsy(); + expect(dispatchSpy).toHaveBeenNthCalledWith(1, clearError()); + expect(dispatchSpy).toHaveBeenNthCalledWith(2, updateEditingTechRecordCancel()); + })); + }); + }); + }); }); diff --git a/src/app/features/tech-record/components/edit-tech-record-button/edit-tech-record-button.component.ts b/src/app/features/tech-record/components/edit-tech-record-button/edit-tech-record-button.component.ts index ece95dddf0..db7ff99785 100644 --- a/src/app/features/tech-record/components/edit-tech-record-button/edit-tech-record-button.component.ts +++ b/src/app/features/tech-record/components/edit-tech-record-button/edit-tech-record-button.component.ts @@ -1,7 +1,5 @@ import { ViewportScroller } from '@angular/common'; -import { - Component, EventEmitter, Input, OnDestroy, Output, -} from '@angular/core'; +import { Component, EventEmitter, Input, OnDestroy, Output } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; import { GlobalErrorService } from '@core/components/global-error/global-error.service'; import { StatusCodes } from '@models/vehicle-tech-record.model'; @@ -9,83 +7,85 @@ import { Store } from '@ngrx/store'; import { RouterService } from '@services/router/router.service'; import { TechnicalRecordService } from '@services/technical-record/technical-record.service'; import { clearAllSectionStates, clearScrollPosition, updateEditingTechRecordCancel } from '@store/technical-records'; -import { - Observable, Subject, distinctUntilChanged, map, takeUntil, -} from 'rxjs'; +import { Observable, Subject, distinctUntilChanged, map, takeUntil } from 'rxjs'; @Component({ - selector: 'app-edit-tech-record-button', - templateUrl: './edit-tech-record-button.component.html', + selector: 'app-edit-tech-record-button', + templateUrl: './edit-tech-record-button.component.html', }) export class EditTechRecordButtonComponent implements OnDestroy { - @Input() isEditing = false; - @Input() isDirty = false; - @Input() customId = ''; + @Input() isEditing = false; + @Input() isDirty = false; + @Input() customId = ''; - @Output() isEditingChange = new EventEmitter(); - @Output() submitChange = new EventEmitter(); - destroy$ = new Subject(); + @Output() isEditingChange = new EventEmitter(); + @Output() submitChange = new EventEmitter(); + destroy$ = new Subject(); - constructor( - private errorService: GlobalErrorService, - private route: ActivatedRoute, - private router: Router, - private store: Store, - private technicalRecordService: TechnicalRecordService, - private viewportScroller: ViewportScroller, - private routerService: RouterService, - ) {} + constructor( + private errorService: GlobalErrorService, + private route: ActivatedRoute, + private router: Router, + private store: Store, + private technicalRecordService: TechnicalRecordService, + private viewportScroller: ViewportScroller, + private routerService: RouterService + ) {} - ngOnDestroy(): void { - this.destroy$.next(true); - this.destroy$.complete(); - } + ngOnDestroy(): void { + this.destroy$.next(true); + this.destroy$.complete(); + } - get isArchived$(): Observable { - return this.technicalRecordService.techRecord$.pipe( - map( - (techRecord) => !(techRecord?.techRecord_statusCode === StatusCodes.CURRENT || techRecord?.techRecord_statusCode === StatusCodes.PROVISIONAL), - ), - ); - } + get isArchived$(): Observable { + return this.technicalRecordService.techRecord$.pipe( + map( + (techRecord) => + !( + techRecord?.techRecord_statusCode === StatusCodes.CURRENT || + techRecord?.techRecord_statusCode === StatusCodes.PROVISIONAL + ) + ) + ); + } - checkIfEditableReasonRequired() { - this.technicalRecordService.techRecord$ - .pipe( - map((techRecord) => techRecord?.techRecord_statusCode), - takeUntil(this.destroy$), - distinctUntilChanged(), - ) - .subscribe((statusCode) => { - if (statusCode !== StatusCodes.PROVISIONAL) { - void this.router.navigate(['amend-reason'], { relativeTo: this.route }); - } else { - void this.router.navigate(['notifiable-alteration-needed'], { relativeTo: this.route }); - } - }); - this.technicalRecordService.clearReasonForCreation(); - } + checkIfEditableReasonRequired() { + this.technicalRecordService.techRecord$ + .pipe( + map((techRecord) => techRecord?.techRecord_statusCode), + takeUntil(this.destroy$), + distinctUntilChanged() + ) + .subscribe((statusCode) => { + if (statusCode !== StatusCodes.PROVISIONAL) { + void this.router.navigate(['amend-reason'], { relativeTo: this.route }); + } else { + void this.router.navigate(['notifiable-alteration-needed'], { relativeTo: this.route }); + } + }); + this.technicalRecordService.clearReasonForCreation(); + } - toggleEditMode() { - this.isEditing = !this.isEditing; - this.isEditingChange.emit(this.isEditing); - } + toggleEditMode() { + this.isEditing = !this.isEditing; + this.isEditingChange.emit(this.isEditing); + } - cancel() { - // eslint-disable-next-line no-restricted-globals, no-alert - if (!this.isDirty || confirm('Your changes will not be saved. Are you sure?')) { - this.toggleEditMode(); - this.errorService.clearErrors(); - this.store.dispatch(updateEditingTechRecordCancel()); - this.store.dispatch(clearAllSectionStates()); - this.store.dispatch(clearScrollPosition()); + cancel() { + // eslint-disable-next-line no-restricted-globals, no-alert + if (!this.isDirty || confirm('Your changes will not be saved. Are you sure?')) { + this.toggleEditMode(); + this.errorService.clearErrors(); + this.store.dispatch(updateEditingTechRecordCancel()); + this.store.dispatch(clearAllSectionStates()); + this.store.dispatch(clearScrollPosition()); - void this.router.navigate(['../'], { relativeTo: this.route }); - } - } + void this.router.navigate(['../'], { relativeTo: this.route }); + } + } - submit() { - this.submitChange.emit(); - this.viewportScroller.scrollToPosition([0, 0]); - } + submit() { + this.submitChange.emit(); + this.viewportScroller.scrollToPosition([0, 0]); + } } diff --git a/src/app/features/tech-record/components/tech-record-amend-reason/tech-record-amend-reason.component.spec.ts b/src/app/features/tech-record/components/tech-record-amend-reason/tech-record-amend-reason.component.spec.ts index 7f15c9beec..6e219d412d 100644 --- a/src/app/features/tech-record/components/tech-record-amend-reason/tech-record-amend-reason.component.spec.ts +++ b/src/app/features/tech-record/components/tech-record-amend-reason/tech-record-amend-reason.component.spec.ts @@ -9,98 +9,100 @@ import { StoreModule } from '@ngrx/store'; import { TechRecordAmendReasonComponent } from './tech-record-amend-reason.component'; describe('TechRecordAmendReasonComponent', () => { - let component: TechRecordAmendReasonComponent; - let fixture: ComponentFixture; - let route: ActivatedRoute; - let router: Router; + let component: TechRecordAmendReasonComponent; + let fixture: ComponentFixture; + let route: ActivatedRoute; + let router: Router; - beforeEach(async () => { - await TestBed.configureTestingModule({ - declarations: [TechRecordAmendReasonComponent], - imports: [ - RouterTestingModule.withRoutes([{ path: 'test-reason', component: jest.fn() }]), - DynamicFormsModule, - ReactiveFormsModule, - StoreModule.forRoot({}), - ], - providers: [GlobalErrorService], - }).compileComponents(); + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [TechRecordAmendReasonComponent], + imports: [ + RouterTestingModule.withRoutes([{ path: 'test-reason', component: jest.fn() }]), + DynamicFormsModule, + ReactiveFormsModule, + StoreModule.forRoot({}), + ], + providers: [GlobalErrorService], + }).compileComponents(); - route = TestBed.inject(ActivatedRoute); - router = TestBed.inject(Router); - }); + route = TestBed.inject(ActivatedRoute); + router = TestBed.inject(Router); + }); - beforeEach(() => { - fixture = TestBed.createComponent(TechRecordAmendReasonComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); + beforeEach(() => { + fixture = TestBed.createComponent(TechRecordAmendReasonComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); - describe('handleSubmit', () => { - let errorsService: GlobalErrorService; + describe('handleSubmit', () => { + let errorsService: GlobalErrorService; - beforeEach(() => { - errorsService = TestBed.inject(GlobalErrorService); - }); + beforeEach(() => { + errorsService = TestBed.inject(GlobalErrorService); + }); - it('should call handleSubmit', () => { - const handleSubmitSpy = jest.spyOn(component, 'handleSubmit').mockImplementation(); + it('should call handleSubmit', () => { + const handleSubmitSpy = jest.spyOn(component, 'handleSubmit').mockImplementation(); - fixture.debugElement.query(By.css('#submit')).nativeElement.click(); + fixture.debugElement.query(By.css('#submit')).nativeElement.click(); - expect(handleSubmitSpy).toHaveBeenCalledTimes(1); - }); + expect(handleSubmitSpy).toHaveBeenCalledTimes(1); + }); - it('should clear errors when the form is valid', () => { - component.form.get('reason')?.setValue('test-reason'); + it('should clear errors when the form is valid', () => { + component.form.get('reason')?.setValue('test-reason'); - const clearErrorsSpy = jest.spyOn(errorsService, 'clearErrors').mockImplementation(); + const clearErrorsSpy = jest.spyOn(errorsService, 'clearErrors').mockImplementation(); - fixture.debugElement.query(By.css('#submit')).nativeElement.click(); + fixture.debugElement.query(By.css('#submit')).nativeElement.click(); - expect(clearErrorsSpy).toHaveBeenCalledTimes(1); - }); + expect(clearErrorsSpy).toHaveBeenCalledTimes(1); + }); - it('should set errors when the form is invalid', () => { - const setErrorsSpy = jest.spyOn(errorsService, 'setErrors').mockImplementation(); + it('should set errors when the form is invalid', () => { + const setErrorsSpy = jest.spyOn(errorsService, 'setErrors').mockImplementation(); - fixture.debugElement.query(By.css('#submit')).nativeElement.click(); + fixture.debugElement.query(By.css('#submit')).nativeElement.click(); - expect(setErrorsSpy).toHaveBeenCalledTimes(1); - expect(setErrorsSpy).toHaveBeenCalledWith([{ anchorLink: 'reasonForAmend', error: 'Reason for amending is required' }]); - }); + expect(setErrorsSpy).toHaveBeenCalledTimes(1); + expect(setErrorsSpy).toHaveBeenCalledWith([ + { anchorLink: 'reasonForAmend', error: 'Reason for amending is required' }, + ]); + }); - it('should navigate when the form is valid and a reason is provided', () => { - component.form.get('reason')?.setValue('test-reason'); + it('should navigate when the form is valid and a reason is provided', () => { + component.form.get('reason')?.setValue('test-reason'); - const navigateSpy = jest.spyOn(router, 'navigate').mockImplementation(); + const navigateSpy = jest.spyOn(router, 'navigate').mockImplementation(); - fixture.debugElement.query(By.css('#submit')).nativeElement.click(); + fixture.debugElement.query(By.css('#submit')).nativeElement.click(); - expect(navigateSpy).toHaveBeenCalledTimes(1); - expect(navigateSpy).toHaveBeenCalledWith(['../test-reason'], { relativeTo: route }); - }); + expect(navigateSpy).toHaveBeenCalledTimes(1); + expect(navigateSpy).toHaveBeenCalledWith(['../test-reason'], { relativeTo: route }); + }); - it('should not navigate when the form is invalid', () => { - const navigateSpy = jest.spyOn(router, 'navigate').mockImplementation(); + it('should not navigate when the form is invalid', () => { + const navigateSpy = jest.spyOn(router, 'navigate').mockImplementation(); - fixture.debugElement.query(By.css('#submit')).nativeElement.click(); + fixture.debugElement.query(By.css('#submit')).nativeElement.click(); - expect(navigateSpy).not.toHaveBeenCalled(); - }); + expect(navigateSpy).not.toHaveBeenCalled(); + }); - it('should not navigate when a reason is not provided', () => { - component.form.removeControl('reason'); + it('should not navigate when a reason is not provided', () => { + component.form.removeControl('reason'); - const navigateSpy = jest.spyOn(router, 'navigate').mockImplementation(); + const navigateSpy = jest.spyOn(router, 'navigate').mockImplementation(); - fixture.debugElement.query(By.css('#submit')).nativeElement.click(); + fixture.debugElement.query(By.css('#submit')).nativeElement.click(); - expect(navigateSpy).not.toHaveBeenCalled(); - }); - }); + expect(navigateSpy).not.toHaveBeenCalled(); + }); + }); }); diff --git a/src/app/features/tech-record/components/tech-record-amend-reason/tech-record-amend-reason.component.ts b/src/app/features/tech-record/components/tech-record-amend-reason/tech-record-amend-reason.component.ts index 0dae7ac821..4e0c759b60 100644 --- a/src/app/features/tech-record/components/tech-record-amend-reason/tech-record-amend-reason.component.ts +++ b/src/app/features/tech-record/components/tech-record-amend-reason/tech-record-amend-reason.component.ts @@ -3,55 +3,65 @@ import { Validators } from '@angular/forms'; import { ActivatedRoute, Router } from '@angular/router'; import { GlobalError } from '@core/components/global-error/global-error.interface'; import { GlobalErrorService } from '@core/components/global-error/global-error.service'; -import { - CustomFormControl, CustomFormGroup, FormNodeOption, FormNodeTypes, -} from '@forms/services/dynamic-form.types'; +import { CustomFormControl, CustomFormGroup, FormNodeOption, FormNodeTypes } from '@forms/services/dynamic-form.types'; import { ReasonForEditing } from '@models/vehicle-tech-record.model'; @Component({ - selector: 'app-tech-amend-reason', - templateUrl: './tech-record-amend-reason.component.html', - changeDetection: ChangeDetectionStrategy.OnPush, + selector: 'app-tech-amend-reason', + templateUrl: './tech-record-amend-reason.component.html', + changeDetection: ChangeDetectionStrategy.OnPush, }) export class TechRecordAmendReasonComponent { - reasons: Array> = [ - { label: 'Correcting an error', value: ReasonForEditing.CORRECTING_AN_ERROR, hint: 'Amend the current technical record' }, - { - label: 'Notifiable alteration needed', - value: ReasonForEditing.NOTIFIABLE_ALTERATION_NEEDED, - hint: 'Create a new provisional technical record', - }, - ]; - - form: CustomFormGroup; - - constructor(private errorService: GlobalErrorService, private route: ActivatedRoute, private router: Router) { - this.errorService.clearErrors(); - - this.form = new CustomFormGroup( - { name: 'reasonForAmend', type: FormNodeTypes.GROUP }, - { reason: new CustomFormControl({ name: 'reason', type: FormNodeTypes.CONTROL }, undefined, [Validators.required]) }, - ); - } - - handleSubmit(): void { - const reason: string = this.form.get('reason')?.value; - const errors: GlobalError[] = [ - { - error: 'Reason for amending is required', - anchorLink: 'reasonForAmend', - }, - ]; - - if (this.form.valid) { - this.errorService.clearErrors(); - if (reason) { - void this.router.navigate([`../${reason}`], { relativeTo: this.route }); - } - - return; - } - - this.errorService.setErrors(errors); - } + reasons: Array> = [ + { + label: 'Correcting an error', + value: ReasonForEditing.CORRECTING_AN_ERROR, + hint: 'Amend the current technical record', + }, + { + label: 'Notifiable alteration needed', + value: ReasonForEditing.NOTIFIABLE_ALTERATION_NEEDED, + hint: 'Create a new provisional technical record', + }, + ]; + + form: CustomFormGroup; + + constructor( + private errorService: GlobalErrorService, + private route: ActivatedRoute, + private router: Router + ) { + this.errorService.clearErrors(); + + this.form = new CustomFormGroup( + { name: 'reasonForAmend', type: FormNodeTypes.GROUP }, + { + reason: new CustomFormControl({ name: 'reason', type: FormNodeTypes.CONTROL }, undefined, [ + Validators.required, + ]), + } + ); + } + + handleSubmit(): void { + const reason: string = this.form.get('reason')?.value; + const errors: GlobalError[] = [ + { + error: 'Reason for amending is required', + anchorLink: 'reasonForAmend', + }, + ]; + + if (this.form.valid) { + this.errorService.clearErrors(); + if (reason) { + void this.router.navigate([`../${reason}`], { relativeTo: this.route }); + } + + return; + } + + this.errorService.setErrors(errors); + } } diff --git a/src/app/features/tech-record/components/tech-record-amend-vin/tech-record-amend-vin.component.spec.ts b/src/app/features/tech-record/components/tech-record-amend-vin/tech-record-amend-vin.component.spec.ts index 8328405d89..c07c9b74bc 100644 --- a/src/app/features/tech-record/components/tech-record-amend-vin/tech-record-amend-vin.component.spec.ts +++ b/src/app/features/tech-record/components/tech-record-amend-vin/tech-record-amend-vin.component.spec.ts @@ -1,6 +1,4 @@ -import { - ComponentFixture, fakeAsync, TestBed, tick, -} from '@angular/core/testing'; +import { ComponentFixture, TestBed, fakeAsync, tick } from '@angular/core/testing'; import { ReactiveFormsModule } from '@angular/forms'; import { ActivatedRoute, ActivatedRouteSnapshot, Router } from '@angular/router'; import { RouterTestingModule } from '@angular/router/testing'; @@ -17,112 +15,118 @@ import { SharedModule } from '@shared/shared.module'; import { initialAppState } from '@store/index'; import { selectRouteNestedParams } from '@store/router/selectors/router.selectors'; import { amendVin, amendVinSuccess } from '@store/technical-records'; -import { of, ReplaySubject } from 'rxjs'; +import { ReplaySubject, of } from 'rxjs'; import { AmendVinComponent } from './tech-record-amend-vin.component'; const mockTechRecordService = { - editableTechRecord$: of({}), - selectedVehicleTechRecord$: of({}), - viewableTechRecord$: jest.fn(), - updateEditingTechRecord: jest.fn(), - isUnique: jest.fn(), - getVehicleTypeWithSmallTrl: jest.fn(), - validateVinForUpdate: jest.fn().mockReturnValue(of(null)), + editableTechRecord$: of({}), + selectedVehicleTechRecord$: of({}), + viewableTechRecord$: jest.fn(), + updateEditingTechRecord: jest.fn(), + isUnique: jest.fn(), + getVehicleTypeWithSmallTrl: jest.fn(), + validateVinForUpdate: jest.fn().mockReturnValue(of(null)), }; const mockDynamicFormService = { - createForm: jest.fn(), + createForm: jest.fn(), }; describe('TechRecordChangeVinComponent', () => { - const actions$ = new ReplaySubject(); - let component: AmendVinComponent; - let errorService: GlobalErrorService; - let expectedTechRecord = {} as V3TechRecordModel; - let fixture: ComponentFixture; - let route: ActivatedRoute; - let router: Router; - let store: MockStore; - - beforeEach(async () => { - await TestBed.configureTestingModule({ - declarations: [AmendVinComponent], - providers: [ - GlobalErrorService, - provideMockActions(() => actions$), - provideMockStore({ initialState: initialAppState }), - { provide: ActivatedRoute, useValue: { params: of([{ id: 1 }]), snapshot: new ActivatedRouteSnapshot() } }, - { provide: DynamicFormService, useValue: mockDynamicFormService }, - { provide: TechnicalRecordService, useValue: mockTechRecordService }, - ], - imports: [RouterTestingModule, SharedModule, ReactiveFormsModule, DynamicFormsModule], - }).compileComponents(); - }); - - beforeEach(() => { - fixture = TestBed.createComponent(AmendVinComponent); - errorService = TestBed.inject(GlobalErrorService); - route = TestBed.inject(ActivatedRoute); - router = TestBed.inject(Router); - store = TestBed.inject(MockStore); - component = fixture.componentInstance; - component.form.controls['vin'].clearAsyncValidators(); - component.form.controls['vin'].setAsyncValidators(mockTechRecordService.validateVinForUpdate.bind(this)); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); - - describe('handleSubmit', () => { - beforeEach(() => { - expectedTechRecord = { systemNumber: 'foo', createdTimestamp: 'bar', newVin: 'testVin' } as unknown as TechRecordType<'put'>; - component.techRecord = expectedTechRecord; - }); - it('should dispatch the amendVin action with the new vin', () => { - const createdTimestamp = '2022'; - const systemNumber = '123456'; - const newVin = 'myNewVin'; - store.overrideSelector(selectRouteNestedParams, { createdTimestamp, systemNumber }); - const dispatchSpy = jest.spyOn(store, 'dispatch'); - - component.form.controls['vin'].setValue('myNewVin'); - - component.handleSubmit(); - - expect(dispatchSpy).toHaveBeenCalledWith(amendVin({ newVin, systemNumber, createdTimestamp })); - }); - }); - - describe('navigateBack', () => { - it('should clear all errors', () => { - jest.spyOn(router, 'navigate').mockImplementation(); - - const clearErrorsSpy = jest.spyOn(errorService, 'clearErrors'); - - component.navigateBack(); - - expect(clearErrorsSpy).toHaveBeenCalledTimes(1); - }); - - it('should navigate back to the previous page', () => { - const navigateSpy = jest.spyOn(router, 'navigate').mockImplementation(() => Promise.resolve(true)); - - component.navigateBack(); - - expect(navigateSpy).toHaveBeenCalledWith(['..'], { relativeTo: route }); - }); - - it('should navigate away amendVinSuccess', fakeAsync(() => { - const navigateSpy = jest.spyOn(router, 'navigate'); - jest.spyOn(router, 'navigate').mockImplementation(); - - actions$.next( - amendVinSuccess({ vehicleTechRecord: { systemNumber: 'foo', createdTimestamp: 'bar', vin: 'testVin' } as TechRecordType<'get'> }), - ); - tick(); - - expect(navigateSpy).toHaveBeenCalled(); - })); - }); + const actions$ = new ReplaySubject(); + let component: AmendVinComponent; + let errorService: GlobalErrorService; + let expectedTechRecord = {} as V3TechRecordModel; + let fixture: ComponentFixture; + let route: ActivatedRoute; + let router: Router; + let store: MockStore; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [AmendVinComponent], + providers: [ + GlobalErrorService, + provideMockActions(() => actions$), + provideMockStore({ initialState: initialAppState }), + { provide: ActivatedRoute, useValue: { params: of([{ id: 1 }]), snapshot: new ActivatedRouteSnapshot() } }, + { provide: DynamicFormService, useValue: mockDynamicFormService }, + { provide: TechnicalRecordService, useValue: mockTechRecordService }, + ], + imports: [RouterTestingModule, SharedModule, ReactiveFormsModule, DynamicFormsModule], + }).compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(AmendVinComponent); + errorService = TestBed.inject(GlobalErrorService); + route = TestBed.inject(ActivatedRoute); + router = TestBed.inject(Router); + store = TestBed.inject(MockStore); + component = fixture.componentInstance; + component.form.controls['vin'].clearAsyncValidators(); + component.form.controls['vin'].setAsyncValidators(mockTechRecordService.validateVinForUpdate.bind(this)); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + describe('handleSubmit', () => { + beforeEach(() => { + expectedTechRecord = { + systemNumber: 'foo', + createdTimestamp: 'bar', + newVin: 'testVin', + } as unknown as TechRecordType<'put'>; + component.techRecord = expectedTechRecord; + }); + it('should dispatch the amendVin action with the new vin', () => { + const createdTimestamp = '2022'; + const systemNumber = '123456'; + const newVin = 'myNewVin'; + store.overrideSelector(selectRouteNestedParams, { createdTimestamp, systemNumber }); + const dispatchSpy = jest.spyOn(store, 'dispatch'); + + component.form.controls['vin'].setValue('myNewVin'); + + component.handleSubmit(); + + expect(dispatchSpy).toHaveBeenCalledWith(amendVin({ newVin, systemNumber, createdTimestamp })); + }); + }); + + describe('navigateBack', () => { + it('should clear all errors', () => { + jest.spyOn(router, 'navigate').mockImplementation(); + + const clearErrorsSpy = jest.spyOn(errorService, 'clearErrors'); + + component.navigateBack(); + + expect(clearErrorsSpy).toHaveBeenCalledTimes(1); + }); + + it('should navigate back to the previous page', () => { + const navigateSpy = jest.spyOn(router, 'navigate').mockImplementation(() => Promise.resolve(true)); + + component.navigateBack(); + + expect(navigateSpy).toHaveBeenCalledWith(['..'], { relativeTo: route }); + }); + + it('should navigate away amendVinSuccess', fakeAsync(() => { + const navigateSpy = jest.spyOn(router, 'navigate'); + jest.spyOn(router, 'navigate').mockImplementation(); + + actions$.next( + amendVinSuccess({ + vehicleTechRecord: { systemNumber: 'foo', createdTimestamp: 'bar', vin: 'testVin' } as TechRecordType<'get'>, + }) + ); + tick(); + + expect(navigateSpy).toHaveBeenCalled(); + })); + }); }); diff --git a/src/app/features/tech-record/components/tech-record-amend-vin/tech-record-amend-vin.component.ts b/src/app/features/tech-record/components/tech-record-amend-vin/tech-record-amend-vin.component.ts index d8dc24693c..62dfa64f23 100644 --- a/src/app/features/tech-record/components/tech-record-amend-vin/tech-record-amend-vin.component.ts +++ b/src/app/features/tech-record/components/tech-record-amend-vin/tech-record-amend-vin.component.ts @@ -14,139 +14,143 @@ import { RouterService } from '@services/router/router.service'; import { TechnicalRecordService } from '@services/technical-record/technical-record.service'; import { State } from '@store/index'; import { amendVin, amendVinSuccess } from '@store/technical-records'; -import { - Subject, take, takeUntil, withLatestFrom, -} from 'rxjs'; +import { Subject, take, takeUntil, withLatestFrom } from 'rxjs'; @Component({ - selector: 'app-change-amend-vin', - templateUrl: './tech-record-amend-vin.component.html', + selector: 'app-change-amend-vin', + templateUrl: './tech-record-amend-vin.component.html', }) export class AmendVinComponent implements OnDestroy, OnInit { - techRecord?: V3TechRecordModel; - form!: FormGroup; - private destroy$ = new Subject(); - - constructor( - private actions$: Actions, - private globalErrorService: GlobalErrorService, - private route: ActivatedRoute, - private router: Router, - private technicalRecordService: TechnicalRecordService, - private routerService: RouterService, - private store: Store, - ) { - this.initForm(); - this.handleAmendVinSuccess(); - } - - ngOnInit(): void { - this.subscribeToTechRecordUpdates(); - } - - ngOnDestroy(): void { - this.destroy$.next(); - this.destroy$.complete(); - } - - get width(): FormNodeWidth { - return FormNodeWidth.L; - } - - get vehicleType(): VehicleTypes | undefined { - return this.techRecord ? this.technicalRecordService.getVehicleTypeWithSmallTrl(this.techRecord) : undefined; - } - - get makeAndModel(): string | undefined { - return this.techRecord ? this.technicalRecordService.getMakeAndModel(this.techRecord) : undefined; - } - - get currentVrm(): string | undefined { - return this.techRecord?.techRecord_vehicleType !== 'trl' ? this.techRecord?.primaryVrm ?? '' : undefined; - } - - isFormValid(): boolean { - const errors: GlobalError[] = []; - DynamicFormService.validate(this.form, errors); - this.globalErrorService.setErrors(errors); - - if (this.form.value.vin === this.techRecord?.vin) { - this.globalErrorService.addError({ error: 'You must provide a new VIN', anchorLink: 'newVin' }); - return false; - } - - return this.form.valid; - } - - navigateBack(): void { - this.globalErrorService.clearErrors(); - void this.router.navigate(['..'], { relativeTo: this.route }); - } - - handleSubmit(): void { - if (this.shouldUpdateTechRecord()) { - this.updateTechRecord(); - } - } - - private initForm(): void { - this.form = new FormGroup({ - vin: new CustomFormControl( - { - name: 'input-vin', - label: 'Vin', - type: FormNodeTypes.CONTROL, - }, - '', - [ - CustomValidators.alphanumeric(), - Validators.minLength(3), - Validators.maxLength(21), - Validators.required, - CustomValidators.validateVinCharacters(), - ], - [this.technicalRecordService.validateVinForUpdate(this.techRecord?.vin)], - ), - }); - } - - private handleAmendVinSuccess(): void { - this.actions$.pipe(ofType(amendVinSuccess), takeUntil(this.destroy$)).subscribe(({ vehicleTechRecord }) => { - void this.router.navigate([`/tech-records/${vehicleTechRecord.systemNumber}/${vehicleTechRecord.createdTimestamp}`]); - }); - } - - private subscribeToTechRecordUpdates(): void { - this.technicalRecordService.techRecord$.pipe(takeUntil(this.destroy$)).subscribe((record) => { - if (record?.techRecord_statusCode === 'archived' || !record) { - this.navigateBack(); - } else { - this.techRecord = record; - } - }); - } - - private shouldUpdateTechRecord(): boolean { - return this.isFormValid() || (this.form.status === 'PENDING' && this.form.errors === null); - } - - private updateTechRecord(): void { - const record = { ...this.techRecord } as TechRecordType<'put'>; - record.vin = this.form.value.vin; - - this.technicalRecordService.updateEditingTechRecord({ - ...record, - techRecord_reasonForCreation: 'Vin changed', - }); - - this.routerService - .getRouteNestedParam$('systemNumber') - .pipe(take(1), takeUntil(this.destroy$), withLatestFrom(this.routerService.getRouteNestedParam$('createdTimestamp'))) - .subscribe(([systemNumber, createdTimestamp]) => { - if (systemNumber && createdTimestamp) { - const newVin = this.form.value.vin; - this.store.dispatch(amendVin({ newVin, systemNumber, createdTimestamp })); - } - }); - } + techRecord?: V3TechRecordModel; + form!: FormGroup; + private destroy$ = new Subject(); + + constructor( + private actions$: Actions, + private globalErrorService: GlobalErrorService, + private route: ActivatedRoute, + private router: Router, + private technicalRecordService: TechnicalRecordService, + private routerService: RouterService, + private store: Store + ) { + this.initForm(); + this.handleAmendVinSuccess(); + } + + ngOnInit(): void { + this.subscribeToTechRecordUpdates(); + } + + ngOnDestroy(): void { + this.destroy$.next(); + this.destroy$.complete(); + } + + get width(): FormNodeWidth { + return FormNodeWidth.L; + } + + get vehicleType(): VehicleTypes | undefined { + return this.techRecord ? this.technicalRecordService.getVehicleTypeWithSmallTrl(this.techRecord) : undefined; + } + + get makeAndModel(): string | undefined { + return this.techRecord ? this.technicalRecordService.getMakeAndModel(this.techRecord) : undefined; + } + + get currentVrm(): string | undefined { + return this.techRecord?.techRecord_vehicleType !== 'trl' ? this.techRecord?.primaryVrm ?? '' : undefined; + } + + isFormValid(): boolean { + const errors: GlobalError[] = []; + DynamicFormService.validate(this.form, errors); + this.globalErrorService.setErrors(errors); + + if (this.form.value.vin === this.techRecord?.vin) { + this.globalErrorService.addError({ error: 'You must provide a new VIN', anchorLink: 'newVin' }); + return false; + } + + return this.form.valid; + } + + navigateBack(): void { + this.globalErrorService.clearErrors(); + void this.router.navigate(['..'], { relativeTo: this.route }); + } + + handleSubmit(): void { + if (this.shouldUpdateTechRecord()) { + this.updateTechRecord(); + } + } + + private initForm(): void { + this.form = new FormGroup({ + vin: new CustomFormControl( + { + name: 'input-vin', + label: 'Vin', + type: FormNodeTypes.CONTROL, + }, + '', + [ + CustomValidators.alphanumeric(), + Validators.minLength(3), + Validators.maxLength(21), + Validators.required, + CustomValidators.validateVinCharacters(), + ], + [this.technicalRecordService.validateVinForUpdate(this.techRecord?.vin)] + ), + }); + } + + private handleAmendVinSuccess(): void { + this.actions$.pipe(ofType(amendVinSuccess), takeUntil(this.destroy$)).subscribe(({ vehicleTechRecord }) => { + void this.router.navigate([ + `/tech-records/${vehicleTechRecord.systemNumber}/${vehicleTechRecord.createdTimestamp}`, + ]); + }); + } + + private subscribeToTechRecordUpdates(): void { + this.technicalRecordService.techRecord$.pipe(takeUntil(this.destroy$)).subscribe((record) => { + if (record?.techRecord_statusCode === 'archived' || !record) { + this.navigateBack(); + } else { + this.techRecord = record; + } + }); + } + + private shouldUpdateTechRecord(): boolean { + return this.isFormValid() || (this.form.status === 'PENDING' && this.form.errors === null); + } + + private updateTechRecord(): void { + const record = { ...this.techRecord } as TechRecordType<'put'>; + record.vin = this.form.value.vin; + + this.technicalRecordService.updateEditingTechRecord({ + ...record, + techRecord_reasonForCreation: 'Vin changed', + }); + + this.routerService + .getRouteNestedParam$('systemNumber') + .pipe( + take(1), + takeUntil(this.destroy$), + withLatestFrom(this.routerService.getRouteNestedParam$('createdTimestamp')) + ) + .subscribe(([systemNumber, createdTimestamp]) => { + if (systemNumber && createdTimestamp) { + const newVin = this.form.value.vin; + this.store.dispatch(amendVin({ newVin, systemNumber, createdTimestamp })); + } + }); + } } diff --git a/src/app/features/tech-record/components/tech-record-amend-vrm-reason/tech-record-amend-vrm-reason.component.spec.ts b/src/app/features/tech-record/components/tech-record-amend-vrm-reason/tech-record-amend-vrm-reason.component.spec.ts index cbe521e7ba..10719b606a 100644 --- a/src/app/features/tech-record/components/tech-record-amend-vrm-reason/tech-record-amend-vrm-reason.component.spec.ts +++ b/src/app/features/tech-record/components/tech-record-amend-vrm-reason/tech-record-amend-vrm-reason.component.spec.ts @@ -1,7 +1,5 @@ import { HttpClientTestingModule } from '@angular/common/http/testing'; -import { - ComponentFixture, TestBed, -} from '@angular/core/testing'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ReactiveFormsModule } from '@angular/forms'; import { ActivatedRoute, ActivatedRouteSnapshot, Router } from '@angular/router'; import { RouterTestingModule } from '@angular/router/testing'; @@ -18,68 +16,70 @@ import { ReplaySubject, of } from 'rxjs'; import { AmendVrmReasonComponent } from './tech-record-amend-vrm-reason.component'; const mockDynamicFormService = { - createForm: jest.fn(), + createForm: jest.fn(), }; describe('TechRecordChangeVrmComponent', () => { - const actions$ = new ReplaySubject(); - let component: AmendVrmReasonComponent; - let errorService: GlobalErrorService; - let fixture: ComponentFixture; - let router: Router; + const actions$ = new ReplaySubject(); + let component: AmendVrmReasonComponent; + let errorService: GlobalErrorService; + let fixture: ComponentFixture; + let router: Router; - beforeEach(async () => { - await TestBed.configureTestingModule({ - declarations: [AmendVrmReasonComponent], - providers: [ - GlobalErrorService, - provideMockActions(() => actions$), - provideMockStore({ initialState: initialAppState }), - { provide: ActivatedRoute, useValue: { params: of([{ id: 1 }]), snapshot: new ActivatedRouteSnapshot() } }, - { provide: DynamicFormService, useValue: mockDynamicFormService }, - TechnicalRecordService, - ], - imports: [RouterTestingModule, SharedModule, ReactiveFormsModule, DynamicFormsModule, HttpClientTestingModule], - }).compileComponents(); - }); + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [AmendVrmReasonComponent], + providers: [ + GlobalErrorService, + provideMockActions(() => actions$), + provideMockStore({ initialState: initialAppState }), + { provide: ActivatedRoute, useValue: { params: of([{ id: 1 }]), snapshot: new ActivatedRouteSnapshot() } }, + { provide: DynamicFormService, useValue: mockDynamicFormService }, + TechnicalRecordService, + ], + imports: [RouterTestingModule, SharedModule, ReactiveFormsModule, DynamicFormsModule, HttpClientTestingModule], + }).compileComponents(); + }); - beforeEach(() => { - fixture = TestBed.createComponent(AmendVrmReasonComponent); - errorService = TestBed.inject(GlobalErrorService); - router = TestBed.inject(Router); - component = fixture.componentInstance; - }); + beforeEach(() => { + fixture = TestBed.createComponent(AmendVrmReasonComponent); + errorService = TestBed.inject(GlobalErrorService); + router = TestBed.inject(Router); + component = fixture.componentInstance; + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); - describe('errors', () => { - it('should add an error when the reason for amending is not selected', () => { - const addErrorSpy = jest.spyOn(errorService, 'setErrors'); + it('should create', () => { + expect(component).toBeTruthy(); + }); + describe('errors', () => { + it('should add an error when the reason for amending is not selected', () => { + const addErrorSpy = jest.spyOn(errorService, 'setErrors'); - component.submit(); + component.submit(); - expect(addErrorSpy).toHaveBeenCalledWith([{ error: 'Reason for change is required', anchorLink: 'is-cherished-transfer' }]); - }); - }); - describe('submit', () => { - it('should navigate to correct-error', () => { - fixture.ngZone?.run(() => { - const navigationSpy = jest.spyOn(router, 'navigate'); - component.form.controls['isCherishedTransfer'].setValue('correcting-error'); - component.submit(); + expect(addErrorSpy).toHaveBeenCalledWith([ + { error: 'Reason for change is required', anchorLink: 'is-cherished-transfer' }, + ]); + }); + }); + describe('submit', () => { + it('should navigate to correct-error', () => { + fixture.ngZone?.run(() => { + const navigationSpy = jest.spyOn(router, 'navigate'); + component.form.controls['isCherishedTransfer'].setValue('correcting-error'); + component.submit(); - expect(navigationSpy).toHaveBeenCalledWith(['correcting-error'], { relativeTo: expect.anything() }); - }); - }); - it('should navigate to cherished-transfer', () => { - fixture.ngZone?.run(() => { - const navigationSpy = jest.spyOn(router, 'navigate'); - component.form.controls['isCherishedTransfer'].setValue('cherished-transfer'); - component.submit(); + expect(navigationSpy).toHaveBeenCalledWith(['correcting-error'], { relativeTo: expect.anything() }); + }); + }); + it('should navigate to cherished-transfer', () => { + fixture.ngZone?.run(() => { + const navigationSpy = jest.spyOn(router, 'navigate'); + component.form.controls['isCherishedTransfer'].setValue('cherished-transfer'); + component.submit(); - expect(navigationSpy).toHaveBeenCalledWith(['cherished-transfer'], { relativeTo: expect.anything() }); - }); - }); - }); + expect(navigationSpy).toHaveBeenCalledWith(['cherished-transfer'], { relativeTo: expect.anything() }); + }); + }); + }); }); diff --git a/src/app/features/tech-record/components/tech-record-amend-vrm-reason/tech-record-amend-vrm-reason.component.ts b/src/app/features/tech-record/components/tech-record-amend-vrm-reason/tech-record-amend-vrm-reason.component.ts index e2510c0a79..1cc889cb7c 100644 --- a/src/app/features/tech-record/components/tech-record-amend-vrm-reason/tech-record-amend-vrm-reason.component.ts +++ b/src/app/features/tech-record/components/tech-record-amend-vrm-reason/tech-record-amend-vrm-reason.component.ts @@ -4,90 +4,88 @@ import { ActivatedRoute, Router } from '@angular/router'; import { GlobalError } from '@core/components/global-error/global-error.interface'; import { GlobalErrorService } from '@core/components/global-error/global-error.service'; import { DynamicFormService } from '@forms/services/dynamic-form.service'; -import { - CustomFormControl, FormNodeOption, FormNodeTypes, FormNodeWidth, -} from '@forms/services/dynamic-form.types'; +import { CustomFormControl, FormNodeOption, FormNodeTypes, FormNodeWidth } from '@forms/services/dynamic-form.types'; import { VehicleTypes, VehiclesOtherThan } from '@models/vehicle-tech-record.model'; import { TechnicalRecordService } from '@services/technical-record/technical-record.service'; import { Subject, take, takeUntil } from 'rxjs'; @Component({ - selector: 'app-amend-vrm-reason', - templateUrl: './tech-record-amend-vrm-reason.component.html', - styleUrls: ['./tech-record-amend-vrm-reason.component.scss'], + selector: 'app-amend-vrm-reason', + templateUrl: './tech-record-amend-vrm-reason.component.html', + styleUrls: ['./tech-record-amend-vrm-reason.component.scss'], }) export class AmendVrmReasonComponent implements OnDestroy, OnInit { - techRecord?: VehiclesOtherThan<'trl'>; - makeAndModel?: string; - - form = new FormGroup({ - isCherishedTransfer: new CustomFormControl( - { name: 'is-cherished-transfer', label: 'Reason for change', type: FormNodeTypes.CONTROL }, - undefined, - [Validators.required], - ), - }); - - private destroy$ = new Subject(); - - constructor( - public dfs: DynamicFormService, - private globalErrorService: GlobalErrorService, - private route: ActivatedRoute, - private router: Router, - private technicalRecordService: TechnicalRecordService, - ) {} - - ngOnInit(): void { - this.technicalRecordService.techRecord$.pipe(take(1), takeUntil(this.destroy$)).subscribe((record) => { - if (record?.techRecord_statusCode === 'archived' || !record) { - return this.navigateBack(); - } - this.techRecord = record as VehiclesOtherThan<'trl'>; - this.makeAndModel = this.technicalRecordService.getMakeAndModel(record); - }); - } - - ngOnDestroy(): void { - this.destroy$.next(); - this.destroy$.complete(); - } - - get reasons(): Array> { - return [ - { label: 'Cherished transfer', value: 'cherished-transfer', hint: 'Current VRM will be archived' }, - { label: 'Correcting an error', value: 'correcting-error', hint: 'Current VRM will not be archived' }, - ]; - } - - get width(): FormNodeWidth { - return FormNodeWidth.L; - } - - get vehicleType(): VehicleTypes | undefined { - return this.techRecord ? this.technicalRecordService.getVehicleTypeWithSmallTrl(this.techRecord) : undefined; - } - - navigateBack() { - this.globalErrorService.clearErrors(); - void this.router.navigate(['..'], { relativeTo: this.route }); - } - - submit(): void { - if (!this.isFormValid) { - return; - } - - void this.router.navigate([this.form.controls['isCherishedTransfer'].value], { relativeTo: this.route }); - } - - get isFormValid(): boolean { - const errors: GlobalError[] = []; - - DynamicFormService.validate(this.form, errors); - - this.globalErrorService.setErrors(errors); - - return this.form.valid; - } + techRecord?: VehiclesOtherThan<'trl'>; + makeAndModel?: string; + + form = new FormGroup({ + isCherishedTransfer: new CustomFormControl( + { name: 'is-cherished-transfer', label: 'Reason for change', type: FormNodeTypes.CONTROL }, + undefined, + [Validators.required] + ), + }); + + private destroy$ = new Subject(); + + constructor( + public dfs: DynamicFormService, + private globalErrorService: GlobalErrorService, + private route: ActivatedRoute, + private router: Router, + private technicalRecordService: TechnicalRecordService + ) {} + + ngOnInit(): void { + this.technicalRecordService.techRecord$.pipe(take(1), takeUntil(this.destroy$)).subscribe((record) => { + if (record?.techRecord_statusCode === 'archived' || !record) { + return this.navigateBack(); + } + this.techRecord = record as VehiclesOtherThan<'trl'>; + this.makeAndModel = this.technicalRecordService.getMakeAndModel(record); + }); + } + + ngOnDestroy(): void { + this.destroy$.next(); + this.destroy$.complete(); + } + + get reasons(): Array> { + return [ + { label: 'Cherished transfer', value: 'cherished-transfer', hint: 'Current VRM will be archived' }, + { label: 'Correcting an error', value: 'correcting-error', hint: 'Current VRM will not be archived' }, + ]; + } + + get width(): FormNodeWidth { + return FormNodeWidth.L; + } + + get vehicleType(): VehicleTypes | undefined { + return this.techRecord ? this.technicalRecordService.getVehicleTypeWithSmallTrl(this.techRecord) : undefined; + } + + navigateBack() { + this.globalErrorService.clearErrors(); + void this.router.navigate(['..'], { relativeTo: this.route }); + } + + submit(): void { + if (!this.isFormValid) { + return; + } + + void this.router.navigate([this.form.controls['isCherishedTransfer'].value], { relativeTo: this.route }); + } + + get isFormValid(): boolean { + const errors: GlobalError[] = []; + + DynamicFormService.validate(this.form, errors); + + this.globalErrorService.setErrors(errors); + + return this.form.valid; + } } diff --git a/src/app/features/tech-record/components/tech-record-amend-vrm/tech-record-amend-vrm.component.spec.ts b/src/app/features/tech-record/components/tech-record-amend-vrm/tech-record-amend-vrm.component.spec.ts index 962cf5989f..0f25609fce 100644 --- a/src/app/features/tech-record/components/tech-record-amend-vrm/tech-record-amend-vrm.component.spec.ts +++ b/src/app/features/tech-record/components/tech-record-amend-vrm/tech-record-amend-vrm.component.spec.ts @@ -1,4 +1,4 @@ -import { ComponentFixture, fakeAsync, TestBed } from '@angular/core/testing'; +import { ComponentFixture, TestBed, fakeAsync } from '@angular/core/testing'; import { ReactiveFormsModule } from '@angular/forms'; import { ActivatedRoute, ActivatedRouteSnapshot, Router } from '@angular/router'; import { RouterTestingModule } from '@angular/router/testing'; @@ -12,191 +12,211 @@ import { Action } from '@ngrx/store'; import { MockStore, provideMockStore } from '@ngrx/store/testing'; import { TechnicalRecordService } from '@services/technical-record/technical-record.service'; import { SharedModule } from '@shared/shared.module'; -import { initialAppState, State } from '@store/index'; +import { State, initialAppState } from '@store/index'; import { selectRouteData } from '@store/router/selectors/router.selectors'; import { amendVrm, amendVrmSuccess } from '@store/technical-records'; -import { of, ReplaySubject } from 'rxjs'; +import { ReplaySubject, of } from 'rxjs'; import { AmendVrmComponent } from './tech-record-amend-vrm.component'; const mockTechRecordService = { - techRecord$: of({}), - get viewableTechRecord$() { - return of({ - systemNumber: 'foo', createdTimestamp: 'bar', vin: 'testVin', primaryVrm: 'TESTVRM', - }); - }, - updateEditingTechRecord: jest.fn(), - validateVrmDoesNotExist: jest.fn(), - validateVrmForCherishedTransfer: jest.fn(), - checkVrmNotActive: jest.fn(), - getVehicleTypeWithSmallTrl: jest.fn(), + techRecord$: of({}), + get viewableTechRecord$() { + return of({ + systemNumber: 'foo', + createdTimestamp: 'bar', + vin: 'testVin', + primaryVrm: 'TESTVRM', + }); + }, + updateEditingTechRecord: jest.fn(), + validateVrmDoesNotExist: jest.fn(), + validateVrmForCherishedTransfer: jest.fn(), + checkVrmNotActive: jest.fn(), + getVehicleTypeWithSmallTrl: jest.fn(), }; const mockDynamicFormService = { - createForm: jest.fn(), + createForm: jest.fn(), }; describe('TechRecordChangeVrmComponent', () => { - const actions$ = new ReplaySubject(); - let component: AmendVrmComponent; - let errorService: GlobalErrorService; - let fixture: ComponentFixture; - let route: ActivatedRoute; - let router: Router; - let store: MockStore; - - beforeEach(async () => { - await TestBed.configureTestingModule({ - declarations: [AmendVrmComponent], - providers: [ - GlobalErrorService, - provideMockActions(() => actions$), - provideMockStore({ initialState: initialAppState }), - { provide: ActivatedRoute, useValue: { params: of([{ id: 1, reason: 'correcting-error' }]), snapshot: new ActivatedRouteSnapshot() } }, - { provide: DynamicFormService, useValue: mockDynamicFormService }, - { provide: TechnicalRecordService, useValue: mockTechRecordService }, - ], - imports: [RouterTestingModule, SharedModule, ReactiveFormsModule], - }).compileComponents(); - }); - beforeEach(() => { - fixture = TestBed.createComponent(AmendVrmComponent); - errorService = TestBed.inject(GlobalErrorService); - route = TestBed.inject(ActivatedRoute); - router = TestBed.inject(Router); - store = TestBed.inject(MockStore); - component = fixture.componentInstance; - component.cherishedTransferForm.controls['currentVrm'].clearAsyncValidators(); - component.cherishedTransferForm.controls['currentVrm'].setAsyncValidators(mockTechRecordService.validateVrmForCherishedTransfer.bind(this)); - component.cherishedTransferForm.controls['thirdMark'].clearAsyncValidators(); - component.cherishedTransferForm.controls['thirdMark'].setAsyncValidators(mockTechRecordService.validateVrmDoesNotExist.bind(this)); - component.correctingAnErrorForm.controls['newVrm'].clearAsyncValidators(); - component.correctingAnErrorForm.controls['newVrm'].setAsyncValidators(mockTechRecordService.validateVrmDoesNotExist.bind(this)); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); - - describe('navigateBack', () => { - it('should clear all errors', () => { - jest.spyOn(router, 'navigate').mockImplementation(); - - const clearErrorsSpy = jest.spyOn(errorService, 'clearErrors'); - - component.navigateBack(); - - expect(clearErrorsSpy).toHaveBeenCalledTimes(1); - }); - - it('should navigate back to the previous page', () => { - const navigateSpy = jest.spyOn(router, 'navigate').mockImplementation(() => Promise.resolve(true)); - - component.navigateBack(); - - expect(navigateSpy).toHaveBeenCalledWith(['../../'], { relativeTo: route }); - }); - - it('should navigate to a new record on amendVrmSuccess', () => { - const navigateSpy = jest.spyOn(router, 'navigate').mockImplementation(() => Promise.resolve(true)); - - store.overrideSelector(selectRouteData, { data: { isEditing: true } }); - component.ngOnInit(); - - actions$.next(amendVrmSuccess({ vehicleTechRecord: mockVehicleTechnicalRecord('psv') as TechRecordType<'get'> })); - - expect(navigateSpy).toHaveBeenCalled(); - }); - }); - - describe('handleSubmit', () => { - beforeEach(() => { - component.techRecord = mockVehicleTechnicalRecord('psv') as VehiclesOtherThan<'trl'>; - jest.resetAllMocks(); - jest.resetModules(); - }); - - it('should add an error when the vrm field is not filled out', () => { - const addErrorSpy = jest.spyOn(errorService, 'setErrors'); - - component.handleSubmit(); - - expect(addErrorSpy).toHaveBeenCalledWith([{ anchorLink: 'new-Vrm', error: 'New VRM is required' }]); - }); - - it('should not dispatch an action if isFormValid returns false', () => { - const dispatchSpy = jest.spyOn(store, 'dispatch'); - - component.handleSubmit(); - - expect(dispatchSpy).not.toHaveBeenCalledWith( - amendVrm({ - newVrm: '', - cherishedTransfer: false, - thirdMark: '', - systemNumber: 'PSV', - createdTimestamp: 'now', - }), - ); - }); - it('should dispatch the amendVrm action', fakeAsync(() => { - const dispatchSpy = jest.spyOn(store, 'dispatch'); - mockTechRecordService.validateVrmDoesNotExist.mockReturnValue(of(null)); - component.correctingAnErrorForm.controls['newVrm'].setValue('TESTVRM1'); - - component.handleSubmit(); - - expect(dispatchSpy).toHaveBeenCalledWith( - amendVrm({ - newVrm: 'TESTVRM1', cherishedTransfer: false, systemNumber: 'PSV', createdTimestamp: 'now', thirdMark: undefined, - }), - ); - })); - it('should dispatch the action with the correct information', () => { - const dispatchSpy = jest.spyOn(store, 'dispatch'); - mockTechRecordService.validateVrmDoesNotExist.mockReturnValue(of(null)); - mockTechRecordService.validateVrmForCherishedTransfer.mockReturnValue(of(null)); - component.cherishedTransferForm.controls['currentVrm'].setValue('TESTVRM1'); - component.cherishedTransferForm.controls['thirdMark'].setValue('3MARK'); - component.isCherishedTransfer = true; - - component.handleSubmit(); - - expect(dispatchSpy).toHaveBeenCalledWith( - amendVrm({ - newVrm: 'TESTVRM1', cherishedTransfer: true, systemNumber: 'PSV', createdTimestamp: 'now', thirdMark: '3MARK', - }), - ); - }); - }); - - describe('showWarning', () => { - it('should return true if the vehicle type is a psv', () => { - component.techRecord = { techRecord_vehicleType: 'psv' } as VehiclesOtherThan<'trl'>; - mockTechRecordService.getVehicleTypeWithSmallTrl.mockReturnValue('psv'); - - expect(component.showWarning).toBe(true); - }); - - it('should return true if the vehicle type is a hgv', () => { - component.techRecord = { techRecord_vehicleType: 'hgv' } as VehiclesOtherThan<'trl'>; - mockTechRecordService.getVehicleTypeWithSmallTrl.mockReturnValue('hgv'); - - expect(component.showWarning).toBe(true); - }); - - it('should return false if the vehicle type is not a psv or hgv', () => { - component.techRecord = { techRecord_vehicleType: 'lgv' } as VehiclesOtherThan<'trl'>; - mockTechRecordService.getVehicleTypeWithSmallTrl.mockReturnValue('lgv'); - - expect(component.showWarning).toBe(false); - }); - - it('should default to false if the vehicle type is not present', () => { - component.techRecord = undefined; - mockTechRecordService.getVehicleTypeWithSmallTrl.mockReturnValue(undefined); - - expect(component.showWarning).toBe(false); - }); - }); + const actions$ = new ReplaySubject(); + let component: AmendVrmComponent; + let errorService: GlobalErrorService; + let fixture: ComponentFixture; + let route: ActivatedRoute; + let router: Router; + let store: MockStore; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [AmendVrmComponent], + providers: [ + GlobalErrorService, + provideMockActions(() => actions$), + provideMockStore({ initialState: initialAppState }), + { + provide: ActivatedRoute, + useValue: { params: of([{ id: 1, reason: 'correcting-error' }]), snapshot: new ActivatedRouteSnapshot() }, + }, + { provide: DynamicFormService, useValue: mockDynamicFormService }, + { provide: TechnicalRecordService, useValue: mockTechRecordService }, + ], + imports: [RouterTestingModule, SharedModule, ReactiveFormsModule], + }).compileComponents(); + }); + beforeEach(() => { + fixture = TestBed.createComponent(AmendVrmComponent); + errorService = TestBed.inject(GlobalErrorService); + route = TestBed.inject(ActivatedRoute); + router = TestBed.inject(Router); + store = TestBed.inject(MockStore); + component = fixture.componentInstance; + component.cherishedTransferForm.controls['currentVrm'].clearAsyncValidators(); + component.cherishedTransferForm.controls['currentVrm'].setAsyncValidators( + mockTechRecordService.validateVrmForCherishedTransfer.bind(this) + ); + component.cherishedTransferForm.controls['thirdMark'].clearAsyncValidators(); + component.cherishedTransferForm.controls['thirdMark'].setAsyncValidators( + mockTechRecordService.validateVrmDoesNotExist.bind(this) + ); + component.correctingAnErrorForm.controls['newVrm'].clearAsyncValidators(); + component.correctingAnErrorForm.controls['newVrm'].setAsyncValidators( + mockTechRecordService.validateVrmDoesNotExist.bind(this) + ); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + describe('navigateBack', () => { + it('should clear all errors', () => { + jest.spyOn(router, 'navigate').mockImplementation(); + + const clearErrorsSpy = jest.spyOn(errorService, 'clearErrors'); + + component.navigateBack(); + + expect(clearErrorsSpy).toHaveBeenCalledTimes(1); + }); + + it('should navigate back to the previous page', () => { + const navigateSpy = jest.spyOn(router, 'navigate').mockImplementation(() => Promise.resolve(true)); + + component.navigateBack(); + + expect(navigateSpy).toHaveBeenCalledWith(['../../'], { relativeTo: route }); + }); + + it('should navigate to a new record on amendVrmSuccess', () => { + const navigateSpy = jest.spyOn(router, 'navigate').mockImplementation(() => Promise.resolve(true)); + + store.overrideSelector(selectRouteData, { data: { isEditing: true } }); + component.ngOnInit(); + + actions$.next(amendVrmSuccess({ vehicleTechRecord: mockVehicleTechnicalRecord('psv') as TechRecordType<'get'> })); + + expect(navigateSpy).toHaveBeenCalled(); + }); + }); + + describe('handleSubmit', () => { + beforeEach(() => { + component.techRecord = mockVehicleTechnicalRecord('psv') as VehiclesOtherThan<'trl'>; + jest.resetAllMocks(); + jest.resetModules(); + }); + + it('should add an error when the vrm field is not filled out', () => { + const addErrorSpy = jest.spyOn(errorService, 'setErrors'); + + component.handleSubmit(); + + expect(addErrorSpy).toHaveBeenCalledWith([{ anchorLink: 'new-Vrm', error: 'New VRM is required' }]); + }); + + it('should not dispatch an action if isFormValid returns false', () => { + const dispatchSpy = jest.spyOn(store, 'dispatch'); + + component.handleSubmit(); + + expect(dispatchSpy).not.toHaveBeenCalledWith( + amendVrm({ + newVrm: '', + cherishedTransfer: false, + thirdMark: '', + systemNumber: 'PSV', + createdTimestamp: 'now', + }) + ); + }); + it('should dispatch the amendVrm action', fakeAsync(() => { + const dispatchSpy = jest.spyOn(store, 'dispatch'); + mockTechRecordService.validateVrmDoesNotExist.mockReturnValue(of(null)); + component.correctingAnErrorForm.controls['newVrm'].setValue('TESTVRM1'); + + component.handleSubmit(); + + expect(dispatchSpy).toHaveBeenCalledWith( + amendVrm({ + newVrm: 'TESTVRM1', + cherishedTransfer: false, + systemNumber: 'PSV', + createdTimestamp: 'now', + thirdMark: undefined, + }) + ); + })); + it('should dispatch the action with the correct information', () => { + const dispatchSpy = jest.spyOn(store, 'dispatch'); + mockTechRecordService.validateVrmDoesNotExist.mockReturnValue(of(null)); + mockTechRecordService.validateVrmForCherishedTransfer.mockReturnValue(of(null)); + component.cherishedTransferForm.controls['currentVrm'].setValue('TESTVRM1'); + component.cherishedTransferForm.controls['thirdMark'].setValue('3MARK'); + component.isCherishedTransfer = true; + + component.handleSubmit(); + + expect(dispatchSpy).toHaveBeenCalledWith( + amendVrm({ + newVrm: 'TESTVRM1', + cherishedTransfer: true, + systemNumber: 'PSV', + createdTimestamp: 'now', + thirdMark: '3MARK', + }) + ); + }); + }); + + describe('showWarning', () => { + it('should return true if the vehicle type is a psv', () => { + component.techRecord = { techRecord_vehicleType: 'psv' } as VehiclesOtherThan<'trl'>; + mockTechRecordService.getVehicleTypeWithSmallTrl.mockReturnValue('psv'); + + expect(component.showWarning).toBe(true); + }); + + it('should return true if the vehicle type is a hgv', () => { + component.techRecord = { techRecord_vehicleType: 'hgv' } as VehiclesOtherThan<'trl'>; + mockTechRecordService.getVehicleTypeWithSmallTrl.mockReturnValue('hgv'); + + expect(component.showWarning).toBe(true); + }); + + it('should return false if the vehicle type is not a psv or hgv', () => { + component.techRecord = { techRecord_vehicleType: 'lgv' } as VehiclesOtherThan<'trl'>; + mockTechRecordService.getVehicleTypeWithSmallTrl.mockReturnValue('lgv'); + + expect(component.showWarning).toBe(false); + }); + + it('should default to false if the vehicle type is not present', () => { + component.techRecord = undefined; + mockTechRecordService.getVehicleTypeWithSmallTrl.mockReturnValue(undefined); + + expect(component.showWarning).toBe(false); + }); + }); }); diff --git a/src/app/features/tech-record/components/tech-record-amend-vrm/tech-record-amend-vrm.component.ts b/src/app/features/tech-record/components/tech-record-amend-vrm/tech-record-amend-vrm.component.ts index 90cf3ceed4..c670543151 100644 --- a/src/app/features/tech-record/components/tech-record-amend-vrm/tech-record-amend-vrm.component.ts +++ b/src/app/features/tech-record/components/tech-record-amend-vrm/tech-record-amend-vrm.component.ts @@ -1,6 +1,4 @@ -import { - Component, EventEmitter, OnDestroy, OnInit, Output, QueryList, ViewChildren, -} from '@angular/core'; +import { Component, EventEmitter, OnDestroy, OnInit, Output, QueryList, ViewChildren, inject } from '@angular/core'; import { FormGroup, Validators } from '@angular/forms'; import { ActivatedRoute, Router } from '@angular/router'; import { GlobalError } from '@core/components/global-error/global-error.interface'; @@ -19,161 +17,180 @@ import { TechnicalRecordServiceState } from '@store/technical-records/reducers/t import { Subject, take, takeUntil } from 'rxjs'; @Component({ - selector: 'app-change-amend-vrm', - templateUrl: './tech-record-amend-vrm.component.html', - styleUrls: ['./tech-record-amend-vrm.component.scss'], + selector: 'app-change-amend-vrm', + templateUrl: './tech-record-amend-vrm.component.html', + styleUrls: ['./tech-record-amend-vrm.component.scss'], }) export class AmendVrmComponent implements OnDestroy, OnInit { - techRecord?: VehiclesOtherThan<'trl'>; - makeAndModel?: string; - isCherishedTransfer = false; - systemNumber?: string; - createdTimestamp?: string; - formValidity = false; - width: FormNodeWidth = FormNodeWidth.L; - - cherishedTransferForm = new FormGroup({ - currentVrm: new CustomFormControl( - { - name: 'current-Vrm', - label: 'Current VRM', - type: FormNodeTypes.CONTROL, - }, - '', - [Validators.required, CustomValidators.alphanumeric(), CustomValidators.notZNumber, Validators.minLength(3), Validators.maxLength(9)], - this.technicalRecordService.validateVrmForCherishedTransfer(), - ), - previousVrm: new CustomFormControl({ - name: 'previous-Vrm', - label: 'Previous VRM', - type: FormNodeTypes.CONTROL, - disabled: true, - }), - thirdMark: new CustomFormControl( - { - name: 'third-Mark', - label: 'Third Mark', - type: FormNodeTypes.CONTROL, - }, - undefined, - [CustomValidators.alphanumeric(), CustomValidators.notZNumber, Validators.minLength(3), Validators.maxLength(9)], - ), - }); - correctingAnErrorForm = new FormGroup({ - newVrm: new CustomFormControl( - { - name: 'new-Vrm', - label: 'New VRM', - type: FormNodeTypes.CONTROL, - }, - '', - [Validators.required, CustomValidators.alphanumeric(), CustomValidators.notZNumber, Validators.minLength(3), Validators.maxLength(9)], - ), - }); - - @Output() isFormDirty = new EventEmitter(); - @Output() isFormInvalid = new EventEmitter(); - - @ViewChildren(DynamicFormGroupComponent) sections!: QueryList; - - private destroy$ = new Subject(); - - constructor( - private actions$: Actions, - public dfs: DynamicFormService, - private globalErrorService: GlobalErrorService, - private route: ActivatedRoute, - private router: Router, - private store: Store, - private technicalRecordService: TechnicalRecordService, - ) {} - - ngOnInit(): void { - this.route.params.pipe(takeUntil(this.destroy$)).subscribe((params) => { - this.systemNumber = params['systemNumber']; - this.createdTimestamp = params['createdTimestamp']; - this.isCherishedTransfer = params['reason'] === 'cherished-transfer'; - }); - this.technicalRecordService.techRecord$.pipe(take(1), takeUntil(this.destroy$)).subscribe((record) => { - if (record?.techRecord_statusCode === 'archived' || !record) { - return this.navigateBack(); - } - this.techRecord = record as VehiclesOtherThan<'trl'>; - this.makeAndModel = this.technicalRecordService.getMakeAndModel(record); - }); - - this.actions$.pipe(ofType(amendVrmSuccess), takeUntil(this.destroy$)).subscribe(({ vehicleTechRecord }) => { - void this.router.navigate(['/tech-records', `${vehicleTechRecord.systemNumber}`, `${vehicleTechRecord.createdTimestamp}`]); - }); - - this.cherishedTransferForm.controls['previousVrm'].setValue(this.techRecord?.primaryVrm ?? ''); - this.cherishedTransferForm.controls['previousVrm'].disable(); - this.cherishedTransferForm.controls['thirdMark'].setAsyncValidators( - this.technicalRecordService.validateVrmDoesNotExist(this.techRecord?.primaryVrm ?? ''), - ); - this.correctingAnErrorForm.controls['newVrm'].setAsyncValidators( - this.technicalRecordService.validateVrmDoesNotExist(this.techRecord?.primaryVrm ?? ''), - ); - } - - ngOnDestroy(): void { - this.destroy$.next(); - this.destroy$.complete(); - } - - get vehicleType(): VehicleTypes | undefined { - return this.techRecord ? this.technicalRecordService.getVehicleTypeWithSmallTrl(this.techRecord) : undefined; - } - - navigateBack() { - this.globalErrorService.clearErrors(); - void this.router.navigate(['../../'], { relativeTo: this.route }); - } - - handleFormChange() { - if (this.isCherishedTransfer) { - this.cherishedTransferForm.get('currentVrm')?.updateValueAndValidity(); - } - } - - handleSubmit(): void { - if (!this.isFormValid()) { - return; - } - - this.store.dispatch( - amendVrm({ - newVrm: this.isCherishedTransfer ? this.cherishedTransferForm.value.currentVrm : this.correctingAnErrorForm.value.newVrm, - cherishedTransfer: this.isCherishedTransfer, - thirdMark: this.isCherishedTransfer ? this.cherishedTransferForm.value.thirdMark : undefined, - systemNumber: (this.techRecord as TechRecordType<'get'>)?.systemNumber, - createdTimestamp: (this.techRecord as TechRecordType<'get'>)?.createdTimestamp, - }), - ); - } - - isFormValid(): boolean { - this.globalErrorService.clearErrors(); - - const errors: GlobalError[] = []; - - if (this.isCherishedTransfer) { - DynamicFormService.validate(this.cherishedTransferForm, errors, false); - } else { - DynamicFormService.validate(this.correctingAnErrorForm, errors, false); - } - - if (errors?.length > 0) { - this.globalErrorService.setErrors(errors); - return false; - } - return true; - } - - get showWarning(): boolean { - if (this.vehicleType) { - return this.vehicleType === 'psv' || this.vehicleType === 'hgv'; - } - return false; - } + techRecord?: VehiclesOtherThan<'trl'>; + makeAndModel?: string; + isCherishedTransfer = false; + systemNumber?: string; + createdTimestamp?: string; + formValidity = false; + width: FormNodeWidth = FormNodeWidth.L; + + private technicalRecordService = inject(TechnicalRecordService); + + cherishedTransferForm = new FormGroup({ + currentVrm: new CustomFormControl( + { + name: 'current-Vrm', + label: 'Current VRM', + type: FormNodeTypes.CONTROL, + }, + '', + [ + Validators.required, + CustomValidators.alphanumeric(), + CustomValidators.notZNumber, + Validators.minLength(3), + Validators.maxLength(9), + ], + this.technicalRecordService.validateVrmForCherishedTransfer() + ), + previousVrm: new CustomFormControl({ + name: 'previous-Vrm', + label: 'Previous VRM', + type: FormNodeTypes.CONTROL, + disabled: true, + }), + thirdMark: new CustomFormControl( + { + name: 'third-Mark', + label: 'Third Mark', + type: FormNodeTypes.CONTROL, + }, + undefined, + [CustomValidators.alphanumeric(), CustomValidators.notZNumber, Validators.minLength(3), Validators.maxLength(9)] + ), + }); + correctingAnErrorForm = new FormGroup({ + newVrm: new CustomFormControl( + { + name: 'new-Vrm', + label: 'New VRM', + type: FormNodeTypes.CONTROL, + }, + '', + [ + Validators.required, + CustomValidators.alphanumeric(), + CustomValidators.notZNumber, + Validators.minLength(3), + Validators.maxLength(9), + ] + ), + }); + + @Output() isFormDirty = new EventEmitter(); + @Output() isFormInvalid = new EventEmitter(); + + @ViewChildren(DynamicFormGroupComponent) sections!: QueryList; + + private destroy$ = new Subject(); + + constructor( + private actions$: Actions, + public dfs: DynamicFormService, + private globalErrorService: GlobalErrorService, + private route: ActivatedRoute, + private router: Router, + private store: Store + ) {} + + ngOnInit(): void { + this.route.params.pipe(takeUntil(this.destroy$)).subscribe((params) => { + this.systemNumber = params['systemNumber']; + this.createdTimestamp = params['createdTimestamp']; + this.isCherishedTransfer = params['reason'] === 'cherished-transfer'; + }); + this.technicalRecordService.techRecord$.pipe(take(1), takeUntil(this.destroy$)).subscribe((record) => { + if (record?.techRecord_statusCode === 'archived' || !record) { + return this.navigateBack(); + } + this.techRecord = record as VehiclesOtherThan<'trl'>; + this.makeAndModel = this.technicalRecordService.getMakeAndModel(record); + }); + + this.actions$.pipe(ofType(amendVrmSuccess), takeUntil(this.destroy$)).subscribe(({ vehicleTechRecord }) => { + void this.router.navigate([ + '/tech-records', + `${vehicleTechRecord.systemNumber}`, + `${vehicleTechRecord.createdTimestamp}`, + ]); + }); + + this.cherishedTransferForm.controls['previousVrm'].setValue(this.techRecord?.primaryVrm ?? ''); + this.cherishedTransferForm.controls['previousVrm'].disable(); + this.cherishedTransferForm.controls['thirdMark'].setAsyncValidators( + this.technicalRecordService.validateVrmDoesNotExist(this.techRecord?.primaryVrm ?? '') + ); + this.correctingAnErrorForm.controls['newVrm'].setAsyncValidators( + this.technicalRecordService.validateVrmDoesNotExist(this.techRecord?.primaryVrm ?? '') + ); + } + + ngOnDestroy(): void { + this.destroy$.next(); + this.destroy$.complete(); + } + + get vehicleType(): VehicleTypes | undefined { + return this.techRecord ? this.technicalRecordService.getVehicleTypeWithSmallTrl(this.techRecord) : undefined; + } + + navigateBack() { + this.globalErrorService.clearErrors(); + void this.router.navigate(['../../'], { relativeTo: this.route }); + } + + handleFormChange() { + if (this.isCherishedTransfer) { + this.cherishedTransferForm.get('currentVrm')?.updateValueAndValidity(); + } + } + + handleSubmit(): void { + if (!this.isFormValid()) { + return; + } + + this.store.dispatch( + amendVrm({ + newVrm: this.isCherishedTransfer + ? this.cherishedTransferForm.value.currentVrm + : this.correctingAnErrorForm.value.newVrm, + cherishedTransfer: this.isCherishedTransfer, + thirdMark: this.isCherishedTransfer ? this.cherishedTransferForm.value.thirdMark : undefined, + systemNumber: (this.techRecord as TechRecordType<'get'>)?.systemNumber, + createdTimestamp: (this.techRecord as TechRecordType<'get'>)?.createdTimestamp, + }) + ); + } + + isFormValid(): boolean { + this.globalErrorService.clearErrors(); + + const errors: GlobalError[] = []; + + if (this.isCherishedTransfer) { + DynamicFormService.validate(this.cherishedTransferForm, errors, false); + } else { + DynamicFormService.validate(this.correctingAnErrorForm, errors, false); + } + + if (errors?.length > 0) { + this.globalErrorService.setErrors(errors); + return false; + } + return true; + } + + get showWarning(): boolean { + if (this.vehicleType) { + return this.vehicleType === 'psv' || this.vehicleType === 'hgv'; + } + return false; + } } diff --git a/src/app/features/tech-record/components/tech-record-change-status/tech-record-change-status.component.spec.ts b/src/app/features/tech-record/components/tech-record-change-status/tech-record-change-status.component.spec.ts index 4804d30568..ef212aac05 100644 --- a/src/app/features/tech-record/components/tech-record-change-status/tech-record-change-status.component.spec.ts +++ b/src/app/features/tech-record/components/tech-record-change-status/tech-record-change-status.component.spec.ts @@ -16,37 +16,44 @@ import { TechRecordTitleComponent } from '../tech-record-title/tech-record-title import { TechRecordChangeStatusComponent } from './tech-record-change-status.component'; describe('TechRecordChangeStatusComponent', () => { - let actions$: ReplaySubject; - let component: TechRecordChangeStatusComponent; - let fixture: ComponentFixture; + let actions$: ReplaySubject; + let component: TechRecordChangeStatusComponent; + let fixture: ComponentFixture; - beforeEach(async () => { - actions$ = new ReplaySubject(); + beforeEach(async () => { + actions$ = new ReplaySubject(); - await TestBed.configureTestingModule({ - declarations: [TechRecordChangeStatusComponent, TechRecordTitleComponent], - imports: [DynamicFormsModule, HttpClientTestingModule, ReactiveFormsModule, RouterTestingModule, SharedModule, StoreModule.forRoot({})], - providers: [ - provideMockActions(() => actions$), - provideMockStore({ initialState: initialAppState }), - { provide: APP_BASE_HREF, useValue: '/' }, - { - provide: UserService, - useValue: { - roles$: of([Roles.TechRecordArchive]), - }, - }, - ], - }).compileComponents(); - }); + await TestBed.configureTestingModule({ + declarations: [TechRecordChangeStatusComponent, TechRecordTitleComponent], + imports: [ + DynamicFormsModule, + HttpClientTestingModule, + ReactiveFormsModule, + RouterTestingModule, + SharedModule, + StoreModule.forRoot({}), + ], + providers: [ + provideMockActions(() => actions$), + provideMockStore({ initialState: initialAppState }), + { provide: APP_BASE_HREF, useValue: '/' }, + { + provide: UserService, + useValue: { + roles$: of([Roles.TechRecordArchive]), + }, + }, + ], + }).compileComponents(); + }); - beforeEach(() => { - fixture = TestBed.createComponent(TechRecordChangeStatusComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); + beforeEach(() => { + fixture = TestBed.createComponent(TechRecordChangeStatusComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); }); diff --git a/src/app/features/tech-record/components/tech-record-change-status/tech-record-change-status.component.ts b/src/app/features/tech-record/components/tech-record-change-status/tech-record-change-status.component.ts index f8e1a38a1d..89aa6a1752 100644 --- a/src/app/features/tech-record/components/tech-record-change-status/tech-record-change-status.component.ts +++ b/src/app/features/tech-record/components/tech-record-change-status/tech-record-change-status.component.ts @@ -9,103 +9,117 @@ import { Store } from '@ngrx/store'; import { TechnicalRecordService } from '@services/technical-record/technical-record.service'; import { State } from '@store/index'; import { - archiveTechRecord, archiveTechRecordSuccess, promoteTechRecord, promoteTechRecordSuccess, + archiveTechRecord, + archiveTechRecordSuccess, + promoteTechRecord, + promoteTechRecordSuccess, } from '@store/technical-records'; import { Subject, takeUntil } from 'rxjs'; @Component({ - selector: 'app-tech-record-change-status', - templateUrl: './tech-record-change-status.component.html', + selector: 'app-tech-record-change-status', + templateUrl: './tech-record-change-status.component.html', }) export class TechRecordChangeStatusComponent implements OnInit, OnDestroy { - techRecord: TechRecordType<'get'> | undefined; - - form: CustomFormGroup; - - isPromotion = false; - - destroy$ = new Subject(); - - constructor( - private actions$: Actions, - private errorService: GlobalErrorService, - private route: ActivatedRoute, - private router: Router, - private store: Store, - private technicalRecordService: TechnicalRecordService, - ) { - this.form = new CustomFormGroup( - { name: 'reasonForPromotion', type: FormNodeTypes.GROUP }, - { reason: new CustomFormControl({ name: 'reason', type: FormNodeTypes.CONTROL }, undefined, [Validators.required]) }, - ); - } - - ngOnInit(): void { - this.technicalRecordService.techRecord$.pipe(takeUntil(this.destroy$)).subscribe((record) => { - this.techRecord = record as TechRecordType<'get'>; - }); - - this.actions$.pipe(ofType(promoteTechRecordSuccess, archiveTechRecordSuccess), takeUntil(this.destroy$)).subscribe(({ vehicleTechRecord }) => { - void this.router.navigate([`/tech-records/${vehicleTechRecord.systemNumber}/${vehicleTechRecord.createdTimestamp}`]); - - this.technicalRecordService.clearEditingTechRecord(); - }); - - this.route.queryParamMap.pipe(takeUntil(this.destroy$)).subscribe((params) => { - this.isPromotion = params.get('to') === 'current'; - }); - } - - ngOnDestroy(): void { - this.destroy$.next(); - this.destroy$.complete(); - } - - get label(): string { - return `Reason for ${this.isPromotion ? 'promotion' : 'archiving'}`; - } - - get buttonLabel(): string { - return this.isPromotion ? 'Promote' : 'Archive'; - } - - navigateBack(relativePath = '..'): void { - void this.router.navigate([relativePath], { relativeTo: this.route }); - } - - handleSubmit(form: { reason: string }): void { - if (!this.techRecord) { - return; - } - - if (this.form.valid) { - this.errorService.clearErrors(); - } else { - this.errorService.setErrors([ - { error: `Reason for ${this.isPromotion ? 'promotion' : 'archiving'} is required`, anchorLink: 'reasonForAmend' }, - ]); - } - - if (!this.form.valid || !form.reason) { - return; - } - - if (this.isPromotion) { - this.store.dispatch( - promoteTechRecord({ - systemNumber: this.techRecord.systemNumber, - createdTimestamp: this.techRecord.createdTimestamp, - reasonForPromoting: this.form.value.reason, - }), - ); - } else { - this.store.dispatch( - archiveTechRecord({ - systemNumber: this.techRecord.systemNumber, - createdTimestamp: this.techRecord.createdTimestamp, - reasonForArchiving: this.form.value.reason, - }), - ); - } - } + techRecord: TechRecordType<'get'> | undefined; + + form: CustomFormGroup; + + isPromotion = false; + + destroy$ = new Subject(); + + constructor( + private actions$: Actions, + private errorService: GlobalErrorService, + private route: ActivatedRoute, + private router: Router, + private store: Store, + private technicalRecordService: TechnicalRecordService + ) { + this.form = new CustomFormGroup( + { name: 'reasonForPromotion', type: FormNodeTypes.GROUP }, + { + reason: new CustomFormControl({ name: 'reason', type: FormNodeTypes.CONTROL }, undefined, [ + Validators.required, + ]), + } + ); + } + + ngOnInit(): void { + this.technicalRecordService.techRecord$.pipe(takeUntil(this.destroy$)).subscribe((record) => { + this.techRecord = record as TechRecordType<'get'>; + }); + + this.actions$ + .pipe(ofType(promoteTechRecordSuccess, archiveTechRecordSuccess), takeUntil(this.destroy$)) + .subscribe(({ vehicleTechRecord }) => { + void this.router.navigate([ + `/tech-records/${vehicleTechRecord.systemNumber}/${vehicleTechRecord.createdTimestamp}`, + ]); + + this.technicalRecordService.clearEditingTechRecord(); + }); + + this.route.queryParamMap.pipe(takeUntil(this.destroy$)).subscribe((params) => { + this.isPromotion = params.get('to') === 'current'; + }); + } + + ngOnDestroy(): void { + this.destroy$.next(); + this.destroy$.complete(); + } + + get label(): string { + return `Reason for ${this.isPromotion ? 'promotion' : 'archiving'}`; + } + + get buttonLabel(): string { + return this.isPromotion ? 'Promote' : 'Archive'; + } + + navigateBack(relativePath = '..'): void { + void this.router.navigate([relativePath], { relativeTo: this.route }); + } + + handleSubmit(form: { reason: string }): void { + if (!this.techRecord) { + return; + } + + if (this.form.valid) { + this.errorService.clearErrors(); + } else { + this.errorService.setErrors([ + { + error: `Reason for ${this.isPromotion ? 'promotion' : 'archiving'} is required`, + anchorLink: 'reasonForAmend', + }, + ]); + } + + if (!this.form.valid || !form.reason) { + return; + } + + if (this.isPromotion) { + this.store.dispatch( + promoteTechRecord({ + systemNumber: this.techRecord.systemNumber, + createdTimestamp: this.techRecord.createdTimestamp, + reasonForPromoting: this.form.value.reason, + }) + ); + } else { + this.store.dispatch( + archiveTechRecord({ + systemNumber: this.techRecord.systemNumber, + createdTimestamp: this.techRecord.createdTimestamp, + reasonForArchiving: this.form.value.reason, + }) + ); + } + } } diff --git a/src/app/features/tech-record/components/tech-record-change-type/tech-record-change-type.component.html b/src/app/features/tech-record/components/tech-record-change-type/tech-record-change-type.component.html index e024b9a33e..71d53b378d 100644 --- a/src/app/features/tech-record/components/tech-record-change-type/tech-record-change-type.component.html +++ b/src/app/features/tech-record/components/tech-record-change-type/tech-record-change-type.component.html @@ -37,7 +37,7 @@

Are you sure you want to change this Vehicle type?<
- Confirm and continue + Confirm and continue Back
diff --git a/src/app/features/tech-record/components/tech-record-change-type/tech-record-change-type.component.spec.ts b/src/app/features/tech-record/components/tech-record-change-type/tech-record-change-type.component.spec.ts index 1aa9e84bd9..c3294cb4b5 100644 --- a/src/app/features/tech-record/components/tech-record-change-type/tech-record-change-type.component.spec.ts +++ b/src/app/features/tech-record/components/tech-record-change-type/tech-record-change-type.component.spec.ts @@ -21,150 +21,157 @@ import { ChangeVehicleTypeComponent } from './tech-record-change-type.component' const mockGetVehicleType = jest.fn(); const mockTechRecordService = { - get techRecord$() { - return of({}); - }, - getMakeAndModel: jest.fn(), - clearReasonForCreation: jest.fn(), - getVehicleTypeWithSmallTrl: mockGetVehicleType, + get techRecord$() { + return of({}); + }, + getMakeAndModel: jest.fn(), + clearReasonForCreation: jest.fn(), + getVehicleTypeWithSmallTrl: mockGetVehicleType, }; const mockDynamicFormService = { - createForm: jest.fn(), + createForm: jest.fn(), }; describe('TechRecordChangeTypeComponent', () => { - const actions$ = new ReplaySubject(); - let component: ChangeVehicleTypeComponent; - let errorService: GlobalErrorService; - let expectedTechRecord = {} as V3TechRecordModel; - let fixture: ComponentFixture; - let route: ActivatedRoute; - let router: Router; - let store: MockStore; - - beforeEach(async () => { - await TestBed.configureTestingModule({ - declarations: [ChangeVehicleTypeComponent], - providers: [ - GlobalErrorService, - provideMockActions(() => actions$), - provideMockStore({ initialState: initialAppState }), - { provide: ActivatedRoute, useValue: { params: of([{ id: 1 }]) } }, - { provide: DynamicFormService, useValue: mockDynamicFormService }, - { provide: TechnicalRecordService, useValue: mockTechRecordService }, - ], - imports: [DynamicFormsModule, RouterTestingModule, SharedModule, FixNavigationTriggeredOutsideAngularZoneNgModule], - }).compileComponents(); - }); - - beforeEach(() => { - fixture = TestBed.createComponent(ChangeVehicleTypeComponent); - errorService = TestBed.inject(GlobalErrorService); - route = TestBed.inject(ActivatedRoute); - router = TestBed.inject(Router); - store = TestBed.inject(MockStore); - component = fixture.componentInstance; - expectedTechRecord = { - systemNumber: 'foo', - createdTimestamp: 'bar', - vin: 'testVin', - techRecord_vehicleType: VehicleTypes.PSV, - techRecord_chassisMake: 'test-make', - techRecord_chassisModel: 'test-model', - } as V3TechRecordModel; - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); - - describe('makeAndModel', () => { - it('should should return the make and model', () => { - const techRecord = expectedTechRecord as TechRecordType<'psv'>; - const expectedMakeModel = `${techRecord.techRecord_chassisMake} - ${techRecord.techRecord_chassisModel}`; - - jest.spyOn(mockTechRecordService, 'getMakeAndModel').mockReturnValueOnce(expectedMakeModel); - - component.techRecord = expectedTechRecord; - component.ngOnInit(); - - expect(component.makeAndModel).toBe(expectedMakeModel); - }); - - it('should return an empty string when the current record is null', () => { - delete component.techRecord; - component.ngOnInit(); - - expect(component.makeAndModel).toBeUndefined(); - }); - }); - - describe('vehicleTypeOptions', () => { - it('should return all types except for the current one', () => { - component.techRecord = expectedTechRecord; - mockGetVehicleType.mockReturnValue('psv'); - const expectedOptions = getOptionsFromEnumAcronym(VehicleTypes).filter((type) => type.value !== VehicleTypes.PSV); - expect(component.vehicleTypeOptions).toStrictEqual(expectedOptions); - }); - }); - - describe('navigateBack', () => { - it('should clear all errors', () => { - jest.spyOn(router, 'navigate').mockImplementation(); - - const clearErrorsSpy = jest.spyOn(errorService, 'clearErrors'); - - component.navigateBack(); - - expect(clearErrorsSpy).toHaveBeenCalledTimes(1); - }); - - it('should navigate back to the previous page', () => { - const navigateSpy = jest.spyOn(router, 'navigate').mockImplementation(() => Promise.resolve(true)); - - component.navigateBack(); - - expect(navigateSpy).toHaveBeenCalledWith(['..'], { relativeTo: route }); - }); - }); - - describe('handleSubmit', () => { - it('should add an error when no vehicle type is selected', () => { - const addErrorSpy = jest.spyOn(errorService, 'addError'); - - component.handleSubmit(null as unknown as VehicleTypes); - - expect(addErrorSpy).toHaveBeenCalledWith({ error: 'You must provide a new vehicle type', anchorLink: 'selectedVehicleType' }); - }); - - it('should dispatch the changeVehicleType action', () => { - jest.spyOn(router, 'navigate').mockImplementation(); + const actions$ = new ReplaySubject(); + let component: ChangeVehicleTypeComponent; + let errorService: GlobalErrorService; + let expectedTechRecord = {} as V3TechRecordModel; + let fixture: ComponentFixture; + let route: ActivatedRoute; + let router: Router; + let store: MockStore; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ChangeVehicleTypeComponent], + providers: [ + GlobalErrorService, + provideMockActions(() => actions$), + provideMockStore({ initialState: initialAppState }), + { provide: ActivatedRoute, useValue: { params: of([{ id: 1 }]) } }, + { provide: DynamicFormService, useValue: mockDynamicFormService }, + { provide: TechnicalRecordService, useValue: mockTechRecordService }, + ], + imports: [ + DynamicFormsModule, + RouterTestingModule, + SharedModule, + FixNavigationTriggeredOutsideAngularZoneNgModule, + ], + }).compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(ChangeVehicleTypeComponent); + errorService = TestBed.inject(GlobalErrorService); + route = TestBed.inject(ActivatedRoute); + router = TestBed.inject(Router); + store = TestBed.inject(MockStore); + component = fixture.componentInstance; + expectedTechRecord = { + systemNumber: 'foo', + createdTimestamp: 'bar', + vin: 'testVin', + techRecord_vehicleType: VehicleTypes.PSV, + techRecord_chassisMake: 'test-make', + techRecord_chassisModel: 'test-model', + } as V3TechRecordModel; + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + describe('makeAndModel', () => { + it('should should return the make and model', () => { + const techRecord = expectedTechRecord as TechRecordType<'psv'>; + const expectedMakeModel = `${techRecord.techRecord_chassisMake} - ${techRecord.techRecord_chassisModel}`; + + jest.spyOn(mockTechRecordService, 'getMakeAndModel').mockReturnValueOnce(expectedMakeModel); + + component.techRecord = expectedTechRecord; + component.ngOnInit(); + + expect(component.makeAndModel).toBe(expectedMakeModel); + }); + + it('should return an empty string when the current record is null', () => { + delete component.techRecord; + component.ngOnInit(); + + expect(component.makeAndModel).toBeUndefined(); + }); + }); + + describe('vehicleTypeOptions', () => { + it('should return all types except for the current one', () => { + component.techRecord = expectedTechRecord; + mockGetVehicleType.mockReturnValue('psv'); + const expectedOptions = getOptionsFromEnumAcronym(VehicleTypes).filter((type) => type.value !== VehicleTypes.PSV); + expect(component.vehicleTypeOptions).toStrictEqual(expectedOptions); + }); + }); + + describe('navigateBack', () => { + it('should clear all errors', () => { + jest.spyOn(router, 'navigate').mockImplementation(); + + const clearErrorsSpy = jest.spyOn(errorService, 'clearErrors'); + + component.navigateBack(); + + expect(clearErrorsSpy).toHaveBeenCalledTimes(1); + }); + + it('should navigate back to the previous page', () => { + const navigateSpy = jest.spyOn(router, 'navigate').mockImplementation(() => Promise.resolve(true)); + + component.navigateBack(); + + expect(navigateSpy).toHaveBeenCalledWith(['..'], { relativeTo: route }); + }); + }); + + describe('handleSubmit', () => { + it('should add an error when no vehicle type is selected', () => { + const setErrorsSpy = jest.spyOn(errorService, 'setErrors'); + + component.handleSubmit(null as unknown as VehicleTypes); + + expect(setErrorsSpy).toHaveBeenCalledWith([ + { error: 'You must provide a new vehicle type', anchorLink: 'selectedVehicleType' }, + ]); + }); + + it('should dispatch the changeVehicleType action', () => { + jest.spyOn(router, 'navigate').mockImplementation(); + + const dispatchSpy = jest.spyOn(store, 'dispatch'); + + component.handleSubmit(VehicleTypes.PSV); - const dispatchSpy = jest.spyOn(store, 'dispatch'); + expect(dispatchSpy).toHaveBeenCalledWith(changeVehicleType({ techRecord_vehicleType: VehicleTypes.PSV })); + }); - component.handleSubmit(VehicleTypes.PSV); + it('should call clearReasonForCreation', () => { + jest.spyOn(router, 'navigate').mockImplementation(); - expect(dispatchSpy).toHaveBeenCalledWith(changeVehicleType({ techRecord_vehicleType: VehicleTypes.PSV })); - }); + const clearReasonForCreationSpy = jest.spyOn(mockTechRecordService, 'clearReasonForCreation'); - it('should call clearReasonForCreation', () => { - jest.spyOn(router, 'navigate').mockImplementation(); + jest.resetAllMocks(); + component.handleSubmit(VehicleTypes.PSV); - const clearReasonForCreationSpy = jest.spyOn(mockTechRecordService, 'clearReasonForCreation'); + expect(clearReasonForCreationSpy).toHaveBeenCalledTimes(1); + }); - jest.resetAllMocks(); - component.handleSubmit(VehicleTypes.PSV); + it('navigate to the editing page', () => { + const navigateSpy = jest.spyOn(router, 'navigate').mockImplementation(() => Promise.resolve(true)); - expect(clearReasonForCreationSpy).toHaveBeenCalledTimes(1); - }); + component.handleSubmit(VehicleTypes.PSV); - it('navigate to the editing page', () => { - const navigateSpy = jest.spyOn(router, 'navigate').mockImplementation(() => Promise.resolve(true)); - - component.handleSubmit(VehicleTypes.PSV); - - expect(navigateSpy).toHaveBeenCalledWith(['../amend-reason'], { relativeTo: route }); - }); - }); + expect(navigateSpy).toHaveBeenCalledWith(['../amend-reason'], { relativeTo: route }); + }); + }); }); diff --git a/src/app/features/tech-record/components/tech-record-change-type/tech-record-change-type.component.ts b/src/app/features/tech-record/components/tech-record-change-type/tech-record-change-type.component.ts index cad604794f..373e44d106 100644 --- a/src/app/features/tech-record/components/tech-record-change-type/tech-record-change-type.component.ts +++ b/src/app/features/tech-record/components/tech-record-change-type/tech-record-change-type.component.ts @@ -8,9 +8,7 @@ import { MultiOptions } from '@forms/models/options.model'; import { CustomFormControl, FormNodeTypes } from '@forms/services/dynamic-form.types'; import { getOptionsFromEnumAcronym } from '@forms/utils/enum-map'; -import { - StatusCodes, V3TechRecordModel, VehicleTypes, -} from '@models/vehicle-tech-record.model'; +import { StatusCodes, V3TechRecordModel, VehicleTypes } from '@models/vehicle-tech-record.model'; import { Store } from '@ngrx/store'; import { TechnicalRecordService } from '@services/technical-record/technical-record.service'; import { changeVehicleType } from '@store/technical-records'; @@ -18,86 +16,93 @@ import { TechnicalRecordServiceState } from '@store/technical-records/reducers/t import { take } from 'rxjs'; @Component({ - selector: 'app-change-vehicle-type', - templateUrl: './tech-record-change-type.component.html', - styleUrls: ['./tech-record-change-type.component.scss'], + selector: 'app-change-vehicle-type', + templateUrl: './tech-record-change-type.component.html', + styleUrls: ['./tech-record-change-type.component.scss'], }) export class ChangeVehicleTypeComponent implements OnInit { - techRecord?: V3TechRecordModel; - makeAndModel?: string; - - form: FormGroup = new FormGroup({ - selectVehicleType: new CustomFormControl( - { name: 'change-vehicle-type-select', label: 'Select a new vehicle type', type: FormNodeTypes.CONTROL }, - '', - [Validators.required], - ), - }); - - constructor( - private globalErrorService: GlobalErrorService, - private route: ActivatedRoute, - private router: Router, - private store: Store, - private technicalRecordService: TechnicalRecordService, - ) {} - - ngOnInit(): void { - this.globalErrorService.clearErrors(); - this.technicalRecordService.techRecord$.pipe(take(1)).subscribe((techRecord) => { - if (!techRecord) { - this.navigateBack(); - } else { - this.techRecord = techRecord; - } - }); - - if (this.techRecord) { - this.makeAndModel = this.technicalRecordService.getMakeAndModel(this.techRecord); - } - } - - get vehicleType(): VehicleTypes | undefined { - return this.techRecord ? this.technicalRecordService.getVehicleTypeWithSmallTrl(this.techRecord) : undefined; - } - - get vehicleTypeOptions(): MultiOptions { - return getOptionsFromEnumAcronym(VehicleTypes).filter((type) => type.value !== this.vehicleType); - } - - get currentVrm() { - return this.techRecord?.techRecord_vehicleType !== 'trl' ? this.techRecord?.primaryVrm : undefined; - } - - navigateBack() { - this.globalErrorService.clearErrors(); - void this.router.navigate(['..'], { relativeTo: this.route }); - } - - handleSubmit(selectedVehicleType: VehicleTypes): void { - if (!selectedVehicleType) { - return this.globalErrorService.addError({ error: 'You must provide a new vehicle type', anchorLink: 'selectedVehicleType' }); - } - - if ( - selectedVehicleType === VehicleTypes.TRL - && ((this.techRecord as TechRecordTypeByVehicle<'trl'>)?.techRecord_euVehicleCategory === EUVehicleCategory.O1 - || (this.techRecord as TechRecordTypeByVehicle<'trl'>)?.techRecord_euVehicleCategory === EUVehicleCategory.O2) - ) { - return this.globalErrorService.addError({ - error: 'You cannot change vehicle type to TRL when EU vehicle category is set to \'O1\' or \'O2\'', - anchorLink: 'selectedVehicleType', - }); - } - - this.store.dispatch(changeVehicleType({ techRecord_vehicleType: selectedVehicleType })); - - this.technicalRecordService.clearReasonForCreation(); - - this.globalErrorService.clearErrors(); - - const routeSuffix = this.techRecord?.techRecord_statusCode !== StatusCodes.PROVISIONAL ? 'amend-reason' : 'notifiable-alteration-needed'; - - void this.router.navigate([`../${routeSuffix}`], { relativeTo: this.route }); - } + techRecord?: V3TechRecordModel; + makeAndModel?: string; + + form: FormGroup = new FormGroup({ + selectVehicleType: new CustomFormControl( + { name: 'change-vehicle-type-select', label: 'Select a new vehicle type', type: FormNodeTypes.CONTROL }, + '', + [Validators.required] + ), + }); + + constructor( + private globalErrorService: GlobalErrorService, + private route: ActivatedRoute, + private router: Router, + private store: Store, + private technicalRecordService: TechnicalRecordService + ) {} + + ngOnInit(): void { + this.globalErrorService.clearErrors(); + this.technicalRecordService.techRecord$.pipe(take(1)).subscribe((techRecord) => { + if (!techRecord) { + this.navigateBack(); + } else { + this.techRecord = techRecord; + } + }); + + if (this.techRecord) { + this.makeAndModel = this.technicalRecordService.getMakeAndModel(this.techRecord); + } + } + + get vehicleType(): VehicleTypes | undefined { + return this.techRecord ? this.technicalRecordService.getVehicleTypeWithSmallTrl(this.techRecord) : undefined; + } + + get vehicleTypeOptions(): MultiOptions { + return getOptionsFromEnumAcronym(VehicleTypes).filter((type) => type.value !== this.vehicleType); + } + + get currentVrm() { + return this.techRecord?.techRecord_vehicleType !== 'trl' ? this.techRecord?.primaryVrm : undefined; + } + + navigateBack() { + this.globalErrorService.clearErrors(); + void this.router.navigate(['..'], { relativeTo: this.route }); + } + + handleSubmit(selectedVehicleType: VehicleTypes): void { + if (!selectedVehicleType) { + return this.globalErrorService.setErrors([ + { error: 'You must provide a new vehicle type', anchorLink: 'selectedVehicleType' }, + ]); + } + + if ( + selectedVehicleType === VehicleTypes.TRL && + ((this.techRecord as TechRecordTypeByVehicle<'trl'>)?.techRecord_euVehicleCategory === EUVehicleCategory.O1 || + (this.techRecord as TechRecordTypeByVehicle<'trl'>)?.techRecord_euVehicleCategory === EUVehicleCategory.O2) + ) { + return this.globalErrorService.setErrors([ + { + error: "You cannot change vehicle type to TRL when EU vehicle category is set to 'O1' or 'O2'", + anchorLink: 'selectedVehicleType', + }, + ]); + } + + this.store.dispatch(changeVehicleType({ techRecord_vehicleType: selectedVehicleType })); + + this.technicalRecordService.clearReasonForCreation(); + + this.globalErrorService.clearErrors(); + + const routeSuffix = + this.techRecord?.techRecord_statusCode !== StatusCodes.PROVISIONAL + ? 'amend-reason' + : 'notifiable-alteration-needed'; + + void this.router.navigate([`../${routeSuffix}`], { relativeTo: this.route }); + } } diff --git a/src/app/features/tech-record/components/tech-record-change-visibility/tech-record-change-visibility.component.spec.ts b/src/app/features/tech-record/components/tech-record-change-visibility/tech-record-change-visibility.component.spec.ts index 32aedacf5c..316e40c076 100644 --- a/src/app/features/tech-record/components/tech-record-change-visibility/tech-record-change-visibility.component.spec.ts +++ b/src/app/features/tech-record/components/tech-record-change-visibility/tech-record-change-visibility.component.spec.ts @@ -17,36 +17,42 @@ import { TechRecordTitleComponent } from '../tech-record-title/tech-record-title import { TechRecordChangeVisibilityComponent } from './tech-record-change-visibility.component'; describe('TechRecordHoldComponent', () => { - let actions$: ReplaySubject; - let component: TechRecordChangeVisibilityComponent; - let fixture: ComponentFixture; + let actions$: ReplaySubject; + let component: TechRecordChangeVisibilityComponent; + let fixture: ComponentFixture; - beforeEach(async () => { - await TestBed.configureTestingModule({ - declarations: [TechRecordChangeVisibilityComponent, TechRecordTitleComponent], - imports: [DynamicFormsModule, HttpClientTestingModule, ReactiveFormsModule, RouterTestingModule, SharedModule, StoreModule.forRoot({})], - providers: [ - provideMockActions(() => actions$), - provideMockStore({ initialState: initialAppState }), - { provide: APP_BASE_HREF, useValue: '/' }, - { - provide: UserService, - useValue: { - roles$: of([Roles.TechRecordArchive]), - }, - }, - ], - }).compileComponents(); + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [TechRecordChangeVisibilityComponent, TechRecordTitleComponent], + imports: [ + DynamicFormsModule, + HttpClientTestingModule, + ReactiveFormsModule, + RouterTestingModule, + SharedModule, + StoreModule.forRoot({}), + ], + providers: [ + provideMockActions(() => actions$), + provideMockStore({ initialState: initialAppState }), + { provide: APP_BASE_HREF, useValue: '/' }, + { + provide: UserService, + useValue: { + roles$: of([Roles.TechRecordArchive]), + }, + }, + ], + }).compileComponents(); + }); - }); + beforeEach(() => { + fixture = TestBed.createComponent(TechRecordChangeVisibilityComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); - beforeEach(() => { - fixture = TestBed.createComponent(TechRecordChangeVisibilityComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); }); diff --git a/src/app/features/tech-record/components/tech-record-change-visibility/tech-record-change-visibility.component.ts b/src/app/features/tech-record/components/tech-record-change-visibility/tech-record-change-visibility.component.ts index 79bdbf5f4a..5981efd8dd 100644 --- a/src/app/features/tech-record/components/tech-record-change-visibility/tech-record-change-visibility.component.ts +++ b/src/app/features/tech-record/components/tech-record-change-visibility/tech-record-change-visibility.component.ts @@ -12,100 +12,109 @@ import { TechnicalRecordService } from '@services/technical-record/technical-rec import { State } from '@store/index'; import { techRecord, updateTechRecord, updateTechRecordSuccess } from '@store/technical-records'; import cloneDeep from 'lodash.clonedeep'; -import { - Subject, skipWhile, take, takeUntil, withLatestFrom, -} from 'rxjs'; +import { Subject, skipWhile, take, takeUntil, withLatestFrom } from 'rxjs'; @Component({ - selector: 'app-tech-record-change-visibility', - templateUrl: './tech-record-change-visibility.component.html', - styleUrls: ['./tech-record-change-visibility.component.scss'], + selector: 'app-tech-record-change-visibility', + templateUrl: './tech-record-change-visibility.component.html', + styleUrls: ['./tech-record-change-visibility.component.scss'], }) export class TechRecordChangeVisibilityComponent implements OnInit, OnDestroy { - techRecord?: V3TechRecordModel; + techRecord?: V3TechRecordModel; - form: CustomFormGroup; - private destroy$ = new Subject(); + form: CustomFormGroup; + private destroy$ = new Subject(); - constructor( - private actions$: Actions, - private errorService: GlobalErrorService, - private route: ActivatedRoute, - private router: Router, - private store: Store, - private technicalRecordService: TechnicalRecordService, - private routerService: RouterService, - ) { - this.form = new CustomFormGroup( - { name: 'reasonForChangingVisibility', type: FormNodeTypes.GROUP }, - { reason: new CustomFormControl({ name: 'reason', type: FormNodeTypes.CONTROL }, undefined, [Validators.required]) }, - ); - this.actions$.pipe(ofType(updateTechRecordSuccess), takeUntil(this.destroy$)).subscribe(({ vehicleTechRecord }) => { - void this.router.navigate([`/tech-records/${vehicleTechRecord.systemNumber}/${vehicleTechRecord.createdTimestamp}`]); - }); - } + constructor( + private actions$: Actions, + private errorService: GlobalErrorService, + private route: ActivatedRoute, + private router: Router, + private store: Store, + private technicalRecordService: TechnicalRecordService, + private routerService: RouterService + ) { + this.form = new CustomFormGroup( + { name: 'reasonForChangingVisibility', type: FormNodeTypes.GROUP }, + { + reason: new CustomFormControl({ name: 'reason', type: FormNodeTypes.CONTROL }, undefined, [ + Validators.required, + ]), + } + ); + this.actions$.pipe(ofType(updateTechRecordSuccess), takeUntil(this.destroy$)).subscribe(({ vehicleTechRecord }) => { + void this.router.navigate([ + `/tech-records/${vehicleTechRecord.systemNumber}/${vehicleTechRecord.createdTimestamp}`, + ]); + }); + } - get title(): string { - return `${this.techRecord?.techRecord_hiddenInVta ? 'Show' : 'Hide'} record in VTA`; - } + get title(): string { + return `${this.techRecord?.techRecord_hiddenInVta ? 'Show' : 'Hide'} record in VTA`; + } - get buttonLabel(): string { - return `${this.techRecord?.techRecord_hiddenInVta ? 'Show' : 'Hide'} record`; - } + get buttonLabel(): string { + return `${this.techRecord?.techRecord_hiddenInVta ? 'Show' : 'Hide'} record`; + } - ngOnInit(): void { - this.store - .select(techRecord) - .pipe(take(1)) - .subscribe((record) => { - this.techRecord = record; - }); - } + ngOnInit(): void { + this.store + .select(techRecord) + .pipe(take(1)) + .subscribe((record) => { + this.techRecord = record; + }); + } - ngOnDestroy(): void { - this.destroy$.next(); - this.destroy$.complete(); - } + ngOnDestroy(): void { + this.destroy$.next(); + this.destroy$.complete(); + } - goBack(): void { - void this.router.navigate(['..'], { relativeTo: this.route }); - } + goBack(): void { + void this.router.navigate(['..'], { relativeTo: this.route }); + } - handleSubmit(form: { reason: string }): void { - if (this.form.valid) { - this.errorService.clearErrors(); - } else { - this.errorService.setErrors([ - { - error: `Reason for ${this.techRecord?.techRecord_hiddenInVta ? 'showing' : 'hiding'} is required`, - anchorLink: 'reasonForChangingVisibility', - }, - ]); - } + handleSubmit(form: { reason: string }): void { + if (this.form.valid) { + this.errorService.clearErrors(); + } else { + this.errorService.setErrors([ + { + error: `Reason for ${this.techRecord?.techRecord_hiddenInVta ? 'showing' : 'hiding'} is required`, + anchorLink: 'reasonForChangingVisibility', + }, + ]); + } - if (!this.form.valid || !form.reason) { - return; - } + if (!this.form.valid || !form.reason) { + return; + } - const updatedTechRecord: TechRecordType<'put'> = { - ...cloneDeep(this.techRecord as TechRecordType<'put'>), - techRecord_reasonForCreation: form.reason, - techRecord_hiddenInVta: !this.techRecord?.techRecord_hiddenInVta, - }; + const updatedTechRecord: TechRecordType<'put'> = { + ...cloneDeep(this.techRecord as TechRecordType<'put'>), + techRecord_reasonForCreation: form.reason, + techRecord_hiddenInVta: !this.techRecord?.techRecord_hiddenInVta, + }; - this.technicalRecordService.updateEditingTechRecord(updatedTechRecord); + this.technicalRecordService.updateEditingTechRecord(updatedTechRecord); - this.technicalRecordService.techRecord$ - .pipe( - takeUntil(this.destroy$), - skipWhile((technicalRecord) => technicalRecord?.techRecord_hiddenInVta !== this.techRecord?.techRecord_hiddenInVta), - withLatestFrom(this.routerService.getRouteNestedParam$('systemNumber'), this.routerService.getRouteNestedParam$('createdTimestamp')), - take(1), - ) - .subscribe(([, systemNumber, createdTimestamp]) => { - if (systemNumber && createdTimestamp) { - this.store.dispatch(updateTechRecord({ systemNumber, createdTimestamp })); - } - }); - } + this.technicalRecordService.techRecord$ + .pipe( + takeUntil(this.destroy$), + skipWhile( + (technicalRecord) => technicalRecord?.techRecord_hiddenInVta !== this.techRecord?.techRecord_hiddenInVta + ), + withLatestFrom( + this.routerService.getRouteNestedParam$('systemNumber'), + this.routerService.getRouteNestedParam$('createdTimestamp') + ), + take(1) + ) + .subscribe(([, systemNumber, createdTimestamp]) => { + if (systemNumber && createdTimestamp) { + this.store.dispatch(updateTechRecord({ systemNumber, createdTimestamp })); + } + }); + } } diff --git a/src/app/features/tech-record/components/tech-record-edit-additional-examiner-note/tech-record-edit-additional-examiner-note.component.spec.ts b/src/app/features/tech-record/components/tech-record-edit-additional-examiner-note/tech-record-edit-additional-examiner-note.component.spec.ts index 6568ebf897..708979f9da 100644 --- a/src/app/features/tech-record/components/tech-record-edit-additional-examiner-note/tech-record-edit-additional-examiner-note.component.spec.ts +++ b/src/app/features/tech-record/components/tech-record-edit-additional-examiner-note/tech-record-edit-additional-examiner-note.component.spec.ts @@ -1,95 +1,95 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { DynamicFormsModule } from '@forms/dynamic-forms.module'; import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { ActivatedRoute, Router } from '@angular/router'; import { RouterTestingModule } from '@angular/router/testing'; -import { TechnicalRecordService } from '@services/technical-record/technical-record.service'; +import { GlobalErrorService } from '@core/components/global-error/global-error.service'; +import { DynamicFormsModule } from '@forms/dynamic-forms.module'; import { MockStore, provideMockStore } from '@ngrx/store/testing'; +import { TechnicalRecordService } from '@services/technical-record/technical-record.service'; import { initialAppState } from '@store/index'; -import { ActivatedRoute, Router } from '@angular/router'; import { of } from 'rxjs'; -import { GlobalErrorService } from '@core/components/global-error/global-error.service'; import { TechRecordEditAdditionalExaminerNoteComponent } from './tech-record-edit-additional-examiner-note.component'; const mockTechRecordService = { - techRecord$: jest.fn(), + techRecord$: jest.fn(), }; describe('TechRecordEditAdditionalExaminerNoteComponent', () => { - let fixture: ComponentFixture; - let component: TechRecordEditAdditionalExaminerNoteComponent; - let router: Router; - let errorService: GlobalErrorService; - let route: ActivatedRoute; - let store: MockStore; + let fixture: ComponentFixture; + let component: TechRecordEditAdditionalExaminerNoteComponent; + let router: Router; + let errorService: GlobalErrorService; + let route: ActivatedRoute; + let store: MockStore; - beforeEach(async () => { - await TestBed.configureTestingModule({ - declarations: [TechRecordEditAdditionalExaminerNoteComponent], - imports: [DynamicFormsModule, FormsModule, ReactiveFormsModule, RouterTestingModule], - providers: [ - { provide: TechnicalRecordService, useValue: mockTechRecordService }, - provideMockStore({ initialState: initialAppState }), - { provide: ActivatedRoute, useValue: { params: of([{ id: 1 }]) } }, - ], - }).compileComponents(); - fixture = TestBed.createComponent(TechRecordEditAdditionalExaminerNoteComponent); - component = fixture.componentInstance; - router = TestBed.inject(Router); - errorService = TestBed.inject(GlobalErrorService); - route = TestBed.inject(ActivatedRoute); - store = TestBed.inject(MockStore); - }); - it('should create', () => { - expect(component).toBeTruthy(); - }); - describe('ngOnInit', () => { - it('should call all initialisation functions', () => { - const examinerNoteSpy = jest.spyOn(component, 'getExaminerNote').mockReturnValue(); - const techRecordSpy = jest.spyOn(component, 'getTechRecord').mockReturnValue(); - const formSpy = jest.spyOn(component, 'setupForm').mockReturnValue(); - component.ngOnInit(); - expect(examinerNoteSpy).toHaveBeenCalled(); - expect(formSpy).toHaveBeenCalled(); - expect(techRecordSpy).toHaveBeenCalled(); - }); - }); - describe('navigateBack', () => { - it('should clear all errors', () => { - jest.spyOn(router, 'navigate').mockImplementation(); + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [TechRecordEditAdditionalExaminerNoteComponent], + imports: [DynamicFormsModule, FormsModule, ReactiveFormsModule, RouterTestingModule], + providers: [ + { provide: TechnicalRecordService, useValue: mockTechRecordService }, + provideMockStore({ initialState: initialAppState }), + { provide: ActivatedRoute, useValue: { params: of([{ id: 1 }]) } }, + ], + }).compileComponents(); + fixture = TestBed.createComponent(TechRecordEditAdditionalExaminerNoteComponent); + component = fixture.componentInstance; + router = TestBed.inject(Router); + errorService = TestBed.inject(GlobalErrorService); + route = TestBed.inject(ActivatedRoute); + store = TestBed.inject(MockStore); + }); + it('should create', () => { + expect(component).toBeTruthy(); + }); + describe('ngOnInit', () => { + it('should call all initialisation functions', () => { + const examinerNoteSpy = jest.spyOn(component, 'getExaminerNote').mockReturnValue(); + const techRecordSpy = jest.spyOn(component, 'getTechRecord').mockReturnValue(); + const formSpy = jest.spyOn(component, 'setupForm').mockReturnValue(); + component.ngOnInit(); + expect(examinerNoteSpy).toHaveBeenCalled(); + expect(formSpy).toHaveBeenCalled(); + expect(techRecordSpy).toHaveBeenCalled(); + }); + }); + describe('navigateBack', () => { + it('should clear all errors', () => { + jest.spyOn(router, 'navigate').mockImplementation(); - const clearErrorsSpy = jest.spyOn(errorService, 'clearErrors'); + const clearErrorsSpy = jest.spyOn(errorService, 'clearErrors'); - component.navigateBack(); + component.navigateBack(); - expect(clearErrorsSpy).toHaveBeenCalledTimes(1); - }); + expect(clearErrorsSpy).toHaveBeenCalledTimes(1); + }); - it('should navigate back to the previous page', () => { - const navigateSpy = jest.spyOn(router, 'navigate').mockImplementation(() => Promise.resolve(true)); + it('should navigate back to the previous page', () => { + const navigateSpy = jest.spyOn(router, 'navigate').mockImplementation(() => Promise.resolve(true)); - component.navigateBack(); + component.navigateBack(); - expect(navigateSpy).toHaveBeenCalledWith(['../../'], { relativeTo: route }); - }); - }); - describe('handleSubmit', () => { - it('should not dispatch an action if the notes are the same', () => { - const storeSpy = jest.spyOn(store, 'dispatch'); - const navigateBackSpy = jest.spyOn(component, 'navigateBack').mockReturnValue(); - component.originalExaminerNote = 'foobar'; - component.editedExaminerNote = 'foobar'; - component.handleSubmit(); - expect(storeSpy).not.toHaveBeenCalled(); - expect(navigateBackSpy).toHaveBeenCalled(); - }); + expect(navigateSpy).toHaveBeenCalledWith(['../../'], { relativeTo: route }); + }); + }); + describe('handleSubmit', () => { + it('should not dispatch an action if the notes are the same', () => { + const storeSpy = jest.spyOn(store, 'dispatch'); + const navigateBackSpy = jest.spyOn(component, 'navigateBack').mockReturnValue(); + component.originalExaminerNote = 'foobar'; + component.editedExaminerNote = 'foobar'; + component.handleSubmit(); + expect(storeSpy).not.toHaveBeenCalled(); + expect(navigateBackSpy).toHaveBeenCalled(); + }); - it('should dispatch an action if the notes are not the same', () => { - const storeSpy = jest.spyOn(store, 'dispatch'); - const navigateBackSpy = jest.spyOn(component, 'navigateBack').mockReturnValue(); - component.originalExaminerNote = 'foo'; - component.editedExaminerNote = 'bar'; - component.handleSubmit(); - expect(storeSpy).toHaveBeenCalled(); - expect(navigateBackSpy).toHaveBeenCalled(); - }); - }); + it('should dispatch an action if the notes are not the same', () => { + const storeSpy = jest.spyOn(store, 'dispatch'); + const navigateBackSpy = jest.spyOn(component, 'navigateBack').mockReturnValue(); + component.originalExaminerNote = 'foo'; + component.editedExaminerNote = 'bar'; + component.handleSubmit(); + expect(storeSpy).toHaveBeenCalled(); + expect(navigateBackSpy).toHaveBeenCalled(); + }); + }); }); diff --git a/src/app/features/tech-record/components/tech-record-edit-additional-examiner-note/tech-record-edit-additional-examiner-note.component.ts b/src/app/features/tech-record/components/tech-record-edit-additional-examiner-note/tech-record-edit-additional-examiner-note.component.ts index 11e117cea6..4ac33b1d16 100644 --- a/src/app/features/tech-record/components/tech-record-edit-additional-examiner-note/tech-record-edit-additional-examiner-note.component.ts +++ b/src/app/features/tech-record/components/tech-record-edit-additional-examiner-note/tech-record-edit-additional-examiner-note.component.ts @@ -1,108 +1,107 @@ import { Component, OnInit } from '@angular/core'; +import { FormGroup, Validators } from '@angular/forms'; import { ActivatedRoute, Router } from '@angular/router'; -import { TechnicalRecordService } from '@services/technical-record/technical-record.service'; -import { TechRecordType } from '@dvsa/cvs-type-definitions/types/v3/tech-record/tech-record-vehicle-type'; -import { ReplaySubject, take, takeUntil } from 'rxjs'; import { GlobalErrorService } from '@core/components/global-error/global-error.service'; -import { - CustomFormControl, - FormNodeEditTypes, - FormNodeTypes, - FormNodeWidth, -} from '@forms/services/dynamic-form.types'; -import { FormGroup, Validators } from '@angular/forms'; +import { AdditionalExaminerNotes } from '@dvsa/cvs-type-definitions/types/v3/tech-record/get/hgv/complete'; +import { TechRecordType } from '@dvsa/cvs-type-definitions/types/v3/tech-record/tech-record-vehicle-type'; +import { CustomFormControl, FormNodeEditTypes, FormNodeTypes, FormNodeWidth } from '@forms/services/dynamic-form.types'; import { Store } from '@ngrx/store'; +import { TechnicalRecordService } from '@services/technical-record/technical-record.service'; import { State } from '@store/index'; import { updateExistingADRAdditionalExaminerNote } from '@store/technical-records'; -import { AdditionalExaminerNotes } from '@dvsa/cvs-type-definitions/types/v3/tech-record/get/hgv/complete'; +import { ReplaySubject, take, takeUntil } from 'rxjs'; @Component({ - selector: 'tech-record-edit-additional-examiner-note', - templateUrl: './tech-record-edit-additional-examiner-note.component.html', - styleUrls: ['./tech-record-edit-additional-examiner-note.component.scss'], + selector: 'tech-record-edit-additional-examiner-note', + templateUrl: './tech-record-edit-additional-examiner-note.component.html', + styleUrls: ['./tech-record-edit-additional-examiner-note.component.scss'], }) export class TechRecordEditAdditionalExaminerNoteComponent implements OnInit { - currentTechRecord!: TechRecordType<'hgv' | 'trl' | 'lgv'>; - examinerNoteIndex!: number; - editedExaminerNote: string = ''; - originalExaminerNote: string = ''; - examinerNoteObj!: AdditionalExaminerNotes; - destroy$ = new ReplaySubject(1); - form!: FormGroup; - formControl!: CustomFormControl; - - constructor( - private router: Router, - private route: ActivatedRoute, - private technicalRecordService: TechnicalRecordService, - private globalErrorService: GlobalErrorService, - private store: Store, - ) { } + currentTechRecord!: TechRecordType<'hgv' | 'trl' | 'lgv'>; + examinerNoteIndex!: number; + editedExaminerNote = ''; + originalExaminerNote = ''; + examinerNoteObj!: AdditionalExaminerNotes; + destroy$ = new ReplaySubject(1); + form!: FormGroup; + formControl!: CustomFormControl; - ngOnInit() { - this.getTechRecord(); - this.getExaminerNote(); - this.setupForm(); - } + constructor( + private router: Router, + private route: ActivatedRoute, + private technicalRecordService: TechnicalRecordService, + private globalErrorService: GlobalErrorService, + private store: Store + ) {} - getTechRecord() { - this.technicalRecordService.techRecord$.pipe(takeUntil(this.destroy$)).subscribe((currentTechRecord) => { - this.currentTechRecord = currentTechRecord as TechRecordType<'hgv' | 'lgv' | 'trl'>; - }); - } + ngOnInit() { + this.getTechRecord(); + this.getExaminerNote(); + this.setupForm(); + } - getExaminerNote() { - this.route.params.pipe(take(1)).subscribe((params) => { - this.examinerNoteIndex = params['examinerNoteIndex']; - }); - const additionalExaminerNotes = this.currentTechRecord?.techRecord_adrDetails_additionalExaminerNotes; - if (additionalExaminerNotes) { - const examinerNote = additionalExaminerNotes[this.examinerNoteIndex].note; - if (examinerNote) { - this.examinerNoteObj = additionalExaminerNotes[this.examinerNoteIndex]; - this.originalExaminerNote = examinerNote; - this.editedExaminerNote = examinerNote; - } - } - } + getTechRecord() { + this.technicalRecordService.techRecord$.pipe(takeUntil(this.destroy$)).subscribe((currentTechRecord) => { + this.currentTechRecord = currentTechRecord as TechRecordType<'hgv' | 'lgv' | 'trl'>; + }); + } - setupForm() { - this.formControl = new CustomFormControl({ - name: 'additionalExaminerNote', type: FormNodeTypes.CONTROL, - }, '', [Validators.required]); - this.form = new FormGroup({ - additionalExaminerNote: this.formControl, - }); - this.formControl.patchValue(this.editedExaminerNote); - } + getExaminerNote() { + this.route.params.pipe(take(1)).subscribe((params) => { + this.examinerNoteIndex = params['examinerNoteIndex']; + }); + const additionalExaminerNotes = this.currentTechRecord?.techRecord_adrDetails_additionalExaminerNotes; + if (additionalExaminerNotes) { + const examinerNote = additionalExaminerNotes[this.examinerNoteIndex].note; + if (examinerNote) { + this.examinerNoteObj = additionalExaminerNotes[this.examinerNoteIndex]; + this.originalExaminerNote = examinerNote; + this.editedExaminerNote = examinerNote; + } + } + } - navigateBack() { - this.globalErrorService.clearErrors(); - void this.router.navigate(['../../'], { relativeTo: this.route }); - } + setupForm() { + this.formControl = new CustomFormControl( + { + name: 'additionalExaminerNote', + type: FormNodeTypes.CONTROL, + }, + '', + [Validators.required] + ); + this.form = new FormGroup({ + additionalExaminerNote: this.formControl, + }); + this.formControl.patchValue(this.editedExaminerNote); + } - handleSubmit(): void { - if (this.originalExaminerNote !== this.editedExaminerNote) { - this.store.dispatch( - updateExistingADRAdditionalExaminerNote({ - examinerNoteIndex: this.examinerNoteIndex, - additionalExaminerNote: this.editedExaminerNote, - }), - ); - } - this.navigateBack(); - } + navigateBack() { + this.globalErrorService.clearErrors(); + void this.router.navigate(['../../'], { relativeTo: this.route }); + } - ngOnChanges(examinerNote: string) { - this.editedExaminerNote = examinerNote; - } + handleSubmit(): void { + if (this.originalExaminerNote !== this.editedExaminerNote) { + this.store.dispatch( + updateExistingADRAdditionalExaminerNote({ + examinerNoteIndex: this.examinerNoteIndex, + additionalExaminerNote: this.editedExaminerNote, + }) + ); + } + this.navigateBack(); + } - get editTypes(): typeof FormNodeEditTypes { - return FormNodeEditTypes; - } + ngOnChanges(examinerNote: string) { + this.editedExaminerNote = examinerNote; + } - get width(): typeof FormNodeWidth { - return FormNodeWidth; - } + get editTypes(): typeof FormNodeEditTypes { + return FormNodeEditTypes; + } + get width(): typeof FormNodeWidth { + return FormNodeWidth; + } } diff --git a/src/app/features/tech-record/components/tech-record-generate-letter/tech-record-generate-letter.component.spec.ts b/src/app/features/tech-record/components/tech-record-generate-letter/tech-record-generate-letter.component.spec.ts index 7c26c510b7..a93992cbfc 100644 --- a/src/app/features/tech-record/components/tech-record-generate-letter/tech-record-generate-letter.component.spec.ts +++ b/src/app/features/tech-record/components/tech-record-generate-letter/tech-record-generate-letter.component.spec.ts @@ -21,137 +21,143 @@ import { ReplaySubject, of } from 'rxjs'; import { GenerateLetterComponent } from './tech-record-generate-letter.component'; const mockTechRecordService = { - get techRecord$() { - return of(mockVehicleTechnicalRecord('trl')); - }, - updateEditingTechRecord: jest.fn(), - isUnique: jest.fn(), + get techRecord$() { + return of(mockVehicleTechnicalRecord('trl')); + }, + updateEditingTechRecord: jest.fn(), + isUnique: jest.fn(), }; const mockDynamicFormService = { - createForm: jest.fn(), + createForm: jest.fn(), }; describe('TechRecordGenerateLetterComponent', () => { - const actions$ = new ReplaySubject(); - let component: GenerateLetterComponent; - let errorService: GlobalErrorService; - let expectedVehicle = {} as TechRecordType<'trl'>; - let fixture: ComponentFixture; - let route: ActivatedRoute; - let router: Router; - let store: MockStore; - - beforeEach(async () => { - await TestBed.configureTestingModule({ - declarations: [GenerateLetterComponent], - providers: [ - GlobalErrorService, - provideMockActions(() => actions$), - provideMockStore({ initialState: initialAppState }), - { provide: ActivatedRoute, useValue: { params: of([{ id: 1 }]) } }, - { provide: DynamicFormService, useValue: mockDynamicFormService }, - { provide: TechnicalRecordService, useValue: mockTechRecordService }, - { - provide: UserService, - useValue: { - roles$: of(['TechRecord.Amend']), - }, - }, - ], - imports: [RouterTestingModule, SharedModule, ReactiveFormsModule, DynamicFormsModule, FixNavigationTriggeredOutsideAngularZoneNgModule], - }).compileComponents(); - }); - - beforeEach(() => { - fixture = TestBed.createComponent(GenerateLetterComponent); - errorService = TestBed.inject(GlobalErrorService); - route = TestBed.inject(ActivatedRoute); - router = TestBed.inject(Router); - store = TestBed.inject(MockStore); - component = fixture.componentInstance; - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); - - describe('navigateBack', () => { - it('should clear all errors', () => { - jest.spyOn(router, 'navigate').mockImplementation(); - - const clearErrorsSpy = jest.spyOn(errorService, 'clearErrors'); - - component.navigateBack(); - - expect(clearErrorsSpy).toHaveBeenCalledTimes(1); - }); - - it('should navigate back to the previous page', () => { - const navigateSpy = jest.spyOn(router, 'navigate').mockImplementation(() => Promise.resolve(true)); - - component.navigateBack(); - - expect(navigateSpy).toHaveBeenCalledWith(['..'], { relativeTo: route }); - }); - - it('should navigate back on generateLetterSuccess', () => { - const navigateBackSpy = jest.spyOn(router, 'navigate').mockImplementation(() => Promise.resolve(true)); - - component.ngOnInit(); - - actions$.next(generateLetterSuccess()); - - expect(navigateBackSpy).toHaveBeenCalled(); - }); - }); - - describe('handleSubmit', () => { - beforeEach(() => { - expectedVehicle = mockVehicleTechnicalRecord('trl') as TechRecordType<'trl'>; - }); - - it('should add an error when the field is not filled out', () => { - const addErrorSpy = jest.spyOn(errorService, 'addError'); - - component.handleSubmit(); - - expect(addErrorSpy).toHaveBeenCalledWith({ error: 'Letter type is required', anchorLink: 'letterType' }); - }); - - describe('it should dispatch the generateLetter action with the correct paragraphIds', () => { - it('should dispatch with id 3 on acceptance', () => { - const dispatchSpy = jest.spyOn(store, 'dispatch'); - component.techRecord = expectedVehicle; - component.techRecord.techRecord_approvalType = ApprovalType.UKNI_WVTA; - - component.form.get('letterType')?.setValue('trailer acceptance'); - component.handleSubmit(); - - expect(dispatchSpy).toHaveBeenCalledWith(generateLetter({ letterType: 'trailer acceptance', paragraphId: 3 })); - }); - - it('should dispatch with id 4 on rejection', () => { - const dispatchSpy = jest.spyOn(store, 'dispatch'); - component.techRecord = expectedVehicle; - component.techRecord.techRecord_approvalType = ApprovalType.GB_WVTA; - - component.form.get('letterType')?.setValue('trailer rejection'); - component.handleSubmit(); - - expect(dispatchSpy).toHaveBeenCalledWith(generateLetter({ letterType: 'trailer rejection', paragraphId: 4 })); - }); - - it('should dispatch with id 6 on acceptance', () => { - const dispatchSpy = jest.spyOn(store, 'dispatch'); - component.techRecord = expectedVehicle; - component.techRecord.techRecord_approvalType = ApprovalType.GB_WVTA; - - component.form.get('letterType')?.setValue('trailer acceptance'); - component.handleSubmit(); + const actions$ = new ReplaySubject(); + let component: GenerateLetterComponent; + let errorService: GlobalErrorService; + let expectedVehicle = {} as TechRecordType<'trl'>; + let fixture: ComponentFixture; + let route: ActivatedRoute; + let router: Router; + let store: MockStore; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [GenerateLetterComponent], + providers: [ + GlobalErrorService, + provideMockActions(() => actions$), + provideMockStore({ initialState: initialAppState }), + { provide: ActivatedRoute, useValue: { params: of([{ id: 1 }]) } }, + { provide: DynamicFormService, useValue: mockDynamicFormService }, + { provide: TechnicalRecordService, useValue: mockTechRecordService }, + { + provide: UserService, + useValue: { + roles$: of(['TechRecord.Amend']), + }, + }, + ], + imports: [ + RouterTestingModule, + SharedModule, + ReactiveFormsModule, + DynamicFormsModule, + FixNavigationTriggeredOutsideAngularZoneNgModule, + ], + }).compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(GenerateLetterComponent); + errorService = TestBed.inject(GlobalErrorService); + route = TestBed.inject(ActivatedRoute); + router = TestBed.inject(Router); + store = TestBed.inject(MockStore); + component = fixture.componentInstance; + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + describe('navigateBack', () => { + it('should clear all errors', () => { + jest.spyOn(router, 'navigate').mockImplementation(); + + const clearErrorsSpy = jest.spyOn(errorService, 'clearErrors'); + + component.navigateBack(); + + expect(clearErrorsSpy).toHaveBeenCalledTimes(1); + }); + + it('should navigate back to the previous page', () => { + const navigateSpy = jest.spyOn(router, 'navigate').mockImplementation(() => Promise.resolve(true)); + + component.navigateBack(); + + expect(navigateSpy).toHaveBeenCalledWith(['..'], { relativeTo: route }); + }); + + it('should navigate back on generateLetterSuccess', () => { + const navigateBackSpy = jest.spyOn(router, 'navigate').mockImplementation(() => Promise.resolve(true)); + + component.ngOnInit(); + + actions$.next(generateLetterSuccess()); + + expect(navigateBackSpy).toHaveBeenCalled(); + }); + }); + + describe('handleSubmit', () => { + beforeEach(() => { + expectedVehicle = mockVehicleTechnicalRecord('trl') as TechRecordType<'trl'>; + }); + + it('should add an error when the field is not filled out', () => { + const addErrorSpy = jest.spyOn(errorService, 'addError'); + + component.handleSubmit(); + + expect(addErrorSpy).toHaveBeenCalledWith({ error: 'Letter type is required', anchorLink: 'letterType' }); + }); + + describe('it should dispatch the generateLetter action with the correct paragraphIds', () => { + it('should dispatch with id 3 on acceptance', () => { + const dispatchSpy = jest.spyOn(store, 'dispatch'); + component.techRecord = expectedVehicle; + component.techRecord.techRecord_approvalType = ApprovalType.UKNI_WVTA; + + component.form.get('letterType')?.setValue('trailer acceptance'); + component.handleSubmit(); + + expect(dispatchSpy).toHaveBeenCalledWith(generateLetter({ letterType: 'trailer acceptance', paragraphId: 3 })); + }); + + it('should dispatch with id 4 on rejection', () => { + const dispatchSpy = jest.spyOn(store, 'dispatch'); + component.techRecord = expectedVehicle; + component.techRecord.techRecord_approvalType = ApprovalType.GB_WVTA; + + component.form.get('letterType')?.setValue('trailer rejection'); + component.handleSubmit(); + + expect(dispatchSpy).toHaveBeenCalledWith(generateLetter({ letterType: 'trailer rejection', paragraphId: 4 })); + }); + + it('should dispatch with id 6 on acceptance', () => { + const dispatchSpy = jest.spyOn(store, 'dispatch'); + component.techRecord = expectedVehicle; + component.techRecord.techRecord_approvalType = ApprovalType.GB_WVTA; + + component.form.get('letterType')?.setValue('trailer acceptance'); + component.handleSubmit(); - expect(dispatchSpy).toHaveBeenCalledWith(generateLetter({ letterType: 'trailer acceptance', paragraphId: 6 })); - }); - }); - }); + expect(dispatchSpy).toHaveBeenCalledWith(generateLetter({ letterType: 'trailer acceptance', paragraphId: 6 })); + }); + }); + }); }); diff --git a/src/app/features/tech-record/components/tech-record-generate-letter/tech-record-generate-letter.component.ts b/src/app/features/tech-record/components/tech-record-generate-letter/tech-record-generate-letter.component.ts index 69480b088b..0161005f92 100644 --- a/src/app/features/tech-record/components/tech-record-generate-letter/tech-record-generate-letter.component.ts +++ b/src/app/features/tech-record/components/tech-record-generate-letter/tech-record-generate-letter.component.ts @@ -5,9 +5,7 @@ import { GlobalErrorService } from '@core/components/global-error/global-error.s import { ApprovalType } from '@dvsa/cvs-type-definitions/types/v3/tech-record/enums/approvalType.enum.js'; import { TechRecordType } from '@dvsa/cvs-type-definitions/types/v3/tech-record/tech-record-vehicle-type'; import { DynamicFormService } from '@forms/services/dynamic-form.service'; -import { - CustomFormControl, FormNodeOption, FormNodeTypes, FormNodeWidth, -} from '@forms/services/dynamic-form.types'; +import { CustomFormControl, FormNodeOption, FormNodeTypes, FormNodeWidth } from '@forms/services/dynamic-form.types'; import { LETTER_TYPES } from '@forms/templates/general/letter-types'; import { StatusCodes, V3TechRecordModel } from '@models/vehicle-tech-record.model'; import { Actions, ofType } from '@ngrx/effects'; @@ -19,84 +17,87 @@ import { TechnicalRecordServiceState } from '@store/technical-records/reducers/t import { take } from 'rxjs'; @Component({ - selector: 'app-generate-letter', - templateUrl: './tech-record-generate-letter.component.html', - styleUrls: ['./tech-record-generate-letter.component.scss'], + selector: 'app-generate-letter', + templateUrl: './tech-record-generate-letter.component.html', + styleUrls: ['./tech-record-generate-letter.component.scss'], }) export class GenerateLetterComponent implements OnInit { - techRecord?: V3TechRecordModel; - form = new FormGroup({ - letterType: new CustomFormControl({ name: 'letterType', label: 'Type of letter to generate', type: FormNodeTypes.CONTROL }, '', [ - Validators.required, - ]), - }); - width: FormNodeWidth = FormNodeWidth.L; + techRecord?: V3TechRecordModel; + form = new FormGroup({ + letterType: new CustomFormControl( + { name: 'letterType', label: 'Type of letter to generate', type: FormNodeTypes.CONTROL }, + '', + [Validators.required] + ), + }); + width: FormNodeWidth = FormNodeWidth.L; - paragraphMap = new Map([ - [ApprovalType.GB_WVTA, 6], - [ApprovalType.UKNI_WVTA, 3], - [ApprovalType.EU_WVTA_PRE_23, 3], - [ApprovalType.EU_WVTA_23_ON, 7], - [ApprovalType.QNIG, 3], - [ApprovalType.PROV_GB_WVTA, 3], - [ApprovalType.SMALL_SERIES_NKS, 3], - [ApprovalType.SMALL_SERIES_NKSXX, 3], - [ApprovalType.IVA_VCA, 3], - [ApprovalType.IVA_DVSA_NI, 3], - ]); + paragraphMap = new Map([ + [ApprovalType.GB_WVTA, 6], + [ApprovalType.UKNI_WVTA, 3], + [ApprovalType.EU_WVTA_PRE_23, 3], + [ApprovalType.EU_WVTA_23_ON, 7], + [ApprovalType.QNIG, 3], + [ApprovalType.PROV_GB_WVTA, 3], + [ApprovalType.SMALL_SERIES_NKS, 3], + [ApprovalType.SMALL_SERIES_NKSXX, 3], + [ApprovalType.IVA_VCA, 3], + [ApprovalType.IVA_DVSA_NI, 3], + ]); - constructor( - private actions$: Actions, - public dfs: DynamicFormService, - private globalErrorService: GlobalErrorService, - private route: ActivatedRoute, - private router: Router, - private store: Store, - private technicalRecordService: TechnicalRecordService, - public userService: UserService, - ) {} + constructor( + private actions$: Actions, + public dfs: DynamicFormService, + private globalErrorService: GlobalErrorService, + private route: ActivatedRoute, + private router: Router, + private store: Store, + private technicalRecordService: TechnicalRecordService, + public userService: UserService + ) {} - ngOnInit(): void { - this.technicalRecordService.techRecord$.pipe(take(1)).subscribe((techRecord) => { - this.techRecord = techRecord; - }); - this.actions$.pipe(ofType(generateLetterSuccess), take(1)).subscribe(() => this.navigateBack()); - } + ngOnInit(): void { + this.technicalRecordService.techRecord$.pipe(take(1)).subscribe((techRecord) => { + this.techRecord = techRecord; + }); + this.actions$.pipe(ofType(generateLetterSuccess), take(1)).subscribe(() => this.navigateBack()); + } - get reasons(): Array> { - if (this.techRecord?.techRecord_statusCode === StatusCodes.PROVISIONAL) { - return [ - { label: 'Trailer rejected', value: LETTER_TYPES[1].value }, - ]; - } - return [ - { label: 'Trailer accepted', value: LETTER_TYPES[0].value }, - { label: 'Trailer rejected', value: LETTER_TYPES[1].value }, - ]; - } + get reasons(): Array> { + if (this.techRecord?.techRecord_statusCode === StatusCodes.PROVISIONAL) { + return [{ label: 'Trailer rejected', value: LETTER_TYPES[1].value }]; + } + return [ + { label: 'Trailer accepted', value: LETTER_TYPES[0].value }, + { label: 'Trailer rejected', value: LETTER_TYPES[1].value }, + ]; + } - get emailAddress(): string | undefined { - return this.techRecord?.techRecord_vehicleType === 'trl' ? this.techRecord?.techRecord_applicantDetails_emailAddress ?? '' : undefined; - } + get emailAddress(): string | undefined { + return this.techRecord?.techRecord_vehicleType === 'trl' + ? this.techRecord?.techRecord_applicantDetails_emailAddress ?? '' + : undefined; + } - navigateBack() { - this.globalErrorService.clearErrors(); - void this.router.navigate(['..'], { relativeTo: this.route }); - } + navigateBack() { + this.globalErrorService.clearErrors(); + void this.router.navigate(['..'], { relativeTo: this.route }); + } - handleSubmit(): void { - this.globalErrorService.clearErrors(); - if (this.form.value.letterType === '') { - return this.globalErrorService.addError({ error: 'Letter type is required', anchorLink: 'letterType' }); - } - if (!this.techRecord) { - return this.globalErrorService.addError({ error: 'Could not retrieve current technical record' }); - } + handleSubmit(): void { + this.globalErrorService.clearErrors(); + if (this.form.value.letterType === '') { + return this.globalErrorService.addError({ error: 'Letter type is required', anchorLink: 'letterType' }); + } + if (!this.techRecord) { + return this.globalErrorService.addError({ error: 'Could not retrieve current technical record' }); + } - const paragraphId = this.form.value.letterType === 'trailer acceptance' - ? this.paragraphMap.get((this.techRecord as TechRecordType<'trl'>).techRecord_approvalType as ApprovalType) - : 4; + const paragraphId = + this.form.value.letterType === 'trailer acceptance' + ? this.paragraphMap.get((this.techRecord as TechRecordType<'trl'>).techRecord_approvalType as ApprovalType) + : 4; - this.store.dispatch(generateLetter({ letterType: this.form.value.letterType, paragraphId: paragraphId ?? 4 })); - } + this.store.dispatch(generateLetter({ letterType: this.form.value.letterType, paragraphId: paragraphId ?? 4 })); + } } diff --git a/src/app/features/tech-record/components/tech-record-generate-plate/tech-record-generate-plate.component.spec.ts b/src/app/features/tech-record/components/tech-record-generate-plate/tech-record-generate-plate.component.spec.ts index 69ebe19da4..82c3c56133 100644 --- a/src/app/features/tech-record/components/tech-record-generate-plate/tech-record-generate-plate.component.spec.ts +++ b/src/app/features/tech-record/components/tech-record-generate-plate/tech-record-generate-plate.component.spec.ts @@ -1,7 +1,5 @@ import { HttpClientTestingModule } from '@angular/common/http/testing'; -import { - ComponentFixture, fakeAsync, TestBed, tick, -} from '@angular/core/testing'; +import { ComponentFixture, TestBed, fakeAsync, tick } from '@angular/core/testing'; import { ReactiveFormsModule } from '@angular/forms'; import { ActivatedRoute, Router } from '@angular/router'; import { RouterTestingModule } from '@angular/router/testing'; @@ -17,121 +15,124 @@ import { UserService } from '@services/user-service/user-service'; import { SharedModule } from '@shared/shared.module'; import { initialAppState } from '@store/index'; import { generatePlate, generatePlateSuccess } from '@store/technical-records'; -import { of, ReplaySubject } from 'rxjs'; +import { ReplaySubject, of } from 'rxjs'; import { GeneratePlateComponent } from './tech-record-generate-plate.component'; const mockDynamicFormService = { - createForm: jest.fn(), + createForm: jest.fn(), }; describe('TechRecordGeneratePlateComponent', () => { - const actions$ = new ReplaySubject(); - let component: GeneratePlateComponent; - let errorService: GlobalErrorService; - let fixture: ComponentFixture; - let route: ActivatedRoute; - let router: Router; - let store: MockStore; - let technicalRecordService: TechnicalRecordService; - - beforeEach(async () => { - await TestBed.configureTestingModule({ - declarations: [GeneratePlateComponent], - providers: [ - GlobalErrorService, - provideMockActions(() => actions$), - provideMockStore({ initialState: initialAppState }), - { provide: ActivatedRoute, useValue: { params: of([{ id: 1 }]) } }, - { provide: DynamicFormService, useValue: mockDynamicFormService }, - TechnicalRecordService, - { - provide: UserService, - useValue: { - roles$: of(['TechRecord.Amend']), - }, - }, - ], - imports: [RouterTestingModule, SharedModule, ReactiveFormsModule, DynamicFormsModule, HttpClientTestingModule], - }).compileComponents(); - }); - - beforeEach(() => { - fixture = TestBed.createComponent(GeneratePlateComponent); - errorService = TestBed.inject(GlobalErrorService); - route = TestBed.inject(ActivatedRoute); - router = TestBed.inject(Router); - store = TestBed.inject(MockStore); - technicalRecordService = TestBed.inject(TechnicalRecordService); - component = fixture.componentInstance; - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); - - describe('navigateBack', () => { - beforeEach(() => { - jest - .spyOn(technicalRecordService, 'techRecord$', 'get') - .mockReturnValue(of({ systemNumber: 'foo', createdTimestamp: 'bar', vin: 'testVin' } as V3TechRecordModel)); - }); - it('should clear all errors', () => { - jest.spyOn(router, 'navigate').mockImplementation(); - - const clearErrorsSpy = jest.spyOn(errorService, 'clearErrors'); - - component.navigateBack(); - - expect(clearErrorsSpy).toHaveBeenCalledTimes(1); - }); - - it('should navigate back to the previous page', () => { - const navigateSpy = jest.spyOn(router, 'navigate').mockImplementation(() => Promise.resolve(true)); - - component.navigateBack(); - - expect(navigateSpy).toHaveBeenCalledWith(['..'], { relativeTo: route }); - }); - - it('should navigate back on generatePlateSuccess', fakeAsync(() => { - fixture.ngZone?.run(() => { - component.ngOnInit(); - component.form.get('reason')?.setValue('Provisional'); - - const navigateBackSpy = jest.spyOn(component, 'navigateBack').mockImplementation(); - - component.handleSubmit(); - - actions$.next(generatePlateSuccess()); - tick(); - - expect(navigateBackSpy).toHaveBeenCalled(); - }); - })); - }); - - describe('handleSubmit', () => { - beforeEach(() => { - jest - .spyOn(technicalRecordService, 'techRecord$', 'get') - .mockReturnValue(of({ systemNumber: 'foo', createdTimestamp: 'bar', vin: 'testVin' } as V3TechRecordModel)); - }); - it('should add an error when the field is not filled out', () => { - const addErrorSpy = jest.spyOn(errorService, 'addError'); - - component.handleSubmit(); - - expect(addErrorSpy).toHaveBeenCalledWith({ error: 'Reason for generating plate is required', anchorLink: 'reason' }); - }); - - it('should dispatch the generatePlate action', () => { - const dispatchSpy = jest.spyOn(store, 'dispatch'); - - component.form.get('reason')?.setValue('Provisional'); - - component.handleSubmit(); - - expect(dispatchSpy).toHaveBeenCalledWith(generatePlate({ reason: 'Provisional' })); - }); - }); + const actions$ = new ReplaySubject(); + let component: GeneratePlateComponent; + let errorService: GlobalErrorService; + let fixture: ComponentFixture; + let route: ActivatedRoute; + let router: Router; + let store: MockStore; + let technicalRecordService: TechnicalRecordService; + + beforeEach(() => { + TestBed.configureTestingModule({ + declarations: [GeneratePlateComponent], + providers: [ + GlobalErrorService, + provideMockActions(() => actions$), + provideMockStore({ initialState: initialAppState }), + { provide: ActivatedRoute, useValue: { params: of([{ id: 1 }]) } }, + { provide: DynamicFormService, useValue: mockDynamicFormService }, + TechnicalRecordService, + { + provide: UserService, + useValue: { + roles$: of(['TechRecord.Amend']), + }, + }, + ], + imports: [RouterTestingModule, SharedModule, ReactiveFormsModule, DynamicFormsModule, HttpClientTestingModule], + }).compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(GeneratePlateComponent); + errorService = TestBed.inject(GlobalErrorService); + route = TestBed.inject(ActivatedRoute); + router = TestBed.inject(Router); + store = TestBed.inject(MockStore); + technicalRecordService = TestBed.inject(TechnicalRecordService); + component = fixture.componentInstance; + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + describe('navigateBack', () => { + beforeEach(() => { + jest + .spyOn(technicalRecordService, 'techRecord$', 'get') + .mockReturnValue(of({ systemNumber: 'foo', createdTimestamp: 'bar', vin: 'testVin' } as V3TechRecordModel)); + }); + it('should clear all errors', () => { + jest.spyOn(router, 'navigate').mockImplementation(); + + const clearErrorsSpy = jest.spyOn(errorService, 'clearErrors'); + + component.navigateBack(); + + expect(clearErrorsSpy).toHaveBeenCalledTimes(1); + }); + + it('should navigate back to the previous page', () => { + const navigateSpy = jest.spyOn(router, 'navigate').mockImplementation(() => Promise.resolve(true)); + + component.navigateBack(); + + expect(navigateSpy).toHaveBeenCalledWith(['..'], { relativeTo: route }); + }); + + it('should navigate back on generatePlateSuccess', fakeAsync(() => { + fixture.ngZone?.run(() => { + component.ngOnInit(); + component.form.get('reason')?.setValue('Provisional'); + + const navigateBackSpy = jest.spyOn(component, 'navigateBack').mockImplementation(); + + component.handleSubmit(); + + actions$.next(generatePlateSuccess()); + tick(); + + expect(navigateBackSpy).toHaveBeenCalled(); + }); + })); + }); + + describe('handleSubmit', () => { + beforeEach(() => { + jest + .spyOn(technicalRecordService, 'techRecord$', 'get') + .mockReturnValue(of({ systemNumber: 'foo', createdTimestamp: 'bar', vin: 'testVin' } as V3TechRecordModel)); + }); + it('should add an error when the field is not filled out', () => { + const addErrorSpy = jest.spyOn(errorService, 'addError'); + + component.handleSubmit(); + + expect(addErrorSpy).toHaveBeenCalledWith({ + error: 'Reason for generating plate is required', + anchorLink: 'reason', + }); + }); + + it('should dispatch the generatePlate action', () => { + const dispatchSpy = jest.spyOn(store, 'dispatch'); + + component.form.get('reason')?.setValue('Provisional'); + + component.handleSubmit(); + + expect(dispatchSpy).toHaveBeenCalledWith(generatePlate({ reason: 'Provisional' })); + }); + }); }); diff --git a/src/app/features/tech-record/components/tech-record-generate-plate/tech-record-generate-plate.component.ts b/src/app/features/tech-record/components/tech-record-generate-plate/tech-record-generate-plate.component.ts index d1b87ba890..d329add78c 100644 --- a/src/app/features/tech-record/components/tech-record-generate-plate/tech-record-generate-plate.component.ts +++ b/src/app/features/tech-record/components/tech-record-generate-plate/tech-record-generate-plate.component.ts @@ -3,91 +3,97 @@ import { FormGroup, Validators } from '@angular/forms'; import { ActivatedRoute, Router } from '@angular/router'; import { PlatesInner } from '@api/vehicle'; import { GlobalErrorService } from '@core/components/global-error/global-error.service'; -import { - CustomFormControl, FormNodeOption, FormNodeTypes, FormNodeWidth, -} from '@forms/services/dynamic-form.types'; +import { CustomFormControl, FormNodeOption, FormNodeTypes, FormNodeWidth } from '@forms/services/dynamic-form.types'; import { Actions, ofType } from '@ngrx/effects'; import { Store, select } from '@ngrx/store'; import { TechnicalRecordService } from '@services/technical-record/technical-record.service'; import { UserService } from '@services/user-service/user-service'; import { State } from '@store/index'; import { - cannotGeneratePlate, generatePlate, generatePlateSuccess, getCanGeneratePlate, + cannotGeneratePlate, + generatePlate, + generatePlateSuccess, + getCanGeneratePlate, } from '@store/technical-records'; -import { - Observable, map, take, tap, -} from 'rxjs'; +import { Observable, map, take, tap } from 'rxjs'; @Component({ - selector: 'app-generate-plate', - templateUrl: './tech-record-generate-plate.component.html', - styleUrls: ['./tech-record-generate-plate.component.scss'], + selector: 'app-generate-plate', + templateUrl: './tech-record-generate-plate.component.html', + styleUrls: ['./tech-record-generate-plate.component.scss'], }) export class GeneratePlateComponent implements OnInit { - form = new FormGroup({ - reason: new CustomFormControl({ name: 'reason', label: 'Reason for generating plate', type: FormNodeTypes.CONTROL }, '', [Validators.required]), - }); + form = new FormGroup({ + reason: new CustomFormControl( + { name: 'reason', label: 'Reason for generating plate', type: FormNodeTypes.CONTROL }, + '', + [Validators.required] + ), + }); - emailAddress$?: Observable; + emailAddress$?: Observable; - constructor( - private actions$: Actions, - private globalErrorService: GlobalErrorService, - private route: ActivatedRoute, - private router: Router, - private store: Store, - public userService: UserService, - private technicalRecordService: TechnicalRecordService, - ) {} + constructor( + private actions$: Actions, + private globalErrorService: GlobalErrorService, + private route: ActivatedRoute, + private router: Router, + private store: Store, + public userService: UserService, + private technicalRecordService: TechnicalRecordService + ) {} - ngOnInit(): void { - this.actions$.pipe(ofType(generatePlateSuccess), take(1)).subscribe(() => { - this.navigateBack(); - }); - this.store.pipe(select(getCanGeneratePlate), take(1)).subscribe((canGeneratePlate) => { - if (!canGeneratePlate) { - this.navigateBack(); - } - }); - this.emailAddress$ = this.technicalRecordService.techRecord$.pipe( - tap((record) => { - if (record?.techRecord_vehicleType !== 'hgv' && record?.techRecord_vehicleType !== 'trl') this.navigateBack(); - }), - map((record) => { - if (record?.techRecord_vehicleType !== 'hgv' && record?.techRecord_vehicleType !== 'trl') { - return undefined; - } - return record?.techRecord_applicantDetails_emailAddress; - }), - ); - } + ngOnInit(): void { + this.actions$.pipe(ofType(generatePlateSuccess), take(1)).subscribe(() => { + this.navigateBack(); + }); + this.store.pipe(select(getCanGeneratePlate), take(1)).subscribe((canGeneratePlate) => { + if (!canGeneratePlate) { + this.navigateBack(); + } + }); + this.emailAddress$ = this.technicalRecordService.techRecord$.pipe( + tap((record) => { + if (record?.techRecord_vehicleType !== 'hgv' && record?.techRecord_vehicleType !== 'trl') this.navigateBack(); + }), + map((record) => { + if (record?.techRecord_vehicleType !== 'hgv' && record?.techRecord_vehicleType !== 'trl') { + return undefined; + } + return record?.techRecord_applicantDetails_emailAddress; + }) + ); + } - get width(): FormNodeWidth { - return FormNodeWidth.L; - } + get width(): FormNodeWidth { + return FormNodeWidth.L; + } - get reasons(): Array> { - return [ - { label: 'Free replacement', value: PlatesInner.PlateReasonForIssueEnum.FreeReplacement }, - { label: 'Replacement', value: PlatesInner.PlateReasonForIssueEnum.Replacement }, - { label: 'Destroyed', value: PlatesInner.PlateReasonForIssueEnum.Destroyed }, - { label: 'Provisional', value: PlatesInner.PlateReasonForIssueEnum.Provisional }, - { label: 'Original', value: PlatesInner.PlateReasonForIssueEnum.Original }, - { label: 'Manual', value: PlatesInner.PlateReasonForIssueEnum.Manual }, - ]; - } + get reasons(): Array> { + return [ + { label: 'Free replacement', value: PlatesInner.PlateReasonForIssueEnum.FreeReplacement }, + { label: 'Replacement', value: PlatesInner.PlateReasonForIssueEnum.Replacement }, + { label: 'Destroyed', value: PlatesInner.PlateReasonForIssueEnum.Destroyed }, + { label: 'Provisional', value: PlatesInner.PlateReasonForIssueEnum.Provisional }, + { label: 'Original', value: PlatesInner.PlateReasonForIssueEnum.Original }, + { label: 'Manual', value: PlatesInner.PlateReasonForIssueEnum.Manual }, + ]; + } - navigateBack() { - this.globalErrorService.clearErrors(); - void this.router.navigate(['..'], { relativeTo: this.route }); - } + navigateBack() { + this.globalErrorService.clearErrors(); + void this.router.navigate(['..'], { relativeTo: this.route }); + } - handleSubmit(): void { - this.globalErrorService.clearErrors(); - if (!this.form.value.reason) { - return this.globalErrorService.addError({ error: 'Reason for generating plate is required', anchorLink: 'reason' }); - } - this.store.dispatch(generatePlate({ reason: this.form.value.reason })); - this.store.dispatch(cannotGeneratePlate()); - } + handleSubmit(): void { + this.globalErrorService.clearErrors(); + if (!this.form.value.reason) { + return this.globalErrorService.addError({ + error: 'Reason for generating plate is required', + anchorLink: 'reason', + }); + } + this.store.dispatch(generatePlate({ reason: this.form.value.reason })); + this.store.dispatch(cannotGeneratePlate()); + } } diff --git a/src/app/features/tech-record/components/tech-record-history/tech-record-history.component.spec.ts b/src/app/features/tech-record/components/tech-record-history/tech-record-history.component.spec.ts index 2f3cdb7d9f..e2ce5f0833 100644 --- a/src/app/features/tech-record/components/tech-record-history/tech-record-history.component.spec.ts +++ b/src/app/features/tech-record/components/tech-record-history/tech-record-history.component.spec.ts @@ -9,24 +9,24 @@ import { initialAppState } from '@store/index'; import { TechRecordHistoryComponent } from './tech-record-history.component'; describe('TechRecordHistoryComponent', () => { - let component: TechRecordHistoryComponent; - let fixture: ComponentFixture; + let component: TechRecordHistoryComponent; + let fixture: ComponentFixture; - beforeEach(async () => { - await TestBed.configureTestingModule({ - declarations: [TechRecordHistoryComponent], - imports: [HttpClientTestingModule, RouterTestingModule, SharedModule], - providers: [TechnicalRecordService, provideMockStore({ initialState: initialAppState })], - }).compileComponents(); - }); + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [TechRecordHistoryComponent], + imports: [HttpClientTestingModule, RouterTestingModule, SharedModule], + providers: [TechnicalRecordService, provideMockStore({ initialState: initialAppState })], + }).compileComponents(); + }); - beforeEach(() => { - fixture = TestBed.createComponent(TechRecordHistoryComponent); - component = fixture.componentInstance; - component.currentTechRecord = { systemNumber: 'foo', createdTimestamp: 'bar', vin: 'testVin' } as V3TechRecordModel; - fixture.detectChanges(); - }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + beforeEach(() => { + fixture = TestBed.createComponent(TechRecordHistoryComponent); + component = fixture.componentInstance; + component.currentTechRecord = { systemNumber: 'foo', createdTimestamp: 'bar', vin: 'testVin' } as V3TechRecordModel; + fixture.detectChanges(); + }); + it('should create', () => { + expect(component).toBeTruthy(); + }); }); diff --git a/src/app/features/tech-record/components/tech-record-history/tech-record-history.component.ts b/src/app/features/tech-record/components/tech-record-history/tech-record-history.component.ts index af9b4dabc3..ab6448133a 100644 --- a/src/app/features/tech-record/components/tech-record-history/tech-record-history.component.ts +++ b/src/app/features/tech-record/components/tech-record-history/tech-record-history.component.ts @@ -1,6 +1,4 @@ -import { - ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnInit, -} from '@angular/core'; +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnInit } from '@angular/core'; import { TechRecordSearchSchema } from '@dvsa/cvs-type-definitions/types/v3/tech-record/get/search'; import { TechRecordType } from '@dvsa/cvs-type-definitions/types/v3/tech-record/tech-record-verb'; import { V3TechRecordModel } from '@models/vehicle-tech-record.model'; @@ -9,56 +7,61 @@ import { getBySystemNumber, selectTechRecordHistory } from '@store/technical-rec import { Observable, map } from 'rxjs'; @Component({ - selector: 'app-tech-record-history', - templateUrl: './tech-record-history.component.html', - changeDetection: ChangeDetectionStrategy.OnPush, - styleUrls: ['./tech-record-history.component.scss'], + selector: 'app-tech-record-history', + templateUrl: './tech-record-history.component.html', + changeDetection: ChangeDetectionStrategy.OnPush, + styleUrls: ['./tech-record-history.component.scss'], }) export class TechRecordHistoryComponent implements OnInit { - @Input() currentTechRecord?: V3TechRecordModel; + @Input() currentTechRecord?: V3TechRecordModel; - pageStart?: number; - pageEnd?: number; + pageStart?: number; + pageEnd?: number; - constructor(private cdr: ChangeDetectorRef, private store: Store) {} + constructor( + private cdr: ChangeDetectorRef, + private store: Store + ) {} - ngOnInit(): void { - if (this.currentTechRecord) { - this.store.dispatch(getBySystemNumber({ systemNumber: (this.currentTechRecord as TechRecordType<'get'>)?.systemNumber })); - } - } + ngOnInit(): void { + if (this.currentTechRecord) { + this.store.dispatch( + getBySystemNumber({ systemNumber: (this.currentTechRecord as TechRecordType<'get'>)?.systemNumber }) + ); + } + } - get techRecordHistory$() { - return this.store.select(selectTechRecordHistory); - } + get techRecordHistory$() { + return this.store.select(selectTechRecordHistory); + } - get techRecordHistoryPage$(): Observable { - return this.techRecordHistory$?.pipe(map((records) => records?.slice(this.pageStart, this.pageEnd) ?? [])); - } + get techRecordHistoryPage$(): Observable { + return this.techRecordHistory$?.pipe(map((records) => records?.slice(this.pageStart, this.pageEnd) ?? [])); + } - get numberOfRecords$(): Observable { - return this.techRecordHistory$?.pipe(map((records) => records?.length ?? 0)); - } + get numberOfRecords$(): Observable { + return this.techRecordHistory$?.pipe(map((records) => records?.length ?? 0)); + } - convertToUnix(date: Date): number { - return new Date(date).getTime(); - } + convertToUnix(date: Date): number { + return new Date(date).getTime(); + } - handlePaginationChange({ start, end }: { start: number; end: number }) { - this.pageStart = start; - this.pageEnd = end; - this.cdr.detectChanges(); - } + handlePaginationChange({ start, end }: { start: number; end: number }) { + this.pageStart = start; + this.pageEnd = end; + this.cdr.detectChanges(); + } - trackByFn(i: number, tr: TechRecordSearchSchema) { - return tr.createdTimestamp; - } + trackByFn(i: number, tr: TechRecordSearchSchema) { + return tr.createdTimestamp; + } - summaryLinkUrl(searchResult: TechRecordSearchSchema) { - return `/tech-records/${searchResult.systemNumber}/${searchResult.createdTimestamp}`; - } + summaryLinkUrl(searchResult: TechRecordSearchSchema) { + return `/tech-records/${searchResult.systemNumber}/${searchResult.createdTimestamp}`; + } - get currentTimeStamp() { - return (this.currentTechRecord as TechRecordType<'get'>).createdTimestamp; - } + get currentTimeStamp() { + return (this.currentTechRecord as TechRecordType<'get'>).createdTimestamp; + } } diff --git a/src/app/features/tech-record/components/tech-record-search-tyres/tech-record-search-tyres.component.spec.ts b/src/app/features/tech-record/components/tech-record-search-tyres/tech-record-search-tyres.component.spec.ts index df6d8a2d23..f3a8ec16db 100644 --- a/src/app/features/tech-record/components/tech-record-search-tyres/tech-record-search-tyres.component.spec.ts +++ b/src/app/features/tech-record/components/tech-record-search-tyres/tech-record-search-tyres.component.spec.ts @@ -1,7 +1,5 @@ import { HttpClientTestingModule } from '@angular/common/http/testing'; -import { - ComponentFixture, TestBed, fakeAsync, tick, -} from '@angular/core/testing'; +import { ComponentFixture, TestBed, fakeAsync, tick } from '@angular/core/testing'; import { ActivatedRoute, Router } from '@angular/router'; import { RouterTestingModule } from '@angular/router/testing'; import { GlobalError } from '@core/components/global-error/global-error.interface'; @@ -24,236 +22,250 @@ import { Observable, ReplaySubject, of } from 'rxjs'; import { TechRecordSearchTyresComponent } from './tech-record-search-tyres.component'; const mockGlobalErrorService = { - addError: jest.fn(), - clearErrors: jest.fn(), + addError: jest.fn(), + clearErrors: jest.fn(), }; const mockTechRecordService = { - get techRecord$() { - return of({}); - }, + get techRecord$() { + return of({}); + }, }; const mockReferenceDataService = { - addSearchInformation: jest.fn(), - getTyreSearchReturn$: jest.fn(), - getTyreSearchCriteria$: jest.fn(), - loadReferenceDataByKeySearch: jest.fn(), - loadTyreReferenceDataByKeySearch: jest.fn(), - loadReferenceData: jest.fn(), + addSearchInformation: jest.fn(), + getTyreSearchReturn$: jest.fn(), + getTyreSearchCriteria$: jest.fn(), + loadReferenceDataByKeySearch: jest.fn(), + loadTyreReferenceDataByKeySearch: jest.fn(), + loadReferenceData: jest.fn(), }; const mockDynamicFormService = { - createForm: jest.fn(), + createForm: jest.fn(), }; describe('TechRecordSearchTyresComponent', () => { - let component: TechRecordSearchTyresComponent; - let fixture: ComponentFixture; - const actions$ = new ReplaySubject(); - let router: Router; - let route: ActivatedRoute; - let store: MockStore; + let component: TechRecordSearchTyresComponent; + let fixture: ComponentFixture; + const actions$ = new ReplaySubject(); + let router: Router; + let route: ActivatedRoute; + let store: MockStore; - beforeEach(async () => { - await TestBed.configureTestingModule({ - declarations: [TechRecordSearchTyresComponent], - imports: [DynamicFormsModule, RouterTestingModule, SharedModule, HttpClientTestingModule, FixNavigationTriggeredOutsideAngularZoneNgModule], - providers: [ - provideMockActions(() => actions$), - provideMockStore({ initialState: initialAppState }), - { provide: ReferenceDataService, useValue: mockReferenceDataService }, - { provide: TechnicalRecordService, useValue: mockTechRecordService }, - { provide: DynamicFormService, useValue: mockDynamicFormService }, - { provide: GlobalErrorService, useValue: mockGlobalErrorService }, - ], - }).compileComponents(); - }); + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [TechRecordSearchTyresComponent], + imports: [ + DynamicFormsModule, + RouterTestingModule, + SharedModule, + HttpClientTestingModule, + FixNavigationTriggeredOutsideAngularZoneNgModule, + ], + providers: [ + provideMockActions(() => actions$), + provideMockStore({ initialState: initialAppState }), + { provide: ReferenceDataService, useValue: mockReferenceDataService }, + { provide: TechnicalRecordService, useValue: mockTechRecordService }, + { provide: DynamicFormService, useValue: mockDynamicFormService }, + { provide: GlobalErrorService, useValue: mockGlobalErrorService }, + ], + }).compileComponents(); + }); - beforeEach(() => { - fixture = TestBed.createComponent(TechRecordSearchTyresComponent); - router = TestBed.inject(Router); - route = TestBed.inject(ActivatedRoute); - store = TestBed.inject(MockStore); - component = fixture.componentInstance; - }); + beforeEach(() => { + fixture = TestBed.createComponent(TechRecordSearchTyresComponent); + router = TestBed.inject(Router); + route = TestBed.inject(ActivatedRoute); + store = TestBed.inject(MockStore); + component = fixture.componentInstance; + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); - it('should return roles', () => { - const { roles } = component; - expect(roles).toBe(Roles); - }); - it('should return errors', () => { - const expectedError: GlobalError = { error: 'Error message', anchorLink: 'expected' }; - const expectedResult = component.getErrorByName([expectedError], expectedError.anchorLink ?? ''); - expect(expectedResult).toBe(expectedError); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); + it('should return roles', () => { + const { roles } = component; + expect(roles).toBe(Roles); + }); + it('should return errors', () => { + const expectedError: GlobalError = { error: 'Error message', anchorLink: 'expected' }; + const expectedResult = component.getErrorByName([expectedError], expectedError.anchorLink ?? ''); + expect(expectedResult).toBe(expectedError); + }); - describe('handleSearch', () => { - it('should set search results to an empty array before populating data', () => { - component.handleSearch('', ''); - expect(component.searchResults).toStrictEqual([]); - }); - it('should call add error in global error service when term is empty', () => { - const filter = 'code'; - component.handleSearch('', filter); - expect(mockGlobalErrorService.addError).toHaveBeenCalled(); - }); - it('should call add error in global error service when filter is empty', () => { - const term = '103'; - component.handleSearch(term, ''); - expect(mockGlobalErrorService.addError).toHaveBeenCalled(); - }); - it('should call correct endpoint if filter === code', () => { - const filter = 'code'; - const term = '103'; - component.handleSearch(filter, term); - expect(mockReferenceDataService.loadReferenceDataByKeySearch).toHaveBeenCalledWith(ReferenceDataResourceType.Tyres, term); - }); - it('should call correct endpoint if filter === plyrating', () => { - const filter = 'plyrating'; - const term = '103'; - component.handleSearch(filter, term); - expect(mockReferenceDataService.loadTyreReferenceDataByKeySearch).toHaveBeenCalledWith(filter, term); - }); - it('should call correct endpoint if filter === singleload', () => { - const filter = 'singleload'; - const term = '103'; - component.handleSearch(filter, term); - expect(mockReferenceDataService.loadTyreReferenceDataByKeySearch).toHaveBeenCalledWith(filter, term); - }); - it('should call correct endpoint if filter === doubleload', () => { - const filter = 'doubleload'; - const term = '103'; - component.handleSearch(filter, term); - expect(mockReferenceDataService.loadTyreReferenceDataByKeySearch).toHaveBeenCalledWith(filter, term); - }); - it('should navigate and populate the search results on success action', fakeAsync(() => { - const navigateSpy = jest.spyOn(router, 'navigate'); - const mockTyreSearchReturn = ['foo', 'bar']; + describe('handleSearch', () => { + it('should set search results to an empty array before populating data', () => { + component.handleSearch('', ''); + expect(component.searchResults).toStrictEqual([]); + }); + it('should call add error in global error service when term is empty', () => { + const filter = 'code'; + component.handleSearch('', filter); + expect(mockGlobalErrorService.addError).toHaveBeenCalled(); + }); + it('should call add error in global error service when filter is empty', () => { + const term = '103'; + component.handleSearch(term, ''); + expect(mockGlobalErrorService.addError).toHaveBeenCalled(); + }); + it('should call correct endpoint if filter === code', () => { + const filter = 'code'; + const term = '103'; + component.handleSearch(filter, term); + expect(mockReferenceDataService.loadReferenceDataByKeySearch).toHaveBeenCalledWith( + ReferenceDataResourceType.Tyres, + term + ); + }); + it('should call correct endpoint if filter === plyrating', () => { + const filter = 'plyrating'; + const term = '103'; + component.handleSearch(filter, term); + expect(mockReferenceDataService.loadTyreReferenceDataByKeySearch).toHaveBeenCalledWith(filter, term); + }); + it('should call correct endpoint if filter === singleload', () => { + const filter = 'singleload'; + const term = '103'; + component.handleSearch(filter, term); + expect(mockReferenceDataService.loadTyreReferenceDataByKeySearch).toHaveBeenCalledWith(filter, term); + }); + it('should call correct endpoint if filter === doubleload', () => { + const filter = 'doubleload'; + const term = '103'; + component.handleSearch(filter, term); + expect(mockReferenceDataService.loadTyreReferenceDataByKeySearch).toHaveBeenCalledWith(filter, term); + }); + it('should navigate and populate the search results on success action', fakeAsync(() => { + const navigateSpy = jest.spyOn(router, 'navigate'); + const mockTyreSearchReturn = ['foo', 'bar']; - jest.spyOn(store, 'select').mockReturnValue(of(mockTyreSearchReturn)); - component.handleSearch('foo', 'bar'); + jest.spyOn(store, 'select').mockReturnValue(of(mockTyreSearchReturn)); + component.handleSearch('foo', 'bar'); - expect(mockReferenceDataService.loadTyreReferenceDataByKeySearch).toHaveBeenCalledWith('foo', 'bar'); - actions$.next(fetchReferenceDataByKeySearchSuccess); + expect(mockReferenceDataService.loadTyreReferenceDataByKeySearch).toHaveBeenCalledWith('foo', 'bar'); + actions$.next(fetchReferenceDataByKeySearchSuccess); - tick(); + tick(); - expect(navigateSpy).toHaveBeenCalledWith(['.'], { relativeTo: route, queryParams: { 'search-results-page': 1 } }); - expect(component.searchResults).toEqual(mockTyreSearchReturn); - })); + expect(navigateSpy).toHaveBeenCalledWith(['.'], { relativeTo: route, queryParams: { 'search-results-page': 1 } }); + expect(component.searchResults).toEqual(mockTyreSearchReturn); + })); - const testCases = [ - { filter: '', term: 'foo' }, - { filter: 'foo', term: '' }, - { filter: '', term: '' }, - ]; + const testCases = [ + { filter: '', term: 'foo' }, + { filter: 'foo', term: '' }, + { filter: '', term: '' }, + ]; - it.each(testCases)('should return early if the search information has not been provided', ({ filter, term }) => { - jest.resetAllMocks(); - const refDataServiceSpy = jest.spyOn(mockReferenceDataService, 'addSearchInformation'); - const errorServiceSpy = jest.spyOn(mockGlobalErrorService, 'addError'); - component.handleSearch(filter, term); - expect(refDataServiceSpy).not.toHaveBeenCalled(); - expect(errorServiceSpy).toHaveBeenCalledWith({ error: expect.stringContaining(term ? 'filter' : 'criteria'), anchorLink: 'term' }); - }); - }); + it.each(testCases)('should return early if the search information has not been provided', ({ filter, term }) => { + jest.resetAllMocks(); + const refDataServiceSpy = jest.spyOn(mockReferenceDataService, 'addSearchInformation'); + const errorServiceSpy = jest.spyOn(mockGlobalErrorService, 'addError'); + component.handleSearch(filter, term); + expect(refDataServiceSpy).not.toHaveBeenCalled(); + expect(errorServiceSpy).toHaveBeenCalledWith({ + error: expect.stringContaining(term ? 'filter' : 'criteria'), + anchorLink: 'term', + }); + }); + }); - describe('handleSelectTyreData', () => { - it('should have a truthy value for vehicle tech record', () => { - const tyre: ReferenceDataTyre = { - code: '103', - loadIndexSingleLoad: '0', - tyreSize: '0', - dateTimeStamp: '0', - userId: '0', - loadIndexTwinLoad: '0', - plyRating: '18', - resourceType: ReferenceDataResourceType.Tyres, - resourceKey: '103', - }; - component.handleAddTyreToRecord(tyre); - expect(mockTechRecordService.techRecord$).toBeTruthy(); - }); - it('should clear global errors', () => { - const tyre: ReferenceDataTyre = { - code: '103', - loadIndexSingleLoad: '0', - tyreSize: '0', - dateTimeStamp: '0', - userId: '0', - loadIndexTwinLoad: '0', - plyRating: '18', - resourceType: ReferenceDataResourceType.Tyres, - resourceKey: '103', - }; - component.handleAddTyreToRecord(tyre); - expect(mockGlobalErrorService.clearErrors).toHaveBeenCalled(); - }); - }); + describe('handleSelectTyreData', () => { + it('should have a truthy value for vehicle tech record', () => { + const tyre: ReferenceDataTyre = { + code: '103', + loadIndexSingleLoad: '0', + tyreSize: '0', + dateTimeStamp: '0', + userId: '0', + loadIndexTwinLoad: '0', + plyRating: '18', + resourceType: ReferenceDataResourceType.Tyres, + resourceKey: '103', + }; + component.handleAddTyreToRecord(tyre); + expect(mockTechRecordService.techRecord$).toBeTruthy(); + }); + it('should clear global errors', () => { + const tyre: ReferenceDataTyre = { + code: '103', + loadIndexSingleLoad: '0', + tyreSize: '0', + dateTimeStamp: '0', + userId: '0', + loadIndexTwinLoad: '0', + plyRating: '18', + resourceType: ReferenceDataResourceType.Tyres, + resourceKey: '103', + }; + component.handleAddTyreToRecord(tyre); + expect(mockGlobalErrorService.clearErrors).toHaveBeenCalled(); + }); + }); - describe('The cancel function', () => { - it('should clear global errors', () => { - component.cancel(); - expect(mockGlobalErrorService.clearErrors).toHaveBeenCalled(); - }); - }); + describe('The cancel function', () => { + it('should clear global errors', () => { + component.cancel(); + expect(mockGlobalErrorService.clearErrors).toHaveBeenCalled(); + }); + }); - describe('Getters', () => { - it('should get the currentVrm', () => { - const mockVehicleRecord = { - primaryVrm: 'bar', - secondaryVrms: ['foo'], - } as V3TechRecordModel; - component.vehicleTechRecord = mockVehicleRecord; - expect(component.currentVrm).toBe('bar'); - }); - it('should get the paginated fields', () => { - component.searchResults = ['foo', 'bar', 'foobar'] as unknown as ReferenceDataTyre[]; - expect(component.paginatedFields).toEqual(['foo', 'bar', 'foobar']); - }); - it('should get the number of results', () => { - component.searchResults = ['foo', 'bar', 'foobar'] as unknown as ReferenceDataTyre[]; - expect(component.numberOfResults).toEqual(component.searchResults?.length); - }); - }); + describe('Getters', () => { + it('should get the currentVrm', () => { + const mockVehicleRecord = { + primaryVrm: 'bar', + secondaryVrms: ['foo'], + } as V3TechRecordModel; + component.vehicleTechRecord = mockVehicleRecord; + expect(component.currentVrm).toBe('bar'); + }); + it('should get the paginated fields', () => { + component.searchResults = ['foo', 'bar', 'foobar'] as unknown as ReferenceDataTyre[]; + expect(component.paginatedFields).toEqual(['foo', 'bar', 'foobar']); + }); + it('should get the number of results', () => { + component.searchResults = ['foo', 'bar', 'foobar'] as unknown as ReferenceDataTyre[]; + expect(component.numberOfResults).toEqual(component.searchResults?.length); + }); + }); - describe('trackByFn', () => { - it('should return the resourceKey', () => { - expect(component.trackByFn(12, { resourceKey: 'foo' } as unknown as ReferenceDataTyre)).toBe('foo'); - }); - }); + describe('trackByFn', () => { + it('should return the resourceKey', () => { + expect(component.trackByFn(12, { resourceKey: 'foo' } as unknown as ReferenceDataTyre)).toBe('foo'); + }); + }); - describe('OnInit', () => { - it('should patch the form with the search criteria and the search return', () => { - const mockForm = { - controls: { - filter: { - patchValue: jest.fn(), - }, - term: { - patchValue: jest.fn(), - }, - }, - }; - const mockTyreSearchReturn = ['foobar']; - const mockSearchCriteria = { filter: 'foo', term: 'bar' }; - const dfsSpy = jest.spyOn(mockDynamicFormService, 'createForm').mockReturnValue(mockForm); - jest.spyOn(mockReferenceDataService, 'getTyreSearchReturn$').mockReturnValue(of(mockTyreSearchReturn)); - jest.spyOn(mockReferenceDataService, 'getTyreSearchCriteria$').mockReturnValue(of(mockSearchCriteria)); - const filterSpy = jest.spyOn(mockForm.controls.filter, 'patchValue'); - const termSpy = jest.spyOn(mockForm.controls.term, 'patchValue'); - component.ngOnInit(); - expect(dfsSpy).toHaveBeenCalledWith(component.template); - expect(filterSpy).toHaveBeenCalledWith(mockSearchCriteria.filter); - expect(termSpy).toHaveBeenCalledWith(mockSearchCriteria.term); - expect(component.searchResults).toEqual(mockTyreSearchReturn); - }); - it('should navigate if there is no viewable tech record', () => { - const routerSpy = jest.spyOn(router, 'navigate'); - jest.spyOn(mockTechRecordService, 'techRecord$', 'get').mockReturnValue(of(undefined) as unknown as Observable>); - component.ngOnInit(); - expect(routerSpy).toHaveBeenCalledWith(['../..'], { relativeTo: route }); - }); - }); + describe('OnInit', () => { + it('should patch the form with the search criteria and the search return', () => { + const mockForm = { + controls: { + filter: { + patchValue: jest.fn(), + }, + term: { + patchValue: jest.fn(), + }, + }, + }; + const mockTyreSearchReturn = ['foobar']; + const mockSearchCriteria = { filter: 'foo', term: 'bar' }; + const dfsSpy = jest.spyOn(mockDynamicFormService, 'createForm').mockReturnValue(mockForm); + jest.spyOn(mockReferenceDataService, 'getTyreSearchReturn$').mockReturnValue(of(mockTyreSearchReturn)); + jest.spyOn(mockReferenceDataService, 'getTyreSearchCriteria$').mockReturnValue(of(mockSearchCriteria)); + const filterSpy = jest.spyOn(mockForm.controls.filter, 'patchValue'); + const termSpy = jest.spyOn(mockForm.controls.term, 'patchValue'); + component.ngOnInit(); + expect(dfsSpy).toHaveBeenCalledWith(component.template); + expect(filterSpy).toHaveBeenCalledWith(mockSearchCriteria.filter); + expect(termSpy).toHaveBeenCalledWith(mockSearchCriteria.term); + expect(component.searchResults).toEqual(mockTyreSearchReturn); + }); + it('should navigate if there is no viewable tech record', () => { + const routerSpy = jest.spyOn(router, 'navigate'); + jest + .spyOn(mockTechRecordService, 'techRecord$', 'get') + .mockReturnValue(of(undefined) as unknown as Observable>); + component.ngOnInit(); + expect(routerSpy).toHaveBeenCalledWith(['../..'], { relativeTo: route }); + }); + }); }); diff --git a/src/app/features/tech-record/components/tech-record-search-tyres/tech-record-search-tyres.component.ts b/src/app/features/tech-record/components/tech-record-search-tyres/tech-record-search-tyres.component.ts index 72665d8c2b..9f10a98a03 100644 --- a/src/app/features/tech-record/components/tech-record-search-tyres/tech-record-search-tyres.component.ts +++ b/src/app/features/tech-record/components/tech-record-search-tyres/tech-record-search-tyres.component.ts @@ -6,9 +6,7 @@ import { TechRecordType } from '@dvsa/cvs-type-definitions/types/v3/tech-record/ import { TechRecordType as TechRecordTypeByVerb } from '@dvsa/cvs-type-definitions/types/v3/tech-record/tech-record-verb'; import { MultiOptions } from '@forms/models/options.model'; import { DynamicFormService } from '@forms/services/dynamic-form.service'; -import { - CustomFormGroup, FormNode, FormNodeTypes, SearchParams, -} from '@forms/services/dynamic-form.types'; +import { CustomFormGroup, FormNode, FormNodeTypes, SearchParams } from '@forms/services/dynamic-form.types'; import { ReferenceDataResourceType, ReferenceDataTyre, ReferenceDataTyreLoadIndex } from '@models/reference-data.model'; import { Roles } from '@models/roles.enum'; import { V3TechRecordModel } from '@models/vehicle-tech-record.model'; @@ -22,170 +20,175 @@ import { TechnicalRecordServiceState } from '@store/technical-records/reducers/t import { Observable, mergeMap, take } from 'rxjs'; @Component({ - selector: 'app-tyres-search', - templateUrl: './tech-record-search-tyres.component.html', - styleUrls: ['./tech-record-search-tyres.component.scss'], + selector: 'app-tyres-search', + templateUrl: './tech-record-search-tyres.component.html', + styleUrls: ['./tech-record-search-tyres.component.scss'], }) export class TechRecordSearchTyresComponent implements OnInit { - options?: MultiOptions = [ - { label: 'Tyre code', value: 'code' }, - { label: 'Ply rating', value: 'plyrating' }, - { label: 'Single load index', value: 'singleload' }, - { label: 'Double load index', value: 'doubleload' }, - ]; + options?: MultiOptions = [ + { label: 'Tyre code', value: 'code' }, + { label: 'Ply rating', value: 'plyrating' }, + { label: 'Single load index', value: 'singleload' }, + { label: 'Double load index', value: 'doubleload' }, + ]; - constructor( - private cdr: ChangeDetectorRef, - public dfs: DynamicFormService, - public globalErrorService: GlobalErrorService, - private referenceDataService: ReferenceDataService, - private route: ActivatedRoute, - private router: Router, - private technicalRecordService: TechnicalRecordService, - private store: Store, - private actions$: Actions, - ) {} + constructor( + private cdr: ChangeDetectorRef, + public dfs: DynamicFormService, + public globalErrorService: GlobalErrorService, + private referenceDataService: ReferenceDataService, + private route: ActivatedRoute, + private router: Router, + private technicalRecordService: TechnicalRecordService, + private store: Store, + private actions$: Actions + ) {} - public form!: CustomFormGroup; - public searchResults: Array | null = null; - public vehicleTechRecord?: V3TechRecordModel; - public viewableTechRecord?: TechRecordType<'hgv' | 'psv' | 'trl'>; - private params: SearchParams = {}; - private pageStart?: number; - private pageEnd?: number; - public itemsPerPage = 10; + public form!: CustomFormGroup; + public searchResults: Array | null = null; + public vehicleTechRecord?: V3TechRecordModel; + public viewableTechRecord?: TechRecordType<'hgv' | 'psv' | 'trl'>; + private params: SearchParams = {}; + private pageStart?: number; + private pageEnd?: number; + public itemsPerPage = 10; - public template: FormNode = { - name: 'criteria', - type: FormNodeTypes.GROUP, - children: [ - { - name: 'filter', - label: 'Search filter', - value: '', - type: FormNodeTypes.CONTROL, - }, - { - name: 'term', - value: '', - type: FormNodeTypes.CONTROL, - }, - ], - }; + public template: FormNode = { + name: 'criteria', + type: FormNodeTypes.GROUP, + children: [ + { + name: 'filter', + label: 'Search filter', + value: '', + type: FormNodeTypes.CONTROL, + }, + { + name: 'term', + value: '', + type: FormNodeTypes.CONTROL, + }, + ], + }; - ngOnInit() { - this.form = this.dfs.createForm(this.template) as CustomFormGroup; - this.globalErrorService.clearErrors(); - this.route.params.pipe(take(1)).subscribe((p) => { - this.params = p; - }); - this.technicalRecordService.techRecord$.pipe(take(1)).subscribe((data) => { - this.viewableTechRecord = data as TechRecordType<'hgv' | 'psv' | 'trl'>; - }); - this.referenceDataService - .getTyreSearchReturn$() - .pipe(take(1)) - .subscribe((data) => { - this.searchResults = data; - }); - this.referenceDataService - .getTyreSearchCriteria$() - .pipe(take(1)) - .subscribe((v) => { - this.form.controls['filter'].patchValue(v.filter); - this.form.controls['term'].patchValue(v.term); - }); - this.referenceDataService.loadReferenceData(ReferenceDataResourceType.TyreLoadIndex); - if (!this.viewableTechRecord) { - void this.router.navigate(['../..'], { relativeTo: this.route }); - } - this.technicalRecordService.techRecord$.pipe(take(1)).subscribe((data) => { - this.vehicleTechRecord = data; - }); - } + ngOnInit() { + this.form = this.dfs.createForm(this.template) as CustomFormGroup; + this.globalErrorService.clearErrors(); + this.route.params.pipe(take(1)).subscribe((p) => { + this.params = p; + }); + this.technicalRecordService.techRecord$.pipe(take(1)).subscribe((data) => { + this.viewableTechRecord = data as TechRecordType<'hgv' | 'psv' | 'trl'>; + }); + this.referenceDataService + .getTyreSearchReturn$() + .pipe(take(1)) + .subscribe((data) => { + this.searchResults = data; + }); + this.referenceDataService + .getTyreSearchCriteria$() + .pipe(take(1)) + .subscribe((v) => { + this.form.controls['filter'].patchValue(v.filter); + this.form.controls['term'].patchValue(v.term); + }); + this.referenceDataService.loadReferenceData(ReferenceDataResourceType.TyreLoadIndex); + if (!this.viewableTechRecord) { + void this.router.navigate(['../..'], { relativeTo: this.route }); + } + this.technicalRecordService.techRecord$.pipe(take(1)).subscribe((data) => { + this.vehicleTechRecord = data; + }); + } - get roles() { - return Roles; - } - get currentVrm(): string | undefined { - return this.vehicleTechRecord?.techRecord_vehicleType !== 'trl' ? this.vehicleTechRecord?.primaryVrm ?? '' : undefined; - } - get paginatedFields(): ReferenceDataTyre[] { - return this.searchResults?.slice(this.pageStart, this.pageEnd) ?? []; - } - get numberOfResults(): number { - return this.searchResults?.length ?? 0; - } + get roles() { + return Roles; + } + get currentVrm(): string | undefined { + return this.vehicleTechRecord?.techRecord_vehicleType !== 'trl' + ? this.vehicleTechRecord?.primaryVrm ?? '' + : undefined; + } + get paginatedFields(): ReferenceDataTyre[] { + return this.searchResults?.slice(this.pageStart, this.pageEnd) ?? []; + } + get numberOfResults(): number { + return this.searchResults?.length ?? 0; + } - get loadIndex$(): Observable { - return this.referenceDataService.getAll$(ReferenceDataResourceType.TyreLoadIndex) as Observable; - } + get loadIndex$(): Observable { + return this.referenceDataService.getAll$(ReferenceDataResourceType.TyreLoadIndex) as Observable< + ReferenceDataTyreLoadIndex[] + >; + } - handleSearch(filter: string, term: string): void { - this.globalErrorService.clearErrors(); - this.searchResults = []; - const trimmedTerm = term?.trim(); - if (!trimmedTerm || !filter) { - const error = !trimmedTerm ? 'You must provide a search criteria' : 'You must select a valid search filter'; - this.globalErrorService.addError({ error, anchorLink: 'term' }); - return; - } - this.referenceDataService.addSearchInformation(filter, trimmedTerm); - if (filter === 'code') { - this.referenceDataService.loadReferenceDataByKeySearch(ReferenceDataResourceType.Tyres, trimmedTerm); - } else { - this.referenceDataService.loadTyreReferenceDataByKeySearch(filter, trimmedTerm); - } + handleSearch(filter: string, term: string): void { + this.globalErrorService.clearErrors(); + this.searchResults = []; + const trimmedTerm = term?.trim(); + if (!trimmedTerm || !filter) { + const error = !trimmedTerm ? 'You must provide a search criteria' : 'You must select a valid search filter'; + this.globalErrorService.addError({ error, anchorLink: 'term' }); + return; + } + this.referenceDataService.addSearchInformation(filter, trimmedTerm); + if (filter === 'code') { + this.referenceDataService.loadReferenceDataByKeySearch(ReferenceDataResourceType.Tyres, trimmedTerm); + } else { + this.referenceDataService.loadTyreReferenceDataByKeySearch(filter, trimmedTerm); + } - this.actions$ - .pipe( - ofType(fetchReferenceDataByKeySearchSuccess, fetchTyreReferenceDataByKeySearchSuccess), - mergeMap(() => this.store.select(selectSearchReturn(ReferenceDataResourceType.Tyres))), - take(1), - ) - .subscribe((data) => { - void this.router.navigate(['.'], { relativeTo: this.route, queryParams: { 'search-results-page': 1 } }); - this.searchResults = data as ReferenceDataTyre[]; - }); - } + this.actions$ + .pipe( + ofType(fetchReferenceDataByKeySearchSuccess, fetchTyreReferenceDataByKeySearchSuccess), + mergeMap(() => this.store.select(selectSearchReturn(ReferenceDataResourceType.Tyres))), + take(1) + ) + .subscribe((data) => { + void this.router.navigate(['.'], { relativeTo: this.route, queryParams: { 'search-results-page': 1 } }); + this.searchResults = data as ReferenceDataTyre[]; + }); + } - handleAddTyreToRecord(tyre: ReferenceDataTyre): void { - const axleIndex = Number(this.params.axleNumber) - 1; - if (this.viewableTechRecord && !this.viewableTechRecord.techRecord_axles) { - this.viewableTechRecord.techRecord_axles = []; - } - if (this.viewableTechRecord?.techRecord_axles && !this.viewableTechRecord?.techRecord_axles?.[`${axleIndex}`]) { - this.viewableTechRecord.techRecord_axles[`${axleIndex}`] = {}; - } + handleAddTyreToRecord(tyre: ReferenceDataTyre): void { + const axleIndex = Number(this.params.axleNumber) - 1; + if (this.viewableTechRecord && !this.viewableTechRecord.techRecord_axles) { + this.viewableTechRecord.techRecord_axles = []; + } + if (this.viewableTechRecord?.techRecord_axles && !this.viewableTechRecord?.techRecord_axles?.[`${axleIndex}`]) { + this.viewableTechRecord.techRecord_axles[`${axleIndex}`] = {}; + } - if (this.viewableTechRecord?.techRecord_axles) { - this.viewableTechRecord.techRecord_axles[`${axleIndex}`].tyres_tyreCode = Number(tyre.code); - this.viewableTechRecord.techRecord_axles[`${axleIndex}`].tyres_tyreSize = tyre.tyreSize; - this.viewableTechRecord.techRecord_axles[`${axleIndex}`].tyres_plyRating = tyre.plyRating; - if (this.viewableTechRecord.techRecord_axles[`${axleIndex}`].tyres_fitmentCode) { - // eslint-disable-next-line max-len - this.viewableTechRecord.techRecord_axles[`${axleIndex}`].tyres_dataTrAxles = this.viewableTechRecord.techRecord_axles[`${axleIndex}`].tyres_fitmentCode === 'single' - ? parseInt(tyre.loadIndexSingleLoad ?? '0', 10) - : parseInt(tyre.loadIndexTwinLoad ?? '0', 10); - } - this.technicalRecordService.updateEditingTechRecord(this.viewableTechRecord as TechRecordTypeByVerb<'put'>); - void this.router.navigate(['../..'], { relativeTo: this.route }); - } - } + if (this.viewableTechRecord?.techRecord_axles) { + this.viewableTechRecord.techRecord_axles[`${axleIndex}`].tyres_tyreCode = Number(tyre.code); + this.viewableTechRecord.techRecord_axles[`${axleIndex}`].tyres_tyreSize = tyre.tyreSize; + this.viewableTechRecord.techRecord_axles[`${axleIndex}`].tyres_plyRating = tyre.plyRating; + if (this.viewableTechRecord.techRecord_axles[`${axleIndex}`].tyres_fitmentCode) { + // eslint-disable-next-line max-len + this.viewableTechRecord.techRecord_axles[`${axleIndex}`].tyres_dataTrAxles = + this.viewableTechRecord.techRecord_axles[`${axleIndex}`].tyres_fitmentCode === 'single' + ? Number.parseInt(tyre.loadIndexSingleLoad ?? '0', 10) + : Number.parseInt(tyre.loadIndexTwinLoad ?? '0', 10); + } + this.technicalRecordService.updateEditingTechRecord(this.viewableTechRecord as TechRecordTypeByVerb<'put'>); + void this.router.navigate(['../..'], { relativeTo: this.route }); + } + } - handlePaginationChange({ start, end }: { start: number; end: number }) { - this.pageStart = start; - this.pageEnd = end; - this.cdr.detectChanges(); - } - getErrorByName(errors: GlobalError[], name: string): GlobalError | undefined { - return errors.find((error) => error.anchorLink === name); - } - trackByFn(i: number, r: ReferenceDataTyre) { - return r.resourceKey; - } - cancel() { - this.globalErrorService.clearErrors(); - void this.router.navigate(['../..'], { relativeTo: this.route }); - } + handlePaginationChange({ start, end }: { start: number; end: number }) { + this.pageStart = start; + this.pageEnd = end; + this.cdr.detectChanges(); + } + getErrorByName(errors: GlobalError[], name: string): GlobalError | undefined { + return errors.find((error) => error.anchorLink === name); + } + trackByFn(i: number, r: ReferenceDataTyre) { + return r.resourceKey; + } + cancel() { + this.globalErrorService.clearErrors(); + void this.router.navigate(['../..'], { relativeTo: this.route }); + } } diff --git a/src/app/features/tech-record/components/tech-record-summary-changes/tech-record-summary-changes.component.spec.ts b/src/app/features/tech-record/components/tech-record-summary-changes/tech-record-summary-changes.component.spec.ts index 4a6486e76b..59b57aef14 100644 --- a/src/app/features/tech-record/components/tech-record-summary-changes/tech-record-summary-changes.component.spec.ts +++ b/src/app/features/tech-record/components/tech-record-summary-changes/tech-record-summary-changes.component.spec.ts @@ -15,7 +15,11 @@ import { UserService } from '@services/user-service/user-service'; import { SharedModule } from '@shared/shared.module'; import { initialAppState } from '@store/index'; import { - amendVrmSuccess, editingTechRecord, selectTechRecordChanges, selectTechRecordDeletions, techRecord, + amendVrmSuccess, + editingTechRecord, + selectTechRecordChanges, + selectTechRecordDeletions, + techRecord, } from '@store/technical-records'; import { ReplaySubject, of } from 'rxjs'; import { TechRecordSummaryChangesComponent } from './tech-record-summary-changes.component'; @@ -23,204 +27,204 @@ import { TechRecordSummaryChangesComponent } from './tech-record-summary-changes let actions$: ReplaySubject; let store: MockStore; describe('TechRecordSummaryChangesComponent', () => { - let component: TechRecordSummaryChangesComponent; - let fixture: ComponentFixture; - let router: Router; - - beforeEach(async () => { - actions$ = new ReplaySubject(); - await TestBed.configureTestingModule({ - declarations: [TechRecordSummaryChangesComponent], - imports: [DynamicFormsModule, HttpClientTestingModule, RouterTestingModule, SharedModule], - providers: [ - provideMockActions(() => actions$), - provideMockStore({ initialState: initialAppState }), - { - provide: UserService, - useValue: { - name$: of('tester'), - }, - }, - { - provide: RouterService, - useValue: { - getRouteNestedParam$(param: string) { - if (param === 'systemNumber') return of('123456'); - if (param === 'createdTimestamp') return of('123123123'); - return of(''); - }, - }, - }, - ], - }).compileComponents(); - router = TestBed.inject(Router); - fixture = TestBed.createComponent(TechRecordSummaryChangesComponent); - store = TestBed.inject(MockStore); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); - - describe('navigateUponSuccess', () => { - it('should handle state management', () => { - const spy = jest.spyOn(component.actions$, 'pipe'); - component.navigateUponSuccess(); - expect(spy).toHaveBeenCalled(); - }); - }); - - describe('ngOnInit', () => { - it('should call navigateOnSuccess and initSubscriptions', () => { - const navigateOnSuccessSpy = jest.spyOn(component, 'navigateUponSuccess'); - const initSubscriptionsSpy = jest.spyOn(component, 'initSubscriptions'); - component.ngOnInit(); - expect(navigateOnSuccessSpy).toHaveBeenCalled(); - expect(initSubscriptionsSpy).toHaveBeenCalled(); - }); - it('should navigate when updateRecordSuccess dispatched', () => { - const navigateSpy = jest.spyOn(router, 'navigate').mockImplementation(() => Promise.resolve(true)); - - component.ngOnInit(); - - actions$.next( - amendVrmSuccess({ - vehicleTechRecord: { - createdTimestamp: 'now', - vin: 'testVin', - systemNumber: 'testNumber', - } as TechRecordType<'get'>, - }), - ); - - expect(navigateSpy).toHaveBeenCalled(); - }); - }); - - describe('initSubscriptions', () => { - let spy: unknown; - - beforeEach(() => { - spy = jest.spyOn(store, 'select'); - component.ngOnInit(); - }); - it('should grab techRecord from the store', () => { - expect(spy).toHaveBeenCalledWith(techRecord); - }); - it('should grab editingTechRecord from the store', () => { - expect(spy).toHaveBeenCalledWith(editingTechRecord); - }); - it('should grab selectTechRecordChanges from the store', () => { - expect(spy).toHaveBeenCalledWith(selectTechRecordChanges); - }); - it('should grab selectTechRecordDeletions from the store', () => { - expect(spy).toHaveBeenCalledWith(selectTechRecordDeletions); - }); - }); - - describe('ngOnDestroy', () => { - it('should call the destroy.next and destroy.complete', () => { - const nextSpy = jest.spyOn(component.destroy$, 'next'); - const completeSpy = jest.spyOn(component.destroy$, 'complete'); - component.ngOnDestroy(); - expect(nextSpy).toHaveBeenCalled(); - expect(completeSpy).toHaveBeenCalled(); - }); - }); - - describe('submit', () => { - it('should dispatch clearADRDetailsBeforeUpdate', () => { - const dispatch = jest.spyOn(store, 'dispatch'); - component.submit(); - expect(dispatch).toHaveBeenCalled(); - }); - - it('should dispatch updateTechRecords', () => { - const dispatchSpy = jest.spyOn(store, 'dispatch'); - component.submit(); - expect(dispatchSpy).toHaveBeenCalled(); - expect(dispatchSpy).toHaveBeenCalledWith({ - systemNumber: '123456', - createdTimestamp: '123123123', - type: '[Technical Record Service] updateTechRecords', - }); - }); - }); - - describe('cancel', () => { - it('should call globalErrorService.clearErrors and then navigate', () => { - const clearErrorsSpy = jest.spyOn(component.globalErrorService, 'clearErrors'); - const navigateSpy = jest.spyOn(component.router, 'navigate'); - component.cancel(); - expect(clearErrorsSpy).toHaveBeenCalled(); - expect(navigateSpy).toHaveBeenCalled(); - }); - }); - - describe('getTechRecordChangesKeys', () => { - it('should return a list of all of the populated keys', () => { - component.techRecordChanges = { techRecord_grossEecWeight: 1, techRecord_grossDesignWeight: 1 }; - const keys = component.getTechRecordChangesKeys(); - expect(keys).toEqual(['techRecord_grossEecWeight', 'techRecord_grossDesignWeight']); - }); - }); - - describe('getSectionsWhitelist', () => { - it('should return an empty array if vehicleType is null', () => { - component.techRecordEdited = undefined; - const value = component.getSectionsWhitelist(); - expect(value).toEqual([]); - }); - it('should return an empty array if techRecordChanges is null', () => { - component.techRecord = undefined; - const value = component.getSectionsWhitelist(); - expect(value).toEqual([]); - }); - it('should call haveAxlesChanged if vehicleType and techRecordChanges are defined', () => { - const localTechRecordEdited = getEmptyTechRecord(); - component.techRecordEdited = localTechRecordEdited as TechRecordType<'put'>; - component.techRecordChanges = { techRecord_grossEecWeight: 1, techRecord_grossDesignWeight: 1 }; - const value = component.getSectionsWhitelist(); - expect(value).toEqual(['weightsSection']); - }); - }); - - describe('toVisibleFormNode', () => { - it('updates the viewType property from hidden to string', () => { - const children = TechRecordReasonForCreationSection.children as FormNode[]; - const formNode = component.toVisibleFormNode(children[0]); - expect(formNode.viewType).toEqual(FormNodeViewTypes.STRING); - }); - }); + let component: TechRecordSummaryChangesComponent; + let fixture: ComponentFixture; + let router: Router; + + beforeEach(async () => { + actions$ = new ReplaySubject(); + await TestBed.configureTestingModule({ + declarations: [TechRecordSummaryChangesComponent], + imports: [DynamicFormsModule, HttpClientTestingModule, RouterTestingModule, SharedModule], + providers: [ + provideMockActions(() => actions$), + provideMockStore({ initialState: initialAppState }), + { + provide: UserService, + useValue: { + name$: of('tester'), + }, + }, + { + provide: RouterService, + useValue: { + getRouteNestedParam$(param: string) { + if (param === 'systemNumber') return of('123456'); + if (param === 'createdTimestamp') return of('123123123'); + return of(''); + }, + }, + }, + ], + }).compileComponents(); + router = TestBed.inject(Router); + fixture = TestBed.createComponent(TechRecordSummaryChangesComponent); + store = TestBed.inject(MockStore); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + describe('navigateUponSuccess', () => { + it('should handle state management', () => { + const spy = jest.spyOn(component.actions$, 'pipe'); + component.navigateUponSuccess(); + expect(spy).toHaveBeenCalled(); + }); + }); + + describe('ngOnInit', () => { + it('should call navigateOnSuccess and initSubscriptions', () => { + const navigateOnSuccessSpy = jest.spyOn(component, 'navigateUponSuccess'); + const initSubscriptionsSpy = jest.spyOn(component, 'initSubscriptions'); + component.ngOnInit(); + expect(navigateOnSuccessSpy).toHaveBeenCalled(); + expect(initSubscriptionsSpy).toHaveBeenCalled(); + }); + it('should navigate when updateRecordSuccess dispatched', () => { + const navigateSpy = jest.spyOn(router, 'navigate').mockImplementation(() => Promise.resolve(true)); + + component.ngOnInit(); + + actions$.next( + amendVrmSuccess({ + vehicleTechRecord: { + createdTimestamp: 'now', + vin: 'testVin', + systemNumber: 'testNumber', + } as TechRecordType<'get'>, + }) + ); + + expect(navigateSpy).toHaveBeenCalled(); + }); + }); + + describe('initSubscriptions', () => { + let spy: unknown; + + beforeEach(() => { + spy = jest.spyOn(store, 'select'); + component.ngOnInit(); + }); + it('should grab techRecord from the store', () => { + expect(spy).toHaveBeenCalledWith(techRecord); + }); + it('should grab editingTechRecord from the store', () => { + expect(spy).toHaveBeenCalledWith(editingTechRecord); + }); + it('should grab selectTechRecordChanges from the store', () => { + expect(spy).toHaveBeenCalledWith(selectTechRecordChanges); + }); + it('should grab selectTechRecordDeletions from the store', () => { + expect(spy).toHaveBeenCalledWith(selectTechRecordDeletions); + }); + }); + + describe('ngOnDestroy', () => { + it('should call the destroy.next and destroy.complete', () => { + const nextSpy = jest.spyOn(component.destroy$, 'next'); + const completeSpy = jest.spyOn(component.destroy$, 'complete'); + component.ngOnDestroy(); + expect(nextSpy).toHaveBeenCalled(); + expect(completeSpy).toHaveBeenCalled(); + }); + }); + + describe('submit', () => { + it('should dispatch clearADRDetailsBeforeUpdate', () => { + const dispatch = jest.spyOn(store, 'dispatch'); + component.submit(); + expect(dispatch).toHaveBeenCalled(); + }); + + it('should dispatch updateTechRecords', () => { + const dispatchSpy = jest.spyOn(store, 'dispatch'); + component.submit(); + expect(dispatchSpy).toHaveBeenCalled(); + expect(dispatchSpy).toHaveBeenCalledWith({ + systemNumber: '123456', + createdTimestamp: '123123123', + type: '[Technical Record Service] updateTechRecords', + }); + }); + }); + + describe('cancel', () => { + it('should call globalErrorService.clearErrors and then navigate', () => { + const clearErrorsSpy = jest.spyOn(component.globalErrorService, 'clearErrors'); + const navigateSpy = jest.spyOn(component.router, 'navigate'); + component.cancel(); + expect(clearErrorsSpy).toHaveBeenCalled(); + expect(navigateSpy).toHaveBeenCalled(); + }); + }); + + describe('getTechRecordChangesKeys', () => { + it('should return a list of all of the populated keys', () => { + component.techRecordChanges = { techRecord_grossEecWeight: 1, techRecord_grossDesignWeight: 1 }; + const keys = component.getTechRecordChangesKeys(); + expect(keys).toEqual(['techRecord_grossEecWeight', 'techRecord_grossDesignWeight']); + }); + }); + + describe('getSectionsWhitelist', () => { + it('should return an empty array if vehicleType is null', () => { + component.techRecordEdited = undefined; + const value = component.getSectionsWhitelist(); + expect(value).toEqual([]); + }); + it('should return an empty array if techRecordChanges is null', () => { + component.techRecord = undefined; + const value = component.getSectionsWhitelist(); + expect(value).toEqual([]); + }); + it('should call haveAxlesChanged if vehicleType and techRecordChanges are defined', () => { + const localTechRecordEdited = getEmptyTechRecord(); + component.techRecordEdited = localTechRecordEdited as TechRecordType<'put'>; + component.techRecordChanges = { techRecord_grossEecWeight: 1, techRecord_grossDesignWeight: 1 }; + const value = component.getSectionsWhitelist(); + expect(value).toEqual(['weightsSection']); + }); + }); + + describe('toVisibleFormNode', () => { + it('updates the viewType property from hidden to string', () => { + const children = TechRecordReasonForCreationSection.children as FormNode[]; + const formNode = component.toVisibleFormNode(children[0]); + expect(formNode.viewType).toEqual(FormNodeViewTypes.STRING); + }); + }); }); function getEmptyTechRecord(): V3TechRecordModel { - return { - techRecord_createdAt: '', - techRecord_createdById: null, - techRecord_createdByName: null, - techRecord_euVehicleCategory: null, - techRecord_lastUpdatedAt: null, - techRecord_lastUpdatedById: null, - techRecord_lastUpdatedByName: null, - techRecord_manufactureYear: null, - techRecord_noOfAxles: 2, - techRecord_notes: undefined, - techRecord_applicantDetails_address1: null, - techRecord_applicantDetails_address2: null, - techRecord_applicantDetails_address3: null, - techRecord_applicantDetails_emailAddress: null, - techRecord_applicantDetails_name: null, - techRecord_applicantDetails_postCode: null, - techRecord_applicantDetails_postTown: null, - techRecord_applicantDetails_telephoneNumber: null, - techRecord_reasonForCreation: '', - techRecord_regnDate: null, - techRecord_statusCode: '', - techRecord_vehicleConfiguration: 'other', - techRecord_vehicleSubclass: undefined, - techRecord_vehicleType: 'hgv', - } as unknown as V3TechRecordModel; + return { + techRecord_createdAt: '', + techRecord_createdById: null, + techRecord_createdByName: null, + techRecord_euVehicleCategory: null, + techRecord_lastUpdatedAt: null, + techRecord_lastUpdatedById: null, + techRecord_lastUpdatedByName: null, + techRecord_manufactureYear: null, + techRecord_noOfAxles: 2, + techRecord_notes: undefined, + techRecord_applicantDetails_address1: null, + techRecord_applicantDetails_address2: null, + techRecord_applicantDetails_address3: null, + techRecord_applicantDetails_emailAddress: null, + techRecord_applicantDetails_name: null, + techRecord_applicantDetails_postCode: null, + techRecord_applicantDetails_postTown: null, + techRecord_applicantDetails_telephoneNumber: null, + techRecord_reasonForCreation: '', + techRecord_regnDate: null, + techRecord_statusCode: '', + techRecord_vehicleConfiguration: 'other', + techRecord_vehicleSubclass: undefined, + techRecord_vehicleType: 'hgv', + } as unknown as V3TechRecordModel; } diff --git a/src/app/features/tech-record/components/tech-record-summary-changes/tech-record-summary-changes.component.ts b/src/app/features/tech-record/components/tech-record-summary-changes/tech-record-summary-changes.component.ts index 43393017e3..d8e2896158 100644 --- a/src/app/features/tech-record/components/tech-record-summary-changes/tech-record-summary-changes.component.ts +++ b/src/app/features/tech-record/components/tech-record-summary-changes/tech-record-summary-changes.component.ts @@ -1,15 +1,13 @@ -import { - ChangeDetectionStrategy, Component, OnDestroy, OnInit, -} from '@angular/core'; +import { ChangeDetectionStrategy, Component, OnDestroy, OnInit } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; import { GlobalErrorService } from '@core/components/global-error/global-error.service'; import { TechRecordType } from '@dvsa/cvs-type-definitions/types/v3/tech-record/tech-record-verb'; import { - TechRecordGETCar, - TechRecordGETHGV, - TechRecordGETLGV, - TechRecordGETPSV, - TechRecordGETTRL, + TechRecordGETCar, + TechRecordGETHGV, + TechRecordGETLGV, + TechRecordGETPSV, + TechRecordGETTRL, } from '@dvsa/cvs-type-definitions/types/v3/tech-record/tech-record-verb-vehicle-type'; import { FormNode, FormNodeViewTypes } from '@forms/services/dynamic-form.types'; import { VehicleSummary } from '@forms/templates/tech-records/vehicle-summary.template'; @@ -22,215 +20,227 @@ import { TechnicalRecordService } from '@services/technical-record/technical-rec import { UserService } from '@services/user-service/user-service'; import { State } from '@store/index'; import { - clearADRDetailsBeforeUpdate, - clearAllSectionStates, - clearScrollPosition, - editingTechRecord, - selectTechRecordChanges, - selectTechRecordDeletions, - techRecord, updateADRAdditionalExaminerNotes, - updateTechRecord, - updateTechRecordSuccess, + clearADRDetailsBeforeUpdate, + clearAllSectionStates, + clearScrollPosition, + editingTechRecord, + selectTechRecordChanges, + selectTechRecordDeletions, + techRecord, + updateADRAdditionalExaminerNotes, + updateTechRecord, + updateTechRecordSuccess, } from '@store/technical-records'; -import { - Subject, combineLatest, map, take, takeUntil, -} from 'rxjs'; +import { Subject, combineLatest, map, take, takeUntil } from 'rxjs'; @Component({ - selector: 'app-tech-record-summary-changes', - templateUrl: './tech-record-summary-changes.component.html', - styleUrls: ['./tech-record-summary-changes.component.scss'], - changeDetection: ChangeDetectionStrategy.OnPush, + selector: 'app-tech-record-summary-changes', + templateUrl: './tech-record-summary-changes.component.html', + styleUrls: ['./tech-record-summary-changes.component.scss'], + changeDetection: ChangeDetectionStrategy.OnPush, }) export class TechRecordSummaryChangesComponent implements OnInit, OnDestroy { - destroy$ = new Subject(); - - techRecord?: TechRecordType<'get'>; - techRecordEdited?: TechRecordType<'put'>; - techRecordChanges?: Partial>; - techRecordDeletions?: Partial>; - techRecordChangesKeys: string[] = []; - - sectionsWhitelist: string[] = []; - username = ''; - - constructor( - public store$: Store, - public technicalRecordService: TechnicalRecordService, - public router: Router, - public globalErrorService: GlobalErrorService, - public route: ActivatedRoute, - public routerService: RouterService, - public actions$: Actions, - public userService$: UserService, - ) { } - - ngOnInit(): void { - this.navigateUponSuccess(); - this.initSubscriptions(); - } - - navigateUponSuccess(): void { - this.actions$.pipe(ofType(updateTechRecordSuccess), takeUntil(this.destroy$)).subscribe((vehicleTechRecord) => { - this.store$.dispatch(clearAllSectionStates()); - this.store$.dispatch(clearScrollPosition()); - void this.router.navigate([ - `/tech-records/${vehicleTechRecord.vehicleTechRecord.systemNumber}/${vehicleTechRecord.vehicleTechRecord.createdTimestamp}`, - ]); - }); - } - - initSubscriptions(): void { - this.userService$.name$.pipe(takeUntil(this.destroy$)).subscribe((name) => { - this.username = name; - }); - this.store$ - .select(techRecord) - .pipe(take(1), takeUntil(this.destroy$)) - .subscribe((data) => { - if (!data) this.cancel(); - this.techRecord = data; - }); - - this.store$ - .select(editingTechRecord) - .pipe(take(1), takeUntil(this.destroy$)) - .subscribe((data) => { - if (!data) this.cancel(); - this.techRecordEdited = data; - }); - - this.store$ - .select(selectTechRecordChanges) - .pipe(take(1), takeUntil(this.destroy$)) - .subscribe((changes) => { - this.techRecordChanges = changes; - if (this.vehicleType === VehicleTypes.PSV || this.vehicleType === VehicleTypes.HGV) { - delete (this.techRecordChanges as Partial).techRecord_numberOfWheelsDriven; - } if ( - (this.vehicleType === VehicleTypes.CAR || this.vehicleType === VehicleTypes.LGV) - && (changes as TechRecordGETCar | TechRecordGETLGV).techRecord_vehicleSubclass) { - (this.techRecordChanges as TechRecordGETCar | TechRecordGETLGV) - .techRecord_vehicleSubclass = (this.techRecordEdited as TechRecordGETCar | TechRecordGETLGV).techRecord_vehicleSubclass; - } - this.techRecordChangesKeys = this.getTechRecordChangesKeys(); - this.sectionsWhitelist = this.getSectionsWhitelist(); - }); - - this.store$ - .select(selectTechRecordDeletions) - .pipe(take(1), takeUntil(this.destroy$)) - .subscribe((deletions) => { - this.techRecordDeletions = deletions; - }); - } - - ngOnDestroy(): void { - this.destroy$.next(); - this.destroy$.complete(); - } - - get vehicleType() { - return this.techRecordEdited ? this.technicalRecordService.getVehicleTypeWithSmallTrl(this.techRecordEdited) : undefined; - } - - get vehicleSummary(): FormNode { - return VehicleSummary; - } - - get deletedAxles(): Axles { - if (this.techRecordEdited?.techRecord_vehicleType === 'hgv' && this.techRecordDeletions) { - return Object.values((this.techRecordDeletions as Partial).techRecord_axles ?? {}); - } - - if (this.techRecordEdited?.techRecord_vehicleType === 'trl' && this.techRecordDeletions) { - return Object.values((this.techRecordDeletions as Partial).techRecord_axles ?? {}); - } - - if (this.techRecordEdited?.techRecord_vehicleType === 'psv' && this.techRecordDeletions) { - return Object.values((this.techRecordDeletions as Partial).techRecord_axles ?? {}); - } - - return []; - } - - get sectionTemplatesState$() { - return this.technicalRecordService.sectionStates$; - } - - isSectionExpanded$(sectionName: string | number) { - return this.sectionTemplatesState$?.pipe(map((sections) => sections?.includes(sectionName))); - } - - submit() { - combineLatest([this.routerService.getRouteNestedParam$('systemNumber'), this.routerService.getRouteNestedParam$('createdTimestamp')]) - .pipe(take(1), takeUntil(this.destroy$)) - .subscribe(([systemNumber, createdTimestamp]) => { - if (systemNumber && createdTimestamp) { - this.store$.dispatch(updateADRAdditionalExaminerNotes({ username: this.username })); - this.store$.dispatch(clearADRDetailsBeforeUpdate()); - this.store$.dispatch(updateTechRecord({ systemNumber, createdTimestamp })); - } - }); - } - - cancel() { - this.globalErrorService.clearErrors(); - void this.router.navigate(['..'], { relativeTo: this.route }); - } - - getTechRecordChangesKeys(): string[] { - const entries = Object.entries(this.techRecordChanges ?? {}); - const filter = entries.filter(([, value]) => this.isNotEmpty(value)); - const changeMap = filter.map(([key]) => key); - return changeMap; - } - - getSectionsWhitelist() { - const whitelist: string[] = []; - if (this.vehicleType == null) return whitelist; - if (this.techRecordChanges == null) return whitelist; - if (this.technicalRecordService.haveAxlesChanged(this.vehicleType, this.techRecordChanges)) { - whitelist.push('weightsSection'); - } - - return whitelist; - } - - get changesForWeights() { - if (this.techRecordEdited == null) return undefined; - - return ['hgv', 'trl', 'psv'].includes(this.techRecordEdited.techRecord_vehicleType) - ? (this.techRecordChanges as Partial) - : undefined; - } - - get vehicleTemplates() { - return vehicleTemplateMap - .get(this.techRecordEdited?.techRecord_vehicleType as VehicleTypes) - ?.filter((template) => template.name !== 'technicalRecordSummary'); - } - - get customVehicleTemplate() { - return this.vehicleTemplates - ?.map((vehicleTemplate) => ({ - ...this.toVisibleFormNode(vehicleTemplate), - children: vehicleTemplate.children - ?.filter((child) => { - return this.techRecordChangesKeys.includes(child.name); - }) - .map((child) => this.toVisibleFormNode(child)), - })) - .filter((section) => Boolean(section && section.children && section.children.length > 0) || this.sectionsWhitelist.includes(section.name)); - } - - toVisibleFormNode(node: FormNode): FormNode { - return { ...node, viewType: node.viewType === FormNodeViewTypes.HIDDEN ? FormNodeViewTypes.STRING : node.viewType }; - } - - isNotEmpty(value: unknown): boolean { - if (value === '' || value === undefined) return false; - if (typeof value === 'object' && value !== null) return Object.values(value).length > 0; - return true; - } + destroy$ = new Subject(); + + techRecord?: TechRecordType<'get'>; + techRecordEdited?: TechRecordType<'put'>; + techRecordChanges?: Partial>; + techRecordDeletions?: Partial>; + techRecordChangesKeys: string[] = []; + + sectionsWhitelist: string[] = []; + username = ''; + + constructor( + public store$: Store, + public technicalRecordService: TechnicalRecordService, + public router: Router, + public globalErrorService: GlobalErrorService, + public route: ActivatedRoute, + public routerService: RouterService, + public actions$: Actions, + public userService$: UserService + ) {} + + ngOnInit(): void { + this.navigateUponSuccess(); + this.initSubscriptions(); + } + + navigateUponSuccess(): void { + this.actions$.pipe(ofType(updateTechRecordSuccess), takeUntil(this.destroy$)).subscribe((vehicleTechRecord) => { + this.store$.dispatch(clearAllSectionStates()); + this.store$.dispatch(clearScrollPosition()); + void this.router.navigate([ + `/tech-records/${vehicleTechRecord.vehicleTechRecord.systemNumber}/${vehicleTechRecord.vehicleTechRecord.createdTimestamp}`, + ]); + }); + } + + initSubscriptions(): void { + this.userService$.name$.pipe(takeUntil(this.destroy$)).subscribe((name) => { + this.username = name; + }); + this.store$ + .select(techRecord) + .pipe(take(1), takeUntil(this.destroy$)) + .subscribe((data) => { + if (!data) this.cancel(); + this.techRecord = data; + }); + + this.store$ + .select(editingTechRecord) + .pipe(take(1), takeUntil(this.destroy$)) + .subscribe((data) => { + if (!data) this.cancel(); + this.techRecordEdited = data; + }); + + this.store$ + .select(selectTechRecordChanges) + .pipe(take(1), takeUntil(this.destroy$)) + .subscribe((changes) => { + this.techRecordChanges = changes; + if (this.vehicleType === VehicleTypes.PSV || this.vehicleType === VehicleTypes.HGV) { + delete (this.techRecordChanges as Partial) + .techRecord_numberOfWheelsDriven; + } + if ( + (this.vehicleType === VehicleTypes.CAR || this.vehicleType === VehicleTypes.LGV) && + (changes as TechRecordGETCar | TechRecordGETLGV).techRecord_vehicleSubclass + ) { + (this.techRecordChanges as TechRecordGETCar | TechRecordGETLGV).techRecord_vehicleSubclass = ( + this.techRecordEdited as TechRecordGETCar | TechRecordGETLGV + ).techRecord_vehicleSubclass; + } + this.techRecordChangesKeys = this.getTechRecordChangesKeys(); + this.sectionsWhitelist = this.getSectionsWhitelist(); + }); + + this.store$ + .select(selectTechRecordDeletions) + .pipe(take(1), takeUntil(this.destroy$)) + .subscribe((deletions) => { + this.techRecordDeletions = deletions; + }); + } + + ngOnDestroy(): void { + this.destroy$.next(); + this.destroy$.complete(); + } + + get vehicleType() { + return this.techRecordEdited + ? this.technicalRecordService.getVehicleTypeWithSmallTrl(this.techRecordEdited) + : undefined; + } + + get vehicleSummary(): FormNode { + return VehicleSummary; + } + + get deletedAxles(): Axles { + if (this.techRecordEdited?.techRecord_vehicleType === 'hgv' && this.techRecordDeletions) { + return Object.values((this.techRecordDeletions as Partial).techRecord_axles ?? {}); + } + + if (this.techRecordEdited?.techRecord_vehicleType === 'trl' && this.techRecordDeletions) { + return Object.values((this.techRecordDeletions as Partial).techRecord_axles ?? {}); + } + + if (this.techRecordEdited?.techRecord_vehicleType === 'psv' && this.techRecordDeletions) { + return Object.values((this.techRecordDeletions as Partial).techRecord_axles ?? {}); + } + + return []; + } + + get sectionTemplatesState$() { + return this.technicalRecordService.sectionStates$; + } + + isSectionExpanded$(sectionName: string | number) { + return this.sectionTemplatesState$?.pipe(map((sections) => sections?.includes(sectionName))); + } + + submit() { + combineLatest([ + this.routerService.getRouteNestedParam$('systemNumber'), + this.routerService.getRouteNestedParam$('createdTimestamp'), + ]) + .pipe(take(1), takeUntil(this.destroy$)) + .subscribe(([systemNumber, createdTimestamp]) => { + if (systemNumber && createdTimestamp) { + this.store$.dispatch(updateADRAdditionalExaminerNotes({ username: this.username })); + this.store$.dispatch(clearADRDetailsBeforeUpdate()); + this.store$.dispatch(updateTechRecord({ systemNumber, createdTimestamp })); + } + }); + } + + cancel() { + this.globalErrorService.clearErrors(); + void this.router.navigate(['..'], { relativeTo: this.route }); + } + + getTechRecordChangesKeys(): string[] { + const entries = Object.entries(this.techRecordChanges ?? {}); + const filter = entries.filter(([, value]) => this.isNotEmpty(value)); + const changeMap = filter.map(([key]) => key); + return changeMap; + } + + getSectionsWhitelist() { + const whitelist: string[] = []; + if (this.vehicleType == null) return whitelist; + if (this.techRecordChanges == null) return whitelist; + if (this.technicalRecordService.haveAxlesChanged(this.vehicleType, this.techRecordChanges)) { + whitelist.push('weightsSection'); + } + + return whitelist; + } + + get changesForWeights() { + if (this.techRecordEdited == null) return undefined; + + return ['hgv', 'trl', 'psv'].includes(this.techRecordEdited.techRecord_vehicleType) + ? (this.techRecordChanges as Partial) + : undefined; + } + + get vehicleTemplates() { + return vehicleTemplateMap + .get(this.techRecordEdited?.techRecord_vehicleType as VehicleTypes) + ?.filter((template) => template.name !== 'technicalRecordSummary'); + } + + get customVehicleTemplate() { + return this.vehicleTemplates + ?.map((vehicleTemplate) => ({ + ...this.toVisibleFormNode(vehicleTemplate), + children: vehicleTemplate.children + ?.filter((child) => { + return this.techRecordChangesKeys.includes(child.name); + }) + .map((child) => this.toVisibleFormNode(child)), + })) + .filter( + (section) => + Boolean(section && section.children && section.children.length > 0) || + this.sectionsWhitelist.includes(section.name) + ); + } + + toVisibleFormNode(node: FormNode): FormNode { + return { ...node, viewType: node.viewType === FormNodeViewTypes.HIDDEN ? FormNodeViewTypes.STRING : node.viewType }; + } + + isNotEmpty(value: unknown): boolean { + if (value === '' || value === undefined) return false; + if (typeof value === 'object' && value !== null) return Object.values(value).length > 0; + return true; + } } diff --git a/src/app/features/tech-record/components/tech-record-summary/tech-record-summary.component.spec.ts b/src/app/features/tech-record/components/tech-record-summary/tech-record-summary.component.spec.ts index 0b22a7c87b..247eecb5c4 100644 --- a/src/app/features/tech-record/components/tech-record-summary/tech-record-summary.component.spec.ts +++ b/src/app/features/tech-record/components/tech-record-summary/tech-record-summary.component.spec.ts @@ -13,219 +13,222 @@ import { TechRecordType } from '@dvsa/cvs-type-definitions/types/v3/tech-record/ import { Roles } from '@models/roles.enum'; import { V3TechRecordModel, VehicleTypes } from '@models/vehicle-tech-record.model'; import { MockStore, provideMockStore } from '@ngrx/store/testing'; +import { FeatureToggleService } from '@services/feature-toggle-service/feature-toggle-service'; import { TechnicalRecordService } from '@services/technical-record/technical-record.service'; import { UserService } from '@services/user-service/user-service'; import { SharedModule } from '@shared/shared.module'; import { State, initialAppState } from '@store/index'; import { updateEditingTechRecord } from '@store/technical-records'; import { of } from 'rxjs'; -import { FeatureToggleService } from '@services/feature-toggle-service/feature-toggle-service'; import { TechRecordSummaryComponent } from './tech-record-summary.component'; global.scrollTo = jest.fn(); describe('TechRecordSummaryComponent', () => { - let component: TechRecordSummaryComponent; - let fixture: ComponentFixture; - let store: MockStore; - let techRecordService: TechnicalRecordService; - let featureToggleService: FeatureToggleService; - - beforeEach(async () => { - await TestBed.configureTestingModule({ - declarations: [TechRecordSummaryComponent], - imports: [DynamicFormsModule, HttpClientTestingModule, RouterTestingModule, SharedModule], - providers: [ - MultiOptionsService, - provideMockStore({ initialState: initialAppState }), - { - provide: UserService, - useValue: { - roles$: of([Roles.TechRecordAmend]), - }, - }, - TechnicalRecordService, - FeatureToggleService, - ], - }) - .overrideComponent(LettersComponent, { - set: { - selector: 'app-letters', - template: '

Mock Letters Component

', - }, - }) - .compileComponents(); - }); - - beforeEach(() => { - fixture = TestBed.createComponent(TechRecordSummaryComponent); - store = TestBed.inject(MockStore); - techRecordService = TestBed.inject(TechnicalRecordService); - featureToggleService = TestBed.inject(FeatureToggleService); - component = fixture.componentInstance; - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); - - function checkHeadingAndForm(): void { - const heading = fixture.debugElement.query(By.css('.govuk-heading-s')); - expect(heading).toBeFalsy(); - - const form = fixture.nativeElement.querySelector('app-dynamic-form-group'); - expect(form).toBeTruthy(); - } - - describe('TechRecordSummaryComponent View', () => { - it('should show PSV record found', () => { - component.isEditing = false; - jest - .spyOn(techRecordService, 'techRecord$', 'get') - .mockReturnValue( - of({ - systemNumber: 'foo', createdTimestamp: 'bar', vin: 'testVin', techRecord_vehicleType: VehicleTypes.PSV, - } as V3TechRecordModel), - ); - fixture.detectChanges(); - checkHeadingAndForm(); - expect(component.vehicleType).toEqual(VehicleTypes.PSV); - }); - - it('should show PSV record found without dimensions', () => { - component.isEditing = false; - jest - .spyOn(techRecordService, 'techRecord$', 'get') - .mockReturnValue( - of({ - systemNumber: 'foo', createdTimestamp: 'bar', vin: 'testVin', techRecord_vehicleType: VehicleTypes.PSV, - } as V3TechRecordModel), - ); - fixture.detectChanges(); - - checkHeadingAndForm(); - expect((component.techRecordCalculated as TechRecordTypeByVehicle<'psv'>).techRecord_dimensions_height).toBeUndefined(); - }); - - it('should show HGV record found', () => { - component.isEditing = false; - jest - .spyOn(techRecordService, 'techRecord$', 'get') - .mockReturnValue( - of({ - systemNumber: 'foo', createdTimestamp: 'bar', vin: 'testVin', techRecord_vehicleType: VehicleTypes.HGV, - } as V3TechRecordModel), - ); - fixture.detectChanges(); - - checkHeadingAndForm(); - expect(component.vehicleType).toEqual(VehicleTypes.HGV); - }); - - it('should show HGV record found without dimensions', () => { - component.isEditing = false; - jest - .spyOn(techRecordService, 'techRecord$', 'get') - .mockReturnValue( - of({ - systemNumber: 'foo', createdTimestamp: 'bar', vin: 'testVin', techRecord_vehicleType: VehicleTypes.HGV, - } as V3TechRecordModel), - ); - fixture.detectChanges(); - - checkHeadingAndForm(); - expect(component.vehicleType).toEqual(VehicleTypes.HGV); - }); - - it('should show TRL record found', () => { - component.isEditing = false; - jest.spyOn(techRecordService, 'techRecord$', 'get').mockReturnValue( - of({ - systemNumber: 'foo', - createdTimestamp: 'bar', - vin: 'testVin', - techRecord_vehicleType: VehicleTypes.TRL, - techRecord_euVehicleCategory: 'o2', - } as V3TechRecordModel), - ); - fixture.detectChanges(); - - checkHeadingAndForm(); - expect(component.vehicleType).toEqual(VehicleTypes.SMALL_TRL); - }); - - it('should show TRL record found without dimensions', () => { - component.isEditing = false; - jest - .spyOn(techRecordService, 'techRecord$', 'get') - .mockReturnValue( - of({ - systemNumber: 'foo', createdTimestamp: 'bar', vin: 'testVin', techRecord_vehicleType: VehicleTypes.TRL, - } as V3TechRecordModel), - ); - fixture.detectChanges(); - - checkHeadingAndForm(); - expect(component.vehicleType).toEqual(VehicleTypes.TRL); - }); - it('should show adr section if ADR is enabled', () => { - component.isEditing = false; - jest.spyOn(featureToggleService, 'isFeatureEnabled').mockReturnValue(true); - jest - .spyOn(techRecordService, 'techRecord$', 'get') - .mockReturnValue( - of({ - systemNumber: 'foo', - createdTimestamp: 'bar', - vin: 'testVin', - techRecord_vehicleType: VehicleTypes.HGV, - techRecord_adrDetails_dangerousGoods: true, - techRecord_adrDetails_applicantDetails_name: 'Test', - } as V3TechRecordModel), - ); - fixture.detectChanges(); - - checkHeadingAndForm(); - expect(component.adr).toBeDefined(); - }); - it('should not show adr section if ADR is disabled', () => { - component.isEditing = false; - jest.spyOn(featureToggleService, 'isFeatureEnabled').mockReturnValue(false); - jest - .spyOn(techRecordService, 'techRecord$', 'get') - .mockReturnValue( - of({ - systemNumber: 'foo', - createdTimestamp: 'bar', - vin: 'testVin', - techRecord_vehicleType: VehicleTypes.TRL, - techRecord_adrDetails_applicantDetails_name: 'Test', - } as V3TechRecordModel), - ); - fixture.detectChanges(); - - checkHeadingAndForm(); - expect(component.adr).toBeUndefined(); - }); - }); - - describe('handleFormState', () => { - it('should dispatch updateEditingTechRecord', () => { - jest.spyOn(component, 'checkForms').mockImplementation(); - const dispatchSpy = jest.spyOn(store, 'dispatch'); - const mockTechRecord = { - systemNumber: 'foo', - createdTimestamp: 'bar', - vin: 'testVin', - techRecord_vehicleType: VehicleTypes.LGV, - } as unknown as TechRecordType<'put'>; - component.techRecordCalculated = mockTechRecord; - jest.spyOn(store, 'select').mockReturnValue(of(mockTechRecord)); - component.sections = new QueryList(); - - component.handleFormState({}); - - expect(dispatchSpy).toHaveBeenCalledWith(updateEditingTechRecord({ vehicleTechRecord: mockTechRecord })); - }); - }); + let component: TechRecordSummaryComponent; + let fixture: ComponentFixture; + let store: MockStore; + let techRecordService: TechnicalRecordService; + let featureToggleService: FeatureToggleService; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [TechRecordSummaryComponent], + imports: [DynamicFormsModule, HttpClientTestingModule, RouterTestingModule, SharedModule], + providers: [ + MultiOptionsService, + provideMockStore({ initialState: initialAppState }), + { + provide: UserService, + useValue: { + roles$: of([Roles.TechRecordAmend]), + }, + }, + TechnicalRecordService, + FeatureToggleService, + ], + }) + .overrideComponent(LettersComponent, { + set: { + selector: 'app-letters', + template: '

Mock Letters Component

', + }, + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(TechRecordSummaryComponent); + store = TestBed.inject(MockStore); + techRecordService = TestBed.inject(TechnicalRecordService); + featureToggleService = TestBed.inject(FeatureToggleService); + component = fixture.componentInstance; + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + function checkHeadingAndForm(): void { + const heading = fixture.debugElement.query(By.css('.govuk-heading-s')); + expect(heading).toBeFalsy(); + + const form = fixture.nativeElement.querySelector('app-dynamic-form-group'); + expect(form).toBeTruthy(); + } + + describe('TechRecordSummaryComponent View', () => { + it('should show PSV record found', () => { + component.isEditing = false; + jest.spyOn(techRecordService, 'techRecord$', 'get').mockReturnValue( + of({ + systemNumber: 'foo', + createdTimestamp: 'bar', + vin: 'testVin', + techRecord_vehicleType: VehicleTypes.PSV, + } as V3TechRecordModel) + ); + fixture.detectChanges(); + checkHeadingAndForm(); + expect(component.vehicleType).toEqual(VehicleTypes.PSV); + }); + + it('should show PSV record found without dimensions', () => { + component.isEditing = false; + jest.spyOn(techRecordService, 'techRecord$', 'get').mockReturnValue( + of({ + systemNumber: 'foo', + createdTimestamp: 'bar', + vin: 'testVin', + techRecord_vehicleType: VehicleTypes.PSV, + } as V3TechRecordModel) + ); + fixture.detectChanges(); + + checkHeadingAndForm(); + expect( + (component.techRecordCalculated as TechRecordTypeByVehicle<'psv'>).techRecord_dimensions_height + ).toBeUndefined(); + }); + + it('should show HGV record found', () => { + component.isEditing = false; + jest.spyOn(techRecordService, 'techRecord$', 'get').mockReturnValue( + of({ + systemNumber: 'foo', + createdTimestamp: 'bar', + vin: 'testVin', + techRecord_vehicleType: VehicleTypes.HGV, + } as V3TechRecordModel) + ); + fixture.detectChanges(); + + checkHeadingAndForm(); + expect(component.vehicleType).toEqual(VehicleTypes.HGV); + }); + + it('should show HGV record found without dimensions', () => { + component.isEditing = false; + jest.spyOn(techRecordService, 'techRecord$', 'get').mockReturnValue( + of({ + systemNumber: 'foo', + createdTimestamp: 'bar', + vin: 'testVin', + techRecord_vehicleType: VehicleTypes.HGV, + } as V3TechRecordModel) + ); + fixture.detectChanges(); + + checkHeadingAndForm(); + expect(component.vehicleType).toEqual(VehicleTypes.HGV); + }); + + it('should show TRL record found', () => { + component.isEditing = false; + jest.spyOn(techRecordService, 'techRecord$', 'get').mockReturnValue( + of({ + systemNumber: 'foo', + createdTimestamp: 'bar', + vin: 'testVin', + techRecord_vehicleType: VehicleTypes.TRL, + techRecord_euVehicleCategory: 'o2', + } as V3TechRecordModel) + ); + fixture.detectChanges(); + + checkHeadingAndForm(); + expect(component.vehicleType).toEqual(VehicleTypes.SMALL_TRL); + }); + + it('should show TRL record found without dimensions', () => { + component.isEditing = false; + jest.spyOn(techRecordService, 'techRecord$', 'get').mockReturnValue( + of({ + systemNumber: 'foo', + createdTimestamp: 'bar', + vin: 'testVin', + techRecord_vehicleType: VehicleTypes.TRL, + } as V3TechRecordModel) + ); + fixture.detectChanges(); + + checkHeadingAndForm(); + expect(component.vehicleType).toEqual(VehicleTypes.TRL); + }); + it('should show adr section if ADR is enabled', () => { + component.isEditing = false; + jest.spyOn(featureToggleService, 'isFeatureEnabled').mockReturnValue(true); + jest.spyOn(techRecordService, 'techRecord$', 'get').mockReturnValue( + of({ + systemNumber: 'foo', + createdTimestamp: 'bar', + vin: 'testVin', + techRecord_vehicleType: VehicleTypes.HGV, + techRecord_adrDetails_dangerousGoods: true, + techRecord_adrDetails_applicantDetails_name: 'Test', + } as V3TechRecordModel) + ); + fixture.detectChanges(); + + checkHeadingAndForm(); + expect(component.adr).toBeDefined(); + }); + it('should not show adr section if ADR is disabled', () => { + component.isEditing = false; + jest.spyOn(featureToggleService, 'isFeatureEnabled').mockReturnValue(false); + jest.spyOn(techRecordService, 'techRecord$', 'get').mockReturnValue( + of({ + systemNumber: 'foo', + createdTimestamp: 'bar', + vin: 'testVin', + techRecord_vehicleType: VehicleTypes.TRL, + techRecord_adrDetails_applicantDetails_name: 'Test', + } as V3TechRecordModel) + ); + fixture.detectChanges(); + + checkHeadingAndForm(); + expect(component.adr).toBeUndefined(); + }); + }); + + describe('handleFormState', () => { + it('should dispatch updateEditingTechRecord', () => { + jest.spyOn(component, 'checkForms').mockImplementation(); + const dispatchSpy = jest.spyOn(store, 'dispatch'); + const mockTechRecord = { + systemNumber: 'foo', + createdTimestamp: 'bar', + vin: 'testVin', + techRecord_vehicleType: VehicleTypes.LGV, + } as unknown as TechRecordType<'put'>; + component.techRecordCalculated = mockTechRecord; + jest.spyOn(store, 'select').mockReturnValue(of(mockTechRecord)); + component.sections = new QueryList(); + + component.handleFormState({}); + + expect(dispatchSpy).toHaveBeenCalledWith(updateEditingTechRecord({ vehicleTechRecord: mockTechRecord })); + }); + }); }); diff --git a/src/app/features/tech-record/components/tech-record-summary/tech-record-summary.component.ts b/src/app/features/tech-record/components/tech-record-summary/tech-record-summary.component.ts index 89b2ce5759..a96a974f24 100644 --- a/src/app/features/tech-record/components/tech-record-summary/tech-record-summary.component.ts +++ b/src/app/features/tech-record/components/tech-record-summary/tech-record-summary.component.ts @@ -1,14 +1,14 @@ import { ViewportScroller } from '@angular/common'; import { - ChangeDetectionStrategy, - Component, - EventEmitter, - OnDestroy, - OnInit, - Output, - QueryList, - ViewChild, - ViewChildren, + ChangeDetectionStrategy, + Component, + EventEmitter, + OnDestroy, + OnInit, + Output, + QueryList, + ViewChild, + ViewChildren, } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { GlobalError } from '@core/components/global-error/global-error.interface'; @@ -29,9 +29,7 @@ import { WeightsComponent } from '@forms/custom-sections/weights/weights.compone import { DynamicFormService } from '@forms/services/dynamic-form.service'; import { CustomFormArray, CustomFormGroup, FormNode } from '@forms/services/dynamic-form.types'; import { vehicleTemplateMap } from '@forms/utils/tech-record-constants'; -import { - ReasonForEditing, StatusCodes, V3TechRecordModel, VehicleTypes, -} from '@models/vehicle-tech-record.model'; +import { ReasonForEditing, StatusCodes, V3TechRecordModel, VehicleTypes } from '@models/vehicle-tech-record.model'; import { Store } from '@ngrx/store'; import { AxlesService } from '@services/axles/axles.service'; import { FeatureToggleService } from '@services/feature-toggle-service/feature-toggle-service'; @@ -41,228 +39,229 @@ import { RouterService } from '@services/router/router.service'; import { TechnicalRecordService } from '@services/technical-record/technical-record.service'; import { selectScrollPosition } from '@store/technical-records'; import { cloneDeep, mergeWith } from 'lodash'; -import { - Observable, Subject, - debounceTime, map, - take, takeUntil, -} from 'rxjs'; +import { Observable, Subject, debounceTime, map, take, takeUntil } from 'rxjs'; @Component({ - selector: 'app-tech-record-summary', - templateUrl: './tech-record-summary.component.html', - changeDetection: ChangeDetectionStrategy.OnPush, - styleUrls: ['./tech-record-summary.component.scss'], + selector: 'app-tech-record-summary', + templateUrl: './tech-record-summary.component.html', + changeDetection: ChangeDetectionStrategy.OnPush, + styleUrls: ['./tech-record-summary.component.scss'], }) export class TechRecordSummaryComponent implements OnInit, OnDestroy { - @ViewChildren(DynamicFormGroupComponent) sections!: QueryList; - @ViewChild(BodyComponent) body!: BodyComponent; - @ViewChild(DimensionsComponent) dimensions!: DimensionsComponent; - @ViewChild(PsvBrakesComponent) psvBrakes!: PsvBrakesComponent; - @ViewChild(TrlBrakesComponent) trlBrakes!: TrlBrakesComponent; - @ViewChild(TyresComponent) tyres!: TyresComponent; - @ViewChild(WeightsComponent) weights!: WeightsComponent; - @ViewChild(LettersComponent) letters!: LettersComponent; - @ViewChild(ApprovalTypeComponent) approvalType!: ApprovalTypeComponent; - @ViewChild(AdrComponent) adr!: AdrComponent; - - @Output() isFormDirty = new EventEmitter(); - @Output() isFormInvalid = new EventEmitter(); - - techRecordCalculated?: V3TechRecordModel; - sectionTemplates: Array = []; - middleIndex = 0; - isEditing = false; - scrollPosition: [number, number] = [0, 0]; - isADREnabled = false; - isADRCertGenEnabled = false; - - private destroy$ = new Subject(); - - constructor( - private axlesService: AxlesService, - private errorService: GlobalErrorService, - private warningService: GlobalWarningService, - private referenceDataService: ReferenceDataService, - private technicalRecordService: TechnicalRecordService, - private routerService: RouterService, - private activatedRoute: ActivatedRoute, - private viewportScroller: ViewportScroller, - private store: Store, - private loading: LoadingService, - private featureToggleService: FeatureToggleService, - ) { } - - ngOnInit(): void { - this.isADREnabled = this.featureToggleService.isFeatureEnabled('adrToggle'); - this.isADRCertGenEnabled = this.featureToggleService.isFeatureEnabled('adrCertToggle'); - this.technicalRecordService.techRecord$ - .pipe( - map((record) => { - if (!record) { - return; - } - - let techRecord = cloneDeep(record); - techRecord = this.normaliseAxles(record); - - return techRecord; - }), - takeUntil(this.destroy$), - ) - .subscribe((techRecord) => { - if (techRecord) { - this.techRecordCalculated = techRecord; - } - this.referenceDataService.removeTyreSearch(); - this.sectionTemplates = this.vehicleTemplates; - this.middleIndex = Math.floor(this.sectionTemplates.length / 2); - }); - - const editingReason = this.activatedRoute.snapshot.data['reason']; - if (this.isEditing) { - this.technicalRecordService.clearReasonForCreation(); - this.technicalRecordService.techRecord$.pipe(takeUntil(this.destroy$), take(1)).subscribe((techRecord) => { - if (techRecord) { - if (editingReason === ReasonForEditing.NOTIFIABLE_ALTERATION_NEEDED) { - this.technicalRecordService.updateEditingTechRecord({ - ...(techRecord as TechRecordType<'put'>), - techRecord_statusCode: StatusCodes.PROVISIONAL, - }); - } - - if (techRecord?.vin?.match('([IOQ])a*')) { - const warnings: GlobalWarning[] = []; - warnings.push({ warning: 'VIN should not contain I, O or Q', anchorLink: 'vin' }); - this.warningService.setWarnings(warnings); - } - } - }); - } else if (!this.isEditing) { - this.warningService.clearWarnings(); - } - - this.store - .select(selectScrollPosition) - .pipe(take(1), takeUntil(this.destroy$)) - .subscribe((position) => { - this.scrollPosition = position; - }); - - this.loading.showSpinner$.pipe(takeUntil(this.destroy$), debounceTime(10)).subscribe((loading) => { - if (!loading) { - this.viewportScroller.scrollToPosition(this.scrollPosition); - } - }); - } - - ngOnDestroy(): void { - this.destroy$.next(); - this.destroy$.complete(); - } - - get vehicleType() { - return this.techRecordCalculated ? this.technicalRecordService.getVehicleTypeWithSmallTrl(this.techRecordCalculated) : undefined; - } - - get vehicleTemplates(): Array { - this.isEditing$.pipe(takeUntil(this.destroy$)).subscribe((editing) => { - this.isEditing = editing; - }); - if (!this.vehicleType) { - return []; - } - return ( - vehicleTemplateMap.get(this.vehicleType)?.filter((template) => template.name !== (this.isEditing ? 'audit' : 'reasonForCreationSection')) - .filter((template) => template.name !== (this.isADREnabled ? '' : 'adrSection')) - .filter((template) => template.name !== (this.isADRCertGenEnabled ? '' : 'adrCertificateSection')) - ?? [] - ); - } - - get sectionTemplatesState$() { - return this.technicalRecordService.sectionStates$; - } - - isSectionExpanded$(sectionName: string | number) { - return this.sectionTemplatesState$?.pipe(map((sections) => sections?.includes(sectionName))); - } - - get isEditing$(): Observable { - return this.routerService.getRouteDataProperty$('isEditing').pipe(map((isEditing) => !!isEditing)); - } - - get hint(): string { - return 'Complete all required fields to create a testable record'; - } - - get customSectionForms(): Array { - const commonCustomSections = [ - this.body?.form, - this.dimensions?.form, - this.tyres?.form, - this.weights?.form, - this.approvalType?.form, - ]; - - switch (this.vehicleType) { - case VehicleTypes.PSV: - return [...commonCustomSections, this.psvBrakes.form]; - case VehicleTypes.HGV: - return this.isADREnabled ? [...commonCustomSections, this.adr.form] : commonCustomSections; - case VehicleTypes.TRL: - return this.isADREnabled ? [...commonCustomSections, this.trlBrakes.form, this.letters.form, this.adr.form] - : [...commonCustomSections, this.trlBrakes.form, this.letters.form]; - case VehicleTypes.LGV: - return this.isADREnabled ? [this.adr.form] : []; - default: - return []; - } - } - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - handleFormState(event: any): void { - const isPrimitiveArray = (a: unknown, b: unknown) => (Array.isArray(a) && !a.some((i) => typeof i === 'object') ? b : undefined); - - this.techRecordCalculated = mergeWith(cloneDeep(this.techRecordCalculated), event, isPrimitiveArray); - this.technicalRecordService.updateEditingTechRecord(this.techRecordCalculated as TechRecordType<'put'>); - } - - checkForms(): void { - const forms = this.sections?.map((section) => section.form).concat(this.customSectionForms); - - this.isFormDirty.emit(forms.some((form) => form.dirty)); - - this.setErrors(forms); - - this.isFormInvalid.emit(forms.some((form) => form.invalid)); - } - - setErrors(forms: Array): void { - const errors: GlobalError[] = []; - - forms.forEach((form) => DynamicFormService.validate(form, errors)); - - if (errors.length) { - this.errorService.setErrors(errors); - } else { - this.errorService.clearErrors(); - } - } - - private normaliseAxles(record: V3TechRecordModel): V3TechRecordModel { - const type = record.techRecord_vehicleType; - const category = record.techRecord_euVehicleCategory; - - if (type === VehicleTypes.HGV || (type === VehicleTypes.TRL && category !== 'o1' && category !== 'o2')) { - const [axles, axleSpacing] = this.axlesService.normaliseAxles( - record.techRecord_axles ?? [], - record.techRecord_dimensions_axleSpacing, - ); - - record.techRecord_dimensions_axleSpacing = axleSpacing; - record.techRecord_axles = axles; - } - - return record; - } + @ViewChildren(DynamicFormGroupComponent) sections!: QueryList; + @ViewChild(BodyComponent) body!: BodyComponent; + @ViewChild(DimensionsComponent) dimensions!: DimensionsComponent; + @ViewChild(PsvBrakesComponent) psvBrakes!: PsvBrakesComponent; + @ViewChild(TrlBrakesComponent) trlBrakes!: TrlBrakesComponent; + @ViewChild(TyresComponent) tyres!: TyresComponent; + @ViewChild(WeightsComponent) weights!: WeightsComponent; + @ViewChild(LettersComponent) letters!: LettersComponent; + @ViewChild(ApprovalTypeComponent) approvalType!: ApprovalTypeComponent; + @ViewChild(AdrComponent) adr!: AdrComponent; + + @Output() isFormDirty = new EventEmitter(); + @Output() isFormInvalid = new EventEmitter(); + + techRecordCalculated?: V3TechRecordModel; + sectionTemplates: Array = []; + middleIndex = 0; + isEditing = false; + scrollPosition: [number, number] = [0, 0]; + isADREnabled = false; + isADRCertGenEnabled = false; + + private destroy$ = new Subject(); + + constructor( + private axlesService: AxlesService, + private errorService: GlobalErrorService, + private warningService: GlobalWarningService, + private referenceDataService: ReferenceDataService, + private technicalRecordService: TechnicalRecordService, + private routerService: RouterService, + private activatedRoute: ActivatedRoute, + private viewportScroller: ViewportScroller, + private store: Store, + private loading: LoadingService, + private featureToggleService: FeatureToggleService + ) {} + + ngOnInit(): void { + this.isADREnabled = this.featureToggleService.isFeatureEnabled('adrToggle'); + this.isADRCertGenEnabled = this.featureToggleService.isFeatureEnabled('adrCertToggle'); + this.technicalRecordService.techRecord$ + .pipe( + map((record) => { + if (!record) { + return; + } + + let techRecord = cloneDeep(record); + techRecord = this.normaliseAxles(record); + + return techRecord; + }), + takeUntil(this.destroy$) + ) + .subscribe((techRecord) => { + if (techRecord) { + this.techRecordCalculated = techRecord; + } + this.referenceDataService.removeTyreSearch(); + this.sectionTemplates = this.vehicleTemplates; + this.middleIndex = Math.floor(this.sectionTemplates.length / 2); + }); + + const editingReason = this.activatedRoute.snapshot.data['reason']; + if (this.isEditing) { + this.technicalRecordService.clearReasonForCreation(); + this.technicalRecordService.techRecord$.pipe(takeUntil(this.destroy$), take(1)).subscribe((techRecord) => { + if (techRecord) { + if (editingReason === ReasonForEditing.NOTIFIABLE_ALTERATION_NEEDED) { + this.technicalRecordService.updateEditingTechRecord({ + ...(techRecord as TechRecordType<'put'>), + techRecord_statusCode: StatusCodes.PROVISIONAL, + }); + } + + if (techRecord?.vin?.match('([IOQ])a*')) { + const warnings: GlobalWarning[] = []; + warnings.push({ warning: 'VIN should not contain I, O or Q', anchorLink: 'vin' }); + this.warningService.setWarnings(warnings); + } + } + }); + } else if (!this.isEditing) { + this.warningService.clearWarnings(); + } + + this.store + .select(selectScrollPosition) + .pipe(take(1), takeUntil(this.destroy$)) + .subscribe((position) => { + this.scrollPosition = position; + }); + + this.loading.showSpinner$.pipe(takeUntil(this.destroy$), debounceTime(10)).subscribe((loading) => { + if (!loading) { + this.viewportScroller.scrollToPosition(this.scrollPosition); + } + }); + } + + ngOnDestroy(): void { + this.destroy$.next(); + this.destroy$.complete(); + } + + get vehicleType() { + return this.techRecordCalculated + ? this.technicalRecordService.getVehicleTypeWithSmallTrl(this.techRecordCalculated) + : undefined; + } + + get vehicleTemplates(): Array { + this.isEditing$.pipe(takeUntil(this.destroy$)).subscribe((editing) => { + this.isEditing = editing; + }); + if (!this.vehicleType) { + return []; + } + return ( + vehicleTemplateMap + .get(this.vehicleType) + ?.filter((template) => template.name !== (this.isEditing ? 'audit' : 'reasonForCreationSection')) + .filter((template) => template.name !== (this.isADREnabled ? '' : 'adrSection')) + .filter((template) => template.name !== (this.isADRCertGenEnabled ? '' : 'adrCertificateSection')) ?? [] + ); + } + + get sectionTemplatesState$() { + return this.technicalRecordService.sectionStates$; + } + + isSectionExpanded$(sectionName: string | number) { + return this.sectionTemplatesState$?.pipe(map((sections) => sections?.includes(sectionName))); + } + + get isEditing$(): Observable { + return this.routerService.getRouteDataProperty$('isEditing').pipe(map((isEditing) => !!isEditing)); + } + + get hint(): string { + return 'Complete all required fields to create a testable record'; + } + + get customSectionForms(): Array { + const commonCustomSections = [ + this.body?.form, + this.dimensions?.form, + this.tyres?.form, + this.weights?.form, + this.approvalType?.form, + ]; + + switch (this.vehicleType) { + case VehicleTypes.PSV: + return [...commonCustomSections, this.psvBrakes.form]; + case VehicleTypes.HGV: + return this.isADREnabled ? [...commonCustomSections, this.adr.form] : commonCustomSections; + case VehicleTypes.TRL: + return this.isADREnabled + ? [...commonCustomSections, this.trlBrakes.form, this.letters.form, this.adr.form] + : [...commonCustomSections, this.trlBrakes.form, this.letters.form]; + case VehicleTypes.LGV: + return this.isADREnabled ? [this.adr.form] : []; + default: + return []; + } + } + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + handleFormState(event: any): void { + const isPrimitiveArray = (a: unknown, b: unknown) => + Array.isArray(a) && !a.some((i) => typeof i === 'object') ? b : undefined; + + this.techRecordCalculated = mergeWith(cloneDeep(this.techRecordCalculated), event, isPrimitiveArray); + this.technicalRecordService.updateEditingTechRecord(this.techRecordCalculated as TechRecordType<'put'>); + } + + checkForms(): void { + const forms = this.sections?.map((section) => section.form).concat(this.customSectionForms); + + this.isFormDirty.emit(forms.some((form) => form.dirty)); + + this.setErrors(forms); + + this.isFormInvalid.emit(forms.some((form) => form.invalid)); + } + + setErrors(forms: Array): void { + const errors: GlobalError[] = []; + + forms.forEach((form) => DynamicFormService.validate(form, errors)); + + if (errors.length) { + this.errorService.setErrors(errors); + } else { + this.errorService.clearErrors(); + } + } + + private normaliseAxles(record: V3TechRecordModel): V3TechRecordModel { + const type = record.techRecord_vehicleType; + const category = record.techRecord_euVehicleCategory; + + if (type === VehicleTypes.HGV || (type === VehicleTypes.TRL && category !== 'o1' && category !== 'o2')) { + const [axles, axleSpacing] = this.axlesService.normaliseAxles( + record.techRecord_axles ?? [], + record.techRecord_dimensions_axleSpacing + ); + + record.techRecord_dimensions_axleSpacing = axleSpacing; + record.techRecord_axles = axles; + } + + return record; + } } diff --git a/src/app/features/tech-record/components/tech-record-title/tech-record-title.component.spec.ts b/src/app/features/tech-record/components/tech-record-title/tech-record-title.component.spec.ts index 15b767a223..144aee7562 100644 --- a/src/app/features/tech-record/components/tech-record-title/tech-record-title.component.spec.ts +++ b/src/app/features/tech-record/components/tech-record-title/tech-record-title.component.spec.ts @@ -7,10 +7,7 @@ import { TechRecordType } from '@dvsa/cvs-type-definitions/types/v3/tech-record/ import { DynamicFormsModule } from '@forms/dynamic-forms.module'; import { mockVehicleTechnicalRecord } from '@mocks/mock-vehicle-technical-record.mock'; import { Roles } from '@models/roles.enum'; -import { - V3TechRecordModel, VehicleTypes, - VehiclesOtherThan, -} from '@models/vehicle-tech-record.model'; +import { V3TechRecordModel, VehicleTypes, VehiclesOtherThan } from '@models/vehicle-tech-record.model'; import { MockStore, provideMockStore } from '@ngrx/store/testing'; import { TechnicalRecordService } from '@services/technical-record/technical-record.service'; import { UserService } from '@services/user-service/user-service'; @@ -21,103 +18,107 @@ import { Observable, of } from 'rxjs'; import { TechRecordTitleComponent } from './tech-record-title.component'; const MockUserService = { - getUserName$: jest.fn().mockReturnValue(new Observable()), - roles$: of([Roles.TestResultAmend, Roles.TestResultView]), + getUserName$: jest.fn().mockReturnValue(new Observable()), + roles$: of([Roles.TestResultAmend, Roles.TestResultView]), }; describe('TechRecordTitleComponent', () => { - let component: TechRecordTitleComponent; - let fixture: ComponentFixture; - let store: MockStore; - let technicalRecordService: TechnicalRecordService; - let mockRecord: V3TechRecordModel; - - beforeEach(async () => { - await TestBed.configureTestingModule({ - declarations: [TechRecordTitleComponent], - imports: [DynamicFormsModule, HttpClientTestingModule, RouterTestingModule, SharedModule], - providers: [provideMockStore({ initialState: initialAppState }), { provide: UserService, useValue: MockUserService }, TechnicalRecordService], - }).compileComponents(); - }); - - beforeEach(() => { - fixture = TestBed.createComponent(TechRecordTitleComponent); - store = TestBed.inject(MockStore); - component = fixture.componentInstance; - technicalRecordService = TestBed.inject(TechnicalRecordService); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); - - describe('the VRM fields', () => { - beforeEach(() => { - mockRecord = { - systemNumber: 'foo', - createdTimestamp: 'bar', - vin: 'testVin', - primaryVrm: 'TESTVRM', - secondaryVrms: ['TESTVRM1', 'TESTVRM2', 'TESTVRM3', 'TESTVRM4', 'TESTVRM5'], - techRecord_vehicleType: VehicleTypes.LGV, - } as unknown as TechRecordType<'put'>; - jest.spyOn(store, 'select').mockReturnValue(of(mockRecord)); - component.vehicle = mockRecord; - store.overrideSelector(editingTechRecord, mockRecord); - }); - it('should show primary VRM for current record', () => { - fixture.detectChanges(); - - const vrmField = fixture.nativeElement.querySelector('app-number-plate'); - expect(vrmField.textContent).toContain('TEST VRM'); - }); - - it('should show the newest (last) secondary VRM', () => { - fixture.detectChanges(); - - const vrmField = fixture.nativeElement.querySelectorAll('app-number-plate')[1]; - expect(vrmField.textContent).toContain('TESTV RM5'); - expect(vrmField.textContent).not.toContain('TEST VRM'); - expect(vrmField.textContent).not.toContain('TESTV RM2'); - expect(vrmField.textContent).not.toContain('TESTV RM3'); - expect(vrmField.textContent).not.toContain('TESTV RM4'); - }); - it('should not create previous-vrm-span if no secondary vrm exists', () => { - delete (mockRecord as VehiclesOtherThan<'trl'>).secondaryVrms; - fixture.detectChanges(); - - const vrmField = fixture.debugElement.query(By.css('#previous-vrm-span')); - expect(vrmField).toBeNull(); - }); - }); - describe('trailer ID', () => { - it('shows a trailer ID instead of VRM when vehicle type is a trailer', () => { - const mockRecordTrailer = mockVehicleTechnicalRecord(VehicleTypes.TRL); - jest.spyOn(technicalRecordService, 'techRecord$', 'get').mockReturnValue(of(mockRecordTrailer)); - - component.vehicle = mockRecordTrailer; - - store.overrideSelector(selectTechRecord, mockRecordTrailer); - fixture.detectChanges(); - - const trailerIdField = fixture.debugElement.query(By.css('#trailer-id')); - expect(trailerIdField.nativeElement.textContent).toContain('TestId'); - }); - - const smallTrailerEuVehicleCategories = [EUVehicleCategory.O1, EUVehicleCategory.O2]; - - it.each(smallTrailerEuVehicleCategories)('does not show secondary VRMs for small trailer', (euVehicleCategory) => { - const mockRecordTrailer = mockVehicleTechnicalRecord(VehicleTypes.TRL); - jest.spyOn(technicalRecordService, 'techRecord$', 'get').mockReturnValue(of(mockRecordTrailer)); - mockRecordTrailer.techRecord_euVehicleCategory = euVehicleCategory; - component.vehicle = mockRecordTrailer; - store.overrideSelector(selectTechRecord, mockRecordTrailer); - fixture.detectChanges(); - - const trailerIdField = fixture.debugElement.query(By.css('#trailer-id')); - expect(trailerIdField.nativeElement.textContent).toContain('TestId'); - const secondaryVrmField = fixture.debugElement.query(By.css('#previous-vrm')); - expect(secondaryVrmField).toBeNull(); - }); - }); + let component: TechRecordTitleComponent; + let fixture: ComponentFixture; + let store: MockStore; + let technicalRecordService: TechnicalRecordService; + let mockRecord: V3TechRecordModel; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [TechRecordTitleComponent], + imports: [DynamicFormsModule, HttpClientTestingModule, RouterTestingModule, SharedModule], + providers: [ + provideMockStore({ initialState: initialAppState }), + { provide: UserService, useValue: MockUserService }, + TechnicalRecordService, + ], + }).compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(TechRecordTitleComponent); + store = TestBed.inject(MockStore); + component = fixture.componentInstance; + technicalRecordService = TestBed.inject(TechnicalRecordService); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + describe('the VRM fields', () => { + beforeEach(() => { + mockRecord = { + systemNumber: 'foo', + createdTimestamp: 'bar', + vin: 'testVin', + primaryVrm: 'TESTVRM', + secondaryVrms: ['TESTVRM1', 'TESTVRM2', 'TESTVRM3', 'TESTVRM4', 'TESTVRM5'], + techRecord_vehicleType: VehicleTypes.LGV, + } as unknown as TechRecordType<'put'>; + jest.spyOn(store, 'select').mockReturnValue(of(mockRecord)); + component.vehicle = mockRecord; + store.overrideSelector(editingTechRecord, mockRecord); + }); + it('should show primary VRM for current record', () => { + fixture.detectChanges(); + + const vrmField = fixture.nativeElement.querySelector('app-number-plate'); + expect(vrmField.textContent).toContain('TEST VRM'); + }); + + it('should show the newest (last) secondary VRM', () => { + fixture.detectChanges(); + + const vrmField = fixture.nativeElement.querySelectorAll('app-number-plate')[1]; + expect(vrmField.textContent).toContain('TESTV RM5'); + expect(vrmField.textContent).not.toContain('TEST VRM'); + expect(vrmField.textContent).not.toContain('TESTV RM2'); + expect(vrmField.textContent).not.toContain('TESTV RM3'); + expect(vrmField.textContent).not.toContain('TESTV RM4'); + }); + it('should not create previous-vrm-span if no secondary vrm exists', () => { + delete (mockRecord as VehiclesOtherThan<'trl'>).secondaryVrms; + fixture.detectChanges(); + + const vrmField = fixture.debugElement.query(By.css('#previous-vrm-span')); + expect(vrmField).toBeNull(); + }); + }); + describe('trailer ID', () => { + it('shows a trailer ID instead of VRM when vehicle type is a trailer', () => { + const mockRecordTrailer = mockVehicleTechnicalRecord(VehicleTypes.TRL); + jest.spyOn(technicalRecordService, 'techRecord$', 'get').mockReturnValue(of(mockRecordTrailer)); + + component.vehicle = mockRecordTrailer; + + store.overrideSelector(selectTechRecord, mockRecordTrailer); + fixture.detectChanges(); + + const trailerIdField = fixture.debugElement.query(By.css('#trailer-id')); + expect(trailerIdField.nativeElement.textContent).toContain('TestId'); + }); + + const smallTrailerEuVehicleCategories = [EUVehicleCategory.O1, EUVehicleCategory.O2]; + + it.each(smallTrailerEuVehicleCategories)('does not show secondary VRMs for small trailer', (euVehicleCategory) => { + const mockRecordTrailer = mockVehicleTechnicalRecord(VehicleTypes.TRL); + jest.spyOn(technicalRecordService, 'techRecord$', 'get').mockReturnValue(of(mockRecordTrailer)); + mockRecordTrailer.techRecord_euVehicleCategory = euVehicleCategory; + component.vehicle = mockRecordTrailer; + store.overrideSelector(selectTechRecord, mockRecordTrailer); + fixture.detectChanges(); + + const trailerIdField = fixture.debugElement.query(By.css('#trailer-id')); + expect(trailerIdField.nativeElement.textContent).toContain('TestId'); + const secondaryVrmField = fixture.debugElement.query(By.css('#previous-vrm')); + expect(secondaryVrmField).toBeNull(); + }); + }); }); diff --git a/src/app/features/tech-record/components/tech-record-title/tech-record-title.component.ts b/src/app/features/tech-record/components/tech-record-title/tech-record-title.component.ts index a89e9491f9..21056f613a 100644 --- a/src/app/features/tech-record/components/tech-record-title/tech-record-title.component.ts +++ b/src/app/features/tech-record/components/tech-record-title/tech-record-title.component.ts @@ -12,68 +12,77 @@ import { selectTechRecord } from '@store/technical-records'; import { Observable, take } from 'rxjs'; @Component({ - selector: 'app-tech-record-title[vehicle]', - templateUrl: './tech-record-title.component.html', - styleUrls: ['./tech-record-title.component.scss'], + selector: 'app-tech-record-title[vehicle]', + templateUrl: './tech-record-title.component.html', + styleUrls: ['./tech-record-title.component.scss'], }) export class TechRecordTitleComponent implements OnInit { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - @Input() vehicle?: any; - @Input() actions: TechRecordActions = TechRecordActions.NONE; - @Input() hideActions = false; - @Input() customTitle = ''; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + @Input() vehicle?: any; + @Input() actions: TechRecordActions = TechRecordActions.NONE; + @Input() hideActions = false; + @Input() customTitle = ''; - currentTechRecord$?: Observable | undefined>; - queryableActions: string[] = []; - vehicleMakeAndModel = ''; + currentTechRecord$?: Observable | undefined>; + queryableActions: string[] = []; + vehicleMakeAndModel = ''; - constructor(private route: ActivatedRoute, private router: Router, private store: Store, private technicalRecordService: TechnicalRecordService) {} + constructor( + private route: ActivatedRoute, + private router: Router, + private store: Store, + private technicalRecordService: TechnicalRecordService + ) {} - ngOnInit(): void { - this.queryableActions = this.actions.split(','); + ngOnInit(): void { + this.queryableActions = this.actions.split(','); - this.currentTechRecord$ = this.store.select(selectTechRecord) as Observable | undefined>; + this.currentTechRecord$ = this.store.select(selectTechRecord) as Observable | undefined>; - this.currentTechRecord$.pipe(take(1)).subscribe((data) => { - if (data) { - this.vehicleMakeAndModel = this.technicalRecordService.getMakeAndModel(data); - } - }); - } + this.currentTechRecord$.pipe(take(1)).subscribe((data) => { + if (data) { + this.vehicleMakeAndModel = this.technicalRecordService.getMakeAndModel(data); + } + }); + } - get currentVrm(): string | undefined { - return this.vehicle?.techRecord_vehicleType !== 'trl' ? this.vehicle?.primaryVrm ?? '' : undefined; - } + get currentVrm(): string | undefined { + return this.vehicle?.techRecord_vehicleType !== 'trl' ? this.vehicle?.primaryVrm ?? '' : undefined; + } - get otherVrms(): string[] | undefined { - return this.vehicle?.techRecord_vehicleType !== 'trl' ? this.vehicle?.secondaryVrms ?? [] : undefined; - } + get otherVrms(): string[] | undefined { + return this.vehicle?.techRecord_vehicleType !== 'trl' ? this.vehicle?.secondaryVrms ?? [] : undefined; + } - get vehicleTypes(): typeof VehicleTypes { - return VehicleTypes; - } + get vehicleTypes(): typeof VehicleTypes { + return VehicleTypes; + } - get roles(): typeof Roles { - return Roles; - } + get roles(): typeof Roles { + return Roles; + } - get statuses(): typeof StatusCodes { - return StatusCodes; - } + get statuses(): typeof StatusCodes { + return StatusCodes; + } - getVehicleType(techRecord: V3TechRecordModel): VehicleTypes { - return this.technicalRecordService.getVehicleTypeWithSmallTrl(techRecord); - } + getVehicleType(techRecord: V3TechRecordModel): VehicleTypes { + return this.technicalRecordService.getVehicleTypeWithSmallTrl(techRecord); + } - getCompletenessColor(completeness?: string): 'green' | 'red' { - return completeness === 'complete' ? 'green' : 'red'; - } + getCompletenessColor(completeness?: string): 'green' | 'red' { + return completeness === 'complete' ? 'green' : 'red'; + } - isVrmEditable(statusCode: StatusCode | undefined, currentVehicleType: VehicleType, editableVehicleType: VehicleType): boolean { - return !this.hideActions && statusCode !== StatusCodes.ARCHIVED && currentVehicleType === editableVehicleType; - } + isVrmEditable( + statusCode: StatusCode | undefined, + currentVehicleType: VehicleType, + editableVehicleType: VehicleType + ): boolean { + return !this.hideActions && statusCode !== StatusCodes.ARCHIVED && currentVehicleType === editableVehicleType; + } - navigateTo(path: string, queryParams?: Params): void { - void this.router.navigate([path], { relativeTo: this.route, queryParams }); - } + navigateTo(path: string, queryParams?: Params): void { + void this.router.navigate([path], { relativeTo: this.route, queryParams }); + } } diff --git a/src/app/features/tech-record/components/tech-record-unarchive/tech-record-unarchive-component.ts b/src/app/features/tech-record/components/tech-record-unarchive/tech-record-unarchive-component.ts index 1ba1b66e50..284793c9c7 100644 --- a/src/app/features/tech-record/components/tech-record-unarchive/tech-record-unarchive-component.ts +++ b/src/app/features/tech-record/components/tech-record-unarchive/tech-record-unarchive-component.ts @@ -3,9 +3,7 @@ import { Validators } from '@angular/forms'; import { ActivatedRoute, Router } from '@angular/router'; import { GlobalErrorService } from '@core/components/global-error/global-error.service'; import { TechRecordType } from '@dvsa/cvs-type-definitions/types/v3/tech-record/tech-record-verb'; -import { - CustomFormControl, CustomFormGroup, FormNodeOption, FormNodeTypes, -} from '@forms/services/dynamic-form.types'; +import { CustomFormControl, CustomFormGroup, FormNodeOption, FormNodeTypes } from '@forms/services/dynamic-form.types'; import { StatusCodes } from '@models/vehicle-tech-record.model'; import { Actions, ofType } from '@ngrx/effects'; import { Store } from '@ngrx/store'; @@ -15,97 +13,105 @@ import { unarchiveTechRecord, unarchiveTechRecordSuccess } from '@store/technica import { Subject, takeUntil } from 'rxjs'; @Component({ - selector: 'app-tech-record-unarchive', - templateUrl: './tech-record-unarchive.component.html', + selector: 'app-tech-record-unarchive', + templateUrl: './tech-record-unarchive.component.html', }) export class TechRecordUnarchiveComponent implements OnInit, OnDestroy { - techRecord: TechRecordType<'get'> | undefined; - statusCodes: Array> = [ - { label: 'Provisional', value: StatusCodes.PROVISIONAL }, - { label: 'Current', value: StatusCodes.CURRENT }, - ]; - form: CustomFormGroup; - - destroy$ = new Subject(); - - constructor( - private actions$: Actions, - private errorService: GlobalErrorService, - private route: ActivatedRoute, - private router: Router, - private store: Store, - private technicalRecordService: TechnicalRecordService, - ) { - this.form = new CustomFormGroup( - { name: 'unarchivalForm', type: FormNodeTypes.GROUP }, - { - newRecordStatus: new CustomFormControl({ name: 'newRecordStatus', type: FormNodeTypes.CONTROL }, undefined, [Validators.required]), - reason: new CustomFormControl({ name: 'reason', type: FormNodeTypes.CONTROL }, undefined, [Validators.required]), - }, - ); - } - - ngOnInit(): void { - this.technicalRecordService.techRecord$.pipe(takeUntil(this.destroy$)).subscribe((record) => { - this.techRecord = record as TechRecordType<'get'>; - }); - - this.actions$.pipe(ofType(unarchiveTechRecordSuccess), takeUntil(this.destroy$)).subscribe(({ vehicleTechRecord }) => { - void this.router.navigate([`/tech-records/${vehicleTechRecord.systemNumber}/${vehicleTechRecord.createdTimestamp}`]); - - this.technicalRecordService.clearEditingTechRecord(); - }); - } - - ngOnDestroy(): void { - this.destroy$.next(); - this.destroy$.complete(); - } - - navigateBack(relativePath = '..'): void { - void this.router.navigate([relativePath], { relativeTo: this.route }); - } - - handleSubmit(form: { reason: string; newRecordStatus: string }): void { - if (!this.techRecord) { - return; - } - - if (this.form.valid) { - this.errorService.clearErrors(); - } - - if (this.form.invalid) { - this.validateControls(); - } - - if (!this.form.valid || !form.reason || !form.newRecordStatus) { - return; - } - - this.store.dispatch( - unarchiveTechRecord({ - systemNumber: this.techRecord.systemNumber, - createdTimestamp: this.techRecord.createdTimestamp, - reasonForUnarchiving: this.form.value.reason, - status: this.form.value.newRecordStatus, - }), - ); - } - - private validateControls() { - const reasonControl = this.form.controls['reason']; - const newRecordStatusControl = this.form.controls['newRecordStatus']; - - const errors = []; - if (!reasonControl.valid) { - errors.push({ error: 'Reason for unarchival is required', anchorLink: 'reason' }); - } - - if (!newRecordStatusControl.valid) { - errors.push({ error: 'New Record Status is required', anchorLink: 'newRecordStatus' }); - } - - this.errorService.setErrors(errors); - } + techRecord: TechRecordType<'get'> | undefined; + statusCodes: Array> = [ + { label: 'Provisional', value: StatusCodes.PROVISIONAL }, + { label: 'Current', value: StatusCodes.CURRENT }, + ]; + form: CustomFormGroup; + + destroy$ = new Subject(); + + constructor( + private actions$: Actions, + private errorService: GlobalErrorService, + private route: ActivatedRoute, + private router: Router, + private store: Store, + private technicalRecordService: TechnicalRecordService + ) { + this.form = new CustomFormGroup( + { name: 'unarchivalForm', type: FormNodeTypes.GROUP }, + { + newRecordStatus: new CustomFormControl({ name: 'newRecordStatus', type: FormNodeTypes.CONTROL }, undefined, [ + Validators.required, + ]), + reason: new CustomFormControl({ name: 'reason', type: FormNodeTypes.CONTROL }, undefined, [ + Validators.required, + ]), + } + ); + } + + ngOnInit(): void { + this.technicalRecordService.techRecord$.pipe(takeUntil(this.destroy$)).subscribe((record) => { + this.techRecord = record as TechRecordType<'get'>; + }); + + this.actions$ + .pipe(ofType(unarchiveTechRecordSuccess), takeUntil(this.destroy$)) + .subscribe(({ vehicleTechRecord }) => { + void this.router.navigate([ + `/tech-records/${vehicleTechRecord.systemNumber}/${vehicleTechRecord.createdTimestamp}`, + ]); + + this.technicalRecordService.clearEditingTechRecord(); + }); + } + + ngOnDestroy(): void { + this.destroy$.next(); + this.destroy$.complete(); + } + + navigateBack(relativePath = '..'): void { + void this.router.navigate([relativePath], { relativeTo: this.route }); + } + + handleSubmit(form: { reason: string; newRecordStatus: string }): void { + if (!this.techRecord) { + return; + } + + if (this.form.valid) { + this.errorService.clearErrors(); + } + + if (this.form.invalid) { + this.validateControls(); + } + + if (!this.form.valid || !form.reason || !form.newRecordStatus) { + return; + } + + this.store.dispatch( + unarchiveTechRecord({ + systemNumber: this.techRecord.systemNumber, + createdTimestamp: this.techRecord.createdTimestamp, + reasonForUnarchiving: this.form.value.reason, + status: this.form.value.newRecordStatus, + }) + ); + } + + private validateControls() { + const reasonControl = this.form.controls['reason']; + const newRecordStatusControl = this.form.controls['newRecordStatus']; + + const errors = []; + if (!reasonControl.valid) { + errors.push({ error: 'Reason for unarchival is required', anchorLink: 'reason' }); + } + + if (!newRecordStatusControl.valid) { + errors.push({ error: 'New Record Status is required', anchorLink: 'newRecordStatus' }); + } + + this.errorService.setErrors(errors); + } } diff --git a/src/app/features/tech-record/components/tech-record-unarchive/tech-record-unarchive.component.spec.ts b/src/app/features/tech-record/components/tech-record-unarchive/tech-record-unarchive.component.spec.ts index 59746b4bdf..9cce85b866 100644 --- a/src/app/features/tech-record/components/tech-record-unarchive/tech-record-unarchive.component.spec.ts +++ b/src/app/features/tech-record/components/tech-record-unarchive/tech-record-unarchive.component.spec.ts @@ -16,37 +16,44 @@ import { TechRecordTitleComponent } from '../tech-record-title/tech-record-title import { TechRecordUnarchiveComponent } from './tech-record-unarchive-component'; describe('TechRecordUnarchiveComponent', () => { - let actions$: ReplaySubject; - let component: TechRecordUnarchiveComponent; - let fixture: ComponentFixture; + let actions$: ReplaySubject; + let component: TechRecordUnarchiveComponent; + let fixture: ComponentFixture; - beforeEach(async () => { - actions$ = new ReplaySubject(); + beforeEach(async () => { + actions$ = new ReplaySubject(); - await TestBed.configureTestingModule({ - declarations: [TechRecordUnarchiveComponent, TechRecordTitleComponent], - imports: [DynamicFormsModule, HttpClientTestingModule, ReactiveFormsModule, RouterTestingModule, SharedModule, StoreModule.forRoot({})], - providers: [ - provideMockActions(() => actions$), - provideMockStore({ initialState: initialAppState }), - { provide: APP_BASE_HREF, useValue: '/' }, - { - provide: UserService, - useValue: { - roles$: of([Roles.TechRecordArchive]), - }, - }, - ], - }).compileComponents(); - }); + await TestBed.configureTestingModule({ + declarations: [TechRecordUnarchiveComponent, TechRecordTitleComponent], + imports: [ + DynamicFormsModule, + HttpClientTestingModule, + ReactiveFormsModule, + RouterTestingModule, + SharedModule, + StoreModule.forRoot({}), + ], + providers: [ + provideMockActions(() => actions$), + provideMockStore({ initialState: initialAppState }), + { provide: APP_BASE_HREF, useValue: '/' }, + { + provide: UserService, + useValue: { + roles$: of([Roles.TechRecordArchive]), + }, + }, + ], + }).compileComponents(); + }); - beforeEach(() => { - fixture = TestBed.createComponent(TechRecordUnarchiveComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); + beforeEach(() => { + fixture = TestBed.createComponent(TechRecordUnarchiveComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); }); diff --git a/src/app/features/tech-record/components/tech-router-outlet/tech-router-outlet.component.ts b/src/app/features/tech-record/components/tech-router-outlet/tech-router-outlet.component.ts index ab3a77dd1a..1911c09b8c 100644 --- a/src/app/features/tech-record/components/tech-router-outlet/tech-router-outlet.component.ts +++ b/src/app/features/tech-record/components/tech-router-outlet/tech-router-outlet.component.ts @@ -1,7 +1,7 @@ import { Component } from '@angular/core'; @Component({ - selector: 'app-tech-router-outlet', - templateUrl: './tech-router-outlet.component.html', + selector: 'app-tech-router-outlet', + templateUrl: './tech-router-outlet.component.html', }) export class TechRouterOutletComponent {} diff --git a/src/app/features/tech-record/components/test-record-summary/test-record-summary.component.spec.ts b/src/app/features/tech-record/components/test-record-summary/test-record-summary.component.spec.ts index 25612ae2b5..ac1b4359b5 100644 --- a/src/app/features/tech-record/components/test-record-summary/test-record-summary.component.spec.ts +++ b/src/app/features/tech-record/components/test-record-summary/test-record-summary.component.spec.ts @@ -9,110 +9,107 @@ import { SharedModule } from '@shared/shared.module'; import { TestRecordSummaryComponent } from './test-record-summary.component'; describe('TestRecordSummaryComponent', () => { - let component: TestRecordSummaryComponent; - let fixture: ComponentFixture; + let component: TestRecordSummaryComponent; + let fixture: ComponentFixture; - beforeEach(async () => { - await TestBed.configureTestingModule({ - declarations: [TestRecordSummaryComponent], - imports: [RouterTestingModule, SharedModule], - }).compileComponents(); - }); + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [TestRecordSummaryComponent], + imports: [RouterTestingModule, SharedModule], + }).compileComponents(); + }); - beforeEach(() => { - fixture = TestBed.createComponent(TestRecordSummaryComponent); - component = fixture.componentInstance; - }); + beforeEach(() => { + fixture = TestBed.createComponent(TestRecordSummaryComponent); + component = fixture.componentInstance; + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); - it('should not show table if no records found', () => { - component.testResults = []; - fixture.detectChanges(); + it('should not show table if no records found', () => { + component.testResults = []; + fixture.detectChanges(); - const heading = fixture.debugElement.query(By.css('.govuk-heading-s')); - expect(heading).toBeTruthy(); - expect(heading.nativeElement.innerHTML).toBe('No test records found.'); + const heading = fixture.debugElement.query(By.css('.govuk-heading-s')); + expect(heading).toBeTruthy(); + expect(heading.nativeElement.innerHTML).toBe('No test records found.'); - const table = fixture.debugElement.query(By.css('.govuk-table__body')); - expect(table).toBeFalsy(); - }); + const table = fixture.debugElement.query(By.css('.govuk-table__body')); + expect(table).toBeFalsy(); + }); - it('should show table if records found', () => { - component.testResults = [createMockTestResult()]; - fixture.detectChanges(); + it('should show table if records found', () => { + component.testResults = [createMockTestResult()]; + fixture.detectChanges(); - const heading = fixture.debugElement.query(By.css('.govuk-heading-s')); - expect(heading).toBeFalsy(); + const heading = fixture.debugElement.query(By.css('.govuk-heading-s')); + expect(heading).toBeFalsy(); - const table = fixture.debugElement.query(By.css('.govuk-table__body')); - expect(table).toBeTruthy(); - }); + const table = fixture.debugElement.query(By.css('.govuk-table__body')); + expect(table).toBeTruthy(); + }); - it('should concatinate multiple test types', () => { - const testTypeNames = component.getTestTypeName( - createMockTestResult({ - testTypes: [ - createMockTestType({ testTypeName: 'name' }), - createMockTestType({ testTypeName: 'name' }), - ], - }), - ); - expect(testTypeNames).toBe('name,name'); - }); + it('should concatinate multiple test types', () => { + const testTypeNames = component.getTestTypeName( + createMockTestResult({ + testTypes: [createMockTestType({ testTypeName: 'name' }), createMockTestType({ testTypeName: 'name' })], + }) + ); + expect(testTypeNames).toBe('name,name'); + }); - it('should concatinate multiple test results', () => { - const testTypeResults = component.getTestTypeResults( - createMockTestResult({ - testTypes: [ - createMockTestType({ testResult: resultOfTestEnum.pass }), - createMockTestType({ testResult: resultOfTestEnum.pass }), - ], - }), - ); - expect(testTypeResults).toBe('pass,pass'); - }); + it('should concatinate multiple test results', () => { + const testTypeResults = component.getTestTypeResults( + createMockTestResult({ + testTypes: [ + createMockTestType({ testResult: resultOfTestEnum.pass }), + createMockTestType({ testResult: resultOfTestEnum.pass }), + ], + }) + ); + expect(testTypeResults).toBe('pass,pass'); + }); - it('should retrieve all testTypes and creates sorted TestField[]', () => { - const mockRecords = [ - { - testResultId: '1', - testTypes: [ - { - testTypeStartTimestamp: new Date('12/12/2022').toISOString(), - testNumber: '1', - testResult: resultOfTestEnum.pass, - testTypeName: 'annual', - }, - { - testTypeStartTimestamp: new Date('12/12/2023').toISOString(), - testNumber: '2', - testResult: resultOfTestEnum.pass, - testTypeName: 'annual', - }, - ], - }, - { - testResultId: '1', - testTypes: [ - { - testTypeStartTimestamp: new Date('12/12/2021').toISOString(), - testNumber: '1', - testResult: resultOfTestEnum.pass, - testTypeName: 'annual', - }, - ], - }, - ] as TestResultModel[]; - component.testResults = mockRecords; - const testFieldResults = component.sortedTestTypeFields; + it('should retrieve all testTypes and creates sorted TestField[]', () => { + const mockRecords = [ + { + testResultId: '1', + testTypes: [ + { + testTypeStartTimestamp: new Date('12/12/2022').toISOString(), + testNumber: '1', + testResult: resultOfTestEnum.pass, + testTypeName: 'annual', + }, + { + testTypeStartTimestamp: new Date('12/12/2023').toISOString(), + testNumber: '2', + testResult: resultOfTestEnum.pass, + testTypeName: 'annual', + }, + ], + }, + { + testResultId: '1', + testTypes: [ + { + testTypeStartTimestamp: new Date('12/12/2021').toISOString(), + testNumber: '1', + testResult: resultOfTestEnum.pass, + testTypeName: 'annual', + }, + ], + }, + ] as TestResultModel[]; + component.testResults = mockRecords; + const testFieldResults = component.sortedTestTypeFields; - expect(testFieldResults).toHaveLength(3); + expect(testFieldResults).toHaveLength(3); - expect(testFieldResults[0].testTypeStartTimestamp).toBe(mockRecords[0].testTypes[1].testTypeStartTimestamp); - expect(testFieldResults[1].testTypeStartTimestamp).toBe(mockRecords[0].testTypes[0].testTypeStartTimestamp); - expect(testFieldResults[2].testTypeStartTimestamp).toBe(mockRecords[1].testTypes[0].testTypeStartTimestamp); - }); + expect(testFieldResults[0].testTypeStartTimestamp).toBe(mockRecords[0].testTypes[1].testTypeStartTimestamp); + expect(testFieldResults[1].testTypeStartTimestamp).toBe(mockRecords[0].testTypes[0].testTypeStartTimestamp); + expect(testFieldResults[2].testTypeStartTimestamp).toBe(mockRecords[1].testTypes[0].testTypeStartTimestamp); + }); }); diff --git a/src/app/features/tech-record/components/test-record-summary/test-record-summary.component.ts b/src/app/features/tech-record/components/test-record-summary/test-record-summary.component.ts index 6ddbbaa2de..443c28ea5a 100644 --- a/src/app/features/tech-record/components/test-record-summary/test-record-summary.component.ts +++ b/src/app/features/tech-record/components/test-record-summary/test-record-summary.component.ts @@ -1,81 +1,81 @@ -import { - ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, -} from '@angular/core'; -import { TestResultModel } from '@models/test-results/test-result.model'; -import { resultOfTestEnum } from '@models/test-types/test-type.model'; +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input } from '@angular/core'; import { Roles } from '@models/roles.enum'; import { TestResultStatus } from '@models/test-results/test-result-status.enum'; +import { TestResultModel } from '@models/test-results/test-result.model'; +import { resultOfTestEnum } from '@models/test-types/test-type.model'; interface TestField { - testTypeStartTimestamp: string | Date; - testTypeName: string; - testNumber: string; - testResult: resultOfTestEnum; - testResultId: string; - testResultStatus?: TestResultStatus; + testTypeStartTimestamp: string | Date; + testTypeName: string; + testNumber: string; + testResult: resultOfTestEnum; + testResultId: string; + testResultStatus?: TestResultStatus; } @Component({ - selector: 'app-test-record-summary', - templateUrl: './test-record-summary.component.html', - changeDetection: ChangeDetectionStrategy.OnPush, + selector: 'app-test-record-summary', + templateUrl: './test-record-summary.component.html', + changeDetection: ChangeDetectionStrategy.OnPush, }) export class TestRecordSummaryComponent { - @Input() isEditing = false; - @Input() testResults: TestResultModel[] = []; + @Input() isEditing = false; + @Input() testResults: TestResultModel[] = []; - pageStart?: number; - pageEnd?: number; + pageStart?: number; + pageEnd?: number; - constructor(private cdr: ChangeDetectorRef) {} + constructor(private cdr: ChangeDetectorRef) {} - public get roles(): typeof Roles { - return Roles; - } + public get roles(): typeof Roles { + return Roles; + } - get numberOfRecords(): number { - return this.testResults.length; - } + get numberOfRecords(): number { + return this.testResults.length; + } - get paginatedTestFields(): TestField[] { - return this.sortedTestTypeFields.slice(this.pageStart, this.pageEnd); - } + get paginatedTestFields(): TestField[] { + return this.sortedTestTypeFields.slice(this.pageStart, this.pageEnd); + } - get sortedTestTypeFields(): TestField[] { - const byDate = (a: TestField, b: TestField) => new Date(b.testTypeStartTimestamp).getTime() - new Date(a.testTypeStartTimestamp).getTime(); + get sortedTestTypeFields(): TestField[] { + const byDate = (a: TestField, b: TestField) => + new Date(b.testTypeStartTimestamp).getTime() - new Date(a.testTypeStartTimestamp).getTime(); - return this.testResults - .flatMap((record) => - record.testTypes.map((testType) => ({ - testTypeStartTimestamp: testType.testTypeStartTimestamp, - testTypeName: testType.testTypeName, - testNumber: testType.testNumber, - testResult: testType.testResult, - testResultId: record.testResultId, - testResultStatus: record.testStatus, - }))) - .sort(byDate); - } + return this.testResults + .flatMap((record) => + record.testTypes.map((testType) => ({ + testTypeStartTimestamp: testType.testTypeStartTimestamp, + testTypeName: testType.testTypeName, + testNumber: testType.testNumber, + testResult: testType.testResult, + testResultId: record.testResultId, + testResultStatus: record.testStatus, + })) + ) + .sort(byDate); + } - getResult(test: TestField): string { - return test.testResultStatus === TestResultStatus.CANCELLED ? TestResultStatus.CANCELLED : test.testResult; - } + getResult(test: TestField): string { + return test.testResultStatus === TestResultStatus.CANCELLED ? TestResultStatus.CANCELLED : test.testResult; + } - getTestTypeName(testResult: TestResultModel): string { - return testResult.testTypes.map((t) => t.testTypeName).join(','); - } + getTestTypeName(testResult: TestResultModel): string { + return testResult.testTypes.map((t) => t.testTypeName).join(','); + } - getTestTypeResults(testResult: TestResultModel): string { - return testResult.testTypes.map((t) => t.testResult).join(','); - } + getTestTypeResults(testResult: TestResultModel): string { + return testResult.testTypes.map((t) => t.testResult).join(','); + } - trackByFn(i: number, t: TestField): string { - return t.testNumber; - } + trackByFn(i: number, t: TestField): string { + return t.testNumber; + } - handlePaginationChange({ start, end }: { start: number; end: number }): void { - this.pageStart = start; - this.pageEnd = end; - this.cdr.detectChanges(); - } + handlePaginationChange({ start, end }: { start: number; end: number }): void { + this.pageStart = start; + this.pageEnd = end; + this.cdr.detectChanges(); + } } diff --git a/src/app/features/tech-record/components/test-record-summary/test-record-summary.stories.ts b/src/app/features/tech-record/components/test-record-summary/test-record-summary.stories.ts index 1fa4d530e9..d9ed212f2c 100644 --- a/src/app/features/tech-record/components/test-record-summary/test-record-summary.stories.ts +++ b/src/app/features/tech-record/components/test-record-summary/test-record-summary.stories.ts @@ -1,23 +1,28 @@ -import { Meta, Story } from '@storybook/angular'; -import { TestRecordSummaryComponent } from './test-record-summary.component'; import { mockTestResult } from '@mocks/mock-test-result'; import { TestResultModel } from '@models/test-results/test-result.model'; +import { Meta, Story } from '@storybook/angular'; +import { TestRecordSummaryComponent } from './test-record-summary.component'; const fakeRecord: TestResultModel = mockTestResult(); export default { - title: 'Test Record Summary', - component: TestRecordSummaryComponent + title: 'Test Record Summary', + component: TestRecordSummaryComponent, } as Meta; export const NoRecords: Story = () => ({ - props: {} + props: {}, }); export const Record: Story = () => ({ - props: { testRecords: [fakeRecord] } + props: { testRecords: [fakeRecord] }, }); export const Records: Story = () => ({ - props: { testRecords: [fakeRecord, { ...fakeRecord, testStartTimestamp: '2022-02-02:13.23', vin: 'xthoeu213', testStatus: 'PASSED' }] } + props: { + testRecords: [ + fakeRecord, + { ...fakeRecord, testStartTimestamp: '2022-02-02:13.23', vin: 'xthoeu213', testStatus: 'PASSED' }, + ], + }, }); diff --git a/src/app/features/tech-record/components/vehicle-technical-record/vehicle-technical-record.component.spec.ts b/src/app/features/tech-record/components/vehicle-technical-record/vehicle-technical-record.component.spec.ts index 0bd7a87c78..2d4ae4b74b 100644 --- a/src/app/features/tech-record/components/vehicle-technical-record/vehicle-technical-record.component.spec.ts +++ b/src/app/features/tech-record/components/vehicle-technical-record/vehicle-technical-record.component.spec.ts @@ -7,9 +7,7 @@ import { RouterTestingModule } from '@angular/router/testing'; import { ApiModule } from '@api/test-results'; import { DynamicFormsModule } from '@forms/dynamic-forms.module'; import { MultiOptionsService } from '@forms/services/multi-options.service'; -import { - StatusCodes, TechRecordModel, V3TechRecordModel, -} from '@models/vehicle-tech-record.model'; +import { StatusCodes, TechRecordModel, V3TechRecordModel } from '@models/vehicle-tech-record.model'; import { EffectsModule } from '@ngrx/effects'; import { StoreModule } from '@ngrx/store'; import { provideMockStore } from '@ngrx/store/testing'; @@ -28,79 +26,79 @@ import { VehicleTechnicalRecordComponent } from './vehicle-technical-record.comp global.scrollTo = jest.fn(); describe('VehicleTechnicalRecordComponent', () => { - let component: VehicleTechnicalRecordComponent; - let fixture: ComponentFixture; - @Component({}) - class TechRecordSummaryStubComponent { - checkForms() {} - } + let component: VehicleTechnicalRecordComponent; + let fixture: ComponentFixture; + @Component({}) + class TechRecordSummaryStubComponent { + checkForms() {} + } - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [ - ApiModule, - DynamicFormsModule, - EffectsModule.forRoot(), - HttpClientTestingModule, - RouterModule.forRoot([]), - RouterTestingModule, - SharedModule, - StoreModule.forRoot({}), - ], - declarations: [ - EditTechRecordButtonComponent, - TechRecordHistoryComponent, - TechRecordSummaryComponent, - TechRecordTitleComponent, - TechRecordSummaryStubComponent, - TestRecordSummaryComponent, - VehicleTechnicalRecordComponent, - ], - providers: [ - MultiOptionsService, - provideMockStore({ initialState: initialAppState }), - { provide: APP_BASE_HREF, useValue: '/' }, - { - provide: UserService, - useValue: { - roles$: of(['TestResult.View', 'TestResult.CreateContingency']), - }, - }, - { - provide: TechnicalRecordService, - useValue: { - get techRecord$() { - return of({ - systemNumber: 'foo', - createdTimestamp: 'bar', - vin: 'testVin', - techRecord_statusCode: StatusCodes.CURRENT, - }); - }, - updateEditingTechRecord: () => {}, - get editableTechRecord$() { - return of({ systemNumber: 'foo', createdTimestamp: 'bar', vin: 'testVin' }); - }, - get sectionStates$() { - return of(['TEST_SECTION']); - }, - getVehicleTypeWithSmallTrl: (techRecord: TechRecordModel) => { - return techRecord.vehicleType; - }, - }, - }, - ], - }).compileComponents(); - }); + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [ + ApiModule, + DynamicFormsModule, + EffectsModule.forRoot(), + HttpClientTestingModule, + RouterModule.forRoot([]), + RouterTestingModule, + SharedModule, + StoreModule.forRoot({}), + ], + declarations: [ + EditTechRecordButtonComponent, + TechRecordHistoryComponent, + TechRecordSummaryComponent, + TechRecordTitleComponent, + TechRecordSummaryStubComponent, + TestRecordSummaryComponent, + VehicleTechnicalRecordComponent, + ], + providers: [ + MultiOptionsService, + provideMockStore({ initialState: initialAppState }), + { provide: APP_BASE_HREF, useValue: '/' }, + { + provide: UserService, + useValue: { + roles$: of(['TestResult.View', 'TestResult.CreateContingency']), + }, + }, + { + provide: TechnicalRecordService, + useValue: { + get techRecord$() { + return of({ + systemNumber: 'foo', + createdTimestamp: 'bar', + vin: 'testVin', + techRecord_statusCode: StatusCodes.CURRENT, + }); + }, + updateEditingTechRecord: () => {}, + get editableTechRecord$() { + return of({ systemNumber: 'foo', createdTimestamp: 'bar', vin: 'testVin' }); + }, + get sectionStates$() { + return of(['TEST_SECTION']); + }, + getVehicleTypeWithSmallTrl: (techRecord: TechRecordModel) => { + return techRecord.vehicleType; + }, + }, + }, + ], + }).compileComponents(); + }); - beforeEach(() => { - fixture = TestBed.createComponent(VehicleTechnicalRecordComponent); - component = fixture.componentInstance; - component.techRecord = { systemNumber: 'foo', createdTimestamp: 'bar', vin: 'testVin' } as V3TechRecordModel; - }); + beforeEach(() => { + fixture = TestBed.createComponent(VehicleTechnicalRecordComponent); + component = fixture.componentInstance; + component.techRecord = { systemNumber: 'foo', createdTimestamp: 'bar', vin: 'testVin' } as V3TechRecordModel; + }); - it('should create', () => { - fixture.detectChanges(); - expect(component).toBeTruthy(); - }); + it('should create', () => { + fixture.detectChanges(); + expect(component).toBeTruthy(); + }); }); diff --git a/src/app/features/tech-record/components/vehicle-technical-record/vehicle-technical-record.component.ts b/src/app/features/tech-record/components/vehicle-technical-record/vehicle-technical-record.component.ts index b00822c10b..3b3473000e 100644 --- a/src/app/features/tech-record/components/vehicle-technical-record/vehicle-technical-record.component.ts +++ b/src/app/features/tech-record/components/vehicle-technical-record/vehicle-technical-record.component.ts @@ -1,7 +1,5 @@ import { ViewportScroller } from '@angular/common'; -import { - Component, Input, OnDestroy, OnInit, ViewChild, -} from '@angular/core'; +import { Component, Input, OnDestroy, OnInit, ViewChild } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; import { GlobalErrorService } from '@core/components/global-error/global-error.service'; import { TechRecordSearchSchema } from '@dvsa/cvs-type-definitions/types/v3/tech-record/get/search'; @@ -10,164 +8,166 @@ import { Roles } from '@models/roles.enum'; import { TechRecordActions } from '@models/tech-record/tech-record-actions.enum'; import { TestResultModel } from '@models/test-results/test-result.model'; import { - ReasonForEditing, StatusCodes, TechRecordModel, V3TechRecordModel, VehicleTypes, + ReasonForEditing, + StatusCodes, + TechRecordModel, + V3TechRecordModel, + VehicleTypes, } from '@models/vehicle-tech-record.model'; -import { AdrService } from '@services/adr/adr.service'; import { Actions, ofType } from '@ngrx/effects'; import { Store } from '@ngrx/store'; +import { AdrService } from '@services/adr/adr.service'; import { FeatureToggleService } from '@services/feature-toggle-service/feature-toggle-service'; import { TestRecordsService } from '@services/test-records/test-records.service'; import { UserService } from '@services/user-service/user-service'; import { clearScrollPosition, updateTechRecordSuccess } from '@store/technical-records'; import { TechnicalRecordServiceState } from '@store/technical-records/reducers/technical-record-service.reducer'; -import { - Observable, Subject, take, takeUntil, -} from 'rxjs'; +import { Observable, Subject, take, takeUntil } from 'rxjs'; import { TechRecordSummaryComponent } from '../tech-record-summary/tech-record-summary.component'; @Component({ - selector: 'app-vehicle-technical-record', - templateUrl: './vehicle-technical-record.component.html', - styleUrls: ['./vehicle-technical-record.component.scss'], + selector: 'app-vehicle-technical-record', + templateUrl: './vehicle-technical-record.component.html', + styleUrls: ['./vehicle-technical-record.component.scss'], }) export class VehicleTechnicalRecordComponent implements OnInit, OnDestroy { - @ViewChild(TechRecordSummaryComponent) summary!: TechRecordSummaryComponent; - @Input() techRecord?: V3TechRecordModel; - - testResults$: Observable; - editingReason?: ReasonForEditing; - recordHistory?: TechRecordSearchSchema[]; - - isCurrent = false; - isArchived = false; - isEditing = false; - isDirty = false; - isInvalid = false; - - private destroy$ = new Subject(); - hasTestResultAmend: boolean | undefined = false; - - constructor( - public globalErrorService: GlobalErrorService, - public userService: UserService, - testRecordService: TestRecordsService, - private activatedRoute: ActivatedRoute, - private route: ActivatedRoute, - private router: Router, - private store: Store, - private actions$: Actions, - private viewportScroller: ViewportScroller, - private featureToggleService: FeatureToggleService, - public adrService: AdrService, - ) { - this.testResults$ = testRecordService.testRecords$; - this.isEditing = this.activatedRoute.snapshot.data['isEditing'] ?? false; - this.editingReason = this.activatedRoute.snapshot.data['reason']; - } - ngOnDestroy(): void { - this.destroy$.next(); - this.destroy$.complete(); - } - ngOnInit(): void { - this.actions$.pipe(ofType(updateTechRecordSuccess), takeUntil(this.destroy$)).subscribe((vehicleTechRecord) => { - void this.router.navigate([ - `/tech-records/${vehicleTechRecord.vehicleTechRecord.systemNumber}/${vehicleTechRecord.vehicleTechRecord.createdTimestamp}`, - ]); - }); - this.isArchived = this.techRecord?.techRecord_statusCode === StatusCodes.ARCHIVED; - this.isCurrent = this.techRecord?.techRecord_statusCode === StatusCodes.CURRENT; - - this.userService.roles$.pipe(take(1)).subscribe((storedRoles) => { - this.hasTestResultAmend = storedRoles?.some((role) => { - return Roles.TestResultAmend.split(',').includes(role); - }); - }); - } - - get currentVrm(): string | undefined { - return this.techRecord?.techRecord_vehicleType !== 'trl' ? this.techRecord?.primaryVrm ?? '' : undefined; - } - - get roles(): typeof Roles { - return Roles; - } - - get vehicleTypes(): typeof VehicleTypes { - return VehicleTypes; - } - - get statusCodes(): typeof StatusCodes { - return StatusCodes; - } - - hasPlates(techRecord: TechRecordModel) { - return (techRecord.plates?.length ?? 0) > 0; - } - - getActions(techRecord?: V3TechRecordModel): TechRecordActions { - switch (techRecord?.techRecord_statusCode) { - case StatusCodes.CURRENT: - return TechRecordActions.CURRENT; - case StatusCodes.PROVISIONAL: - return TechRecordActions.PROVISIONAL; - case StatusCodes.ARCHIVED: - return TechRecordActions.ARCHIVED; - default: - return TechRecordActions.NONE; - } - } - - getVehicleDescription(techRecord: TechRecordModel, vehicleType: VehicleTypes | undefined): string { - switch (vehicleType) { - case VehicleTypes.TRL: - return techRecord.vehicleConfiguration ?? ''; - case VehicleTypes.PSV: - return techRecord.bodyMake && techRecord.bodyModel ? `${techRecord.bodyMake}-${techRecord.bodyModel}` : ''; - case VehicleTypes.HGV: - return techRecord.make && techRecord.model ? `${techRecord.make}-${techRecord.model}` : ''; - default: - return 'Unknown Vehicle Type'; - } - } - - showCreateTestButton(): boolean { - return !this.isArchived && !this.isEditing; - } - - async createTest(techRecord?: V3TechRecordModel): Promise { - this.store.dispatch(clearScrollPosition()); - if ( - (techRecord as TechRecordType<'get'>)?.techRecord_recordCompleteness === 'complete' - || (techRecord as TechRecordType<'get'>)?.techRecord_recordCompleteness === 'testable' - ) { - await this.router.navigate(['test-records/create-test/type'], { relativeTo: this.route }); - } else { - this.globalErrorService.setErrors([ - { - error: this.getCreateTestErrorMessage(techRecord?.techRecord_hiddenInVta ?? false), - anchorLink: 'create-test', - }, - ]); - - this.viewportScroller.scrollToPosition([0, 0]); - } - } - - async handleSubmit(): Promise { - this.summary.checkForms(); - if (this.isInvalid) return; - - await this.router.navigate(['change-summary'], { relativeTo: this.route }); - } - - private getCreateTestErrorMessage(hiddenInVta: boolean | undefined): string { - if (hiddenInVta) { - return 'Vehicle record is hidden in VTA. Show the vehicle record in VTA to start recording tests against it.'; - } - - return this.hasTestResultAmend - ? 'This vehicle does not have enough information to be tested. Please complete this record so tests can be recorded against it.' - : 'This vehicle does not have enough information to be tested.' - + ' Call the Contact Centre to complete this record so tests can be recorded against it.'; - } + @ViewChild(TechRecordSummaryComponent) summary!: TechRecordSummaryComponent; + @Input() techRecord?: V3TechRecordModel; + + testResults$: Observable; + editingReason?: ReasonForEditing; + recordHistory?: TechRecordSearchSchema[]; + + isCurrent = false; + isArchived = false; + isEditing = false; + isDirty = false; + isInvalid = false; + + private destroy$ = new Subject(); + hasTestResultAmend: boolean | undefined = false; + + constructor( + public globalErrorService: GlobalErrorService, + public userService: UserService, + testRecordService: TestRecordsService, + private activatedRoute: ActivatedRoute, + private route: ActivatedRoute, + private router: Router, + private store: Store, + private actions$: Actions, + private viewportScroller: ViewportScroller, + private featureToggleService: FeatureToggleService, + public adrService: AdrService + ) { + this.testResults$ = testRecordService.testRecords$; + this.isEditing = this.activatedRoute.snapshot.data['isEditing'] ?? false; + this.editingReason = this.activatedRoute.snapshot.data['reason']; + } + ngOnDestroy(): void { + this.destroy$.next(); + this.destroy$.complete(); + } + ngOnInit(): void { + this.actions$.pipe(ofType(updateTechRecordSuccess), takeUntil(this.destroy$)).subscribe((vehicleTechRecord) => { + void this.router.navigate([ + `/tech-records/${vehicleTechRecord.vehicleTechRecord.systemNumber}/${vehicleTechRecord.vehicleTechRecord.createdTimestamp}`, + ]); + }); + this.isArchived = this.techRecord?.techRecord_statusCode === StatusCodes.ARCHIVED; + this.isCurrent = this.techRecord?.techRecord_statusCode === StatusCodes.CURRENT; + + this.userService.roles$.pipe(take(1)).subscribe((storedRoles) => { + this.hasTestResultAmend = storedRoles?.some((role) => { + return Roles.TestResultAmend.split(',').includes(role); + }); + }); + } + + get currentVrm(): string | undefined { + return this.techRecord?.techRecord_vehicleType !== 'trl' ? this.techRecord?.primaryVrm ?? '' : undefined; + } + + get roles(): typeof Roles { + return Roles; + } + + get vehicleTypes(): typeof VehicleTypes { + return VehicleTypes; + } + + get statusCodes(): typeof StatusCodes { + return StatusCodes; + } + + hasPlates(techRecord: TechRecordModel) { + return (techRecord.plates?.length ?? 0) > 0; + } + + getActions(techRecord?: V3TechRecordModel): TechRecordActions { + switch (techRecord?.techRecord_statusCode) { + case StatusCodes.CURRENT: + return TechRecordActions.CURRENT; + case StatusCodes.PROVISIONAL: + return TechRecordActions.PROVISIONAL; + case StatusCodes.ARCHIVED: + return TechRecordActions.ARCHIVED; + default: + return TechRecordActions.NONE; + } + } + + getVehicleDescription(techRecord: TechRecordModel, vehicleType: VehicleTypes | undefined): string { + switch (vehicleType) { + case VehicleTypes.TRL: + return techRecord.vehicleConfiguration ?? ''; + case VehicleTypes.PSV: + return techRecord.bodyMake && techRecord.bodyModel ? `${techRecord.bodyMake}-${techRecord.bodyModel}` : ''; + case VehicleTypes.HGV: + return techRecord.make && techRecord.model ? `${techRecord.make}-${techRecord.model}` : ''; + default: + return 'Unknown Vehicle Type'; + } + } + + showCreateTestButton(): boolean { + return !this.isArchived && !this.isEditing; + } + + async createTest(techRecord?: V3TechRecordModel): Promise { + this.store.dispatch(clearScrollPosition()); + if ( + (techRecord as TechRecordType<'get'>)?.techRecord_recordCompleteness === 'complete' || + (techRecord as TechRecordType<'get'>)?.techRecord_recordCompleteness === 'testable' + ) { + await this.router.navigate(['test-records/create-test/type'], { relativeTo: this.route }); + } else { + this.globalErrorService.setErrors([ + { + error: this.getCreateTestErrorMessage(techRecord?.techRecord_hiddenInVta ?? false), + anchorLink: 'create-test', + }, + ]); + + this.viewportScroller.scrollToPosition([0, 0]); + } + } + + async handleSubmit(): Promise { + this.summary.checkForms(); + if (this.isInvalid) return; + + await this.router.navigate(['change-summary'], { relativeTo: this.route }); + } + + private getCreateTestErrorMessage(hiddenInVta: boolean | undefined): string { + if (hiddenInVta) { + return 'Vehicle record is hidden in VTA. Show the vehicle record in VTA to start recording tests against it.'; + } + + return this.hasTestResultAmend + ? 'This vehicle does not have enough information to be tested. Please complete this record so tests can be recorded against it.' + : 'This vehicle does not have enough information to be tested.' + + ' Call the Contact Centre to complete this record so tests can be recorded against it.'; + } } diff --git a/src/app/features/tech-record/components/vehicle-technical-record/vehicle-technical-record.stories.ts b/src/app/features/tech-record/components/vehicle-technical-record/vehicle-technical-record.stories.ts index 1e0a385fc4..eea6fcd914 100644 --- a/src/app/features/tech-record/components/vehicle-technical-record/vehicle-technical-record.stories.ts +++ b/src/app/features/tech-record/components/vehicle-technical-record/vehicle-technical-record.stories.ts @@ -3,15 +3,15 @@ import { Meta, Story } from '@storybook/angular'; import { VehicleTechnicalRecordComponent } from './vehicle-technical-record.component'; export default { - title: 'Vehicle Tech Record', - component: VehicleTechnicalRecordComponent + title: 'Vehicle Tech Record', + component: VehicleTechnicalRecordComponent, } as Meta; -const Template: Story = args => ({ - props: args +const Template: Story = (args) => ({ + props: args, }); export const Primary = Template.bind({}); Primary.args = { - vehicleTechRecord: mockVehicleTechnicalRecord() + vehicleTechRecord: mockVehicleTechnicalRecord(), }; diff --git a/src/app/features/tech-record/create-batch/components/batch-vehicle-details/batch-vehicle-details.component.spec.ts b/src/app/features/tech-record/create-batch/components/batch-vehicle-details/batch-vehicle-details.component.spec.ts index adb11ee264..593c04fd5e 100644 --- a/src/app/features/tech-record/create-batch/components/batch-vehicle-details/batch-vehicle-details.component.spec.ts +++ b/src/app/features/tech-record/create-batch/components/batch-vehicle-details/batch-vehicle-details.component.spec.ts @@ -11,52 +11,57 @@ import { of } from 'rxjs'; import { BatchVehicleDetailsComponent } from './batch-vehicle-details.component'; const mockGlobalErrorService = { - addError: jest.fn(), - clearErrors: jest.fn(), - setErrors: jest.fn(), + addError: jest.fn(), + clearErrors: jest.fn(), + setErrors: jest.fn(), }; describe('BatchVehicleDetailsComponent', () => { - let component: BatchVehicleDetailsComponent; - let fixture: ComponentFixture; + let component: BatchVehicleDetailsComponent; + let fixture: ComponentFixture; - beforeEach(async () => { - await TestBed.configureTestingModule({ - declarations: [BatchVehicleDetailsComponent], - imports: [DynamicFormsModule, ReactiveFormsModule, HttpClientTestingModule, RouterTestingModule, SharedModule], - providers: [FormBuilder, { provide: GlobalErrorService, useValue: mockGlobalErrorService }, - provideMockStore({ initialState: initialAppState })], - }).compileComponents(); - }); + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [BatchVehicleDetailsComponent], + imports: [DynamicFormsModule, ReactiveFormsModule, HttpClientTestingModule, RouterTestingModule, SharedModule], + providers: [ + FormBuilder, + { provide: GlobalErrorService, useValue: mockGlobalErrorService }, + provideMockStore({ initialState: initialAppState }), + ], + }).compileComponents(); + }); - beforeEach(() => { - fixture = TestBed.createComponent(BatchVehicleDetailsComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); + beforeEach(() => { + fixture = TestBed.createComponent(BatchVehicleDetailsComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); - describe('isFormValid', () => { - it('should throw an error if there are no vehicles', async () => { - jest.spyOn(component, 'formStatus', 'get').mockImplementationOnce(() => of('VALID')); - component.vehicles.push(component.vehicleForm); - component.vehicleForm.get('vin')?.clearAsyncValidators(); + describe('isFormValid', () => { + it('should throw an error if there are no vehicles', async () => { + jest.spyOn(component, 'formStatus', 'get').mockImplementationOnce(() => of('VALID')); + component.vehicles.push(component.vehicleForm); + component.vehicleForm.get('vin')?.clearAsyncValidators(); - await component.isFormValid(); - component.vehicleForm.updateValueAndValidity(); - expect(mockGlobalErrorService.addError).toHaveBeenCalledWith({ error: 'At least 1 vehicle must be created or updated in a batch' }); - }); - }); + await component.isFormValid(); + component.vehicleForm.updateValueAndValidity(); + expect(mockGlobalErrorService.addError).toHaveBeenCalledWith({ + error: 'At least 1 vehicle must be created or updated in a batch', + }); + }); + }); - describe('checkDuplicateVins', () => { - it('should return duplicate vins and their indexes', () => { - const arr = [{ vin: '123' }, { vin: '123' }, { vin: '123' }, { vin: '' }, { vin: '' }]; - expect(component.checkDuplicateVins(arr)).toStrictEqual([ - { vin: '123', anchor: 1 }, - { vin: '123', anchor: 2 }, - ]); - }); - }); + describe('checkDuplicateVins', () => { + it('should return duplicate vins and their indexes', () => { + const arr = [{ vin: '123' }, { vin: '123' }, { vin: '123' }, { vin: '' }, { vin: '' }]; + expect(component.checkDuplicateVins(arr)).toStrictEqual([ + { vin: '123', anchor: 1 }, + { vin: '123', anchor: 2 }, + ]); + }); + }); }); diff --git a/src/app/features/tech-record/create-batch/components/batch-vehicle-details/batch-vehicle-details.component.ts b/src/app/features/tech-record/create-batch/components/batch-vehicle-details/batch-vehicle-details.component.ts index 60914ad722..fc9f6c7cb2 100644 --- a/src/app/features/tech-record/create-batch/components/batch-vehicle-details/batch-vehicle-details.component.ts +++ b/src/app/features/tech-record/create-batch/components/batch-vehicle-details/batch-vehicle-details.component.ts @@ -1,193 +1,200 @@ import { Component, OnDestroy, OnInit } from '@angular/core'; -import { - AbstractControl, FormArray, FormBuilder, FormControlStatus, FormGroup, Validators, -} from '@angular/forms'; +import { AbstractControl, FormArray, FormBuilder, FormControlStatus, FormGroup, Validators } from '@angular/forms'; import { ActivatedRoute, Router } from '@angular/router'; import { GlobalError } from '@core/components/global-error/global-error.interface'; import { GlobalErrorService } from '@core/components/global-error/global-error.service'; import { DynamicFormService } from '@forms/services/dynamic-form.service'; import { - CustomFormControl, FormNodeEditTypes, FormNodeTypes, FormNodeViewTypes, FormNodeWidth, + CustomFormControl, + FormNodeEditTypes, + FormNodeTypes, + FormNodeViewTypes, + FormNodeWidth, } from '@forms/services/dynamic-form.types'; import { CustomValidators } from '@forms/validators/custom-validators'; import { VehicleTypes } from '@models/vehicle-tech-record.model'; import { BatchTechnicalRecordService } from '@services/batch-technical-record/batch-technical-record.service'; import { TechnicalRecordService } from '@services/technical-record/technical-record.service'; -import { - Observable, Subject, combineLatest, filter, firstValueFrom, take, -} from 'rxjs'; +import { Observable, Subject, combineLatest, filter, firstValueFrom, take } from 'rxjs'; @Component({ - selector: 'app-batch-vehicle-details', - templateUrl: './batch-vehicle-details.component.html', - styleUrls: ['./batch-vehicle-details.component.scss'], + selector: 'app-batch-vehicle-details', + templateUrl: './batch-vehicle-details.component.html', + styleUrls: ['./batch-vehicle-details.component.scss'], }) export class BatchVehicleDetailsComponent implements OnInit, OnDestroy { - form: FormGroup; - vehicleType?: VehicleTypes; - readonly maxNumberOfVehicles = 40; - - private destroy$ = new Subject(); - - constructor( - private fb: FormBuilder, - private globalErrorService: GlobalErrorService, - private router: Router, - private route: ActivatedRoute, - private technicalRecordService: TechnicalRecordService, - private batchTechRecordService: BatchTechnicalRecordService, - ) { - this.form = this.fb.group({ - vehicles: this.fb.array([]), - applicationId: new CustomFormControl({ name: 'applicationId', label: 'Application ID', type: FormNodeTypes.CONTROL }, null, [ - Validators.required, - ]), - }); - - this.technicalRecordService.techRecord$.pipe(take(1)).subscribe((vehicle) => { - if (!vehicle) return this.back(); - }); - - this.batchTechRecordService.vehicleType$.pipe(take(1)).subscribe((vehicleType) => { - this.vehicleType = vehicleType; - }); - } - ngOnInit(): void { - this.addVehicles(this.maxNumberOfVehicles); - combineLatest([this.batchTechRecordService.batchVehicles$, this.batchTechRecordService.applicationId$]) - .pipe(take(1)) - .subscribe(([vehicles, applicationId]) => { - if (this.form && vehicles.length) { - this.form.patchValue({ vehicles, applicationId }); - } - }); - } - - ngOnDestroy(): void { - this.destroy$.next(); - this.destroy$.complete(); - } - - get vehicles(): FormArray { - return this.form.get('vehicles') as FormArray; - } - - get generateNumber$(): Observable { - return this.batchTechRecordService.generateNumber$; - } - - get filledVinsInForm() { - return this.vehicles.value ?? []; - } - - get width(): typeof FormNodeWidth { - return FormNodeWidth; - } - - get vehicleForm(): FormGroup { - return this.fb.group({ - vin: new CustomFormControl( - { name: 'vin', type: FormNodeTypes.CONTROL }, - null, - [CustomValidators.alphanumeric(), Validators.minLength(3), Validators.maxLength(21)], - this.batchTechRecordService.validateForBatch(), - ), - trailerIdOrVrm: new CustomFormControl({ name: 'trailerIdOrVrm', type: FormNodeTypes.CONTROL }, '', [ - CustomValidators.validateVRMTrailerIdLength('vehicleType'), - CustomValidators.alphanumeric(), - ]), - vehicleType: new CustomFormControl( - { - name: 'change-vehicle-type-select', - label: 'Vehicle type', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - }, - this.vehicleType, - ), - createdTimestamp: [''], - systemNumber: [''], - }); - } - - validate(group: AbstractControl): void { - group.get('vin')?.updateValueAndValidity(); - } - - getVinControl(group: AbstractControl): CustomFormControl | null { - return group.get('vin') as CustomFormControl | null; - } - - addVehicles(n: number): void { - for (let i = 0; i < n; i++) { - this.vehicles.push(this.vehicleForm); - } - } - - showErrors(): void { - const errors: GlobalError[] = []; - DynamicFormService.validate(this.form, errors, false); - this.globalErrorService.setErrors(errors); - } - - back(): void { - void this.router.navigate(['..'], { relativeTo: this.route }); - } - - async handleSubmit(): Promise { - const valid = await this.isFormValid(); - if (!valid) return; - - this.globalErrorService.setErrors([]); - this.batchTechRecordService.setApplicationId(this.form.get('applicationId')?.value); - this.batchTechRecordService.upsertVehicleBatch(this.cleanEmptyValues(this.vehicles.value)); - this.back(); - } - - private cleanEmptyValues(input: { vin: string; trailerIdOrVrm?: string }[]): { vin: string; trailerIdOrVrm?: string }[] { - return input.filter((formInput) => !!formInput.vin); - } - - public checkDuplicateVins(input: { vin: string }[]) { - const vinArray = input.map((item) => item.vin); - const duplicates: { vin: string; anchor: number }[] = []; - vinArray.forEach((item, index) => { - if (!!item && vinArray.indexOf(item) !== index) { - duplicates.push({ vin: item, anchor: index }); - } - }); - return duplicates; - } - - async isFormValid(): Promise { - this.globalErrorService.clearErrors(); - this.form.markAllAsTouched(); - - const errors: GlobalError[] = []; - - DynamicFormService.validate(this.form, errors, true); - await firstValueFrom(this.formStatus); - - if (errors?.length) { - this.globalErrorService.setErrors(errors); - } - - if (this.cleanEmptyValues(this.vehicles.value).length === 0) { - this.globalErrorService.addError({ error: 'At least 1 vehicle must be created or updated in a batch' }); - return false; - } - const duplicates = this.checkDuplicateVins(this.vehicles.value); - if (duplicates.length > 0) { - duplicates.forEach((element) => { - this.globalErrorService.addError({ error: `Remove duplicate VIN - ${element.vin}`, anchorLink: `input-vin${element.anchor.toString()}` }); - }); - return false; - } - return this.form.valid; - } - - get formStatus(): Observable { - return this.form.statusChanges.pipe(filter((status) => status !== 'PENDING')); - } + form: FormGroup; + vehicleType?: VehicleTypes; + readonly maxNumberOfVehicles = 40; + + private destroy$ = new Subject(); + + constructor( + private fb: FormBuilder, + private globalErrorService: GlobalErrorService, + private router: Router, + private route: ActivatedRoute, + private technicalRecordService: TechnicalRecordService, + private batchTechRecordService: BatchTechnicalRecordService + ) { + this.form = this.fb.group({ + vehicles: this.fb.array([]), + applicationId: new CustomFormControl( + { name: 'applicationId', label: 'Application ID', type: FormNodeTypes.CONTROL }, + null, + [Validators.required] + ), + }); + + this.technicalRecordService.techRecord$.pipe(take(1)).subscribe((vehicle) => { + if (!vehicle) return this.back(); + }); + + this.batchTechRecordService.vehicleType$.pipe(take(1)).subscribe((vehicleType) => { + this.vehicleType = vehicleType; + }); + } + ngOnInit(): void { + this.addVehicles(this.maxNumberOfVehicles); + combineLatest([this.batchTechRecordService.batchVehicles$, this.batchTechRecordService.applicationId$]) + .pipe(take(1)) + .subscribe(([vehicles, applicationId]) => { + if (this.form && vehicles.length) { + this.form.patchValue({ vehicles, applicationId }); + } + }); + } + + ngOnDestroy(): void { + this.destroy$.next(); + this.destroy$.complete(); + } + + get vehicles(): FormArray { + return this.form.get('vehicles') as FormArray; + } + + get generateNumber$(): Observable { + return this.batchTechRecordService.generateNumber$; + } + + get filledVinsInForm() { + return this.vehicles.value ?? []; + } + + get width(): typeof FormNodeWidth { + return FormNodeWidth; + } + + get vehicleForm(): FormGroup { + return this.fb.group({ + vin: new CustomFormControl( + { name: 'vin', type: FormNodeTypes.CONTROL }, + null, + [CustomValidators.alphanumeric(), Validators.minLength(3), Validators.maxLength(21)], + this.batchTechRecordService.validateForBatch() + ), + trailerIdOrVrm: new CustomFormControl({ name: 'trailerIdOrVrm', type: FormNodeTypes.CONTROL }, '', [ + CustomValidators.validateVRMTrailerIdLength('vehicleType'), + CustomValidators.alphanumeric(), + ]), + vehicleType: new CustomFormControl( + { + name: 'change-vehicle-type-select', + label: 'Vehicle type', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + }, + this.vehicleType + ), + createdTimestamp: [''], + systemNumber: [''], + }); + } + + validate(group: AbstractControl): void { + group.get('vin')?.updateValueAndValidity(); + } + + getVinControl(group: AbstractControl): CustomFormControl | null { + return group.get('vin') as CustomFormControl | null; + } + + addVehicles(n: number): void { + for (let i = 0; i < n; i++) { + this.vehicles.push(this.vehicleForm); + } + } + + showErrors(): void { + const errors: GlobalError[] = []; + DynamicFormService.validate(this.form, errors, false); + this.globalErrorService.setErrors(errors); + } + + back(): void { + void this.router.navigate(['..'], { relativeTo: this.route }); + } + + async handleSubmit(): Promise { + const valid = await this.isFormValid(); + if (!valid) return; + + this.globalErrorService.setErrors([]); + this.batchTechRecordService.setApplicationId(this.form.get('applicationId')?.value); + this.batchTechRecordService.upsertVehicleBatch(this.cleanEmptyValues(this.vehicles.value)); + this.back(); + } + + private cleanEmptyValues( + input: { vin: string; trailerIdOrVrm?: string }[] + ): { vin: string; trailerIdOrVrm?: string }[] { + return input.filter((formInput) => !!formInput.vin); + } + + public checkDuplicateVins(input: { vin: string }[]) { + const vinArray = input.map((item) => item.vin); + const duplicates: { vin: string; anchor: number }[] = []; + vinArray.forEach((item, index) => { + if (!!item && vinArray.indexOf(item) !== index) { + duplicates.push({ vin: item, anchor: index }); + } + }); + return duplicates; + } + + async isFormValid(): Promise { + this.globalErrorService.clearErrors(); + this.form.markAllAsTouched(); + + const errors: GlobalError[] = []; + + DynamicFormService.validate(this.form, errors, true); + await firstValueFrom(this.formStatus); + + if (errors?.length) { + this.globalErrorService.setErrors(errors); + } + + if (this.cleanEmptyValues(this.vehicles.value).length === 0) { + this.globalErrorService.addError({ error: 'At least 1 vehicle must be created or updated in a batch' }); + return false; + } + const duplicates = this.checkDuplicateVins(this.vehicles.value); + if (duplicates.length > 0) { + duplicates.forEach((element) => { + this.globalErrorService.addError({ + error: `Remove duplicate VIN - ${element.vin}`, + anchorLink: `input-vin${element.anchor.toString()}`, + }); + }); + return false; + } + return this.form.valid; + } + + get formStatus(): Observable { + return this.form.statusChanges.pipe(filter((status) => status !== 'PENDING')); + } } diff --git a/src/app/features/tech-record/create-batch/components/batch-vehicle-results/batch-vehicle-results.component.spec.ts b/src/app/features/tech-record/create-batch/components/batch-vehicle-results/batch-vehicle-results.component.spec.ts index d101bdc626..e0a32f5ab3 100644 --- a/src/app/features/tech-record/create-batch/components/batch-vehicle-results/batch-vehicle-results.component.spec.ts +++ b/src/app/features/tech-record/create-batch/components/batch-vehicle-results/batch-vehicle-results.component.spec.ts @@ -8,36 +8,36 @@ import { SharedModule } from '@shared/shared.module'; import { BatchVehicleResultsComponent } from './batch-vehicle-results.component'; describe('BatchVehicleResultsComponent', () => { - let component: BatchVehicleResultsComponent; - let fixture: ComponentFixture; + let component: BatchVehicleResultsComponent; + let fixture: ComponentFixture; - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [HttpClientTestingModule, RouterTestingModule, SharedModule], - declarations: [BatchVehicleResultsComponent], - providers: [provideMockStore({ initialState: initialAppState })], - }).compileComponents(); - }); + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [HttpClientTestingModule, RouterTestingModule, SharedModule], + declarations: [BatchVehicleResultsComponent], + providers: [provideMockStore({ initialState: initialAppState })], + }).compileComponents(); + }); - beforeEach(() => { - fixture = TestBed.createComponent(BatchVehicleResultsComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); + beforeEach(() => { + fixture = TestBed.createComponent(BatchVehicleResultsComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); - it('should expose relevant observables', () => { - expect(component.batchCount$).toBeTruthy(); - expect(component.batchSuccessCount$).toBeTruthy(); - expect(component.batchCreatedCount$).toBeTruthy(); - expect(component.batchTotalCreatedCount$).toBeTruthy(); - expect(component.batchUpdatedCount$).toBeTruthy(); - expect(component.batchTotalUpdatedCount$).toBeTruthy(); - expect(component.applicationId$).toBeTruthy(); - expect(component.batchVehiclesSuccess$).toBeTruthy(); - expect(component.vehicleType$).toBeTruthy(); - }); + it('should expose relevant observables', () => { + expect(component.batchCount$).toBeTruthy(); + expect(component.batchSuccessCount$).toBeTruthy(); + expect(component.batchCreatedCount$).toBeTruthy(); + expect(component.batchTotalCreatedCount$).toBeTruthy(); + expect(component.batchUpdatedCount$).toBeTruthy(); + expect(component.batchTotalUpdatedCount$).toBeTruthy(); + expect(component.applicationId$).toBeTruthy(); + expect(component.batchVehiclesSuccess$).toBeTruthy(); + expect(component.vehicleType$).toBeTruthy(); + }); }); diff --git a/src/app/features/tech-record/create-batch/components/batch-vehicle-results/batch-vehicle-results.component.ts b/src/app/features/tech-record/create-batch/components/batch-vehicle-results/batch-vehicle-results.component.ts index 598763eac4..0792040f63 100644 --- a/src/app/features/tech-record/create-batch/components/batch-vehicle-results/batch-vehicle-results.component.ts +++ b/src/app/features/tech-record/create-batch/components/batch-vehicle-results/batch-vehicle-results.component.ts @@ -3,86 +3,84 @@ import { ActivatedRoute, Router } from '@angular/router'; import { StatusCodes } from '@models/vehicle-tech-record.model'; import { BatchTechnicalRecordService } from '@services/batch-technical-record/batch-technical-record.service'; import { TechnicalRecordService } from '@services/technical-record/technical-record.service'; -import { - Subject, filter, race, take, withLatestFrom, -} from 'rxjs'; +import { Subject, filter, race, take, withLatestFrom } from 'rxjs'; @Component({ - selector: 'app-batch-vehicle-results', - templateUrl: './batch-vehicle-results.component.html', + selector: 'app-batch-vehicle-results', + templateUrl: './batch-vehicle-results.component.html', }) export class BatchVehicleResultsComponent implements OnDestroy { - private destroy$ = new Subject(); + private destroy$ = new Subject(); - constructor( - private technicalRecordService: TechnicalRecordService, - private router: Router, - private route: ActivatedRoute, - private batchTechRecordService: BatchTechnicalRecordService, - ) { - this.batchTechRecordService.batchCount$.pipe(take(1)).subscribe((count) => { - if (!count) { - void this.router.navigate(['../..'], { relativeTo: this.route }); - } - }); + constructor( + private technicalRecordService: TechnicalRecordService, + private router: Router, + private route: ActivatedRoute, + private batchTechRecordService: BatchTechnicalRecordService + ) { + this.batchTechRecordService.batchCount$.pipe(take(1)).subscribe((count) => { + if (!count) { + void this.router.navigate(['../..'], { relativeTo: this.route }); + } + }); - race( - this.batchTechRecordService.batchCount$.pipe( - withLatestFrom(this.batchTechRecordService.batchCreatedCount$, this.batchTechRecordService.batchUpdatedCount$), - filter(([total, created, updated]) => total === created + updated), - ), - this.destroy$, - ) - .pipe(take(1)) - .subscribe(() => { - this.technicalRecordService.clearEditingTechRecord(); - }); - } + race( + this.batchTechRecordService.batchCount$.pipe( + withLatestFrom(this.batchTechRecordService.batchCreatedCount$, this.batchTechRecordService.batchUpdatedCount$), + filter(([total, created, updated]) => total === created + updated) + ), + this.destroy$ + ) + .pipe(take(1)) + .subscribe(() => { + this.technicalRecordService.clearEditingTechRecord(); + }); + } - ngOnDestroy(): void { - this.batchTechRecordService.clearBatch(); - this.technicalRecordService.clearSectionTemplateStates(); - this.destroy$.next(); - this.destroy$.complete(); - } + ngOnDestroy(): void { + this.batchTechRecordService.clearBatch(); + this.technicalRecordService.clearSectionTemplateStates(); + this.destroy$.next(); + this.destroy$.complete(); + } - get vehicleType$() { - return this.batchTechRecordService.vehicleType$; - } + get vehicleType$() { + return this.batchTechRecordService.vehicleType$; + } - get applicationId$() { - return this.batchTechRecordService.applicationId$; - } + get applicationId$() { + return this.batchTechRecordService.applicationId$; + } - get batchVehiclesSuccess$() { - return this.batchTechRecordService.batchVehiclesSuccess$; - } + get batchVehiclesSuccess$() { + return this.batchTechRecordService.batchVehiclesSuccess$; + } - get vehicleStatus() { - return StatusCodes; - } + get vehicleStatus() { + return StatusCodes; + } - get batchCount$() { - return this.batchTechRecordService.batchCount$; - } + get batchCount$() { + return this.batchTechRecordService.batchCount$; + } - get batchSuccessCount$() { - return this.batchTechRecordService.batchSuccessCount$; - } + get batchSuccessCount$() { + return this.batchTechRecordService.batchSuccessCount$; + } - get batchTotalCreatedCount$() { - return this.batchTechRecordService.batchTotalCreatedCount$; - } + get batchTotalCreatedCount$() { + return this.batchTechRecordService.batchTotalCreatedCount$; + } - get batchTotalUpdatedCount$() { - return this.batchTechRecordService.batchTotalUpdatedCount$; - } + get batchTotalUpdatedCount$() { + return this.batchTechRecordService.batchTotalUpdatedCount$; + } - get batchCreatedCount$() { - return this.batchTechRecordService.batchCreatedCount$; - } + get batchCreatedCount$() { + return this.batchTechRecordService.batchCreatedCount$; + } - get batchUpdatedCount$() { - return this.batchTechRecordService.batchUpdatedCount$; - } + get batchUpdatedCount$() { + return this.batchTechRecordService.batchUpdatedCount$; + } } diff --git a/src/app/features/tech-record/create-batch/components/batch-vehicle-template/batch-vehicle-template.component.spec.ts b/src/app/features/tech-record/create-batch/components/batch-vehicle-template/batch-vehicle-template.component.spec.ts index 1db33d5d9a..c2b59435e8 100644 --- a/src/app/features/tech-record/create-batch/components/batch-vehicle-template/batch-vehicle-template.component.spec.ts +++ b/src/app/features/tech-record/create-batch/components/batch-vehicle-template/batch-vehicle-template.component.spec.ts @@ -1,8 +1,6 @@ import { HttpClientTestingModule } from '@angular/common/http/testing'; import { Component } from '@angular/core'; -import { - ComponentFixture, fakeAsync, TestBed, tick, -} from '@angular/core/testing'; +import { ComponentFixture, TestBed, fakeAsync, tick } from '@angular/core/testing'; import { Router } from '@angular/router'; import { RouterTestingModule } from '@angular/router/testing'; import { GlobalErrorService } from '@core/components/global-error/global-error.service'; @@ -11,7 +9,7 @@ import { MockStore, provideMockStore } from '@ngrx/store/testing'; import { BatchTechnicalRecordService } from '@services/batch-technical-record/batch-technical-record.service'; import { TechnicalRecordService } from '@services/technical-record/technical-record.service'; import { FixNavigationTriggeredOutsideAngularZoneNgModule } from '@shared/custom-module/fixNgZoneError'; -import { initialAppState, State } from '@store/.'; +import { State, initialAppState } from '@store/.'; import { createVehicleRecord, updateTechRecord } from '@store/technical-records'; import { BatchRecord } from '@store/technical-records/reducers/batch-create.reducer'; import { of } from 'rxjs'; @@ -22,199 +20,212 @@ import { BatchVehicleTemplateComponent } from './batch-vehicle-template.componen let batchOfVehicles: BatchRecord[] = []; const mockTechRecordService = ({ - editableVehicleTechRecord$: of({ techRecord: [] }), - updateEditingTechRecord: jest.fn(), - createVehicleRecord: jest.fn(), - clearSectionTemplateStates: jest.fn(), + editableVehicleTechRecord$: of({ techRecord: [] }), + updateEditingTechRecord: jest.fn(), + createVehicleRecord: jest.fn(), + clearSectionTemplateStates: jest.fn(), }) as TechnicalRecordService; const mockBatchTechRecordService = ({ - get batchVehicles$() { - return of(batchOfVehicles); - }, - applicationId$: of('TES_1_APPLICATION_ID'), - isBatchCreate$: of(true), - batchCount$: of(2), - vehicleType$: of(VehicleTypes.TRL), - get vehicleStatus$() { - return of('current'); - }, + get batchVehicles$() { + return of(batchOfVehicles); + }, + applicationId$: of('TES_1_APPLICATION_ID'), + isBatchCreate$: of(true), + batchCount$: of(2), + vehicleType$: of(VehicleTypes.TRL), + get vehicleStatus$() { + return of('current'); + }, }) as BatchTechnicalRecordService; @Component({}) class TechRecordSummaryStubComponent { - checkForms() {} + checkForms() {} } describe('BatchVehicleTemplateComponent', () => { - let component: BatchVehicleTemplateComponent; - let fixture: ComponentFixture; - let store: MockStore; - let router: Router; - - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [ - RouterTestingModule.withRoutes([{ path: 'batch-results', component: BatchVehicleResultsComponent }]), - HttpClientTestingModule, - FixNavigationTriggeredOutsideAngularZoneNgModule, - ], - declarations: [BatchVehicleTemplateComponent, TechRecordSummaryStubComponent], - providers: [ - GlobalErrorService, - provideMockStore({ initialState: initialAppState }), - { provide: TechnicalRecordService, useValue: mockTechRecordService }, - { provide: BatchTechnicalRecordService, useValue: mockBatchTechRecordService }, - ], - }).compileComponents(); - }); - - beforeEach(() => { - fixture = TestBed.createComponent(BatchVehicleTemplateComponent); - store = TestBed.inject(MockStore); - router = TestBed.inject(Router); - component = fixture.componentInstance; - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); - // TODO V3 HGV PSV TRL - it('should expose the editableVehicleTechRecord$ observable', () => { - expect(component.vehicle$).toBeTruthy(); - }); - - it('should expose the applicationId$ observable', () => { - expect(component.applicationId$).toBeTruthy(); - }); - - it('should expose the isBatch$ observable', () => { - expect(component.isBatch$).toBeTruthy(); - }); - - it('should expose the batchCount$ observable', () => { - expect(component.batchCount$).toBeTruthy(); - }); - - it('should expose the vehicleType$ observable', () => { - expect(component.vehicleType$).toBeTruthy(); - }); - - describe('should dispatch the createVehicleTechRecord action for every vin and trailerId given', () => { - beforeEach(() => { - component.summary = TestBed.createComponent(TechRecordSummaryStubComponent).componentInstance as TechRecordSummaryComponent; - }); - - it('given a batch of 0', () => { - const dispatchSpy = jest.spyOn(store, 'dispatch').mockImplementation(); - jest.spyOn(component, 'isVehicleStatusValid', 'get').mockReturnValue(true); - component.handleSubmit(); - expect(dispatchSpy).toHaveBeenCalledTimes(0); - }); - - it('given a batch of 2 vehicles to create', () => { - batchOfVehicles = [{ vin: 'EXAMPLEVIN000001' }, { vin: 'EXAMPLEVIN000002' }]; - - const dispatchSpy = jest.spyOn(store, 'dispatch').mockImplementation(); - jest.spyOn(component, 'isVehicleStatusValid', 'get').mockReturnValue(true); - component.handleSubmit(); - expect(dispatchSpy).toHaveBeenCalledTimes(2); - }); - - it('given a batch of 2 vehicles to update', fakeAsync(() => { - jest.spyOn(router, 'navigate').mockImplementation(() => Promise.resolve(true)); - - batchOfVehicles = [ - { - vin: 'EXAMPLEVIN000001', trailerIdOrVrm: '1000001', systemNumber: '1', createdTimestamp: 'foobar', - }, - { - vin: 'EXAMPLEVIN000002', trailerIdOrVrm: '1000002', systemNumber: '2', createdTimestamp: '2022', - }, - ]; - jest.spyOn(mockBatchTechRecordService, 'batchVehicles$', 'get').mockReturnValue(of(batchOfVehicles)); - - jest.spyOn(component, 'isVehicleStatusValid', 'get').mockReturnValue(true); - - const dispatchSpy = jest.spyOn(store, 'dispatch').mockImplementation(); - component.handleSubmit(); - - tick(); - - expect(dispatchSpy).toHaveBeenCalledTimes(2); - expect(dispatchSpy).toHaveBeenNthCalledWith( - 1, - updateTechRecord({ - systemNumber: '1', - createdTimestamp: 'foobar', - }), - ); - - expect(dispatchSpy).toHaveBeenNthCalledWith( - 2, - updateTechRecord({ - systemNumber: '2', - createdTimestamp: '2022', - }), - ); - })); - - it('given a batch of 5 vehicles to create and update', fakeAsync(() => { - jest.spyOn(router, 'navigate').mockImplementation(() => Promise.resolve(true)); - - batchOfVehicles = [ - { - vin: 'EXAMPLEVIN000001', trailerIdOrVrm: '1000001', systemNumber: '1', createdTimestamp: '2022', - }, - { vin: 'EXAMPLEVIN000002' }, - { - vin: 'EXAMPLEVIN000003', trailerIdOrVrm: '1000002', systemNumber: '3', createdTimestamp: '2023', - }, - { vin: 'EXAMPLEVIN000004' }, - { vin: 'EXAMPLEVIN000005' }, - ]; - - jest.spyOn(mockBatchTechRecordService, 'batchVehicles$', 'get').mockReturnValue(of(batchOfVehicles)); - jest.spyOn(component, 'isVehicleStatusValid', 'get').mockReturnValue(true); - const dispatchSpy = jest.spyOn(store, 'dispatch').mockImplementation(); - component.handleSubmit(); - - tick(); - - expect(dispatchSpy).toHaveBeenCalledTimes(5); - expect(dispatchSpy).toHaveBeenNthCalledWith( - 1, - updateTechRecord({ - systemNumber: '1', - createdTimestamp: '2022', - }), - ); - expect(dispatchSpy).toHaveBeenNthCalledWith(2, createVehicleRecord({ vehicle: expect.anything() })); - expect(dispatchSpy).toHaveBeenNthCalledWith( - 3, - updateTechRecord({ - systemNumber: '3', - createdTimestamp: '2023', - }), - ); - expect(dispatchSpy).toHaveBeenNthCalledWith(4, createVehicleRecord({ vehicle: expect.anything() })); - expect(dispatchSpy).toHaveBeenNthCalledWith(5, createVehicleRecord({ vehicle: expect.anything() })); - })); - - it('given a batch of 40', fakeAsync(() => { - jest.spyOn(router, 'navigate').mockImplementation(() => Promise.resolve(true)); - batchOfVehicles = []; - for (let i = 1; i <= 40; i++) { - batchOfVehicles.push({ vin: `EXAMPLEVIN0000${i}`, trailerIdOrVrm: `100000${i}` }); - } - - jest.spyOn(mockBatchTechRecordService, 'batchVehicles$', 'get').mockReturnValue(of(batchOfVehicles)); - jest.spyOn(component, 'isVehicleStatusValid', 'get').mockReturnValue(true); - - const dispatchSpy = jest.spyOn(store, 'dispatch').mockImplementation(); - component.handleSubmit(); - tick(); - expect(dispatchSpy).toHaveBeenCalledTimes(40); - })); - }); + let component: BatchVehicleTemplateComponent; + let fixture: ComponentFixture; + let store: MockStore; + let router: Router; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [ + RouterTestingModule.withRoutes([{ path: 'batch-results', component: BatchVehicleResultsComponent }]), + HttpClientTestingModule, + FixNavigationTriggeredOutsideAngularZoneNgModule, + ], + declarations: [BatchVehicleTemplateComponent, TechRecordSummaryStubComponent], + providers: [ + GlobalErrorService, + provideMockStore({ initialState: initialAppState }), + { provide: TechnicalRecordService, useValue: mockTechRecordService }, + { provide: BatchTechnicalRecordService, useValue: mockBatchTechRecordService }, + ], + }).compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(BatchVehicleTemplateComponent); + store = TestBed.inject(MockStore); + router = TestBed.inject(Router); + component = fixture.componentInstance; + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + // TODO V3 HGV PSV TRL + it('should expose the editableVehicleTechRecord$ observable', () => { + expect(component.vehicle$).toBeTruthy(); + }); + + it('should expose the applicationId$ observable', () => { + expect(component.applicationId$).toBeTruthy(); + }); + + it('should expose the isBatch$ observable', () => { + expect(component.isBatch$).toBeTruthy(); + }); + + it('should expose the batchCount$ observable', () => { + expect(component.batchCount$).toBeTruthy(); + }); + + it('should expose the vehicleType$ observable', () => { + expect(component.vehicleType$).toBeTruthy(); + }); + + describe('should dispatch the createVehicleTechRecord action for every vin and trailerId given', () => { + beforeEach(() => { + component.summary = TestBed.createComponent(TechRecordSummaryStubComponent) + .componentInstance as TechRecordSummaryComponent; + }); + + it('given a batch of 0', () => { + const dispatchSpy = jest.spyOn(store, 'dispatch').mockImplementation(); + jest.spyOn(component, 'isVehicleStatusValid', 'get').mockReturnValue(true); + component.handleSubmit(); + expect(dispatchSpy).toHaveBeenCalledTimes(0); + }); + + it('given a batch of 2 vehicles to create', () => { + batchOfVehicles = [{ vin: 'EXAMPLEVIN000001' }, { vin: 'EXAMPLEVIN000002' }]; + + const dispatchSpy = jest.spyOn(store, 'dispatch').mockImplementation(); + jest.spyOn(component, 'isVehicleStatusValid', 'get').mockReturnValue(true); + component.handleSubmit(); + expect(dispatchSpy).toHaveBeenCalledTimes(2); + }); + + it('given a batch of 2 vehicles to update', fakeAsync(() => { + jest.spyOn(router, 'navigate').mockImplementation(() => Promise.resolve(true)); + + batchOfVehicles = [ + { + vin: 'EXAMPLEVIN000001', + trailerIdOrVrm: '1000001', + systemNumber: '1', + createdTimestamp: 'foobar', + }, + { + vin: 'EXAMPLEVIN000002', + trailerIdOrVrm: '1000002', + systemNumber: '2', + createdTimestamp: '2022', + }, + ]; + jest.spyOn(mockBatchTechRecordService, 'batchVehicles$', 'get').mockReturnValue(of(batchOfVehicles)); + + jest.spyOn(component, 'isVehicleStatusValid', 'get').mockReturnValue(true); + + const dispatchSpy = jest.spyOn(store, 'dispatch').mockImplementation(); + component.handleSubmit(); + + tick(); + + expect(dispatchSpy).toHaveBeenCalledTimes(2); + expect(dispatchSpy).toHaveBeenNthCalledWith( + 1, + updateTechRecord({ + systemNumber: '1', + createdTimestamp: 'foobar', + }) + ); + + expect(dispatchSpy).toHaveBeenNthCalledWith( + 2, + updateTechRecord({ + systemNumber: '2', + createdTimestamp: '2022', + }) + ); + })); + + it('given a batch of 5 vehicles to create and update', fakeAsync(() => { + jest.spyOn(router, 'navigate').mockImplementation(() => Promise.resolve(true)); + + batchOfVehicles = [ + { + vin: 'EXAMPLEVIN000001', + trailerIdOrVrm: '1000001', + systemNumber: '1', + createdTimestamp: '2022', + }, + { vin: 'EXAMPLEVIN000002' }, + { + vin: 'EXAMPLEVIN000003', + trailerIdOrVrm: '1000002', + systemNumber: '3', + createdTimestamp: '2023', + }, + { vin: 'EXAMPLEVIN000004' }, + { vin: 'EXAMPLEVIN000005' }, + ]; + + jest.spyOn(mockBatchTechRecordService, 'batchVehicles$', 'get').mockReturnValue(of(batchOfVehicles)); + jest.spyOn(component, 'isVehicleStatusValid', 'get').mockReturnValue(true); + const dispatchSpy = jest.spyOn(store, 'dispatch').mockImplementation(); + component.handleSubmit(); + + tick(); + + expect(dispatchSpy).toHaveBeenCalledTimes(5); + expect(dispatchSpy).toHaveBeenNthCalledWith( + 1, + updateTechRecord({ + systemNumber: '1', + createdTimestamp: '2022', + }) + ); + expect(dispatchSpy).toHaveBeenNthCalledWith(2, createVehicleRecord({ vehicle: expect.anything() })); + expect(dispatchSpy).toHaveBeenNthCalledWith( + 3, + updateTechRecord({ + systemNumber: '3', + createdTimestamp: '2023', + }) + ); + expect(dispatchSpy).toHaveBeenNthCalledWith(4, createVehicleRecord({ vehicle: expect.anything() })); + expect(dispatchSpy).toHaveBeenNthCalledWith(5, createVehicleRecord({ vehicle: expect.anything() })); + })); + + it('given a batch of 40', fakeAsync(() => { + jest.spyOn(router, 'navigate').mockImplementation(() => Promise.resolve(true)); + batchOfVehicles = []; + for (let i = 1; i <= 40; i++) { + batchOfVehicles.push({ vin: `EXAMPLEVIN0000${i}`, trailerIdOrVrm: `100000${i}` }); + } + + jest.spyOn(mockBatchTechRecordService, 'batchVehicles$', 'get').mockReturnValue(of(batchOfVehicles)); + jest.spyOn(component, 'isVehicleStatusValid', 'get').mockReturnValue(true); + + const dispatchSpy = jest.spyOn(store, 'dispatch').mockImplementation(); + component.handleSubmit(); + tick(); + expect(dispatchSpy).toHaveBeenCalledTimes(40); + })); + }); }); diff --git a/src/app/features/tech-record/create-batch/components/batch-vehicle-template/batch-vehicle-template.component.ts b/src/app/features/tech-record/create-batch/components/batch-vehicle-template/batch-vehicle-template.component.ts index 466717a209..4a7d7933d0 100644 --- a/src/app/features/tech-record/create-batch/components/batch-vehicle-template/batch-vehicle-template.component.ts +++ b/src/app/features/tech-record/create-batch/components/batch-vehicle-template/batch-vehicle-template.component.ts @@ -8,134 +8,140 @@ import { MultiOptions } from '@forms/models/options.model'; import { DynamicFormService } from '@forms/services/dynamic-form.service'; import { CustomFormControl, CustomFormGroup, FormNodeTypes } from '@forms/services/dynamic-form.types'; import { - BatchUpdateVehicleModel, StatusCodes, V3TechRecordModel, VehicleTypes, + BatchUpdateVehicleModel, + StatusCodes, + V3TechRecordModel, + VehicleTypes, } from '@models/vehicle-tech-record.model'; import { Store } from '@ngrx/store'; import { BatchTechnicalRecordService } from '@services/batch-technical-record/batch-technical-record.service'; import { TechnicalRecordService } from '@services/technical-record/technical-record.service'; import { createVehicleRecord, selectTechRecord, updateTechRecord } from '@store/technical-records'; import { TechnicalRecordServiceState } from '@store/technical-records/reducers/technical-record-service.reducer'; -import { - Observable, map, take, withLatestFrom, -} from 'rxjs'; +import { Observable, map, take, withLatestFrom } from 'rxjs'; import { TechRecordSummaryComponent } from '../../../components/tech-record-summary/tech-record-summary.component'; @Component({ - selector: 'app-batch-vehicle-template', - templateUrl: './batch-vehicle-template.component.html', + selector: 'app-batch-vehicle-template', + templateUrl: './batch-vehicle-template.component.html', }) export class BatchVehicleTemplateComponent { - @ViewChild(TechRecordSummaryComponent) summary?: TechRecordSummaryComponent; - isInvalid = false; - form: CustomFormGroup; - public vehicleStatusOptions: MultiOptions = [ - { label: 'Provisional', value: StatusCodes.PROVISIONAL }, - { label: 'Current', value: StatusCodes.CURRENT }, - ]; - - constructor( - private route: ActivatedRoute, - private router: Router, - private store: Store, - private technicalRecordService: TechnicalRecordService, - private batchTechRecordService: BatchTechnicalRecordService, - private globalErrorService: GlobalErrorService, - ) { - this.store - .select(selectTechRecord) - .pipe(take(1)) - .subscribe((vehicle) => { - if (!vehicle) { - void this.router.navigate(['..'], { relativeTo: this.route }); - } - }); - - this.form = new CustomFormGroup( - { name: 'form-group', type: FormNodeTypes.GROUP }, - { - vehicleStatus: new CustomFormControl({ name: 'change-vehicle-status-select', label: 'Vehicle status', type: FormNodeTypes.CONTROL }, '', [ - Validators.required, - ]), - }, - ); - - this.batchTechRecordService.vehicleStatus$.pipe(take(1)).subscribe((vehicleStatus) => { - if (this.form) { - this.form.patchValue({ vehicleStatus }); - } - }); - } - - get vehicle$(): Observable { - return this.store.select(selectTechRecord); - } - - get applicationId$() { - return this.batchTechRecordService.applicationId$; - } - - get isBatch$() { - return this.batchTechRecordService.isBatchCreate$; - } - - get batchCount$() { - return this.batchTechRecordService.batchCount$; - } - - get vehicleType$() { - return this.batchTechRecordService.vehicleType$; - } - - statusChange(): void { - return this.batchTechRecordService.setVehicleStatus(this.form.get('vehicleStatus')?.value); - } - - get isVehicleStatusValid(): boolean { - const errors: GlobalError[] = []; - - DynamicFormService.validate(this.form, errors); - - this.globalErrorService.setErrors(errors); - - return this.form.valid; - } - - handleSubmit() { - this.summary?.checkForms(); - const check = this.isVehicleStatusValid; - - if (!this.isInvalid && check) { - this.store - .select(selectTechRecord) - .pipe( - withLatestFrom(this.batchTechRecordService.batchVehicles$), - take(1), - map(([record, batch]) => - batch.map( - (v): BatchUpdateVehicleModel => - ({ - ...record, - techRecord_statusCode: this.form.value.vehicleStatus ?? StatusCodes.PROVISIONAL, - vin: v.vin, - trailerId: v.vehicleType === VehicleTypes.TRL ? v.trailerIdOrVrm : undefined, - primaryVrm: v.vehicleType !== VehicleTypes.TRL ? v.trailerIdOrVrm : undefined, - systemNumber: v.systemNumber, - createdTimestamp: v.createdTimestamp, - } as unknown as BatchUpdateVehicleModel), - )), - ) - .subscribe((vehicleList) => { - vehicleList.forEach((vehicle) => { - if (!vehicle.systemNumber) { - this.store.dispatch(createVehicleRecord({ vehicle: vehicle as unknown as TechRecordType<'put'> })); - } else { - this.technicalRecordService.updateEditingTechRecord(vehicle); - this.store.dispatch(updateTechRecord({ systemNumber: vehicle.systemNumber, createdTimestamp: vehicle.createdTimestamp })); - } - }); - this.technicalRecordService.clearSectionTemplateStates(); - void this.router.navigate(['batch-results'], { relativeTo: this.route }); - }); - } - } + @ViewChild(TechRecordSummaryComponent) summary?: TechRecordSummaryComponent; + isInvalid = false; + form: CustomFormGroup; + public vehicleStatusOptions: MultiOptions = [ + { label: 'Provisional', value: StatusCodes.PROVISIONAL }, + { label: 'Current', value: StatusCodes.CURRENT }, + ]; + + constructor( + private route: ActivatedRoute, + private router: Router, + private store: Store, + private technicalRecordService: TechnicalRecordService, + private batchTechRecordService: BatchTechnicalRecordService, + private globalErrorService: GlobalErrorService + ) { + this.store + .select(selectTechRecord) + .pipe(take(1)) + .subscribe((vehicle) => { + if (!vehicle) { + void this.router.navigate(['..'], { relativeTo: this.route }); + } + }); + + this.form = new CustomFormGroup( + { name: 'form-group', type: FormNodeTypes.GROUP }, + { + vehicleStatus: new CustomFormControl( + { name: 'change-vehicle-status-select', label: 'Vehicle status', type: FormNodeTypes.CONTROL }, + '', + [Validators.required] + ), + } + ); + + this.batchTechRecordService.vehicleStatus$.pipe(take(1)).subscribe((vehicleStatus) => { + if (this.form) { + this.form.patchValue({ vehicleStatus }); + } + }); + } + + get vehicle$(): Observable { + return this.store.select(selectTechRecord); + } + + get applicationId$() { + return this.batchTechRecordService.applicationId$; + } + + get isBatch$() { + return this.batchTechRecordService.isBatchCreate$; + } + + get batchCount$() { + return this.batchTechRecordService.batchCount$; + } + + get vehicleType$() { + return this.batchTechRecordService.vehicleType$; + } + + statusChange(): void { + return this.batchTechRecordService.setVehicleStatus(this.form.get('vehicleStatus')?.value); + } + + get isVehicleStatusValid(): boolean { + const errors: GlobalError[] = []; + + DynamicFormService.validate(this.form, errors); + + this.globalErrorService.setErrors(errors); + + return this.form.valid; + } + + handleSubmit() { + this.summary?.checkForms(); + const check = this.isVehicleStatusValid; + + if (!this.isInvalid && check) { + this.store + .select(selectTechRecord) + .pipe( + withLatestFrom(this.batchTechRecordService.batchVehicles$), + take(1), + map(([record, batch]) => + batch.map( + (v): BatchUpdateVehicleModel => + ({ + ...record, + techRecord_statusCode: this.form.value.vehicleStatus ?? StatusCodes.PROVISIONAL, + vin: v.vin, + trailerId: v.vehicleType === VehicleTypes.TRL ? v.trailerIdOrVrm : undefined, + primaryVrm: v.vehicleType !== VehicleTypes.TRL ? v.trailerIdOrVrm : undefined, + systemNumber: v.systemNumber, + createdTimestamp: v.createdTimestamp, + }) as unknown as BatchUpdateVehicleModel + ) + ) + ) + .subscribe((vehicleList) => { + vehicleList.forEach((vehicle) => { + if (!vehicle.systemNumber) { + this.store.dispatch(createVehicleRecord({ vehicle: vehicle as unknown as TechRecordType<'put'> })); + } else { + this.technicalRecordService.updateEditingTechRecord(vehicle); + this.store.dispatch( + updateTechRecord({ systemNumber: vehicle.systemNumber, createdTimestamp: vehicle.createdTimestamp }) + ); + } + }); + this.technicalRecordService.clearSectionTemplateStates(); + void this.router.navigate(['batch-results'], { relativeTo: this.route }); + }); + } + } } diff --git a/src/app/features/tech-record/create-batch/components/select-vehicle-type/select-vehicle-type.component.spec.ts b/src/app/features/tech-record/create-batch/components/select-vehicle-type/select-vehicle-type.component.spec.ts index 12aec546b7..de9f9b3990 100644 --- a/src/app/features/tech-record/create-batch/components/select-vehicle-type/select-vehicle-type.component.spec.ts +++ b/src/app/features/tech-record/create-batch/components/select-vehicle-type/select-vehicle-type.component.spec.ts @@ -14,76 +14,76 @@ import { of } from 'rxjs'; import { SelectVehicleTypeComponent } from './select-vehicle-type.component'; describe('SelectVehicleTypeComponent', () => { - let component: SelectVehicleTypeComponent; - let fixture: ComponentFixture; - let errorService: GlobalErrorService; - let route: ActivatedRoute; - let router: Router; - - beforeEach(async () => { - await TestBed.configureTestingModule({ - declarations: [SelectVehicleTypeComponent], - imports: [DynamicFormsModule, HttpClientTestingModule, ReactiveFormsModule, RouterTestingModule, SharedModule], - providers: [ - GlobalErrorService, - provideMockStore({ initialState: initialAppState }), - { provide: ActivatedRoute, useValue: { params: of([{ vehicleType: 'trl' }]) } }, - ], - }).compileComponents(); - - fixture = TestBed.createComponent(SelectVehicleTypeComponent); - errorService = TestBed.inject(GlobalErrorService); - route = TestBed.inject(ActivatedRoute); - router = TestBed.inject(Router); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); - - describe('get vehicleTypeOptions', () => { - it('should return the expected options', () => { - expect(component.vehicleTypeOptions).toBeTruthy(); - }); - }); - - describe('cancel', () => { - it('should clear all errors', () => { - jest.spyOn(router, 'navigate').mockImplementation(); - - const clearErrorsSpy = jest.spyOn(errorService, 'clearErrors'); - - component.cancel(); - - expect(clearErrorsSpy).toHaveBeenCalledTimes(1); - }); - - it('should navigate back to the previous page', () => { - const navigateSpy = jest.spyOn(router, 'navigate').mockImplementation(() => Promise.resolve(true)); - - component.cancel(); - - expect(navigateSpy).toHaveBeenCalledWith(['..'], { relativeTo: route }); - }); - }); - - describe('handleSubmit', () => { - it('should do nothing if the form is not valid', () => { - jest.spyOn(component, 'isFormValid', 'get').mockReturnValue(false); - const navigateSpy = jest.spyOn(router, 'navigate'); - component.handleSubmit(VehicleTypes.TRL); - expect(navigateSpy).toHaveBeenCalledTimes(0); - }); - - it('should navigate to batch records when successful', () => { - jest.spyOn(component, 'isFormValid', 'get').mockReturnValue(true); - const routerSpy = jest.spyOn(router, 'navigate').mockImplementation(() => Promise.resolve(true)); - component.handleSubmit(VehicleTypes.HGV); - - fixture.detectChanges(); - expect(routerSpy).toHaveBeenCalledWith(['hgv'], { relativeTo: route }); - }); - }); + let component: SelectVehicleTypeComponent; + let fixture: ComponentFixture; + let errorService: GlobalErrorService; + let route: ActivatedRoute; + let router: Router; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [SelectVehicleTypeComponent], + imports: [DynamicFormsModule, HttpClientTestingModule, ReactiveFormsModule, RouterTestingModule, SharedModule], + providers: [ + GlobalErrorService, + provideMockStore({ initialState: initialAppState }), + { provide: ActivatedRoute, useValue: { params: of([{ vehicleType: 'trl' }]) } }, + ], + }).compileComponents(); + + fixture = TestBed.createComponent(SelectVehicleTypeComponent); + errorService = TestBed.inject(GlobalErrorService); + route = TestBed.inject(ActivatedRoute); + router = TestBed.inject(Router); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + describe('get vehicleTypeOptions', () => { + it('should return the expected options', () => { + expect(component.vehicleTypeOptions).toBeTruthy(); + }); + }); + + describe('cancel', () => { + it('should clear all errors', () => { + jest.spyOn(router, 'navigate').mockImplementation(); + + const clearErrorsSpy = jest.spyOn(errorService, 'clearErrors'); + + component.cancel(); + + expect(clearErrorsSpy).toHaveBeenCalledTimes(1); + }); + + it('should navigate back to the previous page', () => { + const navigateSpy = jest.spyOn(router, 'navigate').mockImplementation(() => Promise.resolve(true)); + + component.cancel(); + + expect(navigateSpy).toHaveBeenCalledWith(['..'], { relativeTo: route }); + }); + }); + + describe('handleSubmit', () => { + it('should do nothing if the form is not valid', () => { + jest.spyOn(component, 'isFormValid', 'get').mockReturnValue(false); + const navigateSpy = jest.spyOn(router, 'navigate'); + component.handleSubmit(VehicleTypes.TRL); + expect(navigateSpy).toHaveBeenCalledTimes(0); + }); + + it('should navigate to batch records when successful', () => { + jest.spyOn(component, 'isFormValid', 'get').mockReturnValue(true); + const routerSpy = jest.spyOn(router, 'navigate').mockImplementation(() => Promise.resolve(true)); + component.handleSubmit(VehicleTypes.HGV); + + fixture.detectChanges(); + expect(routerSpy).toHaveBeenCalledWith(['hgv'], { relativeTo: route }); + }); + }); }); diff --git a/src/app/features/tech-record/create-batch/components/select-vehicle-type/select-vehicle-type.component.ts b/src/app/features/tech-record/create-batch/components/select-vehicle-type/select-vehicle-type.component.ts index 8ea572c169..196ad264a4 100644 --- a/src/app/features/tech-record/create-batch/components/select-vehicle-type/select-vehicle-type.component.ts +++ b/src/app/features/tech-record/create-batch/components/select-vehicle-type/select-vehicle-type.component.ts @@ -5,9 +5,7 @@ import { GlobalError } from '@core/components/global-error/global-error.interfac import { GlobalErrorService } from '@core/components/global-error/global-error.service'; import { TechRecordType } from '@dvsa/cvs-type-definitions/types/v3/tech-record/tech-record-verb'; import { DynamicFormService } from '@forms/services/dynamic-form.service'; -import { - CustomFormControl, CustomFormGroup, FormNodeOption, FormNodeTypes, -} from '@forms/services/dynamic-form.types'; +import { CustomFormControl, CustomFormGroup, FormNodeOption, FormNodeTypes } from '@forms/services/dynamic-form.types'; import { CustomValidators } from '@forms/validators/custom-validators'; import { StatusCodes, TrailerFormType, VehicleTypes } from '@models/vehicle-tech-record.model'; import { Store } from '@ngrx/store'; @@ -17,78 +15,88 @@ import { selectTechRecord } from '@store/technical-records'; import { take } from 'rxjs'; @Component({ - selector: 'app-select-vehicle-type', - templateUrl: './select-vehicle-type.component.html', + selector: 'app-select-vehicle-type', + templateUrl: './select-vehicle-type.component.html', }) export class SelectVehicleTypeComponent { - form: CustomFormGroup = new CustomFormGroup( - { name: 'form-group', type: FormNodeTypes.GROUP }, - { - vehicleType: new CustomFormControl({ name: 'vehicle-type', label: 'Vehicle type', type: FormNodeTypes.CONTROL }, '', [Validators.required]), - tes1Tes2: new CustomFormControl({ name: 'tes1-tes2', label: 'Trailer form type', type: FormNodeTypes.CONTROL }, '', [ - CustomValidators.requiredIfEquals('vehicleType', [VehicleTypes.TRL]), - ]), - }, - ); + form: CustomFormGroup = new CustomFormGroup( + { name: 'form-group', type: FormNodeTypes.GROUP }, + { + vehicleType: new CustomFormControl( + { name: 'vehicle-type', label: 'Vehicle type', type: FormNodeTypes.CONTROL }, + '', + [Validators.required] + ), + tes1Tes2: new CustomFormControl( + { name: 'tes1-tes2', label: 'Trailer form type', type: FormNodeTypes.CONTROL }, + '', + [CustomValidators.requiredIfEquals('vehicleType', [VehicleTypes.TRL])] + ), + } + ); - public vehicleTypeOptions: Array> = [ - { label: 'Heavy goods vehicle (HGV)', value: VehicleTypes.HGV }, - { label: 'Public service vehicle (PSV)', value: VehicleTypes.PSV }, - { label: 'Trailer (TRL)', value: VehicleTypes.TRL }, - ]; + public vehicleTypeOptions: Array> = [ + { label: 'Heavy goods vehicle (HGV)', value: VehicleTypes.HGV }, + { label: 'Public service vehicle (PSV)', value: VehicleTypes.PSV }, + { label: 'Trailer (TRL)', value: VehicleTypes.TRL }, + ]; - public tes1Tes2Options: Array> = [ - { label: 'TES 1', value: TrailerFormType.TES1 }, - { label: 'TES 2', value: TrailerFormType.TES2 }, - ]; + public tes1Tes2Options: Array> = [ + { label: 'TES 1', value: TrailerFormType.TES1 }, + { label: 'TES 2', value: TrailerFormType.TES2 }, + ]; - constructor( - private globalErrorService: GlobalErrorService, - private batchTechRecordService: BatchTechnicalRecordService, - private trs: TechnicalRecordService, - private route: ActivatedRoute, - private router: Router, - private store: Store, - ) { - this.batchTechRecordService.clearBatch(); - this.trs.clearSectionTemplateStates(); - } + constructor( + private globalErrorService: GlobalErrorService, + private batchTechRecordService: BatchTechnicalRecordService, + private trs: TechnicalRecordService, + private route: ActivatedRoute, + private router: Router, + private store: Store + ) { + this.batchTechRecordService.clearBatch(); + this.trs.clearSectionTemplateStates(); + } - get isFormValid(): boolean { - const errors: GlobalError[] = []; + get isFormValid(): boolean { + const errors: GlobalError[] = []; - DynamicFormService.validate(this.form, errors); + DynamicFormService.validate(this.form, errors); - this.globalErrorService.setErrors(errors); + this.globalErrorService.setErrors(errors); - return this.form.valid; - } + return this.form.valid; + } - cancel() { - this.globalErrorService.clearErrors(); - void this.router.navigate(['..'], { relativeTo: this.route }); - } + cancel() { + this.globalErrorService.clearErrors(); + void this.router.navigate(['..'], { relativeTo: this.route }); + } - handleSubmit(type: VehicleTypes): void { - if (!this.isFormValid) { - return; - } - if (type === VehicleTypes.PSV) { - this.batchTechRecordService.setVehicleStatus(StatusCodes.CURRENT); - } else if (type === VehicleTypes.TRL && this.form.value.tes1Tes2 === TrailerFormType.TES2) { - this.batchTechRecordService.setVehicleStatus(StatusCodes.PROVISIONAL); - } + handleSubmit(type: VehicleTypes): void { + if (!this.isFormValid) { + return; + } + if (type === VehicleTypes.PSV) { + this.batchTechRecordService.setVehicleStatus(StatusCodes.CURRENT); + } else if (type === VehicleTypes.TRL && this.form.value.tes1Tes2 === TrailerFormType.TES2) { + this.batchTechRecordService.setVehicleStatus(StatusCodes.PROVISIONAL); + } - this.batchTechRecordService.setVehicleType(type); + this.batchTechRecordService.setVehicleType(type); - this.store - .select(selectTechRecord) - .pipe(take(1)) - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - .subscribe((vehicle) => !vehicle && this.trs.updateEditingTechRecord({ ...vehicle!, techRecord_vehicleType: type } as TechRecordType<'put'>)); + this.store + .select(selectTechRecord) + .pipe(take(1)) + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + .subscribe( + (vehicle) => + !vehicle && + this.trs.updateEditingTechRecord({ ...vehicle!, techRecord_vehicleType: type } as TechRecordType<'put'>) + ); - this.trs.generateEditingVehicleTechnicalRecordFromVehicleType(type); + this.trs.generateEditingVehicleTechnicalRecordFromVehicleType(type); - void this.router.navigate([type], { relativeTo: this.route }); - } + void this.router.navigate([type], { relativeTo: this.route }); + } } diff --git a/src/app/features/tech-record/create-batch/create-batch-routing.module.ts b/src/app/features/tech-record/create-batch/create-batch-routing.module.ts index 108faf3131..0f4b59f4c0 100644 --- a/src/app/features/tech-record/create-batch/create-batch-routing.module.ts +++ b/src/app/features/tech-record/create-batch/create-batch-routing.module.ts @@ -3,9 +3,9 @@ import { RouterModule, Routes } from '@angular/router'; import { MsalGuard } from '@azure/msal-angular'; import { RoleGuard } from '@guards/role-guard/roles.guard'; import { Roles } from '@models/roles.enum'; +import { TechRecordCreateBatchRoutes } from '@models/routes.enum'; import { RouterOutletComponent } from '@shared/components/router-outlet/router-outlet.component'; import { techRecordDataResolver } from 'src/app/resolvers/tech-record-data/tech-record-data.resolver'; -import { TechRecordCreateBatchRoutes } from '@models/routes.enum'; import { TechRecordSearchTyresComponent } from '../components/tech-record-search-tyres/tech-record-search-tyres.component'; import { BatchVehicleDetailsComponent } from './components/batch-vehicle-details/batch-vehicle-details.component'; import { BatchVehicleResultsComponent } from './components/batch-vehicle-results/batch-vehicle-results.component'; @@ -13,57 +13,60 @@ import { BatchVehicleTemplateComponent } from './components/batch-vehicle-templa import { SelectVehicleTypeComponent } from './components/select-vehicle-type/select-vehicle-type.component'; const routes: Routes = [ - { - path: '', - component: RouterOutletComponent, - data: { roles: Roles.TechRecordCreate }, - canActivate: [MsalGuard, RoleGuard], - resolve: { - data: techRecordDataResolver, - }, - children: [ - { - path: '', - component: SelectVehicleTypeComponent, - data: { roles: Roles.TechRecordCreate }, - canActivate: [MsalGuard, RoleGuard], - }, - { - path: TechRecordCreateBatchRoutes.RECORD, - component: RouterOutletComponent, - data: { title: 'Batch Record', roles: Roles.TechRecordCreate, isCustomLayout: true }, - children: [ - { - path: '', - component: BatchVehicleTemplateComponent, - data: { - title: 'Batch Record', roles: Roles.TechRecordCreate, isCustomLayout: true, isEditing: true, - }, - }, - { - path: TechRecordCreateBatchRoutes.DETAILS, - component: BatchVehicleDetailsComponent, - data: { title: 'Add batch of vehicles', roles: Roles.TechRecordCreate, isEditing: true }, - }, - { - path: TechRecordCreateBatchRoutes.BATCH_RESULT, - data: { title: 'Batch summary' }, - component: BatchVehicleResultsComponent, - }, - { - path: TechRecordCreateBatchRoutes.TYRE_SEARCH, - component: TechRecordSearchTyresComponent, - data: { title: 'Tyre search', roles: Roles.TechRecordCreate, isEditing: true }, - canActivate: [MsalGuard, RoleGuard], - }, - ], - }, - ], - }, + { + path: '', + component: RouterOutletComponent, + data: { roles: Roles.TechRecordCreate }, + canActivate: [MsalGuard, RoleGuard], + resolve: { + data: techRecordDataResolver, + }, + children: [ + { + path: '', + component: SelectVehicleTypeComponent, + data: { roles: Roles.TechRecordCreate }, + canActivate: [MsalGuard, RoleGuard], + }, + { + path: TechRecordCreateBatchRoutes.RECORD, + component: RouterOutletComponent, + data: { title: 'Batch Record', roles: Roles.TechRecordCreate, isCustomLayout: true }, + children: [ + { + path: '', + component: BatchVehicleTemplateComponent, + data: { + title: 'Batch Record', + roles: Roles.TechRecordCreate, + isCustomLayout: true, + isEditing: true, + }, + }, + { + path: TechRecordCreateBatchRoutes.DETAILS, + component: BatchVehicleDetailsComponent, + data: { title: 'Add batch of vehicles', roles: Roles.TechRecordCreate, isEditing: true }, + }, + { + path: TechRecordCreateBatchRoutes.BATCH_RESULT, + data: { title: 'Batch summary' }, + component: BatchVehicleResultsComponent, + }, + { + path: TechRecordCreateBatchRoutes.TYRE_SEARCH, + component: TechRecordSearchTyresComponent, + data: { title: 'Tyre search', roles: Roles.TechRecordCreate, isEditing: true }, + canActivate: [MsalGuard, RoleGuard], + }, + ], + }, + ], + }, ]; @NgModule({ - imports: [RouterModule.forChild(routes)], - exports: [RouterModule], + imports: [RouterModule.forChild(routes)], + exports: [RouterModule], }) export class CreateBatchRoutingModule {} diff --git a/src/app/features/tech-record/create-batch/create-batch.module.ts b/src/app/features/tech-record/create-batch/create-batch.module.ts index 4c5538a73d..1c600ba160 100644 --- a/src/app/features/tech-record/create-batch/create-batch.module.ts +++ b/src/app/features/tech-record/create-batch/create-batch.module.ts @@ -7,23 +7,28 @@ import { DynamicFormsModule } from '@forms/dynamic-forms.module'; import { SharedModule } from '@shared/shared.module'; import { CreateTechRecordsModule } from '../create/create-tech-records.module'; import { SharedTechRecordsModule } from '../shared-tech-record.module'; -import { BatchVehicleTemplateComponent } from './components/batch-vehicle-template/batch-vehicle-template.component'; -import { CreateBatchRoutingModule } from './create-batch-routing.module'; import { BatchVehicleDetailsComponent } from './components/batch-vehicle-details/batch-vehicle-details.component'; import { BatchVehicleResultsComponent } from './components/batch-vehicle-results/batch-vehicle-results.component'; +import { BatchVehicleTemplateComponent } from './components/batch-vehicle-template/batch-vehicle-template.component'; import { SelectVehicleTypeComponent } from './components/select-vehicle-type/select-vehicle-type.component'; +import { CreateBatchRoutingModule } from './create-batch-routing.module'; @NgModule({ - declarations: [BatchVehicleTemplateComponent, BatchVehicleDetailsComponent, BatchVehicleResultsComponent, SelectVehicleTypeComponent], - imports: [ - CommonModule, - CreateBatchRoutingModule, - SharedTechRecordsModule, - ReactiveFormsModule, - DynamicFormsModule, - RouterModule, - SharedModule, - CreateTechRecordsModule, - ], + declarations: [ + BatchVehicleTemplateComponent, + BatchVehicleDetailsComponent, + BatchVehicleResultsComponent, + SelectVehicleTypeComponent, + ], + imports: [ + CommonModule, + CreateBatchRoutingModule, + SharedTechRecordsModule, + ReactiveFormsModule, + DynamicFormsModule, + RouterModule, + SharedModule, + CreateTechRecordsModule, + ], }) export class CreateBatchModule {} diff --git a/src/app/features/tech-record/create/components/hydrate-new-vehicle-record/hydrate-new-vehicle-record.component.spec.ts b/src/app/features/tech-record/create/components/hydrate-new-vehicle-record/hydrate-new-vehicle-record.component.spec.ts index 2f0cb1ee55..1eae33542b 100644 --- a/src/app/features/tech-record/create/components/hydrate-new-vehicle-record/hydrate-new-vehicle-record.component.spec.ts +++ b/src/app/features/tech-record/create/components/hydrate-new-vehicle-record/hydrate-new-vehicle-record.component.spec.ts @@ -1,7 +1,5 @@ import { HttpClientTestingModule } from '@angular/common/http/testing'; -import { - ComponentFixture, fakeAsync, TestBed, tick, -} from '@angular/core/testing'; +import { ComponentFixture, TestBed, fakeAsync, tick } from '@angular/core/testing'; import { ActivatedRoute, Router } from '@angular/router'; import { RouterTestingModule } from '@angular/router/testing'; import { GlobalErrorService } from '@core/components/global-error/global-error.service'; @@ -9,110 +7,110 @@ import { TechRecordType } from '@dvsa/cvs-type-definitions/types/v3/tech-record/ import { provideMockActions } from '@ngrx/effects/testing'; import { Action } from '@ngrx/store'; import { MockStore, provideMockStore } from '@ngrx/store/testing'; +import { UserService } from '@services/user-service/user-service'; import { initialAppState } from '@store/index'; import { selectRouteData } from '@store/router/selectors/router.selectors'; import { createVehicleRecordSuccess } from '@store/technical-records'; -import { firstValueFrom, of, ReplaySubject } from 'rxjs'; -import { UserService } from '@services/user-service/user-service'; +import { ReplaySubject, firstValueFrom, of } from 'rxjs'; import { HydrateNewVehicleRecordComponent } from './hydrate-new-vehicle-record.component'; describe('HydrateNewVehicleRecordComponent', () => { - let component: HydrateNewVehicleRecordComponent; - let fixture: ComponentFixture; - const actions$ = new ReplaySubject(); - let errorService: GlobalErrorService; - let route: ActivatedRoute; - let router: Router; - let store: MockStore; - - beforeEach(async () => { - await TestBed.configureTestingModule({ - declarations: [HydrateNewVehicleRecordComponent], - providers: [ - provideMockActions(() => actions$), - provideMockStore({ initialState: initialAppState }), - { - provide: UserService, - useValue: { - name$: of('tester'), - }, - }, - ], - imports: [HttpClientTestingModule, RouterTestingModule], - }).compileComponents(); - }); - - beforeEach(() => { - fixture = TestBed.createComponent(HydrateNewVehicleRecordComponent); - route = TestBed.inject(ActivatedRoute); - errorService = TestBed.inject(GlobalErrorService); - router = TestBed.inject(Router); - store = TestBed.inject(MockStore); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); - - describe('get vehicle$', () => { - it('should return the editable vehicle', async () => { - const expectedVehicle = { systemNumber: 'foo', createdTimestamp: 'bar', vin: 'testVin' }; - - jest.spyOn(store, 'select').mockReturnValue(of(expectedVehicle)); - - const vehicle = await firstValueFrom(component.vehicle$); - expect(vehicle).toEqual(expectedVehicle); - }); - }); - - describe('navigate', () => { - it('should clear all errors', () => { - jest.spyOn(router, 'navigate').mockImplementation(); - - const clearErrorsSpy = jest.spyOn(errorService, 'clearErrors'); - - component.navigate(); - - expect(clearErrorsSpy).toHaveBeenCalledTimes(1); - }); - // TODO V3 HGV PSV TRL - it('should navigate back to batch results', () => { - const navigateSpy = jest.spyOn(router, 'navigate').mockImplementation(() => Promise.resolve(true)); - - component.navigate(); - - expect(navigateSpy).toHaveBeenCalledWith(['batch-results'], { relativeTo: route }); - }); - }); - - describe('handleSubmit', () => { - it('should not dispatch createVehicleRecord', () => { - const dispatchSpy = jest.spyOn(store, 'dispatch'); - - component.isInvalid = true; - - component.handleSubmit(); - - expect(dispatchSpy).not.toHaveBeenCalled(); - }); - - it('should navigate back', fakeAsync(() => { - const navigateSpy = jest.spyOn(router, 'navigate').mockImplementation(() => Promise.resolve(true)); - - store.overrideSelector(selectRouteData, { data: { isEditing: true } }); - - component.handleSubmit(); - - actions$.next( - createVehicleRecordSuccess({ - vehicleTechRecord: { systemNumber: 'foo', createdTimestamp: 'bar', vin: 'testVin' } as TechRecordType<'get'>, - }), - ); - tick(); - - expect(navigateSpy).toHaveBeenCalledTimes(1); - })); - }); + let component: HydrateNewVehicleRecordComponent; + let fixture: ComponentFixture; + const actions$ = new ReplaySubject(); + let errorService: GlobalErrorService; + let route: ActivatedRoute; + let router: Router; + let store: MockStore; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [HydrateNewVehicleRecordComponent], + providers: [ + provideMockActions(() => actions$), + provideMockStore({ initialState: initialAppState }), + { + provide: UserService, + useValue: { + name$: of('tester'), + }, + }, + ], + imports: [HttpClientTestingModule, RouterTestingModule], + }).compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(HydrateNewVehicleRecordComponent); + route = TestBed.inject(ActivatedRoute); + errorService = TestBed.inject(GlobalErrorService); + router = TestBed.inject(Router); + store = TestBed.inject(MockStore); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + describe('get vehicle$', () => { + it('should return the editable vehicle', async () => { + const expectedVehicle = { systemNumber: 'foo', createdTimestamp: 'bar', vin: 'testVin' }; + + jest.spyOn(store, 'select').mockReturnValue(of(expectedVehicle)); + + const vehicle = await firstValueFrom(component.vehicle$); + expect(vehicle).toEqual(expectedVehicle); + }); + }); + + describe('navigate', () => { + it('should clear all errors', () => { + jest.spyOn(router, 'navigate').mockImplementation(); + + const clearErrorsSpy = jest.spyOn(errorService, 'clearErrors'); + + component.navigate(); + + expect(clearErrorsSpy).toHaveBeenCalledTimes(1); + }); + // TODO V3 HGV PSV TRL + it('should navigate back to batch results', () => { + const navigateSpy = jest.spyOn(router, 'navigate').mockImplementation(() => Promise.resolve(true)); + + component.navigate(); + + expect(navigateSpy).toHaveBeenCalledWith(['batch-results'], { relativeTo: route }); + }); + }); + + describe('handleSubmit', () => { + it('should not dispatch createVehicleRecord', () => { + const dispatchSpy = jest.spyOn(store, 'dispatch'); + + component.isInvalid = true; + + component.handleSubmit(); + + expect(dispatchSpy).not.toHaveBeenCalled(); + }); + + it('should navigate back', fakeAsync(() => { + const navigateSpy = jest.spyOn(router, 'navigate').mockImplementation(() => Promise.resolve(true)); + + store.overrideSelector(selectRouteData, { data: { isEditing: true } }); + + component.handleSubmit(); + + actions$.next( + createVehicleRecordSuccess({ + vehicleTechRecord: { systemNumber: 'foo', createdTimestamp: 'bar', vin: 'testVin' } as TechRecordType<'get'>, + }) + ); + tick(); + + expect(navigateSpy).toHaveBeenCalledTimes(1); + })); + }); }); diff --git a/src/app/features/tech-record/create/components/hydrate-new-vehicle-record/hydrate-new-vehicle-record.component.ts b/src/app/features/tech-record/create/components/hydrate-new-vehicle-record/hydrate-new-vehicle-record.component.ts index 82d7609346..51444abcbd 100644 --- a/src/app/features/tech-record/create/components/hydrate-new-vehicle-record/hydrate-new-vehicle-record.component.ts +++ b/src/app/features/tech-record/create/components/hydrate-new-vehicle-record/hydrate-new-vehicle-record.component.ts @@ -1,6 +1,4 @@ -import { - Component, OnDestroy, OnInit, ViewChild, -} from '@angular/core'; +import { Component, OnDestroy, OnInit, ViewChild } from '@angular/core'; import { FormGroup } from '@angular/forms'; import { ActivatedRoute, Router } from '@angular/router'; import { GlobalErrorService } from '@core/components/global-error/global-error.service'; @@ -10,125 +8,132 @@ import { Actions, ofType } from '@ngrx/effects'; import { Store } from '@ngrx/store'; import { BatchTechnicalRecordService } from '@services/batch-technical-record/batch-technical-record.service'; import { TechnicalRecordService } from '@services/technical-record/technical-record.service'; +import { UserService } from '@services/user-service/user-service'; import { - clearADRDetailsBeforeUpdate, - createVehicleRecord, - createVehicleRecordSuccess, - selectTechRecord, updateADRAdditionalExaminerNotes, + clearADRDetailsBeforeUpdate, + createVehicleRecord, + createVehicleRecordSuccess, + selectTechRecord, + updateADRAdditionalExaminerNotes, } from '@store/technical-records'; import { BatchRecord } from '@store/technical-records/reducers/batch-create.reducer'; import { TechnicalRecordServiceState } from '@store/technical-records/reducers/technical-record-service.reducer'; -import { - Observable, Subject, map, take, takeUntil, withLatestFrom, -} from 'rxjs'; -import { UserService } from '@services/user-service/user-service'; +import { Observable, Subject, map, take, takeUntil, withLatestFrom } from 'rxjs'; import { TechRecordSummaryComponent } from '../../../components/tech-record-summary/tech-record-summary.component'; @Component({ - selector: 'app-hydrate-new-vehicle-record', - templateUrl: './hydrate-new-vehicle-record.component.html', + selector: 'app-hydrate-new-vehicle-record', + templateUrl: './hydrate-new-vehicle-record.component.html', }) export class HydrateNewVehicleRecordComponent implements OnDestroy, OnInit { - @ViewChild(TechRecordSummaryComponent) summary?: TechRecordSummaryComponent; - isInvalid = false; - batchForm?: FormGroup; - username = ''; - - private destroy$ = new Subject(); - - constructor( - private actions$: Actions, - private globalErrorService: GlobalErrorService, - private route: ActivatedRoute, - private router: Router, - private store: Store, - private technicalRecordService: TechnicalRecordService, - private batchTechRecordService: BatchTechnicalRecordService, - public userService$: UserService, - ) { } - - ngOnInit(): void { - this.actions$.pipe(ofType(createVehicleRecordSuccess), takeUntil(this.destroy$)).subscribe(({ vehicleTechRecord }) => { - void this.router.navigate([`/tech-records/${vehicleTechRecord.systemNumber}/${vehicleTechRecord.createdTimestamp}`]); - }); - - this.userService$.name$.pipe(takeUntil(this.destroy$)).subscribe((name) => { - this.username = name; - }); - - this.store - .select(selectTechRecord) - .pipe(take(1)) - .subscribe((vehicle) => { - if (!vehicle) { - void this.router.navigate(['..'], { relativeTo: this.route }); - } - }); - } - - ngOnDestroy(): void { - this.destroy$.next(); - this.destroy$.complete(); - } - - get vehicle$(): Observable { - return this.store.select(selectTechRecord); - } - - get isBatch$(): Observable { - return this.batchTechRecordService.isBatchCreate$; - } - - get batchCount$(): Observable { - return this.batchTechRecordService.batchCount$; - } - - get vehicleTypes(): typeof VehicleTypes { - return VehicleTypes; - } - - navigate(systemNumber?: string, createdTimestamp?: string): void { - this.globalErrorService.clearErrors(); - - if (systemNumber && createdTimestamp) { - void this.router.navigate([`/tech-records/${systemNumber}/${createdTimestamp}`]); - } else { - void this.router.navigate(['batch-results'], { relativeTo: this.route }); - } - } - - handleSubmit(): void { - this.summary?.checkForms(); - - if (this.isInvalid) return; - - this.store.dispatch(updateADRAdditionalExaminerNotes({ username: this.username })); - this.store.dispatch(clearADRDetailsBeforeUpdate()); - this.store - .select(selectTechRecord) - .pipe( - withLatestFrom(this.batchTechRecordService.batchVehicles$), - take(1), - map(([record, batch]) => - (record ? [record as BatchRecord] : []).concat( - batch.map( - (v) => - ({ - ...(record as BatchRecord), - vin: v.vin, - vrms: v.vehicleType !== VehicleTypes.TRL && v.trailerIdOrVrm ? [{ vrm: v.trailerIdOrVrm, isPrimary: true }] : null, - trailerId: v.vehicleType === VehicleTypes.TRL && v.trailerIdOrVrm ? v.trailerIdOrVrm : null, - } as VehicleTechRecordModel), - ), - )), - withLatestFrom(this.isBatch$), - ) - .subscribe(([vehicleList, isBatch]) => { - vehicleList.forEach((vehicle) => { - this.store.dispatch(createVehicleRecord({ vehicle: vehicle as TechRecordType<'put'> })); - }); - this.technicalRecordService.clearSectionTemplateStates(); - if (isBatch) this.navigate(); - }); - } + @ViewChild(TechRecordSummaryComponent) summary?: TechRecordSummaryComponent; + isInvalid = false; + batchForm?: FormGroup; + username = ''; + + private destroy$ = new Subject(); + + constructor( + private actions$: Actions, + private globalErrorService: GlobalErrorService, + private route: ActivatedRoute, + private router: Router, + private store: Store, + private technicalRecordService: TechnicalRecordService, + private batchTechRecordService: BatchTechnicalRecordService, + public userService$: UserService + ) {} + + ngOnInit(): void { + this.actions$ + .pipe(ofType(createVehicleRecordSuccess), takeUntil(this.destroy$)) + .subscribe(({ vehicleTechRecord }) => { + void this.router.navigate([ + `/tech-records/${vehicleTechRecord.systemNumber}/${vehicleTechRecord.createdTimestamp}`, + ]); + }); + + this.userService$.name$.pipe(takeUntil(this.destroy$)).subscribe((name) => { + this.username = name; + }); + + this.store + .select(selectTechRecord) + .pipe(take(1)) + .subscribe((vehicle) => { + if (!vehicle) { + void this.router.navigate(['..'], { relativeTo: this.route }); + } + }); + } + + ngOnDestroy(): void { + this.destroy$.next(); + this.destroy$.complete(); + } + + get vehicle$(): Observable { + return this.store.select(selectTechRecord); + } + + get isBatch$(): Observable { + return this.batchTechRecordService.isBatchCreate$; + } + + get batchCount$(): Observable { + return this.batchTechRecordService.batchCount$; + } + + get vehicleTypes(): typeof VehicleTypes { + return VehicleTypes; + } + + navigate(systemNumber?: string, createdTimestamp?: string): void { + this.globalErrorService.clearErrors(); + + if (systemNumber && createdTimestamp) { + void this.router.navigate([`/tech-records/${systemNumber}/${createdTimestamp}`]); + } else { + void this.router.navigate(['batch-results'], { relativeTo: this.route }); + } + } + + handleSubmit(): void { + this.summary?.checkForms(); + + if (this.isInvalid) return; + + this.store.dispatch(updateADRAdditionalExaminerNotes({ username: this.username })); + this.store.dispatch(clearADRDetailsBeforeUpdate()); + this.store + .select(selectTechRecord) + .pipe( + withLatestFrom(this.batchTechRecordService.batchVehicles$), + take(1), + map(([record, batch]) => + (record ? [record as BatchRecord] : []).concat( + batch.map( + (v) => + ({ + ...(record as BatchRecord), + vin: v.vin, + vrms: + v.vehicleType !== VehicleTypes.TRL && v.trailerIdOrVrm + ? [{ vrm: v.trailerIdOrVrm, isPrimary: true }] + : null, + trailerId: v.vehicleType === VehicleTypes.TRL && v.trailerIdOrVrm ? v.trailerIdOrVrm : null, + }) as VehicleTechRecordModel + ) + ) + ), + withLatestFrom(this.isBatch$) + ) + .subscribe(([vehicleList, isBatch]) => { + vehicleList.forEach((vehicle) => { + this.store.dispatch(createVehicleRecord({ vehicle: vehicle as TechRecordType<'put'> })); + }); + this.technicalRecordService.clearSectionTemplateStates(); + if (isBatch) this.navigate(); + }); + } } diff --git a/src/app/features/tech-record/create/create-tech-record.component.spec.ts b/src/app/features/tech-record/create/create-tech-record.component.spec.ts index 672bf76d7d..3d7af5f5f1 100644 --- a/src/app/features/tech-record/create/create-tech-record.component.spec.ts +++ b/src/app/features/tech-record/create/create-tech-record.component.spec.ts @@ -15,185 +15,191 @@ import { of } from 'rxjs'; import { CreateTechRecordComponent } from './create-tech-record.component'; describe('CreateNewVehicleRecordComponent', () => { - let component: CreateTechRecordComponent; - let fixture: ComponentFixture; - let errorService: GlobalErrorService; - let route: ActivatedRoute; - let router: Router; - let techRecordService: TechnicalRecordService; - - beforeEach(async () => { - await TestBed.configureTestingModule({ - declarations: [CreateTechRecordComponent], - imports: [DynamicFormsModule, HttpClientTestingModule, ReactiveFormsModule, RouterTestingModule, SharedModule], - providers: [ - GlobalErrorService, - provideMockStore({ initialState: initialAppState }), - { provide: ActivatedRoute, useValue: { params: of([{ id: 1 }]) } }, - ], - }).compileComponents(); - }); - - beforeEach(() => { - fixture = TestBed.createComponent(CreateTechRecordComponent); - errorService = TestBed.inject(GlobalErrorService); - route = TestBed.inject(ActivatedRoute); - router = TestBed.inject(Router); - techRecordService = TestBed.inject(TechnicalRecordService); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); - - describe('get vehicleTypeOptions', () => { - it('should return the expected options', () => { - expect(component.vehicleTypeOptions).toBeTruthy(); - }); - }); - - describe('get vehicleStatusOptions', () => { - it('should return the expected options', () => { - expect(component.vehicleStatusOptions).toBeTruthy(); - }); - }); - - describe('get isFormValid', () => { - it('should call validate with the vehicleForm and an empty array', () => { - const validateSpy = jest.spyOn(DynamicFormService, 'validate').mockImplementation(); - const isValid = component.isFormValid; - expect(isValid).toBeDefined(); - expect(validateSpy).toHaveBeenCalledTimes(1); - expect(validateSpy).toHaveBeenCalledWith(component.form, []); - }); - - it('should call setErrors with an empty array', () => { - jest.spyOn(DynamicFormService, 'validate').mockImplementation(() => {}); - const setErrorsSpy = jest.spyOn(errorService, 'setErrors').mockImplementation(); - const isValid = component.isFormValid; - expect(isValid).toBeDefined(); - expect(setErrorsSpy).toHaveBeenCalledTimes(1); - expect(setErrorsSpy).toHaveBeenCalledWith([]); - }); - - it('should return vehicleForm.valid', () => { - const formValid = component.isFormValid; - expect(formValid).toBeFalsy(); - }); - }); - - describe('navigateBack', () => { - it('should clear all errors', () => { - jest.spyOn(router, 'navigate').mockImplementation(); - - const clearErrorsSpy = jest.spyOn(errorService, 'clearErrors'); - - component.navigateBack(); - - expect(clearErrorsSpy).toHaveBeenCalledTimes(1); - }); - - it('should navigate back to the previous page', () => { - const navigateSpy = jest.spyOn(router, 'navigate').mockImplementation(() => Promise.resolve(true)); - - component.navigateBack(); - - expect(navigateSpy).toHaveBeenCalledWith(['..'], { relativeTo: route }); - }); - }); - - describe('handleSubmit', () => { - it('should do nothing if the form is not valid', async () => { - const formUniqueSpy = jest.spyOn(component, 'isFormValueUnique').mockImplementation(); - await component.handleSubmit(); - expect(formUniqueSpy).toHaveBeenCalledTimes(0); - }); - - it('should do nothing if the form value not unique', () => { - const isFormValid = jest.spyOn(component, 'isFormValid', 'get').mockReturnValue(true); - const updateEditingSpy = jest.spyOn(techRecordService, 'updateEditingTechRecord'); - const navigateSpy = jest.spyOn(router, 'navigate'); - const generateTechREcordSpy = jest.spyOn(techRecordService, 'generateEditingVehicleTechnicalRecordFromVehicleType'); - void component.handleSubmit(); - - expect(isFormValid).toHaveReturned(); - expect(updateEditingSpy).toHaveBeenCalledTimes(0); - expect(generateTechREcordSpy).toHaveBeenCalledTimes(0); - expect(navigateSpy).toHaveBeenCalledTimes(0); - }); - - it('should navigate to hydrate when successful', async () => { - jest.spyOn(component, 'isFormValid', 'get').mockReturnValue(true); - jest.spyOn(component, 'isFormValueUnique').mockImplementation(() => Promise.resolve(true)); - const routerSpy = jest.spyOn(router, 'navigate').mockImplementation(() => Promise.resolve(true)); - jest.spyOn(techRecordService, 'updateEditingTechRecord'); - - await component.handleSubmit(); - - fixture.detectChanges(); - expect(routerSpy).toHaveBeenCalledWith(['../create/new-record-details'], { relativeTo: route }); - }); - }); - - describe('isVinUnique', () => { - it('should call isUnique with an emptry string and the type of vin', async () => { - const isUniqueSpy = jest.spyOn(techRecordService, 'isUnique').mockImplementation(() => of(true)); - - await component.isVinUnique(); - - expect(isUniqueSpy).toHaveBeenCalledWith('', SEARCH_TYPES.VIN); - }); - - it('should return true when the VIN is unique', async () => { - jest.spyOn(techRecordService, 'isUnique').mockImplementation(() => of(true)); - - const result = await component.isVinUnique(); - - expect(result).toBeTruthy(); - }); - }); - - describe('isVrmUnique', () => { - it('should return true when the VRM is unique', async () => { - jest.spyOn(techRecordService, 'isUnique').mockImplementation(() => of(true)); + let component: CreateTechRecordComponent; + let fixture: ComponentFixture; + let errorService: GlobalErrorService; + let route: ActivatedRoute; + let router: Router; + let techRecordService: TechnicalRecordService; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [CreateTechRecordComponent], + imports: [DynamicFormsModule, HttpClientTestingModule, ReactiveFormsModule, RouterTestingModule, SharedModule], + providers: [ + GlobalErrorService, + provideMockStore({ initialState: initialAppState }), + { provide: ActivatedRoute, useValue: { params: of([{ id: 1 }]) } }, + ], + }).compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(CreateTechRecordComponent); + errorService = TestBed.inject(GlobalErrorService); + route = TestBed.inject(ActivatedRoute); + router = TestBed.inject(Router); + techRecordService = TestBed.inject(TechnicalRecordService); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + describe('get vehicleTypeOptions', () => { + it('should return the expected options', () => { + expect(component.vehicleTypeOptions).toBeTruthy(); + }); + }); + + describe('get vehicleStatusOptions', () => { + it('should return the expected options', () => { + expect(component.vehicleStatusOptions).toBeTruthy(); + }); + }); + + describe('get isFormValid', () => { + it('should call validate with the vehicleForm and an empty array', () => { + const validateSpy = jest.spyOn(DynamicFormService, 'validate').mockImplementation(); + const isValid = component.isFormValid; + expect(isValid).toBeDefined(); + expect(validateSpy).toHaveBeenCalledTimes(1); + expect(validateSpy).toHaveBeenCalledWith(component.form, []); + }); + + it('should call setErrors with an empty array', () => { + jest.spyOn(DynamicFormService, 'validate').mockImplementation(() => {}); + const setErrorsSpy = jest.spyOn(errorService, 'setErrors').mockImplementation(); + const isValid = component.isFormValid; + expect(isValid).toBeDefined(); + expect(setErrorsSpy).toHaveBeenCalledTimes(1); + expect(setErrorsSpy).toHaveBeenCalledWith([]); + }); + + it('should return vehicleForm.valid', () => { + const formValid = component.isFormValid; + expect(formValid).toBeFalsy(); + }); + }); + + describe('navigateBack', () => { + it('should clear all errors', () => { + jest.spyOn(router, 'navigate').mockImplementation(); + + const clearErrorsSpy = jest.spyOn(errorService, 'clearErrors'); + + component.navigateBack(); + + expect(clearErrorsSpy).toHaveBeenCalledTimes(1); + }); + + it('should navigate back to the previous page', () => { + const navigateSpy = jest.spyOn(router, 'navigate').mockImplementation(() => Promise.resolve(true)); + + component.navigateBack(); + + expect(navigateSpy).toHaveBeenCalledWith(['..'], { relativeTo: route }); + }); + }); + + describe('handleSubmit', () => { + it('should do nothing if the form is not valid', async () => { + const formUniqueSpy = jest.spyOn(component, 'isFormValueUnique').mockImplementation(); + await component.handleSubmit(); + expect(formUniqueSpy).toHaveBeenCalledTimes(0); + }); + + it('should do nothing if the form value not unique', () => { + const isFormValid = jest.spyOn(component, 'isFormValid', 'get').mockReturnValue(true); + const updateEditingSpy = jest.spyOn(techRecordService, 'updateEditingTechRecord'); + const navigateSpy = jest.spyOn(router, 'navigate'); + const generateTechREcordSpy = jest.spyOn( + techRecordService, + 'generateEditingVehicleTechnicalRecordFromVehicleType' + ); + void component.handleSubmit(); + + expect(isFormValid).toHaveReturned(); + expect(updateEditingSpy).toHaveBeenCalledTimes(0); + expect(generateTechREcordSpy).toHaveBeenCalledTimes(0); + expect(navigateSpy).toHaveBeenCalledTimes(0); + }); + + it('should navigate to hydrate when successful', async () => { + jest.spyOn(component, 'isFormValid', 'get').mockReturnValue(true); + jest.spyOn(component, 'isFormValueUnique').mockImplementation(() => Promise.resolve(true)); + const routerSpy = jest.spyOn(router, 'navigate').mockImplementation(() => Promise.resolve(true)); + jest.spyOn(techRecordService, 'updateEditingTechRecord'); + + await component.handleSubmit(); + + fixture.detectChanges(); + expect(routerSpy).toHaveBeenCalledWith(['../create/new-record-details'], { relativeTo: route }); + }); + }); + + describe('isVinUnique', () => { + it('should call isUnique with an emptry string and the type of vin', async () => { + const isUniqueSpy = jest.spyOn(techRecordService, 'isUnique').mockImplementation(() => of(true)); + + await component.isVinUnique(); + + expect(isUniqueSpy).toHaveBeenCalledWith('', SEARCH_TYPES.VIN); + }); + + it('should return true when the VIN is unique', async () => { + jest.spyOn(techRecordService, 'isUnique').mockImplementation(() => of(true)); + + const result = await component.isVinUnique(); + + expect(result).toBeTruthy(); + }); + }); + + describe('isVrmUnique', () => { + it('should return true when the VRM is unique', async () => { + jest.spyOn(techRecordService, 'isUnique').mockImplementation(() => of(true)); + + const result = await component.isVrmUnique(); + + expect(result).toBeTruthy(); + }); + + it('should call addError when the VRM is not unique', async () => { + jest.spyOn(techRecordService, 'isUnique').mockImplementation(() => of(false)); + const addErrorSpy = jest.spyOn(errorService, 'addError').mockImplementation(); + + const result = await component.isVrmUnique(); - const result = await component.isVrmUnique(); - - expect(result).toBeTruthy(); - }); + expect(addErrorSpy).toHaveBeenCalledWith({ error: 'Vrm not unique', anchorLink: 'input-vrm-or-trailer-id' }); + expect(result).toBeFalsy(); + }); + }); - it('should call addError when the VRM is not unique', async () => { - jest.spyOn(techRecordService, 'isUnique').mockImplementation(() => of(false)); - const addErrorSpy = jest.spyOn(errorService, 'addError').mockImplementation(); + describe('isTrailerIdUnique', () => { + it('should return true when the trailer ID is unique', async () => { + jest.spyOn(techRecordService, 'isUnique').mockImplementation(() => of(true)); + component.techRecord = { techRecord_vehicleType: 'trl' }; - const result = await component.isVrmUnique(); + const result = await component.isTrailerIdUnique(); - expect(addErrorSpy).toHaveBeenCalledWith({ error: 'Vrm not unique', anchorLink: 'input-vrm-or-trailer-id' }); - expect(result).toBeFalsy(); - }); - }); + expect(result).toBeTruthy(); + }); - describe('isTrailerIdUnique', () => { - it('should return true when the trailer ID is unique', async () => { - jest.spyOn(techRecordService, 'isUnique').mockImplementation(() => of(true)); - component.techRecord = { techRecord_vehicleType: 'trl' }; + it('should call addError when the trailer ID is not unique', async () => { + jest.spyOn(techRecordService, 'isUnique').mockImplementation(() => of(false)); + const addErrorSpy = jest.spyOn(errorService, 'addError').mockImplementation(); + component.techRecord = { techRecord_vehicleType: 'trl' }; - const result = await component.isTrailerIdUnique(); + const result = await component.isTrailerIdUnique(); - expect(result).toBeTruthy(); - }); - - it('should call addError when the trailer ID is not unique', async () => { - jest.spyOn(techRecordService, 'isUnique').mockImplementation(() => of(false)); - const addErrorSpy = jest.spyOn(errorService, 'addError').mockImplementation(); - component.techRecord = { techRecord_vehicleType: 'trl' }; - - const result = await component.isTrailerIdUnique(); - - expect(addErrorSpy).toHaveBeenCalledWith({ error: 'TrailerId not unique', anchorLink: 'input-vrm-or-trailer-id' }); - expect(result).toBeFalsy(); - }); - }); + expect(addErrorSpy).toHaveBeenCalledWith({ + error: 'TrailerId not unique', + anchorLink: 'input-vrm-or-trailer-id', + }); + expect(result).toBeFalsy(); + }); + }); }); diff --git a/src/app/features/tech-record/create/create-tech-record.component.ts b/src/app/features/tech-record/create/create-tech-record.component.ts index e16cf3897d..c71dc0e36d 100644 --- a/src/app/features/tech-record/create/create-tech-record.component.ts +++ b/src/app/features/tech-record/create/create-tech-record.component.ts @@ -10,10 +10,7 @@ import { DynamicFormService } from '@forms/services/dynamic-form.service'; import { CustomFormControl, CustomFormGroup, FormNodeTypes } from '@forms/services/dynamic-form.types'; import { CustomValidators } from '@forms/validators/custom-validators'; import { SEARCH_TYPES } from '@models/search-types-enum'; -import { - StatusCodes, V3TechRecordModel, VehicleTypes, - VehiclesOtherThan, -} from '@models/vehicle-tech-record.model'; +import { StatusCodes, V3TechRecordModel, VehicleTypes, VehiclesOtherThan } from '@models/vehicle-tech-record.model'; import { Store } from '@ngrx/store'; import { BatchTechnicalRecordService } from '@services/batch-technical-record/batch-technical-record.service'; import { TechnicalRecordService } from '@services/technical-record/technical-record.service'; @@ -21,186 +18,199 @@ import { setSpinnerState } from '@store/spinner/actions/spinner.actions'; import { firstValueFrom } from 'rxjs'; @Component({ - selector: 'app-create', - templateUrl: './create-tech-record.component.html', + selector: 'app-create', + templateUrl: './create-tech-record.component.html', }) export class CreateTechRecordComponent implements OnChanges { - techRecord: Partial = {}; - - isDuplicateVinAllowed = false; - isVinUniqueCheckComplete = false; - - vinUnique = false; - vrmUnique = false; - trlUnique = false; - - form = new CustomFormGroup( - { name: 'main-form', type: FormNodeTypes.GROUP }, - { - vin: new CustomFormControl({ name: 'input-vin', label: 'Vin', type: FormNodeTypes.CONTROL }, '', [ - CustomValidators.alphanumeric(), - CustomValidators.validateVinCharacters(), - Validators.minLength(3), - Validators.maxLength(21), - Validators.required, - ]), - vrmTrm: new CustomFormControl({ name: 'input-vrm-or-trailer-id', label: 'VRM/TRM', type: FormNodeTypes.CONTROL }, '', [ - CustomValidators.alphanumeric(), - CustomValidators.notZNumber, - CustomValidators.validateVRMTrailerIdLength('vehicleType'), - Validators.required, - ]), - vehicleStatus: new CustomFormControl( - { name: 'change-vehicle-status-select', label: 'Vehicle status', type: FormNodeTypes.CONTROL }, - StatusCodes.PROVISIONAL, - [Validators.required], - ), - vehicleType: new CustomFormControl({ name: 'change-vehicle-type-select', label: 'Vehicle type', type: FormNodeTypes.CONTROL }, '', [ - Validators.required, - ]), - generateID: new CustomFormControl({ name: 'generate-c-or-z-num', type: FormNodeTypes.CONTROL }, null), - }, - ); - - public vehicleTypeOptions: MultiOptions = [ - { label: 'Heavy goods vehicle (HGV)', value: VehicleTypes.HGV }, - { label: 'Light goods vehicle (LGV)', value: VehicleTypes.LGV }, - { label: 'Public service vehicle (PSV)', value: VehicleTypes.PSV }, - { label: 'Trailer (TRL)', value: VehicleTypes.TRL }, - { label: 'Small Trailer (Small TRL)', value: VehicleTypes.SMALL_TRL }, - { label: 'Car', value: VehicleTypes.CAR }, - { label: 'Motorcycle', value: VehicleTypes.MOTORCYCLE }, - ]; - - constructor( - private globalErrorService: GlobalErrorService, - private technicalRecordService: TechnicalRecordService, - private batchTechRecordService: BatchTechnicalRecordService, - private route: ActivatedRoute, - private router: Router, - private store: Store, - ) { - this.batchTechRecordService.clearBatch(); - this.technicalRecordService.clearSectionTemplateStates(); - } - - ngOnChanges(): void { - this.isVinUniqueCheckComplete = false; - } - - get isFormValid(): boolean { - const errors: GlobalError[] = []; - - DynamicFormService.validate(this.form, errors); - - this.globalErrorService.setErrors(errors); - - return this.form.valid; - } - - get vehicleStatusOptions(): MultiOptions { - return [ - { label: 'Provisional', value: StatusCodes.PROVISIONAL }, - { label: 'Current', value: StatusCodes.CURRENT }, - ]; - } - - get checkboxOptions(): MultiOptions { - return [{ value: true, label: 'Generate a C/T/Z number on submission of the new record' }]; - } - - toggleVrmInput(checked: CheckboxGroupComponent) { - const { vrmTrm } = this.form.controls; - - if (checked.value) { - vrmTrm.removeValidators(Validators.required); - vrmTrm.setValue(null); - vrmTrm.disable(); - } else { - vrmTrm.addValidators(Validators.required); - vrmTrm.setValue(''); - vrmTrm.enable(); - } - } - - navigateBack() { - this.globalErrorService.clearErrors(); - void this.router.navigate(['..'], { relativeTo: this.route }); - } - - async handleSubmit() { - if (!this.isFormValid) { - return; - } - - this.store.dispatch(setSpinnerState({ showSpinner: true })); - - const formValueUnique = await this.isFormValueUnique(); - - this.store.dispatch(setSpinnerState({ showSpinner: false })); - - if (!formValueUnique) { - this.isDuplicateVinAllowed = true; - return; - } - - this.technicalRecordService.updateEditingTechRecord(this.techRecord as TechRecordType<'put'>); - this.technicalRecordService.generateEditingVehicleTechnicalRecordFromVehicleType(this.techRecord.techRecord_vehicleType as VehicleTypes); - this.technicalRecordService.clearSectionTemplateStates(); - await this.router.navigate(['../create/new-record-details'], { relativeTo: this.route }); - } - - async isFormValueUnique() { - const isTrailer = this.form.value.vehicleType === VehicleTypes.TRL; - - this.techRecord.techRecord_vehicleType = this.form.value.vehicleType; - this.techRecord.techRecord_statusCode = this.form.value.vehicleStatus; - - if (!this.isVinUniqueCheckComplete) { - this.vinUnique = await this.isVinUnique(); - } - - if (this.form.controls['generateID'].value) { - return this.vinUnique || this.isDuplicateVinAllowed; - } - - if (isTrailer) { - this.trlUnique = await this.isTrailerIdUnique(); - return (this.vinUnique || this.isDuplicateVinAllowed) && this.trlUnique; - } - this.vrmUnique = await this.isVrmUnique(); - return (this.vinUnique || this.isDuplicateVinAllowed) && this.vrmUnique; - } - - async isVinUnique(): Promise { - this.techRecord.vin = this.form.value.vin; - const isVinUnique = await firstValueFrom(this.technicalRecordService.isUnique(this.techRecord.vin as string, SEARCH_TYPES.VIN)); - this.isVinUniqueCheckComplete = true; - return isVinUnique; - } - - async isVrmUnique() { - (this.techRecord as VehiclesOtherThan<'trl'>).primaryVrm = this.form.value.vrmTrm; - const isVrmUnique = await firstValueFrom( - this.technicalRecordService.isUnique((this.techRecord as VehiclesOtherThan<'trl'>).primaryVrm?.replace(/\s+/g, '') ?? '', SEARCH_TYPES.VRM), - ); - if (!isVrmUnique) { - this.globalErrorService.addError({ error: 'Vrm not unique', anchorLink: 'input-vrm-or-trailer-id' }); - } - return isVrmUnique; - } - - async isTrailerIdUnique() { - if (this.techRecord.techRecord_vehicleType === 'trl') { - this.techRecord.trailerId = this.form.value.vrmTrm; - const isTrailerIdUnique = await firstValueFrom( - this.technicalRecordService.isUnique(this.techRecord.trailerId as string, SEARCH_TYPES.TRAILER_ID), - ); - if (!isTrailerIdUnique) { - this.globalErrorService.addError({ error: 'TrailerId not unique', anchorLink: 'input-vrm-or-trailer-id' }); - } - return isTrailerIdUnique; - } - return false; - } + techRecord: Partial = {}; + + isDuplicateVinAllowed = false; + isVinUniqueCheckComplete = false; + + vinUnique = false; + vrmUnique = false; + trlUnique = false; + + form = new CustomFormGroup( + { name: 'main-form', type: FormNodeTypes.GROUP }, + { + vin: new CustomFormControl({ name: 'input-vin', label: 'Vin', type: FormNodeTypes.CONTROL }, '', [ + CustomValidators.alphanumeric(), + CustomValidators.validateVinCharacters(), + Validators.minLength(3), + Validators.maxLength(21), + Validators.required, + ]), + vrmTrm: new CustomFormControl( + { name: 'input-vrm-or-trailer-id', label: 'VRM/TRM', type: FormNodeTypes.CONTROL }, + '', + [ + CustomValidators.alphanumeric(), + CustomValidators.notZNumber, + CustomValidators.validateVRMTrailerIdLength('vehicleType'), + Validators.required, + ] + ), + vehicleStatus: new CustomFormControl( + { name: 'change-vehicle-status-select', label: 'Vehicle status', type: FormNodeTypes.CONTROL }, + StatusCodes.PROVISIONAL, + [Validators.required] + ), + vehicleType: new CustomFormControl( + { name: 'change-vehicle-type-select', label: 'Vehicle type', type: FormNodeTypes.CONTROL }, + '', + [Validators.required] + ), + generateID: new CustomFormControl({ name: 'generate-c-or-z-num', type: FormNodeTypes.CONTROL }, null), + } + ); + + public vehicleTypeOptions: MultiOptions = [ + { label: 'Heavy goods vehicle (HGV)', value: VehicleTypes.HGV }, + { label: 'Light goods vehicle (LGV)', value: VehicleTypes.LGV }, + { label: 'Public service vehicle (PSV)', value: VehicleTypes.PSV }, + { label: 'Trailer (TRL)', value: VehicleTypes.TRL }, + { label: 'Small Trailer (Small TRL)', value: VehicleTypes.SMALL_TRL }, + { label: 'Car', value: VehicleTypes.CAR }, + { label: 'Motorcycle', value: VehicleTypes.MOTORCYCLE }, + ]; + + constructor( + private globalErrorService: GlobalErrorService, + private technicalRecordService: TechnicalRecordService, + private batchTechRecordService: BatchTechnicalRecordService, + private route: ActivatedRoute, + private router: Router, + private store: Store + ) { + this.batchTechRecordService.clearBatch(); + this.technicalRecordService.clearSectionTemplateStates(); + } + + ngOnChanges(): void { + this.isVinUniqueCheckComplete = false; + } + + get isFormValid(): boolean { + const errors: GlobalError[] = []; + + DynamicFormService.validate(this.form, errors); + + this.globalErrorService.setErrors(errors); + + return this.form.valid; + } + + get vehicleStatusOptions(): MultiOptions { + return [ + { label: 'Provisional', value: StatusCodes.PROVISIONAL }, + { label: 'Current', value: StatusCodes.CURRENT }, + ]; + } + + get checkboxOptions(): MultiOptions { + return [{ value: true, label: 'Generate a C/T/Z number on submission of the new record' }]; + } + + toggleVrmInput(checked: CheckboxGroupComponent) { + const { vrmTrm } = this.form.controls; + + if (checked.value) { + vrmTrm.removeValidators(Validators.required); + vrmTrm.setValue(null); + vrmTrm.disable(); + } else { + vrmTrm.addValidators(Validators.required); + vrmTrm.setValue(''); + vrmTrm.enable(); + } + } + + navigateBack() { + this.globalErrorService.clearErrors(); + void this.router.navigate(['..'], { relativeTo: this.route }); + } + + async handleSubmit() { + if (!this.isFormValid) { + return; + } + + this.store.dispatch(setSpinnerState({ showSpinner: true })); + + const formValueUnique = await this.isFormValueUnique(); + + this.store.dispatch(setSpinnerState({ showSpinner: false })); + + if (!formValueUnique) { + this.isDuplicateVinAllowed = true; + return; + } + + this.technicalRecordService.updateEditingTechRecord(this.techRecord as TechRecordType<'put'>); + this.technicalRecordService.generateEditingVehicleTechnicalRecordFromVehicleType( + this.techRecord.techRecord_vehicleType as VehicleTypes + ); + this.technicalRecordService.clearSectionTemplateStates(); + await this.router.navigate(['../create/new-record-details'], { relativeTo: this.route }); + } + + async isFormValueUnique() { + const isTrailer = this.form.value.vehicleType === VehicleTypes.TRL; + + this.techRecord.techRecord_vehicleType = this.form.value.vehicleType; + this.techRecord.techRecord_statusCode = this.form.value.vehicleStatus; + + if (!this.isVinUniqueCheckComplete) { + this.vinUnique = await this.isVinUnique(); + } + + if (this.form.controls['generateID'].value) { + return this.vinUnique || this.isDuplicateVinAllowed; + } + + if (isTrailer) { + this.trlUnique = await this.isTrailerIdUnique(); + return (this.vinUnique || this.isDuplicateVinAllowed) && this.trlUnique; + } + this.vrmUnique = await this.isVrmUnique(); + return (this.vinUnique || this.isDuplicateVinAllowed) && this.vrmUnique; + } + + async isVinUnique(): Promise { + this.techRecord.vin = this.form.value.vin; + const isVinUnique = await firstValueFrom( + this.technicalRecordService.isUnique(this.techRecord.vin as string, SEARCH_TYPES.VIN) + ); + this.isVinUniqueCheckComplete = true; + return isVinUnique; + } + + async isVrmUnique() { + (this.techRecord as VehiclesOtherThan<'trl'>).primaryVrm = this.form.value.vrmTrm; + const isVrmUnique = await firstValueFrom( + this.technicalRecordService.isUnique( + (this.techRecord as VehiclesOtherThan<'trl'>).primaryVrm?.replace(/\s+/g, '') ?? '', + SEARCH_TYPES.VRM + ) + ); + if (!isVrmUnique) { + this.globalErrorService.addError({ error: 'Vrm not unique', anchorLink: 'input-vrm-or-trailer-id' }); + } + return isVrmUnique; + } + + async isTrailerIdUnique() { + if (this.techRecord.techRecord_vehicleType === 'trl') { + this.techRecord.trailerId = this.form.value.vrmTrm; + const isTrailerIdUnique = await firstValueFrom( + this.technicalRecordService.isUnique(this.techRecord.trailerId as string, SEARCH_TYPES.TRAILER_ID) + ); + if (!isTrailerIdUnique) { + this.globalErrorService.addError({ error: 'TrailerId not unique', anchorLink: 'input-vrm-or-trailer-id' }); + } + return isTrailerIdUnique; + } + return false; + } } diff --git a/src/app/features/tech-record/create/create-tech-records-routing.module.ts b/src/app/features/tech-record/create/create-tech-records-routing.module.ts index 5cfe959056..d5cff87835 100644 --- a/src/app/features/tech-record/create/create-tech-records-routing.module.ts +++ b/src/app/features/tech-record/create/create-tech-records-routing.module.ts @@ -3,46 +3,49 @@ import { RouterModule, Routes } from '@angular/router'; import { MsalGuard } from '@azure/msal-angular'; import { RoleGuard } from '@guards/role-guard/roles.guard'; import { Roles } from '@models/roles.enum'; -import { techRecordDataResolver } from 'src/app/resolvers/tech-record-data/tech-record-data.resolver'; import { TechRecordCreateRoutes } from '@models/routes.enum'; +import { techRecordDataResolver } from 'src/app/resolvers/tech-record-data/tech-record-data.resolver'; import { TechRecordSearchTyresComponent } from '../components/tech-record-search-tyres/tech-record-search-tyres.component'; import { HydrateNewVehicleRecordComponent } from './components/hydrate-new-vehicle-record/hydrate-new-vehicle-record.component'; import { CreateTechRecordComponent } from './create-tech-record.component'; const routes: Routes = [ - { - path: '', - resolve: { data: techRecordDataResolver }, - canActivate: [MsalGuard, RoleGuard], - children: [ - { - path: '', - component: CreateTechRecordComponent, - data: { roles: Roles.TechRecordCreate }, - }, - { - path: TechRecordCreateRoutes.NEW_RECORD_DETAILS, - children: [ - { - path: '', - component: HydrateNewVehicleRecordComponent, - data: { - title: 'New record details', roles: Roles.TechRecordCreate, isCustomLayout: true, isEditing: true, - }, - }, - { - path: TechRecordCreateRoutes.TYRE_SEARCH, - component: TechRecordSearchTyresComponent, - data: { title: 'Tyre search', roles: Roles.TechRecordCreate, isEditing: true }, - }, - ], - }, - ], - }, + { + path: '', + resolve: { data: techRecordDataResolver }, + canActivate: [MsalGuard, RoleGuard], + children: [ + { + path: '', + component: CreateTechRecordComponent, + data: { roles: Roles.TechRecordCreate }, + }, + { + path: TechRecordCreateRoutes.NEW_RECORD_DETAILS, + children: [ + { + path: '', + component: HydrateNewVehicleRecordComponent, + data: { + title: 'New record details', + roles: Roles.TechRecordCreate, + isCustomLayout: true, + isEditing: true, + }, + }, + { + path: TechRecordCreateRoutes.TYRE_SEARCH, + component: TechRecordSearchTyresComponent, + data: { title: 'Tyre search', roles: Roles.TechRecordCreate, isEditing: true }, + }, + ], + }, + ], + }, ]; @NgModule({ - imports: [RouterModule.forChild(routes)], - exports: [RouterModule], + imports: [RouterModule.forChild(routes)], + exports: [RouterModule], }) export class CreateTechRecordsRoutingModule {} diff --git a/src/app/features/tech-record/create/create-tech-records.module.ts b/src/app/features/tech-record/create/create-tech-records.module.ts index e6a68f4f90..af4b25a772 100644 --- a/src/app/features/tech-record/create/create-tech-records.module.ts +++ b/src/app/features/tech-record/create/create-tech-records.module.ts @@ -11,16 +11,16 @@ import { CreateTechRecordComponent } from './create-tech-record.component'; import { CreateTechRecordsRoutingModule } from './create-tech-records-routing.module'; @NgModule({ - declarations: [CreateTechRecordComponent, HydrateNewVehicleRecordComponent], - imports: [ - CommonModule, - CreateTechRecordsRoutingModule, - ReactiveFormsModule, - DynamicFormsModule, - RouterModule, - SharedModule, - TechRecordsModule, - SharedTechRecordsModule, - ], + declarations: [CreateTechRecordComponent, HydrateNewVehicleRecordComponent], + imports: [ + CommonModule, + CreateTechRecordsRoutingModule, + ReactiveFormsModule, + DynamicFormsModule, + RouterModule, + SharedModule, + TechRecordsModule, + SharedTechRecordsModule, + ], }) export class CreateTechRecordsModule {} diff --git a/src/app/features/tech-record/shared-tech-record.module.ts b/src/app/features/tech-record/shared-tech-record.module.ts index 37e1f3cd94..95cb64495b 100644 --- a/src/app/features/tech-record/shared-tech-record.module.ts +++ b/src/app/features/tech-record/shared-tech-record.module.ts @@ -6,8 +6,8 @@ import { SharedModule } from '@shared/shared.module'; import { TechRecordSummaryComponent } from './components/tech-record-summary/tech-record-summary.component'; @NgModule({ - declarations: [TechRecordSummaryComponent], - imports: [CommonModule, FormsModule, ReactiveFormsModule, DynamicFormsModule, SharedModule], - exports: [TechRecordSummaryComponent], + declarations: [TechRecordSummaryComponent], + imports: [CommonModule, FormsModule, ReactiveFormsModule, DynamicFormsModule, SharedModule], + exports: [TechRecordSummaryComponent], }) export class SharedTechRecordsModule {} diff --git a/src/app/features/tech-record/tech-record-routing.module.ts b/src/app/features/tech-record/tech-record-routing.module.ts index e56d706ea7..b5aa506c77 100644 --- a/src/app/features/tech-record/tech-record-routing.module.ts +++ b/src/app/features/tech-record/tech-record-routing.module.ts @@ -4,247 +4,230 @@ import { MsalGuard } from '@azure/msal-angular'; import { CancelEditTechGuard } from '@guards/cancel-edit-tech/cancel-edit-tech.guard'; import { RoleGuard } from '@guards/role-guard/roles.guard'; import { Roles } from '@models/roles.enum'; +import { TechRecordRoutes } from '@models/routes.enum'; import { ReasonForEditing } from '@models/vehicle-tech-record.model'; import { techRecordCleanResolver } from 'src/app/resolvers/tech-record-clean/tech-record-clean.resolver'; import { techRecordDataResolver } from 'src/app/resolvers/tech-record-data/tech-record-data.resolver'; import { techRecordValidateResolver } from 'src/app/resolvers/tech-record-validate/tech-record-validate.resolver'; import { techRecordViewResolver } from 'src/app/resolvers/tech-record-view/tech-record-view.resolver'; -import { TechRecordRoutes } from '@models/routes.enum'; -import { - AdrGenerateCertificateComponent, -} from './components/adr-generate-certificate/adr-generate-certificate.component'; -import { - TechRecordAmendReasonComponent, -} from './components/tech-record-amend-reason/tech-record-amend-reason.component'; +import { AdrGenerateCertificateComponent } from './components/adr-generate-certificate/adr-generate-certificate.component'; +import { TechRecordAmendReasonComponent } from './components/tech-record-amend-reason/tech-record-amend-reason.component'; import { AmendVinComponent } from './components/tech-record-amend-vin/tech-record-amend-vin.component'; -import { - AmendVrmReasonComponent, -} from './components/tech-record-amend-vrm-reason/tech-record-amend-vrm-reason.component'; +import { AmendVrmReasonComponent } from './components/tech-record-amend-vrm-reason/tech-record-amend-vrm-reason.component'; import { AmendVrmComponent } from './components/tech-record-amend-vrm/tech-record-amend-vrm.component'; -import { - TechRecordChangeStatusComponent, -} from './components/tech-record-change-status/tech-record-change-status.component'; +import { TechRecordChangeStatusComponent } from './components/tech-record-change-status/tech-record-change-status.component'; import { ChangeVehicleTypeComponent } from './components/tech-record-change-type/tech-record-change-type.component'; -import { - TechRecordChangeVisibilityComponent, -} from './components/tech-record-change-visibility/tech-record-change-visibility.component'; -import { - GenerateLetterComponent, -} from './components/tech-record-generate-letter/tech-record-generate-letter.component'; +import { TechRecordChangeVisibilityComponent } from './components/tech-record-change-visibility/tech-record-change-visibility.component'; +import { TechRecordEditAdditionalExaminerNoteComponent } from './components/tech-record-edit-additional-examiner-note/tech-record-edit-additional-examiner-note.component'; +import { GenerateLetterComponent } from './components/tech-record-generate-letter/tech-record-generate-letter.component'; import { GeneratePlateComponent } from './components/tech-record-generate-plate/tech-record-generate-plate.component'; -import { - TechRecordSearchTyresComponent, -} from './components/tech-record-search-tyres/tech-record-search-tyres.component'; -import { - TechRecordSummaryChangesComponent, -} from './components/tech-record-summary-changes/tech-record-summary-changes.component'; +import { TechRecordSearchTyresComponent } from './components/tech-record-search-tyres/tech-record-search-tyres.component'; +import { TechRecordSummaryChangesComponent } from './components/tech-record-summary-changes/tech-record-summary-changes.component'; import { TechRecordUnarchiveComponent } from './components/tech-record-unarchive/tech-record-unarchive-component'; import { TechRecordComponent } from './tech-record.component'; -import { - TechRecordEditAdditionalExaminerNoteComponent, -} from './components/tech-record-edit-additional-examiner-note/tech-record-edit-additional-examiner-note.component'; const routes: Routes = [ - { - path: '', - component: TechRecordComponent, - data: { roles: Roles.TechRecordView, isCustomLayout: true }, - canActivateChild: [MsalGuard, RoleGuard], - canActivate: [CancelEditTechGuard], - resolve: { - load: techRecordViewResolver, - data: techRecordDataResolver, - }, - }, - { - path: TechRecordRoutes.CORRECT_ERROR, - component: TechRecordComponent, - data: { - roles: Roles.TechRecordAmend, - isEditing: true, - reason: ReasonForEditing.CORRECTING_AN_ERROR, - isCustomLayout: true, - }, - canActivate: [MsalGuard, RoleGuard], - resolve: { - techRecord: techRecordViewResolver, - load: techRecordValidateResolver, - clean: techRecordCleanResolver, - }, - }, - { - path: TechRecordRoutes.NOTIFIABLE_ALTERATION_NEEDED, - component: TechRecordComponent, - data: { - roles: Roles.TechRecordAmend, - isEditing: true, - reason: ReasonForEditing.NOTIFIABLE_ALTERATION_NEEDED, - isCustomLayout: true, - }, - canActivate: [MsalGuard, RoleGuard], - resolve: { - techRecord: techRecordViewResolver, - load: techRecordValidateResolver, - clean: techRecordCleanResolver, - }, - }, + { + path: '', + component: TechRecordComponent, + data: { roles: Roles.TechRecordView, isCustomLayout: true }, + canActivateChild: [MsalGuard, RoleGuard], + canActivate: [CancelEditTechGuard], + resolve: { + load: techRecordViewResolver, + data: techRecordDataResolver, + }, + }, + { + path: TechRecordRoutes.CORRECT_ERROR, + component: TechRecordComponent, + data: { + roles: Roles.TechRecordAmend, + isEditing: true, + reason: ReasonForEditing.CORRECTING_AN_ERROR, + isCustomLayout: true, + }, + canActivate: [MsalGuard, RoleGuard], + resolve: { + techRecord: techRecordViewResolver, + load: techRecordValidateResolver, + clean: techRecordCleanResolver, + }, + }, + { + path: TechRecordRoutes.NOTIFIABLE_ALTERATION_NEEDED, + component: TechRecordComponent, + data: { + roles: Roles.TechRecordAmend, + isEditing: true, + reason: ReasonForEditing.NOTIFIABLE_ALTERATION_NEEDED, + isCustomLayout: true, + }, + canActivate: [MsalGuard, RoleGuard], + resolve: { + techRecord: techRecordViewResolver, + load: techRecordValidateResolver, + clean: techRecordCleanResolver, + }, + }, - { - path: TechRecordRoutes.CHANGE_VIN, - component: AmendVinComponent, - data: { title: 'Change VIN', roles: Roles.TechRecordAmend }, - canActivate: [MsalGuard, RoleGuard], - }, - { - path: TechRecordRoutes.CHANGE_VRM, - component: AmendVrmReasonComponent, - data: { title: 'Change VRM', roles: Roles.TechRecordAmend, isEditing: true }, - canActivate: [MsalGuard, RoleGuard], - }, - { - path: TechRecordRoutes.REASON_TO_CHANGE_VRM, - component: AmendVrmComponent, - data: { title: 'Change VRM', roles: Roles.TechRecordAmend, isEditing: true }, - canActivate: [MsalGuard, RoleGuard], - }, - { - path: TechRecordRoutes.GENERATE_PLATE, - component: GeneratePlateComponent, - data: { title: 'Generate plate', roles: Roles.TechRecordAmend }, - canActivate: [MsalGuard, RoleGuard], - resolve: { load: techRecordViewResolver }, - }, - { - path: TechRecordRoutes.GENERATE_LETTER, - component: GenerateLetterComponent, - data: { title: 'Generate letter', roles: Roles.TechRecordAmend }, - canActivate: [MsalGuard, RoleGuard], - resolve: { load: techRecordViewResolver }, - }, - { - path: TechRecordRoutes.AMEND_REASON, - component: TechRecordAmendReasonComponent, - data: { roles: Roles.TechRecordAmend }, - canActivate: [MsalGuard, RoleGuard], - }, - { - path: TechRecordRoutes.CHANGE_STATUS, - component: TechRecordChangeStatusComponent, - data: { title: 'Promote or Archive Tech Record', roles: Roles.TechRecordArchive }, - canActivate: [MsalGuard, RoleGuard], - resolve: { load: techRecordViewResolver }, - }, - { - path: TechRecordRoutes.UNARCHIVE_RECORD, - component: TechRecordUnarchiveComponent, - data: { title: 'Unarchive Record', roles: Roles.TechRecordUnarchive }, - canActivate: [MsalGuard, RoleGuard], - resolve: { load: techRecordViewResolver }, - }, - { - path: TechRecordRoutes.CHANGE_VEHICLE_TYPE, - component: ChangeVehicleTypeComponent, - data: { title: 'Change vehicle type', roles: Roles.TechRecordAmend, isEditing: true }, - canActivate: [MsalGuard, RoleGuard], - resolve: { techRecord: techRecordViewResolver }, - }, - { - path: TechRecordRoutes.CHANGE_VTA_VISIBILITY, - component: TechRecordChangeVisibilityComponent, - data: { roles: Roles.TechRecordAmend }, - canActivate: [MsalGuard, RoleGuard], - resolve: { techRecord: techRecordViewResolver }, - }, - { - path: TechRecordRoutes.CORRECT_ERROR_TYRE_SEARCH, - component: TechRecordSearchTyresComponent, - data: { - title: 'Tyre search', - roles: Roles.TechRecordAmend, - isEditing: true, - reason: ReasonForEditing.CORRECTING_AN_ERROR, - }, - canActivate: [MsalGuard, RoleGuard], - resolve: { techRecord: techRecordViewResolver }, - }, - { - path: TechRecordRoutes.CORRECT_ERROR_CHANGE_SUMMARY, - component: TechRecordSummaryChangesComponent, - data: { - roles: Roles.TechRecordAmend, - isEditing: true, - }, - canActivate: [MsalGuard, RoleGuard], - }, - { - path: TechRecordRoutes.CORRECT_ERROR_EDIT_ADDITIONAL_EXAMINER_NOTE, - component: TechRecordEditAdditionalExaminerNoteComponent, - data: { - title: 'Edit Additional Examiner Note', - roles: Roles.TechRecordAmend, - isEditing: true, - reason: ReasonForEditing.CORRECTING_AN_ERROR, - }, - canActivate: [MsalGuard, RoleGuard], - resolve: { techRecord: techRecordViewResolver }, - }, - { - path: TechRecordRoutes.NOTIFIABLE_ALTERATION_NEEDED_CHANGE_SUMMARY, - component: TechRecordSummaryChangesComponent, - data: { - roles: Roles.TechRecordAmend, - isEditing: true, - }, - canActivate: [MsalGuard, RoleGuard], - }, - { - path: TechRecordRoutes.NOTIFIABLE_ALTERATION_NEEDED_TYRE_SEARCH, - component: TechRecordSearchTyresComponent, - data: { - title: 'Tyre search', - roles: Roles.TechRecordAmend, - isEditing: true, - reason: ReasonForEditing.NOTIFIABLE_ALTERATION_NEEDED, - }, - canActivate: [MsalGuard, RoleGuard], - resolve: { techRecord: techRecordViewResolver }, - }, - { - path: TechRecordRoutes.NOTIFIABLE_ALTERNATION_NEEDED_EDIT_ADDITIONAL_EXAMINER_NOTE, - component: TechRecordEditAdditionalExaminerNoteComponent, - data: { - title: 'Edit Additional Examiner Note', - roles: Roles.TechRecordAmend, - isEditing: true, - reason: ReasonForEditing.NOTIFIABLE_ALTERATION_NEEDED, - }, - canActivate: [MsalGuard, RoleGuard], - resolve: { techRecord: techRecordViewResolver }, - }, - { - path: TechRecordRoutes.TEST_RECORDS, - data: { title: 'Test record', roles: Roles.TestResultView }, - canActivate: [MsalGuard, RoleGuard], - resolve: { techRecord: techRecordViewResolver }, - loadChildren: () => import('../test-records/amend/amend-test-records.module').then((m) => m.AmendTestRecordsModule), - }, - { - path: TechRecordRoutes.CREATE_TEST, - data: { title: 'Create Contingency test', roles: Roles.TestResultCreateContingency }, - canActivate: [MsalGuard, RoleGuard], - resolve: { techRecord: techRecordViewResolver }, - loadChildren: () => import('../test-records/create/create-test-records.module').then((m) => m.CreateTestRecordsModule), - }, - { - path: TechRecordRoutes.ADR_CERTIFICATE, - component: AdrGenerateCertificateComponent, - data: { title: 'Generate ADR Certificate', roles: Roles.TestResultCreateDeskAssessment }, - canActivate: [MsalGuard, RoleGuard], - }, + { + path: TechRecordRoutes.CHANGE_VIN, + component: AmendVinComponent, + data: { title: 'Change VIN', roles: Roles.TechRecordAmend }, + canActivate: [MsalGuard, RoleGuard], + }, + { + path: TechRecordRoutes.CHANGE_VRM, + component: AmendVrmReasonComponent, + data: { title: 'Change VRM', roles: Roles.TechRecordAmend, isEditing: true }, + canActivate: [MsalGuard, RoleGuard], + }, + { + path: TechRecordRoutes.REASON_TO_CHANGE_VRM, + component: AmendVrmComponent, + data: { title: 'Change VRM', roles: Roles.TechRecordAmend, isEditing: true }, + canActivate: [MsalGuard, RoleGuard], + }, + { + path: TechRecordRoutes.GENERATE_PLATE, + component: GeneratePlateComponent, + data: { title: 'Generate plate', roles: Roles.TechRecordAmend }, + canActivate: [MsalGuard, RoleGuard], + resolve: { load: techRecordViewResolver }, + }, + { + path: TechRecordRoutes.GENERATE_LETTER, + component: GenerateLetterComponent, + data: { title: 'Generate letter', roles: Roles.TechRecordAmend }, + canActivate: [MsalGuard, RoleGuard], + resolve: { load: techRecordViewResolver }, + }, + { + path: TechRecordRoutes.AMEND_REASON, + component: TechRecordAmendReasonComponent, + data: { roles: Roles.TechRecordAmend }, + canActivate: [MsalGuard, RoleGuard], + }, + { + path: TechRecordRoutes.CHANGE_STATUS, + component: TechRecordChangeStatusComponent, + data: { title: 'Promote or Archive Tech Record', roles: Roles.TechRecordArchive }, + canActivate: [MsalGuard, RoleGuard], + resolve: { load: techRecordViewResolver }, + }, + { + path: TechRecordRoutes.UNARCHIVE_RECORD, + component: TechRecordUnarchiveComponent, + data: { title: 'Unarchive Record', roles: Roles.TechRecordUnarchive }, + canActivate: [MsalGuard, RoleGuard], + resolve: { load: techRecordViewResolver }, + }, + { + path: TechRecordRoutes.CHANGE_VEHICLE_TYPE, + component: ChangeVehicleTypeComponent, + data: { title: 'Change vehicle type', roles: Roles.TechRecordAmend, isEditing: true }, + canActivate: [MsalGuard, RoleGuard], + resolve: { techRecord: techRecordViewResolver }, + }, + { + path: TechRecordRoutes.CHANGE_VTA_VISIBILITY, + component: TechRecordChangeVisibilityComponent, + data: { roles: Roles.TechRecordAmend }, + canActivate: [MsalGuard, RoleGuard], + resolve: { techRecord: techRecordViewResolver }, + }, + { + path: TechRecordRoutes.CORRECT_ERROR_TYRE_SEARCH, + component: TechRecordSearchTyresComponent, + data: { + title: 'Tyre search', + roles: Roles.TechRecordAmend, + isEditing: true, + reason: ReasonForEditing.CORRECTING_AN_ERROR, + }, + canActivate: [MsalGuard, RoleGuard], + resolve: { techRecord: techRecordViewResolver }, + }, + { + path: TechRecordRoutes.CORRECT_ERROR_CHANGE_SUMMARY, + component: TechRecordSummaryChangesComponent, + data: { + roles: Roles.TechRecordAmend, + isEditing: true, + }, + canActivate: [MsalGuard, RoleGuard], + }, + { + path: TechRecordRoutes.CORRECT_ERROR_EDIT_ADDITIONAL_EXAMINER_NOTE, + component: TechRecordEditAdditionalExaminerNoteComponent, + data: { + title: 'Edit Additional Examiner Note', + roles: Roles.TechRecordAmend, + isEditing: true, + reason: ReasonForEditing.CORRECTING_AN_ERROR, + }, + canActivate: [MsalGuard, RoleGuard], + resolve: { techRecord: techRecordViewResolver }, + }, + { + path: TechRecordRoutes.NOTIFIABLE_ALTERATION_NEEDED_CHANGE_SUMMARY, + component: TechRecordSummaryChangesComponent, + data: { + roles: Roles.TechRecordAmend, + isEditing: true, + }, + canActivate: [MsalGuard, RoleGuard], + }, + { + path: TechRecordRoutes.NOTIFIABLE_ALTERATION_NEEDED_TYRE_SEARCH, + component: TechRecordSearchTyresComponent, + data: { + title: 'Tyre search', + roles: Roles.TechRecordAmend, + isEditing: true, + reason: ReasonForEditing.NOTIFIABLE_ALTERATION_NEEDED, + }, + canActivate: [MsalGuard, RoleGuard], + resolve: { techRecord: techRecordViewResolver }, + }, + { + path: TechRecordRoutes.NOTIFIABLE_ALTERNATION_NEEDED_EDIT_ADDITIONAL_EXAMINER_NOTE, + component: TechRecordEditAdditionalExaminerNoteComponent, + data: { + title: 'Edit Additional Examiner Note', + roles: Roles.TechRecordAmend, + isEditing: true, + reason: ReasonForEditing.NOTIFIABLE_ALTERATION_NEEDED, + }, + canActivate: [MsalGuard, RoleGuard], + resolve: { techRecord: techRecordViewResolver }, + }, + { + path: TechRecordRoutes.TEST_RECORDS, + data: { title: 'Test record', roles: Roles.TestResultView }, + canActivate: [MsalGuard, RoleGuard], + resolve: { techRecord: techRecordViewResolver }, + loadChildren: () => import('../test-records/amend/amend-test-records.module').then((m) => m.AmendTestRecordsModule), + }, + { + path: TechRecordRoutes.CREATE_TEST, + data: { title: 'Create Contingency test', roles: Roles.TestResultCreateContingency }, + canActivate: [MsalGuard, RoleGuard], + resolve: { techRecord: techRecordViewResolver }, + loadChildren: () => + import('../test-records/create/create-test-records.module').then((m) => m.CreateTestRecordsModule), + }, + { + path: TechRecordRoutes.ADR_CERTIFICATE, + component: AdrGenerateCertificateComponent, + data: { title: 'Generate ADR Certificate', roles: Roles.TestResultCreateDeskAssessment }, + canActivate: [MsalGuard, RoleGuard], + }, ]; @NgModule({ - imports: [RouterModule.forChild(routes)], - exports: [RouterModule], + imports: [RouterModule.forChild(routes)], + exports: [RouterModule], }) -export class TechRecordsRoutingModule { } +export class TechRecordsRoutingModule {} diff --git a/src/app/features/tech-record/tech-record.component.spec.ts b/src/app/features/tech-record/tech-record.component.spec.ts index d2b443c28a..ed7b0aa8bf 100644 --- a/src/app/features/tech-record/tech-record.component.spec.ts +++ b/src/app/features/tech-record/tech-record.component.spec.ts @@ -1,59 +1,59 @@ +import { HttpClientTestingModule } from '@angular/common/http/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { MockStore, provideMockStore } from '@ngrx/store/testing'; -import { initialAppState, State } from '@store/.'; +import { ActivatedRouteSnapshot } from '@angular/router'; import { RouterTestingModule } from '@angular/router/testing'; -import { HttpClientTestingModule } from '@angular/common/http/testing'; -import { selectRouteNestedParams } from '@store/router/selectors/router.selectors'; -import { Roles } from '@models/roles.enum'; import { GlobalError } from '@core/components/global-error/global-error.interface'; -import { ActivatedRouteSnapshot } from '@angular/router'; +import { Roles } from '@models/roles.enum'; +import { MockStore, provideMockStore } from '@ngrx/store/testing'; +import { State, initialAppState } from '@store/.'; +import { selectRouteNestedParams } from '@store/router/selectors/router.selectors'; import { TechRecordComponent } from './tech-record.component'; describe('TechRecordComponent', () => { - let component: TechRecordComponent; - let fixture: ComponentFixture; - let store: MockStore; + let component: TechRecordComponent; + let fixture: ComponentFixture; + let store: MockStore; - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [RouterTestingModule, HttpClientTestingModule], - declarations: [TechRecordComponent], - providers: [provideMockStore({ initialState: initialAppState })], - }).compileComponents(); - }); + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [RouterTestingModule, HttpClientTestingModule], + declarations: [TechRecordComponent], + providers: [provideMockStore({ initialState: initialAppState })], + }).compileComponents(); + }); - beforeEach(() => { - store = TestBed.inject(MockStore); - store.overrideSelector(selectRouteNestedParams, { vin: '123456' }); + beforeEach(() => { + store = TestBed.inject(MockStore); + store.overrideSelector(selectRouteNestedParams, { vin: '123456' }); - fixture = TestBed.createComponent(TechRecordComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); + fixture = TestBed.createComponent(TechRecordComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); - it('should return roles', () => { - const { roles } = component; + it('should return roles', () => { + const { roles } = component; - expect(roles).toBe(Roles); - }); + expect(roles).toBe(Roles); + }); - it('should return error', () => { - const expectedError: GlobalError = { error: 'some error', anchorLink: 'expected' }; + it('should return error', () => { + const expectedError: GlobalError = { error: 'some error', anchorLink: 'expected' }; - const expectedResult = component.getErrorByName([expectedError], expectedError.anchorLink ?? ''); + const expectedResult = component.getErrorByName([expectedError], expectedError.anchorLink ?? ''); - expect(expectedResult).toBe(expectedError); - }); + expect(expectedResult).toBe(expectedError); + }); - it('reuse strategy should be set to false', () => { - const snapshot = {} as ActivatedRouteSnapshot; + it('reuse strategy should be set to false', () => { + const snapshot = {} as ActivatedRouteSnapshot; - const expectedResult = (component['router']).routeReuseStrategy.shouldReuseRoute(snapshot, snapshot); + const expectedResult = component['router'].routeReuseStrategy.shouldReuseRoute(snapshot, snapshot); - expect(expectedResult).toBeFalsy(); - }); + expect(expectedResult).toBeFalsy(); + }); }); diff --git a/src/app/features/tech-record/tech-record.component.ts b/src/app/features/tech-record/tech-record.component.ts index f87a0bf1cc..683a3717e5 100644 --- a/src/app/features/tech-record/tech-record.component.ts +++ b/src/app/features/tech-record/tech-record.component.ts @@ -7,38 +7,38 @@ import { TechnicalRecordService } from '@services/technical-record/technical-rec import { take } from 'rxjs'; @Component({ - selector: 'app-tech-record', - templateUrl: './tech-record.component.html', + selector: 'app-tech-record', + templateUrl: './tech-record.component.html', }) export class TechRecordComponent implements OnInit { - systemNumber?: string; - createdTimestamp?: string; + systemNumber?: string; + createdTimestamp?: string; - constructor( - private techRecordService: TechnicalRecordService, - private router: Router, - public errorService: GlobalErrorService, - private route: ActivatedRoute, - ) { - this.router.routeReuseStrategy.shouldReuseRoute = () => false; - } + constructor( + private techRecordService: TechnicalRecordService, + private router: Router, + public errorService: GlobalErrorService, + private route: ActivatedRoute + ) { + this.router.routeReuseStrategy.shouldReuseRoute = () => false; + } - ngOnInit(): void { - this.route.params.pipe(take(1)).subscribe((params) => { - this.systemNumber = params['systemNumber']; - this.createdTimestamp = params['createdTimestamp']; - }); - } + ngOnInit(): void { + this.route.params.pipe(take(1)).subscribe((params) => { + this.systemNumber = params['systemNumber']; + this.createdTimestamp = params['createdTimestamp']; + }); + } - get techRecord$() { - return this.techRecordService.techRecord$; - } + get techRecord$() { + return this.techRecordService.techRecord$; + } - get roles() { - return Roles; - } + get roles() { + return Roles; + } - getErrorByName(errors: GlobalError[], name: string): GlobalError | undefined { - return errors.find((error) => error.anchorLink === name); - } + getErrorByName(errors: GlobalError[], name: string): GlobalError | undefined { + return errors.find((error) => error.anchorLink === name); + } } diff --git a/src/app/features/tech-record/tech-record.module.ts b/src/app/features/tech-record/tech-record.module.ts index 34c34563bf..74fa05f3c1 100644 --- a/src/app/features/tech-record/tech-record.module.ts +++ b/src/app/features/tech-record/tech-record.module.ts @@ -3,57 +3,62 @@ import { NgModule } from '@angular/core'; import { ReactiveFormsModule } from '@angular/forms'; import { DynamicFormsModule } from '@forms/dynamic-forms.module'; import { SharedModule } from '@shared/shared.module'; +import { AdrGenerateCertificateComponent } from './components/adr-generate-certificate/adr-generate-certificate.component'; import { EditTechRecordButtonComponent } from './components/edit-tech-record-button/edit-tech-record-button.component'; import { TechRecordAmendReasonComponent } from './components/tech-record-amend-reason/tech-record-amend-reason.component'; import { AmendVinComponent } from './components/tech-record-amend-vin/tech-record-amend-vin.component'; +import { AmendVrmReasonComponent } from './components/tech-record-amend-vrm-reason/tech-record-amend-vrm-reason.component'; import { AmendVrmComponent } from './components/tech-record-amend-vrm/tech-record-amend-vrm.component'; import { TechRecordChangeStatusComponent } from './components/tech-record-change-status/tech-record-change-status.component'; -import { TechRecordUnarchiveComponent } from './components/tech-record-unarchive/tech-record-unarchive-component'; import { ChangeVehicleTypeComponent } from './components/tech-record-change-type/tech-record-change-type.component'; import { TechRecordChangeVisibilityComponent } from './components/tech-record-change-visibility/tech-record-change-visibility.component'; +import { TechRecordEditAdditionalExaminerNoteComponent } from './components/tech-record-edit-additional-examiner-note/tech-record-edit-additional-examiner-note.component'; import { GenerateLetterComponent } from './components/tech-record-generate-letter/tech-record-generate-letter.component'; import { GeneratePlateComponent } from './components/tech-record-generate-plate/tech-record-generate-plate.component'; import { TechRecordHistoryComponent } from './components/tech-record-history/tech-record-history.component'; import { TechRecordSearchTyresComponent } from './components/tech-record-search-tyres/tech-record-search-tyres.component'; +import { TechRecordSummaryChangesComponent } from './components/tech-record-summary-changes/tech-record-summary-changes.component'; import { TechRecordTitleComponent } from './components/tech-record-title/tech-record-title.component'; +import { TechRecordUnarchiveComponent } from './components/tech-record-unarchive/tech-record-unarchive-component'; import { TechRouterOutletComponent } from './components/tech-router-outlet/tech-router-outlet.component'; import { TestRecordSummaryComponent } from './components/test-record-summary/test-record-summary.component'; import { VehicleTechnicalRecordComponent } from './components/vehicle-technical-record/vehicle-technical-record.component'; import { SharedTechRecordsModule } from './shared-tech-record.module'; import { TechRecordsRoutingModule } from './tech-record-routing.module'; import { TechRecordComponent } from './tech-record.component'; -import { AmendVrmReasonComponent } from './components/tech-record-amend-vrm-reason/tech-record-amend-vrm-reason.component'; -import { TechRecordSummaryChangesComponent } from './components/tech-record-summary-changes/tech-record-summary-changes.component'; -import { AdrGenerateCertificateComponent } from './components/adr-generate-certificate/adr-generate-certificate.component'; -import { - TechRecordEditAdditionalExaminerNoteComponent, -} from './components/tech-record-edit-additional-examiner-note/tech-record-edit-additional-examiner-note.component'; @NgModule({ - declarations: [ - AmendVinComponent, - AmendVrmComponent, - AmendVrmReasonComponent, - ChangeVehicleTypeComponent, - EditTechRecordButtonComponent, - TechRecordAmendReasonComponent, - TechRecordComponent, - TechRecordChangeStatusComponent, - TechRecordUnarchiveComponent, - TechRecordChangeVisibilityComponent, - GeneratePlateComponent, - GenerateLetterComponent, - TechRecordHistoryComponent, - TechRecordSearchTyresComponent, - TechRecordTitleComponent, - TechRecordEditAdditionalExaminerNoteComponent, - TechRouterOutletComponent, - TestRecordSummaryComponent, - VehicleTechnicalRecordComponent, - TechRecordSummaryChangesComponent, - AdrGenerateCertificateComponent, - ], - imports: [CommonModule, DynamicFormsModule, ReactiveFormsModule, SharedModule, TechRecordsRoutingModule, SharedTechRecordsModule], - exports: [EditTechRecordButtonComponent, TechRecordTitleComponent], + declarations: [ + AmendVinComponent, + AmendVrmComponent, + AmendVrmReasonComponent, + ChangeVehicleTypeComponent, + EditTechRecordButtonComponent, + TechRecordAmendReasonComponent, + TechRecordComponent, + TechRecordChangeStatusComponent, + TechRecordUnarchiveComponent, + TechRecordChangeVisibilityComponent, + GeneratePlateComponent, + GenerateLetterComponent, + TechRecordHistoryComponent, + TechRecordSearchTyresComponent, + TechRecordTitleComponent, + TechRecordEditAdditionalExaminerNoteComponent, + TechRouterOutletComponent, + TestRecordSummaryComponent, + VehicleTechnicalRecordComponent, + TechRecordSummaryChangesComponent, + AdrGenerateCertificateComponent, + ], + imports: [ + CommonModule, + DynamicFormsModule, + ReactiveFormsModule, + SharedModule, + TechRecordsRoutingModule, + SharedTechRecordsModule, + ], + exports: [EditTechRecordButtonComponent, TechRecordTitleComponent], }) export class TechRecordsModule {} diff --git a/src/app/features/test-records/amend/amend-test-records-routing.module.ts b/src/app/features/test-records/amend/amend-test-records-routing.module.ts index b1d61e86b9..121155f7a1 100644 --- a/src/app/features/test-records/amend/amend-test-records-routing.module.ts +++ b/src/app/features/test-records/amend/amend-test-records-routing.module.ts @@ -21,136 +21,140 @@ import { TestRouterOutletComponent } from './views/test-router-outlet/test-route import { TestTypeSelectWrapperComponent } from './views/test-type-select-wrapper/test-type-select-wrapper.component'; const routes: Routes = [ - { - path: '', - component: TestRouterOutletComponent, - resolve: { load: testResultResolver, testTypeTaxonomy: testTypeTaxonomyResolver }, - children: [ - { - path: '', - component: TestResultSummaryComponent, - }, - { - path: TestRecordAmendRoutes.AMEND_TEST, - component: TestRouterOutletComponent, - data: { title: 'Amend test record', roles: Roles.TestResultAmend }, - canActivate: [RoleGuard], - children: [ - { - path: '', - component: TestAmendReasonComponent, - }, - { - path: TestRecordAmendRoutes.INCORRECT_TEST_TYPE, - component: TestRouterOutletComponent, - data: { title: 'Select a test type', roles: Roles.TestResultAmend }, - resolve: { testTypeTaxonomy: testTypeTaxonomyResolver }, - canActivate: [RoleGuard], - children: [ - { - path: '', - component: TestTypeSelectWrapperComponent, - }, - ], - }, - { - path: TestRecordAmendRoutes.TEST_DETAILS, - component: TestRouterOutletComponent, - data: { title: 'Test details', roles: Roles.TestResultAmend }, - resolve: { load: testResultResolver, testTypeTaxonomy: testTypeTaxonomyResolver, defectTaxonomy: defectsTaxonomyResolver }, - canActivate: [RoleGuard], - canDeactivate: [CancelEditTestGuard], - children: [ - { - path: '', - component: TestRecordComponent, - }, - { - path: TestRecordAmendRoutes.DEFECT, - component: DefectComponent, - data: { title: 'Defect', roles: Roles.TestResultAmend, isEditing: true }, - canActivate: [RoleGuard], - }, - { - path: TestRecordAmendRoutes.SELECT_DEFECT, - component: TestRouterOutletComponent, - data: { title: 'Select defect', roles: Roles.TestResultAmend }, - canActivate: [RoleGuard], - children: [ - { - path: '', - component: DefectSelectComponent, - canActivate: [RoleGuard], - }, - { - path: TestRecordAmendRoutes.SELECT_DEFECT_REFERENCE, - component: DefectComponent, - data: { title: 'Defect', roles: Roles.TestResultAmend, isEditing: true }, - canActivate: [RoleGuard], - }, - ], - }, - { - path: TestRecordAmendRoutes.REQUIRED_STANDARD, - component: RequiredStandardComponent, - data: { title: 'Required Standard', roles: Roles.TestResultAmend, isEditing: true }, - canActivate: [RoleGuard], - }, - { - path: TestRecordAmendRoutes.SELECT_REQUIRED_STANDARD, - component: TestRouterOutletComponent, - resolve: { RequiredStandards: requiredStandardsResolver }, - data: { title: 'Select Required Standard', roles: Roles.TestResultAmend }, - children: [ - { - path: '', - component: RequiredStandardSelectComponent, - canActivate: [RoleGuard], - }, - { - path: TestRecordAmendRoutes.REQUIRED_STANDARD_REF, - component: RequiredStandardComponent, - data: { title: 'Required Standard', roles: Roles.TestResultAmend, isEditing: true }, - canActivate: [RoleGuard], - }, - ], - }, - ], - }, - ], - }, - { - path: TestRecordAmendRoutes.AMENDED_TEST, - component: AmendedTestRecordComponent, - data: { title: 'Amended test result', roles: Roles.TestResultView }, - canActivate: [RoleGuard], - }, - { - path: TestRecordAmendRoutes.CANCEL_TEST, - component: ConfirmCancellationComponent, - data: { title: 'Cancel test result', roles: Roles.TestResultAmend }, - canActivate: [RoleGuard], - }, - { - path: TestRecordAmendRoutes.DEFECT, - component: DefectComponent, - data: { title: 'Defect', roles: Roles.TestResultView, isEditing: false }, - resolve: { load: testResultResolver }, - canActivate: [RoleGuard], - }, - { - path: TestRecordAmendRoutes.REQUIRED_STANDARD, - component: RequiredStandardComponent, - data: { title: 'Required Standard', roles: Roles.TestResultView, isEditing: false }, - resolve: { load: testResultResolver }, - canActivate: [RoleGuard], - }, - ], - }, + { + path: '', + component: TestRouterOutletComponent, + resolve: { load: testResultResolver, testTypeTaxonomy: testTypeTaxonomyResolver }, + children: [ + { + path: '', + component: TestResultSummaryComponent, + }, + { + path: TestRecordAmendRoutes.AMEND_TEST, + component: TestRouterOutletComponent, + data: { title: 'Amend test record', roles: Roles.TestResultAmend }, + canActivate: [RoleGuard], + children: [ + { + path: '', + component: TestAmendReasonComponent, + }, + { + path: TestRecordAmendRoutes.INCORRECT_TEST_TYPE, + component: TestRouterOutletComponent, + data: { title: 'Select a test type', roles: Roles.TestResultAmend }, + resolve: { testTypeTaxonomy: testTypeTaxonomyResolver }, + canActivate: [RoleGuard], + children: [ + { + path: '', + component: TestTypeSelectWrapperComponent, + }, + ], + }, + { + path: TestRecordAmendRoutes.TEST_DETAILS, + component: TestRouterOutletComponent, + data: { title: 'Test details', roles: Roles.TestResultAmend }, + resolve: { + load: testResultResolver, + testTypeTaxonomy: testTypeTaxonomyResolver, + defectTaxonomy: defectsTaxonomyResolver, + }, + canActivate: [RoleGuard], + canDeactivate: [CancelEditTestGuard], + children: [ + { + path: '', + component: TestRecordComponent, + }, + { + path: TestRecordAmendRoutes.DEFECT, + component: DefectComponent, + data: { title: 'Defect', roles: Roles.TestResultAmend, isEditing: true }, + canActivate: [RoleGuard], + }, + { + path: TestRecordAmendRoutes.SELECT_DEFECT, + component: TestRouterOutletComponent, + data: { title: 'Select defect', roles: Roles.TestResultAmend }, + canActivate: [RoleGuard], + children: [ + { + path: '', + component: DefectSelectComponent, + canActivate: [RoleGuard], + }, + { + path: TestRecordAmendRoutes.SELECT_DEFECT_REFERENCE, + component: DefectComponent, + data: { title: 'Defect', roles: Roles.TestResultAmend, isEditing: true }, + canActivate: [RoleGuard], + }, + ], + }, + { + path: TestRecordAmendRoutes.REQUIRED_STANDARD, + component: RequiredStandardComponent, + data: { title: 'Required Standard', roles: Roles.TestResultAmend, isEditing: true }, + canActivate: [RoleGuard], + }, + { + path: TestRecordAmendRoutes.SELECT_REQUIRED_STANDARD, + component: TestRouterOutletComponent, + resolve: { RequiredStandards: requiredStandardsResolver }, + data: { title: 'Select Required Standard', roles: Roles.TestResultAmend }, + children: [ + { + path: '', + component: RequiredStandardSelectComponent, + canActivate: [RoleGuard], + }, + { + path: TestRecordAmendRoutes.REQUIRED_STANDARD_REF, + component: RequiredStandardComponent, + data: { title: 'Required Standard', roles: Roles.TestResultAmend, isEditing: true }, + canActivate: [RoleGuard], + }, + ], + }, + ], + }, + ], + }, + { + path: TestRecordAmendRoutes.AMENDED_TEST, + component: AmendedTestRecordComponent, + data: { title: 'Amended test result', roles: Roles.TestResultView }, + canActivate: [RoleGuard], + }, + { + path: TestRecordAmendRoutes.CANCEL_TEST, + component: ConfirmCancellationComponent, + data: { title: 'Cancel test result', roles: Roles.TestResultAmend }, + canActivate: [RoleGuard], + }, + { + path: TestRecordAmendRoutes.DEFECT, + component: DefectComponent, + data: { title: 'Defect', roles: Roles.TestResultView, isEditing: false }, + resolve: { load: testResultResolver }, + canActivate: [RoleGuard], + }, + { + path: TestRecordAmendRoutes.REQUIRED_STANDARD, + component: RequiredStandardComponent, + data: { title: 'Required Standard', roles: Roles.TestResultView, isEditing: false }, + resolve: { load: testResultResolver }, + canActivate: [RoleGuard], + }, + ], + }, ]; @NgModule({ - imports: [RouterModule.forChild(routes)], - exports: [RouterModule], + imports: [RouterModule.forChild(routes)], + exports: [RouterModule], }) export class AmendTestRecordsRoutingModule {} diff --git a/src/app/features/test-records/amend/amend-test-records.module.ts b/src/app/features/test-records/amend/amend-test-records.module.ts index 164f22bc8d..309c79a697 100644 --- a/src/app/features/test-records/amend/amend-test-records.module.ts +++ b/src/app/features/test-records/amend/amend-test-records.module.ts @@ -16,17 +16,25 @@ import { TestRouterOutletComponent } from './views/test-router-outlet/test-route import { TestTypeSelectWrapperComponent } from './views/test-type-select-wrapper/test-type-select-wrapper.component'; @NgModule({ - declarations: [ - AmendTestComponent, - AmendedTestRecordComponent, - ConfirmCancellationComponent, - TestAmendmentHistoryComponent, - TestAmendReasonComponent, - TestRecordComponent, - TestResultSummaryComponent, - TestRouterOutletComponent, - TestTypeSelectWrapperComponent, - ], - imports: [AmendTestRecordsRoutingModule, CommonModule, DynamicFormsModule, FormsModule, ReactiveFormsModule, SharedModule, TestRecordsModule], + declarations: [ + AmendTestComponent, + AmendedTestRecordComponent, + ConfirmCancellationComponent, + TestAmendmentHistoryComponent, + TestAmendReasonComponent, + TestRecordComponent, + TestResultSummaryComponent, + TestRouterOutletComponent, + TestTypeSelectWrapperComponent, + ], + imports: [ + AmendTestRecordsRoutingModule, + CommonModule, + DynamicFormsModule, + FormsModule, + ReactiveFormsModule, + SharedModule, + TestRecordsModule, + ], }) export class AmendTestRecordsModule {} diff --git a/src/app/features/test-records/amend/components/test-amendment-history/test-amendment-history.component.spec.ts b/src/app/features/test-records/amend/components/test-amendment-history/test-amendment-history.component.spec.ts index 307a6dd127..860d107029 100644 --- a/src/app/features/test-records/amend/components/test-amendment-history/test-amendment-history.component.spec.ts +++ b/src/app/features/test-records/amend/components/test-amendment-history/test-amendment-history.component.spec.ts @@ -1,7 +1,5 @@ import { formatDate } from '@angular/common'; -import { - ComponentFixture, fakeAsync, TestBed, tick, -} from '@angular/core/testing'; +import { ComponentFixture, TestBed, fakeAsync, tick } from '@angular/core/testing'; import { By } from '@angular/platform-browser'; import { RouterTestingModule } from '@angular/router/testing'; import { mockTestResult, mockTestResultArchived } from '@mocks/mock-test-result'; @@ -9,120 +7,128 @@ import { createMockTestResult } from '@mocks/test-result.mock'; import { TestResultModel } from '@models/test-results/test-result.model'; import { MockStore, provideMockStore } from '@ngrx/store/testing'; import { DefaultNullOrEmpty } from '@shared/pipes/default-null-or-empty/default-null-or-empty.pipe'; -import { initialAppState, State } from '@store/.'; +import { State, initialAppState } from '@store/.'; import { selectedTestSortedAmendmentHistory } from '@store/test-records'; import { TestAmendmentHistoryComponent } from './test-amendment-history.component'; describe('TestAmendmentHistoryComponent', () => { - let component: TestAmendmentHistoryComponent; - let fixture: ComponentFixture; - let store: MockStore; - const pipe = new DefaultNullOrEmpty(); - - beforeEach(async () => { - await TestBed.configureTestingModule({ - declarations: [TestAmendmentHistoryComponent, DefaultNullOrEmpty], - imports: [RouterTestingModule], - providers: [provideMockStore({ initialState: initialAppState })], - }).compileComponents(); - }); - - beforeEach(() => { - fixture = TestBed.createComponent(TestAmendmentHistoryComponent); - component = fixture.componentInstance; - store = TestBed.inject(MockStore); - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); - - describe('Created By', () => { - it('should return testerName entry if createdByName does not exist', () => { - const data = mockTestResultArchived(); - data.createdByName = undefined as unknown as string; - const name = component.getCreatedByName(data); - - expect(name).toBe(data.testerName); - }); - - it('should return testerName entry if createdByName is empty', () => { - const data = { ...mockTestResultArchived(), createdByName: '' }; - const name = component.getCreatedByName(data as TestResultModel); - - expect(name).toBe(data.testerName); - }); - - it('should return createdByName if createdByName not is empty', () => { - const name = component.getCreatedByName(mockTestResult()); - - expect(name).toBe(mockTestResult().createdByName); - expect(name).not.toEqual(mockTestResult().testerName); - }); - }); - - describe('Table', () => { - it('should render on the DOM', () => { - component.testRecord = mockTestResult(); - fixture.detectChanges(); - - const heading = fixture.debugElement.query(By.css('.govuk-table__caption')); - expect(heading).toBeTruthy(); - expect(heading.nativeElement.innerHTML).toContain('Test record amendment history'); - - const table = fixture.debugElement.query(By.css('.govuk-table__body')); - expect(table).toBeTruthy(); - }); - - describe('Table sorting', () => { - beforeEach(() => { - component.testRecord = createMockTestResult({ - createdAt: '2020-01-01T00:00:00.000Z', - reasonForCreation: 'reasonForCreation', - createdByName: 'Tester Man', - testHistory: [ - createMockTestResult({ - createdAt: new Date('2020-01-01').toISOString(), - createdByName: '_', - }), - ], - }); - fixture.detectChanges(); - }); - - it('should have first row be the current record', () => { - const rows = fixture.debugElement.queryAll(By.css('.govuk-table__row')); - expect(rows[0]).toBeTruthy(); - const cells = fixture.debugElement.queryAll(By.css('.govuk-table__cell')); - expect(cells[0].nativeElement.innerHTML).toBe(pipe.transform(component.testRecord?.reasonForCreation)); - expect(cells[1].nativeElement.innerHTML).toBe(component.testRecord?.createdByName); - expect(cells[2].nativeElement.innerHTML).toBe(formatDate(component.testRecord?.createdAt as string, 'MMM d, yyyy', 'en')); - expect(cells[3].nativeElement.innerHTML).toBe(''); - }); - - it('should have the second row be the first entry from amendement version history', fakeAsync(() => { - store.overrideSelector(selectedTestSortedAmendmentHistory, component.testRecord?.testHistory ?? []); - tick(); - fixture.detectChanges(); - const cells = fixture.debugElement.queryAll(By.css('.govuk-table__cell')); - expect(cells[4].nativeElement.innerHTML).toBe(pipe.transform(component.testRecord?.testHistory?.[0].reasonForCreation)); - expect(cells[5].nativeElement.innerHTML).toBe(pipe.transform(component.testRecord?.testHistory?.[0].createdByName)); - expect(cells[6].nativeElement.innerHTML).toBe(formatDate(component.testRecord?.testHistory?.[0].createdAt as string, 'MMM d, yyyy', 'en')); - expect(cells[7].nativeElement.innerHTML).toContain('View'); - })); - }); - - it('should have links to view amended records', fakeAsync(() => { - component.testRecord = mockTestResult(); - store.overrideSelector(selectedTestSortedAmendmentHistory, component.testRecord.testHistory ?? []); - tick(); - fixture.detectChanges(); - - const links = fixture.debugElement.queryAll(By.css('a')); - - links.forEach((e) => expect(e.nativeElement.innerHTML).toBe('View')); - expect(links).toHaveLength(component.testRecord?.testHistory?.length ?? 0); - })); - }); + let component: TestAmendmentHistoryComponent; + let fixture: ComponentFixture; + let store: MockStore; + const pipe = new DefaultNullOrEmpty(); + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [TestAmendmentHistoryComponent, DefaultNullOrEmpty], + imports: [RouterTestingModule], + providers: [provideMockStore({ initialState: initialAppState })], + }).compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(TestAmendmentHistoryComponent); + component = fixture.componentInstance; + store = TestBed.inject(MockStore); + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + describe('Created By', () => { + it('should return testerName entry if createdByName does not exist', () => { + const data = mockTestResultArchived(); + data.createdByName = undefined as unknown as string; + const name = component.getCreatedByName(data); + + expect(name).toBe(data.testerName); + }); + + it('should return testerName entry if createdByName is empty', () => { + const data = { ...mockTestResultArchived(), createdByName: '' }; + const name = component.getCreatedByName(data as TestResultModel); + + expect(name).toBe(data.testerName); + }); + + it('should return createdByName if createdByName not is empty', () => { + const name = component.getCreatedByName(mockTestResult()); + + expect(name).toBe(mockTestResult().createdByName); + expect(name).not.toEqual(mockTestResult().testerName); + }); + }); + + describe('Table', () => { + it('should render on the DOM', () => { + component.testRecord = mockTestResult(); + fixture.detectChanges(); + + const heading = fixture.debugElement.query(By.css('.govuk-table__caption')); + expect(heading).toBeTruthy(); + expect(heading.nativeElement.innerHTML).toContain('Test record amendment history'); + + const table = fixture.debugElement.query(By.css('.govuk-table__body')); + expect(table).toBeTruthy(); + }); + + describe('Table sorting', () => { + beforeEach(() => { + component.testRecord = createMockTestResult({ + createdAt: '2020-01-01T00:00:00.000Z', + reasonForCreation: 'reasonForCreation', + createdByName: 'Tester Man', + testHistory: [ + createMockTestResult({ + createdAt: new Date('2020-01-01').toISOString(), + createdByName: '_', + }), + ], + }); + fixture.detectChanges(); + }); + + it('should have first row be the current record', () => { + const rows = fixture.debugElement.queryAll(By.css('.govuk-table__row')); + expect(rows[0]).toBeTruthy(); + const cells = fixture.debugElement.queryAll(By.css('.govuk-table__cell')); + expect(cells[0].nativeElement.innerHTML).toBe(pipe.transform(component.testRecord?.reasonForCreation)); + expect(cells[1].nativeElement.innerHTML).toBe(component.testRecord?.createdByName); + expect(cells[2].nativeElement.innerHTML).toBe( + formatDate(component.testRecord?.createdAt as string, 'MMM d, yyyy', 'en') + ); + expect(cells[3].nativeElement.innerHTML).toBe(''); + }); + + it('should have the second row be the first entry from amendement version history', fakeAsync(() => { + store.overrideSelector(selectedTestSortedAmendmentHistory, component.testRecord?.testHistory ?? []); + tick(); + fixture.detectChanges(); + const cells = fixture.debugElement.queryAll(By.css('.govuk-table__cell')); + expect(cells[4].nativeElement.innerHTML).toBe( + pipe.transform(component.testRecord?.testHistory?.[0].reasonForCreation) + ); + expect(cells[5].nativeElement.innerHTML).toBe( + pipe.transform(component.testRecord?.testHistory?.[0].createdByName) + ); + expect(cells[6].nativeElement.innerHTML).toBe( + formatDate(component.testRecord?.testHistory?.[0].createdAt as string, 'MMM d, yyyy', 'en') + ); + expect(cells[7].nativeElement.innerHTML).toContain('View'); + })); + }); + + it('should have links to view amended records', fakeAsync(() => { + component.testRecord = mockTestResult(); + store.overrideSelector(selectedTestSortedAmendmentHistory, component.testRecord.testHistory ?? []); + tick(); + fixture.detectChanges(); + + const links = fixture.debugElement.queryAll(By.css('a')); + + links.forEach((e) => expect(e.nativeElement.innerHTML).toBe('View')); + expect(links).toHaveLength(component.testRecord?.testHistory?.length ?? 0); + })); + }); }); diff --git a/src/app/features/test-records/amend/components/test-amendment-history/test-amendment-history.component.ts b/src/app/features/test-records/amend/components/test-amendment-history/test-amendment-history.component.ts index d199e04dd1..f100592c22 100644 --- a/src/app/features/test-records/amend/components/test-amendment-history/test-amendment-history.component.ts +++ b/src/app/features/test-records/amend/components/test-amendment-history/test-amendment-history.component.ts @@ -1,23 +1,23 @@ import { Component, Input } from '@angular/core'; import { TestResultModel } from '@models/test-results/test-result.model'; -import { select, Store } from '@ngrx/store'; +import { Store, select } from '@ngrx/store'; import { selectedTestSortedAmendmentHistory } from '@store/test-records/selectors/test-records.selectors'; import { Observable } from 'rxjs/internal/Observable'; @Component({ - selector: 'app-test-amendment-history', - templateUrl: './test-amendment-history.component.html', + selector: 'app-test-amendment-history', + templateUrl: './test-amendment-history.component.html', }) export class TestAmendmentHistoryComponent { - @Input() testRecord: TestResultModel | undefined; + @Input() testRecord: TestResultModel | undefined; - constructor(private store: Store) {} + constructor(private store: Store) {} - getCreatedByName(testResult: TestResultModel | undefined) { - return testResult?.createdByName || testResult?.testerName; - } + getCreatedByName(testResult: TestResultModel | undefined) { + return testResult?.createdByName || testResult?.testerName; + } - get sortedTestHistory$(): Observable { - return this.store.pipe(select(selectedTestSortedAmendmentHistory)); - } + get sortedTestHistory$(): Observable { + return this.store.pipe(select(selectedTestSortedAmendmentHistory)); + } } diff --git a/src/app/features/test-records/amend/views/amend-test/amend-test.component.spec.ts b/src/app/features/test-records/amend/views/amend-test/amend-test.component.spec.ts index 048477a7b1..fe9413a5fd 100644 --- a/src/app/features/test-records/amend/views/amend-test/amend-test.component.spec.ts +++ b/src/app/features/test-records/amend/views/amend-test/amend-test.component.spec.ts @@ -3,23 +3,23 @@ import { RouterTestingModule } from '@angular/router/testing'; import { AmendTestComponent } from './amend-test.component'; describe('AmendTestComponent', () => { - let component: AmendTestComponent; - let fixture: ComponentFixture; + let component: AmendTestComponent; + let fixture: ComponentFixture; - beforeEach(async () => { - await TestBed.configureTestingModule({ - declarations: [AmendTestComponent], - imports: [RouterTestingModule], - }).compileComponents(); - }); + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [AmendTestComponent], + imports: [RouterTestingModule], + }).compileComponents(); + }); - beforeEach(() => { - fixture = TestBed.createComponent(AmendTestComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); + beforeEach(() => { + fixture = TestBed.createComponent(AmendTestComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); }); diff --git a/src/app/features/test-records/amend/views/amend-test/amend-test.component.ts b/src/app/features/test-records/amend/views/amend-test/amend-test.component.ts index dad7e0e1ab..b359ca2fce 100644 --- a/src/app/features/test-records/amend/views/amend-test/amend-test.component.ts +++ b/src/app/features/test-records/amend/views/amend-test/amend-test.component.ts @@ -1,8 +1,8 @@ import { ChangeDetectionStrategy, Component } from '@angular/core'; @Component({ - selector: 'app-amend-test', - templateUrl: './amend-test.component.html', - changeDetection: ChangeDetectionStrategy.OnPush, + selector: 'app-amend-test', + templateUrl: './amend-test.component.html', + changeDetection: ChangeDetectionStrategy.OnPush, }) export class AmendTestComponent {} diff --git a/src/app/features/test-records/amend/views/amended-test-record/amended-test-record.component.spec.ts b/src/app/features/test-records/amend/views/amended-test-record/amended-test-record.component.spec.ts index e7e50c7811..016ac13173 100644 --- a/src/app/features/test-records/amend/views/amended-test-record/amended-test-record.component.spec.ts +++ b/src/app/features/test-records/amend/views/amended-test-record/amended-test-record.component.spec.ts @@ -1,5 +1,5 @@ import { HttpClientTestingModule } from '@angular/common/http/testing'; -import { ComponentFixture, inject, TestBed } from '@angular/core/testing'; +import { ComponentFixture, TestBed, inject } from '@angular/core/testing'; import { RouterTestingModule } from '@angular/router/testing'; import { ApiModule as TestResultsApiModule } from '@api/test-results'; import { DynamicFormsModule } from '@forms/dynamic-forms.module'; @@ -10,7 +10,7 @@ import { MockStore, provideMockStore } from '@ngrx/store/testing'; import { TestRecordsService } from '@services/test-records/test-records.service'; import { UserService } from '@services/user-service/user-service'; import { SharedModule } from '@shared/shared.module'; -import { initialAppState, State } from '@store/.'; +import { State, initialAppState } from '@store/.'; import { selectAmendedDefectData, selectedAmendedTestResultState } from '@store/test-records'; import { of } from 'rxjs'; import { BaseTestRecordComponent } from '../../../components/base-test-record/base-test-record.component'; @@ -18,47 +18,50 @@ import { VehicleHeaderComponent } from '../../../components/vehicle-header/vehic import { AmendedTestRecordComponent } from './amended-test-record.component'; describe('AmendedTestRecordComponent', () => { - let component: AmendedTestRecordComponent; - let fixture: ComponentFixture; - let store: MockStore; + let component: AmendedTestRecordComponent; + let fixture: ComponentFixture; + let store: MockStore; - beforeEach(async () => { - await TestBed.configureTestingModule({ - declarations: [AmendedTestRecordComponent, BaseTestRecordComponent, VehicleHeaderComponent], - imports: [HttpClientTestingModule, SharedModule, DynamicFormsModule, TestResultsApiModule, RouterTestingModule], - providers: [ - TestRecordsService, - provideMockStore({ initialState: initialAppState }), - { - provide: UserService, - useValue: { - roles$: of([Roles.TestResultView]), - }, - }, - ], - }).compileComponents(); - }); + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [AmendedTestRecordComponent, BaseTestRecordComponent, VehicleHeaderComponent], + imports: [HttpClientTestingModule, SharedModule, DynamicFormsModule, TestResultsApiModule, RouterTestingModule], + providers: [ + TestRecordsService, + provideMockStore({ initialState: initialAppState }), + { + provide: UserService, + useValue: { + roles$: of([Roles.TestResultView]), + }, + }, + ], + }).compileComponents(); + }); - beforeEach(() => { - store = TestBed.inject(MockStore); - store.overrideSelector(selectedAmendedTestResultState, mockTestResult()); - store.overrideSelector(selectAmendedDefectData, mockDefectList()); + beforeEach(() => { + store = TestBed.inject(MockStore); + store.overrideSelector(selectedAmendedTestResultState, mockTestResult()); + store.overrideSelector(selectAmendedDefectData, mockDefectList()); - fixture = TestBed.createComponent(AmendedTestRecordComponent); - component = fixture.componentInstance; - }); + fixture = TestBed.createComponent(AmendedTestRecordComponent); + component = fixture.componentInstance; + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); - it('sets class properties on component init', inject([TestRecordsService], (testRecordsService: TestRecordsService) => { - const amendedTestResultSpy = jest.spyOn(testRecordsService, 'amendedTestResult$', 'get'); - const amendedDefectDataSpy = jest.spyOn(testRecordsService, 'amendedDefectData$', 'get'); + it('sets class properties on component init', inject( + [TestRecordsService], + (testRecordsService: TestRecordsService) => { + const amendedTestResultSpy = jest.spyOn(testRecordsService, 'amendedTestResult$', 'get'); + const amendedDefectDataSpy = jest.spyOn(testRecordsService, 'amendedDefectData$', 'get'); - fixture.detectChanges(); + fixture.detectChanges(); - expect(amendedTestResultSpy).toHaveBeenCalled(); - expect(amendedDefectDataSpy).toHaveBeenCalled(); - })); + expect(amendedTestResultSpy).toHaveBeenCalled(); + expect(amendedDefectDataSpy).toHaveBeenCalled(); + } + )); }); diff --git a/src/app/features/test-records/amend/views/amended-test-record/amended-test-record.component.ts b/src/app/features/test-records/amend/views/amended-test-record/amended-test-record.component.ts index ac7785512e..b2cb3fec0b 100644 --- a/src/app/features/test-records/amend/views/amended-test-record/amended-test-record.component.ts +++ b/src/app/features/test-records/amend/views/amended-test-record/amended-test-record.component.ts @@ -3,22 +3,22 @@ import { FormNode } from '@forms/services/dynamic-form.types'; import { TestResultDefects } from '@models/test-results/test-result-defects.model'; import { TestResultModel } from '@models/test-results/test-result.model'; import { TestRecordsService } from '@services/test-records/test-records.service'; -import { firstValueFrom, Observable, of } from 'rxjs'; +import { Observable, firstValueFrom, of } from 'rxjs'; @Component({ - selector: 'app-amended-test-record', - templateUrl: './amended-test-record.component.html', + selector: 'app-amended-test-record', + templateUrl: './amended-test-record.component.html', }) export class AmendedTestRecordComponent implements OnInit { - testResult$: Observable = of(undefined); - defects$: Observable = of(undefined); - sectionTemplates$: Observable = of(undefined); + testResult$: Observable = of(undefined); + defects$: Observable = of(undefined); + sectionTemplates$: Observable = of(undefined); - constructor(private testRecordsService: TestRecordsService) {} + constructor(private testRecordsService: TestRecordsService) {} - async ngOnInit() { - this.testResult$ = this.testRecordsService.amendedTestResult$; - this.defects$ = this.testRecordsService.amendedDefectData$; - this.testRecordsService.editingTestResult((await firstValueFrom(this.testResult$)) as TestResultModel); - } + async ngOnInit() { + this.testResult$ = this.testRecordsService.amendedTestResult$; + this.defects$ = this.testRecordsService.amendedDefectData$; + this.testRecordsService.editingTestResult((await firstValueFrom(this.testResult$)) as TestResultModel); + } } diff --git a/src/app/features/test-records/amend/views/confirm-cancellation/confirm-cancellation.component.spec.ts b/src/app/features/test-records/amend/views/confirm-cancellation/confirm-cancellation.component.spec.ts index 1515b96ab3..06e655e7ad 100644 --- a/src/app/features/test-records/amend/views/confirm-cancellation/confirm-cancellation.component.spec.ts +++ b/src/app/features/test-records/amend/views/confirm-cancellation/confirm-cancellation.component.spec.ts @@ -14,33 +14,33 @@ import { VehicleHeaderComponent } from '../../../components/vehicle-header/vehic import { ConfirmCancellationComponent } from './confirm-cancellation.component'; describe('ConfirmCancellationComponent', () => { - let component: ConfirmCancellationComponent; - let fixture: ComponentFixture; + let component: ConfirmCancellationComponent; + let fixture: ComponentFixture; - beforeEach(async () => { - await TestBed.configureTestingModule({ - declarations: [ConfirmCancellationComponent, VehicleHeaderComponent], - imports: [DynamicFormsModule, HttpClientTestingModule, ReactiveFormsModule, RouterTestingModule, SharedModule], - providers: [ - provideMockStore({ initialState: initialAppState }), - provideMockActions(() => new ReplaySubject()), - { - provide: TestRecordsService, - useValue: { - cancelTest: () => {}, - }, - }, - ], - }).compileComponents(); - }); + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ConfirmCancellationComponent, VehicleHeaderComponent], + imports: [DynamicFormsModule, HttpClientTestingModule, ReactiveFormsModule, RouterTestingModule, SharedModule], + providers: [ + provideMockStore({ initialState: initialAppState }), + provideMockActions(() => new ReplaySubject()), + { + provide: TestRecordsService, + useValue: { + cancelTest: () => {}, + }, + }, + ], + }).compileComponents(); + }); - beforeEach(() => { - fixture = TestBed.createComponent(ConfirmCancellationComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); + beforeEach(() => { + fixture = TestBed.createComponent(ConfirmCancellationComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); }); diff --git a/src/app/features/test-records/amend/views/confirm-cancellation/confirm-cancellation.component.ts b/src/app/features/test-records/amend/views/confirm-cancellation/confirm-cancellation.component.ts index 77c48a5e71..499dc4390f 100644 --- a/src/app/features/test-records/amend/views/confirm-cancellation/confirm-cancellation.component.ts +++ b/src/app/features/test-records/amend/views/confirm-cancellation/confirm-cancellation.component.ts @@ -10,64 +10,64 @@ import { Store, select } from '@ngrx/store'; import { TestRecordsService } from '@services/test-records/test-records.service'; import { selectRouteNestedParams } from '@store/router/selectors/router.selectors'; import { selectedTestResultState, updateTestResultSuccess } from '@store/test-records'; -import { - Observable, Subject, map, takeUntil, -} from 'rxjs'; +import { Observable, Subject, map, takeUntil } from 'rxjs'; @Component({ - selector: 'app-confirm-cancellation', - templateUrl: './confirm-cancellation.component.html', + selector: 'app-confirm-cancellation', + templateUrl: './confirm-cancellation.component.html', }) export class ConfirmCancellationComponent implements OnDestroy { - form = new CustomFormGroup( - { name: 'cancellation-reason', type: FormNodeTypes.GROUP }, - { reason: new CustomFormControl({ name: 'reason', type: FormNodeTypes.CONTROL }, undefined, [Validators.required]) }, - ); + form = new CustomFormGroup( + { name: 'cancellation-reason', type: FormNodeTypes.GROUP }, + { reason: new CustomFormControl({ name: 'reason', type: FormNodeTypes.CONTROL }, undefined, [Validators.required]) } + ); - private destroy$ = new Subject(); + private destroy$ = new Subject(); - constructor( - private actions$: Actions, - private errorService: GlobalErrorService, - private route: ActivatedRoute, - private router: Router, - private store: Store, - private testRecordsService: TestRecordsService, - private globalErrorService: GlobalErrorService, - private location: Location, - ) { - this.actions$.pipe(ofType(updateTestResultSuccess), takeUntil(this.destroy$)).subscribe(() => { - void this.router.navigate(['../../../../..'], { relativeTo: this.route }); - }); - } + constructor( + private actions$: Actions, + private errorService: GlobalErrorService, + private route: ActivatedRoute, + private router: Router, + private store: Store, + private testRecordsService: TestRecordsService, + private globalErrorService: GlobalErrorService, + private location: Location + ) { + this.actions$.pipe(ofType(updateTestResultSuccess), takeUntil(this.destroy$)).subscribe(() => { + void this.router.navigate(['../../../../..'], { relativeTo: this.route }); + }); + } - ngOnDestroy(): void { - this.errorService.clearErrors(); - this.destroy$.next(); - this.destroy$.complete(); - } + ngOnDestroy(): void { + this.errorService.clearErrors(); + this.destroy$.next(); + this.destroy$.complete(); + } - navigateBack() { - this.globalErrorService.clearErrors(); - this.location.back(); - } + navigateBack() { + this.globalErrorService.clearErrors(); + this.location.back(); + } - get testResult$(): Observable { - return this.store.pipe(select(selectedTestResultState)); - } + get testResult$(): Observable { + return this.store.pipe(select(selectedTestResultState)); + } - get testNumber$(): Observable { - return this.store.pipe( - select(selectRouteNestedParams), - map((params) => params['testNumber']), - ); - } + get testNumber$(): Observable { + return this.store.pipe( + select(selectRouteNestedParams), + map((params) => params['testNumber']) + ); + } - handleSubmit() { - if (!this.form.valid) return; + handleSubmit() { + if (!this.form.valid) return; - const reason: string = this.form.get('reason')?.value; + const reason: string = this.form.get('reason')?.value; - this.testRecordsService.cancelTest(reason); - } + this.testRecordsService.cleanTestResult(); + + this.testRecordsService.cancelTest(reason); + } } diff --git a/src/app/features/test-records/amend/views/test-amend-reason/test-amend-reason.component.spec.ts b/src/app/features/test-records/amend/views/test-amend-reason/test-amend-reason.component.spec.ts index 74445ba60e..edf4e17d4c 100644 --- a/src/app/features/test-records/amend/views/test-amend-reason/test-amend-reason.component.spec.ts +++ b/src/app/features/test-records/amend/views/test-amend-reason/test-amend-reason.component.spec.ts @@ -6,38 +6,38 @@ import { DynamicFormsModule } from '@forms/dynamic-forms.module'; import { TestAmendReasonComponent } from './test-amend-reason.component'; describe('TestAmendReasonComponent', () => { - let component: TestAmendReasonComponent; - let fixture: ComponentFixture; - let router: Router; - let route: ActivatedRoute; + let component: TestAmendReasonComponent; + let fixture: ComponentFixture; + let router: Router; + let route: ActivatedRoute; - beforeEach(async () => { - await TestBed.configureTestingModule({ - declarations: [TestAmendReasonComponent], - imports: [RouterTestingModule, DynamicFormsModule, ReactiveFormsModule], - }).compileComponents(); + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [TestAmendReasonComponent], + imports: [RouterTestingModule, DynamicFormsModule, ReactiveFormsModule], + }).compileComponents(); - router = TestBed.inject(Router); - route = TestBed.inject(ActivatedRoute); - }); + router = TestBed.inject(Router); + route = TestBed.inject(ActivatedRoute); + }); - beforeEach(() => { - fixture = TestBed.createComponent(TestAmendReasonComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); + beforeEach(() => { + fixture = TestBed.createComponent(TestAmendReasonComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); - it.each([ - ['incorrect-test-type', 1], - ['amend-test-details', 2], - ])('should navigate to %s on submit when reason is %n', (path, reason) => { - const navigateSpy = jest.spyOn(router, 'navigate').mockReturnValue(Promise.resolve(true)); - component.form.setValue({ reason }); - component.handleSubmit(); - expect(navigateSpy).toHaveBeenCalledWith([path], { relativeTo: route }); - }); + it.each([ + ['incorrect-test-type', 1], + ['amend-test-details', 2], + ])('should navigate to %s on submit when reason is %n', (path, reason) => { + const navigateSpy = jest.spyOn(router, 'navigate').mockReturnValue(Promise.resolve(true)); + component.form.setValue({ reason }); + component.handleSubmit(); + expect(navigateSpy).toHaveBeenCalledWith([path], { relativeTo: route }); + }); }); diff --git a/src/app/features/test-records/amend/views/test-amend-reason/test-amend-reason.component.ts b/src/app/features/test-records/amend/views/test-amend-reason/test-amend-reason.component.ts index c00b14343d..2a3bc33f05 100644 --- a/src/app/features/test-records/amend/views/test-amend-reason/test-amend-reason.component.ts +++ b/src/app/features/test-records/amend/views/test-amend-reason/test-amend-reason.component.ts @@ -1,39 +1,44 @@ import { ChangeDetectionStrategy, Component } from '@angular/core'; import { Validators } from '@angular/forms'; import { ActivatedRoute, Router } from '@angular/router'; -import { - CustomFormControl, CustomFormGroup, FormNodeOption, FormNodeTypes, -} from '@forms/services/dynamic-form.types'; +import { CustomFormControl, CustomFormGroup, FormNodeOption, FormNodeTypes } from '@forms/services/dynamic-form.types'; @Component({ - selector: 'app-test-amend-reason', - templateUrl: './test-amend-reason.component.html', - changeDetection: ChangeDetectionStrategy.OnPush, + selector: 'app-test-amend-reason', + templateUrl: './test-amend-reason.component.html', + changeDetection: ChangeDetectionStrategy.OnPush, }) export class TestAmendReasonComponent { - private routes: Record = { 1: 'incorrect-test-type', 2: 'amend-test-details' }; + private routes: Record = { 1: 'incorrect-test-type', 2: 'amend-test-details' }; - reasons: Array> = [ - { label: 'The test type is incorrect', value: 1 }, - { label: 'The test details are incorrect', value: 2, hint: 'Change test location, assessor, test details, defects, and results.' }, - ]; + reasons: Array> = [ + { label: 'The test type is incorrect', value: 1 }, + { + label: 'The test details are incorrect', + value: 2, + hint: 'Change test location, assessor, test details, defects, and results.', + }, + ]; - form: CustomFormGroup; + form: CustomFormGroup; - constructor(private router: Router, private route: ActivatedRoute) { - this.form = new CustomFormGroup( - { name: 'reasonForAmend', type: FormNodeTypes.GROUP }, - { - reason: new CustomFormControl({ name: 'reason', type: FormNodeTypes.CONTROL }, 2, [Validators.required]), - }, - ); - } + constructor( + private router: Router, + private route: ActivatedRoute + ) { + this.form = new CustomFormGroup( + { name: 'reasonForAmend', type: FormNodeTypes.GROUP }, + { + reason: new CustomFormControl({ name: 'reason', type: FormNodeTypes.CONTROL }, 2, [Validators.required]), + } + ); + } - handleSubmit() { - const reason: number = this.form.get('reason')?.value; + handleSubmit() { + const reason: number = this.form.get('reason')?.value; - if (this.form.valid && reason) { - void this.router.navigate([this.routes[`${reason}`]], { relativeTo: this.route }); - } - } + if (this.form.valid && reason) { + void this.router.navigate([this.routes[`${reason}`]], { relativeTo: this.route }); + } + } } diff --git a/src/app/features/test-records/amend/views/test-record/test-record.component.spec.ts b/src/app/features/test-records/amend/views/test-record/test-record.component.spec.ts index 48e8ff98fe..421664d132 100644 --- a/src/app/features/test-records/amend/views/test-record/test-record.component.spec.ts +++ b/src/app/features/test-records/amend/views/test-record/test-record.component.spec.ts @@ -1,8 +1,6 @@ import { HttpClientTestingModule } from '@angular/common/http/testing'; import { DebugElement } from '@angular/core'; -import { - ComponentFixture, fakeAsync, TestBed, tick, waitForAsync, -} from '@angular/core/testing'; +import { ComponentFixture, TestBed, fakeAsync, tick, waitForAsync } from '@angular/core/testing'; import { By } from '@angular/platform-browser'; import { Params } from '@angular/router'; import { RouterTestingModule } from '@angular/router/testing'; @@ -18,12 +16,10 @@ import { TechnicalRecordService } from '@services/technical-record/technical-rec import { TestRecordsService } from '@services/test-records/test-records.service'; import { UserService } from '@services/user-service/user-service'; import { SharedModule } from '@shared/shared.module'; -import { initialAppState, State } from '@store/.'; +import { State, initialAppState } from '@store/.'; import { routeEditable, selectRouteData, selectRouteNestedParams } from '@store/router/selectors/router.selectors'; -import { - initialTestResultsState, isTestTypeKeySame, sectionTemplates, testResultInEdit, -} from '@store/test-records'; -import { of, ReplaySubject } from 'rxjs'; +import { initialTestResultsState, isTestTypeKeySame, sectionTemplates, testResultInEdit } from '@store/test-records'; +import { ReplaySubject, of } from 'rxjs'; import { DynamicFormsModule } from '../../../../../forms/dynamic-forms.module'; import { BaseTestRecordComponent } from '../../../components/base-test-record/base-test-record.component'; import { VehicleHeaderComponent } from '../../../components/vehicle-header/vehicle-header.component'; @@ -31,195 +27,202 @@ import { TestAmendmentHistoryComponent } from '../../components/test-amendment-h import { TestRecordComponent } from './test-record.component'; describe('TestRecordComponent', () => { - let component: TestRecordComponent; - let fixture: ComponentFixture; - let el: DebugElement; - let store: MockStore; - let testRecordsService: TestRecordsService; - const actions$ = new ReplaySubject(); - - beforeEach(async () => { - await TestBed.configureTestingModule({ - declarations: [BaseTestRecordComponent, TestAmendmentHistoryComponent, TestRecordComponent, VehicleHeaderComponent], - imports: [DynamicFormsModule, HttpClientTestingModule, RouterTestingModule, TestResultsApiModule, SharedModule], - providers: [ - TestRecordsService, - provideMockStore({ initialState: initialAppState }), - RouterService, - provideMockActions(() => actions$), - { - provide: UserService, - useValue: { - roles$: of(['TestResult.Amend']), - }, - }, - TechnicalRecordService, - ], - }).compileComponents(); - }); - - beforeEach(() => { - fixture = TestBed.createComponent(TestRecordComponent); - component = fixture.componentInstance; - el = fixture.debugElement; - store = TestBed.inject(MockStore); - store.overrideSelector(routeEditable, false); - testRecordsService = TestBed.inject(TestRecordsService); - - store.resetSelectors(); - store.overrideSelector(selectRouteNestedParams, { testResultId: '1', testNumber: 'foo' } as Params); - store.overrideSelector(selectRouteData, { isEditing: false }); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); - - it('should not display anything when there is no data', waitForAsync(() => { - component.testResult$ = of(undefined); - - fixture.detectChanges(); - - expect(fixture.debugElement.query(By.css('h1'))).toBeNull(); - })); - - describe('button actions', () => { - beforeEach(() => { - jest - .spyOn(testRecordsService, 'testResult$', 'get') - .mockReturnValue(of({ vehicleType: 'psv', testTypes: [{ testTypeId: '1' }] } as TestResultModel)); - }); - - it('should display review button when edit query param is true', waitForAsync(() => { - store.overrideSelector(routeEditable, true); - jest.spyOn(component, 'isTestTypeGroupEditable$', 'get').mockReturnValue(of(true)); - - fixture.detectChanges(); - expect(el.query(By.css('button#review-test-result'))).toBeTruthy(); - })); - - it('should run handleSave when save button is clicked', waitForAsync(() => { - store.overrideSelector(routeEditable, true); - component.testMode = TestModeEnum.View; - - jest.spyOn(component, 'isTestTypeGroupEditable$', 'get').mockReturnValue(of(true)); - - fixture.detectChanges(); - - const saveSpy = jest.spyOn(component, 'handleSave'); - el.query(By.css('button#save-test-result')).nativeElement.click(); - expect(saveSpy).toHaveBeenCalledTimes(1); - })); - - it('should run handleReview when review button is clicked', waitForAsync(() => { - store.overrideSelector(routeEditable, true); - - jest.spyOn(component, 'isTestTypeGroupEditable$', 'get').mockReturnValue(of(true)); - - fixture.detectChanges(); - - const reviewSpy = jest.spyOn(component, 'handleReview'); - el.query(By.css('button#review-test-result')).nativeElement.click(); - expect(reviewSpy).toHaveBeenCalledTimes(1); - })); - }); - - describe('TestRecordComponent.prototype.handleSave.name', () => { - beforeEach(() => { - store.setState({ - ...initialAppState, - testRecords: { - ...initialTestResultsState, - ids: ['1'], - entities: { 1: { testTypes: [{ testNumber: 'foo' }] } as TestResultModel }, - editingTestResult: { testTypes: [{ testNumber: 'foo' }] } as TestResultModel, - }, - }); - }); - - it('should return without calling updateTestResultState if forms are clean', fakeAsync(async () => { - store.overrideSelector(isTestTypeKeySame('testTypeId'), true); - const updateTestResultStateSpy = jest.spyOn(testRecordsService, 'updateTestResult'); - await component.handleSave(); - expect(updateTestResultStateSpy).not.toHaveBeenCalled(); - })); - - it('should return without calling updateTestResultState if any forms are invalid', fakeAsync(async () => { - const updateTestResultStateSpy = jest.spyOn(testRecordsService, 'updateTestResult'); - component.isAnyFormDirty = jest.fn().mockReturnValue(true); - component.isAnyFormInvalid = jest.fn().mockReturnValue(true); - await component.handleSave(); - expect(updateTestResultStateSpy).not.toHaveBeenCalled(); - })); - - it('should call updateTestResult with value of all forms merged into one', async () => { - fixture.detectChanges(); - const updateTestResultStateSpy = jest.spyOn(testRecordsService, 'updateTestResult').mockImplementation(() => true); - const testRecord = { testResultId: '1', testTypes: [{ testTypeId: '2' }] } as TestResultModel; - store.overrideSelector(isTestTypeKeySame('testTypeId'), false); - store.overrideSelector(testResultInEdit, testRecord); - store.overrideSelector(sectionTemplates, Object.values(masterTpl.psv['testTypesGroup1'] ?? '')); - - component.isAnyFormDirty = jest.fn().mockReturnValue(true); - component.isAnyFormInvalid = jest.fn().mockReturnValue(false); - - await component.handleSave(); - - fixture.detectChanges(); - expect(updateTestResultStateSpy).toHaveBeenCalledTimes(1); - expect(updateTestResultStateSpy).toHaveBeenCalledWith(testRecord); - }); - }); - - describe('Render banner', () => { - beforeEach(() => { - jest - .spyOn(testRecordsService, 'testResult$', 'get') - .mockReturnValue(of({ vehicleType: 'psv', testTypes: [{ testTypeId: '1' }] } as TestResultModel)); - }); - - it('should render the banner if the test type id is not supported', waitForAsync(() => { - jest.spyOn(component, 'isTestTypeGroupEditable$', 'get').mockReturnValue(of(false)); - fixture.detectChanges(); - const banner = el.query(By.css('div.govuk-notification-banner')); - expect(banner).toBeTruthy(); - })); - - it('should not render the banner if the test type id is supported', fakeAsync(() => { - jest.spyOn(component, 'isTestTypeGroupEditable$', 'get').mockReturnValue(of(true)); - tick(); - fixture.detectChanges(); - const banner = el.query(By.css('div.govuk-notification-banner')); - expect(banner).toBeNull(); - })); - }); - - it('should set testMode to be view when has errors is false', async () => { - expect(component.testMode).toEqual(TestModeEnum.Edit); - - const errorsSpy = jest.spyOn(component, 'hasErrors').mockReturnValue(Promise.resolve(false)); - await component.handleReview(); - - expect(errorsSpy).toHaveBeenCalledTimes(1); - - expect(component.testMode).toEqual(TestModeEnum.View); - }); - - it('should not set testMode to be view when has errors is true', async () => { - expect(component.testMode).toEqual(TestModeEnum.Edit); - - const errorsSpy = jest.spyOn(component, 'hasErrors').mockReturnValue(Promise.resolve(true)); - await component.handleReview(); - - expect(errorsSpy).toHaveBeenCalledTimes(1); - - expect(component.testMode).toEqual(TestModeEnum.Edit); - }); - - it('should set testMode back to edit', () => { - component.testMode = TestModeEnum.View; - component.handleCancel(); - - expect(component.testMode).toEqual(TestModeEnum.Edit); - }); + let component: TestRecordComponent; + let fixture: ComponentFixture; + let el: DebugElement; + let store: MockStore; + let testRecordsService: TestRecordsService; + const actions$ = new ReplaySubject(); + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ + BaseTestRecordComponent, + TestAmendmentHistoryComponent, + TestRecordComponent, + VehicleHeaderComponent, + ], + imports: [DynamicFormsModule, HttpClientTestingModule, RouterTestingModule, TestResultsApiModule, SharedModule], + providers: [ + TestRecordsService, + provideMockStore({ initialState: initialAppState }), + RouterService, + provideMockActions(() => actions$), + { + provide: UserService, + useValue: { + roles$: of(['TestResult.Amend']), + }, + }, + TechnicalRecordService, + ], + }).compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(TestRecordComponent); + component = fixture.componentInstance; + el = fixture.debugElement; + store = TestBed.inject(MockStore); + store.overrideSelector(routeEditable, false); + testRecordsService = TestBed.inject(TestRecordsService); + + store.resetSelectors(); + store.overrideSelector(selectRouteNestedParams, { testResultId: '1', testNumber: 'foo' } as Params); + store.overrideSelector(selectRouteData, { isEditing: false }); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + it('should not display anything when there is no data', waitForAsync(() => { + component.testResult$ = of(undefined); + + fixture.detectChanges(); + + expect(fixture.debugElement.query(By.css('h1'))).toBeNull(); + })); + + describe('button actions', () => { + beforeEach(() => { + jest + .spyOn(testRecordsService, 'testResult$', 'get') + .mockReturnValue(of({ vehicleType: 'psv', testTypes: [{ testTypeId: '1' }] } as TestResultModel)); + }); + + it('should display review button when edit query param is true', waitForAsync(() => { + store.overrideSelector(routeEditable, true); + jest.spyOn(component, 'isTestTypeGroupEditable$', 'get').mockReturnValue(of(true)); + + fixture.detectChanges(); + expect(el.query(By.css('button#review-test-result'))).toBeTruthy(); + })); + + it('should run handleSave when save button is clicked', waitForAsync(() => { + store.overrideSelector(routeEditable, true); + component.testMode = TestModeEnum.View; + + jest.spyOn(component, 'isTestTypeGroupEditable$', 'get').mockReturnValue(of(true)); + + fixture.detectChanges(); + + const saveSpy = jest.spyOn(component, 'handleSave'); + el.query(By.css('button#save-test-result')).nativeElement.click(); + expect(saveSpy).toHaveBeenCalledTimes(1); + })); + + it('should run handleReview when review button is clicked', waitForAsync(() => { + store.overrideSelector(routeEditable, true); + + jest.spyOn(component, 'isTestTypeGroupEditable$', 'get').mockReturnValue(of(true)); + + fixture.detectChanges(); + + const reviewSpy = jest.spyOn(component, 'handleReview'); + el.query(By.css('button#review-test-result')).nativeElement.click(); + expect(reviewSpy).toHaveBeenCalledTimes(1); + })); + }); + + describe('TestRecordComponent.prototype.handleSave.name', () => { + beforeEach(() => { + store.setState({ + ...initialAppState, + testRecords: { + ...initialTestResultsState, + ids: ['1'], + entities: { 1: { testTypes: [{ testNumber: 'foo' }] } as TestResultModel }, + editingTestResult: { testTypes: [{ testNumber: 'foo' }] } as TestResultModel, + }, + }); + }); + + it('should return without calling updateTestResultState if forms are clean', fakeAsync(async () => { + store.overrideSelector(isTestTypeKeySame('testTypeId'), true); + const updateTestResultStateSpy = jest.spyOn(testRecordsService, 'updateTestResult'); + await component.handleSave(); + expect(updateTestResultStateSpy).not.toHaveBeenCalled(); + })); + + it('should return without calling updateTestResultState if any forms are invalid', fakeAsync(async () => { + const updateTestResultStateSpy = jest.spyOn(testRecordsService, 'updateTestResult'); + component.isAnyFormDirty = jest.fn().mockReturnValue(true); + component.isAnyFormInvalid = jest.fn().mockReturnValue(true); + await component.handleSave(); + expect(updateTestResultStateSpy).not.toHaveBeenCalled(); + })); + + it('should call updateTestResult with value of all forms merged into one', async () => { + fixture.detectChanges(); + const updateTestResultStateSpy = jest + .spyOn(testRecordsService, 'updateTestResult') + .mockImplementation(() => true); + const testRecord = { testResultId: '1', testTypes: [{ testTypeId: '2' }] } as TestResultModel; + store.overrideSelector(isTestTypeKeySame('testTypeId'), false); + store.overrideSelector(testResultInEdit, testRecord); + store.overrideSelector(sectionTemplates, Object.values(masterTpl.psv['testTypesGroup1'] ?? '')); + + component.isAnyFormDirty = jest.fn().mockReturnValue(true); + component.isAnyFormInvalid = jest.fn().mockReturnValue(false); + + await component.handleSave(); + + fixture.detectChanges(); + expect(updateTestResultStateSpy).toHaveBeenCalledTimes(1); + expect(updateTestResultStateSpy).toHaveBeenCalledWith(testRecord); + }); + }); + + describe('Render banner', () => { + beforeEach(() => { + jest + .spyOn(testRecordsService, 'testResult$', 'get') + .mockReturnValue(of({ vehicleType: 'psv', testTypes: [{ testTypeId: '1' }] } as TestResultModel)); + }); + + it('should render the banner if the test type id is not supported', waitForAsync(() => { + jest.spyOn(component, 'isTestTypeGroupEditable$', 'get').mockReturnValue(of(false)); + fixture.detectChanges(); + const banner = el.query(By.css('div.govuk-notification-banner')); + expect(banner).toBeTruthy(); + })); + + it('should not render the banner if the test type id is supported', fakeAsync(() => { + jest.spyOn(component, 'isTestTypeGroupEditable$', 'get').mockReturnValue(of(true)); + tick(); + fixture.detectChanges(); + const banner = el.query(By.css('div.govuk-notification-banner')); + expect(banner).toBeNull(); + })); + }); + + it('should set testMode to be view when has errors is false', async () => { + expect(component.testMode).toEqual(TestModeEnum.Edit); + + const errorsSpy = jest.spyOn(component, 'hasErrors').mockReturnValue(Promise.resolve(false)); + await component.handleReview(); + + expect(errorsSpy).toHaveBeenCalledTimes(1); + + expect(component.testMode).toEqual(TestModeEnum.View); + }); + + it('should not set testMode to be view when has errors is true', async () => { + expect(component.testMode).toEqual(TestModeEnum.Edit); + + const errorsSpy = jest.spyOn(component, 'hasErrors').mockReturnValue(Promise.resolve(true)); + await component.handleReview(); + + expect(errorsSpy).toHaveBeenCalledTimes(1); + + expect(component.testMode).toEqual(TestModeEnum.Edit); + }); + + it('should set testMode back to edit', () => { + component.testMode = TestModeEnum.View; + component.handleCancel(); + + expect(component.testMode).toEqual(TestModeEnum.Edit); + }); }); diff --git a/src/app/features/test-records/amend/views/test-record/test-record.component.ts b/src/app/features/test-records/amend/views/test-record/test-record.component.ts index a903c86903..17f21594a1 100644 --- a/src/app/features/test-records/amend/views/test-record/test-record.component.ts +++ b/src/app/features/test-records/amend/views/test-record/test-record.component.ts @@ -1,6 +1,4 @@ -import { - Component, OnDestroy, OnInit, ViewChild, -} from '@angular/core'; +import { Component, OnDestroy, OnInit, ViewChild } from '@angular/core'; import { FormGroup } from '@angular/forms'; import { ActivatedRoute, Router } from '@angular/router'; import { GlobalError } from '@core/components/global-error/global-error.interface'; @@ -15,162 +13,170 @@ import { RouterService } from '@services/router/router.service'; import { TestRecordsService } from '@services/test-records/test-records.service'; import { updateTestResultSuccess } from '@store/test-records'; import cloneDeep from 'lodash.clonedeep'; -import { - Observable, Subject, combineLatest, filter, firstValueFrom, map, of, switchMap, take, takeUntil, -} from 'rxjs'; +import { Observable, Subject, combineLatest, filter, firstValueFrom, map, of, switchMap, take, takeUntil } from 'rxjs'; import { BaseTestRecordComponent } from '../../../components/base-test-record/base-test-record.component'; @Component({ - selector: 'app-test-records', - templateUrl: './test-record.component.html', + selector: 'app-test-records', + templateUrl: './test-record.component.html', }) export class TestRecordComponent implements OnInit, OnDestroy { - @ViewChild(BaseTestRecordComponent) private baseTestRecordComponent?: BaseTestRecordComponent; - - private destroy$ = new Subject(); - - testResult$: Observable = of(undefined); - sectionTemplates$: Observable = of(undefined); - testMode = TestModeEnum.Edit; - - constructor( - private actions$: Actions, - private errorService: GlobalErrorService, - private route: ActivatedRoute, - private router: Router, - private routerService: RouterService, - private testRecordsService: TestRecordsService, - ) { - this.router.routeReuseStrategy.shouldReuseRoute = () => false; - } - - ngOnInit(): void { - this.testResult$ = this.testRecordsService.editingTestResult$.pipe( - switchMap((editingTestResult) => (editingTestResult ? of(editingTestResult) : this.testRecordsService.testResult$)), - ); - this.sectionTemplates$ = this.testRecordsService.sectionTemplates$; - - this.actions$.pipe(ofType(updateTestResultSuccess), takeUntil(this.destroy$)).subscribe(() => { - void this.router.navigate(['../..'], { relativeTo: this.route.parent }); - }); - - combineLatest([this.testResult$, this.routerService.getQueryParam$('testType'), this.testRecordsService.sectionTemplates$]) - .pipe( - take(1), - filter(([testResult]) => !!testResult), - ) - .subscribe(([testResult, testType, sectionTemplates]) => { - if (!sectionTemplates && testResult) { - testResult.reasonForCreation = ''; - this.testRecordsService.editingTestResult(testResult); - } - if (testType && testType !== testResult?.testTypes[0].testTypeId) { - this.testRecordsService.testTypeChange(testType); - } - }); - } - - ngOnDestroy(): void { - this.errorService.clearErrors(); - - this.destroy$.next(); - this.destroy$.complete(); - } - - public get Roles() { - return Roles; - } - - /** - * Merge all section form values into one testResult and trigger action to update testResult. - * @returns void - */ - async handleSave(): Promise { - if (await this.hasErrors()) { - return; - } - - this.testRecordsService.cleanTestResult(); - - const testResult = await firstValueFrom(this.testResult$); - const testResultClone = cloneDeep(testResult) as TestResultModel; - - this.testRecordsService.updateTestResult(testResultClone); - } - - async handleReview(): Promise { - if (await this.hasErrors()) { - return; - } - - this.testMode = TestModeEnum.View; - } - - async hasErrors(): Promise { - const errors: GlobalError[] = []; - const forms = []; - - if (this.baseTestRecordComponent) { - const { sections, defects } = this.baseTestRecordComponent; - if (sections) { - sections.forEach((section) => { - forms.push(section.form); - }); - } - - if (defects) { - forms.push(defects.form); - } - } - - // if all forms are not marked as dirty, return - if (!this.isAnyFormDirty(forms) && (await firstValueFrom(this.testRecordsService.isSameTestTypeId$))) { - return true; - } - - forms.forEach((form) => { - DynamicFormService.validate(form, errors); - }); - - if (errors.length > 0) { - this.errorService.setErrors(errors); - } - - if (this.isAnyFormInvalid(forms)) { - return true; - } - - return false; - } - - handleCancel() { - this.testMode = this.testMode === TestModeEnum.Cancel || this.testMode === TestModeEnum.View ? TestModeEnum.Edit : TestModeEnum.Cancel; - } - - handleConfirmCancel() { - void this.router.navigate(['../..'], { relativeTo: this.route.parent }); - } - - get isTestTypeGroupEditable$(): Observable { - return this.testRecordsService.isTestTypeGroupEditable$; - } - - handleNewTestResult(testResult: TestResultModel) { - this.testRecordsService.updateEditingTestResult(testResult); - } - - isAnyFormDirty(forms: Array) { - return forms.some((form) => form.dirty); - } - - isAnyFormInvalid(forms: Array) { - return forms.some((form) => form.invalid); - } - get testNumber$(): Observable { - return this.routerService.routeNestedParams$.pipe(map((params) => params['testNumber'])); - } - - public get TestModeEnum(): typeof TestModeEnum { - return TestModeEnum; - } + @ViewChild(BaseTestRecordComponent) private baseTestRecordComponent?: BaseTestRecordComponent; + + private destroy$ = new Subject(); + + testResult$: Observable = of(undefined); + sectionTemplates$: Observable = of(undefined); + testMode = TestModeEnum.Edit; + + constructor( + private actions$: Actions, + private errorService: GlobalErrorService, + private route: ActivatedRoute, + private router: Router, + private routerService: RouterService, + private testRecordsService: TestRecordsService + ) { + this.router.routeReuseStrategy.shouldReuseRoute = () => false; + } + + ngOnInit(): void { + this.testResult$ = this.testRecordsService.editingTestResult$.pipe( + switchMap((editingTestResult) => + editingTestResult ? of(editingTestResult) : this.testRecordsService.testResult$ + ) + ); + this.sectionTemplates$ = this.testRecordsService.sectionTemplates$; + + this.actions$.pipe(ofType(updateTestResultSuccess), takeUntil(this.destroy$)).subscribe(() => { + void this.router.navigate(['../..'], { relativeTo: this.route.parent }); + }); + + combineLatest([ + this.testResult$, + this.routerService.getQueryParam$('testType'), + this.testRecordsService.sectionTemplates$, + ]) + .pipe( + take(1), + filter(([testResult]) => !!testResult) + ) + .subscribe(([testResult, testType, sectionTemplates]) => { + if (!sectionTemplates && testResult) { + testResult.reasonForCreation = ''; + this.testRecordsService.editingTestResult(testResult); + } + if (testType && testType !== testResult?.testTypes[0].testTypeId) { + // @ts-ignore + this.testRecordsService.testTypeChange(testType); + } + }); + } + + ngOnDestroy(): void { + this.errorService.clearErrors(); + + this.destroy$.next(); + this.destroy$.complete(); + } + + public get Roles() { + return Roles; + } + + /** + * Merge all section form values into one testResult and trigger action to update testResult. + * @returns void + */ + async handleSave(): Promise { + if (await this.hasErrors()) { + return; + } + + this.testRecordsService.cleanTestResult(); + + const testResult = await firstValueFrom(this.testResult$); + const testResultClone = cloneDeep(testResult) as TestResultModel; + + this.testRecordsService.updateTestResult(testResultClone); + } + + async handleReview(): Promise { + if (await this.hasErrors()) { + return; + } + + this.testMode = TestModeEnum.View; + } + + async hasErrors(): Promise { + const errors: GlobalError[] = []; + const forms = []; + + if (this.baseTestRecordComponent) { + const { sections, defects } = this.baseTestRecordComponent; + if (sections) { + sections.forEach((section) => { + forms.push(section.form); + }); + } + + if (defects) { + forms.push(defects.form); + } + } + + // if all forms are not marked as dirty, return + if (!this.isAnyFormDirty(forms) && (await firstValueFrom(this.testRecordsService.isSameTestTypeId$))) { + return true; + } + + forms.forEach((form) => { + DynamicFormService.validate(form, errors); + }); + + if (errors.length > 0) { + this.errorService.setErrors(errors); + } + + if (this.isAnyFormInvalid(forms)) { + return true; + } + + return false; + } + + handleCancel() { + this.testMode = + this.testMode === TestModeEnum.Cancel || this.testMode === TestModeEnum.View + ? TestModeEnum.Edit + : TestModeEnum.Cancel; + } + + handleConfirmCancel() { + void this.router.navigate(['../..'], { relativeTo: this.route.parent }); + } + + get isTestTypeGroupEditable$(): Observable { + return this.testRecordsService.isTestTypeGroupEditable$; + } + + handleNewTestResult(testResult: TestResultModel) { + this.testRecordsService.updateEditingTestResult(testResult); + } + + isAnyFormDirty(forms: Array) { + return forms.some((form) => form.dirty); + } + + isAnyFormInvalid(forms: Array) { + return forms.some((form) => form.invalid); + } + get testNumber$(): Observable { + return this.routerService.routeNestedParams$.pipe(map((params) => params['testNumber'])); + } + + public get TestModeEnum(): typeof TestModeEnum { + return TestModeEnum; + } } diff --git a/src/app/features/test-records/amend/views/test-result-summary/test-result-summary.component.spec.ts b/src/app/features/test-records/amend/views/test-result-summary/test-result-summary.component.spec.ts index 794b080c22..bc78d8f21b 100644 --- a/src/app/features/test-records/amend/views/test-result-summary/test-result-summary.component.spec.ts +++ b/src/app/features/test-records/amend/views/test-result-summary/test-result-summary.component.spec.ts @@ -13,24 +13,29 @@ import { VehicleHeaderComponent } from '../../../components/vehicle-header/vehic import { TestResultSummaryComponent } from './test-result-summary.component'; describe('TestResultSummaryComponent', () => { - let component: TestResultSummaryComponent; - let fixture: ComponentFixture; + let component: TestResultSummaryComponent; + let fixture: ComponentFixture; - beforeEach(async () => { - await TestBed.configureTestingModule({ - declarations: [TestResultSummaryComponent, VehicleHeaderComponent, NumberPlateComponent, TagComponent], - imports: [RouterTestingModule, HttpClientTestingModule, TestResultsApiModule], - providers: [provideMockStore({ initialState: initialAppState }), TestRecordsService, TechnicalRecordService, RouterService], - }).compileComponents(); - }); + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [TestResultSummaryComponent, VehicleHeaderComponent, NumberPlateComponent, TagComponent], + imports: [RouterTestingModule, HttpClientTestingModule, TestResultsApiModule], + providers: [ + provideMockStore({ initialState: initialAppState }), + TestRecordsService, + TechnicalRecordService, + RouterService, + ], + }).compileComponents(); + }); - beforeEach(() => { - fixture = TestBed.createComponent(TestResultSummaryComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); + beforeEach(() => { + fixture = TestBed.createComponent(TestResultSummaryComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); }); diff --git a/src/app/features/test-records/amend/views/test-result-summary/test-result-summary.component.ts b/src/app/features/test-records/amend/views/test-result-summary/test-result-summary.component.ts index e739d32bf3..8c0dff8681 100644 --- a/src/app/features/test-records/amend/views/test-result-summary/test-result-summary.component.ts +++ b/src/app/features/test-records/amend/views/test-result-summary/test-result-summary.component.ts @@ -2,33 +2,33 @@ import { Component, OnInit } from '@angular/core'; import { FormNode } from '@forms/services/dynamic-form.types'; import { TestResultModel } from '@models/test-results/test-result.model'; import { TestRecordsService } from '@services/test-records/test-records.service'; -import { - Observable, of, skipWhile, switchMap, take, -} from 'rxjs'; +import { Observable, of, skipWhile, switchMap, take } from 'rxjs'; @Component({ - selector: 'app-test-result-summary', - templateUrl: './test-result-summary.component.html', - styleUrls: ['./test-result-summary.component.scss'], + selector: 'app-test-result-summary', + templateUrl: './test-result-summary.component.html', + styleUrls: ['./test-result-summary.component.scss'], }) export class TestResultSummaryComponent implements OnInit { - testResult$: Observable = of(undefined); - sectionTemplates$: Observable = of(undefined); + testResult$: Observable = of(undefined); + sectionTemplates$: Observable = of(undefined); - constructor(private testRecordsService: TestRecordsService) {} + constructor(private testRecordsService: TestRecordsService) {} - ngOnInit(): void { - this.testResult$ = this.testRecordsService.editingTestResult$.pipe( - switchMap((editingTestResult) => (editingTestResult ? of(editingTestResult) : this.testRecordsService.testResult$)), - ); + ngOnInit(): void { + this.testResult$ = this.testRecordsService.editingTestResult$.pipe( + switchMap((editingTestResult) => + editingTestResult ? of(editingTestResult) : this.testRecordsService.testResult$ + ) + ); - this.testResult$ - .pipe( - skipWhile((testResult) => !testResult), - take(1), - ) - .subscribe((testResult) => this.testRecordsService.editingTestResult(testResult as TestResultModel)); + this.testResult$ + .pipe( + skipWhile((testResult) => !testResult), + take(1) + ) + .subscribe((testResult) => this.testRecordsService.editingTestResult(testResult as TestResultModel)); - this.sectionTemplates$ = this.testRecordsService.sectionTemplates$; - } + this.sectionTemplates$ = this.testRecordsService.sectionTemplates$; + } } diff --git a/src/app/features/test-records/amend/views/test-router-outlet/test-router-outlet.component.spec.ts b/src/app/features/test-records/amend/views/test-router-outlet/test-router-outlet.component.spec.ts index 4da59bb9c3..d1d8bd0c1c 100644 --- a/src/app/features/test-records/amend/views/test-router-outlet/test-router-outlet.component.spec.ts +++ b/src/app/features/test-records/amend/views/test-router-outlet/test-router-outlet.component.spec.ts @@ -3,23 +3,23 @@ import { RouterTestingModule } from '@angular/router/testing'; import { TestRouterOutletComponent } from './test-router-outlet.component'; describe('TestRouterOutletComponent', () => { - let component: TestRouterOutletComponent; - let fixture: ComponentFixture; + let component: TestRouterOutletComponent; + let fixture: ComponentFixture; - beforeEach(async () => { - await TestBed.configureTestingModule({ - declarations: [TestRouterOutletComponent], - imports: [RouterTestingModule], - }).compileComponents(); - }); + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [TestRouterOutletComponent], + imports: [RouterTestingModule], + }).compileComponents(); + }); - beforeEach(() => { - fixture = TestBed.createComponent(TestRouterOutletComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); + beforeEach(() => { + fixture = TestBed.createComponent(TestRouterOutletComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); }); diff --git a/src/app/features/test-records/amend/views/test-router-outlet/test-router-outlet.component.ts b/src/app/features/test-records/amend/views/test-router-outlet/test-router-outlet.component.ts index 9427a72c5b..8ae25e17ef 100644 --- a/src/app/features/test-records/amend/views/test-router-outlet/test-router-outlet.component.ts +++ b/src/app/features/test-records/amend/views/test-router-outlet/test-router-outlet.component.ts @@ -1,8 +1,8 @@ import { ChangeDetectionStrategy, Component } from '@angular/core'; @Component({ - selector: 'app-test-router-outlet', - templateUrl: './test-router-outlet.component.html', - changeDetection: ChangeDetectionStrategy.OnPush, + selector: 'app-test-router-outlet', + templateUrl: './test-router-outlet.component.html', + changeDetection: ChangeDetectionStrategy.OnPush, }) export class TestRouterOutletComponent {} diff --git a/src/app/features/test-records/amend/views/test-type-select-wrapper/test-type-select-wrapper.component.spec.ts b/src/app/features/test-records/amend/views/test-type-select-wrapper/test-type-select-wrapper.component.spec.ts index 2ee5166276..600dc57755 100644 --- a/src/app/features/test-records/amend/views/test-type-select-wrapper/test-type-select-wrapper.component.spec.ts +++ b/src/app/features/test-records/amend/views/test-type-select-wrapper/test-type-select-wrapper.component.spec.ts @@ -1,51 +1,51 @@ +import { HttpClientTestingModule } from '@angular/common/http/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ActivatedRoute, Router } from '@angular/router'; import { RouterTestingModule } from '@angular/router/testing'; import { TestType } from '@api/test-types'; +import { provideMockStore } from '@ngrx/store/testing'; import { TestTypesService } from '@services/test-types/test-types.service'; -import { of } from 'rxjs'; -import { HttpClientTestingModule } from '@angular/common/http/testing'; import { State, initialAppState } from '@store/index'; -import { provideMockStore } from '@ngrx/store/testing'; +import { of } from 'rxjs'; import { TestTypeSelectComponent } from '../../../components/test-type-select/test-type-select.component'; import { TestTypeSelectWrapperComponent } from './test-type-select-wrapper.component'; describe('TestTypeSelectWrapperComponent', () => { - let component: TestTypeSelectWrapperComponent; - let fixture: ComponentFixture; - let router: Router; - let route: ActivatedRoute; + let component: TestTypeSelectWrapperComponent; + let fixture: ComponentFixture; + let router: Router; + let route: ActivatedRoute; - beforeEach(async () => { - await TestBed.configureTestingModule({ - declarations: [TestTypeSelectWrapperComponent, TestTypeSelectComponent], - imports: [RouterTestingModule, HttpClientTestingModule], - providers: [ - provideMockStore({ initialState: initialAppState }), - { provide: TestTypesService, useValue: { selectAllTestTypes$: of([]), testTypeIdChanged: () => {} } }, - ], - }).compileComponents(); - }); + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [TestTypeSelectWrapperComponent, TestTypeSelectComponent], + imports: [RouterTestingModule, HttpClientTestingModule], + providers: [ + provideMockStore({ initialState: initialAppState }), + { provide: TestTypesService, useValue: { selectAllTestTypes$: of([]), testTypeIdChanged: () => {} } }, + ], + }).compileComponents(); + }); - beforeEach(() => { - fixture = TestBed.createComponent(TestTypeSelectWrapperComponent); - component = fixture.componentInstance; - router = TestBed.inject(Router); - route = TestBed.inject(ActivatedRoute); - fixture.detectChanges(); - }); + beforeEach(() => { + fixture = TestBed.createComponent(TestTypeSelectWrapperComponent); + component = fixture.componentInstance; + router = TestBed.inject(Router); + route = TestBed.inject(ActivatedRoute); + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); - it('should navigate to sibling path "amend-test-details"', () => { - const navigateSpy = jest.spyOn(router, 'navigate').mockReturnValue(Promise.resolve(true)); - component.handleTestTypeSelection({ id: '1' } as TestType); - expect(navigateSpy).toHaveBeenCalledWith(['..', 'amend-test-details'], { - queryParams: { testType: '1' }, - queryParamsHandling: 'merge', - relativeTo: route, - }); - }); + it('should navigate to sibling path "amend-test-details"', () => { + const navigateSpy = jest.spyOn(router, 'navigate').mockReturnValue(Promise.resolve(true)); + component.handleTestTypeSelection({ id: '1' } as TestType); + expect(navigateSpy).toHaveBeenCalledWith(['..', 'amend-test-details'], { + queryParams: { testType: '1' }, + queryParamsHandling: 'merge', + relativeTo: route, + }); + }); }); diff --git a/src/app/features/test-records/amend/views/test-type-select-wrapper/test-type-select-wrapper.component.ts b/src/app/features/test-records/amend/views/test-type-select-wrapper/test-type-select-wrapper.component.ts index 36be430806..e7b6c5bcef 100644 --- a/src/app/features/test-records/amend/views/test-type-select-wrapper/test-type-select-wrapper.component.ts +++ b/src/app/features/test-records/amend/views/test-type-select-wrapper/test-type-select-wrapper.component.ts @@ -3,18 +3,21 @@ import { ActivatedRoute, Router } from '@angular/router'; import { TestType } from '@api/test-types'; @Component({ - selector: 'app-test-type-select-wrapper', - templateUrl: './test-type-select-wrapper.component.html', - changeDetection: ChangeDetectionStrategy.OnPush, + selector: 'app-test-type-select-wrapper', + templateUrl: './test-type-select-wrapper.component.html', + changeDetection: ChangeDetectionStrategy.OnPush, }) export class TestTypeSelectWrapperComponent { - constructor(private router: Router, private route: ActivatedRoute) {} + constructor( + private router: Router, + private route: ActivatedRoute + ) {} - handleTestTypeSelection(testType: TestType) { - void this.router.navigate(['..', 'amend-test-details'], { - queryParams: { testType: testType.id }, - queryParamsHandling: 'merge', - relativeTo: this.route, - }); - } + handleTestTypeSelection(testType: TestType) { + void this.router.navigate(['..', 'amend-test-details'], { + queryParams: { testType: testType.id }, + queryParamsHandling: 'merge', + relativeTo: this.route, + }); + } } diff --git a/src/app/features/test-records/components/base-test-record/base-test-record.component.spec.ts b/src/app/features/test-records/components/base-test-record/base-test-record.component.spec.ts index f148213333..f4b6bd5bff 100644 --- a/src/app/features/test-records/components/base-test-record/base-test-record.component.spec.ts +++ b/src/app/features/test-records/components/base-test-record/base-test-record.component.spec.ts @@ -2,7 +2,11 @@ import { HttpClientTestingModule } from '@angular/common/http/testing'; import { QueryList } from '@angular/core'; import { ComponentFixture, TestBed } from '@angular/core/testing'; import { RouterTestingModule } from '@angular/router/testing'; -import { DefaultService as CreateTestResultsService, GetTestResultsService, UpdateTestResultsService } from '@api/test-results'; +import { + DefaultService as CreateTestResultsService, + GetTestResultsService, + UpdateTestResultsService, +} from '@api/test-results'; import { GlobalErrorService } from '@core/components/global-error/global-error.service'; import { DynamicFormGroupComponent } from '@forms/components/dynamic-form-group/dynamic-form-group.component'; import { DynamicFormsModule } from '@forms/dynamic-forms.module'; @@ -24,93 +28,103 @@ import { VehicleHeaderComponent } from '../vehicle-header/vehicle-header.compone import { BaseTestRecordComponent } from './base-test-record.component'; describe('BaseTestRecordComponent', () => { - let component: BaseTestRecordComponent; - let fixture: ComponentFixture; + let component: BaseTestRecordComponent; + let fixture: ComponentFixture; - beforeEach(async () => { - await TestBed.configureTestingModule({ - declarations: [BaseTestRecordComponent, DefaultNullOrEmpty, VehicleHeaderComponent], - imports: [DynamicFormsModule, HttpClientTestingModule, SharedModule, RouterTestingModule], - providers: [ - RouterService, - GlobalErrorService, - provideMockStore({ initialState: initialAppState }), - TestTypesService, - TechnicalRecordService, - UpdateTestResultsService, - GetTestResultsService, - CreateTestResultsService, - { - provide: UserService, - useValue: { - roles$: of([Roles.TestResultCreateContingency, Roles.TestResultAmend]), - }, - }, - ], - }).compileComponents(); - }); + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [BaseTestRecordComponent, DefaultNullOrEmpty, VehicleHeaderComponent], + imports: [DynamicFormsModule, HttpClientTestingModule, SharedModule, RouterTestingModule], + providers: [ + RouterService, + GlobalErrorService, + provideMockStore({ initialState: initialAppState }), + TestTypesService, + TechnicalRecordService, + UpdateTestResultsService, + GetTestResultsService, + CreateTestResultsService, + { + provide: UserService, + useValue: { + roles$: of([Roles.TestResultCreateContingency, Roles.TestResultAmend]), + }, + }, + ], + }).compileComponents(); + }); - beforeEach(() => { - fixture = TestBed.createComponent(BaseTestRecordComponent); - component = fixture.componentInstance; - component.testResult = { vin: 'ABC002', testTypes: [{ testResult: resultOfTestEnum.fail }] } as TestResultModel; - jest.clearAllMocks(); - fixture.detectChanges(); - }); + beforeEach(() => { + fixture = TestBed.createComponent(BaseTestRecordComponent); + component = fixture.componentInstance; + component.testResult = { vin: 'ABC002', testTypes: [{ testResult: resultOfTestEnum.fail }] } as TestResultModel; + jest.clearAllMocks(); + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); - describe('BaseTestRecordComponent.prototype.handleFormChange.name', () => { - it('should emit the new test result', (done) => { - const event = { vin: 'ABC001' } as TestResultModel; - const expectedValue = { vin: 'ABC001' }; + describe('BaseTestRecordComponent.prototype.handleFormChange.name', () => { + it('should emit the new test result', (done) => { + const event = { vin: 'ABC001' } as TestResultModel; + const expectedValue = { vin: 'ABC001' }; - component.newTestResult.subscribe((testResult) => { - expect(testResult).toEqual(expectedValue); - done(); - }); + component.newTestResult.subscribe((testResult) => { + expect(testResult).toEqual(expectedValue); + done(); + }); - component.handleFormChange(event); - }); - }); + component.handleFormChange(event); + }); + }); - describe('validateEuVehicleCategory', () => { - it('should call the validate function of eu vehicle category', () => { - component.sections = [{ form: new CustomFormGroup({ name: 'vehicleSection', type: FormNodeTypes.GROUP, children: [] }, {}) }, - { - form: new CustomFormGroup({ - name: 'testSection', - type: FormNodeTypes.GROUP, - children: [], - }, {}), - }] as unknown as QueryList; + describe('validateEuVehicleCategory', () => { + it('should call the validate function of eu vehicle category', () => { + component.sections = [ + { form: new CustomFormGroup({ name: 'vehicleSection', type: FormNodeTypes.GROUP, children: [] }, {}) }, + { + form: new CustomFormGroup( + { + name: 'testSection', + type: FormNodeTypes.GROUP, + children: [], + }, + {} + ), + }, + ] as unknown as QueryList; - const spy = jest.spyOn(DynamicFormService, 'validateControl'); - spy.mockImplementation(() => undefined); + const spy = jest.spyOn(DynamicFormService, 'validateControl'); + spy.mockImplementation(() => undefined); - component.validateEuVehicleCategory('test'); + component.validateEuVehicleCategory('test'); - expect(spy).toHaveBeenCalledTimes(1); - }); + expect(spy).toHaveBeenCalledTimes(1); + }); - it('should not call the validate function of eu vehicle category', () => { - component.sections = [{ form: new CustomFormGroup({ name: 'anotherTestSection', type: FormNodeTypes.GROUP, children: [] }, {}) }, - { - form: new CustomFormGroup({ - name: 'testSection', - type: FormNodeTypes.GROUP, - children: [], - }, {}), - }] as unknown as QueryList; + it('should not call the validate function of eu vehicle category', () => { + component.sections = [ + { form: new CustomFormGroup({ name: 'anotherTestSection', type: FormNodeTypes.GROUP, children: [] }, {}) }, + { + form: new CustomFormGroup( + { + name: 'testSection', + type: FormNodeTypes.GROUP, + children: [], + }, + {} + ), + }, + ] as unknown as QueryList; - const spy = jest.spyOn(DynamicFormService, 'validateControl'); - spy.mockImplementation(() => undefined); + const spy = jest.spyOn(DynamicFormService, 'validateControl'); + spy.mockImplementation(() => undefined); - component.validateEuVehicleCategory('test'); + component.validateEuVehicleCategory('test'); - expect(spy).toHaveBeenCalledTimes(0); - }); - }); + expect(spy).toHaveBeenCalledTimes(0); + }); + }); }); diff --git a/src/app/features/test-records/components/base-test-record/base-test-record.component.ts b/src/app/features/test-records/components/base-test-record/base-test-record.component.ts index d267609e2b..54ff4e0cff 100644 --- a/src/app/features/test-records/components/base-test-record/base-test-record.component.ts +++ b/src/app/features/test-records/components/base-test-record/base-test-record.component.ts @@ -1,5 +1,13 @@ import { - AfterViewInit, Component, EventEmitter, Input, Output, QueryList, ViewChild, ViewChildren, + AfterViewInit, + Component, + EventEmitter, + Input, + Output, + QueryList, + ViewChild, + ViewChildren, + inject, } from '@angular/core'; import { GlobalError } from '@core/components/global-error/global-error.interface'; import { GlobalErrorService } from '@core/components/global-error/global-error.service'; @@ -14,104 +22,101 @@ import { Roles } from '@models/roles.enum'; import { TestResultStatus } from '@models/test-results/test-result-status.enum'; import { TestResultModel } from '@models/test-results/test-result.model'; import { resultOfTestEnum } from '@models/test-types/test-type.model'; -import { V3TechRecordModel, VehicleTypes } from '@models/vehicle-tech-record.model'; +import { VehicleTypes } from '@models/vehicle-tech-record.model'; import { Store } from '@ngrx/store'; import { RouterService } from '@services/router/router.service'; import { TestRecordsService } from '@services/test-records/test-records.service'; import { DefectsState, filteredDefects } from '@store/defects'; -import { selectTechRecord } from '@store/technical-records'; import merge from 'lodash.merge'; import { Observable, map } from 'rxjs'; @Component({ - selector: 'app-base-test-record[testResult]', - templateUrl: './base-test-record.component.html', - styleUrls: ['./base-test-record.component.scss'], + selector: 'app-base-test-record[testResult]', + templateUrl: './base-test-record.component.html', + styleUrls: ['./base-test-record.component.scss'], }) export class BaseTestRecordComponent implements AfterViewInit { - @ViewChildren(DynamicFormGroupComponent) sections?: QueryList; - @ViewChild(DefectsComponent) defects?: DefectsComponent; - @ViewChild(CustomDefectsComponent) customDefects?: CustomDefectsComponent; - @ViewChild(RequiredStandardsComponent) requiredStandards?: RequiredStandardsComponent; - - @Input() testResult!: TestResultModel; - @Input() isEditing = false; - @Input() expandSections = false; - @Input() isReview = false; - - @Output() newTestResult = new EventEmitter(); - - techRecord$: Observable; - constructor( - private defectsStore: Store, - private routerService: RouterService, - private testRecordsService: TestRecordsService, - private store: Store, - private globalErrorService: GlobalErrorService, - ) { - this.techRecord$ = this.store.select(selectTechRecord); - } - - ngAfterViewInit(): void { - this.handleFormChange({}); - } - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - handleFormChange(event: any) { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - let latestTest: any; - this.sections?.forEach((section) => { - const { form } = section; - latestTest = merge(latestTest, form.getCleanValue(form)); - }); - const defectsValue = this.defects?.form.getCleanValue(this.defects?.form); - const customDefectsValue = this.customDefects?.form.getCleanValue(this.customDefects?.form); - const requiredStandardsValue = this.requiredStandards?.form.getCleanValue(this.requiredStandards?.form); - - latestTest = merge(latestTest, defectsValue, customDefectsValue, requiredStandardsValue, event); - - if (latestTest && Object.keys(latestTest).length > 0) { - this.newTestResult.emit(latestTest as TestResultModel); - } - } - - // eslint-disable-next-line @typescript-eslint/no-unused-vars - validateEuVehicleCategory(_event: unknown) { - this.sections?.forEach((section) => { - const { form } = section; - if (form.meta.name === 'vehicleSection') { - const errors: GlobalError[] = []; - DynamicFormService.validateControl(form.get('euVehicleCategory') as CustomFormControl, errors); - this.globalErrorService.setErrors(errors); - } - }); - } - - getDefects$(type: VehicleTypes): Observable { - return this.defectsStore.select(filteredDefects(type)); - } - - get isTestTypeGroupEditable$(): Observable { - return this.testRecordsService.isTestTypeGroupEditable$; - } - - get roles(): typeof Roles { - return Roles; - } - - get statuses(): typeof TestResultStatus { - return TestResultStatus; - } - - get sectionTemplates$(): Observable { - return this.testRecordsService.sectionTemplates$; - } - - get resultOfTest(): resultOfTestEnum { - return this.testResult?.testTypes[0].testResult; - } - - get testNumber$(): Observable { - return this.routerService.routeNestedParams$.pipe(map((params) => params['testNumber'])); - } + @ViewChildren(DynamicFormGroupComponent) sections?: QueryList; + @ViewChild(DefectsComponent) defects?: DefectsComponent; + @ViewChild(CustomDefectsComponent) customDefects?: CustomDefectsComponent; + @ViewChild(RequiredStandardsComponent) requiredStandards?: RequiredStandardsComponent; + + @Input() testResult!: TestResultModel; + @Input() isEditing = false; + @Input() expandSections = false; + @Input() isReview = false; + + @Output() newTestResult = new EventEmitter(); + + private defectsStore = inject(Store); + private routerService = inject(RouterService); + private testRecordsService = inject(TestRecordsService); + private globalErrorService = inject(GlobalErrorService); + + ngAfterViewInit(): void { + this.handleFormChange({}); + } + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + handleFormChange(event: any) { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + let latestTest: any; + this.sections?.forEach((section) => { + const { form } = section; + latestTest = merge(latestTest, form.getCleanValue(form)); + }); + const defectsValue = this.defects?.form.getCleanValue(this.defects?.form); + const customDefectsValue = this.customDefects?.form.getCleanValue(this.customDefects?.form); + const requiredStandardsValue = this.requiredStandards?.form.getCleanValue(this.requiredStandards?.form); + + latestTest = merge(latestTest, defectsValue, customDefectsValue, requiredStandardsValue, event); + + if (this.shouldUpdateTest(latestTest)) { + this.newTestResult.emit(latestTest); + } + } + + shouldUpdateTest(latestTest: unknown): latestTest is TestResultModel { + return !!latestTest && Object.keys(latestTest).length > 0; + } + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + validateEuVehicleCategory(_event: unknown) { + this.sections?.forEach((section) => { + const { form } = section; + if (form.meta.name === 'vehicleSection') { + const errors: GlobalError[] = []; + DynamicFormService.validateControl(form.get('euVehicleCategory') as CustomFormControl, errors); + this.globalErrorService.setErrors(errors); + } + }); + } + + getDefects$(type: VehicleTypes): Observable { + return this.defectsStore.select(filteredDefects(type)); + } + + get isTestTypeGroupEditable$(): Observable { + return this.testRecordsService.isTestTypeGroupEditable$; + } + + get roles(): typeof Roles { + return Roles; + } + + get statuses(): typeof TestResultStatus { + return TestResultStatus; + } + + get sectionTemplates$(): Observable { + return this.testRecordsService.sectionTemplates$; + } + + get resultOfTest(): resultOfTestEnum { + return this.testResult?.testTypes[0].testResult; + } + + get testNumber$(): Observable { + return this.routerService.routeNestedParams$.pipe(map((params) => params['testNumber'])); + } } diff --git a/src/app/features/test-records/components/test-type-select/test-type-select.component.spec.ts b/src/app/features/test-records/components/test-type-select/test-type-select.component.spec.ts index 6046fb2cd8..240dea8a36 100644 --- a/src/app/features/test-records/components/test-type-select/test-type-select.component.spec.ts +++ b/src/app/features/test-records/components/test-type-select/test-type-select.component.spec.ts @@ -10,67 +10,67 @@ import { of } from 'rxjs'; import { TestTypeSelectComponent } from './test-type-select.component'; describe('TestTypeSelectComponent', () => { - let component: TestTypeSelectComponent; - let fixture: ComponentFixture; + let component: TestTypeSelectComponent; + let fixture: ComponentFixture; - beforeEach(async () => { - await TestBed.configureTestingModule({ - declarations: [TestTypeSelectComponent], - imports: [RouterTestingModule, HttpClientTestingModule], - providers: [ - DynamicFormService, - provideMockStore({ initialState: initialAppState }), - { provide: TestTypesService, useValue: { selectAllTestTypes$: of([]), testTypeIdChanged: () => {} } }, - ], - }).compileComponents(); - }); + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [TestTypeSelectComponent], + imports: [RouterTestingModule, HttpClientTestingModule], + providers: [ + DynamicFormService, + provideMockStore({ initialState: initialAppState }), + { provide: TestTypesService, useValue: { selectAllTestTypes$: of([]), testTypeIdChanged: () => {} } }, + ], + }).compileComponents(); + }); - beforeEach(() => { - fixture = TestBed.createComponent(TestTypeSelectComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); + beforeEach(() => { + fixture = TestBed.createComponent(TestTypeSelectComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); - it('should return testType id', () => { - expect(component.tackByFn(0, createMockTestTypeCategory({ id: '1' }))).toBe('1'); - }); + it('should return testType id', () => { + expect(component.tackByFn(0, createMockTestTypeCategory({ id: '1' }))).toBe('1'); + }); - it('should return true if prop nextTestTypesOrCategories exists', () => { - expect(component.hasNext(createMockTestTypeCategory({ nextTestTypesOrCategories: [] }))).toBeTruthy(); - expect(component.hasNext(createMockTestTypeCategory())).toBeFalsy(); - }); + it('should return true if prop nextTestTypesOrCategories exists', () => { + expect(component.hasNext(createMockTestTypeCategory({ nextTestTypesOrCategories: [] }))).toBeTruthy(); + expect(component.hasNext(createMockTestTypeCategory())).toBeFalsy(); + }); - it('should return true if category with given id exists in array', () => { - component.categories = new Array(3).fill(0).map((id) => createMockTestTypeCategory({ id: `${id + 1}` })); - expect(component.isSelected('1')).toBeTruthy(); - expect(component.isSelected('4')).toBeFalsy(); - }); + it('should return true if category with given id exists in array', () => { + component.categories = new Array(3).fill(0).map((id) => createMockTestTypeCategory({ id: `${id + 1}` })); + expect(component.isSelected('1')).toBeTruthy(); + expect(component.isSelected('4')).toBeFalsy(); + }); - describe('TestTypeSelectComponent.prototype.handleCategory.name', () => { - it('should emit selected testType through testTypeSelected', (done) => { - component.testTypeSelected.subscribe((val) => { - expect(val.id).toBe('1'); - done(); - }); - component.categories = new Array(3).fill(0).map((id) => createMockTestTypeCategory({ id: `${id + 1}` })); - component.handleCategory(createMockTestTypeCategory({ id: '1' }), 0); - }); + describe('TestTypeSelectComponent.prototype.handleCategory.name', () => { + it('should emit selected testType through testTypeSelected', (done) => { + component.testTypeSelected.subscribe((val) => { + expect(val.id).toBe('1'); + done(); + }); + component.categories = new Array(3).fill(0).map((id) => createMockTestTypeCategory({ id: `${id + 1}` })); + component.handleCategory(createMockTestTypeCategory({ id: '1' }), 0); + }); - it('should push a new category into categories', () => { - component.categories = new Array(2).fill(0).map((id) => createMockTestTypeCategory({ id: `${id + 1}` })); - component.handleCategory(createMockTestTypeCategory({ id: '3', nextTestTypesOrCategories: [] }), 2); - expect(component.categories).toHaveLength(3); - }); + it('should push a new category into categories', () => { + component.categories = new Array(2).fill(0).map((id) => createMockTestTypeCategory({ id: `${id + 1}` })); + component.handleCategory(createMockTestTypeCategory({ id: '3', nextTestTypesOrCategories: [] }), 2); + expect(component.categories).toHaveLength(3); + }); - it('should replace last category for given categories', () => { - component.categories = new Array(2).fill(0).map((id) => createMockTestTypeCategory({ id: `${id + 1}` })); - component.handleCategory(createMockTestTypeCategory({ id: '3', nextTestTypesOrCategories: [] }), 1); - expect(component.categories).toHaveLength(2); - expect(component.categories[1].id).toBe('3'); - }); - }); + it('should replace last category for given categories', () => { + component.categories = new Array(2).fill(0).map((id) => createMockTestTypeCategory({ id: `${id + 1}` })); + component.handleCategory(createMockTestTypeCategory({ id: '3', nextTestTypesOrCategories: [] }), 1); + expect(component.categories).toHaveLength(2); + expect(component.categories[1].id).toBe('3'); + }); + }); }); diff --git a/src/app/features/test-records/components/test-type-select/test-type-select.component.ts b/src/app/features/test-records/components/test-type-select/test-type-select.component.ts index 1b05a6d36e..5ac8fcb55c 100644 --- a/src/app/features/test-records/components/test-type-select/test-type-select.component.ts +++ b/src/app/features/test-records/components/test-type-select/test-type-select.component.ts @@ -1,6 +1,4 @@ -import { - Component, EventEmitter, OnInit, Output, -} from '@angular/core'; +import { Component, EventEmitter, OnInit, Output } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; import { TestType, TestTypeCategory, TestTypesTaxonomy } from '@api/test-types'; import { TechnicalRecordService } from '@services/technical-record/technical-record.service'; @@ -8,53 +6,52 @@ import { TestTypesService } from '@services/test-types/test-types.service'; import { Observable } from 'rxjs'; @Component({ - selector: 'app-test-type-select', - templateUrl: './test-type-select.component.html', - styleUrls: ['./test-type-select.component.scss'], + selector: 'app-test-type-select', + templateUrl: './test-type-select.component.html', + styleUrls: ['./test-type-select.component.scss'], }) export class TestTypeSelectComponent implements OnInit { - @Output() testTypeSelected = new EventEmitter(); - - categories: Array = []; - - constructor( - private testTypesService: TestTypesService, - private technicalRecordService: TechnicalRecordService, - private router: Router, - private route: ActivatedRoute, - ) {} - ngOnInit(): void { - - this.technicalRecordService.techRecordHistory$.subscribe((recordHistory) => { - if (!recordHistory) { - void this.router.navigate(['../../'], { relativeTo: this.route.parent }); - } - }); - } - - get selectAllTestTypes$(): Observable { - return this.testTypesService.selectAllTestTypes$; - } - - tackByFn(i: number, testType: TestType | TestTypeCategory) { - return testType.id; - } - - handleCategory(category: TestType | TestTypeCategory, i: number) { - this.categories.length = i; - - if (Object.prototype.hasOwnProperty.call(category, 'nextTestTypesOrCategories')) { - this.categories.push(category as TestTypeCategory); - } else { - this.testTypeSelected.emit(category); - } - } - - hasNext(category: TestType | TestTypeCategory) { - return Object.prototype.hasOwnProperty.call(category, 'nextTestTypesOrCategories'); - } - - isSelected(id: string) { - return this.categories.map((t) => t.id).includes(id); - } + @Output() testTypeSelected = new EventEmitter(); + + categories: Array = []; + + constructor( + private testTypesService: TestTypesService, + private technicalRecordService: TechnicalRecordService, + private router: Router, + private route: ActivatedRoute + ) {} + ngOnInit(): void { + this.technicalRecordService.techRecordHistory$.subscribe((recordHistory) => { + if (!recordHistory) { + void this.router.navigate(['../../'], { relativeTo: this.route.parent }); + } + }); + } + + get selectAllTestTypes$(): Observable { + return this.testTypesService.selectAllTestTypes$; + } + + tackByFn(i: number, testType: TestType | TestTypeCategory) { + return testType.id; + } + + handleCategory(category: TestType | TestTypeCategory, i: number) { + this.categories.length = i; + + if (Object.prototype.hasOwnProperty.call(category, 'nextTestTypesOrCategories')) { + this.categories.push(category as TestTypeCategory); + } else { + this.testTypeSelected.emit(category); + } + } + + hasNext(category: TestType | TestTypeCategory) { + return Object.prototype.hasOwnProperty.call(category, 'nextTestTypesOrCategories'); + } + + isSelected(id: string) { + return this.categories.map((t) => t.id).includes(id); + } } diff --git a/src/app/features/test-records/components/vehicle-header/vehicle-header.component.spec.ts b/src/app/features/test-records/components/vehicle-header/vehicle-header.component.spec.ts index 5fda510aa2..6332718574 100644 --- a/src/app/features/test-records/components/vehicle-header/vehicle-header.component.spec.ts +++ b/src/app/features/test-records/components/vehicle-header/vehicle-header.component.spec.ts @@ -1,99 +1,99 @@ import { HttpClientTestingModule } from '@angular/common/http/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { RouterTestingModule } from '@angular/router/testing'; import { TestTypesService } from '@api/test-types'; +import { V3TechRecordModel, VehicleConfigurations, VehicleTypes } from '@models/vehicle-tech-record.model'; import { provideMockStore } from '@ngrx/store/testing'; import { ResultOfTestService } from '@services/result-of-test/result-of-test.service'; +import { TechnicalRecordService } from '@services/technical-record/technical-record.service'; import { SharedModule } from '@shared/shared.module'; import { initialAppState } from '@store/.'; -import { VehicleTypes, VehicleConfigurations, V3TechRecordModel } from '@models/vehicle-tech-record.model'; -import { RouterTestingModule } from '@angular/router/testing'; -import { TechnicalRecordService } from '@services/technical-record/technical-record.service'; import { of } from 'rxjs'; import { VehicleHeaderComponent } from './vehicle-header.component'; const mockTechnicalRecordService = { - get techRecord$() { - return of({ systemNumber: 'foo', createdTimestamp: 'bar', vin: 'testVin' }); - }, + get techRecord$() { + return of({ systemNumber: 'foo', createdTimestamp: 'bar', vin: 'testVin' }); + }, }; describe('VehicleHeaderComponent', () => { - let component: VehicleHeaderComponent; - let fixture: ComponentFixture; + let component: VehicleHeaderComponent; + let fixture: ComponentFixture; - beforeEach(async () => { - await TestBed.configureTestingModule({ - declarations: [VehicleHeaderComponent], - imports: [SharedModule, HttpClientTestingModule, RouterTestingModule], - providers: [ - TestTypesService, - provideMockStore({ initialState: initialAppState }), - ResultOfTestService, - { provide: TechnicalRecordService, useValue: mockTechnicalRecordService }, - ], - }).compileComponents(); - }); + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [VehicleHeaderComponent], + imports: [SharedModule, HttpClientTestingModule, RouterTestingModule], + providers: [ + TestTypesService, + provideMockStore({ initialState: initialAppState }), + ResultOfTestService, + { provide: TechnicalRecordService, useValue: mockTechnicalRecordService }, + ], + }).compileComponents(); + }); - beforeEach(() => { - fixture = TestBed.createComponent(VehicleHeaderComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); + beforeEach(() => { + fixture = TestBed.createComponent(VehicleHeaderComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); - it('should combine the odometer reading', () => { - expect(component.combinedOdometerReading('1234', 'kilometres')).toBe('1234 km'); - }); + it('should combine the odometer reading', () => { + expect(component.combinedOdometerReading('1234', 'kilometres')).toBe('1234 km'); + }); - it('should display the unit if the reading is undefined', () => { - expect(component.combinedOdometerReading(undefined, 'kilometres')).toBe(' km'); - }); + it('should display the unit if the reading is undefined', () => { + expect(component.combinedOdometerReading(undefined, 'kilometres')).toBe(' km'); + }); - it('should display the reading if the unit is undefined', () => { - expect(component.combinedOdometerReading('1234', undefined)).toBe('1234 '); - }); + it('should display the reading if the unit is undefined', () => { + expect(component.combinedOdometerReading('1234', undefined)).toBe('1234 '); + }); - it('should display the correct data based on vehicle type', () => { - const mockRecord = { - techRecord_vehicleConfiguration: VehicleConfigurations.RIGID, - techRecord_bodyMake: 'testBody', - techRecord_bodyModel: 'testBodyModel', - techRecord_chassisMake: 'testChassis', - techRecord_chassisModel: 'testChassisModel', - techRecord_make: 'testHGV', - techRecord_model: 'testHGVModel', - } as unknown as V3TechRecordModel; + it('should display the correct data based on vehicle type', () => { + const mockRecord = { + techRecord_vehicleConfiguration: VehicleConfigurations.RIGID, + techRecord_bodyMake: 'testBody', + techRecord_bodyModel: 'testBodyModel', + techRecord_chassisMake: 'testChassis', + techRecord_chassisModel: 'testChassisModel', + techRecord_make: 'testHGV', + techRecord_model: 'testHGVModel', + } as unknown as V3TechRecordModel; - expect(component.getVehicleDescription(mockRecord, VehicleTypes.TRL)).toBe('rigid'); - expect(component.getVehicleDescription(mockRecord, VehicleTypes.PSV)).toBe('testBody-testBodyModel'); - expect(component.getVehicleDescription(mockRecord, VehicleTypes.HGV)).toBe('testHGV-testHGVModel'); - }); + expect(component.getVehicleDescription(mockRecord, VehicleTypes.TRL)).toBe('rigid'); + expect(component.getVehicleDescription(mockRecord, VehicleTypes.PSV)).toBe('testBody-testBodyModel'); + expect(component.getVehicleDescription(mockRecord, VehicleTypes.HGV)).toBe('testHGV-testHGVModel'); + }); - it('should display an empty string if all required data cannot be retrieved', () => { - const mockRecord = { - techRecord_bodyMake: '', - techRecord_bodyModel: 'testBodyModel', - techRecord_chassisMake: '', - techRecord_chassisModel: 'testChassisModel', - techRecord_make: '', - techRecord_model: 'testHGVModel', - } as unknown as V3TechRecordModel; + it('should display an empty string if all required data cannot be retrieved', () => { + const mockRecord = { + techRecord_bodyMake: '', + techRecord_bodyModel: 'testBodyModel', + techRecord_chassisMake: '', + techRecord_chassisModel: 'testChassisModel', + techRecord_make: '', + techRecord_model: 'testHGVModel', + } as unknown as V3TechRecordModel; - expect(component.getVehicleDescription(mockRecord, VehicleTypes.TRL)).toBeFalsy(); - expect(component.getVehicleDescription(mockRecord, VehicleTypes.PSV)).toBeFalsy(); - expect(component.getVehicleDescription(mockRecord, VehicleTypes.HGV)).toBeFalsy(); - }); + expect(component.getVehicleDescription(mockRecord, VehicleTypes.TRL)).toBeFalsy(); + expect(component.getVehicleDescription(mockRecord, VehicleTypes.PSV)).toBeFalsy(); + expect(component.getVehicleDescription(mockRecord, VehicleTypes.HGV)).toBeFalsy(); + }); - it('should display "Unknown Vehicle Type" if vehicle type is unknown/undefined', () => { - const mockRecord = { - techRecord_bodyMake: 'testBodyMake', - techRecord_bodyModel: 'testBodyModel', - techRecord_chassisMake: 'testChassisMake', - techRecord_chassisModel: 'testChassisModel', - } as unknown as V3TechRecordModel; - expect(component.getVehicleDescription(mockRecord, undefined)).toBe('Unknown Vehicle Type'); - }); + it('should display "Unknown Vehicle Type" if vehicle type is unknown/undefined', () => { + const mockRecord = { + techRecord_bodyMake: 'testBodyMake', + techRecord_bodyModel: 'testBodyModel', + techRecord_chassisMake: 'testChassisMake', + techRecord_chassisModel: 'testChassisModel', + } as unknown as V3TechRecordModel; + expect(component.getVehicleDescription(mockRecord, undefined)).toBe('Unknown Vehicle Type'); + }); }); diff --git a/src/app/features/test-records/components/vehicle-header/vehicle-header.component.ts b/src/app/features/test-records/components/vehicle-header/vehicle-header.component.ts index 1f4c447c39..56a16cd209 100644 --- a/src/app/features/test-records/components/vehicle-header/vehicle-header.component.ts +++ b/src/app/features/test-records/components/vehicle-header/vehicle-header.component.ts @@ -14,92 +14,98 @@ import { techRecord } from '@store/technical-records'; import { Observable } from 'rxjs'; @Component({ - selector: 'app-vehicle-header', - templateUrl: './vehicle-header.component.html', - styleUrls: ['./vehicle-header.component.scss'], - changeDetection: ChangeDetectionStrategy.OnPush, + selector: 'app-vehicle-header', + templateUrl: './vehicle-header.component.html', + styleUrls: ['./vehicle-header.component.scss'], + changeDetection: ChangeDetectionStrategy.OnPush, }) export class VehicleHeaderComponent { - @Input() isEditing = false; - @Input() testResult?: TestResultModel | null; - @Input() testNumber?: string | null; - @Input() isReview = false; + @Input() isEditing = false; + @Input() testResult?: TestResultModel | null; + @Input() testNumber?: string | null; + @Input() isReview = false; - constructor(private testTypesService: TestTypesService, private techRecordService: TechnicalRecordService, private store: Store) {} + constructor( + private testTypesService: TestTypesService, + private techRecordService: TechnicalRecordService, + private store: Store + ) {} - get test(): TestType | undefined { - return this.testResult?.testTypes?.find((t) => this.testNumber === t.testNumber); - } + get test(): TestType | undefined { + return this.testResult?.testTypes?.find((t) => this.testNumber === t.testNumber); + } - get selectAllTestTypes$(): Observable { - return this.testTypesService.selectAllTestTypes$; - } + get selectAllTestTypes$(): Observable { + return this.testTypesService.selectAllTestTypes$; + } - combinedOdometerReading(reading: string | undefined, unit: string | undefined) { - return `${reading ?? ''} ${(unit && (unit === 'kilometres' ? 'km' : 'mi')) ?? ''}`; - } + combinedOdometerReading(reading: string | undefined, unit: string | undefined) { + return `${reading ?? ''} ${(unit && (unit === 'kilometres' ? 'km' : 'mi')) ?? ''}`; + } - get techRecord$(): Observable { - return this.store.select(techRecord); - } + get techRecord$(): Observable { + return this.store.select(techRecord); + } - get vehicleTypes() { - return VehicleTypes; - } + get vehicleTypes() { + return VehicleTypes; + } - get referenceDataType() { - return ReferenceDataResourceType; - } + get referenceDataType() { + return ReferenceDataResourceType; + } - get resultOfTest(): string | undefined { - return this.testResult?.testStatus === TestResultStatus.CANCELLED ? TestResultStatus.CANCELLED : this.testResult?.testTypes[0].testResult; - } + get resultOfTest(): string | undefined { + return this.testResult?.testStatus === TestResultStatus.CANCELLED + ? TestResultStatus.CANCELLED + : this.testResult?.testTypes[0].testResult; + } - get tagType(): TagTypes { - switch (this.resultOfTest) { - case resultOfTestEnum.pass: - return TagType.GREEN; - case resultOfTestEnum.prs: - return TagType.BLUE; - case resultOfTestEnum.fail: - return TagType.RED; - case TestResultStatus.CANCELLED: - return TagType.YELLOW; - default: - return TagType.ORANGE; - } - } + get tagType(): TagTypes { + switch (this.resultOfTest) { + case resultOfTestEnum.pass: + return TagType.GREEN; + case resultOfTestEnum.prs: + return TagType.BLUE; + case resultOfTestEnum.fail: + return TagType.RED; + case TestResultStatus.CANCELLED: + return TagType.YELLOW; + default: + return TagType.ORANGE; + } + } - get testCode(): string | undefined { - const testCode = this.testResult?.testTypes[0].testCode; - return testCode ? `(${testCode})` : ''; - } + get testCode(): string | undefined { + const testCode = this.testResult?.testTypes[0].testCode; + return testCode ? `(${testCode})` : ''; + } - // eslint-disable-next-line @typescript-eslint/no-shadow - getVehicleDescription(techRecord: V3TechRecordModel, vehicleType: VehicleTypes | undefined) { - switch (vehicleType) { - case VehicleTypes.TRL: - return (techRecord as TechRecordType).techRecord_vehicleConfiguration ?? ''; - case VehicleTypes.PSV: - return (techRecord as TechRecordType).techRecord_bodyMake - && (techRecord as TechRecordType).techRecord_bodyModel - ? `${(techRecord as TechRecordType).techRecord_bodyMake ?? ''}-${ - (techRecord as TechRecordType).techRecord_bodyModel ?? '' - }` - : ''; - case VehicleTypes.HGV: - return (techRecord as TechRecordType).techRecord_make - && (techRecord as TechRecordType).techRecord_model - ? `${(techRecord as TechRecordType).techRecord_make ?? ''}-${ - (techRecord as TechRecordType).techRecord_model ?? '' - }` - : ''; - case VehicleTypes.MOTORCYCLE: - case VehicleTypes.LGV: - case VehicleTypes.CAR: - return ''; - default: - return 'Unknown Vehicle Type'; - } - } + // eslint-disable-next-line @typescript-eslint/no-shadow + getVehicleDescription(techRecord: V3TechRecordModel, vehicleType: VehicleTypes | undefined) { + switch (vehicleType) { + case VehicleTypes.TRL: + return (techRecord as TechRecordType).techRecord_vehicleConfiguration ?? ''; + case VehicleTypes.PSV: + return (techRecord as TechRecordType).techRecord_bodyMake && + (techRecord as TechRecordType).techRecord_bodyModel + ? `${(techRecord as TechRecordType).techRecord_bodyMake ?? ''}-${ + (techRecord as TechRecordType).techRecord_bodyModel ?? '' + }` + : ''; + case VehicleTypes.HGV: + return (techRecord as TechRecordType).techRecord_make && + (techRecord as TechRecordType).techRecord_model + ? `${(techRecord as TechRecordType).techRecord_make ?? ''}-${ + (techRecord as TechRecordType).techRecord_model ?? '' + }` + : ''; + case VehicleTypes.MOTORCYCLE: + case VehicleTypes.LGV: + case VehicleTypes.CAR: + return ''; + default: + return 'Unknown Vehicle Type'; + } + } } diff --git a/src/app/features/test-records/create/create-test-records-routing.module.ts b/src/app/features/test-records/create/create-test-records-routing.module.ts index f3687d34ee..791467732d 100644 --- a/src/app/features/test-records/create/create-test-records-routing.module.ts +++ b/src/app/features/test-records/create/create-test-records-routing.module.ts @@ -6,99 +6,103 @@ import { DefectComponent } from '@forms/custom-sections/defect/defect.component' import { RequiredStandardComponent } from '@forms/custom-sections/required-standard/required-standard.component'; import { RoleGuard } from '@guards/role-guard/roles.guard'; import { Roles } from '@models/roles.enum'; +import { TestRecordCreateRoutes } from '@models/routes.enum'; import { contingencyTestResolver } from 'src/app/resolvers/contingency-test/contingency-test.resolver'; import { defectsTaxonomyResolver } from 'src/app/resolvers/defects-taxonomy/defects-taxonomy.resolver'; import { requiredStandardsResolver } from 'src/app/resolvers/required-standards/required-standards.resolver'; import { testStationsResolver } from 'src/app/resolvers/test-stations/test-stations.resolver'; import { testTypeTaxonomyResolver } from 'src/app/resolvers/test-type-taxonomy/test-type-taxonomy.resolver'; -import { TestRecordCreateRoutes } from '@models/routes.enum'; import { CreateTestRecordComponent } from './views/create-test-record/create-test-record.component'; import { CreateTestTypeComponent } from './views/create-test-type/create-test-type.component'; import { TestRouterOutletComponent } from './views/test-router-outlet/test-router-outlet.component'; const routes: Routes = [ - { - path: '', - component: TestRouterOutletComponent, - resolve: { contingencyTest: contingencyTestResolver }, - children: [ - { - path: '', - redirectTo: 'type', - }, - { - path: TestRecordCreateRoutes.TYPE, - component: CreateTestTypeComponent, - resolve: { testTypeTaxonomy: testTypeTaxonomyResolver, contingencyTest: contingencyTestResolver }, - }, - { - path: TestRecordCreateRoutes.TEST_DETAILS, - component: TestRouterOutletComponent, - resolve: { TestTypeTaxonomy: testTypeTaxonomyResolver, defectTaxonomy: defectsTaxonomyResolver, testStations: testStationsResolver }, - data: { title: 'Test details', roles: Roles.TestResultCreateContingency, breadcrumbPreserveQueryParams: true }, - canActivate: [RoleGuard], - children: [ - { - path: '', - component: CreateTestRecordComponent, - }, - { - path: TestRecordCreateRoutes.DEFECT, - component: DefectComponent, - data: { title: 'Defect', roles: Roles.TestResultCreateContingency, isEditing: true }, - canActivate: [RoleGuard], - }, - { - path: TestRecordCreateRoutes.SELECT_DEFECT, - component: TestRouterOutletComponent, - data: { title: 'Select defect', roles: Roles.TestResultCreateContingency }, - children: [ - { - path: '', - component: DefectSelectComponent, - canActivate: [RoleGuard], - }, - { - path: TestRecordCreateRoutes.SELECT_DEFECT_REF, - component: DefectComponent, - data: { title: 'Defect', roles: Roles.TestResultCreateContingency, isEditing: true }, - canActivate: [RoleGuard], - }, - ], - }, - { - path: TestRecordCreateRoutes.REQUIRED_STANDARD, - component: RequiredStandardComponent, - data: { title: 'Required Standard', roles: Roles.TestResultCreateContingency, isEditing: true }, - canActivate: [RoleGuard], - }, - { - path: TestRecordCreateRoutes.SELECT_REQUIRED_STANDARD, - component: TestRouterOutletComponent, - resolve: { RequiredStandards: requiredStandardsResolver }, - data: { title: 'Select Required Standard', roles: Roles.TestResultCreateContingency }, - children: [ - { - path: '', - component: RequiredStandardSelectComponent, - canActivate: [RoleGuard], - }, - { - path: TestRecordCreateRoutes.REQUIRED_STANDARD_REF, - component: RequiredStandardComponent, - data: { title: 'Required Standard', roles: Roles.TestResultCreateContingency, isEditing: true }, - canActivate: [RoleGuard], - }, - ], - }, - ], - }, - ], - }, + { + path: '', + component: TestRouterOutletComponent, + resolve: { contingencyTest: contingencyTestResolver }, + children: [ + { + path: '', + redirectTo: 'type', + }, + { + path: TestRecordCreateRoutes.TYPE, + component: CreateTestTypeComponent, + resolve: { testTypeTaxonomy: testTypeTaxonomyResolver, contingencyTest: contingencyTestResolver }, + }, + { + path: TestRecordCreateRoutes.TEST_DETAILS, + component: TestRouterOutletComponent, + resolve: { + TestTypeTaxonomy: testTypeTaxonomyResolver, + defectTaxonomy: defectsTaxonomyResolver, + testStations: testStationsResolver, + }, + data: { title: 'Test details', roles: Roles.TestResultCreateContingency, breadcrumbPreserveQueryParams: true }, + canActivate: [RoleGuard], + children: [ + { + path: '', + component: CreateTestRecordComponent, + }, + { + path: TestRecordCreateRoutes.DEFECT, + component: DefectComponent, + data: { title: 'Defect', roles: Roles.TestResultCreateContingency, isEditing: true }, + canActivate: [RoleGuard], + }, + { + path: TestRecordCreateRoutes.SELECT_DEFECT, + component: TestRouterOutletComponent, + data: { title: 'Select defect', roles: Roles.TestResultCreateContingency }, + children: [ + { + path: '', + component: DefectSelectComponent, + canActivate: [RoleGuard], + }, + { + path: TestRecordCreateRoutes.SELECT_DEFECT_REF, + component: DefectComponent, + data: { title: 'Defect', roles: Roles.TestResultCreateContingency, isEditing: true }, + canActivate: [RoleGuard], + }, + ], + }, + { + path: TestRecordCreateRoutes.REQUIRED_STANDARD, + component: RequiredStandardComponent, + data: { title: 'Required Standard', roles: Roles.TestResultCreateContingency, isEditing: true }, + canActivate: [RoleGuard], + }, + { + path: TestRecordCreateRoutes.SELECT_REQUIRED_STANDARD, + component: TestRouterOutletComponent, + resolve: { RequiredStandards: requiredStandardsResolver }, + data: { title: 'Select Required Standard', roles: Roles.TestResultCreateContingency }, + children: [ + { + path: '', + component: RequiredStandardSelectComponent, + canActivate: [RoleGuard], + }, + { + path: TestRecordCreateRoutes.REQUIRED_STANDARD_REF, + component: RequiredStandardComponent, + data: { title: 'Required Standard', roles: Roles.TestResultCreateContingency, isEditing: true }, + canActivate: [RoleGuard], + }, + ], + }, + ], + }, + ], + }, ]; @NgModule({ - imports: [RouterModule.forChild(routes)], - exports: [RouterModule], + imports: [RouterModule.forChild(routes)], + exports: [RouterModule], }) export class CreateTestRecordsRoutingModule {} diff --git a/src/app/features/test-records/create/create-test-records.module.ts b/src/app/features/test-records/create/create-test-records.module.ts index ec12acd0c7..e196a31c94 100644 --- a/src/app/features/test-records/create/create-test-records.module.ts +++ b/src/app/features/test-records/create/create-test-records.module.ts @@ -10,7 +10,15 @@ import { CreateTestTypeComponent } from './views/create-test-type/create-test-ty import { TestRouterOutletComponent } from './views/test-router-outlet/test-router-outlet.component'; @NgModule({ - declarations: [CreateTestTypeComponent, CreateTestRecordComponent, TestRouterOutletComponent], - imports: [CommonModule, CreateTestRecordsRoutingModule, DynamicFormsModule, SharedModule, FormsModule, ReactiveFormsModule, TestRecordsModule], + declarations: [CreateTestTypeComponent, CreateTestRecordComponent, TestRouterOutletComponent], + imports: [ + CommonModule, + CreateTestRecordsRoutingModule, + DynamicFormsModule, + SharedModule, + FormsModule, + ReactiveFormsModule, + TestRecordsModule, + ], }) export class CreateTestRecordsModule {} diff --git a/src/app/features/test-records/create/views/create-test-record/create-test-record.component.spec.ts b/src/app/features/test-records/create/views/create-test-record/create-test-record.component.spec.ts index 5097ab41d1..027368c626 100644 --- a/src/app/features/test-records/create/views/create-test-record/create-test-record.component.spec.ts +++ b/src/app/features/test-records/create/views/create-test-record/create-test-record.component.spec.ts @@ -1,10 +1,12 @@ import { HttpClientTestingModule } from '@angular/common/http/testing'; -import { - ComponentFixture, fakeAsync, flush, TestBed, tick, -} from '@angular/core/testing'; +import { ComponentFixture, TestBed, fakeAsync, flush, tick } from '@angular/core/testing'; import { Router } from '@angular/router'; import { RouterTestingModule } from '@angular/router/testing'; -import { DefaultService as CreateTestResultsService, GetTestResultsService, UpdateTestResultsService } from '@api/test-results'; +import { + DefaultService as CreateTestResultsService, + GetTestResultsService, + UpdateTestResultsService, +} from '@api/test-results'; import { GlobalErrorService } from '@core/components/global-error/global-error.service'; import { RoleRequiredDirective } from '@directives/app-role-required.directive'; import { AbandonDialogComponent } from '@forms/custom-sections/abandon-dialog/abandon-dialog.component'; @@ -30,200 +32,202 @@ import { NumberPlateComponent } from '@shared/components/number-plate/number-pla import { DefaultNullOrEmpty } from '@shared/pipes/default-null-or-empty/default-null-or-empty.pipe'; import { TestTypeNamePipe } from '@shared/pipes/test-type-name/test-type-name.pipe'; import { SharedModule } from '@shared/shared.module'; -import { initialAppState, State } from '@store/.'; -import { - sectionTemplates, testResultInEdit, toEditOrNotToEdit, -} from '@store/test-records'; -import { Observable, of, ReplaySubject } from 'rxjs'; +import { State, initialAppState } from '@store/.'; +import { sectionTemplates, testResultInEdit, toEditOrNotToEdit } from '@store/test-records'; +import { Observable, ReplaySubject, of } from 'rxjs'; import { BaseTestRecordComponent } from '../../../components/base-test-record/base-test-record.component'; import { VehicleHeaderComponent } from '../../../components/vehicle-header/vehicle-header.component'; import { CreateTestRecordComponent } from './create-test-record.component'; describe('CreateTestRecordComponent', () => { - let component: CreateTestRecordComponent; - let fixture: ComponentFixture; - const actions$ = new ReplaySubject(); - let router: Router; - let testRecordsService: TestRecordsService; - let store: MockStore; - - const mockTechnicalRecordService = { - get viewableTechRecord$() { - return { systemNumber: 'foo', createdTimestamp: 'bar', vin: 'testVin' }; - }, - }; - const MockUserService = { - getUserName$: jest.fn().mockReturnValue(new Observable()), - roles$: of([Roles.TestResultAmend, Roles.TestResultView]), - }; - - beforeEach(async () => { - await TestBed.configureTestingModule({ - declarations: [ - CreateTestRecordComponent, - BaseTestRecordComponent, - DefaultNullOrEmpty, - TestTypeNamePipe, - ButtonComponent, - ButtonGroupComponent, - IconComponent, - NumberPlateComponent, - VehicleHeaderComponent, - RoleRequiredDirective, - ], - imports: [DynamicFormsModule, HttpClientTestingModule, RouterTestingModule, SharedModule], - providers: [ - GlobalErrorService, - RouterService, - TestRecordsService, - GetTestResultsService, - UpdateTestResultsService, - CreateTestResultsService, - { provide: UserService, useValue: MockUserService }, - provideMockStore({ initialState: initialAppState }), - provideMockActions(() => actions$), - { provide: TechnicalRecordService, useValue: mockTechnicalRecordService }, - DynamicFormService, - ], - }).compileComponents(); - }); - - beforeEach(() => { - fixture = TestBed.createComponent(CreateTestRecordComponent); - component = fixture.componentInstance; - router = TestBed.inject(Router); - testRecordsService = TestBed.inject(TestRecordsService); - store = TestBed.inject(MockStore); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); - - it('should navigate back to the tech record', () => { - const navigateSpy = jest.spyOn(router, 'navigate').mockImplementation(() => Promise.resolve(true)); - component.backToTechRecord(); - expect(navigateSpy).toHaveBeenCalled(); - }); - - it('should call createTestResult with value of all forms merged into one', async () => { - fixture.detectChanges(); - const createTestResultSpy = jest.spyOn(testRecordsService, 'createTestResult').mockImplementation(() => {}); - const testRecord = { testResultId: '1', testTypes: [{ testTypeId: '2' }] } as TestResultModel; - store.overrideSelector(testResultInEdit, testRecord); - store.overrideSelector(sectionTemplates, Object.values(contingencyTestTemplates.psv['testTypesGroup1'] ?? {})); - - component.isAnyFormInvalid = jest.fn().mockReturnValue(false); - - await component.handleSave(); - fixture.detectChanges(); - expect(createTestResultSpy).toHaveBeenCalledTimes(1); - expect(createTestResultSpy).toHaveBeenCalledWith(testRecord); - }); - - it('should not call createTestResult if some forms are invalid', async () => { - const createTestResultSpy = jest.spyOn(testRecordsService, 'createTestResult').mockImplementation(() => {}); - const testRecord = { testResultId: '1', testTypes: [{ testTypeId: '2' }] } as TestResultModel; - store.overrideSelector(testResultInEdit, testRecord); - store.overrideSelector(sectionTemplates, Object.values(contingencyTestTemplates.psv['testTypesGroup1'] ?? '')); - - fixture.detectChanges(); - component.isAnyFormInvalid = jest.fn().mockReturnValue(true); - - await component.handleSave(); - - expect(createTestResultSpy).not.toHaveBeenCalled(); - }); - - it('should dispatch the action to update the test result in edit', () => { - const updateTestResultSpy = jest.spyOn(testRecordsService, 'updateEditingTestResult').mockImplementation(() => {}); - component.handleNewTestResult({} as TestResultModel); - expect(updateTestResultSpy).toHaveBeenCalled(); - }); - - describe('CreateTestRecordComponent.prototype.isAnyFormInvalid.name', () => { - beforeEach(() => { - store.overrideSelector(testResultInEdit, mockTestResult()); - store.overrideSelector(toEditOrNotToEdit, undefined); - }); - - afterEach(() => { - store.resetSelectors(); - }); - - it('should return true if some forms are invalid', () => { - component.abandonDialog = { dynamicFormGroup: { form: { controls: { errors: 'foo' }, invalid: true } } } as unknown as AbandonDialogComponent; - component.testMode = TestModeEnum.Abandon; - DynamicFormService.validate = jest.fn(); - expect(component.isAnyFormInvalid()).toBe(true); - }); - - it('should return false if no forms are invalid', fakeAsync(() => { - tick(); - fixture.detectChanges(); - expect(component.isAnyFormInvalid()).toBe(false); - flush(); - })); - }); - - describe('CreateTestRecordComponent.prototype.abandon.name', () => { - it('should set testMode to be abandon', () => { - component.abandon(); - expect(component.testMode).toEqual(TestModeEnum.Abandon); - }); - }); - - describe('CreateTestRecordComponent.prototype.handleAbandonAction.name', () => { - it('should call handle save', async () => { - const handleSaveSpy = jest.spyOn(component, 'handleSave'); - - await component.handleAbandonAction('yes'); - - expect(handleSaveSpy).toHaveBeenCalledTimes(1); - }); - - it('should set testMode to be edit', async () => { - component.testMode = TestModeEnum.Abandon; - - await component.handleAbandonAction('no'); - - expect(component.testMode).toEqual(TestModeEnum.Edit); - }); - }); - - it('should combine forms', async () => { - component['baseTestRecordComponent'] = { - sections: { forEach: jest.fn().mockReturnValue([{ foo: 'foo' }]) }, - } as unknown as BaseTestRecordComponent; - - // eslint-disable-next-line @typescript-eslint/no-misused-promises - const createTestResultSpy = jest.spyOn(testRecordsService, 'createTestResult').mockImplementation(() => Promise.resolve(true)); - const testRecord = { testResultId: '1', testTypes: [{ testTypeId: '2' }] } as TestResultModel; - store.overrideSelector(testResultInEdit, testRecord); - store.overrideSelector(sectionTemplates, Object.values(contingencyTestTemplates.psv['testTypesGroup1'] ?? '')); - - fixture.detectChanges(); - - await component.handleSave(); - - fixture.detectChanges(); - expect(createTestResultSpy).toHaveBeenCalledTimes(1); - expect(createTestResultSpy).toHaveBeenCalledWith(testRecord); - }); - - it('should set testMode to be view', async () => { - component.techRecord = {} as V3TechRecordModel; - component.isAnyFormInvalid = jest.fn().mockReturnValue(false); - await component.handleReview(); - - expect(component.testMode).toEqual(TestModeEnum.View); - }); - - it('should set testMode back to edit', async () => { - component.isAnyFormInvalid = jest.fn().mockReturnValue(false); - await component.handleReview(); - component.handleCancel(); - - expect(component.testMode).toEqual(TestModeEnum.Edit); - }); + let component: CreateTestRecordComponent; + let fixture: ComponentFixture; + const actions$ = new ReplaySubject(); + let router: Router; + let testRecordsService: TestRecordsService; + let store: MockStore; + + const mockTechnicalRecordService = { + get viewableTechRecord$() { + return { systemNumber: 'foo', createdTimestamp: 'bar', vin: 'testVin' }; + }, + }; + const MockUserService = { + getUserName$: jest.fn().mockReturnValue(new Observable()), + roles$: of([Roles.TestResultAmend, Roles.TestResultView]), + }; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ + CreateTestRecordComponent, + BaseTestRecordComponent, + DefaultNullOrEmpty, + TestTypeNamePipe, + ButtonComponent, + ButtonGroupComponent, + IconComponent, + NumberPlateComponent, + VehicleHeaderComponent, + RoleRequiredDirective, + ], + imports: [DynamicFormsModule, HttpClientTestingModule, RouterTestingModule, SharedModule], + providers: [ + GlobalErrorService, + RouterService, + TestRecordsService, + GetTestResultsService, + UpdateTestResultsService, + CreateTestResultsService, + { provide: UserService, useValue: MockUserService }, + provideMockStore({ initialState: initialAppState }), + provideMockActions(() => actions$), + { provide: TechnicalRecordService, useValue: mockTechnicalRecordService }, + DynamicFormService, + ], + }).compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(CreateTestRecordComponent); + component = fixture.componentInstance; + router = TestBed.inject(Router); + testRecordsService = TestBed.inject(TestRecordsService); + store = TestBed.inject(MockStore); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + it('should navigate back to the tech record', () => { + const navigateSpy = jest.spyOn(router, 'navigate').mockImplementation(() => Promise.resolve(true)); + component.backToTechRecord(); + expect(navigateSpy).toHaveBeenCalled(); + }); + + it('should call createTestResult with value of all forms merged into one', async () => { + fixture.detectChanges(); + const createTestResultSpy = jest.spyOn(testRecordsService, 'createTestResult').mockImplementation(() => {}); + const testRecord = { testResultId: '1', testTypes: [{ testTypeId: '2' }] } as TestResultModel; + store.overrideSelector(testResultInEdit, testRecord); + store.overrideSelector(sectionTemplates, Object.values(contingencyTestTemplates.psv['testTypesGroup1'] ?? {})); + + component.isAnyFormInvalid = jest.fn().mockReturnValue(false); + + await component.handleSave(); + fixture.detectChanges(); + expect(createTestResultSpy).toHaveBeenCalledTimes(1); + expect(createTestResultSpy).toHaveBeenCalledWith(testRecord); + }); + + it('should not call createTestResult if some forms are invalid', async () => { + const createTestResultSpy = jest.spyOn(testRecordsService, 'createTestResult').mockImplementation(() => {}); + const testRecord = { testResultId: '1', testTypes: [{ testTypeId: '2' }] } as TestResultModel; + store.overrideSelector(testResultInEdit, testRecord); + store.overrideSelector(sectionTemplates, Object.values(contingencyTestTemplates.psv['testTypesGroup1'] ?? '')); + + fixture.detectChanges(); + component.isAnyFormInvalid = jest.fn().mockReturnValue(true); + + await component.handleSave(); + + expect(createTestResultSpy).not.toHaveBeenCalled(); + }); + + it('should dispatch the action to update the test result in edit', () => { + const updateTestResultSpy = jest.spyOn(testRecordsService, 'updateEditingTestResult').mockImplementation(() => {}); + component.handleNewTestResult({} as TestResultModel); + expect(updateTestResultSpy).toHaveBeenCalled(); + }); + + describe('CreateTestRecordComponent.prototype.isAnyFormInvalid.name', () => { + beforeEach(() => { + store.overrideSelector(testResultInEdit, mockTestResult()); + store.overrideSelector(toEditOrNotToEdit, undefined); + }); + + afterEach(() => { + store.resetSelectors(); + }); + + it('should return true if some forms are invalid', () => { + component.abandonDialog = { + dynamicFormGroup: { form: { controls: { errors: 'foo' }, invalid: true } }, + } as unknown as AbandonDialogComponent; + component.testMode = TestModeEnum.Abandon; + DynamicFormService.validate = jest.fn(); + expect(component.isAnyFormInvalid()).toBe(true); + }); + + it('should return false if no forms are invalid', fakeAsync(() => { + tick(); + fixture.detectChanges(); + expect(component.isAnyFormInvalid()).toBe(false); + flush(); + })); + }); + + describe('CreateTestRecordComponent.prototype.abandon.name', () => { + it('should set testMode to be abandon', () => { + component.abandon(); + expect(component.testMode).toEqual(TestModeEnum.Abandon); + }); + }); + + describe('CreateTestRecordComponent.prototype.handleAbandonAction.name', () => { + it('should call handle save', async () => { + const handleSaveSpy = jest.spyOn(component, 'handleSave'); + + await component.handleAbandonAction('yes'); + + expect(handleSaveSpy).toHaveBeenCalledTimes(1); + }); + + it('should set testMode to be edit', async () => { + component.testMode = TestModeEnum.Abandon; + + await component.handleAbandonAction('no'); + + expect(component.testMode).toEqual(TestModeEnum.Edit); + }); + }); + + it('should combine forms', async () => { + component['baseTestRecordComponent'] = { + sections: { forEach: jest.fn().mockReturnValue([{ foo: 'foo' }]) }, + } as unknown as BaseTestRecordComponent; + + // eslint-disable-next-line @typescript-eslint/no-misused-promises + const createTestResultSpy = jest + .spyOn(testRecordsService, 'createTestResult') + .mockImplementation(() => Promise.resolve(true)); + const testRecord = { testResultId: '1', testTypes: [{ testTypeId: '2' }] } as TestResultModel; + store.overrideSelector(testResultInEdit, testRecord); + store.overrideSelector(sectionTemplates, Object.values(contingencyTestTemplates.psv['testTypesGroup1'] ?? '')); + + fixture.detectChanges(); + + await component.handleSave(); + + fixture.detectChanges(); + expect(createTestResultSpy).toHaveBeenCalledTimes(1); + expect(createTestResultSpy).toHaveBeenCalledWith(testRecord); + }); + + it('should set testMode to be view', async () => { + component.techRecord = {} as V3TechRecordModel; + component.isAnyFormInvalid = jest.fn().mockReturnValue(false); + await component.handleReview(); + + expect(component.testMode).toEqual(TestModeEnum.View); + }); + + it('should set testMode back to edit', async () => { + component.isAnyFormInvalid = jest.fn().mockReturnValue(false); + await component.handleReview(); + component.handleCancel(); + + expect(component.testMode).toEqual(TestModeEnum.Edit); + }); }); diff --git a/src/app/features/test-records/create/views/create-test-record/create-test-record.component.ts b/src/app/features/test-records/create/views/create-test-record/create-test-record.component.ts index 3bf3344e50..0dbec068ca 100644 --- a/src/app/features/test-records/create/views/create-test-record/create-test-record.component.ts +++ b/src/app/features/test-records/create/views/create-test-record/create-test-record.component.ts @@ -1,6 +1,4 @@ -import { - AfterViewInit, ChangeDetectorRef, Component, OnDestroy, OnInit, ViewChild, -} from '@angular/core'; +import { AfterViewInit, ChangeDetectorRef, Component, OnDestroy, OnInit, ViewChild } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; import { GlobalError } from '@core/components/global-error/global-error.interface'; import { GlobalErrorService } from '@core/components/global-error/global-error.service'; @@ -23,223 +21,272 @@ import { selectTechRecord } from '@store/technical-records'; import { createTestResultSuccess } from '@store/test-records'; import { getTypeOfTest } from '@store/test-types/selectors/test-types.selectors'; import cloneDeep from 'lodash.clonedeep'; -import { - BehaviorSubject, Observable, Subject, filter, firstValueFrom, map, of, take, takeUntil, tap, -} from 'rxjs'; +import { BehaviorSubject, Observable, Subject, filter, firstValueFrom, map, of, take, takeUntil, tap } from 'rxjs'; import { BaseTestRecordComponent } from '../../../components/base-test-record/base-test-record.component'; @Component({ - selector: 'app-create-test-record', - templateUrl: './create-test-record.component.html', + selector: 'app-create-test-record', + templateUrl: './create-test-record.component.html', }) export class CreateTestRecordComponent implements OnInit, OnDestroy, AfterViewInit { - @ViewChild(BaseTestRecordComponent) private baseTestRecordComponent?: BaseTestRecordComponent; - @ViewChild(AbandonDialogComponent) abandonDialog?: AbandonDialogComponent; - - private destroy$ = new Subject(); - - canCreate$ = new BehaviorSubject(false); - testMode = TestModeEnum.Edit; - testResult$: Observable = of(undefined); - testTypeId?: string; - techRecord: V3TechRecordModel | undefined = undefined; - - constructor( - private actions$: Actions, - private errorService: GlobalErrorService, - private route: ActivatedRoute, - private router: Router, - private routerService: RouterService, - private testRecordsService: TestRecordsService, - private cdr: ChangeDetectorRef, - private resultOfTestService: ResultOfTestService, - private store: Store, - private warningService: GlobalWarningService, - ) { - this.router.routeReuseStrategy.shouldReuseRoute = () => false; - } - - ngOnInit(): void { - this.testResult$ = this.testRecordsService.editingTestResult$.pipe(tap((editingTestResult) => !editingTestResult && this.backToTechRecord())); - - this.routerService - .getQueryParam$('testType') - .pipe( - take(1), - tap((testType) => !testType && this.backToTechRecord()), - filter((tt) => !!tt), - ) - .subscribe((testTypeId) => { - this.testRecordsService.contingencyTestTypeSelected(testTypeId as string); - this.testTypeId = testTypeId; - }); - - this.watchForCreateSuccess(); - - this.testRecordsService.canCreate$.pipe(take(1)).subscribe((val) => this.canCreate$.next(val)); - - this.store.select(selectTechRecord).pipe(take(1)).subscribe((techRecord) => { - this.techRecord = techRecord; - }); - } - - ngOnDestroy(): void { - this.errorService.clearErrors(); - - this.destroy$.next(); - this.destroy$.complete(); - } - - ngAfterViewInit(): void { - this.cdr.detectChanges(); - } - - backToTechRecord(): void { - void this.router.navigate(['../../..'], { relativeTo: this.route.parent }); - } - - /** - * Merge all section form values into one testResult and trigger action to update testResult. - * @returns void - */ - async handleSave(): Promise { - if (this.isAnyFormInvalid()) { - return; - } - - this.testRecordsService.cleanTestResult(); - - const testResult = await firstValueFrom(this.testResult$); - const testResultClone = cloneDeep(testResult) as TestResultModel; - - this.testRecordsService.createTestResult(testResultClone); - } - - async handleReview() { - if (this.isAnyFormInvalid()) return; - if (this.techRecord == null) return; - - this.errorService.clearErrors(); - this.testMode = TestModeEnum.View; - - const testResult = await firstValueFrom(this.testResult$); - if (testResult && this.techRecord?.techRecord_statusCode === StatusCodes.PROVISIONAL - && this.testTypeId - && this.validateUpdateStatus(testResult.testTypes[0].testResult, this.testTypeId)) { - const warnings: GlobalWarning[] = []; - warnings.push({ warning: 'This test will update the tech record to current, if the page is showing as provisional then refresh the page' }); - this.warningService.setWarnings(warnings); - } - } - - handleCancel() { - this.testMode = TestModeEnum.Edit; - this.warningService.clearWarnings(); - } - - watchForCreateSuccess() { - this.actions$.pipe(ofType(createTestResultSuccess), takeUntil(this.destroy$)).subscribe(() => { - this.backToTechRecord(); - }); - } - - handleNewTestResult(testResult: TestResultModel) { - this.testRecordsService.updateEditingTestResult(testResult); - } - - isAnyFormInvalid() { - const errors: GlobalError[] = []; - const forms = []; - - if (this.baseTestRecordComponent?.sections) { - this.baseTestRecordComponent.sections.forEach((section) => forms.push(section.form)); - } - - if (this.baseTestRecordComponent?.defects) { - forms.push(this.baseTestRecordComponent.defects.form); - } - - if (this.testMode === TestModeEnum.Abandon && this.abandonDialog?.dynamicFormGroup) { - forms.push(this.abandonDialog.dynamicFormGroup.form); - } - - forms.forEach((form) => { - DynamicFormService.validate(form, errors); - }); - - if (errors.length) { - this.errorService.setErrors(errors); - } - - return forms.some((form) => form.invalid); - } - - abandon() { - this.resultOfTestService.toggleAbandoned(resultOfTestEnum.abandoned); - - if (this.isAnyFormInvalid()) { - return; - } - - this.testMode = TestModeEnum.Abandon; - } - - async handleAbandonAction(event: string) { - switch (event) { - case 'yes': - await this.handleSave(); - break; - case 'no': - this.abandonDialog?.dynamicFormGroup?.form.reset(); - this.resultOfTestService.toggleAbandoned(resultOfTestEnum.pass); - this.testMode = TestModeEnum.Edit; - break; - default: - console.error('Invalid action'); - } - } - - get isDeskBased() { - return this.store.pipe( - select(getTypeOfTest(this.testTypeId)), - map((typeOfTest) => typeOfTest === TypeOfTest.DESK_BASED), - ); - } - - public get TestModeEnum(): typeof TestModeEnum { - return TestModeEnum; - } - - validateUpdateStatus = ( - testResult: string, - testTypeId: string, - ): boolean => ( - (testResult === 'pass' || testResult === 'prs') - && (this.isTestTypeFirstTest(testTypeId) - || this.isTestTypeNotifiableAlteration(testTypeId) - || this.isTestTypeCOIF(testTypeId) - || this.isTestTypeIVA(testTypeId)) - ); - - isTestTypeFirstTest(testTypeId: string): boolean { - const firstTestIds = ['41', '95', '65', '66', '67', '103', '104', '82', '83', '119', '120']; - return firstTestIds.includes(testTypeId); - } - - isTestTypeNotifiableAlteration(testTypeId: string): boolean { - const notifiableAlterationIds = ['38', '47', '48']; - return notifiableAlterationIds.includes(testTypeId); - } - - isTestTypeCOIF(testTypeId: string): boolean { - const coifIds = ['142', '143', '175', '176']; - return coifIds.includes(testTypeId); - } - - isTestTypeIVA(testTypeId: string): boolean { - const ivaIds = ['133', '134', '138', '139', '140', '165', '169', '167', '170', '135', '172', '173', '439', '449', - '136', '187', '126', '186', '193', '192', '195', '162', '191', '128', '188', '189', '125', '161', '158', '159', - '154', '190', '129', '196', '194', '197', '185', '420', '438', '163', '153', '184', '130', '183']; - return ivaIds.includes(testTypeId); - } + @ViewChild(BaseTestRecordComponent) private baseTestRecordComponent?: BaseTestRecordComponent; + @ViewChild(AbandonDialogComponent) abandonDialog?: AbandonDialogComponent; + + private destroy$ = new Subject(); + + canCreate$ = new BehaviorSubject(false); + testMode = TestModeEnum.Edit; + testResult$: Observable = of(undefined); + testTypeId?: string; + techRecord: V3TechRecordModel | undefined = undefined; + + constructor( + private actions$: Actions, + private errorService: GlobalErrorService, + private route: ActivatedRoute, + private router: Router, + private routerService: RouterService, + private testRecordsService: TestRecordsService, + private cdr: ChangeDetectorRef, + private resultOfTestService: ResultOfTestService, + private store: Store, + private warningService: GlobalWarningService + ) { + this.router.routeReuseStrategy.shouldReuseRoute = () => false; + } + + ngOnInit(): void { + this.testResult$ = this.testRecordsService.editingTestResult$.pipe( + tap((editingTestResult) => !editingTestResult && this.backToTechRecord()) + ); + + this.routerService + .getQueryParam$('testType') + .pipe( + take(1), + tap((testType) => !testType && this.backToTechRecord()), + filter((tt) => !!tt) + ) + .subscribe((testTypeId) => { + this.testRecordsService.contingencyTestTypeSelected(testTypeId as string); + // @ts-ignore + this.testTypeId = testTypeId; + }); + + this.watchForCreateSuccess(); + + this.testRecordsService.canCreate$.pipe(take(1)).subscribe((val) => this.canCreate$.next(val)); + + this.store + .select(selectTechRecord) + .pipe(take(1)) + .subscribe((techRecord) => { + this.techRecord = techRecord; + }); + } + + ngOnDestroy(): void { + this.errorService.clearErrors(); + + this.destroy$.next(); + this.destroy$.complete(); + } + + ngAfterViewInit(): void { + this.cdr.detectChanges(); + } + + backToTechRecord(): void { + void this.router.navigate(['../../..'], { relativeTo: this.route.parent }); + } + + /** + * Merge all section form values into one testResult and trigger action to update testResult. + * @returns void + */ + async handleSave(): Promise { + if (this.isAnyFormInvalid()) { + return; + } + + this.testRecordsService.cleanTestResult(); + + const testResult = await firstValueFrom(this.testResult$); + const testResultClone = cloneDeep(testResult) as TestResultModel; + + this.testRecordsService.createTestResult(testResultClone); + } + + async handleReview() { + if (this.isAnyFormInvalid()) return; + if (this.techRecord == null) return; + + this.errorService.clearErrors(); + this.testMode = TestModeEnum.View; + + const testResult = await firstValueFrom(this.testResult$); + if ( + testResult && + this.techRecord?.techRecord_statusCode === StatusCodes.PROVISIONAL && + this.testTypeId && + this.validateUpdateStatus(testResult.testTypes[0].testResult, this.testTypeId) + ) { + const warnings: GlobalWarning[] = []; + warnings.push({ + warning: + 'This test will update the tech record to current, if the page is showing as provisional then refresh the page', + }); + this.warningService.setWarnings(warnings); + } + } + + handleCancel() { + this.testMode = TestModeEnum.Edit; + this.warningService.clearWarnings(); + } + + watchForCreateSuccess() { + this.actions$.pipe(ofType(createTestResultSuccess), takeUntil(this.destroy$)).subscribe(() => { + this.backToTechRecord(); + }); + } + + handleNewTestResult(testResult: TestResultModel) { + this.testRecordsService.updateEditingTestResult(testResult); + } + + isAnyFormInvalid() { + const errors: GlobalError[] = []; + const forms = []; + + if (this.baseTestRecordComponent?.sections) { + this.baseTestRecordComponent.sections.forEach((section) => forms.push(section.form)); + } + + if (this.baseTestRecordComponent?.defects) { + forms.push(this.baseTestRecordComponent.defects.form); + } + + if (this.testMode === TestModeEnum.Abandon && this.abandonDialog?.dynamicFormGroup) { + forms.push(this.abandonDialog.dynamicFormGroup.form); + } + + forms.forEach((form) => { + DynamicFormService.validate(form, errors); + }); + + if (errors.length) { + this.errorService.setErrors(errors); + } + + return forms.some((form) => form.invalid); + } + + abandon() { + this.resultOfTestService.toggleAbandoned(resultOfTestEnum.abandoned); + + if (this.isAnyFormInvalid()) { + return; + } + + this.testMode = TestModeEnum.Abandon; + } + + async handleAbandonAction(event: string) { + switch (event) { + case 'yes': + await this.handleSave(); + break; + case 'no': + this.abandonDialog?.dynamicFormGroup?.form.reset(); + this.resultOfTestService.toggleAbandoned(resultOfTestEnum.pass); + this.testMode = TestModeEnum.Edit; + break; + default: + console.error('Invalid action'); + } + } + + get isDeskBased() { + return this.store.pipe( + select(getTypeOfTest(this.testTypeId)), + map((typeOfTest) => typeOfTest === TypeOfTest.DESK_BASED) + ); + } + + public get TestModeEnum(): typeof TestModeEnum { + return TestModeEnum; + } + + validateUpdateStatus = (testResult: string, testTypeId: string): boolean => + (testResult === 'pass' || testResult === 'prs') && + (this.isTestTypeFirstTest(testTypeId) || + this.isTestTypeNotifiableAlteration(testTypeId) || + this.isTestTypeCOIF(testTypeId) || + this.isTestTypeIVA(testTypeId)); + + isTestTypeFirstTest(testTypeId: string): boolean { + const firstTestIds = ['41', '95', '65', '66', '67', '103', '104', '82', '83', '119', '120']; + return firstTestIds.includes(testTypeId); + } + + isTestTypeNotifiableAlteration(testTypeId: string): boolean { + const notifiableAlterationIds = ['38', '47', '48']; + return notifiableAlterationIds.includes(testTypeId); + } + + isTestTypeCOIF(testTypeId: string): boolean { + const coifIds = ['142', '143', '175', '176']; + return coifIds.includes(testTypeId); + } + + isTestTypeIVA(testTypeId: string): boolean { + const ivaIds = [ + '133', + '134', + '138', + '139', + '140', + '165', + '169', + '167', + '170', + '135', + '172', + '173', + '439', + '449', + '136', + '187', + '126', + '186', + '193', + '192', + '195', + '162', + '191', + '128', + '188', + '189', + '125', + '161', + '158', + '159', + '154', + '190', + '129', + '196', + '194', + '197', + '185', + '420', + '438', + '163', + '153', + '184', + '130', + '183', + ]; + return ivaIds.includes(testTypeId); + } } diff --git a/src/app/features/test-records/create/views/create-test-type/create-test-type.component.spec.ts b/src/app/features/test-records/create/views/create-test-type/create-test-type.component.spec.ts index b9ae1f44a2..6c41251730 100644 --- a/src/app/features/test-records/create/views/create-test-type/create-test-type.component.spec.ts +++ b/src/app/features/test-records/create/views/create-test-type/create-test-type.component.spec.ts @@ -13,85 +13,92 @@ import { TestTypeSelectComponent } from '../../../components/test-type-select/te import { CreateTestTypeComponent } from './create-test-type.component'; describe('CreateTestTypeComponent', () => { - let component: CreateTestTypeComponent; - let fixture: ComponentFixture; - let router: Router; - let route: ActivatedRoute; - let techRecordService: TechnicalRecordService; + let component: CreateTestTypeComponent; + let fixture: ComponentFixture; + let router: Router; + let route: ActivatedRoute; + let techRecordService: TechnicalRecordService; - beforeEach(async () => { - await TestBed.configureTestingModule({ - declarations: [CreateTestTypeComponent, TestTypeSelectComponent], - imports: [HttpClientTestingModule, RouterTestingModule], - providers: [ - provideMockStore({ initialState: initialAppState }), - { - provide: TechnicalRecordService, - }, - { provide: TestTypesService, useValue: { selectAllTestTypes$: of([]), testTypeIdChanged: () => {} } }, - ], - }).compileComponents(); - }); + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [CreateTestTypeComponent, TestTypeSelectComponent], + imports: [HttpClientTestingModule, RouterTestingModule], + providers: [ + provideMockStore({ initialState: initialAppState }), + { + provide: TechnicalRecordService, + }, + { provide: TestTypesService, useValue: { selectAllTestTypes$: of([]), testTypeIdChanged: () => {} } }, + ], + }).compileComponents(); + }); - beforeEach(() => { - fixture = TestBed.createComponent(CreateTestTypeComponent); - techRecordService = TestBed.inject(TechnicalRecordService); - component = fixture.componentInstance; - router = TestBed.inject(Router); - route = TestBed.inject(ActivatedRoute); + beforeEach(() => { + fixture = TestBed.createComponent(CreateTestTypeComponent); + techRecordService = TestBed.inject(TechnicalRecordService); + component = fixture.componentInstance; + router = TestBed.inject(Router); + route = TestBed.inject(ActivatedRoute); - jest.spyOn(window, 'alert').mockImplementation(); + jest.spyOn(window, 'alert').mockImplementation(); - fixture.detectChanges(); - }); + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); - it('should navigate to sibling path "amend-test-details"', () => { - const navigateSpy = jest.spyOn(router, 'navigate').mockReturnValue(Promise.resolve(true)); - component.handleSelectedTestType({ id: '1' } as TestType); - expect(navigateSpy).toHaveBeenCalledWith(['..', 'test-details'], { - queryParams: { testType: '1' }, - queryParamsHandling: 'merge', - relativeTo: route, - }); - }); + it('should navigate to sibling path "amend-test-details"', () => { + const navigateSpy = jest.spyOn(router, 'navigate').mockReturnValue(Promise.resolve(true)); + component.handleSelectedTestType({ id: '1' } as TestType); + expect(navigateSpy).toHaveBeenCalledWith(['..', 'test-details'], { + queryParams: { testType: '1' }, + queryParamsHandling: 'merge', + relativeTo: route, + }); + }); - describe('AfterContentInit', () => { - const testCases = [ - { - record: { - systemNumber: 'foo', createdTimestamp: 'bar', vin: 'testVin', techRecord_recordCompleteness: 'foo', - } as V3TechRecordModel, - message: - 'Incomplete vehicle record.\n\n' - + 'This vehicle does not have enough data to be tested. ' - + 'Call Technical Support to correct this record and use SAR to test this vehicle.', - }, - { - record: { - systemNumber: 'foo', - createdTimestamp: 'bar', - vin: 'testVin', - techRecord_hiddenInVta: true, - techRecord_recordCompleteness: 'complete', - } as V3TechRecordModel, - message: 'Vehicle record is hidden in VTA.\n\nShow the vehicle record in VTA to start recording tests against it.', - }, - ]; + describe('AfterContentInit', () => { + const testCases = [ + { + record: { + systemNumber: 'foo', + createdTimestamp: 'bar', + vin: 'testVin', + techRecord_recordCompleteness: 'foo', + } as V3TechRecordModel, + message: + 'Incomplete vehicle record.\n\n' + + 'This vehicle does not have enough data to be tested. ' + + 'Call Technical Support to correct this record and use SAR to test this vehicle.', + }, + { + record: { + systemNumber: 'foo', + createdTimestamp: 'bar', + vin: 'testVin', + techRecord_hiddenInVta: true, + techRecord_recordCompleteness: 'complete', + } as V3TechRecordModel, + message: + 'Vehicle record is hidden in VTA.\n\nShow the vehicle record in VTA to start recording tests against it.', + }, + ]; - it.each(testCases)('should get the vehicle record and alert with the appropriate message', ({ record, message }) => { - jest.resetAllMocks(); - const mockTechRecordSpy = jest.spyOn(techRecordService, 'techRecord$', 'get').mockReturnValue(of(record)); - const alertSpy = jest.spyOn(window, 'alert').mockImplementation(); - const navigateSpy = jest.spyOn(router, 'navigate').mockReturnValue(Promise.resolve(true)); - component.ngAfterContentInit(); - expect(mockTechRecordSpy).toHaveBeenCalledTimes(1); - expect(alertSpy).toHaveBeenCalledTimes(1); - expect(alertSpy).toHaveBeenCalledWith(message); - expect(navigateSpy).toHaveBeenCalledWith(['../../..'], { relativeTo: route }); - }); - }); + it.each(testCases)( + 'should get the vehicle record and alert with the appropriate message', + ({ record, message }) => { + jest.resetAllMocks(); + const mockTechRecordSpy = jest.spyOn(techRecordService, 'techRecord$', 'get').mockReturnValue(of(record)); + const alertSpy = jest.spyOn(window, 'alert').mockImplementation(); + const navigateSpy = jest.spyOn(router, 'navigate').mockReturnValue(Promise.resolve(true)); + component.ngAfterContentInit(); + expect(mockTechRecordSpy).toHaveBeenCalledTimes(1); + expect(alertSpy).toHaveBeenCalledTimes(1); + expect(alertSpy).toHaveBeenCalledWith(message); + expect(navigateSpy).toHaveBeenCalledWith(['../../..'], { relativeTo: route }); + } + ); + }); }); diff --git a/src/app/features/test-records/create/views/create-test-type/create-test-type.component.ts b/src/app/features/test-records/create/views/create-test-type/create-test-type.component.ts index 7a64355d26..9928718dc3 100644 --- a/src/app/features/test-records/create/views/create-test-type/create-test-type.component.ts +++ b/src/app/features/test-records/create/views/create-test-type/create-test-type.component.ts @@ -10,49 +10,51 @@ import { contingencyTestTypeSelected } from '@store/test-records'; import { take } from 'rxjs'; @Component({ - selector: 'app-create-test-type', - templateUrl: './create-test-type.component.html', - changeDetection: ChangeDetectionStrategy.OnPush, + selector: 'app-create-test-type', + templateUrl: './create-test-type.component.html', + changeDetection: ChangeDetectionStrategy.OnPush, }) export class CreateTestTypeComponent implements AfterContentInit { - constructor( - private store: Store, - private router: Router, - private route: ActivatedRoute, - private technicalRecordService: TechnicalRecordService, - ) {} + constructor( + private store: Store, + private router: Router, + private route: ActivatedRoute, + private technicalRecordService: TechnicalRecordService + ) {} - ngAfterContentInit(): void { - this.technicalRecordService.techRecord$.pipe(take(1)).subscribe((techRecord) => { - if (techRecord?.techRecord_hiddenInVta) { - // eslint-disable-next-line no-alert - alert('Vehicle record is hidden in VTA.\n\nShow the vehicle record in VTA to start recording tests against it.'); + ngAfterContentInit(): void { + this.technicalRecordService.techRecord$.pipe(take(1)).subscribe((techRecord) => { + if (techRecord?.techRecord_hiddenInVta) { + // eslint-disable-next-line no-alert + alert( + 'Vehicle record is hidden in VTA.\n\nShow the vehicle record in VTA to start recording tests against it.' + ); - void this.router.navigate(['../../..'], { relativeTo: this.route }); - } else if ( - (techRecord as TechRecordType<'get'>)?.techRecord_recordCompleteness !== 'complete' - && (techRecord as TechRecordType<'get'>)?.techRecord_recordCompleteness !== 'testable' - ) { - // eslint-disable-next-line no-alert - alert( - 'Incomplete vehicle record.\n\n' - + 'This vehicle does not have enough data to be tested. ' - + 'Call Technical Support to correct this record and use SAR to test this vehicle.', - ); + void this.router.navigate(['../../..'], { relativeTo: this.route }); + } else if ( + (techRecord as TechRecordType<'get'>)?.techRecord_recordCompleteness !== 'complete' && + (techRecord as TechRecordType<'get'>)?.techRecord_recordCompleteness !== 'testable' + ) { + // eslint-disable-next-line no-alert + alert( + 'Incomplete vehicle record.\n\n' + + 'This vehicle does not have enough data to be tested. ' + + 'Call Technical Support to correct this record and use SAR to test this vehicle.' + ); - void this.router.navigate(['../../..'], { relativeTo: this.route }); - } - }); - } + void this.router.navigate(['../../..'], { relativeTo: this.route }); + } + }); + } - handleSelectedTestType(testType: TestType) { - this.store.dispatch(contingencyTestTypeSelected({ testType: testType.id })); - this.store.dispatch(clearAllSectionStates()); + handleSelectedTestType(testType: TestType) { + this.store.dispatch(contingencyTestTypeSelected({ testType: testType.id })); + this.store.dispatch(clearAllSectionStates()); - void this.router.navigate(['..', 'test-details'], { - queryParams: { testType: testType.id }, - queryParamsHandling: 'merge', - relativeTo: this.route, - }); - } + void this.router.navigate(['..', 'test-details'], { + queryParams: { testType: testType.id }, + queryParamsHandling: 'merge', + relativeTo: this.route, + }); + } } diff --git a/src/app/features/test-records/create/views/test-router-outlet/test-router-outlet.component.spec.ts b/src/app/features/test-records/create/views/test-router-outlet/test-router-outlet.component.spec.ts index 4da59bb9c3..d1d8bd0c1c 100644 --- a/src/app/features/test-records/create/views/test-router-outlet/test-router-outlet.component.spec.ts +++ b/src/app/features/test-records/create/views/test-router-outlet/test-router-outlet.component.spec.ts @@ -3,23 +3,23 @@ import { RouterTestingModule } from '@angular/router/testing'; import { TestRouterOutletComponent } from './test-router-outlet.component'; describe('TestRouterOutletComponent', () => { - let component: TestRouterOutletComponent; - let fixture: ComponentFixture; + let component: TestRouterOutletComponent; + let fixture: ComponentFixture; - beforeEach(async () => { - await TestBed.configureTestingModule({ - declarations: [TestRouterOutletComponent], - imports: [RouterTestingModule], - }).compileComponents(); - }); + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [TestRouterOutletComponent], + imports: [RouterTestingModule], + }).compileComponents(); + }); - beforeEach(() => { - fixture = TestBed.createComponent(TestRouterOutletComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); + beforeEach(() => { + fixture = TestBed.createComponent(TestRouterOutletComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); }); diff --git a/src/app/features/test-records/create/views/test-router-outlet/test-router-outlet.component.ts b/src/app/features/test-records/create/views/test-router-outlet/test-router-outlet.component.ts index 9427a72c5b..8ae25e17ef 100644 --- a/src/app/features/test-records/create/views/test-router-outlet/test-router-outlet.component.ts +++ b/src/app/features/test-records/create/views/test-router-outlet/test-router-outlet.component.ts @@ -1,8 +1,8 @@ import { ChangeDetectionStrategy, Component } from '@angular/core'; @Component({ - selector: 'app-test-router-outlet', - templateUrl: './test-router-outlet.component.html', - changeDetection: ChangeDetectionStrategy.OnPush, + selector: 'app-test-router-outlet', + templateUrl: './test-router-outlet.component.html', + changeDetection: ChangeDetectionStrategy.OnPush, }) export class TestRouterOutletComponent {} diff --git a/src/app/features/test-records/test-records.module.ts b/src/app/features/test-records/test-records.module.ts index 8cc4809ad4..248506de5e 100644 --- a/src/app/features/test-records/test-records.module.ts +++ b/src/app/features/test-records/test-records.module.ts @@ -8,8 +8,8 @@ import { TestTypeSelectComponent } from './components/test-type-select/test-type import { VehicleHeaderComponent } from './components/vehicle-header/vehicle-header.component'; @NgModule({ - declarations: [BaseTestRecordComponent, TestTypeSelectComponent, VehicleHeaderComponent], - imports: [CommonModule, SharedModule, DynamicFormsModule, RouterModule], - exports: [BaseTestRecordComponent, TestTypeSelectComponent, VehicleHeaderComponent], + declarations: [BaseTestRecordComponent, TestTypeSelectComponent, VehicleHeaderComponent], + imports: [CommonModule, SharedModule, DynamicFormsModule, RouterModule], + exports: [BaseTestRecordComponent, TestTypeSelectComponent, VehicleHeaderComponent], }) export class TestRecordsModule {} diff --git a/src/app/forms/components/approval-type/approval-type-focus-next.directive.spec.ts b/src/app/forms/components/approval-type/approval-type-focus-next.directive.spec.ts index 9b422898f5..fbefa03d7b 100644 --- a/src/app/forms/components/approval-type/approval-type-focus-next.directive.spec.ts +++ b/src/app/forms/components/approval-type/approval-type-focus-next.directive.spec.ts @@ -4,7 +4,7 @@ import { By } from '@angular/platform-browser'; import { ApprovalTypeFocusNextDirective } from './approval-type-focus-next.directive'; @Component({ - template: ` + template: ` `, @@ -12,42 +12,42 @@ import { ApprovalTypeFocusNextDirective } from './approval-type-focus-next.direc class TestComponent {} describe('ApprovalTypeFocusNext', () => { - let fixture: ComponentFixture; - let component: HTMLInputElement; - - beforeEach(async () => { - await TestBed.configureTestingModule({ - declarations: [ApprovalTypeFocusNextDirective, TestComponent], - providers: [TemplateRef], - }).compileComponents(); - - fixture = TestBed.createComponent(TestComponent); - fixture.detectChanges(); - - component = fixture.debugElement.query(By.directive(ApprovalTypeFocusNextDirective)).nativeElement; - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); - - describe('onInput', () => { - it('should, when the value of the input equals the character limit, focus the next element', () => { - const input: HTMLInputElement = fixture.debugElement.query(By.css('#next')).nativeElement; - const focusSpy = jest.spyOn(input, 'focus'); - const getElementSpy = jest.spyOn(document, 'getElementById'); - component.value = 'abcdefghijklmnopqrst'; // 20 characters - component.dispatchEvent(new KeyboardEvent('input', { key: '.' })); - expect(focusSpy).toHaveBeenCalled(); - expect(getElementSpy).toHaveBeenCalled(); - }); - - it('should not attempt to focus any element when the input value is less than the character limit', () => { - const input: HTMLInputElement = fixture.debugElement.query(By.css('#next')).nativeElement; - const focusSpy = jest.spyOn(input, 'focus'); - component.value = 'abcdefghijklmnopqrs'; // 19 characters - component.dispatchEvent(new KeyboardEvent('input', { key: '.' })); - expect(focusSpy).not.toHaveBeenCalled(); - }); - }); + let fixture: ComponentFixture; + let component: HTMLInputElement; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ApprovalTypeFocusNextDirective, TestComponent], + providers: [TemplateRef], + }).compileComponents(); + + fixture = TestBed.createComponent(TestComponent); + fixture.detectChanges(); + + component = fixture.debugElement.query(By.directive(ApprovalTypeFocusNextDirective)).nativeElement; + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + describe('onInput', () => { + it('should, when the value of the input equals the character limit, focus the next element', () => { + const input: HTMLInputElement = fixture.debugElement.query(By.css('#next')).nativeElement; + const focusSpy = jest.spyOn(input, 'focus'); + const getElementSpy = jest.spyOn(document, 'getElementById'); + component.value = 'abcdefghijklmnopqrst'; // 20 characters + component.dispatchEvent(new KeyboardEvent('input', { key: '.' })); + expect(focusSpy).toHaveBeenCalled(); + expect(getElementSpy).toHaveBeenCalled(); + }); + + it('should not attempt to focus any element when the input value is less than the character limit', () => { + const input: HTMLInputElement = fixture.debugElement.query(By.css('#next')).nativeElement; + const focusSpy = jest.spyOn(input, 'focus'); + component.value = 'abcdefghijklmnopqrs'; // 19 characters + component.dispatchEvent(new KeyboardEvent('input', { key: '.' })); + expect(focusSpy).not.toHaveBeenCalled(); + }); + }); }); diff --git a/src/app/forms/components/approval-type/approval-type-focus-next.directive.ts b/src/app/forms/components/approval-type/approval-type-focus-next.directive.ts index de40b75141..1c2eac93f4 100644 --- a/src/app/forms/components/approval-type/approval-type-focus-next.directive.ts +++ b/src/app/forms/components/approval-type/approval-type-focus-next.directive.ts @@ -1,24 +1,22 @@ -import { - Directive, ElementRef, HostListener, Input, -} from '@angular/core'; +import { Directive, ElementRef, HostListener, Input } from '@angular/core'; @Directive({ - selector: '[appFocusNextApprovalType]', + selector: '[appFocusNextApprovalType]', }) export class ApprovalTypeFocusNextDirective { - @Input('appFocusNextApprovalType') nextInputId = ''; - @Input() characterLimit = 0; + @Input('appFocusNextApprovalType') nextInputId = ''; + @Input() characterLimit = 0; - constructor(private el: ElementRef) {} + constructor(private el: ElementRef) {} - @HostListener('input', ['$event']) - onInput() { - const { value } = this.el.nativeElement; - if (value.length === this.characterLimit) { - const nextInput = document.getElementById(this.nextInputId); - if (nextInput) { - nextInput.focus(); - } - } - } + @HostListener('input', ['$event']) + onInput() { + const { value } = this.el.nativeElement; + if (value.length === this.characterLimit) { + const nextInput = document.getElementById(this.nextInputId); + if (nextInput) { + nextInput.focus(); + } + } + } } diff --git a/src/app/forms/components/approval-type/approval-type.component.spec.ts b/src/app/forms/components/approval-type/approval-type.component.spec.ts index 46670d8de9..b3671be132 100644 --- a/src/app/forms/components/approval-type/approval-type.component.spec.ts +++ b/src/app/forms/components/approval-type/approval-type.component.spec.ts @@ -1,13 +1,9 @@ import { Component, ViewChild } from '@angular/core'; import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { - FormsModule, ReactiveFormsModule, -} from '@angular/forms'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { GlobalErrorService } from '@core/components/global-error/global-error.service'; import { ApprovalType } from '@dvsa/cvs-type-definitions/types/v3/tech-record/enums/approvalType.enum.js'; -import { - CustomFormControl, CustomFormGroup, FormNodeTypes, FormNodeWidth, -} from '@forms/services/dynamic-form.types'; +import { CustomFormControl, CustomFormGroup, FormNodeTypes, FormNodeWidth } from '@forms/services/dynamic-form.types'; import { provideMockStore } from '@ngrx/store/testing'; import { initialAppState } from '@store/index'; import { BaseControlComponent } from '../base-control/base-control.component'; @@ -16,420 +12,494 @@ import { FieldErrorMessageComponent } from '../field-error-message/field-error-m import { ApprovalTypeInputComponent } from './approval-type.component'; @Component({ - selector: 'app-host-component', - template: `
+ selector: 'app-host-component', + template: `
`, - styles: [], + styles: [], }) class HostComponent { - @ViewChild(ApprovalTypeInputComponent, { static: true }) approvalTypeComponent!: ApprovalTypeInputComponent; - - form = new CustomFormGroup({ - name: 'approvalType', - type: FormNodeTypes.GROUP, - }, { - approvalType: new CustomFormControl({ name: 'approvalType', type: FormNodeTypes.CONTROL }), - }); + @ViewChild(ApprovalTypeInputComponent, { static: true }) approvalTypeComponent!: ApprovalTypeInputComponent; + + form = new CustomFormGroup( + { + name: 'approvalType', + type: FormNodeTypes.GROUP, + }, + { + approvalType: new CustomFormControl({ name: 'approvalType', type: FormNodeTypes.CONTROL }), + } + ); } describe('ApprovalTypeComponent', () => { - let component: HostComponent; - let fixture: ComponentFixture; - - beforeEach(async () => { - await TestBed.configureTestingModule({ - declarations: [BaseControlComponent, ApprovalTypeInputComponent, FieldErrorMessageComponent, FocusNextDirective, HostComponent], - imports: [FormsModule, ReactiveFormsModule], - providers: [GlobalErrorService, provideMockStore({ initialState: initialAppState })], - }).compileComponents(); - }); - - beforeEach(() => { - fixture = TestBed.createComponent(HostComponent); - component = fixture.componentInstance; - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); - - describe('ngOnInit', () => { - it('should initialise all the compoennts subscriptions', () => { - const spy = jest.spyOn(component.approvalTypeComponent, 'subscribeAndPropagateChanges'); - component.approvalTypeComponent.ngOnInit(); - expect(spy).toHaveBeenCalled(); - }); - }); - - describe('ngAfterViewInit', () => { - it('should write the control value back to its control', () => { - const spy = jest.spyOn(component.approvalTypeComponent, 'valueWriteBack'); - component.approvalTypeComponent.ngAfterContentInit(); - expect(spy).toHaveBeenCalled(); - }); - }); - - describe('widths', () => { - it('should return form node widths', () => { - expect(component.approvalTypeComponent.widths).toBe(FormNodeWidth); - }); - }); - - describe('clearInput', () => { - it('should set all the approval type input number boxes to undefined, and call onChange', () => { - const spy = jest.spyOn(component.approvalTypeComponent, 'onChange'); - component.approvalTypeComponent.clearInput(); - expect(component.approvalTypeComponent.approvalTypeNumber1).toBeUndefined(); - expect(component.approvalTypeComponent.approvalTypeNumber2).toBeUndefined(); - expect(component.approvalTypeComponent.approvalTypeNumber3).toBeUndefined(); - expect(component.approvalTypeComponent.approvalTypeNumber4).toBeUndefined(); - expect(spy).toHaveBeenCalledWith(null); - }); - }); - - describe('validate', () => { - it('should, for the NTA format, call setErrors if the first approvalType number box is NOT filled in', () => { - component.approvalTypeComponent.approvalType = ApprovalType.NTA; - component.approvalTypeComponent.approvalTypeNumber1 = ''; // first number is required - component.approvalTypeComponent.validate(); - expect(component.approvalTypeComponent.errors?.error).toBe(true); - }); - - it('should, for the NTA formats, NOT call setErrors if the first approvalType number box is filled in', () => { - component.approvalTypeComponent.approvalType = ApprovalType.NTA; - component.approvalTypeComponent.approvalTypeNumber1 = 'number'; // first number is filled in, which is sufficient - component.approvalTypeComponent.validate(); - expect(component.approvalTypeComponent.errors?.error).toBeUndefined(); - }); - - it('should, for the GB_WVTA format, call setErrors if any of the first four number boxes are NOT filled in', () => { - component.approvalTypeComponent.approvalType = ApprovalType.GB_WVTA; - - // All numbers missing - component.approvalTypeComponent.approvalTypeNumber1 = ''; - component.approvalTypeComponent.validate(); - expect(component.approvalTypeComponent.errors?.error).toBe(true); - - // First number filled in, rest missing - component.approvalTypeComponent.approvalTypeNumber1 = 'number1'; - component.approvalTypeComponent.approvalTypeNumber2 = ''; - component.approvalTypeComponent.validate(); - expect(component.approvalTypeComponent.errors?.error).toBe(true); - - // First two numbers filled in, rest missing - component.approvalTypeComponent.approvalTypeNumber1 = 'number1'; - component.approvalTypeComponent.approvalTypeNumber2 = 'number2'; - component.approvalTypeComponent.approvalTypeNumber3 = ''; - component.approvalTypeComponent.validate(); - expect(component.approvalTypeComponent.errors?.error).toBe(true); - - // First three numbers filled in, rest missing - component.approvalTypeComponent.approvalTypeNumber1 = 'number1'; - component.approvalTypeComponent.approvalTypeNumber2 = 'number2'; - component.approvalTypeComponent.approvalTypeNumber3 = 'number3'; - component.approvalTypeComponent.approvalTypeNumber4 = ''; - component.approvalTypeComponent.validate(); - expect(component.approvalTypeComponent.errors?.error).toBe(true); - }); - - it('should, for the GB_WVTA format, NOT call setErrors if all of the first four number boxes are filled in', () => { - component.approvalTypeComponent.approvalType = ApprovalType.GB_WVTA; - component.approvalTypeComponent.approvalTypeNumber1 = 'number1'; - component.approvalTypeComponent.approvalTypeNumber2 = 'number2'; - component.approvalTypeComponent.approvalTypeNumber3 = 'number3'; - component.approvalTypeComponent.approvalTypeNumber4 = 'number4'; - component.approvalTypeComponent.validate(); - expect(component.approvalTypeComponent.errors?.error).toBeUndefined(); - }); - - it('should, for the NSSTA format, call setErrors if any of the first two number boxes are NOT filled in', () => { - component.approvalTypeComponent.approvalType = ApprovalType.NSSTA; - - // All numbers missing - component.approvalTypeComponent.approvalTypeNumber1 = ''; - component.approvalTypeComponent.validate(); - expect(component.approvalTypeComponent.errors?.error).toBe(true); - - // First number filled in, rest missing - component.approvalTypeComponent.approvalTypeNumber1 = 'number1'; - component.approvalTypeComponent.approvalTypeNumber2 = ''; - component.approvalTypeComponent.validate(); - expect(component.approvalTypeComponent.errors?.error).toBe(true); - }); - - it('should, for the NSSTA format, NOT call setErrors if all of the first two number boxes are filled in', () => { - component.approvalTypeComponent.approvalType = ApprovalType.NSSTA; - component.approvalTypeComponent.approvalTypeNumber1 = 'number1'; - component.approvalTypeComponent.approvalTypeNumber2 = 'number2'; - component.approvalTypeComponent.validate(); - expect(component.approvalTypeComponent.errors?.error).toBeUndefined(); - }); - }); - - describe('processApprovalTypeNumber', () => { - it('should, for NTA, return either the first approvalNumber or null if this is falsy', () => { - component.approvalTypeComponent.approvalType = ApprovalType.NTA; - const result1 = component.approvalTypeComponent.processApprovalTypeNumber('number1', undefined, undefined, undefined); - expect(result1).toBe('number1'); - - const result2 = component.approvalTypeComponent.processApprovalTypeNumber(undefined, undefined, undefined, undefined); - expect(result2).toBeNull(); - }); - - it('should, for ECTA, return either a full ECTA string if all 4 boxes are filled in, otherwise null', () => { - component.approvalTypeComponent.approvalType = ApprovalType.ECTA; - - const result1 = component.approvalTypeComponent.processApprovalTypeNumber('AA', 'BBBB', 'CCCC', 'DDDDDD'); - expect(result1).toBe('eAA*BBBB/CCCC*DDDDDD'); - - const result2 = component.approvalTypeComponent.processApprovalTypeNumber('AA', 'BBBB', 'CCCC', undefined); - expect(result2).toBeNull(); - - const result3 = component.approvalTypeComponent.processApprovalTypeNumber('AA', 'BBBB', undefined, undefined); - expect(result3).toBeNull(); - - const result4 = component.approvalTypeComponent.processApprovalTypeNumber('AA', undefined, undefined, undefined); - expect(result4).toBeNull(); - - const result5 = component.approvalTypeComponent.processApprovalTypeNumber(undefined, undefined, undefined, undefined); - expect(result5).toBeNull(); - }); + let component: HostComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ + BaseControlComponent, + ApprovalTypeInputComponent, + FieldErrorMessageComponent, + FocusNextDirective, + HostComponent, + ], + imports: [FormsModule, ReactiveFormsModule], + providers: [GlobalErrorService, provideMockStore({ initialState: initialAppState })], + }).compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(HostComponent); + component = fixture.componentInstance; + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + describe('ngOnInit', () => { + it('should initialise all the compoennts subscriptions', () => { + const spy = jest.spyOn(component.approvalTypeComponent, 'subscribeAndPropagateChanges'); + component.approvalTypeComponent.ngOnInit(); + expect(spy).toHaveBeenCalled(); + }); + }); + + describe('ngAfterViewInit', () => { + it('should write the control value back to its control', () => { + const spy = jest.spyOn(component.approvalTypeComponent, 'valueWriteBack'); + component.approvalTypeComponent.ngAfterContentInit(); + expect(spy).toHaveBeenCalled(); + }); + }); + + describe('widths', () => { + it('should return form node widths', () => { + expect(component.approvalTypeComponent.widths).toBe(FormNodeWidth); + }); + }); + + describe('clearInput', () => { + it('should set all the approval type input number boxes to undefined, and call onChange', () => { + const spy = jest.spyOn(component.approvalTypeComponent, 'onChange'); + component.approvalTypeComponent.clearInput(); + expect(component.approvalTypeComponent.approvalTypeNumber1).toBeUndefined(); + expect(component.approvalTypeComponent.approvalTypeNumber2).toBeUndefined(); + expect(component.approvalTypeComponent.approvalTypeNumber3).toBeUndefined(); + expect(component.approvalTypeComponent.approvalTypeNumber4).toBeUndefined(); + expect(spy).toHaveBeenCalledWith(null); + }); + }); + + describe('validate', () => { + it('should, for the NTA format, call setErrors if the first approvalType number box is NOT filled in', () => { + component.approvalTypeComponent.approvalType = ApprovalType.NTA; + component.approvalTypeComponent.approvalTypeNumber1 = ''; // first number is required + component.approvalTypeComponent.validate(); + expect(component.approvalTypeComponent.errors?.error).toBe(true); + }); + + it('should, for the NTA formats, NOT call setErrors if the first approvalType number box is filled in', () => { + component.approvalTypeComponent.approvalType = ApprovalType.NTA; + component.approvalTypeComponent.approvalTypeNumber1 = 'number'; // first number is filled in, which is sufficient + component.approvalTypeComponent.validate(); + expect(component.approvalTypeComponent.errors?.error).toBeUndefined(); + }); + + it('should, for the GB_WVTA format, call setErrors if any of the first four number boxes are NOT filled in', () => { + component.approvalTypeComponent.approvalType = ApprovalType.GB_WVTA; + + // All numbers missing + component.approvalTypeComponent.approvalTypeNumber1 = ''; + component.approvalTypeComponent.validate(); + expect(component.approvalTypeComponent.errors?.error).toBe(true); + + // First number filled in, rest missing + component.approvalTypeComponent.approvalTypeNumber1 = 'number1'; + component.approvalTypeComponent.approvalTypeNumber2 = ''; + component.approvalTypeComponent.validate(); + expect(component.approvalTypeComponent.errors?.error).toBe(true); + + // First two numbers filled in, rest missing + component.approvalTypeComponent.approvalTypeNumber1 = 'number1'; + component.approvalTypeComponent.approvalTypeNumber2 = 'number2'; + component.approvalTypeComponent.approvalTypeNumber3 = ''; + component.approvalTypeComponent.validate(); + expect(component.approvalTypeComponent.errors?.error).toBe(true); + + // First three numbers filled in, rest missing + component.approvalTypeComponent.approvalTypeNumber1 = 'number1'; + component.approvalTypeComponent.approvalTypeNumber2 = 'number2'; + component.approvalTypeComponent.approvalTypeNumber3 = 'number3'; + component.approvalTypeComponent.approvalTypeNumber4 = ''; + component.approvalTypeComponent.validate(); + expect(component.approvalTypeComponent.errors?.error).toBe(true); + }); + + it('should, for the GB_WVTA format, NOT call setErrors if all of the first four number boxes are filled in', () => { + component.approvalTypeComponent.approvalType = ApprovalType.GB_WVTA; + component.approvalTypeComponent.approvalTypeNumber1 = 'number1'; + component.approvalTypeComponent.approvalTypeNumber2 = 'number2'; + component.approvalTypeComponent.approvalTypeNumber3 = 'number3'; + component.approvalTypeComponent.approvalTypeNumber4 = 'number4'; + component.approvalTypeComponent.validate(); + expect(component.approvalTypeComponent.errors?.error).toBeUndefined(); + }); + + it('should, for the NSSTA format, call setErrors if any of the first two number boxes are NOT filled in', () => { + component.approvalTypeComponent.approvalType = ApprovalType.NSSTA; + + // All numbers missing + component.approvalTypeComponent.approvalTypeNumber1 = ''; + component.approvalTypeComponent.validate(); + expect(component.approvalTypeComponent.errors?.error).toBe(true); + + // First number filled in, rest missing + component.approvalTypeComponent.approvalTypeNumber1 = 'number1'; + component.approvalTypeComponent.approvalTypeNumber2 = ''; + component.approvalTypeComponent.validate(); + expect(component.approvalTypeComponent.errors?.error).toBe(true); + }); + + it('should, for the NSSTA format, NOT call setErrors if all of the first two number boxes are filled in', () => { + component.approvalTypeComponent.approvalType = ApprovalType.NSSTA; + component.approvalTypeComponent.approvalTypeNumber1 = 'number1'; + component.approvalTypeComponent.approvalTypeNumber2 = 'number2'; + component.approvalTypeComponent.validate(); + expect(component.approvalTypeComponent.errors?.error).toBeUndefined(); + }); + }); + + describe('processApprovalTypeNumber', () => { + it('should, for NTA, return either the first approvalNumber or null if this is falsy', () => { + component.approvalTypeComponent.approvalType = ApprovalType.NTA; + const result1 = component.approvalTypeComponent.processApprovalTypeNumber( + 'number1', + undefined, + undefined, + undefined + ); + expect(result1).toBe('number1'); + + const result2 = component.approvalTypeComponent.processApprovalTypeNumber( + undefined, + undefined, + undefined, + undefined + ); + expect(result2).toBeNull(); + }); + + it('should, for ECTA, return either a full ECTA string if all 4 boxes are filled in, otherwise null', () => { + component.approvalTypeComponent.approvalType = ApprovalType.ECTA; + + const result1 = component.approvalTypeComponent.processApprovalTypeNumber('AA', 'BBBB', 'CCCC', 'DDDDDD'); + expect(result1).toBe('eAA*BBBB/CCCC*DDDDDD'); + + const result2 = component.approvalTypeComponent.processApprovalTypeNumber('AA', 'BBBB', 'CCCC', undefined); + expect(result2).toBeNull(); + + const result3 = component.approvalTypeComponent.processApprovalTypeNumber('AA', 'BBBB', undefined, undefined); + expect(result3).toBeNull(); + + const result4 = component.approvalTypeComponent.processApprovalTypeNumber('AA', undefined, undefined, undefined); + expect(result4).toBeNull(); + + const result5 = component.approvalTypeComponent.processApprovalTypeNumber( + undefined, + undefined, + undefined, + undefined + ); + expect(result5).toBeNull(); + }); + + it('should, for IVA, return either the first approvalNumber or null if this is falsy', () => { + component.approvalTypeComponent.approvalType = ApprovalType.IVA; + const result1 = component.approvalTypeComponent.processApprovalTypeNumber( + 'number1', + undefined, + undefined, + undefined + ); + expect(result1).toBe('number1'); + + const result2 = component.approvalTypeComponent.processApprovalTypeNumber( + undefined, + undefined, + undefined, + undefined + ); + expect(result2).toBeNull(); + }); + + it('should, for NSSTA, return either a full NSSTA string if all 2 boxes are filled in, otherwise null', () => { + component.approvalTypeComponent.approvalType = ApprovalType.NSSTA; + + const result3 = component.approvalTypeComponent.processApprovalTypeNumber('AA', 'BBBBBB', undefined, undefined); + expect(result3).toBe('eAA*NKS*BBBBBB'); + + const result4 = component.approvalTypeComponent.processApprovalTypeNumber('AA', undefined, undefined, undefined); + expect(result4).toBeNull(); + + const result5 = component.approvalTypeComponent.processApprovalTypeNumber( + undefined, + undefined, + undefined, + undefined + ); + expect(result5).toBeNull(); + }); + + it('should, for ECSSTA, return either a full ECSSTA string if all 4 boxes are filled in, otherwise null', () => { + component.approvalTypeComponent.approvalType = ApprovalType.ECSSTA; + + const result1 = component.approvalTypeComponent.processApprovalTypeNumber('AA', 'BB', 'CCCC', 'DDDDDD'); + expect(result1).toBe('eAA*KSBB/CCCC*DDDDDD'); + + const result2 = component.approvalTypeComponent.processApprovalTypeNumber('AA', 'BB', 'CCCC', undefined); + expect(result2).toBeNull(); + + const result3 = component.approvalTypeComponent.processApprovalTypeNumber('AA', 'BB', undefined, undefined); + expect(result3).toBeNull(); + + const result4 = component.approvalTypeComponent.processApprovalTypeNumber('AA', undefined, undefined, undefined); + expect(result4).toBeNull(); + + const result5 = component.approvalTypeComponent.processApprovalTypeNumber( + undefined, + undefined, + undefined, + undefined + ); + expect(result5).toBeNull(); + }); + + it('should, for GB_WVTA, return either a full GB_WVTA string if all 4 boxes are filled in, otherwise null', () => { + component.approvalTypeComponent.approvalType = ApprovalType.GB_WVTA; + + const result1 = component.approvalTypeComponent.processApprovalTypeNumber('AAA', 'BBBB', 'CCCC', 'DDDDDDD'); + expect(result1).toBe('AAA*BBBB/CCCC*DDDDDDD'); + + const result2 = component.approvalTypeComponent.processApprovalTypeNumber('AAA', 'BBBB', 'CCCC', undefined); + expect(result2).toBeNull(); + + const result3 = component.approvalTypeComponent.processApprovalTypeNumber('AAA', 'BBBB', undefined, undefined); + expect(result3).toBeNull(); + + const result4 = component.approvalTypeComponent.processApprovalTypeNumber('AAA', undefined, undefined, undefined); + expect(result4).toBeNull(); + + const result5 = component.approvalTypeComponent.processApprovalTypeNumber( + undefined, + undefined, + undefined, + undefined + ); + expect(result5).toBeNull(); + }); + + it('should, for UKNI_WVTA, return either a full UKNI_WVTA string if all 4 boxes are filled in, otherwise null', () => { + component.approvalTypeComponent.approvalType = ApprovalType.UKNI_WVTA; + + const result1 = component.approvalTypeComponent.processApprovalTypeNumber('A', 'BBBB', 'CCCC', 'DDDDDD'); + expect(result1).toBe('A11*BBBB/CCCC*DDDDDD'); + + const result2 = component.approvalTypeComponent.processApprovalTypeNumber('A', 'BBBB', 'CCCC', undefined); + expect(result2).toBeNull(); + + const result3 = component.approvalTypeComponent.processApprovalTypeNumber('A', 'BBBB', undefined, undefined); + expect(result3).toBeNull(); + + const result4 = component.approvalTypeComponent.processApprovalTypeNumber('A', undefined, undefined, undefined); + expect(result4).toBeNull(); + + const result5 = component.approvalTypeComponent.processApprovalTypeNumber( + undefined, + undefined, + undefined, + undefined + ); + expect(result5).toBeNull(); + }); + + it('should, for EU_WVTA_PRE_23, return either a full EU_WVTA_PRE_23 string if all 4 boxes are filled in, otherwise null', () => { + component.approvalTypeComponent.approvalType = ApprovalType.EU_WVTA_PRE_23; + + const result1 = component.approvalTypeComponent.processApprovalTypeNumber('AA', 'BBBB', 'CCCC', 'DDDDDD'); + expect(result1).toBe('eAA*BBBB/CCCC*DDDDDD'); - it('should, for IVA, return either the first approvalNumber or null if this is falsy', () => { - component.approvalTypeComponent.approvalType = ApprovalType.IVA; - const result1 = component.approvalTypeComponent.processApprovalTypeNumber('number1', undefined, undefined, undefined); - expect(result1).toBe('number1'); + const result2 = component.approvalTypeComponent.processApprovalTypeNumber('AA', 'BBBB', 'CCCC', undefined); + expect(result2).toBeNull(); - const result2 = component.approvalTypeComponent.processApprovalTypeNumber(undefined, undefined, undefined, undefined); - expect(result2).toBeNull(); - }); + const result3 = component.approvalTypeComponent.processApprovalTypeNumber('AA', 'BBBB', undefined, undefined); + expect(result3).toBeNull(); - it('should, for NSSTA, return either a full NSSTA string if all 2 boxes are filled in, otherwise null', () => { - component.approvalTypeComponent.approvalType = ApprovalType.NSSTA; + const result4 = component.approvalTypeComponent.processApprovalTypeNumber('AA', undefined, undefined, undefined); + expect(result4).toBeNull(); - const result3 = component.approvalTypeComponent.processApprovalTypeNumber('AA', 'BBBBBB', undefined, undefined); - expect(result3).toBe('eAA*NKS*BBBBBB'); + const result5 = component.approvalTypeComponent.processApprovalTypeNumber( + undefined, + undefined, + undefined, + undefined + ); + expect(result5).toBeNull(); + }); - const result4 = component.approvalTypeComponent.processApprovalTypeNumber('AA', undefined, undefined, undefined); - expect(result4).toBeNull(); + it('should, for EU_WVTA_23_ON, return either a full EU_WVTA_23_ON string if all 4 boxes are filled in, otherwise null', () => { + component.approvalTypeComponent.approvalType = ApprovalType.EU_WVTA_23_ON; - const result5 = component.approvalTypeComponent.processApprovalTypeNumber(undefined, undefined, undefined, undefined); - expect(result5).toBeNull(); - }); + const result1 = component.approvalTypeComponent.processApprovalTypeNumber('AA', 'BBBB', 'CCCC', 'DDDDDD'); + expect(result1).toBe('eAA*BBBB/CCCC*DDDDDD'); - it('should, for ECSSTA, return either a full ECSSTA string if all 4 boxes are filled in, otherwise null', () => { - component.approvalTypeComponent.approvalType = ApprovalType.ECSSTA; + const result2 = component.approvalTypeComponent.processApprovalTypeNumber('AA', 'BBBB', 'CCCC', undefined); + expect(result2).toBeNull(); - const result1 = component.approvalTypeComponent.processApprovalTypeNumber('AA', 'BB', 'CCCC', 'DDDDDD'); - expect(result1).toBe('eAA*KSBB/CCCC*DDDDDD'); + const result3 = component.approvalTypeComponent.processApprovalTypeNumber('AA', 'BBBB', undefined, undefined); + expect(result3).toBeNull(); - const result2 = component.approvalTypeComponent.processApprovalTypeNumber('AA', 'BB', 'CCCC', undefined); - expect(result2).toBeNull(); + const result4 = component.approvalTypeComponent.processApprovalTypeNumber('AA', undefined, undefined, undefined); + expect(result4).toBeNull(); - const result3 = component.approvalTypeComponent.processApprovalTypeNumber('AA', 'BB', undefined, undefined); - expect(result3).toBeNull(); + const result5 = component.approvalTypeComponent.processApprovalTypeNumber( + undefined, + undefined, + undefined, + undefined + ); + expect(result5).toBeNull(); + }); - const result4 = component.approvalTypeComponent.processApprovalTypeNumber('AA', undefined, undefined, undefined); - expect(result4).toBeNull(); + it('should, for QNIG, return either a full QNIG string if all 4 boxes are filled in, otherwise null', () => { + component.approvalTypeComponent.approvalType = ApprovalType.QNIG; - const result5 = component.approvalTypeComponent.processApprovalTypeNumber(undefined, undefined, undefined, undefined); - expect(result5).toBeNull(); - }); + const result1 = component.approvalTypeComponent.processApprovalTypeNumber('AA', 'BBBB', 'CCCC', 'DDDDDD'); + expect(result1).toBe('eAA*BBBB/CCCC*DDDDDD'); - it('should, for GB_WVTA, return either a full GB_WVTA string if all 4 boxes are filled in, otherwise null', () => { - component.approvalTypeComponent.approvalType = ApprovalType.GB_WVTA; + const result2 = component.approvalTypeComponent.processApprovalTypeNumber('AA', 'BBBB', 'CCCC', undefined); + expect(result2).toBeNull(); - const result1 = component.approvalTypeComponent.processApprovalTypeNumber('AAA', 'BBBB', 'CCCC', 'DDDDDDD'); - expect(result1).toBe('AAA*BBBB/CCCC*DDDDDDD'); + const result3 = component.approvalTypeComponent.processApprovalTypeNumber('AA', 'BBBB', undefined, undefined); + expect(result3).toBeNull(); - const result2 = component.approvalTypeComponent.processApprovalTypeNumber('AAA', 'BBBB', 'CCCC', undefined); - expect(result2).toBeNull(); - - const result3 = component.approvalTypeComponent.processApprovalTypeNumber('AAA', 'BBBB', undefined, undefined); - expect(result3).toBeNull(); - - const result4 = component.approvalTypeComponent.processApprovalTypeNumber('AAA', undefined, undefined, undefined); - expect(result4).toBeNull(); - - const result5 = component.approvalTypeComponent.processApprovalTypeNumber(undefined, undefined, undefined, undefined); - expect(result5).toBeNull(); - }); - - it('should, for UKNI_WVTA, return either a full UKNI_WVTA string if all 4 boxes are filled in, otherwise null', () => { - component.approvalTypeComponent.approvalType = ApprovalType.UKNI_WVTA; - - const result1 = component.approvalTypeComponent.processApprovalTypeNumber('A', 'BBBB', 'CCCC', 'DDDDDD'); - expect(result1).toBe('A11*BBBB/CCCC*DDDDDD'); - - const result2 = component.approvalTypeComponent.processApprovalTypeNumber('A', 'BBBB', 'CCCC', undefined); - expect(result2).toBeNull(); - - const result3 = component.approvalTypeComponent.processApprovalTypeNumber('A', 'BBBB', undefined, undefined); - expect(result3).toBeNull(); - - const result4 = component.approvalTypeComponent.processApprovalTypeNumber('A', undefined, undefined, undefined); - expect(result4).toBeNull(); - - const result5 = component.approvalTypeComponent.processApprovalTypeNumber(undefined, undefined, undefined, undefined); - expect(result5).toBeNull(); - }); - - it('should, for EU_WVTA_PRE_23, return either a full EU_WVTA_PRE_23 string if all 4 boxes are filled in, otherwise null', () => { - component.approvalTypeComponent.approvalType = ApprovalType.EU_WVTA_PRE_23; - - const result1 = component.approvalTypeComponent.processApprovalTypeNumber('AA', 'BBBB', 'CCCC', 'DDDDDD'); - expect(result1).toBe('eAA*BBBB/CCCC*DDDDDD'); - - const result2 = component.approvalTypeComponent.processApprovalTypeNumber('AA', 'BBBB', 'CCCC', undefined); - expect(result2).toBeNull(); - - const result3 = component.approvalTypeComponent.processApprovalTypeNumber('AA', 'BBBB', undefined, undefined); - expect(result3).toBeNull(); - - const result4 = component.approvalTypeComponent.processApprovalTypeNumber('AA', undefined, undefined, undefined); - expect(result4).toBeNull(); - - const result5 = component.approvalTypeComponent.processApprovalTypeNumber(undefined, undefined, undefined, undefined); - expect(result5).toBeNull(); - }); - - it('should, for EU_WVTA_23_ON, return either a full EU_WVTA_23_ON string if all 4 boxes are filled in, otherwise null', () => { - component.approvalTypeComponent.approvalType = ApprovalType.EU_WVTA_23_ON; - - const result1 = component.approvalTypeComponent.processApprovalTypeNumber('AA', 'BBBB', 'CCCC', 'DDDDDD'); - expect(result1).toBe('eAA*BBBB/CCCC*DDDDDD'); - - const result2 = component.approvalTypeComponent.processApprovalTypeNumber('AA', 'BBBB', 'CCCC', undefined); - expect(result2).toBeNull(); - - const result3 = component.approvalTypeComponent.processApprovalTypeNumber('AA', 'BBBB', undefined, undefined); - expect(result3).toBeNull(); - - const result4 = component.approvalTypeComponent.processApprovalTypeNumber('AA', undefined, undefined, undefined); - expect(result4).toBeNull(); - - const result5 = component.approvalTypeComponent.processApprovalTypeNumber(undefined, undefined, undefined, undefined); - expect(result5).toBeNull(); - }); - - it('should, for QNIG, return either a full QNIG string if all 4 boxes are filled in, otherwise null', () => { - component.approvalTypeComponent.approvalType = ApprovalType.QNIG; - - const result1 = component.approvalTypeComponent.processApprovalTypeNumber('AA', 'BBBB', 'CCCC', 'DDDDDD'); - expect(result1).toBe('eAA*BBBB/CCCC*DDDDDD'); - - const result2 = component.approvalTypeComponent.processApprovalTypeNumber('AA', 'BBBB', 'CCCC', undefined); - expect(result2).toBeNull(); - - const result3 = component.approvalTypeComponent.processApprovalTypeNumber('AA', 'BBBB', undefined, undefined); - expect(result3).toBeNull(); - - const result4 = component.approvalTypeComponent.processApprovalTypeNumber('AA', undefined, undefined, undefined); - expect(result4).toBeNull(); - - const result5 = component.approvalTypeComponent.processApprovalTypeNumber(undefined, undefined, undefined, undefined); - expect(result5).toBeNull(); - }); - - it('should, for PROV_GB_WVTA, return either a full PROV_GB_WVTA string if all 4 boxes are filled in, otherwise null', () => { - component.approvalTypeComponent.approvalType = ApprovalType.PROV_GB_WVTA; - - const result1 = component.approvalTypeComponent.processApprovalTypeNumber('AAA', 'BBBB', 'CCCC', 'DDDDDD'); - expect(result1).toBe('AAA*BBBB/CCCC*DDDDDD'); - - const result2 = component.approvalTypeComponent.processApprovalTypeNumber('AAA', 'BBBB', 'CCCC', undefined); - expect(result2).toBeNull(); - - const result3 = component.approvalTypeComponent.processApprovalTypeNumber('AAA', 'BBBB', undefined, undefined); - expect(result3).toBeNull(); - - const result4 = component.approvalTypeComponent.processApprovalTypeNumber('AAA', undefined, undefined, undefined); - expect(result4).toBeNull(); - - const result5 = component.approvalTypeComponent.processApprovalTypeNumber(undefined, undefined, undefined, undefined); - expect(result5).toBeNull(); - }); - - it('should, for SMALL_SERIES_NKSXX, return either a full SMALL_SERIES_NKSXX string if all 4 boxes are filled in, otherwise null', () => { - component.approvalTypeComponent.approvalType = ApprovalType.SMALL_SERIES_NKSXX; - - const result2 = component.approvalTypeComponent.processApprovalTypeNumber('XX', 'AA', 'BBBB', 'CCCCCC'); - expect(result2).toBe('XX11*NKSAA/BBBB*CCCCCC'); - - const result4 = component.approvalTypeComponent.processApprovalTypeNumber('XX', 'AA', undefined, undefined); - expect(result4).toBeNull(); - - const result5 = component.approvalTypeComponent.processApprovalTypeNumber('XX', undefined, undefined, undefined); - expect(result5).toBeNull(); - }); - - it('should, for SMALL_SERIES_NKS, return either a full SMALL_SERIES_NKS string if all 4 boxes are filled in, otherwise null', () => { - component.approvalTypeComponent.approvalType = ApprovalType.SMALL_SERIES_NKS; - - const result2 = component.approvalTypeComponent.processApprovalTypeNumber('XX', 'AA', 'BBBB', 'CCCCCC'); - expect(result2).toBe('XX11*NKS*AA'); - - const result4 = component.approvalTypeComponent.processApprovalTypeNumber('XX', 'AA', undefined, undefined); - expect(result4).toBe('XX11*NKS*AA'); - - const result5 = component.approvalTypeComponent.processApprovalTypeNumber('XX', undefined, undefined, undefined); - expect(result5).toBeNull(); - }); - - it('should, for IVA_VCA, return either a full IVA_VCA string if all 4 boxes are filled in, otherwise null', () => { - component.approvalTypeComponent.approvalType = ApprovalType.IVA_VCA; - - const result2 = component.approvalTypeComponent.processApprovalTypeNumber('XX', 'AA', 'BBBB', 'CCCCCC'); - expect(result2).toBe('n11*NIVXX/AA*BBBB'); - - const result5 = component.approvalTypeComponent.processApprovalTypeNumber('XX', undefined, undefined, undefined); - expect(result5).toBeNull(); - }); - }); - - describe('getId', () => { - it('should format the id of the control given its meta name', () => { - component.approvalTypeComponent.approvalType = ApprovalType.NSSTA; - - const result = component.approvalTypeComponent.getId('name'); - expect(result).toBe('name-approvalTypeNumber1-NSSTA'); - }); - }); - - describe('set approval type numbers', () => { - // the following are added for test coverage as these methods just next subjects - it('should return void for onTechRecord_approvalTypeNumber1_Change', () => { - const result = component.approvalTypeComponent.onTechRecord_approvalTypeNumber1_Change('number'); - expect(result).toBeUndefined(); - }); - - it('should return void for onTechRecord_approvalTypeNumber2_Change', () => { - const result = component.approvalTypeComponent.onTechRecord_approvalTypeNumber2_Change('number'); - expect(result).toBeUndefined(); - }); - - it('should return void for onTechRecord_approvalTypeNumber3_Change', () => { - const result = component.approvalTypeComponent.onTechRecord_approvalTypeNumber3_Change('number'); - expect(result).toBeUndefined(); - }); - - it('should return void for onTechRecord_approvalTypeNumber4_Change', () => { - const result = component.approvalTypeComponent.onTechRecord_approvalTypeNumber4_Change('number'); - expect(result).toBeUndefined(); - }); - }); - - describe('valueWriteBack', () => { - it('should return if a falsy value is passed', () => { - const result = component.approvalTypeComponent.valueWriteBack(null); - expect(result).toBeUndefined(); - }); - - it('should return if the component is missing an approvalType', () => { - component.approvalTypeComponent.approvalType = undefined; - const result = component.approvalTypeComponent.valueWriteBack(null); - expect(result).toBeUndefined(); - }); - }); + const result4 = component.approvalTypeComponent.processApprovalTypeNumber('AA', undefined, undefined, undefined); + expect(result4).toBeNull(); + + const result5 = component.approvalTypeComponent.processApprovalTypeNumber( + undefined, + undefined, + undefined, + undefined + ); + expect(result5).toBeNull(); + }); + + it('should, for PROV_GB_WVTA, return either a full PROV_GB_WVTA string if all 4 boxes are filled in, otherwise null', () => { + component.approvalTypeComponent.approvalType = ApprovalType.PROV_GB_WVTA; + + const result1 = component.approvalTypeComponent.processApprovalTypeNumber('AAA', 'BBBB', 'CCCC', 'DDDDDD'); + expect(result1).toBe('AAA*BBBB/CCCC*DDDDDD'); + + const result2 = component.approvalTypeComponent.processApprovalTypeNumber('AAA', 'BBBB', 'CCCC', undefined); + expect(result2).toBeNull(); + + const result3 = component.approvalTypeComponent.processApprovalTypeNumber('AAA', 'BBBB', undefined, undefined); + expect(result3).toBeNull(); + + const result4 = component.approvalTypeComponent.processApprovalTypeNumber('AAA', undefined, undefined, undefined); + expect(result4).toBeNull(); + + const result5 = component.approvalTypeComponent.processApprovalTypeNumber( + undefined, + undefined, + undefined, + undefined + ); + expect(result5).toBeNull(); + }); + + it('should, for SMALL_SERIES_NKSXX, return either a full SMALL_SERIES_NKSXX string if all 4 boxes are filled in, otherwise null', () => { + component.approvalTypeComponent.approvalType = ApprovalType.SMALL_SERIES_NKSXX; + + const result2 = component.approvalTypeComponent.processApprovalTypeNumber('XX', 'AA', 'BBBB', 'CCCCCC'); + expect(result2).toBe('XX11*NKSAA/BBBB*CCCCCC'); + + const result4 = component.approvalTypeComponent.processApprovalTypeNumber('XX', 'AA', undefined, undefined); + expect(result4).toBeNull(); + + const result5 = component.approvalTypeComponent.processApprovalTypeNumber('XX', undefined, undefined, undefined); + expect(result5).toBeNull(); + }); + + it('should, for SMALL_SERIES_NKS, return either a full SMALL_SERIES_NKS string if all 4 boxes are filled in, otherwise null', () => { + component.approvalTypeComponent.approvalType = ApprovalType.SMALL_SERIES_NKS; + + const result2 = component.approvalTypeComponent.processApprovalTypeNumber('XX', 'AA', 'BBBB', 'CCCCCC'); + expect(result2).toBe('XX11*NKS*AA'); + + const result4 = component.approvalTypeComponent.processApprovalTypeNumber('XX', 'AA', undefined, undefined); + expect(result4).toBe('XX11*NKS*AA'); + + const result5 = component.approvalTypeComponent.processApprovalTypeNumber('XX', undefined, undefined, undefined); + expect(result5).toBeNull(); + }); + + it('should, for IVA_VCA, return either a full IVA_VCA string if all 4 boxes are filled in, otherwise null', () => { + component.approvalTypeComponent.approvalType = ApprovalType.IVA_VCA; + + const result2 = component.approvalTypeComponent.processApprovalTypeNumber('XX', 'AA', 'BBBB', 'CCCCCC'); + expect(result2).toBe('n11*NIVXX/AA*BBBB'); + + const result5 = component.approvalTypeComponent.processApprovalTypeNumber('XX', undefined, undefined, undefined); + expect(result5).toBeNull(); + }); + }); + + describe('getId', () => { + it('should format the id of the control given its meta name', () => { + component.approvalTypeComponent.approvalType = ApprovalType.NSSTA; + + const result = component.approvalTypeComponent.getId('name'); + expect(result).toBe('name-approvalTypeNumber1-NSSTA'); + }); + }); + + describe('set approval type numbers', () => { + // the following are added for test coverage as these methods just next subjects + it('should return void for onTechRecord_approvalTypeNumber1_Change', () => { + const result = component.approvalTypeComponent.onTechRecord_approvalTypeNumber1_Change('number'); + expect(result).toBeUndefined(); + }); + + it('should return void for onTechRecord_approvalTypeNumber2_Change', () => { + const result = component.approvalTypeComponent.onTechRecord_approvalTypeNumber2_Change('number'); + expect(result).toBeUndefined(); + }); + + it('should return void for onTechRecord_approvalTypeNumber3_Change', () => { + const result = component.approvalTypeComponent.onTechRecord_approvalTypeNumber3_Change('number'); + expect(result).toBeUndefined(); + }); + + it('should return void for onTechRecord_approvalTypeNumber4_Change', () => { + const result = component.approvalTypeComponent.onTechRecord_approvalTypeNumber4_Change('number'); + expect(result).toBeUndefined(); + }); + }); + + describe('valueWriteBack', () => { + it('should return if a falsy value is passed', () => { + const result = component.approvalTypeComponent.valueWriteBack(null); + expect(result).toBeUndefined(); + }); + + it('should return if the component is missing an approvalType', () => { + component.approvalTypeComponent.approvalType = undefined; + const result = component.approvalTypeComponent.valueWriteBack(null); + expect(result).toBeUndefined(); + }); + }); }); diff --git a/src/app/forms/components/approval-type/approval-type.component.ts b/src/app/forms/components/approval-type/approval-type.component.ts index 43a8e258c9..4d4ae1a8be 100644 --- a/src/app/forms/components/approval-type/approval-type.component.ts +++ b/src/app/forms/components/approval-type/approval-type.component.ts @@ -1,454 +1,486 @@ import { - AfterContentInit, ChangeDetectorRef, Component, Injector, Input, OnChanges, OnDestroy, OnInit, + AfterContentInit, + ChangeDetectorRef, + Component, + Injector, + Input, + OnChanges, + OnDestroy, + OnInit, } from '@angular/core'; import { NG_VALUE_ACCESSOR } from '@angular/forms'; import { GlobalErrorService } from '@core/components/global-error/global-error.service'; import { ApprovalType } from '@dvsa/cvs-type-definitions/types/v3/tech-record/enums/approvalType.enum.js'; import { FormNodeWidth } from '@forms/services/dynamic-form.types'; -import { - BehaviorSubject, Observable, Subscription, combineLatest, -} from 'rxjs'; +import { BehaviorSubject, Observable, Subscription, combineLatest } from 'rxjs'; import { BaseControlComponent } from '../base-control/base-control.component'; const patterns: Record = { - NTA: /^(.+)$/i, // 25 - 'IVA - DVSA/NI': /^(.+)$/i, // 25 - IVA: /^(.+)$/i, // 25 - ECTA: /^e(.{2})\*(.{4})\/(.{4})\*(.{6})$/i, // 20 - NSSTA: /^e(.{2})\*NKS\*(.{6})$/i, // 14 - ECSSTA: /^e(.{2})\*KS(.{2})\/(.{4})\*(.{6})$/i, // 18 - 'GB WVTA': /^(.{3})\*(.{4})\/(.{4})\*(.{7})$/i, // 21 - 'UKNI WVTA': /^(.{1})11\*(.{4})\/(.{4})\*(.{6})$/i, // 20 - 'EU WVTA Pre 23': /^e(.{2})\*(.{4})\/(.{4})\*(.{6})$/i, // 20 - 'EU WVTA 23 on': /^e(.{2})\*(.{4})\/(.{4})\*(.{6})$/i, // 20 - QNIG: /^e(.{2})\*(.{4})\/(.{4})\*(.{6})$/i, // 20 - 'Prov.GB WVTA': /^(.{3})\*(.{4})\/(.{4})\*(.{6})$/i, // 20 - 'Small series NKSXX': /^(.?)11\*NKS(.{0,2})\/(.{0,4})\*(.{0,6})$/i, // 25 - 'Small series NKS': /^(.?)11\*NKS\*(.{0,6})$/i, // 23 - 'IVA - VCA': /^n11\*NIV(.{2})\/(.{4})\*(.{6})$/i, // 19 + NTA: /^(.+)$/i, // 25 + 'IVA - DVSA/NI': /^(.+)$/i, // 25 + IVA: /^(.+)$/i, // 25 + ECTA: /^e(.{2})\*(.{4})\/(.{4})\*(.{6})$/i, // 20 + NSSTA: /^e(.{2})\*NKS\*(.{6})$/i, // 14 + ECSSTA: /^e(.{2})\*KS(.{2})\/(.{4})\*(.{6})$/i, // 18 + 'GB WVTA': /^(.{3})\*(.{4})\/(.{4})\*(.{7})$/i, // 21 + 'UKNI WVTA': /^(.{1})11\*(.{4})\/(.{4})\*(.{6})$/i, // 20 + 'EU WVTA Pre 23': /^e(.{2})\*(.{4})\/(.{4})\*(.{6})$/i, // 20 + 'EU WVTA 23 on': /^e(.{2})\*(.{4})\/(.{4})\*(.{6})$/i, // 20 + QNIG: /^e(.{2})\*(.{4})\/(.{4})\*(.{6})$/i, // 20 + 'Prov.GB WVTA': /^(.{3})\*(.{4})\/(.{4})\*(.{6})$/i, // 20 + 'Small series NKSXX': /^(.?)11\*NKS(.{0,2})\/(.{0,4})\*(.{0,6})$/i, // 25 + 'Small series NKS': /^(.?)11\*NKS\*(.{0,6})$/i, // 23 + 'IVA - VCA': /^n11\*NIV(.{2})\/(.{4})\*(.{6})$/i, // 19 }; const patternsPartialMatch: Record = { - NTA: /^(.+)$/i, - 'IVA - DVSA/NI': /^(.+)$/i, - IVA: /^(.+)$/i, - ECTA: /^e(.{0,2})\*(.{0,4})\/(.{0,4})\*(.{0,6})$/i, - NSSTA: /^e(.{0,2})\*NKS\*(.{0,6})$/i, - ECSSTA: /^e(.{0,2})\*KS(.{0,2})\/(.{0,4})\*(.{0,6})$/i, - 'GB WVTA': /^(.{0,3})\*(.{0,4})\/(.{0,4})\*(.{0,7})$/i, - 'UKNI WVTA': /^(.?)11\*(.{0,4})\/(.{0,4})\*(.{0,6})$/i, - 'EU WVTA Pre 23': /^e(.{0,2})\*(.{0,4})\/(.{0,4})\*(.{0,6})$/i, - 'EU WVTA 23 on': /^e(.{0,2})\*(.{0,4})\/(.{0,4})\*(.{0,6})$/i, - QNIG: /^e(.{0,2})\*(.{0,4})\/(.{0,4})\*(.{0,6})$/i, - 'Prov.GB WVTA': /^(.{0,3})\*(.{0,4})\/(.{0,4})\*(.{0,6})$/i, - 'Small series NKSXX': /^(.?)11\*NKS(.{0,2})\/(.{0,4})\*(.{0,6})$/i, // 25 - 'Small series NKS': /^(.?)11\*NKS\*(.{0,6})$/i, // 23 - 'IVA - VCA': /^n11\*NIV(.{0,2})\/(.{0,4})\*(.{0,6})$/i, + NTA: /^(.+)$/i, + 'IVA - DVSA/NI': /^(.+)$/i, + IVA: /^(.+)$/i, + ECTA: /^e(.{0,2})\*(.{0,4})\/(.{0,4})\*(.{0,6})$/i, + NSSTA: /^e(.{0,2})\*NKS\*(.{0,6})$/i, + ECSSTA: /^e(.{0,2})\*KS(.{0,2})\/(.{0,4})\*(.{0,6})$/i, + 'GB WVTA': /^(.{0,3})\*(.{0,4})\/(.{0,4})\*(.{0,7})$/i, + 'UKNI WVTA': /^(.?)11\*(.{0,4})\/(.{0,4})\*(.{0,6})$/i, + 'EU WVTA Pre 23': /^e(.{0,2})\*(.{0,4})\/(.{0,4})\*(.{0,6})$/i, + 'EU WVTA 23 on': /^e(.{0,2})\*(.{0,4})\/(.{0,4})\*(.{0,6})$/i, + QNIG: /^e(.{0,2})\*(.{0,4})\/(.{0,4})\*(.{0,6})$/i, + 'Prov.GB WVTA': /^(.{0,3})\*(.{0,4})\/(.{0,4})\*(.{0,6})$/i, + 'Small series NKSXX': /^(.?)11\*NKS(.{0,2})\/(.{0,4})\*(.{0,6})$/i, // 25 + 'Small series NKS': /^(.?)11\*NKS\*(.{0,6})$/i, // 23 + 'IVA - VCA': /^n11\*NIV(.{0,2})\/(.{0,4})\*(.{0,6})$/i, }; const patternsGenericPartialMatch: Record = { - NTA: /^(.+)$/i, - 'IVA - DVSA/NI': /^(.+)$/i, - IVA: /^(.+)$/i, - ECTA: /^(.{0,2})(.{0,4})(.{0,4})(.{0,6})$/i, - NSSTA: /^(.{0,2})(.{0,6})$/i, - ECSSTA: /^(.{0,2})(.{0,2})(.{0,4})(.{0,6})$/i, - 'GB WVTA': /^(.{0,3})(.{0,4})(.{0,4})(.{0,7})$/i, - 'UKNI WVTA': /^(.?)(.{0,4})(.{0,4})(.{0,6})$/i, - 'EU WVTA Pre 23': /^(.{0,2})(.{0,4})(.{0,4})(.{0,6})$/i, - 'EU WVTA 23 on': /^(.{0,2})(.{0,4})(.{0,4})(.{0,6})$/i, - QNIG: /^(.{0,2})(.{0,4})(.{0,4})(.{0,6})$/i, - 'Prov.GB WVTA': /^(.{0,3})(.{0,4})(.{0,4})(.{0,6})$/i, - 'Small series NKSXX': /^(.?)11\*NKS(.{0,2})\/(.{0,4})\*(.{0,6})$/i, // 25 - 'Small series NKS': /^(.?)11\*NKS\*(.{0,6})$/i, // 23 - 'IVA - VCA': /^(.{0,2})(.{0,4})(.{0,6})$/i, + NTA: /^(.+)$/i, + 'IVA - DVSA/NI': /^(.+)$/i, + IVA: /^(.+)$/i, + ECTA: /^(.{0,2})(.{0,4})(.{0,4})(.{0,6})$/i, + NSSTA: /^(.{0,2})(.{0,6})$/i, + ECSSTA: /^(.{0,2})(.{0,2})(.{0,4})(.{0,6})$/i, + 'GB WVTA': /^(.{0,3})(.{0,4})(.{0,4})(.{0,7})$/i, + 'UKNI WVTA': /^(.?)(.{0,4})(.{0,4})(.{0,6})$/i, + 'EU WVTA Pre 23': /^(.{0,2})(.{0,4})(.{0,4})(.{0,6})$/i, + 'EU WVTA 23 on': /^(.{0,2})(.{0,4})(.{0,4})(.{0,6})$/i, + QNIG: /^(.{0,2})(.{0,4})(.{0,4})(.{0,6})$/i, + 'Prov.GB WVTA': /^(.{0,3})(.{0,4})(.{0,4})(.{0,6})$/i, + 'Small series NKSXX': /^(.?)11\*NKS(.{0,2})\/(.{0,4})\*(.{0,6})$/i, // 25 + 'Small series NKS': /^(.?)11\*NKS\*(.{0,6})$/i, // 23 + 'IVA - VCA': /^(.{0,2})(.{0,4})(.{0,6})$/i, }; const characterLimit: Record = { - NTA: 25, - 'IVA - DVSA/NI': 25, - IVA: 25, - ECTA: 20, - NSSTA: 14, - ECSSTA: 18, - 'GB WVTA': 21, - 'UKNI WVTA': 20, - 'EU WVTA Pre 23': 20, - 'EU WVTA 23 on': 20, - QNIG: 20, - 'Prov.GB WVTA': 20, - 'Small series NKSXX': 25, - 'Small series NKS': 23, - 'IVA - VCA': 19, + NTA: 25, + 'IVA - DVSA/NI': 25, + IVA: 25, + ECTA: 20, + NSSTA: 14, + ECSSTA: 18, + 'GB WVTA': 21, + 'UKNI WVTA': 20, + 'EU WVTA Pre 23': 20, + 'EU WVTA 23 on': 20, + QNIG: 20, + 'Prov.GB WVTA': 20, + 'Small series NKSXX': 25, + 'Small series NKS': 23, + 'IVA - VCA': 19, }; const characterLimitGeneric: Record = { - NTA: 25, - 'IVA - DVSA/NI': 25, - IVA: 25, - ECTA: 16, - NSSTA: 8, - ECSSTA: 14, - 'GB WVTA': 18, - 'UKNI WVTA': 15, - 'EU WVTA Pre 23': 16, - 'EU WVTA 23 on': 16, - QNIG: 16, - 'Prov.GB WVTA': 17, - 'Small series NKSXX': 25, - 'Small series NKS': 23, - 'IVA - VCA': 12, + NTA: 25, + 'IVA - DVSA/NI': 25, + IVA: 25, + ECTA: 16, + NSSTA: 8, + ECSSTA: 14, + 'GB WVTA': 18, + 'UKNI WVTA': 15, + 'EU WVTA Pre 23': 16, + 'EU WVTA 23 on': 16, + QNIG: 16, + 'Prov.GB WVTA': 17, + 'Small series NKSXX': 25, + 'Small series NKS': 23, + 'IVA - VCA': 12, }; @Component({ - selector: './app-approval-type-input', - templateUrl: './approval-type.component.html', - styleUrls: ['./approval-type.component.scss'], - providers: [ - { - provide: NG_VALUE_ACCESSOR, - useExisting: ApprovalTypeInputComponent, - multi: true, - }, - ], + selector: './app-approval-type-input', + templateUrl: './approval-type.component.html', + styleUrls: ['./approval-type.component.scss'], + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: ApprovalTypeInputComponent, + multi: true, + }, + ], }) -export class ApprovalTypeInputComponent extends BaseControlComponent implements OnChanges, OnInit, OnDestroy, AfterContentInit { - @Input() isEditing = false; - @Input() approvalType?: string; - @Input() approvalTypeChange: boolean | undefined; - - private approvalTypeNumber_1: BehaviorSubject = new BehaviorSubject(undefined); - private approvalTypeNumber_2: BehaviorSubject = new BehaviorSubject(undefined); - private approvalTypeNumber_3: BehaviorSubject = new BehaviorSubject(undefined); - private approvalTypeNumber_4: BehaviorSubject = new BehaviorSubject(undefined); - - private approvalTypeNumber1$: Observable; - private approvalTypeNumber2$: Observable; - private approvalTypeNumber3$: Observable; - private approvalTypeNumber4$: Observable; - - private subscriptions: Array = []; - public errors?: { - error: boolean; - errors?: { - error: boolean; - reason: string; - index: number; - }[]; - }; - protected formSubmitted? = false; - public approvalTypeNumber1?: string; - public approvalTypeNumber2?: string; - public approvalTypeNumber3?: string; - public approvalTypeNumber4?: string; - - constructor(injector: Injector, changeDetectorRef: ChangeDetectorRef, public globalErrorService: GlobalErrorService) { - super(injector, changeDetectorRef); - this.approvalTypeNumber1$ = this.approvalTypeNumber_1.asObservable(); - this.approvalTypeNumber2$ = this.approvalTypeNumber_2.asObservable(); - this.approvalTypeNumber3$ = this.approvalTypeNumber_3.asObservable(); - this.approvalTypeNumber4$ = this.approvalTypeNumber_4.asObservable(); - this.globalErrorService.errors$.subscribe((globalErrors) => { - if (globalErrors.length) { - this.formSubmitted = true; - } - }); - } - - ngOnChanges(): void { - this.valueWriteBack(this.value); - - if (this.approvalTypeChange) { - this.clearInput(); - } - } - - ngOnInit(): void { - this.subscriptions.push(this.subscribeAndPropagateChanges()); - } - - override ngAfterContentInit(): void { - super.ngAfterContentInit(); - this.valueWriteBack(this.value); - } - - ngOnDestroy(): void { - this.subscriptions.forEach((s) => s && s.unsubscribe()); - } - - onTechRecord_approvalTypeNumber1_Change(event: string | undefined) { - this.approvalTypeNumber_1.next(event); - } - - onTechRecord_approvalTypeNumber2_Change(event: string | undefined) { - this.approvalTypeNumber_2.next(event); - } - - onTechRecord_approvalTypeNumber3_Change(event: string | undefined) { - this.approvalTypeNumber_3.next(event); - } - - onTechRecord_approvalTypeNumber4_Change(event: string | undefined) { - this.approvalTypeNumber_4.next(event); - } - - valueWriteBack(value: string | null): void { - if (!value || !this.approvalType) { - return; - } - - const NTA_IVA_Types = ['NTA', 'IVA', 'IVA - DVSA/NI']; - const OtherTypes = [ - 'ECTA', - 'NSSTA', - 'ECSSTA', - 'GB WVTA', - 'UKNI WVTA', - 'EU WVTA Pre 23', - 'EU WVTA 23 on', - 'QNIG', - 'Prov.GB WVTA', - 'IVA - VCA', - 'Small series NKSXX', - 'Small series NKS', - ]; - - if (NTA_IVA_Types.includes(this.approvalType)) { - this.extractValuesNtaIva(value, patterns[this.approvalType]); - } else if (OtherTypes.includes(this.approvalType)) { - this.extractValues(value); - } - } - - private extractValues(value: string): void { - if (!value || !this.approvalType) return; - - const getMatchedValues = (_value: string, pattern: RegExp) => { - const result = _value.match(pattern); - return result ? result.slice(1) : []; - }; - - const pattern = patterns[this.approvalType]; - const patternPartial = patternsPartialMatch[this.approvalType] || /^$/; - - let matches = getMatchedValues(value, pattern); - if (matches) { - this.setTypeApprovalNumbers(matches.filter((x) => x)); - } - - if (!matches.length) { - const limit = characterLimit[this.approvalType]; - const limitedValue = value.length > limit ? value.substring(0, limit) : value; - matches = getMatchedValues(limitedValue, patternPartial); - this.setTypeApprovalNumbers(matches.filter((x) => x)); - } - - if (!matches.length) { - const limit = characterLimitGeneric[this.approvalType]; - const limitedValue = value.length > limit ? value.substring(0, limit) : value; - const genericPattern = patternsGenericPartialMatch[this.approvalType] || /^$/; - matches = getMatchedValues(limitedValue, genericPattern); - this.setTypeApprovalNumbers(matches.filter((x) => x)); - } - - if (!matches.length) { - console.error('Unknown approvalType:', this.approvalType); - } - } - - private extractValuesNtaIva(value: string, pattern: RegExp): void { - const matches = value.match(pattern)?.map((x) => (x.length > 25 ? x.substring(0, 25) : x)); - this.setTypeApprovalNumbers(matches || []); - } - - private setTypeApprovalNumbers(matches: string[]) { - if (matches) { - const [approvalTypeNumber1, approvalTypeNumber2, approvalTypeNumber3, approvalTypeNumber4] = matches; - this.approvalTypeNumber1 = approvalTypeNumber1; - this.approvalTypeNumber_1.next(approvalTypeNumber1); - this.approvalTypeNumber2 = approvalTypeNumber2; - this.approvalTypeNumber_2.next(approvalTypeNumber2); - this.approvalTypeNumber3 = approvalTypeNumber3; - this.approvalTypeNumber_3.next(approvalTypeNumber3); - this.approvalTypeNumber4 = approvalTypeNumber4; - this.approvalTypeNumber_4.next(approvalTypeNumber4); - } - } - - /** - * Subscribes to all date segments and propagates value as `string`. - * @returns Subscription - */ - subscribeAndPropagateChanges() { - return combineLatest([this.approvalTypeNumber1$, this.approvalTypeNumber2$, this.approvalTypeNumber3$, this.approvalTypeNumber4$]).subscribe( - ([approvalTypeNumber1, approvalTypeNumber2, approvalTypeNumber3, approvalTypeNumber4]) => { - if (!approvalTypeNumber1 && !approvalTypeNumber2 && !approvalTypeNumber3 && !approvalTypeNumber4) { - this.onChange(null); - } else { - this.onChange(this.processApprovalTypeNumber(approvalTypeNumber1, approvalTypeNumber2, approvalTypeNumber3, approvalTypeNumber4)); - } - }, - ); - } - - validate() { - const setErrors = () => { - this.errors = { - error: true, - errors: [ - { - error: true, - reason: 'Approval type number is required with Approval type', - index: 0, - }, - ], - }; - }; - - const oneRequired = () => !this.approvalTypeNumber1 && this.approvalType; - const twoRequired = () => oneRequired() || !this.approvalTypeNumber2; - const threeRequired = () => twoRequired() || !this.approvalTypeNumber3; - const fourRequired = () => threeRequired() || !this.approvalTypeNumber4; - - switch (this.approvalType) { - case ApprovalType.NTA: - case ApprovalType.IVA: - case ApprovalType.IVA_DVSA_NI: - if (oneRequired()) { - setErrors(); - } - break; - - case ApprovalType.GB_WVTA: - case ApprovalType.EU_WVTA_PRE_23: - case ApprovalType.EU_WVTA_23_ON: - case ApprovalType.PROV_GB_WVTA: - case ApprovalType.QNIG: - case ApprovalType.ECTA: - case ApprovalType.ECSSTA: - case ApprovalType.UKNI_WVTA: - if (fourRequired()) { - setErrors(); - } - break; - - case ApprovalType.IVA_VCA: - case ApprovalType.SMALL_SERIES_NKSXX: - if (fourRequired()) { - setErrors(); - } - break; - - case ApprovalType.NSSTA: - case ApprovalType.SMALL_SERIES_NKS: - if (twoRequired()) { - setErrors(); - } - break; - - default: - break; - } - } - - clearInput() { - this.approvalTypeNumber1 = undefined; - this.approvalTypeNumber2 = undefined; - this.approvalTypeNumber3 = undefined; - this.approvalTypeNumber4 = undefined; - - this.approvalTypeNumber_1.next(this.approvalTypeNumber1); - this.approvalTypeNumber_2.next(this.approvalTypeNumber2); - this.approvalTypeNumber_3.next(this.approvalTypeNumber3); - this.approvalTypeNumber_4.next(this.approvalTypeNumber4); - - this.onChange(null); - } - - get widths(): typeof FormNodeWidth { - return FormNodeWidth; - } - - processApprovalTypeNumber( - approvalTypeNumber1: string | undefined, - approvalTypeNumber2: string | undefined, - approvalTypeNumber3: string | undefined, - approvalTypeNumber4: string | undefined, - ) { - switch (this.approvalType) { - case ApprovalType.NTA: - return approvalTypeNumber1 || null; - - case ApprovalType.ECTA: - return approvalTypeNumber1 && approvalTypeNumber2 && approvalTypeNumber3 && approvalTypeNumber4 - ? `e${approvalTypeNumber1}*${approvalTypeNumber2}/${approvalTypeNumber3}*${approvalTypeNumber4}` - : null; - - case ApprovalType.IVA: - return approvalTypeNumber1 || null; - - case ApprovalType.NSSTA: - return approvalTypeNumber1 && approvalTypeNumber2 ? `e${approvalTypeNumber1}*NKS*${approvalTypeNumber2}` : null; - - case ApprovalType.ECSSTA: - return approvalTypeNumber1 && approvalTypeNumber2 && approvalTypeNumber3 && approvalTypeNumber4 - ? `e${approvalTypeNumber1}*KS${approvalTypeNumber2}/${approvalTypeNumber3}*${approvalTypeNumber4}` - : null; - - case ApprovalType.GB_WVTA: - return approvalTypeNumber1 && approvalTypeNumber2 && approvalTypeNumber3 && approvalTypeNumber4 - ? `${approvalTypeNumber1}*${approvalTypeNumber2}/${approvalTypeNumber3}*${approvalTypeNumber4}` - : null; - - case ApprovalType.UKNI_WVTA: - return approvalTypeNumber1 && approvalTypeNumber2 && approvalTypeNumber3 && approvalTypeNumber4 - ? `${approvalTypeNumber1}11*${approvalTypeNumber2}/${approvalTypeNumber3}*${approvalTypeNumber4}` - : null; - - case ApprovalType.EU_WVTA_PRE_23: - return approvalTypeNumber1 && approvalTypeNumber2 && approvalTypeNumber3 && approvalTypeNumber4 - ? `e${approvalTypeNumber1}*${approvalTypeNumber2}/${approvalTypeNumber3}*${approvalTypeNumber4}` - : null; - - case ApprovalType.EU_WVTA_23_ON: - return approvalTypeNumber1 && approvalTypeNumber2 && approvalTypeNumber3 && approvalTypeNumber4 - ? `e${approvalTypeNumber1}*${approvalTypeNumber2}/${approvalTypeNumber3}*${approvalTypeNumber4}` - : null; - - case ApprovalType.QNIG: - return approvalTypeNumber1 && approvalTypeNumber2 && approvalTypeNumber3 && approvalTypeNumber4 - ? `e${approvalTypeNumber1}*${approvalTypeNumber2}/${approvalTypeNumber3}*${approvalTypeNumber4}` - : null; - - case ApprovalType.PROV_GB_WVTA: - return approvalTypeNumber1 && approvalTypeNumber2 && approvalTypeNumber3 && approvalTypeNumber4 - ? `${approvalTypeNumber1}*${approvalTypeNumber2}/${approvalTypeNumber3}*${approvalTypeNumber4}` - : null; - - case ApprovalType.SMALL_SERIES_NKSXX: - return approvalTypeNumber1 && approvalTypeNumber2 && approvalTypeNumber3 - ? `${approvalTypeNumber1}11*NKS${approvalTypeNumber2}/${approvalTypeNumber3}*${approvalTypeNumber4}` - : null; - - case ApprovalType.SMALL_SERIES_NKS: - return approvalTypeNumber1 && approvalTypeNumber2 ? `${approvalTypeNumber1}11*NKS*${approvalTypeNumber2}` : null; - - case ApprovalType.IVA_VCA: - return approvalTypeNumber1 && approvalTypeNumber2 && approvalTypeNumber3 - ? `n11*NIV${approvalTypeNumber1}/${approvalTypeNumber2}*${approvalTypeNumber3}` - : null; - - case ApprovalType.IVA_DVSA_NI: - return approvalTypeNumber1 || null; - default: - return 'Unknown approval type'; - } - } - - getId(name: string) { - const id = `${name}-approvalTypeNumber1-${this.approvalType}`; - if (this.control) { - this.control.meta.customId = id; - } - return id; - } +export class ApprovalTypeInputComponent + extends BaseControlComponent + implements OnChanges, OnInit, OnDestroy, AfterContentInit +{ + @Input() isEditing = false; + @Input() approvalType?: string; + @Input() approvalTypeChange: boolean | undefined; + + private approvalTypeNumber_1: BehaviorSubject = new BehaviorSubject( + undefined + ); + private approvalTypeNumber_2: BehaviorSubject = new BehaviorSubject( + undefined + ); + private approvalTypeNumber_3: BehaviorSubject = new BehaviorSubject( + undefined + ); + private approvalTypeNumber_4: BehaviorSubject = new BehaviorSubject( + undefined + ); + + private approvalTypeNumber1$: Observable; + private approvalTypeNumber2$: Observable; + private approvalTypeNumber3$: Observable; + private approvalTypeNumber4$: Observable; + + private subscriptions: Array = []; + public errors?: { + error: boolean; + errors?: { + error: boolean; + reason: string; + index: number; + }[]; + }; + protected formSubmitted? = false; + public approvalTypeNumber1?: string; + public approvalTypeNumber2?: string; + public approvalTypeNumber3?: string; + public approvalTypeNumber4?: string; + + constructor( + injector: Injector, + changeDetectorRef: ChangeDetectorRef, + public globalErrorService: GlobalErrorService + ) { + super(injector, changeDetectorRef); + this.approvalTypeNumber1$ = this.approvalTypeNumber_1.asObservable(); + this.approvalTypeNumber2$ = this.approvalTypeNumber_2.asObservable(); + this.approvalTypeNumber3$ = this.approvalTypeNumber_3.asObservable(); + this.approvalTypeNumber4$ = this.approvalTypeNumber_4.asObservable(); + this.globalErrorService.errors$.subscribe((globalErrors) => { + if (globalErrors.length) { + this.formSubmitted = true; + } + }); + } + + ngOnChanges(): void { + this.valueWriteBack(this.value); + + if (this.approvalTypeChange) { + this.clearInput(); + } + } + + ngOnInit(): void { + this.subscriptions.push(this.subscribeAndPropagateChanges()); + } + + override ngAfterContentInit(): void { + super.ngAfterContentInit(); + this.valueWriteBack(this.value); + } + + ngOnDestroy(): void { + this.subscriptions.forEach((s) => s && s.unsubscribe()); + } + + onTechRecord_approvalTypeNumber1_Change(event: string | undefined) { + this.approvalTypeNumber_1.next(event); + } + + onTechRecord_approvalTypeNumber2_Change(event: string | undefined) { + this.approvalTypeNumber_2.next(event); + } + + onTechRecord_approvalTypeNumber3_Change(event: string | undefined) { + this.approvalTypeNumber_3.next(event); + } + + onTechRecord_approvalTypeNumber4_Change(event: string | undefined) { + this.approvalTypeNumber_4.next(event); + } + + valueWriteBack(value: string | null): void { + if (!value || !this.approvalType) { + return; + } + + const NTA_IVA_Types = ['NTA', 'IVA', 'IVA - DVSA/NI']; + const OtherTypes = [ + 'ECTA', + 'NSSTA', + 'ECSSTA', + 'GB WVTA', + 'UKNI WVTA', + 'EU WVTA Pre 23', + 'EU WVTA 23 on', + 'QNIG', + 'Prov.GB WVTA', + 'IVA - VCA', + 'Small series NKSXX', + 'Small series NKS', + ]; + + if (NTA_IVA_Types.includes(this.approvalType)) { + this.extractValuesNtaIva(value, patterns[this.approvalType]); + } else if (OtherTypes.includes(this.approvalType)) { + this.extractValues(value); + } + } + + private extractValues(value: string): void { + if (!value || !this.approvalType) return; + + const getMatchedValues = (_value: string, pattern: RegExp) => { + const result = _value.match(pattern); + return result ? result.slice(1) : []; + }; + + const pattern = patterns[this.approvalType]; + const patternPartial = patternsPartialMatch[this.approvalType] || /^$/; + + let matches = getMatchedValues(value, pattern); + if (matches) { + this.setTypeApprovalNumbers(matches.filter((x) => x)); + } + + if (!matches.length) { + const limit = characterLimit[this.approvalType]; + const limitedValue = value.length > limit ? value.substring(0, limit) : value; + matches = getMatchedValues(limitedValue, patternPartial); + this.setTypeApprovalNumbers(matches.filter((x) => x)); + } + + if (!matches.length) { + const limit = characterLimitGeneric[this.approvalType]; + const limitedValue = value.length > limit ? value.substring(0, limit) : value; + const genericPattern = patternsGenericPartialMatch[this.approvalType] || /^$/; + matches = getMatchedValues(limitedValue, genericPattern); + this.setTypeApprovalNumbers(matches.filter((x) => x)); + } + + if (!matches.length) { + console.error('Unknown approvalType:', this.approvalType); + } + } + + private extractValuesNtaIva(value: string, pattern: RegExp): void { + const matches = value.match(pattern)?.map((x) => (x.length > 25 ? x.substring(0, 25) : x)); + this.setTypeApprovalNumbers(matches || []); + } + + private setTypeApprovalNumbers(matches: string[]) { + if (matches) { + const [approvalTypeNumber1, approvalTypeNumber2, approvalTypeNumber3, approvalTypeNumber4] = matches; + this.approvalTypeNumber1 = approvalTypeNumber1; + this.approvalTypeNumber_1.next(approvalTypeNumber1); + this.approvalTypeNumber2 = approvalTypeNumber2; + this.approvalTypeNumber_2.next(approvalTypeNumber2); + this.approvalTypeNumber3 = approvalTypeNumber3; + this.approvalTypeNumber_3.next(approvalTypeNumber3); + this.approvalTypeNumber4 = approvalTypeNumber4; + this.approvalTypeNumber_4.next(approvalTypeNumber4); + } + } + + /** + * Subscribes to all date segments and propagates value as `string`. + * @returns Subscription + */ + subscribeAndPropagateChanges() { + return combineLatest([ + this.approvalTypeNumber1$, + this.approvalTypeNumber2$, + this.approvalTypeNumber3$, + this.approvalTypeNumber4$, + ]).subscribe(([approvalTypeNumber1, approvalTypeNumber2, approvalTypeNumber3, approvalTypeNumber4]) => { + if (!approvalTypeNumber1 && !approvalTypeNumber2 && !approvalTypeNumber3 && !approvalTypeNumber4) { + this.onChange(null); + } else { + this.onChange( + this.processApprovalTypeNumber( + approvalTypeNumber1, + approvalTypeNumber2, + approvalTypeNumber3, + approvalTypeNumber4 + ) + ); + } + }); + } + + validate() { + const setErrors = () => { + this.errors = { + error: true, + errors: [ + { + error: true, + reason: 'Approval type number is required with Approval type', + index: 0, + }, + ], + }; + }; + + const oneRequired = () => !this.approvalTypeNumber1 && this.approvalType; + const twoRequired = () => oneRequired() || !this.approvalTypeNumber2; + const threeRequired = () => twoRequired() || !this.approvalTypeNumber3; + const fourRequired = () => threeRequired() || !this.approvalTypeNumber4; + + switch (this.approvalType) { + case ApprovalType.NTA: + case ApprovalType.IVA: + case ApprovalType.IVA_DVSA_NI: + if (oneRequired()) { + setErrors(); + } + break; + + case ApprovalType.GB_WVTA: + case ApprovalType.EU_WVTA_PRE_23: + case ApprovalType.EU_WVTA_23_ON: + case ApprovalType.PROV_GB_WVTA: + case ApprovalType.QNIG: + case ApprovalType.ECTA: + case ApprovalType.ECSSTA: + case ApprovalType.UKNI_WVTA: + if (fourRequired()) { + setErrors(); + } + break; + + case ApprovalType.IVA_VCA: + case ApprovalType.SMALL_SERIES_NKSXX: + if (fourRequired()) { + setErrors(); + } + break; + + case ApprovalType.NSSTA: + case ApprovalType.SMALL_SERIES_NKS: + if (twoRequired()) { + setErrors(); + } + break; + + default: + break; + } + } + + clearInput() { + this.approvalTypeNumber1 = undefined; + this.approvalTypeNumber2 = undefined; + this.approvalTypeNumber3 = undefined; + this.approvalTypeNumber4 = undefined; + + this.approvalTypeNumber_1.next(this.approvalTypeNumber1); + this.approvalTypeNumber_2.next(this.approvalTypeNumber2); + this.approvalTypeNumber_3.next(this.approvalTypeNumber3); + this.approvalTypeNumber_4.next(this.approvalTypeNumber4); + + this.onChange(null); + } + + get widths(): typeof FormNodeWidth { + return FormNodeWidth; + } + + processApprovalTypeNumber( + approvalTypeNumber1: string | undefined, + approvalTypeNumber2: string | undefined, + approvalTypeNumber3: string | undefined, + approvalTypeNumber4: string | undefined + ) { + switch (this.approvalType) { + case ApprovalType.NTA: + return approvalTypeNumber1 || null; + + case ApprovalType.ECTA: + return approvalTypeNumber1 && approvalTypeNumber2 && approvalTypeNumber3 && approvalTypeNumber4 + ? `e${approvalTypeNumber1}*${approvalTypeNumber2}/${approvalTypeNumber3}*${approvalTypeNumber4}` + : null; + + case ApprovalType.IVA: + return approvalTypeNumber1 || null; + + case ApprovalType.NSSTA: + return approvalTypeNumber1 && approvalTypeNumber2 ? `e${approvalTypeNumber1}*NKS*${approvalTypeNumber2}` : null; + + case ApprovalType.ECSSTA: + return approvalTypeNumber1 && approvalTypeNumber2 && approvalTypeNumber3 && approvalTypeNumber4 + ? `e${approvalTypeNumber1}*KS${approvalTypeNumber2}/${approvalTypeNumber3}*${approvalTypeNumber4}` + : null; + + case ApprovalType.GB_WVTA: + return approvalTypeNumber1 && approvalTypeNumber2 && approvalTypeNumber3 && approvalTypeNumber4 + ? `${approvalTypeNumber1}*${approvalTypeNumber2}/${approvalTypeNumber3}*${approvalTypeNumber4}` + : null; + + case ApprovalType.UKNI_WVTA: + return approvalTypeNumber1 && approvalTypeNumber2 && approvalTypeNumber3 && approvalTypeNumber4 + ? `${approvalTypeNumber1}11*${approvalTypeNumber2}/${approvalTypeNumber3}*${approvalTypeNumber4}` + : null; + + case ApprovalType.EU_WVTA_PRE_23: + return approvalTypeNumber1 && approvalTypeNumber2 && approvalTypeNumber3 && approvalTypeNumber4 + ? `e${approvalTypeNumber1}*${approvalTypeNumber2}/${approvalTypeNumber3}*${approvalTypeNumber4}` + : null; + + case ApprovalType.EU_WVTA_23_ON: + return approvalTypeNumber1 && approvalTypeNumber2 && approvalTypeNumber3 && approvalTypeNumber4 + ? `e${approvalTypeNumber1}*${approvalTypeNumber2}/${approvalTypeNumber3}*${approvalTypeNumber4}` + : null; + + case ApprovalType.QNIG: + return approvalTypeNumber1 && approvalTypeNumber2 && approvalTypeNumber3 && approvalTypeNumber4 + ? `e${approvalTypeNumber1}*${approvalTypeNumber2}/${approvalTypeNumber3}*${approvalTypeNumber4}` + : null; + + case ApprovalType.PROV_GB_WVTA: + return approvalTypeNumber1 && approvalTypeNumber2 && approvalTypeNumber3 && approvalTypeNumber4 + ? `${approvalTypeNumber1}*${approvalTypeNumber2}/${approvalTypeNumber3}*${approvalTypeNumber4}` + : null; + + case ApprovalType.SMALL_SERIES_NKSXX: + return approvalTypeNumber1 && approvalTypeNumber2 && approvalTypeNumber3 + ? `${approvalTypeNumber1}11*NKS${approvalTypeNumber2}/${approvalTypeNumber3}*${approvalTypeNumber4}` + : null; + + case ApprovalType.SMALL_SERIES_NKS: + return approvalTypeNumber1 && approvalTypeNumber2 + ? `${approvalTypeNumber1}11*NKS*${approvalTypeNumber2}` + : null; + + case ApprovalType.IVA_VCA: + return approvalTypeNumber1 && approvalTypeNumber2 && approvalTypeNumber3 + ? `n11*NIV${approvalTypeNumber1}/${approvalTypeNumber2}*${approvalTypeNumber3}` + : null; + + case ApprovalType.IVA_DVSA_NI: + return approvalTypeNumber1 || null; + default: + return 'Unknown approval type'; + } + } + + getId(name: string) { + const id = `${name}-approvalTypeNumber1-${this.approvalType}`; + if (this.control) { + this.control.meta.customId = id; + } + return id; + } } diff --git a/src/app/forms/components/autocomplete/autocomplete.component.spec.ts b/src/app/forms/components/autocomplete/autocomplete.component.spec.ts index ffaee4b948..4cd953f538 100644 --- a/src/app/forms/components/autocomplete/autocomplete.component.spec.ts +++ b/src/app/forms/components/autocomplete/autocomplete.component.spec.ts @@ -8,96 +8,97 @@ import { FieldErrorMessageComponent } from '../field-error-message/field-error-m import { AutocompleteComponent } from './autocomplete.component'; jest.mock('accessible-autocomplete/dist/accessible-autocomplete.min', () => { - return { - __esModule: true, - default: jest.fn(), - enhanceSelectElement: () => {}, - }; + return { + __esModule: true, + default: jest.fn(), + enhanceSelectElement: () => {}, + }; }); @Component({ - selector: 'app-host-component', - template: '
', + selector: 'app-host-component', + template: + '
', }) class HostComponent { - name = 'autocomplete'; - options$ = of([ - { label: 'option1', value: 'option1' }, - { label: 'option2', value: 'option2' }, - ]); - form = new FormGroup({ foo: new CustomFormControl({ name: 'foo', type: FormNodeTypes.CONTROL }, '') }); + name = 'autocomplete'; + options$ = of([ + { label: 'option1', value: 'option1' }, + { label: 'option2', value: 'option2' }, + ]); + form = new FormGroup({ foo: new CustomFormControl({ name: 'foo', type: FormNodeTypes.CONTROL }, '') }); } describe('AutocompleteComponent', () => { - let component: HostComponent; - let fixture: ComponentFixture; - let autocompleteComponent: AutocompleteComponent; - - beforeEach(async () => { - await TestBed.configureTestingModule({ - declarations: [AutocompleteComponent, HostComponent, FieldErrorMessageComponent], - imports: [FormsModule, ReactiveFormsModule], - }).compileComponents(); - }); - - beforeEach(() => { - fixture = TestBed.createComponent(HostComponent); - component = fixture.componentInstance; - autocompleteComponent = fixture.debugElement.query(By.directive(AutocompleteComponent)).componentInstance; - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); - - it.each([ - ['option1', of([{ label: 'option1', value: 'option1' }]), 'option1'], - [undefined, of([{ label: 'option1', value: 'option1' }]), 'option3'], - ])('should return %s for %o when looking for $s', (expected, options$, label) => { - autocompleteComponent.options$ = options$; - expect(autocompleteComponent.findOptionValue(label)).toBe(expected); - }); - - it('should call handleChange when input value changes', () => { - const handleChangeSpy = jest.spyOn(autocompleteComponent, 'handleChange'); - const autocompleteInput: HTMLInputElement = fixture.debugElement.query(By.css(`#${component.name}`)).nativeElement; - - autocompleteInput.dispatchEvent(new Event('change')); - - expect(handleChangeSpy).toHaveBeenCalled(); - }); - - it('should find option value by label and propagate to form control', () => { - const findOptionValueSpy = jest.spyOn(autocompleteComponent, 'findOptionValue'); - const control = component.form.get('foo'); - - autocompleteComponent.handleChange({ target: { value: 'option2' } } as unknown as Event); - - expect(findOptionValueSpy).toHaveBeenCalled(); - expect(control?.value).toBe('option2'); - expect(control?.touched).toBeTruthy(); - }); - - it('should propagate "" to form control when input is left empty', () => { - const findOptionValueSpy = jest.spyOn(autocompleteComponent, 'findOptionValue'); - const control = component.form.get('foo'); - - autocompleteComponent.handleChange({ target: { value: '' } } as unknown as Event); - - expect(findOptionValueSpy).toHaveBeenCalled(); - expect(control?.value).toBe(''); - expect(control?.touched).toBeTruthy(); - }); - - it('should propagate "[INVALID_OPTION]" to form control when value is not an option', () => { - const findOptionValueSpy = jest.spyOn(autocompleteComponent, 'findOptionValue'); - const control = component.form.get('foo'); - - autocompleteComponent.handleChange({ target: { value: 'option3' } } as unknown as Event); - - expect(findOptionValueSpy).toHaveBeenCalled(); - expect(control?.value).toBe('[INVALID_OPTION]'); - expect(control?.touched).toBeTruthy(); - }); + let component: HostComponent; + let fixture: ComponentFixture; + let autocompleteComponent: AutocompleteComponent; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [AutocompleteComponent, HostComponent, FieldErrorMessageComponent], + imports: [FormsModule, ReactiveFormsModule], + }).compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(HostComponent); + component = fixture.componentInstance; + autocompleteComponent = fixture.debugElement.query(By.directive(AutocompleteComponent)).componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + it.each([ + ['option1', of([{ label: 'option1', value: 'option1' }]), 'option1'], + [undefined, of([{ label: 'option1', value: 'option1' }]), 'option3'], + ])('should return %s for %o when looking for $s', (expected, options$, label) => { + autocompleteComponent.options$ = options$; + expect(autocompleteComponent.findOptionValue(label)).toBe(expected); + }); + + it('should call handleChange when input value changes', () => { + const handleChangeSpy = jest.spyOn(autocompleteComponent, 'handleChange'); + const autocompleteInput: HTMLInputElement = fixture.debugElement.query(By.css(`#${component.name}`)).nativeElement; + + autocompleteInput.dispatchEvent(new Event('change')); + + expect(handleChangeSpy).toHaveBeenCalled(); + }); + + it('should find option value by label and propagate to form control', () => { + const findOptionValueSpy = jest.spyOn(autocompleteComponent, 'findOptionValue'); + const control = component.form.get('foo'); + + autocompleteComponent.handleChange({ target: { value: 'option2' } } as unknown as Event); + + expect(findOptionValueSpy).toHaveBeenCalled(); + expect(control?.value).toBe('option2'); + expect(control?.touched).toBeTruthy(); + }); + + it('should propagate "" to form control when input is left empty', () => { + const findOptionValueSpy = jest.spyOn(autocompleteComponent, 'findOptionValue'); + const control = component.form.get('foo'); + + autocompleteComponent.handleChange({ target: { value: '' } } as unknown as Event); + + expect(findOptionValueSpy).toHaveBeenCalled(); + expect(control?.value).toBe(''); + expect(control?.touched).toBeTruthy(); + }); + + it('should propagate "[INVALID_OPTION]" to form control when value is not an option', () => { + const findOptionValueSpy = jest.spyOn(autocompleteComponent, 'findOptionValue'); + const control = component.form.get('foo'); + + autocompleteComponent.handleChange({ target: { value: 'option3' } } as unknown as Event); + + expect(findOptionValueSpy).toHaveBeenCalled(); + expect(control?.value).toBe('[INVALID_OPTION]'); + expect(control?.touched).toBeTruthy(); + }); }); diff --git a/src/app/forms/components/autocomplete/autocomplete.component.ts b/src/app/forms/components/autocomplete/autocomplete.component.ts index bc1b0c9c82..7a08717079 100644 --- a/src/app/forms/components/autocomplete/autocomplete.component.ts +++ b/src/app/forms/components/autocomplete/autocomplete.component.ts @@ -1,7 +1,5 @@ import { DOCUMENT } from '@angular/common'; -import { - AfterContentInit, AfterViewInit, ChangeDetectorRef, Component, Inject, Injector, Input, -} from '@angular/core'; +import { AfterContentInit, AfterViewInit, ChangeDetectorRef, Component, Inject, Injector, Input } from '@angular/core'; import { NG_VALUE_ACCESSOR } from '@angular/forms'; import { CustomValidators } from '@forms/validators/custom-validators'; import { enhanceSelectElement } from 'accessible-autocomplete/dist/accessible-autocomplete.min'; @@ -9,98 +7,105 @@ import { Observable, lastValueFrom, takeWhile } from 'rxjs'; import { BaseControlComponent } from '../base-control/base-control.component'; @Component({ - selector: 'app-autocomplete', - templateUrl: './autocomplete.component.html', - styleUrls: ['./autocomplete.component.scss'], - providers: [ - { - provide: NG_VALUE_ACCESSOR, - useExisting: AutocompleteComponent, - multi: true, - }, - ], + selector: 'app-autocomplete', + templateUrl: './autocomplete.component.html', + styleUrls: ['./autocomplete.component.scss'], + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: AutocompleteComponent, + multi: true, + }, + ], }) export class AutocompleteComponent extends BaseControlComponent implements AfterViewInit, AfterContentInit { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - @Input() options$!: Observable; - @Input() defaultValue = ''; - - // eslint-disable-next-line max-len - DROP_DOWN_ARROW = ''; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - options: any[] = []; - - constructor(injector: Injector, @Inject(DOCUMENT) private document: Document, changeDetectorRef: ChangeDetectorRef) { - super(injector, changeDetectorRef); - } - - ngAfterViewInit(): void { - // eslint-disable-next-line @typescript-eslint/no-this-alias - const self = this; - - lastValueFrom(this.options$.pipe(takeWhile((options) => !options || options.length === 0, true))).then((options) => { - this.options = options; - this.cdr.detectChanges(); - - enhanceSelectElement({ - selectElement: this.document.querySelector(`#${this.name}`), - autoselect: false, - defaultValue: '', - showAllValues: true, - confirmOnBlur: false, - dropdownArrow: () => this.DROP_DOWN_ARROW, - onConfirm(selected) { - self.handleChangeForOption(selected); - }, - }); - - window.document.querySelector(`#${this.name}`)?.addEventListener('change', (event) => this.handleChange(event)); - }).catch(() => {}); - } - - override ngAfterContentInit(): void { - super.ngAfterContentInit(); - this.addValidators(); - } - - get style(): string { - return `autocomplete__wrapper${this.noBottomMargin ? '' : ' extra-margin'}`; - } - - get innerStyle(): string { - return this.width ? ` govuk-input--width-${this.width}` : ' internal-wrapper'; - } - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - handleChange(event: any) { - const { - target: { value }, - } = event; - - this.handleChangeForOption(value); - } - - handleChangeForOption(value: string) { - const optionValue = this.findOptionValue(value); - - this.control?.patchValue(optionValue ?? '[INVALID_OPTION]'); - this.control?.markAsTouched(); - this.control?.updateValueAndValidity(); - this.cdr.detectChanges(); - } - - /** - * Takes the value from the autocomplete element and looks for a matching option in the options array. - * Returns the found value or undefined if no match. - * If value is empty, returns `''`. - * @param value - value to get option for - * @returns `string | undefined` - */ - findOptionValue(label: string) { - return label ? this.options.find((option) => option.label === label)?.value : ''; - } - - addValidators() { - this.control?.addValidators([CustomValidators.invalidOption]); - } + // eslint-disable-next-line @typescript-eslint/no-explicit-any + @Input() options$!: Observable; + @Input() defaultValue = ''; + + // eslint-disable-next-line max-len + DROP_DOWN_ARROW = + ''; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + options: any[] = []; + + constructor( + injector: Injector, + @Inject(DOCUMENT) private document: Document, + changeDetectorRef: ChangeDetectorRef + ) { + super(injector, changeDetectorRef); + } + + ngAfterViewInit(): void { + // eslint-disable-next-line @typescript-eslint/no-this-alias + const self = this; + + lastValueFrom(this.options$.pipe(takeWhile((options) => !options || options.length === 0, true))) + .then((options) => { + this.options = options; + this.cdr.detectChanges(); + + enhanceSelectElement({ + selectElement: this.document.querySelector(`#${this.name}`), + autoselect: false, + defaultValue: '', + showAllValues: true, + confirmOnBlur: false, + dropdownArrow: () => this.DROP_DOWN_ARROW, + onConfirm(selected) { + self.handleChangeForOption(selected); + }, + }); + + window.document.querySelector(`#${this.name}`)?.addEventListener('change', (event) => this.handleChange(event)); + }) + .catch(() => {}); + } + + override ngAfterContentInit(): void { + super.ngAfterContentInit(); + this.addValidators(); + } + + get style(): string { + return `autocomplete__wrapper${this.noBottomMargin ? '' : ' extra-margin'}`; + } + + get innerStyle(): string { + return this.width ? ` govuk-input--width-${this.width}` : ' internal-wrapper'; + } + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + handleChange(event: any) { + const { + target: { value }, + } = event; + + this.handleChangeForOption(value); + } + + handleChangeForOption(value: string) { + const optionValue = this.findOptionValue(value); + + this.control?.patchValue(optionValue ?? '[INVALID_OPTION]'); + this.control?.markAsTouched(); + this.control?.updateValueAndValidity(); + this.cdr.detectChanges(); + } + + /** + * Takes the value from the autocomplete element and looks for a matching option in the options array. + * Returns the found value or undefined if no match. + * If value is empty, returns `''`. + * @param value - value to get option for + * @returns `string | undefined` + */ + findOptionValue(label: string) { + return label ? this.options.find((option) => option.label === label)?.value : ''; + } + + addValidators() { + this.control?.addValidators([CustomValidators.invalidOption]); + } } diff --git a/src/app/forms/components/autocomplete/autocomplete.stories.ts b/src/app/forms/components/autocomplete/autocomplete.stories.ts index ecba69c689..8a9e8bbf4d 100644 --- a/src/app/forms/components/autocomplete/autocomplete.stories.ts +++ b/src/app/forms/components/autocomplete/autocomplete.stories.ts @@ -1,45 +1,45 @@ import { CommonModule } from '@angular/common'; import { FormsModule, ReactiveFormsModule } from '@angular/forms'; -import { Meta, moduleMetadata, Story } from '@storybook/angular'; +import { Meta, Story, moduleMetadata } from '@storybook/angular'; import { AutocompleteComponent } from './autocomplete.component'; export default { - title: 'Forms/Autocomplete', - component: AutocompleteComponent, - decorators: [ - moduleMetadata({ - declarations: [AutocompleteComponent], - imports: [CommonModule, FormsModule, ReactiveFormsModule] - }) - ] + title: 'Forms/Autocomplete', + component: AutocompleteComponent, + decorators: [ + moduleMetadata({ + declarations: [AutocompleteComponent], + imports: [CommonModule, FormsModule, ReactiveFormsModule], + }), + ], } as Meta; -const Template: Story = args => { - const { id, label, options, name, hint, defaultValue = 'red' } = args; - return { - component: AutocompleteComponent, - template: `
`, - props: { - id, - label, - hint, - name, - options, - defaultValue - } - }; +const Template: Story = (args) => { + const { id, label, options, name, hint, defaultValue = 'red' } = args; + return { + component: AutocompleteComponent, + template: `
`, + props: { + id, + label, + hint, + name, + options, + defaultValue, + }, + }; }; const defaultArgs = { - label: 'Autocomplete', - hint: 'Type any letter to start searching', - name: 'name', - id: 'name' + '-wrapper', - options: ['red', 'yellow', 'blue', 'green'], - defaultValue: 'red' + label: 'Autocomplete', + hint: 'Type any letter to start searching', + name: 'name', + id: 'name' + '-wrapper', + options: ['red', 'yellow', 'blue', 'green'], + defaultValue: 'red', }; export const Enabled = Template.bind({}); Enabled.args = { - ...defaultArgs + ...defaultArgs, }; diff --git a/src/app/forms/components/base-control/base-control.component.spec.ts b/src/app/forms/components/base-control/base-control.component.spec.ts index d2723b6564..ff05f29a77 100644 --- a/src/app/forms/components/base-control/base-control.component.spec.ts +++ b/src/app/forms/components/base-control/base-control.component.spec.ts @@ -4,137 +4,140 @@ import { CustomFormControl, FormNodeTypes } from '../../services/dynamic-form.ty import { BaseControlComponent } from './base-control.component'; describe('BaseControlComponent', () => { - let component: BaseControlComponent; - let fixture: ComponentFixture; - - const controlMetaData = { name: 'testControl', type: FormNodeTypes.CONTROL, children: [] }; - - describe('has control binding', () => { - beforeEach(async () => { - const NG_CONTROL_PROVIDER = { - provide: NgControl, - useClass: class extends NgControl { - control = new CustomFormControl(controlMetaData, '', [Validators.required]); - viewToModelUpdate() {} - }, - }; - - await TestBed.configureTestingModule({ - declarations: [BaseControlComponent], - imports: [FormsModule], - }) - .overrideComponent(BaseControlComponent, { add: { providers: [NG_CONTROL_PROVIDER] } }) - .compileComponents(); - }); - - beforeEach(() => { - fixture = TestBed.createComponent(BaseControlComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); - - it('should register a change', () => { - component.registerOnChange('FUNCTION' as unknown as () => void); - expect(component.onChange).toBe('FUNCTION'); - }); - - it('should register it has been touched', () => { - component.registerOnTouched('FUNCTION' as unknown as () => void); - expect(component.onTouched).toBe('FUNCTION'); - }); - - it('should call onChange successfully', () => { - const onChangepy = jest.spyOn(component, 'onChange'); - - component.onChange(null); - - expect(onChangepy).toHaveBeenCalledWith(null); - }); - - it('should call onTouched successfully', () => { - const onTouchedpy = jest.spyOn(component, 'onTouched'); - - component.onTouched(); - - expect(onTouchedpy).toHaveBeenCalled(); - }); - - it('should return disabled', () => { - expect(component.disabled).toBeFalsy(); - }); - - it('should return metadata', () => { - expect(component.meta).toEqual(controlMetaData); - }); - - it('should correctly set focused flag', () => { - component.handleEvent(new Event('focus')); - expect(component.focused).toBeTruthy(); - - component.handleEvent(new Event('blur')); - expect(component.focused).toBeFalsy(); - - const handleEventSpy = jest.spyOn(component, 'handleEvent'); - const state = component.handleEvent(new Event('submit')); - expect(handleEventSpy).toHaveBeenCalled(); - expect(state).toBeNull(); - }); - - describe('interacting with the value', () => { - it('writeValue should set the value', () => { - component.writeValue('anything'); - expect(component.value).toBe('anything'); - }); - - it('set should set the value', () => { - component.value = 'anything'; - expect(component.value).toBe('anything'); - }); - }); - - describe('validation', () => { - it('should get mapped message for first validation error', () => { - component.label = 'Test control'; - component.control?.markAsTouched(); - fixture.detectChanges(); - expect(component.error).toBe('Test control is required'); - }); - - it('should get "" when control is valid', () => { - component.label = 'Test control'; - component.control?.patchValue('test'); - component.control?.markAsTouched(); - fixture.detectChanges(); - expect(component.error).toBe(''); - }); - }); - }); - - describe('does not have control binding', () => { - beforeEach(async () => { - await TestBed.configureTestingModule({ declarations: [BaseControlComponent], imports: [FormsModule] }).compileComponents(); - }); - - beforeEach(() => { - fixture = TestBed.createComponent(BaseControlComponent); - component = fixture.componentInstance; - }); - - it('shoud throw no control binding error', () => { - expect(component).toBeTruthy(); - expect(() => fixture.detectChanges()).toThrow(Error); - }); - - it('should return undefined for metadata', () => { - expect(component.meta).toBeUndefined(); - }); - - it('disabled should default to false', () => { - expect(component.disabled).toBeFalsy(); - }); - }); + let component: BaseControlComponent; + let fixture: ComponentFixture; + + const controlMetaData = { name: 'testControl', type: FormNodeTypes.CONTROL, children: [] }; + + describe('has control binding', () => { + beforeEach(async () => { + const NG_CONTROL_PROVIDER = { + provide: NgControl, + useClass: class extends NgControl { + control = new CustomFormControl(controlMetaData, '', [Validators.required]); + viewToModelUpdate() {} + }, + }; + + await TestBed.configureTestingModule({ + declarations: [BaseControlComponent], + imports: [FormsModule], + }) + .overrideComponent(BaseControlComponent, { add: { providers: [NG_CONTROL_PROVIDER] } }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(BaseControlComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + it('should register a change', () => { + component.registerOnChange('FUNCTION' as unknown as () => void); + expect(component.onChange).toBe('FUNCTION'); + }); + + it('should register it has been touched', () => { + component.registerOnTouched('FUNCTION' as unknown as () => void); + expect(component.onTouched).toBe('FUNCTION'); + }); + + it('should call onChange successfully', () => { + const onChangepy = jest.spyOn(component, 'onChange'); + + component.onChange(null); + + expect(onChangepy).toHaveBeenCalledWith(null); + }); + + it('should call onTouched successfully', () => { + const onTouchedpy = jest.spyOn(component, 'onTouched'); + + component.onTouched(); + + expect(onTouchedpy).toHaveBeenCalled(); + }); + + it('should return disabled', () => { + expect(component.disabled).toBeFalsy(); + }); + + it('should return metadata', () => { + expect(component.meta).toEqual(controlMetaData); + }); + + it('should correctly set focused flag', () => { + component.handleEvent(new Event('focus')); + expect(component.focused).toBeTruthy(); + + component.handleEvent(new Event('blur')); + expect(component.focused).toBeFalsy(); + + const handleEventSpy = jest.spyOn(component, 'handleEvent'); + const state = component.handleEvent(new Event('submit')); + expect(handleEventSpy).toHaveBeenCalled(); + expect(state).toBeNull(); + }); + + describe('interacting with the value', () => { + it('writeValue should set the value', () => { + component.writeValue('anything'); + expect(component.value).toBe('anything'); + }); + + it('set should set the value', () => { + component.value = 'anything'; + expect(component.value).toBe('anything'); + }); + }); + + describe('validation', () => { + it('should get mapped message for first validation error', () => { + component.label = 'Test control'; + component.control?.markAsTouched(); + fixture.detectChanges(); + expect(component.error).toBe('Test control is required'); + }); + + it('should get "" when control is valid', () => { + component.label = 'Test control'; + component.control?.patchValue('test'); + component.control?.markAsTouched(); + fixture.detectChanges(); + expect(component.error).toBe(''); + }); + }); + }); + + describe('does not have control binding', () => { + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [BaseControlComponent], + imports: [FormsModule], + }).compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(BaseControlComponent); + component = fixture.componentInstance; + }); + + it('shoud throw no control binding error', () => { + expect(component).toBeTruthy(); + expect(() => fixture.detectChanges()).toThrow(Error); + }); + + it('should return undefined for metadata', () => { + expect(component.meta).toBeUndefined(); + }); + + it('disabled should default to false', () => { + expect(component.disabled).toBeFalsy(); + }); + }); }); diff --git a/src/app/forms/components/base-control/base-control.component.ts b/src/app/forms/components/base-control/base-control.component.ts index f9fcdfa896..01641e5065 100644 --- a/src/app/forms/components/base-control/base-control.component.ts +++ b/src/app/forms/components/base-control/base-control.component.ts @@ -1,5 +1,11 @@ import { - AfterContentInit, ChangeDetectionStrategy, ChangeDetectorRef, Component, ContentChild, Injector, Input, + AfterContentInit, + ChangeDetectionStrategy, + ChangeDetectorRef, + Component, + ContentChild, + Injector, + Input, } from '@angular/core'; import { ControlValueAccessor, NgControl } from '@angular/forms'; import { PrefixDirective } from '@forms/directives/prefix.directive'; @@ -10,103 +16,106 @@ import { CustomControl, FormNodeViewTypes, FormNodeWidth } from '../../services/ import { ErrorMessageMap } from '../../utils/error-message-map'; @Component({ - selector: 'app-base-control', - template: '', - styles: [], - changeDetection: ChangeDetectionStrategy.OnPush, + selector: 'app-base-control', + template: '', + styles: [], + changeDetection: ChangeDetectionStrategy.OnPush, }) export class BaseControlComponent implements ControlValueAccessor, AfterContentInit { - @ContentChild(PrefixDirective) prefix?: PrefixDirective; - @ContentChild(SuffixDirective) suffix?: SuffixDirective; - - @Input() name = ''; - @Input() customId?: string; - @Input() hint?: string; - @Input() label?: string; - @Input() width?: FormNodeWidth; - @Input() viewType: FormNodeViewTypes = FormNodeViewTypes.STRING; - @Input() noBottomMargin = false; - @Input() warning?: string | null = null; - - public onChange: (event: unknown) => void = () => {}; - public onTouched = () => {}; - public focused = false; - public errorMessage?: string; - public control?: CustomControl; - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - private control_value: any; - - constructor(protected injector: Injector, protected cdr: ChangeDetectorRef) { - this.name = ''; - } - - ngAfterContentInit(): void { - const ngControl: NgControl | null = this.injector.get(NgControl, null); - if (ngControl) { - this.control = ngControl.control as CustomControl; - if (this.control && this.control.meta) { - this.control.meta.changeDetection = this.cdr; - } - } else { - throw new Error(`No control binding for ${this.name}`); - } - } - - get error(): string { - if (this.control?.touched && this.control.invalid) { - const { errors } = this.control; - if (errors) { - const errorList = Object.keys(errors); - const firstError = ErrorMessageMap[errorList[0] as ValidatorNames]; - return this.control.meta.customErrorMessage ?? firstError(errors[errorList[0]], this.label); - } - } - return ''; - } - - get value() { - return this.control_value; - } - - set value(value) { - this.control_value = value; - } - - get disabled() { - return this.control?.disabled ?? false; - } - - get meta() { - return this.control?.meta; - } - - public handleEvent(event: Event) { - switch (event.type) { - case 'focus': - this.focused = true; - return true; - case 'blur': - this.focused = false; - return false; - default: - return null; - } - } - - writeValue(obj: unknown): void { - this.value = obj; - } - - registerOnChange(fn: (event: unknown) => void): void { - this.onChange = fn; - } - - registerOnTouched(fn: () => void): void { - this.onTouched = fn; - } - - trackBy(i: number) { - return i; - } + @ContentChild(PrefixDirective) prefix?: PrefixDirective; + @ContentChild(SuffixDirective) suffix?: SuffixDirective; + + @Input() name = ''; + @Input() customId?: string; + @Input() hint?: string; + @Input() label?: string; + @Input() width?: FormNodeWidth; + @Input() viewType: FormNodeViewTypes = FormNodeViewTypes.STRING; + @Input() noBottomMargin = false; + @Input() warning?: string | null = null; + + public onChange: (event: unknown) => void = () => {}; + public onTouched = () => {}; + public focused = false; + public errorMessage?: string; + public control?: CustomControl; + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + private control_value: any; + + constructor( + protected injector: Injector, + protected cdr: ChangeDetectorRef + ) { + this.name = ''; + } + + ngAfterContentInit(): void { + const ngControl: NgControl | null = this.injector.get(NgControl, null); + if (ngControl) { + this.control = ngControl.control as CustomControl; + if (this.control && this.control.meta) { + this.control.meta.changeDetection = this.cdr; + } + } else { + throw new Error(`No control binding for ${this.name}`); + } + } + + get error(): string { + if (this.control?.touched && this.control.invalid) { + const { errors } = this.control; + if (errors) { + const errorList = Object.keys(errors); + const firstError = ErrorMessageMap[errorList[0] as ValidatorNames]; + return this.control.meta.customErrorMessage ?? firstError(errors[errorList[0]], this.label); + } + } + return ''; + } + + get value() { + return this.control_value; + } + + set value(value) { + this.control_value = value; + } + + get disabled() { + return this.control?.disabled ?? false; + } + + get meta() { + return this.control?.meta; + } + + public handleEvent(event: Event) { + switch (event.type) { + case 'focus': + this.focused = true; + return true; + case 'blur': + this.focused = false; + return false; + default: + return null; + } + } + + writeValue(obj: unknown): void { + this.value = obj; + } + + registerOnChange(fn: (event: unknown) => void): void { + this.onChange = fn; + } + + registerOnTouched(fn: () => void): void { + this.onTouched = fn; + } + + trackBy(i: number) { + return i; + } } diff --git a/src/app/forms/components/checkbox-group/checkbox-group.component.spec.ts b/src/app/forms/components/checkbox-group/checkbox-group.component.spec.ts index b76049e456..19b676e853 100644 --- a/src/app/forms/components/checkbox-group/checkbox-group.component.spec.ts +++ b/src/app/forms/components/checkbox-group/checkbox-group.component.spec.ts @@ -9,80 +9,80 @@ import { FieldErrorMessageComponent } from '../field-error-message/field-error-m import { CheckboxGroupComponent } from './checkbox-group.component'; @Component({ - selector: 'app-host-component', - template: `
+ selector: 'app-host-component', + template: `
`, - styles: [], + styles: [], }) class HostComponent { - form = new FormGroup({ - foo: new CustomFormControl({ name: 'foo', type: FormNodeTypes.CONTROL, children: [] }, null), - }); - delimited?: { regex: string; separator: string }; - options: MultiOptions = [ - { label: 'Value 1', value: '1' }, - { label: 'Value 2', value: '2' }, - { label: 'Value 3', value: '3' }, - ]; + form = new FormGroup({ + foo: new CustomFormControl({ name: 'foo', type: FormNodeTypes.CONTROL, children: [] }, null), + }); + delimited?: { regex: string; separator: string }; + options: MultiOptions = [ + { label: 'Value 1', value: '1' }, + { label: 'Value 2', value: '2' }, + { label: 'Value 3', value: '3' }, + ]; } describe('CheckboxGroupComponent', () => { - let component: HostComponent; - let fixture: ComponentFixture; + let component: HostComponent; + let fixture: ComponentFixture; - beforeEach(async () => { - await TestBed.configureTestingModule({ - declarations: [HostComponent, CheckboxGroupComponent, BaseControlComponent, FieldErrorMessageComponent], - imports: [FormsModule, ReactiveFormsModule], - }).compileComponents(); - }); + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [HostComponent, CheckboxGroupComponent, BaseControlComponent, FieldErrorMessageComponent], + imports: [FormsModule, ReactiveFormsModule], + }).compileComponents(); + }); - beforeEach(() => { - fixture = TestBed.createComponent(HostComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); + beforeEach(() => { + fixture = TestBed.createComponent(HostComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); - describe('value as array', () => { - it('should be propagated from element to the form control', () => { - const foo = component.form.get('foo'); - const boxes = fixture.debugElement.queryAll(By.css('input[type="checkbox"]')); - expect(boxes).toHaveLength(3); + describe('value as array', () => { + it('should be propagated from element to the form control', () => { + const foo = component.form.get('foo'); + const boxes = fixture.debugElement.queryAll(By.css('input[type="checkbox"]')); + expect(boxes).toHaveLength(3); - (boxes[1].nativeElement as HTMLInputElement).click(); - (boxes[0].nativeElement as HTMLInputElement).click(); - expect(foo?.value).toEqual(['2', '1']); - (boxes[1].nativeElement as HTMLInputElement).click(); - (boxes[0].nativeElement as HTMLInputElement).click(); - expect(foo?.value).toBeNull(); - }); - }); + (boxes[1].nativeElement as HTMLInputElement).click(); + (boxes[0].nativeElement as HTMLInputElement).click(); + expect(foo?.value).toEqual(['2', '1']); + (boxes[1].nativeElement as HTMLInputElement).click(); + (boxes[0].nativeElement as HTMLInputElement).click(); + expect(foo?.value).toBeNull(); + }); + }); - describe('value as separated string', () => { - it('should be propagated from element to the form control', () => { - component.delimited = { regex: '\\. (? { + it('should be propagated from element to the form control', () => { + component.delimited = { regex: '\\. (? { - it('should be propagated from the form control to the element', () => { - component.form.patchValue({ foo: ['1', '2'] }); - fixture.detectChanges(); - const checkedBoxes = fixture.debugElement.queryAll(By.css('input[type="checkbox"][checked=true]')); - expect(checkedBoxes).toHaveLength(2); - }); - }); + describe('value propagated to element', () => { + it('should be propagated from the form control to the element', () => { + component.form.patchValue({ foo: ['1', '2'] }); + fixture.detectChanges(); + const checkedBoxes = fixture.debugElement.queryAll(By.css('input[type="checkbox"][checked=true]')); + expect(checkedBoxes).toHaveLength(2); + }); + }); }); diff --git a/src/app/forms/components/checkbox-group/checkbox-group.component.ts b/src/app/forms/components/checkbox-group/checkbox-group.component.ts index 21d57351fc..00c9982fa9 100644 --- a/src/app/forms/components/checkbox-group/checkbox-group.component.ts +++ b/src/app/forms/components/checkbox-group/checkbox-group.component.ts @@ -5,55 +5,56 @@ import { BaseControlComponent } from '../base-control/base-control.component'; type OptionsType = string | number | boolean; @Component({ - selector: 'app-checkbox-group', - templateUrl: './checkbox-group.component.html', - providers: [ - { - provide: NG_VALUE_ACCESSOR, - useExisting: CheckboxGroupComponent, - multi: true, - }, - ], + selector: 'app-checkbox-group', + templateUrl: './checkbox-group.component.html', + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: CheckboxGroupComponent, + multi: true, + }, + ], }) export class CheckboxGroupComponent extends BaseControlComponent { - @Input() options: FormNodeOption[] = []; - @Input() delimited?: { regex?: string; separator: string }; + @Input() options: FormNodeOption[] = []; + @Input() delimited?: { regex?: string; separator: string }; - isChecked(option: string | number | boolean): boolean { - return this.value && this.value.includes(option); - } + isChecked(option: string | number | boolean): boolean { + return this.value && this.value.includes(option); + } - handleChange(event: boolean, option: FormNodeOption): void { - return event ? this.add(option) : this.remove(option); - } + handleChange(event: boolean, option: FormNodeOption): void { + return event ? this.add(option) : this.remove(option); + } - private add(option: FormNodeOption) { - if (!this.value) { - this.value = this.delimited ? option.value : [option.value]; - } else { - this.value = this.value.concat(this.delimited ? `${this.delimited.separator}${option.value}` : option.value); - } + private add(option: FormNodeOption) { + if (!this.value) { + this.value = this.delimited ? option.value : [option.value]; + } else { + this.value = this.value.concat(this.delimited ? `${this.delimited.separator}${option.value}` : option.value); + } - this.onChange(this.value); - } + this.onChange(this.value); + } - private remove(option: FormNodeOption) { - // eslint-disable-next-line security/detect-non-literal-regexp - const separator = this.delimited && this.delimited?.regex ? new RegExp(this.delimited?.regex) : this.delimited?.separator; + private remove(option: FormNodeOption) { + // eslint-disable-next-line security/detect-non-literal-regexp + const separator = + this.delimited && this.delimited?.regex ? new RegExp(this.delimited?.regex) : this.delimited?.separator; - let newValue = separator ? this.value?.split(separator) : [...this.value]; + let newValue = separator ? this.value?.split(separator) : [...this.value]; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - newValue = newValue?.filter((v: any) => v !== option.value); + // eslint-disable-next-line @typescript-eslint/no-explicit-any + newValue = newValue?.filter((v: any) => v !== option.value); - newValue = separator ? newValue?.join(this.delimited?.separator) : newValue; + newValue = separator ? newValue?.join(this.delimited?.separator) : newValue; - this.value = newValue !== '' && newValue?.length !== 0 ? newValue : null; + this.value = newValue !== '' && newValue?.length !== 0 ? newValue : null; - this.onChange(this.value); - } + this.onChange(this.value); + } - trackByFn(i: number) { - return i; - } + trackByFn(i: number) { + return i; + } } diff --git a/src/app/forms/components/checkbox-group/checkbox-group.stories.ts b/src/app/forms/components/checkbox-group/checkbox-group.stories.ts index 538382ce1a..b63d08bb6e 100644 --- a/src/app/forms/components/checkbox-group/checkbox-group.stories.ts +++ b/src/app/forms/components/checkbox-group/checkbox-group.stories.ts @@ -5,62 +5,62 @@ import { BaseControlComponent } from '../base-control/base-control.component'; import { CheckboxGroupComponent } from './checkbox-group.component'; export default { - title: 'Forms/Checkbox group', - component: CheckboxGroupComponent, - args: { - options: [ - { label: 'Red', value: 'red' }, - { label: 'Green', value: 'green' }, - { label: 'Blue', value: 'blue' } - ] - }, - argTypes: { - label: { - name: 'label', - type: 'string', - description: 'Fieldset label' - } - }, - decorators: [ - moduleMetadata({ - declarations: [CheckboxGroupComponent, BaseControlComponent], - imports: [CommonModule, FormsModule, ReactiveFormsModule] - }) - ] + title: 'Forms/Checkbox group', + component: CheckboxGroupComponent, + args: { + options: [ + { label: 'Red', value: 'red' }, + { label: 'Green', value: 'green' }, + { label: 'Blue', value: 'blue' }, + ], + }, + argTypes: { + label: { + name: 'label', + type: 'string', + description: 'Fieldset label', + }, + }, + decorators: [ + moduleMetadata({ + declarations: [CheckboxGroupComponent, BaseControlComponent], + imports: [CommonModule, FormsModule, ReactiveFormsModule], + }), + ], } as Meta; -const Template: Story = args => { - const { label, options, name, hint, value = [], disabled = false, validators = [] } = args; - const form = new FormGroup({ [name]: new FormControl({ value, disabled }, validators) }); - return { - component: CheckboxGroupComponent, - template: `
{{ form.value | json }}
`, - props: { - form, - label, - name, - options, - hint - } - }; +const Template: Story = (args) => { + const { label, options, name, hint, value = [], disabled = false, validators = [] } = args; + const form = new FormGroup({ [name]: new FormControl({ value, disabled }, validators) }); + return { + component: CheckboxGroupComponent, + template: `
{{ form.value | json }}
`, + props: { + form, + label, + name, + options, + hint, + }, + }; }; export const Enabled = Template.bind({}); Enabled.args = { - label: 'Colors', - name: 'colors' + label: 'Colors', + name: 'colors', }; export const Disabled = Template.bind({}); Disabled.args = { - ...Enabled.args, - disabled: true + ...Enabled.args, + disabled: true, }; export const Invalid = Template.bind({}); Invalid.args = { - ...Enabled.args, - value: ['red'], - hint: 'uncheck "Red" and click outside to see error message.', - validators: [Validators.required] + ...Enabled.args, + value: ['red'], + hint: 'uncheck "Red" and click outside to see error message.', + validators: [Validators.required], }; diff --git a/src/app/forms/components/checkbox/checkbox.component.spec.ts b/src/app/forms/components/checkbox/checkbox.component.spec.ts index 67e56ecd15..4ff559b985 100644 --- a/src/app/forms/components/checkbox/checkbox.component.spec.ts +++ b/src/app/forms/components/checkbox/checkbox.component.spec.ts @@ -5,35 +5,35 @@ import { CustomFormControl, FormNodeTypes } from '@forms/services/dynamic-form.t import { CheckboxComponent } from './checkbox.component'; @Component({ - selector: 'app-host-component', - template: `
+ selector: 'app-host-component', + template: `
`, - styles: [], + styles: [], }) class HostComponent { - form = new FormGroup({ - foo: new CustomFormControl({ name: 'foo', type: FormNodeTypes.CONTROL, children: [] }, ''), - }); + form = new FormGroup({ + foo: new CustomFormControl({ name: 'foo', type: FormNodeTypes.CONTROL, children: [] }, ''), + }); } describe('CheckboxComponent', () => { - let component: HostComponent; - let fixture: ComponentFixture; + let component: HostComponent; + let fixture: ComponentFixture; - beforeEach(async () => { - await TestBed.configureTestingModule({ - declarations: [CheckboxComponent], - }).compileComponents(); - }); + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [CheckboxComponent], + }).compileComponents(); + }); - beforeEach(() => { - fixture = TestBed.createComponent(HostComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); + beforeEach(() => { + fixture = TestBed.createComponent(HostComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); }); diff --git a/src/app/forms/components/checkbox/checkbox.component.ts b/src/app/forms/components/checkbox/checkbox.component.ts index b14574eb37..2155065234 100644 --- a/src/app/forms/components/checkbox/checkbox.component.ts +++ b/src/app/forms/components/checkbox/checkbox.component.ts @@ -3,8 +3,8 @@ import { NG_VALUE_ACCESSOR } from '@angular/forms'; import { BaseControlComponent } from '../base-control/base-control.component'; @Component({ - selector: 'app-checkbox', - templateUrl: './checkbox.component.html', - providers: [{ provide: NG_VALUE_ACCESSOR, useExisting: CheckboxComponent, multi: true }], + selector: 'app-checkbox', + templateUrl: './checkbox.component.html', + providers: [{ provide: NG_VALUE_ACCESSOR, useExisting: CheckboxComponent, multi: true }], }) export class CheckboxComponent extends BaseControlComponent {} diff --git a/src/app/forms/components/contingency-adr-generate-cert/contingency-adr-generate-cert.component.spec.ts b/src/app/forms/components/contingency-adr-generate-cert/contingency-adr-generate-cert.component.spec.ts index d21c7286da..99e8db312e 100644 --- a/src/app/forms/components/contingency-adr-generate-cert/contingency-adr-generate-cert.component.spec.ts +++ b/src/app/forms/components/contingency-adr-generate-cert/contingency-adr-generate-cert.component.spec.ts @@ -14,103 +14,104 @@ import { ReplaySubject, of } from 'rxjs'; import { ContingencyAdrGenerateCertComponent } from './contingency-adr-generate-cert.component'; const mockRecord = { - systemNumber: 'sysNum', - createdTimestamp: 'now', - techRecord_statusCode: 'current', - techRecord_adrPassCertificateDetails: [ - { - createdByName: 'tester', - certificateType: 'pass', - generatedTimestamp: '2025-01-12T10:36:33.987Z', - certificateId: '12345', - }, - { - createdByName: 'tester', - certificateType: 'pass', - generatedTimestamp: '2025-01-14T10:36:33.987Z', - certificateId: '12345', - }, - ], - techRecord_vehicleType: 'hgv', - partialVin: '4321', - vin: '87654321', - techRecord_createdAt: 'now', - techRecord_createdById: 'tester', - techRecord_createdByName: 'tester', - techRecord_reasonForCreation: 'test', - techRecord_recordCompleteness: 'skeleton', - techRecord_vehicleClass_description: 'heavy goods vehicle', - techRecord_bodyType_description: 'box', - primaryVrm: '12345', - + systemNumber: 'sysNum', + createdTimestamp: 'now', + techRecord_statusCode: 'current', + techRecord_adrPassCertificateDetails: [ + { + createdByName: 'tester', + certificateType: 'pass', + generatedTimestamp: '2025-01-12T10:36:33.987Z', + certificateId: '12345', + }, + { + createdByName: 'tester', + certificateType: 'pass', + generatedTimestamp: '2025-01-14T10:36:33.987Z', + certificateId: '12345', + }, + ], + techRecord_vehicleType: 'hgv', + partialVin: '4321', + vin: '87654321', + techRecord_createdAt: 'now', + techRecord_createdById: 'tester', + techRecord_createdByName: 'tester', + techRecord_reasonForCreation: 'test', + techRecord_recordCompleteness: 'skeleton', + techRecord_vehicleClass_description: 'heavy goods vehicle', + techRecord_bodyType_description: 'box', + primaryVrm: '12345', } as unknown as V3TechRecordModel; describe('AdrGenerateCertTestComponent', () => { - let component: ContingencyAdrGenerateCertComponent; - let fixture: ComponentFixture; - let store: Store; - let techRecordService: TechnicalRecordService; - let actions$: ReplaySubject; - beforeEach(async () => { - await TestBed.configureTestingModule({ - declarations: [ContingencyAdrGenerateCertComponent], - providers: [ - provideMockStore({ initialState: initialAppState }), - { provide: ActivatedRoute, useValue: { params: of([{ id: 1 }]) } }, - provideMockActions(() => actions$), - TechnicalRecordService, - ], - imports: [HttpClientTestingModule], - schemas: [CUSTOM_ELEMENTS_SCHEMA], - }).compileComponents(); - }); - beforeEach(() => { - fixture = TestBed.createComponent(ContingencyAdrGenerateCertComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - store = TestBed.inject(Store); - techRecordService = TestBed.inject(TechnicalRecordService); - fixture = TestBed.createComponent(ContingencyAdrGenerateCertComponent); - component = fixture.componentInstance; - }); + let component: ContingencyAdrGenerateCertComponent; + let fixture: ComponentFixture; + let store: Store; + let techRecordService: TechnicalRecordService; + let actions$: ReplaySubject; + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ContingencyAdrGenerateCertComponent], + providers: [ + provideMockStore({ initialState: initialAppState }), + { provide: ActivatedRoute, useValue: { params: of([{ id: 1 }]) } }, + provideMockActions(() => actions$), + TechnicalRecordService, + ], + imports: [HttpClientTestingModule], + schemas: [CUSTOM_ELEMENTS_SCHEMA], + }).compileComponents(); + }); + beforeEach(() => { + fixture = TestBed.createComponent(ContingencyAdrGenerateCertComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + store = TestBed.inject(Store); + techRecordService = TestBed.inject(TechnicalRecordService); + fixture = TestBed.createComponent(ContingencyAdrGenerateCertComponent); + component = fixture.componentInstance; + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); - describe('lastCertificateDate', () => { - it('should return the most recent certificate date generation date', () => { - jest.spyOn(techRecordService, 'techRecord$', 'get').mockReturnValue(of(mockRecord)); - expect(component.lastCertificateDate).toBe('An ADR certificate was last generated on 14/01/2025'); - }); - it('should return a default string if there are no ADR certificates on a record', () => { - const newMock = { ...mockRecord, techRecord_adrPassCertificateDetails: [] }; - jest.spyOn(techRecordService, 'techRecord$', 'get').mockReturnValue(of(newMock)); - expect(component.lastCertificateDate).toBe('There are no previous ADR certificates for this vehicle'); - }); - }); + describe('lastCertificateDate', () => { + it('should return the most recent certificate date generation date', () => { + jest.spyOn(techRecordService, 'techRecord$', 'get').mockReturnValue(of(mockRecord)); + expect(component.lastCertificateDate).toBe('An ADR certificate was last generated on 14/01/2025'); + }); + it('should return a default string if there are no ADR certificates on a record', () => { + const newMock = { ...mockRecord, techRecord_adrPassCertificateDetails: [] }; + jest.spyOn(techRecordService, 'techRecord$', 'get').mockReturnValue(of(newMock)); + expect(component.lastCertificateDate).toBe('There are no previous ADR certificates for this vehicle'); + }); + }); - describe('documentParams', () => { - it('should return a map in correct format', () => { - expect(component.documentParams('testFileName')).toEqual(new Map([['fileName', 'testFileName']])); - }); - }); + describe('documentParams', () => { + it('should return a map in correct format', () => { + expect(component.documentParams('testFileName')).toEqual(new Map([['fileName', 'testFileName']])); + }); + }); - describe('handleSubmit', () => { - beforeEach(() => { - jest.spyOn(techRecordService, 'techRecord$', 'get').mockReturnValue(of(mockRecord)); - }); - it('should dispatch the correct action with correct params', () => { - const dispatchSpy = jest.spyOn(store, 'dispatch'); - component.ngOnInit(); - component.handleSubmit(); + describe('handleSubmit', () => { + beforeEach(() => { + jest.spyOn(techRecordService, 'techRecord$', 'get').mockReturnValue(of(mockRecord)); + }); + it('should dispatch the correct action with correct params', () => { + const dispatchSpy = jest.spyOn(store, 'dispatch'); + component.ngOnInit(); + component.handleSubmit(); - expect(dispatchSpy).toHaveBeenCalled(); - expect(dispatchSpy).toHaveBeenCalledWith(generateADRCertificate({ - systemNumber: 'sysNum', - createdTimestamp: 'now', - certificateType: 'PASS', - })); - }); - }); + expect(dispatchSpy).toHaveBeenCalled(); + expect(dispatchSpy).toHaveBeenCalledWith( + generateADRCertificate({ + systemNumber: 'sysNum', + createdTimestamp: 'now', + certificateType: 'PASS', + }) + ); + }); + }); }); diff --git a/src/app/forms/components/contingency-adr-generate-cert/contingency-adr-generate-cert.component.ts b/src/app/forms/components/contingency-adr-generate-cert/contingency-adr-generate-cert.component.ts index a2ce418825..dd8bbf11ed 100644 --- a/src/app/forms/components/contingency-adr-generate-cert/contingency-adr-generate-cert.component.ts +++ b/src/app/forms/components/contingency-adr-generate-cert/contingency-adr-generate-cert.component.ts @@ -1,9 +1,9 @@ -import { - Component, - inject, -} from '@angular/core'; +import { Component, inject } from '@angular/core'; import { ADRCertificateDetails } from '@dvsa/cvs-type-definitions/types/v3/tech-record/get/trl/complete'; -import { TechRecordGETHGV, TechRecordGETTRL } from '@dvsa/cvs-type-definitions/types/v3/tech-record/tech-record-verb-vehicle-type'; +import { + TechRecordGETHGV, + TechRecordGETTRL, +} from '@dvsa/cvs-type-definitions/types/v3/tech-record/tech-record-verb-vehicle-type'; import { CustomFormControlComponent } from '@forms/custom-sections/custom-form-control/custom-form-control.component'; import { Actions, ofType } from '@ngrx/effects'; import { Store } from '@ngrx/store'; @@ -12,73 +12,74 @@ import { TechnicalRecordService } from '@services/technical-record/technical-rec import { State } from '@store/index'; import { retryInterceptorFailure } from '@store/retry-interceptor/actions/retry-interceptor.actions'; import { generateADRCertificate, generateADRCertificateSuccess } from '@store/technical-records'; -import { - Subject, - take, - takeUntil, -} from 'rxjs'; +import { Subject, take, takeUntil } from 'rxjs'; @Component({ - selector: 'app-contingency-adr-generate-cert', - templateUrl: './contingency-adr-generate-cert.component.html', - styleUrls: ['./contingency-adr-generate-cert.component.scss'], + selector: 'app-contingency-adr-generate-cert', + templateUrl: './contingency-adr-generate-cert.component.html', + styleUrls: ['./contingency-adr-generate-cert.component.scss'], }) export class ContingencyAdrGenerateCertComponent extends CustomFormControlComponent { - systemNumber?: string; - createdTimestamp?: string; - store = inject(Store); - techRecordService = inject(TechnicalRecordService); - actions$ = inject(Actions); - loading = inject(LoadingService); - fileName?: string; - errorString?: string | null; + systemNumber?: string; + createdTimestamp?: string; + store = inject(Store); + techRecordService = inject(TechnicalRecordService); + actions$ = inject(Actions); + loading = inject(LoadingService); + fileName?: string; + errorString?: string | null; - private destroy$ = new Subject(); + private destroy$ = new Subject(); - ngOnInit(): void { - this.techRecordService.techRecord$.pipe(takeUntil(this.destroy$)).subscribe((record) => { - this.systemNumber = (record as TechRecordGETHGV).systemNumber; - this.createdTimestamp = (record as TechRecordGETHGV).createdTimestamp; - }); + ngOnInit(): void { + this.techRecordService.techRecord$.pipe(takeUntil(this.destroy$)).subscribe((record) => { + this.systemNumber = (record as TechRecordGETHGV).systemNumber; + this.createdTimestamp = (record as TechRecordGETHGV).createdTimestamp; + }); - this.actions$.pipe(ofType(generateADRCertificateSuccess), takeUntil(this.destroy$)).subscribe(({ id }) => { - this.fileName = id; - this.cdr.detectChanges(); - }); + this.actions$.pipe(ofType(generateADRCertificateSuccess), takeUntil(this.destroy$)).subscribe(({ id }) => { + this.fileName = id; + this.cdr.detectChanges(); + }); - this.actions$.pipe(ofType(retryInterceptorFailure), takeUntil(this.destroy$)).subscribe(() => { - this.errorString = 'Try link again or Enter 000000 in Certificate Number and then press "Pass And Issue Documents Centrally" on TAS'; - this.cdr.detectChanges(); - }); - } + this.actions$.pipe(ofType(retryInterceptorFailure), takeUntil(this.destroy$)).subscribe(() => { + this.errorString = + 'Try link again or Enter 000000 in Certificate Number and then press "Pass And Issue Documents Centrally" on TAS'; + this.cdr.detectChanges(); + }); + } - ngOnDestroy(): void { - this.destroy$.next(); - this.destroy$.complete(); - } + ngOnDestroy(): void { + this.destroy$.next(); + this.destroy$.complete(); + } - get lastCertificateDate() { - let sortedTests: ADRCertificateDetails[] | undefined; - this.techRecordService.techRecord$.pipe(take(1)).subscribe((record) => { - sortedTests = (record as TechRecordGETHGV | TechRecordGETTRL).techRecord_adrPassCertificateDetails?.sort((a, b) => - a.generatedTimestamp && b.generatedTimestamp ? new Date(b.generatedTimestamp).getTime() - new Date(a.generatedTimestamp).getTime() : 0); - }); - return (sortedTests && sortedTests?.length > 0) - ? `An ADR certificate was last generated on ${new Date(sortedTests[0].generatedTimestamp).toLocaleDateString('en-UK')}` - : 'There are no previous ADR certificates for this vehicle'; - } + get lastCertificateDate() { + let sortedTests: ADRCertificateDetails[] | undefined; + this.techRecordService.techRecord$.pipe(take(1)).subscribe((record) => { + sortedTests = (record as TechRecordGETHGV | TechRecordGETTRL).techRecord_adrPassCertificateDetails?.sort( + (a, b) => + a.generatedTimestamp && b.generatedTimestamp + ? new Date(b.generatedTimestamp).getTime() - new Date(a.generatedTimestamp).getTime() + : 0 + ); + }); + return sortedTests && sortedTests?.length > 0 + ? `An ADR certificate was last generated on ${new Date(sortedTests[0].generatedTimestamp).toLocaleDateString('en-UK')}` + : 'There are no previous ADR certificates for this vehicle'; + } - documentParams(certificate: string): Map { - return new Map([['fileName', certificate]]); - } + documentParams(certificate: string): Map { + return new Map([['fileName', certificate]]); + } - handleSubmit(): void { - this.store.dispatch(generateADRCertificate( - { - systemNumber: this.systemNumber ?? '', - createdTimestamp: this.createdTimestamp ?? '', - certificateType: 'PASS', - }, - )); - } + handleSubmit(): void { + this.store.dispatch( + generateADRCertificate({ + systemNumber: this.systemNumber ?? '', + createdTimestamp: this.createdTimestamp ?? '', + certificateType: 'PASS', + }) + ); + } } diff --git a/src/app/forms/components/date/date.component.spec.ts b/src/app/forms/components/date/date.component.spec.ts index b4d4dccbe1..fdc68ba2d5 100644 --- a/src/app/forms/components/date/date.component.spec.ts +++ b/src/app/forms/components/date/date.component.spec.ts @@ -1,8 +1,6 @@ /* eslint-disable jest/no-conditional-expect */ import { Component, ViewChild } from '@angular/core'; -import { - ComponentFixture, TestBed, fakeAsync, tick, -} from '@angular/core/testing'; +import { ComponentFixture, TestBed, fakeAsync, tick } from '@angular/core/testing'; import { FormGroup, FormsModule, ReactiveFormsModule } from '@angular/forms'; import { GlobalErrorService } from '@core/components/global-error/global-error.service'; import { CustomFormControl, FormNodeTypes } from '@forms/services/dynamic-form.types'; @@ -14,170 +12,188 @@ import { DateComponent } from './date.component'; import { FocusNextDirective } from './focus-next.directive'; @Component({ - selector: 'app-host-component', - template: `
+ selector: 'app-host-component', + template: `
`, - styles: [], + styles: [], }) class HostComponent { - @ViewChild(DateComponent, { static: true }) dateComponent?: DateComponent; - form = new FormGroup({ - foo: new CustomFormControl({ name: 'foo', type: FormNodeTypes.CONTROL, children: [] }, null), - }); + @ViewChild(DateComponent, { static: true }) dateComponent?: DateComponent; + form = new FormGroup({ + foo: new CustomFormControl({ name: 'foo', type: FormNodeTypes.CONTROL, children: [] }, null), + }); } describe('DateComponent', () => { - let component: HostComponent; - let fixture: ComponentFixture; - - beforeEach(async () => { - await TestBed.configureTestingModule({ - declarations: [BaseControlComponent, DateComponent, FieldErrorMessageComponent, FocusNextDirective, HostComponent], - imports: [FormsModule, ReactiveFormsModule], - providers: [GlobalErrorService, provideMockStore({ initialState: initialAppState })], - }).compileComponents(); - }); - - beforeEach(() => { - fixture = TestBed.createComponent(HostComponent); - component = fixture.componentInstance; - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); - - it('should add validators', () => { - const addValidatorsSpy = jest.spyOn(component.dateComponent as DateComponent, 'addValidators'); - fixture.detectChanges(); - - expect(addValidatorsSpy).toHaveBeenCalledTimes(1); - }); - - describe('control values', () => { - it.each([ - ['2342346-6213-234T00:00:00.000', 2342346, 6213, 234, 1, 6], - ['2022-01-12T00:00:00.000', 2022, 0o1, 12, 1, 6], - ['2022--01T00:00:00.000', 2022, NaN, 0o1, 0o1, 0o1], - ['2022-01-T00:00:00.000', 2022, 0o1, NaN, 0o1, 0o1], - ['2022-02-01T13:45:00.000', 2022, 2, 0o1, 13, 45, true], - ['-01-01T01:01:00.000', NaN, 0o1, 0o1, 0o1, 0o1, true], - [undefined, undefined, undefined, undefined, undefined, undefined, true], - ])( - 'should be %s for %d, %d, %d, %d, %d', - ( - expected: string | undefined, - year: number | undefined, - month: number | undefined, - day: number | undefined, - hour: number | undefined, - minute: number | undefined, - displayTime = false, - ) => { - if (component.dateComponent) { - component.dateComponent.originalDate = '2022-01-01T01:06:00.000'; - component.dateComponent.displayTime = displayTime; - } - - fixture.detectChanges(); - - component.dateComponent?.onDayChange(day); - component.dateComponent?.onMonthChange(month); - component.dateComponent?.onYearChange(year); - component.dateComponent?.onHourChange(hour); - component.dateComponent?.onMinuteChange(minute); - if (expected === undefined) { - expect(component.form.get('foo')?.value).toBeNull(); - } else { - expect((component.form.get('foo')?.value as Date).toString()).toEqual(expected.toString()); - } - }, - ); - - it.each([ - ['2342346-6213-234', 2342346, 6213, 234, 1, 6], - ['2022-01-12', 2022, 0o1, 12, 1, 6], - ['2022--01', 2022, NaN, 0o1, 0o1, 0o1], - ['2022-01-', 2022, 0o1, NaN, 0o1, 0o1], - ['2022-02-01', 2022, 2, 0o1, 13, 45, true], - ['-01-01', NaN, 0o1, 0o1, 0o1, 0o1, true], - [undefined, undefined, undefined, undefined, undefined, undefined, true], - ])( - 'should be %s for %d, %d, %d, %d, %d', - ( - expected: string | undefined, - year: number | undefined, - month: number | undefined, - day: number | undefined, - hour: number | undefined, - minute: number | undefined, - displayTime = false, - ) => { - if (component.dateComponent) { - component.dateComponent.originalDate = '2022-01-01T01:06:00.000'; - component.dateComponent.displayTime = displayTime; - component.dateComponent.isoDate = false; - } - - fixture.detectChanges(); - - component.dateComponent?.onDayChange(day); - component.dateComponent?.onMonthChange(month); - component.dateComponent?.onYearChange(year); - component.dateComponent?.onHourChange(hour); - component.dateComponent?.onMinuteChange(minute); - if (expected === undefined) { - expect(component.form.get('foo')?.value).toBeNull(); - } else { - expect((component.form.get('foo')?.value as Date).toString()).toEqual(expected.toString()); - } - }, - ); - - it('should propagate control value to subjects', fakeAsync(() => { - const date = new Date('1995-12-17T03:24:00'); - component.dateComponent?.control?.patchValue(date.toISOString()); - - component.dateComponent?.valueWriteBack(date.toISOString()); - - tick(); - component.dateComponent?.control?.meta.changeDetection?.detectChanges(); - - expect(component.dateComponent?.day).toEqual(date.getDate()); - expect(component.dateComponent?.month).toEqual(date.getMonth() + 1); - expect(component.dateComponent?.year).toEqual(date.getFullYear()); - expect(component.dateComponent?.hour).toEqual(date.getUTCHours()); - expect(component.dateComponent?.minute).toEqual(date.getUTCMinutes()); - })); - }); - - describe('error handling', () => { - it('should return empty if the day, month and year are not defined', () => { - if (component.dateComponent) { - component.dateComponent.errors = { error: true, date: new Date(), errors: [{ error: false, reason: 'foo', index: 1 }] }; - } - expect(component.dateComponent?.elementHasErrors(1)).toBe(false); - }); - - it('should return true if there are some errors with the same index', () => { - if (component.dateComponent) { - component.dateComponent.day = 2; - component.dateComponent.year = 2021; - component.dateComponent.month = 2; - component.dateComponent.errors = { error: true, date: new Date(), errors: [{ error: false, reason: 'foo', index: 1 }] }; - } - expect(component.dateComponent?.elementHasErrors(1)).toBe(true); - }); - - it('should return false if there are no errors with the same index', () => { - if (component.dateComponent) { - component.dateComponent.day = 2; - component.dateComponent.year = 2021; - component.dateComponent.month = 2; - component.dateComponent.errors = { error: true, date: new Date(), errors: [{ error: false, reason: 'foo', index: 1 }] }; - } - expect(component.dateComponent?.elementHasErrors(2)).toBe(false); - }); - }); + let component: HostComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ + BaseControlComponent, + DateComponent, + FieldErrorMessageComponent, + FocusNextDirective, + HostComponent, + ], + imports: [FormsModule, ReactiveFormsModule], + providers: [GlobalErrorService, provideMockStore({ initialState: initialAppState })], + }).compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(HostComponent); + component = fixture.componentInstance; + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + it('should add validators', () => { + const addValidatorsSpy = jest.spyOn(component.dateComponent as DateComponent, 'addValidators'); + fixture.detectChanges(); + + expect(addValidatorsSpy).toHaveBeenCalledTimes(1); + }); + + describe('control values', () => { + it.each([ + ['2342346-6213-234T00:00:00.000', 2342346, 6213, 234, 1, 6], + ['2022-01-12T00:00:00.000', 2022, 0o1, 12, 1, 6], + ['2022--01T00:00:00.000', 2022, Number.NaN, 0o1, 0o1, 0o1], + ['2022-01-T00:00:00.000', 2022, 0o1, Number.NaN, 0o1, 0o1], + ['2022-02-01T13:45:00.000', 2022, 2, 0o1, 13, 45, true], + ['-01-01T01:01:00.000', Number.NaN, 0o1, 0o1, 0o1, 0o1, true], + [undefined, undefined, undefined, undefined, undefined, undefined, true], + ])( + 'should be %s for %d, %d, %d, %d, %d', + ( + expected: string | undefined, + year: number | undefined, + month: number | undefined, + day: number | undefined, + hour: number | undefined, + minute: number | undefined, + displayTime = false + ) => { + if (component.dateComponent) { + component.dateComponent.originalDate = '2022-01-01T01:06:00.000'; + component.dateComponent.displayTime = displayTime; + } + + fixture.detectChanges(); + + component.dateComponent?.onDayChange(day); + component.dateComponent?.onMonthChange(month); + component.dateComponent?.onYearChange(year); + component.dateComponent?.onHourChange(hour); + component.dateComponent?.onMinuteChange(minute); + if (expected === undefined) { + expect(component.form.get('foo')?.value).toBeNull(); + } else { + expect((component.form.get('foo')?.value as Date).toString()).toEqual(expected.toString()); + } + } + ); + + it.each([ + ['2342346-6213-234', 2342346, 6213, 234, 1, 6], + ['2022-01-12', 2022, 0o1, 12, 1, 6], + ['2022--01', 2022, Number.NaN, 0o1, 0o1, 0o1], + ['2022-01-', 2022, 0o1, Number.NaN, 0o1, 0o1], + ['2022-02-01', 2022, 2, 0o1, 13, 45, true], + ['-01-01', Number.NaN, 0o1, 0o1, 0o1, 0o1, true], + [undefined, undefined, undefined, undefined, undefined, undefined, true], + ])( + 'should be %s for %d, %d, %d, %d, %d', + ( + expected: string | undefined, + year: number | undefined, + month: number | undefined, + day: number | undefined, + hour: number | undefined, + minute: number | undefined, + displayTime = false + ) => { + if (component.dateComponent) { + component.dateComponent.originalDate = '2022-01-01T01:06:00.000'; + component.dateComponent.displayTime = displayTime; + component.dateComponent.isoDate = false; + } + + fixture.detectChanges(); + + component.dateComponent?.onDayChange(day); + component.dateComponent?.onMonthChange(month); + component.dateComponent?.onYearChange(year); + component.dateComponent?.onHourChange(hour); + component.dateComponent?.onMinuteChange(minute); + if (expected === undefined) { + expect(component.form.get('foo')?.value).toBeNull(); + } else { + expect((component.form.get('foo')?.value as Date).toString()).toEqual(expected.toString()); + } + } + ); + + it('should propagate control value to subjects', fakeAsync(() => { + const date = new Date('1995-12-17T03:24:00'); + component.dateComponent?.control?.patchValue(date.toISOString()); + + component.dateComponent?.valueWriteBack(date.toISOString()); + + tick(); + component.dateComponent?.control?.meta.changeDetection?.detectChanges(); + + expect(component.dateComponent?.day).toEqual(date.getDate()); + expect(component.dateComponent?.month).toEqual(date.getMonth() + 1); + expect(component.dateComponent?.year).toEqual(date.getFullYear()); + expect(component.dateComponent?.hour).toEqual(date.getUTCHours()); + expect(component.dateComponent?.minute).toEqual(date.getUTCMinutes()); + })); + }); + + describe('error handling', () => { + it('should return empty if the day, month and year are not defined', () => { + if (component.dateComponent) { + component.dateComponent.errors = { + error: true, + date: new Date(), + errors: [{ error: false, reason: 'foo', index: 1 }], + }; + } + expect(component.dateComponent?.elementHasErrors(1)).toBe(false); + }); + + it('should return true if there are some errors with the same index', () => { + if (component.dateComponent) { + component.dateComponent.day = 2; + component.dateComponent.year = 2021; + component.dateComponent.month = 2; + component.dateComponent.errors = { + error: true, + date: new Date(), + errors: [{ error: false, reason: 'foo', index: 1 }], + }; + } + expect(component.dateComponent?.elementHasErrors(1)).toBe(true); + }); + + it('should return false if there are no errors with the same index', () => { + if (component.dateComponent) { + component.dateComponent.day = 2; + component.dateComponent.year = 2021; + component.dateComponent.month = 2; + component.dateComponent.errors = { + error: true, + date: new Date(), + errors: [{ error: false, reason: 'foo', index: 1 }], + }; + } + expect(component.dateComponent?.elementHasErrors(2)).toBe(false); + }); + }); }); diff --git a/src/app/forms/components/date/date.component.ts b/src/app/forms/components/date/date.component.ts index 408a6ef7f7..e686f2ce2c 100644 --- a/src/app/forms/components/date/date.component.ts +++ b/src/app/forms/components/date/date.component.ts @@ -1,210 +1,222 @@ /* eslint-disable no-underscore-dangle */ import { - AfterContentInit, ChangeDetectorRef, Component, EventEmitter, Injector, Input, OnDestroy, OnInit, Output, ViewChild, + AfterContentInit, + ChangeDetectorRef, + Component, + EventEmitter, + Injector, + Input, + OnDestroy, + OnInit, + Output, + ViewChild, } from '@angular/core'; import { AbstractControlDirective, NG_VALUE_ACCESSOR } from '@angular/forms'; import { GlobalErrorService } from '@core/components/global-error/global-error.service'; import { ValidatorNames } from '@forms/models/validators.enum'; -import { - BehaviorSubject, Observable, Subscription, combineLatest, -} from 'rxjs'; +import { BehaviorSubject, Observable, Subscription, combineLatest } from 'rxjs'; import validateDate from 'validate-govuk-date'; import { DateValidators } from '../../validators/date/date.validators'; import { BaseControlComponent } from '../base-control/base-control.component'; type Segments = { - day: Observable; - month: Observable; - year: Observable; - hour?: Observable; - minute?: Observable; + day: Observable; + month: Observable; + year: Observable; + hour?: Observable; + minute?: Observable; }; @Component({ - selector: 'app-date', - templateUrl: './date.component.html', - providers: [ - { - provide: NG_VALUE_ACCESSOR, - useExisting: DateComponent, - multi: true, - }, - ], + selector: 'app-date', + templateUrl: './date.component.html', + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: DateComponent, + multi: true, + }, + ], }) export class DateComponent extends BaseControlComponent implements OnInit, OnDestroy, AfterContentInit { - @Input() displayTime = false; - @Input() isoDate = true; - @Input() customError? = false; - @ViewChild('dayModel') dayModel?: AbstractControlDirective; - @Output() blur = new EventEmitter(); - - private day_: BehaviorSubject = new BehaviorSubject(undefined); - private month_: BehaviorSubject = new BehaviorSubject(undefined); - private year_: BehaviorSubject = new BehaviorSubject(undefined); - private hour_: BehaviorSubject = new BehaviorSubject(undefined); - private minute_: BehaviorSubject = new BehaviorSubject(undefined); - private day$: Observable; - private month$: Observable; - private year$: Observable; - private hour$: Observable; - private minute$: Observable; - private subscriptions: Array = []; - public originalDate = ''; - public errors?: { error: boolean; date?: Date; errors?: { error: boolean; reason: string; index: number }[] }; - private dateFieldOrDefault?: Record<'hours' | 'minutes' | 'seconds', string | number>; - protected formSubmitted? = false; - - public day?: number; - public month?: number; - public year?: number; - public hour?: number; - public minute?: number; - - dayId = ''; - monthId = ''; - yearId = ''; - - constructor(injector: Injector, changeDetectorRef: ChangeDetectorRef, public globalErrorService: GlobalErrorService) { - super(injector, changeDetectorRef); - this.day$ = this.day_.asObservable(); - this.month$ = this.month_.asObservable(); - this.year$ = this.year_.asObservable(); - this.hour$ = this.hour_.asObservable(); - this.minute$ = this.minute_.asObservable(); - this.globalErrorService.errors$.subscribe((globalErrors) => { - if (globalErrors.length) { - this.formSubmitted = true; - } - }); - } - - ngOnInit(): void { - this.subscriptions.push(this.subscribeAndPropagateChanges()); - this.dayId = `${this.customId ?? this.name}-day`; - this.monthId = `${this.customId ?? this.name}-month`; - this.yearId = `${this.customId ?? this.name}-year`; - } - - override ngAfterContentInit(): void { - super.ngAfterContentInit(); - this.originalDate = this.value; - this.dateFieldOrDefault = { - hours: this.originalDate ? new Date(this.originalDate).getHours() : '00', - minutes: this.originalDate ? new Date(this.originalDate).getMinutes() : '00', - seconds: this.originalDate ? new Date(this.originalDate).getSeconds() : '00', - }; - this.addValidators(); - this.valueWriteBack(this.value); - } - - ngOnDestroy(): void { - this.subscriptions.forEach((s) => s && s.unsubscribe()); - } - - onDayChange(event: number | undefined) { - this.day_.next(event); - } - - onMonthChange(event: number | undefined) { - this.month_.next(event); - } - - onYearChange(event: number | undefined) { - this.year_.next(event); - } - - onHourChange(event: number | undefined) { - this.hour_.next(event); - } - - onMinuteChange(event: number | undefined) { - this.minute_.next(event); - } - - valueWriteBack(value: string | null): void { - if (value && typeof value === 'string') { - const date = new Date(value); - this.day = date.getDate(); - this.day_.next(this.day); - this.month = date.getMonth() + 1; - this.month_.next(this.month); - this.year = date.getFullYear(); - this.year_.next(this.year); - this.hour = date.getHours(); - this.hour_.next(this.hour); - this.minute = date.getMinutes(); - this.minute_.next(this.minute); - } - } - - /** - * Subscribes to all date segments and propagates value as `Date`. - * @returns Subscription - */ - subscribeAndPropagateChanges() { - const dateFields: Segments = this.displayTime - ? { - day: this.day$, - month: this.month$, - year: this.year$, - hour: this.hour$, - minute: this.minute$, - } - : { day: this.day$, month: this.month$, year: this.year$ }; - return combineLatest(dateFields).subscribe({ - next: ({ - day, month, year, hour, minute, - }) => { - if (!day && !month && !year && !hour && !minute) { - this.onChange(null); - return; - } - hour = this.displayTime ? hour : this.dateFieldOrDefault?.hours; - minute = this.displayTime ? minute : this.dateFieldOrDefault?.minutes; - const second = this.dateFieldOrDefault?.seconds; - this.onChange(this.processDate(year, month, day, hour, minute, second)); - }, - }); - } - - processDate( - year: number | string | undefined, - month: number | string | undefined, - day: number | string | undefined, - hour: number | string | undefined, - minute: number | string | undefined, - second: number | string | undefined, - ) { - if (this.isoDate) { - return `${year || ''}-${this.padded(month)}-${this.padded(day)}T${this.padded(hour)}:${this.padded(minute)}:${this.padded(second)}.000`; - } - return `${year || ''}-${this.padded(month)}-${this.padded(day)}`; - } - - padded(n: number | string | undefined, l = 2) { - return n != null && !Number.isNaN(+n) ? String(n).padStart(l, '0') || '' : ''; - } - - /** - * Note: This function is not testable because `validDate` returns a reference that can't be compared to in spec file with `hasValidator` function. - */ - addValidators() { - this.control?.addValidators([DateValidators.validDate(this.displayTime, this.label)]); - this.control?.meta.validators?.push({ name: ValidatorNames.Custom, args: DateValidators.validDate(this.displayTime, this.label) }); - } - - validate() { - this.errors = validateDate(this.day || '', this.month || '', this.year || '', this.label); - } - - elementHasErrors(i: number) { - return this.day || this.month || this.year ? this.errors?.errors?.some((e) => e.index === i) : false; - } - - getId(name: string) { - const id = `${name}-day`; - if (this.control) { - this.control.meta.customId = id; - } - return id; - } + @Input() displayTime = false; + @Input() isoDate = true; + @Input() customError? = false; + @ViewChild('dayModel') dayModel?: AbstractControlDirective; + @Output() blur = new EventEmitter(); + + private day_: BehaviorSubject = new BehaviorSubject(undefined); + private month_: BehaviorSubject = new BehaviorSubject(undefined); + private year_: BehaviorSubject = new BehaviorSubject(undefined); + private hour_: BehaviorSubject = new BehaviorSubject(undefined); + private minute_: BehaviorSubject = new BehaviorSubject(undefined); + private day$: Observable; + private month$: Observable; + private year$: Observable; + private hour$: Observable; + private minute$: Observable; + private subscriptions: Array = []; + public originalDate = ''; + public errors?: { error: boolean; date?: Date; errors?: { error: boolean; reason: string; index: number }[] }; + private dateFieldOrDefault?: Record<'hours' | 'minutes' | 'seconds', string | number>; + protected formSubmitted? = false; + + public day?: number; + public month?: number; + public year?: number; + public hour?: number; + public minute?: number; + + dayId = ''; + monthId = ''; + yearId = ''; + + constructor( + injector: Injector, + changeDetectorRef: ChangeDetectorRef, + public globalErrorService: GlobalErrorService + ) { + super(injector, changeDetectorRef); + this.day$ = this.day_.asObservable(); + this.month$ = this.month_.asObservable(); + this.year$ = this.year_.asObservable(); + this.hour$ = this.hour_.asObservable(); + this.minute$ = this.minute_.asObservable(); + this.globalErrorService.errors$.subscribe((globalErrors) => { + if (globalErrors.length) { + this.formSubmitted = true; + } + }); + } + + ngOnInit(): void { + this.subscriptions.push(this.subscribeAndPropagateChanges()); + this.dayId = `${this.customId ?? this.name}-day`; + this.monthId = `${this.customId ?? this.name}-month`; + this.yearId = `${this.customId ?? this.name}-year`; + } + + override ngAfterContentInit(): void { + super.ngAfterContentInit(); + this.originalDate = this.value; + this.dateFieldOrDefault = { + hours: this.originalDate ? new Date(this.originalDate).getHours() : '00', + minutes: this.originalDate ? new Date(this.originalDate).getMinutes() : '00', + seconds: this.originalDate ? new Date(this.originalDate).getSeconds() : '00', + }; + this.addValidators(); + this.valueWriteBack(this.value); + } + + ngOnDestroy(): void { + this.subscriptions.forEach((s) => s && s.unsubscribe()); + } + + onDayChange(event: number | undefined) { + this.day_.next(event); + } + + onMonthChange(event: number | undefined) { + this.month_.next(event); + } + + onYearChange(event: number | undefined) { + this.year_.next(event); + } + + onHourChange(event: number | undefined) { + this.hour_.next(event); + } + + onMinuteChange(event: number | undefined) { + this.minute_.next(event); + } + + valueWriteBack(value: string | null): void { + if (value && typeof value === 'string') { + const date = new Date(value); + this.day = date.getDate(); + this.day_.next(this.day); + this.month = date.getMonth() + 1; + this.month_.next(this.month); + this.year = date.getFullYear(); + this.year_.next(this.year); + this.hour = date.getHours(); + this.hour_.next(this.hour); + this.minute = date.getMinutes(); + this.minute_.next(this.minute); + } + } + + /** + * Subscribes to all date segments and propagates value as `Date`. + * @returns Subscription + */ + subscribeAndPropagateChanges() { + const dateFields: Segments = this.displayTime + ? { + day: this.day$, + month: this.month$, + year: this.year$, + hour: this.hour$, + minute: this.minute$, + } + : { day: this.day$, month: this.month$, year: this.year$ }; + return combineLatest(dateFields).subscribe({ + next: ({ day, month, year, hour, minute }) => { + if (!day && !month && !year && !hour && !minute) { + this.onChange(null); + return; + } + hour = this.displayTime ? hour : this.dateFieldOrDefault?.hours; + minute = this.displayTime ? minute : this.dateFieldOrDefault?.minutes; + const second = this.dateFieldOrDefault?.seconds; + this.onChange(this.processDate(year, month, day, hour, minute, second)); + }, + }); + } + + processDate( + year: number | string | undefined, + month: number | string | undefined, + day: number | string | undefined, + hour: number | string | undefined, + minute: number | string | undefined, + second: number | string | undefined + ) { + if (this.isoDate) { + return `${year || ''}-${this.padded(month)}-${this.padded(day)}T${this.padded(hour)}:${this.padded(minute)}:${this.padded(second)}.000`; + } + return `${year || ''}-${this.padded(month)}-${this.padded(day)}`; + } + + padded(n: number | string | undefined, l = 2) { + return n != null && !Number.isNaN(+n) ? String(n).padStart(l, '0') || '' : ''; + } + + /** + * Note: This function is not testable because `validDate` returns a reference that can't be compared to in spec file with `hasValidator` function. + */ + addValidators() { + this.control?.addValidators([DateValidators.validDate(this.displayTime, this.label)]); + this.control?.meta.validators?.push({ + name: ValidatorNames.Custom, + args: DateValidators.validDate(this.displayTime, this.label), + }); + } + + validate() { + this.errors = validateDate(this.day || '', this.month || '', this.year || '', this.label); + } + + elementHasErrors(i: number) { + return this.day || this.month || this.year ? this.errors?.errors?.some((e) => e.index === i) : false; + } + + getId(name: string) { + const id = `${name}-day`; + if (this.control) { + this.control.meta.customId = id; + } + return id; + } } diff --git a/src/app/forms/components/date/focus-next.directive.spec.ts b/src/app/forms/components/date/focus-next.directive.spec.ts index 3d965c28ab..f66abf041a 100644 --- a/src/app/forms/components/date/focus-next.directive.spec.ts +++ b/src/app/forms/components/date/focus-next.directive.spec.ts @@ -4,8 +4,8 @@ import { By } from '@angular/platform-browser'; import { FocusNextDirective } from './focus-next.directive'; @Component({ - selector: 'app-test', - template: ` + selector: 'app-test', + template: `
@@ -16,76 +16,76 @@ import { FocusNextDirective } from './focus-next.directive'; `, }) class TestComponent { - displayTime = false; + displayTime = false; } describe('FocusNextDirective', () => { - let component: TestComponent; - let fixture: ComponentFixture; - let de: DebugElement; + let component: TestComponent; + let fixture: ComponentFixture; + let de: DebugElement; - beforeEach(async () => { - await TestBed.configureTestingModule({ - declarations: [TestComponent, FocusNextDirective], - }).compileComponents(); - }); + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [TestComponent, FocusNextDirective], + }).compileComponents(); + }); - beforeEach(() => { - fixture = TestBed.createComponent(TestComponent); - component = fixture.componentInstance; - de = fixture.debugElement; - }); + beforeEach(() => { + fixture = TestBed.createComponent(TestComponent); + component = fixture.componentInstance; + de = fixture.debugElement; + }); - it('should create an instance', () => { - expect(component).toBeDefined(); - }); + it('should create an instance', () => { + expect(component).toBeDefined(); + }); - it('should tab from day to month after two numbers', () => { - const day: HTMLInputElement = fixture.debugElement.query(By.css('#test-day')).nativeElement; - const month: HTMLInputElement = fixture.debugElement.query(By.css('#test-month')).nativeElement; - day.value = '01'; - day.dispatchEvent(new Event('input')); - fixture.detectChanges(); - const focusedElement = de.query(By.css(':focus')).nativeElement; - expect(month).toBe(focusedElement); - }); + it('should tab from day to month after two numbers', () => { + const day: HTMLInputElement = fixture.debugElement.query(By.css('#test-day')).nativeElement; + const month: HTMLInputElement = fixture.debugElement.query(By.css('#test-month')).nativeElement; + day.value = '01'; + day.dispatchEvent(new Event('input')); + fixture.detectChanges(); + const focusedElement = de.query(By.css(':focus')).nativeElement; + expect(month).toBe(focusedElement); + }); - it('should tab from month to year after two numbers', () => { - const month: HTMLInputElement = fixture.debugElement.query(By.css('#test-month')).nativeElement; - const year: HTMLInputElement = fixture.debugElement.query(By.css('#test-year')).nativeElement; - month.value = '01'; - month.dispatchEvent(new Event('input')); - fixture.detectChanges(); - const focusedElement = de.query(By.css(':focus')).nativeElement; - expect(year).toBe(focusedElement); - }); + it('should tab from month to year after two numbers', () => { + const month: HTMLInputElement = fixture.debugElement.query(By.css('#test-month')).nativeElement; + const year: HTMLInputElement = fixture.debugElement.query(By.css('#test-year')).nativeElement; + month.value = '01'; + month.dispatchEvent(new Event('input')); + fixture.detectChanges(); + const focusedElement = de.query(By.css(':focus')).nativeElement; + expect(year).toBe(focusedElement); + }); - it('should tab from hour to minute after two numbers', () => { - const hour: HTMLInputElement = fixture.debugElement.query(By.css('#test-hour')).nativeElement; - const minute: HTMLInputElement = fixture.debugElement.query(By.css('#test-minute')).nativeElement; - hour.value = '01'; - hour.dispatchEvent(new Event('input')); - fixture.detectChanges(); - const focusedElement = de.query(By.css(':focus')).nativeElement; - expect(minute).toBe(focusedElement); - }); + it('should tab from hour to minute after two numbers', () => { + const hour: HTMLInputElement = fixture.debugElement.query(By.css('#test-hour')).nativeElement; + const minute: HTMLInputElement = fixture.debugElement.query(By.css('#test-minute')).nativeElement; + hour.value = '01'; + hour.dispatchEvent(new Event('input')); + fixture.detectChanges(); + const focusedElement = de.query(By.css(':focus')).nativeElement; + expect(minute).toBe(focusedElement); + }); - it('should tab from year to hour after four numbers if displayTime is true', () => { - const year: HTMLInputElement = fixture.debugElement.query(By.css('#test-year')).nativeElement; - const hour: HTMLInputElement = fixture.debugElement.query(By.css('#test-hour')).nativeElement; + it('should tab from year to hour after four numbers if displayTime is true', () => { + const year: HTMLInputElement = fixture.debugElement.query(By.css('#test-year')).nativeElement; + const hour: HTMLInputElement = fixture.debugElement.query(By.css('#test-hour')).nativeElement; - year.focus(); - year.value = '2000'; - year.dispatchEvent(new Event('input')); - fixture.detectChanges(); - let focusedElement = de.query(By.css(':focus')).nativeElement; - expect(year).toBe(focusedElement); + year.focus(); + year.value = '2000'; + year.dispatchEvent(new Event('input')); + fixture.detectChanges(); + let focusedElement = de.query(By.css(':focus')).nativeElement; + expect(year).toBe(focusedElement); - component.displayTime = true; - fixture.detectChanges(); - year.dispatchEvent(new Event('input')); - fixture.detectChanges(); - focusedElement = de.query(By.css(':focus')).nativeElement; - expect(hour).toBe(focusedElement); - }); + component.displayTime = true; + fixture.detectChanges(); + year.dispatchEvent(new Event('input')); + fixture.detectChanges(); + focusedElement = de.query(By.css(':focus')).nativeElement; + expect(hour).toBe(focusedElement); + }); }); diff --git a/src/app/forms/components/date/focus-next.directive.ts b/src/app/forms/components/date/focus-next.directive.ts index 81c50e94ad..c299e84651 100644 --- a/src/app/forms/components/date/focus-next.directive.ts +++ b/src/app/forms/components/date/focus-next.directive.ts @@ -1,48 +1,46 @@ -import { - Directive, ElementRef, HostListener, Input, -} from '@angular/core'; +import { Directive, ElementRef, HostListener, Input } from '@angular/core'; @Directive({ - selector: '[appFocusNext]', + selector: '[appFocusNext]', }) export class FocusNextDirective { - @Input() displayTime = false; + @Input() displayTime = false; - constructor(private el: ElementRef) {} + constructor(private el: ElementRef) {} - @HostListener('input', ['$event']) - onInput() { - const { - nativeElement: { id, value }, - } = this.el; - const segments = id.split('-'); - const next = this.getNextElement(segments.splice(-1)[0], value); + @HostListener('input', ['$event']) + onInput() { + const { + nativeElement: { id, value }, + } = this.el; + const segments = id.split('-'); + const next = this.getNextElement(segments.splice(-1)[0], value); - if (next) { - document.getElementById(`${segments.join('-')}${next}`)?.focus(); - } - } + if (next) { + document.getElementById(`${segments.join('-')}${next}`)?.focus(); + } + } - private getNextElement(currentSegment: string, value: string): string | undefined { - let nextEl; + private getNextElement(currentSegment: string, value: string): string | undefined { + let nextEl; - if (value.length === 2) { - switch (currentSegment) { - case 'day': - nextEl = '-month'; - break; - case 'month': - nextEl = '-year'; - break; - case 'hour': - nextEl = '-minute'; - break; - default: - } - } else if (value.length === 4 && this.displayTime && currentSegment === 'year') { - nextEl = '-hour'; - } + if (value.length === 2) { + switch (currentSegment) { + case 'day': + nextEl = '-month'; + break; + case 'month': + nextEl = '-year'; + break; + case 'hour': + nextEl = '-minute'; + break; + default: + } + } else if (value.length === 4 && this.displayTime && currentSegment === 'year') { + nextEl = '-hour'; + } - return nextEl; - } + return nextEl; + } } diff --git a/src/app/forms/components/defect-select/defect-select.component.spec.ts b/src/app/forms/components/defect-select/defect-select.component.spec.ts index 47c37812af..abd44145d2 100644 --- a/src/app/forms/components/defect-select/defect-select.component.spec.ts +++ b/src/app/forms/components/defect-select/defect-select.component.spec.ts @@ -10,84 +10,84 @@ import { initialAppState } from '@store/.'; import { DefectSelectComponent } from './defect-select.component'; describe('DefectSelectComponent', () => { - let component: DefectSelectComponent; - let fixture: ComponentFixture; + let component: DefectSelectComponent; + let fixture: ComponentFixture; - const defect: Defect = { - additionalInfo: {}, - forVehicleType: [VehicleTypes.PSV], - imDescription: 'some description', - imNumber: 1, - items: [ - { - deficiencies: [ - { - deficiencyCategory: deficiencyCategory.Advisory, - deficiencyId: 'some id', - deficiencySubId: 'some sub id', - deficiencyText: 'hey yo', - forVehicleType: [VehicleTypes.PSV], - ref: 'some ref', - stdForProhibition: false, - }, - ], - forVehicleType: [VehicleTypes.PSV], - itemDescription: 'yolo', - itemNumber: 2, - }, - ], - }; + const defect: Defect = { + additionalInfo: {}, + forVehicleType: [VehicleTypes.PSV], + imDescription: 'some description', + imNumber: 1, + items: [ + { + deficiencies: [ + { + deficiencyCategory: deficiencyCategory.Advisory, + deficiencyId: 'some id', + deficiencySubId: 'some sub id', + deficiencyText: 'hey yo', + forVehicleType: [VehicleTypes.PSV], + ref: 'some ref', + stdForProhibition: false, + }, + ], + forVehicleType: [VehicleTypes.PSV], + itemDescription: 'yolo', + itemNumber: 2, + }, + ], + }; - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [RouterTestingModule], - declarations: [DefectSelectComponent], - providers: [provideMockStore({ initialState: initialAppState })], - }).compileComponents(); - }); + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [RouterTestingModule], + declarations: [DefectSelectComponent], + providers: [provideMockStore({ initialState: initialAppState })], + }).compileComponents(); + }); - beforeEach(() => { - fixture = TestBed.createComponent(DefectSelectComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); + beforeEach(() => { + fixture = TestBed.createComponent(DefectSelectComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); - it('should return all types', () => { - enum Types { - 'Defect', - 'Item', - 'Deficiency', - } - expect(component.types).toStrictEqual(Types); - }); + it('should return all types', () => { + enum Types { + Defect = 0, + Item = 1, + Deficiency = 2, + } + expect(component.types).toStrictEqual(Types); + }); - describe('hasItems', () => { - it('should correctly detect a defect with items', () => { - expect(component.hasItems(defect)).toBeTruthy(); - }); + describe('hasItems', () => { + it('should correctly detect a defect with items', () => { + expect(component.hasItems(defect)).toBeTruthy(); + }); - it('should correctly detect a defect without items', () => { - const defectWithNoItems: Defect = { ...defect, items: [] }; - expect(component.hasItems(defectWithNoItems)).toBeFalsy(); - }); - }); + it('should correctly detect a defect without items', () => { + const defectWithNoItems: Defect = { ...defect, items: [] }; + expect(component.hasItems(defectWithNoItems)).toBeFalsy(); + }); + }); - describe('hasDeficiencies', () => { - it('should correctly detect an item with deficiencies', () => { - expect(component.hasDeficiencies(defect.items[0])).toBeTruthy(); - }); + describe('hasDeficiencies', () => { + it('should correctly detect an item with deficiencies', () => { + expect(component.hasDeficiencies(defect.items[0])).toBeTruthy(); + }); - it('should correctly detect an item without deficiencies', () => { - const itemWithNoDeficiencies: Item = { - ...defect.items[0], - deficiencies: [], - }; + it('should correctly detect an item without deficiencies', () => { + const itemWithNoDeficiencies: Item = { + ...defect.items[0], + deficiencies: [], + }; - expect(component.hasDeficiencies(itemWithNoDeficiencies)).toBeFalsy(); - }); - }); + expect(component.hasDeficiencies(itemWithNoDeficiencies)).toBeFalsy(); + }); + }); }); diff --git a/src/app/forms/components/defect-select/defect-select.component.ts b/src/app/forms/components/defect-select/defect-select.component.ts index 569b84c93c..59b3c737ff 100644 --- a/src/app/forms/components/defect-select/defect-select.component.ts +++ b/src/app/forms/components/defect-select/defect-select.component.ts @@ -13,103 +13,106 @@ import { TestResultsState } from '@store/test-records/reducers/test-records.redu import { Subject, filter, takeUntil } from 'rxjs'; @Component({ - selector: 'app-defect-select', - templateUrl: './defect-select.component.html', - styleUrls: ['./defect-select.component.scss'], + selector: 'app-defect-select', + templateUrl: './defect-select.component.html', + styleUrls: ['./defect-select.component.scss'], }) export class DefectSelectComponent implements OnInit, OnDestroy { - defects: Defect[] = []; - isEditing = false; - selectedDefect?: Defect; - selectedItem?: Item; - selectedDeficiency?: Deficiency; - vehicleType!: VehicleTypes; + defects: Defect[] = []; + isEditing = false; + selectedDefect?: Defect; + selectedItem?: Item; + selectedDeficiency?: Deficiency; + vehicleType!: VehicleTypes; - onDestroy$ = new Subject(); + onDestroy$ = new Subject(); - constructor( - private testResultsStore: Store, - private defectsStore: Store, - private router: Router, - private route: ActivatedRoute, - ) {} + constructor( + private testResultsStore: Store, + private defectsStore: Store, + private router: Router, + private route: ActivatedRoute + ) {} - ngOnInit(): void { - this.testResultsStore - .select(toEditOrNotToEdit) - .pipe( - takeUntil(this.onDestroy$), - filter((testResult) => !!testResult), - ) - .subscribe((testResult) => { - if (testResult) { - this.vehicleType = testResult.vehicleType; - } - }); + ngOnInit(): void { + this.testResultsStore + .select(toEditOrNotToEdit) + .pipe( + takeUntil(this.onDestroy$), + filter((testResult) => !!testResult) + ) + .subscribe((testResult) => { + if (testResult) { + this.vehicleType = testResult.vehicleType; + } + }); - this.defectsStore.select(filteredDefects(this.vehicleType)).subscribe((defectsTaxonomy) => { - this.defects = defectsTaxonomy; - }); - } + this.defectsStore.select(filteredDefects(this.vehicleType)).subscribe((defectsTaxonomy) => { + this.defects = defectsTaxonomy; + }); + } - ngOnDestroy(): void { - this.onDestroy$.next(true); - this.onDestroy$.complete(); - } + ngOnDestroy(): void { + this.onDestroy$.next(true); + this.onDestroy$.complete(); + } - get types(): typeof Types { - return Types; - } + get types(): typeof Types { + return Types; + } - hasItems(defect: Defect): boolean { - return defect.items && defect.items.length > 0; - } + hasItems(defect: Defect): boolean { + return defect.items && defect.items.length > 0; + } - hasDeficiencies(item: Item): boolean { - return item.deficiencies && item.deficiencies.length > 0; - } + hasDeficiencies(item: Item): boolean { + return item.deficiencies && item.deficiencies.length > 0; + } - categoryColor(category: string): 'red' | 'orange' | 'yellow' | 'green' | 'blue' { - return (>{ - major: 'orange', - minor: 'yellow', - dangerous: 'red', - advisory: 'blue', - })[`${category}`]; - } + categoryColor(category: string): 'red' | 'orange' | 'yellow' | 'green' | 'blue' { + return (>{ + major: 'orange', + minor: 'yellow', + dangerous: 'red', + advisory: 'blue', + })[`${category}`]; + } - handleSelect(selected?: Defect | Item | Deficiency, type?: Types): void { - switch (type) { - case Types.Defect: - this.selectedDefect = selected as Defect; - this.selectedItem = undefined; - this.selectedDeficiency = undefined; - break; - case Types.Item: - this.selectedItem = selected as Item; - this.selectedDeficiency = undefined; - break; - case Types.Deficiency: - this.selectedDeficiency = selected as Deficiency; - void this.router.navigate([this.selectedDeficiency.ref], { relativeTo: this.route, queryParamsHandling: 'merge' }); - break; - default: - let advisoryRoute = `${this.selectedDefect?.imNumber}.${this.selectedItem?.itemNumber}.advisory`; - if (this.selectedDefect?.imNumber === 71 && this.selectedItem?.itemNumber === 1) { - advisoryRoute += this.selectedItem.itemDescription === 'All Roller Brake Test Machines:' ? '.0' : '.1'; - } + handleSelect(selected?: Defect | Item | Deficiency, type?: Types): void { + switch (type) { + case Types.Defect: + this.selectedDefect = selected as Defect; + this.selectedItem = undefined; + this.selectedDeficiency = undefined; + break; + case Types.Item: + this.selectedItem = selected as Item; + this.selectedDeficiency = undefined; + break; + case Types.Deficiency: + this.selectedDeficiency = selected as Deficiency; + void this.router.navigate([this.selectedDeficiency.ref], { + relativeTo: this.route, + queryParamsHandling: 'merge', + }); + break; + default: + let advisoryRoute = `${this.selectedDefect?.imNumber}.${this.selectedItem?.itemNumber}.advisory`; + if (this.selectedDefect?.imNumber === 71 && this.selectedItem?.itemNumber === 1) { + advisoryRoute += this.selectedItem.itemDescription === 'All Roller Brake Test Machines:' ? '.0' : '.1'; + } - void this.router.navigate([advisoryRoute], { - relativeTo: this.route, - queryParamsHandling: 'merge', - }); - break; - } - } + void this.router.navigate([advisoryRoute], { + relativeTo: this.route, + queryParamsHandling: 'merge', + }); + break; + } + } } enum Types { - Defect, - Item, - Deficiency, + Defect = 0, + Item = 1, + Deficiency = 2, } diff --git a/src/app/forms/components/dynamic-form-field/dynamic-form-field.component.spec.ts b/src/app/forms/components/dynamic-form-field/dynamic-form-field.component.spec.ts index 171a06b59b..6d2aea4656 100644 --- a/src/app/forms/components/dynamic-form-field/dynamic-form-field.component.spec.ts +++ b/src/app/forms/components/dynamic-form-field/dynamic-form-field.component.spec.ts @@ -12,74 +12,78 @@ import { of } from 'rxjs'; import { DynamicFormFieldComponent } from './dynamic-form-field.component'; describe('DynamicFormFieldComponent', () => { - let component: DynamicFormFieldComponent; - let fixture: ComponentFixture; - let service: ReferenceDataService; + let component: DynamicFormFieldComponent; + let fixture: ComponentFixture; + let service: ReferenceDataService; - beforeEach(async () => { - await TestBed.configureTestingModule({ - declarations: [DynamicFormFieldComponent], - imports: [FormsModule, HttpClientTestingModule, ReactiveFormsModule], - providers: [ - provideMockStore({ initialState: initialAppState }), - ReferenceDataService, - TestStationsService, - { provide: UserService, useValue: {} }, - ], - }).compileComponents(); - service = TestBed.inject(ReferenceDataService); - }); + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [DynamicFormFieldComponent], + imports: [FormsModule, HttpClientTestingModule, ReactiveFormsModule], + providers: [ + provideMockStore({ initialState: initialAppState }), + ReferenceDataService, + TestStationsService, + { provide: UserService, useValue: {} }, + ], + }).compileComponents(); + service = TestBed.inject(ReferenceDataService); + }); - beforeEach(() => { - fixture = TestBed.createComponent(DynamicFormFieldComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - component.control = { - key: 'birthday', - value: new CustomFormControl({ - name: 'test', - type: FormNodeTypes.CONTROL, - referenceData: ReferenceDataResourceType.CountryOfRegistration, - }), - }; - }); + beforeEach(() => { + fixture = TestBed.createComponent(DynamicFormFieldComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + component.control = { + key: 'birthday', + value: new CustomFormControl({ + name: 'test', + type: FormNodeTypes.CONTROL, + referenceData: ReferenceDataResourceType.CountryOfRegistration, + }), + }; + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); - it('should get options', () => { - const options = component.options$; - expect(options).toBeTruthy(); - }); + it('should get options', () => { + const options = component.options$; + expect(options).toBeTruthy(); + }); - it('should return the metadata options', (done) => { - component.control = { - key: 'birthday', - value: new CustomFormControl({ name: 'test', type: FormNodeTypes.CONTROL, options: [{ value: '1', label: 'test' }] }), - }; - component.form = new FormGroup({}); - component.options$.subscribe((value) => { - expect(value).toBeTruthy(); - expect(value).toEqual([{ value: '1', label: 'test' }]); - done(); - }); - }); + it('should return the metadata options', (done) => { + component.control = { + key: 'birthday', + value: new CustomFormControl({ + name: 'test', + type: FormNodeTypes.CONTROL, + options: [{ value: '1', label: 'test' }], + }), + }; + component.form = new FormGroup({}); + component.options$.subscribe((value) => { + expect(value).toBeTruthy(); + expect(value).toEqual([{ value: '1', label: 'test' }]); + done(); + }); + }); - it('should return the reference data options', (done) => { - service.getAll$ = jest.fn().mockReturnValue(of([{ resourceKey: '1', description: 'test' }])); - component.form = new FormGroup({}); - component.options$.subscribe((value) => { - expect(value).toBeTruthy(); - expect(value).toEqual([{ value: '1', label: 'test' }]); - done(); - }); - }); + it('should return the reference data options', (done) => { + service.getAll$ = jest.fn().mockReturnValue(of([{ resourceKey: '1', description: 'test' }])); + component.form = new FormGroup({}); + component.options$.subscribe((value) => { + expect(value).toBeTruthy(); + expect(value).toEqual([{ value: '1', label: 'test' }]); + done(); + }); + }); - it('should fetch the reference data on init', () => { - service.loadReferenceData = jest.fn(); - component.ngAfterContentInit(); - const spy = jest.spyOn(service, 'loadReferenceData'); - expect(spy).toHaveBeenCalled(); - }); + it('should fetch the reference data on init', () => { + service.loadReferenceData = jest.fn(); + component.ngAfterContentInit(); + const spy = jest.spyOn(service, 'loadReferenceData'); + expect(spy).toHaveBeenCalled(); + }); }); diff --git a/src/app/forms/components/dynamic-form-field/dynamic-form-field.component.ts b/src/app/forms/components/dynamic-form-field/dynamic-form-field.component.ts index 041898395b..485609cac6 100644 --- a/src/app/forms/components/dynamic-form-field/dynamic-form-field.component.ts +++ b/src/app/forms/components/dynamic-form-field/dynamic-form-field.component.ts @@ -1,7 +1,5 @@ import { KeyValue } from '@angular/common'; -import { - AfterContentInit, Component, InjectionToken, Injector, Input, OnInit, -} from '@angular/core'; +import { AfterContentInit, Component, InjectionToken, Injector, Input, OnInit } from '@angular/core'; import { FormGroup, NgControl } from '@angular/forms'; // eslint-disable-next-line import/no-cycle import { CustomFormControl, FormNodeEditTypes, FormNodeOption } from '@forms/services/dynamic-form.types'; @@ -9,51 +7,54 @@ import { MultiOptionsService } from '@forms/services/multi-options.service'; import { Observable, map, of } from 'rxjs'; @Component({ - selector: 'app-dynamic-form-field', - templateUrl: './dynamic-form-field.component.html', - providers: [MultiOptionsService], + selector: 'app-dynamic-form-field', + templateUrl: './dynamic-form-field.component.html', + providers: [MultiOptionsService], }) export class DynamicFormFieldComponent implements OnInit, AfterContentInit { - @Input() control?: KeyValue; - @Input() form?: FormGroup; - @Input() customId?: string; - - customFormControlInjector?: Injector; - - constructor(private optionsService: MultiOptionsService, private injector: Injector) {} - - get formNodeEditTypes(): typeof FormNodeEditTypes { - return FormNodeEditTypes; - } - - get options$(): Observable[]> { - const meta = this.control?.value.meta; - - return meta?.referenceData - ? this.optionsService.getOptions(meta.referenceData).pipe(map((l) => (l || []))) - : of((meta?.options as FormNodeOption[]) ?? []); - } - - ngOnInit(): void { - this.createCustomFormControlInjector(); - } - - ngAfterContentInit(): void { - const referenceData = this.control?.value.meta?.referenceData; - - if (referenceData) { - this.optionsService.loadOptions(referenceData); - } - } - - createCustomFormControlInjector() { - this.customFormControlInjector = Injector.create({ - providers: [ - { provide: FORM_INJECTION_TOKEN, useValue: this.form }, - { provide: NgControl, useValue: { control: this.control } }, - ], - parent: this.injector, - }); - } + @Input() control?: KeyValue; + @Input() form?: FormGroup; + @Input() customId?: string; + + customFormControlInjector?: Injector; + + constructor( + private optionsService: MultiOptionsService, + private injector: Injector + ) {} + + get formNodeEditTypes(): typeof FormNodeEditTypes { + return FormNodeEditTypes; + } + + get options$(): Observable[]> { + const meta = this.control?.value.meta; + + return meta?.referenceData + ? this.optionsService.getOptions(meta.referenceData).pipe(map((l) => l || [])) + : of((meta?.options as FormNodeOption[]) ?? []); + } + + ngOnInit(): void { + this.createCustomFormControlInjector(); + } + + ngAfterContentInit(): void { + const referenceData = this.control?.value.meta?.referenceData; + + if (referenceData) { + this.optionsService.loadOptions(referenceData); + } + } + + createCustomFormControlInjector() { + this.customFormControlInjector = Injector.create({ + providers: [ + { provide: FORM_INJECTION_TOKEN, useValue: this.form }, + { provide: NgControl, useValue: { control: this.control } }, + ], + parent: this.injector, + }); + } } export const FORM_INJECTION_TOKEN = new InjectionToken('form'); diff --git a/src/app/forms/components/dynamic-form-group/dynamic-form-group.component.spec.ts b/src/app/forms/components/dynamic-form-group/dynamic-form-group.component.spec.ts index 93fa604f86..a13d9dc031 100644 --- a/src/app/forms/components/dynamic-form-group/dynamic-form-group.component.spec.ts +++ b/src/app/forms/components/dynamic-form-group/dynamic-form-group.component.spec.ts @@ -1,7 +1,5 @@ import { HttpClientTestingModule } from '@angular/common/http/testing'; -import { - ComponentFixture, fakeAsync, inject, TestBed, tick, -} from '@angular/core/testing'; +import { ComponentFixture, TestBed, fakeAsync, inject, tick } from '@angular/core/testing'; import { FormGroup } from '@angular/forms'; import { By } from '@angular/platform-browser'; import { RouterTestingModule } from '@angular/router/testing'; @@ -16,448 +14,511 @@ import { FormNode, FormNodeTypes, FormNodeViewTypes } from '../../services/dynam import { DynamicFormGroupComponent } from './dynamic-form-group.component'; describe('DynamicFormGroupComponent', () => { - let component: DynamicFormGroupComponent; - let fixture: ComponentFixture; - - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [DynamicFormsModule, HttpClientTestingModule, RouterTestingModule], - providers: [ - provideMockStore({ initialState: initialAppState }), - ReferenceDataService, - TestStationsService, - { provide: UserService, useValue: {} }, - ], - }).compileComponents(); - }); - - beforeEach(() => { - fixture = TestBed.createComponent(DynamicFormGroupComponent); - component = fixture.componentInstance; - // Don't detect changes on the first load as it will prevent change detection - // from working in the test function due to change detection being OnPush - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); - - describe('DynamicFormGroupComponent.prototype.trackByFn.name', () => { - it.each([ - [3, [3, 'some value']], - ['foo', [3, { key: 'foo' }]], - ])('should return %s when given %o', (expected, props) => { - const [index, item] = props; - expect(component.trackByFn(index as number, item)).toBe(expected); - }); - }); - - it.each([ - [ - { a: 'b', c: 1 }, - [ - { key: 'a', value: 'b' }, - { key: 'c', value: 1 }, - ], - ], - [ - { - name: 'test-name', - label: 'test-label', - type: 'test-type', - children: [ - { - name: 'test-c-name', label: 'test-c-label', value: 'test-c-value', children: [], type: 'test-c-control', viewType: 'test-c-viewType', - }, - ], - }, - [ - { key: 'name', value: 'test-name' }, - { key: 'label', value: 'test-label' }, - { key: 'type', value: 'test-type' }, - { - key: 'children', - value: [ - { - name: 'test-c-name', label: 'test-c-label', value: 'test-c-value', children: [], type: 'test-c-control', viewType: 'test-c-viewType', - }, - ], - }, - ], - ], - ])('entriesOf: should split the keys out into values', (input, expected) => { - expect(component.entriesOf(input as unknown as FormGroup)).toStrictEqual(expected); - }); - - describe('formNodeTypes', () => { - it('should return FormNodeTypes enum', () => { - Object.entries(FormNodeTypes).forEach((entry) => { - expect(FormNodeTypes).toEqual(component.formNodeTypes); - expect(component.formNodeTypes[entry[0] as keyof typeof FormNodeTypes]).toBe(entry[1]); - }); - }); - }); - - describe('formNodeViewTypes', () => { - it('should return FormNodeViewTypes enum', () => { - Object.entries(FormNodeViewTypes).forEach((entry) => { - expect(FormNodeViewTypes).toEqual(component.formNodeViewTypes); - expect(component.formNodeViewTypes[entry[0] as keyof typeof FormNodeViewTypes]).toBe(entry[1]); - }); - }); - }); - - describe('template', () => { - const template = { - name: 'myForm', - type: FormNodeTypes.GROUP, - children: [ - { - name: 'levelOneControl', type: FormNodeTypes.CONTROL, label: 'Level one control', value: 'some string', - }, - { - name: 'levelOneGroup', - type: FormNodeTypes.GROUP, - children: [ - { - name: 'levelTwoControl', type: FormNodeTypes.CONTROL, label: 'Level two control', value: 'some string', - }, - { - name: 'levelTwoArray', - type: FormNodeTypes.ARRAY, - children: [ - { name: 'levelTwoArrayControlOne', type: FormNodeTypes.CONTROL, value: '1' }, - { name: 'levelTwoArrayControlTwo', type: FormNodeTypes.CONTROL, value: '2' }, - ], - }, - ], - }, - ], - }; - - const data = { - levelOneControl: 'some string', - levelOneGroup: { - levelTwoControl: 'some string', - levelTwoArray: [ - { - levelTwoArrayControlOne: 'some string', - levelTwoArrayControlTwo: 'some string', - }, - ], - }, - }; - - it('should generate the correct number of detail summary elements', inject([DynamicFormService], (dfs: DynamicFormService) => { - component.form = dfs.createForm(template, data); - - fixture.detectChanges(); - - const trList = fixture.debugElement.queryAll(By.css('tr')); - const tdList = fixture.debugElement.queryAll(By.css('td')); - - expect(trList).toHaveLength(4); - expect(tdList).toHaveLength(8); - })); - - it('should generate the correct number of input elements', inject([DynamicFormService], (dfs: DynamicFormService) => { - component.edit = true; - component.form = dfs.createForm(template, data); - - fixture.detectChanges(); - - const inputList = fixture.debugElement.queryAll(By.css('input')); - - expect(inputList).toHaveLength(4); - })); - }); - - describe('template for nested array with groups', () => { - const template = { - name: 'myForm', - type: FormNodeTypes.GROUP, - children: [ - { - name: 'levelOneControl', type: FormNodeTypes.CONTROL, label: 'Level one control', value: 'some string', - }, - { - name: 'levelOneGroup', - type: FormNodeTypes.GROUP, - children: [ - { - name: 'levelTwoControl', type: FormNodeTypes.CONTROL, label: 'Level two control', value: 'some string', - }, - { - name: 'levelTwoArray', - type: FormNodeTypes.ARRAY, - children: [ - { - name: 'levelThreeArray', - type: FormNodeTypes.ARRAY, - children: [ - { - name: 'levelThreeGroup', - type: FormNodeTypes.GROUP, - children: [ - { name: 'levelThreeArrayControlOne', type: FormNodeTypes.CONTROL, value: '1' }, - { name: 'levelThreeArrayControlTwo', type: FormNodeTypes.CONTROL, value: '2' }, - ], - }, - ], - }, - ], - }, - ], - }, - ], - }; - - const data = { - levelOneControl: 'some string', - levelOneGroup: { - levelTwoControl: 'some string', - levelTwoArray: [ - [ - { levelThreeArrayControlOne: 'some string', levelThreeArrayControlTwo: 'some string' }, - { levelThreeArrayControlOne: 'some string', levelThreeArrayControlTwo: 'some string' }, - ], - [ - { levelThreeArrayControlOne: 'some string', levelThreeArrayControlTwo: 'some string' }, - { levelThreeArrayControlOne: 'some string', levelThreeArrayControlTwo: 'some string' }, - ], - ], - }, - }; - - it('should generate the correct number of detail summary elements', inject([DynamicFormService], (dfs: DynamicFormService) => { - component.form = dfs.createForm(template, data); - - fixture.detectChanges(); - - const trList = fixture.debugElement.queryAll(By.css('tr')); - const tdList = fixture.debugElement.queryAll(By.css('td')); - - expect(trList).toHaveLength(10); - expect(tdList).toHaveLength(20); - })); - - it('should generate the correct number of input elements', inject([DynamicFormService], (dfs: DynamicFormService) => { - component.edit = true; - component.form = dfs.createForm(template, data); - - fixture.detectChanges(); - - const inputList = fixture.debugElement.queryAll(By.css('input')); - - expect(inputList).toHaveLength(10); - })); - - it('should generate the correct number of input elements if I reduce the data', inject([DynamicFormService], (dfs: DynamicFormService) => { - const newData = { - levelOneControl: 'some string', - levelOneGroup: { - levelTwoControl: 'some string', - levelTwoArray: [ - [{ levelThreeArrayControlOne: 'some string', levelThreeArrayControlTwo: 'some string' }], - [{ levelThreeArrayControlOne: 'some string', levelThreeArrayControlTwo: 'some string' }], - ], - }, - }; - component.edit = true; - component.form = dfs.createForm(template, newData); - - fixture.detectChanges(); - - const inputList = fixture.debugElement.queryAll(By.css('input')); - - expect(inputList).toHaveLength(6); - })); - - it('should generate the correct number of input elements if I increase the data', inject([DynamicFormService], (dfs: DynamicFormService) => { - const newData = { - levelOneControl: 'some string', - levelOneGroup: { - levelTwoControl: 'some string', - levelTwoArray: [ - [ - { levelThreeArrayControlOne: 'some string', levelThreeArrayControlTwo: 'some string' }, - { levelThreeArrayControlOne: 'some string', levelThreeArrayControlTwo: 'some string' }, - { levelThreeArrayControlOne: 'some string', levelThreeArrayControlTwo: 'some string' }, - ], - [ - { levelThreeArrayControlOne: 'some string', levelThreeArrayControlTwo: 'some string' }, - { levelThreeArrayControlOne: 'some string', levelThreeArrayControlTwo: 'some string' }, - { levelThreeArrayControlOne: 'some string', levelThreeArrayControlTwo: 'some string' }, - ], - ], - }, - }; - component.edit = true; - component.form = dfs.createForm(template, newData); - - fixture.detectChanges(); - - const inputList = fixture.debugElement.queryAll(By.css('input')); - - expect(inputList).toHaveLength(14); - })); - }); - - describe('template for array with groups', () => { - const template = { - name: 'myForm', - type: FormNodeTypes.GROUP, - children: [ - { - name: 'levelOneControl', type: FormNodeTypes.CONTROL, label: 'Level one control', value: 'some string', - }, - { - name: 'levelOneGroup', - type: FormNodeTypes.GROUP, - children: [ - { - name: 'levelTwoControl', type: FormNodeTypes.CONTROL, label: 'Level two control', value: 'some string', - }, - { - name: 'levelTwoArray', - type: FormNodeTypes.ARRAY, - children: [ - { - name: 'levelTwoGroup', - type: FormNodeTypes.GROUP, - children: [ - { name: 'levelTwoArrayControlOne', type: FormNodeTypes.CONTROL, value: '1' }, - { name: 'levelTwoArrayControlTwo', type: FormNodeTypes.CONTROL, value: '2' }, - ], - }, - ], - }, - ], - }, - ], - }; - - const data = { - levelOneControl: 'some string', - levelOneGroup: { - levelTwoControl: 'some string', - levelTwoArray: [ - { - levelTwoArrayControlOne: 'some string', - levelTwoArrayControlTwo: 'some string', - }, - { - levelTwoArrayControlOne: 'some string', - levelTwoArrayControlTwo: 'some string', - }, - ], - }, - }; - - it('should generate the correct number of detail summary elements', inject([DynamicFormService], (dfs: DynamicFormService) => { - component.form = dfs.createForm(template, data); - - fixture.detectChanges(); - - const trList = fixture.debugElement.queryAll(By.css('tr')); - const tdList = fixture.debugElement.queryAll(By.css('td')); - - expect(trList).toHaveLength(6); - expect(tdList).toHaveLength(12); - })); - - it('should generate the correct number of input elements', inject([DynamicFormService], (dfs: DynamicFormService) => { - component.edit = true; - component.form = dfs.createForm(template, data); - - fixture.detectChanges(); - - const inputList = fixture.debugElement.queryAll(By.css('input')); - - expect(inputList).toHaveLength(6); - })); - - it('should generate the correct number of input elements if I reduce the data', inject([DynamicFormService], (dfs: DynamicFormService) => { - const newData = { - levelOneControl: 'some string', - levelOneGroup: { - levelTwoControl: 'some string', - levelTwoArray: [ - { - levelTwoArrayControlOne: 'some string', - levelTwoArrayControlTwo: 'some string', - }, - ], - }, - }; - component.edit = true; - component.form = dfs.createForm(template, newData); - - fixture.detectChanges(); - - const inputList = fixture.debugElement.queryAll(By.css('input')); - - expect(inputList).toHaveLength(4); - })); - - it('should generate the correct number of input elements if I increase the data', inject([DynamicFormService], (dfs: DynamicFormService) => { - const newData = { - levelOneControl: 'some string', - levelOneGroup: { - levelTwoControl: 'some string', - levelTwoArray: [ - { - levelTwoArrayControlOne: 'some string', - levelTwoArrayControlTwo: 'some string', - }, - { - levelTwoArrayControlOne: 'some string', - levelTwoArrayControlTwo: 'some string', - }, - { - levelTwoArrayControlOne: 'some string', - levelTwoArrayControlTwo: 'some string', - }, - ], - }, - }; - component.edit = true; - component.form = dfs.createForm(template, newData); - - fixture.detectChanges(); - - const inputList = fixture.debugElement.queryAll(By.css('input')); - - expect(inputList).toHaveLength(8); - })); - }); - - describe('value changes', () => { - const template = { - name: 'myForm', - type: FormNodeTypes.GROUP, - children: [{ - name: 'levelOneControl', type: FormNodeTypes.CONTROL, label: 'Level one control', value: 'some string', - }], - }; - - const data = { - levelOneControl: 'some string', - }; - it('should output an event when the value of the control changes', fakeAsync( - inject([DynamicFormService], (dfs: DynamicFormService) => { - component.edit = true; - component.form = dfs.createForm(template, data); - - fixture.detectChanges(); - - const control = component.form.get('levelOneControl'); - control?.patchValue('foo'); - const emitter = jest.spyOn(component.formChange, 'emit'); - tick(500); - expect(emitter).toHaveBeenCalledWith({ ...data, levelOneControl: 'foo' }); - expect(emitter).toHaveBeenCalledTimes(1); - }), - )); - }); + let component: DynamicFormGroupComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [DynamicFormsModule, HttpClientTestingModule, RouterTestingModule], + providers: [ + provideMockStore({ initialState: initialAppState }), + ReferenceDataService, + TestStationsService, + { provide: UserService, useValue: {} }, + ], + }).compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(DynamicFormGroupComponent); + component = fixture.componentInstance; + // Don't detect changes on the first load as it will prevent change detection + // from working in the test function due to change detection being OnPush + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + describe('DynamicFormGroupComponent.prototype.trackByFn.name', () => { + it.each([ + [3, [3, 'some value']], + ['foo', [3, { key: 'foo' }]], + ])('should return %s when given %o', (expected, props) => { + const [index, item] = props; + expect(component.trackByFn(index as number, item)).toBe(expected); + }); + }); + + it.each([ + [ + { a: 'b', c: 1 }, + [ + { key: 'a', value: 'b' }, + { key: 'c', value: 1 }, + ], + ], + [ + { + name: 'test-name', + label: 'test-label', + type: 'test-type', + children: [ + { + name: 'test-c-name', + label: 'test-c-label', + value: 'test-c-value', + children: [], + type: 'test-c-control', + viewType: 'test-c-viewType', + }, + ], + }, + [ + { key: 'name', value: 'test-name' }, + { key: 'label', value: 'test-label' }, + { key: 'type', value: 'test-type' }, + { + key: 'children', + value: [ + { + name: 'test-c-name', + label: 'test-c-label', + value: 'test-c-value', + children: [], + type: 'test-c-control', + viewType: 'test-c-viewType', + }, + ], + }, + ], + ], + ])('entriesOf: should split the keys out into values', (input, expected) => { + expect(component.entriesOf(input as unknown as FormGroup)).toStrictEqual(expected); + }); + + describe('formNodeTypes', () => { + it('should return FormNodeTypes enum', () => { + Object.entries(FormNodeTypes).forEach((entry) => { + expect(FormNodeTypes).toEqual(component.formNodeTypes); + expect(component.formNodeTypes[entry[0] as keyof typeof FormNodeTypes]).toBe(entry[1]); + }); + }); + }); + + describe('formNodeViewTypes', () => { + it('should return FormNodeViewTypes enum', () => { + Object.entries(FormNodeViewTypes).forEach((entry) => { + expect(FormNodeViewTypes).toEqual(component.formNodeViewTypes); + expect(component.formNodeViewTypes[entry[0] as keyof typeof FormNodeViewTypes]).toBe(entry[1]); + }); + }); + }); + + describe('template', () => { + const template = { + name: 'myForm', + type: FormNodeTypes.GROUP, + children: [ + { + name: 'levelOneControl', + type: FormNodeTypes.CONTROL, + label: 'Level one control', + value: 'some string', + }, + { + name: 'levelOneGroup', + type: FormNodeTypes.GROUP, + children: [ + { + name: 'levelTwoControl', + type: FormNodeTypes.CONTROL, + label: 'Level two control', + value: 'some string', + }, + { + name: 'levelTwoArray', + type: FormNodeTypes.ARRAY, + children: [ + { name: 'levelTwoArrayControlOne', type: FormNodeTypes.CONTROL, value: '1' }, + { name: 'levelTwoArrayControlTwo', type: FormNodeTypes.CONTROL, value: '2' }, + ], + }, + ], + }, + ], + }; + + const data = { + levelOneControl: 'some string', + levelOneGroup: { + levelTwoControl: 'some string', + levelTwoArray: [ + { + levelTwoArrayControlOne: 'some string', + levelTwoArrayControlTwo: 'some string', + }, + ], + }, + }; + + it('should generate the correct number of detail summary elements', inject( + [DynamicFormService], + (dfs: DynamicFormService) => { + component.form = dfs.createForm(template, data); + + fixture.detectChanges(); + + const trList = fixture.debugElement.queryAll(By.css('tr')); + const tdList = fixture.debugElement.queryAll(By.css('td')); + + expect(trList).toHaveLength(4); + expect(tdList).toHaveLength(8); + } + )); + + it('should generate the correct number of input elements', inject( + [DynamicFormService], + (dfs: DynamicFormService) => { + component.edit = true; + component.form = dfs.createForm(template, data); + + fixture.detectChanges(); + + const inputList = fixture.debugElement.queryAll(By.css('input')); + + expect(inputList).toHaveLength(4); + } + )); + }); + + describe('template for nested array with groups', () => { + const template = { + name: 'myForm', + type: FormNodeTypes.GROUP, + children: [ + { + name: 'levelOneControl', + type: FormNodeTypes.CONTROL, + label: 'Level one control', + value: 'some string', + }, + { + name: 'levelOneGroup', + type: FormNodeTypes.GROUP, + children: [ + { + name: 'levelTwoControl', + type: FormNodeTypes.CONTROL, + label: 'Level two control', + value: 'some string', + }, + { + name: 'levelTwoArray', + type: FormNodeTypes.ARRAY, + children: [ + { + name: 'levelThreeArray', + type: FormNodeTypes.ARRAY, + children: [ + { + name: 'levelThreeGroup', + type: FormNodeTypes.GROUP, + children: [ + { name: 'levelThreeArrayControlOne', type: FormNodeTypes.CONTROL, value: '1' }, + { name: 'levelThreeArrayControlTwo', type: FormNodeTypes.CONTROL, value: '2' }, + ], + }, + ], + }, + ], + }, + ], + }, + ], + }; + + const data = { + levelOneControl: 'some string', + levelOneGroup: { + levelTwoControl: 'some string', + levelTwoArray: [ + [ + { levelThreeArrayControlOne: 'some string', levelThreeArrayControlTwo: 'some string' }, + { levelThreeArrayControlOne: 'some string', levelThreeArrayControlTwo: 'some string' }, + ], + [ + { levelThreeArrayControlOne: 'some string', levelThreeArrayControlTwo: 'some string' }, + { levelThreeArrayControlOne: 'some string', levelThreeArrayControlTwo: 'some string' }, + ], + ], + }, + }; + + it('should generate the correct number of detail summary elements', inject( + [DynamicFormService], + (dfs: DynamicFormService) => { + component.form = dfs.createForm(template, data); + + fixture.detectChanges(); + + const trList = fixture.debugElement.queryAll(By.css('tr')); + const tdList = fixture.debugElement.queryAll(By.css('td')); + + expect(trList).toHaveLength(10); + expect(tdList).toHaveLength(20); + } + )); + + it('should generate the correct number of input elements', inject( + [DynamicFormService], + (dfs: DynamicFormService) => { + component.edit = true; + component.form = dfs.createForm(template, data); + + fixture.detectChanges(); + + const inputList = fixture.debugElement.queryAll(By.css('input')); + + expect(inputList).toHaveLength(10); + } + )); + + it('should generate the correct number of input elements if I reduce the data', inject( + [DynamicFormService], + (dfs: DynamicFormService) => { + const newData = { + levelOneControl: 'some string', + levelOneGroup: { + levelTwoControl: 'some string', + levelTwoArray: [ + [{ levelThreeArrayControlOne: 'some string', levelThreeArrayControlTwo: 'some string' }], + [{ levelThreeArrayControlOne: 'some string', levelThreeArrayControlTwo: 'some string' }], + ], + }, + }; + component.edit = true; + component.form = dfs.createForm(template, newData); + + fixture.detectChanges(); + + const inputList = fixture.debugElement.queryAll(By.css('input')); + + expect(inputList).toHaveLength(6); + } + )); + + it('should generate the correct number of input elements if I increase the data', inject( + [DynamicFormService], + (dfs: DynamicFormService) => { + const newData = { + levelOneControl: 'some string', + levelOneGroup: { + levelTwoControl: 'some string', + levelTwoArray: [ + [ + { levelThreeArrayControlOne: 'some string', levelThreeArrayControlTwo: 'some string' }, + { levelThreeArrayControlOne: 'some string', levelThreeArrayControlTwo: 'some string' }, + { levelThreeArrayControlOne: 'some string', levelThreeArrayControlTwo: 'some string' }, + ], + [ + { levelThreeArrayControlOne: 'some string', levelThreeArrayControlTwo: 'some string' }, + { levelThreeArrayControlOne: 'some string', levelThreeArrayControlTwo: 'some string' }, + { levelThreeArrayControlOne: 'some string', levelThreeArrayControlTwo: 'some string' }, + ], + ], + }, + }; + component.edit = true; + component.form = dfs.createForm(template, newData); + + fixture.detectChanges(); + + const inputList = fixture.debugElement.queryAll(By.css('input')); + + expect(inputList).toHaveLength(14); + } + )); + }); + + describe('template for array with groups', () => { + const template = { + name: 'myForm', + type: FormNodeTypes.GROUP, + children: [ + { + name: 'levelOneControl', + type: FormNodeTypes.CONTROL, + label: 'Level one control', + value: 'some string', + }, + { + name: 'levelOneGroup', + type: FormNodeTypes.GROUP, + children: [ + { + name: 'levelTwoControl', + type: FormNodeTypes.CONTROL, + label: 'Level two control', + value: 'some string', + }, + { + name: 'levelTwoArray', + type: FormNodeTypes.ARRAY, + children: [ + { + name: 'levelTwoGroup', + type: FormNodeTypes.GROUP, + children: [ + { name: 'levelTwoArrayControlOne', type: FormNodeTypes.CONTROL, value: '1' }, + { name: 'levelTwoArrayControlTwo', type: FormNodeTypes.CONTROL, value: '2' }, + ], + }, + ], + }, + ], + }, + ], + }; + + const data = { + levelOneControl: 'some string', + levelOneGroup: { + levelTwoControl: 'some string', + levelTwoArray: [ + { + levelTwoArrayControlOne: 'some string', + levelTwoArrayControlTwo: 'some string', + }, + { + levelTwoArrayControlOne: 'some string', + levelTwoArrayControlTwo: 'some string', + }, + ], + }, + }; + + it('should generate the correct number of detail summary elements', inject( + [DynamicFormService], + (dfs: DynamicFormService) => { + component.form = dfs.createForm(template, data); + + fixture.detectChanges(); + + const trList = fixture.debugElement.queryAll(By.css('tr')); + const tdList = fixture.debugElement.queryAll(By.css('td')); + + expect(trList).toHaveLength(6); + expect(tdList).toHaveLength(12); + } + )); + + it('should generate the correct number of input elements', inject( + [DynamicFormService], + (dfs: DynamicFormService) => { + component.edit = true; + component.form = dfs.createForm(template, data); + + fixture.detectChanges(); + + const inputList = fixture.debugElement.queryAll(By.css('input')); + + expect(inputList).toHaveLength(6); + } + )); + + it('should generate the correct number of input elements if I reduce the data', inject( + [DynamicFormService], + (dfs: DynamicFormService) => { + const newData = { + levelOneControl: 'some string', + levelOneGroup: { + levelTwoControl: 'some string', + levelTwoArray: [ + { + levelTwoArrayControlOne: 'some string', + levelTwoArrayControlTwo: 'some string', + }, + ], + }, + }; + component.edit = true; + component.form = dfs.createForm(template, newData); + + fixture.detectChanges(); + + const inputList = fixture.debugElement.queryAll(By.css('input')); + + expect(inputList).toHaveLength(4); + } + )); + + it('should generate the correct number of input elements if I increase the data', inject( + [DynamicFormService], + (dfs: DynamicFormService) => { + const newData = { + levelOneControl: 'some string', + levelOneGroup: { + levelTwoControl: 'some string', + levelTwoArray: [ + { + levelTwoArrayControlOne: 'some string', + levelTwoArrayControlTwo: 'some string', + }, + { + levelTwoArrayControlOne: 'some string', + levelTwoArrayControlTwo: 'some string', + }, + { + levelTwoArrayControlOne: 'some string', + levelTwoArrayControlTwo: 'some string', + }, + ], + }, + }; + component.edit = true; + component.form = dfs.createForm(template, newData); + + fixture.detectChanges(); + + const inputList = fixture.debugElement.queryAll(By.css('input')); + + expect(inputList).toHaveLength(8); + } + )); + }); + + describe('value changes', () => { + const template = { + name: 'myForm', + type: FormNodeTypes.GROUP, + children: [ + { + name: 'levelOneControl', + type: FormNodeTypes.CONTROL, + label: 'Level one control', + value: 'some string', + }, + ], + }; + + const data = { + levelOneControl: 'some string', + }; + it('should output an event when the value of the control changes', fakeAsync( + inject([DynamicFormService], (dfs: DynamicFormService) => { + component.edit = true; + component.form = dfs.createForm(template, data); + + fixture.detectChanges(); + + const control = component.form.get('levelOneControl'); + control?.patchValue('foo'); + const emitter = jest.spyOn(component.formChange, 'emit'); + tick(500); + expect(emitter).toHaveBeenCalledWith({ ...data, levelOneControl: 'foo' }); + expect(emitter).toHaveBeenCalledTimes(1); + }) + )); + }); }); diff --git a/src/app/forms/components/dynamic-form-group/dynamic-form-group.component.ts b/src/app/forms/components/dynamic-form-group/dynamic-form-group.component.ts index 96f3eb6ad6..4989d18fb6 100644 --- a/src/app/forms/components/dynamic-form-group/dynamic-form-group.component.ts +++ b/src/app/forms/components/dynamic-form-group/dynamic-form-group.component.ts @@ -1,69 +1,86 @@ import { - ChangeDetectionStrategy, Component, EventEmitter, Input, OnChanges, OnDestroy, OnInit, Output, SimpleChanges, + ChangeDetectionStrategy, + Component, + EventEmitter, + Input, + OnChanges, + OnDestroy, + OnInit, + Output, + SimpleChanges, } from '@angular/core'; import { FormGroup } from '@angular/forms'; import { Subject, debounceTime, takeUntil } from 'rxjs'; import { DynamicFormService } from '../../services/dynamic-form.service'; import { - CustomFormArray, CustomFormGroup, FormNode, FormNodeTypes, FormNodeViewTypes, + CustomFormArray, + CustomFormGroup, + FormNode, + FormNodeTypes, + FormNodeViewTypes, } from '../../services/dynamic-form.types'; @Component({ - selector: 'app-dynamic-form-group', - templateUrl: './dynamic-form-group.component.html', - styleUrls: ['./dynamic-form-group.component.scss'], - changeDetection: ChangeDetectionStrategy.OnPush, + selector: 'app-dynamic-form-group', + templateUrl: './dynamic-form-group.component.html', + styleUrls: ['./dynamic-form-group.component.scss'], + changeDetection: ChangeDetectionStrategy.OnPush, }) export class DynamicFormGroupComponent implements OnChanges, OnInit, OnDestroy { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - @Input() data: any = {}; - @Input() template?: FormNode; - @Input() edit = false; - @Output() formChange = new EventEmitter(); + // eslint-disable-next-line @typescript-eslint/no-explicit-any + @Input() data: any = {}; + @Input() template?: FormNode; + @Input() edit = false; + @Output() formChange = new EventEmitter(); - form: CustomFormGroup | CustomFormArray = new CustomFormGroup({ name: 'dynamic-form', type: FormNodeTypes.GROUP, children: [] }, {}); + form: CustomFormGroup | CustomFormArray = new CustomFormGroup( + { name: 'dynamic-form', type: FormNodeTypes.GROUP, children: [] }, + {} + ); - private destroy$ = new Subject(); + private destroy$ = new Subject(); - constructor(private dfs: DynamicFormService) {} + constructor(private dfs: DynamicFormService) {} - ngOnChanges(changes: SimpleChanges): void { - const { template, data } = changes; - if (template && template.currentValue) { - this.form = this.dfs.createForm(template.currentValue, this.data); - } - if (data?.currentValue && data.currentValue !== data.previousValue) { - this.form.patchValue(data.currentValue, { emitEvent: false }); - } - } + ngOnChanges(changes: SimpleChanges): void { + const { template, data } = changes; + if (template && template.currentValue) { + this.form = this.dfs.createForm(template.currentValue, this.data); + } + if (data?.currentValue && data.currentValue !== data.previousValue) { + this.form.patchValue(data.currentValue, { emitEvent: false }); + } + } - ngOnInit(): void { - this.form.cleanValueChanges.pipe(debounceTime(400), takeUntil(this.destroy$)).subscribe((e) => this.formChange.emit(e)); - } + ngOnInit(): void { + this.form.cleanValueChanges + .pipe(debounceTime(400), takeUntil(this.destroy$)) + .subscribe((e) => this.formChange.emit(e)); + } - ngOnDestroy(): void { - this.destroy$.next(); - this.destroy$.complete(); - } + ngOnDestroy(): void { + this.destroy$.next(); + this.destroy$.complete(); + } - // eslint-disable-next-line @typescript-eslint/no-explicit-any - entriesOf(obj: FormGroup): { key: string; value: any }[] { - return Object.entries(obj).map(([key, value]) => ({ - key, - value, - })); - } + // eslint-disable-next-line @typescript-eslint/no-explicit-any + entriesOf(obj: FormGroup): { key: string; value: any }[] { + return Object.entries(obj).map(([key, value]) => ({ + key, + value, + })); + } - // eslint-disable-next-line @typescript-eslint/no-explicit-any - trackByFn(index: number, item: any) { - return Object.prototype.hasOwnProperty.call(item, 'key') ? item.key : index; - } + // eslint-disable-next-line @typescript-eslint/no-explicit-any + trackByFn(index: number, item: any) { + return Object.prototype.hasOwnProperty.call(item, 'key') ? item.key : index; + } - get formNodeTypes(): typeof FormNodeTypes { - return FormNodeTypes; - } + get formNodeTypes(): typeof FormNodeTypes { + return FormNodeTypes; + } - get formNodeViewTypes(): typeof FormNodeViewTypes { - return FormNodeViewTypes; - } + get formNodeViewTypes(): typeof FormNodeViewTypes { + return FormNodeViewTypes; + } } diff --git a/src/app/forms/components/field-error-message/field-error-message.component.spec.ts b/src/app/forms/components/field-error-message/field-error-message.component.spec.ts index 7afdf48b71..d5aa120cbc 100644 --- a/src/app/forms/components/field-error-message/field-error-message.component.spec.ts +++ b/src/app/forms/components/field-error-message/field-error-message.component.spec.ts @@ -3,22 +3,22 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { FieldErrorMessageComponent } from './field-error-message.component'; describe('FieldErrorMessageComponent', () => { - let component: FieldErrorMessageComponent; - let fixture: ComponentFixture; + let component: FieldErrorMessageComponent; + let fixture: ComponentFixture; - beforeEach(async () => { - await TestBed.configureTestingModule({ - declarations: [FieldErrorMessageComponent], - }).compileComponents(); - }); + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [FieldErrorMessageComponent], + }).compileComponents(); + }); - beforeEach(() => { - fixture = TestBed.createComponent(FieldErrorMessageComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); + beforeEach(() => { + fixture = TestBed.createComponent(FieldErrorMessageComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); }); diff --git a/src/app/forms/components/field-error-message/field-error-message.component.ts b/src/app/forms/components/field-error-message/field-error-message.component.ts index 295d923983..63915294d5 100644 --- a/src/app/forms/components/field-error-message/field-error-message.component.ts +++ b/src/app/forms/components/field-error-message/field-error-message.component.ts @@ -1,10 +1,10 @@ import { Component, Input } from '@angular/core'; @Component({ - selector: 'app-field-error-message', - templateUrl: './field-error-message.component.html', + selector: 'app-field-error-message', + templateUrl: './field-error-message.component.html', }) export class FieldErrorMessageComponent { - @Input() name = ''; - @Input() error?: string | null; + @Input() name = ''; + @Input() error?: string | null; } diff --git a/src/app/forms/components/field-warning-message/field-warning-message.component.spec.ts b/src/app/forms/components/field-warning-message/field-warning-message.component.spec.ts index b4f1fe5fe1..05449e3ca4 100644 --- a/src/app/forms/components/field-warning-message/field-warning-message.component.spec.ts +++ b/src/app/forms/components/field-warning-message/field-warning-message.component.spec.ts @@ -3,21 +3,20 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { FieldWarningMessageComponent } from './field-warning-message.component'; describe('FieldWarningMessageComponent', () => { - let component: FieldWarningMessageComponent; - let fixture: ComponentFixture; + let component: FieldWarningMessageComponent; + let fixture: ComponentFixture; - beforeEach(async () => { - await TestBed.configureTestingModule({ - declarations: [FieldWarningMessageComponent], - }) - .compileComponents(); + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [FieldWarningMessageComponent], + }).compileComponents(); - fixture = TestBed.createComponent(FieldWarningMessageComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); + fixture = TestBed.createComponent(FieldWarningMessageComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); }); diff --git a/src/app/forms/components/field-warning-message/field-warning-message.component.ts b/src/app/forms/components/field-warning-message/field-warning-message.component.ts index 72d1b782f1..9a0f7e52ae 100644 --- a/src/app/forms/components/field-warning-message/field-warning-message.component.ts +++ b/src/app/forms/components/field-warning-message/field-warning-message.component.ts @@ -1,10 +1,10 @@ import { ChangeDetectionStrategy, Component, Input } from '@angular/core'; @Component({ - selector: 'app-field-warning-message', - templateUrl: './field-warning-message.component.html', - changeDetection: ChangeDetectionStrategy.OnPush, + selector: 'app-field-warning-message', + templateUrl: './field-warning-message.component.html', + changeDetection: ChangeDetectionStrategy.OnPush, }) export class FieldWarningMessageComponent { - @Input() warningMessage: string | undefined; + @Input() warningMessage: string | undefined; } diff --git a/src/app/forms/components/number-input/number-input.component.spec.ts b/src/app/forms/components/number-input/number-input.component.spec.ts index 20c2e2942e..e75b95bb90 100644 --- a/src/app/forms/components/number-input/number-input.component.spec.ts +++ b/src/app/forms/components/number-input/number-input.component.spec.ts @@ -8,36 +8,42 @@ import { FieldWarningMessageComponent } from '../field-warning-message/field-war import { NumberInputComponent } from './number-input.component'; @Component({ - selector: 'app-host-component', - template: `
+ selector: 'app-host-component', + template: `
`, - styles: [], + styles: [], }) class HostComponent { - form = new FormGroup({ - foo: new CustomFormControl({ name: 'foo', type: FormNodeTypes.CONTROL }, ''), - }); + form = new FormGroup({ + foo: new CustomFormControl({ name: 'foo', type: FormNodeTypes.CONTROL }, ''), + }); } describe('NumberInputComponent', () => { - let component: HostComponent; - let fixture: ComponentFixture; + let component: HostComponent; + let fixture: ComponentFixture; - beforeEach(async () => { - await TestBed.configureTestingModule({ - declarations: [HostComponent, NumberInputComponent, BaseControlComponent, FieldErrorMessageComponent, FieldWarningMessageComponent], - imports: [FormsModule, ReactiveFormsModule], - }).compileComponents(); - }); + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ + HostComponent, + NumberInputComponent, + BaseControlComponent, + FieldErrorMessageComponent, + FieldWarningMessageComponent, + ], + imports: [FormsModule, ReactiveFormsModule], + }).compileComponents(); + }); - beforeEach(() => { - fixture = TestBed.createComponent(HostComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); + beforeEach(() => { + fixture = TestBed.createComponent(HostComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); }); diff --git a/src/app/forms/components/number-input/number-input.component.ts b/src/app/forms/components/number-input/number-input.component.ts index d099ed761a..9292c1c4a7 100644 --- a/src/app/forms/components/number-input/number-input.component.ts +++ b/src/app/forms/components/number-input/number-input.component.ts @@ -3,48 +3,49 @@ import { NG_VALUE_ACCESSOR } from '@angular/forms'; import { BaseControlComponent } from '../base-control/base-control.component'; @Component({ - selector: 'app-number-input', - templateUrl: './number-input.component.html', - styleUrls: ['./number-input.component.scss'], - providers: [ - { - provide: NG_VALUE_ACCESSOR, - useExisting: NumberInputComponent, - multi: true, - }, - ], + selector: 'app-number-input', + templateUrl: './number-input.component.html', + styleUrls: ['./number-input.component.scss'], + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: NumberInputComponent, + multi: true, + }, + ], }) export class NumberInputComponent extends BaseControlComponent implements AfterContentInit { - @Input() vehicleType?: string | null; - @Input() enableDecimals: boolean | undefined = false; - get style(): string { - return `govuk-input ${this.width ? `govuk-input--width-${this.width}` : ''}`; - } + @Input() vehicleType?: string | null; + @Input() enableDecimals: boolean | undefined = false; + get style(): string { + return `govuk-input ${this.width ? `govuk-input--width-${this.width}` : ''}`; + } - get getWarningMessage(): string { + get getWarningMessage(): string { + if (this.isCorrectVehicleType()) { + if (this.shouldDisplayLengthWarning()) + return 'This length dimension field value is greater than 12,000mm. Check your input before proceeding'; + if (this.shouldDisplayWidthWarning()) + return 'This width dimension field value is greater than 2,600mm. Check your input before proceeding'; + } + return ''; + } - if (this.isCorrectVehicleType()) { - if (this.shouldDisplayLengthWarning()) return 'This length dimension field value is greater than 12,000mm. Check your input before proceeding'; - if (this.shouldDisplayWidthWarning()) return 'This width dimension field value is greater than 2,600mm. Check your input before proceeding'; - } - return ''; - } + shouldDisplayLengthWarning(): boolean { + return this.label === 'Length' && Number.parseInt(this.value, 10) > 12000; + } + shouldDisplayWidthWarning(): boolean { + return this.label === 'Width' && Number.parseInt(this.value, 10) > 2600; + } - shouldDisplayLengthWarning(): boolean { - return this.label === 'Length' && parseInt(this.value, 10) > 12000; - } - shouldDisplayWidthWarning(): boolean { - return this.label === 'Width' && parseInt(this.value, 10) > 2600; - } + isCorrectVehicleType(): boolean { + return this.vehicleType === 'hgv' || this.vehicleType === 'trl'; + } - isCorrectVehicleType(): boolean { - return this.vehicleType === 'hgv' || this.vehicleType === 'trl'; - } - - override ngAfterContentInit(): void { - super.ngAfterContentInit(); - if (this.control) { - this.control.meta.customId = this.name; - } - } + override ngAfterContentInit(): void { + super.ngAfterContentInit(); + if (this.control) { + this.control.meta.customId = this.name; + } + } } diff --git a/src/app/forms/components/radio-group/radio-group.component.spec.ts b/src/app/forms/components/radio-group/radio-group.component.spec.ts index 79c2881a0f..bda7a4d031 100644 --- a/src/app/forms/components/radio-group/radio-group.component.spec.ts +++ b/src/app/forms/components/radio-group/radio-group.component.spec.ts @@ -2,75 +2,75 @@ import { Component } from '@angular/core'; import { ComponentFixture, TestBed } from '@angular/core/testing'; import { FormGroup, FormsModule, ReactiveFormsModule } from '@angular/forms'; import { By } from '@angular/platform-browser'; -import { ReferenceDataService } from '@services/reference-data/reference-data.service'; import { provideMockStore } from '@ngrx/store/testing'; +import { ReferenceDataService } from '@services/reference-data/reference-data.service'; import { initialAppState } from '@store/.'; -import { CustomFormControl, FormNodeTypes, FormNodeOption } from '../../services/dynamic-form.types'; -import { RadioGroupComponent } from './radio-group.component'; +import { CustomFormControl, FormNodeOption, FormNodeTypes } from '../../services/dynamic-form.types'; import { BaseControlComponent } from '../base-control/base-control.component'; import { FieldErrorMessageComponent } from '../field-error-message/field-error-message.component'; +import { RadioGroupComponent } from './radio-group.component'; @Component({ - selector: 'app-host-component', - template: `
+ selector: 'app-host-component', + template: `
`, - styles: [], + styles: [], }) class HostComponent { - form = new FormGroup({ - foo: new CustomFormControl({ name: 'foo', type: FormNodeTypes.CONTROL, children: [] }, null), - }); - options: FormNodeOption[] = [ - { label: 'Value 1', value: '1' }, - { label: 'Value 2', value: '2' }, - { label: 'Value 3', value: '3' }, - ]; + form = new FormGroup({ + foo: new CustomFormControl({ name: 'foo', type: FormNodeTypes.CONTROL, children: [] }, null), + }); + options: FormNodeOption[] = [ + { label: 'Value 1', value: '1' }, + { label: 'Value 2', value: '2' }, + { label: 'Value 3', value: '3' }, + ]; } describe('RadioGroupComponent', () => { - let component: HostComponent; - let fixture: ComponentFixture; + let component: HostComponent; + let fixture: ComponentFixture; - beforeEach(async () => { - await TestBed.configureTestingModule({ - declarations: [HostComponent, RadioGroupComponent, BaseControlComponent, FieldErrorMessageComponent], - imports: [FormsModule, ReactiveFormsModule], - providers: [ReferenceDataService, provideMockStore({ initialState: initialAppState })], - }).compileComponents(); - }); + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [HostComponent, RadioGroupComponent, BaseControlComponent, FieldErrorMessageComponent], + imports: [FormsModule, ReactiveFormsModule], + providers: [ReferenceDataService, provideMockStore({ initialState: initialAppState })], + }).compileComponents(); + }); - beforeEach(() => { - fixture = TestBed.createComponent(HostComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); + beforeEach(() => { + fixture = TestBed.createComponent(HostComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); - describe('value', () => { - it('should be propagated from element to the form control', () => { - const foo = component.form.get('foo'); - const radios = fixture.debugElement.queryAll(By.css('input[type="radio"]')); - expect(radios).toHaveLength(3); + describe('value', () => { + it('should be propagated from element to the form control', () => { + const foo = component.form.get('foo'); + const radios = fixture.debugElement.queryAll(By.css('input[type="radio"]')); + expect(radios).toHaveLength(3); - (radios[0].nativeElement as HTMLInputElement).click(); - expect(foo?.value).toBe('1'); - (radios[1].nativeElement as HTMLInputElement).click(); - expect(foo?.value).toBe('2'); - (radios[1].nativeElement as HTMLInputElement).click(); - expect(foo?.value).not.toBeNull(); - }); + (radios[0].nativeElement as HTMLInputElement).click(); + expect(foo?.value).toBe('1'); + (radios[1].nativeElement as HTMLInputElement).click(); + expect(foo?.value).toBe('2'); + (radios[1].nativeElement as HTMLInputElement).click(); + expect(foo?.value).not.toBeNull(); + }); - it('should check a radio button when the form value is updated', () => { - component.form.patchValue({ foo: '2' }); - fixture.detectChanges(); - const radios2 = fixture.debugElement.queryAll(By.css('input[checked=true]')); - expect(radios2).toHaveLength(1); - expect(radios2[0].nativeElement.id).toBe('foo-2-radio'); - component.form.patchValue({ foo: '3' }); - fixture.detectChanges(); - const radios3 = fixture.debugElement.queryAll(By.css('input[checked=true]')); - expect(radios3).toHaveLength(1); - expect(radios3[0].nativeElement.id).toBe('foo-3-radio'); - }); - }); + it('should check a radio button when the form value is updated', () => { + component.form.patchValue({ foo: '2' }); + fixture.detectChanges(); + const radios2 = fixture.debugElement.queryAll(By.css('input[checked=true]')); + expect(radios2).toHaveLength(1); + expect(radios2[0].nativeElement.id).toBe('foo-2-radio'); + component.form.patchValue({ foo: '3' }); + fixture.detectChanges(); + const radios3 = fixture.debugElement.queryAll(By.css('input[checked=true]')); + expect(radios3).toHaveLength(1); + expect(radios3[0].nativeElement.id).toBe('foo-3-radio'); + }); + }); }); diff --git a/src/app/forms/components/radio-group/radio-group.component.ts b/src/app/forms/components/radio-group/radio-group.component.ts index c57aa7116f..eb8873fd8f 100644 --- a/src/app/forms/components/radio-group/radio-group.component.ts +++ b/src/app/forms/components/radio-group/radio-group.component.ts @@ -4,33 +4,32 @@ import { FormNodeOption } from '@forms/services/dynamic-form.types'; import { BaseControlComponent } from '../base-control/base-control.component'; @Component({ - selector: 'app-radio-group', - templateUrl: './radio-group.component.html', - providers: [ - { - provide: NG_VALUE_ACCESSOR, - useExisting: RadioGroupComponent, - multi: true, - }, - ], - styleUrls: ['./radio-group.component.scss'], + selector: 'app-radio-group', + templateUrl: './radio-group.component.html', + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: RadioGroupComponent, + multi: true, + }, + ], + styleUrls: ['./radio-group.component.scss'], }) export class RadioGroupComponent extends BaseControlComponent { - @Input() options: FormNodeOption[] = []; - @Input() inline = false; + @Input() options: FormNodeOption[] = []; + @Input() inline = false; - getLabel(value: string | number | boolean | null): string | undefined { - return this.options.find((option) => option.value === value)?.label; - } + getLabel(value: string | number | boolean | null): string | undefined { + return this.options.find((option) => option.value === value)?.label; + } - trackByFn = (index: number): number => index; - - getId(value: string | number | boolean | null, name: string) { - const id = `${name}-${value}-radio`; - if (this.control) { - this.control.meta.customId = id; - } - return id; - } + trackByFn = (index: number): number => index; + getId(value: string | number | boolean | null, name: string) { + const id = `${name}-${value}-radio`; + if (this.control) { + this.control.meta.customId = id; + } + return id; + } } diff --git a/src/app/forms/components/radio-group/radio-group.stories.ts b/src/app/forms/components/radio-group/radio-group.stories.ts index 7bcde00aad..38ea5fee0b 100644 --- a/src/app/forms/components/radio-group/radio-group.stories.ts +++ b/src/app/forms/components/radio-group/radio-group.stories.ts @@ -1,60 +1,60 @@ import { CommonModule } from '@angular/common'; import { FormControl, FormGroup, FormsModule, ReactiveFormsModule } from '@angular/forms'; -import { Meta, moduleMetadata, Story } from '@storybook/angular'; +import { Meta, Story, moduleMetadata } from '@storybook/angular'; import { BaseControlComponent } from '../base-control/base-control.component'; import { RadioGroupComponent } from './radio-group.component'; export default { - title: 'Forms/Radio button group', - component: RadioGroupComponent, - decorators: [ - moduleMetadata({ - declarations: [RadioGroupComponent, BaseControlComponent], - imports: [CommonModule, FormsModule, ReactiveFormsModule] - }) - ] + title: 'Forms/Radio button group', + component: RadioGroupComponent, + decorators: [ + moduleMetadata({ + declarations: [RadioGroupComponent, BaseControlComponent], + imports: [CommonModule, FormsModule, ReactiveFormsModule], + }), + ], } as Meta; -const Template: Story = args => { - const { label, options, name, hint, value = null, disabled = false, validators = [] } = args; - const form = new FormGroup({ [name]: new FormControl({ value, disabled }, validators) }); - return { - component: RadioGroupComponent, - template: `
{{ form.value | json }}
`, - props: { - form, - label, - name, - options, - hint - } - }; +const Template: Story = (args) => { + const { label, options, name, hint, value = null, disabled = false, validators = [] } = args; + const form = new FormGroup({ [name]: new FormControl({ value, disabled }, validators) }); + return { + component: RadioGroupComponent, + template: `
{{ form.value | json }}
`, + props: { + form, + label, + name, + options, + hint, + }, + }; }; const defaultArgs = { - label: 'Colour picker', - name: 'color', - options: [ - { label: 'Red', value: 'red' }, - { label: 'Yellow', value: 'yellow', hint: 'this is the colour of the sun' }, - { label: 'Blue', value: 'blue', hint: 'this is the colour of the sea' } - ], - hint: 'These are the three primary colours' + label: 'Colour picker', + name: 'color', + options: [ + { label: 'Red', value: 'red' }, + { label: 'Yellow', value: 'yellow', hint: 'this is the colour of the sun' }, + { label: 'Blue', value: 'blue', hint: 'this is the colour of the sea' }, + ], + hint: 'These are the three primary colours', }; export const Enabled = Template.bind({}); Enabled.args = { - ...defaultArgs + ...defaultArgs, }; export const Disabled = Template.bind({}); Disabled.args = { - ...defaultArgs, - disabled: true + ...defaultArgs, + disabled: true, }; export const Initilisation = Template.bind({}); Initilisation.args = { - ...defaultArgs, - value: 'red' + ...defaultArgs, + value: 'red', }; diff --git a/src/app/forms/components/read-only/read-only.component.spec.ts b/src/app/forms/components/read-only/read-only.component.spec.ts index 357a5516ee..ec72b6c376 100644 --- a/src/app/forms/components/read-only/read-only.component.spec.ts +++ b/src/app/forms/components/read-only/read-only.component.spec.ts @@ -7,36 +7,36 @@ import { BaseControlComponent } from '../base-control/base-control.component'; import { ReadOnlyComponent } from './read-only.component'; @Component({ - selector: 'app-host-component', - template: `
+ selector: 'app-host-component', + template: `
`, - styles: [], + styles: [], }) class HostComponent { - form = new FormGroup({ - foo: new CustomFormControl({ name: 'foo', type: FormNodeTypes.CONTROL }, ''), - }); + form = new FormGroup({ + foo: new CustomFormControl({ name: 'foo', type: FormNodeTypes.CONTROL }, ''), + }); } describe('ReadOnlyComponent', () => { - let component: HostComponent; - let fixture: ComponentFixture; + let component: HostComponent; + let fixture: ComponentFixture; - beforeEach(async () => { - await TestBed.configureTestingModule({ - declarations: [BaseControlComponent, HostComponent, ReadOnlyComponent], - imports: [FormsModule, ReactiveFormsModule, SharedModule], - }).compileComponents(); - }); + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [BaseControlComponent, HostComponent, ReadOnlyComponent], + imports: [FormsModule, ReactiveFormsModule, SharedModule], + }).compileComponents(); + }); - beforeEach(() => { - fixture = TestBed.createComponent(HostComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); + beforeEach(() => { + fixture = TestBed.createComponent(HostComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); }); diff --git a/src/app/forms/components/read-only/read-only.component.ts b/src/app/forms/components/read-only/read-only.component.ts index c0225c4ee4..0764f64076 100644 --- a/src/app/forms/components/read-only/read-only.component.ts +++ b/src/app/forms/components/read-only/read-only.component.ts @@ -3,17 +3,17 @@ import { NG_VALUE_ACCESSOR } from '@angular/forms'; import { BaseControlComponent } from '../base-control/base-control.component'; @Component({ - selector: 'app-read-only', - templateUrl: './read-only.component.html', - providers: [ - { - provide: NG_VALUE_ACCESSOR, - useExisting: ReadOnlyComponent, - multi: true, - }, - ], + selector: 'app-read-only', + templateUrl: './read-only.component.html', + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: ReadOnlyComponent, + multi: true, + }, + ], }) export class ReadOnlyComponent extends BaseControlComponent { - @Input() readOnlySuffix?: string; - @Input() date?: boolean = false; + @Input() readOnlySuffix?: string; + @Input() date?: boolean = false; } diff --git a/src/app/forms/components/required-standard-select/required-standard-select.component.spec.ts b/src/app/forms/components/required-standard-select/required-standard-select.component.spec.ts index c037993ff8..acd4a5827d 100644 --- a/src/app/forms/components/required-standard-select/required-standard-select.component.spec.ts +++ b/src/app/forms/components/required-standard-select/required-standard-select.component.spec.ts @@ -2,101 +2,116 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { Router } from '@angular/router'; import { RouterTestingModule } from '@angular/router/testing'; import { EUVehicleCategory } from '@dvsa/cvs-type-definitions/types/required-standards/defects/enums/euVehicleCategory.enum.js'; -import { RequiredStandard, RequiredStandardTaxonomySection } from '@dvsa/cvs-type-definitions/types/required-standards/defects/get'; +import { + RequiredStandard, + RequiredStandardTaxonomySection, +} from '@dvsa/cvs-type-definitions/types/required-standards/defects/get'; import { INSPECTION_TYPE } from '@models/test-results/test-result-required-standard.model'; import { provideMockStore } from '@ngrx/store/testing'; import { initialAppState } from '@store/index'; import { RequiredStandardSelectComponent } from './required-standard-select.component'; describe('RequiredStandardSelectComponent', () => { - let component: RequiredStandardSelectComponent; - let fixture: ComponentFixture; - let router: Router; - - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [RouterTestingModule], - declarations: [RequiredStandardSelectComponent], - providers: [provideMockStore({ initialState: initialAppState })], - }).compileComponents(); - }); - - beforeEach(() => { - fixture = TestBed.createComponent(RequiredStandardSelectComponent); - router = TestBed.inject(Router); - component = fixture.componentInstance; - fixture.detectChanges(); - jest.clearAllMocks(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); - - describe('handleSelectBasicOrNormal', () => { - it('should work for basic inspection', () => { - component.basicAndNormalRequiredStandards = { - basic: ['basic' as unknown as RequiredStandardTaxonomySection, 'basic1' as unknown as RequiredStandardTaxonomySection], - normal: ['normal' as unknown as RequiredStandardTaxonomySection, 'normal1' as unknown as RequiredStandardTaxonomySection], - euVehicleCategories: [EUVehicleCategory.M1], - }; - - component.handleSelectBasicOrNormal(INSPECTION_TYPE.BASIC); - - expect(component.requiredStandards).toStrictEqual(['basic', 'basic1']); - }); - it('should work for normal inspection', () => { - component.basicAndNormalRequiredStandards = { - basic: ['basic' as unknown as RequiredStandardTaxonomySection, 'basic1' as unknown as RequiredStandardTaxonomySection], - normal: ['normal' as unknown as RequiredStandardTaxonomySection, 'normal1' as unknown as RequiredStandardTaxonomySection], - euVehicleCategories: [EUVehicleCategory.M1], - }; - - component.handleSelectBasicOrNormal(INSPECTION_TYPE.NORMAL); - - expect(component.requiredStandards).toStrictEqual(['normal', 'normal1']); - }); - }); - - describe('handleSelect', () => { - it('should handle when I pick an inspection type', () => { - const spy = jest.spyOn(component, 'handleSelectBasicOrNormal'); - - component.handleSelect(INSPECTION_TYPE.BASIC, Types.InspectionType); - - expect(spy).toHaveBeenCalled(); - expect(component.selectedInspectionType).toBe(INSPECTION_TYPE.BASIC); - expect(component.selectedSection).toBeUndefined(); - expect(component.selectedRequiredStandard).toBeUndefined(); - }); - it('should handle when I pick a section', () => { - component.handleSelect('section' as unknown as RequiredStandardTaxonomySection, Types.Section); - - expect(component.selectedSection).toBe('section'); - expect(component.selectedRequiredStandard).toBeUndefined(); - }); - it('should handle when I pick a required standard', () => { - const spy = jest.spyOn(router, 'navigate'); - - component.handleSelect({ refCalculation: '1.2' } as unknown as RequiredStandard, Types.RequiredStandard); - - expect(spy).toHaveBeenCalled(); - expect(spy).toHaveBeenCalledWith(['normal/1.2'], expect.anything()); - expect(component.selectedRequiredStandard).toStrictEqual({ refCalculation: '1.2' }); - }); - it('should error when I try call it with another type', () => { - const spy = jest.spyOn(console, 'error'); - - component.handleSelect(undefined, undefined); - - expect(spy).toHaveBeenCalled(); - }); - }); + let component: RequiredStandardSelectComponent; + let fixture: ComponentFixture; + let router: Router; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [RouterTestingModule], + declarations: [RequiredStandardSelectComponent], + providers: [provideMockStore({ initialState: initialAppState })], + }).compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(RequiredStandardSelectComponent); + router = TestBed.inject(Router); + component = fixture.componentInstance; + fixture.detectChanges(); + jest.clearAllMocks(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + describe('handleSelectBasicOrNormal', () => { + it('should work for basic inspection', () => { + component.basicAndNormalRequiredStandards = { + basic: [ + 'basic' as unknown as RequiredStandardTaxonomySection, + 'basic1' as unknown as RequiredStandardTaxonomySection, + ], + normal: [ + 'normal' as unknown as RequiredStandardTaxonomySection, + 'normal1' as unknown as RequiredStandardTaxonomySection, + ], + euVehicleCategories: [EUVehicleCategory.M1], + }; + + component.handleSelectBasicOrNormal(INSPECTION_TYPE.BASIC); + + expect(component.requiredStandards).toStrictEqual(['basic', 'basic1']); + }); + it('should work for normal inspection', () => { + component.basicAndNormalRequiredStandards = { + basic: [ + 'basic' as unknown as RequiredStandardTaxonomySection, + 'basic1' as unknown as RequiredStandardTaxonomySection, + ], + normal: [ + 'normal' as unknown as RequiredStandardTaxonomySection, + 'normal1' as unknown as RequiredStandardTaxonomySection, + ], + euVehicleCategories: [EUVehicleCategory.M1], + }; + + component.handleSelectBasicOrNormal(INSPECTION_TYPE.NORMAL); + + expect(component.requiredStandards).toStrictEqual(['normal', 'normal1']); + }); + }); + + describe('handleSelect', () => { + it('should handle when I pick an inspection type', () => { + const spy = jest.spyOn(component, 'handleSelectBasicOrNormal'); + + component.handleSelect(INSPECTION_TYPE.BASIC, Types.InspectionType); + + expect(spy).toHaveBeenCalled(); + expect(component.selectedInspectionType).toBe(INSPECTION_TYPE.BASIC); + expect(component.selectedSection).toBeUndefined(); + expect(component.selectedRequiredStandard).toBeUndefined(); + }); + it('should handle when I pick a section', () => { + component.handleSelect('section' as unknown as RequiredStandardTaxonomySection, Types.Section); + + expect(component.selectedSection).toBe('section'); + expect(component.selectedRequiredStandard).toBeUndefined(); + }); + it('should handle when I pick a required standard', () => { + const spy = jest.spyOn(router, 'navigate'); + + component.handleSelect({ refCalculation: '1.2' } as unknown as RequiredStandard, Types.RequiredStandard); + + expect(spy).toHaveBeenCalled(); + expect(spy).toHaveBeenCalledWith(['normal/1.2'], expect.anything()); + expect(component.selectedRequiredStandard).toStrictEqual({ refCalculation: '1.2' }); + }); + it('should error when I try call it with another type', () => { + const spy = jest.spyOn(console, 'error'); + + component.handleSelect(undefined, undefined); + + expect(spy).toHaveBeenCalled(); + }); + }); }); enum Types { - InspectionType, - Section, - // eslint-disable-next-line @typescript-eslint/no-shadow - RequiredStandard, + InspectionType = 0, + Section = 1, + // eslint-disable-next-line @typescript-eslint/no-shadow + RequiredStandard = 2, } diff --git a/src/app/forms/components/required-standard-select/required-standard-select.component.ts b/src/app/forms/components/required-standard-select/required-standard-select.component.ts index b9aa6d2bb8..ee794eae54 100644 --- a/src/app/forms/components/required-standard-select/required-standard-select.component.ts +++ b/src/app/forms/components/required-standard-select/required-standard-select.component.ts @@ -1,9 +1,9 @@ import { Component, OnDestroy, OnInit } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; import { - DefectGETRequiredStandards, - RequiredStandard, - RequiredStandardTaxonomySection, + DefectGETRequiredStandards, + RequiredStandard, + RequiredStandardTaxonomySection, } from '@dvsa/cvs-type-definitions/types/required-standards/defects/get'; import { INSPECTION_TYPE } from '@models/test-results/test-result-required-standard.model'; import { Store } from '@ngrx/store'; @@ -12,91 +12,97 @@ import { getRequiredStandardsState } from '@store/required-standards/selectors/r import { Subject, takeUntil } from 'rxjs'; @Component({ - selector: 'app-required-standard-select', - templateUrl: './required-standard-select.component.html', - styleUrls: ['./required-standard-select.component.scss'], + selector: 'app-required-standard-select', + templateUrl: './required-standard-select.component.html', + styleUrls: ['./required-standard-select.component.scss'], }) export class RequiredStandardSelectComponent implements OnInit, OnDestroy { + requiredStandards?: RequiredStandardTaxonomySection[]; + normalAndBasic?: boolean; + isEditing = false; + selectedInspectionType?: INSPECTION_TYPE; + selectedSection?: RequiredStandardTaxonomySection; + selectedRequiredStandard?: RequiredStandard; + basicAndNormalRequiredStandards?: DefectGETRequiredStandards; - requiredStandards?: RequiredStandardTaxonomySection[]; - normalAndBasic?: boolean; - isEditing = false; - selectedInspectionType?: INSPECTION_TYPE; - selectedSection?: RequiredStandardTaxonomySection; - selectedRequiredStandard?: RequiredStandard; - basicAndNormalRequiredStandards?: DefectGETRequiredStandards; + onDestroy$ = new Subject(); - onDestroy$ = new Subject(); + constructor( + private requiredStandardsStore: Store, + private router: Router, + private route: ActivatedRoute + ) {} - constructor( - private requiredStandardsStore: Store, - private router: Router, - private route: ActivatedRoute, - ) {} + ngOnInit(): void { + this.requiredStandardsStore + .select(getRequiredStandardsState) + .pipe(takeUntil(this.onDestroy$)) + .subscribe((requiredStandards) => { + if (requiredStandards.basic.length) { + this.normalAndBasic = true; + this.requiredStandards = []; + this.basicAndNormalRequiredStandards = requiredStandards; + } else { + this.requiredStandards = requiredStandards.normal; + this.selectedInspectionType = INSPECTION_TYPE.NORMAL; + } + }); + } - ngOnInit(): void { - this.requiredStandardsStore.select(getRequiredStandardsState).pipe(takeUntil(this.onDestroy$)).subscribe((requiredStandards) => { - if (requiredStandards.basic.length) { - this.normalAndBasic = true; - this.requiredStandards = []; - this.basicAndNormalRequiredStandards = requiredStandards; - } else { - this.requiredStandards = requiredStandards.normal; - this.selectedInspectionType = INSPECTION_TYPE.NORMAL; - } - }); + ngOnDestroy(): void { + this.onDestroy$.next(true); + this.onDestroy$.complete(); + } - } + handleSelectBasicOrNormal(inspectionType: INSPECTION_TYPE): void { + this.requiredStandards = + inspectionType === INSPECTION_TYPE.BASIC + ? this.basicAndNormalRequiredStandards?.basic + : this.basicAndNormalRequiredStandards?.normal; + } - ngOnDestroy(): void { - this.onDestroy$.next(true); - this.onDestroy$.complete(); - } + handleSelect(selected?: INSPECTION_TYPE | RequiredStandardTaxonomySection | RequiredStandard, type?: Types): void { + switch (type) { + case Types.InspectionType: + this.handleSelectBasicOrNormal(selected as INSPECTION_TYPE); + this.selectedInspectionType = selected as INSPECTION_TYPE; + this.selectedSection = undefined; + this.selectedRequiredStandard = undefined; + break; + case Types.Section: + this.selectedSection = selected as RequiredStandardTaxonomySection; + this.selectedRequiredStandard = undefined; + break; + case Types.RequiredStandard: + this.selectedRequiredStandard = selected as RequiredStandard; + if (this.selectedRequiredStandard) { + void this.router.navigate( + [`${this.selectedInspectionType}/${this.selectedRequiredStandard.refCalculation}`], + { + relativeTo: this.route, + queryParamsHandling: 'merge', + } + ); + } + break; + default: + console.error('Unsupported:'); + break; + } + } - handleSelectBasicOrNormal(inspectionType: INSPECTION_TYPE): void { - this.requiredStandards = inspectionType === INSPECTION_TYPE.BASIC - ? this.basicAndNormalRequiredStandards?.basic : this.basicAndNormalRequiredStandards?.normal; - } + get types(): typeof Types { + return Types; + } - handleSelect(selected?: INSPECTION_TYPE | RequiredStandardTaxonomySection | RequiredStandard, type?: Types): void { - switch (type) { - case Types.InspectionType: - this.handleSelectBasicOrNormal(selected as INSPECTION_TYPE); - this.selectedInspectionType = selected as INSPECTION_TYPE; - this.selectedSection = undefined; - this.selectedRequiredStandard = undefined; - break; - case Types.Section: - this.selectedSection = selected as RequiredStandardTaxonomySection; - this.selectedRequiredStandard = undefined; - break; - case Types.RequiredStandard: - this.selectedRequiredStandard = selected as RequiredStandard; - if (this.selectedRequiredStandard) { - void this.router.navigate([`${this.selectedInspectionType}/${this.selectedRequiredStandard.refCalculation}`], { - relativeTo: this.route, - queryParamsHandling: 'merge', - }); - } - break; - default: - console.error('Unsupported:'); - break; - } - } - - get types(): typeof Types { - return Types; - } - - get inspectionTypes(): INSPECTION_TYPE[] { - return Object.values(INSPECTION_TYPE) as INSPECTION_TYPE[]; - } + get inspectionTypes(): INSPECTION_TYPE[] { + return Object.values(INSPECTION_TYPE) as INSPECTION_TYPE[]; + } } enum Types { - InspectionType, - Section, - // eslint-disable-next-line @typescript-eslint/no-shadow - RequiredStandard, + InspectionType = 0, + Section = 1, + // eslint-disable-next-line @typescript-eslint/no-shadow + RequiredStandard = 2, } diff --git a/src/app/forms/components/select/select.component.spec.ts b/src/app/forms/components/select/select.component.spec.ts index 2fd78b8035..5d599a2047 100644 --- a/src/app/forms/components/select/select.component.spec.ts +++ b/src/app/forms/components/select/select.component.spec.ts @@ -9,63 +9,63 @@ import { FieldErrorMessageComponent } from '../field-error-message/field-error-m import { SelectComponent } from './select.component'; @Component({ - selector: 'app-host-component', - template: `
+ selector: 'app-host-component', + template: `
`, - styles: [], + styles: [], }) class HostComponent { - @ViewChild(SelectComponent) select?: SelectComponent; + @ViewChild(SelectComponent) select?: SelectComponent; - form = new FormGroup({ - foo: new CustomFormControl({ name: 'foo', type: FormNodeTypes.CONTROL, children: [] }, null), - }); - options: FormNodeOption[] = [ - { label: 'Value 1', value: '1' }, - { label: 'Value 2', value: '2' }, - { label: 'Value 3', value: '3' }, - ]; + form = new FormGroup({ + foo: new CustomFormControl({ name: 'foo', type: FormNodeTypes.CONTROL, children: [] }, null), + }); + options: FormNodeOption[] = [ + { label: 'Value 1', value: '1' }, + { label: 'Value 2', value: '2' }, + { label: 'Value 3', value: '3' }, + ]; } describe('SelectComponent', () => { - let component: HostComponent; - let fixture: ComponentFixture; + let component: HostComponent; + let fixture: ComponentFixture; - beforeEach(async () => { - await TestBed.configureTestingModule({ - declarations: [BaseControlComponent, FieldErrorMessageComponent, HostComponent, SelectComponent], - imports: [FormsModule, ReactiveFormsModule], - }).compileComponents(); - }); + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [BaseControlComponent, FieldErrorMessageComponent, HostComponent, SelectComponent], + imports: [FormsModule, ReactiveFormsModule], + }).compileComponents(); + }); - beforeEach(() => { - fixture = TestBed.createComponent(HostComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); + beforeEach(() => { + fixture = TestBed.createComponent(HostComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); - describe('value', () => { - it('should be propagated from element to the form control', () => { - const select = fixture.debugElement.query(By.css('select')).nativeElement as HTMLSelectElement; - const foo = component.form.get('foo'); - select.selectedIndex = 1; - select.dispatchEvent(new Event('change')); + describe('value', () => { + it('should be propagated from element to the form control', () => { + const select = fixture.debugElement.query(By.css('select')).nativeElement as HTMLSelectElement; + const foo = component.form.get('foo'); + select.selectedIndex = 1; + select.dispatchEvent(new Event('change')); - expect(foo?.value).toBe('1'); - expect(foo?.value).not.toBeNull(); - }); + expect(foo?.value).toBe('1'); + expect(foo?.value).not.toBeNull(); + }); - it('should select the right option when the form value is updated', () => { - component.form.patchValue({ foo: '2' }); - fixture.detectChanges(); - const select = fixture.debugElement.query(By.css('select')); - expect(select).toBeTruthy(); - expect(select.nativeElement.value).toBe('2: 2'); - }); - }); + it('should select the right option when the form value is updated', () => { + component.form.patchValue({ foo: '2' }); + fixture.detectChanges(); + const select = fixture.debugElement.query(By.css('select')); + expect(select).toBeTruthy(); + expect(select.nativeElement.value).toBe('2: 2'); + }); + }); }); diff --git a/src/app/forms/components/select/select.component.ts b/src/app/forms/components/select/select.component.ts index 31b2809ccf..290e91da29 100644 --- a/src/app/forms/components/select/select.component.ts +++ b/src/app/forms/components/select/select.component.ts @@ -1,27 +1,25 @@ -import { - Component, EventEmitter, Input, Output, -} from '@angular/core'; +import { Component, EventEmitter, Input, Output } from '@angular/core'; import { NG_VALUE_ACCESSOR } from '@angular/forms'; import { FormNodeOption } from '@forms/services/dynamic-form.types'; import { BaseControlComponent } from '../base-control/base-control.component'; @Component({ - selector: 'app-select[options]', - templateUrl: './select.component.html', - styleUrls: ['./select.component.scss'], - providers: [ - { - provide: NG_VALUE_ACCESSOR, - useExisting: SelectComponent, - multi: true, - }, - ], + selector: 'app-select[options]', + templateUrl: './select.component.html', + styleUrls: ['./select.component.scss'], + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: SelectComponent, + multi: true, + }, + ], }) export class SelectComponent extends BaseControlComponent { - @Input() options!: Array>; - @Output() blur = new EventEmitter(); + @Input() options!: Array>; + @Output() blur = new EventEmitter(); - get style(): string { - return `govuk-select ${this.width ? `govuk-input--width-${this.width}` : ''}`; - } + get style(): string { + return `govuk-select ${this.width ? `govuk-input--width-${this.width}` : ''}`; + } } diff --git a/src/app/forms/components/select/select.stories.ts b/src/app/forms/components/select/select.stories.ts index 21fc3c0959..6f51c82cb4 100644 --- a/src/app/forms/components/select/select.stories.ts +++ b/src/app/forms/components/select/select.stories.ts @@ -1,60 +1,60 @@ import { CommonModule } from '@angular/common'; import { FormControl, FormGroup, FormsModule, ReactiveFormsModule } from '@angular/forms'; -import { Meta, moduleMetadata, Story } from '@storybook/angular'; +import { Meta, Story, moduleMetadata } from '@storybook/angular'; import { BaseControlComponent } from '../base-control/base-control.component'; import { SelectComponent } from './select.component'; export default { - title: 'Forms/Select', - component: SelectComponent, - decorators: [ - moduleMetadata({ - declarations: [SelectComponent, BaseControlComponent], - imports: [CommonModule, FormsModule, ReactiveFormsModule] - }) - ] + title: 'Forms/Select', + component: SelectComponent, + decorators: [ + moduleMetadata({ + declarations: [SelectComponent, BaseControlComponent], + imports: [CommonModule, FormsModule, ReactiveFormsModule], + }), + ], } as Meta; -const Template: Story = args => { - const { label, options, name, hint, value = null, disabled = false, validators = [] } = args; - const form = new FormGroup({ [name]: new FormControl({ value, disabled }, validators) }); - return { - component: SelectComponent, - template: `
{{ form.value | json }}
`, - props: { - form, - label, - name, - options, - hint - } - }; +const Template: Story = (args) => { + const { label, options, name, hint, value = null, disabled = false, validators = [] } = args; + const form = new FormGroup({ [name]: new FormControl({ value, disabled }, validators) }); + return { + component: SelectComponent, + template: `
{{ form.value | json }}
`, + props: { + form, + label, + name, + options, + hint, + }, + }; }; const defaultArgs = { - label: 'Colour picker', - name: 'color', - options: [ - { label: 'Red', value: 'red' }, - { label: 'Yellow', value: 'yellow', hint: 'this is the colour of the sun' }, - { label: 'Blue', value: 'blue', hint: 'this is the colour of the sea' } - ], - hint: 'These are the three primary colours' + label: 'Colour picker', + name: 'color', + options: [ + { label: 'Red', value: 'red' }, + { label: 'Yellow', value: 'yellow', hint: 'this is the colour of the sun' }, + { label: 'Blue', value: 'blue', hint: 'this is the colour of the sea' }, + ], + hint: 'These are the three primary colours', }; export const Enabled = Template.bind({}); Enabled.args = { - ...defaultArgs + ...defaultArgs, }; export const Disabled = Template.bind({}); Disabled.args = { - ...defaultArgs, - disabled: true + ...defaultArgs, + disabled: true, }; export const Initilisation = Template.bind({}); Initilisation.args = { - ...defaultArgs, - value: 'red' + ...defaultArgs, + value: 'red', }; diff --git a/src/app/forms/components/suggestive-input/suggestive-input.component.spec.ts b/src/app/forms/components/suggestive-input/suggestive-input.component.spec.ts index 8bd077eb04..ad37755123 100644 --- a/src/app/forms/components/suggestive-input/suggestive-input.component.spec.ts +++ b/src/app/forms/components/suggestive-input/suggestive-input.component.spec.ts @@ -9,70 +9,70 @@ import { FieldErrorMessageComponent } from '../field-error-message/field-error-m import { SuggestiveInputComponent } from './suggestive-input.component'; @Component({ - selector: 'app-host-component', - template: `
+ selector: 'app-host-component', + template: `
`, - styles: [], + styles: [], }) class HostComponent { - form = new FormGroup({ - foo: new CustomFormControl({ name: 'foo', type: FormNodeTypes.CONTROL, children: [] }, ''), - }); - options$: Observable = of([ - { label: 'Banana', value: 'banana' }, - { label: 'Apple', value: 'apple' }, - ]); + form = new FormGroup({ + foo: new CustomFormControl({ name: 'foo', type: FormNodeTypes.CONTROL, children: [] }, ''), + }); + options$: Observable = of([ + { label: 'Banana', value: 'banana' }, + { label: 'Apple', value: 'apple' }, + ]); } describe('SuggestiveInputComponent', () => { - let component: HostComponent; - let fixture: ComponentFixture; - let suggestiveInput: SuggestiveInputComponent; + let component: HostComponent; + let fixture: ComponentFixture; + let suggestiveInput: SuggestiveInputComponent; - beforeEach(async () => { - await TestBed.configureTestingModule({ - declarations: [HostComponent, SuggestiveInputComponent, FieldErrorMessageComponent], - imports: [FormsModule, ReactiveFormsModule], - }).compileComponents(); - }); + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [HostComponent, SuggestiveInputComponent, FieldErrorMessageComponent], + imports: [FormsModule, ReactiveFormsModule], + }).compileComponents(); + }); - beforeEach(() => { - fixture = TestBed.createComponent(HostComponent); - component = fixture.componentInstance; - suggestiveInput = fixture.debugElement.query(By.directive(SuggestiveInputComponent)).componentInstance; - fixture.detectChanges(); - }); + beforeEach(() => { + fixture = TestBed.createComponent(HostComponent); + component = fixture.componentInstance; + suggestiveInput = fixture.debugElement.query(By.directive(SuggestiveInputComponent)).componentInstance; + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); - describe('getters', () => { - it('should retutn the correct class', () => { - suggestiveInput.width = FormNodeWidth.L; - expect(suggestiveInput.style).toBe('govuk-input govuk-input--width-10'); - suggestiveInput.width = undefined; - expect(suggestiveInput.style).toBe('govuk-input'); - }); - }); + describe('getters', () => { + it('should retutn the correct class', () => { + suggestiveInput.width = FormNodeWidth.L; + expect(suggestiveInput.style).toBe('govuk-input govuk-input--width-10'); + suggestiveInput.width = undefined; + expect(suggestiveInput.style).toBe('govuk-input'); + }); + }); - describe('SuggestiveInputComponent.prototype.handleChangeForOption.name', () => { - it('should find matching option and patch value', async () => { - await suggestiveInput.handleChangeForOption('Banana'); - expect(component.form.get('foo')?.value).toBe('banana'); - }); + describe('SuggestiveInputComponent.prototype.handleChangeForOption.name', () => { + it('should find matching option and patch value', async () => { + await suggestiveInput.handleChangeForOption('Banana'); + expect(component.form.get('foo')?.value).toBe('banana'); + }); - it('should not find matching option and patch `[INVALID_OPTION]`, control should also be invalid', async () => { - await suggestiveInput.handleChangeForOption('Orange'); - const foo = component.form.get('foo'); - expect(foo?.value).toBe('[INVALID_OPTION]'); - expect(foo?.invalid).toBeTruthy(); - }); + it('should not find matching option and patch `[INVALID_OPTION]`, control should also be invalid', async () => { + await suggestiveInput.handleChangeForOption('Orange'); + const foo = component.form.get('foo'); + expect(foo?.value).toBe('[INVALID_OPTION]'); + expect(foo?.invalid).toBeTruthy(); + }); - it('should not find matching option and patch empty string', async () => { - await suggestiveInput.handleChangeForOption(''); - expect(component.form.get('foo')?.value).toBe(''); - }); - }); + it('should not find matching option and patch empty string', async () => { + await suggestiveInput.handleChangeForOption(''); + expect(component.form.get('foo')?.value).toBe(''); + }); + }); }); diff --git a/src/app/forms/components/suggestive-input/suggestive-input.component.ts b/src/app/forms/components/suggestive-input/suggestive-input.component.ts index f60e0d2e11..ee36c0ce77 100644 --- a/src/app/forms/components/suggestive-input/suggestive-input.component.ts +++ b/src/app/forms/components/suggestive-input/suggestive-input.component.ts @@ -1,84 +1,78 @@ -import { - AfterContentInit, ChangeDetectionStrategy, - Component, - Input, OnInit, -} from '@angular/core'; +import { AfterContentInit, ChangeDetectionStrategy, Component, Input, OnInit } from '@angular/core'; import { NG_VALUE_ACCESSOR } from '@angular/forms'; import { MultiOption } from '@forms/models/options.model'; import { CustomValidators } from '@forms/validators/custom-validators'; -import { - Observable, - firstValueFrom, - skipWhile, take, -} from 'rxjs'; +import { Observable, firstValueFrom, skipWhile, take } from 'rxjs'; import { BaseControlComponent } from '../base-control/base-control.component'; @Component({ - selector: 'app-suggestive-input', - templateUrl: './suggestive-input.component.html', - styleUrls: ['./suggestive-input.component.scss'], - changeDetection: ChangeDetectionStrategy.OnPush, - providers: [ - { - provide: NG_VALUE_ACCESSOR, - useExisting: SuggestiveInputComponent, - multi: true, - }, - ], + selector: 'app-suggestive-input', + templateUrl: './suggestive-input.component.html', + styleUrls: ['./suggestive-input.component.scss'], + changeDetection: ChangeDetectionStrategy.OnPush, + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: SuggestiveInputComponent, + multi: true, + }, + ], }) export class SuggestiveInputComponent extends BaseControlComponent implements AfterContentInit, OnInit { - @Input() options$!: Observable; - @Input() defaultValue = ''; + @Input() options$!: Observable; + @Input() defaultValue = ''; - field_value = ''; + field_value = ''; - ngOnInit(): void { - this.options$ - .pipe( - skipWhile((options) => !options.length), - take(1), - ) - // eslint-disable-next-line @typescript-eslint/no-misused-promises - .subscribe(async () => { - this.field_value = (await this.findOption(this.value, 'value'))?.label ?? ''; - }); - } + ngOnInit(): void { + this.options$ + .pipe( + skipWhile((options) => !options.length), + take(1) + ) + // eslint-disable-next-line @typescript-eslint/no-misused-promises + .subscribe(async () => { + this.field_value = (await this.findOption(this.value, 'value'))?.label ?? ''; + }); + } - override ngAfterContentInit() { - super.ngAfterContentInit(); - this.addValidators(); - } + override ngAfterContentInit() { + super.ngAfterContentInit(); + this.addValidators(); + } - get style(): string { - return `govuk-input${this.width ? ` govuk-input--width-${this.width}` : ''}`; - } + get style(): string { + return `govuk-input${this.width ? ` govuk-input--width-${this.width}` : ''}`; + } - async handleChangeForOption(value: string) { - const option = await this.findOption(value); + async handleChangeForOption(value: string) { + const option = await this.findOption(value); - this.field_value = option?.label ?? value; - // if option found, patch option value else if value patch `[INVALID_OPTION]` else value (empty string) - // eslint-disable-next-line no-nested-ternary - this.control?.patchValue(option ? option.value : value ? '[INVALID_OPTION]' : value); - this.cdr.markForCheck(); - } + this.field_value = option?.label ?? value; + // if option found, patch option value else if value patch `[INVALID_OPTION]` else value (empty string) + // eslint-disable-next-line no-nested-ternary + this.control?.patchValue(option ? option.value : value ? '[INVALID_OPTION]' : value); + this.cdr.markForCheck(); + } - /** - * Takes the value from the autocomplete element and looks for a matching option in the options array. - * Returns a promise of the found option or undefined if no match. - * @param {string} value - value to get option for - * @param {string} key - option search key. Defaults to `label` - * @returns `MultiOption | undefined` - */ - async findOption(val: string, key = 'label'): Promise { - return firstValueFrom(this.options$).then((options) => options.find((option) => option[key as keyof MultiOption] === val)); - } + /** + * Takes the value from the autocomplete element and looks for a matching option in the options array. + * Returns a promise of the found option or undefined if no match. + * @param {string} value - value to get option for + * @param {string} key - option search key. Defaults to `label` + * @returns `MultiOption | undefined` + */ + async findOption(val: string, key = 'label'): Promise { + return firstValueFrom(this.options$).then((options) => + options.find((option) => option[key as keyof MultiOption] === val) + ); + } - addValidators() { - this.control?.addValidators([CustomValidators.invalidOption]); - } + addValidators() { + this.control?.addValidators([CustomValidators.invalidOption]); + } - trackByFn(i: number, option: MultiOption) { - return option.value ?? i; - } + trackByFn(i: number, option: MultiOption) { + return option.value ?? i; + } } diff --git a/src/app/forms/components/switchable-input/switchable-input.component.spec.ts b/src/app/forms/components/switchable-input/switchable-input.component.spec.ts index 87a38408b1..ee1328e759 100644 --- a/src/app/forms/components/switchable-input/switchable-input.component.spec.ts +++ b/src/app/forms/components/switchable-input/switchable-input.component.spec.ts @@ -14,38 +14,38 @@ import { TextInputComponent } from '../text-input/text-input.component'; import { SwitchableInputComponent } from './switchable-input.component'; describe('SwitchableInputComponent', () => { - let component: SwitchableInputComponent; - let fixture: ComponentFixture; + let component: SwitchableInputComponent; + let fixture: ComponentFixture; - beforeEach(async () => { - await TestBed.configureTestingModule({ - declarations: [ - AutocompleteComponent, - DateComponent, - FieldErrorMessageComponent, - NumberInputComponent, - RadioGroupComponent, - ReadOnlyComponent, - SelectComponent, - SuffixDirective, - SwitchableInputComponent, - TextAreaComponent, - TextInputComponent, - ], - imports: [FormsModule, ReactiveFormsModule], - }).compileComponents(); - }); + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ + AutocompleteComponent, + DateComponent, + FieldErrorMessageComponent, + NumberInputComponent, + RadioGroupComponent, + ReadOnlyComponent, + SelectComponent, + SuffixDirective, + SwitchableInputComponent, + TextAreaComponent, + TextInputComponent, + ], + imports: [FormsModule, ReactiveFormsModule], + }).compileComponents(); + }); - beforeEach(() => { - fixture = TestBed.createComponent(SwitchableInputComponent); - component = fixture.componentInstance; - component.type = FormNodeEditTypes.TEXT; - component.form = new FormGroup({ foo: new CustomFormControl({ name: 'foo', type: FormNodeTypes.CONTROL }, '') }); - component.name = 'foo'; - fixture.detectChanges(); - }); + beforeEach(() => { + fixture = TestBed.createComponent(SwitchableInputComponent); + component = fixture.componentInstance; + component.type = FormNodeEditTypes.TEXT; + component.form = new FormGroup({ foo: new CustomFormControl({ name: 'foo', type: FormNodeTypes.CONTROL }, '') }); + component.name = 'foo'; + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); }); diff --git a/src/app/forms/components/switchable-input/switchable-input.component.ts b/src/app/forms/components/switchable-input/switchable-input.component.ts index 64a7f09dc6..921060ab53 100644 --- a/src/app/forms/components/switchable-input/switchable-input.component.ts +++ b/src/app/forms/components/switchable-input/switchable-input.component.ts @@ -5,52 +5,52 @@ import { FormNodeEditTypes, FormNodeWidth } from '@forms/services/dynamic-form.t import { Observable, of } from 'rxjs'; @Component({ - selector: 'app-switchable-input[form][type][name][isEditing]', - templateUrl: './switchable-input.component.html', + selector: 'app-switchable-input[form][type][name][isEditing]', + templateUrl: './switchable-input.component.html', }) export class SwitchableInputComponent implements OnInit { - @Input() type!: FormNodeEditTypes; - @Input() form!: FormGroup; - @Input() name!: string; - - @Input() isEditing = true; - - @Input() customId?: string; - @Input() idExtension?: number; - @Input() label?: string; - @Input() prefix?: string; - @Input() suffix?: string; - @Input() width?: FormNodeWidth; - @Input() options?: MultiOptions = []; - @Input() propOptions$?: Observable; - @Input() hint?: string; - @Input() approvalType?: string; - @Input() approvalTypeChange?: boolean | undefined = false; - - @Input() readOnlyDate?: boolean; - @Input() vehicleType?: string | null; - delimiter = { regex: '\\. (? { - return this.propOptions$ ?? of(this.options ?? []); - } - - get types(): typeof FormNodeEditTypes { - return FormNodeEditTypes; - } + @Input() type!: FormNodeEditTypes; + @Input() form!: FormGroup; + @Input() name!: string; + + @Input() isEditing = true; + + @Input() customId?: string; + @Input() idExtension?: number; + @Input() label?: string; + @Input() prefix?: string; + @Input() suffix?: string; + @Input() width?: FormNodeWidth; + @Input() options?: MultiOptions = []; + @Input() propOptions$?: Observable; + @Input() hint?: string; + @Input() approvalType?: string; + @Input() approvalTypeChange?: boolean | undefined = false; + + @Input() readOnlyDate?: boolean; + @Input() vehicleType?: string | null; + delimiter = { regex: '\\. (? { + return this.propOptions$ ?? of(this.options ?? []); + } + + get types(): typeof FormNodeEditTypes { + return FormNodeEditTypes; + } } diff --git a/src/app/forms/components/text-area/text-area.component.spec.ts b/src/app/forms/components/text-area/text-area.component.spec.ts index af1f61e753..6bca85037f 100644 --- a/src/app/forms/components/text-area/text-area.component.spec.ts +++ b/src/app/forms/components/text-area/text-area.component.spec.ts @@ -5,35 +5,35 @@ import { CustomFormControl, FormNodeTypes } from '../../services/dynamic-form.ty import { TextAreaComponent } from './text-area.component'; @Component({ - selector: 'app-host-component', - template: `
+ selector: 'app-host-component', + template: `
`, - styles: [], + styles: [], }) class HostComponent { - form = new FormGroup({ - foo: new CustomFormControl({ name: 'foo', type: FormNodeTypes.CONTROL, children: [] }, ''), - }); + form = new FormGroup({ + foo: new CustomFormControl({ name: 'foo', type: FormNodeTypes.CONTROL, children: [] }, ''), + }); } describe('TextAreaComponent', () => { - let component: HostComponent; - let fixture: ComponentFixture; + let component: HostComponent; + let fixture: ComponentFixture; - beforeEach(async () => { - await TestBed.configureTestingModule({ - declarations: [TextAreaComponent], - }).compileComponents(); - }); + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [TextAreaComponent], + }).compileComponents(); + }); - beforeEach(() => { - fixture = TestBed.createComponent(HostComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); + beforeEach(() => { + fixture = TestBed.createComponent(HostComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); }); diff --git a/src/app/forms/components/text-area/text-area.component.ts b/src/app/forms/components/text-area/text-area.component.ts index 9cec4b2e01..cd6f5ab680 100644 --- a/src/app/forms/components/text-area/text-area.component.ts +++ b/src/app/forms/components/text-area/text-area.component.ts @@ -4,18 +4,18 @@ import { ValidatorNames } from '@forms/models/validators.enum'; import { BaseControlComponent } from '../base-control/base-control.component'; @Component({ - selector: 'app-text-area', - templateUrl: './text-area.component.html', - providers: [ - { - provide: NG_VALUE_ACCESSOR, - useExisting: TextAreaComponent, - multi: true, - }, - ], + selector: 'app-text-area', + templateUrl: './text-area.component.html', + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: TextAreaComponent, + multi: true, + }, + ], }) export class TextAreaComponent extends BaseControlComponent { - get maxLength(): number | undefined { - return this.control?.meta.validators?.find((v) => v.name === ValidatorNames.MaxLength)?.args as number | undefined; - } + get maxLength(): number | undefined { + return this.control?.meta.validators?.find((v) => v.name === ValidatorNames.MaxLength)?.args as number | undefined; + } } diff --git a/src/app/forms/components/text-input/text-input.component.spec.ts b/src/app/forms/components/text-input/text-input.component.spec.ts index d11c9a3754..8294e04e9f 100644 --- a/src/app/forms/components/text-input/text-input.component.spec.ts +++ b/src/app/forms/components/text-input/text-input.component.spec.ts @@ -5,35 +5,35 @@ import { CustomFormControl, FormNodeTypes } from '../../services/dynamic-form.ty import { TextInputComponent } from './text-input.component'; @Component({ - selector: 'app-host-component', - template: `
+ selector: 'app-host-component', + template: `
`, - styles: [], + styles: [], }) class HostComponent { - form = new FormGroup({ - foo: new CustomFormControl({ name: 'foo', type: FormNodeTypes.CONTROL, children: [] }, ''), - }); + form = new FormGroup({ + foo: new CustomFormControl({ name: 'foo', type: FormNodeTypes.CONTROL, children: [] }, ''), + }); } describe('TextInputComponent', () => { - let component: HostComponent; - let fixture: ComponentFixture; + let component: HostComponent; + let fixture: ComponentFixture; - beforeEach(async () => { - await TestBed.configureTestingModule({ - declarations: [TextInputComponent], - }).compileComponents(); - }); + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [TextInputComponent], + }).compileComponents(); + }); - beforeEach(() => { - fixture = TestBed.createComponent(HostComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); + beforeEach(() => { + fixture = TestBed.createComponent(HostComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); }); diff --git a/src/app/forms/components/text-input/text-input.component.ts b/src/app/forms/components/text-input/text-input.component.ts index 9d59668f8f..5684cf3d23 100644 --- a/src/app/forms/components/text-input/text-input.component.ts +++ b/src/app/forms/components/text-input/text-input.component.ts @@ -1,29 +1,24 @@ -import { - Component, - EventEmitter, - Input, - Output, -} from '@angular/core'; +import { Component, EventEmitter, Input, Output } from '@angular/core'; import { NG_VALUE_ACCESSOR } from '@angular/forms'; import { BaseControlComponent } from '../base-control/base-control.component'; @Component({ - selector: 'app-text-input', - templateUrl: './text-input.component.html', - styleUrls: ['./text-input.component.scss'], - providers: [ - { - provide: NG_VALUE_ACCESSOR, - useExisting: TextInputComponent, - multi: true, - }, - ], + selector: 'app-text-input', + templateUrl: './text-input.component.html', + styleUrls: ['./text-input.component.scss'], + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: TextInputComponent, + multi: true, + }, + ], }) export class TextInputComponent extends BaseControlComponent { - @Input() numeric = false; - @Output() blur = new EventEmitter(); + @Input() numeric = false; + @Output() blur = new EventEmitter(); - get style(): string { - return `govuk-input ${this.width ? `govuk-input--width-${this.width}` : ''}`; - } + get style(): string { + return `govuk-input ${this.width ? `govuk-input--width-${this.width}` : ''}`; + } } diff --git a/src/app/forms/components/view-combination/view-combination.component.spec.ts b/src/app/forms/components/view-combination/view-combination.component.spec.ts index ec6f078521..9c0238efad 100644 --- a/src/app/forms/components/view-combination/view-combination.component.spec.ts +++ b/src/app/forms/components/view-combination/view-combination.component.spec.ts @@ -6,55 +6,55 @@ import { CustomFormControl, FormNode, FormNodeTypes } from '../../services/dynam import { ViewCombinationComponent } from './view-combination.component'; describe('ViewCombinationComponent', () => { - let component: ViewCombinationComponent; - let fixture: ComponentFixture; - - const formNode: FormNode = { - name: 'combination', - label: 'label', - type: FormNodeTypes.COMBINATION, - options: { - leftComponentName: 'aName', - rightComponentName: 'aName2', - separator: ' ', - }, - children: [], - }; - - const formGroup = new FormGroup({ - aName: new CustomFormControl({ name: 'aName', type: FormNodeTypes.CONTROL, children: [] }, ''), - aName2: new CustomFormControl({ name: 'aName2', type: FormNodeTypes.CONTROL, children: [] }, ''), - }); - - beforeEach(async () => { - await TestBed.configureTestingModule({ - declarations: [ViewCombinationComponent], - imports: [SharedModule], - }).compileComponents(); - }); - - beforeEach(() => { - fixture = TestBed.createComponent(ViewCombinationComponent); - component = fixture.componentInstance; - component.formNode = formNode; - component.formGroup = formGroup; - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); - - it('should find the left and right components to make up the combination', () => { - expect(component.leftComponent?.meta.name).toBe('aName'); - expect(component.rightComponent?.meta.name).toBe('aName2'); - }); - - it('should render correct values', () => { - const ddText: HTMLSpanElement = fixture.debugElement.query(By.css('span')).nativeElement; - expect(ddText.innerHTML).toBe('- -'); - formGroup.patchValue({ aName: 'Hello', aName2: 'World' }); - fixture.detectChanges(); - expect(ddText.innerHTML).toBe('Hello World'); - }); + let component: ViewCombinationComponent; + let fixture: ComponentFixture; + + const formNode: FormNode = { + name: 'combination', + label: 'label', + type: FormNodeTypes.COMBINATION, + options: { + leftComponentName: 'aName', + rightComponentName: 'aName2', + separator: ' ', + }, + children: [], + }; + + const formGroup = new FormGroup({ + aName: new CustomFormControl({ name: 'aName', type: FormNodeTypes.CONTROL, children: [] }, ''), + aName2: new CustomFormControl({ name: 'aName2', type: FormNodeTypes.CONTROL, children: [] }, ''), + }); + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ViewCombinationComponent], + imports: [SharedModule], + }).compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(ViewCombinationComponent); + component = fixture.componentInstance; + component.formNode = formNode; + component.formGroup = formGroup; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + it('should find the left and right components to make up the combination', () => { + expect(component.leftComponent?.meta.name).toBe('aName'); + expect(component.rightComponent?.meta.name).toBe('aName2'); + }); + + it('should render correct values', () => { + const ddText: HTMLSpanElement = fixture.debugElement.query(By.css('span')).nativeElement; + expect(ddText.innerHTML).toBe('- -'); + formGroup.patchValue({ aName: 'Hello', aName2: 'World' }); + fixture.detectChanges(); + expect(ddText.innerHTML).toBe('Hello World'); + }); }); diff --git a/src/app/forms/components/view-combination/view-combination.component.ts b/src/app/forms/components/view-combination/view-combination.component.ts index 51ec500382..b6cedb56e3 100644 --- a/src/app/forms/components/view-combination/view-combination.component.ts +++ b/src/app/forms/components/view-combination/view-combination.component.ts @@ -3,39 +3,39 @@ import { FormGroup, NG_VALUE_ACCESSOR } from '@angular/forms'; import { CustomFormControl, FormNode, FormNodeCombinationOptions } from '../../services/dynamic-form.types'; @Component({ - selector: '[app-view-combination]', - templateUrl: './view-combination.component.html', - providers: [ - { - provide: NG_VALUE_ACCESSOR, - useExisting: ViewCombinationComponent, - multi: true, - }, - ], + selector: '[app-view-combination]', + templateUrl: './view-combination.component.html', + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: ViewCombinationComponent, + multi: true, + }, + ], }) export class ViewCombinationComponent implements OnInit { - @Input() formNode: FormNode; - @Input() formGroup: FormGroup; + @Input() formNode: FormNode; + @Input() formGroup: FormGroup; - leftComponent?: CustomFormControl; - rightComponent?: CustomFormControl; - separator = ' '; - label?: string; + leftComponent?: CustomFormControl; + rightComponent?: CustomFormControl; + separator = ' '; + label?: string; - constructor() { - this.formNode = {}; - this.formGroup = {}; - } + constructor() { + this.formNode = {}; + this.formGroup = {}; + } - ngOnInit(): void { - const options = this.formNode.options; - this.leftComponent = this.findComponentByName(options.leftComponentName, this.formGroup); - this.rightComponent = this.findComponentByName(options.rightComponentName, this.formGroup); - this.separator = options.separator; - this.label = this.formNode.label; - } + ngOnInit(): void { + const options = this.formNode.options; + this.leftComponent = this.findComponentByName(options.leftComponentName, this.formGroup); + this.rightComponent = this.findComponentByName(options.rightComponentName, this.formGroup); + this.separator = options.separator; + this.label = this.formNode.label; + } - private findComponentByName(nodeName: string, formGroup: FormGroup): CustomFormControl { - return formGroup.get(nodeName) as CustomFormControl; - } + private findComponentByName(nodeName: string, formGroup: FormGroup): CustomFormControl { + return formGroup.get(nodeName) as CustomFormControl; + } } diff --git a/src/app/forms/components/view-list-item/view-list-item.component.spec.ts b/src/app/forms/components/view-list-item/view-list-item.component.spec.ts index e631c2eefb..f60934808b 100644 --- a/src/app/forms/components/view-list-item/view-list-item.component.spec.ts +++ b/src/app/forms/components/view-list-item/view-list-item.component.spec.ts @@ -5,35 +5,35 @@ import { CustomFormControl, FormNodeTypes } from '../../services/dynamic-form.ty import { ViewListItemComponent } from './view-list-item.component'; @Component({ - selector: 'app-host-component', - template: `
+ selector: 'app-host-component', + template: `
`, - styles: [], + styles: [], }) class HostComponent { - form = new FormGroup({ - foo: new CustomFormControl({ name: 'foo', type: FormNodeTypes.CONTROL, children: [] }, ''), - }); + form = new FormGroup({ + foo: new CustomFormControl({ name: 'foo', type: FormNodeTypes.CONTROL, children: [] }, ''), + }); } describe('ListItemOutputComponent', () => { - let component: HostComponent; - let fixture: ComponentFixture; + let component: HostComponent; + let fixture: ComponentFixture; - beforeEach(async () => { - await TestBed.configureTestingModule({ - declarations: [ViewListItemComponent], - }).compileComponents(); - }); + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ViewListItemComponent], + }).compileComponents(); + }); - beforeEach(() => { - fixture = TestBed.createComponent(HostComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); + beforeEach(() => { + fixture = TestBed.createComponent(HostComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); }); diff --git a/src/app/forms/components/view-list-item/view-list-item.component.ts b/src/app/forms/components/view-list-item/view-list-item.component.ts index 59306d15bf..bac47102d5 100644 --- a/src/app/forms/components/view-list-item/view-list-item.component.ts +++ b/src/app/forms/components/view-list-item/view-list-item.component.ts @@ -4,39 +4,37 @@ import { FormNodeViewTypes } from '../../services/dynamic-form.types'; import { BaseControlComponent } from '../base-control/base-control.component'; @Component({ - selector: '[app-view-list-item]', - templateUrl: './view-list-item.component.html', - providers: [ - { - provide: NG_VALUE_ACCESSOR, - useExisting: ViewListItemComponent, - multi: true, - }, - ], - styleUrls: ['./view-list-item.component.scss'], + selector: '[app-view-list-item]', + templateUrl: './view-list-item.component.html', + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: ViewListItemComponent, + multi: true, + }, + ], + styleUrls: ['./view-list-item.component.scss'], }) export class ViewListItemComponent extends BaseControlComponent { - customFormControlInjector?: Injector; + customFormControlInjector?: Injector; - get formNodeViewTypes(): typeof FormNodeViewTypes { - return FormNodeViewTypes; - } + get formNodeViewTypes(): typeof FormNodeViewTypes { + return FormNodeViewTypes; + } - get displayAsRow() { - return !(this.viewType === this.formNodeViewTypes.FULLWIDTH || this.viewType === this.formNodeViewTypes.CUSTOM); - } + get displayAsRow() { + return !(this.viewType === this.formNodeViewTypes.FULLWIDTH || this.viewType === this.formNodeViewTypes.CUSTOM); + } - override ngAfterContentInit(): void { - super.ngAfterContentInit(); - this.createCustomFormControlInjector(); - } + override ngAfterContentInit(): void { + super.ngAfterContentInit(); + this.createCustomFormControlInjector(); + } - createCustomFormControlInjector() { - this.customFormControlInjector = Injector.create({ - providers: [ - { provide: NgControl, useValue: { control: this.control } }, - ], - parent: this.injector, - }); - } + createCustomFormControlInjector() { + this.customFormControlInjector = Injector.create({ + providers: [{ provide: NgControl, useValue: { control: this.control } }], + parent: this.injector, + }); + } } diff --git a/src/app/forms/custom-sections/abandon-dialog/abandon-dialog.component.spec.ts b/src/app/forms/custom-sections/abandon-dialog/abandon-dialog.component.spec.ts index 81d7331e15..266da1ad8d 100644 --- a/src/app/forms/custom-sections/abandon-dialog/abandon-dialog.component.spec.ts +++ b/src/app/forms/custom-sections/abandon-dialog/abandon-dialog.component.spec.ts @@ -14,45 +14,45 @@ import { initialAppState } from '@store/index'; import { AbandonDialogComponent } from './abandon-dialog.component'; describe('AbandonDialogComponent', () => { - let component: AbandonDialogComponent; - let fixture: ComponentFixture; + let component: AbandonDialogComponent; + let fixture: ComponentFixture; - beforeEach(async () => { - await TestBed.configureTestingModule({ - declarations: [AbandonDialogComponent], - imports: [DynamicFormsModule, SharedModule, RouterTestingModule, HttpClientTestingModule], - providers: [provideMockStore({ initialState: initialAppState }), DynamicFormService], - }).compileComponents(); - }); + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [AbandonDialogComponent], + imports: [DynamicFormsModule, SharedModule, RouterTestingModule, HttpClientTestingModule], + providers: [provideMockStore({ initialState: initialAppState }), DynamicFormService], + }).compileComponents(); + }); - beforeEach(() => { - fixture = TestBed.createComponent(AbandonDialogComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); + beforeEach(() => { + fixture = TestBed.createComponent(AbandonDialogComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); - describe('template getter', () => { - it('should get the template with TIR reasons for abandoning if the testType is a TIR', () => { - const mockTestResult = { testTypes: [{ testTypeId: TEST_TYPES_GROUP5_13[0] }] } as TestResultModel; - component.testResult = mockTestResult; - const ReasonsForAbandoning = component.getTemplate().children?.[0].children?.[0].children?.[0].referenceData; - expect(ReasonsForAbandoning).toEqual(ReferenceDataResourceType.TirReasonsForAbandoning); - }); - it('should get the specialist reasons for abandoning', () => { - const mockTestResult = { testTypes: [{ testTypeId: SPECIALIST_TEST_TYPE_IDS[0] }] } as TestResultModel; - component.testResult = mockTestResult; - const ReasonsForAbandoning = component.getTemplate().children?.[0].children?.[0].children?.[0].referenceData; - expect(ReasonsForAbandoning).toEqual(ReferenceDataResourceType.SpecialistReasonsForAbandoning); - }); - it('should get the reasons for regular reasons for abandoning by default', () => { - const mockTestResult = { testTypes: [{ testTypeId: 'foobar' }] } as TestResultModel; - component.testResult = mockTestResult; - const ReasonsForAbandoning = component.getTemplate().children?.[0].children?.[0].children?.[0].referenceData; - expect(ReasonsForAbandoning).toEqual(SpecialRefData.ReasonsForAbandoning); - }); - }); + describe('template getter', () => { + it('should get the template with TIR reasons for abandoning if the testType is a TIR', () => { + const mockTestResult = { testTypes: [{ testTypeId: TEST_TYPES_GROUP5_13[0] }] } as TestResultModel; + component.testResult = mockTestResult; + const ReasonsForAbandoning = component.getTemplate().children?.[0].children?.[0].children?.[0].referenceData; + expect(ReasonsForAbandoning).toEqual(ReferenceDataResourceType.TirReasonsForAbandoning); + }); + it('should get the specialist reasons for abandoning', () => { + const mockTestResult = { testTypes: [{ testTypeId: SPECIALIST_TEST_TYPE_IDS[0] }] } as TestResultModel; + component.testResult = mockTestResult; + const ReasonsForAbandoning = component.getTemplate().children?.[0].children?.[0].children?.[0].referenceData; + expect(ReasonsForAbandoning).toEqual(ReferenceDataResourceType.SpecialistReasonsForAbandoning); + }); + it('should get the reasons for regular reasons for abandoning by default', () => { + const mockTestResult = { testTypes: [{ testTypeId: 'foobar' }] } as TestResultModel; + component.testResult = mockTestResult; + const ReasonsForAbandoning = component.getTemplate().children?.[0].children?.[0].children?.[0].referenceData; + expect(ReasonsForAbandoning).toEqual(SpecialRefData.ReasonsForAbandoning); + }); + }); }); diff --git a/src/app/forms/custom-sections/abandon-dialog/abandon-dialog.component.ts b/src/app/forms/custom-sections/abandon-dialog/abandon-dialog.component.ts index eb457dd7fe..a7d10cb5b4 100644 --- a/src/app/forms/custom-sections/abandon-dialog/abandon-dialog.component.ts +++ b/src/app/forms/custom-sections/abandon-dialog/abandon-dialog.component.ts @@ -1,6 +1,4 @@ -import { - Component, EventEmitter, Input, OnInit, Output, ViewChild, -} from '@angular/core'; +import { Component, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core'; import { TEST_TYPES_GROUP5_13 } from '@forms/models/testTypeId.enum'; import { ValidatorNames } from '@forms/models/validators.enum'; import { FormNode, FormNodeEditTypes, FormNodeTypes } from '@forms/services/dynamic-form.types'; @@ -13,73 +11,74 @@ import merge from 'lodash.merge'; import { DynamicFormGroupComponent } from '../../components/dynamic-form-group/dynamic-form-group.component'; const ABANDON_FORM = (ReasonsForAbandoning: ReferenceDataResourceType | SpecialRefData): FormNode => ({ - name: 'abandonSection', - type: FormNodeTypes.GROUP, - children: [ - { - name: 'testTypes', - label: 'Test Types', - type: FormNodeTypes.ARRAY, - children: [ - { - name: '0', // it is important here that the name of the node for an ARRAY type should be an index value - type: FormNodeTypes.GROUP, - children: [ - { - name: 'reasonForAbandoning', - type: FormNodeTypes.CONTROL, - label: 'Why was this test abandoned?', - hint: 'Select all that apply.', - editType: FormNodeEditTypes.CHECKBOXGROUP, - delimited: { regex: '\\. (?(); - template?: FormNode; - ngOnInit() { - this.template = this.getTemplate(); - } + @ViewChild(DynamicFormGroupComponent) dynamicFormGroup?: DynamicFormGroupComponent; + @Input() testResult?: TestResultModel; + @Output() newTestResult = new EventEmitter(); + template?: FormNode; + ngOnInit() { + this.template = this.getTemplate(); + } - getTemplate(): FormNode { - const testTypeId = this.testResult?.testTypes[0].testTypeId ?? ''; + getTemplate(): FormNode { + const testTypeId = this.testResult?.testTypes[0].testTypeId ?? ''; - if (TEST_TYPES_GROUP5_13.includes(testTypeId)) { - return ABANDON_FORM(ReferenceDataResourceType.TirReasonsForAbandoning); - } if (TestRecordsService.getTestTypeGroup(testTypeId)?.includes('Specialist')) { - return ABANDON_FORM(ReferenceDataResourceType.SpecialistReasonsForAbandoning); - } - return ABANDON_FORM(SpecialRefData.ReasonsForAbandoning); - } + if (TEST_TYPES_GROUP5_13.includes(testTypeId)) { + return ABANDON_FORM(ReferenceDataResourceType.TirReasonsForAbandoning); + } + if (TestRecordsService.getTestTypeGroup(testTypeId)?.includes('Specialist')) { + return ABANDON_FORM(ReferenceDataResourceType.SpecialistReasonsForAbandoning); + } + return ABANDON_FORM(SpecialRefData.ReasonsForAbandoning); + } - // eslint-disable-next-line @typescript-eslint/no-explicit-any - handleFormChange(event: any) { - const latestTest = merge(this.testResult, event); - if (latestTest && Object.keys(latestTest).length > 0) { - this.newTestResult.emit(latestTest as TestResultModel); - } - } + // eslint-disable-next-line @typescript-eslint/no-explicit-any + handleFormChange(event: any) { + const latestTest = merge(this.testResult, event); + if (latestTest && Object.keys(latestTest).length > 0) { + this.newTestResult.emit(latestTest as TestResultModel); + } + } } diff --git a/src/app/forms/custom-sections/adr-certificate-history/adr-certificate-history.component.spec.ts b/src/app/forms/custom-sections/adr-certificate-history/adr-certificate-history.component.spec.ts index 88f82c7134..58ad199b41 100644 --- a/src/app/forms/custom-sections/adr-certificate-history/adr-certificate-history.component.spec.ts +++ b/src/app/forms/custom-sections/adr-certificate-history/adr-certificate-history.component.spec.ts @@ -1,101 +1,99 @@ +import { ViewportScroller } from '@angular/common'; +import { HttpClientTestingModule } from '@angular/common/http/testing'; +import { NO_ERRORS_SCHEMA } from '@angular/core'; import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { createMockHgv } from '@mocks/hgv-record.mock'; -import { provideMockStore } from '@ngrx/store/testing'; -import { initialAppState } from '@store/index'; -import { ADRCertificateDetails } from '@dvsa/cvs-type-definitions/types/v3/tech-record/get/hgv/complete'; +import { Router } from '@angular/router'; import { RouterTestingModule } from '@angular/router/testing'; -import { HttpClientTestingModule } from '@angular/common/http/testing'; import { GlobalErrorService } from '@core/components/global-error/global-error.service'; +import { ADRCertificateDetails } from '@dvsa/cvs-type-definitions/types/v3/tech-record/get/hgv/complete'; +import { createMockHgv } from '@mocks/hgv-record.mock'; +import { provideMockStore } from '@ngrx/store/testing'; import { AdrService } from '@services/adr/adr.service'; -import { Router } from '@angular/router'; -import { NO_ERRORS_SCHEMA } from '@angular/core'; -import { ViewportScroller } from '@angular/common'; +import { initialAppState } from '@store/index'; import { AdrCertificateHistoryComponent } from './adr-certificate-history.component'; describe('TechRecordAdrCertificateHistoryComponent', () => { - let component: AdrCertificateHistoryComponent; - let fixture: ComponentFixture; - let globalErrorService: GlobalErrorService; - let adrService: AdrService; - let router: Router; - let viewportScroller: ViewportScroller; - beforeEach(async () => { - await TestBed.configureTestingModule({ - declarations: [AdrCertificateHistoryComponent], - providers: [ - provideMockStore({ initialState: initialAppState }), - ], - imports: [RouterTestingModule, HttpClientTestingModule], - schemas: [NO_ERRORS_SCHEMA], - }).compileComponents(); - }); - beforeEach(() => { - fixture = TestBed.createComponent(AdrCertificateHistoryComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - component.currentTechRecord = createMockHgv(0); - globalErrorService = TestBed.inject(GlobalErrorService); - adrService = TestBed.inject(AdrService); - router = TestBed.inject(Router); - viewportScroller = TestBed.inject(ViewportScroller); - }); - it('should create', () => { - expect(component).toBeTruthy(); - }); - describe('showTable', () => { - it('should return false if isEditing returns true', () => { - component.isEditing = true; - expect(component.showTable()).toBe(false); - }); - it('should return false if numberOfADRCertificates returns 0', () => { - component.currentTechRecord = createMockHgv(0); - component.isEditing = false; - component.currentTechRecord.techRecord_adrPassCertificateDetails = []; - expect(component.showTable()).toBe(false); - }); - it('should return true if numberOfADRCertificates returns > 0', () => { - component.currentTechRecord = createMockHgv(0); - component.isEditing = false; - component.currentTechRecord.techRecord_adrPassCertificateDetails = [ - { - createdByName: '', - certificateType: 'PASS', - generatedTimestamp: '', - certificateId: '', - } as unknown as ADRCertificateDetails, - ]; - expect(component.showTable()).toBe(true); - }); - }); + let component: AdrCertificateHistoryComponent; + let fixture: ComponentFixture; + let globalErrorService: GlobalErrorService; + let adrService: AdrService; + let router: Router; + let viewportScroller: ViewportScroller; + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [AdrCertificateHistoryComponent], + providers: [provideMockStore({ initialState: initialAppState })], + imports: [RouterTestingModule, HttpClientTestingModule], + schemas: [NO_ERRORS_SCHEMA], + }).compileComponents(); + }); + beforeEach(() => { + fixture = TestBed.createComponent(AdrCertificateHistoryComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + component.currentTechRecord = createMockHgv(0); + globalErrorService = TestBed.inject(GlobalErrorService); + adrService = TestBed.inject(AdrService); + router = TestBed.inject(Router); + viewportScroller = TestBed.inject(ViewportScroller); + }); + it('should create', () => { + expect(component).toBeTruthy(); + }); + describe('showTable', () => { + it('should return false if isEditing returns true', () => { + component.isEditing = true; + expect(component.showTable()).toBe(false); + }); + it('should return false if numberOfADRCertificates returns 0', () => { + component.currentTechRecord = createMockHgv(0); + component.isEditing = false; + component.currentTechRecord.techRecord_adrPassCertificateDetails = []; + expect(component.showTable()).toBe(false); + }); + it('should return true if numberOfADRCertificates returns > 0', () => { + component.currentTechRecord = createMockHgv(0); + component.isEditing = false; + component.currentTechRecord.techRecord_adrPassCertificateDetails = [ + { + createdByName: '', + certificateType: 'PASS', + generatedTimestamp: '', + certificateId: '', + } as unknown as ADRCertificateDetails, + ]; + expect(component.showTable()).toBe(true); + }); + }); - describe('validateADRDetailsAndNavigate', () => { - it('should navigate when carriesDangerousGoodsSpy returns true', () => { - fixture.ngZone?.run(() => { - component.currentTechRecord = createMockHgv(0); - component.currentTechRecord.techRecord_adrDetails_dangerousGoods = true; - const clearErrorsSpy = jest.spyOn(globalErrorService, 'clearErrors'); - const carriesDangerousGoodsSpy = jest.spyOn(adrService, 'carriesDangerousGoods'); - const routerSpy = jest.spyOn(router, 'navigate'); - component.validateADRDetailsAndNavigate(); - expect(clearErrorsSpy).toHaveBeenCalled(); - expect(carriesDangerousGoodsSpy).toHaveBeenCalled(); - expect(routerSpy).toHaveBeenCalled(); - }); - }); - it('should not navigate when carriesDangerousGoods returns false', () => { - component.currentTechRecord = createMockHgv(0); - component.currentTechRecord.techRecord_adrDetails_dangerousGoods = false; - const clearErrorsSpy = jest.spyOn(globalErrorService, 'clearErrors'); - const addErrorSpy = jest.spyOn(globalErrorService, 'addError'); - const carriesDangerousGoodsSpy = jest.spyOn(adrService, 'carriesDangerousGoods'); - const routerSpy = jest.spyOn(router, 'navigate'); - const viewportScrollerSpy = jest.spyOn(viewportScroller, 'scrollToPosition').mockImplementation(() => {}); - component.validateADRDetailsAndNavigate(); - expect(clearErrorsSpy).toHaveBeenCalled(); - expect(carriesDangerousGoodsSpy).toHaveBeenCalled(); - expect(viewportScrollerSpy).toHaveBeenCalled(); - expect(addErrorSpy).toHaveBeenCalled(); - expect(routerSpy).not.toHaveBeenCalled(); - }); - }); + describe('validateADRDetailsAndNavigate', () => { + it('should navigate when carriesDangerousGoodsSpy returns true', () => { + fixture.ngZone?.run(() => { + component.currentTechRecord = createMockHgv(0); + component.currentTechRecord.techRecord_adrDetails_dangerousGoods = true; + const clearErrorsSpy = jest.spyOn(globalErrorService, 'clearErrors'); + const carriesDangerousGoodsSpy = jest.spyOn(adrService, 'carriesDangerousGoods'); + const routerSpy = jest.spyOn(router, 'navigate'); + component.validateADRDetailsAndNavigate(); + expect(clearErrorsSpy).toHaveBeenCalled(); + expect(carriesDangerousGoodsSpy).toHaveBeenCalled(); + expect(routerSpy).toHaveBeenCalled(); + }); + }); + it('should not navigate when carriesDangerousGoods returns false', () => { + component.currentTechRecord = createMockHgv(0); + component.currentTechRecord.techRecord_adrDetails_dangerousGoods = false; + const clearErrorsSpy = jest.spyOn(globalErrorService, 'clearErrors'); + const addErrorSpy = jest.spyOn(globalErrorService, 'addError'); + const carriesDangerousGoodsSpy = jest.spyOn(adrService, 'carriesDangerousGoods'); + const routerSpy = jest.spyOn(router, 'navigate'); + const viewportScrollerSpy = jest.spyOn(viewportScroller, 'scrollToPosition').mockImplementation(() => {}); + component.validateADRDetailsAndNavigate(); + expect(clearErrorsSpy).toHaveBeenCalled(); + expect(carriesDangerousGoodsSpy).toHaveBeenCalled(); + expect(viewportScrollerSpy).toHaveBeenCalled(); + expect(addErrorSpy).toHaveBeenCalled(); + expect(routerSpy).not.toHaveBeenCalled(); + }); + }); }); diff --git a/src/app/forms/custom-sections/adr-certificate-history/adr-certificate-history.component.ts b/src/app/forms/custom-sections/adr-certificate-history/adr-certificate-history.component.ts index 1970d0eb96..c422c8b1d3 100644 --- a/src/app/forms/custom-sections/adr-certificate-history/adr-certificate-history.component.ts +++ b/src/app/forms/custom-sections/adr-certificate-history/adr-certificate-history.component.ts @@ -1,116 +1,114 @@ -import { - Component, inject, Input, -} from '@angular/core'; -import { - TechRecordType, -} from '@dvsa/cvs-type-definitions/types/v3/tech-record/tech-record-vehicle-type'; -import { ADRCertificateDetails } from '@dvsa/cvs-type-definitions/types/v3/tech-record/get/hgv/complete'; -import { - map, Observable, Subject, takeUntil, -} from 'rxjs'; -import { RouterService } from '@services/router/router.service'; -import { FeatureToggleService } from '@services/feature-toggle-service/feature-toggle-service'; -import { GlobalErrorService } from '@core/components/global-error/global-error.service'; -import { AdrService } from '@services/adr/adr.service'; import { ViewportScroller } from '@angular/common'; +import { Component, Input, inject } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; -import { Roles } from '@models/roles.enum'; +import { GlobalErrorService } from '@core/components/global-error/global-error.service'; +import { ADRCertificateDetails } from '@dvsa/cvs-type-definitions/types/v3/tech-record/get/hgv/complete'; +import { TechRecordType } from '@dvsa/cvs-type-definitions/types/v3/tech-record/tech-record-vehicle-type'; import { CustomFormControlComponent } from '@forms/custom-sections/custom-form-control/custom-form-control.component'; +import { Roles } from '@models/roles.enum'; +import { AdrService } from '@services/adr/adr.service'; +import { FeatureToggleService } from '@services/feature-toggle-service/feature-toggle-service'; +import { RouterService } from '@services/router/router.service'; import { cloneDeep } from 'lodash'; +import { Observable, Subject, map, takeUntil } from 'rxjs'; @Component({ - selector: 'app-adr-certificate-history', - templateUrl: './adr-certificate-history.html', - styleUrls: ['./adr-certificate-history.scss'], + selector: 'app-adr-certificate-history', + templateUrl: './adr-certificate-history.html', + styleUrls: ['./adr-certificate-history.scss'], }) export class AdrCertificateHistoryComponent extends CustomFormControlComponent { - @Input() currentTechRecord?: TechRecordType<'hgv' | 'lgv' | 'trl'>; - isEditing = false; - private destroy$ = new Subject(); - routerService = inject(RouterService); - featureToggleService = inject(FeatureToggleService); - globalErrorService = inject(GlobalErrorService); - adrService = inject(AdrService); - viewportScroller = inject(ViewportScroller); - router = inject(Router); - route = inject(ActivatedRoute); - pageStart?: number; - pageEnd?: number; + @Input() currentTechRecord?: TechRecordType<'hgv' | 'lgv' | 'trl'>; + isEditing = false; + private destroy$ = new Subject(); + routerService = inject(RouterService); + featureToggleService = inject(FeatureToggleService); + globalErrorService = inject(GlobalErrorService); + adrService = inject(AdrService); + viewportScroller = inject(ViewportScroller); + router = inject(Router); + route = inject(ActivatedRoute); + pageStart?: number; + pageEnd?: number; - ngOnInit() { - this.isEditing$.pipe(takeUntil(this.destroy$)).subscribe((editing) => { - this.isEditing = editing; - }); - } + ngOnInit() { + this.isEditing$.pipe(takeUntil(this.destroy$)).subscribe((editing) => { + this.isEditing = editing; + }); + } - get roles(): typeof Roles { - return Roles; - } + get roles(): typeof Roles { + return Roles; + } - get isEditing$(): Observable { - return this.routerService.getRouteDataProperty$('isEditing').pipe(map((isEditing) => !!isEditing)); - } + get isEditing$(): Observable { + return this.routerService.getRouteDataProperty$('isEditing').pipe(map((isEditing) => !!isEditing)); + } - get sortedCertificates(): ADRCertificateDetails[] | undefined { - return cloneDeep(this.currentTechRecord?.techRecord_adrPassCertificateDetails)?.sort((a, b) => - a.generatedTimestamp && b.generatedTimestamp ? new Date(b.generatedTimestamp).getTime() - new Date(a.generatedTimestamp).getTime() : 0); - } + get sortedCertificates(): ADRCertificateDetails[] | undefined { + return cloneDeep(this.currentTechRecord?.techRecord_adrPassCertificateDetails)?.sort((a, b) => + a.generatedTimestamp && b.generatedTimestamp + ? new Date(b.generatedTimestamp).getTime() - new Date(a.generatedTimestamp).getTime() + : 0 + ); + } - get adrCertificateHistory(): ADRCertificateDetails[] { - return this.sortedCertificates?.slice(this.pageStart, this.pageEnd) || []; - } + get adrCertificateHistory(): ADRCertificateDetails[] { + return this.sortedCertificates?.slice(this.pageStart, this.pageEnd) || []; + } - trackByFn(i: number, tr: ADRCertificateDetails) { - return tr.generatedTimestamp; - } + trackByFn(i: number, tr: ADRCertificateDetails) { + return tr.generatedTimestamp; + } - get numberOfADRCertificates(): number { - return this.sortedCertificates?.length || 0; - } + get numberOfADRCertificates(): number { + return this.sortedCertificates?.length || 0; + } - getFileName(certificate: ADRCertificateDetails) { - return certificate.certificateId; - } - documentParams(certificate: ADRCertificateDetails): Map { - return new Map([['fileName', this.getFileName(certificate)]]); - } + getFileName(certificate: ADRCertificateDetails) { + return certificate.certificateId; + } + documentParams(certificate: ADRCertificateDetails): Map { + return new Map([['fileName', this.getFileName(certificate)]]); + } - get isArchived(): boolean { - return this.currentTechRecord?.techRecord_statusCode === 'archived'; - } + get isArchived(): boolean { + return this.currentTechRecord?.techRecord_statusCode === 'archived'; + } - showTable(): boolean { - return !this.isEditing && this.numberOfADRCertificates > 0; - } + showTable(): boolean { + return !this.isEditing && this.numberOfADRCertificates > 0; + } - get reasonForNoRecords(): string { - if (this.isEditing) { - return 'This section is not available when amending or creating a technical record.'; - } - if (this.numberOfADRCertificates === 0) { - return 'No ADR certificates found.'; - } - return ''; - } + get reasonForNoRecords(): string { + if (this.isEditing) { + return 'This section is not available when amending or creating a technical record.'; + } + if (this.numberOfADRCertificates === 0) { + return 'No ADR certificates found.'; + } + return ''; + } - validateADRDetailsAndNavigate(): void { - this.globalErrorService.clearErrors(); - if (this.currentTechRecord) { - if (!this.adrService.carriesDangerousGoods(this.currentTechRecord)) { - this.viewportScroller.scrollToPosition([0, 0]); - this.globalErrorService.addError( - { error: 'This vehicle is not able to carry dangerous goods, add ADR details to the technical record to generate a certificate.' }, - ); - return; - } - } + validateADRDetailsAndNavigate(): void { + this.globalErrorService.clearErrors(); + if (this.currentTechRecord) { + if (!this.adrService.carriesDangerousGoods(this.currentTechRecord)) { + this.viewportScroller.scrollToPosition([0, 0]); + this.globalErrorService.addError({ + error: + 'This vehicle is not able to carry dangerous goods, add ADR details to the technical record to generate a certificate.', + }); + return; + } + } - void this.router.navigate(['adr-certificate'], { relativeTo: this.route }); - } + void this.router.navigate(['adr-certificate'], { relativeTo: this.route }); + } - handlePaginationChange({ start, end }: { start: number; end: number }) { - this.pageStart = start; - this.pageEnd = end; - this.cdr.detectChanges(); - } + handlePaginationChange({ start, end }: { start: number; end: number }) { + this.pageStart = start; + this.pageEnd = end; + this.cdr.detectChanges(); + } } diff --git a/src/app/forms/custom-sections/adr-examiner-notes-history-edit/adr-examiner-notes-history.component-edit.spec.ts b/src/app/forms/custom-sections/adr-examiner-notes-history-edit/adr-examiner-notes-history.component-edit.spec.ts index 76ff27c32c..ae38f2bd99 100644 --- a/src/app/forms/custom-sections/adr-examiner-notes-history-edit/adr-examiner-notes-history.component-edit.spec.ts +++ b/src/app/forms/custom-sections/adr-examiner-notes-history-edit/adr-examiner-notes-history.component-edit.spec.ts @@ -1,110 +1,107 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { ActivatedRoute, Router } from '@angular/router'; +import { RouterTestingModule } from '@angular/router/testing'; import { TechRecordType } from '@dvsa/cvs-type-definitions/types/v3/tech-record/tech-record-vehicle-type'; -import { - AdrExaminerNotesHistoryEditComponent, -} from '@forms/custom-sections/adr-examiner-notes-history-edit/adr-examiner-notes-history.component-edit'; +import { AdrExaminerNotesHistoryEditComponent } from '@forms/custom-sections/adr-examiner-notes-history-edit/adr-examiner-notes-history.component-edit'; import { DynamicFormsModule } from '@forms/dynamic-forms.module'; import { mockVehicleTechnicalRecord } from '@mocks/mock-vehicle-technical-record.mock'; import { VehicleTypes } from '@models/vehicle-tech-record.model'; -import { TechnicalRecordService } from '@services/technical-record/technical-record.service'; import { provideMockStore } from '@ngrx/store/testing'; +import { TechnicalRecordService } from '@services/technical-record/technical-record.service'; import { initialAppState } from '@store/index'; -import { ActivatedRoute, Router } from '@angular/router'; import { of } from 'rxjs'; -import { RouterTestingModule } from '@angular/router/testing'; const mockTechRecordService = { - techRecord$: jest.fn(), + techRecord$: jest.fn(), }; describe('AdrExaminerNotesHistoryEditComponent', () => { - let component: AdrExaminerNotesHistoryEditComponent; - let fixture: ComponentFixture; - let router: Router; - - const MOCK_HGV = mockVehicleTechnicalRecord(VehicleTypes.HGV) as TechRecordType<'hgv'>; + let component: AdrExaminerNotesHistoryEditComponent; + let fixture: ComponentFixture; + let router: Router; - beforeEach(async () => { - await TestBed.configureTestingModule({ - declarations: [AdrExaminerNotesHistoryEditComponent], - imports: [DynamicFormsModule, FormsModule, ReactiveFormsModule, RouterTestingModule], - providers: [ - { provide: TechnicalRecordService, useValue: mockTechRecordService }, - provideMockStore({ initialState: initialAppState }), - { provide: ActivatedRoute, useValue: { params: of([{ id: 1 }]) } }, - ], - }).compileComponents(); - fixture = TestBed.createComponent(AdrExaminerNotesHistoryEditComponent); - component = fixture.componentInstance; - router = TestBed.inject(Router); - }); - describe('ngOnDestroy', () => { - it('should call destroy$.next and destroy$.complete', () => { - const nextSpy = jest.spyOn(component.destroy$, 'next'); - const completeSpy = jest.spyOn(component.destroy$, 'complete'); - component.ngOnDestroy(); - expect(nextSpy).toHaveBeenCalled(); - expect(completeSpy).toHaveBeenCalled(); - }); - }); + const MOCK_HGV = mockVehicleTechnicalRecord(VehicleTypes.HGV) as TechRecordType<'hgv'>; - describe('getAdditionalExaminerNotes', () => { - it('should return an empty array if additionalExaminerNotes is empty', () => { - component.currentTechRecord = mockVehicleTechnicalRecord(VehicleTypes.HGV) as TechRecordType<'hgv'>; - component.currentTechRecord.techRecord_adrDetails_additionalExaminerNotes = []; - const notes = component.getAdditionalExaminerNotes(); - expect(notes).toEqual([]); - }); - it('should return a populated array if additionalExaminerNotes is not empty', () => { + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [AdrExaminerNotesHistoryEditComponent], + imports: [DynamicFormsModule, FormsModule, ReactiveFormsModule, RouterTestingModule], + providers: [ + { provide: TechnicalRecordService, useValue: mockTechRecordService }, + provideMockStore({ initialState: initialAppState }), + { provide: ActivatedRoute, useValue: { params: of([{ id: 1 }]) } }, + ], + }).compileComponents(); + fixture = TestBed.createComponent(AdrExaminerNotesHistoryEditComponent); + component = fixture.componentInstance; + router = TestBed.inject(Router); + }); + describe('ngOnDestroy', () => { + it('should call destroy$.next and destroy$.complete', () => { + const nextSpy = jest.spyOn(component.destroy$, 'next'); + const completeSpy = jest.spyOn(component.destroy$, 'complete'); + component.ngOnDestroy(); + expect(nextSpy).toHaveBeenCalled(); + expect(completeSpy).toHaveBeenCalled(); + }); + }); - component.currentTechRecord = mockVehicleTechnicalRecord(VehicleTypes.HGV) as TechRecordType<'hgv'>; - const testNote = { - note: 'testNote', - createdAtDate: new Date().toISOString(), - lastUpdatedBy: 'Someone Somewhere', - }; - component.currentTechRecord.techRecord_adrDetails_additionalExaminerNotes = [testNote]; - const notes = component.getAdditionalExaminerNotes(); - expect(notes).toEqual([testNote]); - }); - }); - describe('getEditAdditionalExaminerNotePage', () => { - it('should navigate you to the EditAdditionalExaminerNotePage', () => { - const routerSpy = jest.spyOn(router, 'navigate'); - component.getEditAdditionalExaminerNotePage(1); - expect(routerSpy).toHaveBeenCalled(); - }); - }); + describe('getAdditionalExaminerNotes', () => { + it('should return an empty array if additionalExaminerNotes is empty', () => { + component.currentTechRecord = mockVehicleTechnicalRecord(VehicleTypes.HGV) as TechRecordType<'hgv'>; + component.currentTechRecord.techRecord_adrDetails_additionalExaminerNotes = []; + const notes = component.getAdditionalExaminerNotes(); + expect(notes).toEqual([]); + }); + it('should return a populated array if additionalExaminerNotes is not empty', () => { + component.currentTechRecord = mockVehicleTechnicalRecord(VehicleTypes.HGV) as TechRecordType<'hgv'>; + const testNote = { + note: 'testNote', + createdAtDate: new Date().toISOString(), + lastUpdatedBy: 'Someone Somewhere', + }; + component.currentTechRecord.techRecord_adrDetails_additionalExaminerNotes = [testNote]; + const notes = component.getAdditionalExaminerNotes(); + expect(notes).toEqual([testNote]); + }); + }); + describe('getEditAdditionalExaminerNotePage', () => { + it('should navigate you to the EditAdditionalExaminerNotePage', () => { + const routerSpy = jest.spyOn(router, 'navigate'); + component.getEditAdditionalExaminerNotePage(1); + expect(routerSpy).toHaveBeenCalled(); + }); + }); - describe('handlePaginationChange', () => { - it('should set the start and end pages', () => { - component.handlePaginationChange({ start: 0, end: 3 }); + describe('handlePaginationChange', () => { + it('should set the start and end pages', () => { + component.handlePaginationChange({ start: 0, end: 3 }); - expect(component.pageStart).toBe(0); - expect(component.pageEnd).toBe(3); - }); - }); + expect(component.pageStart).toBe(0); + expect(component.pageEnd).toBe(3); + }); + }); - describe('currentAdrNotesPage', () => { - it('should return a sliced array of adr notes depending on the page the user is on', () => { - component.currentTechRecord = { ...MOCK_HGV }; - component.pageStart = 1; - component.pageEnd = 2; - component.currentTechRecord.techRecord_adrDetails_additionalExaminerNotes = [ - { createdAtDate: 'test1', lastUpdatedBy: 'test1', note: 'test note 1' }, - { createdAtDate: 'test2', lastUpdatedBy: 'test2', note: 'test note 2' }, - ]; - expect(component.currentAdrNotesPage).toEqual([ - { createdAtDate: 'test2', lastUpdatedBy: 'test2', note: 'test note 2' }, - ]); - }); + describe('currentAdrNotesPage', () => { + it('should return a sliced array of adr notes depending on the page the user is on', () => { + component.currentTechRecord = { ...MOCK_HGV }; + component.pageStart = 1; + component.pageEnd = 2; + component.currentTechRecord.techRecord_adrDetails_additionalExaminerNotes = [ + { createdAtDate: 'test1', lastUpdatedBy: 'test1', note: 'test note 1' }, + { createdAtDate: 'test2', lastUpdatedBy: 'test2', note: 'test note 2' }, + ]; + expect(component.currentAdrNotesPage).toEqual([ + { createdAtDate: 'test2', lastUpdatedBy: 'test2', note: 'test note 2' }, + ]); + }); - it('should return an empty array if the adr examiner notes is undefined', () => { - component.currentTechRecord = { ...MOCK_HGV }; - component.pageStart = 2; - component.pageEnd = 3; - component.currentTechRecord.techRecord_adrDetails_additionalExaminerNotes = undefined; - expect(component.currentAdrNotesPage).toEqual([]); - }); - }); + it('should return an empty array if the adr examiner notes is undefined', () => { + component.currentTechRecord = { ...MOCK_HGV }; + component.pageStart = 2; + component.pageEnd = 3; + component.currentTechRecord.techRecord_adrDetails_additionalExaminerNotes = undefined; + expect(component.currentAdrNotesPage).toEqual([]); + }); + }); }); diff --git a/src/app/forms/custom-sections/adr-examiner-notes-history-edit/adr-examiner-notes-history.component-edit.ts b/src/app/forms/custom-sections/adr-examiner-notes-history-edit/adr-examiner-notes-history.component-edit.ts index 08e212cf4e..b65f753556 100644 --- a/src/app/forms/custom-sections/adr-examiner-notes-history-edit/adr-examiner-notes-history.component-edit.ts +++ b/src/app/forms/custom-sections/adr-examiner-notes-history-edit/adr-examiner-notes-history.component-edit.ts @@ -1,84 +1,88 @@ import { KeyValue, ViewportScroller } from '@angular/common'; -import { - AfterContentInit, - Component, inject, OnDestroy, OnInit, -} from '@angular/core'; +import { AfterContentInit, Component, OnDestroy, OnInit, inject } from '@angular/core'; import { FormArray, NgControl } from '@angular/forms'; +import { ActivatedRoute, Router } from '@angular/router'; +import { AdditionalExaminerNotes } from '@dvsa/cvs-type-definitions/types/v3/tech-record/get/hgv/complete'; import { TechRecordType } from '@dvsa/cvs-type-definitions/types/v3/tech-record/tech-record-vehicle-type'; import { BaseControlComponent } from '@forms/components/base-control/base-control.component'; import { CustomControl, CustomFormControl } from '@forms/services/dynamic-form.types'; +import { ReasonForEditing } from '@models/vehicle-tech-record.model'; +import { Store } from '@ngrx/store'; import { TechnicalRecordService } from '@services/technical-record/technical-record.service'; -import { ReplaySubject, takeUntil } from 'rxjs'; import { updateScrollPosition } from '@store/technical-records'; import { TechnicalRecordServiceState } from '@store/technical-records/reducers/technical-record-service.reducer'; -import { Store } from '@ngrx/store'; -import { ActivatedRoute, Router } from '@angular/router'; -import { ReasonForEditing } from '@models/vehicle-tech-record.model'; -import { AdditionalExaminerNotes } from '@dvsa/cvs-type-definitions/types/v3/tech-record/get/hgv/complete'; +import { ReplaySubject, takeUntil } from 'rxjs'; @Component({ - selector: 'app-adr-examiner-notes-history', - templateUrl: './adr-examiner-notes-history-edit.component.html', - styleUrls: ['adr-examiner-notes-history.component-edit.scss'], + selector: 'app-adr-examiner-notes-history', + templateUrl: './adr-examiner-notes-history-edit.component.html', + styleUrls: ['adr-examiner-notes-history.component-edit.scss'], }) -export class AdrExaminerNotesHistoryEditComponent extends BaseControlComponent implements OnInit, OnDestroy, AfterContentInit { - destroy$ = new ReplaySubject(1); - formArray = new FormArray([]); - currentTechRecord?: TechRecordType<'hgv' | 'lgv' | 'trl'> = undefined; - technicalRecordService = inject(TechnicalRecordService); - store = inject(Store); - viewportScroller = inject(ViewportScroller); - router = inject(Router); - route = inject(ActivatedRoute); - editingReason?: ReasonForEditing; - pageStart?: number; - pageEnd?: number; +export class AdrExaminerNotesHistoryEditComponent + extends BaseControlComponent + implements OnInit, OnDestroy, AfterContentInit +{ + destroy$ = new ReplaySubject(1); + formArray = new FormArray([]); + currentTechRecord?: TechRecordType<'hgv' | 'lgv' | 'trl'> = undefined; + technicalRecordService = inject(TechnicalRecordService); + store = inject(Store); + viewportScroller = inject(ViewportScroller); + router = inject(Router); + route = inject(ActivatedRoute); + editingReason?: ReasonForEditing; + pageStart?: number; + pageEnd?: number; - ngOnInit(): void { - this.formArray.valueChanges.pipe(takeUntil(this.destroy$)).subscribe((changes) => { - this.control?.patchValue(changes, { emitModelToViewChange: true }); - }); - this.technicalRecordService.techRecord$.pipe(takeUntil(this.destroy$)).subscribe((currentTechRecord) => { - this.currentTechRecord = currentTechRecord as TechRecordType<'hgv' | 'lgv' | 'trl'>; - }); - this.editingReason = this.route.snapshot.data['reason']; - } + ngOnInit(): void { + this.formArray.valueChanges.pipe(takeUntil(this.destroy$)).subscribe((changes) => { + this.control?.patchValue(changes, { emitModelToViewChange: true }); + }); + this.technicalRecordService.techRecord$.pipe(takeUntil(this.destroy$)).subscribe((currentTechRecord) => { + this.currentTechRecord = currentTechRecord as TechRecordType<'hgv' | 'lgv' | 'trl'>; + }); + this.editingReason = this.route.snapshot.data['reason']; + } - override ngAfterContentInit(): void { - const injectedControl = this.injector.get(NgControl, null); - if (injectedControl) { - const ngControl = injectedControl.control as unknown as KeyValue; - if (ngControl.value) { - this.name = ngControl.key; - this.control = ngControl.value; - } - } - } + override ngAfterContentInit(): void { + const injectedControl = this.injector.get(NgControl, null); + if (injectedControl) { + const ngControl = injectedControl.control as unknown as KeyValue; + if (ngControl.value) { + this.name = ngControl.key; + this.control = ngControl.value; + } + } + } - handlePaginationChange({ start, end }: { start: number; end: number }): void { - this.pageStart = start; - this.pageEnd = end; - this.cdr.detectChanges(); - } + handlePaginationChange({ start, end }: { start: number; end: number }): void { + this.pageStart = start; + this.pageEnd = end; + this.cdr.detectChanges(); + } - getAdditionalExaminerNotes(): AdditionalExaminerNotes[] { - return this.currentTechRecord?.techRecord_adrDetails_additionalExaminerNotes ?? []; - } + getAdditionalExaminerNotes(): AdditionalExaminerNotes[] { + return (this.currentTechRecord?.techRecord_adrDetails_additionalExaminerNotes ?? []).sort( + (a, b) => +new Date(b.createdAtDate ?? '') - +new Date(a.createdAtDate ?? '') + ); + } - get currentAdrNotesPage(): AdditionalExaminerNotes[] { - return this.currentTechRecord?.techRecord_adrDetails_additionalExaminerNotes?.slice(this.pageStart, this.pageEnd) ?? []; - } + get currentAdrNotesPage(): AdditionalExaminerNotes[] { + return ( + this.currentTechRecord?.techRecord_adrDetails_additionalExaminerNotes?.slice(this.pageStart, this.pageEnd) ?? [] + ); + } - getEditAdditionalExaminerNotePage(examinerNoteIndex: number) { - const route = `../${this.editingReason}/edit-additional-examiner-note/${examinerNoteIndex}`; + getEditAdditionalExaminerNotePage(examinerNoteIndex: number) { + const route = `../${this.editingReason}/edit-additional-examiner-note/${examinerNoteIndex}`; - this.store.dispatch(updateScrollPosition({ position: this.viewportScroller.getScrollPosition() })); + this.store.dispatch(updateScrollPosition({ position: this.viewportScroller.getScrollPosition() })); - void this.router.navigate([route], { relativeTo: this.route, state: this.currentTechRecord }); - } + void this.router.navigate([route], { relativeTo: this.route, state: this.currentTechRecord }); + } - ngOnDestroy(): void { - this.destroy$.next(true); - this.destroy$.complete(); - } + ngOnDestroy(): void { + this.destroy$.next(true); + this.destroy$.complete(); + } } diff --git a/src/app/forms/custom-sections/adr-examiner-notes-history-view/adr-examiner-notes-history-view.component.html b/src/app/forms/custom-sections/adr-examiner-notes-history-view/adr-examiner-notes-history-view.component.html index 52d754749e..8dd11abb10 100644 --- a/src/app/forms/custom-sections/adr-examiner-notes-history-view/adr-examiner-notes-history-view.component.html +++ b/src/app/forms/custom-sections/adr-examiner-notes-history-view/adr-examiner-notes-history-view.component.html @@ -1,6 +1,9 @@ - +
+ @@ -20,7 +23,7 @@ diff --git a/src/app/forms/custom-sections/adr-examiner-notes-history-view/adr-examiner-notes-history-view.component.spec.ts b/src/app/forms/custom-sections/adr-examiner-notes-history-view/adr-examiner-notes-history-view.component.spec.ts index cac1bda6d0..fb57508ff5 100644 --- a/src/app/forms/custom-sections/adr-examiner-notes-history-view/adr-examiner-notes-history-view.component.spec.ts +++ b/src/app/forms/custom-sections/adr-examiner-notes-history-view/adr-examiner-notes-history-view.component.spec.ts @@ -1,114 +1,116 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { NG_VALUE_ACCESSOR, NgControl } from '@angular/forms'; +import { TechRecordType } from '@dvsa/cvs-type-definitions/types/v3/tech-record/tech-record-vehicle-type'; import { CustomFormControl, FormNodeTypes } from '@forms/services/dynamic-form.types'; -import { provideMockStore } from '@ngrx/store/testing'; -import { State, initialAppState } from '@store/index'; -import { TechnicalRecordService } from '@services/technical-record/technical-record.service'; import { mockVehicleTechnicalRecord } from '@mocks/mock-vehicle-technical-record.mock'; import { VehicleTypes } from '@models/vehicle-tech-record.model'; -import { TechRecordType } from '@dvsa/cvs-type-definitions/types/v3/tech-record/tech-record-vehicle-type'; -import { of } from 'rxjs'; +import { provideMockStore } from '@ngrx/store/testing'; import { RouterService } from '@services/router/router.service'; +import { TechnicalRecordService } from '@services/technical-record/technical-record.service'; +import { State, initialAppState } from '@store/index'; +import { of } from 'rxjs'; import { AdrExaminerNotesHistoryViewComponent } from './adr-examiner-notes-history-view.component'; describe('AdrExaminerNotesHistoryViewComponent', () => { - let component: AdrExaminerNotesHistoryViewComponent; - let fixture: ComponentFixture; + let component: AdrExaminerNotesHistoryViewComponent; + let fixture: ComponentFixture; - const MOCK_HGV = mockVehicleTechnicalRecord(VehicleTypes.HGV) as TechRecordType<'hgv'>; - const mockTechRecordService = { - techRecord$: of({ ...MOCK_HGV }), - }; - const mockRouterService = {}; + const MOCK_HGV = mockVehicleTechnicalRecord(VehicleTypes.HGV) as TechRecordType<'hgv'>; + const mockTechRecordService = { + techRecord$: of({ ...MOCK_HGV }), + }; + const mockRouterService = {}; - const control = new CustomFormControl({ - name: 'techRecord_adrDetails_additionalExaminerNotes', - type: FormNodeTypes.CONTROL, - }); + const control = new CustomFormControl({ + name: 'techRecord_adrDetails_additionalExaminerNotes', + type: FormNodeTypes.CONTROL, + label: 'Additional Examiner Notes History', + }); - beforeEach(async () => { - await TestBed.configureTestingModule({ - declarations: [AdrExaminerNotesHistoryViewComponent], - providers: [ - provideMockStore({ initialState: initialAppState }), - { provide: TechnicalRecordService, useValue: mockTechRecordService }, - { provide: RouterService, useValue: mockRouterService }, - { provide: NG_VALUE_ACCESSOR, useExisting: AdrExaminerNotesHistoryViewComponent, multi: true }, - { - provide: NgControl, - useValue: { - control: { key: control.meta.name, value: control }, - }, - }, - ], - }) - .compileComponents(); + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [AdrExaminerNotesHistoryViewComponent], + providers: [ + provideMockStore({ initialState: initialAppState }), + { provide: TechnicalRecordService, useValue: mockTechRecordService }, + { provide: RouterService, useValue: mockRouterService }, + { provide: NG_VALUE_ACCESSOR, useExisting: AdrExaminerNotesHistoryViewComponent, multi: true }, + { + provide: NgControl, + useValue: { + control: { key: control.meta.name, value: control }, + }, + }, + ], + }).compileComponents(); - fixture = TestBed.createComponent(AdrExaminerNotesHistoryViewComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); + fixture = TestBed.createComponent(AdrExaminerNotesHistoryViewComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); - describe('ngOnInit', () => { - it('should set the currentTechRecord when ngOnInit is fired', () => { - component.currentTechRecord = undefined; + describe('ngOnInit', () => { + it('should set the currentTechRecord when ngOnInit is fired', () => { + component.currentTechRecord = undefined; - component.ngOnInit(); - expect(component.currentTechRecord).toEqual(MOCK_HGV); - }); - }); + component.ngOnInit(); + expect(component.currentTechRecord).toEqual(MOCK_HGV); + }); + }); - describe('adrNotes', () => { - it('should return an array of the technical records adr examiner notes', () => { - component.currentTechRecord = { ...MOCK_HGV }; - component.currentTechRecord.techRecord_adrDetails_additionalExaminerNotes = [ - { createdAtDate: 'test', lastUpdatedBy: 'test', note: 'test note' }, - ]; - expect(component.adrNotes).toEqual([{ createdAtDate: 'test', lastUpdatedBy: 'test', note: 'test note' }]); - }); + describe('adrNotes', () => { + it('should return an array of the technical records adr examiner notes', () => { + component.currentTechRecord = { ...MOCK_HGV }; + component.currentTechRecord.techRecord_adrDetails_additionalExaminerNotes = [ + { createdAtDate: 'test', lastUpdatedBy: 'test', note: 'test note' }, + ]; + expect(component.getAdditionalExaminerNotes()).toEqual([ + { createdAtDate: 'test', lastUpdatedBy: 'test', note: 'test note' }, + ]); + }); - it('should return an empty array if the adr examiner notes is undefined', () => { - component.currentTechRecord = { ...MOCK_HGV }; - component.currentTechRecord.techRecord_adrDetails_additionalExaminerNotes = undefined; - expect(component.adrNotes).toEqual([]); - }); - }); + it('should return an empty array if the adr examiner notes is undefined', () => { + component.currentTechRecord = { ...MOCK_HGV }; + component.currentTechRecord.techRecord_adrDetails_additionalExaminerNotes = undefined; + expect(component.getAdditionalExaminerNotes()).toEqual([]); + }); + }); - describe('currentAdrNotesPage', () => { - it('should return a sliced array of adr notes depending on the page the user is on', () => { - component.currentTechRecord = { ...MOCK_HGV }; - component.pageStart = 2; - component.pageEnd = 3; - component.currentTechRecord.techRecord_adrDetails_additionalExaminerNotes = [ - { createdAtDate: 'test1', lastUpdatedBy: 'test1', note: 'test note 1' }, - { createdAtDate: 'test2', lastUpdatedBy: 'test2', note: 'test note 2' }, - { createdAtDate: 'test3', lastUpdatedBy: 'test3', note: 'test note 3' }, - { createdAtDate: 'test4', lastUpdatedBy: 'test4', note: 'test note 4' }, - ]; - expect(component.currentAdrNotesPage).toEqual([ - { createdAtDate: 'test3', lastUpdatedBy: 'test3', note: 'test note 3' }, - ]); - }); + describe('currentAdrNotesPage', () => { + it('should return a sliced array of adr notes depending on the page the user is on', () => { + component.currentTechRecord = { ...MOCK_HGV }; + component.pageStart = 2; + component.pageEnd = 3; + component.currentTechRecord.techRecord_adrDetails_additionalExaminerNotes = [ + { createdAtDate: 'test1', lastUpdatedBy: 'test1', note: 'test note 1' }, + { createdAtDate: 'test2', lastUpdatedBy: 'test2', note: 'test note 2' }, + { createdAtDate: 'test3', lastUpdatedBy: 'test3', note: 'test note 3' }, + { createdAtDate: 'test4', lastUpdatedBy: 'test4', note: 'test note 4' }, + ]; + expect(component.currentAdrNotesPage).toEqual([ + { createdAtDate: 'test3', lastUpdatedBy: 'test3', note: 'test note 3' }, + ]); + }); - it('should return an empty array if the adr examiner notes is undefined', () => { - component.currentTechRecord = { ...MOCK_HGV }; - component.pageStart = 1; - component.pageEnd = 2; - component.currentTechRecord.techRecord_adrDetails_additionalExaminerNotes = undefined; - expect(component.currentAdrNotesPage).toEqual([]); - }); - }); + it('should return an empty array if the adr examiner notes is undefined', () => { + component.currentTechRecord = { ...MOCK_HGV }; + component.pageStart = 1; + component.pageEnd = 2; + component.currentTechRecord.techRecord_adrDetails_additionalExaminerNotes = undefined; + expect(component.currentAdrNotesPage).toEqual([]); + }); + }); - describe('handlePaginationChange', () => { - it('should set the start and end pages', () => { - component.handlePaginationChange({ start: 0, end: 3 }); + describe('handlePaginationChange', () => { + it('should set the start and end pages', () => { + component.handlePaginationChange({ start: 0, end: 3 }); - expect(component.pageStart).toBe(0); - expect(component.pageEnd).toBe(3); - }); - }); + expect(component.pageStart).toBe(0); + expect(component.pageEnd).toBe(3); + }); + }); }); diff --git a/src/app/forms/custom-sections/adr-examiner-notes-history-view/adr-examiner-notes-history-view.component.ts b/src/app/forms/custom-sections/adr-examiner-notes-history-view/adr-examiner-notes-history-view.component.ts index 4e12d6f635..709703773d 100644 --- a/src/app/forms/custom-sections/adr-examiner-notes-history-view/adr-examiner-notes-history-view.component.ts +++ b/src/app/forms/custom-sections/adr-examiner-notes-history-view/adr-examiner-notes-history-view.component.ts @@ -1,62 +1,62 @@ -import { - Component, inject, OnDestroy, OnInit, -} from '@angular/core'; +import { Component, OnDestroy, OnInit, inject } from '@angular/core'; import { NG_VALUE_ACCESSOR } from '@angular/forms'; -import { BaseControlComponent } from '@forms/components/base-control/base-control.component'; import { AdditionalExaminerNotes } from '@dvsa/cvs-type-definitions/types/v3/tech-record/get/hgv/complete'; import { TechRecordType } from '@dvsa/cvs-type-definitions/types/v3/tech-record/tech-record-vehicle-type'; -import { TechnicalRecordService } from '@services/technical-record/technical-record.service'; -import { - map, Observable, Subject, takeUntil, -} from 'rxjs'; +import { BaseControlComponent } from '@forms/components/base-control/base-control.component'; import { RouterService } from '@services/router/router.service'; +import { TechnicalRecordService } from '@services/technical-record/technical-record.service'; +import { Observable, Subject, map, takeUntil } from 'rxjs'; @Component({ - selector: 'app-adr-examiner-notes-history-view', - templateUrl: './adr-examiner-notes-history-view.component.html', - styleUrls: ['./adr-examiner-notes-history-view.component.scss'], - providers: [ - { - provide: NG_VALUE_ACCESSOR, - useExisting: AdrExaminerNotesHistoryViewComponent, - multi: true, - }, - ], + selector: 'app-adr-examiner-notes-history-view', + templateUrl: './adr-examiner-notes-history-view.component.html', + styleUrls: ['./adr-examiner-notes-history-view.component.scss'], + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: AdrExaminerNotesHistoryViewComponent, + multi: true, + }, + ], }) export class AdrExaminerNotesHistoryViewComponent extends BaseControlComponent implements OnInit, OnDestroy { - technicalRecordService = inject(TechnicalRecordService); - routerService = inject(RouterService); - currentTechRecord?: TechRecordType<'hgv' | 'lgv' | 'trl'> | undefined; - pageStart?: number; - pageEnd?: number; - private destroy$: Subject = new Subject(); + technicalRecordService = inject(TechnicalRecordService); + routerService = inject(RouterService); + currentTechRecord?: TechRecordType<'hgv' | 'lgv' | 'trl'> | undefined; + pageStart?: number; + pageEnd?: number; + private destroy$: Subject = new Subject(); - ngOnInit(): void { - this.technicalRecordService.techRecord$.pipe(takeUntil(this.destroy$)).subscribe((currentTechRecord) => { - this.currentTechRecord = currentTechRecord as TechRecordType<'hgv' | 'lgv' | 'trl'>; - }); - } + ngOnInit(): void { + this.technicalRecordService.techRecord$.pipe(takeUntil(this.destroy$)).subscribe((currentTechRecord) => { + this.currentTechRecord = currentTechRecord as TechRecordType<'hgv' | 'lgv' | 'trl'>; + }); + } - get isEditing$(): Observable { - return this.routerService.getRouteDataProperty$('isEditing').pipe(map((isEditing) => !!isEditing)); - } + get isEditing$(): Observable { + return this.routerService.getRouteDataProperty$('isEditing').pipe(map((isEditing) => !!isEditing)); + } - handlePaginationChange({ start, end }: { start: number; end: number }): void { - this.pageStart = start; - this.pageEnd = end; - this.cdr.detectChanges(); - } + handlePaginationChange({ start, end }: { start: number; end: number }): void { + this.pageStart = start; + this.pageEnd = end; + this.cdr.detectChanges(); + } - get adrNotes(): AdditionalExaminerNotes[] { - return this.currentTechRecord?.techRecord_adrDetails_additionalExaminerNotes ?? []; - } + getAdditionalExaminerNotes(): AdditionalExaminerNotes[] { + return (this.currentTechRecord?.techRecord_adrDetails_additionalExaminerNotes ?? []).sort( + (a, b) => +new Date(b.createdAtDate ?? '') - +new Date(a.createdAtDate ?? '') + ); + } - get currentAdrNotesPage(): AdditionalExaminerNotes[] { - return this.currentTechRecord?.techRecord_adrDetails_additionalExaminerNotes?.slice(this.pageStart, this.pageEnd) ?? []; - } + get currentAdrNotesPage(): AdditionalExaminerNotes[] { + return ( + this.currentTechRecord?.techRecord_adrDetails_additionalExaminerNotes?.slice(this.pageStart, this.pageEnd) ?? [] + ); + } - ngOnDestroy(): void { - this.destroy$.next(); - this.destroy$.complete(); - } + ngOnDestroy(): void { + this.destroy$.next(); + this.destroy$.complete(); + } } diff --git a/src/app/forms/custom-sections/adr-new-certificate-required-view/adr-new-certificate-required-view.component.spec.ts b/src/app/forms/custom-sections/adr-new-certificate-required-view/adr-new-certificate-required-view.component.spec.ts index d36ddc7698..1b81207b10 100644 --- a/src/app/forms/custom-sections/adr-new-certificate-required-view/adr-new-certificate-required-view.component.spec.ts +++ b/src/app/forms/custom-sections/adr-new-certificate-required-view/adr-new-certificate-required-view.component.spec.ts @@ -3,39 +3,38 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { NG_VALUE_ACCESSOR, NgControl } from '@angular/forms'; import { CustomFormControl, FormNodeTypes } from '@forms/services/dynamic-form.types'; import { provideMockStore } from '@ngrx/store/testing'; -import { State, initialAppState } from '@store/index'; import { SharedModule } from '@shared/shared.module'; +import { State, initialAppState } from '@store/index'; import { AdrTankDetailsM145ViewComponent } from '../adr-tank-details-m145-view/adr-tank-details-m145-view.component'; import { AdrNewCertificateRequiredViewComponent } from './adr-new-certificate-required-view.component'; describe('AdrNewCertificateRequiredViewComponent', () => { - let component: AdrNewCertificateRequiredViewComponent; - let fixture: ComponentFixture; + let component: AdrNewCertificateRequiredViewComponent; + let fixture: ComponentFixture; - const control = new CustomFormControl({ - name: 'techRecord_adrDetails_m145Statement', - type: FormNodeTypes.CONTROL, - value: [], - }); + const control = new CustomFormControl({ + name: 'techRecord_adrDetails_m145Statement', + type: FormNodeTypes.CONTROL, + value: [], + }); - beforeEach(async () => { - await TestBed.configureTestingModule({ - declarations: [AdrNewCertificateRequiredViewComponent], - imports: [SharedModule], - providers: [ - provideMockStore({ initialState: initialAppState }), - { provide: NG_VALUE_ACCESSOR, useExisting: AdrTankDetailsM145ViewComponent, multi: true }, - { provide: NgControl, useValue: { control } }, - ], - }) - .compileComponents(); + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [AdrNewCertificateRequiredViewComponent], + imports: [SharedModule], + providers: [ + provideMockStore({ initialState: initialAppState }), + { provide: NG_VALUE_ACCESSOR, useExisting: AdrTankDetailsM145ViewComponent, multi: true }, + { provide: NgControl, useValue: { control } }, + ], + }).compileComponents(); - fixture = TestBed.createComponent(AdrNewCertificateRequiredViewComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); + fixture = TestBed.createComponent(AdrNewCertificateRequiredViewComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); }); diff --git a/src/app/forms/custom-sections/adr-new-certificate-required-view/adr-new-certificate-required-view.component.ts b/src/app/forms/custom-sections/adr-new-certificate-required-view/adr-new-certificate-required-view.component.ts index 132dcd90de..d9d211587d 100644 --- a/src/app/forms/custom-sections/adr-new-certificate-required-view/adr-new-certificate-required-view.component.ts +++ b/src/app/forms/custom-sections/adr-new-certificate-required-view/adr-new-certificate-required-view.component.ts @@ -3,15 +3,15 @@ import { NG_VALUE_ACCESSOR } from '@angular/forms'; import { BaseControlComponent } from '@forms/components/base-control/base-control.component'; @Component({ - selector: 'app-adr-new-certificate-required-view', - templateUrl: './adr-new-certificate-required-view.component.html', - styleUrl: './adr-new-certificate-required-view.component.scss', - providers: [ - { - provide: NG_VALUE_ACCESSOR, - useExisting: AdrNewCertificateRequiredViewComponent, - multi: true, - }, - ], + selector: 'app-adr-new-certificate-required-view', + templateUrl: './adr-new-certificate-required-view.component.html', + styleUrl: './adr-new-certificate-required-view.component.scss', + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: AdrNewCertificateRequiredViewComponent, + multi: true, + }, + ], }) export class AdrNewCertificateRequiredViewComponent extends BaseControlComponent {} diff --git a/src/app/forms/custom-sections/adr-tank-details-initial-inspection-view/adr-tank-details-initial-inspection-view.component.spec.ts b/src/app/forms/custom-sections/adr-tank-details-initial-inspection-view/adr-tank-details-initial-inspection-view.component.spec.ts index 7f746c1056..095c294d97 100644 --- a/src/app/forms/custom-sections/adr-tank-details-initial-inspection-view/adr-tank-details-initial-inspection-view.component.spec.ts +++ b/src/app/forms/custom-sections/adr-tank-details-initial-inspection-view/adr-tank-details-initial-inspection-view.component.spec.ts @@ -6,36 +6,35 @@ import { State, initialAppState } from '@store/index'; import { AdrTankDetailsInitialInspectionViewComponent } from './adr-tank-details-initial-inspection-view.component'; describe('AdrTankDetailsInitialInspectionViewComponent', () => { - let component: AdrTankDetailsInitialInspectionViewComponent; - let fixture: ComponentFixture; + let component: AdrTankDetailsInitialInspectionViewComponent; + let fixture: ComponentFixture; - const control = new CustomFormControl({ - name: 'tankInspectionsInitialView', - type: FormNodeTypes.CONTROL, - }); + const control = new CustomFormControl({ + name: 'tankInspectionsInitialView', + type: FormNodeTypes.CONTROL, + }); - beforeEach(async () => { - await TestBed.configureTestingModule({ - declarations: [AdrTankDetailsInitialInspectionViewComponent], - providers: [ - provideMockStore({ initialState: initialAppState }), - { provide: NG_VALUE_ACCESSOR, useExisting: AdrTankDetailsInitialInspectionViewComponent, multi: true }, - { - provide: NgControl, - useValue: { - control: { key: control.meta.name, value: control }, - }, - }, - ], - }) - .compileComponents(); + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [AdrTankDetailsInitialInspectionViewComponent], + providers: [ + provideMockStore({ initialState: initialAppState }), + { provide: NG_VALUE_ACCESSOR, useExisting: AdrTankDetailsInitialInspectionViewComponent, multi: true }, + { + provide: NgControl, + useValue: { + control: { key: control.meta.name, value: control }, + }, + }, + ], + }).compileComponents(); - fixture = TestBed.createComponent(AdrTankDetailsInitialInspectionViewComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); + fixture = TestBed.createComponent(AdrTankDetailsInitialInspectionViewComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); }); diff --git a/src/app/forms/custom-sections/adr-tank-details-initial-inspection-view/adr-tank-details-initial-inspection-view.component.ts b/src/app/forms/custom-sections/adr-tank-details-initial-inspection-view/adr-tank-details-initial-inspection-view.component.ts index 9379dc1b64..68cf585b19 100644 --- a/src/app/forms/custom-sections/adr-tank-details-initial-inspection-view/adr-tank-details-initial-inspection-view.component.ts +++ b/src/app/forms/custom-sections/adr-tank-details-initial-inspection-view/adr-tank-details-initial-inspection-view.component.ts @@ -2,8 +2,8 @@ import { Component } from '@angular/core'; import { BaseControlComponent } from '@forms/components/base-control/base-control.component'; @Component({ - selector: 'app-adr-tank-details-initial-inspection-view', - templateUrl: './adr-tank-details-initial-inspection-view.component.html', - styleUrls: ['./adr-tank-details-initial-inspection-view.component.scss'], + selector: 'app-adr-tank-details-initial-inspection-view', + templateUrl: './adr-tank-details-initial-inspection-view.component.html', + styleUrls: ['./adr-tank-details-initial-inspection-view.component.scss'], }) export class AdrTankDetailsInitialInspectionViewComponent extends BaseControlComponent {} diff --git a/src/app/forms/custom-sections/adr-tank-details-m145-view/adr-tank-details-m145-view.component.spec.ts b/src/app/forms/custom-sections/adr-tank-details-m145-view/adr-tank-details-m145-view.component.spec.ts index a01d96c1fd..d57f554834 100644 --- a/src/app/forms/custom-sections/adr-tank-details-m145-view/adr-tank-details-m145-view.component.spec.ts +++ b/src/app/forms/custom-sections/adr-tank-details-m145-view/adr-tank-details-m145-view.component.spec.ts @@ -1,7 +1,5 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { - FormsModule, NG_VALUE_ACCESSOR, NgControl, ReactiveFormsModule, -} from '@angular/forms'; +import { FormsModule, NG_VALUE_ACCESSOR, NgControl, ReactiveFormsModule } from '@angular/forms'; import { DynamicFormsModule } from '@forms/dynamic-forms.module'; import { CustomFormControl, FormNodeTypes } from '@forms/services/dynamic-form.types'; import { provideMockStore } from '@ngrx/store/testing'; @@ -9,33 +7,32 @@ import { State, initialAppState } from '@store/index'; import { AdrTankDetailsM145ViewComponent } from './adr-tank-details-m145-view.component'; describe('AdrTankDetailsM145ViewComponent', () => { - let component: AdrTankDetailsM145ViewComponent; - let fixture: ComponentFixture; + let component: AdrTankDetailsM145ViewComponent; + let fixture: ComponentFixture; - const control = new CustomFormControl({ - name: 'techRecord_adrDetails_m145Statement', - type: FormNodeTypes.CONTROL, - value: [], - }); + const control = new CustomFormControl({ + name: 'techRecord_adrDetails_m145Statement', + type: FormNodeTypes.CONTROL, + value: [], + }); - beforeEach(async () => { - await TestBed.configureTestingModule({ - declarations: [AdrTankDetailsM145ViewComponent], - imports: [DynamicFormsModule, FormsModule, ReactiveFormsModule], - providers: [ - provideMockStore({ initialState: initialAppState }), - { provide: NG_VALUE_ACCESSOR, useExisting: AdrTankDetailsM145ViewComponent, multi: true }, - { provide: NgControl, useValue: { control } }, - ], - }) - .compileComponents(); + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [AdrTankDetailsM145ViewComponent], + imports: [DynamicFormsModule, FormsModule, ReactiveFormsModule], + providers: [ + provideMockStore({ initialState: initialAppState }), + { provide: NG_VALUE_ACCESSOR, useExisting: AdrTankDetailsM145ViewComponent, multi: true }, + { provide: NgControl, useValue: { control } }, + ], + }).compileComponents(); - fixture = TestBed.createComponent(AdrTankDetailsM145ViewComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); + fixture = TestBed.createComponent(AdrTankDetailsM145ViewComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); }); diff --git a/src/app/forms/custom-sections/adr-tank-details-m145-view/adr-tank-details-m145-view.component.ts b/src/app/forms/custom-sections/adr-tank-details-m145-view/adr-tank-details-m145-view.component.ts index b94c9c0b5d..5efce00d67 100644 --- a/src/app/forms/custom-sections/adr-tank-details-m145-view/adr-tank-details-m145-view.component.ts +++ b/src/app/forms/custom-sections/adr-tank-details-m145-view/adr-tank-details-m145-view.component.ts @@ -3,17 +3,15 @@ import { NG_VALUE_ACCESSOR } from '@angular/forms'; import { BaseControlComponent } from '@forms/components/base-control/base-control.component'; @Component({ - selector: 'app-adr-tank-details-m145-view', - templateUrl: './adr-tank-details-m145-view.component.html', - styleUrls: ['./adr-tank-details-m145-view.component.scss'], - providers: [ - { - provide: NG_VALUE_ACCESSOR, - useExisting: AdrTankDetailsM145ViewComponent, - multi: true, - }, - ], + selector: 'app-adr-tank-details-m145-view', + templateUrl: './adr-tank-details-m145-view.component.html', + styleUrls: ['./adr-tank-details-m145-view.component.scss'], + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: AdrTankDetailsM145ViewComponent, + multi: true, + }, + ], }) -export class AdrTankDetailsM145ViewComponent extends BaseControlComponent { - -} +export class AdrTankDetailsM145ViewComponent extends BaseControlComponent {} diff --git a/src/app/forms/custom-sections/adr-tank-details-subsequent-inspections-edit/adr-tank-details-subsequent-inspections-edit.component.spec.ts b/src/app/forms/custom-sections/adr-tank-details-subsequent-inspections-edit/adr-tank-details-subsequent-inspections-edit.component.spec.ts index 50f8f35277..b5788ad7d0 100644 --- a/src/app/forms/custom-sections/adr-tank-details-subsequent-inspections-edit/adr-tank-details-subsequent-inspections-edit.component.spec.ts +++ b/src/app/forms/custom-sections/adr-tank-details-subsequent-inspections-edit/adr-tank-details-subsequent-inspections-edit.component.spec.ts @@ -1,8 +1,6 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { - FormsModule, NG_VALUE_ACCESSOR, NgControl, ReactiveFormsModule, -} from '@angular/forms'; +import { FormsModule, NG_VALUE_ACCESSOR, NgControl, ReactiveFormsModule } from '@angular/forms'; import { DateComponent } from '@forms/components/date/date.component'; import { FORM_INJECTION_TOKEN } from '@forms/components/dynamic-form-field/dynamic-form-field.component'; import { FieldErrorMessageComponent } from '@forms/components/field-error-message/field-error-message.component'; @@ -10,164 +8,164 @@ import { SelectComponent } from '@forms/components/select/select.component'; import { TextInputComponent } from '@forms/components/text-input/text-input.component'; import { DynamicFormsModule } from '@forms/dynamic-forms.module'; import { - CustomFormControl, - CustomFormGroup, - FormNodeTypes, - FormNodeViewTypes, + CustomFormControl, + CustomFormGroup, + FormNodeTypes, + FormNodeViewTypes, } from '@forms/services/dynamic-form.types'; import { provideMockStore } from '@ngrx/store/testing'; import { State, initialAppState } from '@store/index'; import { AdrTankDetailsSubsequentInspectionsEditComponent } from './adr-tank-details-subsequent-inspections-edit.component'; describe('AdrTankDetailsSubsequentInspectionsEditComponent', () => { - let component: AdrTankDetailsSubsequentInspectionsEditComponent; - let fixture: ComponentFixture; - - const control = new CustomFormControl({ - name: 'techRecord_adrDetails_tank_tankDetails_tc3Details', - type: FormNodeTypes.CONTROL, - }); - - beforeEach(async () => { - await TestBed.configureTestingModule({ - declarations: [ - AdrTankDetailsSubsequentInspectionsEditComponent, - SelectComponent, - FieldErrorMessageComponent, - DateComponent, - TextInputComponent, - ], - imports: [DynamicFormsModule, FormsModule, ReactiveFormsModule], - providers: [ - provideMockStore({ initialState: initialAppState }), - { provide: NG_VALUE_ACCESSOR, useExisting: AdrTankDetailsSubsequentInspectionsEditComponent, multi: true }, - { - provide: NgControl, - useValue: { - control: { key: control.meta.name, value: control }, - }, - }, - { - provide: FORM_INJECTION_TOKEN, - useValue: new CustomFormGroup({ - name: '1', - label: 'Subsequent', - type: FormNodeTypes.GROUP, - customId: `subsequent[${1}]`, - children: [ - { - name: 'tc3Type', - type: FormNodeTypes.CONTROL, - label: 'TC3: Inspection Type', - customId: `tc3Type[${1}]`, - }, - { - name: 'tc3PeriodicNumber', - label: 'TC3: Certificate Number', - type: FormNodeTypes.CONTROL, + let component: AdrTankDetailsSubsequentInspectionsEditComponent; + let fixture: ComponentFixture; - }, - { - name: 'tc3PeriodicExpiryDate', - label: 'TC3: Expiry Date', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.DATE, - }, - ], - }, { - tc3Type: new CustomFormControl({ - name: 'tc3Type', - type: FormNodeTypes.CONTROL, - }), - tc3PeriodicNumber: new CustomFormControl({ - name: 'tc3PeriodicNumber', - type: FormNodeTypes.CONTROL, - }), - tc3PeriodicExpiryDate: new CustomFormControl({ - name: 'tc3PeriodicExpiryDate', - type: FormNodeTypes.CONTROL, - }), - }), + const control = new CustomFormControl({ + name: 'techRecord_adrDetails_tank_tankDetails_tc3Details', + type: FormNodeTypes.CONTROL, + }); - }, - ], - }) - .compileComponents(); + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ + AdrTankDetailsSubsequentInspectionsEditComponent, + SelectComponent, + FieldErrorMessageComponent, + DateComponent, + TextInputComponent, + ], + imports: [DynamicFormsModule, FormsModule, ReactiveFormsModule], + providers: [ + provideMockStore({ initialState: initialAppState }), + { provide: NG_VALUE_ACCESSOR, useExisting: AdrTankDetailsSubsequentInspectionsEditComponent, multi: true }, + { + provide: NgControl, + useValue: { + control: { key: control.meta.name, value: control }, + }, + }, + { + provide: FORM_INJECTION_TOKEN, + useValue: new CustomFormGroup( + { + name: '1', + label: 'Subsequent', + type: FormNodeTypes.GROUP, + customId: `subsequent[${1}]`, + children: [ + { + name: 'tc3Type', + type: FormNodeTypes.CONTROL, + label: 'TC3: Inspection Type', + customId: `tc3Type[${1}]`, + }, + { + name: 'tc3PeriodicNumber', + label: 'TC3: Certificate Number', + type: FormNodeTypes.CONTROL, + }, + { + name: 'tc3PeriodicExpiryDate', + label: 'TC3: Expiry Date', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.DATE, + }, + ], + }, + { + tc3Type: new CustomFormControl({ + name: 'tc3Type', + type: FormNodeTypes.CONTROL, + }), + tc3PeriodicNumber: new CustomFormControl({ + name: 'tc3PeriodicNumber', + type: FormNodeTypes.CONTROL, + }), + tc3PeriodicExpiryDate: new CustomFormControl({ + name: 'tc3PeriodicExpiryDate', + type: FormNodeTypes.CONTROL, + }), + } + ), + }, + ], + }).compileComponents(); - fixture = TestBed.createComponent(AdrTankDetailsSubsequentInspectionsEditComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); + fixture = TestBed.createComponent(AdrTankDetailsSubsequentInspectionsEditComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); - beforeEach(() => { - component.formArray.patchValue([]); - }); + beforeEach(() => { + component.formArray.patchValue([]); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); - it('should, upon instantiation, add a form control to the form array', () => { - expect(component.formArray).toHaveLength(0); - }); + it('should, upon instantiation, add a form control to the form array', () => { + expect(component.formArray).toHaveLength(0); + }); - describe('ngOnInit', () => { - it('should set a subscription to listen to form array changes, and patch the control value', () => { - const ngOnInitSpy = jest.spyOn(component, 'ngOnInit'); - component.ngOnInit(); - expect(ngOnInitSpy).toHaveBeenCalled(); - }); - }); + describe('ngOnInit', () => { + it('should set a subscription to listen to form array changes, and patch the control value', () => { + const ngOnInitSpy = jest.spyOn(component, 'ngOnInit'); + component.ngOnInit(); + expect(ngOnInitSpy).toHaveBeenCalled(); + }); + }); - describe('ngOnDestroy', () => { - it('should next true and complete the destroy subject, triggering all subscriptions linked to it to complete also', () => { - const ngOnDestroySpy = jest.spyOn(component, 'ngOnDestroy'); - const destroySubjectNextSpy = jest.spyOn(component.destroy$, 'next'); - const destroySubjectCompletedSpy = jest.spyOn(component.destroy$, 'complete'); - component.ngOnDestroy(); - expect(ngOnDestroySpy).toHaveBeenCalled(); - expect(destroySubjectNextSpy).toHaveBeenCalledWith(true); - expect(destroySubjectCompletedSpy).toHaveBeenCalled(); - }); - }); + describe('ngOnDestroy', () => { + it('should next true and complete the destroy subject, triggering all subscriptions linked to it to complete also', () => { + const ngOnDestroySpy = jest.spyOn(component, 'ngOnDestroy'); + const destroySubjectNextSpy = jest.spyOn(component.destroy$, 'next'); + const destroySubjectCompletedSpy = jest.spyOn(component.destroy$, 'complete'); + component.ngOnDestroy(); + expect(ngOnDestroySpy).toHaveBeenCalled(); + expect(destroySubjectNextSpy).toHaveBeenCalledWith(true); + expect(destroySubjectCompletedSpy).toHaveBeenCalled(); + }); + }); - describe('createSubsequentInspection', () => { - it('should return a new form group representing a subsequent inspection (for ADR Tank Details)', () => { - const index = 1; - const methodSpy = jest.spyOn(component, 'createSubsequentInspection'); - const result = component.createSubsequentInspection(1); - expect(methodSpy).toHaveBeenCalled(); - expect(result).toBeDefined(); - expect(result.meta).toBeDefined(); - expect(result.meta.customId).toBe(`subsequent[${index}]`); - expect(Object.values(result.controls)).toHaveLength(3); - expect(result.get('techRecord_adrDetails_tank_tankDetails_tc3Details_tc3Type')).toBeDefined(); - expect(result.get('techRecord_adrDetails_tank_tankDetails_tc3Type_tc3PeriodicNumber')).toBeDefined(); - expect(result.get('techRecord_adrDetails_tank_tankDetails_tc3Type_tc3PeriodicExpiryDate')).toBeDefined(); - }); - }); + describe('createSubsequentInspection', () => { + it('should return a new form group representing a subsequent inspection (for ADR Tank Details)', () => { + const index = 1; + const methodSpy = jest.spyOn(component, 'createSubsequentInspection'); + const result = component.createSubsequentInspection(1); + expect(methodSpy).toHaveBeenCalled(); + expect(result).toBeDefined(); + expect(result.meta).toBeDefined(); + expect(result.meta.customId).toBe(`subsequent[${index}]`); + expect(Object.values(result.controls)).toHaveLength(3); + expect(result.get('techRecord_adrDetails_tank_tankDetails_tc3Details_tc3Type')).toBeDefined(); + expect(result.get('techRecord_adrDetails_tank_tankDetails_tc3Type_tc3PeriodicNumber')).toBeDefined(); + expect(result.get('techRecord_adrDetails_tank_tankDetails_tc3Type_tc3PeriodicExpiryDate')).toBeDefined(); + }); + }); - describe('addSubsequentInspection', () => { - it('should add an extra form group to the end of the form array', () => { - const methodSpy = jest.spyOn(component, 'addSubsequentInspection'); - component.addSubsequentInspection(); - expect(methodSpy).toHaveBeenCalled(); - expect(component.formArray).toHaveLength(1); - }); - }); + describe('addSubsequentInspection', () => { + it('should add an extra form group to the end of the form array', () => { + const methodSpy = jest.spyOn(component, 'addSubsequentInspection'); + component.addSubsequentInspection(); + expect(methodSpy).toHaveBeenCalled(); + expect(component.formArray).toHaveLength(1); + }); + }); - describe('removeSubsequentInspection', () => { - it('should remove the form group, from the form array at the correct index', () => { - component.addSubsequentInspection(); - component.addSubsequentInspection(); - component.addSubsequentInspection(); - const methodSpy = jest.spyOn(component, 'removeSubsequentInspection'); - component.removeSubsequentInspection(3); - component.removeSubsequentInspection(2); - component.removeSubsequentInspection(1); - component.removeSubsequentInspection(0); - expect(methodSpy).toHaveBeenCalledTimes(4); - expect(component.formArray).toHaveLength(0); - }); - }); + describe('removeSubsequentInspection', () => { + it('should remove the form group, from the form array at the correct index', () => { + component.addSubsequentInspection(); + component.addSubsequentInspection(); + component.addSubsequentInspection(); + const methodSpy = jest.spyOn(component, 'removeSubsequentInspection'); + component.removeSubsequentInspection(3); + component.removeSubsequentInspection(2); + component.removeSubsequentInspection(1); + component.removeSubsequentInspection(0); + expect(methodSpy).toHaveBeenCalledTimes(4); + expect(component.formArray).toHaveLength(0); + }); + }); }); diff --git a/src/app/forms/custom-sections/adr-tank-details-subsequent-inspections-edit/adr-tank-details-subsequent-inspections-edit.component.ts b/src/app/forms/custom-sections/adr-tank-details-subsequent-inspections-edit/adr-tank-details-subsequent-inspections-edit.component.ts index f31bd9d781..afed416351 100644 --- a/src/app/forms/custom-sections/adr-tank-details-subsequent-inspections-edit/adr-tank-details-subsequent-inspections-edit.component.ts +++ b/src/app/forms/custom-sections/adr-tank-details-subsequent-inspections-edit/adr-tank-details-subsequent-inspections-edit.component.ts @@ -1,134 +1,134 @@ -import { - AfterContentInit, - Component, - OnDestroy, - OnInit, -} from '@angular/core'; +import { AfterContentInit, Component, OnDestroy, OnInit } from '@angular/core'; import { FormArray, NG_VALUE_ACCESSOR, Validators } from '@angular/forms'; import { TC3Types } from '@dvsa/cvs-type-definitions/types/v3/tech-record/enums/tc3Types.enum.js'; import { ValidatorNames } from '@forms/models/validators.enum'; import { - CustomFormControl, - CustomFormGroup, - FormNodeEditTypes, - FormNodeTypes, - FormNodeViewTypes, + CustomFormControl, + CustomFormGroup, + FormNodeEditTypes, + FormNodeTypes, + FormNodeViewTypes, } from '@forms/services/dynamic-form.types'; import { getOptionsFromEnum } from '@forms/utils/enum-map'; import { CustomValidators } from '@forms/validators/custom-validators'; -import { - ReplaySubject, - takeUntil, -} from 'rxjs'; +import { ReplaySubject, takeUntil } from 'rxjs'; import { CustomFormControlComponent } from '../custom-form-control/custom-form-control.component'; @Component({ - selector: 'app-adr-tank-details-subsequent-inspections', - templateUrl: './adr-tank-details-subsequent-inspections-edit.component.html', - styleUrls: ['./adr-tank-details-subsequent-inspections-edit.component.scss'], - providers: [{ provide: NG_VALUE_ACCESSOR, useExisting: AdrTankDetailsSubsequentInspectionsEditComponent, multi: true }], + selector: 'app-adr-tank-details-subsequent-inspections', + templateUrl: './adr-tank-details-subsequent-inspections-edit.component.html', + styleUrls: ['./adr-tank-details-subsequent-inspections-edit.component.scss'], + providers: [ + { provide: NG_VALUE_ACCESSOR, useExisting: AdrTankDetailsSubsequentInspectionsEditComponent, multi: true }, + ], }) -export class AdrTankDetailsSubsequentInspectionsEditComponent extends CustomFormControlComponent implements OnInit, OnDestroy, AfterContentInit { - destroy$ = new ReplaySubject(1); +export class AdrTankDetailsSubsequentInspectionsEditComponent + extends CustomFormControlComponent + implements OnInit, OnDestroy, AfterContentInit +{ + destroy$ = new ReplaySubject(1); - formArray = new FormArray([]); + formArray = new FormArray([]); - ngOnInit() { - this.formArray.valueChanges.pipe(takeUntil(this.destroy$)).subscribe((changes) => { - this.control?.patchValue(changes, { emitModelToViewChange: true }); - }); - } + ngOnInit() { + this.formArray.valueChanges.pipe(takeUntil(this.destroy$)).subscribe((changes) => { + this.control?.patchValue(changes, { emitModelToViewChange: true }); + }); + } - ngOnDestroy(): void { - this.destroy$.next(true); - this.destroy$.complete(); - } + ngOnDestroy(): void { + this.destroy$.next(true); + this.destroy$.complete(); + } - override ngAfterContentInit() { - super.ngAfterContentInit(); - if (this.form) { - const value = this.form?.get(this.name)?.value; - const values = Array.isArray(value) && value.length ? value : []; + override ngAfterContentInit() { + super.ngAfterContentInit(); + if (this.form) { + const value = this.form?.get(this.name)?.value; + const values = Array.isArray(value) && value.length ? value : []; - values.forEach((formValue: { tc3Type: string, tc3PeriodicNumber: string, tc3PeriodicExpiryDate: string }, index: number) => { - const control = this.createSubsequentInspection(index); - control.patchValue(formValue); - this.formArray.push(control); - }); - } - } + values.forEach( + (formValue: { tc3Type: string; tc3PeriodicNumber: string; tc3PeriodicExpiryDate: string }, index: number) => { + const control = this.createSubsequentInspection(index); + control.patchValue(formValue); + this.formArray.push(control); + } + ); + } + } - createSubsequentInspection(index: number) { - const newFormGroup = new CustomFormGroup({ - name: index.toString(), - label: 'Subsequent', - type: FormNodeTypes.GROUP, - customId: `subsequent[${index}]`, - validators: [{ name: ValidatorNames.Tc3TestValidator }], - children: [ - { - name: 'tc3Type', - type: FormNodeTypes.CONTROL, - label: 'TC3: Inspection Type', - options: getOptionsFromEnum(TC3Types), - customId: `tc3Type[${index}]`, - }, - { - name: 'tc3PeriodicNumber', - label: 'TC3: Certificate Number', - type: FormNodeTypes.CONTROL, - customId: `tc3PeriodicNumber[${index}]`, - }, - { - name: 'tc3PeriodicExpiryDate', - label: 'TC3: Expiry Date', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.DATE, - viewType: FormNodeViewTypes.DATE, - customId: `tc3PeriodicExpiryDate[${index}]`, - }, - ], - }, { - tc3Type: new CustomFormControl({ - name: 'tc3Type', - type: FormNodeTypes.CONTROL, - }), - tc3PeriodicNumber: new CustomFormControl({ - name: 'tc3PeriodicNumber', - type: FormNodeTypes.CONTROL, - }), - tc3PeriodicExpiryDate: new CustomFormControl({ - name: 'tc3PeriodicExpiryDate', - type: FormNodeTypes.CONTROL, - }), - }); + createSubsequentInspection(index: number) { + const newFormGroup = new CustomFormGroup( + { + name: index.toString(), + label: 'Subsequent', + type: FormNodeTypes.GROUP, + customId: `subsequent[${index}]`, + validators: [{ name: ValidatorNames.Tc3TestValidator }], + children: [ + { + name: 'tc3Type', + type: FormNodeTypes.CONTROL, + label: 'TC3: Inspection Type', + options: getOptionsFromEnum(TC3Types), + customId: `tc3Type[${index}]`, + }, + { + name: 'tc3PeriodicNumber', + label: 'TC3: Certificate Number', + type: FormNodeTypes.CONTROL, + customId: `tc3PeriodicNumber[${index}]`, + }, + { + name: 'tc3PeriodicExpiryDate', + label: 'TC3: Expiry Date', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.DATE, + viewType: FormNodeViewTypes.DATE, + customId: `tc3PeriodicExpiryDate[${index}]`, + }, + ], + }, + { + tc3Type: new CustomFormControl({ + name: 'tc3Type', + type: FormNodeTypes.CONTROL, + }), + tc3PeriodicNumber: new CustomFormControl({ + name: 'tc3PeriodicNumber', + type: FormNodeTypes.CONTROL, + }), + tc3PeriodicExpiryDate: new CustomFormControl({ + name: 'tc3PeriodicExpiryDate', + type: FormNodeTypes.CONTROL, + }), + } + ); - newFormGroup.get('tc3PeriodicNumber')?.addValidators(Validators.maxLength(75)); - newFormGroup.get('tc3PeriodicExpiryDate')?.addValidators( - CustomValidators.tc3TestValidator({ inspectionNumber: index + 1 }), - ); - newFormGroup.get('tc3PeriodicNumber')?.addValidators( - CustomValidators.tc3TestValidator({ inspectionNumber: index + 1 }), - ); - newFormGroup.get('tc3Type')?.addValidators( - CustomValidators.tc3TestValidator({ inspectionNumber: index + 1 }), - ); + newFormGroup.get('tc3PeriodicNumber')?.addValidators(Validators.maxLength(75)); + newFormGroup + .get('tc3PeriodicExpiryDate') + ?.addValidators(CustomValidators.tc3TestValidator({ inspectionNumber: index + 1 })); + newFormGroup + .get('tc3PeriodicNumber') + ?.addValidators(CustomValidators.tc3TestValidator({ inspectionNumber: index + 1 })); + newFormGroup.get('tc3Type')?.addValidators(CustomValidators.tc3TestValidator({ inspectionNumber: index + 1 })); - return newFormGroup; - } + return newFormGroup; + } - addSubsequentInspection() { - this.formArray.push(this.createSubsequentInspection(this.formArray.length)); - } + addSubsequentInspection() { + this.formArray.push(this.createSubsequentInspection(this.formArray.length)); + } - removeSubsequentInspection(index: number) { - this.formArray.removeAt(index); - } + removeSubsequentInspection(index: number) { + this.formArray.removeAt(index); + } - handleChanges(index: number): void { - this.formArray.controls[`${index}`].markAllAsTouched(); - this.formArray.controls[`${index}`].get('tc3Type')?.updateValueAndValidity(); - this.formArray.controls[`${index}`].get('tc3PeriodicNumber')?.updateValueAndValidity(); - this.formArray.controls[`${index}`].get('tc3PeriodicExpiryDate')?.updateValueAndValidity(); - } + handleChanges(index: number): void { + this.formArray.controls[`${index}`].markAllAsTouched(); + this.formArray.controls[`${index}`].get('tc3Type')?.updateValueAndValidity(); + this.formArray.controls[`${index}`].get('tc3PeriodicNumber')?.updateValueAndValidity(); + this.formArray.controls[`${index}`].get('tc3PeriodicExpiryDate')?.updateValueAndValidity(); + } } diff --git a/src/app/forms/custom-sections/adr-tank-details-subsequent-inspections-view/adr-tank-details-subsequent-inspections-view.component.spec.ts b/src/app/forms/custom-sections/adr-tank-details-subsequent-inspections-view/adr-tank-details-subsequent-inspections-view.component.spec.ts index adcab2b9b3..69f7ebd74e 100644 --- a/src/app/forms/custom-sections/adr-tank-details-subsequent-inspections-view/adr-tank-details-subsequent-inspections-view.component.spec.ts +++ b/src/app/forms/custom-sections/adr-tank-details-subsequent-inspections-view/adr-tank-details-subsequent-inspections-view.component.spec.ts @@ -1,7 +1,5 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { - FormsModule, NG_VALUE_ACCESSOR, NgControl, ReactiveFormsModule, -} from '@angular/forms'; +import { FormsModule, NG_VALUE_ACCESSOR, NgControl, ReactiveFormsModule } from '@angular/forms'; import { DynamicFormsModule } from '@forms/dynamic-forms.module'; import { CustomFormControl, FormNodeTypes } from '@forms/services/dynamic-form.types'; import { provideMockStore } from '@ngrx/store/testing'; @@ -9,33 +7,32 @@ import { State, initialAppState } from '@store/index'; import { AdrTankDetailsSubsequentInspectionsViewComponent } from './adr-tank-details-subsequent-inspections-view.component'; describe('AdrTankDetailsSubsequentInspectionsViewComponent', () => { - let component: AdrTankDetailsSubsequentInspectionsViewComponent; - let fixture: ComponentFixture; + let component: AdrTankDetailsSubsequentInspectionsViewComponent; + let fixture: ComponentFixture; - const control = new CustomFormControl({ - name: 'techRecord_adrDetails_tank_tankDetails_tc3Details', - type: FormNodeTypes.CONTROL, - value: [], - }); + const control = new CustomFormControl({ + name: 'techRecord_adrDetails_tank_tankDetails_tc3Details', + type: FormNodeTypes.CONTROL, + value: [], + }); - beforeEach(async () => { - await TestBed.configureTestingModule({ - declarations: [AdrTankDetailsSubsequentInspectionsViewComponent], - imports: [DynamicFormsModule, FormsModule, ReactiveFormsModule], - providers: [ - provideMockStore({ initialState: initialAppState }), - { provide: NG_VALUE_ACCESSOR, useExisting: AdrTankDetailsSubsequentInspectionsViewComponent, multi: true }, - { provide: NgControl, useValue: { control } }, - ], - }) - .compileComponents(); + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [AdrTankDetailsSubsequentInspectionsViewComponent], + imports: [DynamicFormsModule, FormsModule, ReactiveFormsModule], + providers: [ + provideMockStore({ initialState: initialAppState }), + { provide: NG_VALUE_ACCESSOR, useExisting: AdrTankDetailsSubsequentInspectionsViewComponent, multi: true }, + { provide: NgControl, useValue: { control } }, + ], + }).compileComponents(); - fixture = TestBed.createComponent(AdrTankDetailsSubsequentInspectionsViewComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); + fixture = TestBed.createComponent(AdrTankDetailsSubsequentInspectionsViewComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); }); diff --git a/src/app/forms/custom-sections/adr-tank-details-subsequent-inspections-view/adr-tank-details-subsequent-inspections-view.component.ts b/src/app/forms/custom-sections/adr-tank-details-subsequent-inspections-view/adr-tank-details-subsequent-inspections-view.component.ts index c931f32c54..efbb59c4a0 100644 --- a/src/app/forms/custom-sections/adr-tank-details-subsequent-inspections-view/adr-tank-details-subsequent-inspections-view.component.ts +++ b/src/app/forms/custom-sections/adr-tank-details-subsequent-inspections-view/adr-tank-details-subsequent-inspections-view.component.ts @@ -3,17 +3,15 @@ import { NG_VALUE_ACCESSOR } from '@angular/forms'; import { BaseControlComponent } from '@forms/components/base-control/base-control.component'; @Component({ - selector: 'app-adr-tank-details-subsequent-inspections-view', - templateUrl: './adr-tank-details-subsequent-inspections-view.component.html', - styleUrls: ['./adr-tank-details-subsequent-inspections-view.component.scss'], - providers: [ - { - provide: NG_VALUE_ACCESSOR, - useExisting: AdrTankDetailsSubsequentInspectionsViewComponent, - multi: true, - }, - ], + selector: 'app-adr-tank-details-subsequent-inspections-view', + templateUrl: './adr-tank-details-subsequent-inspections-view.component.html', + styleUrls: ['./adr-tank-details-subsequent-inspections-view.component.scss'], + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: AdrTankDetailsSubsequentInspectionsViewComponent, + multi: true, + }, + ], }) -export class AdrTankDetailsSubsequentInspectionsViewComponent extends BaseControlComponent { - -} +export class AdrTankDetailsSubsequentInspectionsViewComponent extends BaseControlComponent {} diff --git a/src/app/forms/custom-sections/adr-tank-statement-un-number-edit/adr-tank-statement-un-number-edit.component.spec.ts b/src/app/forms/custom-sections/adr-tank-statement-un-number-edit/adr-tank-statement-un-number-edit.component.spec.ts index a4e417396c..8c92817282 100644 --- a/src/app/forms/custom-sections/adr-tank-statement-un-number-edit/adr-tank-statement-un-number-edit.component.spec.ts +++ b/src/app/forms/custom-sections/adr-tank-statement-un-number-edit/adr-tank-statement-un-number-edit.component.spec.ts @@ -1,9 +1,6 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { - FormBuilder, - FormControl, FormGroup, NG_VALUE_ACCESSOR, NgControl, -} from '@angular/forms'; +import { FormBuilder, FormControl, FormGroup, NG_VALUE_ACCESSOR, NgControl } from '@angular/forms'; import { GlobalWarningService } from '@core/components/global-warning/global-warning.service'; import { FORM_INJECTION_TOKEN } from '@forms/components/dynamic-form-field/dynamic-form-field.component'; import { DynamicFormsModule } from '@forms/dynamic-forms.module'; @@ -14,100 +11,99 @@ import { initialAppState } from '@store/index'; import { AdrTankStatementUnNumberEditComponent } from './adr-tank-statement-un-number-edit.component'; describe('AdrTankStatementUnNumberEditComponent', () => { - let fb: FormBuilder; - let component: AdrTankStatementUnNumberEditComponent; - let fixture: ComponentFixture; + let fb: FormBuilder; + let component: AdrTankStatementUnNumberEditComponent; + let fixture: ComponentFixture; - const control = new CustomFormControl({ - name: 'techRecord_adrDetails_tank_tankDetails_tankStatement_productListUnNo', - label: 'UN Number', - type: FormNodeTypes.CONTROL, - }); + const control = new CustomFormControl({ + name: 'techRecord_adrDetails_tank_tankDetails_tankStatement_productListUnNo', + label: 'UN Number', + type: FormNodeTypes.CONTROL, + }); - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [DynamicFormsModule, SharedModule], - declarations: [AdrTankStatementUnNumberEditComponent], - providers: [ - FormBuilder, - provideMockStore({ initialState: initialAppState }), - { provide: GlobalWarningService, useValue: { error$: jest.fn() } }, - { provide: NG_VALUE_ACCESSOR, useExisting: AdrTankStatementUnNumberEditComponent, multi: true }, - { - provide: NgControl, - useValue: { - control: { key: control.meta.name, value: control }, - }, - }, - { - provide: FORM_INJECTION_TOKEN, - useValue: new FormGroup({ - techRecord_adrDetails_additionalNotes_guidanceNotes: new FormControl(null), - }), - }, - ], - }) - .compileComponents(); + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [DynamicFormsModule, SharedModule], + declarations: [AdrTankStatementUnNumberEditComponent], + providers: [ + FormBuilder, + provideMockStore({ initialState: initialAppState }), + { provide: GlobalWarningService, useValue: { error$: jest.fn() } }, + { provide: NG_VALUE_ACCESSOR, useExisting: AdrTankStatementUnNumberEditComponent, multi: true }, + { + provide: NgControl, + useValue: { + control: { key: control.meta.name, value: control }, + }, + }, + { + provide: FORM_INJECTION_TOKEN, + useValue: new FormGroup({ + techRecord_adrDetails_additionalNotes_guidanceNotes: new FormControl(null), + }), + }, + ], + }).compileComponents(); - fb = TestBed.inject(FormBuilder); - fixture = TestBed.createComponent(AdrTankStatementUnNumberEditComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); + fb = TestBed.inject(FormBuilder); + fixture = TestBed.createComponent(AdrTankStatementUnNumberEditComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); - describe('ngAfterContentInit', () => { - it('should add an initial control to the form array once the form is injected into the component', () => { - const spy = jest.spyOn(component, 'addControl'); - component.ngAfterContentInit(); - expect(spy).toHaveBeenCalled(); - expect(component.formArray.value).toHaveLength(1); - }); - }); + describe('ngAfterContentInit', () => { + it('should add an initial control to the form array once the form is injected into the component', () => { + const spy = jest.spyOn(component, 'addControl'); + component.ngAfterContentInit(); + expect(spy).toHaveBeenCalled(); + expect(component.formArray.value).toHaveLength(1); + }); + }); - describe('addControl', () => { - it('should add a copy of the control to the end of the form array', () => { - const spy = jest.spyOn(component, 'addControl'); - component.formArray = fb.array([]); - component.addControl('valid'); - component.addControl('valid'); - component.addControl('valid'); - component.addControl('valid'); - component.addControl('valid'); - expect(spy).toHaveBeenCalled(); - expect(component.formArray.value).toHaveLength(5); - }); + describe('addControl', () => { + it('should add a copy of the control to the end of the form array', () => { + const spy = jest.spyOn(component, 'addControl'); + component.formArray = fb.array([]); + component.addControl('valid'); + component.addControl('valid'); + component.addControl('valid'); + component.addControl('valid'); + component.addControl('valid'); + expect(spy).toHaveBeenCalled(); + expect(component.formArray.value).toHaveLength(5); + }); - it('should prevent the adding of additional controls, when the previous one is empty', () => { - component.formArray = fb.array([]); - component.addControl('valid'); - component.addControl(); - component.addControl(); - expect(component.formArray.value).toHaveLength(2); - }); - }); + it('should prevent the adding of additional controls, when the previous one is empty', () => { + component.formArray = fb.array([]); + component.addControl('valid'); + component.addControl(); + component.addControl(); + expect(component.formArray.value).toHaveLength(2); + }); + }); - describe('removeControl', () => { - it('should remove the UN number control from the form array at the index specified', () => { - const methodSpy = jest.spyOn(component, 'removeControl'); + describe('removeControl', () => { + it('should remove the UN number control from the form array at the index specified', () => { + const methodSpy = jest.spyOn(component, 'removeControl'); - component.formArray.patchValue(['valid']); // ensure initial is valid to allow adding subsequent controls - component.addControl('valid'); - component.addControl('valid'); - component.addControl('valid'); - component.addControl('valid'); + component.formArray.patchValue(['valid']); // ensure initial is valid to allow adding subsequent controls + component.addControl('valid'); + component.addControl('valid'); + component.addControl('valid'); + component.addControl('valid'); - component.removeControl(3); - component.removeControl(2); + component.removeControl(3); + component.removeControl(2); - expect(methodSpy).toHaveBeenCalledTimes(2); - expect(component.formArray.controls.at(4)).toBeUndefined(); - expect(component.formArray.controls.at(3)).toBeUndefined(); - expect(component.formArray.controls.at(2)).toBeDefined(); - expect(component.formArray.controls).toHaveLength(3); - }); - }); + expect(methodSpy).toHaveBeenCalledTimes(2); + expect(component.formArray.controls.at(4)).toBeUndefined(); + expect(component.formArray.controls.at(3)).toBeUndefined(); + expect(component.formArray.controls.at(2)).toBeDefined(); + expect(component.formArray.controls).toHaveLength(3); + }); + }); }); diff --git a/src/app/forms/custom-sections/adr-tank-statement-un-number-edit/adr-tank-statement-un-number-edit.component.ts b/src/app/forms/custom-sections/adr-tank-statement-un-number-edit/adr-tank-statement-un-number-edit.component.ts index d9a3294edb..fc1572a6f5 100644 --- a/src/app/forms/custom-sections/adr-tank-statement-un-number-edit/adr-tank-statement-un-number-edit.component.ts +++ b/src/app/forms/custom-sections/adr-tank-statement-un-number-edit/adr-tank-statement-un-number-edit.component.ts @@ -1,66 +1,63 @@ -import { - Component, OnDestroy, inject, -} from '@angular/core'; +import { Component, OnDestroy, inject } from '@angular/core'; import { FormBuilder, Validators } from '@angular/forms'; import { CustomFormControl, FormNodeTypes } from '@forms/services/dynamic-form.types'; import { Subject, takeUntil } from 'rxjs'; import { CustomFormControlComponent } from '../custom-form-control/custom-form-control.component'; @Component({ - selector: 'app-adr-tank-statement-un-number', - templateUrl: './adr-tank-statement-un-number-edit.component.html', - styleUrls: ['./adr-tank-statement-un-number-edit.component.scss'], + selector: 'app-adr-tank-statement-un-number', + templateUrl: './adr-tank-statement-un-number-edit.component.html', + styleUrls: ['./adr-tank-statement-un-number-edit.component.scss'], }) export class AdrTankStatementUnNumberEditComponent extends CustomFormControlComponent implements OnDestroy { - fb = inject(FormBuilder); - - destroy$ = new Subject(); - formArray = this.fb.array([]); - - onFormArrayChange = this.formArray.valueChanges.pipe(takeUntil(this.destroy$)).subscribe((changes) => { - this.control?.patchValue(changes, { emitModelToViewChange: true }); - }); - - ngOnDestroy(): void { - this.destroy$.next(true); - this.destroy$.complete(); - } - - override ngAfterContentInit(): void { - super.ngAfterContentInit(); - this.buildFormArray(); - } - - buildFormArray() { - const values: string[] = this.control?.value ?? ['']; - values.forEach((value) => this.addControl(value)); - } - - addControl(value = '') { - const lastUnNumber = this.formArray.at(-1); - - if (this.formArray.length > 0 && (lastUnNumber.invalid || !lastUnNumber.value)) { - // If the parent control or lastUnNumber control isn't already invalid set additional errors - if (!this.control?.invalid && !lastUnNumber.invalid) { - lastUnNumber.setErrors({ required: true }); - } - - // Mark as touched to show errors - this.formArray.markAllAsTouched(); - - return; - } - - this.formArray.push( - new CustomFormControl( - { type: FormNodeTypes.CONTROL, name: 'UN number' }, - value, - [Validators.minLength(1), Validators.maxLength(1500)], - ), - ); - } - - removeControl(index: number) { - this.formArray.removeAt(index); - } + fb = inject(FormBuilder); + + destroy$ = new Subject(); + formArray = this.fb.array([]); + + onFormArrayChange = this.formArray.valueChanges.pipe(takeUntil(this.destroy$)).subscribe((changes) => { + this.control?.patchValue(changes, { emitModelToViewChange: true }); + }); + + ngOnDestroy(): void { + this.destroy$.next(true); + this.destroy$.complete(); + } + + override ngAfterContentInit(): void { + super.ngAfterContentInit(); + this.buildFormArray(); + } + + buildFormArray() { + const values: string[] = this.control?.value ?? ['']; + values.forEach((value) => this.addControl(value)); + } + + addControl(value = '') { + const lastUnNumber = this.formArray.at(-1); + + if (this.formArray.length > 0 && (lastUnNumber.invalid || !lastUnNumber.value)) { + // If the parent control or lastUnNumber control isn't already invalid set additional errors + if (!this.control?.invalid && !lastUnNumber.invalid) { + lastUnNumber.setErrors({ required: true }); + } + + // Mark as touched to show errors + this.formArray.markAllAsTouched(); + + return; + } + + this.formArray.push( + new CustomFormControl({ type: FormNodeTypes.CONTROL, name: 'UN number' }, value, [ + Validators.minLength(1), + Validators.maxLength(1500), + ]) + ); + } + + removeControl(index: number) { + this.formArray.removeAt(index); + } } diff --git a/src/app/forms/custom-sections/adr-tank-statement-un-number-view/adr-tank-statement-un-number-view.component.spec.ts b/src/app/forms/custom-sections/adr-tank-statement-un-number-view/adr-tank-statement-un-number-view.component.spec.ts index 345ab0107f..98d6419531 100644 --- a/src/app/forms/custom-sections/adr-tank-statement-un-number-view/adr-tank-statement-un-number-view.component.spec.ts +++ b/src/app/forms/custom-sections/adr-tank-statement-un-number-view/adr-tank-statement-un-number-view.component.spec.ts @@ -9,34 +9,33 @@ import { State, initialAppState } from '@store/index'; import { AdrTankStatementUnNumberViewComponent } from './adr-tank-statement-un-number-view.component'; describe('AdrTankStatementUnNumberViewComponent', () => { - let component: AdrTankStatementUnNumberViewComponent; - let fixture: ComponentFixture; + let component: AdrTankStatementUnNumberViewComponent; + let fixture: ComponentFixture; - const control = new CustomFormControl({ - name: 'techRecord_adrDetails_tank_tankDetails_tankStatement_productListUnNo', - label: 'UN Number', - type: FormNodeTypes.CONTROL, - value: ['UN number 1'], - }); + const control = new CustomFormControl({ + name: 'techRecord_adrDetails_tank_tankDetails_tankStatement_productListUnNo', + label: 'UN Number', + type: FormNodeTypes.CONTROL, + value: ['UN number 1'], + }); - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [DynamicFormsModule, SharedModule, FormsModule], - declarations: [AdrTankStatementUnNumberViewComponent], - providers: [ - provideMockStore({ initialState: initialAppState }), - { provide: NG_VALUE_ACCESSOR, useExisting: AdrTankStatementUnNumberViewComponent, multi: true }, - { provide: NgControl, useValue: { control } }, - ], - }) - .compileComponents(); + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [DynamicFormsModule, SharedModule, FormsModule], + declarations: [AdrTankStatementUnNumberViewComponent], + providers: [ + provideMockStore({ initialState: initialAppState }), + { provide: NG_VALUE_ACCESSOR, useExisting: AdrTankStatementUnNumberViewComponent, multi: true }, + { provide: NgControl, useValue: { control } }, + ], + }).compileComponents(); - fixture = TestBed.createComponent(AdrTankStatementUnNumberViewComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); + fixture = TestBed.createComponent(AdrTankStatementUnNumberViewComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); }); diff --git a/src/app/forms/custom-sections/adr-tank-statement-un-number-view/adr-tank-statement-un-number-view.component.ts b/src/app/forms/custom-sections/adr-tank-statement-un-number-view/adr-tank-statement-un-number-view.component.ts index 4d0e72691f..33b58ba9fe 100644 --- a/src/app/forms/custom-sections/adr-tank-statement-un-number-view/adr-tank-statement-un-number-view.component.ts +++ b/src/app/forms/custom-sections/adr-tank-statement-un-number-view/adr-tank-statement-un-number-view.component.ts @@ -2,8 +2,8 @@ import { Component } from '@angular/core'; import { BaseControlComponent } from '@forms/components/base-control/base-control.component'; @Component({ - selector: 'app-adr-tank-statement-un-number-view', - templateUrl: './adr-tank-statement-un-number-view.component.html', - styleUrls: ['./adr-tank-statement-un-number-view.component.scss'], + selector: 'app-adr-tank-statement-un-number-view', + templateUrl: './adr-tank-statement-un-number-view.component.html', + styleUrls: ['./adr-tank-statement-un-number-view.component.scss'], }) export class AdrTankStatementUnNumberViewComponent extends BaseControlComponent {} diff --git a/src/app/forms/custom-sections/adr/adr.component.spec.ts b/src/app/forms/custom-sections/adr/adr.component.spec.ts index 691eaab492..88ece1f511 100644 --- a/src/app/forms/custom-sections/adr/adr.component.spec.ts +++ b/src/app/forms/custom-sections/adr/adr.component.spec.ts @@ -11,90 +11,97 @@ import { initialAppState } from '@store/index'; import { AdrComponent } from './adr.component'; describe('AdrComponent', () => { - let component: AdrComponent; - let fixture: ComponentFixture; + let component: AdrComponent; + let fixture: ComponentFixture; - beforeEach(async () => { - await TestBed.configureTestingModule({ - declarations: [AdrComponent], - imports: [DynamicFormsModule, FormsModule, ReactiveFormsModule, HttpClientTestingModule], - providers: [ - provideMockStore({ initialState: initialAppState }), - { provide: TechnicalRecordService, useValue: { updateEditingTechRecord: jest.fn() } }, - { provide: AdrService, useValue: { carriesDangerousGoods: jest.fn(), determineTankStatementSelect: jest.fn() } }, - ], - }).compileComponents(); + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [AdrComponent], + imports: [DynamicFormsModule, FormsModule, ReactiveFormsModule, HttpClientTestingModule], + providers: [ + provideMockStore({ initialState: initialAppState }), + { provide: TechnicalRecordService, useValue: { updateEditingTechRecord: jest.fn() } }, + { + provide: AdrService, + useValue: { carriesDangerousGoods: jest.fn(), determineTankStatementSelect: jest.fn() }, + }, + ], + }).compileComponents(); - fixture = TestBed.createComponent(AdrComponent); - component = fixture.componentInstance; - component.techRecord = createMockHgv(1234); - fixture.detectChanges(); - }); + fixture = TestBed.createComponent(AdrComponent); + component = fixture.componentInstance; + component.techRecord = createMockHgv(1234); + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); - describe('ngOnInit', () => { - it('should populate the dangerous goods property', () => { - const spy = jest.spyOn(component.adrService, 'carriesDangerousGoods'); - component.ngOnInit(); - expect(spy).toHaveBeenCalled(); - }); - }); + describe('ngOnInit', () => { + it('should populate the dangerous goods property', () => { + const spy = jest.spyOn(component.adrService, 'carriesDangerousGoods'); + component.ngOnInit(); + expect(spy).toHaveBeenCalled(); + }); + }); - describe('handleFormChange', () => { - it('the form should be updated', () => { - const testData = { test: 11 }; - const spy = jest.spyOn(component.form, 'patchValue'); - component.handleFormChange(testData); - expect(spy).toHaveBeenCalled(); - }); + describe('handleFormChange', () => { + it('the form should be updated', () => { + const testData = { test: 11 }; + const spy = jest.spyOn(component.form, 'patchValue'); + component.handleFormChange(testData); + expect(spy).toHaveBeenCalled(); + }); - it('should not update the form if the event is null', () => { - const testData = null as unknown as Record; - const spy = jest.spyOn(component.form, 'patchValue'); - component.handleFormChange(testData); - expect(spy).not.toHaveBeenCalled(); - }); + it('should not update the form if the event is null', () => { + const testData = null as unknown as Record; + const spy = jest.spyOn(component.form, 'patchValue'); + component.handleFormChange(testData); + expect(spy).not.toHaveBeenCalled(); + }); - it('should not update the form if the techRecord is null', () => { - component.techRecord = null as unknown as TechRecordType<'hgv' | 'lgv' | 'trl'>; - const testData = { test: 11 }; - const spy = jest.spyOn(component.form, 'patchValue'); - component.handleFormChange(testData); - expect(spy).not.toHaveBeenCalled(); - }); - }); + it('should not update the form if the techRecord is null', () => { + component.techRecord = null as unknown as TechRecordType<'hgv' | 'lgv' | 'trl'>; + const testData = { test: 11 }; + const spy = jest.spyOn(component.form, 'patchValue'); + component.handleFormChange(testData); + expect(spy).not.toHaveBeenCalled(); + }); + }); - describe('adr documentation methods', () => { - it('should return false if I do not have a document id', () => { - component.techRecord = { } as unknown as TechRecordType<'hgv' | 'lgv' | 'trl'>; - const res = component.hasAdrDocumentation(); - expect(res).toBeFalsy(); - }); + describe('adr documentation methods', () => { + it('should return false if I do not have a document id', () => { + component.techRecord = {} as unknown as TechRecordType<'hgv' | 'lgv' | 'trl'>; + const res = component.hasAdrDocumentation(); + expect(res).toBeFalsy(); + }); - it('should return true if I do have a document id', () => { - component.techRecord = { techRecord_adrDetails_documentId: '1234' } as unknown as TechRecordType<'hgv' | 'lgv' | 'trl'>; - const res = component.hasAdrDocumentation(); - expect(res).toBeTruthy(); - }); + it('should return true if I do have a document id', () => { + component.techRecord = { techRecord_adrDetails_documentId: '1234' } as unknown as TechRecordType< + 'hgv' | 'lgv' | 'trl' + >; + const res = component.hasAdrDocumentation(); + expect(res).toBeTruthy(); + }); - it('should return a map with filename in', () => { - const map = new Map([['adrDocumentId', 'filename']]); - component.techRecord.techRecord_adrDetails_documentId = 'filename'; - expect(component.documentParams).toStrictEqual(map); - }); + it('should return a map with filename in', () => { + const map = new Map([['adrDocumentId', 'filename']]); + component.techRecord.techRecord_adrDetails_documentId = 'filename'; + expect(component.documentParams).toStrictEqual(map); + }); - it('should return the filename', () => { - component.techRecord.techRecord_adrDetails_documentId = 'filename'; - expect(component.fileName).toBe('filename'); - }); + it('should return the filename', () => { + component.techRecord.techRecord_adrDetails_documentId = 'filename'; + expect(component.fileName).toBe('filename'); + }); - it('should error if no filename', () => { - component.techRecord.techRecord_adrDetails_documentId = undefined; - // eslint-disable-next-line no-unused-expressions, @typescript-eslint/no-unused-expressions - expect(() => { component.fileName; }).toThrow('Could not find ADR Documentation'); - }); - }); + it('should error if no filename', () => { + component.techRecord.techRecord_adrDetails_documentId = undefined; + // eslint-disable-next-line no-unused-expressions, @typescript-eslint/no-unused-expressions + expect(() => { + component.fileName; + }).toThrow('Could not find ADR Documentation'); + }); + }); }); diff --git a/src/app/forms/custom-sections/adr/adr.component.ts b/src/app/forms/custom-sections/adr/adr.component.ts index cc6868db46..18c1101dce 100644 --- a/src/app/forms/custom-sections/adr/adr.component.ts +++ b/src/app/forms/custom-sections/adr/adr.component.ts @@ -1,9 +1,4 @@ -import { - Component, - Input, - OnDestroy, - OnInit, -} from '@angular/core'; +import { Component, Input, OnDestroy, OnInit } from '@angular/core'; import { TechRecordType } from '@dvsa/cvs-type-definitions/types/v3/tech-record/tech-record-vehicle-type'; import { TechRecordType as TechRecordTypeVerb } from '@dvsa/cvs-type-definitions/types/v3/tech-record/tech-record-verb'; import { DynamicFormService } from '@forms/services/dynamic-form.service'; @@ -17,67 +12,71 @@ import { AdrService } from '@services/adr/adr.service'; import { ReplaySubject, skipWhile, takeUntil } from 'rxjs'; @Component({ - selector: 'app-adr', - templateUrl: './adr.component.html', - styleUrls: ['./adr.component.scss'], + selector: 'app-adr', + templateUrl: './adr.component.html', + styleUrls: ['./adr.component.scss'], }) export class AdrComponent implements OnInit, OnDestroy { - @Input() techRecord!: TechRecordType<'hgv' | 'lgv' | 'trl'>; - @Input() isEditing = false; - @Input() isReviewScreen = false; + @Input() techRecord!: TechRecordType<'hgv' | 'lgv' | 'trl'>; + @Input() isEditing = false; + @Input() isReviewScreen = false; - template!: FormNode; - form!: CustomFormGroup; - destroy$ = new ReplaySubject(1); + template!: FormNode; + form!: CustomFormGroup; + destroy$ = new ReplaySubject(1); - constructor( - private dfs: DynamicFormService, - private technicalRecordService: TechnicalRecordService, - private globalErrorService: GlobalErrorService, - public adrService: AdrService, - ) { } + constructor( + private dfs: DynamicFormService, + private technicalRecordService: TechnicalRecordService, + private globalErrorService: GlobalErrorService, + public adrService: AdrService + ) {} - ngOnInit(): void { - this.template = this.isReviewScreen ? AdrSummaryTemplate : AdrTemplate; - this.form = this.dfs.createForm(this.template, this.techRecord) as CustomFormGroup; - this.techRecord.techRecord_adrDetails_dangerousGoods = this.adrService.carriesDangerousGoods(this.techRecord); - if (this.techRecord.techRecord_adrDetails_dangerousGoods && !this.isReviewScreen) { - this.techRecord.techRecord_adrDetails_tank_tankDetails_tankStatement_select = this.adrService.determineTankStatementSelect(this.techRecord); - } - this.handleSubmit(); - } + ngOnInit(): void { + this.template = this.isReviewScreen ? AdrSummaryTemplate : AdrTemplate; + this.form = this.dfs.createForm(this.template, this.techRecord) as CustomFormGroup; + this.techRecord.techRecord_adrDetails_dangerousGoods = this.adrService.carriesDangerousGoods(this.techRecord); + if (this.techRecord.techRecord_adrDetails_dangerousGoods && !this.isReviewScreen) { + this.techRecord.techRecord_adrDetails_tank_tankDetails_tankStatement_select = + this.adrService.determineTankStatementSelect(this.techRecord); + } + this.handleSubmit(); + } - ngOnDestroy(): void { - this.destroy$.next(true); - this.destroy$.complete(); - } + ngOnDestroy(): void { + this.destroy$.next(true); + this.destroy$.complete(); + } - handleFormChange(event: Record) { - if (event == null) return; - if (this.techRecord == null) return; + handleFormChange(event: Record) { + if (event == null) return; + if (this.techRecord == null) return; - this.form.patchValue(event); - this.technicalRecordService.updateEditingTechRecord({ ...this.techRecord, ...event } as TechRecordTypeVerb<'put'>); - } + this.form.patchValue(event); + this.technicalRecordService.updateEditingTechRecord({ ...this.techRecord, ...event } as TechRecordTypeVerb<'put'>); + } - get documentParams(): Map { - return new Map([['adrDocumentId', this.fileName]]); - } + get documentParams(): Map { + return new Map([['adrDocumentId', this.fileName]]); + } - get fileName(): string { - if (this.hasAdrDocumentation()) { - return this.techRecord.techRecord_adrDetails_documentId ?? ''; - } - throw new Error('Could not find ADR Documentation.'); - } + get fileName(): string { + if (this.hasAdrDocumentation()) { + return this.techRecord.techRecord_adrDetails_documentId ?? ''; + } + throw new Error('Could not find ADR Documentation.'); + } - hasAdrDocumentation(): boolean { - return !!this.techRecord.techRecord_adrDetails_documentId && !this.isEditing; - } + hasAdrDocumentation(): boolean { + return !!this.techRecord.techRecord_adrDetails_documentId && !this.isEditing; + } - handleSubmit() { - this.globalErrorService.errors$ - .pipe(takeUntil(this.destroy$), skipWhile((errors) => errors.length === 0)) - .subscribe(() => this.globalErrorService.focusAllControls()); - } + handleSubmit() { + this.globalErrorService.errors$ + .pipe( + takeUntil(this.destroy$), + skipWhile((errors) => errors.length === 0) + ) + .subscribe(() => this.globalErrorService.focusAllControls()); + } } diff --git a/src/app/forms/custom-sections/approval-type/approval-type.component.ts b/src/app/forms/custom-sections/approval-type/approval-type.component.ts index aaf62b990b..1a00f49920 100644 --- a/src/app/forms/custom-sections/approval-type/approval-type.component.ts +++ b/src/app/forms/custom-sections/approval-type/approval-type.component.ts @@ -1,6 +1,4 @@ -import { - Component, EventEmitter, Input, OnChanges, OnDestroy, OnInit, Output, SimpleChanges, -} from '@angular/core'; +import { Component, EventEmitter, Input, OnChanges, OnDestroy, OnInit, Output, SimpleChanges } from '@angular/core'; import { FormControl } from '@angular/forms'; import { TechRecord } from '@api/vehicle'; import { ApprovalType as approvalType } from '@dvsa/cvs-type-definitions/types/v3/tech-record/enums/approvalType.enum.js'; @@ -8,7 +6,11 @@ import { ApprovalType as approvalTypeHgvOrPsv } from '@dvsa/cvs-type-definitions import { TechRecordType } from '@dvsa/cvs-type-definitions/types/v3/tech-record/tech-record-vehicle-type'; import { DynamicFormService } from '@forms/services/dynamic-form.service'; import { - CustomFormGroup, FormNode, FormNodeEditTypes, FormNodeViewTypes, FormNodeWidth, + CustomFormGroup, + FormNode, + FormNodeEditTypes, + FormNodeViewTypes, + FormNodeWidth, } from '@forms/services/dynamic-form.types'; import { HgvAndTrlTypeApprovalTemplate } from '@forms/templates/general/approval-type.template'; import { PsvTypeApprovalTemplate } from '@forms/templates/psv/psv-approval-type.template'; @@ -17,84 +19,88 @@ import { VehicleTypes } from '@models/vehicle-tech-record.model'; import { Subject, debounceTime, takeUntil } from 'rxjs'; @Component({ - selector: 'app-approval-type[techRecord]', - templateUrl: './approval-type.component.html', - styleUrls: ['./approval-type.component.scss'], + selector: 'app-approval-type[techRecord]', + templateUrl: './approval-type.component.html', + styleUrls: ['./approval-type.component.scss'], }) export class ApprovalTypeComponent implements OnInit, OnChanges, OnDestroy { - @Input() techRecord!: TechRecordType<'hgv' | 'psv' | 'trl'>; - @Input() isEditing = false; - @Output() formChange = new EventEmitter(); - @Output() approvalTypeNumberChange = new EventEmitter(); + @Input() techRecord!: TechRecordType<'hgv' | 'psv' | 'trl'>; + @Input() isEditing = false; + @Output() formChange = new EventEmitter(); + @Output() approvalTypeNumberChange = new EventEmitter(); - public form!: CustomFormGroup; - private destroy$ = new Subject(); - protected chosenApprovalType: string | undefined; - protected approvalTypeChange = false; - protected approvalType: typeof approvalTypeHgvOrPsv | typeof approvalType = approvalType; - formControls: { [key: string]: FormControl } = {}; + public form!: CustomFormGroup; + private destroy$ = new Subject(); + protected chosenApprovalType: string | undefined; + protected approvalTypeChange = false; + protected approvalType: typeof approvalTypeHgvOrPsv | typeof approvalType = approvalType; + formControls: { [key: string]: FormControl } = {}; - constructor(private dfs: DynamicFormService) {} + constructor(private dfs: DynamicFormService) {} - ngOnInit() { - this.approvalType = this.techRecord.techRecord_vehicleType === 'psv' - || this.techRecord.techRecord_vehicleType === 'hgv' ? approvalTypeHgvOrPsv : approvalType; - this.form = this.dfs.createForm( - this.techRecord.techRecord_vehicleType === 'psv' ? PsvTypeApprovalTemplate : HgvAndTrlTypeApprovalTemplate, - this.techRecord, - ) as CustomFormGroup; - this.form.cleanValueChanges.pipe(debounceTime(400), takeUntil(this.destroy$)).subscribe((e) => this.formChange.emit(e)); - Object.keys(this.form.controls).forEach((key) => { - this.formControls[`${key}`] = this.form.get(key) as FormControl; - }); - } + ngOnInit() { + this.approvalType = + this.techRecord.techRecord_vehicleType === 'psv' || this.techRecord.techRecord_vehicleType === 'hgv' + ? approvalTypeHgvOrPsv + : approvalType; + this.form = this.dfs.createForm( + this.techRecord.techRecord_vehicleType === 'psv' ? PsvTypeApprovalTemplate : HgvAndTrlTypeApprovalTemplate, + this.techRecord + ) as CustomFormGroup; + this.form.cleanValueChanges + .pipe(debounceTime(400), takeUntil(this.destroy$)) + .subscribe((e) => this.formChange.emit(e)); + Object.keys(this.form.controls).forEach((key) => { + this.formControls[`${key}`] = this.form.get(key) as FormControl; + }); + } - ngOnChanges(changes: SimpleChanges): void { - const { techRecord } = changes; - if (this.form && techRecord?.currentValue && techRecord.currentValue !== techRecord.previousValue) { - const { currentValue } = techRecord; + ngOnChanges(changes: SimpleChanges): void { + const { techRecord } = changes; + if (this.form && techRecord?.currentValue && techRecord.currentValue !== techRecord.previousValue) { + const { currentValue } = techRecord; - this.form.patchValue(currentValue, { emitEvent: false }); - this.chosenApprovalType = currentValue.techRecord_approvalType ? currentValue.techRecord_approvalType : ''; - techRecord.currentValue.techRecord_coifDate = currentValue.techRecord_coifDate - ? currentValue.techRecord_coifDate.split('T')[0] - : ''; - } - } + this.form.patchValue(currentValue, { emitEvent: false }); + this.chosenApprovalType = currentValue.techRecord_approvalType ? currentValue.techRecord_approvalType : ''; + techRecord.currentValue.techRecord_coifDate = currentValue.techRecord_coifDate + ? currentValue.techRecord_coifDate.split('T')[0] + : ''; + } + } - ngOnDestroy(): void { - this.destroy$.next(); - this.destroy$.complete(); - } + ngOnDestroy(): void { + this.destroy$.next(); + this.destroy$.complete(); + } - get template(): FormNode { - switch (this.techRecord.techRecord_vehicleType) { - case VehicleTypes.PSV: - return PsvTypeApprovalTemplate; - case VehicleTypes.HGV: - return HgvAndTrlTypeApprovalTemplate; - case VehicleTypes.TRL: - return HgvAndTrlTypeApprovalTemplate; - default: - throw Error('Incorrect vehicle type!'); - } - } + get template(): FormNode { + switch (this.techRecord.techRecord_vehicleType) { + case VehicleTypes.PSV: + return PsvTypeApprovalTemplate; + case VehicleTypes.HGV: + return HgvAndTrlTypeApprovalTemplate; + case VehicleTypes.TRL: + return HgvAndTrlTypeApprovalTemplate; + default: + throw Error('Incorrect vehicle type!'); + } + } - get editTypes(): typeof FormNodeEditTypes { - return FormNodeEditTypes; - } + get editTypes(): typeof FormNodeEditTypes { + return FormNodeEditTypes; + } - get widths(): typeof FormNodeWidth { - return FormNodeWidth; - } + get widths(): typeof FormNodeWidth { + return FormNodeWidth; + } - get isPsv(): boolean { - return this.techRecord.techRecord_vehicleType === VehicleTypes.PSV; - } + get isPsv(): boolean { + return this.techRecord.techRecord_vehicleType === VehicleTypes.PSV; + } - get formNodeViewTypes(): typeof FormNodeViewTypes { - return FormNodeViewTypes; - } - protected readonly TechRecord = TechRecord; - protected readonly getOptionsFromEnum = getOptionsFromEnum; + get formNodeViewTypes(): typeof FormNodeViewTypes { + return FormNodeViewTypes; + } + protected readonly TechRecord = TechRecord; + protected readonly getOptionsFromEnum = getOptionsFromEnum; } diff --git a/src/app/forms/custom-sections/approval-type/approval-type.directive.ts b/src/app/forms/custom-sections/approval-type/approval-type.directive.ts index 52d62cea8f..18de794880 100644 --- a/src/app/forms/custom-sections/approval-type/approval-type.directive.ts +++ b/src/app/forms/custom-sections/approval-type/approval-type.directive.ts @@ -1,20 +1,18 @@ -import { - Directive, ElementRef, HostListener, Input, -} from '@angular/core'; +import { Directive, ElementRef, HostListener, Input } from '@angular/core'; @Directive({ - selector: '[appFocusOnCharacterLimit]', + selector: '[appFocusOnCharacterLimit]', }) export class FocusOnCharacterLimitDirective { - @Input() nextElement: ElementRef | undefined; - @Input() characterLimit = 0; + @Input() nextElement: ElementRef | undefined; + @Input() characterLimit = 0; - constructor(private el: ElementRef) {} + constructor(private el: ElementRef) {} - @HostListener('input', ['$event.target.value']) - onInput(value: string): void { - if (value.length >= this.characterLimit && this.nextElement) { - this.nextElement.nativeElement.focus(); - } - } + @HostListener('input', ['$event.target.value']) + onInput(value: string): void { + if (value.length >= this.characterLimit && this.nextElement) { + this.nextElement.nativeElement.focus(); + } + } } diff --git a/src/app/forms/custom-sections/body/body.component.spec.ts b/src/app/forms/custom-sections/body/body.component.spec.ts index 56912ce914..ebd9fcd41f 100644 --- a/src/app/forms/custom-sections/body/body.component.spec.ts +++ b/src/app/forms/custom-sections/body/body.component.spec.ts @@ -24,241 +24,246 @@ import { lastValueFrom, of } from 'rxjs'; import { BodyComponent } from './body.component'; describe('BodyComponent', () => { - let component: BodyComponent; - let fixture: ComponentFixture; - let store: MockStore; - let multiOptionsService: MultiOptionsService; - let dynamicFormService: DynamicFormService; - let referenceDataService: ReferenceDataService; + let component: BodyComponent; + let fixture: ComponentFixture; + let store: MockStore; + let multiOptionsService: MultiOptionsService; + let dynamicFormService: DynamicFormService; + let referenceDataService: ReferenceDataService; - beforeEach(async () => { - await TestBed.configureTestingModule({ - declarations: [BodyComponent], - imports: [DynamicFormsModule, FormsModule, HttpClientTestingModule, ReactiveFormsModule, RouterTestingModule], - providers: [ - provideMockStore({ initialState: initialAppState }), - ReferenceDataService, - { provide: UserService, useValue: {} }, - { provide: MultiOptionsService, useValue: { getOptions: jest.fn(), loadOptions: jest.fn() } }, - ], - }).compileComponents(); - store = TestBed.inject(MockStore); - multiOptionsService = TestBed.inject(MultiOptionsService); - dynamicFormService = TestBed.inject(DynamicFormService); - referenceDataService = TestBed.inject(ReferenceDataService); - }); + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [BodyComponent], + imports: [DynamicFormsModule, FormsModule, HttpClientTestingModule, ReactiveFormsModule, RouterTestingModule], + providers: [ + provideMockStore({ initialState: initialAppState }), + ReferenceDataService, + { provide: UserService, useValue: {} }, + { provide: MultiOptionsService, useValue: { getOptions: jest.fn(), loadOptions: jest.fn() } }, + ], + }).compileComponents(); + store = TestBed.inject(MockStore); + multiOptionsService = TestBed.inject(MultiOptionsService); + dynamicFormService = TestBed.inject(DynamicFormService); + referenceDataService = TestBed.inject(ReferenceDataService); + }); - beforeEach(() => { - fixture = TestBed.createComponent(BodyComponent); - component = fixture.componentInstance; - component.techRecord = { - systemNumber: 'foo', - createdTimestamp: 'bar', - vin: 'testVin', - techRecord_vehicleType: VehicleTypes.PSV, - techRecord_brakes_dtpNumber: '000000', - techRecord_bodyModel: 'model', - techRecord_bodyType_description: 'type', - techRecord_chassisMake: 'chassisType', - } as unknown as TechRecordType<'psv'>; - fixture.detectChanges(); - }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + beforeEach(() => { + fixture = TestBed.createComponent(BodyComponent); + component = fixture.componentInstance; + component.techRecord = { + systemNumber: 'foo', + createdTimestamp: 'bar', + vin: 'testVin', + techRecord_vehicleType: VehicleTypes.PSV, + techRecord_brakes_dtpNumber: '000000', + techRecord_bodyModel: 'model', + techRecord_bodyType_description: 'type', + techRecord_chassisMake: 'chassisType', + } as unknown as TechRecordType<'psv'>; + fixture.detectChanges(); + }); + it('should create', () => { + expect(component).toBeTruthy(); + }); - describe('The DTpNumber value on this.form', () => { - it('should match the corresponding values on vehicleTechRecord', () => { - expect((component.techRecord as TechRecordType<'psv'>).techRecord_brakes_dtpNumber).toStrictEqual( - component.form.value.techRecord_brakes_dtpNumber, - ); - }); - }); - describe('The bodyModel value on this.form', () => { - it('should match the corresponding values on vehicleTechRecord', () => { - expect((component.techRecord as TechRecordType<'psv'>).techRecord_bodyModel).toStrictEqual(component.form.value.techRecord_bodyModel); - }); - }); - describe('The bodyType value on this.form', () => { - it('should match the corresponding values on vehicleTechRecord', () => { - expect((component.techRecord as TechRecordType<'psv'>).techRecord_bodyType_description).toStrictEqual( - component.form.controls['techRecord_bodyType_description']?.value, - ); - }); - }); - describe('The chassisMake value on this.form', () => { - it('should match the corresponding values on vehicleTechRecord', () => { - expect((component.techRecord as TechRecordType<'psv'>).techRecord_chassisMake).toStrictEqual( - component.form.controls['techRecord_chassisMake']?.value, - ); - }); - }); - describe('updateArticulatedHgvVehicleBodyType', () => { - it('should dispatch updateEditingTechRecord if vehicle is hgv and articulated', () => { - const mockRecord = { - techRecord_vehicleType: 'hgv', - techRecord_vehicleConfiguration: 'articulated', - techRecord_bodyType_description: '', - systemNumber: 'foo', - createdTimestamp: 'bar', - vin: 'testVin', - techRecord_brakes_dtpNumber: '000000', - techRecord_bodyModel: 'model', - techRecord_chassisMake: 'chassisType', - } as unknown as V3TechRecordModel; - component.techRecord = mockRecord; + describe('The DTpNumber value on this.form', () => { + it('should match the corresponding values on vehicleTechRecord', () => { + expect((component.techRecord as TechRecordType<'psv'>).techRecord_brakes_dtpNumber).toStrictEqual( + component.form.value.techRecord_brakes_dtpNumber + ); + }); + }); + describe('The bodyModel value on this.form', () => { + it('should match the corresponding values on vehicleTechRecord', () => { + expect((component.techRecord as TechRecordType<'psv'>).techRecord_bodyModel).toStrictEqual( + component.form.value.techRecord_bodyModel + ); + }); + }); + describe('The bodyType value on this.form', () => { + it('should match the corresponding values on vehicleTechRecord', () => { + expect((component.techRecord as TechRecordType<'psv'>).techRecord_bodyType_description).toStrictEqual( + component.form.controls['techRecord_bodyType_description']?.value + ); + }); + }); + describe('The chassisMake value on this.form', () => { + it('should match the corresponding values on vehicleTechRecord', () => { + expect((component.techRecord as TechRecordType<'psv'>).techRecord_chassisMake).toStrictEqual( + component.form.controls['techRecord_chassisMake']?.value + ); + }); + }); + describe('updateArticulatedHgvVehicleBodyType', () => { + it('should dispatch updateEditingTechRecord if vehicle is hgv and articulated', () => { + const mockRecord = { + techRecord_vehicleType: 'hgv', + techRecord_vehicleConfiguration: 'articulated', + techRecord_bodyType_description: '', + systemNumber: 'foo', + createdTimestamp: 'bar', + vin: 'testVin', + techRecord_brakes_dtpNumber: '000000', + techRecord_bodyModel: 'model', + techRecord_chassisMake: 'chassisType', + } as unknown as V3TechRecordModel; + component.techRecord = mockRecord; - const dispatchSpy = jest.spyOn(store, 'dispatch'); - component.updateHgvVehicleBodyType(mockRecord as TechRecordType<'hgv'>); - expect(dispatchSpy).toHaveBeenCalled(); - expect(dispatchSpy).toHaveBeenCalledWith(expect.objectContaining({ - vehicleTechRecord: { - createdTimestamp: 'bar', - systemNumber: 'foo', - techRecord_bodyModel: 'model', - techRecord_bodyType_description: 'articulated', - techRecord_brakes_dtpNumber: '000000', - techRecord_chassisMake: 'chassisType', - techRecord_vehicleConfiguration: 'articulated', - techRecord_vehicleType: 'hgv', - techRecord_bodyType_code: 'a', - vin: 'testVin', - }, - })); - }); - it('should not dispatch updateEditingTechRecord if vehicle is hgv and rigid', () => { - const mockRecord = { - techRecord_vehicleType: 'hgv', - techRecord_vehicleConfiguration: 'rigid', - techRecord_bodyType_description: '', - systemNumber: 'foo', - createdTimestamp: 'bar', - vin: 'testVin', - techRecord_brakes_dtpNumber: '000000', - techRecord_bodyModel: 'model', - techRecord_chassisMake: 'chassisType', - } as unknown as V3TechRecordModel; + const dispatchSpy = jest.spyOn(store, 'dispatch'); + component.updateHgvVehicleBodyType(mockRecord as TechRecordType<'hgv'>); + expect(dispatchSpy).toHaveBeenCalled(); + expect(dispatchSpy).toHaveBeenCalledWith( + expect.objectContaining({ + vehicleTechRecord: { + createdTimestamp: 'bar', + systemNumber: 'foo', + techRecord_bodyModel: 'model', + techRecord_bodyType_description: 'articulated', + techRecord_brakes_dtpNumber: '000000', + techRecord_chassisMake: 'chassisType', + techRecord_vehicleConfiguration: 'articulated', + techRecord_vehicleType: 'hgv', + techRecord_bodyType_code: 'a', + vin: 'testVin', + }, + }) + ); + }); + it('should not dispatch updateEditingTechRecord if vehicle is hgv and rigid', () => { + const mockRecord = { + techRecord_vehicleType: 'hgv', + techRecord_vehicleConfiguration: 'rigid', + techRecord_bodyType_description: '', + systemNumber: 'foo', + createdTimestamp: 'bar', + vin: 'testVin', + techRecord_brakes_dtpNumber: '000000', + techRecord_bodyModel: 'model', + techRecord_chassisMake: 'chassisType', + } as unknown as V3TechRecordModel; - component.techRecord = mockRecord; + component.techRecord = mockRecord; - const dispatchSpy = jest.spyOn(store, 'dispatch'); - component.updateHgvVehicleBodyType(mockRecord as TechRecordType<'hgv'>); - expect(dispatchSpy).not.toHaveBeenCalled(); - }); - }); + const dispatchSpy = jest.spyOn(store, 'dispatch'); + component.updateHgvVehicleBodyType(mockRecord as TechRecordType<'hgv'>); + expect(dispatchSpy).not.toHaveBeenCalled(); + }); + }); - describe('loadOptions', () => { + describe('loadOptions', () => { + it('should trigger the loading of HGV make ref data when viewing a HGV', () => { + const spy = jest.spyOn(multiOptionsService, 'loadOptions'); + component.disableLoadOptions = false; + component.techRecord = createMockHgv(123); + component.loadOptions(); + expect(spy).toHaveBeenCalledWith(ReferenceDataResourceType.HgvMake); + }); - it('should trigger the loading of HGV make ref data when viewing a HGV', () => { - const spy = jest.spyOn(multiOptionsService, 'loadOptions'); - component.disableLoadOptions = false; - component.techRecord = createMockHgv(123); - component.loadOptions(); - expect(spy).toHaveBeenCalledWith(ReferenceDataResourceType.HgvMake); - }); + it('should trigger the loading of PSV make ref data when viewing a PSV', () => { + const spy = jest.spyOn(multiOptionsService, 'loadOptions'); + component.disableLoadOptions = false; + component.techRecord = createMockPsv(123); + component.loadOptions(); + expect(spy).toHaveBeenCalledWith(ReferenceDataResourceType.PsvMake); + }); - it('should trigger the loading of PSV make ref data when viewing a PSV', () => { - const spy = jest.spyOn(multiOptionsService, 'loadOptions'); - component.disableLoadOptions = false; - component.techRecord = createMockPsv(123); - component.loadOptions(); - expect(spy).toHaveBeenCalledWith(ReferenceDataResourceType.PsvMake); - }); + it('should trigger the loading of TRL make ref data when viewing a TRL', () => { + const spy = jest.spyOn(multiOptionsService, 'loadOptions'); + component.disableLoadOptions = false; + component.techRecord = createMockTrl(123); + component.loadOptions(); + expect(spy).toHaveBeenCalledWith(ReferenceDataResourceType.TrlMake); + }); + }); - it('should trigger the loading of TRL make ref data when viewing a TRL', () => { - const spy = jest.spyOn(multiOptionsService, 'loadOptions'); - component.disableLoadOptions = false; - component.techRecord = createMockTrl(123); - component.loadOptions(); - expect(spy).toHaveBeenCalledWith(ReferenceDataResourceType.TrlMake); - }); - }); + describe('ngOnInit', () => { + it('should use the PSV body type template to create the form when the vehicle type of the provided tech record is PSV', () => { + const techRecord = createMockPsv(123); + const dfsSpy = jest.spyOn(dynamicFormService, 'createForm'); + component.techRecord = techRecord; + component.ngOnInit(); + expect(dfsSpy).toHaveBeenCalledWith(PsvBodyTemplate, techRecord); + }); - describe('ngOnInit', () => { - it('should use the PSV body type template to create the form when the vehicle type of the provided tech record is PSV', () => { - const techRecord = createMockPsv(123); - const dfsSpy = jest.spyOn(dynamicFormService, 'createForm'); - component.techRecord = techRecord; - component.ngOnInit(); - expect(dfsSpy).toHaveBeenCalledWith(PsvBodyTemplate, techRecord); - }); + it('should use the HGV body type template to create the form when the vehicle type of the provided tech record is HGV', () => { + const techRecord = createMockHgv(123); + const dfsSpy = jest.spyOn(dynamicFormService, 'createForm'); + component.techRecord = techRecord; + component.ngOnInit(); + expect(dfsSpy).toHaveBeenCalledWith(HgvAndTrlBodyTemplate, techRecord); + }); - it('should use the HGV body type template to create the form when the vehicle type of the provided tech record is HGV', () => { - const techRecord = createMockHgv(123); - const dfsSpy = jest.spyOn(dynamicFormService, 'createForm'); - component.techRecord = techRecord; - component.ngOnInit(); - expect(dfsSpy).toHaveBeenCalledWith(HgvAndTrlBodyTemplate, techRecord); - }); + it('should call load options to fetch the ref data required for displaying the vehicle type', () => { + const loadOptionsSpy = jest.spyOn(component, 'loadOptions'); + component.ngOnInit(); + expect(loadOptionsSpy).toHaveBeenCalled(); + }); + }); - it('should call load options to fetch the ref data required for displaying the vehicle type', () => { - const loadOptionsSpy = jest.spyOn(component, 'loadOptions'); - component.ngOnInit(); - expect(loadOptionsSpy).toHaveBeenCalled(); - }); - }); + describe('ngOnChanges', () => { + it('should do nothing if the tech record input has not changed', () => { + const formSpy = jest.spyOn(component.form, 'patchValue'); + component.ngOnChanges({} as SimpleChanges); + expect(formSpy).not.toHaveBeenCalled(); + }); - describe('ngOnChanges', () => { - it('should do nothing if the tech record input has not changed', () => { - const formSpy = jest.spyOn(component.form, 'patchValue'); - component.ngOnChanges({} as SimpleChanges); - expect(formSpy).not.toHaveBeenCalled(); - }); + it('should patch the form with the current tech record when this changes', () => { + const formSpy = jest.spyOn(component.form, 'patchValue'); + component.ngOnChanges({ techRecord: { currentValue: {}, previousValue: null } } as unknown as SimpleChanges); + expect(formSpy).toHaveBeenCalled(); + }); + }); - it('should patch the form with the current tech record when this changes', () => { - const formSpy = jest.spyOn(component.form, 'patchValue'); - component.ngOnChanges({ techRecord: { currentValue: {}, previousValue: null } } as unknown as SimpleChanges); - expect(formSpy).toHaveBeenCalled(); - }); - }); + describe('editTypes', () => { + it('should return the form node edit types enum', () => { + expect(component.editTypes).toBe(FormNodeEditTypes); + }); + }); - describe('editTypes', () => { - it('should return the form node edit types enum', () => { - expect(component.editTypes).toBe(FormNodeEditTypes); - }); - }); + describe('widths', () => { + it('should return the form node width enum', () => { + expect(component.widths).toBe(FormNodeWidth); + }); + }); - describe('widths', () => { - it('should return the form node width enum', () => { - expect(component.widths).toBe(FormNodeWidth); - }); - }); + describe('bodyMakes', () => { + it('should return an observable which emits PSV body make ref data when the tech record is a PSV', async () => { + const mockData = [{ label: 'PSV', value: 'psv' }]; + const spy = jest.spyOn(multiOptionsService, 'getOptions').mockReturnValue(of(mockData)); + component.techRecord = createMockPsv(123); + await expect(lastValueFrom(component.bodyMakes$)).resolves.toEqual(mockData); + expect(spy).toHaveBeenCalledWith(ReferenceDataResourceType.PsvMake); + }); - describe('bodyMakes', () => { - it('should return an observable which emits PSV body make ref data when the tech record is a PSV', async () => { - const mockData = [{ label: 'PSV', value: 'psv' }]; - const spy = jest.spyOn(multiOptionsService, 'getOptions').mockReturnValue(of(mockData)); - component.techRecord = createMockPsv(123); - await expect(lastValueFrom(component.bodyMakes$)).resolves.toEqual(mockData); - expect(spy).toHaveBeenCalledWith(ReferenceDataResourceType.PsvMake); - }); + it('should return an observable which emits HGV body make ref data when the tech record is a HGV', async () => { + const mockData = [{ label: 'HGV', value: 'HGV' }]; + const spy = jest.spyOn(multiOptionsService, 'getOptions').mockReturnValue(of(mockData)); + component.techRecord = createMockHgv(123); + await expect(lastValueFrom(component.bodyMakes$)).resolves.toEqual(mockData); + expect(spy).toHaveBeenCalledWith(ReferenceDataResourceType.HgvMake); + }); - it('should return an observable which emits HGV body make ref data when the tech record is a HGV', async () => { - const mockData = [{ label: 'HGV', value: 'HGV' }]; - const spy = jest.spyOn(multiOptionsService, 'getOptions').mockReturnValue(of(mockData)); - component.techRecord = createMockHgv(123); - await expect(lastValueFrom(component.bodyMakes$)).resolves.toEqual(mockData); - expect(spy).toHaveBeenCalledWith(ReferenceDataResourceType.HgvMake); - }); + it('should return an observable which emits TRL body make ref data when the tech record is a TRL', async () => { + const mockData = [{ label: 'TRL', value: 'TRL' }]; + const spy = jest.spyOn(multiOptionsService, 'getOptions').mockReturnValue(of(mockData)); + component.techRecord = createMockTrl(123); + await expect(lastValueFrom(component.bodyMakes$)).resolves.toEqual(mockData); + expect(spy).toHaveBeenCalledWith(ReferenceDataResourceType.TrlMake); + }); + }); - it('should return an observable which emits TRL body make ref data when the tech record is a TRL', async () => { - const mockData = [{ label: 'TRL', value: 'TRL' }]; - const spy = jest.spyOn(multiOptionsService, 'getOptions').mockReturnValue(of(mockData)); - component.techRecord = createMockTrl(123); - await expect(lastValueFrom(component.bodyMakes$)).resolves.toEqual(mockData); - expect(spy).toHaveBeenCalledWith(ReferenceDataResourceType.TrlMake); - }); - }); - - describe('dtpNumbers', () => { - it('should return an observable which emits an array of multi options derrived from reference data', async () => { - const allMockData = [{ resourceKey: 'psv', resourceType: ReferenceDataResourceType.PsvMake }]; - const getAllSpy = jest.spyOn(referenceDataService, 'getAll$').mockReturnValue(of(allMockData)); - const getPsvMakeDataLoadingSpy = jest.spyOn(referenceDataService, 'getReferencePsvMakeDataLoading$').mockReturnValue(of(false)); - component.techRecord = createMockPsv(123); - await expect(lastValueFrom(component.dtpNumbers$)).resolves.toEqual([{ value: 'psv', label: 'psv' }]); - expect(getAllSpy).toHaveBeenCalledWith(ReferenceDataResourceType.PsvMake); - expect(getPsvMakeDataLoadingSpy).toHaveBeenCalled(); - }); - }); + describe('dtpNumbers', () => { + it('should return an observable which emits an array of multi options derrived from reference data', async () => { + const allMockData = [{ resourceKey: 'psv', resourceType: ReferenceDataResourceType.PsvMake }]; + const getAllSpy = jest.spyOn(referenceDataService, 'getAll$').mockReturnValue(of(allMockData)); + const getPsvMakeDataLoadingSpy = jest + .spyOn(referenceDataService, 'getReferencePsvMakeDataLoading$') + .mockReturnValue(of(false)); + component.techRecord = createMockPsv(123); + await expect(lastValueFrom(component.dtpNumbers$)).resolves.toEqual([{ value: 'psv', label: 'psv' }]); + expect(getAllSpy).toHaveBeenCalledWith(ReferenceDataResourceType.PsvMake); + expect(getPsvMakeDataLoadingSpy).toHaveBeenCalled(); + }); + }); }); diff --git a/src/app/forms/custom-sections/body/body.component.ts b/src/app/forms/custom-sections/body/body.component.ts index 86e4a75f6b..4f847f17cf 100644 --- a/src/app/forms/custom-sections/body/body.component.ts +++ b/src/app/forms/custom-sections/body/body.component.ts @@ -1,19 +1,18 @@ -import { - Component, EventEmitter, Input, OnChanges, OnDestroy, OnInit, Output, SimpleChanges, -} from '@angular/core'; +import { Component, EventEmitter, Input, OnChanges, OnDestroy, OnInit, Output, SimpleChanges } from '@angular/core'; import { TechRecordType as TechRecordVehicleType } from '@dvsa/cvs-type-definitions/types/v3/tech-record/tech-record-vehicle-type'; import { TechRecordType } from '@dvsa/cvs-type-definitions/types/v3/tech-record/tech-record-verb'; import { MultiOptions } from '@forms/models/options.model'; import { DynamicFormService } from '@forms/services/dynamic-form.service'; -import { - CustomFormGroup, FormNode, FormNodeEditTypes, FormNodeWidth, -} from '@forms/services/dynamic-form.types'; +import { CustomFormGroup, FormNode, FormNodeEditTypes, FormNodeWidth } from '@forms/services/dynamic-form.types'; import { MultiOptionsService } from '@forms/services/multi-options.service'; import { HgvAndTrlBodyTemplate } from '@forms/templates/general/hgv-trl-body.template'; import { PsvBodyTemplate } from '@forms/templates/psv/psv-body.template'; import { getOptionsFromEnum } from '@forms/utils/enum-map'; import { - BodyTypeCode, BodyTypeDescription, vehicleBodyTypeCodeMap, vehicleBodyTypeDescriptionMap, + BodyTypeCode, + BodyTypeDescription, + vehicleBodyTypeCodeMap, + vehicleBodyTypeDescriptionMap, } from '@models/body-type-enum'; import { PsvMake, ReferenceDataResourceType } from '@models/reference-data.model'; import { V3TechRecordModel, VehicleTypes } from '@models/vehicle-tech-record.model'; @@ -22,152 +21,159 @@ import { ReferenceDataService } from '@services/reference-data/reference-data.se import { State } from '@store/index'; import { selectReferenceDataByResourceKey } from '@store/reference-data'; import { updateBody, updateEditingTechRecord } from '@store/technical-records'; -import { - Observable, Subject, combineLatest, debounceTime, map, mergeMap, skipWhile, take, takeUntil, -} from 'rxjs'; +import { Observable, Subject, combineLatest, debounceTime, map, mergeMap, skipWhile, take, takeUntil } from 'rxjs'; @Component({ - selector: 'app-body', - templateUrl: './body.component.html', - styleUrls: ['./body.component.scss'], + selector: 'app-body', + templateUrl: './body.component.html', + styleUrls: ['./body.component.scss'], }) export class BodyComponent implements OnInit, OnChanges, OnDestroy { - @Input() techRecord!: V3TechRecordModel; - @Input() isEditing = false; - @Input() disableLoadOptions = false; - - @Output() formChange = new EventEmitter(); - - public form!: CustomFormGroup; - private template!: FormNode; - private destroy$ = new Subject(); - - constructor( - private dfs: DynamicFormService, - private optionsService: MultiOptionsService, - private referenceDataService: ReferenceDataService, - private store: Store, - ) { } - - ngOnInit(): void { - this.template = this.techRecord.techRecord_vehicleType === VehicleTypes.PSV ? PsvBodyTemplate : HgvAndTrlBodyTemplate; - this.form = this.dfs.createForm(this.template, this.techRecord) as CustomFormGroup; - this.form.cleanValueChanges - .pipe( - debounceTime(400), - takeUntil(this.destroy$), - // eslint-disable-next-line @typescript-eslint/no-explicit-any - mergeMap((event: any) => - this.store.pipe( - select(selectReferenceDataByResourceKey(ReferenceDataResourceType.PsvMake, event.techRecord_brakes_dtpNumber)), - take(1), - map((referenceData) => [event, referenceData as PsvMake]), - )), - ) - .subscribe(([event, psvMake]) => { - // Set the body type code automatically based selection - if (event?.techRecord_bodyType_description) { - // body type codes are specific to the vehicle type - const vehicleType = this.techRecord.techRecord_vehicleType === 'hgv' - ? `${this.techRecord.techRecord_vehicleConfiguration}Hgv` - : this.techRecord.techRecord_vehicleType; - const bodyTypes = vehicleBodyTypeDescriptionMap.get(vehicleType as VehicleTypes) as Map; - event.techRecord_bodyType_code = bodyTypes.get(event?.techRecord_bodyType_description); - } - - this.formChange.emit(event); - - if ( - this.techRecord.techRecord_vehicleType === VehicleTypes.PSV - && event?.techRecord_brakes_dtpNumber - && event.techRecord_brakes_dtpNumber.length >= 4 - && psvMake - ) { - this.store.dispatch(updateBody({ psvMake })); - } - }); - - this.loadOptions(); - } - - ngOnChanges(changes: SimpleChanges): void { - const { techRecord } = changes; - - if (this.form && techRecord?.currentValue && techRecord.currentValue !== techRecord.previousValue) { - this.form.patchValue(techRecord.currentValue, { emitEvent: false }); - } - } - - ngOnDestroy(): void { - this.destroy$.next(); - this.destroy$.complete(); - } - - get editTypes(): typeof FormNodeEditTypes { - return FormNodeEditTypes; - } - - get widths(): typeof FormNodeWidth { - return FormNodeWidth; - } - - get bodyTypes(): MultiOptions { - let vehicleType: string = this.techRecord.techRecord_vehicleType; - - if (this.techRecord.techRecord_vehicleType === 'hgv') { - vehicleType = `${this.techRecord.techRecord_vehicleConfiguration}Hgv`; - this.updateHgvVehicleBodyType(this.techRecord); - } - const optionsMap = vehicleBodyTypeCodeMap.get(vehicleType) ?? []; - const values = [...optionsMap.values()]; - return getOptionsFromEnum(values.sort()); - - } - - get bodyMakes$(): Observable { - if (this.techRecord.techRecord_vehicleType === VehicleTypes.HGV) { - return this.optionsService.getOptions(ReferenceDataResourceType.HgvMake); - } - if (this.techRecord.techRecord_vehicleType === VehicleTypes.PSV) { - return this.optionsService.getOptions(ReferenceDataResourceType.PsvMake); - } - return this.optionsService.getOptions(ReferenceDataResourceType.TrlMake); - } - - get dtpNumbers$(): Observable { - return combineLatest([ - this.referenceDataService.getAll$(ReferenceDataResourceType.PsvMake), - this.referenceDataService.getReferencePsvMakeDataLoading$(), - ]).pipe( - skipWhile(([, loading]) => loading), - take(1), - map(([data]) => { - return data?.map((option) => ({ value: option.resourceKey, label: option.resourceKey })) as MultiOptions; - }), - ); - } - - loadOptions(): void { - if (this.disableLoadOptions) return; - - if (this.techRecord.techRecord_vehicleType === VehicleTypes.HGV) { - this.optionsService.loadOptions(ReferenceDataResourceType.HgvMake); - } else if (this.techRecord.techRecord_vehicleType === VehicleTypes.PSV) { - this.optionsService.loadOptions(ReferenceDataResourceType.PsvMake); - } else { - this.optionsService.loadOptions(ReferenceDataResourceType.TrlMake); - } - } - - updateHgvVehicleBodyType(record: TechRecordVehicleType<'hgv'>) { - if (record.techRecord_vehicleConfiguration === 'articulated') { - this.store.dispatch(updateEditingTechRecord({ - vehicleTechRecord: { - ...this.techRecord, - techRecord_bodyType_description: 'articulated', - techRecord_bodyType_code: 'a', - } as TechRecordType<'put'>, - })); - } - } + @Input() techRecord!: V3TechRecordModel; + @Input() isEditing = false; + @Input() disableLoadOptions = false; + + @Output() formChange = new EventEmitter(); + + public form!: CustomFormGroup; + private template!: FormNode; + private destroy$ = new Subject(); + + constructor( + private dfs: DynamicFormService, + private optionsService: MultiOptionsService, + private referenceDataService: ReferenceDataService, + private store: Store + ) {} + + ngOnInit(): void { + this.template = + this.techRecord.techRecord_vehicleType === VehicleTypes.PSV ? PsvBodyTemplate : HgvAndTrlBodyTemplate; + this.form = this.dfs.createForm(this.template, this.techRecord) as CustomFormGroup; + this.form.cleanValueChanges + .pipe( + debounceTime(400), + takeUntil(this.destroy$), + // eslint-disable-next-line @typescript-eslint/no-explicit-any + mergeMap((event: any) => + this.store.pipe( + select( + selectReferenceDataByResourceKey(ReferenceDataResourceType.PsvMake, event.techRecord_brakes_dtpNumber) + ), + take(1), + map((referenceData) => [event, referenceData as PsvMake]) + ) + ) + ) + .subscribe(([event, psvMake]) => { + // Set the body type code automatically based selection + if (event?.techRecord_bodyType_description) { + // body type codes are specific to the vehicle type + const vehicleType = + this.techRecord.techRecord_vehicleType === 'hgv' + ? `${this.techRecord.techRecord_vehicleConfiguration}Hgv` + : this.techRecord.techRecord_vehicleType; + const bodyTypes = vehicleBodyTypeDescriptionMap.get(vehicleType as VehicleTypes) as Map< + BodyTypeDescription, + BodyTypeCode + >; + event.techRecord_bodyType_code = bodyTypes.get(event?.techRecord_bodyType_description); + } + + this.formChange.emit(event); + + if ( + this.techRecord.techRecord_vehicleType === VehicleTypes.PSV && + event?.techRecord_brakes_dtpNumber && + event.techRecord_brakes_dtpNumber.length >= 4 && + psvMake + ) { + this.store.dispatch(updateBody({ psvMake })); + } + }); + + this.loadOptions(); + } + + ngOnChanges(changes: SimpleChanges): void { + const { techRecord } = changes; + + if (this.form && techRecord?.currentValue && techRecord.currentValue !== techRecord.previousValue) { + this.form.patchValue(techRecord.currentValue, { emitEvent: false }); + } + } + + ngOnDestroy(): void { + this.destroy$.next(); + this.destroy$.complete(); + } + + get editTypes(): typeof FormNodeEditTypes { + return FormNodeEditTypes; + } + + get widths(): typeof FormNodeWidth { + return FormNodeWidth; + } + + get bodyTypes(): MultiOptions { + let vehicleType: string = this.techRecord.techRecord_vehicleType; + + if (this.techRecord.techRecord_vehicleType === 'hgv') { + vehicleType = `${this.techRecord.techRecord_vehicleConfiguration}Hgv`; + this.updateHgvVehicleBodyType(this.techRecord); + } + const optionsMap = vehicleBodyTypeCodeMap.get(vehicleType) ?? []; + const values = [...optionsMap.values()]; + return getOptionsFromEnum(values.sort()); + } + + get bodyMakes$(): Observable { + if (this.techRecord.techRecord_vehicleType === VehicleTypes.HGV) { + return this.optionsService.getOptions(ReferenceDataResourceType.HgvMake); + } + if (this.techRecord.techRecord_vehicleType === VehicleTypes.PSV) { + return this.optionsService.getOptions(ReferenceDataResourceType.PsvMake); + } + return this.optionsService.getOptions(ReferenceDataResourceType.TrlMake); + } + + get dtpNumbers$(): Observable { + return combineLatest([ + this.referenceDataService.getAll$(ReferenceDataResourceType.PsvMake), + this.referenceDataService.getReferencePsvMakeDataLoading$(), + ]).pipe( + skipWhile(([, loading]) => loading), + take(1), + map(([data]) => { + return data?.map((option) => ({ value: option.resourceKey, label: option.resourceKey })) as MultiOptions; + }) + ); + } + + loadOptions(): void { + if (this.disableLoadOptions) return; + + if (this.techRecord.techRecord_vehicleType === VehicleTypes.HGV) { + this.optionsService.loadOptions(ReferenceDataResourceType.HgvMake); + } else if (this.techRecord.techRecord_vehicleType === VehicleTypes.PSV) { + this.optionsService.loadOptions(ReferenceDataResourceType.PsvMake); + } else { + this.optionsService.loadOptions(ReferenceDataResourceType.TrlMake); + } + } + + updateHgvVehicleBodyType(record: TechRecordVehicleType<'hgv'>) { + if (record.techRecord_vehicleConfiguration === 'articulated') { + this.store.dispatch( + updateEditingTechRecord({ + vehicleTechRecord: { + ...this.techRecord, + techRecord_bodyType_description: 'articulated', + techRecord_bodyType_code: 'a', + } as TechRecordType<'put'>, + }) + ); + } + } } diff --git a/src/app/forms/custom-sections/custom-defect/custom-defect.component.spec.ts b/src/app/forms/custom-sections/custom-defect/custom-defect.component.spec.ts index 3fe253da22..919dbbe633 100644 --- a/src/app/forms/custom-sections/custom-defect/custom-defect.component.spec.ts +++ b/src/app/forms/custom-sections/custom-defect/custom-defect.component.spec.ts @@ -5,22 +5,22 @@ import { SharedModule } from '@shared/shared.module'; import { CustomDefectComponent } from './custom-defect.component'; describe('CustomDefectComponent', () => { - let component: CustomDefectComponent; - let fixture: ComponentFixture; + let component: CustomDefectComponent; + let fixture: ComponentFixture; - beforeEach(async () => { - await TestBed.configureTestingModule({ - declarations: [], - imports: [SharedModule, DynamicFormsModule], - }).compileComponents(); - }); + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [], + imports: [SharedModule, DynamicFormsModule], + }).compileComponents(); + }); - beforeEach(() => { - fixture = TestBed.createComponent(CustomDefectComponent); - component = fixture.componentInstance; - }); + beforeEach(() => { + fixture = TestBed.createComponent(CustomDefectComponent); + component = fixture.componentInstance; + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); }); diff --git a/src/app/forms/custom-sections/custom-defect/custom-defect.component.ts b/src/app/forms/custom-sections/custom-defect/custom-defect.component.ts index 548783da63..255faab4b3 100644 --- a/src/app/forms/custom-sections/custom-defect/custom-defect.component.ts +++ b/src/app/forms/custom-sections/custom-defect/custom-defect.component.ts @@ -1,17 +1,15 @@ -import { - Component, EventEmitter, Input, Output, -} from '@angular/core'; +import { Component, EventEmitter, Input, Output } from '@angular/core'; import { CustomFormGroup } from '@forms/services/dynamic-form.types'; @Component({ - selector: 'app-custom-defect[index][form]', - templateUrl: './custom-defect.component.html', - styleUrls: ['./custom-defect.component.scss'], + selector: 'app-custom-defect[index][form]', + templateUrl: './custom-defect.component.html', + styleUrls: ['./custom-defect.component.scss'], }) export class CustomDefectComponent { - @Input() form!: CustomFormGroup; - @Input() index!: number; - @Input() isEditing = false; - @Input() templateName?: string; - @Output() removeCustomDefect = new EventEmitter(); + @Input() form!: CustomFormGroup; + @Input() index!: number; + @Input() isEditing = false; + @Input() templateName?: string; + @Output() removeCustomDefect = new EventEmitter(); } diff --git a/src/app/forms/custom-sections/custom-defects/custom-defects.component.spec.ts b/src/app/forms/custom-sections/custom-defects/custom-defects.component.spec.ts index 60dd8d1d64..9c8e35ab47 100644 --- a/src/app/forms/custom-sections/custom-defects/custom-defects.component.spec.ts +++ b/src/app/forms/custom-sections/custom-defects/custom-defects.component.spec.ts @@ -1,6 +1,6 @@ import { HttpClientTestingModule } from '@angular/common/http/testing'; import { DebugElement } from '@angular/core'; -import { ComponentFixture, inject, TestBed } from '@angular/core/testing'; +import { ComponentFixture, TestBed, inject } from '@angular/core/testing'; import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { By } from '@angular/platform-browser'; import { RouterTestingModule } from '@angular/router/testing'; @@ -11,104 +11,104 @@ import { CustomDefectComponent } from '../custom-defect/custom-defect.component' import { CustomDefectsComponent } from './custom-defects.component'; describe('CustomDefectsComponent', () => { - let component: CustomDefectsComponent; - let fixture: ComponentFixture; - let el: DebugElement; + let component: CustomDefectsComponent; + let fixture: ComponentFixture; + let el: DebugElement; - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [FormsModule, ReactiveFormsModule, HttpClientTestingModule, RouterTestingModule], - declarations: [CustomDefectsComponent, CustomDefectComponent], - providers: [DynamicFormService, provideMockStore({})], - }).compileComponents(); - }); + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [FormsModule, ReactiveFormsModule, HttpClientTestingModule, RouterTestingModule], + declarations: [CustomDefectsComponent, CustomDefectComponent], + providers: [DynamicFormService, provideMockStore({})], + }).compileComponents(); + }); - beforeEach(() => { - fixture = TestBed.createComponent(CustomDefectsComponent); - component = fixture.componentInstance; - el = fixture.debugElement; - component.template = { name: 'test component', type: FormNodeTypes.GROUP }; - fixture.detectChanges(); - }); + beforeEach(() => { + fixture = TestBed.createComponent(CustomDefectsComponent); + component = fixture.componentInstance; + el = fixture.debugElement; + component.template = { name: 'test component', type: FormNodeTypes.GROUP }; + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); - it('should render correct header', () => { - expect(el.query(By.css('h2')).nativeElement.innerHTML).toBe('Custom Defects'); - }); + it('should render correct header', () => { + expect(el.query(By.css('h2')).nativeElement.innerHTML).toBe('Custom Defects'); + }); - describe('add and remove custom defects', () => { - const template = { - name: 'customDefectsSection', - label: 'Custom Defects', - type: 'group', - children: [ - { - name: 'testTypes', - label: 'Test Types', - type: 'array', - children: [ - { - name: '0', - type: 'group', - children: [ - { - name: 'customDefects', - type: 'array', - children: [ - { - name: '0', - type: 'group', - children: [ - { - name: 'referenceNumber', - label: 'Reference Number', - type: 'control', - }, - { - name: 'defectName', - label: 'Defect Name', - type: 'control', - }, - { - name: 'defectNotes', - label: 'Defect Notes', - type: 'control', - }, - ], - }, - ], - }, - ], - }, - ], - }, - ], - }; + describe('add and remove custom defects', () => { + const template = { + name: 'customDefectsSection', + label: 'Custom Defects', + type: 'group', + children: [ + { + name: 'testTypes', + label: 'Test Types', + type: 'array', + children: [ + { + name: '0', + type: 'group', + children: [ + { + name: 'customDefects', + type: 'array', + children: [ + { + name: '0', + type: 'group', + children: [ + { + name: 'referenceNumber', + label: 'Reference Number', + type: 'control', + }, + { + name: 'defectName', + label: 'Defect Name', + type: 'control', + }, + { + name: 'defectNotes', + label: 'Defect Notes', + type: 'control', + }, + ], + }, + ], + }, + ], + }, + ], + }, + ], + }; - const data = { - testTypes: [ - { - customDefects: [], - }, - ], - }; + const data = { + testTypes: [ + { + customDefects: [], + }, + ], + }; - it('should add new custom defect', inject([DynamicFormService], (dfs: DynamicFormService) => { - component.form = dfs.createForm(template, data) as CustomFormGroup; - expect(component.defectCount).toBe(0); - component.handleAddCustomDefect(); - expect(component.defectCount).toBe(1); - })); + it('should add new custom defect', inject([DynamicFormService], (dfs: DynamicFormService) => { + component.form = dfs.createForm(template, data) as CustomFormGroup; + expect(component.defectCount).toBe(0); + component.handleAddCustomDefect(); + expect(component.defectCount).toBe(1); + })); - it('should remove custom defect', inject([DynamicFormService], (dfs: DynamicFormService) => { - component.form = dfs.createForm(template, data) as CustomFormGroup; - component.handleAddCustomDefect(); - expect(component.defectCount).toBe(1); - component.handleRemoveDefect(0); - expect(component.defectCount).toBe(0); - })); - }); + it('should remove custom defect', inject([DynamicFormService], (dfs: DynamicFormService) => { + component.form = dfs.createForm(template, data) as CustomFormGroup; + component.handleAddCustomDefect(); + expect(component.defectCount).toBe(1); + component.handleRemoveDefect(0); + expect(component.defectCount).toBe(0); + })); + }); }); diff --git a/src/app/forms/custom-sections/custom-defects/custom-defects.component.ts b/src/app/forms/custom-sections/custom-defects/custom-defects.component.ts index c3dfd355ce..f8a254efbb 100644 --- a/src/app/forms/custom-sections/custom-defects/custom-defects.component.ts +++ b/src/app/forms/custom-sections/custom-defects/custom-defects.component.ts @@ -1,67 +1,67 @@ -import { - Component, EventEmitter, Input, OnDestroy, OnInit, Output, -} from '@angular/core'; +import { Component, EventEmitter, Input, OnDestroy, OnInit, Output } from '@angular/core'; import { CustomDefect, CustomDefects } from '@api/test-results'; import { DynamicFormService } from '@forms/services/dynamic-form.service'; import { CustomFormArray, CustomFormGroup, FormNode } from '@forms/services/dynamic-form.types'; import { Subscription } from 'rxjs'; @Component({ - selector: 'app-custom-defects[template]', - templateUrl: './custom-defects.component.html', - styleUrls: [], + selector: 'app-custom-defects[template]', + templateUrl: './custom-defects.component.html', + styleUrls: [], }) export class CustomDefectsComponent implements OnInit, OnDestroy { - @Input() isEditing = false; - @Input() template!: FormNode; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - @Input() data: any = {}; + @Input() isEditing = false; + @Input() template!: FormNode; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + @Input() data: any = {}; - @Output() formChange = new EventEmitter(); - form!: CustomFormGroup; + @Output() formChange = new EventEmitter(); + form!: CustomFormGroup; - private formSubscription = new Subscription(); - defectNameType?: string; + private formSubscription = new Subscription(); + defectNameType?: string; - constructor(private dfs: DynamicFormService) {} + constructor(private dfs: DynamicFormService) {} - ngOnInit(): void { - this.form = this.dfs.createForm(this.template, this.data) as CustomFormGroup; - this.formSubscription = this.form.cleanValueChanges.subscribe((event) => { - this.formChange.emit(event); - }); - this.defectNameType = this.template.name === 'additionalDefectsSection' ? 'Additional Defect' : 'Custom Defect'; - } + ngOnInit(): void { + this.form = this.dfs.createForm(this.template, this.data) as CustomFormGroup; + this.formSubscription = this.form.cleanValueChanges.subscribe((event) => { + this.formChange.emit(event); + }); + this.defectNameType = this.template.name === 'additionalDefectsSection' ? 'Additional Defect' : 'Custom Defect'; + } - ngOnDestroy(): void { - this.formSubscription.unsubscribe(); - } + ngOnDestroy(): void { + this.formSubscription.unsubscribe(); + } - get customDefectsForm() { - return this.form?.get(['testTypes', '0', 'customDefects']) as CustomFormArray; - } + get customDefectsForm() { + return this.form?.get(['testTypes', '0', 'customDefects']) as CustomFormArray; + } - getCustomDefectForm(i: number) { - return this.customDefectsForm?.controls[`${i}`] as CustomFormGroup; - } + getCustomDefectForm(i: number) { + return this.customDefectsForm?.controls[`${i}`] as CustomFormGroup; + } - trackByFn(index: number): number { - return index; - } + trackByFn(index: number): number { + return index; + } - get defectCount() { - return this.customDefectsForm?.controls.length; - } + get defectCount() { + return this.customDefectsForm?.controls.length; + } - get customDefects(): CustomDefects { - return this.customDefectsForm.controls.map((control) => (control as CustomFormGroup).getCleanValue(control as CustomFormGroup) as CustomDefect); - } + get customDefects(): CustomDefects { + return this.customDefectsForm.controls.map( + (control) => (control as CustomFormGroup).getCleanValue(control as CustomFormGroup) as CustomDefect + ); + } - handleRemoveDefect(index: number): void { - this.customDefectsForm.removeAt(index); - } + handleRemoveDefect(index: number): void { + this.customDefectsForm.removeAt(index); + } - handleAddCustomDefect() { - this.customDefectsForm.addControl(); - } + handleAddCustomDefect() { + this.customDefectsForm.addControl(); + } } diff --git a/src/app/forms/custom-sections/custom-form-control/custom-form-control.component.spec.ts b/src/app/forms/custom-sections/custom-form-control/custom-form-control.component.spec.ts index b113e722e9..41c767c278 100644 --- a/src/app/forms/custom-sections/custom-form-control/custom-form-control.component.spec.ts +++ b/src/app/forms/custom-sections/custom-form-control/custom-form-control.component.spec.ts @@ -3,21 +3,20 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { CustomFormControlComponent } from './custom-form-control.component'; describe('CustomFormControlComponent', () => { - let component: CustomFormControlComponent; - let fixture: ComponentFixture; + let component: CustomFormControlComponent; + let fixture: ComponentFixture; - beforeEach(async () => { - await TestBed.configureTestingModule({ - declarations: [CustomFormControlComponent], - }) - .compileComponents(); + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [CustomFormControlComponent], + }).compileComponents(); - fixture = TestBed.createComponent(CustomFormControlComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); + fixture = TestBed.createComponent(CustomFormControlComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); }); diff --git a/src/app/forms/custom-sections/custom-form-control/custom-form-control.component.ts b/src/app/forms/custom-sections/custom-form-control/custom-form-control.component.ts index 5602eb74b2..284fb8c158 100644 --- a/src/app/forms/custom-sections/custom-form-control/custom-form-control.component.ts +++ b/src/app/forms/custom-sections/custom-form-control/custom-form-control.component.ts @@ -6,28 +6,28 @@ import { FORM_INJECTION_TOKEN } from '@forms/components/dynamic-form-field/dynam import { CustomControl } from '@forms/services/dynamic-form.types'; @Component({ - selector: 'app-custom-form-control', - templateUrl: './custom-form-control.component.html', - providers: [ - { - provide: NG_VALUE_ACCESSOR, - useExisting: CustomFormControlComponent, - multi: true, - }, - ], + selector: 'app-custom-form-control', + templateUrl: './custom-form-control.component.html', + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: CustomFormControlComponent, + multi: true, + }, + ], }) export class CustomFormControlComponent extends BaseControlComponent { - protected form?: FormGroup; + protected form?: FormGroup; - override ngAfterContentInit(): void { - const injectedControl = this.injector.get(NgControl, null); - if (injectedControl) { - const ngControl = injectedControl.control as unknown as KeyValue; - if (ngControl.value) { - this.name = ngControl.key; - this.control = ngControl.value; - this.form = this.injector.get(FORM_INJECTION_TOKEN) as FormGroup; - } - } - } + override ngAfterContentInit(): void { + const injectedControl = this.injector.get(NgControl, null); + if (injectedControl) { + const ngControl = injectedControl.control as unknown as KeyValue; + if (ngControl.value) { + this.name = ngControl.key; + this.control = ngControl.value; + this.form = this.injector.get(FORM_INJECTION_TOKEN) as FormGroup; + } + } + } } diff --git a/src/app/forms/custom-sections/defect/defect.component.spec.ts b/src/app/forms/custom-sections/defect/defect.component.spec.ts index 9a0c7e10ba..7278bddcae 100644 --- a/src/app/forms/custom-sections/defect/defect.component.spec.ts +++ b/src/app/forms/custom-sections/defect/defect.component.spec.ts @@ -1,7 +1,5 @@ import { HttpClientTestingModule } from '@angular/common/http/testing'; -import { - ComponentFixture, fakeAsync, TestBed, tick, -} from '@angular/core/testing'; +import { ComponentFixture, TestBed, fakeAsync, tick } from '@angular/core/testing'; import { ActivatedRoute, Router } from '@angular/router'; import { RouterTestingModule } from '@angular/router/testing'; import { DynamicFormsModule } from '@forms/dynamic-forms.module'; @@ -15,237 +13,247 @@ import { VehicleTypes } from '@models/vehicle-tech-record.model'; import { MockStore, provideMockStore } from '@ngrx/store/testing'; import { SharedModule } from '@shared/shared.module'; import { defects, selectByImNumber } from '@store/defects'; -import { initialAppState, State } from '@store/index'; +import { State, initialAppState } from '@store/index'; import { selectRouteParams } from '@store/router/selectors/router.selectors'; -import { - createDefect, removeDefect, toEditOrNotToEdit, updateDefect, -} from '@store/test-records'; +import { createDefect, removeDefect, toEditOrNotToEdit, updateDefect } from '@store/test-records'; import { DefectComponent } from './defect.component'; describe('DefectComponent', () => { - let component: DefectComponent; - let fixture: ComponentFixture; - let router: Router; - let store: MockStore; - - const deficiency: Deficiency = { - deficiencyCategory: deficiencyCategory.Major, - deficiencyId: 'a', - deficiencySubId: '', - deficiencyText: 'missing.', - forVehicleType: [VehicleTypes.PSV], - ref: '1.1.a', - stdForProhibition: false, - }; - - const item: Item = { - deficiencies: [deficiency], - forVehicleType: [VehicleTypes.PSV], - itemDescription: 'A registration plate:', - itemNumber: 1, - }; - - const defect: Defect = { - additionalInfo: { - [VehicleTypes.PSV]: { - location: { - longitudinal: ['front', 'rear'], - }, - notes: true, - }, - }, - forVehicleType: [VehicleTypes.PSV], - imDescription: 'Registration Plate', - imNumber: 1, - items: [item], - }; - - const fakeActivatedRoute = { - snapshot: { data: { key: 'value' } }, - }; - - beforeEach(async () => { - await TestBed.configureTestingModule({ - declarations: [], - imports: [SharedModule, DynamicFormsModule, RouterTestingModule, HttpClientTestingModule], - providers: [{ provide: ActivatedRoute, useValue: fakeActivatedRoute }, provideMockStore({ initialState: initialAppState })], - }).compileComponents(); - - router = TestBed.inject(Router); - store = TestBed.inject(MockStore); - }); - - beforeEach(() => { - fixture = TestBed.createComponent(DefectComponent); - component = fixture.componentInstance; - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); - - it('should navigate back to test record', () => { - const navigateSpy = jest.spyOn(router, 'navigate').mockImplementation(() => Promise.resolve(true)); - component.navigateBack(); - expect(navigateSpy).toHaveBeenCalled(); - }); - - describe('should initialize info Dictionary', () => { - it('should initialize notes to true', fakeAsync(() => { - store.overrideSelector(selectRouteParams, { defectIndex: '0' }); - store.overrideSelector(toEditOrNotToEdit, { - vehicleType: VehicleTypes.PSV, - testTypes: [{ defects: [{ imNumber: 1, deficiencyCategory: deficiencyCategory.Major }] }], - } as TestResultModel); - store.overrideSelector(selectByImNumber(1, VehicleTypes.PSV), { imNumber: 1 } as Defect); - tick(); - fixture.detectChanges(); - - component.initializeInfoDictionary(defect); - expect(component.includeNotes).toBe(true); - })); - - it('should initialize info dictionary to the longitude', fakeAsync(() => { - store.overrideSelector(selectRouteParams, { defectIndex: '0' }); - store.overrideSelector(toEditOrNotToEdit, { - vehicleType: VehicleTypes.PSV, - testTypes: [{ defects: [{ imNumber: 1, deficiencyCategory: deficiencyCategory.Major }] }], - } as TestResultModel); - store.overrideSelector(selectByImNumber(1, VehicleTypes.PSV), { imNumber: 1 } as Defect); - tick(); - fixture.detectChanges(); - - component.initializeInfoDictionary(defect); - expect(component.infoDictionary).toEqual({ - longitudinal: [ - { - label: 'Front', - value: 'front', - }, - { - label: 'Rear', - value: 'rear', - }, - ], - }); - })); - }); - - describe('should initialize defect', () => { - it('should initialize defect using index', fakeAsync(() => { - store.overrideSelector(selectRouteParams, { defectIndex: '0' }); - store.overrideSelector(toEditOrNotToEdit, { - vehicleType: VehicleTypes.PSV, - testTypes: [{ defects: [{ imNumber: 1, deficiencyCategory: deficiencyCategory.Major }] }], - } as TestResultModel); - store.overrideSelector(selectByImNumber(1, VehicleTypes.PSV), { imNumber: 1 } as Defect); - tick(); - fixture.detectChanges(); - - expect(component.defect).toBeDefined(); - })); - - it('should initialize defect using ref', fakeAsync(() => { - store.overrideSelector(selectRouteParams, { ref: '1.1.a' }); - store.overrideSelector(toEditOrNotToEdit, { - vehicleType: VehicleTypes.PSV, - testTypes: [{ defects: [{ imNumber: 1, imDescription: 'desc', deficiencyCategory: deficiencyCategory.Major }] }], - } as TestResultModel); - store.overrideSelector(defects, [defect]); - tick(); - fixture.detectChanges(); - - expect(component.defect).toBeDefined(); - })); - }); - - describe('should get isDangerous', () => { - it('should return true when defect is dangerous', () => { - component.defect = { deficiencyCategory: deficiencyCategory.Dangerous }; - expect(component.isDangerous).toBe(true); - }); - it('should return false when defect is advisory', () => { - component.defect = { deficiencyCategory: deficiencyCategory.Advisory }; - expect(component.isDangerous).toBe(false); - }); - it('should return false when defect is major', () => { - component.defect = { deficiencyCategory: deficiencyCategory.Major }; - expect(component.isDangerous).toBe(false); - }); - it('should return false when defect is minor', () => { - component.defect = { deficiencyCategory: deficiencyCategory.Minor }; - expect(component.isDangerous).toBe(false); - }); - }); - - describe('should get isAdvisory', () => { - it('should return true when defect is advisory', () => { - component.defect = { deficiencyCategory: deficiencyCategory.Advisory }; - expect(component.isAdvisory).toBe(true); - }); - it('should return false when defect is dangerous', () => { - component.defect = { deficiencyCategory: deficiencyCategory.Dangerous }; - expect(component.isAdvisory).toBe(false); - }); - it('should return false when defect is major', () => { - component.defect = { deficiencyCategory: deficiencyCategory.Major }; - expect(component.isAdvisory).toBe(false); - }); - it('should return false when defect is minor', () => { - component.defect = { deficiencyCategory: deficiencyCategory.Minor }; - expect(component.isAdvisory).toBe(false); - }); - }); - - describe('should dispatch', () => { - it('should dispatch create defect action', fakeAsync(() => { - store.overrideSelector(selectRouteParams, { ref: '1.1.a' }); - store.overrideSelector(toEditOrNotToEdit, { - vehicleType: VehicleTypes.PSV, - testTypes: [{ defects: [{ imNumber: 1, imDescription: 'desc', deficiencyCategory: deficiencyCategory.Major }] }], - } as TestResultModel); - store.overrideSelector(defects, [defect]); - tick(); - fixture.detectChanges(); - - const dispatchSpy = jest.spyOn(store, 'dispatch'); - component.handleSubmit(); - - expect(dispatchSpy).toHaveBeenCalledWith(createDefect({ defect: component.form.getCleanValue(component.form) as TestResultDefect })); - })); - - it('should dispatch update defect action', fakeAsync(() => { - store.overrideSelector(selectRouteParams, { defectIndex: '0' }); - store.overrideSelector(toEditOrNotToEdit, { - vehicleType: VehicleTypes.PSV, - testTypes: [{ defects: [{ imNumber: 1, deficiencyCategory: deficiencyCategory.Major }] }], - } as TestResultModel); - store.overrideSelector(selectByImNumber(1, VehicleTypes.PSV), { imNumber: 1 } as Defect); - tick(); - fixture.detectChanges(); - - const dispatchSpy = jest.spyOn(store, 'dispatch'); - component.handleSubmit(); - - expect(dispatchSpy).toHaveBeenCalledWith( - updateDefect({ defect: component.form.getCleanValue(component.form) as TestResultDefect, index: component.index }), - ); - })); - - it('should dispatch delete defect action', fakeAsync(() => { - store.overrideSelector(selectRouteParams, { defectIndex: '0' }); - store.overrideSelector(toEditOrNotToEdit, { - vehicleType: VehicleTypes.PSV, - testTypes: [{ defects: [{ imNumber: 1, deficiencyCategory: deficiencyCategory.Major }] }], - } as TestResultModel); - store.overrideSelector(selectByImNumber(1, VehicleTypes.PSV), { imNumber: 1 } as Defect); - tick(); - fixture.detectChanges(); - - const dispatchSpy = jest.spyOn(store, 'dispatch'); - component.handleRemove(); - - expect(dispatchSpy).toHaveBeenCalledWith(removeDefect({ index: component.index })); - })); - }); + let component: DefectComponent; + let fixture: ComponentFixture; + let router: Router; + let store: MockStore; + + const deficiency: Deficiency = { + deficiencyCategory: deficiencyCategory.Major, + deficiencyId: 'a', + deficiencySubId: '', + deficiencyText: 'missing.', + forVehicleType: [VehicleTypes.PSV], + ref: '1.1.a', + stdForProhibition: false, + }; + + const item: Item = { + deficiencies: [deficiency], + forVehicleType: [VehicleTypes.PSV], + itemDescription: 'A registration plate:', + itemNumber: 1, + }; + + const defect: Defect = { + additionalInfo: { + [VehicleTypes.PSV]: { + location: { + longitudinal: ['front', 'rear'], + }, + notes: true, + }, + }, + forVehicleType: [VehicleTypes.PSV], + imDescription: 'Registration Plate', + imNumber: 1, + items: [item], + }; + + const fakeActivatedRoute = { + snapshot: { data: { key: 'value' } }, + }; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [], + imports: [SharedModule, DynamicFormsModule, RouterTestingModule, HttpClientTestingModule], + providers: [ + { provide: ActivatedRoute, useValue: fakeActivatedRoute }, + provideMockStore({ initialState: initialAppState }), + ], + }).compileComponents(); + + router = TestBed.inject(Router); + store = TestBed.inject(MockStore); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(DefectComponent); + component = fixture.componentInstance; + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + it('should navigate back to test record', () => { + const navigateSpy = jest.spyOn(router, 'navigate').mockImplementation(() => Promise.resolve(true)); + component.navigateBack(); + expect(navigateSpy).toHaveBeenCalled(); + }); + + describe('should initialize info Dictionary', () => { + it('should initialize notes to true', fakeAsync(() => { + store.overrideSelector(selectRouteParams, { defectIndex: '0' }); + store.overrideSelector(toEditOrNotToEdit, { + vehicleType: VehicleTypes.PSV, + testTypes: [{ defects: [{ imNumber: 1, deficiencyCategory: deficiencyCategory.Major }] }], + } as TestResultModel); + store.overrideSelector(selectByImNumber(1, VehicleTypes.PSV), { imNumber: 1 } as Defect); + tick(); + fixture.detectChanges(); + + component.initializeInfoDictionary(defect); + expect(component.includeNotes).toBe(true); + })); + + it('should initialize info dictionary to the longitude', fakeAsync(() => { + store.overrideSelector(selectRouteParams, { defectIndex: '0' }); + store.overrideSelector(toEditOrNotToEdit, { + vehicleType: VehicleTypes.PSV, + testTypes: [{ defects: [{ imNumber: 1, deficiencyCategory: deficiencyCategory.Major }] }], + } as TestResultModel); + store.overrideSelector(selectByImNumber(1, VehicleTypes.PSV), { imNumber: 1 } as Defect); + tick(); + fixture.detectChanges(); + + component.initializeInfoDictionary(defect); + expect(component.infoDictionary).toEqual({ + longitudinal: [ + { + label: 'Front', + value: 'front', + }, + { + label: 'Rear', + value: 'rear', + }, + ], + }); + })); + }); + + describe('should initialize defect', () => { + it('should initialize defect using index', fakeAsync(() => { + store.overrideSelector(selectRouteParams, { defectIndex: '0' }); + store.overrideSelector(toEditOrNotToEdit, { + vehicleType: VehicleTypes.PSV, + testTypes: [{ defects: [{ imNumber: 1, deficiencyCategory: deficiencyCategory.Major }] }], + } as TestResultModel); + store.overrideSelector(selectByImNumber(1, VehicleTypes.PSV), { imNumber: 1 } as Defect); + tick(); + fixture.detectChanges(); + + expect(component.defect).toBeDefined(); + })); + + it('should initialize defect using ref', fakeAsync(() => { + store.overrideSelector(selectRouteParams, { ref: '1.1.a' }); + store.overrideSelector(toEditOrNotToEdit, { + vehicleType: VehicleTypes.PSV, + testTypes: [ + { defects: [{ imNumber: 1, imDescription: 'desc', deficiencyCategory: deficiencyCategory.Major }] }, + ], + } as TestResultModel); + store.overrideSelector(defects, [defect]); + tick(); + fixture.detectChanges(); + + expect(component.defect).toBeDefined(); + })); + }); + + describe('should get isDangerous', () => { + it('should return true when defect is dangerous', () => { + component.defect = { deficiencyCategory: deficiencyCategory.Dangerous }; + expect(component.isDangerous).toBe(true); + }); + it('should return false when defect is advisory', () => { + component.defect = { deficiencyCategory: deficiencyCategory.Advisory }; + expect(component.isDangerous).toBe(false); + }); + it('should return false when defect is major', () => { + component.defect = { deficiencyCategory: deficiencyCategory.Major }; + expect(component.isDangerous).toBe(false); + }); + it('should return false when defect is minor', () => { + component.defect = { deficiencyCategory: deficiencyCategory.Minor }; + expect(component.isDangerous).toBe(false); + }); + }); + + describe('should get isAdvisory', () => { + it('should return true when defect is advisory', () => { + component.defect = { deficiencyCategory: deficiencyCategory.Advisory }; + expect(component.isAdvisory).toBe(true); + }); + it('should return false when defect is dangerous', () => { + component.defect = { deficiencyCategory: deficiencyCategory.Dangerous }; + expect(component.isAdvisory).toBe(false); + }); + it('should return false when defect is major', () => { + component.defect = { deficiencyCategory: deficiencyCategory.Major }; + expect(component.isAdvisory).toBe(false); + }); + it('should return false when defect is minor', () => { + component.defect = { deficiencyCategory: deficiencyCategory.Minor }; + expect(component.isAdvisory).toBe(false); + }); + }); + + describe('should dispatch', () => { + it('should dispatch create defect action', fakeAsync(() => { + store.overrideSelector(selectRouteParams, { ref: '1.1.a' }); + store.overrideSelector(toEditOrNotToEdit, { + vehicleType: VehicleTypes.PSV, + testTypes: [ + { defects: [{ imNumber: 1, imDescription: 'desc', deficiencyCategory: deficiencyCategory.Major }] }, + ], + } as TestResultModel); + store.overrideSelector(defects, [defect]); + tick(); + fixture.detectChanges(); + + const dispatchSpy = jest.spyOn(store, 'dispatch'); + component.handleSubmit(); + + expect(dispatchSpy).toHaveBeenCalledWith( + createDefect({ defect: component.form.getCleanValue(component.form) as TestResultDefect }) + ); + })); + + it('should dispatch update defect action', fakeAsync(() => { + store.overrideSelector(selectRouteParams, { defectIndex: '0' }); + store.overrideSelector(toEditOrNotToEdit, { + vehicleType: VehicleTypes.PSV, + testTypes: [{ defects: [{ imNumber: 1, deficiencyCategory: deficiencyCategory.Major }] }], + } as TestResultModel); + store.overrideSelector(selectByImNumber(1, VehicleTypes.PSV), { imNumber: 1 } as Defect); + tick(); + fixture.detectChanges(); + + const dispatchSpy = jest.spyOn(store, 'dispatch'); + component.handleSubmit(); + + expect(dispatchSpy).toHaveBeenCalledWith( + updateDefect({ + defect: component.form.getCleanValue(component.form) as TestResultDefect, + index: component.index, + }) + ); + })); + + it('should dispatch delete defect action', fakeAsync(() => { + store.overrideSelector(selectRouteParams, { defectIndex: '0' }); + store.overrideSelector(toEditOrNotToEdit, { + vehicleType: VehicleTypes.PSV, + testTypes: [{ defects: [{ imNumber: 1, deficiencyCategory: deficiencyCategory.Major }] }], + } as TestResultModel); + store.overrideSelector(selectByImNumber(1, VehicleTypes.PSV), { imNumber: 1 } as Defect); + tick(); + fixture.detectChanges(); + + const dispatchSpy = jest.spyOn(store, 'dispatch'); + component.handleRemove(); + + expect(dispatchSpy).toHaveBeenCalledWith(removeDefect({ index: component.index })); + })); + }); }); diff --git a/src/app/forms/custom-sections/defect/defect.component.ts b/src/app/forms/custom-sections/defect/defect.component.ts index 6b967e408a..270dee2d2b 100644 --- a/src/app/forms/custom-sections/defect/defect.component.ts +++ b/src/app/forms/custom-sections/defect/defect.component.ts @@ -1,7 +1,5 @@ import { KeyValue } from '@angular/common'; -import { - ChangeDetectionStrategy, Component, OnDestroy, OnInit, -} from '@angular/core'; +import { ChangeDetectionStrategy, Component, OnDestroy, OnInit } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; import { GlobalError } from '@core/components/global-error/global-error.interface'; import { GlobalErrorService } from '@core/components/global-error/global-error.service'; @@ -21,216 +19,222 @@ import { DefaultNullOrEmpty } from '@shared/pipes/default-null-or-empty/default- import { selectByDeficiencyRef, selectByImNumber } from '@store/defects'; import { State } from '@store/index'; import { selectRouteParam } from '@store/router/selectors/router.selectors'; -import { - createDefect, removeDefect, testResultInEdit, toEditOrNotToEdit, updateDefect, -} from '@store/test-records'; -import { - Subject, filter, take, takeUntil, withLatestFrom, -} from 'rxjs'; +import { createDefect, removeDefect, testResultInEdit, toEditOrNotToEdit, updateDefect } from '@store/test-records'; +import { Subject, filter, take, takeUntil, withLatestFrom } from 'rxjs'; @Component({ - selector: 'app-defect', - templateUrl: './defect.component.html', - styleUrls: ['./defect.component.scss'], - providers: [DefaultNullOrEmpty], - changeDetection: ChangeDetectionStrategy.OnPush, + selector: 'app-defect', + templateUrl: './defect.component.html', + styleUrls: ['./defect.component.scss'], + providers: [DefaultNullOrEmpty], + changeDetection: ChangeDetectionStrategy.OnPush, }) export class DefectComponent implements OnInit, OnDestroy { - form!: CustomFormGroup; - index!: number; - isEditing: boolean; - includeNotes = false; - private vehicleType?: VehicleTypes; - - private defectsForm?: CustomFormArray; - private defects?: TestResultDefects; - defect?: TestResultDefect; - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - infoDictionary: Record>> = {}; - onDestroy$ = new Subject(); - - booleanOptions: FormNodeOption[] = [ - { value: true, label: 'Yes' }, - { value: false, label: 'No' }, - ]; - - constructor( - private activatedRoute: ActivatedRoute, - private dfs: DynamicFormService, - private router: Router, - private store: Store, - private resultService: ResultOfTestService, - private errorService: GlobalErrorService, - ) { - this.isEditing = this.activatedRoute.snapshot.data['isEditing']; - } - - ngOnInit(): void { - const defectIndex = this.store.pipe(select(selectRouteParam('defectIndex'))); - const defectRef = this.store.pipe(select(selectRouteParam('ref'))); - - this.store - .select(this.isEditing ? testResultInEdit : toEditOrNotToEdit) - .pipe( - withLatestFrom(defectIndex, defectRef), - takeUntil(this.onDestroy$), - filter(([testResult]) => !!testResult), - ) - .subscribe(([testResult, defectIndexValue, defectRefValue]) => { - if (!testResult) this.navigateBack(); - this.defects = testResult?.testTypes[0].defects; - this.vehicleType = testResult?.vehicleType; - this.defectsForm = (this.dfs.createForm(DefectsTpl, testResult) as CustomFormGroup).get(['testTypes', '0', 'defects']) as CustomFormArray; - if (defectIndexValue) { - this.index = Number(defectIndexValue); - this.form = this.defectsForm.controls[this.index] as CustomFormGroup; - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - this.defect = this.defects![this.index]; - } else if (defectRefValue && this.vehicleType) { - this.store - .select(selectByDeficiencyRef(defectRefValue, this.vehicleType)) - .pipe(take(1)) - .subscribe(([defect, item, deficiency]) => { - this.initializeDefect(defect as Defect, item as Item, deficiency as Deficiency); - }); - } - }); - - if (!this.defect) this.navigateBack(); - - if (this.vehicleType) { - this.store - .select(selectByImNumber(this.defect?.imNumber || NaN, this.vehicleType)) - .pipe( - takeUntil(this.onDestroy$), - filter((d) => !!d), - ) - .subscribe((defectsTaxonomy) => { - this.initializeInfoDictionary(defectsTaxonomy); - }); - } - } - - ngOnDestroy(): void { - this.onDestroy$.next(true); - this.onDestroy$.complete(); - } - - get isDangerous(): boolean { - return this.defect?.deficiencyCategory === 'dangerous'; - } - - get isAdvisory(): boolean { - return this.defect?.deficiencyCategory === 'advisory'; - } - - get isDangerousAsterisk(): boolean { - return this.defect?.stdForProhibition === true; - } - - handleSubmit() { - const errors: GlobalError[] = []; - DynamicFormService.validate(this.form, errors); - - if (errors.length > 0) { - this.errorService.setErrors(errors); - } - - if (this.form.invalid) { - return; - } - - if (this.index || this.index === 0) { - this.store.dispatch(updateDefect({ defect: this.form.getCleanValue(this.form) as TestResultDefect, index: this.index })); - } else { - this.store.dispatch(createDefect({ defect: this.form.getCleanValue(this.form) as TestResultDefect })); - } - - this.navigateBack(); - } - - handleRemove() { - this.store.dispatch(removeDefect({ index: this.index })); - this.navigateBack(); - } - - navigateBack() { - this.resultService.updateResultOfTest(); - void this.router.navigate(['../..'], { relativeTo: this.activatedRoute, queryParamsHandling: 'preserve' }); - } - - toggleDefectField(field: keyof TestResultDefect) { - if (!this.defect) { - return; - } - this.defect = { ...this.defect, [field]: !this.defect[`${field}`] } as TestResultDefect; - this.defectsForm?.controls[this.index ?? this.defectsForm.length - 1].get(field)?.patchValue(this.defect[`${field}`]); - } - - initializeInfoDictionary(defect: Defect | undefined) { - const infoShorthand = defect?.additionalInfo; - - const info = defect?.additionalInfo[this.vehicleType as keyof typeof infoShorthand] as AdditionalInfoSection | undefined; - - this.includeNotes = !!info?.notes; - - if (info) { - type LocationKey = keyof typeof info.location; - - Object.keys(info.location).forEach((key) => { - const options = info?.location[key as LocationKey]; - if (options) { - this.infoDictionary[`${key}`] = this.mapOptions(options); - } - }); - } - } - - initializeDefect(defect: Defect, item: Item, deficiency: Deficiency) { - const testResultDefect: TestResultDefect = { - imDescription: defect.imDescription, - imNumber: defect.imNumber, - - itemDescription: item.itemDescription, - itemNumber: item.itemNumber, - - // initializing if defect is advisory - deficiencyCategory: DeficiencyCategoryEnum.Advisory, - deficiencyRef: `${defect.imNumber}.${item.itemNumber}`, - prohibitionIssued: false, - stdForProhibition: false, - }; - - if (deficiency) { - testResultDefect.deficiencyCategory = deficiency.deficiencyCategory; - testResultDefect.deficiencyId = deficiency.deficiencyId; - testResultDefect.deficiencySubId = deficiency.deficiencySubId; - testResultDefect.deficiencyText = deficiency.deficiencyText; - testResultDefect.deficiencyRef = deficiency.ref; - testResultDefect.stdForProhibition = deficiency.stdForProhibition; - } else if (item.itemDescription.endsWith(':')) { - testResultDefect.itemDescription = item.itemDescription.slice(0, -1); - } - - this.defectsForm?.addControl(testResultDefect); - this.form = this.defectsForm?.controls[this.defectsForm.length - 1] as CustomFormGroup; - this.defect = testResultDefect; - } - - categoryColor(category = 'major'): 'red' | 'orange' | 'yellow' | 'green' | 'blue' { - return (>{ - major: 'orange', - minor: 'yellow', - dangerous: 'red', - advisory: 'blue', - })[`${category}`]; - } - - trackByFn = (_index: number, keyValuePair: KeyValue>): string => keyValuePair.key; - - mapOptions = (options: Array): Array> => - options.map((option) => ({ value: option, label: this.pascalCase(String(option)) })); - - pascalCase = (s: string): string => s.charAt(0).toUpperCase() + s.slice(1).replace(/([A-Z])/g, ' $1'); + form!: CustomFormGroup; + index!: number; + isEditing: boolean; + includeNotes = false; + private vehicleType?: VehicleTypes; + + private defectsForm?: CustomFormArray; + private defects?: TestResultDefects; + defect?: TestResultDefect; + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + infoDictionary: Record>> = {}; + onDestroy$ = new Subject(); + + booleanOptions: FormNodeOption[] = [ + { value: true, label: 'Yes' }, + { value: false, label: 'No' }, + ]; + + constructor( + private activatedRoute: ActivatedRoute, + private dfs: DynamicFormService, + private router: Router, + private store: Store, + private resultService: ResultOfTestService, + private errorService: GlobalErrorService + ) { + this.isEditing = this.activatedRoute.snapshot.data['isEditing']; + } + + ngOnInit(): void { + const defectIndex = this.store.pipe(select(selectRouteParam('defectIndex'))); + const defectRef = this.store.pipe(select(selectRouteParam('ref'))); + + this.store + .select(this.isEditing ? testResultInEdit : toEditOrNotToEdit) + .pipe( + withLatestFrom(defectIndex, defectRef), + takeUntil(this.onDestroy$), + filter(([testResult]) => !!testResult) + ) + .subscribe(([testResult, defectIndexValue, defectRefValue]) => { + if (!testResult) this.navigateBack(); + this.defects = testResult?.testTypes[0].defects; + this.vehicleType = testResult?.vehicleType; + this.defectsForm = (this.dfs.createForm(DefectsTpl, testResult) as CustomFormGroup).get([ + 'testTypes', + '0', + 'defects', + ]) as CustomFormArray; + if (defectIndexValue) { + this.index = Number(defectIndexValue); + this.form = this.defectsForm.controls[this.index] as CustomFormGroup; + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + this.defect = this.defects![this.index]; + } else if (defectRefValue && this.vehicleType) { + this.store + .select(selectByDeficiencyRef(defectRefValue, this.vehicleType)) + .pipe(take(1)) + .subscribe(([defect, item, deficiency]) => { + this.initializeDefect(defect as Defect, item as Item, deficiency as Deficiency); + }); + } + }); + + if (!this.defect) this.navigateBack(); + + if (this.vehicleType) { + this.store + .select(selectByImNumber(this.defect?.imNumber || Number.NaN, this.vehicleType)) + .pipe( + takeUntil(this.onDestroy$), + filter((d) => !!d) + ) + .subscribe((defectsTaxonomy) => { + this.initializeInfoDictionary(defectsTaxonomy); + }); + } + } + + ngOnDestroy(): void { + this.onDestroy$.next(true); + this.onDestroy$.complete(); + } + + get isDangerous(): boolean { + return this.defect?.deficiencyCategory === 'dangerous'; + } + + get isAdvisory(): boolean { + return this.defect?.deficiencyCategory === 'advisory'; + } + + get isDangerousAsterisk(): boolean { + return this.defect?.stdForProhibition === true; + } + + handleSubmit() { + const errors: GlobalError[] = []; + DynamicFormService.validate(this.form, errors); + + if (errors.length > 0) { + this.errorService.setErrors(errors); + } + + if (this.form.invalid) { + return; + } + + if (this.index || this.index === 0) { + this.store.dispatch( + updateDefect({ defect: this.form.getCleanValue(this.form) as TestResultDefect, index: this.index }) + ); + } else { + this.store.dispatch(createDefect({ defect: this.form.getCleanValue(this.form) as TestResultDefect })); + } + + this.navigateBack(); + } + + handleRemove() { + this.store.dispatch(removeDefect({ index: this.index })); + this.navigateBack(); + } + + navigateBack() { + this.resultService.updateResultOfTest(); + void this.router.navigate(['../..'], { relativeTo: this.activatedRoute, queryParamsHandling: 'preserve' }); + } + + toggleDefectField(field: keyof TestResultDefect) { + if (!this.defect) { + return; + } + this.defect = { ...this.defect, [field]: !this.defect[`${field}`] } as TestResultDefect; + this.defectsForm?.controls[this.index ?? this.defectsForm.length - 1] + .get(field) + ?.patchValue(this.defect[`${field}`]); + } + + initializeInfoDictionary(defect: Defect | undefined) { + const infoShorthand = defect?.additionalInfo; + + const info = defect?.additionalInfo[this.vehicleType as keyof typeof infoShorthand] as + | AdditionalInfoSection + | undefined; + + this.includeNotes = !!info?.notes; + + if (info) { + type LocationKey = keyof typeof info.location; + + Object.keys(info.location).forEach((key) => { + const options = info?.location[key as LocationKey]; + if (options) { + this.infoDictionary[`${key}`] = this.mapOptions(options); + } + }); + } + } + + initializeDefect(defect: Defect, item: Item, deficiency: Deficiency) { + const testResultDefect: TestResultDefect = { + imDescription: defect.imDescription, + imNumber: defect.imNumber, + + itemDescription: item.itemDescription, + itemNumber: item.itemNumber, + + // initializing if defect is advisory + deficiencyCategory: DeficiencyCategoryEnum.Advisory, + deficiencyRef: `${defect.imNumber}.${item.itemNumber}`, + prohibitionIssued: false, + stdForProhibition: false, + }; + + if (deficiency) { + testResultDefect.deficiencyCategory = deficiency.deficiencyCategory; + testResultDefect.deficiencyId = deficiency.deficiencyId; + testResultDefect.deficiencySubId = deficiency.deficiencySubId; + testResultDefect.deficiencyText = deficiency.deficiencyText; + testResultDefect.deficiencyRef = deficiency.ref; + testResultDefect.stdForProhibition = deficiency.stdForProhibition; + } else if (item.itemDescription.endsWith(':')) { + testResultDefect.itemDescription = item.itemDescription.slice(0, -1); + } + + this.defectsForm?.addControl(testResultDefect); + this.form = this.defectsForm?.controls[this.defectsForm.length - 1] as CustomFormGroup; + this.defect = testResultDefect; + } + + categoryColor(category = 'major'): 'red' | 'orange' | 'yellow' | 'green' | 'blue' { + return (>{ + major: 'orange', + minor: 'yellow', + dangerous: 'red', + advisory: 'blue', + })[`${category}`]; + } + + trackByFn = (_index: number, keyValuePair: KeyValue>): string => keyValuePair.key; + + mapOptions = (options: Array): Array> => + options.map((option) => ({ value: option, label: this.pascalCase(String(option)) })); + + pascalCase = (s: string): string => s.charAt(0).toUpperCase() + s.slice(1).replace(/([A-Z])/g, ' $1'); } diff --git a/src/app/forms/custom-sections/defects/defects.component.spec.ts b/src/app/forms/custom-sections/defects/defects.component.spec.ts index 55a8fdb260..57ecf9db38 100644 --- a/src/app/forms/custom-sections/defects/defects.component.spec.ts +++ b/src/app/forms/custom-sections/defects/defects.component.spec.ts @@ -1,8 +1,6 @@ import { HttpClientTestingModule } from '@angular/common/http/testing'; import { DebugElement } from '@angular/core'; -import { - ComponentFixture, fakeAsync, TestBed, tick, -} from '@angular/core/testing'; +import { ComponentFixture, TestBed, fakeAsync, tick } from '@angular/core/testing'; import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { By } from '@angular/platform-browser'; import { RouterTestingModule } from '@angular/router/testing'; @@ -17,48 +15,55 @@ import { DefectComponent } from '../defect/defect.component'; import { DefectsComponent } from './defects.component'; describe('DefectsComponent', () => { - let component: DefectsComponent; - let fixture: ComponentFixture; - let el: DebugElement; + let component: DefectsComponent; + let fixture: ComponentFixture; + let el: DebugElement; - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [FormsModule, ReactiveFormsModule, RouterTestingModule, HttpClientTestingModule], - declarations: [DefectComponent, DefectSelectComponent, DefectsComponent, ButtonComponent, TruncatePipe, TagComponent], - providers: [DynamicFormService, provideMockStore({ initialState: initialAppState })], - }).compileComponents(); - }); + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [FormsModule, ReactiveFormsModule, RouterTestingModule, HttpClientTestingModule], + declarations: [ + DefectComponent, + DefectSelectComponent, + DefectsComponent, + ButtonComponent, + TruncatePipe, + TagComponent, + ], + providers: [DynamicFormService, provideMockStore({ initialState: initialAppState })], + }).compileComponents(); + }); - beforeEach(() => { - fixture = TestBed.createComponent(DefectsComponent); - component = fixture.componentInstance; - el = fixture.debugElement; - }); + beforeEach(() => { + fixture = TestBed.createComponent(DefectsComponent); + component = fixture.componentInstance; + el = fixture.debugElement; + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); - it('should render correct header', () => { - fixture.detectChanges(); - expect(el.query(By.css('h2')).nativeElement.innerHTML).toBe('Defects'); - }); + it('should render correct header', () => { + fixture.detectChanges(); + expect(el.query(By.css('h2')).nativeElement.innerHTML).toBe('Defects'); + }); - describe('No defects', () => { - it('should be displayed when defects is undefined or empty array', fakeAsync(() => { - const expectedText = 'No defects'; + describe('No defects', () => { + it('should be displayed when defects is undefined or empty array', fakeAsync(() => { + const expectedText = 'No defects'; - tick(); - fixture.detectChanges(); + tick(); + fixture.detectChanges(); - let text: HTMLParagraphElement = el.query(By.css('p')).nativeElement; - expect(text.innerHTML).toBe(expectedText); + let text: HTMLParagraphElement = el.query(By.css('p')).nativeElement; + expect(text.innerHTML).toBe(expectedText); - tick(); - fixture.detectChanges(); + tick(); + fixture.detectChanges(); - text = el.query(By.css('p')).nativeElement; - expect(text.innerHTML).toBe(expectedText); - })); - }); + text = el.query(By.css('p')).nativeElement; + expect(text.innerHTML).toBe(expectedText); + })); + }); }); diff --git a/src/app/forms/custom-sections/defects/defects.component.ts b/src/app/forms/custom-sections/defects/defects.component.ts index d1a359c0b3..6aa682007a 100644 --- a/src/app/forms/custom-sections/defects/defects.component.ts +++ b/src/app/forms/custom-sections/defects/defects.component.ts @@ -1,6 +1,4 @@ -import { - Component, EventEmitter, Input, OnDestroy, OnInit, Output, -} from '@angular/core'; +import { Component, EventEmitter, Input, OnDestroy, OnInit, Output } from '@angular/core'; import { DynamicFormService } from '@forms/services/dynamic-form.service'; import { CustomFormArray, CustomFormGroup, FormNode } from '@forms/services/dynamic-form.types'; import { Defect } from '@models/defects/defect.model'; @@ -9,59 +7,62 @@ import { TestResultModel } from '@models/test-results/test-result.model'; import { Subscription, debounceTime } from 'rxjs'; @Component({ - selector: 'app-defects[defects][template]', - templateUrl: './defects.component.html', + selector: 'app-defects[defects][template]', + templateUrl: './defects.component.html', }) export class DefectsComponent implements OnInit, OnDestroy { - @Input() isEditing = false; - @Input() defects!: Defect[] | null; - @Input() template!: FormNode; - @Input() data: Partial = {}; + @Input() isEditing = false; + @Input() defects!: Defect[] | null; + @Input() template!: FormNode; + @Input() data: Partial = {}; - @Output() formChange = new EventEmitter(); + @Output() formChange = new EventEmitter(); - public form!: CustomFormGroup; - private formSubscription = new Subscription(); - private defectsFormArray?: CustomFormArray; + public form!: CustomFormGroup; + private formSubscription = new Subscription(); + private defectsFormArray?: CustomFormArray; - constructor(private dfs: DynamicFormService) {} + constructor(private dfs: DynamicFormService) {} - ngOnInit(): void { - this.form = this.dfs.createForm(this.template, this.data) as CustomFormGroup; - this.formSubscription = this.form.cleanValueChanges.pipe(debounceTime(400)).subscribe((event) => { - this.formChange.emit(event); - }); - } + ngOnInit(): void { + this.form = this.dfs.createForm(this.template, this.data) as CustomFormGroup; + this.formSubscription = this.form.cleanValueChanges.pipe(debounceTime(400)).subscribe((event) => { + this.formChange.emit(event); + }); + } - ngOnDestroy(): void { - this.formSubscription.unsubscribe(); - } + ngOnDestroy(): void { + this.formSubscription.unsubscribe(); + } - get defectsForm(): CustomFormArray { - if (!this.defectsFormArray) { - this.defectsFormArray = this.form?.get(['testTypes', '0', 'defects']) as CustomFormArray; - } - return this.defectsFormArray; - } + get defectsForm(): CustomFormArray { + if (!this.defectsFormArray) { + this.defectsFormArray = this.form?.get(['testTypes', '0', 'defects']) as CustomFormArray; + } + return this.defectsFormArray; + } - get defectCount(): number { - return this.defectsForm?.controls.length; - } + get defectCount(): number { + return this.defectsForm?.controls.length; + } - get testDefects(): TestResultDefect[] { - return this.defectsForm.controls.map((control) => { - const formGroup = control as CustomFormGroup; - return formGroup.getCleanValue(formGroup) as TestResultDefect; - }); - } + get testDefects(): TestResultDefect[] { + return this.defectsForm.controls.map((control) => { + const formGroup = control as CustomFormGroup; + return formGroup.getCleanValue(formGroup) as TestResultDefect; + }); + } - categoryColor(category: CategoryColorKey): CategoryColor { - return categoryColors[`${category}`]; - } + categoryColor(category: CategoryColorKey): CategoryColor { + return categoryColors[`${category}`]; + } } const categoryColors = { - major: 'orange', minor: 'yellow', dangerous: 'red', advisory: 'blue', + major: 'orange', + minor: 'yellow', + dangerous: 'red', + advisory: 'blue', } as const; type CategoryColors = typeof categoryColors; diff --git a/src/app/forms/custom-sections/dimensions/dimensions.component.spec.ts b/src/app/forms/custom-sections/dimensions/dimensions.component.spec.ts index 1e57c7070f..3d0817b91b 100644 --- a/src/app/forms/custom-sections/dimensions/dimensions.component.spec.ts +++ b/src/app/forms/custom-sections/dimensions/dimensions.component.spec.ts @@ -10,23 +10,23 @@ import { initialAppState } from '@store/index'; import { DimensionsComponent } from './dimensions.component'; describe('DimensionsComponent', () => { - let component: DimensionsComponent; - let fixture: ComponentFixture; + let component: DimensionsComponent; + let fixture: ComponentFixture; - beforeEach(async () => { - await TestBed.configureTestingModule({ - declarations: [DimensionsComponent], - imports: [DynamicFormsModule, FormsModule, ReactiveFormsModule, HttpClientTestingModule, RouterTestingModule], - providers: [provideMockStore({ initialState: initialAppState })], - }).compileComponents(); - }); + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [DimensionsComponent], + imports: [DynamicFormsModule, FormsModule, ReactiveFormsModule, HttpClientTestingModule, RouterTestingModule], + providers: [provideMockStore({ initialState: initialAppState })], + }).compileComponents(); + }); - beforeEach(() => { - fixture = TestBed.createComponent(DimensionsComponent); - component = fixture.componentInstance; - component.techRecord = mockVehicleTechnicalRecord('psv') as TechRecordType<'hgv'>; - }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + beforeEach(() => { + fixture = TestBed.createComponent(DimensionsComponent); + component = fixture.componentInstance; + component.techRecord = mockVehicleTechnicalRecord('psv') as TechRecordType<'hgv'>; + }); + it('should create', () => { + expect(component).toBeTruthy(); + }); }); diff --git a/src/app/forms/custom-sections/dimensions/dimensions.component.ts b/src/app/forms/custom-sections/dimensions/dimensions.component.ts index 1c0dfb467a..79dc58a25a 100644 --- a/src/app/forms/custom-sections/dimensions/dimensions.component.ts +++ b/src/app/forms/custom-sections/dimensions/dimensions.component.ts @@ -1,10 +1,12 @@ -import { - Component, EventEmitter, Input, OnChanges, OnDestroy, OnInit, Output, SimpleChanges, -} from '@angular/core'; +import { Component, EventEmitter, Input, OnChanges, OnDestroy, OnInit, Output, SimpleChanges } from '@angular/core'; import { TechRecordType } from '@dvsa/cvs-type-definitions/types/v3/tech-record/tech-record-vehicle-type'; import { DynamicFormService } from '@forms/services/dynamic-form.service'; import { - CustomFormArray, CustomFormGroup, FormNode, FormNodeEditTypes, FormNodeWidth, + CustomFormArray, + CustomFormGroup, + FormNode, + FormNodeEditTypes, + FormNodeWidth, } from '@forms/services/dynamic-form.types'; import { HgvDimensionsTemplate } from '@forms/templates/hgv/hgv-dimensions.template'; import { PsvDimensionsTemplate } from '@forms/templates/psv/psv-dimensions.template'; @@ -13,86 +15,88 @@ import { VehicleTypes } from '@models/vehicle-tech-record.model'; import { Subject, debounceTime, takeUntil } from 'rxjs'; @Component({ - selector: 'app-dimensions', - templateUrl: './dimensions.component.html', - styleUrls: ['./dimensions.component.scss'], + selector: 'app-dimensions', + templateUrl: './dimensions.component.html', + styleUrls: ['./dimensions.component.scss'], }) export class DimensionsComponent implements OnInit, OnChanges, OnDestroy { - @Input() techRecord!: TechRecordType<'trl'> | TechRecordType<'psv'> | TechRecordType<'hgv'>; - @Input() isEditing = false; - @Output() formChange = new EventEmitter(); - - form!: CustomFormGroup; - - private destroy$ = new Subject(); - - constructor(private dfs: DynamicFormService) {} - - ngOnInit(): void { - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - this.form = this.dfs.createForm(this.template!, this.techRecord) as CustomFormGroup; - - this.form.cleanValueChanges.pipe(debounceTime(400), takeUntil(this.destroy$)).subscribe((e) => this.formChange.emit(e)); - } - - ngOnChanges(changes: SimpleChanges): void { - const { techRecord } = changes; - - if (this.form && techRecord?.currentValue && techRecord.currentValue !== techRecord.previousValue) { - this.form.patchValue(techRecord.currentValue, { emitEvent: false }); - } - } - - ngOnDestroy(): void { - this.destroy$.next(); - this.destroy$.complete(); - } - - get template(): FormNode | undefined { - switch (this.techRecord.techRecord_vehicleType) { - case VehicleTypes.PSV: - return PsvDimensionsTemplate; - case VehicleTypes.HGV: - return HgvDimensionsTemplate; - case VehicleTypes.TRL: - return TrlDimensionsTemplate; - default: - return undefined; - } - } - - get isPsv(): boolean { - return this.techRecord.techRecord_vehicleType === VehicleTypes.PSV; - } - - get isTrl(): boolean { - return this.techRecord.techRecord_vehicleType === VehicleTypes.TRL; - } - - get widths(): typeof FormNodeWidth { - return FormNodeWidth; - } - - get types(): typeof FormNodeEditTypes { - return FormNodeEditTypes; - } - - get dimensions(): CustomFormGroup { - return this.form.get(['dimensions']) as CustomFormGroup; - } - - get hasAxleSpacings(): boolean { - if (this.techRecord.techRecord_vehicleType === 'psv') { - return false; - } - return !!this.techRecord.techRecord_dimensions_axleSpacing?.length; - } - - get axleSpacings(): CustomFormArray { - return this.form.get(['techRecord_dimensions_axleSpacing']) as CustomFormArray; - } - - getAxleSpacing(i: number): CustomFormGroup { - return this.form.get(['techRecord_dimensions_axleSpacing', i]) as CustomFormGroup; - } + @Input() techRecord!: TechRecordType<'trl'> | TechRecordType<'psv'> | TechRecordType<'hgv'>; + @Input() isEditing = false; + @Output() formChange = new EventEmitter(); + + form!: CustomFormGroup; + + private destroy$ = new Subject(); + + constructor(private dfs: DynamicFormService) {} + + ngOnInit(): void { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + this.form = this.dfs.createForm(this.template!, this.techRecord) as CustomFormGroup; + + this.form.cleanValueChanges + .pipe(debounceTime(400), takeUntil(this.destroy$)) + .subscribe((e) => this.formChange.emit(e)); + } + + ngOnChanges(changes: SimpleChanges): void { + const { techRecord } = changes; + + if (this.form && techRecord?.currentValue && techRecord.currentValue !== techRecord.previousValue) { + this.form.patchValue(techRecord.currentValue, { emitEvent: false }); + } + } + + ngOnDestroy(): void { + this.destroy$.next(); + this.destroy$.complete(); + } + + get template(): FormNode | undefined { + switch (this.techRecord.techRecord_vehicleType) { + case VehicleTypes.PSV: + return PsvDimensionsTemplate; + case VehicleTypes.HGV: + return HgvDimensionsTemplate; + case VehicleTypes.TRL: + return TrlDimensionsTemplate; + default: + return undefined; + } + } + + get isPsv(): boolean { + return this.techRecord.techRecord_vehicleType === VehicleTypes.PSV; + } + + get isTrl(): boolean { + return this.techRecord.techRecord_vehicleType === VehicleTypes.TRL; + } + + get widths(): typeof FormNodeWidth { + return FormNodeWidth; + } + + get types(): typeof FormNodeEditTypes { + return FormNodeEditTypes; + } + + get dimensions(): CustomFormGroup { + return this.form.get(['dimensions']) as CustomFormGroup; + } + + get hasAxleSpacings(): boolean { + if (this.techRecord.techRecord_vehicleType === 'psv') { + return false; + } + return !!this.techRecord.techRecord_dimensions_axleSpacing?.length; + } + + get axleSpacings(): CustomFormArray { + return this.form.get(['techRecord_dimensions_axleSpacing']) as CustomFormArray; + } + + getAxleSpacing(i: number): CustomFormGroup { + return this.form.get(['techRecord_dimensions_axleSpacing', i]) as CustomFormGroup; + } } diff --git a/src/app/forms/custom-sections/letters/letters.component.spec.ts b/src/app/forms/custom-sections/letters/letters.component.spec.ts index b561d57279..564d15b5ab 100644 --- a/src/app/forms/custom-sections/letters/letters.component.spec.ts +++ b/src/app/forms/custom-sections/letters/letters.component.spec.ts @@ -19,110 +19,118 @@ import { of } from 'rxjs'; import { LettersComponent } from './letters.component'; const mockTechRecordService = { - techRecordHistory$: of([{ - vin: 'test', - techRecord_statusCode: 'current', - techRecord_vehicleType: 'trl', - createdTimestamp: '12345', - systemNumber: '123', - techRecord_manufactureYear: 2021, - }] as TechRecordSearchSchema[]), + techRecordHistory$: of([ + { + vin: 'test', + techRecord_statusCode: 'current', + techRecord_vehicleType: 'trl', + createdTimestamp: '12345', + systemNumber: '123', + techRecord_manufactureYear: 2021, + }, + ] as TechRecordSearchSchema[]), }; describe('LettersComponent', () => { - let component: LettersComponent; - let fixture: ComponentFixture; + let component: LettersComponent; + let fixture: ComponentFixture; - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [DynamicFormsModule, SharedModule, StoreModule.forRoot({}), HttpClientTestingModule, RouterModule.forRoot([]), RouterTestingModule], - declarations: [LettersComponent], - providers: [ - provideMockStore({ initialState: initialAppState }), - { - provide: UserService, - useValue: { - roles$: of([Roles.TechRecordAmend]), - }, - }, - { - provide: ActivatedRoute, - useValue: { - useValue: { params: of([{ id: 1 }]) }, - }, - }, - { - provide: APP_BASE_HREF, - useValue: '/', - }, - { - provide: TechnicalRecordService, - useValue: mockTechRecordService, - }, - ], - }).compileComponents(); + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [ + DynamicFormsModule, + SharedModule, + StoreModule.forRoot({}), + HttpClientTestingModule, + RouterModule.forRoot([]), + RouterTestingModule, + ], + declarations: [LettersComponent], + providers: [ + provideMockStore({ initialState: initialAppState }), + { + provide: UserService, + useValue: { + roles$: of([Roles.TechRecordAmend]), + }, + }, + { + provide: ActivatedRoute, + useValue: { + useValue: { params: of([{ id: 1 }]) }, + }, + }, + { + provide: APP_BASE_HREF, + useValue: '/', + }, + { + provide: TechnicalRecordService, + useValue: mockTechRecordService, + }, + ], + }).compileComponents(); + }); - }); + beforeEach(() => { + fixture = TestBed.createComponent(LettersComponent); + component = fixture.componentInstance; + component.techRecord = { + systemNumber: 'foo', + createdTimestamp: 'bar', + vin: 'testVin', + techRecord_statusCode: 'current', + } as TechRecordType<'trl'>; + fixture.detectChanges(); + }); - beforeEach(() => { - fixture = TestBed.createComponent(LettersComponent); - component = fixture.componentInstance; - component.techRecord = { - systemNumber: 'foo', - createdTimestamp: 'bar', - vin: 'testVin', - techRecord_statusCode: 'current', - } as TechRecordType<'trl'>; - fixture.detectChanges(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); + describe('eligibleForLetter', () => { + it('should return true if the approval type is valid', () => { + (component.techRecord as TechRecordType<'trl'>).techRecord_approvalType = ApprovalType.EU_WVTA_23_ON; + expect(component.eligibleForLetter).toBeTruthy(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); - describe('eligibleForLetter', () => { - it('should return true if the approval type is valid', () => { - (component.techRecord as TechRecordType<'trl'>).techRecord_approvalType = ApprovalType.EU_WVTA_23_ON; - expect(component.eligibleForLetter).toBeTruthy(); - }); + it('should return false if the approval type is valid', () => { + (component.techRecord as TechRecordType<'trl'>).techRecord_approvalType = ApprovalType.NTA; + expect(component.eligibleForLetter).toBeFalsy(); + }); - it('should return false if the approval type is valid', () => { - (component.techRecord as TechRecordType<'trl'>).techRecord_approvalType = ApprovalType.NTA; - expect(component.eligibleForLetter).toBeFalsy(); - }); + it('should return false if the statuscode is archived', () => { + (component.techRecord as TechRecordType<'trl'>).techRecord_approvalType = ApprovalType.GB_WVTA; + (component.techRecord as TechRecordType<'trl'>).techRecord_statusCode = StatusCodes.ARCHIVED; + expect(component.eligibleForLetter).toBeFalsy(); + }); + }); - it('should return false if the statuscode is archived', () => { - (component.techRecord as TechRecordType<'trl'>).techRecord_approvalType = ApprovalType.GB_WVTA; - (component.techRecord as TechRecordType<'trl'>).techRecord_statusCode = StatusCodes.ARCHIVED; - expect(component.eligibleForLetter).toBeFalsy(); - }); - }); + describe('checkRecordHistoryHasCurrent', () => { + it('should return false if the current technical record history has current status', () => { + expect(component.hasCurrent).toBeFalsy(); + }); - describe('checkRecordHistoryHasCurrent', () => { - it('should return false if the current technical record history has current status', () => { - expect(component.hasCurrent).toBeFalsy(); - }); + it('should return true if the provisional technical record history has current status', () => { + (component.techRecord as TechRecordType<'trl'>).techRecord_statusCode = 'provisional'; + component.ngOnInit(); + expect(component.hasCurrent).toBeTruthy(); + }); + }); - it('should return true if the provisional technical record history has current status', () => { - (component.techRecord as TechRecordType<'trl'>).techRecord_statusCode = 'provisional'; - component.ngOnInit(); - expect(component.hasCurrent).toBeTruthy(); - }); - }); + describe('letter', () => { + it('should return the letter if it exists', () => { + (component.techRecord as TechRecordType<'trl'>).techRecord_letterOfAuth_letterType = 'trailer acceptance'; + (component.techRecord as TechRecordType<'trl'>).techRecord_letterOfAuth_paragraphId = 3; + (component.techRecord as TechRecordType<'trl'>).techRecord_letterOfAuth_letterIssuer = 'issuer'; + expect(component.letter).toBeTruthy(); + expect(component.letter?.paragraphId).toBe(3); + expect(component.letter?.letterIssuer).toBe('issuer'); + }); - describe('letter', () => { - it('should return the letter if it exists', () => { - (component.techRecord as TechRecordType<'trl'>).techRecord_letterOfAuth_letterType = 'trailer acceptance'; - (component.techRecord as TechRecordType<'trl'>).techRecord_letterOfAuth_paragraphId = 3; - (component.techRecord as TechRecordType<'trl'>).techRecord_letterOfAuth_letterIssuer = 'issuer'; - expect(component.letter).toBeTruthy(); - expect(component.letter?.paragraphId).toBe(3); - expect(component.letter?.letterIssuer).toBe('issuer'); - }); + it('should return undefined if it does not exist', () => { + (component.techRecord as TechRecordType<'trl'>).techRecord_letterOfAuth_letterType = undefined; - it('should return undefined if it does not exist', () => { - (component.techRecord as TechRecordType<'trl'>).techRecord_letterOfAuth_letterType = undefined; - - expect(component.letter).toBeUndefined(); - }); - }); + expect(component.letter).toBeUndefined(); + }); + }); }); diff --git a/src/app/forms/custom-sections/letters/letters.component.ts b/src/app/forms/custom-sections/letters/letters.component.ts index c47017ec4a..ceb21c8d60 100644 --- a/src/app/forms/custom-sections/letters/letters.component.ts +++ b/src/app/forms/custom-sections/letters/letters.component.ts @@ -1,8 +1,6 @@ /* eslint-disable @typescript-eslint/no-non-null-asserted-optional-chain */ import { ViewportScroller } from '@angular/common'; -import { - Component, EventEmitter, Input, OnChanges, OnDestroy, OnInit, Output, -} from '@angular/core'; +import { Component, EventEmitter, Input, OnChanges, OnDestroy, OnInit, Output } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; import { TechRecordSearchSchema } from '@dvsa/cvs-type-definitions/types/v3/tech-record/get/search'; import { ParagraphIds } from '@dvsa/cvs-type-definitions/types/v3/tech-record/get/trl/complete'; @@ -16,141 +14,147 @@ import { LettersIntoAuthApprovalType, LettersOfAuth, StatusCodes } from '@models import { Store } from '@ngrx/store'; import { TechnicalRecordService } from '@services/technical-record/technical-record.service'; import { updateScrollPosition } from '@store/technical-records'; -import { - ReplaySubject, Subscription, debounceTime, takeUntil, -} from 'rxjs'; +import { ReplaySubject, Subscription, debounceTime, takeUntil } from 'rxjs'; @Component({ - selector: 'app-letters[techRecord]', - templateUrl: './letters.component.html', - styleUrls: ['./letters.component.scss'], + selector: 'app-letters[techRecord]', + templateUrl: './letters.component.html', + styleUrls: ['./letters.component.scss'], }) export class LettersComponent implements OnInit, OnDestroy, OnChanges { - @Input() techRecord?: TechRecordType<'trl'>; - @Input() isEditing = false; - - @Output() formChange = new EventEmitter(); - - form!: CustomFormGroup; - - hasCurrent = false; - - private formSubscription = new Subscription(); - private destroy$ = new ReplaySubject(1); - - constructor( - private dynamicFormService: DynamicFormService, - private techRecordService: TechnicalRecordService, - private viewportScroller: ViewportScroller, - private router: Router, - private route: ActivatedRoute, - private store: Store, - ) {} - - ngOnInit(): void { - this.form = this.dynamicFormService.createForm(LettersTemplate, this.techRecord) as CustomFormGroup; - this.formSubscription = this.form.cleanValueChanges.pipe(debounceTime(400)).subscribe((event) => this.formChange.emit(event)); - this.checkForCurrentRecordInHistory(); - } - - ngOnChanges(): void { - if (this.techRecord) { - this.form?.patchValue(this.techRecord, { emitEvent: false }); - } - } - - ngOnDestroy(): void { - this.formSubscription.unsubscribe(); - this.destroy$.next(true); - this.destroy$.unsubscribe(); - } - - checkForCurrentRecordInHistory() { - this.techRecordService.techRecordHistory$.pipe(takeUntil(this.destroy$)).subscribe((historyArray: TechRecordSearchSchema[] | undefined) => { - historyArray?.forEach((history: TechRecordSearchSchema) => { - if (history.techRecord_statusCode === StatusCodes.CURRENT - && this.techRecord?.techRecord_statusCode === StatusCodes.PROVISIONAL) { - this.hasCurrent = true; - } - }); - }); - } - - get roles(): typeof Roles { - return Roles; - } - - get types(): typeof FormNodeEditTypes { - return FormNodeEditTypes; - } - - get letter(): LettersOfAuth | undefined { - return this.techRecord?.techRecord_letterOfAuth_letterType - ? { - letterType: this.techRecord?.techRecord_letterOfAuth_letterType, - paragraphId: this.techRecord?.techRecord_letterOfAuth_paragraphId as ParagraphIds, - letterIssuer: this.techRecord?.techRecord_letterOfAuth_letterIssuer as string, - letterDateRequested: this.techRecord?.techRecord_letterOfAuth_letterDateRequested as string, - letterContents: '', - } - : undefined; - } - - get eligibleForLetter(): boolean { - const isArchivedTechRecord = this.techRecord?.techRecord_statusCode === StatusCodes.ARCHIVED; - return this.correctApprovalType && !isArchivedTechRecord && !this.isEditing && !this.hasCurrent; - } - - get reasonForIneligibility(): string { - if (this.isEditing) { - return 'This section is not available when amending or creating a technical record.'; - } - - if (this.techRecord?.techRecord_statusCode === StatusCodes.PROVISIONAL) { - if (this.hasCurrent) { - // eslint-disable-next-line max-len - return 'Generating letters is not applicable to provisional records, where a current record also exists for a vehicle. Open the current record to generate letters.'; - } - } else if (this.techRecord?.techRecord_statusCode === StatusCodes.ARCHIVED) { - return 'Generating letters is not applicable to archived technical records.'; - } - - if (!this.correctApprovalType) { - return 'This trailer does not have the right approval type to be eligible for a letter of authorisation.'; - } - - return ''; - } - - get correctApprovalType(): boolean { - return ( - !!this.techRecord?.techRecord_approvalType - && (Object.values(LettersIntoAuthApprovalType) as string[]).includes(this.techRecord.techRecord_approvalType.valueOf()) - ); - } - - get documentParams(): Map { - if (!this.techRecord) { - throw new Error('Could not find vehicle record associated with this technical record.'); - } - return new Map([ - ['systemNumber', (this.techRecord as TechRecordTypeVehicleVerb<'trl', 'get'>)?.systemNumber], - ['vinNumber', this.techRecord?.vin], - ]); - } - - get fileName(): string { - if (!this.letter) { - return ''; - } - if (!this.techRecord) { - return ''; - } - return `letter_${(this.techRecord as TechRecordTypeVehicleVerb<'trl', 'get'>).systemNumber}_${this.techRecord.vin}`; - } - - generateLetter() { - this.store.dispatch(updateScrollPosition({ position: this.viewportScroller.getScrollPosition() })); - void this.router.navigate(['generate-letter'], { relativeTo: this.route }); - } + @Input() techRecord?: TechRecordType<'trl'>; + @Input() isEditing = false; + + @Output() formChange = new EventEmitter(); + + form!: CustomFormGroup; + + hasCurrent = false; + + private formSubscription = new Subscription(); + private destroy$ = new ReplaySubject(1); + + constructor( + private dynamicFormService: DynamicFormService, + private techRecordService: TechnicalRecordService, + private viewportScroller: ViewportScroller, + private router: Router, + private route: ActivatedRoute, + private store: Store + ) {} + + ngOnInit(): void { + this.form = this.dynamicFormService.createForm(LettersTemplate, this.techRecord) as CustomFormGroup; + this.formSubscription = this.form.cleanValueChanges + .pipe(debounceTime(400)) + .subscribe((event) => this.formChange.emit(event)); + this.checkForCurrentRecordInHistory(); + } + + ngOnChanges(): void { + if (this.techRecord) { + this.form?.patchValue(this.techRecord, { emitEvent: false }); + } + } + + ngOnDestroy(): void { + this.formSubscription.unsubscribe(); + this.destroy$.next(true); + this.destroy$.unsubscribe(); + } + + checkForCurrentRecordInHistory() { + this.techRecordService.techRecordHistory$ + .pipe(takeUntil(this.destroy$)) + .subscribe((historyArray: TechRecordSearchSchema[] | undefined) => { + historyArray?.forEach((history: TechRecordSearchSchema) => { + if ( + history.techRecord_statusCode === StatusCodes.CURRENT && + this.techRecord?.techRecord_statusCode === StatusCodes.PROVISIONAL + ) { + this.hasCurrent = true; + } + }); + }); + } + + get roles(): typeof Roles { + return Roles; + } + + get types(): typeof FormNodeEditTypes { + return FormNodeEditTypes; + } + + get letter(): LettersOfAuth | undefined { + return this.techRecord?.techRecord_letterOfAuth_letterType + ? { + letterType: this.techRecord?.techRecord_letterOfAuth_letterType, + paragraphId: this.techRecord?.techRecord_letterOfAuth_paragraphId as ParagraphIds, + letterIssuer: this.techRecord?.techRecord_letterOfAuth_letterIssuer as string, + letterDateRequested: this.techRecord?.techRecord_letterOfAuth_letterDateRequested as string, + letterContents: '', + } + : undefined; + } + + get eligibleForLetter(): boolean { + const isArchivedTechRecord = this.techRecord?.techRecord_statusCode === StatusCodes.ARCHIVED; + return this.correctApprovalType && !isArchivedTechRecord && !this.isEditing && !this.hasCurrent; + } + + get reasonForIneligibility(): string { + if (this.isEditing) { + return 'This section is not available when amending or creating a technical record.'; + } + + if (this.techRecord?.techRecord_statusCode === StatusCodes.PROVISIONAL) { + if (this.hasCurrent) { + // eslint-disable-next-line max-len + return 'Generating letters is not applicable to provisional records, where a current record also exists for a vehicle. Open the current record to generate letters.'; + } + } else if (this.techRecord?.techRecord_statusCode === StatusCodes.ARCHIVED) { + return 'Generating letters is not applicable to archived technical records.'; + } + + if (!this.correctApprovalType) { + return 'This trailer does not have the right approval type to be eligible for a letter of authorisation.'; + } + + return ''; + } + + get correctApprovalType(): boolean { + return ( + !!this.techRecord?.techRecord_approvalType && + (Object.values(LettersIntoAuthApprovalType) as string[]).includes( + this.techRecord.techRecord_approvalType.valueOf() + ) + ); + } + + get documentParams(): Map { + if (!this.techRecord) { + throw new Error('Could not find vehicle record associated with this technical record.'); + } + return new Map([ + ['systemNumber', (this.techRecord as TechRecordTypeVehicleVerb<'trl', 'get'>)?.systemNumber], + ['vinNumber', this.techRecord?.vin], + ]); + } + + get fileName(): string { + if (!this.letter) { + return ''; + } + if (!this.techRecord) { + return ''; + } + return `letter_${(this.techRecord as TechRecordTypeVehicleVerb<'trl', 'get'>).systemNumber}_${this.techRecord.vin}`; + } + + generateLetter() { + this.store.dispatch(updateScrollPosition({ position: this.viewportScroller.getScrollPosition() })); + void this.router.navigate(['generate-letter'], { relativeTo: this.route }); + } } diff --git a/src/app/forms/custom-sections/modified-weights/modified-weights.component.spec.ts b/src/app/forms/custom-sections/modified-weights/modified-weights.component.spec.ts index ef47f6be92..d904fe6aec 100644 --- a/src/app/forms/custom-sections/modified-weights/modified-weights.component.spec.ts +++ b/src/app/forms/custom-sections/modified-weights/modified-weights.component.spec.ts @@ -9,163 +9,163 @@ import { initialAppState } from '@store/index'; import { ModifiedWeightsComponent } from './modified-weights.component'; describe('ModifiedWeightsComponent', () => { - let component: ModifiedWeightsComponent; - let fixture: ComponentFixture; - - beforeEach(async () => { - await TestBed.configureTestingModule({ - declarations: [ModifiedWeightsComponent], - imports: [HttpClientTestingModule, RouterTestingModule], - providers: [provideMockStore({ initialState: initialAppState })], - }).compileComponents(); - - fixture = TestBed.createComponent(ModifiedWeightsComponent); - component = fixture.componentInstance; - component.changes = {}; - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); - - describe('ngOnInit', () => { - it('Vehicle type: PSV, Axle changed: Gross. Should determine that only the PSV gross axle have changed', () => { - component.vehicleType = VehicleTypes.PSV; - component.changes = { - techRecord_grossKerbWeight: 1, - techRecord_grossLadenWeight: 3, - techRecord_grossGbWeight: 4, - techRecord_grossDesignWeight: 5, - }; - fixture.detectChanges(); - component.ngOnInit(); - - expect(component.psvGrossAxleChanged).toBe(true); - expect(component.hgvGrossAxleChanged).toBe(false); - expect(component.trlGrossAxleChanged).toBe(false); - expect(component.hgvTrainAxleChanged).toBe(false); - expect(component.psvTrainAxleChanged).toBe(false); - expect(component.maxTrainAxleChanged).toBe(false); - }); - - it('Vehicle type: HGV, Axle changed: Gross. Should determine that only the PSV gross axle have changed', () => { - component.vehicleType = VehicleTypes.HGV; - component.changes = { - techRecord_grossGbWeight: 1, - techRecord_grossEecWeight: 2, - techRecord_grossDesignWeight: 3, - }; - - fixture.detectChanges(); - component.ngOnInit(); - - expect(component.psvGrossAxleChanged).toBe(false); - expect(component.hgvGrossAxleChanged).toBe(true); - expect(component.trlGrossAxleChanged).toBe(false); - expect(component.hgvTrainAxleChanged).toBe(false); - expect(component.psvTrainAxleChanged).toBe(false); - expect(component.maxTrainAxleChanged).toBe(false); - }); - - it('Vehicle type: TRL, Axle changed: Gross. Should determine that only the HGV gross axle have changed', () => { - component.vehicleType = VehicleTypes.TRL; - component.changes = { - techRecord_grossGbWeight: 1, - techRecord_grossEecWeight: 2, - techRecord_grossDesignWeight: 3, - }; - fixture.detectChanges(); - component.ngOnInit(); - - expect(component.psvGrossAxleChanged).toBe(false); - expect(component.hgvGrossAxleChanged).toBe(false); - expect(component.trlGrossAxleChanged).toBe(true); - expect(component.hgvTrainAxleChanged).toBe(false); - expect(component.psvTrainAxleChanged).toBe(false); - expect(component.maxTrainAxleChanged).toBe(false); - }); - - it('Vehicle type: HGV, Axle changed: Gross. Should determine that only the HGV gross axle have changed', () => { - component.vehicleType = VehicleTypes.HGV; - component.changes = { - techRecord_trainGbWeight: 1, - techRecord_trainEecWeight: 2, - techRecord_trainDesignWeight: 3, - }; - - fixture.detectChanges(); - component.ngOnInit(); - - expect(component.psvGrossAxleChanged).toBe(false); - expect(component.hgvGrossAxleChanged).toBe(false); - expect(component.trlGrossAxleChanged).toBe(false); - expect(component.hgvTrainAxleChanged).toBe(true); - expect(component.psvTrainAxleChanged).toBe(false); - expect(component.maxTrainAxleChanged).toBe(false); - }); - - it('Vehicle type: PSV, Axle changed: Train. Should determine that only the PSV train axle have changed', () => { - component.vehicleType = VehicleTypes.PSV; - component.changes = { - techRecord_trainDesignWeight: 1, - techRecord_maxTrainGbWeight: 3, - }; - - fixture.detectChanges(); - component.ngOnInit(); - - expect(component.psvGrossAxleChanged).toBe(false); - expect(component.hgvGrossAxleChanged).toBe(false); - expect(component.trlGrossAxleChanged).toBe(false); - expect(component.hgvTrainAxleChanged).toBe(false); - expect(component.psvTrainAxleChanged).toBe(true); - expect(component.maxTrainAxleChanged).toBe(false); - }); - - it('Vehicle type: HGV, Axle changed: Max Train. Should determine that only the HGV max train axle have changed', () => { - component.vehicleType = VehicleTypes.HGV; - component.changes = { - techRecord_maxTrainGbWeight: 1, - techRecord_maxTrainEecWeight: 2, - techRecord_maxTrainDesignWeight: 3, - }; - - fixture.detectChanges(); - component.ngOnInit(); - - expect(component.psvGrossAxleChanged).toBe(false); - expect(component.hgvGrossAxleChanged).toBe(false); - expect(component.trlGrossAxleChanged).toBe(false); - expect(component.hgvTrainAxleChanged).toBe(false); - expect(component.psvTrainAxleChanged).toBe(false); - expect(component.maxTrainAxleChanged).toBe(true); - }); - }); - - describe('getAxleTemplate', () => { - it('should return the correct template for the vehicle type', () => { - component.vehicleType = VehicleTypes.HGV; - fixture.detectChanges(); - - const getAxleTemplateSpy = jest.spyOn(component, 'getAxleTemplate'); - const template = component.getAxleTemplate(); - - expect(getAxleTemplateSpy).toHaveBeenCalled(); - expect(template).toBeDefined(); - expect(template).toBeInstanceOf(Array); - }); - - it('should return undefined, as an invalid vehicle type has been provided', () => { - component.vehicleType = '' as VehicleTypes; - fixture.detectChanges(); - - const getAxleTemplateSpy = jest.spyOn(component, 'getAxleTemplate'); - jest.spyOn(vehicleTemplateMap, 'get').mockReturnValue(undefined); - const template = component.getAxleTemplate(); - - expect(getAxleTemplateSpy).toHaveBeenCalled(); - expect(template).toBeUndefined(); - }); - }); + let component: ModifiedWeightsComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ModifiedWeightsComponent], + imports: [HttpClientTestingModule, RouterTestingModule], + providers: [provideMockStore({ initialState: initialAppState })], + }).compileComponents(); + + fixture = TestBed.createComponent(ModifiedWeightsComponent); + component = fixture.componentInstance; + component.changes = {}; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + describe('ngOnInit', () => { + it('Vehicle type: PSV, Axle changed: Gross. Should determine that only the PSV gross axle have changed', () => { + component.vehicleType = VehicleTypes.PSV; + component.changes = { + techRecord_grossKerbWeight: 1, + techRecord_grossLadenWeight: 3, + techRecord_grossGbWeight: 4, + techRecord_grossDesignWeight: 5, + }; + fixture.detectChanges(); + component.ngOnInit(); + + expect(component.psvGrossAxleChanged).toBe(true); + expect(component.hgvGrossAxleChanged).toBe(false); + expect(component.trlGrossAxleChanged).toBe(false); + expect(component.hgvTrainAxleChanged).toBe(false); + expect(component.psvTrainAxleChanged).toBe(false); + expect(component.maxTrainAxleChanged).toBe(false); + }); + + it('Vehicle type: HGV, Axle changed: Gross. Should determine that only the PSV gross axle have changed', () => { + component.vehicleType = VehicleTypes.HGV; + component.changes = { + techRecord_grossGbWeight: 1, + techRecord_grossEecWeight: 2, + techRecord_grossDesignWeight: 3, + }; + + fixture.detectChanges(); + component.ngOnInit(); + + expect(component.psvGrossAxleChanged).toBe(false); + expect(component.hgvGrossAxleChanged).toBe(true); + expect(component.trlGrossAxleChanged).toBe(false); + expect(component.hgvTrainAxleChanged).toBe(false); + expect(component.psvTrainAxleChanged).toBe(false); + expect(component.maxTrainAxleChanged).toBe(false); + }); + + it('Vehicle type: TRL, Axle changed: Gross. Should determine that only the HGV gross axle have changed', () => { + component.vehicleType = VehicleTypes.TRL; + component.changes = { + techRecord_grossGbWeight: 1, + techRecord_grossEecWeight: 2, + techRecord_grossDesignWeight: 3, + }; + fixture.detectChanges(); + component.ngOnInit(); + + expect(component.psvGrossAxleChanged).toBe(false); + expect(component.hgvGrossAxleChanged).toBe(false); + expect(component.trlGrossAxleChanged).toBe(true); + expect(component.hgvTrainAxleChanged).toBe(false); + expect(component.psvTrainAxleChanged).toBe(false); + expect(component.maxTrainAxleChanged).toBe(false); + }); + + it('Vehicle type: HGV, Axle changed: Gross. Should determine that only the HGV gross axle have changed', () => { + component.vehicleType = VehicleTypes.HGV; + component.changes = { + techRecord_trainGbWeight: 1, + techRecord_trainEecWeight: 2, + techRecord_trainDesignWeight: 3, + }; + + fixture.detectChanges(); + component.ngOnInit(); + + expect(component.psvGrossAxleChanged).toBe(false); + expect(component.hgvGrossAxleChanged).toBe(false); + expect(component.trlGrossAxleChanged).toBe(false); + expect(component.hgvTrainAxleChanged).toBe(true); + expect(component.psvTrainAxleChanged).toBe(false); + expect(component.maxTrainAxleChanged).toBe(false); + }); + + it('Vehicle type: PSV, Axle changed: Train. Should determine that only the PSV train axle have changed', () => { + component.vehicleType = VehicleTypes.PSV; + component.changes = { + techRecord_trainDesignWeight: 1, + techRecord_maxTrainGbWeight: 3, + }; + + fixture.detectChanges(); + component.ngOnInit(); + + expect(component.psvGrossAxleChanged).toBe(false); + expect(component.hgvGrossAxleChanged).toBe(false); + expect(component.trlGrossAxleChanged).toBe(false); + expect(component.hgvTrainAxleChanged).toBe(false); + expect(component.psvTrainAxleChanged).toBe(true); + expect(component.maxTrainAxleChanged).toBe(false); + }); + + it('Vehicle type: HGV, Axle changed: Max Train. Should determine that only the HGV max train axle have changed', () => { + component.vehicleType = VehicleTypes.HGV; + component.changes = { + techRecord_maxTrainGbWeight: 1, + techRecord_maxTrainEecWeight: 2, + techRecord_maxTrainDesignWeight: 3, + }; + + fixture.detectChanges(); + component.ngOnInit(); + + expect(component.psvGrossAxleChanged).toBe(false); + expect(component.hgvGrossAxleChanged).toBe(false); + expect(component.trlGrossAxleChanged).toBe(false); + expect(component.hgvTrainAxleChanged).toBe(false); + expect(component.psvTrainAxleChanged).toBe(false); + expect(component.maxTrainAxleChanged).toBe(true); + }); + }); + + describe('getAxleTemplate', () => { + it('should return the correct template for the vehicle type', () => { + component.vehicleType = VehicleTypes.HGV; + fixture.detectChanges(); + + const getAxleTemplateSpy = jest.spyOn(component, 'getAxleTemplate'); + const template = component.getAxleTemplate(); + + expect(getAxleTemplateSpy).toHaveBeenCalled(); + expect(template).toBeDefined(); + expect(template).toBeInstanceOf(Array); + }); + + it('should return undefined, as an invalid vehicle type has been provided', () => { + component.vehicleType = '' as VehicleTypes; + fixture.detectChanges(); + + const getAxleTemplateSpy = jest.spyOn(component, 'getAxleTemplate'); + jest.spyOn(vehicleTemplateMap, 'get').mockReturnValue(undefined); + const template = component.getAxleTemplate(); + + expect(getAxleTemplateSpy).toHaveBeenCalled(); + expect(template).toBeUndefined(); + }); + }); }); diff --git a/src/app/forms/custom-sections/modified-weights/modified-weights.component.ts b/src/app/forms/custom-sections/modified-weights/modified-weights.component.ts index 2b760b8e69..10d73cb456 100644 --- a/src/app/forms/custom-sections/modified-weights/modified-weights.component.ts +++ b/src/app/forms/custom-sections/modified-weights/modified-weights.component.ts @@ -1,73 +1,83 @@ import { Component, Input, OnInit } from '@angular/core'; -import { TechRecordGETHGV, TechRecordGETPSV, TechRecordGETTRL } from '@dvsa/cvs-type-definitions/types/v3/tech-record/tech-record-verb-vehicle-type'; +import { + TechRecordGETHGV, + TechRecordGETPSV, + TechRecordGETTRL, +} from '@dvsa/cvs-type-definitions/types/v3/tech-record/tech-record-verb-vehicle-type'; import { FormNode } from '@forms/services/dynamic-form.types'; import { vehicleTemplateMap } from '@forms/utils/tech-record-constants'; import { VehicleTypes } from '@models/vehicle-tech-record.model'; import { TechnicalRecordService } from '@services/technical-record/technical-record.service'; @Component({ - selector: 'app-modified-weights', - templateUrl: './modified-weights.component.html', - styleUrls: ['./modified-weights.component.scss'], + selector: 'app-modified-weights', + templateUrl: './modified-weights.component.html', + styleUrls: ['./modified-weights.component.scss'], }) export class ModifiedWeightsComponent implements OnInit { - @Input() vehicleType!: VehicleTypes; - @Input() changes!: Partial; + @Input() vehicleType!: VehicleTypes; + @Input() changes!: Partial; - axleTemplate: FormNode[] | undefined; - psvGrossAxleChanged = false; - hgvGrossAxleChanged = false; - trlGrossAxleChanged = false; - hgvTrainAxleChanged = false; - psvTrainAxleChanged = false; - maxTrainAxleChanged = false; + axleTemplate: FormNode[] | undefined; + psvGrossAxleChanged = false; + hgvGrossAxleChanged = false; + trlGrossAxleChanged = false; + hgvTrainAxleChanged = false; + psvTrainAxleChanged = false; + maxTrainAxleChanged = false; - constructor(private readonly technicalRecordService: TechnicalRecordService) {} + constructor(private readonly technicalRecordService: TechnicalRecordService) {} - get isPSV(): boolean { - return this.vehicleType === VehicleTypes.PSV; - } + get isPSV(): boolean { + return this.vehicleType === VehicleTypes.PSV; + } - get isHGV(): boolean { - return this.vehicleType === VehicleTypes.HGV; - } + get isHGV(): boolean { + return this.vehicleType === VehicleTypes.HGV; + } - get isTRL(): boolean { - return this.vehicleType === VehicleTypes.TRL; - } + get isTRL(): boolean { + return this.vehicleType === VehicleTypes.TRL; + } - get psvChanges(): Partial | undefined { - return this.isPSV ? (this.changes as Partial) : undefined; - } + get psvChanges(): Partial | undefined { + return this.isPSV ? (this.changes as Partial) : undefined; + } - get hgvChanges(): Partial | undefined { - return this.isHGV ? (this.changes as Partial) : undefined; - } + get hgvChanges(): Partial | undefined { + return this.isHGV ? (this.changes as Partial) : undefined; + } - get trlChanges(): Partial | undefined { - return this.isTRL ? (this.changes as Partial) : undefined; - } + get trlChanges(): Partial | undefined { + return this.isTRL ? (this.changes as Partial) : undefined; + } - get hgvAndtrlGrossAxleChanged() { - return this.hgvGrossAxleChanged || this.trlGrossAxleChanged; - } + get hgvAndtrlGrossAxleChanged() { + return this.hgvGrossAxleChanged || this.trlGrossAxleChanged; + } - ngOnInit(): void { - this.axleTemplate = this.getAxleTemplate(); - this.psvGrossAxleChanged = this.isPSV && this.technicalRecordService.hasPsvGrossAxleChanged(this.changes as Partial); - this.hgvGrossAxleChanged = this.isHGV && this.technicalRecordService.hasHgvGrossAxleChanged(this.changes as Partial); - this.trlGrossAxleChanged = this.isTRL && this.technicalRecordService.hasTrlGrossAxleChanged(this.changes as Partial); - this.hgvTrainAxleChanged = this.isHGV && this.technicalRecordService.hasHgvTrainAxleChanged(this.changes as Partial); - this.psvTrainAxleChanged = this.isPSV && this.technicalRecordService.hasPsvTrainAxleChanged(this.changes as Partial); - this.maxTrainAxleChanged = this.isHGV && this.technicalRecordService.hasMaxTrainAxleChanged(this.changes as Partial); - } + ngOnInit(): void { + this.axleTemplate = this.getAxleTemplate(); + this.psvGrossAxleChanged = + this.isPSV && this.technicalRecordService.hasPsvGrossAxleChanged(this.changes as Partial); + this.hgvGrossAxleChanged = + this.isHGV && this.technicalRecordService.hasHgvGrossAxleChanged(this.changes as Partial); + this.trlGrossAxleChanged = + this.isTRL && this.technicalRecordService.hasTrlGrossAxleChanged(this.changes as Partial); + this.hgvTrainAxleChanged = + this.isHGV && this.technicalRecordService.hasHgvTrainAxleChanged(this.changes as Partial); + this.psvTrainAxleChanged = + this.isPSV && this.technicalRecordService.hasPsvTrainAxleChanged(this.changes as Partial); + this.maxTrainAxleChanged = + this.isHGV && this.technicalRecordService.hasMaxTrainAxleChanged(this.changes as Partial); + } - getAxleTemplate(): FormNode[] | undefined { - return vehicleTemplateMap - .get(this.vehicleType) - ?.find((template) => template.name === 'weightsSection') - ?.children?.find((child) => child.name === 'techRecord_axles') - ?.children?.at(0) - ?.children?.filter((child) => child.name !== 'axleNumber'); - } + getAxleTemplate(): FormNode[] | undefined { + return vehicleTemplateMap + .get(this.vehicleType) + ?.find((template) => template.name === 'weightsSection') + ?.children?.find((child) => child.name === 'techRecord_axles') + ?.children?.at(0) + ?.children?.filter((child) => child.name !== 'axleNumber'); + } } diff --git a/src/app/forms/custom-sections/plates/plates.component.spec.ts b/src/app/forms/custom-sections/plates/plates.component.spec.ts index fa6e9feb40..ff51aa45a5 100644 --- a/src/app/forms/custom-sections/plates/plates.component.spec.ts +++ b/src/app/forms/custom-sections/plates/plates.component.spec.ts @@ -22,457 +22,514 @@ import { PlatesComponent } from './plates.component'; global.scrollTo = jest.fn(); describe('PlatesComponent', () => { - let component: PlatesComponent; - let fixture: ComponentFixture; - let router: Router; - let errorService: GlobalErrorService; - let store: MockStore; + let component: PlatesComponent; + let fixture: ComponentFixture; + let router: Router; + let errorService: GlobalErrorService; + let store: MockStore; - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [DynamicFormsModule, SharedModule, StoreModule.forRoot({}), HttpClientTestingModule, RouterModule.forRoot([]), RouterTestingModule], - declarations: [PlatesComponent], - providers: [ - provideMockStore({ initialState: initialAppState }), - { - provide: UserService, - useValue: { - roles$: of([Roles.TechRecordAmend]), - }, - }, - { - provide: ActivatedRoute, - useValue: { - useValue: { params: of([{ id: 1 }]) }, - }, - }, - { - provide: APP_BASE_HREF, - useValue: '/', - }, - GlobalErrorService, - ], - }).compileComponents(); - }); + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [ + DynamicFormsModule, + SharedModule, + StoreModule.forRoot({}), + HttpClientTestingModule, + RouterModule.forRoot([]), + RouterTestingModule, + ], + declarations: [PlatesComponent], + providers: [ + provideMockStore({ initialState: initialAppState }), + { + provide: UserService, + useValue: { + roles$: of([Roles.TechRecordAmend]), + }, + }, + { + provide: ActivatedRoute, + useValue: { + useValue: { params: of([{ id: 1 }]) }, + }, + }, + { + provide: APP_BASE_HREF, + useValue: '/', + }, + GlobalErrorService, + ], + }).compileComponents(); + }); - beforeEach(() => { - fixture = TestBed.createComponent(PlatesComponent); - component = fixture.componentInstance; - router = TestBed.inject(Router); - errorService = TestBed.inject(GlobalErrorService); - component.techRecord = { systemNumber: 'foo', createdTimestamp: 'bar', vin: 'testVin' } as TechRecordType<'hgv' | 'trl'>; - store = TestBed.inject(MockStore); - fixture.detectChanges(); - }); + beforeEach(() => { + fixture = TestBed.createComponent(PlatesComponent); + component = fixture.componentInstance; + router = TestBed.inject(Router); + errorService = TestBed.inject(GlobalErrorService); + component.techRecord = { systemNumber: 'foo', createdTimestamp: 'bar', vin: 'testVin' } as TechRecordType< + 'hgv' | 'trl' + >; + store = TestBed.inject(MockStore); + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); - describe('mostRecentPlate', () => { - it('should fetch the plate if only 1 exists', () => { - (component.techRecord as TechRecordType<'trl'>).techRecord_plates = [ - { - plateIssueDate: new Date().toISOString(), - plateSerialNumber: '123456', - plateIssuer: 'issuer', - plateReasonForIssue: 'Replacement', - }, - ]; - const plateFetched = component.mostRecentPlate; + describe('mostRecentPlate', () => { + it('should fetch the plate if only 1 exists', () => { + (component.techRecord as TechRecordType<'trl'>).techRecord_plates = [ + { + plateIssueDate: new Date().toISOString(), + plateSerialNumber: '123456', + plateIssuer: 'issuer', + plateReasonForIssue: 'Replacement', + }, + ]; + const plateFetched = component.mostRecentPlate; - expect(plateFetched).toBeDefined(); - expect(plateFetched?.plateSerialNumber).toBe('123456'); - }); + expect(plateFetched).toBeDefined(); + expect(plateFetched?.plateSerialNumber).toBe('123456'); + }); - it('should fetch the latest plate if more than 1 exists', () => { - component.techRecord = { - techRecord_plates: [ - { - plateIssueDate: new Date(new Date().getTime()).toISOString(), - plateSerialNumber: '123456', - plateIssuer: 'issuer', - plateReasonForIssue: 'Replacement', - }, - { - plateIssueDate: new Date(new Date().getTime() + 5).toISOString(), - plateSerialNumber: '234567', - plateIssuer: 'issuer', - plateReasonForIssue: 'Replacement', - }, - { - plateIssueDate: new Date(new Date().getTime() - 5).toISOString(), - plateSerialNumber: '345678', - plateIssuer: 'issuer', - plateReasonForIssue: 'Replacement', - }, - ], - } as TechRecordType<'trl'>; + it('should fetch the latest plate if more than 1 exists', () => { + component.techRecord = { + techRecord_plates: [ + { + plateIssueDate: new Date(new Date().getTime()).toISOString(), + plateSerialNumber: '123456', + plateIssuer: 'issuer', + plateReasonForIssue: 'Replacement', + }, + { + plateIssueDate: new Date(new Date().getTime() + 5).toISOString(), + plateSerialNumber: '234567', + plateIssuer: 'issuer', + plateReasonForIssue: 'Replacement', + }, + { + plateIssueDate: new Date(new Date().getTime() - 5).toISOString(), + plateSerialNumber: '345678', + plateIssuer: 'issuer', + plateReasonForIssue: 'Replacement', + }, + ], + } as TechRecordType<'trl'>; - const plateFetched = component.mostRecentPlate; + const plateFetched = component.mostRecentPlate; - expect(plateFetched).toBeDefined(); - expect(plateFetched?.plateSerialNumber).toBe('234567'); - }); + expect(plateFetched).toBeDefined(); + expect(plateFetched?.plateSerialNumber).toBe('234567'); + }); - it('should return null if plates are empty', () => { - component.techRecord = { techRecord_plates: [] } as unknown as TechRecordType<'trl'>; + it('should return null if plates are empty', () => { + component.techRecord = { techRecord_plates: [] } as unknown as TechRecordType<'trl'>; - const plateFetched = component.mostRecentPlate; + const plateFetched = component.mostRecentPlate; - expect(plateFetched).toBeUndefined(); - }); - }); + expect(plateFetched).toBeUndefined(); + }); + }); - describe('hasPlates', () => { - it('should return false if plates is undefined', () => { - component.techRecord = { techRecord_plates: undefined } as unknown as TechRecordType<'trl'>; + describe('hasPlates', () => { + it('should return false if plates is undefined', () => { + component.techRecord = { techRecord_plates: undefined } as unknown as TechRecordType<'trl'>; - expect(component.hasPlates).toBeFalsy(); - }); + expect(component.hasPlates).toBeFalsy(); + }); - it('should return false if plates is empty', () => { - component.techRecord = { techRecord_plates: [] } as unknown as TechRecordType<'trl'>; + it('should return false if plates is empty', () => { + component.techRecord = { techRecord_plates: [] } as unknown as TechRecordType<'trl'>; - expect(component.hasPlates).toBeFalsy(); - }); + expect(component.hasPlates).toBeFalsy(); + }); - it('should return true if plates is not empty', () => { - component.techRecord = { - techRecord_plates: [ - { - plateIssueDate: new Date().toISOString(), - plateSerialNumber: '123456', - plateIssuer: 'issuer', - plateReasonForIssue: 'Replacement', - }, - ], - } as TechRecordType<'trl'>; + it('should return true if plates is not empty', () => { + component.techRecord = { + techRecord_plates: [ + { + plateIssueDate: new Date().toISOString(), + plateSerialNumber: '123456', + plateIssuer: 'issuer', + plateReasonForIssue: 'Replacement', + }, + ], + } as TechRecordType<'trl'>; - expect(component.hasPlates).toBeTruthy(); - }); - }); + expect(component.hasPlates).toBeTruthy(); + }); + }); - describe('validateTechRecordPlates', () => { - beforeEach(() => { - component.techRecord = { - primaryVrm: '123456', - techRecord_approvalType: 'NTA', - techRecord_approvalTypeNumber: '1', - techRecord_bodyType_code: 'b', - techRecord_bodyType_description: 'box', - techRecord_brakes_dtpNumber: '12345', - techRecord_dimensions_length: 1, - techRecord_dimensions_width: 1, - techRecord_frontVehicleTo5thWheelCouplingMax: 1, - techRecord_frontVehicleTo5thWheelCouplingMin: 1, - techRecord_functionCode: 'R', - techRecord_grossDesignWeight: 1, - techRecord_grossEecWeight: 1, - techRecord_grossGbWeight: 1, - techRecord_make: 'AEC', - techRecord_manufactureYear: 2020, - techRecord_maxTrainDesignWeight: 1, - techRecord_maxTrainEecWeight: 11, - techRecord_maxTrainGbWeight: 1, - techRecord_model: '123', - techRecord_noOfAxles: 1, - techRecord_ntaNumber: '1', - techRecord_reasonForCreation: 'Test', - techRecord_recordCompleteness: 'skeleton', - techRecord_regnDate: '2020-10-10', - techRecord_speedLimiterMrk: true, - techRecord_statusCode: 'current', - techRecord_trainDesignWeight: 1, - techRecord_trainEecWeight: 1, - techRecord_trainGbWeight: 1, - techRecord_tyreUseCode: 'a', - techRecord_vehicleType: 'hgv', - techRecord_variantNumber: '1', - techRecord_roadFriendly: true, - techRecord_vehicleConfiguration: VehicleConfigurations.RIGID, - vin: 'HGVTEST01', - techRecord_axles: [], - } as unknown as TechRecordType<'hgv' | 'trl'>; + describe('validateTechRecordPlates', () => { + beforeEach(() => { + component.techRecord = { + primaryVrm: '123456', + techRecord_approvalType: 'NTA', + techRecord_approvalTypeNumber: '1', + techRecord_bodyType_code: 'b', + techRecord_bodyType_description: 'box', + techRecord_brakes_dtpNumber: '12345', + techRecord_dimensions_length: 1, + techRecord_dimensions_width: 1, + techRecord_frontVehicleTo5thWheelCouplingMax: 1, + techRecord_frontVehicleTo5thWheelCouplingMin: 1, + techRecord_functionCode: 'R', + techRecord_grossDesignWeight: 1, + techRecord_grossEecWeight: 1, + techRecord_grossGbWeight: 1, + techRecord_make: 'AEC', + techRecord_manufactureYear: 2020, + techRecord_maxTrainDesignWeight: 1, + techRecord_maxTrainEecWeight: 11, + techRecord_maxTrainGbWeight: 1, + techRecord_model: '123', + techRecord_noOfAxles: 1, + techRecord_ntaNumber: '1', + techRecord_reasonForCreation: 'Test', + techRecord_recordCompleteness: 'skeleton', + techRecord_regnDate: '2020-10-10', + techRecord_speedLimiterMrk: true, + techRecord_statusCode: 'current', + techRecord_trainDesignWeight: 1, + techRecord_trainEecWeight: 1, + techRecord_trainGbWeight: 1, + techRecord_tyreUseCode: 'a', + techRecord_vehicleType: 'hgv', + techRecord_variantNumber: '1', + techRecord_roadFriendly: true, + techRecord_vehicleConfiguration: VehicleConfigurations.RIGID, + vin: 'HGVTEST01', + techRecord_axles: [], + } as unknown as TechRecordType<'hgv' | 'trl'>; - component.techRecord.techRecord_axles = [ - { - parkingBrakeMrk: true, - axleNumber: 1, - brakes_brakeActuator: 1, - brakes_leverLength: 1, - brakes_springBrakeParking: true, - weights_gbWeight: 1, - weights_designWeight: 2, - weights_ladenWeight: 3, - weights_kerbWeight: 4, - weights_eecWeight: 5, - tyres_tyreCode: 1, - tyres_tyreSize: '2', - tyres_plyRating: '3', - tyres_fitmentCode: 'single', - tyres_dataTrAxles: 1, - tyres_speedCategorySymbol: 'a7', - }, - ]; - }); - it('should show an error if tech record is not valid for plates', () => { - component.techRecord.vin = ''; - const plateFieldsErrorMessage = 'All fields marked plate are mandatory to generate a plate.'; - const errorSpy = jest.spyOn(errorService, 'addError'); - component.validateTechRecordPlates(); - expect(errorSpy).toHaveBeenCalledWith({ error: plateFieldsErrorMessage }); - }); - it('should dispatch the canGeneratePlate action if the record is valid', () => { - fixture.ngZone?.run(() => { - const dispatchSpy = jest.spyOn(store, 'dispatch'); - component.validateTechRecordPlates(); - expect(dispatchSpy).toHaveBeenCalledWith(canGeneratePlate()); - }); - }); - it('should call router.navigate on a valid record', () => { - fixture.ngZone?.run(() => { - const navigateSpy = jest.spyOn(router, 'navigate'); - component.validateTechRecordPlates(); - expect(navigateSpy).toHaveBeenCalledWith(['generate-plate'], { relativeTo: expect.anything() }); - }); - }); - }); + component.techRecord.techRecord_axles = [ + { + parkingBrakeMrk: true, + axleNumber: 1, + brakes_brakeActuator: 1, + brakes_leverLength: 1, + brakes_springBrakeParking: true, + weights_gbWeight: 1, + weights_designWeight: 2, + weights_ladenWeight: 3, + weights_kerbWeight: 4, + weights_eecWeight: 5, + tyres_tyreCode: 1, + tyres_tyreSize: '2', + tyres_plyRating: '3', + tyres_fitmentCode: 'single', + tyres_dataTrAxles: 1, + tyres_speedCategorySymbol: 'a7', + }, + ]; + }); + it('should show an error if tech record is not valid for plates', () => { + component.techRecord.vin = ''; + const plateFieldsErrorMessage = 'All fields marked plate are mandatory to generate a plate.'; + const errorSpy = jest.spyOn(errorService, 'addError'); + component.validateTechRecordPlates(); + expect(errorSpy).toHaveBeenCalledWith({ error: plateFieldsErrorMessage }); + }); + it('should dispatch the canGeneratePlate action if the record is valid', () => { + fixture.ngZone?.run(() => { + const dispatchSpy = jest.spyOn(store, 'dispatch'); + component.validateTechRecordPlates(); + expect(dispatchSpy).toHaveBeenCalledWith(canGeneratePlate()); + }); + }); + it('should call router.navigate on a valid record', () => { + fixture.ngZone?.run(() => { + const navigateSpy = jest.spyOn(router, 'navigate'); + component.validateTechRecordPlates(); + expect(navigateSpy).toHaveBeenCalledWith(['generate-plate'], { relativeTo: expect.anything() }); + }); + }); + }); - describe('cannotGeneratePlate', () => { - it('should not generate if missing a tyre fitment code', () => { - component.techRecord = { - primaryVrm: 'thing', - vin: 'thing', - techRecord_brakes_dtpNumber: 'thing', - techRecord_regnDate: 'thing', - techRecord_manufactureYear: 1, - techRecord_speedLimiterMrk: true, - techRecord_variantNumber: 'thing', - techRecord_make: 'thing', - techRecord_model: 'thing', - techRecord_functionCode: 'thing', - techRecord_frontVehicleTo5thWheelCouplingMin: 1, - techRecord_frontVehicleTo5thWheelCouplingMax: 1, - techRecord_dimensions_length: 1, - techRecord_dimensions_width: 1, - techRecord_tyreUseCode: '2R', - techRecord_roadFriendly: true, - techRecord_vehicleConfiguration: VehicleConfiguration.ARTICULATED, - techRecord_axles: [{ - tyres_tyreSize: '215/25', weights_gbWeight: '123', tyres_plyRating: '2R', - }, - { - tyres_fitmentCode: 'single', tyres_tyreSize: '215/25', weights_gbWeight: '123', tyres_plyRating: '2R', - }], - techRecord_noOfAxles: 2, - } as unknown as TechRecordType<'hgv'>; - const res = component.cannotGeneratePlate(hgvRequiredFields); - expect(res).toBeTruthy(); - }); - it('should not generate if both axles missing a tyre fitment code', () => { - component.techRecord = { - primaryVrm: 'thing', - vin: 'thing', - techRecord_brakes_dtpNumber: 'thing', - techRecord_regnDate: 'thing', - techRecord_manufactureYear: 1, - techRecord_speedLimiterMrk: true, - techRecord_variantNumber: 'thing', - techRecord_make: 'thing', - techRecord_model: 'thing', - techRecord_functionCode: 'thing', - techRecord_frontVehicleTo5thWheelCouplingMin: 1, - techRecord_frontVehicleTo5thWheelCouplingMax: 1, - techRecord_dimensions_length: 1, - techRecord_dimensions_width: 1, - techRecord_tyreUseCode: '2R', - techRecord_roadFriendly: true, - techRecord_vehicleConfiguration: VehicleConfiguration.ARTICULATED, - techRecord_axles: [{ - tyres_tyreSize: '215/25', weights_gbWeight: '123', tyres_plyRating: '2R', - }, - { - tyres_tyreSize: '215/25', weights_gbWeight: '123', tyres_plyRating: '2R', - }], - techRecord_noOfAxles: 2, - } as unknown as TechRecordType<'hgv'>; - const res = component.cannotGeneratePlate(hgvRequiredFields); - expect(res).toBeTruthy(); - }); - it('should not generate if missing a tyre size', () => { - component.techRecord = { - primaryVrm: 'thing', - vin: 'thing', - techRecord_brakes_dtpNumber: 'thing', - techRecord_regnDate: 'thing', - techRecord_manufactureYear: 1, - techRecord_speedLimiterMrk: true, - techRecord_variantNumber: 'thing', - techRecord_make: 'thing', - techRecord_model: 'thing', - techRecord_functionCode: 'thing', - techRecord_frontVehicleTo5thWheelCouplingMin: 1, - techRecord_frontVehicleTo5thWheelCouplingMax: 1, - techRecord_dimensions_length: 1, - techRecord_dimensions_width: 1, - techRecord_tyreUseCode: '2R', - techRecord_roadFriendly: true, - techRecord_vehicleConfiguration: VehicleConfiguration.ARTICULATED, - techRecord_axles: [{ - tyres_fitmentCode: 'single', weights_gbWeight: '123', tyres_plyRating: '2R', - }, - { - tyres_fitmentCode: 'single', tyres_tyreSize: '215/25', weights_gbWeight: '123', tyres_plyRating: '2R', - }], - techRecord_noOfAxles: 2, - } as unknown as TechRecordType<'hgv'>; - const res = component.cannotGeneratePlate(hgvRequiredFields); - expect(res).toBeTruthy(); - }); - it('should not generate if a load/plyRating', () => { - component.techRecord = { - primaryVrm: 'thing', - vin: 'thing', - techRecord_brakes_dtpNumber: 'thing', - techRecord_regnDate: 'thing', - techRecord_manufactureYear: 1, - techRecord_speedLimiterMrk: true, - techRecord_variantNumber: 'thing', - techRecord_make: 'thing', - techRecord_model: 'thing', - techRecord_functionCode: 'thing', - techRecord_frontVehicleTo5thWheelCouplingMin: 1, - techRecord_frontVehicleTo5thWheelCouplingMax: 1, - techRecord_dimensions_length: 1, - techRecord_dimensions_width: 1, - techRecord_tyreUseCode: '2R', - techRecord_roadFriendly: true, - techRecord_vehicleConfiguration: VehicleConfiguration.ARTICULATED, - techRecord_axles: [{ - tyres_fitmentCode: 'single', tyres_tyreSize: '215/25', weights_gbWeight: '123', - }, - { - tyres_fitmentCode: 'single', tyres_tyreSize: '215/25', weights_gbWeight: '123', - }], - techRecord_noOfAxles: 2, - } as unknown as TechRecordType<'hgv'>; - const res = component.cannotGeneratePlate(hgvRequiredFields); - expect(res).toBeTruthy(); - }); - it('should not generate if missing axles', () => { - component.techRecord = { - primaryVrm: 'thing', - vin: 'thing', - techRecord_brakes_dtpNumber: 'thing', - techRecord_regnDate: 'thing', - techRecord_manufactureYear: 1, - techRecord_speedLimiterMrk: true, - techRecord_variantNumber: 'thing', - techRecord_make: 'thing', - techRecord_model: 'thing', - techRecord_functionCode: 'thing', - techRecord_frontVehicleTo5thWheelCouplingMin: 1, - techRecord_frontVehicleTo5thWheelCouplingMax: 1, - techRecord_dimensions_length: 1, - techRecord_dimensions_width: 1, - techRecord_tyreUseCode: '2R', - techRecord_roadFriendly: true, - techRecord_vehicleConfiguration: VehicleConfiguration.ARTICULATED, - techRecord_axles: [], - techRecord_noOfAxles: 0, - } as unknown as TechRecordType<'hgv'>; - const res = component.cannotGeneratePlate(hgvRequiredFields); - expect(res).toBeTruthy(); - }); - it('should not generate without gbWeight on axle 1', () => { - component.techRecord = { - primaryVrm: 'thing', - vin: 'thing', - techRecord_brakes_dtpNumber: 'thing', - techRecord_regnDate: 'thing', - techRecord_manufactureYear: 1, - techRecord_speedLimiterMrk: true, - techRecord_variantNumber: 'thing', - techRecord_make: 'thing', - techRecord_model: 'thing', - techRecord_functionCode: 'thing', - techRecord_frontVehicleTo5thWheelCouplingMin: 1, - techRecord_frontVehicleTo5thWheelCouplingMax: 1, - techRecord_dimensions_length: 1, - techRecord_dimensions_width: 1, - techRecord_tyreUseCode: '2R', - techRecord_roadFriendly: true, - techRecord_vehicleConfiguration: VehicleConfiguration.ARTICULATED, - techRecord_axles: [{ - tyres_fitmentCode: 'single', tyres_tyreSize: '215/25', tyres_plyRating: '2R', - }, - { - tyres_fitmentCode: 'single', tyres_tyreSize: '215/25', tyres_plyRating: '2R', - }], - techRecord_noOfAxles: 2, - } as unknown as TechRecordType<'hgv'>; - const res = component.cannotGeneratePlate(hgvRequiredFields); - expect(res).toBeTruthy(); - }); - it('should not generate with a missing required hgv validation field', () => { - component.techRecord = { - primaryVrm: 'thing', - vin: 'thing', - techRecord_brakes_dtpNumber: 'thing', - techRecord_regnDate: 'thing', - techRecord_manufactureYear: 1, - techRecord_speedLimiterMrk: true, - techRecord_variantNumber: 'thing', - techRecord_model: 'thing', - techRecord_functionCode: 'thing', - techRecord_frontVehicleTo5thWheelCouplingMin: 1, - techRecord_frontVehicleTo5thWheelCouplingMax: 1, - techRecord_dimensions_length: 1, - techRecord_dimensions_width: 1, - techRecord_tyreUseCode: '2R', - techRecord_roadFriendly: true, - techRecord_vehicleConfiguration: VehicleConfiguration.ARTICULATED, - techRecord_axles: [{ - tyres_fitmentCode: 'single', tyres_tyreSize: '215/25', weights_gbWeight: '123', tyres_plyRating: '2R', - }, - { - tyres_fitmentCode: 'single', tyres_tyreSize: '215/25', weights_gbWeight: '123', tyres_plyRating: '2R', - }], - techRecord_noOfAxles: 2, - } as unknown as TechRecordType<'hgv'>; - const res = component.cannotGeneratePlate(hgvRequiredFields); - expect(res).toBeTruthy(); - }); - it('should generate with a hgv plate', () => { - component.techRecord = { - primaryVrm: 'thing', - vin: 'thing', - techRecord_brakes_dtpNumber: 'thing', - techRecord_regnDate: 'thing', - techRecord_manufactureYear: 1, - techRecord_speedLimiterMrk: true, - techRecord_variantNumber: 'thing', - techRecord_make: 'thing', - techRecord_model: 'thing', - techRecord_functionCode: 'thing', - techRecord_frontVehicleTo5thWheelCouplingMin: 1, - techRecord_frontVehicleTo5thWheelCouplingMax: 1, - techRecord_dimensions_length: 1, - techRecord_dimensions_width: 1, - techRecord_tyreUseCode: '2R', - techRecord_roadFriendly: true, - techRecord_vehicleConfiguration: VehicleConfiguration.ARTICULATED, - techRecord_axles: [{ - tyres_fitmentCode: 'single', tyres_tyreSize: '215/25', weights_gbWeight: '123', tyres_plyRating: '2R', - }, - { - tyres_fitmentCode: 'single', tyres_tyreSize: '215/25', weights_gbWeight: '123', tyres_plyRating: '2R', - }], - techRecord_noOfAxles: 2, - } as unknown as TechRecordType<'hgv'>; - const res = component.cannotGeneratePlate(hgvRequiredFields); - expect(res).toBeFalsy(); - }); - }); + describe('cannotGeneratePlate', () => { + it('should not generate if missing a tyre fitment code', () => { + component.techRecord = { + primaryVrm: 'thing', + vin: 'thing', + techRecord_brakes_dtpNumber: 'thing', + techRecord_regnDate: 'thing', + techRecord_manufactureYear: 1, + techRecord_speedLimiterMrk: true, + techRecord_variantNumber: 'thing', + techRecord_make: 'thing', + techRecord_model: 'thing', + techRecord_functionCode: 'thing', + techRecord_frontVehicleTo5thWheelCouplingMin: 1, + techRecord_frontVehicleTo5thWheelCouplingMax: 1, + techRecord_dimensions_length: 1, + techRecord_dimensions_width: 1, + techRecord_tyreUseCode: '2R', + techRecord_roadFriendly: true, + techRecord_vehicleConfiguration: VehicleConfiguration.ARTICULATED, + techRecord_axles: [ + { + tyres_tyreSize: '215/25', + weights_gbWeight: '123', + tyres_plyRating: '2R', + }, + { + tyres_fitmentCode: 'single', + tyres_tyreSize: '215/25', + weights_gbWeight: '123', + tyres_plyRating: '2R', + }, + ], + techRecord_noOfAxles: 2, + } as unknown as TechRecordType<'hgv'>; + const res = component.cannotGeneratePlate(hgvRequiredFields); + expect(res).toBeTruthy(); + }); + it('should not generate if both axles missing a tyre fitment code', () => { + component.techRecord = { + primaryVrm: 'thing', + vin: 'thing', + techRecord_brakes_dtpNumber: 'thing', + techRecord_regnDate: 'thing', + techRecord_manufactureYear: 1, + techRecord_speedLimiterMrk: true, + techRecord_variantNumber: 'thing', + techRecord_make: 'thing', + techRecord_model: 'thing', + techRecord_functionCode: 'thing', + techRecord_frontVehicleTo5thWheelCouplingMin: 1, + techRecord_frontVehicleTo5thWheelCouplingMax: 1, + techRecord_dimensions_length: 1, + techRecord_dimensions_width: 1, + techRecord_tyreUseCode: '2R', + techRecord_roadFriendly: true, + techRecord_vehicleConfiguration: VehicleConfiguration.ARTICULATED, + techRecord_axles: [ + { + tyres_tyreSize: '215/25', + weights_gbWeight: '123', + tyres_plyRating: '2R', + }, + { + tyres_tyreSize: '215/25', + weights_gbWeight: '123', + tyres_plyRating: '2R', + }, + ], + techRecord_noOfAxles: 2, + } as unknown as TechRecordType<'hgv'>; + const res = component.cannotGeneratePlate(hgvRequiredFields); + expect(res).toBeTruthy(); + }); + it('should not generate if missing a tyre size', () => { + component.techRecord = { + primaryVrm: 'thing', + vin: 'thing', + techRecord_brakes_dtpNumber: 'thing', + techRecord_regnDate: 'thing', + techRecord_manufactureYear: 1, + techRecord_speedLimiterMrk: true, + techRecord_variantNumber: 'thing', + techRecord_make: 'thing', + techRecord_model: 'thing', + techRecord_functionCode: 'thing', + techRecord_frontVehicleTo5thWheelCouplingMin: 1, + techRecord_frontVehicleTo5thWheelCouplingMax: 1, + techRecord_dimensions_length: 1, + techRecord_dimensions_width: 1, + techRecord_tyreUseCode: '2R', + techRecord_roadFriendly: true, + techRecord_vehicleConfiguration: VehicleConfiguration.ARTICULATED, + techRecord_axles: [ + { + tyres_fitmentCode: 'single', + weights_gbWeight: '123', + tyres_plyRating: '2R', + }, + { + tyres_fitmentCode: 'single', + tyres_tyreSize: '215/25', + weights_gbWeight: '123', + tyres_plyRating: '2R', + }, + ], + techRecord_noOfAxles: 2, + } as unknown as TechRecordType<'hgv'>; + const res = component.cannotGeneratePlate(hgvRequiredFields); + expect(res).toBeTruthy(); + }); + it('should not generate if a load/plyRating', () => { + component.techRecord = { + primaryVrm: 'thing', + vin: 'thing', + techRecord_brakes_dtpNumber: 'thing', + techRecord_regnDate: 'thing', + techRecord_manufactureYear: 1, + techRecord_speedLimiterMrk: true, + techRecord_variantNumber: 'thing', + techRecord_make: 'thing', + techRecord_model: 'thing', + techRecord_functionCode: 'thing', + techRecord_frontVehicleTo5thWheelCouplingMin: 1, + techRecord_frontVehicleTo5thWheelCouplingMax: 1, + techRecord_dimensions_length: 1, + techRecord_dimensions_width: 1, + techRecord_tyreUseCode: '2R', + techRecord_roadFriendly: true, + techRecord_vehicleConfiguration: VehicleConfiguration.ARTICULATED, + techRecord_axles: [ + { + tyres_fitmentCode: 'single', + tyres_tyreSize: '215/25', + weights_gbWeight: '123', + }, + { + tyres_fitmentCode: 'single', + tyres_tyreSize: '215/25', + weights_gbWeight: '123', + }, + ], + techRecord_noOfAxles: 2, + } as unknown as TechRecordType<'hgv'>; + const res = component.cannotGeneratePlate(hgvRequiredFields); + expect(res).toBeTruthy(); + }); + it('should not generate if missing axles', () => { + component.techRecord = { + primaryVrm: 'thing', + vin: 'thing', + techRecord_brakes_dtpNumber: 'thing', + techRecord_regnDate: 'thing', + techRecord_manufactureYear: 1, + techRecord_speedLimiterMrk: true, + techRecord_variantNumber: 'thing', + techRecord_make: 'thing', + techRecord_model: 'thing', + techRecord_functionCode: 'thing', + techRecord_frontVehicleTo5thWheelCouplingMin: 1, + techRecord_frontVehicleTo5thWheelCouplingMax: 1, + techRecord_dimensions_length: 1, + techRecord_dimensions_width: 1, + techRecord_tyreUseCode: '2R', + techRecord_roadFriendly: true, + techRecord_vehicleConfiguration: VehicleConfiguration.ARTICULATED, + techRecord_axles: [], + techRecord_noOfAxles: 0, + } as unknown as TechRecordType<'hgv'>; + const res = component.cannotGeneratePlate(hgvRequiredFields); + expect(res).toBeTruthy(); + }); + it('should not generate without gbWeight on axle 1', () => { + component.techRecord = { + primaryVrm: 'thing', + vin: 'thing', + techRecord_brakes_dtpNumber: 'thing', + techRecord_regnDate: 'thing', + techRecord_manufactureYear: 1, + techRecord_speedLimiterMrk: true, + techRecord_variantNumber: 'thing', + techRecord_make: 'thing', + techRecord_model: 'thing', + techRecord_functionCode: 'thing', + techRecord_frontVehicleTo5thWheelCouplingMin: 1, + techRecord_frontVehicleTo5thWheelCouplingMax: 1, + techRecord_dimensions_length: 1, + techRecord_dimensions_width: 1, + techRecord_tyreUseCode: '2R', + techRecord_roadFriendly: true, + techRecord_vehicleConfiguration: VehicleConfiguration.ARTICULATED, + techRecord_axles: [ + { + tyres_fitmentCode: 'single', + tyres_tyreSize: '215/25', + tyres_plyRating: '2R', + }, + { + tyres_fitmentCode: 'single', + tyres_tyreSize: '215/25', + tyres_plyRating: '2R', + }, + ], + techRecord_noOfAxles: 2, + } as unknown as TechRecordType<'hgv'>; + const res = component.cannotGeneratePlate(hgvRequiredFields); + expect(res).toBeTruthy(); + }); + it('should not generate with a missing required hgv validation field', () => { + component.techRecord = { + primaryVrm: 'thing', + vin: 'thing', + techRecord_brakes_dtpNumber: 'thing', + techRecord_regnDate: 'thing', + techRecord_manufactureYear: 1, + techRecord_speedLimiterMrk: true, + techRecord_variantNumber: 'thing', + techRecord_model: 'thing', + techRecord_functionCode: 'thing', + techRecord_frontVehicleTo5thWheelCouplingMin: 1, + techRecord_frontVehicleTo5thWheelCouplingMax: 1, + techRecord_dimensions_length: 1, + techRecord_dimensions_width: 1, + techRecord_tyreUseCode: '2R', + techRecord_roadFriendly: true, + techRecord_vehicleConfiguration: VehicleConfiguration.ARTICULATED, + techRecord_axles: [ + { + tyres_fitmentCode: 'single', + tyres_tyreSize: '215/25', + weights_gbWeight: '123', + tyres_plyRating: '2R', + }, + { + tyres_fitmentCode: 'single', + tyres_tyreSize: '215/25', + weights_gbWeight: '123', + tyres_plyRating: '2R', + }, + ], + techRecord_noOfAxles: 2, + } as unknown as TechRecordType<'hgv'>; + const res = component.cannotGeneratePlate(hgvRequiredFields); + expect(res).toBeTruthy(); + }); + it('should generate with a hgv plate', () => { + component.techRecord = { + primaryVrm: 'thing', + vin: 'thing', + techRecord_brakes_dtpNumber: 'thing', + techRecord_regnDate: 'thing', + techRecord_manufactureYear: 1, + techRecord_speedLimiterMrk: true, + techRecord_variantNumber: 'thing', + techRecord_make: 'thing', + techRecord_model: 'thing', + techRecord_functionCode: 'thing', + techRecord_frontVehicleTo5thWheelCouplingMin: 1, + techRecord_frontVehicleTo5thWheelCouplingMax: 1, + techRecord_dimensions_length: 1, + techRecord_dimensions_width: 1, + techRecord_tyreUseCode: '2R', + techRecord_roadFriendly: true, + techRecord_vehicleConfiguration: VehicleConfiguration.ARTICULATED, + techRecord_axles: [ + { + tyres_fitmentCode: 'single', + tyres_tyreSize: '215/25', + weights_gbWeight: '123', + tyres_plyRating: '2R', + }, + { + tyres_fitmentCode: 'single', + tyres_tyreSize: '215/25', + weights_gbWeight: '123', + tyres_plyRating: '2R', + }, + ], + techRecord_noOfAxles: 2, + } as unknown as TechRecordType<'hgv'>; + const res = component.cannotGeneratePlate(hgvRequiredFields); + expect(res).toBeFalsy(); + }); + }); }); diff --git a/src/app/forms/custom-sections/plates/plates.component.ts b/src/app/forms/custom-sections/plates/plates.component.ts index 572fd06cb4..b76a8969e9 100644 --- a/src/app/forms/custom-sections/plates/plates.component.ts +++ b/src/app/forms/custom-sections/plates/plates.component.ts @@ -1,7 +1,5 @@ import { ViewportScroller } from '@angular/common'; -import { - ChangeDetectorRef, Component, EventEmitter, Input, OnChanges, OnDestroy, OnInit, Output, -} from '@angular/core'; +import { ChangeDetectorRef, Component, EventEmitter, Input, OnChanges, OnDestroy, OnInit, Output } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; import { GlobalErrorService } from '@core/components/global-error/global-error.service'; import { HGVPlates } from '@dvsa/cvs-type-definitions/types/v3/tech-record/get/hgv/complete'; @@ -20,152 +18,161 @@ import { cloneDeep } from 'lodash'; import { Subscription, debounceTime } from 'rxjs'; @Component({ - selector: 'app-plates[techRecord]', - templateUrl: './plates.component.html', - styleUrls: ['./plates.component.scss'], + selector: 'app-plates[techRecord]', + templateUrl: './plates.component.html', + styleUrls: ['./plates.component.scss'], }) export class PlatesComponent implements OnInit, OnDestroy, OnChanges { - @Input() techRecord!: TechRecordType<'hgv' | 'trl'>; - @Input() isEditing = false; - - @Output() formChange = new EventEmitter(); - - form!: CustomFormGroup; - pageStart?: number; - pageEnd?: number; - - private formSubscription = new Subscription(); - - constructor( - private dynamicFormService: DynamicFormService, - private cdr: ChangeDetectorRef, - private globalErrorService: GlobalErrorService, - private router: Router, - private route: ActivatedRoute, - private store: Store, - private viewportScroller: ViewportScroller, - ) {} - - ngOnInit(): void { - this.form = this.dynamicFormService.createForm(PlatesTemplate, this.techRecord) as CustomFormGroup; - this.formSubscription = this.form.cleanValueChanges.pipe(debounceTime(400)).subscribe((event) => this.formChange.emit(event)); - } - - ngOnChanges(): void { - this.form?.patchValue(this.techRecord, { emitEvent: false }); - } - - ngOnDestroy(): void { - this.formSubscription.unsubscribe(); - } - - get roles(): typeof Roles { - return Roles; - } - - get types(): typeof FormNodeEditTypes { - return FormNodeEditTypes; - } - - get hasPlates(): boolean { - return !!this.techRecord.techRecord_plates?.length; - } - - get sortedPlates(): HGVPlates[] | TRLPlates[] | undefined { - return cloneDeep(this.techRecord.techRecord_plates)?.sort((a, b) => - a.plateIssueDate && b.plateIssueDate ? new Date(b.plateIssueDate).getTime() - new Date(a.plateIssueDate).getTime() : 0); - } - - get plates() { - return this.sortedPlates?.slice(this.pageStart, this.pageEnd) ?? []; - } - - get mostRecentPlate() { - return cloneDeep(this.techRecord.techRecord_plates) - ?.sort((a, b) => - a.plateIssueDate && b.plateIssueDate ? new Date(a.plateIssueDate).getTime() - new Date(b.plateIssueDate).getTime() : 0) - ?.pop(); - } - - get numberOfPlates(): number { - return this.sortedPlates?.length || 0; - } - - handlePaginationChange({ start, end }: { start: number; end: number }) { - this.pageStart = start; - this.pageEnd = end; - this.cdr.detectChanges(); - } - - trackByFn(i: number, tr: HGVPlates | TRLPlates) { - return tr.plateIssueDate; - } - - get documentParams(): Map { - return new Map([['plateSerialNumber', this.fileName]]); - } - - get fileName(): string { - if (this.mostRecentPlate) { - return `plate_${this.mostRecentPlate.plateSerialNumber}`; - } - throw new Error('Could not find plate.'); - } - - get eligibleForPlates(): boolean { - return this.techRecord.techRecord_statusCode === StatusCodes.CURRENT && !this.isEditing; - } - - get reasonForIneligibility(): string { - if (this.isEditing) { - return 'This section is not available when amending or creating a technical record.'; - } - - if (this.techRecord.techRecord_statusCode !== StatusCodes.CURRENT) { - return 'Generating plates is only applicable to current technical records.'; - } - return ''; - } - - validateTechRecordPlates(): void { - this.globalErrorService.clearErrors(); - const plateValidationTable = this.techRecord.techRecord_vehicleType === 'trl' ? trlRequiredFields : hgvRequiredFields; - - if (this.cannotGeneratePlate(plateValidationTable)) { - this.viewportScroller.scrollToPosition([0, 0]); - this.globalErrorService.addError({ error: 'All fields marked plate are mandatory to generate a plate.' }); - return; - } - this.store.dispatch(canGeneratePlate()); - this.store.dispatch(updateScrollPosition({ position: this.viewportScroller.getScrollPosition() })); - void this.router.navigate(['generate-plate'], { relativeTo: this.route }); - } - - cannotGeneratePlate(plateRequiredFields: string[]): boolean { - const isOneFieldEmpty = plateRequiredFields.some((field) => { - const value = this.techRecord[field as keyof TechRecordType<'hgv' | 'trl'>]; - return value === undefined || value === null || value === ''; - }); - - // Only gbWeight of Axle 1 is required - const { techRecord_noOfAxles: noOfAxles, techRecord_axles: axles } = this.techRecord; - const areAxlesInvalid = !noOfAxles || noOfAxles < 1 || !axles || axles[0].weights_gbWeight == null; - - // check tyre fields - const areTyresInvalid = this.techRecord.techRecord_axles?.some((axle) => { - const tyreRequiredInvalid = tyreRequiredFields.map((field) => { - const value = axle[field as keyof Axle<'hgv'>]; - return value === undefined || value === null || value === ''; - }); - - if (tyreRequiredInvalid.some((value) => value)) { - return true; - } - // either one of ply rating or load index is required - const plyOrLoad = axle['tyres_plyRating' as keyof Axle<'hgv'>] || axle['tyres_dataTrAxles' as keyof Axle<'hgv'>]; - return plyOrLoad === undefined || plyOrLoad === null || plyOrLoad === ''; - }); - - return isOneFieldEmpty || areAxlesInvalid || !!areTyresInvalid; - } + @Input() techRecord!: TechRecordType<'hgv' | 'trl'>; + @Input() isEditing = false; + + @Output() formChange = new EventEmitter(); + + form!: CustomFormGroup; + pageStart?: number; + pageEnd?: number; + + private formSubscription = new Subscription(); + + constructor( + private dynamicFormService: DynamicFormService, + private cdr: ChangeDetectorRef, + private globalErrorService: GlobalErrorService, + private router: Router, + private route: ActivatedRoute, + private store: Store, + private viewportScroller: ViewportScroller + ) {} + + ngOnInit(): void { + this.form = this.dynamicFormService.createForm(PlatesTemplate, this.techRecord) as CustomFormGroup; + this.formSubscription = this.form.cleanValueChanges + .pipe(debounceTime(400)) + .subscribe((event) => this.formChange.emit(event)); + } + + ngOnChanges(): void { + this.form?.patchValue(this.techRecord, { emitEvent: false }); + } + + ngOnDestroy(): void { + this.formSubscription.unsubscribe(); + } + + get roles(): typeof Roles { + return Roles; + } + + get types(): typeof FormNodeEditTypes { + return FormNodeEditTypes; + } + + get hasPlates(): boolean { + return !!this.techRecord.techRecord_plates?.length; + } + + get sortedPlates(): HGVPlates[] | TRLPlates[] | undefined { + return cloneDeep(this.techRecord.techRecord_plates)?.sort((a, b) => + a.plateIssueDate && b.plateIssueDate + ? new Date(b.plateIssueDate).getTime() - new Date(a.plateIssueDate).getTime() + : 0 + ); + } + + get plates() { + return this.sortedPlates?.slice(this.pageStart, this.pageEnd) ?? []; + } + + get mostRecentPlate() { + return cloneDeep(this.techRecord.techRecord_plates) + ?.sort((a, b) => + a.plateIssueDate && b.plateIssueDate + ? new Date(a.plateIssueDate).getTime() - new Date(b.plateIssueDate).getTime() + : 0 + ) + ?.pop(); + } + + get numberOfPlates(): number { + return this.sortedPlates?.length || 0; + } + + handlePaginationChange({ start, end }: { start: number; end: number }) { + this.pageStart = start; + this.pageEnd = end; + this.cdr.detectChanges(); + } + + trackByFn(i: number, tr: HGVPlates | TRLPlates) { + return tr.plateIssueDate; + } + + get documentParams(): Map { + return new Map([['plateSerialNumber', this.fileName]]); + } + + get fileName(): string { + if (this.mostRecentPlate) { + return `plate_${this.mostRecentPlate.plateSerialNumber}`; + } + throw new Error('Could not find plate.'); + } + + get eligibleForPlates(): boolean { + return this.techRecord.techRecord_statusCode === StatusCodes.CURRENT && !this.isEditing; + } + + get reasonForIneligibility(): string { + if (this.isEditing) { + return 'This section is not available when amending or creating a technical record.'; + } + + if (this.techRecord.techRecord_statusCode !== StatusCodes.CURRENT) { + return 'Generating plates is only applicable to current technical records.'; + } + return ''; + } + + validateTechRecordPlates(): void { + this.globalErrorService.clearErrors(); + const plateValidationTable = + this.techRecord.techRecord_vehicleType === 'trl' ? trlRequiredFields : hgvRequiredFields; + + if (this.cannotGeneratePlate(plateValidationTable)) { + this.viewportScroller.scrollToPosition([0, 0]); + this.globalErrorService.addError({ error: 'All fields marked plate are mandatory to generate a plate.' }); + return; + } + this.store.dispatch(canGeneratePlate()); + this.store.dispatch(updateScrollPosition({ position: this.viewportScroller.getScrollPosition() })); + void this.router.navigate(['generate-plate'], { relativeTo: this.route }); + } + + cannotGeneratePlate(plateRequiredFields: string[]): boolean { + const isOneFieldEmpty = plateRequiredFields.some((field) => { + const value = this.techRecord[field as keyof TechRecordType<'hgv' | 'trl'>]; + return value === undefined || value === null || value === ''; + }); + + // Only gbWeight of Axle 1 is required + const { techRecord_noOfAxles: noOfAxles, techRecord_axles: axles } = this.techRecord; + const areAxlesInvalid = !noOfAxles || noOfAxles < 1 || !axles || axles[0].weights_gbWeight == null; + + // check tyre fields + const areTyresInvalid = this.techRecord.techRecord_axles?.some((axle) => { + const tyreRequiredInvalid = tyreRequiredFields.map((field) => { + const value = axle[field as keyof Axle<'hgv'>]; + return value === undefined || value === null || value === ''; + }); + + if (tyreRequiredInvalid.some((value) => value)) { + return true; + } + // either one of ply rating or load index is required + const plyOrLoad = axle['tyres_plyRating' as keyof Axle<'hgv'>] || axle['tyres_dataTrAxles' as keyof Axle<'hgv'>]; + return plyOrLoad === undefined || plyOrLoad === null || plyOrLoad === ''; + }); + + return isOneFieldEmpty || areAxlesInvalid || !!areTyresInvalid; + } } diff --git a/src/app/forms/custom-sections/psv-brakes/psv-brakes.component.spec.ts b/src/app/forms/custom-sections/psv-brakes/psv-brakes.component.spec.ts index 474090df76..9be07a56a1 100644 --- a/src/app/forms/custom-sections/psv-brakes/psv-brakes.component.spec.ts +++ b/src/app/forms/custom-sections/psv-brakes/psv-brakes.component.spec.ts @@ -2,7 +2,10 @@ import { HttpClientTestingModule } from '@angular/common/http/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing'; import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { RouterTestingModule } from '@angular/router/testing'; -import { TechRecordPSV, TechRecordType } from '@dvsa/cvs-type-definitions/types/v3/tech-record/tech-record-vehicle-type'; +import { + TechRecordPSV, + TechRecordType, +} from '@dvsa/cvs-type-definitions/types/v3/tech-record/tech-record-vehicle-type'; import { DynamicFormsModule } from '@forms/dynamic-forms.module'; import { MultiOptionsService } from '@forms/services/multi-options.service'; import { mockVehicleTechnicalRecord } from '@mocks/mock-vehicle-technical-record.mock'; @@ -13,109 +16,113 @@ import { initialAppState } from '@store/index'; import { PsvBrakesComponent } from './psv-brakes.component'; describe('PsvBrakesComponent', () => { - let component: PsvBrakesComponent; - let fixture: ComponentFixture; + let component: PsvBrakesComponent; + let fixture: ComponentFixture; - beforeEach(async () => { - await TestBed.configureTestingModule({ - declarations: [PsvBrakesComponent], - imports: [DynamicFormsModule, FormsModule, HttpClientTestingModule, ReactiveFormsModule, RouterTestingModule], - providers: [ - MultiOptionsService, - provideMockStore({ initialState: initialAppState }), - ReferenceDataService, - { provide: UserService, useValue: {} }, - ], - }).compileComponents(); - }); + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [PsvBrakesComponent], + imports: [DynamicFormsModule, FormsModule, HttpClientTestingModule, ReactiveFormsModule, RouterTestingModule], + providers: [ + MultiOptionsService, + provideMockStore({ initialState: initialAppState }), + ReferenceDataService, + { provide: UserService, useValue: {} }, + ], + }).compileComponents(); + }); - beforeEach(() => { - fixture = TestBed.createComponent(PsvBrakesComponent); - component = fixture.componentInstance; + beforeEach(() => { + fixture = TestBed.createComponent(PsvBrakesComponent); + component = fixture.componentInstance; - component.vehicleTechRecord = mockVehicleTechnicalRecord('psv') as TechRecordType<'psv'>; - component.vehicleTechRecord.techRecord_axles = [ - { - axleNumber: 1, - tyres_tyreSize: '295/80-22.5', - tyres_speedCategorySymbol: 'p', - tyres_fitmentCode: 'double', - tyres_dataTrAxles: 0, - tyres_plyRating: 'A', - tyres_tyreCode: 456, - parkingBrakeMrk: false, + component.vehicleTechRecord = mockVehicleTechnicalRecord('psv') as TechRecordType<'psv'>; + component.vehicleTechRecord.techRecord_axles = [ + { + axleNumber: 1, + tyres_tyreSize: '295/80-22.5', + tyres_speedCategorySymbol: 'p', + tyres_fitmentCode: 'double', + tyres_dataTrAxles: 0, + tyres_plyRating: 'A', + tyres_tyreCode: 456, + parkingBrakeMrk: false, - weights_kerbWeight: 1, - weights_ladenWeight: 2, - weights_gbWeight: 3, - // TODO: V3 2 eecweights in type package, which is this? - // weights_eecWeight: 4, - weights_designWeight: 5, - }, - { - axleNumber: 2, - parkingBrakeMrk: true, + weights_kerbWeight: 1, + weights_ladenWeight: 2, + weights_gbWeight: 3, + // TODO: V3 2 eecweights in type package, which is this? + // weights_eecWeight: 4, + weights_designWeight: 5, + }, + { + axleNumber: 2, + parkingBrakeMrk: true, - tyres_tyreSize: '295/80-22.5', - tyres_speedCategorySymbol: 'p', - tyres_fitmentCode: 'double', - tyres_dataTrAxles: 0, - tyres_plyRating: 'A', - tyres_tyreCode: 456, + tyres_tyreSize: '295/80-22.5', + tyres_speedCategorySymbol: 'p', + tyres_fitmentCode: 'double', + tyres_dataTrAxles: 0, + tyres_plyRating: 'A', + tyres_tyreCode: 456, - weights_kerbWeight: 1, - weights_ladenWeight: 2, - weights_gbWeight: 3, - // weights_eecWeight: 4, - weights_designWeight: 5, - }, - { - axleNumber: 3, - parkingBrakeMrk: true, + weights_kerbWeight: 1, + weights_ladenWeight: 2, + weights_gbWeight: 3, + // weights_eecWeight: 4, + weights_designWeight: 5, + }, + { + axleNumber: 3, + parkingBrakeMrk: true, - tyres_tyreSize: '295/80-22.5', - tyres_speedCategorySymbol: 'p', - tyres_fitmentCode: 'double', - tyres_dataTrAxles: 0, - tyres_plyRating: 'A', - tyres_tyreCode: 456, + tyres_tyreSize: '295/80-22.5', + tyres_speedCategorySymbol: 'p', + tyres_fitmentCode: 'double', + tyres_dataTrAxles: 0, + tyres_plyRating: 'A', + tyres_tyreCode: 456, - weights_kerbWeight: 1, - weights_ladenWeight: 2, - weights_gbWeight: 3, - // weights_eecWeight: 4, - weights_designWeight: 5, - }, - ]; + weights_kerbWeight: 1, + weights_ladenWeight: 2, + weights_gbWeight: 3, + // weights_eecWeight: 4, + weights_designWeight: 5, + }, + ]; - fixture.detectChanges(); - }); + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); - describe('The brake code value on this.form', () => { - it('should match the corresponding values on vehicleTechRecord', () => { - expect(component.vehicleTechRecord?.techRecord_brakes_brakeCode).toStrictEqual(component.form.value.techRecord_brakes_brakeCode); - }); - }); - describe('The dataTrBrakeOne value on this.form', () => { - it('should match the corresponding values on vehicleTechRecord', () => { - expect(component.vehicleTechRecord?.techRecord_brakes_retarderBrakeOne).toStrictEqual(component.form.value.techRecord_brakes_retarderBrakeOne); - }); - }); - describe('The brakeCodeOriginal value on this.form', () => { - it('should match the corresponding values on vehicleTechRecord', () => { - expect(component.vehicleTechRecord?.techRecord_brakes_brakeCodeOriginal).toStrictEqual( - component.form.controls['techRecord_brakes_brakeCodeOriginal']?.value, - ); - }); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); + describe('The brake code value on this.form', () => { + it('should match the corresponding values on vehicleTechRecord', () => { + expect(component.vehicleTechRecord?.techRecord_brakes_brakeCode).toStrictEqual( + component.form.value.techRecord_brakes_brakeCode + ); + }); + }); + describe('The dataTrBrakeOne value on this.form', () => { + it('should match the corresponding values on vehicleTechRecord', () => { + expect(component.vehicleTechRecord?.techRecord_brakes_retarderBrakeOne).toStrictEqual( + component.form.value.techRecord_brakes_retarderBrakeOne + ); + }); + }); + describe('The brakeCodeOriginal value on this.form', () => { + it('should match the corresponding values on vehicleTechRecord', () => { + expect(component.vehicleTechRecord?.techRecord_brakes_brakeCodeOriginal).toStrictEqual( + component.form.controls['techRecord_brakes_brakeCodeOriginal']?.value + ); + }); + }); - describe('The axle value on this.form', () => { - it('should match the corresponding values on vehicleTechRecord', () => { - const axles = component.vehicleTechRecord?.techRecord_axles as NonNullable; - expect(axles[0]).toEqual(expect.objectContaining(component.form.controls['techRecord_axles']?.value[0])); - }); - }); + describe('The axle value on this.form', () => { + it('should match the corresponding values on vehicleTechRecord', () => { + const axles = component.vehicleTechRecord?.techRecord_axles as NonNullable; + expect(axles[0]).toEqual(expect.objectContaining(component.form.controls['techRecord_axles']?.value[0])); + }); + }); }); diff --git a/src/app/forms/custom-sections/psv-brakes/psv-brakes.component.ts b/src/app/forms/custom-sections/psv-brakes/psv-brakes.component.ts index f715f7aed1..cfc142c26a 100644 --- a/src/app/forms/custom-sections/psv-brakes/psv-brakes.component.ts +++ b/src/app/forms/custom-sections/psv-brakes/psv-brakes.component.ts @@ -1,13 +1,9 @@ -import { - Component, EventEmitter, Input, OnChanges, OnDestroy, OnInit, Output, SimpleChanges, -} from '@angular/core'; +import { Component, EventEmitter, Input, OnChanges, OnDestroy, OnInit, Output, SimpleChanges } from '@angular/core'; import { FormArray, FormGroup } from '@angular/forms'; import { TechRecordType } from '@dvsa/cvs-type-definitions/types/v3/tech-record/tech-record-vehicle-type'; import { MultiOptions } from '@forms/models/options.model'; import { DynamicFormService } from '@forms/services/dynamic-form.service'; -import { - CustomFormGroup, FormNode, FormNodeEditTypes, FormNodeWidth, -} from '@forms/services/dynamic-form.types'; +import { CustomFormGroup, FormNode, FormNodeEditTypes, FormNodeWidth } from '@forms/services/dynamic-form.types'; import { MultiOptionsService } from '@forms/services/multi-options.service'; import { PsvBrakesTemplate } from '@forms/templates/psv/psv-brakes.template'; import { getOptionsFromEnum } from '@forms/utils/enum-map'; @@ -17,135 +13,137 @@ import { Store } from '@ngrx/store'; import { ReferenceDataState, selectBrakeByCode } from '@store/reference-data'; import { updateBrakeForces } from '@store/technical-records'; import { TechnicalRecordServiceState } from '@store/technical-records/reducers/technical-record-service.reducer'; -import { - debounceTime, Observable, of, Subject, switchMap, takeUntil, withLatestFrom, -} from 'rxjs'; +import { Observable, Subject, debounceTime, of, switchMap, takeUntil, withLatestFrom } from 'rxjs'; @Component({ - selector: 'app-psv-brakes', - templateUrl: './psv-brakes.component.html', - styleUrls: ['./psv-brakes.component.scss'], + selector: 'app-psv-brakes', + templateUrl: './psv-brakes.component.html', + styleUrls: ['./psv-brakes.component.scss'], }) export class PsvBrakesComponent implements OnInit, OnChanges, OnDestroy { - @Input() vehicleTechRecord?: TechRecordType<'psv'>; - @Input() isEditing = false; - - @Output() formChange = new EventEmitter(); - - form!: CustomFormGroup; - template!: FormNode; - - selectedBrake?: Brake; - - private destroy$ = new Subject(); - - constructor( - private dfs: DynamicFormService, - private optionsService: MultiOptionsService, - private referenceDataStore: Store, - private store: Store, - ) {} - - ngOnInit(): void { - this.form = this.dfs.createForm(PsvBrakesTemplate, this.vehicleTechRecord) as CustomFormGroup; - - (this.form.cleanValueChanges as Observable>>) - .pipe( - switchMap((event) => { - return event?.techRecord_brakes_brakeCodeOriginal - ? this.referenceDataStore.select(selectBrakeByCode(event.techRecord_brakes_brakeCodeOriginal)) - : of(undefined); - }), - withLatestFrom(this.form.cleanValueChanges as Observable>>), - debounceTime(400), - takeUntil(this.destroy$), - ) - .subscribe(([selectedBrake, event]) => { - // Set the brake details automatically based selection - if (selectedBrake && event?.techRecord_brakes_brakeCodeOriginal) { - event.techRecord_brakeCode = `${this.brakeCodePrefix}${selectedBrake.resourceKey}`; - event.techRecord_brakes_brakeCode = `${this.brakeCodePrefix}${selectedBrake.resourceKey}`; - event.techRecord_brakes_dataTrBrakeOne = selectedBrake.service; - event.techRecord_brakes_dataTrBrakeTwo = selectedBrake.secondary; - event.techRecord_brakes_dataTrBrakeThree = selectedBrake.parking; - } - - if (event?.techRecord_axles) { - event.techRecord_axles = event.techRecord_axles.filter((axle) => !!axle?.axleNumber); - } - - this.formChange.emit(event); - - if (event.techRecord_brakes_brakeCodeOriginal) { - this.store.dispatch(updateBrakeForces({})); - } - }); - - this.optionsService.loadOptions(ReferenceDataResourceType.Brakes); - } - - ngOnChanges(changes: SimpleChanges): void { - const { vehicleTechRecord } = changes; - - if (this.form && vehicleTechRecord?.currentValue && vehicleTechRecord.currentValue !== vehicleTechRecord.previousValue) { - this.form.patchValue(vehicleTechRecord.currentValue, { emitEvent: false }); - } - } - - ngOnDestroy(): void { - this.destroy$.next(); - this.destroy$.complete(); - } - - get brakeCode(): string { - return `${this.brakeCodePrefix}${this.form.get('techRecord_brakes_brakeCodeOriginal')?.value}`; - } - - get brakesForm(): FormGroup { - return this.form.get('brakes') as FormGroup; - } - - get booleanOptions(): MultiOptions { - return [ - { value: true, label: 'Yes' }, - { value: false, label: 'No' }, - ]; - } - - get retarderOptions(): MultiOptions { - return getOptionsFromEnum(Retarders); - } - - get brakeCodeOptions$(): Observable { - return this.optionsService.getOptions(ReferenceDataResourceType.Brakes) as Observable; - } - - get editTypes(): typeof FormNodeEditTypes { - return FormNodeEditTypes; - } - - get widths(): typeof FormNodeWidth { - return FormNodeWidth; - } - - get brakeCodePrefix(): string { - if (!this.vehicleTechRecord?.techRecord_grossLadenWeight) { - return ''; - } - const prefix = `${Math.round(this.vehicleTechRecord.techRecord_grossLadenWeight / 100)}`; - - return prefix.length <= 2 ? `0${prefix}` : prefix; - } - - get axles(): FormArray { - return this.form.get(['techRecord_axles']) as FormArray; - } - - getAxleForm(i: number): FormGroup { - return this.form.get(['techRecord_axles', i]) as FormGroup; - } - - round(n: number): number { - return Math.round(n); - } + @Input() vehicleTechRecord?: TechRecordType<'psv'>; + @Input() isEditing = false; + + @Output() formChange = new EventEmitter(); + + form!: CustomFormGroup; + template!: FormNode; + + selectedBrake?: Brake; + + private destroy$ = new Subject(); + + constructor( + private dfs: DynamicFormService, + private optionsService: MultiOptionsService, + private referenceDataStore: Store, + private store: Store + ) {} + + ngOnInit(): void { + this.form = this.dfs.createForm(PsvBrakesTemplate, this.vehicleTechRecord) as CustomFormGroup; + + (this.form.cleanValueChanges as Observable>>) + .pipe( + switchMap((event) => { + return event?.techRecord_brakes_brakeCodeOriginal + ? this.referenceDataStore.select(selectBrakeByCode(event.techRecord_brakes_brakeCodeOriginal)) + : of(undefined); + }), + withLatestFrom(this.form.cleanValueChanges as Observable>>), + debounceTime(400), + takeUntil(this.destroy$) + ) + .subscribe(([selectedBrake, event]) => { + // Set the brake details automatically based selection + if (selectedBrake && event?.techRecord_brakes_brakeCodeOriginal) { + event.techRecord_brakeCode = `${this.brakeCodePrefix}${selectedBrake.resourceKey}`; + event.techRecord_brakes_brakeCode = `${this.brakeCodePrefix}${selectedBrake.resourceKey}`; + event.techRecord_brakes_dataTrBrakeOne = selectedBrake.service; + event.techRecord_brakes_dataTrBrakeTwo = selectedBrake.secondary; + event.techRecord_brakes_dataTrBrakeThree = selectedBrake.parking; + } + + if (event?.techRecord_axles) { + event.techRecord_axles = event.techRecord_axles.filter((axle) => !!axle?.axleNumber); + } + + this.formChange.emit(event); + + if (event.techRecord_brakes_brakeCodeOriginal) { + this.store.dispatch(updateBrakeForces({})); + } + }); + + this.optionsService.loadOptions(ReferenceDataResourceType.Brakes); + } + + ngOnChanges(changes: SimpleChanges): void { + const { vehicleTechRecord } = changes; + + if ( + this.form && + vehicleTechRecord?.currentValue && + vehicleTechRecord.currentValue !== vehicleTechRecord.previousValue + ) { + this.form.patchValue(vehicleTechRecord.currentValue, { emitEvent: false }); + } + } + + ngOnDestroy(): void { + this.destroy$.next(); + this.destroy$.complete(); + } + + get brakeCode(): string { + return `${this.brakeCodePrefix}${this.form.get('techRecord_brakes_brakeCodeOriginal')?.value}`; + } + + get brakesForm(): FormGroup { + return this.form.get('brakes') as FormGroup; + } + + get booleanOptions(): MultiOptions { + return [ + { value: true, label: 'Yes' }, + { value: false, label: 'No' }, + ]; + } + + get retarderOptions(): MultiOptions { + return getOptionsFromEnum(Retarders); + } + + get brakeCodeOptions$(): Observable { + return this.optionsService.getOptions(ReferenceDataResourceType.Brakes) as Observable; + } + + get editTypes(): typeof FormNodeEditTypes { + return FormNodeEditTypes; + } + + get widths(): typeof FormNodeWidth { + return FormNodeWidth; + } + + get brakeCodePrefix(): string { + if (!this.vehicleTechRecord?.techRecord_grossLadenWeight) { + return ''; + } + const prefix = `${Math.round(this.vehicleTechRecord.techRecord_grossLadenWeight / 100)}`; + + return prefix.length <= 2 ? `0${prefix}` : prefix; + } + + get axles(): FormArray { + return this.form.get(['techRecord_axles']) as FormArray; + } + + getAxleForm(i: number): FormGroup { + return this.form.get(['techRecord_axles', i]) as FormGroup; + } + + round(n: number): number { + return Math.round(n); + } } diff --git a/src/app/forms/custom-sections/required-standard/required-standard.component.spec.ts b/src/app/forms/custom-sections/required-standard/required-standard.component.spec.ts index ef3afe706a..d8b0e9ed9b 100644 --- a/src/app/forms/custom-sections/required-standard/required-standard.component.spec.ts +++ b/src/app/forms/custom-sections/required-standard/required-standard.component.spec.ts @@ -16,173 +16,173 @@ import { testResultInEdit } from '@store/test-records'; import { RequiredStandardComponent } from './required-standard.component'; describe('RequiredStandardComponent', () => { - let component: RequiredStandardComponent; - let fixture: ComponentFixture; - let router: Router; - let store: MockStore; - let resultService: ResultOfTestService; - let dfs: DynamicFormService; - - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [FormsModule, ReactiveFormsModule, RouterTestingModule, HttpClientTestingModule], - declarations: [RequiredStandardComponent], - providers: [DynamicFormService, provideMockStore({ initialState: initialAppState })], - }).compileComponents(); - }); - - beforeEach(() => { - fixture = TestBed.createComponent(RequiredStandardComponent); - router = TestBed.inject(Router); - store = TestBed.inject(MockStore); - resultService = TestBed.inject(ResultOfTestService); - dfs = TestBed.inject(DynamicFormService); - component = fixture.componentInstance; - jest.clearAllMocks(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); - - describe('ngOnInit', () => { - it('should init and navigate back if no test result', () => { - component.isEditing = true; - store.overrideSelector(testResultInEdit, undefined); - const spy = jest.spyOn(component, 'navigateBack'); - - component.ngOnInit(); - - expect(spy).toHaveBeenCalledTimes(1); - }); - - it('should init and use index to amend the required standard', () => { - component.isEditing = true; - store.overrideSelector( - testResultInEdit, - mockTestResult(), - ); - store.overrideSelector(selectRouteParams, { requiredStandardIndex: '0' }); - component.ngOnInit(); - - expect(component.amendingRs).toBeTruthy(); - expect(component.requiredStandard).toBeDefined(); - }); - - it('should init and get required standard from the store', () => { - component.isEditing = true; - store.overrideSelector( - testResultInEdit, - mockTestResult(), - ); - store.overrideSelector(selectRouteParams, { ref: '1.1', inspectionType: 'basic', requiredStandardIndex: undefined }); - - store.overrideSelector( - getRequiredStandardFromTypeAndRef(INSPECTION_TYPE.BASIC, '1.1'), - { sectionNumber: '1' } as unknown as TestResultRequiredStandard, - ); - - component.ngOnInit(); - - expect(component.amendingRs).toBeFalsy(); - expect(component.requiredStandard).toBeDefined(); - }); - - it('should init and fail to required standard from the store so navigate back', () => { - component.isEditing = true; - store.overrideSelector( - testResultInEdit, - mockTestResult(), - ); - store.overrideSelector(selectRouteParams, { ref: '1.1', inspectionType: 'basic', requiredStandardIndex: undefined }); - - store.overrideSelector( - getRequiredStandardFromTypeAndRef(INSPECTION_TYPE.BASIC, '1.1'), - undefined, - ); - const spy = jest.spyOn(component, 'navigateBack'); - - component.ngOnInit(); - - expect(spy).toHaveBeenCalledTimes(1); - }); - }); - - describe('navigateBack', () => { - it('should navigate back two levels if in amend mode', () => { - jest.spyOn(resultService, 'updateResultOfTestRequiredStandards').getMockImplementation(); - component.amendingRs = true; - const spy = jest.spyOn(router, 'navigate'); - - component.navigateBack(); - - expect(spy).toHaveBeenCalledWith(['../../'], expect.anything()); - - }); - - it('should navigate back three levels if not in amend mode', () => { - jest.spyOn(resultService, 'updateResultOfTestRequiredStandards').getMockImplementation(); - component.amendingRs = false; - const spy = jest.spyOn(router, 'navigate'); - - component.navigateBack(); - - expect(spy).toHaveBeenCalledWith(['../../../'], expect.anything()); - }); - }); - - describe('toggleRsPrsField', () => { - it('should error if there is no required standard', () => { - component.requiredStandard = undefined; - - const res = component.toggleRsPrsField(); - - expect(res).toBeUndefined(); - }); - it('should flip bool value of prs', () => { - component.requiredStandard = { prs: true } as unknown as TestResultRequiredStandard; - - component.toggleRsPrsField(); - - expect(component.requiredStandard).toStrictEqual({ prs: false }); - }); - }); - - describe('handleSubmit', () => { - beforeEach(() => { - component.form = dfs.createForm({ - name: 'test section', - type: FormNodeTypes.SECTION, - children: [{ name: 'prs', type: FormNodeTypes.CONTROL }], - }, { prs: false }) as CustomFormGroup; - }); - it('should return if the form is invalid', () => { - jest.spyOn(DynamicFormService, 'validate').mockImplementation(); - component.form.controls['prs'].setErrors({ incorrect: true }); - - const res = component.handleSubmit(); - - expect(res).toBeUndefined(); - }); - - it('should call update RS if in amend mode', () => { - jest.spyOn(DynamicFormService, 'validate').mockImplementation(); - component.index = 1; - const dispatchSpy = jest.spyOn(store, 'dispatch'); - - component.handleSubmit(); - - expect(dispatchSpy).toHaveBeenCalledWith(expect.objectContaining({ type: '[test-results] update required standard' })); - }); - - it('should call create RS if not in amend mode', () => { - jest.spyOn(DynamicFormService, 'validate').mockImplementation(); - const dispatchSpy = jest.spyOn(store, 'dispatch'); - - component.handleSubmit(); - - expect(dispatchSpy).toHaveBeenCalledWith(expect.objectContaining({ type: '[test-results] create required standard' })); - }); - }); - + let component: RequiredStandardComponent; + let fixture: ComponentFixture; + let router: Router; + let store: MockStore; + let resultService: ResultOfTestService; + let dfs: DynamicFormService; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [FormsModule, ReactiveFormsModule, RouterTestingModule, HttpClientTestingModule], + declarations: [RequiredStandardComponent], + providers: [DynamicFormService, provideMockStore({ initialState: initialAppState })], + }).compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(RequiredStandardComponent); + router = TestBed.inject(Router); + store = TestBed.inject(MockStore); + resultService = TestBed.inject(ResultOfTestService); + dfs = TestBed.inject(DynamicFormService); + component = fixture.componentInstance; + jest.clearAllMocks(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + describe('ngOnInit', () => { + it('should init and navigate back if no test result', () => { + component.isEditing = true; + store.overrideSelector(testResultInEdit, undefined); + const spy = jest.spyOn(component, 'navigateBack'); + + component.ngOnInit(); + + expect(spy).toHaveBeenCalledTimes(1); + }); + + it('should init and use index to amend the required standard', () => { + component.isEditing = true; + store.overrideSelector(testResultInEdit, mockTestResult()); + store.overrideSelector(selectRouteParams, { requiredStandardIndex: '0' }); + component.ngOnInit(); + + expect(component.amendingRs).toBeTruthy(); + expect(component.requiredStandard).toBeDefined(); + }); + + it('should init and get required standard from the store', () => { + component.isEditing = true; + store.overrideSelector(testResultInEdit, mockTestResult()); + store.overrideSelector(selectRouteParams, { + ref: '1.1', + inspectionType: 'basic', + requiredStandardIndex: undefined, + }); + + store.overrideSelector(getRequiredStandardFromTypeAndRef(INSPECTION_TYPE.BASIC, '1.1'), { + sectionNumber: '1', + } as unknown as TestResultRequiredStandard); + + component.ngOnInit(); + + expect(component.amendingRs).toBeFalsy(); + expect(component.requiredStandard).toBeDefined(); + }); + + it('should init and fail to required standard from the store so navigate back', () => { + component.isEditing = true; + store.overrideSelector(testResultInEdit, mockTestResult()); + store.overrideSelector(selectRouteParams, { + ref: '1.1', + inspectionType: 'basic', + requiredStandardIndex: undefined, + }); + + store.overrideSelector(getRequiredStandardFromTypeAndRef(INSPECTION_TYPE.BASIC, '1.1'), undefined); + const spy = jest.spyOn(component, 'navigateBack'); + + component.ngOnInit(); + + expect(spy).toHaveBeenCalledTimes(1); + }); + }); + + describe('navigateBack', () => { + it('should navigate back two levels if in amend mode', () => { + jest.spyOn(resultService, 'updateResultOfTestRequiredStandards').getMockImplementation(); + component.amendingRs = true; + const spy = jest.spyOn(router, 'navigate'); + + component.navigateBack(); + + expect(spy).toHaveBeenCalledWith(['../../'], expect.anything()); + }); + + it('should navigate back three levels if not in amend mode', () => { + jest.spyOn(resultService, 'updateResultOfTestRequiredStandards').getMockImplementation(); + component.amendingRs = false; + const spy = jest.spyOn(router, 'navigate'); + + component.navigateBack(); + + expect(spy).toHaveBeenCalledWith(['../../../'], expect.anything()); + }); + }); + + describe('toggleRsPrsField', () => { + it('should error if there is no required standard', () => { + component.requiredStandard = undefined; + + const res = component.toggleRsPrsField(); + + expect(res).toBeUndefined(); + }); + it('should flip bool value of prs', () => { + component.requiredStandard = { prs: true } as unknown as TestResultRequiredStandard; + + component.toggleRsPrsField(); + + expect(component.requiredStandard).toStrictEqual({ prs: false }); + }); + }); + + describe('handleSubmit', () => { + beforeEach(() => { + component.form = dfs.createForm( + { + name: 'test section', + type: FormNodeTypes.SECTION, + children: [{ name: 'prs', type: FormNodeTypes.CONTROL }], + }, + { prs: false } + ) as CustomFormGroup; + }); + it('should return if the form is invalid', () => { + jest.spyOn(DynamicFormService, 'validate').mockImplementation(); + component.form.controls['prs'].setErrors({ incorrect: true }); + + const res = component.handleSubmit(); + + expect(res).toBeUndefined(); + }); + + it('should call update RS if in amend mode', () => { + jest.spyOn(DynamicFormService, 'validate').mockImplementation(); + component.index = 1; + const dispatchSpy = jest.spyOn(store, 'dispatch'); + + component.handleSubmit(); + + expect(dispatchSpy).toHaveBeenCalledWith( + expect.objectContaining({ type: '[test-results] update required standard' }) + ); + }); + + it('should call create RS if not in amend mode', () => { + jest.spyOn(DynamicFormService, 'validate').mockImplementation(); + const dispatchSpy = jest.spyOn(store, 'dispatch'); + + component.handleSubmit(); + + expect(dispatchSpy).toHaveBeenCalledWith( + expect.objectContaining({ type: '[test-results] create required standard' }) + ); + }); + }); }); diff --git a/src/app/forms/custom-sections/required-standard/required-standard.component.ts b/src/app/forms/custom-sections/required-standard/required-standard.component.ts index 51a35c9e36..4d8f112977 100644 --- a/src/app/forms/custom-sections/required-standard/required-standard.component.ts +++ b/src/app/forms/custom-sections/required-standard/required-standard.component.ts @@ -1,6 +1,4 @@ -import { - ChangeDetectionStrategy, Component, OnDestroy, OnInit, -} from '@angular/core'; +import { ChangeDetectionStrategy, Component, OnDestroy, OnInit } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; import { GlobalError } from '@core/components/global-error/global-error.interface'; import { GlobalErrorService } from '@core/components/global-error/global-error.service'; @@ -14,124 +12,145 @@ import { DefaultNullOrEmpty } from '@shared/pipes/default-null-or-empty/default- import { getRequiredStandardFromTypeAndRef } from '@store/required-standards/selectors/required-standards.selector'; import { selectRouteParam } from '@store/router/selectors/router.selectors'; import { - createRequiredStandard, removeRequiredStandard, testResultInEdit, toEditOrNotToEdit, updateRequiredStandard, + createRequiredStandard, + removeRequiredStandard, + testResultInEdit, + toEditOrNotToEdit, + updateRequiredStandard, } from '@store/test-records'; -import { - Subject, distinctUntilChanged, - takeUntil, withLatestFrom, -} from 'rxjs'; +import { Subject, distinctUntilChanged, takeUntil, withLatestFrom } from 'rxjs'; @Component({ - selector: 'app-required-standard', - templateUrl: './required-standard.component.html', - providers: [DefaultNullOrEmpty], - changeDetection: ChangeDetectionStrategy.OnPush, + selector: 'app-required-standard', + templateUrl: './required-standard.component.html', + providers: [DefaultNullOrEmpty], + changeDetection: ChangeDetectionStrategy.OnPush, }) export class RequiredStandardComponent implements OnInit, OnDestroy { - form!: CustomFormGroup; - index!: number; - requiredStandard?: TestResultRequiredStandard; - onDestroy$ = new Subject(); - isEditing: boolean; - amendingRs?: boolean; - - private requiredStandardForm?: CustomFormArray; + form!: CustomFormGroup; + index!: number; + requiredStandard?: TestResultRequiredStandard; + onDestroy$ = new Subject(); + isEditing: boolean; + amendingRs?: boolean; - constructor( - private store: Store, - private activatedRoute: ActivatedRoute, - private resultService: ResultOfTestService, - private router: Router, - private dfs: DynamicFormService, - private errorService: GlobalErrorService, - ) { - this.isEditing = this.activatedRoute.snapshot.data['isEditing']; - } + private requiredStandardForm?: CustomFormArray; - ngOnInit(): void { - const inspectionType = this.store.pipe(select(selectRouteParam('inspectionType'))); - const rsRefCalculation = this.store.pipe(select(selectRouteParam('ref'))); - const requiredStandardIndex = this.store.pipe(select(selectRouteParam('requiredStandardIndex'))); + constructor( + private store: Store, + private activatedRoute: ActivatedRoute, + private resultService: ResultOfTestService, + private router: Router, + private dfs: DynamicFormService, + private errorService: GlobalErrorService + ) { + this.isEditing = this.activatedRoute.snapshot.data['isEditing']; + } - this.store.pipe(select(this.isEditing ? testResultInEdit : toEditOrNotToEdit)).pipe( - withLatestFrom(inspectionType, rsRefCalculation, requiredStandardIndex), - takeUntil(this.onDestroy$), - distinctUntilChanged((prev, curr) => prev[0]?.testTypes[0]?.testResult === curr[0]?.testTypes[0]?.testResult), - ).subscribe(([testResult, inspectionTypeValue, rsRefCalculationValue, requiredStandardIndexValue]) => { - if (!testResult) this.navigateBack(); - this.requiredStandardForm = (this.dfs.createForm(RequiredStandardsTpl, testResult) as CustomFormGroup) - .get(['testTypes', '0', 'requiredStandards']) as CustomFormArray; - if (requiredStandardIndexValue) { - this.amendingRs = true; - this.index = Number(requiredStandardIndexValue); - this.form = this.requiredStandardForm.controls[this.index] as CustomFormGroup; - this.requiredStandard = testResult?.testTypes[0]?.requiredStandards?.at(this.index); - } else { - this.amendingRs = false; - this.store.pipe( - select(getRequiredStandardFromTypeAndRef(inspectionTypeValue as INSPECTION_TYPE, rsRefCalculationValue ?? '')), - takeUntil(this.onDestroy$), - ) - .subscribe((requiredStandard) => { - if (!requiredStandard) this.navigateBack(); - const rsControl = { - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - ...requiredStandard!, - prs: false, - additionalNotes: '', - }; + ngOnInit(): void { + const inspectionType = this.store.pipe(select(selectRouteParam('inspectionType'))); + const rsRefCalculation = this.store.pipe(select(selectRouteParam('ref'))); + const requiredStandardIndex = this.store.pipe(select(selectRouteParam('requiredStandardIndex'))); - this.requiredStandard = rsControl; - this.requiredStandardForm?.addControl(rsControl); - this.form = this.requiredStandardForm?.controls[this.index ?? this.requiredStandardForm.length - 1] as CustomFormGroup; + this.store + .pipe(select(this.isEditing ? testResultInEdit : toEditOrNotToEdit)) + .pipe( + withLatestFrom(inspectionType, rsRefCalculation, requiredStandardIndex), + takeUntil(this.onDestroy$), + distinctUntilChanged((prev, curr) => prev[0]?.testTypes[0]?.testResult === curr[0]?.testTypes[0]?.testResult) + ) + .subscribe(([testResult, inspectionTypeValue, rsRefCalculationValue, requiredStandardIndexValue]) => { + if (!testResult) this.navigateBack(); + this.requiredStandardForm = (this.dfs.createForm(RequiredStandardsTpl, testResult) as CustomFormGroup).get([ + 'testTypes', + '0', + 'requiredStandards', + ]) as CustomFormArray; + if (requiredStandardIndexValue) { + this.amendingRs = true; + this.index = Number(requiredStandardIndexValue); + this.form = this.requiredStandardForm.controls[this.index] as CustomFormGroup; + this.requiredStandard = testResult?.testTypes[0]?.requiredStandards?.at(this.index); + } else { + this.amendingRs = false; + this.store + .pipe( + select( + getRequiredStandardFromTypeAndRef(inspectionTypeValue as INSPECTION_TYPE, rsRefCalculationValue ?? '') + ), + takeUntil(this.onDestroy$) + ) + .subscribe((requiredStandard) => { + if (!requiredStandard) this.navigateBack(); + const rsControl = { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + ...requiredStandard!, + prs: false, + additionalNotes: '', + }; - }); - } - }); - } + this.requiredStandard = rsControl; + this.requiredStandardForm?.addControl(rsControl); + this.form = this.requiredStandardForm?.controls[ + this.index ?? this.requiredStandardForm.length - 1 + ] as CustomFormGroup; + }); + } + }); + } - ngOnDestroy(): void { - this.onDestroy$.next(true); - this.onDestroy$.complete(); - } + ngOnDestroy(): void { + this.onDestroy$.next(true); + this.onDestroy$.complete(); + } - navigateBack() { - this.resultService.updateResultOfTestRequiredStandards(); - void this.router.navigate(this.amendingRs ? ['../../'] : ['../../../'], { relativeTo: this.activatedRoute, queryParamsHandling: 'preserve' }); - } + navigateBack() { + this.resultService.updateResultOfTestRequiredStandards(); + void this.router.navigate(this.amendingRs ? ['../../'] : ['../../../'], { + relativeTo: this.activatedRoute, + queryParamsHandling: 'preserve', + }); + } - toggleRsPrsField() { - if (!this.requiredStandard) { - return; - } - this.requiredStandard.prs = !this.requiredStandard.prs; - this.requiredStandardForm?.controls[this.index ?? this.requiredStandardForm.length - 1].get('prs')?.patchValue(this.requiredStandard.prs); - } + toggleRsPrsField() { + if (!this.requiredStandard) { + return; + } + this.requiredStandard.prs = !this.requiredStandard.prs; + this.requiredStandardForm?.controls[this.index ?? this.requiredStandardForm.length - 1] + .get('prs') + ?.patchValue(this.requiredStandard.prs); + } - handleSubmit() { - const errors: GlobalError[] = []; - DynamicFormService.validate(this.form, errors); + handleSubmit() { + const errors: GlobalError[] = []; + DynamicFormService.validate(this.form, errors); - if (errors.length > 0) { - this.errorService.setErrors(errors); - } + if (errors.length > 0) { + this.errorService.setErrors(errors); + } - if (this.form.invalid) { - return; - } + if (this.form.invalid) { + return; + } - if (this.index || this.index === 0) { - this.store.dispatch( - updateRequiredStandard({ requiredStandard: this.form.getCleanValue(this.form) as TestResultRequiredStandard, index: this.index }), - ); - } else { - this.store.dispatch(createRequiredStandard({ requiredStandard: this.form.getCleanValue(this.form) as TestResultRequiredStandard })); - } - this.navigateBack(); - } + if (this.index || this.index === 0) { + this.store.dispatch( + updateRequiredStandard({ + requiredStandard: this.form.getCleanValue(this.form) as TestResultRequiredStandard, + index: this.index, + }) + ); + } else { + this.store.dispatch( + createRequiredStandard({ requiredStandard: this.form.getCleanValue(this.form) as TestResultRequiredStandard }) + ); + } + this.navigateBack(); + } - handleRemove() { - this.store.dispatch(removeRequiredStandard({ index: this.index })); - this.navigateBack(); - } + handleRemove() { + this.store.dispatch(removeRequiredStandard({ index: this.index })); + this.navigateBack(); + } } diff --git a/src/app/forms/custom-sections/required-standards/required-standards.component.spec.ts b/src/app/forms/custom-sections/required-standards/required-standards.component.spec.ts index 4565712825..74a5ec5554 100644 --- a/src/app/forms/custom-sections/required-standards/required-standards.component.spec.ts +++ b/src/app/forms/custom-sections/required-standards/required-standards.component.spec.ts @@ -1,8 +1,6 @@ import { HttpClientTestingModule } from '@angular/common/http/testing'; import { DebugElement } from '@angular/core'; -import { - ComponentFixture, TestBed, fakeAsync, tick, -} from '@angular/core/testing'; +import { ComponentFixture, TestBed, fakeAsync, tick } from '@angular/core/testing'; import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { By } from '@angular/platform-browser'; import { Router } from '@angular/router'; @@ -17,80 +15,78 @@ import { initialAppState } from '@store/index'; import { RequiredStandardsComponent } from './required-standards.component'; describe('RequiredStandardsComponent', () => { - let component: RequiredStandardsComponent; - let fixture: ComponentFixture; - let el: DebugElement; - let router: Router; - - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [FormsModule, ReactiveFormsModule, RouterTestingModule, HttpClientTestingModule], - declarations: [RequiredStandardsComponent, ButtonComponent, TruncatePipe, TagComponent], - providers: [DynamicFormService, provideMockStore({ initialState: initialAppState })], - }).compileComponents(); - }); - - beforeEach(() => { - fixture = TestBed.createComponent(RequiredStandardsComponent); - router = TestBed.inject(Router); - component = fixture.componentInstance; - el = fixture.debugElement; - jest.clearAllMocks(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); - - it('should render correct header', () => { - fixture.detectChanges(); - expect(el.query(By.css('h2')).nativeElement.innerHTML).toBe('Required Standards'); - }); - - describe('No required standards', () => { - it('should be displayed when required standards is undefined or empty array', fakeAsync(() => { - const expectedText = 'No required standards'; - - tick(); - fixture.detectChanges(); - - let text: HTMLParagraphElement = el.query(By.css('p')).nativeElement; - expect(text.innerHTML).toBe(expectedText); - - tick(); - fixture.detectChanges(); - - text = el.query(By.css('p')).nativeElement; - expect(text.innerHTML).toBe(expectedText); - })); - }); - - describe('onAddRequiredStandards', () => { - it('should let me add a RS and call the navigator', () => { - - const spy = jest.spyOn(router, 'navigate'); - - component.testData = { - euVehicleCategory: EUVehicleCategory.M1, - }; - - component.onAddRequiredStandard(); - - expect(spy).toHaveBeenCalledTimes(1); - - }); - it('should not add a RS and emit a value to the parent', () => { - const spy = jest.spyOn(router, 'navigate'); - const emitSpy = jest.spyOn(component.validateEuVehicleCategory, 'emit'); - - component.testData = { - testerName: 'bar', - }; - - component.onAddRequiredStandard(); - - expect(spy).toHaveBeenCalledTimes(0); - expect(emitSpy).toHaveBeenCalledTimes(1); - }); - }); + let component: RequiredStandardsComponent; + let fixture: ComponentFixture; + let el: DebugElement; + let router: Router; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [FormsModule, ReactiveFormsModule, RouterTestingModule, HttpClientTestingModule], + declarations: [RequiredStandardsComponent, ButtonComponent, TruncatePipe, TagComponent], + providers: [DynamicFormService, provideMockStore({ initialState: initialAppState })], + }).compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(RequiredStandardsComponent); + router = TestBed.inject(Router); + component = fixture.componentInstance; + el = fixture.debugElement; + jest.clearAllMocks(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + it('should render correct header', () => { + fixture.detectChanges(); + expect(el.query(By.css('h2')).nativeElement.innerHTML).toBe('Required Standards'); + }); + + describe('No required standards', () => { + it('should be displayed when required standards is undefined or empty array', fakeAsync(() => { + const expectedText = 'No required standards'; + + tick(); + fixture.detectChanges(); + + let text: HTMLParagraphElement = el.query(By.css('p')).nativeElement; + expect(text.innerHTML).toBe(expectedText); + + tick(); + fixture.detectChanges(); + + text = el.query(By.css('p')).nativeElement; + expect(text.innerHTML).toBe(expectedText); + })); + }); + + describe('onAddRequiredStandards', () => { + it('should let me add a RS and call the navigator', () => { + const spy = jest.spyOn(router, 'navigate'); + + component.testData = { + euVehicleCategory: EUVehicleCategory.M1, + }; + + component.onAddRequiredStandard(); + + expect(spy).toHaveBeenCalledTimes(1); + }); + it('should not add a RS and emit a value to the parent', () => { + const spy = jest.spyOn(router, 'navigate'); + const emitSpy = jest.spyOn(component.validateEuVehicleCategory, 'emit'); + + component.testData = { + testerName: 'bar', + }; + + component.onAddRequiredStandard(); + + expect(spy).toHaveBeenCalledTimes(0); + expect(emitSpy).toHaveBeenCalledTimes(1); + }); + }); }); diff --git a/src/app/forms/custom-sections/required-standards/required-standards.component.ts b/src/app/forms/custom-sections/required-standards/required-standards.component.ts index 812796ee4f..e2c34e3a12 100644 --- a/src/app/forms/custom-sections/required-standards/required-standards.component.ts +++ b/src/app/forms/custom-sections/required-standards/required-standards.component.ts @@ -1,7 +1,5 @@ import { ViewportScroller } from '@angular/common'; -import { - Component, EventEmitter, Input, OnChanges, OnDestroy, OnInit, Output, SimpleChanges, -} from '@angular/core'; +import { Component, EventEmitter, Input, OnChanges, OnDestroy, OnInit, Output, SimpleChanges } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; import { GlobalErrorService } from '@core/components/global-error/global-error.service'; import { DynamicFormService } from '@forms/services/dynamic-form.service'; @@ -12,91 +10,91 @@ import { Store, select } from '@ngrx/store'; import { ResultOfTestService } from '@services/result-of-test/result-of-test.service'; import { testResultInEdit } from '@store/test-records'; import { isEqual } from 'lodash'; -import { - Subject, Subscription, debounceTime, distinctUntilChanged, - takeUntil, -} from 'rxjs'; +import { Subject, Subscription, debounceTime, distinctUntilChanged, takeUntil } from 'rxjs'; @Component({ - selector: 'app-required-standards[template]', - templateUrl: './required-standards.component.html', + selector: 'app-required-standards[template]', + templateUrl: './required-standards.component.html', }) export class RequiredStandardsComponent implements OnInit, OnDestroy, OnChanges { - @Input() isEditing = false; - @Input() template!: FormNode; - @Input() testData: Partial = {}; + @Input() isEditing = false; + @Input() template!: FormNode; + @Input() testData: Partial = {}; - @Output() formChange = new EventEmitter(); - @Output() validateEuVehicleCategory = new EventEmitter(); + @Output() formChange = new EventEmitter(); + @Output() validateEuVehicleCategory = new EventEmitter(); - public form!: CustomFormGroup; - private formSubscription = new Subscription(); - private requiredStandardsFormArray?: CustomFormArray; + public form!: CustomFormGroup; + private formSubscription = new Subscription(); + private requiredStandardsFormArray?: CustomFormArray; - onDestroy$ = new Subject(); + onDestroy$ = new Subject(); - constructor( - private dfs: DynamicFormService, - private router: Router, - private route: ActivatedRoute, - private viewportScroller: ViewportScroller, - private globalErrorService: GlobalErrorService, - private resultService: ResultOfTestService, - private store: Store, - ) {} + constructor( + private dfs: DynamicFormService, + private router: Router, + private route: ActivatedRoute, + private viewportScroller: ViewportScroller, + private globalErrorService: GlobalErrorService, + private resultService: ResultOfTestService, + private store: Store + ) {} - ngOnInit(): void { - this.form = this.dfs.createForm(this.template, this.testData) as CustomFormGroup; - this.formSubscription = this.form.cleanValueChanges.pipe(debounceTime(400)).subscribe((event) => { - this.formChange.emit(event); - }); - this.store.pipe(select(testResultInEdit)).pipe( - takeUntil(this.onDestroy$), - distinctUntilChanged((prev, curr) => isEqual(prev?.testTypes?.at(0)?.requiredStandards, curr?.testTypes?.at(0)?.requiredStandards)), - ) - .subscribe(() => { - this.resultService.updateResultOfTestRequiredStandards(); - }); - } + ngOnInit(): void { + this.form = this.dfs.createForm(this.template, this.testData) as CustomFormGroup; + this.formSubscription = this.form.cleanValueChanges.pipe(debounceTime(400)).subscribe((event) => { + this.formChange.emit(event); + }); + this.store + .pipe(select(testResultInEdit)) + .pipe( + takeUntil(this.onDestroy$), + distinctUntilChanged((prev, curr) => + isEqual(prev?.testTypes?.at(0)?.requiredStandards, curr?.testTypes?.at(0)?.requiredStandards) + ) + ) + .subscribe(() => { + this.resultService.updateResultOfTestRequiredStandards(); + }); + } - ngOnDestroy(): void { - this.formSubscription.unsubscribe(); - } + ngOnDestroy(): void { + this.formSubscription.unsubscribe(); + } - ngOnChanges(changes: SimpleChanges): void { - const { testData } = changes; + ngOnChanges(changes: SimpleChanges): void { + const { testData } = changes; - if (testData?.currentValue?.euVehicleCategory !== testData?.previousValue?.euVehicleCategory) { - this.form?.get(['testTypes', '0', 'requiredStandards'])?.patchValue([], { emitEvent: true }); - } + if (testData?.currentValue?.euVehicleCategory !== testData?.previousValue?.euVehicleCategory) { + this.form?.get(['testTypes', '0', 'requiredStandards'])?.patchValue([], { emitEvent: true }); + } + } - } + onAddRequiredStandard(): void { + this.globalErrorService.clearErrors(); + if (!this.testData?.euVehicleCategory) { + this.validateEuVehicleCategory.emit(); + this.viewportScroller.scrollToPosition([0, 0]); + return; + } + void this.router.navigate(['selectRequiredStandard'], { queryParamsHandling: 'preserve', relativeTo: this.route }); + } - onAddRequiredStandard(): void { - this.globalErrorService.clearErrors(); - if (!this.testData?.euVehicleCategory) { - this.validateEuVehicleCategory.emit(); - this.viewportScroller.scrollToPosition([0, 0]); - return; - } - void this.router.navigate(['selectRequiredStandard'], { queryParamsHandling: 'preserve', relativeTo: this.route }); - } + get requiredStandardsForm(): CustomFormArray { + if (!this.requiredStandardsFormArray) { + this.requiredStandardsFormArray = this.form?.get(['testTypes', '0', 'requiredStandards']) as CustomFormArray; + } + return this.requiredStandardsFormArray; + } - get requiredStandardsForm(): CustomFormArray { - if (!this.requiredStandardsFormArray) { - this.requiredStandardsFormArray = this.form?.get(['testTypes', '0', 'requiredStandards']) as CustomFormArray; - } - return this.requiredStandardsFormArray; - } + get requiredStandardsCount(): number { + return this.requiredStandardsForm?.controls.length; + } - get requiredStandardsCount(): number { - return this.requiredStandardsForm?.controls.length; - } - - get testRequiredStandards(): TestResultRequiredStandard[] { - return this.requiredStandardsForm.controls.map((control) => { - const formGroup = control as CustomFormGroup; - return formGroup.getCleanValue(formGroup) as TestResultRequiredStandard; - }); - } + get testRequiredStandards(): TestResultRequiredStandard[] { + return this.requiredStandardsForm.controls.map((control) => { + const formGroup = control as CustomFormGroup; + return formGroup.getCleanValue(formGroup) as TestResultRequiredStandard; + }); + } } diff --git a/src/app/forms/custom-sections/trl-brakes/trl-brakes.component.spec.ts b/src/app/forms/custom-sections/trl-brakes/trl-brakes.component.spec.ts index c62b3e86dd..9c28fbe1ee 100644 --- a/src/app/forms/custom-sections/trl-brakes/trl-brakes.component.spec.ts +++ b/src/app/forms/custom-sections/trl-brakes/trl-brakes.component.spec.ts @@ -5,102 +5,107 @@ import { DynamicFormsModule } from '@forms/dynamic-forms.module'; import { provideMockStore } from '@ngrx/store/testing'; import { initialAppState } from '@store/index'; -import { TechRecordTRL, TechRecordType } from '@dvsa/cvs-type-definitions/types/v3/tech-record/tech-record-vehicle-type'; +import { + TechRecordTRL, + TechRecordType, +} from '@dvsa/cvs-type-definitions/types/v3/tech-record/tech-record-vehicle-type'; import { mockVehicleTechnicalRecord } from '@mocks/mock-vehicle-technical-record.mock'; import { TrlBrakesComponent } from './trl-brakes.component'; describe('BrakesComponent', () => { - let component: TrlBrakesComponent; - let fixture: ComponentFixture; + let component: TrlBrakesComponent; + let fixture: ComponentFixture; - beforeEach(async () => { - await TestBed.configureTestingModule({ - declarations: [TrlBrakesComponent], - imports: [DynamicFormsModule, HttpClientTestingModule, RouterTestingModule], - providers: [provideMockStore({ initialState: initialAppState })], - }).compileComponents(); - }); + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [TrlBrakesComponent], + imports: [DynamicFormsModule, HttpClientTestingModule, RouterTestingModule], + providers: [provideMockStore({ initialState: initialAppState })], + }).compileComponents(); + }); - beforeEach(() => { - fixture = TestBed.createComponent(TrlBrakesComponent); - component = fixture.componentInstance; - component.vehicleTechRecord = mockVehicleTechnicalRecord('trl') as TechRecordType<'trl'>; - component.vehicleTechRecord.techRecord_axles = [ - { - parkingBrakeMrk: true, - axleNumber: 1, - brakes_brakeActuator: 1, - brakes_leverLength: 1, - brakes_springBrakeParking: true, - weights_gbWeight: 1, - weights_designWeight: 2, - weights_ladenWeight: 3, - weights_kerbWeight: 4, - tyres_tyreCode: 1, - tyres_tyreSize: '2', - tyres_plyRating: '3', - tyres_fitmentCode: 'single', - tyres_dataTrAxles: 1, - tyres_speedCategorySymbol: 'a7', - }, - { - parkingBrakeMrk: true, - axleNumber: 2, - brakes_brakeActuator: 1, - brakes_leverLength: 1, - brakes_springBrakeParking: false, - weights_gbWeight: 1, - weights_designWeight: 2, - weights_ladenWeight: 3, - weights_kerbWeight: 4, - tyres_tyreCode: 1, - tyres_tyreSize: '2', - tyres_plyRating: '3', - tyres_fitmentCode: 'single', - tyres_dataTrAxles: 1, - tyres_speedCategorySymbol: 'a7', - }, - { - parkingBrakeMrk: false, - axleNumber: 3, - brakes_brakeActuator: 1, - brakes_leverLength: 1, - brakes_springBrakeParking: true, - weights_gbWeight: 1, - weights_designWeight: 2, - weights_ladenWeight: 3, - weights_kerbWeight: 4, - tyres_tyreCode: 1, - tyres_tyreSize: '2', - tyres_plyRating: '3', - tyres_fitmentCode: 'single', - tyres_dataTrAxles: 1, - tyres_speedCategorySymbol: 'a7', - }, - ]; - fixture.detectChanges(); - }); + beforeEach(() => { + fixture = TestBed.createComponent(TrlBrakesComponent); + component = fixture.componentInstance; + component.vehicleTechRecord = mockVehicleTechnicalRecord('trl') as TechRecordType<'trl'>; + component.vehicleTechRecord.techRecord_axles = [ + { + parkingBrakeMrk: true, + axleNumber: 1, + brakes_brakeActuator: 1, + brakes_leverLength: 1, + brakes_springBrakeParking: true, + weights_gbWeight: 1, + weights_designWeight: 2, + weights_ladenWeight: 3, + weights_kerbWeight: 4, + tyres_tyreCode: 1, + tyres_tyreSize: '2', + tyres_plyRating: '3', + tyres_fitmentCode: 'single', + tyres_dataTrAxles: 1, + tyres_speedCategorySymbol: 'a7', + }, + { + parkingBrakeMrk: true, + axleNumber: 2, + brakes_brakeActuator: 1, + brakes_leverLength: 1, + brakes_springBrakeParking: false, + weights_gbWeight: 1, + weights_designWeight: 2, + weights_ladenWeight: 3, + weights_kerbWeight: 4, + tyres_tyreCode: 1, + tyres_tyreSize: '2', + tyres_plyRating: '3', + tyres_fitmentCode: 'single', + tyres_dataTrAxles: 1, + tyres_speedCategorySymbol: 'a7', + }, + { + parkingBrakeMrk: false, + axleNumber: 3, + brakes_brakeActuator: 1, + brakes_leverLength: 1, + brakes_springBrakeParking: true, + weights_gbWeight: 1, + weights_designWeight: 2, + weights_ladenWeight: 3, + weights_kerbWeight: 4, + tyres_tyreCode: 1, + tyres_tyreSize: '2', + tyres_plyRating: '3', + tyres_fitmentCode: 'single', + tyres_dataTrAxles: 1, + tyres_speedCategorySymbol: 'a7', + }, + ]; + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); - describe('The brake code value on this.form', () => { - it('should match the corresponding values on vehicleTechRecord', () => { - expect(component.vehicleTechRecord.techRecord_brakes_loadSensingValve).toStrictEqual(component.form.value.techRecord_brakes_loadSensingValve); - }); - }); - describe('The dataTrBrakeOne value on this.form', () => { - it('should match the corresponding values on vehicleTechRecord', () => { - expect(component.vehicleTechRecord.techRecord_brakes_antilockBrakingSystem).toStrictEqual( - component.form.value.techRecord_brakes_antilockBrakingSystem, - ); - }); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); + describe('The brake code value on this.form', () => { + it('should match the corresponding values on vehicleTechRecord', () => { + expect(component.vehicleTechRecord.techRecord_brakes_loadSensingValve).toStrictEqual( + component.form.value.techRecord_brakes_loadSensingValve + ); + }); + }); + describe('The dataTrBrakeOne value on this.form', () => { + it('should match the corresponding values on vehicleTechRecord', () => { + expect(component.vehicleTechRecord.techRecord_brakes_antilockBrakingSystem).toStrictEqual( + component.form.value.techRecord_brakes_antilockBrakingSystem + ); + }); + }); - describe('The axle value on this.form', () => { - it('should match the corresponding values on vehicleTechRecord', () => { - const axles = component.vehicleTechRecord.techRecord_axles as NonNullable; - expect(axles[0]).toEqual(expect.objectContaining(component.form.controls['techRecord_axles']?.value[0])); - }); - }); + describe('The axle value on this.form', () => { + it('should match the corresponding values on vehicleTechRecord', () => { + const axles = component.vehicleTechRecord.techRecord_axles as NonNullable; + expect(axles[0]).toEqual(expect.objectContaining(component.form.controls['techRecord_axles']?.value[0])); + }); + }); }); diff --git a/src/app/forms/custom-sections/trl-brakes/trl-brakes.component.ts b/src/app/forms/custom-sections/trl-brakes/trl-brakes.component.ts index 0470b17d7a..4e7bdb3615 100644 --- a/src/app/forms/custom-sections/trl-brakes/trl-brakes.component.ts +++ b/src/app/forms/custom-sections/trl-brakes/trl-brakes.component.ts @@ -1,6 +1,4 @@ -import { - Component, EventEmitter, Input, OnChanges, OnDestroy, OnInit, Output, SimpleChanges, -} from '@angular/core'; +import { Component, EventEmitter, Input, OnChanges, OnDestroy, OnInit, Output, SimpleChanges } from '@angular/core'; import { FormArray, FormGroup } from '@angular/forms'; import { TechRecordType } from '@dvsa/cvs-type-definitions/types/v3/tech-record/tech-record-vehicle-type'; import { MultiOptions } from '@forms/models/options.model'; @@ -10,77 +8,81 @@ import { TrlBrakesTemplate } from '@forms/templates/trl/trl-brakes.template'; import { Subject, debounceTime, takeUntil } from 'rxjs'; @Component({ - selector: 'app-trl-brakes[vehicleTechRecord]', - templateUrl: './trl-brakes.component.html', - styleUrls: ['./trl-brakes.component.scss'], + selector: 'app-trl-brakes[vehicleTechRecord]', + templateUrl: './trl-brakes.component.html', + styleUrls: ['./trl-brakes.component.scss'], }) export class TrlBrakesComponent implements OnInit, OnChanges, OnDestroy { - @Input() vehicleTechRecord!: TechRecordType<'trl'>; - @Input() isEditing = false; - @Output() formChange = new EventEmitter(); - - form!: CustomFormGroup; - - booleanOptions: MultiOptions = [ - { value: true, label: 'Yes' }, - { value: false, label: 'No' }, - ]; - - private destroy$ = new Subject(); - - constructor(private dfs: DynamicFormService) {} - - ngOnInit(): void { - this.form = this.dfs.createForm(TrlBrakesTemplate, this.vehicleTechRecord) as CustomFormGroup; - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - this.form.cleanValueChanges.pipe(debounceTime(400), takeUntil(this.destroy$)).subscribe((event: any) => { - if (event?.techRecord_axles) { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - event.techRecord_axles = (event.techRecord_axles).filter((axle: any) => !!axle?.axleNumber); - } - - this.formChange.emit(event); - }); - } - - ngOnChanges(changes: SimpleChanges): void { - const { vehicleTechRecord } = changes; - - if (this.form && vehicleTechRecord?.currentValue && vehicleTechRecord.currentValue !== vehicleTechRecord.previousValue) { - this.form.patchValue(vehicleTechRecord.currentValue, { emitEvent: false }); - } - } - - ngOnDestroy(): void { - this.destroy$.next(); - this.destroy$.complete(); - } - - get types(): typeof FormNodeEditTypes { - return FormNodeEditTypes; - } - - get axles(): FormArray { - return this.form.get(['techRecord_axles']) as FormArray; - } - - getAxleForm(i: number): FormGroup { - return this.form.get(['techRecord_axles', i]) as FormGroup; - } - - getAxleBrakes(i: number): FormGroup { - return this.form.get(['techRecord_axles', i, 'brakes']) as FormGroup; - } - - stripName = (s: string): string => { - const splitString = s.split('_').pop() ?? ''; - return ( - splitString.charAt(0).toUpperCase() - + splitString - .slice(1) - .replace(/([A-Z])/g, ' $1') - .toLowerCase() - ); - }; + @Input() vehicleTechRecord!: TechRecordType<'trl'>; + @Input() isEditing = false; + @Output() formChange = new EventEmitter(); + + form!: CustomFormGroup; + + booleanOptions: MultiOptions = [ + { value: true, label: 'Yes' }, + { value: false, label: 'No' }, + ]; + + private destroy$ = new Subject(); + + constructor(private dfs: DynamicFormService) {} + + ngOnInit(): void { + this.form = this.dfs.createForm(TrlBrakesTemplate, this.vehicleTechRecord) as CustomFormGroup; + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + this.form.cleanValueChanges.pipe(debounceTime(400), takeUntil(this.destroy$)).subscribe((event: any) => { + if (event?.techRecord_axles) { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + event.techRecord_axles = event.techRecord_axles.filter((axle: any) => !!axle?.axleNumber); + } + + this.formChange.emit(event); + }); + } + + ngOnChanges(changes: SimpleChanges): void { + const { vehicleTechRecord } = changes; + + if ( + this.form && + vehicleTechRecord?.currentValue && + vehicleTechRecord.currentValue !== vehicleTechRecord.previousValue + ) { + this.form.patchValue(vehicleTechRecord.currentValue, { emitEvent: false }); + } + } + + ngOnDestroy(): void { + this.destroy$.next(); + this.destroy$.complete(); + } + + get types(): typeof FormNodeEditTypes { + return FormNodeEditTypes; + } + + get axles(): FormArray { + return this.form.get(['techRecord_axles']) as FormArray; + } + + getAxleForm(i: number): FormGroup { + return this.form.get(['techRecord_axles', i]) as FormGroup; + } + + getAxleBrakes(i: number): FormGroup { + return this.form.get(['techRecord_axles', i, 'brakes']) as FormGroup; + } + + stripName = (s: string): string => { + const splitString = s.split('_').pop() ?? ''; + return ( + splitString.charAt(0).toUpperCase() + + splitString + .slice(1) + .replace(/([A-Z])/g, ' $1') + .toLowerCase() + ); + }; } diff --git a/src/app/forms/custom-sections/tyres/tyres.component.spec.ts b/src/app/forms/custom-sections/tyres/tyres.component.spec.ts index 3af9a7b5a2..de9c189164 100644 --- a/src/app/forms/custom-sections/tyres/tyres.component.spec.ts +++ b/src/app/forms/custom-sections/tyres/tyres.component.spec.ts @@ -14,256 +14,292 @@ import { of, throwError } from 'rxjs'; import { TyresComponent } from './tyres.component'; const mockReferenceDataService = { - fetchReferenceDataByKey: jest.fn(), - loadReferenceData: jest.fn(), - getAll$: jest.fn().mockReturnValue(of([])), + fetchReferenceDataByKey: jest.fn(), + loadReferenceData: jest.fn(), + getAll$: jest.fn().mockReturnValue(of([])), }; const mockTechRecordService = { - getAxleFittingWeightValueFromLoadIndex: jest.fn(), + getAxleFittingWeightValueFromLoadIndex: jest.fn(), }; describe('TyresComponent', () => { - let component: TyresComponent; - let fixture: ComponentFixture; - let spy: jest.SpyInstance; - - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [RouterTestingModule, HttpClientTestingModule, DynamicFormsModule, StoreModule.forRoot({})], - declarations: [TyresComponent], - providers: [ - provideMockStore({ initialState: initialAppState }), - { provide: ReferenceDataService, useValue: mockReferenceDataService }, - { provide: TechnicalRecordService, useValue: mockTechRecordService }, - ], - }).compileComponents(); - }); - - beforeEach(() => { - fixture = TestBed.createComponent(TyresComponent); - component = fixture.componentInstance; - component.vehicleTechRecord = mockVehicleTechnicalRecord('psv'); - component.vehicleTechRecord.techRecord_axles = [ - { - axleNumber: 1, - tyres_tyreSize: '295/80-22.5', - tyres_speedCategorySymbol: 'p', - tyres_fitmentCode: 'double', - tyres_dataTrAxles: 0, - tyres_plyRating: 'A', - tyres_tyreCode: 456, - parkingBrakeMrk: false, - - weights_kerbWeight: 1, - weights_ladenWeight: 2, - weights_gbWeight: 3, - // TODO: V3 2 eecweights in type package, which is this? - // weights_eecWeight: 4, - weights_designWeight: 5, - }, - { - axleNumber: 2, - parkingBrakeMrk: true, - - tyres_tyreSize: '295/80-22.5', - tyres_speedCategorySymbol: 'p', - tyres_fitmentCode: 'double', - tyres_dataTrAxles: 0, - tyres_plyRating: 'A', - tyres_tyreCode: 456, - - weights_kerbWeight: 1, - weights_ladenWeight: 2, - weights_gbWeight: 3, - // weights_eecWeight: 4, - weights_designWeight: 5, - }, - { - axleNumber: 3, - parkingBrakeMrk: true, - - tyres_tyreSize: '295/80-22.5', - tyres_speedCategorySymbol: 'p', - tyres_fitmentCode: 'double', - tyres_dataTrAxles: 0, - tyres_plyRating: 'A', - tyres_tyreCode: 456, - - weights_kerbWeight: 1, - weights_ladenWeight: 2, - weights_gbWeight: 3, - // weights_eecWeight: 4, - weights_designWeight: 5, - }, - ]; - - fixture.detectChanges(); - spy = jest.spyOn(component, 'addTyreToTechRecord'); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); - - describe('checkAxleWeights', () => { - const currentAxle = [{ - weights_gbWeight: 1650, axleNumber: 1, tyres_dataTrAxles: 100, tyres_fitmentCode: 'single', tyres_tyreCode: '123', - }]; - const previousAxle = [{ - weights_gbWeight: 1649, axleNumber: 1, tyres_dataTrAxles: 100, tyres_fitmentCode: 'single', tyres_tyreCode: '123', - }]; - let simpleChanges: SimpleChanges; - beforeEach(() => { - simpleChanges = { - vehicleTechRecord: { currentValue: { techRecord_axles: currentAxle }, previousValue: { techRecord_axles: previousAxle }, firstChange: false }, - } as unknown as SimpleChanges; - component.isEditing = true; - }); - it('should return if isEditing is false', () => { - component.isEditing = false; - component.checkAxleWeights(simpleChanges); - expect(mockTechRecordService.getAxleFittingWeightValueFromLoadIndex).not.toHaveBeenCalled(); - }); - it('should call getAxleFittingWeightValueFromLoadIndex', () => { - component.checkAxleWeights(simpleChanges); - expect(mockTechRecordService.getAxleFittingWeightValueFromLoadIndex).toHaveBeenCalled(); - }); - it('should add an invalid axle to the invalidAxles array', () => { - mockTechRecordService.getAxleFittingWeightValueFromLoadIndex.mockReturnValue(1649); - component.checkAxleWeights(simpleChanges); - expect(component.invalidAxles).toContain(currentAxle[0].axleNumber); - }); - it('should not add a valid axle to the invalidAxles array', () => { - mockTechRecordService.getAxleFittingWeightValueFromLoadIndex.mockReturnValue(1651); - component.checkAxleWeights(simpleChanges); - expect(component.invalidAxles).not.toContain(currentAxle[0].axleNumber); - }); - }); - // TODO V3 PSV - describe('checkFitmentCodeHasChanged', () => { - it('should return false when there has been no change', () => { - const currentAxle = [{ tyres_fitmentCode: 'single', tyres_tyreCode: '123' }]; - const previousAxle = [{ tyres_fitmentCode: 'single', tyres_tyreCode: '123' }]; - - const simpleChanges = { - vehicleTechRecord: { currentValue: { techRecord_axles: currentAxle }, previousValue: { techRecord_axles: previousAxle }, firstChange: false }, - }; - - expect(component.checkFitmentCodeHasChanged(simpleChanges as unknown as SimpleChanges)).toBe(false); - }); - - it('should return true when there has been a change', () => { - jest.spyOn(component, 'getTyresRefData').mockImplementation(() => null); - const currentAxle = [{ tyres_fitmentCode: 'single', tyres_tyreCode: '123' }]; - const previousAxle = [{ tyres_fitmentCode: 'double', tyres_tyreCode: '123' }]; - - const simpleChanges = { - vehicleTechRecord: { currentValue: { techRecord_axles: currentAxle }, previousValue: { techRecord_axles: previousAxle }, firstChange: false }, - }; - - expect(component.checkFitmentCodeHasChanged(simpleChanges as unknown as SimpleChanges)).toBe(true); - }); - - it('should return false when there has been no change with multiple objects', () => { - const currentAxle = [ - { tyres_fitmentCode: 'single', tyres_tyreCode: '123' }, - { tyres_fitmentCode: 'single', tyres_tyreCode: '123' }, - ]; - const previousAxle = [ - { tyres_fitmentCode: 'single', tyres_tyreCode: '123' }, - { tyres_fitmentCode: 'single', tyres_tyreCode: '123' }, - ]; - - const simpleChanges = { - vehicleTechRecord: { currentValue: { techRecord_axles: currentAxle }, previousValue: { techRecord_axles: previousAxle }, firstChange: false }, - }; - - expect(component.checkFitmentCodeHasChanged(simpleChanges as unknown as SimpleChanges)).toBe(false); - }); - - it('should return true when there has been a change with multiple objects', () => { - jest.spyOn(component, 'getTyresRefData').mockImplementation(() => null); - const currentAxle = [ - { tyres_fitmentCode: 'single', tyres_tyreCode: '123' }, - { tyres_fitmentCode: 'single', tyres_tyreCode: '123' }, - ]; - const previousAxle = [ - { tyres_fitmentCode: 'double', tyres_tyreCode: '123' }, - { tyres_fitmentCode: 'single', tyres_tyreCode: '123' }, - ]; - - const simpleChanges = { - vehicleTechRecord: { currentValue: { techRecord_axles: currentAxle }, previousValue: { techRecord_axles: previousAxle }, firstChange: false }, - }; - - expect(component.checkFitmentCodeHasChanged(simpleChanges as unknown as SimpleChanges)).toBe(true); - }); - - it('should return false when this is a first change', () => { - const currentAxle = [ - { tyres_fitmentCode: 'single', tyres_tyreCode: '123' }, - { tyres_fitmentCode: 'single', tyres_tyreCode: '123' }, - ]; - const previousAxle = [ - { tyres_fitmentCode: 'double', tyres_tyreCode: '123' }, - { tyres_fitmentCode: 'single', tyres_tyreCode: '123' }, - ]; - - const simpleChanges = { - vehicleTechRecord: { currentValue: { techRecord_axles: currentAxle }, previousValue: { techRecord_axles: previousAxle }, firstChange: true }, - }; - - expect(component.checkFitmentCodeHasChanged(simpleChanges as unknown as SimpleChanges)).toBe(false); - }); - }); - - describe('getTyresRefData', () => { - it('should call add tyre to tech record with correct values', () => { - mockReferenceDataService.fetchReferenceDataByKey.mockImplementationOnce(() => { - return of({ - code: '101', - loadIndexSingleLoad: '123', - tyreSize: '123L:123', - dateTimeStamp: 'date', - userId: 'user', - loadIndexTwinLoad: '126', - plyRating: '12', - }); - }); - component.getTyresRefData(1); - - expect(spy).toHaveBeenCalledTimes(1); - }); - - it('should call add tyre to tech record with correct values when failure', () => { - mockReferenceDataService.fetchReferenceDataByKey.mockReturnValue(throwError(() => 'error')); - - component.getTyresRefData(1); - - expect(component.isError).toBe(true); - expect(component.errorMessage).toBe('Cannot find data of this tyre on axle 1'); - expect(spy).toHaveBeenCalledTimes(1); - }); - }); - - describe('addTyreToTechRecord', () => { - it('should update the tech record with the new tyre', () => { - const tyre = { - tyreSize: '123', - dataTrAxles: 123, - plyRating: '12', - tyreCode: 101, - }; - - component.addTyreToTechRecord(tyre, 1); - - const axles = component.vehicleTechRecord.techRecord_axles as Axles; - - expect(axles[0]?.tyres_tyreSize).toBe(tyre.tyreSize); - expect(axles[0]?.tyres_dataTrAxles).toBe(tyre.dataTrAxles); - expect(axles[0]?.tyres_plyRating).toBe(tyre.plyRating); - expect(axles[0]?.tyres_tyreCode).toBe(tyre.tyreCode); - }); - }); + let component: TyresComponent; + let fixture: ComponentFixture; + let spy: jest.SpyInstance; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [RouterTestingModule, HttpClientTestingModule, DynamicFormsModule, StoreModule.forRoot({})], + declarations: [TyresComponent], + providers: [ + provideMockStore({ initialState: initialAppState }), + { provide: ReferenceDataService, useValue: mockReferenceDataService }, + { provide: TechnicalRecordService, useValue: mockTechRecordService }, + ], + }).compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(TyresComponent); + component = fixture.componentInstance; + component.vehicleTechRecord = mockVehicleTechnicalRecord('psv'); + component.vehicleTechRecord.techRecord_axles = [ + { + axleNumber: 1, + tyres_tyreSize: '295/80-22.5', + tyres_speedCategorySymbol: 'p', + tyres_fitmentCode: 'double', + tyres_dataTrAxles: 0, + tyres_plyRating: 'A', + tyres_tyreCode: 456, + parkingBrakeMrk: false, + + weights_kerbWeight: 1, + weights_ladenWeight: 2, + weights_gbWeight: 3, + // TODO: V3 2 eecweights in type package, which is this? + // weights_eecWeight: 4, + weights_designWeight: 5, + }, + { + axleNumber: 2, + parkingBrakeMrk: true, + + tyres_tyreSize: '295/80-22.5', + tyres_speedCategorySymbol: 'p', + tyres_fitmentCode: 'double', + tyres_dataTrAxles: 0, + tyres_plyRating: 'A', + tyres_tyreCode: 456, + + weights_kerbWeight: 1, + weights_ladenWeight: 2, + weights_gbWeight: 3, + // weights_eecWeight: 4, + weights_designWeight: 5, + }, + { + axleNumber: 3, + parkingBrakeMrk: true, + + tyres_tyreSize: '295/80-22.5', + tyres_speedCategorySymbol: 'p', + tyres_fitmentCode: 'double', + tyres_dataTrAxles: 0, + tyres_plyRating: 'A', + tyres_tyreCode: 456, + + weights_kerbWeight: 1, + weights_ladenWeight: 2, + weights_gbWeight: 3, + // weights_eecWeight: 4, + weights_designWeight: 5, + }, + ]; + + fixture.detectChanges(); + spy = jest.spyOn(component, 'addTyreToTechRecord'); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + describe('checkAxleWeights', () => { + const currentAxle = [ + { + weights_gbWeight: 1650, + axleNumber: 1, + tyres_dataTrAxles: 100, + tyres_fitmentCode: 'single', + tyres_tyreCode: '123', + }, + ]; + const previousAxle = [ + { + weights_gbWeight: 1649, + axleNumber: 1, + tyres_dataTrAxles: 100, + tyres_fitmentCode: 'single', + tyres_tyreCode: '123', + }, + ]; + let simpleChanges: SimpleChanges; + beforeEach(() => { + simpleChanges = { + vehicleTechRecord: { + currentValue: { techRecord_axles: currentAxle }, + previousValue: { techRecord_axles: previousAxle }, + firstChange: false, + }, + } as unknown as SimpleChanges; + component.isEditing = true; + }); + it('should return if isEditing is false', () => { + component.isEditing = false; + component.checkAxleWeights(simpleChanges); + expect(mockTechRecordService.getAxleFittingWeightValueFromLoadIndex).not.toHaveBeenCalled(); + }); + it('should call getAxleFittingWeightValueFromLoadIndex', () => { + component.checkAxleWeights(simpleChanges); + expect(mockTechRecordService.getAxleFittingWeightValueFromLoadIndex).toHaveBeenCalled(); + }); + it('should add an invalid axle to the invalidAxles array', () => { + mockTechRecordService.getAxleFittingWeightValueFromLoadIndex.mockReturnValue(1649); + component.checkAxleWeights(simpleChanges); + expect(component.invalidAxles).toContain(currentAxle[0].axleNumber); + }); + it('should not add a valid axle to the invalidAxles array', () => { + mockTechRecordService.getAxleFittingWeightValueFromLoadIndex.mockReturnValue(1651); + component.checkAxleWeights(simpleChanges); + expect(component.invalidAxles).not.toContain(currentAxle[0].axleNumber); + }); + }); + // TODO V3 PSV + describe('checkFitmentCodeHasChanged', () => { + it('should return false when there has been no change', () => { + const currentAxle = [{ tyres_fitmentCode: 'single', tyres_tyreCode: '123' }]; + const previousAxle = [{ tyres_fitmentCode: 'single', tyres_tyreCode: '123' }]; + + const simpleChanges = { + vehicleTechRecord: { + currentValue: { techRecord_axles: currentAxle }, + previousValue: { techRecord_axles: previousAxle }, + firstChange: false, + }, + }; + + expect(component.checkFitmentCodeHasChanged(simpleChanges as unknown as SimpleChanges)).toBe(false); + }); + + it('should return true when there has been a change', () => { + jest.spyOn(component, 'getTyresRefData').mockImplementation(() => null); + const currentAxle = [{ tyres_fitmentCode: 'single', tyres_tyreCode: '123' }]; + const previousAxle = [{ tyres_fitmentCode: 'double', tyres_tyreCode: '123' }]; + + const simpleChanges = { + vehicleTechRecord: { + currentValue: { techRecord_axles: currentAxle }, + previousValue: { techRecord_axles: previousAxle }, + firstChange: false, + }, + }; + + expect(component.checkFitmentCodeHasChanged(simpleChanges as unknown as SimpleChanges)).toBe(true); + }); + + it('should return false when there has been no change with multiple objects', () => { + const currentAxle = [ + { tyres_fitmentCode: 'single', tyres_tyreCode: '123' }, + { tyres_fitmentCode: 'single', tyres_tyreCode: '123' }, + ]; + const previousAxle = [ + { tyres_fitmentCode: 'single', tyres_tyreCode: '123' }, + { tyres_fitmentCode: 'single', tyres_tyreCode: '123' }, + ]; + + const simpleChanges = { + vehicleTechRecord: { + currentValue: { techRecord_axles: currentAxle }, + previousValue: { techRecord_axles: previousAxle }, + firstChange: false, + }, + }; + + expect(component.checkFitmentCodeHasChanged(simpleChanges as unknown as SimpleChanges)).toBe(false); + }); + + it('should return true when there has been a change with multiple objects', () => { + jest.spyOn(component, 'getTyresRefData').mockImplementation(() => null); + const currentAxle = [ + { tyres_fitmentCode: 'single', tyres_tyreCode: '123' }, + { tyres_fitmentCode: 'single', tyres_tyreCode: '123' }, + ]; + const previousAxle = [ + { tyres_fitmentCode: 'double', tyres_tyreCode: '123' }, + { tyres_fitmentCode: 'single', tyres_tyreCode: '123' }, + ]; + + const simpleChanges = { + vehicleTechRecord: { + currentValue: { techRecord_axles: currentAxle }, + previousValue: { techRecord_axles: previousAxle }, + firstChange: false, + }, + }; + + expect(component.checkFitmentCodeHasChanged(simpleChanges as unknown as SimpleChanges)).toBe(true); + }); + + it('should return false when this is a first change', () => { + const currentAxle = [ + { tyres_fitmentCode: 'single', tyres_tyreCode: '123' }, + { tyres_fitmentCode: 'single', tyres_tyreCode: '123' }, + ]; + const previousAxle = [ + { tyres_fitmentCode: 'double', tyres_tyreCode: '123' }, + { tyres_fitmentCode: 'single', tyres_tyreCode: '123' }, + ]; + + const simpleChanges = { + vehicleTechRecord: { + currentValue: { techRecord_axles: currentAxle }, + previousValue: { techRecord_axles: previousAxle }, + firstChange: true, + }, + }; + + expect(component.checkFitmentCodeHasChanged(simpleChanges as unknown as SimpleChanges)).toBe(false); + }); + }); + + describe('getTyresRefData', () => { + it('should call add tyre to tech record with correct values', () => { + mockReferenceDataService.fetchReferenceDataByKey.mockImplementationOnce(() => { + return of({ + code: '101', + loadIndexSingleLoad: '123', + tyreSize: '123L:123', + dateTimeStamp: 'date', + userId: 'user', + loadIndexTwinLoad: '126', + plyRating: '12', + }); + }); + component.getTyresRefData(1); + + expect(spy).toHaveBeenCalledTimes(1); + }); + + it('should call add tyre to tech record with correct values when failure', () => { + mockReferenceDataService.fetchReferenceDataByKey.mockReturnValue(throwError(() => 'error')); + + component.getTyresRefData(1); + + expect(component.isError).toBe(true); + expect(component.errorMessage).toBe('Cannot find data of this tyre on axle 1'); + expect(spy).toHaveBeenCalledTimes(1); + }); + }); + + describe('addTyreToTechRecord', () => { + it('should update the tech record with the new tyre', () => { + const tyre = { + tyreSize: '123', + dataTrAxles: 123, + plyRating: '12', + tyreCode: 101, + }; + + component.addTyreToTechRecord(tyre, 1); + + const axles = component.vehicleTechRecord.techRecord_axles as Axles; + + expect(axles[0]?.tyres_tyreSize).toBe(tyre.tyreSize); + expect(axles[0]?.tyres_dataTrAxles).toBe(tyre.dataTrAxles); + expect(axles[0]?.tyres_plyRating).toBe(tyre.plyRating); + expect(axles[0]?.tyres_tyreCode).toBe(tyre.tyreCode); + }); + }); }); diff --git a/src/app/forms/custom-sections/tyres/tyres.component.ts b/src/app/forms/custom-sections/tyres/tyres.component.ts index bef4ac5676..03c7108d13 100644 --- a/src/app/forms/custom-sections/tyres/tyres.component.ts +++ b/src/app/forms/custom-sections/tyres/tyres.component.ts @@ -1,7 +1,5 @@ import { ViewportScroller } from '@angular/common'; -import { - Component, EventEmitter, Input, OnChanges, OnDestroy, OnInit, Output, SimpleChanges, -} from '@angular/core'; +import { Component, EventEmitter, Input, OnChanges, OnDestroy, OnInit, Output, SimpleChanges } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; import { TyreUseCode as HgvTyreUseCode } from '@dvsa/cvs-type-definitions/types/v3/tech-record/enums/tyreUseCodeHgv.enum.js'; import { TyreUseCode as TrlTyreUseCode } from '@dvsa/cvs-type-definitions/types/v3/tech-record/enums/tyreUseCodeTrl.enum.js'; @@ -9,7 +7,11 @@ import { TechRecordType } from '@dvsa/cvs-type-definitions/types/v3/tech-record/ import { MultiOptions } from '@forms/models/options.model'; import { DynamicFormService } from '@forms/services/dynamic-form.service'; import { - CustomFormArray, CustomFormGroup, FormNode, FormNodeEditTypes, FormNodeWidth, + CustomFormArray, + CustomFormGroup, + FormNode, + FormNodeEditTypes, + FormNodeWidth, } from '@forms/services/dynamic-form.types'; import { tyresTemplateHgv } from '@forms/templates/hgv/hgv-tyres.template'; import { PsvTyresTemplate } from '@forms/templates/psv/psv-tyres.template'; @@ -17,7 +19,13 @@ import { tyresTemplateTrl } from '@forms/templates/trl/trl-tyres.template'; import { getOptionsFromEnum, getOptionsFromEnumOneChar } from '@forms/utils/enum-map'; import { ReferenceDataResourceType, ReferenceDataTyre, ReferenceDataTyreLoadIndex } from '@models/reference-data.model'; import { - Axle, FitmentCode, ReasonForEditing, SpeedCategorySymbol, Tyre, Tyres, VehicleTypes, + Axle, + FitmentCode, + ReasonForEditing, + SpeedCategorySymbol, + Tyre, + Tyres, + VehicleTypes, } from '@models/vehicle-tech-record.model'; import { Store } from '@ngrx/store'; import { ReferenceDataService } from '@services/reference-data/reference-data.service'; @@ -26,280 +34,287 @@ import { selectAllReferenceDataByResourceType } from '@store/reference-data'; import { addAxle, removeAxle, updateScrollPosition } from '@store/technical-records'; import { TechnicalRecordServiceState } from '@store/technical-records/reducers/technical-record-service.reducer'; import { cloneDeep } from 'lodash'; -import { - Observable, - ReplaySubject, - filter, - takeUntil, -} from 'rxjs'; +import { Observable, ReplaySubject, filter, takeUntil } from 'rxjs'; @Component({ - selector: 'app-tyres', - templateUrl: './tyres.component.html', - styleUrls: ['./tyres.component.scss'], + selector: 'app-tyres', + templateUrl: './tyres.component.html', + styleUrls: ['./tyres.component.scss'], }) export class TyresComponent implements OnInit, OnDestroy, OnChanges { - @Input() vehicleTechRecord!: TechRecordType<'hgv' | 'psv' | 'trl'>; - @Input() isEditing = false; - - @Output() formChange = new EventEmitter(); - - private destroy$ = new ReplaySubject(1); - - public isError = false; - public errorMessage?: string; - public form!: CustomFormGroup; - private editingReason?: ReasonForEditing; - private loadIndexValues: ReferenceDataTyreLoadIndex[] | null = []; - - tyresReferenceData: ReferenceDataTyre[] = []; - invalidAxles: Array = []; - - constructor( - private dynamicFormsService: DynamicFormService, - private route: ActivatedRoute, - private router: Router, - private store: Store, - private viewportScroller: ViewportScroller, - private referenceDataService: ReferenceDataService, - private technicalRecordService: TechnicalRecordService, - ) { - this.editingReason = this.route.snapshot.data['reason']; - } - - ngOnInit(): void { - this.form = this.dynamicFormsService.createForm(this.template as FormNode, this.vehicleTechRecord) as CustomFormGroup; - this.form.cleanValueChanges.pipe(takeUntil(this.destroy$)).subscribe((event) => { - if (event && !Array.isArray(event) && event['axles']) { - event['axles'] = (event['axles'] as Axle[]).filter((axle) => !!axle?.axleNumber); - } - this.formChange.emit(event); - }); - - this.store.select(selectAllReferenceDataByResourceType(ReferenceDataResourceType.Tyres)) - .pipe(takeUntil(this.destroy$), filter(Boolean)).subscribe(((data) => { - this.tyresReferenceData = data as ReferenceDataTyre[]; - })); - - this.loadIndex$.pipe(takeUntil(this.destroy$)).subscribe((value): void => { - this.loadIndexValues = value; - }); - } - - ngOnChanges(simpleChanges: SimpleChanges): void { - const fitmentUpdated = this.checkFitmentCodeHasChanged(simpleChanges); - this.checkAxleWeights(simpleChanges); - if (!fitmentUpdated) { - this.form?.patchValue(this.vehicleTechRecord, { emitEvent: false }); - } - } - - ngOnDestroy(): void { - this.destroy$.next(true); - this.destroy$.unsubscribe(); - } - - get template(): FormNode | undefined { - switch (this.vehicleTechRecord.techRecord_vehicleType) { - case VehicleTypes.PSV: - return PsvTyresTemplate; - case VehicleTypes.HGV: - return tyresTemplateHgv; - case VehicleTypes.TRL: - return tyresTemplateTrl; - default: - return undefined; - } - } - - get isPsv(): boolean { - return this.vehicleTechRecord.techRecord_vehicleType === VehicleTypes.PSV; - } - - get isTrl(): boolean { - return this.vehicleTechRecord.techRecord_vehicleType === VehicleTypes.TRL; - } - - get types(): typeof FormNodeEditTypes { - return FormNodeEditTypes; - } - - get widths(): typeof FormNodeWidth { - return FormNodeWidth; - } - - get speedCategorySymbol(): MultiOptions { - return getOptionsFromEnum(SpeedCategorySymbol); - } - - get fitmentCode(): MultiOptions { - return getOptionsFromEnumOneChar(FitmentCode); - } - - get axles(): CustomFormArray { - return this.form.get(['techRecord_axles']) as CustomFormArray; - } - - get requiredPlates(): boolean { - return this.vehicleTechRecord.techRecord_vehicleType !== VehicleTypes.PSV && this.isEditing === true; - } - - getAxleTyres(i: number): CustomFormGroup { - return this.axles.get([i]) as CustomFormGroup; - } - - get loadIndex$(): Observable { - return this.referenceDataService.getAll$(ReferenceDataResourceType.TyreLoadIndex) as Observable; - } - - checkAxleWeights(simpleChanges: SimpleChanges) { - const { vehicleTechRecord } = simpleChanges; - this.invalidAxles = []; - if ( - !this.isEditing - || !vehicleTechRecord.currentValue.techRecord_axles - || (vehicleTechRecord.previousValue - && !vehicleTechRecord.previousValue.techRecord_axles - && (vehicleTechRecord.currentValue.techRecord_axles === vehicleTechRecord.previousValue.techRecord_axles))) { - return; - } - vehicleTechRecord.currentValue.techRecord_axles.forEach((axle: Axle) => { - if (axle.tyres_dataTrAxles && axle.weights_gbWeight && axle.axleNumber) { - const weightValue = this.technicalRecordService.getAxleFittingWeightValueFromLoadIndex( - axle.tyres_dataTrAxles.toString(), - axle.tyres_fitmentCode, - this.loadIndexValues, - ); - if (weightValue && axle.weights_gbWeight > weightValue) { - this.invalidAxles.push(axle.axleNumber); - } - } - }); - } - - checkFitmentCodeHasChanged(simpleChanges: SimpleChanges): boolean { - const { vehicleTechRecord } = simpleChanges; - if (vehicleTechRecord.firstChange !== undefined && vehicleTechRecord.firstChange === false) { - const currentAxles = vehicleTechRecord.currentValue.techRecord_axles; - const previousAxles = vehicleTechRecord.previousValue.techRecord_axles; - - if (!previousAxles) return false; - - // eslint-disable-next-line no-restricted-syntax - for (const [index, axle] of currentAxles.entries()) { - if ( - axle?.tyres_fitmentCode !== undefined - && previousAxles[`${index}`] - && previousAxles[`${index}`].tyres_fitmentCode !== undefined - && axle.tyres_fitmentCode !== previousAxles[`${index}`].tyres_fitmentCode - && axle.tyres_tyreCode === previousAxles[`${index}`].tyres_tyreCode - ) { - - this.getTyresRefData(axle.axleNumber); - return true; - } - } - } - - return false; - } - - handleNoAxleInfo(axleNumber: number) { - this.errorMessage = `Cannot find data of this tyre on axle ${axleNumber}`; - this.isError = true; - const newTyre = new Tyre({ - tyreCode: null, - tyreSize: null, - plyRating: null, - dataTrAxles: null, - }); - - this.addTyreToTechRecord(newTyre, axleNumber); - } - - getTyresRefData(axleNumber: number): void { - this.errorMessage = undefined; - - const { techRecord_axles: axles } = this.vehicleTechRecord; - if (axles == null) return; - - const axle = axles[axleNumber - 1]; - if (axle) { - const { tyres_tyreCode: code } = axle; - const tyreReferenceData = this.tyresReferenceData.find((tyre) => tyre.code === String(code)); - if (tyreReferenceData) { - const { tyres_fitmentCode: fit } = axle; - const indexLoad = fit === FitmentCode.SINGLE - ? parseInt(String(tyreReferenceData.loadIndexSingleLoad), 10) - : parseInt(String(tyreReferenceData.loadIndexTwinLoad), 10); - - const newTyre = new Tyre({ - tyreCode: code, - tyreSize: tyreReferenceData.tyreSize, - plyRating: tyreReferenceData.plyRating, - dataTrAxles: indexLoad, - }); - - this.addTyreToTechRecord(newTyre, axleNumber); - - return; - } - } - - this.handleNoAxleInfo(axleNumber); - } - - getTyreSearchPage(axleNumber: number) { - const route = this.editingReason ? `../${this.editingReason}/tyre-search/${axleNumber}` : `./tyre-search/${axleNumber}`; - - this.store.dispatch(updateScrollPosition({ position: this.viewportScroller.getScrollPosition() })); - - void this.router.navigate([route], { relativeTo: this.route, state: this.vehicleTechRecord }); - } - - addTyreToTechRecord(tyre: Tyres, axleNumber: number): void { - this.vehicleTechRecord = cloneDeep(this.vehicleTechRecord); - const axleIndex = this.vehicleTechRecord.techRecord_axles?.findIndex((ax) => ax.axleNumber === axleNumber); - - if (axleIndex === undefined || axleIndex === -1) { - return; - } - const axle = this.vehicleTechRecord.techRecord_axles?.[`${axleIndex}`]; - if (axle) { - axle.tyres_tyreCode = tyre.tyreCode; - axle.tyres_tyreSize = tyre.tyreSize; - axle.tyres_plyRating = tyre.plyRating; - axle.tyres_dataTrAxles = tyre.dataTrAxles; - this.form.patchValue(this.vehicleTechRecord); - } - } - - addAxle(): void { - if (!this.vehicleTechRecord.techRecord_axles || this.vehicleTechRecord.techRecord_axles.length < 10) { - this.isError = false; - this.store.dispatch(addAxle()); - } else { - this.isError = true; - this.errorMessage = `Cannot have more than ${10} axles`; - } - } - - removeAxle(index: number): void { - const minLength = this.isTrl ? 1 : 2; - const axles = this.vehicleTechRecord.techRecord_axles; - - if (axles && axles.length > minLength) { - this.isError = false; - this.store.dispatch(removeAxle({ index })); - } else { - this.isError = true; - this.errorMessage = `Cannot have less than ${minLength} axles`; - } - } - - protected readonly getOptionsFromEnum = getOptionsFromEnum; - - get tyreUseCode() { - return getOptionsFromEnum(this.vehicleTechRecord.techRecord_vehicleType === 'hgv' ? HgvTyreUseCode : TrlTyreUseCode); - } + @Input() vehicleTechRecord!: TechRecordType<'hgv' | 'psv' | 'trl'>; + @Input() isEditing = false; + + @Output() formChange = new EventEmitter(); + + private destroy$ = new ReplaySubject(1); + + public isError = false; + public errorMessage?: string; + public form!: CustomFormGroup; + private editingReason?: ReasonForEditing; + private loadIndexValues: ReferenceDataTyreLoadIndex[] | null = []; + + tyresReferenceData: ReferenceDataTyre[] = []; + invalidAxles: Array = []; + + constructor( + private dynamicFormsService: DynamicFormService, + private route: ActivatedRoute, + private router: Router, + private store: Store, + private viewportScroller: ViewportScroller, + private referenceDataService: ReferenceDataService, + private technicalRecordService: TechnicalRecordService + ) { + this.editingReason = this.route.snapshot.data['reason']; + } + + ngOnInit(): void { + this.form = this.dynamicFormsService.createForm( + this.template as FormNode, + this.vehicleTechRecord + ) as CustomFormGroup; + this.form.cleanValueChanges.pipe(takeUntil(this.destroy$)).subscribe((event) => { + if (event && !Array.isArray(event) && event['axles']) { + event['axles'] = (event['axles'] as Axle[]).filter((axle) => !!axle?.axleNumber); + } + this.formChange.emit(event); + }); + + this.store + .select(selectAllReferenceDataByResourceType(ReferenceDataResourceType.Tyres)) + .pipe(takeUntil(this.destroy$), filter(Boolean)) + .subscribe((data) => { + this.tyresReferenceData = data as ReferenceDataTyre[]; + }); + + this.loadIndex$.pipe(takeUntil(this.destroy$)).subscribe((value): void => { + this.loadIndexValues = value; + }); + } + + ngOnChanges(simpleChanges: SimpleChanges): void { + const fitmentUpdated = this.checkFitmentCodeHasChanged(simpleChanges); + this.checkAxleWeights(simpleChanges); + if (!fitmentUpdated) { + this.form?.patchValue(this.vehicleTechRecord, { emitEvent: false }); + } + } + + ngOnDestroy(): void { + this.destroy$.next(true); + this.destroy$.unsubscribe(); + } + + get template(): FormNode | undefined { + switch (this.vehicleTechRecord.techRecord_vehicleType) { + case VehicleTypes.PSV: + return PsvTyresTemplate; + case VehicleTypes.HGV: + return tyresTemplateHgv; + case VehicleTypes.TRL: + return tyresTemplateTrl; + default: + return undefined; + } + } + + get isPsv(): boolean { + return this.vehicleTechRecord.techRecord_vehicleType === VehicleTypes.PSV; + } + + get isTrl(): boolean { + return this.vehicleTechRecord.techRecord_vehicleType === VehicleTypes.TRL; + } + + get types(): typeof FormNodeEditTypes { + return FormNodeEditTypes; + } + + get widths(): typeof FormNodeWidth { + return FormNodeWidth; + } + + get speedCategorySymbol(): MultiOptions { + return getOptionsFromEnum(SpeedCategorySymbol); + } + + get fitmentCode(): MultiOptions { + return getOptionsFromEnumOneChar(FitmentCode); + } + + get axles(): CustomFormArray { + return this.form.get(['techRecord_axles']) as CustomFormArray; + } + + get requiredPlates(): boolean { + return this.vehicleTechRecord.techRecord_vehicleType !== VehicleTypes.PSV && this.isEditing === true; + } + + getAxleTyres(i: number): CustomFormGroup { + return this.axles.get([i]) as CustomFormGroup; + } + + get loadIndex$(): Observable { + return this.referenceDataService.getAll$(ReferenceDataResourceType.TyreLoadIndex) as Observable< + ReferenceDataTyreLoadIndex[] + >; + } + + checkAxleWeights(simpleChanges: SimpleChanges) { + const { vehicleTechRecord } = simpleChanges; + this.invalidAxles = []; + if ( + !this.isEditing || + !vehicleTechRecord.currentValue.techRecord_axles || + (vehicleTechRecord.previousValue && + !vehicleTechRecord.previousValue.techRecord_axles && + vehicleTechRecord.currentValue.techRecord_axles === vehicleTechRecord.previousValue.techRecord_axles) + ) { + return; + } + vehicleTechRecord.currentValue.techRecord_axles.forEach((axle: Axle) => { + if (axle.tyres_dataTrAxles && axle.weights_gbWeight && axle.axleNumber) { + const weightValue = this.technicalRecordService.getAxleFittingWeightValueFromLoadIndex( + axle.tyres_dataTrAxles.toString(), + axle.tyres_fitmentCode, + this.loadIndexValues + ); + if (weightValue && axle.weights_gbWeight > weightValue) { + this.invalidAxles.push(axle.axleNumber); + } + } + }); + } + + checkFitmentCodeHasChanged(simpleChanges: SimpleChanges): boolean { + const { vehicleTechRecord } = simpleChanges; + if (vehicleTechRecord.firstChange !== undefined && vehicleTechRecord.firstChange === false) { + const currentAxles = vehicleTechRecord.currentValue.techRecord_axles; + const previousAxles = vehicleTechRecord.previousValue.techRecord_axles; + + if (!previousAxles) return false; + + // eslint-disable-next-line no-restricted-syntax + for (const [index, axle] of currentAxles.entries()) { + if ( + axle?.tyres_fitmentCode !== undefined && + previousAxles[`${index}`] && + previousAxles[`${index}`].tyres_fitmentCode !== undefined && + axle.tyres_fitmentCode !== previousAxles[`${index}`].tyres_fitmentCode && + axle.tyres_tyreCode === previousAxles[`${index}`].tyres_tyreCode + ) { + this.getTyresRefData(axle.axleNumber); + return true; + } + } + } + + return false; + } + + handleNoAxleInfo(axleNumber: number) { + this.errorMessage = `Cannot find data of this tyre on axle ${axleNumber}`; + this.isError = true; + const newTyre = new Tyre({ + tyreCode: null, + tyreSize: null, + plyRating: null, + dataTrAxles: null, + }); + + this.addTyreToTechRecord(newTyre, axleNumber); + } + + getTyresRefData(axleNumber: number): void { + this.errorMessage = undefined; + + const { techRecord_axles: axles } = this.vehicleTechRecord; + if (axles == null) return; + + const axle = axles[axleNumber - 1]; + if (axle) { + const { tyres_tyreCode: code } = axle; + const tyreReferenceData = this.tyresReferenceData.find((tyre) => tyre.code === String(code)); + if (tyreReferenceData) { + const { tyres_fitmentCode: fit } = axle; + const indexLoad = + fit === FitmentCode.SINGLE + ? Number.parseInt(String(tyreReferenceData.loadIndexSingleLoad), 10) + : Number.parseInt(String(tyreReferenceData.loadIndexTwinLoad), 10); + + const newTyre = new Tyre({ + tyreCode: code, + tyreSize: tyreReferenceData.tyreSize, + plyRating: tyreReferenceData.plyRating, + dataTrAxles: indexLoad, + }); + + this.addTyreToTechRecord(newTyre, axleNumber); + + return; + } + } + + this.handleNoAxleInfo(axleNumber); + } + + getTyreSearchPage(axleNumber: number) { + const route = this.editingReason + ? `../${this.editingReason}/tyre-search/${axleNumber}` + : `./tyre-search/${axleNumber}`; + + this.store.dispatch(updateScrollPosition({ position: this.viewportScroller.getScrollPosition() })); + + void this.router.navigate([route], { relativeTo: this.route, state: this.vehicleTechRecord }); + } + + addTyreToTechRecord(tyre: Tyres, axleNumber: number): void { + this.vehicleTechRecord = cloneDeep(this.vehicleTechRecord); + const axleIndex = this.vehicleTechRecord.techRecord_axles?.findIndex((ax) => ax.axleNumber === axleNumber); + + if (axleIndex === undefined || axleIndex === -1) { + return; + } + const axle = this.vehicleTechRecord.techRecord_axles?.[`${axleIndex}`]; + if (axle) { + axle.tyres_tyreCode = tyre.tyreCode; + axle.tyres_tyreSize = tyre.tyreSize; + axle.tyres_plyRating = tyre.plyRating; + axle.tyres_dataTrAxles = tyre.dataTrAxles; + this.form.patchValue(this.vehicleTechRecord); + } + } + + addAxle(): void { + if (!this.vehicleTechRecord.techRecord_axles || this.vehicleTechRecord.techRecord_axles.length < 10) { + this.isError = false; + this.store.dispatch(addAxle()); + } else { + this.isError = true; + this.errorMessage = `Cannot have more than ${10} axles`; + } + } + + removeAxle(index: number): void { + const minLength = this.isTrl ? 1 : 2; + const axles = this.vehicleTechRecord.techRecord_axles; + + if (axles && axles.length > minLength) { + this.isError = false; + this.store.dispatch(removeAxle({ index })); + } else { + this.isError = true; + this.errorMessage = `Cannot have less than ${minLength} axles`; + } + } + + protected readonly getOptionsFromEnum = getOptionsFromEnum; + + get tyreUseCode() { + return getOptionsFromEnum( + this.vehicleTechRecord.techRecord_vehicleType === 'hgv' ? HgvTyreUseCode : TrlTyreUseCode + ); + } } diff --git a/src/app/forms/custom-sections/weights/weights.component.spec.ts b/src/app/forms/custom-sections/weights/weights.component.spec.ts index 19a37cabb0..57061faf3d 100644 --- a/src/app/forms/custom-sections/weights/weights.component.spec.ts +++ b/src/app/forms/custom-sections/weights/weights.component.spec.ts @@ -11,146 +11,146 @@ import { NumberInputComponent } from '../../components/number-input/number-input import { WeightsComponent } from './weights.component'; describe('WeightsComponent', () => { - let component: WeightsComponent; - let fixture: ComponentFixture; - let store: MockStore; - - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [DynamicFormsModule, StoreModule.forRoot({}), HttpClientTestingModule, RouterTestingModule], - declarations: [NumberInputComponent, WeightsComponent], - providers: [provideMockStore({ initialState: initialAppState })], - }).compileComponents(); - }); - - beforeEach(() => { - fixture = TestBed.createComponent(WeightsComponent); - store = TestBed.inject(MockStore); - component = fixture.componentInstance; - component.vehicleTechRecord = mockVehicleTechnicalRecord('trl') as TechRecordType<'trl'>; - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); - - describe('calculateGrossLadenWeight', () => { - it('should calculate the gross laden weight correctly for PSV vehicles made before 1988', () => { - component.vehicleTechRecord = mockVehicleTechnicalRecord('psv') as TechRecordType<'psv'>; - component.vehicleTechRecord.techRecord_seatsUpperDeck = 10; - component.vehicleTechRecord.techRecord_seatsLowerDeck = 10; - component.vehicleTechRecord.techRecord_manufactureYear = 1987; - component.vehicleTechRecord.techRecord_standingCapacity = 10; - component.vehicleTechRecord.techRecord_grossKerbWeight = 1000; - component.ngOnInit(); - - expect(component.calculateGrossLadenWeight()).toBe(2969); - }); - - it('should calculate the gross laden weight correctly for PSV vehicles made during/after 1988', () => { - component.vehicleTechRecord = mockVehicleTechnicalRecord('psv') as TechRecordType<'psv'>; - component.vehicleTechRecord.techRecord_seatsUpperDeck = 10; - component.vehicleTechRecord.techRecord_seatsLowerDeck = 10; - component.vehicleTechRecord.techRecord_manufactureYear = 1988; - component.vehicleTechRecord.techRecord_standingCapacity = 10; - component.vehicleTechRecord.techRecord_grossKerbWeight = 1000; - component.ngOnInit(); - - expect(component.calculateGrossLadenWeight()).toBe(3015); - }); - }); - - describe('getAxleForm', () => { - it('should return the axle form group for the axle at the specified index', () => { - component.addAxle(); - component.addAxle(); - - expect(component.getAxleForm(0)).toBeDefined(); - }); - }); - - describe('addAxle', () => { - it('should dispatch the addAxle action if the tech record has less than 10 axles associated with it', () => { - const dispatchSpy = jest.spyOn(store, 'dispatch'); - component.vehicleTechRecord = mockVehicleTechnicalRecord('psv') as TechRecordType<'psv'>; - component.vehicleTechRecord.techRecord_noOfAxles = 9; - component.vehicleTechRecord.techRecord_axles = new Array(9).fill({}); - component.ngOnInit(); - - component.addAxle(); - expect(dispatchSpy).toHaveBeenCalledWith({ - type: '[Technical Record Service] addAxle', - }); - }); - - it('should set isError to true, and errorMessage to a suitable string, if adding would result in more than 10 axles', () => { - const dispatchSpy = jest.spyOn(store, 'dispatch'); - component.vehicleTechRecord = mockVehicleTechnicalRecord('psv') as TechRecordType<'psv'>; - component.vehicleTechRecord.techRecord_noOfAxles = 10; - component.vehicleTechRecord.techRecord_axles = new Array(10).fill({}); - component.ngOnInit(); - - component.addAxle(); - expect(dispatchSpy).toHaveBeenCalledTimes(0); - expect(component.isError).toBe(true); - expect(component.errorMessage).toBe('Cannot have more than 10 axles'); - }); - }); - - describe('removeAxle', () => { - it('should dispatch the remove axle action if the vehicle is a TRL with more than 1 axle', () => { - const dispatchSpy = jest.spyOn(store, 'dispatch'); - component.vehicleTechRecord = mockVehicleTechnicalRecord('trl') as TechRecordType<'trl'>; - component.vehicleTechRecord.techRecord_noOfAxles = 2; - component.vehicleTechRecord.techRecord_axles = new Array(2).fill({}); - component.ngOnInit(); - - component.removeAxle(1); - expect(dispatchSpy).toHaveBeenCalledWith({ - index: 1, - type: '[Technical Record Service] removeAxle', - }); - }); - - it('should dispatch the remove axle action if the vehicle is not a TRL, but has more than 2 axles', () => { - const dispatchSpy = jest.spyOn(store, 'dispatch'); - component.vehicleTechRecord = mockVehicleTechnicalRecord('psv') as TechRecordType<'psv'>; - component.vehicleTechRecord.techRecord_noOfAxles = 3; - component.vehicleTechRecord.techRecord_axles = new Array(3).fill({}); - component.ngOnInit(); - - component.removeAxle(1); - expect(dispatchSpy).toHaveBeenCalledWith({ - index: 1, - type: '[Technical Record Service] removeAxle', - }); - }); - - it('should set isError to true and display an appropriate error message if the vehicle is a TRL and has 1 or fewer axles', () => { - const dispatchSpy = jest.spyOn(store, 'dispatch'); - component.vehicleTechRecord = mockVehicleTechnicalRecord('trl') as TechRecordType<'trl'>; - component.vehicleTechRecord.techRecord_noOfAxles = 1; - component.vehicleTechRecord.techRecord_axles = new Array(1).fill({}); - component.ngOnInit(); - - component.removeAxle(0); - expect(dispatchSpy).toHaveBeenCalledTimes(0); - expect(component.isError).toBe(true); - expect(component.errorMessage).toBe('Cannot have less than 1 axles'); - }); - - it('should set isError to true and display an appropriate error message if the vehicle is not a TRL and has 2 or fewer axles', () => { - const dispatchSpy = jest.spyOn(store, 'dispatch'); - component.vehicleTechRecord = mockVehicleTechnicalRecord('psv') as TechRecordType<'psv'>; - component.vehicleTechRecord.techRecord_noOfAxles = 2; - component.vehicleTechRecord.techRecord_axles = new Array(2).fill({}); - component.ngOnInit(); - - component.removeAxle(1); - expect(dispatchSpy).toHaveBeenCalledTimes(0); - expect(component.isError).toBe(true); - expect(component.errorMessage).toBe('Cannot have less than 2 axles'); - }); - }); + let component: WeightsComponent; + let fixture: ComponentFixture; + let store: MockStore; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [DynamicFormsModule, StoreModule.forRoot({}), HttpClientTestingModule, RouterTestingModule], + declarations: [NumberInputComponent, WeightsComponent], + providers: [provideMockStore({ initialState: initialAppState })], + }).compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(WeightsComponent); + store = TestBed.inject(MockStore); + component = fixture.componentInstance; + component.vehicleTechRecord = mockVehicleTechnicalRecord('trl') as TechRecordType<'trl'>; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + describe('calculateGrossLadenWeight', () => { + it('should calculate the gross laden weight correctly for PSV vehicles made before 1988', () => { + component.vehicleTechRecord = mockVehicleTechnicalRecord('psv') as TechRecordType<'psv'>; + component.vehicleTechRecord.techRecord_seatsUpperDeck = 10; + component.vehicleTechRecord.techRecord_seatsLowerDeck = 10; + component.vehicleTechRecord.techRecord_manufactureYear = 1987; + component.vehicleTechRecord.techRecord_standingCapacity = 10; + component.vehicleTechRecord.techRecord_grossKerbWeight = 1000; + component.ngOnInit(); + + expect(component.calculateGrossLadenWeight()).toBe(2969); + }); + + it('should calculate the gross laden weight correctly for PSV vehicles made during/after 1988', () => { + component.vehicleTechRecord = mockVehicleTechnicalRecord('psv') as TechRecordType<'psv'>; + component.vehicleTechRecord.techRecord_seatsUpperDeck = 10; + component.vehicleTechRecord.techRecord_seatsLowerDeck = 10; + component.vehicleTechRecord.techRecord_manufactureYear = 1988; + component.vehicleTechRecord.techRecord_standingCapacity = 10; + component.vehicleTechRecord.techRecord_grossKerbWeight = 1000; + component.ngOnInit(); + + expect(component.calculateGrossLadenWeight()).toBe(3015); + }); + }); + + describe('getAxleForm', () => { + it('should return the axle form group for the axle at the specified index', () => { + component.addAxle(); + component.addAxle(); + + expect(component.getAxleForm(0)).toBeDefined(); + }); + }); + + describe('addAxle', () => { + it('should dispatch the addAxle action if the tech record has less than 10 axles associated with it', () => { + const dispatchSpy = jest.spyOn(store, 'dispatch'); + component.vehicleTechRecord = mockVehicleTechnicalRecord('psv') as TechRecordType<'psv'>; + component.vehicleTechRecord.techRecord_noOfAxles = 9; + component.vehicleTechRecord.techRecord_axles = new Array(9).fill({}); + component.ngOnInit(); + + component.addAxle(); + expect(dispatchSpy).toHaveBeenCalledWith({ + type: '[Technical Record Service] addAxle', + }); + }); + + it('should set isError to true, and errorMessage to a suitable string, if adding would result in more than 10 axles', () => { + const dispatchSpy = jest.spyOn(store, 'dispatch'); + component.vehicleTechRecord = mockVehicleTechnicalRecord('psv') as TechRecordType<'psv'>; + component.vehicleTechRecord.techRecord_noOfAxles = 10; + component.vehicleTechRecord.techRecord_axles = new Array(10).fill({}); + component.ngOnInit(); + + component.addAxle(); + expect(dispatchSpy).toHaveBeenCalledTimes(0); + expect(component.isError).toBe(true); + expect(component.errorMessage).toBe('Cannot have more than 10 axles'); + }); + }); + + describe('removeAxle', () => { + it('should dispatch the remove axle action if the vehicle is a TRL with more than 1 axle', () => { + const dispatchSpy = jest.spyOn(store, 'dispatch'); + component.vehicleTechRecord = mockVehicleTechnicalRecord('trl') as TechRecordType<'trl'>; + component.vehicleTechRecord.techRecord_noOfAxles = 2; + component.vehicleTechRecord.techRecord_axles = new Array(2).fill({}); + component.ngOnInit(); + + component.removeAxle(1); + expect(dispatchSpy).toHaveBeenCalledWith({ + index: 1, + type: '[Technical Record Service] removeAxle', + }); + }); + + it('should dispatch the remove axle action if the vehicle is not a TRL, but has more than 2 axles', () => { + const dispatchSpy = jest.spyOn(store, 'dispatch'); + component.vehicleTechRecord = mockVehicleTechnicalRecord('psv') as TechRecordType<'psv'>; + component.vehicleTechRecord.techRecord_noOfAxles = 3; + component.vehicleTechRecord.techRecord_axles = new Array(3).fill({}); + component.ngOnInit(); + + component.removeAxle(1); + expect(dispatchSpy).toHaveBeenCalledWith({ + index: 1, + type: '[Technical Record Service] removeAxle', + }); + }); + + it('should set isError to true and display an appropriate error message if the vehicle is a TRL and has 1 or fewer axles', () => { + const dispatchSpy = jest.spyOn(store, 'dispatch'); + component.vehicleTechRecord = mockVehicleTechnicalRecord('trl') as TechRecordType<'trl'>; + component.vehicleTechRecord.techRecord_noOfAxles = 1; + component.vehicleTechRecord.techRecord_axles = new Array(1).fill({}); + component.ngOnInit(); + + component.removeAxle(0); + expect(dispatchSpy).toHaveBeenCalledTimes(0); + expect(component.isError).toBe(true); + expect(component.errorMessage).toBe('Cannot have less than 1 axles'); + }); + + it('should set isError to true and display an appropriate error message if the vehicle is not a TRL and has 2 or fewer axles', () => { + const dispatchSpy = jest.spyOn(store, 'dispatch'); + component.vehicleTechRecord = mockVehicleTechnicalRecord('psv') as TechRecordType<'psv'>; + component.vehicleTechRecord.techRecord_noOfAxles = 2; + component.vehicleTechRecord.techRecord_axles = new Array(2).fill({}); + component.ngOnInit(); + + component.removeAxle(1); + expect(dispatchSpy).toHaveBeenCalledTimes(0); + expect(component.isError).toBe(true); + expect(component.errorMessage).toBe('Cannot have less than 2 axles'); + }); + }); }); diff --git a/src/app/forms/custom-sections/weights/weights.component.ts b/src/app/forms/custom-sections/weights/weights.component.ts index 06b54806c4..954b552da8 100644 --- a/src/app/forms/custom-sections/weights/weights.component.ts +++ b/src/app/forms/custom-sections/weights/weights.component.ts @@ -1,12 +1,8 @@ /* eslint-disable no-underscore-dangle */ -import { - Component, EventEmitter, Input, OnChanges, OnDestroy, OnInit, Output, SimpleChanges, -} from '@angular/core'; +import { Component, EventEmitter, Input, OnChanges, OnDestroy, OnInit, Output, SimpleChanges } from '@angular/core'; import { TechRecordType } from '@dvsa/cvs-type-definitions/types/v3/tech-record/tech-record-vehicle-type'; import { DynamicFormService } from '@forms/services/dynamic-form.service'; -import { - CustomFormArray, CustomFormGroup, FormNode, FormNodeEditTypes, -} from '@forms/services/dynamic-form.types'; +import { CustomFormArray, CustomFormGroup, FormNode, FormNodeEditTypes } from '@forms/services/dynamic-form.types'; import { HgvWeight } from '@forms/templates/hgv/hgv-weight.template'; import { PsvWeightsTemplate } from '@forms/templates/psv/psv-weight.template'; import { TrlWeight } from '@forms/templates/trl/trl-weight.template'; @@ -17,190 +13,210 @@ import { TechnicalRecordServiceState } from '@store/technical-records/reducers/t import { Subscription } from 'rxjs'; @Component({ - selector: 'app-weights[vehicleTechRecord]', - templateUrl: './weights.component.html', - styleUrls: ['./weights.component.scss'], + selector: 'app-weights[vehicleTechRecord]', + templateUrl: './weights.component.html', + styleUrls: ['./weights.component.scss'], }) export class WeightsComponent implements OnInit, OnDestroy, OnChanges { - @Input() vehicleTechRecord!: TechRecordType<'psv'> | TechRecordType<'trl'> | TechRecordType<'hgv'>; - @Input() isEditing = false; - @Output() formChange = new EventEmitter(); - - public form!: CustomFormGroup; - private _formSubscription = new Subscription(); - public isError = false; - public errorMessage?: string; - - constructor(public dynamicFormsService: DynamicFormService, private store: Store) {} - - ngOnInit(): void { - this.initializeForm(); - this.subscribeToFieldsForGrossLadenWeightRecalculation(); - this.subscribeToFormChanges(); - } - - ngOnChanges(changes: SimpleChanges): void { - this.handleVehicleTechRecordChange(changes); - } - - ngOnDestroy(): void { - this._formSubscription.unsubscribe(); - } - - get template(): FormNode { - switch (this.vehicleTechRecord.techRecord_vehicleType) { - case VehicleTypes.PSV: - return PsvWeightsTemplate; - case VehicleTypes.HGV: - return HgvWeight; - case VehicleTypes.TRL: - return TrlWeight; - default: - throw Error('Incorrect vehicle type!'); - } - } - - get isPsv(): boolean { - return this.vehicleTechRecord.techRecord_vehicleType === VehicleTypes.PSV; - } - - get isHgv(): boolean { - return this.vehicleTechRecord.techRecord_vehicleType === VehicleTypes.HGV; - } - - get isTrl(): boolean { - return this.vehicleTechRecord.techRecord_vehicleType === VehicleTypes.TRL; - } - - get requiredPlates(): boolean { - return !this.isPsv && this.isEditing; - } - - get types(): typeof FormNodeEditTypes { - return FormNodeEditTypes; - } - - get axles(): CustomFormArray { - return this.form.get(['techRecord_axles']) as CustomFormArray; - } - - private initializeForm(): void { - this.form = this.dynamicFormsService.createForm(this.template, this.vehicleTechRecord) as CustomFormGroup; - } - - private subscribeToFieldsForGrossLadenWeightRecalculation(): void { - const fields = [ - 'techRecord_seatsUpperDeck', - 'techRecord_seatsLowerDeck', - 'techRecord_manufactureYear', - 'techRecord_grossKerbWeight', - 'techRecord_standingCapacity', - ]; - - fields.forEach((field) => { - this.form.get(field)?.valueChanges.subscribe(() => { - if (this.form.value.techRecord_manufactureYear) { - const newGrossLadenWeight = this.calculateGrossLadenWeight(); - this.form.patchValue({ techRecord_grossLadenWeight: newGrossLadenWeight }, { emitEvent: false }); - } - }); - }); - } - - private handleVehicleTechRecordChange(changes: SimpleChanges): void { - const { vehicleTechRecord } = changes; - if (this.form && vehicleTechRecord) { - const { currentValue, previousValue } = vehicleTechRecord; - - const fieldsChanged = [ - 'techRecord_seatsUpperDeck', - 'techRecord_seatsLowerDeck', - 'techRecord_manufactureYear', - 'techRecord_grossKerbWeight', - 'techRecord_standingCapacity', - ].some((field) => currentValue[`${field}`] !== previousValue[`${field}`]); - - if (fieldsChanged && currentValue.techRecord_manufactureYear && this.vehicleTechRecord.techRecord_vehicleType === 'psv') { - this.vehicleTechRecord.techRecord_grossLadenWeight = this.calculateGrossLadenWeight(); - } - - this.form.patchValue(this.vehicleTechRecord, { emitEvent: false }); - } - } - - private subscribeToFormChanges(): void { - this._formSubscription.add( - this.form.valueChanges.subscribe((event) => { - if (event?.techRecord_grossLadenWeight) { - (this.vehicleTechRecord as TechRecordType<'psv'>).techRecord_grossLadenWeight = event.techRecord_grossLadenWeight; - this.form.patchValue({ techRecord_grossLadenWeight: event.techRecord_grossLadenWeight }, { emitEvent: false }); - this.formChange.emit(event); - updateBrakeForces({ grossLadenWeight: event.techRecord_grossLadenWeight, grossKerbWeight: event.techRecord_grossKerbWeight }); - return; - } - this.handleFormChanges(event); - }), - ); - } - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - private handleFormChanges(event: any): void { - if (this.isPsv && this.determineRecalculationNeeded(event) && this.form.value.techRecord_manufactureYear) { - event.techRecord_grossLadenWeight = this.calculateGrossLadenWeight(); - this.form.get('techRecord_grossLadenWeight')?.setValue(event.techRecord_grossLadenWeight, { emitEvent: false }); - } - - this.formChange.emit(event); - if (event?.techRecord_grossLadenWeight || event?.techRecord_grossKerbWeight) { - this.store.dispatch( - updateBrakeForces({ grossLadenWeight: event.techRecord_grossLadenWeight, grossKerbWeight: event.techRecord_grossKerbWeight }), - ); - } - } - - private determineRecalculationNeeded(event: Record): boolean { - return ['techRecord_seatsUpperDeck', 'techRecord_seatsLowerDeck', 'techRecord_manufactureYear', 'techRecord_grossKerbWeight'].some( - (field) => event[`${field}`] !== undefined, - ); - } - - calculateGrossLadenWeight(): number { - const psvRecord = this.vehicleTechRecord as TechRecordType<'psv'>; - const techRecord_seatsUpperDeck = psvRecord?.techRecord_seatsUpperDeck ?? 0; - const techRecord_seatsLowerDeck = psvRecord?.techRecord_seatsLowerDeck ?? 0; - const techRecord_manufactureYear = psvRecord?.techRecord_manufactureYear ?? 0; - const techRecord_grossKerbWeight = psvRecord?.techRecord_grossKerbWeight ?? 0; - const techRecord_standingCapacity = psvRecord?.techRecord_standingCapacity ?? 0; - const kgAllowedPerPerson = techRecord_manufactureYear >= 1988 ? 65 : 63.5; - - const totalPassengers = techRecord_seatsUpperDeck + techRecord_seatsLowerDeck + techRecord_standingCapacity + 1; // Add 1 for the driver - return Math.ceil(totalPassengers * kgAllowedPerPerson + techRecord_grossKerbWeight); - } - - getAxleForm(i: number): CustomFormGroup { - return this.axles.get([i]) as CustomFormGroup; - } - - addAxle(): void { - if (!this.vehicleTechRecord.techRecord_axles || this.vehicleTechRecord.techRecord_axles.length < 10) { - this.isError = false; - this.store.dispatch(addAxle()); - } else { - this.isError = true; - this.errorMessage = `Cannot have more than ${10} axles`; - } - } - - removeAxle(index: number): void { - const minLength = this.isTrl ? 1 : 2; - const axles = this.vehicleTechRecord.techRecord_axles; - - if (axles && axles.length > minLength) { - this.isError = false; - this.store.dispatch(removeAxle({ index })); - } else { - this.isError = true; - this.errorMessage = `Cannot have less than ${minLength} axles`; - } - } + @Input() vehicleTechRecord!: TechRecordType<'psv'> | TechRecordType<'trl'> | TechRecordType<'hgv'>; + @Input() isEditing = false; + @Output() formChange = new EventEmitter(); + + public form!: CustomFormGroup; + private _formSubscription = new Subscription(); + public isError = false; + public errorMessage?: string; + + constructor( + public dynamicFormsService: DynamicFormService, + private store: Store + ) {} + + ngOnInit(): void { + this.initializeForm(); + this.subscribeToFieldsForGrossLadenWeightRecalculation(); + this.subscribeToFormChanges(); + } + + ngOnChanges(changes: SimpleChanges): void { + this.handleVehicleTechRecordChange(changes); + } + + ngOnDestroy(): void { + this._formSubscription.unsubscribe(); + } + + get template(): FormNode { + switch (this.vehicleTechRecord.techRecord_vehicleType) { + case VehicleTypes.PSV: + return PsvWeightsTemplate; + case VehicleTypes.HGV: + return HgvWeight; + case VehicleTypes.TRL: + return TrlWeight; + default: + throw Error('Incorrect vehicle type!'); + } + } + + get isPsv(): boolean { + return this.vehicleTechRecord.techRecord_vehicleType === VehicleTypes.PSV; + } + + get isHgv(): boolean { + return this.vehicleTechRecord.techRecord_vehicleType === VehicleTypes.HGV; + } + + get isTrl(): boolean { + return this.vehicleTechRecord.techRecord_vehicleType === VehicleTypes.TRL; + } + + get requiredPlates(): boolean { + return !this.isPsv && this.isEditing; + } + + get types(): typeof FormNodeEditTypes { + return FormNodeEditTypes; + } + + get axles(): CustomFormArray { + return this.form.get(['techRecord_axles']) as CustomFormArray; + } + + private initializeForm(): void { + this.form = this.dynamicFormsService.createForm(this.template, this.vehicleTechRecord) as CustomFormGroup; + } + + private subscribeToFieldsForGrossLadenWeightRecalculation(): void { + const fields = [ + 'techRecord_seatsUpperDeck', + 'techRecord_seatsLowerDeck', + 'techRecord_manufactureYear', + 'techRecord_grossKerbWeight', + 'techRecord_standingCapacity', + ]; + + fields.forEach((field) => { + this.form.get(field)?.valueChanges.subscribe(() => { + if (this.form.value.techRecord_manufactureYear) { + const newGrossLadenWeight = this.calculateGrossLadenWeight(); + this.form.patchValue({ techRecord_grossLadenWeight: newGrossLadenWeight }, { emitEvent: false }); + } + }); + }); + } + + private handleVehicleTechRecordChange(changes: SimpleChanges): void { + const { vehicleTechRecord } = changes; + if (this.form && vehicleTechRecord) { + const { currentValue, previousValue } = vehicleTechRecord; + + const fieldsChanged = [ + 'techRecord_seatsUpperDeck', + 'techRecord_seatsLowerDeck', + 'techRecord_manufactureYear', + 'techRecord_grossKerbWeight', + 'techRecord_standingCapacity', + ].some((field) => currentValue[`${field}`] !== previousValue[`${field}`]); + + if ( + fieldsChanged && + currentValue.techRecord_manufactureYear && + this.vehicleTechRecord.techRecord_vehicleType === 'psv' + ) { + this.vehicleTechRecord.techRecord_grossLadenWeight = this.calculateGrossLadenWeight(); + } + + this.form.patchValue(this.vehicleTechRecord, { emitEvent: false }); + } + } + + private subscribeToFormChanges(): void { + this._formSubscription.add( + this.form.valueChanges.subscribe((event) => { + if (event?.techRecord_grossLadenWeight) { + (this.vehicleTechRecord as TechRecordType<'psv'>).techRecord_grossLadenWeight = + event.techRecord_grossLadenWeight; + this.form.patchValue( + { techRecord_grossLadenWeight: event.techRecord_grossLadenWeight }, + { emitEvent: false } + ); + this.formChange.emit(event); + updateBrakeForces({ + grossLadenWeight: event.techRecord_grossLadenWeight, + grossKerbWeight: event.techRecord_grossKerbWeight, + }); + return; + } + this.handleFormChanges(event); + }) + ); + } + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + private handleFormChanges(event: any): void { + if (this.isPsv && this.determineRecalculationNeeded(event) && this.form.value.techRecord_manufactureYear) { + event.techRecord_grossLadenWeight = this.calculateGrossLadenWeight(); + this.form.get('techRecord_grossLadenWeight')?.setValue(event.techRecord_grossLadenWeight, { emitEvent: false }); + } + + this.formChange.emit(event); + if (event?.techRecord_grossLadenWeight || event?.techRecord_grossKerbWeight) { + this.store.dispatch( + updateBrakeForces({ + grossLadenWeight: event.techRecord_grossLadenWeight, + grossKerbWeight: event.techRecord_grossKerbWeight, + }) + ); + } + } + + private determineRecalculationNeeded(event: Record): boolean { + return [ + 'techRecord_seatsUpperDeck', + 'techRecord_seatsLowerDeck', + 'techRecord_manufactureYear', + 'techRecord_grossKerbWeight', + ].some((field) => event[`${field}`] !== undefined); + } + + calculateGrossLadenWeight(): number { + const psvRecord = this.vehicleTechRecord as TechRecordType<'psv'>; + const techRecord_seatsUpperDeck = psvRecord?.techRecord_seatsUpperDeck ?? 0; + const techRecord_seatsLowerDeck = psvRecord?.techRecord_seatsLowerDeck ?? 0; + const techRecord_manufactureYear = psvRecord?.techRecord_manufactureYear ?? 0; + const techRecord_grossKerbWeight = psvRecord?.techRecord_grossKerbWeight ?? 0; + const techRecord_standingCapacity = psvRecord?.techRecord_standingCapacity ?? 0; + const kgAllowedPerPerson = techRecord_manufactureYear >= 1988 ? 65 : 63.5; + + const totalPassengers = techRecord_seatsUpperDeck + techRecord_seatsLowerDeck + techRecord_standingCapacity + 1; // Add 1 for the driver + return Math.ceil(totalPassengers * kgAllowedPerPerson + techRecord_grossKerbWeight); + } + + getAxleForm(i: number): CustomFormGroup { + return this.axles.get([i]) as CustomFormGroup; + } + + addAxle(): void { + if (!this.vehicleTechRecord.techRecord_axles || this.vehicleTechRecord.techRecord_axles.length < 10) { + this.isError = false; + this.store.dispatch(addAxle()); + } else { + this.isError = true; + this.errorMessage = `Cannot have more than ${10} axles`; + } + } + + removeAxle(index: number): void { + const minLength = this.isTrl ? 1 : 2; + const axles = this.vehicleTechRecord.techRecord_axles; + + if (axles && axles.length > minLength) { + this.isError = false; + this.store.dispatch(removeAxle({ index })); + } else { + this.isError = true; + this.errorMessage = `Cannot have less than ${minLength} axles`; + } + } } diff --git a/src/app/forms/directives/app-no-space.directive.spec.ts b/src/app/forms/directives/app-no-space.directive.spec.ts index 54a6bdc5ff..7061a3bde2 100644 --- a/src/app/forms/directives/app-no-space.directive.spec.ts +++ b/src/app/forms/directives/app-no-space.directive.spec.ts @@ -1,133 +1,131 @@ import { Component } from '@angular/core'; import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { - FormControl, FormGroup, FormsModule, ReactiveFormsModule, -} from '@angular/forms'; +import { FormControl, FormGroup, FormsModule, ReactiveFormsModule } from '@angular/forms'; import { By } from '@angular/platform-browser'; import { NoSpaceDirective } from './app-no-space.directive'; @Component({ - template: `
+ template: `
`, }) class TestComponent { - form = new FormGroup({ - foo: new FormControl(), - }); + form = new FormGroup({ + foo: new FormControl(), + }); } describe('NoSpaceDirective', () => { - let fixture: ComponentFixture; - let input1: HTMLInputElement; - let input2: HTMLInputElement; - let component: TestComponent; - - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [FormsModule, ReactiveFormsModule], - declarations: [NoSpaceDirective, TestComponent], - }).compileComponents(); - - fixture = TestBed.createComponent(TestComponent); - fixture.detectChanges(); - - input1 = fixture.debugElement.query(By.css('#bar')).nativeElement; - input2 = fixture.debugElement.query(By.css('#baz')).nativeElement; - component = fixture.componentInstance; - }); - - it('should create an instance', () => { - expect(input1).toBeTruthy(); - expect(input2).toBeTruthy(); - }); - - it('should prevent default event behaviour if a prohibited key is pressed', () => { - const $event = new KeyboardEvent('keydown', { key: 'Space', cancelable: true }); - - expect(input1.dispatchEvent($event)).toBe(false); - expect($event.defaultPrevented).toBe(true); - - expect(input2.dispatchEvent($event)).toBe(false); - expect($event.defaultPrevented).toBe(true); - }); - - it('should not prevent default event behaviour if a prohibited key is pressed', () => { - const $event = new KeyboardEvent('keydown', { key: 'a', cancelable: true }); - - expect(input1.dispatchEvent($event)).toBe(true); - expect($event.defaultPrevented).toBe(false); - - expect(input2.dispatchEvent($event)).toBe(true); - expect($event.defaultPrevented).toBe(false); - }); - - describe('should remove all whitespaces on focusout', () => { - it('with form', () => { - input1.value = 'this has spaces '; - input1.dispatchEvent(new Event('focusout')); - - expect(input1.value).toBe('thishasspaces'); - expect(component.form.get('foo')?.value).toBe('thishasspaces'); - }); - - it('without form', () => { - input2.value = 'this has spaces '; - input2.dispatchEvent(new Event('focusout')); - - expect(input2.value).toBe('thishasspaces'); - }); - - describe('it should dispatch the appropriate number of input events', () => { - it('if the value has changed', () => { - const dispatchEventSpy = jest.spyOn(input1, 'dispatchEvent'); - input1.value = 'this has spaces '; - input1.dispatchEvent(new Event('focusout')); - - expect(dispatchEventSpy).toHaveBeenCalledTimes(2); - }); - - it('if the value has not changed', () => { - const dispatchEventSpy = jest.spyOn(input1, 'dispatchEvent'); - input1.value = 'thishasspaces'; - input1.dispatchEvent(new Event('focusout')); - - expect(dispatchEventSpy).toHaveBeenCalledTimes(1); - }); - }); - }); - - describe('should remove all whitespaces on input', () => { - it('with form', () => { - input1.value = 'this has spaces '; - input1.dispatchEvent(new Event('input')); - - expect(input1.value).toBe('thishasspaces'); - expect(component.form.get('foo')?.value).toBe('thishasspaces'); - }); - - it('without form', () => { - input2.value = 'this has spaces '; - input2.dispatchEvent(new Event('input')); - - expect(input2.value).toBe('thishasspaces'); - }); - - describe('it should dispatch the appropriate number of input events', () => { - it('if the value has changed', () => { - const dispatchEventSpy = jest.spyOn(input1, 'dispatchEvent'); - input1.value = 'this has spaces '; - input1.dispatchEvent(new Event('input')); - - expect(dispatchEventSpy).toHaveBeenCalledTimes(2); - }); - - it('if the value has not changed', () => { - const dispatchEventSpy = jest.spyOn(input1, 'dispatchEvent'); - input1.value = 'thishasspaces'; - input1.dispatchEvent(new Event('input')); - - expect(dispatchEventSpy).toHaveBeenCalledTimes(1); - }); - }); - }); + let fixture: ComponentFixture; + let input1: HTMLInputElement; + let input2: HTMLInputElement; + let component: TestComponent; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [FormsModule, ReactiveFormsModule], + declarations: [NoSpaceDirective, TestComponent], + }).compileComponents(); + + fixture = TestBed.createComponent(TestComponent); + fixture.detectChanges(); + + input1 = fixture.debugElement.query(By.css('#bar')).nativeElement; + input2 = fixture.debugElement.query(By.css('#baz')).nativeElement; + component = fixture.componentInstance; + }); + + it('should create an instance', () => { + expect(input1).toBeTruthy(); + expect(input2).toBeTruthy(); + }); + + it('should prevent default event behaviour if a prohibited key is pressed', () => { + const $event = new KeyboardEvent('keydown', { key: 'Space', cancelable: true }); + + expect(input1.dispatchEvent($event)).toBe(false); + expect($event.defaultPrevented).toBe(true); + + expect(input2.dispatchEvent($event)).toBe(false); + expect($event.defaultPrevented).toBe(true); + }); + + it('should not prevent default event behaviour if a prohibited key is pressed', () => { + const $event = new KeyboardEvent('keydown', { key: 'a', cancelable: true }); + + expect(input1.dispatchEvent($event)).toBe(true); + expect($event.defaultPrevented).toBe(false); + + expect(input2.dispatchEvent($event)).toBe(true); + expect($event.defaultPrevented).toBe(false); + }); + + describe('should remove all whitespaces on focusout', () => { + it('with form', () => { + input1.value = 'this has spaces '; + input1.dispatchEvent(new Event('focusout')); + + expect(input1.value).toBe('thishasspaces'); + expect(component.form.get('foo')?.value).toBe('thishasspaces'); + }); + + it('without form', () => { + input2.value = 'this has spaces '; + input2.dispatchEvent(new Event('focusout')); + + expect(input2.value).toBe('thishasspaces'); + }); + + describe('it should dispatch the appropriate number of input events', () => { + it('if the value has changed', () => { + const dispatchEventSpy = jest.spyOn(input1, 'dispatchEvent'); + input1.value = 'this has spaces '; + input1.dispatchEvent(new Event('focusout')); + + expect(dispatchEventSpy).toHaveBeenCalledTimes(2); + }); + + it('if the value has not changed', () => { + const dispatchEventSpy = jest.spyOn(input1, 'dispatchEvent'); + input1.value = 'thishasspaces'; + input1.dispatchEvent(new Event('focusout')); + + expect(dispatchEventSpy).toHaveBeenCalledTimes(1); + }); + }); + }); + + describe('should remove all whitespaces on input', () => { + it('with form', () => { + input1.value = 'this has spaces '; + input1.dispatchEvent(new Event('input')); + + expect(input1.value).toBe('thishasspaces'); + expect(component.form.get('foo')?.value).toBe('thishasspaces'); + }); + + it('without form', () => { + input2.value = 'this has spaces '; + input2.dispatchEvent(new Event('input')); + + expect(input2.value).toBe('thishasspaces'); + }); + + describe('it should dispatch the appropriate number of input events', () => { + it('if the value has changed', () => { + const dispatchEventSpy = jest.spyOn(input1, 'dispatchEvent'); + input1.value = 'this has spaces '; + input1.dispatchEvent(new Event('input')); + + expect(dispatchEventSpy).toHaveBeenCalledTimes(2); + }); + + it('if the value has not changed', () => { + const dispatchEventSpy = jest.spyOn(input1, 'dispatchEvent'); + input1.value = 'thishasspaces'; + input1.dispatchEvent(new Event('input')); + + expect(dispatchEventSpy).toHaveBeenCalledTimes(1); + }); + }); + }); }); diff --git a/src/app/forms/directives/app-no-space.directive.ts b/src/app/forms/directives/app-no-space.directive.ts index 632f119ceb..ee18b5ecdf 100644 --- a/src/app/forms/directives/app-no-space.directive.ts +++ b/src/app/forms/directives/app-no-space.directive.ts @@ -1,29 +1,29 @@ import { Directive, HostListener } from '@angular/core'; @Directive({ - selector: '[appNoSpace]', + selector: '[appNoSpace]', }) export class NoSpaceDirective { - @HostListener('keydown', ['$event']) - public onKeyDown(e: KeyboardEvent): void { - if (e.key === ' ' || e.key === 'Space') { - e.preventDefault(); - } - } + @HostListener('keydown', ['$event']) + public onKeyDown(e: KeyboardEvent): void { + if (e.key === ' ' || e.key === 'Space') { + e.preventDefault(); + } + } - @HostListener('focusout', ['$event.target']) - public onBlur(input: HTMLInputElement): void { - const oldValue = input.value; - input.value = input.value.replace(/\s/g, ''); + @HostListener('focusout', ['$event.target']) + public onBlur(input: HTMLInputElement): void { + const oldValue = input.value; + input.value = input.value.replace(/\s/g, ''); - if (input.value !== oldValue) input.dispatchEvent(new Event('input')); - } + if (input.value !== oldValue) input.dispatchEvent(new Event('input')); + } - @HostListener('input', ['$event.target']) - public onInput(input: HTMLInputElement): void { - const oldValue = input.value; - input.value = input.value.replace(/\s/g, ''); + @HostListener('input', ['$event.target']) + public onInput(input: HTMLInputElement): void { + const oldValue = input.value; + input.value = input.value.replace(/\s/g, ''); - if (input.value !== oldValue) input.dispatchEvent(new Event('input')); - } + if (input.value !== oldValue) input.dispatchEvent(new Event('input')); + } } diff --git a/src/app/forms/directives/app-number-only.directive.spec.ts b/src/app/forms/directives/app-number-only.directive.spec.ts index 8b18941f55..8fea9835c6 100644 --- a/src/app/forms/directives/app-number-only.directive.spec.ts +++ b/src/app/forms/directives/app-number-only.directive.spec.ts @@ -4,35 +4,35 @@ import { By } from '@angular/platform-browser'; import { NumberOnlyDirective } from './app-number-only.directive'; @Component({ - template: ' ', + template: ' ', }) class TestComponent {} describe('NumberOnlyDirective', () => { - let fixture: ComponentFixture; - let input: HTMLInputElement; + let fixture: ComponentFixture; + let input: HTMLInputElement; - beforeEach(() => { - fixture = TestBed.configureTestingModule({ - declarations: [NumberOnlyDirective, TestComponent], - }).createComponent(TestComponent); - fixture.detectChanges(); + beforeEach(() => { + fixture = TestBed.configureTestingModule({ + declarations: [NumberOnlyDirective, TestComponent], + }).createComponent(TestComponent); + fixture.detectChanges(); - input = fixture.debugElement.query(By.directive(NumberOnlyDirective)).nativeElement; - }); - it('should create an instance', () => { - expect(input).toBeTruthy(); - }); + input = fixture.debugElement.query(By.directive(NumberOnlyDirective)).nativeElement; + }); + it('should create an instance', () => { + expect(input).toBeTruthy(); + }); - it('should prevent default event behaviour if a prohibited key is pressed', () => { - const $event = new KeyboardEvent('keydown', { key: 'e', cancelable: true }); - expect(input.dispatchEvent($event)).toBe(false); - expect($event.defaultPrevented).toBe(true); - }); + it('should prevent default event behaviour if a prohibited key is pressed', () => { + const $event = new KeyboardEvent('keydown', { key: 'e', cancelable: true }); + expect(input.dispatchEvent($event)).toBe(false); + expect($event.defaultPrevented).toBe(true); + }); - it('should not prevent default event behaviour if a prohibited key is pressed', () => { - const $event = new KeyboardEvent('keydown', { key: '6', cancelable: true }); - expect(input.dispatchEvent($event)).toBe(true); - expect($event.defaultPrevented).toBe(false); - }); + it('should not prevent default event behaviour if a prohibited key is pressed', () => { + const $event = new KeyboardEvent('keydown', { key: '6', cancelable: true }); + expect(input.dispatchEvent($event)).toBe(true); + expect($event.defaultPrevented).toBe(false); + }); }); diff --git a/src/app/forms/directives/app-number-only.directive.ts b/src/app/forms/directives/app-number-only.directive.ts index 35d8b0421c..a41a5dd491 100644 --- a/src/app/forms/directives/app-number-only.directive.ts +++ b/src/app/forms/directives/app-number-only.directive.ts @@ -1,34 +1,47 @@ import { Directive, ElementRef, HostListener } from '@angular/core'; @Directive({ - selector: '[appNumberOnly]', + selector: '[appNumberOnly]', }) export class NumberOnlyDirective { - inputElement: HTMLInputElement; + inputElement: HTMLInputElement; - private navigationKeys = ['Backspace', 'Delete', 'Tab', 'Escape', 'Enter', 'Home', 'End', 'ArrowLeft', 'ArrowRight', 'Clear', 'Copy', 'Paste']; + private navigationKeys = [ + 'Backspace', + 'Delete', + 'Tab', + 'Escape', + 'Enter', + 'Home', + 'End', + 'ArrowLeft', + 'ArrowRight', + 'Clear', + 'Copy', + 'Paste', + ]; - constructor(private el: ElementRef) { - this.inputElement = el.nativeElement; - } + constructor(private el: ElementRef) { + this.inputElement = el.nativeElement; + } - @HostListener('keydown', ['$event']) - onKeyDown(e: KeyboardEvent) { - if ( - this.navigationKeys.indexOf(e.key) > -1 - || (e.key === 'a' && e.ctrlKey === true) - || (e.key === 'c' && e.ctrlKey === true) - || (e.key === 'v' && e.ctrlKey === true) - || (e.key === 'x' && e.ctrlKey === true) - || (e.key === 'a' && e.metaKey === true) - || (e.key === 'c' && e.metaKey === true) - || (e.key === 'v' && e.metaKey === true) - || (e.key === 'x' && e.metaKey === true) - ) { - return; - } - if (e.key === ' ' || Number.isNaN(Number(e.key))) { - e.preventDefault(); - } - } + @HostListener('keydown', ['$event']) + onKeyDown(e: KeyboardEvent) { + if ( + this.navigationKeys.indexOf(e.key) > -1 || + (e.key === 'a' && e.ctrlKey === true) || + (e.key === 'c' && e.ctrlKey === true) || + (e.key === 'v' && e.ctrlKey === true) || + (e.key === 'x' && e.ctrlKey === true) || + (e.key === 'a' && e.metaKey === true) || + (e.key === 'c' && e.metaKey === true) || + (e.key === 'v' && e.metaKey === true) || + (e.key === 'x' && e.metaKey === true) + ) { + return; + } + if (e.key === ' ' || Number.isNaN(Number(e.key))) { + e.preventDefault(); + } + } } diff --git a/src/app/forms/directives/app-to-uppercase.directive.spec.ts b/src/app/forms/directives/app-to-uppercase.directive.spec.ts index 4a9004c5db..71a7a05355 100644 --- a/src/app/forms/directives/app-to-uppercase.directive.spec.ts +++ b/src/app/forms/directives/app-to-uppercase.directive.spec.ts @@ -1,41 +1,39 @@ import { Component } from '@angular/core'; import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { - FormControl, FormGroup, FormsModule, ReactiveFormsModule, -} from '@angular/forms'; +import { FormControl, FormGroup, FormsModule, ReactiveFormsModule } from '@angular/forms'; import { By } from '@angular/platform-browser'; import { ToUppercaseDirective } from './app-to-uppercase.directive'; @Component({ - template: '
', + template: '
', }) class TestComponent { - form = new FormGroup({ - foo: new FormControl(), - }); + form = new FormGroup({ + foo: new FormControl(), + }); } describe('ToUppercaseDirective', () => { - let fixture: ComponentFixture; - let input: HTMLInputElement; - let component: TestComponent; + let fixture: ComponentFixture; + let input: HTMLInputElement; + let component: TestComponent; - beforeEach(() => { - fixture = TestBed.configureTestingModule({ - imports: [FormsModule, ReactiveFormsModule], - declarations: [ToUppercaseDirective, TestComponent], - }).createComponent(TestComponent); - fixture.detectChanges(); + beforeEach(() => { + fixture = TestBed.configureTestingModule({ + imports: [FormsModule, ReactiveFormsModule], + declarations: [ToUppercaseDirective, TestComponent], + }).createComponent(TestComponent); + fixture.detectChanges(); - input = fixture.debugElement.query(By.directive(ToUppercaseDirective)).nativeElement; - component = fixture.componentInstance; - }); + input = fixture.debugElement.query(By.directive(ToUppercaseDirective)).nativeElement; + component = fixture.componentInstance; + }); - it('should make the text uppercase on input', () => { - input.value = 'lowercase'; - input.dispatchEvent(new Event('focusout')); + it('should make the text uppercase on input', () => { + input.value = 'lowercase'; + input.dispatchEvent(new Event('focusout')); - expect(input.value).toBe('LOWERCASE'); - expect(component.form.get('foo')?.value).toBe('LOWERCASE'); - }); + expect(input.value).toBe('LOWERCASE'); + expect(component.form.get('foo')?.value).toBe('LOWERCASE'); + }); }); diff --git a/src/app/forms/directives/app-to-uppercase.directive.ts b/src/app/forms/directives/app-to-uppercase.directive.ts index 1e438a86ee..73f16d2421 100644 --- a/src/app/forms/directives/app-to-uppercase.directive.ts +++ b/src/app/forms/directives/app-to-uppercase.directive.ts @@ -1,12 +1,12 @@ import { Directive, HostListener } from '@angular/core'; @Directive({ - selector: '[appToUppercase]', + selector: '[appToUppercase]', }) export class ToUppercaseDirective { - @HostListener('focusout', ['$event.target']) - public onBlur(input: HTMLInputElement): void { - input.value = input.value.toUpperCase(); - input.dispatchEvent(new Event('input')); - } + @HostListener('focusout', ['$event.target']) + public onBlur(input: HTMLInputElement): void { + input.value = input.value.toUpperCase(); + input.dispatchEvent(new Event('input')); + } } diff --git a/src/app/forms/directives/app-trim-whitespace.directive.spec.ts b/src/app/forms/directives/app-trim-whitespace.directive.spec.ts index d38b06beb1..62ccc51b40 100644 --- a/src/app/forms/directives/app-trim-whitespace.directive.spec.ts +++ b/src/app/forms/directives/app-trim-whitespace.directive.spec.ts @@ -1,108 +1,106 @@ import { Component } from '@angular/core'; import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { - FormControl, FormGroup, FormsModule, ReactiveFormsModule, -} from '@angular/forms'; +import { FormControl, FormGroup, FormsModule, ReactiveFormsModule } from '@angular/forms'; import { By } from '@angular/platform-browser'; import { TrimWhitespaceDirective } from './app-trim-whitespace.directive'; @Component({ - template: `
+ template: `
`, }) class TestComponent { - form = new FormGroup({ - foo: new FormControl(), - }); + form = new FormGroup({ + foo: new FormControl(), + }); } describe('TrimWhitespaceDirective', () => { - let fixture: ComponentFixture; - let input1: HTMLInputElement; - let input2: HTMLInputElement; - let component: TestComponent; - - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [FormsModule, ReactiveFormsModule], - declarations: [TrimWhitespaceDirective, TestComponent], - }).compileComponents(); - - fixture = TestBed.createComponent(TestComponent); - fixture.detectChanges(); - - input1 = fixture.debugElement.query(By.css('#bar')).nativeElement; - input2 = fixture.debugElement.query(By.css('#baz')).nativeElement; - component = fixture.componentInstance; - }); - - describe('should trim whitespaces on focusout', () => { - it('with form', () => { - input1.value = 'this has spaces '; - input1.dispatchEvent(new Event('focusout')); - - expect(input1.value).toBe('this has spaces'); - expect(component.form.get('foo')?.value).toBe('this has spaces'); - }); - - it('without form', () => { - input2.value = 'this has spaces '; - input2.dispatchEvent(new Event('focusout')); - - expect(input2.value).toBe('this has spaces'); - }); - - describe('it should dispatch the appropriate number of events', () => { - it('if the value has changed', () => { - const dispatchEventSpy = jest.spyOn(input1, 'dispatchEvent'); - input1.value = 'this has spaces '; - input1.dispatchEvent(new Event('focusout')); - - expect(dispatchEventSpy).toHaveBeenCalledTimes(2); - }); - - it('if the value has not changed', () => { - const dispatchEventSpy = jest.spyOn(input1, 'dispatchEvent'); - input1.value = 'this has spaces'; - input1.dispatchEvent(new Event('focusout')); - - expect(dispatchEventSpy).toHaveBeenCalledTimes(1); - }); - }); - }); - - describe('should trim whitespaces on input', () => { - it('should trim the values and update the form', () => { - input1.value = 'this has spaces '; - input1.dispatchEvent(new Event('input')); - - expect(input1.value).toBe('this has spaces'); - expect(component.form.get('foo')?.value).toBe('this has spaces'); - }); - - it('without form', () => { - input2.value = 'this has spaces '; - input2.dispatchEvent(new Event('input')); - - expect(input2.value).toBe('this has spaces'); - }); - - describe('it should dispatch the appropriate number of input events', () => { - it('if the value has changed', () => { - const dispatchEventSpy = jest.spyOn(input1, 'dispatchEvent'); - input1.value = 'this has spaces '; - input1.dispatchEvent(new Event('input')); - - expect(dispatchEventSpy).toHaveBeenCalledTimes(2); - }); - - it('if the value has not changed', () => { - const dispatchEventSpy = jest.spyOn(input1, 'dispatchEvent'); - input1.value = 'this has spaces'; - input1.dispatchEvent(new Event('input')); - - expect(dispatchEventSpy).toHaveBeenCalledTimes(1); - }); - }); - }); + let fixture: ComponentFixture; + let input1: HTMLInputElement; + let input2: HTMLInputElement; + let component: TestComponent; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [FormsModule, ReactiveFormsModule], + declarations: [TrimWhitespaceDirective, TestComponent], + }).compileComponents(); + + fixture = TestBed.createComponent(TestComponent); + fixture.detectChanges(); + + input1 = fixture.debugElement.query(By.css('#bar')).nativeElement; + input2 = fixture.debugElement.query(By.css('#baz')).nativeElement; + component = fixture.componentInstance; + }); + + describe('should trim whitespaces on focusout', () => { + it('with form', () => { + input1.value = 'this has spaces '; + input1.dispatchEvent(new Event('focusout')); + + expect(input1.value).toBe('this has spaces'); + expect(component.form.get('foo')?.value).toBe('this has spaces'); + }); + + it('without form', () => { + input2.value = 'this has spaces '; + input2.dispatchEvent(new Event('focusout')); + + expect(input2.value).toBe('this has spaces'); + }); + + describe('it should dispatch the appropriate number of events', () => { + it('if the value has changed', () => { + const dispatchEventSpy = jest.spyOn(input1, 'dispatchEvent'); + input1.value = 'this has spaces '; + input1.dispatchEvent(new Event('focusout')); + + expect(dispatchEventSpy).toHaveBeenCalledTimes(2); + }); + + it('if the value has not changed', () => { + const dispatchEventSpy = jest.spyOn(input1, 'dispatchEvent'); + input1.value = 'this has spaces'; + input1.dispatchEvent(new Event('focusout')); + + expect(dispatchEventSpy).toHaveBeenCalledTimes(1); + }); + }); + }); + + describe('should trim whitespaces on input', () => { + it('should trim the values and update the form', () => { + input1.value = 'this has spaces '; + input1.dispatchEvent(new Event('input')); + + expect(input1.value).toBe('this has spaces'); + expect(component.form.get('foo')?.value).toBe('this has spaces'); + }); + + it('without form', () => { + input2.value = 'this has spaces '; + input2.dispatchEvent(new Event('input')); + + expect(input2.value).toBe('this has spaces'); + }); + + describe('it should dispatch the appropriate number of input events', () => { + it('if the value has changed', () => { + const dispatchEventSpy = jest.spyOn(input1, 'dispatchEvent'); + input1.value = 'this has spaces '; + input1.dispatchEvent(new Event('input')); + + expect(dispatchEventSpy).toHaveBeenCalledTimes(2); + }); + + it('if the value has not changed', () => { + const dispatchEventSpy = jest.spyOn(input1, 'dispatchEvent'); + input1.value = 'this has spaces'; + input1.dispatchEvent(new Event('input')); + + expect(dispatchEventSpy).toHaveBeenCalledTimes(1); + }); + }); + }); }); diff --git a/src/app/forms/directives/app-trim-whitespace.directive.ts b/src/app/forms/directives/app-trim-whitespace.directive.ts index 55e283fd32..6ee4c551e5 100644 --- a/src/app/forms/directives/app-trim-whitespace.directive.ts +++ b/src/app/forms/directives/app-trim-whitespace.directive.ts @@ -1,22 +1,22 @@ import { Directive, HostListener } from '@angular/core'; @Directive({ - selector: '[appTrimWhitespace]', + selector: '[appTrimWhitespace]', }) export class TrimWhitespaceDirective { - @HostListener('focusout', ['$event.target']) - public onBlur(input: HTMLInputElement): void { - const oldValue = input.value; - input.value = input.value.trim(); + @HostListener('focusout', ['$event.target']) + public onBlur(input: HTMLInputElement): void { + const oldValue = input.value; + input.value = input.value.trim(); - if (input.value !== oldValue) input.dispatchEvent(new Event('input')); - } + if (input.value !== oldValue) input.dispatchEvent(new Event('input')); + } - @HostListener('input', ['$event.target']) - public onInput(input: HTMLInputElement): void { - const oldValue = input.value; - input.value = input.value.trim(); + @HostListener('input', ['$event.target']) + public onInput(input: HTMLInputElement): void { + const oldValue = input.value; + input.value = input.value.trim(); - if (input.value !== oldValue) input.dispatchEvent(new Event('input')); - } + if (input.value !== oldValue) input.dispatchEvent(new Event('input')); + } } diff --git a/src/app/forms/directives/prefix.directive.spec.ts b/src/app/forms/directives/prefix.directive.spec.ts index dc78ae6999..0ba9243be7 100644 --- a/src/app/forms/directives/prefix.directive.spec.ts +++ b/src/app/forms/directives/prefix.directive.spec.ts @@ -3,27 +3,27 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { PrefixDirective } from './prefix.directive'; @Component({ - template: '', + template: '', }) class TestComponent {} describe('PrefixDirective', () => { - let fixture: ComponentFixture; - let component: TestComponent; + let fixture: ComponentFixture; + let component: TestComponent; - beforeEach(async () => { - await TestBed.configureTestingModule({ - declarations: [PrefixDirective, TestComponent], - providers: [TemplateRef], - }).compileComponents(); + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [PrefixDirective, TestComponent], + providers: [TemplateRef], + }).compileComponents(); - fixture = TestBed.createComponent(TestComponent); - fixture.detectChanges(); + fixture = TestBed.createComponent(TestComponent); + fixture.detectChanges(); - component = fixture.componentInstance; - }); + component = fixture.componentInstance; + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); }); diff --git a/src/app/forms/directives/prefix.directive.ts b/src/app/forms/directives/prefix.directive.ts index 8c326b6869..7b0bb5e3d2 100644 --- a/src/app/forms/directives/prefix.directive.ts +++ b/src/app/forms/directives/prefix.directive.ts @@ -1,8 +1,8 @@ import { Directive, TemplateRef } from '@angular/core'; @Directive({ - selector: '[appPrefix]', + selector: '[appPrefix]', }) export class PrefixDirective { - constructor(public templateRef: TemplateRef) {} + constructor(public templateRef: TemplateRef) {} } diff --git a/src/app/forms/directives/suffix.directive.spec.ts b/src/app/forms/directives/suffix.directive.spec.ts index beab7b4c77..a7d14644a7 100644 --- a/src/app/forms/directives/suffix.directive.spec.ts +++ b/src/app/forms/directives/suffix.directive.spec.ts @@ -3,27 +3,27 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { SuffixDirective } from './suffix.directive'; @Component({ - template: '', + template: '', }) class TestComponent {} describe('SuffixDirective', () => { - let fixture: ComponentFixture; - let component: TestComponent; + let fixture: ComponentFixture; + let component: TestComponent; - beforeEach(async () => { - await TestBed.configureTestingModule({ - declarations: [SuffixDirective, TestComponent], - providers: [TemplateRef], - }).compileComponents(); + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [SuffixDirective, TestComponent], + providers: [TemplateRef], + }).compileComponents(); - fixture = TestBed.createComponent(TestComponent); - fixture.detectChanges(); + fixture = TestBed.createComponent(TestComponent); + fixture.detectChanges(); - component = fixture.componentInstance; - }); + component = fixture.componentInstance; + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); }); diff --git a/src/app/forms/directives/suffix.directive.ts b/src/app/forms/directives/suffix.directive.ts index d4e5b77934..46bc9ab097 100644 --- a/src/app/forms/directives/suffix.directive.ts +++ b/src/app/forms/directives/suffix.directive.ts @@ -1,8 +1,8 @@ import { Directive, TemplateRef } from '@angular/core'; @Directive({ - selector: '[appSuffix]', + selector: '[appSuffix]', }) export class SuffixDirective { - constructor(public templateRef: TemplateRef) {} + constructor(public templateRef: TemplateRef) {} } diff --git a/src/app/forms/dynamic-forms.module.ts b/src/app/forms/dynamic-forms.module.ts index a9aea0b22a..ad435ca704 100644 --- a/src/app/forms/dynamic-forms.module.ts +++ b/src/app/forms/dynamic-forms.module.ts @@ -4,12 +4,8 @@ import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { RouterModule } from '@angular/router'; import { ApprovalTypeFocusNextDirective } from '@forms/components/approval-type/approval-type-focus-next.directive'; import { ApprovalTypeInputComponent } from '@forms/components/approval-type/approval-type.component'; -import { - AdrCertificateHistoryComponent, -} from '@forms/custom-sections/adr-certificate-history/adr-certificate-history.component'; -import { - AdrExaminerNotesHistoryEditComponent, -} from '@forms/custom-sections/adr-examiner-notes-history-edit/adr-examiner-notes-history.component-edit'; +import { AdrCertificateHistoryComponent } from '@forms/custom-sections/adr-certificate-history/adr-certificate-history.component'; +import { AdrExaminerNotesHistoryEditComponent } from '@forms/custom-sections/adr-examiner-notes-history-edit/adr-examiner-notes-history.component-edit'; import { ApprovalTypeComponent } from '@forms/custom-sections/approval-type/approval-type.component'; import { TruncatePipe } from '@shared/pipes/truncate/truncate.pipe'; import { SharedModule } from '@shared/shared.module'; @@ -38,25 +34,13 @@ import { ViewCombinationComponent } from './components/view-combination/view-com import { ViewListItemComponent } from './components/view-list-item/view-list-item.component'; import { AbandonDialogComponent } from './custom-sections/abandon-dialog/abandon-dialog.component'; import { AdrExaminerNotesHistoryViewComponent } from './custom-sections/adr-examiner-notes-history-view/adr-examiner-notes-history-view.component'; -import { - AdrNewCertificateRequiredViewComponent, -} from './custom-sections/adr-new-certificate-required-view/adr-new-certificate-required-view.component'; -import { - AdrTankDetailsInitialInspectionViewComponent, -} from './custom-sections/adr-tank-details-initial-inspection-view/adr-tank-details-initial-inspection-view.component'; +import { AdrNewCertificateRequiredViewComponent } from './custom-sections/adr-new-certificate-required-view/adr-new-certificate-required-view.component'; +import { AdrTankDetailsInitialInspectionViewComponent } from './custom-sections/adr-tank-details-initial-inspection-view/adr-tank-details-initial-inspection-view.component'; import { AdrTankDetailsM145ViewComponent } from './custom-sections/adr-tank-details-m145-view/adr-tank-details-m145-view.component'; -import { - AdrTankDetailsSubsequentInspectionsEditComponent, -} from './custom-sections/adr-tank-details-subsequent-inspections-edit/adr-tank-details-subsequent-inspections-edit.component'; -import { - AdrTankDetailsSubsequentInspectionsViewComponent, -} from './custom-sections/adr-tank-details-subsequent-inspections-view/adr-tank-details-subsequent-inspections-view.component'; -import { - AdrTankStatementUnNumberEditComponent, -} from './custom-sections/adr-tank-statement-un-number-edit/adr-tank-statement-un-number-edit.component'; -import { - AdrTankStatementUnNumberViewComponent, -} from './custom-sections/adr-tank-statement-un-number-view/adr-tank-statement-un-number-view.component'; +import { AdrTankDetailsSubsequentInspectionsEditComponent } from './custom-sections/adr-tank-details-subsequent-inspections-edit/adr-tank-details-subsequent-inspections-edit.component'; +import { AdrTankDetailsSubsequentInspectionsViewComponent } from './custom-sections/adr-tank-details-subsequent-inspections-view/adr-tank-details-subsequent-inspections-view.component'; +import { AdrTankStatementUnNumberEditComponent } from './custom-sections/adr-tank-statement-un-number-edit/adr-tank-statement-un-number-edit.component'; +import { AdrTankStatementUnNumberViewComponent } from './custom-sections/adr-tank-statement-un-number-view/adr-tank-statement-un-number-view.component'; import { AdrComponent } from './custom-sections/adr/adr.component'; import { BodyComponent } from './custom-sections/body/body.component'; import { CustomDefectComponent } from './custom-sections/custom-defect/custom-defect.component'; @@ -82,119 +66,119 @@ import { PrefixDirective } from './directives/prefix.directive'; import { SuffixDirective } from './directives/suffix.directive'; @NgModule({ - declarations: [ - BaseControlComponent, - TextInputComponent, - ViewListItemComponent, - DynamicFormGroupComponent, - ViewCombinationComponent, - CheckboxGroupComponent, - RadioGroupComponent, - DefectComponent, - DefectsComponent, - AutocompleteComponent, - NumberInputComponent, - TextAreaComponent, - NumberOnlyDirective, - ToUppercaseDirective, - NoSpaceDirective, - TrimWhitespaceDirective, - DateComponent, - SelectComponent, - DynamicFormFieldComponent, - FieldErrorMessageComponent, - DefectSelectComponent, - RequiredStandardSelectComponent, - FocusNextDirective, - TruncatePipe, - WeightsComponent, - LettersComponent, - PlatesComponent, - DimensionsComponent, - TrlBrakesComponent, - ReadOnlyComponent, - CustomDefectsComponent, - RequiredStandardComponent, - RequiredStandardsComponent, - CustomDefectComponent, - SwitchableInputComponent, - ReadOnlyComponent, - SuffixDirective, - AbandonDialogComponent, - BodyComponent, - TyresComponent, - PsvBrakesComponent, - PrefixDirective, - SuggestiveInputComponent, - CheckboxComponent, - ApprovalTypeComponent, - ApprovalTypeInputComponent, - ApprovalTypeFocusNextDirective, - ModifiedWeightsComponent, - FieldWarningMessageComponent, - AdrComponent, - AdrTankDetailsSubsequentInspectionsEditComponent, - AdrTankStatementUnNumberEditComponent, - CustomFormControlComponent, - AdrExaminerNotesHistoryEditComponent, - AdrExaminerNotesHistoryViewComponent, - AdrTankDetailsSubsequentInspectionsViewComponent, - AdrTankDetailsInitialInspectionViewComponent, - AdrTankStatementUnNumberViewComponent, - AdrCertificateHistoryComponent, - AdrTankDetailsM145ViewComponent, - ContingencyAdrGenerateCertComponent, - AdrNewCertificateRequiredViewComponent, - ], - imports: [CommonModule, FormsModule, ReactiveFormsModule, SharedModule, RouterModule], - exports: [ - TextInputComponent, - ViewListItemComponent, - DynamicFormGroupComponent, - ViewCombinationComponent, - CheckboxGroupComponent, - RadioGroupComponent, - DefectComponent, - DefectsComponent, - AutocompleteComponent, - NumberInputComponent, - TextAreaComponent, - DateComponent, - SelectComponent, - DynamicFormFieldComponent, - FieldErrorMessageComponent, - DefectSelectComponent, - RequiredStandardSelectComponent, - WeightsComponent, - LettersComponent, - PlatesComponent, - TyresComponent, - DimensionsComponent, - TrlBrakesComponent, - ReadOnlyComponent, - RequiredStandardComponent, - RequiredStandardsComponent, - CustomDefectsComponent, - CustomDefectComponent, - SwitchableInputComponent, - SuffixDirective, - ReadOnlyComponent, - AbandonDialogComponent, - BodyComponent, - PsvBrakesComponent, - PrefixDirective, - SuggestiveInputComponent, - CheckboxComponent, - ToUppercaseDirective, - NoSpaceDirective, - TrimWhitespaceDirective, - ApprovalTypeComponent, - ApprovalTypeInputComponent, - ApprovalTypeFocusNextDirective, - ModifiedWeightsComponent, - AdrComponent, - AdrCertificateHistoryComponent, - FieldWarningMessageComponent, - ], + declarations: [ + BaseControlComponent, + TextInputComponent, + ViewListItemComponent, + DynamicFormGroupComponent, + ViewCombinationComponent, + CheckboxGroupComponent, + RadioGroupComponent, + DefectComponent, + DefectsComponent, + AutocompleteComponent, + NumberInputComponent, + TextAreaComponent, + NumberOnlyDirective, + ToUppercaseDirective, + NoSpaceDirective, + TrimWhitespaceDirective, + DateComponent, + SelectComponent, + DynamicFormFieldComponent, + FieldErrorMessageComponent, + DefectSelectComponent, + RequiredStandardSelectComponent, + FocusNextDirective, + TruncatePipe, + WeightsComponent, + LettersComponent, + PlatesComponent, + DimensionsComponent, + TrlBrakesComponent, + ReadOnlyComponent, + CustomDefectsComponent, + RequiredStandardComponent, + RequiredStandardsComponent, + CustomDefectComponent, + SwitchableInputComponent, + ReadOnlyComponent, + SuffixDirective, + AbandonDialogComponent, + BodyComponent, + TyresComponent, + PsvBrakesComponent, + PrefixDirective, + SuggestiveInputComponent, + CheckboxComponent, + ApprovalTypeComponent, + ApprovalTypeInputComponent, + ApprovalTypeFocusNextDirective, + ModifiedWeightsComponent, + FieldWarningMessageComponent, + AdrComponent, + AdrTankDetailsSubsequentInspectionsEditComponent, + AdrTankStatementUnNumberEditComponent, + CustomFormControlComponent, + AdrExaminerNotesHistoryEditComponent, + AdrExaminerNotesHistoryViewComponent, + AdrTankDetailsSubsequentInspectionsViewComponent, + AdrTankDetailsInitialInspectionViewComponent, + AdrTankStatementUnNumberViewComponent, + AdrCertificateHistoryComponent, + AdrTankDetailsM145ViewComponent, + ContingencyAdrGenerateCertComponent, + AdrNewCertificateRequiredViewComponent, + ], + imports: [CommonModule, FormsModule, ReactiveFormsModule, SharedModule, RouterModule], + exports: [ + TextInputComponent, + ViewListItemComponent, + DynamicFormGroupComponent, + ViewCombinationComponent, + CheckboxGroupComponent, + RadioGroupComponent, + DefectComponent, + DefectsComponent, + AutocompleteComponent, + NumberInputComponent, + TextAreaComponent, + DateComponent, + SelectComponent, + DynamicFormFieldComponent, + FieldErrorMessageComponent, + DefectSelectComponent, + RequiredStandardSelectComponent, + WeightsComponent, + LettersComponent, + PlatesComponent, + TyresComponent, + DimensionsComponent, + TrlBrakesComponent, + ReadOnlyComponent, + RequiredStandardComponent, + RequiredStandardsComponent, + CustomDefectsComponent, + CustomDefectComponent, + SwitchableInputComponent, + SuffixDirective, + ReadOnlyComponent, + AbandonDialogComponent, + BodyComponent, + PsvBrakesComponent, + PrefixDirective, + SuggestiveInputComponent, + CheckboxComponent, + ToUppercaseDirective, + NoSpaceDirective, + TrimWhitespaceDirective, + ApprovalTypeComponent, + ApprovalTypeInputComponent, + ApprovalTypeFocusNextDirective, + ModifiedWeightsComponent, + AdrComponent, + AdrCertificateHistoryComponent, + FieldWarningMessageComponent, + ], }) -export class DynamicFormsModule { } +export class DynamicFormsModule {} diff --git a/src/app/forms/models/async-validators.enum.ts b/src/app/forms/models/async-validators.enum.ts index 4232bbe6ff..98f51b08b3 100644 --- a/src/app/forms/models/async-validators.enum.ts +++ b/src/app/forms/models/async-validators.enum.ts @@ -1,13 +1,14 @@ export enum AsyncValidatorNames { - ResultDependantOnCustomDefects = 'resultDependantOnCustomDefects', - ResultDependantOnRequiredStandards = 'resultDependantOnRequiredStandards', - UpdateTestStationDetails = 'updateTestStationDetails', - UpdateTesterDetails = 'updateTesterDetails', - RequiredIfNotFail = 'requiredIfNotfail', - RequiredIfNotAbandoned = 'requiredIfNotabandoned', - RequiredIfNotResultAndSiblingEquals = 'requiredIfNotResultAndSiblingEquals', - RequiredIfNotResult = 'requiredIfNotResult', - HideIfEqualsWithCondition = 'hideIfEqualsWithCondition', - PassResultDependantOnCustomDefects = 'passResultDependantOnCustomDefects', - RequiredWhenCarryingDangerousGoods = 'requiredWhenCarryingDangerousGoods', + ResultDependantOnCustomDefects = 'resultDependantOnCustomDefects', + ResultDependantOnRequiredStandards = 'resultDependantOnRequiredStandards', + UpdateTestStationDetails = 'updateTestStationDetails', + UpdateTesterDetails = 'updateTesterDetails', + RequiredIfNotFail = 'requiredIfNotfail', + RequiredIfNotAbandoned = 'requiredIfNotabandoned', + RequiredIfNotResultAndSiblingEquals = 'requiredIfNotResultAndSiblingEquals', + RequiredIfNotResult = 'requiredIfNotResult', + HideIfEqualsWithCondition = 'hideIfEqualsWithCondition', + PassResultDependantOnCustomDefects = 'passResultDependantOnCustomDefects', + RequiredWhenCarryingDangerousGoods = 'requiredWhenCarryingDangerousGoods', + Custom = 'custom', } diff --git a/src/app/forms/models/condition.model.ts b/src/app/forms/models/condition.model.ts index 8f491028b6..4908893c8d 100644 --- a/src/app/forms/models/condition.model.ts +++ b/src/app/forms/models/condition.model.ts @@ -1,10 +1,10 @@ export enum operatorEnum { - Equals = 'equals', - NotEquals = 'not equals', + Equals = 'equals', + NotEquals = 'not equals', } export interface Condition { - field: string; - operator: operatorEnum; - value: unknown; + field: string; + operator: operatorEnum; + value: unknown; } diff --git a/src/app/forms/models/options.model.ts b/src/app/forms/models/options.model.ts index 75b764014c..055ef6adab 100644 --- a/src/app/forms/models/options.model.ts +++ b/src/app/forms/models/options.model.ts @@ -1,5 +1,5 @@ export interface MultiOption { - label: string; - value: string | number | boolean; + label: string; + value: string | number | boolean; } export type MultiOptions = Array; diff --git a/src/app/forms/models/plateRequiredFields.model.ts b/src/app/forms/models/plateRequiredFields.model.ts index f1162de17a..0a6fedebad 100644 --- a/src/app/forms/models/plateRequiredFields.model.ts +++ b/src/app/forms/models/plateRequiredFields.model.ts @@ -1,43 +1,40 @@ export const hgvRequiredFields: string[] = [ - 'primaryVrm', - 'vin', - 'techRecord_brakes_dtpNumber', - 'techRecord_regnDate', - 'techRecord_manufactureYear', - 'techRecord_speedLimiterMrk', - 'techRecord_variantNumber', - 'techRecord_make', - 'techRecord_model', - 'techRecord_functionCode', - 'techRecord_dimensions_length', - 'techRecord_dimensions_width', - 'techRecord_tyreUseCode', - 'techRecord_axles', - 'techRecord_roadFriendly', - 'techRecord_vehicleConfiguration', + 'primaryVrm', + 'vin', + 'techRecord_brakes_dtpNumber', + 'techRecord_regnDate', + 'techRecord_manufactureYear', + 'techRecord_speedLimiterMrk', + 'techRecord_variantNumber', + 'techRecord_make', + 'techRecord_model', + 'techRecord_functionCode', + 'techRecord_dimensions_length', + 'techRecord_dimensions_width', + 'techRecord_tyreUseCode', + 'techRecord_axles', + 'techRecord_roadFriendly', + 'techRecord_vehicleConfiguration', ]; -export const tyreRequiredFields: string[] = [ - 'tyres_tyreSize', - 'tyres_fitmentCode', -]; +export const tyreRequiredFields: string[] = ['tyres_tyreSize', 'tyres_fitmentCode']; export const trlRequiredFields: string[] = [ - 'trailerId', - 'vin', - 'techRecord_brakes_dtpNumber', - 'techRecord_manufactureYear', - 'techRecord_maxLoadOnCoupling', - 'techRecord_variantNumber', - 'techRecord_make', - 'techRecord_model', - 'techRecord_functionCode', - 'techRecord_couplingCenterToRearTrlMax', - 'techRecord_couplingCenterToRearTrlMin', - 'techRecord_dimensions_length', - 'techRecord_dimensions_width', - 'techRecord_tyreUseCode', - 'techRecord_axles', - 'techRecord_roadFriendly', - 'techRecord_vehicleConfiguration', + 'trailerId', + 'vin', + 'techRecord_brakes_dtpNumber', + 'techRecord_manufactureYear', + 'techRecord_maxLoadOnCoupling', + 'techRecord_variantNumber', + 'techRecord_make', + 'techRecord_model', + 'techRecord_functionCode', + 'techRecord_couplingCenterToRearTrlMax', + 'techRecord_couplingCenterToRearTrlMin', + 'techRecord_dimensions_length', + 'techRecord_dimensions_width', + 'techRecord_tyreUseCode', + 'techRecord_axles', + 'techRecord_roadFriendly', + 'techRecord_vehicleConfiguration', ]; diff --git a/src/app/forms/models/testTypeId.enum.ts b/src/app/forms/models/testTypeId.enum.ts index a7d5d8ab96..c445ba05b5 100644 --- a/src/app/forms/models/testTypeId.enum.ts +++ b/src/app/forms/models/testTypeId.enum.ts @@ -13,27 +13,31 @@ export const TEST_TYPES_GROUP2: string[] = ['15', '16', '23', '19', '22']; // 38 through 36 - tests for PSV - Notifiable alteration check, voluntary brake test, voluntary multi check, voluntary speed limiter check // voluntary smoke test, voluntary headlamp aim test, vitesse 100 replacement, vitesse 100 application, voluntary tempo 100 // 86 through 90 - tests for HGV - voluntary multi-check, voluntary speed limiter check, voluntary smoke and headlamp aim test -// 87 through 85 - tests for HGV and TRL - voluntary shaker plate check, Free/Paid notifiable alteration, voluntary break test +// NOTE: the 47, and 48 from group 8 are excluded and use there own template to enable issuing documents centrally export const TEST_TYPES_GROUP3_4_8: string[] = [ - '38', - '30', - '33', - '34', - '32', - '31', - '100', - '121', - '36', - '86', - '88', - '89', - '90', - '87', - '47', - '48', - '85', + '38', + '30', + '33', + '34', + '32', + '31', + '100', + '121', + '36', + '86', + '88', + '89', + '90', + '87', + '85', ]; +// 47 - free notifiable alteration (HGV/TRL), 48 - paid notifiable alteration (HGV/TRL) +export const TEST_TYPES_GROUP8_NOTIFABLE = ['47', '48']; + +// 87 through 85 - tests for HGV and TRL - voluntary shaker plate check, Free/Paid notifiable alteration, voluntary break test +export const TEST_TYPES_GROUP8_VOLUNTARY = ['85', '87']; + // 56 and 49 - tests for HGV and TRL - Paid TIR retest, TIR test // 57 - test for TRL - Free TIR retest export const TEST_TYPES_GROUP5_13: string[] = ['56', '49', '57']; @@ -45,34 +49,27 @@ export const TEST_TYPES_GROUP6_11: string[] = ['62', '63', '122', '101', '91']; // ADR tests for HGV and TRL export const TEST_TYPES_GROUP7: string[] = ['59', '60', '50']; -// tests for HGV and TRL - Annual tests, First tests, Annual retests, Paid/Part paid prohibition clearance +// tests for HGV and TRL - Annual tests, Annual retests, Paid/Part paid prohibition clearance on annual test export const TEST_TYPES_GROUP9_10: string[] = [ - '76', - '95', - '94', - '53', - '54', - '65', - '66', - '70', - '79', - '82', - '83', - '41', - '40', - '98', - '99', - '103', - '104', - '67', - '107', - '113', - '116', - '119', - '120', - '199', + '76', + '94', + '53', + '54', + '70', + '79', + '40', + '98', + '99', + '67', + '107', + '113', + '116', + '199', ]; +// tests for HGV and TRL - First tests, Paid/Part paid prohibition clearance on first test +export const TEST_TYPES_GROUP9_10_CENTRAL_DOCS = ['95', '41', '65', '103', '66', '104', '82', '119', '83', '120']; + // tests for TRL - Paid/Part paid prohibition clearance(retest, full inspection, part inspection, without cert) export const TEST_TYPES_GROUP12_14: string[] = ['117', '108', '109', '110', '114', '71', '72', '73', '77', '80']; @@ -83,48 +80,55 @@ export const TEST_TYPES_GROUP15_16: string[] = ['39', '201', '45', '44']; // Test/Retest - Free/Paid - IVA inspection, MSVA inspection export const TEST_TYPES_GROUP1_SPEC_TEST: string[] = [ - '125', - '126', - '186', - '187', - '128', - '188', - '189', - '129', - '130', - '133', - '134', - '135', - '136', - '138', - '139', - '140', - '150', - '151', - '158', - '159', - '161', - '192', - '193', - '162', - '194', - '195', - '163', - '166', - '167', - '169', - '170', - '172', - '173', - '181', - '182', + '125', + '126', + '186', + '187', + '128', + '188', + '189', + '129', + '130', + '133', + '134', + '135', + '136', + '138', + '139', + '140', + '158', + '159', + '161', + '192', + '193', + '162', + '194', + '195', + '163', + '166', + '167', + '169', + '170', + '172', + '173', ]; // Test/Retest COIF with annual test, Seatbelt installation check COIF with annual test export const TEST_TYPES_GROUP2_SPEC_TEST: string[] = ['142', '146', '175', '177']; // Test/Retest COIF without annual test, Type approved to bus directive COIF, Annex 7 COIF, TILT COIF retest -export const TEST_TYPES_GROUP3_SPEC_TEST: string[] = ['143', '144', '148', '176', '178', '179']; +export const TEST_TYPES_GROUP3_SPEC_TEST: string[] = [ + '143', + '144', + '148', + '176', + '178', + '179', + '150', + '151', + '181', + '182', +]; // Test Seatbelt installation check COIF without annual test export const TEST_TYPES_GROUP4_SPEC_TEST: string[] = ['147']; @@ -133,102 +137,116 @@ export const TEST_TYPES_GROUP4_SPEC_TEST: string[] = ['147']; export const TEST_TYPES_GROUP5_SPEC_TEST: string[] = ['153', '190', '191', '154', '184', '196', '197', '185']; export const SPECIALIST_TEST_TYPE_IDS: string[] = [ - '125', - '126', - '186', - '187', - '128', - '188', - '189', - '129', - '130', - '133', - '134', - '135', - '136', - '138', - '139', - '140', - '150', - '151', - '158', - '159', - '161', - '192', - '193', - '162', - '194', - '195', - '163', - '166', - '167', - '169', - '170', - '172', - '173', - '181', - '182', - '142', - '146', - '175', - '177', - '143', - '144', - '148', - '176', - '178', - '179', - '147', - '153', - '190', - '191', - '154', - '184', - '196', - '197', - '185', + '125', + '126', + '186', + '187', + '128', + '188', + '189', + '129', + '130', + '133', + '134', + '135', + '136', + '138', + '139', + '140', + '150', + '151', + '158', + '159', + '161', + '192', + '193', + '162', + '194', + '195', + '163', + '166', + '167', + '169', + '170', + '172', + '173', + '181', + '182', + '142', + '146', + '175', + '177', + '143', + '144', + '148', + '176', + '178', + '179', + '147', + '153', + '190', + '191', + '154', + '184', + '196', + '197', + '185', ]; export const TEST_TYPES_GROUP1_DESK_BASED_TEST: string[] = ['417', '418']; export const TEST_TYPES_GROUP2_DESK_BASED_TEST: string[] = ['403', '404', '415']; export const TEST_TYPES_GROUP3_DESK_BASED_TEST: string[] = [ - '407', - '408', - '414', - '420', - '426', - '431', - '432', - '443', - '444', - '445', - '446', - '447', - '448', + '407', + '408', + '414', + '420', + '426', + '431', + '432', + '443', + '444', + '445', + '446', + '447', + '448', +]; +export const TEST_TYPES_GROUP4_DESK_BASED_TEST: string[] = [ + '409', + '411', + '412', + '423', + '424', + '425', + '433', + '435', + '436', + '437', + '438', ]; -export const TEST_TYPES_GROUP4_DESK_BASED_TEST: string[] = ['409', '411', '412', '423', '424', '425', '433', '435', '436', '437', '438']; export const TEST_TYPES_GROUP5_DESK_BASED_TEST: string[] = ['439', '441', '442', '449']; export const TEST_TYPES = { - testTypesGroup1: TEST_TYPES_GROUP1, - testTypesGroup2: TEST_TYPES_GROUP2, - testTypesGroup3And4And8: TEST_TYPES_GROUP3_4_8, - testTypesGroup7: TEST_TYPES_GROUP7, - testTypesGroup9And10: TEST_TYPES_GROUP9_10, - testTypesGroup6And11: TEST_TYPES_GROUP6_11, - testTypesGroup12And14: TEST_TYPES_GROUP12_14, - testTypesGroup5And13: TEST_TYPES_GROUP5_13, - testTypesGroup15And16: TEST_TYPES_GROUP15_16, - testTypesSpecialistGroup1: TEST_TYPES_GROUP1_SPEC_TEST, - testTypesSpecialistGroup2: TEST_TYPES_GROUP2_SPEC_TEST, - testTypesSpecialistGroup3: TEST_TYPES_GROUP3_SPEC_TEST, - testTypesSpecialistGroup4: TEST_TYPES_GROUP4_SPEC_TEST, - testTypesSpecialistGroup5: TEST_TYPES_GROUP5_SPEC_TEST, - testTypesDeskBasedGroup1: TEST_TYPES_GROUP1_DESK_BASED_TEST, - testTypesDeskBasedGroup2: TEST_TYPES_GROUP2_DESK_BASED_TEST, - testTypesDeskBasedGroup3: TEST_TYPES_GROUP3_DESK_BASED_TEST, - testTypesDeskBasedGroup4: TEST_TYPES_GROUP4_DESK_BASED_TEST, - testTypesDeskBasedGroup5: TEST_TYPES_GROUP5_DESK_BASED_TEST, - testTypesSpecialistGroup1OldIVAorMSVA: TEST_TYPES_GROUP1_SPEC_TEST, - testTypesSpecialistGroup5OldIVAorMSVA: TEST_TYPES_GROUP5_SPEC_TEST, + testTypesGroup1: TEST_TYPES_GROUP1, + testTypesGroup2: TEST_TYPES_GROUP2, + testTypesGroup3And4And8: TEST_TYPES_GROUP3_4_8, + testTypesGroup7: TEST_TYPES_GROUP7, + testTypesGroup8Notifiable: TEST_TYPES_GROUP8_NOTIFABLE, + testTypesGroup9And10: TEST_TYPES_GROUP9_10, + testTypesGroup9And10CentralDocs: TEST_TYPES_GROUP9_10_CENTRAL_DOCS, + testTypesGroup6And11: TEST_TYPES_GROUP6_11, + testTypesGroup12And14: TEST_TYPES_GROUP12_14, + testTypesGroup5And13: TEST_TYPES_GROUP5_13, + testTypesGroup15And16: TEST_TYPES_GROUP15_16, + testTypesSpecialistGroup1: TEST_TYPES_GROUP1_SPEC_TEST, + testTypesSpecialistGroup2: TEST_TYPES_GROUP2_SPEC_TEST, + testTypesSpecialistGroup3: TEST_TYPES_GROUP3_SPEC_TEST, + testTypesSpecialistGroup4: TEST_TYPES_GROUP4_SPEC_TEST, + testTypesSpecialistGroup5: TEST_TYPES_GROUP5_SPEC_TEST, + testTypesDeskBasedGroup1: TEST_TYPES_GROUP1_DESK_BASED_TEST, + testTypesDeskBasedGroup2: TEST_TYPES_GROUP2_DESK_BASED_TEST, + testTypesDeskBasedGroup3: TEST_TYPES_GROUP3_DESK_BASED_TEST, + testTypesDeskBasedGroup4: TEST_TYPES_GROUP4_DESK_BASED_TEST, + testTypesDeskBasedGroup5: TEST_TYPES_GROUP5_DESK_BASED_TEST, + testTypesSpecialistGroup1OldIVAorMSVA: TEST_TYPES_GROUP1_SPEC_TEST, + testTypesSpecialistGroup5OldIVAorMSVA: TEST_TYPES_GROUP5_SPEC_TEST, }; diff --git a/src/app/forms/models/validators.enum.ts b/src/app/forms/models/validators.enum.ts index d79b068524..a7c2148a05 100644 --- a/src/app/forms/models/validators.enum.ts +++ b/src/app/forms/models/validators.enum.ts @@ -1,49 +1,50 @@ export enum ValidatorNames { - CustomPattern = 'customPattern', - DisableIfEquals = 'disableIfEquals', - Email = 'email', - EnableIfEquals = 'enableIfEquals', - HideIfEmpty = 'hideIfEmpty', - HideIfNotEqual = 'hideIfNotEqual', - HideIfParentSiblingEqual = 'hideIfParentSiblingEqual', - HideIfParentSiblingNotEqual = 'hideIfParentSiblingNotEqual', - MaxLength = 'maxlength', - MinLength = 'minlength', - Max = 'max', - Min = 'min', - Alphanumeric = 'alphanumeric', - Numeric = 'numeric', - Pattern = 'pattern', - Required = 'required', - RequiredIfEquals = 'requiredIfEquals', - requiredIfAllEquals = 'requiredIfAllEquals', - RequiredIfNotHidden = 'requiredIfNotHidden', - RequiredIfNotEquals = 'requiredIfNotEquals', - Defined = 'defined', - ValidateDefectNotes = 'validateDefectNotes', - ValidateVRMTrailerIdLength = 'validateVRMTrailerIdLength', - PastDate = 'pastDate', - FutureDate = 'futureDate', - AheadOfDate = 'aheadOfDate', - DateNotExceed = 'dateNotExceed', - PastYear = 'pastYear', - CopyValueToRootControl = 'copyValueToRootControl', - ValidateProhibitionIssued = 'validateProhibitionIssued', - NotZNumber = 'notZNumber', - MustEqualSibling = 'mustEqualSibling', - HandlePsvPassengersChange = 'HandlePsvPassengersChange', - IsMemberOfEnum = 'isMemberOfEnum', - UpdateFunctionCode = 'updateFunctionCode', - ShowGroupsWhenEqualTo = 'showGroupsWhenEqualTo', - HideGroupsWhenEqualTo = 'hideGroupsWhenEqualTo', - ShowGroupsWhenIncludes = 'showGroupsWhenIncludes', - HideGroupsWhenIncludes = 'hideGroupsWhenIncludes', - ShowGroupsWhenExcludes = 'showGroupsWhenExcludes', - HideGroupsWhenExcludes = 'hideGroupsWhenExcludes', - AddWarningForAdrField = 'addWarningForAdrField', - IsArray = 'isArray', - Custom = 'custom', - Tc3TestValidator = 'tc3TestValidator', - DateIsInvalid = 'dateIsInvalid', - MinArrayLengthIfNotEmpty = 'minArrayLengthIfNotEmpty', + CustomPattern = 'customPattern', + DisableIfEquals = 'disableIfEquals', + Email = 'email', + EnableIfEquals = 'enableIfEquals', + HideIfEmpty = 'hideIfEmpty', + HideIfNotEqual = 'hideIfNotEqual', + HideIfParentSiblingEqual = 'hideIfParentSiblingEqual', + HideIfParentSiblingNotEqual = 'hideIfParentSiblingNotEqual', + MaxLength = 'maxlength', + MinLength = 'minlength', + Max = 'max', + Min = 'min', + Alphanumeric = 'alphanumeric', + Numeric = 'numeric', + Pattern = 'pattern', + Required = 'required', + RequiredIfEquals = 'requiredIfEquals', + requiredIfAllEquals = 'requiredIfAllEquals', + RequiredIfNotHidden = 'requiredIfNotHidden', + RequiredIfNotEquals = 'requiredIfNotEquals', + Defined = 'defined', + ValidateDefectNotes = 'validateDefectNotes', + ValidateVRMTrailerIdLength = 'validateVRMTrailerIdLength', + PastDate = 'pastDate', + FutureDate = 'futureDate', + AheadOfDate = 'aheadOfDate', + DateNotExceed = 'dateNotExceed', + PastYear = 'pastYear', + CopyValueToRootControl = 'copyValueToRootControl', + ValidateProhibitionIssued = 'validateProhibitionIssued', + NotZNumber = 'notZNumber', + MustEqualSibling = 'mustEqualSibling', + HandlePsvPassengersChange = 'HandlePsvPassengersChange', + IsMemberOfEnum = 'isMemberOfEnum', + UpdateFunctionCode = 'updateFunctionCode', + ShowGroupsWhenEqualTo = 'showGroupsWhenEqualTo', + HideGroupsWhenEqualTo = 'hideGroupsWhenEqualTo', + ShowGroupsWhenIncludes = 'showGroupsWhenIncludes', + HideGroupsWhenIncludes = 'hideGroupsWhenIncludes', + ShowGroupsWhenExcludes = 'showGroupsWhenExcludes', + HideGroupsWhenExcludes = 'hideGroupsWhenExcludes', + AddWarningForAdrField = 'addWarningForAdrField', + IsArray = 'isArray', + Custom = 'custom', + Tc3TestValidator = 'tc3TestValidator', + DateIsInvalid = 'dateIsInvalid', + MinArrayLengthIfNotEmpty = 'minArrayLengthIfNotEmpty', + IssueRequired = 'issueRequired', } diff --git a/src/app/forms/services/dynamic-form.service.spec.ts b/src/app/forms/services/dynamic-form.service.spec.ts index c02f9dce36..634d7b751d 100644 --- a/src/app/forms/services/dynamic-form.service.spec.ts +++ b/src/app/forms/services/dynamic-form.service.spec.ts @@ -1,8 +1,6 @@ import { HttpClientTestingModule } from '@angular/common/http/testing'; import { TestBed } from '@angular/core/testing'; -import { - AbstractControl, FormArray, ValidatorFn, Validators, -} from '@angular/forms'; +import { AbstractControl, FormArray, ValidatorFn, Validators } from '@angular/forms'; import { RouterTestingModule } from '@angular/router/testing'; import { GlobalError } from '@core/components/global-error/global-error.interface'; import { ValidatorNames } from '@forms/models/validators.enum'; @@ -10,244 +8,258 @@ import { provideMockStore } from '@ngrx/store/testing'; import { initialAppState } from '@store/.'; import { DynamicFormService } from './dynamic-form.service'; import { - CustomControl, CustomFormArray, CustomFormControl, CustomFormGroup, FormNode, FormNodeTypes, FormNodeViewTypes, + CustomControl, + CustomFormArray, + CustomFormControl, + CustomFormGroup, + FormNode, + FormNodeTypes, + FormNodeViewTypes, } from './dynamic-form.types'; describe('DynamicFormService', () => { - let service: DynamicFormService; - - beforeEach(() => { - TestBed.configureTestingModule({ - providers: [provideMockStore({ initialState: initialAppState })], - imports: [RouterTestingModule, HttpClientTestingModule], - }); - service = TestBed.inject(DynamicFormService); - }); - - it('should be created', () => { - expect(service).toBeTruthy(); - }); - - describe('createForm', () => { - it('should return an empty FormGroup if the root node has no children', () => { - const node: FormNode = { - name: 'empty', - type: FormNodeTypes.GROUP, - }; - - expect(service.createForm(node)).toMatchObject({}); - }); - it('should return a FormGroup containing a single control', () => { - const node: FormNode = { - name: 'group', - type: FormNodeTypes.GROUP, - children: [ - { - name: 'vin', - label: 'Vehicle Identification Number', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.STRING, - }, - ], - }; - - const outputGroup = service.createForm(node); - const children = node.children as FormNode[]; - - expect( - ( - outputGroup.controls as { - [key: string]: AbstractControl; - } - )[children[0].name], - ).toBeTruthy(); - }); - - it('should return a FormGroup mirroring the nested structure of the controls it was created with', () => { - const node: FormNode = { - name: 'group', - type: FormNodeTypes.GROUP, - children: [ - { - name: 'sub-group', - type: FormNodeTypes.GROUP, - children: [ - { - name: 'vin', - label: 'Vehicle Identification Number', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.STRING, - }, - ], - }, - ], - }; - - const children = node.children as FormNode[]; - const grandChildren = children[0].children as FormNode[]; - - const outputGroup = service.createForm(node); - const subGroup = ( - outputGroup.controls as { - [key: string]: AbstractControl; - } - )[children[0].name] as CustomFormGroup; - - expect(subGroup.controls[grandChildren[0].name]).toBeTruthy(); - }); - - it('should return a formGroup with a nested FormArray', () => { - const node: FormNode = { - name: 'group', - type: FormNodeTypes.GROUP, - children: [ - { - name: 'vins', - type: FormNodeTypes.ARRAY, - children: [ - { - name: '0', - label: 'Vehicle Identification Number', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.STRING, - }, - ], - }, - ], - }; - - const data = { - vins: ['123', '456'], - }; - - const outputGroup = service.createForm(node, data); - const formArray = outputGroup.get('vins'); - expect(formArray instanceof FormArray).toBeTruthy(); - expect((formArray as FormArray).controls).toHaveLength(2); - }); - - it('should return a formGroup with a nested FormArray with data given', () => { - const node: FormNode = { - name: 'group', - type: FormNodeTypes.GROUP, - children: [ - { - name: 'axelsArray', - type: FormNodeTypes.ARRAY, - children: [ - { - name: '0', - type: FormNodeTypes.GROUP, - children: [ - { - name: 'vin', - label: 'Vehicle Identification Number', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.STRING, - }, - ], - }, - ], - }, - ], - }; - - const data = { - axelsArray: [ - { - vin: '12345', - }, - { - vin: '78910', - }, - ], - }; - - const outputGroup = service.createForm(node, data); - const formArray = outputGroup.get('axelsArray'); - const subGroup = (formArray as CustomFormArray).controls; - - expect(subGroup).toHaveLength(2); - }); - - it('should return a formGroup with a nested FormArray of simple controls', () => { - const node: FormNode = { - name: 'group', - type: FormNodeTypes.GROUP, - children: [ - { - name: 'axelsArray', - type: FormNodeTypes.ARRAY, - children: [ - { - name: 'vin', - label: 'Vehicle Identification Number', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.STRING, - }, - ], - }, - ], - }; - - const data = { axelsArray: ['12345', '78910'] }; - - const outputGroup = service.createForm(node, data); - const formArray = outputGroup.get('axelsArray'); - const subGroup = (formArray as CustomFormArray).controls; - - expect(subGroup).toHaveLength(2); - }); - - it('should add correct validators', () => { - const node: FormNode = { - name: 'group', - type: FormNodeTypes.GROUP, - children: [ - { - name: 'foo', - type: FormNodeTypes.CONTROL, - validators: [{ name: 'required' }], - }, - ], - }; - - const outputGroup = service.createForm(node); - const control = outputGroup.get('foo'); - expect(control instanceof CustomFormControl).toBeTruthy(); - expect(control?.hasValidator(Validators.required)).toBeTruthy(); - }); - }); - - describe('addValidators', () => { - it('should add validators', () => { - const control: CustomControl = new CustomFormControl({ name: 'testControl', type: FormNodeTypes.CONTROL, children: [] }); - const validators = [{ name: ValidatorNames.Required }]; - const expectedValidator: ValidatorFn = Validators.required; - service.addValidators(control, validators); - expect(control.hasValidator(expectedValidator)).toBeTruthy(); - }); - }); - - describe('static validate functions', () => { - it('should return a list of global errors for invalid controls', () => { - const errors: GlobalError[] = []; - const form = new CustomFormGroup( - { name: 'group', type: FormNodeTypes.GROUP }, - { - foo: new CustomFormControl({ name: 'foo', type: FormNodeTypes.CONTROL }, '', { validators: [Validators.required] }), - bar: new CustomFormGroup( - { name: 'innerGroup', type: FormNodeTypes.GROUP }, - { - baz: new CustomFormControl({ name: 'baz', type: FormNodeTypes.CONTROL }, '', { validators: [Validators.required] }), - }, - ), - }, - ); - - DynamicFormService.validate(form, errors); - - expect(errors).toHaveLength(2); - }); - }); + let service: DynamicFormService; + + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [provideMockStore({ initialState: initialAppState })], + imports: [RouterTestingModule, HttpClientTestingModule], + }); + service = TestBed.inject(DynamicFormService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); + + describe('createForm', () => { + it('should return an empty FormGroup if the root node has no children', () => { + const node: FormNode = { + name: 'empty', + type: FormNodeTypes.GROUP, + }; + + expect(service.createForm(node)).toMatchObject({}); + }); + it('should return a FormGroup containing a single control', () => { + const node: FormNode = { + name: 'group', + type: FormNodeTypes.GROUP, + children: [ + { + name: 'vin', + label: 'Vehicle Identification Number', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.STRING, + }, + ], + }; + + const outputGroup = service.createForm(node); + const children = node.children as FormNode[]; + + expect( + ( + outputGroup.controls as { + [key: string]: AbstractControl; + } + )[children[0].name] + ).toBeTruthy(); + }); + + it('should return a FormGroup mirroring the nested structure of the controls it was created with', () => { + const node: FormNode = { + name: 'group', + type: FormNodeTypes.GROUP, + children: [ + { + name: 'sub-group', + type: FormNodeTypes.GROUP, + children: [ + { + name: 'vin', + label: 'Vehicle Identification Number', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.STRING, + }, + ], + }, + ], + }; + + const children = node.children as FormNode[]; + const grandChildren = children[0].children as FormNode[]; + + const outputGroup = service.createForm(node); + const subGroup = ( + outputGroup.controls as { + [key: string]: AbstractControl; + } + )[children[0].name] as CustomFormGroup; + + expect(subGroup.controls[grandChildren[0].name]).toBeTruthy(); + }); + + it('should return a formGroup with a nested FormArray', () => { + const node: FormNode = { + name: 'group', + type: FormNodeTypes.GROUP, + children: [ + { + name: 'vins', + type: FormNodeTypes.ARRAY, + children: [ + { + name: '0', + label: 'Vehicle Identification Number', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.STRING, + }, + ], + }, + ], + }; + + const data = { + vins: ['123', '456'], + }; + + const outputGroup = service.createForm(node, data); + const formArray = outputGroup.get('vins'); + expect(formArray instanceof FormArray).toBeTruthy(); + expect((formArray as FormArray).controls).toHaveLength(2); + }); + + it('should return a formGroup with a nested FormArray with data given', () => { + const node: FormNode = { + name: 'group', + type: FormNodeTypes.GROUP, + children: [ + { + name: 'axelsArray', + type: FormNodeTypes.ARRAY, + children: [ + { + name: '0', + type: FormNodeTypes.GROUP, + children: [ + { + name: 'vin', + label: 'Vehicle Identification Number', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.STRING, + }, + ], + }, + ], + }, + ], + }; + + const data = { + axelsArray: [ + { + vin: '12345', + }, + { + vin: '78910', + }, + ], + }; + + const outputGroup = service.createForm(node, data); + const formArray = outputGroup.get('axelsArray'); + const subGroup = (formArray as CustomFormArray).controls; + + expect(subGroup).toHaveLength(2); + }); + + it('should return a formGroup with a nested FormArray of simple controls', () => { + const node: FormNode = { + name: 'group', + type: FormNodeTypes.GROUP, + children: [ + { + name: 'axelsArray', + type: FormNodeTypes.ARRAY, + children: [ + { + name: 'vin', + label: 'Vehicle Identification Number', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.STRING, + }, + ], + }, + ], + }; + + const data = { axelsArray: ['12345', '78910'] }; + + const outputGroup = service.createForm(node, data); + const formArray = outputGroup.get('axelsArray'); + const subGroup = (formArray as CustomFormArray).controls; + + expect(subGroup).toHaveLength(2); + }); + + it('should add correct validators', () => { + const node: FormNode = { + name: 'group', + type: FormNodeTypes.GROUP, + children: [ + { + name: 'foo', + type: FormNodeTypes.CONTROL, + validators: [{ name: 'required' }], + }, + ], + }; + + const outputGroup = service.createForm(node); + const control = outputGroup.get('foo'); + expect(control instanceof CustomFormControl).toBeTruthy(); + expect(control?.hasValidator(Validators.required)).toBeTruthy(); + }); + }); + + describe('addValidators', () => { + it('should add validators', () => { + const control: CustomControl = new CustomFormControl({ + name: 'testControl', + type: FormNodeTypes.CONTROL, + children: [], + }); + const validators = [{ name: ValidatorNames.Required }]; + const expectedValidator: ValidatorFn = Validators.required; + service.addValidators(control, validators); + expect(control.hasValidator(expectedValidator)).toBeTruthy(); + }); + }); + + describe('static validate functions', () => { + it('should return a list of global errors for invalid controls', () => { + const errors: GlobalError[] = []; + const form = new CustomFormGroup( + { name: 'group', type: FormNodeTypes.GROUP }, + { + foo: new CustomFormControl({ name: 'foo', type: FormNodeTypes.CONTROL }, '', { + validators: [Validators.required], + }), + bar: new CustomFormGroup( + { name: 'innerGroup', type: FormNodeTypes.GROUP }, + { + baz: new CustomFormControl({ name: 'baz', type: FormNodeTypes.CONTROL }, '', { + validators: [Validators.required], + }), + } + ), + } + ); + + DynamicFormService.validate(form, errors); + + expect(errors).toHaveLength(2); + }); + }); }); diff --git a/src/app/forms/services/dynamic-form.service.ts b/src/app/forms/services/dynamic-form.service.ts index 719ca039cc..de464f6635 100644 --- a/src/app/forms/services/dynamic-form.service.ts +++ b/src/app/forms/services/dynamic-form.service.ts @@ -1,8 +1,5 @@ import { Injectable } from '@angular/core'; -import { - AsyncValidatorFn, FormArray, FormControl, FormGroup, - ValidatorFn, Validators, -} from '@angular/forms'; +import { AsyncValidatorFn, FormArray, FormControl, FormGroup, ValidatorFn, Validators } from '@angular/forms'; import { GlobalError } from '@core/components/global-error/global-error.interface'; import { AsyncValidatorNames } from '@forms/models/async-validators.enum'; import { Condition } from '@forms/models/condition.model'; @@ -15,224 +12,251 @@ import { DefectValidators } from '@forms/validators/defects/defect.validators'; import { resultOfTestEnum } from '@models/test-types/test-type.model'; import { Store } from '@ngrx/store'; import { State } from '@store/index'; -import { - CustomFormArray, CustomFormControl, CustomFormGroup, FormNode, FormNodeTypes, -} from './dynamic-form.types'; +import { CustomFormArray, CustomFormControl, CustomFormGroup, FormNode, FormNodeTypes } from './dynamic-form.types'; type CustomFormFields = CustomFormControl | CustomFormArray | CustomFormGroup; @Injectable({ - providedIn: 'root', + providedIn: 'root', }) export class DynamicFormService { - constructor(private store: Store) { } - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - validatorMap: Record ValidatorFn> = { - [ValidatorNames.AheadOfDate]: (arg: string) => CustomValidators.aheadOfDate(arg), - [ValidatorNames.Alphanumeric]: () => CustomValidators.alphanumeric(), - [ValidatorNames.Email]: () => CustomValidators.email(), - [ValidatorNames.CopyValueToRootControl]: (arg: string) => CustomValidators.copyValueToRootControl(arg), - [ValidatorNames.CustomPattern]: (args: string[]) => CustomValidators.customPattern([...args]), - [ValidatorNames.DateNotExceed]: (args: { sibling: string; months: number }) => CustomValidators.dateNotExceed(args.sibling, args.months), - [ValidatorNames.Defined]: () => CustomValidators.defined(), - [ValidatorNames.DisableIfEquals]: (args: { sibling: string; value: unknown }) => CustomValidators.disableIfEquals(args.sibling, args.value), - [ValidatorNames.EnableIfEquals]: (args: { sibling: string; value: unknown }) => CustomValidators.enableIfEquals(args.sibling, args.value), - [ValidatorNames.FutureDate]: () => CustomValidators.futureDate, - [ValidatorNames.PastYear]: () => CustomValidators.pastYear, - [ValidatorNames.HideIfEmpty]: (args: string) => CustomValidators.hideIfEmpty(args), - [ValidatorNames.HideIfNotEqual]: (args: { sibling: string; value: unknown }) => CustomValidators.hideIfNotEqual(args.sibling, args.value), - [ValidatorNames.HideIfParentSiblingEqual]: (args: { sibling: string; value: unknown }) => - CustomValidators.hideIfParentSiblingEquals(args.sibling, args.value), - [ValidatorNames.HideIfParentSiblingNotEqual]: (args: { sibling: string; value: unknown }) => - CustomValidators.hideIfParentSiblingNotEqual(args.sibling, args.value), - [ValidatorNames.Max]: (args: number) => Validators.max(args), - [ValidatorNames.MaxLength]: (args: number) => Validators.maxLength(args), - [ValidatorNames.Min]: (args: number) => Validators.min(args), - [ValidatorNames.MinLength]: (args: number) => Validators.minLength(args), - [ValidatorNames.NotZNumber]: () => CustomValidators.notZNumber, - [ValidatorNames.Numeric]: () => CustomValidators.numeric(), - [ValidatorNames.PastDate]: () => CustomValidators.pastDate, - [ValidatorNames.Pattern]: (args: string) => Validators.pattern(args), - [ValidatorNames.Required]: () => Validators.required, - [ValidatorNames.RequiredIfEquals]: (args: { sibling: string; value: unknown[] }) => CustomValidators.requiredIfEquals(args.sibling, args.value), - [ValidatorNames.requiredIfAllEquals]: (args: { sibling: string; value: unknown[] }) => - CustomValidators.requiredIfAllEquals(args.sibling, args.value), - [ValidatorNames.RequiredIfNotEquals]: (args: { sibling: string; value: unknown[] }) => - CustomValidators.requiredIfNotEquals(args.sibling, args.value), - [ValidatorNames.ValidateVRMTrailerIdLength]: (args: { sibling: string }) => CustomValidators.validateVRMTrailerIdLength(args.sibling), - [ValidatorNames.ValidateDefectNotes]: () => DefectValidators.validateDefectNotes, - [ValidatorNames.ValidateProhibitionIssued]: () => DefectValidators.validateProhibitionIssued, - [ValidatorNames.MustEqualSibling]: (args: { sibling: string }) => CustomValidators.mustEqualSibling(args.sibling), - [ValidatorNames.HandlePsvPassengersChange]: (args: { passengersOne: string; passengersTwo: string }) => - CustomValidators.handlePsvPassengersChange(args.passengersOne, args.passengersTwo), - [ValidatorNames.IsMemberOfEnum]: (args: { enum: Record; options?: Partial }) => - CustomValidators.isMemberOfEnum(args.enum, args.options), - [ValidatorNames.UpdateFunctionCode]: () => CustomValidators.updateFunctionCode(), - [ValidatorNames.ShowGroupsWhenEqualTo]: (args: { values: unknown[], groups: string[] }) => - CustomValidators.showGroupsWhenEqualTo(args.values, args.groups), - [ValidatorNames.HideGroupsWhenEqualTo]: (args: { values: unknown[], groups: string[] }) => - CustomValidators.hideGroupsWhenEqualTo(args.values, args.groups), - [ValidatorNames.ShowGroupsWhenIncludes]: (args: { values: unknown[], groups: string[] }) => - CustomValidators.showGroupsWhenIncludes(args.values, args.groups), - [ValidatorNames.HideGroupsWhenIncludes]: (args: { values: unknown[], groups: string[] }) => - CustomValidators.hideGroupsWhenIncludes(args.values, args.groups), - [ValidatorNames.ShowGroupsWhenExcludes]: (args: { values: unknown[], groups: string[] }) => - CustomValidators.showGroupsWhenExcludes(args.values, args.groups), - [ValidatorNames.HideGroupsWhenExcludes]: (args: { values: unknown[], groups: string[] }) => - CustomValidators.hideGroupsWhenExcludes(args.values, args.groups), - [ValidatorNames.AddWarningForAdrField]: (warning: string) => CustomValidators.addWarningForAdrField(warning), - [ValidatorNames.IsArray]: (args: Partial) => CustomValidators.isArray(args), - [ValidatorNames.Custom]: (...args) => CustomValidators.custom(...args), - [ValidatorNames.Tc3TestValidator]: (args: { inspectionNumber: number }) => CustomValidators.tc3TestValidator(args), - [ValidatorNames.RequiredIfNotHidden]: () => CustomValidators.requiredIfNotHidden(), - [ValidatorNames.DateIsInvalid]: () => CustomValidators.dateIsInvalid, - [ValidatorNames.MinArrayLengthIfNotEmpty]: (args: { minimumLength: number, message: string }) => - CustomValidators.minArrayLengthIfNotEmpty(args.minimumLength, args.message), - }; - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - asyncValidatorMap: Record AsyncValidatorFn> = { - [AsyncValidatorNames.HideIfEqualsWithCondition]: (args: { sibling: string; value: string; conditions: Condition | Condition[] }) => - CustomAsyncValidators.hideIfEqualsWithCondition(this.store, args.sibling, args.value, args.conditions), - [AsyncValidatorNames.PassResultDependantOnCustomDefects]: () => CustomAsyncValidators.passResultDependantOnCustomDefects(this.store), - [AsyncValidatorNames.RequiredIfNotAbandoned]: () => CustomAsyncValidators.requiredIfNotAbandoned(this.store), - [AsyncValidatorNames.RequiredIfNotFail]: () => CustomAsyncValidators.requiredIfNotFail(this.store), - [AsyncValidatorNames.RequiredIfNotResult]: (args: { testResult: resultOfTestEnum | resultOfTestEnum[] }) => - CustomAsyncValidators.requiredIfNotResult(this.store, args.testResult), - [AsyncValidatorNames.RequiredIfNotResultAndSiblingEquals]: (args: { - testResult: resultOfTestEnum | resultOfTestEnum[]; sibling: string; value: unknown - }) => CustomAsyncValidators.requiredIfNotResultAndSiblingEquals(this.store, args.testResult, args.sibling, args.value), - [AsyncValidatorNames.ResultDependantOnCustomDefects]: () => CustomAsyncValidators.resultDependantOnCustomDefects(this.store), - [AsyncValidatorNames.ResultDependantOnRequiredStandards]: () => CustomAsyncValidators.resultDependantOnRequiredStandards(this.store), - [AsyncValidatorNames.UpdateTesterDetails]: () => CustomAsyncValidators.updateTesterDetails(this.store), - [AsyncValidatorNames.UpdateTestStationDetails]: () => CustomAsyncValidators.updateTestStationDetails(this.store), - [AsyncValidatorNames.RequiredWhenCarryingDangerousGoods]: () => CustomAsyncValidators.requiredWhenCarryingDangerousGoods(this.store), - }; - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - createForm(formNode: FormNode, data?: any): CustomFormGroup | CustomFormArray { - if (!formNode) { - return new CustomFormGroup(formNode, {}); - } - - const form: CustomFormGroup | CustomFormArray = formNode.type === FormNodeTypes.ARRAY - ? new CustomFormArray(formNode, [], this.store) - : new CustomFormGroup(formNode, {}); - - data = data ?? (formNode.type === FormNodeTypes.ARRAY ? [] : {}); - - formNode.children?.forEach((child) => { - const { - name, type, value, validators, asyncValidators, disabled, - } = child; - - const control = FormNodeTypes.CONTROL === type - ? new CustomFormControl({ ...child }, { value, disabled: !!disabled }) - : this.createForm(child, data[`${name}`]); - - if (validators?.length) { - this.addValidators(control, validators); - } - - if (asyncValidators?.length) { - this.addAsyncValidators(control, asyncValidators); - } - - if (form instanceof FormGroup) { - form.addControl(name, control); - } else if (form instanceof FormArray) { - this.createControls(child, data).forEach((element) => form.push(element)); - } - }); - - if (data) { - form.patchValue(data); - } - - return form; - } - - createControls(child: FormNode, data: unknown): CustomFormFields[] { - // Note: There's a quirk here when dealing with arrays where if - // `data` is an array then `child.name` should be a correct index so - // make sure the template has the correct name to the node. - return Array.isArray(data) - ? data.map(() => - FormNodeTypes.CONTROL !== child.type - ? this.createForm(child, data[Number(child.name)]) - : new CustomFormControl({ ...child }, { value: child.value, disabled: !!child.disabled })) - : [new CustomFormControl({ ...child }, { value: child.value, disabled: !!child.disabled })]; - } - - addValidators(control: CustomFormFields, validators: Array<{ name: ValidatorNames; args?: unknown }> = []) { - validators.forEach((v) => control.addValidators(this.validatorMap[v.name](v.args))); - } - - addAsyncValidators(control: CustomFormFields, validators: Array<{ name: AsyncValidatorNames; args?: unknown }> = []) { - validators.forEach((v) => control.addAsyncValidators(this.asyncValidatorMap[v.name](v.args))); - } - - static validate(form: CustomFormGroup | CustomFormArray | FormGroup | FormArray, errors: GlobalError[], updateValidity = true) { - this.getFormLevelErrors(form, errors); - Object.entries(form.controls).forEach(([, value]) => { - if (!(value instanceof FormControl || value instanceof CustomFormControl)) { - this.validate(value as CustomFormGroup | CustomFormArray, errors, updateValidity); - } else { - value.markAsTouched(); - if (updateValidity) { - value.updateValueAndValidity(); - } - (value as CustomFormControl).meta?.changeDetection?.detectChanges(); - this.getControlErrors(value, errors); - } - }); - } - - static getFormLevelErrors(form: CustomFormGroup | CustomFormArray | FormGroup | FormArray, errors: GlobalError[]) { - if (!(form instanceof CustomFormGroup || form instanceof CustomFormArray)) { - return; - } - if (form.errors) { - Object.entries(form.errors).forEach(([key, error]) => { - // If an anchor link is provided, use that, otherwise determine target element from customId or name - const anchorLink = form.meta?.customId ?? form.meta?.name; - errors.push({ - error: ErrorMessageMap[`${key}`](error), - anchorLink, - }); - }); - } - } - - static validateControl(control: FormControl | CustomFormControl, errors: GlobalError[]) { - control.markAsTouched(); - (control as CustomFormControl).meta?.changeDetection?.detectChanges(); - this.getControlErrors(control, errors); - } - - private static getControlErrors(control: FormControl | CustomFormControl, validationErrorList: GlobalError[]) { - const { errors } = control; - const meta = (control as CustomFormControl).meta as FormNode | undefined; - - if (errors) { - if (meta?.hide) return; - Object.entries(errors).forEach(([error, data]) => { - // If an anchor link is provided, use that, otherwise determine target element from customId or name - const defaultAnchorLink = meta?.customId ?? meta?.name; - const anchorLink = typeof data === 'object' && data !== null - ? data.anchorLink ?? defaultAnchorLink - : defaultAnchorLink; - - // If typeof data is an array, assume we're passing the service multiple global errors - const globalErrors = Array.isArray(data) ? data : [{ - error: meta?.customErrorMessage ?? ErrorMessageMap[`${error}`](data, meta?.customValidatorErrorName ?? meta?.label), - anchorLink, - }]; - - validationErrorList.push(...globalErrors); - }); - } - - } + constructor(private store: Store) {} + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + validatorMap: Record ValidatorFn> = { + [ValidatorNames.AheadOfDate]: (arg: string) => CustomValidators.aheadOfDate(arg), + [ValidatorNames.Alphanumeric]: () => CustomValidators.alphanumeric(), + [ValidatorNames.Email]: () => CustomValidators.email(), + [ValidatorNames.CopyValueToRootControl]: (arg: string) => CustomValidators.copyValueToRootControl(arg), + [ValidatorNames.CustomPattern]: (args: string[]) => CustomValidators.customPattern([...args]), + [ValidatorNames.DateNotExceed]: (args: { sibling: string; months: number }) => + CustomValidators.dateNotExceed(args.sibling, args.months), + [ValidatorNames.Defined]: () => CustomValidators.defined(), + [ValidatorNames.DisableIfEquals]: (args: { sibling: string; value: unknown }) => + CustomValidators.disableIfEquals(args.sibling, args.value), + [ValidatorNames.EnableIfEquals]: (args: { sibling: string; value: unknown }) => + CustomValidators.enableIfEquals(args.sibling, args.value), + [ValidatorNames.FutureDate]: () => CustomValidators.futureDate, + [ValidatorNames.PastYear]: () => CustomValidators.pastYear, + [ValidatorNames.HideIfEmpty]: (args: string) => CustomValidators.hideIfEmpty(args), + [ValidatorNames.HideIfNotEqual]: (args: { sibling: string; value: unknown }) => + CustomValidators.hideIfNotEqual(args.sibling, args.value), + [ValidatorNames.HideIfParentSiblingEqual]: (args: { sibling: string; value: unknown }) => + CustomValidators.hideIfParentSiblingEquals(args.sibling, args.value), + [ValidatorNames.HideIfParentSiblingNotEqual]: (args: { sibling: string; value: unknown }) => + CustomValidators.hideIfParentSiblingNotEqual(args.sibling, args.value), + [ValidatorNames.Max]: (args: number) => Validators.max(args), + [ValidatorNames.MaxLength]: (args: number) => Validators.maxLength(args), + [ValidatorNames.Min]: (args: number) => Validators.min(args), + [ValidatorNames.MinLength]: (args: number) => Validators.minLength(args), + [ValidatorNames.NotZNumber]: () => CustomValidators.notZNumber, + [ValidatorNames.Numeric]: () => CustomValidators.numeric(), + [ValidatorNames.PastDate]: () => CustomValidators.pastDate, + [ValidatorNames.Pattern]: (args: string) => Validators.pattern(args), + [ValidatorNames.Required]: () => Validators.required, + [ValidatorNames.RequiredIfEquals]: (args: { sibling: string; value: unknown[]; customErrorMessage?: string }) => + CustomValidators.requiredIfEquals(args.sibling, args.value, args.customErrorMessage), + [ValidatorNames.requiredIfAllEquals]: (args: { sibling: string; value: unknown[] }) => + CustomValidators.requiredIfAllEquals(args.sibling, args.value), + [ValidatorNames.RequiredIfNotEquals]: (args: { sibling: string; value: unknown[] }) => + CustomValidators.requiredIfNotEquals(args.sibling, args.value), + [ValidatorNames.ValidateVRMTrailerIdLength]: (args: { sibling: string }) => + CustomValidators.validateVRMTrailerIdLength(args.sibling), + [ValidatorNames.ValidateDefectNotes]: () => DefectValidators.validateDefectNotes, + [ValidatorNames.ValidateProhibitionIssued]: () => DefectValidators.validateProhibitionIssued, + [ValidatorNames.MustEqualSibling]: (args: { sibling: string }) => CustomValidators.mustEqualSibling(args.sibling), + [ValidatorNames.HandlePsvPassengersChange]: (args: { passengersOne: string; passengersTwo: string }) => + CustomValidators.handlePsvPassengersChange(args.passengersOne, args.passengersTwo), + [ValidatorNames.IsMemberOfEnum]: (args: { + enum: Record; + options?: Partial; + }) => CustomValidators.isMemberOfEnum(args.enum, args.options), + [ValidatorNames.UpdateFunctionCode]: () => CustomValidators.updateFunctionCode(), + [ValidatorNames.ShowGroupsWhenEqualTo]: (args: { values: unknown[]; groups: string[] }) => + CustomValidators.showGroupsWhenEqualTo(args.values, args.groups), + [ValidatorNames.HideGroupsWhenEqualTo]: (args: { values: unknown[]; groups: string[] }) => + CustomValidators.hideGroupsWhenEqualTo(args.values, args.groups), + [ValidatorNames.ShowGroupsWhenIncludes]: (args: { values: unknown[]; groups: string[] }) => + CustomValidators.showGroupsWhenIncludes(args.values, args.groups), + [ValidatorNames.HideGroupsWhenIncludes]: (args: { values: unknown[]; groups: string[] }) => + CustomValidators.hideGroupsWhenIncludes(args.values, args.groups), + [ValidatorNames.ShowGroupsWhenExcludes]: (args: { values: unknown[]; groups: string[] }) => + CustomValidators.showGroupsWhenExcludes(args.values, args.groups), + [ValidatorNames.HideGroupsWhenExcludes]: (args: { values: unknown[]; groups: string[] }) => + CustomValidators.hideGroupsWhenExcludes(args.values, args.groups), + [ValidatorNames.AddWarningForAdrField]: (warning: string) => CustomValidators.addWarningForAdrField(warning), + [ValidatorNames.IsArray]: (args: Partial) => CustomValidators.isArray(args), + [ValidatorNames.Custom]: (...args) => CustomValidators.custom(...args), + [ValidatorNames.Tc3TestValidator]: (args: { inspectionNumber: number }) => CustomValidators.tc3TestValidator(args), + [ValidatorNames.RequiredIfNotHidden]: () => CustomValidators.requiredIfNotHidden(), + [ValidatorNames.DateIsInvalid]: () => CustomValidators.dateIsInvalid, + [ValidatorNames.MinArrayLengthIfNotEmpty]: (args: { minimumLength: number; message: string }) => + CustomValidators.minArrayLengthIfNotEmpty(args.minimumLength, args.message), + [ValidatorNames.IssueRequired]: () => CustomValidators.issueRequired(), + }; + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + asyncValidatorMap: Record AsyncValidatorFn> = { + [AsyncValidatorNames.HideIfEqualsWithCondition]: (args: { + sibling: string; + value: string; + conditions: Condition | Condition[]; + }) => CustomAsyncValidators.hideIfEqualsWithCondition(this.store, args.sibling, args.value, args.conditions), + [AsyncValidatorNames.PassResultDependantOnCustomDefects]: () => + CustomAsyncValidators.passResultDependantOnCustomDefects(this.store), + [AsyncValidatorNames.RequiredIfNotAbandoned]: () => CustomAsyncValidators.requiredIfNotAbandoned(this.store), + [AsyncValidatorNames.RequiredIfNotFail]: () => CustomAsyncValidators.requiredIfNotFail(this.store), + [AsyncValidatorNames.RequiredIfNotResult]: (args: { testResult: resultOfTestEnum | resultOfTestEnum[] }) => + CustomAsyncValidators.requiredIfNotResult(this.store, args.testResult), + [AsyncValidatorNames.RequiredIfNotResultAndSiblingEquals]: (args: { + testResult: resultOfTestEnum | resultOfTestEnum[]; + sibling: string; + value: unknown; + }) => + CustomAsyncValidators.requiredIfNotResultAndSiblingEquals(this.store, args.testResult, args.sibling, args.value), + [AsyncValidatorNames.ResultDependantOnCustomDefects]: () => + CustomAsyncValidators.resultDependantOnCustomDefects(this.store), + [AsyncValidatorNames.ResultDependantOnRequiredStandards]: () => + CustomAsyncValidators.resultDependantOnRequiredStandards(this.store), + [AsyncValidatorNames.UpdateTesterDetails]: () => CustomAsyncValidators.updateTesterDetails(this.store), + [AsyncValidatorNames.UpdateTestStationDetails]: () => CustomAsyncValidators.updateTestStationDetails(this.store), + [AsyncValidatorNames.RequiredWhenCarryingDangerousGoods]: () => + CustomAsyncValidators.requiredWhenCarryingDangerousGoods(this.store), + [AsyncValidatorNames.Custom]: (...args) => CustomAsyncValidators.custom(this.store, ...args), + }; + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + createForm(formNode: FormNode, data?: any): CustomFormGroup | CustomFormArray { + if (!formNode) { + return new CustomFormGroup(formNode, {}); + } + + const form: CustomFormGroup | CustomFormArray = + formNode.type === FormNodeTypes.ARRAY + ? new CustomFormArray(formNode, [], this.store) + : new CustomFormGroup(formNode, {}); + + data = data ?? (formNode.type === FormNodeTypes.ARRAY ? [] : {}); + + formNode.children?.forEach((child) => { + const { name, type, value, validators, asyncValidators, disabled } = child; + + const control = + FormNodeTypes.CONTROL === type + ? new CustomFormControl({ ...child }, { value, disabled: !!disabled }) + : this.createForm(child, data[`${name}`]); + + if (validators?.length) { + this.addValidators(control, validators); + } + + if (asyncValidators?.length) { + this.addAsyncValidators(control, asyncValidators); + } + + if (form instanceof FormGroup) { + form.addControl(name, control); + } else if (form instanceof FormArray) { + this.createControls(child, data).forEach((element) => form.push(element)); + } + }); + + if (data) { + form.patchValue(data); + } + + return form; + } + + createControls(child: FormNode, data: unknown): CustomFormFields[] { + // Note: There's a quirk here when dealing with arrays where if + // `data` is an array then `child.name` should be a correct index so + // make sure the template has the correct name to the node. + return Array.isArray(data) + ? data.map(() => + FormNodeTypes.CONTROL !== child.type + ? this.createForm(child, data[Number(child.name)]) + : new CustomFormControl({ ...child }, { value: child.value, disabled: !!child.disabled }) + ) + : [new CustomFormControl({ ...child }, { value: child.value, disabled: !!child.disabled })]; + } + + addValidators(control: CustomFormFields, validators: Array<{ name: ValidatorNames; args?: unknown }> = []) { + validators.forEach((v) => control.addValidators(this.validatorMap[v.name](v.args))); + } + + addAsyncValidators(control: CustomFormFields, validators: Array<{ name: AsyncValidatorNames; args?: unknown }> = []) { + validators.forEach((v) => control.addAsyncValidators(this.asyncValidatorMap[v.name](v.args))); + } + + static validate( + form: CustomFormGroup | CustomFormArray | FormGroup | FormArray, + errors: GlobalError[], + updateValidity = true + ) { + this.getFormLevelErrors(form, errors); + Object.entries(form.controls).forEach(([, value]) => { + if (!(value instanceof FormControl || value instanceof CustomFormControl)) { + this.validate(value as CustomFormGroup | CustomFormArray, errors, updateValidity); + } else { + value.markAsTouched(); + if (updateValidity) { + value.updateValueAndValidity(); + } + (value as CustomFormControl).meta?.changeDetection?.detectChanges(); + this.getControlErrors(value, errors); + } + }); + } + + static getFormLevelErrors(form: CustomFormGroup | CustomFormArray | FormGroup | FormArray, errors: GlobalError[]) { + if (!(form instanceof CustomFormGroup || form instanceof CustomFormArray)) { + return; + } + if (form.errors) { + Object.entries(form.errors).forEach(([key, error]) => { + // If an anchor link is provided, use that, otherwise determine target element from customId or name + const anchorLink = form.meta?.customId ?? form.meta?.name; + errors.push({ + error: ErrorMessageMap[`${key}`](error), + anchorLink, + }); + }); + } + } + + static validateControl(control: FormControl | CustomFormControl, errors: GlobalError[]) { + control.markAsTouched(); + (control as CustomFormControl).meta?.changeDetection?.detectChanges(); + this.getControlErrors(control, errors); + } + + private static getControlErrors(control: FormControl | CustomFormControl, validationErrorList: GlobalError[]) { + const { errors } = control; + const meta = (control as CustomFormControl).meta as FormNode | undefined; + + if (errors) { + if (meta?.hide) return; + Object.entries(errors).forEach(([error, data]) => { + // If an anchor link is provided, use that, otherwise determine target element from customId or name + const defaultAnchorLink = meta?.customId ?? meta?.name; + const anchorLink = + typeof data === 'object' && data !== null ? data.anchorLink ?? defaultAnchorLink : defaultAnchorLink; + + // If typeof data is an array, assume we're passing the service multiple global errors + const globalErrors = Array.isArray(data) + ? data + : [ + { + error: + meta?.customErrorMessage ?? + ErrorMessageMap[`${error}`](data, meta?.customValidatorErrorName ?? meta?.label), + anchorLink, + }, + ]; + + validationErrorList.push(...globalErrors); + }); + } + } } diff --git a/src/app/forms/services/dynamic-form.types.spec.ts b/src/app/forms/services/dynamic-form.types.spec.ts index ced81f8e7f..a9737423a6 100644 --- a/src/app/forms/services/dynamic-form.types.spec.ts +++ b/src/app/forms/services/dynamic-form.types.spec.ts @@ -1,5 +1,5 @@ import { HttpClientTestingModule } from '@angular/common/http/testing'; -import { inject, TestBed } from '@angular/core/testing'; +import { TestBed, inject } from '@angular/core/testing'; import { RouterTestingModule } from '@angular/router/testing'; import { provideMockStore } from '@ngrx/store/testing'; import { initialAppState } from '@store/.'; @@ -7,64 +7,73 @@ import { DynamicFormService } from './dynamic-form.service'; import { FormNode, FormNodeTypes } from './dynamic-form.types'; describe('Custom Classes', () => { - beforeEach(() => { - TestBed.configureTestingModule({ - providers: [provideMockStore({ initialState: initialAppState })], - imports: [HttpClientTestingModule, RouterTestingModule], - }); - }); + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [provideMockStore({ initialState: initialAppState })], + imports: [HttpClientTestingModule, RouterTestingModule], + }); + }); - describe('getCleanValue', () => { - const template = { - name: 'myForm', - type: FormNodeTypes.GROUP, - children: [ - { - name: 'combinationLevelOne', - label: 'Combination Level One', - type: FormNodeTypes.COMBINATION, - options: { - leftComponentName: 'levelOneControl1', - rightComponentName: 'levelOneControl2', - separator: ' ', - }, - }, - { - name: 'levelOneControl1', type: FormNodeTypes.CONTROL, label: 'Level one control 1', value: 'some string', - }, - { - name: 'levelOneControl2', type: FormNodeTypes.CONTROL, label: 'Level one control 2', value: 'some string', - }, - { name: 'sectionLabel', type: FormNodeTypes.SECTION, label: 'Section Label' }, - { - name: 'levelOneGroup', - type: FormNodeTypes.GROUP, - children: [ - { - name: 'levelTwoControl', type: FormNodeTypes.CONTROL, label: 'Level two control', value: 'some string', - }, - { - name: 'levelTwoArray', - type: FormNodeTypes.ARRAY, - children: [{ name: '0', type: FormNodeTypes.CONTROL, value: '1' }], - }, - ], - }, - ], - }; + describe('getCleanValue', () => { + const template = { + name: 'myForm', + type: FormNodeTypes.GROUP, + children: [ + { + name: 'combinationLevelOne', + label: 'Combination Level One', + type: FormNodeTypes.COMBINATION, + options: { + leftComponentName: 'levelOneControl1', + rightComponentName: 'levelOneControl2', + separator: ' ', + }, + }, + { + name: 'levelOneControl1', + type: FormNodeTypes.CONTROL, + label: 'Level one control 1', + value: 'some string', + }, + { + name: 'levelOneControl2', + type: FormNodeTypes.CONTROL, + label: 'Level one control 2', + value: 'some string', + }, + { name: 'sectionLabel', type: FormNodeTypes.SECTION, label: 'Section Label' }, + { + name: 'levelOneGroup', + type: FormNodeTypes.GROUP, + children: [ + { + name: 'levelTwoControl', + type: FormNodeTypes.CONTROL, + label: 'Level two control', + value: 'some string', + }, + { + name: 'levelTwoArray', + type: FormNodeTypes.ARRAY, + children: [{ name: '0', type: FormNodeTypes.CONTROL, value: '1' }], + }, + ], + }, + ], + }; - const expected = { - levelOneControl1: 'some string', - levelOneControl2: 'some string', - levelOneGroup: { levelTwoControl: 'some string', levelTwoArray: ['1', '2'] }, - }; + const expected = { + levelOneControl1: 'some string', + levelOneControl2: 'some string', + levelOneGroup: { levelTwoControl: 'some string', levelTwoArray: ['1', '2'] }, + }; - it('should return a json object where properties are only FormNodeTypes GROUP | ARRAY | CONTROL', inject( - [DynamicFormService], - (dfs: DynamicFormService) => { - const form = dfs.createForm(template, expected); - expect(form.getCleanValue(form)).toEqual(expected); - }, - )); - }); + it('should return a json object where properties are only FormNodeTypes GROUP | ARRAY | CONTROL', inject( + [DynamicFormService], + (dfs: DynamicFormService) => { + const form = dfs.createForm(template, expected); + expect(form.getCleanValue(form)).toEqual(expected); + } + )); + }); }); diff --git a/src/app/forms/services/dynamic-form.types.ts b/src/app/forms/services/dynamic-form.types.ts index 4f30654ceb..1741082f66 100644 --- a/src/app/forms/services/dynamic-form.types.ts +++ b/src/app/forms/services/dynamic-form.types.ts @@ -1,16 +1,18 @@ // eslint-disable-next-line max-classes-per-file import { ChangeDetectorRef } from '@angular/core'; import { - AbstractControl, - AbstractControlOptions, - AsyncValidatorFn, - FormArray, - FormControl, - FormControlOptions, - FormGroup, - ValidatorFn, + AbstractControl, + AbstractControlOptions, + AsyncValidatorFn, + FormArray, + FormControl, + FormControlOptions, + FormGroup, + ValidatorFn, } from '@angular/forms'; import { Params } from '@angular/router'; +// eslint-disable-next-line import/no-cycle +import { BaseControlComponent } from '@forms/components/base-control/base-control.component'; import { AsyncValidatorNames } from '@forms/models/async-validators.enum'; import { ValidatorNames } from '@forms/models/validators.enum'; import { ReferenceDataResourceType } from '@models/reference-data.model'; @@ -18,289 +20,287 @@ import { Store } from '@ngrx/store'; import { TagTypes } from '@shared/components/tag/tag.component'; // eslint-disable-next-line import/no-cycle import { State } from '@store/.'; -// eslint-disable-next-line import/no-cycle -import { BaseControlComponent } from '@forms/components/base-control/base-control.component'; -import { map, Observable } from 'rxjs'; -import { SpecialRefData } from './multi-options.service'; +import { Observable, map } from 'rxjs'; // eslint-disable-next-line import/no-cycle import { DynamicFormService } from './dynamic-form.service'; +import { SpecialRefData } from './multi-options.service'; export enum FormNodeViewTypes { - DATE = 'date', - DATETIME = 'dateTime', - FULLWIDTH = 'fullWidth', - HIDDEN = 'hidden', - STRING = 'string', - SUBHEADING = 'subHeading', - TIME = 'time', - VEHICLETYPE = 'vehicleType', - VRM = 'vrm', - CUSTOM = 'custom', - ADR_EXAMINER_NOTES = 'adrExaminerNotes', // TODO: remove in favour of custom + DATE = 'date', + DATETIME = 'dateTime', + FULLWIDTH = 'fullWidth', + HIDDEN = 'hidden', + STRING = 'string', + SUBHEADING = 'subHeading', + TIME = 'time', + VEHICLETYPE = 'vehicleType', + VRM = 'vrm', + CUSTOM = 'custom', + ADR_EXAMINER_NOTES = 'adrExaminerNotes', // TODO: remove in favour of custom } export enum TagTypeLabels { - REQUIRED = 'Required', - PLATES = 'Plates', + REQUIRED = 'Required', + PLATES = 'Plates', } export enum FormNodeTypes { - ARRAY = 'array', - COMBINATION = 'combination', - CONTROL = 'control', - DIMENSIONS = 'dimensions', - GROUP = 'group', - ROOT = 'root', - SECTION = 'section', - TITLE = 'title', - SUBTITLE = 'subtitle', + ARRAY = 'array', + COMBINATION = 'combination', + CONTROL = 'control', + DIMENSIONS = 'dimensions', + GROUP = 'group', + ROOT = 'root', + SECTION = 'section', + TITLE = 'title', + SUBTITLE = 'subtitle', } export enum FormNodeEditTypes { - AUTOCOMPLETE = 'autocomplete', - CHECKBOX = 'checkbox', - CHECKBOXGROUP = 'checkboxgroup', - DATE = 'date', - DATETIME = 'datetime', - DROPDOWN = 'dropdown', - HIDDEN = 'hidden', - NUMBER = 'number', - NUMERICSTRING = 'numericstring', - RADIO = 'radio', - SELECT = 'select', - TEXT = 'text', - TEXTAREA = 'textarea', - APPROVAL_TYPE = 'approvalType', - CUSTOM = 'custom', + AUTOCOMPLETE = 'autocomplete', + CHECKBOX = 'checkbox', + CHECKBOXGROUP = 'checkboxgroup', + DATE = 'date', + DATETIME = 'datetime', + DROPDOWN = 'dropdown', + HIDDEN = 'hidden', + NUMBER = 'number', + NUMERICSTRING = 'numericstring', + RADIO = 'radio', + SELECT = 'select', + TEXT = 'text', + TEXTAREA = 'textarea', + APPROVAL_TYPE = 'approvalType', + CUSTOM = 'custom', } export enum FormNodeWidth { - XXXL = 50, - XXL = 30, - XL = 20, - L = 10, - M = 5, - S = 4, - XS = 3, - XXS = 2, - UNSET = 'unset', + XXXL = 50, + XXL = 30, + XL = 20, + L = 10, + M = 5, + S = 4, + XS = 3, + XXS = 2, + UNSET = 'unset', } export interface FormNodeOption { - value: T; - label: string; - hint?: string; + value: T; + label: string; + hint?: string; } type AsyncValidatorOptions = AsyncValidatorFn | AsyncValidatorFn[] | null; export interface FormNode { - name: string; - children?: FormNode[]; - type: FormNodeTypes; // maybe updateType? - viewType?: FormNodeViewTypes; - editType?: FormNodeEditTypes; - width?: FormNodeWidth; - label?: string; - hint?: string; - delimited?: { regex?: string; separator: string }; - value?: unknown; - path?: string; - options?: FormNodeOption[] | FormNodeCombinationOptions; - validators?: FormNodeValidator[]; - customErrorMessage?: string; - customValidatorErrorName?: string; - asyncValidators?: { name: AsyncValidatorNames; args?: unknown }[]; - disabled?: boolean; - readonly?: boolean; - hide?: boolean; - required?: boolean; - changeDetection?: ChangeDetectorRef; - subHeadingLink?: SubHeadingLink; - referenceData?: ReferenceDataResourceType | SpecialRefData; - suffix?: string; - isoDate?: boolean; - class?: string; - customId?: string; - warning?: string; - customTags?: CustomTag[]; - enableDecimals?: boolean; - groups?: string[]; - viewComponent?: typeof BaseControlComponent; - editComponent?: typeof BaseControlComponent; + name: string; + children?: FormNode[]; + type: FormNodeTypes; // maybe updateType? + viewType?: FormNodeViewTypes; + editType?: FormNodeEditTypes; + width?: FormNodeWidth; + label?: string; + hint?: string; + delimited?: { regex?: string; separator: string }; + value?: unknown; + path?: string; + options?: FormNodeOption[] | FormNodeCombinationOptions; + validators?: FormNodeValidator[]; + customErrorMessage?: string; + customValidatorErrorName?: string; + asyncValidators?: { name: AsyncValidatorNames; args?: unknown }[]; + disabled?: boolean; + readonly?: boolean; + hide?: boolean; + required?: boolean; + changeDetection?: ChangeDetectorRef; + subHeadingLink?: SubHeadingLink; + referenceData?: ReferenceDataResourceType | SpecialRefData; + suffix?: string; + isoDate?: boolean; + class?: string; + customId?: string; + warning?: string; + customTags?: CustomTag[]; + enableDecimals?: boolean; + groups?: string[]; + viewComponent?: typeof BaseControlComponent; + editComponent?: typeof BaseControlComponent; } export interface CustomTag { - label: TagTypeLabels; - colour: TagTypes; + label: TagTypeLabels; + colour: TagTypes; } export interface FormNodeValidator { - name: ValidatorNames; - args?: unknown; + name: ValidatorNames; + args?: unknown; } export interface FormNodeCombinationOptions { - leftComponentName: string; - rightComponentName: string; - separator: string; + leftComponentName: string; + rightComponentName: string; + separator: string; } export interface SubHeadingLink { - label: string; - url: string; - queryParams?: Params; + label: string; + url: string; + queryParams?: Params; } export interface CustomControl extends FormControl { - meta: FormNode; + meta: FormNode; } export interface SearchParams { - systemNumber?: string; - vin?: string; - reason?: string; - axleNumber?: number; + systemNumber?: string; + vin?: string; + reason?: string; + axleNumber?: number; } export class CustomFormControl extends FormControl implements CustomControl { - meta: FormNode; - - constructor( - meta: FormNode, - formState?: unknown, - validatorOrOpts?: ValidatorFn | ValidatorFn[] | FormControlOptions | null, - asyncValidator?: AsyncValidatorOptions, - ) { - super(formState, validatorOrOpts, asyncValidator); - this.meta = meta; - } + meta: FormNode; + + constructor( + meta: FormNode, + formState?: unknown, + validatorOrOpts?: ValidatorFn | ValidatorFn[] | FormControlOptions | null, + asyncValidator?: AsyncValidatorOptions + ) { + super(formState, validatorOrOpts, asyncValidator); + this.meta = meta; + } } interface BaseForm { - /** - * function that returns the json object value of the form after removing all the disabled controls - * and properties where meta.type is not 'control', 'group' or 'array - * - * @returns form json value - */ - getCleanValue: (form: CustomFormGroup | CustomFormArray) => { [key: string]: unknown } | Array<[]>; - - cleanValueChanges: Observable; + /** + * function that returns the json object value of the form after removing all the disabled controls + * and properties where meta.type is not 'control', 'group' or 'array + * + * @returns form json value + */ + getCleanValue: (form: CustomFormGroup | CustomFormArray) => { [key: string]: unknown } | Array<[]>; + + cleanValueChanges: Observable; } export interface CustomGroup extends FormGroup { - meta: FormNode; + meta: FormNode; } export class CustomFormGroup extends FormGroup implements CustomGroup, BaseForm { - meta: FormNode; - - constructor( - meta: FormNode, - controls: { - [key: string]: AbstractControl; - }, - validatorOrOpts?: ValidatorFn | ValidatorFn[] | AbstractControlOptions | null, - asyncValidator?: AsyncValidatorOptions, - ) { - super(controls, validatorOrOpts, asyncValidator); - this.meta = meta; - } - - getCleanValue = cleanValue.bind(this); - - get cleanValueChanges() { - return this.valueChanges.pipe(map(() => this.getCleanValue(this))); - } + meta: FormNode; + + constructor( + meta: FormNode, + controls: { + [key: string]: AbstractControl; + }, + validatorOrOpts?: ValidatorFn | ValidatorFn[] | AbstractControlOptions | null, + asyncValidator?: AsyncValidatorOptions + ) { + super(controls, validatorOrOpts, asyncValidator); + this.meta = meta; + } + + getCleanValue = cleanValue.bind(this); + + get cleanValueChanges() { + return this.valueChanges.pipe(map(() => this.getCleanValue(this))); + } } export interface CustomArray extends FormArray { - meta: FormNode; + meta: FormNode; } export class CustomFormArray extends FormArray implements CustomArray, BaseForm { - meta: FormNode; - private dynamicFormService: DynamicFormService; - - constructor( - meta: FormNode, - controls: AbstractControl[], - store: Store, - validatorOrOpts?: ValidatorFn | ValidatorFn[] | AbstractControlOptions | null, - asyncValidator?: AsyncValidatorOptions, - ) { - super(controls, validatorOrOpts, asyncValidator); - this.meta = meta; - this.dynamicFormService = new DynamicFormService(store); - } - - getCleanValue = cleanValue.bind(this); - - get cleanValueChanges() { - return this.valueChanges.pipe(map(() => this.getCleanValue(this))); - } - - addControl(data?: unknown): void { - if (this.meta?.children) { - super.push(this.dynamicFormService.createForm(this.meta.children[0], data)); - } - } - - override patchValue( - value: unknown[] | undefined | null, - options?: { - onlySelf?: boolean; - emitEvent?: boolean; - }, - ): void { - if (value) { - if (value.length !== this.controls.length && this.meta.children && this.meta.children[0].type === 'group') { - if (value.length > this.controls.length) { - super.push(this.dynamicFormService.createForm(this.meta.children[0], value)); - } else { - this.controls.pop(); - } - } - super.patchValue(value, options); - } - } + meta: FormNode; + private dynamicFormService: DynamicFormService; + + constructor( + meta: FormNode, + controls: AbstractControl[], + store: Store, + validatorOrOpts?: ValidatorFn | ValidatorFn[] | AbstractControlOptions | null, + asyncValidator?: AsyncValidatorOptions + ) { + super(controls, validatorOrOpts, asyncValidator); + this.meta = meta; + this.dynamicFormService = new DynamicFormService(store); + } + + getCleanValue = cleanValue.bind(this); + + get cleanValueChanges() { + return this.valueChanges.pipe(map(() => this.getCleanValue(this))); + } + + addControl(data?: unknown): void { + if (this.meta?.children) { + super.push(this.dynamicFormService.createForm(this.meta.children[0], data)); + } + } + + override patchValue( + value: unknown[] | undefined | null, + options?: { + onlySelf?: boolean; + emitEvent?: boolean; + } + ): void { + if (value) { + if (value.length !== this.controls.length && this.meta.children && this.meta.children[0].type === 'group') { + if (value.length > this.controls.length) { + super.push(this.dynamicFormService.createForm(this.meta.children[0], value)); + } else { + this.controls.pop(); + } + } + super.patchValue(value, options); + } + } } // TODO: clean this // eslint-disable-next-line @typescript-eslint/no-explicit-any const cleanValue = (form: CustomFormGroup | CustomFormArray): Record | Array<[]> => { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const localCleanValue = form instanceof CustomFormArray ? [] : ({} as Record); - Object.keys(form.controls).forEach((key) => { - // eslint-disable-next-line @typescript-eslint/no-explicit-any, security/detect-object-injection - const control = (form.controls as any)[key]; - if (control instanceof CustomFormGroup && control.meta.type === FormNodeTypes.GROUP) { - // eslint-disable-next-line @typescript-eslint/no-explicit-any, security/detect-object-injection - (localCleanValue as any)[key] = objectOrNull(control.getCleanValue(control)); - } else if (control instanceof CustomFormArray) { - // eslint-disable-next-line @typescript-eslint/no-explicit-any, security/detect-object-injection - (localCleanValue as any)[key] = control.getCleanValue(control); - } else if (control instanceof CustomFormControl && control.meta.type === FormNodeTypes.CONTROL) { - if (control.meta.required && control.meta.hide) { - pushOrAssignAt(control.meta.value || null, localCleanValue, key); - } else if (!control.meta.hide) { - pushOrAssignAt(control.value, localCleanValue, key); - } - } - }); - - return localCleanValue; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const localCleanValue = form instanceof CustomFormArray ? [] : ({} as Record); + Object.keys(form.controls).forEach((key) => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any, security/detect-object-injection + const control = (form.controls as any)[key]; + if (control instanceof CustomFormGroup && control.meta.type === FormNodeTypes.GROUP) { + // eslint-disable-next-line @typescript-eslint/no-explicit-any, security/detect-object-injection + (localCleanValue as any)[key] = objectOrNull(control.getCleanValue(control)); + } else if (control instanceof CustomFormArray) { + // eslint-disable-next-line @typescript-eslint/no-explicit-any, security/detect-object-injection + (localCleanValue as any)[key] = control.getCleanValue(control); + } else if (control instanceof CustomFormControl && control.meta.type === FormNodeTypes.CONTROL) { + if (control.meta.required && control.meta.hide) { + pushOrAssignAt(control.meta.value || null, localCleanValue, key); + } else if (!control.meta.hide) { + pushOrAssignAt(control.value, localCleanValue, key); + } + } + }); + + return localCleanValue; }; function objectOrNull(obj: Object) { - return Object.values(obj).some((value) => undefined !== value) ? obj : null; + return Object.values(obj).some((value) => undefined !== value) ? obj : null; } // eslint-disable-next-line @typescript-eslint/no-explicit-any function pushOrAssignAt(value: any, localCleanValue: Array<[]> | Record, key: string) { - if (Array.isArray(localCleanValue)) { - localCleanValue.push(value); - } else { - localCleanValue[`${key}`] = value; - } + if (Array.isArray(localCleanValue)) { + localCleanValue.push(value); + } else { + localCleanValue[`${key}`] = value; + } } diff --git a/src/app/forms/services/multi-options.service.ts b/src/app/forms/services/multi-options.service.ts index 669ea9f57a..d0393b46ba 100644 --- a/src/app/forms/services/multi-options.service.ts +++ b/src/app/forms/services/multi-options.service.ts @@ -1,53 +1,53 @@ import { Injectable } from '@angular/core'; import { MultiOptions } from '@forms/models/options.model'; import { ReferenceDataResourceType } from '@models/reference-data.model'; -import { select, Store } from '@ngrx/store'; +import { Store, select } from '@ngrx/store'; import { ReferenceDataService } from '@services/reference-data/reference-data.service'; import { TestStationsService } from '@services/test-stations/test-stations.service'; import { fetchReasonsForAbandoning } from '@store/reference-data'; // eslint-disable-next-line import/no-cycle import { testResultInEdit } from '@store/test-records'; -import { fetchTestStations, TestStationsState } from '@store/test-stations'; +import { TestStationsState, fetchTestStations } from '@store/test-stations'; import { Observable, switchMap } from 'rxjs'; @Injectable({ providedIn: 'root' }) export class MultiOptionsService { - constructor( - private referenceDataService: ReferenceDataService, - private store: Store, - private testStationsService: TestStationsService, - ) {} + constructor( + private referenceDataService: ReferenceDataService, + private store: Store, + private testStationsService: TestStationsService + ) {} - getOptions(referenceData: ReferenceDataResourceType | SpecialRefData): Observable { - switch (referenceData) { - case SpecialRefData.TEST_STATION_P_NUMBER: - return this.testStationsService.getTestStationsOptions(); - case SpecialRefData.ReasonsForAbandoning: - return this.store.pipe( - select(testResultInEdit), - switchMap((testResult) => this.referenceDataService.getReasonsForAbandoning(testResult?.vehicleType)), - ); - default: - return this.referenceDataService.getReferenceDataOptions(referenceData); - } - } + getOptions(referenceData: ReferenceDataResourceType | SpecialRefData): Observable { + switch (referenceData) { + case SpecialRefData.TEST_STATION_P_NUMBER: + return this.testStationsService.getTestStationsOptions(); + case SpecialRefData.ReasonsForAbandoning: + return this.store.pipe( + select(testResultInEdit), + switchMap((testResult) => this.referenceDataService.getReasonsForAbandoning(testResult?.vehicleType)) + ); + default: + return this.referenceDataService.getReferenceDataOptions(referenceData); + } + } - loadOptions(referenceData: ReferenceDataResourceType | SpecialRefData): void { - switch (referenceData) { - case SpecialRefData.TEST_STATION_P_NUMBER: - this.store.dispatch(fetchTestStations()); - break; - case SpecialRefData.ReasonsForAbandoning: - this.store.dispatch(fetchReasonsForAbandoning()); - break; - default: - this.referenceDataService.loadReferenceData(referenceData); - break; - } - } + loadOptions(referenceData: ReferenceDataResourceType | SpecialRefData): void { + switch (referenceData) { + case SpecialRefData.TEST_STATION_P_NUMBER: + this.store.dispatch(fetchTestStations()); + break; + case SpecialRefData.ReasonsForAbandoning: + this.store.dispatch(fetchReasonsForAbandoning()); + break; + default: + this.referenceDataService.loadReferenceData(referenceData); + break; + } + } } export enum SpecialRefData { - TEST_STATION_P_NUMBER = 'testStationPNumber', - ReasonsForAbandoning = 'REASONS_FOR_ABANDONING', + TEST_STATION_P_NUMBER = 'testStationPNumber', + ReasonsForAbandoning = 'REASONS_FOR_ABANDONING', } diff --git a/src/app/forms/templates/car/car-tech-record.template.ts b/src/app/forms/templates/car/car-tech-record.template.ts index f3add8a924..fa57a0c0cf 100644 --- a/src/app/forms/templates/car/car-tech-record.template.ts +++ b/src/app/forms/templates/car/car-tech-record.template.ts @@ -2,96 +2,100 @@ import { EUVehicleCategory } from '@dvsa/cvs-type-definitions/types/v3/tech-reco import { VehicleConfiguration } from '@dvsa/cvs-type-definitions/types/v3/tech-record/enums/vehicleConfigurationLightVehicle.enum.js'; import { ValidatorNames } from '@forms/models/validators.enum'; import { - FormNode, FormNodeEditTypes, FormNodeTypes, FormNodeViewTypes, FormNodeWidth, - TagTypeLabels, + FormNode, + FormNodeEditTypes, + FormNodeTypes, + FormNodeViewTypes, + FormNodeWidth, + TagTypeLabels, } from '@forms/services/dynamic-form.types'; import { getOptionsFromEnum } from '@forms/utils/enum-map'; import { VehicleSubclass } from '@models/vehicle-tech-record.model'; import { TagType } from '@shared/components/tag/tag.component'; export const CarTechRecord: FormNode = { - name: 'techRecordSummary', - type: FormNodeTypes.GROUP, - label: 'Vehicle Summary', - children: [ - { - name: 'techRecord_vehicleType', - label: 'Vehicle type', - value: '', - width: FormNodeWidth.XS, - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.VEHICLETYPE, - disabled: true, - customTags: [{ colour: TagType.RED, label: TagTypeLabels.REQUIRED }], - }, - { - name: 'techRecord_statusCode', - label: 'Record status', - value: '', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - customTags: [{ colour: TagType.RED, label: TagTypeLabels.REQUIRED }], - }, - { - name: 'techRecord_regnDate', - label: 'Date of first registration', - value: null, - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.DATE, - editType: FormNodeEditTypes.DATE, - isoDate: false, - }, - { - name: 'techRecord_manufactureYear', - label: 'Year of manufacture', - value: null, - width: FormNodeWidth.XS, - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.NUMBER, - validators: [ - { name: ValidatorNames.Max, args: 9999 }, - { name: ValidatorNames.Min, args: 1000 }, - { name: ValidatorNames.PastYear }, - ], - }, - { - name: 'techRecord_noOfAxles', - label: 'Number of axles', - value: 2, - width: FormNodeWidth.XXS, - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.NUMBER, - validators: [{ name: ValidatorNames.Max, args: 99 }], - customTags: [{ colour: TagType.RED, label: TagTypeLabels.REQUIRED }], - }, - { - name: 'techRecord_vehicleSubclass', - label: 'Vehicle Subclass', - width: FormNodeWidth.XXS, - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.CHECKBOXGROUP, - options: getOptionsFromEnum(VehicleSubclass), - customTags: [{ colour: TagType.RED, label: TagTypeLabels.REQUIRED }], - }, - { - name: 'techRecord_vehicleConfiguration', - label: 'Vehicle configuration', - value: VehicleConfiguration.OTHER, - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.SELECT, - options: getOptionsFromEnum(VehicleConfiguration), - validators: [{ name: ValidatorNames.Required }], - customTags: [{ colour: TagType.RED, label: TagTypeLabels.REQUIRED }], - }, - { - name: 'techRecord_euVehicleCategory', - label: 'EU vehicle category', - value: null, - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.SELECT, - options: getOptionsFromEnum(EUVehicleCategory), - width: FormNodeWidth.S, - }, - ], + name: 'techRecordSummary', + type: FormNodeTypes.GROUP, + label: 'Vehicle Summary', + children: [ + { + name: 'techRecord_vehicleType', + label: 'Vehicle type', + value: '', + width: FormNodeWidth.XS, + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.VEHICLETYPE, + disabled: true, + customTags: [{ colour: TagType.RED, label: TagTypeLabels.REQUIRED }], + }, + { + name: 'techRecord_statusCode', + label: 'Record status', + value: '', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + customTags: [{ colour: TagType.RED, label: TagTypeLabels.REQUIRED }], + }, + { + name: 'techRecord_regnDate', + label: 'Date of first registration', + value: null, + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.DATE, + editType: FormNodeEditTypes.DATE, + isoDate: false, + }, + { + name: 'techRecord_manufactureYear', + label: 'Year of manufacture', + value: null, + width: FormNodeWidth.XS, + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.NUMBER, + validators: [ + { name: ValidatorNames.Max, args: 9999 }, + { name: ValidatorNames.Min, args: 1000 }, + { name: ValidatorNames.PastYear }, + ], + }, + { + name: 'techRecord_noOfAxles', + label: 'Number of axles', + value: 2, + width: FormNodeWidth.XXS, + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.NUMBER, + validators: [{ name: ValidatorNames.Max, args: 99 }], + customTags: [{ colour: TagType.RED, label: TagTypeLabels.REQUIRED }], + }, + { + name: 'techRecord_vehicleSubclass', + label: 'Vehicle Subclass', + width: FormNodeWidth.XXS, + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.CHECKBOXGROUP, + options: getOptionsFromEnum(VehicleSubclass), + customTags: [{ colour: TagType.RED, label: TagTypeLabels.REQUIRED }], + }, + { + name: 'techRecord_vehicleConfiguration', + label: 'Vehicle configuration', + value: VehicleConfiguration.OTHER, + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.SELECT, + options: getOptionsFromEnum(VehicleConfiguration), + validators: [{ name: ValidatorNames.Required }], + customTags: [{ colour: TagType.RED, label: TagTypeLabels.REQUIRED }], + }, + { + name: 'techRecord_euVehicleCategory', + label: 'EU vehicle category', + value: null, + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.SELECT, + options: getOptionsFromEnum(EUVehicleCategory), + width: FormNodeWidth.S, + }, + ], }; diff --git a/src/app/forms/templates/general/adr-certificate.template.ts b/src/app/forms/templates/general/adr-certificate.template.ts index 1a6b8ec067..97842c5a00 100644 --- a/src/app/forms/templates/general/adr-certificate.template.ts +++ b/src/app/forms/templates/general/adr-certificate.template.ts @@ -1,18 +1,16 @@ +import { AdrCertificateHistoryComponent } from '@forms/custom-sections/adr-certificate-history/adr-certificate-history.component'; import { FormNode, FormNodeTypes, FormNodeViewTypes } from '@forms/services/dynamic-form.types'; -import { - AdrCertificateHistoryComponent, -} from '@forms/custom-sections/adr-certificate-history/adr-certificate-history.component'; export const AdrCertificateTemplate: FormNode = { - name: 'adrCertificateSection', - type: FormNodeTypes.SECTION, - label: 'ADR Certificates', - children: [ - { - name: 'techRecord_adrDetails_certificates', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.CUSTOM, - viewComponent: AdrCertificateHistoryComponent, - }, - ], + name: 'adrCertificateSection', + type: FormNodeTypes.SECTION, + label: 'ADR Certificates', + children: [ + { + name: 'techRecord_adrDetails_certificates', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.CUSTOM, + viewComponent: AdrCertificateHistoryComponent, + }, + ], }; diff --git a/src/app/forms/templates/general/adr-summary.template.ts b/src/app/forms/templates/general/adr-summary.template.ts index 3ef290f770..3f8ea2bc46 100644 --- a/src/app/forms/templates/general/adr-summary.template.ts +++ b/src/app/forms/templates/general/adr-summary.template.ts @@ -1,830 +1,817 @@ -import { - ADRAdditionalNotesNumber, -} from '@dvsa/cvs-type-definitions/types/v3/tech-record/enums/adrAdditionalNotesNumber.enum.js'; +import { ADRAdditionalNotesNumber } from '@dvsa/cvs-type-definitions/types/v3/tech-record/enums/adrAdditionalNotesNumber.enum.js'; import { ADRBodyType } from '@dvsa/cvs-type-definitions/types/v3/tech-record/enums/adrBodyType.enum.js'; -import { - ADRCompatibilityGroupJ, -} from '@dvsa/cvs-type-definitions/types/v3/tech-record/enums/adrCompatibilityGroupJ.enum.js'; +import { ADRCompatibilityGroupJ } from '@dvsa/cvs-type-definitions/types/v3/tech-record/enums/adrCompatibilityGroupJ.enum.js'; import { ADRDangerousGood } from '@dvsa/cvs-type-definitions/types/v3/tech-record/enums/adrDangerousGood.enum.js'; import { ADRTankDetailsTankStatementSelect } from '@dvsa/cvs-type-definitions/types/v3/tech-record/enums/adrTankDetailsTankStatementSelect.enum.js'; import { ADRTankStatementSubstancePermitted } from '@dvsa/cvs-type-definitions/types/v3/tech-record/enums/adrTankStatementSubstancePermitted.js'; -import { - AdrExaminerNotesHistoryEditComponent, -} from '@forms/custom-sections/adr-examiner-notes-history-edit/adr-examiner-notes-history.component-edit'; -import { - AdrExaminerNotesHistoryViewComponent, -} from '@forms/custom-sections/adr-examiner-notes-history-view/adr-examiner-notes-history-view.component'; -import { - AdrNewCertificateRequiredViewComponent, -} from '@forms/custom-sections/adr-new-certificate-required-view/adr-new-certificate-required-view.component'; -import { - AdrTankDetailsInitialInspectionViewComponent, -} from '@forms/custom-sections/adr-tank-details-initial-inspection-view/adr-tank-details-initial-inspection-view.component'; +import { AdrExaminerNotesHistoryEditComponent } from '@forms/custom-sections/adr-examiner-notes-history-edit/adr-examiner-notes-history.component-edit'; +import { AdrExaminerNotesHistoryViewComponent } from '@forms/custom-sections/adr-examiner-notes-history-view/adr-examiner-notes-history-view.component'; +import { AdrNewCertificateRequiredViewComponent } from '@forms/custom-sections/adr-new-certificate-required-view/adr-new-certificate-required-view.component'; +import { AdrTankDetailsInitialInspectionViewComponent } from '@forms/custom-sections/adr-tank-details-initial-inspection-view/adr-tank-details-initial-inspection-view.component'; import { AdrTankDetailsM145ViewComponent } from '@forms/custom-sections/adr-tank-details-m145-view/adr-tank-details-m145-view.component'; -import { - AdrTankDetailsSubsequentInspectionsEditComponent, -} from '@forms/custom-sections/adr-tank-details-subsequent-inspections-edit/adr-tank-details-subsequent-inspections-edit.component'; -import { - AdrTankDetailsSubsequentInspectionsViewComponent, -} from '@forms/custom-sections/adr-tank-details-subsequent-inspections-view/adr-tank-details-subsequent-inspections-view.component'; -import { - AdrTankStatementUnNumberEditComponent, -} from '@forms/custom-sections/adr-tank-statement-un-number-edit/adr-tank-statement-un-number-edit.component'; -import { - AdrTankStatementUnNumberViewComponent, -} from '@forms/custom-sections/adr-tank-statement-un-number-view/adr-tank-statement-un-number-view.component'; +import { AdrTankDetailsSubsequentInspectionsEditComponent } from '@forms/custom-sections/adr-tank-details-subsequent-inspections-edit/adr-tank-details-subsequent-inspections-edit.component'; +import { AdrTankDetailsSubsequentInspectionsViewComponent } from '@forms/custom-sections/adr-tank-details-subsequent-inspections-view/adr-tank-details-subsequent-inspections-view.component'; +import { AdrTankStatementUnNumberEditComponent } from '@forms/custom-sections/adr-tank-statement-un-number-edit/adr-tank-statement-un-number-edit.component'; +import { AdrTankStatementUnNumberViewComponent } from '@forms/custom-sections/adr-tank-statement-un-number-view/adr-tank-statement-un-number-view.component'; import { ValidatorNames } from '@forms/models/validators.enum'; import { getOptionsFromEnum } from '@forms/utils/enum-map'; import { TC2Types } from '@models/adr.enum'; import { - FormNode, - FormNodeEditTypes, - FormNodeTypes, - FormNodeViewTypes, - FormNodeWidth, + FormNode, + FormNodeEditTypes, + FormNodeTypes, + FormNodeViewTypes, + FormNodeWidth, } from '../../services/dynamic-form.types'; export const AdrSummaryTemplate: FormNode = { - name: 'adrSection', - type: FormNodeTypes.SECTION, - label: 'ADR', - children: [ - { - name: 'techRecord_adrDetails_dangerousGoods', - label: 'Able to carry dangerous goods', - width: FormNodeWidth.XS, - value: false, - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.RADIO, - options: [ - { value: true, label: 'Yes' }, - { value: false, label: 'No' }, - ], - validators: [ - { name: ValidatorNames.ShowGroupsWhenEqualTo, args: { values: [true], groups: ['dangerous_goods'] } }, - { name: ValidatorNames.HideGroupsWhenEqualTo, args: { values: [false], groups: ['dangerous_goods'] } }, - { name: ValidatorNames.AddWarningForAdrField, args: 'By selecting this field it will delete all previous ADR field inputs' }, - ], - }, - { - name: 'applicationDetailsSectionTitle', - type: FormNodeTypes.TITLE, - label: 'Applicant Details', - groups: ['applicant_details', 'dangerous_goods'], - hide: true, - }, - { - name: 'techRecord_adrDetails_applicantDetails_name', - label: 'Name', - value: null, - width: FormNodeWidth.XXL, - type: FormNodeTypes.CONTROL, - validators: [ - { name: ValidatorNames.MaxLength, args: 150 }, - ], - groups: ['applicant_details', 'dangerous_goods'], - hide: true, - }, - { - name: 'techRecord_adrDetails_applicantDetails_street', - label: 'Street', - value: null, - width: FormNodeWidth.XXL, - type: FormNodeTypes.CONTROL, - validators: [{ name: ValidatorNames.MaxLength, args: 150 }], - groups: ['applicant_details', 'dangerous_goods'], - hide: true, - }, - { - name: 'techRecord_adrDetails_applicantDetails_town', - label: 'Town', - value: null, - width: FormNodeWidth.XXL, - type: FormNodeTypes.CONTROL, - validators: [{ name: ValidatorNames.MaxLength, args: 100 }], - groups: ['applicant_details', 'dangerous_goods'], - hide: true, - }, - { - name: 'techRecord_adrDetails_applicantDetails_city', - label: 'City', - value: null, - width: FormNodeWidth.XL, - type: FormNodeTypes.CONTROL, - validators: [{ name: ValidatorNames.MaxLength, args: 100 }], - groups: ['applicant_details', 'dangerous_goods'], - hide: true, - }, - { - name: 'techRecord_adrDetails_applicantDetails_postcode', - label: 'Postcode', - value: null, - width: FormNodeWidth.L, - type: FormNodeTypes.CONTROL, - validators: [{ name: ValidatorNames.MaxLength, args: 25 }], - groups: ['applicant_details', 'dangerous_goods'], - hide: true, - }, - { - name: 'adrDetailsSectionTitle', - type: FormNodeTypes.TITLE, - label: 'ADR Details', - groups: ['adr_details', 'dangerous_goods'], - hide: true, - }, - { - name: 'techRecord_adrDetails_vehicleDetails_type', - label: 'ADR body type', - type: FormNodeTypes.CONTROL, - width: FormNodeWidth.L, - editType: FormNodeEditTypes.SELECT, - groups: ['adr_details', 'dangerous_goods'], - hide: true, - options: getOptionsFromEnum(ADRBodyType), - validators: [ - { name: ValidatorNames.RequiredIfEquals, args: { sibling: 'techRecord_adrDetails_dangerousGoods', value: [true] } }, - { - name: ValidatorNames.ShowGroupsWhenIncludes, - args: { - values: Object.values(ADRBodyType).filter((value) => value.includes('battery') || value.includes('tank')) as string[], - groups: ['tank_details'], - }, - }, - { - name: ValidatorNames.HideGroupsWhenExcludes, - args: { - values: Object.values(ADRBodyType).filter((value) => value.includes('battery') || value.includes('tank')) as string[], - groups: ['tank_details', 'tank_details_hide'], - }, - }, - { - name: ValidatorNames.ShowGroupsWhenIncludes, - args: { - values: Object.values(ADRBodyType).filter((value) => value.includes('battery')) as string[], - groups: ['battery_list'], - }, - }, - { - name: ValidatorNames.HideGroupsWhenExcludes, - args: { - values: Object.values(ADRBodyType).filter((value) => value.includes('battery')) as string[], - groups: ['battery_list'], - }, - }, - ], - }, - { - name: 'techRecord_adrDetails_vehicleDetails_usedOnInternationalJourneys', - label: 'Vehicle used on international journeys', - type: FormNodeTypes.CONTROL, - options: [ - { value: 'yes', label: 'Yes' }, - { value: 'no', label: 'No' }, - { value: 'n/a', label: 'Not applicable' }, - ], - hide: true, - groups: ['adr_details', 'dangerous_goods'], - }, - { - name: 'techRecord_adrDetails_vehicleDetails_approvalDate', - label: 'Date processed', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.DATE, - viewType: FormNodeViewTypes.DATE, - groups: ['adr_details', 'dangerous_goods'], - hide: true, - isoDate: false, - validators: [ - { name: ValidatorNames.PastDate }, - { name: ValidatorNames.RequiredIfEquals, args: { sibling: 'techRecord_adrDetails_dangerousGoods', value: [true] } }, - ], - }, - { - name: 'techRecord_adrDetails_permittedDangerousGoods', - label: 'Permitted dangerous goods', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.CHECKBOXGROUP, - groups: ['adr_details', 'dangerous_goods'], - hide: true, - options: getOptionsFromEnum(ADRDangerousGood), - validators: [ - { - name: ValidatorNames.ShowGroupsWhenIncludes, - args: { - values: [ADRDangerousGood.EXPLOSIVES_TYPE_2, ADRDangerousGood.EXPLOSIVES_TYPE_3], - groups: ['compatibility_group_j'], - }, - }, - { - name: ValidatorNames.HideGroupsWhenExcludes, - args: { - values: [ADRDangerousGood.EXPLOSIVES_TYPE_2, ADRDangerousGood.EXPLOSIVES_TYPE_3], - groups: ['compatibility_group_j'], - }, - }, - { name: ValidatorNames.RequiredIfEquals, args: { sibling: 'techRecord_adrDetails_dangerousGoods', value: [true] } }, - ], - }, - { - name: 'techRecord_adrDetails_compatibilityGroupJ', - label: 'Compatibility group J', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.RADIO, - groups: ['compatibility_group_j', 'adr_details', 'dangerous_goods'], - hide: true, - options: [ - { value: ADRCompatibilityGroupJ.I, label: 'Yes' }, - { value: ADRCompatibilityGroupJ.E, label: 'No' }, - ], - validators: [ - { - name: ValidatorNames.RequiredIfEquals, - args: { - sibling: 'techRecord_adrDetails_permittedDangerousGoods', - value: [ADRDangerousGood.EXPLOSIVES_TYPE_2, ADRDangerousGood.EXPLOSIVES_TYPE_3], - }, - }, - ], - }, - { - name: 'techRecord_adrDetails_additionalNotes_number', - label: 'Guidance notes', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.CHECKBOXGROUP, - groups: ['adr_details', 'dangerous_goods'], - hide: true, - width: FormNodeWidth.XS, - value: [], - options: getOptionsFromEnum(ADRAdditionalNotesNumber), - validators: [], - }, - { - name: 'techRecord_adrDetails_adrTypeApprovalNo', - label: 'ADR type approval number', - value: '', - type: FormNodeTypes.CONTROL, - width: FormNodeWidth.L, - groups: ['adr_details', 'dangerous_goods'], - hide: true, - validators: [ - { name: ValidatorNames.MaxLength, args: 40 }, - ], - }, - { - name: 'tankDetailsSectionTitle', - type: FormNodeTypes.TITLE, - label: 'Tank Details', - groups: ['tank_details', 'dangerous_goods'], - hide: true, - }, - { - name: 'techRecord_adrDetails_tank_tankDetails_tankManufacturer', - label: 'Tank Make', - value: null, - type: FormNodeTypes.CONTROL, - width: FormNodeWidth.XXL, - groups: ['tank_details', 'dangerous_goods'], - validators: [ - { name: ValidatorNames.MaxLength, args: 70 }, - { - name: ValidatorNames.RequiredIfEquals, - args: { - sibling: 'techRecord_adrDetails_vehicleDetails_type', - value: Object.values(ADRBodyType).filter((value) => value.includes('battery') || value.includes('tank')) as string[], - }, - }, - ], - hide: true, - }, - { - name: 'techRecord_adrDetails_tank_tankDetails_yearOfManufacture', - label: 'Tank Year of manufacture', - value: null, - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.NUMBER, - width: FormNodeWidth.XS, - groups: ['tank_details', 'dangerous_goods'], - validators: [ - { name: ValidatorNames.Max, args: 9999 }, - { name: ValidatorNames.Min, args: 1000 }, - { name: ValidatorNames.PastYear }, - { - name: ValidatorNames.RequiredIfEquals, - args: { - sibling: 'techRecord_adrDetails_vehicleDetails_type', - value: Object.values(ADRBodyType).filter((value) => value.includes('battery') || value.includes('tank')) as string[], - }, - }, - ], - hide: true, - }, - { - name: 'techRecord_adrDetails_tank_tankDetails_tankManufacturerSerialNo', - label: 'Manufacturer serial number', - value: null, - type: FormNodeTypes.CONTROL, - width: FormNodeWidth.L, - groups: ['tank_details', 'dangerous_goods'], - validators: [ - { name: ValidatorNames.MaxLength, args: 50 }, - { - name: ValidatorNames.RequiredIfEquals, - args: { - sibling: 'techRecord_adrDetails_vehicleDetails_type', - value: Object.values(ADRBodyType).filter((value) => value.includes('battery') || value.includes('tank')) as string[], - }, - }, - ], - hide: true, - }, - { - name: 'techRecord_adrDetails_tank_tankDetails_tankTypeAppNo', - label: 'Tank type approval number', - value: null, - type: FormNodeTypes.CONTROL, - width: FormNodeWidth.L, - groups: ['tank_details', 'dangerous_goods'], - validators: [ - { name: ValidatorNames.MaxLength, args: 65 }, - { - name: ValidatorNames.RequiredIfEquals, - args: { - sibling: 'techRecord_adrDetails_vehicleDetails_type', - value: Object.values(ADRBodyType).filter((value) => value.includes('battery') || value.includes('tank')) as string[], - }, - }, - ], - hide: true, - }, - { - name: 'techRecord_adrDetails_tank_tankDetails_tankCode', - label: 'Code', - value: null, - type: FormNodeTypes.CONTROL, - width: FormNodeWidth.L, - groups: ['tank_details', 'dangerous_goods'], - validators: [ - { name: ValidatorNames.MaxLength, args: 30 }, - { - name: ValidatorNames.RequiredIfEquals, - args: { - sibling: 'techRecord_adrDetails_vehicleDetails_type', - value: Object.values(ADRBodyType).filter((value) => value.includes('battery') || value.includes('tank')) as string[], - }, - }, - ], - hide: true, - }, - { - name: 'techRecord_adrDetails_tank_tankDetails_tankStatement_substancesPermitted', - label: 'Substances permitted', - width: FormNodeWidth.XS, - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.RADIO, - options: getOptionsFromEnum(ADRTankStatementSubstancePermitted), - groups: ['tank_details', 'dangerous_goods'], - hide: true, - validators: [ - { - name: ValidatorNames.RequiredIfEquals, - args: { - sibling: 'techRecord_adrDetails_vehicleDetails_type', - value: Object.values(ADRBodyType).filter((value) => value.includes('battery') || value.includes('tank')) as string[], - }, - }, - { - name: ValidatorNames.ShowGroupsWhenEqualTo, - args: { - values: [ADRTankStatementSubstancePermitted.UNDER_UN_NUMBER], - groups: ['statement_select'], - }, - }, - { - name: ValidatorNames.HideGroupsWhenEqualTo, - args: { - values: [ADRTankStatementSubstancePermitted.UNDER_TANK_CODE, null, undefined], - groups: ['statement_select', 'statement_select_hide'], - }, - }, - ], - }, - { - name: 'techRecord_adrDetails_tank_tankDetails_tankStatement_select', - label: 'Select', - width: FormNodeWidth.XS, - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.RADIO, - groups: ['statement_select', 'tank_details_hide', 'dangerous_goods'], - hide: true, - options: getOptionsFromEnum(ADRTankDetailsTankStatementSelect), - validators: [ - { - name: ValidatorNames.RequiredIfEquals, - args: { - sibling: 'techRecord_adrDetails_vehicleDetails_type', - value: Object.values(ADRBodyType).filter((value) => value.includes('battery') || value.includes('tank')) as string[], - }, - }, - { - name: ValidatorNames.ShowGroupsWhenIncludes, - args: { - values: [ADRTankDetailsTankStatementSelect.STATEMENT], - groups: ['statement'], - }, - }, - { - name: ValidatorNames.ShowGroupsWhenIncludes, - args: { - values: [ADRTankDetailsTankStatementSelect.PRODUCT_LIST], - groups: ['productList'], - }, - }, - { - name: ValidatorNames.HideGroupsWhenExcludes, - args: { - values: [ADRTankDetailsTankStatementSelect.STATEMENT], - groups: ['statement'], - }, - }, - { - name: ValidatorNames.HideGroupsWhenExcludes, - args: { - values: [ADRTankDetailsTankStatementSelect.PRODUCT_LIST], - groups: ['productList'], - }, - }, - ], - }, - { - name: 'techRecord_adrDetails_tank_tankDetails_tankStatement_statement', - label: 'Reference number', - type: FormNodeTypes.CONTROL, - groups: ['statement', 'statement_select_hide', 'tank_details_hide', 'dangerous_goods'], - hide: true, - customErrorMessage: 'Reference number is required when selecting Statement', - validators: [ - { name: ValidatorNames.MaxLength, args: 1500 }, - { - name: ValidatorNames.RequiredIfEquals, - args: { - sibling: 'techRecord_adrDetails_tank_tankDetails_tankStatement_select', - value: [ADRTankDetailsTankStatementSelect.STATEMENT], - }, - }, - ], - }, - { - name: 'techRecord_adrDetails_tank_tankDetails_tankStatement_productListRefNo', - label: 'Reference number', - type: FormNodeTypes.CONTROL, - groups: ['productList', 'statement_select_hide', 'tank_details_hide', 'dangerous_goods'], - hide: true, - customErrorMessage: 'Reference number or UN number is required when selecting Product List', - validators: [ - { name: ValidatorNames.MaxLength, args: 1500 }, - { - name: ValidatorNames.RequiredIfEquals, - args: { - sibling: 'techRecord_adrDetails_tank_tankDetails_tankStatement_productListUnNo', - value: [[], [null], [''], null, undefined], - }, - }, - ], - }, - { - name: 'techRecord_adrDetails_tank_tankDetails_tankStatement_productListUnNo', - label: 'UN number', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.CUSTOM, - viewType: FormNodeViewTypes.CUSTOM, - editComponent: AdrTankStatementUnNumberEditComponent, - viewComponent: AdrTankStatementUnNumberViewComponent, - groups: ['productList', 'statement_select_hide', 'tank_details_hide', 'dangerous_goods'], - hide: true, - customErrorMessage: 'Reference number or UN number is required when selecting Product List', - validators: [ - { - name: ValidatorNames.RequiredIfEquals, - args: { sibling: 'techRecord_adrDetails_tank_tankDetails_tankStatement_productListRefNo', value: [null, undefined, ''] }, - }, - ], - }, - { - name: 'techRecord_adrDetails_tank_tankDetails_tankStatement_productList', - label: 'Additional Details', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.TEXTAREA, - groups: ['productList', 'statement_select_hide', 'tank_details_hide', 'dangerous_goods'], - hide: true, - validators: [ - { name: ValidatorNames.MaxLength, args: 1500 }, - ], - }, - { - name: 'techRecord_adrDetails_tank_tankDetails_specialProvisions', - label: 'Special Provisions', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.TEXTAREA, - hide: true, - validators: [ - { name: ValidatorNames.MaxLength, args: 1024 }, - ], - groups: ['dangerous_goods', 'tank_details'], - }, - { - name: 'tankInspectionsSectionTitle', - type: FormNodeTypes.TITLE, - label: 'Tank Inspections', - groups: ['tank_details', 'dangerous_goods'], - hide: true, - validators: [ - { - name: ValidatorNames.RequiredIfEquals, - args: { - sibling: 'techRecord_adrDetails_vehicleDetails_type', - value: Object.values(ADRBodyType).filter((value) => value.includes('battery') || value.includes('tank')) as string[], - }, - }, - ], - }, - { - name: 'tankInspectionsInitialView', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.CUSTOM, - viewComponent: AdrTankDetailsInitialInspectionViewComponent, - groups: ['tank_details', 'dangerous_goods'], - hide: true, - }, - { - name: 'techRecord_adrDetails_tank_tankDetails_tc2Details_tc2Type', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - label: 'TC2: Inspection type', - value: TC2Types.INITIAL, - hide: true, - groups: ['tank_details', 'dangerous_goods'], - validators: [], - }, - { - name: 'techRecord_adrDetails_tank_tankDetails_tc2Details_tc2IntermediateApprovalNo', - label: 'TC2: Certificate Number', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.HIDDEN, - hide: true, - groups: ['tank_details', 'dangerous_goods'], - validators: [ - { name: ValidatorNames.MaxLength, args: 70 }, - ], - }, - { - name: 'techRecord_adrDetails_tank_tankDetails_tc2Details_tc2IntermediateExpiryDate', - label: 'TC2: Expiry Date', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.HIDDEN, - isoDate: false, - hide: true, - groups: ['tank_details', 'dangerous_goods'], - validators: [], - }, - { - name: 'techRecord_adrDetails_tank_tankDetails_tc3Details', - label: 'Subsequent Inspections', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.CUSTOM, - viewComponent: AdrTankDetailsSubsequentInspectionsViewComponent, - editType: FormNodeEditTypes.CUSTOM, - editComponent: AdrTankDetailsSubsequentInspectionsEditComponent, - hide: true, - groups: ['tank_details', 'dangerous_goods'], - validators: [ - { - name: ValidatorNames.Tc3TestValidator, - args: { inspectionNumber: 0 }, - }, - ], - }, - { - name: 'techRecord_adrDetails_memosApply', - label: 'Memo 7/9 (3 month extension) applied', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.CHECKBOXGROUP, - groups: ['tank_details', 'dangerous_goods'], - hide: true, - options: [ - { value: '07/09 3mth leak ext ', label: 'Yes' }, - ], - validators: [], - }, - { - name: 'techRecord_adrDetails_m145Statement', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.CUSTOM, - viewComponent: AdrTankDetailsM145ViewComponent, - editType: FormNodeEditTypes.CHECKBOX, - groups: ['tank_details', 'dangerous_goods'], - hide: true, - validators: [], - }, - { - name: 'techRecord_adrDetails_listStatementApplicable', - label: 'Battery List Applicable', - width: FormNodeWidth.XS, - value: false, - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.RADIO, - options: [ - { value: true, label: 'Yes' }, - { value: false, label: 'No' }, - ], - validators: [ - { name: ValidatorNames.ShowGroupsWhenEqualTo, args: { values: [true], groups: ['battery_list_applicable'] } }, - { name: ValidatorNames.HideGroupsWhenEqualTo, args: { values: [false], groups: ['battery_list_applicable'] } }, - { - name: ValidatorNames.RequiredIfEquals, - args: { - sibling: 'techRecord_adrDetails_vehicleDetails_type', - value: Object.values(ADRBodyType).filter((value) => value.includes('battery')) as string[], - }, - }, - ], - hide: true, - groups: ['dangerous_goods', 'battery_list'], - }, - { - name: 'techRecord_adrDetails_batteryListNumber', - label: 'Reference number', - value: null, - type: FormNodeTypes.CONTROL, - width: FormNodeWidth.L, - groups: ['battery_list', 'battery_list_applicable', 'dangerous_goods'], - validators: [ - { name: ValidatorNames.MaxLength, args: 8 }, - { - name: ValidatorNames.RequiredIfEquals, - args: { - sibling: 'techRecord_adrDetails_listStatementApplicable', - value: [true], - }, - }, - ], - hide: true, - }, - { - name: 'DeclarationsSectionTitle', - label: 'Declarations seen', - type: FormNodeTypes.TITLE, - groups: ['declarations_details', 'dangerous_goods'], - hide: true, - }, - { - name: 'techRecord_adrDetails_brakeDeclarationsSeen', - label: 'Manufacturer brake declaration', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.CHECKBOX, - groups: ['declarations_details', 'dangerous_goods'], - value: false, - hide: true, - validators: [ - { - name: ValidatorNames.ShowGroupsWhenEqualTo, - args: { - values: [true], - groups: ['issuer_section'], - }, - }, - { - name: ValidatorNames.HideGroupsWhenEqualTo, - args: { - values: [false], - groups: ['issuer_section', 'weight_section'], - }, - }, - ], - - }, - { - name: 'techRecord_adrDetails_brakeDeclarationIssuer', - label: 'Issuer', - type: FormNodeTypes.CONTROL, - hide: true, - groups: ['issuer_section', 'dangerous_goods'], - editType: FormNodeEditTypes.TEXTAREA, - validators: [ - { name: ValidatorNames.MaxLength, args: 500 }, - ], - }, - { - name: 'techRecord_adrDetails_brakeEndurance', - label: 'Brake endurance', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.CHECKBOX, - - groups: ['issuer_section', 'dangerous_goods'], - hide: true, - validators: [ - { - name: ValidatorNames.ShowGroupsWhenEqualTo, - args: { - values: [true], - groups: ['weight_section'], - }, - }, - { - name: ValidatorNames.HideGroupsWhenEqualTo, - args: { - values: [false, null, undefined], - groups: ['weight_section'], - }, - }, - ], - - }, - { - name: 'techRecord_adrDetails_weight', - label: 'Weight (tonnes)', - type: FormNodeTypes.CONTROL, - width: FormNodeWidth.L, - groups: ['weight_section', 'dangerous_goods'], - hide: true, - validators: [ - { name: ValidatorNames.Max, args: 999999999 }, - { - name: ValidatorNames.RequiredIfNotHidden, - }, - ], + name: 'adrSection', + type: FormNodeTypes.SECTION, + label: 'ADR', + children: [ + { + name: 'techRecord_adrDetails_dangerousGoods', + label: 'Able to carry dangerous goods', + width: FormNodeWidth.XS, + value: false, + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.RADIO, + options: [ + { value: true, label: 'Yes' }, + { value: false, label: 'No' }, + ], + validators: [ + { name: ValidatorNames.ShowGroupsWhenEqualTo, args: { values: [true], groups: ['dangerous_goods'] } }, + { name: ValidatorNames.HideGroupsWhenEqualTo, args: { values: [false], groups: ['dangerous_goods'] } }, + { + name: ValidatorNames.AddWarningForAdrField, + args: 'By selecting this field it will delete all previous ADR field inputs', + }, + ], + }, + { + name: 'applicationDetailsSectionTitle', + type: FormNodeTypes.TITLE, + label: 'Applicant Details', + groups: ['applicant_details', 'dangerous_goods'], + hide: true, + }, + { + name: 'techRecord_adrDetails_applicantDetails_name', + label: 'Name', + value: null, + width: FormNodeWidth.XXL, + type: FormNodeTypes.CONTROL, + validators: [{ name: ValidatorNames.MaxLength, args: 150 }], + groups: ['applicant_details', 'dangerous_goods'], + hide: true, + }, + { + name: 'techRecord_adrDetails_applicantDetails_street', + label: 'Street', + value: null, + width: FormNodeWidth.XXL, + type: FormNodeTypes.CONTROL, + validators: [{ name: ValidatorNames.MaxLength, args: 150 }], + groups: ['applicant_details', 'dangerous_goods'], + hide: true, + }, + { + name: 'techRecord_adrDetails_applicantDetails_town', + label: 'Town', + value: null, + width: FormNodeWidth.XXL, + type: FormNodeTypes.CONTROL, + validators: [{ name: ValidatorNames.MaxLength, args: 100 }], + groups: ['applicant_details', 'dangerous_goods'], + hide: true, + }, + { + name: 'techRecord_adrDetails_applicantDetails_city', + label: 'City', + value: null, + width: FormNodeWidth.XL, + type: FormNodeTypes.CONTROL, + validators: [{ name: ValidatorNames.MaxLength, args: 100 }], + groups: ['applicant_details', 'dangerous_goods'], + hide: true, + }, + { + name: 'techRecord_adrDetails_applicantDetails_postcode', + label: 'Postcode', + value: null, + width: FormNodeWidth.L, + type: FormNodeTypes.CONTROL, + validators: [{ name: ValidatorNames.MaxLength, args: 25 }], + groups: ['applicant_details', 'dangerous_goods'], + hide: true, + }, + { + name: 'adrDetailsSectionTitle', + type: FormNodeTypes.TITLE, + label: 'ADR Details', + groups: ['adr_details', 'dangerous_goods'], + hide: true, + }, + { + name: 'techRecord_adrDetails_vehicleDetails_type', + label: 'ADR body type', + type: FormNodeTypes.CONTROL, + width: FormNodeWidth.L, + editType: FormNodeEditTypes.SELECT, + groups: ['adr_details', 'dangerous_goods'], + hide: true, + options: getOptionsFromEnum(ADRBodyType), + validators: [ + { + name: ValidatorNames.RequiredIfEquals, + args: { sibling: 'techRecord_adrDetails_dangerousGoods', value: [true] }, + }, + { + name: ValidatorNames.ShowGroupsWhenIncludes, + args: { + values: Object.values(ADRBodyType).filter( + (value) => value.includes('battery') || value.includes('tank') + ) as string[], + groups: ['tank_details'], + }, + }, + { + name: ValidatorNames.HideGroupsWhenExcludes, + args: { + values: Object.values(ADRBodyType).filter( + (value) => value.includes('battery') || value.includes('tank') + ) as string[], + groups: ['tank_details', 'tank_details_hide'], + }, + }, + { + name: ValidatorNames.ShowGroupsWhenIncludes, + args: { + values: Object.values(ADRBodyType).filter((value) => value.includes('battery')) as string[], + groups: ['battery_list'], + }, + }, + { + name: ValidatorNames.HideGroupsWhenExcludes, + args: { + values: Object.values(ADRBodyType).filter((value) => value.includes('battery')) as string[], + groups: ['battery_list'], + }, + }, + ], + }, + { + name: 'techRecord_adrDetails_vehicleDetails_usedOnInternationalJourneys', + label: 'Vehicle used on international journeys', + type: FormNodeTypes.CONTROL, + options: [ + { value: 'yes', label: 'Yes' }, + { value: 'no', label: 'No' }, + { value: 'n/a', label: 'Not applicable' }, + ], + hide: true, + groups: ['adr_details', 'dangerous_goods'], + }, + { + name: 'techRecord_adrDetails_vehicleDetails_approvalDate', + label: 'Date processed', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.DATE, + viewType: FormNodeViewTypes.DATE, + groups: ['adr_details', 'dangerous_goods'], + hide: true, + isoDate: false, + validators: [ + { name: ValidatorNames.PastDate }, + { + name: ValidatorNames.RequiredIfEquals, + args: { sibling: 'techRecord_adrDetails_dangerousGoods', value: [true] }, + }, + ], + }, + { + name: 'techRecord_adrDetails_permittedDangerousGoods', + label: 'Permitted dangerous goods', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.CHECKBOXGROUP, + groups: ['adr_details', 'dangerous_goods'], + hide: true, + options: getOptionsFromEnum(ADRDangerousGood), + validators: [ + { + name: ValidatorNames.ShowGroupsWhenIncludes, + args: { + values: [ADRDangerousGood.EXPLOSIVES_TYPE_2, ADRDangerousGood.EXPLOSIVES_TYPE_3], + groups: ['compatibility_group_j'], + }, + }, + { + name: ValidatorNames.HideGroupsWhenExcludes, + args: { + values: [ADRDangerousGood.EXPLOSIVES_TYPE_2, ADRDangerousGood.EXPLOSIVES_TYPE_3], + groups: ['compatibility_group_j'], + }, + }, + { + name: ValidatorNames.RequiredIfEquals, + args: { sibling: 'techRecord_adrDetails_dangerousGoods', value: [true] }, + }, + ], + }, + { + name: 'techRecord_adrDetails_compatibilityGroupJ', + label: 'Compatibility group J', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.RADIO, + groups: ['compatibility_group_j', 'adr_details', 'dangerous_goods'], + hide: true, + options: [ + { value: ADRCompatibilityGroupJ.I, label: 'Yes' }, + { value: ADRCompatibilityGroupJ.E, label: 'No' }, + ], + validators: [ + { + name: ValidatorNames.RequiredIfEquals, + args: { + sibling: 'techRecord_adrDetails_permittedDangerousGoods', + value: [ADRDangerousGood.EXPLOSIVES_TYPE_2, ADRDangerousGood.EXPLOSIVES_TYPE_3], + }, + }, + ], + }, + { + name: 'techRecord_adrDetails_additionalNotes_number', + label: 'Guidance notes', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.CHECKBOXGROUP, + groups: ['adr_details', 'dangerous_goods'], + hide: true, + width: FormNodeWidth.XS, + value: [], + options: getOptionsFromEnum(ADRAdditionalNotesNumber), + validators: [], + }, + { + name: 'techRecord_adrDetails_adrTypeApprovalNo', + label: 'ADR type approval number', + value: '', + type: FormNodeTypes.CONTROL, + width: FormNodeWidth.L, + groups: ['adr_details', 'dangerous_goods'], + hide: true, + validators: [{ name: ValidatorNames.MaxLength, args: 40 }], + }, + { + name: 'tankDetailsSectionTitle', + type: FormNodeTypes.TITLE, + label: 'Tank Details', + groups: ['tank_details', 'dangerous_goods'], + hide: true, + }, + { + name: 'techRecord_adrDetails_tank_tankDetails_tankManufacturer', + label: 'Tank Make', + value: null, + type: FormNodeTypes.CONTROL, + width: FormNodeWidth.XXL, + groups: ['tank_details', 'dangerous_goods'], + validators: [ + { name: ValidatorNames.MaxLength, args: 70 }, + { + name: ValidatorNames.RequiredIfEquals, + args: { + sibling: 'techRecord_adrDetails_vehicleDetails_type', + value: Object.values(ADRBodyType).filter( + (value) => value.includes('battery') || value.includes('tank') + ) as string[], + }, + }, + ], + hide: true, + }, + { + name: 'techRecord_adrDetails_tank_tankDetails_yearOfManufacture', + label: 'Tank Year of manufacture', + value: null, + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.NUMBER, + width: FormNodeWidth.XS, + groups: ['tank_details', 'dangerous_goods'], + validators: [ + { name: ValidatorNames.Max, args: 9999 }, + { name: ValidatorNames.Min, args: 1000 }, + { name: ValidatorNames.PastYear }, + { + name: ValidatorNames.RequiredIfEquals, + args: { + sibling: 'techRecord_adrDetails_vehicleDetails_type', + value: Object.values(ADRBodyType).filter( + (value) => value.includes('battery') || value.includes('tank') + ) as string[], + }, + }, + ], + hide: true, + }, + { + name: 'techRecord_adrDetails_tank_tankDetails_tankManufacturerSerialNo', + label: 'Manufacturer serial number', + value: null, + type: FormNodeTypes.CONTROL, + width: FormNodeWidth.L, + groups: ['tank_details', 'dangerous_goods'], + validators: [ + { name: ValidatorNames.MaxLength, args: 50 }, + { + name: ValidatorNames.RequiredIfEquals, + args: { + sibling: 'techRecord_adrDetails_vehicleDetails_type', + value: Object.values(ADRBodyType).filter( + (value) => value.includes('battery') || value.includes('tank') + ) as string[], + }, + }, + ], + hide: true, + }, + { + name: 'techRecord_adrDetails_tank_tankDetails_tankTypeAppNo', + label: 'Tank type approval number', + value: null, + type: FormNodeTypes.CONTROL, + width: FormNodeWidth.L, + groups: ['tank_details', 'dangerous_goods'], + validators: [ + { name: ValidatorNames.MaxLength, args: 65 }, + { + name: ValidatorNames.RequiredIfEquals, + args: { + sibling: 'techRecord_adrDetails_vehicleDetails_type', + value: Object.values(ADRBodyType).filter( + (value) => value.includes('battery') || value.includes('tank') + ) as string[], + }, + }, + ], + hide: true, + }, + { + name: 'techRecord_adrDetails_tank_tankDetails_tankCode', + label: 'Code', + value: null, + type: FormNodeTypes.CONTROL, + width: FormNodeWidth.L, + groups: ['tank_details', 'dangerous_goods'], + validators: [ + { name: ValidatorNames.MaxLength, args: 30 }, + { + name: ValidatorNames.RequiredIfEquals, + args: { + sibling: 'techRecord_adrDetails_vehicleDetails_type', + value: Object.values(ADRBodyType).filter( + (value) => value.includes('battery') || value.includes('tank') + ) as string[], + }, + }, + ], + hide: true, + }, + { + name: 'techRecord_adrDetails_tank_tankDetails_tankStatement_substancesPermitted', + label: 'Substances permitted', + width: FormNodeWidth.XS, + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.RADIO, + options: getOptionsFromEnum(ADRTankStatementSubstancePermitted), + groups: ['tank_details', 'dangerous_goods'], + hide: true, + validators: [ + { + name: ValidatorNames.RequiredIfEquals, + args: { + sibling: 'techRecord_adrDetails_vehicleDetails_type', + value: Object.values(ADRBodyType).filter( + (value) => value.includes('battery') || value.includes('tank') + ) as string[], + }, + }, + { + name: ValidatorNames.ShowGroupsWhenEqualTo, + args: { + values: [ADRTankStatementSubstancePermitted.UNDER_UN_NUMBER], + groups: ['statement_select'], + }, + }, + { + name: ValidatorNames.HideGroupsWhenEqualTo, + args: { + values: [ADRTankStatementSubstancePermitted.UNDER_TANK_CODE, null, undefined], + groups: ['statement_select', 'statement_select_hide'], + }, + }, + ], + }, + { + name: 'techRecord_adrDetails_tank_tankDetails_tankStatement_select', + label: 'Select', + width: FormNodeWidth.XS, + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.RADIO, + groups: ['statement_select', 'tank_details_hide', 'dangerous_goods'], + hide: true, + options: getOptionsFromEnum(ADRTankDetailsTankStatementSelect), + validators: [ + { + name: ValidatorNames.RequiredIfEquals, + args: { + sibling: 'techRecord_adrDetails_vehicleDetails_type', + value: Object.values(ADRBodyType).filter( + (value) => value.includes('battery') || value.includes('tank') + ) as string[], + }, + }, + { + name: ValidatorNames.ShowGroupsWhenIncludes, + args: { + values: [ADRTankDetailsTankStatementSelect.STATEMENT], + groups: ['statement'], + }, + }, + { + name: ValidatorNames.ShowGroupsWhenIncludes, + args: { + values: [ADRTankDetailsTankStatementSelect.PRODUCT_LIST], + groups: ['productList'], + }, + }, + { + name: ValidatorNames.HideGroupsWhenExcludes, + args: { + values: [ADRTankDetailsTankStatementSelect.STATEMENT], + groups: ['statement'], + }, + }, + { + name: ValidatorNames.HideGroupsWhenExcludes, + args: { + values: [ADRTankDetailsTankStatementSelect.PRODUCT_LIST], + groups: ['productList'], + }, + }, + ], + }, + { + name: 'techRecord_adrDetails_tank_tankDetails_tankStatement_statement', + label: 'Reference number', + type: FormNodeTypes.CONTROL, + groups: ['statement', 'statement_select_hide', 'tank_details_hide', 'dangerous_goods'], + hide: true, + customErrorMessage: 'Reference number is required when selecting Statement', + validators: [ + { name: ValidatorNames.MaxLength, args: 1500 }, + { + name: ValidatorNames.RequiredIfEquals, + args: { + sibling: 'techRecord_adrDetails_tank_tankDetails_tankStatement_select', + value: [ADRTankDetailsTankStatementSelect.STATEMENT], + }, + }, + ], + }, + { + name: 'techRecord_adrDetails_tank_tankDetails_tankStatement_productListRefNo', + label: 'Reference number', + type: FormNodeTypes.CONTROL, + groups: ['productList', 'statement_select_hide', 'tank_details_hide', 'dangerous_goods'], + hide: true, + customErrorMessage: 'Reference number or UN number is required when selecting Product List', + validators: [ + { name: ValidatorNames.MaxLength, args: 1500 }, + { + name: ValidatorNames.RequiredIfEquals, + args: { + sibling: 'techRecord_adrDetails_tank_tankDetails_tankStatement_productListUnNo', + value: [[], [null], [''], null, undefined], + }, + }, + ], + }, + { + name: 'techRecord_adrDetails_tank_tankDetails_tankStatement_productListUnNo', + label: 'UN number', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.CUSTOM, + viewType: FormNodeViewTypes.CUSTOM, + editComponent: AdrTankStatementUnNumberEditComponent, + viewComponent: AdrTankStatementUnNumberViewComponent, + groups: ['productList', 'statement_select_hide', 'tank_details_hide', 'dangerous_goods'], + hide: true, + customErrorMessage: 'Reference number or UN number is required when selecting Product List', + validators: [ + { + name: ValidatorNames.RequiredIfEquals, + args: { + sibling: 'techRecord_adrDetails_tank_tankDetails_tankStatement_productListRefNo', + value: [null, undefined, ''], + }, + }, + ], + }, + { + name: 'techRecord_adrDetails_tank_tankDetails_tankStatement_productList', + label: 'Additional Details', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.TEXTAREA, + groups: ['productList', 'statement_select_hide', 'tank_details_hide', 'dangerous_goods'], + hide: true, + validators: [{ name: ValidatorNames.MaxLength, args: 1500 }], + }, + { + name: 'techRecord_adrDetails_tank_tankDetails_specialProvisions', + label: 'Special Provisions', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.TEXTAREA, + hide: true, + validators: [{ name: ValidatorNames.MaxLength, args: 1024 }], + groups: ['dangerous_goods', 'tank_details'], + }, + { + name: 'tankInspectionsSectionTitle', + type: FormNodeTypes.TITLE, + label: 'Tank Inspections', + groups: ['tank_details', 'dangerous_goods'], + hide: true, + validators: [ + { + name: ValidatorNames.RequiredIfEquals, + args: { + sibling: 'techRecord_adrDetails_vehicleDetails_type', + value: Object.values(ADRBodyType).filter( + (value) => value.includes('battery') || value.includes('tank') + ) as string[], + }, + }, + ], + }, + { + name: 'tankInspectionsInitialView', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.CUSTOM, + viewComponent: AdrTankDetailsInitialInspectionViewComponent, + groups: ['tank_details', 'dangerous_goods'], + hide: true, + }, + { + name: 'techRecord_adrDetails_tank_tankDetails_tc2Details_tc2Type', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + label: 'TC2: Inspection type', + value: TC2Types.INITIAL, + hide: true, + groups: ['tank_details', 'dangerous_goods'], + validators: [], + }, + { + name: 'techRecord_adrDetails_tank_tankDetails_tc2Details_tc2IntermediateApprovalNo', + label: 'TC2: Certificate Number', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + hide: true, + groups: ['tank_details', 'dangerous_goods'], + validators: [{ name: ValidatorNames.MaxLength, args: 70 }], + }, + { + name: 'techRecord_adrDetails_tank_tankDetails_tc2Details_tc2IntermediateExpiryDate', + label: 'TC2: Expiry Date', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + isoDate: false, + hide: true, + groups: ['tank_details', 'dangerous_goods'], + validators: [], + }, + { + name: 'techRecord_adrDetails_tank_tankDetails_tc3Details', + label: 'Subsequent Inspections', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.CUSTOM, + viewComponent: AdrTankDetailsSubsequentInspectionsViewComponent, + editType: FormNodeEditTypes.CUSTOM, + editComponent: AdrTankDetailsSubsequentInspectionsEditComponent, + hide: true, + groups: ['tank_details', 'dangerous_goods'], + validators: [ + { + name: ValidatorNames.Tc3TestValidator, + args: { inspectionNumber: 0 }, + }, + ], + }, + { + name: 'techRecord_adrDetails_memosApply', + label: 'Memo 7/9 (3 month extension) applied', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.CHECKBOXGROUP, + groups: ['tank_details', 'dangerous_goods'], + hide: true, + options: [{ value: '07/09 3mth leak ext ', label: 'Yes' }], + validators: [], + }, + { + name: 'techRecord_adrDetails_m145Statement', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.CUSTOM, + viewComponent: AdrTankDetailsM145ViewComponent, + editType: FormNodeEditTypes.CHECKBOX, + groups: ['tank_details', 'dangerous_goods'], + hide: true, + validators: [], + }, + { + name: 'techRecord_adrDetails_listStatementApplicable', + label: 'Battery List Applicable', + width: FormNodeWidth.XS, + value: false, + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.RADIO, + options: [ + { value: true, label: 'Yes' }, + { value: false, label: 'No' }, + ], + validators: [ + { name: ValidatorNames.ShowGroupsWhenEqualTo, args: { values: [true], groups: ['battery_list_applicable'] } }, + { name: ValidatorNames.HideGroupsWhenEqualTo, args: { values: [false], groups: ['battery_list_applicable'] } }, + { + name: ValidatorNames.RequiredIfEquals, + args: { + sibling: 'techRecord_adrDetails_vehicleDetails_type', + value: Object.values(ADRBodyType).filter((value) => value.includes('battery')) as string[], + }, + }, + ], + hide: true, + groups: ['dangerous_goods', 'battery_list'], + }, + { + name: 'techRecord_adrDetails_batteryListNumber', + label: 'Reference number', + value: null, + type: FormNodeTypes.CONTROL, + width: FormNodeWidth.L, + groups: ['battery_list', 'battery_list_applicable', 'dangerous_goods'], + validators: [ + { name: ValidatorNames.MaxLength, args: 8 }, + { + name: ValidatorNames.RequiredIfEquals, + args: { + sibling: 'techRecord_adrDetails_listStatementApplicable', + value: [true], + }, + }, + ], + hide: true, + }, + { + name: 'DeclarationsSectionTitle', + label: 'Declarations seen', + type: FormNodeTypes.TITLE, + groups: ['declarations_details', 'dangerous_goods'], + hide: true, + }, + { + name: 'techRecord_adrDetails_brakeDeclarationsSeen', + label: 'Manufacturer brake declaration', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.CHECKBOX, + groups: ['declarations_details', 'dangerous_goods'], + value: false, + hide: true, + validators: [ + { + name: ValidatorNames.ShowGroupsWhenEqualTo, + args: { + values: [true], + groups: ['issuer_section'], + }, + }, + { + name: ValidatorNames.HideGroupsWhenEqualTo, + args: { + values: [false], + groups: ['issuer_section', 'weight_section'], + }, + }, + ], + }, + { + name: 'techRecord_adrDetails_brakeDeclarationIssuer', + label: 'Issuer', + type: FormNodeTypes.CONTROL, + hide: true, + groups: ['issuer_section', 'dangerous_goods'], + editType: FormNodeEditTypes.TEXTAREA, + validators: [{ name: ValidatorNames.MaxLength, args: 500 }], + }, + { + name: 'techRecord_adrDetails_brakeEndurance', + label: 'Brake endurance', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.CHECKBOX, - }, - { - name: 'techRecord_adrDetails_declarationsSeen', - label: 'Owner/operator declaration', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.CHECKBOX, - groups: ['declarations_details', 'dangerous_goods'], - hide: true, - }, - { - name: 'NewCertificateRequested', - label: 'New Certificate required', - type: FormNodeTypes.TITLE, - groups: ['dangerous_goods'], - hide: true, - }, - { - name: 'techRecord_adrDetails_newCertificateRequested', - label: 'Yes', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.CHECKBOX, - viewType: FormNodeViewTypes.CUSTOM, - viewComponent: AdrNewCertificateRequiredViewComponent, - value: false, - groups: ['dangerous_goods'], - hide: true, - }, - { - name: 'ExaminerNotesSectionTitle', - label: 'Additional Examiner Notes History', - type: FormNodeTypes.TITLE, - groups: ['adr_details', 'dangerous_goods'], - hide: true, - }, - { - name: 'techRecord_adrDetails_additionalExaminerNotes_note', - label: 'Additional Examiner Notes', - value: null, - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.TEXTAREA, - groups: ['adr_details', 'dangerous_goods'], - hide: true, - validators: [ - { name: ValidatorNames.MaxLength, args: 1024 }, - ], - }, - { - name: 'techRecord_adrDetails_additionalExaminerNotes', - label: 'Additional examiner notes history', - value: null, - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.CUSTOM, - viewComponent: AdrExaminerNotesHistoryViewComponent, - editType: FormNodeEditTypes.CUSTOM, - editComponent: AdrExaminerNotesHistoryEditComponent, - groups: ['adr_details', 'dangerous_goods'], - hide: true, - }, - { - name: 'techRecord_adrDetails_adrCertificateNotes', - label: 'ADR Certificate Notes', - value: null, - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.TEXTAREA, - viewType: FormNodeViewTypes.STRING, - groups: ['adrDetails', 'dangerous_goods'], - hide: true, - validators: [ - { name: ValidatorNames.MaxLength, args: 1500 }, - ], - }, - ], + groups: ['issuer_section', 'dangerous_goods'], + hide: true, + validators: [ + { + name: ValidatorNames.ShowGroupsWhenEqualTo, + args: { + values: [true], + groups: ['weight_section'], + }, + }, + { + name: ValidatorNames.HideGroupsWhenEqualTo, + args: { + values: [false, null, undefined], + groups: ['weight_section'], + }, + }, + ], + }, + { + name: 'techRecord_adrDetails_weight', + label: 'Weight (tonnes)', + type: FormNodeTypes.CONTROL, + width: FormNodeWidth.L, + groups: ['weight_section', 'dangerous_goods'], + hide: true, + validators: [ + { name: ValidatorNames.Max, args: 999999999 }, + { + name: ValidatorNames.RequiredIfNotHidden, + }, + ], + }, + { + name: 'techRecord_adrDetails_declarationsSeen', + label: 'Owner/operator declaration', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.CHECKBOX, + groups: ['declarations_details', 'dangerous_goods'], + hide: true, + }, + { + name: 'NewCertificateRequested', + label: 'New Certificate required', + type: FormNodeTypes.TITLE, + groups: ['dangerous_goods'], + hide: true, + }, + { + name: 'techRecord_adrDetails_newCertificateRequested', + label: 'Yes', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.CHECKBOX, + viewType: FormNodeViewTypes.CUSTOM, + viewComponent: AdrNewCertificateRequiredViewComponent, + value: false, + groups: ['dangerous_goods'], + hide: true, + }, + { + name: 'techRecord_adrDetails_additionalExaminerNotes_note', + label: 'Additional Examiner Notes', + value: null, + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.TEXTAREA, + groups: ['adr_details', 'dangerous_goods'], + hide: true, + validators: [{ name: ValidatorNames.MaxLength, args: 1024 }], + }, + { + name: 'techRecord_adrDetails_additionalExaminerNotes', + value: null, + label: 'Additional Examiner Notes History', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.CUSTOM, + viewComponent: AdrExaminerNotesHistoryViewComponent, + editType: FormNodeEditTypes.CUSTOM, + editComponent: AdrExaminerNotesHistoryEditComponent, + groups: ['adr_details', 'dangerous_goods'], + hide: true, + }, + { + name: 'techRecord_adrDetails_adrCertificateNotes', + label: 'ADR Certificate Notes', + value: null, + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.TEXTAREA, + viewType: FormNodeViewTypes.STRING, + groups: ['adrDetails', 'dangerous_goods'], + hide: true, + validators: [{ name: ValidatorNames.MaxLength, args: 1500 }], + }, + ], }; diff --git a/src/app/forms/templates/general/adr.template.ts b/src/app/forms/templates/general/adr.template.ts index 2ea993c830..79e51ea7da 100644 --- a/src/app/forms/templates/general/adr.template.ts +++ b/src/app/forms/templates/general/adr.template.ts @@ -1,837 +1,820 @@ -import { - ADRAdditionalNotesNumber, -} from '@dvsa/cvs-type-definitions/types/v3/tech-record/enums/adrAdditionalNotesNumber.enum.js'; +import { ADRAdditionalNotesNumber } from '@dvsa/cvs-type-definitions/types/v3/tech-record/enums/adrAdditionalNotesNumber.enum.js'; import { ADRBodyType } from '@dvsa/cvs-type-definitions/types/v3/tech-record/enums/adrBodyType.enum.js'; -import { - ADRCompatibilityGroupJ, -} from '@dvsa/cvs-type-definitions/types/v3/tech-record/enums/adrCompatibilityGroupJ.enum.js'; +import { ADRCompatibilityGroupJ } from '@dvsa/cvs-type-definitions/types/v3/tech-record/enums/adrCompatibilityGroupJ.enum.js'; import { ADRDangerousGood } from '@dvsa/cvs-type-definitions/types/v3/tech-record/enums/adrDangerousGood.enum.js'; -import { - ADRTankDetailsTankStatementSelect, -} from '@dvsa/cvs-type-definitions/types/v3/tech-record/enums/adrTankDetailsTankStatementSelect.enum.js'; -import { - ADRTankStatementSubstancePermitted, -} from '@dvsa/cvs-type-definitions/types/v3/tech-record/enums/adrTankStatementSubstancePermitted.js'; -import { - AdrExaminerNotesHistoryEditComponent, -} from '@forms/custom-sections/adr-examiner-notes-history-edit/adr-examiner-notes-history.component-edit'; -import { - AdrExaminerNotesHistoryViewComponent, -} from '@forms/custom-sections/adr-examiner-notes-history-view/adr-examiner-notes-history-view.component'; -import { - AdrNewCertificateRequiredViewComponent, -} from '@forms/custom-sections/adr-new-certificate-required-view/adr-new-certificate-required-view.component'; -import { - AdrTankDetailsInitialInspectionViewComponent, -} from '@forms/custom-sections/adr-tank-details-initial-inspection-view/adr-tank-details-initial-inspection-view.component'; -import { - AdrTankDetailsM145ViewComponent, -} from '@forms/custom-sections/adr-tank-details-m145-view/adr-tank-details-m145-view.component'; -import { - AdrTankDetailsSubsequentInspectionsEditComponent, -} from '@forms/custom-sections/adr-tank-details-subsequent-inspections-edit/adr-tank-details-subsequent-inspections-edit.component'; -import { - AdrTankDetailsSubsequentInspectionsViewComponent, -} from '@forms/custom-sections/adr-tank-details-subsequent-inspections-view/adr-tank-details-subsequent-inspections-view.component'; -import { - AdrTankStatementUnNumberEditComponent, -} from '@forms/custom-sections/adr-tank-statement-un-number-edit/adr-tank-statement-un-number-edit.component'; -import { - AdrTankStatementUnNumberViewComponent, -} from '@forms/custom-sections/adr-tank-statement-un-number-view/adr-tank-statement-un-number-view.component'; +import { ADRTankDetailsTankStatementSelect } from '@dvsa/cvs-type-definitions/types/v3/tech-record/enums/adrTankDetailsTankStatementSelect.enum.js'; +import { ADRTankStatementSubstancePermitted } from '@dvsa/cvs-type-definitions/types/v3/tech-record/enums/adrTankStatementSubstancePermitted.js'; +import { AdrExaminerNotesHistoryEditComponent } from '@forms/custom-sections/adr-examiner-notes-history-edit/adr-examiner-notes-history.component-edit'; +import { AdrExaminerNotesHistoryViewComponent } from '@forms/custom-sections/adr-examiner-notes-history-view/adr-examiner-notes-history-view.component'; +import { AdrNewCertificateRequiredViewComponent } from '@forms/custom-sections/adr-new-certificate-required-view/adr-new-certificate-required-view.component'; +import { AdrTankDetailsInitialInspectionViewComponent } from '@forms/custom-sections/adr-tank-details-initial-inspection-view/adr-tank-details-initial-inspection-view.component'; +import { AdrTankDetailsM145ViewComponent } from '@forms/custom-sections/adr-tank-details-m145-view/adr-tank-details-m145-view.component'; +import { AdrTankDetailsSubsequentInspectionsEditComponent } from '@forms/custom-sections/adr-tank-details-subsequent-inspections-edit/adr-tank-details-subsequent-inspections-edit.component'; +import { AdrTankDetailsSubsequentInspectionsViewComponent } from '@forms/custom-sections/adr-tank-details-subsequent-inspections-view/adr-tank-details-subsequent-inspections-view.component'; +import { AdrTankStatementUnNumberEditComponent } from '@forms/custom-sections/adr-tank-statement-un-number-edit/adr-tank-statement-un-number-edit.component'; +import { AdrTankStatementUnNumberViewComponent } from '@forms/custom-sections/adr-tank-statement-un-number-view/adr-tank-statement-un-number-view.component'; import { ValidatorNames } from '@forms/models/validators.enum'; import { getOptionsFromEnum } from '@forms/utils/enum-map'; import { AdrValidators } from '@forms/validators/adr/adr.validators'; import { TC2Types } from '@models/adr.enum'; import { - FormNode, - FormNodeEditTypes, - FormNodeTypes, - FormNodeViewTypes, - FormNodeWidth, + FormNode, + FormNodeEditTypes, + FormNodeTypes, + FormNodeViewTypes, + FormNodeWidth, } from '../../services/dynamic-form.types'; export const AdrTemplate: FormNode = { - name: 'adrSection', - type: FormNodeTypes.SECTION, - label: 'ADR', - children: [ - { - name: 'techRecord_adrDetails_dangerousGoods', - label: 'Able to carry dangerous goods', - width: FormNodeWidth.XS, - value: false, - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.RADIO, - options: [ - { value: true, label: 'Yes' }, - { value: false, label: 'No' }, - ], - validators: [ - { name: ValidatorNames.ShowGroupsWhenEqualTo, args: { values: [true], groups: ['dangerous_goods'] } }, - { name: ValidatorNames.HideGroupsWhenEqualTo, args: { values: [false], groups: ['dangerous_goods', 'dangerous_goods_hide'] } }, - { name: ValidatorNames.AddWarningForAdrField, args: 'By selecting this field it will delete all previous ADR field inputs' }, - ], - }, - { - name: 'applicationDetailsSectionTitle', - type: FormNodeTypes.TITLE, - label: 'Applicant Details', - groups: ['applicant_details', 'dangerous_goods'], - hide: true, - }, - { - name: 'techRecord_adrDetails_applicantDetails_name', - label: 'Name', - value: null, - width: FormNodeWidth.XXL, - type: FormNodeTypes.CONTROL, - validators: [ - { name: ValidatorNames.MaxLength, args: 150 }, - ], - groups: ['applicant_details', 'dangerous_goods'], - hide: true, - }, - { - name: 'techRecord_adrDetails_applicantDetails_street', - label: 'Street', - value: null, - width: FormNodeWidth.XXL, - type: FormNodeTypes.CONTROL, - validators: [{ name: ValidatorNames.MaxLength, args: 150 }], - groups: ['applicant_details', 'dangerous_goods'], - hide: true, - }, - { - name: 'techRecord_adrDetails_applicantDetails_town', - label: 'Town', - value: null, - width: FormNodeWidth.XXL, - type: FormNodeTypes.CONTROL, - validators: [{ name: ValidatorNames.MaxLength, args: 100 }], - groups: ['applicant_details', 'dangerous_goods'], - hide: true, - }, - { - name: 'techRecord_adrDetails_applicantDetails_city', - label: 'City', - value: null, - width: FormNodeWidth.XL, - type: FormNodeTypes.CONTROL, - validators: [{ name: ValidatorNames.MaxLength, args: 100 }], - groups: ['applicant_details', 'dangerous_goods'], - hide: true, - }, - { - name: 'techRecord_adrDetails_applicantDetails_postcode', - label: 'Postcode', - value: null, - width: FormNodeWidth.L, - type: FormNodeTypes.CONTROL, - validators: [{ name: ValidatorNames.MaxLength, args: 25 }], - groups: ['applicant_details', 'dangerous_goods'], - hide: true, - }, - { - name: 'adrDetailsSectionTitle', - type: FormNodeTypes.TITLE, - label: 'ADR Details', - groups: ['adr_details', 'dangerous_goods'], - hide: true, - }, - { - name: 'techRecord_adrDetails_vehicleDetails_type', - label: 'ADR body type', - type: FormNodeTypes.CONTROL, - width: FormNodeWidth.L, - editType: FormNodeEditTypes.SELECT, - groups: ['adr_details', 'dangerous_goods'], - hide: true, - options: getOptionsFromEnum(ADRBodyType), - validators: [ - { name: ValidatorNames.RequiredIfEquals, args: { sibling: 'techRecord_adrDetails_dangerousGoods', value: [true] } }, - { - name: ValidatorNames.ShowGroupsWhenIncludes, - args: { - values: Object.values(ADRBodyType).filter((value) => value.includes('battery') || value.includes('tank')) as string[], - groups: ['tank_details'], - }, - }, - { - name: ValidatorNames.HideGroupsWhenExcludes, - args: { - values: Object.values(ADRBodyType).filter((value) => value.includes('battery') || value.includes('tank')) as string[], - groups: ['tank_details', 'tank_details_hide'], - }, - }, - { - name: ValidatorNames.ShowGroupsWhenIncludes, - args: { - values: Object.values(ADRBodyType).filter((value) => value.includes('battery')) as string[], - groups: ['battery_list'], - }, - }, - { - name: ValidatorNames.HideGroupsWhenExcludes, - args: { - values: Object.values(ADRBodyType).filter((value) => value.includes('battery')) as string[], - groups: ['battery_list', 'battery_list_hide'], - }, - }, - ], - }, - { - name: 'techRecord_adrDetails_vehicleDetails_usedOnInternationalJourneys', - label: 'Vehicle used on international journeys', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.RADIO, - options: [ - { value: 'yes', label: 'Yes' }, - { value: 'no', label: 'No' }, - { value: 'n/a', label: 'Not applicable' }, - ], - hide: true, - groups: ['adr_details', 'dangerous_goods'], - }, - { - name: 'techRecord_adrDetails_vehicleDetails_approvalDate', - label: 'Date processed', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.DATE, - viewType: FormNodeViewTypes.DATE, - groups: ['adr_details', 'dangerous_goods'], - hide: true, - isoDate: false, - validators: [ - { name: ValidatorNames.RequiredIfEquals, args: { sibling: 'techRecord_adrDetails_dangerousGoods', value: [true] } }, - { name: ValidatorNames.PastDate }, - { name: ValidatorNames.DateIsInvalid }, - ], - }, - { - name: 'techRecord_adrDetails_permittedDangerousGoods', - label: 'Permitted dangerous goods', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.CHECKBOXGROUP, - groups: ['adr_details', 'dangerous_goods'], - hide: true, - options: getOptionsFromEnum(ADRDangerousGood), - validators: [ - { - name: ValidatorNames.ShowGroupsWhenIncludes, - args: { - values: [ADRDangerousGood.EXPLOSIVES_TYPE_2, ADRDangerousGood.EXPLOSIVES_TYPE_3], - groups: ['compatibility_group_j'], - }, - }, - { - name: ValidatorNames.HideGroupsWhenExcludes, - args: { - values: [ADRDangerousGood.EXPLOSIVES_TYPE_2, ADRDangerousGood.EXPLOSIVES_TYPE_3], - groups: ['compatibility_group_j'], - }, - }, - { name: ValidatorNames.RequiredIfEquals, args: { sibling: 'techRecord_adrDetails_dangerousGoods', value: [true] } }, - ], - }, - { - name: 'techRecord_adrDetails_compatibilityGroupJ', - label: 'Compatibility group J', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.RADIO, - groups: ['compatibility_group_j', 'adr_details', 'dangerous_goods'], - hide: true, - options: [ - { value: ADRCompatibilityGroupJ.I, label: 'Yes' }, - { value: ADRCompatibilityGroupJ.E, label: 'No' }, - ], - validators: [ - { - name: ValidatorNames.RequiredIfEquals, - args: { - sibling: 'techRecord_adrDetails_permittedDangerousGoods', - value: [ADRDangerousGood.EXPLOSIVES_TYPE_2, ADRDangerousGood.EXPLOSIVES_TYPE_3], - }, - }, - ], - }, - { - name: 'techRecord_adrDetails_additionalNotes_number', - label: 'Guidance notes', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.CHECKBOXGROUP, - groups: ['adr_details', 'dangerous_goods'], - hide: true, - width: FormNodeWidth.XS, - value: [], - options: getOptionsFromEnum(ADRAdditionalNotesNumber), - validators: [ - { name: ValidatorNames.RequiredIfEquals, args: { sibling: 'techRecord_adrDetails_dangerousGoods', value: [true] } }, - ], - }, - { - name: 'techRecord_adrDetails_adrTypeApprovalNo', - label: 'ADR type approval number', - value: '', - type: FormNodeTypes.CONTROL, - width: FormNodeWidth.L, - groups: ['adr_details', 'dangerous_goods'], - hide: true, - validators: [ - { name: ValidatorNames.MaxLength, args: 40 }, - ], - }, - { - name: 'tankDetailsSectionTitle', - type: FormNodeTypes.TITLE, - label: 'Tank Details', - groups: ['tank_details', 'dangerous_goods'], - hide: true, - }, - { - name: 'techRecord_adrDetails_tank_tankDetails_tankManufacturer', - label: 'Tank Make', - value: null, - type: FormNodeTypes.CONTROL, - width: FormNodeWidth.XXL, - groups: ['tank_details', 'dangerous_goods'], - validators: [ - { name: ValidatorNames.MaxLength, args: 70 }, - { - name: ValidatorNames.RequiredIfEquals, - args: { - sibling: 'techRecord_adrDetails_vehicleDetails_type', - value: Object.values(ADRBodyType).filter((value) => value.includes('battery') || value.includes('tank')) as string[], - }, - }, - ], - hide: true, - }, - { - name: 'techRecord_adrDetails_tank_tankDetails_yearOfManufacture', - label: 'Tank Year of manufacture', - value: null, - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.NUMBER, - width: FormNodeWidth.XS, - groups: ['tank_details', 'dangerous_goods'], - validators: [ - { name: ValidatorNames.Max, args: 9999 }, - { name: ValidatorNames.Min, args: 1000 }, - { name: ValidatorNames.PastYear }, - { - name: ValidatorNames.RequiredIfEquals, - args: { - sibling: 'techRecord_adrDetails_vehicleDetails_type', - value: Object.values(ADRBodyType).filter((value) => value.includes('battery') || value.includes('tank')) as string[], - }, - }, - ], - hide: true, - }, - { - name: 'techRecord_adrDetails_tank_tankDetails_tankManufacturerSerialNo', - label: 'Manufacturer serial number', - value: null, - type: FormNodeTypes.CONTROL, - width: FormNodeWidth.L, - groups: ['tank_details', 'dangerous_goods'], - validators: [ - { name: ValidatorNames.MaxLength, args: 50 }, - { - name: ValidatorNames.RequiredIfEquals, - args: { - sibling: 'techRecord_adrDetails_vehicleDetails_type', - value: Object.values(ADRBodyType).filter((value) => value.includes('battery') || value.includes('tank')) as string[], - }, - }, - ], - hide: true, - }, - { - name: 'techRecord_adrDetails_tank_tankDetails_tankTypeAppNo', - label: 'Tank type approval number', - value: null, - type: FormNodeTypes.CONTROL, - width: FormNodeWidth.L, - groups: ['tank_details', 'dangerous_goods'], - validators: [ - { name: ValidatorNames.MaxLength, args: 65 }, - { - name: ValidatorNames.RequiredIfEquals, - args: { - sibling: 'techRecord_adrDetails_vehicleDetails_type', - value: Object.values(ADRBodyType).filter((value) => value.includes('battery') || value.includes('tank')) as string[], - }, - }, - ], - hide: true, - }, - { - name: 'techRecord_adrDetails_tank_tankDetails_tankCode', - label: 'Code', - value: null, - type: FormNodeTypes.CONTROL, - width: FormNodeWidth.L, - groups: ['tank_details', 'dangerous_goods'], - validators: [ - { name: ValidatorNames.MaxLength, args: 30 }, - { - name: ValidatorNames.RequiredIfEquals, - args: { - sibling: 'techRecord_adrDetails_vehicleDetails_type', - value: Object.values(ADRBodyType).filter((value) => value.includes('battery') || value.includes('tank')) as string[], - }, - }, - ], - hide: true, - }, - { - name: 'techRecord_adrDetails_tank_tankDetails_tankStatement_substancesPermitted', - label: 'Substances permitted', - width: FormNodeWidth.XS, - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.RADIO, - options: getOptionsFromEnum(ADRTankStatementSubstancePermitted), - groups: ['tank_details', 'dangerous_goods'], - hide: true, - validators: [ - { - name: ValidatorNames.RequiredIfEquals, - args: { - sibling: 'techRecord_adrDetails_vehicleDetails_type', - value: Object.values(ADRBodyType).filter((value) => value.includes('battery') || value.includes('tank')) as string[], - }, - }, - { - name: ValidatorNames.ShowGroupsWhenEqualTo, - args: { - values: [ADRTankStatementSubstancePermitted.UNDER_UN_NUMBER], - groups: ['statement_select'], - }, - }, - { - name: ValidatorNames.HideGroupsWhenEqualTo, - args: { - values: [ADRTankStatementSubstancePermitted.UNDER_TANK_CODE, null, undefined], - groups: ['statement_select', 'statement_select_hide'], - }, - }, - ], - }, - { - name: 'techRecord_adrDetails_tank_tankDetails_tankStatement_select', - label: 'Select', - width: FormNodeWidth.XS, - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.RADIO, - groups: ['statement_select', 'tank_details_hide', 'dangerous_goods'], - hide: true, - options: getOptionsFromEnum(ADRTankDetailsTankStatementSelect), - validators: [ - { - name: ValidatorNames.RequiredIfEquals, - args: { - sibling: 'techRecord_adrDetails_tank_tankDetails_tankStatement_substancesPermitted', - value: [ADRTankStatementSubstancePermitted.UNDER_UN_NUMBER], - }, - }, - { - name: ValidatorNames.ShowGroupsWhenIncludes, - args: { - values: [ADRTankDetailsTankStatementSelect.STATEMENT], - groups: ['statement'], - }, - }, - { - name: ValidatorNames.ShowGroupsWhenIncludes, - args: { - values: [ADRTankDetailsTankStatementSelect.PRODUCT_LIST], - groups: ['productList'], - }, - }, - { - name: ValidatorNames.HideGroupsWhenExcludes, - args: { - values: [ADRTankDetailsTankStatementSelect.STATEMENT], - groups: ['statement'], - }, - }, - { - name: ValidatorNames.HideGroupsWhenExcludes, - args: { - values: [ADRTankDetailsTankStatementSelect.PRODUCT_LIST], - groups: ['productList'], - }, - }, - ], - }, - { - name: 'techRecord_adrDetails_tank_tankDetails_tankStatement_statement', - label: 'Reference number', - type: FormNodeTypes.CONTROL, - groups: ['statement', 'statement_select_hide', 'tank_details_hide', 'dangerous_goods'], - hide: true, - validators: [ - { name: ValidatorNames.MaxLength, args: 1500 }, - ], - }, - { - name: 'techRecord_adrDetails_tank_tankDetails_tankStatement_productListRefNo', - label: 'Reference number', - type: FormNodeTypes.CONTROL, - groups: ['productList', 'statement_select_hide', 'tank_details_hide', 'dangerous_goods'], - hide: true, - validators: [ - { name: ValidatorNames.MaxLength, args: 1500 }, - { name: ValidatorNames.Custom, args: AdrValidators.validateProductListRefNo }], - }, - { - name: 'techRecord_adrDetails_tank_tankDetails_tankStatement_productListUnNo', - label: 'UN number', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.CUSTOM, - viewType: FormNodeViewTypes.CUSTOM, - editComponent: AdrTankStatementUnNumberEditComponent, - viewComponent: AdrTankStatementUnNumberViewComponent, - groups: ['productList', 'statement_select_hide', 'tank_details_hide', 'dangerous_goods'], - hide: true, - validators: [ - { name: ValidatorNames.Custom, args: AdrValidators.validateProductListUNNumbers }, - ], - }, - { - name: 'techRecord_adrDetails_tank_tankDetails_tankStatement_productList', - label: 'Additional Details', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.TEXTAREA, - groups: ['productList', 'statement_select_hide', 'tank_details_hide', 'dangerous_goods'], - hide: true, - validators: [ - { name: ValidatorNames.MaxLength, args: 1500 }, - ], - }, - { - name: 'techRecord_adrDetails_tank_tankDetails_specialProvisions', - label: 'Special Provisions', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.TEXTAREA, - hide: true, - validators: [ - { name: ValidatorNames.MaxLength, args: 1024 }, - ], - groups: ['dangerous_goods', 'tank_details'], - }, - { - name: 'tankInspectionsSectionTitle', - type: FormNodeTypes.TITLE, - label: 'Tank Inspections', - groups: ['tank_details', 'dangerous_goods'], - hide: true, - validators: [ - { - name: ValidatorNames.RequiredIfEquals, - args: { - sibling: 'techRecord_adrDetails_vehicleDetails_type', - value: Object.values(ADRBodyType).filter((value) => value.includes('battery') || value.includes('tank')) as string[], - }, - }, - ], - }, - { - name: 'tankInspectionsInitialView', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.CUSTOM, - viewComponent: AdrTankDetailsInitialInspectionViewComponent, - groups: ['tank_details', 'dangerous_goods'], - hide: true, - }, - { - name: 'techRecord_adrDetails_tank_tankDetails_tc2Details_tc2Type', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - label: 'TC2: Inspection type', - value: TC2Types.INITIAL, - hide: true, - groups: ['tank_details', 'dangerous_goods'], - }, - { - name: 'techRecord_adrDetails_tank_tankDetails_tc2Details_tc2IntermediateApprovalNo', - label: 'TC2: Certificate Number', - viewType: FormNodeViewTypes.HIDDEN, - type: FormNodeTypes.CONTROL, - hide: true, - groups: ['tank_details', 'dangerous_goods'], - validators: [ - { name: ValidatorNames.MaxLength, args: 70 }, - ], - }, - { - name: 'techRecord_adrDetails_tank_tankDetails_tc2Details_tc2IntermediateExpiryDate', - label: 'TC2: Expiry Date', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.DATE, - isoDate: false, - hide: true, - groups: ['tank_details', 'dangerous_goods'], - validators: [], - }, - { - name: 'techRecord_adrDetails_tank_tankDetails_tc3Details', - label: 'Subsequent Inspections', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.CUSTOM, - viewComponent: AdrTankDetailsSubsequentInspectionsViewComponent, - editType: FormNodeEditTypes.CUSTOM, - editComponent: AdrTankDetailsSubsequentInspectionsEditComponent, - hide: true, - groups: ['tank_details', 'dangerous_goods'], - validators: [ - { - name: ValidatorNames.Tc3TestValidator, - args: { inspectionNumber: 0 }, - }, - ], - }, - { - name: 'techRecord_adrDetails_memosApply', - label: 'Memo 7/9 (3 month extension) applied', - hint: 'Only applicable for vehicles used on national journeys', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.CHECKBOXGROUP, - groups: ['tank_details', 'dangerous_goods'], - hide: true, - options: [ - { value: '07/09 3mth leak ext ', label: 'Yes' }, - ], - validators: [], - }, - { - name: 'M15Subtitle', - label: 'M145', - type: FormNodeTypes.SUBTITLE, - viewType: FormNodeViewTypes.HIDDEN, - groups: ['tank_details', 'dangerous_goods'], - hide: true, - }, - { - name: 'techRecord_adrDetails_m145Statement', - label: 'Yes', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.CUSTOM, - viewComponent: AdrTankDetailsM145ViewComponent, - editType: FormNodeEditTypes.CHECKBOX, - groups: ['tank_details', 'dangerous_goods'], - hide: true, - validators: [], - }, - { - name: 'techRecord_adrDetails_listStatementApplicable', - label: 'Battery List Applicable', - width: FormNodeWidth.XS, - value: false, - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.RADIO, - options: [ - { value: true, label: 'Yes' }, - { value: false, label: 'No' }, - ], - validators: [ - { name: ValidatorNames.ShowGroupsWhenEqualTo, args: { values: [true], groups: ['battery_list_applicable'] } }, - { name: ValidatorNames.HideGroupsWhenEqualTo, args: { values: [false, undefined, null], groups: ['battery_list_applicable'] } }, - { - name: ValidatorNames.RequiredIfEquals, - args: { - sibling: 'techRecord_adrDetails_vehicleDetails_type', - value: Object.values(ADRBodyType).filter((value) => value.includes('battery')) as string[], - }, - }, - ], - hide: true, - groups: ['dangerous_goods', 'battery_list'], - }, - { - name: 'techRecord_adrDetails_batteryListNumber', - label: 'Reference number', - value: null, - type: FormNodeTypes.CONTROL, - width: FormNodeWidth.L, - groups: ['battery_list_applicable', 'battery_list_hide', 'dangerous_goods'], - validators: [ - { name: ValidatorNames.MaxLength, args: 8 }, - { - name: ValidatorNames.RequiredIfEquals, - args: { - sibling: 'techRecord_adrDetails_listStatementApplicable', - value: [true], - }, - }, - ], - hide: true, - }, - { - name: 'DeclarationsSectionTitle', - label: 'Declarations seen', - type: FormNodeTypes.TITLE, - groups: ['declarations_details', 'dangerous_goods'], - hide: true, - }, - { - name: 'techRecord_adrDetails_brakeDeclarationsSeen', - label: 'Manufacturer brake declaration', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.CHECKBOX, - groups: ['declarations_details', 'dangerous_goods'], - value: false, - hide: true, - validators: [ - { - name: ValidatorNames.ShowGroupsWhenEqualTo, - args: { - values: [true], - groups: ['issuer_section'], - }, - }, - { - name: ValidatorNames.HideGroupsWhenEqualTo, - args: { - values: [false], - groups: ['issuer_section', 'issuer_section_hide'], - }, - }, - ], - - }, - { - name: 'techRecord_adrDetails_brakeDeclarationIssuer', - label: 'Issuer', - type: FormNodeTypes.CONTROL, - hide: true, - groups: ['issuer_section', 'dangerous_goods_hide'], - editType: FormNodeEditTypes.TEXTAREA, - validators: [ - { name: ValidatorNames.MaxLength, args: 500 }, - ], - }, - { - name: 'techRecord_adrDetails_brakeEndurance', - label: 'Brake endurance', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.CHECKBOX, - groups: ['issuer_section', 'dangerous_goods_hide'], - hide: true, - validators: [ - { - name: ValidatorNames.ShowGroupsWhenEqualTo, - args: { - values: [true], - groups: ['weight_section'], - }, - }, - { - name: ValidatorNames.HideGroupsWhenEqualTo, - args: { - values: [false, null, undefined], - groups: ['weight_section'], - }, - }, - ], - - }, - { - name: 'techRecord_adrDetails_weight', - label: 'Weight (tonnes)', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.NUMBER, - enableDecimals: true, - width: FormNodeWidth.L, - groups: ['weight_section', 'issuer_section_hide', 'dangerous_goods_hide'], - hide: true, - validators: [ - { name: ValidatorNames.Max, args: 99999999 }, - { - name: ValidatorNames.RequiredIfNotHidden, - }, - { - name: ValidatorNames.CustomPattern, - args: ['^\\d*(\\.\\d{0,2})?$', 'Max 2 decimal places'], - }, - ], - }, - { - name: 'techRecord_adrDetails_declarationsSeen', - label: 'Owner/operator declaration', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.CHECKBOX, - groups: ['declarations_details', 'dangerous_goods'], - hide: true, - }, - { - name: 'NewCertificateRequested', - label: 'New Certificate required', - type: FormNodeTypes.TITLE, - groups: ['dangerous_goods'], - hide: true, - }, - { - name: 'techRecord_adrDetails_newCertificateRequested', - label: 'Yes', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.CUSTOM, - viewComponent: AdrNewCertificateRequiredViewComponent, - editType: FormNodeEditTypes.CHECKBOX, - value: false, - groups: ['dangerous_goods'], - hide: true, - }, - { - name: 'ExaminerNotesSectionTitle', - label: 'Additional Examiner Notes History', - type: FormNodeTypes.TITLE, - groups: ['adr_details', 'dangerous_goods'], - hide: true, - }, - { - name: 'techRecord_adrDetails_additionalExaminerNotes_note', - label: 'Additional Examiner Notes', - hint: 'Will not be present on the ADR certificate', - value: null, - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.TEXTAREA, - viewType: FormNodeViewTypes.HIDDEN, - groups: ['adr_details', 'dangerous_goods'], - hide: true, - validators: [ - { name: ValidatorNames.MaxLength, args: 1024 }, - ], - }, - { - name: 'techRecord_adrDetails_additionalExaminerNotes', - label: 'Additional examiner notes history', - value: null, - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.CUSTOM, - viewComponent: AdrExaminerNotesHistoryViewComponent, - editType: FormNodeEditTypes.CUSTOM, - editComponent: AdrExaminerNotesHistoryEditComponent, - groups: ['adr_details', 'dangerous_goods'], - hide: true, - }, - { - name: 'techRecord_adrDetails_adrCertificateNotes', - label: 'ADR Certificate Notes', - value: null, - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.TEXTAREA, - viewType: FormNodeViewTypes.STRING, - groups: ['adrDetails', 'dangerous_goods'], - hide: true, - validators: [ - { name: ValidatorNames.MaxLength, args: 1500 }, - ], - }, - ], + name: 'adrSection', + type: FormNodeTypes.SECTION, + label: 'ADR', + children: [ + { + name: 'techRecord_adrDetails_dangerousGoods', + label: 'Able to carry dangerous goods', + width: FormNodeWidth.XS, + value: false, + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.RADIO, + options: [ + { value: true, label: 'Yes' }, + { value: false, label: 'No' }, + ], + validators: [ + { name: ValidatorNames.ShowGroupsWhenEqualTo, args: { values: [true], groups: ['dangerous_goods'] } }, + { + name: ValidatorNames.HideGroupsWhenEqualTo, + args: { values: [false], groups: ['dangerous_goods', 'dangerous_goods_hide'] }, + }, + { + name: ValidatorNames.AddWarningForAdrField, + args: 'By selecting this field it will delete all previous ADR field inputs', + }, + ], + }, + { + name: 'applicationDetailsSectionTitle', + type: FormNodeTypes.TITLE, + label: 'Applicant Details', + groups: ['applicant_details', 'dangerous_goods'], + hide: true, + }, + { + name: 'techRecord_adrDetails_applicantDetails_name', + label: 'Name', + value: null, + width: FormNodeWidth.XXL, + type: FormNodeTypes.CONTROL, + validators: [{ name: ValidatorNames.MaxLength, args: 150 }], + groups: ['applicant_details', 'dangerous_goods'], + hide: true, + }, + { + name: 'techRecord_adrDetails_applicantDetails_street', + label: 'Street', + value: null, + width: FormNodeWidth.XXL, + type: FormNodeTypes.CONTROL, + validators: [{ name: ValidatorNames.MaxLength, args: 150 }], + groups: ['applicant_details', 'dangerous_goods'], + hide: true, + }, + { + name: 'techRecord_adrDetails_applicantDetails_town', + label: 'Town', + value: null, + width: FormNodeWidth.XXL, + type: FormNodeTypes.CONTROL, + validators: [{ name: ValidatorNames.MaxLength, args: 100 }], + groups: ['applicant_details', 'dangerous_goods'], + hide: true, + }, + { + name: 'techRecord_adrDetails_applicantDetails_city', + label: 'City', + value: null, + width: FormNodeWidth.XL, + type: FormNodeTypes.CONTROL, + validators: [{ name: ValidatorNames.MaxLength, args: 100 }], + groups: ['applicant_details', 'dangerous_goods'], + hide: true, + }, + { + name: 'techRecord_adrDetails_applicantDetails_postcode', + label: 'Postcode', + value: null, + width: FormNodeWidth.L, + type: FormNodeTypes.CONTROL, + validators: [{ name: ValidatorNames.MaxLength, args: 25 }], + groups: ['applicant_details', 'dangerous_goods'], + hide: true, + }, + { + name: 'adrDetailsSectionTitle', + type: FormNodeTypes.TITLE, + label: 'ADR Details', + groups: ['adr_details', 'dangerous_goods'], + hide: true, + }, + { + name: 'techRecord_adrDetails_vehicleDetails_type', + label: 'ADR body type', + type: FormNodeTypes.CONTROL, + width: FormNodeWidth.L, + editType: FormNodeEditTypes.SELECT, + groups: ['adr_details', 'dangerous_goods'], + hide: true, + options: getOptionsFromEnum(ADRBodyType), + validators: [ + { + name: ValidatorNames.RequiredIfEquals, + args: { sibling: 'techRecord_adrDetails_dangerousGoods', value: [true] }, + }, + { + name: ValidatorNames.ShowGroupsWhenIncludes, + args: { + values: Object.values(ADRBodyType).filter( + (value) => value.includes('battery') || value.includes('tank') + ) as string[], + groups: ['tank_details'], + }, + }, + { + name: ValidatorNames.HideGroupsWhenExcludes, + args: { + values: Object.values(ADRBodyType).filter( + (value) => value.includes('battery') || value.includes('tank') + ) as string[], + groups: ['tank_details', 'tank_details_hide'], + }, + }, + { + name: ValidatorNames.ShowGroupsWhenIncludes, + args: { + values: Object.values(ADRBodyType).filter((value) => value.includes('battery')) as string[], + groups: ['battery_list'], + }, + }, + { + name: ValidatorNames.HideGroupsWhenExcludes, + args: { + values: Object.values(ADRBodyType).filter((value) => value.includes('battery')) as string[], + groups: ['battery_list', 'battery_list_hide'], + }, + }, + ], + }, + { + name: 'techRecord_adrDetails_vehicleDetails_usedOnInternationalJourneys', + label: 'Vehicle used on international journeys', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.RADIO, + options: [ + { value: 'yes', label: 'Yes' }, + { value: 'no', label: 'No' }, + { value: 'n/a', label: 'Not applicable' }, + ], + hide: true, + groups: ['adr_details', 'dangerous_goods'], + }, + { + name: 'techRecord_adrDetails_vehicleDetails_approvalDate', + label: 'Date processed', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.DATE, + viewType: FormNodeViewTypes.DATE, + groups: ['adr_details', 'dangerous_goods'], + hide: true, + isoDate: false, + validators: [ + { + name: ValidatorNames.RequiredIfEquals, + args: { sibling: 'techRecord_adrDetails_dangerousGoods', value: [true] }, + }, + { name: ValidatorNames.PastDate }, + { name: ValidatorNames.DateIsInvalid }, + ], + }, + { + name: 'techRecord_adrDetails_permittedDangerousGoods', + label: 'Permitted dangerous goods', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.CHECKBOXGROUP, + groups: ['adr_details', 'dangerous_goods'], + hide: true, + options: getOptionsFromEnum(ADRDangerousGood), + validators: [ + { + name: ValidatorNames.ShowGroupsWhenIncludes, + args: { + values: [ADRDangerousGood.EXPLOSIVES_TYPE_2, ADRDangerousGood.EXPLOSIVES_TYPE_3], + groups: ['compatibility_group_j'], + }, + }, + { + name: ValidatorNames.HideGroupsWhenExcludes, + args: { + values: [ADRDangerousGood.EXPLOSIVES_TYPE_2, ADRDangerousGood.EXPLOSIVES_TYPE_3], + groups: ['compatibility_group_j'], + }, + }, + { + name: ValidatorNames.RequiredIfEquals, + args: { sibling: 'techRecord_adrDetails_dangerousGoods', value: [true] }, + }, + ], + }, + { + name: 'techRecord_adrDetails_compatibilityGroupJ', + label: 'Compatibility group J', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.RADIO, + groups: ['compatibility_group_j', 'adr_details', 'dangerous_goods'], + hide: true, + options: [ + { value: ADRCompatibilityGroupJ.I, label: 'Yes' }, + { value: ADRCompatibilityGroupJ.E, label: 'No' }, + ], + validators: [ + { + name: ValidatorNames.RequiredIfEquals, + args: { + sibling: 'techRecord_adrDetails_permittedDangerousGoods', + value: [ADRDangerousGood.EXPLOSIVES_TYPE_2, ADRDangerousGood.EXPLOSIVES_TYPE_3], + }, + }, + ], + }, + { + name: 'techRecord_adrDetails_additionalNotes_number', + label: 'Guidance notes', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.CHECKBOXGROUP, + groups: ['adr_details', 'dangerous_goods'], + hide: true, + width: FormNodeWidth.XS, + value: [], + options: getOptionsFromEnum(ADRAdditionalNotesNumber), + validators: [ + { + name: ValidatorNames.RequiredIfEquals, + args: { sibling: 'techRecord_adrDetails_dangerousGoods', value: [true] }, + }, + ], + }, + { + name: 'techRecord_adrDetails_adrTypeApprovalNo', + label: 'ADR type approval number', + value: '', + type: FormNodeTypes.CONTROL, + width: FormNodeWidth.L, + groups: ['adr_details', 'dangerous_goods'], + hide: true, + validators: [{ name: ValidatorNames.MaxLength, args: 40 }], + }, + { + name: 'tankDetailsSectionTitle', + type: FormNodeTypes.TITLE, + label: 'Tank Details', + groups: ['tank_details', 'dangerous_goods'], + hide: true, + }, + { + name: 'techRecord_adrDetails_tank_tankDetails_tankManufacturer', + label: 'Tank Make', + value: null, + type: FormNodeTypes.CONTROL, + width: FormNodeWidth.XXL, + groups: ['tank_details', 'dangerous_goods'], + validators: [ + { name: ValidatorNames.MaxLength, args: 70 }, + { + name: ValidatorNames.RequiredIfEquals, + args: { + sibling: 'techRecord_adrDetails_vehicleDetails_type', + value: Object.values(ADRBodyType).filter( + (value) => value.includes('battery') || value.includes('tank') + ) as string[], + }, + }, + ], + hide: true, + }, + { + name: 'techRecord_adrDetails_tank_tankDetails_yearOfManufacture', + label: 'Tank Year of manufacture', + value: null, + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.NUMBER, + width: FormNodeWidth.XS, + groups: ['tank_details', 'dangerous_goods'], + validators: [ + { name: ValidatorNames.Max, args: 9999 }, + { name: ValidatorNames.Min, args: 1000 }, + { name: ValidatorNames.PastYear }, + { + name: ValidatorNames.RequiredIfEquals, + args: { + sibling: 'techRecord_adrDetails_vehicleDetails_type', + value: Object.values(ADRBodyType).filter( + (value) => value.includes('battery') || value.includes('tank') + ) as string[], + }, + }, + ], + hide: true, + }, + { + name: 'techRecord_adrDetails_tank_tankDetails_tankManufacturerSerialNo', + label: 'Manufacturer serial number', + value: null, + type: FormNodeTypes.CONTROL, + width: FormNodeWidth.L, + groups: ['tank_details', 'dangerous_goods'], + validators: [ + { name: ValidatorNames.MaxLength, args: 50 }, + { + name: ValidatorNames.RequiredIfEquals, + args: { + sibling: 'techRecord_adrDetails_vehicleDetails_type', + value: Object.values(ADRBodyType).filter( + (value) => value.includes('battery') || value.includes('tank') + ) as string[], + }, + }, + ], + hide: true, + }, + { + name: 'techRecord_adrDetails_tank_tankDetails_tankTypeAppNo', + label: 'Tank type approval number', + value: null, + type: FormNodeTypes.CONTROL, + width: FormNodeWidth.L, + groups: ['tank_details', 'dangerous_goods'], + validators: [ + { name: ValidatorNames.MaxLength, args: 65 }, + { + name: ValidatorNames.RequiredIfEquals, + args: { + sibling: 'techRecord_adrDetails_vehicleDetails_type', + value: Object.values(ADRBodyType).filter( + (value) => value.includes('battery') || value.includes('tank') + ) as string[], + }, + }, + ], + hide: true, + }, + { + name: 'techRecord_adrDetails_tank_tankDetails_tankCode', + label: 'Code', + value: null, + type: FormNodeTypes.CONTROL, + width: FormNodeWidth.L, + groups: ['tank_details', 'dangerous_goods'], + validators: [ + { name: ValidatorNames.MaxLength, args: 30 }, + { + name: ValidatorNames.RequiredIfEquals, + args: { + sibling: 'techRecord_adrDetails_vehicleDetails_type', + value: Object.values(ADRBodyType).filter( + (value) => value.includes('battery') || value.includes('tank') + ) as string[], + }, + }, + ], + hide: true, + }, + { + name: 'techRecord_adrDetails_tank_tankDetails_tankStatement_substancesPermitted', + label: 'Substances permitted', + width: FormNodeWidth.XS, + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.RADIO, + options: getOptionsFromEnum(ADRTankStatementSubstancePermitted), + groups: ['tank_details', 'dangerous_goods'], + hide: true, + validators: [ + { + name: ValidatorNames.RequiredIfEquals, + args: { + sibling: 'techRecord_adrDetails_vehicleDetails_type', + value: Object.values(ADRBodyType).filter( + (value) => value.includes('battery') || value.includes('tank') + ) as string[], + }, + }, + { + name: ValidatorNames.ShowGroupsWhenEqualTo, + args: { + values: [ADRTankStatementSubstancePermitted.UNDER_UN_NUMBER], + groups: ['statement_select'], + }, + }, + { + name: ValidatorNames.HideGroupsWhenEqualTo, + args: { + values: [ADRTankStatementSubstancePermitted.UNDER_TANK_CODE, null, undefined], + groups: ['statement_select', 'statement_select_hide'], + }, + }, + ], + }, + { + name: 'techRecord_adrDetails_tank_tankDetails_tankStatement_select', + label: 'Select', + width: FormNodeWidth.XS, + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.RADIO, + groups: ['statement_select', 'tank_details_hide', 'dangerous_goods'], + hide: true, + options: getOptionsFromEnum(ADRTankDetailsTankStatementSelect), + validators: [ + { + name: ValidatorNames.RequiredIfEquals, + args: { + sibling: 'techRecord_adrDetails_tank_tankDetails_tankStatement_substancesPermitted', + value: [ADRTankStatementSubstancePermitted.UNDER_UN_NUMBER], + }, + }, + { + name: ValidatorNames.ShowGroupsWhenIncludes, + args: { + values: [ADRTankDetailsTankStatementSelect.STATEMENT], + groups: ['statement'], + }, + }, + { + name: ValidatorNames.ShowGroupsWhenIncludes, + args: { + values: [ADRTankDetailsTankStatementSelect.PRODUCT_LIST], + groups: ['productList'], + }, + }, + { + name: ValidatorNames.HideGroupsWhenExcludes, + args: { + values: [ADRTankDetailsTankStatementSelect.STATEMENT], + groups: ['statement'], + }, + }, + { + name: ValidatorNames.HideGroupsWhenExcludes, + args: { + values: [ADRTankDetailsTankStatementSelect.PRODUCT_LIST], + groups: ['productList'], + }, + }, + ], + }, + { + name: 'techRecord_adrDetails_tank_tankDetails_tankStatement_statement', + label: 'Reference number', + type: FormNodeTypes.CONTROL, + groups: ['statement', 'statement_select_hide', 'tank_details_hide', 'dangerous_goods'], + hide: true, + validators: [{ name: ValidatorNames.MaxLength, args: 1500 }], + }, + { + name: 'techRecord_adrDetails_tank_tankDetails_tankStatement_productListRefNo', + label: 'Reference number', + type: FormNodeTypes.CONTROL, + groups: ['productList', 'statement_select_hide', 'tank_details_hide', 'dangerous_goods'], + hide: true, + validators: [ + { name: ValidatorNames.MaxLength, args: 1500 }, + { name: ValidatorNames.Custom, args: AdrValidators.validateProductListRefNo }, + ], + }, + { + name: 'techRecord_adrDetails_tank_tankDetails_tankStatement_productListUnNo', + label: 'UN number', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.CUSTOM, + viewType: FormNodeViewTypes.CUSTOM, + editComponent: AdrTankStatementUnNumberEditComponent, + viewComponent: AdrTankStatementUnNumberViewComponent, + groups: ['productList', 'statement_select_hide', 'tank_details_hide', 'dangerous_goods'], + hide: true, + validators: [{ name: ValidatorNames.Custom, args: AdrValidators.validateProductListUNNumbers }], + }, + { + name: 'techRecord_adrDetails_tank_tankDetails_tankStatement_productList', + label: 'Additional Details', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.TEXTAREA, + groups: ['productList', 'statement_select_hide', 'tank_details_hide', 'dangerous_goods'], + hide: true, + validators: [{ name: ValidatorNames.MaxLength, args: 1500 }], + }, + { + name: 'techRecord_adrDetails_tank_tankDetails_specialProvisions', + label: 'Special Provisions', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.TEXTAREA, + hide: true, + validators: [{ name: ValidatorNames.MaxLength, args: 1024 }], + groups: ['dangerous_goods', 'tank_details'], + }, + { + name: 'tankInspectionsSectionTitle', + type: FormNodeTypes.TITLE, + label: 'Tank Inspections', + groups: ['tank_details', 'dangerous_goods'], + hide: true, + validators: [ + { + name: ValidatorNames.RequiredIfEquals, + args: { + sibling: 'techRecord_adrDetails_vehicleDetails_type', + value: Object.values(ADRBodyType).filter( + (value) => value.includes('battery') || value.includes('tank') + ) as string[], + }, + }, + ], + }, + { + name: 'tankInspectionsInitialView', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.CUSTOM, + viewComponent: AdrTankDetailsInitialInspectionViewComponent, + groups: ['tank_details', 'dangerous_goods'], + hide: true, + }, + { + name: 'techRecord_adrDetails_tank_tankDetails_tc2Details_tc2Type', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + label: 'TC2: Inspection type', + value: TC2Types.INITIAL, + hide: true, + groups: ['tank_details', 'dangerous_goods'], + }, + { + name: 'techRecord_adrDetails_tank_tankDetails_tc2Details_tc2IntermediateApprovalNo', + label: 'TC2: Certificate Number', + viewType: FormNodeViewTypes.HIDDEN, + type: FormNodeTypes.CONTROL, + hide: true, + groups: ['tank_details', 'dangerous_goods'], + validators: [{ name: ValidatorNames.MaxLength, args: 70 }], + }, + { + name: 'techRecord_adrDetails_tank_tankDetails_tc2Details_tc2IntermediateExpiryDate', + label: 'TC2: Expiry Date', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.DATE, + isoDate: false, + hide: true, + groups: ['tank_details', 'dangerous_goods'], + validators: [], + }, + { + name: 'techRecord_adrDetails_tank_tankDetails_tc3Details', + label: 'Subsequent Inspections', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.CUSTOM, + viewComponent: AdrTankDetailsSubsequentInspectionsViewComponent, + editType: FormNodeEditTypes.CUSTOM, + editComponent: AdrTankDetailsSubsequentInspectionsEditComponent, + hide: true, + groups: ['tank_details', 'dangerous_goods'], + validators: [ + { + name: ValidatorNames.Tc3TestValidator, + args: { inspectionNumber: 0 }, + }, + ], + }, + { + name: 'techRecord_adrDetails_memosApply', + label: 'Memo 7/9 (3 month extension) applied', + hint: 'Only applicable for vehicles used on national journeys', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.CHECKBOXGROUP, + groups: ['tank_details', 'dangerous_goods'], + hide: true, + options: [{ value: '07/09 3mth leak ext ', label: 'Yes' }], + validators: [], + }, + { + name: 'M15Subtitle', + label: 'M145', + type: FormNodeTypes.SUBTITLE, + viewType: FormNodeViewTypes.HIDDEN, + groups: ['tank_details', 'dangerous_goods'], + hide: true, + }, + { + name: 'techRecord_adrDetails_m145Statement', + label: 'Yes', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.CUSTOM, + viewComponent: AdrTankDetailsM145ViewComponent, + editType: FormNodeEditTypes.CHECKBOX, + groups: ['tank_details', 'dangerous_goods'], + hide: true, + validators: [], + }, + { + name: 'techRecord_adrDetails_listStatementApplicable', + label: 'Battery List Applicable', + width: FormNodeWidth.XS, + value: false, + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.RADIO, + options: [ + { value: true, label: 'Yes' }, + { value: false, label: 'No' }, + ], + validators: [ + { name: ValidatorNames.ShowGroupsWhenEqualTo, args: { values: [true], groups: ['battery_list_applicable'] } }, + { + name: ValidatorNames.HideGroupsWhenEqualTo, + args: { values: [false, undefined, null], groups: ['battery_list_applicable'] }, + }, + { + name: ValidatorNames.RequiredIfEquals, + args: { + sibling: 'techRecord_adrDetails_vehicleDetails_type', + value: Object.values(ADRBodyType).filter((value) => value.includes('battery')) as string[], + }, + }, + ], + hide: true, + groups: ['dangerous_goods', 'battery_list'], + }, + { + name: 'techRecord_adrDetails_batteryListNumber', + label: 'Reference number', + value: null, + type: FormNodeTypes.CONTROL, + width: FormNodeWidth.L, + groups: ['battery_list_applicable', 'battery_list_hide', 'dangerous_goods'], + validators: [ + { name: ValidatorNames.MaxLength, args: 8 }, + { + name: ValidatorNames.RequiredIfEquals, + args: { + sibling: 'techRecord_adrDetails_listStatementApplicable', + value: [true], + }, + }, + ], + hide: true, + }, + { + name: 'DeclarationsSectionTitle', + label: 'Declarations seen', + type: FormNodeTypes.TITLE, + groups: ['declarations_details', 'dangerous_goods'], + hide: true, + }, + { + name: 'techRecord_adrDetails_brakeDeclarationsSeen', + label: 'Manufacturer brake declaration', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.CHECKBOX, + groups: ['declarations_details', 'dangerous_goods'], + value: false, + hide: true, + validators: [ + { + name: ValidatorNames.ShowGroupsWhenEqualTo, + args: { + values: [true], + groups: ['issuer_section'], + }, + }, + { + name: ValidatorNames.HideGroupsWhenEqualTo, + args: { + values: [false], + groups: ['issuer_section', 'issuer_section_hide'], + }, + }, + ], + }, + { + name: 'techRecord_adrDetails_brakeDeclarationIssuer', + label: 'Issuer', + type: FormNodeTypes.CONTROL, + hide: true, + groups: ['issuer_section', 'dangerous_goods_hide'], + editType: FormNodeEditTypes.TEXTAREA, + validators: [{ name: ValidatorNames.MaxLength, args: 500 }], + }, + { + name: 'techRecord_adrDetails_brakeEndurance', + label: 'Brake endurance', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.CHECKBOX, + groups: ['issuer_section', 'dangerous_goods_hide'], + hide: true, + validators: [ + { + name: ValidatorNames.ShowGroupsWhenEqualTo, + args: { + values: [true], + groups: ['weight_section'], + }, + }, + { + name: ValidatorNames.HideGroupsWhenEqualTo, + args: { + values: [false, null, undefined], + groups: ['weight_section'], + }, + }, + ], + }, + { + name: 'techRecord_adrDetails_weight', + label: 'Weight (tonnes)', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.NUMBER, + enableDecimals: true, + width: FormNodeWidth.L, + groups: ['weight_section', 'issuer_section_hide', 'dangerous_goods_hide'], + hide: true, + validators: [ + { name: ValidatorNames.Max, args: 99999999 }, + { + name: ValidatorNames.RequiredIfNotHidden, + }, + { + name: ValidatorNames.CustomPattern, + args: ['^\\d*(\\.\\d{0,2})?$', 'Max 2 decimal places'], + }, + ], + }, + { + name: 'techRecord_adrDetails_declarationsSeen', + label: 'Owner/operator declaration', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.CHECKBOX, + groups: ['declarations_details', 'dangerous_goods'], + hide: true, + }, + { + name: 'NewCertificateRequested', + label: 'New Certificate required', + type: FormNodeTypes.TITLE, + groups: ['dangerous_goods'], + hide: true, + }, + { + name: 'techRecord_adrDetails_newCertificateRequested', + label: 'Yes', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.CUSTOM, + viewComponent: AdrNewCertificateRequiredViewComponent, + editType: FormNodeEditTypes.CHECKBOX, + value: false, + groups: ['dangerous_goods'], + hide: true, + }, + { + name: 'techRecord_adrDetails_additionalExaminerNotes_note', + label: 'Additional Examiner Notes', + hint: 'Will not be present on the ADR certificate', + value: null, + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.TEXTAREA, + viewType: FormNodeViewTypes.HIDDEN, + groups: ['adr_details', 'dangerous_goods'], + hide: true, + validators: [{ name: ValidatorNames.MaxLength, args: 1024 }], + }, + { + name: 'techRecord_adrDetails_additionalExaminerNotes', + value: null, + label: 'Additional Examiner Notes History', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.CUSTOM, + viewComponent: AdrExaminerNotesHistoryViewComponent, + editType: FormNodeEditTypes.CUSTOM, + editComponent: AdrExaminerNotesHistoryEditComponent, + groups: ['adr_details', 'dangerous_goods'], + hide: true, + }, + { + name: 'techRecord_adrDetails_adrCertificateNotes', + label: 'ADR Certificate Notes', + value: null, + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.TEXTAREA, + viewType: FormNodeViewTypes.STRING, + groups: ['adrDetails', 'dangerous_goods'], + hide: true, + validators: [{ name: ValidatorNames.MaxLength, args: 1500 }], + }, + ], }; diff --git a/src/app/forms/templates/general/applicant-details.template.ts b/src/app/forms/templates/general/applicant-details.template.ts index 926cd5d80f..7601205ed2 100644 --- a/src/app/forms/templates/general/applicant-details.template.ts +++ b/src/app/forms/templates/general/applicant-details.template.ts @@ -2,73 +2,73 @@ import { ValidatorNames } from '@forms/models/validators.enum'; import { FormNode, FormNodeTypes, FormNodeWidth } from '../../services/dynamic-form.types'; export const ApplicantDetails: FormNode = { - name: 'techRecord', - type: FormNodeTypes.GROUP, - label: 'Last Applicant', - children: [ - { - name: 'techRecord_applicantDetails_name', - label: 'Name or company', - value: null, - width: FormNodeWidth.XXL, - type: FormNodeTypes.CONTROL, - validators: [{ name: ValidatorNames.MaxLength, args: 150 }], - }, - { - name: 'techRecord_applicantDetails_address1', - label: 'Address line 1', - value: null, - width: FormNodeWidth.XXL, - type: FormNodeTypes.CONTROL, - validators: [{ name: ValidatorNames.MaxLength, args: 60 }], - }, - { - name: 'techRecord_applicantDetails_address2', - label: 'Address line 2', - value: null, - width: FormNodeWidth.XXL, - type: FormNodeTypes.CONTROL, - validators: [{ name: ValidatorNames.MaxLength, args: 60 }], - }, - { - name: 'techRecord_applicantDetails_postTown', - label: 'Town or City', - value: null, - width: FormNodeWidth.XL, - type: FormNodeTypes.CONTROL, - validators: [{ name: ValidatorNames.MaxLength, args: 60 }], - }, - { - name: 'techRecord_applicantDetails_address3', - label: 'County', - value: null, - width: FormNodeWidth.XL, - type: FormNodeTypes.CONTROL, - validators: [{ name: ValidatorNames.MaxLength, args: 60 }], - }, - { - name: 'techRecord_applicantDetails_postCode', - label: 'Postcode', - value: null, - width: FormNodeWidth.L, - type: FormNodeTypes.CONTROL, - validators: [{ name: ValidatorNames.MaxLength, args: 12 }], - }, - { - name: 'techRecord_applicantDetails_telephoneNumber', - label: 'Telephone number', - value: null, - width: FormNodeWidth.XL, - type: FormNodeTypes.CONTROL, - validators: [{ name: ValidatorNames.MaxLength, args: 25 }], - }, - { - name: 'techRecord_applicantDetails_emailAddress', - label: 'Email address', - value: null, - width: FormNodeWidth.XL, - type: FormNodeTypes.CONTROL, - validators: [{ name: ValidatorNames.MaxLength, args: 255 }, { name: ValidatorNames.Email }], - }, - ], + name: 'techRecord', + type: FormNodeTypes.GROUP, + label: 'Last Applicant', + children: [ + { + name: 'techRecord_applicantDetails_name', + label: 'Name or company', + value: null, + width: FormNodeWidth.XXL, + type: FormNodeTypes.CONTROL, + validators: [{ name: ValidatorNames.MaxLength, args: 150 }], + }, + { + name: 'techRecord_applicantDetails_address1', + label: 'Address line 1', + value: null, + width: FormNodeWidth.XXL, + type: FormNodeTypes.CONTROL, + validators: [{ name: ValidatorNames.MaxLength, args: 60 }], + }, + { + name: 'techRecord_applicantDetails_address2', + label: 'Address line 2', + value: null, + width: FormNodeWidth.XXL, + type: FormNodeTypes.CONTROL, + validators: [{ name: ValidatorNames.MaxLength, args: 60 }], + }, + { + name: 'techRecord_applicantDetails_postTown', + label: 'Town or City', + value: null, + width: FormNodeWidth.XL, + type: FormNodeTypes.CONTROL, + validators: [{ name: ValidatorNames.MaxLength, args: 60 }], + }, + { + name: 'techRecord_applicantDetails_address3', + label: 'County', + value: null, + width: FormNodeWidth.XL, + type: FormNodeTypes.CONTROL, + validators: [{ name: ValidatorNames.MaxLength, args: 60 }], + }, + { + name: 'techRecord_applicantDetails_postCode', + label: 'Postcode', + value: null, + width: FormNodeWidth.L, + type: FormNodeTypes.CONTROL, + validators: [{ name: ValidatorNames.MaxLength, args: 12 }], + }, + { + name: 'techRecord_applicantDetails_telephoneNumber', + label: 'Telephone number', + value: null, + width: FormNodeWidth.XL, + type: FormNodeTypes.CONTROL, + validators: [{ name: ValidatorNames.MaxLength, args: 25 }], + }, + { + name: 'techRecord_applicantDetails_emailAddress', + label: 'Email address', + value: null, + width: FormNodeWidth.XL, + type: FormNodeTypes.CONTROL, + validators: [{ name: ValidatorNames.MaxLength, args: 255 }, { name: ValidatorNames.Email }], + }, + ], }; diff --git a/src/app/forms/templates/general/approval-type.template.ts b/src/app/forms/templates/general/approval-type.template.ts index 81efd73b25..e148505851 100644 --- a/src/app/forms/templates/general/approval-type.template.ts +++ b/src/app/forms/templates/general/approval-type.template.ts @@ -3,74 +3,80 @@ import { ValidatorNames } from '@forms/models/validators.enum'; import { getOptionsFromEnum } from '@forms/utils/enum-map'; import { TagType } from '@shared/components/tag/tag.component'; import { - FormNode, FormNodeEditTypes, FormNodeTypes, FormNodeWidth, TagTypeLabels, + FormNode, + FormNodeEditTypes, + FormNodeTypes, + FormNodeWidth, + TagTypeLabels, } from '../../services/dynamic-form.types'; export const HgvAndTrlTypeApprovalTemplate: FormNode = { - name: 'approvalSection', - label: 'Type approval', - type: FormNodeTypes.GROUP, - children: [ - { - name: 'techRecord_approvalType', - label: 'Approval type', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.SELECT, - options: getOptionsFromEnum(ApprovalType), - validators: [{ name: ValidatorNames.IsMemberOfEnum, args: { enum: ApprovalType, options: { allowFalsy: true } } }], - }, - { - name: 'techRecord_approvalTypeNumber', - label: 'Approval type number', - type: FormNodeTypes.CONTROL, - width: FormNodeWidth.XL, - validators: [ - { - name: ValidatorNames.RequiredIfEquals, - args: { - sibling: 'techRecord_approvalType', - value: [ - 'NTA', - 'ECTA', - 'ECSSTA', - 'IVA', - 'NSSTA', - 'GB WVTA', - 'UKNI WVTA', - 'EU WVTA Pre 23', - 'EU WVTA 23 on', - 'QNIG', - 'Prov.GB WVTA', - 'Small series NKSXX', - 'Small series NKS', - 'IVA - VCA', - 'IVA - DVSA/NI', - ], - }, - }, - ], - }, - { - name: 'techRecord_ntaNumber', - label: 'National type number', - type: FormNodeTypes.CONTROL, - width: FormNodeWidth.XXL, - validators: [{ name: ValidatorNames.MaxLength, args: 40 }], - }, - { - name: 'techRecord_variantNumber', - label: 'Variant number', - type: FormNodeTypes.CONTROL, - width: FormNodeWidth.XL, - validators: [{ name: ValidatorNames.MaxLength, args: 25 }], - customTags: [{ colour: TagType.PURPLE, label: TagTypeLabels.PLATES }], - }, - { - name: 'techRecord_variantVersionNumber', - label: 'Variant version number', - type: FormNodeTypes.CONTROL, - width: FormNodeWidth.XXL, - validators: [{ name: ValidatorNames.MaxLength, args: 35 }], - }, - ], + name: 'approvalSection', + label: 'Type approval', + type: FormNodeTypes.GROUP, + children: [ + { + name: 'techRecord_approvalType', + label: 'Approval type', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.SELECT, + options: getOptionsFromEnum(ApprovalType), + validators: [ + { name: ValidatorNames.IsMemberOfEnum, args: { enum: ApprovalType, options: { allowFalsy: true } } }, + ], + }, + { + name: 'techRecord_approvalTypeNumber', + label: 'Approval type number', + type: FormNodeTypes.CONTROL, + width: FormNodeWidth.XL, + validators: [ + { + name: ValidatorNames.RequiredIfEquals, + args: { + sibling: 'techRecord_approvalType', + value: [ + 'NTA', + 'ECTA', + 'ECSSTA', + 'IVA', + 'NSSTA', + 'GB WVTA', + 'UKNI WVTA', + 'EU WVTA Pre 23', + 'EU WVTA 23 on', + 'QNIG', + 'Prov.GB WVTA', + 'Small series NKSXX', + 'Small series NKS', + 'IVA - VCA', + 'IVA - DVSA/NI', + ], + }, + }, + ], + }, + { + name: 'techRecord_ntaNumber', + label: 'National type number', + type: FormNodeTypes.CONTROL, + width: FormNodeWidth.XXL, + validators: [{ name: ValidatorNames.MaxLength, args: 40 }], + }, + { + name: 'techRecord_variantNumber', + label: 'Variant number', + type: FormNodeTypes.CONTROL, + width: FormNodeWidth.XL, + validators: [{ name: ValidatorNames.MaxLength, args: 25 }], + customTags: [{ colour: TagType.PURPLE, label: TagTypeLabels.PLATES }], + }, + { + name: 'techRecord_variantVersionNumber', + label: 'Variant version number', + type: FormNodeTypes.CONTROL, + width: FormNodeWidth.XXL, + validators: [{ name: ValidatorNames.MaxLength, args: 35 }], + }, + ], }; diff --git a/src/app/forms/templates/general/audit.template.ts b/src/app/forms/templates/general/audit.template.ts index a5d4c47122..4e0449c7b5 100644 --- a/src/app/forms/templates/general/audit.template.ts +++ b/src/app/forms/templates/general/audit.template.ts @@ -1,54 +1,54 @@ import { FormNode, FormNodeTypes, FormNodeViewTypes } from '@forms/services/dynamic-form.types'; export const Audit: FormNode = { - name: 'audit', - type: FormNodeTypes.GROUP, - label: 'Audit', - children: [ - { - name: 'techRecord_reasonForCreation', - label: 'Reason for creation', - value: '', - type: FormNodeTypes.CONTROL, - validators: [], - }, - { - name: 'techRecord_createdAt', - label: 'Created at', - value: '', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.DATETIME, - }, - { - name: 'techRecord_createdByName', - label: 'Created by', - value: null, - type: FormNodeTypes.CONTROL, - }, - { - name: 'techRecord_createdById', - label: 'Created by ID', - value: null, - type: FormNodeTypes.CONTROL, - }, - { - name: 'techRecord_lastUpdatedAt', - label: 'Last updated at', - value: null, - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.DATETIME, - }, - { - name: 'techRecord_lastUpdatedByName', - label: 'Last updated by', - value: null, - type: FormNodeTypes.CONTROL, - }, - { - name: 'techRecord_lastUpdatedById', - label: 'Last updated by ID', - value: null, - type: FormNodeTypes.CONTROL, - }, - ], + name: 'audit', + type: FormNodeTypes.GROUP, + label: 'Audit', + children: [ + { + name: 'techRecord_reasonForCreation', + label: 'Reason for creation', + value: '', + type: FormNodeTypes.CONTROL, + validators: [], + }, + { + name: 'techRecord_createdAt', + label: 'Created at', + value: '', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.DATETIME, + }, + { + name: 'techRecord_createdByName', + label: 'Created by', + value: null, + type: FormNodeTypes.CONTROL, + }, + { + name: 'techRecord_createdById', + label: 'Created by ID', + value: null, + type: FormNodeTypes.CONTROL, + }, + { + name: 'techRecord_lastUpdatedAt', + label: 'Last updated at', + value: null, + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.DATETIME, + }, + { + name: 'techRecord_lastUpdatedByName', + label: 'Last updated by', + value: null, + type: FormNodeTypes.CONTROL, + }, + { + name: 'techRecord_lastUpdatedById', + label: 'Last updated by ID', + value: null, + type: FormNodeTypes.CONTROL, + }, + ], }; diff --git a/src/app/forms/templates/general/defect.template.ts b/src/app/forms/templates/general/defect.template.ts index 2a1db1c9e4..2b4684d184 100644 --- a/src/app/forms/templates/general/defect.template.ts +++ b/src/app/forms/templates/general/defect.template.ts @@ -2,192 +2,192 @@ import { ValidatorNames } from '@forms/models/validators.enum'; import { FormNode, FormNodeEditTypes, FormNodeTypes } from '@forms/services/dynamic-form.types'; export const DefectsTpl: FormNode = { - name: 'defects', - label: 'Defects', - type: FormNodeTypes.GROUP, - children: [ - { - name: 'testTypes', - label: 'Test Types', - type: FormNodeTypes.ARRAY, - children: [ - { - name: '0', // it is important here that the name of the node for an ARRAY type should be an index value - type: FormNodeTypes.GROUP, - children: [ - { - name: 'defects', - type: FormNodeTypes.ARRAY, - children: [ - { - name: '0', - type: FormNodeTypes.GROUP, - children: [ - { - name: 'deficiencyRef', - label: 'Deficiency ref', - type: FormNodeTypes.CONTROL, - disabled: true, - }, - { - name: 'deficiencyCategory', - label: 'Deficiency category', - type: FormNodeTypes.CONTROL, - disabled: true, - }, - { - name: 'imNumber', - label: 'IM number', - type: FormNodeTypes.CONTROL, - disabled: true, - }, - { - name: 'imDescription', - label: 'IM description', - type: FormNodeTypes.CONTROL, - disabled: true, - }, - { - name: 'itemNumber', - label: 'Item No.', - type: FormNodeTypes.CONTROL, - disabled: true, - }, - { - name: 'itemDescription', - label: 'Item description', - type: FormNodeTypes.CONTROL, - disabled: true, - }, - { - name: 'deficiencyId', - label: 'Deficiency ID', - value: null, - type: FormNodeTypes.CONTROL, - disabled: true, - }, - { - name: 'deficiencySubId', - label: 'Deficiency sub ID', - value: null, - type: FormNodeTypes.CONTROL, - disabled: true, - }, - { - name: 'deficiencyText', - label: 'Deficiency text', - value: null, - type: FormNodeTypes.CONTROL, - disabled: true, - }, - { - name: 'additionalInformation', - label: 'Additional details', - type: FormNodeTypes.GROUP, - children: [ - { - name: 'location', - label: 'Location', - type: FormNodeTypes.GROUP, - children: [ - { - name: 'vertical', - label: 'Vertical', - value: null, - type: FormNodeTypes.CONTROL, - }, - { - name: 'horizontal', - label: 'Horizontal', - value: null, - type: FormNodeTypes.CONTROL, - }, - { - name: 'lateral', - label: 'Lateral', - value: null, - type: FormNodeTypes.CONTROL, - }, - { - name: 'longitudinal', - label: 'Longitudinal', - value: null, - type: FormNodeTypes.CONTROL, - }, - { - name: 'rowNumber', - label: 'Row number', - value: null, - type: FormNodeTypes.CONTROL, - }, - { - name: 'seatNumber', - label: 'Seat number', - value: null, - type: FormNodeTypes.CONTROL, - }, - { - name: 'axleNumber', - label: 'Axle number', - value: null, - type: FormNodeTypes.CONTROL, - }, - ], - }, - { - name: 'notes', - label: 'Notes', - value: null, - type: FormNodeTypes.CONTROL, - validators: [{ name: ValidatorNames.ValidateDefectNotes }], - }, - ], - }, - { - name: 'prs', - label: 'PRS', - value: null, - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.RADIO, - options: [ - { value: true, label: 'Yes' }, - { value: false, label: 'No' }, - ], - }, - { - name: 'prohibitionIssued', - label: 'Prohibition issued', - value: null, - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.RADIO, - options: [ - { value: true, label: 'Yes' }, - { value: false, label: 'No' }, - ], - validators: [ - { - name: ValidatorNames.RequiredIfEquals, - args: { sibling: 'deficiencyCategory', value: ['dangerous'] }, - }, - { - name: ValidatorNames.ValidateProhibitionIssued, - }, - ], - }, - { - name: 'stdForProhibition', - label: 'STD for prohibition', - value: null, - type: FormNodeTypes.CONTROL, - disabled: true, - }, - ], - }, - ], - }, - ], - }, - ], - }, - ], + name: 'defects', + label: 'Defects', + type: FormNodeTypes.GROUP, + children: [ + { + name: 'testTypes', + label: 'Test Types', + type: FormNodeTypes.ARRAY, + children: [ + { + name: '0', // it is important here that the name of the node for an ARRAY type should be an index value + type: FormNodeTypes.GROUP, + children: [ + { + name: 'defects', + type: FormNodeTypes.ARRAY, + children: [ + { + name: '0', + type: FormNodeTypes.GROUP, + children: [ + { + name: 'deficiencyRef', + label: 'Deficiency ref', + type: FormNodeTypes.CONTROL, + disabled: true, + }, + { + name: 'deficiencyCategory', + label: 'Deficiency category', + type: FormNodeTypes.CONTROL, + disabled: true, + }, + { + name: 'imNumber', + label: 'IM number', + type: FormNodeTypes.CONTROL, + disabled: true, + }, + { + name: 'imDescription', + label: 'IM description', + type: FormNodeTypes.CONTROL, + disabled: true, + }, + { + name: 'itemNumber', + label: 'Item No.', + type: FormNodeTypes.CONTROL, + disabled: true, + }, + { + name: 'itemDescription', + label: 'Item description', + type: FormNodeTypes.CONTROL, + disabled: true, + }, + { + name: 'deficiencyId', + label: 'Deficiency ID', + value: null, + type: FormNodeTypes.CONTROL, + disabled: true, + }, + { + name: 'deficiencySubId', + label: 'Deficiency sub ID', + value: null, + type: FormNodeTypes.CONTROL, + disabled: true, + }, + { + name: 'deficiencyText', + label: 'Deficiency text', + value: null, + type: FormNodeTypes.CONTROL, + disabled: true, + }, + { + name: 'additionalInformation', + label: 'Additional details', + type: FormNodeTypes.GROUP, + children: [ + { + name: 'location', + label: 'Location', + type: FormNodeTypes.GROUP, + children: [ + { + name: 'vertical', + label: 'Vertical', + value: null, + type: FormNodeTypes.CONTROL, + }, + { + name: 'horizontal', + label: 'Horizontal', + value: null, + type: FormNodeTypes.CONTROL, + }, + { + name: 'lateral', + label: 'Lateral', + value: null, + type: FormNodeTypes.CONTROL, + }, + { + name: 'longitudinal', + label: 'Longitudinal', + value: null, + type: FormNodeTypes.CONTROL, + }, + { + name: 'rowNumber', + label: 'Row number', + value: null, + type: FormNodeTypes.CONTROL, + }, + { + name: 'seatNumber', + label: 'Seat number', + value: null, + type: FormNodeTypes.CONTROL, + }, + { + name: 'axleNumber', + label: 'Axle number', + value: null, + type: FormNodeTypes.CONTROL, + }, + ], + }, + { + name: 'notes', + label: 'Notes', + value: null, + type: FormNodeTypes.CONTROL, + validators: [{ name: ValidatorNames.ValidateDefectNotes }], + }, + ], + }, + { + name: 'prs', + label: 'PRS', + value: null, + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.RADIO, + options: [ + { value: true, label: 'Yes' }, + { value: false, label: 'No' }, + ], + }, + { + name: 'prohibitionIssued', + label: 'Prohibition issued', + value: null, + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.RADIO, + options: [ + { value: true, label: 'Yes' }, + { value: false, label: 'No' }, + ], + validators: [ + { + name: ValidatorNames.RequiredIfEquals, + args: { sibling: 'deficiencyCategory', value: ['dangerous'] }, + }, + { + name: ValidatorNames.ValidateProhibitionIssued, + }, + ], + }, + { + name: 'stdForProhibition', + label: 'STD for prohibition', + value: null, + type: FormNodeTypes.CONTROL, + disabled: true, + }, + ], + }, + ], + }, + ], + }, + ], + }, + ], }; diff --git a/src/app/forms/templates/general/document-types.ts b/src/app/forms/templates/general/document-types.ts index 1eafede9b7..4dbffe83b6 100644 --- a/src/app/forms/templates/general/document-types.ts +++ b/src/app/forms/templates/general/document-types.ts @@ -1,165 +1,165 @@ import { FormNodeOption } from '@forms/services/dynamic-form.types'; export const DOCUMENT_TYPES: FormNodeOption[] = [ - { - value: 'AAV - HGV Annual Test', - label: 'AAV - HGV Annual Test', - }, - { - value: 'COIF Master', - label: 'COIF Master', - }, - { - value: 'Tempo 100 Sp Ord', - label: 'Tempo 100 Sp Ord', - }, - { - value: 'Deleted', - label: 'Deleted', - }, - { - value: 'XPT - Tr Plating Cert paid', - label: 'XPT - Tr Plating Cert paid', - }, + { + value: 'AAV - HGV Annual Test', + label: 'AAV - HGV Annual Test', + }, + { + value: 'COIF Master', + label: 'COIF Master', + }, + { + value: 'Tempo 100 Sp Ord', + label: 'Tempo 100 Sp Ord', + }, + { + value: 'Deleted', + label: 'Deleted', + }, + { + value: 'XPT - Tr Plating Cert paid', + label: 'XPT - Tr Plating Cert paid', + }, - { - value: 'FFV - HGV First Test', - label: 'FFV - HGV First Test', - }, - { - value: 'Repl Vitesse 100', - label: 'Repl Vitesse 100', - }, - { - value: 'TCV - HGV Test Cert', - label: 'TCV - HGV Test Cert', - }, - { - value: 'ZZZ - Miscellaneous', - label: 'ZZZ - Miscellaneous', - }, - { - value: 'Test Certificate', - label: 'Test Certificate', - }, - { - value: 'XCT - Trailer Test Cert free', - label: 'XCT - Trailer Test Cert free', - }, - { - value: 'C52 - COC and VTG52A', - label: 'C52 - COC and VTG52A', - }, - { - value: 'Tempo 100 Report', - label: 'Tempo 100 Report', - }, - { - value: 'Main File Amendment', - label: 'Main File Amendment', - }, - { - value: 'PSV Doc', - label: 'PSV Doc', - }, - { - value: 'PSV Repl COC', - label: 'PSV Repl COC', - }, - { - value: 'TAV - COC', - label: 'TAV - COC', - }, - { - value: 'NPT - Trailer Alteration', - label: 'NPT - Trailer Alteration', - }, - { - value: 'OMO Certificate', - label: 'OMO Certificate', - }, - { - value: 'PSV Repl COIF', - label: 'PSV Repl COIF', - }, - { - value: 'COIF Application', - label: 'COIF Application', - }, - { - value: 'XPV - HGV Plating Cert Free', - label: 'XPV - HGV Plating Cert Free', - }, - { - value: 'TCT - Trailer Test Cert', - label: 'TCT - Trailer Test Cert', - }, - { - value: 'Tempo 100 App', - label: 'Tempo 100 App', - }, - { - value: 'PSV Decision on N/ALT', - label: 'PSV Decision on N/ALT', - }, - { - value: 'Special Order PSV', - label: 'Special Order PSV', - }, - { - value: 'NPV - HGV Alteration', - label: 'NPV - HGV Alteration', - }, - { - value: 'No Description Found', - label: 'No Description Found', - }, - { - value: 'Vitesse 100 Sp Ord', - label: 'Vitesse 100 Sp Ord', - }, - { - value: 'Brake Test Details', - label: 'Brake Test Details', - }, - { - value: 'COIF Productional', - label: 'COIF Productional', - }, - { - value: 'RDT - Test Disc Paid', - label: 'RDT - Test Disc Paid', - }, - { - value: 'RCV - HGV Test Cert', - label: 'RCV - HGV Test Cert', - }, - { - value: 'FFT - Trailer First Test', - label: 'FFT - Trailer First Test', - }, - { - value: 'IPT - Trailer EEC Plate/Cert', - label: 'IPT - Trailer EEC Plate/Cert', - }, - { - value: 'XDT - Test Disc Free', - label: 'XDT - Test Disc Free', - }, - { - value: 'PRV - HGV Plating Cert paid', - label: 'PRV - HGV Plating Cert paid', - }, - { - value: 'COF Cert', - label: 'COF Cert', - }, - { - value: 'PRT - Tr Plating Cert paid', - label: 'PRT - Tr Plating Cert paid', - }, - { - value: 'Tempo 100 Permit', - label: 'Tempo 100 Permit', - }, + { + value: 'FFV - HGV First Test', + label: 'FFV - HGV First Test', + }, + { + value: 'Repl Vitesse 100', + label: 'Repl Vitesse 100', + }, + { + value: 'TCV - HGV Test Cert', + label: 'TCV - HGV Test Cert', + }, + { + value: 'ZZZ - Miscellaneous', + label: 'ZZZ - Miscellaneous', + }, + { + value: 'Test Certificate', + label: 'Test Certificate', + }, + { + value: 'XCT - Trailer Test Cert free', + label: 'XCT - Trailer Test Cert free', + }, + { + value: 'C52 - COC and VTG52A', + label: 'C52 - COC and VTG52A', + }, + { + value: 'Tempo 100 Report', + label: 'Tempo 100 Report', + }, + { + value: 'Main File Amendment', + label: 'Main File Amendment', + }, + { + value: 'PSV Doc', + label: 'PSV Doc', + }, + { + value: 'PSV Repl COC', + label: 'PSV Repl COC', + }, + { + value: 'TAV - COC', + label: 'TAV - COC', + }, + { + value: 'NPT - Trailer Alteration', + label: 'NPT - Trailer Alteration', + }, + { + value: 'OMO Certificate', + label: 'OMO Certificate', + }, + { + value: 'PSV Repl COIF', + label: 'PSV Repl COIF', + }, + { + value: 'COIF Application', + label: 'COIF Application', + }, + { + value: 'XPV - HGV Plating Cert Free', + label: 'XPV - HGV Plating Cert Free', + }, + { + value: 'TCT - Trailer Test Cert', + label: 'TCT - Trailer Test Cert', + }, + { + value: 'Tempo 100 App', + label: 'Tempo 100 App', + }, + { + value: 'PSV Decision on N/ALT', + label: 'PSV Decision on N/ALT', + }, + { + value: 'Special Order PSV', + label: 'Special Order PSV', + }, + { + value: 'NPV - HGV Alteration', + label: 'NPV - HGV Alteration', + }, + { + value: 'No Description Found', + label: 'No Description Found', + }, + { + value: 'Vitesse 100 Sp Ord', + label: 'Vitesse 100 Sp Ord', + }, + { + value: 'Brake Test Details', + label: 'Brake Test Details', + }, + { + value: 'COIF Productional', + label: 'COIF Productional', + }, + { + value: 'RDT - Test Disc Paid', + label: 'RDT - Test Disc Paid', + }, + { + value: 'RCV - HGV Test Cert', + label: 'RCV - HGV Test Cert', + }, + { + value: 'FFT - Trailer First Test', + label: 'FFT - Trailer First Test', + }, + { + value: 'IPT - Trailer EEC Plate/Cert', + label: 'IPT - Trailer EEC Plate/Cert', + }, + { + value: 'XDT - Test Disc Free', + label: 'XDT - Test Disc Free', + }, + { + value: 'PRV - HGV Plating Cert paid', + label: 'PRV - HGV Plating Cert paid', + }, + { + value: 'COF Cert', + label: 'COF Cert', + }, + { + value: 'PRT - Tr Plating Cert paid', + label: 'PRT - Tr Plating Cert paid', + }, + { + value: 'Tempo 100 Permit', + label: 'Tempo 100 Permit', + }, ]; diff --git a/src/app/forms/templates/general/documents.template.ts b/src/app/forms/templates/general/documents.template.ts index 6e6e90424b..a3cd51d7f6 100644 --- a/src/app/forms/templates/general/documents.template.ts +++ b/src/app/forms/templates/general/documents.template.ts @@ -1,32 +1,32 @@ -import { DOCUMENT_TYPES } from '@forms/templates/general/document-types'; import { ValidatorNames } from '@forms/models/validators.enum'; +import { DOCUMENT_TYPES } from '@forms/templates/general/document-types'; import { FormNode, FormNodeEditTypes, FormNodeTypes } from '../../services/dynamic-form.types'; export const DocumentsTemplate: FormNode = { - name: 'documentsSection', - label: 'Documents', - type: FormNodeTypes.GROUP, - children: [ - { - name: 'techRecord_microfilm_microfilmDocumentType', - label: 'Microfilm document type', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.AUTOCOMPLETE, - options: DOCUMENT_TYPES, - }, - { - name: 'techRecord_microfilm_microfilmRollNumber', - label: 'Microfilm roll number', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.TEXT, - validators: [{ name: ValidatorNames.MaxLength, args: 5 }], - }, - { - name: 'techRecord_microfilm_microfilmSerialNumber', - label: 'Microfilm serial number', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.TEXT, - validators: [{ name: ValidatorNames.MaxLength, args: 4 }], - }, - ], + name: 'documentsSection', + label: 'Documents', + type: FormNodeTypes.GROUP, + children: [ + { + name: 'techRecord_microfilm_microfilmDocumentType', + label: 'Microfilm document type', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.AUTOCOMPLETE, + options: DOCUMENT_TYPES, + }, + { + name: 'techRecord_microfilm_microfilmRollNumber', + label: 'Microfilm roll number', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.TEXT, + validators: [{ name: ValidatorNames.MaxLength, args: 5 }], + }, + { + name: 'techRecord_microfilm_microfilmSerialNumber', + label: 'Microfilm serial number', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.TEXT, + validators: [{ name: ValidatorNames.MaxLength, args: 4 }], + }, + ], }; diff --git a/src/app/forms/templates/general/hgv-trl-body.template.ts b/src/app/forms/templates/general/hgv-trl-body.template.ts index e097b75124..a22b12d789 100644 --- a/src/app/forms/templates/general/hgv-trl-body.template.ts +++ b/src/app/forms/templates/general/hgv-trl-body.template.ts @@ -2,85 +2,95 @@ import { AsyncValidatorNames } from '@forms/models/async-validators.enum'; import { ValidatorNames } from '@forms/models/validators.enum'; import { TagType } from '@shared/components/tag/tag.component'; import { - FormNode, FormNodeEditTypes, FormNodeTypes, FormNodeViewTypes, FormNodeWidth, TagTypeLabels, + FormNode, + FormNodeEditTypes, + FormNodeTypes, + FormNodeViewTypes, + FormNodeWidth, + TagTypeLabels, } from '../../services/dynamic-form.types'; export const HgvAndTrlBodyTemplate: FormNode = { - name: 'bodySection', - label: 'Body', - type: FormNodeTypes.GROUP, - children: [ - { - name: 'techRecord_brakes_dtpNumber', - label: 'DTp number', - value: null, - width: FormNodeWidth.L, - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.TEXT, - validators: [{ name: ValidatorNames.MaxLength, args: 6 }], - customTags: [{ colour: TagType.PURPLE, label: TagTypeLabels.PLATES }], - }, - { - name: 'techRecord_make', - label: 'Body make', - value: null, - width: FormNodeWidth.L, - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.TEXT, - validators: [{ name: ValidatorNames.MaxLength, args: 50 }], - asyncValidators: [{ name: AsyncValidatorNames.RequiredWhenCarryingDangerousGoods }], - customTags: [{ colour: TagType.PURPLE, label: TagTypeLabels.PLATES }], - }, - { - name: 'techRecord_model', - label: 'Body model', - value: null, - width: FormNodeWidth.L, - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.TEXT, - validators: [{ name: ValidatorNames.MaxLength, args: 30 }], - customTags: [{ colour: TagType.PURPLE, label: TagTypeLabels.PLATES }], - }, - { - name: 'techRecord_bodyType_description', - label: 'Body type', - value: null, - customId: 'techRecord_bodyType_description', - width: FormNodeWidth.L, - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.SELECT, - validators: [{ name: ValidatorNames.Required }], - customTags: [{ colour: TagType.RED, label: TagTypeLabels.REQUIRED }], - }, - { - name: 'techRecord_bodyType_code', - value: null, - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'techRecord_functionCode', - label: 'Function code', - value: null, - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.SELECT, - width: FormNodeWidth.S, - options: [ - { value: 'r', label: 'R' }, - { value: 'a', label: 'A' }, - ], - validators: [{ name: ValidatorNames.MaxLength, args: 1 }], - customTags: [{ colour: TagType.PURPLE, label: TagTypeLabels.PLATES }], - }, - { - name: 'techRecord_conversionRefNo', - label: 'Conversion ref no', - value: null, - width: FormNodeWidth.L, - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.TEXT, - validators: [{ name: ValidatorNames.CustomPattern, args: ['^[A-Z0-9 ]{0,10}$', 'max length 10 uppercase letters or numbers'] }], - }, - ], + name: 'bodySection', + label: 'Body', + type: FormNodeTypes.GROUP, + children: [ + { + name: 'techRecord_brakes_dtpNumber', + label: 'DTp number', + value: null, + width: FormNodeWidth.L, + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.TEXT, + validators: [{ name: ValidatorNames.MaxLength, args: 6 }], + customTags: [{ colour: TagType.PURPLE, label: TagTypeLabels.PLATES }], + }, + { + name: 'techRecord_make', + label: 'Body make', + value: null, + width: FormNodeWidth.L, + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.TEXT, + validators: [{ name: ValidatorNames.MaxLength, args: 50 }], + asyncValidators: [{ name: AsyncValidatorNames.RequiredWhenCarryingDangerousGoods }], + customTags: [{ colour: TagType.PURPLE, label: TagTypeLabels.PLATES }], + }, + { + name: 'techRecord_model', + label: 'Body model', + value: null, + width: FormNodeWidth.L, + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.TEXT, + validators: [{ name: ValidatorNames.MaxLength, args: 30 }], + customTags: [{ colour: TagType.PURPLE, label: TagTypeLabels.PLATES }], + }, + { + name: 'techRecord_bodyType_description', + label: 'Body type', + value: null, + customId: 'techRecord_bodyType_description', + width: FormNodeWidth.L, + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.SELECT, + validators: [{ name: ValidatorNames.Required }], + customTags: [{ colour: TagType.RED, label: TagTypeLabels.REQUIRED }], + }, + { + name: 'techRecord_bodyType_code', + value: null, + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'techRecord_functionCode', + label: 'Function code', + value: null, + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.SELECT, + width: FormNodeWidth.S, + options: [ + { value: 'r', label: 'R' }, + { value: 'a', label: 'A' }, + ], + validators: [{ name: ValidatorNames.MaxLength, args: 1 }], + customTags: [{ colour: TagType.PURPLE, label: TagTypeLabels.PLATES }], + }, + { + name: 'techRecord_conversionRefNo', + label: 'Conversion ref no', + value: null, + width: FormNodeWidth.L, + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.TEXT, + validators: [ + { + name: ValidatorNames.CustomPattern, + args: ['^[A-Z0-9 ]{0,10}$', 'max length 10 uppercase letters or numbers'], + }, + ], + }, + ], }; diff --git a/src/app/forms/templates/general/letter-types.ts b/src/app/forms/templates/general/letter-types.ts index d793768d2d..655c3d66ec 100644 --- a/src/app/forms/templates/general/letter-types.ts +++ b/src/app/forms/templates/general/letter-types.ts @@ -1,12 +1,12 @@ import { FormNodeOption } from '@forms/services/dynamic-form.types'; export const LETTER_TYPES: FormNodeOption[] = [ - { - value: 'trailer acceptance', - label: 'Trailer acceptance', - }, - { - value: 'trailer rejection', - label: 'Trailer rejection', - }, + { + value: 'trailer acceptance', + label: 'Trailer acceptance', + }, + { + value: 'trailer rejection', + label: 'Trailer rejection', + }, ]; diff --git a/src/app/forms/templates/general/letters.template.ts b/src/app/forms/templates/general/letters.template.ts index d37b124cba..bfb8e5bbed 100644 --- a/src/app/forms/templates/general/letters.template.ts +++ b/src/app/forms/templates/general/letters.template.ts @@ -2,37 +2,37 @@ import { FormNode, FormNodeTypes, FormNodeViewTypes } from '../../services/dynam import { LETTER_TYPES } from './letter-types'; export const LettersTemplate: FormNode = { - name: 'lettersSection', - label: 'Letters', - type: FormNodeTypes.GROUP, - children: [ - { - name: 'techRecord_letterOfAuth_letterIssuer', - label: 'Letter issuer', - type: FormNodeTypes.CONTROL, - options: LETTER_TYPES, - }, - { - name: 'techRecord_letterOfAuth_letterType', - label: 'Type of letter', - type: FormNodeTypes.CONTROL, - options: LETTER_TYPES, - }, - { - name: 'techRecord_letterOfAuth_letterDateRequested', - label: 'Date requested', - type: FormNodeTypes.CONTROL, - }, - { - name: 'techRecord_letterOfAuth_paragraphId', - label: 'Paragraph ID', - type: FormNodeTypes.CONTROL, - }, - { - name: 'techRecord_letterOfAuth_letterContents', - label: 'Content', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.HIDDEN, - }, - ], + name: 'lettersSection', + label: 'Letters', + type: FormNodeTypes.GROUP, + children: [ + { + name: 'techRecord_letterOfAuth_letterIssuer', + label: 'Letter issuer', + type: FormNodeTypes.CONTROL, + options: LETTER_TYPES, + }, + { + name: 'techRecord_letterOfAuth_letterType', + label: 'Type of letter', + type: FormNodeTypes.CONTROL, + options: LETTER_TYPES, + }, + { + name: 'techRecord_letterOfAuth_letterDateRequested', + label: 'Date requested', + type: FormNodeTypes.CONTROL, + }, + { + name: 'techRecord_letterOfAuth_paragraphId', + label: 'Paragraph ID', + type: FormNodeTypes.CONTROL, + }, + { + name: 'techRecord_letterOfAuth_letterContents', + label: 'Content', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + }, + ], }; diff --git a/src/app/forms/templates/general/manufacturer.template.ts b/src/app/forms/templates/general/manufacturer.template.ts index 245eca5ff4..1631c1deeb 100644 --- a/src/app/forms/templates/general/manufacturer.template.ts +++ b/src/app/forms/templates/general/manufacturer.template.ts @@ -1,82 +1,80 @@ import { ValidatorNames } from '@forms/models/validators.enum'; -import { - FormNode, FormNodeEditTypes, FormNodeTypes, FormNodeWidth, -} from '../../services/dynamic-form.types'; +import { FormNode, FormNodeEditTypes, FormNodeTypes, FormNodeWidth } from '../../services/dynamic-form.types'; export const ManufacturerTemplate: FormNode = { - name: 'manufacturerSection', - label: 'Manufacturer', - type: FormNodeTypes.GROUP, - children: [ - { - name: 'techRecord_manufacturerDetails_name', - label: 'Name', - type: FormNodeTypes.CONTROL, - width: FormNodeWidth.XXL, - validators: [{ name: ValidatorNames.MaxLength, args: 150 }], - }, - { - name: 'techRecord_manufacturerDetails_address1', - label: 'Address line 1', - type: FormNodeTypes.CONTROL, - width: FormNodeWidth.XXL, - validators: [{ name: ValidatorNames.MaxLength, args: 60 }], - }, - { - name: 'techRecord_manufacturerDetails_address2', - label: 'Address line 2', - type: FormNodeTypes.CONTROL, - width: FormNodeWidth.XXL, - validators: [{ name: ValidatorNames.MaxLength, args: 60 }], - }, - { - name: 'techRecord_manufacturerDetails_postTown', - label: 'Town or City', - type: FormNodeTypes.CONTROL, - width: FormNodeWidth.XL, - validators: [{ name: ValidatorNames.MaxLength, args: 60 }], - }, - { - name: 'techRecord_manufacturerDetails_address3', - label: 'County', - type: FormNodeTypes.CONTROL, - width: FormNodeWidth.XL, - validators: [{ name: ValidatorNames.MaxLength, args: 60 }], - }, - { - name: 'techRecord_manufacturerDetails_postCode', - label: 'Postcode', - type: FormNodeTypes.CONTROL, - width: FormNodeWidth.L, - validators: [{ name: ValidatorNames.MaxLength, args: 12 }], - }, - { - name: 'techRecord_manufacturerDetails_telephoneNumber', - label: 'Telephone number', - type: FormNodeTypes.CONTROL, - width: FormNodeWidth.XL, - validators: [{ name: ValidatorNames.MaxLength, args: 25 }], - }, - { - name: 'techRecord_manufacturerDetails_emailAddress', - label: 'Email address', - type: FormNodeTypes.CONTROL, - width: FormNodeWidth.XL, - validators: [{ name: ValidatorNames.MaxLength, args: 255 }, { name: ValidatorNames.Email }], - }, - { - name: 'techRecord_manufacturerDetails_faxNumber', - label: 'Fax Number', - type: FormNodeTypes.CONTROL, - width: FormNodeWidth.XL, - validators: [{ name: ValidatorNames.MaxLength, args: 25 }], - }, - { - name: 'techRecord_manufacturerDetails_manufacturerNotes', - label: 'Manufacturer Notes', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.TEXTAREA, - validators: [{ name: ValidatorNames.MaxLength, args: 1024 }], - }, - ], + name: 'manufacturerSection', + label: 'Manufacturer', + type: FormNodeTypes.GROUP, + children: [ + { + name: 'techRecord_manufacturerDetails_name', + label: 'Name', + type: FormNodeTypes.CONTROL, + width: FormNodeWidth.XXL, + validators: [{ name: ValidatorNames.MaxLength, args: 150 }], + }, + { + name: 'techRecord_manufacturerDetails_address1', + label: 'Address line 1', + type: FormNodeTypes.CONTROL, + width: FormNodeWidth.XXL, + validators: [{ name: ValidatorNames.MaxLength, args: 60 }], + }, + { + name: 'techRecord_manufacturerDetails_address2', + label: 'Address line 2', + type: FormNodeTypes.CONTROL, + width: FormNodeWidth.XXL, + validators: [{ name: ValidatorNames.MaxLength, args: 60 }], + }, + { + name: 'techRecord_manufacturerDetails_postTown', + label: 'Town or City', + type: FormNodeTypes.CONTROL, + width: FormNodeWidth.XL, + validators: [{ name: ValidatorNames.MaxLength, args: 60 }], + }, + { + name: 'techRecord_manufacturerDetails_address3', + label: 'County', + type: FormNodeTypes.CONTROL, + width: FormNodeWidth.XL, + validators: [{ name: ValidatorNames.MaxLength, args: 60 }], + }, + { + name: 'techRecord_manufacturerDetails_postCode', + label: 'Postcode', + type: FormNodeTypes.CONTROL, + width: FormNodeWidth.L, + validators: [{ name: ValidatorNames.MaxLength, args: 12 }], + }, + { + name: 'techRecord_manufacturerDetails_telephoneNumber', + label: 'Telephone number', + type: FormNodeTypes.CONTROL, + width: FormNodeWidth.XL, + validators: [{ name: ValidatorNames.MaxLength, args: 25 }], + }, + { + name: 'techRecord_manufacturerDetails_emailAddress', + label: 'Email address', + type: FormNodeTypes.CONTROL, + width: FormNodeWidth.XL, + validators: [{ name: ValidatorNames.MaxLength, args: 255 }, { name: ValidatorNames.Email }], + }, + { + name: 'techRecord_manufacturerDetails_faxNumber', + label: 'Fax Number', + type: FormNodeTypes.CONTROL, + width: FormNodeWidth.XL, + validators: [{ name: ValidatorNames.MaxLength, args: 25 }], + }, + { + name: 'techRecord_manufacturerDetails_manufacturerNotes', + label: 'Manufacturer Notes', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.TEXTAREA, + validators: [{ name: ValidatorNames.MaxLength, args: 1024 }], + }, + ], }; diff --git a/src/app/forms/templates/general/notes.template.ts b/src/app/forms/templates/general/notes.template.ts index a8121dc9a1..877fff80aa 100644 --- a/src/app/forms/templates/general/notes.template.ts +++ b/src/app/forms/templates/general/notes.template.ts @@ -1,19 +1,17 @@ import { ValidatorNames } from '@forms/models/validators.enum'; -import { - FormNode, FormNodeEditTypes, FormNodeTypes, FormNodeViewTypes, -} from '../../services/dynamic-form.types'; +import { FormNode, FormNodeEditTypes, FormNodeTypes, FormNodeViewTypes } from '../../services/dynamic-form.types'; export const NotesTemplate: FormNode = { - name: 'notesSection', - label: 'Notes', - type: FormNodeTypes.GROUP, - children: [ - { - name: 'techRecord_notes', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.FULLWIDTH, - editType: FormNodeEditTypes.TEXTAREA, - validators: [{ name: ValidatorNames.MaxLength, args: 1024 }], - }, - ], + name: 'notesSection', + label: 'Notes', + type: FormNodeTypes.GROUP, + children: [ + { + name: 'techRecord_notes', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.FULLWIDTH, + editType: FormNodeEditTypes.TEXTAREA, + validators: [{ name: ValidatorNames.MaxLength, args: 1024 }], + }, + ], }; diff --git a/src/app/forms/templates/general/plates.template.ts b/src/app/forms/templates/general/plates.template.ts index 7a5b468d11..0d007a0669 100644 --- a/src/app/forms/templates/general/plates.template.ts +++ b/src/app/forms/templates/general/plates.template.ts @@ -1,58 +1,56 @@ import { getOptionsFromEnum } from '@forms/utils/enum-map'; import { PlateReasonForIssue } from '@models/vehicle-tech-record.model'; -import { - FormNode, FormNodeEditTypes, FormNodeTypes, FormNodeWidth, -} from '../../services/dynamic-form.types'; +import { FormNode, FormNodeEditTypes, FormNodeTypes, FormNodeWidth } from '../../services/dynamic-form.types'; export const PlatesTemplate: FormNode = { - name: 'platesSection', - label: 'Plates', - type: FormNodeTypes.GROUP, - children: [ - { - name: 'techRecord_plates', - type: FormNodeTypes.ARRAY, - children: [ - { - name: '0', - type: FormNodeTypes.GROUP, - children: [ - { - name: 'plateSerialNumber', - label: 'Plate serial number', - value: null, - type: FormNodeTypes.CONTROL, - width: FormNodeWidth.L, - disabled: true, - }, - { - name: 'plateIssueDate', - label: 'Plate issue date', - value: null, - type: FormNodeTypes.CONTROL, - width: FormNodeWidth.L, - disabled: true, - }, - { - name: 'plateReasonForIssue', - label: 'Plate reason for issue', - value: null, - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.SELECT, - options: getOptionsFromEnum(PlateReasonForIssue), - width: FormNodeWidth.L, - }, - { - name: 'plateIssuer', - label: 'Plate issuer', - value: null, - type: FormNodeTypes.CONTROL, - width: FormNodeWidth.L, - disabled: true, - }, - ], - }, - ], - }, - ], + name: 'platesSection', + label: 'Plates', + type: FormNodeTypes.GROUP, + children: [ + { + name: 'techRecord_plates', + type: FormNodeTypes.ARRAY, + children: [ + { + name: '0', + type: FormNodeTypes.GROUP, + children: [ + { + name: 'plateSerialNumber', + label: 'Plate serial number', + value: null, + type: FormNodeTypes.CONTROL, + width: FormNodeWidth.L, + disabled: true, + }, + { + name: 'plateIssueDate', + label: 'Plate issue date', + value: null, + type: FormNodeTypes.CONTROL, + width: FormNodeWidth.L, + disabled: true, + }, + { + name: 'plateReasonForIssue', + label: 'Plate reason for issue', + value: null, + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.SELECT, + options: getOptionsFromEnum(PlateReasonForIssue), + width: FormNodeWidth.L, + }, + { + name: 'plateIssuer', + label: 'Plate issuer', + value: null, + type: FormNodeTypes.CONTROL, + width: FormNodeWidth.L, + disabled: true, + }, + ], + }, + ], + }, + ], }; diff --git a/src/app/forms/templates/general/reason-for-creation.template.ts b/src/app/forms/templates/general/reason-for-creation.template.ts index f20082f673..88bf70cd38 100644 --- a/src/app/forms/templates/general/reason-for-creation.template.ts +++ b/src/app/forms/templates/general/reason-for-creation.template.ts @@ -1,38 +1,41 @@ import { ValidatorNames } from '@forms/models/validators.enum'; import { - FormNode, FormNodeEditTypes, FormNodeTypes, FormNodeViewTypes, - TagTypeLabels, + FormNode, + FormNodeEditTypes, + FormNodeTypes, + FormNodeViewTypes, + TagTypeLabels, } from '@forms/services/dynamic-form.types'; import { TagType } from '@shared/components/tag/tag.component'; export const TechRecordReasonForCreationSection: FormNode = { - name: 'reasonForCreationSection', - label: 'Reason for creation', - type: FormNodeTypes.GROUP, - children: [ - { - name: 'techRecord_reasonForCreation', - label: 'Reason for creation', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.TEXTAREA, - validators: [{ name: ValidatorNames.MaxLength, args: 500 }, { name: ValidatorNames.Required }], - customTags: [{ colour: TagType.RED, label: TagTypeLabels.REQUIRED }], - }, - ], + name: 'reasonForCreationSection', + label: 'Reason for creation', + type: FormNodeTypes.GROUP, + children: [ + { + name: 'techRecord_reasonForCreation', + label: 'Reason for creation', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.TEXTAREA, + validators: [{ name: ValidatorNames.MaxLength, args: 500 }, { name: ValidatorNames.Required }], + customTags: [{ colour: TagType.RED, label: TagTypeLabels.REQUIRED }], + }, + ], }; export const TechRecordReasonForCreationHiddenSection: FormNode = { - name: 'requiredSection', - label: 'Reason for creation', - type: FormNodeTypes.GROUP, - children: [ - { - name: 'techRecord_reasonForCreation', - label: 'Reason for creation', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - }, - ], + name: 'requiredSection', + label: 'Reason for creation', + type: FormNodeTypes.GROUP, + children: [ + { + name: 'techRecord_reasonForCreation', + label: 'Reason for creation', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + }, + ], }; diff --git a/src/app/forms/templates/general/required-standards.template.ts b/src/app/forms/templates/general/required-standards.template.ts index beebdd561c..dcb9bda821 100644 --- a/src/app/forms/templates/general/required-standards.template.ts +++ b/src/app/forms/templates/general/required-standards.template.ts @@ -1,93 +1,93 @@ import { FormNode, FormNodeEditTypes, FormNodeTypes } from '@forms/services/dynamic-form.types'; export const RequiredStandardsTpl: FormNode = { - name: 'requiredStandards', - label: 'Required Standards', - type: FormNodeTypes.GROUP, - children: [ - { - name: 'testTypes', - label: 'Test Types', - type: FormNodeTypes.ARRAY, - children: [ - { - name: '0', // it is important here that the name of the node for an ARRAY type should be an index value - type: FormNodeTypes.GROUP, - children: [ - { - name: 'requiredStandards', - type: FormNodeTypes.ARRAY, - children: [ - { - name: '0', - type: FormNodeTypes.GROUP, - children: [ - { - name: 'sectionNumber', - label: 'Section number', - type: FormNodeTypes.CONTROL, - disabled: true, - }, - { - name: 'sectionDescription', - label: 'Section description', - type: FormNodeTypes.CONTROL, - disabled: true, - }, - { - name: 'rsNumber', - label: 'Required standard number', - type: FormNodeTypes.CONTROL, - disabled: true, - }, - { - name: 'requiredStandard', - label: 'Required standard description', - type: FormNodeTypes.CONTROL, - disabled: true, - }, - { - name: 'refCalculation', - label: 'Ref calculation', - type: FormNodeTypes.CONTROL, - disabled: true, - }, - { - name: 'additionalInfo', - label: 'Additional information', - type: FormNodeTypes.CONTROL, - disabled: true, - }, - { - name: 'inspectionTypes', - label: 'Inspection Types', - type: FormNodeTypes.CONTROL, - disabled: true, - }, - { - name: 'additionalNotes', - label: 'Notes', - value: null, - type: FormNodeTypes.CONTROL, - }, - { - name: 'prs', - label: 'PRS', - value: null, - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.RADIO, - options: [ - { value: true, label: 'Yes' }, - { value: false, label: 'No' }, - ], - }, - ], - }, - ], - }, - ], - }, - ], - }, - ], + name: 'requiredStandards', + label: 'Required Standards', + type: FormNodeTypes.GROUP, + children: [ + { + name: 'testTypes', + label: 'Test Types', + type: FormNodeTypes.ARRAY, + children: [ + { + name: '0', // it is important here that the name of the node for an ARRAY type should be an index value + type: FormNodeTypes.GROUP, + children: [ + { + name: 'requiredStandards', + type: FormNodeTypes.ARRAY, + children: [ + { + name: '0', + type: FormNodeTypes.GROUP, + children: [ + { + name: 'sectionNumber', + label: 'Section number', + type: FormNodeTypes.CONTROL, + disabled: true, + }, + { + name: 'sectionDescription', + label: 'Section description', + type: FormNodeTypes.CONTROL, + disabled: true, + }, + { + name: 'rsNumber', + label: 'Required standard number', + type: FormNodeTypes.CONTROL, + disabled: true, + }, + { + name: 'requiredStandard', + label: 'Required standard description', + type: FormNodeTypes.CONTROL, + disabled: true, + }, + { + name: 'refCalculation', + label: 'Ref calculation', + type: FormNodeTypes.CONTROL, + disabled: true, + }, + { + name: 'additionalInfo', + label: 'Additional information', + type: FormNodeTypes.CONTROL, + disabled: true, + }, + { + name: 'inspectionTypes', + label: 'Inspection Types', + type: FormNodeTypes.CONTROL, + disabled: true, + }, + { + name: 'additionalNotes', + label: 'Notes', + value: null, + type: FormNodeTypes.CONTROL, + }, + { + name: 'prs', + label: 'PRS', + value: null, + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.RADIO, + options: [ + { value: true, label: 'Yes' }, + { value: false, label: 'No' }, + ], + }, + ], + }, + ], + }, + ], + }, + ], + }, + ], }; diff --git a/src/app/forms/templates/hgv/hgv-dimensions.template.ts b/src/app/forms/templates/hgv/hgv-dimensions.template.ts index de3a2553ad..e65329446a 100644 --- a/src/app/forms/templates/hgv/hgv-dimensions.template.ts +++ b/src/app/forms/templates/hgv/hgv-dimensions.template.ts @@ -1,84 +1,82 @@ import { ValidatorNames } from '@forms/models/validators.enum'; import { TagType } from '@shared/components/tag/tag.component'; -import { - FormNode, FormNodeEditTypes, FormNodeTypes, TagTypeLabels, -} from '../../services/dynamic-form.types'; +import { FormNode, FormNodeEditTypes, FormNodeTypes, TagTypeLabels } from '../../services/dynamic-form.types'; export const HgvDimensionsTemplate: FormNode = { - name: 'dimensionsSection', - label: 'Dimensions', - type: FormNodeTypes.SECTION, - children: [ - { - name: 'techRecord_dimensions_length', - label: 'Length (mm)', - value: null, - type: FormNodeTypes.CONTROL, - validators: [{ name: ValidatorNames.Max, args: 99999 }], - customTags: [{ colour: TagType.PURPLE, label: TagTypeLabels.PLATES }], - }, - { - name: 'techRecord_dimensions_width', - label: 'Width (mm)', - value: null, - type: FormNodeTypes.CONTROL, - validators: [{ name: ValidatorNames.Max, args: 99999 }], - customTags: [{ colour: TagType.PURPLE, label: TagTypeLabels.PLATES }], - }, - { - name: 'techRecord_dimensions_axleSpacing', - type: FormNodeTypes.ARRAY, - children: [ - { - name: '0', - type: FormNodeTypes.GROUP, - children: [ - { - name: 'value', - label: 'Axle to axle (mm)', - value: null, - editType: FormNodeEditTypes.NUMBER, - type: FormNodeTypes.CONTROL, - validators: [{ name: ValidatorNames.Max, args: 99999 }], - }, - ], - }, - ], - }, - { - name: 'techRecord_frontAxleToRearAxle', - label: 'Front axle to rear axle (mm)', - value: null, - type: FormNodeTypes.CONTROL, - validators: [{ name: ValidatorNames.Max, args: 99999 }], - }, - { - name: 'techRecord_frontVehicleTo5thWheelCouplingMin', - label: 'Minimum', - value: null, - type: FormNodeTypes.CONTROL, - validators: [{ name: ValidatorNames.Max, args: 99999 }], - }, - { - name: 'techRecord_frontVehicleTo5thWheelCouplingMax', - label: 'Maximum', - value: null, - type: FormNodeTypes.CONTROL, - validators: [{ name: ValidatorNames.Max, args: 99999 }], - }, - { - name: 'techRecord_frontAxleTo5thWheelMin', - label: 'Minimum', - value: null, - type: FormNodeTypes.CONTROL, - validators: [{ name: ValidatorNames.Max, args: 99999 }], - }, - { - name: 'techRecord_frontAxleTo5thWheelMax', - label: 'Maximum', - value: null, - type: FormNodeTypes.CONTROL, - validators: [{ name: ValidatorNames.Max, args: 99999 }], - }, - ], + name: 'dimensionsSection', + label: 'Dimensions', + type: FormNodeTypes.SECTION, + children: [ + { + name: 'techRecord_dimensions_length', + label: 'Length (mm)', + value: null, + type: FormNodeTypes.CONTROL, + validators: [{ name: ValidatorNames.Max, args: 99999 }], + customTags: [{ colour: TagType.PURPLE, label: TagTypeLabels.PLATES }], + }, + { + name: 'techRecord_dimensions_width', + label: 'Width (mm)', + value: null, + type: FormNodeTypes.CONTROL, + validators: [{ name: ValidatorNames.Max, args: 99999 }], + customTags: [{ colour: TagType.PURPLE, label: TagTypeLabels.PLATES }], + }, + { + name: 'techRecord_dimensions_axleSpacing', + type: FormNodeTypes.ARRAY, + children: [ + { + name: '0', + type: FormNodeTypes.GROUP, + children: [ + { + name: 'value', + label: 'Axle to axle (mm)', + value: null, + editType: FormNodeEditTypes.NUMBER, + type: FormNodeTypes.CONTROL, + validators: [{ name: ValidatorNames.Max, args: 99999 }], + }, + ], + }, + ], + }, + { + name: 'techRecord_frontAxleToRearAxle', + label: 'Front axle to rear axle (mm)', + value: null, + type: FormNodeTypes.CONTROL, + validators: [{ name: ValidatorNames.Max, args: 99999 }], + }, + { + name: 'techRecord_frontVehicleTo5thWheelCouplingMin', + label: 'Minimum', + value: null, + type: FormNodeTypes.CONTROL, + validators: [{ name: ValidatorNames.Max, args: 99999 }], + }, + { + name: 'techRecord_frontVehicleTo5thWheelCouplingMax', + label: 'Maximum', + value: null, + type: FormNodeTypes.CONTROL, + validators: [{ name: ValidatorNames.Max, args: 99999 }], + }, + { + name: 'techRecord_frontAxleTo5thWheelMin', + label: 'Minimum', + value: null, + type: FormNodeTypes.CONTROL, + validators: [{ name: ValidatorNames.Max, args: 99999 }], + }, + { + name: 'techRecord_frontAxleTo5thWheelMax', + label: 'Maximum', + value: null, + type: FormNodeTypes.CONTROL, + validators: [{ name: ValidatorNames.Max, args: 99999 }], + }, + ], }; diff --git a/src/app/forms/templates/hgv/hgv-tech-record.template.ts b/src/app/forms/templates/hgv/hgv-tech-record.template.ts index ca1eb60f57..33cf2e8a88 100644 --- a/src/app/forms/templates/hgv/hgv-tech-record.template.ts +++ b/src/app/forms/templates/hgv/hgv-tech-record.template.ts @@ -6,232 +6,239 @@ import { EmissionStandard } from '@models/test-types/emissions.enum'; import { FuelTypes } from '@models/vehicle-tech-record.model'; import { TagType } from '@shared/components/tag/tag.component'; import { - FormNode, FormNodeEditTypes, FormNodeTypes, FormNodeViewTypes, FormNodeWidth, TagTypeLabels, + FormNode, + FormNodeEditTypes, + FormNodeTypes, + FormNodeViewTypes, + FormNodeWidth, + TagTypeLabels, } from '../../services/dynamic-form.types'; export const HgvTechRecord: FormNode = { - name: 'techRecordSummary', - type: FormNodeTypes.GROUP, - label: 'Vehicle Summary', - children: [ - { - name: 'techRecord_vehicleType', - label: 'Vehicle type', - value: '', - width: FormNodeWidth.XS, - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.VEHICLETYPE, - disabled: true, - customTags: [{ colour: TagType.RED, label: TagTypeLabels.REQUIRED }], - }, - { - name: 'techRecord_statusCode', - label: 'Record status', - value: '', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - customTags: [{ colour: TagType.RED, label: TagTypeLabels.REQUIRED }], - }, - { - name: 'techRecord_numberOfWheelsDriven', - value: null, - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - }, - { - name: 'techRecord_regnDate', - label: 'Date of first registration', - value: null, - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.DATE, - editType: FormNodeEditTypes.DATE, - validators: [], - isoDate: false, - customTags: [{ colour: TagType.PURPLE, label: TagTypeLabels.PLATES }], - }, - { - name: 'techRecord_manufactureYear', - label: 'Year of manufacture', - value: null, - width: FormNodeWidth.XS, - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.NUMBER, - validators: [ - { name: ValidatorNames.Max, args: 9999 }, - { name: ValidatorNames.Min, args: 1000 }, - { name: ValidatorNames.PastYear }, - ], - customTags: [{ colour: TagType.PURPLE, label: TagTypeLabels.PLATES }], - }, - { - name: 'techRecord_noOfAxles', - label: 'Number of axles', - value: null, - width: FormNodeWidth.XXS, - type: FormNodeTypes.CONTROL, - validators: [], - disabled: true, - customTags: [{ colour: TagType.RED, label: TagTypeLabels.REQUIRED }], - }, - { - name: 'techRecord_speedLimiterMrk', - label: 'Speed limiter exempt', - value: null, - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.RADIO, - options: [ - { value: true, label: 'Exempt' }, - { value: false, label: 'Not exempt' }, - ], - validators: [], - class: 'flex--half', - customTags: [{ colour: TagType.PURPLE, label: TagTypeLabels.PLATES }], - }, - { - name: 'techRecord_tachoExemptMrk', - label: 'Tacho exempt', - value: null, - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.RADIO, - options: [ - { value: true, label: 'Exempt' }, - { value: false, label: 'Not exempt' }, - ], - validators: [], - class: 'flex--half', - }, - { - name: 'techRecord_euroStandard', - label: 'Euro standard', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.RADIO, - options: [{ label: '0.10 g/kWh Euro III PM', value: '0.10 g/kWh Euro 3 PM' }, - ...getOptionsFromEnum(EmissionStandard), - ], - }, - { - name: 'techRecord_roadFriendly', - label: 'Road friendly suspension', - value: null, - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.RADIO, - options: [ - { value: true, label: 'Yes' }, - { value: false, label: 'No' }, - ], - customTags: [{ colour: TagType.PURPLE, label: TagTypeLabels.PLATES }], - }, - { - name: 'techRecord_fuelPropulsionSystem', - label: 'Fuel / propulsion system', - value: null, - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.SELECT, - options: getOptionsFromEnum(FuelTypes), - validators: [], - }, - { - name: 'techRecord_drawbarCouplingFitted', - label: 'Drawbar coupling fitted', - value: null, - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.RADIO, - options: [ - { value: true, label: 'Yes' }, - { value: false, label: 'No' }, - ], - }, - { - name: 'techRecord_vehicleClass_description', - label: 'Vehicle class', - value: '', - customId: 'vehicleClassDescription', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.SELECT, - options: [ - { label: 'heavy goods vehicle', value: 'heavy goods vehicle' }, - ], - validators: [{ name: ValidatorNames.Required }], - customTags: [{ colour: TagType.RED, label: TagTypeLabels.REQUIRED }], - }, - { - name: 'techRecord_vehicleConfiguration', - label: 'Vehicle configuration', - value: null, - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.SELECT, - options: getOptionsFromEnum(VehicleConfiguration), - validators: [{ name: ValidatorNames.UpdateFunctionCode }], - customTags: [{ colour: TagType.RED, label: TagTypeLabels.REQUIRED }, { colour: TagType.PURPLE, label: TagTypeLabels.PLATES }], - }, - { - name: 'techRecord_offRoad', - label: 'Off road vehicle', - value: null, - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.RADIO, - options: [ - { value: true, label: 'Yes' }, - { value: false, label: 'No' }, - ], - }, - { - name: 'techRecord_euVehicleCategory', - label: 'EU vehicle category', - value: null, - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.SELECT, - width: FormNodeWidth.S, - options: getOptionsFromEnum(EUVehicleCategory), - validators: [], - }, - { - name: 'techRecord_emissionsLimit', - label: 'Emission limit (m-1) (plate value)', - value: null, - width: FormNodeWidth.XXS, - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.NUMBER, - validators: [ - { name: ValidatorNames.Max, args: 99 }, - { - name: ValidatorNames.CustomPattern, - args: ['^\\d*(\\.\\d{0,5})?$', 'Max 5 decimal places'], - }, - ], - enableDecimals: true, - }, - { - name: 'techRecord_departmentalVehicleMarker', - label: 'Departmental vehicle marker', - value: null, - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.RADIO, - options: [ - { value: true, label: 'Yes' }, - { value: false, label: 'No' }, - ], - }, - { - name: 'techRecord_alterationMarker', - label: 'Alteration marker', - value: null, - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.RADIO, - options: [ - { value: true, label: 'Yes' }, - { value: false, label: 'No' }, - ], - }, - { - name: 'techRecord_functionCode', - label: 'Function code', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - ], + name: 'techRecordSummary', + type: FormNodeTypes.GROUP, + label: 'Vehicle Summary', + children: [ + { + name: 'techRecord_vehicleType', + label: 'Vehicle type', + value: '', + width: FormNodeWidth.XS, + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.VEHICLETYPE, + disabled: true, + customTags: [{ colour: TagType.RED, label: TagTypeLabels.REQUIRED }], + }, + { + name: 'techRecord_statusCode', + label: 'Record status', + value: '', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + customTags: [{ colour: TagType.RED, label: TagTypeLabels.REQUIRED }], + }, + { + name: 'techRecord_numberOfWheelsDriven', + value: null, + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + }, + { + name: 'techRecord_regnDate', + label: 'Date of first registration', + value: null, + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.DATE, + editType: FormNodeEditTypes.DATE, + validators: [], + isoDate: false, + customTags: [{ colour: TagType.PURPLE, label: TagTypeLabels.PLATES }], + }, + { + name: 'techRecord_manufactureYear', + label: 'Year of manufacture', + value: null, + width: FormNodeWidth.XS, + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.NUMBER, + validators: [ + { name: ValidatorNames.Max, args: 9999 }, + { name: ValidatorNames.Min, args: 1000 }, + { name: ValidatorNames.PastYear }, + ], + customTags: [{ colour: TagType.PURPLE, label: TagTypeLabels.PLATES }], + }, + { + name: 'techRecord_noOfAxles', + label: 'Number of axles', + value: null, + width: FormNodeWidth.XXS, + type: FormNodeTypes.CONTROL, + validators: [], + disabled: true, + customTags: [{ colour: TagType.RED, label: TagTypeLabels.REQUIRED }], + }, + { + name: 'techRecord_speedLimiterMrk', + label: 'Speed limiter exempt', + value: null, + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.RADIO, + options: [ + { value: true, label: 'Exempt' }, + { value: false, label: 'Not exempt' }, + ], + validators: [], + class: 'flex--half', + customTags: [{ colour: TagType.PURPLE, label: TagTypeLabels.PLATES }], + }, + { + name: 'techRecord_tachoExemptMrk', + label: 'Tacho exempt', + value: null, + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.RADIO, + options: [ + { value: true, label: 'Exempt' }, + { value: false, label: 'Not exempt' }, + ], + validators: [], + class: 'flex--half', + }, + { + name: 'techRecord_euroStandard', + label: 'Euro standard', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.RADIO, + options: [ + { label: '0.10 g/kWh Euro III PM', value: '0.10 g/kWh Euro 3 PM' }, + ...getOptionsFromEnum(EmissionStandard), + ], + }, + { + name: 'techRecord_roadFriendly', + label: 'Road friendly suspension', + value: null, + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.RADIO, + options: [ + { value: true, label: 'Yes' }, + { value: false, label: 'No' }, + ], + customTags: [{ colour: TagType.PURPLE, label: TagTypeLabels.PLATES }], + }, + { + name: 'techRecord_fuelPropulsionSystem', + label: 'Fuel / propulsion system', + value: null, + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.SELECT, + options: getOptionsFromEnum(FuelTypes), + validators: [], + }, + { + name: 'techRecord_drawbarCouplingFitted', + label: 'Drawbar coupling fitted', + value: null, + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.RADIO, + options: [ + { value: true, label: 'Yes' }, + { value: false, label: 'No' }, + ], + }, + { + name: 'techRecord_vehicleClass_description', + label: 'Vehicle class', + value: '', + customId: 'vehicleClassDescription', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.SELECT, + options: [{ label: 'heavy goods vehicle', value: 'heavy goods vehicle' }], + validators: [{ name: ValidatorNames.Required }], + customTags: [{ colour: TagType.RED, label: TagTypeLabels.REQUIRED }], + }, + { + name: 'techRecord_vehicleConfiguration', + label: 'Vehicle configuration', + value: null, + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.SELECT, + options: getOptionsFromEnum(VehicleConfiguration), + validators: [{ name: ValidatorNames.UpdateFunctionCode }], + customTags: [ + { colour: TagType.RED, label: TagTypeLabels.REQUIRED }, + { colour: TagType.PURPLE, label: TagTypeLabels.PLATES }, + ], + }, + { + name: 'techRecord_offRoad', + label: 'Off road vehicle', + value: null, + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.RADIO, + options: [ + { value: true, label: 'Yes' }, + { value: false, label: 'No' }, + ], + }, + { + name: 'techRecord_euVehicleCategory', + label: 'EU vehicle category', + value: null, + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.SELECT, + width: FormNodeWidth.S, + options: getOptionsFromEnum(EUVehicleCategory), + validators: [], + }, + { + name: 'techRecord_emissionsLimit', + label: 'Emission limit (m-1) (plate value)', + value: null, + width: FormNodeWidth.XXS, + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.NUMBER, + validators: [ + { name: ValidatorNames.Max, args: 99 }, + { + name: ValidatorNames.CustomPattern, + args: ['^\\d*(\\.\\d{0,5})?$', 'Max 5 decimal places'], + }, + ], + enableDecimals: true, + }, + { + name: 'techRecord_departmentalVehicleMarker', + label: 'Departmental vehicle marker', + value: null, + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.RADIO, + options: [ + { value: true, label: 'Yes' }, + { value: false, label: 'No' }, + ], + }, + { + name: 'techRecord_alterationMarker', + label: 'Alteration marker', + value: null, + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.RADIO, + options: [ + { value: true, label: 'Yes' }, + { value: false, label: 'No' }, + ], + }, + { + name: 'techRecord_functionCode', + label: 'Function code', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + ], }; diff --git a/src/app/forms/templates/hgv/hgv-tyres.template.ts b/src/app/forms/templates/hgv/hgv-tyres.template.ts index c025cbc04c..70664e3d2d 100644 --- a/src/app/forms/templates/hgv/hgv-tyres.template.ts +++ b/src/app/forms/templates/hgv/hgv-tyres.template.ts @@ -1,98 +1,109 @@ import { TyreUseCode } from '@dvsa/cvs-type-definitions/types/v3/tech-record/enums/tyreUseCodeHgv.enum.js'; import { ValidatorNames } from '@forms/models/validators.enum'; import { - FormNode, - FormNodeEditTypes, - FormNodeTypes, - FormNodeWidth, - TagTypeLabels, + FormNode, + FormNodeEditTypes, + FormNodeTypes, + FormNodeWidth, + TagTypeLabels, } from '@forms/services/dynamic-form.types'; import { getOptionsFromEnum } from '@forms/utils/enum-map'; import { TagType } from '@shared/components/tag/tag.component'; export const tyresTemplateHgv: FormNode = { - name: 'tyreSection', - type: FormNodeTypes.GROUP, - label: 'Tyres', - children: [ - { - name: 'techRecord_tyreUseCode', - label: 'Tyre use code', - value: null, - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.SELECT, - width: FormNodeWidth.UNSET, - options: getOptionsFromEnum(TyreUseCode), - customTags: [{ colour: TagType.PURPLE, label: TagTypeLabels.PLATES }], - }, - { - name: 'techRecord_axles', - value: '', - type: FormNodeTypes.ARRAY, - validators: [{ - name: ValidatorNames.MinArrayLengthIfNotEmpty, args: { minimumLength: 2, message: 'You cannot submit a HGV with less than 2 axles.' }, - }], - children: [ - { - name: '0', - label: 'Axle', - value: '', - type: FormNodeTypes.GROUP, - children: [ - { - name: 'axleNumber', - label: 'Axle Number', - type: FormNodeTypes.CONTROL, - }, - { - name: 'tyres_tyreCode', - label: 'Tyre Code', - value: null, - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.NUMBER, - validators: [{ name: ValidatorNames.Numeric }, { name: ValidatorNames.Max, args: 99999 }, { name: ValidatorNames.Min, args: 0 }], - }, - { - name: 'tyres_tyreSize', - label: 'Tyre Size', - value: null, - type: FormNodeTypes.CONTROL, - disabled: true, - validators: [ - { name: ValidatorNames.MaxLength, args: 12 }, - { name: ValidatorNames.Min, args: 0 }, - ], - }, - { - name: 'tyres_plyRating', - label: 'Ply Rating', - value: null, - type: FormNodeTypes.CONTROL, - disabled: true, - validators: [ - { name: ValidatorNames.MaxLength, args: 2 }, - { name: ValidatorNames.Min, args: 0 }, - ], - }, - { - name: 'tyres_fitmentCode', - label: 'Fitment code', - value: null, - type: FormNodeTypes.CONTROL, - validators: [], - }, - { - name: 'tyres_dataTrAxles', - label: 'Load index', - value: null, - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.NUMBER, - disabled: true, - validators: [{ name: ValidatorNames.Numeric }, { name: ValidatorNames.Max, args: 999 }, { name: ValidatorNames.Min, args: 0 }], - }, - ], - }, - ], - }, - ], + name: 'tyreSection', + type: FormNodeTypes.GROUP, + label: 'Tyres', + children: [ + { + name: 'techRecord_tyreUseCode', + label: 'Tyre use code', + value: null, + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.SELECT, + width: FormNodeWidth.UNSET, + options: getOptionsFromEnum(TyreUseCode), + customTags: [{ colour: TagType.PURPLE, label: TagTypeLabels.PLATES }], + }, + { + name: 'techRecord_axles', + value: '', + type: FormNodeTypes.ARRAY, + validators: [ + { + name: ValidatorNames.MinArrayLengthIfNotEmpty, + args: { minimumLength: 2, message: 'You cannot submit a HGV with less than 2 axles.' }, + }, + ], + children: [ + { + name: '0', + label: 'Axle', + value: '', + type: FormNodeTypes.GROUP, + children: [ + { + name: 'axleNumber', + label: 'Axle Number', + type: FormNodeTypes.CONTROL, + }, + { + name: 'tyres_tyreCode', + label: 'Tyre Code', + value: null, + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.NUMBER, + validators: [ + { name: ValidatorNames.Numeric }, + { name: ValidatorNames.Max, args: 99999 }, + { name: ValidatorNames.Min, args: 0 }, + ], + }, + { + name: 'tyres_tyreSize', + label: 'Tyre Size', + value: null, + type: FormNodeTypes.CONTROL, + disabled: true, + validators: [ + { name: ValidatorNames.MaxLength, args: 12 }, + { name: ValidatorNames.Min, args: 0 }, + ], + }, + { + name: 'tyres_plyRating', + label: 'Ply Rating', + value: null, + type: FormNodeTypes.CONTROL, + disabled: true, + validators: [ + { name: ValidatorNames.MaxLength, args: 2 }, + { name: ValidatorNames.Min, args: 0 }, + ], + }, + { + name: 'tyres_fitmentCode', + label: 'Fitment code', + value: null, + type: FormNodeTypes.CONTROL, + validators: [], + }, + { + name: 'tyres_dataTrAxles', + label: 'Load index', + value: null, + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.NUMBER, + disabled: true, + validators: [ + { name: ValidatorNames.Numeric }, + { name: ValidatorNames.Max, args: 999 }, + { name: ValidatorNames.Min, args: 0 }, + ], + }, + ], + }, + ], + }, + ], }; diff --git a/src/app/forms/templates/hgv/hgv-weight.template.ts b/src/app/forms/templates/hgv/hgv-weight.template.ts index 12d02ef858..9fa08e91d6 100644 --- a/src/app/forms/templates/hgv/hgv-weight.template.ts +++ b/src/app/forms/templates/hgv/hgv-weight.template.ts @@ -1,179 +1,177 @@ import { ValidatorNames } from '@forms/models/validators.enum'; import { TagType } from '@shared/components/tag/tag.component'; -import { - FormNode, FormNodeEditTypes, FormNodeTypes, TagTypeLabels, -} from '../../services/dynamic-form.types'; +import { FormNode, FormNodeEditTypes, FormNodeTypes, TagTypeLabels } from '../../services/dynamic-form.types'; const requiredValidation = [ - { name: ValidatorNames.Numeric, args: 99999 }, - { name: ValidatorNames.Max, args: 99999 }, - { name: ValidatorNames.Min, args: 0 }, + { name: ValidatorNames.Numeric, args: 99999 }, + { name: ValidatorNames.Max, args: 99999 }, + { name: ValidatorNames.Min, args: 0 }, ]; const optionalValidation = [ - { name: ValidatorNames.Numeric, args: 99999 }, - { name: ValidatorNames.Max, args: 99999 }, - { name: ValidatorNames.Min, args: 0 }, + { name: ValidatorNames.Numeric, args: 99999 }, + { name: ValidatorNames.Max, args: 99999 }, + { name: ValidatorNames.Min, args: 0 }, ]; export const HgvWeight: FormNode = { - name: 'weightsSection', - label: 'Weights', - type: FormNodeTypes.GROUP, - children: [ - { - name: 'grossSection', - label: 'Gross vehicle weight', - value: '', - type: FormNodeTypes.SECTION, - }, - { - name: 'techRecord_grossGbWeight', - label: 'GB', - customValidatorErrorName: 'Gross GB Weight', - value: null, - type: FormNodeTypes.CONTROL, - validators: requiredValidation, - customTags: [{ colour: TagType.PURPLE, label: TagTypeLabels.PLATES }], - }, - { - name: 'techRecord_grossEecWeight', - label: 'EEC (optional)', - customValidatorErrorName: 'Gross EEC Weight', - value: null, - editType: FormNodeEditTypes.NUMBER, - type: FormNodeTypes.CONTROL, - validators: optionalValidation, - customTags: [{ colour: TagType.PURPLE, label: TagTypeLabels.PLATES }], - }, - { - name: 'techRecord_grossDesignWeight', - label: 'Design', - customValidatorErrorName: 'Gross Design Weight', - value: null, - type: FormNodeTypes.CONTROL, - validators: requiredValidation, - customTags: [{ colour: TagType.PURPLE, label: TagTypeLabels.PLATES }], - }, - { - name: 'grossTrainSection', - label: 'Gross train weight', - value: null, - type: FormNodeTypes.SECTION, - }, - { - name: 'techRecord_trainGbWeight', - label: 'GB', - customValidatorErrorName: 'Train GB Weight', - value: null, - type: FormNodeTypes.CONTROL, - validators: requiredValidation, - customTags: [{ colour: TagType.PURPLE, label: TagTypeLabels.PLATES }], - }, - { - name: 'techRecord_trainEecWeight', - label: 'EEC (optional)', - customValidatorErrorName: 'Train EEC Weight', - value: null, - editType: FormNodeEditTypes.NUMBER, - type: FormNodeTypes.CONTROL, - validators: optionalValidation, - customTags: [{ colour: TagType.PURPLE, label: TagTypeLabels.PLATES }], - }, - { - name: 'techRecord_trainDesignWeight', - label: 'Design (optional)', - customValidatorErrorName: 'Train Design Weight', - value: null, - type: FormNodeTypes.CONTROL, - validators: optionalValidation, - customTags: [{ colour: TagType.PURPLE, label: TagTypeLabels.PLATES }], - }, - { - name: 'maxTrainSection', - label: 'Max train weight', - value: null, - type: FormNodeTypes.SECTION, - }, - { - name: 'techRecord_maxTrainGbWeight', - label: 'GB', - customValidatorErrorName: 'Max Train GB Weight', - value: null, - type: FormNodeTypes.CONTROL, - validators: requiredValidation, - customTags: [{ colour: TagType.PURPLE, label: TagTypeLabels.PLATES }], - }, - { - name: 'techRecord_maxTrainEecWeight', - label: 'EEC (optional)', - customValidatorErrorName: 'Max Train EEC Weight', - value: null, - editType: FormNodeEditTypes.NUMBER, - type: FormNodeTypes.CONTROL, - validators: optionalValidation, - customTags: [{ colour: TagType.PURPLE, label: TagTypeLabels.PLATES }], - }, - { - name: 'techRecord_maxTrainDesignWeight', - label: 'Design (optional)', - customValidatorErrorName: 'Max Train Design Weight', - value: null, - type: FormNodeTypes.CONTROL, - validators: optionalValidation, - customTags: [{ colour: TagType.PURPLE, label: TagTypeLabels.PLATES }], - }, - { - name: 'axleSection', - label: 'Axle weights', - value: '', - type: FormNodeTypes.SECTION, - customTags: [{ colour: TagType.PURPLE, label: TagTypeLabels.PLATES }], - }, - { - name: 'techRecord_axles', - value: '', - type: FormNodeTypes.ARRAY, - children: [ - { - name: '0', - label: 'Axle', - value: '', - type: FormNodeTypes.GROUP, - children: [ - { - name: 'axleNumber', - label: 'Axle Number', - type: FormNodeTypes.CONTROL, - }, - { - name: 'weights_gbWeight', - label: 'GB weight', - customValidatorErrorName: 'Axle GB Weight', - value: null, - type: FormNodeTypes.CONTROL, - validators: requiredValidation, - }, - { - name: 'weights_eecWeight', - label: 'EEC (optional)', - customValidatorErrorName: 'Axle EEC Weight', - value: null, - editType: FormNodeEditTypes.NUMBER, - type: FormNodeTypes.CONTROL, - validators: optionalValidation, - }, - { - name: 'weights_designWeight', - label: 'Design weight', - customValidatorErrorName: 'Axle Design Weight', - value: null, - type: FormNodeTypes.CONTROL, - validators: requiredValidation, - }, - ], - }, - ], - }, - ], + name: 'weightsSection', + label: 'Weights', + type: FormNodeTypes.GROUP, + children: [ + { + name: 'grossSection', + label: 'Gross vehicle weight', + value: '', + type: FormNodeTypes.SECTION, + }, + { + name: 'techRecord_grossGbWeight', + label: 'GB', + customValidatorErrorName: 'Gross GB Weight', + value: null, + type: FormNodeTypes.CONTROL, + validators: requiredValidation, + customTags: [{ colour: TagType.PURPLE, label: TagTypeLabels.PLATES }], + }, + { + name: 'techRecord_grossEecWeight', + label: 'EEC (optional)', + customValidatorErrorName: 'Gross EEC Weight', + value: null, + editType: FormNodeEditTypes.NUMBER, + type: FormNodeTypes.CONTROL, + validators: optionalValidation, + customTags: [{ colour: TagType.PURPLE, label: TagTypeLabels.PLATES }], + }, + { + name: 'techRecord_grossDesignWeight', + label: 'Design', + customValidatorErrorName: 'Gross Design Weight', + value: null, + type: FormNodeTypes.CONTROL, + validators: requiredValidation, + customTags: [{ colour: TagType.PURPLE, label: TagTypeLabels.PLATES }], + }, + { + name: 'grossTrainSection', + label: 'Gross train weight', + value: null, + type: FormNodeTypes.SECTION, + }, + { + name: 'techRecord_trainGbWeight', + label: 'GB', + customValidatorErrorName: 'Train GB Weight', + value: null, + type: FormNodeTypes.CONTROL, + validators: requiredValidation, + customTags: [{ colour: TagType.PURPLE, label: TagTypeLabels.PLATES }], + }, + { + name: 'techRecord_trainEecWeight', + label: 'EEC (optional)', + customValidatorErrorName: 'Train EEC Weight', + value: null, + editType: FormNodeEditTypes.NUMBER, + type: FormNodeTypes.CONTROL, + validators: optionalValidation, + customTags: [{ colour: TagType.PURPLE, label: TagTypeLabels.PLATES }], + }, + { + name: 'techRecord_trainDesignWeight', + label: 'Design (optional)', + customValidatorErrorName: 'Train Design Weight', + value: null, + type: FormNodeTypes.CONTROL, + validators: optionalValidation, + customTags: [{ colour: TagType.PURPLE, label: TagTypeLabels.PLATES }], + }, + { + name: 'maxTrainSection', + label: 'Max train weight', + value: null, + type: FormNodeTypes.SECTION, + }, + { + name: 'techRecord_maxTrainGbWeight', + label: 'GB', + customValidatorErrorName: 'Max Train GB Weight', + value: null, + type: FormNodeTypes.CONTROL, + validators: requiredValidation, + customTags: [{ colour: TagType.PURPLE, label: TagTypeLabels.PLATES }], + }, + { + name: 'techRecord_maxTrainEecWeight', + label: 'EEC (optional)', + customValidatorErrorName: 'Max Train EEC Weight', + value: null, + editType: FormNodeEditTypes.NUMBER, + type: FormNodeTypes.CONTROL, + validators: optionalValidation, + customTags: [{ colour: TagType.PURPLE, label: TagTypeLabels.PLATES }], + }, + { + name: 'techRecord_maxTrainDesignWeight', + label: 'Design (optional)', + customValidatorErrorName: 'Max Train Design Weight', + value: null, + type: FormNodeTypes.CONTROL, + validators: optionalValidation, + customTags: [{ colour: TagType.PURPLE, label: TagTypeLabels.PLATES }], + }, + { + name: 'axleSection', + label: 'Axle weights', + value: '', + type: FormNodeTypes.SECTION, + customTags: [{ colour: TagType.PURPLE, label: TagTypeLabels.PLATES }], + }, + { + name: 'techRecord_axles', + value: '', + type: FormNodeTypes.ARRAY, + children: [ + { + name: '0', + label: 'Axle', + value: '', + type: FormNodeTypes.GROUP, + children: [ + { + name: 'axleNumber', + label: 'Axle Number', + type: FormNodeTypes.CONTROL, + }, + { + name: 'weights_gbWeight', + label: 'GB weight', + customValidatorErrorName: 'Axle GB Weight', + value: null, + type: FormNodeTypes.CONTROL, + validators: requiredValidation, + }, + { + name: 'weights_eecWeight', + label: 'EEC (optional)', + customValidatorErrorName: 'Axle EEC Weight', + value: null, + editType: FormNodeEditTypes.NUMBER, + type: FormNodeTypes.CONTROL, + validators: optionalValidation, + }, + { + name: 'weights_designWeight', + label: 'Design weight', + customValidatorErrorName: 'Axle Design Weight', + value: null, + type: FormNodeTypes.CONTROL, + validators: requiredValidation, + }, + ], + }, + ], + }, + ], }; diff --git a/src/app/forms/templates/lgv/lgv-tech-record.template.ts b/src/app/forms/templates/lgv/lgv-tech-record.template.ts index 2c12a75724..657f282f8e 100644 --- a/src/app/forms/templates/lgv/lgv-tech-record.template.ts +++ b/src/app/forms/templates/lgv/lgv-tech-record.template.ts @@ -2,98 +2,103 @@ import { EUVehicleCategory } from '@dvsa/cvs-type-definitions/types/v3/tech-reco import { VehicleConfiguration } from '@dvsa/cvs-type-definitions/types/v3/tech-record/enums/vehicleConfigurationLightVehicle.enum.js'; import { ValidatorNames } from '@forms/models/validators.enum'; import { - FormNode, FormNodeEditTypes, FormNodeTypes, FormNodeViewTypes, FormNodeWidth, TagTypeLabels, + FormNode, + FormNodeEditTypes, + FormNodeTypes, + FormNodeViewTypes, + FormNodeWidth, + TagTypeLabels, } from '@forms/services/dynamic-form.types'; import { getOptionsFromEnum } from '@forms/utils/enum-map'; import { VehicleSubclass } from '@models/vehicle-tech-record.model'; import { TagType } from '@shared/components/tag/tag.component'; export const LgvTechRecord: FormNode = { - name: 'techRecordSummary', - type: FormNodeTypes.GROUP, - label: 'Vehicle Summary', - children: [ - { - name: 'techRecord_vehicleType', - label: 'Vehicle type', - value: '', - width: FormNodeWidth.XS, - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.VEHICLETYPE, - disabled: true, - validators: [], - customTags: [{ colour: TagType.RED, label: TagTypeLabels.REQUIRED }], - }, - { - name: 'techRecord_statusCode', - label: 'Record status', - value: '', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - customTags: [{ colour: TagType.RED, label: TagTypeLabels.REQUIRED }], - }, - { - name: 'techRecord_regnDate', - label: 'Date of first registration', - value: null, - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.DATE, - editType: FormNodeEditTypes.DATE, - validators: [], - isoDate: false, - }, - { - name: 'techRecord_manufactureYear', - label: 'Year of manufacture', - value: null, - width: FormNodeWidth.XS, - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.NUMBER, - validators: [ - { name: ValidatorNames.Max, args: 9999 }, - { name: ValidatorNames.Min, args: 1000 }, - { name: ValidatorNames.PastYear }, - ], - }, - { - name: 'techRecord_noOfAxles', - label: 'Number of axles', - width: FormNodeWidth.XXS, - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.NUMBER, - value: 2, - validators: [{ name: ValidatorNames.Max, args: 99 }], - customTags: [{ colour: TagType.RED, label: TagTypeLabels.REQUIRED }], - }, - { - name: 'techRecord_vehicleSubclass', - label: 'Vehicle Subclass', - width: FormNodeWidth.XXS, - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.CHECKBOXGROUP, - options: getOptionsFromEnum(VehicleSubclass), - customTags: [{ colour: TagType.RED, label: TagTypeLabels.REQUIRED }], - }, - { - name: 'techRecord_vehicleConfiguration', - label: 'Vehicle configuration', - value: VehicleConfiguration.OTHER, - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.SELECT, - options: getOptionsFromEnum(VehicleConfiguration), - validators: [{ name: ValidatorNames.Required }], - customTags: [{ colour: TagType.RED, label: TagTypeLabels.REQUIRED }], - }, - { - name: 'techRecord_euVehicleCategory', - label: 'EU vehicle category', - value: null, - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.SELECT, - width: FormNodeWidth.S, - options: getOptionsFromEnum(EUVehicleCategory), - validators: [], - }, - ], + name: 'techRecordSummary', + type: FormNodeTypes.GROUP, + label: 'Vehicle Summary', + children: [ + { + name: 'techRecord_vehicleType', + label: 'Vehicle type', + value: '', + width: FormNodeWidth.XS, + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.VEHICLETYPE, + disabled: true, + validators: [], + customTags: [{ colour: TagType.RED, label: TagTypeLabels.REQUIRED }], + }, + { + name: 'techRecord_statusCode', + label: 'Record status', + value: '', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + customTags: [{ colour: TagType.RED, label: TagTypeLabels.REQUIRED }], + }, + { + name: 'techRecord_regnDate', + label: 'Date of first registration', + value: null, + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.DATE, + editType: FormNodeEditTypes.DATE, + validators: [], + isoDate: false, + }, + { + name: 'techRecord_manufactureYear', + label: 'Year of manufacture', + value: null, + width: FormNodeWidth.XS, + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.NUMBER, + validators: [ + { name: ValidatorNames.Max, args: 9999 }, + { name: ValidatorNames.Min, args: 1000 }, + { name: ValidatorNames.PastYear }, + ], + }, + { + name: 'techRecord_noOfAxles', + label: 'Number of axles', + width: FormNodeWidth.XXS, + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.NUMBER, + value: 2, + validators: [{ name: ValidatorNames.Max, args: 99 }], + customTags: [{ colour: TagType.RED, label: TagTypeLabels.REQUIRED }], + }, + { + name: 'techRecord_vehicleSubclass', + label: 'Vehicle Subclass', + width: FormNodeWidth.XXS, + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.CHECKBOXGROUP, + options: getOptionsFromEnum(VehicleSubclass), + customTags: [{ colour: TagType.RED, label: TagTypeLabels.REQUIRED }], + }, + { + name: 'techRecord_vehicleConfiguration', + label: 'Vehicle configuration', + value: VehicleConfiguration.OTHER, + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.SELECT, + options: getOptionsFromEnum(VehicleConfiguration), + validators: [{ name: ValidatorNames.Required }], + customTags: [{ colour: TagType.RED, label: TagTypeLabels.REQUIRED }], + }, + { + name: 'techRecord_euVehicleCategory', + label: 'EU vehicle category', + value: null, + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.SELECT, + width: FormNodeWidth.S, + options: getOptionsFromEnum(EUVehicleCategory), + validators: [], + }, + ], }; diff --git a/src/app/forms/templates/motorcycle/motorcycle-tech-record.template.ts b/src/app/forms/templates/motorcycle/motorcycle-tech-record.template.ts index be8854368f..d17d4a31f7 100644 --- a/src/app/forms/templates/motorcycle/motorcycle-tech-record.template.ts +++ b/src/app/forms/templates/motorcycle/motorcycle-tech-record.template.ts @@ -2,122 +2,133 @@ import { EUVehicleCategory } from '@dvsa/cvs-type-definitions/types/v3/tech-reco import { VehicleConfiguration } from '@dvsa/cvs-type-definitions/types/v3/tech-record/enums/vehicleConfigurationLightVehicle.enum.js'; import { ValidatorNames } from '@forms/models/validators.enum'; import { - FormNode, FormNodeEditTypes, FormNodeTypes, FormNodeViewTypes, FormNodeWidth, TagTypeLabels, + FormNode, + FormNodeEditTypes, + FormNodeTypes, + FormNodeViewTypes, + FormNodeWidth, + TagTypeLabels, } from '@forms/services/dynamic-form.types'; import { getOptionsFromEnum } from '@forms/utils/enum-map'; import { TagType } from '@shared/components/tag/tag.component'; export const MotorcycleTechRecord: FormNode = { - name: 'techRecordSummary', - type: FormNodeTypes.GROUP, - label: 'Vehicle Summary', - children: [ - { - name: 'techRecord_vehicleType', - label: 'Vehicle type', - value: '', - width: FormNodeWidth.M, - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.VEHICLETYPE, - disabled: true, - validators: [], - customTags: [{ colour: TagType.RED, label: TagTypeLabels.REQUIRED }], - }, - { - name: 'techRecord_statusCode', - label: 'Record status', - value: '', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - customTags: [{ colour: TagType.RED, label: TagTypeLabels.REQUIRED }], - }, - { - name: 'techRecord_regnDate', - label: 'Date of first registration', - value: '', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.DATE, - editType: FormNodeEditTypes.DATE, - validators: [], - isoDate: false, - }, - { - name: 'techRecord_manufactureYear', - label: 'Year of manufacture', - value: null, - width: FormNodeWidth.XS, - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.NUMBER, - validators: [ - { name: ValidatorNames.Max, args: 9999 }, - { name: ValidatorNames.Min, args: 1000 }, - { name: ValidatorNames.PastYear }, - ], - }, - { - name: 'techRecord_noOfAxles', - label: 'Number of axles', - width: FormNodeWidth.XXS, - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.NUMBER, - value: 2, - validators: [{ name: ValidatorNames.Max, args: 99 }], - }, - { - name: 'techRecord_vehicleClass_description', - label: 'Vehicle class', - value: '', - customId: 'vehicleClassDescription', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.STRING, - editType: FormNodeEditTypes.SELECT, - options: [ - { label: 'motorbikes over 200cc or with a sidecar', value: 'motorbikes over 200cc or with a sidecar' }, - { label: 'not applicable', value: 'not applicable' }, - { label: 'small psv (ie: less than or equal to 22 passengers)', value: 'small psv (ie: less than or equal to 22 seats)' }, - { label: 'motorbikes up to 200cc', value: 'motorbikes up to 200cc' }, - { label: 'trailer', value: 'trailer' }, - { label: 'large psv(ie: greater than or equal to 23 passengers)', value: 'large psv(ie: greater than 23 seats)' }, - { label: '3 wheelers', value: '3 wheelers' }, - { label: 'heavy goods vehicle', value: 'heavy goods vehicle' }, - { label: 'MOT class 4', value: 'MOT class 4' }, - { label: 'MOT class 7', value: 'MOT class 7' }, - { label: 'MOT class 5', value: 'MOT class 5' }, - ], - class: '.govuk-input--width-10', - validators: [{ name: ValidatorNames.Required }], - customTags: [{ colour: TagType.RED, label: TagTypeLabels.REQUIRED }], - }, - { - name: 'techRecord_vehicleConfiguration', - label: 'Vehicle configuration', - value: VehicleConfiguration.OTHER, - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.SELECT, - options: getOptionsFromEnum(VehicleConfiguration), - validators: [{ name: ValidatorNames.Required }], - customTags: [{ colour: TagType.RED, label: TagTypeLabels.REQUIRED }], - }, - { - name: 'techRecord_euVehicleCategory', - label: 'EU vehicle category', - value: null, - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.SELECT, - width: FormNodeWidth.S, - options: getOptionsFromEnum(EUVehicleCategory), - validators: [], - }, - { - name: 'techRecord_numberOfWheelsDriven', - label: 'Number of wheels', - value: null, - width: FormNodeWidth.XXS, - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.NUMBER, - validators: [{ name: ValidatorNames.Max, args: 9999 }], - customTags: [{ colour: TagType.RED, label: TagTypeLabels.REQUIRED }], - }, - ], + name: 'techRecordSummary', + type: FormNodeTypes.GROUP, + label: 'Vehicle Summary', + children: [ + { + name: 'techRecord_vehicleType', + label: 'Vehicle type', + value: '', + width: FormNodeWidth.M, + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.VEHICLETYPE, + disabled: true, + validators: [], + customTags: [{ colour: TagType.RED, label: TagTypeLabels.REQUIRED }], + }, + { + name: 'techRecord_statusCode', + label: 'Record status', + value: '', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + customTags: [{ colour: TagType.RED, label: TagTypeLabels.REQUIRED }], + }, + { + name: 'techRecord_regnDate', + label: 'Date of first registration', + value: '', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.DATE, + editType: FormNodeEditTypes.DATE, + validators: [], + isoDate: false, + }, + { + name: 'techRecord_manufactureYear', + label: 'Year of manufacture', + value: null, + width: FormNodeWidth.XS, + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.NUMBER, + validators: [ + { name: ValidatorNames.Max, args: 9999 }, + { name: ValidatorNames.Min, args: 1000 }, + { name: ValidatorNames.PastYear }, + ], + }, + { + name: 'techRecord_noOfAxles', + label: 'Number of axles', + width: FormNodeWidth.XXS, + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.NUMBER, + value: 2, + validators: [{ name: ValidatorNames.Max, args: 99 }], + }, + { + name: 'techRecord_vehicleClass_description', + label: 'Vehicle class', + value: '', + customId: 'vehicleClassDescription', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.STRING, + editType: FormNodeEditTypes.SELECT, + options: [ + { label: 'motorbikes over 200cc or with a sidecar', value: 'motorbikes over 200cc or with a sidecar' }, + { label: 'not applicable', value: 'not applicable' }, + { + label: 'small psv (ie: less than or equal to 22 passengers)', + value: 'small psv (ie: less than or equal to 22 seats)', + }, + { label: 'motorbikes up to 200cc', value: 'motorbikes up to 200cc' }, + { label: 'trailer', value: 'trailer' }, + { + label: 'large psv(ie: greater than or equal to 23 passengers)', + value: 'large psv(ie: greater than 23 seats)', + }, + { label: '3 wheelers', value: '3 wheelers' }, + { label: 'heavy goods vehicle', value: 'heavy goods vehicle' }, + { label: 'MOT class 4', value: 'MOT class 4' }, + { label: 'MOT class 7', value: 'MOT class 7' }, + { label: 'MOT class 5', value: 'MOT class 5' }, + ], + class: '.govuk-input--width-10', + validators: [{ name: ValidatorNames.Required }], + customTags: [{ colour: TagType.RED, label: TagTypeLabels.REQUIRED }], + }, + { + name: 'techRecord_vehicleConfiguration', + label: 'Vehicle configuration', + value: VehicleConfiguration.OTHER, + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.SELECT, + options: getOptionsFromEnum(VehicleConfiguration), + validators: [{ name: ValidatorNames.Required }], + customTags: [{ colour: TagType.RED, label: TagTypeLabels.REQUIRED }], + }, + { + name: 'techRecord_euVehicleCategory', + label: 'EU vehicle category', + value: null, + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.SELECT, + width: FormNodeWidth.S, + options: getOptionsFromEnum(EUVehicleCategory), + validators: [], + }, + { + name: 'techRecord_numberOfWheelsDriven', + label: 'Number of wheels', + value: null, + width: FormNodeWidth.XXS, + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.NUMBER, + validators: [{ name: ValidatorNames.Max, args: 9999 }], + customTags: [{ colour: TagType.RED, label: TagTypeLabels.REQUIRED }], + }, + ], }; diff --git a/src/app/forms/templates/psv/psv-approval-type.template.ts b/src/app/forms/templates/psv/psv-approval-type.template.ts index 74bc2fbe9f..53e053b6f4 100644 --- a/src/app/forms/templates/psv/psv-approval-type.template.ts +++ b/src/app/forms/templates/psv/psv-approval-type.template.ts @@ -2,99 +2,103 @@ import { ApprovalType } from '@dvsa/cvs-type-definitions/types/v3/tech-record/en import { ValidatorNames } from '@forms/models/validators.enum'; import { getOptionsFromEnum } from '@forms/utils/enum-map'; import { - FormNode, FormNodeEditTypes, FormNodeTypes, FormNodeViewTypes, FormNodeWidth, + FormNode, + FormNodeEditTypes, + FormNodeTypes, + FormNodeViewTypes, + FormNodeWidth, } from '../../services/dynamic-form.types'; export const PsvTypeApprovalTemplate: FormNode = { - name: 'approvalSection', - label: 'Type approval', - type: FormNodeTypes.GROUP, - children: [ - { - name: 'techRecord_approvalType', - label: 'Approval type', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.SELECT, - options: getOptionsFromEnum(ApprovalType), - validators: [{ name: ValidatorNames.IsMemberOfEnum, args: { enum: ApprovalType, options: { allowFalsy: true } } }], - }, - { - name: 'techRecord_approvalTypeNumber', - label: 'Approval type number', - type: FormNodeTypes.CONTROL, - width: FormNodeWidth.XL, - validators: [ - { - name: ValidatorNames.RequiredIfEquals, - args: { - sibling: 'techRecord_approvalType', - value: [ - 'NTA', - 'ECTA', - 'ECSSTA', - 'IVA', - 'NSSTA', - 'GB WVTA', - 'UKNI WVTA', - 'EU WVTA Pre 23', - 'EU WVTA 23 on', - 'QNIG', - 'Prov.GB WVTA', - 'Small series NKSXX', - 'Small series NKS', - 'IVA - VCA', - 'IVA - DVSA/NI', - ], - }, - }, - ], - }, - { - name: 'techRecord_ntaNumber', - label: 'National type number', - type: FormNodeTypes.CONTROL, - width: FormNodeWidth.XL, - validators: [{ name: ValidatorNames.MaxLength, args: 40 }], - }, - { - name: 'techRecord_coifSerialNumber', - label: 'COIF Serial number', - type: FormNodeTypes.CONTROL, - width: FormNodeWidth.M, - validators: [{ name: ValidatorNames.MaxLength, args: 8 }], - }, - { - name: 'techRecord_coifCertifierName', - label: 'COIF Certifier name', - type: FormNodeTypes.CONTROL, - width: FormNodeWidth.XL, - validators: [{ name: ValidatorNames.MaxLength, args: 20 }], - }, - { - name: 'techRecord_coifDate', - label: 'COIF Certifier date', - value: null, - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.DATE, - editType: FormNodeEditTypes.DATE, - validators: [ - { name: ValidatorNames.PastDate }, - ], - isoDate: false, - }, - { - name: 'techRecord_variantNumber', - label: 'Variant number', - type: FormNodeTypes.CONTROL, - width: FormNodeWidth.XL, - validators: [{ name: ValidatorNames.MaxLength, args: 25 }], - }, - { - name: 'techRecord_variantVersionNumber', - label: 'Variant version number', - type: FormNodeTypes.CONTROL, - width: FormNodeWidth.XL, - validators: [{ name: ValidatorNames.MaxLength, args: 35 }], - }, - ], + name: 'approvalSection', + label: 'Type approval', + type: FormNodeTypes.GROUP, + children: [ + { + name: 'techRecord_approvalType', + label: 'Approval type', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.SELECT, + options: getOptionsFromEnum(ApprovalType), + validators: [ + { name: ValidatorNames.IsMemberOfEnum, args: { enum: ApprovalType, options: { allowFalsy: true } } }, + ], + }, + { + name: 'techRecord_approvalTypeNumber', + label: 'Approval type number', + type: FormNodeTypes.CONTROL, + width: FormNodeWidth.XL, + validators: [ + { + name: ValidatorNames.RequiredIfEquals, + args: { + sibling: 'techRecord_approvalType', + value: [ + 'NTA', + 'ECTA', + 'ECSSTA', + 'IVA', + 'NSSTA', + 'GB WVTA', + 'UKNI WVTA', + 'EU WVTA Pre 23', + 'EU WVTA 23 on', + 'QNIG', + 'Prov.GB WVTA', + 'Small series NKSXX', + 'Small series NKS', + 'IVA - VCA', + 'IVA - DVSA/NI', + ], + }, + }, + ], + }, + { + name: 'techRecord_ntaNumber', + label: 'National type number', + type: FormNodeTypes.CONTROL, + width: FormNodeWidth.XL, + validators: [{ name: ValidatorNames.MaxLength, args: 40 }], + }, + { + name: 'techRecord_coifSerialNumber', + label: 'COIF Serial number', + type: FormNodeTypes.CONTROL, + width: FormNodeWidth.M, + validators: [{ name: ValidatorNames.MaxLength, args: 8 }], + }, + { + name: 'techRecord_coifCertifierName', + label: 'COIF Certifier name', + type: FormNodeTypes.CONTROL, + width: FormNodeWidth.XL, + validators: [{ name: ValidatorNames.MaxLength, args: 20 }], + }, + { + name: 'techRecord_coifDate', + label: 'COIF Certifier date', + value: null, + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.DATE, + editType: FormNodeEditTypes.DATE, + validators: [{ name: ValidatorNames.PastDate }], + isoDate: false, + }, + { + name: 'techRecord_variantNumber', + label: 'Variant number', + type: FormNodeTypes.CONTROL, + width: FormNodeWidth.XL, + validators: [{ name: ValidatorNames.MaxLength, args: 25 }], + }, + { + name: 'techRecord_variantVersionNumber', + label: 'Variant version number', + type: FormNodeTypes.CONTROL, + width: FormNodeWidth.XL, + validators: [{ name: ValidatorNames.MaxLength, args: 35 }], + }, + ], }; diff --git a/src/app/forms/templates/psv/psv-body.template.ts b/src/app/forms/templates/psv/psv-body.template.ts index 0e233c7a38..3e9451eac8 100644 --- a/src/app/forms/templates/psv/psv-body.template.ts +++ b/src/app/forms/templates/psv/psv-body.template.ts @@ -1,98 +1,107 @@ import { ValidatorNames } from '@forms/models/validators.enum'; import { TagType } from '@shared/components/tag/tag.component'; import { - FormNode, FormNodeEditTypes, FormNodeTypes, FormNodeWidth, TagTypeLabels, + FormNode, + FormNodeEditTypes, + FormNodeTypes, + FormNodeWidth, + TagTypeLabels, } from '../../services/dynamic-form.types'; export const PsvBodyTemplate: FormNode = { - name: 'bodySection', - label: 'Body', - type: FormNodeTypes.GROUP, - children: [ - { - name: 'techRecord_brakes_dtpNumber', - label: 'DTP number', - value: null, - width: FormNodeWidth.S, - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.AUTOCOMPLETE, - validators: [{ name: ValidatorNames.Required }], - customTags: [{ colour: TagType.RED, label: TagTypeLabels.REQUIRED }], - }, - { - name: 'techRecord_modelLiteral', - label: 'Model literal', - value: null, - type: FormNodeTypes.CONTROL, - validators: [{ name: ValidatorNames.MaxLength, args: 30 }], - }, - { - name: 'techRecord_chassisMake', - label: 'Chassis make', - value: '', - type: FormNodeTypes.CONTROL, - validators: [{ name: ValidatorNames.MaxLength, args: 30 }], - disabled: true, - }, - { - name: 'techRecord_chassisModel', - label: 'Chassis model', - value: '', - type: FormNodeTypes.CONTROL, - validators: [{ name: ValidatorNames.MaxLength, args: 20 }], - disabled: true, - }, - { - name: 'techRecord_bodyMake', - label: 'Body make', - value: '', - type: FormNodeTypes.CONTROL, - validators: [{ name: ValidatorNames.MaxLength, args: 20 }], - disabled: true, - }, - { - name: 'techRecord_bodyModel', - label: 'Body model', - value: '', - type: FormNodeTypes.CONTROL, - validators: [{ name: ValidatorNames.MaxLength, args: 20 }], - }, - { - name: 'techRecord_bodyType_description', - label: 'Body type', - value: '', - customId: 'bodyType', - type: FormNodeTypes.CONTROL, - disabled: true, - validators: [{ name: ValidatorNames.Required }], - }, - { - name: 'techRecord_functionCode', - label: 'Function code', - value: null, - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.SELECT, - options: [ - { value: 'r', label: 'R' }, - { value: 'a', label: 'A' }, - ], - validators: [{ name: ValidatorNames.MaxLength, args: 1 }], - }, - { - name: 'techRecord_conversionRefNo', - label: 'Conversion ref no', - value: null, - type: FormNodeTypes.CONTROL, - validators: [{ name: ValidatorNames.CustomPattern, args: ['^[A-Z0-9 ]{0,10}$', 'max length 10 uppercase letters or numbers'] }], - }, - { - name: 'techRecord_modelLiteral', - label: 'Model literal', - value: null, - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - validators: [{ name: ValidatorNames.MaxLength, args: 30 }], - width: FormNodeWidth.L, - }, - ], + name: 'bodySection', + label: 'Body', + type: FormNodeTypes.GROUP, + children: [ + { + name: 'techRecord_brakes_dtpNumber', + label: 'DTP number', + value: null, + width: FormNodeWidth.S, + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.AUTOCOMPLETE, + validators: [{ name: ValidatorNames.Required }], + customTags: [{ colour: TagType.RED, label: TagTypeLabels.REQUIRED }], + }, + { + name: 'techRecord_modelLiteral', + label: 'Model literal', + value: null, + type: FormNodeTypes.CONTROL, + validators: [{ name: ValidatorNames.MaxLength, args: 30 }], + }, + { + name: 'techRecord_chassisMake', + label: 'Chassis make', + value: '', + type: FormNodeTypes.CONTROL, + validators: [{ name: ValidatorNames.MaxLength, args: 30 }], + disabled: true, + }, + { + name: 'techRecord_chassisModel', + label: 'Chassis model', + value: '', + type: FormNodeTypes.CONTROL, + validators: [{ name: ValidatorNames.MaxLength, args: 20 }], + disabled: true, + }, + { + name: 'techRecord_bodyMake', + label: 'Body make', + value: '', + type: FormNodeTypes.CONTROL, + validators: [{ name: ValidatorNames.MaxLength, args: 20 }], + disabled: true, + }, + { + name: 'techRecord_bodyModel', + label: 'Body model', + value: '', + type: FormNodeTypes.CONTROL, + validators: [{ name: ValidatorNames.MaxLength, args: 20 }], + }, + { + name: 'techRecord_bodyType_description', + label: 'Body type', + value: '', + customId: 'bodyType', + type: FormNodeTypes.CONTROL, + disabled: true, + validators: [{ name: ValidatorNames.Required }], + }, + { + name: 'techRecord_functionCode', + label: 'Function code', + value: null, + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.SELECT, + options: [ + { value: 'r', label: 'R' }, + { value: 'a', label: 'A' }, + ], + validators: [{ name: ValidatorNames.MaxLength, args: 1 }], + }, + { + name: 'techRecord_conversionRefNo', + label: 'Conversion ref no', + value: null, + type: FormNodeTypes.CONTROL, + validators: [ + { + name: ValidatorNames.CustomPattern, + args: ['^[A-Z0-9 ]{0,10}$', 'max length 10 uppercase letters or numbers'], + }, + ], + }, + { + name: 'techRecord_modelLiteral', + label: 'Model literal', + value: null, + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + validators: [{ name: ValidatorNames.MaxLength, args: 30 }], + width: FormNodeWidth.L, + }, + ], }; diff --git a/src/app/forms/templates/psv/psv-brakes.template.ts b/src/app/forms/templates/psv/psv-brakes.template.ts index a006bc67e7..ce7ecfdf1e 100644 --- a/src/app/forms/templates/psv/psv-brakes.template.ts +++ b/src/app/forms/templates/psv/psv-brakes.template.ts @@ -2,82 +2,82 @@ import { ValidatorNames } from '@forms/models/validators.enum'; import { FormNode, FormNodeTypes } from '../../services/dynamic-form.types'; export const PsvBrakesTemplate: FormNode = { - name: 'psvBrakesSection', - label: 'Brakes', - type: FormNodeTypes.GROUP, - children: [ - { - name: 'techRecord_brakes_brakeCodeOriginal', - label: 'Brake code', - value: null, - type: FormNodeTypes.CONTROL, - validators: [{ name: ValidatorNames.MaxLength, args: 6 }], - }, - { - name: 'techRecord_brakes_brakeCode', - label: 'Brake code', - value: null, - type: FormNodeTypes.CONTROL, - validators: [{ name: ValidatorNames.MaxLength, args: 6 }], - }, - { - name: 'techRecord_brakes_dataTrBrakeOne', - label: 'Service *', - value: null, - type: FormNodeTypes.CONTROL, - disabled: true, - }, - { - name: 'techRecord_brakes_dataTrBrakeTwo', - label: 'Secondary *', - value: null, - type: FormNodeTypes.CONTROL, - disabled: true, - }, - { - name: 'techRecord_brakes_dataTrBrakeThree', - label: 'Parking *', - value: null, - type: FormNodeTypes.CONTROL, - disabled: true, - }, - { - name: 'techRecord_brakes_retarderBrakeOne', - label: 'Retarder 1', - value: null, - type: FormNodeTypes.CONTROL, - }, - { - name: 'techRecord_brakes_retarderBrakeTwo', - label: 'Retarder 2', - value: null, - type: FormNodeTypes.CONTROL, - }, - { - name: 'techRecord_axles', - value: '', - type: FormNodeTypes.ARRAY, - children: [ - { - name: '0', - label: 'Axle', - value: '', - type: FormNodeTypes.GROUP, - children: [ - { - name: 'axleNumber', - label: 'Axle Number', - type: FormNodeTypes.CONTROL, - }, - { - name: 'parkingBrakeMrk', - label: 'Parking Brake', - value: false, - type: FormNodeTypes.CONTROL, - }, - ], - }, - ], - }, - ], + name: 'psvBrakesSection', + label: 'Brakes', + type: FormNodeTypes.GROUP, + children: [ + { + name: 'techRecord_brakes_brakeCodeOriginal', + label: 'Brake code', + value: null, + type: FormNodeTypes.CONTROL, + validators: [{ name: ValidatorNames.MaxLength, args: 6 }], + }, + { + name: 'techRecord_brakes_brakeCode', + label: 'Brake code', + value: null, + type: FormNodeTypes.CONTROL, + validators: [{ name: ValidatorNames.MaxLength, args: 6 }], + }, + { + name: 'techRecord_brakes_dataTrBrakeOne', + label: 'Service *', + value: null, + type: FormNodeTypes.CONTROL, + disabled: true, + }, + { + name: 'techRecord_brakes_dataTrBrakeTwo', + label: 'Secondary *', + value: null, + type: FormNodeTypes.CONTROL, + disabled: true, + }, + { + name: 'techRecord_brakes_dataTrBrakeThree', + label: 'Parking *', + value: null, + type: FormNodeTypes.CONTROL, + disabled: true, + }, + { + name: 'techRecord_brakes_retarderBrakeOne', + label: 'Retarder 1', + value: null, + type: FormNodeTypes.CONTROL, + }, + { + name: 'techRecord_brakes_retarderBrakeTwo', + label: 'Retarder 2', + value: null, + type: FormNodeTypes.CONTROL, + }, + { + name: 'techRecord_axles', + value: '', + type: FormNodeTypes.ARRAY, + children: [ + { + name: '0', + label: 'Axle', + value: '', + type: FormNodeTypes.GROUP, + children: [ + { + name: 'axleNumber', + label: 'Axle Number', + type: FormNodeTypes.CONTROL, + }, + { + name: 'parkingBrakeMrk', + label: 'Parking Brake', + value: false, + type: FormNodeTypes.CONTROL, + }, + ], + }, + ], + }, + ], }; diff --git a/src/app/forms/templates/psv/psv-dda.template.ts b/src/app/forms/templates/psv/psv-dda.template.ts index 147fe404da..943ddfa05c 100644 --- a/src/app/forms/templates/psv/psv-dda.template.ts +++ b/src/app/forms/templates/psv/psv-dda.template.ts @@ -2,105 +2,105 @@ import { ValidatorNames } from '@forms/models/validators.enum'; import { FormNode, FormNodeEditTypes, FormNodeTypes } from '../../services/dynamic-form.types'; export const PsvDdaTemplate: FormNode = { - name: 'dda', - type: FormNodeTypes.GROUP, - label: 'Disability Discrimination Act', - children: [ - { - name: 'techRecord_dda_certificateIssued', - label: 'DDA certificate issued', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.RADIO, - options: [ - { value: true, label: 'Yes' }, - { value: false, label: 'No' }, - { value: null, label: 'I do not know' }, - ], - }, - { - name: 'techRecord_dda_wheelchairCapacity', - label: 'Wheelchair capacity', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.NUMBER, - validators: [{ name: ValidatorNames.Max, args: 99 }], - }, - { - name: 'techRecord_dda_wheelchairFittings', - label: 'Wheelchair fittings', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.TEXTAREA, - validators: [{ name: ValidatorNames.MaxLength, args: 250 }], - }, - { - name: 'techRecord_dda_wheelchairLiftPresent', - label: 'Wheelchair lift present', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.RADIO, - options: [ - { value: true, label: 'Yes' }, - { value: false, label: 'No' }, - { value: null, label: 'I do not know' }, - ], - }, - { - name: 'techRecord_dda_wheelchairLiftInformation', - label: 'Wheelchair lift information', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.TEXTAREA, - validators: [{ name: ValidatorNames.MaxLength, args: 250 }], - }, - { - name: 'techRecord_dda_wheelchairRampPresent', - label: 'Wheelchair ramp present', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.RADIO, - options: [ - { value: true, label: 'Yes' }, - { value: false, label: 'No' }, - { value: null, label: 'I do not know' }, - ], - }, - { - name: 'techRecord_dda_wheelchairRampInformation', - label: 'Wheelchair ramp information', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.TEXTAREA, - validators: [{ name: ValidatorNames.MaxLength, args: 250 }], - }, - { - name: 'techRecord_dda_minEmergencyExits', - label: 'Minimum emergency exits needed', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.NUMBER, - validators: [{ name: ValidatorNames.Max, args: 99 }], - }, - { - name: 'techRecord_dda_outswing', - label: 'Outswing', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.TEXTAREA, - validators: [{ name: ValidatorNames.MaxLength, args: 250 }], - }, - { - name: 'techRecord_dda_ddaSchedules', - label: 'DDA schedules', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.TEXTAREA, - validators: [{ name: ValidatorNames.MaxLength, args: 250 }], - }, - { - name: 'techRecord_dda_seatbeltsFitted', - label: 'Seatbelts fitted', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.NUMBER, - validators: [{ name: ValidatorNames.Max, args: 999 }], - }, - { - name: 'techRecord_dda_ddaNotes', - label: 'DDA notes', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.TEXTAREA, - validators: [{ name: ValidatorNames.MaxLength, args: 1024 }], - }, - ], + name: 'dda', + type: FormNodeTypes.GROUP, + label: 'Disability Discrimination Act', + children: [ + { + name: 'techRecord_dda_certificateIssued', + label: 'DDA certificate issued', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.RADIO, + options: [ + { value: true, label: 'Yes' }, + { value: false, label: 'No' }, + { value: null, label: 'I do not know' }, + ], + }, + { + name: 'techRecord_dda_wheelchairCapacity', + label: 'Wheelchair capacity', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.NUMBER, + validators: [{ name: ValidatorNames.Max, args: 99 }], + }, + { + name: 'techRecord_dda_wheelchairFittings', + label: 'Wheelchair fittings', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.TEXTAREA, + validators: [{ name: ValidatorNames.MaxLength, args: 250 }], + }, + { + name: 'techRecord_dda_wheelchairLiftPresent', + label: 'Wheelchair lift present', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.RADIO, + options: [ + { value: true, label: 'Yes' }, + { value: false, label: 'No' }, + { value: null, label: 'I do not know' }, + ], + }, + { + name: 'techRecord_dda_wheelchairLiftInformation', + label: 'Wheelchair lift information', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.TEXTAREA, + validators: [{ name: ValidatorNames.MaxLength, args: 250 }], + }, + { + name: 'techRecord_dda_wheelchairRampPresent', + label: 'Wheelchair ramp present', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.RADIO, + options: [ + { value: true, label: 'Yes' }, + { value: false, label: 'No' }, + { value: null, label: 'I do not know' }, + ], + }, + { + name: 'techRecord_dda_wheelchairRampInformation', + label: 'Wheelchair ramp information', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.TEXTAREA, + validators: [{ name: ValidatorNames.MaxLength, args: 250 }], + }, + { + name: 'techRecord_dda_minEmergencyExits', + label: 'Minimum emergency exits needed', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.NUMBER, + validators: [{ name: ValidatorNames.Max, args: 99 }], + }, + { + name: 'techRecord_dda_outswing', + label: 'Outswing', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.TEXTAREA, + validators: [{ name: ValidatorNames.MaxLength, args: 250 }], + }, + { + name: 'techRecord_dda_ddaSchedules', + label: 'DDA schedules', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.TEXTAREA, + validators: [{ name: ValidatorNames.MaxLength, args: 250 }], + }, + { + name: 'techRecord_dda_seatbeltsFitted', + label: 'Seatbelts fitted', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.NUMBER, + validators: [{ name: ValidatorNames.Max, args: 999 }], + }, + { + name: 'techRecord_dda_ddaNotes', + label: 'DDA notes', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.TEXTAREA, + validators: [{ name: ValidatorNames.MaxLength, args: 1024 }], + }, + ], }; diff --git a/src/app/forms/templates/psv/psv-dimensions.template.ts b/src/app/forms/templates/psv/psv-dimensions.template.ts index 069cfe3a99..2fad617a7c 100644 --- a/src/app/forms/templates/psv/psv-dimensions.template.ts +++ b/src/app/forms/templates/psv/psv-dimensions.template.ts @@ -2,37 +2,37 @@ import { ValidatorNames } from '@forms/models/validators.enum'; import { FormNode, FormNodeTypes } from '../../services/dynamic-form.types'; export const PsvDimensionsTemplate: FormNode = { - name: 'dimensionsSection', - label: 'Dimensions', - type: FormNodeTypes.SECTION, - children: [ - { - name: 'techRecord_dimensions_height', - label: 'Height (mm)', - value: null, - type: FormNodeTypes.CONTROL, - validators: [{ name: ValidatorNames.Max, args: 99999 }], - }, - { - name: 'techRecord_dimensions_length', - label: 'Length (mm)', - value: null, - type: FormNodeTypes.CONTROL, - validators: [{ name: ValidatorNames.Max, args: 99999 }], - }, - { - name: 'techRecord_dimensions_width', - label: 'Width (mm)', - value: null, - type: FormNodeTypes.CONTROL, - validators: [{ name: ValidatorNames.Max, args: 99999 }], - }, - { - name: 'techRecord_frontAxleToRearAxle', - label: 'Front axle to rear axle (mm)', - value: null, - type: FormNodeTypes.CONTROL, - validators: [{ name: ValidatorNames.Max, args: 99999 }], - }, - ], + name: 'dimensionsSection', + label: 'Dimensions', + type: FormNodeTypes.SECTION, + children: [ + { + name: 'techRecord_dimensions_height', + label: 'Height (mm)', + value: null, + type: FormNodeTypes.CONTROL, + validators: [{ name: ValidatorNames.Max, args: 99999 }], + }, + { + name: 'techRecord_dimensions_length', + label: 'Length (mm)', + value: null, + type: FormNodeTypes.CONTROL, + validators: [{ name: ValidatorNames.Max, args: 99999 }], + }, + { + name: 'techRecord_dimensions_width', + label: 'Width (mm)', + value: null, + type: FormNodeTypes.CONTROL, + validators: [{ name: ValidatorNames.Max, args: 99999 }], + }, + { + name: 'techRecord_frontAxleToRearAxle', + label: 'Front axle to rear axle (mm)', + value: null, + type: FormNodeTypes.CONTROL, + validators: [{ name: ValidatorNames.Max, args: 99999 }], + }, + ], }; diff --git a/src/app/forms/templates/psv/psv-notes.template.ts b/src/app/forms/templates/psv/psv-notes.template.ts index f1f1235d6b..dfce3af85d 100644 --- a/src/app/forms/templates/psv/psv-notes.template.ts +++ b/src/app/forms/templates/psv/psv-notes.template.ts @@ -1,28 +1,26 @@ import { ValidatorNames } from '@forms/models/validators.enum'; -import { - FormNode, FormNodeEditTypes, FormNodeTypes, FormNodeViewTypes, -} from '../../services/dynamic-form.types'; +import { FormNode, FormNodeEditTypes, FormNodeTypes, FormNodeViewTypes } from '../../services/dynamic-form.types'; export const PsvNotes: FormNode = { - name: 'notesSection', - label: 'Notes', - type: FormNodeTypes.GROUP, - children: [ - { - name: 'techRecord_remarks', - label: 'Notes', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.STRING, - editType: FormNodeEditTypes.TEXTAREA, - validators: [{ name: ValidatorNames.MaxLength, args: 1024 }], - }, - { - name: 'techRecord_dispensations', - type: FormNodeTypes.CONTROL, - label: 'Dispensations', - viewType: FormNodeViewTypes.STRING, - editType: FormNodeEditTypes.TEXTAREA, - validators: [{ name: ValidatorNames.MaxLength, args: 160 }], - }, - ], + name: 'notesSection', + label: 'Notes', + type: FormNodeTypes.GROUP, + children: [ + { + name: 'techRecord_remarks', + label: 'Notes', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.STRING, + editType: FormNodeEditTypes.TEXTAREA, + validators: [{ name: ValidatorNames.MaxLength, args: 1024 }], + }, + { + name: 'techRecord_dispensations', + type: FormNodeTypes.CONTROL, + label: 'Dispensations', + viewType: FormNodeViewTypes.STRING, + editType: FormNodeEditTypes.TEXTAREA, + validators: [{ name: ValidatorNames.MaxLength, args: 160 }], + }, + ], }; diff --git a/src/app/forms/templates/psv/psv-tech-record.template.ts b/src/app/forms/templates/psv/psv-tech-record.template.ts index 9524d51634..4232786c73 100644 --- a/src/app/forms/templates/psv/psv-tech-record.template.ts +++ b/src/app/forms/templates/psv/psv-tech-record.template.ts @@ -7,283 +7,293 @@ import { VehicleSize } from '@models/vehicle-size.enum'; import { FuelTypes } from '@models/vehicle-tech-record.model'; import { TagType } from '@shared/components/tag/tag.component'; import { - FormNode, FormNodeEditTypes, FormNodeTypes, FormNodeViewTypes, FormNodeWidth, TagTypeLabels, + FormNode, + FormNodeEditTypes, + FormNodeTypes, + FormNodeViewTypes, + FormNodeWidth, + TagTypeLabels, } from '../../services/dynamic-form.types'; export const PsvTechRecord: FormNode = { - name: 'techRecordSummary', - type: FormNodeTypes.GROUP, - label: 'Vehicle Summary', - children: [ - { - name: 'techRecord_vehicleType', - label: 'Vehicle type', - value: '', - width: FormNodeWidth.XS, - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.VEHICLETYPE, - disabled: true, - customTags: [{ colour: TagType.RED, label: TagTypeLabels.REQUIRED }], - }, - { - name: 'techRecord_statusCode', - label: 'Record status', - value: '', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - customTags: [{ colour: TagType.RED, label: TagTypeLabels.REQUIRED }], - }, - { - name: 'techRecord_numberOfWheelsDriven', - value: null, - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - }, - { - name: 'techRecord_regnDate', - label: 'Date of first registration', - value: null, - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.DATE, - editType: FormNodeEditTypes.DATE, - validators: [], - isoDate: false, - }, - { - name: 'techRecord_manufactureYear', - label: 'Year of manufacture', - value: null, - width: FormNodeWidth.XS, - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.NUMBER, - validators: [ - { name: ValidatorNames.Max, args: 9999 }, - { name: ValidatorNames.Min, args: 1000 }, - { name: ValidatorNames.PastYear }, - ], - }, - { - name: 'techRecord_noOfAxles', - label: 'Number of axles', - value: null, - width: FormNodeWidth.XXS, - type: FormNodeTypes.CONTROL, - validators: [], - disabled: true, - customTags: [{ colour: TagType.RED, label: TagTypeLabels.REQUIRED }], - }, - { - name: 'techRecord_speedLimiterMrk', - label: 'Speed limiter exempt', - value: null, - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.RADIO, - options: [ - { value: true, label: 'Exempt' }, - { value: false, label: 'Not exempt' }, - ], - validators: [], - class: 'flex--half', - }, - { - name: 'techRecord_tachoExemptMrk', - label: 'Tacho exempt', - value: null, - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.RADIO, - options: [ - { value: true, label: 'Exempt' }, - { value: false, label: 'Not exempt' }, - ], - validators: [], - class: 'flex--half', - }, - { - name: 'techRecord_euroStandard', - label: 'Euro standard', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.RADIO, - options: [{ label: '0.10 g/kWh Euro III PM', value: '0.10 g/kWh Euro 3 PM' }, - ...getOptionsFromEnum(EmissionStandard), - ], - }, - { - name: 'techRecord_fuelPropulsionSystem', - label: 'Fuel / propulsion system', - value: null, - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.STRING, - editType: FormNodeEditTypes.SELECT, - options: getOptionsFromEnum(FuelTypes), - validators: [], - }, - { - name: 'techRecord_vehicleConfiguration', - label: 'Vehicle configuration', - value: null, - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.SELECT, - options: getOptionsFromEnum(VehicleConfiguration), - validators: [{ name: ValidatorNames.UpdateFunctionCode }], - customTags: [{ colour: TagType.RED, label: TagTypeLabels.REQUIRED }], - }, - { - name: 'techRecord_euVehicleCategory', - label: 'EU vehicle category', - value: null, - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.SELECT, - width: FormNodeWidth.S, - options: getOptionsFromEnum(EUVehicleCategory), - validators: [], - }, - { - name: 'techRecord_emissionsLimit', - label: 'Emission limit (m-1) (plate value)', - value: null, - width: FormNodeWidth.XXS, - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.NUMBER, - validators: [ - { name: ValidatorNames.Max, args: 99 }, - { - name: ValidatorNames.CustomPattern, - args: ['^\\d*(\\.\\d{0,5})?$', 'Max 5 decimal places'], - }, - ], - enableDecimals: true, - }, - { name: 'seatsTitle', label: 'Seats:', type: FormNodeTypes.TITLE }, - { - name: 'techRecord_seatsUpperDeck', - label: 'Upper deck', - value: null, - width: FormNodeWidth.M, - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.NUMBER, - validators: [ - { name: ValidatorNames.Max, args: 99 }, - { - name: ValidatorNames.HandlePsvPassengersChange, - args: { passengersOne: 'techRecord_seatsLowerDeck', passengersTwo: 'techRecord_standingCapacity' }, - }, - ], - class: 'flex--half', - customTags: [{ colour: TagType.RED, label: TagTypeLabels.REQUIRED }], - }, - { - name: 'techRecord_seatsLowerDeck', - label: 'Lower deck', - value: null, - width: FormNodeWidth.M, - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.NUMBER, - validators: [ - { name: ValidatorNames.Max, args: 999 }, - { - name: ValidatorNames.HandlePsvPassengersChange, - args: { passengersOne: 'techRecord_standingCapacity', passengersTwo: 'techRecord_seatsUpperDeck' }, - }, - ], - class: 'flex--half', - customTags: [{ colour: TagType.RED, label: TagTypeLabels.REQUIRED }], - }, - { - name: 'techRecord_standingCapacity', - label: 'Standing capacity', - value: null, - width: FormNodeWidth.XXS, - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.NUMBER, - validators: [ - { name: ValidatorNames.Max, args: 999 }, - { - name: ValidatorNames.HandlePsvPassengersChange, - args: { passengersOne: 'techRecord_seatsLowerDeck', passengersTwo: 'techRecord_seatsUpperDeck' }, - }, - ], - }, - { - name: 'techRecord_vehicleClass_description', - label: 'Vehicle class', - value: null, - hint: 'The Vehicle Class is calculated automatically based on the number of seats and standing capacity. Only change the Class if you need to', - customId: 'vehicleClassDescription', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.STRING, - editType: FormNodeEditTypes.SELECT, - options: [ - { label: 'small psv (ie: less than or equal to 22 passengers)', value: 'small psv (ie: less than or equal to 22 seats)' }, - { label: 'large psv(ie: greater than or equal to 23 passengers)', value: 'large psv(ie: greater than 23 seats)' }, - ], - class: '.govuk-input--width-10', - validators: [{ name: ValidatorNames.Required }], - customTags: [{ colour: TagType.RED, label: TagTypeLabels.REQUIRED }], - }, - { - name: 'techRecord_vehicleSize', - label: 'Vehicle size', - value: null, - hint: 'The Vehicle Size is calculated automatically based on the number of seats and standing capacity. Only change the Size if you need to', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.RADIO, - options: getOptionsFromEnum(VehicleSize), - customTags: [{ colour: TagType.RED, label: TagTypeLabels.REQUIRED }], - }, - { - name: 'techRecord_numberOfSeatbelts', - label: 'Number of seat belts', - value: null, - width: FormNodeWidth.XXS, - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.NUMERICSTRING, - validators: [{ name: ValidatorNames.Max, args: 99 }], - }, - { - name: 'techRecord_seatbeltInstallationApprovalDate', - label: 'Seatbelt installation approval date / type approved', - value: null, - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.DATE, - editType: FormNodeEditTypes.DATE, - isoDate: false, - validators: [ - { name: ValidatorNames.PastDate }, - ], - }, - { - name: 'techRecord_departmentalVehicleMarker', - label: 'Departmental vehicle marker', - value: null, - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.RADIO, - options: [ - { value: true, label: 'Yes' }, - { value: false, label: 'No' }, - ], - validators: [], - }, - { - name: 'techRecord_alterationMarker', - label: 'Alteration marker', - value: null, - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.RADIO, - options: [ - { value: true, label: 'Yes' }, - { value: false, label: 'No' }, - ], - validators: [], - }, - { - name: 'techRecord_functionCode', - label: 'Function code', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - ], + name: 'techRecordSummary', + type: FormNodeTypes.GROUP, + label: 'Vehicle Summary', + children: [ + { + name: 'techRecord_vehicleType', + label: 'Vehicle type', + value: '', + width: FormNodeWidth.XS, + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.VEHICLETYPE, + disabled: true, + customTags: [{ colour: TagType.RED, label: TagTypeLabels.REQUIRED }], + }, + { + name: 'techRecord_statusCode', + label: 'Record status', + value: '', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + customTags: [{ colour: TagType.RED, label: TagTypeLabels.REQUIRED }], + }, + { + name: 'techRecord_numberOfWheelsDriven', + value: null, + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + }, + { + name: 'techRecord_regnDate', + label: 'Date of first registration', + value: null, + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.DATE, + editType: FormNodeEditTypes.DATE, + validators: [], + isoDate: false, + }, + { + name: 'techRecord_manufactureYear', + label: 'Year of manufacture', + value: null, + width: FormNodeWidth.XS, + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.NUMBER, + validators: [ + { name: ValidatorNames.Max, args: 9999 }, + { name: ValidatorNames.Min, args: 1000 }, + { name: ValidatorNames.PastYear }, + ], + }, + { + name: 'techRecord_noOfAxles', + label: 'Number of axles', + value: null, + width: FormNodeWidth.XXS, + type: FormNodeTypes.CONTROL, + validators: [], + disabled: true, + customTags: [{ colour: TagType.RED, label: TagTypeLabels.REQUIRED }], + }, + { + name: 'techRecord_speedLimiterMrk', + label: 'Speed limiter exempt', + value: null, + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.RADIO, + options: [ + { value: true, label: 'Exempt' }, + { value: false, label: 'Not exempt' }, + ], + validators: [], + class: 'flex--half', + }, + { + name: 'techRecord_tachoExemptMrk', + label: 'Tacho exempt', + value: null, + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.RADIO, + options: [ + { value: true, label: 'Exempt' }, + { value: false, label: 'Not exempt' }, + ], + validators: [], + class: 'flex--half', + }, + { + name: 'techRecord_euroStandard', + label: 'Euro standard', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.RADIO, + options: [ + { label: '0.10 g/kWh Euro III PM', value: '0.10 g/kWh Euro 3 PM' }, + ...getOptionsFromEnum(EmissionStandard), + ], + }, + { + name: 'techRecord_fuelPropulsionSystem', + label: 'Fuel / propulsion system', + value: null, + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.STRING, + editType: FormNodeEditTypes.SELECT, + options: getOptionsFromEnum(FuelTypes), + validators: [], + }, + { + name: 'techRecord_vehicleConfiguration', + label: 'Vehicle configuration', + value: null, + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.SELECT, + options: getOptionsFromEnum(VehicleConfiguration), + validators: [{ name: ValidatorNames.UpdateFunctionCode }], + customTags: [{ colour: TagType.RED, label: TagTypeLabels.REQUIRED }], + }, + { + name: 'techRecord_euVehicleCategory', + label: 'EU vehicle category', + value: null, + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.SELECT, + width: FormNodeWidth.S, + options: getOptionsFromEnum(EUVehicleCategory), + validators: [], + }, + { + name: 'techRecord_emissionsLimit', + label: 'Emission limit (m-1) (plate value)', + value: null, + width: FormNodeWidth.XXS, + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.NUMBER, + validators: [ + { name: ValidatorNames.Max, args: 99 }, + { + name: ValidatorNames.CustomPattern, + args: ['^\\d*(\\.\\d{0,5})?$', 'Max 5 decimal places'], + }, + ], + enableDecimals: true, + }, + { name: 'seatsTitle', label: 'Seats:', type: FormNodeTypes.TITLE }, + { + name: 'techRecord_seatsUpperDeck', + label: 'Upper deck', + value: null, + width: FormNodeWidth.M, + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.NUMBER, + validators: [ + { name: ValidatorNames.Max, args: 99 }, + { + name: ValidatorNames.HandlePsvPassengersChange, + args: { passengersOne: 'techRecord_seatsLowerDeck', passengersTwo: 'techRecord_standingCapacity' }, + }, + ], + class: 'flex--half', + customTags: [{ colour: TagType.RED, label: TagTypeLabels.REQUIRED }], + }, + { + name: 'techRecord_seatsLowerDeck', + label: 'Lower deck', + value: null, + width: FormNodeWidth.M, + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.NUMBER, + validators: [ + { name: ValidatorNames.Max, args: 999 }, + { + name: ValidatorNames.HandlePsvPassengersChange, + args: { passengersOne: 'techRecord_standingCapacity', passengersTwo: 'techRecord_seatsUpperDeck' }, + }, + ], + class: 'flex--half', + customTags: [{ colour: TagType.RED, label: TagTypeLabels.REQUIRED }], + }, + { + name: 'techRecord_standingCapacity', + label: 'Standing capacity', + value: null, + width: FormNodeWidth.XXS, + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.NUMBER, + validators: [ + { name: ValidatorNames.Max, args: 999 }, + { + name: ValidatorNames.HandlePsvPassengersChange, + args: { passengersOne: 'techRecord_seatsLowerDeck', passengersTwo: 'techRecord_seatsUpperDeck' }, + }, + ], + }, + { + name: 'techRecord_vehicleClass_description', + label: 'Vehicle class', + value: null, + hint: 'The Vehicle Class is calculated automatically based on the number of seats and standing capacity. Only change the Class if you need to', + customId: 'vehicleClassDescription', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.STRING, + editType: FormNodeEditTypes.SELECT, + options: [ + { + label: 'small psv (ie: less than or equal to 22 passengers)', + value: 'small psv (ie: less than or equal to 22 seats)', + }, + { + label: 'large psv(ie: greater than or equal to 23 passengers)', + value: 'large psv(ie: greater than 23 seats)', + }, + ], + class: '.govuk-input--width-10', + validators: [{ name: ValidatorNames.Required }], + customTags: [{ colour: TagType.RED, label: TagTypeLabels.REQUIRED }], + }, + { + name: 'techRecord_vehicleSize', + label: 'Vehicle size', + value: null, + hint: 'The Vehicle Size is calculated automatically based on the number of seats and standing capacity. Only change the Size if you need to', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.RADIO, + options: getOptionsFromEnum(VehicleSize), + customTags: [{ colour: TagType.RED, label: TagTypeLabels.REQUIRED }], + }, + { + name: 'techRecord_numberOfSeatbelts', + label: 'Number of seat belts', + value: null, + width: FormNodeWidth.XXS, + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.NUMERICSTRING, + validators: [{ name: ValidatorNames.Max, args: 99 }], + }, + { + name: 'techRecord_seatbeltInstallationApprovalDate', + label: 'Seatbelt installation approval date / type approved', + value: null, + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.DATE, + editType: FormNodeEditTypes.DATE, + isoDate: false, + validators: [{ name: ValidatorNames.PastDate }], + }, + { + name: 'techRecord_departmentalVehicleMarker', + label: 'Departmental vehicle marker', + value: null, + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.RADIO, + options: [ + { value: true, label: 'Yes' }, + { value: false, label: 'No' }, + ], + validators: [], + }, + { + name: 'techRecord_alterationMarker', + label: 'Alteration marker', + value: null, + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.RADIO, + options: [ + { value: true, label: 'Yes' }, + { value: false, label: 'No' }, + ], + validators: [], + }, + { + name: 'techRecord_functionCode', + label: 'Function code', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + ], }; diff --git a/src/app/forms/templates/psv/psv-tyres.template.ts b/src/app/forms/templates/psv/psv-tyres.template.ts index 0b51ee6874..7fcb0a6963 100644 --- a/src/app/forms/templates/psv/psv-tyres.template.ts +++ b/src/app/forms/templates/psv/psv-tyres.template.ts @@ -2,94 +2,109 @@ import { ValidatorNames } from '@forms/models/validators.enum'; import { FormNode, FormNodeEditTypes, FormNodeTypes } from '@forms/services/dynamic-form.types'; export const PsvTyresTemplate: FormNode = { - name: 'tyreSection', - type: FormNodeTypes.GROUP, - label: 'Tyres', - children: [ - { - name: 'techRecord_speedRestriction', - label: 'Speed Restriction', - value: null, - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.NUMBER, - validators: [{ name: ValidatorNames.Numeric }, { name: ValidatorNames.Max, args: 99 }, { name: ValidatorNames.Min, args: 0 }], - }, - { - name: 'techRecord_axles', - value: '', - type: FormNodeTypes.ARRAY, - validators: [{ - name: ValidatorNames.MinArrayLengthIfNotEmpty, args: { minimumLength: 2, message: 'You cannot submit a PSV with less than 2 axles.' }, - }], - children: [ - { - name: '0', - label: 'Axle', - value: '', - type: FormNodeTypes.GROUP, - children: [ - { - name: 'axleNumber', - label: 'Axle Number', - type: FormNodeTypes.CONTROL, - }, + name: 'tyreSection', + type: FormNodeTypes.GROUP, + label: 'Tyres', + children: [ + { + name: 'techRecord_speedRestriction', + label: 'Speed Restriction', + value: null, + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.NUMBER, + validators: [ + { name: ValidatorNames.Numeric }, + { name: ValidatorNames.Max, args: 99 }, + { name: ValidatorNames.Min, args: 0 }, + ], + }, + { + name: 'techRecord_axles', + value: '', + type: FormNodeTypes.ARRAY, + validators: [ + { + name: ValidatorNames.MinArrayLengthIfNotEmpty, + args: { minimumLength: 2, message: 'You cannot submit a PSV with less than 2 axles.' }, + }, + ], + children: [ + { + name: '0', + label: 'Axle', + value: '', + type: FormNodeTypes.GROUP, + children: [ + { + name: 'axleNumber', + label: 'Axle Number', + type: FormNodeTypes.CONTROL, + }, - { - name: 'tyres_tyreCode', - label: 'Tyre Code', - value: null, - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.NUMBER, - validators: [{ name: ValidatorNames.Numeric }, { name: ValidatorNames.Max, args: 99999 }, { name: ValidatorNames.Min, args: 0 }], - }, - { - name: 'tyres_tyreSize', - label: 'Tyre Size', - value: null, - type: FormNodeTypes.CONTROL, - disabled: true, - validators: [ - { name: ValidatorNames.MaxLength, args: 12 }, - { name: ValidatorNames.Min, args: 0 }, - ], - }, - { - name: 'tyres_plyRating', - label: 'Ply Rating', - value: null, - type: FormNodeTypes.CONTROL, - disabled: true, - validators: [ - { name: ValidatorNames.MaxLength, args: 2 }, - { name: ValidatorNames.Min, args: 0 }, - ], - }, - { - name: 'tyres_speedCategorySymbol', - label: 'Speed category symbol', - value: null, - type: FormNodeTypes.CONTROL, - validators: [], - }, - { - name: 'tyres_fitmentCode', - label: 'Fitment code', - value: null, - type: FormNodeTypes.CONTROL, - validators: [], - }, - { - name: 'tyres_dataTrAxles', - label: 'Load index', - value: null, - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.NUMBER, - disabled: true, - validators: [{ name: ValidatorNames.Numeric }, { name: ValidatorNames.Max, args: 999 }, { name: ValidatorNames.Min, args: 0 }], - }, - ], - }, - ], - }, - ], + { + name: 'tyres_tyreCode', + label: 'Tyre Code', + value: null, + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.NUMBER, + validators: [ + { name: ValidatorNames.Numeric }, + { name: ValidatorNames.Max, args: 99999 }, + { name: ValidatorNames.Min, args: 0 }, + ], + }, + { + name: 'tyres_tyreSize', + label: 'Tyre Size', + value: null, + type: FormNodeTypes.CONTROL, + disabled: true, + validators: [ + { name: ValidatorNames.MaxLength, args: 12 }, + { name: ValidatorNames.Min, args: 0 }, + ], + }, + { + name: 'tyres_plyRating', + label: 'Ply Rating', + value: null, + type: FormNodeTypes.CONTROL, + disabled: true, + validators: [ + { name: ValidatorNames.MaxLength, args: 2 }, + { name: ValidatorNames.Min, args: 0 }, + ], + }, + { + name: 'tyres_speedCategorySymbol', + label: 'Speed category symbol', + value: null, + type: FormNodeTypes.CONTROL, + validators: [], + }, + { + name: 'tyres_fitmentCode', + label: 'Fitment code', + value: null, + type: FormNodeTypes.CONTROL, + validators: [], + }, + { + name: 'tyres_dataTrAxles', + label: 'Load index', + value: null, + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.NUMBER, + disabled: true, + validators: [ + { name: ValidatorNames.Numeric }, + { name: ValidatorNames.Max, args: 999 }, + { name: ValidatorNames.Min, args: 0 }, + ], + }, + ], + }, + ], + }, + ], }; diff --git a/src/app/forms/templates/psv/psv-weight.template.ts b/src/app/forms/templates/psv/psv-weight.template.ts index 433a32b761..2fdcca4cc9 100644 --- a/src/app/forms/templates/psv/psv-weight.template.ts +++ b/src/app/forms/templates/psv/psv-weight.template.ts @@ -2,145 +2,145 @@ import { ValidatorNames } from '@forms/models/validators.enum'; import { FormNode, FormNodeTypes } from '../../services/dynamic-form.types'; const requiredValidation = [ - { name: ValidatorNames.Numeric, args: 99999 }, - { name: ValidatorNames.Max, args: 99999 }, - { name: ValidatorNames.Min, args: 0 }, + { name: ValidatorNames.Numeric, args: 99999 }, + { name: ValidatorNames.Max, args: 99999 }, + { name: ValidatorNames.Min, args: 0 }, ]; const optionalValidation = [ - { name: ValidatorNames.Numeric, args: 99999 }, - { name: ValidatorNames.Max, args: 99999 }, - { name: ValidatorNames.Min, args: 0 }, + { name: ValidatorNames.Numeric, args: 99999 }, + { name: ValidatorNames.Max, args: 99999 }, + { name: ValidatorNames.Min, args: 0 }, ]; export const PsvWeightsTemplate: FormNode = { - name: 'weightsSection', - label: 'Weights', - type: FormNodeTypes.SECTION, - children: [ - { - name: 'techRecord_grossSection', - label: 'Gross vehicle weight', - value: null, - type: FormNodeTypes.SECTION, - }, - { - name: 'techRecord_grossKerbWeight', - label: 'Kerb', - customValidatorErrorName: 'Gross Kerb Weight', - value: null, - type: FormNodeTypes.CONTROL, - validators: requiredValidation, - }, - { - name: 'techRecord_grossLadenWeight', - label: 'Laden', - customValidatorErrorName: 'Gross Laden Weight', - value: null, - type: FormNodeTypes.CONTROL, - validators: requiredValidation, - }, - { - name: 'techRecord_grossGbWeight', - label: 'GB', - customValidatorErrorName: 'Gross GB Weight', - value: null, - type: FormNodeTypes.CONTROL, - validators: requiredValidation, - }, - { - name: 'techRecord_grossDesignWeight', - label: 'Design', - customValidatorErrorName: 'Gross Design Weight', - value: null, - type: FormNodeTypes.CONTROL, - validators: requiredValidation, - }, - { - name: 'techRecord_unladenWeight', - label: 'Unladen weight', - value: null, - type: FormNodeTypes.CONTROL, - validators: requiredValidation, - }, - { - name: 'techRecord_trainSection', - label: 'Train weight', - value: null, - type: FormNodeTypes.SECTION, - }, - { - name: 'techRecord_maxTrainGbWeight', - label: 'Max train GB', - value: null, - type: FormNodeTypes.CONTROL, - validators: optionalValidation, - }, - { - name: 'techRecord_trainDesignWeight', - label: 'Train design', - value: null, - type: FormNodeTypes.CONTROL, - validators: optionalValidation, - }, - { - name: 'axleSection', - label: 'Axle weights', - value: '', - type: FormNodeTypes.SECTION, - }, - { - name: 'techRecord_axles', - value: '', - type: FormNodeTypes.ARRAY, - children: [ - { - name: '0', - label: 'Axle', - value: '', - type: FormNodeTypes.GROUP, - children: [ - { - name: 'axleNumber', - label: 'Axle Number', - type: FormNodeTypes.CONTROL, - }, + name: 'weightsSection', + label: 'Weights', + type: FormNodeTypes.SECTION, + children: [ + { + name: 'techRecord_grossSection', + label: 'Gross vehicle weight', + value: null, + type: FormNodeTypes.SECTION, + }, + { + name: 'techRecord_grossKerbWeight', + label: 'Kerb', + customValidatorErrorName: 'Gross Kerb Weight', + value: null, + type: FormNodeTypes.CONTROL, + validators: requiredValidation, + }, + { + name: 'techRecord_grossLadenWeight', + label: 'Laden', + customValidatorErrorName: 'Gross Laden Weight', + value: null, + type: FormNodeTypes.CONTROL, + validators: requiredValidation, + }, + { + name: 'techRecord_grossGbWeight', + label: 'GB', + customValidatorErrorName: 'Gross GB Weight', + value: null, + type: FormNodeTypes.CONTROL, + validators: requiredValidation, + }, + { + name: 'techRecord_grossDesignWeight', + label: 'Design', + customValidatorErrorName: 'Gross Design Weight', + value: null, + type: FormNodeTypes.CONTROL, + validators: requiredValidation, + }, + { + name: 'techRecord_unladenWeight', + label: 'Unladen weight', + value: null, + type: FormNodeTypes.CONTROL, + validators: requiredValidation, + }, + { + name: 'techRecord_trainSection', + label: 'Train weight', + value: null, + type: FormNodeTypes.SECTION, + }, + { + name: 'techRecord_maxTrainGbWeight', + label: 'Max train GB', + value: null, + type: FormNodeTypes.CONTROL, + validators: optionalValidation, + }, + { + name: 'techRecord_trainDesignWeight', + label: 'Train design', + value: null, + type: FormNodeTypes.CONTROL, + validators: optionalValidation, + }, + { + name: 'axleSection', + label: 'Axle weights', + value: '', + type: FormNodeTypes.SECTION, + }, + { + name: 'techRecord_axles', + value: '', + type: FormNodeTypes.ARRAY, + children: [ + { + name: '0', + label: 'Axle', + value: '', + type: FormNodeTypes.GROUP, + children: [ + { + name: 'axleNumber', + label: 'Axle Number', + type: FormNodeTypes.CONTROL, + }, - { - name: 'weights_kerbWeight', - label: 'Kerb weight', - customValidatorErrorName: 'Axle Kerb Weight', - value: null, - type: FormNodeTypes.CONTROL, - validators: requiredValidation, - }, - { - name: 'weights_ladenWeight', - label: 'Laden weight', - customValidatorErrorName: 'Axle Laden Weight', - value: null, - type: FormNodeTypes.CONTROL, - validators: requiredValidation, - }, - { - name: 'weights_gbWeight', - label: 'GB weight', - customValidatorErrorName: 'Axle GB Weight', - value: null, - type: FormNodeTypes.CONTROL, - validators: requiredValidation, - }, - { - name: 'weights_designWeight', - label: 'Design weight', - customValidatorErrorName: 'Axle Design Weight', - value: null, - type: FormNodeTypes.CONTROL, - validators: requiredValidation, - }, - ], - }, - ], - }, - ], + { + name: 'weights_kerbWeight', + label: 'Kerb weight', + customValidatorErrorName: 'Axle Kerb Weight', + value: null, + type: FormNodeTypes.CONTROL, + validators: requiredValidation, + }, + { + name: 'weights_ladenWeight', + label: 'Laden weight', + customValidatorErrorName: 'Axle Laden Weight', + value: null, + type: FormNodeTypes.CONTROL, + validators: requiredValidation, + }, + { + name: 'weights_gbWeight', + label: 'GB weight', + customValidatorErrorName: 'Axle GB Weight', + value: null, + type: FormNodeTypes.CONTROL, + validators: requiredValidation, + }, + { + name: 'weights_designWeight', + label: 'Design weight', + customValidatorErrorName: 'Axle Design Weight', + value: null, + type: FormNodeTypes.CONTROL, + validators: requiredValidation, + }, + ], + }, + ], + }, + ], }; diff --git a/src/app/forms/templates/search/single-search-result.template.ts b/src/app/forms/templates/search/single-search-result.template.ts index 9e53fa0cfa..9ea4bb66a8 100644 --- a/src/app/forms/templates/search/single-search-result.template.ts +++ b/src/app/forms/templates/search/single-search-result.template.ts @@ -2,63 +2,67 @@ import { Params } from '@angular/router'; import { ValidatorNames } from '@forms/models/validators.enum'; import { FormNode, FormNodeTypes, FormNodeViewTypes } from '../../services/dynamic-form.types'; -export function createSingleSearchResult(systemNumber: string, createdTimestamp: string, queryParams?: Params): FormNode { - return { - name: 'singleSearchResult', - type: FormNodeTypes.GROUP, - label: 'Technical Record', - viewType: FormNodeViewTypes.SUBHEADING, - subHeadingLink: { - label: 'Select technical record', - url: `/tech-records/${systemNumber}/${createdTimestamp}`, - queryParams, - }, - children: [ - { - name: 'vin', - label: 'Vehicle identification number (VIN)', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.STRING, - }, - { - name: 'vrm', - label: 'Vehicle registration mark (VRM)', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.VRM, - }, - { - name: 'trailerId', - label: 'Trailer ID', - type: FormNodeTypes.CONTROL, - }, - { - name: 'vehicleType', - label: 'Vehicle type', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.STRING, - validators: [ - { name: ValidatorNames.HideIfNotEqual, args: { sibling: 'trailerId', value: 'TRL' } }, - { name: ValidatorNames.HideIfNotEqual, args: { sibling: 'vrm', value: ['HGV', 'PSV'] } }, - ], - }, - { - name: 'manufactureYear', - label: 'Year of manufacture', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.STRING, - }, - { - name: 'make', - label: 'Make', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.STRING, - }, - { - name: 'model', - label: 'Model', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.STRING, - }, - ], - }; +export function createSingleSearchResult( + systemNumber: string, + createdTimestamp: string, + queryParams?: Params +): FormNode { + return { + name: 'singleSearchResult', + type: FormNodeTypes.GROUP, + label: 'Technical Record', + viewType: FormNodeViewTypes.SUBHEADING, + subHeadingLink: { + label: 'Select technical record', + url: `/tech-records/${systemNumber}/${createdTimestamp}`, + queryParams, + }, + children: [ + { + name: 'vin', + label: 'Vehicle identification number (VIN)', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.STRING, + }, + { + name: 'vrm', + label: 'Vehicle registration mark (VRM)', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.VRM, + }, + { + name: 'trailerId', + label: 'Trailer ID', + type: FormNodeTypes.CONTROL, + }, + { + name: 'vehicleType', + label: 'Vehicle type', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.STRING, + validators: [ + { name: ValidatorNames.HideIfNotEqual, args: { sibling: 'trailerId', value: 'TRL' } }, + { name: ValidatorNames.HideIfNotEqual, args: { sibling: 'vrm', value: ['HGV', 'PSV'] } }, + ], + }, + { + name: 'manufactureYear', + label: 'Year of manufacture', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.STRING, + }, + { + name: 'make', + label: 'Make', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.STRING, + }, + { + name: 'model', + label: 'Model', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.STRING, + }, + ], + }; } diff --git a/src/app/forms/templates/small-trailer/small-trailer-tech-record.template.ts b/src/app/forms/templates/small-trailer/small-trailer-tech-record.template.ts index 640e9a1ba1..fddd9ead07 100644 --- a/src/app/forms/templates/small-trailer/small-trailer-tech-record.template.ts +++ b/src/app/forms/templates/small-trailer/small-trailer-tech-record.template.ts @@ -1,112 +1,123 @@ +import { EUVehicleCategory } from '@dvsa/cvs-type-definitions/types/v3/tech-record/enums/euVehicleCategory.enum.js'; +import { VehicleConfiguration } from '@dvsa/cvs-type-definitions/types/v3/tech-record/enums/vehicleConfigurationLightVehicle.enum.js'; import { ValidatorNames } from '@forms/models/validators.enum'; import { - FormNode, FormNodeEditTypes, FormNodeTypes, FormNodeViewTypes, FormNodeWidth, TagTypeLabels, + FormNode, + FormNodeEditTypes, + FormNodeTypes, + FormNodeViewTypes, + FormNodeWidth, + TagTypeLabels, } from '@forms/services/dynamic-form.types'; import { getOptionsFromEnum } from '@forms/utils/enum-map'; import { VehicleSubclass } from '@models/vehicle-tech-record.model'; import { TagType } from '@shared/components/tag/tag.component'; -import { EUVehicleCategory } from '@dvsa/cvs-type-definitions/types/v3/tech-record/enums/euVehicleCategory.enum.js'; -import { VehicleConfiguration } from '@dvsa/cvs-type-definitions/types/v3/tech-record/enums/vehicleConfigurationLightVehicle.enum.js'; export const SmallTrailerTechRecord: FormNode = { - name: 'techRecordSummary', - type: FormNodeTypes.GROUP, - label: 'Vehicle Summary', - children: [ - { - name: 'techRecord_vehicleType', - label: 'Vehicle type', - value: '', - width: FormNodeWidth.S, - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.VEHICLETYPE, - disabled: true, - validators: [], - customTags: [{ colour: TagType.RED, label: TagTypeLabels.REQUIRED }], - }, - { - name: 'techRecord_statusCode', - label: 'Record status', - value: '', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - customTags: [{ colour: TagType.RED, label: TagTypeLabels.REQUIRED }], - }, - { - name: 'techRecord_manufactureYear', - label: 'Year of manufacture', - value: null, - width: FormNodeWidth.XS, - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.NUMBER, - validators: [ - { name: ValidatorNames.Max, args: 9999 }, - { name: ValidatorNames.Min, args: 1000 }, - { name: ValidatorNames.PastYear }, - ], - }, - { - name: 'techRecord_noOfAxles', - label: 'Number of axles', - value: null, - width: FormNodeWidth.XXS, - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.NUMBER, - validators: [{ name: ValidatorNames.Max, args: 99 }], - customTags: [{ colour: TagType.RED, label: TagTypeLabels.REQUIRED }], - }, - { - customId: 'vehicleClassDescription', - name: 'techRecord_vehicleClass_description', - label: 'Vehicle class', - value: '', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.SELECT, - options: [ - { label: 'motorbikes over 200cc or with a sidecar', value: 'motorbikes over 200cc or with a sidecar' }, - { label: 'not applicable', value: 'not applicable' }, - { label: 'small psv (ie: less than or equal to 22 passengers)', value: 'small psv (ie: less than or equal to 22 seats)' }, - { label: 'motorbikes up to 200cc', value: 'motorbikes up to 200cc' }, - { label: 'trailer', value: 'trailer' }, - { label: 'large psv(ie: greater than or equal to 23 passengers)', value: 'large psv(ie: greater than 23 seats)' }, - { label: '3 wheelers', value: '3 wheelers' }, - { label: 'heavy goods vehicle', value: 'heavy goods vehicle' }, - { label: 'MOT class 4', value: 'MOT class 4' }, - { label: 'MOT class 7', value: 'MOT class 7' }, - { label: 'MOT class 5', value: 'MOT class 5' }, - ], - validators: [{ name: ValidatorNames.Required }], - customTags: [{ colour: TagType.RED, label: TagTypeLabels.REQUIRED }], - }, - { - name: 'techRecord_vehicleSubclass', - label: 'Vehicle Subclass', - width: FormNodeWidth.XXS, - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.CHECKBOXGROUP, - options: getOptionsFromEnum(VehicleSubclass), - }, - { - name: 'techRecord_vehicleConfiguration', - label: 'Vehicle configuration', - value: null, - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.SELECT, - options: getOptionsFromEnum(VehicleConfiguration), - customTags: [{ colour: TagType.RED, label: TagTypeLabels.REQUIRED }], - }, - { - name: 'techRecord_euVehicleCategory', - label: 'EU vehicle category', - value: EUVehicleCategory.O1, - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.SELECT, - width: FormNodeWidth.S, - options: getOptionsFromEnum(EUVehicleCategory).filter( - (option) => option.value === EUVehicleCategory.O1 || option.value === EUVehicleCategory.O2, - ), - customTags: [{ colour: TagType.RED, label: TagTypeLabels.REQUIRED }], - }, - ], + name: 'techRecordSummary', + type: FormNodeTypes.GROUP, + label: 'Vehicle Summary', + children: [ + { + name: 'techRecord_vehicleType', + label: 'Vehicle type', + value: '', + width: FormNodeWidth.S, + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.VEHICLETYPE, + disabled: true, + validators: [], + customTags: [{ colour: TagType.RED, label: TagTypeLabels.REQUIRED }], + }, + { + name: 'techRecord_statusCode', + label: 'Record status', + value: '', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + customTags: [{ colour: TagType.RED, label: TagTypeLabels.REQUIRED }], + }, + { + name: 'techRecord_manufactureYear', + label: 'Year of manufacture', + value: null, + width: FormNodeWidth.XS, + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.NUMBER, + validators: [ + { name: ValidatorNames.Max, args: 9999 }, + { name: ValidatorNames.Min, args: 1000 }, + { name: ValidatorNames.PastYear }, + ], + }, + { + name: 'techRecord_noOfAxles', + label: 'Number of axles', + value: null, + width: FormNodeWidth.XXS, + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.NUMBER, + validators: [{ name: ValidatorNames.Max, args: 99 }], + customTags: [{ colour: TagType.RED, label: TagTypeLabels.REQUIRED }], + }, + { + customId: 'vehicleClassDescription', + name: 'techRecord_vehicleClass_description', + label: 'Vehicle class', + value: '', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.SELECT, + options: [ + { label: 'motorbikes over 200cc or with a sidecar', value: 'motorbikes over 200cc or with a sidecar' }, + { label: 'not applicable', value: 'not applicable' }, + { + label: 'small psv (ie: less than or equal to 22 passengers)', + value: 'small psv (ie: less than or equal to 22 seats)', + }, + { label: 'motorbikes up to 200cc', value: 'motorbikes up to 200cc' }, + { label: 'trailer', value: 'trailer' }, + { + label: 'large psv(ie: greater than or equal to 23 passengers)', + value: 'large psv(ie: greater than 23 seats)', + }, + { label: '3 wheelers', value: '3 wheelers' }, + { label: 'heavy goods vehicle', value: 'heavy goods vehicle' }, + { label: 'MOT class 4', value: 'MOT class 4' }, + { label: 'MOT class 7', value: 'MOT class 7' }, + { label: 'MOT class 5', value: 'MOT class 5' }, + ], + validators: [{ name: ValidatorNames.Required }], + customTags: [{ colour: TagType.RED, label: TagTypeLabels.REQUIRED }], + }, + { + name: 'techRecord_vehicleSubclass', + label: 'Vehicle Subclass', + width: FormNodeWidth.XXS, + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.CHECKBOXGROUP, + options: getOptionsFromEnum(VehicleSubclass), + }, + { + name: 'techRecord_vehicleConfiguration', + label: 'Vehicle configuration', + value: null, + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.SELECT, + options: getOptionsFromEnum(VehicleConfiguration), + customTags: [{ colour: TagType.RED, label: TagTypeLabels.REQUIRED }], + }, + { + name: 'techRecord_euVehicleCategory', + label: 'EU vehicle category', + value: EUVehicleCategory.O1, + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.SELECT, + width: FormNodeWidth.S, + options: getOptionsFromEnum(EUVehicleCategory).filter( + (option) => option.value === EUVehicleCategory.O1 || option.value === EUVehicleCategory.O2 + ), + customTags: [{ colour: TagType.RED, label: TagTypeLabels.REQUIRED }], + }, + ], }; diff --git a/src/app/forms/templates/tech-records/vehicle-summary.template.ts b/src/app/forms/templates/tech-records/vehicle-summary.template.ts index f07ac223e1..5ce6be3ef7 100644 --- a/src/app/forms/templates/tech-records/vehicle-summary.template.ts +++ b/src/app/forms/templates/tech-records/vehicle-summary.template.ts @@ -1,53 +1,51 @@ -import { - FormNode, FormNodeTypes, FormNodeViewTypes, FormNodeEditTypes, -} from '@forms/services/dynamic-form.types'; +import { FormNode, FormNodeEditTypes, FormNodeTypes, FormNodeViewTypes } from '@forms/services/dynamic-form.types'; export const VehicleSummary: FormNode = { - name: 'vehicleSummary', - label: 'Vehicle Summary', - type: FormNodeTypes.GROUP, - children: [ - { - name: 'vin', - label: 'Vehicle identification number (VIN)', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.STRING, - editType: FormNodeEditTypes.TEXT, - }, - { - name: 'primaryVrm', - label: 'Vehicle registration mark (VRM)', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.STRING, - editType: FormNodeEditTypes.TEXT, - }, - { - name: 'techRecord_vehicleType', - label: 'Vehicle type', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.STRING, - editType: FormNodeEditTypes.TEXT, - }, - { - name: 'techRecord_manufactureYear', - label: 'Year of manufacture', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.STRING, - editType: FormNodeEditTypes.TEXT, - }, - { - name: 'techRecord_make', - label: 'Make', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.STRING, - editType: FormNodeEditTypes.TEXT, - }, - { - name: 'techRecord_model', - label: 'Model', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.STRING, - editType: FormNodeEditTypes.TEXT, - }, - ], + name: 'vehicleSummary', + label: 'Vehicle Summary', + type: FormNodeTypes.GROUP, + children: [ + { + name: 'vin', + label: 'Vehicle identification number (VIN)', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.STRING, + editType: FormNodeEditTypes.TEXT, + }, + { + name: 'primaryVrm', + label: 'Vehicle registration mark (VRM)', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.STRING, + editType: FormNodeEditTypes.TEXT, + }, + { + name: 'techRecord_vehicleType', + label: 'Vehicle type', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.STRING, + editType: FormNodeEditTypes.TEXT, + }, + { + name: 'techRecord_manufactureYear', + label: 'Year of manufacture', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.STRING, + editType: FormNodeEditTypes.TEXT, + }, + { + name: 'techRecord_make', + label: 'Make', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.STRING, + editType: FormNodeEditTypes.TEXT, + }, + { + name: 'techRecord_model', + label: 'Model', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.STRING, + editType: FormNodeEditTypes.TEXT, + }, + ], }; diff --git a/src/app/forms/templates/test-records/create-master.template.ts b/src/app/forms/templates/test-records/create-master.template.ts index a42180ab83..af7c51f37b 100644 --- a/src/app/forms/templates/test-records/create-master.template.ts +++ b/src/app/forms/templates/test-records/create-master.template.ts @@ -7,8 +7,12 @@ import { AdditionalDefectsSection } from './section-templates/additionalDefects/ import { CustomDefectsSection } from './section-templates/customDefects/custom-defects-section.template'; import { DeskBasedEmissionsSection } from './section-templates/emissions/desk-based-emissions-section.template'; import { EmissionsSection } from './section-templates/emissions/emissions-section.template'; +import { AdrNotesSection } from './section-templates/notes/adr-notes-section.template'; import { NotesSection } from './section-templates/notes/notes-section.template'; -import { reasonForCreationHiddenSection, reasonForCreationSection } from './section-templates/reasonForCreation/reasonForCreation.template'; +import { + reasonForCreationHiddenSection, + reasonForCreationSection, +} from './section-templates/reasonForCreation/reasonForCreation.template'; import { CreateRequiredSectionHgvTrl } from './section-templates/required/contingency-required-hidden-section-hgv-trl.template'; import { CreateRequiredSectionLgvCar } from './section-templates/required/contingency-required-hidden-section-lgv-car.template'; import { CreateRequiredSectionMotorcycle } from './section-templates/required/contingency-required-hidden-section-motorcycle.template'; @@ -21,23 +25,23 @@ import { SeatbeltHiddenSection } from './section-templates/required/seatbelt-hid import { SpecialistRequiredSectionLgvCarSmallTrl } from './section-templates/required/specialist-required-hidden-section-lgv-car.template'; import { SeatbeltSection } from './section-templates/seatbelt/seatbelt-section.template'; import { ContingencyTestSectionGroup1 } from './section-templates/test/contingency/contingency-test-section-group1.template'; -import { ContingencyTestSectionGroup12and14 } from './section-templates/test/contingency/contingency-test-section-group12and14.template'; -import { ContingencyTestSectionGroup15and16 } from './section-templates/test/contingency/contingency-test-section-group15and16.template'; import { ContingencyTestSectionGroup3And4And8 } from './section-templates/test/contingency/contingency-test-section-group3And4And8.template'; import { ContingencyTestSectionGroup5And13 } from './section-templates/test/contingency/contingency-test-section-group5And13.template'; import { ContingencyTestSectionGroup6And11 } from './section-templates/test/contingency/contingency-test-section-group6And11.template'; import { ContingencyTestSectionGroup7 } from './section-templates/test/contingency/contingency-test-section-group7.template'; +import { ContingencyTestSectionGroup8Notifiable } from './section-templates/test/contingency/contingency-test-section-group8Notifiable.template'; import { ContingencyTestSectionGroup9And10 } from './section-templates/test/contingency/contingency-test-section-group9And10.template'; +import { ContingencyTestSectionGroup9And10CentralDocs } from './section-templates/test/contingency/contingency-test-section-group9And10CentralDocs.template'; +import { ContingencyTestSectionGroup12and14 } from './section-templates/test/contingency/contingency-test-section-group12and14.template'; +import { ContingencyTestSectionGroup15and16 } from './section-templates/test/contingency/contingency-test-section-group15and16.template'; import { ContingencyTestSectionSpecialistGroup1 } from './section-templates/test/contingency/contingency-test-section-specialist-group1.template'; import { ContingencyTestSectionSpecialistGroup2 } from './section-templates/test/contingency/contingency-test-section-specialist-group2.template'; -import { - ContingencyTestSectionSpecialistGroup3And4, -} from './section-templates/test/contingency/contingency-test-section-specialist-group3And4.template'; +import { ContingencyTestSectionSpecialistGroup3And4 } from './section-templates/test/contingency/contingency-test-section-specialist-group3And4.template'; import { ContingencyTestSectionSpecialistGroup5 } from './section-templates/test/contingency/contingency-test-section-specialist-group5.template'; +import { OldIVAContingencyTestSectionSpecialistGroup1 } from './section-templates/test/contingency/old-contingency-specialist-group1.template'; +import { OldIVAContingencyTestSectionSpecialistGroup5 } from './section-templates/test/contingency/old-contingency-specialist-group5.template'; import { DeskBasedTestSectionGroup1Psv } from './section-templates/test/desk-based/desk-based-test-section-group1-PSV.template'; -import { - DeskBasedTestSectionGroup1And4HgvTrl as DeskBasedTestSectionGroup1And4And5HgvTrl, -} from './section-templates/test/desk-based/desk-based-test-section-group1And4-HGV-TRL.template'; +import { DeskBasedTestSectionGroup1And4HgvTrl as DeskBasedTestSectionGroup1And4And5HgvTrl } from './section-templates/test/desk-based/desk-based-test-section-group1And4-HGV-TRL.template'; import { DeskBasedTestSectionGroup2And5 } from './section-templates/test/desk-based/desk-based-test-section-group2.template'; import { DeskBasedTestSectionGroup3 } from './section-templates/test/desk-based/desk-based-test-section-group3.template'; import { DeskBasedTestSectionGroup4Psv } from './section-templates/test/desk-based/desk-based-test-section-group4-PSV.template'; @@ -50,702 +54,752 @@ import { ContingencyVehicleSectionDefaultTrl } from './section-templates/vehicle import { ContingencyIvaMsvaVehicleSection } from './section-templates/vehicle/contingency-iva-msva-vehicle-section.template'; import { DeskBasedVehicleSectionDefaultPsvHgv } from './section-templates/vehicle/desk-based-default-psv-hgv-vehicle-section.template'; import { DeskBasedVehicleSectionDefaultTrl } from './section-templates/vehicle/desk-based-default-trl-vehicle-section.template'; -import { - DeskBasedVehicleSectionHgvGroup1And2And4 as DeskBasedVehicleSectionHgvGroup1And2And4And5, -} from './section-templates/vehicle/desk-based-test-hgv-vehicle-section-group1And2And4.template'; +import { DeskBasedVehicleSectionHgvGroup1And2And4 as DeskBasedVehicleSectionHgvGroup1And2And4And5 } from './section-templates/vehicle/desk-based-test-hgv-vehicle-section-group1And2And4.template'; import { DeskBasedVehicleSectionGroup4LGV } from './section-templates/vehicle/desk-based-vehicle-section-group4-lgv.template'; import { DeskBasedVehicleSectionGroup5Lgv } from './section-templates/vehicle/desk-based-vehicle-section-group5-lgv.template'; import { VehicleSectionGroup3 } from './section-templates/vehicle/group-3-light-vehicle-section.template'; import { ContingencyVisitSection } from './section-templates/visit/contingency-visit-section.template'; import { VisitSection } from './section-templates/visit/visit-section.template'; -import { OldIVAContingencyTestSectionSpecialistGroup1 } from './section-templates/test/contingency/old-contingency-specialist-group1.template'; -import { OldIVAContingencyTestSectionSpecialistGroup5 } from './section-templates/test/contingency/old-contingency-specialist-group5.template'; const groups1and2Template: Record = { - required: CreateRequiredSection, - vehicle: ContingencyVehicleSectionDefaultPsvHgvLight, - test: ContingencyTestSectionGroup1, - seatbelts: SeatbeltSection, - visit: ContingencyVisitSection, - notes: NotesSection, - defects: DefectsTpl, - customDefects: CustomDefectsHiddenSection, - reasonForCreation: reasonForCreationSection, + required: CreateRequiredSection, + vehicle: ContingencyVehicleSectionDefaultPsvHgvLight, + test: ContingencyTestSectionGroup1, + seatbelts: SeatbeltSection, + visit: ContingencyVisitSection, + notes: NotesSection, + defects: DefectsTpl, + customDefects: CustomDefectsHiddenSection, + reasonForCreation: reasonForCreationSection, }; -export const contingencyTestTemplates: Record>>> = { - psv: { - default: { - vehicle: ContingencyVehicleSectionDefaultPsvHgvLight, - test: TestSection, - seatbelts: SeatbeltSection, - emissions: EmissionsSection, - visit: ContingencyVisitSection, - notes: NotesSection, - defects: DefectsTpl, - customDefects: CustomDefectsSection, - required: CreateRequiredSection, - }, - testTypesGroup1: groups1and2Template, - testTypesGroup2: groups1and2Template, - testTypesGroup3And4And8: { - vehicle: ContingencyVehicleSectionDefaultPsvHgvLight, - test: ContingencyTestSectionGroup3And4And8, - visit: ContingencyVisitSection, - seatbelts: SeatbeltHiddenSection, - notes: NotesSection, - customDefects: CustomDefectsSection, - defects: defectsHiddenSection, - reasonForCreation: reasonForCreationSection, - required: CreateRequiredSection, - }, - testTypesGroup15And16: { - required: CreateRequiredSection, - vehicle: ContingencyVehicleSectionDefaultPsvHgvLight, - test: ContingencyTestSectionGroup15and16, - emissions: EmissionsSection, - seatbelts: SeatbeltHiddenSection, - visit: ContingencyVisitSection, - notes: NotesSection, - customDefects: CustomDefectsSection, - reasonForCreation: reasonForCreationSection, - }, - testTypesSpecialistGroup1: { - vehicle: ContingencyIvaMsvaVehicleSection, - test: ContingencyTestSectionSpecialistGroup1, - seatbelts: SeatbeltHiddenSection, - visit: ContingencyVisitSection, - notes: NotesSection, - requiredStandards: RequiredStandardsTpl, - defects: defectsHiddenSection, - customDefects: AdditionalDefectsSection, - reasonForCreation: reasonForCreationSection, - required: CreateRequiredSection, - }, - testTypesSpecialistGroup1OldIVAorMSVA: { - vehicle: ContingencyVehicleSectionDefaultPsvHgvLight, - test: OldIVAContingencyTestSectionSpecialistGroup1, - seatbelts: SeatbeltHiddenSection, - visit: ContingencyVisitSection, - notes: NotesSection, - customDefects: CustomDefectsSection, - defects: defectsHiddenSection, - reasonForCreation: reasonForCreationSection, - required: CreateRequiredSection, - }, - testTypesSpecialistGroup2: { - vehicle: ContingencyVehicleSectionDefaultPsvHgvLight, - test: ContingencyTestSectionSpecialistGroup2, - seatbelts: SeatbeltSection, - visit: ContingencyVisitSection, - notes: NotesSection, - customDefects: CustomDefectsSection, - defects: defectsHiddenSection, - reasonForCreation: reasonForCreationSection, - required: CreateRequiredSection, - }, - testTypesSpecialistGroup3: { - vehicle: ContingencyVehicleSectionDefaultPsvHgvLight, - test: ContingencyTestSectionSpecialistGroup3And4, - seatbelts: SeatbeltHiddenSection, - visit: ContingencyVisitSection, - notes: NotesSection, - customDefects: CustomDefectsSection, - defects: defectsHiddenSection, - reasonForCreation: reasonForCreationSection, - required: CreateRequiredSection, - }, - testTypesSpecialistGroup4: { - vehicle: ContingencyVehicleSectionDefaultPsvHgvLight, - test: ContingencyTestSectionSpecialistGroup3And4, - seatbelts: SeatbeltSection, - visit: ContingencyVisitSection, - notes: NotesSection, - customDefects: CustomDefectsSection, - defects: defectsHiddenSection, - reasonForCreation: reasonForCreationSection, - required: CreateRequiredSection, - }, - testTypesSpecialistGroup5: { - vehicle: ContingencyIvaMsvaVehicleSection, - test: ContingencyTestSectionSpecialistGroup5, - seatbelts: SeatbeltHiddenSection, - visit: ContingencyVisitSection, - notes: NotesSection, - requiredStandards: RequiredStandardsTpl, - customDefects: AdditionalDefectsSection, - defects: defectsHiddenSection, - reasonForCreation: reasonForCreationSection, - required: CreateRequiredSection, - }, - testTypesSpecialistGroup5OldIVAorMSVA: { - vehicle: ContingencyVehicleSectionDefaultPsvHgvLight, - test: OldIVAContingencyTestSectionSpecialistGroup5, - seatbelts: SeatbeltHiddenSection, - visit: ContingencyVisitSection, - notes: NotesSection, - customDefects: CustomDefectsSection, - defects: defectsHiddenSection, - reasonForCreation: reasonForCreationSection, - required: CreateRequiredSection, - }, - testTypesDeskBasedGroup1: { - required: CreateRequiredSection, - vehicle: DeskBasedVehicleSectionDefaultPsvHgv, - test: DeskBasedTestSectionGroup1Psv, - seatbelts: SeatbeltHiddenSection, - visit: ContingencyVisitSection, - notes: NotesSection, - defects: defectsHiddenSection, - customDefects: CustomDefectsHiddenSection, - reasonForCreation: reasonForCreationHiddenSection, - }, - testTypesDeskBasedGroup2: { - vehicle: DeskBasedVehicleSectionDefaultPsvHgv, - required: CreateRequiredSection, - test: DeskBasedTestSectionGroup2And5, - seatbelts: SeatbeltHiddenSection, - emissions: DeskBasedEmissionsSection, - visit: ContingencyVisitSection, - notes: NotesSection, - defects: defectsHiddenSection, - customDefects: CustomDefectsHiddenSection, - reasonForCreation: reasonForCreationHiddenSection, - }, - testTypesDeskBasedGroup3: { - required: CreateRequiredSection, - vehicle: DeskBasedVehicleSectionDefaultPsvHgv, - test: DeskBasedTestSectionGroup3, - seatbelts: SeatbeltHiddenSection, - visit: ContingencyVisitSection, - notes: NotesSection, - defects: defectsHiddenSection, - customDefects: CustomDefectsHiddenSection, - reasonForCreation: reasonForCreationHiddenSection, - }, - testTypesDeskBasedGroup4: { - required: CreateRequiredSection, - vehicle: DeskBasedVehicleSectionDefaultPsvHgv, - test: DeskBasedTestSectionGroup4Psv, - seatbelts: SeatbeltHiddenSection, - visit: ContingencyVisitSection, - notes: NotesSection, - defects: defectsHiddenSection, - customDefects: CustomDefectsHiddenSection, - reasonForCreation: reasonForCreationHiddenSection, - }, - }, - hgv: { - default: { - vehicle: ContingencyVehicleSectionDefaultPsvHgvLight, - test: TestSection, - visit: VisitSection, - notes: NotesSection, - defects: DefectsTpl, - customDefects: CustomDefectsSection, - required: CreateRequiredSectionHgvTrl, - }, - testTypesGroup3And4And8: { - vehicle: ContingencyVehicleSectionDefaultPsvHgvLight, - test: ContingencyTestSectionGroup3And4And8, - visit: ContingencyVisitSection, - notes: NotesSection, - customDefects: CustomDefectsSection, - defects: defectsHiddenSection, - reasonForCreation: reasonForCreationSection, - required: CreateRequiredSectionHgvTrl, - }, - testTypesGroup5And13: { - required: CreateRequiredSectionHgvTrl, - vehicle: ContingencyVehicleSectionDefaultPsvHgvLight, - test: ContingencyTestSectionGroup5And13, - visit: ContingencyVisitSection, - notes: NotesSection, - customDefects: CustomDefectsSection, - defects: defectsHiddenSection, - reasonForCreation: reasonForCreationSection, - }, - testTypesGroup6And11: { - required: CreateRequiredSectionHgvTrl, - vehicle: ContingencyVehicleSectionDefaultPsvHgvLight, - test: ContingencyTestSectionGroup6And11, - visit: ContingencyVisitSection, - notes: NotesSection, - defects: DefectsTpl, - customDefects: CustomDefectsHiddenSection, - reasonForCreation: reasonForCreationSection, - }, - testTypesGroup7: { - required: CreateRequiredSectionHgvTrl, - vehicle: ContingencyVehicleSectionDefaultPsvHgvLight, - test: ContingencyTestSectionGroup7, - visit: ContingencyVisitSection, - notes: NotesSection, - customDefects: CustomDefectsSection, - defects: defectsHiddenSection, - reasonForCreation: reasonForCreationSection, - }, - testTypesGroup9And10: { - required: CreateRequiredSectionHgvTrl, - vehicle: ContingencyVehicleSectionDefaultPsvHgvLight, - test: ContingencyTestSectionGroup9And10, - visit: ContingencyVisitSection, - notes: NotesSection, - defects: DefectsTpl, - customDefects: CustomDefectsHiddenSection, - reasonForCreation: reasonForCreationSection, - }, - testTypesGroup12And14: { - required: CreateRequiredSectionHgvTrl, - vehicle: ContingencyVehicleSectionDefaultPsvHgvLight, - test: ContingencyTestSectionGroup12and14, - visit: ContingencyVisitSection, - notes: NotesSection, - defects: DefectsTpl, - customDefects: CustomDefectsHiddenSection, - reasonForCreation: reasonForCreationSection, - }, - testTypesSpecialistGroup1: { - vehicle: ContingencyIvaMsvaVehicleSection, - test: ContingencyTestSectionSpecialistGroup1, - visit: ContingencyVisitSection, - notes: NotesSection, - requiredStandards: RequiredStandardsTpl, - defects: defectsHiddenSection, - customDefects: AdditionalDefectsSection, - reasonForCreation: reasonForCreationSection, - required: CreateRequiredSectionHgvTrl, - }, - testTypesSpecialistGroup1OldIVAorMSVA: { - vehicle: ContingencyVehicleSectionDefaultPsvHgvLight, - test: OldIVAContingencyTestSectionSpecialistGroup1, - visit: ContingencyVisitSection, - notes: NotesSection, - defects: defectsHiddenSection, - customDefects: CustomDefectsSection, - reasonForCreation: reasonForCreationSection, - required: CreateRequiredSectionHgvTrl, - }, - testTypesSpecialistGroup5: { - vehicle: ContingencyIvaMsvaVehicleSection, - test: ContingencyTestSectionSpecialistGroup5, - visit: ContingencyVisitSection, - notes: NotesSection, - requiredStandards: RequiredStandardsTpl, - defects: defectsHiddenSection, - customDefects: AdditionalDefectsSection, - reasonForCreation: reasonForCreationSection, - required: CreateRequiredSectionHgvTrl, - }, - testTypesSpecialistGroup5OldIVAorMSVA: { - vehicle: ContingencyVehicleSectionDefaultPsvHgvLight, - test: OldIVAContingencyTestSectionSpecialistGroup5, - visit: ContingencyVisitSection, - notes: NotesSection, - defects: defectsHiddenSection, - customDefects: CustomDefectsSection, - reasonForCreation: reasonForCreationSection, - required: CreateRequiredSectionHgvTrl, - }, - testTypesGroup15And16: { - required: CreateRequiredSectionHgvTrl, - vehicle: ContingencyVehicleSectionDefaultPsvHgvLight, - test: ContingencyTestSectionGroup15and16, - emissions: EmissionsSection, - visit: ContingencyVisitSection, - notes: NotesSection, - customDefects: CustomDefectsSection, - reasonForCreation: reasonForCreationSection, - }, - testTypesDeskBasedGroup1: { - required: CreateRequiredSectionHgvTrl, - vehicle: DeskBasedVehicleSectionHgvGroup1And2And4And5, - test: DeskBasedTestSectionGroup1And4And5HgvTrl, - visit: ContingencyVisitSection, - notes: NotesSection, - defects: defectsHiddenSection, - customDefects: CustomDefectsHiddenSection, - reasonForCreation: reasonForCreationHiddenSection, - }, - testTypesDeskBasedGroup2: { - required: CreateRequiredSectionHgvTrl, - vehicle: DeskBasedVehicleSectionHgvGroup1And2And4And5, - test: DeskBasedTestSectionGroup2And5, - emissions: DeskBasedEmissionsSection, - visit: ContingencyVisitSection, - notes: NotesSection, - defects: defectsHiddenSection, - customDefects: CustomDefectsHiddenSection, - reasonForCreation: reasonForCreationHiddenSection, - }, - testTypesDeskBasedGroup3: { - required: CreateRequiredSectionHgvTrl, - vehicle: DeskBasedVehicleSectionDefaultPsvHgv, - test: DeskBasedTestSectionGroup3, - visit: ContingencyVisitSection, - notes: NotesSection, - defects: defectsHiddenSection, - customDefects: CustomDefectsHiddenSection, - reasonForCreation: reasonForCreationHiddenSection, - }, - testTypesDeskBasedGroup4: { - required: CreateRequiredSectionHgvTrl, - vehicle: DeskBasedVehicleSectionHgvGroup1And2And4And5, - test: DeskBasedTestSectionGroup1And4And5HgvTrl, - visit: ContingencyVisitSection, - notes: NotesSection, - defects: defectsHiddenSection, - customDefects: CustomDefectsHiddenSection, - reasonForCreation: reasonForCreationHiddenSection, - }, - testTypesDeskBasedGroup5: { - required: CreateRequiredSectionHgvTrl, - vehicle: DeskBasedVehicleSectionHgvGroup1And2And4And5, - test: DeskBasedTestSectionGroup2And5, - visit: ContingencyVisitSection, - notes: NotesSection, - defects: defectsHiddenSection, - customDefects: CustomDefectsHiddenSection, - reasonForCreation: reasonForCreationHiddenSection, - }, - }, - trl: { - default: { - vehicle: ContingencyVehicleSectionDefaultTrl, - test: TestSection, - visit: VisitSection, - notes: NotesSection, - defects: DefectsTpl, - customDefects: CustomDefectsSection, - required: CreateRequiredSectionHgvTrl, - }, - testTypesGroup3And4And8: { - vehicle: ContingencyVehicleSectionDefaultTrl, - test: ContingencyTestSectionGroup3And4And8, - visit: ContingencyVisitSection, - notes: NotesSection, - customDefects: CustomDefectsSection, - defects: defectsHiddenSection, - reasonForCreation: reasonForCreationSection, - required: CreateRequiredSectionHgvTrl, - }, - testTypesGroup5And13: { - required: CreateRequiredSectionHgvTrl, - vehicle: ContingencyVehicleSectionDefaultTrl, - test: ContingencyTestSectionGroup5And13, - visit: ContingencyVisitSection, - notes: NotesSection, - customDefects: CustomDefectsSection, - defects: defectsHiddenSection, - reasonForCreation: reasonForCreationSection, - }, - testTypesGroup6And11: { - required: CreateRequiredSectionHgvTrl, - vehicle: ContingencyVehicleSectionDefaultTrl, - test: ContingencyTestSectionGroup6And11, - visit: ContingencyVisitSection, - notes: NotesSection, - defects: DefectsTpl, - customDefects: CustomDefectsHiddenSection, - reasonForCreation: reasonForCreationSection, - }, - testTypesGroup7: { - required: CreateRequiredSectionHgvTrl, - vehicle: ContingencyVehicleSectionDefaultTrl, - test: ContingencyTestSectionGroup7, - visit: ContingencyVisitSection, - notes: NotesSection, - customDefects: CustomDefectsSection, - defects: defectsHiddenSection, - reasonForCreation: reasonForCreationSection, - }, - testTypesGroup9And10: { - required: CreateRequiredSectionHgvTrl, - vehicle: ContingencyVehicleSectionDefaultTrl, - test: ContingencyTestSectionGroup9And10, - visit: ContingencyVisitSection, - notes: NotesSection, - defects: DefectsTpl, - customDefects: CustomDefectsHiddenSection, - reasonForCreation: reasonForCreationSection, - }, - testTypesGroup12And14: { - required: CreateRequiredSectionHgvTrl, - vehicle: ContingencyVehicleSectionDefaultTrl, - test: ContingencyTestSectionGroup12and14, - visit: ContingencyVisitSection, - notes: NotesSection, - defects: DefectsTpl, - customDefects: CustomDefectsHiddenSection, - reasonForCreation: reasonForCreationSection, - }, - testTypesSpecialistGroup1: { - vehicle: ContingencyVehicleSectionDefaultTrl, - test: ContingencyTestSectionSpecialistGroup1, - visit: ContingencyVisitSection, - notes: NotesSection, - requiredStandards: RequiredStandardsTpl, - defects: defectsHiddenSection, - customDefects: AdditionalDefectsSection, - reasonForCreation: reasonForCreationSection, - required: CreateRequiredSectionHgvTrl, - }, - testTypesSpecialistGroup1OldIVAorMSVA: { - vehicle: ContingencyVehicleSectionDefaultTrl, - test: OldIVAContingencyTestSectionSpecialistGroup1, - visit: ContingencyVisitSection, - notes: NotesSection, - defects: defectsHiddenSection, - customDefects: CustomDefectsSection, - reasonForCreation: reasonForCreationSection, - required: CreateRequiredSectionHgvTrl, - }, - testTypesSpecialistGroup5: { - vehicle: ContingencyVehicleSectionDefaultTrl, - test: ContingencyTestSectionSpecialistGroup5, - visit: ContingencyVisitSection, - notes: NotesSection, - requiredStandards: RequiredStandardsTpl, - defects: defectsHiddenSection, - customDefects: AdditionalDefectsSection, - reasonForCreation: reasonForCreationSection, - required: CreateRequiredSectionHgvTrl, - }, - testTypesSpecialistGroup5OldIVAorMSVA: { - vehicle: ContingencyVehicleSectionDefaultTrl, - test: OldIVAContingencyTestSectionSpecialistGroup5, - visit: ContingencyVisitSection, - notes: NotesSection, - defects: defectsHiddenSection, - customDefects: CustomDefectsSection, - reasonForCreation: reasonForCreationSection, - required: CreateRequiredSectionHgvTrl, - }, - testTypesDeskBasedGroup1: { - required: CreateRequiredSectionHgvTrl, - vehicle: DeskBasedVehicleSectionDefaultTrl, - test: DeskBasedTestSectionGroup1And4And5HgvTrl, - visit: ContingencyVisitSection, - notes: NotesSection, - defects: defectsHiddenSection, - customDefects: CustomDefectsHiddenSection, - reasonForCreation: reasonForCreationHiddenSection, - }, - testTypesDeskBasedGroup2: { - required: CreateRequiredSectionHgvTrl, - vehicle: DeskBasedVehicleSectionDefaultTrl, - test: DeskBasedTestSectionGroup2And5, - emissions: DeskBasedEmissionsSection, - visit: ContingencyVisitSection, - notes: NotesSection, - defects: defectsHiddenSection, - customDefects: CustomDefectsHiddenSection, - reasonForCreation: reasonForCreationHiddenSection, - }, - testTypesDeskBasedGroup3: { - required: CreateRequiredSectionHgvTrl, - vehicle: DeskBasedVehicleSectionDefaultTrl, - test: DeskBasedTestSectionGroup3, - visit: ContingencyVisitSection, - notes: NotesSection, - defects: defectsHiddenSection, - customDefects: CustomDefectsHiddenSection, - reasonForCreation: reasonForCreationHiddenSection, - }, - testTypesDeskBasedGroup4: { - required: CreateRequiredSectionHgvTrl, - vehicle: DeskBasedVehicleSectionDefaultTrl, - test: DeskBasedTestSectionGroup1And4And5HgvTrl, - visit: ContingencyVisitSection, - notes: NotesSection, - defects: defectsHiddenSection, - customDefects: CustomDefectsHiddenSection, - reasonForCreation: reasonForCreationHiddenSection, - }, - }, - lgv: { - default: { - required: CreateRequiredSectionLgvCar, - vehicle: ContingencyVehicleSectionDefaultPsvHgvLight, - test: TestSection, - defects: defectsHiddenSection, - visit: ContingencyVisitSection, - notes: NotesSection, - reasonForCreation: reasonForCreationSection, - }, - testTypesSpecialistGroup1: { - required: CreateRequiredSectionLgvCar, - vehicle: ContingencyIvaMsvaVehicleSection, - test: ContingencyTestSectionSpecialistGroup1, - requiredStandards: RequiredStandardsTpl, - customDefects: AdditionalDefectsSection, - visit: ContingencyVisitSection, - notes: NotesSection, - reasonForCreation: reasonForCreationSection, - }, - testTypesSpecialistGroup1OldIVAorMSVA: { - required: CreateRequiredSectionLgvCar, - vehicle: ContingencyVehicleSectionDefaultPsvHgvLight, - test: OldIVAContingencyTestSectionSpecialistGroup1, - customDefects: CustomDefectsSection, - visit: ContingencyVisitSection, - notes: NotesSection, - reasonForCreation: reasonForCreationSection, - }, - testTypesDeskBasedGroup4: { - required: DeskBasedRequiredHiddenSectionGroup4And5Lgv, - vehicle: DeskBasedVehicleSectionGroup4LGV, - test: DeskBasedTestSectionGroup4LgvCarMotorcycle, - visit: VisitSection, - notes: NotesSection, - customDefects: CustomDefectsHiddenSection, - reasonForCreation: reasonForCreationSection, - }, - testTypesSpecialistGroup5: { - required: CreateRequiredSectionLgvCar, - vehicle: ContingencyIvaMsvaVehicleSection, - test: ContingencyTestSectionSpecialistGroup5, - requiredStandards: RequiredStandardsTpl, - customDefects: AdditionalDefectsSection, - visit: ContingencyVisitSection, - notes: NotesSection, - reasonForCreation: reasonForCreationSection, - }, - testTypesSpecialistGroup5OldIVAorMSVA: { - required: CreateRequiredSectionLgvCar, - vehicle: ContingencyVehicleSectionDefaultPsvHgvLight, - test: OldIVAContingencyTestSectionSpecialistGroup5, - customDefects: CustomDefectsSection, - visit: ContingencyVisitSection, - notes: NotesSection, - reasonForCreation: reasonForCreationSection, - }, - testTypesDeskBasedGroup5: { - required: DeskBasedRequiredHiddenSectionGroup4And5Lgv, - vehicle: DeskBasedVehicleSectionGroup5Lgv, - test: DeskBasedTestSectionLgvGroup5, - visit: ContingencyVisitSection, - notes: NotesSection, - defects: defectsHiddenSection, - customDefects: CustomDefectsHiddenSection, - reasonForCreation: reasonForCreationSection, - }, - }, - car: { - default: { - required: CreateRequiredSectionLgvCar, - vehicle: ContingencyVehicleSectionDefaultPsvHgvLight, - test: TestSection, - defects: defectsHiddenSection, - visit: ContingencyVisitSection, - notes: NotesSection, - reasonForCreation: reasonForCreationSection, - }, - testTypesSpecialistGroup1: { - required: CreateRequiredSectionLgvCar, - vehicle: ContingencyIvaMsvaVehicleSection, - test: ContingencyTestSectionSpecialistGroup1, - requiredStandards: RequiredStandardsTpl, - customDefects: AdditionalDefectsSection, - visit: ContingencyVisitSection, - notes: NotesSection, - reasonForCreation: reasonForCreationSection, - }, - testTypesSpecialistGroup1OldIVAorMSVA: { - required: CreateRequiredSectionLgvCar, - vehicle: ContingencyVehicleSectionDefaultPsvHgvLight, - test: OldIVAContingencyTestSectionSpecialistGroup1, - customDefects: CustomDefectsSection, - visit: ContingencyVisitSection, - notes: NotesSection, - reasonForCreation: reasonForCreationSection, - }, - testTypesDeskBasedGroup4: { - required: DeskBasedRequiredHiddenSectionGroup4And5Lgv, - vehicle: DeskBasedVehicleSectionGroup4LGV, - test: DeskBasedTestSectionGroup4LgvCarMotorcycle, - visit: VisitSection, - notes: NotesSection, - customDefects: CustomDefectsHiddenSection, - reasonForCreation: reasonForCreationSection, - }, - testTypesSpecialistGroup5: { - required: CreateRequiredSectionLgvCar, - vehicle: ContingencyIvaMsvaVehicleSection, - test: ContingencyTestSectionSpecialistGroup5, - requiredStandards: RequiredStandardsTpl, - customDefects: AdditionalDefectsSection, - visit: ContingencyVisitSection, - notes: NotesSection, - reasonForCreation: reasonForCreationSection, - }, - testTypesSpecialistGroup5OldIVAorMSVA: { - required: CreateRequiredSectionLgvCar, - vehicle: ContingencyVehicleSectionDefaultPsvHgvLight, - test: OldIVAContingencyTestSectionSpecialistGroup5, - customDefects: CustomDefectsSection, - visit: ContingencyVisitSection, - notes: NotesSection, - reasonForCreation: reasonForCreationSection, - }, - }, - 'small trl': { - default: { - required: CreateRequiredSection, - vehicle: ContingencyVehicleSectionDefaultPsvHgvLight, - test: TestSection, - defects: defectsHiddenSection, - visit: ContingencyVisitSection, - notes: NotesSection, - reasonForCreation: reasonForCreationSection, - }, - testTypesDeskBasedGroup3: { - required: SpecialistRequiredSectionLgvCarSmallTrl, - vehicle: VehicleSectionGroup3, - test: SpecialistTestSectionGroup3, - customDefects: CustomDefectsHiddenSection, - visit: VisitSection, - notes: NotesSection, - reasonForCreation: reasonForCreationSection, - }, - }, - motorcycle: { - default: { - required: CreateRequiredSection, - vehicle: ContingencyVehicleSectionDefaultPsvHgvLight, - test: TestSection, - defects: defectsHiddenSection, - visit: ContingencyVisitSection, - notes: NotesSection, - reasonForCreation: reasonForCreationSection, - }, - testTypesSpecialistGroup1: { - required: CreateRequiredSectionMotorcycle, - vehicle: ContingencyIvaMsvaVehicleSection, - test: ContingencyTestSectionSpecialistGroup1, - requiredStandards: RequiredStandardsTpl, - customDefects: AdditionalDefectsSection, - visit: ContingencyVisitSection, - notes: NotesSection, - reasonForCreation: reasonForCreationSection, - }, - testTypesSpecialistGroup1OldIVAorMSVA: { - required: CreateRequiredSectionMotorcycle, - vehicle: ContingencyVehicleSectionDefaultPsvHgvLight, - test: OldIVAContingencyTestSectionSpecialistGroup1, - customDefects: CustomDefectsSection, - visit: ContingencyVisitSection, - notes: NotesSection, - reasonForCreation: reasonForCreationSection, - }, - testTypesDeskBasedGroup4: { - required: DeskBasedRequiredHiddenSectionGroup4Motorcyle, - vehicle: DeskBasedVehicleSectionGroup4LGV, - test: DeskBasedTestSectionGroup4LgvCarMotorcycle, - visit: VisitSection, - notes: NotesSection, - customDefects: CustomDefectsHiddenSection, - reasonForCreation: reasonForCreationSection, - }, - }, +export const contingencyTestTemplates: Record< + VehicleTypes, + Partial>> +> = { + psv: { + default: { + vehicle: ContingencyVehicleSectionDefaultPsvHgvLight, + test: TestSection, + seatbelts: SeatbeltSection, + emissions: EmissionsSection, + visit: ContingencyVisitSection, + notes: NotesSection, + defects: DefectsTpl, + customDefects: CustomDefectsSection, + required: CreateRequiredSection, + }, + testTypesGroup1: groups1and2Template, + testTypesGroup2: groups1and2Template, + testTypesGroup3And4And8: { + vehicle: ContingencyVehicleSectionDefaultPsvHgvLight, + test: ContingencyTestSectionGroup3And4And8, + visit: ContingencyVisitSection, + seatbelts: SeatbeltHiddenSection, + notes: NotesSection, + customDefects: CustomDefectsSection, + defects: defectsHiddenSection, + reasonForCreation: reasonForCreationSection, + required: CreateRequiredSection, + }, + testTypesGroup8Notifiable: { + vehicle: ContingencyVehicleSectionDefaultPsvHgvLight, + test: ContingencyTestSectionGroup8Notifiable, + visit: ContingencyVisitSection, + seatbelts: SeatbeltHiddenSection, + notes: NotesSection, + customDefects: CustomDefectsSection, + defects: defectsHiddenSection, + reasonForCreation: reasonForCreationSection, + required: CreateRequiredSection, + }, + testTypesGroup15And16: { + required: CreateRequiredSection, + vehicle: ContingencyVehicleSectionDefaultPsvHgvLight, + test: ContingencyTestSectionGroup15and16, + emissions: EmissionsSection, + seatbelts: SeatbeltHiddenSection, + visit: ContingencyVisitSection, + notes: NotesSection, + customDefects: CustomDefectsSection, + reasonForCreation: reasonForCreationSection, + }, + testTypesSpecialistGroup1: { + vehicle: ContingencyIvaMsvaVehicleSection, + test: ContingencyTestSectionSpecialistGroup1, + seatbelts: SeatbeltHiddenSection, + visit: ContingencyVisitSection, + notes: NotesSection, + requiredStandards: RequiredStandardsTpl, + defects: defectsHiddenSection, + customDefects: AdditionalDefectsSection, + reasonForCreation: reasonForCreationSection, + required: CreateRequiredSection, + }, + testTypesSpecialistGroup1OldIVAorMSVA: { + vehicle: ContingencyVehicleSectionDefaultPsvHgvLight, + test: OldIVAContingencyTestSectionSpecialistGroup1, + seatbelts: SeatbeltHiddenSection, + visit: ContingencyVisitSection, + notes: NotesSection, + customDefects: CustomDefectsSection, + defects: defectsHiddenSection, + reasonForCreation: reasonForCreationSection, + required: CreateRequiredSection, + }, + testTypesSpecialistGroup2: { + vehicle: ContingencyVehicleSectionDefaultPsvHgvLight, + test: ContingencyTestSectionSpecialistGroup2, + seatbelts: SeatbeltSection, + visit: ContingencyVisitSection, + notes: NotesSection, + customDefects: CustomDefectsSection, + defects: defectsHiddenSection, + reasonForCreation: reasonForCreationSection, + required: CreateRequiredSection, + }, + testTypesSpecialistGroup3: { + vehicle: ContingencyVehicleSectionDefaultPsvHgvLight, + test: ContingencyTestSectionSpecialistGroup3And4, + seatbelts: SeatbeltHiddenSection, + visit: ContingencyVisitSection, + notes: NotesSection, + customDefects: CustomDefectsSection, + defects: defectsHiddenSection, + reasonForCreation: reasonForCreationSection, + required: CreateRequiredSection, + }, + testTypesSpecialistGroup4: { + vehicle: ContingencyVehicleSectionDefaultPsvHgvLight, + test: ContingencyTestSectionSpecialistGroup3And4, + seatbelts: SeatbeltSection, + visit: ContingencyVisitSection, + notes: NotesSection, + customDefects: CustomDefectsSection, + defects: defectsHiddenSection, + reasonForCreation: reasonForCreationSection, + required: CreateRequiredSection, + }, + testTypesSpecialistGroup5: { + vehicle: ContingencyIvaMsvaVehicleSection, + test: ContingencyTestSectionSpecialistGroup5, + seatbelts: SeatbeltHiddenSection, + visit: ContingencyVisitSection, + notes: NotesSection, + requiredStandards: RequiredStandardsTpl, + customDefects: AdditionalDefectsSection, + defects: defectsHiddenSection, + reasonForCreation: reasonForCreationSection, + required: CreateRequiredSection, + }, + testTypesSpecialistGroup5OldIVAorMSVA: { + vehicle: ContingencyVehicleSectionDefaultPsvHgvLight, + test: OldIVAContingencyTestSectionSpecialistGroup5, + seatbelts: SeatbeltHiddenSection, + visit: ContingencyVisitSection, + notes: NotesSection, + customDefects: CustomDefectsSection, + defects: defectsHiddenSection, + reasonForCreation: reasonForCreationSection, + required: CreateRequiredSection, + }, + testTypesDeskBasedGroup1: { + required: CreateRequiredSection, + vehicle: DeskBasedVehicleSectionDefaultPsvHgv, + test: DeskBasedTestSectionGroup1Psv, + seatbelts: SeatbeltHiddenSection, + visit: ContingencyVisitSection, + notes: NotesSection, + defects: defectsHiddenSection, + customDefects: CustomDefectsHiddenSection, + reasonForCreation: reasonForCreationHiddenSection, + }, + testTypesDeskBasedGroup2: { + vehicle: DeskBasedVehicleSectionDefaultPsvHgv, + required: CreateRequiredSection, + test: DeskBasedTestSectionGroup2And5, + seatbelts: SeatbeltHiddenSection, + emissions: DeskBasedEmissionsSection, + visit: ContingencyVisitSection, + notes: NotesSection, + defects: defectsHiddenSection, + customDefects: CustomDefectsHiddenSection, + reasonForCreation: reasonForCreationHiddenSection, + }, + testTypesDeskBasedGroup3: { + required: CreateRequiredSection, + vehicle: DeskBasedVehicleSectionDefaultPsvHgv, + test: DeskBasedTestSectionGroup3, + seatbelts: SeatbeltHiddenSection, + visit: ContingencyVisitSection, + notes: NotesSection, + defects: defectsHiddenSection, + customDefects: CustomDefectsHiddenSection, + reasonForCreation: reasonForCreationHiddenSection, + }, + testTypesDeskBasedGroup4: { + required: CreateRequiredSection, + vehicle: DeskBasedVehicleSectionDefaultPsvHgv, + test: DeskBasedTestSectionGroup4Psv, + seatbelts: SeatbeltHiddenSection, + visit: ContingencyVisitSection, + notes: NotesSection, + defects: defectsHiddenSection, + customDefects: CustomDefectsHiddenSection, + reasonForCreation: reasonForCreationHiddenSection, + }, + }, + hgv: { + default: { + vehicle: ContingencyVehicleSectionDefaultPsvHgvLight, + test: TestSection, + visit: VisitSection, + notes: NotesSection, + defects: DefectsTpl, + customDefects: CustomDefectsSection, + required: CreateRequiredSectionHgvTrl, + }, + testTypesGroup3And4And8: { + vehicle: ContingencyVehicleSectionDefaultPsvHgvLight, + test: ContingencyTestSectionGroup3And4And8, + visit: ContingencyVisitSection, + notes: NotesSection, + customDefects: CustomDefectsSection, + defects: defectsHiddenSection, + reasonForCreation: reasonForCreationSection, + required: CreateRequiredSectionHgvTrl, + }, + testTypesGroup8Notifiable: { + vehicle: ContingencyVehicleSectionDefaultPsvHgvLight, + test: ContingencyTestSectionGroup8Notifiable, + visit: ContingencyVisitSection, + notes: NotesSection, + customDefects: CustomDefectsSection, + defects: defectsHiddenSection, + reasonForCreation: reasonForCreationSection, + required: CreateRequiredSectionHgvTrl, + }, + testTypesGroup5And13: { + required: CreateRequiredSectionHgvTrl, + vehicle: ContingencyVehicleSectionDefaultPsvHgvLight, + test: ContingencyTestSectionGroup5And13, + visit: ContingencyVisitSection, + notes: NotesSection, + customDefects: CustomDefectsSection, + defects: defectsHiddenSection, + reasonForCreation: reasonForCreationSection, + }, + testTypesGroup6And11: { + required: CreateRequiredSectionHgvTrl, + vehicle: ContingencyVehicleSectionDefaultPsvHgvLight, + test: ContingencyTestSectionGroup6And11, + visit: ContingencyVisitSection, + notes: NotesSection, + defects: DefectsTpl, + customDefects: CustomDefectsHiddenSection, + reasonForCreation: reasonForCreationSection, + }, + testTypesGroup7: { + required: CreateRequiredSectionHgvTrl, + vehicle: ContingencyVehicleSectionDefaultPsvHgvLight, + test: ContingencyTestSectionGroup7, + visit: ContingencyVisitSection, + notes: AdrNotesSection, + customDefects: CustomDefectsSection, + defects: defectsHiddenSection, + reasonForCreation: reasonForCreationSection, + }, + testTypesGroup9And10: { + required: CreateRequiredSectionHgvTrl, + vehicle: ContingencyVehicleSectionDefaultPsvHgvLight, + test: ContingencyTestSectionGroup9And10, + visit: ContingencyVisitSection, + notes: NotesSection, + defects: DefectsTpl, + customDefects: CustomDefectsHiddenSection, + reasonForCreation: reasonForCreationSection, + }, + testTypesGroup9And10CentralDocs: { + required: CreateRequiredSectionHgvTrl, + vehicle: ContingencyVehicleSectionDefaultPsvHgvLight, + test: ContingencyTestSectionGroup9And10CentralDocs, + visit: ContingencyVisitSection, + notes: NotesSection, + defects: DefectsTpl, + customDefects: CustomDefectsHiddenSection, + reasonForCreation: reasonForCreationSection, + }, + testTypesGroup12And14: { + required: CreateRequiredSectionHgvTrl, + vehicle: ContingencyVehicleSectionDefaultPsvHgvLight, + test: ContingencyTestSectionGroup12and14, + visit: ContingencyVisitSection, + notes: NotesSection, + defects: DefectsTpl, + customDefects: CustomDefectsHiddenSection, + reasonForCreation: reasonForCreationSection, + }, + testTypesSpecialistGroup1: { + vehicle: ContingencyIvaMsvaVehicleSection, + test: ContingencyTestSectionSpecialistGroup1, + visit: ContingencyVisitSection, + notes: NotesSection, + requiredStandards: RequiredStandardsTpl, + defects: defectsHiddenSection, + customDefects: AdditionalDefectsSection, + reasonForCreation: reasonForCreationSection, + required: CreateRequiredSectionHgvTrl, + }, + testTypesSpecialistGroup1OldIVAorMSVA: { + vehicle: ContingencyVehicleSectionDefaultPsvHgvLight, + test: OldIVAContingencyTestSectionSpecialistGroup1, + visit: ContingencyVisitSection, + notes: NotesSection, + defects: defectsHiddenSection, + customDefects: CustomDefectsSection, + reasonForCreation: reasonForCreationSection, + required: CreateRequiredSectionHgvTrl, + }, + testTypesSpecialistGroup5: { + vehicle: ContingencyIvaMsvaVehicleSection, + test: ContingencyTestSectionSpecialistGroup5, + visit: ContingencyVisitSection, + notes: NotesSection, + requiredStandards: RequiredStandardsTpl, + defects: defectsHiddenSection, + customDefects: AdditionalDefectsSection, + reasonForCreation: reasonForCreationSection, + required: CreateRequiredSectionHgvTrl, + }, + testTypesSpecialistGroup5OldIVAorMSVA: { + vehicle: ContingencyVehicleSectionDefaultPsvHgvLight, + test: OldIVAContingencyTestSectionSpecialistGroup5, + visit: ContingencyVisitSection, + notes: NotesSection, + defects: defectsHiddenSection, + customDefects: CustomDefectsSection, + reasonForCreation: reasonForCreationSection, + required: CreateRequiredSectionHgvTrl, + }, + testTypesGroup15And16: { + required: CreateRequiredSectionHgvTrl, + vehicle: ContingencyVehicleSectionDefaultPsvHgvLight, + test: ContingencyTestSectionGroup15and16, + emissions: EmissionsSection, + visit: ContingencyVisitSection, + notes: NotesSection, + customDefects: CustomDefectsSection, + reasonForCreation: reasonForCreationSection, + }, + testTypesDeskBasedGroup1: { + required: CreateRequiredSectionHgvTrl, + vehicle: DeskBasedVehicleSectionHgvGroup1And2And4And5, + test: DeskBasedTestSectionGroup1And4And5HgvTrl, + visit: ContingencyVisitSection, + notes: NotesSection, + defects: defectsHiddenSection, + customDefects: CustomDefectsHiddenSection, + reasonForCreation: reasonForCreationHiddenSection, + }, + testTypesDeskBasedGroup2: { + required: CreateRequiredSectionHgvTrl, + vehicle: DeskBasedVehicleSectionHgvGroup1And2And4And5, + test: DeskBasedTestSectionGroup2And5, + emissions: DeskBasedEmissionsSection, + visit: ContingencyVisitSection, + notes: NotesSection, + defects: defectsHiddenSection, + customDefects: CustomDefectsHiddenSection, + reasonForCreation: reasonForCreationHiddenSection, + }, + testTypesDeskBasedGroup3: { + required: CreateRequiredSectionHgvTrl, + vehicle: DeskBasedVehicleSectionDefaultPsvHgv, + test: DeskBasedTestSectionGroup3, + visit: ContingencyVisitSection, + notes: NotesSection, + defects: defectsHiddenSection, + customDefects: CustomDefectsHiddenSection, + reasonForCreation: reasonForCreationHiddenSection, + }, + testTypesDeskBasedGroup4: { + required: CreateRequiredSectionHgvTrl, + vehicle: DeskBasedVehicleSectionHgvGroup1And2And4And5, + test: DeskBasedTestSectionGroup1And4And5HgvTrl, + visit: ContingencyVisitSection, + notes: NotesSection, + defects: defectsHiddenSection, + customDefects: CustomDefectsHiddenSection, + reasonForCreation: reasonForCreationHiddenSection, + }, + testTypesDeskBasedGroup5: { + required: CreateRequiredSectionHgvTrl, + vehicle: DeskBasedVehicleSectionHgvGroup1And2And4And5, + test: DeskBasedTestSectionGroup2And5, + visit: ContingencyVisitSection, + notes: NotesSection, + defects: defectsHiddenSection, + customDefects: CustomDefectsHiddenSection, + reasonForCreation: reasonForCreationHiddenSection, + }, + }, + trl: { + default: { + vehicle: ContingencyVehicleSectionDefaultTrl, + test: TestSection, + visit: VisitSection, + notes: NotesSection, + defects: DefectsTpl, + customDefects: CustomDefectsSection, + required: CreateRequiredSectionHgvTrl, + }, + testTypesGroup3And4And8: { + vehicle: ContingencyVehicleSectionDefaultTrl, + test: ContingencyTestSectionGroup3And4And8, + visit: ContingencyVisitSection, + notes: NotesSection, + customDefects: CustomDefectsSection, + defects: defectsHiddenSection, + reasonForCreation: reasonForCreationSection, + required: CreateRequiredSectionHgvTrl, + }, + testTypesGroup8Notifiable: { + vehicle: ContingencyVehicleSectionDefaultTrl, + test: ContingencyTestSectionGroup8Notifiable, + visit: ContingencyVisitSection, + notes: NotesSection, + customDefects: CustomDefectsSection, + defects: defectsHiddenSection, + reasonForCreation: reasonForCreationSection, + required: CreateRequiredSectionHgvTrl, + }, + testTypesGroup5And13: { + required: CreateRequiredSectionHgvTrl, + vehicle: ContingencyVehicleSectionDefaultTrl, + test: ContingencyTestSectionGroup5And13, + visit: ContingencyVisitSection, + notes: NotesSection, + customDefects: CustomDefectsSection, + defects: defectsHiddenSection, + reasonForCreation: reasonForCreationSection, + }, + testTypesGroup6And11: { + required: CreateRequiredSectionHgvTrl, + vehicle: ContingencyVehicleSectionDefaultTrl, + test: ContingencyTestSectionGroup6And11, + visit: ContingencyVisitSection, + notes: NotesSection, + defects: DefectsTpl, + customDefects: CustomDefectsHiddenSection, + reasonForCreation: reasonForCreationSection, + }, + testTypesGroup7: { + required: CreateRequiredSectionHgvTrl, + vehicle: ContingencyVehicleSectionDefaultTrl, + test: ContingencyTestSectionGroup7, + visit: ContingencyVisitSection, + notes: AdrNotesSection, + customDefects: CustomDefectsSection, + defects: defectsHiddenSection, + reasonForCreation: reasonForCreationSection, + }, + testTypesGroup9And10: { + required: CreateRequiredSectionHgvTrl, + vehicle: ContingencyVehicleSectionDefaultTrl, + test: ContingencyTestSectionGroup9And10, + visit: ContingencyVisitSection, + notes: NotesSection, + defects: DefectsTpl, + customDefects: CustomDefectsHiddenSection, + reasonForCreation: reasonForCreationSection, + }, + testTypesGroup9And10CentralDocs: { + required: CreateRequiredSectionHgvTrl, + vehicle: ContingencyVehicleSectionDefaultTrl, + test: ContingencyTestSectionGroup9And10CentralDocs, + visit: ContingencyVisitSection, + notes: NotesSection, + defects: DefectsTpl, + customDefects: CustomDefectsHiddenSection, + reasonForCreation: reasonForCreationSection, + }, + testTypesGroup12And14: { + required: CreateRequiredSectionHgvTrl, + vehicle: ContingencyVehicleSectionDefaultTrl, + test: ContingencyTestSectionGroup12and14, + visit: ContingencyVisitSection, + notes: NotesSection, + defects: DefectsTpl, + customDefects: CustomDefectsHiddenSection, + reasonForCreation: reasonForCreationSection, + }, + testTypesSpecialistGroup1: { + vehicle: ContingencyVehicleSectionDefaultTrl, + test: ContingencyTestSectionSpecialistGroup1, + visit: ContingencyVisitSection, + notes: NotesSection, + requiredStandards: RequiredStandardsTpl, + defects: defectsHiddenSection, + customDefects: AdditionalDefectsSection, + reasonForCreation: reasonForCreationSection, + required: CreateRequiredSectionHgvTrl, + }, + testTypesSpecialistGroup1OldIVAorMSVA: { + vehicle: ContingencyVehicleSectionDefaultTrl, + test: OldIVAContingencyTestSectionSpecialistGroup1, + visit: ContingencyVisitSection, + notes: NotesSection, + defects: defectsHiddenSection, + customDefects: CustomDefectsSection, + reasonForCreation: reasonForCreationSection, + required: CreateRequiredSectionHgvTrl, + }, + testTypesSpecialistGroup5: { + vehicle: ContingencyVehicleSectionDefaultTrl, + test: ContingencyTestSectionSpecialistGroup5, + visit: ContingencyVisitSection, + notes: NotesSection, + requiredStandards: RequiredStandardsTpl, + defects: defectsHiddenSection, + customDefects: AdditionalDefectsSection, + reasonForCreation: reasonForCreationSection, + required: CreateRequiredSectionHgvTrl, + }, + testTypesSpecialistGroup5OldIVAorMSVA: { + vehicle: ContingencyVehicleSectionDefaultTrl, + test: OldIVAContingencyTestSectionSpecialistGroup5, + visit: ContingencyVisitSection, + notes: NotesSection, + defects: defectsHiddenSection, + customDefects: CustomDefectsSection, + reasonForCreation: reasonForCreationSection, + required: CreateRequiredSectionHgvTrl, + }, + testTypesDeskBasedGroup1: { + required: CreateRequiredSectionHgvTrl, + vehicle: DeskBasedVehicleSectionDefaultTrl, + test: DeskBasedTestSectionGroup1And4And5HgvTrl, + visit: ContingencyVisitSection, + notes: NotesSection, + defects: defectsHiddenSection, + customDefects: CustomDefectsHiddenSection, + reasonForCreation: reasonForCreationHiddenSection, + }, + testTypesDeskBasedGroup2: { + required: CreateRequiredSectionHgvTrl, + vehicle: DeskBasedVehicleSectionDefaultTrl, + test: DeskBasedTestSectionGroup2And5, + emissions: DeskBasedEmissionsSection, + visit: ContingencyVisitSection, + notes: NotesSection, + defects: defectsHiddenSection, + customDefects: CustomDefectsHiddenSection, + reasonForCreation: reasonForCreationHiddenSection, + }, + testTypesDeskBasedGroup3: { + required: CreateRequiredSectionHgvTrl, + vehicle: DeskBasedVehicleSectionDefaultTrl, + test: DeskBasedTestSectionGroup3, + visit: ContingencyVisitSection, + notes: NotesSection, + defects: defectsHiddenSection, + customDefects: CustomDefectsHiddenSection, + reasonForCreation: reasonForCreationHiddenSection, + }, + testTypesDeskBasedGroup4: { + required: CreateRequiredSectionHgvTrl, + vehicle: DeskBasedVehicleSectionDefaultTrl, + test: DeskBasedTestSectionGroup1And4And5HgvTrl, + visit: ContingencyVisitSection, + notes: NotesSection, + defects: defectsHiddenSection, + customDefects: CustomDefectsHiddenSection, + reasonForCreation: reasonForCreationHiddenSection, + }, + }, + lgv: { + default: { + required: CreateRequiredSectionLgvCar, + vehicle: ContingencyVehicleSectionDefaultPsvHgvLight, + test: TestSection, + defects: defectsHiddenSection, + visit: ContingencyVisitSection, + notes: NotesSection, + reasonForCreation: reasonForCreationSection, + }, + testTypesSpecialistGroup1: { + required: CreateRequiredSectionLgvCar, + vehicle: ContingencyIvaMsvaVehicleSection, + test: ContingencyTestSectionSpecialistGroup1, + requiredStandards: RequiredStandardsTpl, + customDefects: AdditionalDefectsSection, + visit: ContingencyVisitSection, + notes: NotesSection, + reasonForCreation: reasonForCreationSection, + }, + testTypesSpecialistGroup1OldIVAorMSVA: { + required: CreateRequiredSectionLgvCar, + vehicle: ContingencyVehicleSectionDefaultPsvHgvLight, + test: OldIVAContingencyTestSectionSpecialistGroup1, + customDefects: CustomDefectsSection, + visit: ContingencyVisitSection, + notes: NotesSection, + reasonForCreation: reasonForCreationSection, + }, + testTypesDeskBasedGroup4: { + required: DeskBasedRequiredHiddenSectionGroup4And5Lgv, + vehicle: DeskBasedVehicleSectionGroup4LGV, + test: DeskBasedTestSectionGroup4LgvCarMotorcycle, + visit: VisitSection, + notes: NotesSection, + customDefects: CustomDefectsHiddenSection, + reasonForCreation: reasonForCreationSection, + }, + testTypesSpecialistGroup5: { + required: CreateRequiredSectionLgvCar, + vehicle: ContingencyIvaMsvaVehicleSection, + test: ContingencyTestSectionSpecialistGroup5, + requiredStandards: RequiredStandardsTpl, + customDefects: AdditionalDefectsSection, + visit: ContingencyVisitSection, + notes: NotesSection, + reasonForCreation: reasonForCreationSection, + }, + testTypesSpecialistGroup5OldIVAorMSVA: { + required: CreateRequiredSectionLgvCar, + vehicle: ContingencyVehicleSectionDefaultPsvHgvLight, + test: OldIVAContingencyTestSectionSpecialistGroup5, + customDefects: CustomDefectsSection, + visit: ContingencyVisitSection, + notes: NotesSection, + reasonForCreation: reasonForCreationSection, + }, + testTypesDeskBasedGroup5: { + required: DeskBasedRequiredHiddenSectionGroup4And5Lgv, + vehicle: DeskBasedVehicleSectionGroup5Lgv, + test: DeskBasedTestSectionLgvGroup5, + visit: ContingencyVisitSection, + notes: NotesSection, + defects: defectsHiddenSection, + customDefects: CustomDefectsHiddenSection, + reasonForCreation: reasonForCreationSection, + }, + }, + car: { + default: { + required: CreateRequiredSectionLgvCar, + vehicle: ContingencyVehicleSectionDefaultPsvHgvLight, + test: TestSection, + defects: defectsHiddenSection, + visit: ContingencyVisitSection, + notes: NotesSection, + reasonForCreation: reasonForCreationSection, + }, + testTypesSpecialistGroup1: { + required: CreateRequiredSectionLgvCar, + vehicle: ContingencyIvaMsvaVehicleSection, + test: ContingencyTestSectionSpecialistGroup1, + requiredStandards: RequiredStandardsTpl, + customDefects: AdditionalDefectsSection, + visit: ContingencyVisitSection, + notes: NotesSection, + reasonForCreation: reasonForCreationSection, + }, + testTypesSpecialistGroup1OldIVAorMSVA: { + required: CreateRequiredSectionLgvCar, + vehicle: ContingencyVehicleSectionDefaultPsvHgvLight, + test: OldIVAContingencyTestSectionSpecialistGroup1, + customDefects: CustomDefectsSection, + visit: ContingencyVisitSection, + notes: NotesSection, + reasonForCreation: reasonForCreationSection, + }, + testTypesDeskBasedGroup4: { + required: DeskBasedRequiredHiddenSectionGroup4And5Lgv, + vehicle: DeskBasedVehicleSectionGroup4LGV, + test: DeskBasedTestSectionGroup4LgvCarMotorcycle, + visit: VisitSection, + notes: NotesSection, + customDefects: CustomDefectsHiddenSection, + reasonForCreation: reasonForCreationSection, + }, + testTypesSpecialistGroup5: { + required: CreateRequiredSectionLgvCar, + vehicle: ContingencyIvaMsvaVehicleSection, + test: ContingencyTestSectionSpecialistGroup5, + requiredStandards: RequiredStandardsTpl, + customDefects: AdditionalDefectsSection, + visit: ContingencyVisitSection, + notes: NotesSection, + reasonForCreation: reasonForCreationSection, + }, + testTypesSpecialistGroup5OldIVAorMSVA: { + required: CreateRequiredSectionLgvCar, + vehicle: ContingencyVehicleSectionDefaultPsvHgvLight, + test: OldIVAContingencyTestSectionSpecialistGroup5, + customDefects: CustomDefectsSection, + visit: ContingencyVisitSection, + notes: NotesSection, + reasonForCreation: reasonForCreationSection, + }, + }, + 'small trl': { + default: { + required: CreateRequiredSection, + vehicle: ContingencyVehicleSectionDefaultPsvHgvLight, + test: TestSection, + defects: defectsHiddenSection, + visit: ContingencyVisitSection, + notes: NotesSection, + reasonForCreation: reasonForCreationSection, + }, + testTypesDeskBasedGroup3: { + required: SpecialistRequiredSectionLgvCarSmallTrl, + vehicle: VehicleSectionGroup3, + test: SpecialistTestSectionGroup3, + customDefects: CustomDefectsHiddenSection, + visit: VisitSection, + notes: NotesSection, + reasonForCreation: reasonForCreationSection, + }, + }, + motorcycle: { + default: { + required: CreateRequiredSection, + vehicle: ContingencyVehicleSectionDefaultPsvHgvLight, + test: TestSection, + defects: defectsHiddenSection, + visit: ContingencyVisitSection, + notes: NotesSection, + reasonForCreation: reasonForCreationSection, + }, + testTypesSpecialistGroup1: { + required: CreateRequiredSectionMotorcycle, + vehicle: ContingencyIvaMsvaVehicleSection, + test: ContingencyTestSectionSpecialistGroup1, + requiredStandards: RequiredStandardsTpl, + customDefects: AdditionalDefectsSection, + visit: ContingencyVisitSection, + notes: NotesSection, + reasonForCreation: reasonForCreationSection, + }, + testTypesSpecialistGroup1OldIVAorMSVA: { + required: CreateRequiredSectionMotorcycle, + vehicle: ContingencyVehicleSectionDefaultPsvHgvLight, + test: OldIVAContingencyTestSectionSpecialistGroup1, + customDefects: CustomDefectsSection, + visit: ContingencyVisitSection, + notes: NotesSection, + reasonForCreation: reasonForCreationSection, + }, + testTypesDeskBasedGroup4: { + required: DeskBasedRequiredHiddenSectionGroup4Motorcyle, + vehicle: DeskBasedVehicleSectionGroup4LGV, + test: DeskBasedTestSectionGroup4LgvCarMotorcycle, + visit: VisitSection, + notes: NotesSection, + customDefects: CustomDefectsHiddenSection, + reasonForCreation: reasonForCreationSection, + }, + }, }; diff --git a/src/app/forms/templates/test-records/master.template.ts b/src/app/forms/templates/test-records/master.template.ts index ebe675c807..d137a86173 100644 --- a/src/app/forms/templates/test-records/master.template.ts +++ b/src/app/forms/templates/test-records/master.template.ts @@ -2,15 +2,20 @@ import { TEST_TYPES } from '@forms/models/testTypeId.enum'; import { FormNode } from '@forms/services/dynamic-form.types'; import { VehicleTypes } from '@models/vehicle-tech-record.model'; import { DefectsTpl } from '../general/defect.template'; +import { RequiredStandardsTpl } from '../general/required-standards.template'; +import { AdditionalDefectsSection } from './section-templates/additionalDefects/additional-defects-section.template'; import { CustomDefectsSection } from './section-templates/customDefects/custom-defects-section.template'; import { EmissionsSection } from './section-templates/emissions/emissions-section.template'; +import { AdrNotesSection } from './section-templates/notes/adr-notes-section.template'; import { NotesSection } from './section-templates/notes/notes-section.template'; import { reasonForCreationSection } from './section-templates/reasonForCreation/reasonForCreation.template'; import { CustomDefectsHiddenSection } from './section-templates/required/custom-defects-hidden-section.template'; import { defectsHiddenSection } from './section-templates/required/defect-hidden-section.template'; +import { AmendDeskBasedRequiredHiddenSectionGroup4Motorcyle } from './section-templates/required/desk-based-required-hidden-group4-motorcycle-amend.template'; import { DeskBasedRequiredHiddenSectionGroup5Lgv } from './section-templates/required/desk-based-required-hidden-group5-lgv.template'; import { DeskBasedRequiredSectionHgvTrl } from './section-templates/required/desk-based-required-hidden-section-hgv-trl.template'; import { DeskBasedRequiredSectionPsv } from './section-templates/required/desk-based-required-hidden-section-psv.template'; +import { AmendDeskBasedRequiredHiddenSectionGroup4 } from './section-templates/required/desk-based-required-section-group4-amend.template'; import { RequiredSectionHGVTRL } from './section-templates/required/required-hidden-section-hgv-trl.template'; import { RequiredSection } from './section-templates/required/required-hidden-section.template'; import { SpecialistRequiredSectionHGVTRL } from './section-templates/required/specialist-required-hidden-section-hgv-trl.template'; @@ -23,692 +28,736 @@ import { amendDeskBasedTestSectionGroup1And4HgvTrl } from './section-templates/t import { AmendDeskBasedTestSectionGroup2And5 } from './section-templates/test/desk-based/desk-based-test-section-group2.template'; import { AmendDeskBasedTestSectionGroup3 } from './section-templates/test/desk-based/desk-based-test-section-group3.template'; import { amendDeskBasedTestSectionGroup4Psv } from './section-templates/test/desk-based/desk-based-test-section-group4-PSV.template'; +import { AmendDeskBasedTestSectionGroup4LgvCarMotorcycle } from './section-templates/test/desk-based/desk-based-test-section-group4-lgv-template'; +import { AmendDeskBasedTestSectionLgvGroup5 } from './section-templates/test/desk-based/desk-based-test-section-group5-LGV'; +import { OldIVASpecialistTestSectionGroup1 } from './section-templates/test/specialist/old-specialist-test-section-group1.template'; +import { OldIVASpecialistTestSectionGroup5 } from './section-templates/test/specialist/old-specialist-test-section-group5.template'; import { SpecialistTestSectionGroup1 } from './section-templates/test/specialist/specialist-test-section-group1.template'; import { SpecialistTestSectionGroup2 } from './section-templates/test/specialist/specialist-test-section-group2.template'; import { SpecialistTestSectionGroup3And4 } from './section-templates/test/specialist/specialist-test-section-group3And4.template'; import { SpecialistTestSectionGroup5 } from './section-templates/test/specialist/specialist-test-section-group5.template'; import { TestSectionGroup1 } from './section-templates/test/test-section-group1.template'; -import { TestSectionGroup12And14 } from './section-templates/test/test-section-group12And14.template'; -import { TestSectionGroup15And16 } from './section-templates/test/test-section-group15And16.template'; import { TestSectionGroup2 } from './section-templates/test/test-section-group2.template'; import { TestSectionGroup3And4And8 } from './section-templates/test/test-section-group3And4And8.template'; import { TestSectionGroup5And13 } from './section-templates/test/test-section-group5And13.template'; import { TestSectionGroup6And11 } from './section-templates/test/test-section-group6And11.template'; import { TestSectionGroup7 } from './section-templates/test/test-section-group7.template'; +import { TestSectionGroup8Notifiable } from './section-templates/test/test-section-group8Notifiable.template'; import { TestSectionGroup9And10 } from './section-templates/test/test-section-group9And10.template'; +import { TestSectionGroup9And10CentralDocs } from './section-templates/test/test-section-group9And10CentralDocs.template'; +import { TestSectionGroup12And14 } from './section-templates/test/test-section-group12And14.template'; +import { TestSectionGroup15And16 } from './section-templates/test/test-section-group15And16.template'; import { TestSection } from './section-templates/test/test-section.template'; +import { IvaMsvaVehicleSection } from './section-templates/vehicle/amend-iva-msva-psv-hgv-light.template'; import { VehicleSectionDefaultPsvHgvLight } from './section-templates/vehicle/default-psv-hgv-light-vehicle-section.template'; import { VehicleSectionDefaultTrl } from './section-templates/vehicle/default-trl-vehicle-section.template'; import { DeskBasedVehicleSectionDefaultPsvHgv } from './section-templates/vehicle/desk-based-default-psv-hgv-vehicle-section.template'; import { DeskBasedVehicleSectionDefaultTrl } from './section-templates/vehicle/desk-based-default-trl-vehicle-section.template'; -import { - DeskBasedVehicleSectionHgvGroup1And2And4 as DeskBasedVehicleSectionHgvGroup1And2And4And5, -} from './section-templates/vehicle/desk-based-test-hgv-vehicle-section-group1And2And4.template'; +import { DeskBasedVehicleSectionHgvGroup1And2And4 as DeskBasedVehicleSectionHgvGroup1And2And4And5 } from './section-templates/vehicle/desk-based-test-hgv-vehicle-section-group1And2And4.template'; import { DeskBasedVehicleSectionGroup4LGV } from './section-templates/vehicle/desk-based-vehicle-section-group4-lgv.template'; -import { VisitSection } from './section-templates/visit/visit-section.template'; import { DeskBasedVehicleSectionGroup5Lgv } from './section-templates/vehicle/desk-based-vehicle-section-group5-lgv.template'; -import { AmendDeskBasedRequiredHiddenSectionGroup4 } from './section-templates/required/desk-based-required-section-group4-amend.template'; -import { - AmendDeskBasedRequiredHiddenSectionGroup4Motorcyle, -} from './section-templates/required/desk-based-required-hidden-group4-motorcycle-amend.template'; -import { AmendDeskBasedTestSectionLgvGroup5 } from './section-templates/test/desk-based/desk-based-test-section-group5-LGV'; -import { AmendDeskBasedTestSectionGroup4LgvCarMotorcycle } from './section-templates/test/desk-based/desk-based-test-section-group4-lgv-template'; -import { RequiredStandardsTpl } from '../general/required-standards.template'; -import { AdditionalDefectsSection } from './section-templates/additionalDefects/additional-defects-section.template'; -import { OldIVASpecialistTestSectionGroup1 } from './section-templates/test/specialist/old-specialist-test-section-group1.template'; -import { IvaMsvaVehicleSection } from './section-templates/vehicle/amend-iva-msva-psv-hgv-light.template'; -import { OldIVASpecialistTestSectionGroup5 } from './section-templates/test/specialist/old-specialist-test-section-group5.template'; +import { VisitSection } from './section-templates/visit/visit-section.template'; /** * Keys of root object must a a valid vehicle type. * Keys of child object must be a valid test type id. * Child object must ALWAYS have a 'default' key. */ -export const masterTpl: Record>>> = { - psv: { - default: { - vehicle: VehicleSectionDefaultPsvHgvLight, - test: TestSection, - seatbelts: SeatbeltSection, - emissions: EmissionsSection, - visit: VisitSection, - notes: NotesSection, - defects: DefectsTpl, - reasonForCreation: reasonForCreationSection, - customDefects: CustomDefectsSection, - required: RequiredSection, - }, - testTypesGroup1: { - vehicle: VehicleSectionDefaultPsvHgvLight, - test: TestSectionGroup1, - seatbelts: SeatbeltSection, - visit: VisitSection, - notes: NotesSection, - defects: DefectsTpl, - customDefects: CustomDefectsHiddenSection, - reasonForCreation: reasonForCreationSection, - required: RequiredSection, - }, - testTypesGroup2: { - vehicle: VehicleSectionDefaultPsvHgvLight, - test: TestSectionGroup2, - seatbelts: SeatbeltSection, - visit: VisitSection, - notes: NotesSection, - defects: DefectsTpl, - customDefects: CustomDefectsHiddenSection, - reasonForCreation: reasonForCreationSection, - required: RequiredSection, - }, - testTypesGroup3And4And8: { - vehicle: VehicleSectionDefaultPsvHgvLight, - test: TestSectionGroup3And4And8, - visit: VisitSection, - notes: NotesSection, - customDefects: CustomDefectsSection, - reasonForCreation: reasonForCreationSection, - required: RequiredSection, - }, - testTypesGroup15And16: { - vehicle: VehicleSectionDefaultPsvHgvLight, - test: TestSectionGroup15And16, - emissions: EmissionsSection, - visit: VisitSection, - notes: NotesSection, - customDefects: CustomDefectsSection, - reasonForCreation: reasonForCreationSection, - required: RequiredSection, - }, - testTypesSpecialistGroup1: { - vehicle: IvaMsvaVehicleSection, - test: SpecialistTestSectionGroup1, - visit: VisitSection, - notes: NotesSection, - requiredStandards: RequiredStandardsTpl, - customDefects: AdditionalDefectsSection, - reasonForCreation: reasonForCreationSection, - required: RequiredSpecialistSection, - }, - testTypesSpecialistGroup1OldIVAorMSVA: { - vehicle: VehicleSectionDefaultPsvHgvLight, - test: OldIVASpecialistTestSectionGroup1, - visit: VisitSection, - notes: NotesSection, - customDefects: CustomDefectsSection, - reasonForCreation: reasonForCreationSection, - required: RequiredSpecialistSection, - }, - testTypesSpecialistGroup2: { - vehicle: VehicleSectionDefaultPsvHgvLight, - test: SpecialistTestSectionGroup2, - seatbelts: SeatbeltSection, - visit: VisitSection, - notes: NotesSection, - customDefects: CustomDefectsSection, - reasonForCreation: reasonForCreationSection, - required: RequiredSpecialistSection, - }, - testTypesSpecialistGroup3: { - vehicle: VehicleSectionDefaultPsvHgvLight, - test: SpecialistTestSectionGroup3And4, - visit: VisitSection, - notes: NotesSection, - customDefects: CustomDefectsSection, - reasonForCreation: reasonForCreationSection, - required: RequiredSpecialistSection, - }, - testTypesSpecialistGroup4: { - vehicle: VehicleSectionDefaultPsvHgvLight, - test: SpecialistTestSectionGroup3And4, - seatbelts: SeatbeltSection, - visit: VisitSection, - notes: NotesSection, - customDefects: CustomDefectsSection, - reasonForCreation: reasonForCreationSection, - required: RequiredSpecialistSection, - }, - testTypesSpecialistGroup5: { - vehicle: IvaMsvaVehicleSection, - test: SpecialistTestSectionGroup5, - visit: VisitSection, - notes: NotesSection, - requiredStandards: RequiredStandardsTpl, - customDefects: AdditionalDefectsSection, - reasonForCreation: reasonForCreationSection, - required: RequiredSpecialistSection, - }, - testTypesSpecialistGroup5OldIVAorMSVA: { - vehicle: VehicleSectionDefaultPsvHgvLight, - test: OldIVASpecialistTestSectionGroup5, - visit: VisitSection, - notes: NotesSection, - customDefects: CustomDefectsSection, - reasonForCreation: reasonForCreationSection, - required: RequiredSpecialistSection, - }, - testTypesDeskBasedGroup2: { - required: DeskBasedRequiredSectionPsv, - vehicle: DeskBasedVehicleSectionDefaultPsvHgv, - test: AmendDeskBasedTestSectionGroup2And5, - emissions: EmissionsSection, - visit: VisitSection, - notes: NotesSection, - defects: defectsHiddenSection, - customDefects: CustomDefectsHiddenSection, - reasonForCreation: reasonForCreationSection, - }, - testTypesDeskBasedGroup3: { - required: DeskBasedRequiredSectionPsv, - vehicle: DeskBasedVehicleSectionDefaultPsvHgv, - test: AmendDeskBasedTestSectionGroup3, - visit: VisitSection, - notes: NotesSection, - defects: defectsHiddenSection, - customDefects: CustomDefectsHiddenSection, - reasonForCreation: reasonForCreationSection, - }, - testTypesDeskBasedGroup4: { - required: RequiredSection, - vehicle: DeskBasedVehicleSectionDefaultPsvHgv, - test: amendDeskBasedTestSectionGroup4Psv, - visit: VisitSection, - notes: NotesSection, - defects: defectsHiddenSection, - customDefects: CustomDefectsHiddenSection, - reasonForCreation: reasonForCreationSection, - }, - testTypesDeskBasedGroup1: { - required: RequiredSection, - vehicle: DeskBasedVehicleSectionDefaultPsvHgv, - test: AmendDeskBasedTestSectionGroup1Psv, - visit: VisitSection, - notes: NotesSection, - defects: defectsHiddenSection, - customDefects: CustomDefectsHiddenSection, - reasonForCreation: reasonForCreationSection, - }, - }, - hgv: { - default: { - vehicle: VehicleSectionDefaultPsvHgvLight, - test: TestSection, - emissions: EmissionsSection, - visit: VisitSection, - notes: NotesSection, - reasonForCreation: reasonForCreationSection, - customDefects: CustomDefectsSection, - required: RequiredSectionHGVTRL, - }, - testTypesGroup3And4And8: { - vehicle: VehicleSectionDefaultPsvHgvLight, - test: TestSectionGroup3And4And8, - visit: VisitSection, - notes: NotesSection, - customDefects: CustomDefectsSection, - reasonForCreation: reasonForCreationSection, - required: RequiredSectionHGVTRL, - }, - testTypesGroup5And13: { - vehicle: VehicleSectionDefaultPsvHgvLight, - test: TestSectionGroup5And13, - visit: VisitSection, - notes: NotesSection, - reasonForCreation: reasonForCreationSection, - customDefects: CustomDefectsSection, - required: RequiredSectionHGVTRL, - }, - testTypesGroup6And11: { - vehicle: VehicleSectionDefaultPsvHgvLight, - test: TestSectionGroup6And11, - visit: VisitSection, - notes: NotesSection, - defects: DefectsTpl, - customDefects: CustomDefectsHiddenSection, - reasonForCreation: reasonForCreationSection, - required: RequiredSectionHGVTRL, - }, - testTypesGroup7: { - vehicle: VehicleSectionDefaultPsvHgvLight, - test: TestSectionGroup7, - visit: VisitSection, - notes: NotesSection, - reasonForCreation: reasonForCreationSection, - customDefects: CustomDefectsSection, - required: RequiredSectionHGVTRL, - }, - testTypesGroup9And10: { - vehicle: VehicleSectionDefaultPsvHgvLight, - test: TestSectionGroup9And10, - visit: VisitSection, - notes: NotesSection, - defects: DefectsTpl, - customDefects: CustomDefectsHiddenSection, - reasonForCreation: reasonForCreationSection, - required: RequiredSectionHGVTRL, - }, - testTypesGroup12And14: { - vehicle: VehicleSectionDefaultPsvHgvLight, - test: TestSectionGroup12And14, - visit: VisitSection, - notes: NotesSection, - defects: DefectsTpl, - customDefects: CustomDefectsHiddenSection, - reasonForCreation: reasonForCreationSection, - required: RequiredSectionHGVTRL, - }, - testTypesGroup15And16: { - vehicle: VehicleSectionDefaultPsvHgvLight, - test: TestSectionGroup15And16, - emissions: EmissionsSection, - visit: VisitSection, - notes: NotesSection, - customDefects: CustomDefectsSection, - reasonForCreation: reasonForCreationSection, - required: RequiredSection, - }, - testTypesSpecialistGroup1: { - vehicle: IvaMsvaVehicleSection, - test: SpecialistTestSectionGroup1, - visit: VisitSection, - notes: NotesSection, - requiredStandards: RequiredStandardsTpl, - customDefects: AdditionalDefectsSection, - reasonForCreation: reasonForCreationSection, - required: SpecialistRequiredSectionHGVTRL, - }, - testTypesSpecialistGroup1OldIVAorMSVA: { - vehicle: VehicleSectionDefaultPsvHgvLight, - test: OldIVASpecialistTestSectionGroup1, - visit: VisitSection, - notes: NotesSection, - customDefects: CustomDefectsSection, - reasonForCreation: reasonForCreationSection, - required: SpecialistRequiredSectionHGVTRL, - }, - testTypesSpecialistGroup5: { - vehicle: IvaMsvaVehicleSection, - test: SpecialistTestSectionGroup5, - visit: VisitSection, - notes: NotesSection, - requiredStandards: RequiredStandardsTpl, - customDefects: AdditionalDefectsSection, - reasonForCreation: reasonForCreationSection, - required: SpecialistRequiredSectionHGVTRL, - }, - testTypesSpecialistGroup5OldIVAorMSVA: { - vehicle: VehicleSectionDefaultPsvHgvLight, - test: OldIVASpecialistTestSectionGroup5, - visit: VisitSection, - notes: NotesSection, - customDefects: CustomDefectsSection, - reasonForCreation: reasonForCreationSection, - required: SpecialistRequiredSectionHGVTRL, - }, - testTypesDeskBasedGroup1: { - required: RequiredSection, - vehicle: DeskBasedVehicleSectionHgvGroup1And2And4And5, - test: amendDeskBasedTestSectionGroup1And4HgvTrl, - visit: VisitSection, - notes: NotesSection, - defects: defectsHiddenSection, - customDefects: CustomDefectsHiddenSection, - reasonForCreation: reasonForCreationSection, - }, - testTypesDeskBasedGroup2: { - required: DeskBasedRequiredSectionHgvTrl, - vehicle: DeskBasedVehicleSectionHgvGroup1And2And4And5, - test: AmendDeskBasedTestSectionGroup2And5, - visit: VisitSection, - notes: NotesSection, - defects: defectsHiddenSection, - customDefects: CustomDefectsHiddenSection, - reasonForCreation: reasonForCreationSection, - }, - testTypesDeskBasedGroup3: { - required: DeskBasedRequiredSectionHgvTrl, - vehicle: DeskBasedVehicleSectionDefaultPsvHgv, - test: AmendDeskBasedTestSectionGroup3, - visit: VisitSection, - notes: NotesSection, - defects: defectsHiddenSection, - customDefects: CustomDefectsHiddenSection, - reasonForCreation: reasonForCreationSection, - }, - testTypesDeskBasedGroup4: { - required: RequiredSection, - vehicle: DeskBasedVehicleSectionHgvGroup1And2And4And5, - test: amendDeskBasedTestSectionGroup1And4HgvTrl, - visit: VisitSection, - notes: NotesSection, - defects: defectsHiddenSection, - customDefects: CustomDefectsHiddenSection, - reasonForCreation: reasonForCreationSection, - }, - testTypesDeskBasedGroup5: { - required: RequiredSection, - vehicle: DeskBasedVehicleSectionHgvGroup1And2And4And5, - test: AmendDeskBasedTestSectionGroup2And5, - visit: VisitSection, - notes: NotesSection, - defects: defectsHiddenSection, - customDefects: CustomDefectsHiddenSection, - reasonForCreation: reasonForCreationSection, - }, - }, - trl: { - default: { - vehicle: VehicleSectionDefaultTrl, - test: TestSection, - visit: VisitSection, - notes: NotesSection, - reasonForCreation: reasonForCreationSection, - customDefects: CustomDefectsSection, - required: RequiredSectionHGVTRL, - }, - testTypesGroup3And4And8: { - vehicle: VehicleSectionDefaultTrl, - test: TestSectionGroup3And4And8, - visit: VisitSection, - notes: NotesSection, - customDefects: CustomDefectsSection, - reasonForCreation: reasonForCreationSection, - required: RequiredSectionHGVTRL, - }, - testTypesGroup5And13: { - vehicle: VehicleSectionDefaultTrl, - test: TestSectionGroup5And13, - visit: VisitSection, - notes: NotesSection, - reasonForCreation: reasonForCreationSection, - customDefects: CustomDefectsSection, - required: RequiredSectionHGVTRL, - }, - testTypesGroup6And11: { - vehicle: VehicleSectionDefaultTrl, - test: TestSectionGroup6And11, - visit: VisitSection, - notes: NotesSection, - defects: DefectsTpl, - customDefects: CustomDefectsHiddenSection, - reasonForCreation: reasonForCreationSection, - required: RequiredSectionHGVTRL, - }, - testTypesGroup7: { - vehicle: VehicleSectionDefaultTrl, - test: TestSectionGroup7, - visit: VisitSection, - notes: NotesSection, - reasonForCreation: reasonForCreationSection, - customDefects: CustomDefectsSection, - required: RequiredSectionHGVTRL, - }, - testTypesGroup9And10: { - vehicle: VehicleSectionDefaultTrl, - test: TestSectionGroup9And10, - visit: VisitSection, - notes: NotesSection, - defects: DefectsTpl, - customDefects: CustomDefectsHiddenSection, - reasonForCreation: reasonForCreationSection, - required: RequiredSectionHGVTRL, - }, - testTypesGroup12And14: { - vehicle: VehicleSectionDefaultTrl, - test: TestSectionGroup12And14, - visit: VisitSection, - notes: NotesSection, - defects: DefectsTpl, - customDefects: CustomDefectsHiddenSection, - reasonForCreation: reasonForCreationSection, - required: RequiredSectionHGVTRL, - }, - testTypesSpecialistGroup1: { - vehicle: VehicleSectionDefaultTrl, - test: SpecialistTestSectionGroup1, - visit: VisitSection, - notes: NotesSection, - requiredStandards: RequiredStandardsTpl, - customDefects: AdditionalDefectsSection, - reasonForCreation: reasonForCreationSection, - required: SpecialistRequiredSectionHGVTRL, - }, - testTypesSpecialistGroup1OldIVAorMSVA: { - vehicle: VehicleSectionDefaultTrl, - test: OldIVASpecialistTestSectionGroup1, - visit: VisitSection, - notes: NotesSection, - customDefects: CustomDefectsSection, - reasonForCreation: reasonForCreationSection, - required: SpecialistRequiredSectionHGVTRL, - }, - testTypesSpecialistGroup5: { - vehicle: VehicleSectionDefaultTrl, - test: SpecialistTestSectionGroup5, - visit: VisitSection, - notes: NotesSection, - requiredStandards: RequiredStandardsTpl, - customDefects: AdditionalDefectsSection, - reasonForCreation: reasonForCreationSection, - required: SpecialistRequiredSectionHGVTRL, - }, - testTypesSpecialistGroup5OldIVAorMSVA: { - vehicle: VehicleSectionDefaultTrl, - test: OldIVASpecialistTestSectionGroup5, - visit: VisitSection, - notes: NotesSection, - customDefects: CustomDefectsSection, - reasonForCreation: reasonForCreationSection, - required: SpecialistRequiredSectionHGVTRL, - }, - testTypesDeskBasedGroup1: { - required: RequiredSection, - vehicle: DeskBasedVehicleSectionDefaultTrl, - test: amendDeskBasedTestSectionGroup1And4HgvTrl, - visit: VisitSection, - notes: NotesSection, - defects: defectsHiddenSection, - customDefects: CustomDefectsHiddenSection, - reasonForCreation: reasonForCreationSection, - }, - testTypesDeskBasedGroup2: { - required: DeskBasedRequiredSectionHgvTrl, - vehicle: DeskBasedVehicleSectionHgvGroup1And2And4And5, - test: AmendDeskBasedTestSectionGroup2And5, - visit: VisitSection, - notes: NotesSection, - defects: defectsHiddenSection, - customDefects: CustomDefectsHiddenSection, - reasonForCreation: reasonForCreationSection, - }, - testTypesDeskBasedGroup3: { - required: DeskBasedRequiredSectionHgvTrl, - vehicle: DeskBasedVehicleSectionDefaultTrl, - test: AmendDeskBasedTestSectionGroup3, - visit: VisitSection, - notes: NotesSection, - defects: defectsHiddenSection, - reasonForCreation: reasonForCreationSection, - }, - testTypesDeskBasedGroup4: { - required: RequiredSection, - vehicle: DeskBasedVehicleSectionDefaultTrl, - test: amendDeskBasedTestSectionGroup1And4HgvTrl, - visit: VisitSection, - notes: NotesSection, - defects: defectsHiddenSection, - customDefects: CustomDefectsHiddenSection, - reasonForCreation: reasonForCreationSection, - }, - }, - lgv: { - default: { - required: SpecialistRequiredSectionLgvCarSmallTrl, - vehicle: VehicleSectionDefaultPsvHgvLight, - test: TestSection, - defects: defectsHiddenSection, - visit: VisitSection, - notes: NotesSection, - reasonForCreation: reasonForCreationSection, - }, - testTypesSpecialistGroup1: { - required: SpecialistRequiredSectionLgvCarSmallTrl, - vehicle: IvaMsvaVehicleSection, - test: SpecialistTestSectionGroup1, - requiredStandards: RequiredStandardsTpl, - customDefects: AdditionalDefectsSection, - visit: VisitSection, - notes: NotesSection, - reasonForCreation: reasonForCreationSection, - }, - testTypesSpecialistGroup1OldIVAorMSVA: { - required: SpecialistRequiredSectionLgvCarSmallTrl, - vehicle: VehicleSectionDefaultPsvHgvLight, - test: OldIVASpecialistTestSectionGroup1, - customDefects: CustomDefectsSection, - visit: VisitSection, - notes: NotesSection, - reasonForCreation: reasonForCreationSection, - }, - testTypesSpecialistGroup5: { - required: SpecialistRequiredSectionLgvCarSmallTrl, - vehicle: IvaMsvaVehicleSection, - test: SpecialistTestSectionGroup5, - requiredStandards: RequiredStandardsTpl, - customDefects: AdditionalDefectsSection, - visit: VisitSection, - notes: NotesSection, - reasonForCreation: reasonForCreationSection, - }, - testTypesSpecialistGroup5OldIVAorMSVA: { - required: SpecialistRequiredSectionLgvCarSmallTrl, - vehicle: VehicleSectionDefaultPsvHgvLight, - test: OldIVASpecialistTestSectionGroup5, - customDefects: CustomDefectsSection, - visit: VisitSection, - notes: NotesSection, - reasonForCreation: reasonForCreationSection, - }, - testTypesDeskBasedGroup4: { - required: AmendDeskBasedRequiredHiddenSectionGroup4, - vehicle: DeskBasedVehicleSectionGroup4LGV, - test: AmendDeskBasedTestSectionGroup4LgvCarMotorcycle, - visit: VisitSection, - notes: NotesSection, - reasonForCreation: reasonForCreationSection, - }, - testTypesDeskBasedGroup5: { - required: DeskBasedRequiredHiddenSectionGroup5Lgv, - vehicle: DeskBasedVehicleSectionGroup5Lgv, - test: AmendDeskBasedTestSectionLgvGroup5, - visit: VisitSection, - notes: NotesSection, - reasonForCreation: reasonForCreationSection, - }, - }, - car: { - default: { - required: RequiredSpecialistSection, - vehicle: VehicleSectionDefaultPsvHgvLight, - test: TestSection, - defects: defectsHiddenSection, - visit: VisitSection, - notes: NotesSection, - reasonForCreation: reasonForCreationSection, - }, - testTypesSpecialistGroup1: { - required: SpecialistRequiredSectionLgvCarSmallTrl, - vehicle: IvaMsvaVehicleSection, - test: SpecialistTestSectionGroup1, - requiredStandards: RequiredStandardsTpl, - customDefects: AdditionalDefectsSection, - visit: VisitSection, - notes: NotesSection, - reasonForCreation: reasonForCreationSection, - }, - testTypesSpecialistGroup1OldIVAorMSVA: { - required: SpecialistRequiredSectionLgvCarSmallTrl, - vehicle: VehicleSectionDefaultPsvHgvLight, - test: OldIVASpecialistTestSectionGroup1, - customDefects: CustomDefectsSection, - visit: VisitSection, - notes: NotesSection, - reasonForCreation: reasonForCreationSection, - }, - testTypesSpecialistGroup5: { - required: SpecialistRequiredSectionLgvCarSmallTrl, - vehicle: IvaMsvaVehicleSection, - test: SpecialistTestSectionGroup5, - requiredStandards: RequiredStandardsTpl, - customDefects: AdditionalDefectsSection, - visit: VisitSection, - notes: NotesSection, - reasonForCreation: reasonForCreationSection, - }, - testTypesSpecialistGroup5OldIVAorMSVA: { - required: SpecialistRequiredSectionLgvCarSmallTrl, - vehicle: VehicleSectionDefaultPsvHgvLight, - test: OldIVASpecialistTestSectionGroup5, - customDefects: CustomDefectsSection, - visit: VisitSection, - notes: NotesSection, - reasonForCreation: reasonForCreationSection, - }, - testTypesDeskBasedGroup4: { - required: AmendDeskBasedRequiredHiddenSectionGroup4, - vehicle: DeskBasedVehicleSectionGroup4LGV, - test: AmendDeskBasedTestSectionGroup4LgvCarMotorcycle, - visit: VisitSection, - notes: NotesSection, - reasonForCreation: reasonForCreationSection, - }, - }, - 'small trl': { - default: { - required: RequiredSpecialistSection, - vehicle: VehicleSectionDefaultPsvHgvLight, - test: TestSection, - defects: defectsHiddenSection, - visit: VisitSection, - notes: NotesSection, - reasonForCreation: reasonForCreationSection, - }, - }, - motorcycle: { - default: { - required: RequiredSpecialistSection, - vehicle: VehicleSectionDefaultPsvHgvLight, - test: TestSection, - defects: defectsHiddenSection, - visit: VisitSection, - notes: NotesSection, - reasonForCreation: reasonForCreationSection, - }, - testTypesSpecialistGroup1: { - required: RequiredSpecialistSectionMotorcycle, - vehicle: IvaMsvaVehicleSection, - test: SpecialistTestSectionGroup1, - requiredStandards: RequiredStandardsTpl, - customDefects: AdditionalDefectsSection, - visit: VisitSection, - notes: NotesSection, - reasonForCreation: reasonForCreationSection, - }, - testTypesSpecialistGroup1OldIVAorMSVA: { - required: RequiredSpecialistSectionMotorcycle, - vehicle: VehicleSectionDefaultPsvHgvLight, - test: OldIVASpecialistTestSectionGroup1, - customDefects: CustomDefectsSection, - visit: VisitSection, - notes: NotesSection, - reasonForCreation: reasonForCreationSection, - }, - testTypesDeskBasedGroup4: { - required: AmendDeskBasedRequiredHiddenSectionGroup4Motorcyle, - vehicle: DeskBasedVehicleSectionGroup4LGV, - test: AmendDeskBasedTestSectionGroup4LgvCarMotorcycle, - visit: VisitSection, - notes: NotesSection, - reasonForCreation: reasonForCreationSection, - }, - }, +export const masterTpl: Record< + VehicleTypes, + Partial>> +> = { + psv: { + default: { + vehicle: VehicleSectionDefaultPsvHgvLight, + test: TestSection, + seatbelts: SeatbeltSection, + emissions: EmissionsSection, + visit: VisitSection, + notes: NotesSection, + defects: DefectsTpl, + reasonForCreation: reasonForCreationSection, + customDefects: CustomDefectsSection, + required: RequiredSection, + }, + testTypesGroup1: { + vehicle: VehicleSectionDefaultPsvHgvLight, + test: TestSectionGroup1, + seatbelts: SeatbeltSection, + visit: VisitSection, + notes: NotesSection, + defects: DefectsTpl, + customDefects: CustomDefectsHiddenSection, + reasonForCreation: reasonForCreationSection, + required: RequiredSection, + }, + testTypesGroup2: { + vehicle: VehicleSectionDefaultPsvHgvLight, + test: TestSectionGroup2, + seatbelts: SeatbeltSection, + visit: VisitSection, + notes: NotesSection, + defects: DefectsTpl, + customDefects: CustomDefectsHiddenSection, + reasonForCreation: reasonForCreationSection, + required: RequiredSection, + }, + testTypesGroup3And4And8: { + vehicle: VehicleSectionDefaultPsvHgvLight, + test: TestSectionGroup3And4And8, + visit: VisitSection, + notes: NotesSection, + customDefects: CustomDefectsSection, + reasonForCreation: reasonForCreationSection, + required: RequiredSection, + }, + testTypesGroup8Notifiable: { + vehicle: VehicleSectionDefaultPsvHgvLight, + test: TestSectionGroup8Notifiable, + visit: VisitSection, + notes: NotesSection, + customDefects: CustomDefectsSection, + reasonForCreation: reasonForCreationSection, + required: RequiredSection, + }, + testTypesGroup15And16: { + vehicle: VehicleSectionDefaultPsvHgvLight, + test: TestSectionGroup15And16, + emissions: EmissionsSection, + visit: VisitSection, + notes: NotesSection, + customDefects: CustomDefectsSection, + reasonForCreation: reasonForCreationSection, + required: RequiredSection, + }, + testTypesSpecialistGroup1: { + vehicle: IvaMsvaVehicleSection, + test: SpecialistTestSectionGroup1, + visit: VisitSection, + notes: NotesSection, + requiredStandards: RequiredStandardsTpl, + customDefects: AdditionalDefectsSection, + reasonForCreation: reasonForCreationSection, + required: RequiredSpecialistSection, + }, + testTypesSpecialistGroup1OldIVAorMSVA: { + vehicle: VehicleSectionDefaultPsvHgvLight, + test: OldIVASpecialistTestSectionGroup1, + visit: VisitSection, + notes: NotesSection, + customDefects: CustomDefectsSection, + reasonForCreation: reasonForCreationSection, + required: RequiredSpecialistSection, + }, + testTypesSpecialistGroup2: { + vehicle: VehicleSectionDefaultPsvHgvLight, + test: SpecialistTestSectionGroup2, + seatbelts: SeatbeltSection, + visit: VisitSection, + notes: NotesSection, + customDefects: CustomDefectsSection, + reasonForCreation: reasonForCreationSection, + required: RequiredSpecialistSection, + }, + testTypesSpecialistGroup3: { + vehicle: VehicleSectionDefaultPsvHgvLight, + test: SpecialistTestSectionGroup3And4, + visit: VisitSection, + notes: NotesSection, + customDefects: CustomDefectsSection, + reasonForCreation: reasonForCreationSection, + required: RequiredSpecialistSection, + }, + testTypesSpecialistGroup4: { + vehicle: VehicleSectionDefaultPsvHgvLight, + test: SpecialistTestSectionGroup3And4, + seatbelts: SeatbeltSection, + visit: VisitSection, + notes: NotesSection, + customDefects: CustomDefectsSection, + reasonForCreation: reasonForCreationSection, + required: RequiredSpecialistSection, + }, + testTypesSpecialistGroup5: { + vehicle: IvaMsvaVehicleSection, + test: SpecialistTestSectionGroup5, + visit: VisitSection, + notes: NotesSection, + requiredStandards: RequiredStandardsTpl, + customDefects: AdditionalDefectsSection, + reasonForCreation: reasonForCreationSection, + required: RequiredSpecialistSection, + }, + testTypesSpecialistGroup5OldIVAorMSVA: { + vehicle: VehicleSectionDefaultPsvHgvLight, + test: OldIVASpecialistTestSectionGroup5, + visit: VisitSection, + notes: NotesSection, + customDefects: CustomDefectsSection, + reasonForCreation: reasonForCreationSection, + required: RequiredSpecialistSection, + }, + testTypesDeskBasedGroup2: { + required: DeskBasedRequiredSectionPsv, + vehicle: DeskBasedVehicleSectionDefaultPsvHgv, + test: AmendDeskBasedTestSectionGroup2And5, + emissions: EmissionsSection, + visit: VisitSection, + notes: NotesSection, + defects: defectsHiddenSection, + customDefects: CustomDefectsHiddenSection, + reasonForCreation: reasonForCreationSection, + }, + testTypesDeskBasedGroup3: { + required: DeskBasedRequiredSectionPsv, + vehicle: DeskBasedVehicleSectionDefaultPsvHgv, + test: AmendDeskBasedTestSectionGroup3, + visit: VisitSection, + notes: NotesSection, + defects: defectsHiddenSection, + customDefects: CustomDefectsHiddenSection, + reasonForCreation: reasonForCreationSection, + }, + testTypesDeskBasedGroup4: { + required: RequiredSection, + vehicle: DeskBasedVehicleSectionDefaultPsvHgv, + test: amendDeskBasedTestSectionGroup4Psv, + visit: VisitSection, + notes: NotesSection, + defects: defectsHiddenSection, + customDefects: CustomDefectsHiddenSection, + reasonForCreation: reasonForCreationSection, + }, + testTypesDeskBasedGroup1: { + required: RequiredSection, + vehicle: DeskBasedVehicleSectionDefaultPsvHgv, + test: AmendDeskBasedTestSectionGroup1Psv, + visit: VisitSection, + notes: NotesSection, + defects: defectsHiddenSection, + customDefects: CustomDefectsHiddenSection, + reasonForCreation: reasonForCreationSection, + }, + }, + hgv: { + default: { + vehicle: VehicleSectionDefaultPsvHgvLight, + test: TestSection, + emissions: EmissionsSection, + visit: VisitSection, + notes: NotesSection, + reasonForCreation: reasonForCreationSection, + customDefects: CustomDefectsSection, + required: RequiredSectionHGVTRL, + }, + testTypesGroup3And4And8: { + vehicle: VehicleSectionDefaultPsvHgvLight, + test: TestSectionGroup3And4And8, + visit: VisitSection, + notes: NotesSection, + customDefects: CustomDefectsSection, + reasonForCreation: reasonForCreationSection, + required: RequiredSectionHGVTRL, + }, + testTypesGroup8Notifiable: { + vehicle: VehicleSectionDefaultPsvHgvLight, + test: TestSectionGroup8Notifiable, + visit: VisitSection, + notes: NotesSection, + customDefects: CustomDefectsSection, + reasonForCreation: reasonForCreationSection, + required: RequiredSectionHGVTRL, + }, + testTypesGroup5And13: { + vehicle: VehicleSectionDefaultPsvHgvLight, + test: TestSectionGroup5And13, + visit: VisitSection, + notes: NotesSection, + reasonForCreation: reasonForCreationSection, + customDefects: CustomDefectsSection, + required: RequiredSectionHGVTRL, + }, + testTypesGroup6And11: { + vehicle: VehicleSectionDefaultPsvHgvLight, + test: TestSectionGroup6And11, + visit: VisitSection, + notes: NotesSection, + defects: DefectsTpl, + customDefects: CustomDefectsHiddenSection, + reasonForCreation: reasonForCreationSection, + required: RequiredSectionHGVTRL, + }, + testTypesGroup7: { + vehicle: VehicleSectionDefaultPsvHgvLight, + test: TestSectionGroup7, + visit: VisitSection, + notes: AdrNotesSection, + reasonForCreation: reasonForCreationSection, + customDefects: CustomDefectsSection, + required: RequiredSectionHGVTRL, + }, + testTypesGroup9And10: { + vehicle: VehicleSectionDefaultPsvHgvLight, + test: TestSectionGroup9And10, + visit: VisitSection, + notes: NotesSection, + defects: DefectsTpl, + customDefects: CustomDefectsHiddenSection, + reasonForCreation: reasonForCreationSection, + required: RequiredSectionHGVTRL, + }, + testTypesGroup9And10CentralDocs: { + vehicle: VehicleSectionDefaultPsvHgvLight, + test: TestSectionGroup9And10CentralDocs, + visit: VisitSection, + notes: NotesSection, + defects: DefectsTpl, + customDefects: CustomDefectsHiddenSection, + reasonForCreation: reasonForCreationSection, + required: RequiredSectionHGVTRL, + }, + testTypesGroup12And14: { + vehicle: VehicleSectionDefaultPsvHgvLight, + test: TestSectionGroup12And14, + visit: VisitSection, + notes: NotesSection, + defects: DefectsTpl, + customDefects: CustomDefectsHiddenSection, + reasonForCreation: reasonForCreationSection, + required: RequiredSectionHGVTRL, + }, + testTypesGroup15And16: { + vehicle: VehicleSectionDefaultPsvHgvLight, + test: TestSectionGroup15And16, + emissions: EmissionsSection, + visit: VisitSection, + notes: NotesSection, + customDefects: CustomDefectsSection, + reasonForCreation: reasonForCreationSection, + required: RequiredSection, + }, + testTypesSpecialistGroup1: { + vehicle: IvaMsvaVehicleSection, + test: SpecialistTestSectionGroup1, + visit: VisitSection, + notes: NotesSection, + requiredStandards: RequiredStandardsTpl, + customDefects: AdditionalDefectsSection, + reasonForCreation: reasonForCreationSection, + required: SpecialistRequiredSectionHGVTRL, + }, + testTypesSpecialistGroup1OldIVAorMSVA: { + vehicle: VehicleSectionDefaultPsvHgvLight, + test: OldIVASpecialistTestSectionGroup1, + visit: VisitSection, + notes: NotesSection, + customDefects: CustomDefectsSection, + reasonForCreation: reasonForCreationSection, + required: SpecialistRequiredSectionHGVTRL, + }, + testTypesSpecialistGroup5: { + vehicle: IvaMsvaVehicleSection, + test: SpecialistTestSectionGroup5, + visit: VisitSection, + notes: NotesSection, + requiredStandards: RequiredStandardsTpl, + customDefects: AdditionalDefectsSection, + reasonForCreation: reasonForCreationSection, + required: SpecialistRequiredSectionHGVTRL, + }, + testTypesSpecialistGroup5OldIVAorMSVA: { + vehicle: VehicleSectionDefaultPsvHgvLight, + test: OldIVASpecialistTestSectionGroup5, + visit: VisitSection, + notes: NotesSection, + customDefects: CustomDefectsSection, + reasonForCreation: reasonForCreationSection, + required: SpecialistRequiredSectionHGVTRL, + }, + testTypesDeskBasedGroup1: { + required: RequiredSection, + vehicle: DeskBasedVehicleSectionHgvGroup1And2And4And5, + test: amendDeskBasedTestSectionGroup1And4HgvTrl, + visit: VisitSection, + notes: NotesSection, + defects: defectsHiddenSection, + customDefects: CustomDefectsHiddenSection, + reasonForCreation: reasonForCreationSection, + }, + testTypesDeskBasedGroup2: { + required: DeskBasedRequiredSectionHgvTrl, + vehicle: DeskBasedVehicleSectionHgvGroup1And2And4And5, + test: AmendDeskBasedTestSectionGroup2And5, + visit: VisitSection, + notes: NotesSection, + defects: defectsHiddenSection, + customDefects: CustomDefectsHiddenSection, + reasonForCreation: reasonForCreationSection, + }, + testTypesDeskBasedGroup3: { + required: DeskBasedRequiredSectionHgvTrl, + vehicle: DeskBasedVehicleSectionDefaultPsvHgv, + test: AmendDeskBasedTestSectionGroup3, + visit: VisitSection, + notes: NotesSection, + defects: defectsHiddenSection, + customDefects: CustomDefectsHiddenSection, + reasonForCreation: reasonForCreationSection, + }, + testTypesDeskBasedGroup4: { + required: RequiredSection, + vehicle: DeskBasedVehicleSectionHgvGroup1And2And4And5, + test: amendDeskBasedTestSectionGroup1And4HgvTrl, + visit: VisitSection, + notes: NotesSection, + defects: defectsHiddenSection, + customDefects: CustomDefectsHiddenSection, + reasonForCreation: reasonForCreationSection, + }, + testTypesDeskBasedGroup5: { + required: RequiredSection, + vehicle: DeskBasedVehicleSectionHgvGroup1And2And4And5, + test: AmendDeskBasedTestSectionGroup2And5, + visit: VisitSection, + notes: NotesSection, + defects: defectsHiddenSection, + customDefects: CustomDefectsHiddenSection, + reasonForCreation: reasonForCreationSection, + }, + }, + trl: { + default: { + vehicle: VehicleSectionDefaultTrl, + test: TestSection, + visit: VisitSection, + notes: NotesSection, + reasonForCreation: reasonForCreationSection, + customDefects: CustomDefectsSection, + required: RequiredSectionHGVTRL, + }, + testTypesGroup3And4And8: { + vehicle: VehicleSectionDefaultTrl, + test: TestSectionGroup3And4And8, + visit: VisitSection, + notes: NotesSection, + customDefects: CustomDefectsSection, + reasonForCreation: reasonForCreationSection, + required: RequiredSectionHGVTRL, + }, + testTypesGroup8Notifiable: { + vehicle: VehicleSectionDefaultTrl, + test: TestSectionGroup8Notifiable, + visit: VisitSection, + notes: NotesSection, + customDefects: CustomDefectsSection, + reasonForCreation: reasonForCreationSection, + required: RequiredSectionHGVTRL, + }, + testTypesGroup5And13: { + vehicle: VehicleSectionDefaultTrl, + test: TestSectionGroup5And13, + visit: VisitSection, + notes: NotesSection, + reasonForCreation: reasonForCreationSection, + customDefects: CustomDefectsSection, + required: RequiredSectionHGVTRL, + }, + testTypesGroup6And11: { + vehicle: VehicleSectionDefaultTrl, + test: TestSectionGroup6And11, + visit: VisitSection, + notes: NotesSection, + defects: DefectsTpl, + customDefects: CustomDefectsHiddenSection, + reasonForCreation: reasonForCreationSection, + required: RequiredSectionHGVTRL, + }, + testTypesGroup7: { + vehicle: VehicleSectionDefaultTrl, + test: TestSectionGroup7, + visit: VisitSection, + notes: AdrNotesSection, + reasonForCreation: reasonForCreationSection, + customDefects: CustomDefectsSection, + required: RequiredSectionHGVTRL, + }, + testTypesGroup9And10: { + vehicle: VehicleSectionDefaultTrl, + test: TestSectionGroup9And10, + visit: VisitSection, + notes: NotesSection, + defects: DefectsTpl, + customDefects: CustomDefectsHiddenSection, + reasonForCreation: reasonForCreationSection, + required: RequiredSectionHGVTRL, + }, + testTypesGroup9And10CentralDocs: { + vehicle: VehicleSectionDefaultTrl, + test: TestSectionGroup9And10CentralDocs, + visit: VisitSection, + notes: NotesSection, + defects: DefectsTpl, + customDefects: CustomDefectsHiddenSection, + reasonForCreation: reasonForCreationSection, + required: RequiredSectionHGVTRL, + }, + testTypesGroup12And14: { + vehicle: VehicleSectionDefaultTrl, + test: TestSectionGroup12And14, + visit: VisitSection, + notes: NotesSection, + defects: DefectsTpl, + customDefects: CustomDefectsHiddenSection, + reasonForCreation: reasonForCreationSection, + required: RequiredSectionHGVTRL, + }, + testTypesSpecialistGroup1: { + vehicle: VehicleSectionDefaultTrl, + test: SpecialistTestSectionGroup1, + visit: VisitSection, + notes: NotesSection, + requiredStandards: RequiredStandardsTpl, + customDefects: AdditionalDefectsSection, + reasonForCreation: reasonForCreationSection, + required: SpecialistRequiredSectionHGVTRL, + }, + testTypesSpecialistGroup1OldIVAorMSVA: { + vehicle: VehicleSectionDefaultTrl, + test: OldIVASpecialistTestSectionGroup1, + visit: VisitSection, + notes: NotesSection, + customDefects: CustomDefectsSection, + reasonForCreation: reasonForCreationSection, + required: SpecialistRequiredSectionHGVTRL, + }, + testTypesSpecialistGroup5: { + vehicle: VehicleSectionDefaultTrl, + test: SpecialistTestSectionGroup5, + visit: VisitSection, + notes: NotesSection, + requiredStandards: RequiredStandardsTpl, + customDefects: AdditionalDefectsSection, + reasonForCreation: reasonForCreationSection, + required: SpecialistRequiredSectionHGVTRL, + }, + testTypesSpecialistGroup5OldIVAorMSVA: { + vehicle: VehicleSectionDefaultTrl, + test: OldIVASpecialistTestSectionGroup5, + visit: VisitSection, + notes: NotesSection, + customDefects: CustomDefectsSection, + reasonForCreation: reasonForCreationSection, + required: SpecialistRequiredSectionHGVTRL, + }, + testTypesDeskBasedGroup1: { + required: RequiredSection, + vehicle: DeskBasedVehicleSectionDefaultTrl, + test: amendDeskBasedTestSectionGroup1And4HgvTrl, + visit: VisitSection, + notes: NotesSection, + defects: defectsHiddenSection, + customDefects: CustomDefectsHiddenSection, + reasonForCreation: reasonForCreationSection, + }, + testTypesDeskBasedGroup2: { + required: DeskBasedRequiredSectionHgvTrl, + vehicle: DeskBasedVehicleSectionHgvGroup1And2And4And5, + test: AmendDeskBasedTestSectionGroup2And5, + visit: VisitSection, + notes: NotesSection, + defects: defectsHiddenSection, + customDefects: CustomDefectsHiddenSection, + reasonForCreation: reasonForCreationSection, + }, + testTypesDeskBasedGroup3: { + required: DeskBasedRequiredSectionHgvTrl, + vehicle: DeskBasedVehicleSectionDefaultTrl, + test: AmendDeskBasedTestSectionGroup3, + visit: VisitSection, + notes: NotesSection, + defects: defectsHiddenSection, + reasonForCreation: reasonForCreationSection, + }, + testTypesDeskBasedGroup4: { + required: RequiredSection, + vehicle: DeskBasedVehicleSectionDefaultTrl, + test: amendDeskBasedTestSectionGroup1And4HgvTrl, + visit: VisitSection, + notes: NotesSection, + defects: defectsHiddenSection, + customDefects: CustomDefectsHiddenSection, + reasonForCreation: reasonForCreationSection, + }, + }, + lgv: { + default: { + required: SpecialistRequiredSectionLgvCarSmallTrl, + vehicle: VehicleSectionDefaultPsvHgvLight, + test: TestSection, + defects: defectsHiddenSection, + visit: VisitSection, + notes: NotesSection, + reasonForCreation: reasonForCreationSection, + }, + testTypesSpecialistGroup1: { + required: SpecialistRequiredSectionLgvCarSmallTrl, + vehicle: IvaMsvaVehicleSection, + test: SpecialistTestSectionGroup1, + requiredStandards: RequiredStandardsTpl, + customDefects: AdditionalDefectsSection, + visit: VisitSection, + notes: NotesSection, + reasonForCreation: reasonForCreationSection, + }, + testTypesSpecialistGroup1OldIVAorMSVA: { + required: SpecialistRequiredSectionLgvCarSmallTrl, + vehicle: VehicleSectionDefaultPsvHgvLight, + test: OldIVASpecialistTestSectionGroup1, + customDefects: CustomDefectsSection, + visit: VisitSection, + notes: NotesSection, + reasonForCreation: reasonForCreationSection, + }, + testTypesSpecialistGroup5: { + required: SpecialistRequiredSectionLgvCarSmallTrl, + vehicle: IvaMsvaVehicleSection, + test: SpecialistTestSectionGroup5, + requiredStandards: RequiredStandardsTpl, + customDefects: AdditionalDefectsSection, + visit: VisitSection, + notes: NotesSection, + reasonForCreation: reasonForCreationSection, + }, + testTypesSpecialistGroup5OldIVAorMSVA: { + required: SpecialistRequiredSectionLgvCarSmallTrl, + vehicle: VehicleSectionDefaultPsvHgvLight, + test: OldIVASpecialistTestSectionGroup5, + customDefects: CustomDefectsSection, + visit: VisitSection, + notes: NotesSection, + reasonForCreation: reasonForCreationSection, + }, + testTypesDeskBasedGroup4: { + required: AmendDeskBasedRequiredHiddenSectionGroup4, + vehicle: DeskBasedVehicleSectionGroup4LGV, + test: AmendDeskBasedTestSectionGroup4LgvCarMotorcycle, + visit: VisitSection, + notes: NotesSection, + reasonForCreation: reasonForCreationSection, + }, + testTypesDeskBasedGroup5: { + required: DeskBasedRequiredHiddenSectionGroup5Lgv, + vehicle: DeskBasedVehicleSectionGroup5Lgv, + test: AmendDeskBasedTestSectionLgvGroup5, + visit: VisitSection, + notes: NotesSection, + reasonForCreation: reasonForCreationSection, + }, + }, + car: { + default: { + required: RequiredSpecialistSection, + vehicle: VehicleSectionDefaultPsvHgvLight, + test: TestSection, + defects: defectsHiddenSection, + visit: VisitSection, + notes: NotesSection, + reasonForCreation: reasonForCreationSection, + }, + testTypesSpecialistGroup1: { + required: SpecialistRequiredSectionLgvCarSmallTrl, + vehicle: IvaMsvaVehicleSection, + test: SpecialistTestSectionGroup1, + requiredStandards: RequiredStandardsTpl, + customDefects: AdditionalDefectsSection, + visit: VisitSection, + notes: NotesSection, + reasonForCreation: reasonForCreationSection, + }, + testTypesSpecialistGroup1OldIVAorMSVA: { + required: SpecialistRequiredSectionLgvCarSmallTrl, + vehicle: VehicleSectionDefaultPsvHgvLight, + test: OldIVASpecialistTestSectionGroup1, + customDefects: CustomDefectsSection, + visit: VisitSection, + notes: NotesSection, + reasonForCreation: reasonForCreationSection, + }, + testTypesSpecialistGroup5: { + required: SpecialistRequiredSectionLgvCarSmallTrl, + vehicle: IvaMsvaVehicleSection, + test: SpecialistTestSectionGroup5, + requiredStandards: RequiredStandardsTpl, + customDefects: AdditionalDefectsSection, + visit: VisitSection, + notes: NotesSection, + reasonForCreation: reasonForCreationSection, + }, + testTypesSpecialistGroup5OldIVAorMSVA: { + required: SpecialistRequiredSectionLgvCarSmallTrl, + vehicle: VehicleSectionDefaultPsvHgvLight, + test: OldIVASpecialistTestSectionGroup5, + customDefects: CustomDefectsSection, + visit: VisitSection, + notes: NotesSection, + reasonForCreation: reasonForCreationSection, + }, + testTypesDeskBasedGroup4: { + required: AmendDeskBasedRequiredHiddenSectionGroup4, + vehicle: DeskBasedVehicleSectionGroup4LGV, + test: AmendDeskBasedTestSectionGroup4LgvCarMotorcycle, + visit: VisitSection, + notes: NotesSection, + reasonForCreation: reasonForCreationSection, + }, + }, + 'small trl': { + default: { + required: RequiredSpecialistSection, + vehicle: VehicleSectionDefaultPsvHgvLight, + test: TestSection, + defects: defectsHiddenSection, + visit: VisitSection, + notes: NotesSection, + reasonForCreation: reasonForCreationSection, + }, + }, + motorcycle: { + default: { + required: RequiredSpecialistSection, + vehicle: VehicleSectionDefaultPsvHgvLight, + test: TestSection, + defects: defectsHiddenSection, + visit: VisitSection, + notes: NotesSection, + reasonForCreation: reasonForCreationSection, + }, + testTypesSpecialistGroup1: { + required: RequiredSpecialistSectionMotorcycle, + vehicle: IvaMsvaVehicleSection, + test: SpecialistTestSectionGroup1, + requiredStandards: RequiredStandardsTpl, + customDefects: AdditionalDefectsSection, + visit: VisitSection, + notes: NotesSection, + reasonForCreation: reasonForCreationSection, + }, + testTypesSpecialistGroup1OldIVAorMSVA: { + required: RequiredSpecialistSectionMotorcycle, + vehicle: VehicleSectionDefaultPsvHgvLight, + test: OldIVASpecialistTestSectionGroup1, + customDefects: CustomDefectsSection, + visit: VisitSection, + notes: NotesSection, + reasonForCreation: reasonForCreationSection, + }, + testTypesDeskBasedGroup4: { + required: AmendDeskBasedRequiredHiddenSectionGroup4Motorcyle, + vehicle: DeskBasedVehicleSectionGroup4LGV, + test: AmendDeskBasedTestSectionGroup4LgvCarMotorcycle, + visit: VisitSection, + notes: NotesSection, + reasonForCreation: reasonForCreationSection, + }, + }, }; diff --git a/src/app/forms/templates/test-records/section-templates/additionalDefects/additional-defects-section.template.ts b/src/app/forms/templates/test-records/section-templates/additionalDefects/additional-defects-section.template.ts index f4f4f6fe48..809de0aa3f 100644 --- a/src/app/forms/templates/test-records/section-templates/additionalDefects/additional-defects-section.template.ts +++ b/src/app/forms/templates/test-records/section-templates/additionalDefects/additional-defects-section.template.ts @@ -2,46 +2,46 @@ import { ValidatorNames } from '@forms/models/validators.enum'; import { FormNode, FormNodeTypes } from '@forms/services/dynamic-form.types'; export const AdditionalDefectsSection: FormNode = { - name: 'additionalDefectsSection', - label: 'Additional Defects', - type: FormNodeTypes.GROUP, - children: [ - { - name: 'testTypes', - label: 'Test Types', - type: FormNodeTypes.ARRAY, - children: [ - { - name: '0', // it is important here that the name of the node for an ARRAY type should be an index value - type: FormNodeTypes.GROUP, - children: [ - { - name: 'customDefects', - type: FormNodeTypes.ARRAY, - children: [ - { - name: '0', - type: FormNodeTypes.GROUP, - children: [ - { - name: 'defectName', - label: 'Defect Name', - type: FormNodeTypes.CONTROL, - validators: [{ name: ValidatorNames.Required }, { name: ValidatorNames.MaxLength, args: 200 }], - }, - { - name: 'defectNotes', - label: 'Defect Notes', - type: FormNodeTypes.CONTROL, - validators: [{ name: ValidatorNames.Required }, { name: ValidatorNames.MaxLength, args: 200 }], - }, - ], - }, - ], - }, - ], - }, - ], - }, - ], + name: 'additionalDefectsSection', + label: 'Additional Defects', + type: FormNodeTypes.GROUP, + children: [ + { + name: 'testTypes', + label: 'Test Types', + type: FormNodeTypes.ARRAY, + children: [ + { + name: '0', // it is important here that the name of the node for an ARRAY type should be an index value + type: FormNodeTypes.GROUP, + children: [ + { + name: 'customDefects', + type: FormNodeTypes.ARRAY, + children: [ + { + name: '0', + type: FormNodeTypes.GROUP, + children: [ + { + name: 'defectName', + label: 'Defect Name', + type: FormNodeTypes.CONTROL, + validators: [{ name: ValidatorNames.Required }, { name: ValidatorNames.MaxLength, args: 200 }], + }, + { + name: 'defectNotes', + label: 'Defect Notes', + type: FormNodeTypes.CONTROL, + validators: [{ name: ValidatorNames.Required }, { name: ValidatorNames.MaxLength, args: 200 }], + }, + ], + }, + ], + }, + ], + }, + ], + }, + ], }; diff --git a/src/app/forms/templates/test-records/section-templates/customDefects/custom-defects-section.template.ts b/src/app/forms/templates/test-records/section-templates/customDefects/custom-defects-section.template.ts index eb20f82085..830080d065 100644 --- a/src/app/forms/templates/test-records/section-templates/customDefects/custom-defects-section.template.ts +++ b/src/app/forms/templates/test-records/section-templates/customDefects/custom-defects-section.template.ts @@ -2,53 +2,53 @@ import { ValidatorNames } from '@forms/models/validators.enum'; import { FormNode, FormNodeTypes, FormNodeWidth } from '@forms/services/dynamic-form.types'; export const CustomDefectsSection: FormNode = { - name: 'customDefectsSection', - label: 'Custom Defects', - type: FormNodeTypes.GROUP, - children: [ - { - name: 'testTypes', - label: 'Test Types', - type: FormNodeTypes.ARRAY, - children: [ - { - name: '0', // it is important here that the name of the node for an ARRAY type should be an index value - type: FormNodeTypes.GROUP, - children: [ - { - name: 'customDefects', - type: FormNodeTypes.ARRAY, - children: [ - { - name: '0', - type: FormNodeTypes.GROUP, - children: [ - { - name: 'referenceNumber', - label: 'Reference Number', - type: FormNodeTypes.CONTROL, - validators: [{ name: ValidatorNames.Required }, { name: ValidatorNames.MaxLength, args: 10 }], - width: FormNodeWidth.XL, - }, - { - name: 'defectName', - label: 'Defect Name', - type: FormNodeTypes.CONTROL, - validators: [{ name: ValidatorNames.Required }, { name: ValidatorNames.MaxLength, args: 200 }], - }, - { - name: 'defectNotes', - label: 'Defect Notes', - type: FormNodeTypes.CONTROL, - validators: [{ name: ValidatorNames.Required }, { name: ValidatorNames.MaxLength, args: 200 }], - }, - ], - }, - ], - }, - ], - }, - ], - }, - ], + name: 'customDefectsSection', + label: 'Custom Defects', + type: FormNodeTypes.GROUP, + children: [ + { + name: 'testTypes', + label: 'Test Types', + type: FormNodeTypes.ARRAY, + children: [ + { + name: '0', // it is important here that the name of the node for an ARRAY type should be an index value + type: FormNodeTypes.GROUP, + children: [ + { + name: 'customDefects', + type: FormNodeTypes.ARRAY, + children: [ + { + name: '0', + type: FormNodeTypes.GROUP, + children: [ + { + name: 'referenceNumber', + label: 'Reference Number', + type: FormNodeTypes.CONTROL, + validators: [{ name: ValidatorNames.Required }, { name: ValidatorNames.MaxLength, args: 10 }], + width: FormNodeWidth.XL, + }, + { + name: 'defectName', + label: 'Defect Name', + type: FormNodeTypes.CONTROL, + validators: [{ name: ValidatorNames.Required }, { name: ValidatorNames.MaxLength, args: 200 }], + }, + { + name: 'defectNotes', + label: 'Defect Notes', + type: FormNodeTypes.CONTROL, + validators: [{ name: ValidatorNames.Required }, { name: ValidatorNames.MaxLength, args: 200 }], + }, + ], + }, + ], + }, + ], + }, + ], + }, + ], }; diff --git a/src/app/forms/templates/test-records/section-templates/emissions/desk-based-emissions-section.template.ts b/src/app/forms/templates/test-records/section-templates/emissions/desk-based-emissions-section.template.ts index 11db5133fa..8668a118f0 100644 --- a/src/app/forms/templates/test-records/section-templates/emissions/desk-based-emissions-section.template.ts +++ b/src/app/forms/templates/test-records/section-templates/emissions/desk-based-emissions-section.template.ts @@ -1,142 +1,152 @@ -import { FormNode, FormNodeTypes, FormNodeEditTypes } from '@forms/services/dynamic-form.types'; -import { ValidatorNames } from '@forms/models/validators.enum'; import { AsyncValidatorNames } from '@forms/models/async-validators.enum'; -import { EmissionStandard } from '@models/test-types/emissions.enum'; +import { ValidatorNames } from '@forms/models/validators.enum'; +import { FormNode, FormNodeEditTypes, FormNodeTypes } from '@forms/services/dynamic-form.types'; import { getOptionsFromEnum } from '@forms/utils/enum-map'; +import { EmissionStandard } from '@models/test-types/emissions.enum'; export const DeskBasedEmissionsSection: FormNode = { - name: 'deskBasedEmissionsSection', - label: 'Emissions', - type: FormNodeTypes.GROUP, - children: [ - { - name: 'testTypes', - label: 'Test Types', - type: FormNodeTypes.ARRAY, - children: [ - { - name: '0', // it is important here that the name of the node for an ARRAY type should be an index value - type: FormNodeTypes.GROUP, - children: [ - { - name: 'emissionStandard', - label: 'Emissions standard', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.RADIO, - options: [{ label: '0.10 g/kWh Euro III PM', value: '0.10 g/kWh Euro 3 PM' }, - ...getOptionsFromEnum(EmissionStandard), - ], - required: true, - value: null, - }, - { - name: 'smokeTestKLimitApplied', - label: 'Smoke test K limit applied', - type: FormNodeTypes.CONTROL, - validators: [{ name: ValidatorNames.MaxLength, args: 100 }], - required: true, - value: null, - }, - { - name: 'fuelType', - label: 'Fuel type', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.RADIO, - options: [ - { value: 'diesel', label: 'Diesel' }, - { value: 'gas-cng', label: 'Gas-CNG' }, - { value: 'gas-lng', label: 'Gas-LNG' }, - { value: 'gas-lpg', label: 'Gas-LPG' }, - { value: 'fuel cell', label: 'Fuel cell' }, - { value: 'petrol', label: 'Petrol' }, - { value: 'full electric', label: 'Full electric' }, - ], - required: true, - value: null, - }, - { - name: 'modType', - label: 'Modification Type', - type: FormNodeTypes.GROUP, - children: [ - { - name: 'code', - label: 'Modification code', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.RADIO, - options: [ - { value: 'p', label: 'P' }, - { value: 'm', label: 'M' }, - { value: 'g', label: 'G' }, - ], - validators: [ - { name: ValidatorNames.HideIfParentSiblingEqual, args: { sibling: 'modificationTypeUsed', value: 'p' } }, - { name: ValidatorNames.HideIfParentSiblingNotEqual, args: { sibling: 'particulateTrapFitted', value: 'p' } }, - { name: ValidatorNames.HideIfParentSiblingNotEqual, args: { sibling: 'particulateTrapSerialNumber', value: 'p' } }, - ], - }, - { - name: 'description', - label: 'Modification description', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.RADIO, - options: [ - { value: 'particulate trap', label: 'Particulate trap' }, - { value: 'modification or change of engine', label: 'Modification or change of engine' }, - { value: 'gas engine', label: 'Gas engine' }, - ], - }, - ], - }, - { - name: 'modificationTypeUsed', - label: 'Modification type used', - type: FormNodeTypes.CONTROL, - validators: [{ name: ValidatorNames.MaxLength, args: 100 }], - asyncValidators: [ - { - name: AsyncValidatorNames.RequiredIfNotResultAndSiblingEquals, - args: { testResult: ['fail', 'abandoned'], sibling: 'modType.code', value: 'm' }, - }, - { - name: AsyncValidatorNames.RequiredIfNotResultAndSiblingEquals, - args: { testResult: ['fail', 'abandoned'], sibling: 'modType.code', value: 'g' }, - }, - ], - required: true, - value: null, - }, - { - name: 'particulateTrapFitted', - label: 'Particulate trap fitted', - type: FormNodeTypes.CONTROL, - validators: [{ name: ValidatorNames.MaxLength, args: 100 }], - asyncValidators: [ - { - name: AsyncValidatorNames.RequiredIfNotResultAndSiblingEquals, - args: { testResult: ['fail', 'abandoned'], sibling: 'modType.code', value: 'p' }, - }, - ], - required: true, - value: null, - }, - { - name: 'particulateTrapSerialNumber', - label: 'Particulate trap serial number', - type: FormNodeTypes.CONTROL, - validators: [{ name: ValidatorNames.MaxLength, args: 100 }], - asyncValidators: [ - { - name: AsyncValidatorNames.RequiredIfNotResultAndSiblingEquals, - args: { testResult: ['fail', 'abandoned'], sibling: 'modType.code', value: 'p' }, - }, - ], - required: true, - value: null, - }, - ], - }, - ], - }, - ], + name: 'deskBasedEmissionsSection', + label: 'Emissions', + type: FormNodeTypes.GROUP, + children: [ + { + name: 'testTypes', + label: 'Test Types', + type: FormNodeTypes.ARRAY, + children: [ + { + name: '0', // it is important here that the name of the node for an ARRAY type should be an index value + type: FormNodeTypes.GROUP, + children: [ + { + name: 'emissionStandard', + label: 'Emissions standard', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.RADIO, + options: [ + { label: '0.10 g/kWh Euro III PM', value: '0.10 g/kWh Euro 3 PM' }, + ...getOptionsFromEnum(EmissionStandard), + ], + required: true, + value: null, + }, + { + name: 'smokeTestKLimitApplied', + label: 'Smoke test K limit applied', + type: FormNodeTypes.CONTROL, + validators: [{ name: ValidatorNames.MaxLength, args: 100 }], + required: true, + value: null, + }, + { + name: 'fuelType', + label: 'Fuel type', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.RADIO, + options: [ + { value: 'diesel', label: 'Diesel' }, + { value: 'gas-cng', label: 'Gas-CNG' }, + { value: 'gas-lng', label: 'Gas-LNG' }, + { value: 'gas-lpg', label: 'Gas-LPG' }, + { value: 'fuel cell', label: 'Fuel cell' }, + { value: 'petrol', label: 'Petrol' }, + { value: 'full electric', label: 'Full electric' }, + ], + required: true, + value: null, + }, + { + name: 'modType', + label: 'Modification Type', + type: FormNodeTypes.GROUP, + children: [ + { + name: 'code', + label: 'Modification code', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.RADIO, + options: [ + { value: 'p', label: 'P' }, + { value: 'm', label: 'M' }, + { value: 'g', label: 'G' }, + ], + validators: [ + { + name: ValidatorNames.HideIfParentSiblingEqual, + args: { sibling: 'modificationTypeUsed', value: 'p' }, + }, + { + name: ValidatorNames.HideIfParentSiblingNotEqual, + args: { sibling: 'particulateTrapFitted', value: 'p' }, + }, + { + name: ValidatorNames.HideIfParentSiblingNotEqual, + args: { sibling: 'particulateTrapSerialNumber', value: 'p' }, + }, + ], + }, + { + name: 'description', + label: 'Modification description', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.RADIO, + options: [ + { value: 'particulate trap', label: 'Particulate trap' }, + { value: 'modification or change of engine', label: 'Modification or change of engine' }, + { value: 'gas engine', label: 'Gas engine' }, + ], + }, + ], + }, + { + name: 'modificationTypeUsed', + label: 'Modification type used', + type: FormNodeTypes.CONTROL, + validators: [{ name: ValidatorNames.MaxLength, args: 100 }], + asyncValidators: [ + { + name: AsyncValidatorNames.RequiredIfNotResultAndSiblingEquals, + args: { testResult: ['fail', 'abandoned'], sibling: 'modType.code', value: 'm' }, + }, + { + name: AsyncValidatorNames.RequiredIfNotResultAndSiblingEquals, + args: { testResult: ['fail', 'abandoned'], sibling: 'modType.code', value: 'g' }, + }, + ], + required: true, + value: null, + }, + { + name: 'particulateTrapFitted', + label: 'Particulate trap fitted', + type: FormNodeTypes.CONTROL, + validators: [{ name: ValidatorNames.MaxLength, args: 100 }], + asyncValidators: [ + { + name: AsyncValidatorNames.RequiredIfNotResultAndSiblingEquals, + args: { testResult: ['fail', 'abandoned'], sibling: 'modType.code', value: 'p' }, + }, + ], + required: true, + value: null, + }, + { + name: 'particulateTrapSerialNumber', + label: 'Particulate trap serial number', + type: FormNodeTypes.CONTROL, + validators: [{ name: ValidatorNames.MaxLength, args: 100 }], + asyncValidators: [ + { + name: AsyncValidatorNames.RequiredIfNotResultAndSiblingEquals, + args: { testResult: ['fail', 'abandoned'], sibling: 'modType.code', value: 'p' }, + }, + ], + required: true, + value: null, + }, + ], + }, + ], + }, + ], }; diff --git a/src/app/forms/templates/test-records/section-templates/emissions/emissions-section.template.ts b/src/app/forms/templates/test-records/section-templates/emissions/emissions-section.template.ts index 1d6fbd318a..77b3a33b51 100644 --- a/src/app/forms/templates/test-records/section-templates/emissions/emissions-section.template.ts +++ b/src/app/forms/templates/test-records/section-templates/emissions/emissions-section.template.ts @@ -1,146 +1,164 @@ -import { FormNode, FormNodeTypes, FormNodeEditTypes } from '@forms/services/dynamic-form.types'; -import { ValidatorNames } from '@forms/models/validators.enum'; import { AsyncValidatorNames } from '@forms/models/async-validators.enum'; -import { EmissionStandard } from '@models/test-types/emissions.enum'; +import { ValidatorNames } from '@forms/models/validators.enum'; +import { FormNode, FormNodeEditTypes, FormNodeTypes } from '@forms/services/dynamic-form.types'; import { getOptionsFromEnum } from '@forms/utils/enum-map'; +import { EmissionStandard } from '@models/test-types/emissions.enum'; export const EmissionsSection: FormNode = { - name: 'emissionsSection', - label: 'Emissions', - type: FormNodeTypes.GROUP, - children: [ - { - name: 'testTypes', - label: 'Test Types', - type: FormNodeTypes.ARRAY, - children: [ - { - name: '0', // it is important here that the name of the node for an ARRAY type should be an index value - type: FormNodeTypes.GROUP, - children: [ - { - name: 'emissionStandard', - label: 'Emissions standard', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.RADIO, - options: [{ label: '0.10 g/kWh Euro III PM', value: '0.10 g/kWh Euro 3 PM' }, - ...getOptionsFromEnum(EmissionStandard), - ], - asyncValidators: [{ name: AsyncValidatorNames.RequiredIfNotResult, args: { testResult: ['fail', 'abandoned'] } }], - required: true, - value: null, - }, - { - name: 'smokeTestKLimitApplied', - label: 'Smoke test K limit applied', - type: FormNodeTypes.CONTROL, - validators: [{ name: ValidatorNames.MaxLength, args: 100 }], - asyncValidators: [{ name: AsyncValidatorNames.RequiredIfNotResult, args: { testResult: ['fail', 'abandoned'] } }], - required: true, - value: null, - }, - { - name: 'fuelType', - label: 'Fuel type', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.RADIO, - options: [ - { value: 'diesel', label: 'Diesel' }, - { value: 'gas-cng', label: 'Gas-CNG' }, - { value: 'gas-lng', label: 'Gas-LNG' }, - { value: 'gas-lpg', label: 'Gas-LPG' }, - { value: 'fuel cell', label: 'Fuel cell' }, - { value: 'petrol', label: 'Petrol' }, - { value: 'full electric', label: 'Full electric' }, - ], - asyncValidators: [{ name: AsyncValidatorNames.RequiredIfNotResult, args: { testResult: ['fail', 'abandoned'] } }], - required: true, - value: null, - }, - { - name: 'modType', - label: 'Modification Type', - type: FormNodeTypes.GROUP, - children: [ - { - name: 'code', - label: 'Modification code', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.RADIO, - options: [ - { value: 'p', label: 'P' }, - { value: 'm', label: 'M' }, - { value: 'g', label: 'G' }, - ], - validators: [ - { name: ValidatorNames.HideIfParentSiblingEqual, args: { sibling: 'modificationTypeUsed', value: 'p' } }, - { name: ValidatorNames.HideIfParentSiblingNotEqual, args: { sibling: 'particulateTrapFitted', value: 'p' } }, - { name: ValidatorNames.HideIfParentSiblingNotEqual, args: { sibling: 'particulateTrapSerialNumber', value: 'p' } }, - ], - asyncValidators: [{ name: AsyncValidatorNames.RequiredIfNotResult, args: { testResult: ['fail', 'abandoned'] } }], - }, - { - name: 'description', - label: 'Modification description', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.RADIO, - options: [ - { value: 'particulate trap', label: 'Particulate trap' }, - { value: 'modification or change of engine', label: 'Modification or change of engine' }, - { value: 'gas engine', label: 'Gas engine' }, - ], - }, - ], - }, - { - name: 'modificationTypeUsed', - label: 'Modification type used', - type: FormNodeTypes.CONTROL, - validators: [{ name: ValidatorNames.MaxLength, args: 100 }], - asyncValidators: [ - { - name: AsyncValidatorNames.RequiredIfNotResultAndSiblingEquals, - args: { testResult: ['fail', 'abandoned'], sibling: 'modType.code', value: 'm' }, - }, - { - name: AsyncValidatorNames.RequiredIfNotResultAndSiblingEquals, - args: { testResult: ['fail', 'abandoned'], sibling: 'modType.code', value: 'g' }, - }, - ], - required: true, - value: null, - }, - { - name: 'particulateTrapFitted', - label: 'Particulate trap fitted', - type: FormNodeTypes.CONTROL, - validators: [{ name: ValidatorNames.MaxLength, args: 100 }], - asyncValidators: [ - { - name: AsyncValidatorNames.RequiredIfNotResultAndSiblingEquals, - args: { testResult: ['fail', 'abandoned'], sibling: 'modType.code', value: 'p' }, - }, - ], - required: true, - value: null, - }, - { - name: 'particulateTrapSerialNumber', - label: 'Particulate trap serial number', - type: FormNodeTypes.CONTROL, - validators: [{ name: ValidatorNames.MaxLength, args: 100 }], - asyncValidators: [ - { - name: AsyncValidatorNames.RequiredIfNotResultAndSiblingEquals, - args: { testResult: ['fail', 'abandoned'], sibling: 'modType.code', value: 'p' }, - }, - ], - required: true, - value: null, - }, - ], - }, - ], - }, - ], + name: 'emissionsSection', + label: 'Emissions', + type: FormNodeTypes.GROUP, + children: [ + { + name: 'testTypes', + label: 'Test Types', + type: FormNodeTypes.ARRAY, + children: [ + { + name: '0', // it is important here that the name of the node for an ARRAY type should be an index value + type: FormNodeTypes.GROUP, + children: [ + { + name: 'emissionStandard', + label: 'Emissions standard', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.RADIO, + options: [ + { label: '0.10 g/kWh Euro III PM', value: '0.10 g/kWh Euro 3 PM' }, + ...getOptionsFromEnum(EmissionStandard), + ], + asyncValidators: [ + { name: AsyncValidatorNames.RequiredIfNotResult, args: { testResult: ['fail', 'abandoned'] } }, + ], + required: true, + value: null, + }, + { + name: 'smokeTestKLimitApplied', + label: 'Smoke test K limit applied', + type: FormNodeTypes.CONTROL, + validators: [{ name: ValidatorNames.MaxLength, args: 100 }], + asyncValidators: [ + { name: AsyncValidatorNames.RequiredIfNotResult, args: { testResult: ['fail', 'abandoned'] } }, + ], + required: true, + value: null, + }, + { + name: 'fuelType', + label: 'Fuel type', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.RADIO, + options: [ + { value: 'diesel', label: 'Diesel' }, + { value: 'gas-cng', label: 'Gas-CNG' }, + { value: 'gas-lng', label: 'Gas-LNG' }, + { value: 'gas-lpg', label: 'Gas-LPG' }, + { value: 'fuel cell', label: 'Fuel cell' }, + { value: 'petrol', label: 'Petrol' }, + { value: 'full electric', label: 'Full electric' }, + ], + asyncValidators: [ + { name: AsyncValidatorNames.RequiredIfNotResult, args: { testResult: ['fail', 'abandoned'] } }, + ], + required: true, + value: null, + }, + { + name: 'modType', + label: 'Modification Type', + type: FormNodeTypes.GROUP, + children: [ + { + name: 'code', + label: 'Modification code', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.RADIO, + options: [ + { value: 'p', label: 'P' }, + { value: 'm', label: 'M' }, + { value: 'g', label: 'G' }, + ], + validators: [ + { + name: ValidatorNames.HideIfParentSiblingEqual, + args: { sibling: 'modificationTypeUsed', value: 'p' }, + }, + { + name: ValidatorNames.HideIfParentSiblingNotEqual, + args: { sibling: 'particulateTrapFitted', value: 'p' }, + }, + { + name: ValidatorNames.HideIfParentSiblingNotEqual, + args: { sibling: 'particulateTrapSerialNumber', value: 'p' }, + }, + ], + asyncValidators: [ + { name: AsyncValidatorNames.RequiredIfNotResult, args: { testResult: ['fail', 'abandoned'] } }, + ], + }, + { + name: 'description', + label: 'Modification description', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.RADIO, + options: [ + { value: 'particulate trap', label: 'Particulate trap' }, + { value: 'modification or change of engine', label: 'Modification or change of engine' }, + { value: 'gas engine', label: 'Gas engine' }, + ], + }, + ], + }, + { + name: 'modificationTypeUsed', + label: 'Modification type used', + type: FormNodeTypes.CONTROL, + validators: [{ name: ValidatorNames.MaxLength, args: 100 }], + asyncValidators: [ + { + name: AsyncValidatorNames.RequiredIfNotResultAndSiblingEquals, + args: { testResult: ['fail', 'abandoned'], sibling: 'modType.code', value: 'm' }, + }, + { + name: AsyncValidatorNames.RequiredIfNotResultAndSiblingEquals, + args: { testResult: ['fail', 'abandoned'], sibling: 'modType.code', value: 'g' }, + }, + ], + required: true, + value: null, + }, + { + name: 'particulateTrapFitted', + label: 'Particulate trap fitted', + type: FormNodeTypes.CONTROL, + validators: [{ name: ValidatorNames.MaxLength, args: 100 }], + asyncValidators: [ + { + name: AsyncValidatorNames.RequiredIfNotResultAndSiblingEquals, + args: { testResult: ['fail', 'abandoned'], sibling: 'modType.code', value: 'p' }, + }, + ], + required: true, + value: null, + }, + { + name: 'particulateTrapSerialNumber', + label: 'Particulate trap serial number', + type: FormNodeTypes.CONTROL, + validators: [{ name: ValidatorNames.MaxLength, args: 100 }], + asyncValidators: [ + { + name: AsyncValidatorNames.RequiredIfNotResultAndSiblingEquals, + args: { testResult: ['fail', 'abandoned'], sibling: 'modType.code', value: 'p' }, + }, + ], + required: true, + value: null, + }, + ], + }, + ], + }, + ], }; diff --git a/src/app/forms/templates/test-records/section-templates/notes/adr-notes-section.template.ts b/src/app/forms/templates/test-records/section-templates/notes/adr-notes-section.template.ts new file mode 100644 index 0000000000..5d9fdacf6a --- /dev/null +++ b/src/app/forms/templates/test-records/section-templates/notes/adr-notes-section.template.ts @@ -0,0 +1,59 @@ +import { AsyncValidatorNames } from '@forms/models/async-validators.enum'; +import { ValidatorNames } from '@forms/models/validators.enum'; +import { CustomFormControl, FormNode, FormNodeEditTypes, FormNodeTypes } from '@forms/services/dynamic-form.types'; +import { resultOfTestEnum } from '@models/test-types/test-type.model'; +import { Store, select } from '@ngrx/store'; +import { State } from '@store/index'; +import { testResultInEdit } from '@store/test-records'; +import { map, take, tap } from 'rxjs'; + +export const AdrNotesSection: FormNode = { + name: 'notesSection', + label: 'Notes', + type: FormNodeTypes.GROUP, + children: [ + { + name: 'testTypes', + label: 'Test Types', + type: FormNodeTypes.ARRAY, + children: [ + { + name: '0', // it is important here that the name of the node for an ARRAY type should be an index value + type: FormNodeTypes.GROUP, + children: [ + { + name: 'additionalNotesRecorded', + label: 'Additional Notes', + type: FormNodeTypes.CONTROL, + value: '', + editType: FormNodeEditTypes.TEXTAREA, + validators: [{ name: ValidatorNames.MaxLength, args: 500 }], + asyncValidators: [ + // @TODO abstract into generic custom validator when used in multiple places + { + name: AsyncValidatorNames.Custom, + args: (control: CustomFormControl, store: Store) => { + return store.pipe( + select(testResultInEdit), + take(1), + map((testResult) => { + const testType = testResult?.testTypes.at(0); + const isPRS = testType?.testResult === resultOfTestEnum.prs; + const isPass = testType?.testResult === resultOfTestEnum.pass; + return testType?.centralDocs?.issueRequired && (isPRS || isPass); + }), + tap((issueRequired) => { + control.meta.hint = issueRequired ? 'Enter a reason for issuing documents centrally' : ''; + }), + map(() => null) + ); + }, + }, + ], + }, + ], + }, + ], + }, + ], +}; diff --git a/src/app/forms/templates/test-records/section-templates/notes/notes-section.template.ts b/src/app/forms/templates/test-records/section-templates/notes/notes-section.template.ts index a1ea95e1e2..0253c707c7 100644 --- a/src/app/forms/templates/test-records/section-templates/notes/notes-section.template.ts +++ b/src/app/forms/templates/test-records/section-templates/notes/notes-section.template.ts @@ -2,30 +2,30 @@ import { ValidatorNames } from '@forms/models/validators.enum'; import { FormNode, FormNodeEditTypes, FormNodeTypes } from '@forms/services/dynamic-form.types'; export const NotesSection: FormNode = { - name: 'notesSection', - label: 'Notes', - type: FormNodeTypes.GROUP, - children: [ - { - name: 'testTypes', - label: 'Test Types', - type: FormNodeTypes.ARRAY, - children: [ - { - name: '0', // it is important here that the name of the node for an ARRAY type should be an index value - type: FormNodeTypes.GROUP, - children: [ - { - name: 'additionalNotesRecorded', - label: 'Additional Notes', - type: FormNodeTypes.CONTROL, - value: '', - editType: FormNodeEditTypes.TEXTAREA, - validators: [{ name: ValidatorNames.MaxLength, args: 500 }], - }, - ], - }, - ], - }, - ], + name: 'notesSection', + label: 'Notes', + type: FormNodeTypes.GROUP, + children: [ + { + name: 'testTypes', + label: 'Test Types', + type: FormNodeTypes.ARRAY, + children: [ + { + name: '0', // it is important here that the name of the node for an ARRAY type should be an index value + type: FormNodeTypes.GROUP, + children: [ + { + name: 'additionalNotesRecorded', + label: 'Additional Notes', + type: FormNodeTypes.CONTROL, + value: '', + editType: FormNodeEditTypes.TEXTAREA, + validators: [{ name: ValidatorNames.MaxLength, args: 500 }], + }, + ], + }, + ], + }, + ], }; diff --git a/src/app/forms/templates/test-records/section-templates/reasonForCreation/reasonForCreation.template.ts b/src/app/forms/templates/test-records/section-templates/reasonForCreation/reasonForCreation.template.ts index 4021a6f413..fa7669e897 100644 --- a/src/app/forms/templates/test-records/section-templates/reasonForCreation/reasonForCreation.template.ts +++ b/src/app/forms/templates/test-records/section-templates/reasonForCreation/reasonForCreation.template.ts @@ -1,35 +1,33 @@ import { ValidatorNames } from '@forms/models/validators.enum'; -import { - FormNode, FormNodeEditTypes, FormNodeTypes, FormNodeViewTypes, -} from '@forms/services/dynamic-form.types'; +import { FormNode, FormNodeEditTypes, FormNodeTypes, FormNodeViewTypes } from '@forms/services/dynamic-form.types'; export const reasonForCreationSection: FormNode = { - name: 'reasonForCreationSection', - label: 'Reason for creation', - type: FormNodeTypes.GROUP, - children: [ - { - name: 'reasonForCreation', - label: 'Reason for creation', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.TEXTAREA, - validators: [{ name: ValidatorNames.MaxLength, args: 500 }, { name: ValidatorNames.Required }], - }, - ], + name: 'reasonForCreationSection', + label: 'Reason for creation', + type: FormNodeTypes.GROUP, + children: [ + { + name: 'reasonForCreation', + label: 'Reason for creation', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.TEXTAREA, + validators: [{ name: ValidatorNames.MaxLength, args: 500 }, { name: ValidatorNames.Required }], + }, + ], }; export const reasonForCreationHiddenSection: FormNode = { - name: 'requiredSection', - label: 'Reason for creation', - type: FormNodeTypes.GROUP, - children: [ - { - name: 'reasonForCreation', - label: 'Reason for creation', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - }, - ], + name: 'requiredSection', + label: 'Reason for creation', + type: FormNodeTypes.GROUP, + children: [ + { + name: 'reasonForCreation', + label: 'Reason for creation', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + }, + ], }; diff --git a/src/app/forms/templates/test-records/section-templates/required/contingency-required-hidden-section-hgv-trl.template.ts b/src/app/forms/templates/test-records/section-templates/required/contingency-required-hidden-section-hgv-trl.template.ts index 20a31f0fdb..5172a8b327 100644 --- a/src/app/forms/templates/test-records/section-templates/required/contingency-required-hidden-section-hgv-trl.template.ts +++ b/src/app/forms/templates/test-records/section-templates/required/contingency-required-hidden-section-hgv-trl.template.ts @@ -1,214 +1,212 @@ -import { - FormNode, FormNodeEditTypes, FormNodeTypes, FormNodeViewTypes, -} from '@forms/services/dynamic-form.types'; +import { FormNode, FormNodeEditTypes, FormNodeTypes, FormNodeViewTypes } from '@forms/services/dynamic-form.types'; export const CreateRequiredSectionHgvTrl: FormNode = { - name: 'requiredSection', - type: FormNodeTypes.GROUP, - children: [ - { - name: 'testResultId', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - }, - { - name: 'vehicleType', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'testStatus', - type: FormNodeTypes.CONTROL, - label: 'Test status', - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - options: [ - { label: 'Submitted', value: 'submitted' }, - { label: 'Cancelled', value: 'cancelled' }, - ], - }, - { - name: 'reasonForCancellation', - type: FormNodeTypes.CONTROL, - label: 'Reason for cancellation', - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'systemNumber', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'vehicleClass', - type: FormNodeTypes.GROUP, - children: [ - { - name: 'code', - customId: 'vehicleClassCode', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'description', - customId: 'vehicleClassDescription', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - ], - }, - { - name: 'vehicleType', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'noOfAxles', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'numberOfWheelsDriven', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'regnDate', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'firstUseDate', - type: FormNodeTypes.CONTROL, - label: 'First use date', - value: null, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'createdByName', - type: FormNodeTypes.CONTROL, - label: 'Created by name', - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'createdById', - type: FormNodeTypes.CONTROL, - label: 'Create by ID', - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'lastUpdatedAt', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'lastUpdatedByName', - type: FormNodeTypes.CONTROL, - label: 'Last update by?', - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'lastUpdatedById', - type: FormNodeTypes.CONTROL, - label: 'Last updated by ID', - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'shouldEmailCertificate', - type: FormNodeTypes.CONTROL, - label: 'Should email certificate?', - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - options: [ - { label: 'Yes', value: 'yes' }, - { label: 'No', value: 'no' }, - ], - }, - { - name: 'vehicleConfiguration', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'typeOfTest', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'source', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'testTypes', - label: 'Test Types', - type: FormNodeTypes.ARRAY, - children: [ - { - name: '0', // it is important here that the name of the node for an ARRAY type should be an index value - type: FormNodeTypes.GROUP, - children: [ - { - name: 'testTypeId', - type: FormNodeTypes.CONTROL, - label: 'Test Type ID', - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'testTypeName', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - }, - { - name: 'name', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'secondaryCertificateNumber', - type: FormNodeTypes.CONTROL, - label: 'Second ceritificate number', - value: null, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'certificateLink', - type: FormNodeTypes.CONTROL, - label: 'Certificate link', - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'deletionFlag', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - ], - }, - ], - }, - ], + name: 'requiredSection', + type: FormNodeTypes.GROUP, + children: [ + { + name: 'testResultId', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + }, + { + name: 'vehicleType', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'testStatus', + type: FormNodeTypes.CONTROL, + label: 'Test status', + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + options: [ + { label: 'Submitted', value: 'submitted' }, + { label: 'Cancelled', value: 'cancelled' }, + ], + }, + { + name: 'reasonForCancellation', + type: FormNodeTypes.CONTROL, + label: 'Reason for cancellation', + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'systemNumber', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'vehicleClass', + type: FormNodeTypes.GROUP, + children: [ + { + name: 'code', + customId: 'vehicleClassCode', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'description', + customId: 'vehicleClassDescription', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + ], + }, + { + name: 'vehicleType', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'noOfAxles', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'numberOfWheelsDriven', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'regnDate', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'firstUseDate', + type: FormNodeTypes.CONTROL, + label: 'First use date', + value: null, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'createdByName', + type: FormNodeTypes.CONTROL, + label: 'Created by name', + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'createdById', + type: FormNodeTypes.CONTROL, + label: 'Create by ID', + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'lastUpdatedAt', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'lastUpdatedByName', + type: FormNodeTypes.CONTROL, + label: 'Last update by?', + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'lastUpdatedById', + type: FormNodeTypes.CONTROL, + label: 'Last updated by ID', + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'shouldEmailCertificate', + type: FormNodeTypes.CONTROL, + label: 'Should email certificate?', + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + options: [ + { label: 'Yes', value: 'yes' }, + { label: 'No', value: 'no' }, + ], + }, + { + name: 'vehicleConfiguration', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'typeOfTest', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'source', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'testTypes', + label: 'Test Types', + type: FormNodeTypes.ARRAY, + children: [ + { + name: '0', // it is important here that the name of the node for an ARRAY type should be an index value + type: FormNodeTypes.GROUP, + children: [ + { + name: 'testTypeId', + type: FormNodeTypes.CONTROL, + label: 'Test Type ID', + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'testTypeName', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + }, + { + name: 'name', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'secondaryCertificateNumber', + type: FormNodeTypes.CONTROL, + label: 'Second ceritificate number', + value: null, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'certificateLink', + type: FormNodeTypes.CONTROL, + label: 'Certificate link', + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'deletionFlag', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + ], + }, + ], + }, + ], }; diff --git a/src/app/forms/templates/test-records/section-templates/required/contingency-required-hidden-section-lgv-car.template.ts b/src/app/forms/templates/test-records/section-templates/required/contingency-required-hidden-section-lgv-car.template.ts index 2c98525180..1782b09787 100644 --- a/src/app/forms/templates/test-records/section-templates/required/contingency-required-hidden-section-lgv-car.template.ts +++ b/src/app/forms/templates/test-records/section-templates/required/contingency-required-hidden-section-lgv-car.template.ts @@ -1,225 +1,223 @@ -import { - FormNode, FormNodeEditTypes, FormNodeTypes, FormNodeViewTypes, -} from '@forms/services/dynamic-form.types'; +import { FormNode, FormNodeEditTypes, FormNodeTypes, FormNodeViewTypes } from '@forms/services/dynamic-form.types'; export const CreateRequiredSectionLgvCar: FormNode = { - name: 'requiredSection', - type: FormNodeTypes.GROUP, - children: [ - { - name: 'testResultId', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - }, - { - name: 'vehicleType', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'testStatus', - type: FormNodeTypes.CONTROL, - label: 'Test status', - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - }, - { - name: 'reasonForCancellation', - type: FormNodeTypes.CONTROL, - label: 'Reason for cancellation', - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'systemNumber', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'vehicleClass', - type: FormNodeTypes.GROUP, - children: [ - { - name: 'code', - customId: 'vehicleClassCode', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'description', - customId: 'vehicleClassDescription', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - ], - }, - { - name: 'vehicleSubclass', - type: FormNodeTypes.ARRAY, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - children: [ - { - name: '0', - type: FormNodeTypes.CONTROL, - }, - ], - }, - { - name: 'vehicleType', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'noOfAxles', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'numberOfWheelsDriven', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'regnDate', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'firstUseDate', - type: FormNodeTypes.CONTROL, - label: 'First use date', - value: null, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'createdByName', - type: FormNodeTypes.CONTROL, - label: 'Created by name', - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'createdById', - type: FormNodeTypes.CONTROL, - label: 'Create by ID', - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'lastUpdatedAt', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'lastUpdatedByName', - type: FormNodeTypes.CONTROL, - label: 'Last update by?', - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'lastUpdatedById', - type: FormNodeTypes.CONTROL, - label: 'Last updated by ID', - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'shouldEmailCertificate', - type: FormNodeTypes.CONTROL, - label: 'Should email certificate?', - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - }, - { - name: 'vehicleConfiguration', - value: null, - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'typeOfTest', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'source', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'vehicleSize', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'testTypes', - label: 'Test Types', - type: FormNodeTypes.ARRAY, - children: [ - { - name: '0', // it is important here that the name of the node for an ARRAY type should be an index value - type: FormNodeTypes.GROUP, - children: [ - { - name: 'testTypeId', - type: FormNodeTypes.CONTROL, - label: 'Test Type ID', - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'testTypeName', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - }, - { - name: 'name', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'secondaryCertificateNumber', - type: FormNodeTypes.CONTROL, - label: 'Second ceritificate number', - value: null, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'certificateLink', - type: FormNodeTypes.CONTROL, - label: 'Certificate link', - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'deletionFlag', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - ], - }, - ], - }, - ], + name: 'requiredSection', + type: FormNodeTypes.GROUP, + children: [ + { + name: 'testResultId', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + }, + { + name: 'vehicleType', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'testStatus', + type: FormNodeTypes.CONTROL, + label: 'Test status', + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + }, + { + name: 'reasonForCancellation', + type: FormNodeTypes.CONTROL, + label: 'Reason for cancellation', + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'systemNumber', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'vehicleClass', + type: FormNodeTypes.GROUP, + children: [ + { + name: 'code', + customId: 'vehicleClassCode', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'description', + customId: 'vehicleClassDescription', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + ], + }, + { + name: 'vehicleSubclass', + type: FormNodeTypes.ARRAY, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + children: [ + { + name: '0', + type: FormNodeTypes.CONTROL, + }, + ], + }, + { + name: 'vehicleType', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'noOfAxles', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'numberOfWheelsDriven', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'regnDate', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'firstUseDate', + type: FormNodeTypes.CONTROL, + label: 'First use date', + value: null, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'createdByName', + type: FormNodeTypes.CONTROL, + label: 'Created by name', + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'createdById', + type: FormNodeTypes.CONTROL, + label: 'Create by ID', + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'lastUpdatedAt', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'lastUpdatedByName', + type: FormNodeTypes.CONTROL, + label: 'Last update by?', + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'lastUpdatedById', + type: FormNodeTypes.CONTROL, + label: 'Last updated by ID', + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'shouldEmailCertificate', + type: FormNodeTypes.CONTROL, + label: 'Should email certificate?', + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + }, + { + name: 'vehicleConfiguration', + value: null, + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'typeOfTest', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'source', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'vehicleSize', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'testTypes', + label: 'Test Types', + type: FormNodeTypes.ARRAY, + children: [ + { + name: '0', // it is important here that the name of the node for an ARRAY type should be an index value + type: FormNodeTypes.GROUP, + children: [ + { + name: 'testTypeId', + type: FormNodeTypes.CONTROL, + label: 'Test Type ID', + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'testTypeName', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + }, + { + name: 'name', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'secondaryCertificateNumber', + type: FormNodeTypes.CONTROL, + label: 'Second ceritificate number', + value: null, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'certificateLink', + type: FormNodeTypes.CONTROL, + label: 'Certificate link', + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'deletionFlag', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + ], + }, + ], + }, + ], }; diff --git a/src/app/forms/templates/test-records/section-templates/required/contingency-required-hidden-section-motorcycle.template.ts b/src/app/forms/templates/test-records/section-templates/required/contingency-required-hidden-section-motorcycle.template.ts index 72427ba09b..ffc717355f 100644 --- a/src/app/forms/templates/test-records/section-templates/required/contingency-required-hidden-section-motorcycle.template.ts +++ b/src/app/forms/templates/test-records/section-templates/required/contingency-required-hidden-section-motorcycle.template.ts @@ -1,213 +1,211 @@ -import { - FormNode, FormNodeEditTypes, FormNodeTypes, FormNodeViewTypes, -} from '@forms/services/dynamic-form.types'; +import { FormNode, FormNodeEditTypes, FormNodeTypes, FormNodeViewTypes } from '@forms/services/dynamic-form.types'; export const CreateRequiredSectionMotorcycle: FormNode = { - name: 'requiredSection', - type: FormNodeTypes.GROUP, - children: [ - { - name: 'testResultId', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - }, - { - name: 'vehicleType', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'testStatus', - type: FormNodeTypes.CONTROL, - label: 'Test status', - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - }, - { - name: 'reasonForCancellation', - type: FormNodeTypes.CONTROL, - label: 'Reason for cancellation', - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'systemNumber', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'vehicleClass', - type: FormNodeTypes.GROUP, - children: [ - { - name: 'code', - customId: 'vehicleClassCode', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'description', - customId: 'vehicleClassDescription', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - ], - }, - { - name: 'vehicleType', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'noOfAxles', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'numberOfWheelsDriven', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'regnDate', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'firstUseDate', - type: FormNodeTypes.CONTROL, - label: 'First use date', - value: null, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'createdByName', - type: FormNodeTypes.CONTROL, - label: 'Created by name', - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'createdById', - type: FormNodeTypes.CONTROL, - label: 'Create by ID', - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'lastUpdatedAt', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'lastUpdatedByName', - type: FormNodeTypes.CONTROL, - label: 'Last update by?', - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'lastUpdatedById', - type: FormNodeTypes.CONTROL, - label: 'Last updated by ID', - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'shouldEmailCertificate', - type: FormNodeTypes.CONTROL, - label: 'Should email certificate?', - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - }, - { - name: 'vehicleConfiguration', - value: null, - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'typeOfTest', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'source', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'vehicleSize', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'testTypes', - label: 'Test Types', - type: FormNodeTypes.ARRAY, - children: [ - { - name: '0', // it is important here that the name of the node for an ARRAY type should be an index value - type: FormNodeTypes.GROUP, - children: [ - { - name: 'testTypeId', - type: FormNodeTypes.CONTROL, - label: 'Test Type ID', - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'testTypeName', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - }, - { - name: 'name', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'secondaryCertificateNumber', - type: FormNodeTypes.CONTROL, - label: 'Second ceritificate number', - value: null, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'certificateLink', - type: FormNodeTypes.CONTROL, - label: 'Certificate link', - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'deletionFlag', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - ], - }, - ], - }, - ], + name: 'requiredSection', + type: FormNodeTypes.GROUP, + children: [ + { + name: 'testResultId', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + }, + { + name: 'vehicleType', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'testStatus', + type: FormNodeTypes.CONTROL, + label: 'Test status', + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + }, + { + name: 'reasonForCancellation', + type: FormNodeTypes.CONTROL, + label: 'Reason for cancellation', + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'systemNumber', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'vehicleClass', + type: FormNodeTypes.GROUP, + children: [ + { + name: 'code', + customId: 'vehicleClassCode', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'description', + customId: 'vehicleClassDescription', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + ], + }, + { + name: 'vehicleType', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'noOfAxles', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'numberOfWheelsDriven', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'regnDate', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'firstUseDate', + type: FormNodeTypes.CONTROL, + label: 'First use date', + value: null, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'createdByName', + type: FormNodeTypes.CONTROL, + label: 'Created by name', + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'createdById', + type: FormNodeTypes.CONTROL, + label: 'Create by ID', + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'lastUpdatedAt', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'lastUpdatedByName', + type: FormNodeTypes.CONTROL, + label: 'Last update by?', + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'lastUpdatedById', + type: FormNodeTypes.CONTROL, + label: 'Last updated by ID', + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'shouldEmailCertificate', + type: FormNodeTypes.CONTROL, + label: 'Should email certificate?', + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + }, + { + name: 'vehicleConfiguration', + value: null, + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'typeOfTest', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'source', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'vehicleSize', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'testTypes', + label: 'Test Types', + type: FormNodeTypes.ARRAY, + children: [ + { + name: '0', // it is important here that the name of the node for an ARRAY type should be an index value + type: FormNodeTypes.GROUP, + children: [ + { + name: 'testTypeId', + type: FormNodeTypes.CONTROL, + label: 'Test Type ID', + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'testTypeName', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + }, + { + name: 'name', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'secondaryCertificateNumber', + type: FormNodeTypes.CONTROL, + label: 'Second ceritificate number', + value: null, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'certificateLink', + type: FormNodeTypes.CONTROL, + label: 'Certificate link', + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'deletionFlag', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + ], + }, + ], + }, + ], }; diff --git a/src/app/forms/templates/test-records/section-templates/required/contingency-required-hidden-section.template.ts b/src/app/forms/templates/test-records/section-templates/required/contingency-required-hidden-section.template.ts index b1c2555aa1..553a9ab812 100644 --- a/src/app/forms/templates/test-records/section-templates/required/contingency-required-hidden-section.template.ts +++ b/src/app/forms/templates/test-records/section-templates/required/contingency-required-hidden-section.template.ts @@ -1,226 +1,224 @@ -import { - FormNode, FormNodeEditTypes, FormNodeTypes, FormNodeViewTypes, -} from '@forms/services/dynamic-form.types'; +import { FormNode, FormNodeEditTypes, FormNodeTypes, FormNodeViewTypes } from '@forms/services/dynamic-form.types'; export const CreateRequiredSection: FormNode = { - name: 'requiredSection', - type: FormNodeTypes.GROUP, - children: [ - { - name: 'testResultId', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - }, - { - name: 'vehicleType', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'testStatus', - type: FormNodeTypes.CONTROL, - label: 'Test status', - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - options: [ - { label: 'Submitted', value: 'submitted' }, - { label: 'Cancelled', value: 'cancelled' }, - ], - }, - { - name: 'reasonForCancellation', - type: FormNodeTypes.CONTROL, - label: 'Reason for cancellation', - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'systemNumber', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'vehicleClass', - type: FormNodeTypes.GROUP, - children: [ - { - name: 'code', - customId: 'vehicleClassCode', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'description', - customId: 'vehicleClassDescription', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - ], - }, - { - name: 'vehicleType', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'noOfAxles', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'numberOfWheelsDriven', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'regnDate', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'firstUseDate', - type: FormNodeTypes.CONTROL, - label: 'First use date', - value: null, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'createdByName', - type: FormNodeTypes.CONTROL, - label: 'Created by name', - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'createdById', - type: FormNodeTypes.CONTROL, - label: 'Create by ID', - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'lastUpdatedAt', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'lastUpdatedByName', - type: FormNodeTypes.CONTROL, - label: 'Last update by?', - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'lastUpdatedById', - type: FormNodeTypes.CONTROL, - label: 'Last updated by ID', - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'shouldEmailCertificate', - type: FormNodeTypes.CONTROL, - label: 'Should email certificate?', - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - options: [ - { label: 'Yes', value: 'yes' }, - { label: 'No', value: 'no' }, - ], - }, - { - name: 'numberOfSeats', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'vehicleConfiguration', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'typeOfTest', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'source', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'vehicleSize', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'testTypes', - label: 'Test Types', - type: FormNodeTypes.ARRAY, - children: [ - { - name: '0', // it is important here that the name of the node for an ARRAY type should be an index value - type: FormNodeTypes.GROUP, - children: [ - { - name: 'testTypeId', - type: FormNodeTypes.CONTROL, - label: 'Test Type ID', - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'testTypeName', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - }, - { - name: 'name', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'secondaryCertificateNumber', - type: FormNodeTypes.CONTROL, - label: 'Second ceritificate number', - value: null, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'certificateLink', - type: FormNodeTypes.CONTROL, - label: 'Certificate link', - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'deletionFlag', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - ], - }, - ], - }, - ], + name: 'requiredSection', + type: FormNodeTypes.GROUP, + children: [ + { + name: 'testResultId', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + }, + { + name: 'vehicleType', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'testStatus', + type: FormNodeTypes.CONTROL, + label: 'Test status', + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + options: [ + { label: 'Submitted', value: 'submitted' }, + { label: 'Cancelled', value: 'cancelled' }, + ], + }, + { + name: 'reasonForCancellation', + type: FormNodeTypes.CONTROL, + label: 'Reason for cancellation', + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'systemNumber', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'vehicleClass', + type: FormNodeTypes.GROUP, + children: [ + { + name: 'code', + customId: 'vehicleClassCode', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'description', + customId: 'vehicleClassDescription', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + ], + }, + { + name: 'vehicleType', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'noOfAxles', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'numberOfWheelsDriven', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'regnDate', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'firstUseDate', + type: FormNodeTypes.CONTROL, + label: 'First use date', + value: null, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'createdByName', + type: FormNodeTypes.CONTROL, + label: 'Created by name', + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'createdById', + type: FormNodeTypes.CONTROL, + label: 'Create by ID', + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'lastUpdatedAt', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'lastUpdatedByName', + type: FormNodeTypes.CONTROL, + label: 'Last update by?', + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'lastUpdatedById', + type: FormNodeTypes.CONTROL, + label: 'Last updated by ID', + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'shouldEmailCertificate', + type: FormNodeTypes.CONTROL, + label: 'Should email certificate?', + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + options: [ + { label: 'Yes', value: 'yes' }, + { label: 'No', value: 'no' }, + ], + }, + { + name: 'numberOfSeats', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'vehicleConfiguration', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'typeOfTest', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'source', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'vehicleSize', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'testTypes', + label: 'Test Types', + type: FormNodeTypes.ARRAY, + children: [ + { + name: '0', // it is important here that the name of the node for an ARRAY type should be an index value + type: FormNodeTypes.GROUP, + children: [ + { + name: 'testTypeId', + type: FormNodeTypes.CONTROL, + label: 'Test Type ID', + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'testTypeName', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + }, + { + name: 'name', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'secondaryCertificateNumber', + type: FormNodeTypes.CONTROL, + label: 'Second ceritificate number', + value: null, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'certificateLink', + type: FormNodeTypes.CONTROL, + label: 'Certificate link', + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'deletionFlag', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + ], + }, + ], + }, + ], }; diff --git a/src/app/forms/templates/test-records/section-templates/required/custom-defects-hidden-section.template.ts b/src/app/forms/templates/test-records/section-templates/required/custom-defects-hidden-section.template.ts index d8660da509..c2520add45 100644 --- a/src/app/forms/templates/test-records/section-templates/required/custom-defects-hidden-section.template.ts +++ b/src/app/forms/templates/test-records/section-templates/required/custom-defects-hidden-section.template.ts @@ -1,29 +1,27 @@ -import { - FormNode, FormNodeEditTypes, FormNodeTypes, FormNodeViewTypes, -} from '@forms/services/dynamic-form.types'; +import { FormNode, FormNodeEditTypes, FormNodeTypes, FormNodeViewTypes } from '@forms/services/dynamic-form.types'; export const CustomDefectsHiddenSection: FormNode = { - name: 'requiredSection', - type: FormNodeTypes.GROUP, - children: [ - { - name: 'testTypes', - label: 'Test Types', - type: FormNodeTypes.ARRAY, - children: [ - { - name: '0', // it is important here that the name of the node for an ARRAY type should be an index value - type: FormNodeTypes.GROUP, - children: [ - { - name: 'customDefects', - type: FormNodeTypes.ARRAY, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - }, - ], - }, - ], - }, - ], + name: 'requiredSection', + type: FormNodeTypes.GROUP, + children: [ + { + name: 'testTypes', + label: 'Test Types', + type: FormNodeTypes.ARRAY, + children: [ + { + name: '0', // it is important here that the name of the node for an ARRAY type should be an index value + type: FormNodeTypes.GROUP, + children: [ + { + name: 'customDefects', + type: FormNodeTypes.ARRAY, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + }, + ], + }, + ], + }, + ], }; diff --git a/src/app/forms/templates/test-records/section-templates/required/defect-hidden-section.template.ts b/src/app/forms/templates/test-records/section-templates/required/defect-hidden-section.template.ts index 7c3719e283..840d86c208 100644 --- a/src/app/forms/templates/test-records/section-templates/required/defect-hidden-section.template.ts +++ b/src/app/forms/templates/test-records/section-templates/required/defect-hidden-section.template.ts @@ -1,29 +1,27 @@ -import { - FormNode, FormNodeEditTypes, FormNodeTypes, FormNodeViewTypes, -} from '@forms/services/dynamic-form.types'; +import { FormNode, FormNodeEditTypes, FormNodeTypes, FormNodeViewTypes } from '@forms/services/dynamic-form.types'; export const defectsHiddenSection: FormNode = { - name: 'requiredSection', - type: FormNodeTypes.GROUP, - children: [ - { - name: 'testTypes', - label: 'Test Types', - type: FormNodeTypes.ARRAY, - children: [ - { - name: '0', // it is important here that the name of the node for an ARRAY type should be an index value - type: FormNodeTypes.GROUP, - children: [ - { - name: 'defects', - type: FormNodeTypes.ARRAY, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - }, - ], - }, - ], - }, - ], + name: 'requiredSection', + type: FormNodeTypes.GROUP, + children: [ + { + name: 'testTypes', + label: 'Test Types', + type: FormNodeTypes.ARRAY, + children: [ + { + name: '0', // it is important here that the name of the node for an ARRAY type should be an index value + type: FormNodeTypes.GROUP, + children: [ + { + name: 'defects', + type: FormNodeTypes.ARRAY, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + }, + ], + }, + ], + }, + ], }; diff --git a/src/app/forms/templates/test-records/section-templates/required/desk-based-required-hidden-group4-lgv.template.ts b/src/app/forms/templates/test-records/section-templates/required/desk-based-required-hidden-group4-lgv.template.ts index e752bac584..dd14878fc0 100644 --- a/src/app/forms/templates/test-records/section-templates/required/desk-based-required-hidden-group4-lgv.template.ts +++ b/src/app/forms/templates/test-records/section-templates/required/desk-based-required-hidden-group4-lgv.template.ts @@ -1,270 +1,268 @@ -import { - FormNode, FormNodeEditTypes, FormNodeTypes, FormNodeViewTypes, -} from '@forms/services/dynamic-form.types'; +import { FormNode, FormNodeEditTypes, FormNodeTypes, FormNodeViewTypes } from '@forms/services/dynamic-form.types'; export const DeskBasedRequiredHiddenSectionGroup4And5Lgv: FormNode = { - name: 'requiredSection', - type: FormNodeTypes.GROUP, - children: [ - { - name: 'testResultId', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - }, - { - name: 'vehicleType', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'contingencyTestNumber', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'typeOfTest', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'source', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'testStatus', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'systemNumber', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'testerStaffId', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'testEndTimestamp', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'vehicleClass', - type: FormNodeTypes.GROUP, - editType: FormNodeEditTypes.HIDDEN, + name: 'requiredSection', + type: FormNodeTypes.GROUP, + children: [ + { + name: 'testResultId', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + }, + { + name: 'vehicleType', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'contingencyTestNumber', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'typeOfTest', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'source', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'testStatus', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'systemNumber', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'testerStaffId', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'testEndTimestamp', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'vehicleClass', + type: FormNodeTypes.GROUP, + editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - children: [ - { - name: 'code', - customId: 'vehicleClassCode', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'description', - customId: 'vehicleClassDescription', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - ], - }, - { - name: 'vehicleSubclass', - type: FormNodeTypes.ARRAY, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - children: [ - { - name: '0', - type: FormNodeTypes.CONTROL, - }, - ], - }, - { - name: 'vehicleType', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'noOfAxles', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'numberOfWheelsDriven', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'regnDate', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'firstUseDate', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'createdByName', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'createdById', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'lastUpdatedAt', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'lastUpdatedByName', + viewType: FormNodeViewTypes.HIDDEN, + children: [ + { + name: 'code', + customId: 'vehicleClassCode', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'description', + customId: 'vehicleClassDescription', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + ], + }, + { + name: 'vehicleSubclass', + type: FormNodeTypes.ARRAY, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + children: [ + { + name: '0', + type: FormNodeTypes.CONTROL, + }, + ], + }, + { + name: 'vehicleType', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'noOfAxles', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'numberOfWheelsDriven', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'regnDate', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'firstUseDate', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'createdByName', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'createdById', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'lastUpdatedAt', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'lastUpdatedByName', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'lastUpdatedById', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'lastUpdatedById', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'shouldEmailCertificate', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'vehicleConfiguration', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'reasonForCancellation', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'testTypes', - label: 'Test Types', - type: FormNodeTypes.ARRAY, - children: [ - { - name: '0', // it is important here that the name of the node for an ARRAY type should be an index value - type: FormNodeTypes.GROUP, - children: [ - { - name: 'testTypeId', - label: 'Test Type ID', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'testTypeName', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - }, - { - name: 'prohibitionIssued', - type: FormNodeTypes.CONTROL, - value: null, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'name', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'createdAt', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'lastUpdatedAt', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'certificateLink', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'deletionFlag', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'testNumber', - label: 'Test Number', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - }, - { - name: 'reasonForAbandoning', - type: FormNodeTypes.CONTROL, - value: null, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'additionalCommentsForAbandon', - type: FormNodeTypes.CONTROL, - value: null, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'secondaryCertificateNumber', - type: FormNodeTypes.CONTROL, - value: null, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - ], - }, - ], - }, - ], + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'shouldEmailCertificate', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'vehicleConfiguration', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'reasonForCancellation', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'testTypes', + label: 'Test Types', + type: FormNodeTypes.ARRAY, + children: [ + { + name: '0', // it is important here that the name of the node for an ARRAY type should be an index value + type: FormNodeTypes.GROUP, + children: [ + { + name: 'testTypeId', + label: 'Test Type ID', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'testTypeName', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + }, + { + name: 'prohibitionIssued', + type: FormNodeTypes.CONTROL, + value: null, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'name', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'createdAt', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'lastUpdatedAt', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'certificateLink', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'deletionFlag', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'testNumber', + label: 'Test Number', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + }, + { + name: 'reasonForAbandoning', + type: FormNodeTypes.CONTROL, + value: null, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'additionalCommentsForAbandon', + type: FormNodeTypes.CONTROL, + value: null, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'secondaryCertificateNumber', + type: FormNodeTypes.CONTROL, + value: null, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + ], + }, + ], + }, + ], }; diff --git a/src/app/forms/templates/test-records/section-templates/required/desk-based-required-hidden-group4-motorcycle-amend.template.ts b/src/app/forms/templates/test-records/section-templates/required/desk-based-required-hidden-group4-motorcycle-amend.template.ts index 972e568ddd..2f7725dbfd 100644 --- a/src/app/forms/templates/test-records/section-templates/required/desk-based-required-hidden-group4-motorcycle-amend.template.ts +++ b/src/app/forms/templates/test-records/section-templates/required/desk-based-required-hidden-group4-motorcycle-amend.template.ts @@ -1,244 +1,242 @@ -import { - FormNode, FormNodeEditTypes, FormNodeTypes, FormNodeViewTypes, -} from '@forms/services/dynamic-form.types'; +import { FormNode, FormNodeEditTypes, FormNodeTypes, FormNodeViewTypes } from '@forms/services/dynamic-form.types'; export const AmendDeskBasedRequiredHiddenSectionGroup4Motorcyle: FormNode = { - name: 'requiredSection', - type: FormNodeTypes.GROUP, - children: [ - { - name: 'testResultId', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - }, - { - name: 'vehicleType', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'contingencyTestNumber', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'typeOfTest', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'source', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'testStatus', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'systemNumber', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'testerStaffId', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'testEndTimestamp', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'vehicleClass', - type: FormNodeTypes.GROUP, - editType: FormNodeEditTypes.HIDDEN, + name: 'requiredSection', + type: FormNodeTypes.GROUP, + children: [ + { + name: 'testResultId', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + }, + { + name: 'vehicleType', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'contingencyTestNumber', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'typeOfTest', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'source', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'testStatus', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'systemNumber', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'testerStaffId', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'testEndTimestamp', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'vehicleClass', + type: FormNodeTypes.GROUP, + editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - children: [ - { - name: 'code', - customId: 'vehicleClassCode', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'description', - customId: 'vehicleClassDescription', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - ], - }, - { - name: 'vehicleType', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'noOfAxles', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'numberOfWheelsDriven', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'regnDate', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'firstUseDate', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'createdByName', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'createdById', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'lastUpdatedAt', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'lastUpdatedByName', + viewType: FormNodeViewTypes.HIDDEN, + children: [ + { + name: 'code', + customId: 'vehicleClassCode', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'description', + customId: 'vehicleClassDescription', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + ], + }, + { + name: 'vehicleType', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'noOfAxles', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'numberOfWheelsDriven', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'regnDate', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'firstUseDate', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'createdByName', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'createdById', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'lastUpdatedAt', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'lastUpdatedByName', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'lastUpdatedById', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'lastUpdatedById', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'shouldEmailCertificate', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'vehicleConfiguration', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'reasonForCancellation', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'testTypes', - label: 'Test Types', - type: FormNodeTypes.ARRAY, - children: [ - { - name: '0', // it is important here that the name of the node for an ARRAY type should be an index value - type: FormNodeTypes.GROUP, - children: [ - { - name: 'testTypeId', - label: 'Test Type ID', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'testTypeName', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - }, - { - name: 'name', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'createdAt', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'lastUpdatedAt', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'certificateLink', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'testTypeClassification', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - value: null, - }, - { - name: 'deletionFlag', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'testNumber', - label: 'Test Number', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - }, - { - name: 'secondaryCertificateNumber', - type: FormNodeTypes.CONTROL, - value: null, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - ], - }, - ], - }, - ], + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'shouldEmailCertificate', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'vehicleConfiguration', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'reasonForCancellation', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'testTypes', + label: 'Test Types', + type: FormNodeTypes.ARRAY, + children: [ + { + name: '0', // it is important here that the name of the node for an ARRAY type should be an index value + type: FormNodeTypes.GROUP, + children: [ + { + name: 'testTypeId', + label: 'Test Type ID', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'testTypeName', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + }, + { + name: 'name', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'createdAt', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'lastUpdatedAt', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'certificateLink', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'testTypeClassification', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + value: null, + }, + { + name: 'deletionFlag', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'testNumber', + label: 'Test Number', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + }, + { + name: 'secondaryCertificateNumber', + type: FormNodeTypes.CONTROL, + value: null, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + ], + }, + ], + }, + ], }; diff --git a/src/app/forms/templates/test-records/section-templates/required/desk-based-required-hidden-group4-motorcycle.template.ts b/src/app/forms/templates/test-records/section-templates/required/desk-based-required-hidden-group4-motorcycle.template.ts index 5332f19e9c..04b613f056 100644 --- a/src/app/forms/templates/test-records/section-templates/required/desk-based-required-hidden-group4-motorcycle.template.ts +++ b/src/app/forms/templates/test-records/section-templates/required/desk-based-required-hidden-group4-motorcycle.template.ts @@ -1,258 +1,256 @@ -import { - FormNode, FormNodeEditTypes, FormNodeTypes, FormNodeViewTypes, -} from '@forms/services/dynamic-form.types'; +import { FormNode, FormNodeEditTypes, FormNodeTypes, FormNodeViewTypes } from '@forms/services/dynamic-form.types'; export const DeskBasedRequiredHiddenSectionGroup4Motorcyle: FormNode = { - name: 'requiredSection', - type: FormNodeTypes.GROUP, - children: [ - { - name: 'testResultId', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - }, - { - name: 'vehicleType', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'contingencyTestNumber', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'typeOfTest', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'source', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'testStatus', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'systemNumber', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'testerStaffId', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'testEndTimestamp', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'vehicleClass', - type: FormNodeTypes.GROUP, - editType: FormNodeEditTypes.HIDDEN, + name: 'requiredSection', + type: FormNodeTypes.GROUP, + children: [ + { + name: 'testResultId', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + }, + { + name: 'vehicleType', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'contingencyTestNumber', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'typeOfTest', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'source', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'testStatus', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'systemNumber', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'testerStaffId', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'testEndTimestamp', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'vehicleClass', + type: FormNodeTypes.GROUP, + editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - children: [ - { - name: 'code', - customId: 'vehicleClassCode', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'description', - customId: 'vehicleClassDescription', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - ], - }, - { - name: 'vehicleType', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'noOfAxles', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'numberOfWheelsDriven', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'regnDate', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'firstUseDate', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'createdByName', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'createdById', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'lastUpdatedAt', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'lastUpdatedByName', + viewType: FormNodeViewTypes.HIDDEN, + children: [ + { + name: 'code', + customId: 'vehicleClassCode', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'description', + customId: 'vehicleClassDescription', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + ], + }, + { + name: 'vehicleType', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'noOfAxles', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'numberOfWheelsDriven', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'regnDate', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'firstUseDate', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'createdByName', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'createdById', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'lastUpdatedAt', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'lastUpdatedByName', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'lastUpdatedById', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'lastUpdatedById', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'shouldEmailCertificate', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'vehicleConfiguration', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'reasonForCancellation', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'testTypes', - label: 'Test Types', - type: FormNodeTypes.ARRAY, - children: [ - { - name: '0', // it is important here that the name of the node for an ARRAY type should be an index value - type: FormNodeTypes.GROUP, - children: [ - { - name: 'testTypeId', - label: 'Test Type ID', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'testTypeName', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - }, - { - name: 'prohibitionIssued', - type: FormNodeTypes.CONTROL, - value: null, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'name', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'createdAt', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'lastUpdatedAt', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'certificateLink', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'deletionFlag', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'testNumber', - label: 'Test Number', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - }, - { - name: 'reasonForAbandoning', - type: FormNodeTypes.CONTROL, - value: null, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'additionalCommentsForAbandon', - type: FormNodeTypes.CONTROL, - value: null, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'secondaryCertificateNumber', - type: FormNodeTypes.CONTROL, - value: null, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - ], - }, - ], - }, - ], + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'shouldEmailCertificate', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'vehicleConfiguration', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'reasonForCancellation', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'testTypes', + label: 'Test Types', + type: FormNodeTypes.ARRAY, + children: [ + { + name: '0', // it is important here that the name of the node for an ARRAY type should be an index value + type: FormNodeTypes.GROUP, + children: [ + { + name: 'testTypeId', + label: 'Test Type ID', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'testTypeName', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + }, + { + name: 'prohibitionIssued', + type: FormNodeTypes.CONTROL, + value: null, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'name', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'createdAt', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'lastUpdatedAt', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'certificateLink', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'deletionFlag', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'testNumber', + label: 'Test Number', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + }, + { + name: 'reasonForAbandoning', + type: FormNodeTypes.CONTROL, + value: null, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'additionalCommentsForAbandon', + type: FormNodeTypes.CONTROL, + value: null, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'secondaryCertificateNumber', + type: FormNodeTypes.CONTROL, + value: null, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + ], + }, + ], + }, + ], }; diff --git a/src/app/forms/templates/test-records/section-templates/required/desk-based-required-hidden-group5-lgv.template.ts b/src/app/forms/templates/test-records/section-templates/required/desk-based-required-hidden-group5-lgv.template.ts index 525ec4ca44..fe89b9d195 100644 --- a/src/app/forms/templates/test-records/section-templates/required/desk-based-required-hidden-group5-lgv.template.ts +++ b/src/app/forms/templates/test-records/section-templates/required/desk-based-required-hidden-group5-lgv.template.ts @@ -1,244 +1,242 @@ -import { - FormNode, FormNodeEditTypes, FormNodeTypes, FormNodeViewTypes, -} from '@forms/services/dynamic-form.types'; +import { FormNode, FormNodeEditTypes, FormNodeTypes, FormNodeViewTypes } from '@forms/services/dynamic-form.types'; export const DeskBasedRequiredHiddenSectionGroup5Lgv: FormNode = { - name: 'requiredSection', - type: FormNodeTypes.GROUP, - children: [ - { - name: 'testResultId', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - }, - { - name: 'vehicleType', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'contingencyTestNumber', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'typeOfTest', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'source', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'testStatus', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'systemNumber', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'testerStaffId', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'testEndTimestamp', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'vehicleClass', - type: FormNodeTypes.GROUP, - editType: FormNodeEditTypes.HIDDEN, + name: 'requiredSection', + type: FormNodeTypes.GROUP, + children: [ + { + name: 'testResultId', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + }, + { + name: 'vehicleType', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'contingencyTestNumber', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'typeOfTest', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'source', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'testStatus', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'systemNumber', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'testerStaffId', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'testEndTimestamp', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'vehicleClass', + type: FormNodeTypes.GROUP, + editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - children: [ - { - name: 'code', - customId: 'vehicleClassCode', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'description', - customId: 'vehicleClassDescription', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - ], - }, - { - name: 'vehicleSubclass', - type: FormNodeTypes.ARRAY, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - children: [ - { - name: '0', - type: FormNodeTypes.CONTROL, - }, - ], - }, - { - name: 'vehicleType', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'noOfAxles', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'numberOfWheelsDriven', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'regnDate', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'firstUseDate', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'createdByName', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'createdById', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'lastUpdatedAt', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'lastUpdatedByName', + viewType: FormNodeViewTypes.HIDDEN, + children: [ + { + name: 'code', + customId: 'vehicleClassCode', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'description', + customId: 'vehicleClassDescription', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + ], + }, + { + name: 'vehicleSubclass', + type: FormNodeTypes.ARRAY, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + children: [ + { + name: '0', + type: FormNodeTypes.CONTROL, + }, + ], + }, + { + name: 'vehicleType', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'noOfAxles', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'numberOfWheelsDriven', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'regnDate', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'firstUseDate', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'createdByName', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'createdById', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'lastUpdatedAt', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'lastUpdatedByName', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'lastUpdatedById', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'lastUpdatedById', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'shouldEmailCertificate', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'vehicleConfiguration', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'reasonForCancellation', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'testTypes', - label: 'Test Types', - type: FormNodeTypes.ARRAY, - children: [ - { - name: '0', // it is important here that the name of the node for an ARRAY type should be an index value - type: FormNodeTypes.GROUP, - children: [ - { - name: 'testTypeId', - label: 'Test Type ID', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'name', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'createdAt', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'lastUpdatedAt', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'certificateLink', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'shouldEmailCertificate', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'vehicleConfiguration', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'reasonForCancellation', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'testTypes', + label: 'Test Types', + type: FormNodeTypes.ARRAY, + children: [ + { + name: '0', // it is important here that the name of the node for an ARRAY type should be an index value + type: FormNodeTypes.GROUP, + children: [ + { + name: 'testTypeId', + label: 'Test Type ID', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'name', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'createdAt', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'lastUpdatedAt', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'certificateLink', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'testTypeClassification', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - value: null, - }, - { - name: 'deletionFlag', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'testNumber', - label: 'Test Number', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - }, - ], - }, - ], - }, - ], + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'testTypeClassification', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + value: null, + }, + { + name: 'deletionFlag', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'testNumber', + label: 'Test Number', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + }, + ], + }, + ], + }, + ], }; diff --git a/src/app/forms/templates/test-records/section-templates/required/desk-based-required-hidden-section-hgv-trl.template.ts b/src/app/forms/templates/test-records/section-templates/required/desk-based-required-hidden-section-hgv-trl.template.ts index c95441b203..0ba707c9af 100644 --- a/src/app/forms/templates/test-records/section-templates/required/desk-based-required-hidden-section-hgv-trl.template.ts +++ b/src/app/forms/templates/test-records/section-templates/required/desk-based-required-hidden-section-hgv-trl.template.ts @@ -1,232 +1,230 @@ -import { - FormNode, FormNodeEditTypes, FormNodeTypes, FormNodeViewTypes, -} from '@forms/services/dynamic-form.types'; +import { FormNode, FormNodeEditTypes, FormNodeTypes, FormNodeViewTypes } from '@forms/services/dynamic-form.types'; export const DeskBasedRequiredSectionHgvTrl: FormNode = { - name: 'requiredSection', - type: FormNodeTypes.GROUP, - children: [ - { - name: 'testResultId', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - }, - { - name: 'vehicleType', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'contingencyTestNumber', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'typeOfTest', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'source', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'testStatus', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'systemNumber', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'testerStaffId', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'testEndTimestamp', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'vehicleClass', - type: FormNodeTypes.GROUP, - editType: FormNodeEditTypes.HIDDEN, + name: 'requiredSection', + type: FormNodeTypes.GROUP, + children: [ + { + name: 'testResultId', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + }, + { + name: 'vehicleType', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'contingencyTestNumber', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'typeOfTest', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'source', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'testStatus', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'systemNumber', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'testerStaffId', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'testEndTimestamp', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'vehicleClass', + type: FormNodeTypes.GROUP, + editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - children: [ - { - name: 'code', - customId: 'vehicleClassCode', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'description', - customId: 'vehicleClassDescription', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - ], - }, - { - name: 'vehicleType', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'noOfAxles', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'numberOfWheelsDriven', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'regnDate', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'firstUseDate', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'createdByName', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'createdById', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'lastUpdatedAt', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'lastUpdatedByName', + viewType: FormNodeViewTypes.HIDDEN, + children: [ + { + name: 'code', + customId: 'vehicleClassCode', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'description', + customId: 'vehicleClassDescription', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + ], + }, + { + name: 'vehicleType', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'noOfAxles', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'numberOfWheelsDriven', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'regnDate', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'firstUseDate', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'createdByName', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'createdById', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'lastUpdatedAt', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'lastUpdatedByName', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'lastUpdatedById', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'lastUpdatedById', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'shouldEmailCertificate', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'vehicleConfiguration', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'reasonForCancellation', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'testTypes', - label: 'Test Types', - type: FormNodeTypes.ARRAY, - children: [ - { - name: '0', // it is important here that the name of the node for an ARRAY type should be an index value - type: FormNodeTypes.GROUP, - children: [ - { - name: 'testTypeId', - label: 'Test Type ID', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'name', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'createdAt', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'lastUpdatedAt', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'certificateLink', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'shouldEmailCertificate', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'vehicleConfiguration', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'reasonForCancellation', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'testTypes', + label: 'Test Types', + type: FormNodeTypes.ARRAY, + children: [ + { + name: '0', // it is important here that the name of the node for an ARRAY type should be an index value + type: FormNodeTypes.GROUP, + children: [ + { + name: 'testTypeId', + label: 'Test Type ID', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'name', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'createdAt', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'lastUpdatedAt', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'certificateLink', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'testTypeClassification', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - value: null, - }, - { - name: 'deletionFlag', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'testNumber', - label: 'Test Number', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - }, - ], - }, - ], - }, - ], + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'testTypeClassification', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + value: null, + }, + { + name: 'deletionFlag', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'testNumber', + label: 'Test Number', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + }, + ], + }, + ], + }, + ], }; diff --git a/src/app/forms/templates/test-records/section-templates/required/desk-based-required-hidden-section-psv.template.ts b/src/app/forms/templates/test-records/section-templates/required/desk-based-required-hidden-section-psv.template.ts index 9499576083..8ee62b9e72 100644 --- a/src/app/forms/templates/test-records/section-templates/required/desk-based-required-hidden-section-psv.template.ts +++ b/src/app/forms/templates/test-records/section-templates/required/desk-based-required-hidden-section-psv.template.ts @@ -1,243 +1,241 @@ -import { - FormNode, FormNodeEditTypes, FormNodeTypes, FormNodeViewTypes, -} from '@forms/services/dynamic-form.types'; +import { FormNode, FormNodeEditTypes, FormNodeTypes, FormNodeViewTypes } from '@forms/services/dynamic-form.types'; export const DeskBasedRequiredSectionPsv: FormNode = { - name: 'requiredSection', - type: FormNodeTypes.GROUP, - children: [ - { - name: 'testResultId', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - }, - { - name: 'vehicleType', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'contingencyTestNumber', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'typeOfTest', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'source', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'testStatus', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'systemNumber', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'testerStaffId', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'testEndTimestamp', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'vehicleClass', - type: FormNodeTypes.GROUP, - editType: FormNodeEditTypes.HIDDEN, + name: 'requiredSection', + type: FormNodeTypes.GROUP, + children: [ + { + name: 'testResultId', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + }, + { + name: 'vehicleType', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'contingencyTestNumber', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'typeOfTest', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'source', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'testStatus', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'systemNumber', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'testerStaffId', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'testEndTimestamp', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'vehicleClass', + type: FormNodeTypes.GROUP, + editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - children: [ - { - name: 'code', - customId: 'vehicleClassCode', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'description', - customId: 'vehicleClassDescription', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - ], - }, - { - name: 'vehicleType', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'noOfAxles', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'numberOfWheelsDriven', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'regnDate', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'firstUseDate', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'createdByName', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'createdById', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'lastUpdatedAt', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'lastUpdatedByName', + viewType: FormNodeViewTypes.HIDDEN, + children: [ + { + name: 'code', + customId: 'vehicleClassCode', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'description', + customId: 'vehicleClassDescription', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + ], + }, + { + name: 'vehicleType', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'noOfAxles', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'numberOfWheelsDriven', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'regnDate', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'firstUseDate', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'createdByName', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'createdById', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'lastUpdatedAt', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'lastUpdatedByName', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'lastUpdatedById', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'lastUpdatedById', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'shouldEmailCertificate', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'numberOfSeats', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'vehicleConfiguration', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'vehicleSize', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'reasonForCancellation', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'testTypes', - label: 'Test Types', - type: FormNodeTypes.ARRAY, - children: [ - { - name: '0', // it is important here that the name of the node for an ARRAY type should be an index value - type: FormNodeTypes.GROUP, - children: [ - { - name: 'testTypeId', - label: 'Test Type ID', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'name', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'createdAt', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'lastUpdatedAt', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'certificateLink', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'testTypeClassification', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - value: '', - }, - { - name: 'deletionFlag', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'testNumber', - label: 'Test Number', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - }, - ], - }, - ], - }, - ], + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'shouldEmailCertificate', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'numberOfSeats', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'vehicleConfiguration', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'vehicleSize', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'reasonForCancellation', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'testTypes', + label: 'Test Types', + type: FormNodeTypes.ARRAY, + children: [ + { + name: '0', // it is important here that the name of the node for an ARRAY type should be an index value + type: FormNodeTypes.GROUP, + children: [ + { + name: 'testTypeId', + label: 'Test Type ID', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'name', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'createdAt', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'lastUpdatedAt', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'certificateLink', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'testTypeClassification', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + value: '', + }, + { + name: 'deletionFlag', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'testNumber', + label: 'Test Number', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + }, + ], + }, + ], + }, + ], }; diff --git a/src/app/forms/templates/test-records/section-templates/required/desk-based-required-section-group4-amend.template.ts b/src/app/forms/templates/test-records/section-templates/required/desk-based-required-section-group4-amend.template.ts index da63e667e3..ff767ab956 100644 --- a/src/app/forms/templates/test-records/section-templates/required/desk-based-required-section-group4-amend.template.ts +++ b/src/app/forms/templates/test-records/section-templates/required/desk-based-required-section-group4-amend.template.ts @@ -1,256 +1,254 @@ -import { - FormNode, FormNodeEditTypes, FormNodeTypes, FormNodeViewTypes, -} from '@forms/services/dynamic-form.types'; +import { FormNode, FormNodeEditTypes, FormNodeTypes, FormNodeViewTypes } from '@forms/services/dynamic-form.types'; export const AmendDeskBasedRequiredHiddenSectionGroup4: FormNode = { - name: 'requiredSection', - type: FormNodeTypes.GROUP, - children: [ - { - name: 'testResultId', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - }, - { - name: 'vehicleType', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'contingencyTestNumber', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'typeOfTest', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'source', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'testStatus', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'systemNumber', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'testerStaffId', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'testEndTimestamp', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'vehicleClass', - type: FormNodeTypes.GROUP, - editType: FormNodeEditTypes.HIDDEN, + name: 'requiredSection', + type: FormNodeTypes.GROUP, + children: [ + { + name: 'testResultId', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + }, + { + name: 'vehicleType', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'contingencyTestNumber', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'typeOfTest', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'source', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'testStatus', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'systemNumber', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'testerStaffId', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'testEndTimestamp', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'vehicleClass', + type: FormNodeTypes.GROUP, + editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - children: [ - { - name: 'code', - customId: 'vehicleClassCode', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'description', - customId: 'vehicleClassDescription', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - ], - }, - { - name: 'vehicleSubclass', - type: FormNodeTypes.ARRAY, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - children: [ - { - name: '0', - type: FormNodeTypes.CONTROL, - }, - ], - }, - { - name: 'vehicleType', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'noOfAxles', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'numberOfWheelsDriven', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'regnDate', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'firstUseDate', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'createdByName', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'createdById', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'lastUpdatedAt', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'lastUpdatedByName', + viewType: FormNodeViewTypes.HIDDEN, + children: [ + { + name: 'code', + customId: 'vehicleClassCode', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'description', + customId: 'vehicleClassDescription', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + ], + }, + { + name: 'vehicleSubclass', + type: FormNodeTypes.ARRAY, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + children: [ + { + name: '0', + type: FormNodeTypes.CONTROL, + }, + ], + }, + { + name: 'vehicleType', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'noOfAxles', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'numberOfWheelsDriven', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'regnDate', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'firstUseDate', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'createdByName', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'createdById', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'lastUpdatedAt', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'lastUpdatedByName', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'lastUpdatedById', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'lastUpdatedById', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'shouldEmailCertificate', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'vehicleConfiguration', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'reasonForCancellation', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'testTypes', - label: 'Test Types', - type: FormNodeTypes.ARRAY, - children: [ - { - name: '0', // it is important here that the name of the node for an ARRAY type should be an index value - type: FormNodeTypes.GROUP, - children: [ - { - name: 'testTypeId', - label: 'Test Type ID', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'testTypeName', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - }, - { - name: 'testTypeClassification', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - value: null, - }, - { - name: 'name', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'createdAt', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'lastUpdatedAt', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'certificateLink', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'deletionFlag', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'testNumber', - label: 'Test Number', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - }, - { - name: 'secondaryCertificateNumber', - type: FormNodeTypes.CONTROL, - value: null, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - ], - }, - ], - }, - ], + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'shouldEmailCertificate', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'vehicleConfiguration', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'reasonForCancellation', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'testTypes', + label: 'Test Types', + type: FormNodeTypes.ARRAY, + children: [ + { + name: '0', // it is important here that the name of the node for an ARRAY type should be an index value + type: FormNodeTypes.GROUP, + children: [ + { + name: 'testTypeId', + label: 'Test Type ID', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'testTypeName', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + }, + { + name: 'testTypeClassification', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + value: null, + }, + { + name: 'name', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'createdAt', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'lastUpdatedAt', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'certificateLink', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'deletionFlag', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'testNumber', + label: 'Test Number', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + }, + { + name: 'secondaryCertificateNumber', + type: FormNodeTypes.CONTROL, + value: null, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + ], + }, + ], + }, + ], }; diff --git a/src/app/forms/templates/test-records/section-templates/required/required-hidden-section-hgv-trl.template.ts b/src/app/forms/templates/test-records/section-templates/required/required-hidden-section-hgv-trl.template.ts index 628251f35b..a70c28ad2b 100644 --- a/src/app/forms/templates/test-records/section-templates/required/required-hidden-section-hgv-trl.template.ts +++ b/src/app/forms/templates/test-records/section-templates/required/required-hidden-section-hgv-trl.template.ts @@ -1,231 +1,229 @@ -import { - FormNode, FormNodeEditTypes, FormNodeTypes, FormNodeViewTypes, -} from '@forms/services/dynamic-form.types'; +import { FormNode, FormNodeEditTypes, FormNodeTypes, FormNodeViewTypes } from '@forms/services/dynamic-form.types'; export const RequiredSectionHGVTRL: FormNode = { - name: 'requiredSection', - type: FormNodeTypes.GROUP, - children: [ - { - name: 'testResultId', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - }, - { - name: 'vehicleType', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'contingencyTestNumber', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'typeOfTest', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'source', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'testStatus', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'systemNumber', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'testerStaffId', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'testEndTimestamp', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'vehicleClass', - type: FormNodeTypes.GROUP, - editType: FormNodeEditTypes.HIDDEN, + name: 'requiredSection', + type: FormNodeTypes.GROUP, + children: [ + { + name: 'testResultId', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + }, + { + name: 'vehicleType', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'contingencyTestNumber', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'typeOfTest', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'source', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'testStatus', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'systemNumber', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'testerStaffId', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'testEndTimestamp', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'vehicleClass', + type: FormNodeTypes.GROUP, + editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - children: [ - { - name: 'code', - customId: 'vehicleClassCode', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'description', - customId: 'vehicleClassDescription', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - ], - }, - { - name: 'vehicleType', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'noOfAxles', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'numberOfWheelsDriven', - type: FormNodeTypes.CONTROL, - value: null, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'regnDate', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'firstUseDate', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'createdByName', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'createdById', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'lastUpdatedAt', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'lastUpdatedByName', + viewType: FormNodeViewTypes.HIDDEN, + children: [ + { + name: 'code', + customId: 'vehicleClassCode', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'description', + customId: 'vehicleClassDescription', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + ], + }, + { + name: 'vehicleType', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'noOfAxles', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'numberOfWheelsDriven', + type: FormNodeTypes.CONTROL, + value: null, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'regnDate', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'firstUseDate', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'createdByName', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'createdById', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'lastUpdatedAt', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'lastUpdatedByName', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'lastUpdatedById', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'lastUpdatedById', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'shouldEmailCertificate', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'vehicleConfiguration', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'reasonForCancellation', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'testTypes', - label: 'Test Types', - type: FormNodeTypes.ARRAY, - children: [ - { - name: '0', // it is important here that the name of the node for an ARRAY type should be an index value - type: FormNodeTypes.GROUP, - children: [ - { - name: 'testTypeId', - label: 'Test Type ID', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'name', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'secondaryCertificateNumber', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'createdAt', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'lastUpdatedAt', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'certificateLink', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'shouldEmailCertificate', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'vehicleConfiguration', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'reasonForCancellation', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'testTypes', + label: 'Test Types', + type: FormNodeTypes.ARRAY, + children: [ + { + name: '0', // it is important here that the name of the node for an ARRAY type should be an index value + type: FormNodeTypes.GROUP, + children: [ + { + name: 'testTypeId', + label: 'Test Type ID', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'name', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'secondaryCertificateNumber', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'createdAt', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'lastUpdatedAt', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'certificateLink', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'testTypeClassification', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'deletionFlag', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - ], - }, - ], - }, - ], + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'testTypeClassification', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'deletionFlag', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + ], + }, + ], + }, + ], }; diff --git a/src/app/forms/templates/test-records/section-templates/required/required-hidden-section.template.ts b/src/app/forms/templates/test-records/section-templates/required/required-hidden-section.template.ts index 20a3154443..38f5170262 100644 --- a/src/app/forms/templates/test-records/section-templates/required/required-hidden-section.template.ts +++ b/src/app/forms/templates/test-records/section-templates/required/required-hidden-section.template.ts @@ -1,249 +1,247 @@ -import { - FormNode, FormNodeEditTypes, FormNodeTypes, FormNodeViewTypes, -} from '@forms/services/dynamic-form.types'; +import { FormNode, FormNodeEditTypes, FormNodeTypes, FormNodeViewTypes } from '@forms/services/dynamic-form.types'; export const RequiredSection: FormNode = { - name: 'requiredSection', - type: FormNodeTypes.GROUP, - children: [ - { - name: 'testResultId', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - }, - { - name: 'vehicleType', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'contingencyTestNumber', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'typeOfTest', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'source', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'testStatus', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'systemNumber', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'testerStaffId', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'testEndTimestamp', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'vehicleClass', - type: FormNodeTypes.GROUP, - editType: FormNodeEditTypes.HIDDEN, + name: 'requiredSection', + type: FormNodeTypes.GROUP, + children: [ + { + name: 'testResultId', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + }, + { + name: 'vehicleType', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'contingencyTestNumber', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'typeOfTest', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'source', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'testStatus', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'systemNumber', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'testerStaffId', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'testEndTimestamp', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'vehicleClass', + type: FormNodeTypes.GROUP, + editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - children: [ - { - name: 'code', - customId: 'vehicleClassCode', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'description', - customId: 'vehicleClassDescription', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - ], - }, - { - name: 'vehicleType', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'noOfAxles', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'numberOfWheelsDriven', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'regnDate', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'firstUseDate', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'createdByName', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'createdById', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'lastUpdatedAt', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'lastUpdatedByName', + viewType: FormNodeViewTypes.HIDDEN, + children: [ + { + name: 'code', + customId: 'vehicleClassCode', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'description', + customId: 'vehicleClassDescription', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + ], + }, + { + name: 'vehicleType', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'noOfAxles', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'numberOfWheelsDriven', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'regnDate', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'firstUseDate', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'createdByName', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'createdById', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'lastUpdatedAt', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'lastUpdatedByName', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'lastUpdatedById', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'lastUpdatedById', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'shouldEmailCertificate', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'numberOfSeats', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'vehicleConfiguration', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'vehicleSize', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'reasonForCancellation', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'testTypes', - label: 'Test Types', - type: FormNodeTypes.ARRAY, - children: [ - { - name: '0', // it is important here that the name of the node for an ARRAY type should be an index value - type: FormNodeTypes.GROUP, - children: [ - { - name: 'testTypeId', - label: 'Test Type ID', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'name', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'secondaryCertificateNumber', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'createdAt', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'lastUpdatedAt', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'certificateLink', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'testTypeClassification', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - value: '', - }, - { - name: 'deletionFlag', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'testNumber', - label: 'Test Number', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - }, - ], - }, - ], - }, - ], + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'shouldEmailCertificate', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'numberOfSeats', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'vehicleConfiguration', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'vehicleSize', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'reasonForCancellation', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'testTypes', + label: 'Test Types', + type: FormNodeTypes.ARRAY, + children: [ + { + name: '0', // it is important here that the name of the node for an ARRAY type should be an index value + type: FormNodeTypes.GROUP, + children: [ + { + name: 'testTypeId', + label: 'Test Type ID', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'name', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'secondaryCertificateNumber', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'createdAt', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'lastUpdatedAt', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'certificateLink', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'testTypeClassification', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + value: '', + }, + { + name: 'deletionFlag', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'testNumber', + label: 'Test Number', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + }, + ], + }, + ], + }, + ], }; diff --git a/src/app/forms/templates/test-records/section-templates/required/seatbelt-hidden-section.template.ts b/src/app/forms/templates/test-records/section-templates/required/seatbelt-hidden-section.template.ts index 6dded7eecb..13669f33fc 100644 --- a/src/app/forms/templates/test-records/section-templates/required/seatbelt-hidden-section.template.ts +++ b/src/app/forms/templates/test-records/section-templates/required/seatbelt-hidden-section.template.ts @@ -1,44 +1,42 @@ -import { - FormNode, FormNodeEditTypes, FormNodeTypes, FormNodeViewTypes, -} from '@forms/services/dynamic-form.types'; +import { FormNode, FormNodeEditTypes, FormNodeTypes, FormNodeViewTypes } from '@forms/services/dynamic-form.types'; export const SeatbeltHiddenSection: FormNode = { - name: 'requiredSection', - type: FormNodeTypes.GROUP, - children: [ - { - name: 'testTypes', - label: 'Test Types', - type: FormNodeTypes.ARRAY, - children: [ - { - name: '0', // it is important here that the name of the node for an ARRAY type should be an index value - type: FormNodeTypes.GROUP, - children: [ - { - name: 'seatbeltInstallationCheckDate', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - value: null, - }, - { - name: 'numberOfSeatbeltsFitted', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - value: null, - }, - { - name: 'lastSeatbeltInstallationCheckDate', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - value: null, - }, - ], - }, - ], - }, - ], + name: 'requiredSection', + type: FormNodeTypes.GROUP, + children: [ + { + name: 'testTypes', + label: 'Test Types', + type: FormNodeTypes.ARRAY, + children: [ + { + name: '0', // it is important here that the name of the node for an ARRAY type should be an index value + type: FormNodeTypes.GROUP, + children: [ + { + name: 'seatbeltInstallationCheckDate', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + value: null, + }, + { + name: 'numberOfSeatbeltsFitted', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + value: null, + }, + { + name: 'lastSeatbeltInstallationCheckDate', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + value: null, + }, + ], + }, + ], + }, + ], }; diff --git a/src/app/forms/templates/test-records/section-templates/required/specialist-required-hidden-section-hgv-trl.template.ts b/src/app/forms/templates/test-records/section-templates/required/specialist-required-hidden-section-hgv-trl.template.ts index 3ca3b129ba..952d73d7e6 100644 --- a/src/app/forms/templates/test-records/section-templates/required/specialist-required-hidden-section-hgv-trl.template.ts +++ b/src/app/forms/templates/test-records/section-templates/required/specialist-required-hidden-section-hgv-trl.template.ts @@ -1,224 +1,222 @@ -import { - FormNode, FormNodeEditTypes, FormNodeTypes, FormNodeViewTypes, -} from '@forms/services/dynamic-form.types'; +import { FormNode, FormNodeEditTypes, FormNodeTypes, FormNodeViewTypes } from '@forms/services/dynamic-form.types'; export const SpecialistRequiredSectionHGVTRL: FormNode = { - name: 'requiredSection', - type: FormNodeTypes.GROUP, - children: [ - { - name: 'testResultId', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - }, - { - name: 'vehicleType', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'contingencyTestNumber', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'typeOfTest', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'source', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'testStatus', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'systemNumber', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'testerStaffId', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'testEndTimestamp', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'vehicleClass', - type: FormNodeTypes.GROUP, - editType: FormNodeEditTypes.HIDDEN, + name: 'requiredSection', + type: FormNodeTypes.GROUP, + children: [ + { + name: 'testResultId', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + }, + { + name: 'vehicleType', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'contingencyTestNumber', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'typeOfTest', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'source', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'testStatus', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'systemNumber', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'testerStaffId', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'testEndTimestamp', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'vehicleClass', + type: FormNodeTypes.GROUP, + editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - children: [ - { - name: 'code', - customId: 'vehicleClassCode', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'description', - customId: 'vehicleClassDescription', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - ], - }, - { - name: 'vehicleType', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'noOfAxles', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'numberOfWheelsDriven', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'regnDate', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'firstUseDate', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'createdByName', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'createdById', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'lastUpdatedAt', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'lastUpdatedByName', + viewType: FormNodeViewTypes.HIDDEN, + children: [ + { + name: 'code', + customId: 'vehicleClassCode', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'description', + customId: 'vehicleClassDescription', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + ], + }, + { + name: 'vehicleType', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'noOfAxles', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'numberOfWheelsDriven', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'regnDate', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'firstUseDate', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'createdByName', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'createdById', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'lastUpdatedAt', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'lastUpdatedByName', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'lastUpdatedById', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'lastUpdatedById', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'shouldEmailCertificate', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'vehicleConfiguration', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'reasonForCancellation', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'testTypes', - label: 'Test Types', - type: FormNodeTypes.ARRAY, - children: [ - { - name: '0', // it is important here that the name of the node for an ARRAY type should be an index value - type: FormNodeTypes.GROUP, - children: [ - { - name: 'testTypeId', - label: 'Test Type ID', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'name', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'createdAt', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'lastUpdatedAt', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'certificateLink', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'shouldEmailCertificate', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'vehicleConfiguration', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'reasonForCancellation', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'testTypes', + label: 'Test Types', + type: FormNodeTypes.ARRAY, + children: [ + { + name: '0', // it is important here that the name of the node for an ARRAY type should be an index value + type: FormNodeTypes.GROUP, + children: [ + { + name: 'testTypeId', + label: 'Test Type ID', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'name', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'createdAt', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'lastUpdatedAt', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'certificateLink', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'testTypeClassification', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'deletionFlag', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - ], - }, - ], - }, - ], + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'testTypeClassification', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'deletionFlag', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + ], + }, + ], + }, + ], }; diff --git a/src/app/forms/templates/test-records/section-templates/required/specialist-required-hidden-section-lgv-car.template.ts b/src/app/forms/templates/test-records/section-templates/required/specialist-required-hidden-section-lgv-car.template.ts index b68f033e61..67253a0b30 100644 --- a/src/app/forms/templates/test-records/section-templates/required/specialist-required-hidden-section-lgv-car.template.ts +++ b/src/app/forms/templates/test-records/section-templates/required/specialist-required-hidden-section-lgv-car.template.ts @@ -1,236 +1,234 @@ -import { - FormNode, FormNodeEditTypes, FormNodeTypes, FormNodeViewTypes, -} from '@forms/services/dynamic-form.types'; +import { FormNode, FormNodeEditTypes, FormNodeTypes, FormNodeViewTypes } from '@forms/services/dynamic-form.types'; export const SpecialistRequiredSectionLgvCarSmallTrl: FormNode = { - name: 'requiredSection', - type: FormNodeTypes.GROUP, - children: [ - { - name: 'testResultId', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - }, - { - name: 'vehicleType', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'contingencyTestNumber', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'typeOfTest', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'source', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'testStatus', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'systemNumber', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'testerStaffId', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'testEndTimestamp', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'vehicleClass', - type: FormNodeTypes.GROUP, - editType: FormNodeEditTypes.HIDDEN, + name: 'requiredSection', + type: FormNodeTypes.GROUP, + children: [ + { + name: 'testResultId', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + }, + { + name: 'vehicleType', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'contingencyTestNumber', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'typeOfTest', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'source', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'testStatus', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'systemNumber', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'testerStaffId', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'testEndTimestamp', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'vehicleClass', + type: FormNodeTypes.GROUP, + editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - children: [ - { - name: 'code', - customId: 'vehicleClassCode', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'description', - customId: 'vehicleClassDescription', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - ], - }, - { - name: 'vehicleSubclass', - type: FormNodeTypes.ARRAY, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - children: [ - { - name: '0', - type: FormNodeTypes.CONTROL, - }, - ], - }, - { - name: 'vehicleType', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'noOfAxles', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'numberOfWheelsDriven', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'regnDate', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'firstUseDate', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'createdByName', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'createdById', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'lastUpdatedAt', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'lastUpdatedByName', + viewType: FormNodeViewTypes.HIDDEN, + children: [ + { + name: 'code', + customId: 'vehicleClassCode', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'description', + customId: 'vehicleClassDescription', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + ], + }, + { + name: 'vehicleSubclass', + type: FormNodeTypes.ARRAY, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + children: [ + { + name: '0', + type: FormNodeTypes.CONTROL, + }, + ], + }, + { + name: 'vehicleType', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'noOfAxles', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'numberOfWheelsDriven', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'regnDate', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'firstUseDate', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'createdByName', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'createdById', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'lastUpdatedAt', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'lastUpdatedByName', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'lastUpdatedById', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'lastUpdatedById', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'shouldEmailCertificate', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'vehicleConfiguration', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'reasonForCancellation', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'testTypes', - label: 'Test Types', - type: FormNodeTypes.ARRAY, - children: [ - { - name: '0', // it is important here that the name of the node for an ARRAY type should be an index value - type: FormNodeTypes.GROUP, - children: [ - { - name: 'testTypeId', - label: 'Test Type ID', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'name', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'createdAt', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'lastUpdatedAt', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'certificateLink', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'shouldEmailCertificate', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'vehicleConfiguration', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'reasonForCancellation', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'testTypes', + label: 'Test Types', + type: FormNodeTypes.ARRAY, + children: [ + { + name: '0', // it is important here that the name of the node for an ARRAY type should be an index value + type: FormNodeTypes.GROUP, + children: [ + { + name: 'testTypeId', + label: 'Test Type ID', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'name', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'createdAt', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'lastUpdatedAt', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'certificateLink', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'testTypeClassification', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'deletionFlag', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - ], - }, - ], - }, - ], + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'testTypeClassification', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'deletionFlag', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + ], + }, + ], + }, + ], }; diff --git a/src/app/forms/templates/test-records/section-templates/required/specialist-required-hidden-section-motorcycle.template.ts b/src/app/forms/templates/test-records/section-templates/required/specialist-required-hidden-section-motorcycle.template.ts index 54a62f27b0..1e3cd6ad77 100644 --- a/src/app/forms/templates/test-records/section-templates/required/specialist-required-hidden-section-motorcycle.template.ts +++ b/src/app/forms/templates/test-records/section-templates/required/specialist-required-hidden-section-motorcycle.template.ts @@ -1,227 +1,225 @@ -import { - FormNode, FormNodeEditTypes, FormNodeTypes, FormNodeViewTypes, -} from '@forms/services/dynamic-form.types'; +import { FormNode, FormNodeEditTypes, FormNodeTypes, FormNodeViewTypes } from '@forms/services/dynamic-form.types'; export const RequiredSpecialistSectionMotorcycle: FormNode = { - name: 'requiredSection', - type: FormNodeTypes.GROUP, - children: [ - { - name: 'testResultId', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - }, - { - name: 'vehicleType', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'contingencyTestNumber', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'typeOfTest', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'source', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'testStatus', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'systemNumber', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'testerStaffId', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'testEndTimestamp', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'vehicleClass', - type: FormNodeTypes.GROUP, - editType: FormNodeEditTypes.HIDDEN, + name: 'requiredSection', + type: FormNodeTypes.GROUP, + children: [ + { + name: 'testResultId', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + }, + { + name: 'vehicleType', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'contingencyTestNumber', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'typeOfTest', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'source', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'testStatus', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'systemNumber', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'testerStaffId', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'testEndTimestamp', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'vehicleClass', + type: FormNodeTypes.GROUP, + editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - children: [ - { - name: 'code', - customId: 'vehicleClassCode', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'description', - customId: 'vehicleClassDescription', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - ], - }, - { - name: 'vehicleConfiguration', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'vehicleType', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'noOfAxles', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'numberOfWheelsDriven', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'regnDate', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'firstUseDate', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'createdByName', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'createdById', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'lastUpdatedAt', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'lastUpdatedByName', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'lastUpdatedById', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'shouldEmailCertificate', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'vehicleSize', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'reasonForCancellation', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'testTypes', - label: 'Test Types', - type: FormNodeTypes.ARRAY, - children: [ - { - name: '0', // it is important here that the name of the node for an ARRAY type should be an index value - type: FormNodeTypes.GROUP, - children: [ - { - name: 'testTypeId', - label: 'Test Type ID', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'name', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'createdAt', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'lastUpdatedAt', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'certificateLink', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'testTypeClassification', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'deletionFlag', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - ], - }, - ], - }, - ], + viewType: FormNodeViewTypes.HIDDEN, + children: [ + { + name: 'code', + customId: 'vehicleClassCode', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'description', + customId: 'vehicleClassDescription', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + ], + }, + { + name: 'vehicleConfiguration', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'vehicleType', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'noOfAxles', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'numberOfWheelsDriven', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'regnDate', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'firstUseDate', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'createdByName', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'createdById', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'lastUpdatedAt', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'lastUpdatedByName', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'lastUpdatedById', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'shouldEmailCertificate', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'vehicleSize', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'reasonForCancellation', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'testTypes', + label: 'Test Types', + type: FormNodeTypes.ARRAY, + children: [ + { + name: '0', // it is important here that the name of the node for an ARRAY type should be an index value + type: FormNodeTypes.GROUP, + children: [ + { + name: 'testTypeId', + label: 'Test Type ID', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'name', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'createdAt', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'lastUpdatedAt', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'certificateLink', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'testTypeClassification', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'deletionFlag', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + ], + }, + ], + }, + ], }; diff --git a/src/app/forms/templates/test-records/section-templates/required/specialist-required-hidden-section.template.ts b/src/app/forms/templates/test-records/section-templates/required/specialist-required-hidden-section.template.ts index 24360c3de0..9cfb6fe245 100644 --- a/src/app/forms/templates/test-records/section-templates/required/specialist-required-hidden-section.template.ts +++ b/src/app/forms/templates/test-records/section-templates/required/specialist-required-hidden-section.template.ts @@ -1,233 +1,231 @@ -import { - FormNode, FormNodeEditTypes, FormNodeTypes, FormNodeViewTypes, -} from '@forms/services/dynamic-form.types'; +import { FormNode, FormNodeEditTypes, FormNodeTypes, FormNodeViewTypes } from '@forms/services/dynamic-form.types'; export const RequiredSpecialistSection: FormNode = { - name: 'requiredSection', - type: FormNodeTypes.GROUP, - children: [ - { - name: 'testResultId', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - }, - { - name: 'vehicleType', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'contingencyTestNumber', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'typeOfTest', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'source', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'testStatus', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'systemNumber', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'testerStaffId', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'testEndTimestamp', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'vehicleClass', - type: FormNodeTypes.GROUP, - editType: FormNodeEditTypes.HIDDEN, + name: 'requiredSection', + type: FormNodeTypes.GROUP, + children: [ + { + name: 'testResultId', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + }, + { + name: 'vehicleType', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'contingencyTestNumber', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'typeOfTest', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'source', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'testStatus', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'systemNumber', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'testerStaffId', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'testEndTimestamp', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'vehicleClass', + type: FormNodeTypes.GROUP, + editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - children: [ - { - name: 'code', - customId: 'vehicleClassCode', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'description', - customId: 'vehicleClassDescription', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - ], - }, - { - name: 'vehicleType', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'noOfAxles', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'numberOfWheelsDriven', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'regnDate', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'firstUseDate', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'createdByName', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'createdById', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'lastUpdatedAt', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'lastUpdatedByName', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'lastUpdatedById', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'shouldEmailCertificate', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'numberOfSeats', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'vehicleConfiguration', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'vehicleSize', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'reasonForCancellation', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'testTypes', - label: 'Test Types', - type: FormNodeTypes.ARRAY, - children: [ - { - name: '0', // it is important here that the name of the node for an ARRAY type should be an index value - type: FormNodeTypes.GROUP, - children: [ - { - name: 'testTypeId', - label: 'Test Type ID', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'name', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'createdAt', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'lastUpdatedAt', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'certificateLink', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'testTypeClassification', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'deletionFlag', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - ], - }, - ], - }, - ], + viewType: FormNodeViewTypes.HIDDEN, + children: [ + { + name: 'code', + customId: 'vehicleClassCode', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'description', + customId: 'vehicleClassDescription', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + ], + }, + { + name: 'vehicleType', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'noOfAxles', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'numberOfWheelsDriven', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'regnDate', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'firstUseDate', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'createdByName', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'createdById', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'lastUpdatedAt', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'lastUpdatedByName', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'lastUpdatedById', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'shouldEmailCertificate', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'numberOfSeats', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'vehicleConfiguration', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'vehicleSize', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'reasonForCancellation', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'testTypes', + label: 'Test Types', + type: FormNodeTypes.ARRAY, + children: [ + { + name: '0', // it is important here that the name of the node for an ARRAY type should be an index value + type: FormNodeTypes.GROUP, + children: [ + { + name: 'testTypeId', + label: 'Test Type ID', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'name', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'createdAt', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'lastUpdatedAt', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'certificateLink', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'testTypeClassification', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'deletionFlag', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + ], + }, + ], + }, + ], }; diff --git a/src/app/forms/templates/test-records/section-templates/seatbelt/seatbelt-section.template.ts b/src/app/forms/templates/test-records/section-templates/seatbelt/seatbelt-section.template.ts index 6a3bcc1af3..4f20ce2fec 100644 --- a/src/app/forms/templates/test-records/section-templates/seatbelt/seatbelt-section.template.ts +++ b/src/app/forms/templates/test-records/section-templates/seatbelt/seatbelt-section.template.ts @@ -1,62 +1,72 @@ import { AsyncValidatorNames } from '@forms/models/async-validators.enum'; import { ValidatorNames } from '@forms/models/validators.enum'; import { - FormNode, FormNodeEditTypes, FormNodeTypes, FormNodeViewTypes, FormNodeWidth, + FormNode, + FormNodeEditTypes, + FormNodeTypes, + FormNodeViewTypes, + FormNodeWidth, } from '@forms/services/dynamic-form.types'; export const SeatbeltSection: FormNode = { - name: 'seatbeltSection', - label: 'Seatbelt', - type: FormNodeTypes.GROUP, - children: [ - { - name: 'testTypes', - label: 'Test Types', - type: FormNodeTypes.ARRAY, - children: [ - { - name: '0', // it is important here that the name of the node for an ARRAY type should be an index value - type: FormNodeTypes.GROUP, - children: [ - { - name: 'seatbeltInstallationCheckDate', - label: 'Carried out during test', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.RADIO, - value: null, - options: [ - { value: true, label: 'Yes' }, - { value: false, label: 'No' }, - ], - asyncValidators: [{ name: AsyncValidatorNames.RequiredIfNotAbandoned }], - }, - { - name: 'numberOfSeatbeltsFitted', - label: 'Number of seatbelts fitted', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.NUMBER, - value: null, - validators: [ - { name: ValidatorNames.RequiredIfEquals, args: { sibling: 'seatbeltInstallationCheckDate', value: [true] } }, - { name: ValidatorNames.Max, args: 99 }, - ], - width: FormNodeWidth.M, - }, - { - name: 'lastSeatbeltInstallationCheckDate', - label: 'Most recent installation check', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.DATE, - editType: FormNodeEditTypes.DATE, - value: null, - validators: [ - { name: ValidatorNames.RequiredIfEquals, args: { sibling: 'seatbeltInstallationCheckDate', value: [true] } }, - { name: ValidatorNames.PastDate }, - ], - }, - ], - }, - ], - }, - ], + name: 'seatbeltSection', + label: 'Seatbelt', + type: FormNodeTypes.GROUP, + children: [ + { + name: 'testTypes', + label: 'Test Types', + type: FormNodeTypes.ARRAY, + children: [ + { + name: '0', // it is important here that the name of the node for an ARRAY type should be an index value + type: FormNodeTypes.GROUP, + children: [ + { + name: 'seatbeltInstallationCheckDate', + label: 'Carried out during test', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.RADIO, + value: null, + options: [ + { value: true, label: 'Yes' }, + { value: false, label: 'No' }, + ], + asyncValidators: [{ name: AsyncValidatorNames.RequiredIfNotAbandoned }], + }, + { + name: 'numberOfSeatbeltsFitted', + label: 'Number of seatbelts fitted', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.NUMBER, + value: null, + validators: [ + { + name: ValidatorNames.RequiredIfEquals, + args: { sibling: 'seatbeltInstallationCheckDate', value: [true] }, + }, + { name: ValidatorNames.Max, args: 99 }, + ], + width: FormNodeWidth.M, + }, + { + name: 'lastSeatbeltInstallationCheckDate', + label: 'Most recent installation check', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.DATE, + editType: FormNodeEditTypes.DATE, + value: null, + validators: [ + { + name: ValidatorNames.RequiredIfEquals, + args: { sibling: 'seatbeltInstallationCheckDate', value: [true] }, + }, + { name: ValidatorNames.PastDate }, + ], + }, + ], + }, + ], + }, + ], }; diff --git a/src/app/forms/templates/test-records/section-templates/test/contingency/contingency-test-section-group1.template.ts b/src/app/forms/templates/test-records/section-templates/test/contingency/contingency-test-section-group1.template.ts index 2f98d5f949..1b6ae39ce5 100644 --- a/src/app/forms/templates/test-records/section-templates/test/contingency/contingency-test-section-group1.template.ts +++ b/src/app/forms/templates/test-records/section-templates/test/contingency/contingency-test-section-group1.template.ts @@ -1,122 +1,130 @@ import { ValidatorNames } from '@forms/models/validators.enum'; import { - FormNode, FormNodeEditTypes, FormNodeTypes, FormNodeViewTypes, FormNodeWidth, + FormNode, + FormNodeEditTypes, + FormNodeTypes, + FormNodeViewTypes, + FormNodeWidth, } from '@forms/services/dynamic-form.types'; export const ContingencyTestSectionGroup1: FormNode = { - name: 'testSection', - label: 'Test', - type: FormNodeTypes.GROUP, - children: [ - { - name: 'contingencyTestNumber', - label: 'Contingency Test Number', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.NUMERICSTRING, - validators: [{ name: ValidatorNames.MaxLength, args: 8 }, { name: ValidatorNames.MinLength, args: 6 }, { name: ValidatorNames.Required }], - width: FormNodeWidth.L, - }, - { - name: 'testStartTimestamp', - label: 'Test start date', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - }, - { - name: 'testEndTimestamp', - type: FormNodeTypes.CONTROL, - label: 'Test end date', - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - validators: [{ name: ValidatorNames.AheadOfDate, args: 'testStartTimestamp' }], - }, - { - name: 'testTypes', - label: 'Test Types', - type: FormNodeTypes.ARRAY, - children: [ - { - name: '0', // it is important here that the name of the node for an ARRAY type should be an index value - type: FormNodeTypes.GROUP, - children: [ - { - name: 'testResult', - label: 'Result', - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - type: FormNodeTypes.CONTROL, - }, - { - name: 'reasonForAbandoning', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - value: null, - required: true, - }, - { - name: 'additionalCommentsForAbandon', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - value: null, - required: true, - }, - { - name: 'certificateNumber', - label: 'Certificate number', - value: '', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - }, - { - name: 'testExpiryDate', - label: 'Expiry Date', - disabled: true, - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - }, - { - name: 'testTypeStartTimestamp', - type: FormNodeTypes.CONTROL, - value: '', - label: 'Test start date and time', - viewType: FormNodeViewTypes.TIME, - editType: FormNodeEditTypes.DATETIME, - validators: [ - { name: ValidatorNames.Required }, - { name: ValidatorNames.PastDate }, - { name: ValidatorNames.CopyValueToRootControl, args: 'testStartTimestamp' }, - ], - }, - { - name: 'testTypeEndTimestamp', - type: FormNodeTypes.CONTROL, - value: '', - label: 'Test end date and time', - viewType: FormNodeViewTypes.TIME, - editType: FormNodeEditTypes.DATETIME, - validators: [ - { name: ValidatorNames.Required }, - { name: ValidatorNames.PastDate }, - { name: ValidatorNames.AheadOfDate, args: 'testTypeStartTimestamp' }, - { name: ValidatorNames.CopyValueToRootControl, args: 'testEndTimestamp' }, - ], - }, - { - name: 'prohibitionIssued', - label: 'Prohibition issued', - type: FormNodeTypes.CONTROL, - value: null, - editType: FormNodeEditTypes.HIDDEN, - required: true, - }, - ], - }, - ], - }, - ], + name: 'testSection', + label: 'Test', + type: FormNodeTypes.GROUP, + children: [ + { + name: 'contingencyTestNumber', + label: 'Contingency Test Number', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.NUMERICSTRING, + validators: [ + { name: ValidatorNames.MaxLength, args: 8 }, + { name: ValidatorNames.MinLength, args: 6 }, + { name: ValidatorNames.Required }, + ], + width: FormNodeWidth.L, + }, + { + name: 'testStartTimestamp', + label: 'Test start date', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + }, + { + name: 'testEndTimestamp', + type: FormNodeTypes.CONTROL, + label: 'Test end date', + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + validators: [{ name: ValidatorNames.AheadOfDate, args: 'testStartTimestamp' }], + }, + { + name: 'testTypes', + label: 'Test Types', + type: FormNodeTypes.ARRAY, + children: [ + { + name: '0', // it is important here that the name of the node for an ARRAY type should be an index value + type: FormNodeTypes.GROUP, + children: [ + { + name: 'testResult', + label: 'Result', + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + type: FormNodeTypes.CONTROL, + }, + { + name: 'reasonForAbandoning', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + value: null, + required: true, + }, + { + name: 'additionalCommentsForAbandon', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + value: null, + required: true, + }, + { + name: 'certificateNumber', + label: 'Certificate number', + value: '', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + }, + { + name: 'testExpiryDate', + label: 'Expiry Date', + disabled: true, + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + }, + { + name: 'testTypeStartTimestamp', + type: FormNodeTypes.CONTROL, + value: '', + label: 'Test start date and time', + viewType: FormNodeViewTypes.TIME, + editType: FormNodeEditTypes.DATETIME, + validators: [ + { name: ValidatorNames.Required }, + { name: ValidatorNames.PastDate }, + { name: ValidatorNames.CopyValueToRootControl, args: 'testStartTimestamp' }, + ], + }, + { + name: 'testTypeEndTimestamp', + type: FormNodeTypes.CONTROL, + value: '', + label: 'Test end date and time', + viewType: FormNodeViewTypes.TIME, + editType: FormNodeEditTypes.DATETIME, + validators: [ + { name: ValidatorNames.Required }, + { name: ValidatorNames.PastDate }, + { name: ValidatorNames.AheadOfDate, args: 'testTypeStartTimestamp' }, + { name: ValidatorNames.CopyValueToRootControl, args: 'testEndTimestamp' }, + ], + }, + { + name: 'prohibitionIssued', + label: 'Prohibition issued', + type: FormNodeTypes.CONTROL, + value: null, + editType: FormNodeEditTypes.HIDDEN, + required: true, + }, + ], + }, + ], + }, + ], }; diff --git a/src/app/forms/templates/test-records/section-templates/test/contingency/contingency-test-section-group12and14.template.ts b/src/app/forms/templates/test-records/section-templates/test/contingency/contingency-test-section-group12and14.template.ts index 5e79593724..ce0bb27e91 100644 --- a/src/app/forms/templates/test-records/section-templates/test/contingency/contingency-test-section-group12and14.template.ts +++ b/src/app/forms/templates/test-records/section-templates/test/contingency/contingency-test-section-group12and14.template.ts @@ -1,116 +1,124 @@ import { ValidatorNames } from '@forms/models/validators.enum'; import { - FormNode, FormNodeEditTypes, FormNodeTypes, FormNodeViewTypes, FormNodeWidth, + FormNode, + FormNodeEditTypes, + FormNodeTypes, + FormNodeViewTypes, + FormNodeWidth, } from '@forms/services/dynamic-form.types'; export const ContingencyTestSectionGroup12and14: FormNode = { - name: 'testSection', - label: 'Test', - type: FormNodeTypes.GROUP, - children: [ - { - name: 'contingencyTestNumber', - label: 'Contingency Test Number', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.NUMERICSTRING, - validators: [{ name: ValidatorNames.MaxLength, args: 8 }, { name: ValidatorNames.MinLength, args: 6 }, { name: ValidatorNames.Required }], - width: FormNodeWidth.L, - }, - { - name: 'testStartTimestamp', - label: 'Test start date', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - validators: [{ name: ValidatorNames.PastDate }], - }, - { - name: 'testEndTimestamp', - type: FormNodeTypes.CONTROL, - label: 'Test end date', - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - validators: [{ name: ValidatorNames.AheadOfDate, args: 'testStartTimestamp' }], - }, - { - name: 'testTypes', - label: 'Test Types', - type: FormNodeTypes.ARRAY, - children: [ - { - name: '0', // it is important here that the name of the node for an ARRAY type should be an index value - type: FormNodeTypes.GROUP, - children: [ - { - name: 'testResult', - label: 'Result', - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - value: null, - type: FormNodeTypes.CONTROL, - }, - { - name: 'reasonForAbandoning', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - value: null, - required: true, - }, - { - name: 'additionalCommentsForAbandon', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - value: null, - required: true, - }, - { - name: 'certificateNumber', - label: 'Certificate number', - value: '', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - }, - { - name: 'testTypeStartTimestamp', - type: FormNodeTypes.CONTROL, - value: '', - label: 'Test start date and time', - viewType: FormNodeViewTypes.TIME, - editType: FormNodeEditTypes.DATETIME, - validators: [ - { name: ValidatorNames.Required }, - { name: ValidatorNames.PastDate }, - { name: ValidatorNames.CopyValueToRootControl, args: 'testStartTimestamp' }, - ], - }, - { - name: 'testTypeEndTimestamp', - type: FormNodeTypes.CONTROL, - value: '', - label: 'Test end date and time', - viewType: FormNodeViewTypes.TIME, - editType: FormNodeEditTypes.DATETIME, - validators: [ - { name: ValidatorNames.Required }, - { name: ValidatorNames.PastDate }, - { name: ValidatorNames.AheadOfDate, args: 'testTypeStartTimestamp' }, - { name: ValidatorNames.CopyValueToRootControl, args: 'testEndTimestamp' }, - ], - }, - { - name: 'prohibitionIssued', - label: 'Prohibition issued', - type: FormNodeTypes.CONTROL, - value: null, - editType: FormNodeEditTypes.HIDDEN, - required: true, - }, - ], - }, - ], - }, - ], + name: 'testSection', + label: 'Test', + type: FormNodeTypes.GROUP, + children: [ + { + name: 'contingencyTestNumber', + label: 'Contingency Test Number', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.NUMERICSTRING, + validators: [ + { name: ValidatorNames.MaxLength, args: 8 }, + { name: ValidatorNames.MinLength, args: 6 }, + { name: ValidatorNames.Required }, + ], + width: FormNodeWidth.L, + }, + { + name: 'testStartTimestamp', + label: 'Test start date', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + validators: [{ name: ValidatorNames.PastDate }], + }, + { + name: 'testEndTimestamp', + type: FormNodeTypes.CONTROL, + label: 'Test end date', + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + validators: [{ name: ValidatorNames.AheadOfDate, args: 'testStartTimestamp' }], + }, + { + name: 'testTypes', + label: 'Test Types', + type: FormNodeTypes.ARRAY, + children: [ + { + name: '0', // it is important here that the name of the node for an ARRAY type should be an index value + type: FormNodeTypes.GROUP, + children: [ + { + name: 'testResult', + label: 'Result', + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + value: null, + type: FormNodeTypes.CONTROL, + }, + { + name: 'reasonForAbandoning', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + value: null, + required: true, + }, + { + name: 'additionalCommentsForAbandon', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + value: null, + required: true, + }, + { + name: 'certificateNumber', + label: 'Certificate number', + value: '', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + }, + { + name: 'testTypeStartTimestamp', + type: FormNodeTypes.CONTROL, + value: '', + label: 'Test start date and time', + viewType: FormNodeViewTypes.TIME, + editType: FormNodeEditTypes.DATETIME, + validators: [ + { name: ValidatorNames.Required }, + { name: ValidatorNames.PastDate }, + { name: ValidatorNames.CopyValueToRootControl, args: 'testStartTimestamp' }, + ], + }, + { + name: 'testTypeEndTimestamp', + type: FormNodeTypes.CONTROL, + value: '', + label: 'Test end date and time', + viewType: FormNodeViewTypes.TIME, + editType: FormNodeEditTypes.DATETIME, + validators: [ + { name: ValidatorNames.Required }, + { name: ValidatorNames.PastDate }, + { name: ValidatorNames.AheadOfDate, args: 'testTypeStartTimestamp' }, + { name: ValidatorNames.CopyValueToRootControl, args: 'testEndTimestamp' }, + ], + }, + { + name: 'prohibitionIssued', + label: 'Prohibition issued', + type: FormNodeTypes.CONTROL, + value: null, + editType: FormNodeEditTypes.HIDDEN, + required: true, + }, + ], + }, + ], + }, + ], }; diff --git a/src/app/forms/templates/test-records/section-templates/test/contingency/contingency-test-section-group15and16.template.ts b/src/app/forms/templates/test-records/section-templates/test/contingency/contingency-test-section-group15and16.template.ts index 5cfdd2b459..b0c30a31a6 100644 --- a/src/app/forms/templates/test-records/section-templates/test/contingency/contingency-test-section-group15and16.template.ts +++ b/src/app/forms/templates/test-records/section-templates/test/contingency/contingency-test-section-group15and16.template.ts @@ -1,149 +1,157 @@ import { AsyncValidatorNames } from '@forms/models/async-validators.enum'; import { ValidatorNames } from '@forms/models/validators.enum'; import { - FormNode, FormNodeEditTypes, FormNodeTypes, FormNodeViewTypes, FormNodeWidth, + FormNode, + FormNodeEditTypes, + FormNodeTypes, + FormNodeViewTypes, + FormNodeWidth, } from '@forms/services/dynamic-form.types'; export const ContingencyTestSectionGroup15and16: FormNode = { - name: 'testSection', - label: 'Test', - type: FormNodeTypes.GROUP, - children: [ - { - name: 'contingencyTestNumber', - label: 'Contingency Test Number', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.NUMERICSTRING, - validators: [{ name: ValidatorNames.MaxLength, args: 8 }, { name: ValidatorNames.MinLength, args: 6 }, { name: ValidatorNames.Required }], - width: FormNodeWidth.L, - }, - { - name: 'testStartTimestamp', - label: 'Test start date', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - validators: [{ name: ValidatorNames.PastDate }], - }, - { - name: 'testEndTimestamp', - type: FormNodeTypes.CONTROL, - label: 'Test end date', - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - validators: [{ name: ValidatorNames.AheadOfDate, args: 'testStartTimestamp' }], - }, - { - name: 'testTypes', - label: 'Test Types', - type: FormNodeTypes.ARRAY, - children: [ - { - name: '0', // it is important here that the name of the node for an ARRAY type should be an index value - type: FormNodeTypes.GROUP, - children: [ - { - name: 'testResult', - label: 'Result', - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.RADIO, - options: [ - { value: 'pass', label: 'Pass' }, - { value: 'fail', label: 'Fail' }, - ], - validators: [{ name: ValidatorNames.HideIfNotEqual, args: { sibling: 'testExpiryDate', value: 'pass' } }], - asyncValidators: [{ name: AsyncValidatorNames.PassResultDependantOnCustomDefects }], - type: FormNodeTypes.CONTROL, - }, - { - name: 'reasonForAbandoning', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - value: null, - required: true, - }, - { - name: 'additionalCommentsForAbandon', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - value: null, - required: true, - }, - { - name: 'certificateNumber', - label: 'Certificate number', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.TEXT, - validators: [{ name: ValidatorNames.Required }, { name: ValidatorNames.Alphanumeric }], - viewType: FormNodeViewTypes.HIDDEN, - required: true, - value: null, - }, - { - name: 'testExpiryDate', - label: 'Expiry Date', - value: '', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.TIME, - editType: FormNodeEditTypes.DATE, - validators: [ - { - name: ValidatorNames.RequiredIfEquals, - args: { sibling: 'testResult', value: ['pass'] }, - }, - { name: ValidatorNames.FutureDate }, - ], - }, - { - name: 'testTypeStartTimestamp', - type: FormNodeTypes.CONTROL, - value: '', - label: 'Test start date and time', - viewType: FormNodeViewTypes.TIME, - editType: FormNodeEditTypes.DATETIME, - validators: [ - { name: ValidatorNames.Required }, - { name: ValidatorNames.PastDate }, - { name: ValidatorNames.CopyValueToRootControl, args: 'testStartTimestamp' }, - ], - }, - { - name: 'testTypeEndTimestamp', - type: FormNodeTypes.CONTROL, - value: '', - label: 'Test end date and time', - viewType: FormNodeViewTypes.TIME, - editType: FormNodeEditTypes.DATETIME, - validators: [ - { name: ValidatorNames.Required }, - { name: ValidatorNames.PastDate }, - { name: ValidatorNames.AheadOfDate, args: 'testTypeStartTimestamp' }, - { name: ValidatorNames.CopyValueToRootControl, args: 'testEndTimestamp' }, - ], - }, - { - name: 'prohibitionIssued', - type: FormNodeTypes.CONTROL, - label: 'Prohibition issued', - value: null, - editType: FormNodeEditTypes.RADIO, - options: [ - { value: true, label: 'Yes' }, - { value: false, label: 'No' }, - ], - validators: [{ name: ValidatorNames.Required }], - }, - { - name: 'defects', - type: FormNodeTypes.ARRAY, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - }, - ], - }, - ], - }, - ], + name: 'testSection', + label: 'Test', + type: FormNodeTypes.GROUP, + children: [ + { + name: 'contingencyTestNumber', + label: 'Contingency Test Number', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.NUMERICSTRING, + validators: [ + { name: ValidatorNames.MaxLength, args: 8 }, + { name: ValidatorNames.MinLength, args: 6 }, + { name: ValidatorNames.Required }, + ], + width: FormNodeWidth.L, + }, + { + name: 'testStartTimestamp', + label: 'Test start date', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + validators: [{ name: ValidatorNames.PastDate }], + }, + { + name: 'testEndTimestamp', + type: FormNodeTypes.CONTROL, + label: 'Test end date', + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + validators: [{ name: ValidatorNames.AheadOfDate, args: 'testStartTimestamp' }], + }, + { + name: 'testTypes', + label: 'Test Types', + type: FormNodeTypes.ARRAY, + children: [ + { + name: '0', // it is important here that the name of the node for an ARRAY type should be an index value + type: FormNodeTypes.GROUP, + children: [ + { + name: 'testResult', + label: 'Result', + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.RADIO, + options: [ + { value: 'pass', label: 'Pass' }, + { value: 'fail', label: 'Fail' }, + ], + validators: [{ name: ValidatorNames.HideIfNotEqual, args: { sibling: 'testExpiryDate', value: 'pass' } }], + asyncValidators: [{ name: AsyncValidatorNames.PassResultDependantOnCustomDefects }], + type: FormNodeTypes.CONTROL, + }, + { + name: 'reasonForAbandoning', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + value: null, + required: true, + }, + { + name: 'additionalCommentsForAbandon', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + value: null, + required: true, + }, + { + name: 'certificateNumber', + label: 'Certificate number', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.TEXT, + validators: [{ name: ValidatorNames.Required }, { name: ValidatorNames.Alphanumeric }], + viewType: FormNodeViewTypes.HIDDEN, + required: true, + value: null, + }, + { + name: 'testExpiryDate', + label: 'Expiry Date', + value: '', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.TIME, + editType: FormNodeEditTypes.DATE, + validators: [ + { + name: ValidatorNames.RequiredIfEquals, + args: { sibling: 'testResult', value: ['pass'] }, + }, + { name: ValidatorNames.FutureDate }, + ], + }, + { + name: 'testTypeStartTimestamp', + type: FormNodeTypes.CONTROL, + value: '', + label: 'Test start date and time', + viewType: FormNodeViewTypes.TIME, + editType: FormNodeEditTypes.DATETIME, + validators: [ + { name: ValidatorNames.Required }, + { name: ValidatorNames.PastDate }, + { name: ValidatorNames.CopyValueToRootControl, args: 'testStartTimestamp' }, + ], + }, + { + name: 'testTypeEndTimestamp', + type: FormNodeTypes.CONTROL, + value: '', + label: 'Test end date and time', + viewType: FormNodeViewTypes.TIME, + editType: FormNodeEditTypes.DATETIME, + validators: [ + { name: ValidatorNames.Required }, + { name: ValidatorNames.PastDate }, + { name: ValidatorNames.AheadOfDate, args: 'testTypeStartTimestamp' }, + { name: ValidatorNames.CopyValueToRootControl, args: 'testEndTimestamp' }, + ], + }, + { + name: 'prohibitionIssued', + type: FormNodeTypes.CONTROL, + label: 'Prohibition issued', + value: null, + editType: FormNodeEditTypes.RADIO, + options: [ + { value: true, label: 'Yes' }, + { value: false, label: 'No' }, + ], + validators: [{ name: ValidatorNames.Required }], + }, + { + name: 'defects', + type: FormNodeTypes.ARRAY, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + }, + ], + }, + ], + }, + ], }; diff --git a/src/app/forms/templates/test-records/section-templates/test/contingency/contingency-test-section-group3And4And8.template.ts b/src/app/forms/templates/test-records/section-templates/test/contingency/contingency-test-section-group3And4And8.template.ts index 3fee879648..ae79b30acd 100644 --- a/src/app/forms/templates/test-records/section-templates/test/contingency/contingency-test-section-group3And4And8.template.ts +++ b/src/app/forms/templates/test-records/section-templates/test/contingency/contingency-test-section-group3And4And8.template.ts @@ -1,133 +1,141 @@ import { AsyncValidatorNames } from '@forms/models/async-validators.enum'; import { ValidatorNames } from '@forms/models/validators.enum'; import { - FormNode, FormNodeEditTypes, FormNodeTypes, FormNodeViewTypes, FormNodeWidth, + FormNode, + FormNodeEditTypes, + FormNodeTypes, + FormNodeViewTypes, + FormNodeWidth, } from '@forms/services/dynamic-form.types'; export const ContingencyTestSectionGroup3And4And8: FormNode = { - name: 'testSection', - label: 'Test', - type: FormNodeTypes.GROUP, - children: [ - { - name: 'contingencyTestNumber', - label: 'Contingency Test Number', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.NUMERICSTRING, - validators: [{ name: ValidatorNames.MaxLength, args: 8 }, { name: ValidatorNames.MinLength, args: 6 }, { name: ValidatorNames.Required }], - width: FormNodeWidth.L, - }, - { - name: 'testStartTimestamp', - label: 'Test start date', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - validators: [{ name: ValidatorNames.PastDate }], - }, - { - name: 'testEndTimestamp', - type: FormNodeTypes.CONTROL, - label: 'Test end date', - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - validators: [{ name: ValidatorNames.AheadOfDate, args: 'testStartTimestamp' }], - }, - { - name: 'testTypes', - label: 'Test Types', - type: FormNodeTypes.ARRAY, - children: [ - { - name: '0', // it is important here that the name of the node for an ARRAY type should be an index value - type: FormNodeTypes.GROUP, - children: [ - { - name: 'testResult', - label: 'Result', - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.RADIO, - options: [ - { value: 'pass', label: 'Pass' }, - { value: 'fail', label: 'Fail' }, - ], - asyncValidators: [{ name: AsyncValidatorNames.ResultDependantOnCustomDefects }], - type: FormNodeTypes.CONTROL, - }, - { - name: 'testTypeName', - label: 'Description', - value: '', - disabled: true, + name: 'testSection', + label: 'Test', + type: FormNodeTypes.GROUP, + children: [ + { + name: 'contingencyTestNumber', + label: 'Contingency Test Number', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.NUMERICSTRING, + validators: [ + { name: ValidatorNames.MaxLength, args: 8 }, + { name: ValidatorNames.MinLength, args: 6 }, + { name: ValidatorNames.Required }, + ], + width: FormNodeWidth.L, + }, + { + name: 'testStartTimestamp', + label: 'Test start date', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + validators: [{ name: ValidatorNames.PastDate }], + }, + { + name: 'testEndTimestamp', + type: FormNodeTypes.CONTROL, + label: 'Test end date', + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + validators: [{ name: ValidatorNames.AheadOfDate, args: 'testStartTimestamp' }], + }, + { + name: 'testTypes', + label: 'Test Types', + type: FormNodeTypes.ARRAY, + children: [ + { + name: '0', // it is important here that the name of the node for an ARRAY type should be an index value + type: FormNodeTypes.GROUP, + children: [ + { + name: 'testResult', + label: 'Result', + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.RADIO, + options: [ + { value: 'pass', label: 'Pass' }, + { value: 'fail', label: 'Fail' }, + ], + asyncValidators: [{ name: AsyncValidatorNames.ResultDependantOnCustomDefects }], + type: FormNodeTypes.CONTROL, + }, + { + name: 'testTypeName', + label: 'Description', + value: '', + disabled: true, - type: FormNodeTypes.CONTROL, - }, - { - name: 'reasonForAbandoning', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - value: null, - required: true, - }, - { - name: 'additionalCommentsForAbandon', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - value: null, - required: true, - }, - { - name: 'certificateNumber', - label: 'Certificate number', - value: '', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - }, - { - name: 'testTypeStartTimestamp', - type: FormNodeTypes.CONTROL, - value: '', - label: 'Test start date and time', - viewType: FormNodeViewTypes.TIME, - editType: FormNodeEditTypes.DATETIME, - validators: [ - { name: ValidatorNames.Required }, - { name: ValidatorNames.PastDate }, - { name: ValidatorNames.CopyValueToRootControl, args: 'testStartTimestamp' }, - ], - }, - { - name: 'testTypeEndTimestamp', - type: FormNodeTypes.CONTROL, - value: '', - label: 'Test end date and time', - viewType: FormNodeViewTypes.TIME, - editType: FormNodeEditTypes.DATETIME, - validators: [ - { name: ValidatorNames.Required }, - { name: ValidatorNames.PastDate }, - { name: ValidatorNames.AheadOfDate, args: 'testTypeStartTimestamp' }, - { name: ValidatorNames.CopyValueToRootControl, args: 'testEndTimestamp' }, - ], - }, - { - name: 'prohibitionIssued', - type: FormNodeTypes.CONTROL, - label: 'Prohibition issued', - value: null, - editType: FormNodeEditTypes.RADIO, - options: [ - { value: true, label: 'Yes' }, - { value: false, label: 'No' }, - ], - validators: [{ name: ValidatorNames.Required }], - }, - ], - }, - ], - }, - ], + type: FormNodeTypes.CONTROL, + }, + { + name: 'reasonForAbandoning', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + value: null, + required: true, + }, + { + name: 'additionalCommentsForAbandon', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + value: null, + required: true, + }, + { + name: 'certificateNumber', + label: 'Certificate number', + value: '', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + }, + { + name: 'testTypeStartTimestamp', + type: FormNodeTypes.CONTROL, + value: '', + label: 'Test start date and time', + viewType: FormNodeViewTypes.TIME, + editType: FormNodeEditTypes.DATETIME, + validators: [ + { name: ValidatorNames.Required }, + { name: ValidatorNames.PastDate }, + { name: ValidatorNames.CopyValueToRootControl, args: 'testStartTimestamp' }, + ], + }, + { + name: 'testTypeEndTimestamp', + type: FormNodeTypes.CONTROL, + value: '', + label: 'Test end date and time', + viewType: FormNodeViewTypes.TIME, + editType: FormNodeEditTypes.DATETIME, + validators: [ + { name: ValidatorNames.Required }, + { name: ValidatorNames.PastDate }, + { name: ValidatorNames.AheadOfDate, args: 'testTypeStartTimestamp' }, + { name: ValidatorNames.CopyValueToRootControl, args: 'testEndTimestamp' }, + ], + }, + { + name: 'prohibitionIssued', + type: FormNodeTypes.CONTROL, + label: 'Prohibition issued', + value: null, + editType: FormNodeEditTypes.RADIO, + options: [ + { value: true, label: 'Yes' }, + { value: false, label: 'No' }, + ], + validators: [{ name: ValidatorNames.Required }], + }, + ], + }, + ], + }, + ], }; diff --git a/src/app/forms/templates/test-records/section-templates/test/contingency/contingency-test-section-group5And13.template.ts b/src/app/forms/templates/test-records/section-templates/test/contingency/contingency-test-section-group5And13.template.ts index 9cceae04f5..512e57aed4 100644 --- a/src/app/forms/templates/test-records/section-templates/test/contingency/contingency-test-section-group5And13.template.ts +++ b/src/app/forms/templates/test-records/section-templates/test/contingency/contingency-test-section-group5And13.template.ts @@ -1,140 +1,179 @@ import { AsyncValidatorNames } from '@forms/models/async-validators.enum'; import { ValidatorNames } from '@forms/models/validators.enum'; import { - FormNode, FormNodeEditTypes, FormNodeTypes, FormNodeViewTypes, FormNodeWidth, + FormNode, + FormNodeEditTypes, + FormNodeTypes, + FormNodeViewTypes, + FormNodeWidth, } from '@forms/services/dynamic-form.types'; export const ContingencyTestSectionGroup5And13: FormNode = { - name: 'testSection', - label: 'Test', - type: FormNodeTypes.GROUP, - children: [ - { - name: 'contingencyTestNumber', - label: 'Contingency Test Number', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.NUMERICSTRING, - validators: [{ name: ValidatorNames.MaxLength, args: 8 }, { name: ValidatorNames.MinLength, args: 6 }, { name: ValidatorNames.Required }], - width: FormNodeWidth.L, - }, - { - name: 'testStartTimestamp', - label: 'Test start date', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - validators: [{ name: ValidatorNames.PastDate }], - }, - { - name: 'testEndTimestamp', - type: FormNodeTypes.CONTROL, - label: 'Test end date', - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - validators: [{ name: ValidatorNames.AheadOfDate, args: 'testStartTimestamp' }], - }, - { - name: 'testTypes', - label: 'Test Types', - type: FormNodeTypes.ARRAY, - children: [ - { - name: '0', // it is important here that the name of the node for an ARRAY type should be an index value - type: FormNodeTypes.GROUP, - children: [ - { - name: 'testResult', - label: 'Result', - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.RADIO, - options: [ - { value: 'pass', label: 'Pass' }, - { value: 'fail', label: 'Fail' }, - ], - asyncValidators: [{ name: AsyncValidatorNames.ResultDependantOnCustomDefects }], - validators: [{ name: ValidatorNames.HideIfNotEqual, args: { sibling: 'certificateNumber', value: 'pass' } }], - type: FormNodeTypes.CONTROL, - }, - { - name: 'testTypeName', - label: 'Description', - value: '', - disabled: true, + name: 'testSection', + label: 'Test', + type: FormNodeTypes.GROUP, + children: [ + { + name: 'contingencyTestNumber', + label: 'Contingency Test Number', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.NUMERICSTRING, + validators: [ + { name: ValidatorNames.MaxLength, args: 8 }, + { name: ValidatorNames.MinLength, args: 6 }, + { name: ValidatorNames.Required }, + ], + width: FormNodeWidth.L, + }, + { + name: 'testStartTimestamp', + label: 'Test start date', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + validators: [{ name: ValidatorNames.PastDate }], + }, + { + name: 'testEndTimestamp', + type: FormNodeTypes.CONTROL, + label: 'Test end date', + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + validators: [{ name: ValidatorNames.AheadOfDate, args: 'testStartTimestamp' }], + }, + { + name: 'testTypes', + label: 'Test Types', + type: FormNodeTypes.ARRAY, + children: [ + { + name: '0', // it is important here that the name of the node for an ARRAY type should be an index value + type: FormNodeTypes.GROUP, + children: [ + { + name: 'testResult', + label: 'Result', + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.RADIO, + options: [ + { value: 'pass', label: 'Pass' }, + { value: 'fail', label: 'Fail' }, + ], + asyncValidators: [{ name: AsyncValidatorNames.ResultDependantOnCustomDefects }], + validators: [ + { name: ValidatorNames.HideIfNotEqual, args: { sibling: 'certificateNumber', value: 'pass' } }, + { name: ValidatorNames.HideIfNotEqual, args: { sibling: 'centralDocs', value: 'pass' } }, + ], + type: FormNodeTypes.CONTROL, + }, + { + name: 'centralDocs', + type: FormNodeTypes.GROUP, + children: [ + { + name: 'issueRequired', + type: FormNodeTypes.CONTROL, + label: 'Issue documents centrally', + editType: FormNodeEditTypes.RADIO, + value: false, + options: [ + { value: true, label: 'Yes' }, + { value: false, label: 'No' }, + ], + validators: [ + { + name: ValidatorNames.HideIfParentSiblingEqual, + args: { sibling: 'certificateNumber', value: true }, + }, + ], + }, + { + name: 'reasonsForIssue', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + value: [], + }, + ], + }, + { + name: 'testTypeName', + label: 'Description', + value: '', + disabled: true, - type: FormNodeTypes.CONTROL, - }, - { - name: 'reasonForAbandoning', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - value: null, - required: true, - }, - { - name: 'additionalCommentsForAbandon', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - value: null, - required: true, - }, - { - name: 'certificateNumber', - label: 'Certificate number', - type: FormNodeTypes.CONTROL, - validators: [ - { name: ValidatorNames.Alphanumeric }, - { - name: ValidatorNames.RequiredIfEquals, - args: { sibling: 'testResult', value: ['pass'] }, - }, - ], - required: true, - value: null, - }, - { - name: 'testTypeStartTimestamp', - type: FormNodeTypes.CONTROL, - value: '', - label: 'Test start date and time', - viewType: FormNodeViewTypes.TIME, - editType: FormNodeEditTypes.DATETIME, - validators: [ - { name: ValidatorNames.Required }, - { name: ValidatorNames.PastDate }, - { name: ValidatorNames.CopyValueToRootControl, args: 'testStartTimestamp' }, - ], - }, - { - name: 'testTypeEndTimestamp', - type: FormNodeTypes.CONTROL, - value: '', - label: 'Test end date and time', - viewType: FormNodeViewTypes.TIME, - editType: FormNodeEditTypes.DATETIME, - validators: [ - { name: ValidatorNames.Required }, - { name: ValidatorNames.PastDate }, - { name: ValidatorNames.AheadOfDate, args: 'testTypeStartTimestamp' }, - { name: ValidatorNames.CopyValueToRootControl, args: 'testEndTimestamp' }, - ], - }, - { - name: 'prohibitionIssued', - type: FormNodeTypes.CONTROL, - label: 'Prohibition issued', - value: null, - editType: FormNodeEditTypes.RADIO, - options: [ - { value: true, label: 'Yes' }, - { value: false, label: 'No' }, - ], - validators: [{ name: ValidatorNames.Required }], - }, - ], - }, - ], - }, - ], + type: FormNodeTypes.CONTROL, + }, + { + name: 'reasonForAbandoning', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + value: null, + required: true, + }, + { + name: 'additionalCommentsForAbandon', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + value: null, + required: true, + }, + { + name: 'certificateNumber', + label: 'Certificate number', + type: FormNodeTypes.CONTROL, + validators: [ + { name: ValidatorNames.Alphanumeric }, + // Make required if test result is pass/prs, but issue documents centrally is false + { name: ValidatorNames.IssueRequired }, + ], + required: true, + value: null, + }, + { + name: 'testTypeStartTimestamp', + type: FormNodeTypes.CONTROL, + value: '', + label: 'Test start date and time', + viewType: FormNodeViewTypes.TIME, + editType: FormNodeEditTypes.DATETIME, + validators: [ + { name: ValidatorNames.Required }, + { name: ValidatorNames.PastDate }, + { name: ValidatorNames.CopyValueToRootControl, args: 'testStartTimestamp' }, + ], + }, + { + name: 'testTypeEndTimestamp', + type: FormNodeTypes.CONTROL, + value: '', + label: 'Test end date and time', + viewType: FormNodeViewTypes.TIME, + editType: FormNodeEditTypes.DATETIME, + validators: [ + { name: ValidatorNames.Required }, + { name: ValidatorNames.PastDate }, + { name: ValidatorNames.AheadOfDate, args: 'testTypeStartTimestamp' }, + { name: ValidatorNames.CopyValueToRootControl, args: 'testEndTimestamp' }, + ], + }, + { + name: 'prohibitionIssued', + type: FormNodeTypes.CONTROL, + label: 'Prohibition issued', + value: null, + editType: FormNodeEditTypes.RADIO, + options: [ + { value: true, label: 'Yes' }, + { value: false, label: 'No' }, + ], + validators: [{ name: ValidatorNames.Required }], + }, + ], + }, + ], + }, + ], }; diff --git a/src/app/forms/templates/test-records/section-templates/test/contingency/contingency-test-section-group6And11.template.ts b/src/app/forms/templates/test-records/section-templates/test/contingency/contingency-test-section-group6And11.template.ts index bb48b82af8..edae8e8760 100644 --- a/src/app/forms/templates/test-records/section-templates/test/contingency/contingency-test-section-group6And11.template.ts +++ b/src/app/forms/templates/test-records/section-templates/test/contingency/contingency-test-section-group6And11.template.ts @@ -1,123 +1,131 @@ import { ValidatorNames } from '@forms/models/validators.enum'; import { - FormNode, FormNodeEditTypes, FormNodeTypes, FormNodeViewTypes, FormNodeWidth, + FormNode, + FormNodeEditTypes, + FormNodeTypes, + FormNodeViewTypes, + FormNodeWidth, } from '@forms/services/dynamic-form.types'; export const ContingencyTestSectionGroup6And11: FormNode = { - name: 'testSection', - label: 'Test', - type: FormNodeTypes.GROUP, - children: [ - { - name: 'contingencyTestNumber', - label: 'Contingency Test Number', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.NUMERICSTRING, - validators: [{ name: ValidatorNames.MaxLength, args: 8 }, { name: ValidatorNames.MinLength, args: 6 }, { name: ValidatorNames.Required }], - width: FormNodeWidth.L, - }, - { - name: 'testStartTimestamp', - label: 'Test start date', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - validators: [{ name: ValidatorNames.PastDate }], - }, - { - name: 'testEndTimestamp', - type: FormNodeTypes.CONTROL, - label: 'Test end date', - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - validators: [{ name: ValidatorNames.AheadOfDate, args: 'testStartTimestamp' }], - }, - { - name: 'testTypes', - label: 'Test Types', - type: FormNodeTypes.ARRAY, - children: [ - { - name: '0', // it is important here that the name of the node for an ARRAY type should be an index value - type: FormNodeTypes.GROUP, - children: [ - { - name: 'testResult', - label: 'Result', - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - value: null, - type: FormNodeTypes.CONTROL, - }, - { - name: 'testTypeName', - label: 'Description', - value: '', - disabled: true, - type: FormNodeTypes.CONTROL, - }, - { - name: 'reasonForAbandoning', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - value: null, - required: true, - }, - { - name: 'additionalCommentsForAbandon', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - value: null, - required: true, - }, - { - name: 'certificateNumber', - label: 'Certificate number', - value: '', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - }, - { - name: 'testTypeStartTimestamp', - type: FormNodeTypes.CONTROL, - value: '', - label: 'Test start date and time', - viewType: FormNodeViewTypes.TIME, - editType: FormNodeEditTypes.DATETIME, - validators: [ - { name: ValidatorNames.Required }, - { name: ValidatorNames.PastDate }, - { name: ValidatorNames.CopyValueToRootControl, args: 'testStartTimestamp' }, - ], - }, - { - name: 'testTypeEndTimestamp', - type: FormNodeTypes.CONTROL, - value: '', - label: 'Test end date and time', - viewType: FormNodeViewTypes.TIME, - editType: FormNodeEditTypes.DATETIME, - validators: [ - { name: ValidatorNames.Required }, - { name: ValidatorNames.PastDate }, - { name: ValidatorNames.AheadOfDate, args: 'testTypeStartTimestamp' }, - { name: ValidatorNames.CopyValueToRootControl, args: 'testEndTimestamp' }, - ], - }, - { - name: 'prohibitionIssued', - label: 'Prohibition issued', - type: FormNodeTypes.CONTROL, - value: null, - editType: FormNodeEditTypes.HIDDEN, - required: true, - }, - ], - }, - ], - }, - ], + name: 'testSection', + label: 'Test', + type: FormNodeTypes.GROUP, + children: [ + { + name: 'contingencyTestNumber', + label: 'Contingency Test Number', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.NUMERICSTRING, + validators: [ + { name: ValidatorNames.MaxLength, args: 8 }, + { name: ValidatorNames.MinLength, args: 6 }, + { name: ValidatorNames.Required }, + ], + width: FormNodeWidth.L, + }, + { + name: 'testStartTimestamp', + label: 'Test start date', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + validators: [{ name: ValidatorNames.PastDate }], + }, + { + name: 'testEndTimestamp', + type: FormNodeTypes.CONTROL, + label: 'Test end date', + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + validators: [{ name: ValidatorNames.AheadOfDate, args: 'testStartTimestamp' }], + }, + { + name: 'testTypes', + label: 'Test Types', + type: FormNodeTypes.ARRAY, + children: [ + { + name: '0', // it is important here that the name of the node for an ARRAY type should be an index value + type: FormNodeTypes.GROUP, + children: [ + { + name: 'testResult', + label: 'Result', + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + value: null, + type: FormNodeTypes.CONTROL, + }, + { + name: 'testTypeName', + label: 'Description', + value: '', + disabled: true, + type: FormNodeTypes.CONTROL, + }, + { + name: 'reasonForAbandoning', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + value: null, + required: true, + }, + { + name: 'additionalCommentsForAbandon', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + value: null, + required: true, + }, + { + name: 'certificateNumber', + label: 'Certificate number', + value: '', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + }, + { + name: 'testTypeStartTimestamp', + type: FormNodeTypes.CONTROL, + value: '', + label: 'Test start date and time', + viewType: FormNodeViewTypes.TIME, + editType: FormNodeEditTypes.DATETIME, + validators: [ + { name: ValidatorNames.Required }, + { name: ValidatorNames.PastDate }, + { name: ValidatorNames.CopyValueToRootControl, args: 'testStartTimestamp' }, + ], + }, + { + name: 'testTypeEndTimestamp', + type: FormNodeTypes.CONTROL, + value: '', + label: 'Test end date and time', + viewType: FormNodeViewTypes.TIME, + editType: FormNodeEditTypes.DATETIME, + validators: [ + { name: ValidatorNames.Required }, + { name: ValidatorNames.PastDate }, + { name: ValidatorNames.AheadOfDate, args: 'testTypeStartTimestamp' }, + { name: ValidatorNames.CopyValueToRootControl, args: 'testEndTimestamp' }, + ], + }, + { + name: 'prohibitionIssued', + label: 'Prohibition issued', + type: FormNodeTypes.CONTROL, + value: null, + editType: FormNodeEditTypes.HIDDEN, + required: true, + }, + ], + }, + ], + }, + ], }; diff --git a/src/app/forms/templates/test-records/section-templates/test/contingency/contingency-test-section-group7.template.ts b/src/app/forms/templates/test-records/section-templates/test/contingency/contingency-test-section-group7.template.ts index 93b7789397..4b5f02ff72 100644 --- a/src/app/forms/templates/test-records/section-templates/test/contingency/contingency-test-section-group7.template.ts +++ b/src/app/forms/templates/test-records/section-templates/test/contingency/contingency-test-section-group7.template.ts @@ -2,172 +2,209 @@ import { ContingencyAdrGenerateCertComponent } from '@forms/components/contingen import { AsyncValidatorNames } from '@forms/models/async-validators.enum'; import { ValidatorNames } from '@forms/models/validators.enum'; import { - FormNode, FormNodeEditTypes, FormNodeTypes, FormNodeViewTypes, FormNodeWidth, + FormNode, + FormNodeEditTypes, + FormNodeTypes, + FormNodeViewTypes, + FormNodeWidth, } from '@forms/services/dynamic-form.types'; export const ContingencyTestSectionGroup7: FormNode = { - name: 'testSection', - label: 'Test', - type: FormNodeTypes.GROUP, - children: [ - { - name: 'contingencyTestNumber', - label: 'Contingency Test Number', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.NUMERICSTRING, - validators: [{ name: ValidatorNames.MaxLength, args: 8 }, { name: ValidatorNames.MinLength, args: 6 }, { name: ValidatorNames.Required }], - width: FormNodeWidth.L, - }, - { - name: 'testStartTimestamp', - label: 'Test start date', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - validators: [{ name: ValidatorNames.PastDate }], - }, - { - name: 'testEndTimestamp', - type: FormNodeTypes.CONTROL, - label: 'Test end date', - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - validators: [{ name: ValidatorNames.AheadOfDate, args: 'testStartTimestamp' }], - }, - { - name: 'testTypes', - label: 'Test Types', - type: FormNodeTypes.ARRAY, - children: [ - { - name: '0', // it is important here that the name of the node for an ARRAY type should be an index value - type: FormNodeTypes.GROUP, - children: [ - { - name: 'testResult', - label: 'Result', - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.RADIO, - options: [ - { value: 'pass', label: 'Pass' }, - { value: 'fail', label: 'Fail' }, - ], - validators: [ - { name: ValidatorNames.HideIfNotEqual, args: { sibling: 'certificateNumber', value: 'pass' } }, - { name: ValidatorNames.HideIfNotEqual, args: { sibling: 'generateCert', value: 'pass' } }, - { name: ValidatorNames.HideIfNotEqual, args: { sibling: 'testExpiryDate', value: 'pass' } }, - ], - asyncValidators: [{ name: AsyncValidatorNames.ResultDependantOnCustomDefects }], - type: FormNodeTypes.CONTROL, - }, - { - name: 'testTypeName', - label: 'Description', - value: '', - disabled: true, + name: 'testSection', + label: 'Test', + type: FormNodeTypes.GROUP, + children: [ + { + name: 'contingencyTestNumber', + label: 'Contingency Test Number', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.NUMERICSTRING, + validators: [ + { name: ValidatorNames.MaxLength, args: 8 }, + { name: ValidatorNames.MinLength, args: 6 }, + { name: ValidatorNames.Required }, + ], + width: FormNodeWidth.L, + }, + { + name: 'testStartTimestamp', + label: 'Test start date', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + validators: [{ name: ValidatorNames.PastDate }], + }, + { + name: 'testEndTimestamp', + type: FormNodeTypes.CONTROL, + label: 'Test end date', + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + validators: [{ name: ValidatorNames.AheadOfDate, args: 'testStartTimestamp' }], + }, + { + name: 'testTypes', + label: 'Test Types', + type: FormNodeTypes.ARRAY, + children: [ + { + name: '0', // it is important here that the name of the node for an ARRAY type should be an index value + type: FormNodeTypes.GROUP, + children: [ + { + name: 'testResult', + label: 'Result', + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.RADIO, + options: [ + { value: 'pass', label: 'Pass' }, + { value: 'fail', label: 'Fail' }, + ], + validators: [ + { name: ValidatorNames.HideIfNotEqual, args: { sibling: 'certificateNumber', value: 'pass' } }, + { name: ValidatorNames.HideIfNotEqual, args: { sibling: 'generateCert', value: 'pass' } }, + { name: ValidatorNames.HideIfNotEqual, args: { sibling: 'testExpiryDate', value: 'pass' } }, + { name: ValidatorNames.HideIfNotEqual, args: { sibling: 'centralDocs', value: 'pass' } }, + ], + asyncValidators: [{ name: AsyncValidatorNames.ResultDependantOnCustomDefects }], + type: FormNodeTypes.CONTROL, + }, + { + name: 'centralDocs', + type: FormNodeTypes.GROUP, + children: [ + { + name: 'issueRequired', + type: FormNodeTypes.CONTROL, + label: 'Issue documents centrally', + editType: FormNodeEditTypes.RADIO, + value: false, + options: [ + { value: true, label: 'Yes' }, + { value: false, label: 'No' }, + ], + validators: [ + { + name: ValidatorNames.HideIfParentSiblingEqual, + args: { sibling: 'certificateNumber', value: true }, + }, + ], + }, + { + name: 'reasonsForIssue', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + value: [], + }, + ], + }, + { + name: 'testTypeName', + label: 'Description', + value: '', + disabled: true, - type: FormNodeTypes.CONTROL, - }, - { - name: 'reasonForAbandoning', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - value: null, - required: true, - }, - { - name: 'additionalCommentsForAbandon', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - value: null, - required: true, - }, - { - name: 'generateCert', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.CUSTOM, - editComponent: ContingencyAdrGenerateCertComponent, - validators: [ - { - name: ValidatorNames.ShowGroupsWhenEqualTo, - args: { sibling: 'testResult', value: ['pass'] }, - }, - ], - }, - { - name: 'certificateNumber', - label: 'Certificate number', - type: FormNodeTypes.CONTROL, - validators: [ - { name: ValidatorNames.Alphanumeric }, - { - name: ValidatorNames.RequiredIfEquals, - args: { sibling: 'testResult', value: ['pass'] }, - }, - ], - required: true, - value: null, - }, - { - name: 'testExpiryDate', - label: 'Expiry Date', - value: '', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.TIME, - editType: FormNodeEditTypes.DATE, - validators: [ - { - name: ValidatorNames.RequiredIfEquals, - args: { sibling: 'testResult', value: ['pass'] }, - }, - { name: ValidatorNames.FutureDate }, - ], - }, - { - name: 'testTypeStartTimestamp', - type: FormNodeTypes.CONTROL, - value: '', - label: 'Test start date and time', - viewType: FormNodeViewTypes.TIME, - editType: FormNodeEditTypes.DATETIME, - validators: [ - { name: ValidatorNames.Required }, - { name: ValidatorNames.PastDate }, - { name: ValidatorNames.CopyValueToRootControl, args: 'testStartTimestamp' }, - ], - }, - { - name: 'testTypeEndTimestamp', - type: FormNodeTypes.CONTROL, - value: '', - label: 'Test end date and time', - viewType: FormNodeViewTypes.TIME, - editType: FormNodeEditTypes.DATETIME, - validators: [ - { name: ValidatorNames.Required }, - { name: ValidatorNames.PastDate }, - { name: ValidatorNames.AheadOfDate, args: 'testTypeStartTimestamp' }, - { name: ValidatorNames.CopyValueToRootControl, args: 'testEndTimestamp' }, - ], - }, - { - name: 'prohibitionIssued', - type: FormNodeTypes.CONTROL, - label: 'Prohibition issued', - value: null, - editType: FormNodeEditTypes.RADIO, - options: [ - { value: true, label: 'Yes' }, - { value: false, label: 'No' }, - ], - validators: [{ name: ValidatorNames.Required }], - }, - ], - }, - ], - }, - ], + type: FormNodeTypes.CONTROL, + }, + { + name: 'reasonForAbandoning', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + value: null, + required: true, + }, + { + name: 'additionalCommentsForAbandon', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + value: null, + required: true, + }, + { + name: 'generateCert', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.CUSTOM, + editComponent: ContingencyAdrGenerateCertComponent, + validators: [ + { + name: ValidatorNames.ShowGroupsWhenEqualTo, + args: { sibling: 'testResult', value: ['pass'] }, + }, + ], + }, + { + name: 'certificateNumber', + label: 'Certificate number', + type: FormNodeTypes.CONTROL, + validators: [ + { name: ValidatorNames.Alphanumeric }, + // Make required if test result is pass/prs, but issue documents centrally is false + { name: ValidatorNames.IssueRequired }, + ], + required: true, + value: null, + }, + { + name: 'testExpiryDate', + label: 'Expiry Date', + value: '', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.TIME, + editType: FormNodeEditTypes.DATE, + validators: [ + { + name: ValidatorNames.RequiredIfEquals, + args: { sibling: 'testResult', value: ['pass'] }, + }, + { name: ValidatorNames.FutureDate }, + ], + }, + { + name: 'testTypeStartTimestamp', + type: FormNodeTypes.CONTROL, + value: '', + label: 'Test start date and time', + viewType: FormNodeViewTypes.TIME, + editType: FormNodeEditTypes.DATETIME, + validators: [ + { name: ValidatorNames.Required }, + { name: ValidatorNames.PastDate }, + { name: ValidatorNames.CopyValueToRootControl, args: 'testStartTimestamp' }, + ], + }, + { + name: 'testTypeEndTimestamp', + type: FormNodeTypes.CONTROL, + value: '', + label: 'Test end date and time', + viewType: FormNodeViewTypes.TIME, + editType: FormNodeEditTypes.DATETIME, + validators: [ + { name: ValidatorNames.Required }, + { name: ValidatorNames.PastDate }, + { name: ValidatorNames.AheadOfDate, args: 'testTypeStartTimestamp' }, + { name: ValidatorNames.CopyValueToRootControl, args: 'testEndTimestamp' }, + ], + }, + { + name: 'prohibitionIssued', + type: FormNodeTypes.CONTROL, + label: 'Prohibition issued', + value: null, + editType: FormNodeEditTypes.RADIO, + options: [ + { value: true, label: 'Yes' }, + { value: false, label: 'No' }, + ], + validators: [{ name: ValidatorNames.Required }], + }, + ], + }, + ], + }, + ], }; diff --git a/src/app/forms/templates/test-records/section-templates/test/contingency/contingency-test-section-group8Notifiable.template.ts b/src/app/forms/templates/test-records/section-templates/test/contingency/contingency-test-section-group8Notifiable.template.ts new file mode 100644 index 0000000000..d3eff21dc3 --- /dev/null +++ b/src/app/forms/templates/test-records/section-templates/test/contingency/contingency-test-section-group8Notifiable.template.ts @@ -0,0 +1,166 @@ +import { AsyncValidatorNames } from '@forms/models/async-validators.enum'; +import { ValidatorNames } from '@forms/models/validators.enum'; +import { + FormNode, + FormNodeEditTypes, + FormNodeTypes, + FormNodeViewTypes, + FormNodeWidth, +} from '@forms/services/dynamic-form.types'; + +export const ContingencyTestSectionGroup8Notifiable: FormNode = { + name: 'testSection', + label: 'Test', + type: FormNodeTypes.GROUP, + children: [ + { + name: 'contingencyTestNumber', + label: 'Contingency Test Number', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.NUMERICSTRING, + validators: [ + { name: ValidatorNames.MaxLength, args: 8 }, + { name: ValidatorNames.MinLength, args: 6 }, + { name: ValidatorNames.Required }, + ], + width: FormNodeWidth.L, + }, + { + name: 'testStartTimestamp', + label: 'Test start date', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + validators: [{ name: ValidatorNames.PastDate }], + }, + { + name: 'testEndTimestamp', + type: FormNodeTypes.CONTROL, + label: 'Test end date', + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + validators: [{ name: ValidatorNames.AheadOfDate, args: 'testStartTimestamp' }], + }, + { + name: 'testTypes', + label: 'Test Types', + type: FormNodeTypes.ARRAY, + children: [ + { + name: '0', // it is important here that the name of the node for an ARRAY type should be an index value + type: FormNodeTypes.GROUP, + children: [ + { + name: 'testResult', + label: 'Result', + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.RADIO, + options: [ + { value: 'pass', label: 'Pass' }, + { value: 'fail', label: 'Fail' }, + ], + validators: [{ name: ValidatorNames.HideIfNotEqual, args: { sibling: 'centralDocs', value: ['pass'] } }], + asyncValidators: [{ name: AsyncValidatorNames.ResultDependantOnCustomDefects }], + type: FormNodeTypes.CONTROL, + }, + { + name: 'centralDocs', + type: FormNodeTypes.GROUP, + children: [ + { + name: 'issueRequired', + type: FormNodeTypes.CONTROL, + label: 'Issue documents centrally', + editType: FormNodeEditTypes.RADIO, + value: false, + options: [ + { value: true, label: 'Yes' }, + { value: false, label: 'No' }, + ], + }, + { + name: 'reasonsForIssue', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + value: [], + }, + ], + }, + { + name: 'testTypeName', + label: 'Description', + value: '', + disabled: true, + + type: FormNodeTypes.CONTROL, + }, + { + name: 'reasonForAbandoning', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + value: null, + required: true, + }, + { + name: 'additionalCommentsForAbandon', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + value: null, + required: true, + }, + { + name: 'certificateNumber', + label: 'Certificate number', + value: '', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + }, + { + name: 'testTypeStartTimestamp', + type: FormNodeTypes.CONTROL, + value: '', + label: 'Test start date and time', + viewType: FormNodeViewTypes.TIME, + editType: FormNodeEditTypes.DATETIME, + validators: [ + { name: ValidatorNames.Required }, + { name: ValidatorNames.PastDate }, + { name: ValidatorNames.CopyValueToRootControl, args: 'testStartTimestamp' }, + ], + }, + { + name: 'testTypeEndTimestamp', + type: FormNodeTypes.CONTROL, + value: '', + label: 'Test end date and time', + viewType: FormNodeViewTypes.TIME, + editType: FormNodeEditTypes.DATETIME, + validators: [ + { name: ValidatorNames.Required }, + { name: ValidatorNames.PastDate }, + { name: ValidatorNames.AheadOfDate, args: 'testTypeStartTimestamp' }, + { name: ValidatorNames.CopyValueToRootControl, args: 'testEndTimestamp' }, + ], + }, + { + name: 'prohibitionIssued', + type: FormNodeTypes.CONTROL, + label: 'Prohibition issued', + value: null, + editType: FormNodeEditTypes.RADIO, + options: [ + { value: true, label: 'Yes' }, + { value: false, label: 'No' }, + ], + validators: [{ name: ValidatorNames.Required }], + }, + ], + }, + ], + }, + ], +}; diff --git a/src/app/forms/templates/test-records/section-templates/test/contingency/contingency-test-section-group9And10.template.ts b/src/app/forms/templates/test-records/section-templates/test/contingency/contingency-test-section-group9And10.template.ts index c99c02e8af..481994a6b4 100644 --- a/src/app/forms/templates/test-records/section-templates/test/contingency/contingency-test-section-group9And10.template.ts +++ b/src/app/forms/templates/test-records/section-templates/test/contingency/contingency-test-section-group9And10.template.ts @@ -1,124 +1,132 @@ import { ValidatorNames } from '@forms/models/validators.enum'; import { - FormNode, FormNodeEditTypes, FormNodeTypes, FormNodeViewTypes, FormNodeWidth, + FormNode, + FormNodeEditTypes, + FormNodeTypes, + FormNodeViewTypes, + FormNodeWidth, } from '@forms/services/dynamic-form.types'; export const ContingencyTestSectionGroup9And10: FormNode = { - name: 'testSection', - label: 'Test', - type: FormNodeTypes.GROUP, - children: [ - { - name: 'contingencyTestNumber', - label: 'Contingency Test Number', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.NUMERICSTRING, - validators: [{ name: ValidatorNames.MaxLength, args: 8 }, { name: ValidatorNames.MinLength, args: 6 }, { name: ValidatorNames.Required }], - width: FormNodeWidth.L, - }, - { - name: 'testStartTimestamp', - label: 'Test start date', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - validators: [{ name: ValidatorNames.PastDate }], - }, - { - name: 'testEndTimestamp', - type: FormNodeTypes.CONTROL, - label: 'Test end date', - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - validators: [{ name: ValidatorNames.AheadOfDate, args: 'testStartTimestamp' }], - }, - { - name: 'testTypes', - label: 'Test Types', - type: FormNodeTypes.ARRAY, - children: [ - { - name: '0', // it is important here that the name of the node for an ARRAY type should be an index value - type: FormNodeTypes.GROUP, - children: [ - { - name: 'testResult', - label: 'Result', - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - value: null, - type: FormNodeTypes.CONTROL, - }, - { - name: 'reasonForAbandoning', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - value: null, - required: true, - }, - { - name: 'additionalCommentsForAbandon', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - value: null, - required: true, - }, - { - name: 'certificateNumber', - label: 'Certificate number', - value: '', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - }, - { - name: 'testExpiryDate', - label: 'Expiry Date', - disabled: true, - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - }, - { - name: 'testTypeStartTimestamp', - type: FormNodeTypes.CONTROL, - value: '', - label: 'Test start date and time', - viewType: FormNodeViewTypes.TIME, - editType: FormNodeEditTypes.DATETIME, - validators: [ - { name: ValidatorNames.Required }, - { name: ValidatorNames.PastDate }, - { name: ValidatorNames.CopyValueToRootControl, args: 'testStartTimestamp' }, - ], - }, - { - name: 'testTypeEndTimestamp', - type: FormNodeTypes.CONTROL, - value: '', - label: 'Test end date and time', - viewType: FormNodeViewTypes.TIME, - editType: FormNodeEditTypes.DATETIME, - validators: [ - { name: ValidatorNames.Required }, - { name: ValidatorNames.PastDate }, - { name: ValidatorNames.AheadOfDate, args: 'testTypeStartTimestamp' }, - { name: ValidatorNames.CopyValueToRootControl, args: 'testEndTimestamp' }, - ], - }, - { - name: 'prohibitionIssued', - label: 'Prohibition issued', - type: FormNodeTypes.CONTROL, - value: null, - editType: FormNodeEditTypes.HIDDEN, - required: true, - }, - ], - }, - ], - }, - ], + name: 'testSection', + label: 'Test', + type: FormNodeTypes.GROUP, + children: [ + { + name: 'contingencyTestNumber', + label: 'Contingency Test Number', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.NUMERICSTRING, + validators: [ + { name: ValidatorNames.MaxLength, args: 8 }, + { name: ValidatorNames.MinLength, args: 6 }, + { name: ValidatorNames.Required }, + ], + width: FormNodeWidth.L, + }, + { + name: 'testStartTimestamp', + label: 'Test start date', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + validators: [{ name: ValidatorNames.PastDate }], + }, + { + name: 'testEndTimestamp', + type: FormNodeTypes.CONTROL, + label: 'Test end date', + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + validators: [{ name: ValidatorNames.AheadOfDate, args: 'testStartTimestamp' }], + }, + { + name: 'testTypes', + label: 'Test Types', + type: FormNodeTypes.ARRAY, + children: [ + { + name: '0', // it is important here that the name of the node for an ARRAY type should be an index value + type: FormNodeTypes.GROUP, + children: [ + { + name: 'testResult', + label: 'Result', + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + value: null, + type: FormNodeTypes.CONTROL, + }, + { + name: 'reasonForAbandoning', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + value: null, + required: true, + }, + { + name: 'additionalCommentsForAbandon', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + value: null, + required: true, + }, + { + name: 'certificateNumber', + label: 'Certificate number', + value: '', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + }, + { + name: 'testExpiryDate', + label: 'Expiry Date', + disabled: true, + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + }, + { + name: 'testTypeStartTimestamp', + type: FormNodeTypes.CONTROL, + value: '', + label: 'Test start date and time', + viewType: FormNodeViewTypes.TIME, + editType: FormNodeEditTypes.DATETIME, + validators: [ + { name: ValidatorNames.Required }, + { name: ValidatorNames.PastDate }, + { name: ValidatorNames.CopyValueToRootControl, args: 'testStartTimestamp' }, + ], + }, + { + name: 'testTypeEndTimestamp', + type: FormNodeTypes.CONTROL, + value: '', + label: 'Test end date and time', + viewType: FormNodeViewTypes.TIME, + editType: FormNodeEditTypes.DATETIME, + validators: [ + { name: ValidatorNames.Required }, + { name: ValidatorNames.PastDate }, + { name: ValidatorNames.AheadOfDate, args: 'testTypeStartTimestamp' }, + { name: ValidatorNames.CopyValueToRootControl, args: 'testEndTimestamp' }, + ], + }, + { + name: 'prohibitionIssued', + label: 'Prohibition issued', + type: FormNodeTypes.CONTROL, + value: null, + editType: FormNodeEditTypes.HIDDEN, + required: true, + }, + ], + }, + ], + }, + ], }; diff --git a/src/app/forms/templates/test-records/section-templates/test/contingency/contingency-test-section-group9And10CentralDocs.template.ts b/src/app/forms/templates/test-records/section-templates/test/contingency/contingency-test-section-group9And10CentralDocs.template.ts new file mode 100644 index 0000000000..ef825e6eb7 --- /dev/null +++ b/src/app/forms/templates/test-records/section-templates/test/contingency/contingency-test-section-group9And10CentralDocs.template.ts @@ -0,0 +1,165 @@ +import { ValidatorNames } from '@forms/models/validators.enum'; +import { + FormNode, + FormNodeEditTypes, + FormNodeTypes, + FormNodeViewTypes, + FormNodeWidth, +} from '@forms/services/dynamic-form.types'; + +export const ContingencyTestSectionGroup9And10CentralDocs: FormNode = { + name: 'testSection', + label: 'Test', + type: FormNodeTypes.GROUP, + children: [ + { + name: 'contingencyTestNumber', + label: 'Contingency Test Number', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.NUMERICSTRING, + validators: [ + { name: ValidatorNames.MaxLength, args: 8 }, + { name: ValidatorNames.MinLength, args: 6 }, + { name: ValidatorNames.Required }, + ], + width: FormNodeWidth.L, + }, + { + name: 'testStartTimestamp', + label: 'Test start date', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + validators: [{ name: ValidatorNames.PastDate }], + }, + { + name: 'testEndTimestamp', + type: FormNodeTypes.CONTROL, + label: 'Test end date', + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + validators: [{ name: ValidatorNames.AheadOfDate, args: 'testStartTimestamp' }], + }, + { + name: 'testTypes', + label: 'Test Types', + type: FormNodeTypes.ARRAY, + children: [ + { + name: '0', // it is important here that the name of the node for an ARRAY type should be an index value + type: FormNodeTypes.GROUP, + children: [ + { + name: 'testResult', + label: 'Result', + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + value: null, + type: FormNodeTypes.CONTROL, + validators: [ + { name: ValidatorNames.HideIfNotEqual, args: { sibling: 'centralDocs', value: ['pass', 'prs'] } }, + ], + }, + { + name: 'centralDocs', + type: FormNodeTypes.GROUP, + children: [ + { + name: 'issueRequired', + type: FormNodeTypes.CONTROL, + label: 'Issue documents centrally', + editType: FormNodeEditTypes.RADIO, + value: false, + options: [ + { value: true, label: 'Yes' }, + { value: false, label: 'No' }, + ], + validators: [ + { + name: ValidatorNames.HideIfParentSiblingEqual, + args: { sibling: 'certificateNumber', value: true }, + }, + ], + }, + { + name: 'reasonsForIssue', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + value: [], + }, + ], + }, + { + name: 'reasonForAbandoning', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + value: null, + required: true, + }, + { + name: 'additionalCommentsForAbandon', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + value: null, + required: true, + }, + { + name: 'certificateNumber', + label: 'Certificate number', + value: '', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + }, + { + name: 'testExpiryDate', + label: 'Expiry Date', + disabled: true, + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + }, + { + name: 'testTypeStartTimestamp', + type: FormNodeTypes.CONTROL, + value: '', + label: 'Test start date and time', + viewType: FormNodeViewTypes.TIME, + editType: FormNodeEditTypes.DATETIME, + validators: [ + { name: ValidatorNames.Required }, + { name: ValidatorNames.PastDate }, + { name: ValidatorNames.CopyValueToRootControl, args: 'testStartTimestamp' }, + ], + }, + { + name: 'testTypeEndTimestamp', + type: FormNodeTypes.CONTROL, + value: '', + label: 'Test end date and time', + viewType: FormNodeViewTypes.TIME, + editType: FormNodeEditTypes.DATETIME, + validators: [ + { name: ValidatorNames.Required }, + { name: ValidatorNames.PastDate }, + { name: ValidatorNames.AheadOfDate, args: 'testTypeStartTimestamp' }, + { name: ValidatorNames.CopyValueToRootControl, args: 'testEndTimestamp' }, + ], + }, + { + name: 'prohibitionIssued', + label: 'Prohibition issued', + type: FormNodeTypes.CONTROL, + value: null, + editType: FormNodeEditTypes.HIDDEN, + required: true, + }, + ], + }, + ], + }, + ], +}; diff --git a/src/app/forms/templates/test-records/section-templates/test/contingency/contingency-test-section-specialist-group1.template.ts b/src/app/forms/templates/test-records/section-templates/test/contingency/contingency-test-section-specialist-group1.template.ts index 662d875db6..c057b08719 100644 --- a/src/app/forms/templates/test-records/section-templates/test/contingency/contingency-test-section-specialist-group1.template.ts +++ b/src/app/forms/templates/test-records/section-templates/test/contingency/contingency-test-section-specialist-group1.template.ts @@ -2,158 +2,211 @@ import { AsyncValidatorNames } from '@forms/models/async-validators.enum'; import { TEST_TYPES_GROUP1_SPEC_TEST } from '@forms/models/testTypeId.enum'; import { ValidatorNames } from '@forms/models/validators.enum'; import { - FormNode, FormNodeEditTypes, FormNodeTypes, FormNodeViewTypes, FormNodeWidth, + FormNode, + FormNodeEditTypes, + FormNodeTypes, + FormNodeViewTypes, + FormNodeWidth, } from '@forms/services/dynamic-form.types'; export const ContingencyTestSectionSpecialistGroup1: FormNode = { - name: 'testSection', - label: 'Test', - type: FormNodeTypes.GROUP, - children: [ - { - name: 'contingencyTestNumber', - label: 'Contingency Test Number', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.NUMERICSTRING, - validators: [{ name: ValidatorNames.MaxLength, args: 8 }, { name: ValidatorNames.MinLength, args: 6 }, { name: ValidatorNames.Required }], - width: FormNodeWidth.L, - }, - { - name: 'testStartTimestamp', - label: 'Test start date', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - validators: [{ name: ValidatorNames.PastDate }], - }, - { - name: 'testEndTimestamp', - type: FormNodeTypes.CONTROL, - label: 'Test end date', - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - validators: [{ name: ValidatorNames.AheadOfDate, args: 'testStartTimestamp' }], - }, - { - name: 'testTypes', - label: 'Test Types', - type: FormNodeTypes.ARRAY, - children: [ - { - name: '0', // it is important here that the name of the node for an ARRAY type should be an index value - type: FormNodeTypes.GROUP, - children: [ - { - name: 'testResult', - label: 'Result', - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.RADIO, - options: [ - { value: 'pass', label: 'Pass' }, - { value: 'fail', label: 'Fail' }, - { value: 'prs', label: 'PRS' }, - ], - asyncValidators: [ - { name: AsyncValidatorNames.ResultDependantOnRequiredStandards }, - { - name: AsyncValidatorNames.HideIfEqualsWithCondition, - args: { - sibling: 'certificateNumber', - value: 'fail', - conditions: { field: 'testTypeId', operator: 'equals', value: TEST_TYPES_GROUP1_SPEC_TEST }, - }, - }, - ], - type: FormNodeTypes.CONTROL, - }, - { - name: 'testTypeName', - label: 'Description', - value: '', - disabled: true, + name: 'testSection', + label: 'Test', + type: FormNodeTypes.GROUP, + children: [ + { + name: 'contingencyTestNumber', + label: 'Contingency Test Number', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.NUMERICSTRING, + validators: [ + { name: ValidatorNames.MaxLength, args: 8 }, + { name: ValidatorNames.MinLength, args: 6 }, + { name: ValidatorNames.Required }, + ], + width: FormNodeWidth.L, + }, + { + name: 'testStartTimestamp', + label: 'Test start date', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + validators: [{ name: ValidatorNames.PastDate }], + }, + { + name: 'testEndTimestamp', + type: FormNodeTypes.CONTROL, + label: 'Test end date', + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + validators: [{ name: ValidatorNames.AheadOfDate, args: 'testStartTimestamp' }], + }, + { + name: 'testTypes', + label: 'Test Types', + type: FormNodeTypes.ARRAY, + children: [ + { + name: '0', // it is important here that the name of the node for an ARRAY type should be an index value + type: FormNodeTypes.GROUP, + children: [ + { + name: 'testResult', + label: 'Result', + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.RADIO, + options: [ + { value: 'pass', label: 'Pass' }, + { value: 'fail', label: 'Fail' }, + { value: 'prs', label: 'PRS' }, + ], + validators: [ + { name: ValidatorNames.HideIfNotEqual, args: { sibling: 'centralDocs', value: ['pass', 'prs'] } }, + ], + asyncValidators: [ + { name: AsyncValidatorNames.ResultDependantOnRequiredStandards }, + { + name: AsyncValidatorNames.HideIfEqualsWithCondition, + args: { + sibling: 'certificateNumber', + value: 'fail', + conditions: { field: 'testTypeId', operator: 'equals', value: TEST_TYPES_GROUP1_SPEC_TEST }, + }, + }, + ], + type: FormNodeTypes.CONTROL, + }, + { + name: 'centralDocs', + type: FormNodeTypes.GROUP, + children: [ + { + name: 'issueRequired', + type: FormNodeTypes.CONTROL, + label: 'Issue documents centrally', + editType: FormNodeEditTypes.RADIO, + value: false, + options: [ + { value: true, label: 'Yes' }, + { value: false, label: 'No' }, + ], + validators: [ + { + name: ValidatorNames.HideIfParentSiblingEqual, + args: { sibling: 'certificateNumber', value: true }, + }, + ], + }, + { + name: 'reasonsForIssue', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + value: [], + }, + ], + }, + { + name: 'testTypeName', + label: 'Description', + value: '', + disabled: true, - type: FormNodeTypes.CONTROL, - }, - { - name: 'reasonForAbandoning', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - value: null, - required: true, - }, - { - name: 'additionalCommentsForAbandon', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - value: null, - required: true, - }, - { - name: 'certificateNumber', - label: 'Certificate number', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.STRING, - editType: FormNodeEditTypes.TEXT, - validators: [ - { name: ValidatorNames.Alphanumeric }, - { - name: ValidatorNames.RequiredIfEquals, - args: { - sibling: 'testResult', - value: [ - 'pass', - 'prs', - ], - }, - }, - ], - required: true, - value: null, - }, - { - name: 'testTypeStartTimestamp', - type: FormNodeTypes.CONTROL, - value: '', - label: 'Test start date and time', - viewType: FormNodeViewTypes.TIME, - editType: FormNodeEditTypes.DATETIME, - validators: [ - { name: ValidatorNames.Required }, - { name: ValidatorNames.PastDate }, - { name: ValidatorNames.CopyValueToRootControl, args: 'testStartTimestamp' }, - ], - }, - { - name: 'testTypeEndTimestamp', - type: FormNodeTypes.CONTROL, - value: '', - label: 'Test end date and time', - viewType: FormNodeViewTypes.TIME, - editType: FormNodeEditTypes.DATETIME, - validators: [ - { name: ValidatorNames.Required }, - { name: ValidatorNames.PastDate }, - { name: ValidatorNames.AheadOfDate, args: 'testTypeStartTimestamp' }, - { name: ValidatorNames.CopyValueToRootControl, args: 'testEndTimestamp' }, - ], - }, - { - name: 'prohibitionIssued', - type: FormNodeTypes.CONTROL, - label: 'Prohibition issued', - value: null, - editType: FormNodeEditTypes.RADIO, - options: [ - { value: true, label: 'Yes' }, - { value: false, label: 'No' }, - ], - validators: [{ name: ValidatorNames.Required }], - }, - ], - }, - ], - }, - ], + type: FormNodeTypes.CONTROL, + }, + { + name: 'reasonForAbandoning', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + value: null, + required: true, + }, + { + name: 'additionalCommentsForAbandon', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + value: null, + required: true, + }, + { + name: 'certificateNumber', + label: 'Certificate number', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.STRING, + editType: FormNodeEditTypes.TEXT, + validators: [ + { name: ValidatorNames.Alphanumeric }, + // Make required if test result is pass/prs, but issue documents centrally is false + { name: ValidatorNames.IssueRequired }, + ], + required: true, + value: null, + }, + { + name: 'testTypeStartTimestamp', + type: FormNodeTypes.CONTROL, + value: '', + label: 'Test start date and time', + viewType: FormNodeViewTypes.TIME, + editType: FormNodeEditTypes.DATETIME, + validators: [ + { name: ValidatorNames.Required }, + { name: ValidatorNames.PastDate }, + { name: ValidatorNames.CopyValueToRootControl, args: 'testStartTimestamp' }, + ], + }, + { + name: 'testTypeEndTimestamp', + type: FormNodeTypes.CONTROL, + value: '', + label: 'Test end date and time', + viewType: FormNodeViewTypes.TIME, + editType: FormNodeEditTypes.DATETIME, + validators: [ + { name: ValidatorNames.Required }, + { name: ValidatorNames.PastDate }, + { name: ValidatorNames.AheadOfDate, args: 'testTypeStartTimestamp' }, + { name: ValidatorNames.CopyValueToRootControl, args: 'testEndTimestamp' }, + ], + }, + { + name: 'prohibitionIssued', + type: FormNodeTypes.CONTROL, + label: 'Prohibition issued', + value: null, + editType: FormNodeEditTypes.RADIO, + options: [ + { value: true, label: 'Yes' }, + { value: false, label: 'No' }, + ], + validators: [{ name: ValidatorNames.Required }], + }, + { + name: 'reapplicationDate', + label: 'Reapplication date', + hint: 'For example, 27 3 2007', + editType: FormNodeEditTypes.DATE, + viewType: FormNodeViewTypes.DATE, + type: FormNodeTypes.CONTROL, + groups: ['failOnly'], + validators: [ + { + name: ValidatorNames.RequiredIfEquals, + args: { + sibling: 'testResult', + value: ['fail'], + customErrorMessage: 'Reapplication date is required', + }, + }, + { name: ValidatorNames.FutureDate }, + ], + }, + ], + }, + ], + }, + ], }; diff --git a/src/app/forms/templates/test-records/section-templates/test/contingency/contingency-test-section-specialist-group2.template.ts b/src/app/forms/templates/test-records/section-templates/test/contingency/contingency-test-section-specialist-group2.template.ts index d1b26533fc..c170f94622 100644 --- a/src/app/forms/templates/test-records/section-templates/test/contingency/contingency-test-section-specialist-group2.template.ts +++ b/src/app/forms/templates/test-records/section-templates/test/contingency/contingency-test-section-specialist-group2.template.ts @@ -1,152 +1,164 @@ import { AsyncValidatorNames } from '@forms/models/async-validators.enum'; import { ValidatorNames } from '@forms/models/validators.enum'; import { - FormNode, FormNodeEditTypes, FormNodeTypes, FormNodeViewTypes, FormNodeWidth, + FormNode, + FormNodeEditTypes, + FormNodeTypes, + FormNodeViewTypes, + FormNodeWidth, } from '@forms/services/dynamic-form.types'; export const ContingencyTestSectionSpecialistGroup2: FormNode = { - name: 'testSection', - label: 'Test', - type: FormNodeTypes.GROUP, - children: [ - { - name: 'contingencyTestNumber', - label: 'Contingency Test Number', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.NUMERICSTRING, - validators: [{ name: ValidatorNames.MaxLength, args: 8 }, { name: ValidatorNames.MinLength, args: 6 }, { name: ValidatorNames.Required }], - width: FormNodeWidth.L, - }, - { - name: 'testStartTimestamp', - label: 'Test start date', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - validators: [{ name: ValidatorNames.PastDate }], - }, - { - name: 'testEndTimestamp', - type: FormNodeTypes.CONTROL, - label: 'Test end date', - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - validators: [{ name: ValidatorNames.AheadOfDate, args: 'testStartTimestamp' }], - }, - { - name: 'testTypes', - label: 'Test Types', - type: FormNodeTypes.ARRAY, - children: [ - { - name: '0', // it is important here that the name of the node for an ARRAY type should be an index value - type: FormNodeTypes.GROUP, - children: [ - { - name: 'testResult', - label: 'Result', - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.RADIO, - options: [ - { value: 'pass', label: 'Pass' }, - { value: 'fail', label: 'Fail' }, - { value: 'prs', label: 'PRS' }, - ], - validators: [{ name: ValidatorNames.HideIfNotEqual, args: { sibling: 'secondaryCertificateNumber', value: 'pass' } }], - asyncValidators: [{ name: AsyncValidatorNames.ResultDependantOnCustomDefects }], - type: FormNodeTypes.CONTROL, - }, - { - name: 'testTypeName', - label: 'Description', - value: '', - disabled: true, - - type: FormNodeTypes.CONTROL, - }, - { - name: 'reasonForAbandoning', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - value: null, - required: true, - }, - { - name: 'additionalCommentsForAbandon', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - value: null, - required: true, - }, - { - name: 'certificateNumber', - label: 'Certificate number', - value: '', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - }, - { - name: 'secondaryCertificateNumber', - label: 'Secondary Certificate number', - value: '', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.STRING, - editType: FormNodeEditTypes.TEXT, - hint: 'COIF Certificate number', - validators: [ - { - name: ValidatorNames.RequiredIfEquals, - args: { sibling: 'testResult', value: ['pass'] }, - }, - { name: ValidatorNames.MaxLength, args: 20 }, - { name: ValidatorNames.Alphanumeric }, - ], - }, - { - name: 'testTypeStartTimestamp', - type: FormNodeTypes.CONTROL, - value: '', - label: 'Test start date and time', - viewType: FormNodeViewTypes.TIME, - editType: FormNodeEditTypes.DATETIME, - validators: [ - { name: ValidatorNames.Required }, - { name: ValidatorNames.PastDate }, - { name: ValidatorNames.CopyValueToRootControl, args: 'testStartTimestamp' }, - ], - }, - { - name: 'testTypeEndTimestamp', - type: FormNodeTypes.CONTROL, - value: '', - label: 'Test end date and time', - viewType: FormNodeViewTypes.TIME, - editType: FormNodeEditTypes.DATETIME, - validators: [ - { name: ValidatorNames.Required }, - { name: ValidatorNames.PastDate }, - { name: ValidatorNames.AheadOfDate, args: 'testTypeStartTimestamp' }, - { name: ValidatorNames.CopyValueToRootControl, args: 'testEndTimestamp' }, - ], - }, - { - name: 'prohibitionIssued', - type: FormNodeTypes.CONTROL, - label: 'Prohibition issued', - value: null, - editType: FormNodeEditTypes.RADIO, - options: [ - { value: true, label: 'Yes' }, - { value: false, label: 'No' }, - ], - validators: [{ name: ValidatorNames.Required }], - }, - ], - }, - ], - }, - ], + name: 'testSection', + label: 'Test', + type: FormNodeTypes.GROUP, + children: [ + { + name: 'contingencyTestNumber', + label: 'Contingency Test Number', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.NUMERICSTRING, + validators: [ + { name: ValidatorNames.MaxLength, args: 8 }, + { name: ValidatorNames.MinLength, args: 6 }, + { name: ValidatorNames.Required }, + ], + width: FormNodeWidth.L, + }, + { + name: 'testStartTimestamp', + label: 'Test start date', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + validators: [{ name: ValidatorNames.PastDate }], + }, + { + name: 'testEndTimestamp', + type: FormNodeTypes.CONTROL, + label: 'Test end date', + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + validators: [{ name: ValidatorNames.AheadOfDate, args: 'testStartTimestamp' }], + }, + { + name: 'testTypes', + label: 'Test Types', + type: FormNodeTypes.ARRAY, + children: [ + { + name: '0', // it is important here that the name of the node for an ARRAY type should be an index value + type: FormNodeTypes.GROUP, + children: [ + { + name: 'testResult', + label: 'Result', + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.RADIO, + options: [ + { value: 'pass', label: 'Pass' }, + { value: 'fail', label: 'Fail' }, + { value: 'prs', label: 'PRS' }, + ], + validators: [ + { + name: ValidatorNames.HideIfNotEqual, + args: { sibling: 'secondaryCertificateNumber', value: 'pass' }, + }, + ], + asyncValidators: [{ name: AsyncValidatorNames.ResultDependantOnCustomDefects }], + type: FormNodeTypes.CONTROL, + }, + { + name: 'testTypeName', + label: 'Description', + value: '', + disabled: true, + type: FormNodeTypes.CONTROL, + }, + { + name: 'reasonForAbandoning', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + value: null, + required: true, + }, + { + name: 'additionalCommentsForAbandon', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + value: null, + required: true, + }, + { + name: 'certificateNumber', + label: 'Certificate number', + value: '', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + }, + { + name: 'secondaryCertificateNumber', + label: 'Secondary Certificate number', + value: '', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.STRING, + editType: FormNodeEditTypes.TEXT, + hint: 'COIF Certificate number', + validators: [ + { + name: ValidatorNames.RequiredIfEquals, + args: { sibling: 'testResult', value: ['pass'] }, + }, + { name: ValidatorNames.MaxLength, args: 20 }, + { name: ValidatorNames.Alphanumeric }, + ], + }, + { + name: 'testTypeStartTimestamp', + type: FormNodeTypes.CONTROL, + value: '', + label: 'Test start date and time', + viewType: FormNodeViewTypes.TIME, + editType: FormNodeEditTypes.DATETIME, + validators: [ + { name: ValidatorNames.Required }, + { name: ValidatorNames.PastDate }, + { name: ValidatorNames.CopyValueToRootControl, args: 'testStartTimestamp' }, + ], + }, + { + name: 'testTypeEndTimestamp', + type: FormNodeTypes.CONTROL, + value: '', + label: 'Test end date and time', + viewType: FormNodeViewTypes.TIME, + editType: FormNodeEditTypes.DATETIME, + validators: [ + { name: ValidatorNames.Required }, + { name: ValidatorNames.PastDate }, + { name: ValidatorNames.AheadOfDate, args: 'testTypeStartTimestamp' }, + { name: ValidatorNames.CopyValueToRootControl, args: 'testEndTimestamp' }, + ], + }, + { + name: 'prohibitionIssued', + type: FormNodeTypes.CONTROL, + label: 'Prohibition issued', + value: null, + editType: FormNodeEditTypes.RADIO, + options: [ + { value: true, label: 'Yes' }, + { value: false, label: 'No' }, + ], + validators: [{ name: ValidatorNames.Required }], + }, + ], + }, + ], + }, + ], }; diff --git a/src/app/forms/templates/test-records/section-templates/test/contingency/contingency-test-section-specialist-group3And4.template.ts b/src/app/forms/templates/test-records/section-templates/test/contingency/contingency-test-section-specialist-group3And4.template.ts index 32bbd691ab..6fe6638d68 100644 --- a/src/app/forms/templates/test-records/section-templates/test/contingency/contingency-test-section-specialist-group3And4.template.ts +++ b/src/app/forms/templates/test-records/section-templates/test/contingency/contingency-test-section-specialist-group3And4.template.ts @@ -1,154 +1,165 @@ import { AsyncValidatorNames } from '@forms/models/async-validators.enum'; import { ValidatorNames } from '@forms/models/validators.enum'; import { - FormNode, FormNodeEditTypes, FormNodeTypes, FormNodeViewTypes, FormNodeWidth, + FormNode, + FormNodeEditTypes, + FormNodeTypes, + FormNodeViewTypes, + FormNodeWidth, } from '@forms/services/dynamic-form.types'; export const ContingencyTestSectionSpecialistGroup3And4: FormNode = { - name: 'testSection', - label: 'Test', - type: FormNodeTypes.GROUP, - children: [ - { - name: 'contingencyTestNumber', - label: 'Contingency Test Number', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.NUMERICSTRING, - validators: [{ name: ValidatorNames.MaxLength, args: 8 }, { name: ValidatorNames.MinLength, args: 6 }, { name: ValidatorNames.Required }], - width: FormNodeWidth.L, - }, - { - name: 'testStartTimestamp', - label: 'Test start date', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - validators: [{ name: ValidatorNames.PastDate }], - }, - { - name: 'testEndTimestamp', - label: 'Test end date', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - validators: [{ name: ValidatorNames.AheadOfDate, args: 'testStartTimestamp' }], - }, - { - name: 'testTypes', - label: 'Test Types', - type: FormNodeTypes.ARRAY, - children: [ - { - name: '0', // it is important here that the name of the node for an ARRAY type should be an index value - type: FormNodeTypes.GROUP, - children: [ - { - name: 'testResult', - label: 'Result', - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.RADIO, - options: [ - { value: 'pass', label: 'Pass' }, - { value: 'fail', label: 'Fail' }, - { value: 'prs', label: 'PRS' }, - ], - validators: [ - { name: ValidatorNames.HideIfNotEqual, args: { sibling: 'certificateNumber', value: ['pass'] } }, - { name: ValidatorNames.HideIfNotEqual, args: { sibling: 'secondaryCertificateNumber', value: ['pass'] } }, - ], - asyncValidators: [{ name: AsyncValidatorNames.ResultDependantOnCustomDefects }], - type: FormNodeTypes.CONTROL, - }, - { - name: 'testTypeName', - label: 'Description', - value: '', - disabled: true, - type: FormNodeTypes.CONTROL, - }, - { - name: 'reasonForAbandoning', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - value: null, - required: true, - }, - { - name: 'additionalCommentsForAbandon', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - value: null, - required: true, - }, - { - name: 'certificateNumber', - label: 'Certificate number', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - required: true, - value: null, - }, - { - name: 'secondaryCertificateNumber', - label: 'Secondary certificate number', - value: '', - required: true, - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.TEXT, - validators: [ - { name: ValidatorNames.Alphanumeric }, - { - name: ValidatorNames.RequiredIfEquals, - args: { sibling: 'testResult', value: ['pass'] }, - }, - { name: ValidatorNames.MaxLength, args: 20 }, - ], - }, - { - name: 'testTypeStartTimestamp', - type: FormNodeTypes.CONTROL, - value: '', - label: 'Test start date and time', - viewType: FormNodeViewTypes.TIME, - editType: FormNodeEditTypes.DATETIME, - validators: [ - { name: ValidatorNames.Required }, - { name: ValidatorNames.PastDate }, - { name: ValidatorNames.CopyValueToRootControl, args: 'testStartTimestamp' }, - ], - }, - { - name: 'testTypeEndTimestamp', - type: FormNodeTypes.CONTROL, - value: '', - label: 'Test end date and time', - viewType: FormNodeViewTypes.TIME, - editType: FormNodeEditTypes.DATETIME, - validators: [ - { name: ValidatorNames.Required }, - { name: ValidatorNames.PastDate }, - { name: ValidatorNames.AheadOfDate, args: 'testTypeStartTimestamp' }, - { name: ValidatorNames.CopyValueToRootControl, args: 'testEndTimestamp' }, - ], - }, - { - name: 'prohibitionIssued', - type: FormNodeTypes.CONTROL, - label: 'Prohibition issued', - value: null, - editType: FormNodeEditTypes.RADIO, - options: [ - { value: true, label: 'Yes' }, - { value: false, label: 'No' }, - ], - validators: [{ name: ValidatorNames.Required }], - }, - ], - }, - ], - }, - ], + name: 'testSection', + label: 'Test', + type: FormNodeTypes.GROUP, + children: [ + { + name: 'contingencyTestNumber', + label: 'Contingency Test Number', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.NUMERICSTRING, + validators: [ + { name: ValidatorNames.MaxLength, args: 8 }, + { name: ValidatorNames.MinLength, args: 6 }, + { name: ValidatorNames.Required }, + ], + width: FormNodeWidth.L, + }, + { + name: 'testStartTimestamp', + label: 'Test start date', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + validators: [{ name: ValidatorNames.PastDate }], + }, + { + name: 'testEndTimestamp', + label: 'Test end date', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + validators: [{ name: ValidatorNames.AheadOfDate, args: 'testStartTimestamp' }], + }, + { + name: 'testTypes', + label: 'Test Types', + type: FormNodeTypes.ARRAY, + children: [ + { + name: '0', // it is important here that the name of the node for an ARRAY type should be an index value + type: FormNodeTypes.GROUP, + children: [ + { + name: 'testResult', + label: 'Result', + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.RADIO, + options: [ + { value: 'pass', label: 'Pass' }, + { value: 'fail', label: 'Fail' }, + { value: 'prs', label: 'PRS' }, + ], + validators: [ + { name: ValidatorNames.HideIfNotEqual, args: { sibling: 'certificateNumber', value: ['pass'] } }, + { + name: ValidatorNames.HideIfNotEqual, + args: { sibling: 'secondaryCertificateNumber', value: ['pass'] }, + }, + ], + asyncValidators: [{ name: AsyncValidatorNames.ResultDependantOnCustomDefects }], + type: FormNodeTypes.CONTROL, + }, + { + name: 'testTypeName', + label: 'Description', + value: '', + disabled: true, + type: FormNodeTypes.CONTROL, + }, + { + name: 'reasonForAbandoning', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + value: null, + required: true, + }, + { + name: 'additionalCommentsForAbandon', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + value: null, + required: true, + }, + { + name: 'certificateNumber', + label: 'Certificate number', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + required: true, + value: null, + }, + { + name: 'secondaryCertificateNumber', + label: 'Secondary certificate number', + value: '', + required: true, + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.TEXT, + validators: [ + { name: ValidatorNames.Alphanumeric }, + { + name: ValidatorNames.RequiredIfEquals, + args: { sibling: 'testResult', value: ['pass'] }, + }, + { name: ValidatorNames.MaxLength, args: 20 }, + ], + }, + { + name: 'testTypeStartTimestamp', + type: FormNodeTypes.CONTROL, + value: '', + label: 'Test start date and time', + viewType: FormNodeViewTypes.TIME, + editType: FormNodeEditTypes.DATETIME, + validators: [ + { name: ValidatorNames.Required }, + { name: ValidatorNames.PastDate }, + { name: ValidatorNames.CopyValueToRootControl, args: 'testStartTimestamp' }, + ], + }, + { + name: 'testTypeEndTimestamp', + type: FormNodeTypes.CONTROL, + value: '', + label: 'Test end date and time', + viewType: FormNodeViewTypes.TIME, + editType: FormNodeEditTypes.DATETIME, + validators: [ + { name: ValidatorNames.Required }, + { name: ValidatorNames.PastDate }, + { name: ValidatorNames.AheadOfDate, args: 'testTypeStartTimestamp' }, + { name: ValidatorNames.CopyValueToRootControl, args: 'testEndTimestamp' }, + ], + }, + { + name: 'prohibitionIssued', + type: FormNodeTypes.CONTROL, + label: 'Prohibition issued', + value: null, + editType: FormNodeEditTypes.RADIO, + options: [ + { value: true, label: 'Yes' }, + { value: false, label: 'No' }, + ], + validators: [{ name: ValidatorNames.Required }], + }, + ], + }, + ], + }, + ], }; diff --git a/src/app/forms/templates/test-records/section-templates/test/contingency/contingency-test-section-specialist-group5.template.ts b/src/app/forms/templates/test-records/section-templates/test/contingency/contingency-test-section-specialist-group5.template.ts index 186a335360..f88e62215e 100644 --- a/src/app/forms/templates/test-records/section-templates/test/contingency/contingency-test-section-specialist-group5.template.ts +++ b/src/app/forms/templates/test-records/section-templates/test/contingency/contingency-test-section-specialist-group5.template.ts @@ -2,158 +2,179 @@ import { AsyncValidatorNames } from '@forms/models/async-validators.enum'; import { TEST_TYPES_GROUP5_SPEC_TEST } from '@forms/models/testTypeId.enum'; import { ValidatorNames } from '@forms/models/validators.enum'; import { - FormNode, FormNodeEditTypes, FormNodeTypes, FormNodeViewTypes, FormNodeWidth, + FormNode, + FormNodeEditTypes, + FormNodeTypes, + FormNodeViewTypes, + FormNodeWidth, } from '@forms/services/dynamic-form.types'; export const ContingencyTestSectionSpecialistGroup5: FormNode = { - name: 'testSection', - label: 'Test', - type: FormNodeTypes.GROUP, - children: [ - { - name: 'contingencyTestNumber', - label: 'Contingency Test Number', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.NUMERICSTRING, - validators: [{ name: ValidatorNames.MaxLength, args: 8 }, { name: ValidatorNames.MinLength, args: 6 }, { name: ValidatorNames.Required }], - width: FormNodeWidth.L, - }, - { - name: 'testStartTimestamp', - label: 'Test start date', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - validators: [{ name: ValidatorNames.PastDate }], - }, - { - name: 'testEndTimestamp', - type: FormNodeTypes.CONTROL, - label: 'Test end date', - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - validators: [{ name: ValidatorNames.AheadOfDate, args: 'testStartTimestamp' }], - }, - { - name: 'testTypes', - label: 'Test Types', - type: FormNodeTypes.ARRAY, - children: [ - { - name: '0', // it is important here that the name of the node for an ARRAY type should be an index value - type: FormNodeTypes.GROUP, - children: [ - { - name: 'testResult', - label: 'Result', - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.RADIO, - options: [ - { value: 'pass', label: 'Pass' }, - { value: 'fail', label: 'Fail' }, - { value: 'prs', label: 'PRS' }, - ], - asyncValidators: [ - { name: AsyncValidatorNames.ResultDependantOnRequiredStandards }, - { - name: AsyncValidatorNames.HideIfEqualsWithCondition, - args: { - sibling: 'certificateNumber', - value: 'fail', - conditions: { field: 'testTypeId', operator: 'equals', value: TEST_TYPES_GROUP5_SPEC_TEST }, - }, - }, - ], - type: FormNodeTypes.CONTROL, - }, - { - name: 'testTypeName', - label: 'Description', - value: '', - disabled: true, + name: 'testSection', + label: 'Test', + type: FormNodeTypes.GROUP, + children: [ + { + name: 'contingencyTestNumber', + label: 'Contingency Test Number', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.NUMERICSTRING, + validators: [ + { name: ValidatorNames.MaxLength, args: 8 }, + { name: ValidatorNames.MinLength, args: 6 }, + { name: ValidatorNames.Required }, + ], + width: FormNodeWidth.L, + }, + { + name: 'testStartTimestamp', + label: 'Test start date', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + validators: [{ name: ValidatorNames.PastDate }], + }, + { + name: 'testEndTimestamp', + type: FormNodeTypes.CONTROL, + label: 'Test end date', + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + validators: [{ name: ValidatorNames.AheadOfDate, args: 'testStartTimestamp' }], + }, + { + name: 'testTypes', + label: 'Test Types', + type: FormNodeTypes.ARRAY, + children: [ + { + name: '0', // it is important here that the name of the node for an ARRAY type should be an index value + type: FormNodeTypes.GROUP, + children: [ + { + name: 'testResult', + label: 'Result', + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.RADIO, + options: [ + { value: 'pass', label: 'Pass' }, + { value: 'fail', label: 'Fail' }, + { value: 'prs', label: 'PRS' }, + ], + validators: [ + { + name: ValidatorNames.ShowGroupsWhenIncludes, + args: { + values: ['fail'], + groups: ['failOnly'], + }, + }, + { + name: ValidatorNames.HideGroupsWhenExcludes, + args: { + values: ['fail'], + groups: ['failOnly'], + }, + }, + ], + asyncValidators: [ + { name: AsyncValidatorNames.ResultDependantOnRequiredStandards }, + { + name: AsyncValidatorNames.HideIfEqualsWithCondition, + args: { + sibling: 'certificateNumber', + value: 'fail', + conditions: { field: 'testTypeId', operator: 'equals', value: TEST_TYPES_GROUP5_SPEC_TEST }, + }, + }, + ], + type: FormNodeTypes.CONTROL, + }, + { + name: 'testTypeName', + label: 'Description', + value: '', + disabled: true, - type: FormNodeTypes.CONTROL, - }, - { - name: 'reasonForAbandoning', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - value: null, - required: true, - }, - { - name: 'additionalCommentsForAbandon', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - value: null, - required: true, - }, - { - name: 'certificateNumber', - label: 'Certificate number', - value: null, - type: FormNodeTypes.CONTROL, - required: true, - viewType: FormNodeViewTypes.STRING, - editType: FormNodeEditTypes.TEXT, - validators: [ - { name: ValidatorNames.Alphanumeric }, - { - name: ValidatorNames.RequiredIfEquals, - args: { - sibling: 'testResult', - value: [ - 'pass', - 'prs', - ], - }, - }, - ], - }, - { - name: 'testTypeStartTimestamp', - type: FormNodeTypes.CONTROL, - value: '', - label: 'Test start date and time', - viewType: FormNodeViewTypes.TIME, - editType: FormNodeEditTypes.DATETIME, - validators: [ - { name: ValidatorNames.Required }, - { name: ValidatorNames.PastDate }, - { name: ValidatorNames.CopyValueToRootControl, args: 'testStartTimestamp' }, - ], - }, - { - name: 'testTypeEndTimestamp', - type: FormNodeTypes.CONTROL, - value: '', - label: 'Test end date and time', - viewType: FormNodeViewTypes.TIME, - editType: FormNodeEditTypes.DATETIME, - validators: [ - { name: ValidatorNames.Required }, - { name: ValidatorNames.PastDate }, - { name: ValidatorNames.AheadOfDate, args: 'testTypeStartTimestamp' }, - { name: ValidatorNames.CopyValueToRootControl, args: 'testEndTimestamp' }, - ], - }, - { - name: 'prohibitionIssued', - type: FormNodeTypes.CONTROL, - label: 'Prohibition issued', - value: null, - editType: FormNodeEditTypes.RADIO, - options: [ - { value: true, label: 'Yes' }, - { value: false, label: 'No' }, - ], - validators: [{ name: ValidatorNames.Required }], - }, - ], - }, - ], - }, - ], + type: FormNodeTypes.CONTROL, + }, + { + name: 'reasonForAbandoning', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + value: null, + required: true, + }, + { + name: 'additionalCommentsForAbandon', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + value: null, + required: true, + }, + { + name: 'certificateNumber', + label: 'Certificate number', + value: null, + type: FormNodeTypes.CONTROL, + required: true, + viewType: FormNodeViewTypes.STRING, + editType: FormNodeEditTypes.TEXT, + validators: [ + { name: ValidatorNames.Alphanumeric }, + { + name: ValidatorNames.RequiredIfEquals, + args: { + sibling: 'testResult', + value: ['pass', 'prs'], + }, + }, + ], + }, + { + name: 'testTypeStartTimestamp', + type: FormNodeTypes.CONTROL, + value: '', + label: 'Test start date and time', + viewType: FormNodeViewTypes.TIME, + editType: FormNodeEditTypes.DATETIME, + validators: [ + { name: ValidatorNames.Required }, + { name: ValidatorNames.PastDate }, + { name: ValidatorNames.CopyValueToRootControl, args: 'testStartTimestamp' }, + ], + }, + { + name: 'testTypeEndTimestamp', + type: FormNodeTypes.CONTROL, + value: '', + label: 'Test end date and time', + viewType: FormNodeViewTypes.TIME, + editType: FormNodeEditTypes.DATETIME, + validators: [ + { name: ValidatorNames.Required }, + { name: ValidatorNames.PastDate }, + { name: ValidatorNames.AheadOfDate, args: 'testTypeStartTimestamp' }, + { name: ValidatorNames.CopyValueToRootControl, args: 'testEndTimestamp' }, + ], + }, + { + name: 'prohibitionIssued', + type: FormNodeTypes.CONTROL, + label: 'Prohibition issued', + value: null, + editType: FormNodeEditTypes.RADIO, + options: [ + { value: true, label: 'Yes' }, + { value: false, label: 'No' }, + ], + validators: [{ name: ValidatorNames.Required }], + }, + ], + }, + ], + }, + ], }; diff --git a/src/app/forms/templates/test-records/section-templates/test/contingency/old-contingency-specialist-group1.template.ts b/src/app/forms/templates/test-records/section-templates/test/contingency/old-contingency-specialist-group1.template.ts index ba8416810c..69942f4ea6 100644 --- a/src/app/forms/templates/test-records/section-templates/test/contingency/old-contingency-specialist-group1.template.ts +++ b/src/app/forms/templates/test-records/section-templates/test/contingency/old-contingency-specialist-group1.template.ts @@ -1,146 +1,154 @@ import { AsyncValidatorNames } from '@forms/models/async-validators.enum'; import { ValidatorNames } from '@forms/models/validators.enum'; import { - FormNode, FormNodeEditTypes, FormNodeTypes, FormNodeViewTypes, FormNodeWidth, + FormNode, + FormNodeEditTypes, + FormNodeTypes, + FormNodeViewTypes, + FormNodeWidth, } from '@forms/services/dynamic-form.types'; export const OldIVAContingencyTestSectionSpecialistGroup1: FormNode = { - name: 'testSection', - label: 'Test', - type: FormNodeTypes.GROUP, - children: [ - { - name: 'contingencyTestNumber', - label: 'Contingency Test Number', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.NUMERICSTRING, - validators: [{ name: ValidatorNames.MaxLength, args: 8 }, { name: ValidatorNames.MinLength, args: 6 }, { name: ValidatorNames.Required }], - width: FormNodeWidth.L, - }, - { - name: 'testStartTimestamp', - label: 'Test start date', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - validators: [{ name: ValidatorNames.PastDate }], - }, - { - name: 'testEndTimestamp', - type: FormNodeTypes.CONTROL, - label: 'Test end date', - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - validators: [{ name: ValidatorNames.AheadOfDate, args: 'testStartTimestamp' }], - }, - { - name: 'testTypes', - label: 'Test Types', - type: FormNodeTypes.ARRAY, - children: [ - { - name: '0', // it is important here that the name of the node for an ARRAY type should be an index value - type: FormNodeTypes.GROUP, - children: [ - { - name: 'testResult', - label: 'Result', - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.RADIO, - options: [ - { value: 'pass', label: 'Pass' }, - { value: 'fail', label: 'Fail' }, - { value: 'prs', label: 'PRS' }, - ], - asyncValidators: [ - { name: AsyncValidatorNames.ResultDependantOnCustomDefects }, - { - name: AsyncValidatorNames.HideIfEqualsWithCondition, - args: { - sibling: 'certificateNumber', - value: 'fail', - conditions: { field: 'testTypeId', operator: 'equals', value: ['150', '151', '181', '182'] }, - }, - }, - ], - type: FormNodeTypes.CONTROL, - }, - { - name: 'testTypeName', - label: 'Description', - value: '', - disabled: true, + name: 'testSection', + label: 'Test', + type: FormNodeTypes.GROUP, + children: [ + { + name: 'contingencyTestNumber', + label: 'Contingency Test Number', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.NUMERICSTRING, + validators: [ + { name: ValidatorNames.MaxLength, args: 8 }, + { name: ValidatorNames.MinLength, args: 6 }, + { name: ValidatorNames.Required }, + ], + width: FormNodeWidth.L, + }, + { + name: 'testStartTimestamp', + label: 'Test start date', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + validators: [{ name: ValidatorNames.PastDate }], + }, + { + name: 'testEndTimestamp', + type: FormNodeTypes.CONTROL, + label: 'Test end date', + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + validators: [{ name: ValidatorNames.AheadOfDate, args: 'testStartTimestamp' }], + }, + { + name: 'testTypes', + label: 'Test Types', + type: FormNodeTypes.ARRAY, + children: [ + { + name: '0', // it is important here that the name of the node for an ARRAY type should be an index value + type: FormNodeTypes.GROUP, + children: [ + { + name: 'testResult', + label: 'Result', + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.RADIO, + options: [ + { value: 'pass', label: 'Pass' }, + { value: 'fail', label: 'Fail' }, + { value: 'prs', label: 'PRS' }, + ], + asyncValidators: [ + { name: AsyncValidatorNames.ResultDependantOnCustomDefects }, + { + name: AsyncValidatorNames.HideIfEqualsWithCondition, + args: { + sibling: 'certificateNumber', + value: 'fail', + conditions: { field: 'testTypeId', operator: 'equals', value: ['150', '151', '181', '182'] }, + }, + }, + ], + type: FormNodeTypes.CONTROL, + }, + { + name: 'testTypeName', + label: 'Description', + value: '', + disabled: true, - type: FormNodeTypes.CONTROL, - }, - { - name: 'reasonForAbandoning', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - value: null, - required: true, - }, - { - name: 'additionalCommentsForAbandon', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - value: null, - required: true, - }, - { - name: 'certificateNumber', - label: 'Certificate number', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.STRING, - editType: FormNodeEditTypes.TEXT, - validators: [{ name: ValidatorNames.Alphanumeric }], - required: true, - value: null, - }, - { - name: 'testTypeStartTimestamp', - type: FormNodeTypes.CONTROL, - value: '', - label: 'Test start date and time', - viewType: FormNodeViewTypes.TIME, - editType: FormNodeEditTypes.DATETIME, - validators: [ - { name: ValidatorNames.Required }, - { name: ValidatorNames.PastDate }, - { name: ValidatorNames.CopyValueToRootControl, args: 'testStartTimestamp' }, - ], - }, - { - name: 'testTypeEndTimestamp', - type: FormNodeTypes.CONTROL, - value: '', - label: 'Test end date and time', - viewType: FormNodeViewTypes.TIME, - editType: FormNodeEditTypes.DATETIME, - validators: [ - { name: ValidatorNames.Required }, - { name: ValidatorNames.PastDate }, - { name: ValidatorNames.AheadOfDate, args: 'testTypeStartTimestamp' }, - { name: ValidatorNames.CopyValueToRootControl, args: 'testEndTimestamp' }, - ], - }, - { - name: 'prohibitionIssued', - type: FormNodeTypes.CONTROL, - label: 'Prohibition issued', - value: null, - editType: FormNodeEditTypes.RADIO, - options: [ - { value: true, label: 'Yes' }, - { value: false, label: 'No' }, - ], - validators: [{ name: ValidatorNames.Required }], - }, - ], - }, - ], - }, - ], + type: FormNodeTypes.CONTROL, + }, + { + name: 'reasonForAbandoning', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + value: null, + required: true, + }, + { + name: 'additionalCommentsForAbandon', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + value: null, + required: true, + }, + { + name: 'certificateNumber', + label: 'Certificate number', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.STRING, + editType: FormNodeEditTypes.TEXT, + validators: [{ name: ValidatorNames.Alphanumeric }], + required: true, + value: null, + }, + { + name: 'testTypeStartTimestamp', + type: FormNodeTypes.CONTROL, + value: '', + label: 'Test start date and time', + viewType: FormNodeViewTypes.TIME, + editType: FormNodeEditTypes.DATETIME, + validators: [ + { name: ValidatorNames.Required }, + { name: ValidatorNames.PastDate }, + { name: ValidatorNames.CopyValueToRootControl, args: 'testStartTimestamp' }, + ], + }, + { + name: 'testTypeEndTimestamp', + type: FormNodeTypes.CONTROL, + value: '', + label: 'Test end date and time', + viewType: FormNodeViewTypes.TIME, + editType: FormNodeEditTypes.DATETIME, + validators: [ + { name: ValidatorNames.Required }, + { name: ValidatorNames.PastDate }, + { name: ValidatorNames.AheadOfDate, args: 'testTypeStartTimestamp' }, + { name: ValidatorNames.CopyValueToRootControl, args: 'testEndTimestamp' }, + ], + }, + { + name: 'prohibitionIssued', + type: FormNodeTypes.CONTROL, + label: 'Prohibition issued', + value: null, + editType: FormNodeEditTypes.RADIO, + options: [ + { value: true, label: 'Yes' }, + { value: false, label: 'No' }, + ], + validators: [{ name: ValidatorNames.Required }], + }, + ], + }, + ], + }, + ], }; diff --git a/src/app/forms/templates/test-records/section-templates/test/contingency/old-contingency-specialist-group5.template.ts b/src/app/forms/templates/test-records/section-templates/test/contingency/old-contingency-specialist-group5.template.ts index f950f3affa..dea912e768 100644 --- a/src/app/forms/templates/test-records/section-templates/test/contingency/old-contingency-specialist-group5.template.ts +++ b/src/app/forms/templates/test-records/section-templates/test/contingency/old-contingency-specialist-group5.template.ts @@ -1,134 +1,142 @@ import { AsyncValidatorNames } from '@forms/models/async-validators.enum'; import { ValidatorNames } from '@forms/models/validators.enum'; import { - FormNode, FormNodeEditTypes, FormNodeTypes, FormNodeViewTypes, FormNodeWidth, + FormNode, + FormNodeEditTypes, + FormNodeTypes, + FormNodeViewTypes, + FormNodeWidth, } from '@forms/services/dynamic-form.types'; export const OldIVAContingencyTestSectionSpecialistGroup5: FormNode = { - name: 'testSection', - label: 'Test', - type: FormNodeTypes.GROUP, - children: [ - { - name: 'contingencyTestNumber', - label: 'Contingency Test Number', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.NUMERICSTRING, - validators: [{ name: ValidatorNames.MaxLength, args: 8 }, { name: ValidatorNames.MinLength, args: 6 }, { name: ValidatorNames.Required }], - width: FormNodeWidth.L, - }, - { - name: 'testStartTimestamp', - label: 'Test start date', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - validators: [{ name: ValidatorNames.PastDate }], - }, - { - name: 'testEndTimestamp', - type: FormNodeTypes.CONTROL, - label: 'Test end date', - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - validators: [{ name: ValidatorNames.AheadOfDate, args: 'testStartTimestamp' }], - }, - { - name: 'testTypes', - label: 'Test Types', - type: FormNodeTypes.ARRAY, - children: [ - { - name: '0', // it is important here that the name of the node for an ARRAY type should be an index value - type: FormNodeTypes.GROUP, - children: [ - { - name: 'testResult', - label: 'Result', - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.RADIO, - options: [ - { value: 'pass', label: 'Pass' }, - { value: 'fail', label: 'Fail' }, - { value: 'prs', label: 'PRS' }, - ], - asyncValidators: [{ name: AsyncValidatorNames.ResultDependantOnCustomDefects }], - type: FormNodeTypes.CONTROL, - }, - { - name: 'testTypeName', - label: 'Description', - value: '', - disabled: true, + name: 'testSection', + label: 'Test', + type: FormNodeTypes.GROUP, + children: [ + { + name: 'contingencyTestNumber', + label: 'Contingency Test Number', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.NUMERICSTRING, + validators: [ + { name: ValidatorNames.MaxLength, args: 8 }, + { name: ValidatorNames.MinLength, args: 6 }, + { name: ValidatorNames.Required }, + ], + width: FormNodeWidth.L, + }, + { + name: 'testStartTimestamp', + label: 'Test start date', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + validators: [{ name: ValidatorNames.PastDate }], + }, + { + name: 'testEndTimestamp', + type: FormNodeTypes.CONTROL, + label: 'Test end date', + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + validators: [{ name: ValidatorNames.AheadOfDate, args: 'testStartTimestamp' }], + }, + { + name: 'testTypes', + label: 'Test Types', + type: FormNodeTypes.ARRAY, + children: [ + { + name: '0', // it is important here that the name of the node for an ARRAY type should be an index value + type: FormNodeTypes.GROUP, + children: [ + { + name: 'testResult', + label: 'Result', + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.RADIO, + options: [ + { value: 'pass', label: 'Pass' }, + { value: 'fail', label: 'Fail' }, + { value: 'prs', label: 'PRS' }, + ], + asyncValidators: [{ name: AsyncValidatorNames.ResultDependantOnCustomDefects }], + type: FormNodeTypes.CONTROL, + }, + { + name: 'testTypeName', + label: 'Description', + value: '', + disabled: true, - type: FormNodeTypes.CONTROL, - }, - { - name: 'reasonForAbandoning', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - value: null, - required: true, - }, - { - name: 'additionalCommentsForAbandon', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - value: null, - required: true, - }, - { - name: 'certificateNumber', - label: 'Certificate number', - value: '', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - }, - { - name: 'testTypeStartTimestamp', - type: FormNodeTypes.CONTROL, - value: '', - label: 'Test start date and time', - viewType: FormNodeViewTypes.TIME, - editType: FormNodeEditTypes.DATETIME, - validators: [ - { name: ValidatorNames.Required }, - { name: ValidatorNames.PastDate }, - { name: ValidatorNames.CopyValueToRootControl, args: 'testStartTimestamp' }, - ], - }, - { - name: 'testTypeEndTimestamp', - type: FormNodeTypes.CONTROL, - value: '', - label: 'Test end date and time', - viewType: FormNodeViewTypes.TIME, - editType: FormNodeEditTypes.DATETIME, - validators: [ - { name: ValidatorNames.Required }, - { name: ValidatorNames.PastDate }, - { name: ValidatorNames.AheadOfDate, args: 'testTypeStartTimestamp' }, - { name: ValidatorNames.CopyValueToRootControl, args: 'testEndTimestamp' }, - ], - }, - { - name: 'prohibitionIssued', - type: FormNodeTypes.CONTROL, - label: 'Prohibition issued', - value: null, - editType: FormNodeEditTypes.RADIO, - options: [ - { value: true, label: 'Yes' }, - { value: false, label: 'No' }, - ], - validators: [{ name: ValidatorNames.Required }], - }, - ], - }, - ], - }, - ], + type: FormNodeTypes.CONTROL, + }, + { + name: 'reasonForAbandoning', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + value: null, + required: true, + }, + { + name: 'additionalCommentsForAbandon', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + value: null, + required: true, + }, + { + name: 'certificateNumber', + label: 'Certificate number', + value: '', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + }, + { + name: 'testTypeStartTimestamp', + type: FormNodeTypes.CONTROL, + value: '', + label: 'Test start date and time', + viewType: FormNodeViewTypes.TIME, + editType: FormNodeEditTypes.DATETIME, + validators: [ + { name: ValidatorNames.Required }, + { name: ValidatorNames.PastDate }, + { name: ValidatorNames.CopyValueToRootControl, args: 'testStartTimestamp' }, + ], + }, + { + name: 'testTypeEndTimestamp', + type: FormNodeTypes.CONTROL, + value: '', + label: 'Test end date and time', + viewType: FormNodeViewTypes.TIME, + editType: FormNodeEditTypes.DATETIME, + validators: [ + { name: ValidatorNames.Required }, + { name: ValidatorNames.PastDate }, + { name: ValidatorNames.AheadOfDate, args: 'testTypeStartTimestamp' }, + { name: ValidatorNames.CopyValueToRootControl, args: 'testEndTimestamp' }, + ], + }, + { + name: 'prohibitionIssued', + type: FormNodeTypes.CONTROL, + label: 'Prohibition issued', + value: null, + editType: FormNodeEditTypes.RADIO, + options: [ + { value: true, label: 'Yes' }, + { value: false, label: 'No' }, + ], + validators: [{ name: ValidatorNames.Required }], + }, + ], + }, + ], + }, + ], }; diff --git a/src/app/forms/templates/test-records/section-templates/test/desk-based/desk-based-test-section-group1-PSV.template.ts b/src/app/forms/templates/test-records/section-templates/test/desk-based/desk-based-test-section-group1-PSV.template.ts index 7e4db16897..b7f14c607f 100644 --- a/src/app/forms/templates/test-records/section-templates/test/desk-based/desk-based-test-section-group1-PSV.template.ts +++ b/src/app/forms/templates/test-records/section-templates/test/desk-based/desk-based-test-section-group1-PSV.template.ts @@ -1,221 +1,225 @@ import { ValidatorNames } from '@forms/models/validators.enum'; import { - FormNode, FormNodeEditTypes, FormNodeTypes, FormNodeViewTypes, FormNodeWidth, + FormNode, + FormNodeEditTypes, + FormNodeTypes, + FormNodeViewTypes, + FormNodeWidth, } from '@forms/services/dynamic-form.types'; export const DeskBasedTestSectionGroup1Psv: FormNode = { - name: 'testSection', - label: 'Test', - type: FormNodeTypes.GROUP, - children: [ - { - name: 'testStartTimestamp', - label: 'Test start date', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - }, - { - name: 'testEndTimestamp', - type: FormNodeTypes.CONTROL, - label: 'Test end date', - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - }, - { - name: 'createdAt', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'testTypes', - label: 'Test types', - type: FormNodeTypes.ARRAY, - children: [ - { - name: '0', // it is important here that the name of the node for an ARRAY type should be an index value - type: FormNodeTypes.GROUP, - children: [ - { - name: 'testResult', - label: 'Result', - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - type: FormNodeTypes.CONTROL, - value: 'pass', - }, - { - name: 'reasonForAbandoning', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - value: null, - }, - { - name: 'additionalCommentsForAbandon', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - value: null, - }, - { - name: 'certificateNumber', - label: 'Certificate number', - type: FormNodeTypes.CONTROL, - width: FormNodeWidth.L, - validators: [ - { name: ValidatorNames.Alphanumeric }, - { - name: ValidatorNames.RequiredIfEquals, - args: { sibling: 'testResult', value: ['pass'] }, - }, - ], - required: true, - value: null, - }, + name: 'testSection', + label: 'Test', + type: FormNodeTypes.GROUP, + children: [ + { + name: 'testStartTimestamp', + label: 'Test start date', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + }, + { + name: 'testEndTimestamp', + type: FormNodeTypes.CONTROL, + label: 'Test end date', + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + }, + { + name: 'createdAt', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'testTypes', + label: 'Test types', + type: FormNodeTypes.ARRAY, + children: [ + { + name: '0', // it is important here that the name of the node for an ARRAY type should be an index value + type: FormNodeTypes.GROUP, + children: [ + { + name: 'testResult', + label: 'Result', + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + type: FormNodeTypes.CONTROL, + value: 'pass', + }, + { + name: 'reasonForAbandoning', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + value: null, + }, + { + name: 'additionalCommentsForAbandon', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + value: null, + }, + { + name: 'certificateNumber', + label: 'Certificate number', + type: FormNodeTypes.CONTROL, + width: FormNodeWidth.L, + validators: [ + { name: ValidatorNames.Alphanumeric }, + { + name: ValidatorNames.RequiredIfEquals, + args: { sibling: 'testResult', value: ['pass'] }, + }, + ], + required: true, + value: null, + }, - { - name: 'testTypeStartTimestamp', - type: FormNodeTypes.CONTROL, - value: null, - label: 'Test start date', - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - }, - { - name: 'testTypeEndTimestamp', - type: FormNodeTypes.CONTROL, - value: null, - label: 'Test end date', - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - }, - { - name: 'testExpiryDate', - label: 'Expiry date', - value: null, - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.DATE, - editType: FormNodeEditTypes.DATE, - }, - { - name: 'prohibitionIssued', - label: 'Prohibition issued', - type: FormNodeTypes.CONTROL, - value: null, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - }, - ], - }, - ], - }, - ], + { + name: 'testTypeStartTimestamp', + type: FormNodeTypes.CONTROL, + value: null, + label: 'Test start date', + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + }, + { + name: 'testTypeEndTimestamp', + type: FormNodeTypes.CONTROL, + value: null, + label: 'Test end date', + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + }, + { + name: 'testExpiryDate', + label: 'Expiry date', + value: null, + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.DATE, + editType: FormNodeEditTypes.DATE, + }, + { + name: 'prohibitionIssued', + label: 'Prohibition issued', + type: FormNodeTypes.CONTROL, + value: null, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + }, + ], + }, + ], + }, + ], }; export const AmendDeskBasedTestSectionGroup1Psv: FormNode = { - name: 'testSection', - label: 'Test', - type: FormNodeTypes.GROUP, - children: [ - { - name: 'testStartTimestamp', - label: 'Test start date', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - }, - { - name: 'testEndTimestamp', - type: FormNodeTypes.CONTROL, - label: 'Test end date', - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - }, - { - name: 'testTypes', - label: 'Test types', - type: FormNodeTypes.ARRAY, - children: [ - { - name: '0', // it is important here that the name of the node for an ARRAY type should be an index value - type: FormNodeTypes.GROUP, - children: [ - { - name: 'testCode', - label: 'Test Code', - value: '', - disabled: true, - type: FormNodeTypes.CONTROL, - width: FormNodeWidth.XS, - }, - { - name: 'testResult', - label: 'Result', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.RADIO, - options: [ - { value: 'pass', label: 'Pass' }, - { value: 'fail', label: 'Fail' }, - ], - }, - { - name: 'certificateNumber', - label: 'Certificate number', - type: FormNodeTypes.CONTROL, - width: FormNodeWidth.L, - validators: [ - { name: ValidatorNames.Alphanumeric }, - { - name: ValidatorNames.RequiredIfEquals, - args: { sibling: 'testResult', value: ['pass'] }, - }, - ], - required: true, - value: null, - }, - { - name: 'testTypeStartTimestamp', - type: FormNodeTypes.CONTROL, - value: null, - label: 'Test start date', - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - }, - { - name: 'testTypeEndTimestamp', - type: FormNodeTypes.CONTROL, - value: null, - label: 'Test end date', - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - }, - { - name: 'testExpiryDate', - label: 'Expiry date', - value: null, - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.DATE, - editType: FormNodeEditTypes.DATE, - validators: [{ name: ValidatorNames.HideIfEmpty, args: 'testAnniversaryDate' }], - }, - { - name: 'testAnniversaryDate', - label: 'Anniversary date', - value: '', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.DATE, - editType: FormNodeEditTypes.DATE, - validators: [ - { name: ValidatorNames.AheadOfDate, args: 'testTypeStartTimestamp' }, - { name: ValidatorNames.DateNotExceed, args: { sibling: 'testExpiryDate', months: 14 } }, - ], - }, - ], - }, - ], - }, - ], + name: 'testSection', + label: 'Test', + type: FormNodeTypes.GROUP, + children: [ + { + name: 'testStartTimestamp', + label: 'Test start date', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + }, + { + name: 'testEndTimestamp', + type: FormNodeTypes.CONTROL, + label: 'Test end date', + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + }, + { + name: 'testTypes', + label: 'Test types', + type: FormNodeTypes.ARRAY, + children: [ + { + name: '0', // it is important here that the name of the node for an ARRAY type should be an index value + type: FormNodeTypes.GROUP, + children: [ + { + name: 'testCode', + label: 'Test Code', + value: '', + disabled: true, + type: FormNodeTypes.CONTROL, + width: FormNodeWidth.XS, + }, + { + name: 'testResult', + label: 'Result', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.RADIO, + options: [ + { value: 'pass', label: 'Pass' }, + { value: 'fail', label: 'Fail' }, + ], + }, + { + name: 'certificateNumber', + label: 'Certificate number', + type: FormNodeTypes.CONTROL, + width: FormNodeWidth.L, + validators: [ + { name: ValidatorNames.Alphanumeric }, + { + name: ValidatorNames.RequiredIfEquals, + args: { sibling: 'testResult', value: ['pass'] }, + }, + ], + required: true, + value: null, + }, + { + name: 'testTypeStartTimestamp', + type: FormNodeTypes.CONTROL, + value: null, + label: 'Test start date', + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + }, + { + name: 'testTypeEndTimestamp', + type: FormNodeTypes.CONTROL, + value: null, + label: 'Test end date', + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + }, + { + name: 'testExpiryDate', + label: 'Expiry date', + value: null, + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.DATE, + editType: FormNodeEditTypes.DATE, + validators: [{ name: ValidatorNames.HideIfEmpty, args: 'testAnniversaryDate' }], + }, + { + name: 'testAnniversaryDate', + label: 'Anniversary date', + value: '', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.DATE, + editType: FormNodeEditTypes.DATE, + validators: [ + { name: ValidatorNames.AheadOfDate, args: 'testTypeStartTimestamp' }, + { name: ValidatorNames.DateNotExceed, args: { sibling: 'testExpiryDate', months: 14 } }, + ], + }, + ], + }, + ], + }, + ], }; diff --git a/src/app/forms/templates/test-records/section-templates/test/desk-based/desk-based-test-section-group1And4-HGV-TRL.template.ts b/src/app/forms/templates/test-records/section-templates/test/desk-based/desk-based-test-section-group1And4-HGV-TRL.template.ts index f474b48b08..be890a4ba3 100644 --- a/src/app/forms/templates/test-records/section-templates/test/desk-based/desk-based-test-section-group1And4-HGV-TRL.template.ts +++ b/src/app/forms/templates/test-records/section-templates/test/desk-based/desk-based-test-section-group1And4-HGV-TRL.template.ts @@ -1,195 +1,199 @@ import { - FormNode, FormNodeEditTypes, FormNodeTypes, FormNodeViewTypes, FormNodeWidth, + FormNode, + FormNodeEditTypes, + FormNodeTypes, + FormNodeViewTypes, + FormNodeWidth, } from '@forms/services/dynamic-form.types'; export const DeskBasedTestSectionGroup1And4HgvTrl: FormNode = { - name: 'requiredSection', - type: FormNodeTypes.GROUP, - children: [ - { - name: 'testStartTimestamp', - label: 'Test start date', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - }, - { - name: 'testEndTimestamp', - type: FormNodeTypes.CONTROL, - label: 'Test end date', - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - }, - { - name: 'createdAt', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'testTypes', - label: 'Test Types', - type: FormNodeTypes.ARRAY, - children: [ - { - name: '0', // it is important here that the name of the node for an ARRAY type should be an index value - type: FormNodeTypes.GROUP, - children: [ - { - name: 'testResult', - label: 'Result', - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - type: FormNodeTypes.CONTROL, - value: 'pass', - }, - { - name: 'reasonForAbandoning', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - value: null, - required: true, - }, - { - name: 'additionalCommentsForAbandon', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - value: null, - required: true, - }, - { - name: 'certificateNumber', - label: 'Certificate number', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - width: FormNodeWidth.L, - required: true, - value: null, - }, + name: 'requiredSection', + type: FormNodeTypes.GROUP, + children: [ + { + name: 'testStartTimestamp', + label: 'Test start date', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + }, + { + name: 'testEndTimestamp', + type: FormNodeTypes.CONTROL, + label: 'Test end date', + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + }, + { + name: 'createdAt', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'testTypes', + label: 'Test Types', + type: FormNodeTypes.ARRAY, + children: [ + { + name: '0', // it is important here that the name of the node for an ARRAY type should be an index value + type: FormNodeTypes.GROUP, + children: [ + { + name: 'testResult', + label: 'Result', + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + type: FormNodeTypes.CONTROL, + value: 'pass', + }, + { + name: 'reasonForAbandoning', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + value: null, + required: true, + }, + { + name: 'additionalCommentsForAbandon', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + value: null, + required: true, + }, + { + name: 'certificateNumber', + label: 'Certificate number', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + width: FormNodeWidth.L, + required: true, + value: null, + }, - { - name: 'testTypeStartTimestamp', - type: FormNodeTypes.CONTROL, - value: null, - label: 'Test start date and time', - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - }, - { - name: 'testTypeEndTimestamp', - type: FormNodeTypes.CONTROL, - value: null, - label: 'Test end date and time', - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - }, - { - name: 'prohibitionIssued', - label: 'Prohibition issued', - type: FormNodeTypes.CONTROL, - value: null, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - }, - ], - }, - ], - }, - ], + { + name: 'testTypeStartTimestamp', + type: FormNodeTypes.CONTROL, + value: null, + label: 'Test start date and time', + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + }, + { + name: 'testTypeEndTimestamp', + type: FormNodeTypes.CONTROL, + value: null, + label: 'Test end date and time', + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + }, + { + name: 'prohibitionIssued', + label: 'Prohibition issued', + type: FormNodeTypes.CONTROL, + value: null, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + }, + ], + }, + ], + }, + ], }; export const amendDeskBasedTestSectionGroup1And4HgvTrl: FormNode = { - name: 'testSection', - label: 'Test', - type: FormNodeTypes.GROUP, - children: [ - { - name: 'testStartTimestamp', - label: 'Test start date', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - }, - { - name: 'testEndTimestamp', - type: FormNodeTypes.CONTROL, - label: 'Test end date', - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - }, - { - name: 'createdAt', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'testTypes', - label: 'Test Types', - type: FormNodeTypes.ARRAY, - children: [ - { - name: '0', // it is important here that the name of the node for an ARRAY type should be an index value - type: FormNodeTypes.GROUP, - children: [ - { - name: 'testCode', - label: 'Test Code', - value: '', - disabled: true, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.STRING, - type: FormNodeTypes.CONTROL, - width: FormNodeWidth.XS, - }, - { - name: 'testResult', - label: 'Result', - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - type: FormNodeTypes.CONTROL, - value: 'pass', - }, - { - name: 'certificateNumber', - label: 'Certificate number', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - width: FormNodeWidth.L, - required: true, - value: null, - }, + name: 'testSection', + label: 'Test', + type: FormNodeTypes.GROUP, + children: [ + { + name: 'testStartTimestamp', + label: 'Test start date', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + }, + { + name: 'testEndTimestamp', + type: FormNodeTypes.CONTROL, + label: 'Test end date', + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + }, + { + name: 'createdAt', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'testTypes', + label: 'Test Types', + type: FormNodeTypes.ARRAY, + children: [ + { + name: '0', // it is important here that the name of the node for an ARRAY type should be an index value + type: FormNodeTypes.GROUP, + children: [ + { + name: 'testCode', + label: 'Test Code', + value: '', + disabled: true, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.STRING, + type: FormNodeTypes.CONTROL, + width: FormNodeWidth.XS, + }, + { + name: 'testResult', + label: 'Result', + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + type: FormNodeTypes.CONTROL, + value: 'pass', + }, + { + name: 'certificateNumber', + label: 'Certificate number', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + width: FormNodeWidth.L, + required: true, + value: null, + }, - { - name: 'testTypeStartTimestamp', - type: FormNodeTypes.CONTROL, - value: null, - label: 'Test start date and time', - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - }, - { - name: 'testTypeEndTimestamp', - type: FormNodeTypes.CONTROL, - value: null, - label: 'Test end date and time', - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - }, - { - name: 'testNumber', - label: 'Test Number', - value: '', - disabled: true, - type: FormNodeTypes.CONTROL, - }, - ], - }, - ], - }, - ], + { + name: 'testTypeStartTimestamp', + type: FormNodeTypes.CONTROL, + value: null, + label: 'Test start date and time', + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + }, + { + name: 'testTypeEndTimestamp', + type: FormNodeTypes.CONTROL, + value: null, + label: 'Test end date and time', + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + }, + { + name: 'testNumber', + label: 'Test Number', + value: '', + disabled: true, + type: FormNodeTypes.CONTROL, + }, + ], + }, + ], + }, + ], }; diff --git a/src/app/forms/templates/test-records/section-templates/test/desk-based/desk-based-test-section-group2.template.ts b/src/app/forms/templates/test-records/section-templates/test/desk-based/desk-based-test-section-group2.template.ts index beb205c3e8..7b4f529358 100644 --- a/src/app/forms/templates/test-records/section-templates/test/desk-based/desk-based-test-section-group2.template.ts +++ b/src/app/forms/templates/test-records/section-templates/test/desk-based/desk-based-test-section-group2.template.ts @@ -1,204 +1,208 @@ import { ValidatorNames } from '@forms/models/validators.enum'; import { - FormNode, FormNodeEditTypes, FormNodeTypes, FormNodeViewTypes, FormNodeWidth, + FormNode, + FormNodeEditTypes, + FormNodeTypes, + FormNodeViewTypes, + FormNodeWidth, } from '@forms/services/dynamic-form.types'; export const DeskBasedTestSectionGroup2And5: FormNode = { - name: 'testSection', - label: 'Test', - type: FormNodeTypes.GROUP, - children: [ - { - name: 'testStartTimestamp', - label: 'Test start date', - type: FormNodeTypes.CONTROL, - value: '', - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - }, - { - name: 'testEndTimestamp', - type: FormNodeTypes.CONTROL, - label: 'Test end date', - value: '', - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - }, - { - name: 'createdAt', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'testTypes', - type: FormNodeTypes.ARRAY, - children: [ - { - name: '0', - type: FormNodeTypes.GROUP, - children: [ - { - name: 'testResult', - type: FormNodeTypes.CONTROL, - label: 'Result', - value: 'pass', - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'reasonForAbandoning', - type: FormNodeTypes.CONTROL, - value: null, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - required: true, - }, - { - name: 'additionalCommentsForAbandon', - type: FormNodeTypes.CONTROL, - value: null, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - required: true, - }, - { - name: 'certificateNumber', - type: FormNodeTypes.CONTROL, - label: 'Certificate number', - value: '', - validators: [{ name: ValidatorNames.Alphanumeric }, { name: ValidatorNames.Required }], - width: FormNodeWidth.L, - required: true, - }, - { - name: 'testExpiryDate', - type: FormNodeTypes.CONTROL, - label: 'Expiry Date', - value: null, - viewType: FormNodeViewTypes.DATE, - editType: FormNodeEditTypes.DATE, - }, - { - name: 'testTypeStartTimestamp', - type: FormNodeTypes.CONTROL, - label: 'Test start date and time', - value: null, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - }, - { - name: 'testTypeEndTimestamp', - type: FormNodeTypes.CONTROL, - label: 'Test end date and time', - value: null, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - }, - { - name: 'prohibitionIssued', - type: FormNodeTypes.CONTROL, - value: null, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - }, - ], - }, - ], - }, - ], + name: 'testSection', + label: 'Test', + type: FormNodeTypes.GROUP, + children: [ + { + name: 'testStartTimestamp', + label: 'Test start date', + type: FormNodeTypes.CONTROL, + value: '', + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + }, + { + name: 'testEndTimestamp', + type: FormNodeTypes.CONTROL, + label: 'Test end date', + value: '', + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + }, + { + name: 'createdAt', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'testTypes', + type: FormNodeTypes.ARRAY, + children: [ + { + name: '0', + type: FormNodeTypes.GROUP, + children: [ + { + name: 'testResult', + type: FormNodeTypes.CONTROL, + label: 'Result', + value: 'pass', + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'reasonForAbandoning', + type: FormNodeTypes.CONTROL, + value: null, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + required: true, + }, + { + name: 'additionalCommentsForAbandon', + type: FormNodeTypes.CONTROL, + value: null, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + required: true, + }, + { + name: 'certificateNumber', + type: FormNodeTypes.CONTROL, + label: 'Certificate number', + value: '', + validators: [{ name: ValidatorNames.Alphanumeric }, { name: ValidatorNames.Required }], + width: FormNodeWidth.L, + required: true, + }, + { + name: 'testExpiryDate', + type: FormNodeTypes.CONTROL, + label: 'Expiry Date', + value: null, + viewType: FormNodeViewTypes.DATE, + editType: FormNodeEditTypes.DATE, + }, + { + name: 'testTypeStartTimestamp', + type: FormNodeTypes.CONTROL, + label: 'Test start date and time', + value: null, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + }, + { + name: 'testTypeEndTimestamp', + type: FormNodeTypes.CONTROL, + label: 'Test end date and time', + value: null, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + }, + { + name: 'prohibitionIssued', + type: FormNodeTypes.CONTROL, + value: null, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + }, + ], + }, + ], + }, + ], }; export const AmendDeskBasedTestSectionGroup2And5: FormNode = { - name: 'testSection', - label: 'Test', - type: FormNodeTypes.GROUP, - children: [ - { - name: 'testStartTimestamp', - label: 'Test start date', - type: FormNodeTypes.CONTROL, - value: '', - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - }, - { - name: 'testEndTimestamp', - type: FormNodeTypes.CONTROL, - label: 'Test end date', - value: '', - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - }, - { - name: 'createdAt', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'testTypes', - type: FormNodeTypes.ARRAY, - children: [ - { - name: '0', - type: FormNodeTypes.GROUP, - children: [ - { - name: 'testCode', - label: 'Test Code', - value: '', - disabled: true, - type: FormNodeTypes.CONTROL, - width: FormNodeWidth.XS, - }, - { - name: 'testResult', - label: 'Result', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.RADIO, - options: [ - { value: 'pass', label: 'Pass' }, - { value: 'fail', label: 'Fail' }, - ], - }, - { - name: 'certificateNumber', - type: FormNodeTypes.CONTROL, - label: 'Certificate number', - value: '', - validators: [{ name: ValidatorNames.Alphanumeric }, { name: ValidatorNames.Required }], - width: FormNodeWidth.L, - required: true, - }, - { - name: 'testExpiryDate', - type: FormNodeTypes.CONTROL, - label: 'Expiry Date', - value: null, - viewType: FormNodeViewTypes.DATE, - editType: FormNodeEditTypes.DATE, - }, - { - name: 'testTypeStartTimestamp', - type: FormNodeTypes.CONTROL, - label: 'Test start date and time', - value: null, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - }, - { - name: 'testTypeEndTimestamp', - type: FormNodeTypes.CONTROL, - label: 'Test end date and time', - value: null, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - }, - ], - }, - ], - }, - ], + name: 'testSection', + label: 'Test', + type: FormNodeTypes.GROUP, + children: [ + { + name: 'testStartTimestamp', + label: 'Test start date', + type: FormNodeTypes.CONTROL, + value: '', + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + }, + { + name: 'testEndTimestamp', + type: FormNodeTypes.CONTROL, + label: 'Test end date', + value: '', + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + }, + { + name: 'createdAt', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'testTypes', + type: FormNodeTypes.ARRAY, + children: [ + { + name: '0', + type: FormNodeTypes.GROUP, + children: [ + { + name: 'testCode', + label: 'Test Code', + value: '', + disabled: true, + type: FormNodeTypes.CONTROL, + width: FormNodeWidth.XS, + }, + { + name: 'testResult', + label: 'Result', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.RADIO, + options: [ + { value: 'pass', label: 'Pass' }, + { value: 'fail', label: 'Fail' }, + ], + }, + { + name: 'certificateNumber', + type: FormNodeTypes.CONTROL, + label: 'Certificate number', + value: '', + validators: [{ name: ValidatorNames.Alphanumeric }, { name: ValidatorNames.Required }], + width: FormNodeWidth.L, + required: true, + }, + { + name: 'testExpiryDate', + type: FormNodeTypes.CONTROL, + label: 'Expiry Date', + value: null, + viewType: FormNodeViewTypes.DATE, + editType: FormNodeEditTypes.DATE, + }, + { + name: 'testTypeStartTimestamp', + type: FormNodeTypes.CONTROL, + label: 'Test start date and time', + value: null, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + }, + { + name: 'testTypeEndTimestamp', + type: FormNodeTypes.CONTROL, + label: 'Test end date and time', + value: null, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + }, + ], + }, + ], + }, + ], }; diff --git a/src/app/forms/templates/test-records/section-templates/test/desk-based/desk-based-test-section-group3.template.ts b/src/app/forms/templates/test-records/section-templates/test/desk-based/desk-based-test-section-group3.template.ts index bd0d56b79d..0694519ab0 100644 --- a/src/app/forms/templates/test-records/section-templates/test/desk-based/desk-based-test-section-group3.template.ts +++ b/src/app/forms/templates/test-records/section-templates/test/desk-based/desk-based-test-section-group3.template.ts @@ -1,198 +1,202 @@ import { ValidatorNames } from '@forms/models/validators.enum'; import { - FormNode, FormNodeEditTypes, FormNodeTypes, FormNodeViewTypes, FormNodeWidth, + FormNode, + FormNodeEditTypes, + FormNodeTypes, + FormNodeViewTypes, + FormNodeWidth, } from '@forms/services/dynamic-form.types'; export const DeskBasedTestSectionGroup3: FormNode = { - name: 'testSection', - label: 'Test', - type: FormNodeTypes.GROUP, - children: [ - { - name: 'testStartTimestamp', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - }, - { - name: 'testEndTimestamp', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - }, - { - name: 'createdAt', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'testTypes', - label: 'Test Types', - type: FormNodeTypes.ARRAY, - children: [ - { - name: '0', // it is important here that the name of the node for an ARRAY type should be an index value - type: FormNodeTypes.GROUP, - children: [ - { - name: 'testResult', - label: 'Result', - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - type: FormNodeTypes.CONTROL, - value: 'pass', - }, - { - name: 'reasonForAbandoning', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - value: null, - }, - { - name: 'additionalCommentsForAbandon', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - value: null, - }, - { - name: 'certificateNumber', - label: 'Certificate number', - type: FormNodeTypes.CONTROL, - width: FormNodeWidth.L, - validators: [{ name: ValidatorNames.Alphanumeric }, { name: ValidatorNames.Required }], - value: null, - }, - { - name: 'testTypeStartTimestamp', - type: FormNodeTypes.CONTROL, - value: null, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - }, - { - name: 'testTypeEndTimestamp', - type: FormNodeTypes.CONTROL, - value: null, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - }, - { - name: 'testExpiryDate', - value: null, - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - }, - { - name: 'prohibitionIssued', - label: 'Prohibition issued', - type: FormNodeTypes.CONTROL, - value: null, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - }, - ], - }, - ], - }, - ], + name: 'testSection', + label: 'Test', + type: FormNodeTypes.GROUP, + children: [ + { + name: 'testStartTimestamp', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + }, + { + name: 'testEndTimestamp', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + }, + { + name: 'createdAt', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'testTypes', + label: 'Test Types', + type: FormNodeTypes.ARRAY, + children: [ + { + name: '0', // it is important here that the name of the node for an ARRAY type should be an index value + type: FormNodeTypes.GROUP, + children: [ + { + name: 'testResult', + label: 'Result', + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + type: FormNodeTypes.CONTROL, + value: 'pass', + }, + { + name: 'reasonForAbandoning', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + value: null, + }, + { + name: 'additionalCommentsForAbandon', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + value: null, + }, + { + name: 'certificateNumber', + label: 'Certificate number', + type: FormNodeTypes.CONTROL, + width: FormNodeWidth.L, + validators: [{ name: ValidatorNames.Alphanumeric }, { name: ValidatorNames.Required }], + value: null, + }, + { + name: 'testTypeStartTimestamp', + type: FormNodeTypes.CONTROL, + value: null, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + }, + { + name: 'testTypeEndTimestamp', + type: FormNodeTypes.CONTROL, + value: null, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + }, + { + name: 'testExpiryDate', + value: null, + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + }, + { + name: 'prohibitionIssued', + label: 'Prohibition issued', + type: FormNodeTypes.CONTROL, + value: null, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + }, + ], + }, + ], + }, + ], }; export const AmendDeskBasedTestSectionGroup3: FormNode = { - name: 'testSection', - label: 'Test', - type: FormNodeTypes.GROUP, - children: [ - { - name: 'testStartTimestamp', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - }, - { - name: 'testEndTimestamp', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - }, - { - name: 'createdAt', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'testTypes', - label: 'Test Types', - type: FormNodeTypes.ARRAY, - children: [ - { - name: '0', // it is important here that the name of the node for an ARRAY type should be an index value - type: FormNodeTypes.GROUP, - children: [ - { - name: 'testCode', - label: 'Test Code', - value: '', - disabled: true, - type: FormNodeTypes.CONTROL, - width: FormNodeWidth.XS, - }, - { - name: 'testResult', - label: 'Result', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.RADIO, - options: [ - { value: 'pass', label: 'Pass' }, - { value: 'fail', label: 'Fail' }, - ], - }, - { - name: 'certificateNumber', - label: 'Certificate number', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.TEXT, - width: FormNodeWidth.L, - validators: [{ name: ValidatorNames.Alphanumeric }], - value: null, - }, - { - name: 'testNumber', - value: '', - disabled: true, - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - }, - { - name: 'testTypeStartTimestamp', - type: FormNodeTypes.CONTROL, - value: null, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - }, - { - name: 'testTypeEndTimestamp', - type: FormNodeTypes.CONTROL, - value: null, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - }, - { - name: 'testExpiryDate', - value: null, - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - }, - ], - }, - ], - }, - ], + name: 'testSection', + label: 'Test', + type: FormNodeTypes.GROUP, + children: [ + { + name: 'testStartTimestamp', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + }, + { + name: 'testEndTimestamp', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + }, + { + name: 'createdAt', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'testTypes', + label: 'Test Types', + type: FormNodeTypes.ARRAY, + children: [ + { + name: '0', // it is important here that the name of the node for an ARRAY type should be an index value + type: FormNodeTypes.GROUP, + children: [ + { + name: 'testCode', + label: 'Test Code', + value: '', + disabled: true, + type: FormNodeTypes.CONTROL, + width: FormNodeWidth.XS, + }, + { + name: 'testResult', + label: 'Result', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.RADIO, + options: [ + { value: 'pass', label: 'Pass' }, + { value: 'fail', label: 'Fail' }, + ], + }, + { + name: 'certificateNumber', + label: 'Certificate number', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.TEXT, + width: FormNodeWidth.L, + validators: [{ name: ValidatorNames.Alphanumeric }], + value: null, + }, + { + name: 'testNumber', + value: '', + disabled: true, + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + }, + { + name: 'testTypeStartTimestamp', + type: FormNodeTypes.CONTROL, + value: null, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + }, + { + name: 'testTypeEndTimestamp', + type: FormNodeTypes.CONTROL, + value: null, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + }, + { + name: 'testExpiryDate', + value: null, + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + }, + ], + }, + ], + }, + ], }; diff --git a/src/app/forms/templates/test-records/section-templates/test/desk-based/desk-based-test-section-group4-PSV.template.ts b/src/app/forms/templates/test-records/section-templates/test/desk-based/desk-based-test-section-group4-PSV.template.ts index 7df18ec6dc..c3409ef041 100644 --- a/src/app/forms/templates/test-records/section-templates/test/desk-based/desk-based-test-section-group4-PSV.template.ts +++ b/src/app/forms/templates/test-records/section-templates/test/desk-based/desk-based-test-section-group4-PSV.template.ts @@ -1,227 +1,231 @@ import { ValidatorNames } from '@forms/models/validators.enum'; import { - FormNode, FormNodeEditTypes, FormNodeTypes, FormNodeViewTypes, FormNodeWidth, + FormNode, + FormNodeEditTypes, + FormNodeTypes, + FormNodeViewTypes, + FormNodeWidth, } from '@forms/services/dynamic-form.types'; export const DeskBasedTestSectionGroup4Psv: FormNode = { - name: 'testSection', - label: 'Test', - type: FormNodeTypes.GROUP, - children: [ - { - name: 'testStartTimestamp', - label: 'Test start date', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - }, - { - name: 'testEndTimestamp', - type: FormNodeTypes.CONTROL, - label: 'Test end date', - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - }, - { - name: 'createdAt', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'testTypes', - label: 'Test Types', - type: FormNodeTypes.ARRAY, - children: [ - { - name: '0', // it is important here that the name of the node for an ARRAY type should be an index value - type: FormNodeTypes.GROUP, - children: [ - { - name: 'testResult', - label: 'Result', - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - type: FormNodeTypes.CONTROL, - value: 'pass', - }, - { - name: 'reasonForAbandoning', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - value: null, - }, - { - name: 'additionalCommentsForAbandon', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - value: null, - }, - { - name: 'certificateNumber', - label: 'Certificate number', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - width: FormNodeWidth.L, - validators: [{ name: ValidatorNames.Alphanumeric }], - value: null, - }, - { - name: 'secondaryCertificateNumber', - label: 'Secondary Certificate number', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.TEXT, - width: FormNodeWidth.L, - validators: [{ name: ValidatorNames.Alphanumeric }], - value: null, - }, - { - name: 'testTypeStartTimestamp', - type: FormNodeTypes.CONTROL, - value: null, - label: 'Test start date and time', - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - }, - { - name: 'testTypeEndTimestamp', - type: FormNodeTypes.CONTROL, - value: null, - label: 'Test end date and time', - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - }, - { - name: 'testExpiryDate', - label: 'Expiry Date', - value: null, - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.DATE, - editType: FormNodeEditTypes.DATE, - }, - { - name: 'prohibitionIssued', - label: 'Prohibition issued', - type: FormNodeTypes.CONTROL, - value: null, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - }, - ], - }, - ], - }, - ], + name: 'testSection', + label: 'Test', + type: FormNodeTypes.GROUP, + children: [ + { + name: 'testStartTimestamp', + label: 'Test start date', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + }, + { + name: 'testEndTimestamp', + type: FormNodeTypes.CONTROL, + label: 'Test end date', + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + }, + { + name: 'createdAt', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'testTypes', + label: 'Test Types', + type: FormNodeTypes.ARRAY, + children: [ + { + name: '0', // it is important here that the name of the node for an ARRAY type should be an index value + type: FormNodeTypes.GROUP, + children: [ + { + name: 'testResult', + label: 'Result', + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + type: FormNodeTypes.CONTROL, + value: 'pass', + }, + { + name: 'reasonForAbandoning', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + value: null, + }, + { + name: 'additionalCommentsForAbandon', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + value: null, + }, + { + name: 'certificateNumber', + label: 'Certificate number', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + width: FormNodeWidth.L, + validators: [{ name: ValidatorNames.Alphanumeric }], + value: null, + }, + { + name: 'secondaryCertificateNumber', + label: 'Secondary Certificate number', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.TEXT, + width: FormNodeWidth.L, + validators: [{ name: ValidatorNames.Alphanumeric }], + value: null, + }, + { + name: 'testTypeStartTimestamp', + type: FormNodeTypes.CONTROL, + value: null, + label: 'Test start date and time', + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + }, + { + name: 'testTypeEndTimestamp', + type: FormNodeTypes.CONTROL, + value: null, + label: 'Test end date and time', + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + }, + { + name: 'testExpiryDate', + label: 'Expiry Date', + value: null, + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.DATE, + editType: FormNodeEditTypes.DATE, + }, + { + name: 'prohibitionIssued', + label: 'Prohibition issued', + type: FormNodeTypes.CONTROL, + value: null, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + }, + ], + }, + ], + }, + ], }; export const amendDeskBasedTestSectionGroup4Psv: FormNode = { - name: 'testSection', - label: 'Test', - type: FormNodeTypes.GROUP, - children: [ - { - name: 'testStartTimestamp', - label: 'Test start date', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - }, - { - name: 'testEndTimestamp', - type: FormNodeTypes.CONTROL, - label: 'Test end date', - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - }, - { - name: 'testTypes', - label: 'Test Types', - type: FormNodeTypes.ARRAY, - children: [ - { - name: '0', // it is important here that the name of the node for an ARRAY type should be an index value - type: FormNodeTypes.GROUP, - children: [ - { - name: 'testCode', - label: 'Test Code', - value: '', - disabled: true, - type: FormNodeTypes.CONTROL, - width: FormNodeWidth.XS, - }, - { - name: 'testResult', - label: 'Result', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.RADIO, - options: [ - { value: 'pass', label: 'Pass' }, - { value: 'fail', label: 'Fail' }, - ], - }, - { - name: 'certificateNumber', - label: 'Certificate number', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.TEXT, - width: FormNodeWidth.L, - validators: [{ name: ValidatorNames.Alphanumeric }], - value: null, - }, - { - name: 'secondaryCertificateNumber', - label: 'Secondary Certificate number', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.TEXT, - width: FormNodeWidth.L, - validators: [{ name: ValidatorNames.Alphanumeric }], - value: null, - }, - { - name: 'testTypeStartTimestamp', - type: FormNodeTypes.CONTROL, - value: null, - label: 'Test start date and time', - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - }, - { - name: 'testTypeEndTimestamp', - type: FormNodeTypes.CONTROL, - value: null, - label: 'Test end date and time', - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - }, - { - name: 'testExpiryDate', - label: 'Expiry Date', - value: null, - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.DATE, - editType: FormNodeEditTypes.DATE, - validators: [{ name: ValidatorNames.HideIfEmpty, args: 'testAnniversaryDate' }], - }, - { - name: 'testAnniversaryDate', - label: 'Anniversary date', - value: '', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.DATE, - editType: FormNodeEditTypes.DATE, - validators: [ - { name: ValidatorNames.AheadOfDate, args: 'testTypeStartTimestamp' }, - { name: ValidatorNames.DateNotExceed, args: { sibling: 'testExpiryDate', months: 14 } }, - ], - }, - ], - }, - ], - }, - ], + name: 'testSection', + label: 'Test', + type: FormNodeTypes.GROUP, + children: [ + { + name: 'testStartTimestamp', + label: 'Test start date', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + }, + { + name: 'testEndTimestamp', + type: FormNodeTypes.CONTROL, + label: 'Test end date', + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + }, + { + name: 'testTypes', + label: 'Test Types', + type: FormNodeTypes.ARRAY, + children: [ + { + name: '0', // it is important here that the name of the node for an ARRAY type should be an index value + type: FormNodeTypes.GROUP, + children: [ + { + name: 'testCode', + label: 'Test Code', + value: '', + disabled: true, + type: FormNodeTypes.CONTROL, + width: FormNodeWidth.XS, + }, + { + name: 'testResult', + label: 'Result', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.RADIO, + options: [ + { value: 'pass', label: 'Pass' }, + { value: 'fail', label: 'Fail' }, + ], + }, + { + name: 'certificateNumber', + label: 'Certificate number', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.TEXT, + width: FormNodeWidth.L, + validators: [{ name: ValidatorNames.Alphanumeric }], + value: null, + }, + { + name: 'secondaryCertificateNumber', + label: 'Secondary Certificate number', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.TEXT, + width: FormNodeWidth.L, + validators: [{ name: ValidatorNames.Alphanumeric }], + value: null, + }, + { + name: 'testTypeStartTimestamp', + type: FormNodeTypes.CONTROL, + value: null, + label: 'Test start date and time', + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + }, + { + name: 'testTypeEndTimestamp', + type: FormNodeTypes.CONTROL, + value: null, + label: 'Test end date and time', + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + }, + { + name: 'testExpiryDate', + label: 'Expiry Date', + value: null, + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.DATE, + editType: FormNodeEditTypes.DATE, + validators: [{ name: ValidatorNames.HideIfEmpty, args: 'testAnniversaryDate' }], + }, + { + name: 'testAnniversaryDate', + label: 'Anniversary date', + value: '', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.DATE, + editType: FormNodeEditTypes.DATE, + validators: [ + { name: ValidatorNames.AheadOfDate, args: 'testTypeStartTimestamp' }, + { name: ValidatorNames.DateNotExceed, args: { sibling: 'testExpiryDate', months: 14 } }, + ], + }, + ], + }, + ], + }, + ], }; diff --git a/src/app/forms/templates/test-records/section-templates/test/desk-based/desk-based-test-section-group4-lgv-template.ts b/src/app/forms/templates/test-records/section-templates/test/desk-based/desk-based-test-section-group4-lgv-template.ts index ed9291d02f..63a8e2fc82 100644 --- a/src/app/forms/templates/test-records/section-templates/test/desk-based/desk-based-test-section-group4-lgv-template.ts +++ b/src/app/forms/templates/test-records/section-templates/test/desk-based/desk-based-test-section-group4-lgv-template.ts @@ -1,191 +1,195 @@ import { ValidatorNames } from '@forms/models/validators.enum'; import { - FormNode, FormNodeEditTypes, FormNodeTypes, FormNodeViewTypes, FormNodeWidth, + FormNode, + FormNodeEditTypes, + FormNodeTypes, + FormNodeViewTypes, + FormNodeWidth, } from '@forms/services/dynamic-form.types'; export const DeskBasedTestSectionGroup4LgvCarMotorcycle: FormNode = { - name: 'testSection', - label: 'Test', - type: FormNodeTypes.GROUP, - children: [ - { - name: 'testStartTimestamp', - label: 'Test start date', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - }, - { - name: 'testEndTimestamp', - type: FormNodeTypes.CONTROL, - label: 'Test end date', - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - }, - { - name: 'testTypes', - label: 'Test Types', - type: FormNodeTypes.ARRAY, - children: [ - { - name: '0', // it is important here that the name of the node for an ARRAY type should be an index value - type: FormNodeTypes.GROUP, - children: [ - { - name: 'testResult', - label: 'Result', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.RADIO, - options: [ - { value: 'pass', label: 'Pass' }, - { value: 'fail', label: 'Fail' }, - ], - }, - { - name: 'certificateNumber', - label: 'Certificate number', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.TEXT, - width: FormNodeWidth.L, - validators: [{ name: ValidatorNames.Alphanumeric }, { name: ValidatorNames.Required }], - value: null, - }, - { - name: 'testTypeStartTimestamp', - type: FormNodeTypes.CONTROL, - value: '', - label: 'Test start date and time', - viewType: FormNodeViewTypes.TIME, - editType: FormNodeEditTypes.DATETIME, - validators: [ - { name: ValidatorNames.Required }, - { name: ValidatorNames.PastDate }, - { name: ValidatorNames.CopyValueToRootControl, args: 'testStartTimestamp' }, - ], - }, - { - name: 'testTypeEndTimestamp', - type: FormNodeTypes.CONTROL, - value: '', - label: 'Test end date and time', - viewType: FormNodeViewTypes.TIME, - editType: FormNodeEditTypes.DATETIME, - validators: [ - { name: ValidatorNames.Required }, - { name: ValidatorNames.PastDate }, - { name: ValidatorNames.AheadOfDate, args: 'testTypeStartTimestamp' }, - { name: ValidatorNames.CopyValueToRootControl, args: 'testEndTimestamp' }, - ], - }, - ], - }, - ], - }, - ], + name: 'testSection', + label: 'Test', + type: FormNodeTypes.GROUP, + children: [ + { + name: 'testStartTimestamp', + label: 'Test start date', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + }, + { + name: 'testEndTimestamp', + type: FormNodeTypes.CONTROL, + label: 'Test end date', + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + }, + { + name: 'testTypes', + label: 'Test Types', + type: FormNodeTypes.ARRAY, + children: [ + { + name: '0', // it is important here that the name of the node for an ARRAY type should be an index value + type: FormNodeTypes.GROUP, + children: [ + { + name: 'testResult', + label: 'Result', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.RADIO, + options: [ + { value: 'pass', label: 'Pass' }, + { value: 'fail', label: 'Fail' }, + ], + }, + { + name: 'certificateNumber', + label: 'Certificate number', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.TEXT, + width: FormNodeWidth.L, + validators: [{ name: ValidatorNames.Alphanumeric }, { name: ValidatorNames.Required }], + value: null, + }, + { + name: 'testTypeStartTimestamp', + type: FormNodeTypes.CONTROL, + value: '', + label: 'Test start date and time', + viewType: FormNodeViewTypes.TIME, + editType: FormNodeEditTypes.DATETIME, + validators: [ + { name: ValidatorNames.Required }, + { name: ValidatorNames.PastDate }, + { name: ValidatorNames.CopyValueToRootControl, args: 'testStartTimestamp' }, + ], + }, + { + name: 'testTypeEndTimestamp', + type: FormNodeTypes.CONTROL, + value: '', + label: 'Test end date and time', + viewType: FormNodeViewTypes.TIME, + editType: FormNodeEditTypes.DATETIME, + validators: [ + { name: ValidatorNames.Required }, + { name: ValidatorNames.PastDate }, + { name: ValidatorNames.AheadOfDate, args: 'testTypeStartTimestamp' }, + { name: ValidatorNames.CopyValueToRootControl, args: 'testEndTimestamp' }, + ], + }, + ], + }, + ], + }, + ], }; export const AmendDeskBasedTestSectionGroup4LgvCarMotorcycle: FormNode = { - name: 'testSection', - label: 'Test', - type: FormNodeTypes.GROUP, - children: [ - { - name: 'testStartTimestamp', - label: 'Test start date', - type: FormNodeTypes.CONTROL, - value: '', - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - }, - { - name: 'testEndTimestamp', - type: FormNodeTypes.CONTROL, - label: 'Test end date', - value: '', - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - }, - { - name: 'createdAt', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'testTypes', - type: FormNodeTypes.ARRAY, - children: [ - { - name: '0', - type: FormNodeTypes.GROUP, - children: [ - { - name: 'testCode', - label: 'Test Code', - value: '', - disabled: true, - type: FormNodeTypes.CONTROL, - width: FormNodeWidth.XS, - }, - { - name: 'testNumber', - label: 'Test Number', - value: '', - disabled: true, - type: FormNodeTypes.CONTROL, - width: FormNodeWidth.L, - }, - { - name: 'testResult', - label: 'Result', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.RADIO, - options: [ - { value: 'pass', label: 'Pass' }, - { value: 'fail', label: 'Fail' }, - ], - }, - { - name: 'certificateNumber', - type: FormNodeTypes.CONTROL, - label: 'Certificate number', - value: '', - validators: [{ name: ValidatorNames.Alphanumeric }, { name: ValidatorNames.Required }], - width: FormNodeWidth.L, - required: true, - }, - { - name: 'testTypeStartTimestamp', - type: FormNodeTypes.CONTROL, - value: '', - label: 'Test start date and time', - viewType: FormNodeViewTypes.TIME, - editType: FormNodeEditTypes.DATETIME, - validators: [ - { name: ValidatorNames.Required }, - { name: ValidatorNames.PastDate }, - { name: ValidatorNames.CopyValueToRootControl, args: 'testStartTimestamp' }, - ], - }, - { - name: 'testTypeEndTimestamp', - type: FormNodeTypes.CONTROL, - value: '', - label: 'Test end date and time', - viewType: FormNodeViewTypes.TIME, - editType: FormNodeEditTypes.DATETIME, - validators: [ - { name: ValidatorNames.Required }, - { name: ValidatorNames.PastDate }, - { name: ValidatorNames.AheadOfDate, args: 'testTypeStartTimestamp' }, - { name: ValidatorNames.CopyValueToRootControl, args: 'testEndTimestamp' }, - ], - }, - ], - }, - ], - }, - ], + name: 'testSection', + label: 'Test', + type: FormNodeTypes.GROUP, + children: [ + { + name: 'testStartTimestamp', + label: 'Test start date', + type: FormNodeTypes.CONTROL, + value: '', + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + }, + { + name: 'testEndTimestamp', + type: FormNodeTypes.CONTROL, + label: 'Test end date', + value: '', + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + }, + { + name: 'createdAt', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'testTypes', + type: FormNodeTypes.ARRAY, + children: [ + { + name: '0', + type: FormNodeTypes.GROUP, + children: [ + { + name: 'testCode', + label: 'Test Code', + value: '', + disabled: true, + type: FormNodeTypes.CONTROL, + width: FormNodeWidth.XS, + }, + { + name: 'testNumber', + label: 'Test Number', + value: '', + disabled: true, + type: FormNodeTypes.CONTROL, + width: FormNodeWidth.L, + }, + { + name: 'testResult', + label: 'Result', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.RADIO, + options: [ + { value: 'pass', label: 'Pass' }, + { value: 'fail', label: 'Fail' }, + ], + }, + { + name: 'certificateNumber', + type: FormNodeTypes.CONTROL, + label: 'Certificate number', + value: '', + validators: [{ name: ValidatorNames.Alphanumeric }, { name: ValidatorNames.Required }], + width: FormNodeWidth.L, + required: true, + }, + { + name: 'testTypeStartTimestamp', + type: FormNodeTypes.CONTROL, + value: '', + label: 'Test start date and time', + viewType: FormNodeViewTypes.TIME, + editType: FormNodeEditTypes.DATETIME, + validators: [ + { name: ValidatorNames.Required }, + { name: ValidatorNames.PastDate }, + { name: ValidatorNames.CopyValueToRootControl, args: 'testStartTimestamp' }, + ], + }, + { + name: 'testTypeEndTimestamp', + type: FormNodeTypes.CONTROL, + value: '', + label: 'Test end date and time', + viewType: FormNodeViewTypes.TIME, + editType: FormNodeEditTypes.DATETIME, + validators: [ + { name: ValidatorNames.Required }, + { name: ValidatorNames.PastDate }, + { name: ValidatorNames.AheadOfDate, args: 'testTypeStartTimestamp' }, + { name: ValidatorNames.CopyValueToRootControl, args: 'testEndTimestamp' }, + ], + }, + ], + }, + ], + }, + ], }; diff --git a/src/app/forms/templates/test-records/section-templates/test/desk-based/desk-based-test-section-group5-LGV.ts b/src/app/forms/templates/test-records/section-templates/test/desk-based/desk-based-test-section-group5-LGV.ts index 0a5f80bd3d..c3b1b468ba 100644 --- a/src/app/forms/templates/test-records/section-templates/test/desk-based/desk-based-test-section-group5-LGV.ts +++ b/src/app/forms/templates/test-records/section-templates/test/desk-based/desk-based-test-section-group5-LGV.ts @@ -1,234 +1,238 @@ import { ValidatorNames } from '@forms/models/validators.enum'; import { - FormNode, FormNodeEditTypes, FormNodeTypes, FormNodeViewTypes, FormNodeWidth, + FormNode, + FormNodeEditTypes, + FormNodeTypes, + FormNodeViewTypes, + FormNodeWidth, } from '@forms/services/dynamic-form.types'; export const DeskBasedTestSectionLgvGroup5: FormNode = { - name: 'testSection', - label: 'Test', - type: FormNodeTypes.GROUP, - children: [ - { - name: 'testStartTimestamp', - label: 'Test start date', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - validators: [{ name: ValidatorNames.PastDate }], - }, - { - name: 'testEndTimestamp', - type: FormNodeTypes.CONTROL, - label: 'Test end date', - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - validators: [{ name: ValidatorNames.AheadOfDate, args: 'testStartTimestamp' }], - }, - { - name: 'testTypes', - label: 'Test Types', - type: FormNodeTypes.ARRAY, - children: [ - { - name: '0', // it is important here that the name of the node for an ARRAY type should be an index value - type: FormNodeTypes.GROUP, - children: [ - { - name: 'testResult', - label: 'Result', - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.RADIO, - options: [ - { value: 'pass', label: 'Pass' }, - { value: 'fail', label: 'Fail' }, - ], - type: FormNodeTypes.CONTROL, - }, - { - name: 'certificateNumber', - label: 'Certificate number', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.TEXT, - validators: [{ name: ValidatorNames.Required }, { name: ValidatorNames.Alphanumeric }], - viewType: FormNodeViewTypes.HIDDEN, - required: true, - value: null, - width: FormNodeWidth.L, - }, - { - name: 'testTypeStartTimestamp', - type: FormNodeTypes.CONTROL, - value: '', - label: 'Test start date and time', - viewType: FormNodeViewTypes.TIME, - editType: FormNodeEditTypes.DATETIME, - validators: [ - { name: ValidatorNames.Required }, - { name: ValidatorNames.PastDate }, - { name: ValidatorNames.CopyValueToRootControl, args: 'testStartTimestamp' }, - ], - }, - { - name: 'testTypeEndTimestamp', - type: FormNodeTypes.CONTROL, - value: '', - label: 'Test end date and time', - viewType: FormNodeViewTypes.TIME, - editType: FormNodeEditTypes.DATETIME, - validators: [ - { name: ValidatorNames.Required }, - { name: ValidatorNames.PastDate }, - { name: ValidatorNames.AheadOfDate, args: 'testTypeStartTimestamp' }, - { name: ValidatorNames.CopyValueToRootControl, args: 'testEndTimestamp' }, - ], - }, - { - name: 'testExpiryDate', - label: 'Expiry Date', - value: null, - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.DATE, - editType: FormNodeEditTypes.DATE, - validators: [{ name: ValidatorNames.AheadOfDate, args: 'testTypeStartTimestamp' }], - }, - { - name: 'prohibitionIssued', - type: FormNodeTypes.CONTROL, - label: 'Prohibition issued', - value: null, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'reasonForAbandoning', - type: FormNodeTypes.CONTROL, - label: 'Reason for abandoning', - value: null, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'additionalCommentsForAbandon', - type: FormNodeTypes.CONTROL, - value: null, - label: 'Additional details for abandoning', - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - ], - }, - ], - }, - ], + name: 'testSection', + label: 'Test', + type: FormNodeTypes.GROUP, + children: [ + { + name: 'testStartTimestamp', + label: 'Test start date', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + validators: [{ name: ValidatorNames.PastDate }], + }, + { + name: 'testEndTimestamp', + type: FormNodeTypes.CONTROL, + label: 'Test end date', + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + validators: [{ name: ValidatorNames.AheadOfDate, args: 'testStartTimestamp' }], + }, + { + name: 'testTypes', + label: 'Test Types', + type: FormNodeTypes.ARRAY, + children: [ + { + name: '0', // it is important here that the name of the node for an ARRAY type should be an index value + type: FormNodeTypes.GROUP, + children: [ + { + name: 'testResult', + label: 'Result', + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.RADIO, + options: [ + { value: 'pass', label: 'Pass' }, + { value: 'fail', label: 'Fail' }, + ], + type: FormNodeTypes.CONTROL, + }, + { + name: 'certificateNumber', + label: 'Certificate number', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.TEXT, + validators: [{ name: ValidatorNames.Required }, { name: ValidatorNames.Alphanumeric }], + viewType: FormNodeViewTypes.HIDDEN, + required: true, + value: null, + width: FormNodeWidth.L, + }, + { + name: 'testTypeStartTimestamp', + type: FormNodeTypes.CONTROL, + value: '', + label: 'Test start date and time', + viewType: FormNodeViewTypes.TIME, + editType: FormNodeEditTypes.DATETIME, + validators: [ + { name: ValidatorNames.Required }, + { name: ValidatorNames.PastDate }, + { name: ValidatorNames.CopyValueToRootControl, args: 'testStartTimestamp' }, + ], + }, + { + name: 'testTypeEndTimestamp', + type: FormNodeTypes.CONTROL, + value: '', + label: 'Test end date and time', + viewType: FormNodeViewTypes.TIME, + editType: FormNodeEditTypes.DATETIME, + validators: [ + { name: ValidatorNames.Required }, + { name: ValidatorNames.PastDate }, + { name: ValidatorNames.AheadOfDate, args: 'testTypeStartTimestamp' }, + { name: ValidatorNames.CopyValueToRootControl, args: 'testEndTimestamp' }, + ], + }, + { + name: 'testExpiryDate', + label: 'Expiry Date', + value: null, + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.DATE, + editType: FormNodeEditTypes.DATE, + validators: [{ name: ValidatorNames.AheadOfDate, args: 'testTypeStartTimestamp' }], + }, + { + name: 'prohibitionIssued', + type: FormNodeTypes.CONTROL, + label: 'Prohibition issued', + value: null, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'reasonForAbandoning', + type: FormNodeTypes.CONTROL, + label: 'Reason for abandoning', + value: null, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'additionalCommentsForAbandon', + type: FormNodeTypes.CONTROL, + value: null, + label: 'Additional details for abandoning', + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + ], + }, + ], + }, + ], }; export const AmendDeskBasedTestSectionLgvGroup5: FormNode = { - name: 'testSection', - label: 'Test', - type: FormNodeTypes.GROUP, - children: [ - { - name: 'testStartTimestamp', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - }, - { - name: 'testEndTimestamp', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - }, - { - name: 'createdAt', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - { - name: 'testTypes', - label: 'Test Types', - type: FormNodeTypes.ARRAY, - children: [ - { - name: '0', // it is important here that the name of the node for an ARRAY type should be an index value - type: FormNodeTypes.GROUP, - children: [ - { - name: 'testCode', - label: 'Test Code', - value: '', - disabled: true, - type: FormNodeTypes.CONTROL, - width: FormNodeWidth.XS, - }, - { - name: 'testResult', - label: 'Result', - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.RADIO, - options: [ - { value: 'pass', label: 'Pass' }, - { value: 'fail', label: 'Fail' }, - ], - }, - { - name: 'certificateNumber', - label: 'Certificate number', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.TEXT, - width: FormNodeWidth.L, - validators: [{ name: ValidatorNames.Alphanumeric }], - value: null, - }, - { - name: 'testNumber', - value: '', - disabled: true, - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.HIDDEN, - }, - { - name: 'testTypeStartTimestamp', - type: FormNodeTypes.CONTROL, - value: '', - label: 'Test start date and time', - viewType: FormNodeViewTypes.TIME, - editType: FormNodeEditTypes.DATETIME, - validators: [ - { name: ValidatorNames.Required }, - { name: ValidatorNames.PastDate }, - { name: ValidatorNames.CopyValueToRootControl, args: 'testStartTimestamp' }, - ], - }, - { - name: 'testTypeEndTimestamp', - type: FormNodeTypes.CONTROL, - value: '', - label: 'Test end date and time', - viewType: FormNodeViewTypes.TIME, - editType: FormNodeEditTypes.DATETIME, - validators: [ - { name: ValidatorNames.Required }, - { name: ValidatorNames.PastDate }, - { name: ValidatorNames.AheadOfDate, args: 'testTypeStartTimestamp' }, - { name: ValidatorNames.CopyValueToRootControl, args: 'testEndTimestamp' }, - ], - }, - { - name: 'testExpiryDate', - label: 'Expiry Date', - value: null, - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.DATE, - editType: FormNodeEditTypes.DATE, - validators: [{ name: ValidatorNames.AheadOfDate, args: 'testTypeStartTimestamp' }], - }, - ], - }, - ], - }, - ], + name: 'testSection', + label: 'Test', + type: FormNodeTypes.GROUP, + children: [ + { + name: 'testStartTimestamp', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + }, + { + name: 'testEndTimestamp', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + }, + { + name: 'createdAt', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + { + name: 'testTypes', + label: 'Test Types', + type: FormNodeTypes.ARRAY, + children: [ + { + name: '0', // it is important here that the name of the node for an ARRAY type should be an index value + type: FormNodeTypes.GROUP, + children: [ + { + name: 'testCode', + label: 'Test Code', + value: '', + disabled: true, + type: FormNodeTypes.CONTROL, + width: FormNodeWidth.XS, + }, + { + name: 'testResult', + label: 'Result', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.RADIO, + options: [ + { value: 'pass', label: 'Pass' }, + { value: 'fail', label: 'Fail' }, + ], + }, + { + name: 'certificateNumber', + label: 'Certificate number', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.TEXT, + width: FormNodeWidth.L, + validators: [{ name: ValidatorNames.Alphanumeric }], + value: null, + }, + { + name: 'testNumber', + value: '', + disabled: true, + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.HIDDEN, + }, + { + name: 'testTypeStartTimestamp', + type: FormNodeTypes.CONTROL, + value: '', + label: 'Test start date and time', + viewType: FormNodeViewTypes.TIME, + editType: FormNodeEditTypes.DATETIME, + validators: [ + { name: ValidatorNames.Required }, + { name: ValidatorNames.PastDate }, + { name: ValidatorNames.CopyValueToRootControl, args: 'testStartTimestamp' }, + ], + }, + { + name: 'testTypeEndTimestamp', + type: FormNodeTypes.CONTROL, + value: '', + label: 'Test end date and time', + viewType: FormNodeViewTypes.TIME, + editType: FormNodeEditTypes.DATETIME, + validators: [ + { name: ValidatorNames.Required }, + { name: ValidatorNames.PastDate }, + { name: ValidatorNames.AheadOfDate, args: 'testTypeStartTimestamp' }, + { name: ValidatorNames.CopyValueToRootControl, args: 'testEndTimestamp' }, + ], + }, + { + name: 'testExpiryDate', + label: 'Expiry Date', + value: null, + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.DATE, + editType: FormNodeEditTypes.DATE, + validators: [{ name: ValidatorNames.AheadOfDate, args: 'testTypeStartTimestamp' }], + }, + ], + }, + ], + }, + ], }; diff --git a/src/app/forms/templates/test-records/section-templates/test/specialist/old-specialist-test-section-group1.template.ts b/src/app/forms/templates/test-records/section-templates/test/specialist/old-specialist-test-section-group1.template.ts index b39cf61e19..e9b3a4c0d7 100644 --- a/src/app/forms/templates/test-records/section-templates/test/specialist/old-specialist-test-section-group1.template.ts +++ b/src/app/forms/templates/test-records/section-templates/test/specialist/old-specialist-test-section-group1.template.ts @@ -1,176 +1,183 @@ import { AsyncValidatorNames } from '@forms/models/async-validators.enum'; import { ValidatorNames } from '@forms/models/validators.enum'; import { - FormNode, FormNodeEditTypes, FormNodeTypes, FormNodeViewTypes, FormNodeWidth, + FormNode, + FormNodeEditTypes, + FormNodeTypes, + FormNodeViewTypes, + FormNodeWidth, } from '@forms/services/dynamic-form.types'; import { ReferenceDataResourceType } from '@models/reference-data.model'; export const OldIVASpecialistTestSectionGroup1: FormNode = { - name: 'testSection', - label: 'Test', - type: FormNodeTypes.GROUP, - children: [ - { - name: 'createdAt', - label: 'Created', - disabled: true, - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.DATE, - editType: FormNodeEditTypes.HIDDEN, - }, - { - name: 'testStartTimestamp', - label: 'Test Date', - value: '', - disabled: true, - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.DATE, - editType: FormNodeEditTypes.HIDDEN, - }, - { - name: 'testTypes', - label: 'Test Types', - type: FormNodeTypes.ARRAY, - children: [ - { - name: '0', // it is important here that the name of the node for an ARRAY type should be an index value - type: FormNodeTypes.GROUP, - children: [ - { - name: 'testCode', - label: 'Test Code', - value: '', - disabled: true, - type: FormNodeTypes.CONTROL, - width: FormNodeWidth.XS, - editType: FormNodeEditTypes.HIDDEN, - }, - { - name: 'testResult', - label: 'Result', - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.RADIO, - options: [ - { value: 'pass', label: 'Pass' }, - { value: 'fail', label: 'Fail' }, - { value: 'prs', label: 'PRS' }, - { value: 'abandoned', label: 'Abandoned' }, - ], - validators: [ - { name: ValidatorNames.HideIfNotEqual, args: { sibling: 'reasonForAbandoning', value: 'abandoned' } }, - { name: ValidatorNames.HideIfNotEqual, args: { sibling: 'additionalCommentsForAbandon', value: 'abandoned' } }, - ], - asyncValidators: [ - { name: AsyncValidatorNames.ResultDependantOnCustomDefects }, - { - name: AsyncValidatorNames.HideIfEqualsWithCondition, - args: { - sibling: 'certificateNumber', - value: 'fail', - conditions: { field: 'testTypeId', operator: 'equals', value: ['150', '151', '181', '182'] }, - }, - }, - ], - type: FormNodeTypes.CONTROL, - }, - { - name: 'reasonForAbandoning', - type: FormNodeTypes.CONTROL, - label: 'Reason for abandoning', - editType: FormNodeEditTypes.CHECKBOXGROUP, - delimited: { regex: '\\. (? option.value !== EUVehicleCategory.O1 && option.value !== EUVehicleCategory.O2, - ), - width: FormNodeWidth.S, - }, - { - name: 'techRecord_alterationMarker', - label: 'Alteration marker', - value: null, - type: FormNodeTypes.CONTROL, - viewType: FormNodeViewTypes.HIDDEN, - editType: FormNodeEditTypes.RADIO, - options: [ - { value: true, label: 'Yes' }, - { value: false, label: 'No' }, - ], - }, - { - name: 'techRecord_functionCode', - label: 'Function code', - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.HIDDEN, - viewType: FormNodeViewTypes.HIDDEN, - }, - ], + isoDate: false, + }, + { + name: 'techRecord_noOfAxles', + label: 'Number of axles', + value: null, + width: FormNodeWidth.XXS, + type: FormNodeTypes.CONTROL, + disabled: true, + customTags: [{ colour: TagType.RED, label: TagTypeLabels.REQUIRED }], + }, + { + name: 'techRecord_roadFriendly', + label: 'Road friendly suspension', + value: null, + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.RADIO, + options: [ + { value: true, label: 'Yes' }, + { value: false, label: 'No' }, + ], + class: 'flex--half', + customTags: [{ colour: TagType.PURPLE, label: TagTypeLabels.PLATES }], + }, + { + name: 'techRecord_suspensionType', + label: 'Suspension type (optional)', + value: null, + width: FormNodeWidth.L, + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.STRING, + editType: FormNodeEditTypes.SELECT, + options: [ + { value: 'S', label: 'Steel' }, + { value: 'R', label: 'Rubber' }, + { value: 'A', label: 'Air' }, + { value: 'H', label: 'Hydraulic' }, + { value: 'O', label: 'Other' }, + ], + class: 'flex--half', + }, + { + name: 'techRecord_vehicleClass_description', + label: 'Vehicle class', + value: '', + customId: 'vehicleClassDescription', + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.STRING, + editType: FormNodeEditTypes.SELECT, + options: [{ label: 'trailer', value: 'trailer' }], + validators: [{ name: ValidatorNames.Required }], + customTags: [{ colour: TagType.RED, label: TagTypeLabels.REQUIRED }], + }, + { + name: 'techRecord_couplingType', + label: 'Coupling type (optional)', + value: null, + width: FormNodeWidth.XL, + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.SELECT, + options: CouplingTypeOptions, + validators: [{ name: ValidatorNames.MaxLength, args: 1 }], + class: 'flex--half', + }, + { + name: 'techRecord_maxLoadOnCoupling', + label: 'Max load on coupling (optional)', + value: null, + width: FormNodeWidth.M, + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.NUMBER, + validators: [{ name: ValidatorNames.Max, args: 99999 }], + class: 'flex--half', + customTags: [{ colour: TagType.PURPLE, label: TagTypeLabels.PLATES }], + }, + { + name: 'techRecord_vehicleConfiguration', + label: 'Vehicle configuration', + value: null, + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.SELECT, + options: getOptionsFromEnum(VehicleConfiguration), + validators: [{ name: ValidatorNames.UpdateFunctionCode }], + customTags: [ + { colour: TagType.RED, label: TagTypeLabels.REQUIRED }, + { colour: TagType.PURPLE, label: TagTypeLabels.PLATES }, + ], + }, + { + name: 'techRecord_frameDescription', + label: 'Frame description (optional)', + value: null, + width: FormNodeWidth.XL, + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.SELECT, + options: getOptionsFromEnum(FrameDescriptions), + }, + { + name: 'techRecord_departmentalVehicleMarker', + label: 'Departmental vehicle marker', + value: null, + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.RADIO, + options: [ + { value: true, label: 'Yes' }, + { value: false, label: 'No' }, + ], + }, + { + name: 'techRecord_euVehicleCategory', + label: 'EU vehicle category', + value: null, + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.SELECT, + options: getOptionsFromEnum(EUVehicleCategory).filter( + (option) => option.value !== EUVehicleCategory.O1 && option.value !== EUVehicleCategory.O2 + ), + width: FormNodeWidth.S, + }, + { + name: 'techRecord_alterationMarker', + label: 'Alteration marker', + value: null, + type: FormNodeTypes.CONTROL, + viewType: FormNodeViewTypes.HIDDEN, + editType: FormNodeEditTypes.RADIO, + options: [ + { value: true, label: 'Yes' }, + { value: false, label: 'No' }, + ], + }, + { + name: 'techRecord_functionCode', + label: 'Function code', + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.HIDDEN, + viewType: FormNodeViewTypes.HIDDEN, + }, + ], }; diff --git a/src/app/forms/templates/trl/trl-tyres.template.ts b/src/app/forms/templates/trl/trl-tyres.template.ts index 3f4f8715ca..96d7d8a4c4 100644 --- a/src/app/forms/templates/trl/trl-tyres.template.ts +++ b/src/app/forms/templates/trl/trl-tyres.template.ts @@ -1,92 +1,104 @@ import { TyreUseCode } from '@dvsa/cvs-type-definitions/types/v3/tech-record/enums/tyreUseCodeTrl.enum.js'; import { ValidatorNames } from '@forms/models/validators.enum'; import { - FormNode, FormNodeEditTypes, FormNodeTypes, FormNodeWidth, TagTypeLabels, + FormNode, + FormNodeEditTypes, + FormNodeTypes, + FormNodeWidth, + TagTypeLabels, } from '@forms/services/dynamic-form.types'; import { getOptionsFromEnum } from '@forms/utils/enum-map'; import { TagType } from '@shared/components/tag/tag.component'; export const tyresTemplateTrl: FormNode = { - name: 'tyreSection', - type: FormNodeTypes.GROUP, - label: 'Tyres', - children: [ - { - name: 'techRecord_tyreUseCode', - label: 'Tyre use code', - value: null, - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.SELECT, - width: FormNodeWidth.UNSET, - options: getOptionsFromEnum(TyreUseCode), - customTags: [{ colour: TagType.PURPLE, label: TagTypeLabels.PLATES }], - }, - { - name: 'techRecord_axles', - value: '', - type: FormNodeTypes.ARRAY, - children: [ - { - name: '0', - label: 'Axle', - value: '', - type: FormNodeTypes.GROUP, - children: [ - { - name: 'axleNumber', - label: 'Axle Number', - type: FormNodeTypes.CONTROL, - }, + name: 'tyreSection', + type: FormNodeTypes.GROUP, + label: 'Tyres', + children: [ + { + name: 'techRecord_tyreUseCode', + label: 'Tyre use code', + value: null, + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.SELECT, + width: FormNodeWidth.UNSET, + options: getOptionsFromEnum(TyreUseCode), + customTags: [{ colour: TagType.PURPLE, label: TagTypeLabels.PLATES }], + }, + { + name: 'techRecord_axles', + value: '', + type: FormNodeTypes.ARRAY, + children: [ + { + name: '0', + label: 'Axle', + value: '', + type: FormNodeTypes.GROUP, + children: [ + { + name: 'axleNumber', + label: 'Axle Number', + type: FormNodeTypes.CONTROL, + }, - { - name: 'tyres_tyreCode', - label: 'Tyre Code', - value: null, - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.NUMBER, - validators: [{ name: ValidatorNames.Numeric }, { name: ValidatorNames.Max, args: 99999 }, { name: ValidatorNames.Min, args: 0 }], - }, - { - name: 'tyres_tyreSize', - label: 'Tyre Size', - value: null, - type: FormNodeTypes.CONTROL, - disabled: true, - validators: [ - { name: ValidatorNames.MaxLength, args: 12 }, - { name: ValidatorNames.Min, args: 0 }, - ], - }, - { - name: 'tyres_plyRating', - label: 'Ply Rating', - value: null, - type: FormNodeTypes.CONTROL, - disabled: true, - validators: [ - { name: ValidatorNames.MaxLength, args: 2 }, - { name: ValidatorNames.Min, args: 0 }, - ], - }, - { - name: 'tyres_fitmentCode', - label: 'Fitment code', - value: null, - type: FormNodeTypes.CONTROL, - validators: [], - }, - { - name: 'tyres_dataTrAxles', - label: 'Load index', - value: null, - type: FormNodeTypes.CONTROL, - editType: FormNodeEditTypes.NUMBER, - disabled: true, - validators: [{ name: ValidatorNames.Numeric }, { name: ValidatorNames.Max, args: 999 }, { name: ValidatorNames.Min, args: 0 }], - }, - ], - }, - ], - }, - ], + { + name: 'tyres_tyreCode', + label: 'Tyre Code', + value: null, + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.NUMBER, + validators: [ + { name: ValidatorNames.Numeric }, + { name: ValidatorNames.Max, args: 99999 }, + { name: ValidatorNames.Min, args: 0 }, + ], + }, + { + name: 'tyres_tyreSize', + label: 'Tyre Size', + value: null, + type: FormNodeTypes.CONTROL, + disabled: true, + validators: [ + { name: ValidatorNames.MaxLength, args: 12 }, + { name: ValidatorNames.Min, args: 0 }, + ], + }, + { + name: 'tyres_plyRating', + label: 'Ply Rating', + value: null, + type: FormNodeTypes.CONTROL, + disabled: true, + validators: [ + { name: ValidatorNames.MaxLength, args: 2 }, + { name: ValidatorNames.Min, args: 0 }, + ], + }, + { + name: 'tyres_fitmentCode', + label: 'Fitment code', + value: null, + type: FormNodeTypes.CONTROL, + validators: [], + }, + { + name: 'tyres_dataTrAxles', + label: 'Load index', + value: null, + type: FormNodeTypes.CONTROL, + editType: FormNodeEditTypes.NUMBER, + disabled: true, + validators: [ + { name: ValidatorNames.Numeric }, + { name: ValidatorNames.Max, args: 999 }, + { name: ValidatorNames.Min, args: 0 }, + ], + }, + ], + }, + ], + }, + ], }; diff --git a/src/app/forms/templates/trl/trl-weight.template.ts b/src/app/forms/templates/trl/trl-weight.template.ts index 0677d746ba..5db450a1a2 100644 --- a/src/app/forms/templates/trl/trl-weight.template.ts +++ b/src/app/forms/templates/trl/trl-weight.template.ts @@ -2,104 +2,104 @@ import { ValidatorNames } from '@forms/models/validators.enum'; import { FormNode, FormNodeEditTypes, FormNodeTypes } from '../../services/dynamic-form.types'; const requiredValidation = [ - { name: ValidatorNames.Numeric, args: 99999 }, - { name: ValidatorNames.Max, args: 99999 }, - { name: ValidatorNames.Min, args: 0 }, + { name: ValidatorNames.Numeric, args: 99999 }, + { name: ValidatorNames.Max, args: 99999 }, + { name: ValidatorNames.Min, args: 0 }, ]; const optionalValidation = [ - { name: ValidatorNames.Numeric, args: 99999 }, - { name: ValidatorNames.Max, args: 99999 }, - { name: ValidatorNames.Min, args: 0 }, + { name: ValidatorNames.Numeric, args: 99999 }, + { name: ValidatorNames.Max, args: 99999 }, + { name: ValidatorNames.Min, args: 0 }, ]; export const TrlWeight: FormNode = { - name: 'weightsSection', - label: 'Weights', - type: FormNodeTypes.GROUP, - children: [ - { - name: 'grossSection', - label: 'Gross vehicle weight', - value: '', - type: FormNodeTypes.SECTION, - }, - { - name: 'techRecord_grossGbWeight', - label: 'GB', - customValidatorErrorName: 'Gross GB Weight', - value: null, - type: FormNodeTypes.CONTROL, - validators: requiredValidation, - }, - { - name: 'techRecord_grossEecWeight', - label: 'EEC (optional)', - customValidatorErrorName: 'Gross EEC Weight', - value: null, - editType: FormNodeEditTypes.NUMBER, - type: FormNodeTypes.CONTROL, - validators: optionalValidation, - }, - { - name: 'techRecord_grossDesignWeight', - label: 'Design', - customValidatorErrorName: 'Gross Design Weight', - value: null, - type: FormNodeTypes.CONTROL, - validators: requiredValidation, - }, - { - name: 'axleSection', - label: 'Axle weights', - value: '', - type: FormNodeTypes.SECTION, - }, - { - name: 'techRecord_axles', - value: '', - type: FormNodeTypes.ARRAY, - children: [ - { - name: '0', - label: 'Axle', - value: '', - type: FormNodeTypes.GROUP, - children: [ - { - name: 'axleNumber', - label: 'Axle Number', - type: FormNodeTypes.CONTROL, - }, + name: 'weightsSection', + label: 'Weights', + type: FormNodeTypes.GROUP, + children: [ + { + name: 'grossSection', + label: 'Gross vehicle weight', + value: '', + type: FormNodeTypes.SECTION, + }, + { + name: 'techRecord_grossGbWeight', + label: 'GB', + customValidatorErrorName: 'Gross GB Weight', + value: null, + type: FormNodeTypes.CONTROL, + validators: requiredValidation, + }, + { + name: 'techRecord_grossEecWeight', + label: 'EEC (optional)', + customValidatorErrorName: 'Gross EEC Weight', + value: null, + editType: FormNodeEditTypes.NUMBER, + type: FormNodeTypes.CONTROL, + validators: optionalValidation, + }, + { + name: 'techRecord_grossDesignWeight', + label: 'Design', + customValidatorErrorName: 'Gross Design Weight', + value: null, + type: FormNodeTypes.CONTROL, + validators: requiredValidation, + }, + { + name: 'axleSection', + label: 'Axle weights', + value: '', + type: FormNodeTypes.SECTION, + }, + { + name: 'techRecord_axles', + value: '', + type: FormNodeTypes.ARRAY, + children: [ + { + name: '0', + label: 'Axle', + value: '', + type: FormNodeTypes.GROUP, + children: [ + { + name: 'axleNumber', + label: 'Axle Number', + type: FormNodeTypes.CONTROL, + }, - { - name: 'weights_gbWeight', - label: 'GB weight', - customValidatorErrorName: 'Axle GB Weight', - value: null, - type: FormNodeTypes.CONTROL, - validators: requiredValidation, - }, - { - name: 'weights_eecWeight', - label: 'EEC (optional)', - customValidatorErrorName: 'Axle EEC Weight', - value: null, - editType: FormNodeEditTypes.NUMBER, - type: FormNodeTypes.CONTROL, - validators: optionalValidation, - }, - { - name: 'weights_designWeight', - label: 'Design weight', - customValidatorErrorName: 'Axle Design Weight', - value: null, - type: FormNodeTypes.CONTROL, - validators: requiredValidation, - }, - ], - }, - ], - }, - ], + { + name: 'weights_gbWeight', + label: 'GB weight', + customValidatorErrorName: 'Axle GB Weight', + value: null, + type: FormNodeTypes.CONTROL, + validators: requiredValidation, + }, + { + name: 'weights_eecWeight', + label: 'EEC (optional)', + customValidatorErrorName: 'Axle EEC Weight', + value: null, + editType: FormNodeEditTypes.NUMBER, + type: FormNodeTypes.CONTROL, + validators: optionalValidation, + }, + { + name: 'weights_designWeight', + label: 'Design weight', + customValidatorErrorName: 'Axle Design Weight', + value: null, + type: FormNodeTypes.CONTROL, + validators: requiredValidation, + }, + ], + }, + ], + }, + ], }; diff --git a/src/app/forms/utils/enum-map.ts b/src/app/forms/utils/enum-map.ts index 560afc5839..d58ac8ff13 100644 --- a/src/app/forms/utils/enum-map.ts +++ b/src/app/forms/utils/enum-map.ts @@ -1,14 +1,14 @@ import { MultiOptions } from '@forms/models/options.model'; export function getOptionsFromEnum(object: object): MultiOptions { - // eslint-disable-next-line @typescript-eslint/restrict-plus-operands - return Object.values(object).map((value) => ({ value, label: value.charAt(0).toUpperCase() + value.slice(1) })); + // eslint-disable-next-line @typescript-eslint/restrict-plus-operands + return Object.values(object).map((value) => ({ value, label: value.charAt(0).toUpperCase() + value.slice(1) })); } export function getOptionsFromEnumOneChar(object: object): MultiOptions { - return Object.values(object).map((value) => ({ value, label: value.charAt(0).toUpperCase() })); + return Object.values(object).map((value) => ({ value, label: value.charAt(0).toUpperCase() })); } export function getOptionsFromEnumAcronym(object: object): MultiOptions { - return Object.values(object).map((value) => ({ value, label: value.toUpperCase() })); + return Object.values(object).map((value) => ({ value, label: value.toUpperCase() })); } diff --git a/src/app/forms/utils/error-message-map.spec.ts b/src/app/forms/utils/error-message-map.spec.ts index 66b2c8dca8..2be790f88e 100644 --- a/src/app/forms/utils/error-message-map.spec.ts +++ b/src/app/forms/utils/error-message-map.spec.ts @@ -3,53 +3,69 @@ import { ValidatorNames } from '@forms/models/validators.enum'; import { ErrorMessageMap } from './error-message-map'; describe('ErrorMessageMap', () => { - it.each([ - ['This field is required', ValidatorNames.Required, [true, '']], - ['This field is required', ValidatorNames.Required, [true, undefined]], - ['This field is required', ValidatorNames.Required, [true, null]], - ['Name is required', ValidatorNames.Required, [true, 'Name']], - ['This field must match a pattern', ValidatorNames.Pattern, [true, '']], - ['Name must match a pattern', ValidatorNames.Pattern, [true, 'Name']], - ['This field must match pattern xxx', ValidatorNames.CustomPattern, [{ message: 'must match pattern xxx' }, '']], - ['Name must match pattern xxx', ValidatorNames.CustomPattern, [{ message: 'must match pattern xxx' }, 'Name']], - ['\'Date\' is not valid', 'invalidDate', [{ error: true, reason: '\'Date\' is not valid' }]], - ['Name must be less than or equal to 14 characters', ValidatorNames.MaxLength, [{ requiredLength: 14 }, 'Name']], - ['This field must be less than or equal to 14 characters', ValidatorNames.MaxLength, [{ requiredLength: 14 }, '']], - ['Name must be greater than or equal to 14 characters', ValidatorNames.MinLength, [{ requiredLength: 14 }, 'Name']], - ['This field must be greater than or equal to 14 characters', ValidatorNames.MinLength, [{ requiredLength: 14 }, '']], - ['Name is required with Surname', ValidatorNames.RequiredIfEquals, [{ sibling: 'Surname' }, 'Name']], - ['This field is required with Surname', ValidatorNames.RequiredIfEquals, [{ sibling: 'Surname' }, '']], - ['Notes is required', ValidatorNames.ValidateDefectNotes, undefined], - ['foo', 'invalidTestResult', [{ message: 'foo' }]], - ['This field must be less than or equal to 5', ValidatorNames.Max, [{ max: 5 }, '']], - ['Number must be less than or equal to 5', ValidatorNames.Max, [{ max: 5 }, 'Number']], - ['This field must be greater than or equal to 5', ValidatorNames.Min, [{ min: 5 }, '']], - ['Number must be greater than or equal to 5', ValidatorNames.Min, [{ min: 5 }, 'Number']], - ['Date must be in the past', ValidatorNames.PastDate, [true, 'Date']], - ['This date must be in the past', ValidatorNames.PastDate, [true, undefined]], - ['Date must be in the future', ValidatorNames.FutureDate, [true, 'Date']], - ['This date must be in the future', ValidatorNames.FutureDate, [true, undefined]], - ['This date must be ahead of the previous date', ValidatorNames.AheadOfDate, [true, undefined]], - ['This year must be the current or a past year', ValidatorNames.PastYear, [true, undefined]], - ['bar must be ahead of foo (20/01/2021)', ValidatorNames.AheadOfDate, [{ sibling: 'foo', date: new Date('2021-01-20T00:00:00.000Z') }, 'bar']], - ['This field is required', AsyncValidatorNames.RequiredIfNotFail, [{ sibling: 'foo' }, '']], - ['Name is required', AsyncValidatorNames.RequiredIfNotFail, [{ sibling: 'foo' }, 'Name']], - ['Prohibition notice has not been issued.', ValidatorNames.ValidateProhibitionIssued, undefined], - ['This field is required', AsyncValidatorNames.RequiredIfNotAbandoned, [{ sibling: 'foo' }, '']], - ['Name is required', AsyncValidatorNames.RequiredIfNotAbandoned, [{ sibling: 'foo' }, 'Name']], - ['This field is required', AsyncValidatorNames.RequiredIfNotResult, [{ sibling: 'foo' }, '']], - ['Name is required', AsyncValidatorNames.RequiredIfNotResult, [{ sibling: 'foo' }, 'Name']], - ['This field is required', AsyncValidatorNames.RequiredIfNotResultAndSiblingEquals, [{ sibling: 'foo' }, '']], - ['Name is required', AsyncValidatorNames.RequiredIfNotResultAndSiblingEquals, [{ sibling: 'foo' }, 'Name']], - ['This date must be less than 10 months after the previous date', ValidatorNames.DateNotExceed, [{ months: '10' }, '']], - ['Name must be less than 15 months after foo', ValidatorNames.DateNotExceed, [{ sibling: 'foo', months: '15' }, 'Name']], - ])('should return "%s" for %s with %o', (expected, key, props) => { - if (props) { - // eslint-disable-next-line jest/no-conditional-expect - expect(ErrorMessageMap[`${key}`](...props)).toBe(expected); - } else { - // eslint-disable-next-line jest/no-conditional-expect - expect(ErrorMessageMap[`${key}`]()).toBe(expected); - } - }); + it.each([ + ['This field is required', ValidatorNames.Required, [true, '']], + ['This field is required', ValidatorNames.Required, [true, undefined]], + ['This field is required', ValidatorNames.Required, [true, null]], + ['Name is required', ValidatorNames.Required, [true, 'Name']], + ['This field must match a pattern', ValidatorNames.Pattern, [true, '']], + ['Name must match a pattern', ValidatorNames.Pattern, [true, 'Name']], + ['This field must match pattern xxx', ValidatorNames.CustomPattern, [{ message: 'must match pattern xxx' }, '']], + ['Name must match pattern xxx', ValidatorNames.CustomPattern, [{ message: 'must match pattern xxx' }, 'Name']], + ["'Date' is not valid", 'invalidDate', [{ error: true, reason: "'Date' is not valid" }]], + ['Name must be less than or equal to 14 characters', ValidatorNames.MaxLength, [{ requiredLength: 14 }, 'Name']], + ['This field must be less than or equal to 14 characters', ValidatorNames.MaxLength, [{ requiredLength: 14 }, '']], + ['Name must be greater than or equal to 14 characters', ValidatorNames.MinLength, [{ requiredLength: 14 }, 'Name']], + [ + 'This field must be greater than or equal to 14 characters', + ValidatorNames.MinLength, + [{ requiredLength: 14 }, ''], + ], + ['Name is required with Surname', ValidatorNames.RequiredIfEquals, [{ sibling: 'Surname' }, 'Name']], + ['This field is required with Surname', ValidatorNames.RequiredIfEquals, [{ sibling: 'Surname' }, '']], + ['Notes is required', ValidatorNames.ValidateDefectNotes, undefined], + ['foo', 'invalidTestResult', [{ message: 'foo' }]], + ['This field must be less than or equal to 5', ValidatorNames.Max, [{ max: 5 }, '']], + ['Number must be less than or equal to 5', ValidatorNames.Max, [{ max: 5 }, 'Number']], + ['This field must be greater than or equal to 5', ValidatorNames.Min, [{ min: 5 }, '']], + ['Number must be greater than or equal to 5', ValidatorNames.Min, [{ min: 5 }, 'Number']], + ['Date must be in the past', ValidatorNames.PastDate, [true, 'Date']], + ['This date must be in the past', ValidatorNames.PastDate, [true, undefined]], + ['Date must be in the future', ValidatorNames.FutureDate, [true, 'Date']], + ['This date must be in the future', ValidatorNames.FutureDate, [true, undefined]], + ['This date must be ahead of the previous date', ValidatorNames.AheadOfDate, [true, undefined]], + ['This year must be the current or a past year', ValidatorNames.PastYear, [true, undefined]], + [ + 'bar must be ahead of foo (20/01/2021)', + ValidatorNames.AheadOfDate, + [{ sibling: 'foo', date: new Date('2021-01-20T00:00:00.000Z') }, 'bar'], + ], + ['This field is required', AsyncValidatorNames.RequiredIfNotFail, [{ sibling: 'foo' }, '']], + ['Name is required', AsyncValidatorNames.RequiredIfNotFail, [{ sibling: 'foo' }, 'Name']], + ['Prohibition notice has not been issued.', ValidatorNames.ValidateProhibitionIssued, undefined], + ['This field is required', AsyncValidatorNames.RequiredIfNotAbandoned, [{ sibling: 'foo' }, '']], + ['Name is required', AsyncValidatorNames.RequiredIfNotAbandoned, [{ sibling: 'foo' }, 'Name']], + ['This field is required', AsyncValidatorNames.RequiredIfNotResult, [{ sibling: 'foo' }, '']], + ['Name is required', AsyncValidatorNames.RequiredIfNotResult, [{ sibling: 'foo' }, 'Name']], + ['This field is required', AsyncValidatorNames.RequiredIfNotResultAndSiblingEquals, [{ sibling: 'foo' }, '']], + ['Name is required', AsyncValidatorNames.RequiredIfNotResultAndSiblingEquals, [{ sibling: 'foo' }, 'Name']], + [ + 'This date must be less than 10 months after the previous date', + ValidatorNames.DateNotExceed, + [{ months: '10' }, ''], + ], + [ + 'Name must be less than 15 months after foo', + ValidatorNames.DateNotExceed, + [{ sibling: 'foo', months: '15' }, 'Name'], + ], + ])('should return "%s" for %s with %o', (expected, key, props) => { + if (props) { + // @ts-ignore + expect(ErrorMessageMap[`${key}`](...props)).toBe(expected); + } else { + // @ts-ignore + expect(ErrorMessageMap[`${key}`]()).toBe(expected); + } + }); }); diff --git a/src/app/forms/utils/error-message-map.ts b/src/app/forms/utils/error-message-map.ts index 8318a8007f..9f301062a7 100644 --- a/src/app/forms/utils/error-message-map.ts +++ b/src/app/forms/utils/error-message-map.ts @@ -4,51 +4,63 @@ import { ValidatorNames } from '@forms/models/validators.enum'; const DEFAULT_LABEL = 'This field'; export const ErrorMessageMap: Record = { - // Date errors - invalidDate: (err: { error: boolean; reason: string; index: number }) => `${err.reason}`, - invalidOption: (err: boolean, label?: string) => `${label || DEFAULT_LABEL} is invalid`, - invalidTestResult: (err: { message: string }) => err.message, + // Date errors + invalidDate: (err: { error: boolean; reason: string; index: number }) => `${err.reason}`, + invalidOption: (err: boolean, label?: string) => `${label || DEFAULT_LABEL} is invalid`, + invalidTestResult: (err: { message: string }) => err.message, - validateVin: (error: { message: string }) => error.message, - validateForBatch: (error: { message: string }) => error.message, - validateVrm: (error: { message: string }) => error.message, + validateVin: (error: { message: string }) => error.message, + validateForBatch: (error: { message: string }) => error.message, + validateVrm: (error: { message: string }) => error.message, - [ValidatorNames.AheadOfDate]: (err: { sibling: string; date: Date }, label?: string) => - `${label || 'This date'} must be ahead of ${err.sibling || 'the previous date'}${err.date ? formatDate(err.date, ' (dd/MM/yyyy)', 'en') : ''}`, - [ValidatorNames.CustomPattern]: (err: { message: string }, label?: string) => `${label || DEFAULT_LABEL} ${err.message}`, - [ValidatorNames.DateNotExceed]: (err: { sibling: string; months: number }, label?: string) => - `${label || 'This date'} must be less than ${err.months || 'X'} months after ${err.sibling || 'the previous date'}`, - [ValidatorNames.Defined]: (err: boolean, label?: string) => `${label} is required`, - [ValidatorNames.FutureDate]: (err: boolean, label?: string) => `${label || 'This date'} must be in the future`, - [ValidatorNames.PastYear]: (err: boolean, label?: string) => `${label || 'This year'} must be the current or a past year`, - [ValidatorNames.Max]: (err: { max: number }, label?: string) => `${label || DEFAULT_LABEL} must be less than or equal to ${err.max}`, - [ValidatorNames.MaxLength]: (err: { requiredLength: number }, label?: string) => - `${label || DEFAULT_LABEL} must be less than or equal to ${err.requiredLength} characters`, - [ValidatorNames.Min]: (err: { min: number }, label?: string) => `${label || DEFAULT_LABEL} must be greater than or equal to ${err.min}`, - [ValidatorNames.MinLength]: (err: { requiredLength: number }, label?: string) => - `${label || DEFAULT_LABEL} must be greater than or equal to ${err.requiredLength} characters`, - [ValidatorNames.NotZNumber]: () => 'The VRM/Trailer ID cannot be in a format that is 7 digits followed by the character \'Z\'', - [ValidatorNames.PastDate]: (err: boolean, label?: string) => `${label || 'This date'} must be in the past`, - [ValidatorNames.Pattern]: (err: boolean, label?: string) => `${label || DEFAULT_LABEL} must match a pattern`, - [ValidatorNames.Required]: (err: boolean, label?: string) => `${label || DEFAULT_LABEL} is required`, - [ValidatorNames.RequiredIfEquals]: (err: { sibling: string }, label?: string) => `${label || DEFAULT_LABEL} is required with ${err.sibling}`, - [ValidatorNames.RequiredIfNotEquals]: (err: { sibling: string }, label?: string) => `${label || DEFAULT_LABEL} is required with ${err.sibling}`, - [ValidatorNames.requiredIfAllEquals]: (err: { sibling: string }, label?: string) => `${label || DEFAULT_LABEL} is required with ${err.sibling}`, - [ValidatorNames.RequiredIfNotHidden]: (label?: string) => `${label || DEFAULT_LABEL} is required`, - [ValidatorNames.ValidateDefectNotes]: () => 'Notes is required', - [ValidatorNames.ValidateProhibitionIssued]: () => 'Prohibition notice has not been issued.', - [ValidatorNames.ValidateVRMTrailerIdLength]: (err: { message: string }) => err.message, - [ValidatorNames.MustEqualSibling]: (err: { sibling: string }, label?: string) => `${label || DEFAULT_LABEL} must match ${err.sibling}`, - [ValidatorNames.IsMemberOfEnum]: (err: boolean, label?: string) => `${label || DEFAULT_LABEL} is required`, - [ValidatorNames.IsArray]: (err: { message: string }, label?: string) => `${label || DEFAULT_LABEL}`, - [ValidatorNames.Tc3TestValidator]: (err: { message: string }) => `${err.message}`, - [ValidatorNames.DateIsInvalid]: (err: { message?: string }, label?: string) => err.message ?? `${label || DEFAULT_LABEL} is invalid`, - [ValidatorNames.Custom]: (err: { message: string }) => err.message, - [ValidatorNames.MinArrayLengthIfNotEmpty]: (err: { message: string }) => err.message, + [ValidatorNames.AheadOfDate]: (err: { sibling: string; date: Date }, label?: string) => + `${label || 'This date'} must be ahead of ${err.sibling || 'the previous date'}${err.date ? formatDate(err.date, ' (dd/MM/yyyy)', 'en') : ''}`, + [ValidatorNames.CustomPattern]: (err: { message: string }, label?: string) => + `${label || DEFAULT_LABEL} ${err.message}`, + [ValidatorNames.DateNotExceed]: (err: { sibling: string; months: number }, label?: string) => + `${label || 'This date'} must be less than ${err.months || 'X'} months after ${err.sibling || 'the previous date'}`, + [ValidatorNames.Defined]: (err: boolean, label?: string) => `${label} is required`, + [ValidatorNames.FutureDate]: (err: boolean, label?: string) => `${label || 'This date'} must be in the future`, + [ValidatorNames.PastYear]: (err: boolean, label?: string) => + `${label || 'This year'} must be the current or a past year`, + [ValidatorNames.Max]: (err: { max: number }, label?: string) => + `${label || DEFAULT_LABEL} must be less than or equal to ${err.max}`, + [ValidatorNames.MaxLength]: (err: { requiredLength: number }, label?: string) => + `${label || DEFAULT_LABEL} must be less than or equal to ${err.requiredLength} characters`, + [ValidatorNames.Min]: (err: { min: number }, label?: string) => + `${label || DEFAULT_LABEL} must be greater than or equal to ${err.min}`, + [ValidatorNames.MinLength]: (err: { requiredLength: number }, label?: string) => + `${label || DEFAULT_LABEL} must be greater than or equal to ${err.requiredLength} characters`, + [ValidatorNames.NotZNumber]: () => + "The VRM/Trailer ID cannot be in a format that is 7 digits followed by the character 'Z'", + [ValidatorNames.PastDate]: (err: boolean, label?: string) => `${label || 'This date'} must be in the past`, + [ValidatorNames.Pattern]: (err: boolean, label?: string) => `${label || DEFAULT_LABEL} must match a pattern`, + [ValidatorNames.Required]: (err: boolean, label?: string) => `${label || DEFAULT_LABEL} is required`, + [ValidatorNames.RequiredIfEquals]: (err: { sibling: string; customErrorMessage: string }, label?: string) => + err.customErrorMessage ?? `${label || DEFAULT_LABEL} is required with ${err.sibling}`, + [ValidatorNames.RequiredIfNotEquals]: (err: { sibling: string }, label?: string) => + `${label || DEFAULT_LABEL} is required with ${err.sibling}`, + [ValidatorNames.requiredIfAllEquals]: (err: { sibling: string }, label?: string) => + `${label || DEFAULT_LABEL} is required with ${err.sibling}`, + [ValidatorNames.RequiredIfNotHidden]: (label?: string) => `${label || DEFAULT_LABEL} is required`, + [ValidatorNames.ValidateDefectNotes]: () => 'Notes is required', + [ValidatorNames.ValidateProhibitionIssued]: () => 'Prohibition notice has not been issued.', + [ValidatorNames.ValidateVRMTrailerIdLength]: (err: { message: string }) => err.message, + [ValidatorNames.MustEqualSibling]: (err: { sibling: string }, label?: string) => + `${label || DEFAULT_LABEL} must match ${err.sibling}`, + [ValidatorNames.IsMemberOfEnum]: (err: boolean, label?: string) => `${label || DEFAULT_LABEL} is required`, + [ValidatorNames.IsArray]: (err: { message: string }, label?: string) => `${label || DEFAULT_LABEL}`, + [ValidatorNames.Tc3TestValidator]: (err: { message: string }) => `${err.message}`, + [ValidatorNames.DateIsInvalid]: (err: { message?: string }, label?: string) => + err.message ?? `${label || DEFAULT_LABEL} is invalid`, + [ValidatorNames.Custom]: (err: { message: string }) => err.message, + [ValidatorNames.MinArrayLengthIfNotEmpty]: (err: { message: string }) => err.message, - [AsyncValidatorNames.RequiredIfNotAbandoned]: (err: boolean, label?: string) => `${label || DEFAULT_LABEL} is required`, - [AsyncValidatorNames.RequiredIfNotFail]: (err: boolean, label?: string) => `${label || DEFAULT_LABEL} is required`, - [AsyncValidatorNames.RequiredIfNotResult]: (err: boolean, label?: string) => `${label || DEFAULT_LABEL} is required`, - [AsyncValidatorNames.RequiredIfNotResultAndSiblingEquals]: (err: boolean, label?: string) => `${label || DEFAULT_LABEL} is required`, - [AsyncValidatorNames.RequiredWhenCarryingDangerousGoods]: (err: { message: string }) => err.message, + [AsyncValidatorNames.RequiredIfNotAbandoned]: (err: boolean, label?: string) => + `${label || DEFAULT_LABEL} is required`, + [AsyncValidatorNames.RequiredIfNotFail]: (err: boolean, label?: string) => `${label || DEFAULT_LABEL} is required`, + [AsyncValidatorNames.RequiredIfNotResult]: (err: boolean, label?: string) => `${label || DEFAULT_LABEL} is required`, + [AsyncValidatorNames.RequiredIfNotResultAndSiblingEquals]: (err: boolean, label?: string) => + `${label || DEFAULT_LABEL} is required`, + [AsyncValidatorNames.RequiredWhenCarryingDangerousGoods]: (err: { message: string }) => err.message, }; diff --git a/src/app/forms/utils/tech-record-constants.ts b/src/app/forms/utils/tech-record-constants.ts index 6a43047c6d..dacd898c42 100644 --- a/src/app/forms/utils/tech-record-constants.ts +++ b/src/app/forms/utils/tech-record-constants.ts @@ -26,6 +26,8 @@ import { PsvTyresTemplate } from '@forms/templates/psv/psv-tyres.template'; import { PsvWeightsTemplate } from '@forms/templates/psv/psv-weight.template'; import { SmallTrailerTechRecord } from '@forms/templates/small-trailer/small-trailer-tech-record.template'; +import { AdrCertificateTemplate } from '@forms/templates/general/adr-certificate.template'; +import { AdrTemplate } from '@forms/templates/general/adr.template'; import { TechRecordReasonForCreationSection } from '@forms/templates/general/reason-for-creation.template'; import { TrlAuthIntoServiceTemplate } from '@forms/templates/trl/trl-auth-into-service.template'; import { TrlBrakesTemplate } from '@forms/templates/trl/trl-brakes.template'; @@ -35,93 +37,109 @@ import { TrlTechRecordTemplate } from '@forms/templates/trl/trl-tech-record.temp import { tyresTemplateTrl } from '@forms/templates/trl/trl-tyres.template'; import { TrlWeight } from '@forms/templates/trl/trl-weight.template'; import { VehicleTypes } from '@models/vehicle-tech-record.model'; -import { AdrTemplate } from '@forms/templates/general/adr.template'; -import { AdrCertificateTemplate } from '@forms/templates/general/adr-certificate.template'; // The map below initializes the array of sections that the *ngFor in tech summary component's template will iterate over. // The order in which each section is introduced in the array will determine its order on the page when rendered. // Sections which use custom components require a FormNode object with 'name' and 'label' properties. export const vehicleTemplateMap = new Map>([ - [ - VehicleTypes.PSV, - [ - /* 1 */ TechRecordReasonForCreationSection, - /* 2 */ PsvNotes, - /* 3 */ PsvTechRecord, - /* 4 */ PsvTypeApprovalTemplate, - /* 5 */ PsvBrakesTemplate, - /* 6 */ PsvDdaTemplate, - /* 7 */ DocumentsTemplate, - /* 8 */ PsvBodyTemplate, - /* 9 */ PsvWeightsTemplate, - /* 10 */ PsvTyresTemplate, - /* 11 */ PsvDimensionsTemplate, - ], - ], - [ - VehicleTypes.HGV, - [ - /* 1 */ TechRecordReasonForCreationSection, - /* 2 */ NotesTemplate, - /* 3 */ HgvTechRecord, - /* 4 */ HgvAndTrlTypeApprovalTemplate, - /* 5 */ ApplicantDetails, - /* 6 */ DocumentsTemplate, - /* 7 */ HgvAndTrlBodyTemplate, - /* 8 */ HgvWeight, - /* 9 */ tyresTemplateHgv, - /* 10 */ HgvDimensionsTemplate, - /* 11 */ PlatesTemplate, - /* 12 */ AdrTemplate, - /* 13 */ AdrCertificateTemplate, - ], - ], - [ - VehicleTypes.TRL, - [ - /* 1 */ TechRecordReasonForCreationSection, - /* 2 */ NotesTemplate, - /* 3 */ TrlTechRecordTemplate, - /* 4 */ HgvAndTrlTypeApprovalTemplate, - /* 5 */ ApplicantDetails, - /* 6 */ DocumentsTemplate, - /* 7 */ LettersTemplate, - /* 8 */ HgvAndTrlBodyTemplate, - /* 9 */ TrlWeight, - /* 10 */ tyresTemplateTrl, - /* 11 */ TrlBrakesTemplate, - /* 12 */ TrlPurchasers, - /* 13 */ TrlDimensionsTemplate, - /* 14 */ PlatesTemplate, - /* 15 */ TrlAuthIntoServiceTemplate, - /* 16 */ ManufacturerTemplate, - /* 17 */ AdrTemplate, - /* 18 */ AdrCertificateTemplate, - ], - ], - [ - VehicleTypes.SMALL_TRL, - [TechRecordReasonForCreationSection, /* 2 */ SmallTrailerTechRecord, /* 3 */ ApplicantDetails, /* 4 */ NotesTemplate, /* 5 */ Audit], - ], - [ - VehicleTypes.LGV, - [ - /* 1 */TechRecordReasonForCreationSection, - /* 2 */ LgvTechRecord, - /* 3 */ ApplicantDetails, - /* 4 */ NotesTemplate, - /* 5 */ Audit, - /* 6 */ AdrTemplate, - /* 7 */ AdrCertificateTemplate, - ], - ], - [ - VehicleTypes.CAR, - [TechRecordReasonForCreationSection, /* 2 */ CarTechRecord, /* 3 */ ApplicantDetails, /* 4 */ NotesTemplate, /* 5 */ Audit], - ], - [ - VehicleTypes.MOTORCYCLE, - [TechRecordReasonForCreationSection, /* 2 */ MotorcycleTechRecord, /* 3 */ ApplicantDetails, /* 4 */ NotesTemplate, /* 5 */ Audit], - ], + [ + VehicleTypes.PSV, + [ + /* 1 */ TechRecordReasonForCreationSection, + /* 2 */ PsvNotes, + /* 3 */ PsvTechRecord, + /* 4 */ PsvTypeApprovalTemplate, + /* 5 */ PsvBrakesTemplate, + /* 6 */ PsvDdaTemplate, + /* 7 */ DocumentsTemplate, + /* 8 */ PsvBodyTemplate, + /* 9 */ PsvWeightsTemplate, + /* 10 */ PsvTyresTemplate, + /* 11 */ PsvDimensionsTemplate, + ], + ], + [ + VehicleTypes.HGV, + [ + /* 1 */ TechRecordReasonForCreationSection, + /* 2 */ NotesTemplate, + /* 3 */ HgvTechRecord, + /* 4 */ HgvAndTrlTypeApprovalTemplate, + /* 5 */ ApplicantDetails, + /* 6 */ DocumentsTemplate, + /* 7 */ HgvAndTrlBodyTemplate, + /* 8 */ HgvWeight, + /* 9 */ tyresTemplateHgv, + /* 10 */ HgvDimensionsTemplate, + /* 11 */ PlatesTemplate, + /* 12 */ AdrTemplate, + /* 13 */ AdrCertificateTemplate, + ], + ], + [ + VehicleTypes.TRL, + [ + /* 1 */ TechRecordReasonForCreationSection, + /* 2 */ NotesTemplate, + /* 3 */ TrlTechRecordTemplate, + /* 4 */ HgvAndTrlTypeApprovalTemplate, + /* 5 */ ApplicantDetails, + /* 6 */ DocumentsTemplate, + /* 7 */ LettersTemplate, + /* 8 */ HgvAndTrlBodyTemplate, + /* 9 */ TrlWeight, + /* 10 */ tyresTemplateTrl, + /* 11 */ TrlBrakesTemplate, + /* 12 */ TrlPurchasers, + /* 13 */ TrlDimensionsTemplate, + /* 14 */ PlatesTemplate, + /* 15 */ TrlAuthIntoServiceTemplate, + /* 16 */ ManufacturerTemplate, + /* 17 */ AdrTemplate, + /* 18 */ AdrCertificateTemplate, + ], + ], + [ + VehicleTypes.SMALL_TRL, + [ + TechRecordReasonForCreationSection, + /* 2 */ SmallTrailerTechRecord, + /* 3 */ ApplicantDetails, + /* 4 */ NotesTemplate, + /* 5 */ Audit, + ], + ], + [ + VehicleTypes.LGV, + [ + /* 1 */ TechRecordReasonForCreationSection, + /* 2 */ LgvTechRecord, + /* 3 */ ApplicantDetails, + /* 4 */ NotesTemplate, + /* 5 */ Audit, + /* 6 */ AdrTemplate, + /* 7 */ AdrCertificateTemplate, + ], + ], + [ + VehicleTypes.CAR, + [ + TechRecordReasonForCreationSection, + /* 2 */ CarTechRecord, + /* 3 */ ApplicantDetails, + /* 4 */ NotesTemplate, + /* 5 */ Audit, + ], + ], + [ + VehicleTypes.MOTORCYCLE, + [ + TechRecordReasonForCreationSection, + /* 2 */ MotorcycleTechRecord, + /* 3 */ ApplicantDetails, + /* 4 */ NotesTemplate, + /* 5 */ Audit, + ], + ], ]); diff --git a/src/app/forms/validators/adr/adr.validators.spec.ts b/src/app/forms/validators/adr/adr.validators.spec.ts index fff37cd89d..f1831beda7 100644 --- a/src/app/forms/validators/adr/adr.validators.spec.ts +++ b/src/app/forms/validators/adr/adr.validators.spec.ts @@ -1,155 +1,159 @@ -import { - CustomFormControl, CustomFormGroup, - FormNodeTypes, -} from '@forms/services/dynamic-form.types'; +import { CustomFormControl, CustomFormGroup, FormNodeTypes } from '@forms/services/dynamic-form.types'; import { AdrValidators } from './adr.validators'; describe('ADR Validators', () => { - let form: CustomFormGroup; - - beforeEach(() => { - form = new CustomFormGroup({ type: FormNodeTypes.GROUP, name: 'adrForm' }, { - techRecord_adrDetails_tank_tankDetails_tankStatement_productListUnNo: new CustomFormControl({ - name: 'techRecord_adrDetails_tank_tankDetails_tankStatement_productListUnNo', - type: FormNodeTypes.CONTROL, - }), - techRecord_adrDetails_tank_tankDetails_tankStatement_productListRefNo: new CustomFormControl({ - name: 'techRecord_adrDetails_tank_tankDetails_tankStatement_productListRefNo', - type: FormNodeTypes.CONTROL, - }), - }); - }); - - describe('validateProductListRefNo', () => { - it('should return NULL if the control is hidden', () => { - const control = form.get('techRecord_adrDetails_tank_tankDetails_tankStatement_productListRefNo') as CustomFormControl; - control.meta.hide = true; - - expect(AdrValidators.validateProductListRefNo(control)).toBeNull(); - }); - - it('should return an error message if the product list reference number and first UN number is empty', () => { - const a = form.get('techRecord_adrDetails_tank_tankDetails_tankStatement_productListRefNo') as CustomFormControl; - a.meta.hide = false; - a.patchValue(''); // make so product list reference number is empty - - const b = form.get('techRecord_adrDetails_tank_tankDetails_tankStatement_productListUnNo') as CustomFormControl; - b.meta.hide = false; - b.patchValue(['']); // make so first UN number is empty - - expect(AdrValidators.validateProductListRefNo(a)).toStrictEqual({ - custom: { - message: 'Reference number or UN number 1 is required when selecting Product List', - }, - }); - }); - - it('should return NULL if the product list reference number is populated', () => { - const a = form.get('techRecord_adrDetails_tank_tankDetails_tankStatement_productListRefNo') as CustomFormControl; - a.meta.hide = false; - a.patchValue('reference no.'); // make so product list reference number is populated - - const b = form.get('techRecord_adrDetails_tank_tankDetails_tankStatement_productListUnNo') as CustomFormControl; - b.meta.hide = false; - b.patchValue(['']); // make so first UN number is empty - - expect(AdrValidators.validateProductListRefNo(a)).toBeNull(); - }); - - it('should return NULL if the first UN number is populated', () => { - const a = form.get('techRecord_adrDetails_tank_tankDetails_tankStatement_productListRefNo') as CustomFormControl; - a.meta.hide = false; - a.patchValue(''); // make so product list reference number is empty - - const b = form.get('techRecord_adrDetails_tank_tankDetails_tankStatement_productListUnNo') as CustomFormControl; - b.meta.hide = false; - b.patchValue(['something']); // make so first UN number is empty - - expect(AdrValidators.validateProductListRefNo(a)).toBeNull(); - }); - }); - - describe('validProductListUNNumbers', () => { - it('should return NULL if the control is hidden', () => { - const control = form.get('techRecord_adrDetails_tank_tankDetails_tankStatement_productListUnNo') as CustomFormControl; - control.meta.hide = true; - - expect(AdrValidators.validateProductListUNNumbers(control)).toBeNull(); - }); - - it('should return an error message if the product list reference number and first UN number is empty', () => { - const a = form.get('techRecord_adrDetails_tank_tankDetails_tankStatement_productListRefNo') as CustomFormControl; - a.meta.hide = false; - a.patchValue(''); // make so product list reference number is empty - - const b = form.get('techRecord_adrDetails_tank_tankDetails_tankStatement_productListUnNo') as CustomFormControl; - b.meta.hide = false; - b.patchValue(['']); // make so first UN number is empty - - expect(AdrValidators.validateProductListUNNumbers(b)).toStrictEqual({ - custom: { - anchorLink: 'UN_number_1', - message: 'Reference number or UN number 1 is required when selecting Product List', - }, - }); - }); - - it('should return an error message if the last UN number is empty', () => { - const a = form.get('techRecord_adrDetails_tank_tankDetails_tankStatement_productListRefNo') as CustomFormControl; - a.meta.hide = false; - a.patchValue(''); // make so product list reference number is empty - - const b = form.get('techRecord_adrDetails_tank_tankDetails_tankStatement_productListUnNo') as CustomFormControl; - b.meta.hide = false; - b.patchValue(['first un', '']); // make so last UN number is empty - - expect(AdrValidators.validateProductListUNNumbers(b)).toStrictEqual({ - custom: { - anchorLink: 'UN_number_2', - message: 'UN number 2 is required or remove UN number 2', - }, - }); - }); - - it('should return an error message for any UN number which exceeds 1500 characters', () => { - const longString = new Array(1501).fill('a').join(); - - const a = form.get('techRecord_adrDetails_tank_tankDetails_tankStatement_productListRefNo') as CustomFormControl; - a.meta.hide = false; - a.patchValue(''); // make so product list reference number is empty - - const b = form.get('techRecord_adrDetails_tank_tankDetails_tankStatement_productListUnNo') as CustomFormControl; - b.meta.hide = false; - b.patchValue(['first un', longString, longString, longString]); // make so last UN number is empty - - expect(AdrValidators.validateProductListUNNumbers(b)).toStrictEqual({ - multiple: [ - { - anchorLink: 'UN_number_2', - error: 'UN number 2 must be less than or equal to 1500 characters', - }, - { - anchorLink: 'UN_number_3', - error: 'UN number 3 must be less than or equal to 1500 characters', - }, - { - anchorLink: 'UN_number_4', - error: 'UN number 4 must be less than or equal to 1500 characters', - }, - ], - }); - }); - - it('should return NULL if ALL UN numbers are populated, but less than 1500 characters in length', () => { - const a = form.get('techRecord_adrDetails_tank_tankDetails_tankStatement_productListRefNo') as CustomFormControl; - a.meta.hide = false; - a.patchValue(''); // make so product list reference number is empty - - const b = form.get('techRecord_adrDetails_tank_tankDetails_tankStatement_productListUnNo') as CustomFormControl; - b.meta.hide = false; - b.patchValue(['small string', 'small string', 'small string']); // make so last UN number is empty - - expect(AdrValidators.validateProductListUNNumbers(b)).toBeNull(); - }); - }); + let form: CustomFormGroup; + + beforeEach(() => { + form = new CustomFormGroup( + { type: FormNodeTypes.GROUP, name: 'adrForm' }, + { + techRecord_adrDetails_tank_tankDetails_tankStatement_productListUnNo: new CustomFormControl({ + name: 'techRecord_adrDetails_tank_tankDetails_tankStatement_productListUnNo', + type: FormNodeTypes.CONTROL, + }), + techRecord_adrDetails_tank_tankDetails_tankStatement_productListRefNo: new CustomFormControl({ + name: 'techRecord_adrDetails_tank_tankDetails_tankStatement_productListRefNo', + type: FormNodeTypes.CONTROL, + }), + } + ); + }); + + describe('validateProductListRefNo', () => { + it('should return NULL if the control is hidden', () => { + const control = form.get( + 'techRecord_adrDetails_tank_tankDetails_tankStatement_productListRefNo' + ) as CustomFormControl; + control.meta.hide = true; + + expect(AdrValidators.validateProductListRefNo(control)).toBeNull(); + }); + + it('should return an error message if the product list reference number and first UN number is empty', () => { + const a = form.get('techRecord_adrDetails_tank_tankDetails_tankStatement_productListRefNo') as CustomFormControl; + a.meta.hide = false; + a.patchValue(''); // make so product list reference number is empty + + const b = form.get('techRecord_adrDetails_tank_tankDetails_tankStatement_productListUnNo') as CustomFormControl; + b.meta.hide = false; + b.patchValue(['']); // make so first UN number is empty + + expect(AdrValidators.validateProductListRefNo(a)).toStrictEqual({ + custom: { + message: 'Reference number or UN number 1 is required when selecting Product List', + }, + }); + }); + + it('should return NULL if the product list reference number is populated', () => { + const a = form.get('techRecord_adrDetails_tank_tankDetails_tankStatement_productListRefNo') as CustomFormControl; + a.meta.hide = false; + a.patchValue('reference no.'); // make so product list reference number is populated + + const b = form.get('techRecord_adrDetails_tank_tankDetails_tankStatement_productListUnNo') as CustomFormControl; + b.meta.hide = false; + b.patchValue(['']); // make so first UN number is empty + + expect(AdrValidators.validateProductListRefNo(a)).toBeNull(); + }); + + it('should return NULL if the first UN number is populated', () => { + const a = form.get('techRecord_adrDetails_tank_tankDetails_tankStatement_productListRefNo') as CustomFormControl; + a.meta.hide = false; + a.patchValue(''); // make so product list reference number is empty + + const b = form.get('techRecord_adrDetails_tank_tankDetails_tankStatement_productListUnNo') as CustomFormControl; + b.meta.hide = false; + b.patchValue(['something']); // make so first UN number is empty + + expect(AdrValidators.validateProductListRefNo(a)).toBeNull(); + }); + }); + + describe('validProductListUNNumbers', () => { + it('should return NULL if the control is hidden', () => { + const control = form.get( + 'techRecord_adrDetails_tank_tankDetails_tankStatement_productListUnNo' + ) as CustomFormControl; + control.meta.hide = true; + + expect(AdrValidators.validateProductListUNNumbers(control)).toBeNull(); + }); + + it('should return an error message if the product list reference number and first UN number is empty', () => { + const a = form.get('techRecord_adrDetails_tank_tankDetails_tankStatement_productListRefNo') as CustomFormControl; + a.meta.hide = false; + a.patchValue(''); // make so product list reference number is empty + + const b = form.get('techRecord_adrDetails_tank_tankDetails_tankStatement_productListUnNo') as CustomFormControl; + b.meta.hide = false; + b.patchValue(['']); // make so first UN number is empty + + expect(AdrValidators.validateProductListUNNumbers(b)).toStrictEqual({ + custom: { + anchorLink: 'UN_number_1', + message: 'Reference number or UN number 1 is required when selecting Product List', + }, + }); + }); + + it('should return an error message if the last UN number is empty', () => { + const a = form.get('techRecord_adrDetails_tank_tankDetails_tankStatement_productListRefNo') as CustomFormControl; + a.meta.hide = false; + a.patchValue(''); // make so product list reference number is empty + + const b = form.get('techRecord_adrDetails_tank_tankDetails_tankStatement_productListUnNo') as CustomFormControl; + b.meta.hide = false; + b.patchValue(['first un', '']); // make so last UN number is empty + + expect(AdrValidators.validateProductListUNNumbers(b)).toStrictEqual({ + custom: { + anchorLink: 'UN_number_2', + message: 'UN number 2 is required or remove UN number 2', + }, + }); + }); + + it('should return an error message for any UN number which exceeds 1500 characters', () => { + const longString = new Array(1501).fill('a').join(); + + const a = form.get('techRecord_adrDetails_tank_tankDetails_tankStatement_productListRefNo') as CustomFormControl; + a.meta.hide = false; + a.patchValue(''); // make so product list reference number is empty + + const b = form.get('techRecord_adrDetails_tank_tankDetails_tankStatement_productListUnNo') as CustomFormControl; + b.meta.hide = false; + b.patchValue(['first un', longString, longString, longString]); // make so last UN number is empty + + expect(AdrValidators.validateProductListUNNumbers(b)).toStrictEqual({ + multiple: [ + { + anchorLink: 'UN_number_2', + error: 'UN number 2 must be less than or equal to 1500 characters', + }, + { + anchorLink: 'UN_number_3', + error: 'UN number 3 must be less than or equal to 1500 characters', + }, + { + anchorLink: 'UN_number_4', + error: 'UN number 4 must be less than or equal to 1500 characters', + }, + ], + }); + }); + + it('should return NULL if ALL UN numbers are populated, but less than 1500 characters in length', () => { + const a = form.get('techRecord_adrDetails_tank_tankDetails_tankStatement_productListRefNo') as CustomFormControl; + a.meta.hide = false; + a.patchValue(''); // make so product list reference number is empty + + const b = form.get('techRecord_adrDetails_tank_tankDetails_tankStatement_productListUnNo') as CustomFormControl; + b.meta.hide = false; + b.patchValue(['small string', 'small string', 'small string']); // make so last UN number is empty + + expect(AdrValidators.validateProductListUNNumbers(b)).toBeNull(); + }); + }); }); diff --git a/src/app/forms/validators/adr/adr.validators.ts b/src/app/forms/validators/adr/adr.validators.ts index 910355cada..c6440d1db4 100644 --- a/src/app/forms/validators/adr/adr.validators.ts +++ b/src/app/forms/validators/adr/adr.validators.ts @@ -2,65 +2,69 @@ import { ValidationErrors } from '@angular/forms'; import { CustomFormControl } from '@forms/services/dynamic-form.types'; export class AdrValidators { - static validateProductListRefNo = (control: CustomFormControl): ValidationErrors | null => { - if (control.meta?.hide) { - return null; - } + static validateProductListRefNo = (control: CustomFormControl): ValidationErrors | null => { + if (control.meta?.hide) { + return null; + } - const unNumber1 = control.parent?.get('techRecord_adrDetails_tank_tankDetails_tankStatement_productListUnNo'); + const unNumber1 = control.parent?.get('techRecord_adrDetails_tank_tankDetails_tankStatement_productListUnNo'); - // If either productListRefNo is empty, or the first unNumber, mark as invalid - if ((!control.value && !unNumber1?.value) || (!control.value && !unNumber1?.value?.at(0))) { - return { custom: { message: 'Reference number or UN number 1 is required when selecting Product List' } }; - } + // If either productListRefNo is empty, or the first unNumber, mark as invalid + if ((!control.value && !unNumber1?.value) || (!control.value && !unNumber1?.value?.at(0))) { + return { custom: { message: 'Reference number or UN number 1 is required when selecting Product List' } }; + } - return null; - }; + return null; + }; - static validateProductListUNNumbers = (control: CustomFormControl): ValidationErrors | null => { - if (control.meta?.hide) { - return null; - } + static validateProductListUNNumbers = (control: CustomFormControl): ValidationErrors | null => { + if (control.meta?.hide) { + return null; + } - const productListRefNo = control.parent?.get('techRecord_adrDetails_tank_tankDetails_tankStatement_productListRefNo'); - const firstUnNumber = control.value?.at(0); - const lastUnNumberIndex = (control.value?.length ?? 0) - 1; - const lastUnNumber = control.value?.at(lastUnNumberIndex); + const productListRefNo = control.parent?.get( + 'techRecord_adrDetails_tank_tankDetails_tankStatement_productListRefNo' + ); + const firstUnNumber = control.value?.at(0); + const lastUnNumberIndex = (control.value?.length ?? 0) - 1; + const lastUnNumber = control.value?.at(lastUnNumberIndex); - // If either productListRefNo is empty, or the first unNumber, mark as invalid - if ((!productListRefNo?.value && !control?.value) || (!productListRefNo?.value && !firstUnNumber)) { - return { - custom: { - message: 'Reference number or UN number 1 is required when selecting Product List', - anchorLink: 'UN_number_1', - }, - }; - } + // If either productListRefNo is empty, or the first unNumber, mark as invalid + if ((!productListRefNo?.value && !control?.value) || (!productListRefNo?.value && !firstUnNumber)) { + return { + custom: { + message: 'Reference number or UN number 1 is required when selecting Product List', + anchorLink: 'UN_number_1', + }, + }; + } - // If there are more than 1 UN numbers, and the last UN number is empty, mark as invalid - if (control.value?.length > 1 && !lastUnNumber) { - return { - custom: { - message: `UN number ${lastUnNumberIndex + 1} is required or remove UN number ${lastUnNumberIndex + 1}`, - anchorLink: `UN_number_${lastUnNumberIndex + 1}`, - }, - }; - } + // If there are more than 1 UN numbers, and the last UN number is empty, mark as invalid + if (control.value?.length > 1 && !lastUnNumber) { + return { + custom: { + message: `UN number ${lastUnNumberIndex + 1} is required or remove UN number ${lastUnNumberIndex + 1}`, + anchorLink: `UN_number_${lastUnNumberIndex + 1}`, + }, + }; + } - // If any of the control indices have length greater than 1500 characters, show an error message for each - if (control.value && control.value.some((unNumber: string) => unNumber.length > 1500)) { - return { - multiple: control.value.map((unNumber: string, index: number) => - unNumber.length > 1500 - ? { - error: `UN number ${index + 1} must be less than or equal to 1500 characters`, - anchorLink: `UN_number_${index + 1}`, - } - : null) - .filter(Boolean), - }; - } + // If any of the control indices have length greater than 1500 characters, show an error message for each + if (control.value && control.value.some((unNumber: string) => unNumber.length > 1500)) { + return { + multiple: control.value + .map((unNumber: string, index: number) => + unNumber.length > 1500 + ? { + error: `UN number ${index + 1} must be less than or equal to 1500 characters`, + anchorLink: `UN_number_${index + 1}`, + } + : null + ) + .filter(Boolean), + }; + } - return null; - }; + return null; + }; } diff --git a/src/app/forms/validators/custom-async-validators.spec.ts b/src/app/forms/validators/custom-async-validators.spec.ts index 6c4e919959..fe875b7c32 100644 --- a/src/app/forms/validators/custom-async-validators.spec.ts +++ b/src/app/forms/validators/custom-async-validators.spec.ts @@ -16,803 +16,1028 @@ import { Observable, firstValueFrom, lastValueFrom } from 'rxjs'; import { CustomAsyncValidators } from './custom-async-validators'; describe('resultDependantOnCustomDefects', () => { - let form: FormGroup; - let store: MockStore; + let form: FormGroup; + let store: MockStore; - beforeEach(() => { - TestBed.configureTestingModule({ - providers: [provideMockStore({ initialState: initialAppState })], - }); + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [provideMockStore({ initialState: initialAppState })], + }); - store = TestBed.inject(MockStore); + store = TestBed.inject(MockStore); - form = new FormGroup({ - testResult: new CustomFormControl({ name: 'testResult', type: FormNodeTypes.CONTROL, children: [] }, null), - }); - }); + form = new FormGroup({ + testResult: new CustomFormControl({ name: 'testResult', type: FormNodeTypes.CONTROL, children: [] }, null), + }); + }); - it('should fail validation when value is "pass" and defects are present', async () => { - form.controls['testResult'].patchValue('pass'); + it('should fail validation when value is "pass" and defects are present', async () => { + form.controls['testResult'].patchValue('pass'); - const mockedTestResult = mockTestResult(); - mockedTestResult.testTypes[0].customDefects = [createMockCustomDefect()]; - store.overrideSelector(testResultInEdit, mockedTestResult); + const mockedTestResult = mockTestResult(); + mockedTestResult.testTypes[0].customDefects = [createMockCustomDefect()]; + store.overrideSelector(testResultInEdit, mockedTestResult); - const result = await firstValueFrom( - CustomAsyncValidators.resultDependantOnCustomDefects(store)(form.controls['testResult']) as Observable, - ); + const result = await firstValueFrom( + CustomAsyncValidators.resultDependantOnCustomDefects(store)( + form.controls['testResult'] + ) as Observable + ); - expect(result).toEqual({ invalidTestResult: { message: 'Cannot pass test when defects are present' } }); - }); + expect(result).toEqual({ invalidTestResult: { message: 'Cannot pass test when defects are present' } }); + }); - it('should fail validation when value is "fail" but no defects are present', async () => { - form.controls['testResult'].patchValue('fail'); + it('should fail validation when value is "fail" but no defects are present', async () => { + form.controls['testResult'].patchValue('fail'); - const testResult = mockTestResult(); - testResult.testTypes = []; + const testResult = mockTestResult(); + testResult.testTypes = []; - store.overrideSelector(testResultInEdit, testResult); + store.overrideSelector(testResultInEdit, testResult); - const result = await firstValueFrom( - CustomAsyncValidators.resultDependantOnCustomDefects(store)(form.controls['testResult']) as Observable, - ); + const result = await firstValueFrom( + CustomAsyncValidators.resultDependantOnCustomDefects(store)( + form.controls['testResult'] + ) as Observable + ); - expect(result).toEqual({ invalidTestResult: { message: 'Cannot fail test when no defects are present' } }); - }); + expect(result).toEqual({ invalidTestResult: { message: 'Cannot fail test when no defects are present' } }); + }); - it('should fail validation when value is "prs" but no defects are present', async () => { - form.controls['testResult'].patchValue('prs'); + it('should fail validation when value is "prs" but no defects are present', async () => { + form.controls['testResult'].patchValue('prs'); - const testResult = mockTestResult(); - testResult.testTypes = []; + const testResult = mockTestResult(); + testResult.testTypes = []; - store.overrideSelector(testResultInEdit, testResult); + store.overrideSelector(testResultInEdit, testResult); - const result = await firstValueFrom( - CustomAsyncValidators.resultDependantOnCustomDefects(store)(form.controls['testResult']) as Observable, - ); + const result = await firstValueFrom( + CustomAsyncValidators.resultDependantOnCustomDefects(store)( + form.controls['testResult'] + ) as Observable + ); - expect(result).toEqual({ invalidTestResult: { message: 'Cannot mark test as PRS when no defects are present' } }); - }); + expect(result).toEqual({ invalidTestResult: { message: 'Cannot mark test as PRS when no defects are present' } }); + }); - it('should pass validation when value is "abandoned"', async () => { - form.controls['testResult'].patchValue('abandoned'); + it('should pass validation when value is "abandoned"', async () => { + form.controls['testResult'].patchValue('abandoned'); - store.overrideSelector(testResultInEdit, mockTestResult()); + store.overrideSelector(testResultInEdit, mockTestResult()); - const result = await firstValueFrom( - CustomAsyncValidators.resultDependantOnCustomDefects(store)(form.controls['testResult']) as Observable, - ); + const result = await firstValueFrom( + CustomAsyncValidators.resultDependantOnCustomDefects(store)( + form.controls['testResult'] + ) as Observable + ); - expect(result).toBeNull(); - }); + expect(result).toBeNull(); + }); }); describe('passResultDependantOnCustomDefects', () => { - let form: FormGroup; - let store: MockStore; + let form: FormGroup; + let store: MockStore; - beforeEach(() => { - TestBed.configureTestingModule({ - providers: [provideMockStore({ initialState: initialAppState })], - }); + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [provideMockStore({ initialState: initialAppState })], + }); - store = TestBed.inject(MockStore); + store = TestBed.inject(MockStore); - form = new FormGroup({ - testResult: new CustomFormControl({ name: 'testResult', type: FormNodeTypes.CONTROL, children: [] }, null), - }); - }); + form = new FormGroup({ + testResult: new CustomFormControl({ name: 'testResult', type: FormNodeTypes.CONTROL, children: [] }, null), + }); + }); - it('should fail validation when value is "pass" and defects are present', async () => { - form.controls['testResult'].patchValue('pass'); + it('should fail validation when value is "pass" and defects are present', async () => { + form.controls['testResult'].patchValue('pass'); - const mockedTestResult = mockTestResult(); - mockedTestResult.testTypes[0].customDefects = [createMockCustomDefect()]; - store.overrideSelector(testResultInEdit, mockedTestResult); + const mockedTestResult = mockTestResult(); + mockedTestResult.testTypes[0].customDefects = [createMockCustomDefect()]; + store.overrideSelector(testResultInEdit, mockedTestResult); - const result = await firstValueFrom( - CustomAsyncValidators.passResultDependantOnCustomDefects(store)(form.controls['testResult']) as Observable, - ); + const result = await firstValueFrom( + CustomAsyncValidators.passResultDependantOnCustomDefects(store)( + form.controls['testResult'] + ) as Observable + ); - expect(result).toEqual({ invalidTestResult: { message: 'Cannot pass test when defects are present' } }); - }); + expect(result).toEqual({ invalidTestResult: { message: 'Cannot pass test when defects are present' } }); + }); - it('should pass validation when value is "fail" but no defects are present', async () => { - form.controls['testResult'].patchValue('fail'); + it('should pass validation when value is "fail" but no defects are present', async () => { + form.controls['testResult'].patchValue('fail'); - const testResult = mockTestResult(); - testResult.testTypes = []; + const testResult = mockTestResult(); + testResult.testTypes = []; - store.overrideSelector(testResultInEdit, testResult); + store.overrideSelector(testResultInEdit, testResult); - const result = await firstValueFrom( - CustomAsyncValidators.passResultDependantOnCustomDefects(store)(form.controls['testResult']) as Observable, - ); + const result = await firstValueFrom( + CustomAsyncValidators.passResultDependantOnCustomDefects(store)( + form.controls['testResult'] + ) as Observable + ); - expect(result).toBeNull(); - }); + expect(result).toBeNull(); + }); - it('should pass validation when value is "prs" but no defects are present', async () => { - form.controls['testResult'].patchValue('prs'); + it('should pass validation when value is "prs" but no defects are present', async () => { + form.controls['testResult'].patchValue('prs'); - const testResult = mockTestResult(); - testResult.testTypes = []; + const testResult = mockTestResult(); + testResult.testTypes = []; - store.overrideSelector(testResultInEdit, testResult); + store.overrideSelector(testResultInEdit, testResult); - const result = await firstValueFrom( - CustomAsyncValidators.passResultDependantOnCustomDefects(store)(form.controls['testResult']) as Observable, - ); + const result = await firstValueFrom( + CustomAsyncValidators.passResultDependantOnCustomDefects(store)( + form.controls['testResult'] + ) as Observable + ); - expect(result).toBeNull(); - }); + expect(result).toBeNull(); + }); - it('should pass validation when value is "abandoned"', async () => { - form.controls['testResult'].patchValue('abandoned'); + it('should pass validation when value is "abandoned"', async () => { + form.controls['testResult'].patchValue('abandoned'); - store.overrideSelector(testResultInEdit, mockTestResult()); + store.overrideSelector(testResultInEdit, mockTestResult()); - const result = await firstValueFrom( - CustomAsyncValidators.passResultDependantOnCustomDefects(store)(form.controls['testResult']) as Observable, - ); + const result = await firstValueFrom( + CustomAsyncValidators.passResultDependantOnCustomDefects(store)( + form.controls['testResult'] + ) as Observable + ); - expect(result).toBeNull(); - }); + expect(result).toBeNull(); + }); }); describe('updateTestStationDetails', () => { - let form: FormGroup; - let store: MockStore; - - beforeEach(() => { - TestBed.configureTestingModule({ - providers: [ - provideMockStore({ - initialState: { - ...initialAppState, - testStations: { - ...initialTestStationsState, - ids: ['1'], - entities: { 1: { testStationName: 'foo', testStationPNumber: '1234', testStationType: 'bar' } }, - }, - }, - }), - ], - }); - - store = TestBed.inject(MockStore); - - form = new FormGroup({ - testStationName: new CustomFormControl({ name: 'testStationName', type: FormNodeTypes.CONTROL, children: [] }, null), - testStationType: new CustomFormControl({ name: 'testStationType', type: FormNodeTypes.CONTROL, children: [] }, null), - testStationPNumber: new CustomFormControl({ name: 'testStationPNumber', type: FormNodeTypes.CONTROL, children: [] }, null), - }); - }); - it('should update the test stations details', async () => { - form.controls['testStationPNumber'].patchValue('1234'); - expect(form.controls['testStationPNumber']).toBeTruthy(); - await firstValueFrom(CustomAsyncValidators.updateTestStationDetails(store)(form.controls['testStationPNumber']) as Observable); - expect(form.controls['testStationType'].value).toBe('bar'); - expect(form.controls['testStationName'].value).toBe('foo'); - }); + let form: FormGroup; + let store: MockStore; + + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [ + provideMockStore({ + initialState: { + ...initialAppState, + testStations: { + ...initialTestStationsState, + ids: ['1'], + entities: { 1: { testStationName: 'foo', testStationPNumber: '1234', testStationType: 'bar' } }, + }, + }, + }), + ], + }); + + store = TestBed.inject(MockStore); + + form = new FormGroup({ + testStationName: new CustomFormControl( + { name: 'testStationName', type: FormNodeTypes.CONTROL, children: [] }, + null + ), + testStationType: new CustomFormControl( + { name: 'testStationType', type: FormNodeTypes.CONTROL, children: [] }, + null + ), + testStationPNumber: new CustomFormControl( + { name: 'testStationPNumber', type: FormNodeTypes.CONTROL, children: [] }, + null + ), + }); + }); + it('should update the test stations details', async () => { + form.controls['testStationPNumber'].patchValue('1234'); + expect(form.controls['testStationPNumber']).toBeTruthy(); + await firstValueFrom( + CustomAsyncValidators.updateTestStationDetails(store)(form.controls['testStationPNumber']) as Observable + ); + expect(form.controls['testStationType'].value).toBe('bar'); + expect(form.controls['testStationName'].value).toBe('foo'); + }); }); describe('requiredIfNotFail', () => { - let form: FormGroup; - let store: MockStore; + let form: FormGroup; + let store: MockStore; - beforeEach(() => { - TestBed.configureTestingModule({ - providers: [provideMockStore({ initialState: initialAppState })], - }); + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [provideMockStore({ initialState: initialAppState })], + }); - store = TestBed.inject(MockStore); + store = TestBed.inject(MockStore); - form = new FormGroup({ - foo: new CustomFormControl({ name: 'foo', type: FormNodeTypes.CONTROL, children: [] }, null), - }); - }); + form = new FormGroup({ + foo: new CustomFormControl({ name: 'foo', type: FormNodeTypes.CONTROL, children: [] }, null), + }); + }); - it('should be required when value is "pass"', async () => { - form.controls['foo'].patchValue(''); + it('should be required when value is "pass"', async () => { + form.controls['foo'].patchValue(''); - const testResult = { testTypes: [{ testResult: resultOfTestEnum.pass }] } as TestResultModel; + const testResult = { testTypes: [{ testResult: resultOfTestEnum.pass }] } as TestResultModel; - store.overrideSelector(testResultInEdit, testResult); + store.overrideSelector(testResultInEdit, testResult); - const result = await firstValueFrom(CustomAsyncValidators.requiredIfNotFail(store)(form.controls['foo']) as Observable); + const result = await firstValueFrom( + CustomAsyncValidators.requiredIfNotFail(store)(form.controls['foo']) as Observable + ); - expect(result).toEqual({ requiredIfNotfail: true }); - }); + expect(result).toEqual({ requiredIfNotfail: true }); + }); - it('should pass validation if field is not empty when value is "pass"', async () => { - form.controls['foo'].patchValue('test'); + it('should pass validation if field is not empty when value is "pass"', async () => { + form.controls['foo'].patchValue('test'); - const testResult = { testTypes: [{ testResult: resultOfTestEnum.pass }] } as TestResultModel; + const testResult = { testTypes: [{ testResult: resultOfTestEnum.pass }] } as TestResultModel; - store.overrideSelector(testResultInEdit, testResult); + store.overrideSelector(testResultInEdit, testResult); - const result = await firstValueFrom(CustomAsyncValidators.requiredIfNotFail(store)(form.controls['foo']) as Observable); + const result = await firstValueFrom( + CustomAsyncValidators.requiredIfNotFail(store)(form.controls['foo']) as Observable + ); - expect(result).toBeNull(); - }); + expect(result).toBeNull(); + }); - it('should not be required when value is "fail"', async () => { - form.controls['foo'].patchValue(''); + it('should not be required when value is "fail"', async () => { + form.controls['foo'].patchValue(''); - const testResult = { testTypes: [{ testResult: resultOfTestEnum.fail }] } as TestResultModel; + const testResult = { testTypes: [{ testResult: resultOfTestEnum.fail }] } as TestResultModel; - store.overrideSelector(testResultInEdit, testResult); + store.overrideSelector(testResultInEdit, testResult); - const result = await firstValueFrom(CustomAsyncValidators.requiredIfNotFail(store)(form.controls['foo']) as Observable); + const result = await firstValueFrom( + CustomAsyncValidators.requiredIfNotFail(store)(form.controls['foo']) as Observable + ); - expect(result).toBeNull(); - }); + expect(result).toBeNull(); + }); - it('should be required when value is "prs"', async () => { - form.controls['foo'].patchValue(''); + it('should be required when value is "prs"', async () => { + form.controls['foo'].patchValue(''); - const testResult = { testTypes: [{ testResult: resultOfTestEnum.prs }] } as TestResultModel; + const testResult = { testTypes: [{ testResult: resultOfTestEnum.prs }] } as TestResultModel; - store.overrideSelector(testResultInEdit, testResult); + store.overrideSelector(testResultInEdit, testResult); - const result = await firstValueFrom(CustomAsyncValidators.requiredIfNotFail(store)(form.controls['foo']) as Observable); + const result = await firstValueFrom( + CustomAsyncValidators.requiredIfNotFail(store)(form.controls['foo']) as Observable + ); - expect(result).toEqual({ requiredIfNotfail: true }); - }); + expect(result).toEqual({ requiredIfNotfail: true }); + }); - it('should be required when value is "abandoned"', async () => { - form.controls['foo'].patchValue(''); + it('should be required when value is "abandoned"', async () => { + form.controls['foo'].patchValue(''); - const testResult = { testTypes: [{ testResult: resultOfTestEnum.abandoned }] } as TestResultModel; + const testResult = { testTypes: [{ testResult: resultOfTestEnum.abandoned }] } as TestResultModel; - store.overrideSelector(testResultInEdit, testResult); + store.overrideSelector(testResultInEdit, testResult); - const result = await firstValueFrom(CustomAsyncValidators.requiredIfNotFail(store)(form.controls['foo']) as Observable); + const result = await firstValueFrom( + CustomAsyncValidators.requiredIfNotFail(store)(form.controls['foo']) as Observable + ); - expect(result).toEqual({ requiredIfNotfail: true }); - }); + expect(result).toEqual({ requiredIfNotfail: true }); + }); }); describe('requiredIfNotAbandoned', () => { - let form: FormGroup; - let store: MockStore; + let form: FormGroup; + let store: MockStore; - beforeEach(() => { - TestBed.configureTestingModule({ - providers: [provideMockStore({ initialState: initialAppState })], - }); + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [provideMockStore({ initialState: initialAppState })], + }); - store = TestBed.inject(MockStore); + store = TestBed.inject(MockStore); - form = new FormGroup({ - foo: new CustomFormControl({ name: 'foo', type: FormNodeTypes.CONTROL, children: [] }, null), - }); - }); + form = new FormGroup({ + foo: new CustomFormControl({ name: 'foo', type: FormNodeTypes.CONTROL, children: [] }, null), + }); + }); - it('should be required when value is "pass"', async () => { - form.controls['foo'].patchValue(''); + it('should be required when value is "pass"', async () => { + form.controls['foo'].patchValue(''); - const testResult = { testTypes: [{ testResult: resultOfTestEnum.pass }] } as TestResultModel; + const testResult = { testTypes: [{ testResult: resultOfTestEnum.pass }] } as TestResultModel; - store.overrideSelector(testResultInEdit, testResult); + store.overrideSelector(testResultInEdit, testResult); - const result = await firstValueFrom( - CustomAsyncValidators.requiredIfNotAbandoned(store)(form.controls['foo']) as Observable, - ); + const result = await firstValueFrom( + CustomAsyncValidators.requiredIfNotAbandoned(store)(form.controls['foo']) as Observable + ); - expect(result).toEqual({ requiredIfNotabandoned: true }); - }); + expect(result).toEqual({ requiredIfNotabandoned: true }); + }); - it('should pass validation if field is not empty when value is "pass"', async () => { - form.controls['foo'].patchValue('test'); + it('should pass validation if field is not empty when value is "pass"', async () => { + form.controls['foo'].patchValue('test'); - const testResult = { testTypes: [{ testResult: resultOfTestEnum.pass }] } as TestResultModel; + const testResult = { testTypes: [{ testResult: resultOfTestEnum.pass }] } as TestResultModel; - store.overrideSelector(testResultInEdit, testResult); + store.overrideSelector(testResultInEdit, testResult); - const result = await firstValueFrom( - CustomAsyncValidators.requiredIfNotAbandoned(store)(form.controls['foo']) as Observable, - ); + const result = await firstValueFrom( + CustomAsyncValidators.requiredIfNotAbandoned(store)(form.controls['foo']) as Observable + ); - expect(result).toBeNull(); - }); + expect(result).toBeNull(); + }); - it('should be required when value is "fail"', async () => { - form.controls['foo'].patchValue(''); + it('should be required when value is "fail"', async () => { + form.controls['foo'].patchValue(''); - const testResult = { testTypes: [{ testResult: resultOfTestEnum.fail }] } as TestResultModel; + const testResult = { testTypes: [{ testResult: resultOfTestEnum.fail }] } as TestResultModel; - store.overrideSelector(testResultInEdit, testResult); + store.overrideSelector(testResultInEdit, testResult); - const result = await firstValueFrom( - CustomAsyncValidators.requiredIfNotAbandoned(store)(form.controls['foo']) as Observable, - ); + const result = await firstValueFrom( + CustomAsyncValidators.requiredIfNotAbandoned(store)(form.controls['foo']) as Observable + ); - expect(result).toEqual({ requiredIfNotabandoned: true }); - }); + expect(result).toEqual({ requiredIfNotabandoned: true }); + }); - it('should be required when value is "prs"', async () => { - form.controls['foo'].patchValue(''); + it('should be required when value is "prs"', async () => { + form.controls['foo'].patchValue(''); - const testResult = { testTypes: [{ testResult: resultOfTestEnum.prs }] } as TestResultModel; + const testResult = { testTypes: [{ testResult: resultOfTestEnum.prs }] } as TestResultModel; - store.overrideSelector(testResultInEdit, testResult); + store.overrideSelector(testResultInEdit, testResult); - const result = await firstValueFrom( - CustomAsyncValidators.requiredIfNotAbandoned(store)(form.controls['foo']) as Observable, - ); + const result = await firstValueFrom( + CustomAsyncValidators.requiredIfNotAbandoned(store)(form.controls['foo']) as Observable + ); - expect(result).toEqual({ requiredIfNotabandoned: true }); - }); + expect(result).toEqual({ requiredIfNotabandoned: true }); + }); - it('should not be required when value is "abandoned"', async () => { - form.controls['foo'].patchValue(''); + it('should not be required when value is "abandoned"', async () => { + form.controls['foo'].patchValue(''); - const testResult = { testTypes: [{ testResult: resultOfTestEnum.abandoned }] } as TestResultModel; + const testResult = { testTypes: [{ testResult: resultOfTestEnum.abandoned }] } as TestResultModel; - store.overrideSelector(testResultInEdit, testResult); + store.overrideSelector(testResultInEdit, testResult); - const result = await firstValueFrom( - CustomAsyncValidators.requiredIfNotAbandoned(store)(form.controls['foo']) as Observable, - ); + const result = await firstValueFrom( + CustomAsyncValidators.requiredIfNotAbandoned(store)(form.controls['foo']) as Observable + ); - expect(result).toBeNull(); - }); + expect(result).toBeNull(); + }); }); describe('requiredIfNotResult', () => { - let form: FormGroup; - let store: MockStore; + let form: FormGroup; + let store: MockStore; - beforeEach(() => { - TestBed.configureTestingModule({ - providers: [provideMockStore({ initialState: initialAppState })], - }); + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [provideMockStore({ initialState: initialAppState })], + }); - store = TestBed.inject(MockStore); + store = TestBed.inject(MockStore); - form = new FormGroup({ - foo: new CustomFormControl({ name: 'foo', type: FormNodeTypes.CONTROL, children: [] }, null), - }); - }); + form = new FormGroup({ + foo: new CustomFormControl({ name: 'foo', type: FormNodeTypes.CONTROL, children: [] }, null), + }); + }); - it('should be required when result is "pass" and validator specifies requiredIfNotResult "fail"', async () => { - form.controls['foo'].patchValue(''); + it('should be required when result is "pass" and validator specifies requiredIfNotResult "fail"', async () => { + form.controls['foo'].patchValue(''); - const testResult = { testTypes: [{ testResult: resultOfTestEnum.pass }] } as TestResultModel; + const testResult = { testTypes: [{ testResult: resultOfTestEnum.pass }] } as TestResultModel; - store.overrideSelector(testResultInEdit, testResult); + store.overrideSelector(testResultInEdit, testResult); - const result = await firstValueFrom( - CustomAsyncValidators.requiredIfNotResult(store, resultOfTestEnum.fail)(form.controls['foo']) as Observable, - ); + const result = await firstValueFrom( + CustomAsyncValidators.requiredIfNotResult( + store, + resultOfTestEnum.fail + )(form.controls['foo']) as Observable + ); - expect(result).toEqual({ requiredIfNotfail: true }); - }); + expect(result).toEqual({ requiredIfNotfail: true }); + }); - it('should be required when result is "pass" and validator specifies requiredIfNotResult "fail" or "abandoned"', async () => { - form.controls['foo'].patchValue(''); + it('should be required when result is "pass" and validator specifies requiredIfNotResult "fail" or "abandoned"', async () => { + form.controls['foo'].patchValue(''); - const testResult = { testTypes: [{ testResult: resultOfTestEnum.pass }] } as TestResultModel; + const testResult = { testTypes: [{ testResult: resultOfTestEnum.pass }] } as TestResultModel; - store.overrideSelector(testResultInEdit, testResult); + store.overrideSelector(testResultInEdit, testResult); - const result = await firstValueFrom( - CustomAsyncValidators.requiredIfNotResult(store, [resultOfTestEnum.fail, resultOfTestEnum.abandoned])( - form.controls['foo'], - ) as Observable, - ); + const result = await firstValueFrom( + CustomAsyncValidators.requiredIfNotResult(store, [resultOfTestEnum.fail, resultOfTestEnum.abandoned])( + form.controls['foo'] + ) as Observable + ); - expect(result).toEqual({ requiredIfNotResult: true }); - }); + expect(result).toEqual({ requiredIfNotResult: true }); + }); - it('should pass validation if field is not empty when value is "pass" and validator specifies requiredIfNotResult "fail"', async () => { - form.controls['foo'].patchValue('test'); + it('should pass validation if field is not empty when value is "pass" and validator specifies requiredIfNotResult "fail"', async () => { + form.controls['foo'].patchValue('test'); - const testResult = { testTypes: [{ testResult: resultOfTestEnum.pass }] } as TestResultModel; + const testResult = { testTypes: [{ testResult: resultOfTestEnum.pass }] } as TestResultModel; - store.overrideSelector(testResultInEdit, testResult); + store.overrideSelector(testResultInEdit, testResult); - const result = await firstValueFrom( - CustomAsyncValidators.requiredIfNotResult(store, resultOfTestEnum.fail)(form.controls['foo']) as Observable, - ); + const result = await firstValueFrom( + CustomAsyncValidators.requiredIfNotResult( + store, + resultOfTestEnum.fail + )(form.controls['foo']) as Observable + ); - expect(result).toBeNull(); - }); + expect(result).toBeNull(); + }); - it('should not be required when value is "fail" and validator specifies requiredIfNotResult "fail"', async () => { - form.controls['foo'].patchValue(''); + it('should not be required when value is "fail" and validator specifies requiredIfNotResult "fail"', async () => { + form.controls['foo'].patchValue(''); - const testResult = { testTypes: [{ testResult: resultOfTestEnum.fail }] } as TestResultModel; + const testResult = { testTypes: [{ testResult: resultOfTestEnum.fail }] } as TestResultModel; - store.overrideSelector(testResultInEdit, testResult); + store.overrideSelector(testResultInEdit, testResult); - const result = await firstValueFrom( - CustomAsyncValidators.requiredIfNotResult(store, resultOfTestEnum.fail)(form.controls['foo']) as Observable, - ); + const result = await firstValueFrom( + CustomAsyncValidators.requiredIfNotResult( + store, + resultOfTestEnum.fail + )(form.controls['foo']) as Observable + ); - expect(result).toBeNull(); - }); + expect(result).toBeNull(); + }); - it('should be required when value is "prs" and validator specifies requiredIfNotResult "fail"', async () => { - form.controls['foo'].patchValue(''); + it('should be required when value is "prs" and validator specifies requiredIfNotResult "fail"', async () => { + form.controls['foo'].patchValue(''); - const testResult = { testTypes: [{ testResult: resultOfTestEnum.prs }] } as TestResultModel; + const testResult = { testTypes: [{ testResult: resultOfTestEnum.prs }] } as TestResultModel; - store.overrideSelector(testResultInEdit, testResult); + store.overrideSelector(testResultInEdit, testResult); - const result = await firstValueFrom( - CustomAsyncValidators.requiredIfNotResult(store, resultOfTestEnum.fail)(form.controls['foo']) as Observable, - ); + const result = await firstValueFrom( + CustomAsyncValidators.requiredIfNotResult( + store, + resultOfTestEnum.fail + )(form.controls['foo']) as Observable + ); - expect(result).toEqual({ requiredIfNotfail: true }); - }); + expect(result).toEqual({ requiredIfNotfail: true }); + }); - it('should be required when value is "abandoned" and validator specifies requiredIfNotResult "fail"', async () => { - form.controls['foo'].patchValue(''); + it('should be required when value is "abandoned" and validator specifies requiredIfNotResult "fail"', async () => { + form.controls['foo'].patchValue(''); - const testResult = { testTypes: [{ testResult: resultOfTestEnum.abandoned }] } as TestResultModel; + const testResult = { testTypes: [{ testResult: resultOfTestEnum.abandoned }] } as TestResultModel; - store.overrideSelector(testResultInEdit, testResult); + store.overrideSelector(testResultInEdit, testResult); - const result = await firstValueFrom( - CustomAsyncValidators.requiredIfNotResult(store, resultOfTestEnum.fail)(form.controls['foo']) as Observable, - ); + const result = await firstValueFrom( + CustomAsyncValidators.requiredIfNotResult( + store, + resultOfTestEnum.fail + )(form.controls['foo']) as Observable + ); - expect(result).toEqual({ requiredIfNotfail: true }); - }); + expect(result).toEqual({ requiredIfNotfail: true }); + }); - it('should be required when value is "abandoned" and validator specifies requiredIfNotResult "fail" or "pass"', async () => { - form.controls['foo'].patchValue(''); + it('should be required when value is "abandoned" and validator specifies requiredIfNotResult "fail" or "pass"', async () => { + form.controls['foo'].patchValue(''); - const testResult = { testTypes: [{ testResult: resultOfTestEnum.abandoned }] } as TestResultModel; + const testResult = { testTypes: [{ testResult: resultOfTestEnum.abandoned }] } as TestResultModel; - store.overrideSelector(testResultInEdit, testResult); + store.overrideSelector(testResultInEdit, testResult); - const result = await firstValueFrom( - CustomAsyncValidators.requiredIfNotResult(store, [resultOfTestEnum.fail, resultOfTestEnum.pass])( - form.controls['foo'], - ) as Observable, - ); + const result = await firstValueFrom( + CustomAsyncValidators.requiredIfNotResult(store, [resultOfTestEnum.fail, resultOfTestEnum.pass])( + form.controls['foo'] + ) as Observable + ); - expect(result).toEqual({ requiredIfNotResult: true }); - }); + expect(result).toEqual({ requiredIfNotResult: true }); + }); }); describe('requiredIfNotResultAndSiblingEquals', () => { - let form: FormGroup; - let store: MockStore; - - beforeEach(() => { - TestBed.configureTestingModule({ - providers: [provideMockStore({ initialState: initialAppState })], - }); - - store = TestBed.inject(MockStore); - - form = new FormGroup({ - foo: new CustomFormControl({ name: 'foo', type: FormNodeTypes.CONTROL, children: [] }, null), - bar: new CustomFormControl({ name: 'bar', type: FormNodeTypes.CONTROL, children: [] }, null), - }); - }); - - it('should be required when result is "pass" and "bar is "x" and ' - + 'validator specifies requiredIfNotResultAndSiblingEquals "fail" when sibling "bar" is "x"', async () => { - form.controls['foo'].patchValue(''); - form.controls['bar'].patchValue('x'); - - const testResult = { testTypes: [{ testResult: resultOfTestEnum.pass }] } as TestResultModel; - - store.overrideSelector(testResultInEdit, testResult); - - const result = await firstValueFrom( - CustomAsyncValidators.requiredIfNotResultAndSiblingEquals( - store, - resultOfTestEnum.fail, - 'bar', - 'x', - )(form.controls['foo']) as Observable, - ); - - expect(result).toEqual({ requiredIfNotResultAndSiblingEquals: true }); - }); - - it('should pass validation when result is "fail" and "bar is "x" and ' - + 'validator specifies requiredIfNotResultAndSiblingEquals "fail" when sibling "bar" is "x"', async () => { - form.controls['foo'].patchValue(''); - form.controls['bar'].patchValue('x'); - - const testResult = { testTypes: [{ testResult: resultOfTestEnum.fail }] } as TestResultModel; - - store.overrideSelector(testResultInEdit, testResult); - - const result = await firstValueFrom( - CustomAsyncValidators.requiredIfNotResultAndSiblingEquals( - store, - resultOfTestEnum.fail, - 'bar', - 'x', - )(form.controls['foo']) as Observable, - ); - - expect(result).toBeNull(); - }); - - it('should pass validation when result is "pass" and "bar is "y" and ' - + 'validator specifies requiredIfNotResultAndSiblingEquals "fail" when sibling "bar" is "x"', async () => { - form.controls['foo'].patchValue(''); - form.controls['bar'].patchValue('y'); - - const testResult = { testTypes: [{ testResult: resultOfTestEnum.pass }] } as TestResultModel; - - store.overrideSelector(testResultInEdit, testResult); - - const result = await firstValueFrom( - CustomAsyncValidators.requiredIfNotResultAndSiblingEquals( - store, - resultOfTestEnum.fail, - 'bar', - 'x', - )(form.controls['foo']) as Observable, - ); - - expect(result).toBeNull(); - }); - - it('should be required when result is "pass" and "bar is "x" and ' - + 'validator specifies requiredIfNotResultAndSiblingEquals "fail"/"abandoned" when sibling "bar" is "x"', async () => { - form.controls['foo'].patchValue(''); - form.controls['bar'].patchValue('x'); - - const testResult = { testTypes: [{ testResult: resultOfTestEnum.pass }] } as TestResultModel; - - store.overrideSelector(testResultInEdit, testResult); - - const result = await firstValueFrom( - CustomAsyncValidators.requiredIfNotResultAndSiblingEquals( - store, - [resultOfTestEnum.fail, resultOfTestEnum.abandoned], - 'bar', - 'x', - )(form.controls['foo']) as Observable, - ); - - expect(result).toEqual({ requiredIfNotResultAndSiblingEquals: true }); - }); + let form: FormGroup; + let store: MockStore; + + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [provideMockStore({ initialState: initialAppState })], + }); + + store = TestBed.inject(MockStore); + + form = new FormGroup({ + foo: new CustomFormControl({ name: 'foo', type: FormNodeTypes.CONTROL, children: [] }, null), + bar: new CustomFormControl({ name: 'bar', type: FormNodeTypes.CONTROL, children: [] }, null), + }); + }); + + it( + 'should be required when result is "pass" and "bar is "x" and ' + + 'validator specifies requiredIfNotResultAndSiblingEquals "fail" when sibling "bar" is "x"', + async () => { + form.controls['foo'].patchValue(''); + form.controls['bar'].patchValue('x'); + + const testResult = { testTypes: [{ testResult: resultOfTestEnum.pass }] } as TestResultModel; + + store.overrideSelector(testResultInEdit, testResult); + + const result = await firstValueFrom( + CustomAsyncValidators.requiredIfNotResultAndSiblingEquals( + store, + resultOfTestEnum.fail, + 'bar', + 'x' + )(form.controls['foo']) as Observable + ); + + expect(result).toEqual({ requiredIfNotResultAndSiblingEquals: true }); + } + ); + + it( + 'should pass validation when result is "fail" and "bar is "x" and ' + + 'validator specifies requiredIfNotResultAndSiblingEquals "fail" when sibling "bar" is "x"', + async () => { + form.controls['foo'].patchValue(''); + form.controls['bar'].patchValue('x'); + + const testResult = { testTypes: [{ testResult: resultOfTestEnum.fail }] } as TestResultModel; + + store.overrideSelector(testResultInEdit, testResult); + + const result = await firstValueFrom( + CustomAsyncValidators.requiredIfNotResultAndSiblingEquals( + store, + resultOfTestEnum.fail, + 'bar', + 'x' + )(form.controls['foo']) as Observable + ); + + expect(result).toBeNull(); + } + ); + + it( + 'should pass validation when result is "pass" and "bar is "y" and ' + + 'validator specifies requiredIfNotResultAndSiblingEquals "fail" when sibling "bar" is "x"', + async () => { + form.controls['foo'].patchValue(''); + form.controls['bar'].patchValue('y'); + + const testResult = { testTypes: [{ testResult: resultOfTestEnum.pass }] } as TestResultModel; + + store.overrideSelector(testResultInEdit, testResult); + + const result = await firstValueFrom( + CustomAsyncValidators.requiredIfNotResultAndSiblingEquals( + store, + resultOfTestEnum.fail, + 'bar', + 'x' + )(form.controls['foo']) as Observable + ); + + expect(result).toBeNull(); + } + ); + + it( + 'should be required when result is "pass" and "bar is "x" and ' + + 'validator specifies requiredIfNotResultAndSiblingEquals "fail"/"abandoned" when sibling "bar" is "x"', + async () => { + form.controls['foo'].patchValue(''); + form.controls['bar'].patchValue('x'); + + const testResult = { testTypes: [{ testResult: resultOfTestEnum.pass }] } as TestResultModel; + + store.overrideSelector(testResultInEdit, testResult); + + const result = await firstValueFrom( + CustomAsyncValidators.requiredIfNotResultAndSiblingEquals( + store, + [resultOfTestEnum.fail, resultOfTestEnum.abandoned], + 'bar', + 'x' + )(form.controls['foo']) as Observable + ); + + expect(result).toEqual({ requiredIfNotResultAndSiblingEquals: true }); + } + ); }); describe('hide if equals with condition', () => { - let form: FormGroup; - let store: MockStore; - - beforeEach(() => { - TestBed.configureTestingModule({ - providers: [provideMockStore({ initialState: initialAppState })], - }); - - store = TestBed.inject(MockStore); - - form = new FormGroup({ - foo: new CustomFormControl({ name: 'foo', type: FormNodeTypes.CONTROL, children: [] }, null), - bar: new CustomFormControl({ name: 'bar', type: FormNodeTypes.CONTROL, children: [] }, null), - }); - }); - - it('"bar" should be hidden when "foo" is "x" and testTypeId is "1" and ' - + 'validator specifies hideIfEqualsWithCondition for current field equals "x" with the condition that the ' - + '"testTypeId" field has a value in "1,2,3,4"', async () => { - form.controls['foo'].patchValue('x'); - - const testResult = { testTypes: [{ testTypeId: '1' }] } as TestResultModel; - - store.overrideSelector(testResultInEdit, testResult); - - await firstValueFrom( - CustomAsyncValidators.hideIfEqualsWithCondition(store, 'bar', 'x', { - field: 'testTypeId', - operator: operatorEnum.Equals, - value: ['1', '2', '3', '4'], - })(form.controls['foo']) as Observable, - ); - - expect((form.controls['bar'] as CustomFormControl).meta.hide).toBe(true); - }); - - it('"bar" should not be hidden when "foo" is "x" and testTypeId is "1" and ' - + 'validator specifies hideIfEqualsWithCondition for current field equals "y" with ' - + 'the condition that the "testTypeId" field has a value in "1,2,3,4"', async () => { - form.controls['foo'].patchValue('x'); - - const testResult = { testTypes: [{ testTypeId: '1' }] } as TestResultModel; - - store.overrideSelector(testResultInEdit, testResult); - - await firstValueFrom( - CustomAsyncValidators.hideIfEqualsWithCondition(store, 'bar', 'y', { - field: 'testTypeId', - operator: operatorEnum.Equals, - value: ['1', '2', '3', '4'], - })(form.controls['foo']) as Observable, - ); - - expect((form.controls['bar'] as CustomFormControl).meta.hide).toBe(false); - }); - - it('"bar" should not be hidden when "foo" is "x" and testTypeId is "5" and ' - + 'validator specifies hideIfEqualsWithCondition for current field equals "x" with ' - + 'the condition that the "testTypeId" field has a value in "1,2,3,4"', async () => { - form.controls['foo'].patchValue('x'); - - const testResult = { testTypes: [{ testTypeId: '5' }] } as TestResultModel; - - store.overrideSelector(testResultInEdit, testResult); - - await firstValueFrom( - CustomAsyncValidators.hideIfEqualsWithCondition(store, 'bar', 'x', { - field: 'testTypeId', - operator: operatorEnum.Equals, - value: ['1', '2', '3', '4'], - })(form.controls['foo']) as Observable, - ); - - expect((form.controls['bar'] as CustomFormControl).meta.hide).toBe(false); - }); - - it('"bar" should be hidden when "foo" is "x" and testTypeId is "1" and "odometerReading" is 100 and ' - + 'validator specifies hideIfEqualsWithCondition for current field equals "x" with the condition that the ' - + '"testTypeId" field has a value in "1,2,3,4" and "odometerReading" is 100', async () => { - form.controls['foo'].patchValue('x'); - - const testResult = { odometerReading: 100, testTypes: [{ testTypeId: '1' }] } as TestResultModel; - - store.overrideSelector(testResultInEdit, testResult); - - await firstValueFrom( - CustomAsyncValidators.hideIfEqualsWithCondition(store, 'bar', 'x', [ - { field: 'testTypeId', operator: operatorEnum.Equals, value: ['1', '2', '3', '4'] }, - { field: 'odometerReading', operator: operatorEnum.Equals, value: 100 }, - ])(form.controls['foo']) as Observable, - ); - - expect((form.controls['bar'] as CustomFormControl).meta.hide).toBe(true); - }); - - it('"bar" should not be hidden when "foo" is "x" and testTypeId is "1" and "odometerReading" is 101 and ' - + 'validator specifies hideIfEqualsWithCondition for current field equals "x" with the condition that the "testTypeId" ' - + 'field has a value in "1,2,3,4" and "odometerReading" is 100', async () => { - form.controls['foo'].patchValue('x'); - - const testResult = { odometerReading: 101, testTypes: [{ testTypeId: '1' }] } as TestResultModel; - - store.overrideSelector(testResultInEdit, testResult); - - await firstValueFrom( - CustomAsyncValidators.hideIfEqualsWithCondition(store, 'bar', 'x', [ - { field: 'testTypeId', operator: operatorEnum.Equals, value: ['1', '2', '3', '4'] }, - { field: 'odometerReading', operator: operatorEnum.Equals, value: 100 }, - ])(form.controls['foo']) as Observable, - ); + let form: FormGroup; + let store: MockStore; + + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [provideMockStore({ initialState: initialAppState })], + }); + + store = TestBed.inject(MockStore); + + form = new FormGroup({ + foo: new CustomFormControl({ name: 'foo', type: FormNodeTypes.CONTROL, children: [] }, null), + bar: new CustomFormControl({ name: 'bar', type: FormNodeTypes.CONTROL, children: [] }, null), + }); + }); + + it( + '"bar" should be hidden when "foo" is "x" and testTypeId is "1" and ' + + 'validator specifies hideIfEqualsWithCondition for current field equals "x" with the condition that the ' + + '"testTypeId" field has a value in "1,2,3,4"', + async () => { + form.controls['foo'].patchValue('x'); + + const testResult = { testTypes: [{ testTypeId: '1' }] } as TestResultModel; + + store.overrideSelector(testResultInEdit, testResult); + + await firstValueFrom( + CustomAsyncValidators.hideIfEqualsWithCondition(store, 'bar', 'x', { + field: 'testTypeId', + operator: operatorEnum.Equals, + value: ['1', '2', '3', '4'], + })(form.controls['foo']) as Observable + ); + + expect((form.controls['bar'] as CustomFormControl).meta.hide).toBe(true); + } + ); + + it( + '"bar" should not be hidden when "foo" is "x" and testTypeId is "1" and ' + + 'validator specifies hideIfEqualsWithCondition for current field equals "y" with ' + + 'the condition that the "testTypeId" field has a value in "1,2,3,4"', + async () => { + form.controls['foo'].patchValue('x'); + + const testResult = { testTypes: [{ testTypeId: '1' }] } as TestResultModel; + + store.overrideSelector(testResultInEdit, testResult); + + await firstValueFrom( + CustomAsyncValidators.hideIfEqualsWithCondition(store, 'bar', 'y', { + field: 'testTypeId', + operator: operatorEnum.Equals, + value: ['1', '2', '3', '4'], + })(form.controls['foo']) as Observable + ); + + expect((form.controls['bar'] as CustomFormControl).meta.hide).toBe(false); + } + ); + + it( + '"bar" should not be hidden when "foo" is "x" and testTypeId is "5" and ' + + 'validator specifies hideIfEqualsWithCondition for current field equals "x" with ' + + 'the condition that the "testTypeId" field has a value in "1,2,3,4"', + async () => { + form.controls['foo'].patchValue('x'); + + const testResult = { testTypes: [{ testTypeId: '5' }] } as TestResultModel; + + store.overrideSelector(testResultInEdit, testResult); + + await firstValueFrom( + CustomAsyncValidators.hideIfEqualsWithCondition(store, 'bar', 'x', { + field: 'testTypeId', + operator: operatorEnum.Equals, + value: ['1', '2', '3', '4'], + })(form.controls['foo']) as Observable + ); + + expect((form.controls['bar'] as CustomFormControl).meta.hide).toBe(false); + } + ); + + it( + '"bar" should be hidden when "foo" is "x" and testTypeId is "1" and "odometerReading" is 100 and ' + + 'validator specifies hideIfEqualsWithCondition for current field equals "x" with the condition that the ' + + '"testTypeId" field has a value in "1,2,3,4" and "odometerReading" is 100', + async () => { + form.controls['foo'].patchValue('x'); + + const testResult = { odometerReading: 100, testTypes: [{ testTypeId: '1' }] } as TestResultModel; + + store.overrideSelector(testResultInEdit, testResult); + + await firstValueFrom( + CustomAsyncValidators.hideIfEqualsWithCondition(store, 'bar', 'x', [ + { field: 'testTypeId', operator: operatorEnum.Equals, value: ['1', '2', '3', '4'] }, + { field: 'odometerReading', operator: operatorEnum.Equals, value: 100 }, + ])(form.controls['foo']) as Observable + ); + + expect((form.controls['bar'] as CustomFormControl).meta.hide).toBe(true); + } + ); + + it( + '"bar" should not be hidden when "foo" is "x" and testTypeId is "1" and "odometerReading" is 101 and ' + + 'validator specifies hideIfEqualsWithCondition for current field equals "x" with the condition that the "testTypeId" ' + + 'field has a value in "1,2,3,4" and "odometerReading" is 100', + async () => { + form.controls['foo'].patchValue('x'); + + const testResult = { odometerReading: 101, testTypes: [{ testTypeId: '1' }] } as TestResultModel; + + store.overrideSelector(testResultInEdit, testResult); + + await firstValueFrom( + CustomAsyncValidators.hideIfEqualsWithCondition(store, 'bar', 'x', [ + { field: 'testTypeId', operator: operatorEnum.Equals, value: ['1', '2', '3', '4'] }, + { field: 'odometerReading', operator: operatorEnum.Equals, value: 100 }, + ])(form.controls['foo']) as Observable + ); + + expect((form.controls['bar'] as CustomFormControl).meta.hide).toBe(false); + } + ); +}); - expect((form.controls['bar'] as CustomFormControl).meta.hide).toBe(false); - }); +describe('requiredWhenCarryingDangerousGoods', () => { + let form: FormGroup; + let store: MockStore; + + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [provideMockStore({ initialState: initialAppState })], + }); + + store = TestBed.inject(MockStore); + + form = new FormGroup({ + techRecord_make: new CustomFormControl( + { + name: 'techRecord_make', + type: FormNodeTypes.CONTROL, + children: [], + }, + null + ), + techRecord_adrDetails_dangerousGoods: new CustomFormControl( + { + name: 'techRecord_adrDetails_dangerousGoods', + type: FormNodeTypes.CONTROL, + children: [], + }, + null + ), + }); + }); + it('should return null if the vehicle is not of type HGV or TRL', async () => { + // Not applicable tech record vehicle type + const carTechRecord: TechRecordType<'car', 'put'> = { + techRecord_vehicleType: 'car', + vin: 'car', + techRecord_reasonForCreation: 'test', + techRecord_statusCode: 'provisional', + }; + + store.overrideSelector(editingTechRecord, carTechRecord); + + const result = CustomAsyncValidators.requiredWhenCarryingDangerousGoods(store)( + form.get('techRecord_make') as AbstractControl + ); + await expect(lastValueFrom(result)).resolves.toBeNull(); + }); + + it('should return null if the control is populated', async () => { + // Applicable vehicle tech record type + const hgvTechRecord: TechRecordType<'hgv', 'put'> = { + techRecord_vehicleType: 'hgv', + partialVin: '', + techRecord_bodyType_description: '', + techRecord_noOfAxles: 2, + techRecord_reasonForCreation: 'test', + techRecord_statusCode: 'provisional', + techRecord_vehicleClass_description: 'heavy goods vehicle', + primaryVrm: '', + vin: '', + + // Vehicle does carry dangerous goods + techRecord_adrDetails_dangerousGoods: true, + }; + + // ...but the control is populated + form.get('techRecord_make')?.patchValue('make'); + + store.overrideSelector(editingTechRecord, hgvTechRecord); + + const result = CustomAsyncValidators.requiredWhenCarryingDangerousGoods(store)( + form.get('techRecord_make') as AbstractControl + ); + await expect(lastValueFrom(result)).resolves.toBeNull(); + }); + + it('should return null if the vehicle is an ADR vehicle, but does not carry dangerous goods', async () => { + // Applicable vehicle tech record type + const hgvTechRecord: TechRecordType<'hgv', 'put'> = { + techRecord_vehicleType: 'hgv', + partialVin: '', + techRecord_bodyType_description: '', + techRecord_noOfAxles: 2, + techRecord_reasonForCreation: 'test', + techRecord_statusCode: 'provisional', + techRecord_vehicleClass_description: 'heavy goods vehicle', + primaryVrm: '', + vin: '', + + // Vehicle doesn't carry dangerous goods + techRecord_adrDetails_dangerousGoods: false, + }; + + // ...and the control isn't populated + form.get('techRecord_make')?.patchValue(null); + + store.overrideSelector(editingTechRecord, hgvTechRecord); + + const result = CustomAsyncValidators.requiredWhenCarryingDangerousGoods(store)( + form.get('techRecord_make') as AbstractControl + ); + await expect(lastValueFrom(result)).resolves.toBeNull(); + }); + + it('should return the required validation error when the vehicle is an ADR vehicle, and carries dangerous goods is selected', async () => { + // Applicable vehicle tech record type + const hgvTechRecord: TechRecordType<'hgv', 'put'> = { + techRecord_vehicleType: 'hgv', + partialVin: '', + techRecord_bodyType_description: '', + techRecord_noOfAxles: 2, + techRecord_reasonForCreation: 'test', + techRecord_statusCode: 'provisional', + techRecord_vehicleClass_description: 'heavy goods vehicle', + primaryVrm: '', + vin: '', + + // Vehicle does carry dangerous goods + techRecord_adrDetails_dangerousGoods: true, + }; + + // ...and the control isn't populated + form.get('techRecord_make')?.patchValue(null); + + store.overrideSelector(editingTechRecord, hgvTechRecord); + + const result = CustomAsyncValidators.requiredWhenCarryingDangerousGoods(store)( + form.get('techRecord_make') as AbstractControl + ); + await expect(lastValueFrom(result)).resolves.toStrictEqual({ required: true }); + }); }); describe('requiredWhenCarryingDangerousGoods', () => { - let form: FormGroup; - let store: MockStore; - - beforeEach(() => { - TestBed.configureTestingModule({ - providers: [provideMockStore({ initialState: initialAppState })], - }); - - store = TestBed.inject(MockStore); - - form = new FormGroup({ - techRecord_make: new CustomFormControl({ - name: 'techRecord_make', - type: FormNodeTypes.CONTROL, - children: [], - }, null), - techRecord_adrDetails_dangerousGoods: new CustomFormControl({ - name: 'techRecord_adrDetails_dangerousGoods', - type: FormNodeTypes.CONTROL, - children: [], - }, null), - }); - }); - it('should return null if the vehicle is not of type HGV or TRL', async () => { - // Not applicable tech record vehicle type - const carTechRecord: TechRecordType<'car', 'put'> = { - techRecord_vehicleType: 'car', - vin: 'car', - techRecord_reasonForCreation: 'test', - techRecord_statusCode: 'provisional', - }; - - store.overrideSelector(editingTechRecord, carTechRecord); - - const result = CustomAsyncValidators.requiredWhenCarryingDangerousGoods(store)(form.get('techRecord_make') as AbstractControl); - await expect(lastValueFrom(result)).resolves.toBeNull(); - }); - - it('should return null if the control is populated', async () => { - // Applicable vehicle tech record type - const hgvTechRecord: TechRecordType<'hgv', 'put'> = { - techRecord_vehicleType: 'hgv', - partialVin: '', - techRecord_bodyType_description: '', - techRecord_noOfAxles: 2, - techRecord_reasonForCreation: 'test', - techRecord_statusCode: 'provisional', - techRecord_vehicleClass_description: 'heavy goods vehicle', - primaryVrm: '', - vin: '', - - // Vehicle does carry dangerous goods - techRecord_adrDetails_dangerousGoods: true, - }; - - // ...but the control is populated - form.get('techRecord_make')?.patchValue('make'); - - store.overrideSelector(editingTechRecord, hgvTechRecord); - - const result = CustomAsyncValidators.requiredWhenCarryingDangerousGoods(store)(form.get('techRecord_make') as AbstractControl); - await expect(lastValueFrom(result)).resolves.toBeNull(); - }); - - it('should return null if the vehicle is an ADR vehicle, but does not carry dangerous goods', async () => { - // Applicable vehicle tech record type - const hgvTechRecord: TechRecordType<'hgv', 'put'> = { - techRecord_vehicleType: 'hgv', - partialVin: '', - techRecord_bodyType_description: '', - techRecord_noOfAxles: 2, - techRecord_reasonForCreation: 'test', - techRecord_statusCode: 'provisional', - techRecord_vehicleClass_description: 'heavy goods vehicle', - primaryVrm: '', - vin: '', - - // Vehicle doesn't carry dangerous goods - techRecord_adrDetails_dangerousGoods: false, - }; - - // ...and the control isn't populated - form.get('techRecord_make')?.patchValue(null); - - store.overrideSelector(editingTechRecord, hgvTechRecord); - - const result = CustomAsyncValidators.requiredWhenCarryingDangerousGoods(store)(form.get('techRecord_make') as AbstractControl); - await expect(lastValueFrom(result)).resolves.toBeNull(); - }); - - it('should return the required validation error when the vehicle is an ADR vehicle, and carries dangerous goods is selected', async () => { - // Applicable vehicle tech record type - const hgvTechRecord: TechRecordType<'hgv', 'put'> = { - techRecord_vehicleType: 'hgv', - partialVin: '', - techRecord_bodyType_description: '', - techRecord_noOfAxles: 2, - techRecord_reasonForCreation: 'test', - techRecord_statusCode: 'provisional', - techRecord_vehicleClass_description: 'heavy goods vehicle', - primaryVrm: '', - vin: '', - - // Vehicle does carry dangerous goods - techRecord_adrDetails_dangerousGoods: true, - }; - - // ...and the control isn't populated - form.get('techRecord_make')?.patchValue(null); - - store.overrideSelector(editingTechRecord, hgvTechRecord); - - const result = CustomAsyncValidators.requiredWhenCarryingDangerousGoods(store)(form.get('techRecord_make') as AbstractControl); - await expect(lastValueFrom(result)).resolves.toStrictEqual({ required: true }); - }); + let form: FormGroup; + let store: MockStore; + + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [provideMockStore({ initialState: initialAppState })], + }); + + store = TestBed.inject(MockStore); + + form = new FormGroup({ + techRecord_make: new CustomFormControl( + { + name: 'techRecord_make', + type: FormNodeTypes.CONTROL, + children: [], + }, + null + ), + techRecord_adrDetails_dangerousGoods: new CustomFormControl( + { + name: 'techRecord_adrDetails_dangerousGoods', + type: FormNodeTypes.CONTROL, + children: [], + }, + null + ), + }); + }); + it('should return null if the vehicle is not of type HGV or TRL', async () => { + // Not applicable tech record vehicle type + const carTechRecord: TechRecordType<'car', 'put'> = { + techRecord_vehicleType: 'car', + vin: 'car', + techRecord_reasonForCreation: 'test', + techRecord_statusCode: 'provisional', + }; + + store.overrideSelector(editingTechRecord, carTechRecord); + + const result = CustomAsyncValidators.requiredWhenCarryingDangerousGoods(store)( + form.get('techRecord_make') as AbstractControl + ); + await expect(lastValueFrom(result)).resolves.toBeNull(); + }); + + it('should return null if the control is populated', async () => { + // Applicable vehicle tech record type + const hgvTechRecord: TechRecordType<'hgv', 'put'> = { + techRecord_vehicleType: 'hgv', + partialVin: '', + techRecord_bodyType_description: '', + techRecord_noOfAxles: 2, + techRecord_reasonForCreation: 'test', + techRecord_statusCode: 'provisional', + techRecord_vehicleClass_description: 'heavy goods vehicle', + primaryVrm: '', + vin: '', + + // Vehicle does carry dangerous goods + techRecord_adrDetails_dangerousGoods: true, + }; + + // ...but the control is populated + form.get('techRecord_make')?.patchValue('make'); + + store.overrideSelector(editingTechRecord, hgvTechRecord); + + const result = CustomAsyncValidators.requiredWhenCarryingDangerousGoods(store)( + form.get('techRecord_make') as AbstractControl + ); + await expect(lastValueFrom(result)).resolves.toBeNull(); + }); + + it('should return null if the vehicle is an ADR vehicle, but does not carry dangerous goods', async () => { + // Applicable vehicle tech record type + const hgvTechRecord: TechRecordType<'hgv', 'put'> = { + techRecord_vehicleType: 'hgv', + partialVin: '', + techRecord_bodyType_description: '', + techRecord_noOfAxles: 2, + techRecord_reasonForCreation: 'test', + techRecord_statusCode: 'provisional', + techRecord_vehicleClass_description: 'heavy goods vehicle', + primaryVrm: '', + vin: '', + + // Vehicle doesn't carry dangerous goods + techRecord_adrDetails_dangerousGoods: false, + }; + + // ...and the control isn't populated + form.get('techRecord_make')?.patchValue(null); + + store.overrideSelector(editingTechRecord, hgvTechRecord); + + const result = CustomAsyncValidators.requiredWhenCarryingDangerousGoods(store)( + form.get('techRecord_make') as AbstractControl + ); + await expect(lastValueFrom(result)).resolves.toBeNull(); + }); + + it('should return the required validation error when the vehicle is an ADR vehicle, and carries dangerous goods is selected', async () => { + // Applicable vehicle tech record type + const hgvTechRecord: TechRecordType<'hgv', 'put'> = { + techRecord_vehicleType: 'hgv', + partialVin: '', + techRecord_bodyType_description: '', + techRecord_noOfAxles: 2, + techRecord_reasonForCreation: 'test', + techRecord_statusCode: 'provisional', + techRecord_vehicleClass_description: 'heavy goods vehicle', + primaryVrm: '', + vin: '', + + // Vehicle does carry dangerous goods + techRecord_adrDetails_dangerousGoods: true, + }; + + // ...and the control isn't populated + form.get('techRecord_make')?.patchValue(null); + + store.overrideSelector(editingTechRecord, hgvTechRecord); + + const result = CustomAsyncValidators.requiredWhenCarryingDangerousGoods(store)( + form.get('techRecord_make') as AbstractControl + ); + await expect(lastValueFrom(result)).resolves.toStrictEqual({ required: true }); + }); }); diff --git a/src/app/forms/validators/custom-async-validators.ts b/src/app/forms/validators/custom-async-validators.ts index 395ed33793..228338da9e 100644 --- a/src/app/forms/validators/custom-async-validators.ts +++ b/src/app/forms/validators/custom-async-validators.ts @@ -1,6 +1,4 @@ -import { - AbstractControl, AsyncValidatorFn, ValidationErrors, Validators, -} from '@angular/forms'; +import { AbstractControl, AsyncValidatorFn, ValidationErrors, Validators } from '@angular/forms'; import { Condition, operatorEnum } from '@forms/models/condition.model'; // eslint-disable-next-line import/no-cycle import { CustomFormControl } from '@forms/services/dynamic-form.types'; @@ -14,253 +12,297 @@ import { selectUserByResourceKey } from '@store/reference-data'; import { editingTechRecord } from '@store/technical-records'; import { testResultInEdit } from '@store/test-records'; import { getTestStationFromProperty } from '@store/test-stations'; -import { - Observable, catchError, map, of, take, tap, -} from 'rxjs'; +import { Observable, catchError, map, of, take, tap } from 'rxjs'; export class CustomAsyncValidators { - static resultDependantOnCustomDefects(store: Store): AsyncValidatorFn { - return CustomAsyncValidators.checkResultDependantOnCustomDefects(store, [resultOfTestEnum.pass, resultOfTestEnum.fail, resultOfTestEnum.prs]); - } + static resultDependantOnCustomDefects(store: Store): AsyncValidatorFn { + return CustomAsyncValidators.checkResultDependantOnCustomDefects(store, [ + resultOfTestEnum.pass, + resultOfTestEnum.fail, + resultOfTestEnum.prs, + ]); + } - static resultDependantOnRequiredStandards(store: Store): AsyncValidatorFn { - return CustomAsyncValidators.checkResultDependantOnRequiredStandards(store, [resultOfTestEnum.pass, resultOfTestEnum.fail, resultOfTestEnum.prs]); - } + static resultDependantOnRequiredStandards(store: Store): AsyncValidatorFn { + return CustomAsyncValidators.checkResultDependantOnRequiredStandards(store, [ + resultOfTestEnum.pass, + resultOfTestEnum.fail, + resultOfTestEnum.prs, + ]); + } - static passResultDependantOnCustomDefects(store: Store): AsyncValidatorFn { - return CustomAsyncValidators.checkResultDependantOnCustomDefects(store, resultOfTestEnum.pass); - } + static passResultDependantOnCustomDefects(store: Store): AsyncValidatorFn { + return CustomAsyncValidators.checkResultDependantOnCustomDefects(store, resultOfTestEnum.pass); + } - static checkResultDependantOnCustomDefects(store: Store, limitToResult: resultOfTestEnum | resultOfTestEnum[]): AsyncValidatorFn { - return (control: AbstractControl): Observable => - store.pipe( - take(1), - select(testResultInEdit), - map((testResult) => { - const hasCustomDefects = testResult?.testTypes?.some((testType) => testType?.customDefects && testType.customDefects.length > 0); + static checkResultDependantOnCustomDefects( + store: Store, + limitToResult: resultOfTestEnum | resultOfTestEnum[] + ): AsyncValidatorFn { + return (control: AbstractControl): Observable => + store.pipe( + take(1), + select(testResultInEdit), + map((testResult) => { + const hasCustomDefects = testResult?.testTypes?.some( + (testType) => testType?.customDefects && testType.customDefects.length > 0 + ); - if ( - control.value === 'pass' - && hasCustomDefects - && (!limitToResult || Array.isArray(limitToResult) - ? limitToResult.includes(resultOfTestEnum.pass) : limitToResult === resultOfTestEnum.pass) - ) { - return { invalidTestResult: { message: 'Cannot pass test when defects are present' } }; - } if ( - control.value === 'fail' - && !hasCustomDefects - && (!limitToResult || Array.isArray(limitToResult) - ? limitToResult.includes(resultOfTestEnum.fail) : limitToResult === resultOfTestEnum.fail) - ) { - return { invalidTestResult: { message: 'Cannot fail test when no defects are present' } }; - } if ( - control.value === 'prs' - && !hasCustomDefects - && (!limitToResult || Array.isArray(limitToResult) - ? limitToResult.includes(resultOfTestEnum.prs) : limitToResult === resultOfTestEnum.prs) - ) { - return { invalidTestResult: { message: 'Cannot mark test as PRS when no defects are present' } }; - } - return null; + if ( + control.value === 'pass' && + hasCustomDefects && + (!limitToResult || Array.isArray(limitToResult) + ? limitToResult.includes(resultOfTestEnum.pass) + : limitToResult === resultOfTestEnum.pass) + ) { + return { invalidTestResult: { message: 'Cannot pass test when defects are present' } }; + } + if ( + control.value === 'fail' && + !hasCustomDefects && + (!limitToResult || Array.isArray(limitToResult) + ? limitToResult.includes(resultOfTestEnum.fail) + : limitToResult === resultOfTestEnum.fail) + ) { + return { invalidTestResult: { message: 'Cannot fail test when no defects are present' } }; + } + if ( + control.value === 'prs' && + !hasCustomDefects && + (!limitToResult || Array.isArray(limitToResult) + ? limitToResult.includes(resultOfTestEnum.prs) + : limitToResult === resultOfTestEnum.prs) + ) { + return { invalidTestResult: { message: 'Cannot mark test as PRS when no defects are present' } }; + } + return null; + }) + ); + } - }), - ); - } + static checkResultDependantOnRequiredStandards( + store: Store, + limitToResult: resultOfTestEnum | resultOfTestEnum[] + ): AsyncValidatorFn { + return (control: AbstractControl): Observable => + store.pipe( + take(1), + select(testResultInEdit), + map((testResult) => { + const hasRequiredStandards = testResult?.testTypes?.some( + (testType) => testType?.requiredStandards && testType.requiredStandards.length > 0 + ); - static checkResultDependantOnRequiredStandards(store: Store, limitToResult: resultOfTestEnum | resultOfTestEnum[]): AsyncValidatorFn { - return (control: AbstractControl): Observable => - store.pipe( - take(1), - select(testResultInEdit), - map((testResult) => { - const hasRequiredStandards = testResult?.testTypes?.some(( - testType, - ) => testType?.requiredStandards && testType.requiredStandards.length > 0); + if ( + control.value === 'pass' && + hasRequiredStandards && + (!limitToResult || Array.isArray(limitToResult) + ? limitToResult.includes(resultOfTestEnum.pass) + : limitToResult === resultOfTestEnum.pass) + ) { + return { invalidTestResult: { message: 'Cannot pass test when required standards are present' } }; + } + if ( + control.value === 'fail' && + !hasRequiredStandards && + (!limitToResult || Array.isArray(limitToResult) + ? limitToResult.includes(resultOfTestEnum.fail) + : limitToResult === resultOfTestEnum.fail) + ) { + return { invalidTestResult: { message: 'Cannot fail test when no required standards are present' } }; + } + if ( + control.value === 'prs' && + !hasRequiredStandards && + (!limitToResult || Array.isArray(limitToResult) + ? limitToResult.includes(resultOfTestEnum.prs) + : limitToResult === resultOfTestEnum.prs) + ) { + return { invalidTestResult: { message: 'Cannot mark test as PRS when no required standards are present' } }; + } + return null; + }) + ); + } - if ( - control.value === 'pass' - && hasRequiredStandards - && (!limitToResult || Array.isArray(limitToResult) - ? limitToResult.includes(resultOfTestEnum.pass) : limitToResult === resultOfTestEnum.pass) - ) { - return { invalidTestResult: { message: 'Cannot pass test when required standards are present' } }; - } if ( - control.value === 'fail' - && !hasRequiredStandards - && (!limitToResult || Array.isArray(limitToResult) - ? limitToResult.includes(resultOfTestEnum.fail) : limitToResult === resultOfTestEnum.fail) - ) { - return { invalidTestResult: { message: 'Cannot fail test when no required standards are present' } }; - } if ( - control.value === 'prs' - && !hasRequiredStandards - && (!limitToResult || Array.isArray(limitToResult) - ? limitToResult.includes(resultOfTestEnum.prs) : limitToResult === resultOfTestEnum.prs) - ) { - return { invalidTestResult: { message: 'Cannot mark test as PRS when no required standards are present' } }; - } - return null; + static updateTestStationDetails(store: Store): AsyncValidatorFn { + return (control: AbstractControl): Observable => { + return store.pipe( + select( + getTestStationFromProperty((control as CustomFormControl).meta.name as keyof TestStation, control.value) + ), + take(1), + tap((stations) => { + const testStationName = control.parent?.get('testStationName'); + const testStationType = control.parent?.get('testStationType'); + if (stations) { + if (testStationName) { + testStationName.setValue(stations.testStationName, { emitEvent: true, onlySelf: true }); + } + if (testStationType) { + testStationType.setValue(stations.testStationType, { emitEvent: true, onlySelf: true }); + } + } + }), + map(() => null), + catchError(() => of(null)) + ); + }; + } - }), - ); - } + static updateTesterDetails(store: Store): AsyncValidatorFn { + return (control: AbstractControl): Observable => { + return store.pipe( + select(selectUserByResourceKey(control.value)), + take(1), + tap((user) => { + const testerName = control.parent?.get('testerName'); + const testerEmail = control.parent?.get('testerEmailAddress'); + if (user && testerName && testerEmail) { + testerName.setValue((user as User).name, { emitEvent: false, onlySelf: true }); + testerEmail.setValue((user as User).email, { emitEvent: false, onlySelf: true }); + } + }), + map(() => null), + catchError(() => of(null)) + ); + }; + } - static updateTestStationDetails(store: Store): AsyncValidatorFn { - return (control: AbstractControl): Observable => { - return store.pipe( - select(getTestStationFromProperty((control as CustomFormControl).meta.name as keyof TestStation, control.value)), - take(1), - tap((stations) => { - const testStationName = control.parent?.get('testStationName'); - const testStationType = control.parent?.get('testStationType'); - if (stations) { - if (testStationName) { - testStationName.setValue(stations.testStationName, { emitEvent: true, onlySelf: true }); - } - if (testStationType) { - testStationType.setValue(stations.testStationType, { emitEvent: true, onlySelf: true }); - } - } - }), - map(() => null), - catchError(() => of(null)), - ); - }; - } + static requiredIfNotResult(store: Store, result: resultOfTestEnum | resultOfTestEnum[]): AsyncValidatorFn { + return (control: AbstractControl): Observable => + store.pipe( + take(1), + select(testResultInEdit), + map((testResult) => { + const currentResult = testResult?.testTypes[0].testResult; + if ( + (Array.isArray(result) ? currentResult && !result.includes(currentResult) : currentResult !== result) && + (control.value == null || control.value === '') + ) { + if (Array.isArray(result)) return { requiredIfNotResult: true }; + return { [`requiredIfNot${result}`]: true }; + } + return null; + }) + ); + } - static updateTesterDetails(store: Store): AsyncValidatorFn { - return (control: AbstractControl): Observable => { - return store.pipe( - select(selectUserByResourceKey(control.value)), - take(1), - tap((user) => { - const testerName = control.parent?.get('testerName'); - const testerEmail = control.parent?.get('testerEmailAddress'); - if (user && testerName && testerEmail) { - testerName.setValue((user as User).name, { emitEvent: false, onlySelf: true }); - testerEmail.setValue((user as User).email, { emitEvent: false, onlySelf: true }); - } - }), - map(() => null), - catchError(() => of(null)), - ); - }; - } + static requiredIfNotFail(store: Store): AsyncValidatorFn { + return this.requiredIfNotResult(store, resultOfTestEnum.fail); + } - static requiredIfNotResult(store: Store, result: resultOfTestEnum | resultOfTestEnum[]): AsyncValidatorFn { - return (control: AbstractControl): Observable => - store.pipe( - take(1), - select(testResultInEdit), - map((testResult) => { - const currentResult = testResult?.testTypes[0].testResult; - if ( - (Array.isArray(result) ? currentResult && !result.includes(currentResult) : currentResult !== result) - && (control.value == null || control.value === '') - ) { - if (Array.isArray(result)) return { requiredIfNotResult: true }; - return { [`requiredIfNot${result}`]: true }; - } - return null; - }), - ); - } + static requiredIfNotAbandoned(store: Store): AsyncValidatorFn { + return this.requiredIfNotResult(store, resultOfTestEnum.abandoned); + } - static requiredIfNotFail(store: Store): AsyncValidatorFn { - return this.requiredIfNotResult(store, resultOfTestEnum.fail); - } + static requiredIfNotResultAndSiblingEquals( + store: Store, + result: resultOfTestEnum | resultOfTestEnum[], + sibling: string, + value: unknown + ): AsyncValidatorFn { + return (control: AbstractControl): Observable => + store.pipe( + take(1), + select(testResultInEdit), + map((testResult) => { + if (control?.parent) { + const siblingControl = control.parent.get(sibling) as CustomFormControl; + const siblingValue = siblingControl.value; + const newValue = Array.isArray(value) ? value.includes(siblingValue) : siblingValue === value; - static requiredIfNotAbandoned(store: Store): AsyncValidatorFn { - return this.requiredIfNotResult(store, resultOfTestEnum.abandoned); - } + const currentResult = testResult?.testTypes[0].testResult; - static requiredIfNotResultAndSiblingEquals( - store: Store, - result: resultOfTestEnum | resultOfTestEnum[], - sibling: string, - value: unknown, - ): AsyncValidatorFn { - return (control: AbstractControl): Observable => - store.pipe( - take(1), - select(testResultInEdit), - map((testResult) => { - if (control?.parent) { - const siblingControl = control.parent.get(sibling) as CustomFormControl; - const siblingValue = siblingControl.value; - const newValue = Array.isArray(value) ? value.includes(siblingValue) : siblingValue === value; + if ( + (Array.isArray(result) ? currentResult && !result.includes(currentResult) : currentResult !== result) && + newValue && + (control.value === null || control.value === undefined || control.value === '') + ) { + return { requiredIfNotResultAndSiblingEquals: true }; + } + } - const currentResult = testResult?.testTypes[0].testResult; + return null; + }) + ); + } - if ( - (Array.isArray(result) ? currentResult && !result.includes(currentResult) : currentResult !== result) - && newValue - && (control.value === null || control.value === undefined || control.value === '') - ) { - return { requiredIfNotResultAndSiblingEquals: true }; - } - } + static hideIfEqualsWithCondition( + store: Store, + sibling: string, + value: string, + conditions: Condition | Condition[] + ): AsyncValidatorFn { + return (control: AbstractControl): Observable => + store.pipe( + take(1), + select(testResultInEdit), + map((testResult) => { + if (!testResult || !control?.parent) { + return null; + } - return null; - }), - ); - } + const siblingControl = control.parent.get(sibling) as CustomFormControl; - static hideIfEqualsWithCondition(store: Store, sibling: string, value: string, conditions: Condition | Condition[]): AsyncValidatorFn { - return (control: AbstractControl): Observable => - store.pipe( - take(1), - select(testResultInEdit), - map((testResult) => { - if (!testResult || !control?.parent) { - return null; - } + const conditionsPassed = CustomAsyncValidators.checkConditions(testResult, conditions); - const siblingControl = control.parent.get(sibling) as CustomFormControl; + siblingControl.meta.hide = + conditionsPassed && (Array.isArray(value) ? value.includes(control.value) : control.value === value); - const conditionsPassed = CustomAsyncValidators.checkConditions(testResult, conditions); + return null; + }) + ); + } - siblingControl.meta.hide = conditionsPassed && (Array.isArray(value) ? value.includes(control.value) : control.value === value); + static requiredWhenCarryingDangerousGoods = (store: Store) => { + return (control: AbstractControl): Observable => { + return store.select(editingTechRecord).pipe( + take(1), + map((form) => { + if ( + form && + (form.techRecord_vehicleType === 'hgv' || form.techRecord_vehicleType === 'trl') && + form.techRecord_adrDetails_dangerousGoods + ) { + return Validators.required(control); + } - return null; - }), - ); - } + return null; + }) + ); + }; + }; - static requiredWhenCarryingDangerousGoods = (store: Store) => { - return (control: AbstractControl): Observable => { - return store.select(editingTechRecord).pipe(take(1), map((form) => { - if ( - form - && (form.techRecord_vehicleType === 'hgv' || form.techRecord_vehicleType === 'trl') - && (form.techRecord_adrDetails_dangerousGoods)) { - return Validators.required(control); - } + private static checkConditions(testResult: TestResultModel, conditions: Condition | Condition[]) { + if (!Array.isArray(conditions)) { + return CustomAsyncValidators.checkCondition(testResult, conditions); + } - return null; - })); - }; - }; + return conditions.every((condition) => CustomAsyncValidators.checkCondition(testResult, condition)); + } - private static checkConditions(testResult: TestResultModel, conditions: Condition | Condition[]) { - if (!Array.isArray(conditions)) { - return CustomAsyncValidators.checkCondition(testResult, conditions); - } + private static checkCondition(testResult: TestResultModel, condition: Condition) { + const { field, operator, value } = condition; - return conditions.every((condition) => CustomAsyncValidators.checkCondition(testResult, condition)); - } + // eslint-disable-next-line no-prototype-builtins + const fieldValue = testResult.testTypes[0].hasOwnProperty(field) + ? // eslint-disable-next-line @typescript-eslint/no-explicit-any, security/detect-object-injection + (testResult.testTypes[0] as any)[field] + : testResult[field as keyof TestResultModel]; - private static checkCondition(testResult: TestResultModel, condition: Condition) { - const { field, operator, value } = condition; + const isTrue = Array.isArray(value) ? value.includes(fieldValue) : fieldValue === value; - // eslint-disable-next-line no-prototype-builtins - const fieldValue = testResult.testTypes[0].hasOwnProperty(field) - // eslint-disable-next-line @typescript-eslint/no-explicit-any, security/detect-object-injection - ? (testResult.testTypes[0] as any)[field] - : (testResult)[field as keyof TestResultModel]; + return operator === operatorEnum.Equals ? isTrue : !isTrue; + } - const isTrue = Array.isArray(value) ? value.includes(fieldValue) : fieldValue === value; - - return operator === operatorEnum.Equals ? isTrue : !isTrue; - } + static custom = ( + store: Store, + func: (...args: unknown[]) => Observable, + ...args: unknown[] + ) => { + return (control: AbstractControl): Observable => { + return func(control, store, ...args); + }; + }; } diff --git a/src/app/forms/validators/custom-validators.spec.ts b/src/app/forms/validators/custom-validators.spec.ts index ca5fa5b20e..24533c2ff9 100644 --- a/src/app/forms/validators/custom-validators.spec.ts +++ b/src/app/forms/validators/custom-validators.spec.ts @@ -1,2074 +1,2148 @@ -import { - AbstractControl, - FormArray, - FormControl, FormGroup, -} from '@angular/forms'; +import { AbstractControl, FormArray, FormControl, FormGroup } from '@angular/forms'; import { ADRDangerousGood } from '@dvsa/cvs-type-definitions/types/v3/tech-record/enums/adrDangerousGood.enum.js'; import { ApprovalType } from '@dvsa/cvs-type-definitions/types/v3/tech-record/enums/approvalType.enum.js'; -import { - VehicleClassDescription, -} from '@dvsa/cvs-type-definitions/types/v3/tech-record/enums/vehicleClassDescription.enum.js'; +import { VehicleClassDescription } from '@dvsa/cvs-type-definitions/types/v3/tech-record/enums/vehicleClassDescription.enum.js'; import { ValidatorNames } from '@forms/models/validators.enum'; -import { - CustomFormControl, - CustomFormGroup, - FormNodeTypes, -} from '@forms/services/dynamic-form.types'; +import { CustomFormControl, CustomFormGroup, FormNodeTypes } from '@forms/services/dynamic-form.types'; import { VehicleSizes, VehicleTypes } from '@models/vehicle-tech-record.model'; import { CustomValidators } from './custom-validators'; interface CustomPatternMessage { - customPattern: { - message: string; - }; + customPattern: { + message: string; + }; } describe('Hiding validators', () => { - let form: FormGroup; - - beforeEach(() => { - form = new FormGroup({ - foo: new CustomFormControl({ name: 'foo', type: FormNodeTypes.CONTROL, children: [] }, null), - sibling: new CustomFormControl({ name: 'sibling', type: FormNodeTypes.CONTROL, children: [] }, null), - }); - }); - - describe('Hide if empty', () => { - it('should return null', () => { - expect(CustomValidators.hideIfEmpty('foo')(form.controls['sibling'])).toBeNull(); - }); - - it('should set meta.hide to true if content of sibling is empty', () => { - CustomValidators.hideIfEmpty('foo')(form.controls['sibling']); - expect((form.controls['foo'] as CustomFormControl).meta.hide).toBe(true); - }); - - it('should set meta.hide to false if content of sibling is not empty', () => { - (form.controls['foo'] as CustomFormControl).meta.hide = true; - form.controls['sibling'].patchValue('bar'); - CustomValidators.hideIfEmpty('foo')(form.controls['sibling']); - expect((form.controls['foo'] as CustomFormControl).meta.hide).toBe(false); - }); - }); - - describe('Hide if equals', () => { - it('should return null', () => { - expect(CustomValidators.hideIfEquals('foo', 'foo')(form.controls['sibling'])).toBeNull(); - }); - - it('should set meta.hide to true if content of sibling is equal to the value passed', () => { - const value = 'bar'; - form.controls['sibling'].patchValue(value); - CustomValidators.hideIfEquals('foo', 'bar')(form.controls['sibling']); - expect((form.controls['foo'] as CustomFormControl).meta.hide).toBe(true); - }); - - it('should set meta.hide to true if content of sibling is equal to one of the values passed in the array', () => { - const value = 'bar'; - form.controls['sibling'].patchValue(value); - CustomValidators.hideIfEquals('foo', ['bar', 'foo', 'foobar'])(form.controls['sibling']); - expect((form.controls['foo'] as CustomFormControl).meta.hide).toBe(true); - }); - - it('should set meta.hide to false if content of sibling is not equal to the value passed', () => { - const value = 'bar'; - form.controls['sibling'].patchValue(value); - (form.controls['foo'] as CustomFormControl).meta.hide = true; - CustomValidators.hideIfEquals('foo', 'foo')(form.controls['sibling']); - expect((form.controls['foo'] as CustomFormControl).meta.hide).toBe(false); - }); - }); - - describe('Hide if not equals', () => { - it('should return null', () => { - expect(CustomValidators.hideIfNotEqual('foo', 'foo')(form.controls['sibling'])).toBeNull(); - }); - - it('should set meta.hide to true if content of sibling is not equal to the value passed', () => { - const value = 'foo'; - form.controls['sibling'].patchValue(value); - CustomValidators.hideIfNotEqual('foo', 'bar')(form.controls['sibling']); - expect((form.controls['foo'] as CustomFormControl).meta.hide).toBe(true); - }); - - it('should set meta.hide to true if content of sibling is not equal to any of the values passed in the array', () => { - const value = 'value'; - form.controls['sibling'].patchValue(value); - CustomValidators.hideIfNotEqual('foo', ['bar', 'foo', 'foobar'])(form.controls['sibling']); - expect((form.controls['foo'] as CustomFormControl).meta.hide).toBe(true); - }); - - it('should set meta.hide to false if content of sibling is equal to the value passed', () => { - const value = 'bar'; - form.controls['sibling'].patchValue(value); - (form.controls['foo'] as CustomFormControl).meta.hide = true; - CustomValidators.hideIfNotEqual('foo', 'bar')(form.controls['sibling']); - expect((form.controls['foo'] as CustomFormControl).meta.hide).toBe(false); - }); - }); + let form: FormGroup; + + beforeEach(() => { + form = new FormGroup({ + foo: new CustomFormControl({ name: 'foo', type: FormNodeTypes.CONTROL, children: [] }, null), + sibling: new CustomFormControl({ name: 'sibling', type: FormNodeTypes.CONTROL, children: [] }, null), + }); + }); + + describe('Hide if empty', () => { + it('should return null', () => { + expect(CustomValidators.hideIfEmpty('foo')(form.controls['sibling'])).toBeNull(); + }); + + it('should set meta.hide to true if content of sibling is empty', () => { + CustomValidators.hideIfEmpty('foo')(form.controls['sibling']); + expect((form.controls['foo'] as CustomFormControl).meta.hide).toBe(true); + }); + + it('should set meta.hide to false if content of sibling is not empty', () => { + (form.controls['foo'] as CustomFormControl).meta.hide = true; + form.controls['sibling'].patchValue('bar'); + CustomValidators.hideIfEmpty('foo')(form.controls['sibling']); + expect((form.controls['foo'] as CustomFormControl).meta.hide).toBe(false); + }); + }); + + describe('Hide if equals', () => { + it('should return null', () => { + expect(CustomValidators.hideIfEquals('foo', 'foo')(form.controls['sibling'])).toBeNull(); + }); + + it('should set meta.hide to true if content of sibling is equal to the value passed', () => { + const value = 'bar'; + form.controls['sibling'].patchValue(value); + CustomValidators.hideIfEquals('foo', 'bar')(form.controls['sibling']); + expect((form.controls['foo'] as CustomFormControl).meta.hide).toBe(true); + }); + + it('should set meta.hide to true if content of sibling is equal to one of the values passed in the array', () => { + const value = 'bar'; + form.controls['sibling'].patchValue(value); + CustomValidators.hideIfEquals('foo', ['bar', 'foo', 'foobar'])(form.controls['sibling']); + expect((form.controls['foo'] as CustomFormControl).meta.hide).toBe(true); + }); + + it('should set meta.hide to false if content of sibling is not equal to the value passed', () => { + const value = 'bar'; + form.controls['sibling'].patchValue(value); + (form.controls['foo'] as CustomFormControl).meta.hide = true; + CustomValidators.hideIfEquals('foo', 'foo')(form.controls['sibling']); + expect((form.controls['foo'] as CustomFormControl).meta.hide).toBe(false); + }); + }); + + describe('Hide if not equals', () => { + it('should return null', () => { + expect(CustomValidators.hideIfNotEqual('foo', 'foo')(form.controls['sibling'])).toBeNull(); + }); + + it('should set meta.hide to true if content of sibling is not equal to the value passed', () => { + const value = 'foo'; + form.controls['sibling'].patchValue(value); + CustomValidators.hideIfNotEqual('foo', 'bar')(form.controls['sibling']); + expect((form.controls['foo'] as CustomFormControl).meta.hide).toBe(true); + }); + + it('should set meta.hide to true if content of sibling is not equal to any of the values passed in the array', () => { + const value = 'value'; + form.controls['sibling'].patchValue(value); + CustomValidators.hideIfNotEqual('foo', ['bar', 'foo', 'foobar'])(form.controls['sibling']); + expect((form.controls['foo'] as CustomFormControl).meta.hide).toBe(true); + }); + + it('should set meta.hide to false if content of sibling is equal to the value passed', () => { + const value = 'bar'; + form.controls['sibling'].patchValue(value); + (form.controls['foo'] as CustomFormControl).meta.hide = true; + CustomValidators.hideIfNotEqual('foo', 'bar')(form.controls['sibling']); + expect((form.controls['foo'] as CustomFormControl).meta.hide).toBe(false); + }); + }); }); describe('enable/disable validators', () => { - let form: FormGroup; - - beforeEach(() => { - form = new FormGroup({ - foo: new CustomFormControl({ name: 'foo', type: FormNodeTypes.CONTROL, children: [] }, null), - sibling: new CustomFormControl({ name: 'sibling', type: FormNodeTypes.CONTROL, children: [] }, null), - }); - }); - - describe('enable if equals', () => { - it('should return null', () => { - expect(CustomValidators.enableIfEquals('foo', 'foo')(form.controls['sibling'])).toBeNull(); - }); - - it('should set enabled to true if content of sibling is equal to the value passed', () => { - const value = 'bar'; - form.controls['sibling'].patchValue(value); - CustomValidators.enableIfEquals('foo', 'bar')(form.controls['sibling']); - expect((form.controls['foo'] as CustomFormControl).enabled).toBe(true); - }); - - it('should set enabled to true if content of sibling is equal to one of the values passed in the array', () => { - const value = 'bar'; - form.controls['sibling'].patchValue(value); - CustomValidators.enableIfEquals('foo', ['bar', 'foo', 'foobar'])(form.controls['sibling']); - expect((form.controls['foo'] as CustomFormControl).enabled).toBe(true); - }); - - it('should set enabled to false if content of sibling is not equal to the value passed', () => { - const value = 'bar'; - form.controls['sibling'].patchValue(value); - (form.controls['foo'] as CustomFormControl).enable(); - CustomValidators.enableIfEquals('foo', 'foo')(form.controls['sibling']); - expect((form.controls['foo'] as CustomFormControl).enabled).toBe(false); - }); - }); - - describe('disable if equals', () => { - it('should return null', () => { - expect(CustomValidators.disableIfEquals('foo', 'foo')(form.controls['sibling'])).toBeNull(); - }); - - it('should set disabled to true if content of sibling is equal to the value passed', () => { - const value = 'bar'; - form.controls['sibling'].patchValue(value); - CustomValidators.disableIfEquals('foo', 'bar')(form.controls['sibling']); - expect((form.controls['foo'] as CustomFormControl).disabled).toBe(true); - }); - - it('should set disabled to true if content of sibling is equal to one of the values passed in the array', () => { - const value = 'bar'; - form.controls['sibling'].patchValue(value); - CustomValidators.disableIfEquals('foo', ['bar', 'foo', 'foobar'])(form.controls['sibling']); - expect((form.controls['foo'] as CustomFormControl).disabled).toBe(true); - }); - - it('should set disabled to false if content of sibling is not equal to the value passed', () => { - const value = 'bar'; - form.controls['sibling'].patchValue(value); - (form.controls['foo'] as CustomFormControl).disable(); - CustomValidators.disableIfEquals('foo', 'foo')(form.controls['sibling']); - expect((form.controls['foo'] as CustomFormControl).disabled).toBe(false); - }); - }); + let form: FormGroup; + + beforeEach(() => { + form = new FormGroup({ + foo: new CustomFormControl({ name: 'foo', type: FormNodeTypes.CONTROL, children: [] }, null), + sibling: new CustomFormControl({ name: 'sibling', type: FormNodeTypes.CONTROL, children: [] }, null), + }); + }); + + describe('enable if equals', () => { + it('should return null', () => { + expect(CustomValidators.enableIfEquals('foo', 'foo')(form.controls['sibling'])).toBeNull(); + }); + + it('should set enabled to true if content of sibling is equal to the value passed', () => { + const value = 'bar'; + form.controls['sibling'].patchValue(value); + CustomValidators.enableIfEquals('foo', 'bar')(form.controls['sibling']); + expect((form.controls['foo'] as CustomFormControl).enabled).toBe(true); + }); + + it('should set enabled to true if content of sibling is equal to one of the values passed in the array', () => { + const value = 'bar'; + form.controls['sibling'].patchValue(value); + CustomValidators.enableIfEquals('foo', ['bar', 'foo', 'foobar'])(form.controls['sibling']); + expect((form.controls['foo'] as CustomFormControl).enabled).toBe(true); + }); + + it('should set enabled to false if content of sibling is not equal to the value passed', () => { + const value = 'bar'; + form.controls['sibling'].patchValue(value); + (form.controls['foo'] as CustomFormControl).enable(); + CustomValidators.enableIfEquals('foo', 'foo')(form.controls['sibling']); + expect((form.controls['foo'] as CustomFormControl).enabled).toBe(false); + }); + }); + + describe('disable if equals', () => { + it('should return null', () => { + expect(CustomValidators.disableIfEquals('foo', 'foo')(form.controls['sibling'])).toBeNull(); + }); + + it('should set disabled to true if content of sibling is equal to the value passed', () => { + const value = 'bar'; + form.controls['sibling'].patchValue(value); + CustomValidators.disableIfEquals('foo', 'bar')(form.controls['sibling']); + expect((form.controls['foo'] as CustomFormControl).disabled).toBe(true); + }); + + it('should set disabled to true if content of sibling is equal to one of the values passed in the array', () => { + const value = 'bar'; + form.controls['sibling'].patchValue(value); + CustomValidators.disableIfEquals('foo', ['bar', 'foo', 'foobar'])(form.controls['sibling']); + expect((form.controls['foo'] as CustomFormControl).disabled).toBe(true); + }); + + it('should set disabled to false if content of sibling is not equal to the value passed', () => { + const value = 'bar'; + form.controls['sibling'].patchValue(value); + (form.controls['foo'] as CustomFormControl).disable(); + CustomValidators.disableIfEquals('foo', 'foo')(form.controls['sibling']); + expect((form.controls['foo'] as CustomFormControl).disabled).toBe(false); + }); + }); }); describe('parent sibling validators', () => { - let form: FormGroup; - - beforeEach(() => { - form = new FormGroup({ - parent: new CustomFormGroup( - { name: 'parent', type: FormNodeTypes.GROUP }, - { child: new CustomFormControl({ name: 'child', type: FormNodeTypes.CONTROL }) }, - ), - sibling: new CustomFormControl({ name: 'sibling', type: FormNodeTypes.CONTROL, hide: false }), - }); - }); - - it('should return null', () => { - expect(CustomValidators.hideIfParentSiblingEquals('foo', 'bar')(form.controls['sibling'])).toBeNull(); - }); - - it('should set meta.hide to true if content of parent sibling is equal to the value passed', () => { - const value = 'bar'; - const child = form.get(['parent', 'child']); - child?.patchValue(value); - CustomValidators.hideIfParentSiblingEquals('sibling', value)(child as AbstractControl); - expect((form.controls['sibling'] as CustomFormControl).meta.hide).toBe(true); - }); - - it('should set meta.hide to true if content of parent sibling is not equal to the value passed', () => { - const value = true; - const child = form.get(['parent', 'child']); - child?.patchValue(value); - CustomValidators.hideIfParentSiblingNotEqual('sibling', !value)(child as AbstractControl); - expect((form.controls['sibling'] as CustomFormControl).meta.hide).toBe(true); - }); + let form: FormGroup; + + beforeEach(() => { + form = new FormGroup({ + parent: new CustomFormGroup( + { name: 'parent', type: FormNodeTypes.GROUP }, + { child: new CustomFormControl({ name: 'child', type: FormNodeTypes.CONTROL }) } + ), + sibling: new CustomFormControl({ name: 'sibling', type: FormNodeTypes.CONTROL, hide: false }), + }); + }); + + it('should return null', () => { + expect(CustomValidators.hideIfParentSiblingEquals('foo', 'bar')(form.controls['sibling'])).toBeNull(); + }); + + it('should set meta.hide to true if content of parent sibling is equal to the value passed', () => { + const value = 'bar'; + const child = form.get(['parent', 'child']); + child?.patchValue(value); + CustomValidators.hideIfParentSiblingEquals('sibling', value)(child as AbstractControl); + expect((form.controls['sibling'] as CustomFormControl).meta.hide).toBe(true); + }); + + it('should set meta.hide to true if content of parent sibling is not equal to the value passed', () => { + const value = true; + const child = form.get(['parent', 'child']); + child?.patchValue(value); + CustomValidators.hideIfParentSiblingNotEqual('sibling', !value)(child as AbstractControl); + expect((form.controls['sibling'] as CustomFormControl).meta.hide).toBe(true); + }); }); describe('Required validators', () => { - let form: FormGroup; - - beforeEach(() => { - form = new FormGroup({ - foo: new CustomFormControl({ name: 'foo', type: FormNodeTypes.CONTROL, children: [] }, null), - sibling: new CustomFormControl( - { - name: 'sibling', - label: 'Sibling', - type: FormNodeTypes.CONTROL, - children: [], - }, - null, - ), - }); - form.controls['sibling'].patchValue('some value'); - }); - - describe('Required if equals', () => { - it('should be required (return ValidationErrors) if content of sibling matches a value', () => { - const result = CustomValidators.requiredIfEquals('sibling', ['some value'])(form.controls['foo']); - expect(result).toEqual({ requiredIfEquals: { sibling: 'Sibling' } }); - }); - - it('should not be required (return null) if content of sibling does not match a value', () => { - form.controls['sibling'].patchValue('some other value'); - const result = CustomValidators.requiredIfEquals('sibling', ['some value'])(form.controls['foo']); - expect(result).toBeNull(); - }); - - it('should not be required (return null) if content of sibling does matches a value and we have a value', () => { - form.controls['foo'].patchValue('some foo value'); - const result = CustomValidators.requiredIfEquals('sibling', ['some value'])(form.controls['foo']); - expect(result).toBeNull(); - }); - - it('should not be required (return null) when the sibling and control values overlap (for arrays)', () => { - form.controls['foo'].patchValue(['battery']); - form.controls['sibling'].patchValue(['battery']); - const result = CustomValidators.requiredIfEquals('sibling', ['battery'])(form.controls['foo']); - expect(result).toBeNull(); - }); - - it('should not be required (return null) when sibling value does not match passed value', () => { - form.controls['foo'].patchValue(['not a battery']); - const result = CustomValidators.requiredIfEquals('sibling', ['battery'])(form.controls['foo']); - expect(result).toBeNull(); - }); - - it('should be required (return null) when sibling value overlaps with values passed, and control is empty', () => { - form.controls['sibling'].patchValue(['battery']); - form.controls['foo'].patchValue([null]); // array with only falsy values is considered empty - const result = CustomValidators.requiredIfEquals('sibling', ['battery'])(form.controls['foo']); - expect(result).toEqual({ requiredIfEquals: { sibling: 'Sibling' } }); - }); - - it('should not be required (return null) when sibling value overlaps with values passed, and control is not empty', () => { - form.controls['sibling'].patchValue(['battery']); - form.controls['foo'].patchValue([null, 'truthy']); // array with a falsy value, but some truthy values is considered NOT empty - const result = CustomValidators.requiredIfEquals('sibling', ['battery'])(form.controls['foo']); - expect(result).toBeNull(); - }); - }); - - describe('Required if not equal', () => { - it('should not be required (return null) if content of sibling does not match a value', () => { - const result = CustomValidators.requiredIfNotEquals('sibling', 'some value')(form.controls['foo']); - expect(result).toBeNull(); - }); - - it('should be required (return ValidationErrors) if content of sibling does not match a value', () => { - const result = CustomValidators.requiredIfNotEquals('sibling', 'some other value')(form.controls['foo']); - expect(result).toEqual({ requiredIfNotEquals: { sibling: 'Sibling' } }); - }); - - it('should not be required (return null) if content of sibling does matches a value and we have a value', () => { - form.controls['foo'].patchValue('some foo value'); - const result = CustomValidators.requiredIfEquals('sibling', ['some othervalue'])(form.controls['foo']); - expect(result).toBeNull(); - }); - }); - - describe('Must Equal Sibling', () => { - it('should return null if content of sibling matches a value', () => { - const value = 'some value'; - form.controls['foo'].patchValue(value); - form.controls['sibling'].patchValue(value); - const result = CustomValidators.mustEqualSibling('sibling')(form.controls['foo']); - expect(result).toBeNull(); - }); - - it('should return ValidationErrors if content of sibling does not match the value', () => { - const value = 'some value'; - form.controls['foo'].patchValue(value); - form.controls['sibling'].patchValue('some other value'); - const result = CustomValidators.mustEqualSibling('sibling')(form.controls['foo']); - expect(result).toEqual({ mustEqualSibling: { sibling: 'Sibling' } }); - }); - }); + let form: FormGroup; + + beforeEach(() => { + form = new FormGroup({ + foo: new CustomFormControl({ name: 'foo', type: FormNodeTypes.CONTROL, children: [] }, null), + sibling: new CustomFormControl( + { + name: 'sibling', + label: 'Sibling', + type: FormNodeTypes.CONTROL, + children: [], + }, + null + ), + }); + form.controls['sibling'].patchValue('some value'); + }); + + describe('Required if equals', () => { + it('should be required (return ValidationErrors) if content of sibling matches a value', () => { + const result = CustomValidators.requiredIfEquals('sibling', ['some value'])(form.controls['foo']); + expect(result).toEqual({ requiredIfEquals: { sibling: 'Sibling' } }); + }); + + it('should not be required (return null) if content of sibling does not match a value', () => { + form.controls['sibling'].patchValue('some other value'); + const result = CustomValidators.requiredIfEquals('sibling', ['some value'])(form.controls['foo']); + expect(result).toBeNull(); + }); + + it('should not be required (return null) if content of sibling does matches a value and we have a value', () => { + form.controls['foo'].patchValue('some foo value'); + const result = CustomValidators.requiredIfEquals('sibling', ['some value'])(form.controls['foo']); + expect(result).toBeNull(); + }); + + it('should not be required (return null) when the sibling and control values overlap (for arrays)', () => { + form.controls['foo'].patchValue(['battery']); + form.controls['sibling'].patchValue(['battery']); + const result = CustomValidators.requiredIfEquals('sibling', ['battery'])(form.controls['foo']); + expect(result).toBeNull(); + }); + + it('should not be required (return null) when sibling value does not match passed value', () => { + form.controls['foo'].patchValue(['not a battery']); + const result = CustomValidators.requiredIfEquals('sibling', ['battery'])(form.controls['foo']); + expect(result).toBeNull(); + }); + + it('should be required (return null) when sibling value overlaps with values passed, and control is empty', () => { + form.controls['sibling'].patchValue(['battery']); + form.controls['foo'].patchValue([null]); // array with only falsy values is considered empty + const result = CustomValidators.requiredIfEquals('sibling', ['battery'])(form.controls['foo']); + expect(result).toEqual({ requiredIfEquals: { sibling: 'Sibling' } }); + }); + + it('should not be required (return null) when sibling value overlaps with values passed, and control is not empty', () => { + form.controls['sibling'].patchValue(['battery']); + form.controls['foo'].patchValue([null, 'truthy']); // array with a falsy value, but some truthy values is considered NOT empty + const result = CustomValidators.requiredIfEquals('sibling', ['battery'])(form.controls['foo']); + expect(result).toBeNull(); + }); + }); + + describe('Required if not equal', () => { + it('should not be required (return null) if content of sibling does not match a value', () => { + const result = CustomValidators.requiredIfNotEquals('sibling', 'some value')(form.controls['foo']); + expect(result).toBeNull(); + }); + + it('should be required (return ValidationErrors) if content of sibling does not match a value', () => { + const result = CustomValidators.requiredIfNotEquals('sibling', 'some other value')(form.controls['foo']); + expect(result).toEqual({ requiredIfNotEquals: { sibling: 'Sibling' } }); + }); + + it('should not be required (return null) if content of sibling does matches a value and we have a value', () => { + form.controls['foo'].patchValue('some foo value'); + const result = CustomValidators.requiredIfEquals('sibling', ['some othervalue'])(form.controls['foo']); + expect(result).toBeNull(); + }); + }); + + describe('Must Equal Sibling', () => { + it('should return null if content of sibling matches a value', () => { + const value = 'some value'; + form.controls['foo'].patchValue(value); + form.controls['sibling'].patchValue(value); + const result = CustomValidators.mustEqualSibling('sibling')(form.controls['foo']); + expect(result).toBeNull(); + }); + + it('should return ValidationErrors if content of sibling does not match the value', () => { + const value = 'some value'; + form.controls['foo'].patchValue(value); + form.controls['sibling'].patchValue('some other value'); + const result = CustomValidators.mustEqualSibling('sibling')(form.controls['foo']); + expect(result).toEqual({ mustEqualSibling: { sibling: 'Sibling' } }); + }); + }); }); describe('numeric', () => { - it.each([ - [null, 123456789], - [null, 0], - [null, ''], - [{ customPattern: { message: 'must be a whole number' } }, 'foobar'], - [{ customPattern: { message: 'must be a whole number' } }, '123456bar'], - [{ customPattern: { message: 'must be a whole number' } }, 'foo123456'], - [null, '123546789'], - [null, null], - ])('should return %o for %r', (expected: null | CustomPatternMessage, input: unknown) => { - const numberValidator = CustomValidators.numeric(); - expect(numberValidator(new FormControl(input))).toEqual(expected); - }); + it.each([ + [null, 123456789], + [null, 0], + [null, ''], + [{ customPattern: { message: 'must be a whole number' } }, 'foobar'], + [{ customPattern: { message: 'must be a whole number' } }, '123456bar'], + [{ customPattern: { message: 'must be a whole number' } }, 'foo123456'], + [null, '123546789'], + [null, null], + ])('should return %o for %r', (expected: null | CustomPatternMessage, input: unknown) => { + const numberValidator = CustomValidators.numeric(); + expect(numberValidator(new FormControl(input))).toEqual(expected); + }); }); describe('defined', () => { - it.each([ - [{ defined: false }, undefined], - [null, ''], - [null, null], - [null, 'hello world!'], - [null, 1234], - ])('should return %o for %r', (expected: null | { [index: string]: boolean }, input: unknown) => { - const definedValidator = CustomValidators.defined(); - const form = new FormControl(input); - if (typeof input === 'undefined') { - // Unable to instantiate form with a value that is not defined... - form.patchValue(undefined); - } - expect(definedValidator(form)).toEqual(expected); - }); + it.each([ + [{ defined: false }, undefined], + [null, ''], + [null, null], + [null, 'hello world!'], + [null, 1234], + ])('should return %o for %r', (expected: null | { [index: string]: boolean }, input: unknown) => { + const definedValidator = CustomValidators.defined(); + const form = new FormControl(input); + if (typeof input === 'undefined') { + // Unable to instantiate form with a value that is not defined... + form.patchValue(undefined); + } + expect(definedValidator(form)).toEqual(expected); + }); }); describe('alphanumeric', () => { - it.each([ - [null, '12dc9a'], - [null, 0], - [null, 'asas'], - [{ customPattern: { message: 'must be alphanumeric' } }, 'foobar+'], - [{ customPattern: { message: 'must be alphanumeric' } }, '123456bar-'], - [{ customPattern: { message: 'must be alphanumeric' } }, 'foo123456^@'], - [null, '123546789abcdefghijklmnopqrstuvwxyz'], - [null, null], - ])('should return %o for %r', (expected: null | CustomPatternMessage, input: unknown) => { - const numberValidator = CustomValidators.alphanumeric(); - expect(numberValidator(new FormControl(input))).toEqual(expected); - }); + it.each([ + [null, '12dc9a'], + [null, 0], + [null, 'asas'], + [{ customPattern: { message: 'must be alphanumeric' } }, 'foobar+'], + [{ customPattern: { message: 'must be alphanumeric' } }, '123456bar-'], + [{ customPattern: { message: 'must be alphanumeric' } }, 'foo123456^@'], + [null, '123546789abcdefghijklmnopqrstuvwxyz'], + [null, null], + ])('should return %o for %r', (expected: null | CustomPatternMessage, input: unknown) => { + const numberValidator = CustomValidators.alphanumeric(); + expect(numberValidator(new FormControl(input))).toEqual(expected); + }); }); describe('customPattern', () => { - it.each([ - [null, 123456789, '.*', 'this should always pass'], - [null, 'jkl', 'c*', 'this should be a character'], - [{ customPattern: { message: 'this should not be a number' } }, 123456789, '\\D+', 'this should not be a number'], - [null, '%^', '^\\W+$', 'this should be a symbol'], - [null, null, '.*', 'pass on null'], - ])('should return %o for %r', (expected: null | CustomPatternMessage, input: unknown, regex: string, msg: string) => { - const customPattern = CustomValidators.customPattern([regex, msg]); - const validation = customPattern(new FormControl(input)); - expect(validation).toEqual(expected); - if (validation) { - const { customPattern: { message } } = validation; - // eslint-disable-next-line jest/no-conditional-expect - expect(message).toEqual(msg); - } else { - // eslint-disable-next-line jest/no-conditional-expect - expect(validation).toBeNull(); - } - }); - - it('should throw an error if an invalid regex is given', () => { - expect(CustomValidators.customPattern(['regex', 'msg'])).toThrow(); - }); + it.each([ + [null, 123456789, '.*', 'this should always pass'], + [null, 'jkl', 'c*', 'this should be a character'], + [{ customPattern: { message: 'this should not be a number' } }, 123456789, '\\D+', 'this should not be a number'], + [null, '%^', '^\\W+$', 'this should be a symbol'], + [null, null, '.*', 'pass on null'], + ])('should return %o for %r', (expected: null | CustomPatternMessage, input: unknown, regex: string, msg: string) => { + const customPattern = CustomValidators.customPattern([regex, msg]); + const validation = customPattern(new FormControl(input)); + expect(validation).toEqual(expected); + if (validation) { + const { + customPattern: { message }, + } = validation; + // eslint-disable-next-line jest/no-conditional-expect + expect(message).toEqual(msg); + } else { + // eslint-disable-next-line jest/no-conditional-expect + expect(validation).toBeNull(); + } + }); + + it('should throw an error if an invalid regex is given', () => { + expect(CustomValidators.customPattern(['regex', 'msg'])).toThrow(); + }); }); describe('invalidOption', () => { - it.each([ - [null, ''], - [{ invalidOption: true }, '[INVALID_OPTION]'], - ])('should return %p when control value is %s', (expected: object | null, input: string) => { - expect(CustomValidators.invalidOption(new FormControl(input))).toEqual(expected); - }); + it.each([ + [null, ''], + [{ invalidOption: true }, '[INVALID_OPTION]'], + ])('should return %p when control value is %s', (expected: object | null, input: string) => { + expect(CustomValidators.invalidOption(new FormControl(input))).toEqual(expected); + }); }); describe('pastDate', () => { - beforeAll(() => { - jest.useFakeTimers().setSystemTime(new Date('2022-01-01T00:00:00.000Z')); - }); - - afterAll(() => { - jest.useRealTimers(); - }); - - it.each([ - [null, ''], - [null, null], - [null, '2020-01-01T00:00:00.000Z'], - [{ pastDate: true }, '2022-01-01T00:00:01.000Z'], - ])('should return %p when control value is %s', (expected: object | null, input: string | null) => { - expect(CustomValidators.pastDate(new FormControl(input))).toEqual(expected); - }); + beforeAll(() => { + jest.useFakeTimers().setSystemTime(new Date('2022-01-01T00:00:00.000Z')); + }); + + afterAll(() => { + jest.useRealTimers(); + }); + + it.each([ + [null, ''], + [null, null], + [null, '2020-01-01T00:00:00.000Z'], + [{ pastDate: true }, '2022-01-01T00:00:01.000Z'], + ])('should return %p when control value is %s', (expected: object | null, input: string | null) => { + expect(CustomValidators.pastDate(new FormControl(input))).toEqual(expected); + }); }); describe('futureDate', () => { - beforeAll(() => { - jest.useFakeTimers().setSystemTime(new Date('2022-01-01T00:00:00.000Z')); - }); - - afterAll(() => { - jest.useRealTimers(); - }); - - it.each([ - [null, ''], - [null, null], - [null, '2022-01-01T00:00:01.000Z'], - [{ futureDate: true }, '2020-01-01T00:00:00.000Z'], - ])('should return %p when control value is %s', (expected: object | null, input: string | null) => { - expect(CustomValidators.futureDate(new FormControl(input))).toEqual(expected); - }); + beforeAll(() => { + jest.useFakeTimers().setSystemTime(new Date('2022-01-01T00:00:00.000Z')); + }); + + afterAll(() => { + jest.useRealTimers(); + }); + + it.each([ + [null, ''], + [null, null], + [null, '2022-01-01T00:00:01.000Z'], + [{ futureDate: true }, '2020-01-01T00:00:00.000Z'], + ])('should return %p when control value is %s', (expected: object | null, input: string | null) => { + expect(CustomValidators.futureDate(new FormControl(input))).toEqual(expected); + }); }); describe('pastYear', () => { - beforeAll(() => { - jest.useFakeTimers().setSystemTime(new Date('2022-01-01T00:00:00.000Z')); - }); - - afterAll(() => { - jest.useRealTimers(); - }); - - it.each([ - [null, null], - [{ pastYear: true }, 2023], - [null, 2020], - ])('should return %p when control value is %s', (expected: object | null, input: number | null) => { - expect(CustomValidators.pastYear(new FormControl(input))).toEqual(expected); - }); + beforeAll(() => { + jest.useFakeTimers().setSystemTime(new Date('2022-01-01T00:00:00.000Z')); + }); + + afterAll(() => { + jest.useRealTimers(); + }); + + it.each([ + [null, null], + [{ pastYear: true }, 2023], + [null, 2020], + ])('should return %p when control value is %s', (expected: object | null, input: number | null) => { + expect(CustomValidators.pastYear(new FormControl(input))).toEqual(expected); + }); }); describe('aheadOfDate', () => { - let form: FormGroup; - beforeEach(() => { - form = new FormGroup({ - foo: new CustomFormControl({ name: 'foo', type: FormNodeTypes.CONTROL, children: [] }, null), - sibling: new CustomFormControl( - { - name: 'sibling', - label: 'sibling', - type: FormNodeTypes.CONTROL, - children: [], - }, - null, - ), - }); - }); - - describe('tests', () => { - it('should return an error message if sibling date is ahead of foo', () => { - form.controls['foo'].patchValue(new Date('2020-01-01T00:00:00.000Z').toISOString()); - form.controls['sibling'].patchValue(new Date('2021-01-01T00:00:00.000Z').toISOString()); - - const result = CustomValidators.aheadOfDate('sibling')(form.controls['foo']); - expect(result).toEqual({ aheadOfDate: { sibling: 'sibling', date: new Date('2021-01-01T00:00:00.000Z') } }); - }); - - it('should return null if sibling date is not ahead of foo', () => { - form.controls['foo'].patchValue(new Date('2021-01-01T00:00:00.000Z').toISOString()); - form.controls['sibling'].patchValue(new Date('2020-01-01T00:00:00.000Z').toISOString()); - - const result = CustomValidators.aheadOfDate('sibling')(form.controls['foo']); - expect(result).toBeNull(); - }); - }); + let form: FormGroup; + beforeEach(() => { + form = new FormGroup({ + foo: new CustomFormControl({ name: 'foo', type: FormNodeTypes.CONTROL, children: [] }, null), + sibling: new CustomFormControl( + { + name: 'sibling', + label: 'sibling', + type: FormNodeTypes.CONTROL, + children: [], + }, + null + ), + }); + }); + + describe('tests', () => { + it('should return an error message if sibling date is ahead of foo', () => { + form.controls['foo'].patchValue(new Date('2020-01-01T00:00:00.000Z').toISOString()); + form.controls['sibling'].patchValue(new Date('2021-01-01T00:00:00.000Z').toISOString()); + + const result = CustomValidators.aheadOfDate('sibling')(form.controls['foo']); + expect(result).toEqual({ aheadOfDate: { sibling: 'sibling', date: new Date('2021-01-01T00:00:00.000Z') } }); + }); + + it('should return null if sibling date is not ahead of foo', () => { + form.controls['foo'].patchValue(new Date('2021-01-01T00:00:00.000Z').toISOString()); + form.controls['sibling'].patchValue(new Date('2020-01-01T00:00:00.000Z').toISOString()); + + const result = CustomValidators.aheadOfDate('sibling')(form.controls['foo']); + expect(result).toBeNull(); + }); + }); }); describe('dateNotExceed', () => { - let form: FormGroup; - beforeEach(() => { - form = new FormGroup({ - foo: new CustomFormControl({ name: 'foo', type: FormNodeTypes.CONTROL, children: [] }, null), - sibling: new CustomFormControl( - { - name: 'sibling', - label: 'sibling', - type: FormNodeTypes.CONTROL, - children: [], - }, - null, - ), - }); - }); - - describe('dateNotExceed tests', () => { - it('should return an error message if sibling date plus 10 months is not ahead of foo', () => { - form.controls['foo'].patchValue(new Date('2020-12-01T00:00:00.000Z').toISOString()); - form.controls['sibling'].patchValue(new Date('2020-01-01T00:00:00.000Z').toISOString()); - - const result = CustomValidators.dateNotExceed('sibling', 10)(form.controls['foo']); - expect(result).toEqual({ dateNotExceed: { sibling: 'sibling', months: 10 } }); - }); - - it('should return an error message if sibling date plus 14 months is not ahead of foo', () => { - form.controls['foo'].patchValue(new Date('2021-04-01T00:00:00.000Z').toISOString()); - form.controls['sibling'].patchValue(new Date('2020-01-01T00:00:00.000Z').toISOString()); - - const result = CustomValidators.dateNotExceed('sibling', 14)(form.controls['foo']); - expect(result).toEqual({ dateNotExceed: { sibling: 'sibling', months: 14 } }); - }); - - it('should return null if sibling date plus 10 months is ahead of foo', () => { - form.controls['foo'].patchValue(new Date('2020-09-01T00:00:00.000Z').toISOString()); - form.controls['sibling'].patchValue(new Date('2020-01-01T00:00:00.000Z').toISOString()); - - const result = CustomValidators.dateNotExceed('sibling', 10)(form.controls['foo']); - expect(result).toBeNull(); - }); - - it('should return null if sibling date plus 14 months is ahead of foo', () => { - form.controls['foo'].patchValue(new Date('2021-02-01T00:00:00.000Z').toISOString()); - form.controls['sibling'].patchValue(new Date('2020-01-01T00:00:00.000Z').toISOString()); - - const result = CustomValidators.dateNotExceed('sibling', 14)(form.controls['foo']); - expect(result).toBeNull(); - }); - }); + let form: FormGroup; + beforeEach(() => { + form = new FormGroup({ + foo: new CustomFormControl({ name: 'foo', type: FormNodeTypes.CONTROL, children: [] }, null), + sibling: new CustomFormControl( + { + name: 'sibling', + label: 'sibling', + type: FormNodeTypes.CONTROL, + children: [], + }, + null + ), + }); + }); + + describe('dateNotExceed tests', () => { + it('should return an error message if sibling date plus 10 months is not ahead of foo', () => { + form.controls['foo'].patchValue(new Date('2020-12-01T00:00:00.000Z').toISOString()); + form.controls['sibling'].patchValue(new Date('2020-01-01T00:00:00.000Z').toISOString()); + + const result = CustomValidators.dateNotExceed('sibling', 10)(form.controls['foo']); + expect(result).toEqual({ dateNotExceed: { sibling: 'sibling', months: 10 } }); + }); + + it('should return an error message if sibling date plus 14 months is not ahead of foo', () => { + form.controls['foo'].patchValue(new Date('2021-04-01T00:00:00.000Z').toISOString()); + form.controls['sibling'].patchValue(new Date('2020-01-01T00:00:00.000Z').toISOString()); + + const result = CustomValidators.dateNotExceed('sibling', 14)(form.controls['foo']); + expect(result).toEqual({ dateNotExceed: { sibling: 'sibling', months: 14 } }); + }); + + it('should return null if sibling date plus 10 months is ahead of foo', () => { + form.controls['foo'].patchValue(new Date('2020-09-01T00:00:00.000Z').toISOString()); + form.controls['sibling'].patchValue(new Date('2020-01-01T00:00:00.000Z').toISOString()); + + const result = CustomValidators.dateNotExceed('sibling', 10)(form.controls['foo']); + expect(result).toBeNull(); + }); + + it('should return null if sibling date plus 14 months is ahead of foo', () => { + form.controls['foo'].patchValue(new Date('2021-02-01T00:00:00.000Z').toISOString()); + form.controls['sibling'].patchValue(new Date('2020-01-01T00:00:00.000Z').toISOString()); + + const result = CustomValidators.dateNotExceed('sibling', 14)(form.controls['foo']); + expect(result).toBeNull(); + }); + }); }); describe('validate VRM/TrailerId Length', () => { - let form: FormGroup; - - beforeEach(() => { - form = new FormGroup({ - parent: new CustomFormGroup( - { name: 'parent', type: FormNodeTypes.GROUP }, - { - child: new CustomFormControl({ name: 'child', type: FormNodeTypes.CONTROL }), - sibling: new CustomFormControl({ name: 'sibling', type: FormNodeTypes.CONTROL }), - }, - ), - }); - }); - - it('should return null when value is null', () => { - const value = null; - const child = form.get(['parent', 'child']); - child?.patchValue(value); - - const result = CustomValidators.validateVRMTrailerIdLength('sibling')(child as AbstractControl); - expect(result).toBeNull(); - }); - - it('should return null when value is 7 characters long and Trailer is selected', () => { - const value = 'TESTTRL'; - const child = form.get(['parent', 'child']); - const sibling = form.get(['parent', 'sibling']); - child?.patchValue(value); - sibling?.patchValue(VehicleTypes.TRL); - - const result = CustomValidators.validateVRMTrailerIdLength('sibling')(child as AbstractControl); - expect(result).toBeNull(); - }); - - it('should return null when value is 8 characters long and Trailer is selected', () => { - const value = 'TESTTRLR'; - const child = form.get(['parent', 'child']); - const sibling = form.get(['parent', 'sibling']); - child?.patchValue(value); - sibling?.patchValue(VehicleTypes.TRL); - - const result = CustomValidators.validateVRMTrailerIdLength('sibling')(child as AbstractControl); - expect(result).toBeNull(); - }); - - it('should return null when value is 8 characters long', () => { - const value = 'TESTTRLR'; - const child = form.get(['parent', 'child']); - child?.patchValue(value); - - const result = CustomValidators.validateVRMTrailerIdLength('sibling')(child as AbstractControl); - expect(result).toBeNull(); - }); - - it('should return VRM max length error when value length is greater than 9', () => { - const value = 'TESTVRM123'; - const child = form.get(['parent', 'child']); - child?.patchValue(value); - - const result = CustomValidators.validateVRMTrailerIdLength('sibling')(child as AbstractControl); - expect(result?.['validateVRMTrailerIdLength'].message).toBe('VRM must be less than or equal to 9 characters'); - }); - - it('should return TrailerId min length error when value length is less than 7 and Trailer is selected', () => { - const value = 'TESTTR'; - const child = form.get(['parent', 'child']); - const sibling = form.get(['parent', 'sibling']); - child?.patchValue(value); - sibling?.patchValue(VehicleTypes.TRL); - - const result = CustomValidators.validateVRMTrailerIdLength('sibling')(child as AbstractControl); - expect(result?.['validateVRMTrailerIdLength'].message).toBe('Trailer ID must be greater than or equal to 7 characters'); - }); - - it('should return TrailerId max length error when value length is greater than 8 and Trailer is selected', () => { - const value = 'TESTTRLRR'; - const child = form.get(['parent', 'child']); - const sibling = form.get(['parent', 'sibling']); - child?.patchValue(value); - sibling?.patchValue(VehicleTypes.TRL); - - const result = CustomValidators.validateVRMTrailerIdLength('sibling')(child as AbstractControl); - expect(result?.['validateVRMTrailerIdLength'].message).toBe('Trailer ID must be less than or equal to 8 characters'); - }); + let form: FormGroup; + + beforeEach(() => { + form = new FormGroup({ + parent: new CustomFormGroup( + { name: 'parent', type: FormNodeTypes.GROUP }, + { + child: new CustomFormControl({ name: 'child', type: FormNodeTypes.CONTROL }), + sibling: new CustomFormControl({ name: 'sibling', type: FormNodeTypes.CONTROL }), + } + ), + }); + }); + + it('should return null when value is null', () => { + const value = null; + const child = form.get(['parent', 'child']); + child?.patchValue(value); + + const result = CustomValidators.validateVRMTrailerIdLength('sibling')(child as AbstractControl); + expect(result).toBeNull(); + }); + + it('should return null when value is 7 characters long and Trailer is selected', () => { + const value = 'TESTTRL'; + const child = form.get(['parent', 'child']); + const sibling = form.get(['parent', 'sibling']); + child?.patchValue(value); + sibling?.patchValue(VehicleTypes.TRL); + + const result = CustomValidators.validateVRMTrailerIdLength('sibling')(child as AbstractControl); + expect(result).toBeNull(); + }); + + it('should return null when value is 8 characters long and Trailer is selected', () => { + const value = 'TESTTRLR'; + const child = form.get(['parent', 'child']); + const sibling = form.get(['parent', 'sibling']); + child?.patchValue(value); + sibling?.patchValue(VehicleTypes.TRL); + + const result = CustomValidators.validateVRMTrailerIdLength('sibling')(child as AbstractControl); + expect(result).toBeNull(); + }); + + it('should return null when value is 8 characters long', () => { + const value = 'TESTTRLR'; + const child = form.get(['parent', 'child']); + child?.patchValue(value); + + const result = CustomValidators.validateVRMTrailerIdLength('sibling')(child as AbstractControl); + expect(result).toBeNull(); + }); + + it('should return VRM max length error when value length is greater than 9', () => { + const value = 'TESTVRM123'; + const child = form.get(['parent', 'child']); + child?.patchValue(value); + + const result = CustomValidators.validateVRMTrailerIdLength('sibling')(child as AbstractControl); + expect(result?.['validateVRMTrailerIdLength'].message).toBe('VRM must be less than or equal to 9 characters'); + }); + + it('should return TrailerId min length error when value length is less than 7 and Trailer is selected', () => { + const value = 'TESTTR'; + const child = form.get(['parent', 'child']); + const sibling = form.get(['parent', 'sibling']); + child?.patchValue(value); + sibling?.patchValue(VehicleTypes.TRL); + + const result = CustomValidators.validateVRMTrailerIdLength('sibling')(child as AbstractControl); + expect(result?.['validateVRMTrailerIdLength'].message).toBe( + 'Trailer ID must be greater than or equal to 7 characters' + ); + }); + + it('should return TrailerId max length error when value length is greater than 8 and Trailer is selected', () => { + const value = 'TESTTRLRR'; + const child = form.get(['parent', 'child']); + const sibling = form.get(['parent', 'sibling']); + child?.patchValue(value); + sibling?.patchValue(VehicleTypes.TRL); + + const result = CustomValidators.validateVRMTrailerIdLength('sibling')(child as AbstractControl); + expect(result?.['validateVRMTrailerIdLength'].message).toBe( + 'Trailer ID must be less than or equal to 8 characters' + ); + }); }); describe('handlePsvPassengersChange', () => { - let form: FormGroup; - beforeEach(() => { - form = new FormGroup({ - techRecord_vehicleSize: new CustomFormControl({ name: 'techRecord_vehicleSize', type: FormNodeTypes.CONTROL }, undefined), - techRecord_vehicleClass_description: new CustomFormControl( - { name: 'techRecord_vehicleClass_description', type: FormNodeTypes.CONTROL }, - undefined, - ), - techRecord_seatsLowerDeck: new CustomFormControl({ name: 'techRecord_seatsLowerDeck', type: FormNodeTypes.CONTROL }, undefined), - techRecord_seatsUpperDeck: new CustomFormControl({ name: 'techRecord_seatsUpperDeck', type: FormNodeTypes.CONTROL }, undefined), - techRecord_standingCapacity: new CustomFormControl({ name: 'techRecord_standingCapacity', type: FormNodeTypes.CONTROL }, undefined), - }); - }); - it('should calculate small vehicle size and class based on passenger numbers', () => { - const upper = form.get('techRecord_seatsUpperDeck'); - const lower = form.get('techRecord_seatsLowerDeck'); - const standing = form.get('techRecord_standingCapacity'); - - upper?.patchValue(1); - lower?.patchValue(2); - standing?.patchValue(3); - standing?.markAsDirty(); - - CustomValidators.handlePsvPassengersChange('techRecord_seatsUpperDeck', 'techRecord_seatsLowerDeck')(standing as AbstractControl); - const vehicleSize = form.get('techRecord_vehicleSize')?.value; - const vehicleClass = form.get('techRecord_vehicleClass_description')?.value; - - expect(vehicleSize).toBe(VehicleSizes.SMALL); - expect(vehicleClass).toBe(VehicleClassDescription.SmallPsvIeLessThanOrEqualTo22Seats); - }); - it('should calculate large vehicle size and class based on passenger numbers', () => { - const upper = form.get('techRecord_seatsUpperDeck'); - const lower = form.get('techRecord_seatsLowerDeck'); - const standing = form.get('techRecord_standingCapacity'); - - upper?.patchValue(13); - lower?.patchValue(22); - standing?.patchValue(1); - standing?.markAsDirty(); - - CustomValidators.handlePsvPassengersChange('techRecord_seatsUpperDeck', 'techRecord_seatsLowerDeck')(standing as AbstractControl); - const vehicleSize = form.get('techRecord_vehicleSize')?.value; - const vehicleClass = form.get('techRecord_vehicleClass_description')?.value; - - expect(vehicleSize).toBe(VehicleSizes.LARGE); - expect(vehicleClass).toBe(VehicleClassDescription.LargePsvIeGreaterThan23Seats); - }); + let form: FormGroup; + beforeEach(() => { + form = new FormGroup({ + techRecord_vehicleSize: new CustomFormControl( + { name: 'techRecord_vehicleSize', type: FormNodeTypes.CONTROL }, + undefined + ), + techRecord_vehicleClass_description: new CustomFormControl( + { name: 'techRecord_vehicleClass_description', type: FormNodeTypes.CONTROL }, + undefined + ), + techRecord_seatsLowerDeck: new CustomFormControl( + { name: 'techRecord_seatsLowerDeck', type: FormNodeTypes.CONTROL }, + undefined + ), + techRecord_seatsUpperDeck: new CustomFormControl( + { name: 'techRecord_seatsUpperDeck', type: FormNodeTypes.CONTROL }, + undefined + ), + techRecord_standingCapacity: new CustomFormControl( + { name: 'techRecord_standingCapacity', type: FormNodeTypes.CONTROL }, + undefined + ), + }); + }); + it('should calculate small vehicle size and class based on passenger numbers', () => { + const upper = form.get('techRecord_seatsUpperDeck'); + const lower = form.get('techRecord_seatsLowerDeck'); + const standing = form.get('techRecord_standingCapacity'); + + upper?.patchValue(1); + lower?.patchValue(2); + standing?.patchValue(3); + standing?.markAsDirty(); + + CustomValidators.handlePsvPassengersChange( + 'techRecord_seatsUpperDeck', + 'techRecord_seatsLowerDeck' + )(standing as AbstractControl); + const vehicleSize = form.get('techRecord_vehicleSize')?.value; + const vehicleClass = form.get('techRecord_vehicleClass_description')?.value; + + expect(vehicleSize).toBe(VehicleSizes.SMALL); + expect(vehicleClass).toBe(VehicleClassDescription.SmallPsvIeLessThanOrEqualTo22Seats); + }); + it('should calculate large vehicle size and class based on passenger numbers', () => { + const upper = form.get('techRecord_seatsUpperDeck'); + const lower = form.get('techRecord_seatsLowerDeck'); + const standing = form.get('techRecord_standingCapacity'); + + upper?.patchValue(13); + lower?.patchValue(22); + standing?.patchValue(1); + standing?.markAsDirty(); + + CustomValidators.handlePsvPassengersChange( + 'techRecord_seatsUpperDeck', + 'techRecord_seatsLowerDeck' + )(standing as AbstractControl); + const vehicleSize = form.get('techRecord_vehicleSize')?.value; + const vehicleClass = form.get('techRecord_vehicleClass_description')?.value; + + expect(vehicleSize).toBe(VehicleSizes.LARGE); + expect(vehicleClass).toBe(VehicleClassDescription.LargePsvIeGreaterThan23Seats); + }); }); describe('updateFunctionCode', () => { - let form: FormGroup; - beforeEach(() => { - form = new FormGroup({ - techRecord_vehicleConfiguration: new CustomFormControl({ name: 'techRecord_vehicleConfiguration', type: FormNodeTypes.CONTROL }, undefined), - techRecord_functionCode: new CustomFormControl({ name: 'techRecord_functionCode', type: FormNodeTypes.CONTROL }, undefined), - }); - }); - it('should set the function code to R if given a rigid vehicle configuration', () => { - const functionCode = form.get('techRecord_functionCode'); - const vehicleConfiguration = form.get('techRecord_vehicleConfiguration'); - - vehicleConfiguration?.patchValue('rigid'); - vehicleConfiguration?.markAsDirty(); - - CustomValidators.updateFunctionCode()(vehicleConfiguration as AbstractControl); - const value = functionCode?.value; - expect(value).toBe('R'); - }); - it('should set the function code to A if given a articulated vehicle configuration', () => { - const functionCode = form.get('techRecord_functionCode'); - const vehicleConfiguration = form.get('techRecord_vehicleConfiguration'); - - vehicleConfiguration?.patchValue('articulated'); - vehicleConfiguration?.markAsDirty(); - - CustomValidators.updateFunctionCode()(vehicleConfiguration as AbstractControl); - const value = functionCode?.value; - expect(value).toBe('A'); - }); - it('should set the function code to A if given a semi-trailer vehicle configuration', () => { - const functionCode = form.get('techRecord_functionCode'); - const vehicleConfiguration = form.get('techRecord_vehicleConfiguration'); - - vehicleConfiguration?.patchValue('semi-trailer'); - vehicleConfiguration?.markAsDirty(); - - CustomValidators.updateFunctionCode()(vehicleConfiguration as AbstractControl); - const value = functionCode?.value; - expect(value).toBe('A'); - }); - it('should not set the function code if vehicle configuration is not in the map', () => { - const functionCode = form.get('techRecord_functionCode'); - const vehicleConfiguration = form.get('techRecord_vehicleConfiguration'); - - vehicleConfiguration?.patchValue('invalid'); - vehicleConfiguration?.markAsDirty(); - - CustomValidators.updateFunctionCode()(vehicleConfiguration as AbstractControl); - const value = functionCode?.value; - expect(value).toBeUndefined(); - }); + let form: FormGroup; + beforeEach(() => { + form = new FormGroup({ + techRecord_vehicleConfiguration: new CustomFormControl( + { name: 'techRecord_vehicleConfiguration', type: FormNodeTypes.CONTROL }, + undefined + ), + techRecord_functionCode: new CustomFormControl( + { name: 'techRecord_functionCode', type: FormNodeTypes.CONTROL }, + undefined + ), + }); + }); + it('should set the function code to R if given a rigid vehicle configuration', () => { + const functionCode = form.get('techRecord_functionCode'); + const vehicleConfiguration = form.get('techRecord_vehicleConfiguration'); + + vehicleConfiguration?.patchValue('rigid'); + vehicleConfiguration?.markAsDirty(); + + CustomValidators.updateFunctionCode()(vehicleConfiguration as AbstractControl); + const value = functionCode?.value; + expect(value).toBe('R'); + }); + it('should set the function code to A if given a articulated vehicle configuration', () => { + const functionCode = form.get('techRecord_functionCode'); + const vehicleConfiguration = form.get('techRecord_vehicleConfiguration'); + + vehicleConfiguration?.patchValue('articulated'); + vehicleConfiguration?.markAsDirty(); + + CustomValidators.updateFunctionCode()(vehicleConfiguration as AbstractControl); + const value = functionCode?.value; + expect(value).toBe('A'); + }); + it('should set the function code to A if given a semi-trailer vehicle configuration', () => { + const functionCode = form.get('techRecord_functionCode'); + const vehicleConfiguration = form.get('techRecord_vehicleConfiguration'); + + vehicleConfiguration?.patchValue('semi-trailer'); + vehicleConfiguration?.markAsDirty(); + + CustomValidators.updateFunctionCode()(vehicleConfiguration as AbstractControl); + const value = functionCode?.value; + expect(value).toBe('A'); + }); + it('should not set the function code if vehicle configuration is not in the map', () => { + const functionCode = form.get('techRecord_functionCode'); + const vehicleConfiguration = form.get('techRecord_vehicleConfiguration'); + + vehicleConfiguration?.patchValue('invalid'); + vehicleConfiguration?.markAsDirty(); + + CustomValidators.updateFunctionCode()(vehicleConfiguration as AbstractControl); + const value = functionCode?.value; + expect(value).toBeUndefined(); + }); }); describe('showGroupsWhenEqualTo', () => { - let form: FormGroup; - beforeEach(() => { - form = new CustomFormGroup( - { - name: 'form-group', - type: FormNodeTypes.GROUP, - children: - [ - { - name: 'dangerousGoods', - value: false, - type: FormNodeTypes.CONTROL, - }, - { - name: 'techRecord_adrDetails_applicantDetails_name', - type: FormNodeTypes.CONTROL, - hide: true, - groups: ['adr'], - }, - { - name: 'techRecord_adrDetails_applicantDetails_street', - type: FormNodeTypes.CONTROL, - hide: true, - groups: ['adr'], - }, - { - name: 'techRecord_adrDetails_applicantDetails_town', - type: FormNodeTypes.CONTROL, - hide: true, - groups: ['adr'], - }, - ], - }, - { - dangerousGoods: new CustomFormControl( - { - name: 'dangerousGoods', - type: FormNodeTypes.CONTROL, - }, - undefined, - ), - techRecord_adrDetails_applicantDetails_name: new CustomFormControl( - { - name: 'techRecord_adrDetails_applicantDetails_name', - type: FormNodeTypes.CONTROL, - hide: true, - groups: ['adr', 'name'], - }, - undefined, - ), - techRecord_adrDetails_applicantDetails_street: new CustomFormControl( - { - name: 'techRecord_adrDetails_applicantDetails_street', - type: FormNodeTypes.CONTROL, - hide: true, - groups: ['adr'], - }, - undefined, - ), - techRecord_adrDetails_applicantDetails_town: new CustomFormControl( - { - name: 'techRecord_adrDetails_applicantDetails_town', - type: FormNodeTypes.CONTROL, - hide: true, - groups: ['adr'], - }, - undefined, - ), - }, - ); - }); - it('should set hide as false on a control if it is in a group included in the array passed and values match true', () => { - const adr = form.get('dangerousGoods'); - const name = form.get('techRecord_adrDetails_applicantDetails_name') as CustomFormControl; - const street = form.get('techRecord_adrDetails_applicantDetails_street') as CustomFormControl; - const town = form.get('techRecord_adrDetails_applicantDetails_town') as CustomFormControl; - - adr?.patchValue(true); - - CustomValidators.showGroupsWhenEqualTo([true], ['adr'])(adr as AbstractControl); - - expect(name?.meta.hide).toBe(false); - expect(street?.meta.hide).toBe(false); - expect(town?.meta.hide).toBe(false); - }); - it('should set hide as false on a control if it is in a group included in the array passed and values match false', () => { - const adr = form.get('dangerousGoods'); - const name = form.get('techRecord_adrDetails_applicantDetails_name') as CustomFormControl; - const street = form.get('techRecord_adrDetails_applicantDetails_street') as CustomFormControl; - const town = form.get('techRecord_adrDetails_applicantDetails_town') as CustomFormControl; - - adr?.patchValue(false); - - CustomValidators.showGroupsWhenEqualTo([false], ['adr'])(adr as AbstractControl); - - expect(name?.meta.hide).toBe(false); - expect(street?.meta.hide).toBe(false); - expect(town?.meta.hide).toBe(false); - }); - it('should not change the hide flag if values dont match', () => { - const adr = form.get('dangerousGoods'); - const name = form.get('techRecord_adrDetails_applicantDetails_name') as CustomFormControl; - const street = form.get('techRecord_adrDetails_applicantDetails_street') as CustomFormControl; - const town = form.get('techRecord_adrDetails_applicantDetails_town') as CustomFormControl; - - adr?.patchValue(true); - - CustomValidators.showGroupsWhenEqualTo([false], ['adr'])(adr as AbstractControl); - - expect(name?.meta.hide).toBe(true); - expect(street?.meta.hide).toBe(true); - expect(town?.meta.hide).toBe(true); - }); - it('should only change hide on groups included in array', () => { - const adr = form.get('dangerousGoods'); - const name = form.get('techRecord_adrDetails_applicantDetails_name') as CustomFormControl; - const street = form.get('techRecord_adrDetails_applicantDetails_street') as CustomFormControl; - const town = form.get('techRecord_adrDetails_applicantDetails_town') as CustomFormControl; - - adr?.patchValue(true); - - CustomValidators.showGroupsWhenEqualTo([true], ['name'])(adr as AbstractControl); - - expect(name?.meta.hide).toBe(false); - expect(street?.meta.hide).toBe(true); - expect(town?.meta.hide).toBe(true); - }); + let form: FormGroup; + beforeEach(() => { + form = new CustomFormGroup( + { + name: 'form-group', + type: FormNodeTypes.GROUP, + children: [ + { + name: 'dangerousGoods', + value: false, + type: FormNodeTypes.CONTROL, + }, + { + name: 'techRecord_adrDetails_applicantDetails_name', + type: FormNodeTypes.CONTROL, + hide: true, + groups: ['adr'], + }, + { + name: 'techRecord_adrDetails_applicantDetails_street', + type: FormNodeTypes.CONTROL, + hide: true, + groups: ['adr'], + }, + { + name: 'techRecord_adrDetails_applicantDetails_town', + type: FormNodeTypes.CONTROL, + hide: true, + groups: ['adr'], + }, + ], + }, + { + dangerousGoods: new CustomFormControl( + { + name: 'dangerousGoods', + type: FormNodeTypes.CONTROL, + }, + undefined + ), + techRecord_adrDetails_applicantDetails_name: new CustomFormControl( + { + name: 'techRecord_adrDetails_applicantDetails_name', + type: FormNodeTypes.CONTROL, + hide: true, + groups: ['adr', 'name'], + }, + undefined + ), + techRecord_adrDetails_applicantDetails_street: new CustomFormControl( + { + name: 'techRecord_adrDetails_applicantDetails_street', + type: FormNodeTypes.CONTROL, + hide: true, + groups: ['adr'], + }, + undefined + ), + techRecord_adrDetails_applicantDetails_town: new CustomFormControl( + { + name: 'techRecord_adrDetails_applicantDetails_town', + type: FormNodeTypes.CONTROL, + hide: true, + groups: ['adr'], + }, + undefined + ), + } + ); + }); + it('should set hide as false on a control if it is in a group included in the array passed and values match true', () => { + const adr = form.get('dangerousGoods'); + const name = form.get('techRecord_adrDetails_applicantDetails_name') as CustomFormControl; + const street = form.get('techRecord_adrDetails_applicantDetails_street') as CustomFormControl; + const town = form.get('techRecord_adrDetails_applicantDetails_town') as CustomFormControl; + + adr?.patchValue(true); + + CustomValidators.showGroupsWhenEqualTo([true], ['adr'])(adr as AbstractControl); + + expect(name?.meta.hide).toBe(false); + expect(street?.meta.hide).toBe(false); + expect(town?.meta.hide).toBe(false); + }); + it('should set hide as false on a control if it is in a group included in the array passed and values match false', () => { + const adr = form.get('dangerousGoods'); + const name = form.get('techRecord_adrDetails_applicantDetails_name') as CustomFormControl; + const street = form.get('techRecord_adrDetails_applicantDetails_street') as CustomFormControl; + const town = form.get('techRecord_adrDetails_applicantDetails_town') as CustomFormControl; + + adr?.patchValue(false); + + CustomValidators.showGroupsWhenEqualTo([false], ['adr'])(adr as AbstractControl); + + expect(name?.meta.hide).toBe(false); + expect(street?.meta.hide).toBe(false); + expect(town?.meta.hide).toBe(false); + }); + it('should not change the hide flag if values dont match', () => { + const adr = form.get('dangerousGoods'); + const name = form.get('techRecord_adrDetails_applicantDetails_name') as CustomFormControl; + const street = form.get('techRecord_adrDetails_applicantDetails_street') as CustomFormControl; + const town = form.get('techRecord_adrDetails_applicantDetails_town') as CustomFormControl; + + adr?.patchValue(true); + + CustomValidators.showGroupsWhenEqualTo([false], ['adr'])(adr as AbstractControl); + + expect(name?.meta.hide).toBe(true); + expect(street?.meta.hide).toBe(true); + expect(town?.meta.hide).toBe(true); + }); + it('should only change hide on groups included in array', () => { + const adr = form.get('dangerousGoods'); + const name = form.get('techRecord_adrDetails_applicantDetails_name') as CustomFormControl; + const street = form.get('techRecord_adrDetails_applicantDetails_street') as CustomFormControl; + const town = form.get('techRecord_adrDetails_applicantDetails_town') as CustomFormControl; + + adr?.patchValue(true); + + CustomValidators.showGroupsWhenEqualTo([true], ['name'])(adr as AbstractControl); + + expect(name?.meta.hide).toBe(false); + expect(street?.meta.hide).toBe(true); + expect(town?.meta.hide).toBe(true); + }); }); describe('hideGroupsWhenEqualTo', () => { - let form: CustomFormGroup; - beforeEach(() => { - form = new CustomFormGroup( - { - name: 'form-group', - type: FormNodeTypes.GROUP, - children: - [ - { - name: 'dangerousGoods', - value: false, - type: FormNodeTypes.CONTROL, - }, - { - name: 'techRecord_adrDetails_applicantDetails_name', - type: FormNodeTypes.CONTROL, - hide: false, - groups: ['adr'], - }, - { - name: 'techRecord_adrDetails_applicantDetails_street', - type: FormNodeTypes.CONTROL, - hide: false, - groups: ['adr'], - }, - { - name: 'techRecord_adrDetails_applicantDetails_town', - type: FormNodeTypes.CONTROL, - hide: false, - groups: ['adr'], - }, - ], - }, - { - dangerousGoods: new CustomFormControl( - { - name: 'dangerousGoods', - type: FormNodeTypes.CONTROL, - }, - undefined, - ), - techRecord_adrDetails_applicantDetails_name: new CustomFormControl( - { - name: 'techRecord_adrDetails_applicantDetails_name', - type: FormNodeTypes.CONTROL, - hide: false, - groups: ['adr', 'name'], - }, - undefined, - ), - techRecord_adrDetails_applicantDetails_street: new CustomFormControl( - { - name: 'techRecord_adrDetails_applicantDetails_street', - type: FormNodeTypes.CONTROL, - hide: false, - groups: ['adr'], - }, - undefined, - ), - techRecord_adrDetails_applicantDetails_town: new CustomFormControl( - { - name: 'techRecord_adrDetails_applicantDetails_town', - type: FormNodeTypes.CONTROL, - hide: false, - groups: ['adr'], - }, - undefined, - ), - }, - ); - }); - it('should set hide as true on a control if it is in a group included in the array passed and values match true', () => { - const adr = form.get('dangerousGoods'); - const name = form.get('techRecord_adrDetails_applicantDetails_name') as CustomFormControl; - const street = form.get('techRecord_adrDetails_applicantDetails_street') as CustomFormControl; - const town = form.get('techRecord_adrDetails_applicantDetails_town') as CustomFormControl; - - adr?.patchValue(true); - - CustomValidators.hideGroupsWhenEqualTo([true], ['adr'])(adr as AbstractControl); - - expect(name?.meta.hide).toBe(true); - expect(street?.meta.hide).toBe(true); - expect(town?.meta.hide).toBe(true); - }); - it('should set hide as true on a control if it is in a group included in the array passed and values match false', () => { - const adr = form.get('dangerousGoods'); - const name = form.get('techRecord_adrDetails_applicantDetails_name') as CustomFormControl; - const street = form.get('techRecord_adrDetails_applicantDetails_street') as CustomFormControl; - const town = form.get('techRecord_adrDetails_applicantDetails_town') as CustomFormControl; - - adr?.patchValue(false); - - CustomValidators.hideGroupsWhenEqualTo([false], ['adr'])(adr as AbstractControl); - - expect(name?.meta.hide).toBe(true); - expect(street?.meta.hide).toBe(true); - expect(town?.meta.hide).toBe(true); - }); - it('should not change the hide flag if values dont match', () => { - const adr = form.get('dangerousGoods'); - const name = form.get('techRecord_adrDetails_applicantDetails_name') as CustomFormControl; - const street = form.get('techRecord_adrDetails_applicantDetails_street') as CustomFormControl; - const town = form.get('techRecord_adrDetails_applicantDetails_town') as CustomFormControl; - - adr?.patchValue(true); - - CustomValidators.hideGroupsWhenEqualTo([false], ['adr'])(adr as AbstractControl); - - expect(name?.meta.hide).toBe(false); - expect(street?.meta.hide).toBe(false); - expect(town?.meta.hide).toBe(false); - }); - it('should only change hide on groups included in array', () => { - const adr = form.get('dangerousGoods'); - const name = form.get('techRecord_adrDetails_applicantDetails_name') as CustomFormControl; - const street = form.get('techRecord_adrDetails_applicantDetails_street') as CustomFormControl; - const town = form.get('techRecord_adrDetails_applicantDetails_town') as CustomFormControl; - - adr?.patchValue(true); - - CustomValidators.hideGroupsWhenEqualTo([true], ['name'])(adr as AbstractControl); - - expect(name?.meta.hide).toBe(true); - expect(street?.meta.hide).toBe(false); - expect(town?.meta.hide).toBe(false); - }); + let form: CustomFormGroup; + beforeEach(() => { + form = new CustomFormGroup( + { + name: 'form-group', + type: FormNodeTypes.GROUP, + children: [ + { + name: 'dangerousGoods', + value: false, + type: FormNodeTypes.CONTROL, + }, + { + name: 'techRecord_adrDetails_applicantDetails_name', + type: FormNodeTypes.CONTROL, + hide: false, + groups: ['adr'], + }, + { + name: 'techRecord_adrDetails_applicantDetails_street', + type: FormNodeTypes.CONTROL, + hide: false, + groups: ['adr'], + }, + { + name: 'techRecord_adrDetails_applicantDetails_town', + type: FormNodeTypes.CONTROL, + hide: false, + groups: ['adr'], + }, + ], + }, + { + dangerousGoods: new CustomFormControl( + { + name: 'dangerousGoods', + type: FormNodeTypes.CONTROL, + }, + undefined + ), + techRecord_adrDetails_applicantDetails_name: new CustomFormControl( + { + name: 'techRecord_adrDetails_applicantDetails_name', + type: FormNodeTypes.CONTROL, + hide: false, + groups: ['adr', 'name'], + }, + undefined + ), + techRecord_adrDetails_applicantDetails_street: new CustomFormControl( + { + name: 'techRecord_adrDetails_applicantDetails_street', + type: FormNodeTypes.CONTROL, + hide: false, + groups: ['adr'], + }, + undefined + ), + techRecord_adrDetails_applicantDetails_town: new CustomFormControl( + { + name: 'techRecord_adrDetails_applicantDetails_town', + type: FormNodeTypes.CONTROL, + hide: false, + groups: ['adr'], + }, + undefined + ), + } + ); + }); + it('should set hide as true on a control if it is in a group included in the array passed and values match true', () => { + const adr = form.get('dangerousGoods'); + const name = form.get('techRecord_adrDetails_applicantDetails_name') as CustomFormControl; + const street = form.get('techRecord_adrDetails_applicantDetails_street') as CustomFormControl; + const town = form.get('techRecord_adrDetails_applicantDetails_town') as CustomFormControl; + + adr?.patchValue(true); + + CustomValidators.hideGroupsWhenEqualTo([true], ['adr'])(adr as AbstractControl); + + expect(name?.meta.hide).toBe(true); + expect(street?.meta.hide).toBe(true); + expect(town?.meta.hide).toBe(true); + }); + it('should set hide as true on a control if it is in a group included in the array passed and values match false', () => { + const adr = form.get('dangerousGoods'); + const name = form.get('techRecord_adrDetails_applicantDetails_name') as CustomFormControl; + const street = form.get('techRecord_adrDetails_applicantDetails_street') as CustomFormControl; + const town = form.get('techRecord_adrDetails_applicantDetails_town') as CustomFormControl; + + adr?.patchValue(false); + + CustomValidators.hideGroupsWhenEqualTo([false], ['adr'])(adr as AbstractControl); + + expect(name?.meta.hide).toBe(true); + expect(street?.meta.hide).toBe(true); + expect(town?.meta.hide).toBe(true); + }); + it('should not change the hide flag if values dont match', () => { + const adr = form.get('dangerousGoods'); + const name = form.get('techRecord_adrDetails_applicantDetails_name') as CustomFormControl; + const street = form.get('techRecord_adrDetails_applicantDetails_street') as CustomFormControl; + const town = form.get('techRecord_adrDetails_applicantDetails_town') as CustomFormControl; + + adr?.patchValue(true); + + CustomValidators.hideGroupsWhenEqualTo([false], ['adr'])(adr as AbstractControl); + + expect(name?.meta.hide).toBe(false); + expect(street?.meta.hide).toBe(false); + expect(town?.meta.hide).toBe(false); + }); + it('should only change hide on groups included in array', () => { + const adr = form.get('dangerousGoods'); + const name = form.get('techRecord_adrDetails_applicantDetails_name') as CustomFormControl; + const street = form.get('techRecord_adrDetails_applicantDetails_street') as CustomFormControl; + const town = form.get('techRecord_adrDetails_applicantDetails_town') as CustomFormControl; + + adr?.patchValue(true); + + CustomValidators.hideGroupsWhenEqualTo([true], ['name'])(adr as AbstractControl); + + expect(name?.meta.hide).toBe(true); + expect(street?.meta.hide).toBe(false); + expect(town?.meta.hide).toBe(false); + }); }); describe('addWarningIfFalse', () => { - let form: CustomFormGroup; - beforeEach(() => { - form = new CustomFormGroup( - { - name: 'form-group', - type: FormNodeTypes.GROUP, - children: - [ - { - name: 'dangerousGoods', - value: true, - type: FormNodeTypes.CONTROL, - }, - { - name: 'techRecord_adrDetails_applicantDetails_name', - type: FormNodeTypes.CONTROL, - hide: false, - }, - ], - }, - { - dangerousGoods: new CustomFormControl( - { - name: 'dangerousGoods', - type: FormNodeTypes.CONTROL, - }, - undefined, - ), - techRecord_adrDetails_applicantDetails_name: new CustomFormControl( - { - name: 'techRecord_adrDetails_applicantDetails_name', - type: FormNodeTypes.CONTROL, - hide: false, - }, - undefined, - ), - }, - ); - }); - it('should display a warning if the value is false and it has adr fields on record', () => { - const adr = form.get('dangerousGoods') as CustomFormControl; - const name = form.get('techRecord_adrDetails_applicantDetails_name') as CustomFormControl; - - name.patchValue('test'); - name.markAsTouched(); - adr.patchValue(false); - adr.markAsDirty(); - - CustomValidators.addWarningForAdrField('Test warning')(adr as AbstractControl); - expect(adr.meta.warning).toBe('Test warning'); - }); - it('should remove the warning if the value true', () => { - const adr = form.get('dangerousGoods') as CustomFormControl; - const name = form.get('techRecord_adrDetails_applicantDetails_name') as CustomFormControl; - - name.patchValue('test'); - name.markAsTouched(); - adr.patchValue(false); - adr.markAsDirty(); - - CustomValidators.addWarningForAdrField('Test warning')(adr as AbstractControl); - expect(adr.meta.warning).toBe('Test warning'); - - adr?.patchValue(true); - - CustomValidators.addWarningForAdrField('Test warning')(adr as AbstractControl); - expect(adr.meta.warning).toBeUndefined(); - }); - it('should not have a warning if the control is pristine and value is false', () => { - const adr = form.get('dangerousGoods') as CustomFormControl; - - adr.patchValue(false); - - CustomValidators.addWarningForAdrField('Test warning')(adr as AbstractControl); - expect(adr.meta.warning).toBeUndefined(); - }); - it('should not have a warning if the value is false but there is no adr information on the record', () => { - const adr = form.get('dangerousGoods') as CustomFormControl; - - adr.patchValue(false); - adr.markAsDirty(); - - CustomValidators.addWarningForAdrField('Test warning')(adr as AbstractControl); - expect(adr.meta.warning).toBeUndefined(); - }); - it('should not have a warning if the control is pristine and value is true', () => { - const adr = form.get('dangerousGoods') as CustomFormControl; - - adr?.patchValue(true); - - CustomValidators.addWarningForAdrField('Test warning')(adr as AbstractControl); - expect(adr.meta.warning).toBeUndefined(); - }); + let form: CustomFormGroup; + beforeEach(() => { + form = new CustomFormGroup( + { + name: 'form-group', + type: FormNodeTypes.GROUP, + children: [ + { + name: 'dangerousGoods', + value: true, + type: FormNodeTypes.CONTROL, + }, + { + name: 'techRecord_adrDetails_applicantDetails_name', + type: FormNodeTypes.CONTROL, + hide: false, + }, + ], + }, + { + dangerousGoods: new CustomFormControl( + { + name: 'dangerousGoods', + type: FormNodeTypes.CONTROL, + }, + undefined + ), + techRecord_adrDetails_applicantDetails_name: new CustomFormControl( + { + name: 'techRecord_adrDetails_applicantDetails_name', + type: FormNodeTypes.CONTROL, + hide: false, + }, + undefined + ), + } + ); + }); + it('should display a warning if the value is false and it has adr fields on record', () => { + const adr = form.get('dangerousGoods') as CustomFormControl; + const name = form.get('techRecord_adrDetails_applicantDetails_name') as CustomFormControl; + + name.patchValue('test'); + name.markAsTouched(); + adr.patchValue(false); + adr.markAsDirty(); + + CustomValidators.addWarningForAdrField('Test warning')(adr as AbstractControl); + expect(adr.meta.warning).toBe('Test warning'); + }); + it('should remove the warning if the value true', () => { + const adr = form.get('dangerousGoods') as CustomFormControl; + const name = form.get('techRecord_adrDetails_applicantDetails_name') as CustomFormControl; + + name.patchValue('test'); + name.markAsTouched(); + adr.patchValue(false); + adr.markAsDirty(); + + CustomValidators.addWarningForAdrField('Test warning')(adr as AbstractControl); + expect(adr.meta.warning).toBe('Test warning'); + + adr?.patchValue(true); + + CustomValidators.addWarningForAdrField('Test warning')(adr as AbstractControl); + expect(adr.meta.warning).toBeUndefined(); + }); + it('should not have a warning if the control is pristine and value is false', () => { + const adr = form.get('dangerousGoods') as CustomFormControl; + + adr.patchValue(false); + + CustomValidators.addWarningForAdrField('Test warning')(adr as AbstractControl); + expect(adr.meta.warning).toBeUndefined(); + }); + it('should not have a warning if the value is false but there is no adr information on the record', () => { + const adr = form.get('dangerousGoods') as CustomFormControl; + + adr.patchValue(false); + adr.markAsDirty(); + + CustomValidators.addWarningForAdrField('Test warning')(adr as AbstractControl); + expect(adr.meta.warning).toBeUndefined(); + }); + it('should not have a warning if the control is pristine and value is true', () => { + const adr = form.get('dangerousGoods') as CustomFormControl; + + adr?.patchValue(true); + + CustomValidators.addWarningForAdrField('Test warning')(adr as AbstractControl); + expect(adr.meta.warning).toBeUndefined(); + }); }); describe('enum', () => { - it.each([ - [{ enum: true }, NaN], - [{ enum: true }, undefined], - [{ enum: true }, null], - [{ enum: true }, ''], - [{ enum: true }, 'Small series'], - [null, 'NTA'], - [null, 'ECTA'], - [null, 'IVA'], - [null, 'NSSTA'], - [null, 'ECSSTA'], - [null, 'GB WVTA'], - [null, 'UKNI WVTA'], - [null, 'EU WVTA Pre 23'], - [null, 'EU WVTA 23 on'], - [null, 'QNIG'], - [null, 'Prov.GB WVTA'], - [null, 'Small series NKSXX'], - [null, 'Small series NKS'], - [null, 'IVA - VCA'], - [null, 'IVA - DVSA/NI'], - ])('should return %p when control value is %s', (expected: object | null, input) => { - expect(CustomValidators.isMemberOfEnum(ApprovalType, { allowFalsy: false })(new FormControl(input))).toEqual(expected); - }); - - it.each([ - [null, NaN], - [null, undefined], - [null, null], - [null, ''], - [{ enum: true }, 'Small series'], - [null, 'NTA'], - [null, 'ECTA'], - [null, 'IVA'], - [null, 'NSSTA'], - [null, 'ECSSTA'], - [null, 'GB WVTA'], - [null, 'UKNI WVTA'], - [null, 'EU WVTA Pre 23'], - [null, 'EU WVTA 23 on'], - [null, 'QNIG'], - [null, 'Prov.GB WVTA'], - [null, 'Small series NKSXX'], - [null, 'Small series NKS'], - [null, 'IVA - VCA'], - [null, 'IVA - DVSA/NI'], - ])('should return %p when control value is %s', (expected: object | null, input) => { - expect(CustomValidators.isMemberOfEnum(ApprovalType, { allowFalsy: true })(new FormControl(input))).toEqual(expected); - }); + it.each([ + [{ enum: true }, Number.NaN], + [{ enum: true }, undefined], + [{ enum: true }, null], + [{ enum: true }, ''], + [{ enum: true }, 'Small series'], + [null, 'NTA'], + [null, 'ECTA'], + [null, 'IVA'], + [null, 'NSSTA'], + [null, 'ECSSTA'], + [null, 'GB WVTA'], + [null, 'UKNI WVTA'], + [null, 'EU WVTA Pre 23'], + [null, 'EU WVTA 23 on'], + [null, 'QNIG'], + [null, 'Prov.GB WVTA'], + [null, 'Small series NKSXX'], + [null, 'Small series NKS'], + [null, 'IVA - VCA'], + [null, 'IVA - DVSA/NI'], + ])('should return %p when control value is %s', (expected: object | null, input) => { + expect(CustomValidators.isMemberOfEnum(ApprovalType, { allowFalsy: false })(new FormControl(input))).toEqual( + expected + ); + }); + + it.each([ + [null, Number.NaN], + [null, undefined], + [null, null], + [null, ''], + [{ enum: true }, 'Small series'], + [null, 'NTA'], + [null, 'ECTA'], + [null, 'IVA'], + [null, 'NSSTA'], + [null, 'ECSSTA'], + [null, 'GB WVTA'], + [null, 'UKNI WVTA'], + [null, 'EU WVTA Pre 23'], + [null, 'EU WVTA 23 on'], + [null, 'QNIG'], + [null, 'Prov.GB WVTA'], + [null, 'Small series NKSXX'], + [null, 'Small series NKS'], + [null, 'IVA - VCA'], + [null, 'IVA - DVSA/NI'], + ])('should return %p when control value is %s', (expected: object | null, input) => { + expect(CustomValidators.isMemberOfEnum(ApprovalType, { allowFalsy: true })(new FormControl(input))).toEqual( + expected + ); + }); }); describe('showGroupsWhenIncludes', () => { - let form: CustomFormGroup; - beforeEach(() => { - form = new CustomFormGroup( - { - name: 'form-group', - type: FormNodeTypes.GROUP, - children: - [ - { - name: 'dangerousGoods', - value: false, - type: FormNodeTypes.CONTROL, - }, - { - name: 'techRecord_adrDetails_compatibilityGroupJ', - type: FormNodeTypes.CONTROL, - hide: true, - groups: ['compat', 'details'], - }, - ], - }, - { - dangerousGoods: new CustomFormControl( - { - name: 'dangerousGoods', - type: FormNodeTypes.CONTROL, - groups: ['adr'], - }, - undefined, - ), - techRecord_adrDetails_applicantDetails_name: new CustomFormControl( - { - name: 'techRecord_adrDetails_applicantDetails_name', - type: FormNodeTypes.CONTROL, - hide: false, - groups: ['adr', 'name'], - }, - undefined, - ), - techRecord_adrDetails_permittedDangerousGoods: new CustomFormControl( - { - name: 'techRecord_adrDetails_permittedDangerousGoods', - type: FormNodeTypes.CONTROL, - hide: false, - groups: ['adr', 'details'], - validators: [ - { - name: ValidatorNames.ShowGroupsWhenIncludes, - args: { - values: [ADRDangerousGood.EXPLOSIVES_TYPE_2, ADRDangerousGood.EXPLOSIVES_TYPE_3], - groups: ['compat'], - }, - }, - { - name: ValidatorNames.HideGroupsWhenExcludes, - args: { - values: [ADRDangerousGood.EXPLOSIVES_TYPE_2, ADRDangerousGood.EXPLOSIVES_TYPE_3], - groups: ['compat'], - }, - }, - ], - }, - undefined, - ), - techRecord_adrDetails_compatibilityGroupJ: new CustomFormControl({ - name: 'techRecord_adrDetails_compatibilityGroupJ', - type: FormNodeTypes.CONTROL, - hide: true, - groups: ['compat', 'details'], - }), - }, - ); - }); - - it('should set hide to FALSE when its value DOES include one of the values passed, and IS part of the group whitelist', () => { - const name = form.get('techRecord_adrDetails_applicantDetails_name') as CustomFormControl; - const permitted = form.get('techRecord_adrDetails_permittedDangerousGoods') as CustomFormControl; - const compat = form.get('techRecord_adrDetails_compatibilityGroupJ') as CustomFormControl; - - permitted?.patchValue([ADRDangerousGood.EXPLOSIVES_TYPE_2]); - permitted?.markAsDirty(); - - CustomValidators.showGroupsWhenIncludes([ADRDangerousGood.EXPLOSIVES_TYPE_2], ['compat'])(permitted as AbstractControl); - - expect(name?.meta.hide).toBe(false); - expect(compat?.meta.hide).toBe(false); - }); - - it('should NOT set hide to FALSE when its value DOES include one of the values passed, but is NOT part of the group whitelist', () => { - const name = form.get('techRecord_adrDetails_applicantDetails_name') as CustomFormControl; - const permitted = form.get('techRecord_adrDetails_permittedDangerousGoods') as CustomFormControl; - const compat = form.get('techRecord_adrDetails_compatibilityGroupJ') as CustomFormControl; - - permitted?.patchValue(['I']); - permitted?.markAsDirty(); - - CustomValidators.showGroupsWhenIncludes(['I'], ['random_group', 'other_random_group'])(permitted as AbstractControl); - - expect(name?.meta.hide).toBe(false); - expect(compat?.meta.hide).toBe(true); - }); - - it('should NOT set hide to FALSE when its value DOES NOT include one of the values passed, but IS part of the group whitelist', () => { - const name = form.get('techRecord_adrDetails_applicantDetails_name') as CustomFormControl; - const permitted = form.get('techRecord_adrDetails_permittedDangerousGoods') as CustomFormControl; - const compat = form.get('techRecord_adrDetails_compatibilityGroupJ') as CustomFormControl; - - permitted?.patchValue(['E']); - permitted?.markAsDirty(); - - CustomValidators.showGroupsWhenIncludes(['I'], ['compat', 'other_random_group'])(permitted as AbstractControl); - - expect(name?.meta.hide).toBe(false); - expect(compat?.meta.hide).toBe(true); - }); + let form: CustomFormGroup; + beforeEach(() => { + form = new CustomFormGroup( + { + name: 'form-group', + type: FormNodeTypes.GROUP, + children: [ + { + name: 'dangerousGoods', + value: false, + type: FormNodeTypes.CONTROL, + }, + { + name: 'techRecord_adrDetails_compatibilityGroupJ', + type: FormNodeTypes.CONTROL, + hide: true, + groups: ['compat', 'details'], + }, + ], + }, + { + dangerousGoods: new CustomFormControl( + { + name: 'dangerousGoods', + type: FormNodeTypes.CONTROL, + groups: ['adr'], + }, + undefined + ), + techRecord_adrDetails_applicantDetails_name: new CustomFormControl( + { + name: 'techRecord_adrDetails_applicantDetails_name', + type: FormNodeTypes.CONTROL, + hide: false, + groups: ['adr', 'name'], + }, + undefined + ), + techRecord_adrDetails_permittedDangerousGoods: new CustomFormControl( + { + name: 'techRecord_adrDetails_permittedDangerousGoods', + type: FormNodeTypes.CONTROL, + hide: false, + groups: ['adr', 'details'], + validators: [ + { + name: ValidatorNames.ShowGroupsWhenIncludes, + args: { + values: [ADRDangerousGood.EXPLOSIVES_TYPE_2, ADRDangerousGood.EXPLOSIVES_TYPE_3], + groups: ['compat'], + }, + }, + { + name: ValidatorNames.HideGroupsWhenExcludes, + args: { + values: [ADRDangerousGood.EXPLOSIVES_TYPE_2, ADRDangerousGood.EXPLOSIVES_TYPE_3], + groups: ['compat'], + }, + }, + ], + }, + undefined + ), + techRecord_adrDetails_compatibilityGroupJ: new CustomFormControl({ + name: 'techRecord_adrDetails_compatibilityGroupJ', + type: FormNodeTypes.CONTROL, + hide: true, + groups: ['compat', 'details'], + }), + } + ); + }); + + it('should set hide to FALSE when its value DOES include one of the values passed, and IS part of the group whitelist', () => { + const name = form.get('techRecord_adrDetails_applicantDetails_name') as CustomFormControl; + const permitted = form.get('techRecord_adrDetails_permittedDangerousGoods') as CustomFormControl; + const compat = form.get('techRecord_adrDetails_compatibilityGroupJ') as CustomFormControl; + + permitted?.patchValue([ADRDangerousGood.EXPLOSIVES_TYPE_2]); + permitted?.markAsDirty(); + + CustomValidators.showGroupsWhenIncludes( + [ADRDangerousGood.EXPLOSIVES_TYPE_2], + ['compat'] + )(permitted as AbstractControl); + + expect(name?.meta.hide).toBe(false); + expect(compat?.meta.hide).toBe(false); + }); + + it('should NOT set hide to FALSE when its value DOES include one of the values passed, but is NOT part of the group whitelist', () => { + const name = form.get('techRecord_adrDetails_applicantDetails_name') as CustomFormControl; + const permitted = form.get('techRecord_adrDetails_permittedDangerousGoods') as CustomFormControl; + const compat = form.get('techRecord_adrDetails_compatibilityGroupJ') as CustomFormControl; + + permitted?.patchValue(['I']); + permitted?.markAsDirty(); + + CustomValidators.showGroupsWhenIncludes( + ['I'], + ['random_group', 'other_random_group'] + )(permitted as AbstractControl); + + expect(name?.meta.hide).toBe(false); + expect(compat?.meta.hide).toBe(true); + }); + + it('should NOT set hide to FALSE when its value DOES NOT include one of the values passed, but IS part of the group whitelist', () => { + const name = form.get('techRecord_adrDetails_applicantDetails_name') as CustomFormControl; + const permitted = form.get('techRecord_adrDetails_permittedDangerousGoods') as CustomFormControl; + const compat = form.get('techRecord_adrDetails_compatibilityGroupJ') as CustomFormControl; + + permitted?.patchValue(['E']); + permitted?.markAsDirty(); + + CustomValidators.showGroupsWhenIncludes(['I'], ['compat', 'other_random_group'])(permitted as AbstractControl); + + expect(name?.meta.hide).toBe(false); + expect(compat?.meta.hide).toBe(true); + }); }); describe('hideGroupsWhenIncludes', () => { - let form: CustomFormGroup; - beforeEach(() => { - form = new CustomFormGroup( - { - name: 'form-group', - type: FormNodeTypes.GROUP, - children: - [ - { - name: 'dangerousGoods', - value: false, - type: FormNodeTypes.CONTROL, - }, - { - name: 'techRecord_adrDetails_compatibilityGroupJ', - type: FormNodeTypes.CONTROL, - hide: true, - groups: ['compat', 'details'], - }, - ], - }, - { - dangerousGoods: new CustomFormControl( - { - name: 'dangerousGoods', - type: FormNodeTypes.CONTROL, - groups: ['adr'], - }, - undefined, - ), - techRecord_adrDetails_applicantDetails_name: new CustomFormControl( - { - name: 'techRecord_adrDetails_applicantDetails_name', - type: FormNodeTypes.CONTROL, - hide: false, - groups: ['adr', 'name'], - }, - undefined, - ), - techRecord_adrDetails_permittedDangerousGoods: new CustomFormControl( - { - name: 'techRecord_adrDetails_permittedDangerousGoods', - type: FormNodeTypes.CONTROL, - hide: false, - groups: ['adr', 'details'], - validators: [ - { - name: ValidatorNames.ShowGroupsWhenIncludes, - args: { - values: [ADRDangerousGood.EXPLOSIVES_TYPE_2, ADRDangerousGood.EXPLOSIVES_TYPE_3], - groups: ['compat'], - }, - }, - { - name: ValidatorNames.HideGroupsWhenExcludes, - args: { - values: [ADRDangerousGood.EXPLOSIVES_TYPE_2, ADRDangerousGood.EXPLOSIVES_TYPE_3], - groups: ['compat'], - }, - }, - ], - }, - undefined, - ), - techRecord_adrDetails_compatibilityGroupJ: new CustomFormControl({ - name: 'techRecord_adrDetails_compatibilityGroupJ', - type: FormNodeTypes.CONTROL, - hide: true, - groups: ['compat', 'details'], - }), - }, - ); - }); - it('should set hide to TRUE when its value DOES include one of the values passed, and IS part of the group whitelist', () => { - const name = form.get('techRecord_adrDetails_applicantDetails_name') as CustomFormControl; - const permitted = form.get('techRecord_adrDetails_permittedDangerousGoods') as CustomFormControl; - const compat = form.get('techRecord_adrDetails_compatibilityGroupJ') as CustomFormControl; - - permitted?.patchValue([ADRDangerousGood.EXPLOSIVES_TYPE_2]); - permitted?.markAsDirty(); - - CustomValidators.hideGroupsWhenIncludes([ADRDangerousGood.EXPLOSIVES_TYPE_2], ['compat'])(permitted as AbstractControl); - - expect(name?.meta.hide).toBe(false); - expect(compat?.meta.hide).toBe(true); - }); - - it('should NOT set hide to TRUE when its value DOES include one of the values passed, but is NOT part of the group whitelist', () => { - const name = form.get('techRecord_adrDetails_applicantDetails_name') as CustomFormControl; - const permitted = form.get('techRecord_adrDetails_permittedDangerousGoods') as CustomFormControl; - const compat = form.get('techRecord_adrDetails_compatibilityGroupJ') as CustomFormControl; - compat.meta.hide = false; - - permitted?.patchValue([ADRDangerousGood.EXPLOSIVES_TYPE_2]); - permitted?.markAsDirty(); - - CustomValidators.hideGroupsWhenEqualTo([ADRDangerousGood.EXPLOSIVES_TYPE_2], ['compat', 'other_random_group'])(permitted as AbstractControl); - - expect(name?.meta.hide).toBe(false); - expect(compat?.meta.hide).toBe(false); - }); - - it('should NOT set hide to TRUE when its value DOES NOT include one of the values passed, but IS part of the group whitelist', () => { - const name = form.get('techRecord_adrDetails_applicantDetails_name') as CustomFormControl; - const permitted = form.get('techRecord_adrDetails_permittedDangerousGoods') as CustomFormControl; - const compat = form.get('techRecord_adrDetails_compatibilityGroupJ') as CustomFormControl; - compat.meta.hide = false; - - permitted?.patchValue(['E']); - permitted?.markAsDirty(); - - CustomValidators.hideGroupsWhenIncludes(['I'], ['compat', 'other_random_group'])(permitted as AbstractControl); - - expect(name?.meta.hide).toBe(false); - expect(compat?.meta.hide).toBe(false); - }); + let form: CustomFormGroup; + beforeEach(() => { + form = new CustomFormGroup( + { + name: 'form-group', + type: FormNodeTypes.GROUP, + children: [ + { + name: 'dangerousGoods', + value: false, + type: FormNodeTypes.CONTROL, + }, + { + name: 'techRecord_adrDetails_compatibilityGroupJ', + type: FormNodeTypes.CONTROL, + hide: true, + groups: ['compat', 'details'], + }, + ], + }, + { + dangerousGoods: new CustomFormControl( + { + name: 'dangerousGoods', + type: FormNodeTypes.CONTROL, + groups: ['adr'], + }, + undefined + ), + techRecord_adrDetails_applicantDetails_name: new CustomFormControl( + { + name: 'techRecord_adrDetails_applicantDetails_name', + type: FormNodeTypes.CONTROL, + hide: false, + groups: ['adr', 'name'], + }, + undefined + ), + techRecord_adrDetails_permittedDangerousGoods: new CustomFormControl( + { + name: 'techRecord_adrDetails_permittedDangerousGoods', + type: FormNodeTypes.CONTROL, + hide: false, + groups: ['adr', 'details'], + validators: [ + { + name: ValidatorNames.ShowGroupsWhenIncludes, + args: { + values: [ADRDangerousGood.EXPLOSIVES_TYPE_2, ADRDangerousGood.EXPLOSIVES_TYPE_3], + groups: ['compat'], + }, + }, + { + name: ValidatorNames.HideGroupsWhenExcludes, + args: { + values: [ADRDangerousGood.EXPLOSIVES_TYPE_2, ADRDangerousGood.EXPLOSIVES_TYPE_3], + groups: ['compat'], + }, + }, + ], + }, + undefined + ), + techRecord_adrDetails_compatibilityGroupJ: new CustomFormControl({ + name: 'techRecord_adrDetails_compatibilityGroupJ', + type: FormNodeTypes.CONTROL, + hide: true, + groups: ['compat', 'details'], + }), + } + ); + }); + it('should set hide to TRUE when its value DOES include one of the values passed, and IS part of the group whitelist', () => { + const name = form.get('techRecord_adrDetails_applicantDetails_name') as CustomFormControl; + const permitted = form.get('techRecord_adrDetails_permittedDangerousGoods') as CustomFormControl; + const compat = form.get('techRecord_adrDetails_compatibilityGroupJ') as CustomFormControl; + + permitted?.patchValue([ADRDangerousGood.EXPLOSIVES_TYPE_2]); + permitted?.markAsDirty(); + + CustomValidators.hideGroupsWhenIncludes( + [ADRDangerousGood.EXPLOSIVES_TYPE_2], + ['compat'] + )(permitted as AbstractControl); + + expect(name?.meta.hide).toBe(false); + expect(compat?.meta.hide).toBe(true); + }); + + it('should NOT set hide to TRUE when its value DOES include one of the values passed, but is NOT part of the group whitelist', () => { + const name = form.get('techRecord_adrDetails_applicantDetails_name') as CustomFormControl; + const permitted = form.get('techRecord_adrDetails_permittedDangerousGoods') as CustomFormControl; + const compat = form.get('techRecord_adrDetails_compatibilityGroupJ') as CustomFormControl; + compat.meta.hide = false; + + permitted?.patchValue([ADRDangerousGood.EXPLOSIVES_TYPE_2]); + permitted?.markAsDirty(); + + CustomValidators.hideGroupsWhenEqualTo( + [ADRDangerousGood.EXPLOSIVES_TYPE_2], + ['compat', 'other_random_group'] + )(permitted as AbstractControl); + + expect(name?.meta.hide).toBe(false); + expect(compat?.meta.hide).toBe(false); + }); + + it('should NOT set hide to TRUE when its value DOES NOT include one of the values passed, but IS part of the group whitelist', () => { + const name = form.get('techRecord_adrDetails_applicantDetails_name') as CustomFormControl; + const permitted = form.get('techRecord_adrDetails_permittedDangerousGoods') as CustomFormControl; + const compat = form.get('techRecord_adrDetails_compatibilityGroupJ') as CustomFormControl; + compat.meta.hide = false; + + permitted?.patchValue(['E']); + permitted?.markAsDirty(); + + CustomValidators.hideGroupsWhenIncludes(['I'], ['compat', 'other_random_group'])(permitted as AbstractControl); + + expect(name?.meta.hide).toBe(false); + expect(compat?.meta.hide).toBe(false); + }); }); describe('showGroupsWhenExcludes', () => { - let form: CustomFormGroup; - beforeEach(() => { - form = new CustomFormGroup( - { - name: 'form-group', - type: FormNodeTypes.GROUP, - children: - [ - { - name: 'dangerousGoods', - value: false, - type: FormNodeTypes.CONTROL, - }, - { - name: 'techRecord_adrDetails_compatibilityGroupJ', - type: FormNodeTypes.CONTROL, - hide: true, - groups: ['compat', 'details'], - }, - ], - }, - { - dangerousGoods: new CustomFormControl( - { - name: 'dangerousGoods', - type: FormNodeTypes.CONTROL, - groups: ['adr'], - }, - undefined, - ), - techRecord_adrDetails_applicantDetails_name: new CustomFormControl( - { - name: 'techRecord_adrDetails_applicantDetails_name', - type: FormNodeTypes.CONTROL, - hide: false, - groups: ['adr', 'name'], - }, - undefined, - ), - techRecord_adrDetails_permittedDangerousGoods: new CustomFormControl( - { - name: 'techRecord_adrDetails_permittedDangerousGoods', - type: FormNodeTypes.CONTROL, - hide: false, - groups: ['adr', 'details'], - validators: [ - { - name: ValidatorNames.ShowGroupsWhenIncludes, - args: { - values: [ADRDangerousGood.EXPLOSIVES_TYPE_2, ADRDangerousGood.EXPLOSIVES_TYPE_3], - groups: ['compat'], - }, - }, - { - name: ValidatorNames.HideGroupsWhenExcludes, - args: { - values: [ADRDangerousGood.EXPLOSIVES_TYPE_2, ADRDangerousGood.EXPLOSIVES_TYPE_3], - groups: ['compat'], - }, - }, - ], - }, - undefined, - ), - techRecord_adrDetails_compatibilityGroupJ: new CustomFormControl({ - name: 'techRecord_adrDetails_compatibilityGroupJ', - type: FormNodeTypes.CONTROL, - hide: true, - groups: ['compat', 'details'], - }), - }, - ); - }); - it('should set hide to FALSE when its value DOES NOT include one of the values passed, and IS part of the group whitelist', () => { - const name = form.get('techRecord_adrDetails_applicantDetails_name') as CustomFormControl; - const permitted = form.get('techRecord_adrDetails_permittedDangerousGoods') as CustomFormControl; - const compat = form.get('techRecord_adrDetails_compatibilityGroupJ') as CustomFormControl; - - permitted?.patchValue([ADRDangerousGood.CARBON_DISULPHIDE]); - permitted?.markAsDirty(); - - CustomValidators.showGroupsWhenExcludes([ADRDangerousGood.EXPLOSIVES_TYPE_2], ['compat'])(permitted as AbstractControl); - - expect(name?.meta.hide).toBe(false); - expect(compat?.meta.hide).toBe(false); - }); - - it('should NOT set hide to FALSE when its value DOES NOT include one of the values passed, but is NOT part of the group whitelist', () => { - const name = form.get('techRecord_adrDetails_applicantDetails_name') as CustomFormControl; - const permitted = form.get('techRecord_adrDetails_permittedDangerousGoods') as CustomFormControl; - const compat = form.get('techRecord_adrDetails_compatibilityGroupJ') as CustomFormControl; - - permitted?.patchValue([ADRDangerousGood.CARBON_DISULPHIDE]); - permitted?.markAsDirty(); - - CustomValidators.showGroupsWhenExcludes([ADRDangerousGood.EXPLOSIVES_TYPE_2], ['other_group'])(permitted as AbstractControl); - - expect(name?.meta.hide).toBe(false); - expect(compat?.meta.hide).toBe(true); - }); - - it('should NOT set hide to FALSE when its value DOES include one of the values passed, but IS part of the group whitelist', () => { - const name = form.get('techRecord_adrDetails_applicantDetails_name') as CustomFormControl; - const permitted = form.get('techRecord_adrDetails_permittedDangerousGoods') as CustomFormControl; - const compat = form.get('techRecord_adrDetails_compatibilityGroupJ') as CustomFormControl; - - permitted?.patchValue([ADRDangerousGood.EXPLOSIVES_TYPE_2]); - permitted?.markAsDirty(); - - CustomValidators.showGroupsWhenExcludes([ADRDangerousGood.EXPLOSIVES_TYPE_2], ['compat'])(permitted as AbstractControl); - - expect(name?.meta.hide).toBe(false); - expect(compat?.meta.hide).toBe(true); - }); + let form: CustomFormGroup; + beforeEach(() => { + form = new CustomFormGroup( + { + name: 'form-group', + type: FormNodeTypes.GROUP, + children: [ + { + name: 'dangerousGoods', + value: false, + type: FormNodeTypes.CONTROL, + }, + { + name: 'techRecord_adrDetails_compatibilityGroupJ', + type: FormNodeTypes.CONTROL, + hide: true, + groups: ['compat', 'details'], + }, + ], + }, + { + dangerousGoods: new CustomFormControl( + { + name: 'dangerousGoods', + type: FormNodeTypes.CONTROL, + groups: ['adr'], + }, + undefined + ), + techRecord_adrDetails_applicantDetails_name: new CustomFormControl( + { + name: 'techRecord_adrDetails_applicantDetails_name', + type: FormNodeTypes.CONTROL, + hide: false, + groups: ['adr', 'name'], + }, + undefined + ), + techRecord_adrDetails_permittedDangerousGoods: new CustomFormControl( + { + name: 'techRecord_adrDetails_permittedDangerousGoods', + type: FormNodeTypes.CONTROL, + hide: false, + groups: ['adr', 'details'], + validators: [ + { + name: ValidatorNames.ShowGroupsWhenIncludes, + args: { + values: [ADRDangerousGood.EXPLOSIVES_TYPE_2, ADRDangerousGood.EXPLOSIVES_TYPE_3], + groups: ['compat'], + }, + }, + { + name: ValidatorNames.HideGroupsWhenExcludes, + args: { + values: [ADRDangerousGood.EXPLOSIVES_TYPE_2, ADRDangerousGood.EXPLOSIVES_TYPE_3], + groups: ['compat'], + }, + }, + ], + }, + undefined + ), + techRecord_adrDetails_compatibilityGroupJ: new CustomFormControl({ + name: 'techRecord_adrDetails_compatibilityGroupJ', + type: FormNodeTypes.CONTROL, + hide: true, + groups: ['compat', 'details'], + }), + } + ); + }); + it('should set hide to FALSE when its value DOES NOT include one of the values passed, and IS part of the group whitelist', () => { + const name = form.get('techRecord_adrDetails_applicantDetails_name') as CustomFormControl; + const permitted = form.get('techRecord_adrDetails_permittedDangerousGoods') as CustomFormControl; + const compat = form.get('techRecord_adrDetails_compatibilityGroupJ') as CustomFormControl; + + permitted?.patchValue([ADRDangerousGood.CARBON_DISULPHIDE]); + permitted?.markAsDirty(); + + CustomValidators.showGroupsWhenExcludes( + [ADRDangerousGood.EXPLOSIVES_TYPE_2], + ['compat'] + )(permitted as AbstractControl); + + expect(name?.meta.hide).toBe(false); + expect(compat?.meta.hide).toBe(false); + }); + + it('should NOT set hide to FALSE when its value DOES NOT include one of the values passed, but is NOT part of the group whitelist', () => { + const name = form.get('techRecord_adrDetails_applicantDetails_name') as CustomFormControl; + const permitted = form.get('techRecord_adrDetails_permittedDangerousGoods') as CustomFormControl; + const compat = form.get('techRecord_adrDetails_compatibilityGroupJ') as CustomFormControl; + + permitted?.patchValue([ADRDangerousGood.CARBON_DISULPHIDE]); + permitted?.markAsDirty(); + + CustomValidators.showGroupsWhenExcludes( + [ADRDangerousGood.EXPLOSIVES_TYPE_2], + ['other_group'] + )(permitted as AbstractControl); + + expect(name?.meta.hide).toBe(false); + expect(compat?.meta.hide).toBe(true); + }); + + it('should NOT set hide to FALSE when its value DOES include one of the values passed, but IS part of the group whitelist', () => { + const name = form.get('techRecord_adrDetails_applicantDetails_name') as CustomFormControl; + const permitted = form.get('techRecord_adrDetails_permittedDangerousGoods') as CustomFormControl; + const compat = form.get('techRecord_adrDetails_compatibilityGroupJ') as CustomFormControl; + + permitted?.patchValue([ADRDangerousGood.EXPLOSIVES_TYPE_2]); + permitted?.markAsDirty(); + + CustomValidators.showGroupsWhenExcludes( + [ADRDangerousGood.EXPLOSIVES_TYPE_2], + ['compat'] + )(permitted as AbstractControl); + + expect(name?.meta.hide).toBe(false); + expect(compat?.meta.hide).toBe(true); + }); }); describe('hideGroupsWhenExcludes', () => { - let form: CustomFormGroup; - beforeEach(() => { - form = new CustomFormGroup( - { - name: 'form-group', - type: FormNodeTypes.GROUP, - children: - [ - { - name: 'dangerousGoods', - value: false, - type: FormNodeTypes.CONTROL, - }, - { - name: 'techRecord_adrDetails_compatibilityGroupJ', - type: FormNodeTypes.CONTROL, - hide: true, - groups: ['compat', 'details'], - }, - ], - }, - { - dangerousGoods: new CustomFormControl( - { - name: 'dangerousGoods', - type: FormNodeTypes.CONTROL, - groups: ['adr'], - }, - undefined, - ), - techRecord_adrDetails_applicantDetails_name: new CustomFormControl( - { - name: 'techRecord_adrDetails_applicantDetails_name', - type: FormNodeTypes.CONTROL, - hide: false, - groups: ['adr', 'name'], - }, - undefined, - ), - techRecord_adrDetails_permittedDangerousGoods: new CustomFormControl( - { - name: 'techRecord_adrDetails_permittedDangerousGoods', - type: FormNodeTypes.CONTROL, - hide: false, - groups: ['adr', 'details'], - validators: [ - { - name: ValidatorNames.ShowGroupsWhenIncludes, - args: { - values: [ADRDangerousGood.EXPLOSIVES_TYPE_2, ADRDangerousGood.EXPLOSIVES_TYPE_3], - groups: ['compat'], - }, - }, - { - name: ValidatorNames.HideGroupsWhenExcludes, - args: { - values: [ADRDangerousGood.EXPLOSIVES_TYPE_2, ADRDangerousGood.EXPLOSIVES_TYPE_3], - groups: ['compat'], - }, - }, - ], - }, - undefined, - ), - techRecord_adrDetails_compatibilityGroupJ: new CustomFormControl({ - name: 'techRecord_adrDetails_compatibilityGroupJ', - type: FormNodeTypes.CONTROL, - hide: true, - groups: ['compat', 'details'], - }), - }, - ); - }); - it('should set hide to TRUE when its value DOES NOT include one of the values passed, and IS part of the group whitelist', () => { - const name = form.get('techRecord_adrDetails_applicantDetails_name') as CustomFormControl; - const permitted = form.get('techRecord_adrDetails_permittedDangerousGoods') as CustomFormControl; - const compat = form.get('techRecord_adrDetails_compatibilityGroupJ') as CustomFormControl; - compat.meta.hide = false; - - permitted?.patchValue([ADRDangerousGood.CARBON_DISULPHIDE]); - permitted?.markAsDirty(); - - CustomValidators.hideGroupsWhenExcludes([ADRDangerousGood.EXPLOSIVES_TYPE_2], ['compat'])(permitted as AbstractControl); - - expect(name?.meta.hide).toBe(false); - expect(compat?.meta.hide).toBe(true); - }); - - it('should NOT set hide to TRUE when its value DOES NOT include one of the values passed, but is NOT part of the group whitelist', () => { - const name = form.get('techRecord_adrDetails_applicantDetails_name') as CustomFormControl; - const permitted = form.get('techRecord_adrDetails_permittedDangerousGoods') as CustomFormControl; - const compat = form.get('techRecord_adrDetails_compatibilityGroupJ') as CustomFormControl; - compat.meta.hide = false; - - permitted?.patchValue([ADRDangerousGood.CARBON_DISULPHIDE]); - permitted?.markAsDirty(); - - CustomValidators.hideGroupsWhenExcludes([ADRDangerousGood.EXPLOSIVES_TYPE_2], ['other_group'])(permitted as AbstractControl); - - expect(name?.meta.hide).toBe(false); - expect(compat?.meta.hide).toBe(false); - }); - - it('should NOT set hide to TRUE when its value DOES include one of the values passed, but IS part of the group whitelist', () => { - const name = form.get('techRecord_adrDetails_applicantDetails_name') as CustomFormControl; - const permitted = form.get('techRecord_adrDetails_permittedDangerousGoods') as CustomFormControl; - const compat = form.get('techRecord_adrDetails_compatibilityGroupJ') as CustomFormControl; - compat.meta.hide = false; - - permitted?.patchValue([ADRDangerousGood.EXPLOSIVES_TYPE_2]); - permitted?.markAsDirty(); - - CustomValidators.hideGroupsWhenExcludes([ADRDangerousGood.EXPLOSIVES_TYPE_2], ['compat'])(permitted as AbstractControl); - - expect(name?.meta.hide).toBe(false); - expect(compat?.meta.hide).toBe(false); - }); + let form: CustomFormGroup; + beforeEach(() => { + form = new CustomFormGroup( + { + name: 'form-group', + type: FormNodeTypes.GROUP, + children: [ + { + name: 'dangerousGoods', + value: false, + type: FormNodeTypes.CONTROL, + }, + { + name: 'techRecord_adrDetails_compatibilityGroupJ', + type: FormNodeTypes.CONTROL, + hide: true, + groups: ['compat', 'details'], + }, + ], + }, + { + dangerousGoods: new CustomFormControl( + { + name: 'dangerousGoods', + type: FormNodeTypes.CONTROL, + groups: ['adr'], + }, + undefined + ), + techRecord_adrDetails_applicantDetails_name: new CustomFormControl( + { + name: 'techRecord_adrDetails_applicantDetails_name', + type: FormNodeTypes.CONTROL, + hide: false, + groups: ['adr', 'name'], + }, + undefined + ), + techRecord_adrDetails_permittedDangerousGoods: new CustomFormControl( + { + name: 'techRecord_adrDetails_permittedDangerousGoods', + type: FormNodeTypes.CONTROL, + hide: false, + groups: ['adr', 'details'], + validators: [ + { + name: ValidatorNames.ShowGroupsWhenIncludes, + args: { + values: [ADRDangerousGood.EXPLOSIVES_TYPE_2, ADRDangerousGood.EXPLOSIVES_TYPE_3], + groups: ['compat'], + }, + }, + { + name: ValidatorNames.HideGroupsWhenExcludes, + args: { + values: [ADRDangerousGood.EXPLOSIVES_TYPE_2, ADRDangerousGood.EXPLOSIVES_TYPE_3], + groups: ['compat'], + }, + }, + ], + }, + undefined + ), + techRecord_adrDetails_compatibilityGroupJ: new CustomFormControl({ + name: 'techRecord_adrDetails_compatibilityGroupJ', + type: FormNodeTypes.CONTROL, + hide: true, + groups: ['compat', 'details'], + }), + } + ); + }); + it('should set hide to TRUE when its value DOES NOT include one of the values passed, and IS part of the group whitelist', () => { + const name = form.get('techRecord_adrDetails_applicantDetails_name') as CustomFormControl; + const permitted = form.get('techRecord_adrDetails_permittedDangerousGoods') as CustomFormControl; + const compat = form.get('techRecord_adrDetails_compatibilityGroupJ') as CustomFormControl; + compat.meta.hide = false; + + permitted?.patchValue([ADRDangerousGood.CARBON_DISULPHIDE]); + permitted?.markAsDirty(); + + CustomValidators.hideGroupsWhenExcludes( + [ADRDangerousGood.EXPLOSIVES_TYPE_2], + ['compat'] + )(permitted as AbstractControl); + + expect(name?.meta.hide).toBe(false); + expect(compat?.meta.hide).toBe(true); + }); + + it('should NOT set hide to TRUE when its value DOES NOT include one of the values passed, but is NOT part of the group whitelist', () => { + const name = form.get('techRecord_adrDetails_applicantDetails_name') as CustomFormControl; + const permitted = form.get('techRecord_adrDetails_permittedDangerousGoods') as CustomFormControl; + const compat = form.get('techRecord_adrDetails_compatibilityGroupJ') as CustomFormControl; + compat.meta.hide = false; + + permitted?.patchValue([ADRDangerousGood.CARBON_DISULPHIDE]); + permitted?.markAsDirty(); + + CustomValidators.hideGroupsWhenExcludes( + [ADRDangerousGood.EXPLOSIVES_TYPE_2], + ['other_group'] + )(permitted as AbstractControl); + + expect(name?.meta.hide).toBe(false); + expect(compat?.meta.hide).toBe(false); + }); + + it('should NOT set hide to TRUE when its value DOES include one of the values passed, but IS part of the group whitelist', () => { + const name = form.get('techRecord_adrDetails_applicantDetails_name') as CustomFormControl; + const permitted = form.get('techRecord_adrDetails_permittedDangerousGoods') as CustomFormControl; + const compat = form.get('techRecord_adrDetails_compatibilityGroupJ') as CustomFormControl; + compat.meta.hide = false; + + permitted?.patchValue([ADRDangerousGood.EXPLOSIVES_TYPE_2]); + permitted?.markAsDirty(); + + CustomValidators.hideGroupsWhenExcludes( + [ADRDangerousGood.EXPLOSIVES_TYPE_2], + ['compat'] + )(permitted as AbstractControl); + + expect(name?.meta.hide).toBe(false); + expect(compat?.meta.hide).toBe(false); + }); }); describe('isArray', () => { - let form: CustomFormGroup; - beforeEach(() => { - form = new CustomFormGroup( - { - name: 'form-group', - type: FormNodeTypes.GROUP, - children: - [ - { - name: 'techRecord_adrDetails_additionalNotes_number', - type: FormNodeTypes.CONTROL, - value: [], - }, - ], - }, - { - techRecord_adrDetails_additionalNotes_number: new CustomFormControl( - { - name: 'techRecord_adrDetails_additionalNotes_number', - type: FormNodeTypes.CONTROL, - }, - undefined, - ), - }, - ); - }); - - it('should NOT mark additional notes as INVALID when its value IS an ARRAY', () => { - const control = form.get('techRecord_adrDetails_additionalNotes_number') as CustomFormControl; - control.patchValue([]); - - expect(CustomValidators.isArray()(control)).toBeNull(); - }); - - it('should mark additional notes as INVALID when its values is NOT an ARRAY', () => { - const control = form.get('techRecord_adrDetails_additionalNotes_number') as CustomFormControl; - control.patchValue('not an array'); - - expect(CustomValidators.isArray()(control)).toBeTruthy(); - }); - - it('should NOT mark additional notes as INVALID when its value is an ARRAY of type provided in the ofType argument', () => { - const control = form.get('techRecord_adrDetails_additionalNotes_number') as CustomFormControl; - control.patchValue(['1 string', '2 string']); - expect(CustomValidators.isArray({ ofType: 'string' })(control)).toBeNull(); - - control.patchValue([1, 2, 3]); - expect(CustomValidators.isArray({ ofType: 'number' })(control)).toBeNull(); - - // apparently null is object type in JS - control.patchValue([null, null, null]); - expect(CustomValidators.isArray({ ofType: 'object' })(control)).toBeNull(); - - control.patchValue([undefined, undefined, undefined]); - expect(CustomValidators.isArray({ ofType: 'undefined' })(control)).toBeNull(); - }); - - it('should mark additional notes as INVALID when its value is an ARRAY but not of the type provided in the ofType argument', () => { - const control = form.get('techRecord_adrDetails_additionalNotes_number') as CustomFormControl; - control.patchValue(['1 string', '2 string']); - expect(CustomValidators.isArray({ ofType: 'number' })(control)).toBeTruthy(); - - control.patchValue([1, 2, 3]); - expect(CustomValidators.isArray({ ofType: 'undefined' })(control)).toBeTruthy(); - - control.patchValue([null, null, null]); - expect(CustomValidators.isArray({ ofType: 'string' })(control)).toBeTruthy(); - - control.patchValue([undefined, undefined, undefined]); - expect(CustomValidators.isArray({ ofType: 'object' })(control)).toBeTruthy(); - }); - - it('should NOT mark additional notes as INVALID when its value is an ARRAY and ALL of the values at indices in requiredIndices are truthy', () => { - const control = form.get('techRecord_adrDetails_additionalNotes_number') as CustomFormControl; - control.patchValue(['1 string', '2 string']); - - expect(CustomValidators.isArray({ requiredIndices: [1] })(control)).toBeNull(); - }); - - it('should mark additional notes as INVALID when its value is an ARRAY but AT LEAST ONE of its values at requiredIndices is falsy', () => { - const control = form.get('techRecord_adrDetails_additionalNotes_number') as CustomFormControl; - control.patchValue([null, '2 string']); - - expect(CustomValidators.isArray({ requiredIndices: [0] })(control)).toBeTruthy(); - }); + let form: CustomFormGroup; + beforeEach(() => { + form = new CustomFormGroup( + { + name: 'form-group', + type: FormNodeTypes.GROUP, + children: [ + { + name: 'techRecord_adrDetails_additionalNotes_number', + type: FormNodeTypes.CONTROL, + value: [], + }, + ], + }, + { + techRecord_adrDetails_additionalNotes_number: new CustomFormControl( + { + name: 'techRecord_adrDetails_additionalNotes_number', + type: FormNodeTypes.CONTROL, + }, + undefined + ), + } + ); + }); + + it('should NOT mark additional notes as INVALID when its value IS an ARRAY', () => { + const control = form.get('techRecord_adrDetails_additionalNotes_number') as CustomFormControl; + control.patchValue([]); + + expect(CustomValidators.isArray()(control)).toBeNull(); + }); + + it('should mark additional notes as INVALID when its values is NOT an ARRAY', () => { + const control = form.get('techRecord_adrDetails_additionalNotes_number') as CustomFormControl; + control.patchValue('not an array'); + + expect(CustomValidators.isArray()(control)).toBeTruthy(); + }); + + it('should NOT mark additional notes as INVALID when its value is an ARRAY of type provided in the ofType argument', () => { + const control = form.get('techRecord_adrDetails_additionalNotes_number') as CustomFormControl; + control.patchValue(['1 string', '2 string']); + expect(CustomValidators.isArray({ ofType: 'string' })(control)).toBeNull(); + + control.patchValue([1, 2, 3]); + expect(CustomValidators.isArray({ ofType: 'number' })(control)).toBeNull(); + + // apparently null is object type in JS + control.patchValue([null, null, null]); + expect(CustomValidators.isArray({ ofType: 'object' })(control)).toBeNull(); + + control.patchValue([undefined, undefined, undefined]); + expect(CustomValidators.isArray({ ofType: 'undefined' })(control)).toBeNull(); + }); + + it('should mark additional notes as INVALID when its value is an ARRAY but not of the type provided in the ofType argument', () => { + const control = form.get('techRecord_adrDetails_additionalNotes_number') as CustomFormControl; + control.patchValue(['1 string', '2 string']); + expect(CustomValidators.isArray({ ofType: 'number' })(control)).toBeTruthy(); + + control.patchValue([1, 2, 3]); + expect(CustomValidators.isArray({ ofType: 'undefined' })(control)).toBeTruthy(); + + control.patchValue([null, null, null]); + expect(CustomValidators.isArray({ ofType: 'string' })(control)).toBeTruthy(); + + control.patchValue([undefined, undefined, undefined]); + expect(CustomValidators.isArray({ ofType: 'object' })(control)).toBeTruthy(); + }); + + it('should NOT mark additional notes as INVALID when its value is an ARRAY and ALL of the values at indices in requiredIndices are truthy', () => { + const control = form.get('techRecord_adrDetails_additionalNotes_number') as CustomFormControl; + control.patchValue(['1 string', '2 string']); + + expect(CustomValidators.isArray({ requiredIndices: [1] })(control)).toBeNull(); + }); + + it('should mark additional notes as INVALID when its value is an ARRAY but AT LEAST ONE of its values at requiredIndices is falsy', () => { + const control = form.get('techRecord_adrDetails_additionalNotes_number') as CustomFormControl; + control.patchValue([null, '2 string']); + + expect(CustomValidators.isArray({ requiredIndices: [0] })(control)).toBeTruthy(); + }); }); describe('tc3FieldTestValidator', () => { - let form: FormGroup; - - beforeEach(() => { - form = new CustomFormGroup({ - name: 'group', - label: 'Subsequent', - type: FormNodeTypes.GROUP, - children: [ - { - name: 'tc3Type', - type: FormNodeTypes.CONTROL, - value: null, - label: 'TC3: Inspection Type', - }, - { - name: 'tc3PeriodicNumber', - label: 'TC3: Certificate Number', - value: null, - type: FormNodeTypes.CONTROL, - }, - { - name: 'tc3PeriodicExpiryDate', - label: 'TC3: Expiry Date', - type: FormNodeTypes.CONTROL, - value: null, - isoDate: false, - }, - ], - }, { - tc3Type: new CustomFormControl({ - name: 'tc3Type', - type: FormNodeTypes.CONTROL, - }), - tc3PeriodicNumber: new CustomFormControl({ - name: 'tc3PeriodicNumber', - type: FormNodeTypes.CONTROL, - }), - tc3PeriodicExpiryDate: new CustomFormControl({ - name: 'tc3PeriodicExpiryDate', - type: FormNodeTypes.CONTROL, - }), - }); - }); - it('should give an error if all fields passed to the validator are null', () => { - const type = form.get('tc3Type') as CustomFormControl; - - const validator = CustomValidators.tc3TestValidator( - { - inspectionNumber: 1, - }, - )(type as AbstractControl); - - expect(validator).toEqual( - { - tc3TestValidator: - { - message: 'TC3 Subsequent inspection 1 must have at least one populated field', - }, - }, - ); - }); - it('should give an error if fields passed to the validator are undefined', () => { - const type = form.get('tc3Type') as CustomFormControl; - const date = form.get('tc3PeriodicExpiryDate') as CustomFormControl; - const number = form.get('tc3PeriodicNumber') as CustomFormControl; - - type.patchValue(undefined); - date.patchValue(undefined); - number.patchValue(undefined); - - const validator = CustomValidators.tc3TestValidator( - { - inspectionNumber: 1, - }, - )(type as AbstractControl); - - expect(validator).toEqual( - { - tc3TestValidator: - { - message: 'TC3 Subsequent inspection 1 must have at least one populated field', - }, - }, - ); - }); - it('should give an error if fields passed to the validator are empty strings', () => { - const type = form.get('tc3Type') as CustomFormControl; - const date = form.get('tc3PeriodicExpiryDate') as CustomFormControl; - const number = form.get('tc3PeriodicNumber') as CustomFormControl; - - type.patchValue(''); - date.patchValue(''); - number.patchValue(''); - - const validator = CustomValidators.tc3TestValidator( - { - inspectionNumber: 1, - }, - )(type as AbstractControl); - - expect(validator).toEqual( - { - tc3TestValidator: - { - message: 'TC3 Subsequent inspection 1 must have at least one populated field', - }, - }, - ); - }); - it('should give an error if fields to the validator have a variety of empty values', () => { - const type = form.get('tc3Type') as CustomFormControl; - const date = form.get('tc3PeriodicExpiryDate') as CustomFormControl; - const number = form.get('tc3PeriodicNumber') as CustomFormControl; - - type.patchValue(''); - date.patchValue(null); - number.patchValue(undefined); - - const validator = CustomValidators.tc3TestValidator( - { - inspectionNumber: 1, - }, - )(type as AbstractControl); - - expect(validator).toEqual( - { - tc3TestValidator: - { - message: 'TC3 Subsequent inspection 1 must have at least one populated field', - }, - }, - ); - }); - it('should return null if one field passed to the validator has a value', () => { - const type = form.get('tc3Type') as CustomFormControl; - const number = form.get('tc3PeriodicNumber') as CustomFormControl; - - number.patchValue('test'); - - const validator = CustomValidators.tc3TestValidator( - { - inspectionNumber: 1, - }, - )(type as AbstractControl); - - expect(validator).toBeNull(); - }); + let form: FormGroup; + + beforeEach(() => { + form = new CustomFormGroup( + { + name: 'group', + label: 'Subsequent', + type: FormNodeTypes.GROUP, + children: [ + { + name: 'tc3Type', + type: FormNodeTypes.CONTROL, + value: null, + label: 'TC3: Inspection Type', + }, + { + name: 'tc3PeriodicNumber', + label: 'TC3: Certificate Number', + value: null, + type: FormNodeTypes.CONTROL, + }, + { + name: 'tc3PeriodicExpiryDate', + label: 'TC3: Expiry Date', + type: FormNodeTypes.CONTROL, + value: null, + isoDate: false, + }, + ], + }, + { + tc3Type: new CustomFormControl({ + name: 'tc3Type', + type: FormNodeTypes.CONTROL, + }), + tc3PeriodicNumber: new CustomFormControl({ + name: 'tc3PeriodicNumber', + type: FormNodeTypes.CONTROL, + }), + tc3PeriodicExpiryDate: new CustomFormControl({ + name: 'tc3PeriodicExpiryDate', + type: FormNodeTypes.CONTROL, + }), + } + ); + }); + it('should give an error if all fields passed to the validator are null', () => { + const type = form.get('tc3Type') as CustomFormControl; + + const validator = CustomValidators.tc3TestValidator({ + inspectionNumber: 1, + })(type as AbstractControl); + + expect(validator).toEqual({ + tc3TestValidator: { + message: 'TC3 Subsequent inspection 1 must have at least one populated field', + }, + }); + }); + it('should give an error if fields passed to the validator are undefined', () => { + const type = form.get('tc3Type') as CustomFormControl; + const date = form.get('tc3PeriodicExpiryDate') as CustomFormControl; + const number = form.get('tc3PeriodicNumber') as CustomFormControl; + + type.patchValue(undefined); + date.patchValue(undefined); + number.patchValue(undefined); + + const validator = CustomValidators.tc3TestValidator({ + inspectionNumber: 1, + })(type as AbstractControl); + + expect(validator).toEqual({ + tc3TestValidator: { + message: 'TC3 Subsequent inspection 1 must have at least one populated field', + }, + }); + }); + it('should give an error if fields passed to the validator are empty strings', () => { + const type = form.get('tc3Type') as CustomFormControl; + const date = form.get('tc3PeriodicExpiryDate') as CustomFormControl; + const number = form.get('tc3PeriodicNumber') as CustomFormControl; + + type.patchValue(''); + date.patchValue(''); + number.patchValue(''); + + const validator = CustomValidators.tc3TestValidator({ + inspectionNumber: 1, + })(type as AbstractControl); + + expect(validator).toEqual({ + tc3TestValidator: { + message: 'TC3 Subsequent inspection 1 must have at least one populated field', + }, + }); + }); + it('should give an error if fields to the validator have a variety of empty values', () => { + const type = form.get('tc3Type') as CustomFormControl; + const date = form.get('tc3PeriodicExpiryDate') as CustomFormControl; + const number = form.get('tc3PeriodicNumber') as CustomFormControl; + + type.patchValue(''); + date.patchValue(null); + number.patchValue(undefined); + + const validator = CustomValidators.tc3TestValidator({ + inspectionNumber: 1, + })(type as AbstractControl); + + expect(validator).toEqual({ + tc3TestValidator: { + message: 'TC3 Subsequent inspection 1 must have at least one populated field', + }, + }); + }); + it('should return null if one field passed to the validator has a value', () => { + const type = form.get('tc3Type') as CustomFormControl; + const number = form.get('tc3PeriodicNumber') as CustomFormControl; + + number.patchValue('test'); + + const validator = CustomValidators.tc3TestValidator({ + inspectionNumber: 1, + })(type as AbstractControl); + + expect(validator).toBeNull(); + }); }); describe('tc3ParentValidator', () => { - let form: FormGroup; - - beforeEach(() => { - form = new CustomFormGroup({ - name: 'group', - label: 'Subsequent', - type: FormNodeTypes.GROUP, - children: [ - { - name: 'techRecord_adrDetails_tank_tankDetails_tc3Details', - type: FormNodeTypes.CONTROL, - value: null, - }, - ], - }, { - techRecord_adrDetails_tank_tankDetails_tc3Details: new CustomFormControl({ - name: 'techRecord_adrDetails_tank_tankDetails_tc3Details', - type: FormNodeTypes.CONTROL, - }), - }); - }); - it('should give an error if value contains a test with all null values', () => { - const details = form.get('techRecord_adrDetails_tank_tankDetails_tc3Details') as CustomFormControl; - - details.patchValue([{ tc3Type: null, tc3PeriodicNumber: null, tc3PeriodicExpiryDate: null }]); - - const validator = CustomValidators.tc3TestValidator({ inspectionNumber: 0 })(details as AbstractControl); - - expect(validator).toEqual( - { - tc3TestValidator: - { - message: 'TC3 Subsequent inspection 1 must have at least one populated field', - }, - }, - ); - }); - it('should give an error if fields passed to the validator are undefined', () => { - const details = form.get('techRecord_adrDetails_tank_tankDetails_tc3Details') as CustomFormControl; - - details.patchValue([{ tc3Type: undefined, tc3PeriodicNumber: undefined, tc3PeriodicExpiryDate: undefined }]); - - const validator = CustomValidators.tc3TestValidator({ inspectionNumber: 0 })(details as AbstractControl); - - expect(validator).toEqual( - { - tc3TestValidator: - { - message: 'TC3 Subsequent inspection 1 must have at least one populated field', - }, - }, - ); - }); - it('should give an error if fields passed to the validator are empty strings', () => { - const details = form.get('techRecord_adrDetails_tank_tankDetails_tc3Details') as CustomFormControl; - - details.patchValue([{ tc3Type: '', tc3PeriodicNumber: '', tc3PeriodicExpiryDate: '' }]); - - const validator = CustomValidators.tc3TestValidator({ inspectionNumber: 0 })(details as AbstractControl); - - expect(validator).toEqual( - { - tc3TestValidator: - { - message: 'TC3 Subsequent inspection 1 must have at least one populated field', - }, - }, - ); - }); - it('should give an error if fields to the validator have a variety of empty values', () => { - const details = form.get('techRecord_adrDetails_tank_tankDetails_tc3Details') as CustomFormControl; - - details.patchValue([{ tc3Type: null, tc3PeriodicNumber: undefined, tc3PeriodicExpiryDate: '' }]); - - const validator = CustomValidators.tc3TestValidator({ inspectionNumber: 0 })(details as AbstractControl); - - expect(validator).toEqual( - { - tc3TestValidator: - { - message: 'TC3 Subsequent inspection 1 must have at least one populated field', - }, - }, - ); - }); - it('should tell you which test needs to be filled out', () => { - const details = form.get('techRecord_adrDetails_tank_tankDetails_tc3Details') as CustomFormControl; - - details.patchValue([ - { tc3Type: null, tc3PeriodicNumber: 'test', tc3PeriodicExpiryDate: '' }, - { tc3Type: null, tc3PeriodicNumber: undefined, tc3PeriodicExpiryDate: '' }, - { tc3Type: null, tc3PeriodicNumber: 'test', tc3PeriodicExpiryDate: '' }, - ]); - - const validator = CustomValidators.tc3TestValidator({ inspectionNumber: 0 })(details as AbstractControl); - - expect(validator).toEqual( - { - tc3TestValidator: - { - message: 'TC3 Subsequent inspection 2 must have at least one populated field', - }, - }, - ); - }); - it('should tell you which test needs to be filled out if there are multiple', () => { - const details = form.get('techRecord_adrDetails_tank_tankDetails_tc3Details') as CustomFormControl; - - details.patchValue([ - { tc3Type: null, tc3PeriodicNumber: 'test', tc3PeriodicExpiryDate: '' }, - { tc3Type: null, tc3PeriodicNumber: undefined, tc3PeriodicExpiryDate: '' }, - { tc3Type: null, tc3PeriodicNumber: 'test', tc3PeriodicExpiryDate: '' }, - { tc3Type: null, tc3PeriodicNumber: '', tc3PeriodicExpiryDate: '' }, - { tc3Type: null, tc3PeriodicNumber: '', tc3PeriodicExpiryDate: '' }, - ]); - - const validator = CustomValidators.tc3TestValidator({ inspectionNumber: 0 })(details as AbstractControl); - - expect(validator).toEqual( - { - tc3TestValidator: - { - message: 'TC3 Subsequent inspection 2, 4, 5 must have at least one populated field', - }, - }, - ); - }); - it('should return null if one field passed to the validator has a value', () => { - const details = form.get('techRecord_adrDetails_tank_tankDetails_tc3Details') as CustomFormControl; - - details.patchValue([{ tc3Type: 'test', tc3PeriodicNumber: null, tc3PeriodicExpiryDate: null }]); - const validator = CustomValidators.tc3TestValidator({ inspectionNumber: 0 })(details as AbstractControl); - - expect(validator).toBeNull(); - }); - - describe('dateIsValid', () => { - it.each([ - [{ dateIsInvalid: { message: '\'Date\' day must be a number' } }, null], - [{ dateIsInvalid: { message: '\'Date\' day must be a number' } }, undefined], - [{ dateIsInvalid: { message: '\'Date\' day must be a number' } }, ''], - [{ dateIsInvalid: { message: '\'Date\' day must be a number' } }, '---'], - [{ dateIsInvalid: { message: '\'Date\' day must be a number' } }, '2000--'], - [{ dateIsInvalid: { message: '\'Date\' day must be a number' } }, '2000-12-'], - [{ dateIsInvalid: { message: '\'Date\' day must be a number' } }, '2000-12-A'], - [null, '2000-12-01'], - [null, '2000-12-11'], - [null, '2000-12-31'], - [{ dateIsInvalid: { message: '\'Date\' month must be a number' } }, '2000--11'], - [{ dateIsInvalid: { message: '\'Date\' month must be a number' } }, '2000-C-11'], - [{ dateIsInvalid: { message: '\'Date\' month must be between 1 and 12' } }, '2000-31-11'], - [{ dateIsInvalid: { message: '\'Date\' month must be between 1 and 12' } }, '2000-00-11'], - [{ dateIsInvalid: { message: '\'Date\' year must be a number' } }, '-12-11'], - [{ dateIsInvalid: { message: '\'Date\' year must be a number' } }, 'C-12-11'], - - [null, '2020-02-29'], - [{ dateIsInvalid: { message: '\'Date\' day must be between 1 and 28 in the month of February' } }, '2019-02-29'], - ])('should return %p when control value is %s', (expected: object | null, input) => { - expect(CustomValidators.dateIsInvalid(new FormControl(input))).toEqual(expected); - }); - }); + let form: FormGroup; + + beforeEach(() => { + form = new CustomFormGroup( + { + name: 'group', + label: 'Subsequent', + type: FormNodeTypes.GROUP, + children: [ + { + name: 'techRecord_adrDetails_tank_tankDetails_tc3Details', + type: FormNodeTypes.CONTROL, + value: null, + }, + ], + }, + { + techRecord_adrDetails_tank_tankDetails_tc3Details: new CustomFormControl({ + name: 'techRecord_adrDetails_tank_tankDetails_tc3Details', + type: FormNodeTypes.CONTROL, + }), + } + ); + }); + it('should give an error if value contains a test with all null values', () => { + const details = form.get('techRecord_adrDetails_tank_tankDetails_tc3Details') as CustomFormControl; + + details.patchValue([{ tc3Type: null, tc3PeriodicNumber: null, tc3PeriodicExpiryDate: null }]); + + const validator = CustomValidators.tc3TestValidator({ inspectionNumber: 0 })(details as AbstractControl); + + expect(validator).toEqual({ + tc3TestValidator: { + message: 'TC3 Subsequent inspection 1 must have at least one populated field', + }, + }); + }); + it('should give an error if fields passed to the validator are undefined', () => { + const details = form.get('techRecord_adrDetails_tank_tankDetails_tc3Details') as CustomFormControl; + + details.patchValue([{ tc3Type: undefined, tc3PeriodicNumber: undefined, tc3PeriodicExpiryDate: undefined }]); + + const validator = CustomValidators.tc3TestValidator({ inspectionNumber: 0 })(details as AbstractControl); + + expect(validator).toEqual({ + tc3TestValidator: { + message: 'TC3 Subsequent inspection 1 must have at least one populated field', + }, + }); + }); + it('should give an error if fields passed to the validator are empty strings', () => { + const details = form.get('techRecord_adrDetails_tank_tankDetails_tc3Details') as CustomFormControl; + + details.patchValue([{ tc3Type: '', tc3PeriodicNumber: '', tc3PeriodicExpiryDate: '' }]); + + const validator = CustomValidators.tc3TestValidator({ inspectionNumber: 0 })(details as AbstractControl); + + expect(validator).toEqual({ + tc3TestValidator: { + message: 'TC3 Subsequent inspection 1 must have at least one populated field', + }, + }); + }); + it('should give an error if fields to the validator have a variety of empty values', () => { + const details = form.get('techRecord_adrDetails_tank_tankDetails_tc3Details') as CustomFormControl; + + details.patchValue([{ tc3Type: null, tc3PeriodicNumber: undefined, tc3PeriodicExpiryDate: '' }]); + + const validator = CustomValidators.tc3TestValidator({ inspectionNumber: 0 })(details as AbstractControl); + + expect(validator).toEqual({ + tc3TestValidator: { + message: 'TC3 Subsequent inspection 1 must have at least one populated field', + }, + }); + }); + it('should tell you which test needs to be filled out', () => { + const details = form.get('techRecord_adrDetails_tank_tankDetails_tc3Details') as CustomFormControl; + + details.patchValue([ + { tc3Type: null, tc3PeriodicNumber: 'test', tc3PeriodicExpiryDate: '' }, + { tc3Type: null, tc3PeriodicNumber: undefined, tc3PeriodicExpiryDate: '' }, + { tc3Type: null, tc3PeriodicNumber: 'test', tc3PeriodicExpiryDate: '' }, + ]); + + const validator = CustomValidators.tc3TestValidator({ inspectionNumber: 0 })(details as AbstractControl); + + expect(validator).toEqual({ + tc3TestValidator: { + message: 'TC3 Subsequent inspection 2 must have at least one populated field', + }, + }); + }); + it('should tell you which test needs to be filled out if there are multiple', () => { + const details = form.get('techRecord_adrDetails_tank_tankDetails_tc3Details') as CustomFormControl; + + details.patchValue([ + { tc3Type: null, tc3PeriodicNumber: 'test', tc3PeriodicExpiryDate: '' }, + { tc3Type: null, tc3PeriodicNumber: undefined, tc3PeriodicExpiryDate: '' }, + { tc3Type: null, tc3PeriodicNumber: 'test', tc3PeriodicExpiryDate: '' }, + { tc3Type: null, tc3PeriodicNumber: '', tc3PeriodicExpiryDate: '' }, + { tc3Type: null, tc3PeriodicNumber: '', tc3PeriodicExpiryDate: '' }, + ]); + + const validator = CustomValidators.tc3TestValidator({ inspectionNumber: 0 })(details as AbstractControl); + + expect(validator).toEqual({ + tc3TestValidator: { + message: 'TC3 Subsequent inspection 2, 4, 5 must have at least one populated field', + }, + }); + }); + it('should return null if one field passed to the validator has a value', () => { + const details = form.get('techRecord_adrDetails_tank_tankDetails_tc3Details') as CustomFormControl; + + details.patchValue([{ tc3Type: 'test', tc3PeriodicNumber: null, tc3PeriodicExpiryDate: null }]); + const validator = CustomValidators.tc3TestValidator({ inspectionNumber: 0 })(details as AbstractControl); + + expect(validator).toBeNull(); + }); + + describe('dateIsValid', () => { + it.each([ + [{ dateIsInvalid: { message: "'Date' day must be a number" } }, null], + [{ dateIsInvalid: { message: "'Date' day must be a number" } }, undefined], + [{ dateIsInvalid: { message: "'Date' day must be a number" } }, ''], + [{ dateIsInvalid: { message: "'Date' day must be a number" } }, '---'], + [{ dateIsInvalid: { message: "'Date' day must be a number" } }, '2000--'], + [{ dateIsInvalid: { message: "'Date' day must be a number" } }, '2000-12-'], + [{ dateIsInvalid: { message: "'Date' day must be a number" } }, '2000-12-A'], + [null, '2000-12-01'], + [null, '2000-12-11'], + [null, '2000-12-31'], + [{ dateIsInvalid: { message: "'Date' month must be a number" } }, '2000--11'], + [{ dateIsInvalid: { message: "'Date' month must be a number" } }, '2000-C-11'], + [{ dateIsInvalid: { message: "'Date' month must be between 1 and 12" } }, '2000-31-11'], + [{ dateIsInvalid: { message: "'Date' month must be between 1 and 12" } }, '2000-00-11'], + [{ dateIsInvalid: { message: "'Date' year must be a number" } }, '-12-11'], + [{ dateIsInvalid: { message: "'Date' year must be a number" } }, 'C-12-11'], + + [null, '2020-02-29'], + [{ dateIsInvalid: { message: "'Date' day must be between 1 and 28 in the month of February" } }, '2019-02-29'], + ])('should return %p when control value is %s', (expected: object | null, input) => { + expect(CustomValidators.dateIsInvalid(new FormControl(input))).toEqual(expected); + }); + }); }); describe('minArrayLengthIfNotEmpty', () => { - it('should return null if the form array is empty', () => { - const formArray = new FormArray([]); - expect(CustomValidators.minArrayLengthIfNotEmpty(2, 'Error message')(formArray)).toBeNull(); - }); - - it('should return an error if the array is not empty but doesnt reach minimum length', () => { - const formArray = new FormArray([ - new FormControl({ - axleNumber: 1, - tyres_tyreSize: null, - tyres_fitmentCode: null, - tyres_dataTrAxles: null, - tyres_plyRating: null, - tyres_tyreCode: null, - weights_gbWeight: null, - weights_designWeight: null, - parkingBrakeMrk: false, - weights_eecWeight: null, - }), - ]); - - expect(CustomValidators.minArrayLengthIfNotEmpty(2, 'Error message')(formArray)).toStrictEqual(expect.objectContaining({ - minArrayLengthIfNotEmpty: { message: 'Error message' }, - })); - }); - - it('should return null if the minimum length is reached', () => { - const formArray = new FormArray([ - new FormControl({ - axleNumber: 1, - tyres_tyreSize: null, - tyres_fitmentCode: null, - tyres_dataTrAxles: null, - tyres_plyRating: null, - tyres_tyreCode: null, - weights_gbWeight: null, - weights_designWeight: null, - parkingBrakeMrk: false, - weights_eecWeight: null, - }), - new FormControl({ - axleNumber: 2, - tyres_tyreSize: null, - tyres_fitmentCode: null, - tyres_dataTrAxles: null, - tyres_plyRating: null, - tyres_tyreCode: null, - weights_gbWeight: null, - weights_designWeight: null, - parkingBrakeMrk: false, - weights_eecWeight: null, - }), - ]); - - expect(CustomValidators.minArrayLengthIfNotEmpty(2, 'Error message')(formArray)).toBeNull(); - }); + it('should return null if the form array is empty', () => { + const formArray = new FormArray([]); + expect(CustomValidators.minArrayLengthIfNotEmpty(2, 'Error message')(formArray)).toBeNull(); + }); + + it('should return an error if the array is not empty but doesnt reach minimum length', () => { + const formArray = new FormArray([ + new FormControl({ + axleNumber: 1, + tyres_tyreSize: null, + tyres_fitmentCode: null, + tyres_dataTrAxles: null, + tyres_plyRating: null, + tyres_tyreCode: null, + weights_gbWeight: null, + weights_designWeight: null, + parkingBrakeMrk: false, + weights_eecWeight: null, + }), + ]); + + expect(CustomValidators.minArrayLengthIfNotEmpty(2, 'Error message')(formArray)).toStrictEqual( + expect.objectContaining({ + minArrayLengthIfNotEmpty: { message: 'Error message' }, + }) + ); + }); + + it('should return null if the minimum length is reached', () => { + const formArray = new FormArray([ + new FormControl({ + axleNumber: 1, + tyres_tyreSize: null, + tyres_fitmentCode: null, + tyres_dataTrAxles: null, + tyres_plyRating: null, + tyres_tyreCode: null, + weights_gbWeight: null, + weights_designWeight: null, + parkingBrakeMrk: false, + weights_eecWeight: null, + }), + new FormControl({ + axleNumber: 2, + tyres_tyreSize: null, + tyres_fitmentCode: null, + tyres_dataTrAxles: null, + tyres_plyRating: null, + tyres_tyreCode: null, + weights_gbWeight: null, + weights_designWeight: null, + parkingBrakeMrk: false, + weights_eecWeight: null, + }), + ]); + + expect(CustomValidators.minArrayLengthIfNotEmpty(2, 'Error message')(formArray)).toBeNull(); + }); +}); + +describe('IssueRequired', () => { + let form: FormGroup; + + beforeEach(() => { + form = new FormGroup({ + testResult: new CustomFormControl({ type: FormNodeTypes.CONTROL, name: 'testResult' }, 'pass'), + certificateNumber: new CustomFormControl({ type: FormNodeTypes.CONTROL, name: 'certificateNumber' }, null), + centralDocs: new CustomFormGroup( + { + name: 'centralDocs', + type: FormNodeTypes.GROUP, + children: [{ type: FormNodeTypes.CONTROL, name: 'issueRequired' }], + }, + { + issueRequired: new CustomFormControl({ type: FormNodeTypes.CONTROL, name: 'issueRequired' }, true), + } + ), + }); + }); + + describe('when issueRequired is true', () => { + beforeEach(() => { + form.get(['centralDocs', 'issueRequired'])?.patchValue(true); + }); + + it('should return null when testResult is prs', () => { + form.get('testResult')?.patchValue('prs'); + const control = form.get('certificateNumber') as FormControl; + expect(CustomValidators.issueRequired()(control)).toBeNull(); + }); + + it('should return null when testResult is pass', () => { + form.get('testResult')?.patchValue('pass'); + const control = form.get('certificateNumber') as FormControl; + expect(CustomValidators.issueRequired()(control)).toBeNull(); + }); + }); + + describe('when issueRequired is false', () => { + beforeEach(() => { + form.get(['centralDocs', 'issueRequired'])?.patchValue(false); + }); + it('should return null when testResult is pass, but the control is populated', () => { + form.get('testResult')?.patchValue('pass'); + const control = form.get('certificateNumber') as FormControl; + control.patchValue('value'); + expect(CustomValidators.issueRequired()(control)).toBeNull(); + }); + + it('should return error when testResult is pass, but the control is not populated', () => { + form.get('testResult')?.patchValue('pass'); + const control = form.get('certificateNumber') as FormControl; + control.patchValue(null); + expect(CustomValidators.issueRequired()(control)).toEqual({ + requiredIfEquals: { customErrorMessage: undefined, sibling: undefined }, + }); + }); + }); }); diff --git a/src/app/forms/validators/custom-validators.ts b/src/app/forms/validators/custom-validators.ts index b7155e6821..3a69388b7e 100644 --- a/src/app/forms/validators/custom-validators.ts +++ b/src/app/forms/validators/custom-validators.ts @@ -1,6 +1,4 @@ -import { - AbstractControl, ValidationErrors, ValidatorFn, -} from '@angular/forms'; +import { AbstractControl, ValidationErrors, ValidatorFn } from '@angular/forms'; import { VehicleClassDescription } from '@dvsa/cvs-type-definitions/types/v3/tech-record/enums/vehicleClassDescription.enum.js'; // eslint-disable-next-line import/no-cycle import { CustomFormControl, CustomFormGroup } from '@forms/services/dynamic-form.types'; @@ -8,603 +6,654 @@ import { VehicleSizes, VehicleTypes } from '@models/vehicle-tech-record.model'; import validateDate from 'validate-govuk-date'; export class CustomValidators { - static hideIfEmpty = (sibling: string): ValidatorFn => { - return (control: AbstractControl): ValidationErrors | null => { - if (control?.parent) { - const siblingControl = control.parent.get(sibling) as CustomFormControl; - siblingControl.meta.hide = !control.value; - } - - return null; - }; - }; - - static hideIfEquals = (sibling: string, value: unknown): ValidatorFn => { - return (control: AbstractControl): ValidationErrors | null => { - if (control?.parent) { - const siblingControl = control.parent.get(sibling) as CustomFormControl; - - siblingControl.meta.hide = Array.isArray(value) && control.value ? value.includes(control.value) : control.value === value; - } - - return null; - }; - }; - - static hideIfNotEqual = (sibling: string, value: unknown): ValidatorFn => { - return (control: AbstractControl): ValidationErrors | null => { - if (control?.parent) { - const siblingControl = control.parent.get(sibling) as CustomFormControl; - - siblingControl.meta.hide = Array.isArray(value) ? !value.includes(control.value) : control.value !== value; - } - - return null; - }; - }; - - static enableIfEquals = (sibling: string, value: unknown): ValidatorFn => { - return (control: AbstractControl): ValidationErrors | null => { - if (control?.parent) { - const siblingControl = control.parent.get(sibling) as CustomFormControl; - const isEqual = Array.isArray(value) ? value.includes(control.value) : control.value === value; - if (isEqual) { - siblingControl.enable(); - } else { - siblingControl.disable(); - } - } - return null; - }; - }; - - static disableIfEquals = (sibling: string, value: unknown): ValidatorFn => { - return (control: AbstractControl): ValidationErrors | null => { - if (control?.parent) { - const siblingControl = control.parent.get(sibling) as CustomFormControl; - const isEqual = Array.isArray(value) ? value.includes(control.value) : control.value === value; - if (isEqual) { - siblingControl.disable(); - } else { - siblingControl.enable(); - } - } - return null; - }; - }; - - static hideIfParentSiblingNotEqual = (parentSibling: string, value: unknown): ValidatorFn => { - return (control: AbstractControl): ValidationErrors | null => { - if (control?.parent && control.parent.parent) { - const siblingControl = control.parent.parent.get(parentSibling) as CustomFormControl; - - siblingControl.meta.hide = Array.isArray(value) ? !value.includes(control.value) : control.value !== value; - } - - return null; - }; - }; - - static hideIfParentSiblingEquals = (parentSibling: string, value: unknown): ValidatorFn => { - return (control: AbstractControl): ValidationErrors | null => { - if (control?.parent && control.parent.parent) { - const siblingControl = control.parent.parent.get(parentSibling) as CustomFormControl; - siblingControl.meta.hide = Array.isArray(value) && control.value ? value.includes(control.value) : control.value === value; - } - - return null; - }; - }; - - static requiredIfNotHidden = (): ValidatorFn => - (control: AbstractControl): ValidationErrors | null => { - const customControl = control as CustomFormControl; - if (!control?.parent) return null; - if (customControl.meta.hide === false && !control.value) { - // If meta.hide is false and control value is empty, return a validation error - return { requiredIfNotHidden: customControl.meta.label }; - } - return null; - }; - - static requiredIfEquals = (sibling: string, values: unknown[]): ValidatorFn => - (control: AbstractControl): ValidationErrors | null => { - if (!control?.parent) return null; - - const siblingControl = control.parent.get(sibling) as CustomFormControl; - const siblingValue = siblingControl.value; - - const isSiblingVisible = !siblingControl.meta.hide; - - const isSiblingValueIncluded = Array.isArray(siblingValue) - ? values.some((value) => siblingValue.includes(value)) - : values.includes(siblingValue); - - const isControlValueEmpty = control.value === null - || control.value === undefined - || control.value === '' - || (Array.isArray(control.value) && (control.value.length === 0 || control.value.every((val) => !val))); - - return isSiblingValueIncluded && isControlValueEmpty && isSiblingVisible - ? { requiredIfEquals: { sibling: siblingControl.meta.label } } - : null; - }; - - static requiredIfAllEquals = (sibling: string, values: unknown[]): ValidatorFn => - (control: AbstractControl): ValidationErrors | null => { - if (!control?.parent) return null; - - const siblingControl = control.parent.get(sibling) as CustomFormControl; - const siblingValue = siblingControl.value; - - const isSiblingVisible = !siblingControl.meta.hide; - - const isSiblingValueIncluded = Array.isArray(siblingValue) - ? siblingValue.every((val) => values.includes(val)) - : values.includes(siblingValue); - - const isControlValueEmpty = control.value === null - || control.value === undefined - || control.value === '' - || (Array.isArray(control.value) && (control.value.length === 0 || control.value.every((val) => !val))); - - return isSiblingValueIncluded && isControlValueEmpty && isSiblingVisible - ? { requiredIfEquals: { sibling: siblingControl.meta.label } } - : null; - }; - - static requiredIfNotEquals = (sibling: string, value: unknown): ValidatorFn => { - return (control: AbstractControl): ValidationErrors | null => { - if (control?.parent) { - const siblingControl = control.parent.get(sibling) as CustomFormControl; - const siblingValue = siblingControl.value; - const newValue = Array.isArray(value) ? value.includes(siblingValue) : siblingValue === value; - - if (!newValue && (control.value === null || control.value === undefined || control.value === '')) { - return { requiredIfNotEquals: { sibling: siblingControl.meta.label } }; - } - } - - return null; - }; - }; - - static mustEqualSibling = (sibling: string): ValidatorFn => { - return (control: AbstractControl): ValidationErrors | null => { - if (control?.parent) { - const siblingControl = control.parent.get(sibling) as CustomFormControl; - const siblingValue = siblingControl.value; - const isEqual = Array.isArray(control.value) ? control.value.includes(siblingValue) : siblingValue === control.value; - - if (!isEqual) { - return { mustEqualSibling: { sibling: siblingControl.meta.label } }; - } - } - - return null; - }; - }; - - static validateVRMTrailerIdLength = (sibling: string): ValidatorFn => { - return (control: AbstractControl): ValidationErrors | null => { - if (!control.value) { - return null; - } - - if (control?.parent) { - const siblingControl = control.parent.get(sibling) as CustomFormControl; - const siblingValue = siblingControl.value; - - const isTrailerValueSelected = siblingValue === VehicleTypes.TRL; - - if (isTrailerValueSelected) { - if (control.value.length < 7) { - return { validateVRMTrailerIdLength: { message: 'Trailer ID must be greater than or equal to 7 characters' } }; - } - if (control.value.length > 8) { - return { validateVRMTrailerIdLength: { message: 'Trailer ID must be less than or equal to 8 characters' } }; - } - } else { - if (control.value.length < 1) { - return { validateVRMTrailerIdLength: { message: 'VRM must be greater than or equal to 1 character' } }; - } - if (control.value.length > 9) { - return { validateVRMTrailerIdLength: { message: 'VRM must be less than or equal to 9 characters' } }; - } - } - } - - return null; - }; - }; - - static defined = (): ValidatorFn => { - return (control: AbstractControl): ValidationErrors | null => { - if (typeof control.value === 'undefined') { - return { defined: false }; - } - return null; - }; - }; - - static validateVinCharacters() { - return this.customPattern(['^(?!.*[OIQ]).*$', 'should not contain O, I or Q']); - } - static alphanumeric(): ValidatorFn { - return this.customPattern(['^[a-zA-Z0-9]*$', 'must be alphanumeric']); - } - - static numeric(): ValidatorFn { - return this.customPattern(['^\\d*$', 'must be a whole number']); - } - - static email(): ValidatorFn { - return this.customPattern(['^[\\w\\-\\.\\+]+@([\\w-]+\\.)+[\\w-]{2,}$', 'Enter an email address in the correct format, like name@example.com']); - } - - static customPattern([regEx, message]: string[]): ValidatorFn { - return (control: AbstractControl): ValidationErrors | null => { - if (!control.value) { - return null; - } - - // eslint-disable-next-line security/detect-non-literal-regexp - const valid = new RegExp(regEx).test(control.value); - - return valid ? null : { customPattern: { message } }; - }; - } - - static invalidOption: ValidatorFn = (control: AbstractControl): ValidationErrors | null => { - return control.value === '[INVALID_OPTION]' ? { invalidOption: true } : null; - }; - - static dateIsInvalid: ValidatorFn = (control: AbstractControl): ValidationErrors | null => { - if (control instanceof CustomFormControl && control.meta.hide) return null; - const [yyyy, mm, dd] = (control.value ?? '').split('-'); - const label = control instanceof CustomFormControl ? control.meta.label : undefined; - const checks = validateDate(parseInt(dd ?? '', 10), parseInt(mm ?? '', 10), parseInt(yyyy ?? '', 10), label); - return checks && checks.error ? { dateIsInvalid: { message: checks.errors?.[0]?.reason } } : null; - }; - - static pastDate: ValidatorFn = (control: AbstractControl): ValidationErrors | null => { - const now = new Date(); - const date = control.value; - - if (date && new Date(date).getTime() > now.getTime()) { - return { pastDate: true }; - } - return null; - }; - - static futureDate: ValidatorFn = (control: AbstractControl): ValidationErrors | null => { - const now = new Date(); - const date = control.value; - if (date && new Date(date).getTime() < now.getTime()) { - return { futureDate: true }; - } - return null; - }; - - static pastYear: ValidatorFn = (control: AbstractControl): ValidationErrors | null => { - const currentYear = new Date().getFullYear(); - const inputYear = control.value; - if (inputYear && inputYear > currentYear) { - return { pastYear: true }; - } - return null; - }; - - static aheadOfDate = (sibling: string): ValidatorFn => { - return (control: AbstractControl): ValidationErrors | null => { - const siblingControl = control?.parent?.get(sibling); - if (siblingControl?.value && control.value && new Date(control.value) < new Date(siblingControl.value)) { - return { aheadOfDate: { sibling: (siblingControl as CustomFormControl).meta.label, date: new Date(siblingControl.value) } }; - } - - return null; - }; - }; - - static dateNotExceed = (sibling: string, months: number): ValidatorFn => { - return (control: AbstractControl): ValidationErrors | null => { - const siblingControl = control?.parent?.get(sibling); - if (siblingControl?.value && control.value) { - const maxDate = new Date(siblingControl.value); - maxDate.setMonth(maxDate.getMonth() + months); - - if (new Date(control.value) > maxDate) { - return { dateNotExceed: { sibling: (siblingControl as CustomFormControl).meta.label, months } }; - } - } - - return null; - }; - }; - - /** - * Validator that copies control value to control of given name at the top-level ancestor of control. - * @param rootControlName - control in top-level ancestor of this control - * @returns null - */ - static copyValueToRootControl = (rootControlName: string): ValidatorFn => { - return (control: AbstractControl): ValidationErrors | null => { - const rootControl = control.root.get(rootControlName); - if (rootControl) { - rootControl.setValue(control.value, { onlySelf: true, emitEvent: false }); - } - return null; - }; - }; - - static notZNumber = (control: AbstractControl): ValidationErrors | null => { - if (!control.value) return null; - - const isZNumber = /^[0-9]{7}[zZ]$/.test(control.value); - - return !isZNumber ? null : { notZNumber: true }; - }; - - static handlePsvPassengersChange = (passengersOne: string, passengersTwo: string): ValidatorFn => { - return (control: AbstractControl): ValidationErrors | null => { - if (control.dirty) { - const controlOne: number = control.root.get(passengersOne)?.value; - const controlTwo: number = control.root.get(passengersTwo)?.value; - const controlThree: number = control.value; - - const classControl = control.root.get('techRecord_vehicleClass_description'); - const sizeControl = control.root.get('techRecord_vehicleSize'); - - const totalPassengers = controlOne + controlTwo + controlThree; - - switch (true) { - case totalPassengers <= 22: { - sizeControl?.setValue(VehicleSizes.SMALL, { emitEvent: false }); - classControl?.setValue(VehicleClassDescription.SmallPsvIeLessThanOrEqualTo22Seats, { emitEvent: false }); - break; - } - default: { - sizeControl?.setValue(VehicleSizes.LARGE, { emitEvent: false }); - classControl?.setValue(VehicleClassDescription.LargePsvIeGreaterThan23Seats, { emitEvent: false }); - } - } - control.markAsPristine(); - } - return null; - }; - }; - - static isMemberOfEnum = (checkEnum: Record, options: Partial = {}): ValidatorFn => { - options = { allowFalsy: false, ...options }; - - return (control: AbstractControl): ValidationErrors | null => { - if (options.allowFalsy && !control.value) return null; - return Object.values(checkEnum).includes(control.value) ? null : { enum: true }; - }; - }; - - static updateFunctionCode = (): ValidatorFn => { - return (control: AbstractControl): ValidationErrors | null => { - const vehicleFunctionCode = control.root.get('techRecord_functionCode'); - const functionCodes: Record = { - rigid: 'R', - articulated: 'A', - 'semi-trailer': 'A', - }; - - if (control.dirty) { - vehicleFunctionCode?.setValue(functionCodes[control?.value], { emitEvent: false }); - control.markAsPristine(); - } - return null; - }; - }; - - static modifyControlsByGroup = (control: AbstractControl, groups: string[], modifyFunc: (control: CustomFormControl) => void): void => { - if ((control as CustomFormControl).meta.hide) return; - - const parentGroup = control.parent as CustomFormGroup; - parentGroup.meta.children?.forEach((child) => { - const childControl = parentGroup.get(child.name) as CustomFormControl; - const childGroups = childControl?.meta.groups; - childGroups?.forEach((group) => { - if (groups.includes(group)) { - modifyFunc(childControl); - } - }); - }); - }; - - static setHidePropertyForGroups = (control: AbstractControl, groups: string[], hide: boolean): void => { - this.modifyControlsByGroup(control, groups, (childControl) => { - childControl.meta.hide = hide; - }); - }; - - static showGroupsWhenEqualTo = (values: unknown[] | undefined, groups: string[]): ValidatorFn => { - return (control: AbstractControl): ValidationErrors | null => { - if (values && !values.includes(control.value)) return null; - this.setHidePropertyForGroups(control, groups, false); - - return null; - }; - }; - - static showGroupsWhenIncludes = (values: unknown[] | undefined, groups: string[]): ValidatorFn => { - return (control: AbstractControl): ValidationErrors | null => { - if (values && !values.some((value) => control.value?.includes(value))) return null; - this.setHidePropertyForGroups(control, groups, false); - return null; - }; - }; - - static hideGroupsWhenIncludes = (values: unknown[] | undefined, groups: string[]): ValidatorFn => { - return (control: AbstractControl): ValidationErrors | null => { - if (values && values.some((value) => control.value?.includes(value))) { - this.setHidePropertyForGroups(control, groups, true); - } - - return null; - }; - }; - - static showGroupsWhenExcludes = (values: unknown[] | undefined, groups: string[]): ValidatorFn => { - return (control: AbstractControl): ValidationErrors | null => { - if (values && values.some((value) => control.value?.includes(value))) return null; - this.setHidePropertyForGroups(control, groups, false); - - return null; - }; - }; - - static hideGroupsWhenExcludes = (values: unknown[] | undefined, groups: string[]): ValidatorFn => { - return (control: AbstractControl): ValidationErrors | null => { - if (values && values.some((value) => control.value?.includes(value))) return null; - this.setHidePropertyForGroups(control, groups, true); - - return null; - }; - }; - - static hideGroupsWhenEqualTo = (values: unknown[] | undefined, groups: string[]): ValidatorFn => { - return (control: AbstractControl): ValidationErrors | null => { - if (values && !values.includes(control.value)) return null; - this.setHidePropertyForGroups(control, groups, true); - - return null; - }; - }; - - static addWarningForAdrField = (warning: string): ValidatorFn => { - return (control: AbstractControl): ValidationErrors | null => { - if (control instanceof CustomFormControl) { - if (control.value) { - control.meta.warning = undefined; - return null; - } - - if (control.dirty) { - const { parent } = control; - if (parent instanceof CustomFormGroup) { - const touched = Object.values(parent.controls).some((child) => child !== control && child.touched && child.value); - if (touched) { - control.meta.warning = warning; - } - } - } - } - - return null; - }; - }; - - static isArray = (options: Partial = {}) => { - return (control: AbstractControl): ValidationErrors | null => { - // Only perform subsequent logic if this condition is met, e.g. sibling control has value true - if (options.whenEquals) { - const { sibling, value } = options.whenEquals; - const siblingControl = control.parent?.get(sibling); - const siblingValue = siblingControl?.value; - const isSiblingValueIncluded = Array.isArray(siblingValue) - ? value.some((v) => siblingValue.includes(v)) - : value.includes(siblingValue); - - if (!isSiblingValueIncluded) return null; - } - - if (!Array.isArray(control.value)) return { isArray: 'must be a non-empty array' }; - - if (options.ofType) { - const index = control.value.findIndex((val) => typeof val !== options.ofType); - return index === -1 - ? null - : { isArray: { message: `${index + 1} must be of type ${options.ofType}` } }; - } - - if (options.requiredIndices) { - const index = control.value.findIndex((val, i) => options.requiredIndices?.includes(i) && !val); - return index === -1 - ? null - : { isArray: { message: `${index + 1} is required` } }; - } - - return null; - }; - }; - - static custom = (func: (...args: unknown[]) => ValidationErrors | null, ...args: unknown[]) => { - return (control: AbstractControl): ValidationErrors | null => func(control, ...args); - }; - - static tc3TestValidator = (args: { inspectionNumber: number }) => { - return (control: AbstractControl): ValidationErrors | null => { - if (!control?.parent) return null; - let areControlsEmpty: boolean[] = []; - let inspection = ''; - // the inspection numbers for individual tests start form 1 so this checks if its an individual test or the control that contains the component - if (args.inspectionNumber !== 0) { - const tc3Type = control.parent?.get('tc3Type')?.value; - const tc3PeriodicNumber = control.parent?.get('tc3PeriodicNumber')?.value; - const tc3PeriodicExpiryDate = control.parent?.get('tc3PeriodicExpiryDate')?.value; - // areTc3FieldsEmpty takes an array of tc3 test values and checks that at least one of the fields is filled out for each test - areControlsEmpty = areTc3FieldsEmpty([{ tc3Type, tc3PeriodicExpiryDate, tc3PeriodicNumber }]); - inspection = args.inspectionNumber as unknown as string; - return areControlsEmpty.includes(true) - ? { tc3TestValidator: { message: `TC3 Subsequent inspection ${inspection} must have at least one populated field` } } - : null; - } - // this statement is the same logic but applied to the control that holds all of the tests. - // This allows the error to be displayed in the Global error service - if (!control.value) return null; - areControlsEmpty = areTc3FieldsEmpty(control.value); - areControlsEmpty.forEach((value, index) => { - if (value) { - if (inspection.length === 0) { - inspection = `${index + 1}`; - } else { - inspection += `, ${index + 1}`; - } - } - }); - return areControlsEmpty.includes(true) - ? { tc3TestValidator: { message: `TC3 Subsequent inspection ${inspection} must have at least one populated field` } } - : null; - }; - }; - - static minArrayLengthIfNotEmpty = (minimumLength: number, message: string): ValidatorFn => { - return (control: AbstractControl): ValidationErrors | null => { - if (control.value?.length && control.value.length < minimumLength) { - return { minArrayLengthIfNotEmpty: { message } }; - } - return null; - }; - }; + static hideIfEmpty = (sibling: string): ValidatorFn => { + return (control: AbstractControl): ValidationErrors | null => { + if (control?.parent) { + const siblingControl = control.parent.get(sibling) as CustomFormControl; + siblingControl.meta.hide = !control.value; + } + + return null; + }; + }; + + static hideIfEquals = (sibling: string, value: unknown): ValidatorFn => { + return (control: AbstractControl): ValidationErrors | null => { + if (control?.parent) { + const siblingControl = control.parent.get(sibling) as CustomFormControl; + siblingControl.meta.hide = + Array.isArray(value) && control.value ? value.includes(control.value) : control.value === value; + } + + return null; + }; + }; + + static hideIfNotEqual = (sibling: string, value: unknown): ValidatorFn => { + return (control: AbstractControl): ValidationErrors | null => { + if (control?.parent) { + const siblingControl = control.parent.get(sibling) as CustomFormControl; + siblingControl.meta.hide = Array.isArray(value) ? !value.includes(control.value) : control.value !== value; + } + + return null; + }; + }; + + static enableIfEquals = (sibling: string, value: unknown): ValidatorFn => { + return (control: AbstractControl): ValidationErrors | null => { + if (control?.parent) { + const siblingControl = control.parent.get(sibling) as CustomFormControl; + const isEqual = Array.isArray(value) ? value.includes(control.value) : control.value === value; + if (isEqual) { + siblingControl.enable(); + } else { + siblingControl.disable(); + } + } + return null; + }; + }; + + static disableIfEquals = (sibling: string, value: unknown): ValidatorFn => { + return (control: AbstractControl): ValidationErrors | null => { + if (control?.parent) { + const siblingControl = control.parent.get(sibling) as CustomFormControl; + const isEqual = Array.isArray(value) ? value.includes(control.value) : control.value === value; + if (isEqual) { + siblingControl.disable(); + } else { + siblingControl.enable(); + } + } + return null; + }; + }; + + static hideIfParentSiblingNotEqual = (parentSibling: string, value: unknown): ValidatorFn => { + return (control: AbstractControl): ValidationErrors | null => { + if (control?.parent && control.parent.parent) { + const siblingControl = control.parent.parent.get(parentSibling) as CustomFormControl; + siblingControl.meta.hide = Array.isArray(value) ? !value.includes(control.value) : control.value !== value; + } + + return null; + }; + }; + + static hideIfParentSiblingEquals = (parentSibling: string, value: unknown): ValidatorFn => { + return (control: AbstractControl): ValidationErrors | null => { + if (control && control.parent && control.parent.parent) { + if ((control.parent as CustomFormControl | CustomFormGroup).meta?.hide) return null; + if ((control.parent.parent as CustomFormControl | CustomFormGroup).meta?.hide) return null; + const siblingControl = control.parent.parent.get(parentSibling) as CustomFormControl; + siblingControl.meta.hide = + Array.isArray(value) && control.value ? value.includes(control.value) : control.value === value; + } + + return null; + }; + }; + + static requiredIfNotHidden = + (): ValidatorFn => + (control: AbstractControl): ValidationErrors | null => { + const customControl = control as CustomFormControl; + if (!control?.parent) return null; + if (customControl.meta.hide === false && !control.value) { + // If meta.hide is false and control value is empty, return a validation error + return { requiredIfNotHidden: customControl.meta.label }; + } + return null; + }; + + static requiredIfEquals = + (sibling: string, values: unknown[], customErrorMessage?: string): ValidatorFn => + (control: AbstractControl): ValidationErrors | null => { + if (!control?.parent) return null; + + const siblingControl = control.parent.get(sibling) as CustomFormControl; + const siblingValue = siblingControl.value; + + const isSiblingVisible = !siblingControl.meta.hide; + + const isSiblingValueIncluded = Array.isArray(siblingValue) + ? values.some((value) => siblingValue.includes(value)) + : values.includes(siblingValue); + + const isControlValueEmpty = + control.value === null || + control.value === undefined || + control.value === '' || + (Array.isArray(control.value) && (control.value.length === 0 || control.value.every((val) => !val))); + + return isSiblingValueIncluded && isControlValueEmpty && isSiblingVisible + ? { requiredIfEquals: { sibling: siblingControl.meta.label, customErrorMessage } } + : null; + }; + + static requiredIfAllEquals = + (sibling: string, values: unknown[]): ValidatorFn => + (control: AbstractControl): ValidationErrors | null => { + if (!control?.parent) return null; + + const siblingControl = control.parent.get(sibling) as CustomFormControl; + const siblingValue = siblingControl.value; + + const isSiblingVisible = !siblingControl.meta.hide; + + const isSiblingValueIncluded = Array.isArray(siblingValue) + ? siblingValue.every((val) => values.includes(val)) + : values.includes(siblingValue); + + const isControlValueEmpty = + control.value === null || + control.value === undefined || + control.value === '' || + (Array.isArray(control.value) && (control.value.length === 0 || control.value.every((val) => !val))); + + return isSiblingValueIncluded && isControlValueEmpty && isSiblingVisible + ? { requiredIfEquals: { sibling: siblingControl.meta.label } } + : null; + }; + + static requiredIfNotEquals = (sibling: string, value: unknown): ValidatorFn => { + return (control: AbstractControl): ValidationErrors | null => { + if (control?.parent) { + const siblingControl = control.parent.get(sibling) as CustomFormControl; + const siblingValue = siblingControl.value; + const newValue = Array.isArray(value) ? value.includes(siblingValue) : siblingValue === value; + + if (!newValue && (control.value === null || control.value === undefined || control.value === '')) { + return { requiredIfNotEquals: { sibling: siblingControl.meta.label } }; + } + } + + return null; + }; + }; + + static mustEqualSibling = (sibling: string): ValidatorFn => { + return (control: AbstractControl): ValidationErrors | null => { + if (control?.parent) { + const siblingControl = control.parent.get(sibling) as CustomFormControl; + const siblingValue = siblingControl.value; + const isEqual = Array.isArray(control.value) + ? control.value.includes(siblingValue) + : siblingValue === control.value; + + if (!isEqual) { + return { mustEqualSibling: { sibling: siblingControl.meta.label } }; + } + } + + return null; + }; + }; + + static validateVRMTrailerIdLength = (sibling: string): ValidatorFn => { + return (control: AbstractControl): ValidationErrors | null => { + if (!control.value) { + return null; + } + + if (control?.parent) { + const siblingControl = control.parent.get(sibling) as CustomFormControl; + const siblingValue = siblingControl.value; + + const isTrailerValueSelected = siblingValue === VehicleTypes.TRL; + + if (isTrailerValueSelected) { + if (control.value.length < 7) { + return { + validateVRMTrailerIdLength: { message: 'Trailer ID must be greater than or equal to 7 characters' }, + }; + } + if (control.value.length > 8) { + return { validateVRMTrailerIdLength: { message: 'Trailer ID must be less than or equal to 8 characters' } }; + } + } else { + if (control.value.length < 1) { + return { validateVRMTrailerIdLength: { message: 'VRM must be greater than or equal to 1 character' } }; + } + if (control.value.length > 9) { + return { validateVRMTrailerIdLength: { message: 'VRM must be less than or equal to 9 characters' } }; + } + } + } + + return null; + }; + }; + + static defined = (): ValidatorFn => { + return (control: AbstractControl): ValidationErrors | null => { + if (typeof control.value === 'undefined') { + return { defined: false }; + } + return null; + }; + }; + + static validateVinCharacters() { + return this.customPattern(['^(?!.*[OIQ]).*$', 'should not contain O, I or Q']); + } + static alphanumeric(): ValidatorFn { + return this.customPattern(['^[a-zA-Z0-9]*$', 'must be alphanumeric']); + } + + static numeric(): ValidatorFn { + return this.customPattern(['^\\d*$', 'must be a whole number']); + } + + static email(): ValidatorFn { + return this.customPattern([ + '^[\\w\\-\\.\\+]+@([\\w-]+\\.)+[\\w-]{2,}$', + 'Enter an email address in the correct format, like name@example.com', + ]); + } + + static customPattern([regEx, message]: string[]): ValidatorFn { + return (control: AbstractControl): ValidationErrors | null => { + if (!control.value) { + return null; + } + + // eslint-disable-next-line security/detect-non-literal-regexp + const valid = new RegExp(regEx).test(control.value); + + return valid ? null : { customPattern: { message } }; + }; + } + + static invalidOption: ValidatorFn = (control: AbstractControl): ValidationErrors | null => { + return control.value === '[INVALID_OPTION]' ? { invalidOption: true } : null; + }; + + static dateIsInvalid: ValidatorFn = (control: AbstractControl): ValidationErrors | null => { + if (control instanceof CustomFormControl && control.meta.hide) return null; + const [yyyy, mm, dd] = (control.value ?? '').split('-'); + const label = control instanceof CustomFormControl ? control.meta.label : undefined; + const checks = validateDate( + Number.parseInt(dd ?? '', 10), + Number.parseInt(mm ?? '', 10), + Number.parseInt(yyyy ?? '', 10), + label + ); + return checks && checks.error ? { dateIsInvalid: { message: checks.errors?.[0]?.reason } } : null; + }; + + static pastDate: ValidatorFn = (control: AbstractControl): ValidationErrors | null => { + const now = new Date(); + const date = control.value; + + if (date && new Date(date).getTime() > now.getTime()) { + return { pastDate: true }; + } + return null; + }; + + static futureDate: ValidatorFn = (control: AbstractControl): ValidationErrors | null => { + const now = new Date(); + const date = control.value; + if (date && new Date(date).getTime() < now.getTime()) { + return { futureDate: true }; + } + return null; + }; + + static pastYear: ValidatorFn = (control: AbstractControl): ValidationErrors | null => { + const currentYear = new Date().getFullYear(); + const inputYear = control.value; + if (inputYear && inputYear > currentYear) { + return { pastYear: true }; + } + return null; + }; + + static aheadOfDate = (sibling: string): ValidatorFn => { + return (control: AbstractControl): ValidationErrors | null => { + const siblingControl = control?.parent?.get(sibling); + if (siblingControl?.value && control.value && new Date(control.value) < new Date(siblingControl.value)) { + return { + aheadOfDate: { + sibling: (siblingControl as CustomFormControl).meta.label, + date: new Date(siblingControl.value), + }, + }; + } + + return null; + }; + }; + + static dateNotExceed = (sibling: string, months: number): ValidatorFn => { + return (control: AbstractControl): ValidationErrors | null => { + const siblingControl = control?.parent?.get(sibling); + if (siblingControl?.value && control.value) { + const maxDate = new Date(siblingControl.value); + maxDate.setMonth(maxDate.getMonth() + months); + + if (new Date(control.value) > maxDate) { + return { dateNotExceed: { sibling: (siblingControl as CustomFormControl).meta.label, months } }; + } + } + + return null; + }; + }; + + /** + * Validator that copies control value to control of given name at the top-level ancestor of control. + * @param rootControlName - control in top-level ancestor of this control + * @returns null + */ + static copyValueToRootControl = (rootControlName: string): ValidatorFn => { + return (control: AbstractControl): ValidationErrors | null => { + const rootControl = control.root.get(rootControlName); + if (rootControl) { + rootControl.setValue(control.value, { onlySelf: true, emitEvent: false }); + } + return null; + }; + }; + + static notZNumber = (control: AbstractControl): ValidationErrors | null => { + if (!control.value) return null; + + const isZNumber = /^[0-9]{7}[zZ]$/.test(control.value); + + return !isZNumber ? null : { notZNumber: true }; + }; + + static handlePsvPassengersChange = (passengersOne: string, passengersTwo: string): ValidatorFn => { + return (control: AbstractControl): ValidationErrors | null => { + if (control.dirty) { + const controlOne: number = control.root.get(passengersOne)?.value; + const controlTwo: number = control.root.get(passengersTwo)?.value; + const controlThree: number = control.value; + + const classControl = control.root.get('techRecord_vehicleClass_description'); + const sizeControl = control.root.get('techRecord_vehicleSize'); + + const totalPassengers = controlOne + controlTwo + controlThree; + + switch (true) { + case totalPassengers <= 22: { + sizeControl?.setValue(VehicleSizes.SMALL, { emitEvent: false }); + classControl?.setValue(VehicleClassDescription.SmallPsvIeLessThanOrEqualTo22Seats, { emitEvent: false }); + break; + } + default: { + sizeControl?.setValue(VehicleSizes.LARGE, { emitEvent: false }); + classControl?.setValue(VehicleClassDescription.LargePsvIeGreaterThan23Seats, { emitEvent: false }); + } + } + control.markAsPristine(); + } + return null; + }; + }; + + static isMemberOfEnum = ( + checkEnum: Record, + options: Partial = {} + ): ValidatorFn => { + options = { allowFalsy: false, ...options }; + + return (control: AbstractControl): ValidationErrors | null => { + if (options.allowFalsy && !control.value) return null; + return Object.values(checkEnum).includes(control.value) ? null : { enum: true }; + }; + }; + + static updateFunctionCode = (): ValidatorFn => { + return (control: AbstractControl): ValidationErrors | null => { + const vehicleFunctionCode = control.root.get('techRecord_functionCode'); + const functionCodes: Record = { + rigid: 'R', + articulated: 'A', + 'semi-trailer': 'A', + }; + + if (control.dirty) { + vehicleFunctionCode?.setValue(functionCodes[control?.value], { emitEvent: false }); + control.markAsPristine(); + } + return null; + }; + }; + + static modifyControlsByGroup = ( + control: AbstractControl, + groups: string[], + modifyFunc: (control: CustomFormControl) => void + ): void => { + if ((control as CustomFormControl).meta.hide) return; + + const parentGroup = control.parent as CustomFormGroup; + parentGroup.meta.children?.forEach((child) => { + const childControl = parentGroup.get(child.name) as CustomFormControl; + const childGroups = childControl?.meta.groups; + childGroups?.forEach((group) => { + if (groups.includes(group)) { + modifyFunc(childControl); + } + }); + }); + }; + + static setHidePropertyForGroups = (control: AbstractControl, groups: string[], hide: boolean): void => { + this.modifyControlsByGroup(control, groups, (childControl) => { + childControl.meta.hide = hide; + }); + }; + + static showGroupsWhenEqualTo = (values: unknown[] | undefined, groups: string[]): ValidatorFn => { + return (control: AbstractControl): ValidationErrors | null => { + if (values && !values.includes(control.value)) return null; + this.setHidePropertyForGroups(control, groups, false); + + return null; + }; + }; + + static showGroupsWhenIncludes = (values: unknown[] | undefined, groups: string[]): ValidatorFn => { + return (control: AbstractControl): ValidationErrors | null => { + if (values && !values.some((value) => control.value?.includes(value))) return null; + this.setHidePropertyForGroups(control, groups, false); + return null; + }; + }; + + static hideGroupsWhenIncludes = (values: unknown[] | undefined, groups: string[]): ValidatorFn => { + return (control: AbstractControl): ValidationErrors | null => { + if (values && values.some((value) => control.value?.includes(value))) { + this.setHidePropertyForGroups(control, groups, true); + } + + return null; + }; + }; + + static showGroupsWhenExcludes = (values: unknown[] | undefined, groups: string[]): ValidatorFn => { + return (control: AbstractControl): ValidationErrors | null => { + if (values && values.some((value) => control.value?.includes(value))) return null; + this.setHidePropertyForGroups(control, groups, false); + + return null; + }; + }; + + static hideGroupsWhenExcludes = (values: unknown[] | undefined, groups: string[]): ValidatorFn => { + return (control: AbstractControl): ValidationErrors | null => { + if (values && values.some((value) => control.value?.includes(value))) return null; + this.setHidePropertyForGroups(control, groups, true); + + return null; + }; + }; + + static hideGroupsWhenEqualTo = (values: unknown[] | undefined, groups: string[]): ValidatorFn => { + return (control: AbstractControl): ValidationErrors | null => { + if (values && !values.includes(control.value)) return null; + this.setHidePropertyForGroups(control, groups, true); + + return null; + }; + }; + + static addWarningForAdrField = (warning: string): ValidatorFn => { + return (control: AbstractControl): ValidationErrors | null => { + if (control instanceof CustomFormControl) { + if (control.value) { + control.meta.warning = undefined; + return null; + } + + if (control.dirty) { + const { parent } = control; + if (parent instanceof CustomFormGroup) { + const touched = Object.values(parent.controls).some( + (child) => child !== control && child.touched && child.value + ); + if (touched) { + control.meta.warning = warning; + } + } + } + } + + return null; + }; + }; + + static isArray = (options: Partial = {}) => { + return (control: AbstractControl): ValidationErrors | null => { + // Only perform subsequent logic if this condition is met, e.g. sibling control has value true + if (options.whenEquals) { + const { sibling, value } = options.whenEquals; + const siblingControl = control.parent?.get(sibling); + const siblingValue = siblingControl?.value; + const isSiblingValueIncluded = Array.isArray(siblingValue) + ? value.some((v) => siblingValue.includes(v)) + : value.includes(siblingValue); + + if (!isSiblingValueIncluded) return null; + } + + if (!Array.isArray(control.value)) return { isArray: 'must be a non-empty array' }; + + if (options.ofType) { + const index = control.value.findIndex((val) => typeof val !== options.ofType); + return index === -1 ? null : { isArray: { message: `${index + 1} must be of type ${options.ofType}` } }; + } + + if (options.requiredIndices) { + const index = control.value.findIndex((val, i) => options.requiredIndices?.includes(i) && !val); + return index === -1 ? null : { isArray: { message: `${index + 1} is required` } }; + } + + return null; + }; + }; + + static custom = (func: (...args: unknown[]) => ValidationErrors | null, ...args: unknown[]) => { + return (control: AbstractControl): ValidationErrors | null => func(control, ...args); + }; + + static tc3TestValidator = (args: { inspectionNumber: number }) => { + return (control: AbstractControl): ValidationErrors | null => { + if (!control?.parent) return null; + let areControlsEmpty: boolean[] = []; + let inspection = ''; + // the inspection numbers for individual tests start form 1 so this checks if its an individual test or the control that contains the component + if (args.inspectionNumber !== 0) { + const tc3Type = control.parent?.get('tc3Type')?.value; + const tc3PeriodicNumber = control.parent?.get('tc3PeriodicNumber')?.value; + const tc3PeriodicExpiryDate = control.parent?.get('tc3PeriodicExpiryDate')?.value; + // areTc3FieldsEmpty takes an array of tc3 test values and checks that at least one of the fields is filled out for each test + areControlsEmpty = areTc3FieldsEmpty([{ tc3Type, tc3PeriodicExpiryDate, tc3PeriodicNumber }]); + inspection = args.inspectionNumber as unknown as string; + return areControlsEmpty.includes(true) + ? { + tc3TestValidator: { + message: `TC3 Subsequent inspection ${inspection} must have at least one populated field`, + }, + } + : null; + } + // this statement is the same logic but applied to the control that holds all of the tests. + // This allows the error to be displayed in the Global error service + if (!control.value) return null; + areControlsEmpty = areTc3FieldsEmpty(control.value); + areControlsEmpty.forEach((value, index) => { + if (value) { + if (inspection.length === 0) { + inspection = `${index + 1}`; + } else { + inspection += `, ${index + 1}`; + } + } + }); + return areControlsEmpty.includes(true) + ? { + tc3TestValidator: { + message: `TC3 Subsequent inspection ${inspection} must have at least one populated field`, + }, + } + : null; + }; + }; + + static minArrayLengthIfNotEmpty = (minimumLength: number, message: string): ValidatorFn => { + return (control: AbstractControl): ValidationErrors | null => { + if (control.value?.length && control.value.length < minimumLength) { + return { minArrayLengthIfNotEmpty: { message } }; + } + return null; + }; + }; + + static issueRequired = (): ValidatorFn => { + return (control: AbstractControl): ValidationErrors | null => { + const isPRS = control.parent?.value.testResult === 'prs'; + const isPass = control.parent?.value.testResult === 'pass'; + const issueRequired = control.parent?.value.centralDocs?.issueRequired; + if ((isPRS || isPass) && issueRequired) { + return null; + } + + return CustomValidators.requiredIfEquals('testResult', ['pass'])(control); + }; + }; } export type EnumValidatorOptions = { - allowFalsy: boolean; + allowFalsy: boolean; }; export type IsArrayValidatorOptions = { - ofType: string; - requiredIndices: number[]; - whenEquals: { sibling: string, value: unknown[] } + ofType: string; + requiredIndices: number[]; + whenEquals: { sibling: string; value: unknown[] }; }; -const areTc3FieldsEmpty = (values: { tc3Type: string, tc3PeriodicNumber: string, tc3PeriodicExpiryDate: string }[]) => { - const isValueEmpty: boolean[] = []; - - values.forEach((value) => { - if ( - (value.tc3Type === null || value.tc3Type === undefined || value.tc3Type === '') - && (value.tc3PeriodicNumber === null || value.tc3PeriodicNumber === undefined || value.tc3PeriodicNumber === '') - && (value.tc3PeriodicExpiryDate === null || value.tc3PeriodicExpiryDate === undefined || value.tc3PeriodicExpiryDate === '') - ) { - isValueEmpty.push(true); - } else { - isValueEmpty.push(false); - } - }); - return isValueEmpty; +const areTc3FieldsEmpty = (values: { tc3Type: string; tc3PeriodicNumber: string; tc3PeriodicExpiryDate: string }[]) => { + const isValueEmpty: boolean[] = []; + + values.forEach((value) => { + if ( + (value.tc3Type === null || value.tc3Type === undefined || value.tc3Type === '') && + (value.tc3PeriodicNumber === null || value.tc3PeriodicNumber === undefined || value.tc3PeriodicNumber === '') && + (value.tc3PeriodicExpiryDate === null || + value.tc3PeriodicExpiryDate === undefined || + value.tc3PeriodicExpiryDate === '') + ) { + isValueEmpty.push(true); + } else { + isValueEmpty.push(false); + } + }); + return isValueEmpty; }; diff --git a/src/app/forms/validators/date/date.validators.spec.ts b/src/app/forms/validators/date/date.validators.spec.ts index c15b30f561..605f118c1b 100644 --- a/src/app/forms/validators/date/date.validators.spec.ts +++ b/src/app/forms/validators/date/date.validators.spec.ts @@ -2,75 +2,83 @@ import { FormControl } from '@angular/forms'; import { DateValidators } from './date.validators'; describe('validDate', () => { - it.each([ - [ - { - invalidDate: { - error: true, - index: 0, - reason: '\'Date\' day must be between 1 and 31 in the month of January', - }, - }, - '2000-01-00', - ], - [ - { - invalidDate: { - error: true, - index: 1, - reason: '\'Date\' month must be between 1 and 12', - }, - }, - '2000-00-01', - ], - [ - { - invalidDate: { - error: true, - index: 0, - reason: '\'Date\' must include a day', - }, - }, - '20-01-', - ], - [ - { - invalidDate: { - error: true, - index: 1, - reason: '\'Date\' must include a month', - }, - }, - '20--01', - ], - [ - { - invalidDate: { - error: true, - index: 2, - reason: '\'Date\' must include a year', - }, - }, - '-01-00', - ], - [ - { - invalidDate: { - error: true, - reason: '\'Date\' year must be four digits', - }, - }, - '20-01-01', - ], - [{ invalidDate: { error: true, reason: '\'Date\' must include time' } }, '2022-01-01T:00:00:000Z', true], - [{ invalidDate: { error: true, reason: '\'Date\' hours must be between 0 and 23' } }, '2022-01-01T24:00:00:000Z', true], - [{ invalidDate: { error: true, reason: '\'Date\' must include time' } }, '2022-01-01T00::00:000Z', true], - [{ invalidDate: { error: true, reason: '\'Date\' minutes must be between 0 and 59' } }, '2022-01-01T00:60:00:000Z', true], - [null, ''], - [null, '2022-01-01T00:00:00.000Z'], - ])('should validate date and return %s for %p', (expected, date: string, displayTime = false) => { - const control = new FormControl(date); + it.each([ + [ + { + invalidDate: { + error: true, + index: 0, + reason: "'Date' day must be between 1 and 31 in the month of January", + }, + }, + '2000-01-00', + ], + [ + { + invalidDate: { + error: true, + index: 1, + reason: "'Date' month must be between 1 and 12", + }, + }, + '2000-00-01', + ], + [ + { + invalidDate: { + error: true, + index: 0, + reason: "'Date' must include a day", + }, + }, + '20-01-', + ], + [ + { + invalidDate: { + error: true, + index: 1, + reason: "'Date' must include a month", + }, + }, + '20--01', + ], + [ + { + invalidDate: { + error: true, + index: 2, + reason: "'Date' must include a year", + }, + }, + '-01-00', + ], + [ + { + invalidDate: { + error: true, + reason: "'Date' year must be four digits", + }, + }, + '20-01-01', + ], + [{ invalidDate: { error: true, reason: "'Date' must include time" } }, '2022-01-01T:00:00:000Z', true], + [ + { invalidDate: { error: true, reason: "'Date' hours must be between 0 and 23" } }, + '2022-01-01T24:00:00:000Z', + true, + ], + [{ invalidDate: { error: true, reason: "'Date' must include time" } }, '2022-01-01T00::00:000Z', true], + [ + { invalidDate: { error: true, reason: "'Date' minutes must be between 0 and 59" } }, + '2022-01-01T00:60:00:000Z', + true, + ], + [null, ''], + [null, '2022-01-01T00:00:00.000Z'], + ])('should validate date and return %s for %p', (expected, date: string, displayTime = false) => { + const control = new FormControl(date); - expect(DateValidators.validDate(displayTime)(control)).toEqual(expected); - }); + expect(DateValidators.validDate(displayTime)(control)).toEqual(expected); + }); }); diff --git a/src/app/forms/validators/date/date.validators.ts b/src/app/forms/validators/date/date.validators.ts index 23dafbc4dc..591d60f018 100644 --- a/src/app/forms/validators/date/date.validators.ts +++ b/src/app/forms/validators/date/date.validators.ts @@ -2,45 +2,45 @@ import { AbstractControl, ValidationErrors, ValidatorFn } from '@angular/forms'; import validateDate from 'validate-govuk-date'; export class DateValidators { - static validDate(displayTime = false, label?: string): ValidatorFn { - return (control: AbstractControl): ValidationErrors | null => { - if (!control.value) { - return null; - } - - const [d, t] = (control.value as string).split('T'); - const [year, month, day] = d.split('-'); - const { error, errors } = validateDate(day || '', month || '', year || '', label); - - if (error && errors?.length) { - return { invalidDate: errors[0] }; - } - - if (year.length !== 4) { - return { invalidDate: { error: true, reason: `'${label || 'Date'}' year must be four digits` } }; - } - - if (displayTime) { - return this.validateTime(t, label); - } - - return null; - }; - } - - private static validateTime(time: string, label: string | undefined) { - const [hours, minutes] = time.split(':'); - - if (!hours || !minutes) { - return { invalidDate: { error: true, reason: `'${label || 'Date'}' must include time` } }; - } - if (Number.parseInt(hours, 10) > 23) { - return { invalidDate: { error: true, reason: `'${label || 'Date'}' hours must be between 0 and 23` } }; - } - if (Number.parseInt(minutes, 10) > 59) { - return { invalidDate: { error: true, reason: `'${label || 'Date'}' minutes must be between 0 and 59` } }; - } - - return null; - } + static validDate(displayTime = false, label?: string): ValidatorFn { + return (control: AbstractControl): ValidationErrors | null => { + if (!control.value) { + return null; + } + + const [d, t] = (control.value as string).split('T'); + const [year, month, day] = d.split('-'); + const { error, errors } = validateDate(day || '', month || '', year || '', label); + + if (error && errors?.length) { + return { invalidDate: errors[0] }; + } + + if (year.length !== 4) { + return { invalidDate: { error: true, reason: `'${label || 'Date'}' year must be four digits` } }; + } + + if (displayTime) { + return this.validateTime(t, label); + } + + return null; + }; + } + + private static validateTime(time: string, label: string | undefined) { + const [hours, minutes] = time.split(':'); + + if (!hours || !minutes) { + return { invalidDate: { error: true, reason: `'${label || 'Date'}' must include time` } }; + } + if (Number.parseInt(hours, 10) > 23) { + return { invalidDate: { error: true, reason: `'${label || 'Date'}' hours must be between 0 and 23` } }; + } + if (Number.parseInt(minutes, 10) > 59) { + return { invalidDate: { error: true, reason: `'${label || 'Date'}' minutes must be between 0 and 59` } }; + } + + return null; + } } diff --git a/src/app/forms/validators/defects/defect.validators.spec.ts b/src/app/forms/validators/defects/defect.validators.spec.ts index 17c2c65d32..b5ec5e6cc0 100644 --- a/src/app/forms/validators/defects/defect.validators.spec.ts +++ b/src/app/forms/validators/defects/defect.validators.spec.ts @@ -1,150 +1,152 @@ -import { FormGroup, AbstractControl } from '@angular/forms'; -import { CustomFormGroup, FormNodeTypes, CustomFormControl } from '@forms/services/dynamic-form.types'; +import { AbstractControl, FormGroup } from '@angular/forms'; +import { CustomFormControl, CustomFormGroup, FormNodeTypes } from '@forms/services/dynamic-form.types'; import { deficiencyCategory } from '@models/defects/deficiency-category.enum'; import { DefectValidators } from './defect.validators'; describe('parent sibling validators', () => { - let form: FormGroup; - - beforeEach(() => { - form = new FormGroup({ - parent: new CustomFormGroup( - { name: 'parent', type: FormNodeTypes.GROUP }, - { notes: new CustomFormControl({ name: 'notes', type: FormNodeTypes.CONTROL }) }, - ), - prohibitionIssued: new CustomFormControl({ name: 'prohibitionIssued', type: FormNodeTypes.CONTROL }), - deficiencyCategory: new CustomFormControl({ name: 'deficiencyCategory', type: FormNodeTypes.CONTROL }), - stdForProhibition: new CustomFormControl({ name: 'stdForProhibition', type: FormNodeTypes.CONTROL }), - }); - }); - - it('should return null', () => { - expect(DefectValidators.validateDefectNotes(form.controls['foo'])).toBeNull(); - }); - - it('should return error if deficiency category is dangerous* and prohibition issued is no and value of control is not defined', () => { - const notes = form.get(['parent', 'notes']); - const prohibitionIssued = form.get('prohibitionIssued'); - const defCategory = form.get('deficiencyCategory'); - const stdForProhibition = form.get('stdForProhibition'); - - prohibitionIssued?.patchValue(false); - defCategory?.patchValue(deficiencyCategory.Dangerous); - stdForProhibition?.patchValue(true); - - expect(DefectValidators.validateDefectNotes(notes as AbstractControl)).toEqual({ validateDefectNotes: true }); - }); - - it('should return null if deficiency category is dangerous* and prohibition issued is yes and value of control is not defined', () => { - const notes = form.get(['parent', 'notes']); - const prohibitionIssued = form.get('prohibitionIssued'); - const defCategory = form.get('deficiencyCategory'); - const stdForProhibition = form.get('stdForProhibition'); - - prohibitionIssued?.patchValue(true); - defCategory?.patchValue(deficiencyCategory.Dangerous); - stdForProhibition?.patchValue(true); - - expect(DefectValidators.validateDefectNotes(notes as AbstractControl)).toBeNull(); - }); - - it('should return null if deficiency category is not dangerous* and prohibition issued is yes and value of control is not defined', () => { - const notes = form.get(['parent', 'notes']); - const prohibitionIssued = form.get('prohibitionIssued'); - const defCategory = form.get('deficiencyCategory'); - - prohibitionIssued?.patchValue(true); - defCategory?.patchValue('foo'); - - expect(DefectValidators.validateDefectNotes(notes as AbstractControl)).toBeNull(); - }); - - it('should return null if deficiency category is not dangerous* and prohibition issued is no and value of control is not defined', () => { - const notes = form.get(['parent', 'notes']); - const prohibitionIssued = form.get('prohibitionIssued'); - const defCategory = form.get('deficiencyCategory'); - - prohibitionIssued?.patchValue(false); - defCategory?.patchValue('foo'); - - expect(DefectValidators.validateDefectNotes(notes as AbstractControl)).toBeNull(); - }); - - it('should return null if deficiency category is dangerous* and prohibition issued is no and value of control is defined', () => { - const notes = form.get(['parent', 'notes']); - const prohibitionIssued = form.get('prohibitionIssued'); - const defCategory = form.get('deficiencyCategory'); - const stdForProhibition = form.get('stdForProhibition'); - - notes?.patchValue('foo'); - prohibitionIssued?.patchValue(false); - defCategory?.patchValue(deficiencyCategory.Dangerous); - stdForProhibition?.patchValue(true); - - expect(DefectValidators.validateDefectNotes(notes as AbstractControl)).toBeNull(); - }); + let form: FormGroup; + + beforeEach(() => { + form = new FormGroup({ + parent: new CustomFormGroup( + { name: 'parent', type: FormNodeTypes.GROUP }, + { notes: new CustomFormControl({ name: 'notes', type: FormNodeTypes.CONTROL }) } + ), + prohibitionIssued: new CustomFormControl({ name: 'prohibitionIssued', type: FormNodeTypes.CONTROL }), + deficiencyCategory: new CustomFormControl({ name: 'deficiencyCategory', type: FormNodeTypes.CONTROL }), + stdForProhibition: new CustomFormControl({ name: 'stdForProhibition', type: FormNodeTypes.CONTROL }), + }); + }); + + it('should return null', () => { + expect(DefectValidators.validateDefectNotes(form.controls['foo'])).toBeNull(); + }); + + it('should return error if deficiency category is dangerous* and prohibition issued is no and value of control is not defined', () => { + const notes = form.get(['parent', 'notes']); + const prohibitionIssued = form.get('prohibitionIssued'); + const defCategory = form.get('deficiencyCategory'); + const stdForProhibition = form.get('stdForProhibition'); + + prohibitionIssued?.patchValue(false); + defCategory?.patchValue(deficiencyCategory.Dangerous); + stdForProhibition?.patchValue(true); + + expect(DefectValidators.validateDefectNotes(notes as AbstractControl)).toEqual({ validateDefectNotes: true }); + }); + + it('should return null if deficiency category is dangerous* and prohibition issued is yes and value of control is not defined', () => { + const notes = form.get(['parent', 'notes']); + const prohibitionIssued = form.get('prohibitionIssued'); + const defCategory = form.get('deficiencyCategory'); + const stdForProhibition = form.get('stdForProhibition'); + + prohibitionIssued?.patchValue(true); + defCategory?.patchValue(deficiencyCategory.Dangerous); + stdForProhibition?.patchValue(true); + + expect(DefectValidators.validateDefectNotes(notes as AbstractControl)).toBeNull(); + }); + + it('should return null if deficiency category is not dangerous* and prohibition issued is yes and value of control is not defined', () => { + const notes = form.get(['parent', 'notes']); + const prohibitionIssued = form.get('prohibitionIssued'); + const defCategory = form.get('deficiencyCategory'); + + prohibitionIssued?.patchValue(true); + defCategory?.patchValue('foo'); + + expect(DefectValidators.validateDefectNotes(notes as AbstractControl)).toBeNull(); + }); + + it('should return null if deficiency category is not dangerous* and prohibition issued is no and value of control is not defined', () => { + const notes = form.get(['parent', 'notes']); + const prohibitionIssued = form.get('prohibitionIssued'); + const defCategory = form.get('deficiencyCategory'); + + prohibitionIssued?.patchValue(false); + defCategory?.patchValue('foo'); + + expect(DefectValidators.validateDefectNotes(notes as AbstractControl)).toBeNull(); + }); + + it('should return null if deficiency category is dangerous* and prohibition issued is no and value of control is defined', () => { + const notes = form.get(['parent', 'notes']); + const prohibitionIssued = form.get('prohibitionIssued'); + const defCategory = form.get('deficiencyCategory'); + const stdForProhibition = form.get('stdForProhibition'); + + notes?.patchValue('foo'); + prohibitionIssued?.patchValue(false); + defCategory?.patchValue(deficiencyCategory.Dangerous); + stdForProhibition?.patchValue(true); + + expect(DefectValidators.validateDefectNotes(notes as AbstractControl)).toBeNull(); + }); }); describe('prohibition issued validator', () => { - let form: FormGroup; - - beforeEach(() => { - form = new FormGroup({ - prohibitionIssued: new CustomFormControl({ name: 'prohibitionIssued', type: FormNodeTypes.CONTROL }), - deficiencyCategory: new CustomFormControl({ name: 'deficiencyCategory', type: FormNodeTypes.CONTROL }), - stdForProhibition: new CustomFormControl({ name: 'stdForProhibition', type: FormNodeTypes.CONTROL }), - }); - }); - - it('should return null', () => { - expect(DefectValidators.validateProhibitionIssued(form.controls['prohibitionIssued'])).toBeNull(); - }); - - it('should return error when dangerous no asterisk and prohibition issued is false', () => { - const defCategory = form.get('deficiencyCategory'); - const prohibitionIssued = form.get('prohibitionIssued'); - const stdForProhibition = form.get('stdForProhibition'); - - defCategory?.patchValue(deficiencyCategory.Dangerous); - prohibitionIssued?.patchValue(false); - stdForProhibition?.patchValue(false); - - expect(DefectValidators.validateProhibitionIssued(form.controls['prohibitionIssued'])).toEqual({ validateProhibitionIssued: true }); - }); - - it('should return null when prohibition issued is true', () => { - const defCategory = form.get('deficiencyCategory'); - const prohibitionIssued = form.get('prohibitionIssued'); - const stdForProhibition = form.get('stdForProhibition'); - - defCategory?.patchValue(deficiencyCategory.Dangerous); - prohibitionIssued?.patchValue(true); - stdForProhibition?.patchValue(false); - - expect(DefectValidators.validateProhibitionIssued(form.controls['prohibitionIssued'])).toBeNull(); - }); - - it('should return null when std for prohibition is true', () => { - const defCategory = form.get('deficiencyCategory'); - const prohibitionIssued = form.get('prohibitionIssued'); - const stdForProhibition = form.get('stdForProhibition'); - - defCategory?.patchValue(deficiencyCategory.Dangerous); - prohibitionIssued?.patchValue(false); - stdForProhibition?.patchValue(true); - - expect(DefectValidators.validateProhibitionIssued(form.controls['prohibitionIssued'])).toBeNull(); - }); - - it('should return null when deficiency category is not dangerous', () => { - const defCategory = form.get('deficiencyCategory'); - const prohibitionIssued = form.get('prohibitionIssued'); - const stdForProhibition = form.get('stdForProhibition'); - - defCategory?.patchValue(deficiencyCategory.Minor); - prohibitionIssued?.patchValue(false); - stdForProhibition?.patchValue(false); - - expect(DefectValidators.validateProhibitionIssued(form.controls['prohibitionIssued'])).toBeNull(); - }); + let form: FormGroup; + + beforeEach(() => { + form = new FormGroup({ + prohibitionIssued: new CustomFormControl({ name: 'prohibitionIssued', type: FormNodeTypes.CONTROL }), + deficiencyCategory: new CustomFormControl({ name: 'deficiencyCategory', type: FormNodeTypes.CONTROL }), + stdForProhibition: new CustomFormControl({ name: 'stdForProhibition', type: FormNodeTypes.CONTROL }), + }); + }); + + it('should return null', () => { + expect(DefectValidators.validateProhibitionIssued(form.controls['prohibitionIssued'])).toBeNull(); + }); + + it('should return error when dangerous no asterisk and prohibition issued is false', () => { + const defCategory = form.get('deficiencyCategory'); + const prohibitionIssued = form.get('prohibitionIssued'); + const stdForProhibition = form.get('stdForProhibition'); + + defCategory?.patchValue(deficiencyCategory.Dangerous); + prohibitionIssued?.patchValue(false); + stdForProhibition?.patchValue(false); + + expect(DefectValidators.validateProhibitionIssued(form.controls['prohibitionIssued'])).toEqual({ + validateProhibitionIssued: true, + }); + }); + + it('should return null when prohibition issued is true', () => { + const defCategory = form.get('deficiencyCategory'); + const prohibitionIssued = form.get('prohibitionIssued'); + const stdForProhibition = form.get('stdForProhibition'); + + defCategory?.patchValue(deficiencyCategory.Dangerous); + prohibitionIssued?.patchValue(true); + stdForProhibition?.patchValue(false); + + expect(DefectValidators.validateProhibitionIssued(form.controls['prohibitionIssued'])).toBeNull(); + }); + + it('should return null when std for prohibition is true', () => { + const defCategory = form.get('deficiencyCategory'); + const prohibitionIssued = form.get('prohibitionIssued'); + const stdForProhibition = form.get('stdForProhibition'); + + defCategory?.patchValue(deficiencyCategory.Dangerous); + prohibitionIssued?.patchValue(false); + stdForProhibition?.patchValue(true); + + expect(DefectValidators.validateProhibitionIssued(form.controls['prohibitionIssued'])).toBeNull(); + }); + + it('should return null when deficiency category is not dangerous', () => { + const defCategory = form.get('deficiencyCategory'); + const prohibitionIssued = form.get('prohibitionIssued'); + const stdForProhibition = form.get('stdForProhibition'); + + defCategory?.patchValue(deficiencyCategory.Minor); + prohibitionIssued?.patchValue(false); + stdForProhibition?.patchValue(false); + + expect(DefectValidators.validateProhibitionIssued(form.controls['prohibitionIssued'])).toBeNull(); + }); }); diff --git a/src/app/forms/validators/defects/defect.validators.ts b/src/app/forms/validators/defects/defect.validators.ts index 2d74cbb6d4..e8f8e27c26 100644 --- a/src/app/forms/validators/defects/defect.validators.ts +++ b/src/app/forms/validators/defects/defect.validators.ts @@ -2,44 +2,47 @@ import { AbstractControl, ValidationErrors, ValidatorFn } from '@angular/forms'; import { deficiencyCategory } from '@models/defects/deficiency-category.enum'; export class DefectValidators { - static validateDefectNotes: ValidatorFn = (control: AbstractControl): ValidationErrors | null => { - if (control?.parent && control.parent.parent) { - const grandParent = control.parent.parent; - const defCategory = grandParent?.get('deficiencyCategory')?.value as deficiencyCategory; - const prohibitionIssued = grandParent.get('prohibitionIssued')?.value as boolean; - const stdForProhibition = grandParent.get('stdForProhibition')?.value as boolean; - - const imNumber: string = grandParent.get('imNumber')?.value ? `${grandParent.get('imNumber')?.value}.` : ''; - const itemNumber: string = grandParent.get('itemNumber')?.value ? `${grandParent.get('itemNumber')?.value}.` : ''; - const deficiencyId: string = grandParent.get('deficiencyId')?.value ? `${grandParent.get('deficiencyId')?.value}.` : ''; - const deficiencySubId: string = grandParent.get('deficiencySubId')?.value ?? ''; - - const defectType = imNumber + itemNumber + deficiencyId + deficiencySubId; - - const optionalDefectNotes = ['43.1.a.ii', '41.1.a.ii', '10.1.iii']; - - if (optionalDefectNotes.includes(defectType)) return null; - - if ( - !control.value - && (defCategory === deficiencyCategory.Advisory || (defCategory === deficiencyCategory.Dangerous && stdForProhibition && !prohibitionIssued)) - ) { - return { validateDefectNotes: true }; - } - } - return null; - }; - - static validateProhibitionIssued: ValidatorFn = (control: AbstractControl): ValidationErrors | null => { - if (control?.parent) { - const defCategory = control.parent.get('deficiencyCategory')?.value as deficiencyCategory; - const stdForProhibition = control.parent.get('stdForProhibition')?.value as boolean; - const prohibitionIssued = control.value as boolean; - - if (defCategory === deficiencyCategory.Dangerous && !stdForProhibition && !prohibitionIssued) { - return { validateProhibitionIssued: true }; - } - } - return null; - }; + static validateDefectNotes: ValidatorFn = (control: AbstractControl): ValidationErrors | null => { + if (control?.parent && control.parent.parent) { + const grandParent = control.parent.parent; + const defCategory = grandParent?.get('deficiencyCategory')?.value as deficiencyCategory; + const prohibitionIssued = grandParent.get('prohibitionIssued')?.value as boolean; + const stdForProhibition = grandParent.get('stdForProhibition')?.value as boolean; + + const imNumber: string = grandParent.get('imNumber')?.value ? `${grandParent.get('imNumber')?.value}.` : ''; + const itemNumber: string = grandParent.get('itemNumber')?.value ? `${grandParent.get('itemNumber')?.value}.` : ''; + const deficiencyId: string = grandParent.get('deficiencyId')?.value + ? `${grandParent.get('deficiencyId')?.value}.` + : ''; + const deficiencySubId: string = grandParent.get('deficiencySubId')?.value ?? ''; + + const defectType = imNumber + itemNumber + deficiencyId + deficiencySubId; + + const optionalDefectNotes = ['43.1.a.ii', '41.1.a.ii', '10.1.iii']; + + if (optionalDefectNotes.includes(defectType)) return null; + + if ( + !control.value && + (defCategory === deficiencyCategory.Advisory || + (defCategory === deficiencyCategory.Dangerous && stdForProhibition && !prohibitionIssued)) + ) { + return { validateDefectNotes: true }; + } + } + return null; + }; + + static validateProhibitionIssued: ValidatorFn = (control: AbstractControl): ValidationErrors | null => { + if (control?.parent) { + const defCategory = control.parent.get('deficiencyCategory')?.value as deficiencyCategory; + const stdForProhibition = control.parent.get('stdForProhibition')?.value as boolean; + const prohibitionIssued = control.value as boolean; + + if (defCategory === deficiencyCategory.Dangerous && !stdForProhibition && !prohibitionIssued) { + return { validateProhibitionIssued: true }; + } + } + return null; + }; } diff --git a/src/app/govuk.d.ts b/src/app/govuk.d.ts index d0201c59fe..da6824e0e0 100644 --- a/src/app/govuk.d.ts +++ b/src/app/govuk.d.ts @@ -1,12 +1,12 @@ declare module 'govuk-frontend/govuk/all' { - export function initAll(): void; + export function initAll(): void; } declare module 'validate-govuk-date' { - export default function validateDate( - day: string | number, - month: string | number, - year: string | number, - fieldName?: string - ): { error: boolean; date?: Date; errors?: [{ error: boolean; reason: string; index: number }] }; + export default function validateDate( + day: string | number, + month: string | number, + year: string | number, + fieldName?: string + ): { error: boolean; date?: Date; errors?: [{ error: boolean; reason: string; index: number }] }; } diff --git a/src/app/guards/cancel-edit-tech/cancel-edit-tech.guard.spec.ts b/src/app/guards/cancel-edit-tech/cancel-edit-tech.guard.spec.ts index 789ff8425d..cc446e3ad6 100644 --- a/src/app/guards/cancel-edit-tech/cancel-edit-tech.guard.spec.ts +++ b/src/app/guards/cancel-edit-tech/cancel-edit-tech.guard.spec.ts @@ -6,25 +6,25 @@ import { provideMockStore } from '@ngrx/store/testing'; import { CancelEditTechGuard } from './cancel-edit-tech.guard'; describe('CancelEditTechGuard', () => { - let guard: CancelEditTechGuard; + let guard: CancelEditTechGuard; - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [RouterTestingModule], - providers: [ - CancelEditTechGuard, - provideMockStore({}), - { provide: RouterStateSnapshot, useValue: jest.fn().mockReturnValue({ url: '', toString: jest.fn() }) }, - ], - }); - guard = TestBed.inject(CancelEditTechGuard); - }); + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [RouterTestingModule], + providers: [ + CancelEditTechGuard, + provideMockStore({}), + { provide: RouterStateSnapshot, useValue: jest.fn().mockReturnValue({ url: '', toString: jest.fn() }) }, + ], + }); + guard = TestBed.inject(CancelEditTechGuard); + }); - it('should be created', () => { - expect(guard).toBeTruthy(); - }); + it('should be created', () => { + expect(guard).toBeTruthy(); + }); - it('should return true', () => { - expect(guard.canDeactivate()).toBeTruthy(); - }); + it('should return true', () => { + expect(guard.canDeactivate()).toBeTruthy(); + }); }); diff --git a/src/app/guards/cancel-edit-tech/cancel-edit-tech.guard.ts b/src/app/guards/cancel-edit-tech/cancel-edit-tech.guard.ts index a0a5b53f65..7ccc519456 100644 --- a/src/app/guards/cancel-edit-tech/cancel-edit-tech.guard.ts +++ b/src/app/guards/cancel-edit-tech/cancel-edit-tech.guard.ts @@ -6,18 +6,18 @@ import { updateEditingTechRecordCancel } from '@store/technical-records'; import { TechRecordComponent } from 'src/app/features/tech-record/tech-record.component'; @Injectable({ - providedIn: 'root', + providedIn: 'root', }) export class CancelEditTechGuard implements CanDeactivate, CanActivate { - constructor(private store: Store) {} + constructor(private store: Store) {} - canActivate(): boolean { - this.store.dispatch(updateEditingTechRecordCancel()); - return true; - } + canActivate(): boolean { + this.store.dispatch(updateEditingTechRecordCancel()); + return true; + } - canDeactivate(): boolean { - this.store.dispatch(updateEditingTechRecordCancel()); - return true; - } + canDeactivate(): boolean { + this.store.dispatch(updateEditingTechRecordCancel()); + return true; + } } diff --git a/src/app/guards/cancel-edit-test/cancel-edit-test.guard.spec.ts b/src/app/guards/cancel-edit-test/cancel-edit-test.guard.spec.ts index 2a170b7583..14ddd1d729 100644 --- a/src/app/guards/cancel-edit-test/cancel-edit-test.guard.spec.ts +++ b/src/app/guards/cancel-edit-test/cancel-edit-test.guard.spec.ts @@ -5,26 +5,26 @@ import { provideMockStore } from '@ngrx/store/testing'; import { CancelEditTestGuard } from './cancel-edit-test.guard'; describe('NoEditGuard', () => { - let guard: CancelEditTestGuard; + let guard: CancelEditTestGuard; - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [RouterTestingModule], - providers: [ - CancelEditTestGuard, - provideMockStore({}), - { provide: RouterStateSnapshot, useValue: jest.fn().mockReturnValue({ url: '', toString: jest.fn() }) }, - ], - }); + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [RouterTestingModule], + providers: [ + CancelEditTestGuard, + provideMockStore({}), + { provide: RouterStateSnapshot, useValue: jest.fn().mockReturnValue({ url: '', toString: jest.fn() }) }, + ], + }); - guard = TestBed.inject(CancelEditTestGuard); - }); + guard = TestBed.inject(CancelEditTestGuard); + }); - it('should be created', () => { - expect(guard).toBeTruthy(); - }); + it('should be created', () => { + expect(guard).toBeTruthy(); + }); - it('should return true', () => { - expect(guard.canDeactivate()).toBeTruthy(); - }); + it('should return true', () => { + expect(guard.canDeactivate()).toBeTruthy(); + }); }); diff --git a/src/app/guards/cancel-edit-test/cancel-edit-test.guard.ts b/src/app/guards/cancel-edit-test/cancel-edit-test.guard.ts index f11ace2f71..dd5cd72969 100644 --- a/src/app/guards/cancel-edit-test/cancel-edit-test.guard.ts +++ b/src/app/guards/cancel-edit-test/cancel-edit-test.guard.ts @@ -7,13 +7,13 @@ import { cancelEditingTestResult } from '@store/test-records'; import { TestRecordComponent } from 'src/app/features/test-records/amend/views/test-record/test-record.component'; @Injectable({ - providedIn: 'root', + providedIn: 'root', }) export class CancelEditTestGuard implements CanDeactivate { - constructor(private store: Store) {} + constructor(private store: Store) {} - canDeactivate(): boolean { - this.store.dispatch(cancelEditingTestResult()); - return true; - } + canDeactivate(): boolean { + this.store.dispatch(cancelEditingTestResult()); + return true; + } } diff --git a/src/app/guards/feature-toggle-guard/feature-toggle.guard.spec.ts b/src/app/guards/feature-toggle-guard/feature-toggle.guard.spec.ts index 8017d9c8bd..540d94ce3a 100644 --- a/src/app/guards/feature-toggle-guard/feature-toggle.guard.spec.ts +++ b/src/app/guards/feature-toggle-guard/feature-toggle.guard.spec.ts @@ -5,55 +5,51 @@ import { FeatureToggleService } from '@services/feature-toggle-service/feature-t import { FeatureToggleGuard } from './feature-toggle.guard'; describe('feature toggle guard', () => { - let guard: FeatureToggleGuard; + let guard: FeatureToggleGuard; - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [RouterTestingModule], - providers: [ - FeatureToggleGuard, - { provide: FeatureToggleService, useValue: { isFeatureEnabled: () => true } }, - ], - }); + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [RouterTestingModule], + providers: [FeatureToggleGuard, { provide: FeatureToggleService, useValue: { isFeatureEnabled: () => true } }], + }); - guard = TestBed.inject(FeatureToggleGuard); - }); + guard = TestBed.inject(FeatureToggleGuard); + }); - it('should be created', () => { - expect(guard).toBeTruthy(); - }); + it('should be created', () => { + expect(guard).toBeTruthy(); + }); - it('should return true when the feature is enabled', () => { - const next = new ActivatedRouteSnapshot(); - next.data = { featureToggleName: 'testToggle' }; + it('should return true when the feature is enabled', () => { + const next = new ActivatedRouteSnapshot(); + next.data = { featureToggleName: 'testToggle' }; - jest.spyOn(TestBed.inject(FeatureToggleService), 'isFeatureEnabled').mockReturnValue(true); + jest.spyOn(TestBed.inject(FeatureToggleService), 'isFeatureEnabled').mockReturnValue(true); - const guardResponse = guard.canActivate(next); + const guardResponse = guard.canActivate(next); - expect(guardResponse).toBeTruthy(); - }); + expect(guardResponse).toBeTruthy(); + }); - it('should return false when the feature is disabled', () => { - const next = new ActivatedRouteSnapshot(); - next.data = { featureToggleName: 'testToggle' }; + it('should return false when the feature is disabled', () => { + const next = new ActivatedRouteSnapshot(); + next.data = { featureToggleName: 'testToggle' }; - jest.spyOn(TestBed.inject(FeatureToggleService), 'isFeatureEnabled').mockReturnValue(false); + jest.spyOn(TestBed.inject(FeatureToggleService), 'isFeatureEnabled').mockReturnValue(false); - const guardResponse = guard.canActivate(next); + const guardResponse = guard.canActivate(next); - expect(guardResponse).toBeFalsy(); - }); + expect(guardResponse).toBeFalsy(); + }); - it('should return false when no feature is given', () => { - const next = new ActivatedRouteSnapshot(); - next.data = { }; + it('should return false when no feature is given', () => { + const next = new ActivatedRouteSnapshot(); + next.data = {}; - jest.spyOn(TestBed.inject(FeatureToggleService), 'isFeatureEnabled').mockReturnValue(true); + jest.spyOn(TestBed.inject(FeatureToggleService), 'isFeatureEnabled').mockReturnValue(true); - const guardResponse = guard.canActivate(next); - - expect(guardResponse).toBeFalsy(); - }); + const guardResponse = guard.canActivate(next); + expect(guardResponse).toBeFalsy(); + }); }); diff --git a/src/app/guards/feature-toggle-guard/feature-toggle.guard.ts b/src/app/guards/feature-toggle-guard/feature-toggle.guard.ts index 4358d6ecfe..808847be07 100644 --- a/src/app/guards/feature-toggle-guard/feature-toggle.guard.ts +++ b/src/app/guards/feature-toggle-guard/feature-toggle.guard.ts @@ -3,17 +3,17 @@ import { ActivatedRouteSnapshot, CanActivate } from '@angular/router'; import { FeatureToggleService } from '@services/feature-toggle-service/feature-toggle-service'; @Injectable({ - providedIn: 'root', + providedIn: 'root', }) export class FeatureToggleGuard implements CanActivate { - constructor(private featureToggleService: FeatureToggleService) {} + constructor(private featureToggleService: FeatureToggleService) {} - canActivate(next: ActivatedRouteSnapshot): boolean { - const feature = next.data['featureToggleName']; + canActivate(next: ActivatedRouteSnapshot): boolean { + const feature = next.data['featureToggleName']; - if (feature) { - return this.featureToggleService.isFeatureEnabled(feature); - } - return false; - } + if (feature) { + return this.featureToggleService.isFeatureEnabled(feature); + } + return false; + } } diff --git a/src/app/guards/no-edit/no-edit.guard.spec.ts b/src/app/guards/no-edit/no-edit.guard.spec.ts index 2dd8fb9c72..c90468e99b 100644 --- a/src/app/guards/no-edit/no-edit.guard.spec.ts +++ b/src/app/guards/no-edit/no-edit.guard.spec.ts @@ -8,53 +8,53 @@ import { routeEditable } from '@store/router/selectors/router.selectors'; import { NoEditGuard } from './no-edit.guard'; describe('NoEditGuard', () => { - let guard: NoEditGuard; - let store: MockStore; - let mockRouteEditable: MemoizedSelector, boolean, DefaultProjectorFn>; - let route: ActivatedRoute; - let mockRouterStateSnapshot: RouterStateSnapshot; - - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [RouterTestingModule], - providers: [ - NoEditGuard, - provideMockStore({}), - { provide: RouterStateSnapshot, useValue: jest.fn().mockReturnValue({ url: '', toString: jest.fn() }) }, - ], - }); - - guard = TestBed.inject(NoEditGuard); - store = TestBed.inject(MockStore); - route = TestBed.inject(ActivatedRoute); - mockRouterStateSnapshot = TestBed.inject(RouterStateSnapshot); - mockRouteEditable = store.overrideSelector(routeEditable, false); - }); - - it('should be created', () => { - expect(guard).toBeTruthy(); - }); - - describe('canActivate', () => { - it('should return true when not in edit mode', (done) => { - guard.canActivate(route.snapshot, mockRouterStateSnapshot).subscribe((result) => { - expect(result).toBeTruthy(); - done(); - }); - }); - - it('should reject navigation and return UrlTree without edit query param', (done) => { - mockRouteEditable.setResult(true); - store.refreshState(); - - mockRouterStateSnapshot.url = '/test-result/1/amended/1?edit=true&sanityCheck=bar'; - - guard.canActivate(route.snapshot, mockRouterStateSnapshot).subscribe((tree) => { - expect(tree instanceof UrlTree).toBeTruthy(); - expect((tree as UrlTree).queryParams['edit']).toBeUndefined(); - expect((tree as UrlTree).queryParams['sanityCheck']).toBe('bar'); - done(); - }); - }); - }); + let guard: NoEditGuard; + let store: MockStore; + let mockRouteEditable: MemoizedSelector, boolean, DefaultProjectorFn>; + let route: ActivatedRoute; + let mockRouterStateSnapshot: RouterStateSnapshot; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [RouterTestingModule], + providers: [ + NoEditGuard, + provideMockStore({}), + { provide: RouterStateSnapshot, useValue: jest.fn().mockReturnValue({ url: '', toString: jest.fn() }) }, + ], + }); + + guard = TestBed.inject(NoEditGuard); + store = TestBed.inject(MockStore); + route = TestBed.inject(ActivatedRoute); + mockRouterStateSnapshot = TestBed.inject(RouterStateSnapshot); + mockRouteEditable = store.overrideSelector(routeEditable, false); + }); + + it('should be created', () => { + expect(guard).toBeTruthy(); + }); + + describe('canActivate', () => { + it('should return true when not in edit mode', (done) => { + guard.canActivate(route.snapshot, mockRouterStateSnapshot).subscribe((result) => { + expect(result).toBeTruthy(); + done(); + }); + }); + + it('should reject navigation and return UrlTree without edit query param', (done) => { + mockRouteEditable.setResult(true); + store.refreshState(); + + mockRouterStateSnapshot.url = '/test-result/1/amended/1?edit=true&sanityCheck=bar'; + + guard.canActivate(route.snapshot, mockRouterStateSnapshot).subscribe((tree) => { + expect(tree instanceof UrlTree).toBeTruthy(); + expect((tree as UrlTree).queryParams['edit']).toBeUndefined(); + expect((tree as UrlTree).queryParams['sanityCheck']).toBe('bar'); + done(); + }); + }); + }); }); diff --git a/src/app/guards/no-edit/no-edit.guard.ts b/src/app/guards/no-edit/no-edit.guard.ts index 3aeee845c1..1edd1658b0 100644 --- a/src/app/guards/no-edit/no-edit.guard.ts +++ b/src/app/guards/no-edit/no-edit.guard.ts @@ -1,31 +1,32 @@ import { Injectable } from '@angular/core'; -import { - ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot, UrlTree, -} from '@angular/router'; -import { select, Store } from '@ngrx/store'; +import { ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot, UrlTree } from '@angular/router'; +import { Store, select } from '@ngrx/store'; import { State } from '@store/.'; import { routeEditable } from '@store/router/selectors/router.selectors'; -import { map, Observable } from 'rxjs'; +import { Observable, map } from 'rxjs'; @Injectable({ - providedIn: 'root', + providedIn: 'root', }) export class NoEditGuard implements CanActivate { - constructor(private store: Store, private router: Router) {} + constructor( + private store: Store, + private router: Router + ) {} - canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable { - return this.store.pipe( - select(routeEditable), - map((editable) => { - if (!editable) { - return true; - } + canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable { + return this.store.pipe( + select(routeEditable), + map((editable) => { + if (!editable) { + return true; + } - const tree = this.router.parseUrl(state.url); - delete tree.queryParams['edit']; + const tree = this.router.parseUrl(state.url); + delete tree.queryParams['edit']; - return tree; - }), - ); - } + return tree; + }) + ); + } } diff --git a/src/app/guards/no-query-params/no-query-params.guard.spec.ts b/src/app/guards/no-query-params/no-query-params.guard.spec.ts index 736f6604b5..2096327fed 100644 --- a/src/app/guards/no-query-params/no-query-params.guard.spec.ts +++ b/src/app/guards/no-query-params/no-query-params.guard.spec.ts @@ -2,85 +2,87 @@ import { TestBed } from '@angular/core/testing'; import { MockStore, provideMockStore } from '@ngrx/store/testing'; import { State } from '@store/.'; -import { - Navigation, NavigationExtras, Params, Router, RouterStateSnapshot, UrlTree, -} from '@angular/router'; +import { Navigation, NavigationExtras, Params, Router, RouterStateSnapshot, UrlTree } from '@angular/router'; import { RouterTestingModule } from '@angular/router/testing'; import { selectQueryParams } from '@store/router/selectors/router.selectors'; import { NoQueryParamsGuard } from './no-query-params.guard'; describe('NoQueryParamsGuard', () => { - let guard: NoQueryParamsGuard; - let store: MockStore; - let router: Router; + let guard: NoQueryParamsGuard; + let store: MockStore; + let router: Router; - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [RouterTestingModule], - providers: [NoQueryParamsGuard, provideMockStore({}), { provide: RouterStateSnapshot, useValue: jest.fn().mockReturnValue({ url: '' }) }], - }); - guard = TestBed.inject(NoQueryParamsGuard); - store = TestBed.inject(MockStore); - store.overrideSelector(selectQueryParams, {}); - router = TestBed.inject(Router); - }); + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [RouterTestingModule], + providers: [ + NoQueryParamsGuard, + provideMockStore({}), + { provide: RouterStateSnapshot, useValue: jest.fn().mockReturnValue({ url: '' }) }, + ], + }); + guard = TestBed.inject(NoQueryParamsGuard); + store = TestBed.inject(MockStore); + store.overrideSelector(selectQueryParams, {}); + router = TestBed.inject(Router); + }); - it('should be created', () => { - expect(guard).toBeTruthy(); - }); + it('should be created', () => { + expect(guard).toBeTruthy(); + }); - describe('No query params guard', () => { - it('should return true if there are query params', (done) => { - store.overrideSelector(selectQueryParams, { foo: 'bar' } as Params); - guard.canActivate().subscribe((result) => { - expect(result).toBe(true); - done(); - }); - }); + describe('No query params guard', () => { + it('should return true if there are query params', (done) => { + store.overrideSelector(selectQueryParams, { foo: 'bar' } as Params); + guard.canActivate().subscribe((result) => { + expect(result).toBe(true); + done(); + }); + }); - it('should return an empty url when the previous navigation is undefined', (done) => { - const mockNavigation: Navigation = { - id: 1, - initialUrl: '/some/path' as unknown as UrlTree, - extractedUrl: {} as UrlTree, - trigger: 'hashchange', - extras: {} as NavigationExtras, - previousNavigation: {} as Navigation, - }; - router.getCurrentNavigation = jest.fn().mockReturnValue(mockNavigation); - guard.canActivate().subscribe((tree) => { - expect(tree instanceof UrlTree).toBeTruthy(); - expect((tree as UrlTree).toString()).toBe('/'); - done(); - }); - }); + it('should return an empty url when the previous navigation is undefined', (done) => { + const mockNavigation: Navigation = { + id: 1, + initialUrl: '/some/path' as unknown as UrlTree, + extractedUrl: {} as UrlTree, + trigger: 'hashchange', + extras: {} as NavigationExtras, + previousNavigation: {} as Navigation, + }; + router.getCurrentNavigation = jest.fn().mockReturnValue(mockNavigation); + guard.canActivate().subscribe((tree) => { + expect(tree instanceof UrlTree).toBeTruthy(); + expect((tree as UrlTree).toString()).toBe('/'); + done(); + }); + }); - it('should return the previous Url if the previous navigation is defined', (done) => { - const mockNavigation: Navigation = { - id: 1, - initialUrl: '/some/path' as unknown as UrlTree, - extractedUrl: {} as UrlTree, - trigger: 'hashchange', - extras: {} as NavigationExtras, - previousNavigation: {} as Navigation, - }; + it('should return the previous Url if the previous navigation is defined', (done) => { + const mockNavigation: Navigation = { + id: 1, + initialUrl: '/some/path' as unknown as UrlTree, + extractedUrl: {} as UrlTree, + trigger: 'hashchange', + extras: {} as NavigationExtras, + previousNavigation: {} as Navigation, + }; - const tree = router.parseUrl('/path'); + const tree = router.parseUrl('/path'); - const previousNavigation: Navigation = { - ...mockNavigation, - previousNavigation: { - ...mockNavigation, - finalUrl: tree, - }, - }; - router.getCurrentNavigation = jest.fn().mockReturnValue(previousNavigation); - guard.canActivate().subscribe((url_tree) => { - expect(url_tree instanceof UrlTree).toBeTruthy(); - expect(previousNavigation.previousNavigation?.finalUrl?.toString()).toBeTruthy(); - expect(url_tree.toString()).toEqual(previousNavigation.previousNavigation?.finalUrl?.toString()); - done(); - }); - }); - }); + const previousNavigation: Navigation = { + ...mockNavigation, + previousNavigation: { + ...mockNavigation, + finalUrl: tree, + }, + }; + router.getCurrentNavigation = jest.fn().mockReturnValue(previousNavigation); + guard.canActivate().subscribe((url_tree) => { + expect(url_tree instanceof UrlTree).toBeTruthy(); + expect(previousNavigation.previousNavigation?.finalUrl?.toString()).toBeTruthy(); + expect(url_tree.toString()).toEqual(previousNavigation.previousNavigation?.finalUrl?.toString()); + done(); + }); + }); + }); }); diff --git a/src/app/guards/no-query-params/no-query-params.guard.ts b/src/app/guards/no-query-params/no-query-params.guard.ts index 31dbe03c18..084b47472c 100644 --- a/src/app/guards/no-query-params/no-query-params.guard.ts +++ b/src/app/guards/no-query-params/no-query-params.guard.ts @@ -1,24 +1,27 @@ import { Injectable } from '@angular/core'; import { CanActivate, Router, UrlTree } from '@angular/router'; -import { select, Store } from '@ngrx/store'; +import { Store, select } from '@ngrx/store'; import { State } from '@store/.'; import { selectQueryParams } from '@store/router/selectors/router.selectors'; -import { map, Observable } from 'rxjs'; +import { Observable, map } from 'rxjs'; @Injectable({ - providedIn: 'root', + providedIn: 'root', }) export class NoQueryParamsGuard implements CanActivate { - constructor(private store: Store, private router: Router) {} - canActivate(): Observable { - return this.store.pipe( - select(selectQueryParams), - map((queryParams) => { - if (!Object.keys(queryParams).length) { - return this.router.getCurrentNavigation()?.previousNavigation?.finalUrl ?? this.router.parseUrl(''); - } - return true; - }), - ); - } + constructor( + private store: Store, + private router: Router + ) {} + canActivate(): Observable { + return this.store.pipe( + select(selectQueryParams), + map((queryParams) => { + if (!Object.keys(queryParams).length) { + return this.router.getCurrentNavigation()?.previousNavigation?.finalUrl ?? this.router.parseUrl(''); + } + return true; + }) + ); + } } diff --git a/src/app/guards/role-guard/roles.guard.spec.ts b/src/app/guards/role-guard/roles.guard.spec.ts index 06d009a6b3..4aaa0c557d 100644 --- a/src/app/guards/role-guard/roles.guard.spec.ts +++ b/src/app/guards/role-guard/roles.guard.spec.ts @@ -1,57 +1,57 @@ import { TestBed } from '@angular/core/testing'; import { ActivatedRouteSnapshot } from '@angular/router'; import { RouterTestingModule } from '@angular/router/testing'; +import { InteractionStatus } from '@azure/msal-browser'; import { provideMockStore } from '@ngrx/store/testing'; import { UserService } from '@services/user-service/user-service'; import { initialAppState } from '@store/.'; -import { of, lastValueFrom } from 'rxjs'; -import { InteractionStatus } from '@azure/msal-browser'; +import { lastValueFrom, of } from 'rxjs'; import { RoleGuard } from './roles.guard'; describe('RoleGuard', () => { - let guard: RoleGuard; + let guard: RoleGuard; - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [RouterTestingModule], - providers: [ - RoleGuard, - provideMockStore({ initialState: initialAppState }), - { provide: UserService, useValue: { roles$: of(['CVSFullAccess']), inProgress$: of(InteractionStatus.None) } }, - ], - }); + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [RouterTestingModule], + providers: [ + RoleGuard, + provideMockStore({ initialState: initialAppState }), + { provide: UserService, useValue: { roles$: of(['CVSFullAccess']), inProgress$: of(InteractionStatus.None) } }, + ], + }); - guard = TestBed.inject(RoleGuard); - }); + guard = TestBed.inject(RoleGuard); + }); - it('should be created', () => { - expect(guard).toBeTruthy(); - }); + it('should be created', () => { + expect(guard).toBeTruthy(); + }); - it('should return true when I give it the correct role', async () => { - const next = new ActivatedRouteSnapshot(); - next.data = { roles: ['CVSFullAccess'] }; + it('should return true when I give it the correct role', async () => { + const next = new ActivatedRouteSnapshot(); + next.data = { roles: ['CVSFullAccess'] }; - const guardObservable = guard.canActivate(next); + const guardObservable = guard.canActivate(next); - await expect(lastValueFrom(guardObservable)).resolves.toBe(true); - }); + await expect(lastValueFrom(guardObservable)).resolves.toBe(true); + }); - it('should return false when I give it the incorrect role', async () => { - const next = new ActivatedRouteSnapshot(); - next.data = { roles: ['BadRole'] }; + it('should return false when I give it the incorrect role', async () => { + const next = new ActivatedRouteSnapshot(); + next.data = { roles: ['BadRole'] }; - const guardObservable = guard.canActivate(next); + const guardObservable = guard.canActivate(next); - await expect(lastValueFrom(guardObservable)).resolves.toBe(false); - }); + await expect(lastValueFrom(guardObservable)).resolves.toBe(false); + }); - it('should return true when I give it one incorrect role and one correct role', async () => { - const next = new ActivatedRouteSnapshot(); - next.data = { roles: ['CVSFullAccess', 'BadRole'] }; + it('should return true when I give it one incorrect role and one correct role', async () => { + const next = new ActivatedRouteSnapshot(); + next.data = { roles: ['CVSFullAccess', 'BadRole'] }; - const guardObservable = guard.canActivate(next); + const guardObservable = guard.canActivate(next); - await expect(lastValueFrom(guardObservable)).resolves.toBe(true); - }); + await expect(lastValueFrom(guardObservable)).resolves.toBe(true); + }); }); diff --git a/src/app/guards/role-guard/roles.guard.ts b/src/app/guards/role-guard/roles.guard.ts index 4db0963c14..bf3d6777bd 100644 --- a/src/app/guards/role-guard/roles.guard.ts +++ b/src/app/guards/role-guard/roles.guard.ts @@ -2,23 +2,21 @@ import { Injectable } from '@angular/core'; import { ActivatedRouteSnapshot, CanActivate } from '@angular/router'; import { InteractionStatus } from '@azure/msal-browser'; import { UserService } from '@services/user-service/user-service'; -import { - filter, map, Observable, switchMap, -} from 'rxjs'; +import { Observable, filter, map, switchMap } from 'rxjs'; @Injectable({ - providedIn: 'root', + providedIn: 'root', }) export class RoleGuard implements CanActivate { - constructor(private userService: UserService) {} + constructor(private userService: UserService) {} - canActivate(next: ActivatedRouteSnapshot): Observable { - return this.userService.inProgress$.pipe( - filter((status: InteractionStatus) => InteractionStatus.None === status), - switchMap(() => this.userService.roles$), - map((roles) => { - return roles?.some((x) => next.data['roles'].includes(x)) || false; - }), - ); - } + canActivate(next: ActivatedRouteSnapshot): Observable { + return this.userService.inProgress$.pipe( + filter((status: InteractionStatus) => InteractionStatus.None === status), + switchMap(() => this.userService.roles$), + map((roles) => { + return roles?.some((x) => next.data['roles'].includes(x)) || false; + }) + ); + } } diff --git a/src/app/interceptors/delayed-retry/delayed-retry.interceptor.spec.ts b/src/app/interceptors/delayed-retry/delayed-retry.interceptor.spec.ts index b5f95ee123..4e98843a3d 100644 --- a/src/app/interceptors/delayed-retry/delayed-retry.interceptor.spec.ts +++ b/src/app/interceptors/delayed-retry/delayed-retry.interceptor.spec.ts @@ -1,118 +1,114 @@ import { HTTP_INTERCEPTORS, HttpClient } from '@angular/common/http'; import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; -import { - TestBed, - fakeAsync, flush, - tick, -} from '@angular/core/testing'; +import { TestBed, fakeAsync, flush, tick } from '@angular/core/testing'; import { provideMockStore } from '@ngrx/store/testing'; import { initialAppState } from '@store/index'; import { DelayedRetryInterceptor, HTTP_RETRY_CONFIG } from './delayed-retry.interceptor'; describe('DelayedRetryInterceptor', () => { - let httpTestingController: HttpTestingController; - let client: HttpClient; - let interceptor: DelayedRetryInterceptor; + let httpTestingController: HttpTestingController; + let client: HttpClient; + let interceptor: DelayedRetryInterceptor; - const DUMMY_ENDPOINT = 'https://www.someapi.com'; + const DUMMY_ENDPOINT = 'https://www.someapi.com'; - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [HttpClientTestingModule], - providers: [ - DelayedRetryInterceptor, - { - provide: HTTP_INTERCEPTORS, - useClass: DelayedRetryInterceptor, - multi: true, - }, - provideMockStore({ initialState: initialAppState }), - ], - }); - }); + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [HttpClientTestingModule], + providers: [ + DelayedRetryInterceptor, + { + provide: HTTP_INTERCEPTORS, + useClass: DelayedRetryInterceptor, + multi: true, + }, + provideMockStore({ initialState: initialAppState }), + ], + }); + }); - describe('default config', () => { - it('should be created', () => { - interceptor = TestBed.inject(DelayedRetryInterceptor); - expect(interceptor).toBeTruthy(); - expect(interceptor.config).toEqual({ - count: 3, - delay: 2000, - backoff: false, - whiteList: [], - }); - }); - }); + describe('default config', () => { + it('should be created', () => { + interceptor = TestBed.inject(DelayedRetryInterceptor); + expect(interceptor).toBeTruthy(); + expect(interceptor.config).toEqual({ + count: 3, + delay: 2000, + backoff: false, + whiteList: [], + }); + }); + }); - describe('no backof', () => { - beforeEach(() => { - TestBed.overrideProvider(HTTP_RETRY_CONFIG, { useValue: { delay: 500, count: 3, httpStatusRetry: [504] } }); - client = TestBed.inject(HttpClient); - httpTestingController = TestBed.inject(HttpTestingController); - interceptor = TestBed.inject(DelayedRetryInterceptor); - }); + describe('no backof', () => { + beforeEach(() => { + TestBed.overrideProvider(HTTP_RETRY_CONFIG, { useValue: { delay: 500, count: 3, httpStatusRetry: [504] } }); + client = TestBed.inject(HttpClient); + httpTestingController = TestBed.inject(HttpTestingController); + interceptor = TestBed.inject(DelayedRetryInterceptor); + }); - afterEach(() => { - // After every test, assert that there are no more pending requests. - httpTestingController.verify(); - }); + afterEach(() => { + // After every test, assert that there are no more pending requests. + httpTestingController.verify(); + }); - it('should throw error "Request timed out. Check connectivity and try again." after final retry', fakeAsync(() => { - client.get(DUMMY_ENDPOINT).subscribe({ - error: (e) => { - expect(e).toEqual(new Error('Request timed out. Check connectivity and try again.')); - }, - }); - const retryCount = 3; - for (let i = 0, c = retryCount; i < c; i++) { - tick(500); - const req = httpTestingController.expectOne(DUMMY_ENDPOINT); - req.flush('Deliberate 504 error', { status: 504, statusText: 'Gateway Timeout' }); - } - flush(); - })); + it('should throw error "Request timed out. Check connectivity and try again." after final retry', fakeAsync(() => { + client.get(DUMMY_ENDPOINT).subscribe({ + error: (e) => { + expect(e).toEqual(new Error('Request timed out. Check connectivity and try again.')); + }, + }); + const retryCount = 3; + for (let i = 0, c = retryCount; i < c; i++) { + tick(500); + const req = httpTestingController.expectOne(DUMMY_ENDPOINT); + req.flush('Deliberate 504 error', { status: 504, statusText: 'Gateway Timeout' }); + } + flush(); + })); - it('should cascade errors not in the retry list', (done) => { - client.get(DUMMY_ENDPOINT).subscribe({ - error: (e) => { - const { error, status, statusText } = e; - expect(error).toBe('Deliberate 401 error'); - expect(status).toBe(401); - expect(statusText).toBe('401 Unauthorized'); - done(); - }, - }); - const req = httpTestingController.expectOne(DUMMY_ENDPOINT); - req.flush('Deliberate 401 error', { status: 401, statusText: '401 Unauthorized' }); - }); - }); + it('should cascade errors not in the retry list', (done) => { + client.get(DUMMY_ENDPOINT).subscribe({ + error: (e) => { + const { error, status, statusText } = e; + expect(error).toBe('Deliberate 401 error'); + expect(status).toBe(401); + expect(statusText).toBe('401 Unauthorized'); + done(); + }, + }); + const req = httpTestingController.expectOne(DUMMY_ENDPOINT); + req.flush('Deliberate 401 error', { status: 401, statusText: '401 Unauthorized' }); + }); + }); - describe('backoff', () => { - beforeEach(() => { - TestBed.overrideProvider(HTTP_RETRY_CONFIG, { useValue: { count: 3, delay: 500, backoff: true } }); - client = TestBed.inject(HttpClient); - httpTestingController = TestBed.inject(HttpTestingController); - }); + describe('backoff', () => { + beforeEach(() => { + TestBed.overrideProvider(HTTP_RETRY_CONFIG, { useValue: { count: 3, delay: 500, backoff: true } }); + client = TestBed.inject(HttpClient); + httpTestingController = TestBed.inject(HttpTestingController); + }); - afterEach(() => { - // After every test, assert that there are no more pending requests. - httpTestingController.verify(); - }); + afterEach(() => { + // After every test, assert that there are no more pending requests. + httpTestingController.verify(); + }); - it('should stagger retry requests', fakeAsync(() => { - client.get(DUMMY_ENDPOINT).subscribe({ - error: (e) => { - expect(e).toEqual(new Error('Request timed out. Check connectivity and try again.')); - }, - }); + it('should stagger retry requests', fakeAsync(() => { + client.get(DUMMY_ENDPOINT).subscribe({ + error: (e) => { + expect(e).toEqual(new Error('Request timed out. Check connectivity and try again.')); + }, + }); - const retryCount = 3; - for (let i = 0; i < retryCount; i++) { - tick(500 * (i + 1)); - const req = httpTestingController.expectOne(DUMMY_ENDPOINT); - req.flush('Deliberate 504 error', { status: 504, statusText: 'Gateway Timeout' }); - } - flush(); - })); - }); + const retryCount = 3; + for (let i = 0; i < retryCount; i++) { + tick(500 * (i + 1)); + const req = httpTestingController.expectOne(DUMMY_ENDPOINT); + req.flush('Deliberate 504 error', { status: 504, statusText: 'Gateway Timeout' }); + } + flush(); + })); + }); }); diff --git a/src/app/interceptors/delayed-retry/delayed-retry.interceptor.ts b/src/app/interceptors/delayed-retry/delayed-retry.interceptor.ts index ab0eda8253..3ca28337bf 100644 --- a/src/app/interceptors/delayed-retry/delayed-retry.interceptor.ts +++ b/src/app/interceptors/delayed-retry/delayed-retry.interceptor.ts @@ -1,98 +1,92 @@ -import { - HttpEvent, HttpHandler, HttpInterceptor, HttpRequest, -} from '@angular/common/http'; -import { - Inject, Injectable, InjectionToken, Optional, -} from '@angular/core'; +import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http'; +import { Inject, Injectable, InjectionToken, Optional } from '@angular/core'; import { Store } from '@ngrx/store'; import { State } from '@store/index'; import { retryInterceptorFailure } from '@store/retry-interceptor/actions/retry-interceptor.actions'; import { setSpinnerState } from '@store/spinner/actions/spinner.actions'; -import { - Observable, - retry, - throwError, - timer, -} from 'rxjs'; +import { Observable, retry, throwError, timer } from 'rxjs'; export const HTTP_RETRY_CONFIG = new InjectionToken('HttpRetryConfig'); export interface HttpRetryConfig { - /** - * The maximum number of times to retry. - */ - count?: number; - /** - * The number of milliseconds to delay before retrying. - */ - delay?: number; - /** - * Array of http status codes to retry. If undefined, all requests are retried. - */ - httpStatusRetry?: Array; - /** - * If true, each retry delay will be multiplied by the current retry count. - */ - backoff?: boolean; + /** + * The maximum number of times to retry. + */ + count?: number; + /** + * The number of milliseconds to delay before retrying. + */ + delay?: number; + /** + * Array of http status codes to retry. If undefined, all requests are retried. + */ + httpStatusRetry?: Array; + /** + * If true, each retry delay will be multiplied by the current retry count. + */ + backoff?: boolean; - whiteList?: string[] + whiteList?: string[]; } -interface InternalConfig extends Required>, Pick { - whiteList: string[] +interface InternalConfig + extends Required>, + Pick { + whiteList: string[]; } @Injectable() export class DelayedRetryInterceptor implements HttpInterceptor { - config: InternalConfig; + config: InternalConfig; - private readonly defaultConfig = { - count: 3, - delay: 2000, - backoff: false, - whiteList: [], - }; + private readonly defaultConfig = { + count: 3, + delay: 2000, + backoff: false, + whiteList: [], + }; - constructor(@Optional() @Inject(HTTP_RETRY_CONFIG) private retryConfig: HttpRetryConfig, private store: Store) { - this.config = { ...this.defaultConfig, ...this.retryConfig }; - } + constructor( + @Optional() @Inject(HTTP_RETRY_CONFIG) private retryConfig: HttpRetryConfig, + private store: Store + ) { + this.config = { ...this.defaultConfig, ...this.retryConfig }; + } - intercept(request: HttpRequest, next: HttpHandler): Observable> { - return next.handle(request).pipe(retry({ - delay: (error, retryCount: number) => { - try { - return this.retryHandler(error, retryCount, this.config, request); - } catch (httpError: unknown) { - return throwError(() => { - this.store.dispatch(setSpinnerState({ showSpinner: false })); - this.store.dispatch(retryInterceptorFailure( - { error }, - )); - return httpError; - }); - } - }, - })); - } + intercept(request: HttpRequest, next: HttpHandler): Observable> { + return next.handle(request).pipe( + retry({ + delay: (error, retryCount: number) => { + try { + return this.retryHandler(error, retryCount, this.config, request); + } catch (httpError: unknown) { + return throwError(() => { + this.store.dispatch(setSpinnerState({ showSpinner: false })); + this.store.dispatch(retryInterceptorFailure({ error })); + return httpError; + }); + } + }, + }) + ); + } - // eslint-disable-next-line @typescript-eslint/no-explicit-any - retryHandler(error: any, retryCount: number, config: InternalConfig, request: HttpRequest) { - const { - delay, count, httpStatusRetry, backoff, whiteList, - } = config; - const { status } = error; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + retryHandler(error: any, retryCount: number, config: InternalConfig, request: HttpRequest) { + const { delay, count, httpStatusRetry, backoff, whiteList } = config; + const { status } = error; - if (retryCount < count && whiteList.some((url) => request.url.includes(url))) { - return timer(backoff ? delay * retryCount : delay); - } + if (retryCount < count && whiteList.some((url) => request.url.includes(url))) { + return timer(backoff ? delay * retryCount : delay); + } - if (httpStatusRetry && !httpStatusRetry.includes(status)) { - throw error; - } + if (httpStatusRetry && !httpStatusRetry.includes(status)) { + throw error; + } - if (retryCount >= count) { - throw new Error('Request timed out. Check connectivity and try again.'); - } - return timer(backoff ? delay * retryCount : delay); - } + if (retryCount >= count) { + throw new Error('Request timed out. Check connectivity and try again.'); + } + return timer(backoff ? delay * retryCount : delay); + } } diff --git a/src/app/interceptors/delayed-retry/delayed-retry.module.ts b/src/app/interceptors/delayed-retry/delayed-retry.module.ts index 7924f4ee4d..30b8321cc4 100644 --- a/src/app/interceptors/delayed-retry/delayed-retry.module.ts +++ b/src/app/interceptors/delayed-retry/delayed-retry.module.ts @@ -1,32 +1,30 @@ import { CommonModule } from '@angular/common'; import { HTTP_INTERCEPTORS } from '@angular/common/http'; -import { - ModuleWithProviders, NgModule, Optional, SkipSelf, -} from '@angular/core'; -import { DelayedRetryInterceptor, HttpRetryConfig, HTTP_RETRY_CONFIG } from './delayed-retry.interceptor'; +import { ModuleWithProviders, NgModule, Optional, SkipSelf } from '@angular/core'; +import { DelayedRetryInterceptor, HTTP_RETRY_CONFIG, HttpRetryConfig } from './delayed-retry.interceptor'; @NgModule({ - declarations: [], - imports: [CommonModule], - providers: [ - { - provide: HTTP_INTERCEPTORS, - useClass: DelayedRetryInterceptor, - multi: true, - }, - ], + declarations: [], + imports: [CommonModule], + providers: [ + { + provide: HTTP_INTERCEPTORS, + useClass: DelayedRetryInterceptor, + multi: true, + }, + ], }) export class DelayedRetryModule { - constructor(@Optional() @SkipSelf() parentModule?: DelayedRetryModule) { - if (parentModule) { - throw new Error('DelayedRetryModule is already loaded. Import it in the AppModule only'); - } - } + constructor(@Optional() @SkipSelf() parentModule?: DelayedRetryModule) { + if (parentModule) { + throw new Error('DelayedRetryModule is already loaded. Import it in the AppModule only'); + } + } - static forRoot(config?: HttpRetryConfig): ModuleWithProviders { - return { - ngModule: DelayedRetryModule, - providers: [{ provide: HTTP_RETRY_CONFIG, useValue: config }], - }; - } + static forRoot(config?: HttpRetryConfig): ModuleWithProviders { + return { + ngModule: DelayedRetryModule, + providers: [{ provide: HTTP_RETRY_CONFIG, useValue: config }], + }; + } } diff --git a/src/app/interceptors/error-handling/error-handling.interceptor.spec.ts b/src/app/interceptors/error-handling/error-handling.interceptor.spec.ts index a0a8350397..0177df4752 100644 --- a/src/app/interceptors/error-handling/error-handling.interceptor.spec.ts +++ b/src/app/interceptors/error-handling/error-handling.interceptor.spec.ts @@ -1,89 +1,89 @@ -import { HttpClient, HTTP_INTERCEPTORS } from '@angular/common/http'; +import { HTTP_INTERCEPTORS, HttpClient } from '@angular/common/http'; import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; import { Component } from '@angular/core'; -import { fakeAsync, TestBed, tick } from '@angular/core/testing'; +import { TestBed, fakeAsync, tick } from '@angular/core/testing'; import { Router } from '@angular/router'; import { RouterTestingModule } from '@angular/router/testing'; import { ErrorInterceptor } from './error-handling.interceptor'; @Component({ - selector: 'app-dummy-component', - template: '', + selector: 'app-dummy-component', + template: '', }) class DummyComponent {} describe('ErrorInterceptor', () => { - let router: Router; - let http: HttpClient; - let httpController: HttpTestingController; + let router: Router; + let http: HttpClient; + let httpController: HttpTestingController; - beforeEach(() => { - TestBed.configureTestingModule({ - declarations: [DummyComponent], - imports: [ - HttpClientTestingModule, - RouterTestingModule.withRoutes([ - { - path: '', - component: DummyComponent, - }, - { - path: 'error', - component: DummyComponent, - }, - ]), - ], - providers: [ - ErrorInterceptor, - { - provide: HTTP_INTERCEPTORS, - useClass: ErrorInterceptor, - multi: true, - }, - ], - }); - }); + beforeEach(() => { + TestBed.configureTestingModule({ + declarations: [DummyComponent], + imports: [ + HttpClientTestingModule, + RouterTestingModule.withRoutes([ + { + path: '', + component: DummyComponent, + }, + { + path: 'error', + component: DummyComponent, + }, + ]), + ], + providers: [ + ErrorInterceptor, + { + provide: HTTP_INTERCEPTORS, + useClass: ErrorInterceptor, + multi: true, + }, + ], + }); + }); - beforeEach(() => { - router = TestBed.inject(Router); - http = TestBed.inject(HttpClient); - httpController = TestBed.inject(HttpTestingController); - jest.clearAllMocks(); - }); + beforeEach(() => { + router = TestBed.inject(Router); + http = TestBed.inject(HttpClient); + httpController = TestBed.inject(HttpTestingController); + jest.clearAllMocks(); + }); - it('should be created', () => { - const interceptor: ErrorInterceptor = TestBed.inject(ErrorInterceptor); + it('should be created', () => { + const interceptor: ErrorInterceptor = TestBed.inject(ErrorInterceptor); - expect(interceptor).toBeTruthy(); - }); + expect(interceptor).toBeTruthy(); + }); - it('should navigate to error page on a 500', fakeAsync(() => { - http.get('http://www.google.com').subscribe({ - next: () => {}, - error: (e) => { - expect(e.status).toBe(500); - }, - }); + it('should navigate to error page on a 500', fakeAsync(() => { + http.get('http://www.google.com').subscribe({ + next: () => {}, + error: (e) => { + expect(e.status).toBe(500); + }, + }); - const req = httpController.expectOne('http://www.google.com'); - req.flush('string', { status: 500, statusText: 'Internal Server Error' }); + const req = httpController.expectOne('http://www.google.com'); + req.flush('string', { status: 500, statusText: 'Internal Server Error' }); - tick(); + tick(); - expect(router.url).toBe('/error'); - })); + expect(router.url).toBe('/error'); + })); - it('should not navigate to error page on a success', fakeAsync(() => { - http.get('http://www.google.com').subscribe({ - next: (response) => { - expect(response).toBe('string'); - }, - error: () => {}, - }); + it('should not navigate to error page on a success', fakeAsync(() => { + http.get('http://www.google.com').subscribe({ + next: (response) => { + expect(response).toBe('string'); + }, + error: () => {}, + }); - const req = httpController.expectOne('http://www.google.com'); - req.flush('string'); - tick(); - expect(router.url).toBe('/'); - })); + const req = httpController.expectOne('http://www.google.com'); + req.flush('string'); + tick(); + expect(router.url).toBe('/'); + })); }); diff --git a/src/app/interceptors/error-handling/error-handling.interceptor.ts b/src/app/interceptors/error-handling/error-handling.interceptor.ts index 5aef1090f5..228eaa28f1 100644 --- a/src/app/interceptors/error-handling/error-handling.interceptor.ts +++ b/src/app/interceptors/error-handling/error-handling.interceptor.ts @@ -1,46 +1,45 @@ -import { - HttpErrorResponse, HttpEvent, HttpHandler, HttpInterceptor, HttpRequest, -} from '@angular/common/http'; -import { - Inject, Injectable, InjectionToken, Optional, -} from '@angular/core'; +import { HttpErrorResponse, HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http'; +import { Inject, Injectable, InjectionToken, Optional } from '@angular/core'; import { Router } from '@angular/router'; import { Observable, catchError, throwError } from 'rxjs'; export const HTTP_RESPONSE_REDIRECT = new InjectionToken('HttpResponseRedirectConfig'); export interface HttpResponseRedirectConfig { - /** - * The status codes to check for. - */ - httpStatusRedirect?: Array; - /** - * The route to redirect to - */ - redirectTo?: string; + /** + * The status codes to check for. + */ + httpStatusRedirect?: Array; + /** + * The route to redirect to + */ + redirectTo?: string; } type InternalConfig = Required>; @Injectable() export class ErrorInterceptor implements HttpInterceptor { - config: InternalConfig; + config: InternalConfig; - private readonly defaultConfig = { httpStatusRedirect: [500], redirectTo: 'error' }; + private readonly defaultConfig = { httpStatusRedirect: [500], redirectTo: 'error' }; - constructor(private router: Router, @Optional() @Inject(HTTP_RESPONSE_REDIRECT) private redirectConfig: HttpResponseRedirectConfig) { - this.config = { ...this.defaultConfig, ...this.redirectConfig }; - } + constructor( + private router: Router, + @Optional() @Inject(HTTP_RESPONSE_REDIRECT) private redirectConfig: HttpResponseRedirectConfig + ) { + this.config = { ...this.defaultConfig, ...this.redirectConfig }; + } - intercept(request: HttpRequest, next: HttpHandler): Observable> { - return next.handle(request).pipe( - catchError((error) => { - if (error instanceof HttpErrorResponse) { - if (this.config.httpStatusRedirect.includes(error.status)) { - void this.router.navigateByUrl(this.config.redirectTo); - } - } - return throwError(() => error); - }), - ); - } + intercept(request: HttpRequest, next: HttpHandler): Observable> { + return next.handle(request).pipe( + catchError((error) => { + if (error instanceof HttpErrorResponse) { + if (this.config.httpStatusRedirect.includes(error.status)) { + void this.router.navigateByUrl(this.config.redirectTo); + } + } + return throwError(() => error); + }) + ); + } } diff --git a/src/app/interceptors/error-handling/error-handling.module.ts b/src/app/interceptors/error-handling/error-handling.module.ts index 4ef9ebbb37..1e1d27ebdb 100644 --- a/src/app/interceptors/error-handling/error-handling.module.ts +++ b/src/app/interceptors/error-handling/error-handling.module.ts @@ -1,32 +1,30 @@ import { CommonModule } from '@angular/common'; import { HTTP_INTERCEPTORS } from '@angular/common/http'; -import { - ModuleWithProviders, NgModule, Optional, SkipSelf, -} from '@angular/core'; -import { ErrorInterceptor, HttpResponseRedirectConfig, HTTP_RESPONSE_REDIRECT } from './error-handling.interceptor'; +import { ModuleWithProviders, NgModule, Optional, SkipSelf } from '@angular/core'; +import { ErrorInterceptor, HTTP_RESPONSE_REDIRECT, HttpResponseRedirectConfig } from './error-handling.interceptor'; @NgModule({ - declarations: [], - imports: [CommonModule], - providers: [ - { - provide: HTTP_INTERCEPTORS, - useClass: ErrorInterceptor, - multi: true, - }, - ], + declarations: [], + imports: [CommonModule], + providers: [ + { + provide: HTTP_INTERCEPTORS, + useClass: ErrorInterceptor, + multi: true, + }, + ], }) export class ErrorInterceptorModule { - constructor(@Optional() @SkipSelf() parentModule?: ErrorInterceptorModule) { - if (parentModule) { - throw new Error('ErrorInterceptorModule is already loaded. Import it in the AppModule only'); - } - } + constructor(@Optional() @SkipSelf() parentModule?: ErrorInterceptorModule) { + if (parentModule) { + throw new Error('ErrorInterceptorModule is already loaded. Import it in the AppModule only'); + } + } - static forRoot(config?: HttpResponseRedirectConfig): ModuleWithProviders { - return { - ngModule: ErrorInterceptorModule, - providers: [{ provide: HTTP_RESPONSE_REDIRECT, useValue: config }], - }; - } + static forRoot(config?: HttpResponseRedirectConfig): ModuleWithProviders { + return { + ngModule: ErrorInterceptorModule, + providers: [{ provide: HTTP_RESPONSE_REDIRECT, useValue: config }], + }; + } } diff --git a/src/app/interceptors/interceptor.module.ts b/src/app/interceptors/interceptor.module.ts index b48d89a2ef..09261685de 100644 --- a/src/app/interceptors/interceptor.module.ts +++ b/src/app/interceptors/interceptor.module.ts @@ -4,13 +4,17 @@ import { DelayedRetryModule } from './delayed-retry/delayed-retry.module'; import { ErrorInterceptorModule } from './error-handling/error-handling.module'; @NgModule({ - declarations: [], - imports: [ - CommonModule, - DelayedRetryModule.forRoot({ - count: 3, delay: 2000, httpStatusRetry: [504], backoff: true, whiteList: ['document-retrieval'], - }), - ErrorInterceptorModule.forRoot({ httpStatusRedirect: [500], redirectTo: 'error' }), - ], + declarations: [], + imports: [ + CommonModule, + DelayedRetryModule.forRoot({ + count: 3, + delay: 2000, + httpStatusRetry: [504], + backoff: true, + whiteList: ['document-retrieval'], + }), + ErrorInterceptorModule.forRoot({ httpStatusRedirect: [500], redirectTo: 'error' }), + ], }) export class InterceptorModule {} diff --git a/src/app/models/adr.enum.ts b/src/app/models/adr.enum.ts index 39dd3551fd..bb6e8ca48c 100644 --- a/src/app/models/adr.enum.ts +++ b/src/app/models/adr.enum.ts @@ -1,9 +1,9 @@ export enum TC2Types { - INITIAL = 'initial', + INITIAL = 'initial', } export enum TC3Types { - INTERMEDIATE = 'intermediate', - PERIODIC = 'periodic', - EXCEPTIONAL = 'exceptional', + INTERMEDIATE = 'intermediate', + PERIODIC = 'periodic', + EXCEPTIONAL = 'exceptional', } diff --git a/src/app/models/body-type-enum.ts b/src/app/models/body-type-enum.ts index 9c60d54732..a575d58949 100644 --- a/src/app/models/body-type-enum.ts +++ b/src/app/models/body-type-enum.ts @@ -1,112 +1,112 @@ // The types and codes need to be lowercase for the API. export enum BodyTypeDescription { - ARTIC = 'artic', - ARTICULATED = 'articulated', - BOX = 'box', - CAR_TRANSPORTER = 'car transporter', - CONCRETE_MIXER = 'concrete mixer', - CURTAINSIDER = 'curtainsider', - DOUBLE_DECKER = 'double decker', - FLAT = 'flat', - LIVESTOCK_CARRIER = 'livestock carrier', - LOW_LOADER = 'low loader', - MINI_BUS = 'mini bus', - OTHER = 'other', - OTHER_TANKER = 'other tanker', - PETROL_OR_OIL_TANKER = 'petrol/oil tanker', - REFUSE = 'refuse', - REFRIGERATED = 'refrigerated', - SINGLE_DECKER = 'single decker', - SKELETAL = 'skeletal', - SKIP_LOADER = 'skip loader', - TIPPER = 'tipper', - TRACTOR = 'tractor', + ARTIC = 'artic', + ARTICULATED = 'articulated', + BOX = 'box', + CAR_TRANSPORTER = 'car transporter', + CONCRETE_MIXER = 'concrete mixer', + CURTAINSIDER = 'curtainsider', + DOUBLE_DECKER = 'double decker', + FLAT = 'flat', + LIVESTOCK_CARRIER = 'livestock carrier', + LOW_LOADER = 'low loader', + MINI_BUS = 'mini bus', + OTHER = 'other', + OTHER_TANKER = 'other tanker', + PETROL_OR_OIL_TANKER = 'petrol/oil tanker', + REFUSE = 'refuse', + REFRIGERATED = 'refrigerated', + SINGLE_DECKER = 'single decker', + SKELETAL = 'skeletal', + SKIP_LOADER = 'skip loader', + TIPPER = 'tipper', + TRACTOR = 'tractor', } export enum BodyTypeCode { - A = 'a', - B = 'b', - C = 'c', - D = 'd', - E = 'e', - F = 'f', - I = 'i', - K = 'k', - L = 'l', - M = 'm', - O = 'o', - P = 'p', - R = 'r', - S = 's', - T = 't', - U = 'u', - X = 'x', - Y = 'y', + A = 'a', + B = 'b', + C = 'c', + D = 'd', + E = 'e', + F = 'f', + I = 'i', + K = 'k', + L = 'l', + M = 'm', + O = 'o', + P = 'p', + R = 'r', + S = 's', + T = 't', + U = 'u', + X = 'x', + Y = 'y', } const commonBodyTypeCodeMap = new Map([ - [BodyTypeCode.B, BodyTypeDescription.BOX], - [BodyTypeCode.C, BodyTypeDescription.REFRIGERATED], - [BodyTypeCode.D, BodyTypeDescription.DOUBLE_DECKER], - [BodyTypeCode.E, BodyTypeDescription.CURTAINSIDER], - [BodyTypeCode.F, BodyTypeDescription.FLAT], - [BodyTypeCode.I, BodyTypeDescription.LIVESTOCK_CARRIER], - [BodyTypeCode.K, BodyTypeDescription.SKELETAL], - [BodyTypeCode.O, BodyTypeDescription.OTHER_TANKER], - [BodyTypeCode.P, BodyTypeDescription.PETROL_OR_OIL_TANKER], - [BodyTypeCode.S, BodyTypeDescription.SKIP_LOADER], - [BodyTypeCode.T, BodyTypeDescription.TIPPER], - [BodyTypeCode.X, BodyTypeDescription.OTHER], - [BodyTypeCode.Y, BodyTypeDescription.CAR_TRANSPORTER], + [BodyTypeCode.B, BodyTypeDescription.BOX], + [BodyTypeCode.C, BodyTypeDescription.REFRIGERATED], + [BodyTypeCode.D, BodyTypeDescription.DOUBLE_DECKER], + [BodyTypeCode.E, BodyTypeDescription.CURTAINSIDER], + [BodyTypeCode.F, BodyTypeDescription.FLAT], + [BodyTypeCode.I, BodyTypeDescription.LIVESTOCK_CARRIER], + [BodyTypeCode.K, BodyTypeDescription.SKELETAL], + [BodyTypeCode.O, BodyTypeDescription.OTHER_TANKER], + [BodyTypeCode.P, BodyTypeDescription.PETROL_OR_OIL_TANKER], + [BodyTypeCode.S, BodyTypeDescription.SKIP_LOADER], + [BodyTypeCode.T, BodyTypeDescription.TIPPER], + [BodyTypeCode.X, BodyTypeDescription.OTHER], + [BodyTypeCode.Y, BodyTypeDescription.CAR_TRANSPORTER], ]); const psvBodyTypeCodeMap = new Map([ - [BodyTypeCode.A, BodyTypeDescription.ARTICULATED], - [BodyTypeCode.D, BodyTypeDescription.DOUBLE_DECKER], - [BodyTypeCode.M, BodyTypeDescription.MINI_BUS], - [BodyTypeCode.S, BodyTypeDescription.SINGLE_DECKER], - [BodyTypeCode.O, BodyTypeDescription.OTHER], + [BodyTypeCode.A, BodyTypeDescription.ARTICULATED], + [BodyTypeCode.D, BodyTypeDescription.DOUBLE_DECKER], + [BodyTypeCode.M, BodyTypeDescription.MINI_BUS], + [BodyTypeCode.S, BodyTypeDescription.SINGLE_DECKER], + [BodyTypeCode.O, BodyTypeDescription.OTHER], ]); const hgvBodyTypeCodeMap = new Map([ - [BodyTypeCode.B, BodyTypeDescription.BOX], - [BodyTypeCode.C, BodyTypeDescription.REFRIGERATED], - [BodyTypeCode.E, BodyTypeDescription.CURTAINSIDER], - [BodyTypeCode.F, BodyTypeDescription.FLAT], - [BodyTypeCode.I, BodyTypeDescription.LIVESTOCK_CARRIER], - [BodyTypeCode.K, BodyTypeDescription.SKELETAL], - [BodyTypeCode.O, BodyTypeDescription.OTHER_TANKER], - [BodyTypeCode.P, BodyTypeDescription.PETROL_OR_OIL_TANKER], - [BodyTypeCode.S, BodyTypeDescription.SKIP_LOADER], - [BodyTypeCode.T, BodyTypeDescription.TIPPER], - [BodyTypeCode.X, BodyTypeDescription.OTHER], - [BodyTypeCode.Y, BodyTypeDescription.CAR_TRANSPORTER], - [BodyTypeCode.A, BodyTypeDescription.TRACTOR], - [BodyTypeCode.L, BodyTypeDescription.LOW_LOADER], - [BodyTypeCode.M, BodyTypeDescription.CONCRETE_MIXER], - [BodyTypeCode.R, BodyTypeDescription.REFUSE], + [BodyTypeCode.B, BodyTypeDescription.BOX], + [BodyTypeCode.C, BodyTypeDescription.REFRIGERATED], + [BodyTypeCode.E, BodyTypeDescription.CURTAINSIDER], + [BodyTypeCode.F, BodyTypeDescription.FLAT], + [BodyTypeCode.I, BodyTypeDescription.LIVESTOCK_CARRIER], + [BodyTypeCode.K, BodyTypeDescription.SKELETAL], + [BodyTypeCode.O, BodyTypeDescription.OTHER_TANKER], + [BodyTypeCode.P, BodyTypeDescription.PETROL_OR_OIL_TANKER], + [BodyTypeCode.S, BodyTypeDescription.SKIP_LOADER], + [BodyTypeCode.T, BodyTypeDescription.TIPPER], + [BodyTypeCode.X, BodyTypeDescription.OTHER], + [BodyTypeCode.Y, BodyTypeDescription.CAR_TRANSPORTER], + [BodyTypeCode.A, BodyTypeDescription.TRACTOR], + [BodyTypeCode.L, BodyTypeDescription.LOW_LOADER], + [BodyTypeCode.M, BodyTypeDescription.CONCRETE_MIXER], + [BodyTypeCode.R, BodyTypeDescription.REFUSE], ]); const trlBodyTypeCodeMap = new Map([ - ...commonBodyTypeCodeMap.entries(), - [BodyTypeCode.L, BodyTypeDescription.LOW_LOADER], + ...commonBodyTypeCodeMap.entries(), + [BodyTypeCode.L, BodyTypeDescription.LOW_LOADER], ]); const articulatedHgvBodyTypeCodeMap = new Map([ - [BodyTypeCode.A, BodyTypeDescription.ARTICULATED], + [BodyTypeCode.A, BodyTypeDescription.ARTICULATED], ]); export const vehicleBodyTypeCodeMap = new Map>([ - ['psv', psvBodyTypeCodeMap], - ['rigidHgv', hgvBodyTypeCodeMap], - ['trl', trlBodyTypeCodeMap], - ['articulatedHgv', articulatedHgvBodyTypeCodeMap], + ['psv', psvBodyTypeCodeMap], + ['rigidHgv', hgvBodyTypeCodeMap], + ['trl', trlBodyTypeCodeMap], + ['articulatedHgv', articulatedHgvBodyTypeCodeMap], ]); export const vehicleBodyTypeDescriptionMap = new Map>([ - ['psv', new Map([...psvBodyTypeCodeMap.entries()].map(([k, v]) => [v, k]))], - ['trl', new Map([...trlBodyTypeCodeMap.entries()].map(([k, v]) => [v, k]))], - ['rigidHgv', new Map([...hgvBodyTypeCodeMap.entries()].map(([k, v]) => [v, k]))], - ['articulatedHgv', new Map([...articulatedHgvBodyTypeCodeMap.entries()].map(([k, v]) => [v, k]))], + ['psv', new Map([...psvBodyTypeCodeMap.entries()].map(([k, v]) => [v, k]))], + ['trl', new Map([...trlBodyTypeCodeMap.entries()].map(([k, v]) => [v, k]))], + ['rigidHgv', new Map([...hgvBodyTypeCodeMap.entries()].map(([k, v]) => [v, k]))], + ['articulatedHgv', new Map([...articulatedHgvBodyTypeCodeMap.entries()].map(([k, v]) => [v, k]))], ]); diff --git a/src/app/models/coupling-type-enum.ts b/src/app/models/coupling-type-enum.ts index 7b2c15488e..22d0a4c1e4 100644 --- a/src/app/models/coupling-type-enum.ts +++ b/src/app/models/coupling-type-enum.ts @@ -1,19 +1,19 @@ import { MultiOptions } from '@forms/models/options.model'; export enum CouplingTypeCodeEnum { - F = 'F', - B = 'B', - O = 'O', - A = 'A', - D = 'D', - S = 'S', + F = 'F', + B = 'B', + O = 'O', + A = 'A', + D = 'D', + S = 'S', } export const CouplingTypeOptions: MultiOptions = [ - { label: 'Fifth wheel', value: CouplingTypeCodeEnum.F }, - { label: 'Drawbar', value: CouplingTypeCodeEnum.B }, - { label: 'Other', value: CouplingTypeCodeEnum.O }, - { label: 'Automatic', value: CouplingTypeCodeEnum.A }, - { label: 'Dolly', value: CouplingTypeCodeEnum.D }, - { label: 'Semi', value: CouplingTypeCodeEnum.S }, + { label: 'Fifth wheel', value: CouplingTypeCodeEnum.F }, + { label: 'Drawbar', value: CouplingTypeCodeEnum.B }, + { label: 'Other', value: CouplingTypeCodeEnum.O }, + { label: 'Automatic', value: CouplingTypeCodeEnum.A }, + { label: 'Dolly', value: CouplingTypeCodeEnum.D }, + { label: 'Semi', value: CouplingTypeCodeEnum.S }, ]; diff --git a/src/app/models/defects/additional-information.model.ts b/src/app/models/defects/additional-information.model.ts index 7c97bed775..243eff99ec 100644 --- a/src/app/models/defects/additional-information.model.ts +++ b/src/app/models/defects/additional-information.model.ts @@ -1,12 +1,12 @@ import { Location } from './location.model'; export interface AdditionalInformation { - hgv?: AdditionalInfoSection; - psv?: AdditionalInfoSection; - trl?: AdditionalInfoSection; + hgv?: AdditionalInfoSection; + psv?: AdditionalInfoSection; + trl?: AdditionalInfoSection; } export interface AdditionalInfoSection { - location: Location; - notes: boolean; + location: Location; + notes: boolean; } diff --git a/src/app/models/defects/defect.model.ts b/src/app/models/defects/defect.model.ts index 2723869e44..a47912fc10 100644 --- a/src/app/models/defects/defect.model.ts +++ b/src/app/models/defects/defect.model.ts @@ -3,9 +3,9 @@ import { AdditionalInformation } from './additional-information.model'; import { Item } from './item.model'; export interface Defect { - additionalInfo: AdditionalInformation; - forVehicleType: VehicleTypes[]; - imDescription: string; - imNumber: number; - items: Item[]; + additionalInfo: AdditionalInformation; + forVehicleType: VehicleTypes[]; + imDescription: string; + imNumber: number; + items: Item[]; } diff --git a/src/app/models/defects/deficiency-category.enum.ts b/src/app/models/defects/deficiency-category.enum.ts index a4bbbb6693..f2e83fee80 100644 --- a/src/app/models/defects/deficiency-category.enum.ts +++ b/src/app/models/defects/deficiency-category.enum.ts @@ -1,6 +1,6 @@ export enum deficiencyCategory { - Advisory = 'advisory', - Dangerous = 'dangerous', - Major = 'major', - Minor = 'minor', + Advisory = 'advisory', + Dangerous = 'dangerous', + Major = 'major', + Minor = 'minor', } diff --git a/src/app/models/defects/deficiency.model.ts b/src/app/models/defects/deficiency.model.ts index ddb251c26e..eba54a991b 100644 --- a/src/app/models/defects/deficiency.model.ts +++ b/src/app/models/defects/deficiency.model.ts @@ -2,11 +2,11 @@ import { VehicleTypes } from '@models/vehicle-tech-record.model'; import { deficiencyCategory } from './deficiency-category.enum'; export interface Deficiency { - deficiencyCategory: deficiencyCategory; - deficiencyId: string; - deficiencySubId: string; - deficiencyText: string; - forVehicleType: VehicleTypes[]; - ref: string; - stdForProhibition: boolean; + deficiencyCategory: deficiencyCategory; + deficiencyId: string; + deficiencySubId: string; + deficiencyText: string; + forVehicleType: VehicleTypes[]; + ref: string; + stdForProhibition: boolean; } diff --git a/src/app/models/defects/item.model.ts b/src/app/models/defects/item.model.ts index 69f1bfe8fd..69b877a1cd 100644 --- a/src/app/models/defects/item.model.ts +++ b/src/app/models/defects/item.model.ts @@ -2,8 +2,8 @@ import { VehicleTypes } from '@models/vehicle-tech-record.model'; import { Deficiency } from './deficiency.model'; export interface Item { - deficiencies: Deficiency[]; - forVehicleType: VehicleTypes[]; - itemDescription: string; - itemNumber: number; + deficiencies: Deficiency[]; + forVehicleType: VehicleTypes[]; + itemDescription: string; + itemNumber: number; } diff --git a/src/app/models/defects/location.model.ts b/src/app/models/defects/location.model.ts index 1bcc2c7253..511101c2ec 100644 --- a/src/app/models/defects/location.model.ts +++ b/src/app/models/defects/location.model.ts @@ -1,34 +1,34 @@ export interface Location { - axleNumber?: number[]; - horizontal?: Horizontal[]; - lateral?: Lateral[]; - longitudinal?: Longitudinal[]; - rowNumber?: number[]; - seatNumber?: number[]; - vertical?: Vertical[]; + axleNumber?: number[]; + horizontal?: Horizontal[]; + lateral?: Lateral[]; + longitudinal?: Longitudinal[]; + rowNumber?: number[]; + seatNumber?: number[]; + vertical?: Vertical[]; } export type Vertical = 'upper' | 'lower'; export const VerticalEnum = { - Upper: 'upper' as Vertical, - Lower: 'lower' as Vertical, + Upper: 'upper' as Vertical, + Lower: 'lower' as Vertical, }; export type Horizontal = 'inner' | 'outer'; export const HorizontalEnum = { - Inner: 'inner' as Horizontal, - Outer: 'outer' as Horizontal, + Inner: 'inner' as Horizontal, + Outer: 'outer' as Horizontal, }; export type Lateral = 'nearside' | 'centre' | 'offside'; export const LateralEnum = { - Nearside: 'nearside' as Lateral, - Centre: 'centre' as Lateral, - Offside: 'offside' as Lateral, + Nearside: 'nearside' as Lateral, + Centre: 'centre' as Lateral, + Offside: 'offside' as Lateral, }; export type Longitudinal = 'front' | 'rear'; export const LongitudinalEnum = { - Front: 'front' as Longitudinal, - Rear: 'rear' as Longitudinal, + Front: 'front' as Longitudinal, + Rear: 'rear' as Longitudinal, }; diff --git a/src/app/models/reference-data.model.ts b/src/app/models/reference-data.model.ts index 891fd5071d..429c670e8b 100644 --- a/src/app/models/reference-data.model.ts +++ b/src/app/models/reference-data.model.ts @@ -1,91 +1,91 @@ export enum ReferenceDataResourceType { - Brakes = 'BRAKES', - CountryOfRegistration = 'COUNTRY_OF_REGISTRATION', - HgvMake = 'HGV_MAKE', - PsvMake = 'PSV_MAKE', - ReasonsForAbandoningHgv = 'REASONS_FOR_ABANDONING_HGV', - ReasonsForAbandoningTrl = 'REASONS_FOR_ABANDONING_TRL', - ReasonsForAbandoningPsv = 'REASONS_FOR_ABANDONING_PSV', - ReferenceDataAdminType = 'REFERENCE_DATA_ADMIN_TYPE', - SpecialistReasonsForAbandoning = 'SPECIALIST_REASONS_FOR_ABANDONING', - TirReasonsForAbandoning = 'TIR_REASONS_FOR_ABANDONING', - TrlMake = 'TRL_MAKE', - Tyres = 'TYRES', - User = 'USER', - TyreLoadIndex = 'TYRE_LOAD_INDEX', + Brakes = 'BRAKES', + CountryOfRegistration = 'COUNTRY_OF_REGISTRATION', + HgvMake = 'HGV_MAKE', + PsvMake = 'PSV_MAKE', + ReasonsForAbandoningHgv = 'REASONS_FOR_ABANDONING_HGV', + ReasonsForAbandoningTrl = 'REASONS_FOR_ABANDONING_TRL', + ReasonsForAbandoningPsv = 'REASONS_FOR_ABANDONING_PSV', + ReferenceDataAdminType = 'REFERENCE_DATA_ADMIN_TYPE', + SpecialistReasonsForAbandoning = 'SPECIALIST_REASONS_FOR_ABANDONING', + TirReasonsForAbandoning = 'TIR_REASONS_FOR_ABANDONING', + TrlMake = 'TRL_MAKE', + Tyres = 'TYRES', + User = 'USER', + TyreLoadIndex = 'TYRE_LOAD_INDEX', } type AuditTypes = `${keyof Record}#AUDIT`; export type ReferenceDataResourceTypeAudit = `${keyof Record}` | AuditTypes; export interface ReferenceDataAdminColumn { - name: string; - heading: string; - order: number; + name: string; + heading: string; + order: number; } export interface ReferenceDataModelBase { - resourceType: ReferenceDataResourceType; - resourceKey: string | number; - description?: string; - createdAt?: string; - createdName?: string; - createdId?: string; - reason?: string; - label?: string; + resourceType: ReferenceDataResourceType; + resourceKey: string | number; + description?: string; + createdAt?: string; + createdName?: string; + createdId?: string; + reason?: string; + label?: string; } export interface PsvMake extends ReferenceDataModelBase { - dtpNumber: string; - psvChassisMake: string; - psvChassisModel: string; - psvBodyMake: string; - psvBodyType: string; + dtpNumber: string; + psvChassisMake: string; + psvChassisModel: string; + psvBodyMake: string; + psvBodyType: string; } export interface BodyModel extends ReferenceDataModelBase { - bodyMake: string; + bodyMake: string; } export interface Brake extends ReferenceDataModelBase { - service: string; - secondary: string; - parking: string; + service: string; + secondary: string; + parking: string; } export interface ReferenceDataTyre extends ReferenceDataModelBase { - code: string; - loadIndexSingleLoad?: string; - tyreSize: string; - dateTimeStamp: string; - userId: string; - loadIndexTwinLoad?: string; - plyRating: string; - axleLoadSingle?: string; - axleLoadDouble?: string; + code: string; + loadIndexSingleLoad?: string; + tyreSize: string; + dateTimeStamp: string; + userId: string; + loadIndexTwinLoad?: string; + plyRating: string; + axleLoadSingle?: string; + axleLoadDouble?: string; } export interface ReferenceDataTyreLoadIndex extends ReferenceDataModelBase { - loadIndex: string; + loadIndex: string; } export interface User extends ReferenceDataModelBase { - name: string; - email: string; + name: string; + email: string; } export interface ReferenceDataAdminType extends ReferenceDataModelBase { - code?: string; - dtpNumber?: string; - loadIndexSingleLoad?: string; - loadIndexTwinLoad?: string; - parking?: string; - plyRating?: string; - psvBodyMake?: string; - psvBodyType?: string; - psvChassisMake?: string; - psvChassisModel?: string; - secondary?: string; - service?: string; - tyreSize?: string; + code?: string; + dtpNumber?: string; + loadIndexSingleLoad?: string; + loadIndexTwinLoad?: string; + parking?: string; + plyRating?: string; + psvBodyMake?: string; + psvBodyType?: string; + psvChassisMake?: string; + psvChassisModel?: string; + secondary?: string; + service?: string; + tyreSize?: string; } diff --git a/src/app/models/roles.enum.ts b/src/app/models/roles.enum.ts index df3fff91a6..4477fa9aa7 100644 --- a/src/app/models/roles.enum.ts +++ b/src/app/models/roles.enum.ts @@ -1,14 +1,14 @@ export enum Roles { - Admin = 'CVSFullAccess', - TechRecordView = 'CVSFullAccess,TechRecord.View', - TechRecordCreate = 'CVSFullAccess,TechRecord.Create', - TechRecordAmend = 'CVSFullAccess,TechRecord.Amend', - TechRecordArchive = 'CVSFullAccess,TechRecord.Archive', - TechRecordUnarchive = 'CVSFullAccess,TechRecord.Unarchive', - TestResultView = 'CVSFullAccess,TestResult.View', - TestResultAmend = 'CVSFullAccess,TestResult.Amend', - TestResultCreateContingency = 'CVSFullAccess,TestResult.CreateContingency', - TestResultCreateDeskAssessment = 'CVSFullAccess,TestResult.CreateDeskBased', - ReferenceDataView = 'CVSFullAccess,ReferenceData.View', - ReferenceDataAmend = 'CVSFullAccess,ReferenceData.Amend', + Admin = 'CVSFullAccess', + TechRecordView = 'CVSFullAccess,TechRecord.View', + TechRecordCreate = 'CVSFullAccess,TechRecord.Create', + TechRecordAmend = 'CVSFullAccess,TechRecord.Amend', + TechRecordArchive = 'CVSFullAccess,TechRecord.Archive', + TechRecordUnarchive = 'CVSFullAccess,TechRecord.Unarchive', + TestResultView = 'CVSFullAccess,TestResult.View', + TestResultAmend = 'CVSFullAccess,TestResult.Amend', + TestResultCreateContingency = 'CVSFullAccess,TestResult.CreateContingency', + TestResultCreateDeskAssessment = 'CVSFullAccess,TestResult.CreateDeskBased', + ReferenceDataView = 'CVSFullAccess,ReferenceData.View', + ReferenceDataAmend = 'CVSFullAccess,ReferenceData.Amend', } diff --git a/src/app/models/routes.enum.ts b/src/app/models/routes.enum.ts index 1df9e718a1..b059970a72 100644 --- a/src/app/models/routes.enum.ts +++ b/src/app/models/routes.enum.ts @@ -1,85 +1,85 @@ export enum RootRoutes { - ROOT = '', - SEARCH_TECHNICAL_RECORD = 'search', - CREATE_TECHNICAL_RECORD = 'create', - BATCH_CREATE_TECHNICAL_RECORD = 'create-batch', - CURRENT_TEST_RESULT = 'test-records/:systemNumber/test-result/:testResultId/:testNumber', - CURRENT_TECH_RECORD = 'tech-records/:systemNumber/:createdTimestamp', - REFERENCE_DATA = 'reference-data', - FEATURE_TOGGLE = 'feature-toggle', - ERROR = 'error', - WILDCARD = '**', + ROOT = '', + SEARCH_TECHNICAL_RECORD = 'search', + CREATE_TECHNICAL_RECORD = 'create', + BATCH_CREATE_TECHNICAL_RECORD = 'create-batch', + CURRENT_TEST_RESULT = 'test-records/:systemNumber/test-result/:testResultId/:testNumber', + CURRENT_TECH_RECORD = 'tech-records/:systemNumber/:createdTimestamp', + REFERENCE_DATA = 'reference-data', + FEATURE_TOGGLE = 'feature-toggle', + ERROR = 'error', + WILDCARD = '**', } export enum SearchRoutes { - SEARCH_RESULT = 'results', + SEARCH_RESULT = 'results', } export enum TechRecordRoutes { - CORRECT_ERROR = 'correcting-an-error', - NOTIFIABLE_ALTERATION_NEEDED = 'notifiable-alteration-needed', - CHANGE_VIN = 'change-vin', - CHANGE_VRM = 'change-vrm', - REASON_TO_CHANGE_VRM = 'change-vrm/:reason', - GENERATE_PLATE = 'generate-plate', - GENERATE_LETTER = 'generate-letter', - AMEND_REASON = 'amend-reason', - CHANGE_STATUS = 'change-status', - UNARCHIVE_RECORD = 'unarchive-record', - CHANGE_VEHICLE_TYPE = 'change-vehicle-type', - CHANGE_VTA_VISIBILITY = 'change-vta-visibility', - CORRECT_ERROR_TYRE_SEARCH = 'correcting-an-error/tyre-search/:axleNumber', - CORRECT_ERROR_CHANGE_SUMMARY = 'correcting-an-error/change-summary', - CORRECT_ERROR_EDIT_ADDITIONAL_EXAMINER_NOTE = 'correcting-an-error/edit-additional-examiner-note/:examinerNoteIndex', - NOTIFIABLE_ALTERATION_NEEDED_CHANGE_SUMMARY = 'notifiable-alteration-needed/change-summary', - NOTIFIABLE_ALTERATION_NEEDED_TYRE_SEARCH = 'notifiable-alteration-needed/tyre-search/:axleNumber', - NOTIFIABLE_ALTERNATION_NEEDED_EDIT_ADDITIONAL_EXAMINER_NOTE = 'notifiable-alteration-needed/edit-additional-examiner-note/:examinerNoteIndex', - TEST_RECORDS = 'test-records/test-result/:testResultId/:testNumber', - CREATE_TEST = 'test-records/create-test', - ADR_CERTIFICATE = 'adr-certificate', + CORRECT_ERROR = 'correcting-an-error', + NOTIFIABLE_ALTERATION_NEEDED = 'notifiable-alteration-needed', + CHANGE_VIN = 'change-vin', + CHANGE_VRM = 'change-vrm', + REASON_TO_CHANGE_VRM = 'change-vrm/:reason', + GENERATE_PLATE = 'generate-plate', + GENERATE_LETTER = 'generate-letter', + AMEND_REASON = 'amend-reason', + CHANGE_STATUS = 'change-status', + UNARCHIVE_RECORD = 'unarchive-record', + CHANGE_VEHICLE_TYPE = 'change-vehicle-type', + CHANGE_VTA_VISIBILITY = 'change-vta-visibility', + CORRECT_ERROR_TYRE_SEARCH = 'correcting-an-error/tyre-search/:axleNumber', + CORRECT_ERROR_CHANGE_SUMMARY = 'correcting-an-error/change-summary', + CORRECT_ERROR_EDIT_ADDITIONAL_EXAMINER_NOTE = 'correcting-an-error/edit-additional-examiner-note/:examinerNoteIndex', + NOTIFIABLE_ALTERATION_NEEDED_CHANGE_SUMMARY = 'notifiable-alteration-needed/change-summary', + NOTIFIABLE_ALTERATION_NEEDED_TYRE_SEARCH = 'notifiable-alteration-needed/tyre-search/:axleNumber', + NOTIFIABLE_ALTERNATION_NEEDED_EDIT_ADDITIONAL_EXAMINER_NOTE = 'notifiable-alteration-needed/edit-additional-examiner-note/:examinerNoteIndex', + TEST_RECORDS = 'test-records/test-result/:testResultId/:testNumber', + CREATE_TEST = 'test-records/create-test', + ADR_CERTIFICATE = 'adr-certificate', } export enum TechRecordCreateRoutes { - NEW_RECORD_DETAILS = 'new-record-details', - TYRE_SEARCH = 'tyre-search/:axleNumber', + NEW_RECORD_DETAILS = 'new-record-details', + TYRE_SEARCH = 'tyre-search/:axleNumber', } export enum TechRecordCreateBatchRoutes { - RECORD = ':vehicleType', - DETAILS = 'details', - BATCH_RESULT = 'batch-results', - TYRE_SEARCH = 'tyre-search/:axleNumber', + RECORD = ':vehicleType', + DETAILS = 'details', + BATCH_RESULT = 'batch-results', + TYRE_SEARCH = 'tyre-search/:axleNumber', } export enum ReferenceDataRoutes { - TYPE = ':type', - CREATE = 'create', - DELETED_ITEMS = 'deleted-items', - KEY = ':key', - DELETE = ':key/delete', + TYPE = ':type', + CREATE = 'create', + DELETED_ITEMS = 'deleted-items', + KEY = ':key', + DELETE = ':key/delete', } export enum TestRecordAmendRoutes { - AMEND_TEST = 'amend-test', - INCORRECT_TEST_TYPE = 'incorrect-test-type', - TEST_DETAILS = 'amend-test-details', - DEFECT = 'defect/:defectIndex', - SELECT_DEFECT = 'selectDefect', - SELECT_DEFECT_REFERENCE = ':ref', - AMENDED_TEST = 'amended/:createdAt', - CANCEL_TEST = 'cancel-test', - REQUIRED_STANDARD = 'requiredStandard/:requiredStandardIndex', - SELECT_REQUIRED_STANDARD = 'selectRequiredStandard', - REQUIRED_STANDARD_REF = ':inspectionType/:ref', + AMEND_TEST = 'amend-test', + INCORRECT_TEST_TYPE = 'incorrect-test-type', + TEST_DETAILS = 'amend-test-details', + DEFECT = 'defect/:defectIndex', + SELECT_DEFECT = 'selectDefect', + SELECT_DEFECT_REFERENCE = ':ref', + AMENDED_TEST = 'amended/:createdAt', + CANCEL_TEST = 'cancel-test', + REQUIRED_STANDARD = 'requiredStandard/:requiredStandardIndex', + SELECT_REQUIRED_STANDARD = 'selectRequiredStandard', + REQUIRED_STANDARD_REF = ':inspectionType/:ref', } export enum TestRecordCreateRoutes { - TYPE = 'type', - TEST_DETAILS = 'test-details', - DEFECT = 'defect/:defectIndex', - SELECT_DEFECT = 'selectDefect', - SELECT_DEFECT_REF = ':ref', - REQUIRED_STANDARD = 'requiredStandard/:requiredStandardIndex', - SELECT_REQUIRED_STANDARD = 'selectRequiredStandard', - REQUIRED_STANDARD_REF = ':inspectionType/:ref', + TYPE = 'type', + TEST_DETAILS = 'test-details', + DEFECT = 'defect/:defectIndex', + SELECT_DEFECT = 'selectDefect', + SELECT_DEFECT_REF = ':ref', + REQUIRED_STANDARD = 'requiredStandard/:requiredStandardIndex', + SELECT_REQUIRED_STANDARD = 'selectRequiredStandard', + REQUIRED_STANDARD_REF = ':inspectionType/:ref', } diff --git a/src/app/models/search-types-enum.ts b/src/app/models/search-types-enum.ts index e4168ec8e8..056c4857b4 100644 --- a/src/app/models/search-types-enum.ts +++ b/src/app/models/search-types-enum.ts @@ -1,8 +1,8 @@ export enum SEARCH_TYPES { - VIN = 'vin', - PARTIAL_VIN = 'partialVin', - VRM = 'primaryVrm', - TRAILER_ID = 'trailerId', - SYSTEM_NUMBER = 'systemNumber', - ALL = 'all', + VIN = 'vin', + PARTIAL_VIN = 'partialVin', + VRM = 'primaryVrm', + TRAILER_ID = 'trailerId', + SYSTEM_NUMBER = 'systemNumber', + ALL = 'all', } diff --git a/src/app/models/tech-record/tech-record-actions.enum.ts b/src/app/models/tech-record/tech-record-actions.enum.ts index f101c7e5da..22e89b8330 100644 --- a/src/app/models/tech-record/tech-record-actions.enum.ts +++ b/src/app/models/tech-record/tech-record-actions.enum.ts @@ -1,6 +1,6 @@ export enum TechRecordActions { - CURRENT = 'archive', - PROVISIONAL = 'promote,archive', - ARCHIVED = 'unarchive', - NONE = '', + CURRENT = 'archive', + PROVISIONAL = 'promote,archive', + ARCHIVED = 'unarchive', + NONE = '', } diff --git a/src/app/models/test-results/defectAdditionalInformation.ts b/src/app/models/test-results/defectAdditionalInformation.ts index 73eb9ac08c..b2343b98c9 100644 --- a/src/app/models/test-results/defectAdditionalInformation.ts +++ b/src/app/models/test-results/defectAdditionalInformation.ts @@ -12,6 +12,6 @@ import { DefectAdditionalInformationLocation } from './defectAdditionalInformationLocation'; export interface DefectAdditionalInformation { - location?: DefectAdditionalInformationLocation; - notes?: string; + location?: DefectAdditionalInformationLocation; + notes?: string; } diff --git a/src/app/models/test-results/defectAdditionalInformationLocation.ts b/src/app/models/test-results/defectAdditionalInformationLocation.ts index 67898aa66a..771cdd7857 100644 --- a/src/app/models/test-results/defectAdditionalInformationLocation.ts +++ b/src/app/models/test-results/defectAdditionalInformationLocation.ts @@ -11,33 +11,33 @@ */ export interface DefectAdditionalInformationLocation { - vertical?: VerticalEnum; - horizontal?: HorizontalEnum; - lateral?: LateralEnum; - longitudinal?: LongitudinalEnum; - rowNumber?: number; - seatNumber?: number; - axleNumber?: number; + vertical?: VerticalEnum; + horizontal?: HorizontalEnum; + lateral?: LateralEnum; + longitudinal?: LongitudinalEnum; + rowNumber?: number; + seatNumber?: number; + axleNumber?: number; } export type VerticalEnum = 'upper' | 'lower'; export const VerticalTypeEnum = { - Upper: 'upper' as VerticalEnum, - Lower: 'lower' as VerticalEnum, + Upper: 'upper' as VerticalEnum, + Lower: 'lower' as VerticalEnum, }; export type HorizontalEnum = 'inner' | 'outer'; export const HorizontalTypeEnum = { - Inner: 'inner' as HorizontalEnum, - Outer: 'outer' as HorizontalEnum, + Inner: 'inner' as HorizontalEnum, + Outer: 'outer' as HorizontalEnum, }; export type LateralEnum = 'nearside' | 'centre' | 'offside'; export const LateralTypeEnum = { - Nearside: 'nearside' as LateralEnum, - Centre: 'centre' as LateralEnum, - Offside: 'offside' as LateralEnum, + Nearside: 'nearside' as LateralEnum, + Centre: 'centre' as LateralEnum, + Offside: 'offside' as LateralEnum, }; export type LongitudinalEnum = 'front' | 'rear'; export const LongitudinalTypeEnum = { - Front: 'front' as LongitudinalEnum, - Rear: 'rear' as LongitudinalEnum, + Front: 'front' as LongitudinalEnum, + Rear: 'rear' as LongitudinalEnum, }; diff --git a/src/app/models/test-results/test-result-defect.model.ts b/src/app/models/test-results/test-result-defect.model.ts index a3ea0798d3..4b3dd6552d 100644 --- a/src/app/models/test-results/test-result-defect.model.ts +++ b/src/app/models/test-results/test-result-defect.model.ts @@ -1,25 +1,25 @@ import { DefectAdditionalInformation } from './defectAdditionalInformation'; export interface TestResultDefect { - imNumber?: number; - imDescription?: string; - additionalInformation?: DefectAdditionalInformation; - itemNumber?: number; - itemDescription?: string; - deficiencyRef?: string; - deficiencyId?: string; - deficiencySubId?: string; - deficiencyCategory: DeficiencyCategoryStringEnum; - deficiencyText?: string; - stdForProhibition?: boolean; - prs?: boolean; - prohibitionIssued?: boolean; + imNumber?: number; + imDescription?: string; + additionalInformation?: DefectAdditionalInformation; + itemNumber?: number; + itemDescription?: string; + deficiencyRef?: string; + deficiencyId?: string; + deficiencySubId?: string; + deficiencyCategory: DeficiencyCategoryStringEnum; + deficiencyText?: string; + stdForProhibition?: boolean; + prs?: boolean; + prohibitionIssued?: boolean; } export type DeficiencyCategoryStringEnum = 'advisory' | 'dangerous' | 'major' | 'minor'; export const DeficiencyCategoryEnum = { - Advisory: 'advisory' as DeficiencyCategoryStringEnum, - Dangerous: 'dangerous' as DeficiencyCategoryStringEnum, - Major: 'major' as DeficiencyCategoryStringEnum, - Minor: 'minor' as DeficiencyCategoryStringEnum, + Advisory: 'advisory' as DeficiencyCategoryStringEnum, + Dangerous: 'dangerous' as DeficiencyCategoryStringEnum, + Major: 'major' as DeficiencyCategoryStringEnum, + Minor: 'minor' as DeficiencyCategoryStringEnum, }; diff --git a/src/app/models/test-results/test-result-required-standard.model.ts b/src/app/models/test-results/test-result-required-standard.model.ts index f854e956d7..5b83b71e46 100644 --- a/src/app/models/test-results/test-result-required-standard.model.ts +++ b/src/app/models/test-results/test-result-required-standard.model.ts @@ -1,18 +1,18 @@ import { InspectionType } from '@dvsa/cvs-type-definitions/types/required-standards/defects/get'; export interface TestResultRequiredStandard { - sectionNumber: string; - sectionDescription: string; - rsNumber: number; - requiredStandard: string; - refCalculation: string; - additionalInfo: boolean; - inspectionTypes: InspectionType[]; - prs?: boolean; - additionalNotes?: string; + sectionNumber: string; + sectionDescription: string; + rsNumber: number; + requiredStandard: string; + refCalculation: string; + additionalInfo: boolean; + inspectionTypes: InspectionType[]; + prs?: boolean; + additionalNotes?: string; } export enum INSPECTION_TYPE { - NORMAL = 'normal', - BASIC = 'basic', + NORMAL = 'normal', + BASIC = 'basic', } diff --git a/src/app/models/test-results/test-result-status.enum.ts b/src/app/models/test-results/test-result-status.enum.ts index ef320dfc89..09706aa539 100644 --- a/src/app/models/test-results/test-result-status.enum.ts +++ b/src/app/models/test-results/test-result-status.enum.ts @@ -1,4 +1,4 @@ export enum TestResultStatus { - SUBMITTED = 'submitted', - CANCELLED = 'cancelled', + SUBMITTED = 'submitted', + CANCELLED = 'cancelled', } diff --git a/src/app/models/test-results/test-result-view.enum.ts b/src/app/models/test-results/test-result-view.enum.ts index 7a9d993101..87f5f8dc26 100644 --- a/src/app/models/test-results/test-result-view.enum.ts +++ b/src/app/models/test-results/test-result-view.enum.ts @@ -1,6 +1,6 @@ export const TestModeEnum = { - Edit: 'edit', - View: 'view', - Abandon: 'abandon', - Cancel: 'cancel', + Edit: 'edit', + View: 'view', + Abandon: 'abandon', + Cancel: 'cancel', }; diff --git a/src/app/models/test-results/test-result.model.ts b/src/app/models/test-results/test-result.model.ts index 53e4e25526..d07851f747 100644 --- a/src/app/models/test-results/test-result.model.ts +++ b/src/app/models/test-results/test-result.model.ts @@ -12,65 +12,65 @@ import { TestCodes } from './testCodes.enum'; import { TypeOfTest } from './typeOfTest.enum'; export interface TestResultModel { - testResultId: string; + testResultId: string; - systemNumber: string; - vin: string; - vrm?: string; // Mandatory for PSV and HGV, not applicable to TRL + systemNumber: string; + vin: string; + vrm?: string; // Mandatory for PSV and HGV, not applicable to TRL - createdAt?: string; - testStartTimestamp: string | Date; - testEndTimestamp: string | Date; + createdAt?: string; + testStartTimestamp: string | Date; + testEndTimestamp: string | Date; - testTypes: TestType[]; + testTypes: TestType[]; - trailerId: string; - countryOfRegistration: string; - euVehicleCategory: EUVehicleCategory | null; - odometerReading: number; - odometerReadingUnits: OdometerReadingUnits; - preparerName: string; - preparerId: string; + trailerId: string; + countryOfRegistration: string; + euVehicleCategory: EUVehicleCategory | null; + odometerReading: number; + odometerReadingUnits: OdometerReadingUnits; + preparerName: string; + preparerId: string; - testStationName: string; - testStationPNumber: string; - testStationType: TestStationType; - testerName: string; - testerEmailAddress: string; - testerStaffId: string; + testStationName: string; + testStationPNumber: string; + testStationType: TestStationType; + testerName: string; + testerEmailAddress: string; + testerStaffId: string; - reasonForCreation?: string; - reasonForCancellation?: string; - vehicleType: VehicleTypes; - testHistory?: TestResultModel[]; - testStatus?: TestResultStatus; + reasonForCreation?: string; + reasonForCancellation?: string; + vehicleType: VehicleTypes; + testHistory?: TestResultModel[]; + testStatus?: TestResultStatus; - make?: string; - model?: string; - bodyType?: TechRecordBodyType; + make?: string; + model?: string | null; + bodyType?: TechRecordBodyType; - /** - * Applicable only when updating/creating a test from VTM - */ - createdByName?: string; - createdById?: string; - lastUpdatedByName?: string; - typeOfTest?: TypeOfTest; - contingencyTestNumber?: string; - source?: string; - lastUpdatedById?: string; - testVersion?: string | null; - testCode?: TestCodes; + /** + * Applicable only when updating/creating a test from VTM + */ + createdByName?: string; + createdById?: string; + lastUpdatedByName?: string; + typeOfTest?: TypeOfTest; + contingencyTestNumber?: string; + source?: string; + lastUpdatedById?: string; + testVersion?: string | null; + testCode?: TestCodes; - vehicleSize?: VehicleSize; - vehicleConfiguration?: VehicleConfiguration | null; - noOfAxles?: number; - vehicleClass?: VehicleClass; - vehicleSubclass?: Array; - numberOfWheelsDriven?: number; - statusCode?: StatusCodes; - /** - * Used only for TRL - */ - firstUseDate?: string; + vehicleSize?: VehicleSize; + vehicleConfiguration?: VehicleConfiguration | null; + noOfAxles?: number; + vehicleClass?: VehicleClass; + vehicleSubclass?: Array; + numberOfWheelsDriven?: number; + statusCode?: StatusCodes; + /** + * Used only for TRL + */ + firstUseDate?: string; } diff --git a/src/app/models/test-results/testCodes.enum.ts b/src/app/models/test-results/testCodes.enum.ts index df33ea6c66..d7306fb571 100644 --- a/src/app/models/test-results/testCodes.enum.ts +++ b/src/app/models/test-results/testCodes.enum.ts @@ -1,58 +1,58 @@ export enum TestCodes { - AAL = 'aal', - AAS = 'aas', - ADL = 'adl', - WDL = 'wdl', - WDS = 'wds', - WBL = 'wbl', - WBS = 'wbs', - RHL = 'rhl', - RPL = 'rpl', - RPS = 'rps', - WHL = 'whl', - WHS = 'whs', - RGL = 'rgl', - RSL = 'rsl', - RSS = 'rss', - PHL = 'phl', - PHS = 'phs', - P1L = 'p1l', - P1S = 'p1s', - P8L = 'p8l', - P8S = 'p8s', - PML = 'pml', - PMS = 'pms', - P6L = 'p6l', - P6S = 'p6s', - P2L = 'p2l', - P2S = 'p2s', - P3L = 'p3l', - PGL = 'pgl', - PGS = 'pgs', - PRL = 'prl', - PRS = 'prs', - WIS = 'wis', - WIL = 'wil', - WFL = 'wfl', - WFS = 'wfs', - WEL = 'wel', - WES = 'wes', - QAL = 'qal', - QAS = 'qas', - QGL = 'qgl', - QGS = 'qgs', - QDL = 'qdl', - QDS = 'qds', - QCL = 'qcl', - QCS = 'qcs', - QBL = 'qbl', - QBS = 'qbs', - BIF = 'bif', - TEL = 'tel', - TES = 'tes', - NFL = 'nfl', - NFS = 'nfs', - LCP = 'lcp', - LBP = 'lbp', - LNP = 'lnp', + AAL = 'aal', + AAS = 'aas', + ADL = 'adl', + WDL = 'wdl', + WDS = 'wds', + WBL = 'wbl', + WBS = 'wbs', + RHL = 'rhl', + RPL = 'rpl', + RPS = 'rps', + WHL = 'whl', + WHS = 'whs', + RGL = 'rgl', + RSL = 'rsl', + RSS = 'rss', + PHL = 'phl', + PHS = 'phs', + P1L = 'p1l', + P1S = 'p1s', + P8L = 'p8l', + P8S = 'p8s', + PML = 'pml', + PMS = 'pms', + P6L = 'p6l', + P6S = 'p6s', + P2L = 'p2l', + P2S = 'p2s', + P3L = 'p3l', + PGL = 'pgl', + PGS = 'pgs', + PRL = 'prl', + PRS = 'prs', + WIS = 'wis', + WIL = 'wil', + WFL = 'wfl', + WFS = 'wfs', + WEL = 'wel', + WES = 'wes', + QAL = 'qal', + QAS = 'qas', + QGL = 'qgl', + QGS = 'qgs', + QDL = 'qdl', + QDS = 'qds', + QCL = 'qcl', + QCS = 'qcs', + QBL = 'qbl', + QBS = 'qbs', + BIF = 'bif', + TEL = 'tel', + TES = 'tes', + NFL = 'nfl', + NFS = 'nfs', + LCP = 'lcp', + LBP = 'lbp', + LNP = 'lnp', } diff --git a/src/app/models/test-results/typeOfTest.enum.ts b/src/app/models/test-results/typeOfTest.enum.ts index 61b4dc2ce2..9385a349f1 100644 --- a/src/app/models/test-results/typeOfTest.enum.ts +++ b/src/app/models/test-results/typeOfTest.enum.ts @@ -1,4 +1,4 @@ export enum TypeOfTest { - CONTINGENCY = 'contingency', - DESK_BASED = 'desk-based', + CONTINGENCY = 'contingency', + DESK_BASED = 'desk-based', } diff --git a/src/app/models/test-stations/test-station-type.enum.ts b/src/app/models/test-stations/test-station-type.enum.ts index 33b5b3d4bc..658eb2c740 100644 --- a/src/app/models/test-stations/test-station-type.enum.ts +++ b/src/app/models/test-stations/test-station-type.enum.ts @@ -1,6 +1,6 @@ export enum TestStationType { - ATF = 'atf', - GVTS = 'gvts', - HQ = 'hq', - POTF = 'potf', + ATF = 'atf', + GVTS = 'gvts', + HQ = 'hq', + POTF = 'potf', } diff --git a/src/app/models/test-stations/test-station.model.ts b/src/app/models/test-stations/test-station.model.ts index b3dc11a837..eb045d9dba 100644 --- a/src/app/models/test-stations/test-station.model.ts +++ b/src/app/models/test-stations/test-station.model.ts @@ -1,24 +1,24 @@ import { TestStationType } from './test-station-type.enum'; export interface TestStation { - testStationId: string; - testStationAccessNotes?: string; - testStationAddress: string; - testStationContactNumber: number; - testStationEmails: string[]; - testStationGeneralNotes: string; - testStationLatitude?: number; - testStationLongitude?: number; - testStationName: string; - testStationPNumber: string; - testStationPostcode: string; - testStationStatus: TestStationStatuses; - testStationTown: string; - testStationType: TestStationType; + testStationId: string; + testStationAccessNotes?: string; + testStationAddress: string; + testStationContactNumber: number; + testStationEmails: string[]; + testStationGeneralNotes: string; + testStationLatitude?: number; + testStationLongitude?: number; + testStationName: string; + testStationPNumber: string; + testStationPostcode: string; + testStationStatus: TestStationStatuses; + testStationTown: string; + testStationType: TestStationType; } export enum TestStationStatuses { - active = 'active', - inactive = 'inactive', - pending = 'pending', + active = 'active', + inactive = 'inactive', + pending = 'pending', } diff --git a/src/app/models/test-types/emissions.enum.ts b/src/app/models/test-types/emissions.enum.ts index fab454e6db..817457fb24 100644 --- a/src/app/models/test-types/emissions.enum.ts +++ b/src/app/models/test-types/emissions.enum.ts @@ -1,37 +1,37 @@ export enum FuelType { - Diesel = 'diesel', - GasCng = 'gas-cng', - GasLng = 'gas-lng', - GasLpg = 'gas-lpg', - FuelCell = 'fuel cell', - Petrol = 'petrol', - FullElectric = 'full electric', + Diesel = 'diesel', + GasCng = 'gas-cng', + GasLng = 'gas-lng', + GasLpg = 'gas-lpg', + FuelCell = 'fuel cell', + Petrol = 'petrol', + FullElectric = 'full electric', } export enum EmissionStandard { - EuroIVPM = '0.03 g/kWh Euro IV PM', - Euro3 = 'Euro 3', - Euro4 = 'Euro 4', - Euro5 = 'Euro 5', - Euro6 = 'Euro 6', - EuroV = 'Euro V', - EuroVI = 'Euro VI', - FullElectric = 'Full Electric', + EuroIVPM = '0.03 g/kWh Euro IV PM', + Euro3 = 'Euro 3', + Euro4 = 'Euro 4', + Euro5 = 'Euro 5', + Euro6 = 'Euro 6', + EuroV = 'Euro V', + EuroVI = 'Euro VI', + FullElectric = 'Full Electric', } export enum ModTypeCode { - p = 'p', - m = 'm', - g = 'g', + p = 'p', + m = 'm', + g = 'g', } export enum ModeTypeDescription { - ParticulateTrap = 'particulate trap', - Engine = 'modification or change of engine', - GasEngine = 'gas engine', + ParticulateTrap = 'particulate trap', + Engine = 'modification or change of engine', + GasEngine = 'gas engine', } export interface ModType { - code: ModTypeCode; - description: ModeTypeDescription; + code: ModTypeCode; + description: ModeTypeDescription; } diff --git a/src/app/models/test-types/eu-vehicle-category.enum.ts b/src/app/models/test-types/eu-vehicle-category.enum.ts index 6925551322..637d652be9 100644 --- a/src/app/models/test-types/eu-vehicle-category.enum.ts +++ b/src/app/models/test-types/eu-vehicle-category.enum.ts @@ -1,20 +1,20 @@ export enum EuVehicleCategory { - M1 = 'm1', - M2 = 'm2', - M3 = 'm3', - N1 = 'n1', - N2 = 'n2', - N3 = 'n3', - O1 = 'o1', - O2 = 'o2', - O3 = 'o3', - O4 = 'o4', - L1E_A = 'l1e-a', - L1E = 'l1e', - L2E = 'l2e', - L3E = 'l3e', - L4E = 'l4e', - L5E = 'l5e', - L6E = 'l6e', - L7E = 'l7e', + M1 = 'm1', + M2 = 'm2', + M3 = 'm3', + N1 = 'n1', + N2 = 'n2', + N3 = 'n3', + O1 = 'o1', + O2 = 'o2', + O3 = 'o3', + O4 = 'o4', + L1E_A = 'l1e-a', + L1E = 'l1e', + L2E = 'l2e', + L3E = 'l3e', + L4E = 'l4e', + L5E = 'l5e', + L6E = 'l6e', + L7E = 'l7e', } diff --git a/src/app/models/test-types/odometer-unit.enum.ts b/src/app/models/test-types/odometer-unit.enum.ts index d24f614178..94f47dca6a 100644 --- a/src/app/models/test-types/odometer-unit.enum.ts +++ b/src/app/models/test-types/odometer-unit.enum.ts @@ -1,4 +1,4 @@ export enum OdometerReadingUnits { - KILOMETRES = 'kilometres', - MILES = 'miles', + KILOMETRES = 'kilometres', + MILES = 'miles', } diff --git a/src/app/models/test-types/test-type.model.ts b/src/app/models/test-types/test-type.model.ts index e3939849ef..f98f5fd0dc 100644 --- a/src/app/models/test-types/test-type.model.ts +++ b/src/app/models/test-types/test-type.model.ts @@ -3,53 +3,62 @@ import { TestResultRequiredStandard } from '@models/test-results/test-result-req import * as Emissions from './emissions.enum'; export interface TestType { - testTypeId: string; - testNumber: string; - name: string; - testCode: string; - testTypeName: string; - - testTypeStartTimestamp: string | Date; - testTypeEndTimestamp: string | Date; - testExpiryDate: string | Date; - - certificateNumber: string; - reasonForAbandoning: string | null; - additionalCommentsForAbandon?: string | null; - testAnniversaryDate: string | Date; - prohibitionIssued: boolean | null; - - testResult: resultOfTestEnum; - - seatbeltInstallationCheckDate: boolean; - numberOfSeatbeltsFitted: number; - lastSeatbeltInstallationCheckDate: string | Date | null; - emissionStandard: Emissions.EmissionStandard; - smokeTestKLimitApplied: string; - fuelType: Emissions.FuelType; - modType: Emissions.ModType; - modificationTypeUsed: string; - particulateTrapFitted: string; - particulateTrapSerialNumber: string; - defects?: TestResultDefects; - requiredStandards?: TestResultRequiredStandard[]; - customDefects: CustomDefects[]; - - additionalNotesRecorded: string; - certificateLink?: string | null; - deletionFlag?: boolean; - secondaryCertificateNumber?: string | null; + testTypeId: string; + testNumber: string; + name: string; + testCode: string; + testTypeName: string; + + testTypeStartTimestamp: string | Date; + testTypeEndTimestamp: string | Date; + testExpiryDate: string | Date; + + certificateNumber: string; + reasonForAbandoning: string | null; + additionalCommentsForAbandon?: string | null; + testAnniversaryDate: string | Date; + prohibitionIssued: boolean | null; + + testResult: resultOfTestEnum; + + seatbeltInstallationCheckDate: boolean; + numberOfSeatbeltsFitted: number; + lastSeatbeltInstallationCheckDate: string | Date | null; + emissionStandard: Emissions.EmissionStandard; + smokeTestKLimitApplied: string; + fuelType: Emissions.FuelType; + modType: Emissions.ModType; + modificationTypeUsed: string; + particulateTrapFitted: string; + particulateTrapSerialNumber: string; + defects?: TestResultDefects; + requiredStandards?: TestResultRequiredStandard[]; + customDefects: CustomDefects[]; + + additionalNotesRecorded: string; + certificateLink?: string | null; + deletionFlag?: boolean; + secondaryCertificateNumber?: string | null; + reapplicationDate?: string; + + centralDocs?: CentralDocs; +} + +export interface CentralDocs { + issueRequired: boolean; + notes?: string; + reasonsForIssue?: string[]; } export interface CustomDefects { - referenceNumber?: string; - defectName: string; - defectNotes: string; + referenceNumber?: string; + defectName: string; + defectNotes: string; } export enum resultOfTestEnum { - fail = 'fail', - prs = 'prs', - pass = 'pass', - abandoned = 'abandoned', + fail = 'fail', + prs = 'prs', + pass = 'pass', + abandoned = 'abandoned', } diff --git a/src/app/models/vehicle-class.model.ts b/src/app/models/vehicle-class.model.ts index b444d14e03..34ddeb1073 100644 --- a/src/app/models/vehicle-class.model.ts +++ b/src/app/models/vehicle-class.model.ts @@ -1,44 +1,44 @@ export interface VehicleClass { - code?: CodesEnum; - description?: DescriptionsEnum; + code?: CodesEnum; + description?: DescriptionsEnum; } export type CodesEnum = '2' | 'n' | 's' | '1' | 't' | 'l' | '3' | 'v' | '4' | '7' | '5'; export const CodeEnum = { - _2: '2' as CodesEnum, - N: 'n' as CodesEnum, - S: 's' as CodesEnum, - _1: '1' as CodesEnum, - T: 't' as CodesEnum, - L: 'l' as CodesEnum, - _3: '3' as CodesEnum, - V: 'v' as CodesEnum, - _4: '4' as CodesEnum, - _7: '7' as CodesEnum, - _5: '5' as CodesEnum, + _2: '2' as CodesEnum, + N: 'n' as CodesEnum, + S: 's' as CodesEnum, + _1: '1' as CodesEnum, + T: 't' as CodesEnum, + L: 'l' as CodesEnum, + _3: '3' as CodesEnum, + V: 'v' as CodesEnum, + _4: '4' as CodesEnum, + _7: '7' as CodesEnum, + _5: '5' as CodesEnum, }; export type DescriptionsEnum = - | 'motorbikes over 200cc or with a sidecar' - | 'not applicable' - | 'small psv (ie: less than or equal to 22 seats)' - | 'motorbikes up to 200cc' - | 'trailer' - | 'large psv(ie: greater than 23 seats)' - | '3 wheelers' - | 'heavy goods vehicle' - | 'MOT class 4' - | 'MOT class 7' - | 'MOT class 5'; + | 'motorbikes over 200cc or with a sidecar' + | 'not applicable' + | 'small psv (ie: less than or equal to 22 seats)' + | 'motorbikes up to 200cc' + | 'trailer' + | 'large psv(ie: greater than 23 seats)' + | '3 wheelers' + | 'heavy goods vehicle' + | 'MOT class 4' + | 'MOT class 7' + | 'MOT class 5'; export const DescriptionEnum = { - MotorbikesOver200ccOrWithASidecar: 'motorbikes over 200cc or with a sidecar' as DescriptionsEnum, - NotApplicable: 'not applicable' as DescriptionsEnum, - SmallPsvIeLessThanOrEqualTo22Seats: 'small psv (ie: less than or equal to 22 seats)' as DescriptionsEnum, - MotorbikesUpTo200cc: 'motorbikes up to 200cc' as DescriptionsEnum, - Trailer: 'trailer' as DescriptionsEnum, - LargePsvIeGreaterThan23Seats: 'large psv(ie: greater than 23 seats)' as DescriptionsEnum, - _3Wheelers: '3 wheelers' as DescriptionsEnum, - HeavyGoodsVehicle: 'heavy goods vehicle' as DescriptionsEnum, - MOTClass4: 'MOT class 4' as DescriptionsEnum, - MOTClass7: 'MOT class 7' as DescriptionsEnum, - MOTClass5: 'MOT class 5' as DescriptionsEnum, + MotorbikesOver200ccOrWithASidecar: 'motorbikes over 200cc or with a sidecar' as DescriptionsEnum, + NotApplicable: 'not applicable' as DescriptionsEnum, + SmallPsvIeLessThanOrEqualTo22Seats: 'small psv (ie: less than or equal to 22 seats)' as DescriptionsEnum, + MotorbikesUpTo200cc: 'motorbikes up to 200cc' as DescriptionsEnum, + Trailer: 'trailer' as DescriptionsEnum, + LargePsvIeGreaterThan23Seats: 'large psv(ie: greater than 23 seats)' as DescriptionsEnum, + _3Wheelers: '3 wheelers' as DescriptionsEnum, + HeavyGoodsVehicle: 'heavy goods vehicle' as DescriptionsEnum, + MOTClass4: 'MOT class 4' as DescriptionsEnum, + MOTClass7: 'MOT class 7' as DescriptionsEnum, + MOTClass5: 'MOT class 5' as DescriptionsEnum, }; diff --git a/src/app/models/vehicle-configuration.enum.ts b/src/app/models/vehicle-configuration.enum.ts index 0ebc2bb204..78230f7742 100644 --- a/src/app/models/vehicle-configuration.enum.ts +++ b/src/app/models/vehicle-configuration.enum.ts @@ -1,32 +1,32 @@ export enum VehicleConfiguration { - RIGID = 'rigid', - ARTICULATED = 'articulated', - CENTRE_AXLE_DRAWBAR = 'centre axle drawbar', - SEMI_CAR_TRANSPORTER = 'semi-car transporter', - SEMI_TRAILER = 'semi-trailer', - LOW_LOADER = 'low loader', - OTHER = 'other', - DRAWBAR = 'drawbar', - FOUR_IN_LINE = 'four-in-line', - DOLLY = 'dolly', - FULL_DRAWBAR = 'full drawbar', - LONG_SEMI_TRAILER = 'long semi-trailer', + RIGID = 'rigid', + ARTICULATED = 'articulated', + CENTRE_AXLE_DRAWBAR = 'centre axle drawbar', + SEMI_CAR_TRANSPORTER = 'semi-car transporter', + SEMI_TRAILER = 'semi-trailer', + LOW_LOADER = 'low loader', + OTHER = 'other', + DRAWBAR = 'drawbar', + FOUR_IN_LINE = 'four-in-line', + DOLLY = 'dolly', + FULL_DRAWBAR = 'full drawbar', + LONG_SEMI_TRAILER = 'long semi-trailer', } export enum HgvPsvVehicleConfiguration { - RIGID = 'rigid', - ARTICULATED = 'articulated', + RIGID = 'rigid', + ARTICULATED = 'articulated', } export enum TrlVehicleConfiguration { - CENTRE_AXLE_DRAWBAR = 'centre axle drawbar', - SEMI_CAR_TRANSPORTER = 'semi-car transporter', - SEMI_TRAILER = 'semi-trailer', - LOW_LOADER = 'low loader', - OTHER = 'other', - DRAWBAR = 'drawbar', - FOUR_IN_LINE = 'four-in-line', - DOLLY = 'dolly', - FULL_DRAWBAR = 'full drawbar', - LONG_SEMI_TRAILER = 'long semi-trailer', + CENTRE_AXLE_DRAWBAR = 'centre axle drawbar', + SEMI_CAR_TRANSPORTER = 'semi-car transporter', + SEMI_TRAILER = 'semi-trailer', + LOW_LOADER = 'low loader', + OTHER = 'other', + DRAWBAR = 'drawbar', + FOUR_IN_LINE = 'four-in-line', + DOLLY = 'dolly', + FULL_DRAWBAR = 'full drawbar', + LONG_SEMI_TRAILER = 'long semi-trailer', } diff --git a/src/app/models/vehicle-size.enum.ts b/src/app/models/vehicle-size.enum.ts index ae5a8ddb91..c3c8e50c80 100644 --- a/src/app/models/vehicle-size.enum.ts +++ b/src/app/models/vehicle-size.enum.ts @@ -1,4 +1,4 @@ export enum VehicleSize { - SMALL = 'small', - LARGE = 'large', + SMALL = 'small', + LARGE = 'large', } diff --git a/src/app/models/vehicle-tech-record.model.ts b/src/app/models/vehicle-tech-record.model.ts index 19b6f9e22a..93841e4008 100644 --- a/src/app/models/vehicle-tech-record.model.ts +++ b/src/app/models/vehicle-tech-record.model.ts @@ -7,11 +7,11 @@ import { TechRecordType } from '@dvsa/cvs-type-definitions/types/v3/tech-record/ import { BodyTypeCode, BodyTypeDescription } from './body-type-enum'; export interface VehicleTechRecordModel { - vrms: Vrm[]; - vin: string; - trailerId?: string; - systemNumber: string; - techRecord: TechRecordModel[]; + vrms: Vrm[]; + vin: string; + trailerId?: string; + systemNumber: string; + techRecord: TechRecordModel[]; } export type V3TechRecordModel = TechRecordType<'get'> | TechRecordType<'put'>; @@ -20,488 +20,490 @@ export type Empty = { [key in keyof T]: T[key] | null }; export type VehiclesOtherThan = TechRecordTypeByVehicle>; -export type Axles = NonNullable['techRecord_axles']>; +export type Axles = NonNullable< + TechRecordTypeByVehicle['techRecord_axles'] +>; export type Axle = Axles[0]; export type BatchUpdateVehicleModel = TechRecordType<'put'> & { - createdTimestamp: string; - systemNumber?: string; + createdTimestamp: string; + systemNumber?: string; }; export interface PostNewVehicleModel extends Omit { - primaryVrm?: string; - secondaryVrms?: string[]; + primaryVrm?: string; + secondaryVrms?: string[]; } export interface Vrm { - vrm: string; - isPrimary: boolean; + vrm: string; + isPrimary: boolean; } export enum ReasonForEditing { - CORRECTING_AN_ERROR = 'correcting-an-error', - NOTIFIABLE_ALTERATION_NEEDED = 'notifiable-alteration-needed', + CORRECTING_AN_ERROR = 'correcting-an-error', + NOTIFIABLE_ALTERATION_NEEDED = 'notifiable-alteration-needed', } export enum StatusCodes { - ARCHIVED = 'archived', - CURRENT = 'current', - PROVISIONAL = 'provisional', + ARCHIVED = 'archived', + CURRENT = 'current', + PROVISIONAL = 'provisional', } export enum VehicleTypes { - PSV = 'psv', - HGV = 'hgv', - TRL = 'trl', - LGV = 'lgv', - CAR = 'car', - SMALL_TRL = 'small trl', - MOTORCYCLE = 'motorcycle', + PSV = 'psv', + HGV = 'hgv', + TRL = 'trl', + LGV = 'lgv', + CAR = 'car', + SMALL_TRL = 'small trl', + MOTORCYCLE = 'motorcycle', } export enum TrailerFormType { - TES1 = 'tes1', - TES2 = 'tes2', + TES1 = 'tes1', + TES2 = 'tes2', } export enum FuelTypes { - DIESELPETROL = 'DieselPetrol', - DIESEL = 'Diesel', - PETROL = 'Petrol', - HYBRID = 'Hybrid', - ELECTRIC = 'Electric', - CNG = 'CNG', - FUELCELL = 'Fuel cell', - LNG = 'LNG', - OTHER = 'Other', + DIESELPETROL = 'DieselPetrol', + DIESEL = 'Diesel', + PETROL = 'Petrol', + HYBRID = 'Hybrid', + ELECTRIC = 'Electric', + CNG = 'CNG', + FUELCELL = 'Fuel cell', + LNG = 'LNG', + OTHER = 'Other', } export enum VehicleConfigurations { - RIGID = 'rigid', - ARTICULATED = 'articulated', - CENTRE_AXLE_DRAWBAR = 'centre axle drawbar', - SEMI_CAR_TRANSPORTER = 'semi-car transporter', - SEMI_TRAILER = 'semi-trailer', - LOW_LOADER = 'low loader', - OTHER = 'other', - DRAWBAR = 'drawbar', - FOUR_IN_LINE = 'four-in-line', - DOLLY = 'dolly', - FULL_DRAWBAR = 'full drawbar', - LONG_SEMI_TRAILER = 'long semi-trailer', + RIGID = 'rigid', + ARTICULATED = 'articulated', + CENTRE_AXLE_DRAWBAR = 'centre axle drawbar', + SEMI_CAR_TRANSPORTER = 'semi-car transporter', + SEMI_TRAILER = 'semi-trailer', + LOW_LOADER = 'low loader', + OTHER = 'other', + DRAWBAR = 'drawbar', + FOUR_IN_LINE = 'four-in-line', + DOLLY = 'dolly', + FULL_DRAWBAR = 'full drawbar', + LONG_SEMI_TRAILER = 'long semi-trailer', } export enum FrameDescriptions { - CHANNEL_SECTION = 'Channel section', - SPACE_FRAME = 'Space frame', - I_SECTION = 'I section', - TUBULAR = 'Tubular', - FRAME_SECTION = 'Frame section', - OTHER = 'Other', - INTEGRAL = 'integral', - BOX_SECTION = 'Box section', - U_SECTION = 'U section', + CHANNEL_SECTION = 'Channel section', + SPACE_FRAME = 'Space frame', + I_SECTION = 'I section', + TUBULAR = 'Tubular', + FRAME_SECTION = 'Frame section', + OTHER = 'Other', + INTEGRAL = 'integral', + BOX_SECTION = 'Box section', + U_SECTION = 'U section', } export enum VehicleSizes { - SMALL = 'small', - LARGE = 'large', + SMALL = 'small', + LARGE = 'large', } export enum VehicleSubclass { - N = 'n', - P = 'p', - A = 'a', - S = 's', - C = 'c', - L = 'l', - T = 't', - E = 'e', - M = 'm', - R = 'r', - W = 'w', + N = 'n', + P = 'p', + A = 'a', + S = 's', + C = 'c', + L = 'l', + T = 't', + E = 'e', + M = 'm', + R = 'r', + W = 'w', } export interface LettersOfAuth { - letterType: string; - paragraphId: ParagraphIds; - letterIssuer: string; - letterDateRequested: string; - letterContents: string; + letterType: string; + paragraphId: ParagraphIds; + letterIssuer: string; + letterDateRequested: string; + letterContents: string; } export enum LettersIntoAuthApprovalType { - GB_WVTA = 'GB WVTA', - UKNI_WVTA = 'UKNI WVTA', - EU_WVTA_PRE_23 = 'EU WVTA Pre 23', - EU_WVTA_23_ON = 'EU WVTA 23 on', - QNIG = 'QNIG', - PROV_GB_WVTA = 'Prov.GB WVTA', - SMALL_SERIES_NKSXX = 'Small series NKSXX', - SMALL_SERIES_NKS = 'Small series NKS', - IVA_VCA = 'IVA - VCA', - IVA_DVSA_NI = 'IVA - DVSA/NI', + GB_WVTA = 'GB WVTA', + UKNI_WVTA = 'UKNI WVTA', + EU_WVTA_PRE_23 = 'EU WVTA Pre 23', + EU_WVTA_23_ON = 'EU WVTA 23 on', + QNIG = 'QNIG', + PROV_GB_WVTA = 'Prov.GB WVTA', + SMALL_SERIES_NKSXX = 'Small series NKSXX', + SMALL_SERIES_NKS = 'Small series NKS', + IVA_VCA = 'IVA - VCA', + IVA_DVSA_NI = 'IVA - DVSA/NI', } export enum ParagraphId { - PARAGRAPH_3 = 3, - PARAGRAPH_4 = 4, - PARAGRAPH_5 = 5, - PARAGRAPH_6 = 6, - PARAGRAPH_7 = 7, + PARAGRAPH_3 = 3, + PARAGRAPH_4 = 4, + PARAGRAPH_5 = 5, + PARAGRAPH_6 = 6, + PARAGRAPH_7 = 7, } export enum SpeedCategorySymbol { - A7 = 'a7', - A8 = 'a8', - B = 'b', - C = 'c', - D = 'd', - E = 'e', - F = 'f', - G = 'g', - J = 'j', - K = 'k', - L = 'l', - M = 'm', - N = 'n', - P = 'p', - Q = 'q', + A7 = 'a7', + A8 = 'a8', + B = 'b', + C = 'c', + D = 'd', + E = 'e', + F = 'f', + G = 'g', + J = 'j', + K = 'k', + L = 'l', + M = 'm', + N = 'n', + P = 'p', + Q = 'q', } export enum FitmentCode { - SINGLE = 'single', - DOUBLE = 'double', + SINGLE = 'single', + DOUBLE = 'double', } export interface Tyres { - tyreCode?: number | null; - tyreSize?: string | null; - plyRating?: string | null; - fitmentCode?: FitmentCode | null; - speedCategorySymbol?: SpeedCategorySymbol | null; - dataTrAxles?: number | null; + tyreCode?: number | null; + tyreSize?: string | null; + plyRating?: string | null; + fitmentCode?: FitmentCode | null; + speedCategorySymbol?: SpeedCategorySymbol | null; + dataTrAxles?: number | null; } export class Tyre implements Tyres { - tyreSize!: string | null; - speedCategorySymbol!: SpeedCategorySymbol | null; - fitmentCode!: FitmentCode | null; - dataTrAxles!: number | null; - plyRating!: string | null; - tyreCode!: number | null; + tyreSize!: string | null; + speedCategorySymbol!: SpeedCategorySymbol | null; + fitmentCode!: FitmentCode | null; + dataTrAxles!: number | null; + plyRating!: string | null; + tyreCode!: number | null; - constructor(tyre: Tyres) { - Object.assign(this, tyre); - } + constructor(tyre: Tyres) { + Object.assign(this, tyre); + } } export interface AxleWeights { - kerbWeight?: number | null; - ladenWeight?: number | null; - gbWeight?: number | null; - eecWeight?: number | null; - designWeight?: number | null; + kerbWeight?: number | null; + ladenWeight?: number | null; + gbWeight?: number | null; + eecWeight?: number | null; + designWeight?: number | null; } export interface Purchaser { - name?: string; - address1?: string; - address2?: string; - postTown?: string; - address3?: string | null; - postCode?: string | null; - telephoneNumber?: string | null; - emailAddress?: string | null; - faxNumber?: string | null; - purchaserNotes?: string | null; + name?: string; + address1?: string; + address2?: string; + postTown?: string; + address3?: string | null; + postCode?: string | null; + telephoneNumber?: string | null; + emailAddress?: string | null; + faxNumber?: string | null; + purchaserNotes?: string | null; } export interface BodyType { - description?: BodyTypeDescription; - code?: BodyTypeCode; + description?: BodyTypeDescription; + code?: BodyTypeCode; } export interface TechRecordModel { - historicVin?: string; - historicPrimaryVrm?: string; - historicSecondaryVrms?: string[]; - createdAt: Date; - createdByName?: string; - statusCode?: StatusCodes; - vehicleType: VehicleTypes; - regnDate?: string; - firstUseDate?: string; - manufactureYear?: number; - noOfAxles?: number; - axles?: Axle[]; - suspensionType?: string; - speedRestriction?: number; - speedLimiterMrk?: boolean; - tachoExemptMrk?: boolean; - euroStandard?: string; - roadFriendly?: boolean; - fuelPropulsionSystem?: FuelTypes; - drawbarCouplingFitted?: boolean; - vehicleClass?: { - description: string; - code: string; - }; - vehicleConfiguration?: VehicleConfigurations | null; - couplingType?: string; - maxLoadOnCoupling?: number; - frameDescription?: FrameDescriptions; - offRoad?: boolean; - numberOfWheelsDriven?: number; - euVehicleCategory?: EUVehicleCategory; - emissionsLimit?: number; - seatsLowerDeck?: number; - seatsUpperDeck?: number; - standingCapacity?: number; - vehicleSize?: VehicleSizes; - numberOfSeatbelts?: string; - seatbeltInstallationApprovalDate?: string; - departmentalVehicleMarker?: boolean; - approvalType?: ApprovalType; - approvalTypeNumber?: string; - ntaNumber?: string; - coifSerialNumber?: string; - coifCertifierName?: string; - coifDate?: string | Date; - variantNumber?: string; - variantVersionNumber?: string; - brakes?: Brakes; - applicantDetails?: ApplicantDetails; - microfilm?: Microfilm; - remarks?: string; - reasonForCreation?: string; - modelLiteral?: string; - make?: string; - model?: string; - chassisMake?: string; - chassisModel?: string; - bodyMake?: string; - bodyModel?: string; - bodyType?: BodyType; - functionCode?: string; - conversionRefNo?: string; - purchaserDetails?: Purchaser; - authIntoService?: AuthIntoService; - notes?: string; - vehicleSubclass?: Array; - - // Gross vehicle weights - grossKerbWeight?: number; - grossLadenWeight?: number; - grossGbWeight?: number; - grossEecWeight?: number; - grossDesignWeight?: number; - unladenWeight?: number; - - // Train weights - trainDesignWeight?: number; - trainEecWeight?: number; - trainGbWeight?: number; - - // Max Train Weights - maxTrainGbWeight?: number; - maxTrainDesignWeight?: number; - maxTrainEecWeight?: number; - - // Dimensions - dimensions?: Dimensions; - frontAxleToRearAxle?: number; - rearAxleToRearTrl?: number; - - // Front of vehicle to 5th wheel coupling - frontVehicleTo5thWheelCouplingMin?: number; - frontVehicleTo5thWheelCouplingMax?: number; - - // Front axle to 5th wheel - frontAxleTo5thWheelMin?: number; - frontAxleTo5thWheelMax?: number; - - // Coupling center to rear axle - couplingCenterToRearAxleMin?: number; - couplingCenterToRearAxleMax?: number; - - // Coupling center to rear trailer - couplingCenterToRearTrlMin?: number; - couplingCenterToRearTrlMax?: number; - plates?: Plates[]; - letterOfAuth?: LettersOfAuth; - dda?: DDA; - updateType?: string; - - recordCompleteness?: string; - hiddenInVta?: boolean; + historicVin?: string; + historicPrimaryVrm?: string; + historicSecondaryVrms?: string[]; + createdAt: Date; + createdByName?: string; + statusCode?: StatusCodes; + vehicleType: VehicleTypes; + regnDate?: string; + firstUseDate?: string; + manufactureYear?: number; + noOfAxles?: number; + axles?: Axle[]; + suspensionType?: string; + speedRestriction?: number; + speedLimiterMrk?: boolean; + tachoExemptMrk?: boolean; + euroStandard?: string; + roadFriendly?: boolean; + fuelPropulsionSystem?: FuelTypes; + drawbarCouplingFitted?: boolean; + vehicleClass?: { + description: string; + code: string; + }; + vehicleConfiguration?: VehicleConfigurations | null; + couplingType?: string; + maxLoadOnCoupling?: number; + frameDescription?: FrameDescriptions; + offRoad?: boolean; + numberOfWheelsDriven?: number; + euVehicleCategory?: EUVehicleCategory; + emissionsLimit?: number; + seatsLowerDeck?: number; + seatsUpperDeck?: number; + standingCapacity?: number; + vehicleSize?: VehicleSizes; + numberOfSeatbelts?: string; + seatbeltInstallationApprovalDate?: string; + departmentalVehicleMarker?: boolean; + approvalType?: ApprovalType; + approvalTypeNumber?: string; + ntaNumber?: string; + coifSerialNumber?: string; + coifCertifierName?: string; + coifDate?: string | Date; + variantNumber?: string; + variantVersionNumber?: string; + brakes?: Brakes; + applicantDetails?: ApplicantDetails; + microfilm?: Microfilm; + remarks?: string; + reasonForCreation?: string; + modelLiteral?: string; + make?: string; + model?: string; + chassisMake?: string; + chassisModel?: string; + bodyMake?: string; + bodyModel?: string; + bodyType?: BodyType; + functionCode?: string; + conversionRefNo?: string; + purchaserDetails?: Purchaser; + authIntoService?: AuthIntoService; + notes?: string; + vehicleSubclass?: Array; + + // Gross vehicle weights + grossKerbWeight?: number; + grossLadenWeight?: number; + grossGbWeight?: number; + grossEecWeight?: number; + grossDesignWeight?: number; + unladenWeight?: number; + + // Train weights + trainDesignWeight?: number; + trainEecWeight?: number; + trainGbWeight?: number; + + // Max Train Weights + maxTrainGbWeight?: number; + maxTrainDesignWeight?: number; + maxTrainEecWeight?: number; + + // Dimensions + dimensions?: Dimensions; + frontAxleToRearAxle?: number; + rearAxleToRearTrl?: number; + + // Front of vehicle to 5th wheel coupling + frontVehicleTo5thWheelCouplingMin?: number; + frontVehicleTo5thWheelCouplingMax?: number; + + // Front axle to 5th wheel + frontAxleTo5thWheelMin?: number; + frontAxleTo5thWheelMax?: number; + + // Coupling center to rear axle + couplingCenterToRearAxleMin?: number; + couplingCenterToRearAxleMax?: number; + + // Coupling center to rear trailer + couplingCenterToRearTrlMin?: number; + couplingCenterToRearTrlMax?: number; + plates?: Plates[]; + letterOfAuth?: LettersOfAuth; + dda?: DDA; + updateType?: string; + + recordCompleteness?: string; + hiddenInVta?: boolean; } export interface AuthIntoService { - cocIssueDate?: string | null; - dateReceived?: string | null; - datePending?: string | null; - dateAuthorised?: string | null; - dateRejected?: string | null; + cocIssueDate?: string | null; + dateReceived?: string | null; + datePending?: string | null; + dateAuthorised?: string | null; + dateRejected?: string | null; } export interface DDA { - certificateIssued?: boolean; - wheelchairCapacity?: number; - wheelchairFittings?: string; - wheelchairLiftPresent?: boolean; - wheelchairLiftInformation?: string; - wheelchairRampPresent?: boolean; - wheelchairRampInformation?: string; - minEmergencyExits?: number; - outswing?: string; - ddaSchedules?: string; - seatbeltsFitted?: number; - ddaNotes?: string; + certificateIssued?: boolean; + wheelchairCapacity?: number; + wheelchairFittings?: string; + wheelchairLiftPresent?: boolean; + wheelchairLiftInformation?: string; + wheelchairRampPresent?: boolean; + wheelchairRampInformation?: string; + minEmergencyExits?: number; + outswing?: string; + ddaSchedules?: string; + seatbeltsFitted?: number; + ddaNotes?: string; } export interface Plates { - plateSerialNumber?: string; - plateIssueDate?: Date; - plateReasonForIssue?: PlateReasonForIssue; - plateIssuer?: string; + plateSerialNumber?: string; + plateIssueDate?: Date; + plateReasonForIssue?: PlateReasonForIssue; + plateIssuer?: string; } export enum PlateReasonForIssue { - FREE_REPLACEMENT = 'Free replacement', - REPLACEMENT = 'Replacement', - DESTROYED = 'Destroyed', - PROVISIONAL = 'Provisional', - ORIGINAL = 'Original', - MANUAL = 'Manual', + FREE_REPLACEMENT = 'Free replacement', + REPLACEMENT = 'Replacement', + DESTROYED = 'Destroyed', + PROVISIONAL = 'Provisional', + ORIGINAL = 'Original', + MANUAL = 'Manual', } export interface ApplicantDetails { - name?: string; - address1?: string; - address2?: string; - postTown?: string; - address3?: string; - postCode?: string; - telephoneNumber?: string; - emailAddress?: string; + name?: string; + address1?: string; + address2?: string; + postTown?: string; + address3?: string; + postCode?: string; + telephoneNumber?: string; + emailAddress?: string; } export interface Dimensions { - height?: number; - length?: number; - width?: number; - axleSpacing?: AxleSpacing[]; + height?: number; + length?: number; + width?: number; + axleSpacing?: AxleSpacing[]; } export interface AxleSpacing { - axles?: string; - value?: number | null; + axles?: string; + value?: number | null; } export interface Brakes { - dtpNumber?: string; - loadSensingValve?: string; // TODO: Check from here if these types are correct - antilockBrakingSystem?: string; - axleNumber?: string; - axleBrakeProperties?: AxleBrakeProperties; // Check to here and including object - brakeCode?: string; - brakeCodeOriginal?: string; - dataTrBrakeOne?: string; - dataTrBrakeTwo?: string; - dataTrBrakeThree?: string; - retarderBrakeOne?: Retarders; - retarderBrakeTwo?: Retarders; - brakeForceWheelsNotLocked?: BrakeForceWheelsNotLocked; - brakeForceWheelsUpToHalfLocked?: BrakeForceWheelsUpToHalfLocked; + dtpNumber?: string; + loadSensingValve?: string; // TODO: Check from here if these types are correct + antilockBrakingSystem?: string; + axleNumber?: string; + axleBrakeProperties?: AxleBrakeProperties; // Check to here and including object + brakeCode?: string; + brakeCodeOriginal?: string; + dataTrBrakeOne?: string; + dataTrBrakeTwo?: string; + dataTrBrakeThree?: string; + retarderBrakeOne?: Retarders; + retarderBrakeTwo?: Retarders; + brakeForceWheelsNotLocked?: BrakeForceWheelsNotLocked; + brakeForceWheelsUpToHalfLocked?: BrakeForceWheelsUpToHalfLocked; } export interface BrakeForceWheelsNotLocked { - parkingBrakeForceA?: number; - secondaryBrakeForceA?: number; - serviceBrakeForceA?: number; + parkingBrakeForceA?: number; + secondaryBrakeForceA?: number; + serviceBrakeForceA?: number; } export interface BrakeForceWheelsUpToHalfLocked { - parkingBrakeForceB?: number; - secondaryBrakeForceB?: number; - serviceBrakeForceB?: number; + parkingBrakeForceB?: number; + secondaryBrakeForceB?: number; + serviceBrakeForceB?: number; } export enum Retarders { - ELECTRIC = 'electric', - EXHAUST = 'exhaust', - FRICTION = 'friction', - HYDRAULIC = 'hydraulic', - OTHER = 'other', - NONE = 'none', + ELECTRIC = 'electric', + EXHAUST = 'exhaust', + FRICTION = 'friction', + HYDRAULIC = 'hydraulic', + OTHER = 'other', + NONE = 'none', } export interface AxleBrakeProperties { - brakeActuator?: string; - leverLength?: string; - springBrakeParking?: boolean; + brakeActuator?: string; + leverLength?: string; + springBrakeParking?: boolean; } export interface Microfilm { - microfilmDocumentType?: MicrofilmDocumentType; - microfilmRollNumber?: string; - microfilmSerialNumber?: string; + microfilmDocumentType?: MicrofilmDocumentType; + microfilmRollNumber?: string; + microfilmSerialNumber?: string; } export enum MicrofilmDocumentType { - PSVMisc = 'PSV Miscellaneous', - AAT = 'AAT - Trailer Annual Test', - AIV = 'AIV - HGV International App', - COIFMod = 'COIF Modification', - Trailer = 'Trailer COC + Int Plate', - RCT = 'RCT - Trailer Test Cert paid', - HGV = 'HGV COC + Int Plate', - PSVCarry = 'PSV Carry/Auth', - OMORep = 'OMO Report', - AIT = 'AIT - Trailer International App', - IPV = 'IPV - HGV EEC Plate/Cert', - XCV = 'XCV - HGV Test Cert free', - AAV = 'AAV - HGV Annual Test', - COIFMaster = 'COIF Master', - Tempo100 = 'Tempo 100 Sp Ord', - Deleted = 'Deleted', - PSVNalt = 'PSV N/ALT', - XPT = 'XPT - Tr Plating Cert paid', - FFV = 'FFV - HGV First Test', - Repl = 'Repl Vitesse 100', - TCV = 'TCV - HGV Test Cert', - ZZZ = 'ZZZ - Miscellaneous', - Test = 'Test Certificate', - XCT = 'XCT - Trailer Test Cert free', - C52 = 'C52 - COC and VTG52A', - Tempo100Rep = 'Tempo 100 Report', - Main = 'Main File Amendment', - PSVDoc = 'PSV Doc', - PSVCOC = 'PSV COC', - PSVReplCOC = 'PSV Repl COC', - TAV = 'TAV - COC', - NPT = 'NPT - Trailer Alteration', - OMO = 'OMO Certificate', - PSVReplCOIF = 'PSV Repl COIF', - PSVReplCOF = 'PSV Repl COF', - COIFApp = 'COIF Application', - XPV = 'XPV - HGV Plating Cert Free', - TCT = 'TCT - Trailer Test Cert', - Tempo100App = 'Tempo 100 App', - PSV = 'PSV Decision on N/ALT', - Special = 'Special Order PSV', - NPV = 'NPV - HGV Alteration', - No = 'No Description Found', - Vitesse = 'Vitesse 100 Sp Ord', - Brake = 'Brake Test Details', - COIF = 'COIF Productional', - RDT = 'RDT - Test Disc Paid', - RCV = 'RCV - HGV Test Cert', - FFT = 'FFT - Trailer First Test', - IPT = 'IPT - Trailer EEC Plate/Cert', - XDT = 'XDT - Test Disc Free', - PRV = 'PRV - HGV Plating Cert paid', - COF = 'COF Cert', - PRT = 'PRT - Tr Plating Cert paid', - Tempo = 'Tempo 100 Permit', + PSVMisc = 'PSV Miscellaneous', + AAT = 'AAT - Trailer Annual Test', + AIV = 'AIV - HGV International App', + COIFMod = 'COIF Modification', + Trailer = 'Trailer COC + Int Plate', + RCT = 'RCT - Trailer Test Cert paid', + HGV = 'HGV COC + Int Plate', + PSVCarry = 'PSV Carry/Auth', + OMORep = 'OMO Report', + AIT = 'AIT - Trailer International App', + IPV = 'IPV - HGV EEC Plate/Cert', + XCV = 'XCV - HGV Test Cert free', + AAV = 'AAV - HGV Annual Test', + COIFMaster = 'COIF Master', + Tempo100 = 'Tempo 100 Sp Ord', + Deleted = 'Deleted', + PSVNalt = 'PSV N/ALT', + XPT = 'XPT - Tr Plating Cert paid', + FFV = 'FFV - HGV First Test', + Repl = 'Repl Vitesse 100', + TCV = 'TCV - HGV Test Cert', + ZZZ = 'ZZZ - Miscellaneous', + Test = 'Test Certificate', + XCT = 'XCT - Trailer Test Cert free', + C52 = 'C52 - COC and VTG52A', + Tempo100Rep = 'Tempo 100 Report', + Main = 'Main File Amendment', + PSVDoc = 'PSV Doc', + PSVCOC = 'PSV COC', + PSVReplCOC = 'PSV Repl COC', + TAV = 'TAV - COC', + NPT = 'NPT - Trailer Alteration', + OMO = 'OMO Certificate', + PSVReplCOIF = 'PSV Repl COIF', + PSVReplCOF = 'PSV Repl COF', + COIFApp = 'COIF Application', + XPV = 'XPV - HGV Plating Cert Free', + TCT = 'TCT - Trailer Test Cert', + Tempo100App = 'Tempo 100 App', + PSV = 'PSV Decision on N/ALT', + Special = 'Special Order PSV', + NPV = 'NPV - HGV Alteration', + No = 'No Description Found', + Vitesse = 'Vitesse 100 Sp Ord', + Brake = 'Brake Test Details', + COIF = 'COIF Productional', + RDT = 'RDT - Test Disc Paid', + RCV = 'RCV - HGV Test Cert', + FFT = 'FFT - Trailer First Test', + IPT = 'IPT - Trailer EEC Plate/Cert', + XDT = 'XDT - Test Disc Free', + PRV = 'PRV - HGV Plating Cert paid', + COF = 'COF Cert', + PRT = 'PRT - Tr Plating Cert paid', + Tempo = 'Tempo 100 Permit', } diff --git a/src/app/resolvers/contingency-test/contingency-test.resolver.spec.ts b/src/app/resolvers/contingency-test/contingency-test.resolver.spec.ts index f6863ed1bf..d87b399e84 100644 --- a/src/app/resolvers/contingency-test/contingency-test.resolver.spec.ts +++ b/src/app/resolvers/contingency-test/contingency-test.resolver.spec.ts @@ -12,203 +12,194 @@ import { TechnicalRecordService } from '@services/technical-record/technical-rec import { UserService } from '@services/user-service/user-service'; import { State, initialAppState } from '@store/.'; import { initialContingencyTest } from '@store/test-records'; -import { - Observable, - ReplaySubject, - firstValueFrom, - of, - throwError, -} from 'rxjs'; -import { - contingencyTestResolver, - getBodyMake, - getBodyModel, - getBodyType, -} from './contingency-test.resolver'; +import { Observable, ReplaySubject, firstValueFrom, of, throwError } from 'rxjs'; +import { contingencyTestResolver, getBodyMake, getBodyModel, getBodyType } from './contingency-test.resolver'; describe('ContingencyTestResolver', () => { - let resolver: ResolveFn; - const actions$ = new ReplaySubject(); - let store: MockStore; - let techRecordService: TechnicalRecordService; - - const MockUserService = { - getUserName$: jest.fn().mockReturnValue(new Observable()), - get user$() { - return of('foo'); - }, - }; - - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [HttpClientTestingModule, RouterTestingModule], - providers: [ - provideMockStore({ initialState: initialAppState }), - provideMockActions(() => actions$), - TechnicalRecordService, - { provide: UserService, useValue: MockUserService }, - ], - }); - resolver = (...resolverParameters) => - TestBed.runInInjectionContext(() => contingencyTestResolver(...resolverParameters)); - store = TestBed.inject(MockStore); - techRecordService = TestBed.inject(TechnicalRecordService); - }); - - it('should be created', () => { - expect(resolver).toBeTruthy(); - }); - - it('should return true and dispatch the initial contingency test action', async () => { - const dispatchSpy = jest.spyOn(store, 'dispatch'); - jest.spyOn(techRecordService, 'techRecord$', 'get').mockReturnValue(of(mockVehicleTechnicalRecord('psv') as TechRecordType<'psv'>)); - const result = TestBed.runInInjectionContext( - () => resolver({} as ActivatedRouteSnapshot, {} as RouterStateSnapshot), - ) as Observable; - const resolveResult = await firstValueFrom(result); - - expect(resolveResult).toBe(true); - expect(dispatchSpy).toHaveBeenCalledTimes(1); - expect(dispatchSpy).toHaveBeenCalledWith(expect.objectContaining({ type: initialContingencyTest.type, testResult: expect.anything() })); - }); - - it('should return false if there is an error', async () => { - jest.spyOn(techRecordService, 'techRecord$', 'get').mockReturnValue(of(mockVehicleTechnicalRecord('psv') as TechRecordType<'psv'>)); - jest.spyOn(MockUserService, 'user$', 'get').mockImplementationOnce(() => throwError(() => new Error('foo'))); - const result = TestBed.runInInjectionContext( - () => resolver({} as ActivatedRouteSnapshot, {} as RouterStateSnapshot), - ) as Observable; - const resolveResult = await firstValueFrom(result); - expect(resolveResult).toBe(false); - }); - - describe('getBodyMake', () => { - it('should return vehicle make if vehicle is psv', () => { - const techRecord = { - techRecord_vehicleType: 'psv', - techRecord_bodyMake: 'test make 1', - } as V3TechRecordModel; - - const make = getBodyMake(techRecord); - expect(make).toBe('test make 1'); - }); - - it('should return vehicle make if vehicle is hgv', () => { - const techRecord = { - techRecord_vehicleType: 'hgv', - techRecord_make: 'test make 2', - } as V3TechRecordModel; - - const make = getBodyMake(techRecord); - expect(make).toBe('test make 2'); - }); - - it('should return vehicle make if vehicle is trl', () => { - const techRecord = { - techRecord_vehicleType: 'trl', - techRecord_make: 'test make 3', - } as V3TechRecordModel; - - const make = getBodyMake(techRecord); - expect(make).toBe('test make 3'); - }); - - it('should not return vehicle make if vehicle is not hgv, psv or trl', () => { - const techRecord = { - techRecord_vehicleType: 'car', - } as V3TechRecordModel; - - const make = getBodyMake(techRecord); - expect(make).toBeNull(); - }); - }); - - describe('getBodyModel', () => { - it('should return vehicle model if vehicle is psv', () => { - const techRecord = { - techRecord_vehicleType: 'psv', - techRecord_bodyModel: 'test model 1', - } as V3TechRecordModel; - - const model = getBodyModel(techRecord); - expect(model).toBe('test model 1'); - }); - - it('should return vehicle model if vehicle is hgv', () => { - const techRecord = { - techRecord_vehicleType: 'hgv', - techRecord_model: 'test model 2', - } as V3TechRecordModel; - - const model = getBodyModel(techRecord); - expect(model).toBe('test model 2'); - }); - - it('should return vehicle model if vehicle is trl', () => { - const techRecord = { - techRecord_vehicleType: 'trl', - techRecord_model: 'test model 3', - } as V3TechRecordModel; - - const model = getBodyModel(techRecord); - expect(model).toBe('test model 3'); - }); - - it('should not return vehicle model if vehicle is not hgv, psv or trl', () => { - const techRecord = { - techRecord_vehicleType: 'car', - } as V3TechRecordModel; - - const model = getBodyModel(techRecord); - expect(model).toBeNull(); - }); - }); - - describe('getBodyType', () => { - it('should return vehicle body type if vehicle is psv', () => { - const techRecord = { - techRecord_vehicleType: 'psv', - techRecord_bodyType_code: 'test code 1', - techRecord_bodyType_description: 'flat', - - } as V3TechRecordModel; - - const bodyType = getBodyType(techRecord); - expect(bodyType).toStrictEqual({ code: 'test code 1', description: 'flat' }); - }); - - it('should return vehicle body type if vehicle is hgv', () => { - const techRecord = { - techRecord_vehicleType: 'hgv', - techRecord_bodyType_code: 'test code 2', - techRecord_bodyType_description: 'flat', - - } as V3TechRecordModel; - - const bodyType = getBodyType(techRecord); - expect(bodyType).toStrictEqual({ code: 'test code 2', description: 'flat' }); - }); - - it('should return vehicle body type if vehicle is trl', () => { - const techRecord = { - techRecord_vehicleType: 'trl', - techRecord_bodyType_code: 'test code 3', - techRecord_bodyType_description: 'flat', - - } as V3TechRecordModel; - - const bodyType = getBodyType(techRecord); - expect(bodyType).toStrictEqual({ code: 'test code 3', description: 'flat' }); - }); - - it('should not return vehicle body type if vehicle is not hgv, psv or trl', () => { - const techRecord = { - techRecord_vehicleType: 'car', - } as V3TechRecordModel; - - const bodyType = getBodyType(techRecord); - expect(bodyType).toBeNull(); - }); - }); - + let resolver: ResolveFn; + const actions$ = new ReplaySubject(); + let store: MockStore; + let techRecordService: TechnicalRecordService; + + const MockUserService = { + getUserName$: jest.fn().mockReturnValue(new Observable()), + get user$() { + return of('foo'); + }, + }; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [HttpClientTestingModule, RouterTestingModule], + providers: [ + provideMockStore({ initialState: initialAppState }), + provideMockActions(() => actions$), + TechnicalRecordService, + { provide: UserService, useValue: MockUserService }, + ], + }); + resolver = (...resolverParameters) => + TestBed.runInInjectionContext(() => contingencyTestResolver(...resolverParameters)); + store = TestBed.inject(MockStore); + techRecordService = TestBed.inject(TechnicalRecordService); + }); + + it('should be created', () => { + expect(resolver).toBeTruthy(); + }); + + it('should return true and dispatch the initial contingency test action', async () => { + const dispatchSpy = jest.spyOn(store, 'dispatch'); + jest + .spyOn(techRecordService, 'techRecord$', 'get') + .mockReturnValue(of(mockVehicleTechnicalRecord('psv') as TechRecordType<'psv'>)); + const result = TestBed.runInInjectionContext(() => + resolver({} as ActivatedRouteSnapshot, {} as RouterStateSnapshot) + ) as Observable; + const resolveResult = await firstValueFrom(result); + + expect(resolveResult).toBe(true); + expect(dispatchSpy).toHaveBeenCalledTimes(1); + expect(dispatchSpy).toHaveBeenCalledWith( + expect.objectContaining({ type: initialContingencyTest.type, testResult: expect.anything() }) + ); + }); + + it('should return false if there is an error', async () => { + jest + .spyOn(techRecordService, 'techRecord$', 'get') + .mockReturnValue(of(mockVehicleTechnicalRecord('psv') as TechRecordType<'psv'>)); + jest.spyOn(MockUserService, 'user$', 'get').mockImplementationOnce(() => throwError(() => new Error('foo'))); + const result = TestBed.runInInjectionContext(() => + resolver({} as ActivatedRouteSnapshot, {} as RouterStateSnapshot) + ) as Observable; + const resolveResult = await firstValueFrom(result); + expect(resolveResult).toBe(false); + }); + + describe('getBodyMake', () => { + it('should return vehicle make if vehicle is psv', () => { + const techRecord = { + techRecord_vehicleType: 'psv', + techRecord_bodyMake: 'test make 1', + } as V3TechRecordModel; + + const make = getBodyMake(techRecord); + expect(make).toBe('test make 1'); + }); + + it('should return vehicle make if vehicle is hgv', () => { + const techRecord = { + techRecord_vehicleType: 'hgv', + techRecord_make: 'test make 2', + } as V3TechRecordModel; + + const make = getBodyMake(techRecord); + expect(make).toBe('test make 2'); + }); + + it('should return vehicle make if vehicle is trl', () => { + const techRecord = { + techRecord_vehicleType: 'trl', + techRecord_make: 'test make 3', + } as V3TechRecordModel; + + const make = getBodyMake(techRecord); + expect(make).toBe('test make 3'); + }); + + it('should not return vehicle make if vehicle is not hgv, psv or trl', () => { + const techRecord = { + techRecord_vehicleType: 'car', + } as V3TechRecordModel; + + const make = getBodyMake(techRecord); + expect(make).toBeNull(); + }); + }); + + describe('getBodyModel', () => { + it('should return vehicle model if vehicle is psv', () => { + const techRecord = { + techRecord_vehicleType: 'psv', + techRecord_bodyModel: 'test model 1', + } as V3TechRecordModel; + + const model = getBodyModel(techRecord); + expect(model).toBe('test model 1'); + }); + + it('should return vehicle model if vehicle is hgv', () => { + const techRecord = { + techRecord_vehicleType: 'hgv', + techRecord_model: 'test model 2', + } as V3TechRecordModel; + + const model = getBodyModel(techRecord); + expect(model).toBe('test model 2'); + }); + + it('should return vehicle model if vehicle is trl', () => { + const techRecord = { + techRecord_vehicleType: 'trl', + techRecord_model: 'test model 3', + } as V3TechRecordModel; + + const model = getBodyModel(techRecord); + expect(model).toBe('test model 3'); + }); + + it('should not return vehicle model if vehicle is not hgv, psv or trl', () => { + const techRecord = { + techRecord_vehicleType: 'car', + } as V3TechRecordModel; + + const model = getBodyModel(techRecord); + expect(model).toBeNull(); + }); + }); + + describe('getBodyType', () => { + it('should return vehicle body type if vehicle is psv', () => { + const techRecord = { + techRecord_vehicleType: 'psv', + techRecord_bodyType_code: 'test code 1', + techRecord_bodyType_description: 'flat', + } as V3TechRecordModel; + + const bodyType = getBodyType(techRecord); + expect(bodyType).toStrictEqual({ code: 'test code 1', description: 'flat' }); + }); + + it('should return vehicle body type if vehicle is hgv', () => { + const techRecord = { + techRecord_vehicleType: 'hgv', + techRecord_bodyType_code: 'test code 2', + techRecord_bodyType_description: 'flat', + } as V3TechRecordModel; + + const bodyType = getBodyType(techRecord); + expect(bodyType).toStrictEqual({ code: 'test code 2', description: 'flat' }); + }); + + it('should return vehicle body type if vehicle is trl', () => { + const techRecord = { + techRecord_vehicleType: 'trl', + techRecord_bodyType_code: 'test code 3', + techRecord_bodyType_description: 'flat', + } as V3TechRecordModel; + + const bodyType = getBodyType(techRecord); + expect(bodyType).toStrictEqual({ code: 'test code 3', description: 'flat' }); + }); + + it('should not return vehicle body type if vehicle is not hgv, psv or trl', () => { + const techRecord = { + techRecord_vehicleType: 'car', + } as V3TechRecordModel; + + const bodyType = getBodyType(techRecord); + expect(bodyType).toBeNull(); + }); + }); }); diff --git a/src/app/resolvers/contingency-test/contingency-test.resolver.ts b/src/app/resolvers/contingency-test/contingency-test.resolver.ts index 93f9189b22..da721920ae 100644 --- a/src/app/resolvers/contingency-test/contingency-test.resolver.ts +++ b/src/app/resolvers/contingency-test/contingency-test.resolver.ts @@ -12,126 +12,131 @@ import { UserService } from '@services/user-service/user-service'; import { State } from '@store/.'; import { selectTechRecord } from '@store/technical-records'; import { initialContingencyTest } from '@store/test-records'; -import { - catchError, map, of, switchMap, take, tap, withLatestFrom, -} from 'rxjs'; +import { catchError, map, of, switchMap, take, tap, withLatestFrom } from 'rxjs'; import { v4 as uuidv4 } from 'uuid'; export const contingencyTestResolver: ResolveFn = () => { - const store: Store = inject(Store); - const techRecordService: TechnicalRecordService = inject(TechnicalRecordService); - const userService: UserService = inject(UserService); - return techRecordService.techRecord$.pipe( - switchMap((techRecord) => { - const { vin, systemNumber } = techRecord as TechRecordType<'get'>; - const vrm = techRecord?.techRecord_vehicleType !== 'trl' ? techRecord?.primaryVrm : undefined; - const trailerId = techRecord?.techRecord_vehicleType === 'trl' ? techRecord.trailerId : undefined; - return store.select(selectTechRecord).pipe( - withLatestFrom(userService.user$), - map(([viewableTechRecord, user]) => { - const now = new Date(); - return { - vin, - vrm, - trailerId, - systemNumber, - vehicleType: viewableTechRecord?.techRecord_vehicleType, - statusCode: viewableTechRecord?.techRecord_statusCode, - testResultId: uuidv4(), - euVehicleCategory: (viewableTechRecord as TechRecordType<'get'>)?.techRecord_euVehicleCategory ?? null, - vehicleSize: viewableTechRecord?.techRecord_vehicleType === 'psv' ? viewableTechRecord?.techRecord_vehicleSize : undefined, - vehicleConfiguration: (viewableTechRecord as TechRecordType<'get'>)?.techRecord_vehicleConfiguration ?? null, - vehicleClass: - (viewableTechRecord?.techRecord_vehicleType === 'psv' - || viewableTechRecord?.techRecord_vehicleType === 'trl' - || viewableTechRecord?.techRecord_vehicleType === 'hgv' - || viewableTechRecord?.techRecord_vehicleType === 'motorcycle') - && 'techRecord_vehicleClass_code' in viewableTechRecord - ? { - code: viewableTechRecord?.techRecord_vehicleClass_code, - description: viewableTechRecord?.techRecord_vehicleClass_description, - } - : null, - vehicleSubclass: - viewableTechRecord && 'techRecord_vehicleSubclass' in viewableTechRecord ? viewableTechRecord.techRecord_vehicleSubclass : null, - noOfAxles: viewableTechRecord?.techRecord_noOfAxles ?? 0, - numberOfWheelsDriven: - viewableTechRecord && 'techRecord_numberOfWheelsDriven' in viewableTechRecord - ? viewableTechRecord.techRecord_numberOfWheelsDriven - : null, - testStatus: 'submitted', - regnDate: viewableTechRecord?.techRecord_regnDate, - numberOfSeats: - ((viewableTechRecord as VehicleType<'psv'>)?.techRecord_seatsLowerDeck ?? 0) - + ((viewableTechRecord as VehicleType<'psv'>)?.techRecord_seatsUpperDeck ?? 0), - reasonForCancellation: '', - createdAt: now.toISOString(), - lastUpdatedAt: now.toISOString(), - firstUseDate: viewableTechRecord?.techRecord_vehicleType === 'trl' ? viewableTechRecord?.techRecord_firstUseDate : null, - createdByName: user.name, - createdById: user.oid, - lastUpdatedByName: user.name, - lastUpdatedById: user.oid, - typeOfTest: TypeOfTest.CONTINGENCY, - source: 'vtm', - make: getBodyMake(viewableTechRecord), - model: getBodyModel(viewableTechRecord), - bodyType: getBodyType(viewableTechRecord), - testTypes: [ - { - testResult: 'pass', - prohibitionIssued: null, - additionalCommentsForAbandon: null, - } as TestType, - ], - } as Partial; - }), - ); - }), - tap((testResult) => { - store.dispatch(initialContingencyTest({ testResult })); - }), - take(1), - map(() => { - return true; - }), - catchError(() => { - return of(false); - }), - ); + const store: Store = inject(Store); + const techRecordService: TechnicalRecordService = inject(TechnicalRecordService); + const userService: UserService = inject(UserService); + return techRecordService.techRecord$.pipe( + switchMap((techRecord) => { + const { vin, systemNumber } = techRecord as TechRecordType<'get'>; + const vrm = techRecord?.techRecord_vehicleType !== 'trl' ? techRecord?.primaryVrm : undefined; + const trailerId = techRecord?.techRecord_vehicleType === 'trl' ? techRecord.trailerId : undefined; + return store.select(selectTechRecord).pipe( + withLatestFrom(userService.user$), + map(([viewableTechRecord, user]) => { + const now = new Date(); + return { + vin, + vrm, + trailerId, + systemNumber, + vehicleType: viewableTechRecord?.techRecord_vehicleType, + statusCode: viewableTechRecord?.techRecord_statusCode, + testResultId: uuidv4(), + euVehicleCategory: (viewableTechRecord as TechRecordType<'get'>)?.techRecord_euVehicleCategory ?? null, + vehicleSize: + viewableTechRecord?.techRecord_vehicleType === 'psv' + ? viewableTechRecord?.techRecord_vehicleSize + : undefined, + vehicleConfiguration: + (viewableTechRecord as TechRecordType<'get'>)?.techRecord_vehicleConfiguration ?? null, + vehicleClass: + (viewableTechRecord?.techRecord_vehicleType === 'psv' || + viewableTechRecord?.techRecord_vehicleType === 'trl' || + viewableTechRecord?.techRecord_vehicleType === 'hgv' || + viewableTechRecord?.techRecord_vehicleType === 'motorcycle') && + 'techRecord_vehicleClass_code' in viewableTechRecord + ? { + code: viewableTechRecord?.techRecord_vehicleClass_code, + description: viewableTechRecord?.techRecord_vehicleClass_description, + } + : null, + vehicleSubclass: + viewableTechRecord && 'techRecord_vehicleSubclass' in viewableTechRecord + ? viewableTechRecord.techRecord_vehicleSubclass + : null, + noOfAxles: viewableTechRecord?.techRecord_noOfAxles ?? 0, + numberOfWheelsDriven: + viewableTechRecord && 'techRecord_numberOfWheelsDriven' in viewableTechRecord + ? viewableTechRecord.techRecord_numberOfWheelsDriven + : null, + testStatus: 'submitted', + regnDate: viewableTechRecord?.techRecord_regnDate, + numberOfSeats: + ((viewableTechRecord as VehicleType<'psv'>)?.techRecord_seatsLowerDeck ?? 0) + + ((viewableTechRecord as VehicleType<'psv'>)?.techRecord_seatsUpperDeck ?? 0), + reasonForCancellation: '', + createdAt: now.toISOString(), + lastUpdatedAt: now.toISOString(), + firstUseDate: + viewableTechRecord?.techRecord_vehicleType === 'trl' ? viewableTechRecord?.techRecord_firstUseDate : null, + createdByName: user.name, + createdById: user.oid, + lastUpdatedByName: user.name, + lastUpdatedById: user.oid, + typeOfTest: TypeOfTest.CONTINGENCY, + source: 'vtm', + make: getBodyMake(viewableTechRecord), + model: getBodyModel(viewableTechRecord), + bodyType: getBodyType(viewableTechRecord), + testTypes: [ + { + testResult: 'pass', + prohibitionIssued: null, + additionalCommentsForAbandon: null, + } as TestType, + ], + } as Partial; + }) + ); + }), + tap((testResult) => { + store.dispatch(initialContingencyTest({ testResult })); + }), + take(1), + map(() => { + return true; + }), + catchError(() => { + return of(false); + }) + ); }; export function getBodyMake(techRecord: V3TechRecordModel | undefined) { - if (techRecord?.techRecord_vehicleType === 'psv') { - return techRecord.techRecord_bodyMake; - } + if (techRecord?.techRecord_vehicleType === 'psv') { + return techRecord.techRecord_bodyMake; + } - if (techRecord?.techRecord_vehicleType === 'hgv' || techRecord?.techRecord_vehicleType === 'trl') { - return techRecord.techRecord_make; - } + if (techRecord?.techRecord_vehicleType === 'hgv' || techRecord?.techRecord_vehicleType === 'trl') { + return techRecord.techRecord_make; + } - return null; + return null; } export function getBodyModel(techRecord: V3TechRecordModel | undefined) { - if (techRecord?.techRecord_vehicleType === 'psv') { - return techRecord.techRecord_bodyModel; - } + if (techRecord?.techRecord_vehicleType === 'psv') { + return techRecord.techRecord_bodyModel ? techRecord.techRecord_bodyModel : null; + } - if (techRecord?.techRecord_vehicleType === 'hgv' || techRecord?.techRecord_vehicleType === 'trl') { - return techRecord.techRecord_model; - } + if (techRecord?.techRecord_vehicleType === 'hgv' || techRecord?.techRecord_vehicleType === 'trl') { + return techRecord.techRecord_model; + } - return null; + return null; } export function getBodyType(techRecord: V3TechRecordModel | undefined) { - const vehicleType = techRecord?.techRecord_vehicleType; + const vehicleType = techRecord?.techRecord_vehicleType; - if (!vehicleType || (vehicleType !== 'hgv' && vehicleType !== 'psv' && vehicleType !== 'trl')) return null; + if (!vehicleType || (vehicleType !== 'hgv' && vehicleType !== 'psv' && vehicleType !== 'trl')) return null; - return { - code: techRecord.techRecord_bodyType_code, - description: techRecord.techRecord_bodyType_description, - }; + return { + code: techRecord.techRecord_bodyType_code, + description: techRecord.techRecord_bodyType_description, + }; } diff --git a/src/app/resolvers/defects-taxonomy/defects-taxonomy.resolver.spec.ts b/src/app/resolvers/defects-taxonomy/defects-taxonomy.resolver.spec.ts index 82efbdcb20..62156ad95a 100644 --- a/src/app/resolvers/defects-taxonomy/defects-taxonomy.resolver.spec.ts +++ b/src/app/resolvers/defects-taxonomy/defects-taxonomy.resolver.spec.ts @@ -1,68 +1,68 @@ import { TestBed } from '@angular/core/testing'; +import { ActivatedRouteSnapshot, ResolveFn, RouterStateSnapshot } from '@angular/router'; import { provideMockActions } from '@ngrx/effects/testing'; import { Action } from '@ngrx/store'; import { MockStore, provideMockStore } from '@ngrx/store/testing'; -import { initialAppState, State } from '@store/.'; +import { State, initialAppState } from '@store/.'; import { fetchDefects, fetchDefectsFailed, fetchDefectsSuccess } from '@store/defects'; import { Observable } from 'rxjs'; import { TestScheduler } from 'rxjs/testing'; -import { ActivatedRouteSnapshot, ResolveFn, RouterStateSnapshot } from '@angular/router'; import { defectsTaxonomyResolver } from './defects-taxonomy.resolver'; describe('DefectsTaxonomyResolver', () => { - let resolver: ResolveFn; - let actions$ = new Observable(); - let testScheduler: TestScheduler; - let store: MockStore; + let resolver: ResolveFn; + let actions$ = new Observable(); + let testScheduler: TestScheduler; + let store: MockStore; - beforeEach(() => { - TestBed.configureTestingModule({ - providers: [provideMockStore({ initialState: initialAppState }), provideMockActions(() => actions$)], - }); - resolver = (...resolverParameters) => - TestBed.runInInjectionContext(() => defectsTaxonomyResolver(...resolverParameters)); - store = TestBed.inject(MockStore); - }); + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [provideMockStore({ initialState: initialAppState }), provideMockActions(() => actions$)], + }); + resolver = (...resolverParameters) => + TestBed.runInInjectionContext(() => defectsTaxonomyResolver(...resolverParameters)); + store = TestBed.inject(MockStore); + }); - beforeEach(() => { - testScheduler = new TestScheduler((actual, expected) => { - expect(actual).toEqual(expected); - }); - }); + beforeEach(() => { + testScheduler = new TestScheduler((actual, expected) => { + expect(actual).toEqual(expected); + }); + }); - it('should be created', () => { - expect(resolver).toBeTruthy(); - }); + it('should be created', () => { + expect(resolver).toBeTruthy(); + }); - describe('fetch test types', () => { - it('should resolve to true when all actions are success type', () => { - const dispatchSpy = jest.spyOn(store, 'dispatch'); - const result = TestBed.runInInjectionContext( - () => resolver({} as ActivatedRouteSnapshot, {} as RouterStateSnapshot), - ) as Observable; - testScheduler.run(({ hot, expectObservable }) => { - actions$ = hot('-a', { a: fetchDefectsSuccess }); - expectObservable(result).toBe('-(b|)', { - b: true, - }); - }); + describe('fetch test types', () => { + it('should resolve to true when all actions are success type', () => { + const dispatchSpy = jest.spyOn(store, 'dispatch'); + const result = TestBed.runInInjectionContext(() => + resolver({} as ActivatedRouteSnapshot, {} as RouterStateSnapshot) + ) as Observable; + testScheduler.run(({ hot, expectObservable }) => { + actions$ = hot('-a', { a: fetchDefectsSuccess }); + expectObservable(result).toBe('-(b|)', { + b: true, + }); + }); - expect(dispatchSpy).toHaveBeenCalledWith(fetchDefects()); - }); + expect(dispatchSpy).toHaveBeenCalledWith(fetchDefects()); + }); - it('should resolve to false when one or more actions are of failure type', () => { - const dispatchSpy = jest.spyOn(store, 'dispatch'); - const result = TestBed.runInInjectionContext( - () => resolver({} as ActivatedRouteSnapshot, {} as RouterStateSnapshot), - ) as Observable; - testScheduler.run(({ hot, expectObservable }) => { - actions$ = hot('-a', { a: fetchDefectsFailed }); - expectObservable(result).toBe('-(b|)', { - b: false, - }); - }); + it('should resolve to false when one or more actions are of failure type', () => { + const dispatchSpy = jest.spyOn(store, 'dispatch'); + const result = TestBed.runInInjectionContext(() => + resolver({} as ActivatedRouteSnapshot, {} as RouterStateSnapshot) + ) as Observable; + testScheduler.run(({ hot, expectObservable }) => { + actions$ = hot('-a', { a: fetchDefectsFailed }); + expectObservable(result).toBe('-(b|)', { + b: false, + }); + }); - expect(dispatchSpy).toHaveBeenCalledWith(fetchDefects()); - }); - }); + expect(dispatchSpy).toHaveBeenCalledWith(fetchDefects()); + }); + }); }); diff --git a/src/app/resolvers/defects-taxonomy/defects-taxonomy.resolver.ts b/src/app/resolvers/defects-taxonomy/defects-taxonomy.resolver.ts index 54583e71be..b774614d82 100644 --- a/src/app/resolvers/defects-taxonomy/defects-taxonomy.resolver.ts +++ b/src/app/resolvers/defects-taxonomy/defects-taxonomy.resolver.ts @@ -2,22 +2,17 @@ import { inject } from '@angular/core'; import { ResolveFn } from '@angular/router'; import { Actions, ofType } from '@ngrx/effects'; import { Store } from '@ngrx/store'; -import { - DefectsState, - fetchDefects, - fetchDefectsFailed, - fetchDefectsSuccess, -} from '@store/defects'; +import { DefectsState, fetchDefects, fetchDefectsFailed, fetchDefectsSuccess } from '@store/defects'; import { map, take } from 'rxjs'; export const defectsTaxonomyResolver: ResolveFn = () => { - const store: Store = inject(Store); - const action$: Actions = inject(Actions); - store.dispatch(fetchDefects()); + const store: Store = inject(Store); + const action$: Actions = inject(Actions); + store.dispatch(fetchDefects()); - return action$.pipe( - ofType(fetchDefectsSuccess, fetchDefectsFailed), - take(1), - map((action) => action.type === fetchDefectsSuccess.type), - ); + return action$.pipe( + ofType(fetchDefectsSuccess, fetchDefectsFailed), + take(1), + map((action) => action.type === fetchDefectsSuccess.type) + ); }; diff --git a/src/app/resolvers/required-standards/required-standards.resolver.spec.ts b/src/app/resolvers/required-standards/required-standards.resolver.spec.ts index 105f0117bf..4f807c3f08 100644 --- a/src/app/resolvers/required-standards/required-standards.resolver.spec.ts +++ b/src/app/resolvers/required-standards/required-standards.resolver.spec.ts @@ -6,9 +6,9 @@ import { Action } from '@ngrx/store'; import { MockStore, provideMockStore } from '@ngrx/store/testing'; import { State, initialAppState } from '@store/index'; import { - getRequiredStandards, - getRequiredStandardsFailure, - getRequiredStandardsSuccess, + getRequiredStandards, + getRequiredStandardsFailure, + getRequiredStandardsSuccess, } from '@store/required-standards/actions/required-standards.actions'; import { testResultInEdit } from '@store/test-records/selectors/test-records.selectors'; import { Observable } from 'rxjs'; @@ -16,61 +16,61 @@ import { TestScheduler } from 'rxjs/testing'; import { requiredStandardsResolver } from './required-standards.resolver'; describe('RequiredStandardsResolver', () => { - let resolver: ResolveFn; - let actions$ = new Observable(); - let testScheduler: TestScheduler; - let store: MockStore; + let resolver: ResolveFn; + let actions$ = new Observable(); + let testScheduler: TestScheduler; + let store: MockStore; - beforeEach(() => { - TestBed.configureTestingModule({ - providers: [provideMockStore({ initialState: initialAppState }), provideMockActions(() => actions$)], - }); - resolver = (...resolverParameters) => - TestBed.runInInjectionContext(() => requiredStandardsResolver(...resolverParameters)); - store = TestBed.inject(MockStore); - }); + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [provideMockStore({ initialState: initialAppState }), provideMockActions(() => actions$)], + }); + resolver = (...resolverParameters) => + TestBed.runInInjectionContext(() => requiredStandardsResolver(...resolverParameters)); + store = TestBed.inject(MockStore); + }); - beforeEach(() => { - testScheduler = new TestScheduler((actual, expected) => { - expect(actual).toEqual(expected); - }); - }); + beforeEach(() => { + testScheduler = new TestScheduler((actual, expected) => { + expect(actual).toEqual(expected); + }); + }); - it('should be created', () => { - expect(resolver).toBeTruthy(); - }); + it('should be created', () => { + expect(resolver).toBeTruthy(); + }); - describe('get required standards', () => { - it('should resolve to true when all actions are success type', () => { - store.overrideSelector(testResultInEdit, { euVehicleCategory: 'm1' } as unknown as TestResultModel); - const dispatchSpy = jest.spyOn(store, 'dispatch'); - const result = TestBed.runInInjectionContext( - () => resolver({} as ActivatedRouteSnapshot, {} as RouterStateSnapshot), - ) as Observable; - testScheduler.run(({ hot, expectObservable }) => { - actions$ = hot('-a', { a: getRequiredStandardsSuccess }); - expectObservable(result).toBe('-(b|)', { - b: true, - }); - }); + describe('get required standards', () => { + it('should resolve to true when all actions are success type', () => { + store.overrideSelector(testResultInEdit, { euVehicleCategory: 'm1' } as unknown as TestResultModel); + const dispatchSpy = jest.spyOn(store, 'dispatch'); + const result = TestBed.runInInjectionContext(() => + resolver({} as ActivatedRouteSnapshot, {} as RouterStateSnapshot) + ) as Observable; + testScheduler.run(({ hot, expectObservable }) => { + actions$ = hot('-a', { a: getRequiredStandardsSuccess }); + expectObservable(result).toBe('-(b|)', { + b: true, + }); + }); - expect(dispatchSpy).toHaveBeenCalledWith(getRequiredStandards({ euVehicleCategory: 'm1' })); - }); + expect(dispatchSpy).toHaveBeenCalledWith(getRequiredStandards({ euVehicleCategory: 'm1' })); + }); - it('should resolve to false when one or more actions are of failure type', () => { - store.overrideSelector(testResultInEdit, { euVehicleCategory: 'm1' } as unknown as TestResultModel); - const dispatchSpy = jest.spyOn(store, 'dispatch'); - const result = TestBed.runInInjectionContext( - () => resolver({} as ActivatedRouteSnapshot, {} as RouterStateSnapshot), - ) as Observable; - testScheduler.run(({ hot, expectObservable }) => { - actions$ = hot('-a', { a: getRequiredStandardsFailure }); - expectObservable(result).toBe('-(b|)', { - b: false, - }); - }); + it('should resolve to false when one or more actions are of failure type', () => { + store.overrideSelector(testResultInEdit, { euVehicleCategory: 'm1' } as unknown as TestResultModel); + const dispatchSpy = jest.spyOn(store, 'dispatch'); + const result = TestBed.runInInjectionContext(() => + resolver({} as ActivatedRouteSnapshot, {} as RouterStateSnapshot) + ) as Observable; + testScheduler.run(({ hot, expectObservable }) => { + actions$ = hot('-a', { a: getRequiredStandardsFailure }); + expectObservable(result).toBe('-(b|)', { + b: false, + }); + }); - expect(dispatchSpy).toHaveBeenCalledWith(getRequiredStandards({ euVehicleCategory: 'm1' })); - }); - }); + expect(dispatchSpy).toHaveBeenCalledWith(getRequiredStandards({ euVehicleCategory: 'm1' })); + }); + }); }); diff --git a/src/app/resolvers/required-standards/required-standards.resolver.ts b/src/app/resolvers/required-standards/required-standards.resolver.ts index 393316a573..8ea0d5a753 100644 --- a/src/app/resolvers/required-standards/required-standards.resolver.ts +++ b/src/app/resolvers/required-standards/required-standards.resolver.ts @@ -3,24 +3,24 @@ import { ResolveFn } from '@angular/router'; import { Actions, ofType } from '@ngrx/effects'; import { Store, select } from '@ngrx/store'; import { - getRequiredStandards, - getRequiredStandardsFailure, - getRequiredStandardsSuccess, + getRequiredStandards, + getRequiredStandardsFailure, + getRequiredStandardsSuccess, } from '@store/required-standards/actions/required-standards.actions'; import { RequiredStandardState } from '@store/required-standards/reducers/required-standards.reducer'; import { testResultInEdit } from '@store/test-records/selectors/test-records.selectors'; import { map, take } from 'rxjs'; export const requiredStandardsResolver: ResolveFn = () => { - const store: Store = inject(Store); - const action$: Actions = inject(Actions); - store.pipe(select(testResultInEdit), take(1)).subscribe((editingTestResult) => { - store.dispatch(getRequiredStandards({ euVehicleCategory: editingTestResult?.euVehicleCategory ?? '' })); - }); + const store: Store = inject(Store); + const action$: Actions = inject(Actions); + store.pipe(select(testResultInEdit), take(1)).subscribe((editingTestResult) => { + store.dispatch(getRequiredStandards({ euVehicleCategory: editingTestResult?.euVehicleCategory ?? '' })); + }); - return action$.pipe( - ofType(getRequiredStandardsSuccess, getRequiredStandardsFailure), - take(1), - map((action) => action.type === getRequiredStandardsSuccess.type), - ); + return action$.pipe( + ofType(getRequiredStandardsSuccess, getRequiredStandardsFailure), + take(1), + map((action) => action.type === getRequiredStandardsSuccess.type) + ); }; diff --git a/src/app/resolvers/tech-record-clean/tech-record-clean.resolver.spec.ts b/src/app/resolvers/tech-record-clean/tech-record-clean.resolver.spec.ts index 6a8a02e543..a7d11b552e 100644 --- a/src/app/resolvers/tech-record-clean/tech-record-clean.resolver.spec.ts +++ b/src/app/resolvers/tech-record-clean/tech-record-clean.resolver.spec.ts @@ -10,99 +10,111 @@ import { Observable, firstValueFrom, of } from 'rxjs'; import { techRecordCleanResolver } from './tech-record-clean.resolver'; describe('techRecordCleanResolver', () => { - const executeResolver: ResolveFn> = (...resolverParameters) => - TestBed.runInInjectionContext(() => techRecordCleanResolver(...resolverParameters)); + const executeResolver: ResolveFn> = (...resolverParameters) => + TestBed.runInInjectionContext(() => techRecordCleanResolver(...resolverParameters)); - const actions$ = new Observable(); - const mockActivatedRouteSnapshot = jest.fn; - let store: MockStore; - let activatedRouteSnapshot: ActivatedRouteSnapshot; + const actions$ = new Observable(); + const mockActivatedRouteSnapshot = jest.fn; + let store: MockStore; + let activatedRouteSnapshot: ActivatedRouteSnapshot; - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [RouterTestingModule], - providers: [ - provideMockStore({ initialState: initialAppState }), - provideMockActions(() => actions$), - { provide: ActivatedRouteSnapshot, useValue: mockActivatedRouteSnapshot }, - ], - }); + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [RouterTestingModule], + providers: [ + provideMockStore({ initialState: initialAppState }), + provideMockActions(() => actions$), + { provide: ActivatedRouteSnapshot, useValue: mockActivatedRouteSnapshot }, + ], + }); - store = TestBed.inject(MockStore); - activatedRouteSnapshot = TestBed.inject(ActivatedRouteSnapshot); - activatedRouteSnapshot.data = { - isEditing: true, - }; - }); + store = TestBed.inject(MockStore); + activatedRouteSnapshot = TestBed.inject(ActivatedRouteSnapshot); + activatedRouteSnapshot.data = { + isEditing: true, + }; + }); - it('should be created', () => { - expect(executeResolver).toBeTruthy(); - }); + it('should be created', () => { + expect(executeResolver).toBeTruthy(); + }); - describe('fetch tech record', () => { - it('should update the editing tech record when approval type = Small series and approval number is in NKS format', async () => { - const dispatchSpy = jest.spyOn(store, 'dispatch'); - jest.spyOn(store, 'select').mockReturnValue(of({ - techRecord_vehicleType: 'hgv', - techRecord_approvalType: 'Small series' as ApprovalType, - techRecord_approvalTypeNumber: '811*NKS*666666', - })); - const result = executeResolver(activatedRouteSnapshot, {} as RouterStateSnapshot) as Observable; - const resolveResult = await firstValueFrom(result); + describe('fetch tech record', () => { + it('should update the editing tech record when approval type = Small series and approval number is in NKS format', async () => { + const dispatchSpy = jest.spyOn(store, 'dispatch'); + jest.spyOn(store, 'select').mockReturnValue( + of({ + techRecord_vehicleType: 'hgv', + techRecord_approvalType: 'Small series' as ApprovalType, + techRecord_approvalTypeNumber: '811*NKS*666666', + }) + ); + const result = executeResolver(activatedRouteSnapshot, {} as RouterStateSnapshot) as Observable; + const resolveResult = await firstValueFrom(result); - expect(resolveResult).toBe(true); - expect(dispatchSpy).toHaveBeenCalledTimes(1); - expect(dispatchSpy).toHaveBeenCalledWith(expect.objectContaining({ - vehicleTechRecord: { - techRecord_approvalType: ApprovalType.SMALL_SERIES_NKS, - techRecord_approvalTypeNumber: '811*NKS*666666', - techRecord_vehicleType: 'hgv', - }, - })); - }); + expect(resolveResult).toBe(true); + expect(dispatchSpy).toHaveBeenCalledTimes(1); + expect(dispatchSpy).toHaveBeenCalledWith( + expect.objectContaining({ + vehicleTechRecord: { + techRecord_approvalType: ApprovalType.SMALL_SERIES_NKS, + techRecord_approvalTypeNumber: '811*NKS*666666', + techRecord_vehicleType: 'hgv', + }, + }) + ); + }); - it('should update the editing tech record when approval type = Small series and approval number is in NKSXX format', async () => { - const dispatchSpy = jest.spyOn(store, 'dispatch'); - jest.spyOn(store, 'select').mockReturnValue(of({ - techRecord_vehicleType: 'hgv', - techRecord_approvalType: 'Small series' as ApprovalType, - techRecord_approvalTypeNumber: '811*NKSXX/1234*666666', - })); - const result = executeResolver(activatedRouteSnapshot, {} as RouterStateSnapshot) as Observable; - const resolveResult = await firstValueFrom(result); + it('should update the editing tech record when approval type = Small series and approval number is in NKSXX format', async () => { + const dispatchSpy = jest.spyOn(store, 'dispatch'); + jest.spyOn(store, 'select').mockReturnValue( + of({ + techRecord_vehicleType: 'hgv', + techRecord_approvalType: 'Small series' as ApprovalType, + techRecord_approvalTypeNumber: '811*NKSXX/1234*666666', + }) + ); + const result = executeResolver(activatedRouteSnapshot, {} as RouterStateSnapshot) as Observable; + const resolveResult = await firstValueFrom(result); - expect(resolveResult).toBe(true); - expect(dispatchSpy).toHaveBeenCalledTimes(1); - expect(dispatchSpy).toHaveBeenCalledWith(expect.objectContaining({ - vehicleTechRecord: { - techRecord_approvalType: ApprovalType.SMALL_SERIES_NKSXX, - techRecord_approvalTypeNumber: '811*NKSXX/1234*666666', - techRecord_vehicleType: 'hgv', - }, - })); - }); + expect(resolveResult).toBe(true); + expect(dispatchSpy).toHaveBeenCalledTimes(1); + expect(dispatchSpy).toHaveBeenCalledWith( + expect.objectContaining({ + vehicleTechRecord: { + techRecord_approvalType: ApprovalType.SMALL_SERIES_NKSXX, + techRecord_approvalTypeNumber: '811*NKSXX/1234*666666', + techRecord_vehicleType: 'hgv', + }, + }) + ); + }); - it('should not update the editing tech record when the approval type is Small series, but the approval number is invalid', async () => { - const dispatchSpy = jest.spyOn(store, 'dispatch'); - jest.spyOn(store, 'select').mockReturnValue(of({ - techRecord_vehicleType: 'hgv', - techRecord_approvalType: 'Small series' as ApprovalType, - techRecord_approvalTypeNumber: 'INVALID NUMBER', - })); - const result = executeResolver(activatedRouteSnapshot, {} as RouterStateSnapshot) as Observable; - const resolveResult = await firstValueFrom(result); + it('should not update the editing tech record when the approval type is Small series, but the approval number is invalid', async () => { + const dispatchSpy = jest.spyOn(store, 'dispatch'); + jest.spyOn(store, 'select').mockReturnValue( + of({ + techRecord_vehicleType: 'hgv', + techRecord_approvalType: 'Small series' as ApprovalType, + techRecord_approvalTypeNumber: 'INVALID NUMBER', + }) + ); + const result = executeResolver(activatedRouteSnapshot, {} as RouterStateSnapshot) as Observable; + const resolveResult = await firstValueFrom(result); - expect(resolveResult).toBe(true); - expect(dispatchSpy).toHaveBeenCalledTimes(1); + expect(resolveResult).toBe(true); + expect(dispatchSpy).toHaveBeenCalledTimes(1); - // This bad data gets nulled by the tech-record-validate resolver - expect(dispatchSpy).toHaveBeenCalledWith(expect.objectContaining({ - vehicleTechRecord: { - techRecord_approvalType: 'Small series' as ApprovalType, - techRecord_approvalTypeNumber: 'INVALID NUMBER', - techRecord_vehicleType: 'hgv', - }, - })); - }); - }); + // This bad data gets nulled by the tech-record-validate resolver + expect(dispatchSpy).toHaveBeenCalledWith( + expect.objectContaining({ + vehicleTechRecord: { + techRecord_approvalType: 'Small series' as ApprovalType, + techRecord_approvalTypeNumber: 'INVALID NUMBER', + techRecord_vehicleType: 'hgv', + }, + }) + ); + }); + }); }); diff --git a/src/app/resolvers/tech-record-clean/tech-record-clean.resolver.ts b/src/app/resolvers/tech-record-clean/tech-record-clean.resolver.ts index a733a0cf2f..6232501f60 100644 --- a/src/app/resolvers/tech-record-clean/tech-record-clean.resolver.ts +++ b/src/app/resolvers/tech-record-clean/tech-record-clean.resolver.ts @@ -6,43 +6,41 @@ import { VehicleTypes } from '@models/vehicle-tech-record.model'; import { Store } from '@ngrx/store'; import { State } from '@store/index'; import { selectTechRecord, updateEditingTechRecord } from '@store/technical-records'; -import { - Observable, map, take, tap, -} from 'rxjs'; +import { Observable, map, take, tap } from 'rxjs'; export const techRecordCleanResolver: ResolveFn> = (route) => { - const store = inject(Store); + const store = inject(Store); - return store.select(selectTechRecord).pipe( - take(1), - map((vehicleTechRecord) => vehicleTechRecord as TechRecordType<'put'>), - map((vehicleTechRecord) => cleanseApprovalType(vehicleTechRecord, route)), - tap((vehicleTechRecord) => store.dispatch(updateEditingTechRecord({ vehicleTechRecord }))), - map(() => true), - ); + return store.select(selectTechRecord).pipe( + take(1), + map((vehicleTechRecord) => vehicleTechRecord as TechRecordType<'put'>), + map((vehicleTechRecord) => cleanseApprovalType(vehicleTechRecord, route)), + tap((vehicleTechRecord) => store.dispatch(updateEditingTechRecord({ vehicleTechRecord }))), + map(() => true) + ); }; export const cleanseApprovalType = (record: TechRecordType<'put'>, route: ActivatedRouteSnapshot) => { - if (!route.data['isEditing']) return record; + if (!route.data['isEditing']) return record; - const type = record.techRecord_vehicleType; - if (type === VehicleTypes.HGV || type === VehicleTypes.PSV || type === VehicleTypes.TRL) { - const approvalType = record.techRecord_approvalType; - const approvalNumber = record.techRecord_approvalTypeNumber; - if (approvalType?.toString() === 'Small series' && approvalNumber) { - // infer new approval type based on format of approval type number - const patterns = new Map([ - [ApprovalType.SMALL_SERIES_NKSXX, /^(.?)11\*NKS(.{0,2})\/(.{0,4})\*(.{0,6})$/i], - [ApprovalType.SMALL_SERIES_NKS, /^(.?)11\*NKS\*(.{0,6})$/i], - ]); + const type = record.techRecord_vehicleType; + if (type === VehicleTypes.HGV || type === VehicleTypes.PSV || type === VehicleTypes.TRL) { + const approvalType = record.techRecord_approvalType; + const approvalNumber = record.techRecord_approvalTypeNumber; + if (approvalType?.toString() === 'Small series' && approvalNumber) { + // infer new approval type based on format of approval type number + const patterns = new Map([ + [ApprovalType.SMALL_SERIES_NKSXX, /^(.?)11\*NKS(.{0,2})\/(.{0,4})\*(.{0,6})$/i], + [ApprovalType.SMALL_SERIES_NKS, /^(.?)11\*NKS\*(.{0,6})$/i], + ]); - patterns.forEach((value, key) => { - if (value.test(approvalNumber)) { - record.techRecord_approvalType = key; - } - }); - } - } + patterns.forEach((value, key) => { + if (value.test(approvalNumber)) { + record.techRecord_approvalType = key; + } + }); + } + } - return record; + return record; }; diff --git a/src/app/resolvers/tech-record-data/tech-record-data.resolver.spec.ts b/src/app/resolvers/tech-record-data/tech-record-data.resolver.spec.ts index 7523c09da8..798a6ad0ad 100644 --- a/src/app/resolvers/tech-record-data/tech-record-data.resolver.spec.ts +++ b/src/app/resolvers/tech-record-data/tech-record-data.resolver.spec.ts @@ -6,34 +6,34 @@ import { ReferenceDataService } from '@services/reference-data/reference-data.se import { techRecordDataResolver } from './tech-record-data.resolver'; describe('techRecordDataResolver', () => { - let referenceDataService: ReferenceDataService; - - const routeSnapshot = {} as ActivatedRouteSnapshot; - const routerStateSnapshot = {} as RouterStateSnapshot; - - const executeResolver: ResolveFn = (...resolverParameters) => - TestBed.runInInjectionContext(() => techRecordDataResolver(...resolverParameters)); - - beforeEach(() => { - TestBed.configureTestingModule({ - providers: [{ provide: ReferenceDataService, useValue: { loadReferenceData: jest.fn() } }], - }); - referenceDataService = TestBed.inject(ReferenceDataService); - }); - - it('should be created', () => { - expect(executeResolver).toBeTruthy(); - }); - - it('should attempt to load the tyres ref data', () => { - const spy = jest.spyOn(referenceDataService, 'loadReferenceData'); - void executeResolver(routeSnapshot, routerStateSnapshot); - expect(spy).toHaveBeenCalledWith(ReferenceDataResourceType.Tyres); - }); - - it('should attempt to load the tyres load index ref data', () => { - const spy = jest.spyOn(referenceDataService, 'loadReferenceData'); - void executeResolver(routeSnapshot, routerStateSnapshot); - expect(spy).toHaveBeenCalledWith(ReferenceDataResourceType.TyreLoadIndex); - }); + let referenceDataService: ReferenceDataService; + + const routeSnapshot = {} as ActivatedRouteSnapshot; + const routerStateSnapshot = {} as RouterStateSnapshot; + + const executeResolver: ResolveFn = (...resolverParameters) => + TestBed.runInInjectionContext(() => techRecordDataResolver(...resolverParameters)); + + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [{ provide: ReferenceDataService, useValue: { loadReferenceData: jest.fn() } }], + }); + referenceDataService = TestBed.inject(ReferenceDataService); + }); + + it('should be created', () => { + expect(executeResolver).toBeTruthy(); + }); + + it('should attempt to load the tyres ref data', () => { + const spy = jest.spyOn(referenceDataService, 'loadReferenceData'); + void executeResolver(routeSnapshot, routerStateSnapshot); + expect(spy).toHaveBeenCalledWith(ReferenceDataResourceType.Tyres); + }); + + it('should attempt to load the tyres load index ref data', () => { + const spy = jest.spyOn(referenceDataService, 'loadReferenceData'); + void executeResolver(routeSnapshot, routerStateSnapshot); + expect(spy).toHaveBeenCalledWith(ReferenceDataResourceType.TyreLoadIndex); + }); }); diff --git a/src/app/resolvers/tech-record-data/tech-record-data.resolver.ts b/src/app/resolvers/tech-record-data/tech-record-data.resolver.ts index 81aef69d42..7cbd2c4991 100644 --- a/src/app/resolvers/tech-record-data/tech-record-data.resolver.ts +++ b/src/app/resolvers/tech-record-data/tech-record-data.resolver.ts @@ -4,9 +4,9 @@ import { ReferenceDataResourceType } from '@models/reference-data.model'; import { ReferenceDataService } from '@services/reference-data/reference-data.service'; export const techRecordDataResolver: ResolveFn = () => { - const referenceDataService = inject(ReferenceDataService); - referenceDataService.loadReferenceData(ReferenceDataResourceType.Tyres); - referenceDataService.loadReferenceData(ReferenceDataResourceType.TyreLoadIndex); + const referenceDataService = inject(ReferenceDataService); + referenceDataService.loadReferenceData(ReferenceDataResourceType.Tyres); + referenceDataService.loadReferenceData(ReferenceDataResourceType.TyreLoadIndex); - return true; + return true; }; diff --git a/src/app/resolvers/tech-record-validate/tech-record-validate.resolver.spec.ts b/src/app/resolvers/tech-record-validate/tech-record-validate.resolver.spec.ts index e78cf3604d..47a1b22a9c 100644 --- a/src/app/resolvers/tech-record-validate/tech-record-validate.resolver.spec.ts +++ b/src/app/resolvers/tech-record-validate/tech-record-validate.resolver.spec.ts @@ -1,11 +1,6 @@ import { TestBed } from '@angular/core/testing'; -import { - ActivatedRouteSnapshot, - ResolveFn, - Router, - RouterStateSnapshot, -} from '@angular/router'; +import { ActivatedRouteSnapshot, ResolveFn, Router, RouterStateSnapshot } from '@angular/router'; import { RouterTestingModule } from '@angular/router/testing'; import { provideMockActions } from '@ngrx/effects/testing'; import { Action } from '@ngrx/store'; @@ -15,153 +10,181 @@ import { Observable, firstValueFrom, of } from 'rxjs'; import { techRecordValidateResolver } from './tech-record-validate.resolver'; describe('TechRecordViewResolver', () => { - let resolver: ResolveFn; - const actions$ = new Observable(); - const mockSnapshot = jest.fn; - let store: MockStore; - let router: Router; + let resolver: ResolveFn; + const actions$ = new Observable(); + const mockSnapshot = jest.fn; + let store: MockStore; + let router: Router; - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [RouterTestingModule], - providers: [ - provideMockStore({ initialState: initialAppState }), - provideMockActions(() => actions$), - { provide: RouterStateSnapshot, useValue: mockSnapshot }, - ], - }); - resolver = (...resolverParameters) => - TestBed.runInInjectionContext(() => techRecordValidateResolver(...resolverParameters)); - store = TestBed.inject(MockStore); - router = TestBed.inject(Router); - }); + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [RouterTestingModule], + providers: [ + provideMockStore({ initialState: initialAppState }), + provideMockActions(() => actions$), + { provide: RouterStateSnapshot, useValue: mockSnapshot }, + ], + }); + resolver = (...resolverParameters) => + TestBed.runInInjectionContext(() => techRecordValidateResolver(...resolverParameters)); + store = TestBed.inject(MockStore); + router = TestBed.inject(Router); + }); - it('should be created', () => { - expect(resolver).toBeTruthy(); - }); + it('should be created', () => { + expect(resolver).toBeTruthy(); + }); - describe('fetch tech record result', () => { - it('should dispatch a tech record with null if hgv vehicle configuration invalid', async () => { - const dispatchSpy = jest.spyOn(store, 'dispatch'); - jest.spyOn(store, 'select').mockReturnValue(of({ - techRecord_vehicleType: 'hgv', - techRecord_vehicleConfiguration: 'semi-trailer', - techRecord_euVehicleCategory: 'o1', - })); - const result = TestBed.runInInjectionContext( - () => resolver({} as ActivatedRouteSnapshot, {} as RouterStateSnapshot), - ) as Observable; - const resolveResult = await firstValueFrom(result); + describe('fetch tech record result', () => { + it('should dispatch a tech record with null if hgv vehicle configuration invalid', async () => { + const dispatchSpy = jest.spyOn(store, 'dispatch'); + jest.spyOn(store, 'select').mockReturnValue( + of({ + techRecord_vehicleType: 'hgv', + techRecord_vehicleConfiguration: 'semi-trailer', + techRecord_euVehicleCategory: 'o1', + }) + ); + const result = TestBed.runInInjectionContext(() => + resolver({} as ActivatedRouteSnapshot, {} as RouterStateSnapshot) + ) as Observable; + const resolveResult = await firstValueFrom(result); - expect(resolveResult).toBe(true); - expect(dispatchSpy).toHaveBeenCalledTimes(1); - expect(dispatchSpy).toHaveBeenCalledWith(expect.objectContaining({ - vehicleTechRecord: { - techRecord_vehicleType: 'hgv', - techRecord_vehicleConfiguration: null, - techRecord_vehicleClass_description: 'heavy goods vehicle', - techRecord_euVehicleCategory: null, - }, - })); - }); - it('should dispatch a tech record with null if psv vehicle configuration invalid', async () => { - const dispatchSpy = jest.spyOn(store, 'dispatch'); - jest.spyOn(store, 'select').mockReturnValue(of({ - techRecord_vehicleType: 'psv', - techRecord_vehicleConfiguration: 'semi-trailer', - techRecord_euVehicleCategory: 'o1', - })); - const result = TestBed.runInInjectionContext( - () => resolver({} as ActivatedRouteSnapshot, {} as RouterStateSnapshot), - ) as Observable; - const resolveResult = await firstValueFrom(result); + expect(resolveResult).toBe(true); + expect(dispatchSpy).toHaveBeenCalledTimes(1); + expect(dispatchSpy).toHaveBeenCalledWith( + expect.objectContaining({ + vehicleTechRecord: { + techRecord_vehicleType: 'hgv', + techRecord_vehicleConfiguration: null, + techRecord_vehicleClass_description: 'heavy goods vehicle', + techRecord_euVehicleCategory: null, + }, + }) + ); + }); + it('should dispatch a tech record with null if psv vehicle configuration invalid', async () => { + const dispatchSpy = jest.spyOn(store, 'dispatch'); + jest.spyOn(store, 'select').mockReturnValue( + of({ + techRecord_vehicleType: 'psv', + techRecord_vehicleConfiguration: 'semi-trailer', + techRecord_euVehicleCategory: 'o1', + }) + ); + const result = TestBed.runInInjectionContext(() => + resolver({} as ActivatedRouteSnapshot, {} as RouterStateSnapshot) + ) as Observable; + const resolveResult = await firstValueFrom(result); - expect(resolveResult).toBe(true); - expect(dispatchSpy).toHaveBeenCalledTimes(1); - expect(dispatchSpy).toHaveBeenCalledWith(expect.objectContaining({ - vehicleTechRecord: { - techRecord_vehicleType: 'psv', - techRecord_vehicleConfiguration: null, - techRecord_euVehicleCategory: null, - }, - })); - }); - it('should dispatch a tech record with null if psv vehicle class is invalid', async () => { - const dispatchSpy = jest.spyOn(store, 'dispatch'); - jest.spyOn(store, 'select').mockReturnValue(of({ techRecord_vehicleType: 'psv', techRecord_vehicleClass_description: 'trailer' })); - const result = TestBed.runInInjectionContext( - () => resolver({} as ActivatedRouteSnapshot, {} as RouterStateSnapshot), - ) as Observable; - const resolveResult = await firstValueFrom(result); + expect(resolveResult).toBe(true); + expect(dispatchSpy).toHaveBeenCalledTimes(1); + expect(dispatchSpy).toHaveBeenCalledWith( + expect.objectContaining({ + vehicleTechRecord: { + techRecord_vehicleType: 'psv', + techRecord_vehicleConfiguration: null, + techRecord_euVehicleCategory: null, + }, + }) + ); + }); + it('should dispatch a tech record with null if psv vehicle class is invalid', async () => { + const dispatchSpy = jest.spyOn(store, 'dispatch'); + jest + .spyOn(store, 'select') + .mockReturnValue(of({ techRecord_vehicleType: 'psv', techRecord_vehicleClass_description: 'trailer' })); + const result = TestBed.runInInjectionContext(() => + resolver({} as ActivatedRouteSnapshot, {} as RouterStateSnapshot) + ) as Observable; + const resolveResult = await firstValueFrom(result); - expect(resolveResult).toBe(true); - expect(dispatchSpy).toHaveBeenCalledTimes(1); - expect(dispatchSpy).toHaveBeenCalledWith(expect.objectContaining({ - vehicleTechRecord: { techRecord_vehicleType: 'psv', techRecord_vehicleClass_description: null }, - })); - }); - it('should dispatch a tech record with null if trl vehicle configuration invalid', async () => { - const dispatchSpy = jest.spyOn(store, 'dispatch'); - jest.spyOn(store, 'select').mockReturnValue(of({ - techRecord_vehicleType: 'trl', - techRecord_vehicleConfiguration: 'rigid', - })); - const result = TestBed.runInInjectionContext( - () => resolver({} as ActivatedRouteSnapshot, {} as RouterStateSnapshot), - ) as Observable; - const resolveResult = await firstValueFrom(result); + expect(resolveResult).toBe(true); + expect(dispatchSpy).toHaveBeenCalledTimes(1); + expect(dispatchSpy).toHaveBeenCalledWith( + expect.objectContaining({ + vehicleTechRecord: { techRecord_vehicleType: 'psv', techRecord_vehicleClass_description: null }, + }) + ); + }); + it('should dispatch a tech record with null if trl vehicle configuration invalid', async () => { + const dispatchSpy = jest.spyOn(store, 'dispatch'); + jest.spyOn(store, 'select').mockReturnValue( + of({ + techRecord_vehicleType: 'trl', + techRecord_vehicleConfiguration: 'rigid', + }) + ); + const result = TestBed.runInInjectionContext(() => + resolver({} as ActivatedRouteSnapshot, {} as RouterStateSnapshot) + ) as Observable; + const resolveResult = await firstValueFrom(result); - expect(resolveResult).toBe(true); - expect(dispatchSpy).toHaveBeenCalledTimes(1); - expect(dispatchSpy).toHaveBeenCalledWith(expect.objectContaining({ - vehicleTechRecord: { - techRecord_vehicleType: 'trl', - techRecord_vehicleConfiguration: null, - techRecord_vehicleClass_description: 'trailer', - }, - })); - }); - it('should not dispatch a tech record with trl vehicle configuration is valid', async () => { - const dispatchSpy = jest.spyOn(store, 'dispatch'); - jest.spyOn(store, 'select').mockReturnValue(of({ - techRecord_vehicleType: 'trl', - techRecord_vehicleConfiguration: 'semi-trailer', - techRecord_vehicleClass_description: 'trailer', - techRecord_euVehicleCategory: 'o1', - })); - const result = TestBed.runInInjectionContext( - () => resolver({} as ActivatedRouteSnapshot, {} as RouterStateSnapshot), - ) as Observable; - const resolveResult = await firstValueFrom(result); + expect(resolveResult).toBe(true); + expect(dispatchSpy).toHaveBeenCalledTimes(1); + expect(dispatchSpy).toHaveBeenCalledWith( + expect.objectContaining({ + vehicleTechRecord: { + techRecord_vehicleType: 'trl', + techRecord_vehicleConfiguration: null, + techRecord_vehicleClass_description: 'trailer', + }, + }) + ); + }); + it('should not dispatch a tech record with trl vehicle configuration is valid', async () => { + const dispatchSpy = jest.spyOn(store, 'dispatch'); + jest.spyOn(store, 'select').mockReturnValue( + of({ + techRecord_vehicleType: 'trl', + techRecord_vehicleConfiguration: 'semi-trailer', + techRecord_vehicleClass_description: 'trailer', + techRecord_euVehicleCategory: 'o1', + }) + ); + const result = TestBed.runInInjectionContext(() => + resolver({} as ActivatedRouteSnapshot, {} as RouterStateSnapshot) + ) as Observable; + const resolveResult = await firstValueFrom(result); - expect(resolveResult).toBe(true); - expect(dispatchSpy).toHaveBeenCalledTimes(0); - }); - it('should dispatch a tech record with hgv if hgv vehicle class is invalid', async () => { - const dispatchSpy = jest.spyOn(store, 'dispatch'); - jest.spyOn(store, 'select').mockReturnValue(of({ techRecord_vehicleType: 'hgv', techRecord_vehicleClass_description: 'trailer' })); - const result = TestBed.runInInjectionContext( - () => resolver({} as ActivatedRouteSnapshot, {} as RouterStateSnapshot), - ) as Observable; - const resolveResult = await firstValueFrom(result); + expect(resolveResult).toBe(true); + expect(dispatchSpy).toHaveBeenCalledTimes(0); + }); + it('should dispatch a tech record with hgv if hgv vehicle class is invalid', async () => { + const dispatchSpy = jest.spyOn(store, 'dispatch'); + jest + .spyOn(store, 'select') + .mockReturnValue(of({ techRecord_vehicleType: 'hgv', techRecord_vehicleClass_description: 'trailer' })); + const result = TestBed.runInInjectionContext(() => + resolver({} as ActivatedRouteSnapshot, {} as RouterStateSnapshot) + ) as Observable; + const resolveResult = await firstValueFrom(result); - expect(resolveResult).toBe(true); - expect(dispatchSpy).toHaveBeenCalledTimes(1); - expect(dispatchSpy).toHaveBeenCalledWith(expect.objectContaining({ - vehicleTechRecord: { techRecord_vehicleType: 'hgv', techRecord_vehicleClass_description: 'heavy goods vehicle' }, - })); - }); - it('should navigate if there is no tech record', async () => { - jest.spyOn(store, 'select').mockReturnValue(of(undefined)); - const routerSpy = jest.spyOn(router, 'navigate').mockImplementation(); - const result = TestBed.runInInjectionContext( - () => resolver({ - params: { systemNumber: '12345', createdTimestamp: 'now' }, - } as unknown as ActivatedRouteSnapshot, {} as RouterStateSnapshot), - ) as Observable; - await firstValueFrom(result); - expect(routerSpy).toHaveBeenCalled(); - expect(routerSpy).toHaveBeenCalledWith(['./tech-records/12345/now']); - }); - }); + expect(resolveResult).toBe(true); + expect(dispatchSpy).toHaveBeenCalledTimes(1); + expect(dispatchSpy).toHaveBeenCalledWith( + expect.objectContaining({ + vehicleTechRecord: { + techRecord_vehicleType: 'hgv', + techRecord_vehicleClass_description: 'heavy goods vehicle', + }, + }) + ); + }); + it('should navigate if there is no tech record', async () => { + jest.spyOn(store, 'select').mockReturnValue(of(undefined)); + const routerSpy = jest.spyOn(router, 'navigate').mockImplementation(); + const result = TestBed.runInInjectionContext(() => + resolver( + { + params: { systemNumber: '12345', createdTimestamp: 'now' }, + } as unknown as ActivatedRouteSnapshot, + {} as RouterStateSnapshot + ) + ) as Observable; + await firstValueFrom(result); + expect(routerSpy).toHaveBeenCalled(); + expect(routerSpy).toHaveBeenCalledWith(['./tech-records/12345/now']); + }); + }); }); diff --git a/src/app/resolvers/tech-record-validate/tech-record-validate.resolver.ts b/src/app/resolvers/tech-record-validate/tech-record-validate.resolver.ts index 121e0ed944..75b2239fc5 100644 --- a/src/app/resolvers/tech-record-validate/tech-record-validate.resolver.ts +++ b/src/app/resolvers/tech-record-validate/tech-record-validate.resolver.ts @@ -1,9 +1,5 @@ import { inject } from '@angular/core'; -import { - ActivatedRouteSnapshot, - ResolveFn, - Router, -} from '@angular/router'; +import { ActivatedRouteSnapshot, ResolveFn, Router } from '@angular/router'; import { ApprovalType as approvalType } from '@dvsa/cvs-type-definitions/types/v3/tech-record/enums/approvalType.enum.js'; import { ApprovalType as approvalTypeHgvOrPsv } from '@dvsa/cvs-type-definitions/types/v3/tech-record/enums/approvalTypeHgvOrPsv.enum.js'; import { EUVehicleCategory as EUVehicleCategoryTrl } from '@dvsa/cvs-type-definitions/types/v3/tech-record/enums/euVehicleCategory.enum.js'; @@ -12,132 +8,130 @@ import { EUVehicleCategory as EUVehicleCategoryPsv } from '@dvsa/cvs-type-defini import { TyreUseCode as HgvTyreUseCode } from '@dvsa/cvs-type-definitions/types/v3/tech-record/enums/tyreUseCodeHgv.enum.js'; import { TyreUseCode as TrlTyreUseCode } from '@dvsa/cvs-type-definitions/types/v3/tech-record/enums/tyreUseCodeTrl.enum.js'; import { VehicleClassDescription } from '@dvsa/cvs-type-definitions/types/v3/tech-record/enums/vehicleClassDescription.enum.js'; -import { - VehicleClassDescription as VehicleClassDescriptionPSV, -} from '@dvsa/cvs-type-definitions/types/v3/tech-record/enums/vehicleClassDescriptionPSV.enum.js'; -import { - VehicleConfiguration as VehicleConfigurationHgvPsv, -} from '@dvsa/cvs-type-definitions/types/v3/tech-record/enums/vehicleConfigurationHgvPsv.enum.js'; -import { - VehicleConfiguration as VehicleConfigurationTrl, -} from '@dvsa/cvs-type-definitions/types/v3/tech-record/enums/vehicleConfigurationTrl.enum.js'; +import { VehicleClassDescription as VehicleClassDescriptionPSV } from '@dvsa/cvs-type-definitions/types/v3/tech-record/enums/vehicleClassDescriptionPSV.enum.js'; +import { VehicleConfiguration as VehicleConfigurationHgvPsv } from '@dvsa/cvs-type-definitions/types/v3/tech-record/enums/vehicleConfigurationHgvPsv.enum.js'; +import { VehicleConfiguration as VehicleConfigurationTrl } from '@dvsa/cvs-type-definitions/types/v3/tech-record/enums/vehicleConfigurationTrl.enum.js'; import { TechRecordType as TechRecordVehicleType } from '@dvsa/cvs-type-definitions/types/v3/tech-record/tech-record-vehicle-type'; import { TechRecordType } from '@dvsa/cvs-type-definitions/types/v3/tech-record/tech-record-verb'; import { Store } from '@ngrx/store'; import { State } from '@store/.'; import { selectTechRecord, updateEditingTechRecord } from '@store/technical-records'; import { isEqual } from 'lodash'; -import { - map, - take, -} from 'rxjs'; +import { map, take } from 'rxjs'; export const techRecordValidateResolver: ResolveFn = (route: ActivatedRouteSnapshot) => { - const store: Store = inject(Store); - const router: Router = inject(Router); + const store: Store = inject(Store); + const router: Router = inject(Router); - return store.select(selectTechRecord).pipe( - map((record) => { - if (!record) { - void router.navigate([`./tech-records/${route.params['systemNumber']}/${route.params['createdTimestamp']}`]); - } - let validatedRecord = { ...record } as TechRecordType<'put'>; + return store.select(selectTechRecord).pipe( + map((record) => { + if (!record) { + void router.navigate([`./tech-records/${route.params['systemNumber']}/${route.params['createdTimestamp']}`]); + } + let validatedRecord = { ...record } as TechRecordType<'put'>; - if (record) { - switch (true) { - case (record?.techRecord_vehicleType === 'hgv'): { - validatedRecord = handleHgv(record as TechRecordVehicleType<'hgv'>); - break; - } - case (record?.techRecord_vehicleType === 'psv'): { - validatedRecord = handlePsv(record as TechRecordVehicleType<'psv'>); - break; - } - case (record?.techRecord_vehicleType === 'trl'): { - validatedRecord = handleTrl(record as TechRecordVehicleType<'trl'>); - break; - } - default: break; - } - } - if (!isEqual(validatedRecord, record)) { - store.dispatch(updateEditingTechRecord({ vehicleTechRecord: validatedRecord })); - } - }), - take(1), - map(() => { - return true; - }), - ); + if (record) { + switch (true) { + case record?.techRecord_vehicleType === 'hgv': { + validatedRecord = handleHgv(record as TechRecordVehicleType<'hgv'>); + break; + } + case record?.techRecord_vehicleType === 'psv': { + validatedRecord = handlePsv(record as TechRecordVehicleType<'psv'>); + break; + } + case record?.techRecord_vehicleType === 'trl': { + validatedRecord = handleTrl(record as TechRecordVehicleType<'trl'>); + break; + } + default: + break; + } + } + if (!isEqual(validatedRecord, record)) { + store.dispatch(updateEditingTechRecord({ vehicleTechRecord: validatedRecord })); + } + }), + take(1), + map(() => { + return true; + }) + ); }; const handlePsv = (record: TechRecordVehicleType<'psv'>) => { - const validatedRecord: TechRecordVehicleType<'psv'> = { ...record }; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const checks: any = { - techRecord_vehicleConfiguration: VehicleConfigurationHgvPsv, - techRecord_vehicleClass_description: VehicleClassDescriptionPSV, - techRecord_euVehicleCategory: EUVehicleCategoryPsv, - techRecord_approvalType: approvalTypeHgvOrPsv, - } as const; + const validatedRecord: TechRecordVehicleType<'psv'> = { ...record }; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const checks: any = { + techRecord_vehicleConfiguration: VehicleConfigurationHgvPsv, + techRecord_vehicleClass_description: VehicleClassDescriptionPSV, + techRecord_euVehicleCategory: EUVehicleCategoryPsv, + techRecord_approvalType: approvalTypeHgvOrPsv, + } as const; - Object.keys(checks).forEach((key: string) => { - if (record[key as keyof TechRecordVehicleType<'psv'>]) { - const validateValues: boolean = Object.values(checks[`${key}`]).includes(record[key as keyof TechRecordVehicleType<'psv'>]); + Object.keys(checks).forEach((key: string) => { + if (record[key as keyof TechRecordVehicleType<'psv'>]) { + const validateValues: boolean = Object.values(checks[`${key}`]).includes( + record[key as keyof TechRecordVehicleType<'psv'>] + ); - if (!validateValues) { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - (validatedRecord as any)[key as keyof TechRecordVehicleType<'hgv'>] = null; - } - } - }); - return validatedRecord as TechRecordType<'put'>; + if (!validateValues) { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + (validatedRecord as any)[key as keyof TechRecordVehicleType<'hgv'>] = null; + } + } + }); + return validatedRecord as TechRecordType<'put'>; }; const handleTrl = (record: TechRecordVehicleType<'trl'>) => { - const validatedRecord: TechRecordVehicleType<'trl'> = { ...record }; - validatedRecord.techRecord_vehicleClass_description = VehicleClassDescription.Trailer; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const checks: any = { - techRecord_tyreUseCode: TrlTyreUseCode, - techRecord_vehicleConfiguration: VehicleConfigurationTrl, - techRecord_euVehicleCategory: EUVehicleCategoryTrl, - techRecord_approvalType: approvalType, - }; + const validatedRecord: TechRecordVehicleType<'trl'> = { ...record }; + validatedRecord.techRecord_vehicleClass_description = VehicleClassDescription.Trailer; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const checks: any = { + techRecord_tyreUseCode: TrlTyreUseCode, + techRecord_vehicleConfiguration: VehicleConfigurationTrl, + techRecord_euVehicleCategory: EUVehicleCategoryTrl, + techRecord_approvalType: approvalType, + }; - Object.keys(checks).forEach((key: string) => { - if (record[key as keyof TechRecordVehicleType<'trl'>]) { - const validateValues: boolean = Object.values(checks[`${key}`]).includes(record[key as keyof TechRecordVehicleType<'trl'>]); + Object.keys(checks).forEach((key: string) => { + if (record[key as keyof TechRecordVehicleType<'trl'>]) { + const validateValues: boolean = Object.values(checks[`${key}`]).includes( + record[key as keyof TechRecordVehicleType<'trl'>] + ); - if (!validateValues) { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - (validatedRecord as any)[key as keyof TechRecordVehicleType<'hgv'>] = null; - } - } - }); - return validatedRecord as TechRecordType<'put'>; + if (!validateValues) { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + (validatedRecord as any)[key as keyof TechRecordVehicleType<'hgv'>] = null; + } + } + }); + return validatedRecord as TechRecordType<'put'>; }; const handleHgv = (record: TechRecordVehicleType<'hgv'>) => { - const validatedRecord: TechRecordVehicleType<'hgv'> = { ...record }; - validatedRecord.techRecord_vehicleClass_description = VehicleClassDescription.HeavyGoodsVehicle; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const checks: any = { - techRecord_tyreUseCode: HgvTyreUseCode, - techRecord_vehicleConfiguration: VehicleConfigurationHgvPsv, - techRecord_euVehicleCategory: EUVehicleCategoryHgv, - techRecord_approvalType: approvalTypeHgvOrPsv, - }; + const validatedRecord: TechRecordVehicleType<'hgv'> = { ...record }; + validatedRecord.techRecord_vehicleClass_description = VehicleClassDescription.HeavyGoodsVehicle; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const checks: any = { + techRecord_tyreUseCode: HgvTyreUseCode, + techRecord_vehicleConfiguration: VehicleConfigurationHgvPsv, + techRecord_euVehicleCategory: EUVehicleCategoryHgv, + techRecord_approvalType: approvalTypeHgvOrPsv, + }; - Object.keys(checks).forEach((key: string) => { - if (record[key as keyof TechRecordVehicleType<'hgv'>]) { - const validateValues: boolean = Object.values(checks[`${key}`]).includes(record[key as keyof TechRecordVehicleType<'hgv'>]); + Object.keys(checks).forEach((key: string) => { + if (record[key as keyof TechRecordVehicleType<'hgv'>]) { + const validateValues: boolean = Object.values(checks[`${key}`]).includes( + record[key as keyof TechRecordVehicleType<'hgv'>] + ); - if (!validateValues) { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - (validatedRecord as any)[key as keyof TechRecordVehicleType<'hgv'>] = null; - } - } - }); - return validatedRecord as TechRecordType<'put'>; + if (!validateValues) { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + (validatedRecord as any)[key as keyof TechRecordVehicleType<'hgv'>] = null; + } + } + }); + return validatedRecord as TechRecordType<'put'>; }; diff --git a/src/app/resolvers/tech-record-view/tech-record-view.resolver.spec.ts b/src/app/resolvers/tech-record-view/tech-record-view.resolver.spec.ts index 7decd860b2..a9156c8125 100644 --- a/src/app/resolvers/tech-record-view/tech-record-view.resolver.spec.ts +++ b/src/app/resolvers/tech-record-view/tech-record-view.resolver.spec.ts @@ -12,78 +12,85 @@ import { TestScheduler } from 'rxjs/testing'; import { techRecordViewResolver } from './tech-record-view.resolver'; describe('TechRecordViewResolver', () => { - let resolver: ResolveFn; - let actions$ = new Observable(); - let testScheduler: TestScheduler; - const mockSnapshot = jest.fn; - let store: MockStore; + let resolver: ResolveFn; + let actions$ = new Observable(); + let testScheduler: TestScheduler; + const mockSnapshot = jest.fn; + let store: MockStore; - beforeEach(() => { - TestBed.configureTestingModule({ - providers: [ - provideMockStore({ initialState: initialAppState }), - provideMockActions(() => actions$), - { provide: RouterStateSnapshot, useValue: mockSnapshot }, - ], - }); - store = TestBed.inject(MockStore); - resolver = (...resolverParameters) => TestBed.runInInjectionContext(() => techRecordViewResolver(...resolverParameters)); - }); + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [ + provideMockStore({ initialState: initialAppState }), + provideMockActions(() => actions$), + { provide: RouterStateSnapshot, useValue: mockSnapshot }, + ], + }); + store = TestBed.inject(MockStore); + resolver = (...resolverParameters) => + TestBed.runInInjectionContext(() => techRecordViewResolver(...resolverParameters)); + }); - beforeEach(() => { - testScheduler = new TestScheduler((actual, expected) => { - expect(actual).toEqual(expected); - }); - }); + beforeEach(() => { + testScheduler = new TestScheduler((actual, expected) => { + expect(actual).toEqual(expected); + }); + }); - it('should be created', () => { - expect(resolver).toBeTruthy(); - }); + it('should be created', () => { + expect(resolver).toBeTruthy(); + }); - describe('fetch tech record result', () => { - it('should resolved to true when both success actions are triggered', () => { - const dispatchSpy = jest.spyOn(store, 'dispatch'); - const result = TestBed.runInInjectionContext(() => resolver({} as ActivatedRouteSnapshot, {} as RouterStateSnapshot)) as Observable; - store.overrideSelector(selectRouteParam('systemNumber'), undefined); - store.overrideSelector(selectRouteParam('createdTimestamp'), undefined); - testScheduler.run(({ hot, expectObservable }) => { - actions$ = hot('-a-b-', { a: getTechRecordV3Success, b: fetchTestResultsBySystemNumberSuccess }); - expectObservable(result).toBe('---(c|)', { - c: true, - }); - }); + describe('fetch tech record result', () => { + it('should resolved to true when both success actions are triggered', () => { + const dispatchSpy = jest.spyOn(store, 'dispatch'); + const result = TestBed.runInInjectionContext(() => + resolver({} as ActivatedRouteSnapshot, {} as RouterStateSnapshot) + ) as Observable; + store.overrideSelector(selectRouteParam('systemNumber'), undefined); + store.overrideSelector(selectRouteParam('createdTimestamp'), undefined); + testScheduler.run(({ hot, expectObservable }) => { + actions$ = hot('-a-b-', { a: getTechRecordV3Success, b: fetchTestResultsBySystemNumberSuccess }); + expectObservable(result).toBe('---(c|)', { + c: true, + }); + }); - expect(dispatchSpy).toHaveBeenCalledTimes(2); - }); + expect(dispatchSpy).toHaveBeenCalledTimes(2); + }); - it('should resolve to false if \'getTechRecordV3Failure\' action if dispatched', () => { - const dispatchSpy = jest.spyOn(store, 'dispatch'); - const result = TestBed.runInInjectionContext(() => resolver({} as ActivatedRouteSnapshot, {} as RouterStateSnapshot)) as Observable; - store.overrideSelector(selectRouteParam('systemNumber'), undefined); - store.overrideSelector(selectRouteParam('createdTimestamp'), undefined); - testScheduler.run(({ hot, expectObservable }) => { - actions$ = hot('-a-b-', { a: getTechRecordV3Failure, b: fetchTestResultsBySystemNumberSuccess }); - expectObservable(result).toBe('---(c|)', { - c: false, - }); - }); + it("should resolve to false if 'getTechRecordV3Failure' action if dispatched", () => { + const dispatchSpy = jest.spyOn(store, 'dispatch'); + const result = TestBed.runInInjectionContext(() => + resolver({} as ActivatedRouteSnapshot, {} as RouterStateSnapshot) + ) as Observable; + store.overrideSelector(selectRouteParam('systemNumber'), undefined); + store.overrideSelector(selectRouteParam('createdTimestamp'), undefined); + testScheduler.run(({ hot, expectObservable }) => { + actions$ = hot('-a-b-', { a: getTechRecordV3Failure, b: fetchTestResultsBySystemNumberSuccess }); + expectObservable(result).toBe('---(c|)', { + c: false, + }); + }); - expect(dispatchSpy).toHaveBeenCalledTimes(2); - }); + expect(dispatchSpy).toHaveBeenCalledTimes(2); + }); - it('should resolved to false if \'fetchTestResultsBySystemNumberFailed\' action is dipatched', () => { - const dispatchSpy = jest.spyOn(store, 'dispatch'); - const result = TestBed.runInInjectionContext(() => resolver({} as ActivatedRouteSnapshot, {} as RouterStateSnapshot)) as Observable; - store.overrideSelector(selectRouteParam('systemNumber'), undefined); - store.overrideSelector(selectRouteParam('createdTimestamp'), undefined); - testScheduler.run(({ hot, expectObservable }) => { - actions$ = hot('-a-b-', { a: getTechRecordV3Success, b: fetchTestResultsBySystemNumberFailed }); - expectObservable(result).toBe('---(c|)', { - c: false, - }); - }); + it("should resolved to false if 'fetchTestResultsBySystemNumberFailed' action is dipatched", () => { + const dispatchSpy = jest.spyOn(store, 'dispatch'); + const result = TestBed.runInInjectionContext(() => + resolver({} as ActivatedRouteSnapshot, {} as RouterStateSnapshot) + ) as Observable; + store.overrideSelector(selectRouteParam('systemNumber'), undefined); + store.overrideSelector(selectRouteParam('createdTimestamp'), undefined); + testScheduler.run(({ hot, expectObservable }) => { + actions$ = hot('-a-b-', { a: getTechRecordV3Success, b: fetchTestResultsBySystemNumberFailed }); + expectObservable(result).toBe('---(c|)', { + c: false, + }); + }); - expect(dispatchSpy).toHaveBeenCalledTimes(2); - }); - }); + expect(dispatchSpy).toHaveBeenCalledTimes(2); + }); + }); }); diff --git a/src/app/resolvers/tech-record-view/tech-record-view.resolver.ts b/src/app/resolvers/tech-record-view/tech-record-view.resolver.ts index ac80f3b057..c4d8aa74b9 100644 --- a/src/app/resolvers/tech-record-view/tech-record-view.resolver.ts +++ b/src/app/resolvers/tech-record-view/tech-record-view.resolver.ts @@ -5,25 +5,33 @@ import { Store, select } from '@ngrx/store'; import { State } from '@store/.'; import { selectRouteNestedParams } from '@store/router/selectors/router.selectors'; import { getTechRecordV3, getTechRecordV3Failure, getTechRecordV3Success } from '@store/technical-records'; -import { fetchTestResultsBySystemNumber, fetchTestResultsBySystemNumberFailed, fetchTestResultsBySystemNumberSuccess } from '@store/test-records'; import { - count, - map, - take, -} from 'rxjs'; + fetchTestResultsBySystemNumber, + fetchTestResultsBySystemNumberFailed, + fetchTestResultsBySystemNumberSuccess, +} from '@store/test-records'; +import { count, map, take } from 'rxjs'; export const techRecordViewResolver: ResolveFn = () => { - const store: Store = inject(Store); - const action$: Actions = inject(Actions); - store.pipe(select(selectRouteNestedParams), take(1)).subscribe(({ systemNumber, createdTimestamp }) => { - store.dispatch(getTechRecordV3({ systemNumber, createdTimestamp })); - store.dispatch(fetchTestResultsBySystemNumber({ systemNumber })); - }); + const store: Store = inject(Store); + const action$: Actions = inject(Actions); + store.pipe(select(selectRouteNestedParams), take(1)).subscribe(({ systemNumber, createdTimestamp }) => { + store.dispatch(getTechRecordV3({ systemNumber, createdTimestamp })); + store.dispatch(fetchTestResultsBySystemNumber({ systemNumber })); + }); - return action$.pipe( - ofType(getTechRecordV3Success, fetchTestResultsBySystemNumberSuccess, getTechRecordV3Failure, fetchTestResultsBySystemNumberFailed), - take(2), - count((action) => action.type === getTechRecordV3Success.type || action.type === fetchTestResultsBySystemNumberSuccess.type), - map((total) => (total === 2)), - ); + return action$.pipe( + ofType( + getTechRecordV3Success, + fetchTestResultsBySystemNumberSuccess, + getTechRecordV3Failure, + fetchTestResultsBySystemNumberFailed + ), + take(2), + count( + (action) => + action.type === getTechRecordV3Success.type || action.type === fetchTestResultsBySystemNumberSuccess.type + ), + map((total) => total === 2) + ); }; diff --git a/src/app/resolvers/test-result/test-result.resolver.spec.ts b/src/app/resolvers/test-result/test-result.resolver.spec.ts index cdd921a99e..1c101820c2 100644 --- a/src/app/resolvers/test-result/test-result.resolver.spec.ts +++ b/src/app/resolvers/test-result/test-result.resolver.spec.ts @@ -4,79 +4,79 @@ import { RouterTestingModule } from '@angular/router/testing'; import { provideMockActions } from '@ngrx/effects/testing'; import { Action } from '@ngrx/store'; import { MockStore, provideMockStore } from '@ngrx/store/testing'; -import { initialAppState, State } from '@store/.'; +import { State, initialAppState } from '@store/.'; import { - fetchSelectedTestResult, - fetchSelectedTestResultFailed, - fetchSelectedTestResultSuccess, - selectedTestResultState, + fetchSelectedTestResult, + fetchSelectedTestResultFailed, + fetchSelectedTestResultSuccess, + selectedTestResultState, } from '@store/test-records'; import { Observable } from 'rxjs'; import { TestScheduler } from 'rxjs/testing'; import { testResultResolver } from './test-result.resolver'; describe('TestResultResolver', () => { - let resolver: ResolveFn; - let actions$ = new Observable(); - let testScheduler: TestScheduler; - const mockSnapshot = jest.fn; - let store: MockStore; + let resolver: ResolveFn; + let actions$ = new Observable(); + let testScheduler: TestScheduler; + const mockSnapshot = jest.fn; + let store: MockStore; - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [RouterTestingModule], - providers: [ - provideMockStore({ initialState: initialAppState }), - provideMockActions(() => actions$), - { provide: RouterStateSnapshot, useValue: mockSnapshot }, - ], - }); - resolver = (...resolverParameters) => - TestBed.runInInjectionContext(() => testResultResolver(...resolverParameters)); - store = TestBed.inject(MockStore); - }); + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [RouterTestingModule], + providers: [ + provideMockStore({ initialState: initialAppState }), + provideMockActions(() => actions$), + { provide: RouterStateSnapshot, useValue: mockSnapshot }, + ], + }); + resolver = (...resolverParameters) => + TestBed.runInInjectionContext(() => testResultResolver(...resolverParameters)); + store = TestBed.inject(MockStore); + }); - beforeEach(() => { - testScheduler = new TestScheduler((actual, expected) => { - expect(actual).toEqual(expected); - }); - }); + beforeEach(() => { + testScheduler = new TestScheduler((actual, expected) => { + expect(actual).toEqual(expected); + }); + }); - it('should be created', () => { - expect(resolver).toBeTruthy(); - }); + it('should be created', () => { + expect(resolver).toBeTruthy(); + }); - describe('fetch test result', () => { - it('should resolve to true when all actions are success type', () => { - const dispatchSpy = jest.spyOn(store, 'dispatch'); - const result = TestBed.runInInjectionContext( - () => resolver({} as ActivatedRouteSnapshot, {} as RouterStateSnapshot), - ) as Observable; - store.overrideSelector(selectedTestResultState, undefined); - testScheduler.run(({ hot, expectObservable }) => { - actions$ = hot('-a', { a: fetchSelectedTestResultSuccess }); - expectObservable(result).toBe('-(b|)', { - b: true, - }); - }); + describe('fetch test result', () => { + it('should resolve to true when all actions are success type', () => { + const dispatchSpy = jest.spyOn(store, 'dispatch'); + const result = TestBed.runInInjectionContext(() => + resolver({} as ActivatedRouteSnapshot, {} as RouterStateSnapshot) + ) as Observable; + store.overrideSelector(selectedTestResultState, undefined); + testScheduler.run(({ hot, expectObservable }) => { + actions$ = hot('-a', { a: fetchSelectedTestResultSuccess }); + expectObservable(result).toBe('-(b|)', { + b: true, + }); + }); - expect(dispatchSpy).toHaveBeenCalledWith(fetchSelectedTestResult()); - }); + expect(dispatchSpy).toHaveBeenCalledWith(fetchSelectedTestResult()); + }); - it('should resolve to false when one or more actions are of failure type', () => { - const dispatchSpy = jest.spyOn(store, 'dispatch'); - const result = TestBed.runInInjectionContext( - () => resolver({} as ActivatedRouteSnapshot, {} as RouterStateSnapshot), - ) as Observable; - store.overrideSelector(selectedTestResultState, undefined); - testScheduler.run(({ hot, expectObservable }) => { - actions$ = hot('-a', { a: fetchSelectedTestResultFailed }); - expectObservable(result).toBe('-(b|)', { - b: false, - }); - }); + it('should resolve to false when one or more actions are of failure type', () => { + const dispatchSpy = jest.spyOn(store, 'dispatch'); + const result = TestBed.runInInjectionContext(() => + resolver({} as ActivatedRouteSnapshot, {} as RouterStateSnapshot) + ) as Observable; + store.overrideSelector(selectedTestResultState, undefined); + testScheduler.run(({ hot, expectObservable }) => { + actions$ = hot('-a', { a: fetchSelectedTestResultFailed }); + expectObservable(result).toBe('-(b|)', { + b: false, + }); + }); - expect(dispatchSpy).toHaveBeenCalledWith(fetchSelectedTestResult()); - }); - }); + expect(dispatchSpy).toHaveBeenCalledWith(fetchSelectedTestResult()); + }); + }); }); diff --git a/src/app/resolvers/test-result/test-result.resolver.ts b/src/app/resolvers/test-result/test-result.resolver.ts index e37915ceb9..f6d1549260 100644 --- a/src/app/resolvers/test-result/test-result.resolver.ts +++ b/src/app/resolvers/test-result/test-result.resolver.ts @@ -4,24 +4,24 @@ import { Actions, ofType } from '@ngrx/effects'; import { Store } from '@ngrx/store'; import { State } from '@store/.'; import { - cancelEditingTestResult, - fetchSelectedTestResult, - fetchSelectedTestResultFailed, - fetchSelectedTestResultSuccess, + cancelEditingTestResult, + fetchSelectedTestResult, + fetchSelectedTestResultFailed, + fetchSelectedTestResultSuccess, } from '@store/test-records'; import { map, take } from 'rxjs'; export const testResultResolver: ResolveFn = () => { - const store: Store = inject(Store); - const action$: Actions = inject(Actions); - store.dispatch(fetchSelectedTestResult()); - store.dispatch(cancelEditingTestResult()); + const store: Store = inject(Store); + const action$: Actions = inject(Actions); + store.dispatch(fetchSelectedTestResult()); + store.dispatch(cancelEditingTestResult()); - return action$.pipe( - ofType(fetchSelectedTestResultSuccess, fetchSelectedTestResultFailed), - take(1), - map((action) => { - return action.type === fetchSelectedTestResultSuccess.type; - }), - ); + return action$.pipe( + ofType(fetchSelectedTestResultSuccess, fetchSelectedTestResultFailed), + take(1), + map((action) => { + return action.type === fetchSelectedTestResultSuccess.type; + }) + ); }; diff --git a/src/app/resolvers/test-stations/test-stations.resolver.spec.ts b/src/app/resolvers/test-stations/test-stations.resolver.spec.ts index cc076c0e67..f76288356f 100644 --- a/src/app/resolvers/test-stations/test-stations.resolver.spec.ts +++ b/src/app/resolvers/test-stations/test-stations.resolver.spec.ts @@ -10,60 +10,60 @@ import { TestScheduler } from 'rxjs/testing'; import { testStationsResolver } from './test-stations.resolver'; describe('TestTypeTaxonomyResolver', () => { - let resolver: ResolveFn; - let actions$ = new Observable(); - let testScheduler: TestScheduler; - let store: MockStore; + let resolver: ResolveFn; + let actions$ = new Observable(); + let testScheduler: TestScheduler; + let store: MockStore; - beforeEach(() => { - TestBed.configureTestingModule({ - providers: [provideMockStore({ initialState: initialAppState }), provideMockActions(() => actions$)], - }); - store = TestBed.inject(MockStore); - resolver = (...resolverParameters) => - TestBed.runInInjectionContext(() => testStationsResolver(...resolverParameters)); - }); + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [provideMockStore({ initialState: initialAppState }), provideMockActions(() => actions$)], + }); + store = TestBed.inject(MockStore); + resolver = (...resolverParameters) => + TestBed.runInInjectionContext(() => testStationsResolver(...resolverParameters)); + }); - beforeEach(() => { - testScheduler = new TestScheduler((actual, expected) => { - expect(actual).toEqual(expected); - }); - }); + beforeEach(() => { + testScheduler = new TestScheduler((actual, expected) => { + expect(actual).toEqual(expected); + }); + }); - it('should be created', () => { - expect(resolver).toBeTruthy(); - }); + it('should be created', () => { + expect(resolver).toBeTruthy(); + }); - describe('fetch test stations', () => { - it('should resolve to true when all actions are success type', () => { - const dispatchSpy = jest.spyOn(store, 'dispatch'); - const result = TestBed.runInInjectionContext( - () => resolver({} as ActivatedRouteSnapshot, {} as RouterStateSnapshot), - ) as Observable; - testScheduler.run(({ hot, expectObservable }) => { - actions$ = hot('-a', { a: fetchTestStationsSuccess }); + describe('fetch test stations', () => { + it('should resolve to true when all actions are success type', () => { + const dispatchSpy = jest.spyOn(store, 'dispatch'); + const result = TestBed.runInInjectionContext(() => + resolver({} as ActivatedRouteSnapshot, {} as RouterStateSnapshot) + ) as Observable; + testScheduler.run(({ hot, expectObservable }) => { + actions$ = hot('-a', { a: fetchTestStationsSuccess }); - expectObservable(result).toBe('-(b|)', { - b: true, - }); - }); + expectObservable(result).toBe('-(b|)', { + b: true, + }); + }); - expect(dispatchSpy).toHaveBeenCalledWith(fetchTestStations()); - }); + expect(dispatchSpy).toHaveBeenCalledWith(fetchTestStations()); + }); - it('should resolve to false when one or more actions are of failure type', () => { - const dispatchSpy = jest.spyOn(store, 'dispatch'); - const result = TestBed.runInInjectionContext( - () => resolver({} as ActivatedRouteSnapshot, {} as RouterStateSnapshot), - ) as Observable; - testScheduler.run(({ hot, expectObservable }) => { - actions$ = hot('-a', { a: fetchTestStationsFailed }); - expectObservable(result).toBe('-(b|)', { - b: false, - }); - }); + it('should resolve to false when one or more actions are of failure type', () => { + const dispatchSpy = jest.spyOn(store, 'dispatch'); + const result = TestBed.runInInjectionContext(() => + resolver({} as ActivatedRouteSnapshot, {} as RouterStateSnapshot) + ) as Observable; + testScheduler.run(({ hot, expectObservable }) => { + actions$ = hot('-a', { a: fetchTestStationsFailed }); + expectObservable(result).toBe('-(b|)', { + b: false, + }); + }); - expect(dispatchSpy).toHaveBeenCalledWith(fetchTestStations()); - }); - }); + expect(dispatchSpy).toHaveBeenCalledWith(fetchTestStations()); + }); + }); }); diff --git a/src/app/resolvers/test-stations/test-stations.resolver.ts b/src/app/resolvers/test-stations/test-stations.resolver.ts index e7874e178e..45f8a8d123 100644 --- a/src/app/resolvers/test-stations/test-stations.resolver.ts +++ b/src/app/resolvers/test-stations/test-stations.resolver.ts @@ -7,13 +7,13 @@ import { fetchTestStations, fetchTestStationsFailed, fetchTestStationsSuccess } import { map, take } from 'rxjs'; export const testStationsResolver: ResolveFn = () => { - const store: Store = inject(Store); - const action$: Actions = inject(Actions); - store.dispatch(fetchTestStations()); + const store: Store = inject(Store); + const action$: Actions = inject(Actions); + store.dispatch(fetchTestStations()); - return action$.pipe( - ofType(fetchTestStationsSuccess, fetchTestStationsFailed), - take(1), - map((action) => action.type === fetchTestStationsSuccess.type), - ); + return action$.pipe( + ofType(fetchTestStationsSuccess, fetchTestStationsFailed), + take(1), + map((action) => action.type === fetchTestStationsSuccess.type) + ); }; diff --git a/src/app/resolvers/test-type-taxonomy/test-type-taxonomy.resolver.spec.ts b/src/app/resolvers/test-type-taxonomy/test-type-taxonomy.resolver.spec.ts index 0f8499a73e..82c4e4208a 100644 --- a/src/app/resolvers/test-type-taxonomy/test-type-taxonomy.resolver.spec.ts +++ b/src/app/resolvers/test-type-taxonomy/test-type-taxonomy.resolver.spec.ts @@ -1,68 +1,72 @@ import { TestBed } from '@angular/core/testing'; +import { ActivatedRouteSnapshot, ResolveFn, RouterStateSnapshot } from '@angular/router'; import { provideMockActions } from '@ngrx/effects/testing'; import { Action } from '@ngrx/store'; import { MockStore, provideMockStore } from '@ngrx/store/testing'; -import { initialAppState, State } from '@store/.'; -import { fetchTestTypes, fetchTestTypesFailed, fetchTestTypesSuccess } from '@store/test-types/actions/test-types.actions'; +import { State, initialAppState } from '@store/.'; +import { + fetchTestTypes, + fetchTestTypesFailed, + fetchTestTypesSuccess, +} from '@store/test-types/actions/test-types.actions'; import { Observable } from 'rxjs'; import { TestScheduler } from 'rxjs/testing'; -import { ActivatedRouteSnapshot, ResolveFn, RouterStateSnapshot } from '@angular/router'; import { testTypeTaxonomyResolver } from './test-type-taxonomy.resolver'; describe('TestTypeTaxonomyResolver', () => { - let resolver: ResolveFn; - let actions$ = new Observable(); - let testScheduler: TestScheduler; - let store: MockStore; + let resolver: ResolveFn; + let actions$ = new Observable(); + let testScheduler: TestScheduler; + let store: MockStore; - beforeEach(() => { - TestBed.configureTestingModule({ - providers: [provideMockStore({ initialState: initialAppState }), provideMockActions(() => actions$)], - }); - resolver = (...resolverParameters) => - TestBed.runInInjectionContext(() => testTypeTaxonomyResolver(...resolverParameters)); - store = TestBed.inject(MockStore); - }); + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [provideMockStore({ initialState: initialAppState }), provideMockActions(() => actions$)], + }); + resolver = (...resolverParameters) => + TestBed.runInInjectionContext(() => testTypeTaxonomyResolver(...resolverParameters)); + store = TestBed.inject(MockStore); + }); - beforeEach(() => { - testScheduler = new TestScheduler((actual, expected) => { - expect(actual).toEqual(expected); - }); - }); + beforeEach(() => { + testScheduler = new TestScheduler((actual, expected) => { + expect(actual).toEqual(expected); + }); + }); - it('should be created', () => { - expect(resolver).toBeTruthy(); - }); + it('should be created', () => { + expect(resolver).toBeTruthy(); + }); - describe('fetch test types', () => { - it('should resolve to true when all actions are success type', () => { - const dispatchSpy = jest.spyOn(store, 'dispatch'); - const result = TestBed.runInInjectionContext( - () => resolver({} as ActivatedRouteSnapshot, {} as RouterStateSnapshot), - ) as Observable; - testScheduler.run(({ hot, expectObservable }) => { - actions$ = hot('-a', { a: fetchTestTypesSuccess }); - expectObservable(result).toBe('-(b|)', { - b: true, - }); - }); + describe('fetch test types', () => { + it('should resolve to true when all actions are success type', () => { + const dispatchSpy = jest.spyOn(store, 'dispatch'); + const result = TestBed.runInInjectionContext(() => + resolver({} as ActivatedRouteSnapshot, {} as RouterStateSnapshot) + ) as Observable; + testScheduler.run(({ hot, expectObservable }) => { + actions$ = hot('-a', { a: fetchTestTypesSuccess }); + expectObservable(result).toBe('-(b|)', { + b: true, + }); + }); - expect(dispatchSpy).toHaveBeenCalledWith(fetchTestTypes()); - }); + expect(dispatchSpy).toHaveBeenCalledWith(fetchTestTypes()); + }); - it('should resolve to false when one or more actions are of failure type', () => { - const dispatchSpy = jest.spyOn(store, 'dispatch'); - const result = TestBed.runInInjectionContext( - () => resolver({} as ActivatedRouteSnapshot, {} as RouterStateSnapshot), - ) as Observable; - testScheduler.run(({ hot, expectObservable }) => { - actions$ = hot('-a', { a: fetchTestTypesFailed }); - expectObservable(result).toBe('-(b|)', { - b: false, - }); - }); + it('should resolve to false when one or more actions are of failure type', () => { + const dispatchSpy = jest.spyOn(store, 'dispatch'); + const result = TestBed.runInInjectionContext(() => + resolver({} as ActivatedRouteSnapshot, {} as RouterStateSnapshot) + ) as Observable; + testScheduler.run(({ hot, expectObservable }) => { + actions$ = hot('-a', { a: fetchTestTypesFailed }); + expectObservable(result).toBe('-(b|)', { + b: false, + }); + }); - expect(dispatchSpy).toHaveBeenCalledWith(fetchTestTypes()); - }); - }); + expect(dispatchSpy).toHaveBeenCalledWith(fetchTestTypes()); + }); + }); }); diff --git a/src/app/resolvers/test-type-taxonomy/test-type-taxonomy.resolver.ts b/src/app/resolvers/test-type-taxonomy/test-type-taxonomy.resolver.ts index b0565828d6..01db50d442 100644 --- a/src/app/resolvers/test-type-taxonomy/test-type-taxonomy.resolver.ts +++ b/src/app/resolvers/test-type-taxonomy/test-type-taxonomy.resolver.ts @@ -3,17 +3,21 @@ import { ResolveFn } from '@angular/router'; import { Actions, ofType } from '@ngrx/effects'; import { Store } from '@ngrx/store'; import { State } from '@store/.'; -import { fetchTestTypes, fetchTestTypesFailed, fetchTestTypesSuccess } from '@store/test-types/actions/test-types.actions'; +import { + fetchTestTypes, + fetchTestTypesFailed, + fetchTestTypesSuccess, +} from '@store/test-types/actions/test-types.actions'; import { map, take } from 'rxjs'; export const testTypeTaxonomyResolver: ResolveFn = () => { - const store: Store = inject(Store); - const action$: Actions = inject(Actions); - store.dispatch(fetchTestTypes()); + const store: Store = inject(Store); + const action$: Actions = inject(Actions); + store.dispatch(fetchTestTypes()); - return action$.pipe( - ofType(fetchTestTypesSuccess, fetchTestTypesFailed), - take(1), - map((action) => action.type === fetchTestTypesSuccess.type), - ); + return action$.pipe( + ofType(fetchTestTypesSuccess, fetchTestTypesFailed), + take(1), + map((action) => action.type === fetchTestTypesSuccess.type) + ); }; diff --git a/src/app/resolvers/title/title.resolver.spec.ts b/src/app/resolvers/title/title.resolver.spec.ts index ce4a0aab10..674fc1d563 100644 --- a/src/app/resolvers/title/title.resolver.spec.ts +++ b/src/app/resolvers/title/title.resolver.spec.ts @@ -1,68 +1,67 @@ import { TestBed } from '@angular/core/testing'; import { Title } from '@angular/platform-browser'; +import { ActivatedRouteSnapshot, ResolveFn, RouterStateSnapshot } from '@angular/router'; import { RouterTestingModule } from '@angular/router/testing'; import { MockStore, provideMockStore } from '@ngrx/store/testing'; -import { initialAppState, State } from '@store/.'; -import { ActivatedRouteSnapshot, ResolveFn, RouterStateSnapshot } from '@angular/router'; +import { State, initialAppState } from '@store/.'; import { titleResolver } from './title.resolver'; describe('TitleResolver', () => { - let resolver: ResolveFn; - let titleService: Title; - let store: MockStore; + let resolver: ResolveFn; + let titleService: Title; + let store: MockStore; - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [RouterTestingModule], - providers: [Title, provideMockStore({ initialState: initialAppState })], - }); + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [RouterTestingModule], + providers: [Title, provideMockStore({ initialState: initialAppState })], + }); - resolver = (...resolverParameters) => - TestBed.runInInjectionContext(() => titleResolver(...resolverParameters)); - titleService = TestBed.inject(Title); - store = TestBed.inject(MockStore); - }); + resolver = (...resolverParameters) => TestBed.runInInjectionContext(() => titleResolver(...resolverParameters)); + titleService = TestBed.inject(Title); + store = TestBed.inject(MockStore); + }); - it('should be created', () => { - expect(resolver).toBeTruthy(); - }); + it('should be created', () => { + expect(resolver).toBeTruthy(); + }); - it('should set title using Title service', () => { - const titleServiceSpy = jest.spyOn(titleService, 'setTitle'); - const result = TestBed.runInInjectionContext( - () => resolver({} as ActivatedRouteSnapshot, {} as RouterStateSnapshot), - ) as Promise; - store.setState({ - ...initialAppState, - router: { - state: { - root: { - params: { - systemNumber: 'SYS0001', - }, - data: { - title: 'Test Results', - }, - url: [ - { - path: 'SYS0001', - parameters: {}, - }, - ], - outlet: 'primary', - routeConfig: { - path: ':systemNumber', - }, - queryParams: {}, - fragment: null, - children: [], - }, - }, - navigationId: 1, - }, - }); - const resolved = result; - expect(resolved).toBeTruthy(); - expect(titleServiceSpy).toHaveBeenCalledWith('Vehicle Testing Management - Test Results'); - }); + it('should set title using Title service', () => { + const titleServiceSpy = jest.spyOn(titleService, 'setTitle'); + const result = TestBed.runInInjectionContext(() => + resolver({} as ActivatedRouteSnapshot, {} as RouterStateSnapshot) + ) as Promise; + store.setState({ + ...initialAppState, + router: { + state: { + root: { + params: { + systemNumber: 'SYS0001', + }, + data: { + title: 'Test Results', + }, + url: [ + { + path: 'SYS0001', + parameters: {}, + }, + ], + outlet: 'primary', + routeConfig: { + path: ':systemNumber', + }, + queryParams: {}, + fragment: null, + children: [], + }, + }, + navigationId: 1, + }, + }); + const resolved = result; + expect(resolved).toBeTruthy(); + expect(titleServiceSpy).toHaveBeenCalledWith('Vehicle Testing Management - Test Results'); + }); }); diff --git a/src/app/resolvers/title/title.resolver.ts b/src/app/resolvers/title/title.resolver.ts index 996a5d0dc1..ac248af656 100644 --- a/src/app/resolvers/title/title.resolver.ts +++ b/src/app/resolvers/title/title.resolver.ts @@ -6,15 +6,15 @@ import { State } from '@store/.'; import { selectRouteData } from '@store/router/selectors/router.selectors'; export const titleResolver: ResolveFn = () => { - const store: Store = inject(Store); - const titleService: Title = inject(Title); - return new Promise((resolve) => { - store.pipe(select(selectRouteData)).subscribe((navigationData) => { - const { title } = navigationData; - if (title) { - titleService.setTitle(`Vehicle Testing Management - ${title as string}`); - } - }); - resolve(true); - }); + const store: Store = inject(Store); + const titleService: Title = inject(Title); + return new Promise((resolve) => { + store.pipe(select(selectRouteData)).subscribe((navigationData) => { + const { title } = navigationData; + if (title) { + titleService.setTitle(`Vehicle Testing Management - ${title as string}`); + } + }); + resolve(true); + }); }; diff --git a/src/app/services/adr/adr.service.spec.ts b/src/app/services/adr/adr.service.spec.ts index c56465de82..b65e059c39 100644 --- a/src/app/services/adr/adr.service.spec.ts +++ b/src/app/services/adr/adr.service.spec.ts @@ -5,86 +5,112 @@ import { TechRecordType } from '@dvsa/cvs-type-definitions/types/v3/tech-record/ import { AdrService } from './adr.service'; describe('AdrService', () => { - let service: AdrService; + let service: AdrService; - beforeEach(() => { - TestBed.configureTestingModule({}); - service = TestBed.inject(AdrService); - }); + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(AdrService); + }); - it('should be created', () => { - expect(service).toBeTruthy(); - }); + it('should be created', () => { + expect(service).toBeTruthy(); + }); - describe('carriesDangerousGoods', () => { - it('should return true if vehicle carries dangerous goods', () => { - expect(service.carriesDangerousGoods({ techRecord_adrDetails_batteryListNumber: 'number' } as TechRecordType<'hgv'>)).toBe(true); - }); - it('should return false if vehicle does not carry dangerous goods', () => { - expect(service.carriesDangerousGoods({ techRecord_reasonForCreation: 'carries non-dangerous goods' } as TechRecordType<'hgv'>)).toBe(false); - }); - }); + describe('carriesDangerousGoods', () => { + it('should return true if vehicle carries dangerous goods', () => { + expect( + service.carriesDangerousGoods({ techRecord_adrDetails_batteryListNumber: 'number' } as TechRecordType<'hgv'>) + ).toBe(true); + }); + it('should return false if vehicle does not carry dangerous goods', () => { + expect( + service.carriesDangerousGoods({ + techRecord_reasonForCreation: 'carries non-dangerous goods', + } as TechRecordType<'hgv'>) + ).toBe(false); + }); + }); - describe('determineTankStatementSelect', () => { - it('should return STATEMENT if tankStatement_statement is truthy', () => { - const techRecord = { techRecord_adrDetails_tank_tankDetails_tankStatement_statement: 'Reference no.' } as TechRecordType<'hgv'>; - expect(service.determineTankStatementSelect(techRecord)).toBe(ADRTankDetailsTankStatementSelect.STATEMENT); - }); + describe('determineTankStatementSelect', () => { + it('should return STATEMENT if tankStatement_statement is truthy', () => { + const techRecord = { + techRecord_adrDetails_tank_tankDetails_tankStatement_statement: 'Reference no.', + } as TechRecordType<'hgv'>; + expect(service.determineTankStatementSelect(techRecord)).toBe(ADRTankDetailsTankStatementSelect.STATEMENT); + }); - it('should return PRODUCT_LIST if tankStatement_productList is truthy', () => { - const techRecord = { techRecord_adrDetails_tank_tankDetails_tankStatement_productList: 'Product list' } as TechRecordType<'hgv'>; - expect(service.determineTankStatementSelect(techRecord)).toBe(ADRTankDetailsTankStatementSelect.PRODUCT_LIST); - }); + it('should return PRODUCT_LIST if tankStatement_productList is truthy', () => { + const techRecord = { + techRecord_adrDetails_tank_tankDetails_tankStatement_productList: 'Product list', + } as TechRecordType<'hgv'>; + expect(service.determineTankStatementSelect(techRecord)).toBe(ADRTankDetailsTankStatementSelect.PRODUCT_LIST); + }); - it('should return PRODUCT_LIST if tankStatement_productListRefNo is truthy', () => { - const techRecord = { techRecord_adrDetails_tank_tankDetails_tankStatement_productListRefNo: 'Reference no.' } as TechRecordType<'hgv'>; - expect(service.determineTankStatementSelect(techRecord)).toBe(ADRTankDetailsTankStatementSelect.PRODUCT_LIST); - }); + it('should return PRODUCT_LIST if tankStatement_productListRefNo is truthy', () => { + const techRecord = { + techRecord_adrDetails_tank_tankDetails_tankStatement_productListRefNo: 'Reference no.', + } as TechRecordType<'hgv'>; + expect(service.determineTankStatementSelect(techRecord)).toBe(ADRTankDetailsTankStatementSelect.PRODUCT_LIST); + }); - it('should return PRODUCT_LIST if tankStatement_productListUnNo is truthy AND has 1 index', () => { - const techRecord = { techRecord_adrDetails_tank_tankDetails_tankStatement_productListUnNo: ['UN no.'] } as TechRecordType<'hgv'>; - expect(service.determineTankStatementSelect(techRecord)).toBe(ADRTankDetailsTankStatementSelect.PRODUCT_LIST); - }); + it('should return PRODUCT_LIST if tankStatement_productListUnNo is truthy AND has 1 index', () => { + const techRecord = { + techRecord_adrDetails_tank_tankDetails_tankStatement_productListUnNo: ['UN no.'], + } as TechRecordType<'hgv'>; + expect(service.determineTankStatementSelect(techRecord)).toBe(ADRTankDetailsTankStatementSelect.PRODUCT_LIST); + }); - it('should return null if tankStatement_statement is falsy', () => { - const a = { } as TechRecordType<'hgv'>; - const b = { techRecord_adrDetails_tank_tankDetails_tankStatement_statement: null } as TechRecordType<'hgv'>; - const c = { techRecord_adrDetails_tank_tankDetails_tankStatement_statement: undefined } as TechRecordType<'hgv'>; - expect(service.determineTankStatementSelect(a)).toBeNull(); - expect(service.determineTankStatementSelect(b)).toBeNull(); - expect(service.determineTankStatementSelect(c)).toBeNull(); - }); + it('should return null if tankStatement_statement is falsy', () => { + const a = {} as TechRecordType<'hgv'>; + const b = { techRecord_adrDetails_tank_tankDetails_tankStatement_statement: null } as TechRecordType<'hgv'>; + const c = { techRecord_adrDetails_tank_tankDetails_tankStatement_statement: undefined } as TechRecordType<'hgv'>; + expect(service.determineTankStatementSelect(a)).toBeNull(); + expect(service.determineTankStatementSelect(b)).toBeNull(); + expect(service.determineTankStatementSelect(c)).toBeNull(); + }); - it('should return null if tankStatement_productList is falsy', () => { - const a = { } as TechRecordType<'hgv'>; - const b = { techRecord_adrDetails_tank_tankDetails_tankStatement_productList: null } as TechRecordType<'hgv'>; - const c = { techRecord_adrDetails_tank_tankDetails_tankStatement_productList: undefined } as TechRecordType<'hgv'>; - expect(service.determineTankStatementSelect(a)).toBeNull(); - expect(service.determineTankStatementSelect(b)).toBeNull(); - expect(service.determineTankStatementSelect(c)).toBeNull(); - }); + it('should return null if tankStatement_productList is falsy', () => { + const a = {} as TechRecordType<'hgv'>; + const b = { techRecord_adrDetails_tank_tankDetails_tankStatement_productList: null } as TechRecordType<'hgv'>; + const c = { + techRecord_adrDetails_tank_tankDetails_tankStatement_productList: undefined, + } as TechRecordType<'hgv'>; + expect(service.determineTankStatementSelect(a)).toBeNull(); + expect(service.determineTankStatementSelect(b)).toBeNull(); + expect(service.determineTankStatementSelect(c)).toBeNull(); + }); - it('should return null if tankStatement_productListRefNo is falsy', () => { - const a = { } as TechRecordType<'hgv'>; - const b = { techRecord_adrDetails_tank_tankDetails_tankStatement_productListRefNo: null } as TechRecordType<'hgv'>; - const c = { techRecord_adrDetails_tank_tankDetails_tankStatement_productListRefNo: undefined } as TechRecordType<'hgv'>; - expect(service.determineTankStatementSelect(a)).toBeNull(); - expect(service.determineTankStatementSelect(b)).toBeNull(); - expect(service.determineTankStatementSelect(c)).toBeNull(); - }); + it('should return null if tankStatement_productListRefNo is falsy', () => { + const a = {} as TechRecordType<'hgv'>; + const b = { + techRecord_adrDetails_tank_tankDetails_tankStatement_productListRefNo: null, + } as TechRecordType<'hgv'>; + const c = { + techRecord_adrDetails_tank_tankDetails_tankStatement_productListRefNo: undefined, + } as TechRecordType<'hgv'>; + expect(service.determineTankStatementSelect(a)).toBeNull(); + expect(service.determineTankStatementSelect(b)).toBeNull(); + expect(service.determineTankStatementSelect(c)).toBeNull(); + }); - it('should return null if tankStatement_productListUnNo is falsy AND has 1 index', () => { - const techRecord = { techRecord_adrDetails_tank_tankDetails_tankStatement_productListUnNo: ['UN no.'] } as TechRecordType<'hgv'>; - expect(service.determineTankStatementSelect(techRecord)).toBe(ADRTankDetailsTankStatementSelect.PRODUCT_LIST); + it('should return null if tankStatement_productListUnNo is falsy AND has 1 index', () => { + const techRecord = { + techRecord_adrDetails_tank_tankDetails_tankStatement_productListUnNo: ['UN no.'], + } as TechRecordType<'hgv'>; + expect(service.determineTankStatementSelect(techRecord)).toBe(ADRTankDetailsTankStatementSelect.PRODUCT_LIST); - const a = { } as TechRecordType<'hgv'>; - const b = { techRecord_adrDetails_tank_tankDetails_tankStatement_productListUnNo: null } as TechRecordType<'hgv'>; - const c = { techRecord_adrDetails_tank_tankDetails_tankStatement_productListUnNo: undefined } as TechRecordType<'hgv'>; - const d = { techRecord_adrDetails_tank_tankDetails_tankStatement_productListUnNo: [] as string[] } as TechRecordType<'hgv'>; - expect(service.determineTankStatementSelect(a)).toBeNull(); - expect(service.determineTankStatementSelect(b)).toBeNull(); - expect(service.determineTankStatementSelect(c)).toBeNull(); - expect(service.determineTankStatementSelect(d)).toBeNull(); - }); - }); + const a = {} as TechRecordType<'hgv'>; + const b = { techRecord_adrDetails_tank_tankDetails_tankStatement_productListUnNo: null } as TechRecordType<'hgv'>; + const c = { + techRecord_adrDetails_tank_tankDetails_tankStatement_productListUnNo: undefined, + } as TechRecordType<'hgv'>; + const d = { + techRecord_adrDetails_tank_tankDetails_tankStatement_productListUnNo: [] as string[], + } as TechRecordType<'hgv'>; + expect(service.determineTankStatementSelect(a)).toBeNull(); + expect(service.determineTankStatementSelect(b)).toBeNull(); + expect(service.determineTankStatementSelect(c)).toBeNull(); + expect(service.determineTankStatementSelect(d)).toBeNull(); + }); + }); }); diff --git a/src/app/services/adr/adr.service.ts b/src/app/services/adr/adr.service.ts index 5ad10f5fbe..a1d6e33057 100644 --- a/src/app/services/adr/adr.service.ts +++ b/src/app/services/adr/adr.service.ts @@ -3,28 +3,36 @@ import { ADRTankDetailsTankStatementSelect } from '@dvsa/cvs-type-definitions/ty import { TechRecordType } from '@dvsa/cvs-type-definitions/types/v3/tech-record/tech-record-vehicle-type'; @Injectable({ - providedIn: 'root', + providedIn: 'root', }) export class AdrService { - determineTankStatementSelect(techRecord: TechRecordType<'hgv' | 'lgv' | 'trl'>) { - const { - techRecord_adrDetails_tank_tankDetails_tankStatement_statement: statement, - techRecord_adrDetails_tank_tankDetails_tankStatement_productList: productList, - techRecord_adrDetails_tank_tankDetails_tankStatement_productListUnNo: productListUnNo, - techRecord_adrDetails_tank_tankDetails_tankStatement_productListRefNo: productListRefNo, - } = techRecord; + determineTankStatementSelect(techRecord: TechRecordType<'hgv' | 'lgv' | 'trl'>) { + const { + techRecord_adrDetails_tank_tankDetails_tankStatement_statement: statement, + techRecord_adrDetails_tank_tankDetails_tankStatement_productList: productList, + techRecord_adrDetails_tank_tankDetails_tankStatement_productListUnNo: productListUnNo, + techRecord_adrDetails_tank_tankDetails_tankStatement_productListRefNo: productListRefNo, + } = techRecord; - if (statement) return ADRTankDetailsTankStatementSelect.STATEMENT; - if (productList || productListRefNo || (productListUnNo && productListUnNo.length > 0)) return ADRTankDetailsTankStatementSelect.PRODUCT_LIST; + if (statement) return ADRTankDetailsTankStatementSelect.STATEMENT; + if (productList || productListRefNo || (productListUnNo && productListUnNo.length > 0)) + return ADRTankDetailsTankStatementSelect.PRODUCT_LIST; - return null; - } + return null; + } - carriesDangerousGoods(techRecord: TechRecordType<'hgv' | 'lgv' | 'trl'>) { - return techRecord.techRecord_adrDetails_dangerousGoods - || (techRecord.techRecord_adrDetails_dangerousGoods !== false && Boolean(Object.keys(techRecord).find((key) => - key !== 'techRecord_adrDetails_dangerousGoods' - && key.includes('adrDetails') - && techRecord[key as keyof TechRecordType<'hgv' | 'lgv' | 'trl'>] != null))); - } + carriesDangerousGoods(techRecord: TechRecordType<'hgv' | 'lgv' | 'trl'>) { + return ( + techRecord.techRecord_adrDetails_dangerousGoods || + (techRecord.techRecord_adrDetails_dangerousGoods !== false && + Boolean( + Object.keys(techRecord).find( + (key) => + key !== 'techRecord_adrDetails_dangerousGoods' && + key.includes('adrDetails') && + techRecord[key as keyof TechRecordType<'hgv' | 'lgv' | 'trl'>] != null + ) + )) + ); + } } diff --git a/src/app/services/axles/axles.service.spec.ts b/src/app/services/axles/axles.service.spec.ts index ba5ee047d5..d55a1d175f 100644 --- a/src/app/services/axles/axles.service.spec.ts +++ b/src/app/services/axles/axles.service.spec.ts @@ -2,102 +2,110 @@ import { TestBed } from '@angular/core/testing'; import { AxlesService } from './axles.service'; describe('AxlesService', () => { - let service: AxlesService; - - beforeEach(() => { - TestBed.configureTestingModule({}); - service = TestBed.inject(AxlesService); - }); - - it('should be created', () => { - expect(service).toBeTruthy(); - }); - - describe('normaliseVehicleTechRecordAxles', () => { - it('should not change anything if the tech record is correct', () => { - const generateAxleSpacingSpy = jest.spyOn(service, 'generateAxleSpacing').mockImplementation(); - const generateAxlesFromAxleSpacingsSpy = jest.spyOn(service, 'generateAxlesFromAxleSpacings').mockImplementation(); - - expect(generateAxleSpacingSpy).not.toHaveBeenCalled(); - expect(generateAxlesFromAxleSpacingsSpy).not.toHaveBeenCalled(); - }); - - it('should call generate spacings if there are more axles', () => { - const expectedResult = [{ axles: '1-2' }]; - - const generateAxleSpacingSpy = jest.spyOn(service, 'generateAxleSpacing').mockImplementation(() => expectedResult); - const generateAxlesFromAxleSpacingsSpy = jest.spyOn(service, 'generateAxlesFromAxleSpacings').mockImplementation(); - - const [, newAxleSpacings] = service.normaliseAxles([{}, {}], []); - - expect(newAxleSpacings).toBe(expectedResult); - expect(generateAxleSpacingSpy).toHaveBeenCalledWith(2, []); - expect(generateAxlesFromAxleSpacingsSpy).not.toHaveBeenCalled(); - }); - - it('should call generate axles if there are more spacings', () => { - const expectedResult = [{ axleNumber: 1 }]; - - const generateAxleSpacingSpy = jest.spyOn(service, 'generateAxleSpacing').mockImplementation(); - const generateAxlesFromAxleSpacingsSpy = jest.spyOn(service, 'generateAxlesFromAxleSpacings').mockImplementation(() => expectedResult); - - const [newAxles] = service.normaliseAxles([{}], [{}]); - - expect(newAxles).toBe(expectedResult); - expect(generateAxleSpacingSpy).not.toHaveBeenCalled(); - expect(generateAxlesFromAxleSpacingsSpy).toHaveBeenCalledWith(1, [{}]); - }); - }); - - describe('generateAxleSpacing', () => { - it('should generate 3 axle spacings', () => { - const result = service.generateAxleSpacing(4); - - expect(result).toStrictEqual([ - { axles: '1-2', value: null }, - { axles: '2-3', value: null }, - { axles: '3-4', value: null }, - ]); - }); - - it('should generate no axle spacings', () => { - const result = service.generateAxleSpacing(1); - - expect(result).toStrictEqual([]); - }); - - it('should generate 3 axle spacings when adding a axle', () => { - const originalAxleSpacings = [ - { axles: '1-2', value: 100 }, - { axles: '2-3', value: 200 }, - ]; - - const result = service.generateAxleSpacing(4, originalAxleSpacings); - - expect(result).toStrictEqual([ - { axles: '1-2', value: 100 }, - { axles: '2-3', value: 200 }, - { axles: '3-4', value: null }, - ]); - }); - }); - - describe('generateAxles', () => { - it('should generate 3 axles from no previous data', () => { - const result = service.generateAxlesFromAxleSpacings(2); - - expect(result).toHaveLength(3); - expect(result[0]?.axleNumber).toBe(1); - expect(result[2]?.axleNumber).toBe(3); - }); - - it('should generate 3 axles from 1 previous axle', () => { - const previousAxles = [{ axleNumber: 1 }]; - const result = service.generateAxlesFromAxleSpacings(2, previousAxles); - - expect(result).toHaveLength(3); - expect(result[0]?.axleNumber).toBe(1); - expect(result[2]?.axleNumber).toBe(3); - }); - }); + let service: AxlesService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(AxlesService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); + + describe('normaliseVehicleTechRecordAxles', () => { + it('should not change anything if the tech record is correct', () => { + const generateAxleSpacingSpy = jest.spyOn(service, 'generateAxleSpacing').mockImplementation(); + const generateAxlesFromAxleSpacingsSpy = jest + .spyOn(service, 'generateAxlesFromAxleSpacings') + .mockImplementation(); + + expect(generateAxleSpacingSpy).not.toHaveBeenCalled(); + expect(generateAxlesFromAxleSpacingsSpy).not.toHaveBeenCalled(); + }); + + it('should call generate spacings if there are more axles', () => { + const expectedResult = [{ axles: '1-2' }]; + + const generateAxleSpacingSpy = jest + .spyOn(service, 'generateAxleSpacing') + .mockImplementation(() => expectedResult); + const generateAxlesFromAxleSpacingsSpy = jest + .spyOn(service, 'generateAxlesFromAxleSpacings') + .mockImplementation(); + + const [, newAxleSpacings] = service.normaliseAxles([{}, {}], []); + + expect(newAxleSpacings).toBe(expectedResult); + expect(generateAxleSpacingSpy).toHaveBeenCalledWith(2, []); + expect(generateAxlesFromAxleSpacingsSpy).not.toHaveBeenCalled(); + }); + + it('should call generate axles if there are more spacings', () => { + const expectedResult = [{ axleNumber: 1 }]; + + const generateAxleSpacingSpy = jest.spyOn(service, 'generateAxleSpacing').mockImplementation(); + const generateAxlesFromAxleSpacingsSpy = jest + .spyOn(service, 'generateAxlesFromAxleSpacings') + .mockImplementation(() => expectedResult); + + const [newAxles] = service.normaliseAxles([{}], [{}]); + + expect(newAxles).toBe(expectedResult); + expect(generateAxleSpacingSpy).not.toHaveBeenCalled(); + expect(generateAxlesFromAxleSpacingsSpy).toHaveBeenCalledWith(1, [{}]); + }); + }); + + describe('generateAxleSpacing', () => { + it('should generate 3 axle spacings', () => { + const result = service.generateAxleSpacing(4); + + expect(result).toStrictEqual([ + { axles: '1-2', value: null }, + { axles: '2-3', value: null }, + { axles: '3-4', value: null }, + ]); + }); + + it('should generate no axle spacings', () => { + const result = service.generateAxleSpacing(1); + + expect(result).toStrictEqual([]); + }); + + it('should generate 3 axle spacings when adding a axle', () => { + const originalAxleSpacings = [ + { axles: '1-2', value: 100 }, + { axles: '2-3', value: 200 }, + ]; + + const result = service.generateAxleSpacing(4, originalAxleSpacings); + + expect(result).toStrictEqual([ + { axles: '1-2', value: 100 }, + { axles: '2-3', value: 200 }, + { axles: '3-4', value: null }, + ]); + }); + }); + + describe('generateAxles', () => { + it('should generate 3 axles from no previous data', () => { + const result = service.generateAxlesFromAxleSpacings(2); + + expect(result).toHaveLength(3); + expect(result[0]?.axleNumber).toBe(1); + expect(result[2]?.axleNumber).toBe(3); + }); + + it('should generate 3 axles from 1 previous axle', () => { + const previousAxles = [{ axleNumber: 1 }]; + const result = service.generateAxlesFromAxleSpacings(2, previousAxles); + + expect(result).toHaveLength(3); + expect(result[0]?.axleNumber).toBe(1); + expect(result[2]?.axleNumber).toBe(3); + }); + }); }); diff --git a/src/app/services/axles/axles.service.ts b/src/app/services/axles/axles.service.ts index 5626a7765e..3e2bfc27c3 100644 --- a/src/app/services/axles/axles.service.ts +++ b/src/app/services/axles/axles.service.ts @@ -1,70 +1,63 @@ import { Injectable } from '@angular/core'; -import { - Axle, AxleSpacing, Axles, Empty, -} from '@models/vehicle-tech-record.model'; +import { Axle, AxleSpacing, Axles, Empty } from '@models/vehicle-tech-record.model'; import cloneDeep from 'lodash.clonedeep'; @Injectable({ - providedIn: 'root', + providedIn: 'root', }) export class AxlesService { - normaliseAxles( - axles?: Axles, - axleSpacings?: AxleSpacing[], - ): [Axles | undefined, AxleSpacing[] | undefined] { - let newAxles = cloneDeep(axles ?? []); - let newAxleSpacings = cloneDeep(axleSpacings ?? []); - - if (newAxles.length > newAxleSpacings.length + 1) { - newAxleSpacings = this.generateAxleSpacing(newAxles.length, newAxleSpacings); - } else if (newAxles.length < newAxleSpacings.length + 1 && newAxles.length) { - newAxles = this.generateAxlesFromAxleSpacings(newAxleSpacings.length, newAxles); - } - - newAxles.sort((a, b) => (a.axleNumber ?? 0) - (b.axleNumber ?? 0)); - - return [newAxles, newAxleSpacings]; - } - - generateAxleSpacing(numberOfAxles: number, axleSpacingOriginal?: AxleSpacing[]): AxleSpacing[] { - const axleSpacing: AxleSpacing[] = []; - - let axleNumber = 1; - while (axleNumber < numberOfAxles) { - axleSpacing.push({ - axles: `${axleNumber}-${axleNumber + 1}`, - value: axleSpacingOriginal && axleSpacingOriginal[axleNumber - 1] ? axleSpacingOriginal[axleNumber - 1].value : null, - }); - axleNumber++; - } - - return axleSpacing; - } - - generateAxlesFromAxleSpacings( - vehicleAxleSpacingsLength: number, - previousAxles?: Empty[], - ): Empty[] { - const axles = previousAxles ?? []; - - for (let i = axles.length; i < vehicleAxleSpacingsLength + 1; i++) { - axles.push(this.generateEmptyAxle(i + 1)); - } - - return axles; - } - - generateEmptyAxle(axleNumber: number): Empty & { axleNumber?: number } { - return { - axleNumber, - weights_gbWeight: null, - weights_eecWeight: null, - weights_designWeight: null, - tyres_tyreSize: null, - tyres_fitmentCode: null, - tyres_dataTrAxles: null, - tyres_plyRating: null, - tyres_tyreCode: null, - }; - } + normaliseAxles(axles?: Axles, axleSpacings?: AxleSpacing[]): [Axles | undefined, AxleSpacing[] | undefined] { + let newAxles = cloneDeep(axles ?? []); + let newAxleSpacings = cloneDeep(axleSpacings ?? []); + + if (newAxles.length > newAxleSpacings.length + 1) { + newAxleSpacings = this.generateAxleSpacing(newAxles.length, newAxleSpacings); + } else if (newAxles.length < newAxleSpacings.length + 1 && newAxles.length) { + newAxles = this.generateAxlesFromAxleSpacings(newAxleSpacings.length, newAxles); + } + + newAxles.sort((a, b) => (a.axleNumber ?? 0) - (b.axleNumber ?? 0)); + + return [newAxles, newAxleSpacings]; + } + + generateAxleSpacing(numberOfAxles: number, axleSpacingOriginal?: AxleSpacing[]): AxleSpacing[] { + const axleSpacing: AxleSpacing[] = []; + + let axleNumber = 1; + while (axleNumber < numberOfAxles) { + axleSpacing.push({ + axles: `${axleNumber}-${axleNumber + 1}`, + value: + axleSpacingOriginal && axleSpacingOriginal[axleNumber - 1] ? axleSpacingOriginal[axleNumber - 1].value : null, + }); + axleNumber++; + } + + return axleSpacing; + } + + generateAxlesFromAxleSpacings(vehicleAxleSpacingsLength: number, previousAxles?: Empty[]): Empty[] { + const axles = previousAxles ?? []; + + for (let i = axles.length; i < vehicleAxleSpacingsLength + 1; i++) { + axles.push(this.generateEmptyAxle(i + 1)); + } + + return axles; + } + + generateEmptyAxle(axleNumber: number): Empty & { axleNumber?: number } { + return { + axleNumber, + weights_gbWeight: null, + weights_eecWeight: null, + weights_designWeight: null, + tyres_tyreSize: null, + tyres_fitmentCode: null, + tyres_dataTrAxles: null, + tyres_plyRating: null, + tyres_tyreCode: null, + }; + } } diff --git a/src/app/services/batch-technical-record/batch-technical-record.service.spec.ts b/src/app/services/batch-technical-record/batch-technical-record.service.spec.ts index 7abfbcfb76..4b477fd789 100644 --- a/src/app/services/batch-technical-record/batch-technical-record.service.spec.ts +++ b/src/app/services/batch-technical-record/batch-technical-record.service.spec.ts @@ -1,8 +1,6 @@ import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; import { TestBed } from '@angular/core/testing'; -import { - AbstractControl, FormControl, FormGroup, ValidationErrors, -} from '@angular/forms'; +import { AbstractControl, FormControl, FormGroup, ValidationErrors } from '@angular/forms'; import { RouterTestingModule } from '@angular/router/testing'; import { TechRecordSearchSchema } from '@dvsa/cvs-type-definitions/types/v3/tech-record/get/search'; import { CustomFormControl, FormNodeTypes } from '@forms/services/dynamic-form.types'; @@ -15,198 +13,220 @@ import { Observable, firstValueFrom, of } from 'rxjs'; import { BatchTechnicalRecordService } from './batch-technical-record.service'; describe('TechnicalRecordService', () => { - let service: BatchTechnicalRecordService; - let httpClient: HttpTestingController; - let technicalRecordHttpService: TechnicalRecordHttpService; - let technicalRecordService: TechnicalRecordService; - - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [HttpClientTestingModule, RouterTestingModule], - providers: [ - BatchTechnicalRecordService, - provideMockStore({ initialState: initialAppState }), - TechnicalRecordHttpService, - TechnicalRecordService, - ], - }); - httpClient = TestBed.inject(HttpTestingController); - service = TestBed.inject(BatchTechnicalRecordService); - technicalRecordHttpService = TestBed.inject(TechnicalRecordHttpService); - technicalRecordService = TestBed.inject(TechnicalRecordService); - }); - - afterEach(() => { - // After every test, assert that there are no more pending requests. - httpClient.verify(); - }); - - it('should be created', () => { - expect(service).toBeTruthy(); - }); - - describe('validateForBatch', () => { - let testGroup: FormGroup; - beforeEach(() => { - testGroup = new FormGroup({ - vin: new CustomFormControl({ name: 'vin', type: FormNodeTypes.CONTROL }, null, null), - trailerIdOrVrm: new FormControl({ name: 'trailerIdOrVrm', value: '' }, null), - systemNumber: new FormControl({ name: 'systemNumber', value: '' }, null), - oldVehicleStatus: new FormControl({ name: 'oldVehicleStatus', value: '' }, null), - vehicleType: new FormControl({ name: 'vehicleType', value: '' }, null), - createdTimestamp: new FormControl({ name: 'createdTimestamp', value: '' }, null), - }); - }); - - it('return null if vin and trailer id are not provided', (done) => { - expect.assertions(1); - testGroup.get('vin')?.setValue(null); - testGroup.get('trailerIdOrVrm')?.setValue(null); - const serviceCall = service.validateForBatch()(testGroup.get('vin') as AbstractControl); - (serviceCall as Observable).subscribe((errors) => { - expect(errors).toBeNull(); - done(); - }); - }); - - it('return null if the required controls do not exist', (done) => { - expect.assertions(1); - testGroup = new FormGroup({ vin: new FormControl({ name: 'vin' }) }); - const serviceCall = service.validateForBatch()(testGroup.get('vin') as AbstractControl); - (serviceCall as Observable).subscribe((errors) => { - expect(errors).toBeNull(); - done(); - }); - }); - - it('throws an error if trailer id is provided but vin is not', (done) => { - expect.assertions(1); - testGroup.get('trailerIdOrVrm')?.setValue('test'); - - const serviceCall = service.validateForBatch()(testGroup.get('vin') as AbstractControl); - (serviceCall as Observable).subscribe((errors) => { - expect(errors).toEqual({ validateForBatch: { message: 'VIN is required' } }); - done(); - }); - }); - - describe('when only vin is provided', () => { - it('should return null when it is a unique vin', async () => { - testGroup.get('trailerIdOrVrm')?.setValue(''); - testGroup.get('vin')?.setValue('TESTVIN'); - - const isUniqueSpy = jest.spyOn(technicalRecordService, 'isUnique').mockReturnValueOnce(of(true)); - - const serviceCall = service.validateForBatch()(testGroup.get('vin') as AbstractControl) as Observable; - const errors = await firstValueFrom(serviceCall); - expect(isUniqueSpy).toHaveBeenCalled(); - expect(errors).toBeNull(); - }); - - it('should set a warning when it is not a unique vin', async () => { - testGroup.get('trailerIdOrVrm')?.setValue(''); - const vinControl = testGroup.get('vin') as CustomFormControl; - vinControl.setValue('TESTVIN'); - jest.spyOn(technicalRecordService, 'isUnique').mockReturnValueOnce(of(false)); - await firstValueFrom(service.validateForBatch()(testGroup.get('vin') as AbstractControl) as Observable); - expect(vinControl.meta.warning).toBe('This VIN already exists, if you continue it will be associated with two vehicles'); - }); - }); - - describe('when both vin and trailer id are provided', () => { - it('returns null if only 1 vehicle exists with those values with no current tech record', async () => { - expect.assertions(1); - testGroup.get('vin')?.setValue('TESTVIN'); - testGroup.get('trailerIdOrVrm')?.setValue('TESTTRAILERID'); - const mockSearchResult = { - vin: 'TESTVIN', - trailerId: 'TESTTRAILERID', - systemNumber: 'TESTSYSTEMNUMBER', - techRecord_statusCode: StatusCodes.PROVISIONAL, - createdTimestamp: '1234', - } as TechRecordSearchSchema; - - jest.spyOn(technicalRecordHttpService, 'search$').mockReturnValue(of([mockSearchResult])); - - const serviceCall = service.validateForBatch()(testGroup.get('vin') as AbstractControl) as Observable; - const errors = await firstValueFrom(serviceCall); - expect(errors).toBeNull(); - }); - - it('throws error if only 1 trailer exists with those values with a current tech record', async () => { - expect.assertions(1); - testGroup.get('vin')?.setValue('TESTVIN'); - testGroup.get('trailerIdOrVrm')?.setValue('TESTTRAILERID'); - testGroup.get('vehicleType')?.setValue(VehicleTypes.TRL); - const mockSearchResult = { - vin: 'TESTVIN', - trailerId: 'TESTTRAILERID', - systemNumber: 'TESTSYSTEMNUMBER', - techRecord_statusCode: StatusCodes.CURRENT, - techRecord_vehicleType: VehicleTypes.TRL, - } as TechRecordSearchSchema; - - jest.spyOn(technicalRecordHttpService, 'search$').mockReturnValue(of([mockSearchResult])); - - const serviceCall = service.validateForBatch()(testGroup.get('vin') as AbstractControl) as Observable; - const errors = await firstValueFrom(serviceCall); - expect(errors).toEqual({ validateForBatch: { message: 'This record cannot be updated as it has a current tech record' } }); - }); - - it('returns null if only 1 vehicle other than trailer exists with those values with a current tech record', async () => { - expect.assertions(1); - testGroup.get('vin')?.setValue('TESTVIN'); - testGroup.get('trailerIdOrVrm')?.setValue('TESTTRAILERID'); - const mockSearchResult = { - vin: '1234', - trailerId: 'TESTTRAILERID', - systemNumber: 'TESTSYSTEMNUMBER', - techRecord_statusCode: StatusCodes.CURRENT, - techRecord_vehicleType: VehicleTypes.PSV, - createdTimestamp: '1234', - } as TechRecordSearchSchema; - - jest.spyOn(technicalRecordHttpService, 'search$').mockReturnValue(of([mockSearchResult])); - - const serviceCall = service.validateForBatch()(testGroup.get('vin') as AbstractControl) as Observable; - const errors = await firstValueFrom(serviceCall); - expect(errors).toBeNull(); - }); - - it('throws an error if more than 1 vehicle exists with those values', async () => { - expect.assertions(1); - testGroup.get('vin')?.setValue('TESTVIN'); - testGroup.get('trailerIdOrVrm')?.setValue('TESTTRAILERID'); - const mockSearchResult = { - vin: 'TESTVIN', - trailerId: 'TESTTRAILERID', - techRecord_statusCode: StatusCodes.PROVISIONAL, - } as TechRecordSearchSchema; - - jest.spyOn(technicalRecordHttpService, 'search$').mockReturnValue(of([mockSearchResult, { ...mockSearchResult, systemNumber: 'foobar' }])); - - const errors = await firstValueFrom( - service.validateForBatch()(testGroup.get('vin') as AbstractControl) as Observable, - ); - expect(errors).toEqual({ validateForBatch: { message: 'More than one vehicle has this VIN and VRM/Trailer ID' } }); - }); - }); - - it('throws an error if no vehicle exists with those values', async () => { - expect.assertions(1); - - const vinControl = testGroup.get('vin'); - const trailerIdOrVrmControl = testGroup.get('trailerIdOrVrm'); - - if (vinControl && trailerIdOrVrmControl) { - vinControl.setValue('TESTVIN'); - trailerIdOrVrmControl.setValue('TESTTRAILERID'); - } - - jest.spyOn(technicalRecordHttpService, 'search$').mockReturnValue(of([])); - - const errors = await firstValueFrom(service.validateForBatch()(testGroup.get('vin') as AbstractControl) as Observable); - expect(errors).toEqual({ validateForBatch: { message: 'Could not find a record with matching VIN and VRM/Trailer ID' } }); - }); - }); + let service: BatchTechnicalRecordService; + let httpClient: HttpTestingController; + let technicalRecordHttpService: TechnicalRecordHttpService; + let technicalRecordService: TechnicalRecordService; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [HttpClientTestingModule, RouterTestingModule], + providers: [ + BatchTechnicalRecordService, + provideMockStore({ initialState: initialAppState }), + TechnicalRecordHttpService, + TechnicalRecordService, + ], + }); + httpClient = TestBed.inject(HttpTestingController); + service = TestBed.inject(BatchTechnicalRecordService); + technicalRecordHttpService = TestBed.inject(TechnicalRecordHttpService); + technicalRecordService = TestBed.inject(TechnicalRecordService); + }); + + afterEach(() => { + // After every test, assert that there are no more pending requests. + httpClient.verify(); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); + + describe('validateForBatch', () => { + let testGroup: FormGroup; + beforeEach(() => { + testGroup = new FormGroup({ + vin: new CustomFormControl({ name: 'vin', type: FormNodeTypes.CONTROL }, null, null), + trailerIdOrVrm: new FormControl({ name: 'trailerIdOrVrm', value: '' }, null), + systemNumber: new FormControl({ name: 'systemNumber', value: '' }, null), + oldVehicleStatus: new FormControl({ name: 'oldVehicleStatus', value: '' }, null), + vehicleType: new FormControl({ name: 'vehicleType', value: '' }, null), + createdTimestamp: new FormControl({ name: 'createdTimestamp', value: '' }, null), + }); + }); + + it('return null if vin and trailer id are not provided', (done) => { + expect.assertions(1); + testGroup.get('vin')?.setValue(null); + testGroup.get('trailerIdOrVrm')?.setValue(null); + const serviceCall = service.validateForBatch()(testGroup.get('vin') as AbstractControl); + (serviceCall as Observable).subscribe((errors) => { + expect(errors).toBeNull(); + done(); + }); + }); + + it('return null if the required controls do not exist', (done) => { + expect.assertions(1); + testGroup = new FormGroup({ vin: new FormControl({ name: 'vin' }) }); + const serviceCall = service.validateForBatch()(testGroup.get('vin') as AbstractControl); + (serviceCall as Observable).subscribe((errors) => { + expect(errors).toBeNull(); + done(); + }); + }); + + it('throws an error if trailer id is provided but vin is not', (done) => { + expect.assertions(1); + testGroup.get('trailerIdOrVrm')?.setValue('test'); + + const serviceCall = service.validateForBatch()(testGroup.get('vin') as AbstractControl); + (serviceCall as Observable).subscribe((errors) => { + expect(errors).toEqual({ validateForBatch: { message: 'VIN is required' } }); + done(); + }); + }); + + describe('when only vin is provided', () => { + it('should return null when it is a unique vin', async () => { + testGroup.get('trailerIdOrVrm')?.setValue(''); + testGroup.get('vin')?.setValue('TESTVIN'); + + const isUniqueSpy = jest.spyOn(technicalRecordService, 'isUnique').mockReturnValueOnce(of(true)); + + const serviceCall = service.validateForBatch()( + testGroup.get('vin') as AbstractControl + ) as Observable; + const errors = await firstValueFrom(serviceCall); + expect(isUniqueSpy).toHaveBeenCalled(); + expect(errors).toBeNull(); + }); + + it('should set a warning when it is not a unique vin', async () => { + testGroup.get('trailerIdOrVrm')?.setValue(''); + const vinControl = testGroup.get('vin') as CustomFormControl; + vinControl.setValue('TESTVIN'); + jest.spyOn(technicalRecordService, 'isUnique').mockReturnValueOnce(of(false)); + await firstValueFrom( + service.validateForBatch()(testGroup.get('vin') as AbstractControl) as Observable + ); + expect(vinControl.meta.warning).toBe( + 'This VIN already exists, if you continue it will be associated with two vehicles' + ); + }); + }); + + describe('when both vin and trailer id are provided', () => { + it('returns null if only 1 vehicle exists with those values with no current tech record', async () => { + expect.assertions(1); + testGroup.get('vin')?.setValue('TESTVIN'); + testGroup.get('trailerIdOrVrm')?.setValue('TESTTRAILERID'); + const mockSearchResult = { + vin: 'TESTVIN', + trailerId: 'TESTTRAILERID', + systemNumber: 'TESTSYSTEMNUMBER', + techRecord_statusCode: StatusCodes.PROVISIONAL, + createdTimestamp: '1234', + } as TechRecordSearchSchema; + + jest.spyOn(technicalRecordHttpService, 'search$').mockReturnValue(of([mockSearchResult])); + + const serviceCall = service.validateForBatch()( + testGroup.get('vin') as AbstractControl + ) as Observable; + const errors = await firstValueFrom(serviceCall); + expect(errors).toBeNull(); + }); + + it('throws error if only 1 trailer exists with those values with a current tech record', async () => { + expect.assertions(1); + testGroup.get('vin')?.setValue('TESTVIN'); + testGroup.get('trailerIdOrVrm')?.setValue('TESTTRAILERID'); + testGroup.get('vehicleType')?.setValue(VehicleTypes.TRL); + const mockSearchResult = { + vin: 'TESTVIN', + trailerId: 'TESTTRAILERID', + systemNumber: 'TESTSYSTEMNUMBER', + techRecord_statusCode: StatusCodes.CURRENT, + techRecord_vehicleType: VehicleTypes.TRL, + } as TechRecordSearchSchema; + + jest.spyOn(technicalRecordHttpService, 'search$').mockReturnValue(of([mockSearchResult])); + + const serviceCall = service.validateForBatch()( + testGroup.get('vin') as AbstractControl + ) as Observable; + const errors = await firstValueFrom(serviceCall); + expect(errors).toEqual({ + validateForBatch: { message: 'This record cannot be updated as it has a current tech record' }, + }); + }); + + it('returns null if only 1 vehicle other than trailer exists with those values with a current tech record', async () => { + expect.assertions(1); + testGroup.get('vin')?.setValue('TESTVIN'); + testGroup.get('trailerIdOrVrm')?.setValue('TESTTRAILERID'); + const mockSearchResult = { + vin: '1234', + trailerId: 'TESTTRAILERID', + systemNumber: 'TESTSYSTEMNUMBER', + techRecord_statusCode: StatusCodes.CURRENT, + techRecord_vehicleType: VehicleTypes.PSV, + createdTimestamp: '1234', + } as TechRecordSearchSchema; + + jest.spyOn(technicalRecordHttpService, 'search$').mockReturnValue(of([mockSearchResult])); + + const serviceCall = service.validateForBatch()( + testGroup.get('vin') as AbstractControl + ) as Observable; + const errors = await firstValueFrom(serviceCall); + expect(errors).toBeNull(); + }); + + it('throws an error if more than 1 vehicle exists with those values', async () => { + expect.assertions(1); + testGroup.get('vin')?.setValue('TESTVIN'); + testGroup.get('trailerIdOrVrm')?.setValue('TESTTRAILERID'); + const mockSearchResult = { + vin: 'TESTVIN', + trailerId: 'TESTTRAILERID', + techRecord_statusCode: StatusCodes.PROVISIONAL, + } as TechRecordSearchSchema; + + jest + .spyOn(technicalRecordHttpService, 'search$') + .mockReturnValue(of([mockSearchResult, { ...mockSearchResult, systemNumber: 'foobar' }])); + + const errors = await firstValueFrom( + service.validateForBatch()(testGroup.get('vin') as AbstractControl) as Observable + ); + expect(errors).toEqual({ + validateForBatch: { message: 'More than one vehicle has this VIN and VRM/Trailer ID' }, + }); + }); + }); + + it('throws an error if no vehicle exists with those values', async () => { + expect.assertions(1); + + const vinControl = testGroup.get('vin'); + const trailerIdOrVrmControl = testGroup.get('trailerIdOrVrm'); + + if (vinControl && trailerIdOrVrmControl) { + vinControl.setValue('TESTVIN'); + trailerIdOrVrmControl.setValue('TESTTRAILERID'); + } + + jest.spyOn(technicalRecordHttpService, 'search$').mockReturnValue(of([])); + + const errors = await firstValueFrom( + service.validateForBatch()(testGroup.get('vin') as AbstractControl) as Observable + ); + expect(errors).toEqual({ + validateForBatch: { message: 'Could not find a record with matching VIN and VRM/Trailer ID' }, + }); + }); + }); }); diff --git a/src/app/services/batch-technical-record/batch-technical-record.service.ts b/src/app/services/batch-technical-record/batch-technical-record.service.ts index 7a34016b41..2f11df8021 100644 --- a/src/app/services/batch-technical-record/batch-technical-record.service.ts +++ b/src/app/services/batch-technical-record/batch-technical-record.service.ts @@ -1,194 +1,196 @@ import { Injectable } from '@angular/core'; import { AbstractControl, AsyncValidatorFn, ValidationErrors } from '@angular/forms'; import { CustomFormControl } from '@forms/services/dynamic-form.types'; +import { SEARCH_TYPES } from '@models/search-types-enum'; import { StatusCodes, VehicleTypes } from '@models/vehicle-tech-record.model'; import { Store, select } from '@ngrx/store'; import { TechnicalRecordHttpService } from '@services/technical-record-http/technical-record-http.service'; -import { SEARCH_TYPES } from '@models/search-types-enum'; import { TechnicalRecordService } from '@services/technical-record/technical-record.service'; import { updateEditingTechRecordCancel } from '@store/technical-records'; import { - clearBatch, - setApplicationId, - setGenerateNumberFlag, - setVehicleStatus, - setVehicleType, - upsertVehicleBatch, + clearBatch, + setApplicationId, + setGenerateNumberFlag, + setVehicleStatus, + setVehicleType, + upsertVehicleBatch, } from '@store/technical-records/actions/batch-create.actions'; import { BatchRecord } from '@store/technical-records/reducers/batch-create.reducer'; import { - selectAllBatch, - selectApplicationId, - selectBatchCount, - selectBatchCreatedCount, - selectBatchCreatedSuccessCount, - selectBatchSuccess, - selectBatchSuccessCount, - selectBatchUpdatedCount, - selectBatchUpdatedSuccessCount, - selectGenerateNumber, - selectIsBatch, - selectVehicleStatus, - selectVehicleType, + selectAllBatch, + selectApplicationId, + selectBatchCount, + selectBatchCreatedCount, + selectBatchCreatedSuccessCount, + selectBatchSuccess, + selectBatchSuccessCount, + selectBatchUpdatedCount, + selectBatchUpdatedSuccessCount, + selectGenerateNumber, + selectIsBatch, + selectVehicleStatus, + selectVehicleType, } from '@store/technical-records/selectors/batch-create.selectors'; -import { - Observable, - catchError, - map, - of, -} from 'rxjs'; +import { Observable, catchError, map, of } from 'rxjs'; @Injectable({ providedIn: 'root' }) export class BatchTechnicalRecordService { - constructor( - private store: Store, - private techRecordHttpService: TechnicalRecordHttpService, - private technicalRecordService: TechnicalRecordService, - ) {} - - validateForBatch(): AsyncValidatorFn { - return (control: AbstractControl): Observable => { - const trailerIdOrVrmControl = control.parent?.get('trailerIdOrVrm') as CustomFormControl; - const vinControl = control.parent?.get('vin') as CustomFormControl; - const systemNumberControl = control.parent?.get('systemNumber') as CustomFormControl; - const createdTimeStampControl = control.parent?.get('createdTimestamp') as CustomFormControl; - - if (trailerIdOrVrmControl && vinControl) { - const trailerIdOrVrm = trailerIdOrVrmControl.value; - const vin = vinControl.value; - delete vinControl.meta.warning; - - if (trailerIdOrVrm && vin) { - return this.validateVinAndTrailerIdOrVrm(vin, trailerIdOrVrm, systemNumberControl, createdTimeStampControl); - } if (!trailerIdOrVrm && vin) { - return this.validateVinForBatch(vinControl); - } if (trailerIdOrVrm && !vin) { - return of({ validateForBatch: { message: 'VIN is required' } }); - } - } - return of(null); - }; - } - - private validateVinAndTrailerIdOrVrm( - vin: string, - trailerIdOrVrm: string, - systemNumberControl: CustomFormControl, - createdTimestampControl: CustomFormControl, - ): Observable { - return this.techRecordHttpService.search$(SEARCH_TYPES.VIN, vin).pipe( - map((result) => { - const recordsWithTrailerIdOrVrm = result.filter( - (vehicleTechRecord) => vehicleTechRecord.trailerId === trailerIdOrVrm || vehicleTechRecord.primaryVrm === trailerIdOrVrm, - ); - if (!recordsWithTrailerIdOrVrm.length) { - return { validateForBatch: { message: 'Could not find a record with matching VIN and VRM/Trailer ID' } }; - } - if (new Set(recordsWithTrailerIdOrVrm.map((record) => record.systemNumber)).size > 1) { - return { validateForBatch: { message: 'More than one vehicle has this VIN and VRM/Trailer ID' } }; - } - if ( - recordsWithTrailerIdOrVrm.find( - (techRecord) => techRecord.techRecord_statusCode === StatusCodes.CURRENT && techRecord.techRecord_vehicleType === VehicleTypes.TRL, - ) - ) { - return { validateForBatch: { message: 'This record cannot be updated as it has a current tech record' } }; - } - systemNumberControl.setValue(result[0].systemNumber); - const techRecordToUpdate = recordsWithTrailerIdOrVrm.find((techRecord) => techRecord.techRecord_statusCode !== StatusCodes.ARCHIVED); - createdTimestampControl.setValue(techRecordToUpdate?.createdTimestamp); - return null; - }), - catchError(() => of({ validateForBatch: { message: 'Could not find a record with matching VIN' } })), - ); - } - - private validateVinForBatch(vinControl: CustomFormControl): Observable { - return this.technicalRecordService.isUnique(vinControl.value, SEARCH_TYPES.VIN).pipe( - map((result) => { - if (!result) { - vinControl.meta.warning = 'This VIN already exists, if you continue it will be associated with two vehicles'; - } - return null; - }), - catchError(() => of(null)), - ); - } - - upsertVehicleBatch(vehicles: Array<{ vin: string; trailerId?: string; primaryVrm?: string }>) { - this.store.dispatch(upsertVehicleBatch({ vehicles })); - } - - clearEditingTechRecord() { - this.store.dispatch(updateEditingTechRecordCancel()); - } - - setApplicationId(applicationId: string) { - this.store.dispatch(setApplicationId({ applicationId })); - } - setGenerateNumberFlag(generateNumber: boolean) { - this.store.dispatch(setGenerateNumberFlag({ generateNumber })); - } - setVehicleStatus(vehicleStatus: string) { - this.store.dispatch(setVehicleStatus({ vehicleStatus })); - } - setVehicleType(vehicleType: VehicleTypes) { - this.store.dispatch(setVehicleType({ vehicleType })); - } - - clearBatch() { - this.store.dispatch(clearBatch()); - } - - get batchVehicles$(): Observable { - return this.store.pipe(select(selectAllBatch)); - } - - get batchVehiclesSuccess$(): Observable { - return this.store.pipe(select(selectBatchSuccess)); - } - - get isBatchCreate$(): Observable { - return this.store.pipe(select(selectIsBatch)); - } - - get batchCount$(): Observable { - return this.store.pipe(select(selectBatchCount)); - } - - get batchSuccessCount$(): Observable { - return this.store.pipe(select(selectBatchSuccessCount)); - } - - get batchCreatedCount$(): Observable { - return this.store.pipe(select(selectBatchCreatedSuccessCount)); - } - - get batchTotalCreatedCount$(): Observable { - return this.store.pipe(select(selectBatchCreatedCount)); - } - - get batchUpdatedCount$(): Observable { - return this.store.pipe(select(selectBatchUpdatedSuccessCount)); - } - - get batchTotalUpdatedCount$(): Observable { - return this.store.pipe(select(selectBatchUpdatedCount)); - } - - get applicationId$(): Observable { - return this.store.pipe(select(selectApplicationId)); - } - - get vehicleStatus$(): Observable { - return this.store.pipe(select(selectVehicleStatus)); - } - - get vehicleType$(): Observable { - return this.store.pipe(select(selectVehicleType)); - } - - get generateNumber$(): Observable { - return this.store.pipe(select(selectGenerateNumber)); - } + constructor( + private store: Store, + private techRecordHttpService: TechnicalRecordHttpService, + private technicalRecordService: TechnicalRecordService + ) {} + + validateForBatch(): AsyncValidatorFn { + return (control: AbstractControl): Observable => { + const trailerIdOrVrmControl = control.parent?.get('trailerIdOrVrm') as CustomFormControl; + const vinControl = control.parent?.get('vin') as CustomFormControl; + const systemNumberControl = control.parent?.get('systemNumber') as CustomFormControl; + const createdTimeStampControl = control.parent?.get('createdTimestamp') as CustomFormControl; + + if (trailerIdOrVrmControl && vinControl) { + const trailerIdOrVrm = trailerIdOrVrmControl.value; + const vin = vinControl.value; + delete vinControl.meta.warning; + + if (trailerIdOrVrm && vin) { + return this.validateVinAndTrailerIdOrVrm(vin, trailerIdOrVrm, systemNumberControl, createdTimeStampControl); + } + if (!trailerIdOrVrm && vin) { + return this.validateVinForBatch(vinControl); + } + if (trailerIdOrVrm && !vin) { + return of({ validateForBatch: { message: 'VIN is required' } }); + } + } + return of(null); + }; + } + + private validateVinAndTrailerIdOrVrm( + vin: string, + trailerIdOrVrm: string, + systemNumberControl: CustomFormControl, + createdTimestampControl: CustomFormControl + ): Observable { + return this.techRecordHttpService.search$(SEARCH_TYPES.VIN, vin).pipe( + map((result) => { + const recordsWithTrailerIdOrVrm = result.filter( + (vehicleTechRecord) => + vehicleTechRecord.trailerId === trailerIdOrVrm || vehicleTechRecord.primaryVrm === trailerIdOrVrm + ); + if (!recordsWithTrailerIdOrVrm.length) { + return { validateForBatch: { message: 'Could not find a record with matching VIN and VRM/Trailer ID' } }; + } + if (new Set(recordsWithTrailerIdOrVrm.map((record) => record.systemNumber)).size > 1) { + return { validateForBatch: { message: 'More than one vehicle has this VIN and VRM/Trailer ID' } }; + } + if ( + recordsWithTrailerIdOrVrm.find( + (techRecord) => + techRecord.techRecord_statusCode === StatusCodes.CURRENT && + techRecord.techRecord_vehicleType === VehicleTypes.TRL + ) + ) { + return { validateForBatch: { message: 'This record cannot be updated as it has a current tech record' } }; + } + systemNumberControl.setValue(result[0].systemNumber); + const techRecordToUpdate = recordsWithTrailerIdOrVrm.find( + (techRecord) => techRecord.techRecord_statusCode !== StatusCodes.ARCHIVED + ); + createdTimestampControl.setValue(techRecordToUpdate?.createdTimestamp); + return null; + }), + catchError(() => of({ validateForBatch: { message: 'Could not find a record with matching VIN' } })) + ); + } + + private validateVinForBatch(vinControl: CustomFormControl): Observable { + return this.technicalRecordService.isUnique(vinControl.value, SEARCH_TYPES.VIN).pipe( + map((result) => { + if (!result) { + vinControl.meta.warning = 'This VIN already exists, if you continue it will be associated with two vehicles'; + } + return null; + }), + catchError(() => of(null)) + ); + } + + upsertVehicleBatch(vehicles: Array<{ vin: string; trailerId?: string; primaryVrm?: string }>) { + this.store.dispatch(upsertVehicleBatch({ vehicles })); + } + + clearEditingTechRecord() { + this.store.dispatch(updateEditingTechRecordCancel()); + } + + setApplicationId(applicationId: string) { + this.store.dispatch(setApplicationId({ applicationId })); + } + setGenerateNumberFlag(generateNumber: boolean) { + this.store.dispatch(setGenerateNumberFlag({ generateNumber })); + } + setVehicleStatus(vehicleStatus: string) { + this.store.dispatch(setVehicleStatus({ vehicleStatus })); + } + setVehicleType(vehicleType: VehicleTypes) { + this.store.dispatch(setVehicleType({ vehicleType })); + } + + clearBatch() { + this.store.dispatch(clearBatch()); + } + + get batchVehicles$(): Observable { + return this.store.pipe(select(selectAllBatch)); + } + + get batchVehiclesSuccess$(): Observable { + return this.store.pipe(select(selectBatchSuccess)); + } + + get isBatchCreate$(): Observable { + return this.store.pipe(select(selectIsBatch)); + } + + get batchCount$(): Observable { + return this.store.pipe(select(selectBatchCount)); + } + + get batchSuccessCount$(): Observable { + return this.store.pipe(select(selectBatchSuccessCount)); + } + + get batchCreatedCount$(): Observable { + return this.store.pipe(select(selectBatchCreatedSuccessCount)); + } + + get batchTotalCreatedCount$(): Observable { + return this.store.pipe(select(selectBatchCreatedCount)); + } + + get batchUpdatedCount$(): Observable { + return this.store.pipe(select(selectBatchUpdatedSuccessCount)); + } + + get batchTotalUpdatedCount$(): Observable { + return this.store.pipe(select(selectBatchUpdatedCount)); + } + + get applicationId$(): Observable { + return this.store.pipe(select(selectApplicationId)); + } + + get vehicleStatus$(): Observable { + return this.store.pipe(select(selectVehicleStatus)); + } + + get vehicleType$(): Observable { + return this.store.pipe(select(selectVehicleType)); + } + + get generateNumber$(): Observable { + return this.store.pipe(select(selectGenerateNumber)); + } } diff --git a/src/app/services/defects/defects.service.spec.ts b/src/app/services/defects/defects.service.spec.ts index 377716317a..7b318c9a55 100644 --- a/src/app/services/defects/defects.service.spec.ts +++ b/src/app/services/defects/defects.service.spec.ts @@ -6,92 +6,92 @@ import { environment } from '../../../environments/environment'; import { DefectsService } from './defects.service'; describe('DefectsService', () => { - let service: DefectsService; - let httpTestingController: HttpTestingController; - - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [HttpClientTestingModule], - }); - - httpTestingController = TestBed.inject(HttpTestingController); - service = TestBed.inject(DefectsService); - }); - - afterEach(() => { - // After every test, assert that there are no more pending requests. - httpTestingController.verify(); - }); - - it('should be created', () => { - expect(service).toBeTruthy(); - }); - - describe('fetchDefects', () => { - it('should get an array of matching results', () => { - const expectedResult = [{ imDescription: 'Some Description' } as Defect]; - service.fetchDefects().subscribe((response) => expect(response).toEqual(expectedResult)); - - // Check for correct requests: should have made one request to search from expected URL - const req = httpTestingController.expectOne(`${environment.VTM_API_URI}/defects`); - expect(req.request.method).toBe('GET'); - - // Provide each request with a mock response - req.flush(expectedResult); - }); - - it('should handle errors', (done) => { - service.fetchDefects().subscribe({ - next: () => {}, - error: (e) => { - expect(e.error).toBe('Deliberate 500 error'); - expect(e.status).toBe(500); - expect(e.statusText).toBe('Server Error'); - done(); - }, - }); - - // Check for correct requests: should have made one request to search from expected URL - const req = httpTestingController.expectOne(`${environment.VTM_API_URI}/defects`); - expect(req.request.method).toBe('GET'); - - // Respond with mock error - req.flush('Deliberate 500 error', { status: 500, statusText: 'Server Error' }); - }); - }); - - describe('fetchDefect', () => { - it('should get a matching result', () => { - const expectedId = 1; - const expectedResult = { imDescription: 'Some Description' } as Defect; - service.fetchDefect(expectedId).subscribe((response) => expect(response).toEqual(expectedResult)); - - // Check for correct requests: should have made one request to search from expected URL - const req = httpTestingController.expectOne(`${environment.VTM_API_URI}/defects/${expectedId}`); - expect(req.request.method).toBe('GET'); - - // Provide each request with a mock response - req.flush(expectedResult); - }); - - it('should handle errors', (done) => { - const expectedId = 1; - service.fetchDefect(expectedId).subscribe({ - next: () => {}, - error: (e) => { - expect(e.error).toBe('Deliberate 500 error'); - expect(e.status).toBe(500); - expect(e.statusText).toBe('Server Error'); - done(); - }, - }); - - // Check for correct requests: should have made one request to search from expected URL - const req = httpTestingController.expectOne(`${environment.VTM_API_URI}/defects/${expectedId}`); - expect(req.request.method).toBe('GET'); - - // Respond with mock error - req.flush('Deliberate 500 error', { status: 500, statusText: 'Server Error' }); - }); - }); + let service: DefectsService; + let httpTestingController: HttpTestingController; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [HttpClientTestingModule], + }); + + httpTestingController = TestBed.inject(HttpTestingController); + service = TestBed.inject(DefectsService); + }); + + afterEach(() => { + // After every test, assert that there are no more pending requests. + httpTestingController.verify(); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); + + describe('fetchDefects', () => { + it('should get an array of matching results', () => { + const expectedResult = [{ imDescription: 'Some Description' } as Defect]; + service.fetchDefects().subscribe((response) => expect(response).toEqual(expectedResult)); + + // Check for correct requests: should have made one request to search from expected URL + const req = httpTestingController.expectOne(`${environment.VTM_API_URI}/defects`); + expect(req.request.method).toBe('GET'); + + // Provide each request with a mock response + req.flush(expectedResult); + }); + + it('should handle errors', (done) => { + service.fetchDefects().subscribe({ + next: () => {}, + error: (e) => { + expect(e.error).toBe('Deliberate 500 error'); + expect(e.status).toBe(500); + expect(e.statusText).toBe('Server Error'); + done(); + }, + }); + + // Check for correct requests: should have made one request to search from expected URL + const req = httpTestingController.expectOne(`${environment.VTM_API_URI}/defects`); + expect(req.request.method).toBe('GET'); + + // Respond with mock error + req.flush('Deliberate 500 error', { status: 500, statusText: 'Server Error' }); + }); + }); + + describe('fetchDefect', () => { + it('should get a matching result', () => { + const expectedId = 1; + const expectedResult = { imDescription: 'Some Description' } as Defect; + service.fetchDefect(expectedId).subscribe((response) => expect(response).toEqual(expectedResult)); + + // Check for correct requests: should have made one request to search from expected URL + const req = httpTestingController.expectOne(`${environment.VTM_API_URI}/defects/${expectedId}`); + expect(req.request.method).toBe('GET'); + + // Provide each request with a mock response + req.flush(expectedResult); + }); + + it('should handle errors', (done) => { + const expectedId = 1; + service.fetchDefect(expectedId).subscribe({ + next: () => {}, + error: (e) => { + expect(e.error).toBe('Deliberate 500 error'); + expect(e.status).toBe(500); + expect(e.statusText).toBe('Server Error'); + done(); + }, + }); + + // Check for correct requests: should have made one request to search from expected URL + const req = httpTestingController.expectOne(`${environment.VTM_API_URI}/defects/${expectedId}`); + expect(req.request.method).toBe('GET'); + + // Respond with mock error + req.flush('Deliberate 500 error', { status: 500, statusText: 'Server Error' }); + }); + }); }); diff --git a/src/app/services/defects/defects.service.ts b/src/app/services/defects/defects.service.ts index 737ebcfee3..06f66c4114 100644 --- a/src/app/services/defects/defects.service.ts +++ b/src/app/services/defects/defects.service.ts @@ -6,15 +6,15 @@ import { environment } from '../../../environments/environment'; @Injectable({ providedIn: 'root' }) export class DefectsService { - private url = `${environment.VTM_API_URI}/defects`; + private url = `${environment.VTM_API_URI}/defects`; - constructor(private http: HttpClient) {} + constructor(private http: HttpClient) {} - fetchDefects(): Observable { - return this.http.get(this.url, { responseType: 'json' }); - } + fetchDefects(): Observable { + return this.http.get(this.url, { responseType: 'json' }); + } - fetchDefect(id: number): Observable { - return this.http.get(`${this.url}/${id}`, { responseType: 'json' }); - } + fetchDefect(id: number): Observable { + return this.http.get(`${this.url}/${id}`, { responseType: 'json' }); + } } diff --git a/src/app/services/documents/documents.service.spec.ts b/src/app/services/documents/documents.service.spec.ts index daf77764fd..2aa993f966 100644 --- a/src/app/services/documents/documents.service.spec.ts +++ b/src/app/services/documents/documents.service.spec.ts @@ -9,80 +9,80 @@ const responseBody = 'Response body'; const domParser = new DOMParser(); const fakeAnchor = domParser - .parseFromString('', 'text/html') - .querySelector('a'); + .parseFromString('', 'text/html') + .querySelector('a'); describe('DocumentsService', () => { - let service: DocumentsService; - - beforeEach(() => { - TestBed.configureTestingModule({}); - service = TestBed.inject(DocumentsService); - }); - - it('should be created', () => { - expect(service).toBeTruthy(); - }); - - describe('openDocumentFromResponse', () => { - it('should download a document sent back from a http response', () => { - const convertToBlobSpy = jest.spyOn(service, 'convertToBlob'); - const createFileLinkSpy = jest.spyOn(service, 'createFileLink'); - const simulateClickSpy = jest.spyOn(service, 'simulateClick'); - - service.openDocumentFromResponse(fileName, responseBody); - - expect(convertToBlobSpy).toHaveBeenCalledWith(responseBody, 'pdf'); - expect(createFileLinkSpy).toHaveBeenCalledWith(fileName, new Blob(), 'pdf'); - expect(simulateClickSpy).toHaveBeenCalledWith(fakeAnchor); - }); - - it('should download a document sent back with a signed url', () => { - const convertToBlobSpy = jest.spyOn(service, 'convertToBlob'); - const createFileLinkSpy = jest.spyOn(service, 'createFileLink'); - const simulateClickSpy = jest.spyOn(service, 'simulateClick'); - - const fakeAnchorZip = domParser - .parseFromString('', 'text/html') - .querySelector('a'); - - service.openDocumentFromResponse(fileName, responseBody, 'zip'); - - expect(convertToBlobSpy).not.toHaveBeenCalled(); - expect(createFileLinkSpy).not.toHaveBeenCalled(); - expect(simulateClickSpy).toHaveBeenCalledWith(fakeAnchorZip); - }); - }); - - describe('convertToBlob', () => { - it('should throw an error if the data provided is not a string', () => { - expect(service.convertToBlob.bind(0)).toThrow(); - expect(service.convertToBlob.bind(null)).toThrow(); - expect(service.convertToBlob.bind(undefined)).toThrow(); - }); - - it('should return a blob of type application/pdf based on the string provided', () => { - expect(service.convertToBlob('')).toEqual(new Blob()); - }); - }); - - describe('createFileLink', () => { - it('should create a downloadable anchor link for a pdf file, using the file name and blob provided', () => { - expect(service.createFileLink(fileName, new Blob(), 'pdf')).toEqual(fakeAnchor); - }); - }); - - describe('simulateClick', () => { - it('should add the downloadable pdf anchor element to the DOM, programmatically click it, then remove it', () => { - const appendChildSpy = jest.spyOn(document.body, 'appendChild'); - const clickSpy = jest.spyOn(fakeAnchor as HTMLAnchorElement, 'click'); - const removeChildSpy = jest.spyOn(document.body, 'removeChild'); - - service.simulateClick(fakeAnchor as HTMLAnchorElement); - - expect(appendChildSpy).toHaveBeenCalled(); - expect(clickSpy).toHaveBeenCalled(); - expect(removeChildSpy).toHaveBeenCalled(); - }); - }); + let service: DocumentsService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(DocumentsService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); + + describe('openDocumentFromResponse', () => { + it('should download a document sent back from a http response', () => { + const convertToBlobSpy = jest.spyOn(service, 'convertToBlob'); + const createFileLinkSpy = jest.spyOn(service, 'createFileLink'); + const simulateClickSpy = jest.spyOn(service, 'simulateClick'); + + service.openDocumentFromResponse(fileName, responseBody); + + expect(convertToBlobSpy).toHaveBeenCalledWith(responseBody, 'pdf'); + expect(createFileLinkSpy).toHaveBeenCalledWith(fileName, new Blob(), 'pdf'); + expect(simulateClickSpy).toHaveBeenCalledWith(fakeAnchor); + }); + + it('should download a document sent back with a signed url', () => { + const convertToBlobSpy = jest.spyOn(service, 'convertToBlob'); + const createFileLinkSpy = jest.spyOn(service, 'createFileLink'); + const simulateClickSpy = jest.spyOn(service, 'simulateClick'); + + const fakeAnchorZip = domParser + .parseFromString('', 'text/html') + .querySelector('a'); + + service.openDocumentFromResponse(fileName, responseBody, 'zip'); + + expect(convertToBlobSpy).not.toHaveBeenCalled(); + expect(createFileLinkSpy).not.toHaveBeenCalled(); + expect(simulateClickSpy).toHaveBeenCalledWith(fakeAnchorZip); + }); + }); + + describe('convertToBlob', () => { + it('should throw an error if the data provided is not a string', () => { + expect(service.convertToBlob.bind(0)).toThrow(); + expect(service.convertToBlob.bind(null)).toThrow(); + expect(service.convertToBlob.bind(undefined)).toThrow(); + }); + + it('should return a blob of type application/pdf based on the string provided', () => { + expect(service.convertToBlob('')).toEqual(new Blob()); + }); + }); + + describe('createFileLink', () => { + it('should create a downloadable anchor link for a pdf file, using the file name and blob provided', () => { + expect(service.createFileLink(fileName, new Blob(), 'pdf')).toEqual(fakeAnchor); + }); + }); + + describe('simulateClick', () => { + it('should add the downloadable pdf anchor element to the DOM, programmatically click it, then remove it', () => { + const appendChildSpy = jest.spyOn(document.body, 'appendChild'); + const clickSpy = jest.spyOn(fakeAnchor as HTMLAnchorElement, 'click'); + const removeChildSpy = jest.spyOn(document.body, 'removeChild'); + + service.simulateClick(fakeAnchor as HTMLAnchorElement); + + expect(appendChildSpy).toHaveBeenCalled(); + expect(clickSpy).toHaveBeenCalled(); + expect(removeChildSpy).toHaveBeenCalled(); + }); + }); }); diff --git a/src/app/services/documents/documents.service.ts b/src/app/services/documents/documents.service.ts index 9338e2ca51..84eb99ee51 100644 --- a/src/app/services/documents/documents.service.ts +++ b/src/app/services/documents/documents.service.ts @@ -2,54 +2,53 @@ import { Injectable } from '@angular/core'; @Injectable({ providedIn: 'root' }) export class DocumentsService { - openDocumentFromResponse(fileName: string, responseBody: unknown, fileType: string = 'pdf'): void { - if (fileType === 'zip') { - const link: HTMLAnchorElement = document.createElement('a'); + openDocumentFromResponse(fileName: string, responseBody: unknown, fileType = 'pdf'): void { + if (fileType === 'zip') { + const link: HTMLAnchorElement = document.createElement('a'); - link.href = (responseBody as string).toString(); - link.download = `${fileName}.${fileType}`; + link.href = (responseBody as string).toString(); + link.download = `${fileName}.${fileType}`; - this.simulateClick(link); - } else { - const blob = this.convertToBlob(responseBody, fileType); + this.simulateClick(link); + } else { + const blob = this.convertToBlob(responseBody, fileType); - const link = this.createFileLink(fileName, blob, fileType); + const link = this.createFileLink(fileName, blob, fileType); - this.simulateClick(link); - } + this.simulateClick(link); + } + } - } + convertToBlob(data: unknown, fileType?: string): Blob { + if (typeof data !== 'string') throw new Error('Cannot convert to a blob. Data needs to be of type string'); - convertToBlob(data: unknown, fileType?: string): Blob { - if (typeof data !== 'string') throw new Error('Cannot convert to a blob. Data needs to be of type string'); + const byteArray = new Uint8Array( + window + .atob(data) + .split('') + .map((char) => char.charCodeAt(0)) + ); - const byteArray = new Uint8Array( - window - .atob(data) - .split('') - .map((char) => char.charCodeAt(0)), - ); + return new Blob([byteArray], { type: `application/${fileType}; charset=utf-8` }); + } - return new Blob([byteArray], { type: `application/${fileType}; charset=utf-8` }); - } + createFileLink(fileName: string, blob: Blob, fileType?: string): HTMLAnchorElement { + const url = window.URL.createObjectURL(blob); - createFileLink(fileName: string, blob: Blob, fileType?: string): HTMLAnchorElement { - const url = window.URL.createObjectURL(blob); + const link: HTMLAnchorElement = document.createElement('a'); - const link: HTMLAnchorElement = document.createElement('a'); + link.href = url; + link.target = '_blank'; + link.download = `${fileName}.${fileType}`; - link.href = url; - link.target = '_blank'; - link.download = `${fileName}.${fileType}`; + return link; + } - return link; - } + simulateClick(link: HTMLAnchorElement): void { + document.body.appendChild(link); - simulateClick(link: HTMLAnchorElement): void { - document.body.appendChild(link); + link.click(); - link.click(); - - document.body.removeChild(link); - } + document.body.removeChild(link); + } } diff --git a/src/app/services/feature-toggle-service/feature-toggle-service.spec.ts b/src/app/services/feature-toggle-service/feature-toggle-service.spec.ts index b6777c472b..5e257424d2 100644 --- a/src/app/services/feature-toggle-service/feature-toggle-service.spec.ts +++ b/src/app/services/feature-toggle-service/feature-toggle-service.spec.ts @@ -3,92 +3,92 @@ import { HttpClientTestingModule, HttpTestingController } from '@angular/common/ import { TestBed } from '@angular/core/testing'; import { Store } from '@ngrx/store'; import { of } from 'rxjs'; -import { FeatureToggleService } from './feature-toggle-service'; import { environment } from '../../../environments/environment'; +import { FeatureToggleService } from './feature-toggle-service'; describe('feature toggle service', () => { - let service: FeatureToggleService; - let httpClient: HttpClient; + let service: FeatureToggleService; + let httpClient: HttpClient; - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [HttpClientTestingModule], - providers: [Store, FeatureToggleService], - }); + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [HttpClientTestingModule], + providers: [Store, FeatureToggleService], + }); - httpClient = TestBed.inject(HttpClient); - TestBed.inject(HttpTestingController); - service = TestBed.inject(FeatureToggleService); - }); + httpClient = TestBed.inject(HttpClient); + TestBed.inject(HttpTestingController); + service = TestBed.inject(FeatureToggleService); + }); - it('should create the user service', () => { - expect(service).toBeTruthy(); - }); + it('should create the user service', () => { + expect(service).toBeTruthy(); + }); - describe('loadConfig', () => { - it('should create the config from a http request', async () => { - service.configPath = 'test/path.json'; + describe('loadConfig', () => { + it('should create the config from a http request', async () => { + service.configPath = 'test/path.json'; - const expectedConfig = { - testToggle: 'true', - }; + const expectedConfig = { + testToggle: 'true', + }; - jest.spyOn(httpClient, 'get').mockReturnValueOnce(of(expectedConfig)); + jest.spyOn(httpClient, 'get').mockReturnValueOnce(of(expectedConfig)); - await service.loadConfig(); + await service.loadConfig(); - expect(service.config).toBeTruthy(); - expect(service.config).toEqual(expectedConfig); - }); - }); + expect(service.config).toBeTruthy(); + expect(service.config).toEqual(expectedConfig); + }); + }); - describe('getConfig', () => { - it('should return the correct feature toggle config', () => { - environment.TARGET_ENV = 'dev'; - expect(service.getConfig()).toBe('assets/featureToggle.json'); + describe('getConfig', () => { + it('should return the correct feature toggle config', () => { + environment.TARGET_ENV = 'dev'; + expect(service.getConfig()).toBe('assets/featureToggle.json'); - environment.TARGET_ENV = 'undefined'; - expect(service.getConfig()).toBe('assets/featureToggle.json'); + environment.TARGET_ENV = 'undefined'; + expect(service.getConfig()).toBe('assets/featureToggle.json'); - environment.TARGET_ENV = 'integration'; - expect(service.getConfig()).toBe('assets/featureToggle.int.json'); + environment.TARGET_ENV = 'integration'; + expect(service.getConfig()).toBe('assets/featureToggle.int.json'); - environment.TARGET_ENV = 'preprod'; - expect(service.getConfig()).toBe('assets/featureToggle.preprod.json'); + environment.TARGET_ENV = 'preprod'; + expect(service.getConfig()).toBe('assets/featureToggle.preprod.json'); - environment.TARGET_ENV = 'prod'; - expect(service.getConfig()).toBe('assets/featureToggle.prod.json'); - }); - }); + environment.TARGET_ENV = 'prod'; + expect(service.getConfig()).toBe('assets/featureToggle.prod.json'); + }); + }); - describe('isFeatureEnabled', () => { - it('should return false if there is no config', () => { - service.config = null; - const result = service.isFeatureEnabled('testToggle'); - expect(result).toBeFalsy(); - }); - it('should return false if the key is not in the config', () => { - service.config = { - randomKey: false, - }; - const result = service.isFeatureEnabled('testToggle'); - expect(result).toBeFalsy(); - }); - it('should return false if the key is in the config but is set to false', () => { - service.config = { - randomKey: false, - testToggle: false, - }; - const result = service.isFeatureEnabled('testToggle'); - expect(result).toBeFalsy(); - }); - it('should return true if the key is in the config but is set to false so should be hidden', () => { - service.config = { - randomKey: false, - testToggle: true, - }; - const result = service.isFeatureEnabled('testToggle'); - expect(result).toBeTruthy(); - }); - }); + describe('isFeatureEnabled', () => { + it('should return false if there is no config', () => { + service.config = null; + const result = service.isFeatureEnabled('testToggle'); + expect(result).toBeFalsy(); + }); + it('should return false if the key is not in the config', () => { + service.config = { + randomKey: false, + }; + const result = service.isFeatureEnabled('testToggle'); + expect(result).toBeFalsy(); + }); + it('should return false if the key is in the config but is set to false', () => { + service.config = { + randomKey: false, + testToggle: false, + }; + const result = service.isFeatureEnabled('testToggle'); + expect(result).toBeFalsy(); + }); + it('should return true if the key is in the config but is set to false so should be hidden', () => { + service.config = { + randomKey: false, + testToggle: true, + }; + const result = service.isFeatureEnabled('testToggle'); + expect(result).toBeTruthy(); + }); + }); }); diff --git a/src/app/services/feature-toggle-service/feature-toggle-service.ts b/src/app/services/feature-toggle-service/feature-toggle-service.ts index b0bd080d0d..21c930a63a 100644 --- a/src/app/services/feature-toggle-service/feature-toggle-service.ts +++ b/src/app/services/feature-toggle-service/feature-toggle-service.ts @@ -5,41 +5,40 @@ import { lastValueFrom, take } from 'rxjs'; import { environment } from '../../../environments/environment'; export interface FeatureConfig { - [key:string]:boolean; + [key: string]: boolean; } @Injectable({ - providedIn: 'root', + providedIn: 'root', }) export class FeatureToggleService { - config: FeatureConfig | null = null; - configPath = this.getConfig(); + config: FeatureConfig | null = null; + configPath = this.getConfig(); - constructor(private http: HttpClient) {} + constructor(private http: HttpClient) {} - async loadConfig() { - // eslint-disable-next-line no-return-assign - return this.config = await lastValueFrom(this.http.get(this.configPath).pipe(take(1))); - } + async loadConfig() { + // eslint-disable-next-line no-return-assign + return (this.config = await lastValueFrom(this.http.get(this.configPath).pipe(take(1)))); + } - getConfig() { - switch (environment.TARGET_ENV) { - case 'prod': - return 'assets/featureToggle.prod.json'; - case 'integration': - return 'assets/featureToggle.int.json'; - case 'preprod': - return 'assets/featureToggle.preprod.json'; - default: - return 'assets/featureToggle.json'; - } - } - - isFeatureEnabled(key: string) { - if (this.config && has(this.config, key)) { - return get(this.config, key, false); - } - return false; - } + getConfig() { + switch (environment.TARGET_ENV) { + case 'prod': + return 'assets/featureToggle.prod.json'; + case 'integration': + return 'assets/featureToggle.int.json'; + case 'preprod': + return 'assets/featureToggle.preprod.json'; + default: + return 'assets/featureToggle.json'; + } + } + isFeatureEnabled(key: string) { + if (this.config && has(this.config, key)) { + return get(this.config, key, false); + } + return false; + } } diff --git a/src/app/services/google-analytics/google-analytics.service.spec.ts b/src/app/services/google-analytics/google-analytics.service.spec.ts deleted file mode 100644 index 1fdd986430..0000000000 --- a/src/app/services/google-analytics/google-analytics.service.spec.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { TestBed } from '@angular/core/testing'; -import { GoogleAnalyticsService } from './google-analytics.service'; - -describe('GoogleAnalyticsServiceService', () => { - let service: GoogleAnalyticsService; - - beforeEach(() => { - TestBed.configureTestingModule({}); - service = TestBed.inject(GoogleAnalyticsService); - }); - - it('should be created', () => { - expect(service).toBeTruthy(); - }); -}); diff --git a/src/app/services/google-analytics/google-analytics.service.ts b/src/app/services/google-analytics/google-analytics.service.ts deleted file mode 100644 index 15b16c4eb3..0000000000 --- a/src/app/services/google-analytics/google-analytics.service.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { Injectable } from '@angular/core'; - -@Injectable({ providedIn: 'root' }) -export class GoogleAnalyticsService { - public pageView(pageName: string, pageUrl: string) { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - (window).dataLayer.push('pageView', { pageName, pageUrl }); - } -} diff --git a/src/app/services/loading/loading.service.spec.ts b/src/app/services/loading/loading.service.spec.ts index dfad671222..e973318133 100644 --- a/src/app/services/loading/loading.service.spec.ts +++ b/src/app/services/loading/loading.service.spec.ts @@ -1,7 +1,7 @@ import { TestBed } from '@angular/core/testing'; import { RouterTestingModule } from '@angular/router/testing'; import { MockStore, provideMockStore } from '@ngrx/store/testing'; -import { initialAppState, State } from '@store/.'; +import { State, initialAppState } from '@store/.'; import { getSpinner } from '@store/spinner/selectors/spinner.selectors'; import { technicalRecordsLoadingState } from '@store/technical-records'; import { testResultLoadingState } from '@store/test-records'; @@ -12,59 +12,59 @@ import { SpinnerComponent } from '../../core/components/spinner/spinner.componen import { LoadingService } from './loading.service'; describe('Spinner-Service', () => { - let service: LoadingService; - let mockStore: MockStore; + let service: LoadingService; + let mockStore: MockStore; - beforeEach(() => { - TestBed.configureTestingModule({ - declarations: [SpinnerComponent], - imports: [RouterTestingModule], - providers: [LoadingService, provideMockStore({ initialState: initialAppState })], - }); + beforeEach(() => { + TestBed.configureTestingModule({ + declarations: [SpinnerComponent], + imports: [RouterTestingModule], + providers: [LoadingService, provideMockStore({ initialState: initialAppState })], + }); - service = TestBed.inject(LoadingService); - mockStore = TestBed.inject(MockStore); - }); + service = TestBed.inject(LoadingService); + mockStore = TestBed.inject(MockStore); + }); - it('should create the spinner service', () => { - expect(service).toBeTruthy(); - }); + it('should create the spinner service', () => { + expect(service).toBeTruthy(); + }); - describe('showSpinner$', () => { - beforeEach(() => { - mockStore.resetSelectors(); - }); + describe('showSpinner$', () => { + beforeEach(() => { + mockStore.resetSelectors(); + }); - it('Should return false when global spinner, testResult and technicalRecords loading state is false', async () => { - mockStore.overrideSelector(getSpinner, false); - mockStore.overrideSelector(testResultLoadingState, false); - mockStore.overrideSelector(technicalRecordsLoadingState, false); - expect(await firstValueFrom(service.showSpinner$.pipe(take(1)))).toBeFalsy(); - }); + it('Should return false when global spinner, testResult and technicalRecords loading state is false', async () => { + mockStore.overrideSelector(getSpinner, false); + mockStore.overrideSelector(testResultLoadingState, false); + mockStore.overrideSelector(technicalRecordsLoadingState, false); + expect(await firstValueFrom(service.showSpinner$.pipe(take(1)))).toBeFalsy(); + }); - it('Should return true when global spinner state is true', async () => { - mockStore.overrideSelector(getSpinner, true); - expect(await firstValueFrom(service.showSpinner$.pipe(take(1)))).toBeTruthy(); - }); + it('Should return true when global spinner state is true', async () => { + mockStore.overrideSelector(getSpinner, true); + expect(await firstValueFrom(service.showSpinner$.pipe(take(1)))).toBeTruthy(); + }); - it('Should return true when testResult loading state is true', async () => { - mockStore.overrideSelector(testResultLoadingState, true); - expect(await firstValueFrom(service.showSpinner$.pipe(take(1)))).toBeTruthy(); - }); + it('Should return true when testResult loading state is true', async () => { + mockStore.overrideSelector(testResultLoadingState, true); + expect(await firstValueFrom(service.showSpinner$.pipe(take(1)))).toBeTruthy(); + }); - it('Should return true when TechnicalRecords loading state is true', async () => { - mockStore.overrideSelector(technicalRecordsLoadingState, true); - expect(await firstValueFrom(service.showSpinner$.pipe(take(1)))).toBeTruthy(); - }); + it('Should return true when TechnicalRecords loading state is true', async () => { + mockStore.overrideSelector(technicalRecordsLoadingState, true); + expect(await firstValueFrom(service.showSpinner$.pipe(take(1)))).toBeTruthy(); + }); - it('Should return true when testTypes loading state is true', async () => { - mockStore.overrideSelector(selectTestTypesLoadingState, true); - expect(await firstValueFrom(service.showSpinner$.pipe(take(1)))).toBeTruthy(); - }); + it('Should return true when testTypes loading state is true', async () => { + mockStore.overrideSelector(selectTestTypesLoadingState, true); + expect(await firstValueFrom(service.showSpinner$.pipe(take(1)))).toBeTruthy(); + }); - it('Should return true when test stations loading state is true', async () => { - mockStore.overrideSelector(testStationsLoadingState, true); - expect(await firstValueFrom(service.showSpinner$.pipe(take(1)))).toBeTruthy(); - }); - }); + it('Should return true when test stations loading state is true', async () => { + mockStore.overrideSelector(testStationsLoadingState, true); + expect(await firstValueFrom(service.showSpinner$.pipe(take(1)))).toBeTruthy(); + }); + }); }); diff --git a/src/app/services/loading/loading.service.ts b/src/app/services/loading/loading.service.ts index 6a62e419d4..75514811d4 100644 --- a/src/app/services/loading/loading.service.ts +++ b/src/app/services/loading/loading.service.ts @@ -1,5 +1,5 @@ -import { Injectable } from '@angular/core'; -import { select, Store } from '@ngrx/store'; +import { Injectable, inject } from '@angular/core'; +import { Store, select } from '@ngrx/store'; import { defectsLoadingState } from '@store/defects'; import { referenceDataLoadingState } from '@store/reference-data'; import { requiredStandardsLoadingState } from '@store/required-standards/selectors/required-standards.selector'; @@ -9,39 +9,39 @@ import { technicalRecordsLoadingState } from '@store/technical-records'; import { testResultLoadingState } from '@store/test-records'; import { testStationsLoadingState } from '@store/test-stations'; import { selectTestTypesLoadingState } from '@store/test-types/selectors/test-types.selectors'; -import { combineLatest, map, Observable } from 'rxjs'; +import { Observable, combineLatest, map } from 'rxjs'; @Injectable({ - providedIn: 'root', + providedIn: 'root', }) export class LoadingService { - globalLoadingState$: Observable = this.store.pipe(select(getSpinner)); - testResultLoadingState$: Observable = this.store.pipe(select(testResultLoadingState)); - techRecordsLoadingState$: Observable = this.store.pipe(select(technicalRecordsLoadingState)); - testTypesLoadingState$: Observable = this.store.pipe(select(selectTestTypesLoadingState)); - testStationsLoadingState$: Observable = this.store.pipe(select(testStationsLoadingState)); - defectsLoadingState$: Observable = this.store.pipe(select(defectsLoadingState)); - referenceDataLoadingState$: Observable = this.store.pipe(select(referenceDataLoadingState)); - techRecordSearchLoadingState$: Observable = this.store.pipe(select(selectTechRecordSearchLoadingState)); - requiredStandardsLoadingState$: Observable = this.store.pipe(select(requiredStandardsLoadingState)); + private store = inject(Store); - constructor(private store: Store) {} + globalLoadingState$: Observable = this.store.pipe(select(getSpinner)); + testResultLoadingState$: Observable = this.store.pipe(select(testResultLoadingState)); + techRecordsLoadingState$: Observable = this.store.pipe(select(technicalRecordsLoadingState)); + testTypesLoadingState$: Observable = this.store.pipe(select(selectTestTypesLoadingState)); + testStationsLoadingState$: Observable = this.store.pipe(select(testStationsLoadingState)); + defectsLoadingState$: Observable = this.store.pipe(select(defectsLoadingState)); + referenceDataLoadingState$: Observable = this.store.pipe(select(referenceDataLoadingState)); + techRecordSearchLoadingState$: Observable = this.store.pipe(select(selectTechRecordSearchLoadingState)); + requiredStandardsLoadingState$: Observable = this.store.pipe(select(requiredStandardsLoadingState)); - private get reduceLoadingStates$() { - return combineLatest([ - this.globalLoadingState$, - this.testResultLoadingState$, - this.techRecordsLoadingState$, - this.testTypesLoadingState$, - this.testStationsLoadingState$, - this.defectsLoadingState$, - this.referenceDataLoadingState$, - this.techRecordSearchLoadingState$, - this.requiredStandardsLoadingState$, - ]).pipe(map((states) => states.some((b) => b))); - } + private get reduceLoadingStates$() { + return combineLatest([ + this.globalLoadingState$, + this.testResultLoadingState$, + this.techRecordsLoadingState$, + this.testTypesLoadingState$, + this.testStationsLoadingState$, + this.defectsLoadingState$, + this.referenceDataLoadingState$, + this.techRecordSearchLoadingState$, + this.requiredStandardsLoadingState$, + ]).pipe(map((states) => states.some((b) => b))); + } - get showSpinner$(): Observable { - return this.reduceLoadingStates$; - } + get showSpinner$(): Observable { + return this.reduceLoadingStates$; + } } diff --git a/src/app/services/reference-data/reference-data.service.spec.ts b/src/app/services/reference-data/reference-data.service.spec.ts index cc6fff2ae1..db229ec402 100644 --- a/src/app/services/reference-data/reference-data.service.spec.ts +++ b/src/app/services/reference-data/reference-data.service.spec.ts @@ -3,405 +3,415 @@ import { TestBed } from '@angular/core/testing'; import { ReferenceDataApiResponse, ReferenceDataItem } from '@api/reference-data'; import { MultiOptions } from '@forms/models/options.model'; import { - ReferenceDataModelBase, ReferenceDataResourceType, ReferenceDataTyre, User, + ReferenceDataModelBase, + ReferenceDataResourceType, + ReferenceDataTyre, + User, } from '@models/reference-data.model'; import { VehicleTypes } from '@models/vehicle-tech-record.model'; import { MockStore, provideMockStore } from '@ngrx/store/testing'; import { UserService } from '@services/user-service/user-service'; import { initialAppState } from '@store/.'; import { - ReferenceDataEntityStateSearch, - STORE_FEATURE_REFERENCE_DATA_KEY, - addSearchInformation, - fetchReferenceData, - fetchReferenceDataByKeySearch, - fetchTyreReferenceDataByKeySearch, - initialReferenceDataState, - referencePsvMakeLoadingState, - removeTyreSearch, - selectTyreSearchCriteria, + ReferenceDataEntityStateSearch, + STORE_FEATURE_REFERENCE_DATA_KEY, + addSearchInformation, + fetchReferenceData, + fetchReferenceDataByKeySearch, + fetchTyreReferenceDataByKeySearch, + initialReferenceDataState, + referencePsvMakeLoadingState, + removeTyreSearch, + selectTyreSearchCriteria, } from '@store/reference-data'; import { testCases } from '@store/reference-data/reference-data.test-cases'; import { firstValueFrom, of, take } from 'rxjs'; import { ReferenceDataService } from './reference-data.service'; describe('ReferenceDataService', () => { - let service: ReferenceDataService; - let controller: HttpTestingController; - let store: MockStore; - - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [HttpClientTestingModule], - providers: [ - provideMockStore({ initialState: initialAppState }), - ReferenceDataService, - { provide: UserService, useValue: { id$: of('id'), name$: of('Jack') } }, - ], - }); - - service = TestBed.inject(ReferenceDataService); - controller = TestBed.inject(HttpTestingController); - store = TestBed.inject(MockStore); - - controller.verify(); - }); - - afterEach(() => { - store.refreshState(); - }); - - it('should be created', () => { - expect(service).toBeTruthy(); - }); - - describe('API', () => { - describe('resourceTypes', () => { - it.each(testCases)('should return all data for a given resourceType', (value) => { - const apiResponse: ReferenceDataApiResponse = { data: value.payload }; - service.fetchReferenceData(value.resourceType).subscribe((response) => { - expect(response).toEqual(apiResponse); - }); - - const req = controller.expectOne('https://url/api/v1/reference/COUNTRY_OF_REGISTRATION'); - req.flush(apiResponse); - }); - }); - - it('should thrown an error if resource type is not given', (done) => { - service.fetchReferenceData(undefined as unknown as ReferenceDataResourceType).subscribe({ - error: (e) => { - expect(e.message).toBe('Reference data resourceType is required'); - done(); - }, - }); - }); - }); - - describe('fetchReferenceDataByKeySearch', () => { - it('should call the correct end point', () => { - const value = { - payload: [ - { - tyreCode: '123', - resourceType: ReferenceDataResourceType.Tyres, - resourceKey: '123', - code: '123', - loadIndexSingleLoad: '102', - tyreSize: 'size', - dateTimeStamp: 'time', - userId: '1234', - loadIndexTwinLoad: '101', - plyRating: '18', - }, - ], - }; - const apiResponse: ReferenceDataApiResponse = { data: value.payload }; - service.fetchReferenceDataByKeySearch(ReferenceDataResourceType.Tyres, '101').subscribe((data) => { - expect(data).toEqual(apiResponse); - }); - - const req = controller.expectOne('https://url/api/v1/reference/lookup/TYRES/101'); - req.flush(apiResponse); - }); - }); - - describe('fetchTyreReferenceDataByKeySearch', () => { - it('should call the correct end point', () => { - const value = { - payload: [ - { - tyreCode: '123', - resourceType: ReferenceDataResourceType.Tyres, - resourceKey: '123', - code: '123', - loadIndexSingleLoad: '102', - tyreSize: 'size', - dateTimeStamp: 'time', - userId: '1234', - loadIndexTwinLoad: '101', - plyRating: '18', - }, - ], - }; - const apiResponse: ReferenceDataApiResponse = { data: value.payload }; - service.fetchTyreReferenceDataByKeySearch('plyrating', '10').subscribe((data) => { - expect(data).toEqual(apiResponse); - }); - - const req = controller.expectOne('https://url/api/v1/reference/lookup/tyres/plyrating/10'); - req.flush(apiResponse); - }); - }); - - describe('resourceKeys', () => { - it.each(testCases)('should return one result for a given resourceType and resourceKey', (value) => { - const getOneFromResourceSpy = jest.spyOn(service, 'referenceResourceTypeResourceKeyGet'); - const { resourceType, resourceKey, payload } = value; - const resource = payload.find((p) => p.resourceKey === resourceKey) as ReferenceDataItem; - expect(resource).toBeDefined(); - const expectedResult: ReferenceDataApiResponse = { data: [resource] }; - - service.fetchReferenceDataByKey(resourceType, resourceKey).subscribe((response) => { - expect(response).toEqual(expectedResult); - expect(getOneFromResourceSpy).toHaveBeenCalled(); - }); - - const req = controller.expectOne('https://url/api/v1/reference/COUNTRY_OF_REGISTRATION/gb'); - req.flush(expectedResult); - }); - }); - describe('createReferenceDataItem', () => { - it('should return the created item', () => { - const resourceType = ReferenceDataResourceType.CountryOfRegistration; - const resourceKey = 'testKey'; - const item = { description: 'test Item' }; - const apiResponse = { resourceType, resourceKey, ...item }; - service.createReferenceDataItem(resourceType, resourceKey, item).subscribe((data) => { - expect(data).toEqual(item); - }); - - const req = controller.expectOne(`https://url/api/v1/reference/${resourceType}/${resourceKey}`); - expect(req.request.method).toBe('POST'); - - req.flush(apiResponse); - }); - }); - - describe('amendReferenceDataItem', () => { - it('should return the amended item', () => { - const resourceType = ReferenceDataResourceType.CountryOfRegistration; - const resourceKey = 'testKey'; - const item = { description: 'test Item' }; - const apiResponse = { resourceType, resourceKey, ...item }; - service.amendReferenceDataItem(resourceType, resourceKey, item).subscribe((data) => { - expect(data).toEqual(apiResponse); - }); - - const req = controller.expectOne(`https://url/api/v1/reference/${resourceType}/${resourceKey}`); - expect(req.request.method).toBe('PUT'); - - req.flush(apiResponse); - }); - }); - - describe('deleteReferenceDataItem', () => { - it('should return a delete item', () => { - const resourceType = ReferenceDataResourceType.CountryOfRegistration; - const resourceKey = 'testKey'; - const apiResponse = { success: 'true' }; - service.deleteReferenceDataItem(resourceType, resourceKey, { createdId: 'test' }).subscribe((data) => { - expect(data).toEqual(apiResponse); - }); - const req = controller.expectOne(`https://url/api/v1/reference/${resourceType}/${resourceKey}`); - expect(req.request.method).toBe('DELETE'); - - req.flush(apiResponse); - }); - }); - - describe('selectors', () => { - beforeEach(() => { - store.setState({ - ...initialAppState, - [STORE_FEATURE_REFERENCE_DATA_KEY]: { - ...initialReferenceDataState, - [ReferenceDataResourceType.CountryOfRegistration]: { - ids: ['gb', 'gba'], - entities: { - gb: { - resourceType: ReferenceDataResourceType.CountryOfRegistration, - resourceKey: 'gb', - description: 'Great Britain and Northern Ireland - GB', - }, - gba: { - resourceType: ReferenceDataResourceType.CountryOfRegistration, - resourceKey: 'gba', - description: 'Alderney - GBA', - }, - }, - }, - [ReferenceDataResourceType.ReasonsForAbandoningPsv]: { - ids: ['foobar'], - entities: { - foobar: { - resourceType: ReferenceDataResourceType.ReasonsForAbandoningPsv, - resourceKey: 'foobar', - }, - }, - }, - }, - }); - }); - - it('should get all of the reference data', (done) => { - service.getAll$(ReferenceDataResourceType.CountryOfRegistration).subscribe((response) => { - expect(response).toEqual([ - { - resourceType: ReferenceDataResourceType.CountryOfRegistration, - resourceKey: 'gb', - description: 'Great Britain and Northern Ireland - GB', - }, - { - resourceType: ReferenceDataResourceType.CountryOfRegistration, - resourceKey: 'gba', - description: 'Alderney - GBA', - }, - ]); - done(); - }); - }); - - it('should get a specific reference data record', (done) => { - service.getByKey$(ReferenceDataResourceType.CountryOfRegistration, 'gba').subscribe((response) => { - expect(response).toEqual({ - resourceType: ReferenceDataResourceType.CountryOfRegistration, - resourceKey: 'gba', - description: 'Alderney - GBA', - }); - done(); - }); - }); - - it('should get the tyre search results', (done) => { - const mockReferenceDataTyre = [{ code: 'foo' }] as ReferenceDataTyre[]; - jest.spyOn(service, 'getTyreSearchReturn$').mockReturnValue(of(mockReferenceDataTyre)); - service - .getTyreSearchReturn$() - .pipe(take(1)) - .subscribe((referenceData) => { - expect(referenceData).toEqual(mockReferenceDataTyre); - done(); - }); - }); - it('should get the tyre search criteria', (done) => { - const mockState = { loading: false } as ReferenceDataEntityStateSearch; - store.overrideSelector(selectTyreSearchCriteria, mockState); - service - .getTyreSearchCriteria$() - .pipe(take(1)) - .subscribe((referenceData) => { - expect(referenceData).toEqual(mockState); - done(); - }); - }); - it('should get the psv make reference data loading', (done) => { - store.overrideSelector(referencePsvMakeLoadingState, false); - service - .getReferencePsvMakeDataLoading$() - .pipe(take(1)) - .subscribe((loadingFlag) => { - expect(loadingFlag).toBe(false); - done(); - }); - }); - - it('should get the data from state and format the response', (done) => { - service - .getReferenceDataOptions(ReferenceDataResourceType.CountryOfRegistration) - .pipe(take(1)) - .subscribe((data) => { - expect(data).toEqual([ - { label: 'Great Britain and Northern Ireland - GB', value: 'gb' }, - { label: 'Alderney - GBA', value: 'gba' }, - ]); - done(); - }); - }); - it('should get the psv data from state and format the response', (done) => { - service - .getReasonsForAbandoning(VehicleTypes.PSV) - .pipe(take(1)) - .subscribe((data) => { - expect(data).toEqual([{ label: 'foobar', value: 'foobar' }]); - done(); - }); - }); - - it('should return if vehicle Type if undefined', (done) => { - service - .getReasonsForAbandoning(undefined) - .pipe(take(1)) - .subscribe((data) => { - expect(data).toEqual([]); - done(); - }); - }); - }); - - describe('helper function', () => { - describe('mapReferenceDataOptions', () => { - it.each([ - { - refData: [ - { - resourceType: ReferenceDataResourceType.Brakes, - resourceKey: 'banana', - description: 'yellow', - }, - ] as ReferenceDataModelBase[], - output: [{ label: 'yellow', value: 'banana' }] as MultiOptions, - }, - { - refData: [ - { - resourceType: ReferenceDataResourceType.User, - resourceKey: 'mike@mail.com', - name: 'Mike', - }, - ] as Array>, - output: [{ label: 'Mike', value: 'mike@mail.com' }], - }, - { - refData: [ - { - resourceType: ReferenceDataResourceType.User, - resourceKey: 'mike@mail.com', - }, - ] as Array>, - output: [{ label: 'mike@mail.com', value: 'mike@mail.com' }], - }, - ])('should return MultiOption Array with description as label', async ({ refData, output }) => { - const options = await firstValueFrom(of(refData).pipe(take(1), service['mapReferenceDataOptions'])); - expect(options).toEqual(output); - }); - }); - }); - - describe('search methods', () => { - describe('fetchReferenceDataByKeySearch', () => { - it('should dispatch the action to fetchReferenceDataByKeySearch', () => { - const dispatchSpy = jest.spyOn(store, 'dispatch'); - service.loadReferenceDataByKeySearch(ReferenceDataResourceType.CountryOfRegistration, 'foo'); - expect(dispatchSpy).toHaveBeenCalledWith( - fetchReferenceDataByKeySearch({ resourceType: ReferenceDataResourceType.CountryOfRegistration, resourceKey: 'foo' }), - ); - }); - }); - describe('loadTyreReferenceDataByKeySearch', () => { - it('should dispatch the action to loadTyreReferenceDataByKeySearch', () => { - const dispatchSpy = jest.spyOn(store, 'dispatch'); - service.loadTyreReferenceDataByKeySearch('foo', 'bar'); - expect(dispatchSpy).toHaveBeenCalledWith(fetchTyreReferenceDataByKeySearch({ searchFilter: 'foo', searchTerm: 'bar' })); - }); - }); - }); - - describe('store methods', () => { - it('should dispatch the fetchReferenceData action', () => { - const dispatchSpy = jest.spyOn(store, 'dispatch'); - service.loadReferenceData(ReferenceDataResourceType.CountryOfRegistration); - expect(dispatchSpy).toHaveBeenCalledWith(fetchReferenceData({ resourceType: ReferenceDataResourceType.CountryOfRegistration })); - }); - it('should dispatch the addSearchInformation action', () => { - const dispatchSpy = jest.spyOn(store, 'dispatch'); - service.addSearchInformation('foo', 'bar'); - expect(dispatchSpy).toHaveBeenCalledWith(addSearchInformation({ filter: 'foo', term: 'bar' })); - }); - it('should dispatch the removeTyreSearch action', () => { - const dispatchSpy = jest.spyOn(store, 'dispatch'); - service.removeTyreSearch(); - expect(dispatchSpy).toHaveBeenCalledWith(removeTyreSearch()); - }); - }); + let service: ReferenceDataService; + let controller: HttpTestingController; + let store: MockStore; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [HttpClientTestingModule], + providers: [ + provideMockStore({ initialState: initialAppState }), + ReferenceDataService, + { provide: UserService, useValue: { id$: of('id'), name$: of('Jack') } }, + ], + }); + + service = TestBed.inject(ReferenceDataService); + controller = TestBed.inject(HttpTestingController); + store = TestBed.inject(MockStore); + + controller.verify(); + }); + + afterEach(() => { + store.refreshState(); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); + + describe('API', () => { + describe('resourceTypes', () => { + it.each(testCases)('should return all data for a given resourceType', (value) => { + const apiResponse: ReferenceDataApiResponse = { data: value.payload }; + service.fetchReferenceData(value.resourceType).subscribe((response) => { + expect(response).toEqual(apiResponse); + }); + + const req = controller.expectOne('https://url/api/v1/reference/COUNTRY_OF_REGISTRATION'); + req.flush(apiResponse); + }); + }); + + it('should thrown an error if resource type is not given', (done) => { + service.fetchReferenceData(undefined as unknown as ReferenceDataResourceType).subscribe({ + error: (e) => { + expect(e.message).toBe('Reference data resourceType is required'); + done(); + }, + }); + }); + }); + + describe('fetchReferenceDataByKeySearch', () => { + it('should call the correct end point', () => { + const value = { + payload: [ + { + tyreCode: '123', + resourceType: ReferenceDataResourceType.Tyres, + resourceKey: '123', + code: '123', + loadIndexSingleLoad: '102', + tyreSize: 'size', + dateTimeStamp: 'time', + userId: '1234', + loadIndexTwinLoad: '101', + plyRating: '18', + }, + ], + }; + const apiResponse: ReferenceDataApiResponse = { data: value.payload }; + service.fetchReferenceDataByKeySearch(ReferenceDataResourceType.Tyres, '101').subscribe((data) => { + expect(data).toEqual(apiResponse); + }); + + const req = controller.expectOne('https://url/api/v1/reference/lookup/TYRES/101'); + req.flush(apiResponse); + }); + }); + + describe('fetchTyreReferenceDataByKeySearch', () => { + it('should call the correct end point', () => { + const value = { + payload: [ + { + tyreCode: '123', + resourceType: ReferenceDataResourceType.Tyres, + resourceKey: '123', + code: '123', + loadIndexSingleLoad: '102', + tyreSize: 'size', + dateTimeStamp: 'time', + userId: '1234', + loadIndexTwinLoad: '101', + plyRating: '18', + }, + ], + }; + const apiResponse: ReferenceDataApiResponse = { data: value.payload }; + service.fetchTyreReferenceDataByKeySearch('plyrating', '10').subscribe((data) => { + expect(data).toEqual(apiResponse); + }); + + const req = controller.expectOne('https://url/api/v1/reference/lookup/tyres/plyrating/10'); + req.flush(apiResponse); + }); + }); + + describe('resourceKeys', () => { + it.each(testCases)('should return one result for a given resourceType and resourceKey', (value) => { + const getOneFromResourceSpy = jest.spyOn(service, 'referenceResourceTypeResourceKeyGet'); + const { resourceType, resourceKey, payload } = value; + const resource = payload.find((p) => p.resourceKey === resourceKey) as ReferenceDataItem; + expect(resource).toBeDefined(); + const expectedResult: ReferenceDataApiResponse = { data: [resource] }; + + service.fetchReferenceDataByKey(resourceType, resourceKey).subscribe((response) => { + expect(response).toEqual(expectedResult); + expect(getOneFromResourceSpy).toHaveBeenCalled(); + }); + + const req = controller.expectOne('https://url/api/v1/reference/COUNTRY_OF_REGISTRATION/gb'); + req.flush(expectedResult); + }); + }); + describe('createReferenceDataItem', () => { + it('should return the created item', () => { + const resourceType = ReferenceDataResourceType.CountryOfRegistration; + const resourceKey = 'testKey'; + const item = { description: 'test Item' }; + const apiResponse = { resourceType, resourceKey, ...item }; + service.createReferenceDataItem(resourceType, resourceKey, item).subscribe((data) => { + expect(data).toEqual(item); + }); + + const req = controller.expectOne(`https://url/api/v1/reference/${resourceType}/${resourceKey}`); + expect(req.request.method).toBe('POST'); + + req.flush(apiResponse); + }); + }); + + describe('amendReferenceDataItem', () => { + it('should return the amended item', () => { + const resourceType = ReferenceDataResourceType.CountryOfRegistration; + const resourceKey = 'testKey'; + const item = { description: 'test Item' }; + const apiResponse = { resourceType, resourceKey, ...item }; + service.amendReferenceDataItem(resourceType, resourceKey, item).subscribe((data) => { + expect(data).toEqual(apiResponse); + }); + + const req = controller.expectOne(`https://url/api/v1/reference/${resourceType}/${resourceKey}`); + expect(req.request.method).toBe('PUT'); + + req.flush(apiResponse); + }); + }); + + describe('deleteReferenceDataItem', () => { + it('should return a delete item', () => { + const resourceType = ReferenceDataResourceType.CountryOfRegistration; + const resourceKey = 'testKey'; + const apiResponse = { success: 'true' }; + service.deleteReferenceDataItem(resourceType, resourceKey, { createdId: 'test' }).subscribe((data) => { + expect(data).toEqual(apiResponse); + }); + const req = controller.expectOne(`https://url/api/v1/reference/${resourceType}/${resourceKey}`); + expect(req.request.method).toBe('DELETE'); + + req.flush(apiResponse); + }); + }); + + describe('selectors', () => { + beforeEach(() => { + store.setState({ + ...initialAppState, + [STORE_FEATURE_REFERENCE_DATA_KEY]: { + ...initialReferenceDataState, + [ReferenceDataResourceType.CountryOfRegistration]: { + ids: ['gb', 'gba'], + entities: { + gb: { + resourceType: ReferenceDataResourceType.CountryOfRegistration, + resourceKey: 'gb', + description: 'Great Britain and Northern Ireland - GB', + }, + gba: { + resourceType: ReferenceDataResourceType.CountryOfRegistration, + resourceKey: 'gba', + description: 'Alderney - GBA', + }, + }, + }, + [ReferenceDataResourceType.ReasonsForAbandoningPsv]: { + ids: ['foobar'], + entities: { + foobar: { + resourceType: ReferenceDataResourceType.ReasonsForAbandoningPsv, + resourceKey: 'foobar', + }, + }, + }, + }, + }); + }); + + it('should get all of the reference data', (done) => { + service.getAll$(ReferenceDataResourceType.CountryOfRegistration).subscribe((response) => { + expect(response).toEqual([ + { + resourceType: ReferenceDataResourceType.CountryOfRegistration, + resourceKey: 'gb', + description: 'Great Britain and Northern Ireland - GB', + }, + { + resourceType: ReferenceDataResourceType.CountryOfRegistration, + resourceKey: 'gba', + description: 'Alderney - GBA', + }, + ]); + done(); + }); + }); + + it('should get a specific reference data record', (done) => { + service.getByKey$(ReferenceDataResourceType.CountryOfRegistration, 'gba').subscribe((response) => { + expect(response).toEqual({ + resourceType: ReferenceDataResourceType.CountryOfRegistration, + resourceKey: 'gba', + description: 'Alderney - GBA', + }); + done(); + }); + }); + + it('should get the tyre search results', (done) => { + const mockReferenceDataTyre = [{ code: 'foo' }] as ReferenceDataTyre[]; + jest.spyOn(service, 'getTyreSearchReturn$').mockReturnValue(of(mockReferenceDataTyre)); + service + .getTyreSearchReturn$() + .pipe(take(1)) + .subscribe((referenceData) => { + expect(referenceData).toEqual(mockReferenceDataTyre); + done(); + }); + }); + it('should get the tyre search criteria', (done) => { + const mockState = { loading: false } as ReferenceDataEntityStateSearch; + store.overrideSelector(selectTyreSearchCriteria, mockState); + service + .getTyreSearchCriteria$() + .pipe(take(1)) + .subscribe((referenceData) => { + expect(referenceData).toEqual(mockState); + done(); + }); + }); + it('should get the psv make reference data loading', (done) => { + store.overrideSelector(referencePsvMakeLoadingState, false); + service + .getReferencePsvMakeDataLoading$() + .pipe(take(1)) + .subscribe((loadingFlag) => { + expect(loadingFlag).toBe(false); + done(); + }); + }); + + it('should get the data from state and format the response', (done) => { + service + .getReferenceDataOptions(ReferenceDataResourceType.CountryOfRegistration) + .pipe(take(1)) + .subscribe((data) => { + expect(data).toEqual([ + { label: 'Great Britain and Northern Ireland - GB', value: 'gb' }, + { label: 'Alderney - GBA', value: 'gba' }, + ]); + done(); + }); + }); + it('should get the psv data from state and format the response', (done) => { + service + .getReasonsForAbandoning(VehicleTypes.PSV) + .pipe(take(1)) + .subscribe((data) => { + expect(data).toEqual([{ label: 'foobar', value: 'foobar' }]); + done(); + }); + }); + + it('should return if vehicle Type if undefined', (done) => { + service + .getReasonsForAbandoning(undefined) + .pipe(take(1)) + .subscribe((data) => { + expect(data).toEqual([]); + done(); + }); + }); + }); + + describe('helper function', () => { + describe('mapReferenceDataOptions', () => { + it.each([ + { + refData: [ + { + resourceType: ReferenceDataResourceType.Brakes, + resourceKey: 'banana', + description: 'yellow', + }, + ] as ReferenceDataModelBase[], + output: [{ label: 'yellow', value: 'banana' }] as MultiOptions, + }, + { + refData: [ + { + resourceType: ReferenceDataResourceType.User, + resourceKey: 'mike@mail.com', + name: 'Mike', + }, + ] as Array>, + output: [{ label: 'Mike', value: 'mike@mail.com' }], + }, + { + refData: [ + { + resourceType: ReferenceDataResourceType.User, + resourceKey: 'mike@mail.com', + }, + ] as Array>, + output: [{ label: 'mike@mail.com', value: 'mike@mail.com' }], + }, + ])('should return MultiOption Array with description as label', async ({ refData, output }) => { + const options = await firstValueFrom(of(refData).pipe(take(1), service['mapReferenceDataOptions'])); + expect(options).toEqual(output); + }); + }); + }); + + describe('search methods', () => { + describe('fetchReferenceDataByKeySearch', () => { + it('should dispatch the action to fetchReferenceDataByKeySearch', () => { + const dispatchSpy = jest.spyOn(store, 'dispatch'); + service.loadReferenceDataByKeySearch(ReferenceDataResourceType.CountryOfRegistration, 'foo'); + expect(dispatchSpy).toHaveBeenCalledWith( + fetchReferenceDataByKeySearch({ + resourceType: ReferenceDataResourceType.CountryOfRegistration, + resourceKey: 'foo', + }) + ); + }); + }); + describe('loadTyreReferenceDataByKeySearch', () => { + it('should dispatch the action to loadTyreReferenceDataByKeySearch', () => { + const dispatchSpy = jest.spyOn(store, 'dispatch'); + service.loadTyreReferenceDataByKeySearch('foo', 'bar'); + expect(dispatchSpy).toHaveBeenCalledWith( + fetchTyreReferenceDataByKeySearch({ searchFilter: 'foo', searchTerm: 'bar' }) + ); + }); + }); + }); + + describe('store methods', () => { + it('should dispatch the fetchReferenceData action', () => { + const dispatchSpy = jest.spyOn(store, 'dispatch'); + service.loadReferenceData(ReferenceDataResourceType.CountryOfRegistration); + expect(dispatchSpy).toHaveBeenCalledWith( + fetchReferenceData({ resourceType: ReferenceDataResourceType.CountryOfRegistration }) + ); + }); + it('should dispatch the addSearchInformation action', () => { + const dispatchSpy = jest.spyOn(store, 'dispatch'); + service.addSearchInformation('foo', 'bar'); + expect(dispatchSpy).toHaveBeenCalledWith(addSearchInformation({ filter: 'foo', term: 'bar' })); + }); + it('should dispatch the removeTyreSearch action', () => { + const dispatchSpy = jest.spyOn(store, 'dispatch'); + service.removeTyreSearch(); + expect(dispatchSpy).toHaveBeenCalledWith(removeTyreSearch()); + }); + }); }); diff --git a/src/app/services/reference-data/reference-data.service.ts b/src/app/services/reference-data/reference-data.service.ts index dff54d4f24..5aef4fd692 100644 --- a/src/app/services/reference-data/reference-data.service.ts +++ b/src/app/services/reference-data/reference-data.service.ts @@ -1,192 +1,223 @@ import { HttpClient } from '@angular/common/http'; import { Inject, Injectable, Optional } from '@angular/core'; import { - BASE_PATH, - Configuration, - ReferenceDataApiResponse, - ReferenceDataService as ReferenceDataApiService, - ReferenceDataItemApiResponse, + BASE_PATH, + Configuration, + ReferenceDataApiResponse, + ReferenceDataService as ReferenceDataApiService, + ReferenceDataItemApiResponse, } from '@api/reference-data'; import { MultiOptions } from '@forms/models/options.model'; import { - ReferenceDataModelBase, ReferenceDataResourceType, ReferenceDataTyre, User, + ReferenceDataModelBase, + ReferenceDataResourceType, + ReferenceDataTyre, + User, } from '@models/reference-data.model'; import { VehicleTypes } from '@models/vehicle-tech-record.model'; import { Store, select } from '@ngrx/store'; import { UserService } from '@services/user-service/user-service'; import { - ReferenceDataEntityStateSearch, - ReferenceDataState, - addSearchInformation, - fetchReferenceData, - fetchReferenceDataByKey, - fetchReferenceDataByKeySearch, - fetchTyreReferenceDataByKeySearch, - referencePsvMakeLoadingState, - removeReferenceDataByKey, - removeTyreSearch, - selectAllReferenceDataByResourceType, - selectReasonsForAbandoning, - selectReferenceDataByResourceKey, - selectSearchReturn, - selectTyreSearchCriteria, + ReferenceDataEntityStateSearch, + ReferenceDataState, + addSearchInformation, + fetchReferenceData, + fetchReferenceDataByKey, + fetchReferenceDataByKeySearch, + fetchTyreReferenceDataByKeySearch, + referencePsvMakeLoadingState, + removeReferenceDataByKey, + removeTyreSearch, + selectAllReferenceDataByResourceType, + selectReasonsForAbandoning, + selectReferenceDataByResourceKey, + selectSearchReturn, + selectTyreSearchCriteria, } from '@store/reference-data'; -import { - Observable, of, switchMap, throwError, withLatestFrom, -} from 'rxjs'; +import { Observable, of, switchMap, throwError, withLatestFrom } from 'rxjs'; @Injectable({ - providedIn: 'root', + providedIn: 'root', }) export class ReferenceDataService extends ReferenceDataApiService { - constructor( - @Optional() @Inject(BASE_PATH) basePath: string, - @Optional() configuration: Configuration, - httpClient: HttpClient, - private usersService: UserService, - private store: Store, - ) { - super(httpClient, basePath, configuration); - } - - // URL to POST new reference data items: /reference/{ type capitalized }/{ new key } POST - // eslint-disable-next-line @typescript-eslint/no-explicit-any - createReferenceDataItem(type: ReferenceDataResourceType, key: string, data: any) { - return this.usersService.id$.pipe( - withLatestFrom(this.usersService.name$), - switchMap(([createdId, createdName]) => { - const referenceData = { - ...data, createdId, createdName, createdAt: new Date(), - }; - return this.referenceResourceTypeResourceKeyPost(type, key, referenceData, 'body', false); - }), - ); - } - - // URL to PUT new reference data items: /reference/{ type capitalized }/{ new key } PUT - // eslint-disable-next-line @typescript-eslint/no-explicit-any - amendReferenceDataItem(type: ReferenceDataResourceType, key: string, data: any) { - return this.usersService.id$.pipe( - withLatestFrom(this.usersService.name$), - switchMap(([createdId, createdName]) => { - const referenceData = { - ...data, createdId, createdName, createdAt: new Date(), - }; - return this.referenceResourceTypeResourceKeyPut(type, key, referenceData, 'body', false); - }), - ); - } - - deleteReferenceDataItem(type: ReferenceDataResourceType, key: string, payload: Record) { - return this.usersService.id$.pipe( - withLatestFrom(this.usersService.name$), - switchMap(([createdId, createdName]) => { - const deleteObject = { - ...payload, createdId, createdName, createdAt: new Date(), - }; - return this.referenceResourceTypeResourceKeyDelete(type, key, deleteObject, 'body', false); - }), - ); - } - - fetchReferenceData(resourceType: ReferenceDataResourceType, paginationToken?: string): Observable { - if (!resourceType) { - return throwError(() => new Error('Reference data resourceType is required')); - } - - return this.referenceResourceTypeGet(resourceType, paginationToken, 'body'); - } - - fetchReferenceDataAudit(resourceType: ReferenceDataResourceType, paginationToken?: string): Observable { - if (!resourceType) { - return throwError(() => new Error('Reference data resourceType is required')); - } - - return this.referenceResourceTypeGet(resourceType, paginationToken, 'body'); - } - - fetchReferenceDataByKey(resourceType: ReferenceDataResourceType, resourceKey: string | number): Observable { - return this.referenceResourceTypeResourceKeyGet(resourceType, resourceKey, 'body'); - } - - loadReferenceDataByKey(resourceType: ReferenceDataResourceType, resourceKey: string | number): void { - return this.store.dispatch(fetchReferenceDataByKey({ resourceType, resourceKey })); - } - - fetchReferenceDataByKeySearch(resourceType: ReferenceDataResourceType, resourceKey: string | number): Observable { - return this.referenceLookupResourceTypeResourceKeyGet(resourceType, resourceKey, 'body'); - } - - fetchTyreReferenceDataByKeySearch(searchFilter: string, searchTerm: string): Observable { - return this.referenceLookupTyresSearchKeyParamGet(searchFilter, searchTerm, 'body'); - } - - loadReferenceDataByKeySearch(resourceType: ReferenceDataResourceType, resourceKey: string | number): void { - this.store.dispatch(fetchReferenceDataByKeySearch({ resourceType, resourceKey })); - } - - loadTyreReferenceDataByKeySearch(searchFilter: string, searchTerm: string): void { - this.store.dispatch(fetchTyreReferenceDataByKeySearch({ searchFilter, searchTerm })); - } - - loadReferenceData(resourceType: ReferenceDataResourceType): void { - this.store.dispatch(fetchReferenceData({ resourceType })); - } - - removeReferenceDataByKey(resourceType: ReferenceDataResourceType, resourceKey: string): void { - this.store.dispatch(removeReferenceDataByKey({ resourceType, resourceKey })); - } - - addSearchInformation(filter: string, term: string): void { - this.store.dispatch(addSearchInformation({ filter, term })); - } - - removeTyreSearch(): void { - this.store.dispatch(removeTyreSearch()); - } - - getTyreSearchReturn$(): Observable { - return this.store.pipe(select(selectSearchReturn(ReferenceDataResourceType.Tyres))) as Observable; - } - - getTyreSearchCriteria$(): Observable { - return this.store.pipe(select(selectTyreSearchCriteria)); - } - - getAll$(resourceType: ReferenceDataResourceType): Observable { - return this.store.pipe(select(selectAllReferenceDataByResourceType(resourceType))); - } - - getByKey$(resourceType: ReferenceDataResourceType, resourceKey: string | number) { - return this.store.pipe(select(selectReferenceDataByResourceKey(resourceType, resourceKey))); - } - - getReferenceDataOptions(resourceType: ReferenceDataResourceType): Observable { - return this.getAll$(resourceType).pipe((source) => this.mapReferenceDataOptions(source)); - } - - private mapReferenceDataOptions( - source: Observable> | undefined>, - ): Observable { - return new Observable((subscriber) => { - source.subscribe({ - next: (val) => { - subscriber.next(val?.map((option) => ({ value: option.resourceKey, label: option.description ?? option.name ?? `${option.resourceKey}` }))); - }, - error: (e) => subscriber.error(e), - complete: () => subscriber.complete(), - }); - }); - } - - getReasonsForAbandoning(vehicleType: VehicleTypes | undefined): Observable { - if (!vehicleType) { - return of([]); - } - return this.store.pipe(select(selectReasonsForAbandoning(vehicleType)), (source) => this.mapReferenceDataOptions(source)); - } - - getReferencePsvMakeDataLoading$(): Observable { - return this.store.pipe(select(referencePsvMakeLoadingState)); - } + constructor( + @Optional() @Inject(BASE_PATH) basePath: string, + @Optional() configuration: Configuration, + httpClient: HttpClient, + private usersService: UserService, + private store: Store + ) { + super(httpClient, basePath, configuration); + } + + // URL to POST new reference data items: /reference/{ type capitalized }/{ new key } POST + // eslint-disable-next-line @typescript-eslint/no-explicit-any + createReferenceDataItem(type: ReferenceDataResourceType, key: string, data: any) { + return this.usersService.id$.pipe( + withLatestFrom(this.usersService.name$), + switchMap(([createdId, createdName]) => { + const referenceData = { + ...data, + createdId, + createdName, + createdAt: new Date(), + }; + return this.referenceResourceTypeResourceKeyPost(type, key, referenceData, 'body', false); + }) + ); + } + + // URL to PUT new reference data items: /reference/{ type capitalized }/{ new key } PUT + // eslint-disable-next-line @typescript-eslint/no-explicit-any + amendReferenceDataItem(type: ReferenceDataResourceType, key: string, data: any) { + return this.usersService.id$.pipe( + withLatestFrom(this.usersService.name$), + switchMap(([createdId, createdName]) => { + const referenceData = { + ...data, + createdId, + createdName, + createdAt: new Date(), + }; + return this.referenceResourceTypeResourceKeyPut(type, key, referenceData, 'body', false); + }) + ); + } + + deleteReferenceDataItem(type: ReferenceDataResourceType, key: string, payload: Record) { + return this.usersService.id$.pipe( + withLatestFrom(this.usersService.name$), + switchMap(([createdId, createdName]) => { + const deleteObject = { + ...payload, + createdId, + createdName, + createdAt: new Date(), + }; + return this.referenceResourceTypeResourceKeyDelete(type, key, deleteObject, 'body', false); + }) + ); + } + + fetchReferenceData( + resourceType: ReferenceDataResourceType, + paginationToken?: string + ): Observable { + if (!resourceType) { + return throwError(() => new Error('Reference data resourceType is required')); + } + + return this.referenceResourceTypeGet(resourceType, paginationToken, 'body'); + } + + fetchReferenceDataAudit( + resourceType: ReferenceDataResourceType, + paginationToken?: string + ): Observable { + if (!resourceType) { + return throwError(() => new Error('Reference data resourceType is required')); + } + + return this.referenceResourceTypeGet(resourceType, paginationToken, 'body'); + } + + fetchReferenceDataByKey( + resourceType: ReferenceDataResourceType, + resourceKey: string | number + ): Observable { + return this.referenceResourceTypeResourceKeyGet(resourceType, resourceKey, 'body'); + } + + loadReferenceDataByKey(resourceType: ReferenceDataResourceType, resourceKey: string | number): void { + return this.store.dispatch(fetchReferenceDataByKey({ resourceType, resourceKey })); + } + + fetchReferenceDataByKeySearch( + resourceType: ReferenceDataResourceType, + resourceKey: string | number + ): Observable { + return this.referenceLookupResourceTypeResourceKeyGet(resourceType, resourceKey, 'body'); + } + + fetchTyreReferenceDataByKeySearch(searchFilter: string, searchTerm: string): Observable { + return this.referenceLookupTyresSearchKeyParamGet(searchFilter, searchTerm, 'body'); + } + + loadReferenceDataByKeySearch(resourceType: ReferenceDataResourceType, resourceKey: string | number): void { + this.store.dispatch(fetchReferenceDataByKeySearch({ resourceType, resourceKey })); + } + + loadTyreReferenceDataByKeySearch(searchFilter: string, searchTerm: string): void { + this.store.dispatch(fetchTyreReferenceDataByKeySearch({ searchFilter, searchTerm })); + } + + loadReferenceData(resourceType: ReferenceDataResourceType): void { + this.store.dispatch(fetchReferenceData({ resourceType })); + } + + removeReferenceDataByKey(resourceType: ReferenceDataResourceType, resourceKey: string): void { + this.store.dispatch(removeReferenceDataByKey({ resourceType, resourceKey })); + } + + addSearchInformation(filter: string, term: string): void { + this.store.dispatch(addSearchInformation({ filter, term })); + } + + removeTyreSearch(): void { + this.store.dispatch(removeTyreSearch()); + } + + getTyreSearchReturn$(): Observable { + return this.store.pipe(select(selectSearchReturn(ReferenceDataResourceType.Tyres))) as Observable< + ReferenceDataTyre[] | null + >; + } + + getTyreSearchCriteria$(): Observable { + return this.store.pipe(select(selectTyreSearchCriteria)); + } + + getAll$(resourceType: ReferenceDataResourceType): Observable { + return this.store.pipe(select(selectAllReferenceDataByResourceType(resourceType))); + } + + getByKey$(resourceType: ReferenceDataResourceType, resourceKey: string | number) { + return this.store.pipe(select(selectReferenceDataByResourceKey(resourceType, resourceKey))); + } + + getReferenceDataOptions(resourceType: ReferenceDataResourceType): Observable { + return this.getAll$(resourceType).pipe((source) => this.mapReferenceDataOptions(source)); + } + + private mapReferenceDataOptions( + source: Observable> | undefined> + ): Observable { + return new Observable((subscriber) => { + source.subscribe({ + next: (val) => { + subscriber.next( + val?.map((option) => ({ + value: option.resourceKey, + label: option.description ?? option.name ?? `${option.resourceKey}`, + })) + ); + }, + error: (e) => subscriber.error(e), + complete: () => subscriber.complete(), + }); + }); + } + + getReasonsForAbandoning(vehicleType: VehicleTypes | undefined): Observable { + if (!vehicleType) { + return of([]); + } + return this.store.pipe(select(selectReasonsForAbandoning(vehicleType)), (source) => + this.mapReferenceDataOptions(source) + ); + } + + getReferencePsvMakeDataLoading$(): Observable { + return this.store.pipe(select(referencePsvMakeLoadingState)); + } } diff --git a/src/app/services/required-standards/required-standards.service.ts b/src/app/services/required-standards/required-standards.service.ts index 8eaa8f3307..b80503116f 100644 --- a/src/app/services/required-standards/required-standards.service.ts +++ b/src/app/services/required-standards/required-standards.service.ts @@ -6,12 +6,13 @@ import { environment } from '../../../environments/environment'; @Injectable({ providedIn: 'root' }) export class RequiredStandardsService { - private url = `${environment.VTM_API_URI}/defects/required-standards`; + private url = `${environment.VTM_API_URI}/defects/required-standards`; - constructor(private http: HttpClient) {} - - getRequiredStandards(euVehicleCategory: string): Observable { - return this.http.get(`${this.url}?euVehicleCategory=${euVehicleCategory}`, { responseType: 'json' }); - } + constructor(private http: HttpClient) {} + getRequiredStandards(euVehicleCategory: string): Observable { + return this.http.get(`${this.url}?euVehicleCategory=${euVehicleCategory}`, { + responseType: 'json', + }); + } } diff --git a/src/app/services/result-of-test/result-of-test.service.spec.ts b/src/app/services/result-of-test/result-of-test.service.spec.ts index f6bb728f6a..c1d8d42f70 100644 --- a/src/app/services/result-of-test/result-of-test.service.spec.ts +++ b/src/app/services/result-of-test/result-of-test.service.spec.ts @@ -1,36 +1,38 @@ import { TestBed } from '@angular/core/testing'; import { resultOfTestEnum } from '@models/test-types/test-type.model'; import { MockStore, provideMockStore } from '@ngrx/store/testing'; -import { initialAppState, State } from '@store/.'; +import { State, initialAppState } from '@store/.'; import { resultOfTestSelector } from '@store/test-records'; import { ResultOfTestService } from './result-of-test.service'; describe('ResultOfTestService', () => { - let service: ResultOfTestService; - let store: MockStore; + let service: ResultOfTestService; + let store: MockStore; - beforeEach(() => { - TestBed.configureTestingModule({ providers: [ResultOfTestService, provideMockStore({ initialState: initialAppState })] }); - service = TestBed.inject(ResultOfTestService); - store = TestBed.inject(MockStore); - }); + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [ResultOfTestService, provideMockStore({ initialState: initialAppState })], + }); + service = TestBed.inject(ResultOfTestService); + store = TestBed.inject(MockStore); + }); - it('should be created', () => { - expect(service).toBeTruthy(); - }); + it('should be created', () => { + expect(service).toBeTruthy(); + }); - it('should dispatch the action to update the result of the test', () => { - const dispatchSpy = jest.spyOn(store, 'dispatch'); - service.updateResultOfTest(); - expect(dispatchSpy).toHaveBeenCalledTimes(1); - }); + it('should dispatch the action to update the result of the test', () => { + const dispatchSpy = jest.spyOn(store, 'dispatch'); + service.updateResultOfTest(); + expect(dispatchSpy).toHaveBeenCalledTimes(1); + }); - it('should get the result from the selector', (done) => { - store.overrideSelector(resultOfTestSelector, resultOfTestEnum.pass); - service.resultOfTest.subscribe((result) => { - expect(result).toBe(resultOfTestEnum.pass); - done(); - }); - }); + it('should get the result from the selector', (done) => { + store.overrideSelector(resultOfTestSelector, resultOfTestEnum.pass); + service.resultOfTest.subscribe((result) => { + expect(result).toBe(resultOfTestEnum.pass); + done(); + }); + }); }); diff --git a/src/app/services/result-of-test/result-of-test.service.ts b/src/app/services/result-of-test/result-of-test.service.ts index 5d04f47d47..39f7356664 100644 --- a/src/app/services/result-of-test/result-of-test.service.ts +++ b/src/app/services/result-of-test/result-of-test.service.ts @@ -1,36 +1,39 @@ import { Injectable } from '@angular/core'; import { resultOfTestEnum } from '@models/test-types/test-type.model'; -import { select, Store } from '@ngrx/store'; +import { Store, select } from '@ngrx/store'; import { State } from '@store/.'; import { - resultOfTestSelector, setResultOfTest, updateResultOfTest, updateResultOfTestRequiredStandards, + resultOfTestSelector, + setResultOfTest, + updateResultOfTest, + updateResultOfTestRequiredStandards, } from '@store/test-records'; import { Observable } from 'rxjs'; @Injectable({ - providedIn: 'root', + providedIn: 'root', }) export class ResultOfTestService { - constructor(private store: Store) {} + constructor(private store: Store) {} - get resultOfTest(): Observable { - return this.store.pipe(select(resultOfTestSelector)); - } + get resultOfTest(): Observable { + return this.store.pipe(select(resultOfTestSelector)); + } - updateResultOfTest() { - this.store.dispatch(updateResultOfTest()); - } + updateResultOfTest() { + this.store.dispatch(updateResultOfTest()); + } - updateResultOfTestRequiredStandards() { - this.store.dispatch(updateResultOfTestRequiredStandards()); - } + updateResultOfTestRequiredStandards() { + this.store.dispatch(updateResultOfTestRequiredStandards()); + } - toggleAbandoned(result: resultOfTestEnum) { - if (result !== resultOfTestEnum.abandoned) { - this.store.dispatch(setResultOfTest({ result })); - } else { - this.store.dispatch(setResultOfTest({ result })); - this.updateResultOfTest(); - } - } + toggleAbandoned(result: resultOfTestEnum) { + if (result !== resultOfTestEnum.abandoned) { + this.store.dispatch(setResultOfTest({ result })); + } else { + this.store.dispatch(setResultOfTest({ result })); + this.updateResultOfTest(); + } + } } diff --git a/src/app/services/router/router.service.spec.ts b/src/app/services/router/router.service.spec.ts index f9f144bec5..349f34af0a 100644 --- a/src/app/services/router/router.service.spec.ts +++ b/src/app/services/router/router.service.spec.ts @@ -1,72 +1,76 @@ import { TestBed } from '@angular/core/testing'; +import { RouterTestingModule } from '@angular/router/testing'; import { MockStore, provideMockStore } from '@ngrx/store/testing'; -import { initialAppState, State } from '@store/.'; -import { selectQueryParams, selectRouteNestedParams, selectRouteParams } from '@store/router/selectors/router.selectors'; +import { State, initialAppState } from '@store/.'; +import { + selectQueryParams, + selectRouteNestedParams, + selectRouteParams, +} from '@store/router/selectors/router.selectors'; import { firstValueFrom, of, take } from 'rxjs'; -import { RouterTestingModule } from '@angular/router/testing'; import { RouterService } from './router.service'; describe('RouterService', () => { - let service: RouterService; - let store: MockStore; + let service: RouterService; + let store: MockStore; - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [RouterTestingModule], - providers: [RouterService, provideMockStore({ initialState: initialAppState })], - }); - service = TestBed.inject(RouterService); - store = TestBed.inject(MockStore); - store.resetSelectors(); - }); + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [RouterTestingModule], + providers: [RouterService, provideMockStore({ initialState: initialAppState })], + }); + service = TestBed.inject(RouterService); + store = TestBed.inject(MockStore); + store.resetSelectors(); + }); - it('should be created', () => { - expect(service).toBeTruthy(); - }); + it('should be created', () => { + expect(service).toBeTruthy(); + }); - describe('queryParams$', () => { - it('should return the query params', async () => { - const queryParams = { foo: 'bar', baz: 'qux' }; - store.overrideSelector(selectQueryParams, queryParams); - expect(await firstValueFrom(service.queryParams$.pipe(take(1)))).toEqual(queryParams); - }); - }); + describe('queryParams$', () => { + it('should return the query params', async () => { + const queryParams = { foo: 'bar', baz: 'qux' }; + store.overrideSelector(selectQueryParams, queryParams); + expect(await firstValueFrom(service.queryParams$.pipe(take(1)))).toEqual(queryParams); + }); + }); - describe('RouterService.prototype.getQueryParam$.name', () => { - it('should return the query param', async () => { - // overriding selectQueryParam(param) has no effect here, I think because it might use selectQueryParams internally so we override that instead - store.overrideSelector(selectQueryParams, { bar: 'foo' }); - store.refreshState(); - expect(await firstValueFrom(service.getQueryParam$('bar').pipe(take(1)))).toBe('foo'); - }); - }); + describe('RouterService.prototype.getQueryParam$.name', () => { + it('should return the query param', async () => { + // overriding selectQueryParam(param) has no effect here, I think because it might use selectQueryParams internally so we override that instead + store.overrideSelector(selectQueryParams, { bar: 'foo' }); + store.refreshState(); + expect(await firstValueFrom(service.getQueryParam$('bar').pipe(take(1)))).toBe('foo'); + }); + }); - describe('RouterService.prototype.getRouteParam$.name', () => { - it('should return an Observable of the given route param', async () => { - store.overrideSelector(selectRouteParams, { bar: 'baz' }); - store.refreshState(); - expect(await firstValueFrom(service.getRouteParam$('bar').pipe(take(1)))).toBe('baz'); - }); - }); + describe('RouterService.prototype.getRouteParam$.name', () => { + it('should return an Observable of the given route param', async () => { + store.overrideSelector(selectRouteParams, { bar: 'baz' }); + store.refreshState(); + expect(await firstValueFrom(service.getRouteParam$('bar').pipe(take(1)))).toBe('baz'); + }); + }); - describe('get routeNestedParams$', () => { - it('should return an Observable route Params', (done) => { - store.overrideSelector(selectRouteNestedParams, { foo: 'bar' }); - store.refreshState(); - service.routeNestedParams$.subscribe((value) => { - expect(value).toEqual({ foo: 'bar' }); - done(); - }); - }); - }); + describe('get routeNestedParams$', () => { + it('should return an Observable route Params', (done) => { + store.overrideSelector(selectRouteNestedParams, { foo: 'bar' }); + store.refreshState(); + service.routeNestedParams$.subscribe((value) => { + expect(value).toEqual({ foo: 'bar' }); + done(); + }); + }); + }); - describe('getRouteNestedParam', () => { - it('should return the correct value', (done) => { - jest.spyOn(service, 'routeNestedParams$', 'get').mockReturnValue(of({ foo: 'bar' })); - service.getRouteNestedParam$('foo').subscribe((value) => { - expect(value).toBe('bar'); - done(); - }); - }); - }); + describe('getRouteNestedParam', () => { + it('should return the correct value', (done) => { + jest.spyOn(service, 'routeNestedParams$', 'get').mockReturnValue(of({ foo: 'bar' })); + service.getRouteNestedParam$('foo').subscribe((value) => { + expect(value).toBe('bar'); + done(); + }); + }); + }); }); diff --git a/src/app/services/router/router.service.ts b/src/app/services/router/router.service.ts index 2a52adbe0b..c4cc5cd093 100644 --- a/src/app/services/router/router.service.ts +++ b/src/app/services/router/router.service.ts @@ -1,64 +1,71 @@ +import { Location } from '@angular/common'; import { Injectable } from '@angular/core'; import { ActivatedRoute, Params, Router } from '@angular/router'; -import { select, Store } from '@ngrx/store'; +import { Store, select } from '@ngrx/store'; import { State } from '@store/.'; import { - routeEditable, - routerState, - selectQueryParam, - selectQueryParams, - selectRouteData, - selectRouteDataProperty, - selectRouteNestedParams, - selectRouteParam, + routeEditable, + routerState, + selectQueryParam, + selectQueryParams, + selectRouteData, + selectRouteDataProperty, + selectRouteNestedParams, + selectRouteParam, } from '@store/router/selectors/router.selectors'; import { Observable, map } from 'rxjs'; -import { Location } from '@angular/common'; @Injectable({ - providedIn: 'root', + providedIn: 'root', }) export class RouterService { - constructor(private store: Store, private router: Router, private activatedRoute: ActivatedRoute, private location: Location) {} + constructor( + private store: Store, + private router: Router, + private activatedRoute: ActivatedRoute, + private location: Location + ) {} - get router$() { - return this.store.pipe(select(routerState)); - } + get router$() { + return this.store.pipe(select(routerState)); + } - get queryParams$(): Observable { - return this.store.pipe(select(selectQueryParams)); - } + get queryParams$(): Observable { + return this.store.pipe(select(selectQueryParams)); + } - getQueryParam$(param: string) { - return this.store.pipe(select(selectQueryParam(param))); - } + getQueryParam$(param: string) { + return this.store.pipe(select(selectQueryParam(param))); + } - getRouteParam$(param: string) { - return this.store.pipe(select(selectRouteParam(param))); - } + getRouteParam$(param: string) { + return this.store.pipe(select(selectRouteParam(param))); + } - get routeNestedParams$() { - return this.store.pipe(select(selectRouteNestedParams)); - } + get routeNestedParams$() { + return this.store.pipe(select(selectRouteNestedParams)); + } - getRouteNestedParam$(param: string): Observable { - return this.routeNestedParams$.pipe(map((route) => route[`${param}`])); - } + getRouteNestedParam$(param: string): Observable { + return this.routeNestedParams$.pipe(map((route) => route[`${param}`])); + } - get routeEditable$() { - return this.store.pipe(select(routeEditable)); - } + get routeEditable$() { + return this.store.pipe(select(routeEditable)); + } - get routeData$() { - return this.store.pipe(select(selectRouteData)); - } + get routeData$() { + return this.store.pipe(select(selectRouteData)); + } - getRouteDataProperty$(property: string) { - return this.store.pipe(select(selectRouteDataProperty(property))); - } + getRouteDataProperty$(property: string) { + return this.store.pipe(select(selectRouteDataProperty(property))); + } - async addQueryParams(queryParams: Params) { - const url = this.router.createUrlTree([], { relativeTo: this.activatedRoute, queryParams, queryParamsHandling: 'merge' }).toString(); - await this.router.navigateByUrl(url); - } + async addQueryParams(queryParams: Params) { + const url = this.router + .createUrlTree([], { relativeTo: this.activatedRoute, queryParams, queryParamsHandling: 'merge' }) + .toString(); + await this.router.navigateByUrl(url); + } } diff --git a/src/app/services/technical-record-http/technical-record-http.service.spec.ts b/src/app/services/technical-record-http/technical-record-http.service.spec.ts index bc3cc44e73..656ea3f387 100644 --- a/src/app/services/technical-record-http/technical-record-http.service.spec.ts +++ b/src/app/services/technical-record-http/technical-record-http.service.spec.ts @@ -14,186 +14,207 @@ import { TechnicalRecordHttpService } from './technical-record-http.service'; // TODO: need to include tests for search$, seachBy, getBySystemNumber, getRecordV3, AmendVrm, promoteTechRecord, generatePlate, generateLetter describe('TechnicalRecordService', () => { - let service: TechnicalRecordHttpService; - let httpClient: HttpTestingController; - let store: MockStore; - - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [HttpClientTestingModule, RouterTestingModule], - providers: [TechnicalRecordHttpService, provideMockStore({ initialState: initialAppState })], - }); - httpClient = TestBed.inject(HttpTestingController); - service = TestBed.inject(TechnicalRecordHttpService); - store = TestBed.inject(MockStore); - }); - - afterEach(() => { - // After every test, assert that there are no more pending requests. - httpClient.verify(); - }); - - it('should be created', () => { - expect(service).toBeTruthy(); - }); - - describe('API', () => { - describe('createVehicleRecord', () => { - it('should call post with the correct URL, body and response type', fakeAsync(() => { - const expectedVehicle = { - systemNumber: 'foo', - createdTimestamp: 'bar', - vin: 'testvin', - primaryVrm: 'vrm1', - techRecord_reasonForCreation: 'test', - } as unknown as TechRecordType<'put'>; - - service.createVehicleRecord$(expectedVehicle).subscribe(); - - const request = httpClient.expectOne(`${environment.VTM_API_URI}/v3/technical-records`); - - expect(request.request.method).toBe('POST'); - expect(request.request.body).toEqual(expectedVehicle); - - request.flush(expectedVehicle); - })); - - it('should return an array with the newly created vehicle record', () => { - const expectedVehicle = { - systemNumber: 'foo', - createdTimestamp: 'bar', - vin: 'testvin', - primaryVrm: 'vrm1', - techRecord_reasonForCreation: 'test', - } as unknown as TechRecordType<'put'>; - - service.createVehicleRecord$(expectedVehicle).pipe(first()).subscribe((response) => { - expect(response).toEqual(expectedVehicle); - }); - - const request = httpClient.expectOne(`${environment.VTM_API_URI}/v3/technical-records`); - request.flush(expectedVehicle); - }); - }); - - describe('updateTechRecords', () => { - it('should return a new tech record and updated status code', fakeAsync(() => { - const systemNumber = '123456'; - const createdTimestamp = '2022'; - const expectedVehicle = { - systemNumber: 'foo', - createdTimestamp: 'bar', - vin: 'testvin', - primaryVrm: 'vrm1', - techRecord_reasonForCreation: 'test', - secondaryVrms: undefined, - } as TechRecordType<'get'>; - service.updateTechRecords$(systemNumber, createdTimestamp, expectedVehicle as TechRecordType<'put'>).subscribe(); - - // Check for correct requests: should have made one request to the PUT URL - const req = httpClient.expectOne(`${environment.VTM_API_URI}/v3/technical-records/${systemNumber}/${createdTimestamp}`); - expect(req.request.method).toBe('PATCH'); - - // should format the vrms for the update payload - expect(req.request.body).toHaveProperty('primaryVrm'); - expect(req.request.body).toHaveProperty('secondaryVrms'); - })); - }); - - describe('archiveTechRecord', () => { - it('should return a new tech record with status archived', fakeAsync(() => { - service.archiveTechnicalRecord$('foo', 'bar', 'foobar').subscribe(); - - const req = httpClient.expectOne(`${environment.VTM_API_URI}/v3/technical-records/archive/foo/bar`); - expect(req.request.method).toBe('PATCH'); - expect(req.request.body).toHaveProperty('reasonForArchiving'); - })); - }); - - describe('generateLetter', () => { - it('should call v3/technical-records/letter', fakeAsync(() => { - const technicalRecord = mockVehicleTechnicalRecord('hgv') as TechRecordType<'get'>; - service.generateLetter$(technicalRecord, 'test', 123, {}).subscribe(); - - const req = httpClient.expectOne(`${environment.VTM_API_URI}/v3/technical-records/letter/HGV/${technicalRecord.createdTimestamp}`); - expect(req.request.method).toBe('POST'); - expect(req.request.body).toHaveProperty('vtmUsername'); - expect(req.request.body).toHaveProperty('letterType'); - expect(req.request.body).toHaveProperty('paragraphId'); - expect(req.request.body).toHaveProperty('recipientEmailAddress'); - })); - }); - - describe('generatePlate', () => { - it('should call v3/technical-records/plate', fakeAsync(() => { - const technicalRecord = mockVehicleTechnicalRecord('hgv') as TechRecordType<'get'>; - service.generatePlate$(technicalRecord, 'reason', {}).subscribe(); - - const req = httpClient.expectOne(`${environment.VTM_API_URI}/v3/technical-records/plate/HGV/${technicalRecord.createdTimestamp}`); - expect(req.request.method).toBe('POST'); - expect(req.request.body).toHaveProperty('vtmUsername'); - expect(req.request.body).toHaveProperty('reasonForCreation'); - expect(req.request.body).toHaveProperty('recipientEmailAddress'); - })); - }); - - describe('promoteTechnicalRecord', () => { - it('should call v3/technical-records/plate', fakeAsync(() => { - const technicalRecord = mockVehicleTechnicalRecord('hgv') as TechRecordType<'get'>; - service.promoteTechnicalRecord$(technicalRecord.systemNumber, technicalRecord.createdTimestamp, 'reason').subscribe(); - - const req = httpClient.expectOne(`${environment.VTM_API_URI}/v3/technical-records/promote/HGV/${technicalRecord.createdTimestamp}`); - expect(req.request.method).toBe('PATCH'); - expect(req.request.body).toHaveProperty('reasonForPromoting'); - })); - }); - - describe('amendVrm', () => { - it('should call v3/technical-records/updateVrm', fakeAsync(() => { - const technicalRecord = mockVehicleTechnicalRecord('hgv') as TechRecordType<'get'>; - service.amendVrm$('new vrm', false, technicalRecord.systemNumber, technicalRecord.createdTimestamp).subscribe(); - - const req = httpClient.expectOne(`${environment.VTM_API_URI}/v3/technical-records/updateVrm/HGV/${technicalRecord.createdTimestamp}`); - expect(req.request.method).toBe('PATCH'); - expect(req.request.body).toHaveProperty('newVrm'); - expect(req.request.body).toHaveProperty('isCherishedTransfer'); - })); - }); - - describe('getRecordV3', () => { - it('should call v3/technical-records/plate/HGV', fakeAsync(() => { - const technicalRecord = mockVehicleTechnicalRecord('hgv') as TechRecordType<'get'>; - service.getRecordV3$(technicalRecord.systemNumber, technicalRecord.createdTimestamp).subscribe(); - - const req = httpClient.expectOne(`${environment.VTM_API_URI}/v3/technical-records/HGV/${technicalRecord.createdTimestamp}`); - expect(req.request.method).toBe('GET'); - })); - }); - - describe('getBySystemNumber', () => { - it('should call service.search$', () => { - const technicalRecord = mockVehicleTechnicalRecord('hgv') as TechRecordType<'get'>; - const spy = jest.spyOn(service, 'search$').mockReturnValue(of()); - service.getBySystemNumber$(technicalRecord.systemNumber).subscribe(); - expect(spy).toHaveBeenCalled(); - }); - }); - - describe('searchBy', () => { - it('should call store.dispatch', fakeAsync(() => { - const spy = jest.spyOn(store, 'dispatch'); - service.searchBy(SEARCH_TYPES.ALL, 'term'); - expect(spy).toHaveBeenCalledWith(fetchSearchResult({ searchBy: SEARCH_TYPES.ALL, term: 'term' })); - })); - }); - - describe('search$', () => { - it('should call v3/technical-records/search', fakeAsync(() => { - service.search$(SEARCH_TYPES.ALL, 'term').subscribe(); - - const req = httpClient.expectOne(`${environment.VTM_API_URI}/v3/technical-records/search/term?searchCriteria=${SEARCH_TYPES.ALL}`); - expect(req.request.method).toBe('GET'); - })); - }); - }); + let service: TechnicalRecordHttpService; + let httpClient: HttpTestingController; + let store: MockStore; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [HttpClientTestingModule, RouterTestingModule], + providers: [TechnicalRecordHttpService, provideMockStore({ initialState: initialAppState })], + }); + httpClient = TestBed.inject(HttpTestingController); + service = TestBed.inject(TechnicalRecordHttpService); + store = TestBed.inject(MockStore); + }); + + afterEach(() => { + // After every test, assert that there are no more pending requests. + httpClient.verify(); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); + + describe('API', () => { + describe('createVehicleRecord', () => { + it('should call post with the correct URL, body and response type', fakeAsync(() => { + const expectedVehicle = { + systemNumber: 'foo', + createdTimestamp: 'bar', + vin: 'testvin', + primaryVrm: 'vrm1', + techRecord_reasonForCreation: 'test', + } as unknown as TechRecordType<'put'>; + + service.createVehicleRecord$(expectedVehicle).subscribe(); + + const request = httpClient.expectOne(`${environment.VTM_API_URI}/v3/technical-records`); + + expect(request.request.method).toBe('POST'); + expect(request.request.body).toEqual(expectedVehicle); + + request.flush(expectedVehicle); + })); + + it('should return an array with the newly created vehicle record', () => { + const expectedVehicle = { + systemNumber: 'foo', + createdTimestamp: 'bar', + vin: 'testvin', + primaryVrm: 'vrm1', + techRecord_reasonForCreation: 'test', + } as unknown as TechRecordType<'put'>; + + service + .createVehicleRecord$(expectedVehicle) + .pipe(first()) + .subscribe((response) => { + expect(response).toEqual(expectedVehicle); + }); + + const request = httpClient.expectOne(`${environment.VTM_API_URI}/v3/technical-records`); + request.flush(expectedVehicle); + }); + }); + + describe('updateTechRecords', () => { + it('should return a new tech record and updated status code', fakeAsync(() => { + const systemNumber = '123456'; + const createdTimestamp = '2022'; + const expectedVehicle = { + systemNumber: 'foo', + createdTimestamp: 'bar', + vin: 'testvin', + primaryVrm: 'vrm1', + techRecord_reasonForCreation: 'test', + secondaryVrms: undefined, + } as TechRecordType<'get'>; + service + .updateTechRecords$(systemNumber, createdTimestamp, expectedVehicle as TechRecordType<'put'>) + .subscribe(); + + // Check for correct requests: should have made one request to the PUT URL + const req = httpClient.expectOne( + `${environment.VTM_API_URI}/v3/technical-records/${systemNumber}/${createdTimestamp}` + ); + expect(req.request.method).toBe('PATCH'); + + // should format the vrms for the update payload + expect(req.request.body).toHaveProperty('primaryVrm'); + expect(req.request.body).toHaveProperty('secondaryVrms'); + })); + }); + + describe('archiveTechRecord', () => { + it('should return a new tech record with status archived', fakeAsync(() => { + service.archiveTechnicalRecord$('foo', 'bar', 'foobar').subscribe(); + + const req = httpClient.expectOne(`${environment.VTM_API_URI}/v3/technical-records/archive/foo/bar`); + expect(req.request.method).toBe('PATCH'); + expect(req.request.body).toHaveProperty('reasonForArchiving'); + })); + }); + + describe('generateLetter', () => { + it('should call v3/technical-records/letter', fakeAsync(() => { + const technicalRecord = mockVehicleTechnicalRecord('hgv') as TechRecordType<'get'>; + service.generateLetter$(technicalRecord, 'test', 123, {}).subscribe(); + + const req = httpClient.expectOne( + `${environment.VTM_API_URI}/v3/technical-records/letter/HGV/${technicalRecord.createdTimestamp}` + ); + expect(req.request.method).toBe('POST'); + expect(req.request.body).toHaveProperty('vtmUsername'); + expect(req.request.body).toHaveProperty('letterType'); + expect(req.request.body).toHaveProperty('paragraphId'); + expect(req.request.body).toHaveProperty('recipientEmailAddress'); + })); + }); + + describe('generatePlate', () => { + it('should call v3/technical-records/plate', fakeAsync(() => { + const technicalRecord = mockVehicleTechnicalRecord('hgv') as TechRecordType<'get'>; + service.generatePlate$(technicalRecord, 'reason', {}).subscribe(); + + const req = httpClient.expectOne( + `${environment.VTM_API_URI}/v3/technical-records/plate/HGV/${technicalRecord.createdTimestamp}` + ); + expect(req.request.method).toBe('POST'); + expect(req.request.body).toHaveProperty('vtmUsername'); + expect(req.request.body).toHaveProperty('reasonForCreation'); + expect(req.request.body).toHaveProperty('recipientEmailAddress'); + })); + }); + + describe('promoteTechnicalRecord', () => { + it('should call v3/technical-records/plate', fakeAsync(() => { + const technicalRecord = mockVehicleTechnicalRecord('hgv') as TechRecordType<'get'>; + service + .promoteTechnicalRecord$(technicalRecord.systemNumber, technicalRecord.createdTimestamp, 'reason') + .subscribe(); + + const req = httpClient.expectOne( + `${environment.VTM_API_URI}/v3/technical-records/promote/HGV/${technicalRecord.createdTimestamp}` + ); + expect(req.request.method).toBe('PATCH'); + expect(req.request.body).toHaveProperty('reasonForPromoting'); + })); + }); + + describe('amendVrm', () => { + it('should call v3/technical-records/updateVrm', fakeAsync(() => { + const technicalRecord = mockVehicleTechnicalRecord('hgv') as TechRecordType<'get'>; + service.amendVrm$('new vrm', false, technicalRecord.systemNumber, technicalRecord.createdTimestamp).subscribe(); + + const req = httpClient.expectOne( + `${environment.VTM_API_URI}/v3/technical-records/updateVrm/HGV/${technicalRecord.createdTimestamp}` + ); + expect(req.request.method).toBe('PATCH'); + expect(req.request.body).toHaveProperty('newVrm'); + expect(req.request.body).toHaveProperty('isCherishedTransfer'); + })); + }); + + describe('getRecordV3', () => { + it('should call v3/technical-records/plate/HGV', fakeAsync(() => { + const technicalRecord = mockVehicleTechnicalRecord('hgv') as TechRecordType<'get'>; + service.getRecordV3$(technicalRecord.systemNumber, technicalRecord.createdTimestamp).subscribe(); + + const req = httpClient.expectOne( + `${environment.VTM_API_URI}/v3/technical-records/HGV/${technicalRecord.createdTimestamp}` + ); + expect(req.request.method).toBe('GET'); + })); + }); + + describe('getBySystemNumber', () => { + it('should call service.search$', () => { + const technicalRecord = mockVehicleTechnicalRecord('hgv') as TechRecordType<'get'>; + const spy = jest.spyOn(service, 'search$').mockReturnValue(of()); + service.getBySystemNumber$(technicalRecord.systemNumber).subscribe(); + expect(spy).toHaveBeenCalled(); + }); + }); + + describe('searchBy', () => { + it('should call store.dispatch', fakeAsync(() => { + const spy = jest.spyOn(store, 'dispatch'); + service.searchBy(SEARCH_TYPES.ALL, 'term'); + expect(spy).toHaveBeenCalledWith(fetchSearchResult({ searchBy: SEARCH_TYPES.ALL, term: 'term' })); + })); + }); + + describe('search$', () => { + it('should call v3/technical-records/search', fakeAsync(() => { + service.search$(SEARCH_TYPES.ALL, 'term').subscribe(); + + const req = httpClient.expectOne( + `${environment.VTM_API_URI}/v3/technical-records/search/term?searchCriteria=${SEARCH_TYPES.ALL}` + ); + expect(req.request.method).toBe('GET'); + })); + }); + }); }); diff --git a/src/app/services/technical-record-http/technical-record-http.service.ts b/src/app/services/technical-record-http/technical-record-http.service.ts index 5e295a3426..f764d1d2a9 100644 --- a/src/app/services/technical-record-http/technical-record-http.service.ts +++ b/src/app/services/technical-record-http/technical-record-http.service.ts @@ -12,139 +12,158 @@ import { environment } from '../../../environments/environment'; @Injectable({ providedIn: 'root' }) export class TechnicalRecordHttpService { - constructor(private http: HttpClient, private store: Store) { } - - search$(type: SEARCH_TYPES, term: string): Observable { - const queryStr = `${term}?searchCriteria=${type}`; - const url = `${environment.VTM_API_URI}/v3/technical-records/search/${queryStr}`; - - return this.http.get(url, { responseType: 'json' }); - } - - searchBy(type: SEARCH_TYPES | undefined, term: string): void { - this.store.dispatch(fetchSearchResult({ searchBy: type, term })); - } - - getBySystemNumber$(systemNumber: string): Observable { - return this.search$(SEARCH_TYPES.SYSTEM_NUMBER, systemNumber); - } - - getRecordV3$(systemNumber: string, createdTimestamp: string): Observable> { - const url = `${environment.VTM_API_URI}/v3/technical-records/${systemNumber}/${createdTimestamp}`; - - return this.http.get>(url, { responseType: 'json' }); - } - - createVehicleRecord$(newVehicleRecord: V3TechRecordModel): Observable> { - const recordCopy: TechRecordType<'put'> = cloneDeep(newVehicleRecord) as TechRecordType<'put'>; - - const body = { - ...recordCopy, - }; - - return this.http.post>(`${environment.VTM_API_URI}/v3/technical-records`, body); - } - - updateTechRecords$(systemNumber: string, createdTimestamp: string, techRecord: TechRecordType<'put'>): Observable> { - const url = `${environment.VTM_API_URI}/v3/technical-records/${systemNumber}/${createdTimestamp}`; - - return this.http.patch>(url, techRecord, { responseType: 'json' }); - } - - amendVrm$( - newVrm: string, - cherishedTransfer: boolean, - systemNumber: string, - createdTimestamp: string, - thirdMark?: string, - ): Observable> { - const url = `${environment.VTM_API_URI}/v3/technical-records/updateVrm/${systemNumber}/${createdTimestamp}`; - const body = { - newVrm, - isCherishedTransfer: cherishedTransfer, - thirdMark: thirdMark ?? undefined, - }; - return this.http.patch>(url, body, { responseType: 'json' }); - } - - amendVin$( - newVin: string, - systemNumber: string, - createdTimestamp: string, - ): Observable> { - const url = `${environment.VTM_API_URI}/v3/technical-records/updateVin/${systemNumber}/${createdTimestamp}`; - const body = { - newVin, - }; - return this.http.patch>(url, body, { responseType: 'json' }); - } - - archiveTechnicalRecord$(systemNumber: string, createdTimestamp: string, reasonForArchiving: string): Observable> { - const url = `${environment.VTM_API_URI}/v3/technical-records/archive/${systemNumber}/${createdTimestamp}`; - - const body = { reasonForArchiving }; - - return this.http.patch>(url, body, { responseType: 'json' }); - } - - promoteTechnicalRecord$(systemNumber: string, createdTimestamp: string, reasonForPromoting: string): Observable> { - const url = `${environment.VTM_API_URI}/v3/technical-records/promote/${systemNumber}/${createdTimestamp}`; - - const body = { reasonForPromoting }; - - return this.http.patch>(url, body, { responseType: 'json' }); - } - - generatePlate$(vehicleRecord: TechRecordType<'get'>, reason: string, user: { name?: string; email?: string }): Observable { - const url = `${environment.VTM_API_URI}/v3/technical-records/plate/${vehicleRecord.systemNumber}/${vehicleRecord.createdTimestamp}`; - - const body = { - reasonForCreation: reason, - vtmUsername: user.name, - recipientEmailAddress: (vehicleRecord)?.techRecord_applicantDetails_emailAddress ?? user.email, - }; - - return this.http.post(url, body, { responseType: 'json' }); - } - - generateLetter$( - vehicleRecord: TechRecordType<'get'>, - letterType: string, - paragraphId: number, - user: { name?: string; email?: string }, - ): Observable { - const url = `${environment.VTM_API_URI}/v3/technical-records/letter/${vehicleRecord.systemNumber}/${vehicleRecord.createdTimestamp}`; - - const body = { - vtmUsername: user.name, - letterType, - paragraphId, - recipientEmailAddress: vehicleRecord.techRecord_applicantDetails_emailAddress - ? (vehicleRecord).techRecord_applicantDetails_emailAddress - : user.email, - }; - - return this.http.post(url, body, { responseType: 'text' }); - } - - unarchiveTechnicalRecord$( - systemNumber: string, - createdTimestamp: string, - reasonForUnarchiving: string, - status: string, - ): Observable> { - const url = `${environment.VTM_API_URI}/v3/technical-records/unarchive/${systemNumber}/${createdTimestamp}`; - - const body = { reasonForUnarchiving, status }; - - return this.http.post>(url, body, { responseType: 'json' }); - } - - generateADRCertificate$(systemNumber: string, createdTimestamp: string, certificateType: string): Observable<{ message: string, id: string }> { - const url = `${environment.VTM_API_URI}/v3/technical-records/adrCertificate/${systemNumber}/${createdTimestamp}`; - - const body = { certificateType }; - - return this.http.post<{ message: string, id: string }>(url, body, { responseType: 'json' }); - } + constructor( + private http: HttpClient, + private store: Store + ) {} + + search$(type: SEARCH_TYPES, term: string): Observable { + const queryStr = `${term}?searchCriteria=${type}`; + const url = `${environment.VTM_API_URI}/v3/technical-records/search/${queryStr}`; + + return this.http.get(url, { responseType: 'json' }); + } + + searchBy(type: SEARCH_TYPES | undefined, term: string): void { + this.store.dispatch(fetchSearchResult({ searchBy: type, term })); + } + + getBySystemNumber$(systemNumber: string): Observable { + return this.search$(SEARCH_TYPES.SYSTEM_NUMBER, systemNumber); + } + + getRecordV3$(systemNumber: string, createdTimestamp: string): Observable> { + const url = `${environment.VTM_API_URI}/v3/technical-records/${systemNumber}/${createdTimestamp}`; + + return this.http.get>(url, { responseType: 'json' }); + } + + createVehicleRecord$(newVehicleRecord: V3TechRecordModel): Observable> { + const recordCopy: TechRecordType<'put'> = cloneDeep(newVehicleRecord) as TechRecordType<'put'>; + + const body = { + ...recordCopy, + }; + + return this.http.post>(`${environment.VTM_API_URI}/v3/technical-records`, body); + } + + updateTechRecords$( + systemNumber: string, + createdTimestamp: string, + techRecord: TechRecordType<'put'> + ): Observable> { + const url = `${environment.VTM_API_URI}/v3/technical-records/${systemNumber}/${createdTimestamp}`; + + return this.http.patch>(url, techRecord, { responseType: 'json' }); + } + + amendVrm$( + newVrm: string, + cherishedTransfer: boolean, + systemNumber: string, + createdTimestamp: string, + thirdMark?: string + ): Observable> { + const url = `${environment.VTM_API_URI}/v3/technical-records/updateVrm/${systemNumber}/${createdTimestamp}`; + const body = { + newVrm, + isCherishedTransfer: cherishedTransfer, + thirdMark: thirdMark ?? undefined, + }; + return this.http.patch>(url, body, { responseType: 'json' }); + } + + amendVin$(newVin: string, systemNumber: string, createdTimestamp: string): Observable> { + const url = `${environment.VTM_API_URI}/v3/technical-records/updateVin/${systemNumber}/${createdTimestamp}`; + const body = { + newVin, + }; + return this.http.patch>(url, body, { responseType: 'json' }); + } + + archiveTechnicalRecord$( + systemNumber: string, + createdTimestamp: string, + reasonForArchiving: string + ): Observable> { + const url = `${environment.VTM_API_URI}/v3/technical-records/archive/${systemNumber}/${createdTimestamp}`; + + const body = { reasonForArchiving }; + + return this.http.patch>(url, body, { responseType: 'json' }); + } + + promoteTechnicalRecord$( + systemNumber: string, + createdTimestamp: string, + reasonForPromoting: string + ): Observable> { + const url = `${environment.VTM_API_URI}/v3/technical-records/promote/${systemNumber}/${createdTimestamp}`; + + const body = { reasonForPromoting }; + + return this.http.patch>(url, body, { responseType: 'json' }); + } + + generatePlate$( + vehicleRecord: TechRecordType<'get'>, + reason: string, + user: { name?: string; email?: string } + ): Observable { + const url = `${environment.VTM_API_URI}/v3/technical-records/plate/${vehicleRecord.systemNumber}/${vehicleRecord.createdTimestamp}`; + + const body = { + reasonForCreation: reason, + vtmUsername: user.name, + recipientEmailAddress: vehicleRecord?.techRecord_applicantDetails_emailAddress ?? user.email, + }; + + return this.http.post(url, body, { responseType: 'json' }); + } + + generateLetter$( + vehicleRecord: TechRecordType<'get'>, + letterType: string, + paragraphId: number, + user: { name?: string; email?: string } + ): Observable { + const url = `${environment.VTM_API_URI}/v3/technical-records/letter/${vehicleRecord.systemNumber}/${vehicleRecord.createdTimestamp}`; + + const body = { + vtmUsername: user.name, + letterType, + paragraphId, + recipientEmailAddress: vehicleRecord.techRecord_applicantDetails_emailAddress + ? vehicleRecord.techRecord_applicantDetails_emailAddress + : user.email, + }; + + return this.http.post(url, body, { responseType: 'text' }); + } + + unarchiveTechnicalRecord$( + systemNumber: string, + createdTimestamp: string, + reasonForUnarchiving: string, + status: string + ): Observable> { + const url = `${environment.VTM_API_URI}/v3/technical-records/unarchive/${systemNumber}/${createdTimestamp}`; + + const body = { reasonForUnarchiving, status }; + + return this.http.post>(url, body, { responseType: 'json' }); + } + + generateADRCertificate$( + systemNumber: string, + createdTimestamp: string, + certificateType: string + ): Observable<{ message: string; id: string }> { + const url = `${environment.VTM_API_URI}/v3/technical-records/adrCertificate/${systemNumber}/${createdTimestamp}`; + + const body = { certificateType }; + + return this.http.post<{ message: string; id: string }>(url, body, { responseType: 'json' }); + } } diff --git a/src/app/services/technical-record/technical-record.service.spec.ts b/src/app/services/technical-record/technical-record.service.spec.ts index 5729050d04..6ee91d17a8 100644 --- a/src/app/services/technical-record/technical-record.service.spec.ts +++ b/src/app/services/technical-record/technical-record.service.spec.ts @@ -7,9 +7,9 @@ import { EUVehicleCategory } from '@dvsa/cvs-type-definitions/types/v3/tech-reco import { TechRecordSearchSchema } from '@dvsa/cvs-type-definitions/types/v3/tech-record/get/search'; import { TechRecordType } from '@dvsa/cvs-type-definitions/types/v3/tech-record/tech-record-verb'; import { - TechRecordGETHGV, - TechRecordGETPSV, - TechRecordGETTRL, + TechRecordGETHGV, + TechRecordGETPSV, + TechRecordGETTRL, } from '@dvsa/cvs-type-definitions/types/v3/tech-record/tech-record-verb-vehicle-type'; import { mockVehicleTechnicalRecord } from '@mocks/mock-vehicle-technical-record.mock'; import { ReferenceDataResourceType, ReferenceDataTyreLoadIndex } from '@models/reference-data.model'; @@ -19,760 +19,822 @@ import { MockStore, provideMockStore } from '@ngrx/store/testing'; import { TechnicalRecordHttpService } from '@services/technical-record-http/technical-record-http.service'; import { State, initialAppState } from '@store/index'; import { updateEditingTechRecord } from '@store/technical-records'; -import { - EmptyError, - firstValueFrom, from, of, -} from 'rxjs'; +import { EmptyError, firstValueFrom, from, of } from 'rxjs'; import { environment } from '../../../environments/environment'; import { TechnicalRecordService } from './technical-record.service'; import FitmentCodeEnum = AxleTyreProperties.FitmentCodeEnum; describe('TechnicalRecordService', () => { - let service: TechnicalRecordService; - let httpClient: HttpTestingController; - let store: MockStore; - let techRecordHttpService: TechnicalRecordHttpService; - - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [HttpClientTestingModule, RouterTestingModule], - providers: [ - TechnicalRecordService, - provideMockStore({ initialState: initialAppState }), - ], - }); - httpClient = TestBed.inject(HttpTestingController); - service = TestBed.inject(TechnicalRecordService); - store = TestBed.inject(MockStore); - techRecordHttpService = TestBed.inject(TechnicalRecordHttpService); - }); - - afterEach(() => { - // After every test, assert that there are no more pending requests. - httpClient.verify(); - }); - - it('should be created', () => { - expect(service).toBeTruthy(); - }); - - describe('isUnique', () => { - it('should validate the search term to be unique when no matching results are returned (Test Case 1)', () => { - const searchParams = { searchTerm: '12345', type: 'vin' }; - const mockData = { systemNumber: 'foo', createdTimestamp: 'bar', vin: 'testVin' } as V3TechRecordModel; - - service.isUnique(searchParams.searchTerm, SEARCH_TYPES.VIN).subscribe((response) => { - expect(response).toBe(true); - }); - - // Check for correct requests: should have made one request to search from expected URL - const req = httpClient.expectOne(`${environment.VTM_API_URI}/v3/technical-records/search/${searchParams.searchTerm}?searchCriteria=vin`); - expect(req.request.method).toBe('GET'); - - // Provide each request with a mock response - req.flush(mockData); - }); - - it('should validate the search term to be unique when no matching results are returned (Test Case 2)', () => { - const searchParams = { searchTerm: 'A_VIN', type: 'vin' }; - const mockData = { systemNumber: 'foo', createdTimestamp: 'bar', vin: 'testVin' } as V3TechRecordModel; - - service.isUnique(searchParams.searchTerm, SEARCH_TYPES.VIN).subscribe((response) => { - expect(response).toBe(true); - }); - - // Check for correct requests: should have made one request to search from expected URL - const req = httpClient.expectOne(`${environment.VTM_API_URI}/v3/technical-records/search/${searchParams.searchTerm}?searchCriteria=vin`); - expect(req.request.method).toBe('GET'); - - // Provide each request with a mock response - req.flush(mockData); - }); - - it('should validate the search term to be non unique when matching results are returned and are current or provisional', () => { - const searchParams = { searchTerm: 'A_VIN', type: 'vin' }; - const mockData = { systemNumber: 'foo', createdTimestamp: 'bar', vin: 'testVin' } as V3TechRecordModel; - - service.isUnique(searchParams.searchTerm, SEARCH_TYPES.VIN).subscribe((response) => { - expect(response).toBe(false); - }); - - // Check for correct requests: should have made one request to search from expected URL - const req = httpClient.expectOne(`${environment.VTM_API_URI}/v3/technical-records/search/${searchParams.searchTerm}?searchCriteria=vin`); - expect(req.request.method).toBe('GET'); - - // Provide each request with a mock response - req.flush(mockData); - }); - - it('should validate the search term to be non unique when vrm is used as a primary', () => { - const searchParams = { searchTerm: 'KP01 ABC', type: 'vrm' }; - const mockData = { systemNumber: 'foo', createdTimestamp: 'bar', vin: 'testVin' } as V3TechRecordModel; - - service.isUnique(searchParams.searchTerm, SEARCH_TYPES.VRM).subscribe((response) => { - expect(response).toBe(false); - }); - - // Check for correct requests: should have made one request to search from expected URL - const req = httpClient.expectOne(`${environment.VTM_API_URI}/v3/technical-records/search/${searchParams.searchTerm}?searchCriteria=primaryVrm`); - expect(req.request.method).toBe('GET'); - - // Provide each request with a mock response - req.flush(mockData); - }); - - it('should validate the search term to be unique when vrm is not used as a primary', () => { - const searchParams = { searchTerm: '12345', type: 'vrm' }; - const mockData = { systemNumber: 'foo', createdTimestamp: 'bar', vin: 'testVin' } as V3TechRecordModel; - - service.isUnique(searchParams.searchTerm, SEARCH_TYPES.VRM).subscribe((response) => { - expect(response).toBe(true); - }); - - // Check for correct requests: should have made one request to search from expected URL - const req = httpClient.expectOne(`${environment.VTM_API_URI}/v3/technical-records/search/${searchParams.searchTerm}?searchCriteria=primaryVrm`); - expect(req.request.method).toBe('GET'); - - // Provide each request with a mock response - req.flush(mockData); - }); - }); - - describe('getVehicleMakeAndModel', () => { - it('should return an empty string if there is no make and model', () => { - const record = { systemNumber: 'foo', createdTimestamp: 'bar', vin: 'testVin' } as unknown as TechRecordType<'put'>; - expect(service.getMakeAndModel(record)).toBe(''); - }); - it('for a PSV returns the chassis make and model', () => { - const record: V3TechRecordModel = { - systemNumber: 'foo', - createdTimestamp: 'bar', - vin: 'testVin', - techRecord_vehicleType: VehicleTypes.PSV, - techRecord_chassisMake: 'test chassis make', - techRecord_chassisModel: 'chassis model', - } as unknown as V3TechRecordModel; - expect(service.getMakeAndModel(record)).toBe('test chassis make - chassis model'); - }); - it('for a any other type returns make and model', () => { - const record: V3TechRecordModel = { - systemNumber: 'foo', - createdTimestamp: 'bar', - vin: 'testVin', - techRecord_vehicleType: VehicleTypes.HGV, - techRecord_make: 'make', - techRecord_model: 'model', - } as unknown as V3TechRecordModel; - expect(service.getMakeAndModel(record)).toBe('make - model'); - }); - }); - - describe('business logic methods', () => { - describe('updateEditingTechRecord', () => { - it(`should patch the missing information for the technical + let service: TechnicalRecordService; + let httpClient: HttpTestingController; + let store: MockStore; + let techRecordHttpService: TechnicalRecordHttpService; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [HttpClientTestingModule, RouterTestingModule], + providers: [TechnicalRecordService, provideMockStore({ initialState: initialAppState })], + }); + httpClient = TestBed.inject(HttpTestingController); + service = TestBed.inject(TechnicalRecordService); + store = TestBed.inject(MockStore); + techRecordHttpService = TestBed.inject(TechnicalRecordHttpService); + }); + + afterEach(() => { + // After every test, assert that there are no more pending requests. + httpClient.verify(); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); + + describe('isUnique', () => { + it('should validate the search term to be unique when no matching results are returned (Test Case 1)', () => { + const searchParams = { searchTerm: '12345', type: 'vin' }; + const mockData = { systemNumber: 'foo', createdTimestamp: 'bar', vin: 'testVin' } as V3TechRecordModel; + + service.isUnique(searchParams.searchTerm, SEARCH_TYPES.VIN).subscribe((response) => { + expect(response).toBe(true); + }); + + // Check for correct requests: should have made one request to search from expected URL + const req = httpClient.expectOne( + `${environment.VTM_API_URI}/v3/technical-records/search/${searchParams.searchTerm}?searchCriteria=vin` + ); + expect(req.request.method).toBe('GET'); + + // Provide each request with a mock response + req.flush(mockData); + }); + + it('should validate the search term to be unique when no matching results are returned (Test Case 2)', () => { + const searchParams = { searchTerm: 'A_VIN', type: 'vin' }; + const mockData = { systemNumber: 'foo', createdTimestamp: 'bar', vin: 'testVin' } as V3TechRecordModel; + + service.isUnique(searchParams.searchTerm, SEARCH_TYPES.VIN).subscribe((response) => { + expect(response).toBe(true); + }); + + // Check for correct requests: should have made one request to search from expected URL + const req = httpClient.expectOne( + `${environment.VTM_API_URI}/v3/technical-records/search/${searchParams.searchTerm}?searchCriteria=vin` + ); + expect(req.request.method).toBe('GET'); + + // Provide each request with a mock response + req.flush(mockData); + }); + + it('should validate the search term to be non unique when matching results are returned and are current or provisional', () => { + const searchParams = { searchTerm: 'A_VIN', type: 'vin' }; + const mockData = { systemNumber: 'foo', createdTimestamp: 'bar', vin: 'testVin' } as V3TechRecordModel; + + service.isUnique(searchParams.searchTerm, SEARCH_TYPES.VIN).subscribe((response) => { + expect(response).toBe(false); + }); + + // Check for correct requests: should have made one request to search from expected URL + const req = httpClient.expectOne( + `${environment.VTM_API_URI}/v3/technical-records/search/${searchParams.searchTerm}?searchCriteria=vin` + ); + expect(req.request.method).toBe('GET'); + + // Provide each request with a mock response + req.flush(mockData); + }); + + it('should validate the search term to be non unique when vrm is used as a primary', () => { + const searchParams = { searchTerm: 'KP01 ABC', type: 'vrm' }; + const mockData = { systemNumber: 'foo', createdTimestamp: 'bar', vin: 'testVin' } as V3TechRecordModel; + + service.isUnique(searchParams.searchTerm, SEARCH_TYPES.VRM).subscribe((response) => { + expect(response).toBe(false); + }); + + // Check for correct requests: should have made one request to search from expected URL + const req = httpClient.expectOne( + `${environment.VTM_API_URI}/v3/technical-records/search/${searchParams.searchTerm}?searchCriteria=primaryVrm` + ); + expect(req.request.method).toBe('GET'); + + // Provide each request with a mock response + req.flush(mockData); + }); + + it('should validate the search term to be unique when vrm is not used as a primary', () => { + const searchParams = { searchTerm: '12345', type: 'vrm' }; + const mockData = { systemNumber: 'foo', createdTimestamp: 'bar', vin: 'testVin' } as V3TechRecordModel; + + service.isUnique(searchParams.searchTerm, SEARCH_TYPES.VRM).subscribe((response) => { + expect(response).toBe(true); + }); + + // Check for correct requests: should have made one request to search from expected URL + const req = httpClient.expectOne( + `${environment.VTM_API_URI}/v3/technical-records/search/${searchParams.searchTerm}?searchCriteria=primaryVrm` + ); + expect(req.request.method).toBe('GET'); + + // Provide each request with a mock response + req.flush(mockData); + }); + }); + + describe('getVehicleMakeAndModel', () => { + it('should return an empty string if there is no make and model', () => { + const record = { + systemNumber: 'foo', + createdTimestamp: 'bar', + vin: 'testVin', + } as unknown as TechRecordType<'put'>; + expect(service.getMakeAndModel(record)).toBe(''); + }); + it('for a PSV returns the chassis make and model', () => { + const record: V3TechRecordModel = { + systemNumber: 'foo', + createdTimestamp: 'bar', + vin: 'testVin', + techRecord_vehicleType: VehicleTypes.PSV, + techRecord_chassisMake: 'test chassis make', + techRecord_chassisModel: 'chassis model', + } as unknown as V3TechRecordModel; + expect(service.getMakeAndModel(record)).toBe('test chassis make - chassis model'); + }); + it('for a any other type returns make and model', () => { + const record: V3TechRecordModel = { + systemNumber: 'foo', + createdTimestamp: 'bar', + vin: 'testVin', + techRecord_vehicleType: VehicleTypes.HGV, + techRecord_make: 'make', + techRecord_model: 'model', + } as unknown as V3TechRecordModel; + expect(service.getMakeAndModel(record)).toBe('make - model'); + }); + }); + + describe('business logic methods', () => { + describe('updateEditingTechRecord', () => { + it(`should patch the missing information for the technical record and dispatch the action to update the editing vehicle record with the full vehicle record`, () => { - const dispatchSpy = jest.spyOn(store, 'dispatch'); - const mockVehicleRecord = { systemNumber: 'foo', createdTimestamp: 'bar', vin: 'testVin' } as unknown as TechRecordType<'put'>; - - service.updateEditingTechRecord(mockVehicleRecord); - expect(dispatchSpy).toHaveBeenCalledTimes(1); - expect(dispatchSpy).toHaveBeenCalledWith(updateEditingTechRecord({ vehicleTechRecord: mockVehicleRecord })); - }); - - it(`should patch from the selected record if the editing is + const dispatchSpy = jest.spyOn(store, 'dispatch'); + const mockVehicleRecord = { + systemNumber: 'foo', + createdTimestamp: 'bar', + vin: 'testVin', + } as unknown as TechRecordType<'put'>; + + service.updateEditingTechRecord(mockVehicleRecord); + expect(dispatchSpy).toHaveBeenCalledTimes(1); + expect(dispatchSpy).toHaveBeenCalledWith(updateEditingTechRecord({ vehicleTechRecord: mockVehicleRecord })); + }); + + it(`should patch from the selected record if the editing is not defined and dispatch the action to update the editing vehicle record with the full vehicle record`, () => { - const dispatchSpy = jest.spyOn(store, 'dispatch'); - const mockVehicleRecord = { systemNumber: 'foo', createdTimestamp: 'bar', vin: 'testVin' } as unknown as TechRecordType<'put'>; - - service.updateEditingTechRecord(mockVehicleRecord); - expect(dispatchSpy).toHaveBeenCalledTimes(1); - expect(dispatchSpy).toHaveBeenCalledWith(updateEditingTechRecord({ vehicleTechRecord: mockVehicleRecord })); - }); - - it('should update the number of axles based on the axles array', () => { - const dispatchSpy = jest.spyOn(store, 'dispatch'); - const mockVehicleRecord = mockVehicleTechnicalRecord('trl'); - mockVehicleRecord.techRecord_noOfAxles = 0; - mockVehicleRecord.techRecord_axles = [{}, {}]; - - service.updateEditingTechRecord(mockVehicleRecord as TechRecordType<'put'>); - - expect(dispatchSpy).toHaveBeenCalledWith( - updateEditingTechRecord({ - vehicleTechRecord: { ...mockVehicleRecord, techRecord_axles: [{}, {}], techRecord_noOfAxles: 2 } as TechRecordType<'put'>, - }), - ); - }); - - it('override the editable tech record and dispatch the action to update the editing vehicle record with the full vehicle record', () => { - const dispatchSpy = jest.spyOn(store, 'dispatch'); - const mockVehicleRecord = { systemNumber: 'foo', createdTimestamp: 'bar', vin: 'testVin' } as unknown as TechRecordType<'put'>; - - const mockEditableVehicleRecord = { systemNumber: 'foo', createdTimestamp: 'bar', vin: 'a random vin' } as unknown as TechRecordType<'put'>; - - service.updateEditingTechRecord(mockVehicleRecord); - expect(dispatchSpy).toHaveBeenCalledTimes(1); - expect(dispatchSpy).not.toHaveBeenCalledWith(updateEditingTechRecord({ vehicleTechRecord: mockEditableVehicleRecord })); - expect(dispatchSpy).toHaveBeenCalledWith(updateEditingTechRecord({ vehicleTechRecord: mockVehicleRecord })); - }); - - it('should throw an error if there is more than one tech record', () => { - const dispatchSpy = jest.spyOn(store, 'dispatch'); - const mockVehicleRecord = { systemNumber: 'foo', createdTimestamp: 'bar', vin: 'testVin' } as unknown as TechRecordType<'put'>; - service.updateEditingTechRecord(mockVehicleRecord); - expect(dispatchSpy).toHaveBeenCalledTimes(1); - expect(dispatchSpy).toHaveBeenCalledWith(updateEditingTechRecord({ vehicleTechRecord: mockVehicleRecord })); - }); - }); - }); - - describe('getMakeAndModel', () => { - it('should should return the make and model', () => { - expect(service.getMakeAndModel({ techRecord_make: 'Test', techRecord_model: 'Car' } as V3TechRecordModel)).toBe('Test - Car'); - }); - - it('should return an empty string when the current record has no values for make and model', () => { - expect(service.getMakeAndModel({ techRecord_make: undefined, techRecord_model: undefined } as V3TechRecordModel)).toBe(''); - }); - }); - - describe('haveAxlesChanged', () => { - it('should return true if a property of the hgv gross axle has changed', () => { - const vehicleType = VehicleTypes.HGV; - const changes = { techRecord_grossDesignWeight: 1, techRecord_grossEecWeight: 2, techRecord_grossGbWeight: 3 } as Partial; - const spy = jest.spyOn(service, 'hasHgvGrossAxleChanged'); - const result = service.haveAxlesChanged(vehicleType, changes); - expect(spy).toHaveBeenCalledWith(changes); - expect(result).toBe(true); - }); - - it('should return false if no properties of the hgv gross axle have changed', () => { - const vehicleType = VehicleTypes.HGV; - const changes = {} as Partial; - const spy = jest.spyOn(service, 'hasHgvGrossAxleChanged'); - const result = service.haveAxlesChanged(vehicleType, changes); - expect(spy).toHaveBeenCalledWith(changes); - expect(result).toBe(false); - }); - - it('should return true if a property of the psv gross axle has changed', () => { - const vehicleType = VehicleTypes.PSV; - const changes = { - techRecord_grossKerbWeight: 1, techRecord_grossLadenWeight: 1, techRecord_grossDesignWeight: 1, techRecord_grossGbWeight: 1, - } as Partial; - - const spy = jest.spyOn(service, 'hasPsvGrossAxleChanged'); - const result = service.haveAxlesChanged(vehicleType, changes); - expect(spy).toHaveBeenCalledWith(changes); - expect(result).toBe(true); - }); - - it('should return false if no properties of the psv gross axle have changed', () => { - const vehicleType = VehicleTypes.PSV; - const changes = {} as Partial; - const spy = jest.spyOn(service, 'hasPsvGrossAxleChanged'); - const result = service.haveAxlesChanged(vehicleType, changes); - expect(spy).toHaveBeenCalledWith(changes); - expect(result).toBe(false); - }); - - it('should return true if a property of the trl gross axle has changed', () => { - const vehicleType = VehicleTypes.TRL; - const changes = { techRecord_grossDesignWeight: 1, techRecord_grossEecWeight: 2, techRecord_grossGbWeight: 3 } as Partial; - const spy = jest.spyOn(service, 'hasTrlGrossAxleChanged'); - const result = service.haveAxlesChanged(vehicleType, changes); - expect(spy).toHaveBeenCalledWith(changes); - expect(result).toBe(true); - }); - - it('should return false if no properties of the trl gross axle have changed', () => { - const vehicleType = VehicleTypes.TRL; - const changes = {} as Partial; - const spy = jest.spyOn(service, 'hasTrlGrossAxleChanged'); - const result = service.haveAxlesChanged(vehicleType, changes); - expect(spy).toHaveBeenCalledWith(changes); - expect(result).toBe(false); - }); - - it('should return true if a property of the max train axle has changed', () => { - const vehicleType = VehicleTypes.HGV; - const changes = { - techRecord_maxTrainDesignWeight: 5, - techRecord_maxTrainEecWeight: 3, - techRecord_maxTrainGbWeight: 3, - } as Partial; - - const spy = jest.spyOn(service, 'hasMaxTrainAxleChanged'); - const result = service.haveAxlesChanged(vehicleType, changes); - expect(spy).toHaveBeenCalledWith(changes); - expect(result).toBe(true); - }); - it('should return true if a property of the hgv train axle has changed', () => { - const vehicleType = VehicleTypes.HGV; - const changes = { techRecord_trainDesignWeight: 1, techRecord_trainEecWeight: 2, techRecord_trainGbWeight: 3 } as Partial; - const spy = jest.spyOn(service, 'hasHgvTrainAxleChanged'); - const result = service.haveAxlesChanged(vehicleType, changes); - expect(spy).toHaveBeenCalledWith(changes); - expect(result).toBe(true); - }); - - it('should return false if no properties of the hgv train axle have changed', () => { - const vehicleType = VehicleTypes.HGV; - const changes = {} as Partial; - const spy = jest.spyOn(service, 'hasHgvTrainAxleChanged'); - const result = service.haveAxlesChanged(vehicleType, changes); - expect(spy).toHaveBeenCalledWith(changes); - expect(result).toBe(false); - }); - - it('should return true if a property of the psv train axle has changed', () => { - const vehicleType = VehicleTypes.PSV; - const changes = { techRecord_trainDesignWeight: 1, techRecord_trainEecWeight: 2, techRecord_trainGbWeight: 3 } as Partial; - const spy = jest.spyOn(service, 'hasPsvTrainAxleChanged'); - const result = service.haveAxlesChanged(vehicleType, changes); - expect(spy).toHaveBeenCalledWith(changes); - expect(result).toBe(true); - }); - - it('should return false if no properties of the psv train axle have changed', () => { - const vehicleType = VehicleTypes.PSV; - const changes = {} as Partial; - const spy = jest.spyOn(service, 'hasPsvTrainAxleChanged'); - const result = service.haveAxlesChanged(vehicleType, changes); - expect(spy).toHaveBeenCalledWith(changes); - expect(result).toBe(false); - }); - }); - - describe('hasPsvTrainAxleChanged', () => { - it('should return true if a property of the psv train axle has changed', () => { - const changes = { techRecord_trainDesignWeight: 1, techRecord_maxTrainGbWeight: 2 } as Partial; - const spy = jest.spyOn(service, 'hasPsvTrainAxleChanged'); - const result = service.hasPsvTrainAxleChanged(changes); - expect(spy).toHaveBeenCalledWith(changes); - expect(result).toBe(true); - }); - - it('should return false if no properties of the psv train axle have changed', () => { - const changes = {} as Partial; - const spy = jest.spyOn(service, 'hasPsvTrainAxleChanged'); - const result = service.hasPsvTrainAxleChanged(changes); - expect(spy).toHaveBeenCalledWith(changes); - expect(result).toBe(false); - }); - }); - - describe('hasHgvTrainAxleChanged', () => { - it('should return true if a property of the hgv train axle has changed', () => { - const changes = { techRecord_trainDesignWeight: 1, techRecord_maxTrainGbWeight: 2 } as Partial; - const spy = jest.spyOn(service, 'hasHgvTrainAxleChanged'); - const result = service.hasHgvTrainAxleChanged(changes); - expect(spy).toHaveBeenCalledWith(changes); - expect(result).toBe(true); - }); - - it('should return false if no properties of the hgv train axle have changed', () => { - const changes = {} as Partial; - const spy = jest.spyOn(service, 'hasHgvTrainAxleChanged'); - const result = service.hasHgvTrainAxleChanged(changes); - expect(spy).toHaveBeenCalledWith(changes); - expect(result).toBe(false); - }); - }); - - describe('hasTrlGrossAxleChanged', () => { - it('should return true if a property of the trl gross axle has changed', () => { - const changes = { techRecord_grossDesignWeight: 1, techRecord_grossEecWeight: 2, techRecord_grossGbWeight: 3 } as Partial; - const spy = jest.spyOn(service, 'hasTrlGrossAxleChanged'); - const result = service.hasTrlGrossAxleChanged(changes); - expect(spy).toHaveBeenCalledWith(changes); - expect(result).toBe(true); - }); - - it('should return false if no properties of the trl gross axle have changed', () => { - const changes = {} as Partial; - const spy = jest.spyOn(service, 'hasTrlGrossAxleChanged'); - const result = service.hasTrlGrossAxleChanged(changes); - expect(spy).toHaveBeenCalledWith(changes); - expect(result).toBe(false); - }); - }); - - describe('hasHgvGrossAxleChanged', () => { - it('should return true if a property of the hgv gross axle has changed', () => { - const changes = { techRecord_grossDesignWeight: 1, techRecord_grossEecWeight: 2, techRecord_grossGbWeight: 3 } as Partial; - const spy = jest.spyOn(service, 'hasHgvGrossAxleChanged'); - const result = service.hasHgvGrossAxleChanged(changes); - expect(spy).toHaveBeenCalledWith(changes); - expect(result).toBe(true); - }); - - it('should return false if no properties of the hgv gross axle have changed', () => { - const changes = {} as Partial; - const spy = jest.spyOn(service, 'hasHgvGrossAxleChanged'); - const result = service.hasHgvGrossAxleChanged(changes); - expect(spy).toHaveBeenCalledWith(changes); - expect(result).toBe(false); - }); - }); - - describe('hasMaxTrainAxleChanged', () => { - it('should return true if a property of the max train axle has changed', () => { - const changes = { techRecord_trainDesignWeight: 1, techRecord_maxTrainGbWeight: 2 } as Partial; - const spy = jest.spyOn(service, 'hasMaxTrainAxleChanged'); - const result = service.hasMaxTrainAxleChanged(changes); expect(spy).toHaveBeenCalledWith(changes); - expect(result).toBe(true); - }); - - it('should return false if no properties of the max train axle have changed', () => { - const changes = {} as Partial; - const spy = jest.spyOn(service, 'hasMaxTrainAxleChanged'); - const result = service.hasMaxTrainAxleChanged(changes); expect(spy).toHaveBeenCalledWith(changes); - expect(result).toBe(false); - }); - }); - - describe('hasPsvGrossAxleChanged', () => { - it('should return true if a property of the psv gross axle has changed', () => { - const changes = { techRecord_grossDesignWeight: 1, techRecord_grossEecWeight: 2, techRecord_grossGbWeight: 3 } as Partial; - const spy = jest.spyOn(service, 'hasPsvGrossAxleChanged'); - const result = service.hasPsvGrossAxleChanged(changes); - expect(spy).toHaveBeenCalledWith(changes); - expect(result).toBe(true); - }); - - it('should return false if no properties of the psv gross axle have changed', () => { - const changes = {} as Partial; - const spy = jest.spyOn(service, 'hasPsvGrossAxleChanged'); - const result = service.hasPsvGrossAxleChanged(changes); - expect(spy).toHaveBeenCalledWith(changes); - expect(result).toBe(false); - }); - }); - - describe('getAxleFittingWeightValueFromLoadIndex', () => { - const loadIndex = '100'; - const loadIndexArray: ReferenceDataTyreLoadIndex[] = [{ - resourceType: ReferenceDataResourceType.Tyres, resourceKey: loadIndex, loadIndex: '825', - }]; - it('should return double the loadIndex value passed in if fitment code is set to single', () => { - const fitmentCodeType = FitmentCodeEnum.Single; - const result = service.getAxleFittingWeightValueFromLoadIndex(loadIndex, fitmentCodeType, loadIndexArray); - expect(result).toBe(1650); - }); - it('should return quadruple the loadIndex value passed in if fitment code is set to single', () => { - const fitmentCodeType = FitmentCodeEnum.Double; - const result = service.getAxleFittingWeightValueFromLoadIndex(loadIndex, fitmentCodeType, loadIndexArray); - expect(result).toBe(3300); - }); - }); - - describe('getVehicleTypeWithSmallTrl', () => { - it('should return small trl when the vehicle is a TRL and has an EU vehicle category of o1 or o2', () => { - const smallTrl1 = { - techRecord_vehicleType: VehicleTypes.TRL, - techRecord_euVehicleCategory: EUVehicleCategory.O1, - } as unknown as V3TechRecordModel; - - const smallTrl2 = { - techRecord_vehicleType: VehicleTypes.TRL, - techRecord_euVehicleCategory: EUVehicleCategory.O2, - } as unknown as V3TechRecordModel; - - expect(service.getVehicleTypeWithSmallTrl(smallTrl1)).toEqual(VehicleTypes.SMALL_TRL); - expect(service.getVehicleTypeWithSmallTrl(smallTrl2)).toEqual(VehicleTypes.SMALL_TRL); - }); - - it('should return the regular vehicle type when the vehicle is not a TRL', () => { - const hgv = { - techRecord_vehicleType: VehicleTypes.HGV, - } as unknown as V3TechRecordModel; - - expect(service.getVehicleTypeWithSmallTrl(hgv)).toEqual(VehicleTypes.HGV); - }); - - it('should return TRL when the vehicle is a TRL and has an EU vehicle category which is neither o1 or o2', () => { - const trl = { - techRecord_vehicleType: VehicleTypes.TRL, - techRecord_euVehicleCategory: EUVehicleCategory.O3, - } as unknown as V3TechRecordModel; - - expect(service.getVehicleTypeWithSmallTrl(trl)).toEqual(VehicleTypes.TRL); - }); - }); - - describe('generateEditingVehicleTechnicalRecordFromVehicleType', () => { - it('should dispatch the createVehicle action with the provided vehicle type', () => { - const dispatchSpy = jest.spyOn(store, 'dispatch'); - service.generateEditingVehicleTechnicalRecordFromVehicleType(VehicleTypes.CAR); - expect(dispatchSpy).toHaveBeenCalledWith({ - techRecord_vehicleType: 'car', - type: '[Technical Record Service] createVehicle', - }); - }); - }); - - describe('validateVinForUpdate', () => { - it('should return an async validator which displays an appropriate error message when no new VIN is provided', async () => { - const vin = 'vin'; - const control = new FormControl('vin'); - const isUniqueSpy = jest.spyOn(service, 'isUnique') - .mockReturnValue(of(false)); - - const validator = service.validateVinForUpdate(vin); - await expect(firstValueFrom(from(validator(control)))) - .resolves.toEqual({ - validateVin: { - message: 'You must provide a new VIN', - }, - }); - - expect(isUniqueSpy).toHaveBeenCalledWith(control.value, SEARCH_TYPES.VIN); - }); - - it('should return an async validator which returns null when the VIN provided is unique', async () => { - const vin = 'vin'; - const control = new FormControl('new vin'); - const isUniqueSpy = jest.spyOn(service, 'isUnique') - .mockReturnValue(of(true)); - - const validator = service.validateVinForUpdate(vin); - await expect(firstValueFrom(from(validator(control)))).resolves.toBeNull(); - expect(isUniqueSpy).toHaveBeenCalledWith(control.value, SEARCH_TYPES.VIN); - }); - - it('should return an async validator which returns an error message when the VIN is different, but is in use', async () => { - const vin = 'vin'; - const control = new FormControl('new vin'); - const isUniqueSpy = jest.spyOn(service, 'isUnique') - .mockReturnValue(of(false)); - - const validator = service.validateVinForUpdate(vin); - await expect(firstValueFrom(from(validator(control)))) - .resolves.toEqual({ - validateVin: { message: 'This VIN already exists, if you continue it will be associated with two vehicles' }, - }); - - expect(isUniqueSpy).toHaveBeenCalledWith(control.value, SEARCH_TYPES.VIN); - }); - }); - - describe('validateVrmDoesNotExist', () => { - beforeEach(() => { - jest.spyOn(techRecordHttpService, 'search$').mockReturnValue(of([])); - }); - - it('should emit empty if the control has a falsy value', async () => { - const previousVrm = 'previous vrm'; - const control = new FormControl(null); - const checkVrmNotActiveSpy = jest.spyOn(service, 'checkVrmNotActive'); - - const validator = service.validateVrmDoesNotExist(previousVrm); - await expect(firstValueFrom(from(validator(control)))) - .rejects.toEqual(new EmptyError()); - - expect(checkVrmNotActiveSpy).toHaveBeenCalledTimes(0); - }); - - it('should return the result of checkVrm not active when the value of the provide control is truthy', async () => { - const previousVrm = 'previous vrm'; - const control = new FormControl('truthy value'); - const checkVrmNotActiveSpy = jest.spyOn(service, 'checkVrmNotActive'); - - const validator = service.validateVrmDoesNotExist(previousVrm); - await expect(firstValueFrom(from(validator(control)))) - .resolves.toBeDefined(); - - expect(checkVrmNotActiveSpy).toHaveBeenCalledTimes(1); - }); - }); - - describe('validateVrmForCherishedTransfer', () => { - beforeEach(() => { - jest.spyOn(techRecordHttpService, 'search$').mockReturnValue(of([])); - }); - - it('should return empty is the value of the control is falsy', async () => { - const control = new FormControl(null); - const searchSpy = jest.spyOn(techRecordHttpService, 'search$'); - const checkVrmNotActiveSpy = jest.spyOn(service, 'checkVrmNotActive'); - - const validator = service.validateVrmForCherishedTransfer(); - await expect(firstValueFrom(from(validator(control)))) - .rejects.toEqual(new EmptyError()); - - expect(searchSpy).toHaveBeenCalledTimes(0); - expect(checkVrmNotActiveSpy).toHaveBeenCalledTimes(0); - }); - - it('should return the result of checkVrmNotActive when the control root has no third mark', async () => { - const form = new FormGroup({ - thirdMark: new FormControl(null), - previousVrm: new FormControl('previous vrm'), - control: new FormControl('current vrm'), - }); - - const searchSpy = jest.spyOn(techRecordHttpService, 'search$'); - const checkVrmNotActiveSpy = jest.spyOn(service, 'checkVrmNotActive'); - - const validator = service.validateVrmForCherishedTransfer(); - await expect(firstValueFrom(from(validator(form.controls.control)))) - .resolves.toBeDefined(); - - expect(searchSpy).toHaveBeenCalledTimes(1); - expect(checkVrmNotActiveSpy).toHaveBeenCalledTimes(1); - }); - - it('should return null when a thirdMark is provided, and a current record exists with the previous VRM', async () => { - const form = new FormGroup({ - thirdMark: new FormControl('third mark'), - previousVrm: new FormControl('previous vrm'), - control: new FormControl('current vrm'), - }); - - const checkVrmNotActiveSpy = jest.spyOn(service, 'checkVrmNotActive'); - const searchSpy = jest.spyOn(techRecordHttpService, 'search$') - .mockReturnValue(of([ - { - primaryVrm: 'previous vrm', - techRecord_statusCode: StatusCodes.CURRENT, - } as TechRecordSearchSchema, - ])); - - const validator = service.validateVrmForCherishedTransfer(); - await expect(firstValueFrom(from(validator(form.controls.control)))) - .resolves.toBeNull(); - - expect(searchSpy).toHaveBeenCalledTimes(1); - expect(checkVrmNotActiveSpy).toHaveBeenCalledTimes(0); - }); - - it('should return an appropriate error message if there is a third mark, but the previous vrm does not belong to a current record', async () => { - const form = new FormGroup({ - thirdMark: new FormControl('third mark'), - previousVrm: new FormControl('previous vrm'), - control: new FormControl('current vrm'), - }); - - const checkVrmNotActiveSpy = jest.spyOn(service, 'checkVrmNotActive'); - const searchSpy = jest.spyOn(techRecordHttpService, 'search$') - .mockReturnValue(of([ - { - primaryVrm: 'previous vrm', - techRecord_statusCode: StatusCodes.PROVISIONAL, - } as TechRecordSearchSchema, - ])); - - const validator = service.validateVrmForCherishedTransfer(); - await expect(firstValueFrom(from(validator(form.controls.control)))) - .resolves.toEqual({ - validateVrm: { - message: 'This VRM does not exist on a current record', - }, - }); - - expect(searchSpy).toHaveBeenCalledTimes(1); - expect(checkVrmNotActiveSpy).toHaveBeenCalledTimes(0); - }); - }); - - describe('clearEditingTechRecord', () => { - it('should dispatch the updateEditingTechRecordCancel action', () => { - const dispatchSpy = jest.spyOn(store, 'dispatch'); - service.clearEditingTechRecord(); - expect(dispatchSpy).toHaveBeenCalledWith({ - type: '[Technical Record Service] updateEditingTechRecordCancel', - }); - }); - }); - - describe('clearSectionTemplateStates', () => { - it('should dispatch the clearAllSectionStates action', () => { - const dispatchSpy = jest.spyOn(store, 'dispatch'); - service.clearSectionTemplateStates(); - expect(dispatchSpy).toHaveBeenCalledWith({ - type: '[Technical Record Service] clearAllSectionState', - }); - }); - }); - - describe('checkVrmNotActive', () => { - beforeEach(() => { - jest.spyOn(techRecordHttpService, 'search$').mockReturnValue(of([])); - }); - - it('should not display an appropriate error message when the provided VRM is the same as the current', async () => { - const vrm = 'vrm'; - const control = new FormControl(vrm); - const spy = jest.spyOn(techRecordHttpService, 'search$'); - - await expect(firstValueFrom(service.checkVrmNotActive(control, vrm))) - .resolves - .toEqual({ validateVrm: { message: 'You must provide a new VRM' } }); - - expect(spy).toHaveBeenCalledWith(SEARCH_TYPES.VRM, vrm); - }); - - it('should determine if the VRM is being used by a current tech record, and display an appropriate message', async () => { - const vrm = 'vrm'; - const control = new FormControl('new vrm'); - const spy = jest.spyOn(techRecordHttpService, 'search$') - .mockReturnValue(of([ - { - primaryVrm: 'new vrm', - vin: 'vin', - techRecord_statusCode: StatusCodes.CURRENT, - } as unknown as TechRecordSearchSchema, - ])); - - await expect(firstValueFrom(service.checkVrmNotActive(control, vrm))) - .resolves - .toEqual({ - validateVrm: { - message: `A current technical record already exists for + const dispatchSpy = jest.spyOn(store, 'dispatch'); + const mockVehicleRecord = { + systemNumber: 'foo', + createdTimestamp: 'bar', + vin: 'testVin', + } as unknown as TechRecordType<'put'>; + + service.updateEditingTechRecord(mockVehicleRecord); + expect(dispatchSpy).toHaveBeenCalledTimes(1); + expect(dispatchSpy).toHaveBeenCalledWith(updateEditingTechRecord({ vehicleTechRecord: mockVehicleRecord })); + }); + + it('should update the number of axles based on the axles array', () => { + const dispatchSpy = jest.spyOn(store, 'dispatch'); + const mockVehicleRecord = mockVehicleTechnicalRecord('trl'); + mockVehicleRecord.techRecord_noOfAxles = 0; + mockVehicleRecord.techRecord_axles = [{}, {}]; + + service.updateEditingTechRecord(mockVehicleRecord as TechRecordType<'put'>); + + expect(dispatchSpy).toHaveBeenCalledWith( + updateEditingTechRecord({ + vehicleTechRecord: { + ...mockVehicleRecord, + techRecord_axles: [{}, {}], + techRecord_noOfAxles: 2, + } as TechRecordType<'put'>, + }) + ); + }); + + it('override the editable tech record and dispatch the action to update the editing vehicle record with the full vehicle record', () => { + const dispatchSpy = jest.spyOn(store, 'dispatch'); + const mockVehicleRecord = { + systemNumber: 'foo', + createdTimestamp: 'bar', + vin: 'testVin', + } as unknown as TechRecordType<'put'>; + + const mockEditableVehicleRecord = { + systemNumber: 'foo', + createdTimestamp: 'bar', + vin: 'a random vin', + } as unknown as TechRecordType<'put'>; + + service.updateEditingTechRecord(mockVehicleRecord); + expect(dispatchSpy).toHaveBeenCalledTimes(1); + expect(dispatchSpy).not.toHaveBeenCalledWith( + updateEditingTechRecord({ vehicleTechRecord: mockEditableVehicleRecord }) + ); + expect(dispatchSpy).toHaveBeenCalledWith(updateEditingTechRecord({ vehicleTechRecord: mockVehicleRecord })); + }); + + it('should throw an error if there is more than one tech record', () => { + const dispatchSpy = jest.spyOn(store, 'dispatch'); + const mockVehicleRecord = { + systemNumber: 'foo', + createdTimestamp: 'bar', + vin: 'testVin', + } as unknown as TechRecordType<'put'>; + service.updateEditingTechRecord(mockVehicleRecord); + expect(dispatchSpy).toHaveBeenCalledTimes(1); + expect(dispatchSpy).toHaveBeenCalledWith(updateEditingTechRecord({ vehicleTechRecord: mockVehicleRecord })); + }); + }); + }); + + describe('getMakeAndModel', () => { + it('should should return the make and model', () => { + expect(service.getMakeAndModel({ techRecord_make: 'Test', techRecord_model: 'Car' } as V3TechRecordModel)).toBe( + 'Test - Car' + ); + }); + + it('should return an empty string when the current record has no values for make and model', () => { + expect( + service.getMakeAndModel({ techRecord_make: undefined, techRecord_model: undefined } as V3TechRecordModel) + ).toBe(''); + }); + }); + + describe('haveAxlesChanged', () => { + it('should return true if a property of the hgv gross axle has changed', () => { + const vehicleType = VehicleTypes.HGV; + const changes = { + techRecord_grossDesignWeight: 1, + techRecord_grossEecWeight: 2, + techRecord_grossGbWeight: 3, + } as Partial; + const spy = jest.spyOn(service, 'hasHgvGrossAxleChanged'); + const result = service.haveAxlesChanged(vehicleType, changes); + expect(spy).toHaveBeenCalledWith(changes); + expect(result).toBe(true); + }); + + it('should return false if no properties of the hgv gross axle have changed', () => { + const vehicleType = VehicleTypes.HGV; + const changes = {} as Partial; + const spy = jest.spyOn(service, 'hasHgvGrossAxleChanged'); + const result = service.haveAxlesChanged(vehicleType, changes); + expect(spy).toHaveBeenCalledWith(changes); + expect(result).toBe(false); + }); + + it('should return true if a property of the psv gross axle has changed', () => { + const vehicleType = VehicleTypes.PSV; + const changes = { + techRecord_grossKerbWeight: 1, + techRecord_grossLadenWeight: 1, + techRecord_grossDesignWeight: 1, + techRecord_grossGbWeight: 1, + } as Partial; + + const spy = jest.spyOn(service, 'hasPsvGrossAxleChanged'); + const result = service.haveAxlesChanged(vehicleType, changes); + expect(spy).toHaveBeenCalledWith(changes); + expect(result).toBe(true); + }); + + it('should return false if no properties of the psv gross axle have changed', () => { + const vehicleType = VehicleTypes.PSV; + const changes = {} as Partial; + const spy = jest.spyOn(service, 'hasPsvGrossAxleChanged'); + const result = service.haveAxlesChanged(vehicleType, changes); + expect(spy).toHaveBeenCalledWith(changes); + expect(result).toBe(false); + }); + + it('should return true if a property of the trl gross axle has changed', () => { + const vehicleType = VehicleTypes.TRL; + const changes = { + techRecord_grossDesignWeight: 1, + techRecord_grossEecWeight: 2, + techRecord_grossGbWeight: 3, + } as Partial; + const spy = jest.spyOn(service, 'hasTrlGrossAxleChanged'); + const result = service.haveAxlesChanged(vehicleType, changes); + expect(spy).toHaveBeenCalledWith(changes); + expect(result).toBe(true); + }); + + it('should return false if no properties of the trl gross axle have changed', () => { + const vehicleType = VehicleTypes.TRL; + const changes = {} as Partial; + const spy = jest.spyOn(service, 'hasTrlGrossAxleChanged'); + const result = service.haveAxlesChanged(vehicleType, changes); + expect(spy).toHaveBeenCalledWith(changes); + expect(result).toBe(false); + }); + + it('should return true if a property of the max train axle has changed', () => { + const vehicleType = VehicleTypes.HGV; + const changes = { + techRecord_maxTrainDesignWeight: 5, + techRecord_maxTrainEecWeight: 3, + techRecord_maxTrainGbWeight: 3, + } as Partial; + + const spy = jest.spyOn(service, 'hasMaxTrainAxleChanged'); + const result = service.haveAxlesChanged(vehicleType, changes); + expect(spy).toHaveBeenCalledWith(changes); + expect(result).toBe(true); + }); + it('should return true if a property of the hgv train axle has changed', () => { + const vehicleType = VehicleTypes.HGV; + const changes = { + techRecord_trainDesignWeight: 1, + techRecord_trainEecWeight: 2, + techRecord_trainGbWeight: 3, + } as Partial; + const spy = jest.spyOn(service, 'hasHgvTrainAxleChanged'); + const result = service.haveAxlesChanged(vehicleType, changes); + expect(spy).toHaveBeenCalledWith(changes); + expect(result).toBe(true); + }); + + it('should return false if no properties of the hgv train axle have changed', () => { + const vehicleType = VehicleTypes.HGV; + const changes = {} as Partial; + const spy = jest.spyOn(service, 'hasHgvTrainAxleChanged'); + const result = service.haveAxlesChanged(vehicleType, changes); + expect(spy).toHaveBeenCalledWith(changes); + expect(result).toBe(false); + }); + + it('should return true if a property of the psv train axle has changed', () => { + const vehicleType = VehicleTypes.PSV; + const changes = { + techRecord_trainDesignWeight: 1, + techRecord_trainEecWeight: 2, + techRecord_trainGbWeight: 3, + } as Partial; + const spy = jest.spyOn(service, 'hasPsvTrainAxleChanged'); + const result = service.haveAxlesChanged(vehicleType, changes); + expect(spy).toHaveBeenCalledWith(changes); + expect(result).toBe(true); + }); + + it('should return false if no properties of the psv train axle have changed', () => { + const vehicleType = VehicleTypes.PSV; + const changes = {} as Partial; + const spy = jest.spyOn(service, 'hasPsvTrainAxleChanged'); + const result = service.haveAxlesChanged(vehicleType, changes); + expect(spy).toHaveBeenCalledWith(changes); + expect(result).toBe(false); + }); + }); + + describe('hasPsvTrainAxleChanged', () => { + it('should return true if a property of the psv train axle has changed', () => { + const changes = { techRecord_trainDesignWeight: 1, techRecord_maxTrainGbWeight: 2 } as Partial; + const spy = jest.spyOn(service, 'hasPsvTrainAxleChanged'); + const result = service.hasPsvTrainAxleChanged(changes); + expect(spy).toHaveBeenCalledWith(changes); + expect(result).toBe(true); + }); + + it('should return false if no properties of the psv train axle have changed', () => { + const changes = {} as Partial; + const spy = jest.spyOn(service, 'hasPsvTrainAxleChanged'); + const result = service.hasPsvTrainAxleChanged(changes); + expect(spy).toHaveBeenCalledWith(changes); + expect(result).toBe(false); + }); + }); + + describe('hasHgvTrainAxleChanged', () => { + it('should return true if a property of the hgv train axle has changed', () => { + const changes = { techRecord_trainDesignWeight: 1, techRecord_maxTrainGbWeight: 2 } as Partial; + const spy = jest.spyOn(service, 'hasHgvTrainAxleChanged'); + const result = service.hasHgvTrainAxleChanged(changes); + expect(spy).toHaveBeenCalledWith(changes); + expect(result).toBe(true); + }); + + it('should return false if no properties of the hgv train axle have changed', () => { + const changes = {} as Partial; + const spy = jest.spyOn(service, 'hasHgvTrainAxleChanged'); + const result = service.hasHgvTrainAxleChanged(changes); + expect(spy).toHaveBeenCalledWith(changes); + expect(result).toBe(false); + }); + }); + + describe('hasTrlGrossAxleChanged', () => { + it('should return true if a property of the trl gross axle has changed', () => { + const changes = { + techRecord_grossDesignWeight: 1, + techRecord_grossEecWeight: 2, + techRecord_grossGbWeight: 3, + } as Partial; + const spy = jest.spyOn(service, 'hasTrlGrossAxleChanged'); + const result = service.hasTrlGrossAxleChanged(changes); + expect(spy).toHaveBeenCalledWith(changes); + expect(result).toBe(true); + }); + + it('should return false if no properties of the trl gross axle have changed', () => { + const changes = {} as Partial; + const spy = jest.spyOn(service, 'hasTrlGrossAxleChanged'); + const result = service.hasTrlGrossAxleChanged(changes); + expect(spy).toHaveBeenCalledWith(changes); + expect(result).toBe(false); + }); + }); + + describe('hasHgvGrossAxleChanged', () => { + it('should return true if a property of the hgv gross axle has changed', () => { + const changes = { + techRecord_grossDesignWeight: 1, + techRecord_grossEecWeight: 2, + techRecord_grossGbWeight: 3, + } as Partial; + const spy = jest.spyOn(service, 'hasHgvGrossAxleChanged'); + const result = service.hasHgvGrossAxleChanged(changes); + expect(spy).toHaveBeenCalledWith(changes); + expect(result).toBe(true); + }); + + it('should return false if no properties of the hgv gross axle have changed', () => { + const changes = {} as Partial; + const spy = jest.spyOn(service, 'hasHgvGrossAxleChanged'); + const result = service.hasHgvGrossAxleChanged(changes); + expect(spy).toHaveBeenCalledWith(changes); + expect(result).toBe(false); + }); + }); + + describe('hasMaxTrainAxleChanged', () => { + it('should return true if a property of the max train axle has changed', () => { + const changes = { techRecord_trainDesignWeight: 1, techRecord_maxTrainGbWeight: 2 } as Partial; + const spy = jest.spyOn(service, 'hasMaxTrainAxleChanged'); + const result = service.hasMaxTrainAxleChanged(changes); + expect(spy).toHaveBeenCalledWith(changes); + expect(result).toBe(true); + }); + + it('should return false if no properties of the max train axle have changed', () => { + const changes = {} as Partial; + const spy = jest.spyOn(service, 'hasMaxTrainAxleChanged'); + const result = service.hasMaxTrainAxleChanged(changes); + expect(spy).toHaveBeenCalledWith(changes); + expect(result).toBe(false); + }); + }); + + describe('hasPsvGrossAxleChanged', () => { + it('should return true if a property of the psv gross axle has changed', () => { + const changes = { + techRecord_grossDesignWeight: 1, + techRecord_grossEecWeight: 2, + techRecord_grossGbWeight: 3, + } as Partial; + const spy = jest.spyOn(service, 'hasPsvGrossAxleChanged'); + const result = service.hasPsvGrossAxleChanged(changes); + expect(spy).toHaveBeenCalledWith(changes); + expect(result).toBe(true); + }); + + it('should return false if no properties of the psv gross axle have changed', () => { + const changes = {} as Partial; + const spy = jest.spyOn(service, 'hasPsvGrossAxleChanged'); + const result = service.hasPsvGrossAxleChanged(changes); + expect(spy).toHaveBeenCalledWith(changes); + expect(result).toBe(false); + }); + }); + + describe('getAxleFittingWeightValueFromLoadIndex', () => { + const loadIndex = '100'; + const loadIndexArray: ReferenceDataTyreLoadIndex[] = [ + { + resourceType: ReferenceDataResourceType.Tyres, + resourceKey: loadIndex, + loadIndex: '825', + }, + ]; + it('should return double the loadIndex value passed in if fitment code is set to single', () => { + const fitmentCodeType = FitmentCodeEnum.Single; + const result = service.getAxleFittingWeightValueFromLoadIndex(loadIndex, fitmentCodeType, loadIndexArray); + expect(result).toBe(1650); + }); + it('should return quadruple the loadIndex value passed in if fitment code is set to single', () => { + const fitmentCodeType = FitmentCodeEnum.Double; + const result = service.getAxleFittingWeightValueFromLoadIndex(loadIndex, fitmentCodeType, loadIndexArray); + expect(result).toBe(3300); + }); + }); + + describe('getVehicleTypeWithSmallTrl', () => { + it('should return small trl when the vehicle is a TRL and has an EU vehicle category of o1 or o2', () => { + const smallTrl1 = { + techRecord_vehicleType: VehicleTypes.TRL, + techRecord_euVehicleCategory: EUVehicleCategory.O1, + } as unknown as V3TechRecordModel; + + const smallTrl2 = { + techRecord_vehicleType: VehicleTypes.TRL, + techRecord_euVehicleCategory: EUVehicleCategory.O2, + } as unknown as V3TechRecordModel; + + expect(service.getVehicleTypeWithSmallTrl(smallTrl1)).toEqual(VehicleTypes.SMALL_TRL); + expect(service.getVehicleTypeWithSmallTrl(smallTrl2)).toEqual(VehicleTypes.SMALL_TRL); + }); + + it('should return the regular vehicle type when the vehicle is not a TRL', () => { + const hgv = { + techRecord_vehicleType: VehicleTypes.HGV, + } as unknown as V3TechRecordModel; + + expect(service.getVehicleTypeWithSmallTrl(hgv)).toEqual(VehicleTypes.HGV); + }); + + it('should return TRL when the vehicle is a TRL and has an EU vehicle category which is neither o1 or o2', () => { + const trl = { + techRecord_vehicleType: VehicleTypes.TRL, + techRecord_euVehicleCategory: EUVehicleCategory.O3, + } as unknown as V3TechRecordModel; + + expect(service.getVehicleTypeWithSmallTrl(trl)).toEqual(VehicleTypes.TRL); + }); + }); + + describe('generateEditingVehicleTechnicalRecordFromVehicleType', () => { + it('should dispatch the createVehicle action with the provided vehicle type', () => { + const dispatchSpy = jest.spyOn(store, 'dispatch'); + service.generateEditingVehicleTechnicalRecordFromVehicleType(VehicleTypes.CAR); + expect(dispatchSpy).toHaveBeenCalledWith({ + techRecord_vehicleType: 'car', + type: '[Technical Record Service] createVehicle', + }); + }); + }); + + describe('validateVinForUpdate', () => { + it('should return an async validator which displays an appropriate error message when no new VIN is provided', async () => { + const vin = 'vin'; + const control = new FormControl('vin'); + const isUniqueSpy = jest.spyOn(service, 'isUnique').mockReturnValue(of(false)); + + const validator = service.validateVinForUpdate(vin); + await expect(firstValueFrom(from(validator(control)))).resolves.toEqual({ + validateVin: { + message: 'You must provide a new VIN', + }, + }); + + expect(isUniqueSpy).toHaveBeenCalledWith(control.value, SEARCH_TYPES.VIN); + }); + + it('should return an async validator which returns null when the VIN provided is unique', async () => { + const vin = 'vin'; + const control = new FormControl('new vin'); + const isUniqueSpy = jest.spyOn(service, 'isUnique').mockReturnValue(of(true)); + + const validator = service.validateVinForUpdate(vin); + await expect(firstValueFrom(from(validator(control)))).resolves.toBeNull(); + expect(isUniqueSpy).toHaveBeenCalledWith(control.value, SEARCH_TYPES.VIN); + }); + + it('should return an async validator which returns an error message when the VIN is different, but is in use', async () => { + const vin = 'vin'; + const control = new FormControl('new vin'); + const isUniqueSpy = jest.spyOn(service, 'isUnique').mockReturnValue(of(false)); + + const validator = service.validateVinForUpdate(vin); + await expect(firstValueFrom(from(validator(control)))).resolves.toEqual({ + validateVin: { message: 'This VIN already exists, if you continue it will be associated with two vehicles' }, + }); + + expect(isUniqueSpy).toHaveBeenCalledWith(control.value, SEARCH_TYPES.VIN); + }); + }); + + describe('validateVrmDoesNotExist', () => { + beforeEach(() => { + jest.spyOn(techRecordHttpService, 'search$').mockReturnValue(of([])); + }); + + it('should emit empty if the control has a falsy value', async () => { + const previousVrm = 'previous vrm'; + const control = new FormControl(null); + const checkVrmNotActiveSpy = jest.spyOn(service, 'checkVrmNotActive'); + + const validator = service.validateVrmDoesNotExist(previousVrm); + await expect(firstValueFrom(from(validator(control)))).rejects.toEqual(new EmptyError()); + + expect(checkVrmNotActiveSpy).toHaveBeenCalledTimes(0); + }); + + it('should return the result of checkVrm not active when the value of the provide control is truthy', async () => { + const previousVrm = 'previous vrm'; + const control = new FormControl('truthy value'); + const checkVrmNotActiveSpy = jest.spyOn(service, 'checkVrmNotActive'); + + const validator = service.validateVrmDoesNotExist(previousVrm); + await expect(firstValueFrom(from(validator(control)))).resolves.toBeDefined(); + + expect(checkVrmNotActiveSpy).toHaveBeenCalledTimes(1); + }); + }); + + describe('validateVrmForCherishedTransfer', () => { + beforeEach(() => { + jest.spyOn(techRecordHttpService, 'search$').mockReturnValue(of([])); + }); + + it('should return empty is the value of the control is falsy', async () => { + const control = new FormControl(null); + const searchSpy = jest.spyOn(techRecordHttpService, 'search$'); + const checkVrmNotActiveSpy = jest.spyOn(service, 'checkVrmNotActive'); + + const validator = service.validateVrmForCherishedTransfer(); + await expect(firstValueFrom(from(validator(control)))).rejects.toEqual(new EmptyError()); + + expect(searchSpy).toHaveBeenCalledTimes(0); + expect(checkVrmNotActiveSpy).toHaveBeenCalledTimes(0); + }); + + it('should return the result of checkVrmNotActive when the control root has no third mark', async () => { + const form = new FormGroup({ + thirdMark: new FormControl(null), + previousVrm: new FormControl('previous vrm'), + control: new FormControl('current vrm'), + }); + + const searchSpy = jest.spyOn(techRecordHttpService, 'search$'); + const checkVrmNotActiveSpy = jest.spyOn(service, 'checkVrmNotActive'); + + const validator = service.validateVrmForCherishedTransfer(); + await expect(firstValueFrom(from(validator(form.controls.control)))).resolves.toBeDefined(); + + expect(searchSpy).toHaveBeenCalledTimes(1); + expect(checkVrmNotActiveSpy).toHaveBeenCalledTimes(1); + }); + + it('should return null when a thirdMark is provided, and a current record exists with the previous VRM', async () => { + const form = new FormGroup({ + thirdMark: new FormControl('third mark'), + previousVrm: new FormControl('previous vrm'), + control: new FormControl('current vrm'), + }); + + const checkVrmNotActiveSpy = jest.spyOn(service, 'checkVrmNotActive'); + const searchSpy = jest.spyOn(techRecordHttpService, 'search$').mockReturnValue( + of([ + { + primaryVrm: 'previous vrm', + techRecord_statusCode: StatusCodes.CURRENT, + } as TechRecordSearchSchema, + ]) + ); + + const validator = service.validateVrmForCherishedTransfer(); + await expect(firstValueFrom(from(validator(form.controls.control)))).resolves.toBeNull(); + + expect(searchSpy).toHaveBeenCalledTimes(1); + expect(checkVrmNotActiveSpy).toHaveBeenCalledTimes(0); + }); + + it('should return an appropriate error message if there is a third mark, but the previous vrm does not belong to a current record', async () => { + const form = new FormGroup({ + thirdMark: new FormControl('third mark'), + previousVrm: new FormControl('previous vrm'), + control: new FormControl('current vrm'), + }); + + const checkVrmNotActiveSpy = jest.spyOn(service, 'checkVrmNotActive'); + const searchSpy = jest.spyOn(techRecordHttpService, 'search$').mockReturnValue( + of([ + { + primaryVrm: 'previous vrm', + techRecord_statusCode: StatusCodes.PROVISIONAL, + } as TechRecordSearchSchema, + ]) + ); + + const validator = service.validateVrmForCherishedTransfer(); + await expect(firstValueFrom(from(validator(form.controls.control)))).resolves.toEqual({ + validateVrm: { + message: 'This VRM does not exist on a current record', + }, + }); + + expect(searchSpy).toHaveBeenCalledTimes(1); + expect(checkVrmNotActiveSpy).toHaveBeenCalledTimes(0); + }); + }); + + describe('clearEditingTechRecord', () => { + it('should dispatch the updateEditingTechRecordCancel action', () => { + const dispatchSpy = jest.spyOn(store, 'dispatch'); + service.clearEditingTechRecord(); + expect(dispatchSpy).toHaveBeenCalledWith({ + type: '[Technical Record Service] updateEditingTechRecordCancel', + }); + }); + }); + + describe('clearSectionTemplateStates', () => { + it('should dispatch the clearAllSectionStates action', () => { + const dispatchSpy = jest.spyOn(store, 'dispatch'); + service.clearSectionTemplateStates(); + expect(dispatchSpy).toHaveBeenCalledWith({ + type: '[Technical Record Service] clearAllSectionState', + }); + }); + }); + + describe('checkVrmNotActive', () => { + beforeEach(() => { + jest.spyOn(techRecordHttpService, 'search$').mockReturnValue(of([])); + }); + + it('should not display an appropriate error message when the provided VRM is the same as the current', async () => { + const vrm = 'vrm'; + const control = new FormControl(vrm); + const spy = jest.spyOn(techRecordHttpService, 'search$'); + + await expect(firstValueFrom(service.checkVrmNotActive(control, vrm))).resolves.toEqual({ + validateVrm: { message: 'You must provide a new VRM' }, + }); + + expect(spy).toHaveBeenCalledWith(SEARCH_TYPES.VRM, vrm); + }); + + it('should determine if the VRM is being used by a current tech record, and display an appropriate message', async () => { + const vrm = 'vrm'; + const control = new FormControl('new vrm'); + const spy = jest.spyOn(techRecordHttpService, 'search$').mockReturnValue( + of([ + { + primaryVrm: 'new vrm', + vin: 'vin', + techRecord_statusCode: StatusCodes.CURRENT, + } as unknown as TechRecordSearchSchema, + ]) + ); + + await expect(firstValueFrom(service.checkVrmNotActive(control, vrm))).resolves.toEqual({ + validateVrm: { + message: `A current technical record already exists for new vrm with the VIN number vin. Please fill in the third mark field`, - }, - }); - - expect(spy).toHaveBeenCalledWith(SEARCH_TYPES.VRM, 'new vrm'); - }); - - it('should determine if the VRM is being used by a provisional tech record, and display an appropriate message', async () => { - const vrm = 'vrm'; - const control = new FormControl('new vrm'); - const spy = jest.spyOn(techRecordHttpService, 'search$') - .mockReturnValue(of([ - { - primaryVrm: 'new vrm', - vin: 'vin', - techRecord_statusCode: StatusCodes.PROVISIONAL, - } as unknown as TechRecordSearchSchema, - ])); - - await expect(firstValueFrom(service.checkVrmNotActive(control, vrm))) - .resolves - .toEqual({ - validateVrm: { - message: 'This VRM already exists on a provisional record with the VIN: vin', - }, - }); - - expect(spy).toHaveBeenCalledWith(SEARCH_TYPES.VRM, 'new vrm'); - }); - - it('should return null when the VRM is not being used by a current or provisional record', async () => { - const vrm = 'vrm'; - const control = new FormControl('new vrm'); - const spy = jest.spyOn(techRecordHttpService, 'search$').mockReturnValue(of([])); - - await expect(firstValueFrom(service.checkVrmNotActive(control, vrm))) - .resolves - .toBeNull(); - - expect(spy).toHaveBeenCalledWith(SEARCH_TYPES.VRM, 'new vrm'); - }); - }); + }, + }); + + expect(spy).toHaveBeenCalledWith(SEARCH_TYPES.VRM, 'new vrm'); + }); + + it('should determine if the VRM is being used by a provisional tech record, and display an appropriate message', async () => { + const vrm = 'vrm'; + const control = new FormControl('new vrm'); + const spy = jest.spyOn(techRecordHttpService, 'search$').mockReturnValue( + of([ + { + primaryVrm: 'new vrm', + vin: 'vin', + techRecord_statusCode: StatusCodes.PROVISIONAL, + } as unknown as TechRecordSearchSchema, + ]) + ); + + await expect(firstValueFrom(service.checkVrmNotActive(control, vrm))).resolves.toEqual({ + validateVrm: { + message: 'This VRM already exists on a provisional record with the VIN: vin', + }, + }); + + expect(spy).toHaveBeenCalledWith(SEARCH_TYPES.VRM, 'new vrm'); + }); + + it('should return null when the VRM is not being used by a current or provisional record', async () => { + const vrm = 'vrm'; + const control = new FormControl('new vrm'); + const spy = jest.spyOn(techRecordHttpService, 'search$').mockReturnValue(of([])); + + await expect(firstValueFrom(service.checkVrmNotActive(control, vrm))).resolves.toBeNull(); + + expect(spy).toHaveBeenCalledWith(SEARCH_TYPES.VRM, 'new vrm'); + }); + }); }); diff --git a/src/app/services/technical-record/technical-record.service.ts b/src/app/services/technical-record/technical-record.service.ts index b5320a752a..c956482078 100644 --- a/src/app/services/technical-record/technical-record.service.ts +++ b/src/app/services/technical-record/technical-record.service.ts @@ -5,329 +5,386 @@ import { AxleTyreProperties } from '@api/vehicle'; import { EUVehicleCategory } from '@dvsa/cvs-type-definitions/types/v3/tech-record/enums/euVehicleCategory.enum.js'; import { TechRecordSearchSchema } from '@dvsa/cvs-type-definitions/types/v3/tech-record/get/search'; import { TechRecordType } from '@dvsa/cvs-type-definitions/types/v3/tech-record/tech-record-verb'; -import { TechRecordGETHGV, TechRecordGETPSV, TechRecordGETTRL } from '@dvsa/cvs-type-definitions/types/v3/tech-record/tech-record-verb-vehicle-type'; +import { + TechRecordGETHGV, + TechRecordGETPSV, + TechRecordGETTRL, +} from '@dvsa/cvs-type-definitions/types/v3/tech-record/tech-record-verb-vehicle-type'; import { ReferenceDataTyreLoadIndex } from '@models/reference-data.model'; import { SEARCH_TYPES } from '@models/search-types-enum'; import { - StatusCodes, - TechRecordModel, - V3TechRecordModel, - VehicleTechRecordModel, - VehicleTypes, + StatusCodes, + TechRecordModel, + V3TechRecordModel, + VehicleTechRecordModel, + VehicleTypes, } from '@models/vehicle-tech-record.model'; import { Store, select } from '@ngrx/store'; import { RouterService } from '@services/router/router.service'; import { TechnicalRecordHttpService } from '@services/technical-record-http/technical-record-http.service'; import { - selectTechRecordSearchResults, - selectTechRecordSearchResultsBySystemNumber, + selectTechRecordSearchResults, + selectTechRecordSearchResultsBySystemNumber, } from '@store/tech-record-search/selector/tech-record-search.selector'; import { - clearAllSectionStates, - createVehicle, - selectSectionState, - selectTechRecord, - selectTechRecordHistory, - techRecord, - updateEditingTechRecord, - updateEditingTechRecordCancel, + clearAllSectionStates, + createVehicle, + selectSectionState, + selectTechRecord, + selectTechRecordHistory, + techRecord, + updateEditingTechRecord, + updateEditingTechRecordCancel, } from '@store/technical-records'; import { cloneDeep } from 'lodash'; import { - Observable, catchError, combineLatest, debounceTime, filter, map, of, switchMap, take, tap, throwError, + Observable, + catchError, + combineLatest, + debounceTime, + filter, + map, + of, + switchMap, + take, + tap, + throwError, } from 'rxjs'; import FitmentCodeEnum = AxleTyreProperties.FitmentCodeEnum; @Injectable({ providedIn: 'root' }) export class TechnicalRecordService { - constructor(private store: Store, private techRecordHttpService: TechnicalRecordHttpService, private routerService: RouterService) { } - - getVehicleTypeWithSmallTrl(technicalRecord: V3TechRecordModel): VehicleTypes { - return technicalRecord.techRecord_vehicleType === VehicleTypes.TRL - && (technicalRecord.techRecord_euVehicleCategory === EUVehicleCategory.O1 - || technicalRecord.techRecord_euVehicleCategory === EUVehicleCategory.O2) - ? (VehicleTypes.SMALL_TRL as VehicleTypes) - : (technicalRecord.techRecord_vehicleType as VehicleTypes); - } - - getAxleFittingWeightValueFromLoadIndex( - loadIndexValue: string, - fitmentCodeType: FitmentCodeEnum | null | undefined, - loadIndex: ReferenceDataTyreLoadIndex[] | null, - ): number | undefined { - let factor = 4; - if (fitmentCodeType === 'single') { - factor = 2; - } - const axleLoadIndex = loadIndex?.find((resource) => resource.resourceKey === loadIndexValue); - return axleLoadIndex?.loadIndex ? +axleLoadIndex.loadIndex * factor : undefined; - } - - isUnique(valueToCheck: string, searchType: SEARCH_TYPES): Observable { - return this.techRecordHttpService.search$(searchType, valueToCheck).pipe( - map((searchResults) => { - if (searchResults.every((result) => result.techRecord_statusCode === StatusCodes.ARCHIVED)) { - return true; - } - - if (searchType === SEARCH_TYPES.VRM) { - return !searchResults.some((result) => result.primaryVrm === valueToCheck); - } - - return false; - }), - catchError((error: HttpErrorResponse) => { - return (error.status === 404 && of(true)) || throwError(() => error); - }), - ); - } - get techRecordHistory$(): Observable { - return this.store.pipe(select(selectTechRecordHistory)); - } - - get techRecord$(): Observable { - return combineLatest([ - this.store.pipe(select(selectTechRecord)), - this.store.pipe(select(techRecord)), - this.routerService.getRouteDataProperty$('isEditing'), - ]).pipe( - tap(([technicalRecord, nonEditingTechRecord, isEditing]) => { - if (isEditing && !technicalRecord && nonEditingTechRecord) { - this.updateEditingTechRecord(nonEditingTechRecord as TechRecordType<'put'>); - } - }), - map(([technicalRecord, nonEditingTechRecord, isEditing]) => (isEditing && !technicalRecord ? nonEditingTechRecord : technicalRecord)), - ); - } - - updateEditingTechRecord(record: TechRecordType<'put'>): void { - if ( - record.techRecord_vehicleType === 'psv' - || record.techRecord_vehicleType === 'hgv' - || (record.techRecord_vehicleType === 'trl' && record.techRecord_euVehicleCategory !== 'o1' && record.techRecord_euVehicleCategory !== 'o2') - ) { - record.techRecord_noOfAxles = record.techRecord_axles && record.techRecord_axles.length > 0 ? record.techRecord_axles?.length : null; - } - this.store.dispatch(updateEditingTechRecord({ vehicleTechRecord: record })); - } - - /** - * A function to filter the correct tech record, this has a hierarchy which is CURRENT -> PROVISIONAL -> ARCHIVED. - * @param record This is a VehicleTechRecordModel passed in from the parent component - * @returns returns the tech record of correct hierarchy precedence or if none exists returns undefined - */ - static filterTechRecordByStatusCode(record: VehicleTechRecordModel): TechRecordModel | undefined { - return ( - record.techRecord.find((foundRecord) => foundRecord.statusCode === StatusCodes.CURRENT) - ?? record.techRecord.find((foundRecord) => foundRecord.statusCode === StatusCodes.PROVISIONAL) - ?? record.techRecord.find((foundRecord) => foundRecord.statusCode === StatusCodes.ARCHIVED) - ); - } - - generateEditingVehicleTechnicalRecordFromVehicleType(vehicleType: VehicleTypes): void { - this.store.dispatch(createVehicle({ techRecord_vehicleType: vehicleType })); - } - - clearReasonForCreation(): void { - this.techRecord$ - .pipe( - map((data) => cloneDeep(data)), - take(1), - ) - .subscribe((data) => { - if (data) { - data.techRecord_reasonForCreation = ''; - this.updateEditingTechRecord(data as TechRecordType<'put'>); - } - }); - } - - validateVinForUpdate(originalVin?: string): AsyncValidatorFn { - return (control: AbstractControl): Observable => { - return of(control.value).pipe( - filter((value: string) => !!value), - debounceTime(1000), - take(1), - switchMap((value) => { - return this.isUnique(value, SEARCH_TYPES.VIN).pipe( - map((result) => { - if (control.value === originalVin) { - return { validateVin: { message: 'You must provide a new VIN' } }; - } - return result ? null : { validateVin: { message: 'This VIN already exists, if you continue it will be associated with two vehicles' } }; - }), - catchError(() => of(null)), - ); - }), - ); - }; - } - - validateVrmDoesNotExist(previousVrm: string): AsyncValidatorFn { - return (control: AbstractControl): Observable => { - return of(control).pipe( - filter((errorControl: AbstractControl) => !!errorControl.value), - take(1), - switchMap((vrmControl) => { - return this.checkVrmNotActive(vrmControl, previousVrm); - }), - ); - }; - } - - validateVrmForCherishedTransfer(): AsyncValidatorFn { - return (control: AbstractControl): Observable => { - return of(control).pipe( - filter((errorControl: AbstractControl) => !!errorControl.value), - take(1), - switchMap((vrmControl) => { - const thirdMark = vrmControl.root.get('thirdMark')?.value; - const previousVrm = vrmControl.root.get('previousVrm')?.value; - if (thirdMark) { - const vrmNotNew = previousVrm === vrmControl.value; - if (vrmNotNew) return of({ validateVrm: { message: 'You must provide a new VRM' } }); - return this.techRecordHttpService.search$(SEARCH_TYPES.VRM, vrmControl.value).pipe( - map((results) => { - if (results.some((result) => result.techRecord_statusCode === StatusCodes.CURRENT)) { - return null; - } - return { validateVrm: { message: 'This VRM does not exist on a current record' } }; - }), - catchError((err: HttpErrorResponse) => { - return ( - (err.status === 404 && of({ validateVrm: { message: 'This VRM does not exist on a current record' } })) || throwError(() => err) - ); - }), - ); - } - return this.checkVrmNotActive(control, previousVrm); - }), - ); - }; - } - - clearEditingTechRecord() { - this.store.dispatch(updateEditingTechRecordCancel()); - } - - get searchResults$(): Observable { - return this.store.pipe(select(selectTechRecordSearchResults)); - } - - get searchResultsWithUniqueSystemNumbers$(): Observable { - return this.store.pipe(select(selectTechRecordSearchResultsBySystemNumber)); - } - get techRecordStatus$(): Observable { - return this.techRecord$.pipe(map((technicalRecord) => technicalRecord?.techRecord_statusCode as StatusCodes | undefined)); - } - - get sectionStates$(): Observable<(string | number)[] | undefined> { - return this.store.pipe(select(selectSectionState)); - } - - getMakeAndModel(technicalRecord: V3TechRecordModel): string { - if ( - technicalRecord.techRecord_vehicleType === 'car' - || technicalRecord.techRecord_vehicleType === 'motorcycle' - || technicalRecord.techRecord_vehicleType === 'lgv' - ) { - return ''; - } - - const make = (technicalRecord?.techRecord_vehicleType === 'psv' - ? technicalRecord.techRecord_chassisMake - : technicalRecord.techRecord_make) ?? ''; - - const model = (technicalRecord.techRecord_vehicleType === 'psv' - ? technicalRecord.techRecord_chassisModel - : technicalRecord.techRecord_model) ?? ''; - - if (!make || !model) { - return make || model; - } - - return `${make} - ${model}`; - } - - clearSectionTemplateStates() { - this.store.dispatch(clearAllSectionStates()); - } - - checkVrmNotActive(control: AbstractControl, previousVrm: string) { - return this.techRecordHttpService.search$(SEARCH_TYPES.VRM, control.value).pipe( - map((results) => { - const currentRecord = results.filter((result) => result.techRecord_statusCode === StatusCodes.CURRENT); - const provisionalRecord = results.filter((result) => result.techRecord_statusCode === StatusCodes.PROVISIONAL); - - if (control.value === previousVrm) { - return { validateVrm: { message: 'You must provide a new VRM' } }; - } - if (currentRecord.length > 0) { - const value = control.value as string; - return { - validateVrm: { - message: `A current technical record already exists for + constructor( + private store: Store, + private techRecordHttpService: TechnicalRecordHttpService, + private routerService: RouterService + ) {} + + getVehicleTypeWithSmallTrl(technicalRecord: V3TechRecordModel): VehicleTypes { + return technicalRecord.techRecord_vehicleType === VehicleTypes.TRL && + (technicalRecord.techRecord_euVehicleCategory === EUVehicleCategory.O1 || + technicalRecord.techRecord_euVehicleCategory === EUVehicleCategory.O2) + ? (VehicleTypes.SMALL_TRL as VehicleTypes) + : (technicalRecord.techRecord_vehicleType as VehicleTypes); + } + + getAxleFittingWeightValueFromLoadIndex( + loadIndexValue: string, + fitmentCodeType: FitmentCodeEnum | null | undefined, + loadIndex: ReferenceDataTyreLoadIndex[] | null + ): number | undefined { + let factor = 4; + if (fitmentCodeType === 'single') { + factor = 2; + } + const axleLoadIndex = loadIndex?.find((resource) => resource.resourceKey === loadIndexValue); + return axleLoadIndex?.loadIndex ? +axleLoadIndex.loadIndex * factor : undefined; + } + + isUnique(valueToCheck: string, searchType: SEARCH_TYPES): Observable { + return this.techRecordHttpService.search$(searchType, valueToCheck).pipe( + map((searchResults) => { + if (searchResults.every((result) => result.techRecord_statusCode === StatusCodes.ARCHIVED)) { + return true; + } + + if (searchType === SEARCH_TYPES.VRM) { + return !searchResults.some((result) => result.primaryVrm === valueToCheck); + } + + return false; + }), + catchError((error: HttpErrorResponse) => { + return (error.status === 404 && of(true)) || throwError(() => error); + }) + ); + } + get techRecordHistory$(): Observable { + return this.store.pipe(select(selectTechRecordHistory)); + } + + get techRecord$(): Observable { + return combineLatest([ + this.store.pipe(select(selectTechRecord)), + this.store.pipe(select(techRecord)), + this.routerService.getRouteDataProperty$('isEditing'), + ]).pipe( + tap(([technicalRecord, nonEditingTechRecord, isEditing]) => { + if (isEditing && !technicalRecord && nonEditingTechRecord) { + this.updateEditingTechRecord(nonEditingTechRecord as TechRecordType<'put'>); + } + }), + map(([technicalRecord, nonEditingTechRecord, isEditing]) => + isEditing && !technicalRecord ? nonEditingTechRecord : technicalRecord + ) + ); + } + + updateEditingTechRecord(record: TechRecordType<'put'>): void { + if ( + record.techRecord_vehicleType === 'psv' || + record.techRecord_vehicleType === 'hgv' || + (record.techRecord_vehicleType === 'trl' && + record.techRecord_euVehicleCategory !== 'o1' && + record.techRecord_euVehicleCategory !== 'o2') + ) { + record.techRecord_noOfAxles = + record.techRecord_axles && record.techRecord_axles.length > 0 ? record.techRecord_axles?.length : null; + } + this.store.dispatch(updateEditingTechRecord({ vehicleTechRecord: record })); + } + + /** + * A function to filter the correct tech record, this has a hierarchy which is CURRENT -> PROVISIONAL -> ARCHIVED. + * @param record This is a VehicleTechRecordModel passed in from the parent component + * @returns returns the tech record of correct hierarchy precedence or if none exists returns undefined + */ + static filterTechRecordByStatusCode(record: VehicleTechRecordModel): TechRecordModel | undefined { + return ( + record.techRecord.find((foundRecord) => foundRecord.statusCode === StatusCodes.CURRENT) ?? + record.techRecord.find((foundRecord) => foundRecord.statusCode === StatusCodes.PROVISIONAL) ?? + record.techRecord.find((foundRecord) => foundRecord.statusCode === StatusCodes.ARCHIVED) + ); + } + + generateEditingVehicleTechnicalRecordFromVehicleType(vehicleType: VehicleTypes): void { + this.store.dispatch(createVehicle({ techRecord_vehicleType: vehicleType })); + } + + clearReasonForCreation(): void { + this.techRecord$ + .pipe( + map((data) => cloneDeep(data)), + take(1) + ) + .subscribe((data) => { + if (data) { + data.techRecord_reasonForCreation = ''; + this.updateEditingTechRecord(data as TechRecordType<'put'>); + } + }); + } + + validateVinForUpdate(originalVin?: string): AsyncValidatorFn { + return (control: AbstractControl): Observable => { + return of(control.value).pipe( + filter((value: string) => !!value), + debounceTime(1000), + take(1), + switchMap((value) => { + return this.isUnique(value, SEARCH_TYPES.VIN).pipe( + map((result) => { + if (control.value === originalVin) { + return { validateVin: { message: 'You must provide a new VIN' } }; + } + return result + ? null + : { + validateVin: { + message: 'This VIN already exists, if you continue it will be associated with two vehicles', + }, + }; + }), + catchError(() => of(null)) + ); + }) + ); + }; + } + + validateVrmDoesNotExist(previousVrm: string): AsyncValidatorFn { + return (control: AbstractControl): Observable => { + return of(control).pipe( + filter((errorControl: AbstractControl) => !!errorControl.value), + take(1), + switchMap((vrmControl) => { + return this.checkVrmNotActive(vrmControl, previousVrm); + }) + ); + }; + } + + validateVrmForCherishedTransfer(): AsyncValidatorFn { + return (control: AbstractControl): Observable => { + return of(control).pipe( + filter((errorControl: AbstractControl) => !!errorControl.value), + take(1), + switchMap((vrmControl) => { + const thirdMark = vrmControl.root.get('thirdMark')?.value; + const previousVrm = vrmControl.root.get('previousVrm')?.value; + if (thirdMark) { + const vrmNotNew = previousVrm === vrmControl.value; + if (vrmNotNew) return of({ validateVrm: { message: 'You must provide a new VRM' } }); + return this.techRecordHttpService.search$(SEARCH_TYPES.VRM, vrmControl.value).pipe( + map((results) => { + if (results.some((result) => result.techRecord_statusCode === StatusCodes.CURRENT)) { + return null; + } + return { validateVrm: { message: 'This VRM does not exist on a current record' } }; + }), + catchError((err: HttpErrorResponse) => { + return ( + (err.status === 404 && + of({ validateVrm: { message: 'This VRM does not exist on a current record' } })) || + throwError(() => err) + ); + }) + ); + } + return this.checkVrmNotActive(control, previousVrm); + }) + ); + }; + } + + clearEditingTechRecord() { + this.store.dispatch(updateEditingTechRecordCancel()); + } + + get searchResults$(): Observable { + return this.store.pipe(select(selectTechRecordSearchResults)); + } + + get searchResultsWithUniqueSystemNumbers$(): Observable { + return this.store.pipe(select(selectTechRecordSearchResultsBySystemNumber)); + } + get techRecordStatus$(): Observable { + return this.techRecord$.pipe( + map((technicalRecord) => technicalRecord?.techRecord_statusCode as StatusCodes | undefined) + ); + } + + get sectionStates$(): Observable<(string | number)[] | undefined> { + return this.store.pipe(select(selectSectionState)); + } + + getMakeAndModel(technicalRecord: V3TechRecordModel): string { + if ( + technicalRecord.techRecord_vehicleType === 'car' || + technicalRecord.techRecord_vehicleType === 'motorcycle' || + technicalRecord.techRecord_vehicleType === 'lgv' + ) { + return ''; + } + + const make = + (technicalRecord?.techRecord_vehicleType === 'psv' + ? technicalRecord.techRecord_chassisMake + : technicalRecord.techRecord_make) ?? ''; + + const model = + (technicalRecord.techRecord_vehicleType === 'psv' + ? technicalRecord.techRecord_chassisModel + : technicalRecord.techRecord_model) ?? ''; + + if (!make || !model) { + return make || model; + } + + return `${make} - ${model}`; + } + + clearSectionTemplateStates() { + this.store.dispatch(clearAllSectionStates()); + } + + checkVrmNotActive(control: AbstractControl, previousVrm: string) { + return this.techRecordHttpService.search$(SEARCH_TYPES.VRM, control.value).pipe( + map((results) => { + const currentRecord = results.filter((result) => result.techRecord_statusCode === StatusCodes.CURRENT); + const provisionalRecord = results.filter((result) => result.techRecord_statusCode === StatusCodes.PROVISIONAL); + + if (control.value === previousVrm) { + return { validateVrm: { message: 'You must provide a new VRM' } }; + } + if (currentRecord.length > 0) { + const value = control.value as string; + return { + validateVrm: { + message: `A current technical record already exists for ${value} with the VIN number ${currentRecord[0].vin}. Please fill in the third mark field`, - }, - }; - } - if (provisionalRecord.length > 0) { - return { validateVrm: { message: `This VRM already exists on a provisional record with the VIN: ${provisionalRecord[0].vin}` } }; - } - return null; - }), - catchError((err: HttpErrorResponse) => { - return (err.status === 404 && of(null)) || throwError(() => err); - }), - ); - } - - hasPsvGrossAxleChanged(changes: Partial): boolean { - return [ - changes.techRecord_grossKerbWeight, - changes.techRecord_grossDesignWeight, - changes.techRecord_grossLadenWeight, - changes.techRecord_grossGbWeight, - ].some(Boolean); - } - - hasHgvGrossAxleChanged(changes: Partial): boolean { - return [changes.techRecord_grossEecWeight, changes.techRecord_grossDesignWeight, changes.techRecord_grossGbWeight].some(Boolean); - } - - hasTrlGrossAxleChanged(changes: Partial): boolean { - return [changes.techRecord_grossEecWeight, changes.techRecord_grossDesignWeight, changes.techRecord_grossGbWeight].some(Boolean); - } - - hasHgvTrainAxleChanged(changes: Partial): boolean { - return [changes.techRecord_trainDesignWeight, changes.techRecord_trainGbWeight, changes.techRecord_trainEecWeight].some(Boolean); - } - - hasPsvTrainAxleChanged(changes: Partial): boolean { - return [changes.techRecord_trainDesignWeight, changes.techRecord_maxTrainGbWeight].some(Boolean); - } - - hasMaxTrainAxleChanged(changes: Partial): boolean { - return [ - changes.techRecord_maxTrainDesignWeight, - changes.techRecord_maxTrainEecWeight, - changes.techRecord_maxTrainGbWeight, - ].some(Boolean); - } - - haveAxlesChanged(vehicleType: VehicleTypes, changes: Partial>) { - if (vehicleType === 'psv' - && (this.hasPsvGrossAxleChanged(changes as Partial) - || this.hasPsvTrainAxleChanged(changes as Partial))) return true; - - if (vehicleType === 'hgv' - && (this.hasHgvTrainAxleChanged(changes as Partial) - || this.hasMaxTrainAxleChanged(changes as Partial) - || this.hasHgvGrossAxleChanged(changes as Partial))) return true; - - if (vehicleType === 'trl' && this.hasTrlGrossAxleChanged(changes as Partial)) return true; - - return false; - } + }, + }; + } + if (provisionalRecord.length > 0) { + return { + validateVrm: { + message: `This VRM already exists on a provisional record with the VIN: ${provisionalRecord[0].vin}`, + }, + }; + } + return null; + }), + catchError((err: HttpErrorResponse) => { + return (err.status === 404 && of(null)) || throwError(() => err); + }) + ); + } + + hasPsvGrossAxleChanged(changes: Partial): boolean { + return [ + changes.techRecord_grossKerbWeight, + changes.techRecord_grossDesignWeight, + changes.techRecord_grossLadenWeight, + changes.techRecord_grossGbWeight, + ].some(Boolean); + } + + hasHgvGrossAxleChanged(changes: Partial): boolean { + return [ + changes.techRecord_grossEecWeight, + changes.techRecord_grossDesignWeight, + changes.techRecord_grossGbWeight, + ].some(Boolean); + } + + hasTrlGrossAxleChanged(changes: Partial): boolean { + return [ + changes.techRecord_grossEecWeight, + changes.techRecord_grossDesignWeight, + changes.techRecord_grossGbWeight, + ].some(Boolean); + } + + hasHgvTrainAxleChanged(changes: Partial): boolean { + return [ + changes.techRecord_trainDesignWeight, + changes.techRecord_trainGbWeight, + changes.techRecord_trainEecWeight, + ].some(Boolean); + } + + hasPsvTrainAxleChanged(changes: Partial): boolean { + return [changes.techRecord_trainDesignWeight, changes.techRecord_maxTrainGbWeight].some(Boolean); + } + + hasMaxTrainAxleChanged(changes: Partial): boolean { + return [ + changes.techRecord_maxTrainDesignWeight, + changes.techRecord_maxTrainEecWeight, + changes.techRecord_maxTrainGbWeight, + ].some(Boolean); + } + + haveAxlesChanged(vehicleType: VehicleTypes, changes: Partial>) { + if ( + vehicleType === 'psv' && + (this.hasPsvGrossAxleChanged(changes as Partial) || + this.hasPsvTrainAxleChanged(changes as Partial)) + ) + return true; + + if ( + vehicleType === 'hgv' && + (this.hasHgvTrainAxleChanged(changes as Partial) || + this.hasMaxTrainAxleChanged(changes as Partial) || + this.hasHgvGrossAxleChanged(changes as Partial)) + ) + return true; + + if (vehicleType === 'trl' && this.hasTrlGrossAxleChanged(changes as Partial)) return true; + + return false; + } } diff --git a/src/app/services/test-records/test-records.service.spec.ts b/src/app/services/test-records/test-records.service.spec.ts index c17d7adb29..32be4c6a84 100644 --- a/src/app/services/test-records/test-records.service.spec.ts +++ b/src/app/services/test-records/test-records.service.spec.ts @@ -1,182 +1,199 @@ import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; import { TestBed } from '@angular/core/testing'; -import { DefaultService as CreateTestResultsService, GetTestResultsService, UpdateTestResultsService } from '@api/test-results'; +import { + DefaultService as CreateTestResultsService, + GetTestResultsService, + UpdateTestResultsService, +} from '@api/test-results'; import { TestResultModel } from '@models/test-results/test-result.model'; import { MockStore, provideMockStore } from '@ngrx/store/testing'; import { State, initialAppState } from '@store/.'; import { - createTestResult, fetchTestResults, fetchTestResultsBySystemNumber, toEditOrNotToEdit, updateTestResult, + createTestResult, + fetchTestResults, + fetchTestResultsBySystemNumber, + toEditOrNotToEdit, + updateTestResult, } from '@store/test-records'; import { mockTestResult } from '../../../mocks/mock-test-result'; import { TestRecordsService } from './test-records.service'; describe('TestRecordsService', () => { - let service: TestRecordsService; - let httpTestingController: HttpTestingController; - let store: MockStore; - - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [HttpClientTestingModule], - providers: [ - TestRecordsService, - provideMockStore({ initialState: initialAppState }), - GetTestResultsService, - UpdateTestResultsService, - CreateTestResultsService, - ], - }); - - httpTestingController = TestBed.inject(HttpTestingController); - service = TestBed.inject(TestRecordsService); - store = TestBed.inject(MockStore); - }); - - afterEach(() => { - // After every test, assert that there are no more pending requests. - httpTestingController.verify(); - }); - - it('should be created', () => { - expect(service).toBeTruthy(); - }); - - describe('API', () => { - describe('fetchTestResultbyServiceId', () => { - it('should throw error when systemNumber is empty', (done) => { - service.fetchTestResultbySystemNumber('').subscribe({ - error: (e) => { - expect(e.message).toBe('systemNumber is required'); - done(); - }, - }); - }); - - it('should add query params to url', () => { - const now = new Date('2022-01-01T00:00:00.000Z'); - service - .fetchTestResultbySystemNumber('SystemNumber', { - status: 'submited', - fromDateTime: now, - toDateTime: now, - testResultId: 'TEST_RESULT_ID', - version: '1', - }) - .subscribe({ next: () => { } }); - - // Check for correct requests: should have made one request to POST search from expected URL - const req = httpTestingController.expectOne( - // eslint-disable-next-line max-len - 'https://url/api/v1/test-results/SystemNumber?status=submited&fromDateTime=2022-01-01T00:00:00.000Z&toDateTime=2022-01-01T00:00:00.000Z&testResultId=TEST_RESULT_ID&version=1', - ); - expect(req.request.method).toBe('GET'); - - // Provide each request with a mock response - req.flush([]); - }); - - it('should get a single test result', () => { - const systemNumber = 'SYS0001'; - const mockData = mockTestResult(); - service.fetchTestResultbySystemNumber(systemNumber).subscribe((response) => { - expect(response).toEqual(mockData); - }); - - // Check for correct requests: should have made one request to POST search from expected URL - const req = httpTestingController.expectOne('https://url/api/v1/test-results/SYS0001'); - expect(req.request.method).toBe('GET'); - - // Provide each request with a mock response - req.flush(mockData); - }); - }); - }); - - describe('TestRecordsService.prototype.loadTestResults.name', () => { - it('should dispatch fetchTestResults action', () => { - const dispatchSpy = jest.spyOn(store, 'dispatch'); - service.loadTestResults(); - expect(dispatchSpy).toHaveBeenCalledWith(fetchTestResults()); - }); - }); - - describe('TestRecordsService.prototype.loadTestResultBySystemNumber.name', () => { - it('should dispatch fetchTestResultsBySystemNumber action', () => { - const dispatchSpy = jest.spyOn(store, 'dispatch'); - const systemNumber = 'SYS0001'; - service.loadTestResultBySystemNumber(systemNumber); - expect(dispatchSpy).toHaveBeenCalledWith(fetchTestResultsBySystemNumber({ systemNumber })); - }); - }); - - describe('TestRecordsService.prototype.updateTestResult.name', () => { - it('should dispatch updateTestResultState action', () => { - const dispatchSpy = jest.spyOn(store, 'dispatch'); - service.updateTestResult({} as TestResultModel); - expect(dispatchSpy).toHaveBeenCalledWith(updateTestResult({ value: {} as TestResultModel })); - }); - }); - - describe('TestRecordsService.prototype.createTestResult.name', () => { - it('should dispatch createTestResult action', () => { - const dispatchSpy = jest.spyOn(store, 'dispatch'); - service.createTestResult({} as TestResultModel); - expect(dispatchSpy).toHaveBeenCalledWith(createTestResult({ value: {} as TestResultModel })); - }); - }); - - describe('getTestTypeGroup', () => { - it('should get the correct testTypeGroup', () => { - expect(TestRecordsService.getTestTypeGroup('1')).toBe('testTypesGroup1'); - }); - - it('should return undefined if the testTypeGroup is not supported', () => { - expect(TestRecordsService.getTestTypeGroup('foo')).toBeUndefined(); - }); - }); - - describe('isTestTypeGroupEditable$', () => { - beforeEach(() => { - store.resetSelectors(); - }); - - it('should return true if the test type id is in a valid test type group and the test type group is in the master template', (done) => { - store.overrideSelector(toEditOrNotToEdit, { vehicleType: 'psv', testTypes: [{ testTypeId: '1' }] } as TestResultModel); - service.isTestTypeGroupEditable$.subscribe((isValid) => { - expect(isValid).toBe(true); - done(); - }); - }); - - it('should return false if the test type id is not in a test type gorup', (done) => { - store.overrideSelector(toEditOrNotToEdit, { vehicleType: 'psv', testTypes: [{ testTypeId: 'foo' }] } as TestResultModel); - service.isTestTypeGroupEditable$.subscribe((isValid) => { - expect(isValid).toBe(false); - done(); - }); - }); - - it('should return false if the test type group is not in the master template', (done) => { - store.overrideSelector(toEditOrNotToEdit, { vehicleType: 'psv', testTypes: [{ testTypeId: 'foo' }] } as TestResultModel); - service.isTestTypeGroupEditable$.subscribe((isValid) => { - expect(isValid).toBe(false); - done(); - }); - }); - - it('should return false if the testResult is undefined', (done) => { - store.overrideSelector(toEditOrNotToEdit, undefined); - service.isTestTypeGroupEditable$.subscribe((isValid) => { - expect(isValid).toBe(false); - done(); - }); - }); - }); - - describe('postTestResult', () => { - it('should call the service', () => { - service['createTestResultsService'].testResultsPost = jest.fn().mockReturnValue('foo'); - expect(service.postTestResult({} as TestResultModel)).toBe('foo'); - }); - }); + let service: TestRecordsService; + let httpTestingController: HttpTestingController; + let store: MockStore; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [HttpClientTestingModule], + providers: [ + TestRecordsService, + provideMockStore({ initialState: initialAppState }), + GetTestResultsService, + UpdateTestResultsService, + CreateTestResultsService, + ], + }); + + httpTestingController = TestBed.inject(HttpTestingController); + service = TestBed.inject(TestRecordsService); + store = TestBed.inject(MockStore); + }); + + afterEach(() => { + // After every test, assert that there are no more pending requests. + httpTestingController.verify(); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); + + describe('API', () => { + describe('fetchTestResultbyServiceId', () => { + it('should throw error when systemNumber is empty', (done) => { + service.fetchTestResultbySystemNumber('').subscribe({ + error: (e) => { + expect(e.message).toBe('systemNumber is required'); + done(); + }, + }); + }); + + it('should add query params to url', () => { + const now = new Date('2022-01-01T00:00:00.000Z'); + service + .fetchTestResultbySystemNumber('SystemNumber', { + status: 'submited', + fromDateTime: now, + toDateTime: now, + testResultId: 'TEST_RESULT_ID', + version: '1', + }) + .subscribe({ next: () => {} }); + + // Check for correct requests: should have made one request to POST search from expected URL + const req = httpTestingController.expectOne( + // eslint-disable-next-line max-len + 'https://url/api/v1/test-results/SystemNumber?status=submited&fromDateTime=2022-01-01T00:00:00.000Z&toDateTime=2022-01-01T00:00:00.000Z&testResultId=TEST_RESULT_ID&version=1' + ); + expect(req.request.method).toBe('GET'); + + // Provide each request with a mock response + req.flush([]); + }); + + it('should get a single test result', () => { + const systemNumber = 'SYS0001'; + const mockData = mockTestResult(); + service.fetchTestResultbySystemNumber(systemNumber).subscribe((response) => { + expect(response).toEqual(mockData); + }); + + // Check for correct requests: should have made one request to POST search from expected URL + const req = httpTestingController.expectOne('https://url/api/v1/test-results/SYS0001'); + expect(req.request.method).toBe('GET'); + + // Provide each request with a mock response + req.flush(mockData); + }); + }); + }); + + describe('TestRecordsService.prototype.loadTestResults.name', () => { + it('should dispatch fetchTestResults action', () => { + const dispatchSpy = jest.spyOn(store, 'dispatch'); + service.loadTestResults(); + expect(dispatchSpy).toHaveBeenCalledWith(fetchTestResults()); + }); + }); + + describe('TestRecordsService.prototype.loadTestResultBySystemNumber.name', () => { + it('should dispatch fetchTestResultsBySystemNumber action', () => { + const dispatchSpy = jest.spyOn(store, 'dispatch'); + const systemNumber = 'SYS0001'; + service.loadTestResultBySystemNumber(systemNumber); + expect(dispatchSpy).toHaveBeenCalledWith(fetchTestResultsBySystemNumber({ systemNumber })); + }); + }); + + describe('TestRecordsService.prototype.updateTestResult.name', () => { + it('should dispatch updateTestResultState action', () => { + const dispatchSpy = jest.spyOn(store, 'dispatch'); + service.updateTestResult({} as TestResultModel); + expect(dispatchSpy).toHaveBeenCalledWith(updateTestResult({ value: {} as TestResultModel })); + }); + }); + + describe('TestRecordsService.prototype.createTestResult.name', () => { + it('should dispatch createTestResult action', () => { + const dispatchSpy = jest.spyOn(store, 'dispatch'); + service.createTestResult({} as TestResultModel); + expect(dispatchSpy).toHaveBeenCalledWith(createTestResult({ value: {} as TestResultModel })); + }); + }); + + describe('getTestTypeGroup', () => { + it('should get the correct testTypeGroup', () => { + expect(TestRecordsService.getTestTypeGroup('1')).toBe('testTypesGroup1'); + }); + + it('should return undefined if the testTypeGroup is not supported', () => { + expect(TestRecordsService.getTestTypeGroup('foo')).toBeUndefined(); + }); + }); + + describe('isTestTypeGroupEditable$', () => { + beforeEach(() => { + store.resetSelectors(); + }); + + it('should return true if the test type id is in a valid test type group and the test type group is in the master template', (done) => { + store.overrideSelector(toEditOrNotToEdit, { + vehicleType: 'psv', + testTypes: [{ testTypeId: '1' }], + } as TestResultModel); + service.isTestTypeGroupEditable$.subscribe((isValid) => { + expect(isValid).toBe(true); + done(); + }); + }); + + it('should return false if the test type id is not in a test type gorup', (done) => { + store.overrideSelector(toEditOrNotToEdit, { + vehicleType: 'psv', + testTypes: [{ testTypeId: 'foo' }], + } as TestResultModel); + service.isTestTypeGroupEditable$.subscribe((isValid) => { + expect(isValid).toBe(false); + done(); + }); + }); + + it('should return false if the test type group is not in the master template', (done) => { + store.overrideSelector(toEditOrNotToEdit, { + vehicleType: 'psv', + testTypes: [{ testTypeId: 'foo' }], + } as TestResultModel); + service.isTestTypeGroupEditable$.subscribe((isValid) => { + expect(isValid).toBe(false); + done(); + }); + }); + + it('should return false if the testResult is undefined', (done) => { + store.overrideSelector(toEditOrNotToEdit, undefined); + service.isTestTypeGroupEditable$.subscribe((isValid) => { + expect(isValid).toBe(false); + done(); + }); + }); + }); + + describe('postTestResult', () => { + it('should call the service', () => { + service['createTestResultsService'].testResultsPost = jest.fn().mockReturnValue('foo'); + expect(service.postTestResult({} as TestResultModel)).toBe('foo'); + }); + }); }); diff --git a/src/app/services/test-records/test-records.service.ts b/src/app/services/test-records/test-records.service.ts index fd97ea0277..db0d0ca0ec 100644 --- a/src/app/services/test-records/test-records.service.ts +++ b/src/app/services/test-records/test-records.service.ts @@ -1,8 +1,11 @@ import { Injectable } from '@angular/core'; import { - CompleteTestResults, DefaultService as CreateTestResultsService, GetTestResultsService, UpdateTestResultsService, + CompleteTestResults, + DefaultService as CreateTestResultsService, + GetTestResultsService, + UpdateTestResultsService, } from '@api/test-results'; -import { TEST_TYPES } from '@forms/models/testTypeId.enum'; +import { TEST_TYPES, TEST_TYPES_GROUP1_SPEC_TEST, TEST_TYPES_GROUP5_SPEC_TEST } from '@forms/models/testTypeId.enum'; import { FormNode } from '@forms/services/dynamic-form.types'; import { contingencyTestTemplates } from '@forms/templates/test-records/create-master.template'; import { masterTpl } from '@forms/templates/test-records/master.template'; @@ -11,212 +14,235 @@ import { TestResultModel } from '@models/test-results/test-result.model'; import { VehicleTypes } from '@models/vehicle-tech-record.model'; import { Store, select } from '@ngrx/store'; import { - TestResultsState, - cancelEditingTestResult, - cleanTestResult, - contingencyTestTypeSelected, - createTestResult, - editingTestResult, - fetchTestResults, - fetchTestResultsBySystemNumber, - isTestTypeKeySame, - sectionTemplates, - selectAllTestResults, - selectAmendedDefectData, - selectDefectData, - selectedAmendedTestResultState, - selectedTestResultState, - testResultInEdit, - testTypeIdChanged, - toEditOrNotToEdit, - updateEditingTestResult, - updateTestResult, - updateTestResultFailed, + TestResultsState, + cancelEditingTestResult, + cleanTestResult, + contingencyTestTypeSelected, + createTestResult, + editingTestResult, + fetchTestResults, + fetchTestResultsBySystemNumber, + isTestTypeKeySame, + sectionTemplates, + selectAllTestResults, + selectAmendedDefectData, + selectDefectData, + selectedAmendedTestResultState, + selectedTestResultState, + testResultInEdit, + testTypeIdChanged, + toEditOrNotToEdit, + updateEditingTestResult, + updateTestResult, + updateTestResultFailed, } from '@store/test-records'; import cloneDeep from 'lodash.clonedeep'; import { Observable, take, throwError } from 'rxjs'; @Injectable({ - providedIn: 'root', + providedIn: 'root', }) export class TestRecordsService { - constructor( - private store: Store, - private updateTestResultsService: UpdateTestResultsService, - private getTestResultService: GetTestResultsService, - private createTestResultsService: CreateTestResultsService, - ) {} - - fetchTestResultbySystemNumber( - systemNumber: string, - queryparams: { - status?: string; - fromDateTime?: Date; - toDateTime?: Date; - testResultId?: string; - version?: string; - } = {}, - ): Observable> { - if (!systemNumber) { - return throwError(() => new Error('systemNumber is required')); - } - - const { - status, fromDateTime, toDateTime, testResultId, version, - } = queryparams; - return this.getTestResultService.testResultsSystemNumberGet(systemNumber, status, fromDateTime, toDateTime, testResultId, version) as Observable< - Array - >; - } - - loadTestResults(): void { - this.store.dispatch(fetchTestResults()); - } - - loadTestResultBySystemNumber(systemNumber: string): void { - this.store.dispatch(fetchTestResultsBySystemNumber({ systemNumber })); - } - - get testResult$() { - return this.store.pipe(select(selectedTestResultState)); - } - - get editingTestResult$() { - return this.store.pipe(select(testResultInEdit)); - } - - get testRecords$() { - return this.store.pipe(select(selectAllTestResults)); - } - - get defectData$() { - return this.store.pipe(select(selectDefectData)); - } - - get amendedTestResult$() { - return this.store.pipe(select(selectedAmendedTestResultState)); - } - - get amendedDefectData$() { - return this.store.pipe(select(selectAmendedDefectData)); - } - - get sectionTemplates$() { - return this.store.pipe(select(sectionTemplates)); - } - - saveTestResult( - systemNumber: string, - user: { name: string; id?: string; userEmail?: string }, - body: TestResultModel, - observe?: 'body', - reportProgress?: boolean, - ): Observable { - const { name, id, userEmail } = user; - const tr = cloneDeep(body); - delete tr.testHistory; - return this.updateTestResultsService.testResultsSystemNumberPut( - { msUserDetails: { msOid: id, msUser: name, msEmailAddress: userEmail }, testResult: tr } as CompleteTestResults, - systemNumber, - observe, - reportProgress, - ) as Observable; - } - - updateTestResult(value: TestResultModel): void { - this.store.dispatch(updateTestResult({ value })); - } - - postTestResult(body: TestResultModel) { - return this.createTestResultsService.testResultsPost(body as CompleteTestResults, 'response', false); - } - - createTestResult(value: TestResultModel): void { - this.store.dispatch(createTestResult({ value })); - } - - cleanTestResult() { - return this.store.dispatch(cleanTestResult()); - } - - static getTestTypeGroup(testTypeId: string): string | undefined { - // eslint-disable-next-line no-restricted-syntax - for (const groupName in TEST_TYPES) { - if (TEST_TYPES[groupName as keyof typeof TEST_TYPES].includes(testTypeId)) { - return groupName; - } - } - return undefined; - } - - editingTestResult(testResult: TestResultModel): void { - this.store.dispatch(editingTestResult({ testTypeId: testResult.testTypes[0].testTypeId })); - } - - cancelEditingTestResult(): void { - this.store.dispatch(cancelEditingTestResult()); - } - - updateEditingTestResult(testResult: TestResultModel): void { - this.store.dispatch(updateEditingTestResult({ testResult })); - } - - get isSameTestTypeId$(): Observable { - return this.store.pipe(select(isTestTypeKeySame('testTypeId'))); - } - - testTypeChange(testTypeId: string) { - this.store.dispatch(testTypeIdChanged({ testTypeId })); - } - - get isTestTypeGroupEditable$() { - return this.store.pipe(select(toEditOrNotToEdit), this.canHandleTestType(masterTpl)); - } - - get canCreate$() { - return this.store.pipe(select(toEditOrNotToEdit), this.canHandleTestType(contingencyTestTemplates)); - } - - private canHandleTestType(templateMap: Record>>) { - return function handleTestType (source: Observable): Observable { - const handle = (testResult: TestResultModel | undefined): boolean => { - if (!testResult) { - return false; - } - - const { vehicleType } = testResult; - const testTypeId = testResult.testTypes && testResult.testTypes[0].testTypeId; - const testTypeGroup = TestRecordsService.getTestTypeGroup(testTypeId); - const vehicleTpl = vehicleType && templateMap[`${vehicleType}`]; - - return !!testTypeGroup && !!vehicleTpl && Object.prototype.hasOwnProperty.call(vehicleTpl, testTypeGroup); - }; - - return new Observable((subscriber) => { - source.subscribe({ - next: (val) => { - subscriber.next(handle(val as unknown as TestResultModel)); - }, - error: (e) => subscriber.error(e), - complete: () => subscriber.complete(), - }); - }); - }; - } - - contingencyTestTypeSelected(testType: string) { - this.store.dispatch(contingencyTestTypeSelected({ testType })); - } - - cancelTest(reason: string): void { - this.store.pipe(select(testResultInEdit), take(1)).subscribe((testResult) => { - if (!testResult) { - return this.store.dispatch(updateTestResultFailed({ errors: [{ error: 'No selected test result.' }] })); - } - - const cancelledTest = { ...testResult, testStatus: TestResultStatus.CANCELLED, reasonForCancellation: reason }; - - this.store.dispatch(updateTestResult({ value: cancelledTest })); - }); - } + constructor( + private store: Store, + private updateTestResultsService: UpdateTestResultsService, + private getTestResultService: GetTestResultsService, + private createTestResultsService: CreateTestResultsService + ) {} + + fetchTestResultbySystemNumber( + systemNumber: string, + queryparams: { + status?: string; + fromDateTime?: Date; + toDateTime?: Date; + testResultId?: string; + version?: string; + } = {} + ): Observable> { + if (!systemNumber) { + return throwError(() => new Error('systemNumber is required')); + } + + const { status, fromDateTime, toDateTime, testResultId, version } = queryparams; + return this.getTestResultService.testResultsSystemNumberGet( + systemNumber, + status, + fromDateTime, + toDateTime, + testResultId, + version + ) as Observable>; + } + + loadTestResults(): void { + this.store.dispatch(fetchTestResults()); + } + + loadTestResultBySystemNumber(systemNumber: string): void { + this.store.dispatch(fetchTestResultsBySystemNumber({ systemNumber })); + } + + get testResult$() { + return this.store.pipe(select(selectedTestResultState)); + } + + get editingTestResult$() { + return this.store.pipe(select(testResultInEdit)); + } + + get testRecords$() { + return this.store.pipe(select(selectAllTestResults)); + } + + get defectData$() { + return this.store.pipe(select(selectDefectData)); + } + + get amendedTestResult$() { + return this.store.pipe(select(selectedAmendedTestResultState)); + } + + get amendedDefectData$() { + return this.store.pipe(select(selectAmendedDefectData)); + } + + get sectionTemplates$() { + return this.store.pipe(select(sectionTemplates)); + } + + saveTestResult( + systemNumber: string, + user: { name: string; id?: string; userEmail?: string }, + body: TestResultModel, + observe?: 'body', + reportProgress?: boolean + ): Observable { + const { name, id, userEmail } = user; + const tr = cloneDeep(body); + delete tr.testHistory; + return this.updateTestResultsService.testResultsSystemNumberPut( + { msUserDetails: { msOid: id, msUser: name, msEmailAddress: userEmail }, testResult: tr } as CompleteTestResults, + systemNumber, + observe, + reportProgress + ) as Observable; + } + + updateTestResult(value: TestResultModel): void { + this.store.dispatch(updateTestResult({ value })); + } + + postTestResult(body: TestResultModel) { + return this.createTestResultsService.testResultsPost(body as CompleteTestResults, 'response', false); + } + + createTestResult(value: TestResultModel): void { + this.store.dispatch(createTestResult({ value })); + } + + cleanTestResult() { + return this.store.dispatch(cleanTestResult()); + } + + prepareTestResultForAmendment(testResults: TestResultModel[], testResult: TestResultModel): TestResultModel { + const lastIvaOrMsvaTest = testResults.find((test) => { + const testType = test?.testTypes[0]; + const testTypeId = testType?.testTypeId ?? ''; + const isIVAorMSVATest = + TEST_TYPES_GROUP1_SPEC_TEST.includes(testTypeId) || TEST_TYPES_GROUP5_SPEC_TEST.includes(testTypeId); + + return isIVAorMSVATest; + }); + + if (!lastIvaOrMsvaTest) { + return testResult; + } + + // If certificateNumber is falsy, then use the last IVA or MSVA test certificate number + testResult.testTypes[0].certificateNumber ??= lastIvaOrMsvaTest.testTypes[0].certificateNumber; + + return testResult; + } + + static getTestTypeGroup(testTypeId: string): string | undefined { + // eslint-disable-next-line no-restricted-syntax + for (const groupName in TEST_TYPES) { + if (TEST_TYPES[groupName as keyof typeof TEST_TYPES].includes(testTypeId)) { + return groupName; + } + } + return undefined; + } + + editingTestResult(testResult: TestResultModel): void { + this.store.dispatch(editingTestResult({ testTypeId: testResult.testTypes[0].testTypeId })); + } + + cancelEditingTestResult(): void { + this.store.dispatch(cancelEditingTestResult()); + } + + updateEditingTestResult(testResult: TestResultModel): void { + this.store.dispatch(updateEditingTestResult({ testResult })); + } + + get isSameTestTypeId$(): Observable { + return this.store.pipe(select(isTestTypeKeySame('testTypeId'))); + } + + testTypeChange(testTypeId: string) { + this.store.dispatch(testTypeIdChanged({ testTypeId })); + } + + get isTestTypeGroupEditable$() { + return this.store.pipe(select(toEditOrNotToEdit), this.canHandleTestType(masterTpl)); + } + + get canCreate$() { + return this.store.pipe(select(toEditOrNotToEdit), this.canHandleTestType(contingencyTestTemplates)); + } + + private canHandleTestType(templateMap: Record>>) { + return function handleTestType(source: Observable): Observable { + const handle = (testResult: TestResultModel | undefined): boolean => { + if (!testResult) { + return false; + } + + const { vehicleType } = testResult; + const testTypeId = testResult.testTypes && testResult.testTypes[0].testTypeId; + const testTypeGroup = TestRecordsService.getTestTypeGroup(testTypeId); + const vehicleTpl = vehicleType && templateMap[`${vehicleType}`]; + + return !!testTypeGroup && !!vehicleTpl && Object.prototype.hasOwnProperty.call(vehicleTpl, testTypeGroup); + }; + + return new Observable((subscriber) => { + source.subscribe({ + next: (val) => { + subscriber.next(handle(val as unknown as TestResultModel)); + }, + error: (e) => subscriber.error(e), + complete: () => subscriber.complete(), + }); + }); + }; + } + + contingencyTestTypeSelected(testType: string) { + this.store.dispatch(contingencyTestTypeSelected({ testType })); + } + + cancelTest(reason: string): void { + this.store.pipe(select(testResultInEdit), take(1)).subscribe((testResult) => { + if (!testResult) { + return this.store.dispatch(updateTestResultFailed({ errors: [{ error: 'No selected test result.' }] })); + } + + const cancelledTest = { ...testResult, testStatus: TestResultStatus.CANCELLED, reasonForCancellation: reason }; + + this.store.dispatch(updateTestResult({ value: cancelledTest })); + }); + } } diff --git a/src/app/services/test-stations/test-stations.service.spec.ts b/src/app/services/test-stations/test-stations.service.spec.ts index 72187c6869..9633c01bf4 100644 --- a/src/app/services/test-stations/test-stations.service.spec.ts +++ b/src/app/services/test-stations/test-stations.service.spec.ts @@ -7,93 +7,93 @@ import { initialAppState } from '../../store'; import { TestStationsService } from './test-stations.service'; describe('TestStationsService', () => { - let service: TestStationsService; - let httpTestingController: HttpTestingController; - - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [HttpClientTestingModule], - providers: [TestStationsService, provideMockStore({ initialState: initialAppState })], - }); - - httpTestingController = TestBed.inject(HttpTestingController); - service = TestBed.inject(TestStationsService); - }); - - afterEach(() => { - // After every test, assert that there are no more pending requests. - httpTestingController.verify(); - }); - - it('should be created', () => { - expect(service).toBeTruthy(); - }); - - describe('fetchTestStations', () => { - it('should get an array of matching results', () => { - const expectedResult = [{ testStationName: 'Some Name' } as TestStation]; - service.fetchTestStations().subscribe((response) => expect(response).toEqual(expectedResult)); - - // Check for correct requests: should have made one request to search from expected URL - const req = httpTestingController.expectOne(`${environment.VTM_API_URI}/test-stations/`); - expect(req.request.method).toBe('GET'); - - // Provide each request with a mock response - req.flush(expectedResult); - }); - - it('should handle errors', (done) => { - service.fetchTestStations().subscribe({ - next: () => {}, - error: (e) => { - expect(e.error).toBe('Deliberate 500 error'); - expect(e.status).toBe(500); - expect(e.statusText).toBe('Server Error'); - done(); - }, - }); - - // Check for correct requests: should have made one request to search from expected URL - const req = httpTestingController.expectOne(`${environment.VTM_API_URI}/test-stations/`); - expect(req.request.method).toBe('GET'); - - // Respond with mock error - req.flush('Deliberate 500 error', { status: 500, statusText: 'Server Error' }); - }); - }); - - describe('fetchTestStation', () => { - it('should get a matching result', () => { - const expectedId = 'some ID'; - const expectedResult = { testStationName: 'Some Name' } as TestStation; - service.fetchTestStation(expectedId).subscribe((response) => expect(response).toEqual(expectedResult)); - - // Check for correct requests: should have made one request to search from expected URL - const req = httpTestingController.expectOne(`${environment.VTM_API_URI}/test-stations/${expectedId}`); - expect(req.request.method).toBe('GET'); - - // Provide each request with a mock response - req.flush(expectedResult); - }); - - it('should handle errors', (done) => { - const expectedId = 'some ID'; - service.fetchTestStation(expectedId).subscribe({ - next: () => {}, - error: (e) => { - expect(e.error).toBe('Deliberate 500 error'); - expect(e.status).toBe(500); - expect(e.statusText).toBe('Server Error'); - done(); - }, - }); - - // Check for correct requests: should have made one request to search from expected URL - const req = httpTestingController.expectOne(`${environment.VTM_API_URI}/test-stations/${expectedId}`); - expect(req.request.method).toBe('GET'); - - // Respond with mock error - req.flush('Deliberate 500 error', { status: 500, statusText: 'Server Error' }); - }); - }); + let service: TestStationsService; + let httpTestingController: HttpTestingController; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [HttpClientTestingModule], + providers: [TestStationsService, provideMockStore({ initialState: initialAppState })], + }); + + httpTestingController = TestBed.inject(HttpTestingController); + service = TestBed.inject(TestStationsService); + }); + + afterEach(() => { + // After every test, assert that there are no more pending requests. + httpTestingController.verify(); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); + + describe('fetchTestStations', () => { + it('should get an array of matching results', () => { + const expectedResult = [{ testStationName: 'Some Name' } as TestStation]; + service.fetchTestStations().subscribe((response) => expect(response).toEqual(expectedResult)); + + // Check for correct requests: should have made one request to search from expected URL + const req = httpTestingController.expectOne(`${environment.VTM_API_URI}/test-stations/`); + expect(req.request.method).toBe('GET'); + + // Provide each request with a mock response + req.flush(expectedResult); + }); + + it('should handle errors', (done) => { + service.fetchTestStations().subscribe({ + next: () => {}, + error: (e) => { + expect(e.error).toBe('Deliberate 500 error'); + expect(e.status).toBe(500); + expect(e.statusText).toBe('Server Error'); + done(); + }, + }); + + // Check for correct requests: should have made one request to search from expected URL + const req = httpTestingController.expectOne(`${environment.VTM_API_URI}/test-stations/`); + expect(req.request.method).toBe('GET'); + + // Respond with mock error + req.flush('Deliberate 500 error', { status: 500, statusText: 'Server Error' }); + }); + }); + + describe('fetchTestStation', () => { + it('should get a matching result', () => { + const expectedId = 'some ID'; + const expectedResult = { testStationName: 'Some Name' } as TestStation; + service.fetchTestStation(expectedId).subscribe((response) => expect(response).toEqual(expectedResult)); + + // Check for correct requests: should have made one request to search from expected URL + const req = httpTestingController.expectOne(`${environment.VTM_API_URI}/test-stations/${expectedId}`); + expect(req.request.method).toBe('GET'); + + // Provide each request with a mock response + req.flush(expectedResult); + }); + + it('should handle errors', (done) => { + const expectedId = 'some ID'; + service.fetchTestStation(expectedId).subscribe({ + next: () => {}, + error: (e) => { + expect(e.error).toBe('Deliberate 500 error'); + expect(e.status).toBe(500); + expect(e.statusText).toBe('Server Error'); + done(); + }, + }); + + // Check for correct requests: should have made one request to search from expected URL + const req = httpTestingController.expectOne(`${environment.VTM_API_URI}/test-stations/${expectedId}`); + expect(req.request.method).toBe('GET'); + + // Respond with mock error + req.flush('Deliberate 500 error', { status: 500, statusText: 'Server Error' }); + }); + }); }); diff --git a/src/app/services/test-stations/test-stations.service.ts b/src/app/services/test-stations/test-stations.service.ts index f6ba2b6e14..6790bf1696 100644 --- a/src/app/services/test-stations/test-stations.service.ts +++ b/src/app/services/test-stations/test-stations.service.ts @@ -3,33 +3,37 @@ import { Injectable } from '@angular/core'; import { MultiOptions } from '@forms/models/options.model'; import { TestStation } from '@models/test-stations/test-station.model'; import { Store } from '@ngrx/store'; -import { testStations, TestStationsState } from '@store/test-stations'; -import { map, Observable } from 'rxjs'; +import { TestStationsState, testStations } from '@store/test-stations'; +import { Observable, map } from 'rxjs'; import { environment } from '../../../environments/environment'; @Injectable({ providedIn: 'root' }) export class TestStationsService { - private url = `${environment.VTM_API_URI}/test-stations/`; + private url = `${environment.VTM_API_URI}/test-stations/`; - constructor(private http: HttpClient, private store: Store) {} + constructor( + private http: HttpClient, + private store: Store + ) {} - fetchTestStations(): Observable> { - return this.http.get>(this.url, { responseType: 'json' }); - } + fetchTestStations(): Observable> { + return this.http.get>(this.url, { responseType: 'json' }); + } - fetchTestStation(id: string): Observable { - return this.http.get(this.url + id, { responseType: 'json' }); - } + fetchTestStation(id: string): Observable { + return this.http.get(this.url + id, { responseType: 'json' }); + } - getTestStationsOptions(): Observable { - return this.store.select(testStations).pipe( - map((allTestStations) => - allTestStations - .sort((a, b) => a.testStationName.localeCompare(b.testStationName)) - .map((testStation) => { - const label = `${testStation.testStationName} - ${testStation.testStationPNumber}`; - return { value: testStation.testStationPNumber, label }; - })), - ); - } + getTestStationsOptions(): Observable { + return this.store.select(testStations).pipe( + map((allTestStations) => + allTestStations + .sort((a, b) => a.testStationName.localeCompare(b.testStationName)) + .map((testStation) => { + const label = `${testStation.testStationName} - ${testStation.testStationPNumber}`; + return { value: testStation.testStationPNumber, label }; + }) + ) + ); + } } diff --git a/src/app/services/test-types/test-types.service.spec.ts b/src/app/services/test-types/test-types.service.spec.ts index 9f48602472..b6c6174589 100644 --- a/src/app/services/test-types/test-types.service.spec.ts +++ b/src/app/services/test-types/test-types.service.spec.ts @@ -5,18 +5,18 @@ import { initialAppState } from '@store/.'; import { TestTypesService } from './test-types.service'; describe('TestTypesService', () => { - let service: TestTypesService; + let service: TestTypesService; - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [HttpClientTestingModule], - providers: [TestTypesService, provideMockStore({ initialState: initialAppState })], - }); + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [HttpClientTestingModule], + providers: [TestTypesService, provideMockStore({ initialState: initialAppState })], + }); - service = TestBed.inject(TestTypesService); - }); + service = TestBed.inject(TestTypesService); + }); - it('should be created', () => { - expect(service).toBeTruthy(); - }); + it('should be created', () => { + expect(service).toBeTruthy(); + }); }); diff --git a/src/app/services/test-types/test-types.service.ts b/src/app/services/test-types/test-types.service.ts index 3bef6041ac..ae9586e44c 100644 --- a/src/app/services/test-types/test-types.service.ts +++ b/src/app/services/test-types/test-types.service.ts @@ -1,8 +1,6 @@ import { HttpClient } from '@angular/common/http'; import { Inject, Injectable, Optional } from '@angular/core'; -import { - BASE_PATH, Configuration, TestTypesService as TestTypesApiService, TestTypesTaxonomy, -} from '@api/test-types'; +import { BASE_PATH, Configuration, TestTypesService as TestTypesApiService, TestTypesTaxonomy } from '@api/test-types'; import { Store } from '@ngrx/store'; import { State } from '@store/.'; import { testTypeIdChanged } from '@store/test-records'; @@ -11,27 +9,27 @@ import { selectTestTypesByVehicleType } from '@store/test-types/selectors/test-t import { Observable } from 'rxjs'; @Injectable({ - providedIn: 'root', + providedIn: 'root', }) export class TestTypesService extends TestTypesApiService { - constructor( - httpClient: HttpClient, - @Optional() @Inject(BASE_PATH) basePath: string, - @Optional() configuration: Configuration, - private store: Store, - ) { - super(httpClient, basePath, configuration); - } + constructor( + httpClient: HttpClient, + @Optional() @Inject(BASE_PATH) basePath: string, + @Optional() configuration: Configuration, + private store: Store + ) { + super(httpClient, basePath, configuration); + } - get selectAllTestTypes$(): Observable { - return this.store.select(selectTestTypesByVehicleType); - } + get selectAllTestTypes$(): Observable { + return this.store.select(selectTestTypesByVehicleType); + } - fetchTestTypes(): void { - this.store.dispatch(fetchTestTypes()); - } + fetchTestTypes(): void { + this.store.dispatch(fetchTestTypes()); + } - testTypeIdChanged(testTypeId: string): void { - this.store.dispatch(testTypeIdChanged({ testTypeId })); - } + testTypeIdChanged(testTypeId: string): void { + this.store.dispatch(testTypeIdChanged({ testTypeId })); + } } diff --git a/src/app/services/user-service/user-service.spec.ts b/src/app/services/user-service/user-service.spec.ts index 463c826cdd..7005e38f5a 100644 --- a/src/app/services/user-service/user-service.spec.ts +++ b/src/app/services/user-service/user-service.spec.ts @@ -9,77 +9,80 @@ import { UserServiceState } from '../../store/user/user-service.reducer'; import { UserService } from './user-service'; jest.mock('jwt-decode', () => ({ - jwtDecode: () => ({ roles: ['12345'] }), + jwtDecode: () => ({ roles: ['12345'] }), })); describe('User-Service', () => { - let service: UserService; + let service: UserService; - let mockStore: Store<{ userservice: UserServiceState }>; - let mockBroadcast: MsalBroadcastService; - let mockMsal: MsalService; + let mockStore: Store<{ userservice: UserServiceState }>; + let mockBroadcast: MsalBroadcastService; + let mockMsal: MsalService; - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [AppModule, RouterTestingModule], - providers: [Store, MsalService, MsalBroadcastService], - }); + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [AppModule, RouterTestingModule], + providers: [Store, MsalService, MsalBroadcastService], + }); - mockStore = TestBed.inject(Store); - mockBroadcast = TestBed.inject(MsalBroadcastService); - mockMsal = TestBed.inject(MsalService); + mockStore = TestBed.inject(Store); + mockBroadcast = TestBed.inject(MsalBroadcastService); + mockMsal = TestBed.inject(MsalService); - service = new UserService(mockStore, mockBroadcast, mockMsal); - }); + service = new UserService(mockStore, mockBroadcast, mockMsal); + }); - it('should create the user service', () => { - expect(service).toBeTruthy(); - }); + it('should create the user service', () => { + expect(service).toBeTruthy(); + }); - describe('User getters', () => { - const user = { - name: 'name', userEmail: 'name@mail.com', oid: '123', accessToken: '12345', - }; + describe('User getters', () => { + const user = { + name: 'name', + userEmail: 'name@mail.com', + oid: '123', + accessToken: '12345', + }; - beforeEach(() => { - service.logIn(user); - }); + beforeEach(() => { + service.logIn(user); + }); - it('should get the userEmail', (done) => { - service.userEmail$.pipe(take(1)).subscribe((data) => { - expect(data).toEqual(user.userEmail); - done(); - }); - }); + it('should get the userEmail', (done) => { + service.userEmail$.pipe(take(1)).subscribe((data) => { + expect(data).toEqual(user.userEmail); + done(); + }); + }); - it('should get the name', (done) => { - service.name$.pipe(take(1)).subscribe((data) => { - expect(data).toEqual(user.name); - done(); - }); - }); + it('should get the name', (done) => { + service.name$.pipe(take(1)).subscribe((data) => { + expect(data).toEqual(user.name); + done(); + }); + }); - it('should get the id', (done) => { - service.id$.pipe(take(1)).subscribe((data) => { - expect(data).toEqual(user.oid); - done(); - }); - }); + it('should get the id', (done) => { + service.id$.pipe(take(1)).subscribe((data) => { + expect(data).toEqual(user.oid); + done(); + }); + }); - it('should get the roles', (done) => { - service.roles$.pipe(take(1)).subscribe((data) => { - expect(data).toEqual([user.accessToken]); - done(); - }); - }); - }); + it('should get the roles', (done) => { + service.roles$.pipe(take(1)).subscribe((data) => { + expect(data).toEqual([user.accessToken]); + done(); + }); + }); + }); - it('should logout', () => { - const dispatchSpy = jest.spyOn(mockStore, 'dispatch'); - const MsalSpy = jest.spyOn(mockMsal, 'logout').mockImplementation(() => of()); - service.logOut(); - expect(dispatchSpy).toHaveBeenCalledTimes(1); - expect(dispatchSpy).toHaveBeenCalledWith(Logout()); - expect(MsalSpy).toHaveBeenCalledTimes(1); - }); + it('should logout', () => { + const dispatchSpy = jest.spyOn(mockStore, 'dispatch'); + const MsalSpy = jest.spyOn(mockMsal, 'logout').mockImplementation(() => of()); + service.logOut(); + expect(dispatchSpy).toHaveBeenCalledTimes(1); + expect(dispatchSpy).toHaveBeenCalledWith(Logout()); + expect(MsalSpy).toHaveBeenCalledTimes(1); + }); }); diff --git a/src/app/services/user-service/user-service.ts b/src/app/services/user-service/user-service.ts index 4d70d859f5..82cdbc5a31 100644 --- a/src/app/services/user-service/user-service.ts +++ b/src/app/services/user-service/user-service.ts @@ -10,74 +10,89 @@ import * as UserServiceState from '../../store/user/user-service.reducer'; @Injectable({ providedIn: 'root' }) export class UserService implements OnDestroy { - private readonly destroying$ = new Subject(); + private readonly destroying$ = new Subject(); - constructor(private store: Store, private msalBroadcastService: MsalBroadcastService, private msal: MsalService) { - this.msalBroadcastService.msalSubject$ - .pipe( - filter((msg: EventMessage) => msg.eventType === EventType.LOGIN_SUCCESS), - takeUntil(this.destroying$), - ) - // eslint-disable-next-line @typescript-eslint/no-explicit-any - .subscribe((result: any) => { - const { - payload: { - account: { - name, - idTokenClaims: { oid, preferred_username, email }, - }, - accessToken, - }, - } = result; - const userEmail = email || preferred_username; - this.logIn({ - name, userEmail, oid, accessToken, - }); - }); - } + constructor( + private store: Store, + private msalBroadcastService: MsalBroadcastService, + private msal: MsalService + ) { + this.msalBroadcastService.msalSubject$ + .pipe( + filter((msg: EventMessage) => msg.eventType === EventType.LOGIN_SUCCESS), + takeUntil(this.destroying$) + ) + // eslint-disable-next-line @typescript-eslint/no-explicit-any + .subscribe((result: any) => { + const { + payload: { + account: { + name, + idTokenClaims: { oid, preferred_username, email }, + }, + accessToken, + }, + } = result; + const userEmail = email || preferred_username; + this.logIn({ + name, + userEmail, + oid, + accessToken, + }); + }); + } - ngOnDestroy(): void { - this.destroying$.next(); - this.destroying$.complete(); - } + ngOnDestroy(): void { + this.destroying$.next(); + this.destroying$.complete(); + } - logIn({ - name, userEmail, oid, accessToken, - }: { name: string; userEmail: string; oid: string; accessToken: string }): void { - window.localStorage.setItem('accessToken', accessToken); - const decodedJWT = jwtDecode(accessToken); - const { roles } = decodedJWT as { roles: string[] }; - this.store.dispatch(UserServiceActions.Login({ - name, userEmail, oid, roles, - })); - } + logIn({ + name, + userEmail, + oid, + accessToken, + }: { name: string; userEmail: string; oid: string; accessToken: string }): void { + window.localStorage.setItem('accessToken', accessToken); + const decodedJWT = jwtDecode(accessToken); + const { roles } = decodedJWT as { roles: string[] }; + this.store.dispatch( + UserServiceActions.Login({ + name, + userEmail, + oid, + roles, + }) + ); + } - get name$(): Observable { - return this.store.pipe(select(UserServiceState.name)); - } + get name$(): Observable { + return this.store.pipe(select(UserServiceState.name)); + } - get userEmail$(): Observable { - return this.store.pipe(select(UserServiceState.userEmail)); - } + get userEmail$(): Observable { + return this.store.pipe(select(UserServiceState.userEmail)); + } - get id$(): Observable { - return this.store.pipe(select(UserServiceState.id)); - } + get id$(): Observable { + return this.store.pipe(select(UserServiceState.id)); + } - get roles$(): Observable { - return this.store.pipe(select(UserServiceState.roles)); - } + get roles$(): Observable { + return this.store.pipe(select(UserServiceState.roles)); + } - logOut(): void { - this.store.dispatch(UserServiceActions.Logout()); - this.msal.logout(); - } + logOut(): void { + this.store.dispatch(UserServiceActions.Logout()); + this.msal.logout(); + } - get inProgress$(): Observable { - return this.msalBroadcastService.inProgress$; - } + get inProgress$(): Observable { + return this.msalBroadcastService.inProgress$; + } - get user$(): Observable { - return this.store.pipe(select(UserServiceState.user)); - } + get user$(): Observable { + return this.store.pipe(select(UserServiceState.user)); + } } diff --git a/src/app/shared/components/accordion-control/accordion-control.component.spec.ts b/src/app/shared/components/accordion-control/accordion-control.component.spec.ts index d4cb6e4dac..981a1f2c20 100644 --- a/src/app/shared/components/accordion-control/accordion-control.component.spec.ts +++ b/src/app/shared/components/accordion-control/accordion-control.component.spec.ts @@ -7,41 +7,41 @@ import { AccordionComponent } from '../accordion/accordion.component'; import { AccordionControlComponent } from './accordion-control.component'; @Component({ - selector: 'app-host', - template: ` + selector: 'app-host', + template: `
Details
`, }) -class HostComponent { } +class HostComponent {} describe('AccordionControlComponent', () => { - let component: AccordionControlComponent; - let fixture: ComponentFixture; + let component: AccordionControlComponent; + let fixture: ComponentFixture; - beforeEach(async () => { - await TestBed.configureTestingModule({ - declarations: [AccordionControlComponent, HostComponent, AccordionComponent], - providers: [provideMockStore({ initialState: initialAppState })], - }).compileComponents(); - }); + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [AccordionControlComponent, HostComponent, AccordionComponent], + providers: [provideMockStore({ initialState: initialAppState })], + }).compileComponents(); + }); - beforeEach(async () => { - fixture = TestBed.createComponent(HostComponent); - component = fixture.debugElement.query(By.directive(AccordionControlComponent)).componentInstance; - fixture.detectChanges(); - await fixture.whenRenderingDone(); - }); + beforeEach(async () => { + fixture = TestBed.createComponent(HostComponent); + component = fixture.debugElement.query(By.directive(AccordionControlComponent)).componentInstance; + fixture.detectChanges(); + await fixture.whenRenderingDone(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); - it('should open and close child accordions', () => { - expect(component.accordions?.length).toBe(1); - expect(component.accordions?.get(0)?.isExpanded).toBeFalsy(); - component.toggle(); - expect(component.accordions?.get(0)?.isExpanded).toBeTruthy(); - component.toggle(); - expect(component.accordions?.get(0)?.isExpanded).toBeFalsy(); - }); + it('should open and close child accordions', () => { + expect(component.accordions?.length).toBe(1); + expect(component.accordions?.get(0)?.isExpanded).toBeFalsy(); + component.toggle(); + expect(component.accordions?.get(0)?.isExpanded).toBeTruthy(); + component.toggle(); + expect(component.accordions?.get(0)?.isExpanded).toBeFalsy(); + }); }); diff --git a/src/app/shared/components/accordion-control/accordion-control.component.ts b/src/app/shared/components/accordion-control/accordion-control.component.ts index 79920d5017..f7130b8bc6 100644 --- a/src/app/shared/components/accordion-control/accordion-control.component.ts +++ b/src/app/shared/components/accordion-control/accordion-control.component.ts @@ -1,58 +1,67 @@ import { - ChangeDetectionStrategy, ChangeDetectorRef, Component, ContentChildren, Input, QueryList, + ChangeDetectionStrategy, + ChangeDetectorRef, + Component, + ContentChildren, + Input, + QueryList, } from '@angular/core'; import { AccordionComponent } from '../accordion/accordion.component'; @Component({ - selector: 'app-accordion-control', - templateUrl: './accordion-control.component.html', - styleUrls: ['accordion-control.component.scss'], - changeDetection: ChangeDetectionStrategy.OnPush, + selector: 'app-accordion-control', + templateUrl: './accordion-control.component.html', + styleUrls: ['accordion-control.component.scss'], + changeDetection: ChangeDetectionStrategy.OnPush, }) export class AccordionControlComponent { - private accordionsList?: QueryList; - get accordions(): QueryList | undefined { - return this.accordionsList; - } - @ContentChildren(AccordionComponent, { descendants: true, emitDistinctChangesOnly: false }) set accordions( - value: QueryList | undefined, - ) { - this.accordionsList = value; - if (this.accordionsList?.length === this.sectionState?.length) { - this.isExpanded = true; - } - if (this.isExpanded) { - this.toggleAccordions(); - } - this.expandAccordions(); - } + private accordionsList?: QueryList; + get accordions(): QueryList | undefined { + return this.accordionsList; + } + @ContentChildren(AccordionComponent, { + descendants: true, + emitDistinctChangesOnly: false, + }) + set accordions(value: QueryList | undefined) { + this.accordionsList = value; + if (this.accordionsList?.length === this.sectionState?.length) { + this.isExpanded = true; + } + if (this.isExpanded) { + this.toggleAccordions(); + } + this.expandAccordions(); + } - @Input() isExpanded = false; - @Input() layout?: string; - @Input() class = ''; - @Input() sectionState: (string | number)[] | undefined | null = []; + @Input() + isExpanded = false; + @Input() + layout?: string; + @Input() class = ''; + @Input() sectionState: (string | number)[] | undefined | null = []; - constructor(private cdr: ChangeDetectorRef) {} + constructor(private cdr: ChangeDetectorRef) {} - get iconStyle(): string { - return `govuk-accordion-nav__chevron${(this.isExpanded ? '' : ' govuk-accordion-nav__chevron--down')}`; - } + get iconStyle(): string { + return `govuk-accordion-nav__chevron${this.isExpanded ? '' : ' govuk-accordion-nav__chevron--down'}`; + } - toggle(): void { - this.isExpanded = !this.isExpanded; - this.toggleAccordions(); - this.cdr.markForCheck(); - } + toggle(): void { + this.isExpanded = !this.isExpanded; + this.toggleAccordions(); + this.cdr.markForCheck(); + } - private expandAccordions(): void { - if (this.accordions && this.sectionState && this.sectionState.length > 0) { - this.accordions?.forEach((a) => (this.sectionState?.includes(a.id) ? a.open(a.id) : a.close(a.id))); - } - } + private expandAccordions(): void { + if (this.accordions && this.sectionState && this.sectionState.length > 0) { + this.accordions?.forEach((a) => (this.sectionState?.includes(a.id) ? a.open(a.id) : a.close(a.id))); + } + } - private toggleAccordions(): void { - if (this.accordions) { - this.accordions.forEach((a) => (this.isExpanded ? a.open(a.id) : a.close(a.id))); - } - } + private toggleAccordions(): void { + if (this.accordions) { + this.accordions.forEach((a) => (this.isExpanded ? a.open(a.id) : a.close(a.id))); + } + } } diff --git a/src/app/shared/components/accordion/accordion.component.spec.ts b/src/app/shared/components/accordion/accordion.component.spec.ts index b6ae536570..2acf7335c9 100644 --- a/src/app/shared/components/accordion/accordion.component.spec.ts +++ b/src/app/shared/components/accordion/accordion.component.spec.ts @@ -7,68 +7,68 @@ import { addSectionState, removeSectionState } from '@store/technical-records'; import { AccordionComponent } from './accordion.component'; @Component({ - selector: 'app-host', - template: '
Details
', + selector: 'app-host', + template: '
Details
', }) class HostComponent {} describe('AccordionComponent', () => { - let component: AccordionComponent; - let fixture: ComponentFixture; - let store: MockStore; - beforeEach(async () => { - await TestBed.configureTestingModule({ - declarations: [AccordionComponent, HostComponent], - providers: [provideMockStore({ initialState: initialAppState })], - }).compileComponents(); - store = TestBed.inject(MockStore); - }); + let component: AccordionComponent; + let fixture: ComponentFixture; + let store: MockStore; + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [AccordionComponent, HostComponent], + providers: [provideMockStore({ initialState: initialAppState })], + }).compileComponents(); + store = TestBed.inject(MockStore); + }); - beforeEach(() => { - fixture = TestBed.createComponent(HostComponent); - component = fixture.debugElement.query(By.directive(AccordionComponent)).componentInstance; - fixture.detectChanges(); - }); + beforeEach(() => { + fixture = TestBed.createComponent(HostComponent); + component = fixture.debugElement.query(By.directive(AccordionComponent)).componentInstance; + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); - it('should render dynamic content', () => { - const content: HTMLDivElement = fixture.debugElement.query(By.css('#content')).nativeElement; - expect(content.innerHTML).toBe('Details'); - }); + it('should render dynamic content', () => { + const content: HTMLDivElement = fixture.debugElement.query(By.css('#content')).nativeElement; + expect(content.innerHTML).toBe('Details'); + }); - it('should toggle expanded value', () => { - const button: HTMLButtonElement = fixture.debugElement.query(By.css('#accordion-control-test')).nativeElement; + it('should toggle expanded value', () => { + const button: HTMLButtonElement = fixture.debugElement.query(By.css('#accordion-control-test')).nativeElement; - button.click(); - expect(component.isExpanded).toBeTruthy(); + button.click(); + expect(component.isExpanded).toBeTruthy(); - button.click(); - expect(component.isExpanded).toBeFalsy(); - }); + button.click(); + expect(component.isExpanded).toBeFalsy(); + }); - it('should set expanded value to true', () => { - const markForCheckSpy = jest.spyOn(component['cdr'], 'markForCheck'); - const dispatchSpy = jest.spyOn(store, 'dispatch'); + it('should set expanded value to true', () => { + const markForCheckSpy = jest.spyOn(component['cdr'], 'markForCheck'); + const dispatchSpy = jest.spyOn(store, 'dispatch'); - component.open('TEST_SECTION'); - expect(component.isExpanded).toBeTruthy(); - expect(markForCheckSpy).toHaveBeenCalledTimes(1); - expect(dispatchSpy).toHaveBeenCalledTimes(1); - expect(dispatchSpy).toHaveBeenCalledWith(addSectionState({ section: 'TEST_SECTION' })); - }); + component.open('TEST_SECTION'); + expect(component.isExpanded).toBeTruthy(); + expect(markForCheckSpy).toHaveBeenCalledTimes(1); + expect(dispatchSpy).toHaveBeenCalledTimes(1); + expect(dispatchSpy).toHaveBeenCalledWith(addSectionState({ section: 'TEST_SECTION' })); + }); - it('should set expanded value to false', () => { - const markForCheckSpy = jest.spyOn(component['cdr'], 'markForCheck'); - const dispatchSpy = jest.spyOn(store, 'dispatch'); + it('should set expanded value to false', () => { + const markForCheckSpy = jest.spyOn(component['cdr'], 'markForCheck'); + const dispatchSpy = jest.spyOn(store, 'dispatch'); - component.isExpanded = true; - component.close('TEST_SECTION'); - expect(component.isExpanded).toBeFalsy(); - expect(markForCheckSpy).toHaveBeenCalledTimes(1); - expect(dispatchSpy).toHaveBeenCalledTimes(1); - expect(dispatchSpy).toHaveBeenCalledWith(removeSectionState({ section: 'TEST_SECTION' })); - }); + component.isExpanded = true; + component.close('TEST_SECTION'); + expect(component.isExpanded).toBeFalsy(); + expect(markForCheckSpy).toHaveBeenCalledTimes(1); + expect(dispatchSpy).toHaveBeenCalledTimes(1); + expect(dispatchSpy).toHaveBeenCalledWith(removeSectionState({ section: 'TEST_SECTION' })); + }); }); diff --git a/src/app/shared/components/accordion/accordion.component.ts b/src/app/shared/components/accordion/accordion.component.ts index 8934cc8dad..d19cf6bd70 100644 --- a/src/app/shared/components/accordion/accordion.component.ts +++ b/src/app/shared/components/accordion/accordion.component.ts @@ -1,38 +1,37 @@ -import { - ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, -} from '@angular/core'; +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input } from '@angular/core'; import { Store } from '@ngrx/store'; -import { - addSectionState, removeSectionState, -} from '@store/technical-records'; +import { addSectionState, removeSectionState } from '@store/technical-records'; @Component({ - selector: 'app-accordion[id]', - templateUrl: './accordion.component.html', - styleUrls: ['./accordion.component.scss'], - changeDetection: ChangeDetectionStrategy.OnPush, + selector: 'app-accordion[id]', + templateUrl: './accordion.component.html', + styleUrls: ['./accordion.component.scss'], + changeDetection: ChangeDetectionStrategy.OnPush, }) export class AccordionComponent { - @Input() title: string | undefined = ''; - @Input() id: string | number = ''; + @Input() title: string | undefined = ''; + @Input() id: string | number = ''; - @Input() isExpanded: boolean | null | undefined = false; + @Input() isExpanded: boolean | null | undefined = false; - constructor(private cdr: ChangeDetectorRef, private store: Store) {} + constructor( + private cdr: ChangeDetectorRef, + private store: Store + ) {} - get iconStyle(): string { - return `govuk-accordion-nav__chevron${(this.isExpanded ? '' : ' govuk-accordion-nav__chevron--down')}`; - } + get iconStyle(): string { + return `govuk-accordion-nav__chevron${this.isExpanded ? '' : ' govuk-accordion-nav__chevron--down'}`; + } - open(sectionName: string | number | undefined): void { - this.isExpanded = true; - this.cdr.markForCheck(); - if (sectionName) this.store.dispatch(addSectionState({ section: sectionName })); - } + open(sectionName: string | number | undefined): void { + this.isExpanded = true; + this.cdr.markForCheck(); + if (sectionName) this.store.dispatch(addSectionState({ section: sectionName })); + } - close(sectionName: string | number | undefined): void { - this.isExpanded = false; - this.cdr.markForCheck(); - if (sectionName) this.store.dispatch(removeSectionState({ section: sectionName })); - } + close(sectionName: string | number | undefined): void { + this.isExpanded = false; + this.cdr.markForCheck(); + if (sectionName) this.store.dispatch(removeSectionState({ section: sectionName })); + } } diff --git a/src/app/shared/components/banner/banner.component.spec.ts b/src/app/shared/components/banner/banner.component.spec.ts index 7b0dc68397..865c11ae07 100644 --- a/src/app/shared/components/banner/banner.component.spec.ts +++ b/src/app/shared/components/banner/banner.component.spec.ts @@ -3,22 +3,22 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { BannerComponent } from './banner.component'; describe('BannerComponent', () => { - let component: BannerComponent; - let fixture: ComponentFixture; + let component: BannerComponent; + let fixture: ComponentFixture; - beforeEach(async () => { - await TestBed.configureTestingModule({ - declarations: [BannerComponent], - }).compileComponents(); - }); + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [BannerComponent], + }).compileComponents(); + }); - beforeEach(() => { - fixture = TestBed.createComponent(BannerComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); + beforeEach(() => { + fixture = TestBed.createComponent(BannerComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); }); diff --git a/src/app/shared/components/banner/banner.component.ts b/src/app/shared/components/banner/banner.component.ts index b58afe14cc..27b1063242 100644 --- a/src/app/shared/components/banner/banner.component.ts +++ b/src/app/shared/components/banner/banner.component.ts @@ -1,7 +1,7 @@ import { Component } from '@angular/core'; @Component({ - selector: 'app-banner', - templateUrl: './banner.component.html', + selector: 'app-banner', + templateUrl: './banner.component.html', }) export class BannerComponent {} diff --git a/src/app/shared/components/base-dialog/base-dialog.component.spec.ts b/src/app/shared/components/base-dialog/base-dialog.component.spec.ts index 28dbb7da27..ac5a25b311 100644 --- a/src/app/shared/components/base-dialog/base-dialog.component.spec.ts +++ b/src/app/shared/components/base-dialog/base-dialog.component.spec.ts @@ -3,22 +3,22 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { BaseDialogComponent } from './base-dialog.component'; describe('BaseDialogComponent', () => { - let component: BaseDialogComponent; - let fixture: ComponentFixture; + let component: BaseDialogComponent; + let fixture: ComponentFixture; - beforeEach(async () => { - await TestBed.configureTestingModule({ - declarations: [BaseDialogComponent], - }).compileComponents(); - }); + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [BaseDialogComponent], + }).compileComponents(); + }); - beforeEach(() => { - fixture = TestBed.createComponent(BaseDialogComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); + beforeEach(() => { + fixture = TestBed.createComponent(BaseDialogComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); }); diff --git a/src/app/shared/components/base-dialog/base-dialog.component.ts b/src/app/shared/components/base-dialog/base-dialog.component.ts index 60b050adda..efd8502c8a 100644 --- a/src/app/shared/components/base-dialog/base-dialog.component.ts +++ b/src/app/shared/components/base-dialog/base-dialog.component.ts @@ -1,13 +1,13 @@ -import { Component, Output, EventEmitter } from '@angular/core'; +import { Component, EventEmitter, Output } from '@angular/core'; @Component({ - selector: 'app-base-dialog', - template: '', + selector: 'app-base-dialog', + template: '', }) export class BaseDialogComponent { - @Output() action = new EventEmitter(); + @Output() action = new EventEmitter(); - handleAction(action: string) { - this.action.emit(action); - } + handleAction(action: string) { + this.action.emit(action); + } } diff --git a/src/app/shared/components/button-group/button-group.component.spec.ts b/src/app/shared/components/button-group/button-group.component.spec.ts index 5aabb154dd..81fe8fd84d 100644 --- a/src/app/shared/components/button-group/button-group.component.spec.ts +++ b/src/app/shared/components/button-group/button-group.component.spec.ts @@ -2,22 +2,22 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ButtonGroupComponent } from './button-group.component'; describe('ButtonGroupComponent', () => { - let component: ButtonGroupComponent; - let fixture: ComponentFixture; + let component: ButtonGroupComponent; + let fixture: ComponentFixture; - beforeEach(async () => { - await TestBed.configureTestingModule({ - declarations: [ButtonGroupComponent], - }).compileComponents(); - }); + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ButtonGroupComponent], + }).compileComponents(); + }); - beforeEach(() => { - fixture = TestBed.createComponent(ButtonGroupComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); + beforeEach(() => { + fixture = TestBed.createComponent(ButtonGroupComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); }); diff --git a/src/app/shared/components/button-group/button-group.component.ts b/src/app/shared/components/button-group/button-group.component.ts index 0f811fb6ca..205b61296e 100644 --- a/src/app/shared/components/button-group/button-group.component.ts +++ b/src/app/shared/components/button-group/button-group.component.ts @@ -1,9 +1,9 @@ import { ChangeDetectionStrategy, Component } from '@angular/core'; @Component({ - selector: 'app-button-group', - templateUrl: './button-group.component.html', - styleUrls: ['./button-group.component.scss'], - changeDetection: ChangeDetectionStrategy.OnPush, + selector: 'app-button-group', + templateUrl: './button-group.component.html', + styleUrls: ['./button-group.component.scss'], + changeDetection: ChangeDetectionStrategy.OnPush, }) export class ButtonGroupComponent {} diff --git a/src/app/shared/components/button/button.component.spec.ts b/src/app/shared/components/button/button.component.spec.ts index 59bb80f3ff..413d986027 100644 --- a/src/app/shared/components/button/button.component.spec.ts +++ b/src/app/shared/components/button/button.component.spec.ts @@ -3,23 +3,23 @@ import { RouterTestingModule } from '@angular/router/testing'; import { ButtonComponent } from './button.component'; describe('ButtonComponent', () => { - let component: ButtonComponent; - let fixture: ComponentFixture; + let component: ButtonComponent; + let fixture: ComponentFixture; - beforeEach(async () => { - await TestBed.configureTestingModule({ - declarations: [ButtonComponent], - imports: [RouterTestingModule], - }).compileComponents(); - }); + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ButtonComponent], + imports: [RouterTestingModule], + }).compileComponents(); + }); - beforeEach(() => { - fixture = TestBed.createComponent(ButtonComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); + beforeEach(() => { + fixture = TestBed.createComponent(ButtonComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); }); diff --git a/src/app/shared/components/button/button.component.ts b/src/app/shared/components/button/button.component.ts index e9fb7dd06a..a890405e21 100644 --- a/src/app/shared/components/button/button.component.ts +++ b/src/app/shared/components/button/button.component.ts @@ -1,19 +1,17 @@ -import { - ChangeDetectionStrategy, Component, EventEmitter, Input, Output, -} from '@angular/core'; +import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core'; import { RouterLinkWithHref } from '@angular/router'; @Component({ - selector: 'app-button', - templateUrl: './button.component.html', - styleUrls: ['./button.component.scss'], - changeDetection: ChangeDetectionStrategy.OnPush, + selector: 'app-button', + templateUrl: './button.component.html', + styleUrls: ['./button.component.scss'], + changeDetection: ChangeDetectionStrategy.OnPush, }) export class ButtonComponent extends RouterLinkWithHref { - @Input() id?: string; - @Input() disabled = false; - @Input() type: 'link' | 'button' = 'button'; - @Input() design: '' | 'secondary' | 'warning' | 'link' = ''; + @Input() id?: string; + @Input() disabled = false; + @Input() type: 'link' | 'button' = 'button'; + @Input() design: '' | 'secondary' | 'warning' | 'link' = ''; - @Output() clicked = new EventEmitter(); + @Output() clicked = new EventEmitter(); } diff --git a/src/app/shared/components/collapsible-text/collapsible-text.component.spec.ts b/src/app/shared/components/collapsible-text/collapsible-text.component.spec.ts index 1dd3750ca8..5a58540798 100644 --- a/src/app/shared/components/collapsible-text/collapsible-text.component.spec.ts +++ b/src/app/shared/components/collapsible-text/collapsible-text.component.spec.ts @@ -2,35 +2,34 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { CollapsibleTextComponent } from './collapsible-text.component'; describe('CollapsibleTextComponent', () => { - let component: CollapsibleTextComponent; - let fixture: ComponentFixture; + let component: CollapsibleTextComponent; + let fixture: ComponentFixture; - beforeEach(async () => { - await TestBed.configureTestingModule({ - declarations: [CollapsibleTextComponent], - }) - .compileComponents(); - }); + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [CollapsibleTextComponent], + }).compileComponents(); + }); - beforeEach(() => { - fixture = TestBed.createComponent(CollapsibleTextComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); + beforeEach(() => { + fixture = TestBed.createComponent(CollapsibleTextComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); - it('should open when open method is called', () => { - component.isCollapsed = true; - component.open(); - expect(component.isCollapsed).toBe(false); - }); + it('should open when open method is called', () => { + component.isCollapsed = true; + component.open(); + expect(component.isCollapsed).toBe(false); + }); - it('should close when close method is called', () => { - component.isCollapsed = false; - component.close(); - expect(component.isCollapsed).toBe(true); - }); + it('should close when close method is called', () => { + component.isCollapsed = false; + component.close(); + expect(component.isCollapsed).toBe(true); + }); }); diff --git a/src/app/shared/components/collapsible-text/collapsible-text.component.ts b/src/app/shared/components/collapsible-text/collapsible-text.component.ts index e39ff48585..031e696c21 100644 --- a/src/app/shared/components/collapsible-text/collapsible-text.component.ts +++ b/src/app/shared/components/collapsible-text/collapsible-text.component.ts @@ -1,21 +1,20 @@ import { Component, Input } from '@angular/core'; @Component({ - selector: 'collapsible-text', - templateUrl: './collapsible-text.component.html', - styleUrls: ['./collapsible-text.component.scss'], + selector: 'collapsible-text', + templateUrl: './collapsible-text.component.html', + styleUrls: ['./collapsible-text.component.scss'], }) export class CollapsibleTextComponent { + @Input() text = ''; + @Input() maxChars = 0; + @Input() isCollapsed = true; - @Input() text: string = ''; - @Input() maxChars: number = 0; - @Input() isCollapsed = true; + open() { + this.isCollapsed = false; + } - open() { - this.isCollapsed = false; - } - - close() { - this.isCollapsed = true; - } + close() { + this.isCollapsed = true; + } } diff --git a/src/app/shared/components/icon/icon.component.spec.ts b/src/app/shared/components/icon/icon.component.spec.ts index bc86bb27ba..2a0824d9d1 100644 --- a/src/app/shared/components/icon/icon.component.spec.ts +++ b/src/app/shared/components/icon/icon.component.spec.ts @@ -2,22 +2,22 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { IconComponent } from './icon.component'; describe('IconComponent', () => { - let component: IconComponent; - let fixture: ComponentFixture; + let component: IconComponent; + let fixture: ComponentFixture; - beforeEach(async () => { - await TestBed.configureTestingModule({ - declarations: [IconComponent], - }).compileComponents(); - }); + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [IconComponent], + }).compileComponents(); + }); - beforeEach(() => { - fixture = TestBed.createComponent(IconComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); + beforeEach(() => { + fixture = TestBed.createComponent(IconComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); }); diff --git a/src/app/shared/components/icon/icon.component.ts b/src/app/shared/components/icon/icon.component.ts index d92d1d9f78..e50f894b56 100644 --- a/src/app/shared/components/icon/icon.component.ts +++ b/src/app/shared/components/icon/icon.component.ts @@ -1,10 +1,10 @@ import { ChangeDetectionStrategy, Component, Input } from '@angular/core'; @Component({ - selector: 'app-icon', - templateUrl: './icon.component.html', - changeDetection: ChangeDetectionStrategy.OnPush, + selector: 'app-icon', + templateUrl: './icon.component.html', + changeDetection: ChangeDetectionStrategy.OnPush, }) export class IconComponent { - @Input() icon = ''; + @Input() icon = ''; } diff --git a/src/app/shared/components/input-spinner/input-spinner.component.spec.ts b/src/app/shared/components/input-spinner/input-spinner.component.spec.ts index 70f0da906d..ae3c9efa91 100644 --- a/src/app/shared/components/input-spinner/input-spinner.component.spec.ts +++ b/src/app/shared/components/input-spinner/input-spinner.component.spec.ts @@ -3,22 +3,22 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { InputSpinnerComponent } from './input-spinner.component'; describe('InputSpinnerComponent', () => { - let component: InputSpinnerComponent; - let fixture: ComponentFixture; + let component: InputSpinnerComponent; + let fixture: ComponentFixture; - beforeEach(async () => { - await TestBed.configureTestingModule({ - declarations: [InputSpinnerComponent], - }).compileComponents(); - }); + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [InputSpinnerComponent], + }).compileComponents(); + }); - beforeEach(() => { - fixture = TestBed.createComponent(InputSpinnerComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); + beforeEach(() => { + fixture = TestBed.createComponent(InputSpinnerComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); }); diff --git a/src/app/shared/components/input-spinner/input-spinner.component.ts b/src/app/shared/components/input-spinner/input-spinner.component.ts index 4aeff2257c..aa16f6e1d6 100644 --- a/src/app/shared/components/input-spinner/input-spinner.component.ts +++ b/src/app/shared/components/input-spinner/input-spinner.component.ts @@ -1,10 +1,10 @@ import { Component, Input } from '@angular/core'; @Component({ - selector: 'app-input-spinner', - templateUrl: './input-spinner.component.html', - styleUrls: ['./input-spinner.component.scss'], + selector: 'app-input-spinner', + templateUrl: './input-spinner.component.html', + styleUrls: ['./input-spinner.component.scss'], }) export class InputSpinnerComponent { - @Input() isValid = ''; + @Input() isValid = ''; } diff --git a/src/app/shared/components/number-plate/number-plate.component.spec.ts b/src/app/shared/components/number-plate/number-plate.component.spec.ts index e43ef17220..4d63f16945 100644 --- a/src/app/shared/components/number-plate/number-plate.component.spec.ts +++ b/src/app/shared/components/number-plate/number-plate.component.spec.ts @@ -3,55 +3,55 @@ import { DefaultNullOrEmpty } from '@shared/pipes/default-null-or-empty/default- import { NumberPlateComponent } from './number-plate.component'; describe('NumberPlateComponent', () => { - let component: NumberPlateComponent; - let fixture: ComponentFixture; - - beforeEach(async () => { - await TestBed.configureTestingModule({ - declarations: [NumberPlateComponent, DefaultNullOrEmpty], - }).compileComponents(); - }); - - beforeEach(() => { - fixture = TestBed.createComponent(NumberPlateComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); - - it('should format a standard vrm', () => { - component.vrm = 'AA21AAA'; - expect(component.vrm).toBe('AA21 AAA'); - }); - - it('should not format a short vrm', () => { - component.vrm = 'A123'; - expect(component.vrm).toBe('A123'); - }); - - describe('isZNumber', () => { - it('should return true if it matches the z number format', () => { - component.vrm = '1234567Z'; - expect(component.isZNumber(component.vrm)).toBeTruthy(); - }); - - it('should return false if the Z is not present at the end', () => { - component.vrm = '12345'; - expect(component.isZNumber(component.vrm)).toBeFalsy(); - component.vrm = '1234Z5'; - expect(component.isZNumber(component.vrm)).toBeFalsy(); - }); - - it('should return false if the z number does not match the format', () => { - component.vrm = 'A12345Z'; - expect(component.isZNumber(component.vrm)).toBeFalsy(); - component.vrm = '12+%345Z'; - expect(component.isZNumber(component.vrm)).toBeFalsy(); - component.vrm = 'Z'; - expect(component.isZNumber(component.vrm)).toBeFalsy(); - }); - }); + let component: NumberPlateComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [NumberPlateComponent, DefaultNullOrEmpty], + }).compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(NumberPlateComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + it('should format a standard vrm', () => { + component.vrm = 'AA21AAA'; + expect(component.vrm).toBe('AA21 AAA'); + }); + + it('should not format a short vrm', () => { + component.vrm = 'A123'; + expect(component.vrm).toBe('A123'); + }); + + describe('isZNumber', () => { + it('should return true if it matches the z number format', () => { + component.vrm = '1234567Z'; + expect(component.isZNumber(component.vrm)).toBeTruthy(); + }); + + it('should return false if the Z is not present at the end', () => { + component.vrm = '12345'; + expect(component.isZNumber(component.vrm)).toBeFalsy(); + component.vrm = '1234Z5'; + expect(component.isZNumber(component.vrm)).toBeFalsy(); + }); + + it('should return false if the z number does not match the format', () => { + component.vrm = 'A12345Z'; + expect(component.isZNumber(component.vrm)).toBeFalsy(); + component.vrm = '12+%345Z'; + expect(component.isZNumber(component.vrm)).toBeFalsy(); + component.vrm = 'Z'; + expect(component.isZNumber(component.vrm)).toBeFalsy(); + }); + }); }); diff --git a/src/app/shared/components/number-plate/number-plate.component.ts b/src/app/shared/components/number-plate/number-plate.component.ts index ecab16c4fa..bfd6fbc55f 100644 --- a/src/app/shared/components/number-plate/number-plate.component.ts +++ b/src/app/shared/components/number-plate/number-plate.component.ts @@ -1,28 +1,28 @@ import { ChangeDetectionStrategy, Component, Input } from '@angular/core'; @Component({ - selector: 'app-number-plate', - templateUrl: './number-plate.component.html', - styleUrls: ['./number-plate.component.scss'], - changeDetection: ChangeDetectionStrategy.OnPush, + selector: 'app-number-plate', + templateUrl: './number-plate.component.html', + styleUrls: ['./number-plate.component.scss'], + changeDetection: ChangeDetectionStrategy.OnPush, }) export class NumberPlateComponent { - private vrmToDisplay: string | undefined; + private vrmToDisplay: string | undefined; - @Input() isSecondary = false; - @Input() set vrm(value: string | undefined) { - // formatting: if the number plate is long enough, add a space before the final 3 characters - if (value && value.length >= 5 && !this.isZNumber(value)) { - this.vrmToDisplay = `${value.slice(0, value.length - 3)} ${value.slice(value.length - 3)}`; - } else { - this.vrmToDisplay = value; - } - } - get vrm(): string | undefined { - return this.vrmToDisplay; - } + @Input() isSecondary = false; + @Input() set vrm(value: string | undefined) { + // formatting: if the number plate is long enough, add a space before the final 3 characters + if (value && value.length >= 5 && !this.isZNumber(value)) { + this.vrmToDisplay = `${value.slice(0, value.length - 3)} ${value.slice(value.length - 3)}`; + } else { + this.vrmToDisplay = value; + } + } + get vrm(): string | undefined { + return this.vrmToDisplay; + } - isZNumber(vrm: string): boolean { - return (/^[0-9]{7}[zZ]$/).test(vrm); - } + isZNumber(vrm: string): boolean { + return /^[0-9]{7}[zZ]$/.test(vrm); + } } diff --git a/src/app/shared/components/pagination/pagination.component.spec.ts b/src/app/shared/components/pagination/pagination.component.spec.ts index 17e1296a41..111c9fd0fc 100644 --- a/src/app/shared/components/pagination/pagination.component.spec.ts +++ b/src/app/shared/components/pagination/pagination.component.spec.ts @@ -1,186 +1,201 @@ import { Component, DebugElement } from '@angular/core'; -import { - ComponentFixture, fakeAsync, TestBed, tick, waitForAsync, -} from '@angular/core/testing'; +import { ComponentFixture, TestBed, fakeAsync, tick, waitForAsync } from '@angular/core/testing'; import { By } from '@angular/platform-browser'; import { ActivatedRoute, Router } from '@angular/router'; import { RouterTestingModule } from '@angular/router/testing'; -import { map, Observable } from 'rxjs'; +import { Observable, map } from 'rxjs'; import { PaginationComponent } from './pagination.component'; @Component({ - selector: 'app-host', - template: '', + selector: 'app-host', + template: '', }) class HostComponent { - tableName = 'test-pagination'; - itemsPerPage = 5; - numberOfItems = 0; - - pageQuery$: Observable; - constructor(private route: ActivatedRoute) { - this.pageQuery$ = route.queryParams.pipe(map((params) => Number.parseInt(params[`${this.tableName}-page`] ?? '1', 10))); - } + tableName = 'test-pagination'; + itemsPerPage = 5; + numberOfItems = 0; + + pageQuery$: Observable; + constructor(private route: ActivatedRoute) { + this.pageQuery$ = route.queryParams.pipe( + map((params) => Number.parseInt(params[`${this.tableName}-page`] ?? '1', 10)) + ); + } } describe('PaginationComponent', () => { - let component: PaginationComponent; - let fixture: ComponentFixture; - let hostComponent: HostComponent; - let el: DebugElement; - let router: Router; - - beforeEach(waitForAsync(async () => { - await TestBed.configureTestingModule({ - declarations: [HostComponent, PaginationComponent], - imports: [RouterTestingModule.withRoutes([{ path: '', component: PaginationComponent }])], - }).compileComponents(); - })); - - beforeEach(() => { - fixture = TestBed.createComponent(HostComponent); - hostComponent = fixture.componentInstance; - component = fixture.debugElement.query(By.directive(PaginationComponent)).componentInstance; - el = fixture.debugElement; - - router = TestBed.inject(Router); - - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); - - it.each([ - [10, 5], - [5, 10], - ])('should return an array length of %d when items per page is %d', (arrayLength: number, itemsPerPage: number) => { - hostComponent.numberOfItems = 50; - component.itemsPerPage = itemsPerPage; - fixture.detectChanges(); - expect(component.pages).toHaveLength(arrayLength); - }); - - it.each([ - [[1, 2, 3, 4, 5], 1, 50, 5], - [[2, 3, 4, 5, 6], 4, 50, 5], - [[6, 7, 8, 9, 10], 10, 50, 5], - [[4, 5, 6, 7, 8], 6, 100, 10], - [[1, 2, 3, 4], 1, 17, 5], - ])( - 'should show pages %s on page %d when number of items is %d and items per page is %d', - fakeAsync((visiblePages: Array, currentPage: number, numberOfItems: number, itemsPerPage: number) => { - hostComponent.itemsPerPage = itemsPerPage; - hostComponent.numberOfItems = numberOfItems; - - fixture.ngZone?.run(() => { - router.initialNavigation(); - router.navigate([], { queryParams: component.pageQuery(currentPage) }).catch((error) => error); - tick(); - fixture.detectChanges(); - }); - - tick(); - - expect(component.visiblePages).toEqual(visiblePages); - }), - ); - - it.each([ - { - currentPage: 1, itemsPerPage: 5, start: 0, end: 5, - }, - { - currentPage: 2, itemsPerPage: 5, start: 5, end: 10, - }, - { - currentPage: 3, itemsPerPage: 15, start: 30, end: 45, - }, - ])( - 'should emit %p', - ({ - currentPage, itemsPerPage, start, end, - }: { currentPage: number; itemsPerPage: number; start: number; end: number }, done) => { - component.paginationOptions.subscribe((opts) => { - expect(opts.currentPage).toBe(currentPage); - expect(opts.start).toBe(start); - expect(opts.end).toBe(end); - done(); - }); - - component.itemsPerPage = itemsPerPage; - component.currentPageSubject.next(currentPage); - }, - ); - - describe('nextPage', () => { - it('should go page 1 to 2', fakeAsync(() => { - hostComponent.numberOfItems = 50; - fixture.detectChanges(); - - fixture.ngZone?.run(() => { - router.initialNavigation(); - }); - - const next: HTMLLinkElement = el.query(By.css(`#${component.tableName}-next-page`)).nativeElement; - next.click(); - - tick(); - fixture.detectChanges(); - - expect(router.url).toBe(`/?${component.tableName}-page=2`); - })); - - it('should not render "next" link when already on last page', fakeAsync(() => { - hostComponent.numberOfItems = 50; - fixture.detectChanges(); - - fixture.ngZone?.run(() => { - router.initialNavigation(); - router.navigate([], { queryParams: component.pageQuery(10) }).catch((error) => error); - tick(); - fixture.detectChanges(); - }); - - const next = el.query(By.css(`#${component.tableName}-next-page`)); - - expect(next).toBeNull(); - })); - }); - - describe('prevPage', () => { - it('should go from page 4 to 3', fakeAsync(() => { - hostComponent.numberOfItems = 50; - fixture.detectChanges(); - - fixture.ngZone?.run(() => { - router.initialNavigation(); - router.navigate([], { queryParams: component.pageQuery(4) }).catch((error) => error); - tick(); - fixture.detectChanges(); - }); - - const prev: HTMLLinkElement = el.query(By.css(`#${component.tableName}-prev-page`)).nativeElement; - prev.click(); - - tick(); - fixture.detectChanges(); - - expect(router.url).toBe(`/?${component.tableName}-page=3`); - })); - - it('should not render "prev" link when already on first page', fakeAsync(() => { - hostComponent.numberOfItems = 50; - fixture.detectChanges(); - - fixture.ngZone?.run(() => { - router.initialNavigation(); - }); - - const prev = el.query(By.css(`#${component.tableName}-prev-page`)); - - expect(prev).toBeNull(); - })); - }); + let component: PaginationComponent; + let fixture: ComponentFixture; + let hostComponent: HostComponent; + let el: DebugElement; + let router: Router; + + beforeEach(waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [HostComponent, PaginationComponent], + imports: [RouterTestingModule.withRoutes([{ path: '', component: PaginationComponent }])], + }).compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(HostComponent); + hostComponent = fixture.componentInstance; + component = fixture.debugElement.query(By.directive(PaginationComponent)).componentInstance; + el = fixture.debugElement; + + router = TestBed.inject(Router); + + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + it.each([ + [10, 5], + [5, 10], + ])('should return an array length of %d when items per page is %d', (arrayLength: number, itemsPerPage: number) => { + hostComponent.numberOfItems = 50; + component.itemsPerPage = itemsPerPage; + fixture.detectChanges(); + expect(component.pages).toHaveLength(arrayLength); + }); + + it.each([ + [[1, 2, 3, 4, 5], 1, 50, 5], + [[2, 3, 4, 5, 6], 4, 50, 5], + [[6, 7, 8, 9, 10], 10, 50, 5], + [[4, 5, 6, 7, 8], 6, 100, 10], + [[1, 2, 3, 4], 1, 17, 5], + ])( + 'should show pages %s on page %d when number of items is %d and items per page is %d', + fakeAsync((visiblePages: Array, currentPage: number, numberOfItems: number, itemsPerPage: number) => { + hostComponent.itemsPerPage = itemsPerPage; + hostComponent.numberOfItems = numberOfItems; + + fixture.ngZone?.run(() => { + router.initialNavigation(); + router.navigate([], { queryParams: component.pageQuery(currentPage) }).catch((error) => error); + tick(); + fixture.detectChanges(); + }); + + tick(); + + expect(component.visiblePages).toEqual(visiblePages); + }) + ); + + it.each([ + { + currentPage: 1, + itemsPerPage: 5, + start: 0, + end: 5, + }, + { + currentPage: 2, + itemsPerPage: 5, + start: 5, + end: 10, + }, + { + currentPage: 3, + itemsPerPage: 15, + start: 30, + end: 45, + }, + ])( + 'should emit %p', + ( + { + currentPage, + itemsPerPage, + start, + end, + }: { currentPage: number; itemsPerPage: number; start: number; end: number }, + done + ) => { + component.paginationOptions.subscribe((opts) => { + expect(opts.currentPage).toBe(currentPage); + expect(opts.start).toBe(start); + expect(opts.end).toBe(end); + done(); + }); + + component.itemsPerPage = itemsPerPage; + component.currentPageSubject.next(currentPage); + } + ); + + describe('nextPage', () => { + it('should go page 1 to 2', fakeAsync(() => { + hostComponent.numberOfItems = 50; + fixture.detectChanges(); + + fixture.ngZone?.run(() => { + router.initialNavigation(); + }); + + const next: HTMLLinkElement = el.query(By.css(`#${component.tableName}-next-page`)).nativeElement; + next.click(); + + tick(); + fixture.detectChanges(); + + expect(router.url).toBe(`/?${component.tableName}-page=2`); + })); + + it('should not render "next" link when already on last page', fakeAsync(() => { + hostComponent.numberOfItems = 50; + fixture.detectChanges(); + + fixture.ngZone?.run(() => { + router.initialNavigation(); + router.navigate([], { queryParams: component.pageQuery(10) }).catch((error) => error); + tick(); + fixture.detectChanges(); + }); + + const next = el.query(By.css(`#${component.tableName}-next-page`)); + + expect(next).toBeNull(); + })); + }); + + describe('prevPage', () => { + it('should go from page 4 to 3', fakeAsync(() => { + hostComponent.numberOfItems = 50; + fixture.detectChanges(); + + fixture.ngZone?.run(() => { + router.initialNavigation(); + router.navigate([], { queryParams: component.pageQuery(4) }).catch((error) => error); + tick(); + fixture.detectChanges(); + }); + + const prev: HTMLLinkElement = el.query(By.css(`#${component.tableName}-prev-page`)).nativeElement; + prev.click(); + + tick(); + fixture.detectChanges(); + + expect(router.url).toBe(`/?${component.tableName}-page=3`); + })); + + it('should not render "prev" link when already on first page', fakeAsync(() => { + hostComponent.numberOfItems = 50; + fixture.detectChanges(); + + fixture.ngZone?.run(() => { + router.initialNavigation(); + }); + + const prev = el.query(By.css(`#${component.tableName}-prev-page`)); + + expect(prev).toBeNull(); + })); + }); }); diff --git a/src/app/shared/components/pagination/pagination.component.ts b/src/app/shared/components/pagination/pagination.component.ts index abbbf321b3..6c46be3403 100644 --- a/src/app/shared/components/pagination/pagination.component.ts +++ b/src/app/shared/components/pagination/pagination.component.ts @@ -1,96 +1,112 @@ import { - ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, Input, OnDestroy, OnInit, Output, + ChangeDetectionStrategy, + ChangeDetectorRef, + Component, + EventEmitter, + Input, + OnDestroy, + OnInit, + Output, } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; -import { - ReplaySubject, Subject, map, takeUntil, -} from 'rxjs'; +import { ReplaySubject, Subject, map, takeUntil } from 'rxjs'; @Component({ - selector: 'app-pagination[tableName]', - templateUrl: './pagination.component.html', - styleUrls: ['./pagination.component.scss'], - changeDetection: ChangeDetectionStrategy.OnPush, + selector: 'app-pagination[tableName]', + templateUrl: './pagination.component.html', + styleUrls: ['./pagination.component.scss'], + changeDetection: ChangeDetectionStrategy.OnPush, }) export class PaginationComponent implements OnInit, OnDestroy { - @Input() tableName!: string; - @Input() numberOfItems = 0; - @Input() itemsPerPage = 5; - @Output() paginationOptions = new EventEmitter<{ currentPage: number; itemsPerPage: number; start: number; end: number }>(); + @Input() tableName!: string; + @Input() numberOfItems = 0; + @Input() itemsPerPage = 5; + @Output() paginationOptions = new EventEmitter<{ + currentPage: number; + itemsPerPage: number; + start: number; + end: number; + }>(); - currentPage = 1; - currentPageSubject = new ReplaySubject(this.currentPage); - numberOfVisiblePages = 5; - _pages?: Array; + currentPage = 1; + currentPageSubject = new ReplaySubject(this.currentPage); + numberOfVisiblePages = 5; + _pages?: Array; - private destroy$ = new Subject(); + private destroy$ = new Subject(); - constructor(private route: ActivatedRoute, private cdr: ChangeDetectorRef) {} + constructor( + private route: ActivatedRoute, + private cdr: ChangeDetectorRef + ) {} - ngOnInit(): void { - this.route.queryParams - .pipe( - takeUntil(this.destroy$), - map((params) => Number.parseInt(params[`${this.tableName}-page`] ?? '1', 10)), - ) - .subscribe({ - next: (page) => { - this.currentPageSubject.next(page); - this.cdr.markForCheck(); - }, - }); + ngOnInit(): void { + this.route.queryParams + .pipe( + takeUntil(this.destroy$), + map((params) => Number.parseInt(params[`${this.tableName}-page`] ?? '1', 10)) + ) + .subscribe({ + next: (page) => { + this.currentPageSubject.next(page); + this.cdr.markForCheck(); + }, + }); - this.currentPageSubject.pipe(takeUntil(this.destroy$)).subscribe({ - next: (page) => { - const [start, end] = [(page - 1) * this.itemsPerPage, page * this.itemsPerPage]; + this.currentPageSubject.pipe(takeUntil(this.destroy$)).subscribe({ + next: (page) => { + const [start, end] = [(page - 1) * this.itemsPerPage, page * this.itemsPerPage]; - this.currentPage = page; - this.paginationOptions.emit({ - currentPage: page, itemsPerPage: this.itemsPerPage, start, end, - }); - this.cdr.markForCheck(); - }, - }); - } + this.currentPage = page; + this.paginationOptions.emit({ + currentPage: page, + itemsPerPage: this.itemsPerPage, + start, + end, + }); + this.cdr.markForCheck(); + }, + }); + } - ngOnDestroy(): void { - this.destroy$.next(); - this.destroy$.complete(); - } + ngOnDestroy(): void { + this.destroy$.next(); + this.destroy$.complete(); + } - pageQuery(page: number) { - return { [`${this.tableName}-page`]: page }; - } - nextPage() { - return this.pageQuery(this.currentPage + 1); - } - prevPage() { - return this.pageQuery(this.currentPage - 1); - } + pageQuery(page: number) { + return { [`${this.tableName}-page`]: page }; + } + nextPage() { + return this.pageQuery(this.currentPage + 1); + } + prevPage() { + return this.pageQuery(this.currentPage - 1); + } - trackByFn(index: number, page: number) { - return page || index; - } + trackByFn(index: number, page: number) { + return page || index; + } - get pages() { - return Array(this.numberOfPages) - .fill('') - .map((x, i) => i + 1); - } + get pages() { + return Array(this.numberOfPages) + .fill('') + .map((x, i) => i + 1); + } - get numberOfPages() { - return Math.ceil(this.numberOfItems / this.itemsPerPage); - } + get numberOfPages() { + return Math.ceil(this.numberOfItems / this.itemsPerPage); + } - /** - * Returns array of visible page buttons. - * Allways returns an odd number of pages while keeping current page in the middle. - */ - get visiblePages() { - const range = (num: number, min: number, max: number) => Math.min(Math.max(num, min), max); - const middle = Math.ceil(this.numberOfVisiblePages / 2); - const clampedPage = range(this.currentPage, middle, this.pages.length - (middle - 1)); + /** + * Returns array of visible page buttons. + * Allways returns an odd number of pages while keeping current page in the middle. + */ + get visiblePages() { + const range = (num: number, min: number, max: number) => Math.min(Math.max(num, min), max); + const middle = Math.ceil(this.numberOfVisiblePages / 2); + const clampedPage = range(this.currentPage, middle, this.pages.length - (middle - 1)); - return this.pages.slice(Math.max(clampedPage - middle, 0), clampedPage + middle - 1); - } + return this.pages.slice(Math.max(clampedPage - middle, 0), clampedPage + middle - 1); + } } diff --git a/src/app/shared/components/router-outlet/router-outlet.component.spec.ts b/src/app/shared/components/router-outlet/router-outlet.component.spec.ts index d04dacffa0..807d44da2d 100644 --- a/src/app/shared/components/router-outlet/router-outlet.component.spec.ts +++ b/src/app/shared/components/router-outlet/router-outlet.component.spec.ts @@ -3,23 +3,23 @@ import { RouterTestingModule } from '@angular/router/testing'; import { RouterOutletComponent } from './router-outlet.component'; describe('RouterOutletComponent', () => { - let component: RouterOutletComponent; - let fixture: ComponentFixture; + let component: RouterOutletComponent; + let fixture: ComponentFixture; - beforeEach(async () => { - await TestBed.configureTestingModule({ - declarations: [RouterOutletComponent], - imports: [RouterTestingModule], - }).compileComponents(); - }); + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [RouterOutletComponent], + imports: [RouterTestingModule], + }).compileComponents(); + }); - beforeEach(() => { - fixture = TestBed.createComponent(RouterOutletComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); + beforeEach(() => { + fixture = TestBed.createComponent(RouterOutletComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); }); diff --git a/src/app/shared/components/router-outlet/router-outlet.component.ts b/src/app/shared/components/router-outlet/router-outlet.component.ts index 02676774da..1f41ee3f94 100644 --- a/src/app/shared/components/router-outlet/router-outlet.component.ts +++ b/src/app/shared/components/router-outlet/router-outlet.component.ts @@ -1,7 +1,7 @@ import { Component } from '@angular/core'; @Component({ - selector: 'app-router-outlet', - templateUrl: './router-outlet.component.html', + selector: 'app-router-outlet', + templateUrl: './router-outlet.component.html', }) export class RouterOutletComponent {} diff --git a/src/app/shared/components/tag/tag.component.spec.ts b/src/app/shared/components/tag/tag.component.spec.ts index 4714994f1f..5536e01837 100644 --- a/src/app/shared/components/tag/tag.component.spec.ts +++ b/src/app/shared/components/tag/tag.component.spec.ts @@ -2,22 +2,22 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { TagComponent } from './tag.component'; describe('TagComponent', () => { - let component: TagComponent; - let fixture: ComponentFixture; + let component: TagComponent; + let fixture: ComponentFixture; - beforeEach(async () => { - await TestBed.configureTestingModule({ - declarations: [TagComponent], - }).compileComponents(); - }); + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [TagComponent], + }).compileComponents(); + }); - beforeEach(() => { - fixture = TestBed.createComponent(TagComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); + beforeEach(() => { + fixture = TestBed.createComponent(TagComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it('should create', () => { + expect(component).toBeTruthy(); + }); }); diff --git a/src/app/shared/components/tag/tag.component.ts b/src/app/shared/components/tag/tag.component.ts index 350dde0a3a..c77dd93cfb 100644 --- a/src/app/shared/components/tag/tag.component.ts +++ b/src/app/shared/components/tag/tag.component.ts @@ -2,19 +2,19 @@ import { ChangeDetectionStrategy, Component, Input } from '@angular/core'; export type TagTypes = 'blue' | 'green' | 'orange' | 'red' | 'yellow' | 'purple'; export const TagType = { - BLUE: 'blue' as TagTypes, - GREEN: 'green' as TagTypes, - ORANGE: 'orange' as TagTypes, - RED: 'red' as TagTypes, - YELLOW: 'yellow' as TagTypes, - PURPLE: 'purple' as TagTypes, + BLUE: 'blue' as TagTypes, + GREEN: 'green' as TagTypes, + ORANGE: 'orange' as TagTypes, + RED: 'red' as TagTypes, + YELLOW: 'yellow' as TagTypes, + PURPLE: 'purple' as TagTypes, }; @Component({ - selector: 'app-tag', - templateUrl: './tag.component.html', - changeDetection: ChangeDetectionStrategy.OnPush, + selector: 'app-tag', + templateUrl: './tag.component.html', + changeDetection: ChangeDetectionStrategy.OnPush, }) export class TagComponent { - @Input() type: string = TagType.BLUE; + @Input() type: string = TagType.BLUE; } diff --git a/src/app/shared/components/test-certificate/test-certificate.component.spec.ts b/src/app/shared/components/test-certificate/test-certificate.component.spec.ts index ac6d76b9b5..9414691733 100644 --- a/src/app/shared/components/test-certificate/test-certificate.component.spec.ts +++ b/src/app/shared/components/test-certificate/test-certificate.component.spec.ts @@ -1,102 +1,114 @@ import { HttpClientTestingModule } from '@angular/common/http/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing'; import { DocumentRetrievalService } from '@api/document-retrieval'; +import { TestResultModel } from '@models/test-results/test-result.model'; +import { resultOfTestEnum } from '@models/test-types/test-type.model'; import { MockStore, provideMockStore } from '@ngrx/store/testing'; +import { FeatureToggleService } from '@services/feature-toggle-service/feature-toggle-service'; import { RetrieveDocumentDirective } from '@shared/directives/retrieve-document/retrieve-document.directive'; -import { initialAppState, State } from '@store/index'; +import { State, initialAppState } from '@store/index'; import { isTestTypeOldIvaOrMsva, toEditOrNotToEdit } from '@store/test-records'; -import { resultOfTestEnum } from '@models/test-types/test-type.model'; -import { TestResultModel } from '@models/test-results/test-result.model'; -import { FeatureToggleService } from '@services/feature-toggle-service/feature-toggle-service'; import { TestCertificateComponent } from './test-certificate.component'; describe('TestCertificateComponent', () => { - let component: TestCertificateComponent; - let fixture: ComponentFixture; - let store: MockStore; - let featureToggleService: FeatureToggleService; - - beforeEach(async () => { - await TestBed.configureTestingModule({ - declarations: [RetrieveDocumentDirective, TestCertificateComponent], - imports: [HttpClientTestingModule], - providers: [DocumentRetrievalService, provideMockStore({ initialState: initialAppState }), FeatureToggleService], - }).compileComponents(); - }); - - beforeEach(() => { - fixture = TestBed.createComponent(TestCertificateComponent); - component = fixture.componentInstance; - store = TestBed.inject(MockStore); - featureToggleService = TestBed.inject(FeatureToggleService); - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); - - describe('ngOnInit', () => { - beforeEach(() => { - jest.spyOn(featureToggleService, 'isFeatureEnabled').mockReturnValue(true); - }); - it('should set certNotNeeded to false if test type is not an iva or msva test', () => { - store.overrideSelector(toEditOrNotToEdit, { testTypes: [{ testResult: resultOfTestEnum.pass, testTypeId: '94' }] } as TestResultModel); - store.overrideSelector(isTestTypeOldIvaOrMsva, false); - component.ngOnInit(); - - expect(component.certNotNeeded).toBe(false); - }); - - it('should set certNotNeeded to true if test type is an old iva or msva test', () => { - store.overrideSelector(toEditOrNotToEdit, { testTypes: [{ testResult: resultOfTestEnum.pass, testTypeId: '125' }] } as TestResultModel); - store.overrideSelector(isTestTypeOldIvaOrMsva, true); - component.ngOnInit(); - - expect(component.certNotNeeded).toBe(true); - }); - - it('should set certNotNeeded to true if test type is an new iva or msva test and the test is a pass', () => { - store.overrideSelector(toEditOrNotToEdit, { testTypes: [{ testResult: resultOfTestEnum.pass, testTypeId: '125' }] } as TestResultModel); - store.overrideSelector(isTestTypeOldIvaOrMsva, false); - component.ngOnInit(); - - expect(component.certNotNeeded).toBe(true); - }); - - it('should set certNotNeeded to true if test type is an new iva or msva test and the test is a prs', () => { - store.overrideSelector(toEditOrNotToEdit, { testTypes: [{ testResult: resultOfTestEnum.prs, testTypeId: '125' }] } as TestResultModel); - store.overrideSelector(isTestTypeOldIvaOrMsva, false); - component.ngOnInit(); - - expect(component.certNotNeeded).toBe(true); - }); - - it('should set certNotNeeded to false if test type is an new iva or msva test and the test is a fail', () => { - store.overrideSelector(toEditOrNotToEdit, { testTypes: [{ testResult: resultOfTestEnum.fail, testTypeId: '125' }] } as TestResultModel); - store.overrideSelector(isTestTypeOldIvaOrMsva, false); - component.ngOnInit(); - - expect(component.certNotNeeded).toBe(false); - }); - - it('should not change certNotNeeded value if no test result is found', () => { - component.certNotNeeded = false; - store.overrideSelector(toEditOrNotToEdit, undefined); - store.overrideSelector(isTestTypeOldIvaOrMsva, true); - component.ngOnInit(); - - expect(component.certNotNeeded).toBe(false); - }); - - it('should not certNotNeeded value if requiredStandards feature is false', () => { - component.certNotNeeded = false; - jest.spyOn(featureToggleService, 'isFeatureEnabled').mockReturnValue(false); - store.overrideSelector(toEditOrNotToEdit, { testTypes: [{ testResult: resultOfTestEnum.pass, testTypeId: '125' }] } as TestResultModel); - store.overrideSelector(isTestTypeOldIvaOrMsva, true); - component.ngOnInit(); - - expect(component.certNotNeeded).toBe(false); - }); - }); + let component: TestCertificateComponent; + let fixture: ComponentFixture; + let store: MockStore; + let featureToggleService: FeatureToggleService; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [RetrieveDocumentDirective, TestCertificateComponent], + imports: [HttpClientTestingModule], + providers: [DocumentRetrievalService, provideMockStore({ initialState: initialAppState }), FeatureToggleService], + }).compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(TestCertificateComponent); + component = fixture.componentInstance; + store = TestBed.inject(MockStore); + featureToggleService = TestBed.inject(FeatureToggleService); + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + describe('ngOnInit', () => { + beforeEach(() => { + jest.spyOn(featureToggleService, 'isFeatureEnabled').mockReturnValue(true); + }); + it('should set certNotNeeded to false if test type is not an iva or msva test', () => { + store.overrideSelector(toEditOrNotToEdit, { + testTypes: [{ testResult: resultOfTestEnum.pass, testTypeId: '94' }], + } as TestResultModel); + store.overrideSelector(isTestTypeOldIvaOrMsva, false); + component.ngOnInit(); + + expect(component.certNotNeeded).toBe(false); + }); + + it('should set certNotNeeded to true if test type is an old iva or msva test', () => { + store.overrideSelector(toEditOrNotToEdit, { + testTypes: [{ testResult: resultOfTestEnum.pass, testTypeId: '125' }], + } as TestResultModel); + store.overrideSelector(isTestTypeOldIvaOrMsva, true); + component.ngOnInit(); + + expect(component.certNotNeeded).toBe(true); + }); + + it('should set certNotNeeded to true if test type is an new iva or msva test and the test is a pass', () => { + store.overrideSelector(toEditOrNotToEdit, { + testTypes: [{ testResult: resultOfTestEnum.pass, testTypeId: '125' }], + } as TestResultModel); + store.overrideSelector(isTestTypeOldIvaOrMsva, false); + component.ngOnInit(); + + expect(component.certNotNeeded).toBe(true); + }); + + it('should set certNotNeeded to true if test type is an new iva or msva test and the test is a prs', () => { + store.overrideSelector(toEditOrNotToEdit, { + testTypes: [{ testResult: resultOfTestEnum.prs, testTypeId: '125' }], + } as TestResultModel); + store.overrideSelector(isTestTypeOldIvaOrMsva, false); + component.ngOnInit(); + + expect(component.certNotNeeded).toBe(true); + }); + + it('should set certNotNeeded to false if test type is an new iva or msva test and the test is a fail', () => { + store.overrideSelector(toEditOrNotToEdit, { + testTypes: [{ testResult: resultOfTestEnum.fail, testTypeId: '125' }], + } as TestResultModel); + store.overrideSelector(isTestTypeOldIvaOrMsva, false); + component.ngOnInit(); + + expect(component.certNotNeeded).toBe(false); + }); + + it('should not change certNotNeeded value if no test result is found', () => { + component.certNotNeeded = false; + store.overrideSelector(toEditOrNotToEdit, undefined); + store.overrideSelector(isTestTypeOldIvaOrMsva, true); + component.ngOnInit(); + + expect(component.certNotNeeded).toBe(false); + }); + + it('should not certNotNeeded value if requiredStandards feature is false', () => { + component.certNotNeeded = false; + jest.spyOn(featureToggleService, 'isFeatureEnabled').mockReturnValue(false); + store.overrideSelector(toEditOrNotToEdit, { + testTypes: [{ testResult: resultOfTestEnum.pass, testTypeId: '125' }], + } as TestResultModel); + store.overrideSelector(isTestTypeOldIvaOrMsva, true); + component.ngOnInit(); + + expect(component.certNotNeeded).toBe(false); + }); + }); }); diff --git a/src/app/shared/components/test-certificate/test-certificate.component.ts b/src/app/shared/components/test-certificate/test-certificate.component.ts index f9371066a2..add1f5beca 100644 --- a/src/app/shared/components/test-certificate/test-certificate.component.ts +++ b/src/app/shared/components/test-certificate/test-certificate.component.ts @@ -1,60 +1,53 @@ -import { - ChangeDetectionStrategy, Component, inject, Input, OnDestroy, OnInit, -} from '@angular/core'; -import { select, Store } from '@ngrx/store'; -import { isTestTypeOldIvaOrMsva, toEditOrNotToEdit } from '@store/test-records'; -import { - Subject, takeUntil, combineLatest, -} from 'rxjs'; -import { State } from '@store/index'; -import { resultOfTestEnum } from '@models/test-types/test-type.model'; +import { ChangeDetectionStrategy, Component, Input, OnDestroy, OnInit, inject } from '@angular/core'; import { TEST_TYPES_GROUP1_SPEC_TEST, TEST_TYPES_GROUP5_SPEC_TEST } from '@forms/models/testTypeId.enum'; +import { resultOfTestEnum } from '@models/test-types/test-type.model'; +import { Store, select } from '@ngrx/store'; import { FeatureToggleService } from '@services/feature-toggle-service/feature-toggle-service'; +import { State } from '@store/index'; +import { isTestTypeOldIvaOrMsva, toEditOrNotToEdit } from '@store/test-records'; +import { Subject, combineLatest, takeUntil } from 'rxjs'; @Component({ - selector: 'app-test-certificate[testNumber][vin]', - templateUrl: './test-certificate.component.html', - styleUrls: ['./test-certificate.component.scss'], - changeDetection: ChangeDetectionStrategy.OnPush, + selector: 'app-test-certificate[testNumber][vin]', + templateUrl: './test-certificate.component.html', + styleUrls: ['./test-certificate.component.scss'], + changeDetection: ChangeDetectionStrategy.OnPush, }) export class TestCertificateComponent implements OnInit, OnDestroy { - store: Store = inject(Store); - featureToggleService = inject(FeatureToggleService); - @Input() testNumber!: string; - @Input() vin!: string; - @Input() isClickable = true; - certNotNeeded: boolean = false; - private destroyed$ = new Subject(); + store: Store = inject(Store); + featureToggleService = inject(FeatureToggleService); + @Input() testNumber!: string; + @Input() vin!: string; + @Input() isClickable = true; + certNotNeeded = false; + private destroyed$ = new Subject(); - ngOnInit(): void { - const isRequiredStandardsEnabled = this.featureToggleService.isFeatureEnabled('requiredStandards'); - combineLatest([ - this.store.pipe(select(toEditOrNotToEdit)), - this.store.pipe(select(isTestTypeOldIvaOrMsva)), - ]).pipe( - takeUntil(this.destroyed$), - ).subscribe(([testResult, isOldIvaOrMsva]) => { - if (testResult && isRequiredStandardsEnabled) { - const { testResult: result, testTypeId: id } = testResult.testTypes[0]; - const isIvaOrMsvaTest = TEST_TYPES_GROUP1_SPEC_TEST.includes(id) || TEST_TYPES_GROUP5_SPEC_TEST.includes(id); - this.certNotNeeded = isOldIvaOrMsva || (isIvaOrMsvaTest && result !== resultOfTestEnum.fail); - } - }); - } + ngOnInit(): void { + const isRequiredStandardsEnabled = this.featureToggleService.isFeatureEnabled('requiredStandards'); + combineLatest([this.store.pipe(select(toEditOrNotToEdit)), this.store.pipe(select(isTestTypeOldIvaOrMsva))]) + .pipe(takeUntil(this.destroyed$)) + .subscribe(([testResult, isOldIvaOrMsva]) => { + if (testResult && isRequiredStandardsEnabled) { + const { testResult: result, testTypeId: id } = testResult.testTypes[0]; + const isIvaOrMsvaTest = TEST_TYPES_GROUP1_SPEC_TEST.includes(id) || TEST_TYPES_GROUP5_SPEC_TEST.includes(id); + this.certNotNeeded = isOldIvaOrMsva || (isIvaOrMsvaTest && result !== resultOfTestEnum.fail); + } + }); + } - get documentParams(): Map { - return new Map([ - ['testNumber', this.testNumber], - ['vinNumber', this.vin], - ]); - } + get documentParams(): Map { + return new Map([ + ['testNumber', this.testNumber], + ['vinNumber', this.vin], + ]); + } - get fileName(): string { - return `${this.testNumber}_${this.vin}`; - } + get fileName(): string { + return `${this.testNumber}_${this.vin}`; + } - ngOnDestroy(): void { - this.destroyed$.next(); - this.destroyed$.complete(); - } + ngOnDestroy(): void { + this.destroyed$.next(); + this.destroyed$.complete(); + } } diff --git a/src/app/shared/custom-module/fixNgZoneError.ts b/src/app/shared/custom-module/fixNgZoneError.ts index 437da7e5cc..1f6e9e5ce3 100644 --- a/src/app/shared/custom-module/fixNgZoneError.ts +++ b/src/app/shared/custom-module/fixNgZoneError.ts @@ -8,6 +8,6 @@ import { Router } from '@angular/router'; */ @NgModule() export class FixNavigationTriggeredOutsideAngularZoneNgModule { - // eslint-disable-next-line @typescript-eslint/no-useless-constructor, @typescript-eslint/no-empty-function, @typescript-eslint/no-unused-vars - constructor(_router: Router) { } + // eslint-disable-next-line @typescript-eslint/no-useless-constructor, @typescript-eslint/no-empty-function, @typescript-eslint/no-unused-vars + constructor(_router: Router) {} } diff --git a/src/app/shared/directives/prevent-double-click/prevent-double-click.directive.spec.ts b/src/app/shared/directives/prevent-double-click/prevent-double-click.directive.spec.ts index 37ab08dde9..d15bbc3b07 100644 --- a/src/app/shared/directives/prevent-double-click/prevent-double-click.directive.spec.ts +++ b/src/app/shared/directives/prevent-double-click/prevent-double-click.directive.spec.ts @@ -4,55 +4,55 @@ import { By } from '@angular/platform-browser'; import { PreventDoubleClickDirective } from './prevent-double-click.directive'; @Component({ - selector: 'app-host', - template: '', + selector: 'app-host', + template: '', }) class HostComponent { - clicked = new EventEmitter(); + clicked = new EventEmitter(); } describe('PreventDoubleClickDirective', () => { - let component: HostComponent; - let fixture: ComponentFixture; + let component: HostComponent; + let fixture: ComponentFixture; - beforeEach(waitForAsync(async () => { - await TestBed.configureTestingModule({ - declarations: [HostComponent, PreventDoubleClickDirective], - }).compileComponents(); - })); + beforeEach(waitForAsync(async () => { + await TestBed.configureTestingModule({ + declarations: [HostComponent, PreventDoubleClickDirective], + }).compileComponents(); + })); - beforeEach(() => { - fixture = TestBed.createComponent(HostComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); + beforeEach(() => { + fixture = TestBed.createComponent(HostComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); - it('should emit clicked once', () => { - const emitSpy = jest.spyOn(component.clicked, 'emit'); - const button: HTMLButtonElement = fixture.debugElement.query(By.css('button')).nativeElement; + it('should emit clicked once', () => { + const emitSpy = jest.spyOn(component.clicked, 'emit'); + const button: HTMLButtonElement = fixture.debugElement.query(By.css('button')).nativeElement; - button.click(); - button.click(); + button.click(); + button.click(); - expect(emitSpy).toHaveBeenCalledTimes(1); - }); + expect(emitSpy).toHaveBeenCalledTimes(1); + }); - it('should emit clicked twice', async () => { - const emitSpy = jest.spyOn(component.clicked, 'emit'); - const button: HTMLButtonElement = fixture.debugElement.query(By.css('button')).nativeElement; + it('should emit clicked twice', async () => { + const emitSpy = jest.spyOn(component.clicked, 'emit'); + const button: HTMLButtonElement = fixture.debugElement.query(By.css('button')).nativeElement; - button.click(); + button.click(); - await new Promise((resolve) => { - const wait = setTimeout(() => { - clearTimeout(wait); - button.click(); - resolve(); - }, 1001); - }); + await new Promise((resolve) => { + const wait = setTimeout(() => { + clearTimeout(wait); + button.click(); + resolve(); + }, 1001); + }); - button.click(); + button.click(); - expect(emitSpy).toHaveBeenCalledTimes(2); - }); + expect(emitSpy).toHaveBeenCalledTimes(2); + }); }); diff --git a/src/app/shared/directives/prevent-double-click/prevent-double-click.directive.ts b/src/app/shared/directives/prevent-double-click/prevent-double-click.directive.ts index b399a3a587..349d8eff07 100644 --- a/src/app/shared/directives/prevent-double-click/prevent-double-click.directive.ts +++ b/src/app/shared/directives/prevent-double-click/prevent-double-click.directive.ts @@ -1,37 +1,37 @@ -import { - Directive, EventEmitter, HostListener, Input, OnDestroy, Output, -} from '@angular/core'; +import { Directive, EventEmitter, HostListener, Input, OnDestroy, Output } from '@angular/core'; import { Subject, Subscription, throttleTime } from 'rxjs'; @Directive({ - selector: '[appPreventDoubleClick]', + selector: '[appPreventDoubleClick]', }) export class PreventDoubleClickDirective implements OnDestroy { - @Input() - throttleTime = 1000; + @Input() + throttleTime = 1000; - @Output() - clicked = new EventEmitter(); + @Output() + clicked = new EventEmitter(); - private clicks = new Subject(); - private subscription: Subscription; + private clicks = new Subject(); + private subscription: Subscription; - constructor() { - this.subscription = this.clicks.pipe(throttleTime(this.throttleTime)).subscribe((e: PointerEvent) => this.emitThrottledClick(e)); - } + constructor() { + this.subscription = this.clicks + .pipe(throttleTime(this.throttleTime)) + .subscribe((e: PointerEvent) => this.emitThrottledClick(e)); + } - emitThrottledClick(e: PointerEvent) { - this.clicked.emit(e); - } + emitThrottledClick(e: PointerEvent) { + this.clicked.emit(e); + } - ngOnDestroy() { - this.subscription.unsubscribe(); - } + ngOnDestroy() { + this.subscription.unsubscribe(); + } - @HostListener('click', ['$event']) - clickEvent(event: PointerEvent) { - event.preventDefault(); - event.stopPropagation(); - this.clicks.next(event); - } + @HostListener('click', ['$event']) + clickEvent(event: PointerEvent) { + event.preventDefault(); + event.stopPropagation(); + this.clicks.next(event); + } } diff --git a/src/app/shared/directives/retrieve-document/retrieve-document.directive.spec.ts b/src/app/shared/directives/retrieve-document/retrieve-document.directive.spec.ts index a9984cc35d..49f42f393c 100644 --- a/src/app/shared/directives/retrieve-document/retrieve-document.directive.spec.ts +++ b/src/app/shared/directives/retrieve-document/retrieve-document.directive.spec.ts @@ -7,24 +7,18 @@ import { State, initialAppState } from '@store/index'; import { RetrieveDocumentDirective } from './retrieve-document.directive'; describe('RetrieveDocumentDirective', () => { - let store: MockStore; - beforeEach(() => { - TestBed.configureTestingModule({ - providers: [ - provideMockStore({ initialState: initialAppState }), - ], - }); - }); - it('should create an instance', () => { - const directive = new RetrieveDocumentDirective( - new DocumentRetrievalService( - {} as HttpClient, - '', - new Configuration(), - ), - new DocumentsService(), - store, - ); - expect(directive).toBeTruthy(); - }); + let store: MockStore; + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [provideMockStore({ initialState: initialAppState })], + }); + }); + it('should create an instance', () => { + const directive = new RetrieveDocumentDirective( + new DocumentRetrievalService({} as HttpClient, '', new Configuration()), + new DocumentsService(), + store + ); + expect(directive).toBeTruthy(); + }); }); diff --git a/src/app/shared/directives/retrieve-document/retrieve-document.directive.ts b/src/app/shared/directives/retrieve-document/retrieve-document.directive.ts index 67222eff74..6e6252f61a 100644 --- a/src/app/shared/directives/retrieve-document/retrieve-document.directive.ts +++ b/src/app/shared/directives/retrieve-document/retrieve-document.directive.ts @@ -9,37 +9,41 @@ import { takeWhile } from 'rxjs'; @Directive({ selector: '[appRetrieveDocument][params][fileName]' }) export class RetrieveDocumentDirective { - @Input() params: Map = new Map(); - @Input() fileName = ''; - @Input() loading?: Boolean; - @Input() certNotNeeded: boolean = false; - @Input() fileType: string = 'pdf'; + @Input() params: Map = new Map(); + @Input() fileName = ''; + @Input() loading?: boolean; + @Input() certNotNeeded = false; + @Input() fileType = 'pdf'; - constructor(private documentRetrievalService: DocumentRetrievalService, private documentsService: DocumentsService, private store: Store) { } + constructor( + private documentRetrievalService: DocumentRetrievalService, + private documentsService: DocumentsService, + private store: Store + ) {} - @HostListener('click', ['$event']) clickEvent(event: PointerEvent) { - if (this.certNotNeeded) return; - event.preventDefault(); - event.stopPropagation(); + @HostListener('click', ['$event']) clickEvent(event: PointerEvent) { + if (this.certNotNeeded) return; + event.preventDefault(); + event.stopPropagation(); - if (this.loading) { - this.store.dispatch(setSpinnerState({ showSpinner: true })); - } + if (this.loading) { + this.store.dispatch(setSpinnerState({ showSpinner: true })); + } - this.documentRetrievalService - .getDocument(this.params) - .pipe(takeWhile((doc) => doc.type !== HttpEventType.Response, true)) - .subscribe((response) => { - switch (response.type) { - case HttpEventType.DownloadProgress: - break; - case HttpEventType.Response: - this.documentsService.openDocumentFromResponse(this.fileName, response.body, this.fileType); - this.store.dispatch(setSpinnerState({ showSpinner: false })); - break; - default: - break; - } - }); - } + this.documentRetrievalService + .getDocument(this.params) + .pipe(takeWhile((doc) => doc.type !== HttpEventType.Response, true)) + .subscribe((response) => { + switch (response.type) { + case HttpEventType.DownloadProgress: + break; + case HttpEventType.Response: + this.documentsService.openDocumentFromResponse(this.fileName, response.body, this.fileType); + this.store.dispatch(setSpinnerState({ showSpinner: false })); + break; + default: + break; + } + }); + } } diff --git a/src/app/shared/pipes/default-null-or-empty/default-null-or-empty.pipe.spec.ts b/src/app/shared/pipes/default-null-or-empty/default-null-or-empty.pipe.spec.ts index 74ec2a5924..cfad598ce8 100644 --- a/src/app/shared/pipes/default-null-or-empty/default-null-or-empty.pipe.spec.ts +++ b/src/app/shared/pipes/default-null-or-empty/default-null-or-empty.pipe.spec.ts @@ -1,40 +1,40 @@ import { DefaultNullOrEmpty } from './default-null-or-empty.pipe'; describe('DefaultNullOrEmpty pipe tests', () => { - // This pipe is a pure, stateless function so no need for BeforeEach - const pipe = new DefaultNullOrEmpty(); - - it('transforms null to "-"', () => { - expect(pipe.transform(null)).toBe('-'); - }); - - it('transforms "" to "-"', () => { - expect(pipe.transform('')).toBe('-'); - }); - - it('transforms " " to "-"', () => { - expect(pipe.transform(' ')).toBe('-'); - }); - - it('transforms boolean "true" to "Yes"', () => { - expect(pipe.transform(true)).toBe('Yes'); - }); - - it('transforms boolean "false" to "No"', () => { - expect(pipe.transform(false)).toBe('No'); - }); - - it('otherwise returns the string value unchanged', () => { - expect(pipe.transform('This is OK')).toBe('This is OK'); - }); - - it('otherwise returns the date value unchanged', () => { - const val = new Date(); - expect(pipe.transform(val)).toBe(val); - }); - - it('should capitalise the first character of a string', () => { - const val = 'lowercase string'; - expect(pipe.transform(val)).toBe('Lowercase string'); - }); + // This pipe is a pure, stateless function so no need for BeforeEach + const pipe = new DefaultNullOrEmpty(); + + it('transforms null to "-"', () => { + expect(pipe.transform(null)).toBe('-'); + }); + + it('transforms "" to "-"', () => { + expect(pipe.transform('')).toBe('-'); + }); + + it('transforms " " to "-"', () => { + expect(pipe.transform(' ')).toBe('-'); + }); + + it('transforms boolean "true" to "Yes"', () => { + expect(pipe.transform(true)).toBe('Yes'); + }); + + it('transforms boolean "false" to "No"', () => { + expect(pipe.transform(false)).toBe('No'); + }); + + it('otherwise returns the string value unchanged', () => { + expect(pipe.transform('This is OK')).toBe('This is OK'); + }); + + it('otherwise returns the date value unchanged', () => { + const val = new Date(); + expect(pipe.transform(val)).toBe(val); + }); + + it('should capitalise the first character of a string', () => { + const val = 'lowercase string'; + expect(pipe.transform(val)).toBe('Lowercase string'); + }); }); diff --git a/src/app/shared/pipes/default-null-or-empty/default-null-or-empty.pipe.ts b/src/app/shared/pipes/default-null-or-empty/default-null-or-empty.pipe.ts index a7b53d3d7b..8c4b8e4bd6 100644 --- a/src/app/shared/pipes/default-null-or-empty/default-null-or-empty.pipe.ts +++ b/src/app/shared/pipes/default-null-or-empty/default-null-or-empty.pipe.ts @@ -2,24 +2,24 @@ import { Pipe, PipeTransform } from '@angular/core'; @Pipe({ name: 'defaultNullOrEmpty' }) export class DefaultNullOrEmpty implements PipeTransform { - titleCaseFirstWord(value: string) { - return value[0].toUpperCase() + value.substring(1); - } + titleCaseFirstWord(value: string) { + return value[0].toUpperCase() + value.substring(1); + } - // eslint-disable-next-line @typescript-eslint/no-explicit-any - transform(value: any) { - if (typeof value === 'string') { - if (value.toLowerCase() === 'true') { - return 'Yes'; - } - if (value.toLowerCase() === 'false') { - return 'No'; - } - return value.trim().length > 0 ? this.titleCaseFirstWord(value) : '-'; - } - if (typeof value === 'boolean') { - return value ? 'Yes' : 'No'; - } - return value == null ? '-' : value; - } + // eslint-disable-next-line @typescript-eslint/no-explicit-any + transform(value: any) { + if (typeof value === 'string') { + if (value.toLowerCase() === 'true') { + return 'Yes'; + } + if (value.toLowerCase() === 'false') { + return 'No'; + } + return value.trim().length > 0 ? this.titleCaseFirstWord(value) : '-'; + } + if (typeof value === 'boolean') { + return value ? 'Yes' : 'No'; + } + return value == null ? '-' : value; + } } diff --git a/src/app/shared/pipes/digit-group-separator/digit-group-separator.pipe.spec.ts b/src/app/shared/pipes/digit-group-separator/digit-group-separator.pipe.spec.ts index 4a5bcf1802..836ff6cfa9 100644 --- a/src/app/shared/pipes/digit-group-separator/digit-group-separator.pipe.spec.ts +++ b/src/app/shared/pipes/digit-group-separator/digit-group-separator.pipe.spec.ts @@ -1,22 +1,22 @@ import { DigitGroupSeparatorPipe } from './digit-group-separator.pipe'; describe('digitGroupSeparator pipe tests', () => { - // This pipe is a pure, stateless function so no need for BeforeEach - const pipe = new DigitGroupSeparatorPipe(); + // This pipe is a pure, stateless function so no need for BeforeEach + const pipe = new DigitGroupSeparatorPipe(); - it('returns undefined', () => { - expect(pipe.transform(undefined)).toBeUndefined(); - }); + it('returns undefined', () => { + expect(pipe.transform(undefined)).toBeUndefined(); + }); - it('does not separate number', () => { - expect(pipe.transform(10)).toBe('10'); - }); + it('does not separate number', () => { + expect(pipe.transform(10)).toBe('10'); + }); - it('transforms 1000 to "1,000"', () => { - expect(pipe.transform(1000)).toBe('1,000'); - }); + it('transforms 1000 to "1,000"', () => { + expect(pipe.transform(1000)).toBe('1,000'); + }); - it('transforms 1000000 to "1,000,000"', () => { - expect(pipe.transform(1000000)).toBe('1,000,000'); - }); + it('transforms 1000000 to "1,000,000"', () => { + expect(pipe.transform(1000000)).toBe('1,000,000'); + }); }); diff --git a/src/app/shared/pipes/digit-group-separator/digit-group-separator.pipe.ts b/src/app/shared/pipes/digit-group-separator/digit-group-separator.pipe.ts index 8bedcae373..3936498556 100644 --- a/src/app/shared/pipes/digit-group-separator/digit-group-separator.pipe.ts +++ b/src/app/shared/pipes/digit-group-separator/digit-group-separator.pipe.ts @@ -1,13 +1,13 @@ import { Pipe, PipeTransform } from '@angular/core'; @Pipe({ - name: 'digitGroupSeparator', + name: 'digitGroupSeparator', }) export class DigitGroupSeparatorPipe implements PipeTransform { - transform(value: number | undefined): string | undefined { - if (value) { - return value.toLocaleString(); - } - return undefined; - } + transform(value: number | undefined): string | undefined { + if (value) { + return value.toLocaleString(); + } + return undefined; + } } diff --git a/src/app/shared/pipes/format-vehicle-type/format-vehicle-type.pipe.spec.ts b/src/app/shared/pipes/format-vehicle-type/format-vehicle-type.pipe.spec.ts index 794fc3473f..430b3b6965 100644 --- a/src/app/shared/pipes/format-vehicle-type/format-vehicle-type.pipe.spec.ts +++ b/src/app/shared/pipes/format-vehicle-type/format-vehicle-type.pipe.spec.ts @@ -2,24 +2,24 @@ import { VehicleTypes } from '@models/vehicle-tech-record.model'; import { FormatVehicleTypePipe } from './format-vehicle-type.pipe'; describe('FormatVehicleTypePipe', () => { - it('create an instance', () => { - const pipe = new FormatVehicleTypePipe(); - expect(pipe).toBeTruthy(); - }); + it('create an instance', () => { + const pipe = new FormatVehicleTypePipe(); + expect(pipe).toBeTruthy(); + }); - describe('transform the values', () => { - const pipe = new FormatVehicleTypePipe(); + describe('transform the values', () => { + const pipe = new FormatVehicleTypePipe(); - it('should return trailer if value is trl', () => { - expect(pipe.transform(VehicleTypes.TRL)).toBe('trailer'); - }); + it('should return trailer if value is trl', () => { + expect(pipe.transform(VehicleTypes.TRL)).toBe('trailer'); + }); - it('should return HGV if value is hgv', () => { - expect(pipe.transform(VehicleTypes.HGV)).toBe('HGV'); - }); + it('should return HGV if value is hgv', () => { + expect(pipe.transform(VehicleTypes.HGV)).toBe('HGV'); + }); - it('should return undefined if value is undefined', () => { - expect(pipe.transform(undefined)).toBeUndefined(); - }); - }); + it('should return undefined if value is undefined', () => { + expect(pipe.transform(undefined)).toBeUndefined(); + }); + }); }); diff --git a/src/app/shared/pipes/format-vehicle-type/format-vehicle-type.pipe.ts b/src/app/shared/pipes/format-vehicle-type/format-vehicle-type.pipe.ts index 6b994b5e7e..aaadfda17e 100644 --- a/src/app/shared/pipes/format-vehicle-type/format-vehicle-type.pipe.ts +++ b/src/app/shared/pipes/format-vehicle-type/format-vehicle-type.pipe.ts @@ -2,15 +2,15 @@ import { Pipe, PipeTransform } from '@angular/core'; import { VehicleTypes } from '@models/vehicle-tech-record.model'; @Pipe({ - name: 'formatVehicleType', + name: 'formatVehicleType', }) export class FormatVehicleTypePipe implements PipeTransform { - transform(value: VehicleTypes | undefined | null): unknown { - switch (value) { - case VehicleTypes.TRL: - return 'trailer'; - default: - return value?.toUpperCase(); - } - } + transform(value: VehicleTypes | undefined | null): unknown { + switch (value) { + case VehicleTypes.TRL: + return 'trailer'; + default: + return value?.toUpperCase(); + } + } } diff --git a/src/app/shared/pipes/get-control-label/get-control-label.pipe.spec.ts b/src/app/shared/pipes/get-control-label/get-control-label.pipe.spec.ts index 3b71d23ae7..1740b4b00f 100644 --- a/src/app/shared/pipes/get-control-label/get-control-label.pipe.spec.ts +++ b/src/app/shared/pipes/get-control-label/get-control-label.pipe.spec.ts @@ -2,27 +2,27 @@ import { FormNodeOption } from '@forms/services/dynamic-form.types'; import { GetControlLabelPipe } from './get-control-label.pipe'; describe('GetControlLabelPipe', () => { - let testOptions: FormNodeOption[]; + let testOptions: FormNodeOption[]; - beforeEach(() => { - testOptions = [ - { label: 'Test', value: 'test' }, - { label: 'Foo', value: 'bar' }, - ]; - }); + beforeEach(() => { + testOptions = [ + { label: 'Test', value: 'test' }, + { label: 'Foo', value: 'bar' }, + ]; + }); - it('create an instance', () => { - const pipe = new GetControlLabelPipe(); - expect(pipe).toBeTruthy(); - }); + it('create an instance', () => { + const pipe = new GetControlLabelPipe(); + expect(pipe).toBeTruthy(); + }); - it('should return the label if one is available', () => { - const pipe = new GetControlLabelPipe(); - expect(pipe.transform('test', testOptions)).toBe('Test'); - }); + it('should return the label if one is available', () => { + const pipe = new GetControlLabelPipe(); + expect(pipe.transform('test', testOptions)).toBe('Test'); + }); - it('should return the value if no label is available', () => { - const pipe = new GetControlLabelPipe(); - expect(pipe.transform('foo', testOptions)).toBe('foo'); - }); + it('should return the value if no label is available', () => { + const pipe = new GetControlLabelPipe(); + expect(pipe.transform('foo', testOptions)).toBe('foo'); + }); }); diff --git a/src/app/shared/pipes/get-control-label/get-control-label.pipe.ts b/src/app/shared/pipes/get-control-label/get-control-label.pipe.ts index d4af9ecc31..f3001aa8f5 100644 --- a/src/app/shared/pipes/get-control-label/get-control-label.pipe.ts +++ b/src/app/shared/pipes/get-control-label/get-control-label.pipe.ts @@ -2,13 +2,13 @@ import { Pipe, PipeTransform } from '@angular/core'; import { FormNodeCombinationOptions, FormNodeOption } from '@forms/services/dynamic-form.types'; @Pipe({ - name: 'getControlLabel', + name: 'getControlLabel', }) export class GetControlLabelPipe implements PipeTransform { - transform( - value: string | number | null | undefined, - options?: FormNodeOption[] | FormNodeCombinationOptions | undefined, - ): string | number | null | undefined { - return (Array.isArray(options) && options?.find((option) => option.value === value)?.label) || value; - } + transform( + value: string | number | null | undefined, + options?: FormNodeOption[] | FormNodeCombinationOptions | undefined + ): string | number | null | undefined { + return (Array.isArray(options) && options?.find((option) => option.value === value)?.label) || value; + } } diff --git a/src/app/shared/pipes/ref-data-decode/ref-data-decode.pipe.spec.ts b/src/app/shared/pipes/ref-data-decode/ref-data-decode.pipe.spec.ts index 4e1e042b24..c632e88af4 100644 --- a/src/app/shared/pipes/ref-data-decode/ref-data-decode.pipe.spec.ts +++ b/src/app/shared/pipes/ref-data-decode/ref-data-decode.pipe.spec.ts @@ -7,130 +7,130 @@ import { STORE_FEATURE_REFERENCE_DATA_KEY, initialReferenceDataState } from '@st import { RefDataDecodePipe } from './ref-data-decode.pipe'; describe('RefDataDecodePipe', () => { - let store: MockStore; - - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [BrowserModule], - providers: [provideMockStore({ initialState: initialAppState })], - }); - - store = TestBed.inject(MockStore); - - store.setState({ - ...initialAppState, - [STORE_FEATURE_REFERENCE_DATA_KEY]: { - ...initialReferenceDataState, - [ReferenceDataResourceType.CountryOfRegistration]: { - ids: ['gb'], - entities: { - gb: { - resourceType: ReferenceDataResourceType.CountryOfRegistration, - resourceKey: 'gb', - description: 'Great Britain', - }, - }, - loading: false, - }, - [(`${ReferenceDataResourceType.CountryOfRegistration}#AUDIT`) as ReferenceDataResourceType]: { - searchReturn: [ - { - resourceType: (`${ReferenceDataResourceType.CountryOfRegistration}#AUDIT`) as ReferenceDataResourceType, - resourceKey: 'a', - description: 'Austria', - }, - ], - loading: false, - }, - [ReferenceDataResourceType.Tyres]: { - ids: ['101'], - entities: { - 101: { - resourceType: ReferenceDataResourceType.Tyres, - resourceKey: '101', - tyreSize: '235/75-17.5', - } as ReferenceDataTyre, - }, - loading: false, - }, - }, - }); - }); - - afterEach(() => { - jest.clearAllMocks(); - store.refreshState(); - }); - - it('should return description', (done) => { - const pipe = new RefDataDecodePipe(store); - - pipe.transform('gb', ReferenceDataResourceType.CountryOfRegistration).subscribe((val) => { - expect(val).toBe('Great Britain'); - done(); - }); - }); - - it('should return description of deleted item', (done) => { - const pipe = new RefDataDecodePipe(store); - - pipe.transform('a', ReferenceDataResourceType.CountryOfRegistration).subscribe((val) => { - expect(val).toBe('Austria'); - done(); - }); - }); - - it('should return tyreSize', (done) => { - const pipe = new RefDataDecodePipe(store); - - pipe.transform('101', ReferenceDataResourceType.Tyres, 'tyreSize').subscribe((val) => { - expect(val).toBe('235/75-17.5'); - done(); - }); - }); - - it('should return untransformed value when description is undefined', (done) => { - const pipe = new RefDataDecodePipe(store); - - pipe.transform('101', ReferenceDataResourceType.Tyres).subscribe((val) => { - expect(val).toBe('101'); - done(); - }); - }); - - it('should return untransformed value when value is not a known resourceKey', (done) => { - const pipe = new RefDataDecodePipe(store); - - pipe.transform('foo', ReferenceDataResourceType.Tyres, 'tyreSize').subscribe((val) => { - expect(val).toBe('foo'); - done(); - }); - }); - - it('should return untransformed value when value is falsy', (done) => { - const pipe = new RefDataDecodePipe(store); - - pipe.transform('', ReferenceDataResourceType.Tyres, 'tyreSize').subscribe((val) => { - expect(val).toBe(''); - done(); - }); - }); - - it('should return untransformed value when resourceType is falsy', (done) => { - const pipe = new RefDataDecodePipe(store); - - pipe.transform('101', '', 'tyreSize').subscribe((val) => { - expect(val).toBe('101'); - done(); - }); - }); - - it('should return untransformed value when data not in state', (done) => { - const pipe = new RefDataDecodePipe(store); - - pipe.transform('bar', 'baz').subscribe((val) => { - expect(val).toBe('bar'); - done(); - }); - }); + let store: MockStore; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [BrowserModule], + providers: [provideMockStore({ initialState: initialAppState })], + }); + + store = TestBed.inject(MockStore); + + store.setState({ + ...initialAppState, + [STORE_FEATURE_REFERENCE_DATA_KEY]: { + ...initialReferenceDataState, + [ReferenceDataResourceType.CountryOfRegistration]: { + ids: ['gb'], + entities: { + gb: { + resourceType: ReferenceDataResourceType.CountryOfRegistration, + resourceKey: 'gb', + description: 'Great Britain', + }, + }, + loading: false, + }, + [`${ReferenceDataResourceType.CountryOfRegistration}#AUDIT` as ReferenceDataResourceType]: { + searchReturn: [ + { + resourceType: `${ReferenceDataResourceType.CountryOfRegistration}#AUDIT` as ReferenceDataResourceType, + resourceKey: 'a', + description: 'Austria', + }, + ], + loading: false, + }, + [ReferenceDataResourceType.Tyres]: { + ids: ['101'], + entities: { + 101: { + resourceType: ReferenceDataResourceType.Tyres, + resourceKey: '101', + tyreSize: '235/75-17.5', + } as ReferenceDataTyre, + }, + loading: false, + }, + }, + }); + }); + + afterEach(() => { + jest.clearAllMocks(); + store.refreshState(); + }); + + it('should return description', (done) => { + const pipe = new RefDataDecodePipe(store); + + pipe.transform('gb', ReferenceDataResourceType.CountryOfRegistration).subscribe((val) => { + expect(val).toBe('Great Britain'); + done(); + }); + }); + + it('should return description of deleted item', (done) => { + const pipe = new RefDataDecodePipe(store); + + pipe.transform('a', ReferenceDataResourceType.CountryOfRegistration).subscribe((val) => { + expect(val).toBe('Austria'); + done(); + }); + }); + + it('should return tyreSize', (done) => { + const pipe = new RefDataDecodePipe(store); + + pipe.transform('101', ReferenceDataResourceType.Tyres, 'tyreSize').subscribe((val) => { + expect(val).toBe('235/75-17.5'); + done(); + }); + }); + + it('should return untransformed value when description is undefined', (done) => { + const pipe = new RefDataDecodePipe(store); + + pipe.transform('101', ReferenceDataResourceType.Tyres).subscribe((val) => { + expect(val).toBe('101'); + done(); + }); + }); + + it('should return untransformed value when value is not a known resourceKey', (done) => { + const pipe = new RefDataDecodePipe(store); + + pipe.transform('foo', ReferenceDataResourceType.Tyres, 'tyreSize').subscribe((val) => { + expect(val).toBe('foo'); + done(); + }); + }); + + it('should return untransformed value when value is falsy', (done) => { + const pipe = new RefDataDecodePipe(store); + + pipe.transform('', ReferenceDataResourceType.Tyres, 'tyreSize').subscribe((val) => { + expect(val).toBe(''); + done(); + }); + }); + + it('should return untransformed value when resourceType is falsy', (done) => { + const pipe = new RefDataDecodePipe(store); + + pipe.transform('101', '', 'tyreSize').subscribe((val) => { + expect(val).toBe('101'); + done(); + }); + }); + + it('should return untransformed value when data not in state', (done) => { + const pipe = new RefDataDecodePipe(store); + + pipe.transform('bar', 'baz').subscribe((val) => { + expect(val).toBe('bar'); + done(); + }); + }); }); diff --git a/src/app/shared/pipes/ref-data-decode/ref-data-decode.pipe.ts b/src/app/shared/pipes/ref-data-decode/ref-data-decode.pipe.ts index d561cbafb1..9e972bb656 100644 --- a/src/app/shared/pipes/ref-data-decode/ref-data-decode.pipe.ts +++ b/src/app/shared/pipes/ref-data-decode/ref-data-decode.pipe.ts @@ -1,83 +1,89 @@ import { OnDestroy, Pipe, PipeTransform } from '@angular/core'; import { SpecialRefData } from '@forms/services/multi-options.service'; -import { ReferenceDataModelBase, ReferenceDataResourceType, ReferenceDataResourceTypeAudit } from '@models/reference-data.model'; +import { + ReferenceDataModelBase, + ReferenceDataResourceType, + ReferenceDataResourceTypeAudit, +} from '@models/reference-data.model'; import { VehicleTypes } from '@models/vehicle-tech-record.model'; import { Store } from '@ngrx/store'; import { State } from '@store/index'; import { - fetchReferenceData, fetchReferenceDataByKeySearch, selectReferenceDataByResourceKey, selectSearchReturn, + fetchReferenceData, + fetchReferenceDataByKeySearch, + selectReferenceDataByResourceKey, + selectSearchReturn, } from '@store/reference-data'; import { getSingleVehicleType } from '@store/technical-records'; -import { - Observable, Subject, - asapScheduler, - combineLatest, map, of, take, -} from 'rxjs'; +import { Observable, Subject, asapScheduler, combineLatest, map, of, take } from 'rxjs'; @Pipe({ - name: 'refDataDecode$', + name: 'refDataDecode$', }) export class RefDataDecodePipe implements PipeTransform, OnDestroy { - constructor(private store: Store) {} + constructor(private store: Store) {} - private destroy$ = new Subject(); + private destroy$ = new Subject(); - ngOnDestroy(): void { - this.destroy$.next(); - this.destroy$.complete(); - } + ngOnDestroy(): void { + this.destroy$.next(); + this.destroy$.complete(); + } - transform( - value: string | number | undefined, - resourceType: string | undefined, - decodeKey: string | number = 'description', - ): Observable { - if (!resourceType || !value) { - return of(value); - } + transform( + value: string | number | undefined, + resourceType: string | undefined, + decodeKey: string | number = 'description' + ): Observable { + if (!resourceType || !value) { + return of(value); + } - if (resourceType === SpecialRefData.ReasonsForAbandoning) { - this.store - .select(getSingleVehicleType) - .pipe(take(1)) - .subscribe((vehicleType) => { - switch (vehicleType) { - case VehicleTypes.HGV: - resourceType = ReferenceDataResourceType.ReasonsForAbandoningHgv; - break; - case VehicleTypes.PSV: - resourceType = ReferenceDataResourceType.ReasonsForAbandoningPsv; - break; - case VehicleTypes.TRL: - resourceType = ReferenceDataResourceType.ReasonsForAbandoningTrl; - break; - default: - resourceType = ReferenceDataResourceType.ReasonsForAbandoningHgv; - break; - } - }); - } + if (resourceType === SpecialRefData.ReasonsForAbandoning) { + this.store + .select(getSingleVehicleType) + .pipe(take(1)) + .subscribe((vehicleType) => { + switch (vehicleType) { + case VehicleTypes.HGV: + resourceType = ReferenceDataResourceType.ReasonsForAbandoningHgv; + break; + case VehicleTypes.PSV: + resourceType = ReferenceDataResourceType.ReasonsForAbandoningPsv; + break; + case VehicleTypes.TRL: + resourceType = ReferenceDataResourceType.ReasonsForAbandoningTrl; + break; + default: + resourceType = ReferenceDataResourceType.ReasonsForAbandoningHgv; + break; + } + }); + } - asapScheduler.schedule(() => { - this.store.dispatch(fetchReferenceData({ resourceType: resourceType as ReferenceDataResourceType })); - }); + asapScheduler.schedule(() => { + this.store.dispatch(fetchReferenceData({ resourceType: resourceType as ReferenceDataResourceType })); + }); - asapScheduler.schedule(() => { - this.store.dispatch( - fetchReferenceDataByKeySearch({ resourceType: (`${resourceType}#AUDIT`) as ReferenceDataResourceType, resourceKey: `${value}#` }), - ); - }); + asapScheduler.schedule(() => { + this.store.dispatch( + fetchReferenceDataByKeySearch({ + resourceType: `${resourceType}#AUDIT` as ReferenceDataResourceType, + resourceKey: `${value}#`, + }) + ); + }); - return combineLatest([ - this.store.select(selectReferenceDataByResourceKey(resourceType as ReferenceDataResourceType, value)), - this.store.select(selectSearchReturn((`${resourceType}#AUDIT`) as ReferenceDataResourceTypeAudit)), - ]).pipe( - map(([refDataItem, refDataItemAudit]) => { - if (!refDataItem) { - return refDataItemAudit?.[0].description ?? value; - } - return refDataItem[decodeKey as keyof ReferenceDataModelBase] ?? value; - }), - ); - } + return combineLatest([ + this.store.select(selectReferenceDataByResourceKey(resourceType as ReferenceDataResourceType, value)), + this.store.select(selectSearchReturn(`${resourceType}#AUDIT` as ReferenceDataResourceTypeAudit)), + ]).pipe( + map(([refDataItem, refDataItemAudit]) => { + if (!refDataItem) { + return refDataItemAudit?.[0].description ?? value; + } + return refDataItem[decodeKey as keyof ReferenceDataModelBase] ?? value; + }) + ); + } } diff --git a/src/app/shared/pipes/test-type-name/test-type-name.pipe.spec.ts b/src/app/shared/pipes/test-type-name/test-type-name.pipe.spec.ts index 1e4c726c09..1e0efd40ac 100644 --- a/src/app/shared/pipes/test-type-name/test-type-name.pipe.spec.ts +++ b/src/app/shared/pipes/test-type-name/test-type-name.pipe.spec.ts @@ -2,45 +2,45 @@ import { TestTypesTaxonomy } from '@api/test-types'; import { TestTypeNamePipe } from './test-type-name.pipe'; const testTypes = [ - { - id: '1', - suggestedTestTypeDisplayName: 'Test Type Display Name 1', - }, - { - id: '2', - testTypeName: 'Test Type Name 2', - }, - { - id: '3', - nextTestTypesOrCategories: [ - { - id: '4', - name: 'Test Name 4', - nextTestTypesOrCategories: [ - { - id: '5', - name: 'Test Name 5', - }, - ], - }, - ], - }, + { + id: '1', + suggestedTestTypeDisplayName: 'Test Type Display Name 1', + }, + { + id: '2', + testTypeName: 'Test Type Name 2', + }, + { + id: '3', + nextTestTypesOrCategories: [ + { + id: '4', + name: 'Test Name 4', + nextTestTypesOrCategories: [ + { + id: '5', + name: 'Test Name 5', + }, + ], + }, + ], + }, ] as TestTypesTaxonomy; describe('TestTypeNamePipe', () => { - it('create an instance', () => { - const pipe = new TestTypeNamePipe(); - expect(pipe).toBeTruthy(); - }); + it('create an instance', () => { + const pipe = new TestTypeNamePipe(); + expect(pipe).toBeTruthy(); + }); - it.each([ - ['Test Name 5', '5'], - ['Test Name 4', '4'], - ['-', '3'], - ['Test Type Name 2', '2'], - ['Test Type Display Name 1', '1'], - ])('should %s for id: %s', (expected: string, id: string) => { - const pipe = new TestTypeNamePipe(); - expect(pipe.transform(id, testTypes)).toBe(expected); - }); + it.each([ + ['Test Name 5', '5'], + ['Test Name 4', '4'], + ['-', '3'], + ['Test Type Name 2', '2'], + ['Test Type Display Name 1', '1'], + ])('should %s for id: %s', (expected: string, id: string) => { + const pipe = new TestTypeNamePipe(); + expect(pipe.transform(id, testTypes)).toBe(expected); + }); }); diff --git a/src/app/shared/pipes/test-type-name/test-type-name.pipe.ts b/src/app/shared/pipes/test-type-name/test-type-name.pipe.ts index a4e96db364..aca802585b 100644 --- a/src/app/shared/pipes/test-type-name/test-type-name.pipe.ts +++ b/src/app/shared/pipes/test-type-name/test-type-name.pipe.ts @@ -2,36 +2,38 @@ import { Pipe, PipeTransform } from '@angular/core'; import { TestType, TestTypeCategory } from '@api/test-types'; @Pipe({ - name: 'testTypeName', + name: 'testTypeName', }) export class TestTypeNamePipe implements PipeTransform { - findTestTypeNameById(id: string, testTypes: Array): TestType | undefined { - function idMatch(testType: TestType | TestTypeCategory) { - if (testType.id === id) { - result = testType; - return true; - } - return Object.prototype.hasOwnProperty.call(testType, 'nextTestTypesOrCategories') - && (testType as TestTypeCategory).nextTestTypesOrCategories?.some(idMatch); - } + findTestTypeNameById(id: string, testTypes: Array): TestType | undefined { + function idMatch(testType: TestType | TestTypeCategory) { + if (testType.id === id) { + result = testType; + return true; + } + return ( + Object.prototype.hasOwnProperty.call(testType, 'nextTestTypesOrCategories') && + (testType as TestTypeCategory).nextTestTypesOrCategories?.some(idMatch) + ); + } - let result; - testTypes.some(idMatch); - return result; - } + let result; + testTypes.some(idMatch); + return result; + } - transform(value: string, testTypes: Array | null): string { - if (!testTypes) { - return value; - } + transform(value: string, testTypes: Array | null): string { + if (!testTypes) { + return value; + } - const match = this.findTestTypeNameById(value, testTypes); + const match = this.findTestTypeNameById(value, testTypes); - if (!match) { - return '-'; - } + if (!match) { + return '-'; + } - const { suggestedTestTypeDisplayName, testTypeName, name } = match; - return suggestedTestTypeDisplayName || testTypeName || name || '-'; - } + const { suggestedTestTypeDisplayName, testTypeName, name } = match; + return suggestedTestTypeDisplayName || testTypeName || name || '-'; + } } diff --git a/src/app/shared/pipes/truncate/truncate.pipe.spec.ts b/src/app/shared/pipes/truncate/truncate.pipe.spec.ts index e6a142369b..ce36e89ad1 100644 --- a/src/app/shared/pipes/truncate/truncate.pipe.spec.ts +++ b/src/app/shared/pipes/truncate/truncate.pipe.spec.ts @@ -1,18 +1,18 @@ import { TruncatePipe } from './truncate.pipe'; describe('truncatePipe pipe tests', () => { - // This pipe is a pure, stateless function so no need for BeforeEach - const pipe = new TruncatePipe(); + // This pipe is a pure, stateless function so no need for BeforeEach + const pipe = new TruncatePipe(); - it('does not truncate string', () => { - expect(pipe.transform('too short', 10, '...')).toBe('too short'); - }); + it('does not truncate string', () => { + expect(pipe.transform('too short', 10, '...')).toBe('too short'); + }); - it('transforms "this string is too long" to "this string is too..."', () => { - expect(pipe.transform('this string is too long', 11)).toBe('this string...'); - }); + it('transforms "this string is too long" to "this string is too..."', () => { + expect(pipe.transform('this string is too long', 11)).toBe('this string...'); + }); - it('transforms "this string is too long" to "this string is too:::"', () => { - expect(pipe.transform('this string is too long', 11, ':::')).toBe('this string:::'); - }); + it('transforms "this string is too long" to "this string is too:::"', () => { + expect(pipe.transform('this string is too long', 11, ':::')).toBe('this string:::'); + }); }); diff --git a/src/app/shared/pipes/truncate/truncate.pipe.ts b/src/app/shared/pipes/truncate/truncate.pipe.ts index e468eaac33..4dc9b42882 100644 --- a/src/app/shared/pipes/truncate/truncate.pipe.ts +++ b/src/app/shared/pipes/truncate/truncate.pipe.ts @@ -1,10 +1,10 @@ import { Pipe, PipeTransform } from '@angular/core'; @Pipe({ - name: 'truncate', + name: 'truncate', }) export class TruncatePipe implements PipeTransform { - transform(value: string, limit = 20, trail = '...'): string { - return value.length > limit ? value.substring(0, limit) + trail : value; - } + transform(value: string, limit = 20, trail = '...'): string { + return value.length > limit ? value.substring(0, limit) + trail : value; + } } diff --git a/src/app/shared/pipes/tyre-axle-load/tyre-axle-load.pipe.spec.ts b/src/app/shared/pipes/tyre-axle-load/tyre-axle-load.pipe.spec.ts index 462fcf3592..e572beb555 100644 --- a/src/app/shared/pipes/tyre-axle-load/tyre-axle-load.pipe.spec.ts +++ b/src/app/shared/pipes/tyre-axle-load/tyre-axle-load.pipe.spec.ts @@ -2,26 +2,28 @@ import { ReferenceDataResourceType } from '@models/reference-data.model'; import { TyreAxleLoadPipe } from './tyre-axle-load.pipe'; describe('TyreAxleLoadPipe', () => { - it('create an instance', () => { - const pipe = new TyreAxleLoadPipe(); - expect(pipe).toBeTruthy(); - }); + it('create an instance', () => { + const pipe = new TyreAxleLoadPipe(); + expect(pipe).toBeTruthy(); + }); - describe('transform the values', () => { - const pipe = new TyreAxleLoadPipe(); + describe('transform the values', () => { + const pipe = new TyreAxleLoadPipe(); - it('should return the value of the axle load if it is defined', () => { - expect(pipe.transform('value', '123', 2, [])).toBe('value'); - }); + it('should return the value of the axle load if it is defined', () => { + expect(pipe.transform('value', '123', 2, [])).toBe('value'); + }); - it('should multiply the value by the given factor and return the value as a string', () => { - expect( - pipe.transform(undefined, '123', 2, [{ resourceType: ReferenceDataResourceType.TyreLoadIndex, resourceKey: '123', loadIndex: '200' }]), - ).toBe((200 * 2).toString()); - }); + it('should multiply the value by the given factor and return the value as a string', () => { + expect( + pipe.transform(undefined, '123', 2, [ + { resourceType: ReferenceDataResourceType.TyreLoadIndex, resourceKey: '123', loadIndex: '200' }, + ]) + ).toBe((200 * 2).toString()); + }); - it('should return undefined if the loadIndex is undefined and the value is undefined', () => { - expect(pipe.transform(undefined, undefined, 3, [])).toBeUndefined(); - }); - }); + it('should return undefined if the loadIndex is undefined and the value is undefined', () => { + expect(pipe.transform(undefined, undefined, 3, [])).toBeUndefined(); + }); + }); }); diff --git a/src/app/shared/pipes/tyre-axle-load/tyre-axle-load.pipe.ts b/src/app/shared/pipes/tyre-axle-load/tyre-axle-load.pipe.ts index 70f2b2eb95..b140e76af3 100644 --- a/src/app/shared/pipes/tyre-axle-load/tyre-axle-load.pipe.ts +++ b/src/app/shared/pipes/tyre-axle-load/tyre-axle-load.pipe.ts @@ -2,19 +2,19 @@ import { Pipe, PipeTransform } from '@angular/core'; import { ReferenceDataTyreLoadIndex } from '@models/reference-data.model'; @Pipe({ - name: 'tyreAxleLoad', + name: 'tyreAxleLoad', }) export class TyreAxleLoadPipe implements PipeTransform { - transform( - axleLoad: string | undefined, - index: string | undefined, - factor: number, - loadIndex: ReferenceDataTyreLoadIndex[] | null, - ): undefined | string | number { - if (axleLoad) { - return axleLoad; - } - const axleLoadIndex = loadIndex?.find((resource) => resource.resourceKey === index); - return axleLoadIndex?.loadIndex ? String(+axleLoadIndex.loadIndex * factor) : undefined; - } + transform( + axleLoad: string | undefined, + index: string | undefined, + factor: number, + loadIndex: ReferenceDataTyreLoadIndex[] | null + ): undefined | string | number { + if (axleLoad) { + return axleLoad; + } + const axleLoadIndex = loadIndex?.find((resource) => resource.resourceKey === index); + return axleLoadIndex?.loadIndex ? String(+axleLoadIndex.loadIndex * factor) : undefined; + } } diff --git a/src/app/shared/shared.module.ts b/src/app/shared/shared.module.ts index c812d56293..5c2117b418 100644 --- a/src/app/shared/shared.module.ts +++ b/src/app/shared/shared.module.ts @@ -29,62 +29,61 @@ import { TestTypeNamePipe } from './pipes/test-type-name/test-type-name.pipe'; import { TyreAxleLoadPipe } from './pipes/tyre-axle-load/tyre-axle-load.pipe'; @NgModule({ - declarations: [ - DefaultNullOrEmpty, - ButtonGroupComponent, - ButtonComponent, - BannerComponent, - RoleRequiredDirective, - FeatureToggleDirective, - FeatureToggleDirective, - TagComponent, - NumberPlateComponent, - IconComponent, - TestTypeNamePipe, - AccordionComponent, - AccordionControlComponent, - PaginationComponent, - TestCertificateComponent, - PreventDoubleClickDirective, - BaseDialogComponent, - DigitGroupSeparatorPipe, - RefDataDecodePipe, - RetrieveDocumentDirective, - InputSpinnerComponent, - RouterOutletComponent, - TyreAxleLoadPipe, - GetControlLabelPipe, - FormatVehicleTypePipe, - CollapsibleTextComponent, - ], - imports: [CommonModule, RouterModule], - exports: [ - DefaultNullOrEmpty, - ButtonGroupComponent, - ButtonComponent, - BannerComponent, - RoleRequiredDirective, - FeatureToggleDirective, - TagComponent, - NumberPlateComponent, - IconComponent, - TestTypeNamePipe, - AccordionComponent, - AccordionControlComponent, - PaginationComponent, - TestCertificateComponent, - BaseDialogComponent, - DigitGroupSeparatorPipe, - RefDataDecodePipe, - RetrieveDocumentDirective, - InputSpinnerComponent, - RouterOutletComponent, - TyreAxleLoadPipe, - GetControlLabelPipe, - FormatVehicleTypePipe, - CollapsibleTextComponent, - - ], - providers: [DocumentRetrievalService], + declarations: [ + DefaultNullOrEmpty, + ButtonGroupComponent, + ButtonComponent, + BannerComponent, + RoleRequiredDirective, + FeatureToggleDirective, + FeatureToggleDirective, + TagComponent, + NumberPlateComponent, + IconComponent, + TestTypeNamePipe, + AccordionComponent, + AccordionControlComponent, + PaginationComponent, + TestCertificateComponent, + PreventDoubleClickDirective, + BaseDialogComponent, + DigitGroupSeparatorPipe, + RefDataDecodePipe, + RetrieveDocumentDirective, + InputSpinnerComponent, + RouterOutletComponent, + TyreAxleLoadPipe, + GetControlLabelPipe, + FormatVehicleTypePipe, + CollapsibleTextComponent, + ], + imports: [CommonModule, RouterModule], + exports: [ + DefaultNullOrEmpty, + ButtonGroupComponent, + ButtonComponent, + BannerComponent, + RoleRequiredDirective, + FeatureToggleDirective, + TagComponent, + NumberPlateComponent, + IconComponent, + TestTypeNamePipe, + AccordionComponent, + AccordionControlComponent, + PaginationComponent, + TestCertificateComponent, + BaseDialogComponent, + DigitGroupSeparatorPipe, + RefDataDecodePipe, + RetrieveDocumentDirective, + InputSpinnerComponent, + RouterOutletComponent, + TyreAxleLoadPipe, + GetControlLabelPipe, + FormatVehicleTypePipe, + CollapsibleTextComponent, + ], + providers: [DocumentRetrievalService], }) export class SharedModule {} diff --git a/src/app/store/app-store.module.ts b/src/app/store/app-store.module.ts index a27747510a..4cb77fba32 100644 --- a/src/app/store/app-store.module.ts +++ b/src/app/store/app-store.module.ts @@ -20,32 +20,32 @@ import { TestTypesStateModule } from './test-types/test-types.module'; import { UserStateModule } from './user/user-state.module'; @NgModule({ - declarations: [], - imports: [ - CommonModule, - StoreModule.forRoot({}), - EffectsModule.forRoot([]), - environment.EnableDevTools - ? StoreDevtoolsModule.instrument({ - name: 'VTM Web Dev Tools', - maxAge: 25, // Retains last 25 states - logOnly: environment.production, // Log-only mode in production - }) - : [], - DefectsStateModule, - GlobalErrorStateModule, - GlobalWarningStateModule, - ReferenceDataStateModule, - RouterStateModule, - SpinnerStateModule, - TechnicalRecordsStateModule, - TestRecordsStateModule, - TestStationsStateModule, - TestTypesStateModule, - UserStateModule, - TechRecordSearchStateModule, - RetryInterceptorStateModule, - RequiredStandardsStateModule, - ], + declarations: [], + imports: [ + CommonModule, + StoreModule.forRoot({}), + EffectsModule.forRoot([]), + environment.EnableDevTools + ? StoreDevtoolsModule.instrument({ + name: 'VTM Web Dev Tools', + maxAge: 25, // Retains last 25 states + logOnly: environment.production, // Log-only mode in production + }) + : [], + DefectsStateModule, + GlobalErrorStateModule, + GlobalWarningStateModule, + ReferenceDataStateModule, + RouterStateModule, + SpinnerStateModule, + TechnicalRecordsStateModule, + TestRecordsStateModule, + TestStationsStateModule, + TestTypesStateModule, + UserStateModule, + TechRecordSearchStateModule, + RetryInterceptorStateModule, + RequiredStandardsStateModule, + ], }) export class AppStoreModule {} diff --git a/src/app/store/defects/actions/defects.actions.spec.ts b/src/app/store/defects/actions/defects.actions.spec.ts index d97c9872d7..86c3a1cbb4 100644 --- a/src/app/store/defects/actions/defects.actions.spec.ts +++ b/src/app/store/defects/actions/defects.actions.spec.ts @@ -1,14 +1,19 @@ import { - fetchDefect, fetchDefectFailed, fetchDefects, fetchDefectsFailed, fetchDefectsSuccess, fetchDefectSuccess, + fetchDefect, + fetchDefectFailed, + fetchDefectSuccess, + fetchDefects, + fetchDefectsFailed, + fetchDefectsSuccess, } from './defects.actions'; describe('Defects Actions', () => { - it('should return correct types', () => { - expect(fetchDefects.type).toBe('[API/defects] Fetch Defects'); - expect(fetchDefectsSuccess.type).toBe('[API/defects] Fetch Defects Success'); - expect(fetchDefectsFailed.type).toBe('[API/defects] Fetch Defects Failed'); - expect(fetchDefect.type).toBe('[API/defects] Fetch Defect by ID'); - expect(fetchDefectSuccess.type).toBe('[API/defects] Fetch Defect by ID Success'); - expect(fetchDefectFailed.type).toBe('[API/defects] Fetch Defect by ID Failed'); - }); + it('should return correct types', () => { + expect(fetchDefects.type).toBe('[API/defects] Fetch Defects'); + expect(fetchDefectsSuccess.type).toBe('[API/defects] Fetch Defects Success'); + expect(fetchDefectsFailed.type).toBe('[API/defects] Fetch Defects Failed'); + expect(fetchDefect.type).toBe('[API/defects] Fetch Defect by ID'); + expect(fetchDefectSuccess.type).toBe('[API/defects] Fetch Defect by ID Success'); + expect(fetchDefectFailed.type).toBe('[API/defects] Fetch Defect by ID Failed'); + }); }); diff --git a/src/app/store/defects/actions/defects.actions.ts b/src/app/store/defects/actions/defects.actions.ts index 2146070585..b2d49ae7e5 100644 --- a/src/app/store/defects/actions/defects.actions.ts +++ b/src/app/store/defects/actions/defects.actions.ts @@ -11,7 +11,7 @@ export const fetchDefectSuccess = createAction(getTitle(false, 'Success'), props export const fetchDefectFailed = createAction(getTitle(false, 'Failed'), props()); function getTitle(isPlural = false, suffix = ''): string { - const plural = isPlural ? 's' : ' by ID'; - suffix = suffix ? ` ${suffix}` : suffix; - return `[API/defects] Fetch Defect${plural}${suffix}`; + const plural = isPlural ? 's' : ' by ID'; + suffix = suffix ? ` ${suffix}` : suffix; + return `[API/defects] Fetch Defect${plural}${suffix}`; } diff --git a/src/app/store/defects/defects-state.module.ts b/src/app/store/defects/defects-state.module.ts index 62a1dc1081..b0d9f1af36 100644 --- a/src/app/store/defects/defects-state.module.ts +++ b/src/app/store/defects/defects-state.module.ts @@ -3,10 +3,14 @@ import { NgModule } from '@angular/core'; import { EffectsModule } from '@ngrx/effects'; import { StoreModule } from '@ngrx/store'; import { DefectsEffects } from './effects/defects.effects'; -import { defectsReducer, STORE_FEATURE_DEFECTS_KEY } from './reducers/defects.reducer'; +import { STORE_FEATURE_DEFECTS_KEY, defectsReducer } from './reducers/defects.reducer'; @NgModule({ - declarations: [], - imports: [CommonModule, StoreModule.forFeature(STORE_FEATURE_DEFECTS_KEY, defectsReducer), EffectsModule.forFeature([DefectsEffects])], + declarations: [], + imports: [ + CommonModule, + StoreModule.forFeature(STORE_FEATURE_DEFECTS_KEY, defectsReducer), + EffectsModule.forFeature([DefectsEffects]), + ], }) export class DefectsStateModule {} diff --git a/src/app/store/defects/effects/defects.effects.spec.ts b/src/app/store/defects/effects/defects.effects.spec.ts index a859d83739..c749d5cdc1 100644 --- a/src/app/store/defects/effects/defects.effects.spec.ts +++ b/src/app/store/defects/effects/defects.effects.spec.ts @@ -9,113 +9,117 @@ import { initialAppState } from '@store/.'; import { Observable } from 'rxjs'; import { TestScheduler } from 'rxjs/testing'; import { - fetchDefect, - fetchDefectFailed, - fetchDefects, - fetchDefectsFailed, - fetchDefectsSuccess, - fetchDefectSuccess, + fetchDefect, + fetchDefectFailed, + fetchDefectSuccess, + fetchDefects, + fetchDefectsFailed, + fetchDefectsSuccess, } from '../actions/defects.actions'; import { DefectsEffects } from './defects.effects'; describe('DefectsEffects', () => { - let effects: DefectsEffects; - let actions$ = new Observable(); - let testScheduler: TestScheduler; - let service: DefectsService; - - const expectedResult = { imNumber: 1 } as Defect; - const testCases = [ - { - id: expectedResult.imNumber, - payload: [expectedResult], - }, - ]; - - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [HttpClientTestingModule], - providers: [ - DefectsEffects, - provideMockActions(() => actions$), - DefectsService, - provideMockStore({ - initialState: initialAppState, - }), - ], - }); - - effects = TestBed.inject(DefectsEffects); - service = TestBed.inject(DefectsService); - }); - - beforeEach(() => { - testScheduler = new TestScheduler((actual, expected) => { - expect(actual).toEqual(expected); - }); - }); - - describe('fetchDefects$', () => { - it.each(testCases)('should return fetchDefectsSuccess action on successfull API call', (value) => { - testScheduler.run(({ hot, cold, expectObservable }) => { - const { payload } = value; - - // mock action to trigger effect - actions$ = hot('-a--', { a: fetchDefects() }); - - // mock service call - jest.spyOn(service, 'fetchDefects').mockReturnValue(cold('--a|', { a: payload })); - - // expect effect to return success action - expectObservable(effects.fetchDefects$).toBe('---b', { - b: fetchDefectsSuccess({ payload }), - }); - }); - }); - - it.each(testCases)('should return fetchDefectsFailed action on API error', () => { - testScheduler.run(({ hot, cold, expectObservable }) => { - actions$ = hot('-a--', { a: fetchDefects() }); - - const expectedError = new Error('Reference data resourceType is required'); - - jest.spyOn(service, 'fetchDefects').mockReturnValue(cold('--#|', {}, expectedError)); - - expectObservable(effects.fetchDefects$).toBe('---b', { b: fetchDefectsFailed({ error: 'Reference data resourceType is required' }) }); - }); - }); - }); - - describe('fetchDefect$', () => { - it.each(testCases)('should return fetchDefectSuccess action on successfull API call', (value) => { - testScheduler.run(({ hot, cold, expectObservable }) => { - const { id, payload } = value; - const entity = payload.find((d) => d.imNumber === id) as Defect; - - // mock action to trigger effect - actions$ = hot('-a--', { a: fetchDefect({ id }) }); - - // mock service call - jest.spyOn(service, 'fetchDefect').mockReturnValue(cold('--a|', { a: entity })); - - // expect effect to return success action - expectObservable(effects.fetchDefect$).toBe('---b', { - b: fetchDefectSuccess({ id, payload: entity }), - }); - }); - }); - - it.each(testCases)('should return fetchDefectFailed action on API error', (value) => { - testScheduler.run(({ hot, cold, expectObservable }) => { - const { id } = value; - actions$ = hot('-a--', { a: fetchDefect({ id }) }); - - const expectedError = new Error('Reference data resourceKey is required'); - - jest.spyOn(service, 'fetchDefect').mockReturnValue(cold('--#|', {}, expectedError)); - - expectObservable(effects.fetchDefect$).toBe('---b', { b: fetchDefectFailed({ error: 'Reference data resourceKey is required' }) }); - }); - }); - }); + let effects: DefectsEffects; + let actions$ = new Observable(); + let testScheduler: TestScheduler; + let service: DefectsService; + + const expectedResult = { imNumber: 1 } as Defect; + const testCases = [ + { + id: expectedResult.imNumber, + payload: [expectedResult], + }, + ]; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [HttpClientTestingModule], + providers: [ + DefectsEffects, + provideMockActions(() => actions$), + DefectsService, + provideMockStore({ + initialState: initialAppState, + }), + ], + }); + + effects = TestBed.inject(DefectsEffects); + service = TestBed.inject(DefectsService); + }); + + beforeEach(() => { + testScheduler = new TestScheduler((actual, expected) => { + expect(actual).toEqual(expected); + }); + }); + + describe('fetchDefects$', () => { + it.each(testCases)('should return fetchDefectsSuccess action on successfull API call', (value) => { + testScheduler.run(({ hot, cold, expectObservable }) => { + const { payload } = value; + + // mock action to trigger effect + actions$ = hot('-a--', { a: fetchDefects() }); + + // mock service call + jest.spyOn(service, 'fetchDefects').mockReturnValue(cold('--a|', { a: payload })); + + // expect effect to return success action + expectObservable(effects.fetchDefects$).toBe('---b', { + b: fetchDefectsSuccess({ payload }), + }); + }); + }); + + it.each(testCases)('should return fetchDefectsFailed action on API error', () => { + testScheduler.run(({ hot, cold, expectObservable }) => { + actions$ = hot('-a--', { a: fetchDefects() }); + + const expectedError = new Error('Reference data resourceType is required'); + + jest.spyOn(service, 'fetchDefects').mockReturnValue(cold('--#|', {}, expectedError)); + + expectObservable(effects.fetchDefects$).toBe('---b', { + b: fetchDefectsFailed({ error: 'Reference data resourceType is required' }), + }); + }); + }); + }); + + describe('fetchDefect$', () => { + it.each(testCases)('should return fetchDefectSuccess action on successfull API call', (value) => { + testScheduler.run(({ hot, cold, expectObservable }) => { + const { id, payload } = value; + const entity = payload.find((d) => d.imNumber === id) as Defect; + + // mock action to trigger effect + actions$ = hot('-a--', { a: fetchDefect({ id }) }); + + // mock service call + jest.spyOn(service, 'fetchDefect').mockReturnValue(cold('--a|', { a: entity })); + + // expect effect to return success action + expectObservable(effects.fetchDefect$).toBe('---b', { + b: fetchDefectSuccess({ id, payload: entity }), + }); + }); + }); + + it.each(testCases)('should return fetchDefectFailed action on API error', (value) => { + testScheduler.run(({ hot, cold, expectObservable }) => { + const { id } = value; + actions$ = hot('-a--', { a: fetchDefect({ id }) }); + + const expectedError = new Error('Reference data resourceKey is required'); + + jest.spyOn(service, 'fetchDefect').mockReturnValue(cold('--#|', {}, expectedError)); + + expectObservable(effects.fetchDefect$).toBe('---b', { + b: fetchDefectFailed({ error: 'Reference data resourceKey is required' }), + }); + }); + }); + }); }); diff --git a/src/app/store/defects/effects/defects.effects.ts b/src/app/store/defects/effects/defects.effects.ts index 8497779325..3593ffcf44 100644 --- a/src/app/store/defects/effects/defects.effects.ts +++ b/src/app/store/defects/effects/defects.effects.ts @@ -1,39 +1,42 @@ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { Actions, createEffect, ofType } from '@ngrx/effects'; import { DefectsService } from '@services/defects/defects.service'; +import { catchError, map, mergeMap, of } from 'rxjs'; import { - catchError, map, mergeMap, of, -} from 'rxjs'; -import { - fetchDefect, - fetchDefectFailed, - fetchDefects, - fetchDefectsFailed, - fetchDefectsSuccess, - fetchDefectSuccess, + fetchDefect, + fetchDefectFailed, + fetchDefectSuccess, + fetchDefects, + fetchDefectsFailed, + fetchDefectsSuccess, } from '../actions/defects.actions'; @Injectable() export class DefectsEffects { - fetchDefects$ = createEffect(() => - this.actions$.pipe( - ofType(fetchDefects), - mergeMap(() => - this.defectsService.fetchDefects().pipe( - map((defects) => fetchDefectsSuccess({ payload: defects })), - catchError((e) => of(fetchDefectsFailed({ error: e.message }))), - )), - )); + private actions$ = inject(Actions); + private defectsService = inject(DefectsService); - fetchDefect$ = createEffect(() => - this.actions$.pipe( - ofType(fetchDefect), - mergeMap(({ id }) => - this.defectsService.fetchDefect(id).pipe( - map((defect) => fetchDefectSuccess({ id, payload: defect })), - catchError((e) => of(fetchDefectFailed({ error: e.message }))), - )), - )); + fetchDefects$ = createEffect(() => + this.actions$.pipe( + ofType(fetchDefects), + mergeMap(() => + this.defectsService.fetchDefects().pipe( + map((defects) => fetchDefectsSuccess({ payload: defects })), + catchError((e) => of(fetchDefectsFailed({ error: e.message }))) + ) + ) + ) + ); - constructor(private actions$: Actions, private defectsService: DefectsService) {} + fetchDefect$ = createEffect(() => + this.actions$.pipe( + ofType(fetchDefect), + mergeMap(({ id }) => + this.defectsService.fetchDefect(id).pipe( + map((defect) => fetchDefectSuccess({ id, payload: defect })), + catchError((e) => of(fetchDefectFailed({ error: e.message }))) + ) + ) + ) + ); } diff --git a/src/app/store/defects/reducers/defects.reducer.spec.ts b/src/app/store/defects/reducers/defects.reducer.spec.ts index dd1ada9d0e..b14cdf0ea0 100644 --- a/src/app/store/defects/reducers/defects.reducer.spec.ts +++ b/src/app/store/defects/reducers/defects.reducer.spec.ts @@ -1,99 +1,99 @@ import { Defect } from '@models/defects/defect.model'; import { - fetchDefect, - fetchDefectFailed, - fetchDefects, - fetchDefectsFailed, - fetchDefectsSuccess, - fetchDefectSuccess, + fetchDefect, + fetchDefectFailed, + fetchDefectSuccess, + fetchDefects, + fetchDefectsFailed, + fetchDefectsSuccess, } from '../actions/defects.actions'; -import { defectsReducer, DefectsState, initialDefectsState } from './defects.reducer'; +import { DefectsState, defectsReducer, initialDefectsState } from './defects.reducer'; describe('Defects Reducer', () => { - const expectedDefects = [{ imNumber: 1, imDescription: 'some description' } as Defect]; + const expectedDefects = [{ imNumber: 1, imDescription: 'some description' } as Defect]; - describe('unknown action', () => { - it('should return the default state', () => { - const action = { - type: 'Unknown', - }; - const state = defectsReducer(initialDefectsState, action); + describe('unknown action', () => { + it('should return the default state', () => { + const action = { + type: 'Unknown', + }; + const state = defectsReducer(initialDefectsState, action); - expect(state).toBe(initialDefectsState); - }); - }); + expect(state).toBe(initialDefectsState); + }); + }); - describe('fetchDefects actions', () => { - it('should set loading to true', () => { - const newState: DefectsState = { ...initialDefectsState, loading: true }; - const action = fetchDefects(); - const state = defectsReducer(initialDefectsState, action); + describe('fetchDefects actions', () => { + it('should set loading to true', () => { + const newState: DefectsState = { ...initialDefectsState, loading: true }; + const action = fetchDefects(); + const state = defectsReducer(initialDefectsState, action); - expect(state).toEqual(newState); - expect(state).not.toBe(newState); - }); + expect(state).toEqual(newState); + expect(state).not.toBe(newState); + }); - describe('fetchDefectsSuccess', () => { - it('should set all test result records', () => { - const newState: DefectsState = { - ...initialDefectsState, - ids: ['1: some description'], - entities: { '1: some description': expectedDefects[0] }, - }; - const action = fetchDefectsSuccess({ payload: [...expectedDefects] }); - const state = defectsReducer(initialDefectsState, action); + describe('fetchDefectsSuccess', () => { + it('should set all test result records', () => { + const newState: DefectsState = { + ...initialDefectsState, + ids: ['1: some description'], + entities: { '1: some description': expectedDefects[0] }, + }; + const action = fetchDefectsSuccess({ payload: [...expectedDefects] }); + const state = defectsReducer(initialDefectsState, action); - expect(state).toEqual(newState); - expect(state).not.toBe(newState); - }); + expect(state).toEqual(newState); + expect(state).not.toBe(newState); + }); - describe('fetchDefectsFailed', () => { - it('should set error state', () => { - const newState = { ...initialDefectsState, loading: false }; - const action = fetchDefectsFailed({ error: 'unit testing error message' }); - const state = defectsReducer({ ...initialDefectsState, loading: true }, action); + describe('fetchDefectsFailed', () => { + it('should set error state', () => { + const newState = { ...initialDefectsState, loading: false }; + const action = fetchDefectsFailed({ error: 'unit testing error message' }); + const state = defectsReducer({ ...initialDefectsState, loading: true }, action); - expect(state).toEqual(newState); - expect(state).not.toBe(newState); - }); - }); - }); - }); + expect(state).toEqual(newState); + expect(state).not.toBe(newState); + }); + }); + }); + }); - describe('fetchDefect actions', () => { - it('should set loading to true', () => { - const newState: DefectsState = { ...initialDefectsState, loading: true }; - const action = fetchDefect({ id: 1 }); - const state = defectsReducer(initialDefectsState, action); + describe('fetchDefect actions', () => { + it('should set loading to true', () => { + const newState: DefectsState = { ...initialDefectsState, loading: true }; + const action = fetchDefect({ id: 1 }); + const state = defectsReducer(initialDefectsState, action); - expect(state).toEqual(newState); - expect(state).not.toBe(newState); - }); + expect(state).toEqual(newState); + expect(state).not.toBe(newState); + }); - describe('fetchDefectSuccess', () => { - it('should set all test result records', () => { - const newState: DefectsState = { - ...initialDefectsState, - ids: ['1: some description'], - entities: { '1: some description': expectedDefects[0] }, - }; - const action = fetchDefectSuccess({ id: 1, payload: expectedDefects[0] }); - const state = defectsReducer(initialDefectsState, action); + describe('fetchDefectSuccess', () => { + it('should set all test result records', () => { + const newState: DefectsState = { + ...initialDefectsState, + ids: ['1: some description'], + entities: { '1: some description': expectedDefects[0] }, + }; + const action = fetchDefectSuccess({ id: 1, payload: expectedDefects[0] }); + const state = defectsReducer(initialDefectsState, action); - expect(state).toEqual(newState); - expect(state).not.toBe(newState); - }); - }); + expect(state).toEqual(newState); + expect(state).not.toBe(newState); + }); + }); - describe('fetchDefectFailed', () => { - it('should set error state', () => { - const newState = { ...initialDefectsState, loading: false }; - const action = fetchDefectFailed({ error: 'unit testing error message' }); - const state = defectsReducer({ ...initialDefectsState, loading: true }, action); + describe('fetchDefectFailed', () => { + it('should set error state', () => { + const newState = { ...initialDefectsState, loading: false }; + const action = fetchDefectFailed({ error: 'unit testing error message' }); + const state = defectsReducer({ ...initialDefectsState, loading: true }, action); - expect(state).toEqual(newState); - expect(state).not.toBe(newState); - }); - }); - }); + expect(state).toEqual(newState); + expect(state).not.toBe(newState); + }); + }); + }); }); diff --git a/src/app/store/defects/reducers/defects.reducer.ts b/src/app/store/defects/reducers/defects.reducer.ts index c97ac8623f..ee377a4e04 100644 --- a/src/app/store/defects/reducers/defects.reducer.ts +++ b/src/app/store/defects/reducers/defects.reducer.ts @@ -1,18 +1,18 @@ import { Defect } from '@models/defects/defect.model'; -import { createEntityAdapter, EntityAdapter, EntityState } from '@ngrx/entity'; +import { EntityAdapter, EntityState, createEntityAdapter } from '@ngrx/entity'; import { createFeatureSelector, createReducer, on } from '@ngrx/store'; import { - fetchDefect, - fetchDefectFailed, - fetchDefects, - fetchDefectsFailed, - fetchDefectsSuccess, - fetchDefectSuccess, + fetchDefect, + fetchDefectFailed, + fetchDefectSuccess, + fetchDefects, + fetchDefectsFailed, + fetchDefectsSuccess, } from '../actions/defects.actions'; export interface DefectsState extends EntityState { - loading: boolean; - error: string; + loading: boolean; + error: string; } export const STORE_FEATURE_DEFECTS_KEY = 'Defects'; @@ -20,19 +20,19 @@ export const STORE_FEATURE_DEFECTS_KEY = 'Defects'; export const defectsFeatureState = createFeatureSelector(STORE_FEATURE_DEFECTS_KEY); export const defectsAdapter: EntityAdapter = createEntityAdapter({ - selectId: (defect) => `${defect.imNumber}: ${defect.imDescription}`, + selectId: (defect) => `${defect.imNumber}: ${defect.imDescription}`, }); export const initialDefectsState = defectsAdapter.getInitialState({ loading: false, error: '' }); export const defectsReducer = createReducer( - initialDefectsState, + initialDefectsState, - on(fetchDefects, (state) => ({ ...state, loading: true })), - on(fetchDefectsSuccess, (state, action) => ({ ...defectsAdapter.setAll(action.payload, state), loading: false })), - on(fetchDefectsFailed, (state) => ({ ...defectsAdapter.setAll([], state), loading: false })), + on(fetchDefects, (state) => ({ ...state, loading: true })), + on(fetchDefectsSuccess, (state, action) => ({ ...defectsAdapter.setAll(action.payload, state), loading: false })), + on(fetchDefectsFailed, (state) => ({ ...defectsAdapter.setAll([], state), loading: false })), - on(fetchDefect, (state) => ({ ...state, loading: true })), - on(fetchDefectSuccess, (state, action) => ({ ...defectsAdapter.upsertOne(action.payload, state), loading: false })), - on(fetchDefectFailed, (state) => ({ ...defectsAdapter.setAll([], state), loading: false })), + on(fetchDefect, (state) => ({ ...state, loading: true })), + on(fetchDefectSuccess, (state, action) => ({ ...defectsAdapter.upsertOne(action.payload, state), loading: false })), + on(fetchDefectFailed, (state) => ({ ...defectsAdapter.setAll([], state), loading: false })) ); diff --git a/src/app/store/defects/selectors/defects.selectors.spec.ts b/src/app/store/defects/selectors/defects.selectors.spec.ts index 416a621385..5fb66a9b81 100644 --- a/src/app/store/defects/selectors/defects.selectors.spec.ts +++ b/src/app/store/defects/selectors/defects.selectors.spec.ts @@ -4,85 +4,83 @@ import { Deficiency } from '@models/defects/deficiency.model'; import { Item } from '@models/defects/item.model'; import { VehicleTypes } from '@models/vehicle-tech-record.model'; import { DefectsState, initialDefectsState } from '../reducers/defects.reducer'; -import { - defect, defects, defectsLoadingState, selectByDeficiencyRef, selectByImNumber, -} from './defects.selectors'; +import { defect, defects, defectsLoadingState, selectByDeficiencyRef, selectByImNumber } from './defects.selectors'; describe('Defects Selectors', () => { - describe('adapter selectors', () => { - it('should return correct state', () => { - const state = { ...initialDefectsState, ids: [1], entities: { 1: { preparerId: 2 } } } as unknown as DefectsState; + describe('adapter selectors', () => { + it('should return correct state', () => { + const state = { ...initialDefectsState, ids: [1], entities: { 1: { preparerId: 2 } } } as unknown as DefectsState; - expect(defects.projector(state)).toEqual([{ preparerId: 2 }]); - expect(defect('1').projector(state)).toEqual({ preparerId: 2 }); - }); - }); + expect(defects.projector(state)).toEqual([{ preparerId: 2 }]); + expect(defect('1').projector(state)).toEqual({ preparerId: 2 }); + }); + }); - describe('testStationsLoadingState', () => { - it('should return loading state', () => { - const state: DefectsState = { ...initialDefectsState, loading: true }; - const selectedState = defectsLoadingState.projector(state); - expect(selectedState).toBeTruthy(); - }); - }); + describe('testStationsLoadingState', () => { + it('should return loading state', () => { + const state: DefectsState = { ...initialDefectsState, loading: true }; + const selectedState = defectsLoadingState.projector(state); + expect(selectedState).toBeTruthy(); + }); + }); - describe('should return correct state', () => { - const deficiency: Deficiency = { - deficiencyCategory: deficiencyCategory.Major, - deficiencyId: 'a', - deficiencySubId: '', - deficiencyText: 'missing.', - forVehicleType: [VehicleTypes.PSV], - ref: '1.1.a', - stdForProhibition: false, - }; + describe('should return correct state', () => { + const deficiency: Deficiency = { + deficiencyCategory: deficiencyCategory.Major, + deficiencyId: 'a', + deficiencySubId: '', + deficiencyText: 'missing.', + forVehicleType: [VehicleTypes.PSV], + ref: '1.1.a', + stdForProhibition: false, + }; - const item: Item = { - deficiencies: [deficiency], - forVehicleType: [VehicleTypes.PSV], - itemDescription: 'A registration plate:', - itemNumber: 1, - }; + const item: Item = { + deficiencies: [deficiency], + forVehicleType: [VehicleTypes.PSV], + itemDescription: 'A registration plate:', + itemNumber: 1, + }; - const mockDefect: Defect = { - additionalInfo: { - [VehicleTypes.PSV]: { - location: { - longitudinal: ['front', 'rear'], - }, - notes: false, - }, - }, - forVehicleType: [VehicleTypes.PSV], - imDescription: 'Registration Plate', - imNumber: 1, - items: [item], - }; - const defect2: Defect = { - additionalInfo: { - [VehicleTypes.PSV]: { - location: { - longitudinal: ['front', 'rear'], - }, - notes: false, - }, - }, - forVehicleType: [VehicleTypes.PSV], - imDescription: 'Registration Plate', - imNumber: 2, - items: [item], - }; + const mockDefect: Defect = { + additionalInfo: { + [VehicleTypes.PSV]: { + location: { + longitudinal: ['front', 'rear'], + }, + notes: false, + }, + }, + forVehicleType: [VehicleTypes.PSV], + imDescription: 'Registration Plate', + imNumber: 1, + items: [item], + }; + const defect2: Defect = { + additionalInfo: { + [VehicleTypes.PSV]: { + location: { + longitudinal: ['front', 'rear'], + }, + notes: false, + }, + }, + forVehicleType: [VehicleTypes.PSV], + imDescription: 'Registration Plate', + imNumber: 2, + items: [item], + }; - const defectList: Defect[] = [mockDefect, defect2]; + const defectList: Defect[] = [mockDefect, defect2]; - it('should return filtered defect by IM number', () => { - const selectedState = selectByImNumber(2, VehicleTypes.PSV).projector(defectList); - expect(selectedState).toEqual(defect2); - }); + it('should return filtered defect by IM number', () => { + const selectedState = selectByImNumber(2, VehicleTypes.PSV).projector(defectList); + expect(selectedState).toEqual(defect2); + }); - it('should return filtered defect by deficiency ref', () => { - const selectedState = selectByDeficiencyRef('1.1.a', VehicleTypes.PSV).projector(defectList); - expect(selectedState).toEqual([mockDefect, item, deficiency]); - }); - }); + it('should return filtered defect by deficiency ref', () => { + const selectedState = selectByDeficiencyRef('1.1.a', VehicleTypes.PSV).projector(defectList); + expect(selectedState).toEqual([mockDefect, item, deficiency]); + }); + }); }); diff --git a/src/app/store/defects/selectors/defects.selectors.ts b/src/app/store/defects/selectors/defects.selectors.ts index 92baa9b415..72e90af203 100644 --- a/src/app/store/defects/selectors/defects.selectors.ts +++ b/src/app/store/defects/selectors/defects.selectors.ts @@ -11,53 +11,56 @@ const { selectAll } = defectsAdapter.getSelectors(); export const defects = createSelector(defectsFeatureState, (state) => selectAll(state)); export const filteredDefects = (type: VehicleTypes) => - createSelector(defects, (defectList) => { - return cloneDeep(defectList) - .filter((defect) => defect.forVehicleType.includes(type)) - .map((defect) => ({ - ...defect, - items: defect.items - .filter((item) => item.forVehicleType.includes(type)) - .map((item) => ({ - ...item, - deficiencies: item.deficiencies.filter((deficiency) => deficiency.forVehicleType.includes(type)), - })), - })); - }); + createSelector(defects, (defectList) => { + return cloneDeep(defectList) + .filter((defect) => defect.forVehicleType.includes(type)) + .map((defect) => ({ + ...defect, + items: defect.items + .filter((item) => item.forVehicleType.includes(type)) + .map((item) => ({ + ...item, + deficiencies: item.deficiencies.filter((deficiency) => deficiency.forVehicleType.includes(type)), + })), + })); + }); export const selectByImNumber = (imNumber: number, vehicleType: VehicleTypes) => - createSelector(filteredDefects(vehicleType), (defectsList) => defectsList.find((defect) => defect.imNumber === imNumber)); + createSelector(filteredDefects(vehicleType), (defectsList) => + defectsList.find((defect) => defect.imNumber === imNumber) + ); export const selectByDeficiencyRef = (deficiencyRef: string, vehicleType: VehicleTypes) => - createSelector(filteredDefects(vehicleType), (defectsList) => { - const deRef = deficiencyRef.split('.'); - const isAdvisory: boolean = deRef[2] === 'advisory'; - let defect: Defect | undefined; let item: Item | undefined; let - deficiency: Deficiency | undefined; + createSelector(filteredDefects(vehicleType), (defectsList) => { + const deRef = deficiencyRef.split('.'); + const isAdvisory: boolean = deRef[2] === 'advisory'; + let defect: Defect | undefined; + let item: Item | undefined; + let deficiency: Deficiency | undefined; - if (deRef) { - defect = defectsList.find((d) => d.imNumber === +deRef[0]); - const items = defect?.items.filter((i) => i.itemNumber === +deRef[1]); + if (deRef) { + defect = defectsList.find((d) => d.imNumber === +deRef[0]); + const items = defect?.items.filter((i) => i.itemNumber === +deRef[1]); - // eslint-disable-next-line @typescript-eslint/no-unused-expressions, no-unused-expressions - !isAdvisory - && items?.forEach((itm) => { - const defRef: Deficiency | undefined = itm.deficiencies.find((d) => d.ref === deficiencyRef); - if (defRef) { - item = itm; - deficiency = defRef; - } - }); + // eslint-disable-next-line @typescript-eslint/no-unused-expressions, no-unused-expressions + !isAdvisory && + items?.forEach((itm) => { + const defRef: Deficiency | undefined = itm.deficiencies.find((d) => d.ref === deficiencyRef); + if (defRef) { + item = itm; + deficiency = defRef; + } + }); - if (!deficiency && isAdvisory && deRef[3]) { - item = items?.[+deRef[3]]; - } else { - item = defect?.items.find((i) => i.itemNumber === +deRef[1]); - } - } + if (!deficiency && isAdvisory && deRef[3]) { + item = items?.[+deRef[3]]; + } else { + item = defect?.items.find((i) => i.itemNumber === +deRef[1]); + } + } - return [defect, item, deficiency]; - }); + return [defect, item, deficiency]; + }); export const psvDefects = filteredDefects(VehicleTypes.PSV); diff --git a/src/app/store/global-error/global-error-state.module.ts b/src/app/store/global-error/global-error-state.module.ts index ebed6b840e..6c9366a608 100644 --- a/src/app/store/global-error/global-error-state.module.ts +++ b/src/app/store/global-error/global-error-state.module.ts @@ -1,10 +1,13 @@ -import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; +import { NgModule } from '@angular/core'; import { StoreModule } from '@ngrx/store'; -import { globalErrorReducer, STORE_FEATURE_GLOBAL_ERROR_KEY } from '@store/global-error/reducers/global-error-service.reducer'; +import { + STORE_FEATURE_GLOBAL_ERROR_KEY, + globalErrorReducer, +} from '@store/global-error/reducers/global-error-service.reducer'; @NgModule({ - declarations: [], - imports: [CommonModule, StoreModule.forFeature(STORE_FEATURE_GLOBAL_ERROR_KEY, globalErrorReducer)], + declarations: [], + imports: [CommonModule, StoreModule.forFeature(STORE_FEATURE_GLOBAL_ERROR_KEY, globalErrorReducer)], }) export class GlobalErrorStateModule {} diff --git a/src/app/store/global-error/reducers/global-error-service.reducer.spec.ts b/src/app/store/global-error/reducers/global-error-service.reducer.spec.ts index 75303f47ae..47d2af39a1 100644 --- a/src/app/store/global-error/reducers/global-error-service.reducer.spec.ts +++ b/src/app/store/global-error/reducers/global-error-service.reducer.spec.ts @@ -1,85 +1,98 @@ -import { routerNavigatedAction, RouterNavigatedPayload, SerializedRouterStateSnapshot } from '@ngrx/router-store'; -import { globalErrorReducer, GlobalErrorState, initialGlobalErrorState } from '@store/global-error/reducers/global-error-service.reducer'; +import { RouterNavigatedPayload, SerializedRouterStateSnapshot, routerNavigatedAction } from '@ngrx/router-store'; import { - fetchTestResults, fetchTestResultsBySystemNumber, fetchTestResultsBySystemNumberFailed, fetchTestResultsFailed, -} from '@store/test-records'; + GlobalErrorState, + globalErrorReducer, + initialGlobalErrorState, +} from '@store/global-error/reducers/global-error-service.reducer'; import { fetchSearchResultFailed } from '@store/tech-record-search/actions/tech-record-search.actions'; +import { + fetchTestResults, + fetchTestResultsBySystemNumber, + fetchTestResultsBySystemNumberFailed, + fetchTestResultsFailed, +} from '@store/test-records'; import { patchErrors, setErrors } from '../actions/global-error.actions'; describe('Global Error Reducer', () => { - describe('unknown action', () => { - it('should return the default state', () => { - const action = { - type: 'Unknown', - }; + describe('unknown action', () => { + it('should return the default state', () => { + const action = { + type: 'Unknown', + }; - const state = globalErrorReducer(initialGlobalErrorState, action); - expect(state).toBe(initialGlobalErrorState); - }); - }); + const state = globalErrorReducer(initialGlobalErrorState, action); + expect(state).toBe(initialGlobalErrorState); + }); + }); - describe('Fail action', () => { - it.each([fetchTestResultsBySystemNumberFailed, fetchTestResultsFailed, fetchSearchResultFailed])( - 'should return the error state', - (actionMethod) => { - const error = 'fetching test records failed'; - const newState: GlobalErrorState = { ...initialGlobalErrorState, errors: [{ error, anchorLink: undefined }] }; - const action = actionMethod({ error }); - const state = globalErrorReducer(initialGlobalErrorState, action); + describe('Fail action', () => { + it.each([fetchTestResultsBySystemNumberFailed, fetchTestResultsFailed, fetchSearchResultFailed])( + 'should return the error state', + (actionMethod) => { + const error = 'fetching test records failed'; + const newState: GlobalErrorState = { ...initialGlobalErrorState, errors: [{ error, anchorLink: undefined }] }; + const action = actionMethod({ error }); + const state = globalErrorReducer(initialGlobalErrorState, action); - expect(state).toEqual(newState); - expect(state).not.toBe(newState); - }, - ); - }); + expect(state).toEqual(newState); + expect(state).not.toBe(newState); + } + ); + }); - describe('Success action', () => { - it.each([fetchTestResultsBySystemNumber, fetchTestResults])('should reset the error state', (actionMethod) => { - const newState = { ...initialGlobalErrorState, errors: [] }; - // all props must be supplied here - const action = actionMethod({ systemNumber: '' }); - const state = globalErrorReducer(initialGlobalErrorState, action); + describe('Success action', () => { + it.each([fetchTestResultsBySystemNumber, fetchTestResults])('should reset the error state', (actionMethod) => { + const newState = { ...initialGlobalErrorState, errors: [] }; + // all props must be supplied here + const action = actionMethod({ systemNumber: '' }); + const state = globalErrorReducer(initialGlobalErrorState, action); - expect(state).toEqual(newState); - expect(state).not.toBe(newState); - }); + expect(state).toEqual(newState); + expect(state).not.toBe(newState); + }); - it.each([routerNavigatedAction])('should reset the error state', (actionMethod) => { - const newState = { ...initialGlobalErrorState, errors: [] }; - // all props must be supplied here - const action = actionMethod({ payload: >{} }); - const state = globalErrorReducer(initialGlobalErrorState, action); + it.each([routerNavigatedAction])('should reset the error state', (actionMethod) => { + const newState = { ...initialGlobalErrorState, errors: [] }; + // all props must be supplied here + const action = actionMethod({ payload: >{} }); + const state = globalErrorReducer(initialGlobalErrorState, action); - expect(state).toEqual(newState); - expect(state).not.toBe(newState); - }); - }); + expect(state).toEqual(newState); + expect(state).not.toBe(newState); + }); + }); - describe('setErrors', () => { - it('should replace existing errors with new ones', () => { - const newState = { ...initialGlobalErrorState, errors: [{ error: 'some error', anchorLink: '' }] }; - const action = setErrors({ errors: [{ error: 'some error', anchorLink: '' }] }); - const state = globalErrorReducer({ ...initialGlobalErrorState, errors: [{ error: 'old error', anchorLink: '' }] }, action); + describe('setErrors', () => { + it('should replace existing errors with new ones', () => { + const newState = { ...initialGlobalErrorState, errors: [{ error: 'some error', anchorLink: '' }] }; + const action = setErrors({ errors: [{ error: 'some error', anchorLink: '' }] }); + const state = globalErrorReducer( + { ...initialGlobalErrorState, errors: [{ error: 'old error', anchorLink: '' }] }, + action + ); - expect(state).toEqual(newState); - expect(state).not.toBe(newState); - }); + expect(state).toEqual(newState); + expect(state).not.toBe(newState); + }); - it('should add new errors after existing ones', () => { - const newState = { - ...initialGlobalErrorState, - errors: [ - { error: 'old error', anchorLink: '' }, - { error: 'new error', anchorLink: '' }, - ], - }; - const action = patchErrors({ - errors: [{ error: 'new error', anchorLink: '' }], - }); - const state = globalErrorReducer({ ...initialGlobalErrorState, errors: [{ error: 'old error', anchorLink: '' }] }, action); + it('should add new errors after existing ones', () => { + const newState = { + ...initialGlobalErrorState, + errors: [ + { error: 'old error', anchorLink: '' }, + { error: 'new error', anchorLink: '' }, + ], + }; + const action = patchErrors({ + errors: [{ error: 'new error', anchorLink: '' }], + }); + const state = globalErrorReducer( + { ...initialGlobalErrorState, errors: [{ error: 'old error', anchorLink: '' }] }, + action + ); - expect(state).toEqual(newState); - expect(state).not.toBe(newState); - }); - }); + expect(state).toEqual(newState); + expect(state).not.toBe(newState); + }); + }); }); diff --git a/src/app/store/global-error/reducers/global-error-service.reducer.ts b/src/app/store/global-error/reducers/global-error-service.reducer.ts index 163d2d3ac1..5591946fea 100644 --- a/src/app/store/global-error/reducers/global-error-service.reducer.ts +++ b/src/app/store/global-error/reducers/global-error-service.reducer.ts @@ -1,9 +1,10 @@ import { GlobalError } from '@core/components/global-error/global-error.interface'; import { routerNavigatedAction } from '@ngrx/router-store'; +import { createFeatureSelector, createReducer, createSelector, on } from '@ngrx/store'; import { - createFeatureSelector, createReducer, createSelector, on, -} from '@ngrx/store'; -import { fetchSearchResult, fetchSearchResultFailed } from '@store/tech-record-search/actions/tech-record-search.actions'; + fetchSearchResult, + fetchSearchResultFailed, +} from '@store/tech-record-search/actions/tech-record-search.actions'; // eslint-disable-next-line import/no-cycle import * as TestResultActions from '@store/test-records'; import _ from 'lodash'; @@ -15,11 +16,11 @@ import * as GlobalErrorActions from '../actions/global-error.actions'; export const STORE_FEATURE_GLOBAL_ERROR_KEY = 'globalError'; export interface GlobalErrorState { - errors: Array; + errors: Array; } export const initialGlobalErrorState: GlobalErrorState = { - errors: [], + errors: [], }; export const getGlobalErrorState = createFeatureSelector(STORE_FEATURE_GLOBAL_ERROR_KEY); @@ -27,49 +28,54 @@ export const getGlobalErrorState = createFeatureSelector(STORE export const globalErrorState = createSelector(getGlobalErrorState, (state) => state.errors); export const globalErrorReducer = createReducer( - initialGlobalErrorState, - on( - GlobalErrorActions.clearError, - TechnicalRecordServiceActions.updateTechRecord, - TestResultActions.fetchTestResults, - TestResultActions.fetchTestResultsBySystemNumber, - TestResultActions.fetchSelectedTestResult, - ReferenceDataActions.fetchReferenceData, - ReferenceDataActions.fetchReferenceDataByKey, - fetchSearchResult, - routerNavigatedAction, - successMethod, - ), + initialGlobalErrorState, + on( + GlobalErrorActions.clearError, + TechnicalRecordServiceActions.updateTechRecord, + TestResultActions.fetchTestResults, + TestResultActions.fetchTestResultsBySystemNumber, + TestResultActions.fetchSelectedTestResult, + ReferenceDataActions.fetchReferenceData, + ReferenceDataActions.fetchReferenceDataByKey, + fetchSearchResult, + routerNavigatedAction, + successMethod + ), - on( - GlobalErrorActions.addError, - TechnicalRecordServiceActions.updateTechRecordFailure, - TechnicalRecordServiceActions.generateLetterFailure, - TechnicalRecordServiceActions.generatePlateFailure, - TechnicalRecordServiceActions.generateADRCertificateFailure, - TechnicalRecordServiceActions.archiveTechRecordFailure, - TechnicalRecordServiceActions.unarchiveTechRecordFailure, - TestResultActions.fetchTestResultsFailed, - TestResultActions.fetchTestResultsBySystemNumberFailed, - TestResultActions.fetchSelectedTestResultFailed, - ReferenceDataActions.fetchReferenceDataFailed, - ReferenceDataActions.fetchReferenceDataByKeyFailed, - fetchSearchResultFailed, - failureMethod, - ), - on(GlobalErrorActions.setErrors, TestResultActions.updateTestResultFailed, TestResultActions.createTestResultFailed, (state, { errors }) => ({ - ...state, - errors: [...(_.uniqWith(errors, (a, b) => _.isEqual(a.error, b.error)))], - })), - on(GlobalErrorActions.patchErrors, (state, { errors }) => ({ ...state, errors: [...state.errors, ...errors] })), - on(createVehicleRecordFailure, (state, action) => ({ errors: [...state.errors, { error: action.error }] })), + on( + GlobalErrorActions.addError, + TechnicalRecordServiceActions.updateTechRecordFailure, + TechnicalRecordServiceActions.generateLetterFailure, + TechnicalRecordServiceActions.generatePlateFailure, + TechnicalRecordServiceActions.generateADRCertificateFailure, + TechnicalRecordServiceActions.archiveTechRecordFailure, + TechnicalRecordServiceActions.unarchiveTechRecordFailure, + TestResultActions.fetchTestResultsFailed, + TestResultActions.fetchTestResultsBySystemNumberFailed, + TestResultActions.fetchSelectedTestResultFailed, + ReferenceDataActions.fetchReferenceDataFailed, + ReferenceDataActions.fetchReferenceDataByKeyFailed, + fetchSearchResultFailed, + failureMethod + ), + on( + GlobalErrorActions.setErrors, + TestResultActions.updateTestResultFailed, + TestResultActions.createTestResultFailed, + (state, { errors }) => ({ + ...state, + errors: [..._.uniqWith(errors, (a, b) => _.isEqual(a.error, b.error))], + }) + ), + on(GlobalErrorActions.patchErrors, (state, { errors }) => ({ ...state, errors: [...state.errors, ...errors] })), + on(createVehicleRecordFailure, (state, action) => ({ errors: [...state.errors, { error: action.error }] })) ); function successMethod(state: GlobalErrorState) { - return { ...state, errors: [] }; + return { ...state, errors: [] }; } // eslint-disable-next-line @typescript-eslint/no-explicit-any function failureMethod(state: GlobalErrorState, errorMessage: { error: any; anchorLink?: any }) { - return { ...state, errors: [...state.errors, { error: errorMessage.error, anchorLink: errorMessage.anchorLink }] }; + return { ...state, errors: [...state.errors, { error: errorMessage.error, anchorLink: errorMessage.anchorLink }] }; } diff --git a/src/app/store/global-error/selectors/global-error.selectors.spec.ts b/src/app/store/global-error/selectors/global-error.selectors.spec.ts index 3a5c13eb0b..f92dff0ba5 100644 --- a/src/app/store/global-error/selectors/global-error.selectors.spec.ts +++ b/src/app/store/global-error/selectors/global-error.selectors.spec.ts @@ -2,11 +2,11 @@ import { GlobalErrorState, initialGlobalErrorState } from '@store/global-error/r import { getErrorMessage } from '@store/global-error/selectors/global-error.selectors'; describe('Global Error Selectors', () => { - describe('getErrorMessage', () => { - it('should return the correct error', () => { - const state: GlobalErrorState = { ...initialGlobalErrorState, errors: [{ error: 'err' }] }; - const selectedState = getErrorMessage.projector(state); - expect(selectedState).toEqual(state.errors); - }); - }); + describe('getErrorMessage', () => { + it('should return the correct error', () => { + const state: GlobalErrorState = { ...initialGlobalErrorState, errors: [{ error: 'err' }] }; + const selectedState = getErrorMessage.projector(state); + expect(selectedState).toEqual(state.errors); + }); + }); }); diff --git a/src/app/store/global-error/selectors/global-error.selectors.ts b/src/app/store/global-error/selectors/global-error.selectors.ts index 17d5d1975c..d55d836426 100644 --- a/src/app/store/global-error/selectors/global-error.selectors.ts +++ b/src/app/store/global-error/selectors/global-error.selectors.ts @@ -2,5 +2,5 @@ import { createSelector } from '@ngrx/store'; import { getGlobalErrorState } from '@store/global-error/reducers/global-error-service.reducer'; export const getErrorMessage = createSelector(getGlobalErrorState, (state) => { - return state.errors; + return state.errors; }); diff --git a/src/app/store/global-warning/actions/global-warning.actions.ts b/src/app/store/global-warning/actions/global-warning.actions.ts index 5a2baa8739..21a6d87d61 100644 --- a/src/app/store/global-warning/actions/global-warning.actions.ts +++ b/src/app/store/global-warning/actions/global-warning.actions.ts @@ -2,4 +2,7 @@ import { GlobalWarning } from '@core/components/global-warning/global-warning.in import { createAction, props } from '@ngrx/store'; export const clearWarning = createAction('[Global Warning Service] Clear Warning'); -export const setWarnings = createAction('[Global Warning Service] Set warnings', props<{ warnings: GlobalWarning[] }>()); +export const setWarnings = createAction( + '[Global Warning Service] Set warnings', + props<{ warnings: GlobalWarning[] }>() +); diff --git a/src/app/store/global-warning/global-warning-state.module.ts b/src/app/store/global-warning/global-warning-state.module.ts index 91df5c65c7..ce1567e72d 100644 --- a/src/app/store/global-warning/global-warning-state.module.ts +++ b/src/app/store/global-warning/global-warning-state.module.ts @@ -1,10 +1,13 @@ -import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; +import { NgModule } from '@angular/core'; import { StoreModule } from '@ngrx/store'; -import { globalWarningReducer, STORE_FEATURE_GLOBAL_WARNING_KEY } from '@store/global-warning/reducers/global-warning-service.reducers'; +import { + STORE_FEATURE_GLOBAL_WARNING_KEY, + globalWarningReducer, +} from '@store/global-warning/reducers/global-warning-service.reducers'; @NgModule({ - declarations: [], - imports: [CommonModule, StoreModule.forFeature(STORE_FEATURE_GLOBAL_WARNING_KEY, globalWarningReducer)], + declarations: [], + imports: [CommonModule, StoreModule.forFeature(STORE_FEATURE_GLOBAL_WARNING_KEY, globalWarningReducer)], }) export class GlobalWarningStateModule {} diff --git a/src/app/store/global-warning/reducers/global-warning-service.reducer.spec.ts b/src/app/store/global-warning/reducers/global-warning-service.reducer.spec.ts index cab1a25cdb..7cab4b262b 100644 --- a/src/app/store/global-warning/reducers/global-warning-service.reducer.spec.ts +++ b/src/app/store/global-warning/reducers/global-warning-service.reducer.spec.ts @@ -1,49 +1,55 @@ -import { routerNavigatedAction, RouterNavigatedPayload, SerializedRouterStateSnapshot } from '@ngrx/router-store'; -import { globalWarningReducer, initialGlobalWarningState } from '@store/global-warning/reducers/global-warning-service.reducers'; -import { setWarnings, clearWarning } from '../actions/global-warning.actions'; +import { RouterNavigatedPayload, SerializedRouterStateSnapshot, routerNavigatedAction } from '@ngrx/router-store'; +import { + globalWarningReducer, + initialGlobalWarningState, +} from '@store/global-warning/reducers/global-warning-service.reducers'; +import { clearWarning, setWarnings } from '../actions/global-warning.actions'; describe('Global Warning Reducer', () => { - describe('unknown action', () => { - it('should return the default state', () => { - const action = { - type: 'Unknown', - }; - - const state = globalWarningReducer(initialGlobalWarningState, action); - expect(state).toBe(initialGlobalWarningState); - }); - }); - - describe('Success action', () => { - it.each([clearWarning])('should reset the warning state', (actionMethod) => { - const newState = { ...initialGlobalWarningState, warnings: [] }; - // all props must be supplied here - const action = actionMethod(); - const state = globalWarningReducer(initialGlobalWarningState, action); - - expect(state).toEqual(newState); - expect(state).not.toBe(newState); - }); - - it.each([routerNavigatedAction])('should reset the warning state', (actionMethod) => { - const newState = { ...initialGlobalWarningState, warnings: [] }; - // all props must be supplied here - const action = actionMethod({ payload: >{} }); - const state = globalWarningReducer(initialGlobalWarningState, action); - - expect(state).toEqual(newState); - expect(state).not.toBe(newState); - }); - }); - - describe('setWarnings', () => { - it('should replace existing warnings with new ones', () => { - const newState = { ...initialGlobalWarningState, warnings: [{ warning: 'some warning', anchorLink: '' }] }; - const action = setWarnings({ warnings: [{ warning: 'some warning', anchorLink: '' }] }); - const state = globalWarningReducer({ ...initialGlobalWarningState, warnings: [{ warning: 'old warning', anchorLink: '' }] }, action); - - expect(state).toEqual(newState); - expect(state).not.toBe(newState); - }); - }); + describe('unknown action', () => { + it('should return the default state', () => { + const action = { + type: 'Unknown', + }; + + const state = globalWarningReducer(initialGlobalWarningState, action); + expect(state).toBe(initialGlobalWarningState); + }); + }); + + describe('Success action', () => { + it.each([clearWarning])('should reset the warning state', (actionMethod) => { + const newState = { ...initialGlobalWarningState, warnings: [] }; + // all props must be supplied here + const action = actionMethod(); + const state = globalWarningReducer(initialGlobalWarningState, action); + + expect(state).toEqual(newState); + expect(state).not.toBe(newState); + }); + + it.each([routerNavigatedAction])('should reset the warning state', (actionMethod) => { + const newState = { ...initialGlobalWarningState, warnings: [] }; + // all props must be supplied here + const action = actionMethod({ payload: >{} }); + const state = globalWarningReducer(initialGlobalWarningState, action); + + expect(state).toEqual(newState); + expect(state).not.toBe(newState); + }); + }); + + describe('setWarnings', () => { + it('should replace existing warnings with new ones', () => { + const newState = { ...initialGlobalWarningState, warnings: [{ warning: 'some warning', anchorLink: '' }] }; + const action = setWarnings({ warnings: [{ warning: 'some warning', anchorLink: '' }] }); + const state = globalWarningReducer( + { ...initialGlobalWarningState, warnings: [{ warning: 'old warning', anchorLink: '' }] }, + action + ); + + expect(state).toEqual(newState); + expect(state).not.toBe(newState); + }); + }); }); diff --git a/src/app/store/global-warning/reducers/global-warning-service.reducers.ts b/src/app/store/global-warning/reducers/global-warning-service.reducers.ts index 71d18c2c29..92465c28cc 100644 --- a/src/app/store/global-warning/reducers/global-warning-service.reducers.ts +++ b/src/app/store/global-warning/reducers/global-warning-service.reducers.ts @@ -1,18 +1,16 @@ -import { routerNavigatedAction } from '@ngrx/router-store'; -import { - createFeatureSelector, createReducer, createSelector, on, -} from '@ngrx/store'; import { GlobalWarning } from '@core/components/global-warning/global-warning.interface'; +import { routerNavigatedAction } from '@ngrx/router-store'; +import { createFeatureSelector, createReducer, createSelector, on } from '@ngrx/store'; import * as GlobalWarningActions from '../actions/global-warning.actions'; export const STORE_FEATURE_GLOBAL_WARNING_KEY = 'globalWarning'; export interface GlobalWarningState { - warnings: Array; + warnings: Array; } export const initialGlobalWarningState: GlobalWarningState = { - warnings: [], + warnings: [], }; export const getGlobalWarningState = createFeatureSelector(STORE_FEATURE_GLOBAL_WARNING_KEY); @@ -20,20 +18,20 @@ export const getGlobalWarningState = createFeatureSelector(S export const globalWarningState = createSelector(getGlobalWarningState, (state) => state.warnings); export const globalWarningReducer = createReducer( - initialGlobalWarningState, - on( - GlobalWarningActions.clearWarning, - routerNavigatedAction, - - successMethod, - ), - - on(GlobalWarningActions.setWarnings, (state, { warnings }) => ({ - ...state, - warnings: [...warnings], - })), + initialGlobalWarningState, + on( + GlobalWarningActions.clearWarning, + routerNavigatedAction, + + successMethod + ), + + on(GlobalWarningActions.setWarnings, (state, { warnings }) => ({ + ...state, + warnings: [...warnings], + })) ); function successMethod(state: GlobalWarningState) { - return { ...state, warnings: [] }; + return { ...state, warnings: [] }; } diff --git a/src/app/store/index.ts b/src/app/store/index.ts index 1b7d8044cb..86b1c06b1c 100644 --- a/src/app/store/index.ts +++ b/src/app/store/index.ts @@ -1,88 +1,107 @@ import { ActionReducerMap } from '@ngrx/store'; // eslint-disable-next-line import/no-cycle import { - globalErrorReducer, - GlobalErrorState, - initialGlobalErrorState, - STORE_FEATURE_GLOBAL_ERROR_KEY, + GlobalErrorState, + STORE_FEATURE_GLOBAL_ERROR_KEY, + globalErrorReducer, + initialGlobalErrorState, } from '@store/global-error/reducers/global-error-service.reducer'; import { - initialSpinnerState, spinnerReducer, SpinnerState, STORE_FEATURE_SPINNER_KEY, + STORE_FEATURE_SPINNER_KEY, + SpinnerState, + initialSpinnerState, + spinnerReducer, } from '@store/spinner/reducers/spinner.reducer'; +import { DefectsState, STORE_FEATURE_DEFECTS_KEY, defectsReducer, initialDefectsState } from './defects'; import { - defectsReducer, DefectsState, initialDefectsState, STORE_FEATURE_DEFECTS_KEY, -} from './defects'; -import { - initialReferenceDataState, referenceDataReducer, ReferenceDataState, STORE_FEATURE_REFERENCE_DATA_KEY, + ReferenceDataState, + STORE_FEATURE_REFERENCE_DATA_KEY, + initialReferenceDataState, + referenceDataReducer, } from './reference-data'; import { - initialRequiredStandardsState, requiredStandardsReducer, RequiredStandardState, STORE_FEATURE_REQUIRED_STANDARDS_KEY, + RequiredStandardState, + STORE_FEATURE_REQUIRED_STANDARDS_KEY, + initialRequiredStandardsState, + requiredStandardsReducer, } from './required-standards/reducers/required-standards.reducer'; import { - initialTechSearchResultState, - SearchResultState, - STORE_FEATURE_SEARCH_TECH_RESULTS_KEY, - techSearchResultReducer, + STORE_FEATURE_SEARCH_TECH_RESULTS_KEY, + SearchResultState, + initialTechSearchResultState, + techSearchResultReducer, } from './tech-record-search/reducer/tech-record-search.reducer'; import { - initialState as initialTechnicalRecordsState, - STORE_FEATURE_TECHNICAL_RECORDS_KEY, - TechnicalRecordServiceState, - vehicleTechRecordReducer, + STORE_FEATURE_TECHNICAL_RECORDS_KEY, + TechnicalRecordServiceState, + initialState as initialTechnicalRecordsState, + vehicleTechRecordReducer, } from './technical-records/reducers/technical-record-service.reducer'; import { - initialTestResultsState, STORE_FEATURE_TEST_RESULTS_KEY, testResultsReducer, TestResultsState, + STORE_FEATURE_TEST_RESULTS_KEY, + TestResultsState, + initialTestResultsState, + testResultsReducer, } from './test-records'; import { - initialTestStationsState, STORE_FEATURE_TEST_STATIONS_KEY, testStationsReducer, TestStationsState, + STORE_FEATURE_TEST_STATIONS_KEY, + TestStationsState, + initialTestStationsState, + testStationsReducer, } from './test-stations'; import { - initialTestTypeState, STORE_FEATURE_TEST_TYPES_KEY, testTypesReducer, TestTypeState, + STORE_FEATURE_TEST_TYPES_KEY, + TestTypeState, + initialTestTypeState, + testTypesReducer, } from './test-types/reducers/test-types.reducer'; import { - initialState as initialUserState, STORE_FEATURE_USER_KEY, userServiceReducer, UserServiceState, + STORE_FEATURE_USER_KEY, + UserServiceState, + initialState as initialUserState, + userServiceReducer, } from './user/user-service.reducer'; export interface State { - [STORE_FEATURE_DEFECTS_KEY]: DefectsState; - [STORE_FEATURE_GLOBAL_ERROR_KEY]: GlobalErrorState; - [STORE_FEATURE_REFERENCE_DATA_KEY]: ReferenceDataState; - [STORE_FEATURE_SPINNER_KEY]: SpinnerState; - [STORE_FEATURE_TECHNICAL_RECORDS_KEY]: TechnicalRecordServiceState; - [STORE_FEATURE_TEST_RESULTS_KEY]: TestResultsState; - [STORE_FEATURE_TEST_STATIONS_KEY]: TestStationsState; - [STORE_FEATURE_TEST_TYPES_KEY]: TestTypeState; - [STORE_FEATURE_USER_KEY]: UserServiceState; - [STORE_FEATURE_SEARCH_TECH_RESULTS_KEY]: SearchResultState; - [STORE_FEATURE_REQUIRED_STANDARDS_KEY]: RequiredStandardState; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - router?: any; + [STORE_FEATURE_DEFECTS_KEY]: DefectsState; + [STORE_FEATURE_GLOBAL_ERROR_KEY]: GlobalErrorState; + [STORE_FEATURE_REFERENCE_DATA_KEY]: ReferenceDataState; + [STORE_FEATURE_SPINNER_KEY]: SpinnerState; + [STORE_FEATURE_TECHNICAL_RECORDS_KEY]: TechnicalRecordServiceState; + [STORE_FEATURE_TEST_RESULTS_KEY]: TestResultsState; + [STORE_FEATURE_TEST_STATIONS_KEY]: TestStationsState; + [STORE_FEATURE_TEST_TYPES_KEY]: TestTypeState; + [STORE_FEATURE_USER_KEY]: UserServiceState; + [STORE_FEATURE_SEARCH_TECH_RESULTS_KEY]: SearchResultState; + [STORE_FEATURE_REQUIRED_STANDARDS_KEY]: RequiredStandardState; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + router?: any; } export const initialAppState = { - [STORE_FEATURE_DEFECTS_KEY]: initialDefectsState, - [STORE_FEATURE_GLOBAL_ERROR_KEY]: initialGlobalErrorState, - [STORE_FEATURE_REFERENCE_DATA_KEY]: initialReferenceDataState, - [STORE_FEATURE_SPINNER_KEY]: initialSpinnerState, - [STORE_FEATURE_TECHNICAL_RECORDS_KEY]: initialTechnicalRecordsState, - [STORE_FEATURE_TEST_RESULTS_KEY]: initialTestResultsState, - [STORE_FEATURE_TEST_STATIONS_KEY]: initialTestStationsState, - [STORE_FEATURE_TEST_TYPES_KEY]: initialTestTypeState, - [STORE_FEATURE_USER_KEY]: initialUserState, - [STORE_FEATURE_SEARCH_TECH_RESULTS_KEY]: initialTechSearchResultState, - [STORE_FEATURE_REQUIRED_STANDARDS_KEY]: initialRequiredStandardsState, + [STORE_FEATURE_DEFECTS_KEY]: initialDefectsState, + [STORE_FEATURE_GLOBAL_ERROR_KEY]: initialGlobalErrorState, + [STORE_FEATURE_REFERENCE_DATA_KEY]: initialReferenceDataState, + [STORE_FEATURE_SPINNER_KEY]: initialSpinnerState, + [STORE_FEATURE_TECHNICAL_RECORDS_KEY]: initialTechnicalRecordsState, + [STORE_FEATURE_TEST_RESULTS_KEY]: initialTestResultsState, + [STORE_FEATURE_TEST_STATIONS_KEY]: initialTestStationsState, + [STORE_FEATURE_TEST_TYPES_KEY]: initialTestTypeState, + [STORE_FEATURE_USER_KEY]: initialUserState, + [STORE_FEATURE_SEARCH_TECH_RESULTS_KEY]: initialTechSearchResultState, + [STORE_FEATURE_REQUIRED_STANDARDS_KEY]: initialRequiredStandardsState, }; export const reducers: ActionReducerMap = { - [STORE_FEATURE_DEFECTS_KEY]: defectsReducer, - [STORE_FEATURE_GLOBAL_ERROR_KEY]: globalErrorReducer, - [STORE_FEATURE_REFERENCE_DATA_KEY]: referenceDataReducer, - [STORE_FEATURE_SPINNER_KEY]: spinnerReducer, - [STORE_FEATURE_TECHNICAL_RECORDS_KEY]: vehicleTechRecordReducer, - [STORE_FEATURE_TEST_RESULTS_KEY]: testResultsReducer, - [STORE_FEATURE_TEST_STATIONS_KEY]: testStationsReducer, - [STORE_FEATURE_TEST_TYPES_KEY]: testTypesReducer, - [STORE_FEATURE_USER_KEY]: userServiceReducer, - [STORE_FEATURE_SEARCH_TECH_RESULTS_KEY]: techSearchResultReducer, - [STORE_FEATURE_REQUIRED_STANDARDS_KEY]: requiredStandardsReducer, + [STORE_FEATURE_DEFECTS_KEY]: defectsReducer, + [STORE_FEATURE_GLOBAL_ERROR_KEY]: globalErrorReducer, + [STORE_FEATURE_REFERENCE_DATA_KEY]: referenceDataReducer, + [STORE_FEATURE_SPINNER_KEY]: spinnerReducer, + [STORE_FEATURE_TECHNICAL_RECORDS_KEY]: vehicleTechRecordReducer, + [STORE_FEATURE_TEST_RESULTS_KEY]: testResultsReducer, + [STORE_FEATURE_TEST_STATIONS_KEY]: testStationsReducer, + [STORE_FEATURE_TEST_TYPES_KEY]: testTypesReducer, + [STORE_FEATURE_USER_KEY]: userServiceReducer, + [STORE_FEATURE_SEARCH_TECH_RESULTS_KEY]: techSearchResultReducer, + [STORE_FEATURE_REQUIRED_STANDARDS_KEY]: requiredStandardsReducer, }; diff --git a/src/app/store/reference-data/actions/reference-data.actions.spec.ts b/src/app/store/reference-data/actions/reference-data.actions.spec.ts index 7e61988dd9..e70a535b9b 100644 --- a/src/app/store/reference-data/actions/reference-data.actions.spec.ts +++ b/src/app/store/reference-data/actions/reference-data.actions.spec.ts @@ -1,46 +1,54 @@ import { - fetchReferenceData, - fetchReferenceDataSuccess, - fetchReferenceDataFailed, - fetchReferenceDataByKey, - fetchReferenceDataByKeyFailed, - fetchReferenceDataByKeySuccess, - fetchReasonsForAbandoning, - fetchReferenceDataByKeySearch, - fetchReferenceDataByKeySearchFailed, - fetchReferenceDataByKeySearchSuccess, - fetchTyreReferenceDataByKeySearch, - fetchTyreReferenceDataByKeySearchFailed, - fetchTyreReferenceDataByKeySearchSuccess, - addSearchInformation, - fetchReferenceDataAudit, - fetchReferenceDataAuditSuccess, - fetchReferenceDataAuditFailed, + addSearchInformation, + fetchReasonsForAbandoning, + fetchReferenceData, + fetchReferenceDataAudit, + fetchReferenceDataAuditFailed, + fetchReferenceDataAuditSuccess, + fetchReferenceDataByKey, + fetchReferenceDataByKeyFailed, + fetchReferenceDataByKeySearch, + fetchReferenceDataByKeySearchFailed, + fetchReferenceDataByKeySearchSuccess, + fetchReferenceDataByKeySuccess, + fetchReferenceDataFailed, + fetchReferenceDataSuccess, + fetchTyreReferenceDataByKeySearch, + fetchTyreReferenceDataByKeySearchFailed, + fetchTyreReferenceDataByKeySearchSuccess, } from './reference-data.actions'; describe('Test Result Actions', () => { - it('should return correct types', () => { - expect(fetchReferenceData.type).toBe('[API/reference-data] Fetch all of ResourceType'); - expect(fetchReferenceDataSuccess.type).toBe('[API/reference-data] Fetch all of ResourceType Success'); - expect(fetchReferenceDataFailed.type).toBe('[API/reference-data] Fetch all of ResourceType Failed'); + it('should return correct types', () => { + expect(fetchReferenceData.type).toBe('[API/reference-data] Fetch all of ResourceType'); + expect(fetchReferenceDataSuccess.type).toBe('[API/reference-data] Fetch all of ResourceType Success'); + expect(fetchReferenceDataFailed.type).toBe('[API/reference-data] Fetch all of ResourceType Failed'); - expect(fetchReferenceDataAudit.type).toBe('[API/reference-data] Fetch all of Audit ResourceType'); - expect(fetchReferenceDataAuditSuccess.type).toBe('[API/reference-data] Fetch all of Audit ResourceType Success'); - expect(fetchReferenceDataAuditFailed.type).toBe('[API/reference-data] Fetch all of Audit ResourceType Failed'); + expect(fetchReferenceDataAudit.type).toBe('[API/reference-data] Fetch all of Audit ResourceType'); + expect(fetchReferenceDataAuditSuccess.type).toBe('[API/reference-data] Fetch all of Audit ResourceType Success'); + expect(fetchReferenceDataAuditFailed.type).toBe('[API/reference-data] Fetch all of Audit ResourceType Failed'); - expect(fetchReferenceDataByKey.type).toBe('[API/reference-data] Fetch ResourceType by Key'); - expect(fetchReferenceDataByKeySuccess.type).toBe('[API/reference-data] Fetch ResourceType by Key Success'); - expect(fetchReferenceDataByKeyFailed.type).toBe('[API/reference-data] Fetch ResourceType by Key Failed'); - expect(fetchReasonsForAbandoning.type).toBe('[API/reference-data] Fetch reasons for abandoning'); + expect(fetchReferenceDataByKey.type).toBe('[API/reference-data] Fetch ResourceType by Key'); + expect(fetchReferenceDataByKeySuccess.type).toBe('[API/reference-data] Fetch ResourceType by Key Success'); + expect(fetchReferenceDataByKeyFailed.type).toBe('[API/reference-data] Fetch ResourceType by Key Failed'); + expect(fetchReasonsForAbandoning.type).toBe('[API/reference-data] Fetch reasons for abandoning'); - expect(fetchReferenceDataByKeySearch.type).toBe('[API/reference-data] Fetch ResourceType by Key and Search'); - expect(fetchReferenceDataByKeySearchSuccess.type).toBe('[API/reference-data] Fetch ResourceType by Key and search Success'); - expect(fetchReferenceDataByKeySearchFailed.type).toBe('[API/reference-data] Fetch ResourceType by Key and search Failed'); + expect(fetchReferenceDataByKeySearch.type).toBe('[API/reference-data] Fetch ResourceType by Key and Search'); + expect(fetchReferenceDataByKeySearchSuccess.type).toBe( + '[API/reference-data] Fetch ResourceType by Key and search Success' + ); + expect(fetchReferenceDataByKeySearchFailed.type).toBe( + '[API/reference-data] Fetch ResourceType by Key and search Failed' + ); - expect(fetchTyreReferenceDataByKeySearch.type).toBe('[API/reference-data] Fetch tyre by filter and term'); - expect(fetchTyreReferenceDataByKeySearchSuccess.type).toBe('[API/reference-data] Fetch tyre by filter and term Success'); - expect(fetchTyreReferenceDataByKeySearchFailed.type).toBe('[API/reference-data] Fetch tyre by filter and term Failed'); + expect(fetchTyreReferenceDataByKeySearch.type).toBe('[API/reference-data] Fetch tyre by filter and term'); + expect(fetchTyreReferenceDataByKeySearchSuccess.type).toBe( + '[API/reference-data] Fetch tyre by filter and term Success' + ); + expect(fetchTyreReferenceDataByKeySearchFailed.type).toBe( + '[API/reference-data] Fetch tyre by filter and term Failed' + ); - expect(addSearchInformation.type).toBe('[API/reference-data] Add Search Information to state'); - }); + expect(addSearchInformation.type).toBe('[API/reference-data] Add Search Information to state'); + }); }); diff --git a/src/app/store/reference-data/actions/reference-data.actions.ts b/src/app/store/reference-data/actions/reference-data.actions.ts index 1522d33868..5b2090e20b 100644 --- a/src/app/store/reference-data/actions/reference-data.actions.ts +++ b/src/app/store/reference-data/actions/reference-data.actions.ts @@ -5,131 +5,155 @@ import { createAction, props } from '@ngrx/store'; const prefix = '[API/reference-data]'; interface featureError extends GlobalError { - resourceType: ReferenceDataResourceType; + resourceType: ReferenceDataResourceType; } export const fetchReferenceData = createAction( - '[API/reference-data] Fetch all of ResourceType', - props<{ resourceType: ReferenceDataResourceType; paginationToken?: string }>(), + '[API/reference-data] Fetch all of ResourceType', + props<{ resourceType: ReferenceDataResourceType; paginationToken?: string }>() ); export const fetchReferenceDataSuccess = createAction( - '[API/reference-data] Fetch all of ResourceType Success', - props<{ resourceType: ReferenceDataResourceType; payload: Array; paginated: boolean }>(), + '[API/reference-data] Fetch all of ResourceType Success', + props<{ resourceType: ReferenceDataResourceType; payload: Array; paginated: boolean }>() +); +export const fetchReferenceDataFailed = createAction( + '[API/reference-data] Fetch all of ResourceType Failed', + props() ); -export const fetchReferenceDataFailed = createAction('[API/reference-data] Fetch all of ResourceType Failed', props()); export const fetchReferenceDataAudit = createAction( - '[API/reference-data] Fetch all of Audit ResourceType', - props<{ resourceType: ReferenceDataResourceType; paginationToken?: string }>(), + '[API/reference-data] Fetch all of Audit ResourceType', + props<{ resourceType: ReferenceDataResourceType; paginationToken?: string }>() ); export const fetchReferenceDataAuditSuccess = createAction( - '[API/reference-data] Fetch all of Audit ResourceType Success', - props<{ resourceType: ReferenceDataResourceType; payload: Array; paginated: boolean }>(), + '[API/reference-data] Fetch all of Audit ResourceType Success', + props<{ resourceType: ReferenceDataResourceType; payload: Array; paginated: boolean }>() +); +export const fetchReferenceDataAuditFailed = createAction( + '[API/reference-data] Fetch all of Audit ResourceType Failed', + props() ); -export const fetchReferenceDataAuditFailed = createAction('[API/reference-data] Fetch all of Audit ResourceType Failed', props()); export const fetchReferenceDataByKey = createAction( - '[API/reference-data] Fetch ResourceType by Key', - props<{ resourceType: ReferenceDataResourceType; resourceKey: string | number }>(), + '[API/reference-data] Fetch ResourceType by Key', + props<{ resourceType: ReferenceDataResourceType; resourceKey: string | number }>() ); export const fetchReferenceDataByKeySuccess = createAction( - '[API/reference-data] Fetch ResourceType by Key Success', - props<{ - resourceType: ReferenceDataResourceType; - resourceKey: string | number; - payload: ReferenceDataModelBase; - }>(), -); -export const fetchReferenceDataByKeyFailed = createAction('[API/reference-data] Fetch ResourceType by Key Failed', props()); + '[API/reference-data] Fetch ResourceType by Key Success', + props<{ + resourceType: ReferenceDataResourceType; + resourceKey: string | number; + payload: ReferenceDataModelBase; + }>() +); +export const fetchReferenceDataByKeyFailed = createAction( + '[API/reference-data] Fetch ResourceType by Key Failed', + props() +); export const fetchReferenceDataByKeySearch = createAction( - '[API/reference-data] Fetch ResourceType by Key and Search', - props<{ resourceType: ReferenceDataResourceType; resourceKey: string | number }>(), + '[API/reference-data] Fetch ResourceType by Key and Search', + props<{ resourceType: ReferenceDataResourceType; resourceKey: string | number }>() ); export const fetchReferenceDataByKeySearchSuccess = createAction( - '[API/reference-data] Fetch ResourceType by Key and search Success', - props<{ - resourceType: ReferenceDataResourceType; - resourceKey: string | number; - payload: Array; - }>(), + '[API/reference-data] Fetch ResourceType by Key and search Success', + props<{ + resourceType: ReferenceDataResourceType; + resourceKey: string | number; + payload: Array; + }>() ); export const fetchReferenceDataByKeySearchFailed = createAction( - '[API/reference-data] Fetch ResourceType by Key and search Failed', - props(), + '[API/reference-data] Fetch ResourceType by Key and search Failed', + props() ); export const fetchTyreReferenceDataByKeySearch = createAction( - '[API/reference-data] Fetch tyre by filter and term', - props<{ searchFilter: string; searchTerm: string }>(), + '[API/reference-data] Fetch tyre by filter and term', + props<{ searchFilter: string; searchTerm: string }>() ); export const fetchTyreReferenceDataByKeySearchSuccess = createAction( - '[API/reference-data] Fetch tyre by filter and term Success', - props<{ - resourceType: ReferenceDataResourceType; - payload: Array; - }>(), + '[API/reference-data] Fetch tyre by filter and term Success', + props<{ + resourceType: ReferenceDataResourceType; + payload: Array; + }>() ); export const fetchTyreReferenceDataByKeySearchFailed = createAction( - '[API/reference-data] Fetch tyre by filter and term Failed', - props(), + '[API/reference-data] Fetch tyre by filter and term Failed', + props() ); export const addSearchInformation = createAction( - '[API/reference-data] Add Search Information to state', - props<{ - filter: string; - term: string; - }>(), + '[API/reference-data] Add Search Information to state', + props<{ + filter: string; + term: string; + }>() ); export const removeTyreSearch = createAction('[API/reference-data] Remove search return from state'); export const removeReferenceDataByKey = createAction( - '[API/reference-data] Remove item from state', - props<{ - resourceType: ReferenceDataResourceType; - resourceKey: string; - }>(), + '[API/reference-data] Remove item from state', + props<{ + resourceType: ReferenceDataResourceType; + resourceKey: string; + }>() ); export const fetchReasonsForAbandoning = createAction('[API/reference-data] Fetch reasons for abandoning'); export const createReferenceDataItem = createAction( - `${prefix} createReferenceDataItem`, - props<{ - resourceType: ReferenceDataResourceType; - resourceKey: string; - payload: ReferenceDataModelBase; - }>(), -); -export const createReferenceDataItemSuccess = createAction(`${prefix} createReferenceDataItemSuccess`, props<{ result: ReferenceDataModelBase }>()); -export const createReferenceDataItemFailure = createAction(`${prefix} createReferenceDataItemFailure`, props()); + `${prefix} createReferenceDataItem`, + props<{ + resourceType: ReferenceDataResourceType; + resourceKey: string; + payload: ReferenceDataModelBase; + }>() +); +export const createReferenceDataItemSuccess = createAction( + `${prefix} createReferenceDataItemSuccess`, + props<{ result: ReferenceDataModelBase }>() +); +export const createReferenceDataItemFailure = createAction( + `${prefix} createReferenceDataItemFailure`, + props() +); export const amendReferenceDataItem = createAction( - `${prefix} amendReferenceDataItem`, - props<{ - resourceType: ReferenceDataResourceType; - resourceKey: string; - payload: ReferenceDataModelBase; - }>(), -); -export const amendReferenceDataItemSuccess = createAction(`${prefix} amendReferenceDataItemSuccess`, props<{ result: ReferenceDataModelBase }>()); -export const amendReferenceDataItemFailure = createAction(`${prefix} amendReferenceDataItemFailure`, props()); + `${prefix} amendReferenceDataItem`, + props<{ + resourceType: ReferenceDataResourceType; + resourceKey: string; + payload: ReferenceDataModelBase; + }>() +); +export const amendReferenceDataItemSuccess = createAction( + `${prefix} amendReferenceDataItemSuccess`, + props<{ result: ReferenceDataModelBase }>() +); +export const amendReferenceDataItemFailure = createAction( + `${prefix} amendReferenceDataItemFailure`, + props() +); export const deleteReferenceDataItem = createAction( - `${prefix} deleteReferenceDataItem`, - props<{ - resourceType: ReferenceDataResourceType; - resourceKey: string; - reason: string; - }>(), + `${prefix} deleteReferenceDataItem`, + props<{ + resourceType: ReferenceDataResourceType; + resourceKey: string; + reason: string; + }>() ); export const deleteReferenceDataItemSuccess = createAction( - `${prefix} deleteReferenceDataItemSuccess`, - props<{ - resourceType: ReferenceDataResourceType; - resourceKey: string; - }>(), + `${prefix} deleteReferenceDataItemSuccess`, + props<{ + resourceType: ReferenceDataResourceType; + resourceKey: string; + }>() +); +export const deleteReferenceDataItemFailure = createAction( + `${prefix} deleteReferenceDataItemFailure`, + props() ); -export const deleteReferenceDataItemFailure = createAction(`${prefix} deleteReferenceDataItemFailure`, props()); diff --git a/src/app/store/reference-data/effects/operators/reference-data.operators.spec.ts b/src/app/store/reference-data/effects/operators/reference-data.operators.spec.ts index e84036102b..e06a55690b 100644 --- a/src/app/store/reference-data/effects/operators/reference-data.operators.spec.ts +++ b/src/app/store/reference-data/effects/operators/reference-data.operators.spec.ts @@ -3,51 +3,51 @@ import { of, take } from 'rxjs'; import { handleNotFound, sortReferenceData } from './reference-data.operators'; describe('Reference data operators', () => { - describe('handleNotFound.prototype.name', () => { - it('should emit source observable when there is data', (done) => { - of({ data: [{ resourceType: 'type', resourceKey: 'key' }] }) - .pipe(take(1), handleNotFound('test')) - .subscribe({ - next: (val) => { - expect(val).toEqual({ data: [{ resourceType: 'type', resourceKey: 'key' }] }); - done(); - }, - }); - }); - it('should throw an error if data is empty', (done) => { - of({ data: [] }) - .pipe(take(1), handleNotFound('test')) - .subscribe({ - error: (e) => { - expect(e.message).toBe('Reference data not found for resource type test'); - done(); - }, - }); - }); - }); + describe('handleNotFound.prototype.name', () => { + it('should emit source observable when there is data', (done) => { + of({ data: [{ resourceType: 'type', resourceKey: 'key' }] }) + .pipe(take(1), handleNotFound('test')) + .subscribe({ + next: (val) => { + expect(val).toEqual({ data: [{ resourceType: 'type', resourceKey: 'key' }] }); + done(); + }, + }); + }); + it('should throw an error if data is empty', (done) => { + of({ data: [] }) + .pipe(take(1), handleNotFound('test')) + .subscribe({ + error: (e) => { + expect(e.message).toBe('Reference data not found for resource type test'); + done(); + }, + }); + }); + }); - describe('sortReferenceData.prototype.name', () => { - it('should sort strings', (done) => { - of({ - data: [ - { resourceType: ReferenceDataResourceType.User, resourceKey: 2, name: 'last name' }, - { resourceType: ReferenceDataResourceType.User, resourceKey: 3, name: 'some name' }, - { resourceType: ReferenceDataResourceType.User, resourceKey: 1, name: 'a name' }, - ], - }) - .pipe(take(1), sortReferenceData(ReferenceDataResourceType.User)) - .subscribe({ - next: (val) => { - expect(val).toEqual({ - data: [ - { resourceType: ReferenceDataResourceType.User, resourceKey: 1, name: 'a name' }, - { resourceType: ReferenceDataResourceType.User, resourceKey: 2, name: 'last name' }, - { resourceType: ReferenceDataResourceType.User, resourceKey: 3, name: 'some name' }, - ], - }); - done(); - }, - }); - }); - }); + describe('sortReferenceData.prototype.name', () => { + it('should sort strings', (done) => { + of({ + data: [ + { resourceType: ReferenceDataResourceType.User, resourceKey: 2, name: 'last name' }, + { resourceType: ReferenceDataResourceType.User, resourceKey: 3, name: 'some name' }, + { resourceType: ReferenceDataResourceType.User, resourceKey: 1, name: 'a name' }, + ], + }) + .pipe(take(1), sortReferenceData(ReferenceDataResourceType.User)) + .subscribe({ + next: (val) => { + expect(val).toEqual({ + data: [ + { resourceType: ReferenceDataResourceType.User, resourceKey: 1, name: 'a name' }, + { resourceType: ReferenceDataResourceType.User, resourceKey: 2, name: 'last name' }, + { resourceType: ReferenceDataResourceType.User, resourceKey: 3, name: 'some name' }, + ], + }); + done(); + }, + }); + }); + }); }); diff --git a/src/app/store/reference-data/effects/operators/reference-data.operators.ts b/src/app/store/reference-data/effects/operators/reference-data.operators.ts index 3f94c6f08c..bb6b35415a 100644 --- a/src/app/store/reference-data/effects/operators/reference-data.operators.ts +++ b/src/app/store/reference-data/effects/operators/reference-data.operators.ts @@ -8,66 +8,66 @@ import { Observable } from 'rxjs'; * @returns emitted source value or error if response is equivalent to not found. */ export function handleNotFound(...args: unknown[]) { - // eslint-disable-next-line func-names - return function (source: Observable): Observable { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const isEmpty = (val: any) => !Object.keys(val).length || (Object.prototype.hasOwnProperty.call(val, 'data') && !val.data.length); - return new Observable((subscriber) => { - source.subscribe({ - next: (val) => { - if (val && isEmpty(val)) { - // eslint-disable-next-line @typescript-eslint/restrict-template-expressions - subscriber.error(new Error(`Reference data not found for resource type ${args}`)); - } + // eslint-disable-next-line func-names + return (source: Observable): Observable => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const isEmpty = (val: any) => + !Object.keys(val).length || (Object.prototype.hasOwnProperty.call(val, 'data') && !val.data.length); + return new Observable((subscriber) => { + source.subscribe({ + next: (val) => { + if (val && isEmpty(val)) { + // eslint-disable-next-line @typescript-eslint/restrict-template-expressions + subscriber.error(new Error(`Reference data not found for resource type ${args}`)); + } - subscriber.next(val); - }, - error: (e) => subscriber.error(e), - complete: () => subscriber.complete(), - }); - }); - }; + subscriber.next(val); + }, + error: (e) => subscriber.error(e), + complete: () => subscriber.complete(), + }); + }); + }; } export function sortReferenceData(resourceType: ReferenceDataResourceType) { - // eslint-disable-next-line func-names - return function (source: Observable): Observable { - return new Observable((subscriber) => { - source.subscribe({ - next: (val) => { - const { data } = val; - subscriber.next({ ...val, data: _sort(resourceType, data) }); - }, - error: (e) => subscriber.error(e), - complete: () => subscriber.complete(), - }); - }); - }; + // eslint-disable-next-line func-names + return (source: Observable): Observable => + new Observable((subscriber) => { + source.subscribe({ + next: (val) => { + const { data } = val; + subscriber.next({ ...val, data: _sort(resourceType, data) }); + }, + error: (e) => subscriber.error(e), + complete: () => subscriber.complete(), + }); + }); } // eslint-disable-next-line no-underscore-dangle function _sort(type: ReferenceDataResourceType, data: ReferenceDataItem[]) { - const dataToSort = [...(data as ReferenceData[])]; - switch (type) { - case ReferenceDataResourceType.User: - return dataToSort.sort(sorter('name')); - default: - return dataToSort; - } + const dataToSort = [...(data as ReferenceData[])]; + switch (type) { + case ReferenceDataResourceType.User: + return dataToSort.sort(sorter('name')); + default: + return dataToSort; + } } type ReferenceData = ReferenceDataItem & Record; function sorter(sortkey: keyof ReferenceData | undefined = 'description') { - // eslint-disable-next-line no-nested-ternary, @typescript-eslint/no-explicit-any - const compare = (a: any, b: any) => (typeof a === 'string' ? (a <= b ? (a < b ? -1 : 0) : 1) : a - b); - return (a: ReferenceData, b: ReferenceData) => { - let l; let - r; - if (sortkey && Object.prototype.hasOwnProperty.call(a, sortkey)) { - l = a[`${sortkey}`]; - r = b[`${sortkey}`]; - } + // eslint-disable-next-line no-nested-ternary, @typescript-eslint/no-explicit-any + const compare = (a: any, b: any) => (typeof a === 'string' ? (a <= b ? (a < b ? -1 : 0) : 1) : a - b); + return (a: ReferenceData, b: ReferenceData) => { + let l; + let r; + if (sortkey && Object.prototype.hasOwnProperty.call(a, sortkey)) { + l = a[`${sortkey}`]; + r = b[`${sortkey}`]; + } - return compare(l, r); - }; + return compare(l, r); + }; } diff --git a/src/app/store/reference-data/effects/reference-data.effects.spec.ts b/src/app/store/reference-data/effects/reference-data.effects.spec.ts index 57060d58b8..4c9c3adf57 100644 --- a/src/app/store/reference-data/effects/reference-data.effects.spec.ts +++ b/src/app/store/reference-data/effects/reference-data.effects.spec.ts @@ -14,515 +14,548 @@ import { testResultInEdit } from '@store/test-records'; import { Observable } from 'rxjs'; import { TestScheduler } from 'rxjs/testing'; import { - amendReferenceDataItem, - amendReferenceDataItemFailure, - amendReferenceDataItemSuccess, - createReferenceDataItem, - createReferenceDataItemFailure, - createReferenceDataItemSuccess, - deleteReferenceDataItem, - deleteReferenceDataItemFailure, - deleteReferenceDataItemSuccess, - fetchReasonsForAbandoning, - fetchReferenceData, - fetchReferenceDataAudit, - fetchReferenceDataAuditFailed, - fetchReferenceDataAuditSuccess, - fetchReferenceDataByKey, - fetchReferenceDataByKeyFailed, - fetchReferenceDataByKeySearch, - fetchReferenceDataByKeySearchFailed, - fetchReferenceDataByKeySearchSuccess, - fetchReferenceDataByKeySuccess, - fetchReferenceDataFailed, - fetchReferenceDataSuccess, - fetchTyreReferenceDataByKeySearch, - fetchTyreReferenceDataByKeySearchFailed, - fetchTyreReferenceDataByKeySearchSuccess, + amendReferenceDataItem, + amendReferenceDataItemFailure, + amendReferenceDataItemSuccess, + createReferenceDataItem, + createReferenceDataItemFailure, + createReferenceDataItemSuccess, + deleteReferenceDataItem, + deleteReferenceDataItemFailure, + deleteReferenceDataItemSuccess, + fetchReasonsForAbandoning, + fetchReferenceData, + fetchReferenceDataAudit, + fetchReferenceDataAuditFailed, + fetchReferenceDataAuditSuccess, + fetchReferenceDataByKey, + fetchReferenceDataByKeyFailed, + fetchReferenceDataByKeySearch, + fetchReferenceDataByKeySearchFailed, + fetchReferenceDataByKeySearchSuccess, + fetchReferenceDataByKeySuccess, + fetchReferenceDataFailed, + fetchReferenceDataSuccess, + fetchTyreReferenceDataByKeySearch, + fetchTyreReferenceDataByKeySearchFailed, + fetchTyreReferenceDataByKeySearchSuccess, } from '../actions/reference-data.actions'; import { testCases } from '../reference-data.test-cases'; import { ReferenceDataEffects } from './reference-data.effects'; describe('ReferenceDataEffects', () => { - let effects: ReferenceDataEffects; - let actions$ = new Observable(); - let testScheduler: TestScheduler; - let referenceDataService: ReferenceDataService; - let store: MockStore; - - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [HttpClientTestingModule], - providers: [ - provideMockActions(() => actions$), - provideMockStore({ initialState: initialAppState }), - ReferenceDataEffects, - ReferenceDataService, - { provide: UserService, useValue: {} }, - ], - }); - - effects = TestBed.inject(ReferenceDataEffects); - store = TestBed.inject(MockStore); - referenceDataService = TestBed.inject(ReferenceDataService); - }); - - beforeEach(() => { - testScheduler = new TestScheduler((actual, expected) => { - expect(actual).toEqual(expected); - }); - }); - - describe('fetchReferenceDataByType$', () => { - it.each(testCases)('should return fetchReferenceDataSuccess action on successful API call', (value) => { - testScheduler.run(({ hot, cold, expectObservable }) => { - const { resourceType, payload } = value; - const apiResponse = { data: [...payload] }; - - // mock action to trigger effect - actions$ = hot('-a--', { a: fetchReferenceData({ resourceType }) }); - - // mock service call - jest.spyOn(referenceDataService, 'fetchReferenceData').mockReturnValue(cold('--a|', { a: apiResponse })); - - // expect effect to return success action - expectObservable(effects.fetchReferenceDataByType$).toBe('---b', { - b: fetchReferenceDataSuccess({ resourceType, payload, paginated: false }), - }); - }); - }); - - it.each(testCases)( - 'should return fetchReferenceDataSuccess and fetchReferenceData actions on successful API call with pagination token', - (value) => { - testScheduler.run(({ hot, cold, expectObservable }) => { - const { resourceType, payload } = value; - const apiResponse = { data: [...payload], paginationToken: 'token' }; - - // mock action to trigger effect - actions$ = hot('-a--', { a: fetchReferenceData({ resourceType }) }); - - // mock service call - jest.spyOn(referenceDataService, 'fetchReferenceData').mockReturnValue(cold('--a|', { a: apiResponse })); - - // expect effect to return success action - expectObservable(effects.fetchReferenceDataByType$).toBe('---(bc)', { - b: fetchReferenceDataSuccess({ resourceType, payload, paginated: true }), - c: fetchReferenceData({ resourceType, paginationToken: 'token' }), - }); - }); - }, - ); - - it('should return fetchReferenceDataFailed action on API error', () => { - testScheduler.run(({ hot, cold, expectObservable }) => { - actions$ = hot('-a--', { a: fetchReferenceData({ resourceType: null as unknown as ReferenceDataResourceType }) }); - - const expectedError = new Error('Reference data resourceType is required'); - - jest.spyOn(referenceDataService, 'fetchReferenceData').mockReturnValue(cold('--#|', {}, expectedError)); - - expectObservable(effects.fetchReferenceDataByType$).toBe('---b', { - b: fetchReferenceDataFailed({ - error: 'Reference data resourceType is required', - resourceType: null as unknown as ReferenceDataResourceType, - }), - }); - }); - }); - - it('should return fetchReferenceDataFailed action when data is not found', () => { - testScheduler.run(({ hot, cold, expectObservable }) => { - actions$ = hot('-a--', { a: fetchReferenceData({ resourceType: ReferenceDataResourceType.HgvMake }) }); - - jest.spyOn(referenceDataService, 'fetchReferenceData').mockReturnValue(cold('--a|', { a: { data: [] } })); - - expectObservable(effects.fetchReferenceDataByType$).toBe('---b', { - b: fetchReferenceDataFailed({ - error: `Reference data not found for resource type ${ReferenceDataResourceType.HgvMake}`, - resourceType: ReferenceDataResourceType.HgvMake, - }), - }); - }); - }); - }); - - describe('fetchReferenceDataByAuditType$', () => { - it.each(testCases)('should return fetchReferenceDataAuditSuccess action on successful API call', (value) => { - testScheduler.run(({ hot, cold, expectObservable }) => { - const { resourceType, payload } = value; - const apiResponse = { data: [...payload] }; - - // mock action to trigger effect - actions$ = hot('-a--', { a: fetchReferenceDataAudit({ resourceType }) }); - - // mock service call - jest.spyOn(referenceDataService, 'fetchReferenceDataAudit').mockReturnValue(cold('--a|', { a: apiResponse })); - - // expect effect to return success action - expectObservable(effects.fetchReferenceDataByAuditType$).toBe('---b', { - b: fetchReferenceDataAuditSuccess({ resourceType, payload, paginated: false }), - }); - }); - }); - - it.each(testCases)( - 'should return fetchReferenceDataAuditSuccess and fetchReferenceDataAudit actions on successful API call with pagination token', - (value) => { - testScheduler.run(({ hot, cold, expectObservable }) => { - const { resourceType, payload } = value; - const apiResponse = { data: [...payload], paginationToken: 'token' }; - - // mock action to trigger effect - actions$ = hot('-a--', { a: fetchReferenceDataAudit({ resourceType }) }); - - // mock service call - jest.spyOn(referenceDataService, 'fetchReferenceDataAudit').mockReturnValue(cold('--a|', { a: apiResponse })); - - // expect effect to return success action - expectObservable(effects.fetchReferenceDataByAuditType$).toBe('---(bc)', { - b: fetchReferenceDataAuditSuccess({ resourceType, payload, paginated: true }), - c: fetchReferenceDataAudit({ resourceType, paginationToken: 'token' }), - }); - }); - }, - ); - - it('should return fetchReferenceDataAuditFailed action on API error', () => { - testScheduler.run(({ hot, cold, expectObservable }) => { - actions$ = hot('-a--', { a: fetchReferenceDataAudit({ resourceType: null as unknown as ReferenceDataResourceType }) }); - - const expectedError = new Error('Reference data resourceType is required'); - - jest.spyOn(referenceDataService, 'fetchReferenceDataAudit').mockReturnValue(cold('--#|', {}, expectedError)); - - expectObservable(effects.fetchReferenceDataByAuditType$).toBe('---b', { - b: fetchReferenceDataAuditFailed({ - error: 'Reference data resourceType is required', - resourceType: null as unknown as ReferenceDataResourceType, - }), - }); - }); - }); - - it('should return fetchReferenceDataAuditFailed action when data is not found', () => { - testScheduler.run(({ hot, cold, expectObservable }) => { - actions$ = hot('-a--', { a: fetchReferenceDataAudit({ resourceType: ReferenceDataResourceType.HgvMake }) }); - - jest.spyOn(referenceDataService, 'fetchReferenceDataAudit').mockReturnValue(cold('--a|', { a: { data: [] } })); - - expectObservable(effects.fetchReferenceDataByAuditType$).toBe('---b', { - b: fetchReferenceDataAuditFailed({ - error: `Reference data not found for resource type ${ReferenceDataResourceType.HgvMake}`, - resourceType: ReferenceDataResourceType.HgvMake, - }), - }); - }); - }); - }); - - describe('fetchReferenceDataByKey$', () => { - it.each(testCases)('should return fetchReferenceDataByKeySuccess action on successful API call', (value) => { - testScheduler.run(({ hot, cold, expectObservable }) => { - const { resourceType, resourceKey, payload } = value; - - const entity = payload.find((p) => p.resourceKey === resourceKey) as ReferenceDataItem; - - // mock action to trigger effect - actions$ = hot('-a--', { a: fetchReferenceDataByKey({ resourceType, resourceKey }) }); - - // mock service call - jest.spyOn(referenceDataService, 'fetchReferenceDataByKey').mockReturnValue(cold('--a|', { a: entity })); - - // expect effect to return success action - expectObservable(effects.fetchReferenceDataByKey$).toBe('---b', { - b: fetchReferenceDataByKeySuccess({ resourceType, resourceKey, payload: entity as ReferenceDataModelBase }), - }); - }); - }); - - it.each(testCases)('should return fetchReferenceDataByKeyFailed action on API error', (value) => { - testScheduler.run(({ hot, cold, expectObservable }) => { - const { resourceType } = value; - actions$ = hot('-a--', { a: fetchReferenceDataByKey({ resourceType, resourceKey: null as unknown as string | number }) }); - - const expectedError = new Error('Reference data resourceKey is required'); - - jest.spyOn(referenceDataService, 'fetchReferenceDataByKey').mockReturnValue(cold('--#|', {}, expectedError)); - - expectObservable(effects.fetchReferenceDataByKey$).toBe('---b', { - b: fetchReferenceDataByKeyFailed({ error: 'Reference data resourceKey is required', resourceType }), - }); - }); - }); - - it.each(testCases)('should return fetchReferenceDataByKeyFailed action when resource not found', (value) => { - testScheduler.run(({ hot, cold, expectObservable }) => { - const { resourceType } = value; - actions$ = hot('-a--', { a: fetchReferenceDataByKey({ resourceType, resourceKey: 'xx' }) }); - - jest.spyOn(referenceDataService, 'fetchReferenceDataByKey').mockReturnValue(cold('--a|', { a: {} })); - - expectObservable(effects.fetchReferenceDataByKey$).toBe('---b', { - b: fetchReferenceDataByKeyFailed({ error: `Reference data not found for resource type ${resourceType},xx`, resourceType }), - }); - }); - }); - }); - - describe('fetchReferenceDataByKeySearch', () => { - it('should return fetchReferenceDataByKeySearchSuccess on successful API call', () => { - testScheduler.run(({ hot, cold, expectObservable }) => { - const resourceType = ReferenceDataResourceType.Tyres; - const resourceKey = '123'; - const value = { - payload: [ - { - tyreCode: '123', - resourceType: ReferenceDataResourceType.Tyres, - resourceKey: '123', - code: '123', - loadIndexSingleLoad: '102', - tyreSize: 'size', - dateTimeStamp: 'time', - userId: '1234', - loadIndexTwinLoad: '101', - plyRating: '18', - }, - ], - }; - const apiResponse = { data: [...value.payload] }; - - actions$ = hot('-a--', { a: fetchReferenceDataByKeySearch({ resourceType, resourceKey }) }); - - jest.spyOn(referenceDataService, 'fetchReferenceDataByKeySearch').mockReturnValue(cold('--a|', { a: apiResponse })); - - expectObservable(effects.fetchReferenceDataByKeySearch$).toBe('---b', { - b: fetchReferenceDataByKeySearchSuccess({ resourceType, resourceKey, payload: value.payload as ReferenceDataModelBase[] }), - }); - }); - }); - - it('should return fetchReferenceDataByKeySearchFailed on successful API call', () => { - testScheduler.run(({ hot, cold, expectObservable }) => { - const resourceType = ReferenceDataResourceType.Tyres; - actions$ = hot('-a--', { a: fetchReferenceDataByKeySearch({ resourceType, resourceKey: null as unknown as string | number }) }); - - const expectedError = new Error('Reference data resourceKey is required'); - - jest.spyOn(referenceDataService, 'fetchReferenceDataByKeySearch').mockReturnValue(cold('--#|', {}, expectedError)); - - expectObservable(effects.fetchReferenceDataByKeySearch$).toBe('---b', { - b: fetchReferenceDataByKeySearchFailed({ error: 'Reference data resourceKey is required', resourceType }), - }); - }); - }); - }); - - describe('fetchTyreReferenceDataByKeySearch', () => { - it('should return fetchTyreReferenceDataByKeySearchSuccess on successful API call', () => { - testScheduler.run(({ hot, cold, expectObservable }) => { - const resourceType = ReferenceDataResourceType.Tyres; - const value = { - payload: [ - { - tyreCode: '123', - resourceType: ReferenceDataResourceType.Tyres, - resourceKey: '123', - code: '123', - loadIndexSingleLoad: '102', - tyreSize: 'size', - dateTimeStamp: 'time', - userId: '1234', - loadIndexTwinLoad: '101', - plyRating: '18', - }, - ], - }; - const apiResponse = { data: [...value.payload] }; - - actions$ = hot('-a--', { a: fetchTyreReferenceDataByKeySearch({ searchFilter: 'plyRating', searchTerm: '123' }) }); - - jest.spyOn(referenceDataService, 'fetchTyreReferenceDataByKeySearch').mockReturnValue(cold('--a|', { a: apiResponse })); - - expectObservable(effects.fetchTyreReferenceDataByKeySearch$).toBe('---b', { - b: fetchTyreReferenceDataByKeySearchSuccess({ resourceType, payload: value.payload as ReferenceDataModelBase[] }), - }); - }); - }); - - it('should return fetchTyreReferenceDataByKeySearchFailed on unsuccessful API call', () => { - testScheduler.run(({ hot, cold, expectObservable }) => { - const resourceType = ReferenceDataResourceType.Tyres; - actions$ = hot('-a--', { - a: fetchTyreReferenceDataByKeySearch({ searchFilter: 'plyRating', searchTerm: null as unknown as string }), - }); - - const expectedError = new Error('Search term is required'); - - jest.spyOn(referenceDataService, 'fetchTyreReferenceDataByKeySearch').mockReturnValue(cold('--#|', {}, expectedError)); - - expectObservable(effects.fetchTyreReferenceDataByKeySearch$).toBe('---b', { - b: fetchTyreReferenceDataByKeySearchFailed({ error: 'Search term is required', resourceType }), - }); - }); - }); - }); - - const vehicleTypeReasonsForAbandoning = [ - { - vehicleType: VehicleTypes.PSV, - resourceType: ReferenceDataResourceType.ReasonsForAbandoningPsv, - }, - { - vehicleType: VehicleTypes.TRL, - resourceType: ReferenceDataResourceType.ReasonsForAbandoningTrl, - }, - { - vehicleType: VehicleTypes.HGV, - resourceType: ReferenceDataResourceType.ReasonsForAbandoningHgv, - }, - ]; - it.each(vehicleTypeReasonsForAbandoning)('should dispatch the action to fetch the reasons for abandoning for the right vehicle', (values) => { - const { vehicleType, resourceType } = values; - const testResult = { vehicleType } as TestResultModel; - - testScheduler.run(({ hot, expectObservable }) => { - store.overrideSelector(testResultInEdit, testResult); - - actions$ = hot('-a', { - a: fetchReasonsForAbandoning(), - }); - - expectObservable(effects.fetchReasonsForAbandoning).toBe('-b', { - b: fetchReferenceData({ - resourceType, - }), - }); - }); - }); - - describe('createReferenceDataItem$', () => { - it('should return createReferenceDataItemSuccess on a successful call', () => { - testScheduler.run(({ hot, cold, expectObservable }) => { - const resourceType = ReferenceDataResourceType.CountryOfRegistration; - const resourceKey = 'testKey'; - const body = { - description: 'test country', - }; - const apiResponse = { ...body, resourceType, resourceKey }; - - actions$ = hot('-a--', { - a: createReferenceDataItem({ resourceType, resourceKey, payload: body as ReferenceDataModelBase }), - }); - - jest.spyOn(referenceDataService, 'createReferenceDataItem').mockReturnValue(cold('--a|', { a: apiResponse })); - - expectObservable(effects.createReferenceDataItem$).toBe('---b', { - b: createReferenceDataItemSuccess({ result: apiResponse as ReferenceDataModelBase }), - }); - }); - }); - it('should return createReferenceDataItemFailure if an error is returned', () => { - testScheduler.run(({ hot, cold, expectObservable }) => { - const resourceType = ReferenceDataResourceType.CountryOfRegistration; - const resourceKey = 'testKey'; - const body = { - description: 'test country', - }; - - actions$ = hot('-a--', { - a: createReferenceDataItem({ resourceType, resourceKey, payload: body as ReferenceDataModelBase }), - }); - - const expectedError = new Error('Something went wrong'); - - jest.spyOn(referenceDataService, 'createReferenceDataItem').mockReturnValue(cold('--#|', {}, expectedError)); - - expectObservable(effects.createReferenceDataItem$).toBe('---b', { - b: createReferenceDataItemFailure({ error: 'Something went wrong' }), - }); - }); - }); - }); - - describe('amendReferenceDataItem$', () => { - it('should return amendReferenceDataI on a successful call', () => { - testScheduler.run(({ hot, cold, expectObservable }) => { - const resourceType = ReferenceDataResourceType.CountryOfRegistration; - const resourceKey = 'testKey'; - const body = { - description: 'test country', - }; - const apiResponse = { ...body, resourceType, resourceKey }; - - actions$ = hot('-a--', { - a: amendReferenceDataItem({ resourceType, resourceKey, payload: body as ReferenceDataModelBase }), - }); - - jest.spyOn(referenceDataService, 'amendReferenceDataItem').mockReturnValue(cold('--a-|', { a: apiResponse })); - - expectObservable(effects.amendReferenceDataItem$).toBe('---b', { - b: amendReferenceDataItemSuccess({ result: apiResponse as ReferenceDataModelBase }), - }); - }); - }); - it('should return amendReferenceDataItemFailure if an error is returned', () => { - testScheduler.run(({ hot, cold, expectObservable }) => { - const resourceType = ReferenceDataResourceType.CountryOfRegistration; - const resourceKey = 'testKey'; - const body = { - description: 'test country', - }; - - actions$ = hot('-a--', { - a: amendReferenceDataItem({ resourceType, resourceKey, payload: body as ReferenceDataModelBase }), - }); - - const expectedError = new Error('Something went wrong'); - - jest.spyOn(referenceDataService, 'amendReferenceDataItem').mockReturnValue(cold('--#|', {}, expectedError)); - - expectObservable(effects.amendReferenceDataItem$).toBe('---b', { - b: amendReferenceDataItemFailure({ error: 'Something went wrong' }), - }); - }); - }); - }); - - describe('deleteReferenceDataItem$', () => { - it('should return deleteReferenceDataSuccess on a successful call', () => { - testScheduler.run(({ hot, cold, expectObservable }) => { - const resourceType = ReferenceDataResourceType.CountryOfRegistration; - const resourceKey = 'testKey'; - const reason = 'for test'; - const apiResponse = { result: true }; - - actions$ = hot('-a--', { a: deleteReferenceDataItem({ resourceType, resourceKey, reason }) }); - - jest.spyOn(referenceDataService, 'deleteReferenceDataItem').mockReturnValue(cold('--a-|', { a: apiResponse as DeleteItem })); - - expectObservable(effects.deleteReferenceDataItem$).toBe('---b', { - b: deleteReferenceDataItemSuccess({ resourceType, resourceKey }), - }); - }); - }); - it('should return deleteReferenceDataFailure if an error is returned', () => { - testScheduler.run(({ hot, cold, expectObservable }) => { - const resourceType = ReferenceDataResourceType.CountryOfRegistration; - const resourceKey = 'testKey'; - const reason = 'testing'; - - actions$ = hot('-a--', { a: deleteReferenceDataItem({ resourceType, resourceKey, reason }) }); - - const expectedError = new Error('Something went wrong'); - - jest.spyOn(referenceDataService, 'deleteReferenceDataItem').mockReturnValue(cold('--#|', {}, expectedError)); - - expectObservable(effects.deleteReferenceDataItem$).toBe('---b', { - b: deleteReferenceDataItemFailure({ error: 'Something went wrong' }), - }); - }); - }); - }); + let effects: ReferenceDataEffects; + let actions$ = new Observable(); + let testScheduler: TestScheduler; + let referenceDataService: ReferenceDataService; + let store: MockStore; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [HttpClientTestingModule], + providers: [ + provideMockActions(() => actions$), + provideMockStore({ initialState: initialAppState }), + ReferenceDataEffects, + ReferenceDataService, + { provide: UserService, useValue: {} }, + ], + }); + + effects = TestBed.inject(ReferenceDataEffects); + store = TestBed.inject(MockStore); + referenceDataService = TestBed.inject(ReferenceDataService); + }); + + beforeEach(() => { + testScheduler = new TestScheduler((actual, expected) => { + expect(actual).toEqual(expected); + }); + }); + + describe('fetchReferenceDataByType$', () => { + it.each(testCases)('should return fetchReferenceDataSuccess action on successful API call', (value) => { + testScheduler.run(({ hot, cold, expectObservable }) => { + const { resourceType, payload } = value; + const apiResponse = { data: [...payload] }; + + // mock action to trigger effect + actions$ = hot('-a--', { a: fetchReferenceData({ resourceType }) }); + + // mock service call + jest.spyOn(referenceDataService, 'fetchReferenceData').mockReturnValue(cold('--a|', { a: apiResponse })); + + // expect effect to return success action + expectObservable(effects.fetchReferenceDataByType$).toBe('---b', { + b: fetchReferenceDataSuccess({ resourceType, payload, paginated: false }), + }); + }); + }); + + it.each(testCases)( + 'should return fetchReferenceDataSuccess and fetchReferenceData actions on successful API call with pagination token', + (value) => { + testScheduler.run(({ hot, cold, expectObservable }) => { + const { resourceType, payload } = value; + const apiResponse = { data: [...payload], paginationToken: 'token' }; + + // mock action to trigger effect + actions$ = hot('-a--', { a: fetchReferenceData({ resourceType }) }); + + // mock service call + jest.spyOn(referenceDataService, 'fetchReferenceData').mockReturnValue(cold('--a|', { a: apiResponse })); + + // expect effect to return success action + expectObservable(effects.fetchReferenceDataByType$).toBe('---(bc)', { + b: fetchReferenceDataSuccess({ resourceType, payload, paginated: true }), + c: fetchReferenceData({ resourceType, paginationToken: 'token' }), + }); + }); + } + ); + + it('should return fetchReferenceDataFailed action on API error', () => { + testScheduler.run(({ hot, cold, expectObservable }) => { + actions$ = hot('-a--', { + a: fetchReferenceData({ resourceType: null as unknown as ReferenceDataResourceType }), + }); + + const expectedError = new Error('Reference data resourceType is required'); + + jest.spyOn(referenceDataService, 'fetchReferenceData').mockReturnValue(cold('--#|', {}, expectedError)); + + expectObservable(effects.fetchReferenceDataByType$).toBe('---b', { + b: fetchReferenceDataFailed({ + error: 'Reference data resourceType is required', + resourceType: null as unknown as ReferenceDataResourceType, + }), + }); + }); + }); + + it('should return fetchReferenceDataFailed action when data is not found', () => { + testScheduler.run(({ hot, cold, expectObservable }) => { + actions$ = hot('-a--', { a: fetchReferenceData({ resourceType: ReferenceDataResourceType.HgvMake }) }); + + jest.spyOn(referenceDataService, 'fetchReferenceData').mockReturnValue(cold('--a|', { a: { data: [] } })); + + expectObservable(effects.fetchReferenceDataByType$).toBe('---b', { + b: fetchReferenceDataFailed({ + error: `Reference data not found for resource type ${ReferenceDataResourceType.HgvMake}`, + resourceType: ReferenceDataResourceType.HgvMake, + }), + }); + }); + }); + }); + + describe('fetchReferenceDataByAuditType$', () => { + it.each(testCases)('should return fetchReferenceDataAuditSuccess action on successful API call', (value) => { + testScheduler.run(({ hot, cold, expectObservable }) => { + const { resourceType, payload } = value; + const apiResponse = { data: [...payload] }; + + // mock action to trigger effect + actions$ = hot('-a--', { a: fetchReferenceDataAudit({ resourceType }) }); + + // mock service call + jest.spyOn(referenceDataService, 'fetchReferenceDataAudit').mockReturnValue(cold('--a|', { a: apiResponse })); + + // expect effect to return success action + expectObservable(effects.fetchReferenceDataByAuditType$).toBe('---b', { + b: fetchReferenceDataAuditSuccess({ resourceType, payload, paginated: false }), + }); + }); + }); + + it.each(testCases)( + 'should return fetchReferenceDataAuditSuccess and fetchReferenceDataAudit actions on successful API call with pagination token', + (value) => { + testScheduler.run(({ hot, cold, expectObservable }) => { + const { resourceType, payload } = value; + const apiResponse = { data: [...payload], paginationToken: 'token' }; + + // mock action to trigger effect + actions$ = hot('-a--', { a: fetchReferenceDataAudit({ resourceType }) }); + + // mock service call + jest.spyOn(referenceDataService, 'fetchReferenceDataAudit').mockReturnValue(cold('--a|', { a: apiResponse })); + + // expect effect to return success action + expectObservable(effects.fetchReferenceDataByAuditType$).toBe('---(bc)', { + b: fetchReferenceDataAuditSuccess({ resourceType, payload, paginated: true }), + c: fetchReferenceDataAudit({ resourceType, paginationToken: 'token' }), + }); + }); + } + ); + + it('should return fetchReferenceDataAuditFailed action on API error', () => { + testScheduler.run(({ hot, cold, expectObservable }) => { + actions$ = hot('-a--', { + a: fetchReferenceDataAudit({ resourceType: null as unknown as ReferenceDataResourceType }), + }); + + const expectedError = new Error('Reference data resourceType is required'); + + jest.spyOn(referenceDataService, 'fetchReferenceDataAudit').mockReturnValue(cold('--#|', {}, expectedError)); + + expectObservable(effects.fetchReferenceDataByAuditType$).toBe('---b', { + b: fetchReferenceDataAuditFailed({ + error: 'Reference data resourceType is required', + resourceType: null as unknown as ReferenceDataResourceType, + }), + }); + }); + }); + + it('should return fetchReferenceDataAuditFailed action when data is not found', () => { + testScheduler.run(({ hot, cold, expectObservable }) => { + actions$ = hot('-a--', { a: fetchReferenceDataAudit({ resourceType: ReferenceDataResourceType.HgvMake }) }); + + jest.spyOn(referenceDataService, 'fetchReferenceDataAudit').mockReturnValue(cold('--a|', { a: { data: [] } })); + + expectObservable(effects.fetchReferenceDataByAuditType$).toBe('---b', { + b: fetchReferenceDataAuditFailed({ + error: `Reference data not found for resource type ${ReferenceDataResourceType.HgvMake}`, + resourceType: ReferenceDataResourceType.HgvMake, + }), + }); + }); + }); + }); + + describe('fetchReferenceDataByKey$', () => { + it.each(testCases)('should return fetchReferenceDataByKeySuccess action on successful API call', (value) => { + testScheduler.run(({ hot, cold, expectObservable }) => { + const { resourceType, resourceKey, payload } = value; + + const entity = payload.find((p) => p.resourceKey === resourceKey) as ReferenceDataItem; + + // mock action to trigger effect + actions$ = hot('-a--', { a: fetchReferenceDataByKey({ resourceType, resourceKey }) }); + + // mock service call + jest.spyOn(referenceDataService, 'fetchReferenceDataByKey').mockReturnValue(cold('--a|', { a: entity })); + + // expect effect to return success action + expectObservable(effects.fetchReferenceDataByKey$).toBe('---b', { + b: fetchReferenceDataByKeySuccess({ resourceType, resourceKey, payload: entity as ReferenceDataModelBase }), + }); + }); + }); + + it.each(testCases)('should return fetchReferenceDataByKeyFailed action on API error', (value) => { + testScheduler.run(({ hot, cold, expectObservable }) => { + const { resourceType } = value; + actions$ = hot('-a--', { + a: fetchReferenceDataByKey({ resourceType, resourceKey: null as unknown as string | number }), + }); + + const expectedError = new Error('Reference data resourceKey is required'); + + jest.spyOn(referenceDataService, 'fetchReferenceDataByKey').mockReturnValue(cold('--#|', {}, expectedError)); + + expectObservable(effects.fetchReferenceDataByKey$).toBe('---b', { + b: fetchReferenceDataByKeyFailed({ error: 'Reference data resourceKey is required', resourceType }), + }); + }); + }); + + it.each(testCases)('should return fetchReferenceDataByKeyFailed action when resource not found', (value) => { + testScheduler.run(({ hot, cold, expectObservable }) => { + const { resourceType } = value; + actions$ = hot('-a--', { a: fetchReferenceDataByKey({ resourceType, resourceKey: 'xx' }) }); + + jest.spyOn(referenceDataService, 'fetchReferenceDataByKey').mockReturnValue(cold('--a|', { a: {} })); + + expectObservable(effects.fetchReferenceDataByKey$).toBe('---b', { + b: fetchReferenceDataByKeyFailed({ + error: `Reference data not found for resource type ${resourceType},xx`, + resourceType, + }), + }); + }); + }); + }); + + describe('fetchReferenceDataByKeySearch', () => { + it('should return fetchReferenceDataByKeySearchSuccess on successful API call', () => { + testScheduler.run(({ hot, cold, expectObservable }) => { + const resourceType = ReferenceDataResourceType.Tyres; + const resourceKey = '123'; + const value = { + payload: [ + { + tyreCode: '123', + resourceType: ReferenceDataResourceType.Tyres, + resourceKey: '123', + code: '123', + loadIndexSingleLoad: '102', + tyreSize: 'size', + dateTimeStamp: 'time', + userId: '1234', + loadIndexTwinLoad: '101', + plyRating: '18', + }, + ], + }; + const apiResponse = { data: [...value.payload] }; + + actions$ = hot('-a--', { a: fetchReferenceDataByKeySearch({ resourceType, resourceKey }) }); + + jest + .spyOn(referenceDataService, 'fetchReferenceDataByKeySearch') + .mockReturnValue(cold('--a|', { a: apiResponse })); + + expectObservable(effects.fetchReferenceDataByKeySearch$).toBe('---b', { + b: fetchReferenceDataByKeySearchSuccess({ + resourceType, + resourceKey, + payload: value.payload as ReferenceDataModelBase[], + }), + }); + }); + }); + + it('should return fetchReferenceDataByKeySearchFailed on successful API call', () => { + testScheduler.run(({ hot, cold, expectObservable }) => { + const resourceType = ReferenceDataResourceType.Tyres; + actions$ = hot('-a--', { + a: fetchReferenceDataByKeySearch({ resourceType, resourceKey: null as unknown as string | number }), + }); + + const expectedError = new Error('Reference data resourceKey is required'); + + jest + .spyOn(referenceDataService, 'fetchReferenceDataByKeySearch') + .mockReturnValue(cold('--#|', {}, expectedError)); + + expectObservable(effects.fetchReferenceDataByKeySearch$).toBe('---b', { + b: fetchReferenceDataByKeySearchFailed({ error: 'Reference data resourceKey is required', resourceType }), + }); + }); + }); + }); + + describe('fetchTyreReferenceDataByKeySearch', () => { + it('should return fetchTyreReferenceDataByKeySearchSuccess on successful API call', () => { + testScheduler.run(({ hot, cold, expectObservable }) => { + const resourceType = ReferenceDataResourceType.Tyres; + const value = { + payload: [ + { + tyreCode: '123', + resourceType: ReferenceDataResourceType.Tyres, + resourceKey: '123', + code: '123', + loadIndexSingleLoad: '102', + tyreSize: 'size', + dateTimeStamp: 'time', + userId: '1234', + loadIndexTwinLoad: '101', + plyRating: '18', + }, + ], + }; + const apiResponse = { data: [...value.payload] }; + + actions$ = hot('-a--', { + a: fetchTyreReferenceDataByKeySearch({ searchFilter: 'plyRating', searchTerm: '123' }), + }); + + jest + .spyOn(referenceDataService, 'fetchTyreReferenceDataByKeySearch') + .mockReturnValue(cold('--a|', { a: apiResponse })); + + expectObservable(effects.fetchTyreReferenceDataByKeySearch$).toBe('---b', { + b: fetchTyreReferenceDataByKeySearchSuccess({ + resourceType, + payload: value.payload as ReferenceDataModelBase[], + }), + }); + }); + }); + + it('should return fetchTyreReferenceDataByKeySearchFailed on unsuccessful API call', () => { + testScheduler.run(({ hot, cold, expectObservable }) => { + const resourceType = ReferenceDataResourceType.Tyres; + actions$ = hot('-a--', { + a: fetchTyreReferenceDataByKeySearch({ searchFilter: 'plyRating', searchTerm: null as unknown as string }), + }); + + const expectedError = new Error('Search term is required'); + + jest + .spyOn(referenceDataService, 'fetchTyreReferenceDataByKeySearch') + .mockReturnValue(cold('--#|', {}, expectedError)); + + expectObservable(effects.fetchTyreReferenceDataByKeySearch$).toBe('---b', { + b: fetchTyreReferenceDataByKeySearchFailed({ error: 'Search term is required', resourceType }), + }); + }); + }); + }); + + const vehicleTypeReasonsForAbandoning = [ + { + vehicleType: VehicleTypes.PSV, + resourceType: ReferenceDataResourceType.ReasonsForAbandoningPsv, + }, + { + vehicleType: VehicleTypes.TRL, + resourceType: ReferenceDataResourceType.ReasonsForAbandoningTrl, + }, + { + vehicleType: VehicleTypes.HGV, + resourceType: ReferenceDataResourceType.ReasonsForAbandoningHgv, + }, + ]; + it.each(vehicleTypeReasonsForAbandoning)( + 'should dispatch the action to fetch the reasons for abandoning for the right vehicle', + (values) => { + const { vehicleType, resourceType } = values; + const testResult = { vehicleType } as TestResultModel; + + testScheduler.run(({ hot, expectObservable }) => { + store.overrideSelector(testResultInEdit, testResult); + + actions$ = hot('-a', { + a: fetchReasonsForAbandoning(), + }); + + expectObservable(effects.fetchReasonsForAbandoning).toBe('-b', { + b: fetchReferenceData({ + resourceType, + }), + }); + }); + } + ); + + describe('createReferenceDataItem$', () => { + it('should return createReferenceDataItemSuccess on a successful call', () => { + testScheduler.run(({ hot, cold, expectObservable }) => { + const resourceType = ReferenceDataResourceType.CountryOfRegistration; + const resourceKey = 'testKey'; + const body = { + description: 'test country', + }; + const apiResponse = { ...body, resourceType, resourceKey }; + + actions$ = hot('-a--', { + a: createReferenceDataItem({ resourceType, resourceKey, payload: body as ReferenceDataModelBase }), + }); + + jest.spyOn(referenceDataService, 'createReferenceDataItem').mockReturnValue(cold('--a|', { a: apiResponse })); + + expectObservable(effects.createReferenceDataItem$).toBe('---b', { + b: createReferenceDataItemSuccess({ result: apiResponse as ReferenceDataModelBase }), + }); + }); + }); + it('should return createReferenceDataItemFailure if an error is returned', () => { + testScheduler.run(({ hot, cold, expectObservable }) => { + const resourceType = ReferenceDataResourceType.CountryOfRegistration; + const resourceKey = 'testKey'; + const body = { + description: 'test country', + }; + + actions$ = hot('-a--', { + a: createReferenceDataItem({ resourceType, resourceKey, payload: body as ReferenceDataModelBase }), + }); + + const expectedError = new Error('Something went wrong'); + + jest.spyOn(referenceDataService, 'createReferenceDataItem').mockReturnValue(cold('--#|', {}, expectedError)); + + expectObservable(effects.createReferenceDataItem$).toBe('---b', { + b: createReferenceDataItemFailure({ error: 'Something went wrong' }), + }); + }); + }); + }); + + describe('amendReferenceDataItem$', () => { + it('should return amendReferenceDataI on a successful call', () => { + testScheduler.run(({ hot, cold, expectObservable }) => { + const resourceType = ReferenceDataResourceType.CountryOfRegistration; + const resourceKey = 'testKey'; + const body = { + description: 'test country', + }; + const apiResponse = { ...body, resourceType, resourceKey }; + + actions$ = hot('-a--', { + a: amendReferenceDataItem({ resourceType, resourceKey, payload: body as ReferenceDataModelBase }), + }); + + jest.spyOn(referenceDataService, 'amendReferenceDataItem').mockReturnValue(cold('--a-|', { a: apiResponse })); + + expectObservable(effects.amendReferenceDataItem$).toBe('---b', { + b: amendReferenceDataItemSuccess({ result: apiResponse as ReferenceDataModelBase }), + }); + }); + }); + it('should return amendReferenceDataItemFailure if an error is returned', () => { + testScheduler.run(({ hot, cold, expectObservable }) => { + const resourceType = ReferenceDataResourceType.CountryOfRegistration; + const resourceKey = 'testKey'; + const body = { + description: 'test country', + }; + + actions$ = hot('-a--', { + a: amendReferenceDataItem({ resourceType, resourceKey, payload: body as ReferenceDataModelBase }), + }); + + const expectedError = new Error('Something went wrong'); + + jest.spyOn(referenceDataService, 'amendReferenceDataItem').mockReturnValue(cold('--#|', {}, expectedError)); + + expectObservable(effects.amendReferenceDataItem$).toBe('---b', { + b: amendReferenceDataItemFailure({ error: 'Something went wrong' }), + }); + }); + }); + }); + + describe('deleteReferenceDataItem$', () => { + it('should return deleteReferenceDataSuccess on a successful call', () => { + testScheduler.run(({ hot, cold, expectObservable }) => { + const resourceType = ReferenceDataResourceType.CountryOfRegistration; + const resourceKey = 'testKey'; + const reason = 'for test'; + const apiResponse = { result: true }; + + actions$ = hot('-a--', { a: deleteReferenceDataItem({ resourceType, resourceKey, reason }) }); + + jest + .spyOn(referenceDataService, 'deleteReferenceDataItem') + .mockReturnValue(cold('--a-|', { a: apiResponse as DeleteItem })); + + expectObservable(effects.deleteReferenceDataItem$).toBe('---b', { + b: deleteReferenceDataItemSuccess({ resourceType, resourceKey }), + }); + }); + }); + it('should return deleteReferenceDataFailure if an error is returned', () => { + testScheduler.run(({ hot, cold, expectObservable }) => { + const resourceType = ReferenceDataResourceType.CountryOfRegistration; + const resourceKey = 'testKey'; + const reason = 'testing'; + + actions$ = hot('-a--', { a: deleteReferenceDataItem({ resourceType, resourceKey, reason }) }); + + const expectedError = new Error('Something went wrong'); + + jest.spyOn(referenceDataService, 'deleteReferenceDataItem').mockReturnValue(cold('--#|', {}, expectedError)); + + expectObservable(effects.deleteReferenceDataItem$).toBe('---b', { + b: deleteReferenceDataItemFailure({ error: 'Something went wrong' }), + }); + }); + }); + }); }); diff --git a/src/app/store/reference-data/effects/reference-data.effects.ts b/src/app/store/reference-data/effects/reference-data.effects.ts index 3b7ef5fd2a..5ed331a6ce 100644 --- a/src/app/store/reference-data/effects/reference-data.effects.ts +++ b/src/app/store/reference-data/effects/reference-data.effects.ts @@ -1,186 +1,232 @@ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { ReferenceDataApiResponse, ReferenceDataApiResponseWithPagination } from '@api/reference-data'; import { ReferenceDataModelBase, ReferenceDataResourceType } from '@models/reference-data.model'; import { VehicleTypes } from '@models/vehicle-tech-record.model'; import { Actions, createEffect, ofType } from '@ngrx/effects'; -import { select, Store } from '@ngrx/store'; +import { Store, select } from '@ngrx/store'; import { ReferenceDataService } from '@services/reference-data/reference-data.service'; -import { UserService } from '@services/user-service/user-service'; import { State } from '@store/.'; import { testResultInEdit } from '@store/test-records'; +import { catchError, map, mergeMap, of, switchMap, take } from 'rxjs'; import { - catchError, map, mergeMap, of, switchMap, take, -} from 'rxjs'; -import { - amendReferenceDataItem, - amendReferenceDataItemFailure, - amendReferenceDataItemSuccess, - createReferenceDataItem, - createReferenceDataItemFailure, - createReferenceDataItemSuccess, - deleteReferenceDataItem, - deleteReferenceDataItemFailure, - deleteReferenceDataItemSuccess, - fetchReasonsForAbandoning, - fetchReferenceData, - fetchReferenceDataAudit, - fetchReferenceDataAuditFailed, - fetchReferenceDataAuditSuccess, - fetchReferenceDataByKey, - fetchReferenceDataByKeyFailed, - fetchReferenceDataByKeySearch, - fetchReferenceDataByKeySearchFailed, - fetchReferenceDataByKeySearchSuccess, - fetchReferenceDataByKeySuccess, - fetchReferenceDataFailed, - fetchReferenceDataSuccess, - fetchTyreReferenceDataByKeySearch, - fetchTyreReferenceDataByKeySearchFailed, - fetchTyreReferenceDataByKeySearchSuccess, + amendReferenceDataItem, + amendReferenceDataItemFailure, + amendReferenceDataItemSuccess, + createReferenceDataItem, + createReferenceDataItemFailure, + createReferenceDataItemSuccess, + deleteReferenceDataItem, + deleteReferenceDataItemFailure, + deleteReferenceDataItemSuccess, + fetchReasonsForAbandoning, + fetchReferenceData, + fetchReferenceDataAudit, + fetchReferenceDataAuditFailed, + fetchReferenceDataAuditSuccess, + fetchReferenceDataByKey, + fetchReferenceDataByKeyFailed, + fetchReferenceDataByKeySearch, + fetchReferenceDataByKeySearchFailed, + fetchReferenceDataByKeySearchSuccess, + fetchReferenceDataByKeySuccess, + fetchReferenceDataFailed, + fetchReferenceDataSuccess, + fetchTyreReferenceDataByKeySearch, + fetchTyreReferenceDataByKeySearchFailed, + fetchTyreReferenceDataByKeySearchSuccess, } from '../actions/reference-data.actions'; import { handleNotFound, sortReferenceData } from './operators'; @Injectable() export class ReferenceDataEffects { - constructor( - private actions$: Actions, - private userService: UserService, - private referenceDataService: ReferenceDataService, - private store: Store, - ) {} + private actions$ = inject(Actions); + private referenceDataService = inject(ReferenceDataService); + private store = inject>(Store); - fetchReferenceDataByType$ = createEffect(() => - this.actions$.pipe( - ofType(fetchReferenceData), - mergeMap(({ resourceType, paginationToken }) => - this.referenceDataService.fetchReferenceData(resourceType, paginationToken).pipe( - handleNotFound(resourceType), - sortReferenceData(resourceType), - switchMap((data) => { - if (isPaginated(data)) { - return of( - fetchReferenceDataSuccess({ resourceType, payload: data.data as ReferenceDataModelBase[], paginated: true }), - fetchReferenceData({ resourceType, paginationToken: data.paginationToken }), - ); - } - return of(fetchReferenceDataSuccess({ resourceType, payload: data.data as ReferenceDataModelBase[], paginated: false })); - }), - catchError((e) => of(fetchReferenceDataFailed({ error: e.message, resourceType }))), - )), - )); + fetchReferenceDataByType$ = createEffect(() => + this.actions$.pipe( + ofType(fetchReferenceData), + mergeMap(({ resourceType, paginationToken }) => + this.referenceDataService.fetchReferenceData(resourceType, paginationToken).pipe( + handleNotFound(resourceType), + sortReferenceData(resourceType), + switchMap((data) => { + if (isPaginated(data)) { + return of( + fetchReferenceDataSuccess({ + resourceType, + payload: data.data as ReferenceDataModelBase[], + paginated: true, + }), + fetchReferenceData({ resourceType, paginationToken: data.paginationToken }) + ); + } + return of( + fetchReferenceDataSuccess({ + resourceType, + payload: data.data as ReferenceDataModelBase[], + paginated: false, + }) + ); + }), + catchError((e) => of(fetchReferenceDataFailed({ error: e.message, resourceType }))) + ) + ) + ) + ); - fetchReferenceDataByAuditType$ = createEffect(() => - this.actions$.pipe( - ofType(fetchReferenceDataAudit), - mergeMap(({ resourceType, paginationToken }) => - this.referenceDataService.fetchReferenceDataAudit(resourceType, paginationToken).pipe( - handleNotFound(resourceType), - sortReferenceData(resourceType), - switchMap((data) => { - if (isPaginated(data)) { - return of( - fetchReferenceDataAuditSuccess({ resourceType, payload: data.data as ReferenceDataModelBase[], paginated: true }), - fetchReferenceDataAudit({ resourceType, paginationToken: data.paginationToken }), - ); - } - return of(fetchReferenceDataAuditSuccess({ resourceType, payload: data.data as ReferenceDataModelBase[], paginated: false })); - }), - catchError((e) => of(fetchReferenceDataAuditFailed({ error: e.message, resourceType }))), - )), - )); + fetchReferenceDataByAuditType$ = createEffect(() => + this.actions$.pipe( + ofType(fetchReferenceDataAudit), + mergeMap(({ resourceType, paginationToken }) => + this.referenceDataService.fetchReferenceDataAudit(resourceType, paginationToken).pipe( + handleNotFound(resourceType), + sortReferenceData(resourceType), + switchMap((data) => { + if (isPaginated(data)) { + return of( + fetchReferenceDataAuditSuccess({ + resourceType, + payload: data.data as ReferenceDataModelBase[], + paginated: true, + }), + fetchReferenceDataAudit({ resourceType, paginationToken: data.paginationToken }) + ); + } + return of( + fetchReferenceDataAuditSuccess({ + resourceType, + payload: data.data as ReferenceDataModelBase[], + paginated: false, + }) + ); + }), + catchError((e) => of(fetchReferenceDataAuditFailed({ error: e.message, resourceType }))) + ) + ) + ) + ); - fetchReferenceDataByKey$ = createEffect(() => - this.actions$.pipe( - ofType(fetchReferenceDataByKey), - mergeMap(({ resourceType, resourceKey }) => - this.referenceDataService.fetchReferenceDataByKey(resourceType, resourceKey).pipe( - handleNotFound(resourceType, resourceKey), - map((data) => fetchReferenceDataByKeySuccess({ resourceType, resourceKey, payload: data as ReferenceDataModelBase })), - catchError((e) => of(fetchReferenceDataByKeyFailed({ error: e.message, resourceType }))), - )), - )); + fetchReferenceDataByKey$ = createEffect(() => + this.actions$.pipe( + ofType(fetchReferenceDataByKey), + mergeMap(({ resourceType, resourceKey }) => + this.referenceDataService.fetchReferenceDataByKey(resourceType, resourceKey).pipe( + handleNotFound(resourceType, resourceKey), + map((data) => + fetchReferenceDataByKeySuccess({ resourceType, resourceKey, payload: data as ReferenceDataModelBase }) + ), + catchError((e) => of(fetchReferenceDataByKeyFailed({ error: e.message, resourceType }))) + ) + ) + ) + ); - fetchReferenceDataByKeySearch$ = createEffect(() => - this.actions$.pipe( - ofType(fetchReferenceDataByKeySearch), - mergeMap(({ resourceType, resourceKey }) => - this.referenceDataService.fetchReferenceDataByKeySearch(resourceType, resourceKey).pipe( - map((data) => fetchReferenceDataByKeySearchSuccess({ resourceType, resourceKey, payload: data.data as ReferenceDataModelBase[] })), - catchError((e) => of(fetchReferenceDataByKeySearchFailed({ error: e.message, resourceType }))), - )), - )); + fetchReferenceDataByKeySearch$ = createEffect(() => + this.actions$.pipe( + ofType(fetchReferenceDataByKeySearch), + mergeMap(({ resourceType, resourceKey }) => + this.referenceDataService.fetchReferenceDataByKeySearch(resourceType, resourceKey).pipe( + map((data) => + fetchReferenceDataByKeySearchSuccess({ + resourceType, + resourceKey, + payload: data.data as ReferenceDataModelBase[], + }) + ), + catchError((e) => of(fetchReferenceDataByKeySearchFailed({ error: e.message, resourceType }))) + ) + ) + ) + ); - fetchTyreReferenceDataByKeySearch$ = createEffect(() => - this.actions$.pipe( - ofType(fetchTyreReferenceDataByKeySearch), - mergeMap(({ searchFilter, searchTerm }) => - this.referenceDataService.fetchTyreReferenceDataByKeySearch(searchFilter, searchTerm).pipe( - map((data) => - fetchTyreReferenceDataByKeySearchSuccess({ - resourceType: ReferenceDataResourceType.Tyres, - payload: data.data as ReferenceDataModelBase[], - })), - catchError((e) => of(fetchTyreReferenceDataByKeySearchFailed({ error: e.message, resourceType: ReferenceDataResourceType.Tyres }))), - )), - )); + fetchTyreReferenceDataByKeySearch$ = createEffect(() => + this.actions$.pipe( + ofType(fetchTyreReferenceDataByKeySearch), + mergeMap(({ searchFilter, searchTerm }) => + this.referenceDataService.fetchTyreReferenceDataByKeySearch(searchFilter, searchTerm).pipe( + map((data) => + fetchTyreReferenceDataByKeySearchSuccess({ + resourceType: ReferenceDataResourceType.Tyres, + payload: data.data as ReferenceDataModelBase[], + }) + ), + catchError((e) => + of( + fetchTyreReferenceDataByKeySearchFailed({ + error: e.message, + resourceType: ReferenceDataResourceType.Tyres, + }) + ) + ) + ) + ) + ) + ); - fetchReasonsForAbandoning = createEffect(() => - this.actions$.pipe( - ofType(fetchReasonsForAbandoning), - mergeMap(() => this.store.pipe(select(testResultInEdit), take(1))), - map((testResult) => { - switch (testResult?.vehicleType) { - case VehicleTypes.PSV: - return fetchReferenceData({ resourceType: ReferenceDataResourceType.ReasonsForAbandoningPsv }); - case VehicleTypes.HGV: - return fetchReferenceData({ resourceType: ReferenceDataResourceType.ReasonsForAbandoningHgv }); - case VehicleTypes.TRL: - return fetchReferenceData({ resourceType: ReferenceDataResourceType.ReasonsForAbandoningTrl }); - default: - throw new Error('Unexpected vehicle type'); - } - }), - )); + fetchReasonsForAbandoning = createEffect(() => + this.actions$.pipe( + ofType(fetchReasonsForAbandoning), + mergeMap(() => this.store.pipe(select(testResultInEdit), take(1))), + map((testResult) => { + switch (testResult?.vehicleType) { + case VehicleTypes.PSV: + return fetchReferenceData({ resourceType: ReferenceDataResourceType.ReasonsForAbandoningPsv }); + case VehicleTypes.HGV: + return fetchReferenceData({ resourceType: ReferenceDataResourceType.ReasonsForAbandoningHgv }); + case VehicleTypes.TRL: + return fetchReferenceData({ resourceType: ReferenceDataResourceType.ReasonsForAbandoningTrl }); + default: + throw new Error('Unexpected vehicle type'); + } + }) + ) + ); - createReferenceDataItem$ = createEffect(() => - this.actions$.pipe( - ofType(createReferenceDataItem), - switchMap(({ resourceType, resourceKey, payload }) => { - payload = { ...payload }; - return this.referenceDataService.createReferenceDataItem(resourceType, resourceKey, payload).pipe( - map((result) => createReferenceDataItemSuccess({ result: result as ReferenceDataModelBase })), - catchError((error) => of(createReferenceDataItemFailure({ error: error.message }))), - ); - }), - )); + createReferenceDataItem$ = createEffect(() => + this.actions$.pipe( + ofType(createReferenceDataItem), + switchMap(({ resourceType, resourceKey, payload }) => { + payload = { ...payload }; + return this.referenceDataService.createReferenceDataItem(resourceType, resourceKey, payload).pipe( + map((result) => createReferenceDataItemSuccess({ result: result as ReferenceDataModelBase })), + catchError((error) => of(createReferenceDataItemFailure({ error: error.message }))) + ); + }) + ) + ); - // The amend effect will work when the referenceData.service.ts is amended on line 395 from to + // The amend effect will work when the referenceData.service.ts is amended on line 395 from to - amendReferenceDataItem$ = createEffect(() => - this.actions$.pipe( - ofType(amendReferenceDataItem), - switchMap(({ resourceType, resourceKey, payload }) => { - payload = { ...payload }; - return this.referenceDataService.amendReferenceDataItem(resourceType, resourceKey, payload).pipe( - map((result) => amendReferenceDataItemSuccess({ result: result as ReferenceDataModelBase })), - catchError((error) => of(amendReferenceDataItemFailure({ error: error.message }))), - ); - }), - )); + amendReferenceDataItem$ = createEffect(() => + this.actions$.pipe( + ofType(amendReferenceDataItem), + switchMap(({ resourceType, resourceKey, payload }) => { + payload = { ...payload }; + return this.referenceDataService.amendReferenceDataItem(resourceType, resourceKey, payload).pipe( + map((result) => amendReferenceDataItemSuccess({ result: result as ReferenceDataModelBase })), + catchError((error) => of(amendReferenceDataItemFailure({ error: error.message }))) + ); + }) + ) + ); - deleteReferenceDataItem$ = createEffect(() => - this.actions$.pipe( - ofType(deleteReferenceDataItem), - switchMap(({ resourceType, resourceKey, reason }) => { - const payload = { reason }; - return this.referenceDataService.deleteReferenceDataItem(resourceType, resourceKey, payload).pipe( - map(() => deleteReferenceDataItemSuccess({ resourceType, resourceKey })), - catchError((error) => of(deleteReferenceDataItemFailure({ error: error.message }))), - ); - }), - )); + deleteReferenceDataItem$ = createEffect(() => + this.actions$.pipe( + ofType(deleteReferenceDataItem), + switchMap(({ resourceType, resourceKey, reason }) => { + const payload = { reason }; + return this.referenceDataService.deleteReferenceDataItem(resourceType, resourceKey, payload).pipe( + map(() => deleteReferenceDataItemSuccess({ resourceType, resourceKey })), + catchError((error) => of(deleteReferenceDataItemFailure({ error: error.message }))) + ); + }) + ) + ); } -function isPaginated(referenceDataApiResponse: ReferenceDataApiResponse): referenceDataApiResponse is ReferenceDataApiResponseWithPagination { - return Object.prototype.hasOwnProperty.call(referenceDataApiResponse, 'paginationToken'); +function isPaginated( + referenceDataApiResponse: ReferenceDataApiResponse +): referenceDataApiResponse is ReferenceDataApiResponseWithPagination { + return Object.prototype.hasOwnProperty.call(referenceDataApiResponse, 'paginationToken'); } diff --git a/src/app/store/reference-data/reducers/reference-data.reducer.spec.ts b/src/app/store/reference-data/reducers/reference-data.reducer.spec.ts index 54bf8cf505..9c6c511e00 100644 --- a/src/app/store/reference-data/reducers/reference-data.reducer.spec.ts +++ b/src/app/store/reference-data/reducers/reference-data.reducer.spec.ts @@ -3,493 +3,511 @@ import { ReferenceDataModelBase, ReferenceDataResourceType } from '@models/refer import { Dictionary } from '@ngrx/entity'; import cloneDeep from 'lodash.clonedeep'; import { - addSearchInformation, - amendReferenceDataItemSuccess, - createReferenceDataItemSuccess, - deleteReferenceDataItemSuccess, - fetchReferenceData, - fetchReferenceDataAudit, - fetchReferenceDataAuditFailed, - fetchReferenceDataAuditSuccess, - fetchReferenceDataByKey, - fetchReferenceDataByKeyFailed, - fetchReferenceDataByKeySearch, - fetchReferenceDataByKeySearchFailed, - fetchReferenceDataByKeySearchSuccess, - fetchReferenceDataByKeySuccess, - fetchReferenceDataFailed, - fetchReferenceDataSuccess, - fetchTyreReferenceDataByKeySearch, - fetchTyreReferenceDataByKeySearchFailed, - fetchTyreReferenceDataByKeySearchSuccess, - removeTyreSearch, + addSearchInformation, + amendReferenceDataItemSuccess, + createReferenceDataItemSuccess, + deleteReferenceDataItemSuccess, + fetchReferenceData, + fetchReferenceDataAudit, + fetchReferenceDataAuditFailed, + fetchReferenceDataAuditSuccess, + fetchReferenceDataByKey, + fetchReferenceDataByKeyFailed, + fetchReferenceDataByKeySearch, + fetchReferenceDataByKeySearchFailed, + fetchReferenceDataByKeySearchSuccess, + fetchReferenceDataByKeySuccess, + fetchReferenceDataFailed, + fetchReferenceDataSuccess, + fetchTyreReferenceDataByKeySearch, + fetchTyreReferenceDataByKeySearchFailed, + fetchTyreReferenceDataByKeySearchSuccess, + removeTyreSearch, } from '../actions/reference-data.actions'; import { testCases } from '../reference-data.test-cases'; import { ReferenceDataState, initialReferenceDataState, referenceDataReducer } from './reference-data.reducer'; describe('Reference Data Reducer', () => { - describe('unknown action', () => { - it('should return the default state', () => { - const action = { - type: 'Unknown', - }; - const state = referenceDataReducer(initialReferenceDataState, action); - - expect(state).toBe(initialReferenceDataState); - }); - }); - - describe('fetchReferenceData', () => { - it('should set loading to true', () => { - const newState: ReferenceDataState = { - ...initialReferenceDataState, - [ReferenceDataResourceType.CountryOfRegistration]: { - ...initialReferenceDataState[ReferenceDataResourceType.CountryOfRegistration], - loading: true, - }, - }; - const action = fetchReferenceData({ resourceType: ReferenceDataResourceType.CountryOfRegistration }); - const state = referenceDataReducer(initialReferenceDataState, action); - - expect(state).toEqual(newState); - expect(state).not.toBe(newState); - }); - }); - - describe('fetchReferenceDataSuccess', () => { - it.each(testCases)('should set all reference data on success', (value) => { - const { resourceType, payload } = value; - const ids = payload.map((v) => v.resourceKey); - const entities: Dictionary = payload.reduce( - (acc, v) => ({ ...acc, [v.resourceKey]: v }), - {} as { [V in ReferenceDataModelBase as V['resourceKey']]: V }, - ); - const newState: ReferenceDataState = { - ...initialReferenceDataState, - [resourceType]: { ids, entities, loading: false }, - }; - const action = fetchReferenceDataSuccess({ resourceType, payload: [...payload], paginated: false }); - const state = referenceDataReducer(initialReferenceDataState, action); - - expect(state).toEqual(newState); - expect(state).not.toBe(newState); - }); - }); - - describe('fetchReferenceDataFailed', () => { - it('should set error state', () => { - const newState = { ...initialReferenceDataState }; - const action = fetchReferenceDataFailed({ error: 'unit testing error message', resourceType: ReferenceDataResourceType.CountryOfRegistration }); - const state = referenceDataReducer({ ...initialReferenceDataState }, action); - - expect(state).toEqual(newState); - expect(state).not.toBe(newState); - }); - }); - - describe('fetchReferenceDataAudit', () => { - it('should set loading to true', () => { - const newState: ReferenceDataState = { - ...initialReferenceDataState, - [ReferenceDataResourceType.CountryOfRegistration]: { - ...initialReferenceDataState[ReferenceDataResourceType.CountryOfRegistration], - loading: true, - }, - }; - const action = fetchReferenceDataAudit({ resourceType: ReferenceDataResourceType.CountryOfRegistration }); - const state = referenceDataReducer(initialReferenceDataState, action); - - expect(state).toEqual(newState); - expect(state).not.toBe(newState); - }); - }); - - describe('fetchReferenceDataAuditSuccess', () => { - it.each(testCases)('should set the the resource data item based on the type', (value) => { - const { resourceType } = value; - const newState: ReferenceDataState = { - ...initialReferenceDataState, - [resourceType]: { ...initialReferenceDataState[`${resourceType}`], searchReturn: value.payload, loading: false }, - }; - - const action = fetchReferenceDataAuditSuccess({ resourceType, payload: value.payload, paginated: false }); - const state = referenceDataReducer(initialReferenceDataState, action); - - expect(state).toEqual(newState); - expect(state).not.toBe(newState); - }); - }); - - describe('fetchReferenceDataAuditFailed', () => { - it('should set error state', () => { - const newState = { ...initialReferenceDataState }; - const action = fetchReferenceDataAuditFailed({ - error: 'unit testing error message', - resourceType: ReferenceDataResourceType.CountryOfRegistration, - }); - const state = referenceDataReducer({ ...initialReferenceDataState }, action); - - expect(state).toEqual(newState); - expect(state).not.toBe(newState); - }); - }); - - describe('fetchReferenceDataByKey actions', () => { - it('should set loading to true', () => { - const newState: ReferenceDataState = { - ...initialReferenceDataState, - [ReferenceDataResourceType.CountryOfRegistration]: { - ...initialReferenceDataState[ReferenceDataResourceType.CountryOfRegistration], - loading: true, - }, - }; - const action = fetchReferenceDataByKey({ - resourceType: ReferenceDataResourceType.CountryOfRegistration, - resourceKey: mockCountriesOfRegistration[0].resourceKey, - }); - const state = referenceDataReducer(initialReferenceDataState, action); - - expect(state).toEqual(newState); - expect(state).not.toBe(newState); - }); - - describe('fetchReferenceDataByKeySuccess', () => { - it.each(testCases)('should set the the resource data item based on the type and key', (value) => { - const { resourceType, resourceKey, payload } = value; - - const entity = payload.find((p) => p.resourceKey === resourceKey) as ReferenceDataModelBase; - const ids = [resourceKey]; - const entities: Dictionary = { [resourceKey]: entity }; - const newState: ReferenceDataState = { - ...initialReferenceDataState, - [resourceType]: { ids, entities, loading: false }, - }; - - const action = fetchReferenceDataByKeySuccess({ resourceType, resourceKey, payload: entity }); - const state = referenceDataReducer(initialReferenceDataState, action); - - expect(state).toEqual(newState); - expect(state).not.toBe(newState); - }); - }); - - describe('fetchReferenceDataByKeyFailed', () => { - it('should set error state', () => { - const newState = { ...initialReferenceDataState }; - const action = fetchReferenceDataByKeyFailed({ - error: 'unit testing error message by key', - resourceType: ReferenceDataResourceType.CountryOfRegistration, - }); - const inputState = { - ...initialReferenceDataState, - [ReferenceDataResourceType.CountryOfRegistration]: { - ...initialReferenceDataState[ReferenceDataResourceType.CountryOfRegistration], - loading: true, - }, - }; - const state = referenceDataReducer(inputState, action); - - expect(state).toEqual(newState); - expect(state).not.toBe(newState); - }); - }); - }); - - describe('fetchReferenceDataByKeySearch actions', () => { - it('should set loading to true', () => { - const newState: ReferenceDataState = { - ...initialReferenceDataState, - [ReferenceDataResourceType.Tyres]: { - ...initialReferenceDataState[ReferenceDataResourceType.Tyres], - searchReturn: null, - loading: true, - }, - }; - const action = fetchReferenceDataByKeySearch({ - resourceType: ReferenceDataResourceType.Tyres, - resourceKey: '101', - }); - const state = referenceDataReducer(initialReferenceDataState, action); - - expect(state).toEqual(newState); - expect(state).not.toBe(newState); - }); - - describe('fetchReferenceDataByKeySearchSuccess', () => { - it('should set the the resource data item based on the type and key', () => { - const resourceType = ReferenceDataResourceType.Tyres; - const resourceKey = '123'; - const value = { - payload: [ - { - tyreCode: '123', - resourceType: ReferenceDataResourceType.Tyres, - resourceKey: '123', - code: '123', - loadIndexSingleLoad: '102', - tyreSize: 'size', - dateTimeStamp: 'time', - userId: '1234', - loadIndexTwinLoad: '101', - plyRating: '18', - }, - ], - }; - const newState: ReferenceDataState = { - ...initialReferenceDataState, - [resourceType]: { ...initialReferenceDataState[`${resourceType}`], searchReturn: value.payload, loading: false }, - }; - - const action = fetchReferenceDataByKeySearchSuccess({ resourceType, resourceKey, payload: value.payload }); - const state = referenceDataReducer(initialReferenceDataState, action); - - expect(state).toEqual(newState); - expect(state).not.toBe(newState); - }); - }); - - describe('fetchReferenceDataByKeySearchFailed', () => { - it('should set error state', () => { - const newState = { - ...initialReferenceDataState, - [ReferenceDataResourceType.Tyres]: { - ...initialReferenceDataState[ReferenceDataResourceType.Tyres], - searchReturn: null, - loading: false, - filter: null, - term: null, - }, - }; - const action = fetchReferenceDataByKeySearchFailed({ - error: 'unit testing error message by key', - resourceType: ReferenceDataResourceType.Tyres, - }); - const inputState = { - ...initialReferenceDataState, - [ReferenceDataResourceType.Tyres]: { - ...initialReferenceDataState[ReferenceDataResourceType.Tyres], - loading: true, - }, - }; - const state = referenceDataReducer(inputState, action); - - expect(state).toEqual(newState); - expect(state).not.toBe(newState); - }); - }); - }); - - describe('fetchTyreReferenceDataByKeySearch actions', () => { - it('should set loading to true', () => { - const newState: ReferenceDataState = { - ...initialReferenceDataState, - [ReferenceDataResourceType.Tyres]: { - ...initialReferenceDataState[ReferenceDataResourceType.Tyres], - searchReturn: null, - loading: true, - }, - }; - const action = fetchTyreReferenceDataByKeySearch({ - searchTerm: 'plyrating', - searchFilter: '101', - }); - const state = referenceDataReducer(initialReferenceDataState, action); - - expect(state).toEqual(newState); - expect(state).not.toBe(newState); - }); - - describe('fetchTyreReferenceDataByKeySearchSuccess', () => { - it('should set the the resource data item based on the type and key', () => { - const resourceType = ReferenceDataResourceType.Tyres; - const value = { - payload: [ - { - tyreCode: '123', - resourceType: ReferenceDataResourceType.Tyres, - resourceKey: '123', - code: '123', - loadIndexSingleLoad: '102', - tyreSize: 'size', - dateTimeStamp: 'time', - userId: '1234', - loadIndexTwinLoad: '101', - plyRating: '18', - }, - ], - }; - const newState: ReferenceDataState = { - ...initialReferenceDataState, - [resourceType]: { ...initialReferenceDataState[`${resourceType}`], searchReturn: value.payload, loading: false }, - }; - - const action = fetchTyreReferenceDataByKeySearchSuccess({ resourceType, payload: value.payload }); - const state = referenceDataReducer(initialReferenceDataState, action); - - expect(state).toEqual(newState); - expect(state).not.toBe(newState); - }); - }); - - describe('addSearchInformation', () => { - it('should update state term and filter', () => { - const newState = { - ...initialReferenceDataState, - [ReferenceDataResourceType.Tyres]: { - ...initialReferenceDataState[ReferenceDataResourceType.Tyres], - loading: false, - filter: 'code', - term: '103', - }, - }; - - const filter = 'code'; - const term = '103'; - const action = addSearchInformation({ filter, term }); - const state = referenceDataReducer(initialReferenceDataState, action); - - expect(state.TYRES).toStrictEqual(newState.TYRES); - }); - }); - - describe('removeTyreSearch', () => { - it('should null search return', () => { - const action = removeTyreSearch(); - const state = referenceDataReducer(initialReferenceDataState, action); - - expect(state.TYRES).toStrictEqual({ - ids: [], - entities: {}, - loading: false, - searchReturn: null, - filter: null, - term: null, - }); - }); - }); - - describe('fetchTyreReferenceDataByKeySearchFailed', () => { - it('should set error state', () => { - const newState = { - ...initialReferenceDataState, - [ReferenceDataResourceType.Tyres]: { - ...initialReferenceDataState[ReferenceDataResourceType.Tyres], - searchReturn: null, - loading: false, - filter: null, - term: null, - }, - }; - const action = fetchTyreReferenceDataByKeySearchFailed({ - error: 'unit testing error message by key', - resourceType: ReferenceDataResourceType.Tyres, - }); - const inputState = { - ...initialReferenceDataState, - [ReferenceDataResourceType.Tyres]: { - ...initialReferenceDataState[ReferenceDataResourceType.Tyres], - loading: true, - }, - }; - const state = referenceDataReducer(inputState, action); - - expect(state).toEqual(newState); - expect(state).not.toBe(newState); - }); - }); - }); - describe('deleteReferenceDataItemSuccess', () => { - it('should remove the specified item from the reference data state', () => { - const newItem = { - resourceType: ReferenceDataResourceType.CountryOfRegistration, - resourceKey: 'test', - description: 'test', - }; - const inputState = cloneDeep(initialReferenceDataState); - - inputState[ReferenceDataResourceType.CountryOfRegistration].entities['test'] = newItem; - inputState[ReferenceDataResourceType.CountryOfRegistration].ids = ['test']; - const action = deleteReferenceDataItemSuccess({ - resourceType: ReferenceDataResourceType.CountryOfRegistration, - resourceKey: 'test', - }); - - const reducer = referenceDataReducer(inputState, action); - expect(reducer).toEqual(initialReferenceDataState); - }); - it('should leave any unspecified items in state', () => { - const newItem = { - resourceType: ReferenceDataResourceType.CountryOfRegistration, - resourceKey: 'test', - description: 'test', - }; - const newItem2 = { - resourceType: ReferenceDataResourceType.CountryOfRegistration, - resourceKey: 'test2', - description: 'test2', - }; - const inputState = cloneDeep(initialReferenceDataState); - - inputState[ReferenceDataResourceType.CountryOfRegistration].entities['test'] = newItem; - inputState[ReferenceDataResourceType.CountryOfRegistration].ids = ['test']; - inputState[ReferenceDataResourceType.CountryOfRegistration].entities['test2'] = newItem2; - inputState[ReferenceDataResourceType.CountryOfRegistration].ids = ['test2']; - - const action = deleteReferenceDataItemSuccess({ - resourceType: ReferenceDataResourceType.CountryOfRegistration, - resourceKey: 'test', - }); - - const reducer = referenceDataReducer(inputState, action); - - expect(reducer).not.toEqual(initialReferenceDataState); - expect(reducer[ReferenceDataResourceType.CountryOfRegistration].entities).toEqual({ test2: newItem2 }); - expect(reducer[ReferenceDataResourceType.CountryOfRegistration].ids).toEqual(['test2']); - }); - }); - describe('createReferenceDataItemSuccess', () => { - it('should insert the new item into the reference data state', () => { - const testItem = { - resourceType: ReferenceDataResourceType.CountryOfRegistration, - resourceKey: 'test', - description: 'test', - }; - const testItem2 = { - resourceType: ReferenceDataResourceType.CountryOfRegistration, - resourceKey: 'test2', - description: 'test2', - }; - const inputState = cloneDeep(initialReferenceDataState); - inputState[ReferenceDataResourceType.CountryOfRegistration].entities = { test2: testItem2 }; - inputState[ReferenceDataResourceType.CountryOfRegistration].ids = ['test2']; - - const action = createReferenceDataItemSuccess({ result: testItem }); - const reducer = referenceDataReducer(inputState, action); - expect(reducer[ReferenceDataResourceType.CountryOfRegistration].entities).toEqual({ test: testItem, test2: testItem2 }); - expect(reducer[ReferenceDataResourceType.CountryOfRegistration].ids).toEqual(['test2', 'test']); - }); - }); - describe('amendReferenceDataItemSuccess', () => { - it('should insert the new item into the reference data state', () => { - const itemToAmend = { - resourceType: ReferenceDataResourceType.CountryOfRegistration, - resourceKey: 'test', - description: 'test', - }; - const amendedItem = { - resourceType: ReferenceDataResourceType.CountryOfRegistration, - resourceKey: 'test', - description: 'this has been amended', - }; - const inputState = cloneDeep(initialReferenceDataState); - inputState[ReferenceDataResourceType.CountryOfRegistration].entities = { test: itemToAmend }; - inputState[ReferenceDataResourceType.CountryOfRegistration].ids = ['test']; - - const action = amendReferenceDataItemSuccess({ result: amendedItem }); - const reducer = referenceDataReducer(inputState, action); - - expect(reducer[ReferenceDataResourceType.CountryOfRegistration].entities).toEqual({ test: amendedItem }); - expect(reducer[ReferenceDataResourceType.CountryOfRegistration].ids).toEqual(['test']); - }); - }); + describe('unknown action', () => { + it('should return the default state', () => { + const action = { + type: 'Unknown', + }; + const state = referenceDataReducer(initialReferenceDataState, action); + + expect(state).toBe(initialReferenceDataState); + }); + }); + + describe('fetchReferenceData', () => { + it('should set loading to true', () => { + const newState: ReferenceDataState = { + ...initialReferenceDataState, + [ReferenceDataResourceType.CountryOfRegistration]: { + ...initialReferenceDataState[ReferenceDataResourceType.CountryOfRegistration], + loading: true, + }, + }; + const action = fetchReferenceData({ resourceType: ReferenceDataResourceType.CountryOfRegistration }); + const state = referenceDataReducer(initialReferenceDataState, action); + + expect(state).toEqual(newState); + expect(state).not.toBe(newState); + }); + }); + + describe('fetchReferenceDataSuccess', () => { + it.each(testCases)('should set all reference data on success', (value) => { + const { resourceType, payload } = value; + const ids = payload.map((v) => v.resourceKey); + const entities: Dictionary = payload.reduce( + (acc, v) => ({ ...acc, [v.resourceKey]: v }), + {} as { [V in ReferenceDataModelBase as V['resourceKey']]: V } + ); + const newState: ReferenceDataState = { + ...initialReferenceDataState, + [resourceType]: { ids, entities, loading: false }, + }; + const action = fetchReferenceDataSuccess({ resourceType, payload: [...payload], paginated: false }); + const state = referenceDataReducer(initialReferenceDataState, action); + + expect(state).toEqual(newState); + expect(state).not.toBe(newState); + }); + }); + + describe('fetchReferenceDataFailed', () => { + it('should set error state', () => { + const newState = { ...initialReferenceDataState }; + const action = fetchReferenceDataFailed({ + error: 'unit testing error message', + resourceType: ReferenceDataResourceType.CountryOfRegistration, + }); + const state = referenceDataReducer({ ...initialReferenceDataState }, action); + + expect(state).toEqual(newState); + expect(state).not.toBe(newState); + }); + }); + + describe('fetchReferenceDataAudit', () => { + it('should set loading to true', () => { + const newState: ReferenceDataState = { + ...initialReferenceDataState, + [ReferenceDataResourceType.CountryOfRegistration]: { + ...initialReferenceDataState[ReferenceDataResourceType.CountryOfRegistration], + loading: true, + }, + }; + const action = fetchReferenceDataAudit({ resourceType: ReferenceDataResourceType.CountryOfRegistration }); + const state = referenceDataReducer(initialReferenceDataState, action); + + expect(state).toEqual(newState); + expect(state).not.toBe(newState); + }); + }); + + describe('fetchReferenceDataAuditSuccess', () => { + it.each(testCases)('should set the the resource data item based on the type', (value) => { + const { resourceType } = value; + const newState: ReferenceDataState = { + ...initialReferenceDataState, + [resourceType]: { + ...initialReferenceDataState[`${resourceType}`], + searchReturn: value.payload, + loading: false, + }, + }; + + const action = fetchReferenceDataAuditSuccess({ resourceType, payload: value.payload, paginated: false }); + const state = referenceDataReducer(initialReferenceDataState, action); + + expect(state).toEqual(newState); + expect(state).not.toBe(newState); + }); + }); + + describe('fetchReferenceDataAuditFailed', () => { + it('should set error state', () => { + const newState = { ...initialReferenceDataState }; + const action = fetchReferenceDataAuditFailed({ + error: 'unit testing error message', + resourceType: ReferenceDataResourceType.CountryOfRegistration, + }); + const state = referenceDataReducer({ ...initialReferenceDataState }, action); + + expect(state).toEqual(newState); + expect(state).not.toBe(newState); + }); + }); + + describe('fetchReferenceDataByKey actions', () => { + it('should set loading to true', () => { + const newState: ReferenceDataState = { + ...initialReferenceDataState, + [ReferenceDataResourceType.CountryOfRegistration]: { + ...initialReferenceDataState[ReferenceDataResourceType.CountryOfRegistration], + loading: true, + }, + }; + const action = fetchReferenceDataByKey({ + resourceType: ReferenceDataResourceType.CountryOfRegistration, + resourceKey: mockCountriesOfRegistration[0].resourceKey, + }); + const state = referenceDataReducer(initialReferenceDataState, action); + + expect(state).toEqual(newState); + expect(state).not.toBe(newState); + }); + + describe('fetchReferenceDataByKeySuccess', () => { + it.each(testCases)('should set the the resource data item based on the type and key', (value) => { + const { resourceType, resourceKey, payload } = value; + + const entity = payload.find((p) => p.resourceKey === resourceKey) as ReferenceDataModelBase; + const ids = [resourceKey]; + const entities: Dictionary = { [resourceKey]: entity }; + const newState: ReferenceDataState = { + ...initialReferenceDataState, + [resourceType]: { ids, entities, loading: false }, + }; + + const action = fetchReferenceDataByKeySuccess({ resourceType, resourceKey, payload: entity }); + const state = referenceDataReducer(initialReferenceDataState, action); + + expect(state).toEqual(newState); + expect(state).not.toBe(newState); + }); + }); + + describe('fetchReferenceDataByKeyFailed', () => { + it('should set error state', () => { + const newState = { ...initialReferenceDataState }; + const action = fetchReferenceDataByKeyFailed({ + error: 'unit testing error message by key', + resourceType: ReferenceDataResourceType.CountryOfRegistration, + }); + const inputState = { + ...initialReferenceDataState, + [ReferenceDataResourceType.CountryOfRegistration]: { + ...initialReferenceDataState[ReferenceDataResourceType.CountryOfRegistration], + loading: true, + }, + }; + const state = referenceDataReducer(inputState, action); + + expect(state).toEqual(newState); + expect(state).not.toBe(newState); + }); + }); + }); + + describe('fetchReferenceDataByKeySearch actions', () => { + it('should set loading to true', () => { + const newState: ReferenceDataState = { + ...initialReferenceDataState, + [ReferenceDataResourceType.Tyres]: { + ...initialReferenceDataState[ReferenceDataResourceType.Tyres], + searchReturn: null, + loading: true, + }, + }; + const action = fetchReferenceDataByKeySearch({ + resourceType: ReferenceDataResourceType.Tyres, + resourceKey: '101', + }); + const state = referenceDataReducer(initialReferenceDataState, action); + + expect(state).toEqual(newState); + expect(state).not.toBe(newState); + }); + + describe('fetchReferenceDataByKeySearchSuccess', () => { + it('should set the the resource data item based on the type and key', () => { + const resourceType = ReferenceDataResourceType.Tyres; + const resourceKey = '123'; + const value = { + payload: [ + { + tyreCode: '123', + resourceType: ReferenceDataResourceType.Tyres, + resourceKey: '123', + code: '123', + loadIndexSingleLoad: '102', + tyreSize: 'size', + dateTimeStamp: 'time', + userId: '1234', + loadIndexTwinLoad: '101', + plyRating: '18', + }, + ], + }; + const newState: ReferenceDataState = { + ...initialReferenceDataState, + [resourceType]: { + ...initialReferenceDataState[`${resourceType}`], + searchReturn: value.payload, + loading: false, + }, + }; + + const action = fetchReferenceDataByKeySearchSuccess({ resourceType, resourceKey, payload: value.payload }); + const state = referenceDataReducer(initialReferenceDataState, action); + + expect(state).toEqual(newState); + expect(state).not.toBe(newState); + }); + }); + + describe('fetchReferenceDataByKeySearchFailed', () => { + it('should set error state', () => { + const newState = { + ...initialReferenceDataState, + [ReferenceDataResourceType.Tyres]: { + ...initialReferenceDataState[ReferenceDataResourceType.Tyres], + searchReturn: null, + loading: false, + filter: null, + term: null, + }, + }; + const action = fetchReferenceDataByKeySearchFailed({ + error: 'unit testing error message by key', + resourceType: ReferenceDataResourceType.Tyres, + }); + const inputState = { + ...initialReferenceDataState, + [ReferenceDataResourceType.Tyres]: { + ...initialReferenceDataState[ReferenceDataResourceType.Tyres], + loading: true, + }, + }; + const state = referenceDataReducer(inputState, action); + + expect(state).toEqual(newState); + expect(state).not.toBe(newState); + }); + }); + }); + + describe('fetchTyreReferenceDataByKeySearch actions', () => { + it('should set loading to true', () => { + const newState: ReferenceDataState = { + ...initialReferenceDataState, + [ReferenceDataResourceType.Tyres]: { + ...initialReferenceDataState[ReferenceDataResourceType.Tyres], + searchReturn: null, + loading: true, + }, + }; + const action = fetchTyreReferenceDataByKeySearch({ + searchTerm: 'plyrating', + searchFilter: '101', + }); + const state = referenceDataReducer(initialReferenceDataState, action); + + expect(state).toEqual(newState); + expect(state).not.toBe(newState); + }); + + describe('fetchTyreReferenceDataByKeySearchSuccess', () => { + it('should set the the resource data item based on the type and key', () => { + const resourceType = ReferenceDataResourceType.Tyres; + const value = { + payload: [ + { + tyreCode: '123', + resourceType: ReferenceDataResourceType.Tyres, + resourceKey: '123', + code: '123', + loadIndexSingleLoad: '102', + tyreSize: 'size', + dateTimeStamp: 'time', + userId: '1234', + loadIndexTwinLoad: '101', + plyRating: '18', + }, + ], + }; + const newState: ReferenceDataState = { + ...initialReferenceDataState, + [resourceType]: { + ...initialReferenceDataState[`${resourceType}`], + searchReturn: value.payload, + loading: false, + }, + }; + + const action = fetchTyreReferenceDataByKeySearchSuccess({ resourceType, payload: value.payload }); + const state = referenceDataReducer(initialReferenceDataState, action); + + expect(state).toEqual(newState); + expect(state).not.toBe(newState); + }); + }); + + describe('addSearchInformation', () => { + it('should update state term and filter', () => { + const newState = { + ...initialReferenceDataState, + [ReferenceDataResourceType.Tyres]: { + ...initialReferenceDataState[ReferenceDataResourceType.Tyres], + loading: false, + filter: 'code', + term: '103', + }, + }; + + const filter = 'code'; + const term = '103'; + const action = addSearchInformation({ filter, term }); + const state = referenceDataReducer(initialReferenceDataState, action); + + expect(state.TYRES).toStrictEqual(newState.TYRES); + }); + }); + + describe('removeTyreSearch', () => { + it('should null search return', () => { + const action = removeTyreSearch(); + const state = referenceDataReducer(initialReferenceDataState, action); + + expect(state.TYRES).toStrictEqual({ + ids: [], + entities: {}, + loading: false, + searchReturn: null, + filter: null, + term: null, + }); + }); + }); + + describe('fetchTyreReferenceDataByKeySearchFailed', () => { + it('should set error state', () => { + const newState = { + ...initialReferenceDataState, + [ReferenceDataResourceType.Tyres]: { + ...initialReferenceDataState[ReferenceDataResourceType.Tyres], + searchReturn: null, + loading: false, + filter: null, + term: null, + }, + }; + const action = fetchTyreReferenceDataByKeySearchFailed({ + error: 'unit testing error message by key', + resourceType: ReferenceDataResourceType.Tyres, + }); + const inputState = { + ...initialReferenceDataState, + [ReferenceDataResourceType.Tyres]: { + ...initialReferenceDataState[ReferenceDataResourceType.Tyres], + loading: true, + }, + }; + const state = referenceDataReducer(inputState, action); + + expect(state).toEqual(newState); + expect(state).not.toBe(newState); + }); + }); + }); + describe('deleteReferenceDataItemSuccess', () => { + it('should remove the specified item from the reference data state', () => { + const newItem = { + resourceType: ReferenceDataResourceType.CountryOfRegistration, + resourceKey: 'test', + description: 'test', + }; + const inputState = cloneDeep(initialReferenceDataState); + + inputState[ReferenceDataResourceType.CountryOfRegistration].entities['test'] = newItem; + inputState[ReferenceDataResourceType.CountryOfRegistration].ids = ['test']; + const action = deleteReferenceDataItemSuccess({ + resourceType: ReferenceDataResourceType.CountryOfRegistration, + resourceKey: 'test', + }); + + const reducer = referenceDataReducer(inputState, action); + expect(reducer).toEqual(initialReferenceDataState); + }); + it('should leave any unspecified items in state', () => { + const newItem = { + resourceType: ReferenceDataResourceType.CountryOfRegistration, + resourceKey: 'test', + description: 'test', + }; + const newItem2 = { + resourceType: ReferenceDataResourceType.CountryOfRegistration, + resourceKey: 'test2', + description: 'test2', + }; + const inputState = cloneDeep(initialReferenceDataState); + + inputState[ReferenceDataResourceType.CountryOfRegistration].entities['test'] = newItem; + inputState[ReferenceDataResourceType.CountryOfRegistration].ids = ['test']; + inputState[ReferenceDataResourceType.CountryOfRegistration].entities['test2'] = newItem2; + inputState[ReferenceDataResourceType.CountryOfRegistration].ids = ['test2']; + + const action = deleteReferenceDataItemSuccess({ + resourceType: ReferenceDataResourceType.CountryOfRegistration, + resourceKey: 'test', + }); + + const reducer = referenceDataReducer(inputState, action); + + expect(reducer).not.toEqual(initialReferenceDataState); + expect(reducer[ReferenceDataResourceType.CountryOfRegistration].entities).toEqual({ test2: newItem2 }); + expect(reducer[ReferenceDataResourceType.CountryOfRegistration].ids).toEqual(['test2']); + }); + }); + describe('createReferenceDataItemSuccess', () => { + it('should insert the new item into the reference data state', () => { + const testItem = { + resourceType: ReferenceDataResourceType.CountryOfRegistration, + resourceKey: 'test', + description: 'test', + }; + const testItem2 = { + resourceType: ReferenceDataResourceType.CountryOfRegistration, + resourceKey: 'test2', + description: 'test2', + }; + const inputState = cloneDeep(initialReferenceDataState); + inputState[ReferenceDataResourceType.CountryOfRegistration].entities = { test2: testItem2 }; + inputState[ReferenceDataResourceType.CountryOfRegistration].ids = ['test2']; + + const action = createReferenceDataItemSuccess({ result: testItem }); + const reducer = referenceDataReducer(inputState, action); + expect(reducer[ReferenceDataResourceType.CountryOfRegistration].entities).toEqual({ + test: testItem, + test2: testItem2, + }); + expect(reducer[ReferenceDataResourceType.CountryOfRegistration].ids).toEqual(['test2', 'test']); + }); + }); + describe('amendReferenceDataItemSuccess', () => { + it('should insert the new item into the reference data state', () => { + const itemToAmend = { + resourceType: ReferenceDataResourceType.CountryOfRegistration, + resourceKey: 'test', + description: 'test', + }; + const amendedItem = { + resourceType: ReferenceDataResourceType.CountryOfRegistration, + resourceKey: 'test', + description: 'this has been amended', + }; + const inputState = cloneDeep(initialReferenceDataState); + inputState[ReferenceDataResourceType.CountryOfRegistration].entities = { test: itemToAmend }; + inputState[ReferenceDataResourceType.CountryOfRegistration].ids = ['test']; + + const action = amendReferenceDataItemSuccess({ result: amendedItem }); + const reducer = referenceDataReducer(inputState, action); + + expect(reducer[ReferenceDataResourceType.CountryOfRegistration].entities).toEqual({ test: amendedItem }); + expect(reducer[ReferenceDataResourceType.CountryOfRegistration].ids).toEqual(['test']); + }); + }); }); diff --git a/src/app/store/reference-data/reducers/reference-data.reducer.ts b/src/app/store/reference-data/reducers/reference-data.reducer.ts index eddbdb819a..f52e8dae0d 100644 --- a/src/app/store/reference-data/reducers/reference-data.reducer.ts +++ b/src/app/store/reference-data/reducers/reference-data.reducer.ts @@ -1,195 +1,253 @@ import { ReferenceDataModelBase, ReferenceDataResourceType } from '@models/reference-data.model'; -import { createEntityAdapter, EntityAdapter, EntityState } from '@ngrx/entity'; +import { EntityAdapter, EntityState, createEntityAdapter } from '@ngrx/entity'; import { createFeatureSelector, createReducer, on } from '@ngrx/store'; import cloneDeep from 'lodash.clonedeep'; import { - addSearchInformation, - amendReferenceDataItemSuccess, - createReferenceDataItemSuccess, - deleteReferenceDataItemSuccess, - fetchReferenceData, - fetchReferenceDataAudit, - fetchReferenceDataAuditFailed, - fetchReferenceDataAuditSuccess, - fetchReferenceDataByKey, - fetchReferenceDataByKeyFailed, - fetchReferenceDataByKeySearch, - fetchReferenceDataByKeySearchFailed, - fetchReferenceDataByKeySearchSuccess, - fetchReferenceDataByKeySuccess, - fetchReferenceDataFailed, - fetchReferenceDataSuccess, - fetchTyreReferenceDataByKeySearch, - fetchTyreReferenceDataByKeySearchFailed, - fetchTyreReferenceDataByKeySearchSuccess, - removeTyreSearch, + addSearchInformation, + amendReferenceDataItemSuccess, + createReferenceDataItemSuccess, + deleteReferenceDataItemSuccess, + fetchReferenceData, + fetchReferenceDataAudit, + fetchReferenceDataAuditFailed, + fetchReferenceDataAuditSuccess, + fetchReferenceDataByKey, + fetchReferenceDataByKeyFailed, + fetchReferenceDataByKeySearch, + fetchReferenceDataByKeySearchFailed, + fetchReferenceDataByKeySearchSuccess, + fetchReferenceDataByKeySuccess, + fetchReferenceDataFailed, + fetchReferenceDataSuccess, + fetchTyreReferenceDataByKeySearch, + fetchTyreReferenceDataByKeySearchFailed, + fetchTyreReferenceDataByKeySearchSuccess, + removeTyreSearch, } from '../actions/reference-data.actions'; export const STORE_FEATURE_REFERENCE_DATA_KEY = 'referenceData'; const selectResourceKey = (a: ReferenceDataModelBase): string | number => { - return a.resourceKey; + return a.resourceKey; }; interface ReferenceDataEntityState extends EntityState { - loading: boolean; + loading: boolean; } export interface ReferenceDataEntityStateSearch extends EntityState { - loading: boolean; - searchReturn: ReferenceDataModelBase[] | null; - term: string | null; - filter: string | null; + loading: boolean; + searchReturn: ReferenceDataModelBase[] | null; + term: string | null; + filter: string | null; } -export type ReferenceDataState = Record; +export type ReferenceDataState = Record< + ReferenceDataResourceType, + ReferenceDataEntityState | ReferenceDataEntityStateSearch +>; function createAdapter() { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - return createEntityAdapter({ selectId: selectResourceKey as any }); + // eslint-disable-next-line @typescript-eslint/no-explicit-any + return createEntityAdapter({ selectId: selectResourceKey as any }); } function getInitialState(resourceType: ReferenceDataResourceType) { - return resourceTypeAdapters[`${resourceType}`].getInitialState({ loading: false }); + return resourceTypeAdapters[`${resourceType}`].getInitialState({ loading: false }); } export const resourceTypeAdapters: Record> = { - [ReferenceDataResourceType.Brakes]: createAdapter(), - [ReferenceDataResourceType.CountryOfRegistration]: createAdapter(), - [ReferenceDataResourceType.HgvMake]: createAdapter(), - [ReferenceDataResourceType.PsvMake]: createAdapter(), - [ReferenceDataResourceType.ReasonsForAbandoningHgv]: createAdapter(), - [ReferenceDataResourceType.ReasonsForAbandoningPsv]: createAdapter(), - [ReferenceDataResourceType.ReasonsForAbandoningTrl]: createAdapter(), - [ReferenceDataResourceType.ReferenceDataAdminType]: createAdapter(), - [ReferenceDataResourceType.SpecialistReasonsForAbandoning]: createAdapter(), - [ReferenceDataResourceType.TirReasonsForAbandoning]: createAdapter(), - [ReferenceDataResourceType.TrlMake]: createAdapter(), - [ReferenceDataResourceType.Tyres]: createAdapter(), - [ReferenceDataResourceType.User]: createAdapter(), - [ReferenceDataResourceType.TyreLoadIndex]: createAdapter(), + [ReferenceDataResourceType.Brakes]: createAdapter(), + [ReferenceDataResourceType.CountryOfRegistration]: createAdapter(), + [ReferenceDataResourceType.HgvMake]: createAdapter(), + [ReferenceDataResourceType.PsvMake]: createAdapter(), + [ReferenceDataResourceType.ReasonsForAbandoningHgv]: createAdapter(), + [ReferenceDataResourceType.ReasonsForAbandoningPsv]: createAdapter(), + [ReferenceDataResourceType.ReasonsForAbandoningTrl]: createAdapter(), + [ReferenceDataResourceType.ReferenceDataAdminType]: createAdapter(), + [ReferenceDataResourceType.SpecialistReasonsForAbandoning]: createAdapter(), + [ReferenceDataResourceType.TirReasonsForAbandoning]: createAdapter(), + [ReferenceDataResourceType.TrlMake]: createAdapter(), + [ReferenceDataResourceType.Tyres]: createAdapter(), + [ReferenceDataResourceType.User]: createAdapter(), + [ReferenceDataResourceType.TyreLoadIndex]: createAdapter(), }; // IMPORTANT: Ensure the keys in initialReferenceDataState call get the initial state from the matching resourceType export const initialReferenceDataState: ReferenceDataState = { - [ReferenceDataResourceType.Brakes]: getInitialState(ReferenceDataResourceType.Brakes), - [ReferenceDataResourceType.CountryOfRegistration]: getInitialState(ReferenceDataResourceType.CountryOfRegistration), - [ReferenceDataResourceType.HgvMake]: getInitialState(ReferenceDataResourceType.HgvMake), - [ReferenceDataResourceType.PsvMake]: getInitialState(ReferenceDataResourceType.PsvMake), - [ReferenceDataResourceType.ReasonsForAbandoningTrl]: getInitialState(ReferenceDataResourceType.ReasonsForAbandoningTrl), - [ReferenceDataResourceType.ReasonsForAbandoningHgv]: getInitialState(ReferenceDataResourceType.ReasonsForAbandoningHgv), - [ReferenceDataResourceType.ReasonsForAbandoningPsv]: getInitialState(ReferenceDataResourceType.ReasonsForAbandoningPsv), - [ReferenceDataResourceType.ReferenceDataAdminType]: getInitialState(ReferenceDataResourceType.ReferenceDataAdminType), - [ReferenceDataResourceType.SpecialistReasonsForAbandoning]: getInitialState(ReferenceDataResourceType.SpecialistReasonsForAbandoning), - [ReferenceDataResourceType.TirReasonsForAbandoning]: getInitialState(ReferenceDataResourceType.TirReasonsForAbandoning), - [ReferenceDataResourceType.TrlMake]: getInitialState(ReferenceDataResourceType.TrlMake), - [ReferenceDataResourceType.Tyres]: getInitialState(ReferenceDataResourceType.Tyres), - [ReferenceDataResourceType.User]: getInitialState(ReferenceDataResourceType.User), - [ReferenceDataResourceType.TyreLoadIndex]: getInitialState(ReferenceDataResourceType.TyreLoadIndex), + [ReferenceDataResourceType.Brakes]: getInitialState(ReferenceDataResourceType.Brakes), + [ReferenceDataResourceType.CountryOfRegistration]: getInitialState(ReferenceDataResourceType.CountryOfRegistration), + [ReferenceDataResourceType.HgvMake]: getInitialState(ReferenceDataResourceType.HgvMake), + [ReferenceDataResourceType.PsvMake]: getInitialState(ReferenceDataResourceType.PsvMake), + [ReferenceDataResourceType.ReasonsForAbandoningTrl]: getInitialState( + ReferenceDataResourceType.ReasonsForAbandoningTrl + ), + [ReferenceDataResourceType.ReasonsForAbandoningHgv]: getInitialState( + ReferenceDataResourceType.ReasonsForAbandoningHgv + ), + [ReferenceDataResourceType.ReasonsForAbandoningPsv]: getInitialState( + ReferenceDataResourceType.ReasonsForAbandoningPsv + ), + [ReferenceDataResourceType.ReferenceDataAdminType]: getInitialState(ReferenceDataResourceType.ReferenceDataAdminType), + [ReferenceDataResourceType.SpecialistReasonsForAbandoning]: getInitialState( + ReferenceDataResourceType.SpecialistReasonsForAbandoning + ), + [ReferenceDataResourceType.TirReasonsForAbandoning]: getInitialState( + ReferenceDataResourceType.TirReasonsForAbandoning + ), + [ReferenceDataResourceType.TrlMake]: getInitialState(ReferenceDataResourceType.TrlMake), + [ReferenceDataResourceType.Tyres]: getInitialState(ReferenceDataResourceType.Tyres), + [ReferenceDataResourceType.User]: getInitialState(ReferenceDataResourceType.User), + [ReferenceDataResourceType.TyreLoadIndex]: getInitialState(ReferenceDataResourceType.TyreLoadIndex), }; export const referenceDataReducer = createReducer( - initialReferenceDataState, - on(fetchReferenceData, (state, action) => ({ ...state, [action.resourceType]: { ...state[action.resourceType], loading: true } })), - on(fetchReferenceDataSuccess, (state, action) => { - const { resourceType, payload, paginated } = action; - return { - ...state, - [resourceType]: { ...resourceTypeAdapters[`${resourceType}`]?.upsertMany(payload, state[`${resourceType}`]), loading: paginated }, - }; - }), - on(fetchReferenceDataFailed, (state, action) => ({ ...state, [action.resourceType]: { ...state[action.resourceType], loading: false } })), - on(fetchReferenceDataAudit, (state, action) => ({ ...state, [action.resourceType]: { ...state[action.resourceType], loading: true } })), - on(fetchReferenceDataAuditSuccess, (state, action) => { - const { resourceType, payload, paginated } = action; - return { - ...state, - [resourceType]: { ...state[action.resourceType], searchReturn: payload, loading: paginated }, - }; - }), - on(fetchReferenceDataAuditFailed, (state, action) => ({ ...state, [action.resourceType]: { ...state[action.resourceType], loading: false } })), - on(fetchReferenceDataByKey, (state, action) => ({ ...state, [action.resourceType]: { ...state[action.resourceType], loading: true } })), - on(fetchReferenceDataByKeySuccess, (state, action) => { - const { resourceType, payload } = action; - return { - ...state, - [resourceType]: { ...resourceTypeAdapters[`${resourceType}`].upsertOne(payload, state[`${resourceType}`]), loading: false }, - }; - }), - on(fetchReferenceDataByKeyFailed, (state, action) => ({ ...state, [action.resourceType]: { ...state[action.resourceType], loading: false } })), - on(fetchReferenceDataByKeySearch, (state, action) => ({ - ...state, - [action.resourceType]: { ...state[action.resourceType], searchReturn: null, loading: true }, - })), - on(fetchReferenceDataByKeySearchSuccess, (state, action) => { - const { resourceType, payload } = action; - return { - ...state, - [resourceType]: { ...state[action.resourceType], searchReturn: payload, loading: false }, - }; - }), - on(fetchReferenceDataByKeySearchFailed, (state, action) => ({ - ...state, - [action.resourceType]: { - ...state[action.resourceType], searchReturn: null, loading: false, filter: null, term: null, - }, - })), - on(fetchTyreReferenceDataByKeySearch, (state) => ({ - ...state, - [ReferenceDataResourceType.Tyres]: { ...state[ReferenceDataResourceType.Tyres], searchReturn: null, loading: true }, - })), - on(fetchTyreReferenceDataByKeySearchSuccess, (state, action) => { - const { resourceType, payload } = action; - return { - ...state, - [resourceType]: { ...state[`${resourceType}`], searchReturn: payload, loading: false }, - }; - }), - on(fetchTyreReferenceDataByKeySearchFailed, (state, action) => ({ - ...state, - [action.resourceType]: { - ...state[action.resourceType], searchReturn: null, loading: false, filter: null, term: null, - }, - })), - on(removeTyreSearch, (state) => ({ - ...state, - [ReferenceDataResourceType.Tyres]: { - ...state[ReferenceDataResourceType.Tyres], searchReturn: null, filter: null, term: null, - }, - })), - on(deleteReferenceDataItemSuccess, (state, action) => { - const { resourceType, resourceKey } = action; - const currentState = cloneDeep(state); - - currentState[`${resourceType}`] = resourceTypeAdapters[`${resourceType}`].removeOne(resourceKey, currentState[`${resourceType}`]); - - return currentState; - }), - on(amendReferenceDataItemSuccess, (state, action) => { - const { result } = action; - const { resourceKey, resourceType } = result; - const currentState = cloneDeep(state); - - currentState[`${resourceType}`] = resourceTypeAdapters[`${resourceType}`].updateOne( - { id: resourceKey.toString(), changes: result }, - currentState[`${resourceType}`], - ); - - return currentState; - }), - on(createReferenceDataItemSuccess, (state, action) => { - const { result } = action; - const { resourceType } = result; - const currentState = cloneDeep(state); - - currentState[`${resourceType}`] = resourceTypeAdapters[`${resourceType}`].addOne(result, currentState[`${resourceType}`]); - - return currentState; - }), - on(addSearchInformation, (state, action) => ({ - ...state, - [ReferenceDataResourceType.Tyres]: { ...state[ReferenceDataResourceType.Tyres], filter: action.filter, term: action.term }, - })), + initialReferenceDataState, + on(fetchReferenceData, (state, action) => ({ + ...state, + [action.resourceType]: { ...state[action.resourceType], loading: true }, + })), + on(fetchReferenceDataSuccess, (state, action) => { + const { resourceType, payload, paginated } = action; + return { + ...state, + [resourceType]: { + ...resourceTypeAdapters[`${resourceType}`]?.upsertMany(payload, state[`${resourceType}`]), + loading: paginated, + }, + }; + }), + on(fetchReferenceDataFailed, (state, action) => ({ + ...state, + [action.resourceType]: { ...state[action.resourceType], loading: false }, + })), + on(fetchReferenceDataAudit, (state, action) => ({ + ...state, + [action.resourceType]: { ...state[action.resourceType], loading: true }, + })), + on(fetchReferenceDataAuditSuccess, (state, action) => { + const { resourceType, payload, paginated } = action; + return { + ...state, + [resourceType]: { ...state[action.resourceType], searchReturn: payload, loading: paginated }, + }; + }), + on(fetchReferenceDataAuditFailed, (state, action) => ({ + ...state, + [action.resourceType]: { ...state[action.resourceType], loading: false }, + })), + on(fetchReferenceDataByKey, (state, action) => ({ + ...state, + [action.resourceType]: { ...state[action.resourceType], loading: true }, + })), + on(fetchReferenceDataByKeySuccess, (state, action) => { + const { resourceType, payload } = action; + return { + ...state, + [resourceType]: { + ...resourceTypeAdapters[`${resourceType}`].upsertOne(payload, state[`${resourceType}`]), + loading: false, + }, + }; + }), + on(fetchReferenceDataByKeyFailed, (state, action) => ({ + ...state, + [action.resourceType]: { ...state[action.resourceType], loading: false }, + })), + on(fetchReferenceDataByKeySearch, (state, action) => ({ + ...state, + [action.resourceType]: { ...state[action.resourceType], searchReturn: null, loading: true }, + })), + on(fetchReferenceDataByKeySearchSuccess, (state, action) => { + const { resourceType, payload } = action; + return { + ...state, + [resourceType]: { ...state[action.resourceType], searchReturn: payload, loading: false }, + }; + }), + on(fetchReferenceDataByKeySearchFailed, (state, action) => ({ + ...state, + [action.resourceType]: { + ...state[action.resourceType], + searchReturn: null, + loading: false, + filter: null, + term: null, + }, + })), + on(fetchTyreReferenceDataByKeySearch, (state) => ({ + ...state, + [ReferenceDataResourceType.Tyres]: { ...state[ReferenceDataResourceType.Tyres], searchReturn: null, loading: true }, + })), + on(fetchTyreReferenceDataByKeySearchSuccess, (state, action) => { + const { resourceType, payload } = action; + return { + ...state, + [resourceType]: { ...state[`${resourceType}`], searchReturn: payload, loading: false }, + }; + }), + on(fetchTyreReferenceDataByKeySearchFailed, (state, action) => ({ + ...state, + [action.resourceType]: { + ...state[action.resourceType], + searchReturn: null, + loading: false, + filter: null, + term: null, + }, + })), + on(removeTyreSearch, (state) => ({ + ...state, + [ReferenceDataResourceType.Tyres]: { + ...state[ReferenceDataResourceType.Tyres], + searchReturn: null, + filter: null, + term: null, + }, + })), + on(deleteReferenceDataItemSuccess, (state, action) => { + const { resourceType, resourceKey } = action; + const currentState = cloneDeep(state); + + currentState[`${resourceType}`] = resourceTypeAdapters[`${resourceType}`].removeOne( + resourceKey, + currentState[`${resourceType}`] + ); + + return currentState; + }), + on(amendReferenceDataItemSuccess, (state, action) => { + const { result } = action; + const { resourceKey, resourceType } = result; + const currentState = cloneDeep(state); + + currentState[`${resourceType}`] = resourceTypeAdapters[`${resourceType}`].updateOne( + { id: resourceKey.toString(), changes: result }, + currentState[`${resourceType}`] + ); + + return currentState; + }), + on(createReferenceDataItemSuccess, (state, action) => { + const { result } = action; + const { resourceType } = result; + const currentState = cloneDeep(state); + + currentState[`${resourceType}`] = resourceTypeAdapters[`${resourceType}`].addOne( + result, + currentState[`${resourceType}`] + ); + + return currentState; + }), + on(addSearchInformation, (state, action) => ({ + ...state, + [ReferenceDataResourceType.Tyres]: { + ...state[ReferenceDataResourceType.Tyres], + filter: action.filter, + term: action.term, + }, + })) ); export const referenceDataFeatureState = createFeatureSelector(STORE_FEATURE_REFERENCE_DATA_KEY); diff --git a/src/app/store/reference-data/reference-data.module.ts b/src/app/store/reference-data/reference-data.module.ts index 37e336fe46..6b6de199cd 100644 --- a/src/app/store/reference-data/reference-data.module.ts +++ b/src/app/store/reference-data/reference-data.module.ts @@ -6,11 +6,11 @@ import { ReferenceDataEffects } from './effects/reference-data.effects'; import { STORE_FEATURE_REFERENCE_DATA_KEY, referenceDataReducer } from './reducers/reference-data.reducer'; @NgModule({ - declarations: [], - imports: [ - CommonModule, - StoreModule.forFeature(STORE_FEATURE_REFERENCE_DATA_KEY, referenceDataReducer), - EffectsModule.forFeature([ReferenceDataEffects]), - ], + declarations: [], + imports: [ + CommonModule, + StoreModule.forFeature(STORE_FEATURE_REFERENCE_DATA_KEY, referenceDataReducer), + EffectsModule.forFeature([ReferenceDataEffects]), + ], }) export class ReferenceDataStateModule {} diff --git a/src/app/store/reference-data/reference-data.test-cases.ts b/src/app/store/reference-data/reference-data.test-cases.ts index 118e231c94..2a7319932c 100644 --- a/src/app/store/reference-data/reference-data.test-cases.ts +++ b/src/app/store/reference-data/reference-data.test-cases.ts @@ -2,9 +2,9 @@ import { mockCountriesOfRegistration } from '@mocks/reference-data/mock-countrie import { ReferenceDataResourceType } from '@models/reference-data.model'; export const testCases = [ - { - resourceType: ReferenceDataResourceType.CountryOfRegistration, - resourceKey: mockCountriesOfRegistration[0].resourceKey, - payload: mockCountriesOfRegistration, - }, + { + resourceType: ReferenceDataResourceType.CountryOfRegistration, + resourceKey: mockCountriesOfRegistration[0].resourceKey, + payload: mockCountriesOfRegistration, + }, ]; diff --git a/src/app/store/reference-data/selectors/reference-data.selectors.spec.ts b/src/app/store/reference-data/selectors/reference-data.selectors.spec.ts index c672a6be45..73975c54b1 100644 --- a/src/app/store/reference-data/selectors/reference-data.selectors.spec.ts +++ b/src/app/store/reference-data/selectors/reference-data.selectors.spec.ts @@ -7,166 +7,199 @@ import { testCases } from '../reference-data.test-cases'; import * as referenceDataSelectors from './reference-data.selectors'; describe('Reference Data Selectors', () => { - describe('selectAllReferenceDataByResourceType', () => { - it.each(testCases)('should return all of the reference data for given resource type', (value) => { - const { resourceType, payload } = value; - const ids = payload.map((v) => v.resourceKey); - const entities: Dictionary = payload.reduce( - (acc, v) => ({ ...acc, [v.resourceKey]: v }), - {} as { [V in ReferenceDataModelBase as V['resourceKey']]: V }, - ); - const state: ReferenceDataState = { ...initialReferenceDataState, [resourceType]: { ids, entities } }; + describe('selectAllReferenceDataByResourceType', () => { + it.each(testCases)('should return all of the reference data for given resource type', (value) => { + const { resourceType, payload } = value; + const ids = payload.map((v) => v.resourceKey); + const entities: Dictionary = payload.reduce( + (acc, v) => ({ ...acc, [v.resourceKey]: v }), + {} as { [V in ReferenceDataModelBase as V['resourceKey']]: V } + ); + const state: ReferenceDataState = { ...initialReferenceDataState, [resourceType]: { ids, entities } }; - const expectedState = referenceDataSelectors.selectAllReferenceDataByResourceType(resourceType).projector(state[`${resourceType}`]); - expect(expectedState).toHaveLength(mockCountriesOfRegistration.length); - expect(expectedState).toEqual(payload); - }); - }); + const expectedState = referenceDataSelectors + .selectAllReferenceDataByResourceType(resourceType) + .projector(state[`${resourceType}`]); + expect(expectedState).toHaveLength(mockCountriesOfRegistration.length); + expect(expectedState).toEqual(payload); + }); + }); - describe('selectReferenceDataByResourceKey', () => { - it.each(testCases)('should return one specific reference data by type and key', (value) => { - const { resourceType, payload } = value; - const ids: string[] = payload.map((v) => v.resourceKey as string); - const entities: Dictionary = payload.reduce( - (acc, v) => ({ ...acc, [v.resourceKey]: v }), - {} as { [V in ReferenceDataModelBase as V['resourceKey']]: V }, - ); - const state: ReferenceDataState = { ...initialReferenceDataState, COUNTRY_OF_REGISTRATION: { ids, entities, loading: false } }; + describe('selectReferenceDataByResourceKey', () => { + it.each(testCases)('should return one specific reference data by type and key', (value) => { + const { resourceType, payload } = value; + const ids: string[] = payload.map((v) => v.resourceKey as string); + const entities: Dictionary = payload.reduce( + (acc, v) => ({ ...acc, [v.resourceKey]: v }), + {} as { [V in ReferenceDataModelBase as V['resourceKey']]: V } + ); + const state: ReferenceDataState = { + ...initialReferenceDataState, + COUNTRY_OF_REGISTRATION: { ids, entities, loading: false }, + }; - const key = ids[Math.floor(Math.random() * ids.length)]; // select a random key + const key = ids[Math.floor(Math.random() * ids.length)]; // select a random key - const expectedState = referenceDataSelectors.selectReferenceDataByResourceKey(resourceType, key).projector(state); - expect(expectedState).toBe(mockCountriesOfRegistration.find((r) => r.resourceKey === key)); - }); - }); + const expectedState = referenceDataSelectors.selectReferenceDataByResourceKey(resourceType, key).projector(state); + expect(expectedState).toBe(mockCountriesOfRegistration.find((r) => r.resourceKey === key)); + }); + }); - describe('selectTyreSearchReturn', () => { - it('should return the search return state to the user', () => { - const value = { - payload: [ - { - tyreCode: '123', - resourceType: ReferenceDataResourceType.Tyres, - resourceKey: '123', - code: '123', - loadIndexSingleLoad: '102', - tyreSize: 'size', - dateTimeStamp: 'time', - userId: '1234', - loadIndexTwinLoad: '101', - plyRating: '18', - }, - ], - }; + describe('selectTyreSearchReturn', () => { + it('should return the search return state to the user', () => { + const value = { + payload: [ + { + tyreCode: '123', + resourceType: ReferenceDataResourceType.Tyres, + resourceKey: '123', + code: '123', + loadIndexSingleLoad: '102', + tyreSize: 'size', + dateTimeStamp: 'time', + userId: '1234', + loadIndexTwinLoad: '101', + plyRating: '18', + }, + ], + }; - const state: ReferenceDataState = { - ...initialReferenceDataState, - [ReferenceDataResourceType.Tyres]: { - ...initialReferenceDataState[ReferenceDataResourceType.Tyres], - loading: false, - searchReturn: value.payload, - }, - }; + const state: ReferenceDataState = { + ...initialReferenceDataState, + [ReferenceDataResourceType.Tyres]: { + ...initialReferenceDataState[ReferenceDataResourceType.Tyres], + loading: false, + searchReturn: value.payload, + }, + }; - const expectedState = referenceDataSelectors.selectSearchReturn(ReferenceDataResourceType.Tyres).projector(state); - expect(expectedState).toBe(value.payload); - }); - }); - describe('selectTyreSearchCriteria', () => { - it('should return the filter and term state to the user', () => { - const value = { - payload: [ - { - tyreCode: '123', - resourceType: ReferenceDataResourceType.Tyres, - resourceKey: '123', - code: '123', - loadIndexSingleLoad: '102', - tyreSize: 'size', - dateTimeStamp: 'time', - userId: '1234', - loadIndexTwinLoad: '101', - plyRating: '18', - }, - ], - }; + const expectedState = referenceDataSelectors.selectSearchReturn(ReferenceDataResourceType.Tyres).projector(state); + expect(expectedState).toBe(value.payload); + }); + }); + describe('selectTyreSearchCriteria', () => { + it('should return the filter and term state to the user', () => { + const value = { + payload: [ + { + tyreCode: '123', + resourceType: ReferenceDataResourceType.Tyres, + resourceKey: '123', + code: '123', + loadIndexSingleLoad: '102', + tyreSize: 'size', + dateTimeStamp: 'time', + userId: '1234', + loadIndexTwinLoad: '101', + plyRating: '18', + }, + ], + }; - const state: ReferenceDataState = { - ...initialReferenceDataState, - [ReferenceDataResourceType.Tyres]: { - ...initialReferenceDataState[ReferenceDataResourceType.Tyres], - loading: false, - searchReturn: value.payload, - filter: 'cake', - term: 'lies', - }, - }; + const state: ReferenceDataState = { + ...initialReferenceDataState, + [ReferenceDataResourceType.Tyres]: { + ...initialReferenceDataState[ReferenceDataResourceType.Tyres], + loading: false, + searchReturn: value.payload, + filter: 'cake', + term: 'lies', + }, + }; - const expectedState = referenceDataSelectors.selectTyreSearchCriteria.projector(state); - expect(expectedState.filter).toBe('cake'); - expect(expectedState.term).toBe('lies'); - }); - }); + const expectedState = referenceDataSelectors.selectTyreSearchCriteria.projector(state); + expect(expectedState.filter).toBe('cake'); + expect(expectedState.term).toBe('lies'); + }); + }); - it('should return true if any feature is loading state', () => { - const state: ReferenceDataState = { ...initialReferenceDataState }; - state.HGV_MAKE.loading = true; - const selectedState = referenceDataSelectors.referenceDataLoadingState.projector(state); - expect(selectedState).toBe(true); - }); + it('should return true if any feature is loading state', () => { + const state: ReferenceDataState = { ...initialReferenceDataState }; + state.HGV_MAKE.loading = true; + const selectedState = referenceDataSelectors.referenceDataLoadingState.projector(state); + expect(selectedState).toBe(true); + }); - it('should return the reasons for abandoning for the right vehicle type', () => { - const selectorSpy = jest.spyOn(referenceDataSelectors, 'selectAllReferenceDataByResourceType'); - referenceDataSelectors.selectReasonsForAbandoning(VehicleTypes.PSV); - expect(selectorSpy).toHaveBeenLastCalledWith(ReferenceDataResourceType.ReasonsForAbandoningPsv); - referenceDataSelectors.selectReasonsForAbandoning(VehicleTypes.HGV); - expect(selectorSpy).toHaveBeenLastCalledWith(ReferenceDataResourceType.ReasonsForAbandoningHgv); - referenceDataSelectors.selectReasonsForAbandoning(VehicleTypes.TRL); - expect(selectorSpy).toHaveBeenLastCalledWith(ReferenceDataResourceType.ReasonsForAbandoningTrl); - }); + it('should return the reasons for abandoning for the right vehicle type', () => { + const selectorSpy = jest.spyOn(referenceDataSelectors, 'selectAllReferenceDataByResourceType'); + referenceDataSelectors.selectReasonsForAbandoning(VehicleTypes.PSV); + expect(selectorSpy).toHaveBeenLastCalledWith(ReferenceDataResourceType.ReasonsForAbandoningPsv); + referenceDataSelectors.selectReasonsForAbandoning(VehicleTypes.HGV); + expect(selectorSpy).toHaveBeenLastCalledWith(ReferenceDataResourceType.ReasonsForAbandoningHgv); + referenceDataSelectors.selectReasonsForAbandoning(VehicleTypes.TRL); + expect(selectorSpy).toHaveBeenLastCalledWith(ReferenceDataResourceType.ReasonsForAbandoningTrl); + }); - describe('selectRefDataBySearchTerm', () => { - let state: ReferenceDataState; - beforeEach(() => { - state = { - ...initialReferenceDataState, - [ReferenceDataResourceType.Tyres]: { - ...initialReferenceDataState[ReferenceDataResourceType.Tyres], - loading: false, - ids: [1, 11, 111, 101, 2], - entities: { - 1: { resourceKey: 1, brakeCode: 1, resourceType: ReferenceDataResourceType.Tyres } as ReferenceDataModelBase, - 11: { resourceKey: 11, brakeCode: 11, resourceType: ReferenceDataResourceType.Tyres } as ReferenceDataModelBase, - 111: { resourceKey: 111, brakeCode: 111, resourceType: ReferenceDataResourceType.Tyres } as ReferenceDataModelBase, - 101: { resourceKey: 101, brakeCode: 101, resourceType: ReferenceDataResourceType.Tyres } as ReferenceDataModelBase, - 2: { resourceKey: 2, brakeCode: 2, resourceType: ReferenceDataResourceType.Tyres } as ReferenceDataModelBase, - }, - }, - }; - }); - it('should return only items that contain the search term 1', () => { - const expectedState = referenceDataSelectors.selectRefDataBySearchTerm('1', ReferenceDataResourceType.Tyres, 'brakeCode').projector(state); - expect(expectedState).toEqual([ - { resourceKey: 1, brakeCode: 1, resourceType: ReferenceDataResourceType.Tyres }, - { resourceKey: 11, brakeCode: 11, resourceType: ReferenceDataResourceType.Tyres }, - { resourceKey: 111, brakeCode: 111, resourceType: ReferenceDataResourceType.Tyres }, - { resourceKey: 101, brakeCode: 101, resourceType: ReferenceDataResourceType.Tyres }, - ]); - }); - it('should return only items that contain the search term 11', () => { - const expectedState = referenceDataSelectors.selectRefDataBySearchTerm('11', ReferenceDataResourceType.Tyres, 'brakeCode').projector(state); - expect(expectedState).toEqual([ - { resourceKey: 11, brakeCode: 11, resourceType: ReferenceDataResourceType.Tyres }, - { resourceKey: 111, brakeCode: 111, resourceType: ReferenceDataResourceType.Tyres }, - ]); - }); - it('should return only items that contain the search term 2', () => { - const expectedState = referenceDataSelectors.selectRefDataBySearchTerm('2', ReferenceDataResourceType.Tyres, 'brakeCode').projector(state); - expect(expectedState).toEqual([{ resourceKey: 2, brakeCode: 2, resourceType: ReferenceDataResourceType.Tyres }]); - }); - it('should return an empty array if there are no items to return', () => { - const expectedState = referenceDataSelectors.selectRefDataBySearchTerm('3', ReferenceDataResourceType.Tyres, 'brakeCode').projector(state); - expect(expectedState).toEqual([]); - }); - }); + describe('selectRefDataBySearchTerm', () => { + let state: ReferenceDataState; + beforeEach(() => { + state = { + ...initialReferenceDataState, + [ReferenceDataResourceType.Tyres]: { + ...initialReferenceDataState[ReferenceDataResourceType.Tyres], + loading: false, + ids: [1, 11, 111, 101, 2], + entities: { + 1: { + resourceKey: 1, + brakeCode: 1, + resourceType: ReferenceDataResourceType.Tyres, + } as ReferenceDataModelBase, + 11: { + resourceKey: 11, + brakeCode: 11, + resourceType: ReferenceDataResourceType.Tyres, + } as ReferenceDataModelBase, + 111: { + resourceKey: 111, + brakeCode: 111, + resourceType: ReferenceDataResourceType.Tyres, + } as ReferenceDataModelBase, + 101: { + resourceKey: 101, + brakeCode: 101, + resourceType: ReferenceDataResourceType.Tyres, + } as ReferenceDataModelBase, + 2: { + resourceKey: 2, + brakeCode: 2, + resourceType: ReferenceDataResourceType.Tyres, + } as ReferenceDataModelBase, + }, + }, + }; + }); + it('should return only items that contain the search term 1', () => { + const expectedState = referenceDataSelectors + .selectRefDataBySearchTerm('1', ReferenceDataResourceType.Tyres, 'brakeCode') + .projector(state); + expect(expectedState).toEqual([ + { resourceKey: 1, brakeCode: 1, resourceType: ReferenceDataResourceType.Tyres }, + { resourceKey: 11, brakeCode: 11, resourceType: ReferenceDataResourceType.Tyres }, + { resourceKey: 111, brakeCode: 111, resourceType: ReferenceDataResourceType.Tyres }, + { resourceKey: 101, brakeCode: 101, resourceType: ReferenceDataResourceType.Tyres }, + ]); + }); + it('should return only items that contain the search term 11', () => { + const expectedState = referenceDataSelectors + .selectRefDataBySearchTerm('11', ReferenceDataResourceType.Tyres, 'brakeCode') + .projector(state); + expect(expectedState).toEqual([ + { resourceKey: 11, brakeCode: 11, resourceType: ReferenceDataResourceType.Tyres }, + { resourceKey: 111, brakeCode: 111, resourceType: ReferenceDataResourceType.Tyres }, + ]); + }); + it('should return only items that contain the search term 2', () => { + const expectedState = referenceDataSelectors + .selectRefDataBySearchTerm('2', ReferenceDataResourceType.Tyres, 'brakeCode') + .projector(state); + expect(expectedState).toEqual([{ resourceKey: 2, brakeCode: 2, resourceType: ReferenceDataResourceType.Tyres }]); + }); + it('should return an empty array if there are no items to return', () => { + const expectedState = referenceDataSelectors + .selectRefDataBySearchTerm('3', ReferenceDataResourceType.Tyres, 'brakeCode') + .projector(state); + expect(expectedState).toEqual([]); + }); + }); }); diff --git a/src/app/store/reference-data/selectors/reference-data.selectors.ts b/src/app/store/reference-data/selectors/reference-data.selectors.ts index 2432ffb249..63a25e3229 100644 --- a/src/app/store/reference-data/selectors/reference-data.selectors.ts +++ b/src/app/store/reference-data/selectors/reference-data.selectors.ts @@ -1,79 +1,95 @@ -import { - Brake, ReferenceDataResourceType, ReferenceDataResourceTypeAudit, -} from '@models/reference-data.model'; +import { Brake, ReferenceDataResourceType, ReferenceDataResourceTypeAudit } from '@models/reference-data.model'; import { VehicleTypes } from '@models/vehicle-tech-record.model'; import { createSelector } from '@ngrx/store'; -import { ReferenceDataEntityStateSearch, referenceDataFeatureState, resourceTypeAdapters } from '../reducers/reference-data.reducer'; +import { + ReferenceDataEntityStateSearch, + referenceDataFeatureState, + resourceTypeAdapters, +} from '../reducers/reference-data.reducer'; -const resourceTypeSelector = (resourceType: ReferenceDataResourceType) => createSelector( - referenceDataFeatureState, - (state) => state[`${resourceType}`], -); +const resourceTypeSelector = (resourceType: ReferenceDataResourceType) => + createSelector(referenceDataFeatureState, (state) => state[`${resourceType}`]); export const selectAllReferenceDataByResourceType = (resourceType: ReferenceDataResourceType) => - createSelector(resourceTypeSelector(resourceType), (state) => - isResourceType(resourceType) ? resourceTypeAdapters[`${resourceType}`].getSelectors().selectAll(state) : undefined); + createSelector(resourceTypeSelector(resourceType), (state) => + isResourceType(resourceType) ? resourceTypeAdapters[`${resourceType}`].getSelectors().selectAll(state) : undefined + ); -export const selectReferenceDataByResourceKey = (resourceType: ReferenceDataResourceType, resourceKey: string | number) => - createSelector(referenceDataFeatureState, (state) => (isResourceType(resourceType) - ? state[`${resourceType}`].entities[`${resourceKey}`] - : undefined)); +export const selectReferenceDataByResourceKey = ( + resourceType: ReferenceDataResourceType, + resourceKey: string | number +) => + createSelector(referenceDataFeatureState, (state) => + isResourceType(resourceType) ? state[`${resourceType}`].entities[`${resourceKey}`] : undefined + ); -export const referenceDataLoadingState = createSelector( - referenceDataFeatureState, - (state) => Object.values(state).some((feature) => feature.loading), +export const referenceDataLoadingState = createSelector(referenceDataFeatureState, (state) => + Object.values(state).some((feature) => feature.loading) ); -export const referencePsvMakeLoadingState = createSelector(referenceDataFeatureState, (state) => state.PSV_MAKE.loading); +export const referencePsvMakeLoadingState = createSelector( + referenceDataFeatureState, + (state) => state.PSV_MAKE.loading +); export const selectBrakeByCode = (code: string) => - createSelector(referenceDataFeatureState, (state) => state[ReferenceDataResourceType.Brakes].entities[`${code}`] as Brake); + createSelector( + referenceDataFeatureState, + (state) => state[ReferenceDataResourceType.Brakes].entities[`${code}`] as Brake + ); export const selectReasonsForAbandoning = (vehicleType: VehicleTypes) => { - switch (vehicleType) { - case VehicleTypes.PSV: - return selectAllReferenceDataByResourceType(ReferenceDataResourceType.ReasonsForAbandoningPsv); - case VehicleTypes.HGV: - return selectAllReferenceDataByResourceType(ReferenceDataResourceType.ReasonsForAbandoningHgv); - case VehicleTypes.TRL: - return selectAllReferenceDataByResourceType(ReferenceDataResourceType.ReasonsForAbandoningTrl); - default: - throw new Error('Unknown Vehicle Type'); - } + switch (vehicleType) { + case VehicleTypes.PSV: + return selectAllReferenceDataByResourceType(ReferenceDataResourceType.ReasonsForAbandoningPsv); + case VehicleTypes.HGV: + return selectAllReferenceDataByResourceType(ReferenceDataResourceType.ReasonsForAbandoningHgv); + case VehicleTypes.TRL: + return selectAllReferenceDataByResourceType(ReferenceDataResourceType.ReasonsForAbandoningTrl); + default: + throw new Error('Unknown Vehicle Type'); + } }; export const selectSearchReturn = (type: ReferenceDataResourceTypeAudit) => - createSelector(referenceDataFeatureState, (state) => { - const data = (state[type as ReferenceDataResourceType] as ReferenceDataEntityStateSearch)?.searchReturn; - return data?.sort((a, b) => b.resourceKey.toString().localeCompare(a.resourceKey.toString())); - }); + createSelector(referenceDataFeatureState, (state) => { + const data = (state[type as ReferenceDataResourceType] as ReferenceDataEntityStateSearch)?.searchReturn; + return data?.sort((a, b) => b.resourceKey.toString().localeCompare(a.resourceKey.toString())); + }); export const selectTyreSearchCriteria = createSelector( - referenceDataFeatureState, - (state) => state[ReferenceDataResourceType.Tyres] as ReferenceDataEntityStateSearch, + referenceDataFeatureState, + (state) => state[ReferenceDataResourceType.Tyres] as ReferenceDataEntityStateSearch ); -export const selectRefDataBySearchTerm = (searchTerm: string, referenceDataType: ReferenceDataResourceType, filter: string) => - createSelector(referenceDataFeatureState, (state) => { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const searchItem: Array = []; +export const selectRefDataBySearchTerm = ( + searchTerm: string, + referenceDataType: ReferenceDataResourceType, + filter: string +) => + createSelector(referenceDataFeatureState, (state) => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const searchItem: Array = []; - state[`${referenceDataType}`].ids.forEach((key) => { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const obj = state[`${referenceDataType}`].entities[`${key}`] as any; - if (obj[`${filter}`].toString().toUpperCase().includes(searchTerm.toString().toUpperCase())) { - searchItem.push(obj); - } - }); - if (searchTerm.length > 0) { - return searchItem; - } - return undefined; - }); + state[`${referenceDataType}`].ids.forEach((key) => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const obj = state[`${referenceDataType}`].entities[`${key}`] as any; + if (obj[`${filter}`].toString().toUpperCase().includes(searchTerm.toString().toUpperCase())) { + searchItem.push(obj); + } + }); + if (searchTerm.length > 0) { + return searchItem; + } + return undefined; + }); export const selectUserByResourceKey = (resourceKey: string) => - createSelector(referenceDataFeatureState, (state) => state[ReferenceDataResourceType.User].entities[`${resourceKey}`]); + createSelector( + referenceDataFeatureState, + (state) => state[ReferenceDataResourceType.User].entities[`${resourceKey}`] + ); export const isResourceType = (resourceType: string): resourceType is ReferenceDataResourceType => { - return Object.values(ReferenceDataResourceType).includes(resourceType as ReferenceDataResourceType); + return Object.values(ReferenceDataResourceType).includes(resourceType as ReferenceDataResourceType); }; diff --git a/src/app/store/required-standards/actions/required-standards.actions.spec.ts b/src/app/store/required-standards/actions/required-standards.actions.spec.ts index 09be66345f..d8a40e7d8a 100644 --- a/src/app/store/required-standards/actions/required-standards.actions.spec.ts +++ b/src/app/store/required-standards/actions/required-standards.actions.spec.ts @@ -1,9 +1,13 @@ -import { getRequiredStandards, getRequiredStandardsFailure, getRequiredStandardsSuccess } from './required-standards.actions'; +import { + getRequiredStandards, + getRequiredStandardsFailure, + getRequiredStandardsSuccess, +} from './required-standards.actions'; describe('Required Standards Actions', () => { - it('should return correct types', () => { - expect(getRequiredStandards.type).toBe('[Required Standards] getRequiredStandards'); - expect(getRequiredStandardsSuccess.type).toBe('[Required Standards] getRequiredStandards Success'); - expect(getRequiredStandardsFailure.type).toBe('[Required Standards] getRequiredStandards Failure'); - }); + it('should return correct types', () => { + expect(getRequiredStandards.type).toBe('[Required Standards] getRequiredStandards'); + expect(getRequiredStandardsSuccess.type).toBe('[Required Standards] getRequiredStandards Success'); + expect(getRequiredStandardsFailure.type).toBe('[Required Standards] getRequiredStandards Failure'); + }); }); diff --git a/src/app/store/required-standards/actions/required-standards.actions.ts b/src/app/store/required-standards/actions/required-standards.actions.ts index 23231f2133..298f122c15 100644 --- a/src/app/store/required-standards/actions/required-standards.actions.ts +++ b/src/app/store/required-standards/actions/required-standards.actions.ts @@ -4,9 +4,12 @@ import { createAction, props } from '@ngrx/store'; const prefix = '[Required Standards]'; -export const getRequiredStandards = createAction(`${prefix} getRequiredStandards`, props<{ euVehicleCategory: string }>()); +export const getRequiredStandards = createAction( + `${prefix} getRequiredStandards`, + props<{ euVehicleCategory: string }>() +); export const getRequiredStandardsSuccess = createAction( - `${prefix} getRequiredStandards Success`, - props<{ requiredStandards: DefectGETRequiredStandards }>(), + `${prefix} getRequiredStandards Success`, + props<{ requiredStandards: DefectGETRequiredStandards }>() ); export const getRequiredStandardsFailure = createAction(`${prefix} getRequiredStandards Failure`, props()); diff --git a/src/app/store/required-standards/effects/required-standards.effects.spec.ts b/src/app/store/required-standards/effects/required-standards.effects.spec.ts index 7220b53a61..1bb68f8bc5 100644 --- a/src/app/store/required-standards/effects/required-standards.effects.spec.ts +++ b/src/app/store/required-standards/effects/required-standards.effects.spec.ts @@ -8,77 +8,82 @@ import { RequiredStandardsService } from '@services/required-standards/required- import { initialAppState } from '@store/index'; import { Observable } from 'rxjs'; import { TestScheduler } from 'rxjs/testing'; -import { getRequiredStandards, getRequiredStandardsFailure, getRequiredStandardsSuccess } from '../actions/required-standards.actions'; +import { + getRequiredStandards, + getRequiredStandardsFailure, + getRequiredStandardsSuccess, +} from '../actions/required-standards.actions'; import { RequiredStandardsEffects } from './required-standards.effects'; describe('RequiredStandardEffects', () => { - let effects: RequiredStandardsEffects; - let actions$ = new Observable(); - let testScheduler: TestScheduler; - let service: RequiredStandardsService; + let effects: RequiredStandardsEffects; + let actions$ = new Observable(); + let testScheduler: TestScheduler; + let service: RequiredStandardsService; - const testCases = [ - { - requiredStandards: [{ - rsNumber: 1, - }] as unknown as DefectGETRequiredStandards, - }, - ]; + const testCases = [ + { + requiredStandards: [ + { + rsNumber: 1, + }, + ] as unknown as DefectGETRequiredStandards, + }, + ]; - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [HttpClientTestingModule], - providers: [ - RequiredStandardsEffects, - provideMockActions(() => actions$), - RequiredStandardsService, - provideMockStore({ - initialState: initialAppState, - }), - ], - }); + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [HttpClientTestingModule], + providers: [ + RequiredStandardsEffects, + provideMockActions(() => actions$), + RequiredStandardsService, + provideMockStore({ + initialState: initialAppState, + }), + ], + }); - effects = TestBed.inject(RequiredStandardsEffects); - service = TestBed.inject(RequiredStandardsService); - }); + effects = TestBed.inject(RequiredStandardsEffects); + service = TestBed.inject(RequiredStandardsService); + }); - beforeEach(() => { - testScheduler = new TestScheduler((actual, expected) => { - expect(actual).toEqual(expected); - }); - }); + beforeEach(() => { + testScheduler = new TestScheduler((actual, expected) => { + expect(actual).toEqual(expected); + }); + }); - describe('getRequiredStandards$', () => { - it.each(testCases)('should return requiredStandardsSuccess action on successful API call', (value) => { - testScheduler.run(({ hot, cold, expectObservable }) => { - const { requiredStandards } = value; + describe('getRequiredStandards$', () => { + it.each(testCases)('should return requiredStandardsSuccess action on successful API call', (value) => { + testScheduler.run(({ hot, cold, expectObservable }) => { + const { requiredStandards } = value; - // mock action to trigger effect - actions$ = hot('-a--', { a: getRequiredStandards({ euVehicleCategory: 'm1' }) }); + // mock action to trigger effect + actions$ = hot('-a--', { a: getRequiredStandards({ euVehicleCategory: 'm1' }) }); - // mock service call - jest.spyOn(service, 'getRequiredStandards').mockReturnValue(cold('--a|', { a: requiredStandards })); + // mock service call + jest.spyOn(service, 'getRequiredStandards').mockReturnValue(cold('--a|', { a: requiredStandards })); - // expect effect to return success action - expectObservable(effects.getRequiredStandards$).toBe('---b', { - b: getRequiredStandardsSuccess({ requiredStandards }), - }); - }); - }); + // expect effect to return success action + expectObservable(effects.getRequiredStandards$).toBe('---b', { + b: getRequiredStandardsSuccess({ requiredStandards }), + }); + }); + }); - it.each(testCases)('should return getRequiredStandardsFailed action on API error', () => { - testScheduler.run(({ hot, cold, expectObservable }) => { - actions$ = hot('-a--', { a: getRequiredStandards({ euVehicleCategory: 'm1' }) }); + it.each(testCases)('should return getRequiredStandardsFailed action on API error', () => { + testScheduler.run(({ hot, cold, expectObservable }) => { + actions$ = hot('-a--', { a: getRequiredStandards({ euVehicleCategory: 'm1' }) }); - const expectedError = new Error('No required standards found'); + const expectedError = new Error('No required standards found'); - jest.spyOn(service, 'getRequiredStandards').mockReturnValue(cold('--#|', {}, expectedError)); + jest.spyOn(service, 'getRequiredStandards').mockReturnValue(cold('--#|', {}, expectedError)); - expectObservable(effects.getRequiredStandards$).toBe( - '---b', - { b: getRequiredStandardsFailure({ error: 'No required standards found' }) }, - ); - }); - }); - }); + expectObservable(effects.getRequiredStandards$).toBe('---b', { + b: getRequiredStandardsFailure({ error: 'No required standards found' }), + }); + }); + }); + }); }); diff --git a/src/app/store/required-standards/effects/required-standards.effects.ts b/src/app/store/required-standards/effects/required-standards.effects.ts index fbc926c935..2ce5ec1ee4 100644 --- a/src/app/store/required-standards/effects/required-standards.effects.ts +++ b/src/app/store/required-standards/effects/required-standards.effects.ts @@ -1,21 +1,27 @@ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { Actions, createEffect, ofType } from '@ngrx/effects'; import { RequiredStandardsService } from '@services/required-standards/required-standards.service'; +import { catchError, map, mergeMap, of } from 'rxjs'; import { - catchError, map, mergeMap, of, -} from 'rxjs'; -import { getRequiredStandards, getRequiredStandardsFailure, getRequiredStandardsSuccess } from '../actions/required-standards.actions'; + getRequiredStandards, + getRequiredStandardsFailure, + getRequiredStandardsSuccess, +} from '../actions/required-standards.actions'; @Injectable() export class RequiredStandardsEffects { - getRequiredStandards$ = createEffect(() => - this.actions$.pipe( - ofType(getRequiredStandards), - mergeMap(({ euVehicleCategory }) => this.requiredStandardsService.getRequiredStandards(euVehicleCategory).pipe( - map((requiredStandards) => getRequiredStandardsSuccess({ requiredStandards })), - catchError((e) => of(getRequiredStandardsFailure({ error: e.message }))), - )), - )); + private actions$ = inject(Actions); + private requiredStandardsService = inject(RequiredStandardsService); - constructor(private actions$: Actions, private requiredStandardsService: RequiredStandardsService) {} + getRequiredStandards$ = createEffect(() => + this.actions$.pipe( + ofType(getRequiredStandards), + mergeMap(({ euVehicleCategory }) => + this.requiredStandardsService.getRequiredStandards(euVehicleCategory).pipe( + map((requiredStandards) => getRequiredStandardsSuccess({ requiredStandards })), + catchError((e) => of(getRequiredStandardsFailure({ error: e.message }))) + ) + ) + ) + ); } diff --git a/src/app/store/required-standards/reducers/required-standards.reducer.spec.ts b/src/app/store/required-standards/reducers/required-standards.reducer.spec.ts index 55637609e5..32a9d5152d 100644 --- a/src/app/store/required-standards/reducers/required-standards.reducer.spec.ts +++ b/src/app/store/required-standards/reducers/required-standards.reducer.spec.ts @@ -1,58 +1,68 @@ import { DefectGETRequiredStandards } from '@dvsa/cvs-type-definitions/types/required-standards/defects/get'; -import { getRequiredStandards, getRequiredStandardsFailure, getRequiredStandardsSuccess } from '../actions/required-standards.actions'; -import { RequiredStandardState, initialRequiredStandardsState, requiredStandardsReducer } from './required-standards.reducer'; +import { + getRequiredStandards, + getRequiredStandardsFailure, + getRequiredStandardsSuccess, +} from '../actions/required-standards.actions'; +import { + RequiredStandardState, + initialRequiredStandardsState, + requiredStandardsReducer, +} from './required-standards.reducer'; describe('Required Standards Reducer', () => { - const expectedRequiredStandards = { - basic: [], - normal: [], - euVehicleCategories: ['m1'], - }; - - describe('unknown action', () => { - it('should return the default state', () => { - const action = { - type: 'Unknown', - }; - const state = requiredStandardsReducer(initialRequiredStandardsState, action); - - expect(state).toBe(initialRequiredStandardsState); - }); - }); - - describe('requiredStandards actions', () => { - it('should set loading to true', () => { - const newState: RequiredStandardState = { ...initialRequiredStandardsState, loading: true }; - const action = getRequiredStandards({ euVehicleCategory: 'm1' }); - const state = requiredStandardsReducer(initialRequiredStandardsState, action); - - expect(state).toEqual(newState); - expect(state).not.toBe(newState); - }); - - describe('getRequiredStandardsSuccess', () => { - it('should set all test result records', () => { - const newState: RequiredStandardState = { - ...initialRequiredStandardsState, - requiredStandards: expectedRequiredStandards as unknown as DefectGETRequiredStandards, - }; - const action = getRequiredStandardsSuccess({ requiredStandards: expectedRequiredStandards as unknown as DefectGETRequiredStandards }); - const state = requiredStandardsReducer(initialRequiredStandardsState, action); - - expect(state).toEqual(newState); - expect(state).not.toBe(newState); - }); - - describe('getRequiredStandardsFailure', () => { - it('should set error state', () => { - const newState = { ...initialRequiredStandardsState, loading: false }; - const action = getRequiredStandardsFailure({ error: 'unit testing error message' }); - const state = requiredStandardsReducer({ ...initialRequiredStandardsState, loading: true }, action); - - expect(state).toEqual(newState); - expect(state).not.toBe(newState); - }); - }); - }); - }); + const expectedRequiredStandards = { + basic: [], + normal: [], + euVehicleCategories: ['m1'], + }; + + describe('unknown action', () => { + it('should return the default state', () => { + const action = { + type: 'Unknown', + }; + const state = requiredStandardsReducer(initialRequiredStandardsState, action); + + expect(state).toBe(initialRequiredStandardsState); + }); + }); + + describe('requiredStandards actions', () => { + it('should set loading to true', () => { + const newState: RequiredStandardState = { ...initialRequiredStandardsState, loading: true }; + const action = getRequiredStandards({ euVehicleCategory: 'm1' }); + const state = requiredStandardsReducer(initialRequiredStandardsState, action); + + expect(state).toEqual(newState); + expect(state).not.toBe(newState); + }); + + describe('getRequiredStandardsSuccess', () => { + it('should set all test result records', () => { + const newState: RequiredStandardState = { + ...initialRequiredStandardsState, + requiredStandards: expectedRequiredStandards as unknown as DefectGETRequiredStandards, + }; + const action = getRequiredStandardsSuccess({ + requiredStandards: expectedRequiredStandards as unknown as DefectGETRequiredStandards, + }); + const state = requiredStandardsReducer(initialRequiredStandardsState, action); + + expect(state).toEqual(newState); + expect(state).not.toBe(newState); + }); + + describe('getRequiredStandardsFailure', () => { + it('should set error state', () => { + const newState = { ...initialRequiredStandardsState, loading: false }; + const action = getRequiredStandardsFailure({ error: 'unit testing error message' }); + const state = requiredStandardsReducer({ ...initialRequiredStandardsState, loading: true }, action); + + expect(state).toEqual(newState); + expect(state).not.toBe(newState); + }); + }); + }); + }); }); diff --git a/src/app/store/required-standards/reducers/required-standards.reducer.ts b/src/app/store/required-standards/reducers/required-standards.reducer.ts index cf3a39b6ea..bf2a0bab5c 100644 --- a/src/app/store/required-standards/reducers/required-standards.reducer.ts +++ b/src/app/store/required-standards/reducers/required-standards.reducer.ts @@ -1,47 +1,55 @@ import { DefectGETRequiredStandards } from '@dvsa/cvs-type-definitions/types/required-standards/defects/get'; import { createFeatureSelector, createReducer, on } from '@ngrx/store'; -import { getRequiredStandards, getRequiredStandardsFailure, getRequiredStandardsSuccess } from '../actions/required-standards.actions'; +import { + getRequiredStandards, + getRequiredStandardsFailure, + getRequiredStandardsSuccess, +} from '../actions/required-standards.actions'; export interface RequiredStandardState { - loading: boolean; - error: string; - requiredStandards: DefectGETRequiredStandards + loading: boolean; + error: string; + requiredStandards: DefectGETRequiredStandards; } export const STORE_FEATURE_REQUIRED_STANDARDS_KEY = 'RequiredStandards'; -export const requiredStandardsFeatureState = createFeatureSelector(STORE_FEATURE_REQUIRED_STANDARDS_KEY); +export const requiredStandardsFeatureState = createFeatureSelector( + STORE_FEATURE_REQUIRED_STANDARDS_KEY +); export const initialRequiredStandardsState: RequiredStandardState = { - loading: false, - error: '', - requiredStandards: { - basic: [], - normal: [], - euVehicleCategories: [], - }, - + loading: false, + error: '', + requiredStandards: { + basic: [], + normal: [], + euVehicleCategories: [], + }, }; export const requiredStandardsReducer = createReducer( - initialRequiredStandardsState, - - on(getRequiredStandards, (state) => ({ ...state, loading: true })), - on(getRequiredStandardsSuccess, (state, action) => ({ - ...state, - requiredStandards: orderRequiredStandards(action.requiredStandards), - loading: false, - })), - on(getRequiredStandardsFailure, (state) => ({ ...state, loading: false })), - + initialRequiredStandardsState, + + on(getRequiredStandards, (state) => ({ ...state, loading: true })), + on(getRequiredStandardsSuccess, (state, action) => ({ + ...state, + requiredStandards: orderRequiredStandards(action.requiredStandards), + loading: false, + })), + on(getRequiredStandardsFailure, (state) => ({ ...state, loading: false })) ); function orderRequiredStandards(requiredStandards: DefectGETRequiredStandards) { - if (requiredStandards.basic.length) { - requiredStandards.basic.sort((current, next) => current.sectionNumber.localeCompare(next.sectionNumber, 'en', { numeric: true })); - } - if (requiredStandards.normal.length) { - requiredStandards.normal.sort((current, next) => current.sectionNumber.localeCompare(next.sectionNumber, 'en', { numeric: true })); - } - return requiredStandards; + if (requiredStandards.basic.length) { + requiredStandards.basic.sort((current, next) => + current.sectionNumber.localeCompare(next.sectionNumber, 'en', { numeric: true }) + ); + } + if (requiredStandards.normal.length) { + requiredStandards.normal.sort((current, next) => + current.sectionNumber.localeCompare(next.sectionNumber, 'en', { numeric: true }) + ); + } + return requiredStandards; } diff --git a/src/app/store/required-standards/required-standards.module.ts b/src/app/store/required-standards/required-standards.module.ts index 8d4678cdcf..bfdbbffae8 100644 --- a/src/app/store/required-standards/required-standards.module.ts +++ b/src/app/store/required-standards/required-standards.module.ts @@ -6,8 +6,11 @@ import { RequiredStandardsEffects } from './effects/required-standards.effects'; import { STORE_FEATURE_REQUIRED_STANDARDS_KEY, requiredStandardsReducer } from './reducers/required-standards.reducer'; @NgModule({ - declarations: [], - imports: [CommonModule, StoreModule.forFeature(STORE_FEATURE_REQUIRED_STANDARDS_KEY, requiredStandardsReducer), - EffectsModule.forFeature([RequiredStandardsEffects])], + declarations: [], + imports: [ + CommonModule, + StoreModule.forFeature(STORE_FEATURE_REQUIRED_STANDARDS_KEY, requiredStandardsReducer), + EffectsModule.forFeature([RequiredStandardsEffects]), + ], }) export class RequiredStandardsStateModule {} diff --git a/src/app/store/required-standards/selectors/required-standards.selector.spec.ts b/src/app/store/required-standards/selectors/required-standards.selector.spec.ts index 022dc05299..5866352b7b 100644 --- a/src/app/store/required-standards/selectors/required-standards.selector.spec.ts +++ b/src/app/store/required-standards/selectors/required-standards.selector.spec.ts @@ -2,74 +2,85 @@ import { EUVehicleCategory } from '@dvsa/cvs-type-definitions/types/required-sta import { DefectGETRequiredStandards } from '@dvsa/cvs-type-definitions/types/required-standards/defects/get'; import { INSPECTION_TYPE } from '@models/test-results/test-result-required-standard.model'; import { RequiredStandardState, initialRequiredStandardsState } from '../reducers/required-standards.reducer'; -import { getRequiredStandardFromTypeAndRef, getRequiredStandardsState, requiredStandardsLoadingState } from './required-standards.selector'; +import { + getRequiredStandardFromTypeAndRef, + getRequiredStandardsState, + requiredStandardsLoadingState, +} from './required-standards.selector'; describe('RequiredStandardsLoadingState', () => { - it('should return loading state', () => { - const state: RequiredStandardState = { ...initialRequiredStandardsState, loading: true }; - const selectedState = requiredStandardsLoadingState.projector(state); - expect(selectedState).toBeTruthy(); - }); + it('should return loading state', () => { + const state: RequiredStandardState = { ...initialRequiredStandardsState, loading: true }; + const selectedState = requiredStandardsLoadingState.projector(state); + expect(selectedState).toBeTruthy(); + }); - describe('getRequiredStandardsState', () => { - it('should return me the required standards state', () => { - const state: RequiredStandardState = { ...initialRequiredStandardsState, loading: false }; - const selectedState = getRequiredStandardsState.projector(state); - expect(selectedState).toBeTruthy(); - }); - }); - - describe('getRequiredStandardFromTypeAndRef', () => { - it('should return me the required standards state when given a inspection type and ref', () => { - const requiredStandard = { - rsNumber: 1, - requiredStandard: 'rs', - refCalculation: '01.1', - additionalInfo: false, - inspectionTypes: [INSPECTION_TYPE.NORMAL], - }; - const requiredStandards: DefectGETRequiredStandards = { - normal: [{ - sectionNumber: '01', - sectionDescription: 'desc', - requiredStandards: [{ - rsNumber: 1, - requiredStandard: 'rs', - refCalculation: '01.1', - additionalInfo: false, - inspectionTypes: [INSPECTION_TYPE.NORMAL], - }], - }], - basic: [], - euVehicleCategories: [EUVehicleCategory.M1], - }; - initialRequiredStandardsState.requiredStandards = requiredStandards; - const state: RequiredStandardState = { ...initialRequiredStandardsState, loading: false }; - const selectedState = getRequiredStandardFromTypeAndRef(INSPECTION_TYPE.NORMAL, '01.1').projector(state); - expect(selectedState).toBeTruthy(); - expect(selectedState).toStrictEqual({ ...requiredStandard, sectionNumber: '01', sectionDescription: 'desc' }); - }); - it('should return undefined if no section or RS is found', () => { - const requiredStandards: DefectGETRequiredStandards = { - normal: [{ - sectionNumber: '01', - sectionDescription: 'desc', - requiredStandards: [{ - rsNumber: 1, - requiredStandard: 'rs', - refCalculation: '01.1', - additionalInfo: false, - inspectionTypes: [INSPECTION_TYPE.NORMAL], - }], - }], - basic: [], - euVehicleCategories: [EUVehicleCategory.M1], - }; - initialRequiredStandardsState.requiredStandards = requiredStandards; - const state: RequiredStandardState = { ...initialRequiredStandardsState, loading: false }; - const selectedState = getRequiredStandardFromTypeAndRef(INSPECTION_TYPE.NORMAL, 'data').projector(state); - expect(selectedState).toBeUndefined(); - }); - }); + describe('getRequiredStandardsState', () => { + it('should return me the required standards state', () => { + const state: RequiredStandardState = { ...initialRequiredStandardsState, loading: false }; + const selectedState = getRequiredStandardsState.projector(state); + expect(selectedState).toBeTruthy(); + }); + }); + describe('getRequiredStandardFromTypeAndRef', () => { + it('should return me the required standards state when given a inspection type and ref', () => { + const requiredStandard = { + rsNumber: 1, + requiredStandard: 'rs', + refCalculation: '01.1', + additionalInfo: false, + inspectionTypes: [INSPECTION_TYPE.NORMAL], + }; + const requiredStandards: DefectGETRequiredStandards = { + normal: [ + { + sectionNumber: '01', + sectionDescription: 'desc', + requiredStandards: [ + { + rsNumber: 1, + requiredStandard: 'rs', + refCalculation: '01.1', + additionalInfo: false, + inspectionTypes: [INSPECTION_TYPE.NORMAL], + }, + ], + }, + ], + basic: [], + euVehicleCategories: [EUVehicleCategory.M1], + }; + initialRequiredStandardsState.requiredStandards = requiredStandards; + const state: RequiredStandardState = { ...initialRequiredStandardsState, loading: false }; + const selectedState = getRequiredStandardFromTypeAndRef(INSPECTION_TYPE.NORMAL, '01.1').projector(state); + expect(selectedState).toBeTruthy(); + expect(selectedState).toStrictEqual({ ...requiredStandard, sectionNumber: '01', sectionDescription: 'desc' }); + }); + it('should return undefined if no section or RS is found', () => { + const requiredStandards: DefectGETRequiredStandards = { + normal: [ + { + sectionNumber: '01', + sectionDescription: 'desc', + requiredStandards: [ + { + rsNumber: 1, + requiredStandard: 'rs', + refCalculation: '01.1', + additionalInfo: false, + inspectionTypes: [INSPECTION_TYPE.NORMAL], + }, + ], + }, + ], + basic: [], + euVehicleCategories: [EUVehicleCategory.M1], + }; + initialRequiredStandardsState.requiredStandards = requiredStandards; + const state: RequiredStandardState = { ...initialRequiredStandardsState, loading: false }; + const selectedState = getRequiredStandardFromTypeAndRef(INSPECTION_TYPE.NORMAL, 'data').projector(state); + expect(selectedState).toBeUndefined(); + }); + }); }); diff --git a/src/app/store/required-standards/selectors/required-standards.selector.ts b/src/app/store/required-standards/selectors/required-standards.selector.ts index 66c986118d..619ea57e0c 100644 --- a/src/app/store/required-standards/selectors/required-standards.selector.ts +++ b/src/app/store/required-standards/selectors/required-standards.selector.ts @@ -2,19 +2,22 @@ import { INSPECTION_TYPE } from '@models/test-results/test-result-required-stand import { createSelector } from '@ngrx/store'; import { requiredStandardsFeatureState } from '../reducers/required-standards.reducer'; -export const getRequiredStandardsState = createSelector(requiredStandardsFeatureState, (state) => state.requiredStandards); +export const getRequiredStandardsState = createSelector( + requiredStandardsFeatureState, + (state) => state.requiredStandards +); export const getRequiredStandardFromTypeAndRef = (inspectionType: INSPECTION_TYPE, rsRefCalculation: string) => - createSelector(requiredStandardsFeatureState, (state) => { - const deRefRsCalculation = rsRefCalculation.split('.'); - const sectionNumber = deRefRsCalculation[0]; - // eslint-disable-next-line security/detect-object-injection - const section = state.requiredStandards[inspectionType] - .find((sec) => sec.sectionNumber === sectionNumber); - const requiredStandard = section?.requiredStandards.find((rs) => rs.refCalculation === rsRefCalculation); + createSelector(requiredStandardsFeatureState, (state) => { + const deRefRsCalculation = rsRefCalculation.split('.'); + const sectionNumber = deRefRsCalculation[0]; + // eslint-disable-next-line security/detect-object-injection + const section = state.requiredStandards[inspectionType].find((sec) => sec.sectionNumber === sectionNumber); + const requiredStandard = section?.requiredStandards.find((rs) => rs.refCalculation === rsRefCalculation); - if (requiredStandard && section) return { ...requiredStandard, sectionNumber, sectionDescription: section.sectionDescription }; - return undefined; - }); + if (requiredStandard && section) + return { ...requiredStandard, sectionNumber, sectionDescription: section.sectionDescription }; + return undefined; + }); export const requiredStandardsLoadingState = createSelector(requiredStandardsFeatureState, (state) => state.loading); diff --git a/src/app/store/retry-interceptor/actions/retry-interceptor.actions.ts b/src/app/store/retry-interceptor/actions/retry-interceptor.actions.ts index 60cbd8aaf9..d3bf7004e2 100644 --- a/src/app/store/retry-interceptor/actions/retry-interceptor.actions.ts +++ b/src/app/store/retry-interceptor/actions/retry-interceptor.actions.ts @@ -1,3 +1,6 @@ import { createAction, props } from '@ngrx/store'; -export const retryInterceptorFailure = createAction('[retry-interceptor] retryInterceptorFailure', props<{ error: string }>()); +export const retryInterceptorFailure = createAction( + '[retry-interceptor] retryInterceptorFailure', + props<{ error: string }>() +); diff --git a/src/app/store/retry-interceptor/retry-interceptor.module.ts b/src/app/store/retry-interceptor/retry-interceptor.module.ts index 50b5181d08..ad904696ec 100644 --- a/src/app/store/retry-interceptor/retry-interceptor.module.ts +++ b/src/app/store/retry-interceptor/retry-interceptor.module.ts @@ -2,7 +2,7 @@ import { CommonModule } from '@angular/common'; import { NgModule } from '@angular/core'; @NgModule({ - declarations: [], - imports: [CommonModule], + declarations: [], + imports: [CommonModule], }) export class RetryInterceptorStateModule {} diff --git a/src/app/store/router/router-state.module.ts b/src/app/store/router/router-state.module.ts index 93801df7bc..8c2399bf2b 100644 --- a/src/app/store/router/router-state.module.ts +++ b/src/app/store/router/router-state.module.ts @@ -1,11 +1,15 @@ import { CommonModule } from '@angular/common'; import { NgModule } from '@angular/core'; -import { routerReducer, StoreRouterConnectingModule } from '@ngrx/router-store'; +import { StoreRouterConnectingModule, routerReducer } from '@ngrx/router-store'; import { StoreModule } from '@ngrx/store'; import { STORE_FEATURE_ROUTER_STORE_KEY } from './selectors/router.selectors'; @NgModule({ - declarations: [], - imports: [CommonModule, StoreModule.forFeature(STORE_FEATURE_ROUTER_STORE_KEY, routerReducer), StoreRouterConnectingModule.forRoot()], + declarations: [], + imports: [ + CommonModule, + StoreModule.forFeature(STORE_FEATURE_ROUTER_STORE_KEY, routerReducer), + StoreRouterConnectingModule.forRoot(), + ], }) export class RouterStateModule {} diff --git a/src/app/store/router/selectors/router.selectors.spec.ts b/src/app/store/router/selectors/router.selectors.spec.ts index 2f28e88505..d933820eb8 100644 --- a/src/app/store/router/selectors/router.selectors.spec.ts +++ b/src/app/store/router/selectors/router.selectors.spec.ts @@ -2,11 +2,11 @@ import { RouterReducerState, SerializedRouterStateSnapshot } from '@ngrx/router- import { selectRouteNestedParams } from './router.selectors'; describe('selectRouteNestedParams', () => { - it('should return all nested params as a flat object', () => { - const router = { - state: { root: { firstChild: { params: { foo: 'bar' }, firstChild: { params: { bar: 'baz' } } } } }, - } as unknown as RouterReducerState; - const params = selectRouteNestedParams.projector(router); - expect(params).toEqual({ foo: 'bar', bar: 'baz' }); - }); + it('should return all nested params as a flat object', () => { + const router = { + state: { root: { firstChild: { params: { foo: 'bar' }, firstChild: { params: { bar: 'baz' } } } } }, + } as unknown as RouterReducerState; + const params = selectRouteNestedParams.projector(router); + expect(params).toEqual({ foo: 'bar', bar: 'baz' }); + }); }); diff --git a/src/app/store/router/selectors/router.selectors.ts b/src/app/store/router/selectors/router.selectors.ts index ca3ab385ac..19f2f4d833 100644 --- a/src/app/store/router/selectors/router.selectors.ts +++ b/src/app/store/router/selectors/router.selectors.ts @@ -1,37 +1,38 @@ import { Params } from '@angular/router'; -import { getRouterSelectors, RouterReducerState } from '@ngrx/router-store'; +import { RouterReducerState, getRouterSelectors } from '@ngrx/router-store'; import { createFeatureSelector, createSelector } from '@ngrx/store'; export const STORE_FEATURE_ROUTER_STORE_KEY = 'router'; export const selectRouter = createFeatureSelector(STORE_FEATURE_ROUTER_STORE_KEY); export const { - selectCurrentRoute, // select the current route - selectFragment, // select the current route fragment - selectQueryParams, // select the current route query params - selectQueryParam, // factory function to select a query param - selectRouteParams, // select the current route params - selectRouteParam, // factory function to select a route param - selectRouteData, // select the current route data - selectUrl, // select the current url + selectCurrentRoute, // select the current route + selectFragment, // select the current route fragment + selectQueryParams, // select the current route query params + selectQueryParam, // factory function to select a query param + selectRouteParams, // select the current route params + selectRouteParam, // factory function to select a route param + selectRouteData, // select the current route data + selectUrl, // select the current url } = getRouterSelectors(); export const routerState = createSelector(selectRouter, (state) => state); export const currentRouteState = createSelector(selectCurrentRoute, (state) => state); -export const selectRouteDataProperty = (property: string) => createSelector(selectRouteData, (data) => data?.[`${property}`]); +export const selectRouteDataProperty = (property: string) => + createSelector(selectRouteData, (data) => data?.[`${property}`]); export const selectRouteNestedParams = createSelector(selectRouter, (router) => { - let currentRoute = router?.state?.root; - let params: Params = {}; - while (currentRoute?.firstChild) { - currentRoute = currentRoute.firstChild; - params = { - ...params, - ...currentRoute.params, - }; - } - return params; + let currentRoute = router?.state?.root; + let params: Params = {}; + while (currentRoute?.firstChild) { + currentRoute = currentRoute.firstChild; + params = { + ...params, + ...currentRoute.params, + }; + } + return params; }); export const routeEditable = createSelector(selectQueryParam('edit'), (edit) => edit === 'true'); diff --git a/src/app/store/spinner/actions/spinner.actions.spec.ts b/src/app/store/spinner/actions/spinner.actions.spec.ts index f73ff3160c..82a224a0b7 100644 --- a/src/app/store/spinner/actions/spinner.actions.spec.ts +++ b/src/app/store/spinner/actions/spinner.actions.spec.ts @@ -2,28 +2,28 @@ import { initialSpinnerState, spinnerReducer } from '../reducers/spinner.reducer import { setSpinnerState } from './spinner.actions'; describe('unknown action', () => { - it('should return the default state', () => { - const action = { - type: 'Unknown', - }; + it('should return the default state', () => { + const action = { + type: 'Unknown', + }; - const state = spinnerReducer(initialSpinnerState, action); - expect(state).toBe(initialSpinnerState); - }); + const state = spinnerReducer(initialSpinnerState, action); + expect(state).toBe(initialSpinnerState); + }); - it('should not change the state', () => { - const action = { - type: 'Unknown', - }; + it('should not change the state', () => { + const action = { + type: 'Unknown', + }; - const state = spinnerReducer({ showSpinner: true }, action); - expect(state).toEqual({ showSpinner: true }); - expect(state).not.toBe({ showSpinner: true }); - }); + const state = spinnerReducer({ showSpinner: true }, action); + expect(state).toEqual({ showSpinner: true }); + expect(state).not.toBe({ showSpinner: true }); + }); }); describe('setSpinnerState', () => { - it('should have the correct type', () => { - expect(setSpinnerState.type).toBe('[UI/spinner] set spinner state'); - }); + it('should have the correct type', () => { + expect(setSpinnerState.type).toBe('[UI/spinner] set spinner state'); + }); }); diff --git a/src/app/store/spinner/reducers/spinner.reducer.spec.ts b/src/app/store/spinner/reducers/spinner.reducer.spec.ts index e4b7b8ecaa..2108d94d80 100644 --- a/src/app/store/spinner/reducers/spinner.reducer.spec.ts +++ b/src/app/store/spinner/reducers/spinner.reducer.spec.ts @@ -2,13 +2,13 @@ import { initialSpinnerState, spinnerReducer } from '@store/spinner/reducers/spi import { setSpinnerState } from '../actions/spinner.actions'; describe('Spinner Reducer', () => { - it('should set correct state', () => { - expect(setSpinnerState.type).toBe('[UI/spinner] set spinner state'); - const state = { ...initialSpinnerState }; - expect(state.showSpinner).toBeFalsy(); - const showingState = spinnerReducer(state, setSpinnerState({ showSpinner: true })); - expect(showingState.showSpinner).toBeTruthy(); - const notShowingState = spinnerReducer(state, setSpinnerState({ showSpinner: false })); - expect(notShowingState.showSpinner).toBeFalsy(); - }); + it('should set correct state', () => { + expect(setSpinnerState.type).toBe('[UI/spinner] set spinner state'); + const state = { ...initialSpinnerState }; + expect(state.showSpinner).toBeFalsy(); + const showingState = spinnerReducer(state, setSpinnerState({ showSpinner: true })); + expect(showingState.showSpinner).toBeTruthy(); + const notShowingState = spinnerReducer(state, setSpinnerState({ showSpinner: false })); + expect(notShowingState.showSpinner).toBeFalsy(); + }); }); diff --git a/src/app/store/spinner/reducers/spinner.reducer.ts b/src/app/store/spinner/reducers/spinner.reducer.ts index cb2ae9f57b..fecb967bd5 100644 --- a/src/app/store/spinner/reducers/spinner.reducer.ts +++ b/src/app/store/spinner/reducers/spinner.reducer.ts @@ -4,14 +4,14 @@ import { setSpinnerState } from '../actions/spinner.actions'; export const STORE_FEATURE_SPINNER_KEY = 'Spinner'; export interface SpinnerState { - showSpinner: boolean; + showSpinner: boolean; } export const initialSpinnerState: SpinnerState = { - showSpinner: false, + showSpinner: false, }; export const spinnerReducer = createReducer( - initialSpinnerState, - on(setSpinnerState, (state, { showSpinner }) => ({ ...state, showSpinner })), + initialSpinnerState, + on(setSpinnerState, (state, { showSpinner }) => ({ ...state, showSpinner })) ); diff --git a/src/app/store/spinner/selectors/spinner.selectors.spec.ts b/src/app/store/spinner/selectors/spinner.selectors.spec.ts index 08bb9f445a..501e06bf32 100644 --- a/src/app/store/spinner/selectors/spinner.selectors.spec.ts +++ b/src/app/store/spinner/selectors/spinner.selectors.spec.ts @@ -2,11 +2,11 @@ import { SpinnerState, initialSpinnerState } from '@store/spinner/reducers/spinn import { getSpinner } from '@store/spinner/selectors/spinner.selectors'; describe('Spinner Selectors', () => { - describe('getSpinner', () => { - it('should return the spinner', () => { - const state: SpinnerState = { ...initialSpinnerState, showSpinner: true }; - const selectedState = getSpinner.projector(state); - expect(selectedState).toEqual(state.showSpinner); - }); - }); + describe('getSpinner', () => { + it('should return the spinner', () => { + const state: SpinnerState = { ...initialSpinnerState, showSpinner: true }; + const selectedState = getSpinner.projector(state); + expect(selectedState).toEqual(state.showSpinner); + }); + }); }); diff --git a/src/app/store/spinner/selectors/spinner.selectors.ts b/src/app/store/spinner/selectors/spinner.selectors.ts index 99b9d40043..97fa3c8503 100644 --- a/src/app/store/spinner/selectors/spinner.selectors.ts +++ b/src/app/store/spinner/selectors/spinner.selectors.ts @@ -1,8 +1,8 @@ import { createFeatureSelector, createSelector } from '@ngrx/store'; -import { SpinnerState, STORE_FEATURE_SPINNER_KEY } from '../reducers/spinner.reducer'; +import { STORE_FEATURE_SPINNER_KEY, SpinnerState } from '../reducers/spinner.reducer'; export const getSpinnerState = createFeatureSelector(STORE_FEATURE_SPINNER_KEY); export const getSpinner = createSelector(getSpinnerState, (state) => { - return state.showSpinner; + return state.showSpinner; }); diff --git a/src/app/store/spinner/spinner-state.module.ts b/src/app/store/spinner/spinner-state.module.ts index ce07911533..91bf97abdc 100644 --- a/src/app/store/spinner/spinner-state.module.ts +++ b/src/app/store/spinner/spinner-state.module.ts @@ -1,10 +1,10 @@ -import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; +import { NgModule } from '@angular/core'; import { StoreModule } from '@ngrx/store'; -import { spinnerReducer, STORE_FEATURE_SPINNER_KEY } from '@store/spinner/reducers/spinner.reducer'; +import { STORE_FEATURE_SPINNER_KEY, spinnerReducer } from '@store/spinner/reducers/spinner.reducer'; @NgModule({ - declarations: [], - imports: [CommonModule, StoreModule.forFeature(STORE_FEATURE_SPINNER_KEY, spinnerReducer)], + declarations: [], + imports: [CommonModule, StoreModule.forFeature(STORE_FEATURE_SPINNER_KEY, spinnerReducer)], }) export class SpinnerStateModule {} diff --git a/src/app/store/tech-record-search/actions/tech-record-search.actions.ts b/src/app/store/tech-record-search/actions/tech-record-search.actions.ts index 161fc808c0..eddddad410 100644 --- a/src/app/store/tech-record-search/actions/tech-record-search.actions.ts +++ b/src/app/store/tech-record-search/actions/tech-record-search.actions.ts @@ -1,13 +1,16 @@ import { GlobalError } from '@core/components/global-error/global-error.interface'; import { TechRecordSearchSchema } from '@dvsa/cvs-type-definitions/types/v3/tech-record/get/search'; -import { createAction, props } from '@ngrx/store'; import { SEARCH_TYPES } from '@models/search-types-enum'; +import { createAction, props } from '@ngrx/store'; export const fetchSearchResult = createAction(getTitle(), props<{ searchBy?: SEARCH_TYPES; term: string }>()); -export const fetchSearchResultSuccess = createAction(getTitle('Success'), props<{ payload: TechRecordSearchSchema[] }>()); +export const fetchSearchResultSuccess = createAction( + getTitle('Success'), + props<{ payload: TechRecordSearchSchema[] }>() +); export const fetchSearchResultFailed = createAction(getTitle('Failed'), props()); function getTitle(suffix = ''): string { - suffix = suffix ? ` ${suffix}` : suffix; - return `[API/tech-records search] Search Results ${suffix}`; + suffix = suffix ? ` ${suffix}` : suffix; + return `[API/tech-records search] Search Results ${suffix}`; } diff --git a/src/app/store/tech-record-search/effects/tech-record-search.effect.spec.ts b/src/app/store/tech-record-search/effects/tech-record-search.effect.spec.ts index 8c767af569..c1f5eea4c7 100644 --- a/src/app/store/tech-record-search/effects/tech-record-search.effect.spec.ts +++ b/src/app/store/tech-record-search/effects/tech-record-search.effect.spec.ts @@ -9,76 +9,83 @@ import { TechnicalRecordHttpService } from '@services/technical-record-http/tech import { initialAppState } from '@store/index'; import { Observable } from 'rxjs'; import { TestScheduler } from 'rxjs/testing'; -import { fetchSearchResult, fetchSearchResultFailed, fetchSearchResultSuccess } from '../actions/tech-record-search.actions'; +import { + fetchSearchResult, + fetchSearchResultFailed, + fetchSearchResultSuccess, +} from '../actions/tech-record-search.actions'; import { TechSearchResultsEffects } from './tech-record-search.effect'; describe('DefectsEffects', () => { - let effects: TechSearchResultsEffects; - let actions$ = new Observable(); - let testScheduler: TestScheduler; - let service: TechnicalRecordHttpService; + let effects: TechSearchResultsEffects; + let actions$ = new Observable(); + let testScheduler: TestScheduler; + let service: TechnicalRecordHttpService; - const expectedResult = { systemNumber: '1' } as TechRecordSearchSchema; - const testCases = [ - { - id: expectedResult.systemNumber, - payload: [expectedResult], - }, - ]; + const expectedResult = { systemNumber: '1' } as TechRecordSearchSchema; + const testCases = [ + { + id: expectedResult.systemNumber, + payload: [expectedResult], + }, + ]; - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [HttpClientTestingModule], - providers: [ - TechSearchResultsEffects, - provideMockActions(() => actions$), - TechnicalRecordHttpService, - provideMockStore({ - initialState: initialAppState, - }), - ], - }); + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [HttpClientTestingModule], + providers: [ + TechSearchResultsEffects, + provideMockActions(() => actions$), + TechnicalRecordHttpService, + provideMockStore({ + initialState: initialAppState, + }), + ], + }); - effects = TestBed.inject(TechSearchResultsEffects); - service = TestBed.inject(TechnicalRecordHttpService); - }); + effects = TestBed.inject(TechSearchResultsEffects); + service = TestBed.inject(TechnicalRecordHttpService); + }); - beforeEach(() => { - testScheduler = new TestScheduler((actual, expected) => { - expect(actual).toEqual(expected); - }); - }); + beforeEach(() => { + testScheduler = new TestScheduler((actual, expected) => { + expect(actual).toEqual(expected); + }); + }); - describe('fetchSearchResults$', () => { - it.each(testCases)('should return fetchSearchResultsSuccess action on successfull API call', (value) => { - testScheduler.run(({ hot, cold, expectObservable }) => { - const { payload } = value; + describe('fetchSearchResults$', () => { + it.each(testCases)('should return fetchSearchResultsSuccess action on successfull API call', (value) => { + testScheduler.run(({ hot, cold, expectObservable }) => { + const { payload } = value; - // mock action to trigger effect - actions$ = hot('-a--', { a: fetchSearchResult }); + // mock action to trigger effect + actions$ = hot('-a--', { a: fetchSearchResult }); - // mock service call - jest.spyOn(service, 'search$').mockReturnValue(cold('--a|', { a: payload })); + // mock service call + jest.spyOn(service, 'search$').mockReturnValue(cold('--a|', { a: payload })); - // expect effect to return success action - expectObservable(effects.fetchSearchResults$).toBe('---b', { - b: fetchSearchResultSuccess({ payload }), - }); - }); - }); + // expect effect to return success action + expectObservable(effects.fetchSearchResults$).toBe('---b', { + b: fetchSearchResultSuccess({ payload }), + }); + }); + }); - it.each(testCases)('should return fetchSearchResults action on API error', () => { - testScheduler.run(({ hot, cold, expectObservable }) => { - actions$ = hot('-a--', { a: fetchSearchResult({ searchBy: SEARCH_TYPES.VIN, term: 'foo' }) }); + it.each(testCases)('should return fetchSearchResults action on API error', () => { + testScheduler.run(({ hot, cold, expectObservable }) => { + actions$ = hot('-a--', { a: fetchSearchResult({ searchBy: SEARCH_TYPES.VIN, term: 'foo' }) }); - const expectedError = new Error('Oopsi'); + const expectedError = new Error('Oopsi'); - jest.spyOn(service, 'search$').mockReturnValue(cold('--#|', {}, expectedError)); + jest.spyOn(service, 'search$').mockReturnValue(cold('--#|', {}, expectedError)); - expectObservable(effects.fetchSearchResults$).toBe('---b', { - b: fetchSearchResultFailed({ error: 'There was a problem getting the Tech Record by vin', anchorLink: 'search-term' }), - }); - }); - }); - }); + expectObservable(effects.fetchSearchResults$).toBe('---b', { + b: fetchSearchResultFailed({ + error: 'There was a problem getting the Tech Record by vin', + anchorLink: 'search-term', + }), + }); + }); + }); + }); }); diff --git a/src/app/store/tech-record-search/effects/tech-record-search.effect.ts b/src/app/store/tech-record-search/effects/tech-record-search.effect.ts index 8c3b75aac3..8fbe11b69e 100644 --- a/src/app/store/tech-record-search/effects/tech-record-search.effect.ts +++ b/src/app/store/tech-record-search/effects/tech-record-search.effect.ts @@ -1,41 +1,53 @@ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; +import { SEARCH_TYPES } from '@models/search-types-enum'; import { Actions, createEffect, ofType } from '@ngrx/effects'; import { TechnicalRecordHttpService } from '@services/technical-record-http/technical-record-http.service'; -import { SEARCH_TYPES } from '@models/search-types-enum'; +import { catchError, map, of, switchMap } from 'rxjs'; import { - catchError, map, of, switchMap, -} from 'rxjs'; -import { fetchSearchResult, fetchSearchResultFailed, fetchSearchResultSuccess } from '../actions/tech-record-search.actions'; + fetchSearchResult, + fetchSearchResultFailed, + fetchSearchResultSuccess, +} from '../actions/tech-record-search.actions'; @Injectable() export class TechSearchResultsEffects { - fetchSearchResults$ = createEffect(() => - this.actions$.pipe( - ofType(fetchSearchResult), - switchMap(({ searchBy, term }) => - this.techRecordHttpService.search$(searchBy ?? SEARCH_TYPES.ALL, term).pipe( - map((results) => fetchSearchResultSuccess({ payload: results })), - catchError((e) => - of(fetchSearchResultFailed({ error: this.getTechRecordErrorMessage(e, 'getTechnicalRecords', searchBy), anchorLink: 'search-term' }))), - )), - )); - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - getTechRecordErrorMessage(error: any, type: string, search?: string): string { - if (typeof error !== 'object') { - return error; - } if (error.status === 404) { - return this.apiErrors[`${type}_404`]; - } - const messageFromSearchType = search === SEARCH_TYPES.ALL ? 'the current search criteria' : search; - return `${this.apiErrors[`${type}_400`]} ${messageFromSearchType ?? JSON.stringify(error.error)}`; + private actions$ = inject(Actions); + private techRecordHttpService = inject(TechnicalRecordHttpService); - } + fetchSearchResults$ = createEffect(() => + this.actions$.pipe( + ofType(fetchSearchResult), + switchMap(({ searchBy, term }) => + this.techRecordHttpService.search$(searchBy ?? SEARCH_TYPES.ALL, term).pipe( + map((results) => fetchSearchResultSuccess({ payload: results })), + catchError((e) => + of( + fetchSearchResultFailed({ + error: this.getTechRecordErrorMessage(e, 'getTechnicalRecords', searchBy), + anchorLink: 'search-term', + }) + ) + ) + ) + ) + ) + ); - private apiErrors: Record = { - getTechnicalRecords_400: 'There was a problem getting the Tech Record by', - getTechnicalRecords_404: 'Vehicle not found, check the vehicle registration mark, trailer ID or vehicle identification number', - }; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + getTechRecordErrorMessage(error: any, type: string, search?: string): string { + if (typeof error !== 'object') { + return error; + } + if (error.status === 404) { + return this.apiErrors[`${type}_404`]; + } + const messageFromSearchType = search === SEARCH_TYPES.ALL ? 'the current search criteria' : search; + return `${this.apiErrors[`${type}_400`]} ${messageFromSearchType ?? JSON.stringify(error.error)}`; + } - constructor(private actions$: Actions, private techRecordHttpService: TechnicalRecordHttpService) {} + private apiErrors: Record = { + getTechnicalRecords_400: 'There was a problem getting the Tech Record by', + getTechnicalRecords_404: + 'Vehicle not found, check the vehicle registration mark, trailer ID or vehicle identification number', + }; } diff --git a/src/app/store/tech-record-search/reducer/tech-record-search.reducer.spec.ts b/src/app/store/tech-record-search/reducer/tech-record-search.reducer.spec.ts index d98557a741..370a99de8b 100644 --- a/src/app/store/tech-record-search/reducer/tech-record-search.reducer.spec.ts +++ b/src/app/store/tech-record-search/reducer/tech-record-search.reducer.spec.ts @@ -1,46 +1,50 @@ import { TechRecordSearchSchema } from '@dvsa/cvs-type-definitions/types/v3/tech-record/get/search'; -import { fetchSearchResult, fetchSearchResultFailed, fetchSearchResultSuccess } from '../actions/tech-record-search.actions'; +import { + fetchSearchResult, + fetchSearchResultFailed, + fetchSearchResultSuccess, +} from '../actions/tech-record-search.actions'; import { SearchResultState, initialTechSearchResultState, techSearchResultReducer } from './tech-record-search.reducer'; describe('fetchSearchResults actions', () => { - it('should set loading to true', () => { - const newState: SearchResultState = { ...initialTechSearchResultState, loading: true }; - const action = fetchSearchResult({ term: 'foo' }); - const state = techSearchResultReducer(initialTechSearchResultState, action); + it('should set loading to true', () => { + const newState: SearchResultState = { ...initialTechSearchResultState, loading: true }; + const action = fetchSearchResult({ term: 'foo' }); + const state = techSearchResultReducer(initialTechSearchResultState, action); - expect(state).toEqual(newState); - expect(state).not.toBe(newState); - }); + expect(state).toEqual(newState); + expect(state).not.toBe(newState); + }); - describe('fetchSearchResultsSuccess', () => { - it('should set all test result records', () => { - const mockSearchResult = [ - { - systemNumber: '123456', - createdTimestamp: '10-22-10', - }, - ] as TechRecordSearchSchema[]; - const newState: SearchResultState = { - ...initialTechSearchResultState, - ids: ['123456#10-22-10'], - entities: { '123456#10-22-10': mockSearchResult[0] }, - }; - const action = fetchSearchResultSuccess({ payload: mockSearchResult }); - const state = techSearchResultReducer(initialTechSearchResultState, action); + describe('fetchSearchResultsSuccess', () => { + it('should set all test result records', () => { + const mockSearchResult = [ + { + systemNumber: '123456', + createdTimestamp: '10-22-10', + }, + ] as TechRecordSearchSchema[]; + const newState: SearchResultState = { + ...initialTechSearchResultState, + ids: ['123456#10-22-10'], + entities: { '123456#10-22-10': mockSearchResult[0] }, + }; + const action = fetchSearchResultSuccess({ payload: mockSearchResult }); + const state = techSearchResultReducer(initialTechSearchResultState, action); - expect(state).toEqual(newState); - expect(state).not.toBe(newState); - }); + expect(state).toEqual(newState); + expect(state).not.toBe(newState); + }); - describe('fetchSearchResultsFailed', () => { - it('should set error state', () => { - const newState = { ...initialTechSearchResultState, loading: false, error: ':cry:' }; - const action = fetchSearchResultFailed({ error: ':cry:' }); - const state = techSearchResultReducer({ ...initialTechSearchResultState, loading: true }, action); + describe('fetchSearchResultsFailed', () => { + it('should set error state', () => { + const newState = { ...initialTechSearchResultState, loading: false, error: ':cry:' }; + const action = fetchSearchResultFailed({ error: ':cry:' }); + const state = techSearchResultReducer({ ...initialTechSearchResultState, loading: true }, action); - expect(state).toEqual(newState); - expect(state).not.toBe(newState); - }); - }); - }); + expect(state).toEqual(newState); + expect(state).not.toBe(newState); + }); + }); + }); }); diff --git a/src/app/store/tech-record-search/reducer/tech-record-search.reducer.ts b/src/app/store/tech-record-search/reducer/tech-record-search.reducer.ts index dd128b0826..311554e567 100644 --- a/src/app/store/tech-record-search/reducer/tech-record-search.reducer.ts +++ b/src/app/store/tech-record-search/reducer/tech-record-search.reducer.ts @@ -1,30 +1,40 @@ import { TechRecordSearchSchema } from '@dvsa/cvs-type-definitions/types/v3/tech-record/get/search'; import { EntityAdapter, EntityState, createEntityAdapter } from '@ngrx/entity'; import { createFeatureSelector, createReducer, on } from '@ngrx/store'; -import { fetchSearchResult, fetchSearchResultFailed, fetchSearchResultSuccess } from '../actions/tech-record-search.actions'; +import { + fetchSearchResult, + fetchSearchResultFailed, + fetchSearchResultSuccess, +} from '../actions/tech-record-search.actions'; export interface SearchResultState extends EntityState { - loading: boolean; - error: string; + loading: boolean; + error: string; } export const STORE_FEATURE_SEARCH_TECH_RESULTS_KEY = 'techSearchResults'; -export const techSearchResultFeatureState = createFeatureSelector(STORE_FEATURE_SEARCH_TECH_RESULTS_KEY); +export const techSearchResultFeatureState = createFeatureSelector( + STORE_FEATURE_SEARCH_TECH_RESULTS_KEY +); -export const techSearchResultAdapter: EntityAdapter = createEntityAdapter({ - selectId: (result) => `${result.systemNumber}#${result.createdTimestamp}`, -}); +export const techSearchResultAdapter: EntityAdapter = + createEntityAdapter({ + selectId: (result) => `${result.systemNumber}#${result.createdTimestamp}`, + }); export const initialTechSearchResultState = techSearchResultAdapter.getInitialState({ loading: false, error: '' }); export const techSearchResultReducer = createReducer( - initialTechSearchResultState, - on(fetchSearchResult, (state) => ({ ...state, loading: true })), - on(fetchSearchResultSuccess, (state, action) => ({ ...techSearchResultAdapter.setAll(action.payload, state), loading: false })), - on(fetchSearchResultFailed, (state, action) => ({ - ...techSearchResultAdapter.setAll([], state), - loading: false, - error: action.error, - })), + initialTechSearchResultState, + on(fetchSearchResult, (state) => ({ ...state, loading: true })), + on(fetchSearchResultSuccess, (state, action) => ({ + ...techSearchResultAdapter.setAll(action.payload, state), + loading: false, + })), + on(fetchSearchResultFailed, (state, action) => ({ + ...techSearchResultAdapter.setAll([], state), + loading: false, + error: action.error, + })) ); diff --git a/src/app/store/tech-record-search/selector/tech-record-search.selector.spec.ts b/src/app/store/tech-record-search/selector/tech-record-search.selector.spec.ts index 84621dea90..6d67775b3a 100644 --- a/src/app/store/tech-record-search/selector/tech-record-search.selector.spec.ts +++ b/src/app/store/tech-record-search/selector/tech-record-search.selector.spec.ts @@ -1,156 +1,163 @@ import { TechRecordSearchSchema } from '@dvsa/cvs-type-definitions/types/v3/tech-record/get/search'; import { initialTechSearchResultState } from '../reducer/tech-record-search.reducer'; -import { selectTechRecordSearchResults, selectTechRecordSearchResultsBySystemNumber } from './tech-record-search.selector'; +import { + selectTechRecordSearchResults, + selectTechRecordSearchResultsBySystemNumber, +} from './tech-record-search.selector'; describe('Tech Record Search Selectors', () => { - describe('selectTechRecordsSearchResults', () => { - it('should return the records in state', () => { - const state = { ...initialTechSearchResultState, ids: [1], entities: { 1: { systemNumber: '123' } as TechRecordSearchSchema } }; - const selectedState = selectTechRecordSearchResults.projector(state); - expect(selectedState).toEqual([{ systemNumber: '123' }]); - }); - }); - describe('selectTechRecordsSearchResultsBySystemNumber', () => { - const testCases = [ - { - results: [ - { - systemNumber: '123456', - }, - { - systemNumber: '123456', - }, - { - systemNumber: '123456', - }, - { - systemNumber: '123456', - }, - ], - }, - { - results: [ - { - systemNumber: '123456', - }, - { - systemNumber: '7545677', - }, - { - systemNumber: '123456', - }, - { - systemNumber: '12', - }, - ], - }, - { - results: [ - { - systemNumber: '4444', - }, - { - systemNumber: '4444', - }, - { - systemNumber: '123456', - }, - { - systemNumber: '12', - }, - ], - }, - ] as { results: TechRecordSearchSchema[] }[]; + describe('selectTechRecordsSearchResults', () => { + it('should return the records in state', () => { + const state = { + ...initialTechSearchResultState, + ids: [1], + entities: { 1: { systemNumber: '123' } as TechRecordSearchSchema }, + }; + const selectedState = selectTechRecordSearchResults.projector(state); + expect(selectedState).toEqual([{ systemNumber: '123' }]); + }); + }); + describe('selectTechRecordsSearchResultsBySystemNumber', () => { + const testCases = [ + { + results: [ + { + systemNumber: '123456', + }, + { + systemNumber: '123456', + }, + { + systemNumber: '123456', + }, + { + systemNumber: '123456', + }, + ], + }, + { + results: [ + { + systemNumber: '123456', + }, + { + systemNumber: '7545677', + }, + { + systemNumber: '123456', + }, + { + systemNumber: '12', + }, + ], + }, + { + results: [ + { + systemNumber: '4444', + }, + { + systemNumber: '4444', + }, + { + systemNumber: '123456', + }, + { + systemNumber: '12', + }, + ], + }, + ] as { results: TechRecordSearchSchema[] }[]; - it.each(testCases)('should group the search results by systemNumber', ({ results }) => { - const selectedState = selectTechRecordSearchResultsBySystemNumber.projector(results); - const expectedLength = new Set(results.map((r) => r.systemNumber)).size; - expect(selectedState).toHaveLength(expectedLength); - }); - const now = new Date(); - const oneDayAgo = new Date(); - oneDayAgo.setDate(new Date().getDate() - 1); - const twoDaysAgo = new Date(); - twoDaysAgo.setDate(new Date().getDate() - 2); - const statusCases = [ - { - results: [ - { - systemNumber: '123456', - techRecord_statusCode: 'current', - }, - { - systemNumber: '123456', - }, - { - systemNumber: '123456', - }, - { - systemNumber: '123456', - }, - ], - status: 'current', - }, - { - results: [ - { - systemNumber: '123456', - }, - { - systemNumber: '123456', - }, - { - systemNumber: '123456', - techRecord_statusCode: 'provisional', - }, - { - systemNumber: '123456', - }, - ], + it.each(testCases)('should group the search results by systemNumber', ({ results }) => { + const selectedState = selectTechRecordSearchResultsBySystemNumber.projector(results); + const expectedLength = new Set(results.map((r) => r.systemNumber)).size; + expect(selectedState).toHaveLength(expectedLength); + }); + const now = new Date(); + const oneDayAgo = new Date(); + oneDayAgo.setDate(new Date().getDate() - 1); + const twoDaysAgo = new Date(); + twoDaysAgo.setDate(new Date().getDate() - 2); + const statusCases = [ + { + results: [ + { + systemNumber: '123456', + techRecord_statusCode: 'current', + }, + { + systemNumber: '123456', + }, + { + systemNumber: '123456', + }, + { + systemNumber: '123456', + }, + ], + status: 'current', + }, + { + results: [ + { + systemNumber: '123456', + }, + { + systemNumber: '123456', + }, + { + systemNumber: '123456', + techRecord_statusCode: 'provisional', + }, + { + systemNumber: '123456', + }, + ], - status: 'provisional', - }, - { - results: [ - { - systemNumber: '123456', - }, - { - systemNumber: '123456', - techRecord_statusCode: 'current', - }, - { - systemNumber: '123456', - techRecord_statusCode: 'provisional', - }, - { - systemNumber: '123456', - }, - ], - status: 'current', - }, - { - results: [ - { - systemNumber: '123456', - createdTimestamp: twoDaysAgo.toISOString(), - }, - { - systemNumber: '123456', - createdTimestamp: oneDayAgo.toISOString(), - }, - { - systemNumber: '123456', - createdTimestamp: now.toISOString(), - techRecord_statusCode: 'this is the right record', - }, - ], - status: 'this is the right record', - }, - ] as { results: TechRecordSearchSchema[]; status: string }[]; - it.each(statusCases)('should group the search results by systemNumber', ({ results, status }) => { - const selectedState = selectTechRecordSearchResultsBySystemNumber.projector(results); - expect(selectedState[0].techRecord_statusCode).toBe(status); - }); - }); + status: 'provisional', + }, + { + results: [ + { + systemNumber: '123456', + }, + { + systemNumber: '123456', + techRecord_statusCode: 'current', + }, + { + systemNumber: '123456', + techRecord_statusCode: 'provisional', + }, + { + systemNumber: '123456', + }, + ], + status: 'current', + }, + { + results: [ + { + systemNumber: '123456', + createdTimestamp: twoDaysAgo.toISOString(), + }, + { + systemNumber: '123456', + createdTimestamp: oneDayAgo.toISOString(), + }, + { + systemNumber: '123456', + createdTimestamp: now.toISOString(), + techRecord_statusCode: 'this is the right record', + }, + ], + status: 'this is the right record', + }, + ] as { results: TechRecordSearchSchema[]; status: string }[]; + it.each(statusCases)('should group the search results by systemNumber', ({ results, status }) => { + const selectedState = selectTechRecordSearchResultsBySystemNumber.projector(results); + expect(selectedState[0].techRecord_statusCode).toBe(status); + }); + }); }); diff --git a/src/app/store/tech-record-search/selector/tech-record-search.selector.ts b/src/app/store/tech-record-search/selector/tech-record-search.selector.ts index 6aa59b2f82..81e75ac834 100644 --- a/src/app/store/tech-record-search/selector/tech-record-search.selector.ts +++ b/src/app/store/tech-record-search/selector/tech-record-search.selector.ts @@ -4,26 +4,35 @@ import { techSearchResultAdapter, techSearchResultFeatureState } from '../reduce const { selectAll } = techSearchResultAdapter.getSelectors(); -export const selectTechRecordSearchLoadingState = createSelector(techSearchResultFeatureState, (state) => state.loading); +export const selectTechRecordSearchLoadingState = createSelector( + techSearchResultFeatureState, + (state) => state.loading +); export const selectTechRecordSearchResults = createSelector(techSearchResultFeatureState, (state) => selectAll(state)); -export const selectTechRecordSearchResultsBySystemNumber = createSelector(selectTechRecordSearchResults, (searchResults) => { - const records: TechRecordSearchSchema[] = []; - const visitedSystemNumbers = new Set(); - searchResults.forEach((result) => { - if (!visitedSystemNumbers.has(result.systemNumber)) { - visitedSystemNumbers.add(result.systemNumber); - const recordsWithSameSystemNumber = searchResults.filter((record) => record.systemNumber === result.systemNumber); - const mostCurrentRecord = recordsWithSameSystemNumber.find((r) => r.techRecord_statusCode === 'current') - ?? recordsWithSameSystemNumber.find((r) => r.techRecord_statusCode === 'provisional') - ?? recordsWithSameSystemNumber.find( - (r) => - new Date(r.createdTimestamp).getTime() - === Math.max(...recordsWithSameSystemNumber.map((rec) => new Date(rec.createdTimestamp).getTime())), - ) - ?? records[0]; - records.push(mostCurrentRecord); - } - }); - return records; -}); +export const selectTechRecordSearchResultsBySystemNumber = createSelector( + selectTechRecordSearchResults, + (searchResults) => { + const records: TechRecordSearchSchema[] = []; + const visitedSystemNumbers = new Set(); + searchResults.forEach((result) => { + if (!visitedSystemNumbers.has(result.systemNumber)) { + visitedSystemNumbers.add(result.systemNumber); + const recordsWithSameSystemNumber = searchResults.filter( + (record) => record.systemNumber === result.systemNumber + ); + const mostCurrentRecord = + recordsWithSameSystemNumber.find((r) => r.techRecord_statusCode === 'current') ?? + recordsWithSameSystemNumber.find((r) => r.techRecord_statusCode === 'provisional') ?? + recordsWithSameSystemNumber.find( + (r) => + new Date(r.createdTimestamp).getTime() === + Math.max(...recordsWithSameSystemNumber.map((rec) => new Date(rec.createdTimestamp).getTime())) + ) ?? + records[0]; + records.push(mostCurrentRecord); + } + }); + return records; + } +); diff --git a/src/app/store/tech-record-search/tech-record-search-state.module.ts b/src/app/store/tech-record-search/tech-record-search-state.module.ts index 7a0fbc05da..82f8711cdb 100644 --- a/src/app/store/tech-record-search/tech-record-search-state.module.ts +++ b/src/app/store/tech-record-search/tech-record-search-state.module.ts @@ -2,15 +2,15 @@ import { CommonModule } from '@angular/common'; import { NgModule } from '@angular/core'; import { EffectsModule } from '@ngrx/effects'; import { StoreModule } from '@ngrx/store'; -import { STORE_FEATURE_SEARCH_TECH_RESULTS_KEY, techSearchResultReducer } from './reducer/tech-record-search.reducer'; import { TechSearchResultsEffects } from './effects/tech-record-search.effect'; +import { STORE_FEATURE_SEARCH_TECH_RESULTS_KEY, techSearchResultReducer } from './reducer/tech-record-search.reducer'; @NgModule({ - declarations: [], - imports: [ - CommonModule, - StoreModule.forFeature(STORE_FEATURE_SEARCH_TECH_RESULTS_KEY, techSearchResultReducer), - EffectsModule.forFeature([TechSearchResultsEffects]), - ], + declarations: [], + imports: [ + CommonModule, + StoreModule.forFeature(STORE_FEATURE_SEARCH_TECH_RESULTS_KEY, techSearchResultReducer), + EffectsModule.forFeature([TechSearchResultsEffects]), + ], }) export class TechRecordSearchStateModule {} diff --git a/src/app/store/technical-records/actions/batch-create.actions.ts b/src/app/store/technical-records/actions/batch-create.actions.ts index 0f1aa96b67..ae41cd0a99 100644 --- a/src/app/store/technical-records/actions/batch-create.actions.ts +++ b/src/app/store/technical-records/actions/batch-create.actions.ts @@ -2,12 +2,24 @@ import { VehicleTypes } from '@models/vehicle-tech-record.model'; import { createAction, props } from '@ngrx/store'; export const upsertVehicleBatch = createAction( - '[Technical Record Batch Create] upsert many', - props<{ vehicles: Array<{ vin: string; trailerId?: string; primaryVrm?: string }> }>(), + '[Technical Record Batch Create] upsert many', + props<{ vehicles: Array<{ vin: string; trailerId?: string; primaryVrm?: string }> }>() ); -export const setGenerateNumberFlag = createAction('[Technical Record Batch Create] set generate number', props<{ generateNumber: boolean }>()); -export const setApplicationId = createAction('[Technical Record Batch Create] set batch ID', props<{ applicationId: string }>()); -export const setVehicleStatus = createAction('[Technical Record Batch Create] set record status', props<{ vehicleStatus: string }>()); -export const setVehicleType = createAction('[Technical Record Batch Create] set batch vehicle type', props<{ vehicleType: VehicleTypes }>()); +export const setGenerateNumberFlag = createAction( + '[Technical Record Batch Create] set generate number', + props<{ generateNumber: boolean }>() +); +export const setApplicationId = createAction( + '[Technical Record Batch Create] set batch ID', + props<{ applicationId: string }>() +); +export const setVehicleStatus = createAction( + '[Technical Record Batch Create] set record status', + props<{ vehicleStatus: string }>() +); +export const setVehicleType = createAction( + '[Technical Record Batch Create] set batch vehicle type', + props<{ vehicleType: VehicleTypes }>() +); export const clearBatch = createAction('[Technical Record Batch Create] clear all'); diff --git a/src/app/store/technical-records/actions/technical-record-service.actions.spec.ts b/src/app/store/technical-records/actions/technical-record-service.actions.spec.ts index efeebae44b..e3bb907ffc 100644 --- a/src/app/store/technical-records/actions/technical-record-service.actions.spec.ts +++ b/src/app/store/technical-records/actions/technical-record-service.actions.spec.ts @@ -1,63 +1,63 @@ import { - archiveTechRecord, - archiveTechRecordFailure, - archiveTechRecordSuccess, - changeVehicleType, - clearADRDetailsBeforeUpdate, - createVehicle, - createVehicleRecord, - createVehicleRecordFailure, - createVehicleRecordSuccess, - getBySystemNumber, - getBySystemNumberFailure, - getBySystemNumberSuccess, - unarchiveTechRecord, - unarchiveTechRecordFailure, - unarchiveTechRecordSuccess, - updateEditingTechRecord, - updateEditingTechRecordCancel, - updateTechRecord, - updateTechRecordFailure, - updateTechRecordSuccess, + archiveTechRecord, + archiveTechRecordFailure, + archiveTechRecordSuccess, + changeVehicleType, + clearADRDetailsBeforeUpdate, + createVehicle, + createVehicleRecord, + createVehicleRecordFailure, + createVehicleRecordSuccess, + getBySystemNumber, + getBySystemNumberFailure, + getBySystemNumberSuccess, + unarchiveTechRecord, + unarchiveTechRecordFailure, + unarchiveTechRecordSuccess, + updateEditingTechRecord, + updateEditingTechRecordCancel, + updateTechRecord, + updateTechRecordFailure, + updateTechRecordSuccess, } from './technical-record-service.actions'; const SUCCESS = ' Success'; const FAILURE = ' Failure'; describe('Technical record actions', () => { - it('should have correct types', () => { - expect(getBySystemNumber.type).toBe(getMessage('getBySystemNumber')); - expect(getBySystemNumberSuccess.type).toBe(getMessage('getBySystemNumber', SUCCESS)); - expect(getBySystemNumberFailure.type).toBe(getMessage('getBySystemNumber', FAILURE)); + it('should have correct types', () => { + expect(getBySystemNumber.type).toBe(getMessage('getBySystemNumber')); + expect(getBySystemNumberSuccess.type).toBe(getMessage('getBySystemNumber', SUCCESS)); + expect(getBySystemNumberFailure.type).toBe(getMessage('getBySystemNumber', FAILURE)); - expect(createVehicleRecord.type).toBe(getMessage('createVehicleRecord')); - expect(createVehicleRecordSuccess.type).toBe(getMessage('createVehicleRecord', SUCCESS)); - expect(createVehicleRecordFailure.type).toBe(getMessage('createVehicleRecord', FAILURE)); + expect(createVehicleRecord.type).toBe(getMessage('createVehicleRecord')); + expect(createVehicleRecordSuccess.type).toBe(getMessage('createVehicleRecord', SUCCESS)); + expect(createVehicleRecordFailure.type).toBe(getMessage('createVehicleRecord', FAILURE)); - expect(updateTechRecord.type).toBe(getMessage('updateTechRecords')); - expect(updateTechRecordSuccess.type).toBe(getMessage('updateTechRecords', SUCCESS)); - expect(updateTechRecordFailure.type).toBe(getMessage('updateTechRecords', FAILURE)); + expect(updateTechRecord.type).toBe(getMessage('updateTechRecords')); + expect(updateTechRecordSuccess.type).toBe(getMessage('updateTechRecords', SUCCESS)); + expect(updateTechRecordFailure.type).toBe(getMessage('updateTechRecords', FAILURE)); - expect(archiveTechRecord.type).toBe(getMessage('archiveTechRecord')); - expect(archiveTechRecordSuccess.type).toBe(getMessage('archiveTechRecord', SUCCESS)); - expect(archiveTechRecordFailure.type).toBe(getMessage('archiveTechRecord', FAILURE)); + expect(archiveTechRecord.type).toBe(getMessage('archiveTechRecord')); + expect(archiveTechRecordSuccess.type).toBe(getMessage('archiveTechRecord', SUCCESS)); + expect(archiveTechRecordFailure.type).toBe(getMessage('archiveTechRecord', FAILURE)); - expect(updateEditingTechRecord.type).toBe(getMessage('updateEditingTechRecord')); + expect(updateEditingTechRecord.type).toBe(getMessage('updateEditingTechRecord')); - expect(updateEditingTechRecordCancel.type).toBe(getMessage('updateEditingTechRecordCancel')); + expect(updateEditingTechRecordCancel.type).toBe(getMessage('updateEditingTechRecordCancel')); - expect(changeVehicleType.type).toBe(getMessage('changeVehicleType')); + expect(changeVehicleType.type).toBe(getMessage('changeVehicleType')); - expect(createVehicle.type).toBe(getMessage('createVehicle')); + expect(createVehicle.type).toBe(getMessage('createVehicle')); - expect(unarchiveTechRecord.type).toBe(getMessage('unarchiveTechRecord')); - expect(unarchiveTechRecordFailure.type).toBe(getMessage('unarchiveTechRecord', FAILURE)); - expect(unarchiveTechRecordSuccess.type).toBe(getMessage('unarchiveTechRecord', SUCCESS)); + expect(unarchiveTechRecord.type).toBe(getMessage('unarchiveTechRecord')); + expect(unarchiveTechRecordFailure.type).toBe(getMessage('unarchiveTechRecord', FAILURE)); + expect(unarchiveTechRecordSuccess.type).toBe(getMessage('unarchiveTechRecord', SUCCESS)); - expect(clearADRDetailsBeforeUpdate.type).toBe(getMessage('clearADRDetailsBeforeUpdate')); - }); + expect(clearADRDetailsBeforeUpdate.type).toBe(getMessage('clearADRDetailsBeforeUpdate')); + }); }); function getMessage(title: string, suffix = '') { - return `[Technical Record Service] ${title}${suffix}`; + return `[Technical Record Service] ${title}${suffix}`; } diff --git a/src/app/store/technical-records/actions/technical-record-service.actions.ts b/src/app/store/technical-records/actions/technical-record-service.actions.ts index 3e70e03b7b..67876e8f48 100644 --- a/src/app/store/technical-records/actions/technical-record-service.actions.ts +++ b/src/app/store/technical-records/actions/technical-record-service.actions.ts @@ -3,65 +3,83 @@ import { TechRecordSearchSchema } from '@dvsa/cvs-type-definitions/types/v3/tech import { TechRecordType } from '@dvsa/cvs-type-definitions/types/v3/tech-record/tech-record-verb'; import { PsvMake } from '@models/reference-data.model'; import { VehicleTypes } from '@models/vehicle-tech-record.model'; -import { - ActionCreator, ActionCreatorProps, createAction, props, -} from '@ngrx/store'; +import { Action, ActionCreator, ActionCreatorProps, createAction, props } from '@ngrx/store'; // eslint-disable-next-line import/no-unresolved -import { TypedAction } from '@ngrx/store/src/models'; const prefix = '[Technical Record Service]'; export const getBySystemNumber = createAction(`${prefix} getBySystemNumber`, props<{ systemNumber: string }>()); -export const getBySystemNumberSuccess = createAction(`${prefix} getBySystemNumber Success`, props<{ techRecordHistory: TechRecordSearchSchema[] }>()); +export const getBySystemNumberSuccess = createAction( + `${prefix} getBySystemNumber Success`, + props<{ techRecordHistory: TechRecordSearchSchema[] }>() +); export const getBySystemNumberFailure = createAction(`${prefix} getBySystemNumber Failure`, props()); -export const getTechRecordV3 = createAction(`${prefix} getTechRecordV3`, props<{ systemNumber: string; createdTimestamp: string }>()); +export const getTechRecordV3 = createAction( + `${prefix} getTechRecordV3`, + props<{ systemNumber: string; createdTimestamp: string }>() +); export const getTechRecordV3Success = createOutcomeAction('getTechRecordV3', true); export const getTechRecordV3Failure = createOutcomeAction('getTechRecordV3', false); -export const createVehicleRecord = createAction(`${prefix} createVehicleRecord`, props<{ vehicle: TechRecordType<'put'> }>()); +export const createVehicleRecord = createAction( + `${prefix} createVehicleRecord`, + props<{ vehicle: TechRecordType<'put'> }>() +); export const createVehicleRecordSuccess = createOutcomeAction('createVehicleRecord', true); export const createVehicleRecordFailure = createOutcomeAction('createVehicleRecord', false); export const updateTechRecord = createAction( - `${prefix} updateTechRecords`, - props<{ systemNumber: string; createdTimestamp: string }>(), + `${prefix} updateTechRecords`, + props<{ systemNumber: string; createdTimestamp: string }>() ); export const updateTechRecordSuccess = createOutcomeAction('updateTechRecords', true); export const updateTechRecordFailure = createOutcomeAction('updateTechRecords', false); export const amendVin = createAction( - `${prefix} amendVin`, - props<{ newVin: string; systemNumber: string; createdTimestamp: string }>(), + `${prefix} amendVin`, + props<{ newVin: string; systemNumber: string; createdTimestamp: string }>() ); export const amendVinSuccess = createOutcomeAction('amendVin', true); export const amendVinFailure = createOutcomeAction('amendVin', false); export const amendVrm = createAction( - `${prefix} amendVrm`, - props<{ newVrm: string; cherishedTransfer: boolean; systemNumber: string; createdTimestamp: string; thirdMark?: string }>(), + `${prefix} amendVrm`, + props<{ + newVrm: string; + cherishedTransfer: boolean; + systemNumber: string; + createdTimestamp: string; + thirdMark?: string; + }>() ); export const amendVrmSuccess = createOutcomeAction('amendVrm', true); export const amendVrmFailure = createOutcomeAction('amendVrm', false); export const archiveTechRecord = createAction( - `${prefix} archiveTechRecord`, - props<{ systemNumber: string; createdTimestamp: string; reasonForArchiving: string }>(), + `${prefix} archiveTechRecord`, + props<{ systemNumber: string; createdTimestamp: string; reasonForArchiving: string }>() ); export const archiveTechRecordSuccess = createOutcomeAction('archiveTechRecord', true); export const archiveTechRecordFailure = createOutcomeAction('archiveTechRecord', false); export const promoteTechRecord = createAction( - `${prefix} promoteTechRecord`, - props<{ systemNumber: string; createdTimestamp: string; reasonForPromoting: string }>(), + `${prefix} promoteTechRecord`, + props<{ systemNumber: string; createdTimestamp: string; reasonForPromoting: string }>() ); export const promoteTechRecordSuccess = createOutcomeAction('promoteTechRecord', true); export const promoteTechRecordFailure = createOutcomeAction('promoteTechRecord', false); -export const updateEditingTechRecord = createAction(`${prefix} updateEditingTechRecord`, props<{ vehicleTechRecord: TechRecordType<'put'> }>()); +export const updateEditingTechRecord = createAction( + `${prefix} updateEditingTechRecord`, + props<{ vehicleTechRecord: TechRecordType<'put'> }>() +); export const updateEditingTechRecordCancel = createAction(`${prefix} updateEditingTechRecordCancel`); -export const changeVehicleType = createAction(`${prefix} changeVehicleType`, props<{ techRecord_vehicleType: VehicleTypes }>()); +export const changeVehicleType = createAction( + `${prefix} changeVehicleType`, + props<{ techRecord_vehicleType: VehicleTypes }>() +); export const createVehicle = createAction(`${prefix} createVehicle`, props<{ techRecord_vehicleType: VehicleTypes }>()); @@ -71,17 +89,23 @@ export const generatePlateFailure = createOutcomeAction('generatePlate', false); export const canGeneratePlate = createAction(`${prefix} canGeneratePlate`); export const cannotGeneratePlate = createAction(`${prefix} cannotGeneratePlate`); -export const generateLetter = createAction(`${prefix} generateLetter`, props<{ letterType: string; paragraphId: number }>()); +export const generateLetter = createAction( + `${prefix} generateLetter`, + props<{ letterType: string; paragraphId: number }>() +); export const generateLetterSuccess = createAction(`${prefix} generateLetter Success`); export const generateLetterFailure = createOutcomeAction('generateLetter', false); -export const updateBrakeForces = createAction(`${prefix} updateBrakesForces`, props<{ grossLadenWeight?: number; grossKerbWeight?: number }>()); +export const updateBrakeForces = createAction( + `${prefix} updateBrakesForces`, + props<{ grossLadenWeight?: number; grossKerbWeight?: number }>() +); export const updateBody = createAction(`${prefix} updatebody`, props<{ psvMake: PsvMake }>()); export const unarchiveTechRecord = createAction( - `${prefix} unarchiveTechRecord`, - props<{ systemNumber: string; createdTimestamp: string; reasonForUnarchiving: string; status: string }>(), + `${prefix} unarchiveTechRecord`, + props<{ systemNumber: string; createdTimestamp: string; reasonForUnarchiving: string; status: string }>() ); export const unarchiveTechRecordSuccess = createOutcomeAction('unarchiveTechRecord', true); export const unarchiveTechRecordFailure = createOutcomeAction('unarchiveTechRecord', false); @@ -93,42 +117,65 @@ export const addSectionState = createAction(`${prefix} addSectionState`, props<{ export const removeSectionState = createAction(`${prefix} removeSectionState`, props<{ section: string | number }>()); export const clearAllSectionStates = createAction(`${prefix} clearAllSectionState`); -export const updateScrollPosition = createAction(`${prefix} updateScrollPosition`, props<{ position: [number, number] }>()); +export const updateScrollPosition = createAction( + `${prefix} updateScrollPosition`, + props<{ position: [number, number] }>() +); export const clearScrollPosition = createAction(`${prefix} clearScrollPosition`); export const clearADRDetailsBeforeUpdate = createAction(`${prefix} clearADRDetailsBeforeUpdate`); -export const updateADRAdditionalExaminerNotes = createAction(`${prefix} updateADRAdditionalExaminerNotes`, props<{ username: string }>()); +export const updateADRAdditionalExaminerNotes = createAction( + `${prefix} updateADRAdditionalExaminerNotes`, + props<{ username: string }>() +); export const updateExistingADRAdditionalExaminerNote = createAction( - `${prefix} updateExistingADRAdditionalExaminerNote`, - props<{ additionalExaminerNote: string, examinerNoteIndex: number }>(), + `${prefix} updateExistingADRAdditionalExaminerNote`, + props<{ additionalExaminerNote: string; examinerNoteIndex: number }>() ); -export const generateADRCertificate = createAction(`${prefix} generateADRCertificate`, props<{ - systemNumber: string, createdTimestamp: string, certificateType: string -}>()); -export const generateADRCertificateSuccess = createAction(`${prefix} generateADRCertificate Success`, props<{ id: string }>()); +export const generateADRCertificate = createAction( + `${prefix} generateADRCertificate`, + props<{ + systemNumber: string; + createdTimestamp: string; + certificateType: string; + }>() +); +export const generateADRCertificateSuccess = createAction( + `${prefix} generateADRCertificate Success`, + props<{ id: string }>() +); export const generateADRCertificateFailure = createOutcomeAction('generateADRCertificate', false); -export const generateContingencyADRCertificate = createAction(`${prefix} generateContingencyADRCertificate`, props<{ - systemNumber: string, createdTimestamp: string, certificateType: string -}>()); +export const generateContingencyADRCertificate = createAction( + `${prefix} generateContingencyADRCertificate`, + props<{ + systemNumber: string; + createdTimestamp: string; + certificateType: string; + }>() +); function createOutcomeAction( - title: string, - isSuccess: T, + title: string, + isSuccess: T ): ActionCreator< - string, - T extends false - ? (props: GlobalError) => GlobalError & TypedAction - : (props: { vehicleTechRecord: TechRecordType<'get'> }) => { vehicleTechRecord: TechRecordType<'get'> } & TypedAction - > { - const suffix = isSuccess ? 'Success' : 'Failure'; - const type = `${prefix} ${title} ${suffix}`; - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const actionCreator: ActionCreatorProps = isSuccess ? props<{ vehicleTechRecord: TechRecordType<'get'>[] }>() : props(); - - return createAction(type, actionCreator); + string, + T extends false + ? (props: GlobalError) => GlobalError & Action + : (props: { vehicleTechRecord: TechRecordType<'get'> }) => { + vehicleTechRecord: TechRecordType<'get'>; + } & Action +> { + const suffix = isSuccess ? 'Success' : 'Failure'; + const type = `${prefix} ${title} ${suffix}`; + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const actionCreator: ActionCreatorProps = isSuccess + ? props<{ vehicleTechRecord: TechRecordType<'get'>[] }>() + : props(); + + return createAction(type, actionCreator); } diff --git a/src/app/store/technical-records/effects/technical-record-service.effects.spec.ts b/src/app/store/technical-records/effects/technical-record-service.effects.spec.ts index 5dfc9f7ced..5a320064f9 100644 --- a/src/app/store/technical-records/effects/technical-record-service.effects.spec.ts +++ b/src/app/store/technical-records/effects/technical-record-service.effects.spec.ts @@ -1,6 +1,6 @@ import { HttpErrorResponse } from '@angular/common/http'; import { HttpClientTestingModule } from '@angular/common/http/testing'; -import { fakeAsync, flush, TestBed } from '@angular/core/testing'; +import { TestBed, fakeAsync, flush } from '@angular/core/testing'; import { RouterTestingModule } from '@angular/router/testing'; import { TechRecordType } from '@dvsa/cvs-type-definitions/types/v3/tech-record/tech-record-verb'; import { V3TechRecordModel, VehicleTypes } from '@models/vehicle-tech-record.model'; @@ -10,389 +10,419 @@ import { MockStore, provideMockStore } from '@ngrx/store/testing'; import { TechnicalRecordHttpService } from '@services/technical-record-http/technical-record-http.service'; import { TechnicalRecordService } from '@services/technical-record/technical-record.service'; import { UserService } from '@services/user-service/user-service'; -import { initialAppState, State } from '@store/index'; +import { State, initialAppState } from '@store/index'; import { Observable, of } from 'rxjs'; import { TestScheduler } from 'rxjs/testing'; import { - archiveTechRecord, - archiveTechRecordFailure, - archiveTechRecordSuccess, - changeVehicleType, - createVehicleRecord, - createVehicleRecordFailure, - createVehicleRecordSuccess, - unarchiveTechRecord, - unarchiveTechRecordFailure, - unarchiveTechRecordSuccess, - updateTechRecord, - updateTechRecordFailure, - updateTechRecordSuccess, + archiveTechRecord, + archiveTechRecordFailure, + archiveTechRecordSuccess, + changeVehicleType, + createVehicleRecord, + createVehicleRecordFailure, + createVehicleRecordSuccess, + unarchiveTechRecord, + unarchiveTechRecordFailure, + unarchiveTechRecordSuccess, + updateTechRecord, + updateTechRecordFailure, + updateTechRecordSuccess, } from '../actions/technical-record-service.actions'; import { editingTechRecord } from '../selectors/technical-record-service.selectors'; import { TechnicalRecordServiceEffects } from './technical-record-service.effects'; describe('TechnicalRecordServiceEffects', () => { - let actions$ = new Observable(); - let effects: TechnicalRecordServiceEffects; - let store: MockStore; - let techRecordHttpService: TechnicalRecordHttpService; - let testScheduler: TestScheduler; - let technicalRecordService: TechnicalRecordService; - - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [HttpClientTestingModule, RouterTestingModule], - providers: [ - TechnicalRecordServiceEffects, - provideMockActions(() => actions$), - provideMockStore({ initialState: initialAppState }), - { provide: UserService, useValue: { name$: of('name'), id$: of('iod') } }, - { provide: TechnicalRecordService, useValue: { updateEditingTechRecord: jest.fn() } }, - ], - }); - effects = TestBed.inject(TechnicalRecordServiceEffects); - techRecordHttpService = TestBed.inject(TechnicalRecordHttpService); - technicalRecordService = TestBed.inject(TechnicalRecordService); - - }); - - beforeEach(() => { - testScheduler = new TestScheduler((actual, expected) => expect(actual).toEqual(expected)); - }); - - describe('createVehicleRecord', () => { - it('should return a vehicle on successful API call', () => { - testScheduler.run(({ hot, cold, expectObservable }) => { - const mockVehicle = { systemNumber: 'foo', createdTimestamp: 'bar', vin: 'testVin' }; - const expectedVehicle = { - ...mockVehicle, - } as TechRecordType<'get'>; - - // mock action to trigger effect - actions$ = hot('-a--', { - a: createVehicleRecord({ vehicle: { systemNumber: 'foo', createdTimestamp: 'bar', vin: 'testVin' } as unknown as TechRecordType<'put'> }), - }); - - // mock service call - jest.spyOn(techRecordHttpService, 'createVehicleRecord$').mockReturnValue(cold('--a|', { a: expectedVehicle })); - - // expect effect to return success action - expectObservable(effects.createVehicleRecord$).toBe('---b', { - b: createVehicleRecordSuccess({ vehicleTechRecord: expectedVehicle }), - }); - }); - }); - - it('should return an error message if not created', () => { - testScheduler.run(({ hot, cold, expectObservable }) => { - // mock action to trigger effect - actions$ = hot('-a--', { - a: createVehicleRecord({ vehicle: { systemNumber: 'foo', createdTimestamp: 'bar', vin: 'testVin' } as unknown as TechRecordType<'put'> }), - }); - - // mock service call - const expectedError = new HttpErrorResponse({ status: 500, statusText: 'Internal server error' }); - - jest.spyOn(techRecordHttpService, 'createVehicleRecord$').mockReturnValue(cold('--#|', {}, expectedError)); - - expectObservable(effects.createVehicleRecord$).toBe('---b', { - b: createVehicleRecordFailure({ error: 'Unable to create vehicle with VIN testVin' }), - }); - }); - }); - }); - - describe('updateTechRecords$', () => { - beforeEach(() => { - store = TestBed.inject(MockStore); - store.overrideSelector(editingTechRecord, {} as unknown as TechRecordType<'put'>); - }); - it('should return a technical record on successful API call', () => { - testScheduler.run(({ hot, cold, expectObservable }) => { - const technicalRecord = { systemNumber: 'foo', createdTimestamp: 'bar', vin: 'testVin' } as TechRecordType<'get'>; - - // mock action to trigger effect - actions$ = hot('-a--', { a: updateTechRecord }); - - // mock service call - jest.spyOn(techRecordHttpService, 'updateTechRecords$').mockReturnValue(cold('--a|', { a: technicalRecord })); - - // expect effect to return success action - expectObservable(effects.updateTechRecord$).toBe('---b', { - b: updateTechRecordSuccess({ vehicleTechRecord: technicalRecord }), - }); - }); - }); - - it('should return an error message if not updated', () => { - testScheduler.run(({ hot, cold, expectObservable }) => { - // mock action to trigger effect - actions$ = hot('-a--', { a: updateTechRecord }); - - // mock service call - const expectedError = new HttpErrorResponse({ status: 500, statusText: 'Internal server error' }); - jest.spyOn(techRecordHttpService, 'updateTechRecords$').mockReturnValue(cold('--#|', {}, expectedError)); - - expectObservable(effects.updateTechRecord$).toBe('---b', { - b: updateTechRecordFailure({ - error: 'Unable to update technical record null', - }), - }); - }); - }); - }); - - describe('archiveTechRecord', () => { - it('should return an archived technical record on successful API call', () => { - testScheduler.run(({ hot, cold, expectObservable }) => { - const technicalRecord = { systemNumber: 'foo', createdTimestamp: 'bar', vin: 'testVin' } as TechRecordType<'get'>; - - // mock action to trigger effect - actions$ = hot('-a--', { a: archiveTechRecord }); - - // mock service call - jest.spyOn(techRecordHttpService, 'archiveTechnicalRecord$').mockReturnValue(cold('--a|', { a: technicalRecord })); - - // expect effect to return success action - expectObservable(effects.archiveTechRecord$).toBe('---b', { - b: archiveTechRecordSuccess({ vehicleTechRecord: technicalRecord }), - }); - }); - }); - - it.each([ - [500, 'Internal server error'], - [400, 'You are not allowed to update an archived tech-record'], - ])('should return an error message if not found', () => { - testScheduler.run(({ hot, cold, expectObservable }) => { - // mock action to trigger effect - actions$ = hot('-a--', { a: archiveTechRecord }); - - // mock service call - const expectedError = new HttpErrorResponse({ status: 500, statusText: 'Internal server error' }); - jest.spyOn(techRecordHttpService, 'archiveTechnicalRecord$').mockReturnValue(cold('--#|', {}, expectedError)); - - expectObservable(effects.archiveTechRecord$).toBe('---b', { - b: archiveTechRecordFailure({ - error: 'Unable to archive technical record null', - }), - }); - }); - }); - }); - - describe('unarchiveTechRecord', () => { - it('should return an unarchived technical record on successful API call', () => { - testScheduler.run(({ hot, cold, expectObservable }) => { - const technicalRecord = { systemNumber: 'foo', createdTimestamp: 'bar', vin: 'testVin' } as TechRecordType<'get'>; - - // mock action to trigger effect - actions$ = hot('-a--', { a: unarchiveTechRecord }); - - // mock service call - jest.spyOn(techRecordHttpService, 'unarchiveTechnicalRecord$').mockReturnValue(cold('--a|', { a: technicalRecord })); - - // expect effect to return success action - expectObservable(effects.unarchiveTechRecord$).toBe('---b', { - b: unarchiveTechRecordSuccess({ vehicleTechRecord: technicalRecord }), - }); - }); - }); - - it('should return an error message if not there is a non-archived record with the same VRM', () => { - testScheduler.run(({ hot, cold, expectObservable }) => { - // mock action to trigger effect - actions$ = hot('-a--', { a: unarchiveTechRecord }); - - // mock service call - const expectedError = new HttpErrorResponse({ status: 400, statusText: 'Cannot archive a record with unarchived records' }); - jest.spyOn(techRecordHttpService, 'unarchiveTechnicalRecord$').mockReturnValue(cold('--#|', {}, expectedError)); - - expectObservable(effects.unarchiveTechRecord$).toBe('---b', { - b: unarchiveTechRecordFailure({ - error: 'Unable to unarchive technical record null', - }), - }); - }); - }); - - it('should return an error message if there was an internal server error', () => { - testScheduler.run(({ hot, cold, expectObservable }) => { - // mock action to trigger effect - actions$ = hot('-a--', { a: unarchiveTechRecord }); - - // mock service call - const expectedError = new HttpErrorResponse({ status: 500, statusText: 'Failed to unarchive record' }); - jest.spyOn(techRecordHttpService, 'unarchiveTechnicalRecord$').mockReturnValue(cold('--#|', {}, expectedError)); - - expectObservable(effects.unarchiveTechRecord$).toBe('---b', { - b: unarchiveTechRecordFailure({ - error: 'Unable to unarchive technical record null', - }), - }); - }); - }); - }); - - describe('generateTechRecordBasedOnSectionTemplates', () => { - beforeEach(() => { - store = TestBed.inject(MockStore); - store.resetSelectors(); - jest.resetModules(); - }); - - it('should generate new techRecord based on vehicle type', fakeAsync(() => { - const techRecordServiceSpy = jest.spyOn(technicalRecordService, 'updateEditingTechRecord'); - const expectedTechRecord = getEmptyTechRecord(); - - testScheduler.run(({ hot, expectObservable }) => { - store.overrideSelector(editingTechRecord, { - vin: 'foo', - primaryVrm: 'bar', - systemNumber: 'foobar', - createdTimestamp: 'barfoo', - techRecord_vehicleType: 'lgv', - } as unknown as TechRecordType<'put'>); - // mock action to trigger effect - actions$ = hot('-a--', { - a: changeVehicleType({ - techRecord_vehicleType: VehicleTypes.CAR, - }), - }); - - expectObservable(effects.generateTechRecordBasedOnSectionTemplatesAfterVehicleTypeChange$).toBe('-b', { - b: expectedTechRecord, - }); - }); - - flush(); - expect(techRecordServiceSpy).toHaveBeenCalledTimes(1); - expect(techRecordServiceSpy).toHaveBeenCalledWith(expectedTechRecord); - })); - it('should default to heavy goods vehicle class when vehicle type is changed to hgv', fakeAsync(() => { - const techRecordServiceSpy = jest.spyOn(technicalRecordService, 'updateEditingTechRecord'); - const expectedTechRecord = getEmptyHGVRecord(); - testScheduler.run(({ hot, expectObservable }) => { - store.overrideSelector(editingTechRecord, { - vin: 'foo', - primaryVrm: 'bar', - systemNumber: 'foobar', - createdTimestamp: 'barfoo', - techRecord_vehicleType: 'lgv', - } as unknown as TechRecordType<'put'>); - // mock action to trigger effect - actions$ = hot('-a--', { - a: changeVehicleType({ - techRecord_vehicleType: VehicleTypes.HGV, - }), - }); - - expectObservable(effects.generateTechRecordBasedOnSectionTemplatesAfterVehicleTypeChange$).toBe('-b', { - b: expectedTechRecord, - }); - }); - - flush(); - expect(techRecordServiceSpy).toHaveBeenCalledTimes(1); - expect(techRecordServiceSpy).toHaveBeenCalledWith(expectedTechRecord); - })); - }); + let actions$ = new Observable(); + let effects: TechnicalRecordServiceEffects; + let store: MockStore; + let techRecordHttpService: TechnicalRecordHttpService; + let testScheduler: TestScheduler; + let technicalRecordService: TechnicalRecordService; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [HttpClientTestingModule, RouterTestingModule], + providers: [ + TechnicalRecordServiceEffects, + provideMockActions(() => actions$), + provideMockStore({ initialState: initialAppState }), + { provide: UserService, useValue: { name$: of('name'), id$: of('iod') } }, + { provide: TechnicalRecordService, useValue: { updateEditingTechRecord: jest.fn() } }, + ], + }); + effects = TestBed.inject(TechnicalRecordServiceEffects); + techRecordHttpService = TestBed.inject(TechnicalRecordHttpService); + technicalRecordService = TestBed.inject(TechnicalRecordService); + }); + + beforeEach(() => { + testScheduler = new TestScheduler((actual, expected) => expect(actual).toEqual(expected)); + }); + + describe('createVehicleRecord', () => { + it('should return a vehicle on successful API call', () => { + testScheduler.run(({ hot, cold, expectObservable }) => { + const mockVehicle = { systemNumber: 'foo', createdTimestamp: 'bar', vin: 'testVin' }; + const expectedVehicle = { + ...mockVehicle, + } as TechRecordType<'get'>; + + // mock action to trigger effect + actions$ = hot('-a--', { + a: createVehicleRecord({ + vehicle: { + systemNumber: 'foo', + createdTimestamp: 'bar', + vin: 'testVin', + } as unknown as TechRecordType<'put'>, + }), + }); + + // mock service call + jest.spyOn(techRecordHttpService, 'createVehicleRecord$').mockReturnValue(cold('--a|', { a: expectedVehicle })); + + // expect effect to return success action + expectObservable(effects.createVehicleRecord$).toBe('---b', { + b: createVehicleRecordSuccess({ vehicleTechRecord: expectedVehicle }), + }); + }); + }); + + it('should return an error message if not created', () => { + testScheduler.run(({ hot, cold, expectObservable }) => { + // mock action to trigger effect + actions$ = hot('-a--', { + a: createVehicleRecord({ + vehicle: { + systemNumber: 'foo', + createdTimestamp: 'bar', + vin: 'testVin', + } as unknown as TechRecordType<'put'>, + }), + }); + + // mock service call + const expectedError = new HttpErrorResponse({ status: 500, statusText: 'Internal server error' }); + + jest.spyOn(techRecordHttpService, 'createVehicleRecord$').mockReturnValue(cold('--#|', {}, expectedError)); + + expectObservable(effects.createVehicleRecord$).toBe('---b', { + b: createVehicleRecordFailure({ error: 'Unable to create vehicle with VIN testVin' }), + }); + }); + }); + }); + + describe('updateTechRecords$', () => { + beforeEach(() => { + store = TestBed.inject(MockStore); + store.overrideSelector(editingTechRecord, {} as unknown as TechRecordType<'put'>); + }); + it('should return a technical record on successful API call', () => { + testScheduler.run(({ hot, cold, expectObservable }) => { + const technicalRecord = { + systemNumber: 'foo', + createdTimestamp: 'bar', + vin: 'testVin', + } as TechRecordType<'get'>; + + // mock action to trigger effect + actions$ = hot('-a--', { a: updateTechRecord }); + + // mock service call + jest.spyOn(techRecordHttpService, 'updateTechRecords$').mockReturnValue(cold('--a|', { a: technicalRecord })); + + // expect effect to return success action + expectObservable(effects.updateTechRecord$).toBe('---b', { + b: updateTechRecordSuccess({ vehicleTechRecord: technicalRecord }), + }); + }); + }); + + it('should return an error message if not updated', () => { + testScheduler.run(({ hot, cold, expectObservable }) => { + // mock action to trigger effect + actions$ = hot('-a--', { a: updateTechRecord }); + + // mock service call + const expectedError = new HttpErrorResponse({ status: 500, statusText: 'Internal server error' }); + jest.spyOn(techRecordHttpService, 'updateTechRecords$').mockReturnValue(cold('--#|', {}, expectedError)); + + expectObservable(effects.updateTechRecord$).toBe('---b', { + b: updateTechRecordFailure({ + error: 'Unable to update technical record null', + }), + }); + }); + }); + }); + + describe('archiveTechRecord', () => { + it('should return an archived technical record on successful API call', () => { + testScheduler.run(({ hot, cold, expectObservable }) => { + const technicalRecord = { + systemNumber: 'foo', + createdTimestamp: 'bar', + vin: 'testVin', + } as TechRecordType<'get'>; + + // mock action to trigger effect + actions$ = hot('-a--', { a: archiveTechRecord }); + + // mock service call + jest + .spyOn(techRecordHttpService, 'archiveTechnicalRecord$') + .mockReturnValue(cold('--a|', { a: technicalRecord })); + + // expect effect to return success action + expectObservable(effects.archiveTechRecord$).toBe('---b', { + b: archiveTechRecordSuccess({ vehicleTechRecord: technicalRecord }), + }); + }); + }); + + it.each([ + [500, 'Internal server error'], + [400, 'You are not allowed to update an archived tech-record'], + ])('should return an error message if not found', () => { + testScheduler.run(({ hot, cold, expectObservable }) => { + // mock action to trigger effect + actions$ = hot('-a--', { a: archiveTechRecord }); + + // mock service call + const expectedError = new HttpErrorResponse({ status: 500, statusText: 'Internal server error' }); + jest.spyOn(techRecordHttpService, 'archiveTechnicalRecord$').mockReturnValue(cold('--#|', {}, expectedError)); + + expectObservable(effects.archiveTechRecord$).toBe('---b', { + b: archiveTechRecordFailure({ + error: 'Unable to archive technical record null', + }), + }); + }); + }); + }); + + describe('unarchiveTechRecord', () => { + it('should return an unarchived technical record on successful API call', () => { + testScheduler.run(({ hot, cold, expectObservable }) => { + const technicalRecord = { + systemNumber: 'foo', + createdTimestamp: 'bar', + vin: 'testVin', + } as TechRecordType<'get'>; + + // mock action to trigger effect + actions$ = hot('-a--', { a: unarchiveTechRecord }); + + // mock service call + jest + .spyOn(techRecordHttpService, 'unarchiveTechnicalRecord$') + .mockReturnValue(cold('--a|', { a: technicalRecord })); + + // expect effect to return success action + expectObservable(effects.unarchiveTechRecord$).toBe('---b', { + b: unarchiveTechRecordSuccess({ vehicleTechRecord: technicalRecord }), + }); + }); + }); + + it('should return an error message if not there is a non-archived record with the same VRM', () => { + testScheduler.run(({ hot, cold, expectObservable }) => { + // mock action to trigger effect + actions$ = hot('-a--', { a: unarchiveTechRecord }); + + // mock service call + const expectedError = new HttpErrorResponse({ + status: 400, + statusText: 'Cannot archive a record with unarchived records', + }); + jest.spyOn(techRecordHttpService, 'unarchiveTechnicalRecord$').mockReturnValue(cold('--#|', {}, expectedError)); + + expectObservable(effects.unarchiveTechRecord$).toBe('---b', { + b: unarchiveTechRecordFailure({ + error: 'Unable to unarchive technical record null', + }), + }); + }); + }); + + it('should return an error message if there was an internal server error', () => { + testScheduler.run(({ hot, cold, expectObservable }) => { + // mock action to trigger effect + actions$ = hot('-a--', { a: unarchiveTechRecord }); + + // mock service call + const expectedError = new HttpErrorResponse({ status: 500, statusText: 'Failed to unarchive record' }); + jest.spyOn(techRecordHttpService, 'unarchiveTechnicalRecord$').mockReturnValue(cold('--#|', {}, expectedError)); + + expectObservable(effects.unarchiveTechRecord$).toBe('---b', { + b: unarchiveTechRecordFailure({ + error: 'Unable to unarchive technical record null', + }), + }); + }); + }); + }); + + describe('generateTechRecordBasedOnSectionTemplates', () => { + beforeEach(() => { + store = TestBed.inject(MockStore); + store.resetSelectors(); + jest.resetModules(); + }); + + it('should generate new techRecord based on vehicle type', fakeAsync(() => { + const techRecordServiceSpy = jest.spyOn(technicalRecordService, 'updateEditingTechRecord'); + const expectedTechRecord = getEmptyTechRecord(); + + testScheduler.run(({ hot, expectObservable }) => { + store.overrideSelector(editingTechRecord, { + vin: 'foo', + primaryVrm: 'bar', + systemNumber: 'foobar', + createdTimestamp: 'barfoo', + techRecord_vehicleType: 'lgv', + } as unknown as TechRecordType<'put'>); + // mock action to trigger effect + actions$ = hot('-a--', { + a: changeVehicleType({ + techRecord_vehicleType: VehicleTypes.CAR, + }), + }); + + expectObservable(effects.generateTechRecordBasedOnSectionTemplatesAfterVehicleTypeChange$).toBe('-b', { + b: expectedTechRecord, + }); + }); + + flush(); + expect(techRecordServiceSpy).toHaveBeenCalledTimes(1); + expect(techRecordServiceSpy).toHaveBeenCalledWith(expectedTechRecord); + })); + it('should default to heavy goods vehicle class when vehicle type is changed to hgv', fakeAsync(() => { + const techRecordServiceSpy = jest.spyOn(technicalRecordService, 'updateEditingTechRecord'); + const expectedTechRecord = getEmptyHGVRecord(); + testScheduler.run(({ hot, expectObservable }) => { + store.overrideSelector(editingTechRecord, { + vin: 'foo', + primaryVrm: 'bar', + systemNumber: 'foobar', + createdTimestamp: 'barfoo', + techRecord_vehicleType: 'lgv', + } as unknown as TechRecordType<'put'>); + // mock action to trigger effect + actions$ = hot('-a--', { + a: changeVehicleType({ + techRecord_vehicleType: VehicleTypes.HGV, + }), + }); + + expectObservable(effects.generateTechRecordBasedOnSectionTemplatesAfterVehicleTypeChange$).toBe('-b', { + b: expectedTechRecord, + }); + }); + + flush(); + expect(techRecordServiceSpy).toHaveBeenCalledTimes(1); + expect(techRecordServiceSpy).toHaveBeenCalledWith(expectedTechRecord); + })); + }); }); function getEmptyTechRecord(): V3TechRecordModel { - return { - techRecord_createdAt: '', - techRecord_createdById: null, - techRecord_createdByName: null, - techRecord_euVehicleCategory: null, - techRecord_lastUpdatedAt: null, - techRecord_lastUpdatedById: null, - techRecord_lastUpdatedByName: null, - techRecord_manufactureYear: null, - techRecord_noOfAxles: 2, - techRecord_notes: undefined, - techRecord_applicantDetails_address1: null, - techRecord_applicantDetails_address2: null, - techRecord_applicantDetails_address3: null, - techRecord_applicantDetails_emailAddress: null, - techRecord_applicantDetails_name: null, - techRecord_applicantDetails_postCode: null, - techRecord_applicantDetails_postTown: null, - techRecord_applicantDetails_telephoneNumber: null, - techRecord_reasonForCreation: '', - techRecord_regnDate: null, - techRecord_statusCode: '', - techRecord_vehicleConfiguration: 'other', - techRecord_vehicleSubclass: undefined, - techRecord_vehicleType: 'car', - } as unknown as V3TechRecordModel; + return { + techRecord_createdAt: '', + techRecord_createdById: null, + techRecord_createdByName: null, + techRecord_euVehicleCategory: null, + techRecord_lastUpdatedAt: null, + techRecord_lastUpdatedById: null, + techRecord_lastUpdatedByName: null, + techRecord_manufactureYear: null, + techRecord_noOfAxles: 2, + techRecord_notes: undefined, + techRecord_applicantDetails_address1: null, + techRecord_applicantDetails_address2: null, + techRecord_applicantDetails_address3: null, + techRecord_applicantDetails_emailAddress: null, + techRecord_applicantDetails_name: null, + techRecord_applicantDetails_postCode: null, + techRecord_applicantDetails_postTown: null, + techRecord_applicantDetails_telephoneNumber: null, + techRecord_reasonForCreation: '', + techRecord_regnDate: null, + techRecord_statusCode: '', + techRecord_vehicleConfiguration: 'other', + techRecord_vehicleSubclass: undefined, + techRecord_vehicleType: 'car', + } as unknown as V3TechRecordModel; } function getEmptyHGVRecord(): V3TechRecordModel { - return { - techRecord_adrDetails_dangerousGoods: false, - techRecord_alterationMarker: null, - techRecord_applicantDetails_address1: null, - techRecord_applicantDetails_address2: null, - techRecord_applicantDetails_address3: null, - techRecord_applicantDetails_emailAddress: null, - techRecord_applicantDetails_name: null, - techRecord_applicantDetails_postCode: null, - techRecord_applicantDetails_postTown: null, - techRecord_applicantDetails_telephoneNumber: null, - techRecord_approvalType: null, - techRecord_axles: [], - techRecord_approvalTypeNumber: undefined, - techRecord_bodyType_code: null, - techRecord_bodyType_description: null, - techRecord_brakes_dtpNumber: null, - techRecord_conversionRefNo: null, - techRecord_departmentalVehicleMarker: null, - techRecord_dimensions_axleSpacing: [], - techRecord_dimensions_length: null, - techRecord_dimensions_width: null, - techRecord_drawbarCouplingFitted: null, - techRecord_emissionsLimit: null, - techRecord_euVehicleCategory: null, - techRecord_euroStandard: undefined, - techRecord_frontAxleTo5thWheelMax: null, - techRecord_frontAxleTo5thWheelMin: null, - techRecord_frontAxleToRearAxle: null, - techRecord_frontVehicleTo5thWheelCouplingMax: null, - techRecord_frontVehicleTo5thWheelCouplingMin: null, - techRecord_fuelPropulsionSystem: null, - techRecord_functionCode: null, - techRecord_grossDesignWeight: null, - techRecord_grossEecWeight: null, - techRecord_grossGbWeight: null, - techRecord_make: null, - techRecord_manufactureYear: null, - techRecord_maxTrainDesignWeight: null, - techRecord_maxTrainEecWeight: null, - techRecord_maxTrainGbWeight: null, - techRecord_microfilm_microfilmDocumentType: undefined, - techRecord_microfilm_microfilmRollNumber: undefined, - techRecord_microfilm_microfilmSerialNumber: undefined, - techRecord_model: null, - techRecord_noOfAxles: null, - techRecord_notes: undefined, - techRecord_ntaNumber: undefined, - techRecord_numberOfWheelsDriven: null, - techRecord_offRoad: null, - techRecord_plates: [], - techRecord_reasonForCreation: undefined, - techRecord_regnDate: null, - techRecord_roadFriendly: null, - techRecord_speedLimiterMrk: null, - techRecord_statusCode: '', - techRecord_tachoExemptMrk: null, - techRecord_trainDesignWeight: null, - techRecord_trainEecWeight: null, - techRecord_trainGbWeight: null, - techRecord_tyreUseCode: null, - techRecord_variantNumber: undefined, - techRecord_variantVersionNumber: undefined, - techRecord_vehicleClass_description: 'heavy goods vehicle', - techRecord_vehicleConfiguration: null, - techRecord_vehicleType: 'hgv', - } as unknown as V3TechRecordModel; + return { + techRecord_adrDetails_dangerousGoods: false, + techRecord_alterationMarker: null, + techRecord_applicantDetails_address1: null, + techRecord_applicantDetails_address2: null, + techRecord_applicantDetails_address3: null, + techRecord_applicantDetails_emailAddress: null, + techRecord_applicantDetails_name: null, + techRecord_applicantDetails_postCode: null, + techRecord_applicantDetails_postTown: null, + techRecord_applicantDetails_telephoneNumber: null, + techRecord_approvalType: null, + techRecord_axles: [], + techRecord_approvalTypeNumber: undefined, + techRecord_bodyType_code: null, + techRecord_bodyType_description: null, + techRecord_brakes_dtpNumber: null, + techRecord_conversionRefNo: null, + techRecord_departmentalVehicleMarker: null, + techRecord_dimensions_axleSpacing: [], + techRecord_dimensions_length: null, + techRecord_dimensions_width: null, + techRecord_drawbarCouplingFitted: null, + techRecord_emissionsLimit: null, + techRecord_euVehicleCategory: null, + techRecord_euroStandard: undefined, + techRecord_frontAxleTo5thWheelMax: null, + techRecord_frontAxleTo5thWheelMin: null, + techRecord_frontAxleToRearAxle: null, + techRecord_frontVehicleTo5thWheelCouplingMax: null, + techRecord_frontVehicleTo5thWheelCouplingMin: null, + techRecord_fuelPropulsionSystem: null, + techRecord_functionCode: null, + techRecord_grossDesignWeight: null, + techRecord_grossEecWeight: null, + techRecord_grossGbWeight: null, + techRecord_make: null, + techRecord_manufactureYear: null, + techRecord_maxTrainDesignWeight: null, + techRecord_maxTrainEecWeight: null, + techRecord_maxTrainGbWeight: null, + techRecord_microfilm_microfilmDocumentType: undefined, + techRecord_microfilm_microfilmRollNumber: undefined, + techRecord_microfilm_microfilmSerialNumber: undefined, + techRecord_model: null, + techRecord_noOfAxles: null, + techRecord_notes: undefined, + techRecord_ntaNumber: undefined, + techRecord_numberOfWheelsDriven: null, + techRecord_offRoad: null, + techRecord_plates: [], + techRecord_reasonForCreation: undefined, + techRecord_regnDate: null, + techRecord_roadFriendly: null, + techRecord_speedLimiterMrk: null, + techRecord_statusCode: '', + techRecord_tachoExemptMrk: null, + techRecord_trainDesignWeight: null, + techRecord_trainEecWeight: null, + techRecord_trainGbWeight: null, + techRecord_tyreUseCode: null, + techRecord_variantNumber: undefined, + techRecord_variantVersionNumber: undefined, + techRecord_vehicleClass_description: 'heavy goods vehicle', + techRecord_vehicleConfiguration: null, + techRecord_vehicleType: 'hgv', + } as unknown as V3TechRecordModel; } diff --git a/src/app/store/technical-records/effects/technical-record-service.effects.ts b/src/app/store/technical-records/effects/technical-record-service.effects.ts index eb6bea5f9e..1bcaabe101 100644 --- a/src/app/store/technical-records/effects/technical-record-service.effects.ts +++ b/src/app/store/technical-records/effects/technical-record-service.effects.ts @@ -1,8 +1,12 @@ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { EUVehicleCategory } from '@dvsa/cvs-type-definitions/types/v3/tech-record/enums/euVehicleCategory.enum.js'; import { VehicleClassDescription } from '@dvsa/cvs-type-definitions/types/v3/tech-record/enums/vehicleClassDescription.enum.js'; import { TechRecordType } from '@dvsa/cvs-type-definitions/types/v3/tech-record/tech-record-verb'; -import { TechRecordGETHGV, TechRecordGETPSV, TechRecordGETTRL } from '@dvsa/cvs-type-definitions/types/v3/tech-record/tech-record-verb-vehicle-type'; +import { + TechRecordGETHGV, + TechRecordGETPSV, + TechRecordGETTRL, +} from '@dvsa/cvs-type-definitions/types/v3/tech-record/tech-record-verb-vehicle-type'; import { DynamicFormService } from '@forms/services/dynamic-form.service'; import { vehicleTemplateMap } from '@forms/utils/tech-record-constants'; import { VehicleTypes } from '@models/vehicle-tech-record.model'; @@ -14,316 +18,364 @@ import { TechnicalRecordService } from '@services/technical-record/technical-rec import { UserService } from '@services/user-service/user-service'; import { State } from '@store/index'; import { cloneDeep, merge } from 'lodash'; +import { catchError, concatMap, map, mergeMap, of, switchMap, tap, withLatestFrom } from 'rxjs'; import { - catchError, concatMap, - map, mergeMap, of, switchMap, tap, - withLatestFrom, -} from 'rxjs'; -import { - amendVin, - amendVinSuccess, - amendVrm, - amendVrmFailure, - amendVrmSuccess, - archiveTechRecord, - archiveTechRecordFailure, - archiveTechRecordSuccess, - changeVehicleType, - createVehicle, - createVehicleRecord, - createVehicleRecordFailure, - createVehicleRecordSuccess, - generateADRCertificate, - generateADRCertificateFailure, - generateADRCertificateSuccess, - generateLetter, - generateLetterFailure, - generateLetterSuccess, - generatePlate, - generatePlateFailure, - generatePlateSuccess, - getBySystemNumber, - getBySystemNumberFailure, - getBySystemNumberSuccess, - getTechRecordV3, - getTechRecordV3Failure, - getTechRecordV3Success, - promoteTechRecord, - promoteTechRecordFailure, - promoteTechRecordSuccess, - unarchiveTechRecord, - unarchiveTechRecordFailure, - unarchiveTechRecordSuccess, - updateTechRecord, - updateTechRecordFailure, - updateTechRecordSuccess, + amendVin, + amendVinSuccess, + amendVrm, + amendVrmFailure, + amendVrmSuccess, + archiveTechRecord, + archiveTechRecordFailure, + archiveTechRecordSuccess, + changeVehicleType, + createVehicle, + createVehicleRecord, + createVehicleRecordFailure, + createVehicleRecordSuccess, + generateADRCertificate, + generateADRCertificateFailure, + generateADRCertificateSuccess, + generateLetter, + generateLetterFailure, + generateLetterSuccess, + generatePlate, + generatePlateFailure, + generatePlateSuccess, + getBySystemNumber, + getBySystemNumberFailure, + getBySystemNumberSuccess, + getTechRecordV3, + getTechRecordV3Failure, + getTechRecordV3Success, + promoteTechRecord, + promoteTechRecordFailure, + promoteTechRecordSuccess, + unarchiveTechRecord, + unarchiveTechRecordFailure, + unarchiveTechRecordSuccess, + updateTechRecord, + updateTechRecordFailure, + updateTechRecordSuccess, } from '../actions/technical-record-service.actions'; import { editingTechRecord, selectTechRecord } from '../selectors/technical-record-service.selectors'; @Injectable() export class TechnicalRecordServiceEffects { - constructor( - private actions$: Actions, - private techRecordHttpService: TechnicalRecordHttpService, - private technicalRecordService: TechnicalRecordService, - private batchTechRecordService: BatchTechnicalRecordService, - private userService: UserService, - private store: Store, - private dfs: DynamicFormService, - ) { } - - getTechnicalRecordHistory$ = createEffect(() => - this.actions$.pipe( - ofType(getBySystemNumber), - mergeMap((action) => { - const anchorLink = 'search-term'; + private actions$ = inject(Actions); + private techRecordHttpService = inject(TechnicalRecordHttpService); + private technicalRecordService = inject(TechnicalRecordService); + private batchTechRecordService = inject(BatchTechnicalRecordService); + private userService = inject(UserService); + private store = inject>(Store); + private dfs = inject(DynamicFormService); - return this.techRecordHttpService.getBySystemNumber$(action.systemNumber).pipe( - map((vehicleTechRecords) => { - return getBySystemNumberSuccess({ techRecordHistory: vehicleTechRecords }); - }), - catchError(() => of(getBySystemNumberFailure({ error: 'could not find technical record history', anchorLink }))), - ); - }), - )); + getTechnicalRecordHistory$ = createEffect(() => + this.actions$.pipe( + ofType(getBySystemNumber), + mergeMap((action) => { + const anchorLink = 'search-term'; - getTechRecordV3$ = createEffect(() => - this.actions$.pipe( - ofType(getTechRecordV3), - mergeMap((action) => { - const anchorLink = 'search-term'; + return this.techRecordHttpService.getBySystemNumber$(action.systemNumber).pipe( + map((vehicleTechRecords) => { + return getBySystemNumberSuccess({ techRecordHistory: vehicleTechRecords }); + }), + catchError(() => + of(getBySystemNumberFailure({ error: 'could not find technical record history', anchorLink })) + ) + ); + }) + ) + ); - return this.techRecordHttpService.getRecordV3$(action.systemNumber, action.createdTimestamp).pipe( - map((vehicleTechRecord) => { - return getTechRecordV3Success({ vehicleTechRecord }); - }), - catchError((error) => - of(getTechRecordV3Failure({ error: this.getTechRecordErrorMessage(error, 'getTechnicalRecords', 'systemNumber'), anchorLink }))), - ); - }), - )); + getTechRecordV3$ = createEffect(() => + this.actions$.pipe( + ofType(getTechRecordV3), + mergeMap((action) => { + const anchorLink = 'search-term'; - createVehicleRecord$ = createEffect(() => - this.actions$.pipe( - ofType(createVehicleRecord), - withLatestFrom(this.batchTechRecordService.applicationId$, this.userService.name$, this.userService.id$), - concatMap(([{ vehicle }, applicationId]) => { - const vehicleRecord = { ...vehicle, techRecord_applicationId: applicationId }; + return this.techRecordHttpService.getRecordV3$(action.systemNumber, action.createdTimestamp).pipe( + map((vehicleTechRecord) => { + return getTechRecordV3Success({ vehicleTechRecord }); + }), + catchError((error) => + of( + getTechRecordV3Failure({ + error: this.getTechRecordErrorMessage(error, 'getTechnicalRecords', 'systemNumber'), + anchorLink, + }) + ) + ) + ); + }) + ) + ); - return this.techRecordHttpService.createVehicleRecord$(vehicleRecord).pipe( - map((response) => createVehicleRecordSuccess({ vehicleTechRecord: response })), - catchError((error) => - of( - createVehicleRecordFailure({ - error: `Unable to create vehicle with VIN ${vehicle.vin}${error.error?.errors - ? ` because:${(error.error.errors?.map((e: string) => `\n${e}`) as string[]).join()}` - : '' - }`, - }), - )), - ); - }), - )); + createVehicleRecord$ = createEffect(() => + this.actions$.pipe( + ofType(createVehicleRecord), + withLatestFrom(this.batchTechRecordService.applicationId$, this.userService.name$, this.userService.id$), + concatMap(([{ vehicle }, applicationId]) => { + const vehicleRecord = { ...vehicle, techRecord_applicationId: applicationId }; - updateTechRecord$ = createEffect(() => - this.actions$.pipe( - ofType(updateTechRecord), - withLatestFrom(this.store.pipe(select(editingTechRecord))), - concatMap(([{ systemNumber, createdTimestamp }, techRecord]) => { - if (!techRecord) { - return of(updateTechRecordFailure({ error: 'There is not technical record in edit' })); - } - return this.techRecordHttpService.updateTechRecords$(systemNumber, createdTimestamp, techRecord).pipe( - map((vehicleTechRecord) => updateTechRecordSuccess({ vehicleTechRecord })), - catchError((error) => of(updateTechRecordFailure({ error: this.getTechRecordErrorMessage(error, 'updateTechnicalRecord') }))), - ); - }), - )); + return this.techRecordHttpService.createVehicleRecord$(vehicleRecord).pipe( + map((response) => createVehicleRecordSuccess({ vehicleTechRecord: response })), + catchError((error) => + of( + createVehicleRecordFailure({ + error: `Unable to create vehicle with VIN ${vehicle.vin}${ + error.error?.errors + ? ` because:${(error.error.errors?.map((e: string) => `\n${e}`) as string[]).join()}` + : '' + }`, + }) + ) + ) + ); + }) + ) + ); - amendVrm$ = createEffect(() => - this.actions$.pipe( - ofType(amendVrm), - switchMap(({ - newVrm, cherishedTransfer, systemNumber, createdTimestamp, thirdMark, - }) => { - return this.techRecordHttpService.amendVrm$(newVrm, cherishedTransfer, systemNumber, createdTimestamp, thirdMark).pipe( - map((vehicleTechRecord) => amendVrmSuccess({ vehicleTechRecord })), - catchError((error) => of(amendVrmFailure({ error: this.getTechRecordErrorMessage(error, 'updateTechnicalRecord') }))), - ); - }), - )); + updateTechRecord$ = createEffect(() => + this.actions$.pipe( + ofType(updateTechRecord), + withLatestFrom(this.store.pipe(select(editingTechRecord))), + concatMap(([{ systemNumber, createdTimestamp }, techRecord]) => { + if (!techRecord) { + return of(updateTechRecordFailure({ error: 'There is not technical record in edit' })); + } + return this.techRecordHttpService.updateTechRecords$(systemNumber, createdTimestamp, techRecord).pipe( + map((vehicleTechRecord) => updateTechRecordSuccess({ vehicleTechRecord })), + catchError((error) => + of(updateTechRecordFailure({ error: this.getTechRecordErrorMessage(error, 'updateTechnicalRecord') })) + ) + ); + }) + ) + ); - amendVin$ = createEffect(() => - this.actions$.pipe( - ofType(amendVin), - switchMap(({ newVin, systemNumber, createdTimestamp }) => { - return this.techRecordHttpService.amendVin$(newVin, systemNumber, createdTimestamp).pipe( - map((vehicleTechRecord) => amendVinSuccess({ vehicleTechRecord })), - catchError((error) => of(amendVrmFailure({ error: this.getTechRecordErrorMessage(error, 'updateTechnicalRecord') }))), - ); - }), - )); + amendVrm$ = createEffect(() => + this.actions$.pipe( + ofType(amendVrm), + switchMap(({ newVrm, cherishedTransfer, systemNumber, createdTimestamp, thirdMark }) => { + return this.techRecordHttpService + .amendVrm$(newVrm, cherishedTransfer, systemNumber, createdTimestamp, thirdMark) + .pipe( + map((vehicleTechRecord) => amendVrmSuccess({ vehicleTechRecord })), + catchError((error) => + of(amendVrmFailure({ error: this.getTechRecordErrorMessage(error, 'updateTechnicalRecord') })) + ) + ); + }) + ) + ); - archiveTechRecord$ = createEffect(() => - this.actions$.pipe( - ofType(archiveTechRecord), - switchMap(({ systemNumber, createdTimestamp, reasonForArchiving }) => - this.techRecordHttpService.archiveTechnicalRecord$(systemNumber, createdTimestamp, reasonForArchiving).pipe( - map((vehicleTechRecord) => archiveTechRecordSuccess({ vehicleTechRecord })), - catchError((error) => of(archiveTechRecordFailure({ error: this.getTechRecordErrorMessage(error, 'archiveTechRecord') }))), - )), - )); + amendVin$ = createEffect(() => + this.actions$.pipe( + ofType(amendVin), + switchMap(({ newVin, systemNumber, createdTimestamp }) => { + return this.techRecordHttpService.amendVin$(newVin, systemNumber, createdTimestamp).pipe( + map((vehicleTechRecord) => amendVinSuccess({ vehicleTechRecord })), + catchError((error) => + of(amendVrmFailure({ error: this.getTechRecordErrorMessage(error, 'updateTechnicalRecord') })) + ) + ); + }) + ) + ); - promoteTechRecord$ = createEffect(() => - this.actions$.pipe( - ofType(promoteTechRecord), - switchMap(({ systemNumber, createdTimestamp, reasonForPromoting }) => - this.techRecordHttpService.promoteTechnicalRecord$(systemNumber, createdTimestamp, reasonForPromoting).pipe( - map((vehicleTechRecord) => promoteTechRecordSuccess({ vehicleTechRecord })), - catchError((error) => of(promoteTechRecordFailure({ error: this.getTechRecordErrorMessage(error, 'promoteTechRecord') }))), - )), - )); - generateTechRecordBasedOnSectionTemplates$ = createEffect( - () => - this.actions$.pipe( - ofType(createVehicle), - withLatestFrom(this.store.pipe(select(editingTechRecord))), - concatMap(([{ techRecord_vehicleType }, editableTechRecord]) => { - const techRecord = { ...cloneDeep(editableTechRecord), techRecord_vehicleType }; + archiveTechRecord$ = createEffect(() => + this.actions$.pipe( + ofType(archiveTechRecord), + switchMap(({ systemNumber, createdTimestamp, reasonForArchiving }) => + this.techRecordHttpService.archiveTechnicalRecord$(systemNumber, createdTimestamp, reasonForArchiving).pipe( + map((vehicleTechRecord) => archiveTechRecordSuccess({ vehicleTechRecord })), + catchError((error) => + of(archiveTechRecordFailure({ error: this.getTechRecordErrorMessage(error, 'archiveTechRecord') })) + ) + ) + ) + ) + ); - if (techRecord_vehicleType === VehicleTypes.SMALL_TRL) { - techRecord.techRecord_vehicleType = VehicleTypes.TRL; - (techRecord as TechRecordGETTRL).techRecord_euVehicleCategory = EUVehicleCategory.O1; - } - if (techRecord.techRecord_vehicleType === VehicleTypes.HGV || techRecord.techRecord_vehicleType === VehicleTypes.PSV) { - (techRecord as TechRecordGETHGV | TechRecordGETPSV).techRecord_vehicleConfiguration = null; - } - if (techRecord_vehicleType === VehicleTypes.HGV) { - (techRecord as TechRecordGETHGV).techRecord_vehicleClass_description = VehicleClassDescription.HeavyGoodsVehicle; - } - if (techRecord_vehicleType === VehicleTypes.TRL) { - (techRecord as TechRecordGETTRL).techRecord_vehicleClass_description = VehicleClassDescription.Trailer; - } - const techRecordTemplate = vehicleTemplateMap.get(techRecord_vehicleType) || []; + promoteTechRecord$ = createEffect(() => + this.actions$.pipe( + ofType(promoteTechRecord), + switchMap(({ systemNumber, createdTimestamp, reasonForPromoting }) => + this.techRecordHttpService.promoteTechnicalRecord$(systemNumber, createdTimestamp, reasonForPromoting).pipe( + map((vehicleTechRecord) => promoteTechRecordSuccess({ vehicleTechRecord })), + catchError((error) => + of(promoteTechRecordFailure({ error: this.getTechRecordErrorMessage(error, 'promoteTechRecord') })) + ) + ) + ) + ) + ); + generateTechRecordBasedOnSectionTemplates$ = createEffect( + () => + this.actions$.pipe( + ofType(createVehicle), + withLatestFrom(this.store.pipe(select(editingTechRecord))), + concatMap(([{ techRecord_vehicleType }, editableTechRecord]) => { + const techRecord = { ...cloneDeep(editableTechRecord), techRecord_vehicleType }; - return of( - techRecordTemplate.reduce((mergedNodes, formNode) => { - const form = this.dfs.createForm(formNode, techRecord); - return merge(mergedNodes, form.getCleanValue(form)); - }, {}) as TechRecordType<'put'>, - ); - }), - tap((mergedForms) => this.technicalRecordService.updateEditingTechRecord(mergedForms)), - ), - { dispatch: false }, - ); + if (techRecord_vehicleType === VehicleTypes.SMALL_TRL) { + techRecord.techRecord_vehicleType = VehicleTypes.TRL; + (techRecord as TechRecordGETTRL).techRecord_euVehicleCategory = EUVehicleCategory.O1; + } + if ( + techRecord.techRecord_vehicleType === VehicleTypes.HGV || + techRecord.techRecord_vehicleType === VehicleTypes.PSV + ) { + (techRecord as TechRecordGETHGV | TechRecordGETPSV).techRecord_vehicleConfiguration = null; + } + if (techRecord_vehicleType === VehicleTypes.HGV) { + (techRecord as TechRecordGETHGV).techRecord_vehicleClass_description = + VehicleClassDescription.HeavyGoodsVehicle; + } + if (techRecord_vehicleType === VehicleTypes.TRL) { + (techRecord as TechRecordGETTRL).techRecord_vehicleClass_description = VehicleClassDescription.Trailer; + } + const techRecordTemplate = vehicleTemplateMap.get(techRecord_vehicleType) || []; - generateTechRecordBasedOnSectionTemplatesAfterVehicleTypeChange$ = createEffect( - () => - this.actions$.pipe( - ofType(changeVehicleType), - withLatestFrom(this.store.pipe(select(editingTechRecord))), - concatMap(([{ techRecord_vehicleType }, editableTechRecord]) => { - const techRecord = { ...cloneDeep(editableTechRecord), techRecord_vehicleType }; + return of( + techRecordTemplate.reduce((mergedNodes, formNode) => { + const form = this.dfs.createForm(formNode, techRecord); + return merge(mergedNodes, form.getCleanValue(form)); + }, {}) as TechRecordType<'put'> + ); + }), + tap((mergedForms) => this.technicalRecordService.updateEditingTechRecord(mergedForms)) + ), + { dispatch: false } + ); - if (techRecord.techRecord_vehicleType === VehicleTypes.SMALL_TRL) { - techRecord.techRecord_vehicleType = VehicleTypes.TRL; - (techRecord as TechRecordGETTRL).techRecord_euVehicleCategory = EUVehicleCategory.O1; - } + generateTechRecordBasedOnSectionTemplatesAfterVehicleTypeChange$ = createEffect( + () => + this.actions$.pipe( + ofType(changeVehicleType), + withLatestFrom(this.store.pipe(select(editingTechRecord))), + concatMap(([{ techRecord_vehicleType }, editableTechRecord]) => { + const techRecord = { ...cloneDeep(editableTechRecord), techRecord_vehicleType }; - if (techRecord_vehicleType === VehicleTypes.HGV || techRecord_vehicleType === VehicleTypes.PSV) { - (techRecord as TechRecordGETHGV | TechRecordGETPSV).techRecord_approvalType = null; - (techRecord as TechRecordGETHGV | TechRecordGETPSV).techRecord_vehicleConfiguration = null; - } + if (techRecord.techRecord_vehicleType === VehicleTypes.SMALL_TRL) { + techRecord.techRecord_vehicleType = VehicleTypes.TRL; + (techRecord as TechRecordGETTRL).techRecord_euVehicleCategory = EUVehicleCategory.O1; + } - if (techRecord_vehicleType === VehicleTypes.HGV) { - (techRecord as TechRecordGETHGV).techRecord_vehicleClass_description = VehicleClassDescription.HeavyGoodsVehicle; - } - if (techRecord_vehicleType === VehicleTypes.TRL) { - (techRecord as TechRecordGETTRL).techRecord_vehicleClass_description = VehicleClassDescription.Trailer; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - (techRecord as any).euVehicleCategory = null; - } + if (techRecord_vehicleType === VehicleTypes.HGV || techRecord_vehicleType === VehicleTypes.PSV) { + (techRecord as TechRecordGETHGV | TechRecordGETPSV).techRecord_approvalType = null; + (techRecord as TechRecordGETHGV | TechRecordGETPSV).techRecord_vehicleConfiguration = null; + } - const techRecordTemplate = vehicleTemplateMap.get(techRecord_vehicleType) || []; - return of( - techRecordTemplate.reduce((mergedNodes, formNode) => { - const form = this.dfs.createForm(formNode, techRecord); - return merge(mergedNodes, form.getCleanValue(form)); - }, {}) as TechRecordType<'put'>, - ); - }), - tap((mergedForms) => this.technicalRecordService.updateEditingTechRecord(mergedForms)), - ), - { dispatch: false }, - ); + if (techRecord_vehicleType === VehicleTypes.HGV) { + (techRecord as TechRecordGETHGV).techRecord_vehicleClass_description = + VehicleClassDescription.HeavyGoodsVehicle; + } + if (techRecord_vehicleType === VehicleTypes.TRL) { + (techRecord as TechRecordGETTRL).techRecord_vehicleClass_description = VehicleClassDescription.Trailer; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + (techRecord as any).euVehicleCategory = null; + } - generatePlate$ = createEffect(() => - this.actions$.pipe( - ofType(generatePlate), - withLatestFrom(this.store.select(selectTechRecord), this.userService.name$, this.userService.userEmail$), - switchMap(([{ reason }, vehicle, name, email]) => - this.techRecordHttpService.generatePlate$(vehicle as TechRecordType<'get'>, reason, { name, email }).pipe( - map(() => generatePlateSuccess()), - catchError((error) => of(generatePlateFailure({ error: this.getTechRecordErrorMessage(error, 'generatePlate') }))), - )), - )); + const techRecordTemplate = vehicleTemplateMap.get(techRecord_vehicleType) || []; + return of( + techRecordTemplate.reduce((mergedNodes, formNode) => { + const form = this.dfs.createForm(formNode, techRecord); + return merge(mergedNodes, form.getCleanValue(form)); + }, {}) as TechRecordType<'put'> + ); + }), + tap((mergedForms) => this.technicalRecordService.updateEditingTechRecord(mergedForms)) + ), + { dispatch: false } + ); - generateLetter$ = createEffect(() => - this.actions$.pipe( - ofType(generateLetter), - withLatestFrom(this.store.select(selectTechRecord), this.userService.name$, this.userService.userEmail$), - switchMap(([{ letterType, paragraphId }, vehicle, name, email]) => - this.techRecordHttpService.generateLetter$(vehicle as TechRecordType<'get'>, letterType, paragraphId, { name, email }).pipe( - map(() => generateLetterSuccess()), - catchError((error) => of(generateLetterFailure({ error: this.getTechRecordErrorMessage(error, 'generateLetter') }))), - )), - )); + generatePlate$ = createEffect(() => + this.actions$.pipe( + ofType(generatePlate), + withLatestFrom(this.store.select(selectTechRecord), this.userService.name$, this.userService.userEmail$), + switchMap(([{ reason }, vehicle, name, email]) => + this.techRecordHttpService.generatePlate$(vehicle as TechRecordType<'get'>, reason, { name, email }).pipe( + map(() => generatePlateSuccess()), + catchError((error) => + of(generatePlateFailure({ error: this.getTechRecordErrorMessage(error, 'generatePlate') })) + ) + ) + ) + ) + ); - unarchiveTechRecord$ = createEffect(() => - this.actions$.pipe( - ofType(unarchiveTechRecord), - switchMap(({ - systemNumber, createdTimestamp, reasonForUnarchiving, status, - }) => - this.techRecordHttpService.unarchiveTechnicalRecord$(systemNumber, createdTimestamp, reasonForUnarchiving, status).pipe( - map((vehicleTechRecord) => unarchiveTechRecordSuccess({ vehicleTechRecord })), - catchError((error) => of(unarchiveTechRecordFailure({ error: this.getTechRecordErrorMessage(error, 'unarchiveTechRecord') }))), - )), - )); + generateLetter$ = createEffect(() => + this.actions$.pipe( + ofType(generateLetter), + withLatestFrom(this.store.select(selectTechRecord), this.userService.name$, this.userService.userEmail$), + switchMap(([{ letterType, paragraphId }, vehicle, name, email]) => + this.techRecordHttpService + .generateLetter$(vehicle as TechRecordType<'get'>, letterType, paragraphId, { name, email }) + .pipe( + map(() => generateLetterSuccess()), + catchError((error) => + of(generateLetterFailure({ error: this.getTechRecordErrorMessage(error, 'generateLetter') })) + ) + ) + ) + ) + ); - generateADRCertificate$ = createEffect(() => - this.actions$.pipe( - ofType(generateADRCertificate), - switchMap(({ - systemNumber, createdTimestamp, certificateType, - }) => - this.techRecordHttpService.generateADRCertificate$(systemNumber, createdTimestamp, certificateType).pipe( - map((res) => generateADRCertificateSuccess({ id: res.id })), - catchError((error) => of(generateADRCertificateFailure({ error: this.getTechRecordErrorMessage(error, 'generateADRCertificate') }))), - )), - )); + unarchiveTechRecord$ = createEffect(() => + this.actions$.pipe( + ofType(unarchiveTechRecord), + switchMap(({ systemNumber, createdTimestamp, reasonForUnarchiving, status }) => + this.techRecordHttpService + .unarchiveTechnicalRecord$(systemNumber, createdTimestamp, reasonForUnarchiving, status) + .pipe( + map((vehicleTechRecord) => unarchiveTechRecordSuccess({ vehicleTechRecord })), + catchError((error) => + of(unarchiveTechRecordFailure({ error: this.getTechRecordErrorMessage(error, 'unarchiveTechRecord') })) + ) + ) + ) + ) + ); - // eslint-disable-next-line @typescript-eslint/no-explicit-any - getTechRecordErrorMessage(error: any, type: string, search?: string): string { - if (typeof error !== 'object') { - return error; - } if (error.status === 404) { - return this.apiErrors[`${type}_404`]; - } - return `${this.apiErrors[`${type}_400`]} ${search ?? JSON.stringify(error.error)}`; + generateADRCertificate$ = createEffect(() => + this.actions$.pipe( + ofType(generateADRCertificate), + switchMap(({ systemNumber, createdTimestamp, certificateType }) => + this.techRecordHttpService.generateADRCertificate$(systemNumber, createdTimestamp, certificateType).pipe( + map((res) => generateADRCertificateSuccess({ id: res.id })), + catchError((error) => + of( + generateADRCertificateFailure({ error: this.getTechRecordErrorMessage(error, 'generateADRCertificate') }) + ) + ) + ) + ) + ) + ); - } + // eslint-disable-next-line @typescript-eslint/no-explicit-any + getTechRecordErrorMessage(error: any, type: string, search?: string): string { + if (typeof error !== 'object') { + return error; + } + if (error.status === 404) { + return this.apiErrors[`${type}_404`]; + } + return `${this.apiErrors[`${type}_400`]} ${search ?? JSON.stringify(error.error)}`; + } - private apiErrors: Record = { - getTechnicalRecords_400: 'There was a problem getting the Tech Record by', - getTechnicalRecords_404: 'Vehicle not found, check the vehicle registration mark, trailer ID or vehicle identification number', - createVehicleRecord_400: 'Unable to create a new vehicle record', - // createProvisionalTechRecord_400: 'Unable to create a new provisional record', - updateTechnicalRecord_400: 'Unable to update technical record', - archiveTechRecord_400: 'Unable to archive technical record', - promoteTechRecord_400: 'Unable to promote technical record', - unarchiveTechRecord_400: 'Unable to unarchive technical record', - generateADRCertificate_400: 'Unable to generate ADR certificate', - }; + private apiErrors: Record = { + getTechnicalRecords_400: 'There was a problem getting the Tech Record by', + getTechnicalRecords_404: + 'Vehicle not found, check the vehicle registration mark, trailer ID or vehicle identification number', + createVehicleRecord_400: 'Unable to create a new vehicle record', + // createProvisionalTechRecord_400: 'Unable to create a new provisional record', + updateTechnicalRecord_400: 'Unable to update technical record', + archiveTechRecord_400: 'Unable to archive technical record', + promoteTechRecord_400: 'Unable to promote technical record', + unarchiveTechRecord_400: 'Unable to unarchive technical record', + generateADRCertificate_400: 'Unable to generate ADR certificate', + }; } diff --git a/src/app/store/technical-records/reducers/batch-create.reducer.ts b/src/app/store/technical-records/reducers/batch-create.reducer.ts index 856cdebc59..ffacd38f87 100644 --- a/src/app/store/technical-records/reducers/batch-create.reducer.ts +++ b/src/app/store/technical-records/reducers/batch-create.reducer.ts @@ -1,74 +1,84 @@ import { TechRecordType } from '@dvsa/cvs-type-definitions/types/v3/tech-record/tech-record-verb'; import { StatusCodes, VehicleTypes } from '@models/vehicle-tech-record.model'; -import { - EntityAdapter, EntityState, Update, createEntityAdapter, -} from '@ngrx/entity'; +import { EntityAdapter, EntityState, Update, createEntityAdapter } from '@ngrx/entity'; import { createReducer, on } from '@ngrx/store'; import { - clearBatch, - setApplicationId, - setGenerateNumberFlag, - setVehicleStatus, - setVehicleType, - upsertVehicleBatch, + clearBatch, + setApplicationId, + setGenerateNumberFlag, + setVehicleStatus, + setVehicleType, + upsertVehicleBatch, } from '../actions/batch-create.actions'; import { createVehicleRecordSuccess, updateTechRecordSuccess } from '../actions/technical-record-service.actions'; export type BatchRecord = { - vin: string; - systemNumber?: string; - trailerIdOrVrm?: string; - vehicleType?: string; - status?: StatusCodes; - created?: boolean; - amendedRecord?: boolean; - createdTimestamp?: string; + vin: string; + systemNumber?: string; + trailerIdOrVrm?: string; + vehicleType?: string; + status?: StatusCodes; + created?: boolean; + amendedRecord?: boolean; + createdTimestamp?: string; }; export interface BatchRecords extends EntityState { - vehicleType?: VehicleTypes; - generateNumber: boolean; - applicationId?: string; - vehicleStatus?: string; + vehicleType?: VehicleTypes; + generateNumber: boolean; + applicationId?: string; + vehicleStatus?: string; } const selectId = (a: BatchRecord): string => { - return a.vin; + return a.vin; }; export const batchAdapter: EntityAdapter = createEntityAdapter({ selectId }); export const initialBatchState: BatchRecords = batchAdapter.getInitialState({ - generateNumber: false, + generateNumber: false, }); export const vehicleBatchCreateReducer = createReducer( - initialBatchState, - on(upsertVehicleBatch, (state, action) => batchAdapter.setAll(action.vehicles, state)), - on(setGenerateNumberFlag, (state, { generateNumber }) => ({ ...state, generateNumber })), - on(setApplicationId, (state, { applicationId }) => ({ ...state, applicationId })), - on(setVehicleStatus, (state, { vehicleStatus }) => ({ ...state, vehicleStatus })), - on(setVehicleType, (state, { vehicleType }) => ({ ...state, vehicleType })), - on(createVehicleRecordSuccess, (state, action) => batchAdapter.updateOne(vehicleRecordsToBatchRecordMapper(action.vehicleTechRecord), state)), - on(updateTechRecordSuccess, (state, action) => - batchAdapter.updateOne(vehicleRecordsToBatchRecordMapper(action.vehicleTechRecord, true, true), state)), - on(clearBatch, (state) => batchAdapter.removeAll({ - ...state, vehicleStatus: '', applicationId: '', vehicleType: undefined, - })), + initialBatchState, + on(upsertVehicleBatch, (state, action) => batchAdapter.setAll(action.vehicles, state)), + on(setGenerateNumberFlag, (state, { generateNumber }) => ({ ...state, generateNumber })), + on(setApplicationId, (state, { applicationId }) => ({ ...state, applicationId })), + on(setVehicleStatus, (state, { vehicleStatus }) => ({ ...state, vehicleStatus })), + on(setVehicleType, (state, { vehicleType }) => ({ ...state, vehicleType })), + on(createVehicleRecordSuccess, (state, action) => + batchAdapter.updateOne(vehicleRecordsToBatchRecordMapper(action.vehicleTechRecord), state) + ), + on(updateTechRecordSuccess, (state, action) => + batchAdapter.updateOne(vehicleRecordsToBatchRecordMapper(action.vehicleTechRecord, true, true), state) + ), + on(clearBatch, (state) => + batchAdapter.removeAll({ + ...state, + vehicleStatus: '', + applicationId: '', + vehicleType: undefined, + }) + ) ); -function vehicleRecordsToBatchRecordMapper(techRecord: TechRecordType<'get'>, created = true, amendedRecord = false): Update { - return { - id: techRecord.vin, - changes: { - vin: techRecord.vin, - systemNumber: techRecord.systemNumber, - trailerIdOrVrm: techRecord.techRecord_vehicleType !== 'trl' ? techRecord.primaryVrm ?? '' : techRecord.trailerId, - vehicleType: techRecord.techRecord_vehicleType, - status: (techRecord.techRecord_statusCode as StatusCodes) ?? undefined, - created, - amendedRecord, - createdTimestamp: techRecord.createdTimestamp, - }, - }; +function vehicleRecordsToBatchRecordMapper( + techRecord: TechRecordType<'get'>, + created = true, + amendedRecord = false +): Update { + return { + id: techRecord.vin, + changes: { + vin: techRecord.vin, + systemNumber: techRecord.systemNumber, + trailerIdOrVrm: techRecord.techRecord_vehicleType !== 'trl' ? techRecord.primaryVrm ?? '' : techRecord.trailerId, + vehicleType: techRecord.techRecord_vehicleType, + status: (techRecord.techRecord_statusCode as StatusCodes) ?? undefined, + created, + amendedRecord, + createdTimestamp: techRecord.createdTimestamp, + }, + }; } diff --git a/src/app/store/technical-records/reducers/technical-record-service.reducer.spec.ts b/src/app/store/technical-records/reducers/technical-record-service.reducer.spec.ts index 9bb48cd36b..8d2ae2ccc4 100644 --- a/src/app/store/technical-records/reducers/technical-record-service.reducer.spec.ts +++ b/src/app/store/technical-records/reducers/technical-record-service.reducer.spec.ts @@ -1,7 +1,11 @@ import { TechRecordSearchSchema } from '@dvsa/cvs-type-definitions/types/v3/tech-record/get/search'; +import { TechRecordType as NonVerbTechRecordType } from '@dvsa/cvs-type-definitions/types/v3/tech-record/tech-record-vehicle-type'; import { TechRecordType } from '@dvsa/cvs-type-definitions/types/v3/tech-record/tech-record-verb'; import { - TechRecordPUTHGV, TechRecordPUTLGV, TechRecordPUTTRL, TechRecordType as TechRecordTypeVehicleVerb, + TechRecordPUTHGV, + TechRecordPUTLGV, + TechRecordPUTTRL, + TechRecordType as TechRecordTypeVehicleVerb, } from '@dvsa/cvs-type-definitions/types/v3/tech-record/tech-record-verb-vehicle-type'; import { createMockHgv } from '@mocks/hgv-record.mock'; import { createMockLgv } from '@mocks/lgv-record.mock'; @@ -9,615 +13,681 @@ import { createMockTrl } from '@mocks/trl-record.mock'; import { BodyTypeCode, BodyTypeDescription } from '@models/body-type-enum'; import { PsvMake } from '@models/reference-data.model'; import { VehicleTypes } from '@models/vehicle-tech-record.model'; -import { - TechRecordType as NonVerbTechRecordType, -} from '@dvsa/cvs-type-definitions/types/v3/tech-record/tech-record-vehicle-type'; import { mockVehicleTechnicalRecord } from '../../../../mocks/mock-vehicle-technical-record.mock'; import { - addAxle, - addSectionState, - archiveTechRecord, - archiveTechRecordFailure, - archiveTechRecordSuccess, - clearADRDetailsBeforeUpdate, - clearAllSectionStates, - clearScrollPosition, - createVehicleRecord, - createVehicleRecordFailure, - createVehicleRecordSuccess, - getBySystemNumber, - getBySystemNumberFailure, - getBySystemNumberSuccess, - removeAxle, - removeSectionState, updateADRAdditionalExaminerNotes, - updateBody, - updateBrakeForces, - updateEditingTechRecord, - updateEditingTechRecordCancel, updateExistingADRAdditionalExaminerNote, - updateScrollPosition, - updateTechRecord, - updateTechRecordFailure, - updateTechRecordSuccess, + addAxle, + addSectionState, + archiveTechRecord, + archiveTechRecordFailure, + archiveTechRecordSuccess, + clearADRDetailsBeforeUpdate, + clearAllSectionStates, + clearScrollPosition, + createVehicleRecord, + createVehicleRecordFailure, + createVehicleRecordSuccess, + getBySystemNumber, + getBySystemNumberFailure, + getBySystemNumberSuccess, + removeAxle, + removeSectionState, + updateADRAdditionalExaminerNotes, + updateBody, + updateBrakeForces, + updateEditingTechRecord, + updateEditingTechRecordCancel, + updateExistingADRAdditionalExaminerNote, + updateScrollPosition, + updateTechRecord, + updateTechRecordFailure, + updateTechRecordSuccess, } from '../actions/technical-record-service.actions'; -import { TechnicalRecordServiceState, initialState, vehicleTechRecordReducer } from './technical-record-service.reducer'; +import { + TechnicalRecordServiceState, + initialState, + vehicleTechRecordReducer, +} from './technical-record-service.reducer'; describe('Vehicle Technical Record Reducer', () => { - describe('unknown action', () => { - it('should return the default state', () => { - const action = { - type: 'Unknown', - }; - const state = vehicleTechRecordReducer(initialState, action); - - expect(state).toBe(initialState); - }); - }); - - describe('getBySystemNumber', () => { - it('should set all vehicle technical records', () => { - const newState: TechnicalRecordServiceState = { ...initialState, loading: true }; - const action = getBySystemNumber({ systemNumber: '001' }); - const state = vehicleTechRecordReducer(initialState, action); - - expect(state).toEqual(newState); - expect(state).not.toBe(newState); - }); - }); - - describe('getBySystemNumberSuccess', () => { - it('should set all vehicle technical records', () => { - const record = { systemNumber: 'foo', createdTimestamp: 'bar', vin: 'testVin' } as TechRecordSearchSchema; - const newState: TechnicalRecordServiceState = { - ...initialState, - techRecordHistory: [{ systemNumber: 'foo', createdTimestamp: 'bar', vin: 'testVin' } as TechRecordSearchSchema], - }; - const action = getBySystemNumberSuccess({ techRecordHistory: [record] }); - const state = vehicleTechRecordReducer(initialState, action); - - expect(state).toEqual(newState); - expect(state).not.toBe(newState); - }); - }); - - describe('getBySystemNumberFailure', () => { - it('should history to an empty array', () => { - const newState = { ...initialState, techRecordHistory: [] }; - const action = getBySystemNumberFailure({ error: 'error' }); - const state = vehicleTechRecordReducer(initialState, action); - - expect(state).toEqual(newState); - expect(state).not.toBe(newState); - }); - }); - - describe('createVehicleRecord', () => { - it('should set loading to true', () => { - const expectedVehicle = { systemNumber: 'foo', createdTimestamp: 'bar', vin: 'testVin' } as TechRecordType<'get'>; - - const oldState: TechnicalRecordServiceState = { ...initialState, vehicleTechRecord: expectedVehicle, loading: false }; - - const newState = vehicleTechRecordReducer(oldState, createVehicleRecord({ vehicle: {} as TechRecordType<'put'> })); - - expect(newState).not.toBe(oldState); - expect(newState.vehicleTechRecord).toEqual(expectedVehicle); - expect(newState.loading).toBeTruthy(); - }); - }); - - describe('createVehicleRecordSuccess', () => { - it('should update the vehicleTechRecords property of the state with the newly created vehicle and set loading to false', () => { - const oldState: TechnicalRecordServiceState = { - ...initialState, - vehicleTechRecord: { systemNumber: 'foo', createdTimestamp: 'bar', vin: 'testVin' } as TechRecordType<'get'>, - }; - - const expectedVehicle = { systemNumber: 'foo', createdTimestamp: 'bar', vin: 'testVin' } as TechRecordType<'get'>; - - const action = createVehicleRecordSuccess({ vehicleTechRecord: expectedVehicle }); - - const newState = vehicleTechRecordReducer(oldState, action); - - expect(newState.loading).toBeFalsy(); - }); - }); - - describe('createVehicleRecordFailure', () => { - it('should add an error to the state and set loading to false', () => { - const action = createVehicleRecordFailure({ error: 'something bad happened' }); - const newState = vehicleTechRecordReducer(initialState, action); - - expect(newState.loading).toBeFalsy(); - }); - }); - - describe('updateTechRecords', () => { - it('should set the new vehicle tech records state after update', () => { - const state: TechnicalRecordServiceState = { - ...initialState, - vehicleTechRecord: { systemNumber: 'foo', createdTimestamp: 'bar', vin: 'testVin' } as TechRecordType<'get'>, - loading: true, - }; - const action = updateTechRecord({ systemNumber: 'foo', createdTimestamp: 'bar' }); - const newState = vehicleTechRecordReducer(state, action); - - expect(newState).toEqual(state); - expect(newState).not.toBe(state); - expect(newState.loading).toBe(true); - }); - }); - - describe('updateTechRecordsSuccess', () => { - it('should set the new vehicle tech records state after update success', () => { - const oldRecord = { systemNumber: 'foo', createdTimestamp: 'bar', vin: 'testVin' } as TechRecordType<'get'>; - const newRecord = { systemNumber: 'foo', createdTimestamp: 'bar', vin: 'testVinNew' } as TechRecordType<'get'>; - - const state: TechnicalRecordServiceState = { - ...initialState, - vehicleTechRecord: oldRecord, - }; - const action = updateTechRecordSuccess({ vehicleTechRecord: newRecord }); - const newState = vehicleTechRecordReducer(state, action); - - expect(state).not.toEqual(newState); - expect(newState.vehicleTechRecord).toEqual(newRecord); - }); - }); - - describe('updateTechRecordsFailure', () => { - it('should set error state', () => { - const error = 'fetching vehicle tech records failed'; - const action = updateTechRecordFailure({ error }); - const newState = vehicleTechRecordReducer(initialState, action); - - expect(initialState).not.toEqual(newState); - expect(newState.error).toEqual(error); - expect(initialState).not.toBe(newState); - }); - }); - - describe('archiveTechRecord', () => { - it('should set the state to loading', () => { - const state: TechnicalRecordServiceState = { - ...initialState, - vehicleTechRecord: { systemNumber: 'foo', createdTimestamp: 'bar', vin: 'testVin' } as TechRecordType<'get'>, - loading: true, - }; - const action = archiveTechRecord({ systemNumber: 'foo', createdTimestamp: 'bar', reasonForArchiving: 'some reason' }); - const newState = vehicleTechRecordReducer(state, action); - - expect(state).toEqual(newState); - expect(state).not.toBe(newState); - expect(state.loading).toBe(true); - }); - }); - - describe('archiveTechRecordSuccess', () => { - it('should set the new vehicle tech records state after update success', () => { - const oldRecord = { systemNumber: 'foo', createdTimestamp: 'bar', vin: 'testVin' } as unknown as TechRecordType<'get'>; - const newRecord = { systemNumber: 'foo', createdTimestamp: 'bar', vin: 'testVinNew' } as unknown as TechRecordType<'get'>; - - const state: TechnicalRecordServiceState = { - ...initialState, - vehicleTechRecord: oldRecord, - }; - const action = archiveTechRecordSuccess({ vehicleTechRecord: newRecord }); - const newState = vehicleTechRecordReducer(state, action); - - expect(state).not.toEqual(newState); - expect(newState.vehicleTechRecord).toEqual(newRecord); - }); - }); - - describe('archiveTechRecordFailure', () => { - it('should set error state', () => { - const error = 'fetching vehicle tech records failed'; - const action = archiveTechRecordFailure({ error }); - const newState = vehicleTechRecordReducer(initialState, action); - - expect(initialState).not.toEqual(newState); - expect(newState.error).toEqual(error); - expect(initialState).not.toBe(newState); - }); - }); - - describe('updateEditingTechRecord', () => { - it('should set the editingTechRecord', () => { - const vehicleTechRecord = { systemNumber: 'foo', createdTimestamp: 'bar', vin: 'testVin' } as unknown as TechRecordType<'put'>; - const action = updateEditingTechRecord({ vehicleTechRecord }); - const newState = vehicleTechRecordReducer(initialState, action); - - expect(initialState).not.toEqual(newState); - expect(newState.editingTechRecord).toEqual(vehicleTechRecord); - expect(initialState).not.toBe(newState); - }); - }); - - describe('updateEditingTechRecordCancel', () => { - it('should clear the state', () => { - initialState.editingTechRecord = { systemNumber: 'foo', createdTimestamp: 'bar', vin: 'testVin' } as unknown as TechRecordType<'put'>; - const action = updateEditingTechRecordCancel(); - const newState = vehicleTechRecordReducer(initialState, action); - - expect(initialState).not.toEqual(newState); - expect(newState.editingTechRecord).toBeUndefined(); - expect(initialState).not.toBe(newState); - }); - }); - - describe('Section state changes', () => { - it('should add the section name to the state', () => { - initialState.sectionState = []; - const action = addSectionState({ section: 'TEST_SECTION' }); - const newState = vehicleTechRecordReducer(initialState, action); - - expect(initialState).not.toEqual(newState); - expect(newState.sectionState).toEqual(['TEST_SECTION']); - expect(initialState).not.toBe(newState); - }); - - it('should avoid duplicating the section name', () => { - initialState.sectionState = ['TEST_SECTION']; - const action = addSectionState({ section: 'TEST_SECTION' }); - const newState = vehicleTechRecordReducer(initialState, action); - - expect(initialState).toEqual(newState); - expect(newState.sectionState).toEqual(['TEST_SECTION']); - expect(initialState).not.toBe(newState); - }); - - it('should remove the section name from the state', () => { - initialState.sectionState = ['TEST_SECTION1', 'TEST_SECTION2']; - const action = removeSectionState({ section: 'TEST_SECTION1' }); - const newState = vehicleTechRecordReducer(initialState, action); - - expect(initialState).not.toEqual(newState); - expect(newState.sectionState).toEqual(['TEST_SECTION2']); - expect(initialState).not.toBe(newState); - }); - - it('should clear all the section names from the state', () => { - initialState.sectionState = ['TEST_SECTION1', 'TEST_SECTION2']; - const action = clearAllSectionStates(); - const newState = vehicleTechRecordReducer(initialState, action); - - expect(initialState).not.toEqual(newState); - expect(newState.sectionState).toEqual([]); - expect(initialState).not.toBe(newState); - }); - }); - // TODO V3 HGV/PSV tests - describe('updating properties of the tech record in edit', () => { - beforeEach(() => { - initialState.editingTechRecord = mockVehicleTechnicalRecord(VehicleTypes.PSV) as TechRecordType<'put'>; - }); - - describe('updateBrakeForces', () => { - it('should not update half locked brake forces with no gross kerb weight', () => { - const asPSV = initialState.editingTechRecord as TechRecordTypeVehicleVerb<'psv', 'put'>; - asPSV.techRecord_brakes_brakeCodeOriginal = '80'; - asPSV.techRecord_brakes_brakeForceWheelsUpToHalfLocked_parkingBrakeForceB = 50; - asPSV.techRecord_brakes_brakeForceWheelsUpToHalfLocked_secondaryBrakeForceB = 30; - asPSV.techRecord_brakes_brakeForceWheelsUpToHalfLocked_serviceBrakeForceB = 10; - expect(asPSV.techRecord_brakes_brakeCode).toBe('1234'); - - const newState = vehicleTechRecordReducer(initialState, updateBrakeForces({ grossLadenWeight: 2000 })); - - const newPSV = newState.editingTechRecord as TechRecordTypeVehicleVerb<'psv', 'put'>; - - expect(newPSV.techRecord_brakes_brakeCode).toBe(`0${2000 / 100}${asPSV.techRecord_brakes_brakeCodeOriginal}`); - - expect(newPSV.techRecord_brakes_brakeForceWheelsNotLocked_serviceBrakeForceA).toBe(Math.round((2000 * 16) / 100)); - expect(newPSV.techRecord_brakes_brakeForceWheelsNotLocked_secondaryBrakeForceA).toBe(Math.round((2000 * 22.5) / 100)); - expect(newPSV.techRecord_brakes_brakeForceWheelsNotLocked_parkingBrakeForceA).toBe(Math.round((2000 * 45) / 100)); - - expect(newPSV.techRecord_brakes_brakeForceWheelsUpToHalfLocked_serviceBrakeForceB).toBe(10); - expect(newPSV.techRecord_brakes_brakeForceWheelsUpToHalfLocked_secondaryBrakeForceB).toBe(30); - expect(newPSV.techRecord_brakes_brakeForceWheelsUpToHalfLocked_parkingBrakeForceB).toBe(50); - }); - - it('should not update half locked brake forces with no gross laden weight', () => { - const asPSV = initialState.editingTechRecord as TechRecordTypeVehicleVerb<'psv', 'put'>; - asPSV.techRecord_brakes_brakeCodeOriginal = '80'; - asPSV.techRecord_brakes_brakeForceWheelsNotLocked_parkingBrakeForceA = 50; - asPSV.techRecord_brakes_brakeForceWheelsNotLocked_secondaryBrakeForceA = 30; - asPSV.techRecord_brakes_brakeForceWheelsNotLocked_serviceBrakeForceA = 10; - - const newState = vehicleTechRecordReducer(initialState, updateBrakeForces({ grossKerbWeight: 1000 })); - - const newPSV = newState.editingTechRecord as TechRecordTypeVehicleVerb<'psv', 'put'>; - - expect(newPSV.techRecord_brakes_brakeCode).toBe('1234'); - - expect(newPSV.techRecord_brakes_brakeForceWheelsNotLocked_serviceBrakeForceA).toBe(10); - expect(newPSV.techRecord_brakes_brakeForceWheelsNotLocked_secondaryBrakeForceA).toBe(30); - expect(newPSV.techRecord_brakes_brakeForceWheelsNotLocked_parkingBrakeForceA).toBe(50); - - expect(newPSV.techRecord_brakes_brakeForceWheelsUpToHalfLocked_serviceBrakeForceB).toBe(Math.round((1000 * 16) / 100)); - expect(newPSV.techRecord_brakes_brakeForceWheelsUpToHalfLocked_secondaryBrakeForceB).toBe(Math.round((1000 * 25) / 100)); - expect(newPSV.techRecord_brakes_brakeForceWheelsUpToHalfLocked_parkingBrakeForceB).toBe(Math.round((1000 * 50) / 100)); - }); - - it('should not update brakeCode with no gross laden weight', () => { - const asPSV = initialState.editingTechRecord as TechRecordTypeVehicleVerb<'psv', 'put'>; - asPSV.techRecord_brakes_brakeCodeOriginal = '80'; - asPSV.techRecord_brakes_brakeCode = '37'; - - const newState = vehicleTechRecordReducer(initialState, updateBrakeForces({ grossKerbWeight: 1000 })); - expect((newState?.editingTechRecord as TechRecordTypeVehicleVerb<'psv', 'put'>).techRecord_brakes_brakeCode).toBe('37'); - }); - - it('should update brake forces', () => { - const asPSV = initialState.editingTechRecord as TechRecordTypeVehicleVerb<'psv', 'put'>; - asPSV.techRecord_brakes_brakeCodeOriginal = '80'; - expect(asPSV.techRecord_brakes_brakeCode).toBe('1234'); - - const newState = vehicleTechRecordReducer(initialState, updateBrakeForces({ grossKerbWeight: 1000, grossLadenWeight: 2000 })); - - expect(newState).not.toBe(initialState); - expect(newState).not.toEqual(initialState); - - const newPSV = newState.editingTechRecord as TechRecordTypeVehicleVerb<'psv', 'put'>; - expect(newPSV.techRecord_brakes_brakeCode).toBe(`0${2000 / 100}${asPSV.techRecord_brakes_brakeCodeOriginal}`); - - expect(newPSV.techRecord_brakes_brakeForceWheelsNotLocked_serviceBrakeForceA).toBe(Math.round((2000 * 16) / 100)); - expect(newPSV.techRecord_brakes_brakeForceWheelsNotLocked_secondaryBrakeForceA).toBe(Math.round((2000 * 22.5) / 100)); - expect(newPSV.techRecord_brakes_brakeForceWheelsNotLocked_parkingBrakeForceA).toBe(Math.round((2000 * 45) / 100)); - - expect(newPSV.techRecord_brakes_brakeForceWheelsUpToHalfLocked_serviceBrakeForceB).toBe(Math.round((1000 * 16) / 100)); - expect(newPSV.techRecord_brakes_brakeForceWheelsUpToHalfLocked_secondaryBrakeForceB).toBe(Math.round((1000 * 25) / 100)); - expect(newPSV.techRecord_brakes_brakeForceWheelsUpToHalfLocked_parkingBrakeForceB).toBe(Math.round((1000 * 50) / 100)); - }); - }); - - describe('updateBody', () => { - it('should update body', () => { - expect((initialState.editingTechRecord as TechRecordTypeVehicleVerb<'psv', 'put'>).techRecord_bodyType_description).toBe( - BodyTypeDescription.DOUBLE_DECKER, - ); - - const expectedData = { - dtpNumber: '9999', - psvChassisMake: 'Toyota', - psvChassisModel: 'Supra', - psvBodyMake: 'Random', - psvBodyType: 'o', - } as PsvMake; - - const newState = vehicleTechRecordReducer(initialState, updateBody({ psvMake: expectedData })); - - expect(newState).not.toBe(initialState); - expect(newState).not.toEqual(initialState); - - const updatedTechRecord = newState.editingTechRecord as TechRecordTypeVehicleVerb<'psv', 'put'>; - expect(updatedTechRecord?.techRecord_bodyType_code).toBe(BodyTypeCode.O); - expect(updatedTechRecord?.techRecord_bodyType_description).toBe(BodyTypeDescription.OTHER); - expect(updatedTechRecord?.techRecord_bodyMake).toBe(expectedData.psvBodyMake); - expect(updatedTechRecord?.techRecord_chassisMake).toBe(expectedData.psvChassisMake); - expect(updatedTechRecord?.techRecord_chassisModel).toBe(expectedData.psvChassisModel); - }); - }); - - describe('addAxle', () => { - describe('it should add an axle', () => { - it('with the axles property defined', () => { - const techRecord = initialState.editingTechRecord as TechRecordTypeVehicleVerb<'psv', 'put'>; - expect(techRecord?.techRecord_noOfAxles).toBe(2); - expect(techRecord?.techRecord_axles?.length).toBe(3); - - const newState = vehicleTechRecordReducer(initialState, addAxle()); - - expect(newState).not.toBe(initialState); - expect(newState).not.toEqual(initialState); - - const updatedTechRecord = newState.editingTechRecord as TechRecordTypeVehicleVerb<'psv', 'put'>; - expect(updatedTechRecord?.techRecord_noOfAxles).toBe(4); - expect(updatedTechRecord?.techRecord_axles?.length).toBe(4); - - const newAxleField = updatedTechRecord?.techRecord_axles ?? []; - - expect(newAxleField[3].tyres_dataTrAxles).toBeNull(); - expect(newAxleField[3].tyres_fitmentCode).toBeNull(); - expect(newAxleField[3].tyres_plyRating).toBeNull(); - expect(newAxleField[3].tyres_speedCategorySymbol).toBeNull(); - expect(newAxleField[3].tyres_tyreCode).toBeNull(); - expect(newAxleField[3].tyres_tyreSize).toBeNull(); - - expect(newAxleField[3].weights_designWeight).toBeDefined(); - expect(newAxleField[3].weights_gbWeight).toBeDefined(); - expect(newAxleField[3].weights_kerbWeight).toBeDefined(); - expect(newAxleField[3].weights_ladenWeight).toBeDefined(); - expect(updatedTechRecord?.techRecord_axles?.pop()?.axleNumber).toBe(4); - }); - - it('without the axles property defined', () => { - const techRecord = initialState.editingTechRecord as TechRecordTypeVehicleVerb<'psv', 'put'>; - delete techRecord?.techRecord_axles; - techRecord.techRecord_noOfAxles = 0; - expect(techRecord?.techRecord_noOfAxles).toBe(0); - - const newState = vehicleTechRecordReducer(initialState, addAxle()); - - expect(newState).not.toBe(initialState); - expect(newState).not.toEqual(initialState); - - const updatedTechRecord = newState.editingTechRecord as TechRecordTypeVehicleVerb<'psv', 'put'>; - expect(updatedTechRecord?.techRecord_noOfAxles).toBe(1); - expect(updatedTechRecord?.techRecord_axles?.length).toBe(1); - - const newAxleField = updatedTechRecord?.techRecord_axles || []; - - expect(newAxleField[0].tyres_dataTrAxles).toBeNull(); - expect(newAxleField[0].tyres_fitmentCode).toBeNull(); - expect(newAxleField[0].tyres_plyRating).toBeNull(); - expect(newAxleField[0].tyres_speedCategorySymbol).toBeNull(); - expect(newAxleField[0].tyres_tyreCode).toBeNull(); - expect(newAxleField[0].tyres_tyreSize).toBeNull(); - - expect(newAxleField[0].weights_designWeight).toBeDefined(); - expect(newAxleField[0].weights_gbWeight).toBeDefined(); - expect(newAxleField[0].weights_kerbWeight).toBeDefined(); - expect(newAxleField[0].weights_ladenWeight).toBeDefined(); - expect(updatedTechRecord?.techRecord_axles?.pop()?.axleNumber).toBe(1); - }); - }); - }); - - describe('removeAxle', () => { - it('should remove specified axle', () => { - const techRecord = initialState.editingTechRecord as TechRecordTypeVehicleVerb<'psv', 'put'>; - expect(techRecord?.techRecord_noOfAxles).toBe(2); - expect(techRecord?.techRecord_axles?.length).toBe(3); - - const newState = vehicleTechRecordReducer(initialState, removeAxle({ index: 0 })); - - expect(newState).not.toBe(initialState); - expect(newState).not.toEqual(initialState); - - const updatedTechRecord = newState.editingTechRecord as TechRecordTypeVehicleVerb<'psv', 'put'>; - expect(updatedTechRecord?.techRecord_noOfAxles).toBe(2); - expect(updatedTechRecord?.techRecord_axles?.length).toBe(2); - expect(updatedTechRecord?.techRecord_axles?.pop()?.axleNumber).toBe(2); - }); - }); - describe('updateScrollPosition', () => { - it('should update the scroll position state', () => { - const newState = vehicleTechRecordReducer(initialState, updateScrollPosition({ position: [1, 2] })); - - expect(newState.scrollPosition).toEqual([1, 2]); - }); - }); - describe('clearScrollPosition', () => { - it('should reset the scroll position', () => { - initialState.scrollPosition = [2, 2]; - const newState = vehicleTechRecordReducer(initialState, clearScrollPosition()); - - expect(newState.scrollPosition).toEqual([0, 0]); - }); - }); - }); - - describe('clearADRDetailsBeforeUpdate', () => { - it('should set all ADR fields to null when: vehicleType = HGV and dangerousGoods = false', () => { - initialState.editingTechRecord = createMockHgv(1234) as TechRecordPUTHGV; - initialState.editingTechRecord.techRecord_adrDetails_dangerousGoods = false; - initialState.editingTechRecord.techRecord_adrDetails_applicantDetails_city = 'Test City'; - - const newState = vehicleTechRecordReducer(initialState, clearADRDetailsBeforeUpdate()); - expect(newState.editingTechRecord).toBeDefined(); - expect((newState.editingTechRecord as TechRecordPUTHGV).techRecord_adrDetails_applicantDetails_city).toBeNull(); - }); - - it('should set all ADR fields to null when: vehicleType = TRL and dangerousGoods = false', () => { - initialState.editingTechRecord = createMockTrl(1234) as TechRecordPUTTRL; - initialState.editingTechRecord.techRecord_adrDetails_dangerousGoods = false; - initialState.editingTechRecord.techRecord_adrDetails_applicantDetails_street = 'Test Street'; - - const newState = vehicleTechRecordReducer(initialState, clearADRDetailsBeforeUpdate()); - expect(newState.editingTechRecord).toBeDefined(); - expect((newState.editingTechRecord as TechRecordPUTTRL).techRecord_adrDetails_applicantDetails_street).toBeNull(); - }); - - it('should set all ADR fields to null when: vehicleType = LGV and dangerousGoods = false', () => { - initialState.editingTechRecord = createMockLgv(1234) as TechRecordPUTLGV; - initialState.editingTechRecord.techRecord_adrDetails_dangerousGoods = false; - initialState.editingTechRecord.techRecord_adrDetails_adrCertificateNotes = 'Test notes'; - - const newState = vehicleTechRecordReducer(initialState, clearADRDetailsBeforeUpdate()); - expect(newState.editingTechRecord).toBeDefined(); - expect((newState.editingTechRecord as TechRecordPUTLGV).techRecord_adrDetails_applicantDetails_street).toBeNull(); - }); - - it('should not set all ADR fields to null when: vehicleType = HGV and dangerousGoods = true', () => { - initialState.editingTechRecord = createMockHgv(1234) as TechRecordPUTHGV; - initialState.editingTechRecord.techRecord_adrDetails_dangerousGoods = true; - initialState.editingTechRecord.techRecord_adrDetails_applicantDetails_town = 'Test Town'; - - const newState = vehicleTechRecordReducer(initialState, clearADRDetailsBeforeUpdate()); - expect(newState.editingTechRecord).toBeDefined(); - expect((newState.editingTechRecord as TechRecordPUTHGV).techRecord_adrDetails_applicantDetails_town).toBe('Test Town'); - }); - - it('should not set all ADR fields to null when: vehicleType = TRL and dangerousGoods = true', () => { - initialState.editingTechRecord = createMockTrl(1234) as TechRecordPUTTRL; - initialState.editingTechRecord.techRecord_adrDetails_dangerousGoods = true; - initialState.editingTechRecord.techRecord_adrDetails_applicantDetails_postcode = 'Test Postcode'; - - const newState = vehicleTechRecordReducer(initialState, clearADRDetailsBeforeUpdate()); - expect(newState.editingTechRecord).toBeDefined(); - expect((newState.editingTechRecord as TechRecordPUTTRL).techRecord_adrDetails_applicantDetails_postcode).toBe('Test Postcode'); - }); - - it('should not set all ADR fields to null when: vehicleType = LGV and dangerousGoods = true', () => { - initialState.editingTechRecord = createMockLgv(1234) as TechRecordPUTLGV; - initialState.editingTechRecord.techRecord_adrDetails_dangerousGoods = true; - initialState.editingTechRecord.techRecord_adrDetails_adrCertificateNotes = 'Test notes'; - - const newState = vehicleTechRecordReducer(initialState, clearADRDetailsBeforeUpdate()); - expect(newState.editingTechRecord).toBeDefined(); - expect((newState.editingTechRecord as TechRecordPUTLGV).techRecord_adrDetails_adrCertificateNotes).toBe('Test notes'); - }); - - }); - describe('handleADRExaminerNoteChanges', () => { - beforeEach(() => { - const mockedDate = new Date(2024, 5, 20); - jest.useFakeTimers(); - jest.setSystemTime(mockedDate); - }); - - afterEach(() => { - jest.useRealTimers(); - }); - it('should handle any changes made to the adr examiner notes', () => { - const testNote = { - note: 'testNote', - createdAtDate: new Date().toISOString(), - lastUpdatedBy: 'someone', - }; - const state: TechnicalRecordServiceState = { - ...initialState, - vehicleTechRecord: { - systemNumber: 'foo', - createdTimestamp: 'bar', - vin: 'testVin', - } as unknown as TechRecordType<'get'>, - editingTechRecord: { - systemNumber: 'foo', - createdTimestamp: 'bar', - vin: 'testVin', - techRecord_adrDetails_additionalExaminerNotes_note: testNote.note, - } as unknown as TechRecordType<'put'>, - loading: true, - }; - const action = updateADRAdditionalExaminerNotes({ username: testNote.lastUpdatedBy }); - const newState = vehicleTechRecordReducer(state, action); - expect((newState.editingTechRecord as unknown as (NonVerbTechRecordType<'hgv' | 'lgv' | 'trl'>))?.techRecord_adrDetails_additionalExaminerNotes) - .toContainEqual(testNote); - }); - }); - describe('handleUpdateExistingADRExaminerNote', () => { - it('should', () => { - const state: TechnicalRecordServiceState = { - ...initialState, - editingTechRecord: { - systemNumber: 'foo', - createdTimestamp: 'bar', - vin: 'testVin', - techRecord_adrDetails_additionalExaminerNotes: [ - { - note: 'foo', - createdAtDate: 'bar', - lastUpdatedBy: 'foo', - }, - ], - } as unknown as TechRecordType<'put'>, - loading: true, - }; - const newNote = 'foobar'; - const action = updateExistingADRAdditionalExaminerNote({ additionalExaminerNote: newNote, examinerNoteIndex: 0 }); - const newState = vehicleTechRecordReducer(state, action); - const editingTechRecord = newState.editingTechRecord as unknown as (NonVerbTechRecordType<'hgv' | 'lgv' | 'trl'>); - expect(editingTechRecord.techRecord_adrDetails_additionalExaminerNotes![0].note).toEqual(newNote); - }); - }); + describe('unknown action', () => { + it('should return the default state', () => { + const action = { + type: 'Unknown', + }; + const state = vehicleTechRecordReducer(initialState, action); + + expect(state).toBe(initialState); + }); + }); + + describe('getBySystemNumber', () => { + it('should set all vehicle technical records', () => { + const newState: TechnicalRecordServiceState = { ...initialState, loading: true }; + const action = getBySystemNumber({ systemNumber: '001' }); + const state = vehicleTechRecordReducer(initialState, action); + + expect(state).toEqual(newState); + expect(state).not.toBe(newState); + }); + }); + + describe('getBySystemNumberSuccess', () => { + it('should set all vehicle technical records', () => { + const record = { systemNumber: 'foo', createdTimestamp: 'bar', vin: 'testVin' } as TechRecordSearchSchema; + const newState: TechnicalRecordServiceState = { + ...initialState, + techRecordHistory: [{ systemNumber: 'foo', createdTimestamp: 'bar', vin: 'testVin' } as TechRecordSearchSchema], + }; + const action = getBySystemNumberSuccess({ techRecordHistory: [record] }); + const state = vehicleTechRecordReducer(initialState, action); + + expect(state).toEqual(newState); + expect(state).not.toBe(newState); + }); + }); + + describe('getBySystemNumberFailure', () => { + it('should history to an empty array', () => { + const newState = { ...initialState, techRecordHistory: [] }; + const action = getBySystemNumberFailure({ error: 'error' }); + const state = vehicleTechRecordReducer(initialState, action); + + expect(state).toEqual(newState); + expect(state).not.toBe(newState); + }); + }); + + describe('createVehicleRecord', () => { + it('should set loading to true', () => { + const expectedVehicle = { systemNumber: 'foo', createdTimestamp: 'bar', vin: 'testVin' } as TechRecordType<'get'>; + + const oldState: TechnicalRecordServiceState = { + ...initialState, + vehicleTechRecord: expectedVehicle, + loading: false, + }; + + const newState = vehicleTechRecordReducer( + oldState, + createVehicleRecord({ vehicle: {} as TechRecordType<'put'> }) + ); + + expect(newState).not.toBe(oldState); + expect(newState.vehicleTechRecord).toEqual(expectedVehicle); + expect(newState.loading).toBeTruthy(); + }); + }); + + describe('createVehicleRecordSuccess', () => { + it('should update the vehicleTechRecords property of the state with the newly created vehicle and set loading to false', () => { + const oldState: TechnicalRecordServiceState = { + ...initialState, + vehicleTechRecord: { systemNumber: 'foo', createdTimestamp: 'bar', vin: 'testVin' } as TechRecordType<'get'>, + }; + + const expectedVehicle = { systemNumber: 'foo', createdTimestamp: 'bar', vin: 'testVin' } as TechRecordType<'get'>; + + const action = createVehicleRecordSuccess({ vehicleTechRecord: expectedVehicle }); + + const newState = vehicleTechRecordReducer(oldState, action); + + expect(newState.loading).toBeFalsy(); + }); + }); + + describe('createVehicleRecordFailure', () => { + it('should add an error to the state and set loading to false', () => { + const action = createVehicleRecordFailure({ error: 'something bad happened' }); + const newState = vehicleTechRecordReducer(initialState, action); + + expect(newState.loading).toBeFalsy(); + }); + }); + + describe('updateTechRecords', () => { + it('should set the new vehicle tech records state after update', () => { + const state: TechnicalRecordServiceState = { + ...initialState, + vehicleTechRecord: { systemNumber: 'foo', createdTimestamp: 'bar', vin: 'testVin' } as TechRecordType<'get'>, + loading: true, + }; + const action = updateTechRecord({ systemNumber: 'foo', createdTimestamp: 'bar' }); + const newState = vehicleTechRecordReducer(state, action); + + expect(newState).toEqual(state); + expect(newState).not.toBe(state); + expect(newState.loading).toBe(true); + }); + }); + + describe('updateTechRecordsSuccess', () => { + it('should set the new vehicle tech records state after update success', () => { + const oldRecord = { systemNumber: 'foo', createdTimestamp: 'bar', vin: 'testVin' } as TechRecordType<'get'>; + const newRecord = { systemNumber: 'foo', createdTimestamp: 'bar', vin: 'testVinNew' } as TechRecordType<'get'>; + + const state: TechnicalRecordServiceState = { + ...initialState, + vehicleTechRecord: oldRecord, + }; + const action = updateTechRecordSuccess({ vehicleTechRecord: newRecord }); + const newState = vehicleTechRecordReducer(state, action); + + expect(state).not.toEqual(newState); + expect(newState.vehicleTechRecord).toEqual(newRecord); + }); + }); + + describe('updateTechRecordsFailure', () => { + it('should set error state', () => { + const error = 'fetching vehicle tech records failed'; + const action = updateTechRecordFailure({ error }); + const newState = vehicleTechRecordReducer(initialState, action); + + expect(initialState).not.toEqual(newState); + expect(newState.error).toEqual(error); + expect(initialState).not.toBe(newState); + }); + }); + + describe('archiveTechRecord', () => { + it('should set the state to loading', () => { + const state: TechnicalRecordServiceState = { + ...initialState, + vehicleTechRecord: { systemNumber: 'foo', createdTimestamp: 'bar', vin: 'testVin' } as TechRecordType<'get'>, + loading: true, + }; + const action = archiveTechRecord({ + systemNumber: 'foo', + createdTimestamp: 'bar', + reasonForArchiving: 'some reason', + }); + const newState = vehicleTechRecordReducer(state, action); + + expect(state).toEqual(newState); + expect(state).not.toBe(newState); + expect(state.loading).toBe(true); + }); + }); + + describe('archiveTechRecordSuccess', () => { + it('should set the new vehicle tech records state after update success', () => { + const oldRecord = { + systemNumber: 'foo', + createdTimestamp: 'bar', + vin: 'testVin', + } as unknown as TechRecordType<'get'>; + const newRecord = { + systemNumber: 'foo', + createdTimestamp: 'bar', + vin: 'testVinNew', + } as unknown as TechRecordType<'get'>; + + const state: TechnicalRecordServiceState = { + ...initialState, + vehicleTechRecord: oldRecord, + }; + const action = archiveTechRecordSuccess({ vehicleTechRecord: newRecord }); + const newState = vehicleTechRecordReducer(state, action); + + expect(state).not.toEqual(newState); + expect(newState.vehicleTechRecord).toEqual(newRecord); + }); + }); + + describe('archiveTechRecordFailure', () => { + it('should set error state', () => { + const error = 'fetching vehicle tech records failed'; + const action = archiveTechRecordFailure({ error }); + const newState = vehicleTechRecordReducer(initialState, action); + + expect(initialState).not.toEqual(newState); + expect(newState.error).toEqual(error); + expect(initialState).not.toBe(newState); + }); + }); + + describe('updateEditingTechRecord', () => { + it('should set the editingTechRecord', () => { + const vehicleTechRecord = { + systemNumber: 'foo', + createdTimestamp: 'bar', + vin: 'testVin', + } as unknown as TechRecordType<'put'>; + const action = updateEditingTechRecord({ vehicleTechRecord }); + const newState = vehicleTechRecordReducer(initialState, action); + + expect(initialState).not.toEqual(newState); + expect(newState.editingTechRecord).toEqual(vehicleTechRecord); + expect(initialState).not.toBe(newState); + }); + }); + + describe('updateEditingTechRecordCancel', () => { + it('should clear the state', () => { + initialState.editingTechRecord = { + systemNumber: 'foo', + createdTimestamp: 'bar', + vin: 'testVin', + } as unknown as TechRecordType<'put'>; + const action = updateEditingTechRecordCancel(); + const newState = vehicleTechRecordReducer(initialState, action); + + expect(initialState).not.toEqual(newState); + expect(newState.editingTechRecord).toBeUndefined(); + expect(initialState).not.toBe(newState); + }); + }); + + describe('Section state changes', () => { + it('should add the section name to the state', () => { + initialState.sectionState = []; + const action = addSectionState({ section: 'TEST_SECTION' }); + const newState = vehicleTechRecordReducer(initialState, action); + + expect(initialState).not.toEqual(newState); + expect(newState.sectionState).toEqual(['TEST_SECTION']); + expect(initialState).not.toBe(newState); + }); + + it('should avoid duplicating the section name', () => { + initialState.sectionState = ['TEST_SECTION']; + const action = addSectionState({ section: 'TEST_SECTION' }); + const newState = vehicleTechRecordReducer(initialState, action); + + expect(initialState).toEqual(newState); + expect(newState.sectionState).toEqual(['TEST_SECTION']); + expect(initialState).not.toBe(newState); + }); + + it('should remove the section name from the state', () => { + initialState.sectionState = ['TEST_SECTION1', 'TEST_SECTION2']; + const action = removeSectionState({ section: 'TEST_SECTION1' }); + const newState = vehicleTechRecordReducer(initialState, action); + + expect(initialState).not.toEqual(newState); + expect(newState.sectionState).toEqual(['TEST_SECTION2']); + expect(initialState).not.toBe(newState); + }); + + it('should clear all the section names from the state', () => { + initialState.sectionState = ['TEST_SECTION1', 'TEST_SECTION2']; + const action = clearAllSectionStates(); + const newState = vehicleTechRecordReducer(initialState, action); + + expect(initialState).not.toEqual(newState); + expect(newState.sectionState).toEqual([]); + expect(initialState).not.toBe(newState); + }); + }); + // TODO V3 HGV/PSV tests + describe('updating properties of the tech record in edit', () => { + beforeEach(() => { + initialState.editingTechRecord = mockVehicleTechnicalRecord(VehicleTypes.PSV) as TechRecordType<'put'>; + }); + + describe('updateBrakeForces', () => { + it('should not update half locked brake forces with no gross kerb weight', () => { + const asPSV = initialState.editingTechRecord as TechRecordTypeVehicleVerb<'psv', 'put'>; + asPSV.techRecord_brakes_brakeCodeOriginal = '80'; + asPSV.techRecord_brakes_brakeForceWheelsUpToHalfLocked_parkingBrakeForceB = 50; + asPSV.techRecord_brakes_brakeForceWheelsUpToHalfLocked_secondaryBrakeForceB = 30; + asPSV.techRecord_brakes_brakeForceWheelsUpToHalfLocked_serviceBrakeForceB = 10; + expect(asPSV.techRecord_brakes_brakeCode).toBe('1234'); + + const newState = vehicleTechRecordReducer(initialState, updateBrakeForces({ grossLadenWeight: 2000 })); + + const newPSV = newState.editingTechRecord as TechRecordTypeVehicleVerb<'psv', 'put'>; + + expect(newPSV.techRecord_brakes_brakeCode).toBe(`0${2000 / 100}${asPSV.techRecord_brakes_brakeCodeOriginal}`); + + expect(newPSV.techRecord_brakes_brakeForceWheelsNotLocked_serviceBrakeForceA).toBe( + Math.round((2000 * 16) / 100) + ); + expect(newPSV.techRecord_brakes_brakeForceWheelsNotLocked_secondaryBrakeForceA).toBe( + Math.round((2000 * 22.5) / 100) + ); + expect(newPSV.techRecord_brakes_brakeForceWheelsNotLocked_parkingBrakeForceA).toBe( + Math.round((2000 * 45) / 100) + ); + + expect(newPSV.techRecord_brakes_brakeForceWheelsUpToHalfLocked_serviceBrakeForceB).toBe(10); + expect(newPSV.techRecord_brakes_brakeForceWheelsUpToHalfLocked_secondaryBrakeForceB).toBe(30); + expect(newPSV.techRecord_brakes_brakeForceWheelsUpToHalfLocked_parkingBrakeForceB).toBe(50); + }); + + it('should not update half locked brake forces with no gross laden weight', () => { + const asPSV = initialState.editingTechRecord as TechRecordTypeVehicleVerb<'psv', 'put'>; + asPSV.techRecord_brakes_brakeCodeOriginal = '80'; + asPSV.techRecord_brakes_brakeForceWheelsNotLocked_parkingBrakeForceA = 50; + asPSV.techRecord_brakes_brakeForceWheelsNotLocked_secondaryBrakeForceA = 30; + asPSV.techRecord_brakes_brakeForceWheelsNotLocked_serviceBrakeForceA = 10; + + const newState = vehicleTechRecordReducer(initialState, updateBrakeForces({ grossKerbWeight: 1000 })); + + const newPSV = newState.editingTechRecord as TechRecordTypeVehicleVerb<'psv', 'put'>; + + expect(newPSV.techRecord_brakes_brakeCode).toBe('1234'); + + expect(newPSV.techRecord_brakes_brakeForceWheelsNotLocked_serviceBrakeForceA).toBe(10); + expect(newPSV.techRecord_brakes_brakeForceWheelsNotLocked_secondaryBrakeForceA).toBe(30); + expect(newPSV.techRecord_brakes_brakeForceWheelsNotLocked_parkingBrakeForceA).toBe(50); + + expect(newPSV.techRecord_brakes_brakeForceWheelsUpToHalfLocked_serviceBrakeForceB).toBe( + Math.round((1000 * 16) / 100) + ); + expect(newPSV.techRecord_brakes_brakeForceWheelsUpToHalfLocked_secondaryBrakeForceB).toBe( + Math.round((1000 * 25) / 100) + ); + expect(newPSV.techRecord_brakes_brakeForceWheelsUpToHalfLocked_parkingBrakeForceB).toBe( + Math.round((1000 * 50) / 100) + ); + }); + + it('should not update brakeCode with no gross laden weight', () => { + const asPSV = initialState.editingTechRecord as TechRecordTypeVehicleVerb<'psv', 'put'>; + asPSV.techRecord_brakes_brakeCodeOriginal = '80'; + asPSV.techRecord_brakes_brakeCode = '37'; + + const newState = vehicleTechRecordReducer(initialState, updateBrakeForces({ grossKerbWeight: 1000 })); + expect( + (newState?.editingTechRecord as TechRecordTypeVehicleVerb<'psv', 'put'>).techRecord_brakes_brakeCode + ).toBe('37'); + }); + + it('should update brake forces', () => { + const asPSV = initialState.editingTechRecord as TechRecordTypeVehicleVerb<'psv', 'put'>; + asPSV.techRecord_brakes_brakeCodeOriginal = '80'; + expect(asPSV.techRecord_brakes_brakeCode).toBe('1234'); + + const newState = vehicleTechRecordReducer( + initialState, + updateBrakeForces({ grossKerbWeight: 1000, grossLadenWeight: 2000 }) + ); + + expect(newState).not.toBe(initialState); + expect(newState).not.toEqual(initialState); + + const newPSV = newState.editingTechRecord as TechRecordTypeVehicleVerb<'psv', 'put'>; + expect(newPSV.techRecord_brakes_brakeCode).toBe(`0${2000 / 100}${asPSV.techRecord_brakes_brakeCodeOriginal}`); + + expect(newPSV.techRecord_brakes_brakeForceWheelsNotLocked_serviceBrakeForceA).toBe( + Math.round((2000 * 16) / 100) + ); + expect(newPSV.techRecord_brakes_brakeForceWheelsNotLocked_secondaryBrakeForceA).toBe( + Math.round((2000 * 22.5) / 100) + ); + expect(newPSV.techRecord_brakes_brakeForceWheelsNotLocked_parkingBrakeForceA).toBe( + Math.round((2000 * 45) / 100) + ); + + expect(newPSV.techRecord_brakes_brakeForceWheelsUpToHalfLocked_serviceBrakeForceB).toBe( + Math.round((1000 * 16) / 100) + ); + expect(newPSV.techRecord_brakes_brakeForceWheelsUpToHalfLocked_secondaryBrakeForceB).toBe( + Math.round((1000 * 25) / 100) + ); + expect(newPSV.techRecord_brakes_brakeForceWheelsUpToHalfLocked_parkingBrakeForceB).toBe( + Math.round((1000 * 50) / 100) + ); + }); + }); + + describe('updateBody', () => { + it('should update body', () => { + expect( + (initialState.editingTechRecord as TechRecordTypeVehicleVerb<'psv', 'put'>).techRecord_bodyType_description + ).toBe(BodyTypeDescription.DOUBLE_DECKER); + + const expectedData = { + dtpNumber: '9999', + psvChassisMake: 'Toyota', + psvChassisModel: 'Supra', + psvBodyMake: 'Random', + psvBodyType: 'o', + } as PsvMake; + + const newState = vehicleTechRecordReducer(initialState, updateBody({ psvMake: expectedData })); + + expect(newState).not.toBe(initialState); + expect(newState).not.toEqual(initialState); + + const updatedTechRecord = newState.editingTechRecord as TechRecordTypeVehicleVerb<'psv', 'put'>; + expect(updatedTechRecord?.techRecord_bodyType_code).toBe(BodyTypeCode.O); + expect(updatedTechRecord?.techRecord_bodyType_description).toBe(BodyTypeDescription.OTHER); + expect(updatedTechRecord?.techRecord_bodyMake).toBe(expectedData.psvBodyMake); + expect(updatedTechRecord?.techRecord_chassisMake).toBe(expectedData.psvChassisMake); + expect(updatedTechRecord?.techRecord_chassisModel).toBe(expectedData.psvChassisModel); + }); + }); + + describe('addAxle', () => { + describe('it should add an axle', () => { + it('with the axles property defined', () => { + const techRecord = initialState.editingTechRecord as TechRecordTypeVehicleVerb<'psv', 'put'>; + expect(techRecord?.techRecord_noOfAxles).toBe(2); + expect(techRecord?.techRecord_axles?.length).toBe(3); + + const newState = vehicleTechRecordReducer(initialState, addAxle()); + + expect(newState).not.toBe(initialState); + expect(newState).not.toEqual(initialState); + + const updatedTechRecord = newState.editingTechRecord as TechRecordTypeVehicleVerb<'psv', 'put'>; + expect(updatedTechRecord?.techRecord_noOfAxles).toBe(4); + expect(updatedTechRecord?.techRecord_axles?.length).toBe(4); + + const newAxleField = updatedTechRecord?.techRecord_axles ?? []; + + expect(newAxleField[3].tyres_dataTrAxles).toBeNull(); + expect(newAxleField[3].tyres_fitmentCode).toBeNull(); + expect(newAxleField[3].tyres_plyRating).toBeNull(); + expect(newAxleField[3].tyres_speedCategorySymbol).toBeNull(); + expect(newAxleField[3].tyres_tyreCode).toBeNull(); + expect(newAxleField[3].tyres_tyreSize).toBeNull(); + + expect(newAxleField[3].weights_designWeight).toBeDefined(); + expect(newAxleField[3].weights_gbWeight).toBeDefined(); + expect(newAxleField[3].weights_kerbWeight).toBeDefined(); + expect(newAxleField[3].weights_ladenWeight).toBeDefined(); + expect(updatedTechRecord?.techRecord_axles?.pop()?.axleNumber).toBe(4); + }); + + it('without the axles property defined', () => { + const techRecord = initialState.editingTechRecord as TechRecordTypeVehicleVerb<'psv', 'put'>; + delete techRecord?.techRecord_axles; + techRecord.techRecord_noOfAxles = 0; + expect(techRecord?.techRecord_noOfAxles).toBe(0); + + const newState = vehicleTechRecordReducer(initialState, addAxle()); + + expect(newState).not.toBe(initialState); + expect(newState).not.toEqual(initialState); + + const updatedTechRecord = newState.editingTechRecord as TechRecordTypeVehicleVerb<'psv', 'put'>; + expect(updatedTechRecord?.techRecord_noOfAxles).toBe(1); + expect(updatedTechRecord?.techRecord_axles?.length).toBe(1); + + const newAxleField = updatedTechRecord?.techRecord_axles || []; + + expect(newAxleField[0].tyres_dataTrAxles).toBeNull(); + expect(newAxleField[0].tyres_fitmentCode).toBeNull(); + expect(newAxleField[0].tyres_plyRating).toBeNull(); + expect(newAxleField[0].tyres_speedCategorySymbol).toBeNull(); + expect(newAxleField[0].tyres_tyreCode).toBeNull(); + expect(newAxleField[0].tyres_tyreSize).toBeNull(); + + expect(newAxleField[0].weights_designWeight).toBeDefined(); + expect(newAxleField[0].weights_gbWeight).toBeDefined(); + expect(newAxleField[0].weights_kerbWeight).toBeDefined(); + expect(newAxleField[0].weights_ladenWeight).toBeDefined(); + expect(updatedTechRecord?.techRecord_axles?.pop()?.axleNumber).toBe(1); + }); + }); + }); + + describe('removeAxle', () => { + it('should remove specified axle', () => { + const techRecord = initialState.editingTechRecord as TechRecordTypeVehicleVerb<'psv', 'put'>; + expect(techRecord?.techRecord_noOfAxles).toBe(2); + expect(techRecord?.techRecord_axles?.length).toBe(3); + + const newState = vehicleTechRecordReducer(initialState, removeAxle({ index: 0 })); + + expect(newState).not.toBe(initialState); + expect(newState).not.toEqual(initialState); + + const updatedTechRecord = newState.editingTechRecord as TechRecordTypeVehicleVerb<'psv', 'put'>; + expect(updatedTechRecord?.techRecord_noOfAxles).toBe(2); + expect(updatedTechRecord?.techRecord_axles?.length).toBe(2); + expect(updatedTechRecord?.techRecord_axles?.pop()?.axleNumber).toBe(2); + }); + }); + describe('updateScrollPosition', () => { + it('should update the scroll position state', () => { + const newState = vehicleTechRecordReducer(initialState, updateScrollPosition({ position: [1, 2] })); + + expect(newState.scrollPosition).toEqual([1, 2]); + }); + }); + describe('clearScrollPosition', () => { + it('should reset the scroll position', () => { + initialState.scrollPosition = [2, 2]; + const newState = vehicleTechRecordReducer(initialState, clearScrollPosition()); + + expect(newState.scrollPosition).toEqual([0, 0]); + }); + }); + }); + + describe('clearADRDetailsBeforeUpdate', () => { + it('should set all ADR fields to null when: vehicleType = HGV and dangerousGoods = false', () => { + initialState.editingTechRecord = createMockHgv(1234) as TechRecordPUTHGV; + initialState.editingTechRecord.techRecord_adrDetails_dangerousGoods = false; + initialState.editingTechRecord.techRecord_adrDetails_applicantDetails_city = 'Test City'; + + const newState = vehicleTechRecordReducer(initialState, clearADRDetailsBeforeUpdate()); + expect(newState.editingTechRecord).toBeDefined(); + expect((newState.editingTechRecord as TechRecordPUTHGV).techRecord_adrDetails_applicantDetails_city).toBeNull(); + }); + + it('should set all ADR fields to null when: vehicleType = TRL and dangerousGoods = false', () => { + initialState.editingTechRecord = createMockTrl(1234) as TechRecordPUTTRL; + initialState.editingTechRecord.techRecord_adrDetails_dangerousGoods = false; + initialState.editingTechRecord.techRecord_adrDetails_applicantDetails_street = 'Test Street'; + + const newState = vehicleTechRecordReducer(initialState, clearADRDetailsBeforeUpdate()); + expect(newState.editingTechRecord).toBeDefined(); + expect((newState.editingTechRecord as TechRecordPUTTRL).techRecord_adrDetails_applicantDetails_street).toBeNull(); + }); + + it('should set all ADR fields to null when: vehicleType = LGV and dangerousGoods = false', () => { + initialState.editingTechRecord = createMockLgv(1234) as TechRecordPUTLGV; + initialState.editingTechRecord.techRecord_adrDetails_dangerousGoods = false; + initialState.editingTechRecord.techRecord_adrDetails_adrCertificateNotes = 'Test notes'; + + const newState = vehicleTechRecordReducer(initialState, clearADRDetailsBeforeUpdate()); + expect(newState.editingTechRecord).toBeDefined(); + expect((newState.editingTechRecord as TechRecordPUTLGV).techRecord_adrDetails_applicantDetails_street).toBeNull(); + }); + + it('should not set all ADR fields to null when: vehicleType = HGV and dangerousGoods = true', () => { + initialState.editingTechRecord = createMockHgv(1234) as TechRecordPUTHGV; + initialState.editingTechRecord.techRecord_adrDetails_dangerousGoods = true; + initialState.editingTechRecord.techRecord_adrDetails_applicantDetails_town = 'Test Town'; + + const newState = vehicleTechRecordReducer(initialState, clearADRDetailsBeforeUpdate()); + expect(newState.editingTechRecord).toBeDefined(); + expect((newState.editingTechRecord as TechRecordPUTHGV).techRecord_adrDetails_applicantDetails_town).toBe( + 'Test Town' + ); + }); + + it('should not set all ADR fields to null when: vehicleType = TRL and dangerousGoods = true', () => { + initialState.editingTechRecord = createMockTrl(1234) as TechRecordPUTTRL; + initialState.editingTechRecord.techRecord_adrDetails_dangerousGoods = true; + initialState.editingTechRecord.techRecord_adrDetails_applicantDetails_postcode = 'Test Postcode'; + + const newState = vehicleTechRecordReducer(initialState, clearADRDetailsBeforeUpdate()); + expect(newState.editingTechRecord).toBeDefined(); + expect((newState.editingTechRecord as TechRecordPUTTRL).techRecord_adrDetails_applicantDetails_postcode).toBe( + 'Test Postcode' + ); + }); + + it('should not set all ADR fields to null when: vehicleType = LGV and dangerousGoods = true', () => { + initialState.editingTechRecord = createMockLgv(1234) as TechRecordPUTLGV; + initialState.editingTechRecord.techRecord_adrDetails_dangerousGoods = true; + initialState.editingTechRecord.techRecord_adrDetails_adrCertificateNotes = 'Test notes'; + + const newState = vehicleTechRecordReducer(initialState, clearADRDetailsBeforeUpdate()); + expect(newState.editingTechRecord).toBeDefined(); + expect((newState.editingTechRecord as TechRecordPUTLGV).techRecord_adrDetails_adrCertificateNotes).toBe( + 'Test notes' + ); + }); + }); + describe('handleADRExaminerNoteChanges', () => { + beforeEach(() => { + const mockedDate = new Date(2024, 5, 20); + jest.useFakeTimers(); + jest.setSystemTime(mockedDate); + }); + + afterEach(() => { + jest.useRealTimers(); + }); + it('should handle any changes made to the adr examiner notes', () => { + const testNote = { + note: 'testNote', + createdAtDate: new Date().toISOString(), + lastUpdatedBy: 'someone', + }; + const state: TechnicalRecordServiceState = { + ...initialState, + vehicleTechRecord: { + systemNumber: 'foo', + createdTimestamp: 'bar', + vin: 'testVin', + } as unknown as TechRecordType<'get'>, + editingTechRecord: { + systemNumber: 'foo', + createdTimestamp: 'bar', + vin: 'testVin', + techRecord_adrDetails_additionalExaminerNotes_note: testNote.note, + } as unknown as TechRecordType<'put'>, + loading: true, + }; + const action = updateADRAdditionalExaminerNotes({ username: testNote.lastUpdatedBy }); + const newState = vehicleTechRecordReducer(state, action); + expect( + (newState.editingTechRecord as unknown as NonVerbTechRecordType<'hgv' | 'lgv' | 'trl'>) + ?.techRecord_adrDetails_additionalExaminerNotes + ).toContainEqual(testNote); + }); + }); + describe('handleUpdateExistingADRExaminerNote', () => { + it('should', () => { + const state: TechnicalRecordServiceState = { + ...initialState, + editingTechRecord: { + systemNumber: 'foo', + createdTimestamp: 'bar', + vin: 'testVin', + techRecord_adrDetails_additionalExaminerNotes: [ + { + note: 'foo', + createdAtDate: 'bar', + lastUpdatedBy: 'foo', + }, + ], + } as unknown as TechRecordType<'put'>, + loading: true, + }; + const newNote = 'foobar'; + const action = updateExistingADRAdditionalExaminerNote({ additionalExaminerNote: newNote, examinerNoteIndex: 0 }); + const newState = vehicleTechRecordReducer(state, action); + const editingTechRecord = newState.editingTechRecord as unknown as NonVerbTechRecordType<'hgv' | 'lgv' | 'trl'>; + expect(editingTechRecord.techRecord_adrDetails_additionalExaminerNotes![0].note).toEqual(newNote); + }); + }); }); diff --git a/src/app/store/technical-records/reducers/technical-record-service.reducer.ts b/src/app/store/technical-records/reducers/technical-record-service.reducer.ts index 320ce60951..26c805c29d 100644 --- a/src/app/store/technical-records/reducers/technical-record-service.reducer.ts +++ b/src/app/store/technical-records/reducers/technical-record-service.reducer.ts @@ -12,556 +12,600 @@ import { createFeatureSelector, createReducer, on } from '@ngrx/store'; import { AxlesService } from '@services/axles/axles.service'; import { cloneDeep } from 'lodash'; import { - clearBatch, - setApplicationId, - setGenerateNumberFlag, - setVehicleStatus, - setVehicleType, - upsertVehicleBatch, + clearBatch, + setApplicationId, + setGenerateNumberFlag, + setVehicleStatus, + setVehicleType, + upsertVehicleBatch, } from '../actions/batch-create.actions'; import { - addAxle, - addSectionState, amendVin, amendVinFailure, amendVinSuccess, - amendVrm, - amendVrmFailure, - amendVrmSuccess, - archiveTechRecord, - archiveTechRecordFailure, - archiveTechRecordSuccess, - canGeneratePlate, - cannotGeneratePlate, - clearADRDetailsBeforeUpdate, - clearAllSectionStates, - clearScrollPosition, - createVehicleRecord, - createVehicleRecordFailure, - createVehicleRecordSuccess, - generateADRCertificate, - generateADRCertificateFailure, - generateADRCertificateSuccess, - generateContingencyADRCertificate, - generateLetter, - generateLetterFailure, - generateLetterSuccess, - generatePlate, - generatePlateFailure, - generatePlateSuccess, - getBySystemNumber, - getBySystemNumberFailure, - getBySystemNumberSuccess, - getTechRecordV3Success, - promoteTechRecord, - promoteTechRecordFailure, - promoteTechRecordSuccess, - removeAxle, - removeSectionState, - unarchiveTechRecord, - unarchiveTechRecordFailure, - unarchiveTechRecordSuccess, updateADRAdditionalExaminerNotes, - updateBody, - updateBrakeForces, - updateEditingTechRecord, - updateEditingTechRecordCancel, updateExistingADRAdditionalExaminerNote, - updateScrollPosition, - updateTechRecord, - updateTechRecordFailure, - updateTechRecordSuccess, + addAxle, + addSectionState, + amendVin, + amendVinFailure, + amendVinSuccess, + amendVrm, + amendVrmFailure, + amendVrmSuccess, + archiveTechRecord, + archiveTechRecordFailure, + archiveTechRecordSuccess, + canGeneratePlate, + cannotGeneratePlate, + clearADRDetailsBeforeUpdate, + clearAllSectionStates, + clearScrollPosition, + createVehicleRecord, + createVehicleRecordFailure, + createVehicleRecordSuccess, + generateADRCertificate, + generateADRCertificateFailure, + generateADRCertificateSuccess, + generateContingencyADRCertificate, + generateLetter, + generateLetterFailure, + generateLetterSuccess, + generatePlate, + generatePlateFailure, + generatePlateSuccess, + getBySystemNumber, + getBySystemNumberFailure, + getBySystemNumberSuccess, + getTechRecordV3Success, + promoteTechRecord, + promoteTechRecordFailure, + promoteTechRecordSuccess, + removeAxle, + removeSectionState, + unarchiveTechRecord, + unarchiveTechRecordFailure, + unarchiveTechRecordSuccess, + updateADRAdditionalExaminerNotes, + updateBody, + updateBrakeForces, + updateEditingTechRecord, + updateEditingTechRecordCancel, + updateExistingADRAdditionalExaminerNote, + updateScrollPosition, + updateTechRecord, + updateTechRecordFailure, + updateTechRecordSuccess, } from '../actions/technical-record-service.actions'; import { BatchRecords, initialBatchState, vehicleBatchCreateReducer } from './batch-create.reducer'; export const STORE_FEATURE_TECHNICAL_RECORDS_KEY = 'TechnicalRecords'; export interface TechnicalRecordServiceState { - vehicleTechRecord: TechRecordType<'get'> | undefined; - loading: boolean; - editingTechRecord?: TechRecordType<'put'>; - error?: unknown; - techRecordHistory?: TechRecordSearchSchema[]; - batchVehicles: BatchRecords; - sectionState?: (string | number)[]; - canGeneratePlate: boolean; - scrollPosition: [number, number]; + vehicleTechRecord: TechRecordType<'get'> | undefined; + loading: boolean; + editingTechRecord?: TechRecordType<'put'>; + error?: unknown; + techRecordHistory?: TechRecordSearchSchema[]; + batchVehicles: BatchRecords; + sectionState?: (string | number)[]; + canGeneratePlate: boolean; + scrollPosition: [number, number]; } export const initialState: TechnicalRecordServiceState = { - vehicleTechRecord: undefined, - batchVehicles: initialBatchState, - loading: false, - sectionState: [], - canGeneratePlate: false, - scrollPosition: [0, 0], + vehicleTechRecord: undefined, + batchVehicles: initialBatchState, + loading: false, + sectionState: [], + canGeneratePlate: false, + scrollPosition: [0, 0], }; -export const getTechRecordState = createFeatureSelector(STORE_FEATURE_TECHNICAL_RECORDS_KEY); +export const getTechRecordState = createFeatureSelector( + STORE_FEATURE_TECHNICAL_RECORDS_KEY +); export const vehicleTechRecordReducer = createReducer( - initialState, - - on(createVehicleRecord, defaultArgs), - on(createVehicleRecordSuccess, successArgs), - on(createVehicleRecordFailure, (state) => ({ ...state, loading: false })), - - on(getBySystemNumber, (state) => ({ ...state, loading: true })), - on(getBySystemNumberSuccess, (state, action) => ({ ...state, loading: false, techRecordHistory: action.techRecordHistory })), - on(getBySystemNumberFailure, (state) => ({ ...state, loading: false, techRecordHistory: [] })), - - on(updateTechRecord, defaultArgs), - on(updateTechRecordSuccess, successArgs), - on(updateTechRecordFailure, updateFailureArgs), - - on(archiveTechRecord, defaultArgs), - on(archiveTechRecordSuccess, successArgs), - on(archiveTechRecordFailure, updateFailureArgs), - - on(unarchiveTechRecord, defaultArgs), - on(unarchiveTechRecordSuccess, successArgs), - on(unarchiveTechRecordFailure, updateFailureArgs), - - on(promoteTechRecord, defaultArgs), - on(promoteTechRecordSuccess, successArgs), - on(promoteTechRecordFailure, updateFailureArgs), - - on(amendVrm, defaultArgs), - on(amendVrmSuccess, successArgs), - on(amendVrmFailure, updateFailureArgs), - - on(amendVin, defaultArgs), - on(amendVinSuccess, successArgs), - on(amendVinFailure, updateFailureArgs), - - on(generatePlate, defaultArgs), - on(generatePlateSuccess, (state) => ({ ...state, editingTechRecord: undefined, loading: false })), - on(generatePlateFailure, failureArgs), - on(canGeneratePlate, (state) => ({ ...state, canGeneratePlate: true })), - on(cannotGeneratePlate, (state) => ({ ...state, canGeneratePlate: false })), - - on(generateLetter, defaultArgs), - on(generateLetterSuccess, (state) => ({ ...state, editingTechRecord: undefined })), - on(generateLetterFailure, failureArgs), - - on(generateADRCertificate, defaultArgs), - on(generateContingencyADRCertificate, defaultArgs), - on(generateADRCertificateSuccess, (state) => ({ ...state, editingTechRecord: undefined, loading: false })), - on(generateADRCertificateFailure, failureArgs), - - on(updateEditingTechRecord, (state, action) => updateEditingTechRec(state, action)), - on(updateEditingTechRecordCancel, (state) => ({ ...state, editingTechRecord: undefined })), - - on(updateBrakeForces, (state, action) => handleUpdateBrakeForces(state, action)), - - on(updateBody, (state, action) => handleUpdateBody(state, action)), - - on(updateADRAdditionalExaminerNotes, (state, action) => handleADRExaminerNoteChanges(state, action.username)), - - on(updateExistingADRAdditionalExaminerNote, (state, action) => handleUpdateExistingADRExaminerNote(state, action)), - - on(addAxle, (state) => handleAddAxle(state)), - on(removeAxle, (state, action) => handleRemoveAxle(state, action)), - - on(addSectionState, (state, action) => handleAddSection(state, action)), - on(removeSectionState, (state, action) => handleRemoveSection(state, action)), - on(clearAllSectionStates, (state) => ({ ...state, sectionState: [] })), - - on( - upsertVehicleBatch, - createVehicleRecordSuccess, - updateTechRecordSuccess, - setApplicationId, - setVehicleStatus, - setVehicleType, - setGenerateNumberFlag, - clearBatch, - (state, action) => ({ - ...state, - batchVehicles: vehicleBatchCreateReducer(state.batchVehicles, action), - }), - ), - - on(getTechRecordV3Success, (state, action) => ({ ...state, vehicleTechRecord: action.vehicleTechRecord })), - - on(updateScrollPosition, (state, action) => ({ ...state, scrollPosition: action.position })), - on(clearScrollPosition, (state) => ({ ...state, scrollPosition: [0, 0] as [number, number] })), - - on(clearADRDetailsBeforeUpdate, (state) => handleClearADRDetails(state)), + initialState, + + on(createVehicleRecord, defaultArgs), + on(createVehicleRecordSuccess, successArgs), + on(createVehicleRecordFailure, (state) => ({ ...state, loading: false })), + + on(getBySystemNumber, (state) => ({ ...state, loading: true })), + on(getBySystemNumberSuccess, (state, action) => ({ + ...state, + loading: false, + techRecordHistory: action.techRecordHistory, + })), + on(getBySystemNumberFailure, (state) => ({ ...state, loading: false, techRecordHistory: [] })), + + on(updateTechRecord, defaultArgs), + on(updateTechRecordSuccess, successArgs), + on(updateTechRecordFailure, updateFailureArgs), + + on(archiveTechRecord, defaultArgs), + on(archiveTechRecordSuccess, successArgs), + on(archiveTechRecordFailure, updateFailureArgs), + + on(unarchiveTechRecord, defaultArgs), + on(unarchiveTechRecordSuccess, successArgs), + on(unarchiveTechRecordFailure, updateFailureArgs), + + on(promoteTechRecord, defaultArgs), + on(promoteTechRecordSuccess, successArgs), + on(promoteTechRecordFailure, updateFailureArgs), + + on(amendVrm, defaultArgs), + on(amendVrmSuccess, successArgs), + on(amendVrmFailure, updateFailureArgs), + + on(amendVin, defaultArgs), + on(amendVinSuccess, successArgs), + on(amendVinFailure, updateFailureArgs), + + on(generatePlate, defaultArgs), + on(generatePlateSuccess, (state) => ({ ...state, editingTechRecord: undefined, loading: false })), + on(generatePlateFailure, failureArgs), + on(canGeneratePlate, (state) => ({ ...state, canGeneratePlate: true })), + on(cannotGeneratePlate, (state) => ({ ...state, canGeneratePlate: false })), + + on(generateLetter, defaultArgs), + on(generateLetterSuccess, (state) => ({ ...state, editingTechRecord: undefined })), + on(generateLetterFailure, failureArgs), + + on(generateADRCertificate, defaultArgs), + on(generateContingencyADRCertificate, defaultArgs), + on(generateADRCertificateSuccess, (state) => ({ ...state, editingTechRecord: undefined, loading: false })), + on(generateADRCertificateFailure, failureArgs), + + on(updateEditingTechRecord, (state, action) => updateEditingTechRec(state, action)), + on(updateEditingTechRecordCancel, (state) => ({ ...state, editingTechRecord: undefined })), + + on(updateBrakeForces, (state, action) => handleUpdateBrakeForces(state, action)), + + on(updateBody, (state, action) => handleUpdateBody(state, action)), + + on(updateADRAdditionalExaminerNotes, (state, action) => handleADRExaminerNoteChanges(state, action.username)), + + on(updateExistingADRAdditionalExaminerNote, (state, action) => handleUpdateExistingADRExaminerNote(state, action)), + + on(addAxle, (state) => handleAddAxle(state)), + on(removeAxle, (state, action) => handleRemoveAxle(state, action)), + + on(addSectionState, (state, action) => handleAddSection(state, action)), + on(removeSectionState, (state, action) => handleRemoveSection(state, action)), + on(clearAllSectionStates, (state) => ({ ...state, sectionState: [] })), + + on( + upsertVehicleBatch, + createVehicleRecordSuccess, + updateTechRecordSuccess, + setApplicationId, + setVehicleStatus, + setVehicleType, + setGenerateNumberFlag, + clearBatch, + (state, action) => ({ + ...state, + batchVehicles: vehicleBatchCreateReducer(state.batchVehicles, action), + }) + ), + + on(getTechRecordV3Success, (state, action) => ({ ...state, vehicleTechRecord: action.vehicleTechRecord })), + + on(updateScrollPosition, (state, action) => ({ ...state, scrollPosition: action.position })), + on(clearScrollPosition, (state) => ({ ...state, scrollPosition: [0, 0] as [number, number] })), + + on(clearADRDetailsBeforeUpdate, (state) => handleClearADRDetails(state)) ); function defaultArgs(state: TechnicalRecordServiceState) { - return { ...state, loading: true }; + return { ...state, loading: true }; } function successArgs(state: TechnicalRecordServiceState, data: { vehicleTechRecord: TechRecordType<'get'> }) { - return { ...state, vehicleTechRecord: data.vehicleTechRecord, loading: false }; + return { ...state, vehicleTechRecord: data.vehicleTechRecord, loading: false }; } function updateFailureArgs(state: TechnicalRecordServiceState, data: { error: unknown }) { - return { ...state, error: data.error, loading: false }; + return { ...state, error: data.error, loading: false }; } function failureArgs(state: TechnicalRecordServiceState, data: { error: unknown }) { - return { - ...state, vehicleTechRecord: undefined, error: data.error, loading: false, - }; + return { + ...state, + vehicleTechRecord: undefined, + error: data.error, + loading: false, + }; } function handleUpdateBrakeForces( - state: TechnicalRecordServiceState, - data: { grossLadenWeight?: number; grossKerbWeight?: number }, + state: TechnicalRecordServiceState, + data: { grossLadenWeight?: number; grossKerbWeight?: number } ): TechnicalRecordServiceState { - const newState = cloneDeep(state); - if (!newState.editingTechRecord) return newState; - if (newState.editingTechRecord.techRecord_vehicleType !== 'psv') { - return newState; - } - - if (data.grossLadenWeight) { - const prefix = `${Math.round(data.grossLadenWeight / 100)}`; - - newState.editingTechRecord.techRecord_brakes_brakeCode = (prefix.length <= 2 - ? `0${prefix}` - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - : prefix) + newState.editingTechRecord.techRecord_brakes_brakeCodeOriginal!; - newState.editingTechRecord.techRecord_brakes_brakeForceWheelsNotLocked_serviceBrakeForceA = Math.round((data.grossLadenWeight * 16) / 100); - newState.editingTechRecord.techRecord_brakes_brakeForceWheelsNotLocked_secondaryBrakeForceA = Math.round((data.grossLadenWeight * 22.5) / 100); - newState.editingTechRecord.techRecord_brakes_brakeForceWheelsNotLocked_parkingBrakeForceA = Math.round((data.grossLadenWeight * 45) / 100); - } - - if (data.grossKerbWeight) { - newState.editingTechRecord.techRecord_brakes_brakeForceWheelsUpToHalfLocked_serviceBrakeForceB = Math.round((data.grossKerbWeight * 16) / 100); - newState.editingTechRecord.techRecord_brakes_brakeForceWheelsUpToHalfLocked_secondaryBrakeForceB = Math.round((data.grossKerbWeight * 25) / 100); - newState.editingTechRecord.techRecord_brakes_brakeForceWheelsUpToHalfLocked_parkingBrakeForceB = Math.round((data.grossKerbWeight * 50) / 100); - } - - return newState; + const newState = cloneDeep(state); + if (!newState.editingTechRecord) return newState; + if (newState.editingTechRecord.techRecord_vehicleType !== 'psv') { + return newState; + } + + if (data.grossLadenWeight) { + const prefix = `${Math.round(data.grossLadenWeight / 100)}`; + + newState.editingTechRecord.techRecord_brakes_brakeCode = + (prefix.length <= 2 + ? `0${prefix}` + : // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + prefix) + newState.editingTechRecord.techRecord_brakes_brakeCodeOriginal!; + newState.editingTechRecord.techRecord_brakes_brakeForceWheelsNotLocked_serviceBrakeForceA = Math.round( + (data.grossLadenWeight * 16) / 100 + ); + newState.editingTechRecord.techRecord_brakes_brakeForceWheelsNotLocked_secondaryBrakeForceA = Math.round( + (data.grossLadenWeight * 22.5) / 100 + ); + newState.editingTechRecord.techRecord_brakes_brakeForceWheelsNotLocked_parkingBrakeForceA = Math.round( + (data.grossLadenWeight * 45) / 100 + ); + } + + if (data.grossKerbWeight) { + newState.editingTechRecord.techRecord_brakes_brakeForceWheelsUpToHalfLocked_serviceBrakeForceB = Math.round( + (data.grossKerbWeight * 16) / 100 + ); + newState.editingTechRecord.techRecord_brakes_brakeForceWheelsUpToHalfLocked_secondaryBrakeForceB = Math.round( + (data.grossKerbWeight * 25) / 100 + ); + newState.editingTechRecord.techRecord_brakes_brakeForceWheelsUpToHalfLocked_parkingBrakeForceB = Math.round( + (data.grossKerbWeight * 50) / 100 + ); + } + + return newState; } -function handleUpdateBody(state: TechnicalRecordServiceState, action: { psvMake: PsvMake }): TechnicalRecordServiceState { - const newState = cloneDeep(state); +function handleUpdateBody( + state: TechnicalRecordServiceState, + action: { psvMake: PsvMake } +): TechnicalRecordServiceState { + const newState = cloneDeep(state); - if (!newState.editingTechRecord) return newState; - if (newState.editingTechRecord.techRecord_vehicleType !== 'psv') { - return newState; - } + if (!newState.editingTechRecord) return newState; + if (newState.editingTechRecord.techRecord_vehicleType !== 'psv') { + return newState; + } - const code = action.psvMake.psvBodyType.toLowerCase() as BodyTypeCode; + const code = action.psvMake.psvBodyType.toLowerCase() as BodyTypeCode; - newState.editingTechRecord.techRecord_bodyType_code = code; - newState.editingTechRecord.techRecord_bodyType_description = vehicleBodyTypeCodeMap.get(VehicleTypes.PSV)?.get(code); - newState.editingTechRecord.techRecord_bodyMake = action.psvMake.psvBodyMake; - newState.editingTechRecord.techRecord_chassisMake = action.psvMake.psvChassisMake; - newState.editingTechRecord.techRecord_chassisModel = action.psvMake.psvChassisModel; + newState.editingTechRecord.techRecord_bodyType_code = code; + newState.editingTechRecord.techRecord_bodyType_description = vehicleBodyTypeCodeMap.get(VehicleTypes.PSV)?.get(code); + newState.editingTechRecord.techRecord_bodyMake = action.psvMake.psvBodyMake; + newState.editingTechRecord.techRecord_chassisMake = action.psvMake.psvChassisMake; + newState.editingTechRecord.techRecord_chassisModel = action.psvMake.psvChassisModel; - return newState; + return newState; } function handleAddAxle(state: TechnicalRecordServiceState): TechnicalRecordServiceState { - const newState = cloneDeep(state); - - if (!newState.editingTechRecord) return newState; - if ( - newState.editingTechRecord.techRecord_vehicleType === 'car' - || newState.editingTechRecord.techRecord_vehicleType === 'lgv' - || newState.editingTechRecord.techRecord_vehicleType === 'motorcycle' - ) { - return newState; - } - if (!newState.editingTechRecord.techRecord_axles) newState.editingTechRecord.techRecord_axles = []; - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const newAxle: any = { - axleNumber: newState.editingTechRecord.techRecord_axles.length + 1, - tyres_tyreSize: null, - tyres_fitmentCode: null, - tyres_dataTrAxles: null, - tyres_plyRating: null, - tyres_tyreCode: null, - weights_gbWeight: null, - weights_designWeight: null, - parkingBrakeMrk: false, - }; - - if ( - newState.editingTechRecord.techRecord_vehicleType === VehicleTypes.HGV - || newState.editingTechRecord.techRecord_vehicleType === VehicleTypes.TRL - ) { - newAxle.weights_eecWeight = null; - } - if (newState.editingTechRecord.techRecord_vehicleType === VehicleTypes.PSV) { - newAxle.weights_kerbWeight = null; - newAxle.weights_ladenWeight = null; - newAxle.tyres_speedCategorySymbol = null; - } - - newState.editingTechRecord.techRecord_axles.push(newAxle); - - newState.editingTechRecord.techRecord_noOfAxles = newState.editingTechRecord.techRecord_axles.length; - - if ( - newState.editingTechRecord.techRecord_vehicleType === VehicleTypes.HGV - || newState.editingTechRecord.techRecord_vehicleType === VehicleTypes.TRL - ) { - newState.editingTechRecord.techRecord_dimensions_axleSpacing = new AxlesService().generateAxleSpacing( - newState.editingTechRecord.techRecord_axles.length, - newState.editingTechRecord.techRecord_dimensions_axleSpacing, - ); - } - - return newState; + const newState = cloneDeep(state); + + if (!newState.editingTechRecord) return newState; + if ( + newState.editingTechRecord.techRecord_vehicleType === 'car' || + newState.editingTechRecord.techRecord_vehicleType === 'lgv' || + newState.editingTechRecord.techRecord_vehicleType === 'motorcycle' + ) { + return newState; + } + if (!newState.editingTechRecord.techRecord_axles) newState.editingTechRecord.techRecord_axles = []; + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const newAxle: any = { + axleNumber: newState.editingTechRecord.techRecord_axles.length + 1, + tyres_tyreSize: null, + tyres_fitmentCode: null, + tyres_dataTrAxles: null, + tyres_plyRating: null, + tyres_tyreCode: null, + weights_gbWeight: null, + weights_designWeight: null, + parkingBrakeMrk: false, + }; + + if ( + newState.editingTechRecord.techRecord_vehicleType === VehicleTypes.HGV || + newState.editingTechRecord.techRecord_vehicleType === VehicleTypes.TRL + ) { + newAxle.weights_eecWeight = null; + } + if (newState.editingTechRecord.techRecord_vehicleType === VehicleTypes.PSV) { + newAxle.weights_kerbWeight = null; + newAxle.weights_ladenWeight = null; + newAxle.tyres_speedCategorySymbol = null; + } + + newState.editingTechRecord.techRecord_axles.push(newAxle); + + newState.editingTechRecord.techRecord_noOfAxles = newState.editingTechRecord.techRecord_axles.length; + + if ( + newState.editingTechRecord.techRecord_vehicleType === VehicleTypes.HGV || + newState.editingTechRecord.techRecord_vehicleType === VehicleTypes.TRL + ) { + newState.editingTechRecord.techRecord_dimensions_axleSpacing = new AxlesService().generateAxleSpacing( + newState.editingTechRecord.techRecord_axles.length, + newState.editingTechRecord.techRecord_dimensions_axleSpacing + ); + } + + return newState; } function handleRemoveAxle(state: TechnicalRecordServiceState, action: { index: number }): TechnicalRecordServiceState { - const newState = cloneDeep(state); - if ( - !newState.editingTechRecord - || newState.editingTechRecord.techRecord_vehicleType === 'car' - || newState.editingTechRecord.techRecord_vehicleType === 'lgv' - || newState.editingTechRecord.techRecord_vehicleType === 'motorcycle' - || !newState.editingTechRecord.techRecord_axles - || !newState.editingTechRecord.techRecord_axles.length - ) { - return newState; - } - - newState.editingTechRecord.techRecord_axles.splice(action.index, 1); - - // eslint-disable-next-line no-return-assign - newState.editingTechRecord.techRecord_axles.forEach((axle, i) => (axle.axleNumber = i + 1)); - - newState.editingTechRecord.techRecord_noOfAxles = newState.editingTechRecord.techRecord_axles.length; - - if ( - newState.editingTechRecord?.techRecord_vehicleType === VehicleTypes.HGV - || newState.editingTechRecord?.techRecord_vehicleType === VehicleTypes.TRL - ) { - newState.editingTechRecord.techRecord_dimensions_axleSpacing = new AxlesService().generateAxleSpacing( - newState.editingTechRecord.techRecord_axles.length, - ); - } - - return newState; + const newState = cloneDeep(state); + if ( + !newState.editingTechRecord || + newState.editingTechRecord.techRecord_vehicleType === 'car' || + newState.editingTechRecord.techRecord_vehicleType === 'lgv' || + newState.editingTechRecord.techRecord_vehicleType === 'motorcycle' || + !newState.editingTechRecord.techRecord_axles || + !newState.editingTechRecord.techRecord_axles.length + ) { + return newState; + } + + newState.editingTechRecord.techRecord_axles.splice(action.index, 1); + + // eslint-disable-next-line no-return-assign + newState.editingTechRecord.techRecord_axles.forEach((axle, i) => (axle.axleNumber = i + 1)); + + newState.editingTechRecord.techRecord_noOfAxles = newState.editingTechRecord.techRecord_axles.length; + + if ( + newState.editingTechRecord?.techRecord_vehicleType === VehicleTypes.HGV || + newState.editingTechRecord?.techRecord_vehicleType === VehicleTypes.TRL + ) { + newState.editingTechRecord.techRecord_dimensions_axleSpacing = new AxlesService().generateAxleSpacing( + newState.editingTechRecord.techRecord_axles.length + ); + } + + return newState; } function handleAddSection(state: TechnicalRecordServiceState, action: { section: string | number }) { - const newState = cloneDeep(state); - if (newState.sectionState?.includes(action.section)) return newState; - return { ...newState, sectionState: newState.sectionState?.concat(action.section) }; + const newState = cloneDeep(state); + if (newState.sectionState?.includes(action.section)) return newState; + return { ...newState, sectionState: newState.sectionState?.concat(action.section) }; } function handleRemoveSection(state: TechnicalRecordServiceState, action: { section: string | number }) { - const newState = cloneDeep(state); - if (!newState.sectionState?.includes(action.section)) return newState; - return { ...newState, sectionState: newState.sectionState?.filter((section) => section !== action.section) }; + const newState = cloneDeep(state); + if (!newState.sectionState?.includes(action.section)) return newState; + return { ...newState, sectionState: newState.sectionState?.filter((section) => section !== action.section) }; } -function updateEditingTechRec(state: TechnicalRecordServiceState, action: { vehicleTechRecord: TechRecordType<'put'> }) { - const newState = { ...state }; - const { editingTechRecord } = state; - const { vehicleTechRecord } = action; +function updateEditingTechRec( + state: TechnicalRecordServiceState, + action: { vehicleTechRecord: TechRecordType<'put'> } +) { + const newState = { ...state }; + const { editingTechRecord } = state; + const { vehicleTechRecord } = action; - newState.editingTechRecord = { ...editingTechRecord, ...vehicleTechRecord } as TechRecordType<'put'>; + newState.editingTechRecord = { ...editingTechRecord, ...vehicleTechRecord } as TechRecordType<'put'>; - return newState; + return newState; } function handleClearADRDetails(state: TechnicalRecordServiceState) { - const { editingTechRecord } = state; - - if (editingTechRecord) { - const { techRecord_vehicleType: type } = editingTechRecord; - if (type === VehicleTypes.HGV || type === VehicleTypes.TRL || type === VehicleTypes.LGV) { - const nulledCompatibilityGroupJ = { - techRecord_adrDetails_compatibilityGroupJ: null, - }; - - const nulledTankDetails = { - techRecord_adrDetails_tank_tankDetails_tankManufacturer: null, - techRecord_adrDetails_tank_tankDetails_yearOfManufacture: null, - techRecord_adrDetails_tank_tankDetails_tankManufacturerSerialNo: null, - techRecord_adrDetails_tank_tankDetails_tankTypeAppNo: null, - techRecord_adrDetails_tank_tankDetails_tankCode: null, - techRecord_adrDetails_tank_tankDetails_specialProvisions: null, - techRecord_adrDetails_tank_tankDetails_tc2Details_tc2Type: null, - techRecord_adrDetails_tank_tankDetails_tc2Details_tc2IntermediateApprovalNo: null, - techRecord_adrDetails_tank_tankDetails_tc2Details_tc2IntermediateExpiryDate: null, - techRecord_adrDetails_tank_tankDetails_tc3Details: null, - techRecord_adrDetails_tank_tankDetails_tankStatement_substancesPermitted: null, - techRecord_adrDetails_tank_tankDetails_tankStatement_statement: null, - techRecord_adrDetails_tank_tankDetails_tankStatement_productListRefNo: null, - techRecord_adrDetails_tank_tankDetails_tankStatement_productListUnNo: null, - techRecord_adrDetails_tank_tankDetails_tankStatement_productList: null, - }; - - const nulledTankStatementStatement = { - techRecord_adrDetails_tank_tankDetails_tankStatement_statement: null, - }; - - const nulledTankStatementProductList = { - techRecord_adrDetails_tank_tankDetails_tankStatement_productListRefNo: null, - techRecord_adrDetails_tank_tankDetails_tankStatement_productListUnNo: null, - techRecord_adrDetails_tank_tankDetails_tankStatement_productList: null, - }; - - const nulledSubstancesPermittedUNNumber = { - techRecord_adrDetails_tank_tankDetails_tankStatement_select: null, - ...nulledTankStatementStatement, - ...nulledTankStatementProductList, - }; - - const nulledBatteryListNumber = { - techRecord_adrDetails_batteryListNumber: null, - }; - - const nulledWeight = { - techRecord_adrDetails_weight: null, - }; - - const nulledBrakeDeclaration = { - techRecord_adrDetails_brakeDeclarationIssuer: null, - techRecord_adrDetails_brakeEndurance: null, - ...nulledWeight, - }; - - if (!editingTechRecord.techRecord_adrDetails_dangerousGoods) { - // vehicle doesn't carry dangerous goods so null this information - return { - ...state, - editingTechRecord: { - ...editingTechRecord, - techRecord_adrDetails_vehicleDetails_type: null, - techRecord_adrDetails_vehicleDetails_usedOnInternationalJourneys: null, - techRecord_adrDetails_vehicleDetails_approvalDate: null, - techRecord_adrDetails_permittedDangerousGoods: null, - ...nulledCompatibilityGroupJ, - techRecord_adrDetails_additionalExaminerNotes: null, - techRecord_adrDetails_applicantDetails_name: null, - techRecord_adrDetails_applicantDetails_street: null, - techRecord_adrDetails_applicantDetails_town: null, - techRecord_adrDetails_applicantDetails_city: null, - techRecord_adrDetails_applicantDetails_postcode: null, - techRecord_adrDetails_memosApply: null, - techRecord_adrDetails_m145Statement: null, - techRecord_adrDetails_documents: null, - techRecord_adrDetails_listStatementApplicable: null, - techRecord_adrDetails_batteryListNumber: null, - techRecord_adrDetails_brakeDeclarationsSeen: null, - techRecord_adrDetails_brakeDeclarationIssuer: null, - techRecord_adrDetails_brakeEndurance: null, - techRecord_adrDetails_weight: null, - techRecord_adrDetails_declarationsSeen: null, - techRecord_adrDetails_additionalNotes_guidanceNotes: null, - techRecord_adrDetails_additionalNotes_number: null, - techRecord_adrDetails_adrTypeApprovalNo: null, - techRecord_adrDetails_adrCertificateNotes: null, - techRecord_adrDetails_newCertificateRequested: null, - ...nulledTankDetails, - }, - }; - } - - let sanitisedEditingTechRecord = { - ...editingTechRecord, - }; - - // Null compatibility group J when permitted dangerous goods is NOT explosives type 2/3 - const explosivesGroups: string[] = [ADRDangerousGood.EXPLOSIVES_TYPE_2, ADRDangerousGood.EXPLOSIVES_TYPE_3]; - if (!editingTechRecord.techRecord_adrDetails_permittedDangerousGoods?.some((value) => explosivesGroups.includes(value))) { - sanitisedEditingTechRecord = { ...sanitisedEditingTechRecord, ...nulledCompatibilityGroupJ }; - } - - // Null all tank details fields when ADR vehicle type does not include the words 'tank' or 'battery' - const adrVehicleTypes: string[] = Object.values(ADRBodyType).filter((value) => value.includes('battery') || value.includes('tank')); - if (!adrVehicleTypes.includes(editingTechRecord.techRecord_adrDetails_vehicleDetails_type as string)) { - sanitisedEditingTechRecord = { ...sanitisedEditingTechRecord, ...nulledTankDetails }; - } - - // Strip all unfilled UN numbers - const { techRecord_adrDetails_tank_tankDetails_tankStatement_productListUnNo: unNumbers } = sanitisedEditingTechRecord; - if (unNumbers) { - sanitisedEditingTechRecord = { - ...sanitisedEditingTechRecord, - techRecord_adrDetails_tank_tankDetails_tankStatement_productListUnNo: unNumbers.filter(Boolean), - }; - } - - // If tank details 'statement' selected, null UN numbers, product list referene no., product list - const { techRecord_adrDetails_tank_tankDetails_tankStatement_select: select } = sanitisedEditingTechRecord; - if (select === ADRTankDetailsTankStatementSelect.STATEMENT) { - sanitisedEditingTechRecord = { ...sanitisedEditingTechRecord, ...nulledTankStatementProductList }; - } - - // If tank details 'product list' selected, null statement reference no. - if (select === ADRTankDetailsTankStatementSelect.PRODUCT_LIST) { - sanitisedEditingTechRecord = { ...sanitisedEditingTechRecord, ...nulledTankStatementStatement }; - } - - // If tank details 'substances permitted' has 'tank code' option selected, null UN and product list reference no. - const { techRecord_adrDetails_tank_tankDetails_tankStatement_substancesPermitted: substancesPermitted } = sanitisedEditingTechRecord; - if (substancesPermitted === ADRTankStatementSubstancePermitted.UNDER_TANK_CODE) { - sanitisedEditingTechRecord = { ...sanitisedEditingTechRecord, ...nulledSubstancesPermittedUNNumber }; - } - - // If battery list applicable is no, null the battery list number - const { techRecord_adrDetails_listStatementApplicable: listStatementApplicable } = sanitisedEditingTechRecord; - if (!listStatementApplicable) { - sanitisedEditingTechRecord = { ...sanitisedEditingTechRecord, ...nulledBatteryListNumber }; - } - // If the ADR body type not includes 'battery', null all fields related battery list applicable - const { techRecord_adrDetails_vehicleDetails_type: vehicleDetailsType } = sanitisedEditingTechRecord; - if (!vehicleDetailsType?.includes('battery')) { - sanitisedEditingTechRecord = { - ...sanitisedEditingTechRecord, ...nulledBatteryListNumber, techRecord_adrDetails_listStatementApplicable: null, - }; - } - // If manufacturer brake declaration is no, null dependent sections - const { techRecord_adrDetails_brakeDeclarationsSeen: brakeDeclarationSeen } = sanitisedEditingTechRecord; - if (!brakeDeclarationSeen) { - sanitisedEditingTechRecord = { ...sanitisedEditingTechRecord, ...nulledBrakeDeclaration }; - } - - // If brake endurance is no, null weight field - const { techRecord_adrDetails_brakeEndurance: brakeEndurance } = sanitisedEditingTechRecord; - if (!brakeEndurance) { - sanitisedEditingTechRecord = { ...sanitisedEditingTechRecord, ...nulledWeight }; - } - - return { ...state, editingTechRecord: sanitisedEditingTechRecord }; - } - - } - - return { ...state }; + const { editingTechRecord } = state; + + if (editingTechRecord) { + const { techRecord_vehicleType: type } = editingTechRecord; + if (type === VehicleTypes.HGV || type === VehicleTypes.TRL || type === VehicleTypes.LGV) { + const nulledCompatibilityGroupJ = { + techRecord_adrDetails_compatibilityGroupJ: null, + }; + + const nulledTankDetails = { + techRecord_adrDetails_tank_tankDetails_tankManufacturer: null, + techRecord_adrDetails_tank_tankDetails_yearOfManufacture: null, + techRecord_adrDetails_tank_tankDetails_tankManufacturerSerialNo: null, + techRecord_adrDetails_tank_tankDetails_tankTypeAppNo: null, + techRecord_adrDetails_tank_tankDetails_tankCode: null, + techRecord_adrDetails_tank_tankDetails_specialProvisions: null, + techRecord_adrDetails_tank_tankDetails_tc2Details_tc2Type: null, + techRecord_adrDetails_tank_tankDetails_tc2Details_tc2IntermediateApprovalNo: null, + techRecord_adrDetails_tank_tankDetails_tc2Details_tc2IntermediateExpiryDate: null, + techRecord_adrDetails_tank_tankDetails_tc3Details: null, + techRecord_adrDetails_tank_tankDetails_tankStatement_substancesPermitted: null, + techRecord_adrDetails_tank_tankDetails_tankStatement_statement: null, + techRecord_adrDetails_tank_tankDetails_tankStatement_productListRefNo: null, + techRecord_adrDetails_tank_tankDetails_tankStatement_productListUnNo: null, + techRecord_adrDetails_tank_tankDetails_tankStatement_productList: null, + }; + + const nulledTankStatementStatement = { + techRecord_adrDetails_tank_tankDetails_tankStatement_statement: null, + }; + + const nulledTankStatementProductList = { + techRecord_adrDetails_tank_tankDetails_tankStatement_productListRefNo: null, + techRecord_adrDetails_tank_tankDetails_tankStatement_productListUnNo: null, + techRecord_adrDetails_tank_tankDetails_tankStatement_productList: null, + }; + + const nulledSubstancesPermittedUNNumber = { + techRecord_adrDetails_tank_tankDetails_tankStatement_select: null, + ...nulledTankStatementStatement, + ...nulledTankStatementProductList, + }; + + const nulledBatteryListNumber = { + techRecord_adrDetails_batteryListNumber: null, + }; + + const nulledWeight = { + techRecord_adrDetails_weight: null, + }; + + const nulledBrakeDeclaration = { + techRecord_adrDetails_brakeDeclarationIssuer: null, + techRecord_adrDetails_brakeEndurance: null, + ...nulledWeight, + }; + + if (!editingTechRecord.techRecord_adrDetails_dangerousGoods) { + // vehicle doesn't carry dangerous goods so null this information + return { + ...state, + editingTechRecord: { + ...editingTechRecord, + techRecord_adrDetails_vehicleDetails_type: null, + techRecord_adrDetails_vehicleDetails_usedOnInternationalJourneys: null, + techRecord_adrDetails_vehicleDetails_approvalDate: null, + techRecord_adrDetails_permittedDangerousGoods: null, + ...nulledCompatibilityGroupJ, + techRecord_adrDetails_additionalExaminerNotes: null, + techRecord_adrDetails_applicantDetails_name: null, + techRecord_adrDetails_applicantDetails_street: null, + techRecord_adrDetails_applicantDetails_town: null, + techRecord_adrDetails_applicantDetails_city: null, + techRecord_adrDetails_applicantDetails_postcode: null, + techRecord_adrDetails_memosApply: null, + techRecord_adrDetails_m145Statement: null, + techRecord_adrDetails_documents: null, + techRecord_adrDetails_listStatementApplicable: null, + techRecord_adrDetails_batteryListNumber: null, + techRecord_adrDetails_brakeDeclarationsSeen: null, + techRecord_adrDetails_brakeDeclarationIssuer: null, + techRecord_adrDetails_brakeEndurance: null, + techRecord_adrDetails_weight: null, + techRecord_adrDetails_declarationsSeen: null, + techRecord_adrDetails_additionalNotes_guidanceNotes: null, + techRecord_adrDetails_additionalNotes_number: null, + techRecord_adrDetails_adrTypeApprovalNo: null, + techRecord_adrDetails_adrCertificateNotes: null, + techRecord_adrDetails_newCertificateRequested: null, + ...nulledTankDetails, + }, + }; + } + + let sanitisedEditingTechRecord = { + ...editingTechRecord, + }; + + // Null compatibility group J when permitted dangerous goods is NOT explosives type 2/3 + const explosivesGroups: string[] = [ADRDangerousGood.EXPLOSIVES_TYPE_2, ADRDangerousGood.EXPLOSIVES_TYPE_3]; + if ( + !editingTechRecord.techRecord_adrDetails_permittedDangerousGoods?.some((value) => + explosivesGroups.includes(value) + ) + ) { + sanitisedEditingTechRecord = { ...sanitisedEditingTechRecord, ...nulledCompatibilityGroupJ }; + } + + // Null all tank details fields when ADR vehicle type does not include the words 'tank' or 'battery' + const adrVehicleTypes: string[] = Object.values(ADRBodyType).filter( + (value) => value.includes('battery') || value.includes('tank') + ); + if (!adrVehicleTypes.includes(editingTechRecord.techRecord_adrDetails_vehicleDetails_type as string)) { + sanitisedEditingTechRecord = { ...sanitisedEditingTechRecord, ...nulledTankDetails }; + } + + // Strip all unfilled UN numbers + const { techRecord_adrDetails_tank_tankDetails_tankStatement_productListUnNo: unNumbers } = + sanitisedEditingTechRecord; + if (unNumbers) { + sanitisedEditingTechRecord = { + ...sanitisedEditingTechRecord, + techRecord_adrDetails_tank_tankDetails_tankStatement_productListUnNo: unNumbers.filter(Boolean), + }; + } + + // If tank details 'statement' selected, null UN numbers, product list referene no., product list + const { techRecord_adrDetails_tank_tankDetails_tankStatement_select: select } = sanitisedEditingTechRecord; + if (select === ADRTankDetailsTankStatementSelect.STATEMENT) { + sanitisedEditingTechRecord = { ...sanitisedEditingTechRecord, ...nulledTankStatementProductList }; + } + + // If tank details 'product list' selected, null statement reference no. + if (select === ADRTankDetailsTankStatementSelect.PRODUCT_LIST) { + sanitisedEditingTechRecord = { ...sanitisedEditingTechRecord, ...nulledTankStatementStatement }; + } + + // If tank details 'substances permitted' has 'tank code' option selected, null UN and product list reference no. + const { techRecord_adrDetails_tank_tankDetails_tankStatement_substancesPermitted: substancesPermitted } = + sanitisedEditingTechRecord; + if (substancesPermitted === ADRTankStatementSubstancePermitted.UNDER_TANK_CODE) { + sanitisedEditingTechRecord = { ...sanitisedEditingTechRecord, ...nulledSubstancesPermittedUNNumber }; + } + + // If battery list applicable is no, null the battery list number + const { techRecord_adrDetails_listStatementApplicable: listStatementApplicable } = sanitisedEditingTechRecord; + if (!listStatementApplicable) { + sanitisedEditingTechRecord = { ...sanitisedEditingTechRecord, ...nulledBatteryListNumber }; + } + // If the ADR body type not includes 'battery', null all fields related battery list applicable + const { techRecord_adrDetails_vehicleDetails_type: vehicleDetailsType } = sanitisedEditingTechRecord; + if (!vehicleDetailsType?.includes('battery')) { + sanitisedEditingTechRecord = { + ...sanitisedEditingTechRecord, + ...nulledBatteryListNumber, + techRecord_adrDetails_listStatementApplicable: null, + }; + } + // If manufacturer brake declaration is no, null dependent sections + const { techRecord_adrDetails_brakeDeclarationsSeen: brakeDeclarationSeen } = sanitisedEditingTechRecord; + if (!brakeDeclarationSeen) { + sanitisedEditingTechRecord = { ...sanitisedEditingTechRecord, ...nulledBrakeDeclaration }; + } + + // If brake endurance is no, null weight field + const { techRecord_adrDetails_brakeEndurance: brakeEndurance } = sanitisedEditingTechRecord; + if (!brakeEndurance) { + sanitisedEditingTechRecord = { ...sanitisedEditingTechRecord, ...nulledWeight }; + } + + return { ...state, editingTechRecord: sanitisedEditingTechRecord }; + } + } + + return { ...state }; } function handleADRExaminerNoteChanges(state: TechnicalRecordServiceState, username: string) { - const { editingTechRecord } = state; - const additionalNoteTechRecord = editingTechRecord as unknown as - (NonVerbTechRecordType<'hgv' | 'lgv' | 'trl'>) & { techRecord_adrDetails_additionalExaminerNotes_note: string }; - if (editingTechRecord) { - if (additionalNoteTechRecord.techRecord_adrDetails_additionalExaminerNotes_note) { - const additionalExaminerNotes = { - note: additionalNoteTechRecord.techRecord_adrDetails_additionalExaminerNotes_note, - lastUpdatedBy: username, - createdAtDate: new Date().toISOString(), - }; - if (additionalNoteTechRecord.techRecord_adrDetails_additionalExaminerNotes === null - || additionalNoteTechRecord.techRecord_adrDetails_additionalExaminerNotes === undefined) { - additionalNoteTechRecord.techRecord_adrDetails_additionalExaminerNotes = []; - } - additionalNoteTechRecord.techRecord_adrDetails_additionalExaminerNotes?.unshift(additionalExaminerNotes); - } - } - return { ...state, editingTechRecord: additionalNoteTechRecord as unknown as (TechRecordType<'put'>) }; + const { editingTechRecord } = state; + const additionalNoteTechRecord = editingTechRecord as unknown as NonVerbTechRecordType<'hgv' | 'lgv' | 'trl'> & { + techRecord_adrDetails_additionalExaminerNotes_note: string; + }; + if (editingTechRecord) { + if (additionalNoteTechRecord.techRecord_adrDetails_additionalExaminerNotes_note) { + const additionalExaminerNotes = { + note: additionalNoteTechRecord.techRecord_adrDetails_additionalExaminerNotes_note, + lastUpdatedBy: username, + createdAtDate: new Date().toISOString(), + }; + if ( + additionalNoteTechRecord.techRecord_adrDetails_additionalExaminerNotes === null || + additionalNoteTechRecord.techRecord_adrDetails_additionalExaminerNotes === undefined + ) { + additionalNoteTechRecord.techRecord_adrDetails_additionalExaminerNotes = []; + } + additionalNoteTechRecord.techRecord_adrDetails_additionalExaminerNotes?.unshift(additionalExaminerNotes); + } + } + return { ...state, editingTechRecord: additionalNoteTechRecord as unknown as TechRecordType<'put'> }; } function handleUpdateExistingADRExaminerNote( - state: TechnicalRecordServiceState, - action: { additionalExaminerNote: string, examinerNoteIndex: number }, + state: TechnicalRecordServiceState, + action: { additionalExaminerNote: string; examinerNoteIndex: number } ) { - const { editingTechRecord } = state; - const editedTechRecord = editingTechRecord as unknown as - (NonVerbTechRecordType<'hgv' | 'lgv' | 'trl'>); - if (editedTechRecord) { - const examinerNotes = editedTechRecord.techRecord_adrDetails_additionalExaminerNotes; - examinerNotes![action.examinerNoteIndex].note = action.additionalExaminerNote; - } - return { ...state, editingTechRecord: editingTechRecord as unknown as (TechRecordType<'put'>) }; + const { editingTechRecord } = state; + const editedTechRecord = editingTechRecord as unknown as NonVerbTechRecordType<'hgv' | 'lgv' | 'trl'>; + if (editedTechRecord) { + const examinerNotes = editedTechRecord.techRecord_adrDetails_additionalExaminerNotes; + examinerNotes![action.examinerNoteIndex].note = action.additionalExaminerNote; + } + return { ...state, editingTechRecord: editingTechRecord as unknown as TechRecordType<'put'> }; } diff --git a/src/app/store/technical-records/selectors/batch-create.selectors.ts b/src/app/store/technical-records/selectors/batch-create.selectors.ts index d248484adb..28248f2776 100644 --- a/src/app/store/technical-records/selectors/batch-create.selectors.ts +++ b/src/app/store/technical-records/selectors/batch-create.selectors.ts @@ -16,7 +16,9 @@ export const selectVehicleType = createSelector(selectBatchState, (state) => sta export const selectBatchSuccess = createSelector(selectAllBatch, (state) => state.filter((v) => v.created)); export const selectBatchSuccessCount = createSelector(selectBatchSuccess, (state) => state.length); -export const selectBatchCreated = createSelector(selectAllBatch, (state) => state.filter((v) => v.amendedRecord === false)); +export const selectBatchCreated = createSelector(selectAllBatch, (state) => + state.filter((v) => v.amendedRecord === false) +); export const selectBatchCreatedCount = createSelector(selectBatchCreated, (state) => state.length); export const selectBatchCreatedSuccess = createSelector(selectBatchCreated, (state) => state.filter((v) => v.created)); export const selectBatchCreatedSuccessCount = createSelector(selectBatchCreatedSuccess, (state) => state.length); diff --git a/src/app/store/technical-records/selectors/technical-record-service.selectors.spec.ts b/src/app/store/technical-records/selectors/technical-record-service.selectors.spec.ts index 89b2129988..9f16fa144b 100644 --- a/src/app/store/technical-records/selectors/technical-record-service.selectors.spec.ts +++ b/src/app/store/technical-records/selectors/technical-record-service.selectors.spec.ts @@ -2,226 +2,253 @@ import { TechRecordSearchSchema } from '@dvsa/cvs-type-definitions/types/v3/tech import { TechRecordType } from '@dvsa/cvs-type-definitions/types/v3/tech-record/tech-record-verb'; import { TechnicalRecordServiceState, initialState } from '../reducers/technical-record-service.reducer'; import { - editingTechRecord, - getSingleVehicleType, - selectSectionState, - selectTechRecord, - selectTechRecordChanges, - selectTechRecordDeletions, - selectTechRecordHistory, - techRecord, - technicalRecordsLoadingState, + editingTechRecord, + getSingleVehicleType, + selectSectionState, + selectTechRecord, + selectTechRecordChanges, + selectTechRecordDeletions, + selectTechRecordHistory, + techRecord, + technicalRecordsLoadingState, } from './technical-record-service.selectors'; describe('Tech Record Selectors', () => { - describe('selectedTestResultState', () => { - it('should return vehicleTechRecords state', () => { - const state: TechnicalRecordServiceState = { - ...initialState, - vehicleTechRecord: { systemNumber: 'foo', createdTimestamp: 'bar', vin: 'testVin' } as unknown as TechRecordType<'get'>, - }; - const selectedState = techRecord.projector(state); - expect(selectedState).toEqual(state.vehicleTechRecord); - }); - }); - - describe('technicalRecordsLoadingState', () => { - it('should return loading state', () => { - const state: TechnicalRecordServiceState = { ...initialState, loading: true }; - const selectedState = technicalRecordsLoadingState.projector(state); - expect(selectedState).toBeTruthy(); - }); - }); - describe('editingTechRecord', () => { - it('should return editingTechRecords state', () => { - const state: TechnicalRecordServiceState = { - ...initialState, - vehicleTechRecord: { systemNumber: 'foo', createdTimestamp: 'bar', vin: 'testVin' } as TechRecordType<'get'>, - editingTechRecord: { systemNumber: 'bar', createdTimestamp: 'foo', vin: 'testVin2' } as unknown as TechRecordType<'put'>, - }; - const selectedState = editingTechRecord.projector(state); - expect(selectedState).toEqual(state.editingTechRecord); - }); - }); - - describe('selectTechRecord', () => { - const routes = [ - { - statusExpected: 'provisional', - techRecord_createdAt: undefined, - isEditing: false, - vehicle: { - systemNumber: 'foo', createdTimestamp: 'bar', vin: 'testVin', techRecord_statusCode: 'provisional', - }, - }, - { - statusExpected: 'provisional', - techRecord_createdAt: undefined, - isEditing: false, - vehicle: { - systemNumber: 'foo', createdTimestamp: 'bar', vin: 'testVin', techRecord_statusCode: 'provisional', - }, - }, - { - statusExpected: 'archived', - techRecord_createdAt: new Date('2022-02-14').toISOString(), - isEditing: false, - vehicle: { - systemNumber: 'foo', createdTimestamp: 'bar', vin: 'testVin', techRecord_statusCode: 'archived', - }, - }, - { - statusExpected: 'current', - techRecord_createdAt: undefined, - isEditing: true, - vehicle: { - systemNumber: 'foo', createdTimestamp: 'bar', vin: 'testVin', techRecord_statusCode: 'provisional', - }, - }, - ]; - it.each(routes)('should return the $statusExpected record', ({ - statusExpected, techRecord_createdAt, isEditing, vehicle, - }) => { - const mockTechRecord = selectTechRecord.projector({ ...vehicle, techRecord_createdAt } as unknown as TechRecordType<'get'>, isEditing, { - systemNumber: 'foo', - createdTimestamp: 'bar', - vin: 'testVin', - techRecord_statusCode: 'current', - } as unknown as TechRecordType<'put'>); - expect(mockTechRecord).toBeDefined(); - expect(mockTechRecord?.techRecord_statusCode).toBe(statusExpected); - expect((mockTechRecord as TechRecordType<'get'>).techRecord_createdAt).toEqual(techRecord_createdAt); - }); - }); - - describe('getSingleVehicleType', () => { - it('should return the correct vehicle type', () => { - const vehicleTechRecord = { - systemNumber: 'foo', - createdTimestamp: 'bar', - vin: 'testVin', - techRecord_vehicleType: 'foobar', - } as unknown as TechRecordType<'get'>; - const state: TechnicalRecordServiceState = { ...initialState, vehicleTechRecord }; - const selectedVehicleType = getSingleVehicleType.projector(state); - expect(selectedVehicleType).toBe(vehicleTechRecord.techRecord_vehicleType); - }); - }); - - describe('techRecordHistory', () => { - it('should return the vehicles tech record history', () => { - const vehicleTechRecords = [ - { systemNumber: 'foo', createdTimestamp: 'bar', vin: 'testVin' }, - { systemNumber: 'bar', createdTimestamp: 'foo', vin: 'testVin2' }, - ] as TechRecordSearchSchema[]; - const state: TechnicalRecordServiceState = { ...initialState, techRecordHistory: vehicleTechRecords }; - const selectedVehicleHistory = selectTechRecordHistory.projector(state); - expect(selectedVehicleHistory).toBe(vehicleTechRecords); - }); - }); - - describe('selectSectionState', () => { - it('should return the sectionState in the technical record state', () => { - const state: TechnicalRecordServiceState = { ...initialState, sectionState: ['TestSection1', 'TestSection2'] }; - const selectedState = selectSectionState.projector(state); - expect(selectedState?.length).toBe(2); - }); - }); - - describe('selectTechRecordDeletions', () => { - it('should return an object with only the deleted fields from the editing tech record', () => { - - const vehicleTechRecord = { systemNumber: 'foo', createdTimestamp: 'bar', vin: 'testVin' } as TechRecordType<'get'>; - const editedTechRecord = { systemNumber: 'foo', createdTimestamp: 'bar' } as unknown as TechRecordType<'put'>; - - const selectedDeletions = selectTechRecordDeletions.projector(vehicleTechRecord, editedTechRecord); - expect(selectedDeletions).toEqual({ vin: 'testVin' }); - }); - it('should detect deeply nested deletions', () => { - - const vehicleTechRecord = { - systemNumber: 'foo', - createdTimestamp: 'bar', - vin: 'testVin', - techRecord_axles: [{ axleNumber: 1 }, { axleNumber: 2 }], - } as unknown as TechRecordType<'get'>; - const editedTechRecord = { - systemNumber: 'foo', - createdTimestamp: 'bar', - techRecord_axles: [{ axleNumber: 1 }], - } as unknown as TechRecordType<'put'>; - - const selectedDeletions = selectTechRecordDeletions.projector(vehicleTechRecord, editedTechRecord); - expect(selectedDeletions).toEqual({ vin: 'testVin', techRecord_axles: { 1: { axleNumber: 2 } } }); - }); - it('should return an empty object if current vehicle is null', () => { - - const vehicleTechRecord = null as unknown as TechRecordType<'get'>; - const editedTechRecord = { - systemNumber: 'foo', - createdTimestamp: 'bar', - techRecord_axles: [{ axleNumber: 1 }], - } as unknown as TechRecordType<'put'>; - - const selectedDeletions = selectTechRecordDeletions.projector(vehicleTechRecord, editedTechRecord); - expect(selectedDeletions).toEqual({}); - }); - it('should return an empty object if edited vehicle is null', () => { - - const vehicleTechRecord = { - systemNumber: 'foo', - createdTimestamp: 'bar', - vin: 'testVin', - techRecord_axles: [{ axleNumber: 1 }, { axleNumber: 2 }], - } as unknown as TechRecordType<'get'>; - const editedTechRecord = null as unknown as TechRecordType<'put'>; - - const selectedDeletions = selectTechRecordDeletions.projector(vehicleTechRecord, editedTechRecord); - expect(selectedDeletions).toEqual({}); - }); - }); - - describe('selectTechRecordChanges', () => { - it('should detect any changes on the flat tech record', () => { - const vehicleTechRecord = { - systemNumber: 'foo', - createdTimestamp: 'bar', - vin: 'testVin', - techRecord_axles: [{ axleNumber: 1 }, { axleNumber: 2 }], - } as unknown as TechRecordType<'get'>; - const editedTechRecord = { - systemNumber: 'foo', - createdTimestamp: 'bar', - techRecord_vehicleType: 'psv', - techRecord_axles: [{ axleNumber: 1 }], - } as unknown as TechRecordType<'put'>; - const selectedChanges = selectTechRecordChanges.projector(vehicleTechRecord, editedTechRecord); - expect(selectedChanges).toEqual({ vin: undefined, techRecord_axles: { 1: undefined }, techRecord_vehicleType: 'psv' }); - }); - it('should return an empty object if current vehicle is null', () => { - - const vehicleTechRecord = null as unknown as TechRecordType<'get'>; - const editedTechRecord = { - systemNumber: 'foo', - createdTimestamp: 'bar', - techRecord_axles: [{ axleNumber: 1 }], - } as unknown as TechRecordType<'put'>; - - const selectedDeletions = selectTechRecordChanges.projector(vehicleTechRecord, editedTechRecord); - expect(selectedDeletions).toEqual({}); - }); - it('should return an empty object if edited vehicle is null', () => { - - const vehicleTechRecord = { - systemNumber: 'foo', - createdTimestamp: 'bar', - vin: 'testVin', - techRecord_axles: [{ axleNumber: 1 }, { axleNumber: 2 }], - } as unknown as TechRecordType<'get'>; - const editedTechRecord = null as unknown as TechRecordType<'put'>; - - const selectedDeletions = selectTechRecordChanges.projector(vehicleTechRecord, editedTechRecord); - expect(selectedDeletions).toEqual({}); - }); - }); + describe('selectedTestResultState', () => { + it('should return vehicleTechRecords state', () => { + const state: TechnicalRecordServiceState = { + ...initialState, + vehicleTechRecord: { + systemNumber: 'foo', + createdTimestamp: 'bar', + vin: 'testVin', + } as unknown as TechRecordType<'get'>, + }; + const selectedState = techRecord.projector(state); + expect(selectedState).toEqual(state.vehicleTechRecord); + }); + }); + + describe('technicalRecordsLoadingState', () => { + it('should return loading state', () => { + const state: TechnicalRecordServiceState = { ...initialState, loading: true }; + const selectedState = technicalRecordsLoadingState.projector(state); + expect(selectedState).toBeTruthy(); + }); + }); + describe('editingTechRecord', () => { + it('should return editingTechRecords state', () => { + const state: TechnicalRecordServiceState = { + ...initialState, + vehicleTechRecord: { systemNumber: 'foo', createdTimestamp: 'bar', vin: 'testVin' } as TechRecordType<'get'>, + editingTechRecord: { + systemNumber: 'bar', + createdTimestamp: 'foo', + vin: 'testVin2', + } as unknown as TechRecordType<'put'>, + }; + const selectedState = editingTechRecord.projector(state); + expect(selectedState).toEqual(state.editingTechRecord); + }); + }); + + describe('selectTechRecord', () => { + const routes = [ + { + statusExpected: 'provisional', + techRecord_createdAt: undefined, + isEditing: false, + vehicle: { + systemNumber: 'foo', + createdTimestamp: 'bar', + vin: 'testVin', + techRecord_statusCode: 'provisional', + }, + }, + { + statusExpected: 'provisional', + techRecord_createdAt: undefined, + isEditing: false, + vehicle: { + systemNumber: 'foo', + createdTimestamp: 'bar', + vin: 'testVin', + techRecord_statusCode: 'provisional', + }, + }, + { + statusExpected: 'archived', + techRecord_createdAt: new Date('2022-02-14').toISOString(), + isEditing: false, + vehicle: { + systemNumber: 'foo', + createdTimestamp: 'bar', + vin: 'testVin', + techRecord_statusCode: 'archived', + }, + }, + { + statusExpected: 'current', + techRecord_createdAt: undefined, + isEditing: true, + vehicle: { + systemNumber: 'foo', + createdTimestamp: 'bar', + vin: 'testVin', + techRecord_statusCode: 'provisional', + }, + }, + ]; + it.each(routes)( + 'should return the $statusExpected record', + ({ statusExpected, techRecord_createdAt, isEditing, vehicle }) => { + const mockTechRecord = selectTechRecord.projector( + { ...vehicle, techRecord_createdAt } as unknown as TechRecordType<'get'>, + isEditing, + { + systemNumber: 'foo', + createdTimestamp: 'bar', + vin: 'testVin', + techRecord_statusCode: 'current', + } as unknown as TechRecordType<'put'> + ); + expect(mockTechRecord).toBeDefined(); + expect(mockTechRecord?.techRecord_statusCode).toBe(statusExpected); + expect((mockTechRecord as TechRecordType<'get'>).techRecord_createdAt).toEqual(techRecord_createdAt); + } + ); + }); + + describe('getSingleVehicleType', () => { + it('should return the correct vehicle type', () => { + const vehicleTechRecord = { + systemNumber: 'foo', + createdTimestamp: 'bar', + vin: 'testVin', + techRecord_vehicleType: 'foobar', + } as unknown as TechRecordType<'get'>; + const state: TechnicalRecordServiceState = { ...initialState, vehicleTechRecord }; + const selectedVehicleType = getSingleVehicleType.projector(state); + expect(selectedVehicleType).toBe(vehicleTechRecord.techRecord_vehicleType); + }); + }); + + describe('techRecordHistory', () => { + it('should return the vehicles tech record history', () => { + const vehicleTechRecords = [ + { systemNumber: 'foo', createdTimestamp: 'bar', vin: 'testVin' }, + { systemNumber: 'bar', createdTimestamp: 'foo', vin: 'testVin2' }, + ] as TechRecordSearchSchema[]; + const state: TechnicalRecordServiceState = { ...initialState, techRecordHistory: vehicleTechRecords }; + const selectedVehicleHistory = selectTechRecordHistory.projector(state); + expect(selectedVehicleHistory).toBe(vehicleTechRecords); + }); + }); + + describe('selectSectionState', () => { + it('should return the sectionState in the technical record state', () => { + const state: TechnicalRecordServiceState = { ...initialState, sectionState: ['TestSection1', 'TestSection2'] }; + const selectedState = selectSectionState.projector(state); + expect(selectedState?.length).toBe(2); + }); + }); + + describe('selectTechRecordDeletions', () => { + it('should return an object with only the deleted fields from the editing tech record', () => { + const vehicleTechRecord = { + systemNumber: 'foo', + createdTimestamp: 'bar', + vin: 'testVin', + } as TechRecordType<'get'>; + const editedTechRecord = { systemNumber: 'foo', createdTimestamp: 'bar' } as unknown as TechRecordType<'put'>; + + const selectedDeletions = selectTechRecordDeletions.projector(vehicleTechRecord, editedTechRecord); + expect(selectedDeletions).toEqual({ vin: 'testVin' }); + }); + it('should detect deeply nested deletions', () => { + const vehicleTechRecord = { + systemNumber: 'foo', + createdTimestamp: 'bar', + vin: 'testVin', + techRecord_axles: [{ axleNumber: 1 }, { axleNumber: 2 }], + } as unknown as TechRecordType<'get'>; + const editedTechRecord = { + systemNumber: 'foo', + createdTimestamp: 'bar', + techRecord_axles: [{ axleNumber: 1 }], + } as unknown as TechRecordType<'put'>; + + const selectedDeletions = selectTechRecordDeletions.projector(vehicleTechRecord, editedTechRecord); + expect(selectedDeletions).toEqual({ vin: 'testVin', techRecord_axles: { 1: { axleNumber: 2 } } }); + }); + it('should return an empty object if current vehicle is null', () => { + const vehicleTechRecord = null as unknown as TechRecordType<'get'>; + const editedTechRecord = { + systemNumber: 'foo', + createdTimestamp: 'bar', + techRecord_axles: [{ axleNumber: 1 }], + } as unknown as TechRecordType<'put'>; + + const selectedDeletions = selectTechRecordDeletions.projector(vehicleTechRecord, editedTechRecord); + expect(selectedDeletions).toEqual({}); + }); + it('should return an empty object if edited vehicle is null', () => { + const vehicleTechRecord = { + systemNumber: 'foo', + createdTimestamp: 'bar', + vin: 'testVin', + techRecord_axles: [{ axleNumber: 1 }, { axleNumber: 2 }], + } as unknown as TechRecordType<'get'>; + const editedTechRecord = null as unknown as TechRecordType<'put'>; + + const selectedDeletions = selectTechRecordDeletions.projector(vehicleTechRecord, editedTechRecord); + expect(selectedDeletions).toEqual({}); + }); + }); + + describe('selectTechRecordChanges', () => { + it('should detect any changes on the flat tech record', () => { + const vehicleTechRecord = { + systemNumber: 'foo', + createdTimestamp: 'bar', + vin: 'testVin', + techRecord_axles: [{ axleNumber: 1 }, { axleNumber: 2 }], + } as unknown as TechRecordType<'get'>; + const editedTechRecord = { + systemNumber: 'foo', + createdTimestamp: 'bar', + techRecord_vehicleType: 'psv', + techRecord_axles: [{ axleNumber: 1 }], + } as unknown as TechRecordType<'put'>; + const selectedChanges = selectTechRecordChanges.projector(vehicleTechRecord, editedTechRecord); + expect(selectedChanges).toEqual({ + vin: undefined, + techRecord_axles: { 1: undefined }, + techRecord_vehicleType: 'psv', + }); + }); + it('should return an empty object if current vehicle is null', () => { + const vehicleTechRecord = null as unknown as TechRecordType<'get'>; + const editedTechRecord = { + systemNumber: 'foo', + createdTimestamp: 'bar', + techRecord_axles: [{ axleNumber: 1 }], + } as unknown as TechRecordType<'put'>; + + const selectedDeletions = selectTechRecordChanges.projector(vehicleTechRecord, editedTechRecord); + expect(selectedDeletions).toEqual({}); + }); + it('should return an empty object if edited vehicle is null', () => { + const vehicleTechRecord = { + systemNumber: 'foo', + createdTimestamp: 'bar', + vin: 'testVin', + techRecord_axles: [{ axleNumber: 1 }, { axleNumber: 2 }], + } as unknown as TechRecordType<'get'>; + const editedTechRecord = null as unknown as TechRecordType<'put'>; + + const selectedDeletions = selectTechRecordChanges.projector(vehicleTechRecord, editedTechRecord); + expect(selectedDeletions).toEqual({}); + }); + }); }); diff --git a/src/app/store/technical-records/selectors/technical-record-service.selectors.ts b/src/app/store/technical-records/selectors/technical-record-service.selectors.ts index ad1b3688e2..d01b8801ae 100644 --- a/src/app/store/technical-records/selectors/technical-record-service.selectors.ts +++ b/src/app/store/technical-records/selectors/technical-record-service.selectors.ts @@ -7,7 +7,10 @@ import { getTechRecordState } from '../reducers/technical-record-service.reducer export const techRecord = createSelector(getTechRecordState, (state) => state.vehicleTechRecord); -export const getSingleVehicleType = createSelector(getTechRecordState, (state) => state.vehicleTechRecord?.techRecord_vehicleType); +export const getSingleVehicleType = createSelector( + getTechRecordState, + (state) => state.vehicleTechRecord?.techRecord_vehicleType +); export const editingTechRecord = createSelector(getTechRecordState, (state) => state.editingTechRecord); @@ -16,26 +19,27 @@ export const technicalRecordsLoadingState = createSelector(getTechRecordState, ( export const getCanGeneratePlate = createSelector(getTechRecordState, (state) => state.canGeneratePlate); export const selectTechRecordHistory = createSelector(getTechRecordState, (state) => - state.techRecordHistory?.sort((a, b) => { - const aTimeCode = new Date(a.createdTimestamp).getTime(); - const bTimeCode = new Date(b.createdTimestamp).getTime(); - // return aTimeCode < bTimeCode ? 1 : aTimeCode > bTimeCode ? -1 : 0; - if (aTimeCode < bTimeCode) { - return 1; - } - if (aTimeCode > bTimeCode) { - return -1; - } - return 0; - })); + state.techRecordHistory?.sort((a, b) => { + const aTimeCode = new Date(a.createdTimestamp).getTime(); + const bTimeCode = new Date(b.createdTimestamp).getTime(); + // return aTimeCode < bTimeCode ? 1 : aTimeCode > bTimeCode ? -1 : 0; + if (aTimeCode < bTimeCode) { + return 1; + } + if (aTimeCode > bTimeCode) { + return -1; + } + return 0; + }) +); export const selectTechRecord = createSelector( - techRecord, - selectRouteDataProperty('isEditing'), - editingTechRecord, - (viewableTechRecord, isEditing, editableTechRecord): V3TechRecordModel | undefined => { - return isEditing ? editableTechRecord : viewableTechRecord; - }, + techRecord, + selectRouteDataProperty('isEditing'), + editingTechRecord, + (viewableTechRecord, isEditing, editableTechRecord): V3TechRecordModel | undefined => { + return isEditing ? editableTechRecord : viewableTechRecord; + } ); export const selectSectionState = createSelector(getTechRecordState, (state) => state.sectionState); @@ -43,16 +47,16 @@ export const selectSectionState = createSelector(getTechRecordState, (state) => export const selectScrollPosition = createSelector(getTechRecordState, (state) => state.scrollPosition); export const selectTechRecordDeletions = createSelector(techRecord, editingTechRecord, (current, amended) => { - if (current == null || amended == null) return {}; + if (current == null || amended == null) return {}; - // Note: we use added changes from the comparison of amended to current, as deletions are not working as expected - return detailedDiff(amended, current).added as Partial>; + // Note: we use added changes from the comparison of amended to current, as deletions are not working as expected + return detailedDiff(amended, current).added as Partial>; }); export const selectTechRecordChanges = createSelector(techRecord, editingTechRecord, (current, amended) => { - if (current == null || amended == null) return {}; + if (current == null || amended == null) return {}; - const changes = detailedDiff(current, amended); + const changes = detailedDiff(current, amended); - return { ...changes.added, ...changes.updated, ...changes.deleted } as Partial>; + return { ...changes.added, ...changes.updated, ...changes.deleted } as Partial>; }); diff --git a/src/app/store/technical-records/technical-records-state.module.ts b/src/app/store/technical-records/technical-records-state.module.ts index 1d4ba44be5..bc2d804240 100644 --- a/src/app/store/technical-records/technical-records-state.module.ts +++ b/src/app/store/technical-records/technical-records-state.module.ts @@ -3,14 +3,17 @@ import { NgModule } from '@angular/core'; import { EffectsModule } from '@ngrx/effects'; import { StoreModule } from '@ngrx/store'; import { TechnicalRecordServiceEffects } from './effects/technical-record-service.effects'; -import { STORE_FEATURE_TECHNICAL_RECORDS_KEY, vehicleTechRecordReducer } from './reducers/technical-record-service.reducer'; +import { + STORE_FEATURE_TECHNICAL_RECORDS_KEY, + vehicleTechRecordReducer, +} from './reducers/technical-record-service.reducer'; @NgModule({ - declarations: [], - imports: [ - CommonModule, - StoreModule.forFeature(STORE_FEATURE_TECHNICAL_RECORDS_KEY, vehicleTechRecordReducer), - EffectsModule.forFeature([TechnicalRecordServiceEffects]), - ], + declarations: [], + imports: [ + CommonModule, + StoreModule.forFeature(STORE_FEATURE_TECHNICAL_RECORDS_KEY, vehicleTechRecordReducer), + EffectsModule.forFeature([TechnicalRecordServiceEffects]), + ], }) export class TechnicalRecordsStateModule {} diff --git a/src/app/store/test-records/actions/test-records.actions.spec.ts b/src/app/store/test-records/actions/test-records.actions.spec.ts index 3739bc19de..9e45466537 100644 --- a/src/app/store/test-records/actions/test-records.actions.spec.ts +++ b/src/app/store/test-records/actions/test-records.actions.spec.ts @@ -1,33 +1,33 @@ import { - createRequiredStandard, - fetchSelectedTestResult, - fetchSelectedTestResultFailed, - fetchSelectedTestResultSuccess, - fetchTestResults, - fetchTestResultsBySystemNumber, - fetchTestResultsBySystemNumberFailed, - fetchTestResultsBySystemNumberSuccess, - fetchTestResultsFailed, - fetchTestResultsSuccess, - removeRequiredStandard, - updateRequiredStandard, - updateResultOfTestRequiredStandards, + createRequiredStandard, + fetchSelectedTestResult, + fetchSelectedTestResultFailed, + fetchSelectedTestResultSuccess, + fetchTestResults, + fetchTestResultsBySystemNumber, + fetchTestResultsBySystemNumberFailed, + fetchTestResultsBySystemNumberSuccess, + fetchTestResultsFailed, + fetchTestResultsSuccess, + removeRequiredStandard, + updateRequiredStandard, + updateResultOfTestRequiredStandards, } from './test-records.actions'; describe('Test Result Actions', () => { - it('should return correct types', () => { - expect(fetchTestResults.type).toBe('[API/test-results] Fetch All'); - expect(fetchTestResultsSuccess.type).toBe('[API/test-results] Fetch All Success'); - expect(fetchTestResultsFailed.type).toBe('[API/test-results] Fetch All Failed'); - expect(fetchTestResultsBySystemNumber.type).toBe('[API/test-results] Fetch All By systemNumber'); - expect(fetchTestResultsBySystemNumberSuccess.type).toBe('[API/test-results] Fetch All By systemNumber Success'); - expect(fetchTestResultsBySystemNumberFailed.type).toBe('[API/test-results] Fetch All By systemNumber Failed'); - expect(fetchSelectedTestResult.type).toBe('[API/test-results], Fetch by ID'); - expect(fetchSelectedTestResultSuccess.type).toBe('[API/test-results], Fetch by ID Success'); - expect(fetchSelectedTestResultFailed.type).toBe('[API/test-results], Fetch by ID Failed'); - expect(createRequiredStandard.type).toBe('[test-results] create required standard'); - expect(updateRequiredStandard.type).toBe('[test-results] update required standard'); - expect(removeRequiredStandard.type).toBe('[test-results] remove required standard'); - expect(updateResultOfTestRequiredStandards.type).toBe('[test-results] update test result required standards'); - }); + it('should return correct types', () => { + expect(fetchTestResults.type).toBe('[API/test-results] Fetch All'); + expect(fetchTestResultsSuccess.type).toBe('[API/test-results] Fetch All Success'); + expect(fetchTestResultsFailed.type).toBe('[API/test-results] Fetch All Failed'); + expect(fetchTestResultsBySystemNumber.type).toBe('[API/test-results] Fetch All By systemNumber'); + expect(fetchTestResultsBySystemNumberSuccess.type).toBe('[API/test-results] Fetch All By systemNumber Success'); + expect(fetchTestResultsBySystemNumberFailed.type).toBe('[API/test-results] Fetch All By systemNumber Failed'); + expect(fetchSelectedTestResult.type).toBe('[API/test-results], Fetch by ID'); + expect(fetchSelectedTestResultSuccess.type).toBe('[API/test-results], Fetch by ID Success'); + expect(fetchSelectedTestResultFailed.type).toBe('[API/test-results], Fetch by ID Failed'); + expect(createRequiredStandard.type).toBe('[test-results] create required standard'); + expect(updateRequiredStandard.type).toBe('[test-results] update required standard'); + expect(removeRequiredStandard.type).toBe('[test-results] remove required standard'); + expect(updateResultOfTestRequiredStandards.type).toBe('[test-results] update test result required standards'); + }); }); diff --git a/src/app/store/test-records/actions/test-records.actions.ts b/src/app/store/test-records/actions/test-records.actions.ts index 6c3c685350..72efdfc1ad 100644 --- a/src/app/store/test-records/actions/test-records.actions.ts +++ b/src/app/store/test-records/actions/test-records.actions.ts @@ -9,62 +9,107 @@ import { Update } from '@ngrx/entity'; import { createAction, props } from '@ngrx/store'; export const fetchTestResults = createAction('[API/test-results] Fetch All'); -export const fetchTestResultsSuccess = createAction('[API/test-results] Fetch All Success', props<{ payload: Array }>()); +export const fetchTestResultsSuccess = createAction( + '[API/test-results] Fetch All Success', + props<{ payload: Array }>() +); export const fetchTestResultsFailed = createAction('[API/test-results] Fetch All Failed', props()); -export const fetchTestResultsBySystemNumber = createAction('[API/test-results] Fetch All By systemNumber', props<{ systemNumber: string }>()); +export const fetchTestResultsBySystemNumber = createAction( + '[API/test-results] Fetch All By systemNumber', + props<{ systemNumber: string }>() +); export const fetchTestResultsBySystemNumberSuccess = createAction( - '[API/test-results] Fetch All By systemNumber Success', - props<{ payload: Array }>(), + '[API/test-results] Fetch All By systemNumber Success', + props<{ payload: Array }>() +); +export const fetchTestResultsBySystemNumberFailed = createAction( + '[API/test-results] Fetch All By systemNumber Failed', + props() ); -export const fetchTestResultsBySystemNumberFailed = createAction('[API/test-results] Fetch All By systemNumber Failed', props()); export const fetchSelectedTestResult = createAction('[API/test-results], Fetch by ID'); -export const fetchSelectedTestResultSuccess = createAction('[API/test-results], Fetch by ID Success', props<{ payload: TestResultModel }>()); -export const fetchSelectedTestResultFailed = createAction('[API/test-results], Fetch by ID Failed', props()); +export const fetchSelectedTestResultSuccess = createAction( + '[API/test-results], Fetch by ID Success', + props<{ payload: TestResultModel }>() +); +export const fetchSelectedTestResultFailed = createAction( + '[API/test-results], Fetch by ID Failed', + props() +); export const createTestResult = createAction('[test-results] Create test result', props<{ value: TestResultModel }>()); -export const createTestResultSuccess = createAction('[API/test-results] Create test result Success', props<{ payload: Update }>()); -export const createTestResultFailed = createAction('[API/test-results] Create test result Failed', props<{ errors: GlobalError[] }>()); +export const createTestResultSuccess = createAction( + '[API/test-results] Create test result Success', + props<{ payload: Update }>() +); +export const createTestResultFailed = createAction( + '[API/test-results] Create test result Failed', + props<{ errors: GlobalError[] }>() +); export const updateTestResult = createAction('[test-results] Update test result', props<{ value: TestResultModel }>()); -export const updateTestResultSuccess = createAction('[API/test-results] Update test result Success', props<{ payload: Update }>()); -export const updateTestResultFailed = createAction('[API/test-results] Update test result Failed', props<{ errors: GlobalError[] }>()); +export const updateTestResultSuccess = createAction( + '[API/test-results] Update test result Success', + props<{ payload: Update }>() +); +export const updateTestResultFailed = createAction( + '[API/test-results] Update test result Failed', + props<{ errors: GlobalError[] }>() +); export const cleanTestResult = createAction('[test-results] Clean test result for submission'); export const editingTestResult = createAction('[test-results] Editing', props<{ testTypeId: string }>()); -export const updateEditingTestResult = createAction('[test-results] Update editing', props<{ testResult: TestResultModel }>()); +export const updateEditingTestResult = createAction( + '[test-results] Update editing', + props<{ testResult: TestResultModel }>() +); export const cancelEditingTestResult = createAction('[test-results] Cancel editing'); -export const setResultOfTest = createAction('[test-results] set the result of the test', props<{ result: resultOfTestEnum }>()); +export const setResultOfTest = createAction( + '[test-results] set the result of the test', + props<{ result: resultOfTestEnum }>() +); export const updateResultOfTest = createAction('[test-results] update the result of the test'); -export const initialContingencyTest = createAction('[Contingency test] Create', props<{ testResult: Partial }>()); +export const initialContingencyTest = createAction( + '[Contingency test] Create', + props<{ testResult: Partial }>() +); -export const contingencyTestTypeSelected = createAction('[Test Results] contingency test type selected', props<{ testType: string }>()); +export const contingencyTestTypeSelected = createAction( + '[Test Results] contingency test type selected', + props<{ testType: string }>() +); export const testTypeIdChanged = createAction('[test-results] test type id changed', props<{ testTypeId: string }>()); export const templateSectionsChanged = createAction( - '[test-results] Template sections changed', - props<{ sectionTemplates: FormNode[]; sectionsValue: TestResultModel | undefined }>(), + '[test-results] Template sections changed', + props<{ sectionTemplates: FormNode[]; sectionsValue: TestResultModel | undefined }>() ); export const createDefect = createAction('[test-results] create defect', props<{ defect: TestResultDefect }>()); -export const updateDefect = createAction('[test-results] save defect', props<{ defect: TestResultDefect; index: number }>()); +export const updateDefect = createAction( + '[test-results] save defect', + props<{ defect: TestResultDefect; index: number }>() +); export const removeDefect = createAction('[test-results] remove defect', props<{ index: number }>()); export const createRequiredStandard = createAction( - '[test-results] create required standard', - props<{ requiredStandard: TestResultRequiredStandard }>(), + '[test-results] create required standard', + props<{ requiredStandard: TestResultRequiredStandard }>() ); export const updateRequiredStandard = createAction( - '[test-results] update required standard', - props<{ requiredStandard: TestResultRequiredStandard, index: number }>(), + '[test-results] update required standard', + props<{ requiredStandard: TestResultRequiredStandard; index: number }>() ); -export const removeRequiredStandard = createAction('[test-results] remove required standard', props<{ index: number }>()); +export const removeRequiredStandard = createAction( + '[test-results] remove required standard', + props<{ index: number }>() +); export const updateResultOfTestRequiredStandards = createAction('[test-results] update test result required standards'); diff --git a/src/app/store/test-records/effects/test-records.effects.spec.ts b/src/app/store/test-records/effects/test-records.effects.spec.ts index 701441dbe9..2700964336 100644 --- a/src/app/store/test-records/effects/test-records.effects.spec.ts +++ b/src/app/store/test-records/effects/test-records.effects.spec.ts @@ -18,6 +18,7 @@ import { VehicleTypes } from '@models/vehicle-tech-record.model'; import { provideMockActions } from '@ngrx/effects/testing'; import { Action } from '@ngrx/store'; import { MockStore, provideMockStore } from '@ngrx/store/testing'; +import { FeatureToggleService } from '@services/feature-toggle-service/feature-toggle-service'; import { RouterService } from '@services/router/router.service'; import { TestRecordsService } from '@services/test-records/test-records.service'; import { UserService } from '@services/user-service/user-service'; @@ -25,809 +26,858 @@ import { State, initialAppState } from '@store/.'; import { selectQueryParams, selectRouteNestedParams } from '@store/router/selectors/router.selectors'; import { Observable, of } from 'rxjs'; import { TestScheduler } from 'rxjs/testing'; -import { FeatureToggleService } from '@services/feature-toggle-service/feature-toggle-service'; import { mockTestResult, mockTestResultList } from '../../../../mocks/mock-test-result'; import { masterTpl } from '../../../forms/templates/test-records/master.template'; import { - contingencyTestTypeSelected, - createTestResult, - createTestResultFailed, - createTestResultSuccess, - editingTestResult, - fetchSelectedTestResult, - fetchSelectedTestResultFailed, - fetchSelectedTestResultSuccess, - fetchTestResultsBySystemNumber, - fetchTestResultsBySystemNumberFailed, - fetchTestResultsBySystemNumberSuccess, - templateSectionsChanged, - testTypeIdChanged, - updateResultOfTest, - updateTestResult, - updateTestResultFailed, - updateTestResultSuccess, + contingencyTestTypeSelected, + createTestResult, + createTestResultFailed, + createTestResultSuccess, + editingTestResult, + fetchSelectedTestResult, + fetchSelectedTestResultFailed, + fetchSelectedTestResultSuccess, + fetchTestResultsBySystemNumber, + fetchTestResultsBySystemNumberFailed, + fetchTestResultsBySystemNumberSuccess, + templateSectionsChanged, + testTypeIdChanged, + updateResultOfTest, + updateTestResult, + updateTestResultFailed, + updateTestResultSuccess, } from '../actions/test-records.actions'; import { isTestTypeOldIvaOrMsva, selectedTestResultState, testResultInEdit } from '../selectors/test-records.selectors'; import { TestResultsEffects } from './test-records.effects'; jest.mock('../../../forms/templates/test-records/master.template', () => ({ - __esModule: true, - default: jest.fn(), - masterTpl: { - psv: { - default: { - test: { - name: 'Default', - type: FormNodeTypes.GROUP, - children: [ - { - name: 'testTypes', - type: FormNodeTypes.ARRAY, - children: [{ name: '0', type: FormNodeTypes.GROUP, children: [{ name: 'testTypeId', type: FormNodeTypes.CONTROL, value: '' }] }], - }, - ], - }, - }, - testTypesGroup1: { - test: { - name: 'Test', - type: FormNodeTypes.GROUP, - children: [ - { - name: 'testTypes', - type: FormNodeTypes.ARRAY, - children: [{ name: '0', type: FormNodeTypes.GROUP, children: [{ name: 'testTypeId', type: FormNodeTypes.CONTROL, value: '' }] }], - }, - ], - }, - }, - testTypesSpecialistGroup1: { - test: { - name: 'NewSpecialistTest', - type: FormNodeTypes.GROUP, - children: [ - { - name: 'testTypes', - type: FormNodeTypes.ARRAY, - children: [{ name: '0', type: FormNodeTypes.GROUP, children: [{ name: 'testTypeId', type: FormNodeTypes.CONTROL, value: '' }] }], - }, - ], - }, - }, - testTypesSpecialistGroup1OldIVAorMSVA: { - test: { - name: 'OldSpecialistTest', - type: FormNodeTypes.GROUP, - children: [ - { - name: 'testTypes', - type: FormNodeTypes.ARRAY, - children: [{ name: '0', type: FormNodeTypes.GROUP, children: [{ name: 'testTypeId', type: FormNodeTypes.CONTROL, value: '' }] }], - }, - ], - }, - }, - }, - }, + __esModule: true, + default: jest.fn(), + masterTpl: { + psv: { + default: { + test: { + name: 'Default', + type: FormNodeTypes.GROUP, + children: [ + { + name: 'testTypes', + type: FormNodeTypes.ARRAY, + children: [ + { + name: '0', + type: FormNodeTypes.GROUP, + children: [{ name: 'testTypeId', type: FormNodeTypes.CONTROL, value: '' }], + }, + ], + }, + ], + }, + }, + testTypesGroup1: { + test: { + name: 'Test', + type: FormNodeTypes.GROUP, + children: [ + { + name: 'testTypes', + type: FormNodeTypes.ARRAY, + children: [ + { + name: '0', + type: FormNodeTypes.GROUP, + children: [{ name: 'testTypeId', type: FormNodeTypes.CONTROL, value: '' }], + }, + ], + }, + ], + }, + }, + testTypesSpecialistGroup1: { + test: { + name: 'NewSpecialistTest', + type: FormNodeTypes.GROUP, + children: [ + { + name: 'testTypes', + type: FormNodeTypes.ARRAY, + children: [ + { + name: '0', + type: FormNodeTypes.GROUP, + children: [{ name: 'testTypeId', type: FormNodeTypes.CONTROL, value: '' }], + }, + ], + }, + ], + }, + }, + testTypesSpecialistGroup1OldIVAorMSVA: { + test: { + name: 'OldSpecialistTest', + type: FormNodeTypes.GROUP, + children: [ + { + name: 'testTypes', + type: FormNodeTypes.ARRAY, + children: [ + { + name: '0', + type: FormNodeTypes.GROUP, + children: [{ name: 'testTypeId', type: FormNodeTypes.CONTROL, value: '' }], + }, + ], + }, + ], + }, + }, + }, + }, })); describe('TestResultsEffects', () => { - let effects: TestResultsEffects; - let actions$ = new Observable(); - let testScheduler: TestScheduler; - let testResultsService: TestRecordsService; - let store: MockStore; - let featureToggleService: FeatureToggleService; - - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [HttpClientTestingModule, TestResultsApiModule, RouterTestingModule], - providers: [ - TestResultsEffects, - provideMockActions(() => actions$), - TestRecordsService, - provideMockStore({ - initialState: initialAppState, - selectors: [ - { - selector: selectRouteNestedParams, - value: [ - { - systemNumber: 'systemNumber01', - testResultId: 'testResult01', - }, - ], - }, - ], - }), - { - provide: UserService, - useValue: { - user$: of({ name: 'testername', userEmail: 'test@test.com', oid: '123zxc' }), - name$: of('name'), - userEmail$: of('userEmail'), - id$: of('iod'), - }, - }, - RouterService, - DynamicFormService, - FeatureToggleService, - ], - }); - - store = TestBed.inject(MockStore); - effects = TestBed.inject(TestResultsEffects); - testResultsService = TestBed.inject(TestRecordsService); - featureToggleService = TestBed.inject(FeatureToggleService); - }); - - beforeEach(() => { - testScheduler = new TestScheduler((actual, expected) => { - expect(actual).toEqual(expected); - }); - }); - - describe('fetchTestResultsBySystemNumber$', () => { - it('should return fetchTestResultBySystemNumberSuccess action on successfull API call', () => { - testScheduler.run(({ hot, cold, expectObservable }) => { - const testResults = mockTestResultList(); - - // mock action to trigger effect - actions$ = hot('-a--', { a: fetchTestResultsBySystemNumber }); - - // mock service call - jest.spyOn(testResultsService, 'fetchTestResultbySystemNumber').mockReturnValue(cold('--a|', { a: testResults })); - - // expect effect to return success action - expectObservable(effects.fetchTestResultsBySystemNumber$).toBe('---b', { - b: fetchTestResultsBySystemNumberSuccess({ payload: testResults }), - }); - }); - }); - - it('should return fetchTestResultsBySystemNumberFailed action on API error', () => { - testScheduler.run(({ hot, cold, expectObservable }) => { - actions$ = hot('-a--', { a: fetchTestResultsBySystemNumber }); - - const expectedError = new HttpErrorResponse({ - status: 500, - statusText: 'Internal server error', - }); - jest.spyOn(testResultsService, 'fetchTestResultbySystemNumber').mockReturnValue(cold('--#|', {}, expectedError)); - - expectObservable(effects.fetchTestResultsBySystemNumber$).toBe('---b', { - b: fetchTestResultsBySystemNumberFailed({ error: 'Http failure response for (unknown url): 500 Internal server error' }), - }); - }); - }); - - it('should return fetchTestResultsBySystemNumberSuccess on a 404', () => { - testScheduler.run(({ hot, cold, expectObservable }) => { - actions$ = hot('-a--', { a: fetchTestResultsBySystemNumber }); - - const expectedError = new HttpErrorResponse({ - status: 404, - statusText: 'Not found', - }); - jest.spyOn(testResultsService, 'fetchTestResultbySystemNumber').mockReturnValue(cold('--#|', {}, expectedError)); - - expectObservable(effects.fetchTestResultsBySystemNumber$).toBe('---b', { - b: fetchTestResultsBySystemNumberSuccess({ payload: [] as TestResultModel[] }), - }); - }); - }); - }); - - describe('fetchSelectedTestResult$', () => { - it('should return fetchSelectedTestResultSuccess', () => { - testScheduler.run(({ hot, cold, expectObservable }) => { - const testResult = mockTestResult(); - - actions$ = hot('-a-', { a: fetchSelectedTestResult() }); - - jest.spyOn(testResultsService, 'fetchTestResultbySystemNumber').mockReturnValue(cold('--a|', { a: [testResult] })); - - expectObservable(effects.fetchSelectedTestResult$).toBe('---b', { - b: fetchSelectedTestResultSuccess({ payload: testResult }), - }); - }); - }); - - it('should return fetchSelectedTestResultFailed if API returns empty array', () => { - testScheduler.run(({ hot, cold, expectObservable }) => { - actions$ = hot('-a-', { a: fetchSelectedTestResult() }); - - jest.spyOn(testResultsService, 'fetchTestResultbySystemNumber').mockReturnValue(cold('--a|', { a: [] })); - - expectObservable(effects.fetchSelectedTestResult$).toBe('---b', { - b: fetchSelectedTestResultFailed({ error: 'Test result not found' }), - }); - }); - }); - - it('should return fetchSelectedTestResultFailed if API returns an error', () => { - testScheduler.run(({ hot, cold, expectObservable }) => { - actions$ = hot('-a-', { a: fetchSelectedTestResult() }); - - // mock service call - const expectedError = new HttpErrorResponse({ - status: 400, - statusText: 'Bad Request', - }); - jest.spyOn(testResultsService, 'fetchTestResultbySystemNumber').mockReturnValue(cold('--#|', {}, expectedError)); - - expectObservable(effects.fetchSelectedTestResult$).toBe('---b', { - b: fetchSelectedTestResultFailed({ error: 'Http failure response for (unknown url): 400 Bad Request' }), - }); - }); - }); - }); - - describe('updateTestResult$', () => { - const newTestResult = { testResultId: '1' } as TestResultModel; - - it('should dispatch updateTestResultSuccess with return payload', () => { - testScheduler.run(({ hot, cold, expectObservable }) => { - actions$ = hot('-a-', { a: updateTestResult({ value: newTestResult }) }); - - jest.spyOn(testResultsService, 'saveTestResult').mockReturnValue(cold('---b', { b: newTestResult })); - - expectObservable(effects.updateTestResult$).toBe('----b', { - b: updateTestResultSuccess({ payload: { id: '1', changes: newTestResult } }), - }); - }); - }); - - it('should dispatch updateTestResultFailed action with empty errors array', () => { - testScheduler.run(({ hot, cold, expectObservable }) => { - actions$ = hot('-a-', { a: updateTestResult({ value: newTestResult }) }); - - jest - .spyOn(testResultsService, 'saveTestResult') - .mockReturnValue(cold('---#|', {}, new HttpErrorResponse({ status: 500, error: 'some error' }))); - - expectObservable(effects.updateTestResult$).toBe('----b', { - b: updateTestResultFailed({ errors: [] }), - }); - }); - }); - - it('should dispatch updateTestResultFailed action with validation errors', () => { - testScheduler.run(({ hot, cold, expectObservable }) => { - actions$ = hot('-a-', { a: updateTestResult({ value: newTestResult }) }); - - jest - .spyOn(testResultsService, 'saveTestResult') - .mockReturnValue( - cold('---#|', {}, new HttpErrorResponse({ status: 400, error: { errors: ['"name" is missing', '"age" is missing', 'random error'] } })), - ); - - const expectedErrors: GlobalError[] = [ - { error: '"name" is missing', anchorLink: 'name' }, - { error: '"age" is missing', anchorLink: 'age' }, - { error: 'random error', anchorLink: '' }, - ]; - expectObservable(effects.updateTestResult$).toBe('----b', { - b: updateTestResultFailed({ errors: expectedErrors }), - }); - }); - }); - }); - - describe('generateSectionTemplatesAndtestResultToUpdate$', () => { - beforeEach(() => { - store.resetSelectors(); - jest.resetModules(); - }); - - it('should dispatch templateSectionsChanged with new sections and test result', () => { - const testResult = createMockTestResult({ - vehicleType: VehicleTypes.PSV, - testTypes: [createMockTestType({ testTypeId: '1' })], - }); - - testScheduler.run(({ hot, expectObservable }) => { - store.overrideSelector(selectedTestResultState, testResult); - - actions$ = hot('-a', { - a: editingTestResult({ - testTypeId: '1', - }), - }); - - expectObservable(effects.generateSectionTemplatesAndtestResultToUpdate$).toBe('-(bc)', { - b: templateSectionsChanged({ - sectionTemplates: Object.values(masterTpl.psv['testTypesGroup1'] as Record), - sectionsValue: { testTypes: [{ testTypeId: '1' }] } as unknown as TestResultModel, - }), - c: updateResultOfTest(), - }); - }); - }); - it('should dispatch templateSectionsChanged with new sections when custom defects is not present', () => { - const testResult = createMockTestResult({ - vehicleType: VehicleTypes.PSV, - testTypes: [createMockTestType({ testTypeId: '126' })], - }); - jest.spyOn(featureToggleService, 'isFeatureEnabled').mockReturnValue(true); - testScheduler.run(({ hot, expectObservable }) => { - store.overrideSelector(selectQueryParams, { edit: 'true' }); - store.overrideSelector(selectedTestResultState, testResult); - store.overrideSelector(isTestTypeOldIvaOrMsva, false); - - actions$ = hot('-a', { - a: editingTestResult({ - testTypeId: '126', - }), - }); - - expectObservable(effects.generateSectionTemplatesAndtestResultToUpdate$).toBe('-(bc)', { - b: templateSectionsChanged({ - sectionTemplates: Object.values(masterTpl.psv['testTypesSpecialistGroup1'] as Record), - sectionsValue: { testTypes: [{ testTypeId: '126' }] } as unknown as TestResultModel, - }), - c: updateResultOfTest(), - }); - }); - }); - it('should dispatch templateSectionsChanged with old sections when custom defects is present', () => { - const testResult = createMockTestResult({ - vehicleType: VehicleTypes.PSV, - testTypes: [createMockTestType({ testTypeId: '126' })], - }); - jest.spyOn(featureToggleService, 'isFeatureEnabled').mockReturnValue(true); - testScheduler.run(({ hot, expectObservable }) => { - store.overrideSelector(selectQueryParams, { edit: 'true' }); - store.overrideSelector(selectedTestResultState, testResult); - store.overrideSelector(isTestTypeOldIvaOrMsva, true); - - actions$ = hot('-a', { - a: editingTestResult({ - testTypeId: '126', - }), - }); - - expectObservable(effects.generateSectionTemplatesAndtestResultToUpdate$).toBe('-(bc)', { - b: templateSectionsChanged({ - sectionTemplates: Object.values(masterTpl.psv['testTypesSpecialistGroup1OldIVAorMSVA'] as Record), - sectionsValue: { testTypes: [{ testTypeId: '126' }] } as unknown as TestResultModel, - }), - c: updateResultOfTest(), - }); - }); - }); - - it('should dispatch templateSectionsChanged with old sections when feature flag is off', () => { - const testResult = createMockTestResult({ - vehicleType: VehicleTypes.PSV, - testTypes: [createMockTestType({ testTypeId: '126' })], - }); - jest.spyOn(featureToggleService, 'isFeatureEnabled').mockReturnValue(false); - testScheduler.run(({ hot, expectObservable }) => { - store.overrideSelector(selectQueryParams, { edit: 'true' }); - store.overrideSelector(selectedTestResultState, testResult); - store.overrideSelector(isTestTypeOldIvaOrMsva, false); - - actions$ = hot('-a', { - a: editingTestResult({ - testTypeId: '126', - }), - }); - - expectObservable(effects.generateSectionTemplatesAndtestResultToUpdate$).toBe('-(bc)', { - b: templateSectionsChanged({ - sectionTemplates: Object.values(masterTpl.psv['testTypesSpecialistGroup1OldIVAorMSVA'] as Record), - sectionsValue: { testTypes: [{ testTypeId: '126' }] } as unknown as TestResultModel, - }), - c: updateResultOfTest(), - }); - }); - }); - it('should return empty section templates if action testResult.vehicleType === undefined', () => { - const testResult = createMockTestResult({ - vehicleType: undefined, - testTypes: [createMockTestType({ testTypeId: '1' })], - }); - - testScheduler.run(({ hot, expectObservable }) => { - store.overrideSelector(selectedTestResultState, testResult); - actions$ = hot('-a', { - a: testTypeIdChanged({ - testTypeId: '1', - }), - }); - - expectObservable(effects.generateSectionTemplatesAndtestResultToUpdate$).toBe('-b', { - b: templateSectionsChanged({ - sectionTemplates: [], - sectionsValue: undefined, - }), - }); - }); - }); - - it('should return empty section templates if action testResult.vehicleType is not known by masterTpl', () => { - const testResult = createMockTestResult({ - vehicleType: 'car' as VehicleTypes, - testTypes: [createMockTestType({ testTypeId: '1' })], - }); - - testScheduler.run(({ hot, expectObservable }) => { - store.overrideSelector(selectedTestResultState, testResult); - - actions$ = hot('-a', { - a: testTypeIdChanged({ - testTypeId: '1', - }), - }); - - expectObservable(effects.generateSectionTemplatesAndtestResultToUpdate$).toBe('-b', { - b: templateSectionsChanged({ - sectionTemplates: [], - sectionsValue: undefined, - }), - }); - }); - }); - - it('should return empty section templates if testTypeId doesnt apply to vehicleType', () => { - const testResult = createMockTestResult({ - vehicleType: VehicleTypes.PSV, - testTypes: [createMockTestType({ testTypeId: '190' })], - }); - - testScheduler.run(({ hot, expectObservable }) => { - store.overrideSelector(selectQueryParams, { edit: 'true' }); - store.overrideSelector(selectedTestResultState, testResult); - - actions$ = hot('-a', { - a: testTypeIdChanged({ - testTypeId: '190', - }), - }); - - expectObservable(effects.generateSectionTemplatesAndtestResultToUpdate$).toBe('-b', { - b: templateSectionsChanged({ - sectionTemplates: [], - sectionsValue: undefined, - }), - }); - }); - }); - - it('should return empty section templates if testTypeId is known but not in master template and edit is true', () => { - const testResult = createMockTestResult({ - vehicleType: VehicleTypes.PSV, - testTypes: [createMockTestType(({ testTypeId: '39' }))], - }); - - testScheduler.run(({ hot, expectObservable }) => { - store.overrideSelector(selectQueryParams, { edit: 'true' }); - store.overrideSelector(selectedTestResultState, testResult); - - actions$ = hot('-a', { - a: testTypeIdChanged({ - testTypeId: '39', - }), - }); - - expectObservable(effects.generateSectionTemplatesAndtestResultToUpdate$).toBe('-b', { - b: templateSectionsChanged({ - sectionTemplates: [], - sectionsValue: undefined, - }), - }); - }); - }); - - it('should return default section templates if testTypeId is known but not in master template and edit is false', () => { - const testResult = createMockTestResult({ - vehicleType: VehicleTypes.PSV, - testTypes: [createMockTestType({ testTypeId: '39' })], - }); - testScheduler.run(({ hot, expectObservable }) => { - store.overrideSelector(selectQueryParams, { edit: 'false' }); - store.overrideSelector(selectedTestResultState, testResult); - - actions$ = hot('-a', { - a: testTypeIdChanged({ - testTypeId: '39', - }), - }); - - expectObservable(effects.generateSectionTemplatesAndtestResultToUpdate$).toBe('-(bc)', { - b: templateSectionsChanged({ - sectionTemplates: Object.values(masterTpl.psv['default'] as Record), - sectionsValue: { testTypes: [{ testTypeId: '39' }] } as unknown as TestResultModel, - }), - c: updateResultOfTest(), - }); - }); - }); - }); - - describe('generateContingencyTestTemplatesAndtestResultToUpdate$', () => { - beforeEach(() => { - store.resetSelectors(); - jest.resetModules(); - }); - - it('should dispatch templateSectionsChanged with new sections and test result', () => { - const testResult = createMockTestResult({ - vehicleType: VehicleTypes.PSV, - testTypes: [createMockTestType({ testTypeId: '1' })], - }); - - store.overrideSelector(testResultInEdit, testResult); - - testScheduler.run(({ hot, expectObservable }) => { - actions$ = hot('-a', { - a: contingencyTestTypeSelected({ - testType: '1', - }), - }); - - expectObservable(effects.generateContingencyTestTemplatesAndtestResultToUpdate$).toBe('-b', { - b: templateSectionsChanged({ - sectionTemplates: Object.values(contingencyTestTemplates.psv['testTypesGroup1'] as Record), - sectionsValue: { - contingencyTestNumber: undefined, - countryOfRegistration: '', - createdById: undefined, - createdByName: undefined, - euVehicleCategory: EUVehicleCategory.M1, - firstUseDate: null, - lastUpdatedAt: undefined, - lastUpdatedById: undefined, - lastUpdatedByName: undefined, - noOfAxles: undefined, - numberOfSeats: undefined, - numberOfWheelsDriven: undefined, - odometerReading: 0, - odometerReadingUnits: OdometerReadingUnits.KILOMETRES, - preparerId: '', - preparerName: '', - reasonForCancellation: undefined, - reasonForCreation: undefined, - regnDate: undefined, - source: undefined, - shouldEmailCertificate: undefined, - systemNumber: '', - testEndTimestamp: '', - testResultId: '', - testStartTimestamp: '', - testStationName: '', - testStationPNumber: '', - testStationType: 'atf', - testStatus: undefined, - testTypes: [ - { - additionalCommentsForAbandon: null, - additionalNotesRecorded: '', - certificateLink: undefined, - certificateNumber: '', - customDefects: [], - defects: [], - deletionFlag: undefined, - lastSeatbeltInstallationCheckDate: '', - name: '', - numberOfSeatbeltsFitted: 0, - prohibitionIssued: false, - reasonForAbandoning: '', - seatbeltInstallationCheckDate: false, - secondaryCertificateNumber: null, - testExpiryDate: '', - testResult: resultOfTestEnum.fail, - testTypeEndTimestamp: '', - testTypeId: '1', - testTypeName: '', - testTypeStartTimestamp: '', - }, - ], - testerEmailAddress: '', - testerName: '', - testerStaffId: '', - typeOfTest: TypeOfTest.CONTINGENCY, - vehicleClass: null, - vehicleConfiguration: undefined, - vehicleSize: undefined, - vehicleType: 'psv', - vin: '', - vrm: '', - } as unknown as TestResultModel, - }), - }); - }); - }); - - it('should dispatch templateSectionsChanged with old sections for IVA when flag is false', () => { - const testResult = createMockTestResult({ - vehicleType: VehicleTypes.PSV, - testTypes: [createMockTestType({ testTypeId: '126' })], - }); - jest.spyOn(featureToggleService, 'isFeatureEnabled').mockReturnValue(false); - store.overrideSelector(testResultInEdit, testResult); - - testScheduler.run(({ hot, expectObservable }) => { - actions$ = hot('-a', { - a: contingencyTestTypeSelected({ - testType: '126', - }), - }); - - expectObservable(effects.generateContingencyTestTemplatesAndtestResultToUpdate$).toBe('-b', { - b: templateSectionsChanged({ - sectionTemplates: Object.values(contingencyTestTemplates.psv['testTypesSpecialistGroup1OldIVAorMSVA'] as Record), - sectionsValue: { - contingencyTestNumber: undefined, - countryOfRegistration: '', - createdById: undefined, - createdByName: undefined, - euVehicleCategory: EUVehicleCategory.M1, - firstUseDate: null, - lastUpdatedAt: undefined, - lastUpdatedById: undefined, - lastUpdatedByName: undefined, - noOfAxles: undefined, - numberOfSeats: undefined, - numberOfWheelsDriven: undefined, - odometerReading: 0, - odometerReadingUnits: OdometerReadingUnits.KILOMETRES, - preparerId: '', - preparerName: '', - reasonForCancellation: undefined, - reasonForCreation: undefined, - regnDate: undefined, - source: undefined, - shouldEmailCertificate: undefined, - systemNumber: '', - testEndTimestamp: '', - testResultId: '', - testStartTimestamp: '', - testStationName: '', - testStationPNumber: '', - testStationType: 'atf', - testStatus: undefined, - testTypes: [ - { - additionalCommentsForAbandon: null, - additionalNotesRecorded: '', - certificateLink: undefined, - certificateNumber: '', - customDefects: [], - defects: [], - deletionFlag: undefined, - lastSeatbeltInstallationCheckDate: '', - name: '', - numberOfSeatbeltsFitted: 0, - prohibitionIssued: false, - reasonForAbandoning: '', - seatbeltInstallationCheckDate: false, - secondaryCertificateNumber: null, - testResult: resultOfTestEnum.fail, - testTypeEndTimestamp: '', - testTypeId: '126', - testTypeName: '', - testTypeStartTimestamp: '', - }, - ], - testerEmailAddress: '', - testerName: '', - testerStaffId: '', - typeOfTest: TypeOfTest.CONTINGENCY, - vehicleClass: null, - vehicleConfiguration: undefined, - vehicleSize: undefined, - vehicleType: 'psv', - vin: '', - vrm: '', - } as unknown as TestResultModel, - }), - }); - }); - }); - - it('should return empty section templates if action testResult.vehicleType === undefined', () => { - const testResult = createMockTestResult({ - vehicleType: undefined, - testTypes: [createMockTestType({ testTypeId: '1' })], - }); - - testScheduler.run(({ hot, expectObservable }) => { - store.overrideSelector(testResultInEdit, testResult); - actions$ = hot('-a', { - a: contingencyTestTypeSelected({ - testType: '1', - }), - }); - - expectObservable(effects.generateContingencyTestTemplatesAndtestResultToUpdate$).toBe('-b', { - b: templateSectionsChanged({ - sectionTemplates: [], - sectionsValue: undefined, - }), - }); - }); - }); - }); - - describe('createTestResult$$', () => { - it('should return createTestResultSuccess action on successfull API call', () => { - testScheduler.run(({ hot, cold, expectObservable }) => { - const testResult: TestResultModel = mockTestResult(); - // mock action to trigger effect - actions$ = hot('-a--', { a: createTestResult({ value: testResult }) }); - // mock service call - jest.spyOn(testResultsService, 'postTestResult') - .mockReturnValue(cold('---b|', { b: testResult }) as unknown as Observable>); - - // expect effect to return success action - expectObservable(effects.createTestResult$).toBe('----b', { - b: createTestResultSuccess({ payload: { id: testResult.testResultId, changes: testResult } }), - }); - }); - }); - - it('should return createTestResultFailed', () => { - testScheduler.run(({ hot, cold, expectObservable }) => { - const testResult: TestResultModel = mockTestResult(); - actions$ = hot('-a--', { a: createTestResult({ value: testResult }) }); - - const expectedError = new HttpErrorResponse({ - status: 500, - statusText: 'Internal server error', - }); - - jest.spyOn(testResultsService, 'postTestResult').mockReturnValue(cold('---#|', {}, expectedError)); - - expectObservable(effects.createTestResult$).toBe('----b', { - b: createTestResultFailed({ errors: [] }), - }); - }); - }); - - it('should return createTestResultFailed and add validation errors', () => { - testScheduler.run(({ hot, cold, expectObservable }) => { - const testResult: TestResultModel = mockTestResult(); - actions$ = hot('-a--', { a: createTestResult({ value: testResult }) }); - - const expectedError = new HttpErrorResponse({ - status: 400, - error: { errors: ['"name" is missing', '"age" is missing', 'random error'] }, - }); - - jest.spyOn(testResultsService, 'postTestResult').mockReturnValue(cold('---#|', {}, expectedError)); - - const expectedErrors: GlobalError[] = [ - { error: '"name" is missing', anchorLink: 'name' }, - { error: '"age" is missing', anchorLink: 'age' }, - { error: 'random error', anchorLink: '' }, - ]; - - expectObservable(effects.createTestResult$).toBe('----b', { - b: createTestResultFailed({ errors: expectedErrors }), - }); - }); - }); - - it('should return createTestResultFailed and add a validation error', () => { - testScheduler.run(({ hot, cold, expectObservable }) => { - const testResult: TestResultModel = mockTestResult(); - actions$ = hot('-a--', { a: createTestResult({ value: testResult }) }); - - const expectedError = new HttpErrorResponse({ - status: 400, - error: 'Certificate number not present on TIR test type', - }); - - jest.spyOn(testResultsService, 'postTestResult').mockReturnValue(cold('---#|', {}, expectedError)); - - const expectedErrors: GlobalError[] = [{ error: 'Certificate number not present on TIR test type' }]; - - expectObservable(effects.createTestResult$).toBe('----b', { - b: createTestResultFailed({ errors: expectedErrors }), - }); - }); - }); - }); + let effects: TestResultsEffects; + let actions$ = new Observable(); + let testScheduler: TestScheduler; + let testResultsService: TestRecordsService; + let store: MockStore; + let featureToggleService: FeatureToggleService; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [HttpClientTestingModule, TestResultsApiModule, RouterTestingModule], + providers: [ + TestResultsEffects, + provideMockActions(() => actions$), + TestRecordsService, + provideMockStore({ + initialState: initialAppState, + selectors: [ + { + selector: selectRouteNestedParams, + value: [ + { + systemNumber: 'systemNumber01', + testResultId: 'testResult01', + }, + ], + }, + ], + }), + { + provide: UserService, + useValue: { + user$: of({ name: 'testername', userEmail: 'test@test.com', oid: '123zxc' }), + name$: of('name'), + userEmail$: of('userEmail'), + id$: of('iod'), + }, + }, + RouterService, + DynamicFormService, + FeatureToggleService, + ], + }); + + store = TestBed.inject(MockStore); + effects = TestBed.inject(TestResultsEffects); + testResultsService = TestBed.inject(TestRecordsService); + featureToggleService = TestBed.inject(FeatureToggleService); + }); + + beforeEach(() => { + testScheduler = new TestScheduler((actual, expected) => { + expect(actual).toEqual(expected); + }); + }); + + describe('fetchTestResultsBySystemNumber$', () => { + it('should return fetchTestResultBySystemNumberSuccess action on successfull API call', () => { + testScheduler.run(({ hot, cold, expectObservable }) => { + const testResults = mockTestResultList(); + + // mock action to trigger effect + actions$ = hot('-a--', { a: fetchTestResultsBySystemNumber }); + + // mock service call + jest + .spyOn(testResultsService, 'fetchTestResultbySystemNumber') + .mockReturnValue(cold('--a|', { a: testResults })); + + // expect effect to return success action + expectObservable(effects.fetchTestResultsBySystemNumber$).toBe('---b', { + b: fetchTestResultsBySystemNumberSuccess({ payload: testResults }), + }); + }); + }); + + it('should return fetchTestResultsBySystemNumberFailed action on API error', () => { + testScheduler.run(({ hot, cold, expectObservable }) => { + actions$ = hot('-a--', { a: fetchTestResultsBySystemNumber }); + + const expectedError = new HttpErrorResponse({ + status: 500, + statusText: 'Internal server error', + }); + jest + .spyOn(testResultsService, 'fetchTestResultbySystemNumber') + .mockReturnValue(cold('--#|', {}, expectedError)); + + expectObservable(effects.fetchTestResultsBySystemNumber$).toBe('---b', { + b: fetchTestResultsBySystemNumberFailed({ + error: 'Http failure response for (unknown url): 500 Internal server error', + }), + }); + }); + }); + + it('should return fetchTestResultsBySystemNumberSuccess on a 404', () => { + testScheduler.run(({ hot, cold, expectObservable }) => { + actions$ = hot('-a--', { a: fetchTestResultsBySystemNumber }); + + const expectedError = new HttpErrorResponse({ + status: 404, + statusText: 'Not found', + }); + jest + .spyOn(testResultsService, 'fetchTestResultbySystemNumber') + .mockReturnValue(cold('--#|', {}, expectedError)); + + expectObservable(effects.fetchTestResultsBySystemNumber$).toBe('---b', { + b: fetchTestResultsBySystemNumberSuccess({ payload: [] as TestResultModel[] }), + }); + }); + }); + }); + + describe('fetchSelectedTestResult$', () => { + it('should return fetchSelectedTestResultSuccess', () => { + testScheduler.run(({ hot, cold, expectObservable }) => { + const testResult = mockTestResult(); + + actions$ = hot('-a-', { a: fetchSelectedTestResult() }); + + jest + .spyOn(testResultsService, 'fetchTestResultbySystemNumber') + .mockReturnValue(cold('--a|', { a: [testResult] })); + + expectObservable(effects.fetchSelectedTestResult$).toBe('---b', { + b: fetchSelectedTestResultSuccess({ payload: testResult }), + }); + }); + }); + + it('should return fetchSelectedTestResultFailed if API returns empty array', () => { + testScheduler.run(({ hot, cold, expectObservable }) => { + actions$ = hot('-a-', { a: fetchSelectedTestResult() }); + + jest.spyOn(testResultsService, 'fetchTestResultbySystemNumber').mockReturnValue(cold('--a|', { a: [] })); + + expectObservable(effects.fetchSelectedTestResult$).toBe('---b', { + b: fetchSelectedTestResultFailed({ error: 'Test result not found' }), + }); + }); + }); + + it('should return fetchSelectedTestResultFailed if API returns an error', () => { + testScheduler.run(({ hot, cold, expectObservable }) => { + actions$ = hot('-a-', { a: fetchSelectedTestResult() }); + + // mock service call + const expectedError = new HttpErrorResponse({ + status: 400, + statusText: 'Bad Request', + }); + jest + .spyOn(testResultsService, 'fetchTestResultbySystemNumber') + .mockReturnValue(cold('--#|', {}, expectedError)); + + expectObservable(effects.fetchSelectedTestResult$).toBe('---b', { + b: fetchSelectedTestResultFailed({ error: 'Http failure response for (unknown url): 400 Bad Request' }), + }); + }); + }); + }); + + describe('updateTestResult$', () => { + const newTestResult = { testResultId: '1' } as TestResultModel; + + it('should dispatch updateTestResultSuccess with return payload', () => { + testScheduler.run(({ hot, cold, expectObservable }) => { + actions$ = hot('-a-', { a: updateTestResult({ value: newTestResult }) }); + + jest.spyOn(testResultsService, 'saveTestResult').mockReturnValue(cold('---b', { b: newTestResult })); + + expectObservable(effects.updateTestResult$).toBe('----b', { + b: updateTestResultSuccess({ payload: { id: '1', changes: newTestResult } }), + }); + }); + }); + + it('should dispatch updateTestResultFailed action with empty errors array', () => { + testScheduler.run(({ hot, cold, expectObservable }) => { + actions$ = hot('-a-', { a: updateTestResult({ value: newTestResult }) }); + + jest + .spyOn(testResultsService, 'saveTestResult') + .mockReturnValue(cold('---#|', {}, new HttpErrorResponse({ status: 500, error: 'some error' }))); + + expectObservable(effects.updateTestResult$).toBe('----b', { + b: updateTestResultFailed({ errors: [] }), + }); + }); + }); + + it('should dispatch updateTestResultFailed action with validation errors', () => { + testScheduler.run(({ hot, cold, expectObservable }) => { + actions$ = hot('-a-', { a: updateTestResult({ value: newTestResult }) }); + + jest.spyOn(testResultsService, 'saveTestResult').mockReturnValue( + cold( + '---#|', + {}, + new HttpErrorResponse({ + status: 400, + error: { errors: ['"name" is missing', '"age" is missing', 'random error'] }, + }) + ) + ); + + const expectedErrors: GlobalError[] = [ + { error: '"name" is missing', anchorLink: 'name' }, + { error: '"age" is missing', anchorLink: 'age' }, + { error: 'random error', anchorLink: '' }, + ]; + expectObservable(effects.updateTestResult$).toBe('----b', { + b: updateTestResultFailed({ errors: expectedErrors }), + }); + }); + }); + }); + + describe('generateSectionTemplatesAndtestResultToUpdate$', () => { + beforeEach(() => { + store.resetSelectors(); + jest.resetModules(); + }); + + it('should dispatch templateSectionsChanged with new sections and test result', () => { + const testResult = createMockTestResult({ + vehicleType: VehicleTypes.PSV, + testTypes: [createMockTestType({ testTypeId: '1' })], + }); + + testScheduler.run(({ hot, expectObservable }) => { + store.overrideSelector(selectedTestResultState, testResult); + + actions$ = hot('-a', { + a: editingTestResult({ + testTypeId: '1', + }), + }); + + expectObservable(effects.generateSectionTemplatesAndtestResultToUpdate$).toBe('-(bc)', { + b: templateSectionsChanged({ + sectionTemplates: Object.values(masterTpl.psv['testTypesGroup1'] as Record), + sectionsValue: { testTypes: [{ testTypeId: '1' }] } as unknown as TestResultModel, + }), + c: updateResultOfTest(), + }); + }); + }); + it('should dispatch templateSectionsChanged with new sections when custom defects is not present', () => { + const testResult = createMockTestResult({ + vehicleType: VehicleTypes.PSV, + testTypes: [createMockTestType({ testTypeId: '126' })], + }); + jest.spyOn(featureToggleService, 'isFeatureEnabled').mockReturnValue(true); + testScheduler.run(({ hot, expectObservable }) => { + store.overrideSelector(selectQueryParams, { edit: 'true' }); + store.overrideSelector(selectedTestResultState, testResult); + store.overrideSelector(isTestTypeOldIvaOrMsva, false); + + actions$ = hot('-a', { + a: editingTestResult({ + testTypeId: '126', + }), + }); + + expectObservable(effects.generateSectionTemplatesAndtestResultToUpdate$).toBe('-(bc)', { + b: templateSectionsChanged({ + sectionTemplates: Object.values(masterTpl.psv['testTypesSpecialistGroup1'] as Record), + sectionsValue: { testTypes: [{ testTypeId: '126' }] } as unknown as TestResultModel, + }), + c: updateResultOfTest(), + }); + }); + }); + it('should dispatch templateSectionsChanged with old sections when custom defects is present', () => { + const testResult = createMockTestResult({ + vehicleType: VehicleTypes.PSV, + testTypes: [createMockTestType({ testTypeId: '126' })], + }); + jest.spyOn(featureToggleService, 'isFeatureEnabled').mockReturnValue(true); + testScheduler.run(({ hot, expectObservable }) => { + store.overrideSelector(selectQueryParams, { edit: 'true' }); + store.overrideSelector(selectedTestResultState, testResult); + store.overrideSelector(isTestTypeOldIvaOrMsva, true); + + actions$ = hot('-a', { + a: editingTestResult({ + testTypeId: '126', + }), + }); + + expectObservable(effects.generateSectionTemplatesAndtestResultToUpdate$).toBe('-(bc)', { + b: templateSectionsChanged({ + sectionTemplates: Object.values( + masterTpl.psv['testTypesSpecialistGroup1OldIVAorMSVA'] as Record + ), + sectionsValue: { testTypes: [{ testTypeId: '126' }] } as unknown as TestResultModel, + }), + c: updateResultOfTest(), + }); + }); + }); + + it('should dispatch templateSectionsChanged with old sections when feature flag is off', () => { + const testResult = createMockTestResult({ + vehicleType: VehicleTypes.PSV, + testTypes: [createMockTestType({ testTypeId: '126' })], + }); + jest.spyOn(featureToggleService, 'isFeatureEnabled').mockReturnValue(false); + testScheduler.run(({ hot, expectObservable }) => { + store.overrideSelector(selectQueryParams, { edit: 'true' }); + store.overrideSelector(selectedTestResultState, testResult); + store.overrideSelector(isTestTypeOldIvaOrMsva, false); + + actions$ = hot('-a', { + a: editingTestResult({ + testTypeId: '126', + }), + }); + + expectObservable(effects.generateSectionTemplatesAndtestResultToUpdate$).toBe('-(bc)', { + b: templateSectionsChanged({ + sectionTemplates: Object.values( + masterTpl.psv['testTypesSpecialistGroup1OldIVAorMSVA'] as Record + ), + sectionsValue: { testTypes: [{ testTypeId: '126' }] } as unknown as TestResultModel, + }), + c: updateResultOfTest(), + }); + }); + }); + it('should return empty section templates if action testResult.vehicleType === undefined', () => { + const testResult = createMockTestResult({ + vehicleType: undefined, + testTypes: [createMockTestType({ testTypeId: '1' })], + }); + + testScheduler.run(({ hot, expectObservable }) => { + store.overrideSelector(selectedTestResultState, testResult); + actions$ = hot('-a', { + a: testTypeIdChanged({ + testTypeId: '1', + }), + }); + + expectObservable(effects.generateSectionTemplatesAndtestResultToUpdate$).toBe('-b', { + b: templateSectionsChanged({ + sectionTemplates: [], + sectionsValue: undefined, + }), + }); + }); + }); + + it('should return empty section templates if action testResult.vehicleType is not known by masterTpl', () => { + const testResult = createMockTestResult({ + vehicleType: 'car' as VehicleTypes, + testTypes: [createMockTestType({ testTypeId: '1' })], + }); + + testScheduler.run(({ hot, expectObservable }) => { + store.overrideSelector(selectedTestResultState, testResult); + + actions$ = hot('-a', { + a: testTypeIdChanged({ + testTypeId: '1', + }), + }); + + expectObservable(effects.generateSectionTemplatesAndtestResultToUpdate$).toBe('-b', { + b: templateSectionsChanged({ + sectionTemplates: [], + sectionsValue: undefined, + }), + }); + }); + }); + + it('should return empty section templates if testTypeId doesnt apply to vehicleType', () => { + const testResult = createMockTestResult({ + vehicleType: VehicleTypes.PSV, + testTypes: [createMockTestType({ testTypeId: '190' })], + }); + + testScheduler.run(({ hot, expectObservable }) => { + store.overrideSelector(selectQueryParams, { edit: 'true' }); + store.overrideSelector(selectedTestResultState, testResult); + + actions$ = hot('-a', { + a: testTypeIdChanged({ + testTypeId: '190', + }), + }); + + expectObservable(effects.generateSectionTemplatesAndtestResultToUpdate$).toBe('-b', { + b: templateSectionsChanged({ + sectionTemplates: [], + sectionsValue: undefined, + }), + }); + }); + }); + + it('should return empty section templates if testTypeId is known but not in master template and edit is true', () => { + const testResult = createMockTestResult({ + vehicleType: VehicleTypes.PSV, + testTypes: [createMockTestType({ testTypeId: '39' })], + }); + + testScheduler.run(({ hot, expectObservable }) => { + store.overrideSelector(selectQueryParams, { edit: 'true' }); + store.overrideSelector(selectedTestResultState, testResult); + + actions$ = hot('-a', { + a: testTypeIdChanged({ + testTypeId: '39', + }), + }); + + expectObservable(effects.generateSectionTemplatesAndtestResultToUpdate$).toBe('-b', { + b: templateSectionsChanged({ + sectionTemplates: [], + sectionsValue: undefined, + }), + }); + }); + }); + + it('should return default section templates if testTypeId is known but not in master template and edit is false', () => { + const testResult = createMockTestResult({ + vehicleType: VehicleTypes.PSV, + testTypes: [createMockTestType({ testTypeId: '39' })], + }); + testScheduler.run(({ hot, expectObservable }) => { + store.overrideSelector(selectQueryParams, { edit: 'false' }); + store.overrideSelector(selectedTestResultState, testResult); + + actions$ = hot('-a', { + a: testTypeIdChanged({ + testTypeId: '39', + }), + }); + + expectObservable(effects.generateSectionTemplatesAndtestResultToUpdate$).toBe('-(bc)', { + b: templateSectionsChanged({ + sectionTemplates: Object.values(masterTpl.psv['default'] as Record), + sectionsValue: { testTypes: [{ testTypeId: '39' }] } as unknown as TestResultModel, + }), + c: updateResultOfTest(), + }); + }); + }); + }); + + describe('generateContingencyTestTemplatesAndtestResultToUpdate$', () => { + beforeEach(() => { + store.resetSelectors(); + jest.resetModules(); + }); + + it('should dispatch templateSectionsChanged with new sections and test result', () => { + const testResult = createMockTestResult({ + vehicleType: VehicleTypes.PSV, + testTypes: [createMockTestType({ testTypeId: '1' })], + }); + + store.overrideSelector(testResultInEdit, testResult); + + testScheduler.run(({ hot, expectObservable }) => { + actions$ = hot('-a', { + a: contingencyTestTypeSelected({ + testType: '1', + }), + }); + + expectObservable(effects.generateContingencyTestTemplatesAndtestResultToUpdate$).toBe('-b', { + b: templateSectionsChanged({ + sectionTemplates: Object.values( + contingencyTestTemplates.psv['testTypesGroup1'] as Record + ), + sectionsValue: { + contingencyTestNumber: undefined, + countryOfRegistration: '', + createdById: undefined, + createdByName: undefined, + euVehicleCategory: EUVehicleCategory.M1, + firstUseDate: null, + lastUpdatedAt: undefined, + lastUpdatedById: undefined, + lastUpdatedByName: undefined, + noOfAxles: undefined, + numberOfSeats: undefined, + numberOfWheelsDriven: undefined, + odometerReading: 0, + odometerReadingUnits: OdometerReadingUnits.KILOMETRES, + preparerId: '', + preparerName: '', + reasonForCancellation: undefined, + reasonForCreation: undefined, + regnDate: undefined, + source: undefined, + shouldEmailCertificate: undefined, + systemNumber: '', + testEndTimestamp: '', + testResultId: '', + testStartTimestamp: '', + testStationName: '', + testStationPNumber: '', + testStationType: 'atf', + testStatus: undefined, + testTypes: [ + { + additionalCommentsForAbandon: null, + additionalNotesRecorded: '', + certificateLink: undefined, + certificateNumber: '', + customDefects: [], + defects: [], + deletionFlag: undefined, + lastSeatbeltInstallationCheckDate: '', + name: '', + numberOfSeatbeltsFitted: 0, + prohibitionIssued: false, + reasonForAbandoning: '', + seatbeltInstallationCheckDate: false, + secondaryCertificateNumber: null, + testExpiryDate: '', + testResult: resultOfTestEnum.fail, + testTypeEndTimestamp: '', + testTypeId: '1', + testTypeName: '', + testTypeStartTimestamp: '', + }, + ], + testerEmailAddress: '', + testerName: '', + testerStaffId: '', + typeOfTest: TypeOfTest.CONTINGENCY, + vehicleClass: null, + vehicleConfiguration: undefined, + vehicleSize: undefined, + vehicleType: 'psv', + vin: '', + vrm: '', + } as unknown as TestResultModel, + }), + }); + }); + }); + + it('should dispatch templateSectionsChanged with old sections for IVA when flag is false', () => { + const testResult = createMockTestResult({ + vehicleType: VehicleTypes.PSV, + testTypes: [createMockTestType({ testTypeId: '126' })], + }); + jest.spyOn(featureToggleService, 'isFeatureEnabled').mockReturnValue(false); + store.overrideSelector(testResultInEdit, testResult); + + testScheduler.run(({ hot, expectObservable }) => { + actions$ = hot('-a', { + a: contingencyTestTypeSelected({ + testType: '126', + }), + }); + + expectObservable(effects.generateContingencyTestTemplatesAndtestResultToUpdate$).toBe('-b', { + b: templateSectionsChanged({ + sectionTemplates: Object.values( + contingencyTestTemplates.psv['testTypesSpecialistGroup1OldIVAorMSVA'] as Record + ), + sectionsValue: { + contingencyTestNumber: undefined, + countryOfRegistration: '', + createdById: undefined, + createdByName: undefined, + euVehicleCategory: EUVehicleCategory.M1, + firstUseDate: null, + lastUpdatedAt: undefined, + lastUpdatedById: undefined, + lastUpdatedByName: undefined, + noOfAxles: undefined, + numberOfSeats: undefined, + numberOfWheelsDriven: undefined, + odometerReading: 0, + odometerReadingUnits: OdometerReadingUnits.KILOMETRES, + preparerId: '', + preparerName: '', + reasonForCancellation: undefined, + reasonForCreation: undefined, + regnDate: undefined, + source: undefined, + shouldEmailCertificate: undefined, + systemNumber: '', + testEndTimestamp: '', + testResultId: '', + testStartTimestamp: '', + testStationName: '', + testStationPNumber: '', + testStationType: 'atf', + testStatus: undefined, + testTypes: [ + { + additionalCommentsForAbandon: null, + additionalNotesRecorded: '', + certificateLink: undefined, + certificateNumber: '', + customDefects: [], + defects: [], + deletionFlag: undefined, + lastSeatbeltInstallationCheckDate: '', + name: '', + numberOfSeatbeltsFitted: 0, + prohibitionIssued: false, + reasonForAbandoning: '', + seatbeltInstallationCheckDate: false, + secondaryCertificateNumber: null, + testResult: resultOfTestEnum.fail, + testTypeEndTimestamp: '', + testTypeId: '126', + testTypeName: '', + testTypeStartTimestamp: '', + }, + ], + testerEmailAddress: '', + testerName: '', + testerStaffId: '', + typeOfTest: TypeOfTest.CONTINGENCY, + vehicleClass: null, + vehicleConfiguration: undefined, + vehicleSize: undefined, + vehicleType: 'psv', + vin: '', + vrm: '', + } as unknown as TestResultModel, + }), + }); + }); + }); + + it('should return empty section templates if action testResult.vehicleType === undefined', () => { + const testResult = createMockTestResult({ + vehicleType: undefined, + testTypes: [createMockTestType({ testTypeId: '1' })], + }); + + testScheduler.run(({ hot, expectObservable }) => { + store.overrideSelector(testResultInEdit, testResult); + actions$ = hot('-a', { + a: contingencyTestTypeSelected({ + testType: '1', + }), + }); + + expectObservable(effects.generateContingencyTestTemplatesAndtestResultToUpdate$).toBe('-b', { + b: templateSectionsChanged({ + sectionTemplates: [], + sectionsValue: undefined, + }), + }); + }); + }); + }); + + describe('createTestResult$$', () => { + it('should return createTestResultSuccess action on successfull API call', () => { + testScheduler.run(({ hot, cold, expectObservable }) => { + const testResult: TestResultModel = mockTestResult(); + // mock action to trigger effect + actions$ = hot('-a--', { a: createTestResult({ value: testResult }) }); + // mock service call + jest + .spyOn(testResultsService, 'postTestResult') + .mockReturnValue(cold('---b|', { b: testResult }) as unknown as Observable>); + + // expect effect to return success action + expectObservable(effects.createTestResult$).toBe('----b', { + b: createTestResultSuccess({ payload: { id: testResult.testResultId, changes: testResult } }), + }); + }); + }); + + it('should return createTestResultFailed', () => { + testScheduler.run(({ hot, cold, expectObservable }) => { + const testResult: TestResultModel = mockTestResult(); + actions$ = hot('-a--', { a: createTestResult({ value: testResult }) }); + + const expectedError = new HttpErrorResponse({ + status: 500, + statusText: 'Internal server error', + }); + + jest.spyOn(testResultsService, 'postTestResult').mockReturnValue(cold('---#|', {}, expectedError)); + + expectObservable(effects.createTestResult$).toBe('----b', { + b: createTestResultFailed({ errors: [] }), + }); + }); + }); + + it('should return createTestResultFailed and add validation errors', () => { + testScheduler.run(({ hot, cold, expectObservable }) => { + const testResult: TestResultModel = mockTestResult(); + actions$ = hot('-a--', { a: createTestResult({ value: testResult }) }); + + const expectedError = new HttpErrorResponse({ + status: 400, + error: { errors: ['"name" is missing', '"age" is missing', 'random error'] }, + }); + + jest.spyOn(testResultsService, 'postTestResult').mockReturnValue(cold('---#|', {}, expectedError)); + + const expectedErrors: GlobalError[] = [ + { error: '"name" is missing', anchorLink: 'name' }, + { error: '"age" is missing', anchorLink: 'age' }, + { error: 'random error', anchorLink: '' }, + ]; + + expectObservable(effects.createTestResult$).toBe('----b', { + b: createTestResultFailed({ errors: expectedErrors }), + }); + }); + }); + + it('should return createTestResultFailed and add a validation error', () => { + testScheduler.run(({ hot, cold, expectObservable }) => { + const testResult: TestResultModel = mockTestResult(); + actions$ = hot('-a--', { a: createTestResult({ value: testResult }) }); + + const expectedError = new HttpErrorResponse({ + status: 400, + error: 'Certificate number not present on TIR test type', + }); + + jest.spyOn(testResultsService, 'postTestResult').mockReturnValue(cold('---#|', {}, expectedError)); + + const expectedErrors: GlobalError[] = [{ error: 'Certificate number not present on TIR test type' }]; + + expectObservable(effects.createTestResult$).toBe('----b', { + b: createTestResultFailed({ errors: expectedErrors }), + }); + }); + }); + }); }); diff --git a/src/app/store/test-records/effects/test-records.effects.ts b/src/app/store/test-records/effects/test-records.effects.ts index 7d1a9de432..632e477c2b 100644 --- a/src/app/store/test-records/effects/test-records.effects.ts +++ b/src/app/store/test-records/effects/test-records.effects.ts @@ -1,4 +1,4 @@ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { Router } from '@angular/router'; import { GlobalError } from '@core/components/global-error/global-error.interface'; import { TEST_TYPES } from '@forms/models/testTypeId.enum'; @@ -11,6 +11,7 @@ import { TestStationType } from '@models/test-stations/test-station-type.enum'; import { StatusCodes } from '@models/vehicle-tech-record.model'; import { Actions, createEffect, ofType } from '@ngrx/effects'; import { Store, select } from '@ngrx/store'; +import { FeatureToggleService } from '@services/feature-toggle-service/feature-toggle-service'; import { TechnicalRecordHttpService } from '@services/technical-record-http/technical-record-http.service'; import { TestRecordsService } from '@services/test-records/test-records.service'; import { UserService } from '@services/user-service/user-service'; @@ -20,279 +21,332 @@ import { updateResultOfTest } from '@store/test-records'; import { getTestStationFromProperty } from '@store/test-stations'; import { selectTestType } from '@store/test-types/selectors/test-types.selectors'; import merge from 'lodash.merge'; +import { catchError, concatMap, delay, filter, map, mergeMap, of, switchMap, take, withLatestFrom } from 'rxjs'; import { - catchError, concatMap, delay, filter, map, mergeMap, of, switchMap, take, - withLatestFrom, -} from 'rxjs'; -import { FeatureToggleService } from '@services/feature-toggle-service/feature-toggle-service'; -import { - contingencyTestTypeSelected, - createTestResult, - createTestResultFailed, - createTestResultSuccess, - editingTestResult, - fetchSelectedTestResult, - fetchSelectedTestResultFailed, - fetchSelectedTestResultSuccess, - fetchTestResultsBySystemNumber, - fetchTestResultsBySystemNumberFailed, - fetchTestResultsBySystemNumberSuccess, - templateSectionsChanged, - testTypeIdChanged, - updateTestResult, - updateTestResultFailed, - updateTestResultSuccess, + contingencyTestTypeSelected, + createTestResult, + createTestResultFailed, + createTestResultSuccess, + editingTestResult, + fetchSelectedTestResult, + fetchSelectedTestResultFailed, + fetchSelectedTestResultSuccess, + fetchTestResultsBySystemNumber, + fetchTestResultsBySystemNumberFailed, + fetchTestResultsBySystemNumberSuccess, + templateSectionsChanged, + testTypeIdChanged, + updateTestResult, + updateTestResultFailed, + updateTestResultSuccess, } from '../actions/test-records.actions'; -import { isTestTypeOldIvaOrMsva, selectedTestResultState, testResultInEdit } from '../selectors/test-records.selectors'; +import { + isTestTypeOldIvaOrMsva, + selectAllTestResultsInDateOrder, + selectedTestResultState, + testResultInEdit, +} from '../selectors/test-records.selectors'; @Injectable() export class TestResultsEffects { - fetchTestResultsBySystemNumber$ = createEffect(() => - this.actions$.pipe( - ofType(fetchTestResultsBySystemNumber), - mergeMap(({ systemNumber }) => - this.testRecordsService.fetchTestResultbySystemNumber(systemNumber, { fromDateTime: new Date(1970) }).pipe( - map((testResults) => fetchTestResultsBySystemNumberSuccess({ payload: testResults })), - catchError((e) => { - switch (e.status) { - case 404: - return of(fetchTestResultsBySystemNumberSuccess({ payload: [] as TestResultModel[] })); - default: return of(fetchTestResultsBySystemNumberFailed({ error: e.message })); - } - }), - )), - )); - - fetchSelectedTestResult$ = createEffect(() => - this.actions$.pipe( - ofType(fetchSelectedTestResult), - mergeMap(() => this.store.pipe(select(selectRouteNestedParams), take(1))), - mergeMap((params) => { - const { systemNumber, testResultId } = params; - return this.testRecordsService - .fetchTestResultbySystemNumber(systemNumber, { fromDateTime: new Date(1970), testResultId, version: 'all' }) - .pipe( - map((vehicleTestRecords) => { - if (vehicleTestRecords && vehicleTestRecords.length === 1) { - return fetchSelectedTestResultSuccess({ payload: vehicleTestRecords[0] }); - } - return fetchSelectedTestResultFailed({ error: 'Test result not found' }); + private actions$ = inject(Actions); + private testRecordsService = inject(TestRecordsService); + private techRecordHttpService = inject(TechnicalRecordHttpService); + private store = inject>(Store); + private router = inject(Router); + private userService = inject(UserService); + private dfs = inject(DynamicFormService); + private featureToggleService = inject(FeatureToggleService); - }), - catchError((e) => { - return of(fetchSelectedTestResultFailed({ error: e.message })); - }), - ); - }), - )); + fetchTestResultsBySystemNumber$ = createEffect(() => + this.actions$.pipe( + ofType(fetchTestResultsBySystemNumber), + mergeMap(({ systemNumber }) => + this.testRecordsService.fetchTestResultbySystemNumber(systemNumber, { fromDateTime: new Date(1970) }).pipe( + map((testResults) => fetchTestResultsBySystemNumberSuccess({ payload: testResults })), + catchError((e) => { + switch (e.status) { + case 404: + return of(fetchTestResultsBySystemNumberSuccess({ payload: [] as TestResultModel[] })); + default: + return of(fetchTestResultsBySystemNumberFailed({ error: e.message })); + } + }) + ) + ) + ) + ); - /** - * Call POST Test Results API to update test result - */ - createTestResult$ = createEffect(() => - this.actions$.pipe( - ofType(createTestResult), - switchMap((action) => { - const testResult = action.value; - return this.testRecordsService.postTestResult(testResult).pipe( - take(1), - map(() => createTestResultSuccess({ payload: { id: testResult.testResultId, changes: testResult } })), - catchError((e) => { - const validationsErrors: GlobalError[] = []; - if (e.status === 400) { - const { - error: { errors }, - } = e; - // eslint-disable-next-line @typescript-eslint/no-unused-expressions, no-unused-expressions - Array.isArray(errors) - ? errors.forEach((error: string) => { - const field = error.match(/"([^"]+)"/); - validationsErrors.push({ error, anchorLink: field && field.length > 1 ? field[1].replace('"', '') : '' }); - }) - : validationsErrors.push({ error: e.error }); - } else if (e.status === 502) { - validationsErrors.push({ error: 'Internal Server Error, please contact technical support', anchorLink: '' }); - } - return of(createTestResultFailed({ errors: validationsErrors })); - }), - ); - }), - )); + fetchSelectedTestResult$ = createEffect(() => + this.actions$.pipe( + ofType(fetchSelectedTestResult), + mergeMap(() => this.store.pipe(select(selectRouteNestedParams), take(1))), + mergeMap((params) => { + const { systemNumber, testResultId } = params; + return this.testRecordsService + .fetchTestResultbySystemNumber(systemNumber, { fromDateTime: new Date(1970), testResultId, version: 'all' }) + .pipe( + map((vehicleTestRecords) => { + if (vehicleTestRecords && vehicleTestRecords.length === 1) { + return fetchSelectedTestResultSuccess({ payload: vehicleTestRecords[0] }); + } + return fetchSelectedTestResultFailed({ error: 'Test result not found' }); + }), + catchError((e) => { + return of(fetchSelectedTestResultFailed({ error: e.message })); + }) + ); + }) + ) + ); - /** - * Call PUT Test Results API to update test result - */ - updateTestResult$ = createEffect(() => - this.actions$.pipe( - ofType(updateTestResult), - mergeMap((action) => - of(action.value).pipe( - withLatestFrom(this.userService.name$, this.userService.id$, this.userService.userEmail$, this.store.pipe(select(selectRouteNestedParams))), - take(1), - )), - mergeMap(([testResult, name, id, userEmail, { systemNumber }]) => { - return this.testRecordsService.saveTestResult(systemNumber, { name, id, userEmail }, testResult).pipe( - take(1), - map((responseBody) => updateTestResultSuccess({ payload: { id: responseBody.testResultId, changes: responseBody } })), - catchError((e) => { - const validationsErrors: GlobalError[] = []; - if (e.status === 400) { - const { - error: { errors }, - } = e; - errors.forEach((error: string) => { - const field = error.match(/"([^"]+)"/); - validationsErrors.push({ error, anchorLink: field && field.length > 1 ? field[1].replace('"', '') : '' }); - }); - } else if (e.status === 502) { - validationsErrors.push({ error: 'Internal Server Error, please contact technical support', anchorLink: '' }); - } - return of(updateTestResultFailed({ errors: validationsErrors })); - }), - ); - }), - )); + /** + * Call POST Test Results API to update test result + */ + createTestResult$ = createEffect(() => + this.actions$.pipe( + ofType(createTestResult), + switchMap((action) => { + const testResult = action.value; + return this.testRecordsService.postTestResult(testResult).pipe( + take(1), + map(() => createTestResultSuccess({ payload: { id: testResult.testResultId, changes: testResult } })), + catchError((e) => { + const validationsErrors: GlobalError[] = []; + if (e.status === 400) { + const { + error: { errors }, + } = e; + // eslint-disable-next-line @typescript-eslint/no-unused-expressions, no-unused-expressions + Array.isArray(errors) + ? errors.forEach((error: string) => { + const field = error.match(/"([^"]+)"/); + validationsErrors.push({ + error, + anchorLink: field && field.length > 1 ? field[1].replace('"', '') : '', + }); + }) + : validationsErrors.push({ error: e.error }); + } else if (e.status === 502) { + validationsErrors.push({ + error: 'Internal Server Error, please contact technical support', + anchorLink: '', + }); + } + return of(createTestResultFailed({ errors: validationsErrors })); + }) + ); + }) + ) + ); - generateSectionTemplatesAndtestResultToUpdate$ = createEffect(() => - this.actions$.pipe( - ofType(editingTestResult, testTypeIdChanged), - mergeMap((action) => - of(action).pipe(withLatestFrom( - this.store.pipe(select(selectedTestResultState)), - this.store.pipe(select(selectQueryParam('edit'))), - this.store.pipe(select(isTestTypeOldIvaOrMsva)), - ), take(1))), - concatMap(([action, selectedTestResult, isEditing, isOldIVAorMSVAtest]) => { - const { testTypeId } = action; + /** + * Call PUT Test Results API to update test result + */ + updateTestResult$ = createEffect(() => + this.actions$.pipe( + ofType(updateTestResult), + mergeMap((action) => + of(action.value).pipe( + withLatestFrom( + this.userService.name$, + this.userService.id$, + this.userService.userEmail$, + this.store.select(selectRouteNestedParams), + this.store.select(selectAllTestResultsInDateOrder) + ), + take(1) + ) + ), + mergeMap(([testResult, name, id, userEmail, { systemNumber }, testResults]) => { + return this.testRecordsService + .saveTestResult( + systemNumber, + { name, id, userEmail }, + this.testRecordsService.prepareTestResultForAmendment(testResults, testResult) + ) + .pipe( + take(1), + map((responseBody) => + updateTestResultSuccess({ payload: { id: responseBody.testResultId, changes: responseBody } }) + ), + catchError((e) => { + const validationsErrors: GlobalError[] = []; + if (e.status === 400) { + const { + error: { errors }, + } = e; + errors.forEach((error: string) => { + const field = error.match(/"([^"]+)"/); + validationsErrors.push({ + error, + anchorLink: field && field.length > 1 ? field[1].replace('"', '') : '', + }); + }); + } else if (e.status === 502) { + validationsErrors.push({ + error: 'Internal Server Error, please contact technical support', + anchorLink: '', + }); + } + return of(updateTestResultFailed({ errors: validationsErrors })); + }) + ); + }) + ) + ); - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - const { vehicleType } = selectedTestResult!; - if (!vehicleType || !Object.prototype.hasOwnProperty.call(masterTpl, vehicleType)) { - return of(templateSectionsChanged({ sectionTemplates: [], sectionsValue: undefined })); - } - const testTypeGroup = TestRecordsService.getTestTypeGroup(testTypeId); + generateSectionTemplatesAndtestResultToUpdate$ = createEffect(() => + this.actions$.pipe( + ofType(editingTestResult, testTypeIdChanged), + mergeMap((action) => + of(action).pipe( + withLatestFrom( + this.store.pipe(select(selectedTestResultState)), + this.store.pipe(select(selectQueryParam('edit'))), + this.store.pipe(select(isTestTypeOldIvaOrMsva)) + ), + take(1) + ) + ), + concatMap(([action, selectedTestResult, isEditing, isOldIVAorMSVAtest]) => { + const { testTypeId } = action; - // tech-debt: feature flag check to be removed when required standard is enabled - const isRequiredStandardsEnabled = this.featureToggleService.isFeatureEnabled('requiredStandards'); - const isIVAorMSVATest = testTypeGroup === 'testTypesSpecialistGroup1' || testTypeGroup === 'testTypesSpecialistGroup5'; + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const { vehicleType } = selectedTestResult!; + if (!vehicleType || !Object.prototype.hasOwnProperty.call(masterTpl, vehicleType)) { + return of(templateSectionsChanged({ sectionTemplates: [], sectionsValue: undefined })); + } + const testTypeGroup = TestRecordsService.getTestTypeGroup(testTypeId); - const vehicleTpl = masterTpl[`${vehicleType}`]; - const testTypeGroupString = (!isRequiredStandardsEnabled || isOldIVAorMSVAtest) - && isIVAorMSVATest ? `${testTypeGroup}OldIVAorMSVA` : testTypeGroup; + // tech-debt: feature flag check to be removed when required standard is enabled + const isRequiredStandardsEnabled = this.featureToggleService.isFeatureEnabled('requiredStandards'); + const isIVAorMSVATest = + testTypeGroup === 'testTypesSpecialistGroup1' || testTypeGroup === 'testTypesSpecialistGroup5'; - let tpl; - if (testTypeGroupString && Object.prototype.hasOwnProperty.call(vehicleTpl, testTypeGroupString)) { - tpl = vehicleTpl[testTypeGroupString as keyof typeof TEST_TYPES]; - } else if (isEditing === 'true') { - tpl = undefined; - } else { - tpl = vehicleTpl['default']; - } + const vehicleTpl = masterTpl[`${vehicleType}`]; + const testTypeGroupString = + (!isRequiredStandardsEnabled || isOldIVAorMSVAtest) && isIVAorMSVATest + ? `${testTypeGroup}OldIVAorMSVA` + : testTypeGroup; - if (!tpl) { - return of(templateSectionsChanged({ sectionTemplates: [], sectionsValue: undefined })); - } + let tpl; + if (testTypeGroupString && Object.prototype.hasOwnProperty.call(vehicleTpl, testTypeGroupString)) { + tpl = vehicleTpl[testTypeGroupString as keyof typeof TEST_TYPES]; + } else if (isEditing === 'true') { + tpl = undefined; + } else { + tpl = vehicleTpl['default']; + } - const mergedForms = {}; - Object.values(tpl).forEach((node) => { - const form = this.dfs.createForm(node, selectedTestResult); - merge(mergedForms, form.getCleanValue(form)); - }); + if (!tpl) { + return of(templateSectionsChanged({ sectionTemplates: [], sectionsValue: undefined })); + } - if (testTypeId) { - (mergedForms as TestResultModel).testTypes[0].testTypeId = testTypeId; - } + const mergedForms = {}; + Object.values(tpl).forEach((node) => { + const form = this.dfs.createForm(node, selectedTestResult); + merge(mergedForms, form.getCleanValue(form)); + }); - return of( - templateSectionsChanged({ sectionTemplates: Object.values(tpl), sectionsValue: mergedForms as TestResultModel }), - updateResultOfTest(), - ); - }), - )); + if (testTypeId) { + (mergedForms as TestResultModel).testTypes[0].testTypeId = testTypeId; + } - generateContingencyTestTemplatesAndtestResultToUpdate$ = createEffect(() => - this.actions$.pipe( - ofType(contingencyTestTypeSelected), - mergeMap((action) => - of(action).pipe( - withLatestFrom( - this.store.select(testResultInEdit), - this.store.select(selectTestType(action.testType)), - this.store.select(getTestStationFromProperty('testStationType', TestStationType.HQ)), - this.userService.user$, - ), - take(1), - )), - concatMap(([action, editedTestResult, testTypeTaxonomy, testStation, user]) => { - const id = action.testType; + return of( + templateSectionsChanged({ + sectionTemplates: Object.values(tpl), + sectionsValue: mergedForms as TestResultModel, + }), + updateResultOfTest() + ); + }) + ) + ); - const vehicleType = editedTestResult?.vehicleType; - if (!vehicleType || !Object.prototype.hasOwnProperty.call(contingencyTestTemplates, vehicleType)) { - return of(templateSectionsChanged({ sectionTemplates: [], sectionsValue: undefined })); - } + generateContingencyTestTemplatesAndtestResultToUpdate$ = createEffect(() => + this.actions$.pipe( + ofType(contingencyTestTypeSelected), + mergeMap((action) => + of(action).pipe( + withLatestFrom( + this.store.select(testResultInEdit), + this.store.select(selectTestType(action.testType)), + this.store.select(getTestStationFromProperty('testStationType', TestStationType.HQ)), + this.userService.user$ + ), + take(1) + ) + ), + concatMap(([action, editedTestResult, testTypeTaxonomy, testStation, user]) => { + const id = action.testType; - const testTypeGroup = TestRecordsService.getTestTypeGroup(id); - // tech-debt: feature flag check to be removed when required standard is enabled - const isRequiredStandardsEnabled = this.featureToggleService.isFeatureEnabled('requiredStandards'); - const isIVAorMSVATest = testTypeGroup === 'testTypesSpecialistGroup1' || testTypeGroup === 'testTypesSpecialistGroup5'; + const vehicleType = editedTestResult?.vehicleType; + if (!vehicleType || !Object.prototype.hasOwnProperty.call(contingencyTestTemplates, vehicleType)) { + return of(templateSectionsChanged({ sectionTemplates: [], sectionsValue: undefined })); + } - const vehicleTpl = contingencyTestTemplates[`${vehicleType}`]; - const testTypeGroupString = !isRequiredStandardsEnabled && isIVAorMSVATest ? `${testTypeGroup}OldIVAorMSVA` : testTypeGroup; + const testTypeGroup = TestRecordsService.getTestTypeGroup(id); + // tech-debt: feature flag check to be removed when required standard is enabled + const isRequiredStandardsEnabled = this.featureToggleService.isFeatureEnabled('requiredStandards'); + const isIVAorMSVATest = + testTypeGroup === 'testTypesSpecialistGroup1' || testTypeGroup === 'testTypesSpecialistGroup5'; - const tpl = testTypeGroupString && Object.prototype.hasOwnProperty.call(vehicleTpl, testTypeGroupString) - ? vehicleTpl[testTypeGroupString as keyof typeof TEST_TYPES] - : vehicleTpl['default']; + const vehicleTpl = contingencyTestTemplates[`${vehicleType}`]; + const testTypeGroupString = + !isRequiredStandardsEnabled && isIVAorMSVATest ? `${testTypeGroup}OldIVAorMSVA` : testTypeGroup; - const mergedForms = {} as TestResultModel; - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - Object.values(tpl!).forEach((node) => { - const form = this.dfs.createForm(node, editedTestResult); - merge(mergedForms, form.getCleanValue(form)); - }); + const tpl = + testTypeGroupString && Object.prototype.hasOwnProperty.call(vehicleTpl, testTypeGroupString) + ? vehicleTpl[testTypeGroupString as keyof typeof TEST_TYPES] + : vehicleTpl['default']; - mergedForms.testTypes[0].testTypeId = id; - mergedForms.testTypes[0].name = testTypeTaxonomy?.name ?? ''; - mergedForms.testTypes[0].testTypeName = testTypeTaxonomy?.testTypeName ?? ''; - mergedForms.typeOfTest = (testTypeTaxonomy?.typeOfTest as TypeOfTest) ?? TypeOfTest.CONTINGENCY; + const mergedForms = {} as TestResultModel; + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + Object.values(tpl!).forEach((node) => { + const form = this.dfs.createForm(node, editedTestResult); + merge(mergedForms, form.getCleanValue(form)); + }); - const now = new Date().toISOString(); + mergedForms.testTypes[0].testTypeId = id; + mergedForms.testTypes[0].name = testTypeTaxonomy?.name ?? ''; + mergedForms.testTypes[0].testTypeName = testTypeTaxonomy?.testTypeName ?? ''; + mergedForms.typeOfTest = (testTypeTaxonomy?.typeOfTest as TypeOfTest) ?? TypeOfTest.CONTINGENCY; - if (mergedForms.typeOfTest !== TypeOfTest.CONTINGENCY) { - mergedForms.testerName = user.name; - mergedForms.testerEmailAddress = user.userEmail; - mergedForms.testerStaffId = user.oid; - mergedForms.testStartTimestamp = now; - mergedForms.testEndTimestamp = now; - mergedForms.testTypes[0].testTypeStartTimestamp = now; - mergedForms.testTypes[0].testTypeEndTimestamp = now; - mergedForms.testStationName = testStation?.testStationName ?? '[INVALID_OPTION]'; - mergedForms.testStationPNumber = testStation?.testStationPNumber ?? '[INVALID_OPTION]'; - mergedForms.testStationType = TestStationType.ATF; - } + const now = new Date().toISOString(); - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - return of(templateSectionsChanged({ sectionTemplates: Object.values(tpl!), sectionsValue: mergedForms })); - }), - )); + if (mergedForms.typeOfTest !== TypeOfTest.CONTINGENCY) { + mergedForms.testerName = user.name; + mergedForms.testerEmailAddress = user.userEmail; + mergedForms.testerStaffId = user.oid; + mergedForms.testStartTimestamp = now; + mergedForms.testEndTimestamp = now; + mergedForms.testTypes[0].testTypeStartTimestamp = now; + mergedForms.testTypes[0].testTypeEndTimestamp = now; + mergedForms.testStationName = testStation?.testStationName ?? '[INVALID_OPTION]'; + mergedForms.testStationPNumber = testStation?.testStationPNumber ?? '[INVALID_OPTION]'; + mergedForms.testStationType = TestStationType.ATF; + } - createTestResultSuccess$ = createEffect(() => this.actions$.pipe( - ofType(createTestResultSuccess), - delay(3000), - map((action) => action.payload.changes.systemNumber as string), - switchMap((systemNumber) => this.techRecordHttpService.getBySystemNumber$(systemNumber)), - map((results) => results.find((result) => result.techRecord_statusCode === StatusCodes.CURRENT)), - filter(Boolean), - switchMap((techRecord) => this.router.navigate(['tech-records', techRecord.systemNumber, techRecord.createdTimestamp])), - ), { dispatch: false }); + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + return of(templateSectionsChanged({ sectionTemplates: Object.values(tpl!), sectionsValue: mergedForms })); + }) + ) + ); - constructor( - private actions$: Actions, - private testRecordsService: TestRecordsService, - private techRecordHttpService: TechnicalRecordHttpService, - private store: Store, - private router: Router, - private userService: UserService, - private dfs: DynamicFormService, - private featureToggleService: FeatureToggleService, - ) { } + createTestResultSuccess$ = createEffect( + () => + this.actions$.pipe( + ofType(createTestResultSuccess), + delay(3000), + map((action) => action.payload.changes.systemNumber as string), + switchMap((systemNumber) => this.techRecordHttpService.getBySystemNumber$(systemNumber)), + map((results) => results.find((result) => result.techRecord_statusCode === StatusCodes.CURRENT)), + filter(Boolean), + switchMap((techRecord) => + this.router.navigate(['tech-records', techRecord.systemNumber, techRecord.createdTimestamp]) + ) + ), + { dispatch: false } + ); } diff --git a/src/app/store/test-records/reducers/test-records.reducer.spec.ts b/src/app/store/test-records/reducers/test-records.reducer.spec.ts index 7fa7042e6d..bc3eead17a 100644 --- a/src/app/store/test-records/reducers/test-records.reducer.spec.ts +++ b/src/app/store/test-records/reducers/test-records.reducer.spec.ts @@ -4,546 +4,543 @@ import { TestResultModel } from '@models/test-results/test-result.model'; import { Action } from '@ngrx/store'; import { mockTestResultList } from '../../../../mocks/mock-test-result'; import { - cleanTestResult, - createDefect, - createRequiredStandard, - fetchSelectedTestResult, - fetchSelectedTestResultFailed, - fetchSelectedTestResultSuccess, - fetchTestResults, - fetchTestResultsBySystemNumber, - fetchTestResultsBySystemNumberFailed, - fetchTestResultsBySystemNumberSuccess, - fetchTestResultsSuccess, - removeDefect, - removeRequiredStandard, - updateDefect, - updateRequiredStandard, - updateResultOfTest, - updateTestResult, - updateTestResultFailed, - updateTestResultSuccess, + cleanTestResult, + createDefect, + createRequiredStandard, + fetchSelectedTestResult, + fetchSelectedTestResultFailed, + fetchSelectedTestResultSuccess, + fetchTestResults, + fetchTestResultsBySystemNumber, + fetchTestResultsBySystemNumberFailed, + fetchTestResultsBySystemNumberSuccess, + fetchTestResultsSuccess, + removeDefect, + removeRequiredStandard, + updateDefect, + updateRequiredStandard, + updateResultOfTest, + updateTestResult, + updateTestResultFailed, + updateTestResultSuccess, } from '../actions/test-records.actions'; import { TestResultsState, initialTestResultsState, testResultsReducer } from './test-records.reducer'; describe('Test Results Reducer', () => { - describe('unknown action', () => { - it('should return the default state', () => { - const action = { - type: 'Unknown', - }; - const state = testResultsReducer(initialTestResultsState, action); - - expect(state).toBe(initialTestResultsState); - }); - }); - - describe('fetchTestResults', () => { - it('should set loading to true', () => { - const oldState: TestResultsState = { ...initialTestResultsState, loading: false }; - const action = fetchTestResults(); - const state = testResultsReducer(oldState, action); - - expect(state.loading).toBe(true); - expect(state).not.toBe(oldState); - }); - }); - - describe('fetchTestResultsSuccess', () => { - it('should set all test result records', () => { - const testResults = mockTestResultList(3); - const newState: TestResultsState = { - ...initialTestResultsState, - ids: ['TestResultId0001', 'TestResultId0002', 'TestResultId0003'], - entities: { TestResultId0001: testResults[0], TestResultId0002: testResults[1], TestResultId0003: testResults[2] }, - }; - const action = fetchTestResultsSuccess({ payload: testResults }); - const state = testResultsReducer(initialTestResultsState, action); - - expect(state).toEqual(newState); - expect(state).not.toBe(newState); - }); - }); - - describe('fetchTestResultsBySystemNumber actions', () => { - it('should set loading to true', () => { - const newState: TestResultsState = { ...initialTestResultsState, loading: true }; - const action = fetchTestResultsBySystemNumber({ systemNumber: 'TestResultId0001' }); - const state = testResultsReducer(initialTestResultsState, action); - - expect(state).toEqual(newState); - expect(state).not.toBe(newState); - }); - - describe('fetchTestResultsBySystemNumberSuccess', () => { - it('should set all test result records', () => { - const testResults = mockTestResultList(); - const newState: TestResultsState = { - ...initialTestResultsState, - ids: ['TestResultId0001'], - entities: { TestResultId0001: testResults[0] }, - }; - const action = fetchTestResultsBySystemNumberSuccess({ payload: testResults }); - const state = testResultsReducer(initialTestResultsState, action); - - expect(state).toEqual(newState); - expect(state).not.toBe(newState); - }); - }); - - describe('fetchTestResultsBySystemNumberFailed', () => { - it('should set error state', () => { - const newState = { ...initialTestResultsState, loading: false }; - const action = fetchTestResultsBySystemNumberFailed({ error: 'unit testing error message' }); - const state = testResultsReducer({ ...initialTestResultsState, loading: true }, action); - - expect(state).toEqual(newState); - expect(state).not.toBe(newState); - }); - }); - }); - - describe('fetchSelectedTestResult actions', () => { - it('should set loading to true', () => { - const newState: TestResultsState = { ...initialTestResultsState, loading: true }; - const action = fetchSelectedTestResult(); - const state = testResultsReducer(initialTestResultsState, action); - - expect(state).toEqual(newState); - expect(state).not.toBe(newState); - }); - - describe('fetchSelectedTestResultSuccess', () => { - it('should set all test result records', () => { - const testResults = mockTestResultList(); - const updatedTestResult = { ...testResults[0], odometerReading: 99999 }; - - const newState: TestResultsState = { - ...initialTestResultsState, - ids: [updatedTestResult.testResultId], - entities: { [updatedTestResult.testResultId]: updatedTestResult }, - }; - const action = fetchSelectedTestResultSuccess({ payload: updatedTestResult }); - const state = testResultsReducer(initialTestResultsState, action); - - expect(state).toEqual(newState); - expect(state).not.toBe(newState); - }); - }); - - describe('fetchSelectedTestResultFailed', () => { - it('should set error state', () => { - const newState = { ...initialTestResultsState, loading: false }; - const action = fetchSelectedTestResultFailed({ error: 'unit testing error message' }); - const state = testResultsReducer({ ...initialTestResultsState, loading: true }, action); - - expect(state).toEqual(newState); - expect(state).not.toBe(newState); - }); - }); - }); - - describe('updateTestResult actions', () => { - it('should set loading to true', () => { - const state: TestResultsState = { ...initialTestResultsState, loading: true }; - const action = updateTestResult({ value: {} as TestResultModel }); - const newState = testResultsReducer(initialTestResultsState, action); - - expect(state).toEqual(newState); - expect(state).not.toBe(newState); - }); - - describe('updateTestResultSuccess', () => { - it('should set loading to false', () => { - const state: TestResultsState = { ...initialTestResultsState, loading: false }; - const action = updateTestResultSuccess({ payload: { id: '', changes: {} as TestResultModel } }); - const newState = testResultsReducer({ ...initialTestResultsState, loading: true }, action); - - expect(state).toEqual(newState); - expect(state).not.toBe(newState); - }); - }); - - describe('updateTestResultFailed', () => { - it('should set loading to false', () => { - const state: TestResultsState = { ...initialTestResultsState, loading: false }; - const action = updateTestResultFailed({ errors: [] }); - const newState = testResultsReducer({ ...initialTestResultsState, loading: true }, action); - - expect(state).toEqual(newState); - expect(state).not.toBe(newState); - }); - }); - }); - - describe('calculateTestResult', () => { - let action: Action; - beforeEach(() => { - action = updateResultOfTest(); - }); - - it('should return a testType with a testResult of pass if empty defects', () => { - const testResult = { - testTypes: [ - { - testResult: 'fail', - defects: [], - }, - ], - } as unknown as TestResultModel; - const newState = testResultsReducer({ ...initialTestResultsState, editingTestResult: testResult }, action); - expect(newState.editingTestResult?.testTypes[0].testResult).toBe('pass'); - }); - - it('should not change the test result of defects key is not present', () => { - const testResult = { - testTypes: [ - { - testResult: 'fail', - }, - ], - } as TestResultModel; - const newState = testResultsReducer({ ...initialTestResultsState, editingTestResult: testResult }, action); - expect(newState.editingTestResult?.testTypes[0].testResult).toEqual(testResult.testTypes[0].testResult); - }); - it('should return a testType with a testResult of pass if advisory defect', () => { - const testResult = { - testTypes: [ - { - testResult: 'fail', - defects: [ - { - deficiencyCategory: 'advisory', - }, - ], - }, - ], - } as TestResultModel; - const newState = testResultsReducer({ ...initialTestResultsState, editingTestResult: testResult }, action); - expect(newState.editingTestResult?.testTypes[0].testResult).toBe('pass'); - }); - it('should return a testType with a testResult of pass if minor defect', () => { - const testResult = { - testTypes: [ - { - testResult: 'fail', - defects: [ - { - deficiencyCategory: 'minor', - }, - ], - }, - ], - } as TestResultModel; - const newState = testResultsReducer({ ...initialTestResultsState, editingTestResult: testResult }, action); - expect(newState.editingTestResult?.testTypes[0].testResult).toBe('pass'); - }); - it('should return a testType with a testResult of pass if minor and advisory defect', () => { - const testResult = { - testTypes: [ - { - testResult: 'fail', - defects: [ - { - deficiencyCategory: 'minor', - }, - { - deficiencyCategory: 'advisory', - }, - ], - }, - ], - } as TestResultModel; - const newState = testResultsReducer({ ...initialTestResultsState, editingTestResult: testResult }, action); - expect(newState.editingTestResult?.testTypes[0].testResult).toBe('pass'); - }); - it('should return a testType with a testResult of fail if at least one major defect', () => { - const testResult = { - testTypes: [ - { - testResult: 'pass', - defects: [ - { - deficiencyCategory: 'major', - }, - { - deficiencyCategory: 'advisory', - }, - ], - }, - ], - } as TestResultModel; - const newState = testResultsReducer({ ...initialTestResultsState, editingTestResult: testResult }, action); - expect(newState.editingTestResult?.testTypes[0].testResult).toBe('fail'); - }); - it('should return a testType with a testResult of fail if at least one dangerous defect', () => { - const testResult = { - testTypes: [ - { - testResult: 'pass', - defects: [ - { - deficiencyCategory: 'dangerous', - }, - { - deficiencyCategory: 'advisory', - }, - ], - }, - ], - } as TestResultModel; - const newState = testResultsReducer({ ...initialTestResultsState, editingTestResult: testResult }, action); - expect(newState.editingTestResult?.testTypes[0].testResult).toBe('fail'); - }); - it('should return a testType with a testResult of prs if major defect is prs and other defects are advisory', () => { - const testResult = { - testTypes: [ - { - testResult: 'pass', - defects: [ - { - deficiencyCategory: 'major', - prs: true, - }, - { - deficiencyCategory: 'advisory', - }, - ], - }, - ], - } as TestResultModel; - const newState = testResultsReducer({ ...initialTestResultsState, editingTestResult: testResult }, action); - expect(newState.editingTestResult?.testTypes[0].testResult).toBe('prs'); - }); - it('should return a testType with a testResult of fail if not all major/dangerous defects are prs', () => { - const testResult = { - testTypes: [ - { - testResult: 'pass', - defects: [ - { - deficiencyCategory: 'major', - }, - { - deficiencyCategory: 'dangerous', - prs: true, - }, - ], - }, - ], - } as TestResultModel; - const newState = testResultsReducer({ ...initialTestResultsState, editingTestResult: testResult }, action); - expect(newState.editingTestResult?.testTypes[0].testResult).toBe('fail'); - }); - it('should handle multiple testTypes', () => { - const testResult = { - testTypes: [ - { - testResult: 'pass', - defects: [ - { - deficiencyCategory: 'major', - }, - { - deficiencyCategory: 'dangerous', - prs: true, - }, - ], - }, - { - testResult: 'pass', - defects: [ - { - deficiencyCategory: 'dangerous', - prs: true, - }, - ], - }, - { - testResult: 'fail', - defects: [ - { - deficiencyCategory: 'advisory', - prs: true, - }, - ], - }, - ], - } as TestResultModel; - const newState = testResultsReducer({ ...initialTestResultsState, editingTestResult: testResult }, action); - expect(newState.editingTestResult?.testTypes[0].testResult).toBe('fail'); - expect(newState.editingTestResult?.testTypes[1].testResult).toBe('prs'); - expect(newState.editingTestResult?.testTypes[2].testResult).toBe('pass'); - }); - }); - - describe('createDefect', () => { - it('should create defect', () => { - const defect = { imNumber: 2 } as TestResultDefect; - const testResult = { - testTypes: [ - { - defects: [defect], - }, - ], - } as unknown as TestResultModel; - const action = createDefect({ defect }); - const newState = testResultsReducer({ ...initialTestResultsState, editingTestResult: testResult }, action); - - expect(newState.editingTestResult?.testTypes[0].defects?.length).toBe(2); - }); - }); - - describe('updateDefect', () => { - it('should update defect', () => { - const defect = { imNumber: 2 } as TestResultDefect; - const newDefect = { imNumber: 1 } as TestResultDefect; - const testResult = { - testTypes: [ - { - defects: [defect], - }, - ], - } as unknown as TestResultModel; - const action = updateDefect({ defect: newDefect, index: 0 }); - const newState = testResultsReducer({ ...initialTestResultsState, editingTestResult: testResult }, action); - - const path = newState.editingTestResult?.testTypes[0] - && newState.editingTestResult?.testTypes[0].defects - && newState.editingTestResult?.testTypes[0].defects[0].imNumber; - - expect(path).toBe(1); - }); - }); - - describe('removeDefect', () => { - it('should remove defect', () => { - const defect = { imNumber: 2 } as TestResultDefect; - const testResult = { - testTypes: [ - { - defects: [defect], - }, - ], - } as unknown as TestResultModel; - const action = removeDefect({ index: 0 }); - const newState = testResultsReducer({ ...initialTestResultsState, editingTestResult: testResult }, action); - - expect(newState.editingTestResult?.testTypes[0].defects?.length).toBe(0); - }); - }); - - describe('createRequiredStandard', () => { - it('should create required standard', () => { - const requiredStandard = { sectionNumber: 2 } as unknown as TestResultRequiredStandard; - const testResult = { - testTypes: [ - { - requiredStandards: [requiredStandard], - }, - ], - } as unknown as TestResultModel; - const action = createRequiredStandard({ requiredStandard }); - const newState = testResultsReducer({ ...initialTestResultsState, editingTestResult: testResult }, action); - - expect(newState.editingTestResult?.testTypes[0].requiredStandards?.length).toBe(2); - }); - }); - - describe('updateRequiredStandard', () => { - it('should update required standard', () => { - const requiredStandard = { sectionNumber: 2 } as unknown as TestResultRequiredStandard; - const newRequiredStandard = { sectionNumber: 1 } as unknown as TestResultRequiredStandard; - const testResult = { - testTypes: [ - { - requiredStandards: [requiredStandard], - }, - ], - } as unknown as TestResultModel; - const action = updateRequiredStandard({ requiredStandard: newRequiredStandard, index: 0 }); - const newState = testResultsReducer({ ...initialTestResultsState, editingTestResult: testResult }, action); - - expect(newState.editingTestResult?.testTypes[0].requiredStandards?.length).toBe(1); - expect(newState.editingTestResult?.testTypes?.at(0)?.requiredStandards?.at(0)?.sectionNumber).toBe(1); - }); - }); - - describe('removeRequiredStandard', () => { - it('should remove required standard', () => { - const requiredStandard = { sectionNumber: 2 } as unknown as TestResultRequiredStandard; - const testResult = { - testTypes: [ - { - requiredStandards: [requiredStandard], - }, - ], - } as unknown as TestResultModel; - const action = removeRequiredStandard({ index: 0 }); - const newState = testResultsReducer({ ...initialTestResultsState, editingTestResult: testResult }, action); - - expect(newState.editingTestResult?.testTypes[0].requiredStandards?.length).toBe(0); - }); - }); - - describe('cleanTestResultPayload', () => { - it('should return the state unaltered if no editing test result', () => { - const action = cleanTestResult(); - const newState = testResultsReducer({ ...initialTestResultsState, editingTestResult: undefined }, action); - - expect(newState).toStrictEqual({ ...initialTestResultsState, editingTestResult: undefined }); - - }); - - it('should return the state unaltered if no test type', () => { - const editingTestResult = { - foo: 'bar', - } as unknown as TestResultModel; - - const action = cleanTestResult(); - const newState = testResultsReducer({ ...initialTestResultsState, editingTestResult }, action); - - expect(newState).toStrictEqual({ ...initialTestResultsState, editingTestResult }); - }); - - it('should return the state unaltered if required standards are populated', () => { - const editingTestResult = { - testTypes: [ - { requiredStandards: ['I am a RS'], testTypeId: '125' }, - ], - } as unknown as TestResultModel; - - const action = cleanTestResult(); - const newState = testResultsReducer({ ...initialTestResultsState, editingTestResult }, action); - - expect(newState).toStrictEqual({ ...initialTestResultsState, editingTestResult }); - }); - - it('should return the state unaltered if test type is not spec 1 or spec 5', () => { - const editingTestResult = { - testTypes: [ - { requiredStandards: ['I am a RS'], testTypeId: 'xyz' }, - ], - } as unknown as TestResultModel; - - const action = cleanTestResult(); - const newState = testResultsReducer({ ...initialTestResultsState, editingTestResult }, action); - - expect(newState).toStrictEqual({ ...initialTestResultsState, editingTestResult }); - - }); - - it('should delete RS if empty from state', () => { - const editingTestResult = { - testTypes: [ - { requiredStandards: [], testTypeId: '125' }, - ], - } as unknown as TestResultModel; - - const action = cleanTestResult(); - const newState = testResultsReducer({ ...initialTestResultsState, editingTestResult }, action); - - expect(newState.editingTestResult?.testTypes[0].requiredStandards).toBeUndefined(); - }); - }); + describe('unknown action', () => { + it('should return the default state', () => { + const action = { + type: 'Unknown', + }; + const state = testResultsReducer(initialTestResultsState, action); + + expect(state).toBe(initialTestResultsState); + }); + }); + + describe('fetchTestResults', () => { + it('should set loading to true', () => { + const oldState: TestResultsState = { ...initialTestResultsState, loading: false }; + const action = fetchTestResults(); + const state = testResultsReducer(oldState, action); + + expect(state.loading).toBe(true); + expect(state).not.toBe(oldState); + }); + }); + + describe('fetchTestResultsSuccess', () => { + it('should set all test result records', () => { + const testResults = mockTestResultList(3); + const newState: TestResultsState = { + ...initialTestResultsState, + ids: ['TestResultId0001', 'TestResultId0002', 'TestResultId0003'], + entities: { + TestResultId0001: testResults[0], + TestResultId0002: testResults[1], + TestResultId0003: testResults[2], + }, + }; + const action = fetchTestResultsSuccess({ payload: testResults }); + const state = testResultsReducer(initialTestResultsState, action); + + expect(state).toEqual(newState); + expect(state).not.toBe(newState); + }); + }); + + describe('fetchTestResultsBySystemNumber actions', () => { + it('should set loading to true', () => { + const newState: TestResultsState = { ...initialTestResultsState, loading: true }; + const action = fetchTestResultsBySystemNumber({ systemNumber: 'TestResultId0001' }); + const state = testResultsReducer(initialTestResultsState, action); + + expect(state).toEqual(newState); + expect(state).not.toBe(newState); + }); + + describe('fetchTestResultsBySystemNumberSuccess', () => { + it('should set all test result records', () => { + const testResults = mockTestResultList(); + const newState: TestResultsState = { + ...initialTestResultsState, + ids: ['TestResultId0001'], + entities: { TestResultId0001: testResults[0] }, + }; + const action = fetchTestResultsBySystemNumberSuccess({ payload: testResults }); + const state = testResultsReducer(initialTestResultsState, action); + + expect(state).toEqual(newState); + expect(state).not.toBe(newState); + }); + }); + + describe('fetchTestResultsBySystemNumberFailed', () => { + it('should set error state', () => { + const newState = { ...initialTestResultsState, loading: false }; + const action = fetchTestResultsBySystemNumberFailed({ error: 'unit testing error message' }); + const state = testResultsReducer({ ...initialTestResultsState, loading: true }, action); + + expect(state).toEqual(newState); + expect(state).not.toBe(newState); + }); + }); + }); + + describe('fetchSelectedTestResult actions', () => { + it('should set loading to true', () => { + const newState: TestResultsState = { ...initialTestResultsState, loading: true }; + const action = fetchSelectedTestResult(); + const state = testResultsReducer(initialTestResultsState, action); + + expect(state).toEqual(newState); + expect(state).not.toBe(newState); + }); + + describe('fetchSelectedTestResultSuccess', () => { + it('should set all test result records', () => { + const testResults = mockTestResultList(); + const updatedTestResult = { ...testResults[0], odometerReading: 99999 }; + + const newState: TestResultsState = { + ...initialTestResultsState, + ids: [updatedTestResult.testResultId], + entities: { [updatedTestResult.testResultId]: updatedTestResult }, + }; + const action = fetchSelectedTestResultSuccess({ payload: updatedTestResult }); + const state = testResultsReducer(initialTestResultsState, action); + + expect(state).toEqual(newState); + expect(state).not.toBe(newState); + }); + }); + + describe('fetchSelectedTestResultFailed', () => { + it('should set error state', () => { + const newState = { ...initialTestResultsState, loading: false }; + const action = fetchSelectedTestResultFailed({ error: 'unit testing error message' }); + const state = testResultsReducer({ ...initialTestResultsState, loading: true }, action); + + expect(state).toEqual(newState); + expect(state).not.toBe(newState); + }); + }); + }); + + describe('updateTestResult actions', () => { + it('should set loading to true', () => { + const state: TestResultsState = { ...initialTestResultsState, loading: true }; + const action = updateTestResult({ value: {} as TestResultModel }); + const newState = testResultsReducer(initialTestResultsState, action); + + expect(state).toEqual(newState); + expect(state).not.toBe(newState); + }); + + describe('updateTestResultSuccess', () => { + it('should set loading to false', () => { + const state: TestResultsState = { ...initialTestResultsState, loading: false }; + const action = updateTestResultSuccess({ payload: { id: '', changes: {} as TestResultModel } }); + const newState = testResultsReducer({ ...initialTestResultsState, loading: true }, action); + + expect(state).toEqual(newState); + expect(state).not.toBe(newState); + }); + }); + + describe('updateTestResultFailed', () => { + it('should set loading to false', () => { + const state: TestResultsState = { ...initialTestResultsState, loading: false }; + const action = updateTestResultFailed({ errors: [] }); + const newState = testResultsReducer({ ...initialTestResultsState, loading: true }, action); + + expect(state).toEqual(newState); + expect(state).not.toBe(newState); + }); + }); + }); + + describe('calculateTestResult', () => { + let action: Action; + beforeEach(() => { + action = updateResultOfTest(); + }); + + it('should return a testType with a testResult of pass if empty defects', () => { + const testResult = { + testTypes: [ + { + testResult: 'fail', + defects: [], + }, + ], + } as unknown as TestResultModel; + const newState = testResultsReducer({ ...initialTestResultsState, editingTestResult: testResult }, action); + expect(newState.editingTestResult?.testTypes[0].testResult).toBe('pass'); + }); + + it('should not change the test result of defects key is not present', () => { + const testResult = { + testTypes: [ + { + testResult: 'fail', + }, + ], + } as TestResultModel; + const newState = testResultsReducer({ ...initialTestResultsState, editingTestResult: testResult }, action); + expect(newState.editingTestResult?.testTypes[0].testResult).toEqual(testResult.testTypes[0].testResult); + }); + it('should return a testType with a testResult of pass if advisory defect', () => { + const testResult = { + testTypes: [ + { + testResult: 'fail', + defects: [ + { + deficiencyCategory: 'advisory', + }, + ], + }, + ], + } as TestResultModel; + const newState = testResultsReducer({ ...initialTestResultsState, editingTestResult: testResult }, action); + expect(newState.editingTestResult?.testTypes[0].testResult).toBe('pass'); + }); + it('should return a testType with a testResult of pass if minor defect', () => { + const testResult = { + testTypes: [ + { + testResult: 'fail', + defects: [ + { + deficiencyCategory: 'minor', + }, + ], + }, + ], + } as TestResultModel; + const newState = testResultsReducer({ ...initialTestResultsState, editingTestResult: testResult }, action); + expect(newState.editingTestResult?.testTypes[0].testResult).toBe('pass'); + }); + it('should return a testType with a testResult of pass if minor and advisory defect', () => { + const testResult = { + testTypes: [ + { + testResult: 'fail', + defects: [ + { + deficiencyCategory: 'minor', + }, + { + deficiencyCategory: 'advisory', + }, + ], + }, + ], + } as TestResultModel; + const newState = testResultsReducer({ ...initialTestResultsState, editingTestResult: testResult }, action); + expect(newState.editingTestResult?.testTypes[0].testResult).toBe('pass'); + }); + it('should return a testType with a testResult of fail if at least one major defect', () => { + const testResult = { + testTypes: [ + { + testResult: 'pass', + defects: [ + { + deficiencyCategory: 'major', + }, + { + deficiencyCategory: 'advisory', + }, + ], + }, + ], + } as TestResultModel; + const newState = testResultsReducer({ ...initialTestResultsState, editingTestResult: testResult }, action); + expect(newState.editingTestResult?.testTypes[0].testResult).toBe('fail'); + }); + it('should return a testType with a testResult of fail if at least one dangerous defect', () => { + const testResult = { + testTypes: [ + { + testResult: 'pass', + defects: [ + { + deficiencyCategory: 'dangerous', + }, + { + deficiencyCategory: 'advisory', + }, + ], + }, + ], + } as TestResultModel; + const newState = testResultsReducer({ ...initialTestResultsState, editingTestResult: testResult }, action); + expect(newState.editingTestResult?.testTypes[0].testResult).toBe('fail'); + }); + it('should return a testType with a testResult of prs if major defect is prs and other defects are advisory', () => { + const testResult = { + testTypes: [ + { + testResult: 'pass', + defects: [ + { + deficiencyCategory: 'major', + prs: true, + }, + { + deficiencyCategory: 'advisory', + }, + ], + }, + ], + } as TestResultModel; + const newState = testResultsReducer({ ...initialTestResultsState, editingTestResult: testResult }, action); + expect(newState.editingTestResult?.testTypes[0].testResult).toBe('prs'); + }); + it('should return a testType with a testResult of fail if not all major/dangerous defects are prs', () => { + const testResult = { + testTypes: [ + { + testResult: 'pass', + defects: [ + { + deficiencyCategory: 'major', + }, + { + deficiencyCategory: 'dangerous', + prs: true, + }, + ], + }, + ], + } as TestResultModel; + const newState = testResultsReducer({ ...initialTestResultsState, editingTestResult: testResult }, action); + expect(newState.editingTestResult?.testTypes[0].testResult).toBe('fail'); + }); + it('should handle multiple testTypes', () => { + const testResult = { + testTypes: [ + { + testResult: 'pass', + defects: [ + { + deficiencyCategory: 'major', + }, + { + deficiencyCategory: 'dangerous', + prs: true, + }, + ], + }, + { + testResult: 'pass', + defects: [ + { + deficiencyCategory: 'dangerous', + prs: true, + }, + ], + }, + { + testResult: 'fail', + defects: [ + { + deficiencyCategory: 'advisory', + prs: true, + }, + ], + }, + ], + } as TestResultModel; + const newState = testResultsReducer({ ...initialTestResultsState, editingTestResult: testResult }, action); + expect(newState.editingTestResult?.testTypes[0].testResult).toBe('fail'); + expect(newState.editingTestResult?.testTypes[1].testResult).toBe('prs'); + expect(newState.editingTestResult?.testTypes[2].testResult).toBe('pass'); + }); + }); + + describe('createDefect', () => { + it('should create defect', () => { + const defect = { imNumber: 2 } as TestResultDefect; + const testResult = { + testTypes: [ + { + defects: [defect], + }, + ], + } as unknown as TestResultModel; + const action = createDefect({ defect }); + const newState = testResultsReducer({ ...initialTestResultsState, editingTestResult: testResult }, action); + + expect(newState.editingTestResult?.testTypes[0].defects?.length).toBe(2); + }); + }); + + describe('updateDefect', () => { + it('should update defect', () => { + const defect = { imNumber: 2 } as TestResultDefect; + const newDefect = { imNumber: 1 } as TestResultDefect; + const testResult = { + testTypes: [ + { + defects: [defect], + }, + ], + } as unknown as TestResultModel; + const action = updateDefect({ defect: newDefect, index: 0 }); + const newState = testResultsReducer({ ...initialTestResultsState, editingTestResult: testResult }, action); + + const path = + newState.editingTestResult?.testTypes[0] && + newState.editingTestResult?.testTypes[0].defects && + newState.editingTestResult?.testTypes[0].defects[0].imNumber; + + expect(path).toBe(1); + }); + }); + + describe('removeDefect', () => { + it('should remove defect', () => { + const defect = { imNumber: 2 } as TestResultDefect; + const testResult = { + testTypes: [ + { + defects: [defect], + }, + ], + } as unknown as TestResultModel; + const action = removeDefect({ index: 0 }); + const newState = testResultsReducer({ ...initialTestResultsState, editingTestResult: testResult }, action); + + expect(newState.editingTestResult?.testTypes[0].defects?.length).toBe(0); + }); + }); + + describe('createRequiredStandard', () => { + it('should create required standard', () => { + const requiredStandard = { sectionNumber: 2 } as unknown as TestResultRequiredStandard; + const testResult = { + testTypes: [ + { + requiredStandards: [requiredStandard], + }, + ], + } as unknown as TestResultModel; + const action = createRequiredStandard({ requiredStandard }); + const newState = testResultsReducer({ ...initialTestResultsState, editingTestResult: testResult }, action); + + expect(newState.editingTestResult?.testTypes[0].requiredStandards?.length).toBe(2); + }); + }); + + describe('updateRequiredStandard', () => { + it('should update required standard', () => { + const requiredStandard = { sectionNumber: 2 } as unknown as TestResultRequiredStandard; + const newRequiredStandard = { sectionNumber: 1 } as unknown as TestResultRequiredStandard; + const testResult = { + testTypes: [ + { + requiredStandards: [requiredStandard], + }, + ], + } as unknown as TestResultModel; + const action = updateRequiredStandard({ requiredStandard: newRequiredStandard, index: 0 }); + const newState = testResultsReducer({ ...initialTestResultsState, editingTestResult: testResult }, action); + + expect(newState.editingTestResult?.testTypes[0].requiredStandards?.length).toBe(1); + expect(newState.editingTestResult?.testTypes?.at(0)?.requiredStandards?.at(0)?.sectionNumber).toBe(1); + }); + }); + + describe('removeRequiredStandard', () => { + it('should remove required standard', () => { + const requiredStandard = { sectionNumber: 2 } as unknown as TestResultRequiredStandard; + const testResult = { + testTypes: [ + { + requiredStandards: [requiredStandard], + }, + ], + } as unknown as TestResultModel; + const action = removeRequiredStandard({ index: 0 }); + const newState = testResultsReducer({ ...initialTestResultsState, editingTestResult: testResult }, action); + + expect(newState.editingTestResult?.testTypes[0].requiredStandards?.length).toBe(0); + }); + }); + + describe('cleanTestResultPayload', () => { + it('should return the state unaltered if no editing test result', () => { + const action = cleanTestResult(); + const newState = testResultsReducer({ ...initialTestResultsState, editingTestResult: undefined }, action); + + expect(newState).toStrictEqual({ ...initialTestResultsState, editingTestResult: undefined }); + }); + + it('should return the state unaltered if no test type', () => { + const editingTestResult = { + foo: 'bar', + } as unknown as TestResultModel; + + const action = cleanTestResult(); + const newState = testResultsReducer({ ...initialTestResultsState, editingTestResult }, action); + + expect(newState).toStrictEqual({ ...initialTestResultsState, editingTestResult }); + }); + + it('should return the state unaltered if required standards are populated', () => { + const editingTestResult = { + testTypes: [{ requiredStandards: ['I am a RS'], testTypeId: '125' }], + } as unknown as TestResultModel; + + const action = cleanTestResult(); + const newState = testResultsReducer({ ...initialTestResultsState, editingTestResult }, action); + + expect(newState).toStrictEqual({ ...initialTestResultsState, editingTestResult }); + }); + + it('should return the state unaltered if test type is not spec 1 or spec 5', () => { + const editingTestResult = { + testTypes: [{ requiredStandards: ['I am a RS'], testTypeId: 'xyz' }], + } as unknown as TestResultModel; + + const action = cleanTestResult(); + const newState = testResultsReducer({ ...initialTestResultsState, editingTestResult }, action); + + expect(newState).toStrictEqual({ ...initialTestResultsState, editingTestResult }); + }); + + it('should delete RS if empty from state', () => { + const editingTestResult = { + testTypes: [{ requiredStandards: [], testTypeId: '125' }], + } as unknown as TestResultModel; + + const action = cleanTestResult(); + const newState = testResultsReducer({ ...initialTestResultsState, editingTestResult }, action); + + expect(newState.editingTestResult?.testTypes[0].requiredStandards).toBeUndefined(); + }); + }); }); diff --git a/src/app/store/test-records/reducers/test-records.reducer.ts b/src/app/store/test-records/reducers/test-records.reducer.ts index aed052d4f7..3d05829fc2 100644 --- a/src/app/store/test-records/reducers/test-records.reducer.ts +++ b/src/app/store/test-records/reducers/test-records.reducer.ts @@ -1,4 +1,8 @@ -import { TEST_TYPES_GROUP1_SPEC_TEST, TEST_TYPES_GROUP5_SPEC_TEST } from '@forms/models/testTypeId.enum'; +import { + TEST_TYPES_GROUP1_SPEC_TEST, + TEST_TYPES_GROUP5_SPEC_TEST, + TEST_TYPES_GROUP9_10_CENTRAL_DOCS, +} from '@forms/models/testTypeId.enum'; // eslint-disable-next-line import/no-cycle import { FormNode } from '@forms/services/dynamic-form.types'; import { DeficiencyCategoryEnum, TestResultDefect } from '@models/test-results/test-result-defect.model'; @@ -11,275 +15,365 @@ import { createFeatureSelector, createReducer, on } from '@ngrx/store'; import cloneDeep from 'lodash.clonedeep'; import merge from 'lodash.merge'; import { - cancelEditingTestResult, - cleanTestResult, - createDefect, - createRequiredStandard, - createTestResult, - createTestResultFailed, - createTestResultSuccess, - fetchSelectedTestResult, - fetchSelectedTestResultFailed, - fetchSelectedTestResultSuccess, - fetchTestResults, - fetchTestResultsBySystemNumber, - fetchTestResultsBySystemNumberFailed, - fetchTestResultsBySystemNumberSuccess, - fetchTestResultsSuccess, - initialContingencyTest, - removeDefect, - removeRequiredStandard, - setResultOfTest, - templateSectionsChanged, - updateDefect, - updateEditingTestResult, - updateRequiredStandard, - updateResultOfTest, - updateResultOfTestRequiredStandards, - updateTestResult, - updateTestResultFailed, - updateTestResultSuccess, + cancelEditingTestResult, + cleanTestResult, + createDefect, + createRequiredStandard, + createTestResult, + createTestResultFailed, + createTestResultSuccess, + fetchSelectedTestResult, + fetchSelectedTestResultFailed, + fetchSelectedTestResultSuccess, + fetchTestResults, + fetchTestResultsBySystemNumber, + fetchTestResultsBySystemNumberFailed, + fetchTestResultsBySystemNumberSuccess, + fetchTestResultsSuccess, + initialContingencyTest, + removeDefect, + removeRequiredStandard, + setResultOfTest, + templateSectionsChanged, + updateDefect, + updateEditingTestResult, + updateRequiredStandard, + updateResultOfTest, + updateResultOfTestRequiredStandards, + updateTestResult, + updateTestResultFailed, + updateTestResultSuccess, } from '../actions/test-records.actions'; export const STORE_FEATURE_TEST_RESULTS_KEY = 'testRecords'; interface Extras { - error: string; - loading: boolean; - editingTestResult?: TestResultModel; - sectionTemplates?: FormNode[]; + error: string; + loading: boolean; + editingTestResult?: TestResultModel; + sectionTemplates?: FormNode[]; } -export interface TestResultsState extends EntityState, Extras { } +export interface TestResultsState extends EntityState, Extras {} const selectTestResultId = (a: TestResultModel): string => { - return a.testResultId; + return a.testResultId; }; -export const testResultAdapter: EntityAdapter = createEntityAdapter({ selectId: selectTestResultId }); +export const testResultAdapter: EntityAdapter = createEntityAdapter({ + selectId: selectTestResultId, +}); export const initialTestResultsState = testResultAdapter.getInitialState({ - error: '', - loading: false, + error: '', + loading: false, }); export const testResultsReducer = createReducer( - initialTestResultsState, - on(fetchTestResults, (state) => ({ ...state, loading: true })), - on(fetchTestResultsSuccess, (state, action) => ({ ...testResultAdapter.setAll(action.payload, state), loading: false })), - - on(fetchTestResultsBySystemNumber, (state) => ({ ...state, loading: true })), - on(fetchTestResultsBySystemNumberSuccess, (state, action) => ({ ...testResultAdapter.setAll(action.payload, state), loading: false })), - on(fetchTestResultsBySystemNumberFailed, (state) => ({ ...testResultAdapter.setAll([], state), loading: false })), - - on(fetchSelectedTestResult, (state) => ({ ...state, loading: true })), - on(fetchSelectedTestResultSuccess, (state, action) => ({ ...testResultAdapter.upsertOne(action.payload, state), loading: false })), - on(fetchSelectedTestResultFailed, (state) => ({ ...state, loading: false })), - - on(createTestResult, updateTestResult, (state) => ({ ...state, loading: true })), - on(updateTestResultSuccess, (state, action) => ({ - ...testResultAdapter.updateOne(action.payload, state), - loading: false, - })), - on(createTestResultSuccess, createTestResultFailed, updateTestResultFailed, (state) => ({ ...state, loading: false })), - - on(updateResultOfTest, (state) => ({ ...state, editingTestResult: calculateTestResult(state.editingTestResult) })), - on(setResultOfTest, (state, action) => ({ ...state, editingTestResult: setTestResult(state.editingTestResult, action.result) })), - - on(updateEditingTestResult, (state, action) => ({ ...state, editingTestResult: merge({}, action.testResult) })), - on(cancelEditingTestResult, (state) => ({ ...state, editingTestResult: undefined, sectionTemplates: undefined })), - - on(initialContingencyTest, (state, action) => ({ - ...state, - editingTestResult: { ...action.testResult } as TestResultModel, - })), - - on(templateSectionsChanged, (state, action) => ({ ...state, sectionTemplates: action.sectionTemplates, editingTestResult: action.sectionsValue })), - - on(createDefect, (state, action) => ({ ...state, editingTestResult: createNewDefect(state.editingTestResult, action.defect) })), - on(updateDefect, (state, action) => ({ ...state, editingTestResult: updateDefectAtIndex(state.editingTestResult, action.defect, action.index) })), - on(removeDefect, (state, action) => ({ ...state, editingTestResult: removeDefectAtIndex(state.editingTestResult, action.index) })), - - on(createRequiredStandard, (state, action) => - ({ ...state, editingTestResult: createNewRequiredStandard(state.editingTestResult, action.requiredStandard) })), - on(updateRequiredStandard, (state, action) => - ({ ...state, editingTestResult: updateRequiredStandardAtIndex(state.editingTestResult, action.requiredStandard, action.index) })), - on(removeRequiredStandard, (state, action) => - ({ ...state, editingTestResult: removeRequiredStandardAtIndex(state.editingTestResult, action.index) })), - - on(updateResultOfTestRequiredStandards, (state) => - ({ ...state, editingTestResult: calculateTestResultRequiredStandards(state.editingTestResult) })), - - on(cleanTestResult, (state) => ({ ...state, editingTestResult: cleanTestResultPayload(state.editingTestResult) })), + initialTestResultsState, + on(fetchTestResults, (state) => ({ ...state, loading: true })), + on(fetchTestResultsSuccess, (state, action) => ({ + ...testResultAdapter.setAll(action.payload, state), + loading: false, + })), + + on(fetchTestResultsBySystemNumber, (state) => ({ ...state, loading: true })), + on(fetchTestResultsBySystemNumberSuccess, (state, action) => ({ + ...testResultAdapter.setAll(action.payload, state), + loading: false, + })), + on(fetchTestResultsBySystemNumberFailed, (state) => ({ ...testResultAdapter.setAll([], state), loading: false })), + + on(fetchSelectedTestResult, (state) => ({ ...state, loading: true })), + on(fetchSelectedTestResultSuccess, (state, action) => ({ + ...testResultAdapter.upsertOne(action.payload, state), + loading: false, + })), + on(fetchSelectedTestResultFailed, (state) => ({ ...state, loading: false })), + + on(createTestResult, updateTestResult, (state) => ({ ...state, loading: true })), + on(updateTestResultSuccess, (state, action) => ({ + ...testResultAdapter.updateOne(action.payload, state), + loading: false, + })), + on(createTestResultSuccess, createTestResultFailed, updateTestResultFailed, (state) => ({ + ...state, + loading: false, + })), + + on(updateResultOfTest, (state) => ({ ...state, editingTestResult: calculateTestResult(state.editingTestResult) })), + on(setResultOfTest, (state, action) => ({ + ...state, + editingTestResult: setTestResult(state.editingTestResult, action.result), + })), + + on(updateEditingTestResult, (state, action) => ({ ...state, editingTestResult: merge({}, action.testResult) })), + on(cancelEditingTestResult, (state) => ({ ...state, editingTestResult: undefined, sectionTemplates: undefined })), + + on(initialContingencyTest, (state, action) => ({ + ...state, + editingTestResult: { ...action.testResult } as TestResultModel, + })), + + on(templateSectionsChanged, (state, action) => ({ + ...state, + sectionTemplates: action.sectionTemplates, + editingTestResult: action.sectionsValue, + })), + + on(createDefect, (state, action) => ({ + ...state, + editingTestResult: createNewDefect(state.editingTestResult, action.defect), + })), + on(updateDefect, (state, action) => ({ + ...state, + editingTestResult: updateDefectAtIndex(state.editingTestResult, action.defect, action.index), + })), + on(removeDefect, (state, action) => ({ + ...state, + editingTestResult: removeDefectAtIndex(state.editingTestResult, action.index), + })), + + on(createRequiredStandard, (state, action) => ({ + ...state, + editingTestResult: createNewRequiredStandard(state.editingTestResult, action.requiredStandard), + })), + on(updateRequiredStandard, (state, action) => ({ + ...state, + editingTestResult: updateRequiredStandardAtIndex(state.editingTestResult, action.requiredStandard, action.index), + })), + on(removeRequiredStandard, (state, action) => ({ + ...state, + editingTestResult: removeRequiredStandardAtIndex(state.editingTestResult, action.index), + })), + + on(updateResultOfTestRequiredStandards, (state) => ({ + ...state, + editingTestResult: calculateTestResultRequiredStandards(state.editingTestResult), + })), + + on(cleanTestResult, (state) => ({ ...state, editingTestResult: cleanTestResultPayload(state.editingTestResult) })) ); export const testResultsFeatureState = createFeatureSelector(STORE_FEATURE_TEST_RESULTS_KEY); -function createNewRequiredStandard(testResultState: TestResultModel | undefined, requiredStandard: TestResultRequiredStandard) { - if (!testResultState) { - return; - } - const testResult = cloneDeep(testResultState); - - if (!testResult.testTypes[0].requiredStandards) { - return; - } - testResult.testTypes[0].requiredStandards.push(requiredStandard); - - return { ...testResult }; +function createNewRequiredStandard( + testResultState: TestResultModel | undefined, + requiredStandard: TestResultRequiredStandard +) { + if (!testResultState) { + return; + } + const testResult = cloneDeep(testResultState); + + if (!testResult.testTypes[0].requiredStandards) { + return; + } + testResult.testTypes[0].requiredStandards.push(requiredStandard); + + return { ...testResult }; } function cleanTestResultPayload(testResult: TestResultModel | undefined) { - if (testResult?.testTypes?.at(0)) { - const { testTypeId, requiredStandards } = testResult.testTypes[0]; - if ((TEST_TYPES_GROUP1_SPEC_TEST.includes(testTypeId) || TEST_TYPES_GROUP5_SPEC_TEST.includes(testTypeId)) && !(requiredStandards ?? []).length) { - delete testResult.testTypes[0].requiredStandards; - } - } - return testResult; + if (!testResult || !testResult.testTypes) { + return testResult; + } + + const testTypes = testResult.testTypes.map((testType, index) => { + // Remove empty requiredStandards from pass/prs non-voluntary IVA/MVSA tests + if (index === 0) { + const { testTypeId, requiredStandards } = testType; + const isGroup1SpecTest = TEST_TYPES_GROUP1_SPEC_TEST.includes(testTypeId); + const isGroup5SpecTest = TEST_TYPES_GROUP5_SPEC_TEST.includes(testTypeId); + if ((isGroup1SpecTest || isGroup5SpecTest) && !(requiredStandards ?? []).length) { + delete testType.requiredStandards; + } + } + + // If the test type is a fail/cancel/abandon, and issueRequired is true, set it to false + const isFail = testType.testResult === resultOfTestEnum.fail; + const isAbandon = testType.testResult === resultOfTestEnum.abandoned; + if ((isFail || isAbandon) && testType.centralDocs?.issueRequired) { + testType.centralDocs.issueRequired = false; + } + + // If test type has issueRequired set to true, set the certificateNumber/secondaryCertificateNumber to 000000 + if (testType.centralDocs?.issueRequired) { + testType.certificateNumber = '000000'; + testType.secondaryCertificateNumber = '000000'; + } + + // When abandoning a first test ensure certificate number is sent up + if (isAbandon && TEST_TYPES_GROUP9_10_CENTRAL_DOCS.includes(testType.testTypeId)) { + testType.certificateNumber = ''; + } + + return testType; + }); + + return { ...testResult, testTypes }; } -function updateRequiredStandardAtIndex(testResultState: TestResultModel | undefined, requiredStandard: TestResultRequiredStandard, index: number) { - if (!testResultState) { - return; - } - const testResult = cloneDeep(testResultState); - if (!testResult.testTypes[0].requiredStandards) { - return; - } - testResult.testTypes[0].requiredStandards[`${index}`] = requiredStandard; - - return { ...testResult }; +function updateRequiredStandardAtIndex( + testResultState: TestResultModel | undefined, + requiredStandard: TestResultRequiredStandard, + index: number +) { + if (!testResultState) { + return; + } + const testResult = cloneDeep(testResultState); + if (!testResult.testTypes[0].requiredStandards) { + return; + } + testResult.testTypes[0].requiredStandards[`${index}`] = requiredStandard; + + return { ...testResult }; } function removeRequiredStandardAtIndex(testResultState: TestResultModel | undefined, index: number) { - if (!testResultState) { - return; - } - const testResult = cloneDeep(testResultState); - if (!testResult.testTypes[0].requiredStandards) { - return; - } - testResult.testTypes[0].requiredStandards.splice(index, 1); - - return { ...testResult }; + if (!testResultState) { + return; + } + const testResult = cloneDeep(testResultState); + if (!testResult.testTypes[0].requiredStandards) { + return; + } + testResult.testTypes[0].requiredStandards.splice(index, 1); + + return { ...testResult }; } -function createNewDefect(testResultState: TestResultModel | undefined, defect: TestResultDefect): TestResultModel | undefined { - if (!testResultState) { - return; - } - const testResult = cloneDeep(testResultState); - - if (!testResult.testTypes[0].defects) { - return; - } - testResult.testTypes[0].defects.push(defect); - - return { ...testResult }; +function createNewDefect( + testResultState: TestResultModel | undefined, + defect: TestResultDefect +): TestResultModel | undefined { + if (!testResultState) { + return; + } + const testResult = cloneDeep(testResultState); + + if (!testResult.testTypes[0].defects) { + return; + } + testResult.testTypes[0].defects.push(defect); + + return { ...testResult }; } -function updateDefectAtIndex(testResultState: TestResultModel | undefined, defect: TestResultDefect, index: number): TestResultModel | undefined { - if (!testResultState) { - return; - } - const testResult = cloneDeep(testResultState); - if (!testResult.testTypes[0].defects) { - return; - } - testResult.testTypes[0].defects[`${index}`] = defect; - - return { ...testResult }; +function updateDefectAtIndex( + testResultState: TestResultModel | undefined, + defect: TestResultDefect, + index: number +): TestResultModel | undefined { + if (!testResultState) { + return; + } + const testResult = cloneDeep(testResultState); + if (!testResult.testTypes[0].defects) { + return; + } + testResult.testTypes[0].defects[`${index}`] = defect; + + return { ...testResult }; } function removeDefectAtIndex(testResultState: TestResultModel | undefined, index: number): TestResultModel | undefined { - if (!testResultState) { - return; - } - const testResult = cloneDeep(testResultState); - if (!testResult.testTypes[0].defects) { - return; - } - testResult.testTypes[0].defects.splice(index, 1); - - return { ...testResult }; + if (!testResultState) { + return; + } + const testResult = cloneDeep(testResultState); + if (!testResult.testTypes[0].defects) { + return; + } + testResult.testTypes[0].defects.splice(index, 1); + + return { ...testResult }; } function calculateTestResult(testResultState: TestResultModel | undefined): TestResultModel | undefined { - if (!testResultState) { - return; - } - - const testResult = cloneDeep(testResultState); - - const newTestTypes = testResult.testTypes.map((testType) => { - if (testType.testResult === resultOfTestEnum.abandoned || !testType.defects || TypeOfTest.DESK_BASED === testResultState?.typeOfTest) { - return testType; - } - - if (!testType.defects.length) { - testType.testResult = resultOfTestEnum.pass; - return testType; - } - - const failOrPrs = testType.defects.some( - (defect) => - defect.deficiencyCategory === DeficiencyCategoryEnum.Major - || defect.deficiencyCategory === DeficiencyCategoryEnum.Dangerous, - ); - if (!failOrPrs) { - testType.testResult = resultOfTestEnum.pass; - return testType; - } - - testType.testResult = testType.defects.every( - (defect) => - defect.deficiencyCategory === DeficiencyCategoryEnum.Advisory - || defect.deficiencyCategory === DeficiencyCategoryEnum.Minor - || (defect.deficiencyCategory === DeficiencyCategoryEnum.Dangerous && defect.prs) - || (defect.deficiencyCategory === DeficiencyCategoryEnum.Major && defect.prs), - ) - ? resultOfTestEnum.prs - : resultOfTestEnum.fail; - - return testType; - }); - return { ...testResult, testTypes: [...newTestTypes] }; + if (!testResultState) { + return; + } + + const testResult = cloneDeep(testResultState); + + const newTestTypes = testResult.testTypes.map((testType) => { + if ( + testType.testResult === resultOfTestEnum.abandoned || + !testType.defects || + TypeOfTest.DESK_BASED === testResultState?.typeOfTest + ) { + return testType; + } + + if (!testType.defects.length) { + testType.testResult = resultOfTestEnum.pass; + return testType; + } + + const failOrPrs = testType.defects.some( + (defect) => + defect.deficiencyCategory === DeficiencyCategoryEnum.Major || + defect.deficiencyCategory === DeficiencyCategoryEnum.Dangerous + ); + if (!failOrPrs) { + testType.testResult = resultOfTestEnum.pass; + return testType; + } + + testType.testResult = testType.defects.every( + (defect) => + defect.deficiencyCategory === DeficiencyCategoryEnum.Advisory || + defect.deficiencyCategory === DeficiencyCategoryEnum.Minor || + (defect.deficiencyCategory === DeficiencyCategoryEnum.Dangerous && defect.prs) || + (defect.deficiencyCategory === DeficiencyCategoryEnum.Major && defect.prs) + ) + ? resultOfTestEnum.prs + : resultOfTestEnum.fail; + + return testType; + }); + return { ...testResult, testTypes: [...newTestTypes] }; } -function calculateTestResultRequiredStandards(testResultState: TestResultModel | undefined): TestResultModel | undefined { - if (!testResultState) { - return; - } - - const testResult = cloneDeep(testResultState); - - const newTestTypes = testResult.testTypes.map((testType) => { - if (testType.testResult === resultOfTestEnum.abandoned || !testType.requiredStandards || TypeOfTest.DESK_BASED === testResultState?.typeOfTest) { - return testType; - } - - if (!testType.requiredStandards.length) { - testType.testResult = resultOfTestEnum.pass; - return testType; - } - - testType.testResult = testType.requiredStandards.every( - (rs) => rs.prs, - ) - ? resultOfTestEnum.prs - : resultOfTestEnum.fail; - - return testType; - }); - return { ...testResult, testTypes: [...newTestTypes] }; +function calculateTestResultRequiredStandards( + testResultState: TestResultModel | undefined +): TestResultModel | undefined { + if (!testResultState) { + return; + } + + const testResult = cloneDeep(testResultState); + + const newTestTypes = testResult.testTypes.map((testType) => { + if ( + testType.testResult === resultOfTestEnum.abandoned || + !testType.requiredStandards || + TypeOfTest.DESK_BASED === testResultState?.typeOfTest + ) { + return testType; + } + + if (!testType.requiredStandards.length) { + testType.testResult = resultOfTestEnum.pass; + return testType; + } + + testType.testResult = testType.requiredStandards.every((rs) => rs.prs) + ? resultOfTestEnum.prs + : resultOfTestEnum.fail; + + return testType; + }); + return { ...testResult, testTypes: [...newTestTypes] }; } function setTestResult(testResult: TestResultModel | undefined, result: resultOfTestEnum): TestResultModel | undefined { - if (!testResult) { - return; - } - const testResultCopy = cloneDeep(testResult); - testResultCopy.testTypes[0].testResult = result; - return testResultCopy; + if (!testResult) { + return; + } + const testResultCopy = cloneDeep(testResult); + testResultCopy.testTypes[0].testResult = result; + return testResultCopy; } diff --git a/src/app/store/test-records/selectors/test-records.selectors.spec.ts b/src/app/store/test-records/selectors/test-records.selectors.spec.ts index d3b1497714..1733b20444 100644 --- a/src/app/store/test-records/selectors/test-records.selectors.spec.ts +++ b/src/app/store/test-records/selectors/test-records.selectors.spec.ts @@ -1,252 +1,280 @@ import { Params } from '@angular/router'; +import { createMockAdditionalDefect, createMockCustomDefect } from '@mocks/custom-defect.mock'; import { mockDefect } from '@mocks/mock-defects'; import { createMockTestResult } from '@mocks/test-result.mock'; import { createMockTestType } from '@mocks/test-type.mock'; import { TestResultModel } from '@models/test-results/test-result.model'; -import { createMockAdditionalDefect, createMockCustomDefect } from '@mocks/custom-defect.mock'; import { mockTestResult } from '../../../../mocks/mock-test-result'; import { TestResultsState, initialTestResultsState } from '../reducers/test-records.reducer'; import { - isTestTypeKeySame, - selectAllTestResults, - selectAmendedDefectData, - selectDefectData, - selectTestResultIds, - selectTestResultsEntities, - selectTestResultsTotal, - selectedAmendedTestResultState, - selectedTestResultState, - selectedTestSortedAmendmentHistory, - testResultLoadingState, - isTestTypeOldIvaOrMsva, + isTestTypeKeySame, + isTestTypeOldIvaOrMsva, + selectAllTestResults, + selectAmendedDefectData, + selectDefectData, + selectTestResultIds, + selectTestResultsEntities, + selectTestResultsTotal, + selectedAmendedTestResultState, + selectedTestResultState, + selectedTestSortedAmendmentHistory, + testResultLoadingState, } from './test-records.selectors'; describe('Test Results Selectors', () => { - describe('adapter selectors', () => { - it('should return correct state', () => { - const state = { ...initialTestResultsState, ids: ['1'], entities: { 1: { preparerId: '2' } } } as unknown as TestResultsState; - expect(selectTestResultIds.projector(state)).toEqual(['1']); - expect(selectTestResultsEntities.projector(state)).toEqual({ 1: { preparerId: '2' } }); - expect(selectAllTestResults.projector(state)).toEqual([{ preparerId: '2' }]); - expect(selectTestResultsTotal.projector(state)).toBe(1); - }); - }); - - describe('selectedTestResultState', () => { - it('should return the correct test result', () => { - const state: TestResultsState = { - ...initialTestResultsState, - ids: ['testResult1'], - entities: { - testResult1: createMockTestResult({ - testResultId: 'testResult1', - testTypes: [createMockTestType({ testNumber: '1' })], - }), - }, - }; - - const selectedState = selectedTestResultState.projector(state.entities, { testResultId: 'testResult1', testNumber: '1' } as Params); - expect(selectedState).toEqual(state.entities['testResult1']); - }); - }); - - describe('testResultLoadingState', () => { - it('should return loading state', () => { - const state: TestResultsState = { ...initialTestResultsState, loading: true }; - const selectedState = testResultLoadingState.projector(state); - expect(selectedState).toBeTruthy(); - }); - }); - - describe('isTestTypeOldIvaOrMsva', () => { - it('should return true if all custom defects have a reference number', () => { - const state: TestResultModel = mockTestResult(); - state.testTypes[0].customDefects = [createMockCustomDefect()]; - const isOldIvaOrMsva = isTestTypeOldIvaOrMsva.projector(state); - expect(isOldIvaOrMsva).toBe(true); - }); - - it('should return false if all custom defects do not have a reference number', () => { - const state: TestResultModel = mockTestResult(); - state.testTypes[0].customDefects = [createMockAdditionalDefect()]; - const isOldIvaOrMsva = isTestTypeOldIvaOrMsva.projector(state); - expect(isOldIvaOrMsva).toBe(false); - }); - - it('should return false if no custom defects are present', () => { - const state: TestResultModel = mockTestResult(); - state.testTypes[0].customDefects = []; - const isOldIvaOrMsva = isTestTypeOldIvaOrMsva.projector(state); - expect(isOldIvaOrMsva).toBe(false); - }); - }); - - describe('selectDefectData', () => { - const state: TestResultModel = mockTestResult(); - state.testTypes[0].defects = [mockDefect()]; - - it('should return defect data for the first testType in selected test result', () => { - const defectState = selectDefectData.projector(state); - expect(defectState?.length).toBe(1); - expect(defectState).toEqual(state.testTypes[0].defects); - }); - - it('should return an empty array if there are no defects', () => { - const noDefectState = { ...state, testTypes: [{ ...state.testTypes[0], defects: undefined }] }; - const defectState = selectDefectData.projector(noDefectState); - expect(defectState?.length).toBe(0); - }); - - it('should return an empty array if there are no test types', () => { - const noTestTypeState = { ...state, testTypes: undefined } as unknown as TestResultModel; - const testTypeState = selectDefectData.projector(noTestTypeState); - expect(testTypeState?.length).toBe(0); - }); - - it('should return an empty array if there are no test results', () => { - const noTestResultState = undefined; - const testResultState = selectDefectData.projector(noTestResultState); - expect(testResultState?.length).toBe(0); - }); - }); - - describe('selectSortedTestAmendmentHistory', () => { - let mock: TestResultModel; - - beforeEach(() => { - const date = new Date('2022-01-02'); - mock = mockTestResult(); - mock.testHistory = mock.testHistory?.concat(...mock.testHistory) ?? []; - mock.testHistory[0].createdAt = undefined; - mock.testHistory[1].createdAt = date.toISOString(); - mock.testHistory[2].createdAt = date.toISOString(); - mock.testHistory[3].createdAt = undefined; - mock.testHistory[4].createdAt = new Date(date.setDate(date.getDate() - 1)).toISOString(); - mock.testHistory[5].createdAt = new Date(date.setDate(date.getDate() + 1)).toISOString(); - mock.testHistory[6].createdAt = new Date(date.setDate(date.getDate() - 1)).toISOString(); - mock.testHistory[7].createdAt = undefined; - mock.testHistory[8].createdAt = date.toISOString(); - mock.testHistory[9].createdAt = date.toISOString(); - }); - - it('should sort the test history', () => { - // Adding entries with null created at at the end if they exist - const sortedTestHistory = selectedTestSortedAmendmentHistory.projector(mock); - let previous = new Date(sortedTestHistory[0].createdAt as string).getTime(); - const notfound: TestResultModel[] = []; - sortedTestHistory?.forEach((test) => { - if (test.createdAt) { - // eslint-disable-next-line jest/no-conditional-expect - expect(new Date(test.createdAt).getTime()).toBeLessThanOrEqual(previous); - previous = new Date(test.createdAt).getTime(); - } else { - notfound.push(test); - } - }); - if (notfound.length > 0) { - // eslint-disable-next-line jest/no-conditional-expect - expect(sortedTestHistory?.slice(-notfound.length)).toEqual(notfound); - } - }); - }); - - describe('selectedAmendedTestResultState', () => { - const testResult = createMockTestResult({ - testHistory: new Array(2).fill(0).map((i) => - createMockTestResult({ - createdAt: `2020-01-01T00:0${i}:00.000Z`, - testTypes: [createMockTestType({ testTypeId: `${i}1`, testNumber: 'ABC00' })], - })), - }); - - it('should return amended record that matches "createdAt" route param value', () => { - const selectedState = selectedAmendedTestResultState.projector(testResult, { testNumber: 'ABC00', createdAt: '2020-01-01T00:00:00.000Z' }); - expect(selectedState).toBeDefined(); - expect(testResult.testHistory?.[1].testTypes).toHaveLength(1); - }); - - it('should return return undefined when "createdAt" route param value does not match any amended records', () => { - expect(selectedAmendedTestResultState.projector(testResult, { testTypeId: '00', createdAt: '2020-01-01T00:02:00.000Z' })).toBeUndefined(); - }); - - it('should return return undefined when "testTypeId" route param value does not match any in amended test record', () => { - expect(selectedAmendedTestResultState.projector(testResult, { testTypeId: '01', createdAt: '2020-01-01T00:02:00.000Z' })).toBeUndefined(); - }); - - it('should return return undefined when there is no selected testResult', () => { - expect(selectedAmendedTestResultState.projector(undefined, { createdAt: '2020-01-01T00:01:00.000Z' })).toBeUndefined(); - }); - - it('should return return undefined when testHistory is empty', () => { - expect(selectedAmendedTestResultState.projector({ ...testResult, testHistory: [] }, { createdAt: '2020-01-01T00:01:00.000Z' })).toBeUndefined(); - }); - }); - - describe('selectAmendedDefectData', () => { - const amendedTestResultState = createMockTestResult({ - testTypes: [ - createMockTestType({ - defects: [mockDefect(1)], - }), - ], - }); - - it('should return defect array from first testType in testResult', () => { - const selectedState = selectAmendedDefectData.projector(amendedTestResultState); - expect(selectedState?.length).toBe(1); - expect(selectedState).toEqual(amendedTestResultState.testTypes[0].defects); - }); - - it('should return empty array if testResult is undefined', () => { - expect(selectAmendedDefectData.projector(undefined)).toEqual([]); - }); - - it('should return empty array if testTypes is empty', () => { - expect(selectAmendedDefectData.projector({ testTypes: [] } as unknown as TestResultModel)).toEqual([]); - }); - }); - - describe('isTestTypeKeySame', () => { - it('should return false if the property is different', () => { - const amendTestResult = { - testTypes: [ - { - testNumber: 'foo', - testTypeId: '1', - }, - ], - } as TestResultModel; - const oldTestResult = { - testTypes: [ - { - testNumber: 'foo', - testTypeId: '2', - }, - ], - } as TestResultModel; - const state = isTestTypeKeySame('testTypeId').projector(amendTestResult, oldTestResult); - expect(state).toBe(false); - }); - - it('should return true if the property is the same', () => { - const amendTestResult = { - testTypes: [ - { - testNumber: 'foo', - testTypeId: '1', - }, - ], - } as TestResultModel; - const oldTestResult = { - testTypes: [ - { - testNumber: 'foo', - testTypeId: '1', - }, - ], - } as TestResultModel; - const state = isTestTypeKeySame('testTypeId').projector(amendTestResult, oldTestResult); - expect(state).toBe(true); - }); - }); + describe('adapter selectors', () => { + it('should return correct state', () => { + const state = { + ...initialTestResultsState, + ids: ['1'], + entities: { 1: { preparerId: '2' } }, + } as unknown as TestResultsState; + expect(selectTestResultIds.projector(state)).toEqual(['1']); + expect(selectTestResultsEntities.projector(state)).toEqual({ 1: { preparerId: '2' } }); + expect(selectAllTestResults.projector(state)).toEqual([{ preparerId: '2' }]); + expect(selectTestResultsTotal.projector(state)).toBe(1); + }); + }); + + describe('selectedTestResultState', () => { + it('should return the correct test result', () => { + const state: TestResultsState = { + ...initialTestResultsState, + ids: ['testResult1'], + entities: { + testResult1: createMockTestResult({ + testResultId: 'testResult1', + testTypes: [createMockTestType({ testNumber: '1' })], + }), + }, + }; + + const selectedState = selectedTestResultState.projector(state.entities, { + testResultId: 'testResult1', + testNumber: '1', + } as Params); + expect(selectedState).toEqual(state.entities['testResult1']); + }); + }); + + describe('testResultLoadingState', () => { + it('should return loading state', () => { + const state: TestResultsState = { ...initialTestResultsState, loading: true }; + const selectedState = testResultLoadingState.projector(state); + expect(selectedState).toBeTruthy(); + }); + }); + + describe('isTestTypeOldIvaOrMsva', () => { + it('should return true if all custom defects have a reference number', () => { + const state: TestResultModel = mockTestResult(); + state.testTypes[0].customDefects = [createMockCustomDefect()]; + const isOldIvaOrMsva = isTestTypeOldIvaOrMsva.projector(state); + expect(isOldIvaOrMsva).toBe(true); + }); + + it('should return false if all custom defects do not have a reference number', () => { + const state: TestResultModel = mockTestResult(); + state.testTypes[0].customDefects = [createMockAdditionalDefect()]; + const isOldIvaOrMsva = isTestTypeOldIvaOrMsva.projector(state); + expect(isOldIvaOrMsva).toBe(false); + }); + + it('should return false if no custom defects are present', () => { + const state: TestResultModel = mockTestResult(); + state.testTypes[0].customDefects = []; + const isOldIvaOrMsva = isTestTypeOldIvaOrMsva.projector(state); + expect(isOldIvaOrMsva).toBe(false); + }); + }); + + describe('selectDefectData', () => { + const state: TestResultModel = mockTestResult(); + state.testTypes[0].defects = [mockDefect()]; + + it('should return defect data for the first testType in selected test result', () => { + const defectState = selectDefectData.projector(state); + expect(defectState?.length).toBe(1); + expect(defectState).toEqual(state.testTypes[0].defects); + }); + + it('should return an empty array if there are no defects', () => { + const noDefectState = { ...state, testTypes: [{ ...state.testTypes[0], defects: undefined }] }; + const defectState = selectDefectData.projector(noDefectState); + expect(defectState?.length).toBe(0); + }); + + it('should return an empty array if there are no test types', () => { + const noTestTypeState = { ...state, testTypes: undefined } as unknown as TestResultModel; + const testTypeState = selectDefectData.projector(noTestTypeState); + expect(testTypeState?.length).toBe(0); + }); + + it('should return an empty array if there are no test results', () => { + const noTestResultState = undefined; + const testResultState = selectDefectData.projector(noTestResultState); + expect(testResultState?.length).toBe(0); + }); + }); + + describe('selectSortedTestAmendmentHistory', () => { + let mock: TestResultModel; + + beforeEach(() => { + const date = new Date('2022-01-02'); + mock = mockTestResult(); + mock.testHistory = mock.testHistory?.concat(...mock.testHistory) ?? []; + mock.testHistory[0].createdAt = undefined; + mock.testHistory[1].createdAt = date.toISOString(); + mock.testHistory[2].createdAt = date.toISOString(); + mock.testHistory[3].createdAt = undefined; + mock.testHistory[4].createdAt = new Date(date.setDate(date.getDate() - 1)).toISOString(); + mock.testHistory[5].createdAt = new Date(date.setDate(date.getDate() + 1)).toISOString(); + mock.testHistory[6].createdAt = new Date(date.setDate(date.getDate() - 1)).toISOString(); + mock.testHistory[7].createdAt = undefined; + mock.testHistory[8].createdAt = date.toISOString(); + mock.testHistory[9].createdAt = date.toISOString(); + }); + + it('should sort the test history', () => { + // Adding entries with null created at at the end if they exist + const sortedTestHistory = selectedTestSortedAmendmentHistory.projector(mock); + let previous = new Date(sortedTestHistory[0].createdAt as string).getTime(); + const notfound: TestResultModel[] = []; + sortedTestHistory?.forEach((test) => { + if (test.createdAt) { + // eslint-disable-next-line jest/no-conditional-expect + expect(new Date(test.createdAt).getTime()).toBeLessThanOrEqual(previous); + previous = new Date(test.createdAt).getTime(); + } else { + notfound.push(test); + } + }); + if (notfound.length > 0) { + // eslint-disable-next-line jest/no-conditional-expect + expect(sortedTestHistory?.slice(-notfound.length)).toEqual(notfound); + } + }); + }); + + describe('selectedAmendedTestResultState', () => { + const testResult = createMockTestResult({ + testHistory: new Array(2).fill(0).map((i) => + createMockTestResult({ + createdAt: `2020-01-01T00:0${i}:00.000Z`, + testTypes: [createMockTestType({ testTypeId: `${i}1`, testNumber: 'ABC00' })], + }) + ), + }); + + it('should return amended record that matches "createdAt" route param value', () => { + const selectedState = selectedAmendedTestResultState.projector(testResult, { + testNumber: 'ABC00', + createdAt: '2020-01-01T00:00:00.000Z', + }); + expect(selectedState).toBeDefined(); + expect(testResult.testHistory?.[1].testTypes).toHaveLength(1); + }); + + it('should return return undefined when "createdAt" route param value does not match any amended records', () => { + expect( + selectedAmendedTestResultState.projector(testResult, { + testTypeId: '00', + createdAt: '2020-01-01T00:02:00.000Z', + }) + ).toBeUndefined(); + }); + + it('should return return undefined when "testTypeId" route param value does not match any in amended test record', () => { + expect( + selectedAmendedTestResultState.projector(testResult, { + testTypeId: '01', + createdAt: '2020-01-01T00:02:00.000Z', + }) + ).toBeUndefined(); + }); + + it('should return return undefined when there is no selected testResult', () => { + expect( + selectedAmendedTestResultState.projector(undefined, { createdAt: '2020-01-01T00:01:00.000Z' }) + ).toBeUndefined(); + }); + + it('should return return undefined when testHistory is empty', () => { + expect( + selectedAmendedTestResultState.projector( + { ...testResult, testHistory: [] }, + { createdAt: '2020-01-01T00:01:00.000Z' } + ) + ).toBeUndefined(); + }); + }); + + describe('selectAmendedDefectData', () => { + const amendedTestResultState = createMockTestResult({ + testTypes: [ + createMockTestType({ + defects: [mockDefect(1)], + }), + ], + }); + + it('should return defect array from first testType in testResult', () => { + const selectedState = selectAmendedDefectData.projector(amendedTestResultState); + expect(selectedState?.length).toBe(1); + expect(selectedState).toEqual(amendedTestResultState.testTypes[0].defects); + }); + + it('should return empty array if testResult is undefined', () => { + expect(selectAmendedDefectData.projector(undefined)).toEqual([]); + }); + + it('should return empty array if testTypes is empty', () => { + expect(selectAmendedDefectData.projector({ testTypes: [] } as unknown as TestResultModel)).toEqual([]); + }); + }); + + describe('isTestTypeKeySame', () => { + it('should return false if the property is different', () => { + const amendTestResult = { + testTypes: [ + { + testNumber: 'foo', + testTypeId: '1', + }, + ], + } as TestResultModel; + const oldTestResult = { + testTypes: [ + { + testNumber: 'foo', + testTypeId: '2', + }, + ], + } as TestResultModel; + const state = isTestTypeKeySame('testTypeId').projector(amendTestResult, oldTestResult); + expect(state).toBe(false); + }); + + it('should return true if the property is the same', () => { + const amendTestResult = { + testTypes: [ + { + testNumber: 'foo', + testTypeId: '1', + }, + ], + } as TestResultModel; + const oldTestResult = { + testTypes: [ + { + testNumber: 'foo', + testTypeId: '1', + }, + ], + } as TestResultModel; + const state = isTestTypeKeySame('testTypeId').projector(amendTestResult, oldTestResult); + expect(state).toBe(true); + }); + }); }); diff --git a/src/app/store/test-records/selectors/test-records.selectors.ts b/src/app/store/test-records/selectors/test-records.selectors.ts index da738f88f0..c139a6c876 100644 --- a/src/app/store/test-records/selectors/test-records.selectors.ts +++ b/src/app/store/test-records/selectors/test-records.selectors.ts @@ -6,9 +6,7 @@ import { selectRouteNestedParams } from '@store/router/selectors/router.selector // eslint-disable-next-line import/no-cycle import { testResultAdapter, testResultsFeatureState } from '../reducers/test-records.reducer'; -const { - selectIds, selectEntities, selectAll, selectTotal, -} = testResultAdapter.getSelectors(); +const { selectIds, selectEntities, selectAll, selectTotal } = testResultAdapter.getSelectors(); // select the array of ids export const selectTestResultIds = createSelector(testResultsFeatureState, (state) => selectIds(state)); @@ -19,85 +17,101 @@ export const selectTestResultsEntities = createSelector(testResultsFeatureState, // select the array of tests result export const selectAllTestResults = createSelector(testResultsFeatureState, (state) => selectAll(state)); +// select the array of tests results in order of createdAt (most recent to oldest) +export const selectAllTestResultsInDateOrder = createSelector(selectAllTestResults, (testResults) => + testResults.sort(byDate) +); + // select the total test results count export const selectTestResultsTotal = createSelector(testResultsFeatureState, (state) => selectTotal(state)); export const selectedTestResultState = createSelector( - selectTestResultsEntities, - selectRouteNestedParams, - (entities, { testResultId, testNumber }) => { - // eslint-disable-next-line security/detect-object-injection - const testResult = entities[testResultId]; - if (!testResult) { - return undefined; - } - - const testTypeFound = testResult.testTypes.find((testType) => testType.testNumber === testNumber); - - if (!testTypeFound) { - return undefined; - } - - return { ...testResult, testTypes: [testTypeFound] } as TestResultModel; - }, + selectTestResultsEntities, + selectRouteNestedParams, + (entities, { testResultId, testNumber }) => { + // eslint-disable-next-line security/detect-object-injection + const testResult = entities[testResultId]; + if (!testResult) { + return undefined; + } + + const testTypeFound = testResult.testTypes.find((testType) => testType.testNumber === testNumber); + + if (!testTypeFound) { + return undefined; + } + + return { ...testResult, testTypes: [testTypeFound] } as TestResultModel; + } ); export const testResultInEdit = createSelector(testResultsFeatureState, (state) => state.editingTestResult); -export const toEditOrNotToEdit = createSelector(testResultInEdit, selectedTestResultState, (editingTestResult, selectedTestResult) => { - return editingTestResult || selectedTestResult; -}); +export const toEditOrNotToEdit = createSelector( + testResultInEdit, + selectedTestResultState, + (editingTestResult, selectedTestResult) => { + return editingTestResult || selectedTestResult; + } +); export const testResultLoadingState = createSelector(testResultsFeatureState, (state) => state.loading); -export const selectDefectData = createSelector(selectedTestResultState, (testResult) => getDefectFromTestResult(testResult)); +export const selectDefectData = createSelector(selectedTestResultState, (testResult) => + getDefectFromTestResult(testResult) +); export const isTestTypeOldIvaOrMsva = createSelector(toEditOrNotToEdit, (testResult) => { - return !!testResult?.testTypes[0]?.customDefects?.length - && !!testResult?.testTypes[0]?.customDefects?.every((defect) => !!defect.referenceNumber); + return ( + !!testResult?.testTypes[0]?.customDefects?.length && + !!testResult?.testTypes[0]?.customDefects?.every((defect) => !!defect.referenceNumber) + ); }); export const selectedTestSortedAmendmentHistory = createSelector(selectedTestResultState, (testResult) => { - if (!testResult || !testResult.testHistory) { - return []; - } + if (!testResult || !testResult.testHistory) { + return []; + } - const testHistory = [...testResult.testHistory]; - return testHistory.sort(byDate); + const testHistory = [...testResult.testHistory]; + return testHistory.sort(byDate); }); export const selectedAmendedTestResultState = createSelector( - selectedTestResultState, - selectRouteNestedParams, - (testRecord, { testNumber, createdAt }) => { - const amendedTest = testRecord?.testHistory?.find((testResult) => testResult.createdAt === createdAt); + selectedTestResultState, + selectRouteNestedParams, + (testRecord, { testNumber, createdAt }) => { + const amendedTest = testRecord?.testHistory?.find((testResult) => testResult.createdAt === createdAt); - if (!amendedTest) { - return undefined; - } + if (!amendedTest) { + return undefined; + } - const testTypeFound = amendedTest.testTypes.find((testType) => testType.testNumber === testNumber); + const testTypeFound = amendedTest.testTypes.find((testType) => testType.testNumber === testNumber); - if (!testTypeFound) { - return undefined; - } + if (!testTypeFound) { + return undefined; + } - return { ...amendedTest, testTypes: [testTypeFound] }; - }, + return { ...amendedTest, testTypes: [testTypeFound] }; + } ); export const selectAmendedDefectData = createSelector(selectedAmendedTestResultState, (amendedTestResult) => { - return getDefectFromTestResult(amendedTestResult); + return getDefectFromTestResult(amendedTestResult); }); export const sectionTemplates = createSelector(testResultsFeatureState, (state) => state.sectionTemplates); -export const resultOfTestSelector = createSelector(toEditOrNotToEdit, (testRecord) => testRecord?.testTypes[0].testResult); +export const resultOfTestSelector = createSelector( + toEditOrNotToEdit, + (testRecord) => testRecord?.testTypes[0].testResult +); export const isTestTypeKeySame = (key: keyof TestType) => - createSelector(selectedAmendedTestResultState, selectedTestResultState, (testRecord, amendedTestRecord) => { - return testRecord?.testTypes[0][`${key}`] === amendedTestRecord?.testTypes[0][`${key}`]; - }); + createSelector(selectedAmendedTestResultState, selectedTestResultState, (testRecord, amendedTestRecord) => { + return testRecord?.testTypes[0][`${key}`] === amendedTestRecord?.testTypes[0][`${key}`]; + }); // Common Functions /** @@ -105,22 +119,22 @@ export const isTestTypeKeySame = (key: keyof TestType) => * TODO: When we have better routing set up, we need to revisit this so that the testType is also selected based on route paramerets/queries. */ function getDefectFromTestResult(testResult: TestResultModel | undefined): TestResultDefects { - return (testResult?.testTypes && testResult.testTypes.length > 0 && testResult.testTypes[0].defects) || []; + return (testResult?.testTypes && testResult.testTypes.length > 0 && testResult.testTypes[0].defects) || []; } function byDate(a: TestResultModel, b: TestResultModel): -1 | 0 | 1 { - if (a === b) { - // equal items sort equally - return 0; - } - - if (!a.createdAt) { - // nulls sort after anything else - return 1; - } - if (!b.createdAt) { - return -1; - } - - return new Date(a.createdAt).getTime() > new Date(b.createdAt).getTime() ? -1 : 1; + if (a === b) { + // equal items sort equally + return 0; + } + + if (!a.createdAt) { + // nulls sort after anything else + return 1; + } + if (!b.createdAt) { + return -1; + } + + return new Date(a.createdAt).getTime() > new Date(b.createdAt).getTime() ? -1 : 1; } diff --git a/src/app/store/test-records/test-records.module.ts b/src/app/store/test-records/test-records.module.ts index 73a9cbfa51..6aacbc2f31 100644 --- a/src/app/store/test-records/test-records.module.ts +++ b/src/app/store/test-records/test-records.module.ts @@ -6,7 +6,11 @@ import { TestResultsEffects } from './effects/test-records.effects'; import { STORE_FEATURE_TEST_RESULTS_KEY, testResultsReducer } from './reducers/test-records.reducer'; @NgModule({ - declarations: [], - imports: [CommonModule, StoreModule.forFeature(STORE_FEATURE_TEST_RESULTS_KEY, testResultsReducer), EffectsModule.forFeature([TestResultsEffects])], + declarations: [], + imports: [ + CommonModule, + StoreModule.forFeature(STORE_FEATURE_TEST_RESULTS_KEY, testResultsReducer), + EffectsModule.forFeature([TestResultsEffects]), + ], }) export class TestRecordsStateModule {} diff --git a/src/app/store/test-stations/actions/test-stations.actions.spec.ts b/src/app/store/test-stations/actions/test-stations.actions.spec.ts index ab9780f7ab..a7cb4a2793 100644 --- a/src/app/store/test-stations/actions/test-stations.actions.spec.ts +++ b/src/app/store/test-stations/actions/test-stations.actions.spec.ts @@ -1,19 +1,19 @@ import { - fetchTestStation, - fetchTestStationFailed, - fetchTestStations, - fetchTestStationsFailed, - fetchTestStationsSuccess, - fetchTestStationSuccess, + fetchTestStation, + fetchTestStationFailed, + fetchTestStationSuccess, + fetchTestStations, + fetchTestStationsFailed, + fetchTestStationsSuccess, } from './test-stations.actions'; describe('Test Stations Actions', () => { - it('should return correct types', () => { - expect(fetchTestStations.type).toBe('[API/test-stations] Fetch Test Stations'); - expect(fetchTestStationsSuccess.type).toBe('[API/test-stations] Fetch Test Stations Success'); - expect(fetchTestStationsFailed.type).toBe('[API/test-stations] Fetch Test Stations Failed'); - expect(fetchTestStation.type).toBe('[API/test-stations] Fetch Test Station by ID'); - expect(fetchTestStationSuccess.type).toBe('[API/test-stations] Fetch Test Station by ID Success'); - expect(fetchTestStationFailed.type).toBe('[API/test-stations] Fetch Test Station by ID Failed'); - }); + it('should return correct types', () => { + expect(fetchTestStations.type).toBe('[API/test-stations] Fetch Test Stations'); + expect(fetchTestStationsSuccess.type).toBe('[API/test-stations] Fetch Test Stations Success'); + expect(fetchTestStationsFailed.type).toBe('[API/test-stations] Fetch Test Stations Failed'); + expect(fetchTestStation.type).toBe('[API/test-stations] Fetch Test Station by ID'); + expect(fetchTestStationSuccess.type).toBe('[API/test-stations] Fetch Test Station by ID Success'); + expect(fetchTestStationFailed.type).toBe('[API/test-stations] Fetch Test Station by ID Failed'); + }); }); diff --git a/src/app/store/test-stations/actions/test-stations.actions.ts b/src/app/store/test-stations/actions/test-stations.actions.ts index 1c0b1b5d0a..42d6128c83 100644 --- a/src/app/store/test-stations/actions/test-stations.actions.ts +++ b/src/app/store/test-stations/actions/test-stations.actions.ts @@ -7,11 +7,14 @@ export const fetchTestStationsSuccess = createAction(getTitle(true, 'Success'), export const fetchTestStationsFailed = createAction(getTitle(true, 'Failed'), props()); export const fetchTestStation = createAction(getTitle(), props<{ id: string }>()); -export const fetchTestStationSuccess = createAction(getTitle(false, 'Success'), props<{ id: string; payload: TestStation }>()); +export const fetchTestStationSuccess = createAction( + getTitle(false, 'Success'), + props<{ id: string; payload: TestStation }>() +); export const fetchTestStationFailed = createAction(getTitle(false, 'Failed'), props()); function getTitle(isPlural = false, suffix = ''): string { - const plural = isPlural ? 's' : ' by ID'; - suffix = suffix ? ` ${suffix}` : suffix; - return `[API/test-stations] Fetch Test Station${plural}${suffix}`; + const plural = isPlural ? 's' : ' by ID'; + suffix = suffix ? ` ${suffix}` : suffix; + return `[API/test-stations] Fetch Test Station${plural}${suffix}`; } diff --git a/src/app/store/test-stations/effects/test-stations.effects.spec.ts b/src/app/store/test-stations/effects/test-stations.effects.spec.ts index 4cd21ad512..70c120781c 100644 --- a/src/app/store/test-stations/effects/test-stations.effects.spec.ts +++ b/src/app/store/test-stations/effects/test-stations.effects.spec.ts @@ -9,116 +9,118 @@ import { initialAppState } from '@store/.'; import { Observable } from 'rxjs'; import { TestScheduler } from 'rxjs/testing'; import { - fetchTestStation, - fetchTestStationFailed, - fetchTestStations, - fetchTestStationsFailed, - fetchTestStationsSuccess, - fetchTestStationSuccess, + fetchTestStation, + fetchTestStationFailed, + fetchTestStationSuccess, + fetchTestStations, + fetchTestStationsFailed, + fetchTestStationsSuccess, } from '../actions/test-stations.actions'; import { TestStationsEffects } from './test-stations.effects'; describe('TestStationsEffects', () => { - let effects: TestStationsEffects; - let actions$ = new Observable(); - let testScheduler: TestScheduler; - let service: TestStationsService; - - const expectedResult = { testStationId: 'some ID' } as TestStation; - const testCases = [ - { - id: expectedResult.testStationId, - payload: [expectedResult], - }, - ]; - - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [HttpClientTestingModule], - providers: [ - TestStationsEffects, - provideMockActions(() => actions$), - TestStationsService, - provideMockStore({ - initialState: initialAppState, - }), - ], - }); - - effects = TestBed.inject(TestStationsEffects); - service = TestBed.inject(TestStationsService); - }); - - beforeEach(() => { - testScheduler = new TestScheduler((actual, expected) => { - expect(actual).toEqual(expected); - }); - }); - - describe('fetchTestStations$', () => { - it.each(testCases)('should return fetchTestStationsSuccess action on successfull API call', (value) => { - testScheduler.run(({ hot, cold, expectObservable }) => { - const { payload } = value; - - // mock action to trigger effect - actions$ = hot('-a--', { a: fetchTestStations() }); - - // mock service call - jest.spyOn(service, 'fetchTestStations').mockReturnValue(cold('--a|', { a: payload })); - - // expect effect to return success action - expectObservable(effects.fetchTestStations$).toBe('---b', { - b: fetchTestStationsSuccess({ payload }), - }); - }); - }); - - it.each(testCases)('should return fetchTestStationsFailed action on API error', () => { - testScheduler.run(({ hot, cold, expectObservable }) => { - actions$ = hot('-a--', { a: fetchTestStations() }); - - const expectedError = new Error('Reference data resourceType is required'); - - jest.spyOn(service, 'fetchTestStations').mockReturnValue(cold('--#|', {}, expectedError)); - - expectObservable(effects.fetchTestStations$).toBe('---b', { - b: fetchTestStationsFailed({ error: 'Reference data resourceType is required' }), - }); - }); - }); - }); - - describe('fetchTestStation$', () => { - it.each(testCases)('should return fetchTestStationSuccess action on successfull API call', (value) => { - testScheduler.run(({ hot, cold, expectObservable }) => { - const { id, payload } = value; - - const entity = payload.find((p) => p.testStationId === id) as TestStation; - - // mock action to trigger effect - actions$ = hot('-a--', { a: fetchTestStation({ id }) }); - - // mock service call - jest.spyOn(service, 'fetchTestStation').mockReturnValue(cold('--a|', { a: entity })); - - // expect effect to return success action - expectObservable(effects.fetchTestStation$).toBe('---b', { - b: fetchTestStationSuccess({ id, payload: entity }), - }); - }); - }); - - it.each(testCases)('should return fetchTestStationFailed action on API error', (value) => { - testScheduler.run(({ hot, cold, expectObservable }) => { - const { id } = value; - actions$ = hot('-a--', { a: fetchTestStation({ id }) }); - - const expectedError = new Error('Reference data resourceKey is required'); - - jest.spyOn(service, 'fetchTestStation').mockReturnValue(cold('--#|', {}, expectedError)); - - expectObservable(effects.fetchTestStation$).toBe('---b', { b: fetchTestStationFailed({ error: 'Reference data resourceKey is required' }) }); - }); - }); - }); + let effects: TestStationsEffects; + let actions$ = new Observable(); + let testScheduler: TestScheduler; + let service: TestStationsService; + + const expectedResult = { testStationId: 'some ID' } as TestStation; + const testCases = [ + { + id: expectedResult.testStationId, + payload: [expectedResult], + }, + ]; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [HttpClientTestingModule], + providers: [ + TestStationsEffects, + provideMockActions(() => actions$), + TestStationsService, + provideMockStore({ + initialState: initialAppState, + }), + ], + }); + + effects = TestBed.inject(TestStationsEffects); + service = TestBed.inject(TestStationsService); + }); + + beforeEach(() => { + testScheduler = new TestScheduler((actual, expected) => { + expect(actual).toEqual(expected); + }); + }); + + describe('fetchTestStations$', () => { + it.each(testCases)('should return fetchTestStationsSuccess action on successfull API call', (value) => { + testScheduler.run(({ hot, cold, expectObservable }) => { + const { payload } = value; + + // mock action to trigger effect + actions$ = hot('-a--', { a: fetchTestStations() }); + + // mock service call + jest.spyOn(service, 'fetchTestStations').mockReturnValue(cold('--a|', { a: payload })); + + // expect effect to return success action + expectObservable(effects.fetchTestStations$).toBe('---b', { + b: fetchTestStationsSuccess({ payload }), + }); + }); + }); + + it.each(testCases)('should return fetchTestStationsFailed action on API error', () => { + testScheduler.run(({ hot, cold, expectObservable }) => { + actions$ = hot('-a--', { a: fetchTestStations() }); + + const expectedError = new Error('Reference data resourceType is required'); + + jest.spyOn(service, 'fetchTestStations').mockReturnValue(cold('--#|', {}, expectedError)); + + expectObservable(effects.fetchTestStations$).toBe('---b', { + b: fetchTestStationsFailed({ error: 'Reference data resourceType is required' }), + }); + }); + }); + }); + + describe('fetchTestStation$', () => { + it.each(testCases)('should return fetchTestStationSuccess action on successfull API call', (value) => { + testScheduler.run(({ hot, cold, expectObservable }) => { + const { id, payload } = value; + + const entity = payload.find((p) => p.testStationId === id) as TestStation; + + // mock action to trigger effect + actions$ = hot('-a--', { a: fetchTestStation({ id }) }); + + // mock service call + jest.spyOn(service, 'fetchTestStation').mockReturnValue(cold('--a|', { a: entity })); + + // expect effect to return success action + expectObservable(effects.fetchTestStation$).toBe('---b', { + b: fetchTestStationSuccess({ id, payload: entity }), + }); + }); + }); + + it.each(testCases)('should return fetchTestStationFailed action on API error', (value) => { + testScheduler.run(({ hot, cold, expectObservable }) => { + const { id } = value; + actions$ = hot('-a--', { a: fetchTestStation({ id }) }); + + const expectedError = new Error('Reference data resourceKey is required'); + + jest.spyOn(service, 'fetchTestStation').mockReturnValue(cold('--#|', {}, expectedError)); + + expectObservable(effects.fetchTestStation$).toBe('---b', { + b: fetchTestStationFailed({ error: 'Reference data resourceKey is required' }), + }); + }); + }); + }); }); diff --git a/src/app/store/test-stations/effects/test-stations.effects.ts b/src/app/store/test-stations/effects/test-stations.effects.ts index 3f52fcb049..9a7fdf4f51 100644 --- a/src/app/store/test-stations/effects/test-stations.effects.ts +++ b/src/app/store/test-stations/effects/test-stations.effects.ts @@ -1,39 +1,42 @@ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { Actions, createEffect, ofType } from '@ngrx/effects'; import { TestStationsService } from '@services/test-stations/test-stations.service'; +import { catchError, map, mergeMap, of } from 'rxjs'; import { - catchError, map, mergeMap, of, -} from 'rxjs'; -import { - fetchTestStation, - fetchTestStationFailed, - fetchTestStations, - fetchTestStationsFailed, - fetchTestStationsSuccess, - fetchTestStationSuccess, + fetchTestStation, + fetchTestStationFailed, + fetchTestStationSuccess, + fetchTestStations, + fetchTestStationsFailed, + fetchTestStationsSuccess, } from '../actions/test-stations.actions'; @Injectable() export class TestStationsEffects { - fetchTestStations$ = createEffect(() => - this.actions$.pipe( - ofType(fetchTestStations), - mergeMap(() => - this.testStationsService.fetchTestStations().pipe( - map((testStations) => fetchTestStationsSuccess({ payload: testStations })), - catchError((e) => of(fetchTestStationsFailed({ error: e.message }))), - )), - )); + private actions$ = inject(Actions); + private testStationsService = inject(TestStationsService); - fetchTestStation$ = createEffect(() => - this.actions$.pipe( - ofType(fetchTestStation), - mergeMap(({ id }) => - this.testStationsService.fetchTestStation(id).pipe( - map((testStation) => fetchTestStationSuccess({ id, payload: testStation })), - catchError((e) => of(fetchTestStationFailed({ error: e.message }))), - )), - )); + fetchTestStations$ = createEffect(() => + this.actions$.pipe( + ofType(fetchTestStations), + mergeMap(() => + this.testStationsService.fetchTestStations().pipe( + map((testStations) => fetchTestStationsSuccess({ payload: testStations })), + catchError((e) => of(fetchTestStationsFailed({ error: e.message }))) + ) + ) + ) + ); - constructor(private actions$: Actions, private testStationsService: TestStationsService) { } + fetchTestStation$ = createEffect(() => + this.actions$.pipe( + ofType(fetchTestStation), + mergeMap(({ id }) => + this.testStationsService.fetchTestStation(id).pipe( + map((testStation) => fetchTestStationSuccess({ id, payload: testStation })), + catchError((e) => of(fetchTestStationFailed({ error: e.message }))) + ) + ) + ) + ); } diff --git a/src/app/store/test-stations/reducers/test-stations.reducer.spec.ts b/src/app/store/test-stations/reducers/test-stations.reducer.spec.ts index c00f4cebd5..f07b83a390 100644 --- a/src/app/store/test-stations/reducers/test-stations.reducer.spec.ts +++ b/src/app/store/test-stations/reducers/test-stations.reducer.spec.ts @@ -1,99 +1,99 @@ import { TestStation } from '@models/test-stations/test-station.model'; import { - fetchTestStation, - fetchTestStationFailed, - fetchTestStations, - fetchTestStationsFailed, - fetchTestStationsSuccess, - fetchTestStationSuccess, + fetchTestStation, + fetchTestStationFailed, + fetchTestStationSuccess, + fetchTestStations, + fetchTestStationsFailed, + fetchTestStationsSuccess, } from '../actions/test-stations.actions'; -import { initialTestStationsState, testStationsReducer, TestStationsState } from './test-stations.reducer'; +import { TestStationsState, initialTestStationsState, testStationsReducer } from './test-stations.reducer'; describe('Test Stations Reducer', () => { - const expectedTestStations = [{ testStationId: 'someId' } as TestStation]; + const expectedTestStations = [{ testStationId: 'someId' } as TestStation]; - describe('unknown action', () => { - it('should return the default state', () => { - const action = { - type: 'Unknown', - }; - const state = testStationsReducer(initialTestStationsState, action); + describe('unknown action', () => { + it('should return the default state', () => { + const action = { + type: 'Unknown', + }; + const state = testStationsReducer(initialTestStationsState, action); - expect(state).toBe(initialTestStationsState); - }); - }); + expect(state).toBe(initialTestStationsState); + }); + }); - describe('fetchTestStations actions', () => { - it('should set loading to true', () => { - const newState: TestStationsState = { ...initialTestStationsState, loading: true }; - const action = fetchTestStations(); - const state = testStationsReducer(initialTestStationsState, action); + describe('fetchTestStations actions', () => { + it('should set loading to true', () => { + const newState: TestStationsState = { ...initialTestStationsState, loading: true }; + const action = fetchTestStations(); + const state = testStationsReducer(initialTestStationsState, action); - expect(state).toEqual(newState); - expect(state).not.toBe(newState); - }); + expect(state).toEqual(newState); + expect(state).not.toBe(newState); + }); - describe('fetchTestStationsSuccess', () => { - it('should set all test result records', () => { - const newState: TestStationsState = { - ...initialTestStationsState, - ids: ['someId'], - entities: { someId: expectedTestStations[0] }, - }; - const action = fetchTestStationsSuccess({ payload: [...expectedTestStations] }); - const state = testStationsReducer(initialTestStationsState, action); + describe('fetchTestStationsSuccess', () => { + it('should set all test result records', () => { + const newState: TestStationsState = { + ...initialTestStationsState, + ids: ['someId'], + entities: { someId: expectedTestStations[0] }, + }; + const action = fetchTestStationsSuccess({ payload: [...expectedTestStations] }); + const state = testStationsReducer(initialTestStationsState, action); - expect(state).toEqual(newState); - expect(state).not.toBe(newState); - }); + expect(state).toEqual(newState); + expect(state).not.toBe(newState); + }); - describe('fetchTestStationsFailed', () => { - it('should set error state', () => { - const newState = { ...initialTestStationsState, loading: false }; - const action = fetchTestStationsFailed({ error: 'unit testing error message' }); - const state = testStationsReducer({ ...initialTestStationsState, loading: true }, action); + describe('fetchTestStationsFailed', () => { + it('should set error state', () => { + const newState = { ...initialTestStationsState, loading: false }; + const action = fetchTestStationsFailed({ error: 'unit testing error message' }); + const state = testStationsReducer({ ...initialTestStationsState, loading: true }, action); - expect(state).toEqual(newState); - expect(state).not.toBe(newState); - }); - }); - }); - }); + expect(state).toEqual(newState); + expect(state).not.toBe(newState); + }); + }); + }); + }); - describe('fetchTestStationById actions', () => { - it('should set loading to true', () => { - const newState: TestStationsState = { ...initialTestStationsState, loading: true }; - const action = fetchTestStation({ id: 'TestStationId0001' }); - const state = testStationsReducer(initialTestStationsState, action); + describe('fetchTestStationById actions', () => { + it('should set loading to true', () => { + const newState: TestStationsState = { ...initialTestStationsState, loading: true }; + const action = fetchTestStation({ id: 'TestStationId0001' }); + const state = testStationsReducer(initialTestStationsState, action); - expect(state).toEqual(newState); - expect(state).not.toBe(newState); - }); + expect(state).toEqual(newState); + expect(state).not.toBe(newState); + }); - describe('fetchTestStationsByIdSuccess', () => { - it('should set all test result records', () => { - const newState: TestStationsState = { - ...initialTestStationsState, - ids: ['someId'], - entities: { someId: expectedTestStations[0] }, - }; - const action = fetchTestStationSuccess({ id: 'TestStationId0001', payload: expectedTestStations[0] }); - const state = testStationsReducer(initialTestStationsState, action); + describe('fetchTestStationsByIdSuccess', () => { + it('should set all test result records', () => { + const newState: TestStationsState = { + ...initialTestStationsState, + ids: ['someId'], + entities: { someId: expectedTestStations[0] }, + }; + const action = fetchTestStationSuccess({ id: 'TestStationId0001', payload: expectedTestStations[0] }); + const state = testStationsReducer(initialTestStationsState, action); - expect(state).toEqual(newState); - expect(state).not.toBe(newState); - }); - }); + expect(state).toEqual(newState); + expect(state).not.toBe(newState); + }); + }); - describe('fetchTestStationsByIdFailed', () => { - it('should set error state', () => { - const newState = { ...initialTestStationsState, loading: false }; - const action = fetchTestStationFailed({ error: 'unit testing error message' }); - const state = testStationsReducer({ ...initialTestStationsState, loading: true }, action); + describe('fetchTestStationsByIdFailed', () => { + it('should set error state', () => { + const newState = { ...initialTestStationsState, loading: false }; + const action = fetchTestStationFailed({ error: 'unit testing error message' }); + const state = testStationsReducer({ ...initialTestStationsState, loading: true }, action); - expect(state).toEqual(newState); - expect(state).not.toBe(newState); - }); - }); - }); + expect(state).toEqual(newState); + expect(state).not.toBe(newState); + }); + }); + }); }); diff --git a/src/app/store/test-stations/reducers/test-stations.reducer.ts b/src/app/store/test-stations/reducers/test-stations.reducer.ts index b0a20e8b74..79f8bf21d3 100644 --- a/src/app/store/test-stations/reducers/test-stations.reducer.ts +++ b/src/app/store/test-stations/reducers/test-stations.reducer.ts @@ -1,18 +1,18 @@ import { TestStation } from '@models/test-stations/test-station.model'; -import { createEntityAdapter, EntityAdapter, EntityState } from '@ngrx/entity'; +import { EntityAdapter, EntityState, createEntityAdapter } from '@ngrx/entity'; import { createFeatureSelector, createReducer, on } from '@ngrx/store'; import { - fetchTestStation, - fetchTestStationFailed, - fetchTestStations, - fetchTestStationsFailed, - fetchTestStationsSuccess, - fetchTestStationSuccess, + fetchTestStation, + fetchTestStationFailed, + fetchTestStationSuccess, + fetchTestStations, + fetchTestStationsFailed, + fetchTestStationsSuccess, } from '../actions/test-stations.actions'; export interface TestStationsState extends EntityState { - loading: boolean; - error: string; + loading: boolean; + error: string; } export const STORE_FEATURE_TEST_STATIONS_KEY = 'testStations'; @@ -20,19 +20,25 @@ export const STORE_FEATURE_TEST_STATIONS_KEY = 'testStations'; export const testStationsFeatureState = createFeatureSelector(STORE_FEATURE_TEST_STATIONS_KEY); export const testStationsAdapter: EntityAdapter = createEntityAdapter({ - selectId: (testStation) => testStation.testStationId, + selectId: (testStation) => testStation.testStationId, }); export const initialTestStationsState = testStationsAdapter.getInitialState({ loading: false, error: '' }); export const testStationsReducer = createReducer( - initialTestStationsState, - - on(fetchTestStations, (state) => ({ ...state, loading: true })), - on(fetchTestStationsSuccess, (state, action) => ({ ...testStationsAdapter.setAll(action.payload, state), loading: false })), - on(fetchTestStationsFailed, (state) => ({ ...testStationsAdapter.setAll([], state), loading: false })), - - on(fetchTestStation, (state) => ({ ...state, loading: true })), - on(fetchTestStationSuccess, (state, action) => ({ ...testStationsAdapter.upsertOne(action.payload, state), loading: false })), - on(fetchTestStationFailed, (state) => ({ ...testStationsAdapter.setAll([], state), loading: false })), + initialTestStationsState, + + on(fetchTestStations, (state) => ({ ...state, loading: true })), + on(fetchTestStationsSuccess, (state, action) => ({ + ...testStationsAdapter.setAll(action.payload, state), + loading: false, + })), + on(fetchTestStationsFailed, (state) => ({ ...testStationsAdapter.setAll([], state), loading: false })), + + on(fetchTestStation, (state) => ({ ...state, loading: true })), + on(fetchTestStationSuccess, (state, action) => ({ + ...testStationsAdapter.upsertOne(action.payload, state), + loading: false, + })), + on(fetchTestStationFailed, (state) => ({ ...testStationsAdapter.setAll([], state), loading: false })) ); diff --git a/src/app/store/test-stations/selectors/test-stations.selectors.spec.ts b/src/app/store/test-stations/selectors/test-stations.selectors.spec.ts index b61bbbf669..90df98d4bb 100644 --- a/src/app/store/test-stations/selectors/test-stations.selectors.spec.ts +++ b/src/app/store/test-stations/selectors/test-stations.selectors.spec.ts @@ -1,57 +1,68 @@ import { TestStation } from '@models/test-stations/test-station.model'; -import { initialTestStationsState, TestStationsState } from '../reducers/test-stations.reducer'; +import { TestStationsState, initialTestStationsState } from '../reducers/test-stations.reducer'; import { - getTestStationFromProperty, testStation, testStations, testStationsLoadingState, + getTestStationFromProperty, + testStation, + testStations, + testStationsLoadingState, } from './test-stations.selectors'; describe('Test Results Selectors', () => { - describe('adapter selectors', () => { - it('should return correct state', () => { - const state = { ...initialTestStationsState, ids: ['1'], entities: { 1: { preparerId: '2' } } } as unknown as TestStationsState; + describe('adapter selectors', () => { + it('should return correct state', () => { + const state = { + ...initialTestStationsState, + ids: ['1'], + entities: { 1: { preparerId: '2' } }, + } as unknown as TestStationsState; - expect(testStations.projector(state)).toEqual([{ preparerId: '2' }]); - expect(testStation('1').projector(state)).toEqual({ preparerId: '2' }); - }); - }); + expect(testStations.projector(state)).toEqual([{ preparerId: '2' }]); + expect(testStation('1').projector(state)).toEqual({ preparerId: '2' }); + }); + }); - describe('testStationsLoadingState', () => { - it('should return loading state', () => { - const state: TestStationsState = { ...initialTestStationsState, loading: true }; - const selectedState = testStationsLoadingState.projector(state); - expect(selectedState).toBeTruthy(); - }); - }); + describe('testStationsLoadingState', () => { + it('should return loading state', () => { + const state: TestStationsState = { ...initialTestStationsState, loading: true }; + const selectedState = testStationsLoadingState.projector(state); + expect(selectedState).toBeTruthy(); + }); + }); - describe('getTestStationsFromProperty', () => { - it('return a test station with a matching testStationName', () => { - const testStationsArray = [ - { - testStationName: 'foo', - testStationPNumber: '1234', - testStationType: 'gvts', - }, - { - testStationName: 'bar', - testStationPNumber: '356728', - testStationType: 'atf', - }, - ] as TestStation[]; - expect(getTestStationFromProperty('testStationName', 'foo').projector(testStationsArray)).toEqual(testStationsArray[0]); - }); - it('return a test station with a matching PNumber', () => { - const testStationsArray = [ - { - testStationName: 'foo', - testStationPNumber: '1234', - testStationType: 'gvts', - }, - { - testStationName: 'bar', - testStationPNumber: '356728', - testStationType: 'atf', - }, - ] as TestStation[]; - expect(getTestStationFromProperty('testStationPNumber', '356728').projector(testStationsArray)).toEqual(testStationsArray[1]); - }); - }); + describe('getTestStationsFromProperty', () => { + it('return a test station with a matching testStationName', () => { + const testStationsArray = [ + { + testStationName: 'foo', + testStationPNumber: '1234', + testStationType: 'gvts', + }, + { + testStationName: 'bar', + testStationPNumber: '356728', + testStationType: 'atf', + }, + ] as TestStation[]; + expect(getTestStationFromProperty('testStationName', 'foo').projector(testStationsArray)).toEqual( + testStationsArray[0] + ); + }); + it('return a test station with a matching PNumber', () => { + const testStationsArray = [ + { + testStationName: 'foo', + testStationPNumber: '1234', + testStationType: 'gvts', + }, + { + testStationName: 'bar', + testStationPNumber: '356728', + testStationType: 'atf', + }, + ] as TestStation[]; + expect(getTestStationFromProperty('testStationPNumber', '356728').projector(testStationsArray)).toEqual( + testStationsArray[1] + ); + }); + }); }); diff --git a/src/app/store/test-stations/selectors/test-stations.selectors.ts b/src/app/store/test-stations/selectors/test-stations.selectors.ts index 470a02bbff..92853a3958 100644 --- a/src/app/store/test-stations/selectors/test-stations.selectors.ts +++ b/src/app/store/test-stations/selectors/test-stations.selectors.ts @@ -11,4 +11,4 @@ export const testStation = (id: string) => createSelector(testStationsFeatureSta export const testStationsLoadingState = createSelector(testStationsFeatureState, (state) => state.loading); export const getTestStationFromProperty = (property: keyof TestStation, value: string) => - createSelector(testStations, (stations) => stations.find((station) => station[`${property}`] === value)); + createSelector(testStations, (stations) => stations.find((station) => station[`${property}`] === value)); diff --git a/src/app/store/test-stations/test-stations-state.module.ts b/src/app/store/test-stations/test-stations-state.module.ts index 4fcdfe769f..616ce93cf0 100644 --- a/src/app/store/test-stations/test-stations-state.module.ts +++ b/src/app/store/test-stations/test-stations-state.module.ts @@ -6,11 +6,11 @@ import { TestStationsEffects } from './effects/test-stations.effects'; import { STORE_FEATURE_TEST_STATIONS_KEY, testStationsReducer } from './reducers/test-stations.reducer'; @NgModule({ - declarations: [], - imports: [ - CommonModule, - StoreModule.forFeature(STORE_FEATURE_TEST_STATIONS_KEY, testStationsReducer), - EffectsModule.forFeature([TestStationsEffects]), - ], + declarations: [], + imports: [ + CommonModule, + StoreModule.forFeature(STORE_FEATURE_TEST_STATIONS_KEY, testStationsReducer), + EffectsModule.forFeature([TestStationsEffects]), + ], }) export class TestStationsStateModule {} diff --git a/src/app/store/test-types/actions/test-types.actions.spec.ts b/src/app/store/test-types/actions/test-types.actions.spec.ts index 8019be9bca..3173b63028 100644 --- a/src/app/store/test-types/actions/test-types.actions.spec.ts +++ b/src/app/store/test-types/actions/test-types.actions.spec.ts @@ -1,9 +1,9 @@ -import { fetchTestTypes, fetchTestTypesSuccess, fetchTestTypesFailed } from './test-types.actions'; +import { fetchTestTypes, fetchTestTypesFailed, fetchTestTypesSuccess } from './test-types.actions'; describe('Test Result Actions', () => { - it('should return correct types', () => { - expect(fetchTestTypes.type).toBe('[API/test-types-taxonomy] Fetch All'); - expect(fetchTestTypesSuccess.type).toBe('[API/test-types-taxonomy] Fetch All Success'); - expect(fetchTestTypesFailed.type).toBe('[API/test-types-taxonomy] Fetch All Failed'); - }); + it('should return correct types', () => { + expect(fetchTestTypes.type).toBe('[API/test-types-taxonomy] Fetch All'); + expect(fetchTestTypesSuccess.type).toBe('[API/test-types-taxonomy] Fetch All Success'); + expect(fetchTestTypesFailed.type).toBe('[API/test-types-taxonomy] Fetch All Failed'); + }); }); diff --git a/src/app/store/test-types/actions/test-types.actions.ts b/src/app/store/test-types/actions/test-types.actions.ts index 4272b0b640..4c5d74d371 100644 --- a/src/app/store/test-types/actions/test-types.actions.ts +++ b/src/app/store/test-types/actions/test-types.actions.ts @@ -5,5 +5,8 @@ import { createAction, props } from '@ngrx/store'; const prefix = '[API/test-types-taxonomy] '; export const fetchTestTypes = createAction(`${prefix}Fetch All`); -export const fetchTestTypesSuccess = createAction(`${prefix}Fetch All Success`, props<{ payload: TestTypesTaxonomy }>()); +export const fetchTestTypesSuccess = createAction( + `${prefix}Fetch All Success`, + props<{ payload: TestTypesTaxonomy }>() +); export const fetchTestTypesFailed = createAction(`${prefix}Fetch All Failed`, props()); diff --git a/src/app/store/test-types/effects/test-types.effects.spec.ts b/src/app/store/test-types/effects/test-types.effects.spec.ts index 4635505c21..2a197a6e11 100644 --- a/src/app/store/test-types/effects/test-types.effects.spec.ts +++ b/src/app/store/test-types/effects/test-types.effects.spec.ts @@ -16,78 +16,80 @@ import { fetchTestTypes, fetchTestTypesFailed, fetchTestTypesSuccess } from '../ import { TestTypeEffects } from './test-types.effects'; describe('TestResultsEffects', () => { - let effects: TestTypeEffects; - let actions$ = new Observable(); - let testScheduler: TestScheduler; - let testTypesService: TestTypesService; + let effects: TestTypeEffects; + let actions$ = new Observable(); + let testScheduler: TestScheduler; + let testTypesService: TestTypesService; - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [HttpClientTestingModule, TestTypesApiModule], - providers: [ - TestTypeEffects, - provideMockActions(() => actions$), - TestTypesService, - provideMockStore({ - initialState: initialAppState, - }), - RouterService, - { - provide: UserService, - useValue: { roles$: of(['TestResult.CreateDeskBased']) }, - }, - ], - }); + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [HttpClientTestingModule, TestTypesApiModule], + providers: [ + TestTypeEffects, + provideMockActions(() => actions$), + TestTypesService, + provideMockStore({ + initialState: initialAppState, + }), + RouterService, + { + provide: UserService, + useValue: { roles$: of(['TestResult.CreateDeskBased']) }, + }, + ], + }); - effects = TestBed.inject(TestTypeEffects); - testTypesService = TestBed.inject(TestTypesService); - }); + effects = TestBed.inject(TestTypeEffects); + testTypesService = TestBed.inject(TestTypesService); + }); - beforeEach(() => { - testScheduler = new TestScheduler((actual, expected) => { - expect(actual).toEqual(expected); - }); - }); + beforeEach(() => { + testScheduler = new TestScheduler((actual, expected) => { + expect(actual).toEqual(expected); + }); + }); - describe('fetchTestTypeTaxonomy$', () => { - it('should return fetchTestTypesSuccess action on successfull API call', () => { - testScheduler.run(({ hot, cold, expectObservable }) => { - const testTypes = {} as TestTypesTaxonomy; + describe('fetchTestTypeTaxonomy$', () => { + it('should return fetchTestTypesSuccess action on successfull API call', () => { + testScheduler.run(({ hot, cold, expectObservable }) => { + const testTypes = {} as TestTypesTaxonomy; - // mock action to trigger effect - actions$ = hot('-a--', { a: fetchTestTypes }); + // mock action to trigger effect + actions$ = hot('-a--', { a: fetchTestTypes }); - // mock service call - (testTypesService.getTestTypes as () => Observable) = jest.fn((): Observable => { - return cold('--a|', { a: testTypes }); - }); + // mock service call + (testTypesService.getTestTypes as () => Observable) = jest.fn( + (): Observable => { + return cold('--a|', { a: testTypes }); + } + ); - // expect effect to return success action - expectObservable(effects.fetchTestTypeTaxonomy$).toBe('---b', { - b: fetchTestTypesSuccess({ payload: testTypes }), - }); - }); - }); + // expect effect to return success action + expectObservable(effects.fetchTestTypeTaxonomy$).toBe('---b', { + b: fetchTestTypesSuccess({ payload: testTypes }), + }); + }); + }); - it('should return fetchTestTypesFailed action on successful API call', () => { - testScheduler.run(({ hot, cold, expectObservable }) => { - actions$ = hot('-a--', { a: fetchTestTypes }); + it('should return fetchTestTypesFailed action on successful API call', () => { + testScheduler.run(({ hot, cold, expectObservable }) => { + actions$ = hot('-a--', { a: fetchTestTypes }); - const expectedError = new HttpErrorResponse({ - status: 500, - statusText: 'Internal server error', - }); + const expectedError = new HttpErrorResponse({ + status: 500, + statusText: 'Internal server error', + }); - // mock service call - (testTypesService.getTestTypes as () => Observable) = jest.fn((): Observable => { - return cold('--#|', {}, expectedError); - }); + // mock service call + (testTypesService.getTestTypes as () => Observable) = jest.fn((): Observable => { + return cold('--#|', {}, expectedError); + }); - // expect effect to return success action - expectObservable(effects.fetchTestTypeTaxonomy$).toBe('---b', { - b: fetchTestTypesFailed({ error: expectedError.message }), - }); - }); - }); - }); + // expect effect to return success action + expectObservable(effects.fetchTestTypeTaxonomy$).toBe('---b', { + b: fetchTestTypesFailed({ error: expectedError.message }), + }); + }); + }); + }); }); diff --git a/src/app/store/test-types/effects/test-types.effects.ts b/src/app/store/test-types/effects/test-types.effects.ts index a77210ea62..833417aa6d 100644 --- a/src/app/store/test-types/effects/test-types.effects.ts +++ b/src/app/store/test-types/effects/test-types.effects.ts @@ -1,29 +1,32 @@ -import { Injectable } from '@angular/core'; +import { Injectable, inject } from '@angular/core'; import { Roles } from '@models/roles.enum'; import { TypeOfTest } from '@models/test-results/typeOfTest.enum'; import { Actions, createEffect, ofType } from '@ngrx/effects'; import { TestTypesService } from '@services/test-types/test-types.service'; import { UserService } from '@services/user-service/user-service'; -import { - catchError, switchMap, map, of, -} from 'rxjs'; +import { catchError, map, of, switchMap } from 'rxjs'; import { fetchTestTypes, fetchTestTypesFailed, fetchTestTypesSuccess } from '../actions/test-types.actions'; @Injectable() export class TestTypeEffects { - fetchTestTypeTaxonomy$ = createEffect(() => - this.actions$.pipe( - ofType(fetchTestTypes), - switchMap(() => this.userService.roles$), - switchMap((roles) => { - const typeOfTest = Roles.TestResultCreateDeskAssessment.split(',').some((role) => roles?.includes(role)) ? TypeOfTest.DESK_BASED : undefined; + private actions$ = inject(Actions); + private testTypeService = inject(TestTypesService); + private userService = inject(UserService); - return this.testTypeService.getTestTypes(typeOfTest).pipe( - map((testTypes) => fetchTestTypesSuccess({ payload: testTypes })), - catchError((e) => of(fetchTestTypesFailed({ error: e.message }))), - ); - }), - )); + fetchTestTypeTaxonomy$ = createEffect(() => + this.actions$.pipe( + ofType(fetchTestTypes), + switchMap(() => this.userService.roles$), + switchMap((roles) => { + const typeOfTest = Roles.TestResultCreateDeskAssessment.split(',').some((role) => roles?.includes(role)) + ? TypeOfTest.DESK_BASED + : undefined; - constructor(private actions$: Actions, private testTypeService: TestTypesService, private userService: UserService) { } + return this.testTypeService.getTestTypes(typeOfTest).pipe( + map((testTypes) => fetchTestTypesSuccess({ payload: testTypes })), + catchError((e) => of(fetchTestTypesFailed({ error: e.message }))) + ); + }) + ) + ); } diff --git a/src/app/store/test-types/reducers/test-types.reducer.spec.ts b/src/app/store/test-types/reducers/test-types.reducer.spec.ts index f0c3b1b400..7ca6de6011 100644 --- a/src/app/store/test-types/reducers/test-types.reducer.spec.ts +++ b/src/app/store/test-types/reducers/test-types.reducer.spec.ts @@ -1,54 +1,54 @@ import { TestTypeCategory, TestTypesTaxonomy } from '@api/test-types'; import { fetchTestTypes, fetchTestTypesFailed, fetchTestTypesSuccess } from '../actions/test-types.actions'; -import { initialTestTypeState, testTypesReducer, TestTypeState } from './test-types.reducer'; +import { TestTypeState, initialTestTypeState, testTypesReducer } from './test-types.reducer'; describe('Test Types Reducer', () => { - describe('unknown action', () => { - it('should return the default state', () => { - const action = { - type: 'Unknown', - }; - const state = testTypesReducer(initialTestTypeState, action); - - expect(state).toBe(initialTestTypeState); - }); - - it('should set loading to true', () => { - const oldState: TestTypeState = { ...initialTestTypeState, loading: false }; - const action = fetchTestTypes(); - const state = testTypesReducer(oldState, action); - - expect(state.loading).toBe(true); - expect(state).not.toBe(oldState); - }); - - it('should set the test types data on success and set loading to false on success action', () => { - const testTypes = [ - { id: '1', name: 'foo' }, - { id: '12', name: 'bar' }, - ] as TestTypesTaxonomy; - const newState: TestTypeState = { - ...initialTestTypeState, - entities: { - 1: { id: '1', name: 'foo' } as TestTypeCategory, - 12: { id: '12', name: 'bar' } as TestTypeCategory, - }, - ids: ['1', '12'], - loading: false, - }; - const action = fetchTestTypesSuccess({ payload: testTypes }); - const state = testTypesReducer({ ...initialTestTypeState, loading: true }, action); - - expect(state).toEqual(newState); - }); - - it('should set loading to false on failed action', () => { - const oldState: TestTypeState = { ...initialTestTypeState, loading: true }; - const action = fetchTestTypesFailed({ error: 'error' }); - const state = testTypesReducer(oldState, action); - - expect(state.loading).toBe(false); - expect(state).not.toBe(oldState); - }); - }); + describe('unknown action', () => { + it('should return the default state', () => { + const action = { + type: 'Unknown', + }; + const state = testTypesReducer(initialTestTypeState, action); + + expect(state).toBe(initialTestTypeState); + }); + + it('should set loading to true', () => { + const oldState: TestTypeState = { ...initialTestTypeState, loading: false }; + const action = fetchTestTypes(); + const state = testTypesReducer(oldState, action); + + expect(state.loading).toBe(true); + expect(state).not.toBe(oldState); + }); + + it('should set the test types data on success and set loading to false on success action', () => { + const testTypes = [ + { id: '1', name: 'foo' }, + { id: '12', name: 'bar' }, + ] as TestTypesTaxonomy; + const newState: TestTypeState = { + ...initialTestTypeState, + entities: { + 1: { id: '1', name: 'foo' } as TestTypeCategory, + 12: { id: '12', name: 'bar' } as TestTypeCategory, + }, + ids: ['1', '12'], + loading: false, + }; + const action = fetchTestTypesSuccess({ payload: testTypes }); + const state = testTypesReducer({ ...initialTestTypeState, loading: true }, action); + + expect(state).toEqual(newState); + }); + + it('should set loading to false on failed action', () => { + const oldState: TestTypeState = { ...initialTestTypeState, loading: true }; + const action = fetchTestTypesFailed({ error: 'error' }); + const state = testTypesReducer(oldState, action); + + expect(state.loading).toBe(false); + expect(state).not.toBe(oldState); + }); + }); }); diff --git a/src/app/store/test-types/reducers/test-types.reducer.ts b/src/app/store/test-types/reducers/test-types.reducer.ts index 6a983437ae..967829dfe6 100644 --- a/src/app/store/test-types/reducers/test-types.reducer.ts +++ b/src/app/store/test-types/reducers/test-types.reducer.ts @@ -1,23 +1,25 @@ import { TestType, TestTypeCategory } from '@api/test-types'; -import { EntityAdapter, createEntityAdapter, EntityState } from '@ngrx/entity'; +import { EntityAdapter, EntityState, createEntityAdapter } from '@ngrx/entity'; import { createFeatureSelector, createReducer, on } from '@ngrx/store'; -import { fetchTestTypesFailed, fetchTestTypesSuccess, fetchTestTypes } from '../actions/test-types.actions'; +import { fetchTestTypes, fetchTestTypesFailed, fetchTestTypesSuccess } from '../actions/test-types.actions'; export const STORE_FEATURE_TEST_TYPES_KEY = 'testTypes'; -export const testTypesAdapter: EntityAdapter = createEntityAdapter(); +export const testTypesAdapter: EntityAdapter = createEntityAdapter< + TestType | TestTypeCategory +>(); export interface TestTypeState extends EntityState { - loading: boolean; + loading: boolean; } export const initialTestTypeState = testTypesAdapter.getInitialState({ loading: false }); export const testTypesReducer = createReducer( - initialTestTypeState, - on(fetchTestTypes, (state) => ({ ...state, loading: true })), - on(fetchTestTypesSuccess, (state, action) => ({ ...testTypesAdapter.setAll(action.payload, state), loading: false })), - on(fetchTestTypesFailed, (state) => ({ ...state, loading: false })), + initialTestTypeState, + on(fetchTestTypes, (state) => ({ ...state, loading: true })), + on(fetchTestTypesSuccess, (state, action) => ({ ...testTypesAdapter.setAll(action.payload, state), loading: false })), + on(fetchTestTypesFailed, (state) => ({ ...state, loading: false })) ); export const testTypesFeatureState = createFeatureSelector(STORE_FEATURE_TEST_TYPES_KEY); diff --git a/src/app/store/test-types/selectors/test-types.selectors.spec.ts b/src/app/store/test-types/selectors/test-types.selectors.spec.ts index 626ed7003a..498429a63d 100644 --- a/src/app/store/test-types/selectors/test-types.selectors.spec.ts +++ b/src/app/store/test-types/selectors/test-types.selectors.spec.ts @@ -1,374 +1,547 @@ import { TestTypeCategory, TestTypesTaxonomy } from '@api/test-types'; import { TechRecordSearchSchema } from '@dvsa/cvs-type-definitions/types/v3/tech-record/get/search'; import { TestResultModel } from '@models/test-results/test-result.model'; -import { StatusCodes, VehicleSubclass, VehicleTypes } from '@models/vehicle-tech-record.model'; +import { StatusCodes, V3TechRecordModel, VehicleSubclass, VehicleTypes } from '@models/vehicle-tech-record.model'; import { selectTestType, selectTestTypesByVehicleType, sortedTestTypes } from './test-types.selectors'; describe('selectors', () => { - describe('selectTestTypesByVehicleType', () => { - it('test with no data', () => { - const selector = selectTestTypesByVehicleType.projector([], { vehicleType: 'psv' } as TestResultModel, []); - expect(selector).toHaveLength(0); - }); - - it('test with vehicle type', () => { - const testTypes = [ - { forVehicleType: ['psv'], forEuVehicleCategory: ['m1'] }, - { forVehicleType: ['psv'], forEuVehicleCategory: ['m2'] }, - { forVehicleType: ['car'] }, - { forVehicleType: ['psv', 'hgv'], nextTestTypesOrCategories: [{ forVehicleType: ['psv', 'hgv'] }, { forVehicleType: ['hgv'] }] }, - ] as TestTypesTaxonomy; - - const expectedTestTypes: TestTypesTaxonomy = [ - { forVehicleType: ['psv'], forEuVehicleCategory: ['m1'] }, - { forVehicleType: ['psv'], forEuVehicleCategory: ['m2'] }, - { forVehicleType: ['psv', 'hgv'], nextTestTypesOrCategories: [{ forVehicleType: ['psv', 'hgv'] }] }, - ] as TestTypesTaxonomy; - const selector = selectTestTypesByVehicleType.projector(testTypes, { vehicleType: 'psv' } as TestResultModel, []); - expect(selector).toHaveLength(3); - expect(selector).toEqual(expectedTestTypes); - }); - - it('test with techRecordHistorys', () => { - const techRecordHistorys: TechRecordSearchSchema[] = [{ - vin: 'iii', - techRecord_statusCode: 'current', - techRecord_vehicleType: 'trl', - createdTimestamp: '2022-01-01', - systemNumber: '000', - techRecord_manufactureYear: null, - }]; - const testTypes: TestTypesTaxonomy = [ - { - forVehicleType: ['trl'], forEuVehicleCategory: ['m1'], id: '41', forProvisionalStatus: true, forProvisionalStatusOnly: true, - }, - { - forVehicleType: ['trl'], forEuVehicleCategory: ['m2'], id: '1', - }, - { - forVehicleType: ['trl'], forEuVehicleCategory: ['m2'], id: '12', forProvisionalStatus: true, - }, - { - forVehicleType: ['trl', 'hgv'], - forProvisionalStatus: true, - forProvisionalStatusOnly: true, - }, - ] as TestTypesTaxonomy; - - const expectedTestTypes: TestTypesTaxonomy = [ - { - forVehicleType: ['trl'], forEuVehicleCategory: ['m2'], id: '12', forProvisionalStatus: true, - }, - { - forVehicleType: ['trl', 'hgv'], - forProvisionalStatus: true, - forProvisionalStatusOnly: true, - }, - ] as TestTypesTaxonomy; - - const additionalExpectedTestTypes: TestTypesTaxonomy = [ - { - forVehicleType: ['trl'], forEuVehicleCategory: ['m2'], id: '1', - }, - { - forVehicleType: ['trl'], forEuVehicleCategory: ['m2'], id: '12', forProvisionalStatus: true, - }, - ] as TestTypesTaxonomy; - const selector = selectTestTypesByVehicleType.projector(testTypes, { - vehicleType: VehicleTypes.TRL, - statusCode: StatusCodes.PROVISIONAL, - } as TestResultModel, techRecordHistorys); - expect(selector).toEqual(expectedTestTypes); - - const selectorAdditional = selectTestTypesByVehicleType.projector(testTypes, { - vehicleType: VehicleTypes.TRL, - statusCode: StatusCodes.CURRENT, - } as TestResultModel, techRecordHistorys); - - expect(selectorAdditional).toEqual(additionalExpectedTestTypes); - }); - - it('test with eu vehicle category', () => { - const testTypes: TestTypesTaxonomy = [ - { forVehicleType: ['psv'], forEuVehicleCategory: ['m1'] }, - { forVehicleType: ['psv'], forEuVehicleCategory: ['m2'] }, - { forVehicleType: ['car'] }, - { forVehicleType: ['psv', 'hgv'], nextTestTypesOrCategories: [{ forVehicleType: ['psv', 'hgv'] }, { forVehicleType: ['hgv'] }] }, - ] as TestTypesTaxonomy; - - const expectedTestTypes: TestTypesTaxonomy = [ - { forVehicleType: ['psv'], forEuVehicleCategory: ['m1'] }, - { forVehicleType: ['car'] }, - { forVehicleType: ['psv', 'hgv'], nextTestTypesOrCategories: [{ forVehicleType: ['psv', 'hgv'] }, { forVehicleType: ['hgv'] }] }, - ] as TestTypesTaxonomy; - const selector = selectTestTypesByVehicleType.projector(testTypes, { euVehicleCategory: 'm1' } as TestResultModel, []); - expect(selector).toHaveLength(3); - expect(selector).toEqual(expectedTestTypes); - }); - - it('test with vehicle size', () => { - const testTypes: TestTypesTaxonomy = [ - { forVehicleType: ['psv'], forEuVehicleCategory: ['m1'], forVehicleSize: ['small'] }, - { forVehicleType: ['psv'], forEuVehicleCategory: ['m2'], forVehicleSize: ['small'] }, - { forVehicleType: ['psv'], forEuVehicleCategory: ['m2'], forVehicleSize: ['large'] }, - { forVehicleType: ['car'] }, - { forVehicleType: ['psv', 'hgv'], nextTestTypesOrCategories: [{ forVehicleType: ['psv', 'hgv'] }, { forVehicleType: ['hgv'] }] }, - ] as TestTypesTaxonomy; - - const expectedTestTypes: TestTypesTaxonomy = [ - { forVehicleType: ['psv'], forEuVehicleCategory: ['m1'], forVehicleSize: ['small'] }, - { forVehicleType: ['psv'], forEuVehicleCategory: ['m2'], forVehicleSize: ['small'] }, - { forVehicleType: ['car'] }, - { forVehicleType: ['psv', 'hgv'], nextTestTypesOrCategories: [{ forVehicleType: ['psv', 'hgv'] }, { forVehicleType: ['hgv'] }] }, - ] as TestTypesTaxonomy; - const selector = selectTestTypesByVehicleType.projector(testTypes, { vehicleSize: 'small' } as TestResultModel, []); - expect(selector).toHaveLength(4); - expect(selector).toEqual(expectedTestTypes); - }); - - it('test with vehicle configuration', () => { - const testTypes: TestTypesTaxonomy = [ - { forVehicleType: ['psv'], forEuVehicleCategory: ['m1'], forVehicleConfiguration: ['articulated'] }, - { forVehicleType: ['psv'], forEuVehicleCategory: ['m2'], forVehicleSize: ['small'] }, - { forVehicleType: ['psv'], forEuVehicleCategory: ['m2'], forVehicleSize: ['large'] }, - { forVehicleType: ['car'] }, - { forVehicleType: ['psv', 'hgv'], nextTestTypesOrCategories: [{ forVehicleType: ['psv', 'hgv'] }, { forVehicleType: ['hgv'] }] }, - ] as TestTypesTaxonomy; - - const expectedTestTypes: TestTypesTaxonomy = [ - { forVehicleType: ['psv'], forEuVehicleCategory: ['m2'], forVehicleSize: ['small'] }, - { forVehicleType: ['psv'], forEuVehicleCategory: ['m2'], forVehicleSize: ['large'] }, - { forVehicleType: ['car'] }, - { forVehicleType: ['psv', 'hgv'], nextTestTypesOrCategories: [{ forVehicleType: ['psv', 'hgv'] }, { forVehicleType: ['hgv'] }] }, - ] as TestTypesTaxonomy; - - const selector = selectTestTypesByVehicleType.projector(testTypes, { vehicleConfiguration: 'rigid' } as TestResultModel, []); - expect(selector).toHaveLength(4); - expect(selector).toEqual(expectedTestTypes); - - const selectorAdditional = selectTestTypesByVehicleType.projector(testTypes, { vehicleConfiguration: 'articulated' } as TestResultModel, []); - expect(selectorAdditional).toHaveLength(5); - }); - - it('test with vehicle number of axles', () => { - const testTypes: TestTypesTaxonomy = [ - { forVehicleType: ['psv'], forEuVehicleCategory: ['m1'], forVehicleAxles: [2] }, - { forVehicleType: ['psv'], forEuVehicleCategory: ['m2'], forVehicleSize: ['small'] }, - { forVehicleType: ['psv'], forEuVehicleCategory: ['m2'], forVehicleSize: ['large'] }, - { forVehicleType: ['car'] }, - { forVehicleType: ['psv', 'hgv'], nextTestTypesOrCategories: [{ forVehicleType: ['psv', 'hgv'] }, { forVehicleType: ['hgv'] }] }, - ] as TestTypesTaxonomy; - - const expectedTestTypes: TestTypesTaxonomy = [ - { forVehicleType: ['psv'], forEuVehicleCategory: ['m2'], forVehicleSize: ['small'] }, - { forVehicleType: ['psv'], forEuVehicleCategory: ['m2'], forVehicleSize: ['large'] }, - { forVehicleType: ['car'] }, - { forVehicleType: ['psv', 'hgv'], nextTestTypesOrCategories: [{ forVehicleType: ['psv', 'hgv'] }, { forVehicleType: ['hgv'] }] }, - ] as TestTypesTaxonomy; - - const selector = selectTestTypesByVehicleType.projector(testTypes, { noOfAxles: 4 } as TestResultModel, []); - expect(selector).toHaveLength(4); - expect(selector).toEqual(expectedTestTypes); - - const selectorAdditional = selectTestTypesByVehicleType.projector(testTypes, { noOfAxles: 2 } as TestResultModel, []); - expect(selectorAdditional).toHaveLength(5); - }); - - it('test with vehicle class using code', () => { - const testTypes: TestTypesTaxonomy = [ - { forVehicleType: ['psv'], forEuVehicleCategory: ['m1'], forVehicleClass: ['n'] }, - { forVehicleType: ['psv'], forEuVehicleCategory: ['m2'], forVehicleSize: ['small'] }, - { forVehicleType: ['psv'], forEuVehicleCategory: ['m2'], forVehicleSize: ['large'] }, - { forVehicleType: ['car'] }, - { forVehicleType: ['psv', 'hgv'], nextTestTypesOrCategories: [{ forVehicleType: ['psv', 'hgv'] }, { forVehicleType: ['hgv'] }] }, - ] as TestTypesTaxonomy; - - const expectedTestTypes: TestTypesTaxonomy = [ - { forVehicleType: ['psv'], forEuVehicleCategory: ['m2'], forVehicleSize: ['small'] }, - { forVehicleType: ['psv'], forEuVehicleCategory: ['m2'], forVehicleSize: ['large'] }, - { forVehicleType: ['car'] }, - { forVehicleType: ['psv', 'hgv'], nextTestTypesOrCategories: [{ forVehicleType: ['psv', 'hgv'] }, { forVehicleType: ['hgv'] }] }, - ] as TestTypesTaxonomy; - - const selector = selectTestTypesByVehicleType.projector(testTypes, { vehicleClass: { code: 's' } } as TestResultModel, []); - expect(selector).toHaveLength(4); - expect(selector).toEqual(expectedTestTypes); - - const selectorAdditional = selectTestTypesByVehicleType.projector(testTypes, { vehicleClass: { code: 'n' } } as TestResultModel, []); - expect(selectorAdditional).toHaveLength(5); - }); - - it('test with vehicle class using description', () => { - const testTypes: TestTypesTaxonomy = [ - { forVehicleType: ['psv'], forEuVehicleCategory: ['m1'], forVehicleClass: ['3 wheelers'] }, - { forVehicleType: ['psv'], forEuVehicleCategory: ['m2'], forVehicleSize: ['small'] }, - { forVehicleType: ['psv'], forEuVehicleCategory: ['m2'], forVehicleSize: ['large'] }, - { forVehicleType: ['car'] }, - { forVehicleType: ['psv', 'hgv'], nextTestTypesOrCategories: [{ forVehicleType: ['psv', 'hgv'] }, { forVehicleType: ['hgv'] }] }, - ] as TestTypesTaxonomy; - - const expectedTestTypes: TestTypesTaxonomy = [ - { forVehicleType: ['psv'], forEuVehicleCategory: ['m2'], forVehicleSize: ['small'] }, - { forVehicleType: ['psv'], forEuVehicleCategory: ['m2'], forVehicleSize: ['large'] }, - { forVehicleType: ['car'] }, - { forVehicleType: ['psv', 'hgv'], nextTestTypesOrCategories: [{ forVehicleType: ['psv', 'hgv'] }, { forVehicleType: ['hgv'] }] }, - ] as TestTypesTaxonomy; - - const selector = selectTestTypesByVehicleType.projector(testTypes, { - vehicleClass: { description: 'motorbikes up to 200cc' }, - } as TestResultModel, []); - expect(selector).toHaveLength(4); - expect(selector).toEqual(expectedTestTypes); - - const selectorAdditional = selectTestTypesByVehicleType.projector(testTypes, { - vehicleClass: { description: '3 wheelers' }, - } as TestResultModel, []); - expect(selectorAdditional).toHaveLength(5); - }); - - it('test with vehicle subclass', () => { - const testTypes: TestTypesTaxonomy = [ - { forVehicleType: ['psv'], forEuVehicleCategory: ['m1'], forVehicleSubclass: ['test'] }, - { forVehicleType: ['psv'], forEuVehicleCategory: ['m2'], forVehicleSize: ['small'] }, - { forVehicleType: ['psv'], forEuVehicleCategory: ['m2'], forVehicleSize: ['large'] }, - { forVehicleType: ['car'] }, - { forVehicleType: ['lgv'], forVehicleSubclass: [VehicleSubclass.A] }, - { forVehicleType: ['motorcycle'], forVehicleSubclass: [VehicleSubclass.C] }, - { forVehicleType: ['psv', 'hgv'], nextTestTypesOrCategories: [{ forVehicleType: ['psv', 'hgv'] }, { forVehicleType: ['hgv'] }] }, - ] as TestTypesTaxonomy; - - const expectedTestTypes: TestTypesTaxonomy = [ - { forVehicleType: ['psv'], forEuVehicleCategory: ['m2'], forVehicleSize: ['small'] }, - { forVehicleType: ['psv'], forEuVehicleCategory: ['m2'], forVehicleSize: ['large'] }, - { forVehicleType: ['car'] }, - { forVehicleType: ['psv', 'hgv'], nextTestTypesOrCategories: [{ forVehicleType: ['psv', 'hgv'] }, { forVehicleType: ['hgv'] }] }, - ] as TestTypesTaxonomy; - - const selector = selectTestTypesByVehicleType.projector(testTypes, { vehicleSubclass: [VehicleSubclass.L] } as TestResultModel, []); - expect(selector).toHaveLength(4); - expect(selector).toEqual(expectedTestTypes); - - const selectorAdditional = selectTestTypesByVehicleType.projector(testTypes, { vehicleSubclass: [VehicleSubclass.C] } as TestResultModel, []); - expect(selectorAdditional).toHaveLength(5); - }); - - it('test with vehicle number of wheels', () => { - const testTypes: TestTypesTaxonomy = [ - { forVehicleType: ['psv'], forEuVehicleCategory: ['m1'], forVehicleWheels: [4] }, - { forVehicleType: ['psv'], forEuVehicleCategory: ['m1'], forVehicleWheels: [2] }, - { forVehicleType: ['psv'], forEuVehicleCategory: ['m2'], forVehicleSize: ['small'] }, - { forVehicleType: ['psv'], forEuVehicleCategory: ['m2'], forVehicleSize: ['large'] }, - { forVehicleType: ['car'] }, - { forVehicleType: ['psv', 'hgv'], nextTestTypesOrCategories: [{ forVehicleType: ['psv', 'hgv'] }, { forVehicleType: ['hgv'] }] }, - ] as TestTypesTaxonomy; - - const expectedTestTypes: TestTypesTaxonomy = [ - { forVehicleType: ['psv'], forEuVehicleCategory: ['m1'], forVehicleWheels: [4] }, - { forVehicleType: ['psv'], forEuVehicleCategory: ['m2'], forVehicleSize: ['small'] }, - { forVehicleType: ['psv'], forEuVehicleCategory: ['m2'], forVehicleSize: ['large'] }, - { forVehicleType: ['car'] }, - { forVehicleType: ['psv', 'hgv'], nextTestTypesOrCategories: [{ forVehicleType: ['psv', 'hgv'] }, { forVehicleType: ['hgv'] }] }, - ] as TestTypesTaxonomy; - - const selector = selectTestTypesByVehicleType.projector(testTypes, { numberOfWheelsDriven: 4 } as TestResultModel, []); - expect(selector).toHaveLength(5); - expect(selector).toEqual(expectedTestTypes); - - const selectorAdditional = selectTestTypesByVehicleType.projector(testTypes, { numberOfWheelsDriven: 2 } as TestResultModel, []); - expect(selectorAdditional).toHaveLength(5); - }); - - it('test with vehicle type, eu code, vehicle size', () => { - const testTypes: TestTypesTaxonomy = [ - { forVehicleType: ['psv'], forEuVehicleCategory: ['m1'], forVehicleSize: ['small'] }, - { forVehicleType: ['psv'], forEuVehicleCategory: ['m2'], forVehicleSize: ['small'] }, - { forVehicleType: ['psv'], forEuVehicleCategory: ['m1'], forVehicleSize: ['large'] }, - { forVehicleType: ['car'], forEuVehicleCategory: ['m1'], forVehicleSize: ['small'] }, - { forVehicleType: ['car'] }, - { forVehicleType: ['car', 'hgv'], nextTestTypesOrCategories: [{ forVehicleType: ['psv', 'hgv'] }, { forVehicleType: ['hgv'] }] }, - { forVehicleType: ['psv', 'hgv'], nextTestTypesOrCategories: [{ forVehicleType: ['psv', 'hgv'] }, { forVehicleType: ['hgv'] }] }, - { - forVehicleType: ['psv', 'hgv'], - nextTestTypesOrCategories: [ - { forVehicleType: ['psv', 'hgv'] }, - { forVehicleType: ['psv'], forEuVehicleCategory: ['m1'], forVehicleSize: ['small'] }, - ], - }, - ] as TestTypesTaxonomy; - - const expectedTestTypes: TestTypesTaxonomy = [ - { forVehicleType: ['psv'], forEuVehicleCategory: ['m1'], forVehicleSize: ['small'] }, - { forVehicleType: ['psv', 'hgv'], nextTestTypesOrCategories: [{ forVehicleType: ['psv', 'hgv'] }] }, - { - forVehicleType: ['psv', 'hgv'], - nextTestTypesOrCategories: [ - { forVehicleType: ['psv', 'hgv'] }, - { forVehicleType: ['psv'], forEuVehicleCategory: ['m1'], forVehicleSize: ['small'] }, - ], - }, - ] as TestTypesTaxonomy; - const selector = selectTestTypesByVehicleType.projector(testTypes, { - vehicleType: 'psv', - euVehicleCategory: 'm1', - vehicleSize: 'small', - } as TestResultModel, []); - expect(selector).toHaveLength(3); - expect(selector).toEqual(expectedTestTypes); - }); - }); - - describe('sortedTestTypes', () => { - const unsortedData = [ - { id: '1', sortId: '4' }, - { id: '7', sortId: '1' }, - { id: '8' }, - { - id: '5', - sortId: '3', - nextTestTypesOrCategories: [ - { id: '39', sortId: '2' }, - { id: '40', sortId: '1' }, - ], - }, - ] as TestTypesTaxonomy; - - it('should sort test types by sortId', () => { - const expectedTestTypes: TestTypesTaxonomy = [ - { id: '8' }, - { id: '7', sortId: '1' }, - { - id: '5', - sortId: '3', - nextTestTypesOrCategories: [ - { id: '40', sortId: '1' }, - { id: '39', sortId: '2' }, - ], - }, - { id: '1', sortId: '4' }, - ] as TestTypesTaxonomy; - const sorted = sortedTestTypes.projector(unsortedData); - expect(sorted).toEqual(expectedTestTypes); - }); - }); - - describe('selectTestType', () => { - it('return the right test type', () => { - const exampleTestTypes: TestTypesTaxonomy = [ - { id: '8' }, - { id: '7', sortId: '1' }, - { - id: '5', - sortId: '3', - nextTestTypesOrCategories: [ - { id: '40', sortId: '1' }, - { id: '39', sortId: '2' }, - ], - }, - { id: '1', sortId: '4' }, - ] as TestTypesTaxonomy; - - const selector = selectTestType('39').projector(exampleTestTypes); - expect(selector).toEqual((exampleTestTypes[2] as TestTypeCategory).nextTestTypesOrCategories?.pop()); - }); - }); + const techRecord = { techRecord_statusCode: 'current' } as V3TechRecordModel; + + describe('selectTestTypesByVehicleType', () => { + it('test with no data', () => { + const selector = selectTestTypesByVehicleType.projector( + [], + { vehicleType: 'psv' } as TestResultModel, + [], + techRecord + ); + expect(selector).toHaveLength(0); + }); + + it('test with vehicle type', () => { + const testTypes = [ + { forVehicleType: ['psv'], forEuVehicleCategory: ['m1'] }, + { forVehicleType: ['psv'], forEuVehicleCategory: ['m2'] }, + { forVehicleType: ['car'] }, + { + forVehicleType: ['psv', 'hgv'], + nextTestTypesOrCategories: [{ forVehicleType: ['psv', 'hgv'] }, { forVehicleType: ['hgv'] }], + }, + ] as TestTypesTaxonomy; + + const expectedTestTypes: TestTypesTaxonomy = [ + { forVehicleType: ['psv'], forEuVehicleCategory: ['m1'] }, + { forVehicleType: ['psv'], forEuVehicleCategory: ['m2'] }, + { forVehicleType: ['psv', 'hgv'], nextTestTypesOrCategories: [{ forVehicleType: ['psv', 'hgv'] }] }, + ] as TestTypesTaxonomy; + const selector = selectTestTypesByVehicleType.projector( + testTypes, + { vehicleType: 'psv' } as TestResultModel, + [], + techRecord + ); + expect(selector).toHaveLength(3); + expect(selector).toEqual(expectedTestTypes); + }); + + it('test with techRecordHistorys', () => { + const techRecordHistorys: TechRecordSearchSchema[] = [ + { + vin: 'iii', + techRecord_statusCode: 'current', + techRecord_vehicleType: 'trl', + createdTimestamp: '2022-01-01', + systemNumber: '000', + techRecord_manufactureYear: null, + }, + ]; + const testTypes: TestTypesTaxonomy = [ + { + forVehicleType: ['trl'], + forEuVehicleCategory: ['m1'], + id: '41', + forProvisionalStatus: true, + forProvisionalStatusOnly: true, + }, + { + forVehicleType: ['trl'], + forEuVehicleCategory: ['m2'], + id: '1', + }, + { + forVehicleType: ['trl'], + forEuVehicleCategory: ['m2'], + id: '12', + forProvisionalStatus: true, + }, + { + forVehicleType: ['trl', 'hgv'], + forProvisionalStatus: true, + forProvisionalStatusOnly: true, + }, + ] as TestTypesTaxonomy; + + const expectedTestTypes: TestTypesTaxonomy = [ + { + forVehicleType: ['trl'], + forEuVehicleCategory: ['m2'], + id: '12', + forProvisionalStatus: true, + }, + { + forVehicleType: ['trl', 'hgv'], + forProvisionalStatus: true, + forProvisionalStatusOnly: true, + }, + ] as TestTypesTaxonomy; + + const additionalExpectedTestTypes: TestTypesTaxonomy = [ + { + forVehicleType: ['trl'], + forEuVehicleCategory: ['m2'], + id: '1', + }, + { + forVehicleType: ['trl'], + forEuVehicleCategory: ['m2'], + id: '12', + forProvisionalStatus: true, + }, + ] as TestTypesTaxonomy; + const selector = selectTestTypesByVehicleType.projector( + testTypes, + { + vehicleType: VehicleTypes.TRL, + statusCode: StatusCodes.PROVISIONAL, + } as TestResultModel, + techRecordHistorys, + { ...techRecord, techRecord_statusCode: StatusCodes.PROVISIONAL } + ); + expect(selector).toEqual(expectedTestTypes); + + const selectorAdditional = selectTestTypesByVehicleType.projector( + testTypes, + { + vehicleType: VehicleTypes.TRL, + statusCode: StatusCodes.CURRENT, + } as TestResultModel, + techRecordHistorys, + techRecord + ); + + expect(selectorAdditional).toEqual(additionalExpectedTestTypes); + }); + + it('test with eu vehicle category', () => { + const testTypes: TestTypesTaxonomy = [ + { forVehicleType: ['psv'], forEuVehicleCategory: ['m1'] }, + { forVehicleType: ['psv'], forEuVehicleCategory: ['m2'] }, + { forVehicleType: ['car'] }, + { + forVehicleType: ['psv', 'hgv'], + nextTestTypesOrCategories: [{ forVehicleType: ['psv', 'hgv'] }, { forVehicleType: ['hgv'] }], + }, + ] as TestTypesTaxonomy; + + const expectedTestTypes: TestTypesTaxonomy = [ + { forVehicleType: ['psv'], forEuVehicleCategory: ['m1'] }, + { forVehicleType: ['car'] }, + { + forVehicleType: ['psv', 'hgv'], + nextTestTypesOrCategories: [{ forVehicleType: ['psv', 'hgv'] }, { forVehicleType: ['hgv'] }], + }, + ] as TestTypesTaxonomy; + const selector = selectTestTypesByVehicleType.projector( + testTypes, + { euVehicleCategory: 'm1' } as TestResultModel, + [], + techRecord + ); + expect(selector).toHaveLength(3); + expect(selector).toEqual(expectedTestTypes); + }); + + it('test with vehicle size', () => { + const testTypes: TestTypesTaxonomy = [ + { forVehicleType: ['psv'], forEuVehicleCategory: ['m1'], forVehicleSize: ['small'] }, + { forVehicleType: ['psv'], forEuVehicleCategory: ['m2'], forVehicleSize: ['small'] }, + { forVehicleType: ['psv'], forEuVehicleCategory: ['m2'], forVehicleSize: ['large'] }, + { forVehicleType: ['car'] }, + { + forVehicleType: ['psv', 'hgv'], + nextTestTypesOrCategories: [{ forVehicleType: ['psv', 'hgv'] }, { forVehicleType: ['hgv'] }], + }, + ] as TestTypesTaxonomy; + + const expectedTestTypes: TestTypesTaxonomy = [ + { forVehicleType: ['psv'], forEuVehicleCategory: ['m1'], forVehicleSize: ['small'] }, + { forVehicleType: ['psv'], forEuVehicleCategory: ['m2'], forVehicleSize: ['small'] }, + { forVehicleType: ['car'] }, + { + forVehicleType: ['psv', 'hgv'], + nextTestTypesOrCategories: [{ forVehicleType: ['psv', 'hgv'] }, { forVehicleType: ['hgv'] }], + }, + ] as TestTypesTaxonomy; + const selector = selectTestTypesByVehicleType.projector( + testTypes, + { vehicleSize: 'small' } as TestResultModel, + [], + techRecord + ); + expect(selector).toHaveLength(4); + expect(selector).toEqual(expectedTestTypes); + }); + + it('test with vehicle configuration', () => { + const testTypes: TestTypesTaxonomy = [ + { forVehicleType: ['psv'], forEuVehicleCategory: ['m1'], forVehicleConfiguration: ['articulated'] }, + { forVehicleType: ['psv'], forEuVehicleCategory: ['m2'], forVehicleSize: ['small'] }, + { forVehicleType: ['psv'], forEuVehicleCategory: ['m2'], forVehicleSize: ['large'] }, + { forVehicleType: ['car'] }, + { + forVehicleType: ['psv', 'hgv'], + nextTestTypesOrCategories: [{ forVehicleType: ['psv', 'hgv'] }, { forVehicleType: ['hgv'] }], + }, + ] as TestTypesTaxonomy; + + const expectedTestTypes: TestTypesTaxonomy = [ + { forVehicleType: ['psv'], forEuVehicleCategory: ['m2'], forVehicleSize: ['small'] }, + { forVehicleType: ['psv'], forEuVehicleCategory: ['m2'], forVehicleSize: ['large'] }, + { forVehicleType: ['car'] }, + { + forVehicleType: ['psv', 'hgv'], + nextTestTypesOrCategories: [{ forVehicleType: ['psv', 'hgv'] }, { forVehicleType: ['hgv'] }], + }, + ] as TestTypesTaxonomy; + + const selector = selectTestTypesByVehicleType.projector( + testTypes, + { vehicleConfiguration: 'rigid' } as TestResultModel, + [], + techRecord + ); + expect(selector).toHaveLength(4); + expect(selector).toEqual(expectedTestTypes); + + const selectorAdditional = selectTestTypesByVehicleType.projector( + testTypes, + { vehicleConfiguration: 'articulated' } as TestResultModel, + [], + techRecord + ); + expect(selectorAdditional).toHaveLength(5); + }); + + it('test with vehicle number of axles', () => { + const testTypes: TestTypesTaxonomy = [ + { forVehicleType: ['psv'], forEuVehicleCategory: ['m1'], forVehicleAxles: [2] }, + { forVehicleType: ['psv'], forEuVehicleCategory: ['m2'], forVehicleSize: ['small'] }, + { forVehicleType: ['psv'], forEuVehicleCategory: ['m2'], forVehicleSize: ['large'] }, + { forVehicleType: ['car'] }, + { + forVehicleType: ['psv', 'hgv'], + nextTestTypesOrCategories: [{ forVehicleType: ['psv', 'hgv'] }, { forVehicleType: ['hgv'] }], + }, + ] as TestTypesTaxonomy; + + const expectedTestTypes: TestTypesTaxonomy = [ + { forVehicleType: ['psv'], forEuVehicleCategory: ['m2'], forVehicleSize: ['small'] }, + { forVehicleType: ['psv'], forEuVehicleCategory: ['m2'], forVehicleSize: ['large'] }, + { forVehicleType: ['car'] }, + { + forVehicleType: ['psv', 'hgv'], + nextTestTypesOrCategories: [{ forVehicleType: ['psv', 'hgv'] }, { forVehicleType: ['hgv'] }], + }, + ] as TestTypesTaxonomy; + + const selector = selectTestTypesByVehicleType.projector( + testTypes, + { noOfAxles: 4 } as TestResultModel, + [], + techRecord + ); + expect(selector).toHaveLength(4); + expect(selector).toEqual(expectedTestTypes); + + const selectorAdditional = selectTestTypesByVehicleType.projector( + testTypes, + { noOfAxles: 2 } as TestResultModel, + [], + techRecord + ); + expect(selectorAdditional).toHaveLength(5); + }); + + it('test with vehicle class using code', () => { + const testTypes: TestTypesTaxonomy = [ + { forVehicleType: ['psv'], forEuVehicleCategory: ['m1'], forVehicleClass: ['n'] }, + { forVehicleType: ['psv'], forEuVehicleCategory: ['m2'], forVehicleSize: ['small'] }, + { forVehicleType: ['psv'], forEuVehicleCategory: ['m2'], forVehicleSize: ['large'] }, + { forVehicleType: ['car'] }, + { + forVehicleType: ['psv', 'hgv'], + nextTestTypesOrCategories: [{ forVehicleType: ['psv', 'hgv'] }, { forVehicleType: ['hgv'] }], + }, + ] as TestTypesTaxonomy; + + const expectedTestTypes: TestTypesTaxonomy = [ + { forVehicleType: ['psv'], forEuVehicleCategory: ['m2'], forVehicleSize: ['small'] }, + { forVehicleType: ['psv'], forEuVehicleCategory: ['m2'], forVehicleSize: ['large'] }, + { forVehicleType: ['car'] }, + { + forVehicleType: ['psv', 'hgv'], + nextTestTypesOrCategories: [{ forVehicleType: ['psv', 'hgv'] }, { forVehicleType: ['hgv'] }], + }, + ] as TestTypesTaxonomy; + + const selector = selectTestTypesByVehicleType.projector( + testTypes, + { vehicleClass: { code: 's' } } as TestResultModel, + [], + techRecord + ); + expect(selector).toHaveLength(4); + expect(selector).toEqual(expectedTestTypes); + + const selectorAdditional = selectTestTypesByVehicleType.projector( + testTypes, + { vehicleClass: { code: 'n' } } as TestResultModel, + [], + techRecord + ); + expect(selectorAdditional).toHaveLength(5); + }); + + it('test with vehicle class using description', () => { + const testTypes: TestTypesTaxonomy = [ + { forVehicleType: ['psv'], forEuVehicleCategory: ['m1'], forVehicleClass: ['3 wheelers'] }, + { forVehicleType: ['psv'], forEuVehicleCategory: ['m2'], forVehicleSize: ['small'] }, + { forVehicleType: ['psv'], forEuVehicleCategory: ['m2'], forVehicleSize: ['large'] }, + { forVehicleType: ['car'] }, + { + forVehicleType: ['psv', 'hgv'], + nextTestTypesOrCategories: [{ forVehicleType: ['psv', 'hgv'] }, { forVehicleType: ['hgv'] }], + }, + ] as TestTypesTaxonomy; + + const expectedTestTypes: TestTypesTaxonomy = [ + { forVehicleType: ['psv'], forEuVehicleCategory: ['m2'], forVehicleSize: ['small'] }, + { forVehicleType: ['psv'], forEuVehicleCategory: ['m2'], forVehicleSize: ['large'] }, + { forVehicleType: ['car'] }, + { + forVehicleType: ['psv', 'hgv'], + nextTestTypesOrCategories: [{ forVehicleType: ['psv', 'hgv'] }, { forVehicleType: ['hgv'] }], + }, + ] as TestTypesTaxonomy; + + const selector = selectTestTypesByVehicleType.projector( + testTypes, + { + vehicleClass: { description: 'motorbikes up to 200cc' }, + } as TestResultModel, + [], + techRecord + ); + expect(selector).toHaveLength(4); + expect(selector).toEqual(expectedTestTypes); + + const selectorAdditional = selectTestTypesByVehicleType.projector( + testTypes, + { + vehicleClass: { description: '3 wheelers' }, + } as TestResultModel, + [], + techRecord + ); + expect(selectorAdditional).toHaveLength(5); + }); + + it('test with vehicle subclass', () => { + const testTypes: TestTypesTaxonomy = [ + { forVehicleType: ['psv'], forEuVehicleCategory: ['m1'], forVehicleSubclass: ['test'] }, + { forVehicleType: ['psv'], forEuVehicleCategory: ['m2'], forVehicleSize: ['small'] }, + { forVehicleType: ['psv'], forEuVehicleCategory: ['m2'], forVehicleSize: ['large'] }, + { forVehicleType: ['car'] }, + { forVehicleType: ['lgv'], forVehicleSubclass: [VehicleSubclass.A] }, + { forVehicleType: ['motorcycle'], forVehicleSubclass: [VehicleSubclass.C] }, + { + forVehicleType: ['psv', 'hgv'], + nextTestTypesOrCategories: [{ forVehicleType: ['psv', 'hgv'] }, { forVehicleType: ['hgv'] }], + }, + ] as TestTypesTaxonomy; + + const expectedTestTypes: TestTypesTaxonomy = [ + { forVehicleType: ['psv'], forEuVehicleCategory: ['m2'], forVehicleSize: ['small'] }, + { forVehicleType: ['psv'], forEuVehicleCategory: ['m2'], forVehicleSize: ['large'] }, + { forVehicleType: ['car'] }, + { + forVehicleType: ['psv', 'hgv'], + nextTestTypesOrCategories: [{ forVehicleType: ['psv', 'hgv'] }, { forVehicleType: ['hgv'] }], + }, + ] as TestTypesTaxonomy; + + const selector = selectTestTypesByVehicleType.projector( + testTypes, + { vehicleSubclass: [VehicleSubclass.L] } as TestResultModel, + [], + techRecord + ); + expect(selector).toHaveLength(4); + expect(selector).toEqual(expectedTestTypes); + + const selectorAdditional = selectTestTypesByVehicleType.projector( + testTypes, + { vehicleSubclass: [VehicleSubclass.C] } as TestResultModel, + [], + techRecord + ); + expect(selectorAdditional).toHaveLength(5); + }); + + it('test with vehicle number of wheels', () => { + const testTypes: TestTypesTaxonomy = [ + { forVehicleType: ['psv'], forEuVehicleCategory: ['m1'], forVehicleWheels: [4] }, + { forVehicleType: ['psv'], forEuVehicleCategory: ['m1'], forVehicleWheels: [2] }, + { forVehicleType: ['psv'], forEuVehicleCategory: ['m2'], forVehicleSize: ['small'] }, + { forVehicleType: ['psv'], forEuVehicleCategory: ['m2'], forVehicleSize: ['large'] }, + { forVehicleType: ['car'] }, + { + forVehicleType: ['psv', 'hgv'], + nextTestTypesOrCategories: [{ forVehicleType: ['psv', 'hgv'] }, { forVehicleType: ['hgv'] }], + }, + ] as TestTypesTaxonomy; + + const expectedTestTypes: TestTypesTaxonomy = [ + { forVehicleType: ['psv'], forEuVehicleCategory: ['m1'], forVehicleWheels: [4] }, + { forVehicleType: ['psv'], forEuVehicleCategory: ['m2'], forVehicleSize: ['small'] }, + { forVehicleType: ['psv'], forEuVehicleCategory: ['m2'], forVehicleSize: ['large'] }, + { forVehicleType: ['car'] }, + { + forVehicleType: ['psv', 'hgv'], + nextTestTypesOrCategories: [{ forVehicleType: ['psv', 'hgv'] }, { forVehicleType: ['hgv'] }], + }, + ] as TestTypesTaxonomy; + + const selector = selectTestTypesByVehicleType.projector( + testTypes, + { numberOfWheelsDriven: 4 } as TestResultModel, + [], + techRecord + ); + expect(selector).toHaveLength(5); + expect(selector).toEqual(expectedTestTypes); + + const selectorAdditional = selectTestTypesByVehicleType.projector( + testTypes, + { numberOfWheelsDriven: 2 } as TestResultModel, + [], + techRecord + ); + expect(selectorAdditional).toHaveLength(5); + }); + + it('test with vehicle type, eu code, vehicle size', () => { + const testTypes: TestTypesTaxonomy = [ + { forVehicleType: ['psv'], forEuVehicleCategory: ['m1'], forVehicleSize: ['small'] }, + { forVehicleType: ['psv'], forEuVehicleCategory: ['m2'], forVehicleSize: ['small'] }, + { forVehicleType: ['psv'], forEuVehicleCategory: ['m1'], forVehicleSize: ['large'] }, + { forVehicleType: ['car'], forEuVehicleCategory: ['m1'], forVehicleSize: ['small'] }, + { forVehicleType: ['car'] }, + { + forVehicleType: ['car', 'hgv'], + nextTestTypesOrCategories: [{ forVehicleType: ['psv', 'hgv'] }, { forVehicleType: ['hgv'] }], + }, + { + forVehicleType: ['psv', 'hgv'], + nextTestTypesOrCategories: [{ forVehicleType: ['psv', 'hgv'] }, { forVehicleType: ['hgv'] }], + }, + { + forVehicleType: ['psv', 'hgv'], + nextTestTypesOrCategories: [ + { forVehicleType: ['psv', 'hgv'] }, + { forVehicleType: ['psv'], forEuVehicleCategory: ['m1'], forVehicleSize: ['small'] }, + ], + }, + ] as TestTypesTaxonomy; + + const expectedTestTypes: TestTypesTaxonomy = [ + { forVehicleType: ['psv'], forEuVehicleCategory: ['m1'], forVehicleSize: ['small'] }, + { forVehicleType: ['psv', 'hgv'], nextTestTypesOrCategories: [{ forVehicleType: ['psv', 'hgv'] }] }, + { + forVehicleType: ['psv', 'hgv'], + nextTestTypesOrCategories: [ + { forVehicleType: ['psv', 'hgv'] }, + { forVehicleType: ['psv'], forEuVehicleCategory: ['m1'], forVehicleSize: ['small'] }, + ], + }, + ] as TestTypesTaxonomy; + const selector = selectTestTypesByVehicleType.projector( + testTypes, + { + vehicleType: 'psv', + euVehicleCategory: 'm1', + vehicleSize: 'small', + } as TestResultModel, + [], + techRecord + ); + expect(selector).toHaveLength(3); + expect(selector).toEqual(expectedTestTypes); + }); + }); + + describe('sortedTestTypes', () => { + const unsortedData = [ + { id: '1', sortId: '4' }, + { id: '7', sortId: '1' }, + { id: '8' }, + { + id: '5', + sortId: '3', + nextTestTypesOrCategories: [ + { id: '39', sortId: '2' }, + { id: '40', sortId: '1' }, + ], + }, + ] as TestTypesTaxonomy; + + it('should sort test types by sortId', () => { + const expectedTestTypes: TestTypesTaxonomy = [ + { id: '8' }, + { id: '7', sortId: '1' }, + { + id: '5', + sortId: '3', + nextTestTypesOrCategories: [ + { id: '40', sortId: '1' }, + { id: '39', sortId: '2' }, + ], + }, + { id: '1', sortId: '4' }, + ] as TestTypesTaxonomy; + const sorted = sortedTestTypes.projector(unsortedData); + expect(sorted).toEqual(expectedTestTypes); + }); + }); + + describe('selectTestType', () => { + it('return the right test type', () => { + const exampleTestTypes: TestTypesTaxonomy = [ + { id: '8' }, + { id: '7', sortId: '1' }, + { + id: '5', + sortId: '3', + nextTestTypesOrCategories: [ + { id: '40', sortId: '1' }, + { id: '39', sortId: '2' }, + ], + }, + { id: '1', sortId: '4' }, + ] as TestTypesTaxonomy; + + const selector = selectTestType('39').projector(exampleTestTypes); + expect(selector).toEqual((exampleTestTypes[2] as TestTypeCategory).nextTestTypesOrCategories?.pop()); + }); + }); }); diff --git a/src/app/store/test-types/selectors/test-types.selectors.ts b/src/app/store/test-types/selectors/test-types.selectors.ts index eb05f4a104..79d5bb4020 100644 --- a/src/app/store/test-types/selectors/test-types.selectors.ts +++ b/src/app/store/test-types/selectors/test-types.selectors.ts @@ -1,17 +1,15 @@ import { TestType } from '@api/test-types'; import { TestTypeCategory } from '@api/test-types/model/testTypeCategory'; import { TestTypesTaxonomy } from '@api/test-types/model/testTypesTaxonomy'; +import { TechRecordSearchSchema } from '@dvsa/cvs-type-definitions/types/v3/tech-record/get/search'; import { TestResultModel } from '@models/test-results/test-result.model'; -import { StatusCodes, VehicleSubclass } from '@models/vehicle-tech-record.model'; +import { StatusCodes, V3TechRecordModel, VehicleSubclass } from '@models/vehicle-tech-record.model'; import { createSelector } from '@ngrx/store'; +import { selectTechRecord, selectTechRecordHistory } from '@store/technical-records'; import { toEditOrNotToEdit } from '@store/test-records'; -import { selectTechRecordHistory } from '@store/technical-records'; -import { TechRecordSearchSchema } from '@dvsa/cvs-type-definitions/types/v3/tech-record/get/search'; import { testTypesAdapter, testTypesFeatureState } from '../reducers/test-types.reducer'; -const { - selectIds, selectEntities, selectAll, selectTotal, -} = testTypesAdapter.getSelectors(); +const { selectIds, selectEntities, selectAll, selectTotal } = testTypesAdapter.getSelectors(); // select the array of ids export const selectTestTypesIds = createSelector(testTypesFeatureState, (state) => selectIds(state)); @@ -28,137 +26,171 @@ export const selectTestTypesTotal = createSelector(testTypesFeatureState, (state export const selectTestTypesLoadingState = createSelector(testTypesFeatureState, (state) => state.loading); export const selectTestTypesByVehicleType = createSelector( - selectAllTestTypes, - toEditOrNotToEdit, - selectTechRecordHistory, - (testTypes, testResult, techRecordHistory) => { - const hasCurrentRecordInHistory = techRecordHistory ? currentRecordInHistoryCheck(techRecordHistory) : false; - - if (testResult) { - return filterTestTypes(testTypes, testResult, hasCurrentRecordInHistory); - } - return []; - }, + selectAllTestTypes, + toEditOrNotToEdit, + selectTechRecordHistory, + selectTechRecord, + (testTypes, testResult, techRecordHistory, techRecord) => { + const hasCurrentRecordInHistory = techRecordHistory ? currentRecordInHistoryCheck(techRecordHistory) : false; + + if (testResult && techRecord) { + return filterTestTypes(testTypes, testResult, hasCurrentRecordInHistory, techRecord); + } + return []; + } ); export const sortedTestTypes = createSelector(selectTestTypesByVehicleType, (testTypes) => { - const sortTestTypes = (testTypesList: TestTypesTaxonomy): TestTypesTaxonomy => { - return testTypesList - .sort((a, b) => { - if (!Object.prototype.hasOwnProperty.call(b, 'sortId')) { - return 1; - } - - if (!Object.prototype.hasOwnProperty.call(a, 'sortId')) { - return -1; - } - - return parseInt((a as TestTypeCategory).sortId || '0', 10) - parseInt((b as TestTypeCategory).sortId || '0', 10); - }) - .map((testType) => { - const newTestType = { ...testType } as TestTypeCategory; - - if (Object.prototype.hasOwnProperty.call(newTestType, 'nextTestTypesOrCategories')) { - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - newTestType.nextTestTypesOrCategories = sortTestTypes(newTestType.nextTestTypesOrCategories!); - } - - return newTestType; - }); - }; - - return sortTestTypes(testTypes); + const sortTestTypes = (testTypesList: TestTypesTaxonomy): TestTypesTaxonomy => { + return testTypesList + .sort((a, b) => { + if (!Object.prototype.hasOwnProperty.call(b, 'sortId')) { + return 1; + } + + if (!Object.prototype.hasOwnProperty.call(a, 'sortId')) { + return -1; + } + + return ( + Number.parseInt((a as TestTypeCategory).sortId || '0', 10) - + Number.parseInt((b as TestTypeCategory).sortId || '0', 10) + ); + }) + .map((testType) => { + const newTestType = { ...testType } as TestTypeCategory; + + if (Object.prototype.hasOwnProperty.call(newTestType, 'nextTestTypesOrCategories')) { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + newTestType.nextTestTypesOrCategories = sortTestTypes(newTestType.nextTestTypesOrCategories!); + } + + return newTestType; + }); + }; + + return sortTestTypes(testTypes); }); export const selectTestType = (id: string | undefined) => - createSelector(selectTestTypesByVehicleType, (testTypes): TestType | undefined => { - function findUsingId(idToSearch: string | undefined, testTypesList: TestTypesTaxonomy | undefined): TestType | undefined { - if (!testTypesList) { - return undefined; - } + createSelector(selectTestTypesByVehicleType, (testTypes): TestType | undefined => { + function findUsingId( + idToSearch: string | undefined, + testTypesList: TestTypesTaxonomy | undefined + ): TestType | undefined { + if (!testTypesList) { + return undefined; + } - // eslint-disable-next-line no-restricted-syntax - for (const testType of testTypesList) { - if (testType.id === idToSearch) { - return testType; - } + // eslint-disable-next-line no-restricted-syntax + for (const testType of testTypesList) { + if (testType.id === idToSearch) { + return testType; + } - const found = findUsingId(idToSearch, (testType as TestTypeCategory).nextTestTypesOrCategories); + const found = findUsingId(idToSearch, (testType as TestTypeCategory).nextTestTypesOrCategories); - if (found) { - return found; - } - } + if (found) { + return found; + } + } - return undefined; - } + return undefined; + } - return findUsingId(id, testTypes); - }); + return findUsingId(id, testTypes); + }); -export const getTypeOfTest = (id: string | undefined) => createSelector(selectTestType(id), (testTypes) => testTypes?.typeOfTest); +export const getTypeOfTest = (id: string | undefined) => + createSelector(selectTestType(id), (testTypes) => testTypes?.typeOfTest); function currentRecordInHistoryCheck(techRecordHistorys: TechRecordSearchSchema[]): boolean { - return techRecordHistorys.some((techRecordHis) => techRecordHis.techRecord_statusCode === 'current'); + return techRecordHistorys.some((techRecordHis) => techRecordHis.techRecord_statusCode === 'current'); } -function filterTestTypes(testTypes: TestTypesTaxonomy, testResult: TestResultModel, hasCurrentRecordInHistory: boolean): TestTypesTaxonomy { - const { - vehicleType, - statusCode, - euVehicleCategory, - vehicleSize, - vehicleConfiguration, - noOfAxles, - vehicleClass, - vehicleSubclass, - numberOfWheelsDriven, - } = testResult; - const filterFirstTestIds : string[] = ['41', '95', '82', '83', '119', '120', '65', '66', '67', '103', '104', '51']; - return ( - testTypes - .filter((testType) => !vehicleType || !testType.forVehicleType || testType.forVehicleType.includes(vehicleType)) - .filter((testType) => !vehicleType || !testType.forVehicleType || testType.forVehicleType.includes(vehicleType)) - .filter((testType) => !statusCode || statusCode !== StatusCodes.PROVISIONAL || testType.forProvisionalStatus) - .filter((testType) => !statusCode || !testType.forProvisionalStatusOnly || statusCode === StatusCodes.PROVISIONAL) - .filter((testType) => !euVehicleCategory || !testType.forEuVehicleCategory || testType.forEuVehicleCategory.includes(euVehicleCategory)) - .filter((testType) => !vehicleSize || !testType.forVehicleSize || testType.forVehicleSize.includes(vehicleSize)) - .filter( - (testType) => !vehicleConfiguration || !testType.forVehicleConfiguration || testType.forVehicleConfiguration.includes(vehicleConfiguration), - ) - .filter((testType) => !noOfAxles || !testType.forVehicleAxles || testType.forVehicleAxles.includes(noOfAxles)) - // if code AND description are null, or if either code OR description are in forVehicleClass, include in filter - .filter( - (testType) => - !vehicleClass - || !testType.forVehicleClass - || (!vehicleClass.code && !vehicleClass.description) - || (vehicleClass.code && testType.forVehicleClass.includes(vehicleClass.code as unknown as string)) - || (vehicleClass.description && testType.forVehicleClass.includes(vehicleClass.description as unknown as string)), - ) - .filter( - (testType) => - !vehicleSubclass - || !testType.forVehicleSubclass - || testType.forVehicleSubclass.some((forVehicleSubclass) => vehicleSubclass.includes(forVehicleSubclass as VehicleSubclass)), - ) - .filter((testType) => !numberOfWheelsDriven || !testType.forVehicleWheels || testType.forVehicleWheels.includes(numberOfWheelsDriven)) - // for some first tests, only show them for provisional record when there are no current records in history - .filter( - (testType) => !statusCode - || statusCode !== StatusCodes.PROVISIONAL - || !hasCurrentRecordInHistory - || !filterFirstTestIds.includes(testType.id), - ) - .map((testType: TestTypeCategory) => { - const newTestType = { ...testType } as TestTypeCategory; - - if (Object.prototype.hasOwnProperty.call(newTestType, 'nextTestTypesOrCategories')) { - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - newTestType.nextTestTypesOrCategories = filterTestTypes(newTestType.nextTestTypesOrCategories!, testResult, hasCurrentRecordInHistory); - } - - return newTestType; - }) - ); +function filterTestTypes( + testTypes: TestTypesTaxonomy, + testResult: TestResultModel, + hasCurrentRecordInHistory: boolean, + techRecord: V3TechRecordModel +): TestTypesTaxonomy { + const { + vehicleType, + euVehicleCategory, + vehicleSize, + vehicleConfiguration, + noOfAxles, + vehicleClass, + vehicleSubclass, + numberOfWheelsDriven, + } = testResult; + const { techRecord_statusCode: statusCode } = techRecord; + const filterFirstTestIds: string[] = ['41', '95', '82', '83', '119', '120', '65', '66', '67', '103', '104', '51']; + return ( + testTypes + .filter((testType) => !vehicleType || !testType.forVehicleType || testType.forVehicleType.includes(vehicleType)) + .filter((testType) => !vehicleType || !testType.forVehicleType || testType.forVehicleType.includes(vehicleType)) + .filter((testType) => !statusCode || statusCode !== StatusCodes.PROVISIONAL || testType.forProvisionalStatus) + .filter((testType) => !statusCode || !testType.forProvisionalStatusOnly || statusCode === StatusCodes.PROVISIONAL) + .filter( + (testType) => + !euVehicleCategory || + !testType.forEuVehicleCategory || + testType.forEuVehicleCategory.includes(euVehicleCategory) + ) + .filter((testType) => !vehicleSize || !testType.forVehicleSize || testType.forVehicleSize.includes(vehicleSize)) + .filter( + (testType) => + !vehicleConfiguration || + !testType.forVehicleConfiguration || + testType.forVehicleConfiguration.includes(vehicleConfiguration) + ) + .filter((testType) => !noOfAxles || !testType.forVehicleAxles || testType.forVehicleAxles.includes(noOfAxles)) + // if code AND description are null, or if either code OR description are in forVehicleClass, include in filter + .filter( + (testType) => + !vehicleClass || + !testType.forVehicleClass || + (!vehicleClass.code && !vehicleClass.description) || + (vehicleClass.code && testType.forVehicleClass.includes(vehicleClass.code as unknown as string)) || + (vehicleClass.description && testType.forVehicleClass.includes(vehicleClass.description as unknown as string)) + ) + .filter( + (testType) => + !vehicleSubclass || + !testType.forVehicleSubclass || + testType.forVehicleSubclass.some((forVehicleSubclass) => + vehicleSubclass.includes(forVehicleSubclass as VehicleSubclass) + ) + ) + .filter( + (testType) => + !numberOfWheelsDriven || + !testType.forVehicleWheels || + testType.forVehicleWheels.includes(numberOfWheelsDriven) + ) + // for some first tests, only show them for provisional record when there are no current records in history + .filter( + (testType) => + !statusCode || + statusCode !== StatusCodes.PROVISIONAL || + !hasCurrentRecordInHistory || + !filterFirstTestIds.includes(testType.id) + ) + .map((testType: TestTypeCategory) => { + const newTestType = { ...testType } as TestTypeCategory; + + if (Object.prototype.hasOwnProperty.call(newTestType, 'nextTestTypesOrCategories')) { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + newTestType.nextTestTypesOrCategories = filterTestTypes( + newTestType.nextTestTypesOrCategories!, + testResult, + hasCurrentRecordInHistory, + techRecord + ); + } + + return newTestType; + }) + ); } diff --git a/src/app/store/test-types/test-types.module.ts b/src/app/store/test-types/test-types.module.ts index d194cd42c7..c015d75ad5 100644 --- a/src/app/store/test-types/test-types.module.ts +++ b/src/app/store/test-types/test-types.module.ts @@ -6,7 +6,11 @@ import { TestTypeEffects } from './effects/test-types.effects'; import { STORE_FEATURE_TEST_TYPES_KEY, testTypesReducer } from './reducers/test-types.reducer'; @NgModule({ - declarations: [], - imports: [CommonModule, StoreModule.forFeature(STORE_FEATURE_TEST_TYPES_KEY, testTypesReducer), EffectsModule.forFeature([TestTypeEffects])], + declarations: [], + imports: [ + CommonModule, + StoreModule.forFeature(STORE_FEATURE_TEST_TYPES_KEY, testTypesReducer), + EffectsModule.forFeature([TestTypeEffects]), + ], }) export class TestTypesStateModule {} diff --git a/src/app/store/user/user-service.actions.ts b/src/app/store/user/user-service.actions.ts index 90d3746849..7f8e0f6f6d 100644 --- a/src/app/store/user/user-service.actions.ts +++ b/src/app/store/user/user-service.actions.ts @@ -1,4 +1,7 @@ import { createAction, props } from '@ngrx/store'; -export const Login = createAction('[User Service] Login', props<{ name: string; userEmail: string; oid: string; roles: string[] }>()); +export const Login = createAction( + '[User Service] Login', + props<{ name: string; userEmail: string; oid: string; roles: string[] }>() +); export const Logout = createAction('[User Service] Logout'); diff --git a/src/app/store/user/user-service.reducer.ts b/src/app/store/user/user-service.reducer.ts index bad0c9a192..fda8dd90f7 100644 --- a/src/app/store/user/user-service.reducer.ts +++ b/src/app/store/user/user-service.reducer.ts @@ -1,24 +1,22 @@ import { Roles } from '@models/roles.enum'; -import { - createFeatureSelector, createReducer, createSelector, on, -} from '@ngrx/store'; +import { createFeatureSelector, createReducer, createSelector, on } from '@ngrx/store'; import { environment } from '../../../environments/environment'; import * as UserServiceActions from './user-service.actions'; export const STORE_FEATURE_USER_KEY = 'user'; export interface UserServiceState { - name: string; - userEmail: string; - oid: string; - roles: string[] | null; + name: string; + userEmail: string; + oid: string; + roles: string[] | null; } export const initialState: UserServiceState = { - name: '(Not logged in)', - userEmail: '', - oid: '', - roles: null, + name: '(Not logged in)', + userEmail: '', + oid: '', + roles: null, }; const getUserState = createFeatureSelector(STORE_FEATURE_USER_KEY); @@ -30,16 +28,28 @@ export const roles = createSelector(getUserState, (state) => state.roles); export const user = createSelector(getUserState, (state) => state); export const userServiceReducer = createReducer( - initialState, - on(UserServiceActions.Login, (state, { - // eslint-disable-next-line @typescript-eslint/no-shadow - name, userEmail, oid, roles, - }) => ({ - name, userEmail, oid, roles: getRoles(roles), - })), - on(UserServiceActions.Logout, () => initialState), + initialState, + on( + UserServiceActions.Login, + ( + state, + { + // eslint-disable-next-line @typescript-eslint/no-shadow + name, + userEmail, + oid, + roles, + } + ) => ({ + name, + userEmail, + oid, + roles: getRoles(roles), + }) + ), + on(UserServiceActions.Logout, () => initialState) ); function getRoles(rolesArray: string[]): string[] { - return environment.RemoveAADFullAccessRole ? rolesArray.filter((role) => role !== Roles.Admin) : rolesArray; + return environment.RemoveAADFullAccessRole ? rolesArray.filter((role) => role !== Roles.Admin) : rolesArray; } diff --git a/src/app/store/user/user-state.module.ts b/src/app/store/user/user-state.module.ts index 063572eca1..b0a7351d32 100644 --- a/src/app/store/user/user-state.module.ts +++ b/src/app/store/user/user-state.module.ts @@ -5,14 +5,14 @@ import { localStorageSync } from 'ngrx-store-localstorage'; import { STORE_FEATURE_USER_KEY, userServiceReducer } from './user-service.reducer'; export function localStorageSyncReducer(reducer: ActionReducer): ActionReducer { - return localStorageSync({ keys: ['name', 'userEmail', 'oid', 'roles'], rehydrate: true })(reducer); + return localStorageSync({ keys: ['name', 'userEmail', 'oid', 'roles'], rehydrate: true })(reducer); } // eslint-disable-next-line @typescript-eslint/no-explicit-any const metaReducers: Array> = [localStorageSyncReducer]; @NgModule({ - declarations: [], - imports: [CommonModule, StoreModule.forFeature(STORE_FEATURE_USER_KEY, userServiceReducer, { metaReducers })], + declarations: [], + imports: [CommonModule, StoreModule.forFeature(STORE_FEATURE_USER_KEY, userServiceReducer, { metaReducers })], }) export class UserStateModule {} diff --git a/src/environments/environment.prod.ts b/src/environments/environment.prod.ts index 71522599d3..05f2cadbb7 100644 --- a/src/environments/environment.prod.ts +++ b/src/environments/environment.prod.ts @@ -1,16 +1,16 @@ export const environment = { - production: true, - TARGET_ENV: 'prod', - RemoveAADFullAccessRole: true, - EnableDevTools: false, - VTM_CLIENT_ID: '', - VTM_AUTHORITY_ID: '', - VTM_REDIRECT_URI: '/', - VTM_API_URI: '', - VTM_API_CLIENT_ID: '', - DOCUMENT_RETRIEVAL_API_KEY: '', - FEEDBACK_URI: '', - SENTRY_DSN: '', - VTM_GTM_CONTAINER_ID: '', - VTM_GTM_MEASUREMENT_ID: '', + production: true, + TARGET_ENV: 'prod', + RemoveAADFullAccessRole: true, + EnableDevTools: false, + VTM_CLIENT_ID: '', + VTM_AUTHORITY_ID: '', + VTM_REDIRECT_URI: '/', + VTM_API_URI: '', + VTM_API_CLIENT_ID: '', + DOCUMENT_RETRIEVAL_API_KEY: '', + FEEDBACK_URI: '', + SENTRY_DSN: '', + VTM_GTM_CONTAINER_ID: '', + VTM_GTM_MEASUREMENT_ID: '', }; diff --git a/src/environments/environment.ts b/src/environments/environment.ts index a0c7c4d134..ecc6dccebd 100644 --- a/src/environments/environment.ts +++ b/src/environments/environment.ts @@ -3,20 +3,20 @@ // The list of file replacements can be found in `angular.json`. export const environment = { - production: false, - TARGET_ENV: 'dev', - RemoveAADFullAccessRole: true, - EnableDevTools: true, - VTM_CLIENT_ID: '', - VTM_AUTHORITY_ID: '', - VTM_REDIRECT_URI: '/', - VTM_API_URI: '', - VTM_API_CLIENT_ID: '', - DOCUMENT_RETRIEVAL_API_KEY: '', - FEEDBACK_URI: '', - SENTRY_DSN: '', - VTM_GTM_CONTAINER_ID: '', - VTM_GTM_MEASUREMENT_ID: '', + production: false, + TARGET_ENV: 'dev', + RemoveAADFullAccessRole: true, + EnableDevTools: true, + VTM_CLIENT_ID: '', + VTM_AUTHORITY_ID: '', + VTM_REDIRECT_URI: '/', + VTM_API_URI: '', + VTM_API_CLIENT_ID: '', + DOCUMENT_RETRIEVAL_API_KEY: '', + FEEDBACK_URI: '', + SENTRY_DSN: '', + VTM_GTM_CONTAINER_ID: '', + VTM_GTM_MEASUREMENT_ID: '', }; /* diff --git a/src/main.ts b/src/main.ts index d9a2e7e4a5..9bd6fb69f4 100644 --- a/src/main.ts +++ b/src/main.ts @@ -5,9 +5,9 @@ import { AppModule } from './app/app.module'; import { environment } from './environments/environment'; if (environment.production) { - enableProdMode(); + enableProdMode(); } platformBrowserDynamic() - .bootstrapModule(AppModule) - .catch((err) => console.error(err)); + .bootstrapModule(AppModule) + .catch((err) => console.error(err)); diff --git a/src/mocks/custom-defect.mock.ts b/src/mocks/custom-defect.mock.ts index 31ed641205..6eeb0d069f 100644 --- a/src/mocks/custom-defect.mock.ts +++ b/src/mocks/custom-defect.mock.ts @@ -1,14 +1,14 @@ import { CustomDefects } from '@models/test-types/test-type.model'; export const createMockCustomDefect = (params: Partial = {}): CustomDefects => ({ - referenceNumber: 'referenceNumber', - defectName: 'defectName', - defectNotes: 'defectNotes', - ...params, + referenceNumber: 'referenceNumber', + defectName: 'defectName', + defectNotes: 'defectNotes', + ...params, }); export const createMockAdditionalDefect = (params: Partial = {}): CustomDefects => ({ - defectName: 'defectName', - defectNotes: 'defectNotes', - ...params, + defectName: 'defectName', + defectNotes: 'defectNotes', + ...params, }); diff --git a/src/mocks/google-analytics-service.mock.ts b/src/mocks/google-analytics-service.mock.ts new file mode 100644 index 0000000000..1f44d6f3f8 --- /dev/null +++ b/src/mocks/google-analytics-service.mock.ts @@ -0,0 +1,5 @@ +export class GoogleAnalyticsServiceMock { + checkForId() {} + pushTag() {} + addGtmToDom() {} +} diff --git a/src/mocks/hgv-record.mock.ts b/src/mocks/hgv-record.mock.ts index daab4cdc4a..2c1361088d 100644 --- a/src/mocks/hgv-record.mock.ts +++ b/src/mocks/hgv-record.mock.ts @@ -6,60 +6,60 @@ import { BodyTypeDescription } from '@models/body-type-enum'; import { FuelTypes, StatusCodes } from '../app/models/vehicle-tech-record.model'; export const createMockHgv = (systemNumber: number): TechRecordType<'hgv'> => ({ - systemNumber: 'HGV', - vin: `XMGDE03FS0H0${12344 + systemNumber + 1}`, - primaryVrm: `KP${String(systemNumber + 1).padStart(2, '0')} ABC`, - secondaryVrms: null, - createdTimestamp: new Date().toISOString(), - techRecord_createdAt: new Date().toISOString(), - techRecord_createdByName: 'Nathan', - techRecord_statusCode: StatusCodes.CURRENT, - techRecord_vehicleType: 'hgv', - techRecord_regnDate: '1234', - techRecord_manufactureYear: 2022, - techRecord_noOfAxles: 2, - techRecord_brakes_dtpNumber: '1234', - techRecord_speedLimiterMrk: true, - techRecord_tachoExemptMrk: true, - techRecord_euroStandard: '123', - techRecord_roadFriendly: true, - techRecord_fuelPropulsionSystem: FuelTypes.HYBRID, - techRecord_drawbarCouplingFitted: true, - techRecord_vehicleClass_description: 'heavy goods vehicle', - techRecord_vehicleClass_code: 'v', - techRecord_vehicleConfiguration: VehicleConfiguration.ARTICULATED, - techRecord_offRoad: true, - techRecord_euVehicleCategory: EUVehicleCategory.N1, - techRecord_emissionsLimit: 1234, - techRecord_departmentalVehicleMarker: true, - techRecord_reasonForCreation: 'Brake Failure', - techRecord_approvalType: ApprovalType.ECSSTA, - techRecord_approvalTypeNumber: 'approval123', - techRecord_axles: undefined, - techRecord_ntaNumber: 'nta789', - techRecord_variantNumber: 'variant123456', - techRecord_variantVersionNumber: 'variantversion123456', - techRecord_dimensions_length: 1, - techRecord_dimensions_width: 2, - techRecord_frontAxleToRearAxle: 3, - techRecord_frontVehicleTo5thWheelCouplingMin: 5, - techRecord_frontVehicleTo5thWheelCouplingMax: 6, - techRecord_frontAxleTo5thWheelMin: 7, - techRecord_frontAxleTo5thWheelMax: 8, - techRecord_plates: undefined, - techRecord_make: '1234', - techRecord_model: '1234', - techRecord_bodyType_description: BodyTypeDescription.OTHER, - techRecord_functionCode: '1', - techRecord_conversionRefNo: '1234', - techRecord_grossGbWeight: 2, - techRecord_grossEecWeight: 4, - techRecord_grossDesignWeight: 3, - techRecord_trainGbWeight: 3, - techRecord_trainEecWeight: 3, - techRecord_trainDesignWeight: 7, - partialVin: 'vin', - techRecord_createdById: '2', - techRecord_numberOfWheelsDriven: 4, - techRecord_recordCompleteness: 'testable', + systemNumber: 'HGV', + vin: `XMGDE03FS0H0${12344 + systemNumber + 1}`, + primaryVrm: `KP${String(systemNumber + 1).padStart(2, '0')} ABC`, + secondaryVrms: null, + createdTimestamp: new Date().toISOString(), + techRecord_createdAt: new Date().toISOString(), + techRecord_createdByName: 'Nathan', + techRecord_statusCode: StatusCodes.CURRENT, + techRecord_vehicleType: 'hgv', + techRecord_regnDate: '1234', + techRecord_manufactureYear: 2022, + techRecord_noOfAxles: 2, + techRecord_brakes_dtpNumber: '1234', + techRecord_speedLimiterMrk: true, + techRecord_tachoExemptMrk: true, + techRecord_euroStandard: '123', + techRecord_roadFriendly: true, + techRecord_fuelPropulsionSystem: FuelTypes.HYBRID, + techRecord_drawbarCouplingFitted: true, + techRecord_vehicleClass_description: 'heavy goods vehicle', + techRecord_vehicleClass_code: 'v', + techRecord_vehicleConfiguration: VehicleConfiguration.ARTICULATED, + techRecord_offRoad: true, + techRecord_euVehicleCategory: EUVehicleCategory.N1, + techRecord_emissionsLimit: 1234, + techRecord_departmentalVehicleMarker: true, + techRecord_reasonForCreation: 'Brake Failure', + techRecord_approvalType: ApprovalType.ECSSTA, + techRecord_approvalTypeNumber: 'approval123', + techRecord_axles: undefined, + techRecord_ntaNumber: 'nta789', + techRecord_variantNumber: 'variant123456', + techRecord_variantVersionNumber: 'variantversion123456', + techRecord_dimensions_length: 1, + techRecord_dimensions_width: 2, + techRecord_frontAxleToRearAxle: 3, + techRecord_frontVehicleTo5thWheelCouplingMin: 5, + techRecord_frontVehicleTo5thWheelCouplingMax: 6, + techRecord_frontAxleTo5thWheelMin: 7, + techRecord_frontAxleTo5thWheelMax: 8, + techRecord_plates: undefined, + techRecord_make: '1234', + techRecord_model: '1234', + techRecord_bodyType_description: BodyTypeDescription.OTHER, + techRecord_functionCode: '1', + techRecord_conversionRefNo: '1234', + techRecord_grossGbWeight: 2, + techRecord_grossEecWeight: 4, + techRecord_grossDesignWeight: 3, + techRecord_trainGbWeight: 3, + techRecord_trainEecWeight: 3, + techRecord_trainDesignWeight: 7, + partialVin: 'vin', + techRecord_createdById: '2', + techRecord_numberOfWheelsDriven: 4, + techRecord_recordCompleteness: 'testable', }); diff --git a/src/mocks/lgv-record.mock.ts b/src/mocks/lgv-record.mock.ts index 74a48b3a03..1be33bb8d7 100644 --- a/src/mocks/lgv-record.mock.ts +++ b/src/mocks/lgv-record.mock.ts @@ -4,19 +4,19 @@ import { TechRecordType } from '@dvsa/cvs-type-definitions/types/v3/tech-record/ import { StatusCodes } from '@models/vehicle-tech-record.model'; export const createMockLgv = (systemNumber: number): TechRecordType<'lgv'> => ({ - systemNumber: 'LGV', - vin: `XMGDE04FS0H0${12344 + systemNumber + 1}`, - secondaryVrms: null, - createdTimestamp: new Date().toISOString(), - techRecord_createdAt: new Date().toISOString(), - techRecord_createdByName: 'Nathan', - techRecord_statusCode: StatusCodes.CURRENT, - techRecord_vehicleType: 'lgv', - techRecord_regnDate: '1234', - techRecord_manufactureYear: 2022, - techRecord_noOfAxles: 2, - techRecord_vehicleConfiguration: VehicleConfiguration.ARTICULATED, - techRecord_euVehicleCategory: EUVehicleCategory.N1, - techRecord_reasonForCreation: 'Brake Failure', - techRecord_createdById: '0', + systemNumber: 'LGV', + vin: `XMGDE04FS0H0${12344 + systemNumber + 1}`, + secondaryVrms: null, + createdTimestamp: new Date().toISOString(), + techRecord_createdAt: new Date().toISOString(), + techRecord_createdByName: 'Nathan', + techRecord_statusCode: StatusCodes.CURRENT, + techRecord_vehicleType: 'lgv', + techRecord_regnDate: '1234', + techRecord_manufactureYear: 2022, + techRecord_noOfAxles: 2, + techRecord_vehicleConfiguration: VehicleConfiguration.ARTICULATED, + techRecord_euVehicleCategory: EUVehicleCategory.N1, + techRecord_reasonForCreation: 'Brake Failure', + techRecord_createdById: '0', }); diff --git a/src/mocks/mock-defects.ts b/src/mocks/mock-defects.ts index 5dca877f7f..15e41b07b0 100644 --- a/src/mocks/mock-defects.ts +++ b/src/mocks/mock-defects.ts @@ -3,27 +3,27 @@ import { DeficiencyCategoryEnum } from '../app/models/test-results/test-result-d export const mockDefectList = (numberOfDefects = 1) => new Array(numberOfDefects).fill(0).map((_, i) => mockDefect(i)); export const mockDefect = (i = 0) => ({ - deficiencyRef: `DeficiencyRef${i}`, - deficiencyCategory: DeficiencyCategoryEnum.Dangerous, - deficiencyId: 'deficiency ID', - deficiencySubId: 'deficiency sub ID', - deficiencyText: 'deficiency text', - imDescription: 'IM description', - imNumber: 34, - itemDescription: 'item description', - itemNumber: 6, - prohibitionIssued: false, - prs: false, - stdForProhibition: false, - additionalInformation: mockDefectAdditionalInformation(i), + deficiencyRef: `DeficiencyRef${i}`, + deficiencyCategory: DeficiencyCategoryEnum.Dangerous, + deficiencyId: 'deficiency ID', + deficiencySubId: 'deficiency sub ID', + deficiencyText: 'deficiency text', + imDescription: 'IM description', + imNumber: 34, + itemDescription: 'item description', + itemNumber: 6, + prohibitionIssued: false, + prs: false, + stdForProhibition: false, + additionalInformation: mockDefectAdditionalInformation(i), }); export const mockDefectAdditionalInformation = (i = 0) => ({ - location: mockDefectLocation(i), - notes: 'Defect notes', + location: mockDefectLocation(i), + notes: 'Defect notes', }); export const mockDefectLocation = (i = 0) => ({ - seatNumber: i + 1, - rowNumber: i + 1, + seatNumber: i + 1, + rowNumber: i + 1, }); diff --git a/src/mocks/mock-test-result.ts b/src/mocks/mock-test-result.ts index e7fd0e4da5..36b87e602c 100644 --- a/src/mocks/mock-test-result.ts +++ b/src/mocks/mock-test-result.ts @@ -6,88 +6,92 @@ import { OdometerReadingUnits } from '@models/test-types/odometer-unit.enum'; import { VehicleTypes } from '../app/models/vehicle-tech-record.model'; import { createMockTestType } from './test-type.mock'; -export const mockTestResult = (i = 0, vehicleType: VehicleTypes = VehicleTypes.PSV, systemNumber = 'SYS0001'): TestResultModel => ({ - testResultId: `TestResultId${String(i + 1).padStart(4, '0')}`, - systemNumber, - vin: 'XMGDE02FS0H012345', - vrm: 'KP02ABC', - createdAt: new Date().toISOString(), - testStartTimestamp: new Date().toISOString(), - testTypes: [createMockTestType(), createMockTestType()], - trailerId: `C${String(i + 1).padStart(5, '0')}`, - countryOfRegistration: 'gb', - euVehicleCategory: EUVehicleCategory.M3, - odometerReading: 100, - odometerReadingUnits: OdometerReadingUnits.KILOMETRES, - reasonForCreation: 'mock test result data', - preparerName: 'Durrell Truck & Van Centre', - preparerId: 'CM2254', - testStationName: 'Abshire-Kub', - testStationPNumber: 'P12346', - testStationType: TestStationType.ATF, - testerName: 'John Smith', - testerEmailAddress: 'john.smith@dvsa.gov.uk', - testStatus: TestResultStatus.SUBMITTED, - vehicleType, - testVersion: 'Current', - createdByName: 'Jane Doe', - testHistory: [ - mockTestResultArchived(0, `TestResultId${String(i + 1).padStart(4, '0')}`, vehicleType, systemNumber), - mockTestResultArchived(1, `TestResultId${String(i + 1).padStart(4, '0')}`, vehicleType, systemNumber), - mockTestResultArchived(2, `TestResultId${String(i + 1).padStart(4, '0')}`, vehicleType, systemNumber), - mockTestResultArchived(3, `TestResultId${String(i + 1).padStart(4, '0')}`, vehicleType, systemNumber), - mockTestResultArchived(4, `TestResultId${String(i + 1).padStart(4, '0')}`, vehicleType, systemNumber), - ], - testEndTimestamp: new Date().toISOString(), - testerStaffId: 'testerStaffId', +export const mockTestResult = ( + i = 0, + vehicleType: VehicleTypes = VehicleTypes.PSV, + systemNumber = 'SYS0001' +): TestResultModel => ({ + testResultId: `TestResultId${String(i + 1).padStart(4, '0')}`, + systemNumber, + vin: 'XMGDE02FS0H012345', + vrm: 'KP02ABC', + createdAt: new Date().toISOString(), + testStartTimestamp: new Date().toISOString(), + testTypes: [createMockTestType(), createMockTestType()], + trailerId: `C${String(i + 1).padStart(5, '0')}`, + countryOfRegistration: 'gb', + euVehicleCategory: EUVehicleCategory.M3, + odometerReading: 100, + odometerReadingUnits: OdometerReadingUnits.KILOMETRES, + reasonForCreation: 'mock test result data', + preparerName: 'Durrell Truck & Van Centre', + preparerId: 'CM2254', + testStationName: 'Abshire-Kub', + testStationPNumber: 'P12346', + testStationType: TestStationType.ATF, + testerName: 'John Smith', + testerEmailAddress: 'john.smith@dvsa.gov.uk', + testStatus: TestResultStatus.SUBMITTED, + vehicleType, + testVersion: 'Current', + createdByName: 'Jane Doe', + testHistory: [ + mockTestResultArchived(0, `TestResultId${String(i + 1).padStart(4, '0')}`, vehicleType, systemNumber), + mockTestResultArchived(1, `TestResultId${String(i + 1).padStart(4, '0')}`, vehicleType, systemNumber), + mockTestResultArchived(2, `TestResultId${String(i + 1).padStart(4, '0')}`, vehicleType, systemNumber), + mockTestResultArchived(3, `TestResultId${String(i + 1).padStart(4, '0')}`, vehicleType, systemNumber), + mockTestResultArchived(4, `TestResultId${String(i + 1).padStart(4, '0')}`, vehicleType, systemNumber), + ], + testEndTimestamp: new Date().toISOString(), + testerStaffId: 'testerStaffId', }); export const mockTestResultArchived = ( - i = 0, - testResultId = 'TestResultId0001', - vehicleType: VehicleTypes = VehicleTypes.PSV, - systemNumber = 'SYS0001', + i = 0, + testResultId = 'TestResultId0001', + vehicleType: VehicleTypes = VehicleTypes.PSV, + systemNumber = 'SYS0001' ) => { - const date = new Date('2022-01-02'); - const createdAt = date.setDate(date.getDate() - (i + 1)); - const testResult: TestResultModel = { - testResultId, - createdAt: new Date(createdAt).toISOString(), - systemNumber, - vin: 'XMGDE02FS0H012345', - vrm: 'KP02 ABC', - testStartTimestamp: new Date().toISOString(), - testTypes: [createMockTestType(), createMockTestType()], - testEndTimestamp: new Date().toISOString(), - testerStaffId: 'testerStaffId', - createdByName: `Person ${i}`, - trailerId: `C${String(i + 1).padStart(5, '0')}`, - countryOfRegistration: 'gb', - euVehicleCategory: EUVehicleCategory.M3, - odometerReading: 100, - odometerReadingUnits: OdometerReadingUnits.KILOMETRES, - reasonForCreation: `reason ${i}`, - preparerName: 'Durrell Truck & Van Centre', - preparerId: 'CM2254', - testStationName: 'Abshire-Kub', - testStationPNumber: 'P12346', - testStationType: TestStationType.ATF, - testerName: `tester ${i}`, - testerEmailAddress: 'john.smith@dvsa.gov.uk', - testVersion: 'Archived', - vehicleType, - }; + const date = new Date('2022-01-02'); + const createdAt = date.setDate(date.getDate() - (i + 1)); + const testResult: TestResultModel = { + testResultId, + createdAt: new Date(createdAt).toISOString(), + systemNumber, + vin: 'XMGDE02FS0H012345', + vrm: 'KP02 ABC', + testStartTimestamp: new Date().toISOString(), + testTypes: [createMockTestType(), createMockTestType()], + testEndTimestamp: new Date().toISOString(), + testerStaffId: 'testerStaffId', + createdByName: `Person ${i}`, + trailerId: `C${String(i + 1).padStart(5, '0')}`, + countryOfRegistration: 'gb', + euVehicleCategory: EUVehicleCategory.M3, + odometerReading: 100, + odometerReadingUnits: OdometerReadingUnits.KILOMETRES, + reasonForCreation: `reason ${i}`, + preparerName: 'Durrell Truck & Van Centre', + preparerId: 'CM2254', + testStationName: 'Abshire-Kub', + testStationPNumber: 'P12346', + testStationType: TestStationType.ATF, + testerName: `tester ${i}`, + testerEmailAddress: 'john.smith@dvsa.gov.uk', + testVersion: 'Archived', + vehicleType, + }; - return testResult; + return testResult; }; export const mockTestResultList = (items = 1, systemNumber = 'PSV') => { - switch (systemNumber.substring(0, 3)) { - case 'HGV': - return new Array(items).fill(0).map((_, i) => mockTestResult(i, VehicleTypes.HGV, systemNumber)); - case 'TRL': - return new Array(items).fill(0).map((_, i) => mockTestResult(i, VehicleTypes.TRL, systemNumber)); - default: - return new Array(items).fill(0).map((_, i) => mockTestResult(i)); - } + switch (systemNumber.substring(0, 3)) { + case 'HGV': + return new Array(items).fill(0).map((_, i) => mockTestResult(i, VehicleTypes.HGV, systemNumber)); + case 'TRL': + return new Array(items).fill(0).map((_, i) => mockTestResult(i, VehicleTypes.TRL, systemNumber)); + default: + return new Array(items).fill(0).map((_, i) => mockTestResult(i)); + } }; diff --git a/src/mocks/mock-vehicle-technical-record.mock.ts b/src/mocks/mock-vehicle-technical-record.mock.ts index acef5f0569..ebba747fbc 100644 --- a/src/mocks/mock-vehicle-technical-record.mock.ts +++ b/src/mocks/mock-vehicle-technical-record.mock.ts @@ -1,15 +1,15 @@ import { VehicleType } from '@dvsa/cvs-type-definitions/types/v3/tech-record/get/search'; -import { createMockPsv } from './psv-record.mock'; import { createMockHgv } from './hgv-record.mock'; +import { createMockPsv } from './psv-record.mock'; import { createMockTrl } from './trl-record.mock'; export const mockVehicleTechnicalRecord = (vehicleType: VehicleType = 'psv', systemNumber = 0) => { - switch (vehicleType) { - case 'hgv': - return createMockHgv(systemNumber); - case 'trl': - return createMockTrl(systemNumber); - default: - return createMockPsv(systemNumber); - } + switch (vehicleType) { + case 'hgv': + return createMockHgv(systemNumber); + case 'trl': + return createMockTrl(systemNumber); + default: + return createMockPsv(systemNumber); + } }; diff --git a/src/mocks/psv-record.mock.ts b/src/mocks/psv-record.mock.ts index dc9676371c..af92110e8f 100644 --- a/src/mocks/psv-record.mock.ts +++ b/src/mocks/psv-record.mock.ts @@ -4,174 +4,170 @@ import { VehicleClassDescription } from '@dvsa/cvs-type-definitions/types/v3/tec import { VehicleConfiguration } from '@dvsa/cvs-type-definitions/types/v3/tech-record/enums/vehicleConfigurationHgvPsv.enum.js'; import { TechRecordType } from '@dvsa/cvs-type-definitions/types/v3/tech-record/tech-record-vehicle-type'; import { BodyTypeDescription } from '@models/body-type-enum'; -import { - Axles, - FuelTypes, Retarders, StatusCodes, - VehicleSizes, -} from '../app/models/vehicle-tech-record.model'; +import { Axles, FuelTypes, Retarders, StatusCodes, VehicleSizes } from '../app/models/vehicle-tech-record.model'; export const createMockPsv = (systemNumber: number): TechRecordType<'psv'> => ({ - systemNumber: 'PSV', - vin: `XMGDE02FS0H0${12344 + systemNumber + 1}`, - primaryVrm: 'KP01ABC', - secondaryVrms: undefined, - createdTimestamp: 'now', - techRecord_createdAt: new Date().toISOString(), - techRecord_createdByName: 'Nathan', - techRecord_statusCode: StatusCodes.PROVISIONAL, - techRecord_vehicleType: 'psv', - techRecord_regnDate: '1234', - techRecord_manufactureYear: 2022, - techRecord_axles: axles as unknown as Axles<'psv'>, - techRecord_noOfAxles: 2, - techRecord_brakes_dtpNumber: '1234', - techRecord_brakes_brakeCode: '1234', - techRecord_brakes_dataTrBrakeOne: '12', - techRecord_brakes_dataTrBrakeTwo: '34', - techRecord_brakes_dataTrBrakeThree: '56', - techRecord_brakes_retarderBrakeOne: Retarders.ELECTRIC, - techRecord_brakes_retarderBrakeTwo: Retarders.ELECTRIC, - techRecord_brakes_brakeCodeOriginal: 'original', - techRecord_brakes_brakeForceWheelsNotLocked_parkingBrakeForceA: 1234, - techRecord_brakes_brakeForceWheelsNotLocked_secondaryBrakeForceA: 1234, - techRecord_brakes_brakeForceWheelsNotLocked_serviceBrakeForceA: 1234, - techRecord_brakes_brakeForceWheelsUpToHalfLocked_parkingBrakeForceB: 1234, - techRecord_brakes_brakeForceWheelsUpToHalfLocked_secondaryBrakeForceB: 1234, - techRecord_brakes_brakeForceWheelsUpToHalfLocked_serviceBrakeForceB: 1234, - techRecord_speedLimiterMrk: true, - techRecord_speedRestriction: 54, - techRecord_tachoExemptMrk: true, - techRecord_euroStandard: 'Euro 4', - techRecord_fuelPropulsionSystem: FuelTypes.HYBRID, + systemNumber: 'PSV', + vin: `XMGDE02FS0H0${12344 + systemNumber + 1}`, + primaryVrm: 'KP01ABC', + secondaryVrms: undefined, + createdTimestamp: 'now', + techRecord_createdAt: new Date().toISOString(), + techRecord_createdByName: 'Nathan', + techRecord_statusCode: StatusCodes.PROVISIONAL, + techRecord_vehicleType: 'psv', + techRecord_regnDate: '1234', + techRecord_manufactureYear: 2022, + techRecord_axles: axles as unknown as Axles<'psv'>, + techRecord_noOfAxles: 2, + techRecord_brakes_dtpNumber: '1234', + techRecord_brakes_brakeCode: '1234', + techRecord_brakes_dataTrBrakeOne: '12', + techRecord_brakes_dataTrBrakeTwo: '34', + techRecord_brakes_dataTrBrakeThree: '56', + techRecord_brakes_retarderBrakeOne: Retarders.ELECTRIC, + techRecord_brakes_retarderBrakeTwo: Retarders.ELECTRIC, + techRecord_brakes_brakeCodeOriginal: 'original', + techRecord_brakes_brakeForceWheelsNotLocked_parkingBrakeForceA: 1234, + techRecord_brakes_brakeForceWheelsNotLocked_secondaryBrakeForceA: 1234, + techRecord_brakes_brakeForceWheelsNotLocked_serviceBrakeForceA: 1234, + techRecord_brakes_brakeForceWheelsUpToHalfLocked_parkingBrakeForceB: 1234, + techRecord_brakes_brakeForceWheelsUpToHalfLocked_secondaryBrakeForceB: 1234, + techRecord_brakes_brakeForceWheelsUpToHalfLocked_serviceBrakeForceB: 1234, + techRecord_speedLimiterMrk: true, + techRecord_speedRestriction: 54, + techRecord_tachoExemptMrk: true, + techRecord_euroStandard: 'Euro 4', + techRecord_fuelPropulsionSystem: FuelTypes.HYBRID, - techRecord_vehicleClass_description: VehicleClassDescription.LARGE_PSV, - techRecord_vehicleClass_code: '1', - techRecord_vehicleConfiguration: VehicleConfiguration.ARTICULATED, - techRecord_euVehicleCategory: EUVehicleCategory.M1, - techRecord_emissionsLimit: 1234, - techRecord_seatsLowerDeck: 1234, - techRecord_seatsUpperDeck: 1234, - techRecord_standingCapacity: 1234, - techRecord_vehicleSize: VehicleSizes.SMALL, - techRecord_numberOfSeatbelts: '1234', - techRecord_seatbeltInstallationApprovalDate: '1234', - techRecord_departmentalVehicleMarker: true, + techRecord_vehicleClass_description: VehicleClassDescription.LARGE_PSV, + techRecord_vehicleClass_code: '1', + techRecord_vehicleConfiguration: VehicleConfiguration.ARTICULATED, + techRecord_euVehicleCategory: EUVehicleCategory.M1, + techRecord_emissionsLimit: 1234, + techRecord_seatsLowerDeck: 1234, + techRecord_seatsUpperDeck: 1234, + techRecord_standingCapacity: 1234, + techRecord_vehicleSize: VehicleSizes.SMALL, + techRecord_numberOfSeatbelts: '1234', + techRecord_seatbeltInstallationApprovalDate: '1234', + techRecord_departmentalVehicleMarker: true, - techRecord_dimensions_height: 30000, - techRecord_dimensions_length: 25000, - techRecord_dimensions_width: 10000, - techRecord_frontAxleToRearAxle: 5000, - techRecord_approvalType: ApprovalType.ECSSTA, - techRecord_approvalTypeNumber: 'approval123', - techRecord_ntaNumber: 'nta789', - techRecord_coifSerialNumber: 'coifSerial123456', - techRecord_coifCertifierName: 'coifName', - techRecord_coifDate: new Date().toISOString(), - techRecord_variantNumber: 'variant123456', - techRecord_variantVersionNumber: 'variantversion123456', + techRecord_dimensions_height: 30000, + techRecord_dimensions_length: 25000, + techRecord_dimensions_width: 10000, + techRecord_frontAxleToRearAxle: 5000, + techRecord_approvalType: ApprovalType.ECSSTA, + techRecord_approvalTypeNumber: 'approval123', + techRecord_ntaNumber: 'nta789', + techRecord_coifSerialNumber: 'coifSerial123456', + techRecord_coifCertifierName: 'coifName', + techRecord_coifDate: new Date().toISOString(), + techRecord_variantNumber: 'variant123456', + techRecord_variantVersionNumber: 'variantversion123456', - techRecord_applicantDetails_name: 'Test', - techRecord_applicantDetails_address1: 'address1', - techRecord_applicantDetails_address2: 'address2', - techRecord_applicantDetails_postTown: 'town', - techRecord_applicantDetails_address3: 'address3', - techRecord_applicantDetails_postCode: 'postCode', - techRecord_applicantDetails_telephoneNumber: '0121', - techRecord_applicantDetails_emailAddress: 'test@email.com', + techRecord_applicantDetails_name: 'Test', + techRecord_applicantDetails_address1: 'address1', + techRecord_applicantDetails_address2: 'address2', + techRecord_applicantDetails_postTown: 'town', + techRecord_applicantDetails_address3: 'address3', + techRecord_applicantDetails_postCode: 'postCode', + techRecord_applicantDetails_telephoneNumber: '0121', + techRecord_applicantDetails_emailAddress: 'test@email.com', - // techRecord_microfilmDocumentType: 'AAT - Trailer Annual Test', - // techRecord_microfilmRollNumber: 'nb123456', - // techRecord_microfilmSerialNumber: 'ser123456', - techRecord_remarks: 'Some notes about the vehicle', - techRecord_dispensations: 'reason given', - techRecord_reasonForCreation: 'Brake Failure', - techRecord_modelLiteral: 'Vehicle model', - techRecord_chassisMake: 'Chassis make', - techRecord_chassisModel: 'Chassis model', - techRecord_bodyMake: 'Body make', - techRecord_bodyModel: 'Body model', - techRecord_bodyType_description: BodyTypeDescription.DOUBLE_DECKER, - techRecord_functionCode: 'r', - techRecord_conversionRefNo: '345345', + // techRecord_microfilmDocumentType: 'AAT - Trailer Annual Test', + // techRecord_microfilmRollNumber: 'nb123456', + // techRecord_microfilmSerialNumber: 'ser123456', + techRecord_remarks: 'Some notes about the vehicle', + techRecord_dispensations: 'reason given', + techRecord_reasonForCreation: 'Brake Failure', + techRecord_modelLiteral: 'Vehicle model', + techRecord_chassisMake: 'Chassis make', + techRecord_chassisModel: 'Chassis model', + techRecord_bodyMake: 'Body make', + techRecord_bodyModel: 'Body model', + techRecord_bodyType_description: BodyTypeDescription.DOUBLE_DECKER, + techRecord_functionCode: 'r', + techRecord_conversionRefNo: '345345', - // Gross vehicle weights - techRecord_grossKerbWeight: 1, - techRecord_grossLadenWeight: 2, - techRecord_grossGbWeight: 3, - // TODO: missing from types package - // techRecord_grossEecWeight: 4, - techRecord_grossDesignWeight: 5, - techRecord_unladenWeight: 6, + // Gross vehicle weights + techRecord_grossKerbWeight: 1, + techRecord_grossLadenWeight: 2, + techRecord_grossGbWeight: 3, + // TODO: missing from types package + // techRecord_grossEecWeight: 4, + techRecord_grossDesignWeight: 5, + techRecord_unladenWeight: 6, - // Train weights - techRecord_maxTrainGbWeight: 7, - techRecord_trainDesignWeight: 8, - techRecord_dda_certificateIssued: true, - techRecord_dda_wheelchairCapacity: 5, - techRecord_dda_wheelchairFittings: 'data', - techRecord_dda_wheelchairLiftPresent: false, - techRecord_dda_wheelchairLiftInformation: 'more data', - techRecord_dda_wheelchairRampPresent: true, - techRecord_dda_wheelchairRampInformation: 'ramp data', - techRecord_dda_minEmergencyExits: 3, - techRecord_dda_outswing: 'Anderson', - techRecord_dda_ddaSchedules: 'dda', - techRecord_dda_seatbeltsFitted: 51, - techRecord_dda_ddaNotes: 'This is a note', + // Train weights + techRecord_maxTrainGbWeight: 7, + techRecord_trainDesignWeight: 8, + techRecord_dda_certificateIssued: true, + techRecord_dda_wheelchairCapacity: 5, + techRecord_dda_wheelchairFittings: 'data', + techRecord_dda_wheelchairLiftPresent: false, + techRecord_dda_wheelchairLiftInformation: 'more data', + techRecord_dda_wheelchairRampPresent: true, + techRecord_dda_wheelchairRampInformation: 'ramp data', + techRecord_dda_minEmergencyExits: 3, + techRecord_dda_outswing: 'Anderson', + techRecord_dda_ddaSchedules: 'dda', + techRecord_dda_seatbeltsFitted: 51, + techRecord_dda_ddaNotes: 'This is a note', }); const axles = [ - { - axleNumber: 1, - tyres_tyreSize: '295/80-22.5', - tyres_speedCategorySymbol: 'p', - tyres_fitmentCode: 'double', - tyres_dataTrAxles: 0, - tyres_plyRating: 'A', - tyres_tyreCode: 456, - parkingBrakeMrk: 'false', + { + axleNumber: 1, + tyres_tyreSize: '295/80-22.5', + tyres_speedCategorySymbol: 'p', + tyres_fitmentCode: 'double', + tyres_dataTrAxles: 0, + tyres_plyRating: 'A', + tyres_tyreCode: 456, + parkingBrakeMrk: 'false', - weights_kerbWeight: 1, - weights_ladenWeight: 2, - weights_gbWeight: 3, - // TODO: V3 2 eecweights in type package, which is this? - // weights_eecWeight: 4, - weights_designWeight: 5, - }, - { - axleNumber: 2, - parkingBrakeMrk: 'true', + weights_kerbWeight: 1, + weights_ladenWeight: 2, + weights_gbWeight: 3, + // TODO: V3 2 eecweights in type package, which is this? + // weights_eecWeight: 4, + weights_designWeight: 5, + }, + { + axleNumber: 2, + parkingBrakeMrk: 'true', - tyres_tyreSize: '295/80-22.5', - tyres_speedCategorySymbol: 'p', - tyres_fitmentCode: 'double', - tyres_dataTrAxles: 0, - tyres_plyRating: 'A', - tyres_tyreCode: 456, + tyres_tyreSize: '295/80-22.5', + tyres_speedCategorySymbol: 'p', + tyres_fitmentCode: 'double', + tyres_dataTrAxles: 0, + tyres_plyRating: 'A', + tyres_tyreCode: 456, - weights_kerbWeight: 1, - weights_ladenWeight: 2, - weights_gbWeight: 3, - // weights_eecWeight: 4, - weights_designWeight: 5, - }, - { - axleNumber: 3, - parkingBrakeMrk: 'true', + weights_kerbWeight: 1, + weights_ladenWeight: 2, + weights_gbWeight: 3, + // weights_eecWeight: 4, + weights_designWeight: 5, + }, + { + axleNumber: 3, + parkingBrakeMrk: 'true', - tyres_tyreSize: '295/80-22.5', - tyres_speedCategorySymbol: 'p', - tyres_fitmentCode: 'double', - tyres_dataTrAxles: 0, - tyres_plyRating: 'A', - tyres_tyreCode: 456, + tyres_tyreSize: '295/80-22.5', + tyres_speedCategorySymbol: 'p', + tyres_fitmentCode: 'double', + tyres_dataTrAxles: 0, + tyres_plyRating: 'A', + tyres_tyreCode: 456, - weights_kerbWeight: 1, - weights_ladenWeight: 2, - weights_gbWeight: 3, - // weights_eecWeight: 4, - weights_designWeight: 5, - }, + weights_kerbWeight: 1, + weights_ladenWeight: 2, + weights_gbWeight: 3, + // weights_eecWeight: 4, + weights_designWeight: 5, + }, ]; // const provisionalTechRecord = { diff --git a/src/mocks/reference-data/mock-countries-of-registration.reference-data.ts b/src/mocks/reference-data/mock-countries-of-registration.reference-data.ts index 7829bc4bab..b80a59673f 100644 --- a/src/mocks/reference-data/mock-countries-of-registration.reference-data.ts +++ b/src/mocks/reference-data/mock-countries-of-registration.reference-data.ts @@ -1,194 +1,194 @@ import { ReferenceDataModelBase, ReferenceDataResourceType } from '@models/reference-data.model'; export const mockCountriesOfRegistration: ReferenceDataModelBase[] = [ - { - resourceType: ReferenceDataResourceType.CountryOfRegistration, - resourceKey: 'gb', - description: 'Great Britain and Northern Ireland - GB', - }, - { - resourceType: ReferenceDataResourceType.CountryOfRegistration, - resourceKey: 'gba', - description: 'Alderney - GBA', - }, - { - resourceType: ReferenceDataResourceType.CountryOfRegistration, - resourceKey: 'gbg', - description: 'Guernsey - GBG', - }, - { - resourceType: ReferenceDataResourceType.CountryOfRegistration, - resourceKey: 'gbj', - description: 'Jersey - GBJ', - }, - { - resourceType: ReferenceDataResourceType.CountryOfRegistration, - resourceKey: 'gbm', - description: 'Isle of Man - GBM', - }, - { - resourceType: ReferenceDataResourceType.CountryOfRegistration, - resourceKey: 'gbz', - description: 'Gibraltar - GBZ', - }, - { - resourceType: ReferenceDataResourceType.CountryOfRegistration, - resourceKey: 'a', - description: 'Austria - A', - }, - { - resourceType: ReferenceDataResourceType.CountryOfRegistration, - resourceKey: 'b', - description: 'Belgium - B', - }, - { - resourceType: ReferenceDataResourceType.CountryOfRegistration, - resourceKey: 'bih', - description: 'BosniaAndHerzegovina - BIH', - }, - { - resourceType: ReferenceDataResourceType.CountryOfRegistration, - resourceKey: 'bg', - description: 'Bulgaria - BG', - }, - { - resourceType: ReferenceDataResourceType.CountryOfRegistration, - resourceKey: 'hr', - description: 'Croatia - HR', - }, - { - resourceType: ReferenceDataResourceType.CountryOfRegistration, - resourceKey: 'cy', - description: 'Cyprus - CY', - }, - { - resourceType: ReferenceDataResourceType.CountryOfRegistration, - resourceKey: 'cz', - description: 'CzechRepublic - CZ', - }, - { - resourceType: ReferenceDataResourceType.CountryOfRegistration, - resourceKey: 'dk', - description: 'Denmark - DK', - }, - { - resourceType: ReferenceDataResourceType.CountryOfRegistration, - resourceKey: 'est', - description: 'Estonia - EST', - }, - { - resourceType: ReferenceDataResourceType.CountryOfRegistration, - resourceKey: 'fin', - description: 'Finland - FIN', - }, - { - resourceType: ReferenceDataResourceType.CountryOfRegistration, - resourceKey: 'f', - description: 'France - F', - }, - { - resourceType: ReferenceDataResourceType.CountryOfRegistration, - resourceKey: 'd', - description: 'Germany - D', - }, - { - resourceType: ReferenceDataResourceType.CountryOfRegistration, - resourceKey: 'gr', - description: 'Greece - GR', - }, - { - resourceType: ReferenceDataResourceType.CountryOfRegistration, - resourceKey: 'h', - description: 'Hungary - H', - }, - { - resourceType: ReferenceDataResourceType.CountryOfRegistration, - resourceKey: 'irl', - description: 'Ireland - IRL', - }, - { - resourceType: ReferenceDataResourceType.CountryOfRegistration, - resourceKey: 'i', - description: 'Italy - I', - }, - { - resourceType: ReferenceDataResourceType.CountryOfRegistration, - resourceKey: 'lv', - description: 'Latvia - LV', - }, - { - resourceType: ReferenceDataResourceType.CountryOfRegistration, - resourceKey: 'lt', - description: 'Lithuania - LT', - }, - { - resourceType: ReferenceDataResourceType.CountryOfRegistration, - resourceKey: 'l', - description: 'Luxembourg - L', - }, - { - resourceType: ReferenceDataResourceType.CountryOfRegistration, - resourceKey: 'm', - description: 'Malta - M', - }, - { - resourceType: ReferenceDataResourceType.CountryOfRegistration, - resourceKey: 'nl', - description: 'Netherlands - NL', - }, - { - resourceType: ReferenceDataResourceType.CountryOfRegistration, - resourceKey: 'n', - description: 'Norway - N', - }, - { - resourceType: ReferenceDataResourceType.CountryOfRegistration, - resourceKey: 'pl', - description: 'Poland - PL', - }, - { - resourceType: ReferenceDataResourceType.CountryOfRegistration, - resourceKey: 'p', - description: 'Portugal - P', - }, - { - resourceType: ReferenceDataResourceType.CountryOfRegistration, - resourceKey: 'ro', - description: 'Romania - RO', - }, - { - resourceType: ReferenceDataResourceType.CountryOfRegistration, - resourceKey: 'sk', - description: 'Slovakia - SK', - }, - { - resourceType: ReferenceDataResourceType.CountryOfRegistration, - resourceKey: 'slo', - description: 'Slovenia - SLO', - }, - { - resourceType: ReferenceDataResourceType.CountryOfRegistration, - resourceKey: 'e', - description: 'Spain - E', - }, - { - resourceType: ReferenceDataResourceType.CountryOfRegistration, - resourceKey: 's', - description: 'Sweden - S', - }, - { - resourceType: ReferenceDataResourceType.CountryOfRegistration, - resourceKey: 'ch', - description: 'Switzerland - CH', - }, - { - resourceType: ReferenceDataResourceType.CountryOfRegistration, - resourceKey: 'non-eu', - description: 'Non EU', - }, - { - resourceType: ReferenceDataResourceType.CountryOfRegistration, - resourceKey: 'not-known', - description: 'Country Not Known', - }, + { + resourceType: ReferenceDataResourceType.CountryOfRegistration, + resourceKey: 'gb', + description: 'Great Britain and Northern Ireland - GB', + }, + { + resourceType: ReferenceDataResourceType.CountryOfRegistration, + resourceKey: 'gba', + description: 'Alderney - GBA', + }, + { + resourceType: ReferenceDataResourceType.CountryOfRegistration, + resourceKey: 'gbg', + description: 'Guernsey - GBG', + }, + { + resourceType: ReferenceDataResourceType.CountryOfRegistration, + resourceKey: 'gbj', + description: 'Jersey - GBJ', + }, + { + resourceType: ReferenceDataResourceType.CountryOfRegistration, + resourceKey: 'gbm', + description: 'Isle of Man - GBM', + }, + { + resourceType: ReferenceDataResourceType.CountryOfRegistration, + resourceKey: 'gbz', + description: 'Gibraltar - GBZ', + }, + { + resourceType: ReferenceDataResourceType.CountryOfRegistration, + resourceKey: 'a', + description: 'Austria - A', + }, + { + resourceType: ReferenceDataResourceType.CountryOfRegistration, + resourceKey: 'b', + description: 'Belgium - B', + }, + { + resourceType: ReferenceDataResourceType.CountryOfRegistration, + resourceKey: 'bih', + description: 'BosniaAndHerzegovina - BIH', + }, + { + resourceType: ReferenceDataResourceType.CountryOfRegistration, + resourceKey: 'bg', + description: 'Bulgaria - BG', + }, + { + resourceType: ReferenceDataResourceType.CountryOfRegistration, + resourceKey: 'hr', + description: 'Croatia - HR', + }, + { + resourceType: ReferenceDataResourceType.CountryOfRegistration, + resourceKey: 'cy', + description: 'Cyprus - CY', + }, + { + resourceType: ReferenceDataResourceType.CountryOfRegistration, + resourceKey: 'cz', + description: 'CzechRepublic - CZ', + }, + { + resourceType: ReferenceDataResourceType.CountryOfRegistration, + resourceKey: 'dk', + description: 'Denmark - DK', + }, + { + resourceType: ReferenceDataResourceType.CountryOfRegistration, + resourceKey: 'est', + description: 'Estonia - EST', + }, + { + resourceType: ReferenceDataResourceType.CountryOfRegistration, + resourceKey: 'fin', + description: 'Finland - FIN', + }, + { + resourceType: ReferenceDataResourceType.CountryOfRegistration, + resourceKey: 'f', + description: 'France - F', + }, + { + resourceType: ReferenceDataResourceType.CountryOfRegistration, + resourceKey: 'd', + description: 'Germany - D', + }, + { + resourceType: ReferenceDataResourceType.CountryOfRegistration, + resourceKey: 'gr', + description: 'Greece - GR', + }, + { + resourceType: ReferenceDataResourceType.CountryOfRegistration, + resourceKey: 'h', + description: 'Hungary - H', + }, + { + resourceType: ReferenceDataResourceType.CountryOfRegistration, + resourceKey: 'irl', + description: 'Ireland - IRL', + }, + { + resourceType: ReferenceDataResourceType.CountryOfRegistration, + resourceKey: 'i', + description: 'Italy - I', + }, + { + resourceType: ReferenceDataResourceType.CountryOfRegistration, + resourceKey: 'lv', + description: 'Latvia - LV', + }, + { + resourceType: ReferenceDataResourceType.CountryOfRegistration, + resourceKey: 'lt', + description: 'Lithuania - LT', + }, + { + resourceType: ReferenceDataResourceType.CountryOfRegistration, + resourceKey: 'l', + description: 'Luxembourg - L', + }, + { + resourceType: ReferenceDataResourceType.CountryOfRegistration, + resourceKey: 'm', + description: 'Malta - M', + }, + { + resourceType: ReferenceDataResourceType.CountryOfRegistration, + resourceKey: 'nl', + description: 'Netherlands - NL', + }, + { + resourceType: ReferenceDataResourceType.CountryOfRegistration, + resourceKey: 'n', + description: 'Norway - N', + }, + { + resourceType: ReferenceDataResourceType.CountryOfRegistration, + resourceKey: 'pl', + description: 'Poland - PL', + }, + { + resourceType: ReferenceDataResourceType.CountryOfRegistration, + resourceKey: 'p', + description: 'Portugal - P', + }, + { + resourceType: ReferenceDataResourceType.CountryOfRegistration, + resourceKey: 'ro', + description: 'Romania - RO', + }, + { + resourceType: ReferenceDataResourceType.CountryOfRegistration, + resourceKey: 'sk', + description: 'Slovakia - SK', + }, + { + resourceType: ReferenceDataResourceType.CountryOfRegistration, + resourceKey: 'slo', + description: 'Slovenia - SLO', + }, + { + resourceType: ReferenceDataResourceType.CountryOfRegistration, + resourceKey: 'e', + description: 'Spain - E', + }, + { + resourceType: ReferenceDataResourceType.CountryOfRegistration, + resourceKey: 's', + description: 'Sweden - S', + }, + { + resourceType: ReferenceDataResourceType.CountryOfRegistration, + resourceKey: 'ch', + description: 'Switzerland - CH', + }, + { + resourceType: ReferenceDataResourceType.CountryOfRegistration, + resourceKey: 'non-eu', + description: 'Non EU', + }, + { + resourceType: ReferenceDataResourceType.CountryOfRegistration, + resourceKey: 'not-known', + description: 'Country Not Known', + }, ]; diff --git a/src/mocks/test-result.mock.ts b/src/mocks/test-result.mock.ts index daf66f0ddb..ef9a6f471d 100644 --- a/src/mocks/test-result.mock.ts +++ b/src/mocks/test-result.mock.ts @@ -5,25 +5,25 @@ import { OdometerReadingUnits } from '@models/test-types/odometer-unit.enum'; import { VehicleTypes } from '@models/vehicle-tech-record.model'; export const createMockTestResult = (params: Partial = {}): TestResultModel => ({ - testResultId: '', - systemNumber: '', - vin: '', - testStartTimestamp: '', - testEndTimestamp: '', - testTypes: [], - trailerId: 'trailerId', - countryOfRegistration: '', - euVehicleCategory: EUVehicleCategory.M1, - odometerReading: 0, - odometerReadingUnits: OdometerReadingUnits.KILOMETRES, - preparerName: '', - preparerId: '', - testStationName: '', - testStationPNumber: '', - testStationType: TestStationType.ATF, - testerName: '', - testerEmailAddress: '', - testerStaffId: '', - vehicleType: VehicleTypes.CAR, - ...params, + testResultId: '', + systemNumber: '', + vin: '', + testStartTimestamp: '', + testEndTimestamp: '', + testTypes: [], + trailerId: 'trailerId', + countryOfRegistration: '', + euVehicleCategory: EUVehicleCategory.M1, + odometerReading: 0, + odometerReadingUnits: OdometerReadingUnits.KILOMETRES, + preparerName: '', + preparerId: '', + testStationName: '', + testStationPNumber: '', + testStationType: TestStationType.ATF, + testerName: '', + testerEmailAddress: '', + testerStaffId: '', + vehicleType: VehicleTypes.CAR, + ...params, }); diff --git a/src/mocks/test-type-category.mock.ts b/src/mocks/test-type-category.mock.ts index b6a45226ef..d1ab3acfa4 100644 --- a/src/mocks/test-type-category.mock.ts +++ b/src/mocks/test-type-category.mock.ts @@ -1,8 +1,8 @@ import { TestTypeCategory } from '@api/test-types'; export const createMockTestTypeCategory = (params: Partial = {}): TestTypeCategory => ({ - id: 'testTypeCategoryId', - name: 'testNameCategoryName', - forVehicleType: ['car'], - ...params, + id: 'testTypeCategoryId', + name: 'testNameCategoryName', + forVehicleType: ['car'], + ...params, }); diff --git a/src/mocks/test-type.mock.ts b/src/mocks/test-type.mock.ts index ef15adda5e..18f60e2151 100644 --- a/src/mocks/test-type.mock.ts +++ b/src/mocks/test-type.mock.ts @@ -1,47 +1,47 @@ -import { - EmissionStandard, FuelType, ModTypeCode, ModeTypeDescription, -} from '@models/test-types/emissions.enum'; +import { EmissionStandard, FuelType, ModTypeCode, ModeTypeDescription } from '@models/test-types/emissions.enum'; import { TestType, resultOfTestEnum } from '@models/test-types/test-type.model'; export const createMockTestType = (params: Partial = {}): TestType => ({ - testTypeId: 'testTypeId', - testNumber: 'testNumber', - name: 'testName', - testCode: 'testCode', - testTypeName: 'testTypeName', - testTypeStartTimestamp: '', - testTypeEndTimestamp: '', - testExpiryDate: '', - certificateNumber: '', - reasonForAbandoning: '', - testAnniversaryDate: 'testAnniversaryDate', - prohibitionIssued: false, - testResult: resultOfTestEnum.fail, - seatbeltInstallationCheckDate: false, - numberOfSeatbeltsFitted: 0, - lastSeatbeltInstallationCheckDate: '', - emissionStandard: EmissionStandard.Euro3, - smokeTestKLimitApplied: 'smokeTestKLimitApplied', - fuelType: FuelType.Diesel, - modificationTypeUsed: 'modificationTypeUsed', - particulateTrapFitted: 'particulateTrapFitted', - particulateTrapSerialNumber: 'particulateTrapSerialNumber', - modType: { - code: ModTypeCode.g, - description: ModeTypeDescription.Engine, - }, - customDefects: [], - additionalNotesRecorded: '', - requiredStandards: [{ - sectionNumber: 'string', - sectionDescription: 'string', - rsNumber: 1, - requiredStandard: 'string', - refCalculation: 'string', - additionalInfo: false, - inspectionTypes: ['basic'], - prs: false, - additionalNotes: 'string', - }], - ...params, + testTypeId: 'testTypeId', + testNumber: 'testNumber', + name: 'testName', + testCode: 'testCode', + testTypeName: 'testTypeName', + testTypeStartTimestamp: '', + testTypeEndTimestamp: '', + testExpiryDate: '', + certificateNumber: '', + reasonForAbandoning: '', + testAnniversaryDate: 'testAnniversaryDate', + prohibitionIssued: false, + testResult: resultOfTestEnum.fail, + seatbeltInstallationCheckDate: false, + numberOfSeatbeltsFitted: 0, + lastSeatbeltInstallationCheckDate: '', + emissionStandard: EmissionStandard.Euro3, + smokeTestKLimitApplied: 'smokeTestKLimitApplied', + fuelType: FuelType.Diesel, + modificationTypeUsed: 'modificationTypeUsed', + particulateTrapFitted: 'particulateTrapFitted', + particulateTrapSerialNumber: 'particulateTrapSerialNumber', + modType: { + code: ModTypeCode.g, + description: ModeTypeDescription.Engine, + }, + customDefects: [], + additionalNotesRecorded: '', + requiredStandards: [ + { + sectionNumber: 'string', + sectionDescription: 'string', + rsNumber: 1, + requiredStandard: 'string', + refCalculation: 'string', + additionalInfo: false, + inspectionTypes: ['basic'], + prs: false, + additionalNotes: 'string', + }, + ], + ...params, }); diff --git a/src/mocks/trl-record.mock.ts b/src/mocks/trl-record.mock.ts index d8508044ae..40d7064aa1 100644 --- a/src/mocks/trl-record.mock.ts +++ b/src/mocks/trl-record.mock.ts @@ -5,43 +5,43 @@ import { TechRecordType } from '@dvsa/cvs-type-definitions/types/v3/tech-record/ import { FrameDescriptions, StatusCodes } from '@models/vehicle-tech-record.model'; export const createMockTrl = (systemNumber: number): TechRecordType<'trl'> => ({ - systemNumber: 'TRL', - vin: `XMGDE04FS0H0${12344 + systemNumber + 1}`, - trailerId: 'TestId', - techRecord_createdAt: new Date().toISOString(), - techRecord_createdByName: 'Nathan', - techRecord_statusCode: StatusCodes.CURRENT, - techRecord_vehicleType: 'trl', - techRecord_regnDate: '1234', - techRecord_firstUseDate: '1234', - techRecord_manufactureYear: 2022, - techRecord_noOfAxles: 2, - techRecord_brakes_dtpNumber: '1234', - techRecord_brakes_loadSensingValve: true, - techRecord_brakes_antilockBrakingSystem: true, - techRecord_axles: undefined, - techRecord_dimensions_length: 25000, - techRecord_dimensions_width: 10000, - techRecord_suspensionType: '1', - techRecord_roadFriendly: true, - techRecord_vehicleClass_description: 'trailer', - techRecord_vehicleClass_code: 't', - techRecord_vehicleConfiguration: VehicleConfiguration.SEMI_TRAILER, - techRecord_couplingType: '1', - techRecord_maxLoadOnCoupling: 1234, - techRecord_frameDescription: FrameDescriptions.FRAME_SECTION, - techRecord_euVehicleCategory: EUVehicleCategory.M1, - techRecord_departmentalVehicleMarker: true, - techRecord_reasonForCreation: 'Brake Failure', - techRecord_approvalType: ApprovalType.ECSSTA, - techRecord_approvalTypeNumber: 'approval123', - techRecord_ntaNumber: 'nta789', - techRecord_variantNumber: 'variant123456', - techRecord_variantVersionNumber: 'variantversion123456', - techRecord_plates: undefined, - createdTimestamp: new Date().toISOString(), - partialVin: 'vin', - techRecord_bodyType_code: '', - techRecord_bodyType_description: '', - techRecord_createdById: '', + systemNumber: 'TRL', + vin: `XMGDE04FS0H0${12344 + systemNumber + 1}`, + trailerId: 'TestId', + techRecord_createdAt: new Date().toISOString(), + techRecord_createdByName: 'Nathan', + techRecord_statusCode: StatusCodes.CURRENT, + techRecord_vehicleType: 'trl', + techRecord_regnDate: '1234', + techRecord_firstUseDate: '1234', + techRecord_manufactureYear: 2022, + techRecord_noOfAxles: 2, + techRecord_brakes_dtpNumber: '1234', + techRecord_brakes_loadSensingValve: true, + techRecord_brakes_antilockBrakingSystem: true, + techRecord_axles: undefined, + techRecord_dimensions_length: 25000, + techRecord_dimensions_width: 10000, + techRecord_suspensionType: '1', + techRecord_roadFriendly: true, + techRecord_vehicleClass_description: 'trailer', + techRecord_vehicleClass_code: 't', + techRecord_vehicleConfiguration: VehicleConfiguration.SEMI_TRAILER, + techRecord_couplingType: '1', + techRecord_maxLoadOnCoupling: 1234, + techRecord_frameDescription: FrameDescriptions.FRAME_SECTION, + techRecord_euVehicleCategory: EUVehicleCategory.M1, + techRecord_departmentalVehicleMarker: true, + techRecord_reasonForCreation: 'Brake Failure', + techRecord_approvalType: ApprovalType.ECSSTA, + techRecord_approvalTypeNumber: 'approval123', + techRecord_ntaNumber: 'nta789', + techRecord_variantNumber: 'variant123456', + techRecord_variantVersionNumber: 'variantversion123456', + techRecord_plates: undefined, + createdTimestamp: new Date().toISOString(), + partialVin: 'vin', + techRecord_bodyType_code: '', + techRecord_bodyType_description: '', + techRecord_createdById: '', }); diff --git a/tsconfig.json b/tsconfig.json index 9ce4ba7648..baea4adc92 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -5,22 +5,21 @@ "outDir": "./dist/out-tsc", "forceConsistentCasingInFileNames": true, "strict": true, + "esModuleInterop": true, "noImplicitOverride": true, "noPropertyAccessFromIndexSignature": true, "noImplicitReturns": true, "noFallthroughCasesInSwitch": true, "sourceMap": true, "declaration": false, - "downlevelIteration": true, "experimentalDecorators": true, "moduleResolution": "node", "resolveJsonModule": true, "importHelpers": true, - "target": "es2017", - "allowSyntheticDefaultImports": true, - "module": "es2020", + "target": "ES2022", + "module": "ES2022", "lib": [ - "es2020", + "es2023", "dom" ], "paths": { @@ -33,7 +32,8 @@ "@services/*": ["src/app/services/*"], "@store/*": ["src/app/store/*"], "@shared/*": ["src/app/shared/*"], - "@directives/*": ["src/app/directives/*"] + "@directives/*": ["src/app/directives/*"], + "govuk-frontend/*": ["node_modules/govuk-frontend/*"] } }, "angularCompilerOptions": { @@ -42,5 +42,5 @@ "strictInputAccessModifiers": true, "strictTemplates": true }, - "exclude": ["cypress.config.ts", "cypress", "node_modules"] + "exclude": ["node_modules"] }
{{ result.lastUpdatedBy }} - {{ result.createdAtDate | date: 'dd/MM/yyyy HH:mm' | defaultNullOrEmpty }} + {{ result.createdAtDate | date : 'dd/MM/yyyy HH:mm' | defaultNullOrEmpty }}