Merge pull request #1484 from Safelite/feature/CSR-1382-loose-ends

Feature/csr 1382 loose ends
This commit is contained in:
Leah Schumann 2023-11-02 13:08:33 -04:00 committed by GitHub
commit 4d9ae22071
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 400 additions and 25 deletions

View file

@ -0,0 +1,369 @@
// Components
import cart from "@/fmg-components/cart/cart.vue";
// Supporting Files
import { shallowMount } from "@vue/test-utils";
import { getMountOptions } from "@/helpers/unit-test-helper.js";
describe("cart.vue", () => {
describe("cartItem computeds", () => {
test("if there are one or more front wipers on the order, a front wiper cart item should be added to the cart", () => {
// Arrange
const lineItems = {
glassParts: [],
supportingItems: [],
vaps: [
{
cartItemType: "FRONT WIPERS",
description: 'WIPER BLADE STANDARD 18"',
kitPrice: 0,
laborAmount: 0,
partNumber: "WB18",
partType: "FRONT WIPER",
salesTax: 2.44,
sellingPrice: 32.49,
},
{
cartItemType: "FRONT WIPERS",
description: 'WIPER BLADE STANDARD 26"',
kitPrice: 0,
laborAmount: 0,
partNumber: "WB26",
partType: "FRONT WIPER",
salesTax: 2.44,
sellingPrice: 32.49,
},
],
promos: [],
};
const availableLineItems = [];
// Act
const { wrapper } = setupMocks({
props: {
modelValue: lineItems,
availableLineItems: availableLineItems,
},
});
// Assert
expect(wrapper.vm.frontWipersCartItem).not.toBeNull();
expect(wrapper.vm.frontWipersCartItem.subTotal).toEqual(64.98);
expect(wrapper.vm.frontWipersCartItem.salesTax).toEqual(4.88);
const found =
wrapper.vm.cartItems.findIndex(
(cartItem) => cartItem == wrapper.vm.frontWipersCartItem
) >= 0;
expect(found).toBe(true);
});
test("if there are one or more rear wipers on the order, a rear wiper cart item should be added to the cart", () => {
// Arrange
const lineItems = {
glassParts: [],
supportingItems: [],
vaps: [
{
cartItemType: "REAR WIPERS",
description: 'REAR WIPER BLADE STANDARD 18"',
kitPrice: 0,
laborAmount: 0,
partNumber: "EWB18",
partType: "REAR WIPER",
salesTax: 2.44,
sellingPrice: 32.49,
},
{
cartItemType: "REAR WIPERS",
description: 'REAR WIPER BLADE STANDARD 26"',
kitPrice: 0,
laborAmount: 0,
partNumber: "RWB26",
partType: "REAR WIPER",
salesTax: 2.44,
sellingPrice: 32.49,
},
],
promos: [],
};
const availableLineItems = [];
// Act
const { wrapper } = setupMocks({
props: {
modelValue: lineItems,
availableLineItems: availableLineItems,
},
});
// Assert
expect(wrapper.vm.rearWipersCartItem).not.toBeNull();
expect(wrapper.vm.rearWipersCartItem.subTotal).toEqual(64.98);
expect(wrapper.vm.rearWipersCartItem.salesTax).toEqual(4.88);
const found =
wrapper.vm.cartItems.findIndex(
(cartItem) => cartItem == wrapper.vm.rearWipersCartItem
) >= 0;
expect(found).toBe(true);
});
test("if there is Rain Defense on the order, a rain defense cart item should be added to the cart", () => {
// Arrange
const lineItems = {
glassParts: [],
supportingItems: [],
vaps: [
{
cartItemType: "RAIN DEFENSE",
description: null,
kitPrice: 0,
laborAmount: 0,
listPrice: "44.99",
partNumber: "RAIN DEFENSE",
partType: "RAIN DEFENSE",
salesTax: 3.37,
sellingPrice: 44.99,
},
],
promos: [],
};
const availableLineItems = [];
// Act
const { wrapper } = setupMocks({
props: {
modelValue: lineItems,
availableLineItems: availableLineItems,
},
});
// Assert
expect(wrapper.vm.rainDefenseCartItem).not.toBeNull();
expect(wrapper.vm.rainDefenseCartItem.subTotal).toEqual(44.99);
expect(wrapper.vm.rainDefenseCartItem.salesTax).toEqual(3.37);
const found =
wrapper.vm.cartItems.findIndex(
(cartItem) => cartItem == wrapper.vm.rainDefenseCartItem
) >= 0;
expect(found).toBe(true);
});
// test("if there are glass parts on the order, a glass part cart item should be added to the cart but should not be displayed", () => {
// // Arrange
// const windshieldWithChildParts = {
// canSafeliteRecalibrate: true,
// childParts: [
// {
// kitPrice: 0,
// laborAmount: 23.55,
// partNumber: "GGG FW4896",
// salesTax: 1.77,
// sellingPrice: 0,
// },
// ],
// color: "Green Tint",
// description:
// "solar, soundproofing, lane keep assist, lane departure warning system, w/adaptive cruise control",
// id: "db22fd44-10dd-456f-979b-ff88cf68cca6",
// kitPrice: 0,
// laborAmount: 60,
// partNumber: "FW04896GTYN",
// partType: "WINDSHIELD",
// recalibrationType: "STATIC",
// requiresCapabilityQuestions: false,
// requiresRecalibration: true,
// salesTax: 63.86,
// sellingPrice: 791.46,
// };
// const lineItems = {
// glassParts: [windshieldWithChildParts],
// supportingItems: [],
// vaps: [],
// promos: [],
// };
// const availableLineItems = [];
// // Act
// const { wrapper } = setupMocks({
// props: {
// modelValue: lineItems,
// availableLineItems: availableLineItems,
// },
// });
// // Assert
// expect(wrapper.vm.glassPartCartItems[0]).not.toBeNull();
// expect(wrapper.vm.glassPartCartItems[0].subTotal).toEqual(875.01);
// expect(wrapper.vm.glassPartCartItems[0].salesTax).toEqual(65.63);
// expect(wrapper.vm.glassPartCartItems[0].isDisplayed).toEqual(false);
// const found =
// wrapper.vm.cartItems.findIndex(
// (cartItem) => cartItem == wrapper.vm.glassPartsCartItem
// ) >= 0;
// expect(found).toBe(true);
// });
// Recycle Fee Cart Item
test("if there is recycling fee on the order, a recycle fee cart item should be added to the cart", () => {
// Arrange
const lineItems = {
glassParts: [],
supportingItems: [
{
description: null,
id: "9ff98501-03a4-432b-884e-e4e28f884a2f",
kitPrice: 0,
laborAmount: 34.98,
partNumber: "RECYCLE FEE",
partType: "REPLACE FEE",
salesTax: 2.62,
sellingPrice: 0,
},
],
vaps: [],
promos: [],
};
const availableLineItems = [];
// Act
const { wrapper } = setupMocks({
props: {
modelValue: lineItems,
availableLineItems: availableLineItems,
},
});
// Assert
expect(wrapper.vm.recycleFeeCartItem).not.toBeNull();
expect(wrapper.vm.recycleFeeCartItem.subTotal).toEqual(34.98);
expect(wrapper.vm.recycleFeeCartItem.salesTax).toEqual(2.62);
const found =
wrapper.vm.cartItems.findIndex(
(cartItem) => cartItem == wrapper.vm.recycleFeeCartItem
) >= 0;
expect(found).toBe(true);
});
// Mobile Fee Cart Item
test("if there is mobile fee on the order, a mobile fee cart item should be added to the cart", () => {
// Arrange
const lineItems = {
glassParts: [],
supportingItems: [
{
description: null,
id: "9ff98501-03a4-432b-884e-e4e28f884a2f",
kitPrice: 0,
laborAmount: 34.98,
partNumber: "MOBILE FEE",
partType: "REPLACE FEE",
salesTax: 2.62,
sellingPrice: 0,
},
],
vaps: [],
promos: [],
};
const availableLineItems = [];
// Act
const { wrapper } = setupMocks({
props: {
modelValue: lineItems,
availableLineItems: availableLineItems,
},
});
// Assert
expect(wrapper.vm.mobileFeeCartItem).not.toBeNull();
expect(wrapper.vm.mobileFeeCartItem.subTotal).toEqual(34.98);
expect(wrapper.vm.mobileFeeCartItem.salesTax).toEqual(2.62);
const found =
wrapper.vm.cartItems.findIndex(
(cartItem) => cartItem == wrapper.vm.mobileFeeCartItem
) >= 0;
expect(found).toBe(true);
});
// Other Supporting Items Cart Item
test("if there are other supporting items on the order, an other supporting items cart item should be added to the cart but should not be displayed", () => {
// Arrange
const recalibrationLineItem = {
cartItemType: "SUPPORTING ITEMS",
description: null,
id: "8ab6ef9b-8620-430a-8a92-29f7d4bb8b00",
kitPrice: 0,
laborAmount: 450,
partNumber: "RECAL STATIC",
partType: "RECALIBRATION",
salesTax: 33.75,
sellingPrice: 0,
};
const lineItems = {
glassParts: [],
supportingItems: [recalibrationLineItem],
vaps: [],
promos: [],
};
const availableLineItems = [];
// Act
const { wrapper } = setupMocks({
props: {
modelValue: lineItems,
availableLineItems: availableLineItems,
},
});
// Assert
expect(wrapper.vm.otherSupportingItemsCartItem).not.toBeNull();
expect(wrapper.vm.otherSupportingItemsCartItem.subTotal).toEqual(450);
expect(wrapper.vm.otherSupportingItemsCartItem.salesTax).toEqual(33.75);
expect(wrapper.vm.otherSupportingItemsCartItem.isDisplayed).toEqual(false);
const found =
wrapper.vm.cartItems.findIndex(
(cartItem) => cartItem == wrapper.vm.otherSupportingItemsCartItem
) >= 0;
expect(found).toBe(true);
});
});
});
function setupMocks({ options, props }) {
const mountOptions = getMountOptions({
...options,
});
const mockBaseMixin = {
methods: {
getTierOnePackagePrice: jest.fn(),
filterOutFees: jest.fn(),
getCmsContent: jest.fn(),
},
};
if (props) mountOptions.propsData = props;
mountOptions.global.mixins = [mockBaseMixin];
const wrapper = shallowMount(cart, mountOptions);
return { wrapper };
}

