From 2d97232d403b76cbcb457a2bd28586facddb8667 Mon Sep 17 00:00:00 2001 From: brydon1 Date: Wed, 5 Jul 2023 13:41:00 -0400 Subject: [PATCH 01/21] Initial commit --- package-lock.json | 1036 ++++++++++++++++- package.json | 3 +- .../textarea-question.spec.vue | 0 .../textarea-question/textarea-question.vue | 188 +++ .../contact-details/contact-details.spec.js | 395 +++++++ .../contact-details/contact-details.vue | 243 +++- 6 files changed, 1801 insertions(+), 64 deletions(-) create mode 100644 src/digital-components/textarea-question/textarea-question.spec.vue create mode 100644 src/digital-components/textarea-question/textarea-question.vue create mode 100644 src/layouts/contact-details/contact-details.spec.js diff --git a/package-lock.json b/package-lock.json index d1fa847c..fee29bbe 100644 --- a/package-lock.json +++ b/package-lock.json @@ -39,7 +39,8 @@ "eslint-plugin-vue": "9.8.0", "jest": "^27.0.5", "sass": "^1.32.7", - "sass-loader": "^12.0.0" + "sass-loader": "^12.0.0", + "vitest": "^0.32.4" } }, "node_modules/@achrinza/node-ipc": { @@ -1774,6 +1775,358 @@ "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", "dev": true }, + "node_modules/@esbuild/android-arm": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.17.19.tgz", + "integrity": "sha512-rIKddzqhmav7MSmoFCmDIb6e2W57geRsM94gV2l38fzhXMwq7hZoClug9USI2pFRGL06f4IOPHHpFNOkWieR8A==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.17.19.tgz", + "integrity": "sha512-KBMWvEZooR7+kzY0BtbTQn0OAYY7CsiydT63pVEaPtVYF0hXbUaOyZog37DKxK7NF3XacBJOpYT4adIJh+avxA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.17.19.tgz", + "integrity": "sha512-uUTTc4xGNDT7YSArp/zbtmbhO0uEEK9/ETW29Wk1thYUJBz3IVnvgEiEwEa9IeLyvnpKrWK64Utw2bgUmDveww==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.17.19.tgz", + "integrity": "sha512-80wEoCfF/hFKM6WE1FyBHc9SfUblloAWx6FJkFWTWiCoht9Mc0ARGEM47e67W9rI09YoUxJL68WHfDRYEAvOhg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.17.19.tgz", + "integrity": "sha512-IJM4JJsLhRYr9xdtLytPLSH9k/oxR3boaUIYiHkAawtwNOXKE8KoU8tMvryogdcT8AU+Bflmh81Xn6Q0vTZbQw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.17.19.tgz", + "integrity": "sha512-pBwbc7DufluUeGdjSU5Si+P3SoMF5DQ/F/UmTSb8HXO80ZEAJmrykPyzo1IfNbAoaqw48YRpv8shwd1NoI0jcQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.17.19.tgz", + "integrity": "sha512-4lu+n8Wk0XlajEhbEffdy2xy53dpR06SlzvhGByyg36qJw6Kpfk7cp45DR/62aPH9mtJRmIyrXAS5UWBrJT6TQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.17.19.tgz", + "integrity": "sha512-cdmT3KxjlOQ/gZ2cjfrQOtmhG4HJs6hhvm3mWSRDPtZ/lP5oe8FWceS10JaSJC13GBd4eH/haHnqf7hhGNLerA==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.17.19.tgz", + "integrity": "sha512-ct1Tg3WGwd3P+oZYqic+YZF4snNl2bsnMKRkb3ozHmnM0dGWuxcPTTntAF6bOP0Sp4x0PjSF+4uHQ1xvxfRKqg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.17.19.tgz", + "integrity": "sha512-w4IRhSy1VbsNxHRQpeGCHEmibqdTUx61Vc38APcsRbuVgK0OPEnQ0YD39Brymn96mOx48Y2laBQGqgZ0j9w6SQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.17.19.tgz", + "integrity": "sha512-2iAngUbBPMq439a+z//gE+9WBldoMp1s5GWsUSgqHLzLJ9WoZLZhpwWuym0u0u/4XmZ3gpHmzV84PonE+9IIdQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.17.19.tgz", + "integrity": "sha512-LKJltc4LVdMKHsrFe4MGNPp0hqDFA1Wpt3jE1gEyM3nKUvOiO//9PheZZHfYRfYl6AwdTH4aTcXSqBerX0ml4A==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.17.19.tgz", + "integrity": "sha512-/c/DGybs95WXNS8y3Ti/ytqETiW7EU44MEKuCAcpPto3YjQbyK3IQVKfF6nbghD7EcLUGl0NbiL5Rt5DMhn5tg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.17.19.tgz", + "integrity": "sha512-FC3nUAWhvFoutlhAkgHf8f5HwFWUL6bYdvLc/TTuxKlvLi3+pPzdZiFKSWz/PF30TB1K19SuCxDTI5KcqASJqA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.17.19.tgz", + "integrity": "sha512-IbFsFbxMWLuKEbH+7sTkKzL6NJmG2vRyy6K7JJo55w+8xDk7RElYn6xvXtDW8HCfoKBFK69f3pgBJSUSQPr+4Q==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.17.19.tgz", + "integrity": "sha512-68ngA9lg2H6zkZcyp22tsVt38mlhWde8l3eJLWkyLrp4HwMUr3c1s/M2t7+kHIhvMjglIBrFpncX1SzMckomGw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.17.19.tgz", + "integrity": "sha512-CwFq42rXCR8TYIjIfpXCbRX0rp1jo6cPIUPSaWwzbVI4aOfX96OXY8M6KNmtPcg7QjYeDmN+DD0Wp3LaBOLf4Q==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.17.19.tgz", + "integrity": "sha512-cnq5brJYrSZ2CF6c35eCmviIN3k3RczmHz8eYaVlNasVqsNY+JKohZU5MKmaOI+KkllCdzOKKdPs762VCPC20g==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.17.19.tgz", + "integrity": "sha512-vCRT7yP3zX+bKWFeP/zdS6SqdWB8OIpaRq/mbXQxTGHnIxspRtigpkUcDMlSCOejlHowLqII7K2JKevwyRP2rg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.17.19.tgz", + "integrity": "sha512-yYx+8jwowUstVdorcMdNlzklLYhPxjniHWFKgRqH7IFlUEa0Umu3KuYplf1HUZZ422e3NU9F4LGb+4O0Kdcaag==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.17.19.tgz", + "integrity": "sha512-eggDKanJszUtCdlVs0RB+h35wNlb5v4TWEkq4vZcmVt5u/HiDZrTXe2bWFQUez3RgNHwx/x4sk5++4NSSicKkw==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.17.19.tgz", + "integrity": "sha512-lAhycmKnVOuRYNtRtatQR1LPQf2oYCkRGkSFnseDAKPl8lu5SOsK/e1sXe5a0Pc5kHIHe6P2I/ilntNv2xf3cA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, "node_modules/@eslint/eslintrc": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.3.tgz", @@ -3099,6 +3452,21 @@ "@types/node": "*" } }, + "node_modules/@types/chai": { + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.5.tgz", + "integrity": "sha512-mEo1sAde+UCE6b2hxn332f1g1E8WfYRu6p5SvTKr2ZKC1f7gFJXk4h5PyGP9Dt6gCaG8y8XhwnXWC6Iy2cmBng==", + "dev": true + }, + "node_modules/@types/chai-subset": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@types/chai-subset/-/chai-subset-1.3.3.tgz", + "integrity": "sha512-frBecisrNGz+F4T6bcc+NLeolfiojh5FxW2klu669+8BARtyQv2C/GkNW6FUodVe4BroGMP/wER/YDGc7rEllw==", + "dev": true, + "dependencies": { + "@types/chai": "*" + } + }, "node_modules/@types/connect": { "version": "3.4.35", "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz", @@ -3370,6 +3738,222 @@ "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==", "dev": true }, + "node_modules/@vitest/expect": { + "version": "0.32.4", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-0.32.4.tgz", + "integrity": "sha512-m7EPUqmGIwIeoU763N+ivkFjTzbaBn0n9evsTOcde03ugy2avPs3kZbYmw3DkcH1j5mxhMhdamJkLQ6dM1bk/A==", + "dev": true, + "dependencies": { + "@vitest/spy": "0.32.4", + "@vitest/utils": "0.32.4", + "chai": "^4.3.7" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner": { + "version": "0.32.4", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-0.32.4.tgz", + "integrity": "sha512-cHOVCkiRazobgdKLnczmz2oaKK9GJOw6ZyRcaPdssO1ej+wzHVIkWiCiNacb3TTYPdzMddYkCgMjZ4r8C0JFCw==", + "dev": true, + "dependencies": { + "@vitest/utils": "0.32.4", + "p-limit": "^4.0.0", + "pathe": "^1.1.1" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner/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/@vitest/runner/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/@vitest/snapshot": { + "version": "0.32.4", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-0.32.4.tgz", + "integrity": "sha512-IRpyqn9t14uqsFlVI2d7DFMImGMs1Q9218of40bdQQgMePwVdmix33yMNnebXcTzDU5eiV3eUsoxxH5v0x/IQA==", + "dev": true, + "dependencies": { + "magic-string": "^0.30.0", + "pathe": "^1.1.1", + "pretty-format": "^29.5.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/snapshot/node_modules/@jest/schemas": { + "version": "29.4.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.4.3.tgz", + "integrity": "sha512-VLYKXQmtmuEz6IxJsrZwzG9NvtkQsWNnWMsKxqWNu3+CnfzJQhp0WDDKWLVV9hLKr0l3SLLFRqcYHjhtyuDVxg==", + "dev": true, + "dependencies": { + "@sinclair/typebox": "^0.25.16" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@vitest/snapshot/node_modules/@sinclair/typebox": { + "version": "0.25.24", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.25.24.tgz", + "integrity": "sha512-XJfwUVUKDHF5ugKwIcxEgc9k8b7HbznCp6eUfWgu710hMPNIO4aw4/zB5RogDQz8nd6gyCDpU9O/m6qYEWY6yQ==", + "dev": true + }, + "node_modules/@vitest/snapshot/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/@vitest/snapshot/node_modules/magic-string": { + "version": "0.30.0", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.0.tgz", + "integrity": "sha512-LA+31JYDJLs82r2ScLrlz1GjSgu66ZV518eyWT+S8VhyQn/JL0u9MeBOvQMGYiPk1DBiSN9DDMOcXvigJZaViQ==", + "dev": true, + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.4.13" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@vitest/snapshot/node_modules/pretty-format": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.5.0.tgz", + "integrity": "sha512-V2mGkI31qdttvTFX7Mt4efOqHXqJWMu4/r66Xh3Z3BwZaPfPJgp6/gbwoujRpPUtfEF6AUUWx3Jim3GCw5g/Qw==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.4.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@vitest/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/@vitest/spy": { + "version": "0.32.4", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-0.32.4.tgz", + "integrity": "sha512-oA7rCOqVOOpE6rEoXuCOADX7Lla1LIa4hljI2MSccbpec54q+oifhziZIJXxlE/CvI2E+ElhBHzVu0VEvJGQKQ==", + "dev": true, + "dependencies": { + "tinyspy": "^2.1.1" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/utils": { + "version": "0.32.4", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-0.32.4.tgz", + "integrity": "sha512-Gwnl8dhd1uJ+HXrYyV0eRqfmk9ek1ASE/LWfTCuWMw+d07ogHqp4hEAV28NiecimK6UY9DpSEPh+pXBA5gtTBg==", + "dev": true, + "dependencies": { + "diff-sequences": "^29.4.3", + "loupe": "^2.3.6", + "pretty-format": "^29.5.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/utils/node_modules/@jest/schemas": { + "version": "29.4.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.4.3.tgz", + "integrity": "sha512-VLYKXQmtmuEz6IxJsrZwzG9NvtkQsWNnWMsKxqWNu3+CnfzJQhp0WDDKWLVV9hLKr0l3SLLFRqcYHjhtyuDVxg==", + "dev": true, + "dependencies": { + "@sinclair/typebox": "^0.25.16" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@vitest/utils/node_modules/@sinclair/typebox": { + "version": "0.25.24", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.25.24.tgz", + "integrity": "sha512-XJfwUVUKDHF5ugKwIcxEgc9k8b7HbznCp6eUfWgu710hMPNIO4aw4/zB5RogDQz8nd6gyCDpU9O/m6qYEWY6yQ==", + "dev": true + }, + "node_modules/@vitest/utils/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/@vitest/utils/node_modules/diff-sequences": { + "version": "29.4.3", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.4.3.tgz", + "integrity": "sha512-ofrBgwpPhCD85kMKtE9RYFFq6OC1A89oW2vvgWZNCwxrUpRUILopY7lsYyMDSjc8g6U6aiO0Qubg6r4Wgt5ZnA==", + "dev": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@vitest/utils/node_modules/pretty-format": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.5.0.tgz", + "integrity": "sha512-V2mGkI31qdttvTFX7Mt4efOqHXqJWMu4/r66Xh3Z3BwZaPfPJgp6/gbwoujRpPUtfEF6AUUWx3Jim3GCw5g/Qw==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.4.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@vitest/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/@vue/babel-helper-vue-jsx-merge-props": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/@vue/babel-helper-vue-jsx-merge-props/-/babel-helper-vue-jsx-merge-props-1.4.0.tgz", @@ -4335,9 +4919,9 @@ } }, "node_modules/acorn": { - "version": "8.8.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.0.tgz", - "integrity": "sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==", + "version": "8.9.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.9.0.tgz", + "integrity": "sha512-jaVNAFBHNLXspO543WnNNPZFRtavh3skAkITqD0/2aeMkKZTN+254PyhwxFYrk3vQ1xfY+2wbesJMs/JC8/PwQ==", "dev": true, "bin": { "acorn": "bin/acorn" @@ -4645,6 +5229,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/assertion-error": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", + "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", + "dev": true, + "engines": { + "node": "*" + } + }, "node_modules/async": { "version": "2.6.4", "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", @@ -5251,6 +5844,15 @@ "node": ">= 0.8" } }, + "node_modules/cac": { + "version": "6.7.14", + "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", + "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/call-bind": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", @@ -5329,6 +5931,24 @@ "node": ">=4" } }, + "node_modules/chai": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.7.tgz", + "integrity": "sha512-HLnAzZ2iupm25PlN0xFreAlBA5zaBSv3og0DdeGA4Ar6h6rJ3A0rolRUKJhSF2V10GZKDgWF/VmAEsNWjCRB+A==", + "dev": true, + "dependencies": { + "assertion-error": "^1.1.0", + "check-error": "^1.0.2", + "deep-eql": "^4.1.2", + "get-func-name": "^2.0.0", + "loupe": "^2.3.1", + "pathval": "^1.1.1", + "type-detect": "^4.0.5" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/chalk": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", @@ -5352,6 +5972,15 @@ "node": ">=10" } }, + "node_modules/check-error": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", + "integrity": "sha512-BrgHpW9NURQgzoNyjfq0Wu6VFO6D7IZEmJNdtgNqpzGG8RuNFHt2jQxWlAs4HMe119chBnv+34syEZtc6IhLtA==", + "dev": true, + "engines": { + "node": "*" + } + }, "node_modules/chokidar": { "version": "3.5.3", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", @@ -6290,6 +6919,18 @@ "integrity": "sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==", "dev": true }, + "node_modules/deep-eql": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.3.tgz", + "integrity": "sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==", + "dev": true, + "dependencies": { + "type-detect": "^4.0.0" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/deep-equal": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-2.1.0.tgz", @@ -6964,6 +7605,43 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/esbuild": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.17.19.tgz", + "integrity": "sha512-XQ0jAPFkK/u3LcVRcvVHQcTIqD6E2H1fvZMA5dQPSOWb3suUbWbfbRf94pjc0bNzRYLfIrDRQXr7X+LHIm5oHw==", + "dev": true, + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/android-arm": "0.17.19", + "@esbuild/android-arm64": "0.17.19", + "@esbuild/android-x64": "0.17.19", + "@esbuild/darwin-arm64": "0.17.19", + "@esbuild/darwin-x64": "0.17.19", + "@esbuild/freebsd-arm64": "0.17.19", + "@esbuild/freebsd-x64": "0.17.19", + "@esbuild/linux-arm": "0.17.19", + "@esbuild/linux-arm64": "0.17.19", + "@esbuild/linux-ia32": "0.17.19", + "@esbuild/linux-loong64": "0.17.19", + "@esbuild/linux-mips64el": "0.17.19", + "@esbuild/linux-ppc64": "0.17.19", + "@esbuild/linux-riscv64": "0.17.19", + "@esbuild/linux-s390x": "0.17.19", + "@esbuild/linux-x64": "0.17.19", + "@esbuild/netbsd-x64": "0.17.19", + "@esbuild/openbsd-x64": "0.17.19", + "@esbuild/sunos-x64": "0.17.19", + "@esbuild/win32-arm64": "0.17.19", + "@esbuild/win32-ia32": "0.17.19", + "@esbuild/win32-x64": "0.17.19" + } + }, "node_modules/escalade": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", @@ -8216,6 +8894,15 @@ "node": "6.* || 8.* || >= 10.*" } }, + "node_modules/get-func-name": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", + "integrity": "sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig==", + "dev": true, + "engines": { + "node": "*" + } + }, "node_modules/get-intrinsic": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.3.tgz", @@ -11975,6 +12662,12 @@ "node": ">=6" } }, + "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/jsonfile": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", @@ -12108,6 +12801,18 @@ "json5": "lib/cli.js" } }, + "node_modules/local-pkg": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-0.4.3.tgz", + "integrity": "sha512-SFppqq5p42fe2qcZQqqEOiVRXl+WCP1MdT6k7BDEW1j++sp5fIY+/fdRQitvKgB5BrBcmrs5m/L0v2FrU5MY1g==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, "node_modules/locate-path": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", @@ -12379,6 +13084,15 @@ "node": ">=4" } }, + "node_modules/loupe": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.6.tgz", + "integrity": "sha512-RaPMZKiMy8/JruncMU5Bt6na1eftNoo++R4Y+N2FrxkDVTrGvcyzFTsaGif4QTeKESheMGegbhw6iUAq+5A8zA==", + "dev": true, + "dependencies": { + "get-func-name": "^2.0.0" + } + }, "node_modules/lower-case": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", @@ -12694,6 +13408,18 @@ "mkdirp": "bin/cmd.js" } }, + "node_modules/mlly": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.4.0.tgz", + "integrity": "sha512-ua8PAThnTwpprIaU47EPeZ/bPUVp2QYBbWMphUQpVdBI3Lgqzm5KZQ45Agm3YJedHXaIHl6pBGabaLSUPPSptg==", + "dev": true, + "dependencies": { + "acorn": "^8.9.0", + "pathe": "^1.1.1", + "pkg-types": "^1.0.3", + "ufo": "^1.1.2" + } + }, "node_modules/module-alias": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/module-alias/-/module-alias-2.2.2.tgz", @@ -12740,9 +13466,15 @@ } }, "node_modules/nanoid": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", - "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==", + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz", + "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], "bin": { "nanoid": "bin/nanoid.cjs" }, @@ -13411,6 +14143,21 @@ "node": ">=8" } }, + "node_modules/pathe": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.1.tgz", + "integrity": "sha512-d+RQGp0MAYTIaDBIMmOfMwz3E+LOZnxx1HZd5R18mmCZY0QBlK0LDZfPc8FW8Ed2DlvsuE6PRjroDY+wg4+j/Q==", + "dev": true + }, + "node_modules/pathval": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", + "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", + "dev": true, + "engines": { + "node": "*" + } + }, "node_modules/picocolors": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", @@ -13487,6 +14234,17 @@ "node": ">=8" } }, + "node_modules/pkg-types": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.0.3.tgz", + "integrity": "sha512-nN7pYi0AQqJnoLPC9eHFQ8AcyaixBUOwvqc5TDnIKCMEE6I0y8P7OKA7fPexsXGCGxQDl/cmrLAp26LhcwxZ4A==", + "dev": true, + "dependencies": { + "jsonc-parser": "^3.2.0", + "mlly": "^1.2.0", + "pathe": "^1.1.0" + } + }, "node_modules/portfinder": { "version": "1.0.32", "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.32.tgz", @@ -13511,9 +14269,9 @@ } }, "node_modules/postcss": { - "version": "8.4.16", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.16.tgz", - "integrity": "sha512-ipHE1XBvKzm5xI7hiHCZJCSugxvsdq2mPnsq5+UF+VHCjiBvtDrlxJfMBToWaP9D5XlgNmcFGqoHmUn0EYEaRQ==", + "version": "8.4.24", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.24.tgz", + "integrity": "sha512-M0RzbcI0sO/XJNucsGjvWU9ERWxb/ytp1w6dKtxTKgixdtQDq4rmx/g8W1hnaheq9jgwL/oyEdH5Bc4WwJKMqg==", "funding": [ { "type": "opencollective", @@ -13522,10 +14280,14 @@ { "type": "tidelift", "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" } ], "dependencies": { - "nanoid": "^3.3.4", + "nanoid": "^3.3.6", "picocolors": "^1.0.0", "source-map-js": "^1.0.2" }, @@ -14647,6 +15409,22 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/rollup": { + "version": "3.26.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.26.0.tgz", + "integrity": "sha512-YzJH0eunH2hr3knvF3i6IkLO/jTjAEwU4HoMUbQl4//Tnl3ou0e7P5SjxdDr8HQJdeUJShlbEHXrrnEHy1l7Yg==", + "dev": true, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=14.18.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, "node_modules/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -15023,6 +15801,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/siginfo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", + "dev": true + }, "node_modules/sigmund": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz", @@ -15215,6 +15999,12 @@ "node": ">=8" } }, + "node_modules/stackback": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", + "dev": true + }, "node_modules/stackframe": { "version": "1.3.4", "resolved": "https://registry.npmjs.org/stackframe/-/stackframe-1.3.4.tgz", @@ -15230,6 +16020,12 @@ "node": ">= 0.8" } }, + "node_modules/std-env": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.3.3.tgz", + "integrity": "sha512-Rz6yejtVyWnVjC1RFvNmYL10kgjC49EOghxWn0RFqlCHGFpQx+Xe7yW3I4ceK1SGrWIGMjD5Kbue8W/udkbMJg==", + "dev": true + }, "node_modules/string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", @@ -15376,6 +16172,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/strip-literal": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-1.0.1.tgz", + "integrity": "sha512-QZTsipNpa2Ppr6v1AmJHESqJ3Uz247MUS0OjrnnZjFAvEoWqxuyFuXn2xLgMtRnijJShAa1HL0gtJyUs7u7n3Q==", + "dev": true, + "dependencies": { + "acorn": "^8.8.2" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, "node_modules/stylehacks": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-5.1.0.tgz", @@ -15665,6 +16473,30 @@ "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==", "dev": true }, + "node_modules/tinybench": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.5.0.tgz", + "integrity": "sha512-kRwSG8Zx4tjF9ZiyH4bhaebu+EDz1BOx9hOigYHlUW4xxI/wKIUQUqo018UlU4ar6ATPBsaMrdbKZ+tmPdohFA==", + "dev": true + }, + "node_modules/tinypool": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-0.5.0.tgz", + "integrity": "sha512-paHQtnrlS1QZYKF/GnLoOM/DN9fqaGOFbCbxzAhwniySnzl9Ebk8w73/dd34DAhe/obUbPAOldTyYXQZxnPBPQ==", + "dev": true, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tinyspy": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-2.1.1.tgz", + "integrity": "sha512-XPJL2uSzcOyBMky6OFrusqWlzfFrXtE0hPuMgW8A2HmaqrPo4ZQHRN/V0QXN3FSjKxpsbRrFc5LI7KOwBsT1/w==", + "dev": true, + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/tmpl": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", @@ -15870,6 +16702,12 @@ "is-typedarray": "^1.0.0" } }, + "node_modules/ufo": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.1.2.tgz", + "integrity": "sha512-TrY6DsjTQQgyS3E3dBaOXf0TpPD8u9FVrVYmKVegJuFw51n/YB9XPt+U6ydzFG5ZIN7+DIjPbNmXoBj9esYhgQ==", + "dev": true + }, "node_modules/unbox-primitive": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", @@ -16070,6 +16908,166 @@ "vue": "^3.0.0" } }, + "node_modules/vite": { + "version": "4.3.9", + "resolved": "https://registry.npmjs.org/vite/-/vite-4.3.9.tgz", + "integrity": "sha512-qsTNZjO9NoJNW7KnOrgYwczm0WctJ8m/yqYAMAK9Lxt4SoySUfS5S8ia9K7JHpa3KEeMfyF8LoJ3c5NeBJy6pg==", + "dev": true, + "dependencies": { + "esbuild": "^0.17.5", + "postcss": "^8.4.23", + "rollup": "^3.21.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + }, + "peerDependencies": { + "@types/node": ">= 14", + "less": "*", + "sass": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "sass": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + }, + "node_modules/vite-node": { + "version": "0.32.4", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-0.32.4.tgz", + "integrity": "sha512-L2gIw+dCxO0LK14QnUMoqSYpa9XRGnTTTDjW2h19Mr+GR0EFj4vx52W41gFXfMLqpA00eK4ZjOVYo1Xk//LFEw==", + "dev": true, + "dependencies": { + "cac": "^6.7.14", + "debug": "^4.3.4", + "mlly": "^1.4.0", + "pathe": "^1.1.1", + "picocolors": "^1.0.0", + "vite": "^3.0.0 || ^4.0.0" + }, + "bin": { + "vite-node": "vite-node.mjs" + }, + "engines": { + "node": ">=v14.18.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/vitest": { + "version": "0.32.4", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-0.32.4.tgz", + "integrity": "sha512-3czFm8RnrsWwIzVDu/Ca48Y/M+qh3vOnF16czJm98Q/AN1y3B6PBsyV8Re91Ty5s7txKNjEhpgtGPcfdbh2MZg==", + "dev": true, + "dependencies": { + "@types/chai": "^4.3.5", + "@types/chai-subset": "^1.3.3", + "@types/node": "*", + "@vitest/expect": "0.32.4", + "@vitest/runner": "0.32.4", + "@vitest/snapshot": "0.32.4", + "@vitest/spy": "0.32.4", + "@vitest/utils": "0.32.4", + "acorn": "^8.9.0", + "acorn-walk": "^8.2.0", + "cac": "^6.7.14", + "chai": "^4.3.7", + "debug": "^4.3.4", + "local-pkg": "^0.4.3", + "magic-string": "^0.30.0", + "pathe": "^1.1.1", + "picocolors": "^1.0.0", + "std-env": "^3.3.3", + "strip-literal": "^1.0.1", + "tinybench": "^2.5.0", + "tinypool": "^0.5.0", + "vite": "^3.0.0 || ^4.0.0", + "vite-node": "0.32.4", + "why-is-node-running": "^2.2.2" + }, + "bin": { + "vitest": "vitest.mjs" + }, + "engines": { + "node": ">=v14.18.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@vitest/browser": "*", + "@vitest/ui": "*", + "happy-dom": "*", + "jsdom": "*", + "playwright": "*", + "safaridriver": "*", + "webdriverio": "*" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@vitest/browser": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + }, + "playwright": { + "optional": true + }, + "safaridriver": { + "optional": true + }, + "webdriverio": { + "optional": true + } + } + }, + "node_modules/vitest/node_modules/magic-string": { + "version": "0.30.0", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.0.tgz", + "integrity": "sha512-LA+31JYDJLs82r2ScLrlz1GjSgu66ZV518eyWT+S8VhyQn/JL0u9MeBOvQMGYiPk1DBiSN9DDMOcXvigJZaViQ==", + "dev": true, + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.4.13" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/vue": { "version": "3.2.38", "resolved": "https://registry.npmjs.org/vue/-/vue-3.2.38.tgz", @@ -16900,6 +17898,22 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/why-is-node-running": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.2.2.tgz", + "integrity": "sha512-6tSwToZxTOcotxHeA+qGCq1mVzKR3CwcJGmVcY+QE8SHy6TnpFnh8PAvPNHYr7EcuVeG0QSMxtYCuO1ta/G/oA==", + "dev": true, + "dependencies": { + "siginfo": "^2.0.0", + "stackback": "0.0.2" + }, + "bin": { + "why-is-node-running": "cli.js" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/wildcard": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.0.tgz", diff --git a/package.json b/package.json index 66144ba2..dea685b5 100644 --- a/package.json +++ b/package.json @@ -40,6 +40,7 @@ "eslint-plugin-vue": "9.8.0", "jest": "^27.0.5", "sass": "^1.32.7", - "sass-loader": "^12.0.0" + "sass-loader": "^12.0.0", + "vitest": "^0.32.4" } } diff --git a/src/digital-components/textarea-question/textarea-question.spec.vue b/src/digital-components/textarea-question/textarea-question.spec.vue new file mode 100644 index 00000000..e69de29b diff --git a/src/digital-components/textarea-question/textarea-question.vue b/src/digital-components/textarea-question/textarea-question.vue new file mode 100644 index 00000000..83a7ca5c --- /dev/null +++ b/src/digital-components/textarea-question/textarea-question.vue @@ -0,0 +1,188 @@ + + + + + diff --git a/src/layouts/contact-details/contact-details.spec.js b/src/layouts/contact-details/contact-details.spec.js new file mode 100644 index 00000000..9a60969c --- /dev/null +++ b/src/layouts/contact-details/contact-details.spec.js @@ -0,0 +1,395 @@ +// Components +import contactDetails from '@/layouts/contact-details/contact-details.vue'; + +// TODO finish this file + +// Supporting Files +import { shallowMount } from '@vue/test-utils'; +import { getMountOptions } from '@/helpers/unit-test-helper'; +import { useMainStore } from '@/store'; +import vehicleQuestionsMixin from '@/mixins/vehicle-questions-mixin'; +import { nextTick } from 'vue'; +import { vi } from 'vitest'; +import baseMixin from '../../mixins/base-mixin'; + +// Mock our module for promises. +vi.mock('@/helpers/layout-helper.js', () => + ({ + settleAllPromises: vi.fn() + })); + +// Mock fetchCmsContentForPage +vi.mock('@/helpers/cms-content-helper', () => + ({ + fetchCmsContentForPage: vi.fn() + })); + +const baseStoreGettersPageData = () => + ({ + partsOrQuestions: [ + { + parts: [ + { + childPartQuestions: [] + } + ], + contactDetails: [ + { + questionSequence: 1, + questionText: + 'Is your vehicle equipped with the optional Lane-Keeping System which tugs on the steering wheel and/or beeps to alert you if you drift too close to the edge of the lane?', + answers: [ + { + answerResult1: 'DYNAMIC', + answerResult2: '1', + answerText: 'Yes', + nextQuestionSequence: null, + answerResult: 'DYNAMIC' + }, + { + answerResult1: 'Unknown', + answerResult2: '0', + answerText: 'No', + nextQuestionSequence: null, + answerResult: 'Unknown' + } + ] + } + ], + partQuestions: [], + questions: [], + glassLocation: 'Windshield', + glassName: 'Single', + answerKey: 'Windshield-Single', + answerData: null + } + ] + }); +const baseStoreGettersDamage = () => + ({ + partsQuestionAnswers: [ + { + glassLocation: 'Windshield', + glassName: 'Single', + result: 'FW04848', + answeredQuestions: [ + { + questionText: + 'Is your vehicle equipped with the Panoramic Sunroof which can be identified by having a glass panel over the rear seats?', + selectedAnswer: '1|nextQuestion|3|Yes', + selectedAnswerText: 'Yes', + questionNum: 1 + }, + { + questionText: + 'Is your vehicle equipped with a heated windshield that melts snow and ice from underneath the windshield wiper blades?', + selectedAnswer: '2|nextQuestion|3|Yes', + selectedAnswerText: 'Yes', + questionNum: 2 + } + ] + } + ] + }); +useMainStore().pageData = baseStoreGettersPageData; +useMainStore().damage = baseStoreGettersDamage; + +describe('contactDetails.vue', () => { + describe('method arePagePrerequisitesValid...', () => { + test('Should return true for valid page requisites if pageData exists', () => { + // Arrange + const { wrapper } = setupMocks({}); + + // Act + const result = wrapper.vm.arePagePrerequisitesValid(); + + // Assert + expect(result).toBe(true); + + wrapper.unmount(); + }); + + test('Should return false for valid page requisites if partsOrQuestions in pageData is missing', () => { + // Arrange + const { wrapper } = setupMocks({}); + useMainStore().pageData = vi.fn(() => + undefined); + + // Act + const result = wrapper.vm.arePagePrerequisitesValid(); + + // Assert + expect(result).toBeFalsy(); + + wrapper.unmount(); + }); + + test('Should be at least one item in partsOrQuestions', () => { + // Arrange + useMainStore().pageData = vi.fn(() => + ({ + partsOrQuestions: [] + })); + useMainStore().damage = baseStoreGettersDamage; + + const { wrapper } = setupMocks({}); + + // Act + const result = wrapper.vm.arePagePrerequisitesValid(); + + // Assert + expect(result).toBeFalsy(); + + wrapper.unmount(); + }); + }); + + describe('watch on selectedAnswers should be set up...', () => { + test('Should trigger handleAnswerUpdates if watched data changes', async () => { + // Arrange + useMainStore().pageData = baseStoreGettersPageData; + useMainStore().damage = baseStoreGettersDamage; + const { wrapper } = setupMocks({}); + const spy = vi.spyOn(wrapper.vm, 'handleAnswerUpdates'); + + // Act + wrapper.setData({ + selectedAnswers: { + 'Windshield-Single': { + answerResult: 'FW04848', + answeredQuestions: [ + { + questionText: + 'Is your vehicle equipped with the Panoramic Sunroof which can be identified by having a glass panel over the rear seats?', + selectedAnswer: '1|nextQuestion|3|Yes', + selectedAnswerText: 'Yes', + questionNum: 1 + }, + { + questionText: + 'Is your vehicle equipped with a heated windshield that melts snow and ice from underneath the windshield wiper blades?', + selectedAnswer: '2|nextQuestion|3|Yes', + selectedAnswerText: 'Yes', + questionNum: 2 + } + ], + index: 0 + } + } + }); + + await nextTick(); + + // Assert + expect(spy).toHaveBeenCalled(); + + wrapper.unmount(); + }); + }); + + describe('forwardButtonAction', () => { + test('Should clear out answerData', () => { + // Arrange + const { wrapper } = setupMocks({}); + + wrapper.vm.questionsData = [ + { + glassLocation: 'Windshield', + glassName: 'Single', + answerData: { + answerResult: 'FW04848', + answeredQuestions: [] + }, + questions: [], + parts: [ + { + childPartQuestions: [{}] + } + ] + } + ]; + wrapper.vm.dispatchStoreAction = vi.fn(() => + ({ + data: [] + })); + + // Act + wrapper.vm.forwardButtonAction(); + + // Assert + expect(wrapper.vm.questionsData[0].answerData).toEqual({}); + + wrapper.unmount(); + }); + + test('Should save to pinia store', async () => { + // Arrange + const { wrapper } = setupMocks({}); + + wrapper.vm.questionsData = [ + { + glassLocation: 'Windshield', + glassName: 'Single', + answerData: { + answerResult: 'FW04848', + answeredQuestions: [] + }, + questions: [], + parts: [ + { + childPartQuestions: [] + } + ] + } + ]; + useMainStore().getPartsOrQuestions = vi.fn(() => + ({ + data: { + partsOrQuestions: [] + } + })); + + // Act + wrapper.vm.forwardButtonAction(); + + await nextTick(); + + // Assert + expect(wrapper.vm.saveCapabilityQuestionAnswers).toHaveBeenCalled; + wrapper.unmount(); + }); + test('Should call GET_PART_FROM_CAPABILITY_QUESTION_ANSWER API', async () => { + // Arrange + const { wrapper } = setupMocks({}); + + wrapper.vm.questionsData = [ + { + glassLocation: 'Windshield', + glassName: 'Single', + answerData: { + answerResult: 'FW04848', + answeredQuestions: [] + }, + questions: [], + parts: [ + { + childPartQuestions: [] + } + ] + } + ]; + wrapper.vm.dispatchStoreAction = vi.fn(() => + ({ + data: [] + })); + + // Act + wrapper.vm.forwardButtonAction(); + + await nextTick(); + + // Assert + expect(wrapper.vm.getPartFromCapabilityQuestionAnswer).toHaveBeenCalled; + + wrapper.unmount(); + }); + + test('Should trigger navigateForward', async () => { + // Arrange + const { wrapper } = setupMocks({}); + + wrapper.vm.questionsData = [ + { + glassLocation: 'Windshield', + glassName: 'Single', + answerData: { + answerResult: 'FW04848', + answeredQuestions: [] + }, + questions: [], + parts: [ + { + childPartQuestions: [] + } + ] + } + ]; + useMainStore().getPartsOrQuestions = vi.fn(() => + ({ + data: { + partsOrQuestions: [] + } + })); + wrapper.vm.navigateForward = vi.fn(); + + // Act + await wrapper.vm.forwardButtonAction(); + + // Assert + expect(wrapper.vm.navigateForward).toHaveBeenCalled(); + + wrapper.unmount(); + }); + }); +}); + +function setupMocks({ + mountOptionsMockData = { + router: { + navigate: vi.fn() + }, + actionList: [ + { + actionName: 'saveCapabilityQuestionAnswers', + data: {} + }, + { + actionName: 'getPartsOrQuestions', + data: {} + } + ], + route: { + query: { + issPage: 'capability-questions' + } + }, + data() { + return { + computedSwitcher: [ + { + glassLocation: 'Windshield', + glassName: 'Single', + answerData: { + answerResult: 'FW04848', + answeredQuestions: [] + } + } + ] + }; + }, + questionsData: { + get() { + return this.computedSwitcher; + }, + set(val) { + this.computedSwitcher = val; + } + } + } +}) { + useMainStore().getPartsOrQuestions = vi.fn(() => + ({ + data: { + partsOrQuestions: [] + } + })); + const mountOptions = getMountOptions({ + ...mountOptionsMockData, + mixins: [baseMixin, vehicleQuestionsMixin] + }); + mountOptions.attachTo = document.body; + + const wrapper = shallowMount(contactDetails, mountOptions); + + return { wrapper }; +} diff --git a/src/layouts/contact-details/contact-details.vue b/src/layouts/contact-details/contact-details.vue index f552e99a..e32c3128 100644 --- a/src/layouts/contact-details/contact-details.vue +++ b/src/layouts/contact-details/contact-details.vue @@ -1,17 +1,91 @@ + From c05ff7b35a622b87bdd10dfa33d3f59ef201ce56 Mon Sep 17 00:00:00 2001 From: brydon1 Date: Wed, 5 Jul 2023 13:46:55 -0400 Subject: [PATCH 02/21] Adding test todos --- .../textarea-question.spec.js | 1 + .../textarea-question.spec.vue | 0 .../contact-details/contact-details.spec.js | 391 ------------------ 3 files changed, 1 insertion(+), 391 deletions(-) create mode 100644 src/digital-components/textarea-question/textarea-question.spec.js delete mode 100644 src/digital-components/textarea-question/textarea-question.spec.vue diff --git a/src/digital-components/textarea-question/textarea-question.spec.js b/src/digital-components/textarea-question/textarea-question.spec.js new file mode 100644 index 00000000..a9c60f39 --- /dev/null +++ b/src/digital-components/textarea-question/textarea-question.spec.js @@ -0,0 +1 @@ +// TODO finish diff --git a/src/digital-components/textarea-question/textarea-question.spec.vue b/src/digital-components/textarea-question/textarea-question.spec.vue deleted file mode 100644 index e69de29b..00000000 diff --git a/src/layouts/contact-details/contact-details.spec.js b/src/layouts/contact-details/contact-details.spec.js index 9a60969c..db058176 100644 --- a/src/layouts/contact-details/contact-details.spec.js +++ b/src/layouts/contact-details/contact-details.spec.js @@ -2,394 +2,3 @@ import contactDetails from '@/layouts/contact-details/contact-details.vue'; // TODO finish this file - -// Supporting Files -import { shallowMount } from '@vue/test-utils'; -import { getMountOptions } from '@/helpers/unit-test-helper'; -import { useMainStore } from '@/store'; -import vehicleQuestionsMixin from '@/mixins/vehicle-questions-mixin'; -import { nextTick } from 'vue'; -import { vi } from 'vitest'; -import baseMixin from '../../mixins/base-mixin'; - -// Mock our module for promises. -vi.mock('@/helpers/layout-helper.js', () => - ({ - settleAllPromises: vi.fn() - })); - -// Mock fetchCmsContentForPage -vi.mock('@/helpers/cms-content-helper', () => - ({ - fetchCmsContentForPage: vi.fn() - })); - -const baseStoreGettersPageData = () => - ({ - partsOrQuestions: [ - { - parts: [ - { - childPartQuestions: [] - } - ], - contactDetails: [ - { - questionSequence: 1, - questionText: - 'Is your vehicle equipped with the optional Lane-Keeping System which tugs on the steering wheel and/or beeps to alert you if you drift too close to the edge of the lane?', - answers: [ - { - answerResult1: 'DYNAMIC', - answerResult2: '1', - answerText: 'Yes', - nextQuestionSequence: null, - answerResult: 'DYNAMIC' - }, - { - answerResult1: 'Unknown', - answerResult2: '0', - answerText: 'No', - nextQuestionSequence: null, - answerResult: 'Unknown' - } - ] - } - ], - partQuestions: [], - questions: [], - glassLocation: 'Windshield', - glassName: 'Single', - answerKey: 'Windshield-Single', - answerData: null - } - ] - }); -const baseStoreGettersDamage = () => - ({ - partsQuestionAnswers: [ - { - glassLocation: 'Windshield', - glassName: 'Single', - result: 'FW04848', - answeredQuestions: [ - { - questionText: - 'Is your vehicle equipped with the Panoramic Sunroof which can be identified by having a glass panel over the rear seats?', - selectedAnswer: '1|nextQuestion|3|Yes', - selectedAnswerText: 'Yes', - questionNum: 1 - }, - { - questionText: - 'Is your vehicle equipped with a heated windshield that melts snow and ice from underneath the windshield wiper blades?', - selectedAnswer: '2|nextQuestion|3|Yes', - selectedAnswerText: 'Yes', - questionNum: 2 - } - ] - } - ] - }); -useMainStore().pageData = baseStoreGettersPageData; -useMainStore().damage = baseStoreGettersDamage; - -describe('contactDetails.vue', () => { - describe('method arePagePrerequisitesValid...', () => { - test('Should return true for valid page requisites if pageData exists', () => { - // Arrange - const { wrapper } = setupMocks({}); - - // Act - const result = wrapper.vm.arePagePrerequisitesValid(); - - // Assert - expect(result).toBe(true); - - wrapper.unmount(); - }); - - test('Should return false for valid page requisites if partsOrQuestions in pageData is missing', () => { - // Arrange - const { wrapper } = setupMocks({}); - useMainStore().pageData = vi.fn(() => - undefined); - - // Act - const result = wrapper.vm.arePagePrerequisitesValid(); - - // Assert - expect(result).toBeFalsy(); - - wrapper.unmount(); - }); - - test('Should be at least one item in partsOrQuestions', () => { - // Arrange - useMainStore().pageData = vi.fn(() => - ({ - partsOrQuestions: [] - })); - useMainStore().damage = baseStoreGettersDamage; - - const { wrapper } = setupMocks({}); - - // Act - const result = wrapper.vm.arePagePrerequisitesValid(); - - // Assert - expect(result).toBeFalsy(); - - wrapper.unmount(); - }); - }); - - describe('watch on selectedAnswers should be set up...', () => { - test('Should trigger handleAnswerUpdates if watched data changes', async () => { - // Arrange - useMainStore().pageData = baseStoreGettersPageData; - useMainStore().damage = baseStoreGettersDamage; - const { wrapper } = setupMocks({}); - const spy = vi.spyOn(wrapper.vm, 'handleAnswerUpdates'); - - // Act - wrapper.setData({ - selectedAnswers: { - 'Windshield-Single': { - answerResult: 'FW04848', - answeredQuestions: [ - { - questionText: - 'Is your vehicle equipped with the Panoramic Sunroof which can be identified by having a glass panel over the rear seats?', - selectedAnswer: '1|nextQuestion|3|Yes', - selectedAnswerText: 'Yes', - questionNum: 1 - }, - { - questionText: - 'Is your vehicle equipped with a heated windshield that melts snow and ice from underneath the windshield wiper blades?', - selectedAnswer: '2|nextQuestion|3|Yes', - selectedAnswerText: 'Yes', - questionNum: 2 - } - ], - index: 0 - } - } - }); - - await nextTick(); - - // Assert - expect(spy).toHaveBeenCalled(); - - wrapper.unmount(); - }); - }); - - describe('forwardButtonAction', () => { - test('Should clear out answerData', () => { - // Arrange - const { wrapper } = setupMocks({}); - - wrapper.vm.questionsData = [ - { - glassLocation: 'Windshield', - glassName: 'Single', - answerData: { - answerResult: 'FW04848', - answeredQuestions: [] - }, - questions: [], - parts: [ - { - childPartQuestions: [{}] - } - ] - } - ]; - wrapper.vm.dispatchStoreAction = vi.fn(() => - ({ - data: [] - })); - - // Act - wrapper.vm.forwardButtonAction(); - - // Assert - expect(wrapper.vm.questionsData[0].answerData).toEqual({}); - - wrapper.unmount(); - }); - - test('Should save to pinia store', async () => { - // Arrange - const { wrapper } = setupMocks({}); - - wrapper.vm.questionsData = [ - { - glassLocation: 'Windshield', - glassName: 'Single', - answerData: { - answerResult: 'FW04848', - answeredQuestions: [] - }, - questions: [], - parts: [ - { - childPartQuestions: [] - } - ] - } - ]; - useMainStore().getPartsOrQuestions = vi.fn(() => - ({ - data: { - partsOrQuestions: [] - } - })); - - // Act - wrapper.vm.forwardButtonAction(); - - await nextTick(); - - // Assert - expect(wrapper.vm.saveCapabilityQuestionAnswers).toHaveBeenCalled; - wrapper.unmount(); - }); - test('Should call GET_PART_FROM_CAPABILITY_QUESTION_ANSWER API', async () => { - // Arrange - const { wrapper } = setupMocks({}); - - wrapper.vm.questionsData = [ - { - glassLocation: 'Windshield', - glassName: 'Single', - answerData: { - answerResult: 'FW04848', - answeredQuestions: [] - }, - questions: [], - parts: [ - { - childPartQuestions: [] - } - ] - } - ]; - wrapper.vm.dispatchStoreAction = vi.fn(() => - ({ - data: [] - })); - - // Act - wrapper.vm.forwardButtonAction(); - - await nextTick(); - - // Assert - expect(wrapper.vm.getPartFromCapabilityQuestionAnswer).toHaveBeenCalled; - - wrapper.unmount(); - }); - - test('Should trigger navigateForward', async () => { - // Arrange - const { wrapper } = setupMocks({}); - - wrapper.vm.questionsData = [ - { - glassLocation: 'Windshield', - glassName: 'Single', - answerData: { - answerResult: 'FW04848', - answeredQuestions: [] - }, - questions: [], - parts: [ - { - childPartQuestions: [] - } - ] - } - ]; - useMainStore().getPartsOrQuestions = vi.fn(() => - ({ - data: { - partsOrQuestions: [] - } - })); - wrapper.vm.navigateForward = vi.fn(); - - // Act - await wrapper.vm.forwardButtonAction(); - - // Assert - expect(wrapper.vm.navigateForward).toHaveBeenCalled(); - - wrapper.unmount(); - }); - }); -}); - -function setupMocks({ - mountOptionsMockData = { - router: { - navigate: vi.fn() - }, - actionList: [ - { - actionName: 'saveCapabilityQuestionAnswers', - data: {} - }, - { - actionName: 'getPartsOrQuestions', - data: {} - } - ], - route: { - query: { - issPage: 'capability-questions' - } - }, - data() { - return { - computedSwitcher: [ - { - glassLocation: 'Windshield', - glassName: 'Single', - answerData: { - answerResult: 'FW04848', - answeredQuestions: [] - } - } - ] - }; - }, - questionsData: { - get() { - return this.computedSwitcher; - }, - set(val) { - this.computedSwitcher = val; - } - } - } -}) { - useMainStore().getPartsOrQuestions = vi.fn(() => - ({ - data: { - partsOrQuestions: [] - } - })); - const mountOptions = getMountOptions({ - ...mountOptionsMockData, - mixins: [baseMixin, vehicleQuestionsMixin] - }); - mountOptions.attachTo = document.body; - - const wrapper = shallowMount(contactDetails, mountOptions); - - return { wrapper }; -} From 4e86ccec33db93a07fdfc999039fb649947a79af Mon Sep 17 00:00:00 2001 From: brydon1 Date: Fri, 7 Jul 2023 12:18:29 -0400 Subject: [PATCH 03/21] Partial test addition --- .eslintrc.js | 64 +++++--- .../contact-details/contact-details.spec.js | 149 ++++++++++++++++++ .../contact-details/contact-details.vue | 20 ++- 3 files changed, 206 insertions(+), 27 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index aa0da332..706c027e 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1,21 +1,45 @@ -module.exports = { - extends: [ - 'eslint-config-airbnb-base', - 'plugin:vue/vue3-strongly-recommended' - ], - rules: { - 'linebreak-style': 'off', - 'vue/component-definition-name-casing': ['warn', 'kebab-case'], - 'vue/require-default-prop': 'off', - 'vue/attribute-hyphenation': ['warn', 'never'], - 'vue/v-on-event-hyphenation': ['warn', 'never'] - }, - settings: { - 'import/resolver': { - alias: { - map: [['@', './src/']], - extensions: ['.js', '.vue'] - } +module.exports = { + env: { + browser: true, + jest: true + }, + extends: [ + 'eslint-config-airbnb-base', + 'plugin:vue/vue3-recommended' + ], + rules: { + 'linebreak-style': 'off', + 'vue/component-definition-name-casing': ['warn', 'kebab-case'], + 'vue/require-default-prop': 'off', + 'vue/attribute-hyphenation': ['warn', 'never'], + 'vue/v-on-event-hyphenation': ['warn', 'never'], + 'object-curly-newline': ['error', { consistent: true }], + 'function-paren-newline': ['error', 'never'], + 'operator-linebreak': ['error', 'before'], + 'implicit-arrow-linebreak': ['error', 'below'], + 'comma-dangle': ['error', 'never'], + indent: ['error', 4], + 'vue/html-indent': 'off', + 'vue/html-closing-bracket-newline': ['error', { + singleline: 'never', + multiline: 'never' + }], + 'vue/html-self-closing': ['error', { + html: { + void: 'any', + normal: 'any', + component: 'any' + }, + svg: 'always', + math: 'always' + }] + }, + settings: { + 'import/resolver': { + alias: { + map: [['@', './src/']], + extensions: ['.js', '.vue'] + } + } } - } -}; \ No newline at end of file +}; diff --git a/src/layouts/contact-details/contact-details.spec.js b/src/layouts/contact-details/contact-details.spec.js index db058176..f8e5293a 100644 --- a/src/layouts/contact-details/contact-details.spec.js +++ b/src/layouts/contact-details/contact-details.spec.js @@ -1,4 +1,153 @@ // Components import contactDetails from '@/layouts/contact-details/contact-details.vue'; +// Supporting Files +import { shallowMount } from '@vue/test-utils'; +import { getMountOptions } from '@/helpers/unit-test-helper'; +import { getRandomString } from '@/helpers/data-generator'; +// import { useMainStore } from '@/store'; +// import vehicleQuestionsMixin from '@/mixins/vehicle-questions-mixin'; +// import { nextTick } from 'vue'; +// import baseMixin from '../../mixins/base-mixin'; // TODO finish this file + +describe('contactDetails.vue', () => { + describe('Rendering', () => { + test('Should render site header', () => { + // Arrange + const wrapper = shallowMount(contactDetails, getMountOptions()); + + // Act + const siteHeader = wrapper.findComponent({ ref: 'siteHeader' }); + + // Assert + expect(siteHeader.exists()).toBe(true); + }); + test('Should render sub title', () => { + // Arrange + const wrapper = shallowMount(contactDetails, getMountOptions()); + + // Act + const siteSubHeader = wrapper.findComponent({ ref: 'siteSubHeader' }); + + // Assert + expect(siteSubHeader.exists()).toBe(true); + }); + test('Should render first name question subcomponent', () => { + // Arrange + const wrapper = shallowMount(contactDetails, getMountOptions()); + + // Act + const firstNameQuestion = wrapper.findComponent({ ref: 'firstNameQuestion' }); + + // Assert + expect(firstNameQuestion.exists()).toBe(true); + }); + test('Should render last name question subcomponent', () => { + // 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 wrapper = shallowMount(contactDetails, getMountOptions()); + + // Act + const textUpdatesCheckbox = wrapper.findComponent({ ref: 'requestTextUpdatesCheckbox' }); + + // Assert + expect(textUpdatesCheckbox.exists()).toBe(true); + }); + test('Should render technician notes textarea question subcomponent', () => { + // Arrange + const wrapper = shallowMount(contactDetails, getMountOptions()); + + // Act + const notesQuestion = wrapper.findComponent({ ref: 'notesQuestion' }); + + // Assert + expect(notesQuestion.exists()).toBe(true); + }); + test.only('Should render disclaimer text', () => { + // Arrange + const disclaimerText = 'the disclaimer.'; + const mockMixin = { + methods: { + getCmsContent: jest.fn().mockImplementation(() => + disclaimerText) + } + }; + const mountOptions = getMountOptions(); + mountOptions.mixins = [mockMixin]; + const wrapper = shallowMount(contactDetails, mountOptions); + wrapper.vm.setCmsContent = jest.fn(); + const expectedDisclaimerText = `*${disclaimerText} I also agree to Safelite\'s`; + + // Act + const componentText = wrapper.text(); + + // Assert + expect(componentText).toContain(expectedDisclaimerText); + }); + test('Should render privacy policy link', () => {}); + test('Should render terms of use link', () => {}); + test('Should render site footer', () => { + // Arrange + const wrapper = shallowMount(contactDetails, getMountOptions()); + + // Act + const footer = wrapper.findComponent({ ref: 'siteFooter' }); + + // Assert + expect(footer.exists()).toBe(true); + }); + test('Existing store data yields expected render', () => { + // Arrange + // const wrapper = shallowMount(contactDetails); + // set up mock store to contain values for firstname,lastname, email and phone number + // set up mock cms content + // Assert + + // expect(wrapper.element).toMatchSnapshot(); + }); + }); + + describe('Errors', () => { + test('Error rendered when first name cleared', () => {}); + test('Error rendered when last name cleared', () => {}); + test('Error rendered when email cleared', () => {}); + test('Error rendered when email wrong syntax', () => {}); + test('Error rendered when phone number cleared', () => {}); + test('Error rendered when phone number wrong syntax', () => {}); + }); + + describe('Navigation', () => { + test('Back button clicked triggers navigation', () => {}); + test('Forward button clicked triggers navigation', () => {}); + }); +}); diff --git a/src/layouts/contact-details/contact-details.vue b/src/layouts/contact-details/contact-details.vue index e32c3128..96aed43f 100644 --- a/src/layouts/contact-details/contact-details.vue +++ b/src/layouts/contact-details/contact-details.vue @@ -6,14 +6,17 @@ @invalidSubmit="onInvalidSubmit">
- +
-

