import { shallowMount, mount } from "@vue/test-utils";
import baseInputButton from "./base-input-button";

describe("baseInputButton.vue", () => {
    describe("general", () => {
        describe("checkbox", () => {
            test("isMultiSelect => baseInputButton is a checkbox", () => {
                // Arrange/Act
                const { wrapper } = setupMocks({
                    mockData: {
                        propsData: {
                            isMultiSelect: true,
                        },
                    },
                });

                // Assert
                const inputElement = wrapper.find("input");
                expect(inputElement.attributes().type).toEqual("checkbox");
            });
        });

        describe("radio", () => {
            test("!isMultiSelect => baseInputButton is a radio button", () => {
                // Arrange/Act
                const { wrapper } = setupMocks({
                    mockData: {
                        propsData: {
                            isMultiSelect: false,
                        },
                    },
                });

                // Assert
                const inputElement = wrapper.find("input");
                expect(inputElement.attributes().type).toEqual("radio");
            });
        });

        // tests in here should be test.each
        describe("shared", () => {
            const isMultiSelectOptions = [true, false];
            test.each(isMultiSelectOptions)("groupName", (isMultiSelect) => {
                const { wrapper } = setupMocks({
                    mockData: {
                        propsData: {
                            groupName: "boogly",
                            isMultiSelect: isMultiSelect,
                        },
                    },
                });

                // Assert
                const inputElement = wrapper.find("input");
                const inputElementAttributes = inputElement.attributes();
                expect(inputElementAttributes.name).toEqual("boogly");
            });
        });
    });

    describe("mouse clicks", () => {
        describe("checkbox", () => {
            describe("clicked once", () => {
                test("correct event and value are emitted and input is checked", async () => {
                    // Arrange
                    const { wrapper } = setupMocks({
                        mockData: {
                            propsData: {
                                isMultiSelect: true,
                                value: "X",
                            },
                        },
                    });
                    // Act
                    await wrapper.find("input").setChecked();

                    // Assert
                    expect(wrapper.emitted()["update:modelValue"][0][0]).toEqual(["X"]);
                    expect(wrapper.find("input").element.checked).toBe(true);
                });
            });

            describe("clicked twice", () => {
                test("input is unchecked", async () => {
                    // Arrange
                    const { wrapper } = setupMocks({
                        mockData: {
                            propsData: {
                                isMultiSelect: true,
                                value: "X",
                            },
                        },
                    });
                    const input = wrapper.find("input");

                    // Act
                    await wrapper.trigger("click");
                    await wrapper.trigger("click");

                    // Assert
                    expect(input.element.checked).toBe(false);
                });
            });

            describe("is initially checked, click once", () => {
                test("input is unchecked", async () => {
                    // Arrange
                    const { wrapper } = setupMocks({
                        mockData: {
                            propsData: {
                                isMultiSelect: true,
                                value: "X",
                                modelValue: ["X"],
                            },
                        },
                    });
                    const input = wrapper.find("input");

                    expect(input.element.checked).toBe(true);

                    // Act
                    await wrapper.trigger("click");

                    // Assert
                    expect(input.element.checked).toBe(false);
                });
            });
        });

        describe("radio", () => {
            test("clicked => correct event and value are emitted", async () => {
                // Arrange
                const { wrapper } = setupMocks({
                    mockData: {
                        propsData: {
                            isMultiSelect: false,
                            value: "X",
                        },
                    },
                });

                // Act
                await wrapper.trigger("mousedown.left");
                await wrapper.trigger("click");

                // Assert
                expect(wrapper.emitted()["update:modelValue"][0][0]).toEqual("X");
            });
        });
    });

    describe("events", () => {
        describe("checkbox", () => {
            test("change event fired from checkbox => update:modelValue is emitted with correct value", async () => {
                // Arrange
                const { wrapper } = setupMocks({
                    mockData: {
                        propsData: {
                            isMultiSelect: true,
                            value: "Hi",
                        },
                    },
                });

                const input = wrapper.find("input");

                // Act
                await input.trigger("change");

                // Assert
                expect(wrapper.emitted()["update:modelValue"][0][0]).toEqual(["Hi"]);
            });

            test("focus and click space on a checkbox => update:modelValue is not emitted", async () => {
                // Arrange
                const { wrapper } = setupMocks({
                    mockData: {
                        propsData: {
                            isMultiSelect: true,
                            value: "Hi",
                        },
                    },
                });

                const input = wrapper.find("input");

                // Act
                await input.trigger("keypress", { key: "space" });

                // Assert
                expect(wrapper.emitted()).not.toHaveProperty("update:modelValue");
            });

            test("focus and click enter on a checkbox => nothing should happen", async () => {
                // Arrange
                const { wrapper } = setupMocks({
                    mockData: {
                        propsData: {
                            isMultiSelect: true,
                            value: "Hi",
                        },
                    },
                });

                const input = wrapper.find("input");

                // Act
                await input.trigger("keypress", { key: "enter" });
                console.log(wrapper.emitted()["update:modelValue"]);

                // Assert
                expect(wrapper.emitted()["update:modelValue"]).toBe(undefined);
            });
        });

        describe("radio", () => {
            test("focus and click space on a radio button => update:modelValue is emitted with correct value", async () => {
                // Arrange
                const { wrapper } = setupMocks({
                    mockData: {
                        propsData: {
                            isMultiSelect: false,
                            value: "Hi",
                        },
                    },
                });

                const input = wrapper.find("input");

                // Act
                await input.trigger("keypress", { key: "space" });

                // Assert
                expect(wrapper.emitted()).toHaveProperty("update:modelValue");
                expect(wrapper.emitted()["update:modelValue"][0][0]).toEqual("Hi");
            });

            test("focus and click enter on a radio button => update:modelValue is emitted with correct value", async () => {
                // Arrange
                const { wrapper } = setupMocks({
                    mockData: {
                        propsData: {
                            isMultiSelect: false,
                            value: "Hi",
                        },
                    },
                });

                const input = wrapper.find("input");

                // Act
                await input.trigger("keypress", { key: "enter" });

                // Assert
                expect(wrapper.emitted()).toHaveProperty("update:modelValue");
                expect(wrapper.emitted()["update:modelValue"][0][0]).toEqual("Hi");
            });
        });
    });

    describe("methods", () => {
        describe("handleEventAction", () => {
            describe("isMultiSelect", () => {
                test("eventType === eventTypes.CLICK => do nothing", () => {
                    // Arrange
                    const { wrapper } = setupMocks({
                        mockData: {
                            propsData: {
                                isMultiSelect: true,
                            },
                        },
                    });
                    wrapper.vm.handleClick = jest.fn();
                    wrapper.vm.handlePushClickEventToGACheck = jest.fn();
                    wrapper.vm.handleSelectionChange = jest.fn();

                    // Act
                    wrapper.vm.handleEventAction("click", { myEvent: "test" });

                    // Assert
                    expect(wrapper.vm.handleClick).not.toHaveBeenCalled();
                    expect(wrapper.vm.handleSelectionChange).not.toHaveBeenCalled();
                    expect(wrapper.vm.handlePushClickEventToGACheck).not.toHaveBeenCalled();
                });

                test("eventType === eventTypes.ENTER => do nothing", () => {
                    // Arrange
                    const { wrapper } = setupMocks({
                        mockData: {
                            propsData: {
                                isMultiSelect: true,
                            },
                        },
                    });
                    wrapper.vm.handleClick = jest.fn();
                    wrapper.vm.handlePushClickEventToGACheck = jest.fn();
                    wrapper.vm.handleSelectionChange = jest.fn();

                    // Act
                    wrapper.vm.handleEventAction("enter", { myEvent: "test" });

                    // Assert
                    expect(wrapper.vm.handleClick).not.toHaveBeenCalled();
                    expect(wrapper.vm.handleSelectionChange).not.toHaveBeenCalled();
                    expect(wrapper.vm.handlePushClickEventToGACheck).not.toHaveBeenCalled();
                });

                test("eventType === eventTypes.CHANGE => call handleClick and handlePushClickEventToGACheck", () => {
                    // Arrange
                    const { wrapper } = setupMocks({
                        mockData: {
                            propsData: {
                                isMultiSelect: true,
                            },
                        },
                    });
                    wrapper.vm.handleClick = jest.fn();
                    wrapper.vm.handlePushClickEventToGACheck = jest.fn();
                    wrapper.vm.handleSelectionChange = jest.fn();

                    // Act
                    wrapper.vm.handleEventAction("change", { myEvent: "test" });

                    // Assert
                    expect(wrapper.vm.handleClick).toHaveBeenCalledWith({
                        myEvent: "test",
                    });
                    expect(wrapper.vm.handlePushClickEventToGACheck).toHaveBeenCalledWith("click");
                    expect(wrapper.vm.handleSelectionChange).not.toHaveBeenCalled();
                });

                test("eventType === eventTypes.MOUNT => do nothing", () => {
                    // Arrange
                    const { wrapper } = setupMocks({
                        mockData: {
                            propsData: {
                                isMultiSelect: true,
                            },
                        },
                    });
                    wrapper.vm.handleClick = jest.fn();
                    wrapper.vm.handlePushClickEventToGACheck = jest.fn();
                    wrapper.vm.handleSelectionChange = jest.fn();

                    // Act
                    wrapper.vm.handleEventAction("mount", { myEvent: "test" });

                    // Assert
                    expect(wrapper.vm.handleClick).not.toHaveBeenCalled();
                    expect(wrapper.vm.handleSelectionChange).not.toHaveBeenCalled();
                    expect(wrapper.vm.handlePushClickEventToGACheck).not.toHaveBeenCalled();
                });

                test("eventType === eventTypes.SPACE => do nothing", () => {
                    // Arrange
                    const { wrapper } = setupMocks({
                        mockData: {
                            propsData: {
                                isMultiSelect: true,
                            },
                        },
                    });
                    wrapper.vm.handleClick = jest.fn();
                    wrapper.vm.handlePushClickEventToGACheck = jest.fn();
                    wrapper.vm.handleSelectionChange = jest.fn();

                    // Act
                    wrapper.vm.handleEventAction("space", { myEvent: "test" });

                    // Assert
                    expect(wrapper.vm.handleClick).not.toHaveBeenCalled();
                    expect(wrapper.vm.handleSelectionChange).not.toHaveBeenCalled();
                    expect(wrapper.vm.handlePushClickEventToGACheck).not.toHaveBeenCalled();
                });
            });

            describe("!isMultiSelect", () => {
                test("eventType === eventTypes.CLICK => call correct methods", () => {
                    // Arrange
                    const { wrapper } = setupMocks({
                        mockData: {
                            propsData: {
                                isMultiSelect: false,
                            },
                        },
                    });
                    wrapper.vm.handleClick = jest.fn();
                    wrapper.vm.handlePushClickEventToGACheck = jest.fn();
                    wrapper.vm.handleSelectionChange = jest.fn();

                    // Act
                    wrapper.vm.handleEventAction("click", { myEvent: "test" });

                    // Assert
                    expect(wrapper.vm.handleClick).toHaveBeenCalledWith({
                        myEvent: "test",
                    });
                    expect(wrapper.vm.handlePushClickEventToGACheck).toHaveBeenCalledWith("click");
                    expect(wrapper.vm.handleSelectionChange).not.toHaveBeenCalled();
                });

                test("eventType === eventTypes.ENTER => call correct methods", () => {
                    // Arrange
                    const { wrapper } = setupMocks({
                        mockData: {
                            propsData: {
                                isMultiSelect: false,
                            },
                        },
                    });
                    wrapper.vm.handleClick = jest.fn();
                    wrapper.vm.handlePushClickEventToGACheck = jest.fn();
                    wrapper.vm.handleSelectionChange = jest.fn();

                    // Act
                    wrapper.vm.handleEventAction("enter", { myEvent: "test" });

                    // Assert
                    expect(wrapper.vm.handleClick).toHaveBeenCalledWith({
                        myEvent: "test",
                    });
                    expect(wrapper.vm.handlePushClickEventToGACheck).toHaveBeenCalledWith("click");
                    expect(wrapper.vm.handleSelectionChange).not.toHaveBeenCalled();
                });

                test("eventType === eventTypes.SPACE => call correct methods", () => {
                    // Arrange
                    const { wrapper } = setupMocks({
                        mockData: {
                            propsData: {
                                isMultiSelect: false,
                            },
                        },
                    });
                    wrapper.vm.handleClick = jest.fn();
                    wrapper.vm.handlePushClickEventToGACheck = jest.fn();
                    wrapper.vm.handleSelectionChange = jest.fn();

                    // Act
                    wrapper.vm.handleEventAction("space", { myEvent: "test" });

                    // Assert
                    expect(wrapper.vm.handleClick).toHaveBeenCalledWith({
                        myEvent: "test",
                    });
                    expect(wrapper.vm.handlePushClickEventToGACheck).toHaveBeenCalledWith("click");
                    expect(wrapper.vm.handleSelectionChange).not.toHaveBeenCalled();
                });

                test("eventType === eventTypes.CHANGE && !selectingInitiatesLoad => call correct methods", () => {
                    // Arrange
                    const { wrapper } = setupMocks({
                        mockData: {
                            propsData: {
                                isMultiSelect: false,
                                selectingInitiatesLoad: false,
                            },
                        },
                    });
                    wrapper.vm.handleClick = jest.fn();
                    wrapper.vm.handlePushClickEventToGACheck = jest.fn();
                    wrapper.vm.handleSelectionChange = jest.fn();

                    // Act
                    wrapper.vm.handleEventAction("change", { myEvent: "test" });

                    // Assert
                    expect(wrapper.vm.handleSelectionChange).not.toHaveBeenCalled();
                    expect(wrapper.vm.handleClick).toHaveBeenCalledWith({
                        myEvent: "test",
                    });
                    expect(wrapper.vm.handlePushClickEventToGACheck).not.toHaveBeenCalled();
                });

                test("eventType === eventTypes.CHANGE && selectingInitiatesLoad => call correct methods", () => {
                    // Arrange
                    const { wrapper } = setupMocks({
                        mockData: {
                            propsData: {
                                isMultiSelect: false,
                                selectingInitiatesLoad: true,
                            },
                        },
                    });
                    wrapper.vm.handleClick = jest.fn();
                    wrapper.vm.handlePushClickEventToGACheck = jest.fn();
                    wrapper.vm.handleSelectionChange = jest.fn();

                    // Act
                    wrapper.vm.handleEventAction("change", { myEvent: "test" });

                    // Assert
                    expect(wrapper.vm.handleSelectionChange).toHaveBeenCalledWith({
                        myEvent: "test",
                    });
                    expect(wrapper.vm.handleClick).not.toHaveBeenCalled();
                    expect(wrapper.vm.handlePushClickEventToGACheck).not.toHaveBeenCalled();
                });
            });
        });

        describe("handleSelectionChange", () => {
            const isCheckbox = [
                [true, ["Hi"]],
                [false, "Hi"],
            ];
            test.each(isCheckbox)(
                "handleChange is called with valueToEmit",
                (isMultiSelect, resultingValueToEmit) => {
                    // Arrange
                    const { wrapper } = setupMocks({
                        mockData: {
                            propsData: {
                                isMultiSelect: isMultiSelect,
                                value: "Hi",
                            },
                        },
                    });

                    wrapper.vm.handleChange = jest.fn();

                    // Act
                    wrapper.vm.handleSelectionChange({ myEvent: "TEST" });

                    // Assert
                    expect(wrapper.vm.handleChange).toHaveBeenCalledWith(resultingValueToEmit);
                }
            );

            describe("checkbox", () => {
                test("modelValue is null => valueToEmit is correct value", () => {
                    // Arrange
                    const { wrapper } = setupMocks({
                        mockData: {
                            propsData: {
                                isMultiSelect: true,
                                value: "Bello",
                                modelValue: null,
                            },
                        },
                    });

                    wrapper.vm.handleChange = jest.fn();

                    // Act
                    wrapper.vm.handleSelectionChange({ myEvent: "TEST" });

                    // Assert
                    expect(wrapper.vm.valueToEmit).toEqual(["Bello"]);
                    expect(wrapper.vm.handleChange).toHaveBeenCalledWith(["Bello"]);
                });

                test("modelValue is undefined => valueToEmit is correct value", () => {
                    // Arrange
                    const { wrapper } = setupMocks({
                        mockData: {
                            propsData: {
                                isMultiSelect: true,
                                value: "Bello",
                                modelValue: undefined,
                            },
                        },
                    });

                    wrapper.vm.handleChange = jest.fn();

                    // Act
                    wrapper.vm.handleSelectionChange({ myEvent: "TEST" });

                    // Assert
                    expect(wrapper.vm.valueToEmit).toEqual(["Bello"]);
                    expect(wrapper.vm.handleChange).toHaveBeenCalledWith(["Bello"]);
                });

                test("modelValue is empty => valueToEmit is correct value", () => {
                    // Arrange
                    const { wrapper } = setupMocks({
                        mockData: {
                            propsData: {
                                isMultiSelect: true,
                                value: "Bello",
                                modelValue: [],
                            },
                        },
                    });

                    wrapper.vm.handleChange = jest.fn();

                    // Act
                    wrapper.vm.handleSelectionChange({ myEvent: "TEST" });

                    // Assert
                    expect(wrapper.vm.valueToEmit).toEqual(["Bello"]);
                    expect(wrapper.vm.handleChange).toHaveBeenCalledWith(["Bello"]);
                });

                test("modelValue is not empty and does not contain this button's value => valueToEmit is correct value", () => {
                    // Arrange
                    const { wrapper } = setupMocks({
                        mockData: {
                            propsData: {
                                isMultiSelect: true,
                                value: "Bello",
                                modelValue: ["Hello"],
                            },
                        },
                    });

                    wrapper.vm.handleChange = jest.fn();

                    // Act
                    wrapper.vm.handleSelectionChange({ myEvent: "TEST" });

                    // Assert
                    expect(wrapper.vm.valueToEmit).toEqual(["Hello", "Bello"]);
                    expect(wrapper.vm.handleChange).toHaveBeenCalledWith(["Hello", "Bello"]);
                });

                // testing when value is at start/middle/end of modelValue
                const modelValues = [
                    [["Bello", "Hello", "Mello"]],
                    [["Hello", "Bello", "Mello"]],
                    [["Hello", "Mello", "Bello"]],
                ];
                test.each(modelValues)(
                    "modelValue is not empty and does contain this button's value => valueToEmit is correct value",
                    (modelValue) => {
                        // Arrange
                        const { wrapper } = setupMocks({
                            mockData: {
                                propsData: {
                                    isMultiSelect: true,
                                    value: "Bello",
                                    modelValue: modelValue,
                                },
                            },
                        });

                        wrapper.vm.handleChange = jest.fn();

                        // Act
                        wrapper.vm.handleSelectionChange({ myEvent: "TEST" });

                        // Assert
                        expect(wrapper.vm.valueToEmit).toEqual(["Hello", "Mello"]);
                        expect(wrapper.vm.handleChange).toHaveBeenCalledWith(["Hello", "Mello"]);
                    }
                );
            });

            describe("radio", () => {
                const modelValues = [null, undefined, "Bello", "Hello"];
                test.each(modelValues)(
                    "regardless of modelValue, set valueToEmit to correct value",
                    (modelValue) => {
                        // Arrange
                        const { wrapper } = setupMocks({
                            mockData: {
                                propsData: {
                                    isMultiSelect: false,
                                    value: "Bello",
                                    modelValue: modelValue,
                                },
                            },
                        });

                        wrapper.vm.handleChange = jest.fn();

                        // Act
                        wrapper.vm.handleSelectionChange({ myEvent: "TEST" });

                        // Assert
                        expect(wrapper.vm.valueToEmit).toEqual("Bello");
                        expect(wrapper.vm.handleChange).toHaveBeenCalledWith("Bello");
                    }
                );
            });
        });

        describe("handleClick", () => {
            const isCheckbox = [[true], [false]];
            test.each(isCheckbox)("handleSelectionChange is also called", async (isMultiSelect) => {
                // Arrange
                const { wrapper } = setupMocks({
                    mockData: {
                        propsData: {
                            isMultiSelect: isMultiSelect,
                            value: "Bello",
                        },
                    },
                });

                wrapper.vm.handleSelectionChange = jest.fn();
                await wrapper.setData({ valueToEmit: "HELLO WORLD" });

                // Act
                wrapper.vm.handleClick({ myEvent: "Test" });

                // Assert
                expect(wrapper.vm.handleSelectionChange).toHaveBeenCalledWith({ myEvent: "Test" });
            });

            test.each(isCheckbox)(
                "update:modelValue is emitted with valueToEmit",
                async (isMultiSelect) => {
                    // Arrange
                    const { wrapper } = setupMocks({
                        mockData: {
                            propsData: {
                                isMultiSelect: isMultiSelect,
                                value: "Bello",
                            },
                        },
                    });

                    wrapper.vm.handleSelectionChange = jest.fn();
                    await wrapper.setData({ valueToEmit: "HELLO WORLD" });

                    // Act
                    wrapper.vm.handleClick({ myEvent: "Test" });

                    // Assert
                    expect(wrapper.emitted()["update:modelValue"][0][0]).toEqual("HELLO WORLD");
                }
            );
        });

        describe("handlePushClickEventToGACheck", () => {
            test("called from click or click-like event => call pushClickEventToGA", () => {
                // Arrange
                const { wrapper } = setupMocks({});
                wrapper.vm.pushEventToGA = jest.fn();

                // Act
                wrapper.vm.handlePushClickEventToGACheck("click");

                // Assert
                expect(wrapper.vm.pushEventToGA).toHaveBeenCalledTimes(1);
            });

            describe("called from keyboard navigation/button-question-focus-helper", () => {
                test("valueToEmit is not null, is a checked radio without selectingInitiatesLoad, and the last value pushed to GA was not this input button's value => pushClickEventToGA is called", async () => {
                    // Arrange
                    const { wrapper } = setupMocks({
                        mockData: {
                            propsData: {
                                isMultiSelect: false,
                                modelValue: "value2",
                                value: "value2",
                                selectingInitiatesLoad: false,
                            },
                        },
                    });
                    wrapper.vm.pushEventToGA = jest.fn();
                    await wrapper.setData({
                        valueToEmit: "value2",
                        lastValuePushedToGa: "value1",
                    });

                    // Act
                    wrapper.vm.handlePushClickEventToGACheck();

                    // Assert
                    expect(wrapper.vm.isChecked).toBe(true);
                    expect(wrapper.find("input").attributes().type).toBe("radio");
                    expect(wrapper.vm.pushEventToGA).toHaveBeenCalled();
                });

                test("valueToEmit is not null, is a checked radio with selectingInitiatesLoad, and the last value pushed to GA was not this input button's value => pushClickEventToGA is called", async () => {
                    // Arrange
                    const { wrapper } = setupMocks({
                        mockData: {
                            propsData: {
                                isMultiSelect: false,
                                modelValue: "value2",
                                value: "value2",
                                selectingInitiatesLoad: true,
                            },
                        },
                    });
                    wrapper.vm.pushEventToGA = jest.fn();
                    await wrapper.setData({
                        valueToEmit: "value2",
                        lastValuePushedToGa: "value1",
                    });

                    // Act
                    wrapper.vm.handlePushClickEventToGACheck();

                    // Assert
                    expect(wrapper.vm.isChecked).toBe(true);
                    expect(wrapper.find("input").attributes().type).toBe("radio");
                    expect(wrapper.vm.pushEventToGA).not.toHaveBeenCalled();
                });

                test("valueToEmit is not null, is a checkbox that's checked, and the last value pushed to GA was not this input button's value => pushClickEventToGA is not called", async () => {
                    // Arrange
                    const { wrapper } = setupMocks({
                        mockData: {
                            propsData: {
                                isMultiSelect: true,
                                modelValue: ["value2"],
                                value: "value2",
                                selectingInitiatesLoad: false,
                            },
                        },
                    });
                    wrapper.vm.pushEventToGA = jest.fn();
                    await wrapper.setData({
                        valueToEmit: "value2",
                        lastValuePushedToGa: "value1",
                    });

                    // Act
                    wrapper.vm.handlePushClickEventToGACheck();

                    // Assert
                    expect(wrapper.vm.isChecked).toBe(true);
                    expect(wrapper.find("input").attributes().type).toBe("checkbox");
                    expect(wrapper.vm.pushEventToGA).not.toHaveBeenCalled();
                });

                test("valueToEmit is null => pushClickEventToGA not called", async () => {
                    // Arrange
                    const { wrapper } = setupMocks({
                        mockData: {
                            propsData: {
                                isMultiSelect: false,
                                modelValue: "value2",
                                value: "value2",
                                selectingInitiatesLoad: false,
                            },
                        },
                    });
                    wrapper.vm.pushEventToGA = jest.fn();
                    await wrapper.setData({
                        valueToEmit: null,
                        lastValuePushedToGa: "value1",
                    });

                    // Act
                    wrapper.vm.handlePushClickEventToGACheck();

                    // Assert
                    expect(wrapper.vm.isChecked).toBe(true);
                    expect(wrapper.find("input").attributes().type).toBe("radio");
                    expect(wrapper.vm.pushEventToGA).not.toHaveBeenCalled();
                });

                test("valueToEmit is not null, is an unchecked checkbox, and the last value pushed to GA was not this input button's value => pushClickEventToGA is not called", async () => {
                    // Arrange
                    const { wrapper } = setupMocks({
                        mockData: {
                            propsData: {
                                isMultiSelect: true,
                                modelValue: ["value1"],
                                value: "value2",
                                selectingInitiatesLoad: false,
                            },
                        },
                    });
                    wrapper.vm.pushEventToGA = jest.fn();
                    await wrapper.setData({
                        valueToEmit: "value2",
                        lastValuePushedToGa: "value1",
                    });

                    // Act
                    wrapper.vm.handlePushClickEventToGACheck();

                    // Assert
                    expect(wrapper.vm.isChecked).toBe(false);
                    expect(wrapper.find("input").attributes().type).toBe("checkbox");
                    expect(wrapper.vm.pushEventToGA).not.toHaveBeenCalled();
                });

                test("is an unchecked radio => pushClickEventToGA not called", async () => {
                    // Arrange
                    const { wrapper } = setupMocks({
                        mockData: {
                            propsData: {
                                isMultiSelect: false,
                                modelValue: "value1",
                                value: "value2",
                                selectingInitiatesLoad: false,
                            },
                        },
                    });
                    wrapper.vm.pushEventToGA = jest.fn();
                    await wrapper.setData({
                        valueToEmit: "value2",
                        lastValuePushedToGa: "value1",
                    });

                    // Act
                    wrapper.vm.handlePushClickEventToGACheck();

                    // Assert
                    expect(wrapper.vm.isChecked).toBe(false);
                    expect(wrapper.find("input").attributes().type).toBe("radio");
                    expect(wrapper.vm.pushEventToGA).not.toHaveBeenCalled();
                });

                test("lastValuePushedToGa is the same as this input button's value => pushClickEventToGA not called", async () => {
                    // Arrange
                    const { wrapper } = setupMocks({
                        mockData: {
                            propsData: {
                                isMultiSelect: false,
                                modelValue: "value2",
                                value: "value2",
                                selectingInitiatesLoad: false,
                            },
                        },
                    });
                    wrapper.vm.pushEventToGA = jest.fn();
                    await wrapper.setData({
                        valueToEmit: "value2",
                        lastValuePushedToGa: "value2",
                    });

                    // Act
                    wrapper.vm.handlePushClickEventToGACheck();

                    // Assert
                    expect(wrapper.vm.isChecked).toBe(true);
                    expect(wrapper.find("input").attributes().type).toBe("radio");
                    expect(wrapper.vm.pushEventToGA).not.toHaveBeenCalled();
                });
            });
        });

        describe("pushClickEventToGA", () => {
            test("pushEventToGA is called correctly", () => {
                // Arrange
                const { wrapper } = setupMocks({
                    mockData: {
                        propsData: {
                            value: "Bello",
                            setLastValuePushedToGa: jest.fn(),
                        },
                    },
                });

                wrapper.setData({
                    GaActions: {
                        CLICKED: "Clicked",
                    },
                });

                // Act
                wrapper.vm.pushClickEventToGA();

                // Assert
                expect(wrapper.vm.pushEventToGA).toHaveBeenCalledWith(
                    "myPage",
                    "Clicked",
                    "Bello",
                    true,
                    undefined
                );
                expect(wrapper.vm.setLastValuePushedToGa).toHaveBeenCalledWith("Bello");
            });

            test("pass in a value => setLastValuePushedToGa is called with value", () => {
                // Arrange
                const { wrapper } = setupMocks({
                    mockData: {
                        propsData: {
                            setLastValuePushedToGa: jest.fn(),
                        },
                    },
                });

                // Act
                wrapper.vm.pushClickEventToGA("hi there");

                // Assert
                expect(wrapper.vm.setLastValuePushedToGa).toHaveBeenCalledWith("hi there");
            });

            test("don't pass in a value => setLastValuePushedToGa is called with input button's value", () => {
                // Arrange
                const { wrapper } = setupMocks({
                    mockData: {
                        propsData: {
                            value: "hello there",
                            setLastValuePushedToGa: jest.fn(),
                        },
                    },
                });

                // Act
                wrapper.vm.pushClickEventToGA();

                // Assert
                expect(wrapper.vm.setLastValuePushedToGa).toHaveBeenCalledWith("hello there");
            });
        });
    });

    describe("computed", () => {
        describe("isChecked", () => {
            describe("checkbox", () => {
                const falsyModelValues = [[], null, undefined];
                test.each(falsyModelValues)(
                    "modelValue is falsy/empty => checkbox isn't checked",
                    (modelValue) => {
                        // Arrange/Act
                        const { wrapper } = setupMocks({
                            mockData: {
                                propsData: {
                                    isMultiSelect: true,
                                    modelValue,
                                },
                            },
                        });

                        // Assert
                        const inputElement = wrapper.find("input");
                        expect(wrapper.vm.isChecked).toEqual(false);
                        expect(inputElement.element.checked).toBe(false);
                    }
                );

                test("modelValue doesn't contain this button's value => checkbox isn't checked", async () => {
                    // Arrange/Act
                    const { wrapper } = setupMocks({
                        mockData: {
                            propsData: {
                                isMultiSelect: true,
                                modelValue: ["Aaa", "Bbb", "Ccc"],
                                value: "Ddd",
                            },
                        },
                    });

                    await wrapper.vm.$nextTick();

                    // Assert
                    const inputElement = wrapper.find("input");
                    expect(wrapper.vm.isChecked).toEqual(false);
                    expect(inputElement.element.checked).toBe(false);
                });

                test("modelValue contains this button's value => checkbox is checked", () => {
                    // Arrange/Act
                    const { wrapper } = setupMocks({
                        mockData: {
                            propsData: {
                                isMultiSelect: true,
                                modelValue: ["Aaa", "Bbb", "Ddd", "Ccc"],
                                value: "Ddd",
                            },
                        },
                    });

                    // Assert
                    const inputElement = wrapper.find("input");
                    expect(wrapper.vm.isChecked).toEqual(true);
                    expect(inputElement.element.checked).toBe(true);
                });
            });

            describe("radio", () => {
                const falsyModelValues = ["", null, undefined, []];
                test.each(falsyModelValues)(
                    "modelValue is falsy/empty => radio button isn't checked",
                    (modelValue) => {
                        // Arrange/Act
                        const { wrapper } = setupMocks({
                            mockData: {
                                propsData: {
                                    isMultiSelect: false,
                                    modelValue,
                                    value: "Aaa",
                                },
                            },
                        });

                        // Assert
                        const inputElement = wrapper.find("input");
                        expect(wrapper.vm.isChecked).toEqual(false);
                        expect(inputElement.element.checked).toBe(false);
                    }
                );

                test("modelValue equals this button's value => radio button is checked", () => {
                    // Arrange/Act
                    const { wrapper } = setupMocks({
                        mockData: {
                            propsData: {
                                isMultiSelect: false,
                                modelValue: "Ddd",
                                value: "Ddd",
                            },
                        },
                    });

                    // Assert
                    const inputElement = wrapper.find("input");
                    expect(wrapper.vm.isChecked).toEqual(true);
                    expect(inputElement.element.checked).toBe(true);
                });

                test("modelValue doesn't equal this button's value => radio button isn't checked", () => {
                    // Arrange/Act
                    const { wrapper } = setupMocks({
                        mockData: {
                            propsData: {
                                isMultiSelect: false,
                                modelValue: "Aaa",
                                value: "Ddd",
                            },
                        },
                    });

                    // Assert
                    const inputElement = wrapper.find("input");
                    expect(wrapper.vm.isChecked).toEqual(false);
                    expect(inputElement.element.checked).toBe(false);
                });
            });
        });

        describe("inputType", () => {
            test("isMultiSelect is true => inputType is 'checkbox'", () => {
                // Arrange/Act
                const { wrapper } = setupMocks({
                    mockData: {
                        propsData: {
                            isMultiSelect: true,
                        },
                    },
                });

                // Assert
                const inputElement = wrapper.find("input");
                expect(wrapper.vm.inputType).toEqual("checkbox");
                expect(inputElement.attributes().type).toBe("checkbox");
            });

            test("isMultiSelect is false => inputType is 'radio'", () => {
                // Arrange/Act
                const { wrapper } = setupMocks({
                    mockData: {
                        propsData: {
                            isMultiSelect: false,
                        },
                    },
                });

                // Assert
                const inputElement = wrapper.find("input");
                expect(wrapper.vm.inputType).toEqual("radio");
                expect(inputElement.attributes().type).toBe("radio");
            });
        });
    });
});

function setupMocks({ mockData = {}, shouldShallowMount = true }) {
    const mountMockData = {
        ...mockData,
        propsData: {
            // to get rid of some annoying warnings
            groupName: "groupName",
            modelValue: mockData.propsData?.isMultiSelect ? [] : "",
            value: "5",
            setLastValuePushedToGa: () => {},
            // should override the above if they exist
            ...mockData.propsData,
        },
    };

    const wrapper = shouldShallowMount
        ? shallowMount(baseInputButton, mountMockData)
        : mount(baseInputButton, mountMockData);

    wrapper.vm.pushEventToGA = jest.fn();
    wrapper.vm.$route = {
        query: {
            fmgPage: "myPage",
        },
    };
    wrapper.vm.GaActions = {};

    return { wrapper };
}