View file

@ -73,13 +73,18 @@
</template>
<script>
import baseMixin from "@/mixins/base-mixin.js";
import { getHighestFullySatisfiedTier, getPackageContents } from "@/helpers/service-package-helper";
// Components
import textLink from "@/ux-components/text-link/text-link";
import textBlock from "@/digital-components/text-block/text-block";
import { LineItemUtilities } from "@/store";
// Mixins
import baseMixin from "@/mixins/base-mixin.js";
// Helpers
import { deepClone } from "@/helpers/object-helper";
import { getHighestFullySatisfiedTier, getPackageContents } from "@/helpers/service-package-helper";
// Constants
import { partTypeStrings } from "@/constants/part-type-strings";
import { cartItemCategories } from "@/constants/cart-item-categories";
import { cartItemTypes } from "@/constants/cart-item-types";
@ -101,7 +106,6 @@ export default {
currency: "USD",
}),
lineItems: deepClone(this.modelValue),
lineItemUtilities: new LineItemUtilities(),
};
},
methods: {
@ -114,7 +118,7 @@ export default {
let subTotal = 0;
vapsItems.forEach((item) => {
subTotal += item.subTotal; // baseMixin.methods.getTotalLineItemPrice(item);
subTotal += item.subTotal;
});
return subTotal;
@ -175,8 +179,10 @@ export default {
cartItems.push(this.rainDefenseCartItem);
}
if (this.glassPartsCartItem) {
cartItems.push(this.glassPartsCartItem);
if (this.glassPartCartItems) {
this.glassPartCartItems.forEach((glassPartCartItem) => {
cartItems.push(glassPartCartItem);
});
}
if (this.otherSupportingItemsCartItem) {
@ -264,6 +270,10 @@ export default {
return this.damage.glassToReplace;
},
glassParts() {
const glassParts = this.lineItems?.glassParts;
return glassParts?.length > 0 ? glassParts : [];
},
vaps() {
const vaps = this.lineItems?.vaps;
return vaps?.length > 0 ? vaps : [];
@ -403,12 +413,12 @@ export default {
return cartItem;
},
glassPartsCartItem() {
let cartItem = null;
glassPartCartItems() {
const glassPartCartItems = [];
if (this.lineItems?.glassParts?.length > 0) {
this.lineItems?.glassParts?.forEach((lineItem) => {
cartItem = {
if (this.glassParts.length > 0) {
this.glassParts.forEach((lineItem) => {
let glassPartCartItem = {
name: null,
category: cartItemCategories.GLASS_PARTS,
cartItemType: cartItemTypes.GLASS_PARTS,
@ -417,24 +427,27 @@ export default {
(lineItem.kitPrice ?? 0) +
(lineItem.laborAmount ?? 0) +
(lineItem.sellingPrice ?? 0),
salesTax: lineItem.salesTax ?? 0,
lineItems: [],
};
if (lineItem.childParts?.length > 0) {
lineItem.childParts.forEach((childPartLineItem) => {
cartItem.subTotal +=
glassPartCartItem.subTotal +=
(childPartLineItem.kitPrice ?? 0) +
(childPartLineItem.laborAmount ?? 0) +
(childPartLineItem.sellingPrice ?? 0);
cartItem.salesTax += childPartLineItem.salesTax;
glassPartCartItem.salesTax += childPartLineItem.salesTax;
});
}
glassPartCartItems.push(glassPartCartItem);
});
}
return cartItem;
return glassPartCartItems;
},
recycleFeeCartItemName() {
return this.getCmsContent("RecycleFeeTextWidget", "Text");

View file

@ -174,6 +174,8 @@ export default {
false
);
// Match all available line items to the line items as they are in the store
// and rebuild the original structure.
const lineItems = mapTaxedAvailableLineItemsToStoreFormat(
availableLineItems,
lineItemsFromStore

View file

@ -2598,15 +2598,6 @@ function addTaxesToPricedLineItems(pricedLineItems, taxingLineItems = []) {
return pricedLineItems;
}
export class LineItemUtilities {
constructor() {
this.getArrayOfAllLineItems = getArrayOfAllLineItems;
this.getFlattenedArrayOfLineItemsWithChildParts =
getFlattenedArrayOfLineItemsWithChildParts;
this.addGuidToLineItemsIfNotAlreadyThere = addGuidToLineItemsIfNotAlreadyThere;
}
}
export function mapTaxedAvailableLineItemsToStoreFormat(availableLineItems, storeLineItems) {
// clone the lineItems array because what we're passing in is referencing the store directly
const lineItems = deepClone(storeLineItems);