+

{{ textUpdateDisclaimerText }} I also agree to Safelite's Date: Fri, 7 Jul 2023 14:20:22 -0400 Subject: [PATCH 04/21] Refactoring global rules --- src/constants/global-rules.js | 13 ++++ .../customer-questions/customer-questions.vue | 24 ++++--- .../capability-questions.vue | 16 ++--- .../contact-details/contact-details.spec.js | 47 ++++++++++--- .../contact-details/contact-details.vue | 67 ++++++++++--------- .../molding-questions/molding-questions.vue | 12 ++-- src/layouts/part-questions/part-questions.vue | 13 ++-- .../policy-holder-details.vue | 17 +++-- .../policy-vehicles/policy-vehicles.vue | 14 ++-- .../provider-preference.vue | 11 +-- .../tpa-recal-modal/tpa-recal-modal.vue | 8 ++- .../service-packages/service-packages.vue | 10 +-- .../vin-lookup-methods/vin-lookup-methods.vue | 16 ++--- src/layouts/welcome-page/welcome-page.vue | 47 ++++++------- src/main.js | 24 +++++++ 15 files changed, 206 insertions(+), 133 deletions(-) create mode 100644 src/constants/global-rules.js diff --git a/src/constants/global-rules.js b/src/constants/global-rules.js new file mode 100644 index 00000000..becfe2f9 --- /dev/null +++ b/src/constants/global-rules.js @@ -0,0 +1,13 @@ +const globalRules = { + POLICYHOLDER_FIRST_NAME_REQUIRED: 'policyholder-first-name-required', + POLICYHOLDER_LAST_NAME_REQUIRED: 'policyholder-last-name-required', + FIRST_NAME_REQUIRED: 'first-name-required', + LAST_NAME_REQUIRED: 'last-name-required', + PHONE_NUMBER_REQUIRED: 'phone-number-required', + EMAIL_ADDRESS_REQUIRED: 'email-required', + EMAIL_ADDRESS_FORMAT: 'email-address-format', + PHONE_NUMBER_FORMAT: 'phone-number-format', + OPTION_REQUIRED: 'option-required' +}; + +export default globalRules; diff --git a/src/layouts/address-lookup/customer-questions/customer-questions.vue b/src/layouts/address-lookup/customer-questions/customer-questions.vue index f4062fe6..349d0702 100644 --- a/src/layouts/address-lookup/customer-questions/customer-questions.vue +++ b/src/layouts/address-lookup/customer-questions/customer-questions.vue @@ -8,7 +8,7 @@ ref="firstName" inputId="08497a2efd9a4a73a70360ab47b4838d" disableAutoFill - validationRules="first-name-required" /> + :validationRules="rules.firstName" />

