/* * Copyright 2023 Avaiga Private Limited * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the * specific language governing permissions and limitations under the License. */ import React from "react"; import { render, waitFor } from "@testing-library/react"; import "@testing-library/jest-dom"; import userEvent from "@testing-library/user-event"; import PaginatedTable from "./PaginatedTable"; import { TaipyContext } from "../../context/taipyContext"; import { TaipyState, INITIAL_STATE } from "../../context/taipyReducers"; import { TableValueType } from "./tableUtils"; const valueKey = "0-99-Entity,Daily hospital occupancy--asc"; const tableValue = { [valueKey]: { data: [ { Day_str: "2020-04-01T00:00:00.000000Z", "Daily hospital occupancy": 856, Entity: "Austria", Code: "AUT", }, { Day_str: "2020-04-02T00:00:00.000000Z", "Daily hospital occupancy": 823, Entity: "Austria", Code: "AUT", }, { Day_str: "2020-04-03T00:00:00.000000Z", "Daily hospital occupancy": 829, Entity: "Austria", Code: "AUT", }, { Day_str: "2020-04-04T00:00:00.000000Z", "Daily hospital occupancy": 826, Entity: "Austria", Code: "AUT", }, { Day_str: "2020-04-05T00:00:00.000000Z", "Daily hospital occupancy": 712, Entity: "Austria", Code: "AUT", }, { Day_str: "2020-04-06T00:00:00.000000Z", "Daily hospital occupancy": 824, Entity: "Austria", Code: "AUT", }, { Day_str: "2020-04-07T00:00:00.000000Z", "Daily hospital occupancy": 857, Entity: "Austria", Code: "AUT", }, { Day_str: "2020-04-08T00:00:00.000000Z", "Daily hospital occupancy": 829, Entity: "Austria", Code: "AUT", }, { Day_str: "2020-04-09T00:00:00.000000Z", "Daily hospital occupancy": 820, Entity: "Austria", Code: "AUT", }, { Day_str: "2020-04-10T00:00:00.000000Z", "Daily hospital occupancy": 771, Entity: "Austria", Code: "AUT", }, ], rowcount: 14477, start: 0, }, }; const tableColumns = JSON.stringify({ Entity: { dfid: "Entity" }, "Daily hospital occupancy": { dfid: "Daily hospital occupancy", type: "int64" }, }); const editableValue = { "0--1-bool,int,float,Code--asc": { data: [ { bool: true, int: 856, float: 1.5, Code: "AUT", }, { bool: false, int: 823, float: 2.5, Code: "ZZZ", }, ], rowcount: 2, start: 0, }, }; const editableColumns = JSON.stringify({ bool: { dfid: "bool", type: "bool", index: 0 }, int: { dfid: "int", type: "int", index: 1 }, float: { dfid: "float", type: "float", index: 2 }, Code: { dfid: "Code", type: "str", index: 3 }, }); describe("PaginatedTable Component", () => { it("renders", async () => { const { getByText } = render(); const elt = getByText("Entity"); expect(elt.tagName).toBe("DIV"); }); it("displays the right info for class", async () => { const { getByText } = render( ); const elt = getByText("Entity").closest("table"); expect(elt?.parentElement?.parentElement?.parentElement).toHaveClass("taipy-table", "taipy-table-paginated"); }); it("is disabled", async () => { const { getByText } = render(); const elt = getByText("Entity"); expect(elt.parentElement).toHaveClass("Mui-disabled"); }); it("is enabled by default", async () => { const { getByText } = render(); const elt = getByText("Entity"); expect(elt.parentElement).not.toHaveClass("Mui-disabled"); }); it("is enabled by active", async () => { const { getByText, getAllByTestId } = render(); const elt = getByText("Entity"); expect(elt.parentElement).not.toHaveClass("Mui-disabled"); expect(getAllByTestId("ArrowDownwardIcon").length).toBeGreaterThan(0); }); it("Hides sort icons when not active", async () => { const { queryByTestId } = render(); expect(queryByTestId("ArrowDownwardIcon")).toBeNull(); }); it("dispatch 2 well formed messages at first render", async () => { const dispatch = jest.fn(); const state: TaipyState = INITIAL_STATE; render( ); expect(dispatch).toHaveBeenCalledWith({ name: "", payload: { id: "table", names: ["varname"], refresh: false }, type: "REQUEST_UPDATE", }); expect(dispatch).toHaveBeenCalledWith({ name: "", payload: { columns: ["Entity", "Daily hospital occupancy"], end: 99, id: "table", orderby: "", pagekey: valueKey, handlenan: false, sort: "asc", start: 0, aggregates: [], applies: undefined, styles: undefined, tooltips: undefined, filters: [], }, type: "REQUEST_DATA_UPDATE", }); }); it("dispatch a well formed message on sort", async () => { const dispatch = jest.fn(); const state: TaipyState = INITIAL_STATE; const { getByText } = render( ); const elt = getByText("Entity"); await userEvent.click(elt); expect(dispatch).toHaveBeenCalledWith({ name: "", payload: { columns: ["Entity", "Daily hospital occupancy"], end: 99, id: undefined, orderby: "Entity", pagekey: "0-99-Entity,Daily hospital occupancy-Entity-asc", handlenan: false, sort: "asc", start: 0, aggregates: [], applies: undefined, styles: undefined, tooltips: undefined, filters: [], }, type: "REQUEST_DATA_UPDATE", }); }); it("dispatch a well formed message on page change", async () => { const dispatch = jest.fn(); const state: TaipyState = { ...INITIAL_STATE, data: { table: undefined } }; const { getByLabelText, rerender } = render( ); const newState = { ...state, data: { ...state.data, table: tableValue } }; rerender( ); const elt = getByLabelText("Go to next page"); await userEvent.click(elt); expect(dispatch).toHaveBeenCalledWith({ name: "", payload: { columns: ["Entity", "Daily hospital occupancy"], end: 199, id: "table", orderby: "", pagekey: "100-199-Entity,Daily hospital occupancy--asc", handlenan: false, sort: "asc", start: 100, aggregates: [], applies: undefined, styles: undefined, tooltips: undefined, filters: [], }, type: "REQUEST_DATA_UPDATE", }); }); it("displays the received data", async () => { const dispatch = jest.fn(); const state: TaipyState = INITIAL_STATE; const { getAllByText, rerender } = render( ); rerender( ); const elts = getAllByText("Austria"); expect(elts.length).toBeGreaterThan(1); expect(elts[0].tagName).toBe("SPAN"); }); it("selects the rows", async () => { const dispatch = jest.fn(); const state: TaipyState = INITIAL_STATE; const selected = [2, 4, 6]; const { findAllByText, rerender } = render( ); rerender( ); const elts = await waitFor(() => findAllByText("Austria")); elts.forEach((elt: HTMLElement, idx: number) => selected.indexOf(idx) == -1 ? expect(elt.parentElement?.parentElement?.parentElement).not.toHaveClass("Mui-selected") : expect(elt.parentElement?.parentElement?.parentElement).toHaveClass("Mui-selected") ); expect(document.querySelectorAll(".Mui-selected")).toHaveLength(selected.length); }); describe("Edit Mode", () => { it("displays the data with edit buttons", async () => { const dispatch = jest.fn(); const state: TaipyState = INITIAL_STATE; const { getAllByTestId, queryAllByTestId, rerender } = render( ); rerender( ); expect(document.querySelectorAll(".MuiSwitch-root")).not.toHaveLength(0); expect(getAllByTestId("EditIcon")).not.toHaveLength(0); expect(queryAllByTestId("CheckIcon")).toHaveLength(0); expect(queryAllByTestId("ClearIcon")).toHaveLength(0); }); it("can edit", async () => { const dispatch = jest.fn(); const state: TaipyState = INITIAL_STATE; const { getByTestId, queryAllByTestId, getAllByTestId, rerender } = render( ); rerender( ); const edits = getAllByTestId("EditIcon"); await userEvent.click(edits[0]); const checkButton = getByTestId("CheckIcon"); getByTestId("ClearIcon"); await userEvent.click(checkButton); expect(queryAllByTestId("CheckIcon")).toHaveLength(0); expect(queryAllByTestId("ClearIcon")).toHaveLength(0); await userEvent.click(edits[1]); const clearButton = getByTestId("ClearIcon"); const input = document.querySelector("input"); expect(input).not.toBeNull(); if (input) { if (input.type == "checkbox") { await userEvent.click(input); } else { await userEvent.type(input, "1"); } } await userEvent.click(clearButton); expect(queryAllByTestId("CheckIcon")).toHaveLength(0); expect(queryAllByTestId("ClearIcon")).toHaveLength(0); dispatch.mockClear(); await userEvent.click(edits[2]); await userEvent.click(getByTestId("CheckIcon")); expect(dispatch).toHaveBeenCalledWith({ name: "", payload: { action: "onEdit", args: [], col: "float", index: 0, user_value: 1.5, value: 1.5, }, type: "SEND_ACTION_ACTION", }); await userEvent.click(edits[3]); const input2 = document.querySelector("input"); expect(input2).not.toBeNull(); if (input2) { if (input2.type == "checkbox") { await userEvent.click(input2); await userEvent.click(getByTestId("ClearIcon")); } else { await userEvent.type(input2, "{Esc}"); } } dispatch.mockClear(); await userEvent.click(edits[5]); const input3 = document.querySelector("input"); expect(input3).not.toBeNull(); if (input3) { if (input3.type == "checkbox") { await userEvent.click(input3); await userEvent.click(getByTestId("CheckIcon")); } else { await userEvent.type(input3, "{Enter}"); } } expect(dispatch).toHaveBeenCalledWith({ name: "", payload: { action: "onEdit", args: [], col: "int", index: 1, user_value: 823, value: 823, }, type: "SEND_ACTION_ACTION", }); }); }); it("can add", async () => { const dispatch = jest.fn(); const state: TaipyState = INITIAL_STATE; const { getByTestId } = render( ); dispatch.mockClear(); const addButton = getByTestId("AddIcon"); await userEvent.click(addButton); expect(dispatch).toHaveBeenCalledWith({ name: "", payload: { action: "onAdd", args: [], index: 0, }, type: "SEND_ACTION_ACTION", }); }); it("can delete", async () => { const dispatch = jest.fn(); const state: TaipyState = INITIAL_STATE; const { getAllByTestId, getByTestId, queryAllByTestId, rerender } = render( ); rerender( ); let deleteButtons = getAllByTestId("DeleteIcon"); expect(deleteButtons).not.toHaveLength(0); await userEvent.click(deleteButtons[0]); const checkButton = getByTestId("CheckIcon"); getByTestId("ClearIcon"); await userEvent.click(checkButton); expect(queryAllByTestId("CheckIcon")).toHaveLength(0); expect(queryAllByTestId("ClearIcon")).toHaveLength(0); await userEvent.click(deleteButtons[1]); const clearButton = getByTestId("ClearIcon"); const input = document.querySelector("input"); expect(input).not.toBeNull(); input && (await userEvent.type(input, "1")); await userEvent.click(clearButton); expect(queryAllByTestId("CheckIcon")).toHaveLength(0); expect(queryAllByTestId("ClearIcon")).toHaveLength(0); await userEvent.click(deleteButtons[2]); const input3 = document.querySelector("input"); expect(input3).not.toBeNull(); input3 && (await userEvent.type(input3, "{esc}")); deleteButtons = getAllByTestId("DeleteIcon"); dispatch.mockClear(); await userEvent.click(deleteButtons[0]); const input2 = document.querySelector("input"); expect(input2).not.toBeNull(); input2 && (await userEvent.type(input2, "{enter}")); expect(dispatch).toHaveBeenCalledWith({ name: "", payload: { action: "onDelete", args: [], index: 0, }, type: "SEND_ACTION_ACTION", }); }); it("can select", async () => { const dispatch = jest.fn(); const state: TaipyState = INITIAL_STATE; const { getByText, rerender } = render( ); rerender( ); dispatch.mockClear(); const elt = getByText("823"); await userEvent.click(elt); expect(dispatch).toHaveBeenCalledWith({ name: "", payload: { action: "onSelect", args: [], col: "int", index: 1, }, type: "SEND_ACTION_ACTION", }); }); });