diff --git a/.eslintrc.js b/.eslintrc.js index 33b69824..7187e7e5 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -45,7 +45,8 @@ module.exports = { }], 'import/extensions': ['error', 'always', { js: 'ignorePackages' }], 'no-param-reassign': ['error', { props: true, ignorePropertyModificationsFor: ['item'] }], - 'no-restricted-syntax': ['off', 'ForOfStatement'] + 'no-restricted-syntax': ['off', 'ForOfStatement'], + 'no-return-await': 'off' }, settings: { 'import/resolver': { diff --git a/src/constants/application-config.js b/src/constants/application-config.js index f6797d9d..c445bcf4 100644 --- a/src/constants/application-config.js +++ b/src/constants/application-config.js @@ -19,8 +19,6 @@ const applicationConfig = Object.freeze({ SAFELITE_HOP: process.env.VUE_APP_SAFELITE_HOP, ISS_DEV_CMS_DOMAIN: 'https://digitalisscms.dev.safelite.io', CASH_PARENT_ACCOUNT_NUMBER: 167132, - ITAC_FAIR_AND_REASONABLE_BILLTO: '214616', // Lib Mutal FnR BillTo - ITAC_CASH_BILLTO: '283393', // Lib Mutal ITAC Cash BillTo GOOGLE_CALENDAR: 'https://www.google.com/calendar/render?action=TEMPLATE', YAHOO_CALENDAR: 'https://calendar.yahoo.com/?v=60', OUTLOOK_CALENDAR: diff --git a/src/constants/cart-item-type.js b/src/constants/cart-item-type.js new file mode 100644 index 00000000..b3e1ecf4 --- /dev/null +++ b/src/constants/cart-item-type.js @@ -0,0 +1,7 @@ +const cartItemType = Object.freeze({ + RECYCLE_FEE: 'RECYCLE FEE', + MOBILE_FEE: 'MOBILE FEE', + VAP: 'VAP' +}); + +export default cartItemType; diff --git a/src/constants/endpoints.js b/src/constants/endpoints.js index 770048e3..2466392d 100644 --- a/src/constants/endpoints.js +++ b/src/constants/endpoints.js @@ -1,15 +1,15 @@ -const CONTENT_BASE_URL = '/content/api/v1/content'; -const LOCATION_BASE_URL = '/location/api/v1/location'; -const SCHEDULE_BASE_URL = '/schedule/api/v1/schedule'; -const PARTS_BASE_URL = '/parts/api/v1/parts'; -const VEHICLE_BASE_URL = '/vehicle/api/v1/vehicle'; -const PRICE_BASE_URL = '/price/api/v1/price'; const ACCOUNT_BASE_URL = '/account/api/v1/account'; -const EXPERIMENTS_BASE_URL = '/experiments/api/v1/experiments'; const ANALYTICS_BASE_URL = '/analytics/api/v1/analytics'; const CLIENT_AUTH_BASE_URL = '/clientauth/api/v1/clientauth'; +const CONTENT_BASE_URL = '/content/api/v1/content'; const COVERAGE_BASE_URL = '/coverage/api/v1/coverage'; +const EXPERIMENTS_BASE_URL = '/experiments/api/v1/experiments'; +const LOCATION_BASE_URL = '/location/api/v1/location'; const ORDER_BASE_URL = '/order/api/v1/order'; +const PARTS_BASE_URL = '/parts/api/v1/parts'; +const PRICE_BASE_URL = '/price/api/v1/price'; +const SCHEDULE_BASE_URL = '/schedule/api/v1/schedule'; +const VEHICLE_BASE_URL = '/vehicle/api/v1/vehicle'; const endpoints = Object.freeze({ GetRouteInfo: { @@ -24,6 +24,10 @@ const endpoints = Object.freeze({ url: (applicationAbbreviation, pageName) => `${CONTENT_BASE_URL}/${applicationAbbreviation}/${pageName}`, method: 'GET' }, + GetBillToInfo: { + url: `${ACCOUNT_BASE_URL}/bill-to-info`, + method: 'POST' + }, GetAlertReasons: { url: `${LOCATION_BASE_URL}/alert-reasons`, method: 'GET' diff --git a/src/constants/part-number-strings.js b/src/constants/part-number-strings.js index 85e3398d..a72684f8 100644 --- a/src/constants/part-number-strings.js +++ b/src/constants/part-number-strings.js @@ -1,4 +1,4 @@ -const partNumberStrings = Object.freeze({ +const partNumberStrings = Object.freeze({ RECYCLE_FEE: 'RECYCLE FEE' }); diff --git a/src/iss-components/cart-dropdown/__snapshots__/cart-dropdown.spec.js.snap b/src/iss-components/cart-dropdown/__snapshots__/cart-dropdown.spec.js.snap index 5baa028a..9ade738d 100644 --- a/src/iss-components/cart-dropdown/__snapshots__/cart-dropdown.spec.js.snap +++ b/src/iss-components/cart-dropdown/__snapshots__/cart-dropdown.spec.js.snap @@ -2,6 +2,11 @@ exports[`cart-dropdown component initial data rendered as expected 1`] = ` Object { + "cartItemType": Object { + "MOBILE_FEE": "MOBILE FEE", + "RECYCLE_FEE": "RECYCLE FEE", + "VAP": "VAP", + }, "isExpanded": false, "widget": Object { "amountDue": "AmountDueTextWidget", diff --git a/src/iss-components/cart-dropdown/cart-dropdown.spec.js b/src/iss-components/cart-dropdown/cart-dropdown.spec.js index 5d69dec5..69be39a8 100644 --- a/src/iss-components/cart-dropdown/cart-dropdown.spec.js +++ b/src/iss-components/cart-dropdown/cart-dropdown.spec.js @@ -46,6 +46,14 @@ function getMountedComponent(mainInitialState = {}, initialData = {}, propsData return { wrapper }; } +beforeEach(() => { + const store = useMainStore(); + const defaultState = getDefaultState(); + Object.keys(defaultState).forEach((key) => { + store[key] = defaultState[key]; + }); +}); + describe('cart-dropdown component', () => { test('initial data rendered as expected', () => { // Arrange @@ -142,6 +150,42 @@ describe('cart-dropdown component', () => { // Assert expect(servicePackage.exists()).toBeTruthy(); }); + test('non service package cart items', () => { + // Arrange + const reference = '#non-packaged-cart-items'; + const isExpanded = true; + const initialData = { isExpanded }; + const { wrapper } = getMountedComponent({}, {}, initialData); + + // Act + const nonPackagedCartItems = wrapper.find(reference); + + // Assert + expect(nonPackagedCartItems.exists()).toBeTruthy(); + }); + test('recycle cart item label when isRecycle true for some cart item', () => { + // Arrange + const reference = '#recycle-fee-label'; + const isExpanded = true; + const initialPropsData = { isExpanded }; + const storeData = { + order: { + lineItems: { + supportingItems: [ + { partNumber: partNumberStrings.RECYCLE_FEE } + ] + } + } + }; + const { wrapper } = getMountedComponent(storeData, {}, initialPropsData); + + // Act + const recycleFeeLabel = wrapper.find(reference); + + // Assert + expect(wrapper.vm.recycleFeeCartItem).not.toBeNull(); + expect(recycleFeeLabel.exists()).toBeTruthy(); + }); test('cart footer', () => { // Arrange const reference = '#cart-footer'; @@ -181,45 +225,6 @@ describe('cart-dropdown component', () => { // Assert expect(salesTax.exists()).toBeTruthy(); }); - test('recycle fee when preset in supported items', () => { - // Arrange - const reference = '#recycle-fee-item'; - const { wrapper } = getMountedComponent({ - order: { - lineItems: { - supportingItems: [{ - partNumber: partNumberStrings.RECYCLE_FEE - }] - } - } - }); - - // Act - const recycleFee = wrapper.find(reference); - - // Assert - expect(wrapper.vm.recycleFeeCartItem).not.toBeNull(); - expect(recycleFee.exists()).toBeTruthy(); - }); - test('mobile fee when preset', () => { - // Arrange - const reference = '#mobile-fee-item'; - const { wrapper } = getMountedComponent({ - order: { - lineItems: { - supportingItems: [], - mobileFee: {} - } - } - }); - - // Act - const mobileFee = wrapper.find(reference); - - // Assert - expect(wrapper.vm.mobileFeeCartItem).not.toBeNull(); - expect(mobileFee.exists()).toBeTruthy(); - }); test('bottom amount due', () => { // Arrange const reference = '#bottom-amount-due'; @@ -285,42 +290,26 @@ describe('cart-dropdown component', () => { // Assert expect(cartBasePrice.exists()).toBeFalsy(); }); - test('recycle fee when not in supported items', () => { + test('recycle cart item label when isRecycle false for all cart items', () => { // Arrange - const reference = '#recycle-fee-item'; - const { wrapper } = getMountedComponent({ + const reference = '#recycle-fee-label'; + const isExpanded = true; + const initialPropsData = { isExpanded }; + const storeData = { order: { lineItems: { supportingItems: [] } } - }); + }; + const { wrapper } = getMountedComponent(storeData, {}, initialPropsData); // Act - const recycleFee = wrapper.find(reference); + const recycleFeeLabel = wrapper.find(reference); // Assert expect(wrapper.vm.recycleFeeCartItem).toBeNull(); - expect(recycleFee.exists()).toBeFalsy(); - }); - test('mobile fee when not in supported items', () => { - // Arrange - const reference = '#mobile-fee-item'; - const { wrapper } = getMountedComponent({ - order: { - lineItems: { - supportingItems: [], - mobileFee: null - } - } - }); - - // Act - const mobileFee = wrapper.find(reference); - - // Assert - expect(wrapper.vm.mobileFeeCartItem).toBeNull(); - expect(mobileFee.exists()).toBeFalsy(); + expect(recycleFeeLabel.exists()).toBeFalsy(); }); }); describe('computed', () => { @@ -452,16 +441,129 @@ describe('cart-dropdown component', () => { }); }); describe('subTotal', () => { - // TODO when method implemented + const supportingItemPartNumber = 'supporter'; + const glassPartNumber = 'glass part'; + const otherPartNumber = 'other'; + const vapPartNumber = 'vap'; + const mobileFeePartNumber = 'mobile'; + const supportingItems = [{ partNumber: supportingItemPartNumber }]; + const glassParts = [{ partNumber: glassPartNumber }]; + const otherParts = [{ partNumber: otherPartNumber }]; + const vaps = [{ partNumber: vapPartNumber }]; + const mobileFee = { partNumber: mobileFeePartNumber }; + const includesPartNumber = (lineItems, partNumber) => lineItems.some((item) => item?.partNumber === partNumber); + const deductiblePrice = 100; + const supportingAndGlassAndOtherPrice = 30; + const feesAndVapsPrice = 50; + const allLineItemsPrice = 70; + getPriceOfLineItems.mockImplementation((lineItems) => { + const allPartNumbers = [ + supportingItemPartNumber, + glassPartNumber, + otherPartNumber, + mobileFeePartNumber, + vapPartNumber + ]; + if (allPartNumbers.every((partNumber) => includesPartNumber(lineItems, partNumber))) { + return allLineItemsPrice; + } + const supportingAndGlassAndOtherPartNumbers = [ + supportingItemPartNumber, + glassPartNumber, + otherPartNumber + ]; + if (supportingAndGlassAndOtherPartNumbers.every((partNumber) => includesPartNumber(lineItems, partNumber))) { + return supportingAndGlassAndOtherPrice; + } + if (includesPartNumber(lineItems, mobileFeePartNumber) && includesPartNumber(lineItems, vapPartNumber)) { + return feesAndVapsPrice; + } + return 1; + }); + const lineItems = { + supportingItems, + glassParts, + otherParts, + vaps, + mobileFee + }; + test('when not no comp and not itac, includes deductible price plus fee and vap price', () => { + // Arrange + const storeData = { + order: { + lineItems, + policy: { + noCoverage: false, + isITAC: false + }, + currentDeductible: deductiblePrice + } + }; + const { wrapper } = getMountedComponent(storeData); + const expected = deductiblePrice + feesAndVapsPrice; + + // Act + const result = wrapper.vm.subTotal; + + // Assert + expect(result).toBe(expected); + }); + test('when no comp, returns all line items price', () => { + // Arrange + const storeData = { + order: { + lineItems, + policy: { + noCoverage: true, + isITAC: false + }, + currentDeductible: deductiblePrice + } + }; + const { wrapper } = getMountedComponent(storeData); + const expected = allLineItemsPrice; + + // Act + const result = wrapper.vm.subTotal; + + // Assert + expect(result).toBe(expected); + }); + test('when ITAC, returns all line items price', () => { + // Arrange + const storeData = { + order: { + lineItems, + policy: { + noCoverage: false, + isITAC: true + }, + currentDeductible: deductiblePrice + } + }; + const { wrapper } = getMountedComponent(storeData); + const expected = allLineItemsPrice; + + // Act + const result = wrapper.vm.subTotal; + + // Assert + expect(result).toBe(expected); + }); }); describe('salesTax', () => { test('salesTax is treated as 0 when line items are null', () => { // Arrange - const storeData = getDefaultState(); - storeData.order.lineItems.glassParts = null; - storeData.order.lineItems.otherParts = null; - storeData.order.lineItems.supportingItems = null; - storeData.order.lineItems.vaps = null; + const storeData = { + order: { + lineItems: { + glassParts: null, + otherParts: null, + supportingItems: null, + vaps: null + } + } + }; const { wrapper } = getMountedComponent(storeData); @@ -474,20 +576,24 @@ describe('cart-dropdown component', () => { test("salesTax is treated as 0 when null or undefined is set as the line item's sales tax.", () => { // Arrange - const storeData = getDefaultState(); - storeData.order.lineItems.glassParts = [ - { partType: 'mock', salesTax: null }, - { partType: 'mock', salesTax: undefined } - ]; - storeData.order.lineItems.supportingItems = [ - { partType: 'mock', salesTax: null }, - { partType: 'mock', salesTax: undefined } - ]; - storeData.order.lineItems.vaps = [ - { partType: 'mock', salesTax: null }, - { partType: 'mock', salesTax: undefined } - ]; - + const storeData = { + order: { + lineItems: { + glassParts: [ + { partType: 'mock', salesTax: null }, + { partType: 'mock', salesTax: undefined } + ], + supportingItems: [ + { partType: 'mock', salesTax: null }, + { partType: 'mock', salesTax: undefined } + ], + vaps: [ + { partType: 'mock', salesTax: null }, + { partType: 'mock', salesTax: undefined } + ] + } + } + }; const { wrapper } = getMountedComponent(storeData); // Act @@ -496,19 +602,29 @@ describe('cart-dropdown component', () => { // Assert expect(result).toBe(0); }); - test('Returns 0 as sales tax when coverage is unverified and order does not contain vaps.', () => { // Arrange - const storeData = getDefaultState(); - storeData.order.lineItems.glassParts = [ - { partType: 'mock', salesTax: 10 } - ]; - storeData.order.lineItems.supportingItems = [ - { partNumber: partNumberStrings.RECYCLE_FEE, partType: 'mock', salesTax: 10, sellingPrice: 39.99 } - ]; - storeData.order.lineItems.vaps = []; - storeData.order.payment.insuranceCoverage.isVerified = false; - + const storeData = { + order: { + lineItems: { + glassParts: [{ partType: 'mock', salesTax: 10 }], + supportingItems: [ + { + partNumber: partNumberStrings.RECYCLE_FEE, + partType: 'mock', + salesTax: 10, + sellingPrice: 39.99 + } + ], + vaps: [] + }, + payment: { + insuranceCoverage: { + isVerified: false + } + } + } + }; const { wrapper } = getMountedComponent(storeData); // Act @@ -520,7 +636,7 @@ describe('cart-dropdown component', () => { test('Returns sum of vaps sales tax when coverage is unverified and order has vaps.', () => { // Arrange - const storeData = getDefaultState(); + const storeData = useMainStore().$state; storeData.order.lineItems.glassParts = [ { partType: 'mock', salesTax: 10 } ]; @@ -541,7 +657,7 @@ describe('cart-dropdown component', () => { test('Returns Recycle Fee tax when coverage is Verified-Deductible, replace service, and no vaps.', () => { // Arrange - const storeData = getDefaultState(); + const storeData = useMainStore().$state; storeData.order.currentDeductible = 250; storeData.order.lineItems.glassParts = [ { partType: 'mock', salesTax: 10, sellingPrice: 150 } @@ -563,7 +679,7 @@ describe('cart-dropdown component', () => { test('Returns sum of vaps + recycle fee sales tax when Verified-Deductible, replace service, and has vaps.', () => { // Arrange - const storeData = getDefaultState(); + const storeData = useMainStore().$state; storeData.order.currentDeductible = 250; storeData.order.lineItems.glassParts = [ { partType: 'mock', salesTax: 10, sellingPrice: 100 } @@ -592,7 +708,7 @@ describe('cart-dropdown component', () => { test('Returns sum of sales tax when Verified-ITAC.', () => { // Arrange - const storeData = getDefaultState(); + const storeData = useMainStore().$state; storeData.order.currentDeductible = 0; storeData.order.lineItems.glassParts = [ { partType: 'mock', salesTax: 10, sellingPrice: 100 } @@ -620,7 +736,7 @@ describe('cart-dropdown component', () => { test('Returns sum of sales tax when Verified-NoComp.', () => { // Arrange - const storeData = getDefaultState(); + const storeData = useMainStore().$state; storeData.order.currentDeductible = 0; storeData.order.lineItems.glassParts = [ { partType: 'mock', salesTax: 10, sellingPrice: 100 } @@ -647,18 +763,10 @@ describe('cart-dropdown component', () => { }); }); describe('availableLineItems', () => { - test('returns expected when glassParts null', async () => { + test('returns expected when all null', () => { // Arrange - const storeData = { - order: { - lineItems: { - glassParts: null, - supportingItems: [] - } - } - }; - const propsData = { availableVaps: [] }; - const { wrapper } = getMountedComponent(storeData, {}, propsData); + const propsData = { availableVaps: null }; + const { wrapper } = getMountedComponent({}, {}, propsData); // Act const result = wrapper.vm.availableLineItems; @@ -666,58 +774,106 @@ describe('cart-dropdown component', () => { // Assert expect(result).toStrictEqual([]); }); - test('returns expected when supportingItems null', async () => { + test('returns expected when glassParts not null', async () => { // Arrange + const glassParts = [{ partNumber: 'glass' }]; const storeData = { order: { - lineItems: { - glassParts: [], - supportingItems: null - } + lineItems: { glassParts } } }; - const propsData = { availableVaps: [] }; + const propsData = { availableVaps: null }; const { wrapper } = getMountedComponent(storeData, {}, propsData); + const expected = glassParts; // Act const result = wrapper.vm.availableLineItems; // Assert - expect(result).toStrictEqual([]); + expect(result).toStrictEqual(expected); + }); + test('returns expected when supportingItems not null', async () => { + // Arrange + const supportingItems = [{ partNumber: 'support' }]; + const storeData = { + order: { + lineItems: { supportingItems } + } + }; + const propsData = { availableVaps: null }; + const { wrapper } = getMountedComponent(storeData, {}, propsData); + const expected = supportingItems; + + // Act + const result = wrapper.vm.availableLineItems; + + // Assert + expect(result).toStrictEqual(expected); + }); + test('returns expected when otherParts not null', () => { + // Arrange + const otherParts = [{ partNumber: 'other' }]; + const storeData = { + order: { + lineItems: { otherParts } + } + }; + const propsData = { availableVaps: null }; + const { wrapper } = getMountedComponent(storeData, {}, propsData); + const expected = otherParts; + + // Act + const result = wrapper.vm.availableLineItems; + + // Assert + expect(result).toStrictEqual(expected); + }); + test('returns expected when mobileFee not null', () => { + // Arrange + const mobileFee = { partNumber: 'mobile' }; + const storeData = { + order: { + lineItems: { mobileFee } + } + }; + const propsData = { availableVaps: null }; + const { wrapper } = getMountedComponent(storeData, {}, propsData); + const expected = [mobileFee]; + + // Act + const result = wrapper.vm.availableLineItems; + + // Assert + expect(result).toStrictEqual(expected); }); test('returns expected when availableVaps null', async () => { // Arrange - const storeData = { - order: { - lineItems: { - glassParts: [], - supportingItems: [] - } - } - }; - const propsData = { - availableVaps: undefined - }; - const { wrapper } = getMountedComponent(storeData, {}, propsData); + const availableVaps = [{ partNumber: 'vaps1' }]; + const propsData = { availableVaps }; + const { wrapper } = getMountedComponent({}, {}, propsData); + const expected = availableVaps; // Act const result = wrapper.vm.availableLineItems; // Assert - expect(result).toStrictEqual([]); + expect(result).toStrictEqual(expected); }); - test('returns expected when glassParts, supportingItems, and availableVaps have content', async () => { + test('returns expected when glassParts, supportingItems, otherParts, mobileFee, and availableVaps have content', async () => { // Arrange const part1 = { partType: 'apple' }; const part2 = { partType: 'banana' }; const part3 = { partType: 'orange' }; const part4 = { partType: 'grape' }; const part5 = { partType: 'raspberry' }; + const mobileFee = { partType: 'mobile' }; const storeData = { order: { lineItems: { glassParts: [part1], - supportingItems: [part2, part3] + supportingItems: [part2, part3], + otherParts: [], + mobileFee } } }; @@ -725,13 +881,24 @@ describe('cart-dropdown component', () => { availableVaps: [part4, part5] }; const { wrapper } = getMountedComponent(storeData, {}, propsData); - const expected = [part1, part2, part3, part4, part5]; + const sortByPartType = (a, b) => { + const typeA = a.partType.toUpperCase(); + const typeB = b.partType.toUpperCase(); + if (typeA < typeB) { + return -1; + } + if (typeA > typeB) { + return 1; + } + return 0; + }; + const expected = [part1, part2, part3, part4, part5, mobileFee]; // Act const result = wrapper.vm.availableLineItems; // Assert - expect(result).toStrictEqual(expected); + expect(result.sort(sortByPartType)).toStrictEqual(expected.sort(sortByPartType)); }); }); describe('servicePackageCartItems', () => { @@ -959,6 +1126,267 @@ describe('cart-dropdown component', () => { })); }); }); + describe('nonServicePackageCartItems', () => { + test('when recycleFee, includes recycle fee', () => { + // Arrange + const storeData = { + order: { + lineItems: { + supportingItems: [ + { + partNumber: partNumberStrings.RECYCLE_FEE + } + ] + } + } + }; + const { wrapper } = getMountedComponent(storeData); + + // Act + const result = wrapper.vm.nonServicePackageCartItems; + + // Assert + expect(result).toContainEqual(expect.objectContaining({ + partType: partTypeStrings.REPLACE_FEE + })); + }); + test('when mobileFee, includes mobile fee', () => { + // Arrange + const storeData = { + order: { + lineItems: { + mobileFee: { + partType: partTypeStrings.MOBILE_FEE + } + } + } + }; + const { wrapper } = getMountedComponent(storeData); + + // Act + const result = wrapper.vm.nonServicePackageCartItems; + + // Assert + expect(result).toContainEqual(expect.objectContaining({ + partType: partTypeStrings.MOBILE_FEE + })); + }); + test('front wiper not included when front wiper in service package', () => { + // Arrange + getPriceOfLineItems.mockImplementation(() => 123); + getPackageContents.mockImplementationOnce(() => [partTypeStrings.FRONT_WIPER]); + const storeData = { + order: { + lineItems: { + vaps: [ + { + partType: partTypeStrings.FRONT_WIPER + } + ] + } + } + }; + const { wrapper } = getMountedComponent(storeData); + + // Act + const result = wrapper.vm.nonServicePackageCartItems; + + // Assert + expect(result).not.toContainEqual(expect.objectContaining({ + partType: partTypeStrings.FRONT_WIPER + })); + }); + test('rear wiper not included when rear wiper in service package', () => { + // Arrange + getPriceOfLineItems.mockImplementation(() => 123); + getPackageContents.mockImplementationOnce(() => [partTypeStrings.REAR_WIPER]); + const storeData = { + order: { + lineItems: { + vaps: [ + { + partType: partTypeStrings.REAR_WIPER + } + ] + } + } + }; + const { wrapper } = getMountedComponent(storeData); + + // Act + const result = wrapper.vm.nonServicePackageCartItems; + + // Assert + expect(result).not.toContainEqual(expect.objectContaining({ + partType: partTypeStrings.REAR_WIPER + })); + }); + test('rain defense not included when rain defense in service package', () => { + // Arrange + getPriceOfLineItems.mockImplementation(() => 123); + getPackageContents.mockImplementationOnce(() => [partTypeStrings.RAIN_DEFENSE]); + const storeData = { + order: { + lineItems: { + vaps: [ + { + partType: partTypeStrings.RAIN_DEFENSE + } + ] + } + } + }; + const { wrapper } = getMountedComponent(storeData); + + // Act + const result = wrapper.vm.nonServicePackageCartItems; + + // Assert + expect(result).not.toContainEqual(expect.objectContaining({ + partType: partTypeStrings.RAIN_DEFENSE + })); + }); + test('front wiper included when not in service package and in vaps', () => { + // Arrange + getPriceOfLineItems.mockImplementation(() => 123); + getPackageContents.mockImplementationOnce(() => []); + const storeData = { + order: { + lineItems: { + vaps: [ + { + partType: partTypeStrings.FRONT_WIPER + } + ] + } + } + }; + const { wrapper } = getMountedComponent(storeData); + + // Act + const result = wrapper.vm.nonServicePackageCartItems; + + // Assert + expect(result).toContainEqual(expect.objectContaining({ + partType: partTypeStrings.FRONT_WIPER + })); + }); + test('rear wiper included when not in service package and in vaps', () => { + // Arrange + getPriceOfLineItems.mockImplementation(() => 123); + getPackageContents.mockImplementationOnce(() => []); + const storeData = { + order: { + lineItems: { + vaps: [ + { + partType: partTypeStrings.REAR_WIPER + } + ] + } + } + }; + const { wrapper } = getMountedComponent(storeData); + + // Act + const result = wrapper.vm.nonServicePackageCartItems; + + // Assert + expect(result).toContainEqual(expect.objectContaining({ + partType: partTypeStrings.REAR_WIPER + })); + }); + test('rain defense included when not in service package and in vaps', () => { + // Arrange + getPriceOfLineItems.mockImplementation(() => 123); + getPackageContents.mockImplementationOnce(() => []); + const storeData = { + order: { + lineItems: { + vaps: [ + { + partType: partTypeStrings.RAIN_DEFENSE + } + ] + } + } + }; + const { wrapper } = getMountedComponent(storeData); + + // Act + const result = wrapper.vm.nonServicePackageCartItems; + + // Assert + expect(result).toContainEqual(expect.objectContaining({ + partType: partTypeStrings.RAIN_DEFENSE + })); + }); + test('item in vaps outside front wiper, rear wiper, and rain defense never included', () => { + // Arrange + getPriceOfLineItems.mockImplementation(() => 123); + getPackageContents.mockImplementationOnce(() => []); + const storeData = { + order: { + lineItems: { + vaps: [ + { + partType: partTypeStrings.RECALIBRATION + } + ] + } + } + }; + const { wrapper } = getMountedComponent(storeData); + + // Act + const result = wrapper.vm.nonServicePackageCartItems; + + // Assert + expect(result).not.toContainEqual(expect.objectContaining({ + partType: partTypeStrings.RECALIBRATION + })); + }); + test('returns expected when multiple variables apply', () => { + // Arrange + getPriceOfLineItems.mockImplementation(() => 123); + getPackageContents.mockImplementationOnce(() => [partTypeStrings.FRONT_WIPER]); + const storeData = { + order: { + lineItems: { + supportingItems: [ + { + partNumber: partNumberStrings.RECYCLE_FEE + } + ], + mobileFee: { + partType: partTypeStrings.MOBILE_FEE + }, + vaps: [ + { partType: partTypeStrings.RECALIBRATION }, + { partType: partTypeStrings.FRONT_WIPER }, + { partType: partTypeStrings.REAR_WIPER } + ] + } + } + }; + const { wrapper } = getMountedComponent(storeData); + + // Act + const result = wrapper.vm.nonServicePackageCartItems; + + // Assert + expect(result.length).toBe(3); + expect(result).toContainEqual(expect.objectContaining({ + partType: partTypeStrings.REPLACE_FEE + })); + expect(result).toContainEqual(expect.objectContaining({ + partType: partTypeStrings.MOBILE_FEE + })); + expect(result).toContainEqual(expect.objectContaining({ + partType: partTypeStrings.REAR_WIPER + })); + }); + }); describe('servicePackageLabelWidget', () => { test.each([ [null], @@ -1181,15 +1609,105 @@ describe('cart-dropdown component', () => { expect(result.name).toBe(expectedName); }); }); + describe('packagePrice', () => { + test('returns 0 when servicePackageCartItems is empty', () => { + // Arrange + getPriceOfLineItems.mockImplementation(() => 123); + getPackageContents.mockImplementationOnce(() => []); + const storeData = { + order: { + lineItems: { + vaps: [ + { + partType: partTypeStrings.REAR_WIPER + } + ] + } + } + }; + const { wrapper } = getMountedComponent(storeData); + + // Act + const result = wrapper.vm.packagePrice; + + // Assert + expect(result).toBe(0); + }); + test('returns expected when servicePackageCartItems not empty', () => { + // Arrange + const rearPrice = 101; + const frontPrice = 9; + getPriceOfLineItems.mockImplementation((lineItems) => { + if (lineItems.some((item) => item?.partType === partTypeStrings.FRONT_WIPER)) { + return frontPrice; + } + if (lineItems.some((item) => item?.partType === partTypeStrings.REAR_WIPER)) { + return rearPrice; + } + return 1; + }); + getPackageContents.mockImplementationOnce(() => [partTypeStrings.FRONT_WIPER, partTypeStrings.REAR_WIPER]); + const storeData = { + order: { + lineItems: { + vaps: [ + { partType: partTypeStrings.FRONT_WIPER }, + { partType: partTypeStrings.REAR_WIPER } + ] + } + } + }; + const { wrapper } = getMountedComponent(storeData); + const expectedPrice = 110; + + // Act + const result = wrapper.vm.packagePrice; + + // Assert + expect(result).toBe(expectedPrice); + }); + test('returns expected when servicePackageCartItems has one item', () => { + // Arrange + const frontPrice = 9; + getPriceOfLineItems.mockImplementation((lineItems) => { + if (lineItems.some((item) => item?.partType === partTypeStrings.FRONT_WIPER)) { + return frontPrice; + } + return 1; + }); + getPackageContents.mockImplementationOnce(() => [partTypeStrings.FRONT_WIPER, partTypeStrings.REAR_WIPER]); + const storeData = { + order: { + lineItems: { + vaps: [ + { partType: partTypeStrings.FRONT_WIPER } + ] + } + } + }; + const { wrapper } = getMountedComponent(storeData); + + // Act + const result = wrapper.vm.packagePrice; + + // Assert + expect(result).toBe(frontPrice); + }); + }); }); describe('method', () => { describe('getDisplayed', () => { test('returns "Verifying coverage" when not no comp, not itac, and deductible null', () => { // Arrange - const storeData = getDefaultState(); - storeData.order.payment.insuranceCoverage.coverageStatus = coverageStatuses.VERIFIED; - storeData.order.policy.isITAC = false; - storeData.order.currentDeductible = null; + const storeData = { + order: { + policy: { + noCoverage: false, + isITAC: false + }, + currentDeductible: null + } + }; const { wrapper } = getMountedComponent(storeData); const amount = 123; @@ -1202,10 +1720,15 @@ describe('cart-dropdown component', () => { }); test('returns "Verifying coverage" when not no comp, not itac, and coverage status PENDING', () => { // Arrange - const storeData = getDefaultState(); - storeData.order.payment.insuranceCoverage.coverageStatus = coverageStatuses.PENDING; - storeData.order.policy.isITAC = false; - storeData.order.currentDeductible = 321; + const storeData = { + order: { + policy: { + noCoverage: false, + isITAC: false + }, + currentDeductible: 321 + } + }; const { wrapper } = getMountedComponent(storeData); const amount = 123; @@ -1521,8 +2044,5 @@ describe('cart-dropdown component', () => { expect(result.subTotal).toBe(expectedSubtotal); }); }); - describe('removeItem', () => { - // TODO when written - }); }); }); diff --git a/src/iss-components/cart-dropdown/cart-dropdown.vue b/src/iss-components/cart-dropdown/cart-dropdown.vue index 54031ec5..c1d148bd 100644 --- a/src/iss-components/cart-dropdown/cart-dropdown.vue +++ b/src/iss-components/cart-dropdown/cart-dropdown.vue @@ -1,5 +1,5 @@