@@ -19,21 +19,15 @@ ref="lastName" inputId="0030e56a57e74a4ab92de7fb8e97fec5" disableAutoFill - validationRules="last-name-required" /> + :validationRules="rules.lastName" />
- \ No newline at end of file + diff --git a/src/layouts/policy-vehicles/policy-vehicles.vue b/src/layouts/policy-vehicles/policy-vehicles.vue index 3466f5b9..d6cd1418 100644 --- a/src/layouts/policy-vehicles/policy-vehicles.vue +++ b/src/layouts/policy-vehicles/policy-vehicles.vue @@ -1,22 +1,35 @@ - \ No newline at end of file + From 8a139b96f6f64be591b3648eee1bc6b7fb5c8007 Mon Sep 17 00:00:00 2001 From: brydon1 Date: Tue, 11 Jul 2023 16:51:33 -0400 Subject: [PATCH 11/21] Adding final touches to contact details --- .../contact-details/contact-details.vue | 36 +- src/store/index.js | 2651 ++++++++--------- src/ux-components/checkbox/checkbox.vue | 8 +- 3 files changed, 1331 insertions(+), 1364 deletions(-) diff --git a/src/layouts/contact-details/contact-details.vue b/src/layouts/contact-details/contact-details.vue index c2ff4ade..96fc0c4d 100644 --- a/src/layouts/contact-details/contact-details.vue +++ b/src/layouts/contact-details/contact-details.vue @@ -33,12 +33,12 @@ :validationRules="rules.lastName" /> + :validationRules="rules.emailAddress" />

