import { fireEvent, render } from "@testing-library/react";

import "../../../../__mocks__/resizeObserverMock";
import { Table } from "../Table";

describe("rendering Table", () => {
  const footers = [
    {
      col1: "footer1-1",
      col2: "footer1-2",
    },
  ];
  const headers = [
    {
      key: "col1",
      label: "header1",
      isSortable: true,
      cellProps: {
        className: "test-cellprop",
      },
    },
    {
      key: "col2",
      label: "header2",
      headCellProps: {
        className: "test-headcell",
      },
    },
  ];
  const rows = [
    {
      col1: "row1-1",
      col2: "row1-2",
    },
    {
      col1: "row2-1",
      col2: "row2-2",
      expand: [
        {
          col1: "row2-expand-1",
          col2: "row2-expand-2",
        },
      ],
    },
  ];
  describe("initial state", () => {
    it("renders table with class table", () => {
      const { getByTestId } = render(
        <Table data-testid="test-id" headers={[]} rows={[]} />,
      );
      expect(getByTestId("test-id").tagName).toBe("TABLE");
      expect(getByTestId("test-id")).toHaveClass("table");
    });
  });
  describe("testing headers", () => {
    it("renders headers", () => {
      const { getByText } = render(<Table headers={headers} rows={rows} />);
      expect(getByText("header1")).toBeInTheDocument();
      expect(getByText("header2")).toBeInTheDocument();
    });
    it("header1 has class test-cellprop", () => {
      const { getByText } = render(<Table headers={headers} rows={rows} />);
      expect(getByText("header1")).toBeInTheDocument();
      expect(getByText("header1")).toHaveClass("test-cellprop");
    });
    it("header1 has aria-sort attribute set to none", () => {
      const { getByText } = render(<Table headers={headers} rows={rows} />);
      expect(getByText("header1")).toBeInTheDocument();
      expect(getByText("header1").getAttribute("aria-sort")).toBe("none");
    });
    it("header1 has button with class table__sort-button", () => {
      const { getByText } = render(<Table headers={headers} rows={rows} />);
      expect(getByText("header1")).toBeInTheDocument();
      expect(getByText("header1").querySelector("button")).toBeInTheDocument();
      expect(getByText("header1").querySelector("button")).toHaveClass(
        "table__sort-button",
      );
    });
    it("header2 has class test-headcell", () => {
      const { getByText } = render(<Table headers={headers} rows={rows} />);
      expect(getByText("header2")).toBeInTheDocument();
      expect(getByText("header2")).toHaveClass("test-headcell");
    });
    it("has additional header cell with class table__cell--shrink when rows are expendable", () => {
      const { container } = render(<Table headers={headers} rows={rows} />);
      const headerCells = container.querySelectorAll("th");
      const exapandableColumn = headerCells[headerCells.length - 1];
      expect(exapandableColumn).toHaveClass("table__cell--shrink");
    });
  });
  describe("testing rows", () => {
    it("renders rows", () => {
      const { getByText } = render(<Table headers={headers} rows={rows} />);
      expect(getByText("row1-1")).toBeInTheDocument();
      expect(getByText("row1-2")).toBeInTheDocument();
      expect(getByText("row2-1")).toBeInTheDocument();
      expect(getByText("row2-2")).toBeInTheDocument();
    });
    it("renders expanded rows", () => {
      const { getByText } = render(<Table headers={headers} rows={rows} />);
      expect(getByText("row2-expand-1")).toBeInTheDocument();
      expect(getByText("row2-expand-2")).toBeInTheDocument();
    });
    it("rows in first column have class test-cellprop from header", () => {
      const { getByText } = render(<Table headers={headers} rows={rows} />);
      expect(getByText("row1-1")).toHaveClass("test-cellprop");
      expect(getByText("row2-1")).toHaveClass("test-cellprop");
      expect(getByText("row2-expand-1")).toHaveClass("test-cellprop");
    });
    it("expandable row has td cell with class table__cell--shrink", () => {
      const { container } = render(<Table headers={headers} rows={rows} />);
      expect(
        container
          .querySelector("tbody")
          .querySelectorAll("tr")[1]
          .querySelectorAll("td")[2],
      ).toHaveClass("table__cell--shrink");
    });
    it("td cell with class table__cell--shrink has button with generated aria-controls", () => {
      const { container } = render(<Table headers={headers} rows={rows} />);
      const ariaControl = container
        .querySelector("tbody")
        .querySelectorAll("tr")[1]
        .querySelectorAll("td")[2]
        .querySelector("button")
        .getAttribute("aria-controls");
      // React 18's useId() generates values with unique prefixes
      expect(ariaControl).toContain("-row-1-0");
    });
    it("td cell with class table__cell--shrink has button with text Zobraziť 1 súvisiaci riadok", () => {
      const { container } = render(<Table headers={headers} rows={rows} />);

      expect(
        container
          .querySelector("tbody")
          .querySelectorAll("tr")[1]
          .querySelectorAll("td")[2]
          .querySelector("button").textContent,
      ).toBe("Zobraziť 1 súvisiaci riadok");
    });
    it("expandable row has class table__subrow", () => {
      const { container } = render(<Table headers={headers} rows={rows} />);
      expect(
        container.querySelector("tbody").querySelectorAll("tr")[2],
      ).toHaveClass("table__subrow");
    });
    it("expandable row has attribute hidden", () => {
      const { container } = render(<Table headers={headers} rows={rows} />);
      expect(
        container.querySelector("tbody").querySelectorAll("tr")[2].hidden,
      ).toBe(true);
    });
    it("expandable row has id same as aria-controls attribute of corresponding expand button", () => {
      const { container } = render(<Table headers={headers} rows={rows} />);
      expect(
        container.querySelector("tbody").querySelectorAll("tr")[2].id,
      ).toBe(
        container
          .querySelector("tbody")
          .querySelectorAll("tr")[1]
          .querySelectorAll("td")[2]
          .querySelector("button")
          .getAttribute("aria-controls"),
      );
    });
  });
  describe("testing other props", () => {
    it("renders footer", () => {
      const { getByText } = render(
        <Table headers={headers} rows={[]} footers={footers} />,
      );
      expect(getByText("footer1-1")).toBeInTheDocument();
      expect(getByText("footer1-2")).toBeInTheDocument();
    });
    it("first footer column has class test-cellprop from header", () => {
      const { getByText } = render(
        <Table headers={headers} rows={[]} footers={footers} />,
      );
      expect(getByText("footer1-1")).toHaveClass("test-cellprop");
    });
    it("table has class table--striped when isStripped is passed", () => {
      const { getByTestId } = render(
        <Table data-testid="test-id" headers={[]} rows={[]} isStriped />,
      );
      expect(getByTestId("test-id").tagName).toBe("TABLE");
      expect(getByTestId("test-id")).toHaveClass("table");
      expect(getByTestId("test-id")).toHaveClass("table--striped");
    });
    it("table has class table--compact when isStripped is passed", () => {
      const { getByTestId } = render(
        <Table data-testid="test-id" headers={[]} rows={[]} isCompact />,
      );
      expect(getByTestId("test-id").tagName).toBe("TABLE");
      expect(getByTestId("test-id")).toHaveClass("table");
      expect(getByTestId("test-id")).toHaveClass("table--compact");
    });
    it("renders div wrapper with class table-scrollable when isScrollable is passed", () => {
      const { container } = render(
        <Table headers={[]} rows={[]} isScrollable />,
      );
      expect(container.getElementsByClassName("table-scrollable").length).toBe(
        1,
      );
      expect(
        container.getElementsByClassName("table-scrollable")[0].tagName,
      ).toBe("DIV");
    });
    it("div wrapper with class table-scrollable has role group", () => {
      const { container } = render(
        <Table headers={[]} rows={[]} isScrollable />,
      );
      expect(container.getElementsByClassName("table-scrollable").length).toBe(
        1,
      );
      expect(
        container
          .getElementsByClassName("table-scrollable")[0]
          .getAttribute("role"),
      ).toBe("group");
    });
    it("div wrapper with class table-scrollable has generated aria-labelledby", () => {
      const { container } = render(
        <Table headers={[]} rows={[]} isScrollable caption="text-caption" />,
      );
      const scrollableDiv =
        container.getElementsByClassName("table-scrollable")[0];
      expect(scrollableDiv).toBeTruthy();
      const ariaLabelledBy = scrollableDiv.getAttribute("aria-labelledby");
      expect(ariaLabelledBy).toBeTruthy();
      // React 18's useId() generates values with special format
      expect(ariaLabelledBy).toContain("-caption");
    });
    it("renders caption inside caption element", () => {
      const { container } = render(
        <Table headers={[]} rows={[]} caption="test-caption" />,
      );
      expect(container.querySelector("caption").textContent).toBe(
        "test-caption",
      );
    });
    it("renders caption inside caption element", () => {
      const { container } = render(
        <Table headers={[]} rows={[]} caption="test-caption" />,
      );
      expect(container.querySelector("caption").textContent).toBe(
        "test-caption",
      );
    });
  });
  describe("fire events", () => {
    it("expandable row is visible after its corresponding button click", () => {
      const { container } = render(<Table headers={headers} rows={rows} />);
      fireEvent.click(
        container
          .querySelector("tbody")
          .querySelectorAll("tr")[1]
          .querySelectorAll("td")[2]
          .querySelector("button"),
      );
      expect(
        container.querySelector("tbody").querySelectorAll("tr")[2].hidden,
      ).toBe(false);
    });
  });
});
