import * as promotionsHelper from "@/helpers/promotions-helper";
import { partTypeStrings } from "@/constants/part-type-strings";
import store from "@/store";
import { storeActions } from "@/constants/store-actions";
import baseMixin from "@/mixins/base-mixin.js";

let mockReturnsForStoreActions = {};

jest.mock("@/mixins/base-mixin", () => ({
    ...jest.requireActual("@/mixins/base-mixin"),
    methods: {
        dispatchStoreAction: jest.fn((action, { activePromos, inactivePromos }, someBool) => {}),
        dispatchStoreActionWithLogging: jest.fn(
            (action, { promoCode, addableVaps }, pageNameToLog, someBool) => {
                return mockReturnsForStoreActions[action];
            }
        ),
    },
}));

afterEach(() => {
    // reset store after each test
    store.getters.lineItems.promos = null;
    store.getters.payment.inactivePromos = null;
    // reset store action returns
    mockReturnsForStoreActions = {};
    // reset mock method call counters
    baseMixin.methods.dispatchStoreAction.mockClear();
    baseMixin.methods.dispatchStoreActionWithLogging.mockClear();
});

describe("promotions-helper.js", () => {
    describe("expected return tests", () => {
        describe("getPromosWithAddableVaps", () => {
            it("returns an empty array when promoList is null or empty", () => {
                // Arrange
                const emptyPromoList = [];
                const nullPromoList = null;

                // Act
                const emptyPromoListReturn =
                    promotionsHelper.getPromosWithAddableVaps(emptyPromoList);
                const nullPromoListReturn =
                    promotionsHelper.getPromosWithAddableVaps(nullPromoList);
                // Assert
                expect(emptyPromoListReturn).toEqual([]);
                expect(nullPromoListReturn).toEqual([]);
            });
            it("returns an empty array when no promo with addable vaps is provided", () => {
                // Arrange
                const promoIdentifier = "identifierString";
                const promoListWithAddableVapsPromo = [
                    { promoIdentifier: promoIdentifier, partNumber: "fakekPromoPartNumber" },
                ];

                // Act
                const promoListWithAddableVapsPromoReturn =
                    promotionsHelper.getPromosWithAddableVaps(promoListWithAddableVapsPromo);

                // Assert
                expect(promoListWithAddableVapsPromoReturn).toEqual([]);
            });
            it("returns an array with the provided promo when a promo with addable vaps is provided", () => {
                // Arrange
                const promoIdentifier = "identifierString";
                const promoListWithAddableVapsPromo = [
                    {
                        promoIdentifier: promoIdentifier,
                        partNumber: promotionsHelper.addableVapsPromoPartNumbers[0],
                    },
                ];

                // Act
                const promoListWithAddableVapsPromoReturn =
                    promotionsHelper.getPromosWithAddableVaps(promoListWithAddableVapsPromo);

                // Assert
                expect(promoListWithAddableVapsPromoReturn[0].promoIdentifier).toEqual(
                    promoIdentifier
                );
            });
        });
        describe("getLineItemsThatMatchPromos", () => {
            it("returns an empty array when parameters or their children are null or empty", () => {
                // Arrange
                const validGuidForId = "09bd698e-97b3-4fca-a707-5cb16a93a127";

                // <promos> setup
                const nullPromos = null;
                const emptyPromos = [];
                const promosWithNullDiscountedLineItemIds = [{ discountedLineItemIds: null }];
                const promosWithEmptyDiscountedLineItemIds = [{ discountedLineItemIds: [] }];
                const validPromosWithOnePromo = [{ discountedLineItemIds: [validGuidForId] }];

                // <availableLineItems setup
                const nullAvailableLineItems = null;
                const emptyAvailableLineItems = [];
                const validAvailableLineItemsWithOneItem = [{ id: validGuidForId }];

                // Act
                // Null or Empty <promos> scenarios
                const nullPromosReturn = promotionsHelper.getLineItemsThatMatchPromos(
                    nullPromos,
                    validAvailableLineItemsWithOneItem
                );
                const emptyPromosReturn = promotionsHelper.getLineItemsThatMatchPromos(
                    emptyPromos,
                    validAvailableLineItemsWithOneItem
                );
                const nullDiscountedLineItemsReturn = promotionsHelper.getLineItemsThatMatchPromos(
                    promosWithNullDiscountedLineItemIds,
                    validAvailableLineItemsWithOneItem
                );
                const emptyDiscountedLineItemsReturn = promotionsHelper.getLineItemsThatMatchPromos(
                    promosWithEmptyDiscountedLineItemIds,
                    validAvailableLineItemsWithOneItem
                );

                // Null or Empty <availableLineItems> scenarios
                const nullAvailableLineItemsReturn = promotionsHelper.getLineItemsThatMatchPromos(
                    validPromosWithOnePromo,
                    nullAvailableLineItems
                );
                const emptyAvailableLineItemsReturn = promotionsHelper.getLineItemsThatMatchPromos(
                    validPromosWithOnePromo,
                    emptyAvailableLineItems
                );

                // Assert
                // Null or Empty <promos> assertions
                expect(nullPromosReturn).toEqual([]);
                expect(emptyPromosReturn).toEqual([]);
                expect(nullDiscountedLineItemsReturn).toEqual([]);
                expect(emptyDiscountedLineItemsReturn).toEqual([]);

                // Null or Empty <availableLineItems> assertions
                expect(nullAvailableLineItemsReturn).toEqual([]);
                expect(emptyAvailableLineItemsReturn).toEqual([]);
            });
            it("returns the matching availableLineItem when provided valid, matching data in promos", () => {
                const promos = [
                    {
                        discountedLineItemIds: [1, 2],
                    },
                ];
                const availableLineItems = [
                    {
                        id: 1,
                        name: "Item 1",
                    },
                    {
                        id: 2,
                        name: "Item 2",
                    },
                    {
                        id: 3,
                        name: "Item 3",
                    },
                ];
                const expected = [
                    {
                        id: 1,
                        name: "Item 1",
                    },
                    {
                        id: 2,
                        name: "Item 2",
                    },
                ];
                expect(
                    promotionsHelper.getLineItemsThatMatchPromos(promos, availableLineItems)
                ).toEqual(expected);
            });
        });
        describe("getAddableVapsFromAvailableLineItems", () => {
            it("returns empty array if availableLineItems is null or empty", () => {
                // Arrange
                const nullAvailableLineItems = null;
                const emptyAvailableLineItems = [];

                // Act
                const nullAvailableLineItemsReturn =
                    promotionsHelper.getAddableVapsFromAvailableLineItems(nullAvailableLineItems);
                const emptyAvailableLineItemsReturn =
                    promotionsHelper.getAddableVapsFromAvailableLineItems(emptyAvailableLineItems);

                // Assert
                expect(nullAvailableLineItemsReturn).toEqual([]);
                expect(emptyAvailableLineItemsReturn).toEqual([]);
            });
            it("returns provided front wiper and rain defense items from availableLineItems", () => {
                // Arrange
                const availableLineItemsWithFrontWipersAndRainDefense = [
                    { partType: partTypeStrings.FRONT_WIPER },
                    { partType: partTypeStrings.RAIN_DEFENSE },
                    { partType: partTypeStrings.REAR_WIPER },
                    { partType: partTypeStrings.RECALIBRATION },
                    { partType: partTypeStrings.REPLACE_FEE },
                    { partType: "Windshield" },
                ];

                // Act
                const addableVapsFromAvailableLineItems =
                    promotionsHelper.getAddableVapsFromAvailableLineItems(
                        availableLineItemsWithFrontWipersAndRainDefense
                    );

                // Assert
                const frontWiperParts = addableVapsFromAvailableLineItems.filter(
                    (lineItem) => lineItem.partType == partTypeStrings.FRONT_WIPER
                );
                const rainDefenseParts = addableVapsFromAvailableLineItems.filter(
                    (lineItem) => lineItem.partType == partTypeStrings.RAIN_DEFENSE
                );
                expect(addableVapsFromAvailableLineItems.length).toEqual(2);
                expect(frontWiperParts.length).toEqual(1);
                expect(rainDefenseParts.length).toEqual(1);
            });
        });
        describe("removeVapsPromosFromPromoArray", () => {
            it("returns empty array if promoArray is null or empty", () => {
                // Arrange
                const nullPromoArray = null;
                const emptyPromoArray = [];

                // Act
                const nullPromoArrayReturn =
                    promotionsHelper.removeVapsPromosFromPromoArray(nullPromoArray);
                const emptyPromoArrayReturn =
                    promotionsHelper.removeVapsPromosFromPromoArray(emptyPromoArray);

                // Assert
                expect(nullPromoArrayReturn).toEqual([]);
                expect(emptyPromoArrayReturn).toEqual([]);
            });
            it("returns list of promos without vapsPromos included in it", () => {
                // Arrange
                const fullPromoArray = [
                    {
                        partNumber:
                            promotionsHelper.promoPartNumberStrings.WIPER_DISCOUNT_PART_NUMBER,
                    },
                    {
                        partNumber:
                            promotionsHelper.promoPartNumberStrings
                                .RAIN_DEFENSE_DISCOUNT_PART_NUMBER,
                    },
                    {
                        partNumber:
                            promotionsHelper.promoPartNumberStrings.GLASS_DISCOUNT_PART_NUMBER,
                    },
                    {
                        partNumber:
                            promotionsHelper.promoPartNumberStrings
                                .GLASS_CLEANER_DISCOUNT_PART_NUMBER,
                    },
                ];

                // Act
                const promosWithoutVapsPromos =
                    promotionsHelper.removeVapsPromosFromPromoArray(fullPromoArray);

                // Assert
                const glassPromoArray = promosWithoutVapsPromos.filter(
                    (promo) =>
                        promo.partNumber ==
                        promotionsHelper.promoPartNumberStrings.GLASS_DISCOUNT_PART_NUMBER
                );
                expect(promosWithoutVapsPromos.length).toEqual(1);
                expect(glassPromoArray.length).toEqual(1);
            });
        });
        describe("revalidatePromosAndValidateQueryStringPromo", () => {
            it("returns null responses if active and inactive promos are null in store and no new promo", async () => {
                // Arrange
                const newPromo = null;
                const pricedLineItems = null;
                const pageNameToLog = "testPage";
                store.getters.order.lineItems.promos = null;
                store.getters.order.payment.inactivePromos = null;

                // Act
                const { validatePromoResponse, revalidatePromoResponse } =
                    await promotionsHelper.revalidatePromosAndValidateQueryStringPromo(
                        newPromo,
                        pricedLineItems,
                        pageNameToLog
                    );

                // Assert
                expect(validatePromoResponse).toBeNull();
                expect(revalidatePromoResponse).toBeNull();
            });
            it("returns null responses if active and inactive promos are empty arrays in store and no new promo", async () => {
                // Arrange
                const newPromo = null;
                const pricedLineItems = null;
                const pageNameToLog = "testPage";
                store.getters.order.lineItems.promos = [];
                store.getters.order.payment.inactivePromos = [];

                // Act
                const { validatePromoResponse, revalidatePromoResponse } =
                    await promotionsHelper.revalidatePromosAndValidateQueryStringPromo(
                        newPromo,
                        pricedLineItems,
                        pageNameToLog
                    );

                // Assert
                expect(validatePromoResponse).toBeNull();
                expect(revalidatePromoResponse).toBeNull();
            });
            it("returns a revalidate response if store has active promos", async () => {
                // Arrange
                const newPromo = null;
                const pricedLineItems = null;
                const pageNameToLog = "testPage";
                store.getters.order.lineItems.promos = ["data"];
                store.getters.order.payment.inactivePromos = [];
                const revalidateResponse = { errors: [] };
                mockReturnsForStoreActions[
                    storeActions.REVALIDATE_ORDER_PROMOS_AND_SAVE_SERVER_DATA
                ] = revalidateResponse;

                // Act
                const { validatePromoResponse, revalidatePromoResponse } =
                    await promotionsHelper.revalidatePromosAndValidateQueryStringPromo(
                        newPromo,
                        pricedLineItems,
                        pageNameToLog
                    );

                // Assert
                expect(validatePromoResponse).toBeNull();
                expect(revalidatePromoResponse).toEqual({ errors: [] });
            });
            it("returns a revalidate response if store has inactive promos", async () => {
                // Arrange
                const newPromo = null;
                const pricedLineItems = null;
                const pageNameToLog = "testPage";
                store.getters.order.lineItems.promos = [];
                store.getters.order.payment.inactivePromos = ["data"];
                const revalidateResponse = { errors: [] };
                mockReturnsForStoreActions[
                    storeActions.REVALIDATE_ORDER_PROMOS_AND_SAVE_SERVER_DATA
                ] = revalidateResponse;

                // Act
                const { validatePromoResponse, revalidatePromoResponse } =
                    await promotionsHelper.revalidatePromosAndValidateQueryStringPromo(
                        newPromo,
                        pricedLineItems,
                        pageNameToLog
                    );

                // Assert
                expect(validatePromoResponse).toBeNull();
                expect(revalidatePromoResponse).toEqual(revalidateResponse);
            });
            it("returns a validate response if a new promo is provided", async () => {
                // Arrange
                const newPromo = "testPromo";
                const pricedLineItems = null;
                const pageNameToLog = "testPage";
                const validateResponse = { orderPromos: [] };
                mockReturnsForStoreActions[storeActions.VALIDATE_ORDER_PROMO_AND_SAVE_SERVER_DATA] =
                    validateResponse;

                // Act
                const { validatePromoResponse, revalidatePromoResponse } =
                    await promotionsHelper.revalidatePromosAndValidateQueryStringPromo(
                        newPromo,
                        pricedLineItems,
                        pageNameToLog
                    );

                // Assert
                expect(revalidatePromoResponse).toBeNull();
                expect(validatePromoResponse).toEqual(validateResponse);
            });
            it("returns a validate and revalidate response if a new promo is provided and inactive/active promos are in the store", async () => {
                // Arrange
                const newPromo = "testPromo";
                const pricedLineItems = null;
                const pageNameToLog = "testPage";
                const validateResponse = { orderPromos: [] };
                const revalidateResponse = { errors: [] };
                store.getters.order.lineItems.promos = ["data"];
                store.getters.order.payment.inactivePromos = ["data"];
                mockReturnsForStoreActions[
                    storeActions.REVALIDATE_ORDER_PROMOS_AND_SAVE_SERVER_DATA
                ] = revalidateResponse;
                mockReturnsForStoreActions[storeActions.VALIDATE_ORDER_PROMO_AND_SAVE_SERVER_DATA] =
                    validateResponse;

                // Act
                const { validatePromoResponse, revalidatePromoResponse } =
                    await promotionsHelper.revalidatePromosAndValidateQueryStringPromo(
                        newPromo,
                        pricedLineItems,
                        pageNameToLog
                    );

                // Assert
                expect(revalidatePromoResponse).toEqual(revalidateResponse);
                expect(validatePromoResponse).toEqual(validateResponse);
            });
        });
        describe("buildToastMessagesFromRevalidateOrValidatePromoResponse", () => {
            it("returns an empty array if promoResponse parameter is null or an unexpected format", () => {
                // Arrange
                const nullPromoResponse = null;
                const unexpectedPromoResponse = { unexpectedKey: "unexpectedValue" };

                // Act
                const nullPromoResponseReturn =
                    promotionsHelper.buildToastMessagesFromRevalidateOrValidatePromoResponse(
                        nullPromoResponse
                    );
                const unexpectedPromoResponseReturn =
                    promotionsHelper.buildToastMessagesFromRevalidateOrValidatePromoResponse(
                        unexpectedPromoResponse
                    );

                // Assert
                expect(nullPromoResponseReturn).toEqual([]);
                expect(unexpectedPromoResponseReturn).toEqual([]);
            });
            describe("validatePromoResponse scenarios", () => {
                it("returns an alert for success and error scenarios", () => {
                    // Arrange
                    const successPromoResponse = { orderPromos: [{ promoCode: "testPromo" }] };
                    const errorPromoResponse = { promoCode: "errorTestPromo" };

                    // Act
                    const successPromoResponseReturn =
                        promotionsHelper.buildToastMessagesFromRevalidateOrValidatePromoResponse(
                            successPromoResponse
                        );
                    const errorPromoResponseReturn =
                        promotionsHelper.buildToastMessagesFromRevalidateOrValidatePromoResponse(
                            errorPromoResponse
                        );

                    // Assert
                    // This depends on "createPromoSuccessAlert" implementation, which is bad practice
                    //  but jest has trouble mocking same file dependencies
                    expect(successPromoResponseReturn[0]).toHaveProperty("type", "alert-success");
                    expect(errorPromoResponseReturn[0]).toHaveProperty("type", "alert-danger");
                });
                it("returns only one success message for a bundle validate promo response", () => {
                    // Arrange
                    const bundlePromoResponse = {
                        orderPromos: [
                            { promoCode: "bundlePromo/458" },
                            { promoCode: "bundlePromo/459" },
                        ],
                    };
                    // Act
                    const bundlePromoResponseReturn =
                        promotionsHelper.buildToastMessagesFromRevalidateOrValidatePromoResponse(
                            bundlePromoResponse
                        );

                    // Assert
                    // This depends on "createPromoSuccessAlert" implementation, which is bad practice
                    //  but jest has trouble mocking same file dependencies
                    expect(bundlePromoResponseReturn).toHaveLength(1);
                });
            });
            describe("revalidatePromoResponse scenarios", () => {
                it("returns alerts for new success and new error scenario", () => {
                    // Arrange
                    const successAndErrorPromoResponse = {
                        promoLineItems: [{ promoCode: "successPromo" }],
                        errors: [{ promoCode: "errorPromo" }],
                    };

                    // Act
                    const successAndErrorPromoResponseReturn =
                        promotionsHelper.buildToastMessagesFromRevalidateOrValidatePromoResponse(
                            successAndErrorPromoResponse
                        );

                    // Assert
                    // This depends on "createPromoSuccessAlert" implementation, which is bad practice
                    //  but jest has trouble mocking same file dependencies
                    const successAlert = successAndErrorPromoResponseReturn.filter((alert) =>
                        alert.messageCopy.toLowerCase().includes("successpromo")
                    );
                    expect(successAlert[0]).toHaveProperty("type", "alert-success");
                    const errorAlert = successAndErrorPromoResponseReturn.filter((alert) =>
                        alert.messageCopy.toLowerCase().includes("errorpromo")
                    );
                    expect(errorAlert[0]).toHaveProperty("type", "alert-danger");
                });
                it("returns only alerts for new success and new error scenario", () => {
                    // Arrange
                    const successAndErrorPromoResponse = {
                        promoLineItems: [
                            { promoCode: "successPromo" },
                            { promoCode: "successPromoAlreadyActive" },
                        ],
                        errors: [
                            { promoCode: "errorPromo" },
                            { promoCode: "errorPromoAlreadyInactive" },
                        ],
                    };
                    const oldActivePromos = [{ promoCode: "successPromoAlreadyActive" }];
                    const oldInactivePromos = ["errorPromoAlreadyInactive"];
                    // Act
                    const successAndErrorPromoResponseReturn =
                        promotionsHelper.buildToastMessagesFromRevalidateOrValidatePromoResponse(
                            successAndErrorPromoResponse,
                            oldActivePromos,
                            oldInactivePromos
                        );

                    // Assert
                    // This depends on "createPromoSuccessAlert" implementation, which is bad practice
                    //  but jest has trouble mocking same file dependencies
                    const successAlert = successAndErrorPromoResponseReturn.filter((alert) =>
                        alert.messageCopy.toLowerCase().includes("successpromo")
                    );
                    expect(successAlert[0]).toHaveProperty("type", "alert-success");
                    const errorAlert = successAndErrorPromoResponseReturn.filter((alert) =>
                        alert.messageCopy.toLowerCase().includes("errorpromo")
                    );
                    expect(errorAlert[0]).toHaveProperty("type", "alert-danger");
                });
            });
        });
        describe("getPromosThatMatchLineItemsOnOrder", () => {
            it("returns an empty array when either parameter is null or empty", () => {
                // Arrange
                const nullPromos = null;
                const emptyPromos = [];
                const validPromos = [{ promoCode: "validPromo", discountedLineItemIds: [1] }];

                const nullLineItemsOnOrder = null;
                const emptyLineItemsOnOrder = [];
                const validLineItemsOnOrder = [{ id: 1 }];

                // Act
                // Bad promos
                const nullPromosReturn = promotionsHelper.getPromosThatMatchLineItemsOnOrder(
                    nullPromos,
                    validLineItemsOnOrder
                );
                const emptyPromosReturn = promotionsHelper.getPromosThatMatchLineItemsOnOrder(
                    emptyPromos,
                    validLineItemsOnOrder
                );

                // Bad lineItemsOnOrder
                const nullLineItemsOnOrderReturn =
                    promotionsHelper.getPromosThatMatchLineItemsOnOrder(
                        validPromos,
                        nullLineItemsOnOrder
                    );
                const emptyLineItemsOnOrderReturn =
                    promotionsHelper.getPromosThatMatchLineItemsOnOrder(
                        validPromos,
                        emptyLineItemsOnOrder
                    );

                // Bad both
                const nullBothReturn = promotionsHelper.getPromosThatMatchLineItemsOnOrder(
                    nullPromos,
                    nullLineItemsOnOrder
                );
                const emptyBothReturn = promotionsHelper.getPromosThatMatchLineItemsOnOrder(
                    emptyPromos,
                    emptyLineItemsOnOrder
                );

                // Assert
                expect(nullPromosReturn).toEqual([]);
                expect(emptyPromosReturn).toEqual([]);
                expect(nullLineItemsOnOrderReturn).toEqual([]);
                expect(emptyLineItemsOnOrderReturn).toEqual([]);
                expect(nullBothReturn).toEqual([]);
                expect(emptyBothReturn).toEqual([]);
            });
            it("returns only matching promos with all discountedLineItemIds in lineItemsOnOrder", () => {
                // Arrange
                const promos = [
                    { promoCode: "validPromo", discountedLineItemIds: [1, 2] },
                    { promoCode: "invalidPromo", discountedLineItemIds: [3, 4] },
                ];
                const lineItemsOnOrder = [{ id: 1 }, { id: 2 }, { id: 3 }];

                const expectedReturnPromos = [
                    { promoCode: "validPromo", discountedLineItemIds: [1, 2] },
                ];

                // Act
                const returnedItems = promotionsHelper.getPromosThatMatchLineItemsOnOrder(
                    promos,
                    lineItemsOnOrder
                );

                // Assert
                expect(returnedItems).toEqual(expectedReturnPromos);
            });
            it("returns a bundle promo only if BOTH bundle pieces are satisfied", () => {
                // Arrange
                const promos = [
                    { promoCode: "validPromo/400", discountedLineItemIds: [1, 2] },
                    { promoCode: "validPromo/401", discountedLineItemIds: [3, 4] },
                    { promoCode: "invalidPromo/500", discountedLineItemIds: [5, 6] },
                    { promoCode: "invalidPromo/501", discountedLineItemIds: [7, 8] },
                ];
                // NOTE: invalidPromo/500 is satisfied with below lineItems while /501 is not
                const lineItemsOnOrder = [
                    { id: 1 },
                    { id: 2 },
                    { id: 3 },
                    { id: 4 },
                    { id: 5 },
                    { id: 6 },
                    { id: 7 },
                ];

                const expectedReturnPromos = [
                    { promoCode: "validPromo/400", discountedLineItemIds: [1, 2] },
                    { promoCode: "validPromo/401", discountedLineItemIds: [3, 4] },
                ];

                // Act
                const returnedItems = promotionsHelper.getPromosThatMatchLineItemsOnOrder(
                    promos,
                    lineItemsOnOrder
                );

                // Assert
                expect(returnedItems).toEqual(expectedReturnPromos);
            });
        });
        describe("getVapsThatNeedToBeAddedToSatisfyPromos", () => {
            it("returns an empty array when promos or availableLineItems are null or empty", () => {
                // Arrange
                const nullPromos = null;
                const emptyPromos = [];
                const validPromos = [
                    {
                        promoCode: "validPromo",
                        partNumber: promotionsHelper.addableVapsPromoPartNumbers[0],
                        discountedLineItemIds: [1],
                    },
                ];

                const nullAvailableLineItems = null;
                const emptyAvailableLineItems = [];
                const validAvailableLineItems = [{ id: 1 }];

                const nullLineItemsOnOrder = null;
                const emptyLineItemsOnOrder = [];
                const validLineItemsOnOrder = { vaps: [{ id: 1 }] };

                // Act
                // Bad promos
                const nullPromosReturn = promotionsHelper.getVapsThatNeedToBeAddedToSatisfyPromos(
                    nullPromos,
                    validAvailableLineItems,
                    validLineItemsOnOrder
                );
                const emptyPromosReturn = promotionsHelper.getVapsThatNeedToBeAddedToSatisfyPromos(
                    emptyPromos,
                    validAvailableLineItems,
                    validLineItemsOnOrder
                );

                // Bad availableLineItems
                const nullAvailableLineItemsReturn =
                    promotionsHelper.getVapsThatNeedToBeAddedToSatisfyPromos(
                        validPromos,
                        nullAvailableLineItems,
                        validLineItemsOnOrder
                    );
                const emptyAvailableLineItemsReturn =
                    promotionsHelper.getVapsThatNeedToBeAddedToSatisfyPromos(
                        validPromos,
                        emptyAvailableLineItems,
                        validLineItemsOnOrder
                    );

                // Bad all
                const nullAllReturn = promotionsHelper.getVapsThatNeedToBeAddedToSatisfyPromos(
                    nullPromos,
                    nullAvailableLineItems,
                    nullLineItemsOnOrder
                );
                const emptyAllReturn = promotionsHelper.getVapsThatNeedToBeAddedToSatisfyPromos(
                    emptyPromos,
                    emptyAvailableLineItems,
                    emptyLineItemsOnOrder
                );

                // Assert
                expect(nullPromosReturn).toEqual([]);
                expect(emptyPromosReturn).toEqual([]);
                expect(nullAvailableLineItemsReturn).toEqual([]);
                expect(emptyAvailableLineItemsReturn).toEqual([]);
                expect(nullAllReturn).toEqual([]);
                expect(emptyAllReturn).toEqual([]);
            });
            it("returns vaps that need to be provided to satisfy promo only", () => {
                // Arrange
                const promos = [
                    {
                        promoCode: "validPromo",
                        partNumber: promotionsHelper.addableVapsPromoPartNumbers[0],
                        discountedLineItemIds: [1],
                    },
                    {
                        promoCode: "invalidPromo",
                        partNumber: "notAVapsPromo",
                        discountedLineItemIds: [2],
                    },
                ];
                const availableLineItems = [{ id: 1 }, { id: 2 }, { id: 3 }];
                const lineItemsOnOrder = [];

                const expectedReturn = [{ id: 1 }];

                // Act
                const actualReturn = promotionsHelper.getVapsThatNeedToBeAddedToSatisfyPromos(
                    promos,
                    availableLineItems,
                    lineItemsOnOrder
                );

                // Assert
                expect(actualReturn).toEqual(expectedReturn);
            });
            it("returns no vaps if those vaps are already on the order", () => {
                // Arrange
                const promos = [
                    {
                        promoCode: "validPromo",
                        partNumber: promotionsHelper.addableVapsPromoPartNumbers[0],
                        discountedLineItemIds: [1],
                    },
                    {
                        promoCode: "invalidPromo",
                        partNumber: "notAVapsPromo",
                        discountedLineItemIds: [2],
                    },
                ];
                const availableLineItems = [{ id: 1 }, { id: 2 }, { id: 3 }];
                const lineItemsOnOrder = { vaps: [{ id: 1 }] };

                const expectedReturn = [];

                // Act
                const actualReturn = promotionsHelper.getVapsThatNeedToBeAddedToSatisfyPromos(
                    promos,
                    availableLineItems,
                    lineItemsOnOrder
                );

                // Assert
                expect(actualReturn).toEqual(expectedReturn);
            });
        });
        describe("removeCurrentlyActivePromoCodesFromInactivePromos", () => {
            it("returns an empty array if inactivePromos is null or empty, don't break if activePromoObjects is null or empty", () => {
                // Arrange
                const nullActivePromoObjects = null;
                const emptyActivePromoObjects = [];
                const validActivePromoObjects = [{ promoCode: "validPromo" }];

                const nullInactivePromos = null;
                const emptyInactivePromos = [];
                // Act
                const nullInactivePromosReturn =
                    promotionsHelper.removeCurrentlyActivePromoCodesFromInactivePromos(
                        validActivePromoObjects,
                        nullInactivePromos
                    );
                const emptyInactivePromosReturn =
                    promotionsHelper.removeCurrentlyActivePromoCodesFromInactivePromos(
                        validActivePromoObjects,
                        emptyInactivePromos
                    );

                const allNullReturn =
                    promotionsHelper.removeCurrentlyActivePromoCodesFromInactivePromos(
                        nullActivePromoObjects,
                        nullInactivePromos
                    );
                const allEmptyReturn =
                    promotionsHelper.removeCurrentlyActivePromoCodesFromInactivePromos(
                        emptyActivePromoObjects,
                        emptyInactivePromos
                    );

                // Assert
                expect(nullInactivePromosReturn).toEqual([]);
                expect(emptyInactivePromosReturn).toEqual([]);
                expect(allNullReturn).toEqual([]);
                expect(allEmptyReturn).toEqual([]);
            });
            it("returns inactivePromos with active promos removed", () => {
                // Arrange
                const activePromoObjects = [{ promoCode: "activePromo" }];
                const inactivePromos = ["activePromo", "inactivePromo"];

                const expectedReturn = ["inactivePromo"];

                // Act
                const actualReturn =
                    promotionsHelper.removeCurrentlyActivePromoCodesFromInactivePromos(
                        activePromoObjects,
                        inactivePromos
                    );

                // Assert
                expect(actualReturn).toEqual(expectedReturn);
            });
            it("returns inactivePromos with active bundle promos removed", () => {
                // Arrange
                const activePromoObjects = [
                    { promoCode: "activeBundlePromo/400" },
                    { promoCode: "activeBundlePromo/401" },
                ];
                const inactivePromos = ["activeBundlePromo", "inactivePromo"];

                const expectedReturn = ["inactivePromo"];

                // Act
                const actualReturn =
                    promotionsHelper.removeCurrentlyActivePromoCodesFromInactivePromos(
                        activePromoObjects,
                        inactivePromos
                    );

                // Assert
                expect(actualReturn).toEqual(expectedReturn);
            });
            it("returns all inactivePromos when no matching activePromo is supplied", () => {
                // Arrange
                const activePromoObjects = [{ promoCode: "activePromo" }];
                const inactivePromos = ["inactivePromo1", "inactivePromo2"];

                // Act
                const actualReturn =
                    promotionsHelper.removeCurrentlyActivePromoCodesFromInactivePromos(
                        activePromoObjects,
                        inactivePromos
                    );

                // Assert
                expect(actualReturn).toEqual(inactivePromos);
            });
        });
        describe("shouldStripPromoQueryString", () => {
            it("returns false when provided with a null parameter", () => {
                // Arrange
                const nullFmgPageQueryValue = null;

                // Act
                const actualReturn =
                    promotionsHelper.shouldStripPromoQueryString(nullFmgPageQueryValue);

                // Assert
                expect(actualReturn).toBeFalsy();
            });
            it("returns true when provided with a parameter from 'pagesToStripPromoQueryStringFrom", () => {
                // Arrange
                const fmgPageQueryValue = promotionsHelper.pagesToStripPromoQueryStringFrom[0];

                // Act
                const actualReturn =
                    promotionsHelper.shouldStripPromoQueryString(fmgPageQueryValue);

                // Assert
                expect(actualReturn).toBeTruthy();
            });
        });
        describe("getNewlyInactivatedPromos", () => {
            it("returns an empty array if newInactivePromos is null or empty, does not break with null oldInactivePromos", () => {
                // Arrange
                const nullNewInactivePromos = null;
                const emptyNewInactivePromos = [];
                const validNewInactivePromos = [{ promoCode: "validPromo" }];

                const nullOldInactivePromos = null;
                const validOldInactivePromos = ["validPromo"];

                // Act
                const nullNewInactivePromosReturn = promotionsHelper.getNewlyInactivatedPromos(
                    validOldInactivePromos,
                    nullNewInactivePromos
                );
                const emptyNewInactivePromosReturn = promotionsHelper.getNewlyInactivatedPromos(
                    validOldInactivePromos,
                    emptyNewInactivePromos
                );

                const nullOldInactivePromosReturn = promotionsHelper.getNewlyInactivatedPromos(
                    nullOldInactivePromos,
                    validNewInactivePromos
                );

                const allNullReturn = promotionsHelper.getNewlyInactivatedPromos(
                    nullOldInactivePromos,
                    nullNewInactivePromos
                );

                // Assert
                expect(nullNewInactivePromosReturn).toEqual([]);
                expect(emptyNewInactivePromosReturn).toEqual([]);
                expect(allNullReturn).toEqual([]);
                // This return was to make sure no errors occurred
                expect(nullOldInactivePromosReturn).not.toBeNull();
            });
            it("returns only inactivePromos that were not already in oldInactivePromos", () => {
                // Arrange
                const newInactivePromos = [
                    { promoCode: "filteredPromo" },
                    { promoCode: "nonFilteredPromo" },
                ];
                const oldInactivePromos = ["filteredPromo"];

                const expectedReturn = ["nonFilteredPromo"];

                // Act
                const actualReturn = promotionsHelper.getNewlyInactivatedPromos(
                    oldInactivePromos,
                    newInactivePromos
                );

                // Assert
                expect(actualReturn).toEqual(expectedReturn);
            });
            it("returns only one new inactivePromos when a new promo bundle is inactivated", () => {
                // Arrange
                const newInactivePromos = [
                    { promoCode: "nonFilteredPromo/400" },
                    { promoCode: "nonFilteredPromo/401" },
                ];
                const oldInactivePromos = ["filteredPromo"];

                const expectedReturn = ["nonFilteredPromo"];

                // Act
                const actualReturn = promotionsHelper.getNewlyInactivatedPromos(
                    oldInactivePromos,
                    newInactivePromos
                );

                // Assert
                expect(actualReturn).toEqual(expectedReturn);
            });
        });
        describe("createPromoErrorAlert", () => {
            it("returns different messages for stacking error codes and any others", () => {
                // Arrange
                const promoCode = "testPromo";
                const additionalInfo = ["testAdditionalInfo"];

                const stackingErrorCode =
                    promotionsHelper.promoErrorCodes.PROMO_STACKING_NOT_ALLOWED;
                const nonStackingErrorCode = "nonStackingErrorCode";

                // Act
                const stackingReturn = promotionsHelper.createPromoErrorAlert(
                    promoCode,
                    stackingErrorCode,
                    additionalInfo
                );
                const nonStackingReturn = promotionsHelper.createPromoErrorAlert(
                    promoCode,
                    nonStackingErrorCode,
                    additionalInfo
                );

                // Assert
                expect(stackingReturn).not.toEqual(nonStackingReturn);
            });
        });
        describe("getPromoCodesFromPromoObjectsWithoutDuplicates", () => {
            it("returns an empty array when promos is null or empty", () => {
                // Arrange
                const nullPromos = null;
                const emptyPromos = [];

                // Act
                const nullPromosReturn =
                    promotionsHelper.getPromoCodesFromPromoObjectsWithoutDuplicates(nullPromos);
                const emptyPromosReturn =
                    promotionsHelper.getPromoCodesFromPromoObjectsWithoutDuplicates(emptyPromos);

                // Assert
                expect(nullPromosReturn).toEqual([]);
                expect(emptyPromosReturn).toEqual([]);
            });
            it("returns promo codes excluding normal and bundle duplicates", () => {
                // Arrange
                const promos = [
                    { promoCode: "duplicatePromo" },
                    { promoCode: "duplicatePromo" },
                    { promoCode: "bundlePromo/400" },
                    { promoCode: "bundlePromo/401" },
                ];
                const expectedReturn = ["duplicatePromo", "bundlePromo"];

                // Act
                const actualReturn =
                    promotionsHelper.getPromoCodesFromPromoObjectsWithoutDuplicates(promos);

                // Assert
                expect(actualReturn).toEqual(expectedReturn);
            });
        });
    });
    describe("expected method calls tests", () => {
        describe("revalidatePromosAndValidateQueryStringPromo", () => {
            it("should call saveActiveAndOrInactivePromos store method upon revalidation AND validation", async () => {
                // Arrange
                const newPromo = "testPromo";
                const pricedLineItems = null;
                const pageNameToLog = "testPage";
                const revalidateResponse = { errors: [] };
                const validateResponse = { orderPromos: [] };
                store.getters.order.lineItems.promos = ["data"];
                store.getters.order.payment.inactivePromos = ["data"];
                mockReturnsForStoreActions[
                    storeActions.REVALIDATE_ORDER_PROMOS_AND_SAVE_SERVER_DATA
                ] = revalidateResponse;
                mockReturnsForStoreActions[storeActions.VALIDATE_ORDER_PROMO_AND_SAVE_SERVER_DATA] =
                    validateResponse;

                // Act
                const { validatePromoResponse, revalidatePromoResponse } =
                    await promotionsHelper.revalidatePromosAndValidateQueryStringPromo(
                        newPromo,
                        pricedLineItems,
                        pageNameToLog
                    );

                // Assert
                expect(baseMixin.methods.dispatchStoreAction).toBeCalledWith(
                    storeActions.SAVE_ACTIVE_AND_OR_INACTIVE_PROMOS,
                    expect.anything(),
                    expect.anything()
                );
                expect(baseMixin.methods.dispatchStoreAction).toBeCalledTimes(2);
            });
            it("should save an inactive promo if the newPromo is invalid, not on the order, and has the right error code", async () => {
                // This is behavior needed to let a query string promo activate if they later
                //  change their order and it becomes valid
                // Arrange
                const newPromo = "testPromo";
                const pricedLineItems = null;
                const pageNameToLog = "testPage";
                // Query string promos are added to inactive if it's a valid promo that doesn't currently work
                //  No suitable item for promo is an example of this
                const validateResponse = {
                    promoCode: newPromo,
                    errorCode: promotionsHelper.promoErrorCodes.NO_SUITABLE_ITEM_FOR_PROMO,
                };
                mockReturnsForStoreActions[storeActions.VALIDATE_ORDER_PROMO_AND_SAVE_SERVER_DATA] =
                    validateResponse;

                // Act
                const { validatePromoResponse, revalidatePromoResponse } =
                    await promotionsHelper.revalidatePromosAndValidateQueryStringPromo(
                        newPromo,
                        pricedLineItems,
                        pageNameToLog
                    );

                // Assert
                expect(baseMixin.methods.dispatchStoreAction).toBeCalledWith(
                    storeActions.SAVE_ACTIVE_AND_OR_INACTIVE_PROMOS,
                    { inactivePromos: ["testPromo"] },
                    expect.anything()
                );
            });
            it("should not save an inactive promo if the promo is already an active promo (read comment below)", async () => {
                // should NOT save an inactive promo if the newPromo is invalid,
                //  not on the order, and has the right error code - but the promo
                //  is already in the store as active
                // This is behavior needed to make sure the query string inactive logic above
                //  doesn't result in duplicated promos
                // Arrange
                const newPromo = "testPromo";
                const pricedLineItems = null;
                const pageNameToLog = "testPage";
                store.getters.order.lineItems.promos = [{ promoCode: newPromo }];
                const revalidateResponse = { errors: [] };
                const validateResponse = {
                    promoCode: newPromo,
                    errorCode: promotionsHelper.promoErrorCodes.NO_SUITABLE_ITEM_FOR_PROMO,
                };
                mockReturnsForStoreActions[storeActions.VALIDATE_ORDER_PROMO_AND_SAVE_SERVER_DATA] =
                    validateResponse;
                mockReturnsForStoreActions[
                    storeActions.REVALIDATE_ORDER_PROMOS_AND_SAVE_SERVER_DATA
                ] = revalidateResponse;

                // Act
                const { validatePromoResponse, revalidatePromoResponse } =
                    await promotionsHelper.revalidatePromosAndValidateQueryStringPromo(
                        newPromo,
                        pricedLineItems,
                        pageNameToLog
                    );

                // Assert
                expect(baseMixin.methods.dispatchStoreAction).not.toBeCalledWith(
                    storeActions.SAVE_ACTIVE_AND_OR_INACTIVE_PROMOS,
                    { inactivePromos: ["testPromo"] },
                    expect.anything()
                );
            });
            it("should not save an inactive promo if the promo is on the 'excludeFromInactivePromoErrorCodes' list", async () => {
                // The excludeFromInactivePromoErrorCodes list includes promos that will never be valid
                //  so there is no value in adding them to the 'inactivePromos' in the store for future
                //  use
                // Arrange
                const newPromo = "testPromo";
                const pricedLineItems = null;
                const pageNameToLog = "testPage";
                const validateResponse = {
                    promoCode: newPromo,
                    errorCode: promotionsHelper.excludeFromInactivePromoErrorCodes[0],
                };
                mockReturnsForStoreActions[storeActions.VALIDATE_ORDER_PROMO_AND_SAVE_SERVER_DATA] =
                    validateResponse;

                // Act
                const { validatePromoResponse, revalidatePromoResponse } =
                    await promotionsHelper.revalidatePromosAndValidateQueryStringPromo(
                        newPromo,
                        pricedLineItems,
                        pageNameToLog
                    );

                // Assert
                expect(baseMixin.methods.dispatchStoreAction).not.toBeCalledWith(
                    storeActions.SAVE_ACTIVE_AND_OR_INACTIVE_PROMOS,
                    expect.anything(),
                    expect.anything()
                );
            });
            it("should not save an inactive promo if the promo is already in the store as inactive", async () => {
                // Arrange
                const newPromo = "testPromo";
                const pricedLineItems = null;
                const pageNameToLog = "testPage";
                const revalidateResponse = { errors: [] };
                const validateResponse = { promoCode: newPromo, errorCode: "someError" };
                store.getters.order.payment.inactivePromos = [newPromo];
                mockReturnsForStoreActions[
                    storeActions.REVALIDATE_ORDER_PROMOS_AND_SAVE_SERVER_DATA
                ] = revalidateResponse;
                mockReturnsForStoreActions[storeActions.VALIDATE_ORDER_PROMO_AND_SAVE_SERVER_DATA] =
                    validateResponse;

                // Act
                const { validatePromoResponse, revalidatePromoResponse } =
                    await promotionsHelper.revalidatePromosAndValidateQueryStringPromo(
                        newPromo,
                        pricedLineItems,
                        pageNameToLog
                    );

                // Assert
                expect(baseMixin.methods.dispatchStoreAction).not.toBeCalledWith(
                    storeActions.SAVE_ACTIVE_AND_OR_INACTIVE_PROMOS,
                    { inactivePromos: [newPromo] },
                    expect.anything()
                );
            });
            it("should call SAVE_INACTIVE_AND_OR_ACTIVE_PROMOS with a list of strings for the inactivePromos parameter", async () => {
                // The store method expects a list of strings for inactivePromos, this call needs to satisfy that
                // Arrange
                store.getters.order.lineItems.promos = ["data"];
                store.getters.order.payment.inactivePromos = ["data"];
                const revalidateResponse = {
                    promoLineItems: ["data"],
                    errors: [
                        { id: 1, promoCode: "testPromo1" },
                        { id: 2, promoCode: "testPromo2" },
                    ],
                };
                mockReturnsForStoreActions[
                    storeActions.REVALIDATE_ORDER_PROMOS_AND_SAVE_SERVER_DATA
                ] = revalidateResponse;
                // Act
                await promotionsHelper.revalidatePromosAndValidateQueryStringPromo(
                    null,
                    null,
                    null
                );

                // Assert
                expect(baseMixin.methods.dispatchStoreAction).toHaveBeenCalledWith(
                    storeActions.SAVE_ACTIVE_AND_OR_INACTIVE_PROMOS,
                    {
                        activePromos: expect.anything(),
                        inactivePromos: ["testPromo1", "testPromo2"],
                    },
                    false
                );
            });
        });
    });
});
