import scrollbarSize, { reset } from "../scrollbarSize";

beforeEach(reset);

describe("scrollbarSize", () => {
  it("should calculate the scrollbar width by comparing the two divs' offsetWidths with cache", () => {
    const outerDiv = document.createElement("div");
    Object.defineProperty(outerDiv, "offsetWidth", {
      value: 100,
      writable: true,
    });
    Object.defineProperty(outerDiv, "offsetHeight", {
      value: 150,
      writable: true,
    });

    const innerDiv = document.createElement("div");
    Object.defineProperty(innerDiv, "offsetWidth", {
      value: 80,
      writable: true,
    });
    Object.defineProperty(innerDiv, "offsetHeight", {
      value: 100,
      writable: true,
    });

    let called = 0;
    const createElement = jest.spyOn(document, "createElement");
    createElement.mockImplementation(() => {
      const div = called % 2 === 0 ? outerDiv : innerDiv;
      called += 1;
      return div;
    });

    expect(scrollbarSize()).toBe(20);
    expect(scrollbarSize("width", false)).toBe(20);
    expect(scrollbarSize("width", true)).toBe(20);

    expect(scrollbarSize("height")).toBe(50);
    expect(scrollbarSize("height", false)).toBe(50);
    expect(scrollbarSize("height", true)).toBe(50);

    Object.defineProperty(outerDiv, "offsetWidth", {
      value: 100,
      writable: true,
    });
    Object.defineProperty(outerDiv, "offsetHeight", {
      value: 150,
      writable: true,
    });
    Object.defineProperty(innerDiv, "offsetWidth", {
      value: 90,
      writable: true,
    });
    Object.defineProperty(innerDiv, "offsetHeight", {
      value: 110,
      writable: true,
    });

    expect(scrollbarSize()).toBe(20);
    expect(scrollbarSize("width", false)).toBe(20);
    expect(scrollbarSize("width", true)).toBe(10);

    expect(scrollbarSize("height")).toBe(50);
    expect(scrollbarSize("height", false)).toBe(50);
    expect(scrollbarSize("height", true)).toBe(40);
    createElement.mockRestore();
  });

  it("should calculate the scrollbar width by creating two divs and attaching then removing from the body", () => {
    const outerDiv = document.createElement("div");
    const innerDiv = document.createElement("div");
    const createElement = jest.spyOn(document, "createElement");
    const bodyAppendChild = jest.spyOn(document.body, "appendChild");
    const bodyRemoveChild = jest.spyOn(document.body, "removeChild");

    createElement
      .mockImplementationOnce(() => outerDiv)
      .mockImplementationOnce(() => innerDiv);

    // jsdom doesn't initialize offsetWidths by default
    expect(outerDiv.offsetWidth).toBe(0);
    expect(innerDiv.offsetWidth).toBe(0);
    expect(scrollbarSize()).toBe(0);
    expect(outerDiv.style.visibility).toBe("hidden");
    expect(outerDiv.style.overflow).toBe("scroll");

    expect(outerDiv.firstChild).toBe(innerDiv);
    expect(bodyAppendChild).toBeCalledWith(outerDiv);
    expect(bodyRemoveChild).toBeCalledWith(outerDiv);
    createElement.mockRestore();
  });
});
