import { shallowMount } from "@vue/test-utils";
import { getMountOptions } from "@/helpers/unit-test-helper.js";
import alert from "./alert";

describe("alert.vue", () => {
    it("Should add class 'alert-dismissible' if isDismissible is true", async () => {
        // Arrange
        const wrapper = shallowMount(
            alert,
            setupMocks({
                propsData: {
                    isDismissible: true,
                    manualHeadline: "testHeader",
                    manualCopy: "testCopy",
                },
            })
        );
        const wrapperDiv = wrapper.find("div");

        // Assert
        expect(wrapperDiv.classes()).toContain("alert-dismissible");
    });

    it("Should add specified alert class", async () => {
        // Arrange
        const wrapper = shallowMount(
            alert,
            setupMocks({
                propsData: {
                    alertClass: "warning",
                    manualHeadline: "testHeader",
                    manualCopy: "testCopy",
                },
            })
        );

        const wrapperDiv = wrapper.find("div");

        // Assert
        expect(wrapperDiv.classes()).toContain("warning");
    });

    it("Should update alert Headline to manualHeadline datam entered and alert copy to manualCopy datam entered when no cmsWidgetName entered.", async () => {
        // Arrange
        const wrapper = shallowMount(alert, setupMocks({}));
        // Assert
        expect(wrapper.vm.alertHeadline).toBe("testHeader");
        expect(wrapper.vm.alertCopy).toBe("testCopy");
    });

    it("Should container a <textBlock> tag if the manualCopy contains a {routerLink: testName, testLink} placeholder", () => {
        // Arrange & Act
        const wrapper = shallowMount(
            alert,
            setupMocks({
                propsData: {
                    manualHeadline: "testHeader",
                    manualCopy: "testCopy with a {routerLink: testName, testLink} inside of it",
                },
                stubs: ["textBlock"],
            })
        );

        // Assert
        expect(wrapper.html()).toEqual(expect.stringContaining("text-block-stub"));
    });

    it("Should contain 'n+1' <p> tags if the body copy has 'n' <p> tags", () => {
        // Arrange & Act
        const wrapper = shallowMount(
            alert,
            setupMocks({
                propsData: {
                    manualHeadline: "testHeader",
                    manualCopy:
                        "<p>testCopy with a {routerLink: testName, testLink} inside of it</p><p>and two paragraphs</p>",
                },
                stubs: ["router-link"],
            })
        );
        // Assert
        expect(wrapper.findAll("p").length === 3).toBe(true);
    });

    it("Should call scrollIntoView() when the clientBoundingRect is not entirely in the viewport (out of view top)", () => {
        // Arrange
        var viewPortHeight = 200;
        setUpViewPort(viewPortHeight);

        Element.prototype.getBoundingClientRect = jest.fn(() => {
            return { top: -100, bottom: 200 };
        });

        var mockScrollIntoView = jest.fn();
        Element.prototype.scrollIntoView = mockScrollIntoView;

        // Act
        const wrapper = shallowMount(alert, setupMocks({}));

        // Assert
        // This is an implementation detail - we just need to test that the final step of snapping
        //   the window to the alert is working. If using a different function to accomplish that
        //   just swap this out with the new function
        expect(mockScrollIntoView).toHaveBeenCalled();
    });

    it("Should call scrollIntoView() when the clientBoundingRect is not entirely in the viewport (bottom is hidden behind footer)", () => {
        // Arrange
        var viewPortHeight = 240;
        setUpViewPort(viewPortHeight);

        Element.prototype.getBoundingClientRect = jest.fn(() => {
            return { top: 100, bottom: 200 };
        });

        var mockScrollIntoView = jest.fn();
        Element.prototype.scrollIntoView = mockScrollIntoView;

        // Act
        const wrapper = shallowMount(alert, setupMocks({}));

        // Assert
        // This is an implementation detail - we just need to test that the final step of snapping
        //   the window to the alert is working. If using a different function to accomplish that
        //   just swap this out with the new function
        expect(mockScrollIntoView).toHaveBeenCalled();
    });

    it("Should not call scrollIntoView() when the clientBoundingRect is not entirely in the viewport but 'shouldScrollToOnMount' is false", () => {
        // Arrange
        var viewPortHeight = 200;
        setUpViewPort(viewPortHeight);

        Element.prototype.getBoundingClientRect = jest.fn(() => {
            return { top: -100, bottom: 200 };
        });

        var mockScrollIntoView = jest.fn();
        Element.prototype.scrollIntoView = mockScrollIntoView;

        // Act
        const wrapper = shallowMount(
            alert,
            setupMocks({
                propsData: {
                    shouldScrollToOnMount: false,
                    manualHeadline: "testHeader",
                    manualCopy: "testCopy",
                },
            })
        );

        // Assert
        // This is an implementation detail - we just need to test that the final step of snapping
        //   the window to the alert is working. If using a different function to accomplish that
        //   just swap this out with the new function
        expect(mockScrollIntoView).not.toHaveBeenCalled();
    });

    it("Should not call scrollIntoView() when the clientBoundingRect is entirely in the viewport", () => {
        // Arrange
        var viewPortHeight = 500;
        setUpViewPort(viewPortHeight);

        Element.prototype.getBoundingClientRect = jest.fn(() => {
            return { top: 100, bottom: 200 };
        });

        var mockScrollIntoView = jest.fn();
        Element.prototype.scrollIntoView = mockScrollIntoView;

        // Act
        const wrapper = shallowMount(alert, setupMocks({}));

        // Assert
        // This is an implementation detail - we just need to test that the final step of snapping
        //   the window to the alert is working. If using a different function to accomplish that
        //   just swap this out with the new function
        expect(mockScrollIntoView).not.toHaveBeenCalled();
    });
});

const mockMixin = {
    methods: {
        getCmsContent: jest.fn(),
        getFooterInfoBoxHeight: jest.fn(() => 50),
    },
    computed: {
        dynamicStrings: jest.fn(() => {
            return { ROUTER_LINK: "routerLink:" };
        }),
    },
};

function setUpViewPort(height) {
    Object.defineProperty(global.window, "innerHeight", {
        writable: true,
        configurable: true,
        value: height,
    });

    Object.defineProperty(window.document.documentElement, "clientHeight", {
        writable: true,
        configurable: true,
        value: height,
    });
}

function setupMocks(mountOptionsMockData = {}) {
    const defaultMountOptions = {
        propsData: {
            manualHeadline: "testHeader",
            manualCopy: "testCopy",
        },
        mixins: [mockMixin],
    };
    const baseMountOptions = getMountOptions(
        Object.assign(defaultMountOptions, mountOptionsMockData)
    );
    const allMountOptions = Object.assign(defaultMountOptions, baseMountOptions);
    return allMountOptions;
}
