+
+
+
+
+
\ 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.