@@ -135,13 +136,15 @@ export default { }); }, data() { + const { firstName, lastName, emailAddress, phoneNumber, textUpdates, notesForTechnician } + = useMainStore().contactInfo; return { - firstName: useMainStore().order.customer.firstName, - lastName: useMainStore().order.customer.lastName, - email: useMainStore().order.customer.emailAddress, - phoneNumber: useMainStore().order.customer.phoneNumber, - requestTextUpdates: false, - notesForTechnician: '', + firstName, + lastName, + emailAddress, + phoneNumber, + requestTextUpdates: textUpdates, + notesForTechnician, widget: { siteHeader: 'SiteHeaderWidget', siteSubHeader: 'SiteSubHeaderWidget', @@ -157,7 +160,7 @@ export default { rules: { firstName: globalRules.FIRST_NAME_REQUIRED, lastName: globalRules.LAST_NAME_REQUIRED, - email: `${globalRules.EMAIL_ADDRESS_REQUIRED}|${globalRules.EMAIL_ADDRESS_FORMAT}`, + emailAddress: `${globalRules.EMAIL_ADDRESS_REQUIRED}|${globalRules.EMAIL_ADDRESS_FORMAT}`, phoneNumber: `${globalRules.PHONE_NUMBER_REQUIRED}|${globalRules.PHONE_NUMBER_FORMAT}` } }; @@ -188,6 +191,15 @@ export default { * @summary Steps to perform when forward button clicked. */ forwardButtonAction() { + const contactInfo = { + firstName: this.firstName, + lastName: this.lastName, + emailAddress: this.emailAddress, + phoneNumber: this.phoneNumber, + textUpdates: this.requestTextUpdates, + notesForTechnician: this.notesForTechnician + }; + useMainStore().updateContactInfo(contactInfo); this.$router.navigate(this.navigationScenarios.CLICKED_FORWARD, this.$route); } diff --git a/src/store/index.js b/src/store/index.js index 7a58d4f3..028d5a5f 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -1,6 +1,9 @@ +/* eslint-disable max-len */ import { defineStore } from 'pinia'; -import { endpoints } from '@/constants/endpoints.js'; +import { endpoints } from '@/constants/endpoints'; +// eslint-disable-next-line import/no-cycle import { getDateForSavedSessionTimeout } from '@/helpers/session-helper'; +// eslint-disable-next-line import/no-cycle import globalMethods from '@/global-methods'; import { experimentTriggers } from '@/constants/experiments'; import { applicationConfig } from '@/constants/application-config'; @@ -10,1458 +13,1406 @@ import { coverageStatuses } from '@/constants/coverage-statuses'; const storeId = 'main'; -const getDefaultState = () => { - return { - order: { - vehicle: { - year: null, - make: null, - model: null, - style: null, - carId: null, - category: null, - vin: null, - imageUrl: null, - imageVifNumber: null, - imageColor: null, - registration: { - licensePlate: null, - address: null, - city: null, - state: null, - zipCode: null, - firstName: null, - lastName: null - } - }, - damage: { - isRepair: null, - numberOfChips: null, - glassToReplace: null, - partQuestionAnswers: null, - moldingQuestionAnswers: null, - capabilityQuestionAnswers: null - }, - policy: { - policyNumber: null, - policyZipCode: null, - dateOfLoss: null, - damageCause: null, - damageState: null, - damageCity: null, - isDamageGlassOnly: null, - noCompensation: null, - deductible: { - repair: null, // numerical value; how much customer owes on deductible in repair case - replace: null // numerical value; how much customer owes on deductible in replace case, - } - }, - customer: { - address: { - streetAddress: null, - streetAddress2: null, - city: null, - state: null, - zipCode: null +const getDefaultState = () => + ({ + order: { + vehicle: { + year: null, + make: null, + model: null, + style: null, + carId: null, + category: null, + vin: null, + imageUrl: null, + imageVifNumber: null, + imageColor: null, + registration: { + licensePlate: null, + address: null, + city: null, + state: null, + zipCode: null, + firstName: null, + lastName: null + } + }, + damage: { + isRepair: null, + numberOfChips: null, + glassToReplace: null, + partQuestionAnswers: null, + moldingQuestionAnswers: null, + capabilityQuestionAnswers: null + }, + policy: { + policyNumber: null, + policyZipCode: null, + dateOfLoss: null, + damageCause: null, + damageState: null, + damageCity: null, + isDamageGlassOnly: null, + noCompensation: null, + deductible: { + repair: null, // numerical value; how much customer owes on deductible in repair case + replace: null // numerical value; how much customer owes on deductible in replace case, + } + }, + customer: { + address: { + streetAddress: null, + streetAddress2: null, + city: null, + state: null, + zipCode: null + }, + firstName: null, + lastName: null, + emailAddress: null, + phoneNumber: null + }, + serviceLocation: { + address: null, + city: null, + state: null, + zipCode: null, + zipCodeCtu: null + }, + lineItems: { + glassParts: null, + otherParts: null, + supportingItems: null, + vaps: null + }, + payment: { + isInsurance: true, + insuranceCoverage: { + isVerified: false, + coverageStatus: coverageStatuses.PENDING + } + }, + referralNumber: null, + referralDate: null, + contactInfo: { + firstName: null, + lastName: null, + emailAddress: null, + phoneNumber: null, + textUpdates: false, + notesForTechnician: '' + } }, - firstName: null, - lastName: null, - emailAddress: null, - phoneNumber: null - }, - serviceLocation: { - address: null, - city: null, - state: null, - zipCode: null, - zipCodeCtu: null - }, - lineItems: { - glassParts: null, - otherParts: null, - supportingItems: null, - vaps: null - }, - payment: { - isInsurance: true, - insuranceCoverage: { - isVerified: false, - coverageStatus: coverageStatuses.PENDING + applicationUser: { + experiments: [], + eventBus: [], + pageData: {}, + savedSessionTimeout: getDateForSavedSessionTimeout(), + saveSessionPromise: null, + savedSessionId: null, + crmCustomerId: null, + lastPageVisited: null, + triggeredSiteEntry: false + }, + issConfig: { + clientName: 'Generic Insurance', // this is the default and will be overriden by the client's name + clientDisplayName: 'Generic Insurance', // this is the default and will be overridden by the client's name or client display name. + styleSheet: '', // Stylesheet used by the client. + accountNumber: 0, // Account number used by the client. + isCoverageEnabled: false, // Indicates if we should be calling coverage on this flow. + isClaimRegistrationRequired: false, // Indicates if the claim registration call needs to be made for a client to complete coverage verification. + isAuthenticated: false, // Indicates if user is authenticated or not. + enableTPAFlow: false, // Indicates if the TPA flow is supported for this client. + successReturnURL: null, // Client URL to return the user to upon a successfull flow completetion (order submission). + failureReturnURL: null, // Client URL to return the user to when they encounter an error or bailout and are unable to complete the flow. + disabledFields: { // Fields that are disabled and read only if sent over from a client. + policyNumber: null, + policyZipCode: null + } } - }, - referralNumber: null, - referralDate: null, - }, - applicationUser: { - experiments: [], - eventBus: [], - pageData: {}, - savedSessionTimeout: getDateForSavedSessionTimeout(), - saveSessionPromise: null, - savedSessionId: null, - crmCustomerId: null, - lastPageVisited: null, - triggeredSiteEntry: false - }, - issConfig: { - clientName: 'Generic Insurance', // this is the default and will be overriden by the client's name - clientDisplayName: 'Generic Insurance', // this is the default and will be overridden by the client's name or client display name. - styleSheet: '', // Stylesheet used by the client. - accountNumber: 0, // Account number used by the client. - isCoverageEnabled: false, // Indicates if we should be calling coverage on this flow. - isClaimRegistrationRequired: false, // Indicates if the claim registration call needs to be made for a client to complete coverage verification. - isAuthenticated: false, // Indicates if user is authenticated or not. - enableTPAFlow: false, // Indicates if the TPA flow is supported for this client. - successReturnURL: null, // Client URL to retrurn the user to upon a successfull flow completetion (order submission). - failureReturnURL: null, // Client URL to retrurn the user to when they encounter an error or bailout and are unable to complete the flow. - disabledFields: { // Fields that are disabled and read only if sent over from a client. - policyNumber: null, - policyZipCode: null - } - } - }; -}; + }); export const state = getDefaultState(); export const useMainStore = defineStore({ id: storeId, - state: () => state, + state: () => + state, getters: { - hasRecalibrationPart: (state) => getHasRecalibrationPart(state), - vehicle: (state) => state.order.vehicle, - damage: (state) => state.order.damage, - lineItems: (state) => state.order.lineItems, - payment: (state) => state.order.payment, - policy: (state) => state.order.policy, - hasAnyNonWindshieldGlassParts: (state) => !state.order.policy.isDamageGlassOnly, - isClaimRegistrationRequired: (state) => state.issConfig.isClaimRegistrationRequired, - eventBusItem: (state) => ( eventCategory, eventSubCategory) => { - - const matchedEvent = state.applicationUser.eventBus.find( - ( { category, subCategory } ) => - category === eventCategory && subCategory === eventSubCategory - ); - return matchedEvent?.eventValue; - }, - eventBus: (state) => state.applicationUser.eventBus, - applicationUserObj: (state) => state.applicationUser, - pageData: (state) => (page) => { - return state.applicationUser.pageData[page]; - }, - customerData: (state) => { - if (state.order.vehicle.registration.address) - { - const registration = state.order.vehicle.registration; - return { - addressQuestions: { - streetAddress: registration.address, - city: registration.city, - state: registration.state, - zipCode: registration.zipCode + hasRecalibrationPart: (state) => + getHasRecalibrationPart(state), + vehicle: (state) => + state.order.vehicle, + damage: (state) => + state.order.damage, + lineItems: (state) => + state.order.lineItems, + payment: (state) => + state.order.payment, + policy: (state) => + state.order.policy, + hasAnyNonWindshieldGlassParts: (state) => + !state.order.policy.isDamageGlassOnly, + isClaimRegistrationRequired: (state) => + state.issConfig.isClaimRegistrationRequired, + eventBusItem: (state) => + (eventCategory, eventSubCategory) => { + const matchedEvent = state.applicationUser.eventBus.find(({ category, subCategory }) => + category === eventCategory && subCategory === eventSubCategory); + return matchedEvent?.eventValue; }, - firstName: registration.firstName, - lastName: registration.lastName - } - } - else { - const address = state.order.customer.address; - return { - addressQuestions: { - streetAddress: address.streetAddress, - city: address.city, - state: address.state, - zipCode: address.zipCode - }, - firstName: state.order.customer.firstName, - lastName: state.order.customer.lastName - } - } - }, - experimentOrder: (state) => { - return { - issVehicleYear: state.order.vehicle.year, - issVehicleMake: state.order.vehicle.make, - issVehicleModel: state.order.vehicle.model, - issVehicleStyle: state.order.vehicle.style, - issIsRepair: state.order.damage.isRepair, - issNumberOfChips: state.order.damage.numberOfChips, - issCarId: state.order.vehicle.carId, - issServiceCity: state.order.serviceLocation.city, - issServiceState: state.order.serviceLocation.state, - issServiceZipCode: state.order.serviceLocation.zipCode, - issParentAccountNumber: state.order.accountNumber, - issIsCoverageVerified: state.order.payment.insuranceCoverage.isVerified, - issHasRecalibrationPart: getHasRecalibrationPart(state), - issSelectedMultiGlass: state.order.damage.glassToReplace?.length > 1, - issSelectedWindshieldGlass: getNonFalseValuesOfPropertyInArrayOfObjects( - state.order.damage.glassToReplace, - 'glassLocation' - ).includes(damageLocationsSelected.WINDSHIELD), - issSelectedBackGlass: getNonFalseValuesOfPropertyInArrayOfObjects( - state.order.damage.glassToReplace, - 'glassLocation' - ).includes(damageLocationsSelected.REAR), - issSelectedDriverSideGlass: getNonFalseValuesOfPropertyInArrayOfObjects( - state.order.damage.glassToReplace, - 'glassLocation' - ).includes(damageLocationsSelected.DRIVER), - issSelectedPassengerSideGlass: getNonFalseValuesOfPropertyInArrayOfObjects( - state.order.damage.glassToReplace, - 'glassLocation' - ).includes(damageLocationsSelected.PASSENGER), - issOrderPartNumbers: [ - ...getNonFalseValuesOfPropertyInArrayOfObjects( - state.order.lineItems.glassParts, - 'partNumber' - ), - ...getNonFalseValuesOfPropertyInArrayOfObjects( - state.order.lineItems.otherParts, - 'partNumber' - ) - ], + eventBus: (state) => + state.applicationUser.eventBus, + applicationUserObj: (state) => + state.applicationUser, + pageData: (state) => + (page) => + state.applicationUser.pageData[page], + customerData: (state) => { + if (state.order.vehicle.registration.address) { + const { registration } = state.order.vehicle; + return { + addressQuestions: { + streetAddress: registration.address, + city: registration.city, + state: registration.state, + zipCode: registration.zipCode + }, + firstName: registration.firstName, + lastName: registration.lastName + }; + } - issOrderPartTypes: [ - ...getNonFalseValuesOfPropertyInArrayOfObjects( - state.order.lineItems.glassParts, - 'recalibrationType' - ), - ...getNonFalseValuesOfPropertyInArrayOfObjects( - state.order.lineItems.otherParts, - 'recalibrationType' - ) - ] - }; - }, - experimentSettings: (state) => { - return state.applicationUser.experiments - .map((x) => x.settings) - .reduce((r, c) => Object.assign(r, c), {}) ?? {} - } + const { address } = state.order.customer; + return { + addressQuestions: { + streetAddress: address.streetAddress, + city: address.city, + state: address.state, + zipCode: address.zipCode + }, + firstName: state.order.customer.firstName, + lastName: state.order.customer.lastName + }; + }, + contactInfo: (s) => + ({ + firstName: s.order.contactInfo.firstName ?? s.order.customer.firstName, + lastName: s.order.contactInfo.lastName ?? s.order.customer.lastName, + emailAddress: s.order.contactInfo.emailAddress ?? s.order.customer.emailAddress, + phoneNumber: s.order.contactInfo.phoneNumber ?? s.order.customer.phoneNumber, + textUpdates: s.order.contactInfo.textUpdates ?? false, + notesForTechnician: s.order.contactInfo.notesForTechnician + }), + experimentOrder: (state) => + ({ + issVehicleYear: state.order.vehicle.year, + issVehicleMake: state.order.vehicle.make, + issVehicleModel: state.order.vehicle.model, + issVehicleStyle: state.order.vehicle.style, + issIsRepair: state.order.damage.isRepair, + issNumberOfChips: state.order.damage.numberOfChips, + issCarId: state.order.vehicle.carId, + issServiceCity: state.order.serviceLocation.city, + issServiceState: state.order.serviceLocation.state, + issServiceZipCode: state.order.serviceLocation.zipCode, + issParentAccountNumber: state.order.accountNumber, + issIsCoverageVerified: state.order.payment.insuranceCoverage.isVerified, + issHasRecalibrationPart: getHasRecalibrationPart(state), + issSelectedMultiGlass: state.order.damage.glassToReplace?.length > 1, + issSelectedWindshieldGlass: getNonFalseValuesOfPropertyInArrayOfObjects(state.order.damage.glassToReplace, + 'glassLocation').includes(damageLocationsSelected.WINDSHIELD), + issSelectedBackGlass: getNonFalseValuesOfPropertyInArrayOfObjects(state.order.damage.glassToReplace, + 'glassLocation').includes(damageLocationsSelected.REAR), + issSelectedDriverSideGlass: getNonFalseValuesOfPropertyInArrayOfObjects(state.order.damage.glassToReplace, + 'glassLocation').includes(damageLocationsSelected.DRIVER), + issSelectedPassengerSideGlass: getNonFalseValuesOfPropertyInArrayOfObjects(state.order.damage.glassToReplace, + 'glassLocation').includes(damageLocationsSelected.PASSENGER), + issOrderPartNumbers: [ + ...getNonFalseValuesOfPropertyInArrayOfObjects(state.order.lineItems.glassParts, + 'partNumber'), + ...getNonFalseValuesOfPropertyInArrayOfObjects(state.order.lineItems.otherParts, + 'partNumber') + ], + + issOrderPartTypes: [ + ...getNonFalseValuesOfPropertyInArrayOfObjects(state.order.lineItems.glassParts, + 'recalibrationType'), + ...getNonFalseValuesOfPropertyInArrayOfObjects(state.order.lineItems.otherParts, + 'recalibrationType') + ] + }), + experimentSettings: (state) => + state.applicationUser.experiments + .map((x) => + x.settings) + .reduce((r, c) => + Object.assign(r, c), {}) ?? {} }, actions: { - // Content API Actions - - lookupVinByAddress({ licenseLastName, licenseStreetAddress, licenseZip, licenseState }) { - return globalMethods.callHttpClient({ - method: endpoints.LookupVinByAddress.method, - endpoint: endpoints.LookupVinByAddress.url, - payload: { - licenseLastName: licenseLastName, - licenseStreetAddress: licenseStreetAddress, - licenseZip: licenseZip, - licenseState: licenseState - } - }); - }, + // Content API Actions - getRouteInfo(pageName) { - return globalMethods.callHttpClient({ - method: endpoints.GetRouteInfo.method, - endpoint: endpoints.GetRouteInfo.url(applicationConfig.APPLICATION_ABBREVIATION), - payload: { - pageName: pageName - } - }); - }, - getHomepageName() { - return globalMethods.callHttpClient({ - method: endpoints.GetHomepageInfo.method, - endpoint: endpoints.GetHomepageInfo.url(applicationConfig.APPLICATION_ABBREVIATION) - }); - }, - - getPageData(pageName) { - return globalMethods.callHttpClient({ - method: endpoints.GetPageData.method, - endpoint: endpoints.GetPageData.url(applicationConfig.APPLICATION_ABBREVIATION, pageName), - payload: {} - }); - }, - - // Vehicle API Actions - getVehicleYears() { - return globalMethods.callHttpClient({ - method: endpoints.GetVehicleYears.method, - endpoint: endpoints.GetVehicleYears.url, - payload: {} - }); - }, - - getVehicleMakes() { - return globalMethods.callHttpClient({ - method: endpoints.GetVehicleMakes.method, - endpoint: endpoints.GetVehicleMakes.url + this.order.vehicle.year, - payload: {} - }); - }, - - getVehicleModels() { - return globalMethods.callHttpClient({ - method: endpoints.GetVehicleModels.method, - endpoint: `${endpoints.GetVehicleModels.url}/${this.order.vehicle.year}/${this.order.vehicle.make}`, - payload: {} - }); - }, - - getVehicleStyles() { - return globalMethods.callHttpClient({ - method: endpoints.GetVehicleStyles.method, - endpoint: `${endpoints.GetVehicleStyles.url}/${this.order.vehicle.year}/${this.order.vehicle.make}/${this.order.vehicle.model}`, - payload: {} - }); - }, - - getDamageOptions(carId) { - return globalMethods.callHttpClient({ - methods: endpoints.GetDamageOptions.method, - endpoint: `${endpoints.GetDamageOptions.url}/${carId}`, - payload: {} - }); - }, - getIsVinbyAddressPermissible(){ - try{ - const response = globalMethods.callHttpClient({ - method:endpoints.IsVinbyAddressPermissible.method, - endpoint:`${endpoints.IsVinbyAddressPermissible.url}?state=${this.order.customer.address.state}`, - payload: {} - }); - return response; - } catch (responseError) { - return { - error: { - status: responseError.status - } - }; - } - }, - getCoveragePolicyInfo({accountNumber, policyNumber, dateOfLoss, zipCode}){ - //TODO: replace place holder correlationId with the real thing - const placeHolderCorrelationId = "00000000-0000-0000-0000-000000000000"; - try{ - const response = globalMethods.callHttpClient({ - method:endpoints.CoveragePolicyInfo.method, - endpoint:endpoints.CoveragePolicyInfo.url, - payload: { - accountNumber: accountNumber, - policyNumber: policyNumber, - dateOfLoss: dateOfLoss, - zipCode: zipCode, - correlationId: placeHolderCorrelationId - } - }); - return response; - } catch (responseError) { - return { - error: { - status: responseError.status - } - }; - } - }, - registerClaim() { - // TODO: replace place holder correlationId with the real thing - const placeHolderCorrelationId = "00000000-0000-0000-0000-000000000000"; - globalMethods.callHttpClient({ - method: endpoints.RegisterClaim.method, - endpoint: endpoints.RegisterClaim.url, - payload: - { - correlationId: placeHolderCorrelationId, - accountNumber: this.issConfig.accountNumber?.toString() ?? "", - insured: { - firstName: this.order.customer.firstName, - lastName: this.order.customer.lastName, - address: { - addressLine1: this.order.customer.address.streetAddress, - addressLine2: this.order.customer.address.streetAddress2, - city: this.order.customer.address.city, - state: this.order.customer.address.state, - zipCode: this.order.customer.address.zipCode, - country: "US" // TODO set from store - }, - homePhone: { - number: this.order.customer.phoneNumber - } - }, - caller: { - homePhone: {} - }, - policyInfo: { - policyNumber: this.order.policy.policyNumber, - safelitePolicy: { - policies: [] - } - }, - lossInfo: { - dateOfLoss: this.order.policy.dateOfLoss, - location: { - city: this.order.policy.damageCity, - state: this.order.policy.damageState, - country: "US" // TODO set from store - }, - vehicle: { - year: this.order.vehicle.year?.toString() ?? "", - make: this.order.vehicle.make, - model: this.order.vehicle.model, - vin: this.order.vehicle.vin - }, - }, - damageDescription: this.order.policy.damageCause - } - }).then((response) => { - const registerClaimFailed = response.data.isError; - this.order.payment.insuranceCoverage.isVerified = !registerClaimFailed; - this.order.payment.insuranceCoverage.coverageStatus = registerClaimFailed - ? coverageStatuses.PENDING - : this.policy.noCompensation - ? coverageStatuses.NO_COMP - : coverageStatuses.VERIFIED; - },(error) => { - this.order.payment.insuranceCoverage.isVerified = false; - this.order.payment.insuranceCoverage.coverageStatus = coverageStatuses.PENDING; - }); - }, - async lookupVinByPlate(licensePlate, licenseState) { - try { - const response = await globalMethods.callHttpClient({ - method: endpoints.LookupVinByPlate.method, - endpoint: endpoints.LookupVinByPlate.url, - payload: { - licensePlate: licensePlate, - licenseState: licenseState - } - }); - - return response; - } catch (responseError) { - return { - error: { - status: responseError.status - } - }; - } - }, - - // PartsOrQuestions API Actions - async getPartsOrQuestions() { - this.resetPartsAndDependencies(); - - const vehicle = this.vehicle; - const damage = this.damage; - const order = this.order; - - const carId = vehicle.carId; - const glassArray = damage.glassToReplace; - const zipCode = vehicle.registration.zipCode ? vehicle.registration.zipCode : order.customer.address.zipCode; - const vin = vehicle.vin; - - // create a new array to avoid mutating state - const glassArrayForPayload = convertGlassPieceNamingForApi(glassArray); - - const response = await globalMethods.callHttpClient({ - method: endpoints.GetPartsOrQuestions.method, - endpoint: endpoints.GetPartsOrQuestions.url, - payload: { - carId: carId, - glassPieces: glassArrayForPayload, - zip: zipCode, - vin: vin - } - }); - - // Flatten location and name properties - response.data.partsOrQuestions = convertGlassPieceNamingFromApi( - response.data.partsOrQuestions - ); - - return response; - }, - - async getParts() { - const vehicle = this.order.vehicle; - const damage = this.order.damage; - const order = this.order; - - const carId = vehicle.carId; - const glassArray = damage.glassToReplace; - const resultsArray = damage.partQuestionAnswers; - const zipCode = vehicle.registration.zipCode ? vehicle.registration.zipCode : order.customer.address.zipCode; - const vin = vehicle.vin; - - // create a new array to avoid mutating state - const glassArrayForPayload = convertGlassPieceNamingForApi(glassArray); - const resultsArrayForPayload = convertResultsForApi(resultsArray); - - const response = await globalMethods.callHttpClient({ - method: endpoints.GetParts.method, - endpoint: endpoints.GetParts.url, - payload: { - carId: carId, - glassPieces: glassArrayForPayload, - answerResults: resultsArrayForPayload, - zip: zipCode, - vin: vin - } - }); - - // Flatten location and name properties - response.data.glassPieceParts = convertGlassPieceNamingFromApi( - response.data.glassPieceParts - ); - - return response; - }, - - getCapabilityQuestions(carId, partNumber) { - return globalMethods.callHttpClient({ - method: endpoints.GetCapabilityQuestions.method, - endpoint: `${endpoints.GetCapabilityQuestions.url}/${carId}/${partNumber}` - }); - }, - - getPartFromCapabilityQuestionAnswer(glassLocation) { - const pageData =this.pageData(issPageValues.CAPABILITY_QUESTIONS); - - const part = pageData.partsOrQuestions.find((x) => x.glassLocation === glassLocation) - .parts[0]; - const capabilityQuestionAnswers = this.order.damage.capabilityQuestionAnswers; - const capabilityQuestionAnswersForPart = capabilityQuestionAnswers.find( - (x) => x.glassLocation === glassLocation - ); - - return globalMethods.callHttpClient({ - method: endpoints.GetPartFromCapabilityAnswer.method, - endpoint: endpoints.GetPartFromCapabilityAnswer.url, - payload: { - part, - capabilityAnswerResults: capabilityQuestionAnswersForPart - } - }); - }, - async getWipers() { - const carId = this.order.vehicle.carId; - //WARNING - //TODO: this is temp test code until serviceLocation is complete. - //const serviceZipCode = this.order.serviceLocation.zipCode; - const serviceZipCode = '44902'; - return globalMethods - .callHttpClient({ - method: endpoints.GetWipers.method, - endpoint: `${endpoints.GetWipers.url}/${carId}/${serviceZipCode}` - }) - .catch((error) => { - // The wiper service sometimes returns 500s on legitimate carId/zipCode combination - return empty array instead of breaking flow - console.error(error); - return []; - }); - }, - - async getRainDefense() { - return globalMethods - .callHttpClient({ - method: endpoints.GetRainDefense.method, - endpoint: `${endpoints.GetRainDefense.url}` - }) - .catch((error) => { - // The wiper service sometimes returns 500s on legitimate carId/zipCode combination - return empty array instead of breaking flow - console.error(error); - return []; - }); - - }, - - async getSupportingItems() { - const glassPartsArray = this.order.lineItems.glassParts ?? []; - const carId = this.order.vehicle.carId; - const isRepair = this.order.damage.isRepair; - const numberOfChips = this.order.damage.numberOfChips; - - return globalMethods - .callHttpClient({ - method: endpoints.GetSupportingItems.method, - endpoint: endpoints.GetSupportingItems.url, + lookupVinByAddress({ licenseLastName, licenseStreetAddress, licenseZip, licenseState }) { + return globalMethods.callHttpClient({ + method: endpoints.LookupVinByAddress.method, + endpoint: endpoints.LookupVinByAddress.url, payload: { - carId: carId, - damageType: isRepair ? 'Repair' : 'Replace', - parentAccountNumber: applicationConfig.CASH_PARENT_ACCOUNT_NUMBER, - parts: glassPartsArray, - numberOfRepairChips: isRepair ? numberOfChips : 0 + licenseLastName, + licenseStreetAddress, + licenseZip, + licenseState } }); - }, + }, - async getPriceOrderItems(availableLineItems) { - let zipCodeToUse = this.order.serviceLocation.zipCode; - let ctuToUse = this.order.serviceLocation.zipCodeCtu; - const availableLineItemsFormattedForRequest = - getLineItemQueryStringForPricing(availableLineItems); - const vehicle = this.order.vehicle; - - //WARNING - //TODO: this is temp test code until serviceLocation is complete. - // and ctu is available. Also, EON may need to be implemented. - zipCodeToUse = '44902'; - ctuToUse = '01820'; - const queryString = - `ParentAccountNumber=${applicationConfig.CASH_PARENT_ACCOUNT_NUMBER}` + - `&CTU=${ctuToUse}` + - `&CarId=${vehicle.carId}` + - `&Make=${vehicle.make}` + - `&Model=${vehicle.model}` + - `&Year=${vehicle.year}` + - `&EON=0` + - `&ZipCode=${zipCodeToUse}` + - `${availableLineItemsFormattedForRequest}`; - - - const response = await globalMethods - .callHttpClient({ - method: endpoints.GetPriceOrderItems.method, - endpoint: `${endpoints.GetPriceOrderItems.url}?${queryString}` - }).catch((error) => { - console.error(error); - return []; + getRouteInfo(pageName) { + return globalMethods.callHttpClient({ + method: endpoints.GetRouteInfo.method, + endpoint: endpoints.GetRouteInfo.url(applicationConfig.APPLICATION_ABBREVIATION), + payload: { + pageName + } }); + }, + getHomepageName() { + return globalMethods.callHttpClient({ + method: endpoints.GetHomepageInfo.method, + endpoint: endpoints.GetHomepageInfo.url(applicationConfig.APPLICATION_ABBREVIATION) + }); + }, - availableLineItems = addPricesToLineItems(availableLineItems, response.data.lineItems); + getPageData(pageName) { + return globalMethods.callHttpClient({ + method: endpoints.GetPageData.method, + endpoint: endpoints.GetPageData.url(applicationConfig.APPLICATION_ABBREVIATION, pageName), + payload: {} + }); + }, - return availableLineItems; - }, + // Vehicle API Actions + getVehicleYears() { + return globalMethods.callHttpClient({ + method: endpoints.GetVehicleYears.method, + endpoint: endpoints.GetVehicleYears.url, + payload: {} + }); + }, - getServiceabilityDetails({ serviceZipCode }) { - const lineItemsWithOnlyPartNumbers = this.order.lineItems.glassParts.map( - (glassPart) => ({ - partNumber: glassPart.partNumber, - }) - ); - const lineItems = buildQueryStringParameterFromArrayOfComplexObjects( - lineItemsWithOnlyPartNumbers, - "lineItems" - ); + getVehicleMakes() { + return globalMethods.callHttpClient({ + method: endpoints.GetVehicleMakes.method, + endpoint: endpoints.GetVehicleMakes.url + this.order.vehicle.year, + payload: {} + }); + }, - const vehicle = this.order.vehicle; - const carId = vehicle.carId; - const damage = this.order.damage; - const glassArray = convertGlassPieceNamingForApi(damage.glassToReplace); - - const glassPieces = buildQueryStringParameterFromArrayOfComplexObjects( - glassArray, - "glassPieces" - ); + getVehicleModels() { + return globalMethods.callHttpClient({ + method: endpoints.GetVehicleModels.method, + endpoint: `${endpoints.GetVehicleModels.url}/${this.order.vehicle.year}/${this.order.vehicle.make}`, + payload: {} + }); + }, - return globalMethods.callHttpClient({ - method: endpoints.GetServiceabilityDetails.method, - endpoint: `${endpoints.GetServiceabilityDetails.url}?zip=${serviceZipCode}&carId=${carId}&${lineItems}&${glassPieces}`, - }); - }, + getVehicleStyles() { + return globalMethods.callHttpClient({ + method: endpoints.GetVehicleStyles.method, + endpoint: `${endpoints.GetVehicleStyles.url}/${this.order.vehicle.year}/${this.order.vehicle.make}/${this.order.vehicle.model}`, + payload: {} + }); + }, - lookupVehicleByVin(vin) { - return globalMethods.callHttpClient({ - method: endpoints.LookupVehicleByVin.method, - endpoint: endpoints.LookupVehicleByVin.url, - payload: { - vin + getDamageOptions(carId) { + return globalMethods.callHttpClient({ + methods: endpoints.GetDamageOptions.method, + endpoint: `${endpoints.GetDamageOptions.url}/${carId}`, + payload: {} + }); + }, + getIsVinbyAddressPermissible() { + try { + const response = globalMethods.callHttpClient({ + method: endpoints.IsVinbyAddressPermissible.method, + endpoint: `${endpoints.IsVinbyAddressPermissible.url}?state=${this.order.customer.address.state}`, + payload: {} + }); + return response; + } catch (responseError) { + return { + error: { + status: responseError.status + } + }; + } + }, + getCoveragePolicyInfo({ accountNumber, policyNumber, dateOfLoss, zipCode }) { + // TODO: replace place holder correlationId with the real thing + const placeHolderCorrelationId = '00000000-0000-0000-0000-000000000000'; + try { + const response = globalMethods.callHttpClient({ + method: endpoints.CoveragePolicyInfo.method, + endpoint: endpoints.CoveragePolicyInfo.url, + payload: { + accountNumber, + policyNumber, + dateOfLoss, + zipCode, + correlationId: placeHolderCorrelationId + } + }); + return response; + } catch (responseError) { + return { + error: { + status: responseError.status + } + }; + } + }, + registerClaim() { + // TODO: replace place holder correlationId with the real thing + const placeHolderCorrelationId = '00000000-0000-0000-0000-000000000000'; + globalMethods.callHttpClient({ + method: endpoints.RegisterClaim.method, + endpoint: endpoints.RegisterClaim.url, + payload: + { + correlationId: placeHolderCorrelationId, + accountNumber: this.issConfig.accountNumber?.toString() ?? '', + insured: { + firstName: this.order.customer.firstName, + lastName: this.order.customer.lastName, + address: { + addressLine1: this.order.customer.address.streetAddress, + addressLine2: this.order.customer.address.streetAddress2, + city: this.order.customer.address.city, + state: this.order.customer.address.state, + zipCode: this.order.customer.address.zipCode, + country: 'US' // TODO set from store + }, + homePhone: { + number: this.order.customer.phoneNumber + } + }, + caller: { + homePhone: {} + }, + policyInfo: { + policyNumber: this.order.policy.policyNumber, + safelitePolicy: { + policies: [] + } + }, + lossInfo: { + dateOfLoss: this.order.policy.dateOfLoss, + location: { + city: this.order.policy.damageCity, + state: this.order.policy.damageState, + country: 'US' // TODO set from store + }, + vehicle: { + year: this.order.vehicle.year?.toString() ?? '', + make: this.order.vehicle.make, + model: this.order.vehicle.model, + vin: this.order.vehicle.vin + } + }, + damageDescription: this.order.policy.damageCause } - }); - }, - - setVehicle() { - return globalMethods - .callHttpClient({ - methods: endpoints.GetVehicle.method, - endpoint: `${endpoints.GetVehicle.url}/${this.order.vehicle.year}/${this.order.vehicle.make}/${this.order.vehicle.model}/${this.order.vehicle.style}`, - payload: {} - }) - .then((response) => { - this.updateVehicle(response.data); - return response; - }); - }, + }).then((response) => { + const registerClaimFailed = response.data.isError; + this.order.payment.insuranceCoverage.isVerified = !registerClaimFailed; + this.order.payment.insuranceCoverage.coverageStatus = registerClaimFailed + ? coverageStatuses.PENDING + : this.policy.noCompensation + ? coverageStatuses.NO_COMP + : coverageStatuses.VERIFIED; + }, (error) => { + this.order.payment.insuranceCoverage.isVerified = false; + this.order.payment.insuranceCoverage.coverageStatus = coverageStatuses.PENDING; + }); + }, + async lookupVinByPlate(licensePlate, licenseState) { + try { + const response = await globalMethods.callHttpClient({ + method: endpoints.LookupVinByPlate.method, + endpoint: endpoints.LookupVinByPlate.url, + payload: { + licensePlate, + licenseState + } + }); - saveVehicleDamage(isWindshieldRepair, selectedGlassToReplace, selectedWindshieldChipCount) { - const selectedGlassPassedInSorted = selectedGlassToReplace.slice().sort(); - const isGlassToReplaceTheSame = - this.order.damage.glassToReplace?.length === selectedGlassToReplace.length && - this.order.damage.glassToReplace - .slice() - .sort() - .every( - (obj, index) => - obj.glassLocation === selectedGlassPassedInSorted[index].glassLocation && - obj.glassName === selectedGlassPassedInSorted[index].glassName - ); - const isWindshieldRepairTheSame = - isWindshieldRepair === this.order.damage.isRepair; + return response; + } catch (responseError) { + return { + error: { + status: responseError.status + } + }; + } + }, - const isChipCountTheSame = - selectedWindshieldChipCount === this.order.damage.numberOfChips; - - const isDamageChanging = - !isGlassToReplaceTheSame || - !isWindshieldRepairTheSame || - (isWindshieldRepair && !isChipCountTheSame); - - if (isDamageChanging) { - //Reset dependent state when changing - //Was resetGlassPartsState, added dependencies for SSR-290 + // PartsOrQuestions API Actions + async getPartsOrQuestions() { this.resetPartsAndDependencies(); - // Save new values - this.updateIsRepair(isWindshieldRepair); - this.updateNumberOfChips(isWindshieldRepair ? parseInt(selectedWindshieldChipCount) : null); - this.updateGlassToReplace(selectedGlassToReplace); - } - }, + const { vehicle } = this; + const { damage } = this; + const { order } = this; - updateRegistration(registrationInfo) { - this.order.vehicle.registration.licensePlate = registrationInfo?.licensePlate; - this.order.vehicle.registration.address = registrationInfo?.address; - this.order.vehicle.registration.city = registrationInfo?.city; - this.order.vehicle.registration.state = registrationInfo?.state; - this.order.vehicle.registration.zipCode = registrationInfo?.zipCode; - this.order.vehicle.registration.firstName = registrationInfo?.firstName; - this.order.vehicle.registration.lastName = registrationInfo?.lastName; - }, + const { carId } = vehicle; + const glassArray = damage.glassToReplace; + const zipCode = vehicle.registration.zipCode ? vehicle.registration.zipCode : order.customer.address.zipCode; + const { vin } = vehicle; - updateServiceLocation(serviceLocationInfo) { - this.order.serviceLocation.address = serviceLocationInfo.address; - this.order.serviceLocation.city = serviceLocationInfo.city; - this.order.serviceLocation.state = serviceLocationInfo.state; - this.order.serviceLocation.zipCode = serviceLocationInfo.zipCode; - this.order.serviceLocation.zipCodeCtu = serviceLocationInfo.zipCodeCtu; - }, + // create a new array to avoid mutating state + const glassArrayForPayload = convertGlassPieceNamingForApi(glassArray); - resetRegistrationState() { - this.order.vehicle.registration.licensePlate = null; - this.order.vehicle.registration.address = null; - this.order.vehicle.registration.city = null; - this.order.vehicle.registration.state = null; - this.order.vehicle.registration.zipCode = null; - this.order.vehicle.registration.firstName = null; - this.order.vehicle.registration.lastName = null; - }, + const response = await globalMethods.callHttpClient({ + method: endpoints.GetPartsOrQuestions.method, + endpoint: endpoints.GetPartsOrQuestions.url, + payload: { + carId, + glassPieces: glassArrayForPayload, + zip: zipCode, + vin + } + }); - updateSupportingItems(partsData) { - this.order.lineItems.supportingItems = partsData; - }, + // Flatten location and name properties + response.data.partsOrQuestions = convertGlassPieceNamingFromApi(response.data.partsOrQuestions); - updateVaps(partsData) { - this.order.lineItems.vaps = partsData; - }, + return response; + }, - updateVehicle(vehicle) { + async getParts() { + const { vehicle } = this.order; + const { damage } = this.order; + const { order } = this; + + const { carId } = vehicle; + const glassArray = damage.glassToReplace; + const resultsArray = damage.partQuestionAnswers; + const zipCode = vehicle.registration.zipCode ? vehicle.registration.zipCode : order.customer.address.zipCode; + const { vin } = vehicle; + + // create a new array to avoid mutating state + const glassArrayForPayload = convertGlassPieceNamingForApi(glassArray); + const resultsArrayForPayload = convertResultsForApi(resultsArray); + + const response = await globalMethods.callHttpClient({ + method: endpoints.GetParts.method, + endpoint: endpoints.GetParts.url, + payload: { + carId, + glassPieces: glassArrayForPayload, + answerResults: resultsArrayForPayload, + zip: zipCode, + vin + } + }); + + // Flatten location and name properties + response.data.glassPieceParts = convertGlassPieceNamingFromApi(response.data.glassPieceParts); + + return response; + }, + + getCapabilityQuestions(carId, partNumber) { + return globalMethods.callHttpClient({ + method: endpoints.GetCapabilityQuestions.method, + endpoint: `${endpoints.GetCapabilityQuestions.url}/${carId}/${partNumber}` + }); + }, + + getPartFromCapabilityQuestionAnswer(glassLocation) { + const pageData = this.pageData(issPageValues.CAPABILITY_QUESTIONS); + + const part = pageData.partsOrQuestions.find((x) => + x.glassLocation === glassLocation) + .parts[0]; + const { capabilityQuestionAnswers } = this.order.damage; + const capabilityQuestionAnswersForPart = capabilityQuestionAnswers.find((x) => + x.glassLocation === glassLocation); + + return globalMethods.callHttpClient({ + method: endpoints.GetPartFromCapabilityAnswer.method, + endpoint: endpoints.GetPartFromCapabilityAnswer.url, + payload: { + part, + capabilityAnswerResults: capabilityQuestionAnswersForPart + } + }); + }, + async getWipers() { + const { carId } = this.order.vehicle; + // WARNING + // TODO: this is temp test code until serviceLocation is complete. + // const serviceZipCode = this.order.serviceLocation.zipCode; + const serviceZipCode = '44902'; + return globalMethods + .callHttpClient({ + method: endpoints.GetWipers.method, + endpoint: `${endpoints.GetWipers.url}/${carId}/${serviceZipCode}` + }) + .catch((error) => { + // The wiper service sometimes returns 500s on legitimate carId/zipCode combination - return empty array instead of breaking flow + console.error(error); + return []; + }); + }, + + async getRainDefense() { + return globalMethods + .callHttpClient({ + method: endpoints.GetRainDefense.method, + endpoint: `${endpoints.GetRainDefense.url}` + }) + .catch((error) => { + // The wiper service sometimes returns 500s on legitimate carId/zipCode combination - return empty array instead of breaking flow + console.error(error); + return []; + }); + }, + + async getSupportingItems() { + const glassPartsArray = this.order.lineItems.glassParts ?? []; + const { carId } = this.order.vehicle; + const { isRepair } = this.order.damage; + const { numberOfChips } = this.order.damage; + + return globalMethods + .callHttpClient({ + method: endpoints.GetSupportingItems.method, + endpoint: endpoints.GetSupportingItems.url, + payload: { + carId, + damageType: isRepair ? 'Repair' : 'Replace', + parentAccountNumber: applicationConfig.CASH_PARENT_ACCOUNT_NUMBER, + parts: glassPartsArray, + numberOfRepairChips: isRepair ? numberOfChips : 0 + } + }); + }, + + async getPriceOrderItems(availableLineItems) { + let zipCodeToUse = this.order.serviceLocation.zipCode; + let ctuToUse = this.order.serviceLocation.zipCodeCtu; + const availableLineItemsFormattedForRequest + = getLineItemQueryStringForPricing(availableLineItems); + const { vehicle } = this.order; + + // WARNING + // TODO: this is temp test code until serviceLocation is complete. + // and ctu is available. Also, EON may need to be implemented. + zipCodeToUse = '44902'; + ctuToUse = '01820'; + const queryString + = `ParentAccountNumber=${applicationConfig.CASH_PARENT_ACCOUNT_NUMBER}` + + `&CTU=${ctuToUse}` + + `&CarId=${vehicle.carId}` + + `&Make=${vehicle.make}` + + `&Model=${vehicle.model}` + + `&Year=${vehicle.year}` + + '&EON=0' + + `&ZipCode=${zipCodeToUse}` + + `${availableLineItemsFormattedForRequest}`; + + const response = await globalMethods + .callHttpClient({ + method: endpoints.GetPriceOrderItems.method, + endpoint: `${endpoints.GetPriceOrderItems.url}?${queryString}` + }).catch((error) => { + console.error(error); + return []; + }); + + availableLineItems = addPricesToLineItems(availableLineItems, response.data.lineItems); + + return availableLineItems; + }, + + getServiceabilityDetails({ serviceZipCode }) { + const lineItemsWithOnlyPartNumbers = this.order.lineItems.glassParts.map((glassPart) => + ({ + partNumber: glassPart.partNumber + })); + const lineItems = buildQueryStringParameterFromArrayOfComplexObjects(lineItemsWithOnlyPartNumbers, + 'lineItems'); + + const { vehicle } = this.order; + const { carId } = vehicle; + const { damage } = this.order; + const glassArray = convertGlassPieceNamingForApi(damage.glassToReplace); + + const glassPieces = buildQueryStringParameterFromArrayOfComplexObjects(glassArray, + 'glassPieces'); + + return globalMethods.callHttpClient({ + method: endpoints.GetServiceabilityDetails.method, + endpoint: `${endpoints.GetServiceabilityDetails.url}?zip=${serviceZipCode}&carId=${carId}&${lineItems}&${glassPieces}` + }); + }, + + lookupVehicleByVin(vin) { + return globalMethods.callHttpClient({ + method: endpoints.LookupVehicleByVin.method, + endpoint: endpoints.LookupVehicleByVin.url, + payload: { + vin + } + }); + }, + + setVehicle() { + return globalMethods + .callHttpClient({ + methods: endpoints.GetVehicle.method, + endpoint: `${endpoints.GetVehicle.url}/${this.order.vehicle.year}/${this.order.vehicle.make}/${this.order.vehicle.model}/${this.order.vehicle.style}`, + payload: {} + }) + .then((response) => { + this.updateVehicle(response.data); + return response; + }); + }, + + saveVehicleDamage(isWindshieldRepair, selectedGlassToReplace, selectedWindshieldChipCount) { + const selectedGlassPassedInSorted = selectedGlassToReplace.slice().sort(); + const isGlassToReplaceTheSame + = this.order.damage.glassToReplace?.length === selectedGlassToReplace.length + && this.order.damage.glassToReplace + .slice() + .sort() + .every((obj, index) => + obj.glassLocation === selectedGlassPassedInSorted[index].glassLocation + && obj.glassName === selectedGlassPassedInSorted[index].glassName); + const isWindshieldRepairTheSame + = isWindshieldRepair === this.order.damage.isRepair; + + const isChipCountTheSame + = selectedWindshieldChipCount === this.order.damage.numberOfChips; + + const isDamageChanging + = !isGlassToReplaceTheSame + || !isWindshieldRepairTheSame + || (isWindshieldRepair && !isChipCountTheSame); + + if (isDamageChanging) { + // Reset dependent state when changing + // Was resetGlassPartsState, added dependencies for SSR-290 + this.resetPartsAndDependencies(); + + // Save new values + this.updateIsRepair(isWindshieldRepair); + this.updateNumberOfChips(isWindshieldRepair ? parseInt(selectedWindshieldChipCount) : null); + this.updateGlassToReplace(selectedGlassToReplace); + } + }, + + updateRegistration(registrationInfo) { + this.order.vehicle.registration.licensePlate = registrationInfo?.licensePlate; + this.order.vehicle.registration.address = registrationInfo?.address; + this.order.vehicle.registration.city = registrationInfo?.city; + this.order.vehicle.registration.state = registrationInfo?.state; + this.order.vehicle.registration.zipCode = registrationInfo?.zipCode; + this.order.vehicle.registration.firstName = registrationInfo?.firstName; + this.order.vehicle.registration.lastName = registrationInfo?.lastName; + }, + + updateServiceLocation(serviceLocationInfo) { + this.order.serviceLocation.address = serviceLocationInfo.address; + this.order.serviceLocation.city = serviceLocationInfo.city; + this.order.serviceLocation.state = serviceLocationInfo.state; + this.order.serviceLocation.zipCode = serviceLocationInfo.zipCode; + this.order.serviceLocation.zipCodeCtu = serviceLocationInfo.zipCodeCtu; + }, + + resetRegistrationState() { + this.order.vehicle.registration.licensePlate = null; + this.order.vehicle.registration.address = null; + this.order.vehicle.registration.city = null; + this.order.vehicle.registration.state = null; + this.order.vehicle.registration.zipCode = null; + this.order.vehicle.registration.firstName = null; + this.order.vehicle.registration.lastName = null; + }, + + updateSupportingItems(partsData) { + this.order.lineItems.supportingItems = partsData; + }, + + updateVaps(partsData) { + this.order.lineItems.vaps = partsData; + }, + + updateVehicle(vehicle) { // Assuming that the method caller pass all the properties. // otherwise need to check for undefined for every property. - this.vehicle.carId = vehicle.carId; - this.order.vehicle.category = vehicle.category; - this.order.vehicle.year = vehicle.year; - this.order.vehicle.make = vehicle.make; - this.order.vehicle.model = vehicle.model; - this.order.vehicle.style = vehicle.style; - this.order.vehicle.vin = vehicle.vin; - this.order.vehicle.imageUrl = vehicle.imageUrl; - this.order.vehicle.imageVifNumber = vehicle.imageVifNumber; - this.order.vehicle.imageColor = vehicle.imageVifColor; + this.vehicle.carId = vehicle.carId; + this.order.vehicle.category = vehicle.category; + this.order.vehicle.year = vehicle.year; + this.order.vehicle.make = vehicle.make; + this.order.vehicle.model = vehicle.model; + this.order.vehicle.style = vehicle.style; + this.order.vehicle.vin = vehicle.vin; + this.order.vehicle.imageUrl = vehicle.imageUrl; + this.order.vehicle.imageVifNumber = vehicle.imageVifNumber; + this.order.vehicle.imageColor = vehicle.imageVifColor; - // These could be undefined - this.order.policy.noCompensation = vehicle.noCoverage; - this.order.policy.deductible.replace = vehicle.deductible; - this.order.policy.deductible.repair = vehicle?.repairWaived ?? false ? 0 : vehicle.deductible; + // These could be undefined + this.order.policy.noCompensation = vehicle.noCoverage; + this.order.policy.deductible.replace = vehicle.deductible; + this.order.policy.deductible.repair = vehicle?.repairWaived ?? false ? 0 : vehicle.deductible; - this.resetSupportingItemsState(); - this.resetVapsState(); - }, - - updateVehicleVin(vin) { - this.order.vehicle.vin = vin; - }, - - resetVehicleState() - { - this.order.vehicle.year = null; - this.order.vehicle.make = null; - this.order.vehicle.model = null; - this.order.vehicle.style = null; - this.order.vehicle.carId = null; - this.order.vehicle.category = null; - this.order.vehicle.vin = null; - this.order.vehicle.imageUrl = null; - this.order.vehicle.imageVifNumber = null; - this.order.vehicle.imageColor = null; - }, - - resetGlassPartsState() { - this.order.lineItems.glassParts = null; - this.order.damage.partQuestionAnswers = null; - this.order.damage.moldingQuestionAnswers = null; - this.order.damage.capabilityQuestionAnswers = null; - this.applicationUser.pageData[issPageValues.PART_QUESTIONS] = null; - this.applicationUser.pageData[issPageValues.VEHICLE_PARTS] = null; - this.applicationUser.pageData[issPageValues.MOLDING_QUESTIONS] = null; - this.applicationUser.pageData[issPageValues.CAPABILITY_QUESTIONS] = null; - }, - - resetSupportingItemsState() { - this.order.lineItems.supportingItems = null; - }, - resetVapsState() { - this.order.lineItems.vaps = null; - }, - resetDamageState() { - this.order.damage.isRepair = null; - this.order.damage.numberOfChips = null; - this.order.damage.glassToReplace = null; - }, - - resetISSConfigState() { - this.issConfig.clientName = 'Generic Insurance'; - this.issConfig.clientDisplayName = 'Generic Insurance'; - this.issConfig.accountNumber = 0; - this.issConfig.styleSheet = ''; - this.issConfig.isCoverageEnabled = false; - this.issConfig.isAuthenticated = false; - this.issConfig.enableTPAFlow = false; - this.issConfig.isClaimRegistrationRequired = false; - this.issConfig.successReturnURL = null; - this.issConfig.failureReturnURL = null; - this.issConfig.disabledFields.policyNumber = false; - this.issConfig.disabledFields.policyZipCode = false; - }, - - updateVehicleYear(year) { - if(this.order.vehicle.year !== year) - { - this.resetVehicleState(); - this.resetDamageAndDependencies(); - - this.order.vehicle.year = year; - } - }, - - updateVehicleMake(make) { - if(this.order.vehicle.make !== make) - { - const year = this.order.vehicle.year; - - this.resetVehicleState(); - this.resetDamageAndDependencies(); - - this.order.vehicle.year = year; - this.order.vehicle.make = make; - } - }, - - updateVehicleModel(model) { - if(this.order.vehicle.model !== model) - { - const year = this.order.vehicle.year; - const make = this.order.vehicle.make; - - this.resetVehicleState(); - this.resetDamageAndDependencies(); - - this.order.vehicle.year = year; - this.order.vehicle.make = make; - this.order.vehicle.model = model; - } - }, - - - updateVehicleStyle(style) { - if(this.order.vehicle.style !== style) - { - const year = this.order.vehicle.year; - const make = this.order.vehicle.make; - const model = this.order.vehicle.model; - - this.resetVehicleState(); - this.resetDamageAndDependencies(); - - this.order.vehicle.year = year; - this.order.vehicle.make = make; - this.order.vehicle.model = model; - this.order.vehicle.style = style; - } - }, - - updateIsRepair(isRepair) { - this.order.damage.isRepair = isRepair; - }, - - updateNumberOfChips(numberOfChips) { - this.order.damage.numberOfChips = numberOfChips; - }, - - updateGlassToReplace(glassToReplace) { - this.order.damage.glassToReplace = glassToReplace; - }, - - updateGlassParts(partsData) { - this.order.lineItems.glassParts = partsData; - }, - - updateMoldingQuestionAnswers(answersArray) { - this.order.damage.moldingQuestionAnswers = answersArray; - }, - - updateCapabilityQuestionAnswers(answersArray) { - this.order.damage.capabilityQuestionAnswers = answersArray; - }, - - updatePartQuestionAnswers(answersArray) { - this.order.damage.partQuestionAnswers = answersArray; - }, - - updatePageData(pageData) { - this.applicationUser.pageData[pageData.page] = pageData.data; - }, - updatePolicyData(welcomePageModel) - { - this.order.policy.policyNumber = welcomePageModel?.policyNumber; - this.order.policy.policyZipCode = welcomePageModel?.policyZipCode; - this.order.policy.dateOfLoss = welcomePageModel?.dateOfLoss; - this.order.policy.damageCause = welcomePageModel?.damageCause; - this.order.policy.damageState = welcomePageModel?.damageState; - this.order.policy.damageCity = welcomePageModel?.damageCity; - this.order.policy.isDamageGlassOnly = welcomePageModel?.isDamageGlassOnly; - this.order.customer.phoneNumber = welcomePageModel?.phoneNumber; - this.order.customer.emailAddress = welcomePageModel?.email; - }, - updatePolicyHolderDetails(customerQuestions) - { - this.order.customer.address.streetAddress = customerQuestions.addressQuestions.streetAddress; - this.order.customer.address.streetAddress2 = customerQuestions.addressQuestions.streetAddress2; - this.order.customer.address.city = customerQuestions.addressQuestions.city; - this.order.customer.address.state = customerQuestions.addressQuestions.state; - this.order.customer.address.zipCode = customerQuestions.addressQuestions.zipCode; - this.order.customer.firstName = customerQuestions.firstName; - this.order.customer.lastName = customerQuestions.lastName; - }, - savePartQuestionAnswers(partQuestionAnswersArray) { - // if part question answers have changed, reset subsequent question answers - const sortedPreviousResultsArray = sortArrayOfObjectsByPropertyValue( - this.order.damage.partQuestionAnswers, - 'result' - ); - const sortedPartQuestionAnswersArray = sortArrayOfObjectsByPropertyValue( - partQuestionAnswersArray, - 'result' - ); - const havePartQuestionAnswersChanged = - sortedPreviousResultsArray?.length !== sortedPartQuestionAnswersArray.length || - !sortedPreviousResultsArray?.every( - (x, i) => x.result === sortedPartQuestionAnswersArray[i].result - ); - - if (havePartQuestionAnswersChanged) { - this.updateGlassParts(null); - this.updateMoldingQuestionAnswers(null); - this.updateCapabilityQuestionAnswers(null); this.resetSupportingItemsState(); this.resetVapsState(); + }, - this.updatePageData({ page: issPageValues.VEHICLE_PARTS, data: null }); - this.updatePageData({ page: issPageValues.MOLDING_QUESTIONS, data: null }); - this.updatePageData({ page: issPageValues.CAPABILITY_QUESTIONS, data: null }); - } + updateVehicleVin(vin) { + this.order.vehicle.vin = vin; + }, - //Save new values - this.updatePartQuestionAnswers(partQuestionAnswersArray); + resetVehicleState() { + this.order.vehicle.year = null; + this.order.vehicle.make = null; + this.order.vehicle.model = null; + this.order.vehicle.style = null; + this.order.vehicle.carId = null; + this.order.vehicle.category = null; + this.order.vehicle.vin = null; + this.order.vehicle.imageUrl = null; + this.order.vehicle.imageVifNumber = null; + this.order.vehicle.imageColor = null; + }, - }, - saveMoldingQuestionAnswers(moldingQuestionAnswersArray) { - const sortedPreviousResultsArray = sortArrayOfObjectsByPropertyValue( - this.order.damage.moldingQuestionAnswers, - 'result' - ); - const sortedMoldingQuestionAnswersArray = sortArrayOfObjectsByPropertyValue( - moldingQuestionAnswersArray, - 'result' - ); - const haveMoldingQuestionAnswersChanged = - sortedPreviousResultsArray?.length !== sortedMoldingQuestionAnswersArray.length || - !sortedPreviousResultsArray?.every( - (x, i) => x.result === sortedMoldingQuestionAnswersArray[i].result - ); - - if (haveMoldingQuestionAnswersChanged) { - this.updateGlassParts(null); - this.updateSupportingItems(null); - this.updateCapabilityQuestionAnswers(null); - this.updatePageData({ page: issPageValues.CAPABILITY_QUESTIONS, data: null }); - } + resetGlassPartsState() { + this.order.lineItems.glassParts = null; + this.order.damage.partQuestionAnswers = null; + this.order.damage.moldingQuestionAnswers = null; + this.order.damage.capabilityQuestionAnswers = null; + this.applicationUser.pageData[issPageValues.PART_QUESTIONS] = null; + this.applicationUser.pageData[issPageValues.VEHICLE_PARTS] = null; + this.applicationUser.pageData[issPageValues.MOLDING_QUESTIONS] = null; + this.applicationUser.pageData[issPageValues.CAPABILITY_QUESTIONS] = null; + }, - // Save new values - this.updateMoldingQuestionAnswers(moldingQuestionAnswersArray); - }, - - saveCapabilityQuestionAnswers(capabilityQuestionAnswersArray) { - const sortedPreviousResultsArray = sortArrayOfObjectsByPropertyValue( - this.order.damage.capabilityQuestionAnswers, - 'result' - ); - const sortedCapabilityQuestionAnswersArray = sortArrayOfObjectsByPropertyValue( - capabilityQuestionAnswersArray, - 'result' - ); - const haveCapabilityQuestionAnswersChanged = - sortedPreviousResultsArray?.length !== sortedCapabilityQuestionAnswersArray.length || - !sortedPreviousResultsArray?.every( - (x, i) => x.result === sortedCapabilityQuestionAnswersArray[i].result - ); - - if (haveCapabilityQuestionAnswersChanged) { - this.updateGlassParts(null); - this.updateSupportingItems(null); - } + resetSupportingItemsState() { + this.order.lineItems.supportingItems = null; + }, + resetVapsState() { + this.order.lineItems.vaps = null; + }, + resetDamageState() { + this.order.damage.isRepair = null; + this.order.damage.numberOfChips = null; + this.order.damage.glassToReplace = null; + }, - // Save new values - this.updateCapabilityQuestionAnswers(capabilityQuestionAnswersArray); - }, - saveGlassParts(glassParts) { - this.order.lineItems.glassParts = glassParts; - }, - saveSupportingItems(supportingItems) { - this.order.lineItems.supportingItems = supportingItems; - }, - saveVaps(vaps) { - this.order.lineItems.vaps = vaps; - }, - saveProviderPreferenceData(data){ - this.updatePageData({ page: issPageValues.PROVIDER_PREFERENCE, data: data}); - }, - addEventToBus (event) { - this.applicationUser.eventBus.push(event); - }, - removeEventFromBus (eventData) { - const matchedEvent = this.applicationUser.eventBus.find( - ( {category, subCategory } ) => - category === eventData.category && subCategory === eventData.subCategory - ); - const itemIndex = this.applicationUser.eventBus.indexOf(matchedEvent); + resetISSConfigState() { + this.issConfig.clientName = 'Generic Insurance'; + this.issConfig.clientDisplayName = 'Generic Insurance'; + this.issConfig.accountNumber = 0; + this.issConfig.styleSheet = ''; + this.issConfig.isCoverageEnabled = false; + this.issConfig.isAuthenticated = false; + this.issConfig.enableTPAFlow = false; + this.issConfig.isClaimRegistrationRequired = false; + this.issConfig.successReturnURL = null; + this.issConfig.failureReturnURL = null; + this.issConfig.disabledFields.policyNumber = false; + this.issConfig.disabledFields.policyZipCode = false; + }, - // If the item exists, remove it. - if (itemIndex > -1) { - this.applicationUser.eventBus.splice(itemIndex, 1); - } - }, + updateVehicleYear(year) { + if (this.order.vehicle.year !== year) { + this.resetVehicleState(); + this.resetDamageAndDependencies(); - // populate initial state - populateInitialState() - { - if(!localStorage.getItem(storeId)) { - this.$state = state; - } - }, + this.order.vehicle.year = year; + } + }, - // Analytics Actions - logExperimentExposure({ userId, sessionKey, pageName, experiment }) { - return globalMethods.callHttpClient({ - method: endpoints.LogExperimentExposureIfAssigned.method, - endpoint: endpoints.LogExperimentExposureIfAssigned.url, - payload: { - experimentForLogging: { - userId: userId, - experimentUniverseId: experiment.universeId, - experimentUniverseName: experiment.universeName, - experimentTestId: experiment.testId, - experimentTestName: experiment.testName, - experimentVariationId: experiment.variationId, - experimentVariationName: experiment.variationName, - enabled: experiment.isActive, - isExposed: experiment.isExposed, - userPartitionNumber: experiment.userPartitionNumber, - assignmentId: experiment.assignmentId, - sessionKey: sessionKey, - pageName: pageName + updateVehicleMake(make) { + if (this.order.vehicle.make !== make) { + const { year } = this.order.vehicle; + + this.resetVehicleState(); + this.resetDamageAndDependencies(); + + this.order.vehicle.year = year; + this.order.vehicle.make = make; + } + }, + + updateVehicleModel(model) { + if (this.order.vehicle.model !== model) { + const { year } = this.order.vehicle; + const { make } = this.order.vehicle; + + this.resetVehicleState(); + this.resetDamageAndDependencies(); + + this.order.vehicle.year = year; + this.order.vehicle.make = make; + this.order.vehicle.model = model; + } + }, + + updateVehicleStyle(style) { + if (this.order.vehicle.style !== style) { + const { year } = this.order.vehicle; + const { make } = this.order.vehicle; + const { model } = this.order.vehicle; + + this.resetVehicleState(); + this.resetDamageAndDependencies(); + + this.order.vehicle.year = year; + this.order.vehicle.make = make; + this.order.vehicle.model = model; + this.order.vehicle.style = style; + } + }, + + updateIsRepair(isRepair) { + this.order.damage.isRepair = isRepair; + }, + + updateNumberOfChips(numberOfChips) { + this.order.damage.numberOfChips = numberOfChips; + }, + + updateGlassToReplace(glassToReplace) { + this.order.damage.glassToReplace = glassToReplace; + }, + + updateGlassParts(partsData) { + this.order.lineItems.glassParts = partsData; + }, + + updateMoldingQuestionAnswers(answersArray) { + this.order.damage.moldingQuestionAnswers = answersArray; + }, + + updateCapabilityQuestionAnswers(answersArray) { + this.order.damage.capabilityQuestionAnswers = answersArray; + }, + + updatePartQuestionAnswers(answersArray) { + this.order.damage.partQuestionAnswers = answersArray; + }, + + updatePageData(pageData) { + this.applicationUser.pageData[pageData.page] = pageData.data; + }, + updatePolicyData(welcomePageModel) { + this.order.policy.policyNumber = welcomePageModel?.policyNumber; + this.order.policy.policyZipCode = welcomePageModel?.policyZipCode; + this.order.policy.dateOfLoss = welcomePageModel?.dateOfLoss; + this.order.policy.damageCause = welcomePageModel?.damageCause; + this.order.policy.damageState = welcomePageModel?.damageState; + this.order.policy.damageCity = welcomePageModel?.damageCity; + this.order.policy.isDamageGlassOnly = welcomePageModel?.isDamageGlassOnly; + this.order.customer.phoneNumber = welcomePageModel?.phoneNumber; + this.order.customer.emailAddress = welcomePageModel?.email; + }, + updatePolicyHolderDetails(customerQuestions) { + this.order.customer.address.streetAddress = customerQuestions.addressQuestions.streetAddress; + this.order.customer.address.streetAddress2 = customerQuestions.addressQuestions.streetAddress2; + this.order.customer.address.city = customerQuestions.addressQuestions.city; + this.order.customer.address.state = customerQuestions.addressQuestions.state; + this.order.customer.address.zipCode = customerQuestions.addressQuestions.zipCode; + this.order.customer.firstName = customerQuestions.firstName; + this.order.customer.lastName = customerQuestions.lastName; + }, + savePartQuestionAnswers(partQuestionAnswersArray) { + // if part question answers have changed, reset subsequent question answers + const sortedPreviousResultsArray = sortArrayOfObjectsByPropertyValue(this.order.damage.partQuestionAnswers, + 'result'); + const sortedPartQuestionAnswersArray = sortArrayOfObjectsByPropertyValue(partQuestionAnswersArray, + 'result'); + const havePartQuestionAnswersChanged + = sortedPreviousResultsArray?.length !== sortedPartQuestionAnswersArray.length + || !sortedPreviousResultsArray?.every((x, i) => + x.result === sortedPartQuestionAnswersArray[i].result); + + if (havePartQuestionAnswersChanged) { + this.updateGlassParts(null); + this.updateMoldingQuestionAnswers(null); + this.updateCapabilityQuestionAnswers(null); + this.resetSupportingItemsState(); + this.resetVapsState(); + + this.updatePageData({ page: issPageValues.VEHICLE_PARTS, data: null }); + this.updatePageData({ page: issPageValues.MOLDING_QUESTIONS, data: null }); + this.updatePageData({ page: issPageValues.CAPABILITY_QUESTIONS, data: null }); + } + + // Save new values + this.updatePartQuestionAnswers(partQuestionAnswersArray); + }, + saveMoldingQuestionAnswers(moldingQuestionAnswersArray) { + const sortedPreviousResultsArray = sortArrayOfObjectsByPropertyValue(this.order.damage.moldingQuestionAnswers, + 'result'); + const sortedMoldingQuestionAnswersArray = sortArrayOfObjectsByPropertyValue(moldingQuestionAnswersArray, + 'result'); + const haveMoldingQuestionAnswersChanged + = sortedPreviousResultsArray?.length !== sortedMoldingQuestionAnswersArray.length + || !sortedPreviousResultsArray?.every((x, i) => + x.result === sortedMoldingQuestionAnswersArray[i].result); + + if (haveMoldingQuestionAnswersChanged) { + this.updateGlassParts(null); + this.updateSupportingItems(null); + this.updateCapabilityQuestionAnswers(null); + this.updatePageData({ page: issPageValues.CAPABILITY_QUESTIONS, data: null }); + } + + // Save new values + this.updateMoldingQuestionAnswers(moldingQuestionAnswersArray); + }, + + saveCapabilityQuestionAnswers(capabilityQuestionAnswersArray) { + const sortedPreviousResultsArray = sortArrayOfObjectsByPropertyValue(this.order.damage.capabilityQuestionAnswers, + 'result'); + const sortedCapabilityQuestionAnswersArray = sortArrayOfObjectsByPropertyValue(capabilityQuestionAnswersArray, + 'result'); + const haveCapabilityQuestionAnswersChanged + = sortedPreviousResultsArray?.length !== sortedCapabilityQuestionAnswersArray.length + || !sortedPreviousResultsArray?.every((x, i) => + x.result === sortedCapabilityQuestionAnswersArray[i].result); + + if (haveCapabilityQuestionAnswersChanged) { + this.updateGlassParts(null); + this.updateSupportingItems(null); + } + + // Save new values + this.updateCapabilityQuestionAnswers(capabilityQuestionAnswersArray); + }, + saveGlassParts(glassParts) { + this.order.lineItems.glassParts = glassParts; + }, + saveSupportingItems(supportingItems) { + this.order.lineItems.supportingItems = supportingItems; + }, + saveVaps(vaps) { + this.order.lineItems.vaps = vaps; + }, + saveProviderPreferenceData(data) { + this.updatePageData({ page: issPageValues.PROVIDER_PREFERENCE, data }); + }, + addEventToBus(event) { + this.applicationUser.eventBus.push(event); + }, + removeEventFromBus(eventData) { + const matchedEvent = this.applicationUser.eventBus.find(({ category, subCategory }) => + category === eventData.category && subCategory === eventData.subCategory); + const itemIndex = this.applicationUser.eventBus.indexOf(matchedEvent); + + // If the item exists, remove it. + if (itemIndex > -1) { + this.applicationUser.eventBus.splice(itemIndex, 1); + } + }, + + // populate initial state + populateInitialState() { + if (!localStorage.getItem(storeId)) { + this.$state = state; + } + }, + + // Analytics Actions + logExperimentExposure({ userId, sessionKey, pageName, experiment }) { + return globalMethods.callHttpClient({ + method: endpoints.LogExperimentExposureIfAssigned.method, + endpoint: endpoints.LogExperimentExposureIfAssigned.url, + payload: { + experimentForLogging: { + userId, + experimentUniverseId: experiment.universeId, + experimentUniverseName: experiment.universeName, + experimentTestId: experiment.testId, + experimentTestName: experiment.testName, + experimentVariationId: experiment.variationId, + experimentVariationName: experiment.variationName, + enabled: experiment.isActive, + isExposed: experiment.isExposed, + userPartitionNumber: experiment.userPartitionNumber, + assignmentId: experiment.assignmentId, + sessionKey, + pageName + } } - } - }); - }, - logPageView({ userId, sessionKey, pageName, sessionId, action, event, shouldUseSessionId, experimentsForUser }) { - const payload = { - userId: userId, - sessionKey: sessionKey, - sessionId: sessionId, - pageName: pageName, - applicationName: applicationConfig.APPLICATION_NAME, - action: action, - event: event, - shouldUseSessionId: shouldUseSessionId, - experimentsForUser: experimentsForUser - }; + }); + }, + logPageView({ userId, sessionKey, pageName, sessionId, action, event, shouldUseSessionId, experimentsForUser }) { + const payload = { + userId, + sessionKey, + sessionId, + pageName, + applicationName: applicationConfig.APPLICATION_NAME, + action, + event, + shouldUseSessionId, + experimentsForUser + }; - return globalMethods.callHttpClient({ - method: endpoints.LogPageView.method, - endpoint: endpoints.LogPageView.url, - payload: payload, - logApiCall: false - }).then( - (response) => { - return response; - }, + return globalMethods.callHttpClient({ + method: endpoints.LogPageView.method, + endpoint: endpoints.LogPageView.url, + payload, + logApiCall: false + }).then((response) => + response, (error) => { - console.log('Analytics Service Error: ' + error.data); - } - ); - }, - logCustomEvent({ userId, sessionKey, pageName, sessionId, category, action, label, value, shouldUseSessionId, experimentsForUser}) - { - if ( pageName == null || pageName.length === 0 ) - pageName = 'none'; + console.log(`Analytics Service Error: ${error.data}`); + }); + }, + logCustomEvent({ userId, sessionKey, pageName, sessionId, category, action, label, value, shouldUseSessionId, experimentsForUser }) { + if (pageName == null || pageName.length === 0) { pageName = 'none'; } - const payload = { - userId: userId, - sessionKey: sessionKey, - sessionId: sessionId, - pageName: pageName, - applicationName: applicationConfig.APPLICATION_NAME, - category: category, - action: action, - label: label, - value: value, - shouldUseSessionId: shouldUseSessionId, - experimentsForUser: experimentsForUser - }; - - return globalMethods.callHttpClient({ - method: endpoints.LogCustomEvent.method, - endpoint: endpoints.LogCustomEvent.url, - payload: payload, - logApiCall: false - }).then( - (response) => { - return response; - }, + const payload = { + userId, + sessionKey, + sessionId, + pageName, + applicationName: applicationConfig.APPLICATION_NAME, + category, + action, + label, + value, + shouldUseSessionId, + experimentsForUser + }; + + return globalMethods.callHttpClient({ + method: endpoints.LogCustomEvent.method, + endpoint: endpoints.LogCustomEvent.url, + payload, + logApiCall: false + }).then((response) => + response, (error) => { - console.log('Analytics Service Error: ' + error.data); - } - ); - }, - initializeSession({ userId, sessionId, userAgent, referrer }) { - const payload = { - applicationName: applicationConfig.APPLICATION_NAME, - userId: userId, - deviceId: userId, - sessionId: sessionId, - userAgent: userAgent, - operatorId: 'WEB', - userName: 'SafeliteISS', - referrer: referrer - }; + console.log(`Analytics Service Error: ${error.data}`); + }); + }, + initializeSession({ userId, sessionId, userAgent, referrer }) { + const payload = { + applicationName: applicationConfig.APPLICATION_NAME, + userId, + deviceId: userId, + sessionId, + userAgent, + operatorId: 'WEB', + userName: 'SafeliteISS', + referrer + }; - return globalMethods.callHttpClient({ - method: endpoints.InitializeSession.method, - endpoint: endpoints.InitializeSession.url, - payload: payload, - logApiCall: false - }).then( - (response) => { - return response; - }, + return globalMethods.callHttpClient({ + method: endpoints.InitializeSession.method, + endpoint: endpoints.InitializeSession.url, + payload, + logApiCall: false + }).then((response) => + response, (error) => { - console.log('Analytics Service Error: ' + error.data); - } - ); - }, + console.log(`Analytics Service Error: ${error.data}`); + }); + }, - updateLastPageVisited(lastPageVisited) { - this.applicationUser.lastPageVisited = lastPageVisited; - }, + updateLastPageVisited(lastPageVisited) { + this.applicationUser.lastPageVisited = lastPageVisited; + }, - updateExperiments(experiments) { - this.applicationUser.experiments = experiments; - }, - updateTriggeredSiteEntry(wasSiteEntryTriggered) { - this.applicationUser.triggeredSiteEntry = wasSiteEntryTriggered; - }, + updateExperiments(experiments) { + this.applicationUser.experiments = experiments; + }, + updateTriggeredSiteEntry(wasSiteEntryTriggered) { + this.applicationUser.triggeredSiteEntry = wasSiteEntryTriggered; + }, - GetExperimentsByUser(userId) { - return globalMethods.callHttpClient({ - method: endpoints.GetExperimentsByUser.method, - endpoint: `${endpoints.GetExperimentsByUser.url}/${userId}`, - payload: {} - }); - }, - - async runExperimentsForTrigger({ userId, triggerEvent, triggerValue }) { - if (triggerEvent === experimentTriggers.SITE_ENTRY) { - this.updateTriggeredSiteEntry(true); - } + updateContactInfo(contactInfo) { + this.order.contactInfo.firstName = contactInfo.firstName; + this.order.contactInfo.lastName = contactInfo.lastName; + this.order.contactInfo.emailAddress = contactInfo.emailAddress; + this.order.contactInfo.phoneNumber = contactInfo.phoneNumber; + this.order.contactInfo.textUpdates = contactInfo.textUpdates; + this.order.contactInfo.notesForTechnician = contactInfo.notesForTechnician; + }, - const payload = { - applicationName: applicationConfig.APPLICATION_NAME, - userId: userId, - triggerEvent: triggerEvent, - triggerValue: triggerValue, - experimentOrder: this.experimentOrder - }; + GetExperimentsByUser(userId) { + return globalMethods.callHttpClient({ + method: endpoints.GetExperimentsByUser.method, + endpoint: `${endpoints.GetExperimentsByUser.url}/${userId}`, + payload: {} + }); + }, - const response = await globalMethods.callHttpClient({ - method: endpoints.RunExperimentsForTrigger.method, - endpoint: endpoints.RunExperimentsForTrigger.url, - payload: payload - }); - - this.updateExperiments(response.data.experiments); - }, - - async validateZip({ zip }) { - return await globalMethods.callHttpClient({ - methods: endpoints.ValidateZip.method, - endpoint: `${endpoints.ValidateZip.url}/${zip}` - }); - }, - - async validateClientTag(clientTag) { - return await globalMethods.callHttpClient({ - methods: endpoints.ValidateClientTag.method, - endpoint: `${endpoints.ValidateClientTag.url}/${clientTag}` - }); - }, - - saveServiceLocation(serviceLocationInfo) { - this.updateServiceLocation(serviceLocationInfo); - }, - - saveRegistrationAddressLookup({ isSelectedGlassAvailableForVehicle, vehicleInfo, registrationInfo }) { - //Reset dependent state when changing - if - ( - registrationInfo?.address !== this.order.vehicle.registration?.address || - registrationInfo?.city !== this.order.vehicle.registration?.city || - registrationInfo?.state !== this.order.vehicle.registration?.state || - registrationInfo?.zipCode !== this.order.vehicle.registration?.zipCode || - registrationInfo?.firstName !== this.order.vehicle.registration?.firstName || - registrationInfo?.lastName !== this.order.vehicle.registration?.lastName - ) - { - if (!isSelectedGlassAvailableForVehicle) { - this.resetDamageState(); - this.resetGlassPartsState(); + async runExperimentsForTrigger({ userId, triggerEvent, triggerValue }) { + if (triggerEvent === experimentTriggers.SITE_ENTRY) { + this.updateTriggeredSiteEntry(true); + } + + const payload = { + applicationName: applicationConfig.APPLICATION_NAME, + userId, + triggerEvent, + triggerValue, + experimentOrder: this.experimentOrder + }; + + const response = await globalMethods.callHttpClient({ + method: endpoints.RunExperimentsForTrigger.method, + endpoint: endpoints.RunExperimentsForTrigger.url, + payload + }); + + this.updateExperiments(response.data.experiments); + }, + + async validateZip({ zip }) { + return await globalMethods.callHttpClient({ + methods: endpoints.ValidateZip.method, + endpoint: `${endpoints.ValidateZip.url}/${zip}` + }); + }, + + async validateClientTag(clientTag) { + return await globalMethods.callHttpClient({ + methods: endpoints.ValidateClientTag.method, + endpoint: `${endpoints.ValidateClientTag.url}/${clientTag}` + }); + }, + + saveServiceLocation(serviceLocationInfo) { + this.updateServiceLocation(serviceLocationInfo); + }, + + saveRegistrationAddressLookup({ isSelectedGlassAvailableForVehicle, vehicleInfo, registrationInfo }) { + // Reset dependent state when changing + if + ( + registrationInfo?.address !== this.order.vehicle.registration?.address + || registrationInfo?.city !== this.order.vehicle.registration?.city + || registrationInfo?.state !== this.order.vehicle.registration?.state + || registrationInfo?.zipCode !== this.order.vehicle.registration?.zipCode + || registrationInfo?.firstName !== this.order.vehicle.registration?.firstName + || registrationInfo?.lastName !== this.order.vehicle.registration?.lastName + ) { + if (!isSelectedGlassAvailableForVehicle) { + this.resetDamageState(); + this.resetGlassPartsState(); + } + + // Save new values + this.updateRegistration(registrationInfo); + } + + if (vehicleInfo) { + this.updateVehicle(vehicleInfo); + } + }, + saveVin({ isSelectedGlassAvailableForVehicle, vehicleInfo }) { + // Reset dependent state when changing + if (vehicleInfo.vin !== this.order.vehicle.vin) { + this.resetRegistrationAndDependencies(); + + if (!isSelectedGlassAvailableForVehicle) { + this.resetDamageState(); + this.resetGlassPartsState(); + } + + // Save new values + this.updateVehicle(vehicleInfo); + } + }, + saveRegistrationLicensePlateLookup({ isSelectedGlassAvailableForVehicle, vehicleInfo, registrationInfo }) { + // Reset dependent state when changing + if (registrationInfo?.licensePlate !== this.order.vehicle.registration?.licensePlate + || registrationInfo?.state !== this.order.vehicle.registration?.state) { + this.resetRegistrationAndDependencies(); + + if (!isSelectedGlassAvailableForVehicle) { + // Dependencies already cleared in above statement + this.resetDamageState(); + this.resetGlassPartsState(); + } + + // Save new values + this.updateVehicle(vehicleInfo); } - //Save new values this.updateRegistration(registrationInfo); - }; - - if(vehicleInfo) - { - this.updateVehicle(vehicleInfo); - } - - }, - saveVin({ isSelectedGlassAvailableForVehicle, vehicleInfo }) { - //Reset dependent state when changing - if (vehicleInfo.vin !== this.order.vehicle.vin) { - this.resetRegistrationAndDependencies(); + }, - if (!isSelectedGlassAvailableForVehicle) { + resetRegistrationAndDependencies() { + this.resetRegistrationState(); + this.resetGlassPartsState(); + this.resetSupportingItemsState(); + this.resetVapsState(); + }, + + resetDamageAndDependencies() { this.resetDamageState(); this.resetGlassPartsState(); - } - - //Save new values - this.updateVehicle(vehicleInfo); + this.resetSupportingItemsState(); + this.resetVapsState(); + }, + + resetPartsAndDependencies() { + this.resetGlassPartsState(); + this.resetSupportingItemsState(); + this.resetVapsState(); + }, + + resetPageFields() { + this.order.policy.policyNumber = null; + this.order.policy.dateOfLoss = null; + this.order.policy.damageCause = null; + this.order.policy.damageCity = null; + this.order.policy.damageState = null; + this.order.customer.phoneNumber = null; + this.order.customer.emailAddress = null; + this.order.customer.firstName = null; + this.order.customer.lastName = null; + this.order.customer.address.city = null; + this.order.customer.address.state = null; + this.order.customer.address.zipCode = null; + this.order.customer.address.streetAddress = null; + this.order.customer.address.streetAddress2 = null; + this.resetVehicleState(); + this.resetDamageState(); } - }, - saveRegistrationLicensePlateLookup({ isSelectedGlassAvailableForVehicle, vehicleInfo, registrationInfo }) { - //Reset dependent state when changing - if (registrationInfo?.licensePlate !== this.order.vehicle.registration?.licensePlate || - registrationInfo?.state !== this.order.vehicle.registration?.state) - { - this.resetRegistrationAndDependencies(); - - if (!isSelectedGlassAvailableForVehicle) { - // Dependencies already cleared in above statement - this.resetDamageState(); - this.resetGlassPartsState(); - } - - //Save new values - this.updateVehicle(vehicleInfo); - } - - this.updateRegistration(registrationInfo); - }, - - resetRegistrationAndDependencies() { - this.resetRegistrationState(); - this.resetGlassPartsState(); - this.resetSupportingItemsState(); - this.resetVapsState(); - }, - - resetDamageAndDependencies() { - this.resetDamageState(); - this.resetGlassPartsState(); - this.resetSupportingItemsState(); - this.resetVapsState(); - }, - - resetPartsAndDependencies() { - this.resetGlassPartsState(); - this.resetSupportingItemsState(); - this.resetVapsState(); - }, - - resetPageFields() - { - - this.order.policy.policyNumber = null; - this.order.policy.dateOfLoss = null; - this.order.policy.damageCause = null; - this.order.policy.damageCity = null; - this.order.policy.damageState = null; - this.order.customer.phoneNumber = null; - this.order.customer.emailAddress = null; - this.order.customer.firstName = null; - this.order.customer.lastName = null; - this.order.customer.address.city = null; - this.order.customer.address.state = null; - this.order.customer.address.zipCode = null; - this.order.customer.address.streetAddress = null; - this.order.customer.address.streetAddress2 = null; - this.resetVehicleState(); - this.resetDamageState(); - } }, persist: true }); - -// Private Functions +// Private Functions function getHasRecalibrationPart(state) { - const hasRequiresRecalibration = - getNonFalseValuesOfPropertyInArrayOfObjects( - state.order.lineItems.glassParts, - 'requiresRecalibration' - )?.length > 0; - const hasRecalibrationType = - getNonFalseValuesOfPropertyInArrayOfObjects( - state.order.lineItems.glassParts, - 'recalibrationType' - )?.length > 0; + const hasRequiresRecalibration + = getNonFalseValuesOfPropertyInArrayOfObjects(state.order.lineItems.glassParts, + 'requiresRecalibration')?.length > 0; + const hasRecalibrationType + = getNonFalseValuesOfPropertyInArrayOfObjects(state.order.lineItems.glassParts, + 'recalibrationType')?.length > 0; - if (hasRequiresRecalibration) { - if (hasRecalibrationType) { - // Has both 'requiresRecalibration' and 'recalibrationType' and 'recalibrationType' - return ( - getNonFalseValuesOfPropertyInArrayOfObjects( - state.order.lineItems.glassParts, - 'recalibrationType' - )[0].toLowerCase() !== 'unknown' - ); - } else { - // Has 'requiresRecalibration' but no 'recalibrationType' at all - return true; - } - } else { - // Does not have 'requiresRecalibration' - return false; - } + if (hasRequiresRecalibration) { + if (hasRecalibrationType) { + // Has both 'requiresRecalibration' and 'recalibrationType' and 'recalibrationType' + return ( + getNonFalseValuesOfPropertyInArrayOfObjects(state.order.lineItems.glassParts, + 'recalibrationType')[0].toLowerCase() !== 'unknown' + ); + } + // Has 'requiresRecalibration' but no 'recalibrationType' at all + return true; + } + // Does not have 'requiresRecalibration' + return false; } function getNonFalseValuesOfPropertyInArrayOfObjects(array, propertyName) { - return (array ?? []).map((x) => x[propertyName]).filter((x) => x); + return (array ?? []).map((x) => + x[propertyName]).filter((x) => + x); } - function sortArrayOfObjectsByPropertyValue(arrayOfObjects, propertyName) { - if (!arrayOfObjects) return null; + if (!arrayOfObjects) return null; - return arrayOfObjects.sort((a, b) => { - if (a[propertyName] < b[propertyName]) return -1; - else if (a[propertyName] > b[propertyName]) return 1; - else return 0; - }); + return arrayOfObjects.sort((a, b) => { + if (a[propertyName] < b[propertyName]) return -1; + if (a[propertyName] > b[propertyName]) return 1; + return 0; + }); } function convertGlassPieceNamingForApi(glassArray) { - if (!glassArray || glassArray.length === 0) return []; + if (!glassArray || glassArray.length === 0) return []; - // check if array already converted. (likely when a session has been saved previously and then reloaded) - if (glassArray[0].location !== undefined) { - return glassArray; - } + // check if array already converted. (likely when a session has been saved previously and then reloaded) + if (glassArray[0].location !== undefined) { + return glassArray; + } - const converted = []; - glassArray.forEach((glass) => { - converted.push({ - location: glass.glassLocation, - name: glass.glassName - }); - }); - return converted; + const converted = []; + glassArray.forEach((glass) => { + converted.push({ + location: glass.glassLocation, + name: glass.glassName + }); + }); + return converted; } function convertResultsForApi(resultsArray) { - if (!resultsArray) return []; - const converted = []; - resultsArray.forEach((answer) => { - converted.push({ - location: answer.glassLocation, - name: answer.glassName, - result: answer.result - }); - }); - return converted; + if (!resultsArray) return []; + const converted = []; + resultsArray.forEach((answer) => { + converted.push({ + location: answer.glassLocation, + name: answer.glassName, + result: answer.result + }); + }); + return converted; } function convertGlassPieceNamingFromApi(glassArray) { - glassArray.forEach((glass) => { - glass.glassLocation = glass.glassPiece.location; - glass.glassName = glass.glassPiece.name; - delete glass.glassPiece; - return glass; - }); - return glassArray; + glassArray.forEach((glass) => { + glass.glassLocation = glass.glassPiece.location; + glass.glassName = glass.glassPiece.name; + delete glass.glassPiece; + return glass; + }); + return glassArray; } function addPricesToLineItems(lineItems, pricingLineItems) { - lineItems.forEach((lineItem) => { - if (lineItem.childParts) { - addPricesToLineItems(lineItem.childParts, pricingLineItems); - } + lineItems.forEach((lineItem) => { + if (lineItem.childParts) { + addPricesToLineItems(lineItem.childParts, pricingLineItems); + } - const lineItemIndex = pricingLineItems.findIndex( - (pricingLineItem) => pricingLineItem.partNumber === lineItem.partNumber - ); + const lineItemIndex = pricingLineItems.findIndex((pricingLineItem) => + pricingLineItem.partNumber === lineItem.partNumber); - if(lineItemIndex > -1) - { - const pricedLineItem = pricingLineItems[lineItemIndex]; - lineItem.laborAmount = pricedLineItem.laborAmount; - lineItem.sellingPrice = pricedLineItem.sellingPrice; - lineItem.kitPrice = pricedLineItem.kitPrice; - } - }); - return lineItems; + if (lineItemIndex > -1) { + const pricedLineItem = pricingLineItems[lineItemIndex]; + lineItem.laborAmount = pricedLineItem.laborAmount; + lineItem.sellingPrice = pricedLineItem.sellingPrice; + lineItem.kitPrice = pricedLineItem.kitPrice; + } + }); + return lineItems; } -function getLineItemQueryStringForPricing(lineItems){ - return lineItems.map((lineItem, index) => { - let queryStringSnippet = `&LineItems[${index}].partNumber=${lineItem.partNumber}`; - if (lineItem.childParts) { - queryStringSnippet += getLineItemQueryStringForPricing(lineItem.childParts); - } - - return queryStringSnippet; - }).join(''); +function getLineItemQueryStringForPricing(lineItems) { + return lineItems.map((lineItem, index) => { + let queryStringSnippet = `&LineItems[${index}].partNumber=${lineItem.partNumber}`; + if (lineItem.childParts) { + queryStringSnippet += getLineItemQueryStringForPricing(lineItem.childParts); + } + + return queryStringSnippet; + }).join(''); } function buildQueryStringParameterFromArrayOfComplexObjects(arrayOfObjects, parameterName) { - let queryStringParameter = ""; - for (let i = 0; i < arrayOfObjects.length; i++) { - for (const [key, value] of Object.entries(arrayOfObjects[i])) { - queryStringParameter += `${parameterName}[${i}].${key}=${value}&`; - } - } - // Remove trailing & - return queryStringParameter.slice(0, -1); + let queryStringParameter = ''; + for (let i = 0; i < arrayOfObjects.length; i++) { + for (const [key, value] of Object.entries(arrayOfObjects[i])) { + queryStringParameter += `${parameterName}[${i}].${key}=${value}&`; + } + } + // Remove trailing & + return queryStringParameter.slice(0, -1); } - diff --git a/src/ux-components/checkbox/checkbox.vue b/src/ux-components/checkbox/checkbox.vue index 7427730f..cb49358a 100644 --- a/src/ux-components/checkbox/checkbox.vue +++ b/src/ux-components/checkbox/checkbox.vue @@ -9,9 +9,9 @@ :id="buttonID" :tabindex="tabIndex" :aria-required="isRequired" + :checked="isChecked" @change="handleCheckChange" - :ref="checkboxName" - /> + :ref="checkboxName" />

{{ screenReaderOnlyText }} @@ -30,6 +30,10 @@ export default { screenReaderOnlyText: String, isRequired: Boolean, hasError: Boolean, + isChecked: { + type: Boolean, + default: false + } }, methods: { handleCheckChange() { From 09adcc11148cc935400f4877e76ad4997b845f81 Mon Sep 17 00:00:00 2001 From: brydon1 Date: Wed, 12 Jul 2023 11:11:52 -0400 Subject: [PATCH 12/21] Adding tests --- .../textarea-question/textarea-question.vue | 4 +- .../contact-details/contact-details.spec.js | 87 ++++++++++++++++++- .../contact-details/contact-details.vue | 16 ++-- src/store/index.js | 6 +- src/store/store.spec.js | 7 +- src/ux-components/text-link/text-link.vue | 12 +-- 6 files changed, 108 insertions(+), 24 deletions(-) diff --git a/src/digital-components/textarea-question/textarea-question.vue b/src/digital-components/textarea-question/textarea-question.vue index 9336a1c0..a9f46273 100644 --- a/src/digital-components/textarea-question/textarea-question.vue +++ b/src/digital-components/textarea-question/textarea-question.vue @@ -30,7 +30,7 @@

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

{ describe('Rendering', () => { @@ -153,7 +154,7 @@ describe('contactDetails.vue', () => { // Assert expect(footer.exists()).toBe(true); }); - test('Mocked store yields expected data', () => { + test('Mocked store with no contact info yields expected data', () => { // Arrange const mountOptions = getMountOptions(); @@ -184,9 +185,50 @@ describe('contactDetails.vue', () => { // Assert expect(wrapper.vm.firstName).toBe(firstName); expect(wrapper.vm.lastName).toBe(lastName); - expect(wrapper.vm.email).toBe(emailAddress); + expect(wrapper.vm.emailAddress).toBe(emailAddress); expect(wrapper.vm.phoneNumber).toBe(phoneNumber); }); + 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), + phoneNumber: getRandomInt(1000000000, 9999999999), + requestTextUpdates: getRandomBoolean(), + notesForTechnician: getRandomString(50, 100) + }; + const mainInitialState = { + order: { + customer, + contactInfo + } + }; + + const wrapper = shallowMount(contactDetails, getMountOptions({ + global: { + plugins: [createTestingPinia({ + initialState: { + main: mainInitialState + } + })] + } + })); + + // 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.phoneNumber); + expect(wrapper.vm.requestTextUpdates).toBe(contactInfo.requestTextUpdates); + expect(wrapper.vm.notesForTechnician).toBe(contactInfo.notesForTechnician); + }); }); describe('Navigation', () => { @@ -222,5 +264,44 @@ describe('contactDetails.vue', () => { expect(wrapper.vm.$router.navigate) .toHaveBeenCalledWith(navigationScenarios.CLICKED_FORWARD, undefined); }); + test('Forward button click updates contact info', () => { + // Arrange + const mountOptions = getMountOptions({ + router: { + navigate: jest.fn() + }, + global: { + plugins: [createTestingPinia()] + } + }); + 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); + const requestTextUpdates = getRandomBoolean(); + const notesForTechnician = getRandomString(1, 100); + wrapper.setData({ + firstName, + lastName, + emailAddress, + phoneNumber, + requestTextUpdates, + notesForTechnician + }); + + // Act + wrapper.vm.forwardButtonAction(); + + // Assert + expect(useMainStore().updateContactInfo).toHaveBeenCalledWith({ + firstName, + lastName, + emailAddress, + phoneNumber, + requestTextUpdates, + notesForTechnician + }); + }); }); }); diff --git a/src/layouts/contact-details/contact-details.vue b/src/layouts/contact-details/contact-details.vue index 96fc0c4d..1aa6c18f 100644 --- a/src/layouts/contact-details/contact-details.vue +++ b/src/layouts/contact-details/contact-details.vue @@ -65,7 +65,7 @@ :isRequired="false" :cmsWidgetName="widget.notesQuestion" maxLength="250" - :inputRows="3" /> + :inputRows="4" /> lastName: null, emailAddress: null, phoneNumber: null, - textUpdates: false, + requestTextUpdates: false, notesForTechnician: '' } }, @@ -201,7 +201,7 @@ export const useMainStore = defineStore({ lastName: s.order.contactInfo.lastName ?? s.order.customer.lastName, emailAddress: s.order.contactInfo.emailAddress ?? s.order.customer.emailAddress, phoneNumber: s.order.contactInfo.phoneNumber ?? s.order.customer.phoneNumber, - textUpdates: s.order.contactInfo.textUpdates ?? false, + requestTextUpdates: s.order.contactInfo.requestTextUpdates ?? false, notesForTechnician: s.order.contactInfo.notesForTechnician }), experimentOrder: (state) => @@ -1142,7 +1142,7 @@ export const useMainStore = defineStore({ this.order.contactInfo.lastName = contactInfo.lastName; this.order.contactInfo.emailAddress = contactInfo.emailAddress; this.order.contactInfo.phoneNumber = contactInfo.phoneNumber; - this.order.contactInfo.textUpdates = contactInfo.textUpdates; + this.order.contactInfo.requestTextUpdates = contactInfo.requestTextUpdates; this.order.contactInfo.notesForTechnician = contactInfo.notesForTechnician; }, diff --git a/src/store/store.spec.js b/src/store/store.spec.js index 03b06257..36d73944 100644 --- a/src/store/store.spec.js +++ b/src/store/store.spec.js @@ -422,5 +422,8 @@ describe("Store", () => { expect(store.payment.insuranceCoverage.isVerified).toBe(false); expect(store.payment.insuranceCoverage.coverageStatus).toBe(coverageStatuses.PENDING); }); - }) -}); \ No newline at end of file + }); + + // TODO add tests + describe('updateContactInfo method', () => {}); +}); diff --git a/src/ux-components/text-link/text-link.vue b/src/ux-components/text-link/text-link.vue index 49d08ffb..9cb5d461 100644 --- a/src/ux-components/text-link/text-link.vue +++ b/src/ux-components/text-link/text-link.vue @@ -1,19 +1,15 @@ From 03af693b148bf79d0c8d5ebe310e33c35e1d6bf8 Mon Sep 17 00:00:00 2001 From: Bill Richardson Date: Wed, 12 Jul 2023 11:29:44 -0400 Subject: [PATCH 13/21] pasting issue fix. --- .../textbox-question/textbox-question.vue | 605 +++++++++--------- 1 file changed, 310 insertions(+), 295 deletions(-) diff --git a/src/digital-components/textbox-question/textbox-question.vue b/src/digital-components/textbox-question/textbox-question.vue index 2a71b795..f7e0d858 100644 --- a/src/digital-components/textbox-question/textbox-question.vue +++ b/src/digital-components/textbox-question/textbox-question.vue @@ -1,325 +1,340 @@ From 0ef1d5046d6cbd920a77e6dd188954c0095303bb Mon Sep 17 00:00:00 2001 From: brydon1 Date: Wed, 12 Jul 2023 12:38:14 -0400 Subject: [PATCH 14/21] Adding store tests --- .../contact-details/contact-details.vue | 2 +- src/store/index.js | 12 +++--- src/store/store.spec.js | 41 ++++++++++++++++++- src/ux-components/checkbox/checkbox.spec.js | 8 ++++ 4 files changed, 54 insertions(+), 9 deletions(-) diff --git a/src/layouts/contact-details/contact-details.vue b/src/layouts/contact-details/contact-details.vue index 1aa6c18f..da19beff 100644 --- a/src/layouts/contact-details/contact-details.vue +++ b/src/layouts/contact-details/contact-details.vue @@ -83,7 +83,7 @@ class="disclaimer-link" linkType="text" text="Terms of Use" - href="//www.safelite.com/terms-of-use" />{{ "." }} + href="//www.safelite.com/terms-of-use" />.

{ }); }); - // TODO add tests - describe('updateContactInfo method', () => {}); + describe('updateContactInfo method', () => { + it('UpdateContactInfo updates contact info in store', () => { + // Arrange + const firstName = getRandomString(4, 10); + const lastName = getRandomString(5, 15); + const emailAddress = false; + const phoneNumber = getRandomInt(1000000000, 9999999999); + const requestTextUpdates = getRandomBoolean(); + const notesForTechnician = getRandomString(50, 150); + + // Act + store.updateContactInfo({ firstName, + lastName, + emailAddress, + phoneNumber, + requestTextUpdates, + notesForTechnician }); + + // Assert + expect(store.contactInfo.firstName).toEqual(firstName); + expect(store.contactInfo.lastName).toEqual(lastName); + expect(store.contactInfo.emailAddress).toEqual(emailAddress); + expect(store.contactInfo.phoneNumber).toEqual(phoneNumber); + expect(store.contactInfo.requestTextUpdates).toEqual(requestTextUpdates); + expect(store.contactInfo.notesForTechnician).toEqual(notesForTechnician); + }); + it('All null values => contact info set in store to all nulls', () => { + // Act + store.updateContactInfo({}); + + // Assert + expect(store.contactInfo.firstName).toEqual(''); + expect(store.contactInfo.lastName).toEqual(''); + expect(store.contactInfo.emailAddress).toEqual(''); + expect(store.contactInfo.phoneNumber).toEqual(''); + expect(store.contactInfo.requestTextUpdates).toEqual(false); + expect(store.contactInfo.notesForTechnician).toEqual(''); + }); + }); }); diff --git a/src/ux-components/checkbox/checkbox.spec.js b/src/ux-components/checkbox/checkbox.spec.js index e231ecc7..7cdd9c6e 100644 --- a/src/ux-components/checkbox/checkbox.spec.js +++ b/src/ux-components/checkbox/checkbox.spec.js @@ -75,4 +75,12 @@ describe("checkbox.vue", () => { expect(paragraph.text()).toEqual("screenreader text"); }); + + it('Default isChecked is false', async () => { + // Act + const wrapper = shallowMount(checkbox, {}); + + // Assert + expect(wrapper.vm.isChecked).toEqual(false); + }); }); From b0075d6fd09d5ff886a993f2097822615c9aee89 Mon Sep 17 00:00:00 2001 From: brydon1 Date: Wed, 12 Jul 2023 14:08:57 -0400 Subject: [PATCH 15/21] Fixing test --- .../textarea-question.spec.js | 2 +- .../contact-details/contact-details.spec.js | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/digital-components/textarea-question/textarea-question.spec.js b/src/digital-components/textarea-question/textarea-question.spec.js index 8e4d93c6..e56412ae 100644 --- a/src/digital-components/textarea-question/textarea-question.spec.js +++ b/src/digital-components/textarea-question/textarea-question.spec.js @@ -69,7 +69,7 @@ describe('textarea-question.vue', () => { }, mixins: [mockMixin] }); - const expected = `0/${maxLength}`; + const expected = `${maxLength}/${maxLength}`; // Act // Assert diff --git a/src/layouts/contact-details/contact-details.spec.js b/src/layouts/contact-details/contact-details.spec.js index 6ca512f8..211ba840 100644 --- a/src/layouts/contact-details/contact-details.spec.js +++ b/src/layouts/contact-details/contact-details.spec.js @@ -210,16 +210,16 @@ describe('contactDetails.vue', () => { contactInfo } }; + const mountOptions = getMountOptions(); + mountOptions.global = { + plugins: [createTestingPinia({ + initialState: { + main: mainInitialState + } + })] + }; - const wrapper = shallowMount(contactDetails, getMountOptions({ - global: { - plugins: [createTestingPinia({ - initialState: { - main: mainInitialState - } - })] - } - })); + const wrapper = shallowMount(contactDetails, mountOptions); // Assert expect(wrapper.vm.firstName).toBe(contactInfo.firstName); From 63596a71a6578d51550bf0cd9c38903893010c4c Mon Sep 17 00:00:00 2001 From: brydon1 Date: Wed, 12 Jul 2023 14:16:26 -0400 Subject: [PATCH 16/21] Updating linting to always exclude .vue --- .eslintrc.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.eslintrc.js b/.eslintrc.js index 706c027e..0d53a39d 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -32,7 +32,8 @@ module.exports = { }, svg: 'always', math: 'always' - }] + }], + 'import/extensions': ['error', 'always', { vue: 'never' }] }, settings: { 'import/resolver': { From 22cf8e484c108368329949d7124f45ad41dea0e4 Mon Sep 17 00:00:00 2001 From: brydon1 Date: Wed, 12 Jul 2023 14:27:38 -0400 Subject: [PATCH 17/21] Adding .vue exclusion for eslint --- .eslintrc.js | 3 ++- .../policy-vehicles/policy-vehicles.vue | 22 +++++++++---------- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index 706c027e..0d53a39d 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -32,7 +32,8 @@ module.exports = { }, svg: 'always', math: 'always' - }] + }], + 'import/extensions': ['error', 'always', { vue: 'never' }] }, settings: { 'import/resolver': { diff --git a/src/layouts/policy-vehicles/policy-vehicles.vue b/src/layouts/policy-vehicles/policy-vehicles.vue index f629c888..2e59d880 100644 --- a/src/layouts/policy-vehicles/policy-vehicles.vue +++ b/src/layouts/policy-vehicles/policy-vehicles.vue @@ -41,19 +41,19 @@