diff --git a/jest.config.js b/jest.config.js index c33a3d6e..a0daf1b2 100644 --- a/jest.config.js +++ b/jest.config.js @@ -1,3 +1,24 @@ module.exports = { - preset: '@vue/cli-plugin-unit-jest/presets/no-babel' -} + verbose: true, + coverageReporters: ["html", "text", "jest-junit", "cobertura"], + testResultsProcessor: "jest-junit", + preset: "@vue/cli-plugin-unit-jest", + transform: { "^.+\\.vue$": "vue-jest" }, + moduleFileExtensions: ["js", "vue"], + modulePathIgnorePatterns: ["vin-lookup"], + collectCoverageFrom: [ + "src/**/*.{js,vue}", + "!src/main.js", + "!src/constants/*.js", + "!src/router/**/*.js", + "!src/helpers/unit-test-helper.js", + "!src/layouts/vehicle-damage/windshield-options/windshield-options.vue" + // END + ], // ! means exclude from coverage. + testMatch: ["**/*.spec.(js|jsx|ts|tsx)|**/__tests__/*.(js|jsx|ts|tsx)"], + coverageThreshold: { + // global: { + // statements: 80, + // }, + }, +}; diff --git a/package-lock.json b/package-lock.json index 0cb1a890..659ddc3a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -990,6 +990,11 @@ "integrity": "sha512-a5Sab1C4/icpTZVzZc5Ghpz88yQtGOyNqYXcZgOssB2uuAr+wF/MvN6bgtW32q7HHrvBki+BsZ0OuNv6EV3K9g==", "dev": true }, + "@popperjs/core": { + "version": "2.11.6", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.6.tgz", + "integrity": "sha512-50/17A98tWUfQ176raKiOGXuYpLyyVMkxxG6oylzL3BPOlA6ADGdK7EYunSa4I064xerltq9TGXs8HmOk5E+vw==" + }, "@sideway/address": { "version": "4.1.4", "resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.4.tgz", @@ -1532,29 +1537,6 @@ "webpack-merge": "^5.7.3", "webpack-virtual-modules": "^0.4.2", "whatwg-fetch": "^3.6.2" - }, - "dependencies": { - "@vue/vue-loader-v15": { - "version": "npm:vue-loader@15.10.0", - "resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-15.10.0.tgz", - "integrity": "sha512-VU6tuO8eKajrFeBzMssFUP9SvakEeeSi1BxdTH5o3+1yUyrldp8IERkSdXlMI2t4kxF2sqYUDsQY+WJBxzBmZg==", - "dev": true, - "requires": { - "@vue/component-compiler-utils": "^3.1.0", - "hash-sum": "^1.0.2", - "loader-utils": "^1.1.0", - "vue-hot-reload-api": "^2.3.0", - "vue-style-loader": "^4.1.0" - }, - "dependencies": { - "hash-sum": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/hash-sum/-/hash-sum-1.0.2.tgz", - "integrity": "sha512-fUs4B4L+mlt8/XAtSOGMUO1TXmAelItBPtJG7CyHJfYTdDjwisntGO2JQz7oUsatOY9o68+57eziUVNw/mRHmA==", - "dev": true - } - } - } } }, "@vue/cli-shared-utils": { @@ -1804,6 +1786,27 @@ "integrity": "sha512-E2P4oXSaWDqTZNbmKZFVLrNN/siVN78YkEqs7pHryWerrlZR9bBFLWdJwRoguX45Ru6HxIflzKl4vQvwRMwm5g==", "dev": true }, + "@vue/vue-loader-v15": { + "version": "npm:vue-loader@15.10.0", + "resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-15.10.0.tgz", + "integrity": "sha512-VU6tuO8eKajrFeBzMssFUP9SvakEeeSi1BxdTH5o3+1yUyrldp8IERkSdXlMI2t4kxF2sqYUDsQY+WJBxzBmZg==", + "dev": true, + "requires": { + "@vue/component-compiler-utils": "^3.1.0", + "hash-sum": "^1.0.2", + "loader-utils": "^1.1.0", + "vue-hot-reload-api": "^2.3.0", + "vue-style-loader": "^4.1.0" + }, + "dependencies": { + "hash-sum": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/hash-sum/-/hash-sum-1.0.2.tgz", + "integrity": "sha512-fUs4B4L+mlt8/XAtSOGMUO1TXmAelItBPtJG7CyHJfYTdDjwisntGO2JQz7oUsatOY9o68+57eziUVNw/mRHmA==", + "dev": true + } + } + }, "@vue/vue3-jest": { "version": "27.0.0", "resolved": "https://registry.npmjs.org/@vue/vue3-jest/-/vue3-jest-27.0.0.tgz", @@ -2499,6 +2502,11 @@ "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", "dev": true }, + "bootstrap": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.2.1.tgz", + "integrity": "sha512-UQi3v2NpVPEi1n35dmRRzBJFlgvWHYwyem6yHhuT6afYF+sziEt46McRbT//kVXZ7b1YUYEVGdXEH74Nx3xzGA==" + }, "brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -3310,7 +3318,8 @@ "deepmerge": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", - "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==" + "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==", + "dev": true }, "default-gateway": { "version": "6.0.3", @@ -7702,6 +7711,20 @@ "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true }, + "pinia": { + "version": "2.0.22", + "resolved": "https://registry.npmjs.org/pinia/-/pinia-2.0.22.tgz", + "integrity": "sha512-u+b8/BC+tmvo3ACbYO2w5NfxHWFOjvvw9DQnyT0dW8aUMCPRQT5QnfZ5R5W2MzZBMTeZRMQI7V/QFbafmM9QHw==", + "requires": { + "@vue/devtools-api": "^6.2.1", + "vue-demi": "*" + } + }, + "pinia-plugin-persistedstate": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/pinia-plugin-persistedstate/-/pinia-plugin-persistedstate-2.2.0.tgz", + "integrity": "sha512-j3CknBg4H17MXDwG+X6JT50wmGkxG5cWshVb5pdV8o/ivMxiVsML9ADwe7SEchas1myIWd6/sYeVv0/IGT5YrQ==" + }, "pirates": { "version": "4.0.5", "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz", @@ -8683,11 +8706,6 @@ "integrity": "sha512-Vpfqwm4EnqGdlsBFNmHhxhElJYrdfcxPThu+ryKS5J8L/fhAwLazFZtq+S+TWZ9ANj2piSQLGj6NQg+lKPmxrw==", "dev": true }, - "shvl": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/shvl/-/shvl-2.0.3.tgz", - "integrity": "sha512-V7C6S9Hlol6SzOJPnQ7qzOVEWUQImt3BNmmzh40wObhla3XOYMe4gGiYzLrJd5TFa+cI2f9LKIRJTTKZSTbWgw==" - }, "side-channel": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", @@ -9393,6 +9411,11 @@ "@vue/shared": "3.2.38" } }, + "vue-demi": { + "version": "0.13.11", + "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.13.11.tgz", + "integrity": "sha512-IR8HoEEGM65YY3ZJYAjMlKygDQn25D5ajNFNoKh9RSDMQtlzCxtfQjdQgv9jjK+m3377SsJXY8ysq8kLCZL25A==" + }, "vue-hot-reload-api": { "version": "2.3.4", "resolved": "https://registry.npmjs.org/vue-hot-reload-api/-/vue-hot-reload-api-2.3.4.tgz", @@ -9504,23 +9527,6 @@ "integrity": "sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw==", "dev": true }, - "vuex": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/vuex/-/vuex-4.0.2.tgz", - "integrity": "sha512-M6r8uxELjZIK8kTKDGgZTYX/ahzblnzC4isU1tpmEuOIIKmV+TRdc+H4s8ds2NuZ7wpUTdGRzJRtoj+lI+pc0Q==", - "requires": { - "@vue/devtools-api": "^6.0.0-beta.11" - } - }, - "vuex-persistedstate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/vuex-persistedstate/-/vuex-persistedstate-4.1.0.tgz", - "integrity": "sha512-3SkEj4NqwM69ikJdFVw6gObeB0NHyspRYMYkR/EbhR0hbvAKyR5gksVhtAfY1UYuWUOCCA0QNGwv9pOwdj+XUQ==", - "requires": { - "deepmerge": "^4.2.2", - "shvl": "^2.0.3" - } - }, "w3c-hr-time": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz", diff --git a/package.json b/package.json index f8c415c0..72c6e699 100644 --- a/package.json +++ b/package.json @@ -5,19 +5,21 @@ "scripts": { "serve": "vue-cli-service serve", "build": "vue-cli-service build", - "test:unit": "vue-cli-service test:unit" + "test:unit": "vue-cli-service test:unit --coverage --ci", + "test:unit:lite": "vue-cli-service test:unit --ci" }, "dependencies": { + "@popperjs/core": "^2.10.2", "axios": "^0.27.2", + "bootstrap": "^5.1.1", + "pinia": "^2.0.22", + "pinia-plugin-persistedstate": "^2.2.0", "vue": "^3.2.13", - "vue-router": "^4.0.3", - "vuex": "^4.0.0", - "vuex-persistedstate": "^4.1.0" + "vue-router": "^4.0.3" }, "devDependencies": { "@vue/cli-plugin-router": "~5.0.0", "@vue/cli-plugin-unit-jest": "~5.0.0", - "@vue/cli-plugin-vuex": "~5.0.0", "@vue/cli-service": "~5.0.0", "@vue/test-utils": "^2.0.0-0", "@vue/vue3-jest": "^27.0.0-alpha.1", diff --git a/src/App.vue b/src/App.vue index ec98c47b..d6c5927b 100644 --- a/src/App.vue +++ b/src/App.vue @@ -1,30 +1,13 @@ + @import "./node_modules/bootstrap/scss/bootstrap"; + @import "@/styles/common-animations.scss"; + \ No newline at end of file diff --git a/src/constants/dynamic-strings.js b/src/constants/dynamic-strings.js new file mode 100644 index 00000000..c526e45a --- /dev/null +++ b/src/constants/dynamic-strings.js @@ -0,0 +1,7 @@ +const dynamicStrings = { + GLOBAL_STATE: "globalState", + CUSTOM: "custom", + ROUTER_LINK: "routerLink:" + }; + + export { dynamicStrings }; \ No newline at end of file diff --git a/src/constants/query-strings.js b/src/constants/query-strings.js new file mode 100644 index 00000000..c1cfb957 --- /dev/null +++ b/src/constants/query-strings.js @@ -0,0 +1,5 @@ +const queryStrings = { + ISS_PAGE: 'issPage' + }; + + export { queryStrings }; \ No newline at end of file diff --git a/src/constants/store-actions.js b/src/constants/store-actions.js index d6ce74f3..5f310f43 100644 --- a/src/constants/store-actions.js +++ b/src/constants/store-actions.js @@ -25,9 +25,21 @@ const storeActions = { GET_PART_FROM_CAPABILITY_QUESTION_ANSWER: "getPartFromCapabilityQuestionAnswer", GET_MOLDING_QUESTIONS: "getMoldingQuestions", VALIDATE_ZIP: "validateZip", - GET_EXPERIMENTS_BY_USER: "GetExperimentsByUser", - RUN_EXPERIMENTS_FOR_TRIGGER: "runExperimentsForTrigger", CLEAR_VIN: "clearVin", + + // VEHICLE MUTATIONS + UPDATE_YEAR: "updateYear", + UPDATE_MAKE: "updateMake", + UPDATE_MODEL: "updateModel", + UPDATE_STYLE: "updateStyle", + UPDATE_CAR_ID: "updateCarId", + UPDATE_VEHICLE_CATEGORY: "updateVehicleCategory", + UPDATE_VEHICLE_IMAGE_URL: "updateVehicleImageUrl", + UPDATE_VEHICLE_IMAGE_VIF_NUMBER: "updateVehicleImageVifNumber", + UPDATE_VEHICLE_IMAGE_COLOR: "updateVehicleImageColor", + UPDATE_VEHICLE_VIN: "updateVehicleVin", + UPDATE_VEHICLE: "updateVehicle", + // DEPENDENCY MUTATIONS diff --git a/src/constants/store-mutations.js b/src/constants/store-mutations.js deleted file mode 100644 index 059cf88d..00000000 --- a/src/constants/store-mutations.js +++ /dev/null @@ -1,19 +0,0 @@ -const storeMutations = { - - // VEHICLE MUTATIONS - UPDATE_YEAR: "updateYear", - UPDATE_MAKE: "updateMake", - UPDATE_MODEL: "updateModel", - UPDATE_STYLE: "updateStyle", - UPDATE_CAR_ID: "updateCarId", - UPDATE_VEHICLE_CATEGORY: "updateVehicleCategory", - UPDATE_VEHICLE_IMAGE_URL: "updateVehicleImageUrl", - UPDATE_VEHICLE_IMAGE_VIF_NUMBER: "updateVehicleImageVifNumber", - UPDATE_VEHICLE_IMAGE_COLOR: "updateVehicleImageColor", - UPDATE_VEHICLE_VIN: "updateVehicleVin", - UPDATE_VEHICLE: "updateVehicle", - - - }; - - export { storeMutations }; \ No newline at end of file diff --git a/src/main.js b/src/main.js index ae9d0aa3..49eb55a4 100644 --- a/src/main.js +++ b/src/main.js @@ -1,12 +1,11 @@ import { createApp } from 'vue'; import App from './App.vue'; import router from './router'; -import store from './store'; +import "../node_modules/bootstrap/dist/js/bootstrap.js"; const vueApp = createApp(App); vueApp.use(router); -vueApp.use(store); vueApp.mount("#app"); diff --git a/src/mixins/base-mixin.js b/src/mixins/base-mixin.js new file mode 100644 index 00000000..7e405d1b --- /dev/null +++ b/src/mixins/base-mixin.js @@ -0,0 +1,50 @@ +import { storeActions } from "@/constants/store-actions.js"; +import { navigationScenarios } from "@/router/router-constants/navigation-scenarios"; +import { vehicleCategories } from "@/constants/vehicle-categories.js"; +import { queryStrings } from "@/constants/query-strings"; +import { dynamicStrings } from "@/constants/dynamic-strings"; + +export default { + data() { + return { + cmsContentByWidget: {} + }; + }, + methods: { + setCmsContent(cmsContent) { + this.$root.cmsContentByWidget = cmsContent; + }, + getCmsContent(widgetName, fieldName) { + return this.$root.cmsContentByWidget?.[widgetName]?.[fieldName] ? this.$root.cmsContentByWidget[widgetName][fieldName] : ''; + }, + }, + computed: { + storeActions() { + return storeActions; + }, + navigationScenarios() { + return navigationScenarios; + }, + vehicleCategories() { + return vehicleCategories; + }, + queryStrings(){ + return queryStrings; + }, + dynamicStrings(){ + return dynamicStrings; + }, + cssClassNameForCmsWidget(){ + return "widget-name-" + this.cmsWidgetName; + }, + }, +}; + +function encodeUriData(payload) { + if (payload && Object.keys(payload).length > 0) { + // Loop through the payload and encode the values + Object.keys(payload).forEach((key) => { + payload[key] = encodeURIComponent(payload[key]); + }); + } +} diff --git a/src/router/index.js b/src/router/index.js index 0c4cf138..c76f41bf 100644 --- a/src/router/index.js +++ b/src/router/index.js @@ -1,5 +1,4 @@ import { storeActions } from "@/constants/store-actions"; -import { storeMutations } from "@/constants/store-mutations"; import { createWebHistory, createRouter } from "vue-router"; const routes = [ diff --git a/src/store/index.js b/src/store/index.js deleted file mode 100644 index 6acc99d8..00000000 --- a/src/store/index.js +++ /dev/null @@ -1,72 +0,0 @@ -import { createStore } from "vuex"; -import { endpoints } from "@/constants/endpoints.js"; -import createPersistedState from "vuex-persistedstate"; -import globalMethods from "@/global-methods"; -import { storeActions } from "@/constants/store-actions"; -import { applicationConfig } from "@/constants/application-config"; -import { issPageValues } from "@/router/router-constants/issPage-values"; - -// Export State -const getDefaultState = () => { - return { - // State ... - }; -}; - -export const state = getDefaultState(); - -// Export Mutations -export const mutations = { - // Mutations... -}; - -// Export Getters -export const getters = { - // Getters... -}; - -function getAllValuesOfPropertyInArrayOfObjects(array, propertyName) { - return (array ?? []).map(x => x[propertyName]).filter(x => x); -} - -// Export Actions -export const actions = { - // Content API Actions - getRouteInfo(context, { pageName }) { - return globalMethods.callHttpClient({ - method: endpoints.GetRouteInfo.method, - endpoint: endpoints.GetRouteInfo.url, - payload: { - pageName: pageName, - }, - }); - }, - - getHomepageName(context) { - return globalMethods.callHttpClient({ - method: endpoints.GetHomepageInfo.method, - endpoint: endpoints.GetHomepageInfo.url, - }); - }, - - getPageData(context, { pageName }) { - return globalMethods.callHttpClient({ - method: endpoints.GetPageData.method, - endpoint: `${endpoints.GetPageData.url}/${pageName}`, - payload: {}, - }); - }, -}; - -export default createStore({ - plugins: [createPersistedState()], - - // IMPORTANT: Be VERY careful when modifying these fields for at least a few reasons: - // * The CMS can reference the fields by name - // * Return users may have a previous "version" of the model, and we don't want - // them to have a breaking experience, because the model might have changed. - state, - mutations, - getters, - actions, -}); \ No newline at end of file diff --git a/src/styles/common-animations.scss b/src/styles/common-animations.scss new file mode 100644 index 00000000..c03c267a --- /dev/null +++ b/src/styles/common-animations.scss @@ -0,0 +1,30 @@ +.fade-enter-active { + transition: opacity 0.8s ease; +} + +.fade-leave-active { + transition: opacity 0.3s ease; +} + +.fade-enter-from, +.fade-leave-to { + opacity: 0; +} + +.route-fade-enter-active .fade-on-route-transition { + transition: opacity 0.2s ease-out; //This duration should be kept in sync with the app.vue element attribute +} + +.route-fade-leave-active .fade-on-route-transition { + transition: opacity 0.2s ease-in; //This duration should be kept in sync with the app.vue element attribute +} + +.route-fade-enter-from .fade-on-route-transition, +.route-fade-leave-to .fade-on-route-transition { + /* + Hack around a Chrome 96 bug in handling nested opacity transitions. + This is not needed in other browsers or Chrome 99+ where the bug + has been fixed. + */ + opacity: 0.001; +} \ No newline at end of file diff --git a/vue.config.js b/vue.config.js index 2c0367e4..940b5656 100644 --- a/vue.config.js +++ b/vue.config.js @@ -1,5 +1,22 @@ +process.env.VUE_APP_CONSUMER_CF_DISTRO = + "https://consumerapidev.safelite.com"; +process.env.VUE_APP_CURRENT_ENVIRONMENT = "Localhost"; + module.exports = { publicPath: "/", + css: { + loaderOptions: { + sass: { + // Load Order Matters!!! + // Note: only include functions, variables and mixins here + additionalData: ` + @import "./node_modules/bootstrap/scss/functions"; + @import "./node_modules/bootstrap/scss/variables"; + @import "./node_modules/bootstrap/scss/mixins"; + `, + }, + }, + }, configureWebpack: { devtool: 'source-map' }, diff --git a/vue.release.config.js b/vue.release.config.js index 2c0367e4..f6b57ccf 100644 --- a/vue.release.config.js +++ b/vue.release.config.js @@ -1,5 +1,21 @@ +process.env.VUE_APP_CONSUMER_CF_DISTRO = "__VUE_APP_CONSUMER_CF_DISTRO__"; +process.env.VUE_APP_CURRENT_ENVIRONMENT = "__VUE_APP_CURRENT_ENVIRONMENT__"; + module.exports = { publicPath: "/", + css: { + loaderOptions: { + sass: { + // Load Order Matters!!! + // Note: only include functions, variables and mixins here + additionalData: ` + @import "./node_modules/bootstrap/scss/functions"; + @import "./node_modules/bootstrap/scss/variables"; + @import "./node_modules/bootstrap/scss/mixins"; + `, + }, + }, + }, configureWebpack: { devtool: 'source-map' },