diff --git a/package-lock.json b/package-lock.json index de0a70e2..a062bda3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -63,7 +63,7 @@ "typescript-eslint": "^8.11.0", "vite": "^6.4.1", "vitest": "^3.2.4", - "volar-service-vetur": "latest", + "volar-service-vetur": "*", "wait-on": "^8.0.2" } }, @@ -4419,6 +4419,28 @@ "node": ">=10" } }, + "node_modules/@types/eslint": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz", + "integrity": "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==", + "dev": true, + "license": "MIT", + "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, + "license": "MIT", + "dependencies": { + "@types/eslint": "*", + "@types/estree": "*" + } + }, "node_modules/@types/estree": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", @@ -5999,148 +6021,163 @@ "dev": true }, "node_modules/@webassemblyjs/ast": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.12.1.tgz", - "integrity": "sha512-EKfMUOPRRUTy5UII4qJDGPpqfwjOmZ5jeGFwid9mnoqIFK+e0vqoi1qH56JpmZSzEL53jKnNzScdmftJyG5xWg==", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz", + "integrity": "sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==", "dev": true, + "license": "MIT", "dependencies": { - "@webassemblyjs/helper-numbers": "1.11.6", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6" + "@webassemblyjs/helper-numbers": "1.13.2", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2" } }, "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 + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.13.2.tgz", + "integrity": "sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==", + "dev": true, + "license": "MIT" }, "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 + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.13.2.tgz", + "integrity": "sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==", + "dev": true, + "license": "MIT" }, "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 + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.14.1.tgz", + "integrity": "sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==", + "dev": true, + "license": "MIT" }, "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==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.13.2.tgz", + "integrity": "sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==", "dev": true, + "license": "MIT", "dependencies": { - "@webassemblyjs/floating-point-hex-parser": "1.11.6", - "@webassemblyjs/helper-api-error": "1.11.6", + "@webassemblyjs/floating-point-hex-parser": "1.13.2", + "@webassemblyjs/helper-api-error": "1.13.2", "@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 + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.13.2.tgz", + "integrity": "sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==", + "dev": true, + "license": "MIT" }, "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==", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.14.1.tgz", + "integrity": "sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==", "dev": true, + "license": "MIT", "dependencies": { - "@webassemblyjs/ast": "1.12.1", - "@webassemblyjs/helper-buffer": "1.12.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6", - "@webassemblyjs/wasm-gen": "1.12.1" + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/wasm-gen": "1.14.1" } }, "node_modules/@webassemblyjs/ieee754": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.6.tgz", - "integrity": "sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.13.2.tgz", + "integrity": "sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==", "dev": true, + "license": "MIT", "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==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.13.2.tgz", + "integrity": "sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==", "dev": true, + "license": "Apache-2.0", "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 + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.13.2.tgz", + "integrity": "sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==", + "dev": true, + "license": "MIT" }, "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==", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.14.1.tgz", + "integrity": "sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==", "dev": true, + "license": "MIT", "dependencies": { - "@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" + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/helper-wasm-section": "1.14.1", + "@webassemblyjs/wasm-gen": "1.14.1", + "@webassemblyjs/wasm-opt": "1.14.1", + "@webassemblyjs/wasm-parser": "1.14.1", + "@webassemblyjs/wast-printer": "1.14.1" } }, "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==", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.14.1.tgz", + "integrity": "sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==", "dev": true, + "license": "MIT", "dependencies": { - "@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" + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/ieee754": "1.13.2", + "@webassemblyjs/leb128": "1.13.2", + "@webassemblyjs/utf8": "1.13.2" } }, "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==", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.14.1.tgz", + "integrity": "sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==", "dev": true, + "license": "MIT", "dependencies": { - "@webassemblyjs/ast": "1.12.1", - "@webassemblyjs/helper-buffer": "1.12.1", - "@webassemblyjs/wasm-gen": "1.12.1", - "@webassemblyjs/wasm-parser": "1.12.1" + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/wasm-gen": "1.14.1", + "@webassemblyjs/wasm-parser": "1.14.1" } }, "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==", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.14.1.tgz", + "integrity": "sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==", "dev": true, + "license": "MIT", "dependencies": { - "@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" + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-api-error": "1.13.2", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/ieee754": "1.13.2", + "@webassemblyjs/leb128": "1.13.2", + "@webassemblyjs/utf8": "1.13.2" } }, "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==", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.14.1.tgz", + "integrity": "sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==", "dev": true, + "license": "MIT", "dependencies": { - "@webassemblyjs/ast": "1.12.1", + "@webassemblyjs/ast": "1.14.1", "@xtuc/long": "4.2.2" } }, @@ -6148,13 +6185,15 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", - "dev": true + "dev": true, + "license": "BSD-3-Clause" }, "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 + "dev": true, + "license": "Apache-2.0" }, "node_modules/abab": { "version": "2.0.6", @@ -6225,13 +6264,17 @@ "node": ">=0.4.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==", + "node_modules/acorn-import-phases": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/acorn-import-phases/-/acorn-import-phases-1.0.4.tgz", + "integrity": "sha512-wKmbr/DDiIXzEOiWrTTUcDm24kQ2vGfZQvM2fwg2vXqR5uW6aapr7ObPtj1th32b9u90/Pf4AItvdTh42fBmVQ==", "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.13.0" + }, "peerDependencies": { - "acorn": "^8" + "acorn": "^8.14.0" } }, "node_modules/acorn-jsx": { @@ -6999,6 +7042,16 @@ } ] }, + "node_modules/baseline-browser-mapping": { + "version": "2.9.19", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.19.tgz", + "integrity": "sha512-ipDqC8FrAl/76p2SSWKSI+H9tFwm7vYqXQrItCuiVPt26Km0jS+NzSsBWAaBusvSbQcfJG+JitdMm+wZAgTYqg==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.js" + } + }, "node_modules/batch": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", @@ -7156,9 +7209,9 @@ "dev": true }, "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==", + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz", + "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==", "dev": true, "funding": [ { @@ -7174,11 +7227,13 @@ "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "dependencies": { - "caniuse-lite": "^1.0.30001646", - "electron-to-chromium": "^1.5.4", - "node-releases": "^2.0.18", - "update-browserslist-db": "^1.1.0" + "baseline-browser-mapping": "^2.9.0", + "caniuse-lite": "^1.0.30001759", + "electron-to-chromium": "^1.5.263", + "node-releases": "^2.0.27", + "update-browserslist-db": "^1.2.0" }, "bin": { "browserslist": "cli.js" @@ -7393,9 +7448,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001653", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001653.tgz", - "integrity": "sha512-XGWQVB8wFQ2+9NZwZ10GxTYC5hk0Fa+q8cSkr0tgvMhYhMHP/QC+WTgrePMDBWiWc/pV+1ik82Al20XOK25Gcw==", + "version": "1.0.30001768", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001768.tgz", + "integrity": "sha512-qY3aDRZC5nWPgHUgIB84WL+nySuo19wk0VJpp/XI9T34lrvkyhRvNVOFJOp2kxClQhiFBu+TaUSudf6oa3vkSA==", "dev": true, "funding": [ { @@ -7410,7 +7465,8 @@ "type": "github", "url": "https://github.com/sponsors/ai" } - ] + ], + "license": "CC-BY-4.0" }, "node_modules/case-sensitive-paths-webpack-plugin": { "version": "2.4.0", @@ -9295,10 +9351,11 @@ "dev": true }, "node_modules/electron-to-chromium": { - "version": "1.5.13", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.13.tgz", - "integrity": "sha512-lbBcvtIJ4J6sS4tb5TLp1b4LyfCdMkwStzXPyAgVgTRAsep4bvrAGaBOP7ZJtQMNJpSQ9SqG4brWOroNaQtm7Q==", - "dev": true + "version": "1.5.286", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.286.tgz", + "integrity": "sha512-9tfDXhJ4RKFNerfjdCcZfufu49vg620741MNs26a9+bhLThdB+plgMeou98CAaHu/WATj2iHOOHTp1hWtABj2A==", + "dev": true, + "license": "ISC" }, "node_modules/emittery": { "version": "0.8.1", @@ -9373,13 +9430,14 @@ } }, "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==", + "version": "5.19.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.19.0.tgz", + "integrity": "sha512-phv3E1Xl4tQOShqSte26C7Fl84EwUdZsyOuSSk9qtAGyyQs2s3jJzComh+Abf4g187lUUAvH+H26omrqia2aGg==", "dev": true, + "license": "MIT", "dependencies": { "graceful-fs": "^4.2.4", - "tapable": "^2.2.0" + "tapable": "^2.3.0" }, "engines": { "node": ">=10.13.0" @@ -9556,10 +9614,11 @@ } }, "node_modules/escalade": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", - "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } @@ -10355,6 +10414,23 @@ "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", "dev": true }, + "node_modules/fast-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", + "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, "node_modules/fastq": { "version": "1.13.0", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", @@ -10904,7 +10980,8 @@ "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 + "dev": true, + "license": "BSD-2-Clause" }, "node_modules/globals": { "version": "11.12.0", @@ -15186,12 +15263,17 @@ } }, "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==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.1.tgz", + "integrity": "sha512-IWqP2SCPhyVFTBtRcgMHdzlf9ul25NwaFx4wCEH/KjAXuuHY4yNjvPXsBokp8jCB936PyWRaPKUNh8NvylLp2Q==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.11.5" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" } }, "node_modules/loader-utils": { @@ -16372,10 +16454,11 @@ "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 + "version": "2.0.27", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz", + "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==", + "dev": true, + "license": "MIT" }, "node_modules/nopt": { "version": "6.0.0", @@ -19604,12 +19687,17 @@ "dev": true }, "node_modules/tapable": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", - "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.0.tgz", + "integrity": "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" } }, "node_modules/tar": { @@ -19743,16 +19831,17 @@ } }, "node_modules/terser-webpack-plugin": { - "version": "5.3.10", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.10.tgz", - "integrity": "sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w==", + "version": "5.3.16", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.16.tgz", + "integrity": "sha512-h9oBFCWrq78NyWWVcSwZarJkZ01c2AyGrzs1crmHZO3QUg9D61Wu4NPjBy69n7JqylFF5y+CsUZYmYEIZ3mR+Q==", "dev": true, + "license": "MIT", "dependencies": { - "@jridgewell/trace-mapping": "^0.3.20", + "@jridgewell/trace-mapping": "^0.3.25", "jest-worker": "^27.4.5", - "schema-utils": "^3.1.1", - "serialize-javascript": "^6.0.1", - "terser": "^5.26.0" + "schema-utils": "^4.3.0", + "serialize-javascript": "^6.0.2", + "terser": "^5.31.1" }, "engines": { "node": ">= 10.13.0" @@ -19776,6 +19865,63 @@ } } }, + "node_modules/terser-webpack-plugin/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==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/terser-webpack-plugin/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, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/terser-webpack-plugin/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==", + "dev": true, + "license": "MIT" + }, + "node_modules/terser-webpack-plugin/node_modules/schema-utils": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.3.tgz", + "integrity": "sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, "node_modules/terser/node_modules/commander": { "version": "2.20.3", "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", @@ -20411,9 +20557,9 @@ } }, "node_modules/update-browserslist-db": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.0.tgz", - "integrity": "sha512-EdRAaAyk2cUE1wOf2DkEhzxqOQvFOoRJFNS6NeyJ01Gp2beMRpBAINjM2iDXE3KCuKhwnvHIQCJm6ThL2Z+HzQ==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", "dev": true, "funding": [ { @@ -20429,9 +20575,10 @@ "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "dependencies": { - "escalade": "^3.1.2", - "picocolors": "^1.0.1" + "escalade": "^3.2.0", + "picocolors": "^1.1.1" }, "bin": { "update-browserslist-db": "cli.js" @@ -21516,10 +21663,11 @@ } }, "node_modules/watchpack": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.2.tgz", - "integrity": "sha512-TnbFSbcOCcDgjZ4piURLCbJ3nJhznVh9kw6F6iokjiFPl8ONxe9A6nMDVXDiNbrSfLILs6vB07F7wLBrwPYzJw==", + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.5.1.tgz", + "integrity": "sha512-Zn5uXdcFNIA1+1Ei5McRd+iRzfhENPCe7LeABkJtNulSxjma+l7ltNx55BWZkRlwRnpOgHqxnjyaDgJnNXnqzg==", "dev": true, + "license": "MIT", "dependencies": { "glob-to-regexp": "^0.4.1", "graceful-fs": "^4.1.2" @@ -21556,34 +21704,37 @@ } }, "node_modules/webpack": { - "version": "5.94.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.94.0.tgz", - "integrity": "sha512-KcsGn50VT+06JH/iunZJedYGUJS5FGjow8wb9c0v5n1Om8O1g4L6LjtfxwlXIATopoQu+vOXXa7gYisWxCoPyg==", + "version": "5.105.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.105.0.tgz", + "integrity": "sha512-gX/dMkRQc7QOMzgTe6KsYFM7DxeIONQSui1s0n/0xht36HvrgbxtM1xBlgx596NbpHuQU8P7QpKwrZYwUX48nw==", "dev": true, + "license": "MIT", "dependencies": { - "@types/estree": "^1.0.5", - "@webassemblyjs/ast": "^1.12.1", - "@webassemblyjs/wasm-edit": "^1.12.1", - "@webassemblyjs/wasm-parser": "^1.12.1", - "acorn": "^8.7.1", - "acorn-import-attributes": "^1.9.5", - "browserslist": "^4.21.10", + "@types/eslint-scope": "^3.7.7", + "@types/estree": "^1.0.8", + "@types/json-schema": "^7.0.15", + "@webassemblyjs/ast": "^1.14.1", + "@webassemblyjs/wasm-edit": "^1.14.1", + "@webassemblyjs/wasm-parser": "^1.14.1", + "acorn": "^8.15.0", + "acorn-import-phases": "^1.0.3", + "browserslist": "^4.28.1", "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.17.1", - "es-module-lexer": "^1.2.1", + "enhanced-resolve": "^5.19.0", + "es-module-lexer": "^2.0.0", "eslint-scope": "5.1.1", "events": "^3.2.0", "glob-to-regexp": "^0.4.1", "graceful-fs": "^4.2.11", "json-parse-even-better-errors": "^2.3.1", - "loader-runner": "^4.2.0", + "loader-runner": "^4.3.1", "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.1", - "webpack-sources": "^3.2.3" + "schema-utils": "^4.3.3", + "tapable": "^2.3.0", + "terser-webpack-plugin": "^5.3.16", + "watchpack": "^2.5.1", + "webpack-sources": "^3.3.3" }, "bin": { "webpack": "bin/webpack.js" @@ -21945,10 +22096,11 @@ } }, "node_modules/webpack-sources": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", - "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.3.3.tgz", + "integrity": "sha512-yd1RBzSGanHkitROoPFd6qsrxt+oFhg/129YzheDGqeustzX0vTZJZsSsQjVQC4yzBQ56K55XU8gaNCtIzOnTg==", "dev": true, + "license": "MIT", "engines": { "node": ">=10.13.0" } @@ -21959,6 +22111,70 @@ "integrity": "sha512-h9atBP/bsZohWpHnr+2sic8Iecb60GxftXsWNLLLSqewgIsGzByd2gcIID4nXcG+3tNe4GQG3dLcff3kXupdRA==", "dev": true }, + "node_modules/webpack/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==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/webpack/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, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/webpack/node_modules/es-module-lexer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.0.0.tgz", + "integrity": "sha512-5POEcUuZybH7IdmGsD8wlf0AI55wMecM9rVBTI/qEAy2c1kTOm3DjFYjrBdI2K3BaJjJYfYFeRtM0t9ssnRuxw==", + "dev": true, + "license": "MIT" + }, + "node_modules/webpack/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==", + "dev": true, + "license": "MIT" + }, + "node_modules/webpack/node_modules/schema-utils": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.3.tgz", + "integrity": "sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, "node_modules/websocket-driver": { "version": "0.7.4", "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", diff --git a/src/constants/error-messages.js b/src/constants/error-messages.js index 81f0fad0..224c6e30 100644 --- a/src/constants/error-messages.js +++ b/src/constants/error-messages.js @@ -65,7 +65,9 @@ const errorMessages = Object.freeze({ STYLE_REQUIRED: 'Vehicle style is required.', MOBILE_LOCATION_REQUIRED: 'Please enter your service address', DATE_REQUIRED: 'Please select a date', - TIME_REQUIRED: 'Please select an appointment time.' + TIME_REQUIRED: 'Please select an appointment time.', + PRIMARY_PHONE_REQUIRED: 'Primary phone number is required.', + BAILOUT_EMAIL_ADDRESS_REQUIRED: 'Email address is required.' }); export default errorMessages; diff --git a/src/constants/global-rules.js b/src/constants/global-rules.js index e82b118a..878b7a27 100644 --- a/src/constants/global-rules.js +++ b/src/constants/global-rules.js @@ -26,7 +26,9 @@ const globalRules = Object.freeze({ EXTENSION_FORMAT: 'extension-format', ZIP_CODE_REQUIRED: 'zip-code-required', ZIP_CODE_SEARCH_FORMAT: 'zip-code-search-format', - OPTION_REQUIRED: 'option-required' + OPTION_REQUIRED: 'option-required', + PRIMARY_PHONE_REQUIRED: 'primary-phone-required', + BAILOUT_EMAIL_ADDRESS_REQUIRED: 'bailout-email-address-required' }); export default globalRules; diff --git a/src/constants/progress-bar-mapper.js b/src/constants/progress-bar-mapper.js index e80fbb5c..efb6225d 100644 --- a/src/constants/progress-bar-mapper.js +++ b/src/constants/progress-bar-mapper.js @@ -53,10 +53,10 @@ export const pageProgressMapper = { 'schedule-page': { percent: 70 }, - 'contact-details': { - percent: 80 - }, 'service-packages': { + percent: 75 + }, + 'contact-details': { percent: 85 }, 'payment-method': { diff --git a/src/constants/vin-lookup-methods.js b/src/constants/vin-lookup-methods.js index 539bdb3d..fd12a0ee 100644 --- a/src/constants/vin-lookup-methods.js +++ b/src/constants/vin-lookup-methods.js @@ -1,7 +1,8 @@ const vinLookupMethodSelections = Object.freeze({ MANUALVIN: 'ManualVin', LICENSEPLATE: 'LicensePlate', - HOMEADDRESS: 'HomeAddress' + HOMEADDRESS: 'HomeAddress', + NOVIN: 'NoVin' }); export default vinLookupMethodSelections; diff --git a/src/digital-components/button-question/button-question.vue b/src/digital-components/button-question/button-question.vue index 68ca6d57..ca731b44 100644 --- a/src/digital-components/button-question/button-question.vue +++ b/src/digital-components/button-question/button-question.vue @@ -79,7 +79,7 @@
diff --git a/src/digital-components/date-picker/date-picker.vue b/src/digital-components/date-picker/date-picker.vue index b785a171..9d1a1443 100644 --- a/src/digital-components/date-picker/date-picker.vue +++ b/src/digital-components/date-picker/date-picker.vue @@ -559,6 +559,7 @@ export default { this.selectTimeSlotForDay(timeSlot); }, async setCalendarData(config = {}) { + // set selectable dates data config.initialShopTimeSlotsResponse.days.forEach((selectableDate) => { const dateObjectToPush = { date: selectableDate.date, diff --git a/src/digital-components/textarea-question/textarea-question.spec.js b/src/digital-components/textarea-question/textarea-question.spec.js index 27968aa1..c3d903a1 100644 --- a/src/digital-components/textarea-question/textarea-question.spec.js +++ b/src/digital-components/textarea-question/textarea-question.spec.js @@ -67,7 +67,7 @@ describe('textarea-question.vue', () => { }, mixins: [mockMixin] }); - const expected = `${maxLength}/${maxLength}`; + const expected = `${maxLength} characters allowed`; // Act // Assert diff --git a/src/digital-components/textarea-question/textarea-question.vue b/src/digital-components/textarea-question/textarea-question.vue index 60293dde..f6d94ebb 100644 --- a/src/digital-components/textarea-question/textarea-question.vue +++ b/src/digital-components/textarea-question/textarea-question.vue @@ -18,7 +18,7 @@ :id="inputId" :ref="inputId" v-model.trim="value" - class="form-control" + class="textarea-input" :name="inputId" :rows="[inputRows ?? 10]" :placeholder="placeholderText" @@ -32,8 +32,15 @@ @drop="trimOnPaste"> -

- {{ maxLength - characterCount }}/{{ maxLength }} characters remaining +

+ {{ maxLength }} characters allowed +

+

+ {{ maxLength - characterCount }} characters left

diff --git a/src/helpers/bailout-helper.js b/src/helpers/bailout-helper.js index 0191755d..b0d364c5 100644 --- a/src/helpers/bailout-helper.js +++ b/src/helpers/bailout-helper.js @@ -10,7 +10,7 @@ function canBailoutNavigateBack() { switch (code) { case BailoutCode.VehicleNotFound: - case BailoutCode.DoNotSeeMyShop: + case BailoutCode.RequestCallback: case BailoutCode.TPANotEnabled: return true; diff --git a/src/helpers/global-rule-definer.js b/src/helpers/global-rule-definer.js index 5aca4f66..b91f3dc0 100644 --- a/src/helpers/global-rule-definer.js +++ b/src/helpers/global-rule-definer.js @@ -24,6 +24,7 @@ function defineGlobalNameRules() { */ function defineGlobalEmailRules() { defineRule(globalRules.EMAIL_ADDRESS_REQUIRED, required(errorMessages.EMAIL_ADDRESS_REQUIRED)); + defineRule(globalRules.BAILOUT_EMAIL_ADDRESS_REQUIRED, required(errorMessages.BAILOUT_EMAIL_ADDRESS_REQUIRED)); defineRule( globalRules.EMAIL_ADDRESS_FORMAT, regex( @@ -71,6 +72,7 @@ function defineGlobalDateOfLossRules() { */ function defineGlobalPhoneNumberRules() { defineRule(globalRules.PHONE_NUMBER_REQUIRED, required(errorMessages.PHONE_NUMBER_REQUIRED)); + defineRule(globalRules.PRIMARY_PHONE_REQUIRED, required(errorMessages.PRIMARY_PHONE_REQUIRED)); defineRule( globalRules.PHONE_NUMBER_FORMAT, regex( diff --git a/src/iss-components/questions-page-layout/questions-page-layout.vue b/src/iss-components/questions-page-layout/questions-page-layout.vue index 4ce11fa1..97d06a51 100644 --- a/src/iss-components/questions-page-layout/questions-page-layout.vue +++ b/src/iss-components/questions-page-layout/questions-page-layout.vue @@ -33,9 +33,17 @@ ref="siteFooter" class="mt-5" cmsWidgetName="SiteFooterWidget" - :isForwardActionDisabled="!isMetaValid" + :isForwardButtonHidden="!isMetaValid" @backClicked="handleBackButtonAction" @ForwardClicked="handleForwardButtonAction" /> +
@@ -48,6 +56,8 @@ import alert from '@/ux-components/alert/alert.vue'; import questionChain from '@/digital-components/question-chain/question-chain.vue'; import siteSubHeader from '@/iss-components/site-sub-header/site-sub-header.vue'; import siteFooter from '@/iss-components/site-footer/site-footer.vue'; +import textLink from '@/ux-components/text-link/text-link.vue'; +import widgetFields from '@/constants/cms-widget-fields'; export default { name: 'questions-page', @@ -56,7 +66,8 @@ export default { alert, questionChain, siteSubHeader, - siteFooter + siteFooter, + textLink }, props: { isMetaValid: Boolean, @@ -68,7 +79,7 @@ export default { loadingText: Array, index: Number }, - emits: ['update:modelValue', 'forwardButtonAction', 'back-click'], + emits: ['update:modelValue', 'forwardButtonAction', 'back-click', 'need-help-click'], computed: { selectedAnswers: { get() { @@ -77,6 +88,9 @@ export default { set(newValue) { this.$emit('update:modelValue', newValue); } + }, + needHelpLinkText() { + return this.getCmsContent('NeedHelpLinkWidget', widgetFields.TEXT_BLOCK_WIDGET.TEXT); } }, methods: { @@ -98,6 +112,9 @@ export default { }, handleBackButtonAction() { this.$emit('back-click'); + }, + handleNeedHelpClick() { + this.$emit('need-help-click'); } } }; @@ -122,4 +139,7 @@ export default { } } } +.need-help-link-container { + margin-bottom: 2.25rem; +} diff --git a/src/layouts/bailout-page/__snapshots__/bailout-page.spec.js.snap b/src/layouts/bailout-page/__snapshots__/bailout-page.spec.js.snap index 62f2829b..b191b402 100644 --- a/src/layouts/bailout-page/__snapshots__/bailout-page.spec.js.snap +++ b/src/layouts/bailout-page/__snapshots__/bailout-page.spec.js.snap @@ -12,13 +12,14 @@ Object { "phoneNumber": "6145550909", }, "rules": Object { - "email": "email-required|email-address-format", - "firstName": "first-name-required", - "lastName": "last-name-required", - "phoneNumber": "phone-number-required|phone-number-format", + "email": "bailout-email-address-required|email-address-format", + "firstName": "policyholder-first-name-required", + "lastName": "policyholder-last-name-required", + "phoneNumber": "primary-phone-required|phone-number-format", }, "widget": Object { "defaultSiteHeader": "SiteSubHeaderWidget", + "disclaimerText": "DisclaimerWidget", "noTpa": "ContentGroupNoTPAWidget", "notSeeingPreferredShop": "ContentGroupNotSeeingPreferredShop", }, diff --git a/src/layouts/bailout-page/bailout-page.spec.js b/src/layouts/bailout-page/bailout-page.spec.js index 5b0c68ee..3ccd149b 100644 --- a/src/layouts/bailout-page/bailout-page.spec.js +++ b/src/layouts/bailout-page/bailout-page.spec.js @@ -156,6 +156,16 @@ describe('Bailout page', () => { // Assert expect(siteFooter.exists()).toBeTruthy(); }); + test('disclaimer text block', () => { + // Arrange + const wrapper = shallowMount(bailoutPage, getMountOptions()); + + // Act + const disclaimerText = wrapper.findComponent({ ref: 'disclaimerText' }); + + // Assert + expect(disclaimerText.exists()).toBeTruthy(); + }); }); describe('computed', () => { describe('subHeaderCmsWidgetName', () => { diff --git a/src/layouts/bailout-page/bailout-page.vue b/src/layouts/bailout-page/bailout-page.vue index a84f0dfe..db6aa074 100644 --- a/src/layouts/bailout-page/bailout-page.vue +++ b/src/layouts/bailout-page/bailout-page.vue @@ -20,28 +20,34 @@ :subContentProperty="subContentProperty" class="sub-header-content mt-2 mb-5" justification="left" /> - - +
+
+ +
+
+ +
+
+ @@ -77,6 +86,7 @@ import siteHeader from '@/iss-components/site-header/site-header.vue'; import siteSubHeader from '@/iss-components/site-sub-header/site-sub-header.vue'; import siteFooter from '@/iss-components/site-footer/site-footer.vue'; import textboxQuestion from '@/digital-components/textbox-question/textbox-question.vue'; +import textBlock from '@/digital-components/text-block/text-block.vue'; // Supporting files import BaseFormMixin from '@/mixins/base-form-mixin.js'; import { fetchCmsContentForPage } from '@/helpers/cms-content-helper'; @@ -99,7 +109,8 @@ export default { siteFooter, textboxQuestion, // eslint-disable-next-line vue/no-reserved-component-names - Form + Form, + textBlock }, mixins: [BaseFormMixin], async beforeRouteEnter(to, from, next) { @@ -130,13 +141,14 @@ export default { widget: { defaultSiteHeader: 'SiteSubHeaderWidget', noTpa: 'ContentGroupNoTPAWidget', - notSeeingPreferredShop: 'ContentGroupNotSeeingPreferredShop' + notSeeingPreferredShop: 'ContentGroupNotSeeingPreferredShop', + disclaimerText: 'DisclaimerWidget' }, rules: { - firstName: globalRules.FIRST_NAME_REQUIRED, - lastName: globalRules.LAST_NAME_REQUIRED, - phoneNumber: `${globalRules.PHONE_NUMBER_REQUIRED}|${globalRules.PHONE_NUMBER_FORMAT}`, - email: `${globalRules.EMAIL_ADDRESS_REQUIRED}|${globalRules.EMAIL_ADDRESS_FORMAT}` + firstName: globalRules.POLICYHOLDER_FIRST_NAME_REQUIRED, + lastName: globalRules.POLICYHOLDER_LAST_NAME_REQUIRED, + phoneNumber: `${globalRules.PRIMARY_PHONE_REQUIRED}|${globalRules.PHONE_NUMBER_FORMAT}`, + email: `${globalRules.BAILOUT_EMAIL_ADDRESS_REQUIRED}|${globalRules.EMAIL_ADDRESS_FORMAT}` } }; }, @@ -221,9 +233,7 @@ export default { }; - diff --git a/src/layouts/contact-confirmation/contact-confirmation.vue b/src/layouts/contact-confirmation/contact-confirmation.vue index 29c52745..2819efd6 100644 --- a/src/layouts/contact-confirmation/contact-confirmation.vue +++ b/src/layouts/contact-confirmation/contact-confirmation.vue @@ -24,6 +24,7 @@ import { fetchCmsContentForPage } from '@/helpers/cms-content-helper'; import issPageValues from '@/router/router-constants/issPage-values.js'; import settleAllPromises from '@/helpers/layout-helper'; import { useMainStore } from '@/store'; +import showIssLoadingModal from '@/helpers/loading-modal-helper'; export default { name: 'contact-confirmation', @@ -47,6 +48,7 @@ export default { next((vm) => { vm.setCmsContent(resultMap.cmsContent); }); + showIssLoadingModal(false); }, setup() { const mainStore = useMainStore(); diff --git a/src/layouts/contact-details/contact-details.spec.js b/src/layouts/contact-details/contact-details.spec.js index 58d16b8e..5b5131a8 100644 --- a/src/layouts/contact-details/contact-details.spec.js +++ b/src/layouts/contact-details/contact-details.spec.js @@ -32,65 +32,175 @@ describe('contactDetails.vue', () => { // Assert expect(siteSubHeader.exists()).toBe(true); }); - test('Should render first name question subcomponent', () => { + test('Should render appointment information alert', () => { // Arrange const wrapper = shallowMount(contactDetails, getMountOptions()); // Act - const firstNameQuestion = wrapper.findComponent({ ref: 'firstNameQuestion' }); + const appointmentInformationAlert = wrapper.findComponent({ ref: 'appointmentInformationAlert' }); // Assert - expect(firstNameQuestion.exists()).toBe(true); + expect(appointmentInformationAlert.exists()).toBe(true); }); - test('Should render last name question subcomponent', () => { + test('Should render same as policy address question subcomponent if service zip is same as customer zip', async () => { // Arrange - const wrapper = shallowMount(contactDetails, getMountOptions()); - - // Act - const lastNameQuestion = wrapper.findComponent({ ref: 'lastNameQuestion' }); - - // Assert - expect(lastNameQuestion.exists()).toBe(true); - }); - test('Should render email question subcomponent', () => { - // Arrange - const wrapper = shallowMount(contactDetails, getMountOptions()); - - // Act - const emailQuestion = wrapper.findComponent({ ref: 'emailQuestion' }); - - // Assert - expect(emailQuestion.exists()).toBe(true); - }); - test('Should render phone number question subcomponent', () => { - // Arrange - const wrapper = shallowMount(contactDetails, getMountOptions()); - - // Act - const phoneNumberQuestion = wrapper.findComponent({ ref: 'phoneNumberQuestion' }); - - // Assert - expect(phoneNumberQuestion.exists()).toBe(true); - }); - test('Should render text updates checkbox subcomponent', () => { - // Arrange - const checkboxLabel = getRandomString(50, 100); - const mockMixin = { - methods: { - getCmsContent: jest.fn().mockImplementation(() => checkboxLabel), - setCmsContent: jest.fn() + const mountOptions = getMountOptions(); + + const customerStreetAddress = getRandomString(10, 50); + const customerStreetAddress2 = getRandomString(0, 50); + const customerCity = getRandomString(4, 20); + const customerState = getRandomString(2, 2); + const serviceStreetAddress = getRandomString(10, 50); + const serviceStreetAddress2 = getRandomString(0, 50); + const serviceCity = getRandomString(4, 20); + const serviceState = getRandomString(2, 2); + const zipCode = getRandomInt(10000, 99999).toString(); + const mainInitialState = { + order: { + customer: { + address: { + streetAddress: customerStreetAddress, + streetAddress2: customerStreetAddress2, + city: customerCity, + state: customerState, + zipCode + } + }, + serviceLocation: { + streetAddress: serviceStreetAddress, + streetAddress2: serviceStreetAddress2, + city: serviceCity, + state: serviceState, + zipCode + } } }; + mountOptions.global.plugins = [createTestingPinia({ + initialState: { + main: mainInitialState + } + })]; + const wrapper = shallowMount(contactDetails, mountOptions); + + await wrapper.vm.$nextTick(); + + // Act + const sameAsPolicyAddressQuestion = wrapper.findComponent({ ref: 'sameAsPolicyAddressQuestion' }); + + // Assert + expect(sameAsPolicyAddressQuestion.exists()).toBe(true); + }); + test('Should not render same as policy address question subcomponent if service zip is different from customer zip', () => { + // Arrange const mountOptions = getMountOptions(); - mountOptions.mixins = [mockMixin]; + + const firstName = getRandomString(4, 15); + const lastName = getRandomString(4, 15); + const emailAddress = getRandomString(10, 20); + const servicePhone = getRandomInt(1000000000, 9999999999).toString(); + const testZipCode = getRandomInt(10000, 99999); + const customerZipCode = testZipCode.toString(); + const serviceZipCode = (testZipCode + 1).toString(); + const mainInitialState = { + order: { + customer: { + firstName, + lastName, + emailAddress, + address: { + zipCode: customerZipCode + } + }, + contactInfo: { + servicePhone + }, + serviceLocation: { + zipCode: serviceZipCode + } + } + }; + mountOptions.global.plugins = [createTestingPinia({ + initialState: { + main: mainInitialState + } + })]; const wrapper = shallowMount(contactDetails, mountOptions); // Act - const textUpdatesCheckbox = wrapper.findComponent({ ref: 'requestTextUpdatesCheckbox' }); + const sameAsPolicyAddressQuestion = wrapper.findComponent({ ref: 'sameAsPolicyAddressQuestion' }); // Assert - expect(textUpdatesCheckbox.exists()).toBe(true); - expect(wrapper.vm.requestTextUpdatesCheckboxText).toBe(`${checkboxLabel}*`); + expect(sameAsPolicyAddressQuestion.exists()).toBe(false); + }); + test('Should render address question subcomponent', () => { + // Arrange + const wrapper = shallowMount(contactDetails, getMountOptions()); + + // Act + const addressQuestion = wrapper.findComponent({ ref: 'addressQuestion' }); + + // Assert + expect(addressQuestion.exists()).toBe(true); + }); + test('Should render apartment question subcomponent', () => { + // Arrange + const wrapper = shallowMount(contactDetails, getMountOptions()); + + // Act + const address2Question = wrapper.findComponent({ ref: 'address2Question' }); + + // Assert + expect(address2Question.exists()).toBe(true); + }); + test('Should render city question subcomponent', () => { + // Arrange + const wrapper = shallowMount(contactDetails, getMountOptions()); + + // Act + const cityQuestion = wrapper.findComponent({ ref: 'cityQuestion' }); + + // Assert + expect(cityQuestion.exists()).toBe(true); + }); + test('Should render state question subcomponent', () => { + // Arrange + const wrapper = shallowMount(contactDetails, getMountOptions()); + + // Act + const stateQuestion = wrapper.findComponent({ ref: 'stateQuestion' }); + + // Assert + expect(stateQuestion.exists()).toBe(true); + }); + test('Should render zip code question subcomponent', () => { + // Arrange + const wrapper = shallowMount(contactDetails, getMountOptions()); + + // Act + const zipCodeQuestion = wrapper.findComponent({ ref: 'zipCodeQuestion' }); + + // Assert + expect(zipCodeQuestion.exists()).toBe(true); + }); + test('Should render change zip code alert', () => { + // Arrange + const wrapper = shallowMount(contactDetails, getMountOptions()); + + // Act + const changeZipCodeAlert = wrapper.findComponent({ ref: 'changeZipCodeAlert' }); + + // Assert + expect(changeZipCodeAlert.exists()).toBe(true); + }); + test('Should render vehicle protected question subcomponent', () => { + // Arrange + const wrapper = shallowMount(contactDetails, getMountOptions()); + + // Act + const vehicleProtectedQuestion = wrapper.findComponent({ ref: 'vehicleProtectedQuestion' }); + + // Assert + expect(vehicleProtectedQuestion.exists()).toBe(true); }); test('Should render technician notes textarea question subcomponent', () => { // Arrange @@ -102,46 +212,15 @@ describe('contactDetails.vue', () => { // Assert expect(notesQuestion.exists()).toBe(true); }); - test('Should render disclaimer text', () => { - // Arrange - const disclaimerText = getRandomString(50, 100); - const mockMixin = { - methods: { - getCmsContent: jest.fn().mockImplementation(() => disclaimerText), - setCmsContent: jest.fn() - } - }; - const mountOptions = getMountOptions(); - mountOptions.mixins = [mockMixin]; - const wrapper = shallowMount(contactDetails, mountOptions); - const expectedDisclaimerText = `*${disclaimerText} I also agree to Safelite's`; - - // Act - const componentText = wrapper.text(); - - // Assert - expect(wrapper.vm.textUpdateDisclaimerText).toBe(`*${disclaimerText}`); - expect(componentText).toContain(expectedDisclaimerText); - }); - test('Should render privacy policy link', () => { + test('Should render clearance text subcomponent', () => { // Arrange const wrapper = shallowMount(contactDetails, getMountOptions()); // Act - const privacyPolicyLink = wrapper.findComponent({ ref: 'privacyPolicyLink' }); + const clearanceText = wrapper.findComponent({ ref: 'clearanceText' }); // Assert - expect(privacyPolicyLink.exists()).toBe(true); - }); - test('Should render terms of use link', () => { - // Arrange - const wrapper = shallowMount(contactDetails, getMountOptions()); - - // Act - const termsOfUseLink = wrapper.findComponent({ ref: 'termsOfUseLink' }); - - // Assert - expect(termsOfUseLink.exists()).toBe(true); + expect(clearanceText.exists()).toBe(true); }); test('Should render site footer', () => { // Arrange @@ -153,82 +232,48 @@ describe('contactDetails.vue', () => { // Assert expect(footer.exists()).toBe(true); }); - test('Mocked store with no contact info yields expected data', () => { + test('Mocked store yields expected data', () => { // Arrange const mountOptions = getMountOptions(); - const firstName = getRandomString(4, 15); - const lastName = getRandomString(4, 15); - const emailAddress = getRandomString(10, 20); - const servicePhone = getRandomInt(1000000000, 9999999999).toString(); + const address = getRandomString(10, 50); + const address2 = getRandomString(0, 50); + const city = getRandomString(4, 20); + const state = getRandomString(2, 2); + const zipCode = getRandomInt(10000, 99999).toString(); + const notesForTechnician = getRandomString(1, 100); + const isVehicleProtected = getRandomBoolean(); const mainInitialState = { order: { - customer: { - firstName, - lastName, - emailAddress + serviceLocation: { + address, + address2, + city, + state, + zipCode, + isVehicleProtected }, contactInfo: { - servicePhone + notesForTechnician } } }; - mountOptions.global = { - plugins: [createTestingPinia({ - initialState: { - main: mainInitialState - } - })] - }; + mountOptions.global.plugins = [createTestingPinia({ + initialState: { + main: mainInitialState + } + })]; const wrapper = shallowMount(contactDetails, mountOptions); // Assert - expect(wrapper.vm.firstName).toBe(firstName); - expect(wrapper.vm.lastName).toBe(lastName); - expect(wrapper.vm.emailAddress).toBe(emailAddress); - expect(wrapper.vm.phoneNumber).toBe(servicePhone); - }); - test('Mock store with contact info yields expected data', () => { - // Arrange - const customer = { - firstName: getRandomString(4, 15), - lastName: getRandomString(4, 15), - emailAddress: getRandomString(10, 20), - phoneNumber: getRandomInt(1000000000, 9999999999) - }; - const contactInfo = { - firstName: getRandomString(4, 15), - lastName: getRandomString(4, 15), - emailAddress: getRandomString(10, 20), - servicePhone: getRandomInt(1000000000, 9999999999), - requestTextUpdates: getRandomBoolean(), - notesForTechnician: getRandomString(50, 100) - }; - const mainInitialState = { - order: { - customer, - contactInfo - } - }; - const mountOptions = getMountOptions(); - mountOptions.global = { - plugins: [createTestingPinia({ - initialState: { - main: mainInitialState - } - })] - }; - - const wrapper = shallowMount(contactDetails, mountOptions); - - // Assert - expect(wrapper.vm.firstName).toBe(contactInfo.firstName); - expect(wrapper.vm.lastName).toBe(contactInfo.lastName); - expect(wrapper.vm.emailAddress).toBe(contactInfo.emailAddress); - expect(wrapper.vm.phoneNumber).toBe(contactInfo.servicePhone); - expect(wrapper.vm.requestTextUpdates).toBe(contactInfo.requestTextUpdates); - expect(wrapper.vm.notesForTechnician).toBe(contactInfo.notesForTechnician); + expect(wrapper.vm.address).toBe(address); + expect(wrapper.vm.address2).toBe(address2); + expect(wrapper.vm.city).toBe(city); + expect(wrapper.vm.state).toBe(state); + expect(wrapper.vm.zipCode).toBe(zipCode); + expect(wrapper.vm.isVehicleProtected).toBe(isVehicleProtected); + expect(wrapper.vm.notesForTechnician).toBe(notesForTechnician); }); }); @@ -250,7 +295,7 @@ describe('contactDetails.vue', () => { expect(wrapper.vm.$router.navigateWithSpinner) .toHaveBeenCalledWith(navigationScenarios.CLICKED_BACK, undefined); }); - test('Forward button clicked triggers appropriate navigation when safelite shop', () => { + test('Forward button clicked triggers appropriate navigation', () => { // Arrange const mountOptions = getMountOptions({ router: { @@ -276,37 +321,44 @@ describe('contactDetails.vue', () => { // Assert expect(wrapper.vm.$router.navigate).toHaveBeenCalled(); expect(wrapper.vm.$router.navigate) - .toHaveBeenCalledWith(navigationScenarios.CLICKED_FORWARD_WITH_SAFELITE_SHOP, undefined); + .toHaveBeenCalledWith(navigationScenarios.CLICKED_FORWARD, undefined); }); - test('Forward button clicked triggers appropriate navigation when TPA shop', () => { + test('Forward button click updates service location info', () => { // Arrange const mountOptions = getMountOptions({ router: { navigate: jest.fn() - }, - navigationScenarios + } }); - const mainInitialState = { - order: { - serviceLocation: { IsSafeliteProvider: false } - } - }; - mountOptions.global.plugins = [createTestingPinia({ - initialState: { - main: mainInitialState - } - })]; const wrapper = shallowMount(contactDetails, mountOptions); + const address = getRandomString(10, 50); + const address2 = getRandomString(0, 50); + const city = getRandomString(4, 20); + const state = getRandomString(2, 2); + const zipCode = getRandomInt(10000, 99999).toString(); + const isVehicleProtected = getRandomBoolean().toString(); + wrapper.setData({ + address, + address2, + city, + state, + zipCode, + isVehicleProtected + }); // Act wrapper.vm.forwardButtonAction(); // Assert - expect(wrapper.vm.$router.navigate).toHaveBeenCalled(); - expect(wrapper.vm.$router.navigate) - .toHaveBeenCalledWith(navigationScenarios.CLICKED_FORWARD_WITH_NON_SAFELITE_SHOP, undefined); + expect(useMainStore().updateServiceLocation).toHaveBeenCalledWith({ + address, + address2, + city, + isVehicleProtected + }); }); - test('Forward button click updates contact info', () => { + + test('Forward button click updates notes for technician', () => { // Arrange const mountOptions = getMountOptions({ router: { @@ -314,18 +366,8 @@ describe('contactDetails.vue', () => { } }); const wrapper = shallowMount(contactDetails, mountOptions); - const firstName = getRandomString(4, 15); - const lastName = getRandomString(4, 15); - const emailAddress = getRandomString(10, 20); - const phoneNumber = getRandomInt(1000000000, 9999999999).toString(); - const requestTextUpdates = getRandomBoolean(); const notesForTechnician = getRandomString(1, 100); wrapper.setData({ - firstName, - lastName, - emailAddress, - phoneNumber, - requestTextUpdates, notesForTechnician }); @@ -334,146 +376,8 @@ describe('contactDetails.vue', () => { // Assert expect(useMainStore().updateContactInfo).toHaveBeenCalledWith({ - firstName, - lastName, - emailAddress, - requestTextUpdates, notesForTechnician }); }); - - test('Forward button click updates phone numbers request text updates', () => { - // Arrange - const mountOptions = getMountOptions({ - router: { - navigate: jest.fn() - } - }); - const wrapper = shallowMount(contactDetails, mountOptions); - const phoneNumber = getRandomInt(1000000000, 9999999999).toString(); - const requestTextUpdates = true; - wrapper.setData({ - phoneNumber, - requestTextUpdates - }); - - // Act - wrapper.vm.forwardButtonAction(); - - // Assert - expect(useMainStore().updatePhoneNumbers).toHaveBeenCalledWith({ - service: phoneNumber, - alternative: phoneNumber - }); - }); - - test('Forward button click updates phone numbers dot not request text updates', () => { - // Arrange - const mountOptions = getMountOptions({ - router: { - navigate: jest.fn() - } - }); - const wrapper = shallowMount(contactDetails, mountOptions); - const phoneNumber = getRandomInt(1000000000, 9999999999).toString(); - const requestTextUpdates = false; - wrapper.setData({ - phoneNumber, - requestTextUpdates - }); - - // Act - wrapper.vm.forwardButtonAction(); - - // Assert - expect(useMainStore().updatePhoneNumbers).toHaveBeenCalledWith({ - home: phoneNumber, - service: phoneNumber - }); - }); - }); - - test('Mocked store with no contact info yields expected data', () => { - // Arrange - const mountOptions = getMountOptions(); - - const firstName = getRandomString(4, 15); - const lastName = getRandomString(4, 15); - const emailAddress = getRandomString(10, 20); - const servicePhone = getRandomInt(1000000000, 9999999999); - const mainInitialState = { - order: { - customer: { - firstName, - lastName, - emailAddress - }, - contactInfo: { - firstName: null, - lastName: null, - emailAddress: null, - servicePhone, - requestTextUpdates: null, - notesForTechnician: null - } - } - }; - mountOptions.global = { - plugins: [createTestingPinia({ - initialState: { - main: mainInitialState - } - })] - }; - - const wrapper = shallowMount(contactDetails, mountOptions); - - // Assert - expect(wrapper.vm.firstName).toBe(firstName); - expect(wrapper.vm.lastName).toBe(lastName); - expect(wrapper.vm.emailAddress).toBe(emailAddress); - expect(wrapper.vm.phoneNumber).toBe(servicePhone); - }); - - test('Mock store with contact info yields expected data', () => { - // Arrange - const customer = { - firstName: getRandomString(4, 15), - lastName: getRandomString(4, 15), - emailAddress: getRandomString(10, 20), - phoneNumber: getRandomInt(1000000000, 9999999999) - }; - const contactInfo = { - firstName: getRandomString(4, 15), - lastName: getRandomString(4, 15), - emailAddress: getRandomString(10, 20), - servicePhone: getRandomInt(1000000000, 9999999999), - requestTextUpdates: getRandomBoolean(), - notesForTechnician: getRandomString(50, 100) - }; - const mainInitialState = { - order: { - customer, - contactInfo - } - }; - const mountOptions = getMountOptions(); - mountOptions.global = { - plugins: [createTestingPinia({ - initialState: { - main: mainInitialState - } - })] - }; - - const wrapper = shallowMount(contactDetails, mountOptions); - - // Assert - expect(wrapper.vm.firstName).toBe(contactInfo.firstName); - expect(wrapper.vm.lastName).toBe(contactInfo.lastName); - expect(wrapper.vm.emailAddress).toBe(contactInfo.emailAddress); - expect(wrapper.vm.phoneNumber).toBe(contactInfo.servicePhone); - expect(wrapper.vm.requestTextUpdates).toBe(contactInfo.requestTextUpdates); - expect(wrapper.vm.notesForTechnician).toBe(contactInfo.notesForTechnician); }); }); diff --git a/src/layouts/contact-details/contact-details.vue b/src/layouts/contact-details/contact-details.vue index 7e0b22f1..c28d0d3a 100644 --- a/src/layouts/contact-details/contact-details.vue +++ b/src/layouts/contact-details/contact-details.vue @@ -15,81 +15,92 @@ - - - - + + + v-if="showSameAsPolicyAddressQuestion" + ref="sameAsPolicyAddressQuestion" + v-model="isSameAsPolicyAddress" + checkboxName="sameAsPolicyAddress" + buttonID="sameAsPolicyAddress" + class="form-group same-address-checkbox" + :checkboxLabel="sameAsPolicyAddressQuestionText" /> + + + + + + + + -

- {{ textUpdateDisclaimerText }} I also agree to - Safelite's - - and - . -

diff --git a/src/layouts/order-confirmation/order-confirmation.spec.js b/src/layouts/order-confirmation/order-confirmation.spec.js index 88f9c71c..6a06885a 100644 --- a/src/layouts/order-confirmation/order-confirmation.spec.js +++ b/src/layouts/order-confirmation/order-confirmation.spec.js @@ -79,12 +79,14 @@ const initialStore = { isInsurance: true, paymentMethod: paymentMethods.PAY_AT_TIME_OF_SERVICE }, - contactInfo: { + customer: { firstName: 'Test', lastName: 'Test', - servicePhone: '111-111-1111', emailAddress: 'test@email.com' }, + contactInfo: { + servicePhone: '123-456-7890' + }, damage: { isRepair: false }, @@ -138,7 +140,14 @@ const sessionStorage = { make: 'Honda', model: 'Civic' }, - customer: {}, + customer: { + firstName: 'Test', + lastName: 'Test', + emailAddress: 'test@email.com' + }, + contactInfo: { + servicePhone: '123-456-7890' + }, customerPortalLoginToken: 'token', currentDeductible: { replace: 100, diff --git a/src/layouts/order-confirmation/order-confirmation.vue b/src/layouts/order-confirmation/order-confirmation.vue index 98edaf23..c3514995 100644 --- a/src/layouts/order-confirmation/order-confirmation.vue +++ b/src/layouts/order-confirmation/order-confirmation.vue @@ -373,7 +373,7 @@ export default { ); // Contact Info - const { contactInfo } = useMainStore().order; + const { contactInfo } = useMainStore(); const contactInfoReqs = !!( contactInfo.firstName && contactInfo.lastName diff --git a/src/layouts/part-questions/part-questions.vue b/src/layouts/part-questions/part-questions.vue index 8690e91a..d1e5641a 100644 --- a/src/layouts/part-questions/part-questions.vue +++ b/src/layouts/part-questions/part-questions.vue @@ -12,7 +12,8 @@ :validationRules="rules.optionRequired" :index="currentGlassIndex" @forwardButtonAction="forwardButtonAction" - @backClick="navigateBackByVehicleQuestions" /> + @backClick="navigateBackByVehicleQuestions" + @needHelpClick="requestCallbackBailout" /> @@ -29,6 +30,8 @@ import { Form } from 'vee-validate'; import vehicleQuestionsMixin from '@/mixins/vehicle-questions-mixin'; import BaseFormMixin from '@/mixins/base-form-mixin.js'; import globalRules from '@/constants/global-rules'; +import bailoutMessage from '@/constants/bailoutMessage'; +import navigationScenarios from '@/router/router-constants/navigation-scenarios'; export default { name: 'part-questions', @@ -145,6 +148,10 @@ export default { // TODO KO delete for quote mvp this.navigateForward(glassPartsForStore, null); }, + requestCallbackBailout() { + this.mainStore.setBailout(bailoutMessage.RequestCallback()); + this.$router.navigate(navigationScenarios.CLICKED_NEED_HELP_WITH_BAILOUT, this.$route); + } }, }; diff --git a/src/layouts/payment-method/payment-method.vue b/src/layouts/payment-method/payment-method.vue index 3dbacbe2..195909ec 100644 --- a/src/layouts/payment-method/payment-method.vue +++ b/src/layouts/payment-method/payment-method.vue @@ -52,7 +52,7 @@ cmsWidgetName="SiteFooterWidget" :isForwardActionDisabled="!meta.valid" :isStackedVertically="true" - @backClicked="navigateBack" + @backClicked="backButtonAction" @ForwardClicked="forwardButtonAction" /> @@ -230,7 +230,7 @@ export default { const customerReqs = !!(firstName && lastName && emailAddress); // Contact Info - const { contactInfo } = useMainStore().order; + const { contactInfo } = useMainStore(); const contactInfoReqs = !!( contactInfo.firstName && contactInfo.lastName @@ -291,6 +291,15 @@ export default { { [routerParams.SKIP_SAVE_SESSION]: true } ); } + }, + backButtonAction() { + const scenario = this.mainStore.isMobileAppointment + ? this.navigationScenarios.CLICKED_BACK_MOBILE + : this.navigationScenarios.CLICKED_BACK_INSHOP; + this.$router.navigate( + scenario, + this.$route + ); } } }; diff --git a/src/layouts/payment-method/review-dropdown/review-dropdown.vue b/src/layouts/payment-method/review-dropdown/review-dropdown.vue index da2c09cb..d1504da4 100644 --- a/src/layouts/payment-method/review-dropdown/review-dropdown.vue +++ b/src/layouts/payment-method/review-dropdown/review-dropdown.vue @@ -92,7 +92,7 @@ export default { return useMainStore().order.serviceLocation.appointmentType; }, customerInfo() { - return useMainStore().order.contactInfo; + return useMainStore().contactInfo; } }, methods: { diff --git a/src/layouts/payment-page/payment-page.spec.js b/src/layouts/payment-page/payment-page.spec.js index 257a6367..89293dd4 100644 --- a/src/layouts/payment-page/payment-page.spec.js +++ b/src/layouts/payment-page/payment-page.spec.js @@ -182,10 +182,12 @@ const defaultOrder = { }, techNotes: '' }, - contactInfo: { + customer: { firstName: 'first', lastName: 'last', - emailAddress: 'builddigitaltest@safelite.com', + emailAddress: 'builddigitaltest@safelite.com' + }, + contactInfo: { servicePhone: '555-555-5555' }, damage: { diff --git a/src/layouts/payment-page/payment-page.vue b/src/layouts/payment-page/payment-page.vue index 8ac64f7c..7dd6b673 100644 --- a/src/layouts/payment-page/payment-page.vue +++ b/src/layouts/payment-page/payment-page.vue @@ -479,15 +479,15 @@ export default { authSignature: '', authSignatureStart: '', referralSequenceNumber: useMainStore().order.referralSequenceNumber, - emailAddress: useMainStore().order.contactInfo.emailAddress, + emailAddress: useMainStore().contactInfo.emailAddress, address1: this.getAddress1(), address2: this.getAddress2(), city: this.getCity(), state: this.getState(), zipCode: this.getZipCode(), - firstName: useMainStore().order.contactInfo.firstName, - lastName: useMainStore().order.contactInfo.lastName, - phoneNumber: useMainStore().order.contactInfo.servicePhone, + firstName: useMainStore().contactInfo.firstName, + lastName: useMainStore().contactInfo.lastName, + phoneNumber: useMainStore().contactInfo.servicePhone, ctu: useMainStore().order.serviceLocation.provider?.address?.zipCodeCtu ?? useMainStore().order.serviceLocation.zipCodeCtu, referralCorrelationId: useMainStore().order.referralCorrelationId, workOrderNumber: this.getWorkOrderNumber(), @@ -593,7 +593,7 @@ export default { ); // Contact Info - const { contactInfo } = useMainStore().order; + const { contactInfo } = useMainStore(); const contactInfoReqs = !!( contactInfo.firstName && contactInfo.lastName diff --git a/src/layouts/schedule-page/schedule-page.vue b/src/layouts/schedule-page/schedule-page.vue index 99139a0a..0cd0f480 100644 --- a/src/layouts/schedule-page/schedule-page.vue +++ b/src/layouts/schedule-page/schedule-page.vue @@ -20,6 +20,7 @@ ref="serviceLocation" @appointmentTypeChanged="appointmentTypeChangedFromServiceLocation" @cityUpdated="cityUpdatedFromServiceLocation" + @inShopZipUpdated="inShopZipUpdatedFromServiceLocation" @mobileZipUpdated="mobileZipUpdatedFromServiceLocation" @providerChanged="providerChangedFromServiceLocation" /> @@ -182,7 +183,8 @@ const getAvailableDates = async ( storeAction.payload.endDate, storeAction.payload.zipCodeOverride ).catch((error) => { - console.log('Error fetching mobile time slots:', error); + // eslint-disable-next-line no-console + console.warn('Error fetching mobile time slots:', error); }); } @@ -224,6 +226,8 @@ export default { const cmsContentPromise = fetchCmsContentForPage(to.query.issPage); const storeServiceLocation = useMainStore().order.serviceLocation; const storeSelectedAppointmentType = storeServiceLocation?.appointmentType; + const isMobileAppointment = storeSelectedAppointmentType === AppointmentTypeStrings.MOBILE + || storeSelectedAppointmentType === AppointmentTypeStrings.MOBILE_NOT_ITAC_AND_NOT_NOCOMP; const storeSelectedProvider = storeServiceLocation?.provider; const serviceZipCode = storeServiceLocation?.zipCode || useMainStore().order.customer.address.zipCode; const zipCodeData = getZipCodeData(serviceZipCode); @@ -275,6 +279,9 @@ export default { // use resultMap to populate layout content. const resultMap = await settleAllPromises(promiseResultMap); const serviceLocationData = { + defaultMobileZipCode: isMobileAppointment + ? storeServiceLocation?.zipCode + : '', glassFees: resultMap.glassFees, mobileFeePart: resultMap.mobileFeePart, providers: resultMap.providers, @@ -285,8 +292,6 @@ export default { next(async (vm) => { const providerToUse = resultMap.providers?.shopProviders .find((provider) => provider.providerNumber === storeSelectedProvider?.providerNumber) || resultMap.providers?.shopProviders[0]; - const isMobileAppointment = storeSelectedAppointmentType === AppointmentTypeStrings.MOBILE - || storeSelectedAppointmentType === AppointmentTypeStrings.MOBILE_NOT_ITAC_AND_NOT_NOCOMP; const initialServiceLocationObj = { mobileProviderNumber: isMobileAppointment ? storeSelectedProvider?.providerNumber @@ -314,10 +319,10 @@ export default { mobileFeePart: null, mobilePremiumAppointmentFee: null, mobileProviderNumber: null, - mobileZipCodeOverride: null, selectableDatesData: [], selectedAppointmentType: this.getAppointmentType(), selectedDate: this.getSelectedDate(), + selectedMobileZipCode: null, selectedProvider: this.getSelectedProvider(), selectedServiceLocation: this.getServiceLocation(), selectedServiceLocationCity: this.getServiceLocationCity(), @@ -401,6 +406,7 @@ export default { }); } else { this.mobileProviderNumber = initialServiceLocationObj.mobileProviderNumber; + this.selectedMobileZipCode = initialServiceLocationObj.zipCode; await this.getDatePickerInitialData().then((initialData) => { this.selectableDatesData = initialData.initialShopTimeSlotsResponse; this.$refs.datePicker.setCalendarData(initialData); @@ -430,11 +436,18 @@ export default { return this.mainStore.order?.serviceLocation?.appointmentType; }, async getAvailableDatesMethod(startDate, endDate) { + let mobileProviderNumberToUse = this.mobileProviderNumber; + if (this.selectedAppointmentType === AppointmentTypeStrings.MOBILE + || this.selectedAppointmentType === AppointmentTypeStrings.MOBILE_NOT_ITAC_AND_NOT_NOCOMP) { + mobileProviderNumberToUse = this.selectedServiceLocation.mobileProviderNumber; + } + const newShopTimeSlots = await getAvailableDates( startDate, endDate, this.selectedAppointmentType, - this.selectedProvider.providerNumber + this.selectedProvider.providerNumber || mobileProviderNumberToUse, + this.selectedMobileZipCode ); // ADD API CALL RESULTS TO EXISTING DATE DATA this.selectableDatesData.days = @@ -590,9 +603,17 @@ export default { handleMqlChange(e) { this.isMobileView = !e.matches; }, + inShopZipUpdatedFromServiceLocation(inShopZipServiceLocation) { + if (inShopZipServiceLocation) { + this.mobileProviderNumber = null; + this.selectedMobileZipCode = null; + this.selectedServiceLocation = inShopZipServiceLocation; + } + }, mobileZipUpdatedFromServiceLocation(mobileZipServiceLocation) { if (mobileZipServiceLocation) { this.mobileProviderNumber = mobileZipServiceLocation.mobileProviderNumber; + this.selectedMobileZipCode = mobileZipServiceLocation.zipCode; this.selectedServiceLocation = mobileZipServiceLocation; if (mobileZipServiceLocation.refreshDatePicker) { diff --git a/src/layouts/schedule-page/service-location/service-location.vue b/src/layouts/schedule-page/service-location/service-location.vue index ea0911c6..e48dddcc 100644 --- a/src/layouts/schedule-page/service-location/service-location.vue +++ b/src/layouts/schedule-page/service-location/service-location.vue @@ -159,7 +159,7 @@ export default { serviceZipQuestion }, mixins: [baseFormMixin], - emits: ['appointment-type-changed', 'city-updated', 'mobile-zip-updated', 'provider-changed'], + emits: ['appointment-type-changed', 'city-updated', 'in-shop-zip-updated', 'mobile-zip-updated', 'provider-changed'], setup() { const mainStore = useMainStore(); return { mainStore }; @@ -171,6 +171,7 @@ export default { city: this.getServiceCityFromStore(), state: this.getServiceStateFromStore(), zipCode: this.getServiceZipCodeFromStore(), + initializingData: false, isGlassServiceableInshop: null, isRecalibrationServiceableInshop: null, isGlassServiceableMobile: null, @@ -289,7 +290,7 @@ export default { } }, async mobileZipCode(newZip, oldZip) { - if (newZip !== '' && newZip !== oldZip) { + if (newZip !== '' && newZip !== oldZip && !this.initializingData) { showIssLoadingModal(true); await this.updateMobileZip(); const updateMobileZipServiceLocationObj = { @@ -348,8 +349,10 @@ export default { } }, methods: { - initializeComponent(initialData) { - this.setData(initialData); + async initializeComponent(initialData) { + this.initializingData = true; + await this.setData(initialData); + this.initializingData = false; }, async forwardButtonAction() { let provider = this.selectedProvider; @@ -372,9 +375,6 @@ export default { } this.mainStore.saveServiceLocation({ - address: this.streetAddress, - address2: this.streetAddress2, - city: this.city, state: this.state, zipCode: this.zipCode, zipCodeCtu: this.zipCodeCtu, @@ -417,6 +417,7 @@ export default { this.zipCode = newZip; const zipCodeData = await getZipCodeData(this.zipCode); this.city = zipCodeData.city; + this.state = zipCodeData.state; this.setCtuForMobile(zipCodeData.zipCodeCtu); this.setContainsMilitaryBase(zipCodeData.containsMilitaryBase); @@ -426,6 +427,17 @@ export default { if (details) { this.setServiceabilityDetails(details); } + + const inShopZipServiceLocationObj = { + appointmentType: AppointmentTypeStrings.IN_SHOP, + mobileProviderNumber: null, + provider: this.selectedProvider, + refreshDatePicker: false, + resetDatePicker: false, + zipCode: newZip, + zipCodeCtu: zipCodeData.zipCodeCtu + }; + this.$emit('in-shop-zip-updated', inShopZipServiceLocationObj); }); }, refreshAvailabilityRating() { @@ -449,11 +461,16 @@ export default { setCtuForMobile(val) { this.zipCodeCtu = val; }, - setData(initialData) { + async setData(initialData) { + if (initialData.defaultMobileZipCode) { + this.mobileZipCode = initialData.defaultMobileZipCode; + } + if (initialData.zipCodeData) { this.zipContainsMilitaryBase = initialData.zipCodeData.containsMilitaryBase; this.zipCodeCtu = initialData.zipCodeData.zipCodeCtu; this.city = initialData.zipCodeData.city; + this.state = initialData.zipCodeData.state; } if (initialData.serviceabilityDetails) { @@ -523,6 +540,7 @@ export default { this.mobileZipError = errorMessages.NO_SERVICE_IN_AREA(toTitleCase(zipCodeData.city)); } else { this.city = zipCodeData.city; + this.state = zipCodeData.state; this.zipCode = this.mobileZipCode; this.setCtuForMobile(zipCodeData.zipCodeCtu); this.setContainsMilitaryBase(zipCodeData.containsMilitaryBase); @@ -601,12 +619,6 @@ $page-side-padding: 1.5rem; div.alert { margin-top: 0.625rem; - :deep(.alert-header) { - button.btn-collapse { - display: block; - } - } - button.looka-likea-link { background: none; border: none; diff --git a/src/layouts/service-packages/service-packages.vue b/src/layouts/service-packages/service-packages.vue index 692546ce..1bed0a7c 100644 --- a/src/layouts/service-packages/service-packages.vue +++ b/src/layouts/service-packages/service-packages.vue @@ -195,8 +195,12 @@ export default { } store.updateVaps(this.selectedVaps); + const scenario = store.isMobileAppointment + ? this.navigationScenarios.CLICKED_FORWARD_MOBILE + : this.navigationScenarios.CLICKED_FORWARD_INSHOP; + this.$router.navigate( - this.navigationScenarios.CLICKED_FORWARD, + scenario, this.$route ); } diff --git a/src/layouts/tpa-search/tpa-search.vue b/src/layouts/tpa-search/tpa-search.vue index 9476a7ad..83d211c1 100644 --- a/src/layouts/tpa-search/tpa-search.vue +++ b/src/layouts/tpa-search/tpa-search.vue @@ -389,9 +389,9 @@ export default { return getTpaProvidersResult?.data?.shopProviders ?? []; }, doNotSeeMyShopLinkClick() { - this.mainStore.setBailout(bailoutMessage.doNotSeeMyShop()); + this.mainStore.setBailout(bailoutMessage.RequestCallback()); this.$router.navigate( - this.navigationScenarios.CLICKED_DO_NOT_SEE_MY_SHOP_LINK, + this.navigationScenarios.CLICKED_NEED_HELP, this.$route ); }, diff --git a/src/layouts/tpa-submit/contact-details-drawer/contact-details-drawer.spec.js b/src/layouts/tpa-submit/contact-details-drawer/contact-details-drawer.spec.js index 901effe9..04fe6ce6 100644 --- a/src/layouts/tpa-submit/contact-details-drawer/contact-details-drawer.spec.js +++ b/src/layouts/tpa-submit/contact-details-drawer/contact-details-drawer.spec.js @@ -38,10 +38,12 @@ describe('contact-details-drawer', () => { const servicePhone = '606-009-2943'; const mainInitialState = { order: { - contactInfo: { + customer: { firstName, lastName, - emailAddress, + emailAddress + }, + contactInfo: { servicePhone } } @@ -112,10 +114,12 @@ describe('contact-details-drawer', () => { const phoneNumber = '123-456-1234'; const mainInitialState = { order: { - contactInfo: { + customer: { firstName, lastName, - emailAddress, + emailAddress + }, + contactInfo: { phoneNumber } } diff --git a/src/layouts/tpa-submit/tpa-submit.spec.js b/src/layouts/tpa-submit/tpa-submit.spec.js index 63b61c40..11136dd7 100644 --- a/src/layouts/tpa-submit/tpa-submit.spec.js +++ b/src/layouts/tpa-submit/tpa-submit.spec.js @@ -388,10 +388,12 @@ describe('tpa-submit', () => { companyName: providerCompanyName } }, - contactInfo: { + customer: { firstName, lastName, - emailAddress, + emailAddress + }, + contactInfo: { servicePhone } } @@ -433,10 +435,12 @@ describe('tpa-submit', () => { companyName: providerCompanyName } }, - contactInfo: { + customer: { firstName, lastName, - emailAddress, + emailAddress + }, + contactInfo: { servicePhone, extension } diff --git a/src/layouts/vehicle-lookup/vehicle-lookup.spec.js b/src/layouts/vehicle-lookup/vehicle-lookup.spec.js index 0aa12f8a..300b656d 100644 --- a/src/layouts/vehicle-lookup/vehicle-lookup.spec.js +++ b/src/layouts/vehicle-lookup/vehicle-lookup.spec.js @@ -13,6 +13,7 @@ import errorMessages from '@/constants/error-messages'; import globalRules from '@/constants/global-rules'; import { required } from '@/helpers/validation-rules'; import VehicleLookup from '@/layouts/vehicle-lookup/vehicle-lookup.vue'; +import vehicleQuestionsMixin from '@/mixins/vehicle-questions-mixin'; const pinia = createTestingPinia(); useMainStore(pinia); @@ -40,6 +41,13 @@ const vinLookupMethodsMockData = { SubText: 'Most convenient VIN match', ImageId: '', SubWidgetName: '' + }, + { + Name: 'NoVin', + Text: 'I\'d prefer not to share my VIN', + SubText: '', + ImageId: '', + SubWidgetName: '' } ] }; @@ -118,7 +126,8 @@ function setupMocks() { }, stubs: { SiteHeader: true, - SiteSubHeader: true + SiteSubHeader: true, + TextBlock: true } } }); @@ -264,6 +273,29 @@ describe('vehicle-lookup.vue', () => { * Currently just trust that our custom navigate() method does its job properly. */ }); + + test('Select "I\'d prefer not to share my VIN" then click "Continue". Calls navigateForward from vehicleQuestionsMixin', async () => { + useMainStore().getPartsOrQuestions = () => ({ data: { partsOrQuestions: [] } }); + navigateSpy = jest.spyOn(vehicleQuestionsMixin.methods, 'navigateForward').mockImplementation(async () => {}); + const { wrapper } = setupMocks(); + + const vinLookupMethodsWrapper = wrapper.findComponent({ name: 'vin-lookup-methods' }); + // Not using initializeComponent() because it doesn't trigger reactivity in test. + await vinLookupMethodsWrapper.setData({ + questionTextFromCms: vinLookupMethodsMockData.QuestionText, + answersFromCms: vinLookupMethodsMockData.Answers + }); + + const noVinOptionLabel = wrapper + .find('[data-test-id="vin-lookup-methods-button-question"] label[for="VinLookupMethods-NoVin"]'); + await noVinOptionLabel.trigger('mousedown'); + + const continueButton = wrapper.get('[data-test-id="site-footer-main-button"]'); + await continueButton.trigger('click'); + + // Assert + expect(navigateSpy).toHaveBeenCalled(); + }); /** * To Do: Error message is not displayed when running test diff --git a/src/layouts/vehicle-lookup/vehicle-lookup.vue b/src/layouts/vehicle-lookup/vehicle-lookup.vue index 27ef29db..ad251447 100644 --- a/src/layouts/vehicle-lookup/vehicle-lookup.vue +++ b/src/layouts/vehicle-lookup/vehicle-lookup.vue @@ -10,8 +10,8 @@
+ class="subheader mt-4" + subHeaderMarginClasses="mt-4" /> +
@@ -35,6 +38,7 @@ import { fetchCmsContentForPage } from '@/helpers/cms-content-helper'; import settleAllPromises from '@/helpers/layout-helper'; import { Form } from 'vee-validate'; import BaseFormMixin from '@/mixins/base-form-mixin'; +import vehicleQuestionsMixin from '@/mixins/vehicle-questions-mixin'; import vinLookupMethodSelections from '@/constants/vin-lookup-methods'; // Import Component @@ -42,6 +46,7 @@ import SiteFooter from '@/iss-components/site-footer/site-footer.vue'; import siteHeader from '@/iss-components/site-header/site-header.vue'; import SiteSubHeader from '@/iss-components/site-sub-header/site-sub-header.vue'; import VinLookupMethods from '@/layouts/vehicle-lookup/vin-lookup-methods/vin-lookup-methods.vue'; +import TextBlock from '@/digital-components/text-block/text-block.vue'; export default { name: 'vehicle-lookup', @@ -51,9 +56,10 @@ export default { SiteFooter, siteHeader, SiteSubHeader, - VinLookupMethods + VinLookupMethods, + TextBlock }, - mixins: [BaseFormMixin], + mixins: [BaseFormMixin, vehicleQuestionsMixin], async beforeRouteEnter(to, from, next) { const cmsContentPromise = fetchCmsContentForPage(to.query.issPage); @@ -86,7 +92,7 @@ export default { arePagePrerequisiteValid() { return true; }, - forwardButtonAction() { + async forwardButtonAction() { switch (this.selectedVinLookupMethod) { case vinLookupMethodSelections.MANUALVIN: this.$router.navigate( @@ -106,6 +112,24 @@ export default { this.$route ); break; + case vinLookupMethodSelections.NOVIN: + const partsOrQuestionsResponse = await this.getPartsOrQuestions(); + if (partsOrQuestionsResponse.error) { + this.mainStore.setBailout(bailoutMessage.PartsServiceError(partsOrQuestionsResponse.error.data)); + window.console.error('Error on retrieving PartsOrQuestions'); + this.hasBailedOut = true; + this.$router.navigate( + this.navigationScenarios.CLICKED_FORWARD_WITH_BAILOUT, + this.$route + ); + } + else { + await this.navigateForward( + partsOrQuestionsResponse.data.partsOrQuestions, + this + ); + } + break; default: break; } @@ -124,5 +148,23 @@ export default { padding-right: .9375rem; } } - +.subheader { + :deep(.all-caps) { + text-transform: uppercase; + } + :deep(.subheader-secondary>p) { + margin-bottom: 0; + } +} +.security-text { + text-align: center; + margin-bottom: 1.5rem; + :deep(a) { + font-weight: $font-weight-normal; + color: $heritage-blue-primary; + &:hover { + color: #125b7e; + } + } +} diff --git a/src/layouts/vehicle-lookup/vin-lookup-methods/vin-lookup-methods.vue b/src/layouts/vehicle-lookup/vin-lookup-methods/vin-lookup-methods.vue index 6c92856a..633dc865 100644 --- a/src/layouts/vehicle-lookup/vin-lookup-methods/vin-lookup-methods.vue +++ b/src/layouts/vehicle-lookup/vin-lookup-methods/vin-lookup-methods.vue @@ -1,11 +1,11 @@ - - +
+ + +