From 03a6e2d994417da3e76fd5da2962e2e0bef02952 Mon Sep 17 00:00:00 2001 From: hiteshkumar87 Date: Thu, 5 Oct 2023 20:33:04 +0530 Subject: [PATCH 01/14] Add-to-calendar --- src/assets/img/icons/Google.svg | 14 + src/assets/img/icons/Outlook.com.svg | 16 + src/assets/img/icons/Outlook.svg | 16 + src/assets/img/icons/Yahoo.svg | 13 + src/assets/img/icons/iCal.svg | 11 + src/constants/calendar-options.js | 8 + src/constants/calendar-status.js | 7 + src/helpers/add-to-calendar-helper.js | 76 +++++ src/helpers/date-helper.js | 65 +++- .../add-to-calendar/add-to-calendar.spec.js | 94 ++++++ .../add-to-calendar/add-to-calendar.vue | 300 ++++++++++++++++++ .../calendar-modal-list-button.vue | 79 +++++ .../calendar-modal-question.spec.js | 53 ++++ .../calendar-modal-question.vue | 178 +++++++++++ src/layouts/confirmation/confirmation.vue | 20 +- src/layouts/payment-method/payment-method.vue | 6 +- 16 files changed, 936 insertions(+), 20 deletions(-) create mode 100644 src/assets/img/icons/Google.svg create mode 100644 src/assets/img/icons/Outlook.com.svg create mode 100644 src/assets/img/icons/Outlook.svg create mode 100644 src/assets/img/icons/Yahoo.svg create mode 100644 src/assets/img/icons/iCal.svg create mode 100644 src/constants/calendar-options.js create mode 100644 src/constants/calendar-status.js create mode 100644 src/helpers/add-to-calendar-helper.js create mode 100644 src/layouts/add-to-calendar/add-to-calendar.spec.js create mode 100644 src/layouts/add-to-calendar/add-to-calendar.vue create mode 100644 src/layouts/add-to-calendar/calendar-modal-question/calendar-modal-list-button/calendar-modal-list-button.vue create mode 100644 src/layouts/add-to-calendar/calendar-modal-question/calendar-modal-question.spec.js create mode 100644 src/layouts/add-to-calendar/calendar-modal-question/calendar-modal-question.vue diff --git a/src/assets/img/icons/Google.svg b/src/assets/img/icons/Google.svg new file mode 100644 index 000000000..606351ec9 --- /dev/null +++ b/src/assets/img/icons/Google.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/src/assets/img/icons/Outlook.com.svg b/src/assets/img/icons/Outlook.com.svg new file mode 100644 index 000000000..467b585ac --- /dev/null +++ b/src/assets/img/icons/Outlook.com.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/src/assets/img/icons/Outlook.svg b/src/assets/img/icons/Outlook.svg new file mode 100644 index 000000000..f974fb13a --- /dev/null +++ b/src/assets/img/icons/Outlook.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/src/assets/img/icons/Yahoo.svg b/src/assets/img/icons/Yahoo.svg new file mode 100644 index 000000000..0206eccbb --- /dev/null +++ b/src/assets/img/icons/Yahoo.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/src/assets/img/icons/iCal.svg b/src/assets/img/icons/iCal.svg new file mode 100644 index 000000000..399bcdea0 --- /dev/null +++ b/src/assets/img/icons/iCal.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/src/constants/calendar-options.js b/src/constants/calendar-options.js new file mode 100644 index 000000000..c87a85b18 --- /dev/null +++ b/src/constants/calendar-options.js @@ -0,0 +1,8 @@ +const calendarOptions = { + ICAL: "iCal", + GOOGLE: "Google", + OUTLOOK: "Outlook", + OUTLOOKCOM: "Outlook.com", + YAHOO: "Yahoo", +}; +export { calendarOptions }; diff --git a/src/constants/calendar-status.js b/src/constants/calendar-status.js new file mode 100644 index 000000000..38cf23467 --- /dev/null +++ b/src/constants/calendar-status.js @@ -0,0 +1,7 @@ +const calendarStatus = { + FREE: "Free", + BUSY: "Busy", + TENTATIVE: "Tentative", + OUT_OF_THE_OFFICE: "OutOfTheOffice", +}; +export { calendarStatus }; diff --git a/src/helpers/add-to-calendar-helper.js b/src/helpers/add-to-calendar-helper.js new file mode 100644 index 000000000..36c700cb3 --- /dev/null +++ b/src/helpers/add-to-calendar-helper.js @@ -0,0 +1,76 @@ +import { calendarStatus } from "@/constants/calendar-status"; +import { getDateFormat } from "@/helpers/date-helper"; +export function getCalendarFile(calFile) { + const dateFormat = "yyyyMMddTHHmmss"; + var calEvent = []; + calEvent.push("BEGIN:VCALENDAR"); + calEvent.push("VERSION:2.0"); + calEvent.push("BEGIN:VEVENT"); + calEvent.push("DTSTAMP:" + getDateFormat(new Date(), dateFormat)); + calEvent.push("UID:" + calFile.UniqueId + "@safelite.com"); + calEvent.push("PRODID:noreply@safelite.com"); + switch (calFile.Status) { + case calendarStatus.BUSY: + calEvent.push("X-MICROSOFT-CDO-BUSYSTATUS:BUSY"); + break; + case calendarStatus.FREE: + calEvent.push("TRANSP:TRANSPARENT"); + break; + case calendarStatus.TENTATIVE: + calEvent.push("STATUS:TENTATIVE"); + break; + case calendarStatus.OUT_OF_THE_OFFICE: + calEvent.push("X-MICROSOFT-CDO-BUSYSTATUS:OOF"); + break; + default: + //throw new Exception("Invalid CalendarStatus"); + } + if (calFile.AllDayEvent) { + calEvent.push("DTSTART;VALUE=DATE:" + getDateFormat(calFile.StartDate, dateFormat)); + calEvent.push("DTEND;;VALUE=DATE:" + getDateFormat(calFile.EndDate, dateFormat)); + } else { + calEvent.push("DTSTART:" + getDateFormat(calFile.StartDate, dateFormat)); + calEvent.push("DTEND:" + getDateFormat(calFile.EndDate, dateFormat)); + } + + calEvent.push("SUMMARY:" + ToCalendarFileString(calFile.Subject, " ")); + if (calFile.Location != null && calFile.Location != "") { + calEvent.push("LOCATION:" + ToCalendarFileString(calFile.Location, " ")); + } + if (calFile.Body != null && calFile.Body != "") { + if (calFile.IsHTML) { + calEvent.push("DESCRIPTION:" + ToCalendarFileString(calFile.Body)); + calEvent.push( + "X-ALT-DESC;FMTTYPE=text/html:" + + '' + + "" + + '

' + + "" + + ToCalendarFileString(calFile.Body) + + "" + ); + } else { + calEvent.push("DESCRIPTION:" + ToCalendarFileString(calFile.Body)); + } + } + calEvent.push("END:VEVENT"); + calEvent.push("END:VCALENDAR"); + return calEvent.join("\r\n"); +} + +function ToCalendarFileString(str, replacementArg = "

") { + return str.replace("\r\n", replacementArg); +} + +export function download(filename, fileBody) { + var element = document.createElement("a"); + element.setAttribute("href", "data:text/plain;charset=utf-8," + encodeURIComponent(fileBody)); + element.setAttribute("download", filename); + + element.style.display = "none"; + document.body.appendChild(element); + + element.click(); + + document.body.removeChild(element); +} diff --git a/src/helpers/date-helper.js b/src/helpers/date-helper.js index 48b3122ab..d1c9f968b 100644 --- a/src/helpers/date-helper.js +++ b/src/helpers/date-helper.js @@ -8,7 +8,6 @@ export function getDateDifferenceInDays(startDate, endDate) { // To calculate the no. of days between two dates return Difference_In_Time / (1000 * 3600 * 24); } - export function getFullDayName(date) { if (date instanceof Date !== true) return; const days = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]; @@ -62,3 +61,67 @@ export function get12HourTimeMobileFormat(time) { } return time.join(""); // return adjusted time or original string } +export function getDateFormat(date, format) { + if (date instanceof Date !== true) return; + const year = date.getFullYear(); + const month = ("0" + (date.getMonth() + 1)).slice(-2); + const day = ("0" + date.getDate()).slice(-2); + const hour = ("0" + date.getHours()).slice(-2); + const minute = ("0" + date.getMinutes()).slice(-2); + const second = ("0" + date.getSeconds()).slice(-2); + return format + .replace("yyyy", year) + .replace("MM", month) + .replace("dd", day) + .replace("hh", hour) + .replace("HH", hour) + .replace("mm", minute) + .replace("ss", second); +} +export function calculateDuration(startDate, endDate) { + if (startDate instanceof Date !== true) return; + if (endDate instanceof Date !== true) return; + return convertMsToTime(endDate - startDate); +} +export function convertMsToTime(milliseconds) { + let seconds = Math.floor(milliseconds / 1000); + let minutes = Math.floor(seconds / 60); + let hours = Math.floor(minutes / 60); + + seconds = seconds % 60; + minutes = minutes % 60; + //commenting to get 24 time format + //hours = hours % 24; + + return `${padTo2Digits(hours)}${padTo2Digits(minutes)}`; +} +export function padTo2Digits(time) { + return ("0" + time).slice(-2); +} +export function combineDateAndTime(date, time) { + var newDate = new Date(date); + var timeParts = time.split(":"); + newDate.setHours(timeParts[0]); + newDate.setMinutes(timeParts[1]); + return newDate; +} +export function addMinutes(date, minutes) { + return new Date(date.getTime() + minutes * 60000); +} +export function shortTimeString(date) { + if (date instanceof Date !== true) return; + const hour = ("0" + date.getHours()).slice(-2); + const minute = ("0" + date.getMinutes()).slice(-2); + //const second = ("0" + date.getSeconds()).slice(-2); + let time = hour + ":" + minute; + // Check correct time format and split into components + time = time.toString().match(/^([01]\d|2[0-3])(:)([0-5]\d)?$/) || [time]; + + if (time.length > 1) { + // If time format correct + time = time.slice(1); // Remove full string match value + time[5] = +time[0] < 12 ? " AM" : " PM"; // Set AM/PM + time[0] = +time[0] % 12 || 12; // Adjust hours + } + return time.join(""); // return adjusted time or original string +} diff --git a/src/layouts/add-to-calendar/add-to-calendar.spec.js b/src/layouts/add-to-calendar/add-to-calendar.spec.js new file mode 100644 index 000000000..1bee62c2c --- /dev/null +++ b/src/layouts/add-to-calendar/add-to-calendar.spec.js @@ -0,0 +1,94 @@ +// Components +import addToCalendar from "@/layouts/add-to-calendar/add-to-calendar.vue"; + +// Supporting Files +import { shallowMount } from "@vue/test-utils"; +import { getMountOptions } from "@/helpers/unit-test-helper.js"; +import store from "@/store"; + +beforeEach(() => { + jest.restoreAllMocks(); + jest.clearAllMocks(); + store.getters = { + order: { + schedule: { + date: "2019-01-01", + startTime: "09:00", + endTime: "10:00", + routeCode: "000", + }, + lineItems: { + glassParts: [ + { + partNumber: "ABC123", + }, + ], + supportingItems: [], + }, + serviceLocation: { + address: "", + address2: null, + city: "", + state: "AZ", + appointmentType: "Inshop", + zipCode: "12345", + zipCodeCtu: "01234", + provider: { + providerNumber: "123", + address: { + streetAddress: "test1", + city: "test", + state: "AZ", + zipCode: "12345", + zipCodeCtu: "01234", + }, + }, + }, + damage: { + isRepair: false, + }, + referralNumber: "1234567", + }, + payment: { + isInsurance: true, + }, + lineItems: { + glassParts: [], + supportingItems: [], + }, + }; +}); +afterEach(() => { + store.getters = {}; + jest.restoreAllMocks(); + jest.clearAllMocks(); +}); + +describe("Add-to-calendar methods...", () => { + test("Add-to-calendar should trigger openModal method", () => { + //Arrange + const { wrapper } = setupMocks({}); + + //Act + wrapper.vm.openCalendarModal(); + + //Assert + expect(wrapper.vm.$refs.calendarModalQuestion.openModal).toBeCalled(); + }); +}); + +function setupMocks({ customMountOptions }) { + const mountOptions = getMountOptions({ + ...customMountOptions, + }); + + const wrapper = shallowMount(addToCalendar, mountOptions); + + //wrapper.vm.setCmsContent = jest.fn(); + //wrapper.vm.$refs.datePicker.initializeComponent = jest.fn(); + //wrapper.vm.$refs.locationAlerts.initializeComponent = jest.fn(); + //wrapper.vm.$refs.funnelFooter.updateButtonText = jest.fn(); + wrapper.vm.$refs.calendarModalQuestion.openModal = jest.fn(); + + return { wrapper }; +} diff --git a/src/layouts/add-to-calendar/add-to-calendar.vue b/src/layouts/add-to-calendar/add-to-calendar.vue new file mode 100644 index 000000000..b0306b5d0 --- /dev/null +++ b/src/layouts/add-to-calendar/add-to-calendar.vue @@ -0,0 +1,300 @@ + + + diff --git a/src/layouts/add-to-calendar/calendar-modal-question/calendar-modal-list-button/calendar-modal-list-button.vue b/src/layouts/add-to-calendar/calendar-modal-question/calendar-modal-list-button/calendar-modal-list-button.vue new file mode 100644 index 000000000..7f4fe2801 --- /dev/null +++ b/src/layouts/add-to-calendar/calendar-modal-question/calendar-modal-list-button/calendar-modal-list-button.vue @@ -0,0 +1,79 @@ + + + diff --git a/src/layouts/add-to-calendar/calendar-modal-question/calendar-modal-question.spec.js b/src/layouts/add-to-calendar/calendar-modal-question/calendar-modal-question.spec.js new file mode 100644 index 000000000..d28c2442f --- /dev/null +++ b/src/layouts/add-to-calendar/calendar-modal-question/calendar-modal-question.spec.js @@ -0,0 +1,53 @@ +import { shallowMount } from "@vue/test-utils"; +import { getMountOptions } from "@/helpers/unit-test-helper.js"; +import calendarModalQuestion from "@/layouts/add-to-calendar/calendar-modal-question/calendar-modal-question.vue"; + +describe("calendar-modal-question.vue", () => { + test("When selected calendar option is changed, a correctly selectedCalendarOption should be emitted", async () => { + //Arrange + const url = "iCal url"; + const name = "iCal"; + const icon = "iCal.svg"; + const expectedEmit = [ + [ + { + url: url, + name: name, + icon: icon, + }, + ], + ]; + + const { wrapper } = setupMocks({ + customMountOptions: { + propsData: { + calendarOptionsData: [ + { + url: url, + name: name, + icon: icon, + }, + ], + }, + }, + }); + + wrapper.vm.$refs.calendarOptions.closeModal = jest.fn(); + wrapper.vm.selectedCalendarOption = name; + + //Act + wrapper.vm.setSelectedCalendarOption(); + + //Assert + expect(wrapper.emitted()["update:modelValue"]).toEqual(expectedEmit); + }); +}); + +function setupMocks({ customMountOptions }) { + const mountOptions = getMountOptions({ + ...customMountOptions, + }); + + const wrapper = shallowMount(calendarModalQuestion, mountOptions); + return { wrapper }; +} diff --git a/src/layouts/add-to-calendar/calendar-modal-question/calendar-modal-question.vue b/src/layouts/add-to-calendar/calendar-modal-question/calendar-modal-question.vue new file mode 100644 index 000000000..abe4ef39b --- /dev/null +++ b/src/layouts/add-to-calendar/calendar-modal-question/calendar-modal-question.vue @@ -0,0 +1,178 @@ + + + diff --git a/src/layouts/confirmation/confirmation.vue b/src/layouts/confirmation/confirmation.vue index 80532c6a7..05754d6a6 100644 --- a/src/layouts/confirmation/confirmation.vue +++ b/src/layouts/confirmation/confirmation.vue @@ -20,21 +20,7 @@

{{ this.getScheduleDate() }}

{{ this.getScheduleTime() }}

- -
-
- -
-
+
@@ -57,6 +43,7 @@ import funnelHeader from "@/fmg-components/funnel-header/funnel-header"; import vehicleBanner from "@/fmg-components/vehicle-banner/vehicle-banner"; import navbar from "@/fmg-components/nav-bar/nav-bar"; +import addToCalendar from "@/layouts/add-to-calendar/add-to-calendar"; //Supporting files import { fetchCmsContentForPage } from "@/helpers/cms-content-helper"; import { AppointmentTypeStrings } from "@/constants/schedule-constants"; @@ -192,7 +179,7 @@ export default { // Schedule const schedule = store.getters.order.schedule; - const scheduleReqs = !!(schedule.date && schedule.startTime); //removing schedule.endTime for testing + const scheduleReqs = !!(schedule.date && schedule.startTime && schedule.endTime); return serviceLocationReqs && scheduleReqs; }, @@ -245,6 +232,7 @@ export default { funnelHeader, navbar, vehicleBanner, + addToCalendar, }, }; diff --git a/src/layouts/payment-method/payment-method.vue b/src/layouts/payment-method/payment-method.vue index 2b34185f4..9bce01671 100644 --- a/src/layouts/payment-method/payment-method.vue +++ b/src/layouts/payment-method/payment-method.vue @@ -191,9 +191,9 @@ export default { this.$router.navigateWithoutSaving(this.navigationScenarios.CLICKED_BACK, this.$route); }, async forwardButtonAction() { - this.dispatchStoreAction(storeActions.SAVE_PIA_ERROR_CODE, null, false); - this.dispatchStoreAction(this.storeActions.SAVE_WORK_ORDER_FLAG, true, false); - await submitWorkOrder({ pageNameToLog: "payment-method" }); + // this.dispatchStoreAction(storeActions.SAVE_PIA_ERROR_CODE, null, false); + // this.dispatchStoreAction(this.storeActions.SAVE_WORK_ORDER_FLAG, true, false); + // await submitWorkOrder({ pageNameToLog: "payment-method" }); this.$router.navigateWithoutSaving( this.navigationScenarios.CLICKED_FORWARD, this.$route From 77bf3fc68c327e73ee658fb8caeb1ab77db73e47 Mon Sep 17 00:00:00 2001 From: sheena Date: Fri, 6 Oct 2023 17:37:08 +0530 Subject: [PATCH 02/14] Update calendar-modal-question.vue updated the space --- .../calendar-modal-question/calendar-modal-question.vue | 1 + 1 file changed, 1 insertion(+) diff --git a/src/layouts/add-to-calendar/calendar-modal-question/calendar-modal-question.vue b/src/layouts/add-to-calendar/calendar-modal-question/calendar-modal-question.vue index abe4ef39b..dacda07f2 100644 --- a/src/layouts/add-to-calendar/calendar-modal-question/calendar-modal-question.vue +++ b/src/layouts/add-to-calendar/calendar-modal-question/calendar-modal-question.vue @@ -15,6 +15,7 @@ :buttonTypeObject="calendarModalListButton" :answers="getCalendarOptionData" groupName="chooseCalendarType" + class="mt-1" textPosition="text-center" v-model="selectedCalendarOption" isRequired From 3bfbeecc5720089eb8100a25746f2cbd20fa3114 Mon Sep 17 00:00:00 2001 From: hiteshkumar87 <104490440+hiteshkumar87@users.noreply.github.com> Date: Fri, 6 Oct 2023 18:41:20 +0530 Subject: [PATCH 03/14] Update payment-method.vue uncomment forwarbuttonAction store actions --- src/layouts/payment-method/payment-method.vue | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/layouts/payment-method/payment-method.vue b/src/layouts/payment-method/payment-method.vue index 9bce01671..9f1666953 100644 --- a/src/layouts/payment-method/payment-method.vue +++ b/src/layouts/payment-method/payment-method.vue @@ -191,9 +191,9 @@ export default { this.$router.navigateWithoutSaving(this.navigationScenarios.CLICKED_BACK, this.$route); }, async forwardButtonAction() { - // this.dispatchStoreAction(storeActions.SAVE_PIA_ERROR_CODE, null, false); - // this.dispatchStoreAction(this.storeActions.SAVE_WORK_ORDER_FLAG, true, false); - // await submitWorkOrder({ pageNameToLog: "payment-method" }); + this.dispatchStoreAction(storeActions.SAVE_PIA_ERROR_CODE, null, false); + this.dispatchStoreAction(this.storeActions.SAVE_WORK_ORDER_FLAG, true, false); + await submitWorkOrder({ pageNameToLog: "payment-method" }); this.$router.navigateWithoutSaving( this.navigationScenarios.CLICKED_FORWARD, this.$route From 042c8d2473456c4b79db649e758ac84ae2d66277 Mon Sep 17 00:00:00 2001 From: hiteshkumar87 Date: Fri, 6 Oct 2023 18:47:59 +0530 Subject: [PATCH 04/14] Update payment-method.vue formatting --- src/layouts/payment-method/payment-method.vue | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/layouts/payment-method/payment-method.vue b/src/layouts/payment-method/payment-method.vue index 9f1666953..2b34185f4 100644 --- a/src/layouts/payment-method/payment-method.vue +++ b/src/layouts/payment-method/payment-method.vue @@ -191,9 +191,9 @@ export default { this.$router.navigateWithoutSaving(this.navigationScenarios.CLICKED_BACK, this.$route); }, async forwardButtonAction() { - this.dispatchStoreAction(storeActions.SAVE_PIA_ERROR_CODE, null, false); - this.dispatchStoreAction(this.storeActions.SAVE_WORK_ORDER_FLAG, true, false); - await submitWorkOrder({ pageNameToLog: "payment-method" }); + this.dispatchStoreAction(storeActions.SAVE_PIA_ERROR_CODE, null, false); + this.dispatchStoreAction(this.storeActions.SAVE_WORK_ORDER_FLAG, true, false); + await submitWorkOrder({ pageNameToLog: "payment-method" }); this.$router.navigateWithoutSaving( this.navigationScenarios.CLICKED_FORWARD, this.$route From f8f22fb619077475225c3b6c3edbe066a210505a Mon Sep 17 00:00:00 2001 From: Adam Caouette Date: Tue, 10 Oct 2023 10:24:28 -0400 Subject: [PATCH 05/14] CSR-1384: reformat updates --- src/fmg-components/cart/cart.vue | 5 ++++- src/layouts/schedule/schedule.spec.js1 | 0 2 files changed, 4 insertions(+), 1 deletion(-) delete mode 100644 src/layouts/schedule/schedule.spec.js1 diff --git a/src/fmg-components/cart/cart.vue b/src/fmg-components/cart/cart.vue index 6225010e4..a8a3c3d5a 100644 --- a/src/fmg-components/cart/cart.vue +++ b/src/fmg-components/cart/cart.vue @@ -196,7 +196,10 @@ export default { }, displayedNonPackageLineItems() { const displayedNonPackageLineItems = []; - const possibleLineItems = [...this.vaps, ...this.supportingItems]; + const possibleLineItems = [ + ...this.vaps, + ...this.supportingItems, + ]; possibleLineItems?.forEach((item) => { let price = item.sellingPrice + item.kitPrice + item.laborAmount; const isAllowed = this.allowableLineItems?.findIndex((lineItem) => { diff --git a/src/layouts/schedule/schedule.spec.js1 b/src/layouts/schedule/schedule.spec.js1 deleted file mode 100644 index e69de29bb..000000000 From 348dba428a73c4eac610e3799e20280b034ebb6e Mon Sep 17 00:00:00 2001 From: Adam Caouette Date: Tue, 10 Oct 2023 14:30:43 -0400 Subject: [PATCH 06/14] CSR-1384: updates for cart work, using lineItems instead of cartItems --- src/fmg-components/cart/cart.vue | 34 ++++--------- src/layouts/payment-method/payment-method.vue | 48 ++++++++++++------- 2 files changed, 39 insertions(+), 43 deletions(-) diff --git a/src/fmg-components/cart/cart.vue b/src/fmg-components/cart/cart.vue index a8a3c3d5a..bba4527ae 100644 --- a/src/fmg-components/cart/cart.vue +++ b/src/fmg-components/cart/cart.vue @@ -54,47 +54,34 @@ From 5a3d9c3b80021b57d3d1347311628a8fbdbe4d0b Mon Sep 17 00:00:00 2001 From: Adam Caouette Date: Tue, 10 Oct 2023 14:32:29 -0400 Subject: [PATCH 07/14] CSR-1384: formatting --- src/fmg-components/cart/cart.vue | 5 +---- src/layouts/payment-method/payment-method.vue | 6 +----- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/src/fmg-components/cart/cart.vue b/src/fmg-components/cart/cart.vue index bba4527ae..d798c3c38 100644 --- a/src/fmg-components/cart/cart.vue +++ b/src/fmg-components/cart/cart.vue @@ -181,10 +181,7 @@ export default { }, displayedNonPackageLineItems() { const displayedNonPackageLineItems = []; - const possibleLineItems = [ - ...this.vaps, - ...this.supportingItems, - ]; + const possibleLineItems = [...this.vaps, ...this.supportingItems]; possibleLineItems?.forEach((item) => { let price = item.sellingPrice + item.kitPrice + item.laborAmount; const isAllowed = this.allowableLineItems?.findIndex((lineItem) => { diff --git a/src/layouts/payment-method/payment-method.vue b/src/layouts/payment-method/payment-method.vue index afe6a5e5c..8e6aa881a 100644 --- a/src/layouts/payment-method/payment-method.vue +++ b/src/layouts/payment-method/payment-method.vue @@ -172,11 +172,7 @@ export default { ); const lineItems = store.getters.lineItems; - const cartItems = [ - ...clonedGlassParts, - ...lineItems.supportingItems, - ...lineItems.vaps, - ]; + const cartItems = [...clonedGlassParts, ...lineItems.supportingItems, ...lineItems.vaps]; // Call the "next" function to complete the transition to this page. next((vm) => { From ab1f47d03870d38fbc55fcbe7384e7ddcb1f6567 Mon Sep 17 00:00:00 2001 From: Adam Caouette Date: Tue, 10 Oct 2023 15:37:21 -0400 Subject: [PATCH 08/14] CSR-1384: use deepClone on resultMap and glassParts --- src/layouts/payment-method/payment-method.vue | 38 ++++++++++--------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/src/layouts/payment-method/payment-method.vue b/src/layouts/payment-method/payment-method.vue index 8e6aa881a..1827c91bb 100644 --- a/src/layouts/payment-method/payment-method.vue +++ b/src/layouts/payment-method/payment-method.vue @@ -105,6 +105,7 @@ import alert from "@/ux-components/alert/alert"; import baseMixin from "@/mixins/base-mixin.js"; import { settleAllPromises } from "@/helpers/layout-helper"; +import { deepClone } from "@/helpers/object-helper"; import { storeActions } from "@/constants/store-actions"; import store from "@/store"; import { fetchCmsContentForPage } from "@/helpers/cms-content-helper"; @@ -120,6 +121,7 @@ export default { null, "payment-method" ); + const rainDefensePromise = baseMixin.methods.dispatchStoreActionWithLogging( storeActions.GET_RAIN_DEFENSE, null, @@ -151,28 +153,30 @@ export default { ]; const resultMap = await settleAllPromises(promiseResultMap); - const clonedGlassParts = store.getters.order.lineItems.glassParts - ? JSON.parse(JSON.stringify(store.getters.order.lineItems.glassParts)) - : []; + + const resultMapCopy = deepClone(resultMap) ?? []; + + const glassPartsCopy = deepClone(store.getters.order.lineItems.glassParts) ?? []; + const availableLineItems = [ - resultMap.rainDefense, - ...resultMap.supportingItems, - ...resultMap.wipers, - ...clonedGlassParts, + resultMapCopy.rainDefense, + ...resultMapCopy.supportingItems, + ...resultMapCopy.wipers, + ...glassPartsCopy, ]; // TODO - DO WE NEED TO GET PRICINGRESULTS HERE? SEEMS TO BE THE SAME AS availableLineItems - const pricingResults = await baseMixin.methods.dispatchStoreActionWithLogging( - storeActions.PRICE_ORDER_ITEMS_AND_SAVE_SERVER_DATA, - { - availableLineItems: availableLineItems, - }, - "payment-method", - false - ); + // const pricingResults = await baseMixin.methods.dispatchStoreActionWithLogging( + // storeActions.PRICE_ORDER_ITEMS_AND_SAVE_SERVER_DATA, + // { + // availableLineItems: availableLineItems, + // }, + // "payment-method", + // false + // ); const lineItems = store.getters.lineItems; - const cartItems = [...clonedGlassParts, ...lineItems.supportingItems, ...lineItems.vaps]; + const cartItems = [...glassPartsCopy, ...lineItems.supportingItems, ...lineItems.vaps]; // Call the "next" function to complete the transition to this page. next((vm) => { @@ -181,7 +185,7 @@ export default { vm.cartItems = cartItems; // TOBEREMOVED BY AJC IN CSR-1384 vm.setCmsContent(resultMap.cmsContent); - vm.availableLineItems = pricingResults; + vm.availableLineItems = availableLineItems; vm.lineItems = lineItems; }); }, From 575978ae6553759c64a4345dc19eb5fef3149fd4 Mon Sep 17 00:00:00 2001 From: Adam Caouette Date: Wed, 11 Oct 2023 10:17:14 -0400 Subject: [PATCH 09/14] CSR-1384: refactoring re: deepClone, naming --- src/layouts/payment-method/payment-method.vue | 45 +++++++++---------- src/store/index.js | 4 +- 2 files changed, 24 insertions(+), 25 deletions(-) diff --git a/src/layouts/payment-method/payment-method.vue b/src/layouts/payment-method/payment-method.vue index bb42bf650..e451958e4 100644 --- a/src/layouts/payment-method/payment-method.vue +++ b/src/layouts/payment-method/payment-method.vue @@ -17,7 +17,7 @@ @@ -34,10 +34,10 @@ + v-on="{ 'buttonEvent.openModal': openModal }" />
- + Date: Wed, 11 Oct 2023 13:59:13 -0400 Subject: [PATCH 10/14] CSR-1384: update store method to avoid mutating original lineItems array --- src/store/index.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/store/index.js b/src/store/index.js index 6ca7221ac..32f004950 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -14,6 +14,7 @@ import { deleteFunnelCookie } from "@/helpers/heritage-integration/cookie-helper import { deepEqual } from "@/helpers/object-helper"; import { AppointmentTypeStrings, PREMIUM_FEE_PART_TYPE } from "@/constants/schedule-constants"; import { getQuerystringParameter } from "@/helpers/querystring-helper"; +import { deepClone } from "@/helpers/object-helper"; import { queryStrings } from "@/constants/query-strings"; import { partTypeStrings } from "@/constants/part-type-strings"; import { @@ -2274,7 +2275,8 @@ function convertGlassPieceNamingFromApi(glassArray) { } function addPricesToLineItems(lineItems, pricingLineItems) { - lineItems.forEach((lineItem) => { + const pricedLineItems = deepClone(lineItems); + pricedLineItems.forEach((lineItem) => { let lineItemIndex = pricingLineItems.findIndex( (pricingLineItem) => pricingLineItem.partNumber === lineItem.partNumber ); @@ -2286,7 +2288,7 @@ function addPricesToLineItems(lineItems, pricingLineItems) { lineItem.sellingPrice = pricedLineItem.sellingPrice; lineItem.kitPrice = pricedLineItem.kitPrice; }); - return lineItems; + return pricedLineItems; } function getFlattenedArrayOfLineItemsWithChildParts(lineItems) { From a47dfbc27e15ad3746d0716dc6550f722b389892 Mon Sep 17 00:00:00 2001 From: Adam Caouette Date: Wed, 11 Oct 2023 14:01:50 -0400 Subject: [PATCH 11/14] CSR-1384: more cleanup, remove deep cloning --- .../add-vaps-modal-buttons.vue | 13 +++++++---- src/layouts/payment-method/payment-method.vue | 22 +++++-------------- 2 files changed, 15 insertions(+), 20 deletions(-) diff --git a/src/fmg-components/add-vaps-modal-buttons/add-vaps-modal-buttons.vue b/src/fmg-components/add-vaps-modal-buttons/add-vaps-modal-buttons.vue index 0a6faa72f..d894f2ee8 100644 --- a/src/fmg-components/add-vaps-modal-buttons/add-vaps-modal-buttons.vue +++ b/src/fmg-components/add-vaps-modal-buttons/add-vaps-modal-buttons.vue @@ -21,9 +21,8 @@ export default { name: "add-vaps-modal-buttons", props: { cartItems: Object, - rainDefenseItem: Object, - wiperItems: Object, vapsTilesCmsName: String, + availableLineItems: Object, }, data() { return { @@ -55,13 +54,19 @@ export default { }); }, rainDefensePrice() { - const lineItem = this.rainDefenseItem; + // const lineItem = this.rainDefenseItem; + const lineItem = this.availableLineItems.find((item) => { + return item.partType === "RAIN DEFENSE"; + }); + const price = lineItem.laborAmount + lineItem.sellingPrice + lineItem.kitPrice; return parseFloat(price).toFixed(2); }, wipersPrice() { let price = 0; - const lineItems = this.wiperItems; + const lineItems = this.availableLineItems.filter((item) => { + return item.partType.includes(" WIPER"); + }); lineItems?.forEach((item) => { price += item.laborAmount + item.sellingPrice + item.kitPrice; }); diff --git a/src/layouts/payment-method/payment-method.vue b/src/layouts/payment-method/payment-method.vue index e451958e4..eddaa6a20 100644 --- a/src/layouts/payment-method/payment-method.vue +++ b/src/layouts/payment-method/payment-method.vue @@ -34,8 +34,7 @@ @@ -105,7 +104,6 @@ import alert from "@/ux-components/alert/alert"; import baseMixin from "@/mixins/base-mixin.js"; import { settleAllPromises } from "@/helpers/layout-helper"; -import { deepClone } from "@/helpers/object-helper"; import { storeActions } from "@/constants/store-actions"; import store from "@/store"; import { fetchCmsContentForPage } from "@/helpers/cms-content-helper"; @@ -128,6 +126,8 @@ export default { null, "payment-method" ); + console.log("rainDefensePromise: ", rainDefensePromise); + const supportingItemsPromise = baseMixin.methods.dispatchStoreActionWithLogging( storeActions.GET_SUPPORTING_ITEMS, null, @@ -155,15 +155,13 @@ export default { const resultMap = await settleAllPromises(promiseResultMap); - const resultMapCopy = deepClone(resultMap) ?? []; - - const glassPartsCopy = deepClone(store.getters.order.lineItems.glassParts) ?? []; + const glassParts = (await store.getters.order.lineItems.glassParts) ?? []; const availableLineItems = [ resultMap.rainDefense, ...resultMap.supportingItems, ...resultMap.wipers, - ...glassPartsCopy, + ...glassParts, ]; const pricedAvailableLineItems = await baseMixin.methods.dispatchStoreActionWithLogging( @@ -176,12 +174,10 @@ export default { ); const lineItems = store.getters.lineItems; - const cartItems = [...glassPartsCopy, ...lineItems.supportingItems, ...lineItems.vaps]; + const cartItems = [...glassParts, ...lineItems.supportingItems, ...lineItems.vaps]; // TOBEREMOVED BY AJC IN CSR-1384 // Call the "next" function to complete the transition to this page. next((vm) => { - vm.availableWipers = resultMap.wipers; // TOBEREMOVED BY AJC IN CSR-1384 - vm.availableRainDefense = resultMap.rainDefense; // TOBEREMOVED BY AJC IN CSR-1384 vm.cartItems = cartItems; // TOBEREMOVED BY AJC IN CSR-1384 vm.setCmsContent(resultMap.cmsContent); @@ -192,8 +188,6 @@ export default { data() { return { cartItems: [], // TOBEREMOVED BY AJC IN CSR-1384 - availableWipers: [], // TOBEREMOVED BY AJC IN CSR-1384 - availableRainDefense: {}, // TOBEREMOVED BY AJC IN CSR-1384 lineItems: [], availableLineItems: [], @@ -326,10 +320,6 @@ export default { // console.log("this.$store.getters.damage ", this.$store.getters.damage) return this.$store.getters.damage; }, - storeLineItems() { - // console.log("storeLineItems, aka lineItems from store: ", this.$store.getters.lineItems) - return this.$store.getters.lineItems; - }, }, components: { funnelHeader, From bc4f083368cceb7e8dfa8ea96fb6a0277f7e8d40 Mon Sep 17 00:00:00 2001 From: hiteshkumar87 Date: Thu, 12 Oct 2023 16:33:13 +0530 Subject: [PATCH 12/14] unit test cases unit test cases --- src/helpers/add-to-calendar-helper.js | 2 +- src/helpers/add-to-calendar-helper.spec.js | 79 +++++++ src/helpers/date-helper.js | 81 ++++---- src/helpers/date-helper.spec.js | 191 +++++++++++++++++ .../add-to-calendar/add-to-calendar.spec.js | 156 ++++++++++++++ src/layouts/confirmation/confirmation.spec.js | 194 +++++++++++++++++- 6 files changed, 651 insertions(+), 52 deletions(-) create mode 100644 src/helpers/add-to-calendar-helper.spec.js create mode 100644 src/helpers/date-helper.spec.js diff --git a/src/helpers/add-to-calendar-helper.js b/src/helpers/add-to-calendar-helper.js index 36c700cb3..93a5e7c18 100644 --- a/src/helpers/add-to-calendar-helper.js +++ b/src/helpers/add-to-calendar-helper.js @@ -6,7 +6,7 @@ export function getCalendarFile(calFile) { calEvent.push("BEGIN:VCALENDAR"); calEvent.push("VERSION:2.0"); calEvent.push("BEGIN:VEVENT"); - calEvent.push("DTSTAMP:" + getDateFormat(new Date(), dateFormat)); + calEvent.push("DTSTAMP:" + calFile.TimeStamp); calEvent.push("UID:" + calFile.UniqueId + "@safelite.com"); calEvent.push("PRODID:noreply@safelite.com"); switch (calFile.Status) { diff --git a/src/helpers/add-to-calendar-helper.spec.js b/src/helpers/add-to-calendar-helper.spec.js new file mode 100644 index 000000000..1cb40d499 --- /dev/null +++ b/src/helpers/add-to-calendar-helper.spec.js @@ -0,0 +1,79 @@ +import { download, getCalendarFile } from "./add-to-calendar-helper"; + +// Create a mock function +const mockDownload = jest.fn((filename, fileBody) => { + // Simulate creating an anchor element + const mockElement = { + setAttribute: jest.fn(), + style: { display: "none" }, + click: jest.fn(), + }; + // Mock the document.createElement method + document.createElement = jest.fn(() => mockElement); + // Mock the document.body.appendChild method + document.body.appendChild = jest.fn(); + // Mock the document.body.removeChild method + document.body.removeChild = jest.fn(); + + // Call the original function with the mock element + download(filename, fileBody); + + // Return the mock element for further assertions + return mockElement; +}); + +// Use the mock function in a test +test("download function", () => { + // Call the mock function with some arguments + const mockElement = mockDownload("test.txt", "Hello world"); + + // Make assertions about the mock function and the mock element + expect(mockDownload).toHaveBeenCalledTimes(1); + expect(mockDownload).toHaveBeenCalledWith("test.txt", "Hello world"); + expect(mockElement.setAttribute).toHaveBeenCalledTimes(2); + expect(mockElement.setAttribute).toHaveBeenCalledWith( + "href", + "data:text/plain;charset=utf-8," + encodeURIComponent("Hello world") + ); + expect(mockElement.setAttribute).toHaveBeenCalledWith("download", "test.txt"); + expect(mockElement.style.display).toBe("none"); + expect(mockElement.click).toHaveBeenCalledTimes(1); + expect(document.body.appendChild).toHaveBeenCalledTimes(1); + expect(document.body.appendChild).toHaveBeenCalledWith(mockElement); + expect(document.body.removeChild).toHaveBeenCalledTimes(1); + expect(document.body.removeChild).toHaveBeenCalledWith(mockElement); +}); + +// Use the mock function in a test +test("getCalendarFile function", () => { + // Create a mock calFile object + const mockCalFile = { + UniqueId: "123456", + Status: "Busy", + StartDate: new Date(2023, 9, 11, 12, 0, 0), + EndDate: new Date(2023, 9, 11, 13, 0, 0), + Subject: "Meeting with John", + Location: "Conference Room", + Body: "Discuss the project progress", + TimeStamp: "20231011T113118", + }; + + // Call the mock function with the mock calFile object + const mockCalendarFile = getCalendarFile(mockCalFile); + + // Make assertions about the mock function and the mock calendar file + expect(mockCalendarFile).toContain("BEGIN:VCALENDAR"); + expect(mockCalendarFile).toContain("VERSION:2.0"); + expect(mockCalendarFile).toContain("BEGIN:VEVENT"); + expect(mockCalendarFile).toContain("DTSTAMP:20231011T113118"); + expect(mockCalendarFile).toContain("UID:123456@safelite.com"); + expect(mockCalendarFile).toContain("PRODID:noreply@safelite.com"); + expect(mockCalendarFile).toContain("DTSTART:20231011T120000"); + expect(mockCalendarFile).toContain("DTEND:20231011T130000"); + expect(mockCalendarFile).toContain("X-MICROSOFT-CDO-BUSYSTATUS:BUSY"); + expect(mockCalendarFile).toContain("SUMMARY:Meeting with John"); + expect(mockCalendarFile).toContain("LOCATION:Conference Room"); + expect(mockCalendarFile).toContain("DESCRIPTION:Discuss the project progress"); + expect(mockCalendarFile).toContain("END:VEVENT"); + expect(mockCalendarFile).toContain("END:VCALENDAR"); +}); diff --git a/src/helpers/date-helper.js b/src/helpers/date-helper.js index d1c9f968b..d42ad326f 100644 --- a/src/helpers/date-helper.js +++ b/src/helpers/date-helper.js @@ -1,35 +1,27 @@ export function getDateDifferenceInDays(startDate, endDate) { + // Create date objects from the input strings var date1 = new Date(endDate); - date1.setHours(0, 0, 0, 0); var date2 = new Date(startDate); - date2.setHours(0, 0, 0, 0); - // To calculate the time difference of two dates - var Difference_In_Time = date1.getTime() - date2.getTime(); - // To calculate the no. of days between two dates - return Difference_In_Time / (1000 * 3600 * 24); + // Use the built-in method to get the difference in milliseconds + var difference = date1 - date2; + // Convert milliseconds to days and return the result + return difference / (1000 * 3600 * 24); } export function getFullDayName(date) { - if (date instanceof Date !== true) return; - const days = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]; - return days[date.getDay()]; + // Use a ternary operator to check if the input is a valid date object + return date instanceof Date + ? // Use the built-in method toLocaleDateString() to get the full day name in the current locale + date.toLocaleDateString(undefined, { weekday: "long" }) + : // Return undefined if the input is not a valid date object + undefined; } export function getFullMonthName(date) { - if (date instanceof Date !== true) return; - const monthNames = [ - "January", - "February", - "March", - "April", - "May", - "June", - "July", - "August", - "September", - "October", - "November", - "December", - ]; - return monthNames[date.getMonth()]; + // Use a ternary operator to check if the input is a valid date object + return date instanceof Date + ? // Use the built-in method toLocaleDateString() to get the full month name in the current locale + date.toLocaleDateString(undefined, { month: "long" }) + : // Return undefined if the input is not a valid date object + undefined; } export function get12HourTimeFormat(time) { // Check correct time format and split into components @@ -96,32 +88,31 @@ export function convertMsToTime(milliseconds) { return `${padTo2Digits(hours)}${padTo2Digits(minutes)}`; } export function padTo2Digits(time) { - return ("0" + time).slice(-2); + // Use the built-in method toString() with a radix of 10 to convert the time value to a decimal string + time = time.toString(10); + // Use the conditional operator to check if the length of the string is less than 2 + return time.length < 2 + ? // If yes, prepend a '0' to the string and return it + "0" + time + : // If no, return the original string + time; } export function combineDateAndTime(date, time) { - var newDate = new Date(date); - var timeParts = time.split(":"); - newDate.setHours(timeParts[0]); - newDate.setMinutes(timeParts[1]); + // Use the Date.parse() method to convert the date and time strings to a numeric value + var timestamp = Date.parse(date + "T" + time); + // Use the new Date() constructor to create a new date object from the numeric value + var newDate = new Date(timestamp); + // Return the new date object return newDate; } export function addMinutes(date, minutes) { return new Date(date.getTime() + minutes * 60000); } export function shortTimeString(date) { - if (date instanceof Date !== true) return; - const hour = ("0" + date.getHours()).slice(-2); - const minute = ("0" + date.getMinutes()).slice(-2); - //const second = ("0" + date.getSeconds()).slice(-2); - let time = hour + ":" + minute; - // Check correct time format and split into components - time = time.toString().match(/^([01]\d|2[0-3])(:)([0-5]\d)?$/) || [time]; - - if (time.length > 1) { - // If time format correct - time = time.slice(1); // Remove full string match value - time[5] = +time[0] < 12 ? " AM" : " PM"; // Set AM/PM - time[0] = +time[0] % 12 || 12; // Adjust hours - } - return time.join(""); // return adjusted time or original string + // Use a ternary operator to check if the input is a valid date object + return date instanceof Date + ? // Use the built-in method toLocaleTimeString() to get the short time string in the current locale + date.toLocaleTimeString(undefined, { hour: "numeric", minute: "numeric", hour12: true }) + : // Return undefined if the input is not a valid date object + undefined; } diff --git a/src/helpers/date-helper.spec.js b/src/helpers/date-helper.spec.js new file mode 100644 index 000000000..4ea2157f5 --- /dev/null +++ b/src/helpers/date-helper.spec.js @@ -0,0 +1,191 @@ +import { + getFullDayName, + getFullMonthName, + get12HourTimeFormat, + get12HourTimeMobileFormat, + getDateFormat, + getDateDifferenceInDays, + calculateDuration, + padTo2Digits, + combineDateAndTime, + addMinutes, + shortTimeString, +} from "./date-helper"; +describe("date-helper.js", () => { + it("get12HourTimeMobileFormat should return time in expected 12 hour mobile format.", () => { + // Define some sample times and their expected 12-hour formats + const testCases = [ + { time: "00:00", expected: "12 AM" }, + { time: "01:23", expected: "1:23 AM" }, + { time: "11:59", expected: "11:59 AM" }, + { time: "12:00", expected: "12 PM" }, + { time: "13:45", expected: "1:45 PM" }, + { time: "23:59", expected: "11:59 PM" }, + ]; + // Loop through the test cases and call the function with each time + for (let testCase of testCases) { + // Call the function and store the result + const result = get12HourTimeMobileFormat(testCase.time); + // Assert that the result is equal to the expected 12-hour format + expect(result).toEqual(testCase.expected); + } + }); + // Test if the function returns the correct 12-hour format for some sample times + it("should return the correct 12-hour format", function () { + // Define some sample times and their expected 12-hour formats + const testCases = [ + { time: "00:00", expected: "12:00 AM" }, + { time: "01:23", expected: "1:23 AM" }, + { time: "11:59", expected: "11:59 AM" }, + { time: "12:00", expected: "12:00 PM" }, + { time: "13:45", expected: "1:45 PM" }, + { time: "23:59", expected: "11:59 PM" }, + ]; + // Loop through the test cases and call the function with each time + for (let testCase of testCases) { + // Call the function and store the result + const result = get12HourTimeFormat(testCase.time); + // Assert that the result is equal to the expected 12-hour format + expect(result).toEqual(testCase.expected); + } + }); + it("getFullMonthName should return full month format.", () => { + // Arrange / Act + const date = new Date("2023-10-01"); + const monthName = getFullMonthName(date); + + // Assert + expect(monthName).toEqual("October"); + }); + it("getFullDayName should return full Day Name format.", () => { + // Arrange / Act + const date = new Date("2023-10-01"); + const dayName = getFullDayName(date); + + // Assert + expect(dayName).toEqual("Sunday"); + }); + it("getDateFormat should return date in the given format.", () => { + // Arrange / Act + const date = new Date("2023-10-01"); + const format = "yyyy-MM-dd"; + const formattedDate = getDateFormat(date, format); + + // Assert + expect(formattedDate).toEqual("2023-10-01"); + }); + it("should return the correct difference in days", function () { + // Define some sample dates and their expected differences + const testCases = [ + { startDate: "2023-10-12", endDate: "2023-10-15", expected: 3 }, + { startDate: "2023-01-01", endDate: "2023-01-31", expected: 30 }, + { startDate: "2022-12-31", endDate: "2023-01-01", expected: 1 }, + { startDate: "2023-02-28", endDate: "2023-03-01", expected: 1 }, + { startDate: "2023-03-01", endDate: "2023-02-28", expected: -1 }, + ]; + // Loop through the test cases and call the function with each pair of dates + for (let testCase of testCases) { + // Call the function and store the result + const result = getDateDifferenceInDays(testCase.startDate, testCase.endDate); + // Assert that the result is equal to the expected difference + expect(result).toEqual(testCase.expected); + } + }); + it("calculateDuration should return duration in hhmm format.", () => { + // Arrange / Act + const startDate = new Date("2023-10-01 10:00:00"); + const endDate = new Date("2023-10-01 23:00:00"); + const duration = calculateDuration(startDate, endDate); + + // Assert + expect(duration).toEqual("1300"); + }); + it("padTo2Digits should return time in 2 digit format.", () => { + // Define some sample time values and their expected strings + const testCases = [ + { time: 0, expected: "00" }, + { time: 1, expected: "01" }, + { time: 9, expected: "09" }, + { time: 10, expected: "10" }, + { time: 59, expected: "59" }, + ]; + // Loop through the test cases and call the function with each time value + for (let testCase of testCases) { + // Call the function and store the result + const result = padTo2Digits(testCase.time); + // Assert that the result is equal to the expected string + expect(result).toEqual(testCase.expected); + } + }); + it("combineDateAndTime should return combined dateTime format.", () => { + // Define some sample inputs and their expected outputs + const testCases = [ + { date: "2023-10-12", time: "12:00", expected: new Date(2023, 9, 12, 12, 0) }, + { date: "2023-01-01", time: "00:00", expected: new Date(2023, 0, 1, 0, 0) }, + { date: "2022-12-31", time: "23:59", expected: new Date(2022, 11, 31, 23, 59) }, + { date: "2023-02-28", time: "13:45", expected: new Date(2023, 1, 28, 13, 45) }, + { date: "2023-03-01", time: "01:23", expected: new Date(2023, 2, 1, 1, 23) }, + ]; + // Loop through the test cases and call the function with each pair of date and time + for (let testCase of testCases) { + // Call the function and store the result + const result = combineDateAndTime(testCase.date, testCase.time); + // Assert that the result is equal to the expected new date object + expect(result.getTime()).toEqual(testCase.expected.getTime()); + } + }); + it("addMinutes should add given time to date.", () => { + // Define some sample inputs and their expected outputs + const testCases = [ + { + date: new Date(2023, 9, 12, 12, 0), + minutes: 15, + expected: new Date(2023, 9, 12, 12, 15), + }, + { + date: new Date(2023, 0, 1, 0, 0), + minutes: -30, + expected: new Date(2022, 11, 31, 23, 30), + }, + { + date: new Date(2022, 11, 31, 23, 59), + minutes: 1, + expected: new Date(2023, 0, 1, 0, 0), + }, + { + date: new Date(2023, 1, 28, 13, 45), + minutes: 60, + expected: new Date(2023, 1, 28, 14, 45), + }, + { + date: new Date(2023, 2, 1, 1, 23), + minutes: -1440, + expected: new Date(2023, 1, 28, 1, 23), + }, + ]; + // Loop through the test cases and call the function with each pair of date and minutes + for (let testCase of testCases) { + // Call the function and store the result + const result = addMinutes(testCase.date, testCase.minutes); + // Assert that the result is equal to the expected modified date object + expect(result.getTime()).toEqual(testCase.expected.getTime()); + } + }); + it("shortTimeString should return time in short format.", () => { + // Define some sample dates and their expected short time strings + const testCases = [ + { date: new Date(2023, 9, 12, 12, 0), expected: "12:00 PM" }, + { date: new Date(2023, 0, 1, 0, 0), expected: "12:00 AM" }, + { date: new Date(2022, 11, 31, 23, 59), expected: "11:59 PM" }, + { date: new Date(2023, 1, 28, 13, 45), expected: "1:45 PM" }, + { date: new Date(2023, 2, 1, 1, 23), expected: "1:23 AM" }, + ]; + // Loop through the test cases and call the function with each date + for (let testCase of testCases) { + // Call the function and store the result + const result = shortTimeString(testCase.date); + // Assert that the result is equal to the expected short time string + expect(result).toEqual(testCase.expected); + } + }); +}); diff --git a/src/layouts/add-to-calendar/add-to-calendar.spec.js b/src/layouts/add-to-calendar/add-to-calendar.spec.js index 1bee62c2c..1e3e5ae4e 100644 --- a/src/layouts/add-to-calendar/add-to-calendar.spec.js +++ b/src/layouts/add-to-calendar/add-to-calendar.spec.js @@ -1,5 +1,7 @@ // Components import addToCalendar from "@/layouts/add-to-calendar/add-to-calendar.vue"; +import { calendarOptions } from "@/constants/calendar-options"; +import { AppointmentTypeStrings, RouteCodeFlags } from "@/constants/schedule-constants"; // Supporting Files import { shallowMount } from "@vue/test-utils"; @@ -75,6 +77,160 @@ describe("Add-to-calendar methods...", () => { //Assert expect(wrapper.vm.$refs.calendarModalQuestion.openModal).toBeCalled(); }); + test("serviceType should return 'replacement and recalibration' when isRepair and funnelHasRecalibrationPart true.", () => { + //Arrange + store.getters.order.damage.isRepair = true; + store.getters.funnelHasRecalibrationPart = true; + const { wrapper } = setupMocks({}); + + //Act + const testValue = wrapper.vm.serviceType(); + + //Assert + expect(testValue).toEqual("replacement and recalibration"); + }); + test("serviceType should return 'replacement' when isRepair is true and funnelHasRecalibrationPart is false.", () => { + //Arrange + store.getters.order.damage.isRepair = true; + store.getters.funnelHasRecalibrationPart = false; + const { wrapper } = setupMocks({}); + + //Act + const testValue = wrapper.vm.serviceType(); + + //Assert + expect(testValue).toEqual("replacement"); + }); + test("serviceType should return 'repair' when isRepair is false.", () => { + //Arrange + store.getters.order.damage.isRepair = false; + const { wrapper } = setupMocks({}); + + //Act + const testValue = wrapper.vm.serviceType(); + + //Assert + expect(testValue).toEqual("repair"); + }); + test("getCalendarData should return expected model value.", () => { + //Arrange + const type = calendarOptions.OUTLOOKCOM; + const { wrapper } = setupMocks({}); + + //Act + const testValue = wrapper.vm.getCalendarData(type); + + //Assert + expect(testValue.name).toEqual(calendarOptions.OUTLOOKCOM); + }); + test("getappointmentData should return expected model value for appointmentType mobile.", () => { + //Arrange + store.getters.order.serviceLocation.appointmentType = AppointmentTypeStrings.MOBILE; + store.getters.order.damage.isRepair = true; + store.getters.funnelHasRecalibrationPart = true; + const { wrapper } = setupMocks({}); + + //Act + const testValue = wrapper.vm.getappointmentData(); + + //Assert + expect(testValue.Subject).toEqual("Safelite replacement and recalibration appointment"); + }); + test("getappointmentData should return expected model value for appointmentType DROP_OFF and OVERNIGHT_DROP_OFF.", () => { + //Arrange + store.getters.order.serviceLocation.appointmentType = AppointmentTypeStrings.DROP_OFF; + store.getters.order.schedule.routeCode = RouteCodeFlags.OVERNIGHT_DROP_OFF; + const { wrapper } = setupMocks({}); + + //Act + const testValue = wrapper.vm.getappointmentData(); + + //Assert + expect(testValue.Subject).toEqual("Drop off your vehicle by 5:30 PM"); + }); + test("getappointmentData should return expected model value for appointmentType DROP_OFF and ALL_DAY_DROP_OFF and isSameDayDropOff.", () => { + //Arrange + store.getters.order.serviceLocation.appointmentType = AppointmentTypeStrings.DROP_OFF; + store.getters.order.schedule.routeCode = RouteCodeFlags.ALL_DAY_DROP_OFF; + store.getters.order.schedule.date = new Date().toISOString().split("T")[0]; + const { wrapper } = setupMocks({}); + + //Act + const testValue = wrapper.vm.getappointmentData(); + + //Assert + expect(testValue.Subject).toEqual("Drop off your vehicle in the next 30 - 60 minutes"); + }); + test("getappointmentData should return expected model value for appointmentType DROP_OFF and ALL_DAY_DROP_OFF.", () => { + //Arrange + store.getters.order.serviceLocation.appointmentType = AppointmentTypeStrings.DROP_OFF; + store.getters.order.schedule.routeCode = RouteCodeFlags.ALL_DAY_DROP_OFF; + const { wrapper } = setupMocks({}); + + //Act + const testValue = wrapper.vm.getappointmentData(); + + //Assert + expect(testValue.Subject).toEqual("Drop off your vehicle between 9:00 AM - 11:00 AM"); + }); + test("getappointmentData should return expected model value for appointmentType DROP_OFF.", () => { + //Arrange + store.getters.order.serviceLocation.appointmentType = AppointmentTypeStrings.DROP_OFF; + store.getters.order.damage.isRepair = false; + const { wrapper } = setupMocks({}); + + //Act + const testValue = wrapper.vm.getappointmentData(); + + //Assert + expect(testValue.Subject).toEqual("Safelite repair appointment"); + }); + test("getappointmentData should return expected model value for appointmentType IN_SHOP.", () => { + //Arrange + store.getters.order.serviceLocation.appointmentType = AppointmentTypeStrings.IN_SHOP; + store.getters.order.damage.isRepair = false; + const { wrapper } = setupMocks({}); + + //Act + const testValue = wrapper.vm.getappointmentData(); + + //Assert + expect(testValue.Subject).toEqual("Safelite repair appointment"); + }); + test("isSameDayDropOff should return true for current schedule date.", () => { + //Arrange + store.getters.order.schedule.date = new Date().toISOString().split("T")[0]; + const { wrapper } = setupMocks({}); + + //Act + const testValue = wrapper.vm.isSameDayDropOff(); + + //Assert + expect(testValue).toEqual(true); + }); + test("setCalendarAppointment should call getAppointment method for iCAL and Outlook.", () => { + //Arrange + const newValue = { name: calendarOptions.OUTLOOK }; + const { wrapper } = setupMocks({}); + wrapper.vm.getAppointment = jest.fn(); + //Act + wrapper.vm.setCalendarAppointment(newValue); + + //Assert + expect(wrapper.vm.getAppointment).toHaveBeenCalled(); + }); + test("setCalendarAppointment should call window.open for Google, yahoo and Outlook.com.", () => { + //Arrange + const newValue = { name: calendarOptions.GOOGLE }; + const { wrapper } = setupMocks({}); + window.open = jest.fn(); + + //Act + wrapper.vm.setCalendarAppointment(newValue); + + //Assert + expect(window.open).toHaveBeenCalledTimes(1); + }); }); function setupMocks({ customMountOptions }) { diff --git a/src/layouts/confirmation/confirmation.spec.js b/src/layouts/confirmation/confirmation.spec.js index c60b19c26..4bef5f8a3 100644 --- a/src/layouts/confirmation/confirmation.spec.js +++ b/src/layouts/confirmation/confirmation.spec.js @@ -1,5 +1,7 @@ // Components import confirmation from "@/layouts/confirmation/confirmation.vue"; +import { AppointmentTypeStrings } from "@/constants/schedule-constants"; +import { applicationConfig } from "@/constants/application-config.js"; // Supporting Files import { shallowMount } from "@vue/test-utils"; @@ -12,14 +14,14 @@ import router from "@/router"; jest.mock("@/helpers/layout-helper.js", () => ({ settleAllPromises: jest.fn(), })); - +var wordingText = "wording Text {custom:ADDRESS}"; beforeEach(() => { jest.restoreAllMocks(); jest.clearAllMocks(); store.getters = { order: { schedule: { - date: "2019-01-01", + date: "2023-01-01", startTime: "09:00", endTime: "10:00", routeCode: "000", @@ -33,9 +35,9 @@ beforeEach(() => { supportingItems: [], }, serviceLocation: { - address: "", - address2: null, - city: "", + address: "test", + address2: "123", + city: "test", state: "AZ", appointmentType: "Inshop", zipCode: "12345", @@ -112,8 +114,188 @@ describe("confirmation.vue", () => { expect(window.location.assign).toHaveBeenCalled(); }); }); +describe("computed properties...", () => { + test("ScheduleDateFormatted should return date in expected format.", () => { + //Arrange + const { wrapper } = setupMocks({}); + + // Act + const testValue = wrapper.vm.ScheduleDateFormatted; + + // Assert + expect(testValue).toEqual("Sunday, January 1"); + }); + test("ScheduleTimeFormatted should return mobile time in expected format.", () => { + //Arrange + store.getters.order.serviceLocation.appointmentType = AppointmentTypeStrings.MOBILE; + store.getters.order.schedule.startTime = "09:00"; + store.getters.order.schedule.endTime = "11:00"; + const { wrapper } = setupMocks({}); + + // Act + const testValue = wrapper.vm.ScheduleTimeFormatted; + + // Assert + expect(testValue).toEqual("Between 9 AM - 11 AM"); + }); + test("ScheduleTimeFormatted should return DROP_OFF time in expected format.", () => { + //Arrange + store.getters.order.serviceLocation.appointmentType = AppointmentTypeStrings.DROP_OFF; + store.getters.order.schedule.startTime = "09:00"; + store.getters.order.schedule.endTime = "11:00"; + const { wrapper } = setupMocks({}); + + // Act + const testValue = wrapper.vm.ScheduleTimeFormatted; + + // Assert + expect(testValue).toEqual("Drop off before 9:30 AM"); + }); + test("ScheduleTimeFormatted should return Inshop time in expected format.", () => { + //Arrange + store.getters.order.serviceLocation.appointmentType = AppointmentTypeStrings.IN_SHOP; + store.getters.order.schedule.startTime = "09:00"; + store.getters.order.schedule.endTime = "11:00"; + const { wrapper } = setupMocks({}); + + // Act + const testValue = wrapper.vm.ScheduleTimeFormatted; + + // Assert + expect(testValue).toEqual("at 9:00 AM"); + }); + test("AppointmentWordingText should return mobile text in expected format.", () => { + //Arrange + store.getters.order.serviceLocation.appointmentType = AppointmentTypeStrings.MOBILE; + + const { wrapper } = setupMocks({}); + // Act + const testValue = wrapper.vm.AppointmentWordingText; + + // Assert + expect(testValue).toEqual("wording Text 123, test,
test, AZ 12345"); + }); + test("AppointmentWordingText should return DROP_OFF text in expected format.", () => { + //Arrange + store.getters.order.serviceLocation.appointmentType = AppointmentTypeStrings.DROP_OFF; + + const { wrapper } = setupMocks({}); + + // Act + const testValue = wrapper.vm.AppointmentWordingText; + + // Assert + expect(testValue).toEqual("wording Text test1,
test, AZ 12345"); + }); + test("AppointmentWordingText should return IN_SHOP text in expected format.", () => { + //Arrange + store.getters.order.serviceLocation.appointmentType = AppointmentTypeStrings.IN_SHOP; + + const { wrapper } = setupMocks({}); + + // Act + const testValue = wrapper.vm.AppointmentWordingText; + + // Assert + expect(testValue).toEqual("wording Text test1,
test, AZ 12345"); + }); + test("ConfirmationEmailText should return text in expected format.", () => { + //Arrange + wordingText = "{custom:MY_ACCOUNT_URL}"; + const { wrapper } = setupMocks({}); + // Act + const testValue = wrapper.vm.ConfirmationEmailText; + + // Assert + expect(testValue).toEqual(applicationConfig.MY_ACCOUNT); + }); + test("ScheduleDate should return date in expected format.", () => { + //Arrange + const { wrapper } = setupMocks({}); + + // Act + const testValue = wrapper.vm.ScheduleDate; + + // Assert + expect(testValue).toEqual("2023-01-01"); + }); + test("ScheduleStartTime should return time in expected format.", () => { + //Arrange + const { wrapper } = setupMocks({}); + + // Act + const testValue = wrapper.vm.ScheduleStartTime; + + // Assert + expect(testValue).toEqual("09:00"); + }); + test("ScheduleEndTime should return time in expected format.", () => { + //Arrange + const { wrapper } = setupMocks({}); + + // Act + const testValue = wrapper.vm.ScheduleEndTime; + + // Assert + expect(testValue).toEqual("10:00"); + }); + test("InShopWordingText should return text in expected format.", () => { + //Arrange + wordingText = "InShopWordingText format"; + const { wrapper } = setupMocks({}); + + // Act + const testValue = wrapper.vm.InShopWordingText; + + // Assert + expect(testValue).toEqual(wordingText); + }); + test("MobileWordingText should return text in expected format.", () => { + //Arrange + wordingText = "MobileWordingText format"; + const { wrapper } = setupMocks({}); + + // Act + const testValue = wrapper.vm.MobileWordingText; + + // Assert + expect(testValue).toEqual(wordingText); + }); + test("DropOffWordingText should return text in expected format.", () => { + //Arrange + wordingText = "DropOffWordingText format"; + const { wrapper } = setupMocks({}); + + // Act + const testValue = wrapper.vm.DropOffWordingText; + + // Assert + expect(testValue).toEqual(wordingText); + }); + test("ServiceLocationFullAddress should return text in expected format.", () => { + //Arrange + + const { wrapper } = setupMocks({}); + + // Act + const testValue = wrapper.vm.ServiceLocationFullAddress; + + // Assert + expect(testValue).toEqual("123, test,
test, AZ 12345"); + }); + test("ProviderFullAddress should return text in expected format.", () => { + //Arrange + + const { wrapper } = setupMocks({}); + + // Act + const testValue = wrapper.vm.ProviderFullAddress; + + // Assert + expect(testValue).toEqual("test1,
test, AZ 12345"); + }); +}); -const wordingText = "wording Text"; function setupMocks({ customMountOptions }) { const mountOptions = getMountOptions({ ...customMountOptions, From edb715425e63f5139ac7c77379dbef878f42e74a Mon Sep 17 00:00:00 2001 From: hiteshkumar87 Date: Thu, 12 Oct 2023 16:42:25 +0530 Subject: [PATCH 13/14] formatting --- src/layouts/confirmation/confirmation.vue | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/layouts/confirmation/confirmation.vue b/src/layouts/confirmation/confirmation.vue index a8e00edb7..7271d0774 100644 --- a/src/layouts/confirmation/confirmation.vue +++ b/src/layouts/confirmation/confirmation.vue @@ -9,7 +9,7 @@
-
+
@@ -24,8 +24,8 @@

{{ this.ScheduleDateFormatted }}

{{ this.ScheduleTimeFormatted }}

- - + +
@@ -37,7 +37,6 @@ isBackButtonHidden="true" @ForwardClicked="forwardButtonAction" :buttonSize="true" /> - From c6916d51fbfba9e216a563518124c0449d4ce084 Mon Sep 17 00:00:00 2001 From: hiteshkumar87 Date: Thu, 12 Oct 2023 16:52:47 +0530 Subject: [PATCH 14/14] Update confirmation.vue getAppointmentWordingText --- src/layouts/confirmation/confirmation.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/layouts/confirmation/confirmation.vue b/src/layouts/confirmation/confirmation.vue index 7271d0774..cb7ca43c1 100644 --- a/src/layouts/confirmation/confirmation.vue +++ b/src/layouts/confirmation/confirmation.vue @@ -12,7 +12,7 @@
+ v-html="this.getAppointmentWordingText">