diff --git a/azure-pipelines.yml b/azure-pipelines.yml
index 9f677925..8c04697c 100644
--- a/azure-pipelines.yml
+++ b/azure-pipelines.yml
@@ -342,6 +342,7 @@ stages:
- template: templates/digital/auto-tag.yml@AzureDevOps
parameters:
dependsOn: prodBuildDeployment
+ environment: digitalCloud-prod
userName: SafeliteAzureDevops
userEmail: githubazuredevops@safelite.com
diff --git a/src/helpers/recal-helper.js b/src/helpers/recal-helper.js
index c6cce97e..f364bf68 100644
--- a/src/helpers/recal-helper.js
+++ b/src/helpers/recal-helper.js
@@ -73,3 +73,22 @@ export function containsRecalParts(lineItems) {
}
}
+export function anyPartWithRequiresRecalFlag(lineItems) {
+ if (!lineItems) {
+ return false;
+ }
+
+ if (Array.isArray(lineItems)) {
+ return lineItems.some((li) => li.requiresRecalibration === true);
+ }
+
+ const flattened = [
+ ...(lineItems.glassParts ?? []),
+ ...(lineItems.supportingItems ?? []),
+ ...(lineItems.otherParts ?? []),
+ ...(lineItems.feeItems ?? []),
+ ...(lineItems.vaps ?? []),
+ ];
+
+ return flattened.some((li) => li.requiresRecalibration === true);
+}
diff --git a/src/iss-components/recal-modal/recal-modal.vue b/src/iss-components/recal-modal/recal-modal.vue
index 2564c9cf..9563d561 100644
--- a/src/iss-components/recal-modal/recal-modal.vue
+++ b/src/iss-components/recal-modal/recal-modal.vue
@@ -30,6 +30,7 @@ export default {
default: 'RecalModal'
}
},
+ emits: ['recal-modal-opened'],
computed: {
modalBodyText() {
return this.getCmsContent(this.cmsWidgetName, 'BodyText').replaceAll('{custom:vehicleMake}', this.vehicleMake);
@@ -50,6 +51,7 @@ export default {
methods: {
openModal() {
this.$refs.recalModal.openModal();
+ this.$emit('recal-modal-opened');
},
footerButtonClick() {
this.$refs.recalModal.closeModal();
diff --git a/src/layouts/coverage-statement/coverage-statement.spec.js b/src/layouts/coverage-statement/coverage-statement.spec.js
index dcccbed6..5c056587 100644
--- a/src/layouts/coverage-statement/coverage-statement.spec.js
+++ b/src/layouts/coverage-statement/coverage-statement.spec.js
@@ -14,6 +14,7 @@ import { useMainStore } from '@/store';
import { getPriceOfLineItems } from '@/helpers/price-calculator.js';
import coverageStatuses from '@/constants/coverage-statuses';
import coverageType from '@/constants/coverage-type';
+import { containsRecalParts } from '@/helpers/recal-helper';
jest.mock('@/helpers/layout-helper.js', () => jest.fn());
jest.mock('@/helpers/price-calculator.js', () => ({
@@ -36,8 +37,6 @@ jest.mock('@/helpers/recal-helper.js', () => ({
containsRecalParts: jest.fn()
}));
-import { containsRecalParts } from '@/helpers/recal-helper.js';
-
const SAFELITE_PROVIDER = 'Safelite';
const CANCEL_CLAIM_REF_NAME = 'CancelClaimModal';
@@ -115,7 +114,8 @@ describe('coverageStatement.vue', () => {
const mainInitialState = {
order: {
damage: {
- isRepair: true
+ isRepair: true,
+ numberOfChips: 2
},
insuranceCoverage: {
coverageStatus: coverageStatuses.PENDING,
@@ -343,7 +343,7 @@ describe('coverageStatement.vue', () => {
// Assert
expect(result).toBeFalsy();
});
- test('returns true when a part in glassParts require recalibration and recalibration has been added to order', () => {
+ test('returns true when a part in glassParts requires recalibration and recalibration has been added to order', () => {
// Arrange
containsRecalParts.mockReturnValue(true);
const mainInitialState = {
@@ -614,7 +614,8 @@ describe('coverageStatement.vue', () => {
const mainInitialState = {
order: {
damage: {
- isRepair: true
+ isRepair: true,
+ numberOfChips: 2
},
insuranceCoverage: {
coverageStatus: coverageStatuses.PENDING,
@@ -644,7 +645,8 @@ describe('coverageStatement.vue', () => {
const mainInitialState = {
order: {
damage: {
- isRepair: true
+ isRepair: true,
+ numberOfChips: 2
},
insuranceCoverage: {
coverageStatus: coverageStatuses.VERIFIED,
@@ -673,7 +675,8 @@ describe('coverageStatement.vue', () => {
const mainInitialState = {
order: {
damage: {
- isRepair: true
+ isRepair: true,
+ numberOfChips: 2
},
insuranceCoverage: {
coverageStatus: coverageStatuses.VERIFIED,
@@ -705,7 +708,8 @@ describe('coverageStatement.vue', () => {
const mainInitialState = {
order: {
damage: {
- isRepair: true
+ isRepair: true,
+ numberOfChips: 2
},
insuranceCoverage: {
coverageStatus: coverageStatuses.VERIFIED,
@@ -732,7 +736,8 @@ describe('coverageStatement.vue', () => {
const mainInitialState = {
order: {
damage: {
- isRepair: true
+ isRepair: true,
+ numberOfChips: 2
},
insuranceCoverage: {
coverageStatus: coverageStatuses.VERIFIED,
@@ -763,7 +768,8 @@ describe('coverageStatement.vue', () => {
const mainInitialState = {
order: {
damage: {
- isRepair: true
+ isRepair: true,
+ numberOfChips: 2
},
insuranceCoverage: {
coverageStatus: coverageStatuses.VERIFIED,
diff --git a/src/layouts/coverage-statement/coverage-statement.vue b/src/layouts/coverage-statement/coverage-statement.vue
index 0e2dadc4..88a0ecb7 100644
--- a/src/layouts/coverage-statement/coverage-statement.vue
+++ b/src/layouts/coverage-statement/coverage-statement.vue
@@ -103,14 +103,16 @@
+ cmsWidgetName="RecalModal"
+ @recalModalOpened="handleRecalModalOpened" />
+ cmsWidgetName="OEMEndorsementModal"
+ @oemEndorsementModalOpened="handleOemEndorsementModalOpened" />
@@ -377,6 +379,71 @@ export default {
setupModalLinks(this);
},
methods: {
+ logEventsToGA() {
+ const clientName = this.mainStore.accountNameForEvents;
+
+ if (this.isDeductibleVisible || this.isITACQuoteVisible) {
+ this.pushEventToGA('coverage', 'Yes', clientName, true, null, '1');
+ if (this.isITACQuoteVisible) {
+ this.pushEventToGA('Coverage', 'ITAC', `${this.deductibleValue}_${this.totalServicePrice}`, true);
+ }
+ else {
+ this.pushEventToGA('coverage', 'full_comprehensive', clientName, true);
+ this.pushEventToGA('coverage', 'deductible', this.deductibleValue.toString(), true);
+ }
+ }
+ else {
+ this.pushEventToGA('coverage', 'No', clientName, true, null, '0');
+ if (this.isNoCompQuoteVisible) {
+ this.pushEventToGA('coverage', 'no_comprehensive', `${this.totalServicePrice}`, true);
+ this.pushEventToGA('coverage', 'deductible', '9999', true);
+ }
+ else {
+ this.pushEventToGA('coverage', 'deductible', '7777', true);
+ }
+ }
+
+ if (this.mainStore.hasOemEndorsement) {
+ this.pushEventToGA('oem_endorsement', 'displayed', 'Yes', true, null, '1');
+ }
+ else {
+ this.pushEventToGA('oem_endorsement', 'displayed', 'No', true, null, '0');
+ }
+
+ if (this.isADAS) {
+ if (this.mainStore.vehicle.canSafeliteService) {
+ this.pushEventToGA('ADAS_self_service', 'ADAS_servicable_status', 'yes', true);
+ if (this.mainStore.hasRecalibrationPart) {
+ if (this.isDeductibleVisible) {
+ const recalibrationType = this.mainStore.lineItems.glassParts.find((part) => part.requiresRecalibration).recalibrationType;
+ if (this.deductibleValue <= 0) {
+ this.pushEventToGA('recalibration_opportunity_verified_0_deductible', this.mainStore.vehicle.carId, `recal_type_${recalibrationType}`, true);
+ }
+ else {
+ this.pushEventToGA('recalibration_opportunity_verified_over_0_deductible', this.mainStore.vehicle.carId, `recal_type_${recalibrationType}`, true);
+ }
+ }
+ else {
+ let appointmentType = '';
+ if (this.isITACQuoteVisible) {
+ appointmentType = 'ITAC';
+ }
+ else if (this.isNoCompQuoteVisible) {
+ appointmentType = 'no_comp';
+ }
+ else {
+ appointmentType = 'unverified';
+ }
+ this.pushEventToGA(`recalibration_opportunity_${appointmentType}`, this.mainStore.vehicle.carId, `recal_type_${this.mainStore.lineItems.glassParts.find((part) => part.requiresRecalibration).recalibrationType}`, true);
+ }
+ }
+ }
+ else {
+ this.pushEventToGA('ADAS_self_service', 'ADAS_servicable_status', 'no', true);
+ }
+ }
+ },
+
arePagePrerequisitesValid() {
return !!useMainStore().vehicle.carId;
},
@@ -396,6 +463,8 @@ export default {
}
}
+ this.logEventsToGA();
+
showIssLoadingModal(false);
this.isPageLoading = false;
},
@@ -414,7 +483,13 @@ export default {
},
async navigateForward() {
showIssLoadingModal(true);
- if (this.isUnverifiedVisible || this.isDeductibleVisible) {
+ if (this.isUnverifiedVisible || this.isDeductibleVisible) {
+ if (this.isRepair) {
+ this.pushEventToGA('RepairDamage', 'Repair', this.mainStore.damage.numberOfChips.toString(), true, null, '1');
+ }
+ else {
+ this.pushEventToGA('RepairDamage', 'Replacement', 'Replacement', true, null, '1');
+ }
this.navigateWithScenario(navigationScenarios.CLICKED_FORWARD);
} else if (this.isITACQuoteVisible || this.isNoCompQuoteVisible) {
this.mainStore.updateIsSafeliteProvider(true);
@@ -478,7 +553,13 @@ export default {
cancelClaim() {
this.mainStore.updateIsSafeliteProvider(false);
this.$router.navigateBailout(bailoutMessage.CoverageCancelledByUser());
- }
+ },
+ handleRecalModalOpened() {
+ this.pushEventToGA('recalibration_modal', 'learn_more_clicked', this.mainStore.vehicle.carId, true);
+ },
+ handleOemEndorsementModalOpened() {
+ this.pushEventToGA('oem_endorsement_modal', 'clicked', 'Yes', true, null, '1');
+ },
}
};
diff --git a/src/layouts/coverage-statement/oem-endorsement-modal/oem-endorsement-modal.vue b/src/layouts/coverage-statement/oem-endorsement-modal/oem-endorsement-modal.vue
index 85ee726b..6daff222 100644
--- a/src/layouts/coverage-statement/oem-endorsement-modal/oem-endorsement-modal.vue
+++ b/src/layouts/coverage-statement/oem-endorsement-modal/oem-endorsement-modal.vue
@@ -26,6 +26,7 @@ export default {
default: 'OEMEndorsementModal'
}
},
+ emits: ['oem-endorsement-modal-opened'],
computed: {
modalBodyText() {
return this.getCmsContent(this.cmsWidgetName, 'BodyText');
@@ -40,6 +41,7 @@ export default {
methods: {
openModal() {
this.$refs.oemEndorsementModal.openModal();
+ this.$emit('oem-endorsement-modal-opened');
},
footerButtonClick() {
this.$refs.oemEndorsementModal.closeModal();
diff --git a/src/layouts/order-confirmation/order-confirmation.vue b/src/layouts/order-confirmation/order-confirmation.vue
index d533f276..21ce4fc7 100644
--- a/src/layouts/order-confirmation/order-confirmation.vue
+++ b/src/layouts/order-confirmation/order-confirmation.vue
@@ -193,7 +193,7 @@ export default {
appointmentType: serviceLocation.appointmentType,
providerAddress: serviceLocation?.provider?.address,
customerPortalLoginToken,
- carrierName: issConfig.clientName,
+ carrierName: issConfig.clientDisplayName,
carrierUrl: issConfig.successReturnURL,
isRepair: damage.isRepair,
hasRecalibrationPart,
diff --git a/src/layouts/provider-preference/tpa-recal-modal/tpa-recal-toggle/tpa-recal-toggle.vue b/src/layouts/provider-preference/tpa-recal-modal/tpa-recal-toggle/tpa-recal-toggle.vue
index 53b2d204..2c64ba69 100644
--- a/src/layouts/provider-preference/tpa-recal-modal/tpa-recal-toggle/tpa-recal-toggle.vue
+++ b/src/layouts/provider-preference/tpa-recal-modal/tpa-recal-toggle/tpa-recal-toggle.vue
@@ -6,11 +6,12 @@
linkType="text"
href="#!"
:text="textForToggle"
- @click="handleClickToggle" />
-
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/layouts/schedule-page/recal-ack-modal/recal-ack-toggle/recal-ack-toggle.vue b/src/layouts/schedule-page/recal-ack-modal/recal-ack-toggle/recal-ack-toggle.vue
new file mode 100644
index 00000000..c4525695
--- /dev/null
+++ b/src/layouts/schedule-page/recal-ack-modal/recal-ack-toggle/recal-ack-toggle.vue
@@ -0,0 +1,115 @@
+
+
+
+
+
diff --git a/src/layouts/schedule-page/schedule-page.spec.js b/src/layouts/schedule-page/schedule-page.spec.js
index a418f35e..fefde032 100644
--- a/src/layouts/schedule-page/schedule-page.spec.js
+++ b/src/layouts/schedule-page/schedule-page.spec.js
@@ -12,6 +12,8 @@ jest.mock('@/helpers/cms-content-helper', () => ({
fetchCmsContentForPage: jest.fn()
}));
+const RECAL_ACK_MODAL_REF_NAME = 'recalAckModal';
+
const mockMixin = {
methods: {
getCmsContent: jest.fn().mockImplementation(() => ''),
@@ -55,6 +57,22 @@ const loadingModalStub = {
}
};
+const serviceLocationStub = {
+ render: () => {},
+ methods: {
+ forwardButtonAction: jest.fn(),
+ initializeComponent: jest.fn()
+ }
+};
+
+const recalAckModalStub = {
+ template: '
',
+ methods: {
+ openModal: jest.fn(),
+ footerButtonClick: jest.fn()
+ }
+};
+
function getShallowMountedComponent(initialData = {}, methodToRun = () => {}) {
const mountOptions = getMountOptions({
router: {
@@ -64,7 +82,9 @@ function getShallowMountedComponent(initialData = {}, methodToRun = () => {}) {
mountOptions.global.stubs = {
siteFooter: footerStub,
- loadingModal: loadingModalStub
+ loadingModal: loadingModalStub,
+ serviceLocation: serviceLocationStub,
+ recalAckModal: recalAckModalStub
};
methodToRun();
@@ -75,6 +95,27 @@ function getShallowMountedComponent(initialData = {}, methodToRun = () => {}) {
);
const wrapper = shallowMount(schedule, mountOptions);
+
+ // Manually create the ref for recalAckModal since shallowMount doesn't populate it
+ Object.defineProperty(wrapper.vm.$refs, RECAL_ACK_MODAL_REF_NAME, {
+ value: {
+ openModal: jest.fn(),
+ footerButtonClick: jest.fn()
+ },
+ writable: false,
+ configurable: true
+ });
+
+ // Manually create the ref for serviceLocation since shallowMount doesn't populate it
+ Object.defineProperty(wrapper.vm.$refs, 'serviceLocation', {
+ value: {
+ forwardButtonAction: jest.fn(),
+ initializeComponent: jest.fn()
+ },
+ writable: false,
+ configurable: true
+ });
+
return { wrapper };
}
@@ -126,152 +167,163 @@ afterEach(() => {
jest.clearAllMocks();
});
-describe('schedule-page.vue', () => {
- beforeAll(() => {
- Object.defineProperty(window, 'matchMedia', { value: jest.fn().mockImplementation((query) => ({
- matches: false,
- media: query,
- onchange: null,
- addEventListener: jest.fn(),
- removeEventListener: jest.fn(),
- dispatchEvent: jest.fn()
- })),
- writable: true });
+beforeAll(() => {
+ Object.defineProperty(window, 'matchMedia', { value: jest.fn().mockImplementation((query) => ({
+ matches: false,
+ media: query,
+ onchange: null,
+ addEventListener: jest.fn(),
+ removeEventListener: jest.fn(),
+ dispatchEvent: jest.fn()
+ })),
+ writable: true });
+});
+describe('Initial Load', () => {
+ test('Should pass arePagePrerequisitesValid with a serviceLocation zipcode [in beforeEach]', () => {
+ // Arrange
+ const { wrapper } = getShallowMountedComponent();
+
+ // Act
+ const arePagePrerequisitesValid = wrapper.vm.arePagePrerequisitesValid();
+
+ // Assert
+ expect(arePagePrerequisitesValid).toBe(true);
});
- describe('Initial Load', () => {
- test('Should pass arePagePrerequisitesValid with a serviceLocation zipcode [in beforeEach]', () => {
- // Arrange
- const { wrapper } = getShallowMountedComponent();
+ test('Should fail arePagePrerequisitesValid with no serviceLocation zipcode', () => {
+ // Arrange
+ const { wrapper } = getShallowMountedComponent();
+ wrapper.vm.mainStore.order.serviceLocation.zipCode = null;
- // Act
- const arePagePrerequisitesValid = wrapper.vm.arePagePrerequisitesValid();
+ // Act
+ const arePagePrerequisitesValid = wrapper.vm.arePagePrerequisitesValid();
- // Assert
- expect(arePagePrerequisitesValid).toBe(true);
- });
- test('Should fail arePagePrerequisitesValid with no serviceLocation zipcode', () => {
- // Arrange
- const { wrapper } = getShallowMountedComponent();
- wrapper.vm.mainStore.order.serviceLocation.zipCode = null;
-
- // Act
- const arePagePrerequisitesValid = wrapper.vm.arePagePrerequisitesValid();
-
- // Assert
- expect(arePagePrerequisitesValid).toBeFalsy();
- });
- test('should return newShopTimeSlots when getAvailableDatesMethod is called', async () => {
- // Arrange
- const { wrapper } = getShallowMountedComponent();
- wrapper.vm.selectableDatesData = {
- days: []
- };
- const store = useMainStore();
- store.getShopTimeSlots.mockImplementation(() => Promise.resolve({
- data: {
- estimatedServiceMinutesMinimum: 90,
- estimatedServiceMinutesMaximum: 120,
- days: [
- {
- date: '2023-12-01',
- timeSlots: [
- {
- id: '06747-01820-S-B*20424*7 AM',
- startTime: '07:00',
- endTime: '08:00',
- offerPremium: false
- }
- ]
- }
- ]
- }
- }));
-
- // Act
- const newShopTimeSlots = await wrapper.vm.getAvailableDatesMethod(
- '2023-01-01',
- '2023-01-15'
- );
-
- // Assert
- expect(newShopTimeSlots).toStrictEqual({
+ // Assert
+ expect(arePagePrerequisitesValid).toBeFalsy();
+ });
+ test('should return newShopTimeSlots when getAvailableDatesMethod is called', async () => {
+ // Arrange
+ const { wrapper } = getShallowMountedComponent();
+ wrapper.vm.selectableDatesData = {
+ days: []
+ };
+ const store = useMainStore();
+ store.getShopTimeSlots.mockImplementation(() => Promise.resolve({
+ data: {
+ estimatedServiceMinutesMinimum: 90,
+ estimatedServiceMinutesMaximum: 120,
days: [
{
date: '2023-12-01',
timeSlots: [
{
- endTime: '08:00',
id: '06747-01820-S-B*20424*7 AM',
- offerPremium: false,
- startTime: '07:00'
+ startTime: '07:00',
+ endTime: '08:00',
+ offerPremium: false
}
]
}
- ],
- estimatedServiceMinutesMinimum: 90,
- estimatedServiceMinutesMaximum: 120
- });
- });
- test('Should call API service in days of 15 or less when getAvailableDatesMethod is called with large date ranges', async () => {
- // Arrange
- const { wrapper } = getShallowMountedComponent();
- wrapper.vm.selectableDatesData = {
- days: []
- };
- const store = useMainStore();
- store.getShopTimeSlots.mockImplementation(() => Promise.resolve({
- data: {
- estimatedServiceMinutesMinimum: 90,
- estimatedServiceMinutesMaximum: 120,
- days: []
+ ]
+ }
+ }));
+
+ // Act
+ const newShopTimeSlots = await wrapper.vm.getAvailableDatesMethod(
+ '2023-01-01',
+ '2023-01-15'
+ );
+
+ // Assert
+ expect(newShopTimeSlots).toStrictEqual({
+ days: [
+ {
+ date: '2023-12-01',
+ timeSlots: [
+ {
+ endTime: '08:00',
+ id: '06747-01820-S-B*20424*7 AM',
+ offerPremium: false,
+ startTime: '07:00'
+ }
+ ]
}
- }));
-
- // Act
- await wrapper.vm.getAvailableDates.call(
- wrapper.vm,
- '2023-01-01',
- '2023-03-31',
- 'Inshop',
- '123',
- '43228'
- );
-
- // Assert
- // 2023-01-01 --> 2023-02-05
- // 2023-02-06 --> 2023-03-12
- // 2023-03-13 --> 2023-03-31
- expect(store.getShopTimeSlots).toHaveBeenCalledTimes(6);
+ ],
+ estimatedServiceMinutesMinimum: 90,
+ estimatedServiceMinutesMaximum: 120
});
});
- describe('Rendering', () => {
- test('Schedule page loads', () => {
- // Arrange
- const { wrapper } = getShallowMountedComponent();
-
- // Assert
- expect(wrapper).toBeTruthy();
- });
- });
- describe('schedule page methods...', () => {
- test('getServiceZipCtuCodeFromStore should return zipCodeCtu', () => {
- // Arrange
- const { wrapper } = getShallowMountedComponent();
- wrapper.vm.selectableDatesData = {
+ test('Should call API service in days of 15 or less when getAvailableDatesMethod is called with large date ranges', async () => {
+ // Arrange
+ const { wrapper } = getShallowMountedComponent();
+ wrapper.vm.selectableDatesData = {
+ days: []
+ };
+ const store = useMainStore();
+ store.getShopTimeSlots.mockImplementation(() => Promise.resolve({
+ data: {
+ estimatedServiceMinutesMinimum: 90,
+ estimatedServiceMinutesMaximum: 120,
days: []
- };
+ }
+ }));
- // Act
- const testValue = wrapper.vm.getServiceZipCtuCodeFromStore();
+ // Act
+ await wrapper.vm.getAvailableDates.call(
+ wrapper.vm,
+ '2023-01-01',
+ '2023-03-31',
+ 'Inshop',
+ '123',
+ '43228'
+ );
- // Assert
- expect(testValue).toStrictEqual('01234');
- });
+ // Assert
+ // 2023-01-01 --> 2023-02-05
+ // 2023-02-06 --> 2023-03-12
+ // 2023-03-13 --> 2023-03-31
+ expect(store.getShopTimeSlots).toHaveBeenCalledTimes(6);
});
- test.skip('forwardButtonAction should call route method navigateWithoutSaving', async () => {
+});
+describe('Rendering', () => {
+ test('Schedule page loads', () => {
+ // Arrange
+ const { wrapper } = getShallowMountedComponent();
+
+ // Assert
+ expect(wrapper).toBeTruthy();
+ });
+});
+describe('schedule page methods...', () => {
+ test('getServiceZipCtuCodeFromStore should return zipCodeCtu', () => {
+ // Arrange
+ const { wrapper } = getShallowMountedComponent();
+ wrapper.vm.selectableDatesData = {
+ days: []
+ };
+
+ // Act
+ const testValue = wrapper.vm.getServiceZipCtuCodeFromStore();
+
+ // Assert
+ expect(testValue).toStrictEqual('01234');
+ });
+ test('forwardButtonAction should call route method navigate', async () => {
// Arrange
const { wrapper } = getShallowMountedComponent();
wrapper.vm.$router.navigate = jest.fn(() => ({}));
+ wrapper.vm.mainStore.saveSchedule = jest.fn();
+ wrapper.vm.navigationScenarios = {
+ CLICKED_FORWARD: 'CLICKED_FORWARD'
+ };
+
+ // Mock the serviceLocation ref using Object.defineProperty to bypass readonly
+ Object.defineProperty(wrapper.vm.$refs, 'serviceLocation', {
+ value: {
+ forwardButtonAction: jest.fn()
+ },
+ configurable: true
+ });
+
wrapper.vm.selectedTimeSlotInfo = {
timeSlot: {
routeCode: 'test-id'
@@ -281,6 +333,59 @@ describe('schedule-page.vue', () => {
// Act
await wrapper.vm.forwardButtonAction();
+ // Assert
+ expect(wrapper.vm.$router.navigate).toHaveBeenCalled();
+ });
+ test('recalAckModal should open when part requires recal, but recal not added to order', async () => {
+ // Arrange
+ const { wrapper } = getShallowMountedComponent();
+ wrapper.vm.mainStore.lineItems.glassParts = [
+ {
+ partNumber: 'TEST123',
+ requiresRecalibration: true
+ }
+ ];
+
+ // Act
+ await wrapper.vm.forwardButtonAction();
+
+ // Assert
+ expect(wrapper.vm.$refs[RECAL_ACK_MODAL_REF_NAME].openModal).toHaveBeenCalled();
+ });
+ test('navigation forward is blocked when recalAckModal is displayed, but checkbox has not been acknowledged', async () => {
+ // Arrange
+ const { wrapper } = getShallowMountedComponent();
+ wrapper.vm.mainStore.lineItems.glassParts = [
+ {
+ partNumber: 'TEST123',
+ requiresRecalibration: true
+ }
+ ];
+
+ // Act
+ await wrapper.vm.forwardButtonAction();
+ await wrapper.vm.$refs[RECAL_ACK_MODAL_REF_NAME].openModal();
+ await wrapper.vm.$refs[RECAL_ACK_MODAL_REF_NAME].footerButtonClick();
+
+ // Assert
+ expect(wrapper.vm.$router.navigate).not.toHaveBeenCalled();
+ });
+ test('navigation forward is allowed when checkbox has been acknowledged on recalAckModal', async () => {
+ // Arrange
+ const { wrapper } = getShallowMountedComponent();
+ wrapper.vm.mainStore.lineItems.glassParts = [
+ {
+ partNumber: 'TEST123',
+ requiresRecalibration: true
+ }
+ ];
+
+ // Act
+ await wrapper.vm.$refs[RECAL_ACK_MODAL_REF_NAME].openModal();
+ wrapper.vm.updateIsRecalAcknowledged(true);
+ await wrapper.vm.$refs[RECAL_ACK_MODAL_REF_NAME].footerButtonClick();
+ await wrapper.vm.forwardButtonAction();
+
// Assert
expect(wrapper.vm.$router.navigate).toHaveBeenCalled();
});
diff --git a/src/layouts/schedule-page/schedule-page.vue b/src/layouts/schedule-page/schedule-page.vue
index 2e65643b..904329f3 100644
--- a/src/layouts/schedule-page/schedule-page.vue
+++ b/src/layouts/schedule-page/schedule-page.vue
@@ -64,6 +64,11 @@
:ref="SUGGEST_TIMESLOT_MODAL_REF_NAME"
cmsWidgetName="SuggestTimeslotModal"
@confirmAppointmentClicked="autoSelectTimeslotConfirmed" />
+
diff --git a/src/store/index.js b/src/store/index.js
index 12dc746f..2aa8248a 100644
--- a/src/store/index.js
+++ b/src/store/index.js
@@ -225,7 +225,8 @@ export const getDefaultState = () => ({
loadedFromDupeCheck: null,
loadedSessionClearedPreviousData: null,
availableVaps: null,
- visitedDuplicateCheckPage: false
+ visitedDuplicateCheckPage: false,
+ isRecalAcknowledgedForScheduling: false
},
applicationUser: {
experiments: [],
@@ -3163,7 +3164,7 @@ export const useMainStore = defineStore({
resetSubmittedOrder() {
// clear from sessionStorage
window.sessionStorage.removeItem(webStorageConstants.SUBMITTED_ORDER);
- }
+ },
},
persist: {
// Default is localStorage, but sessionStorage is used here to ensure state is cleared when the browser tab is closed, preventing potential issues with stale data on return visits.