PaginatedTable.spec.tsx 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849
  1. /*
  2. * Copyright 2021-2024 Avaiga Private Limited
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
  5. * the License. You may obtain a copy of the License at
  6. *
  7. * http://www.apache.org/licenses/LICENSE-2.0
  8. *
  9. * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
  10. * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
  11. * specific language governing permissions and limitations under the License.
  12. */
  13. import React, { act } from "react";
  14. import { fireEvent, render, waitFor } from "@testing-library/react";
  15. import "@testing-library/jest-dom";
  16. import userEvent from "@testing-library/user-event";
  17. import PaginatedTable from "./PaginatedTable";
  18. import { TaipyContext } from "../../context/taipyContext";
  19. import { TaipyState, INITIAL_STATE } from "../../context/taipyReducers";
  20. import { TableValueType } from "./tableUtils";
  21. const valueKey = "0-99-Entity,Daily hospital occupancy-asc";
  22. const tableValue = {
  23. [valueKey]: {
  24. data: [
  25. {
  26. Day_str: "2020-04-01T00:00:00.000000Z",
  27. "Daily hospital occupancy": 856,
  28. Entity: "Austria",
  29. Code: "AUT",
  30. },
  31. {
  32. Day_str: "2020-04-02T00:00:00.000000Z",
  33. "Daily hospital occupancy": 823,
  34. Entity: "Austria",
  35. Code: "AUT",
  36. },
  37. {
  38. Day_str: "2020-04-03T00:00:00.000000Z",
  39. "Daily hospital occupancy": 829,
  40. Entity: "Austria",
  41. Code: "AUT",
  42. },
  43. {
  44. Day_str: "2020-04-04T00:00:00.000000Z",
  45. "Daily hospital occupancy": 826,
  46. Entity: "Austria",
  47. Code: "AUT",
  48. },
  49. {
  50. Day_str: "2020-04-05T00:00:00.000000Z",
  51. "Daily hospital occupancy": 712,
  52. Entity: "Austria",
  53. Code: "AUT",
  54. },
  55. {
  56. Day_str: "2020-04-06T00:00:00.000000Z",
  57. "Daily hospital occupancy": 824,
  58. Entity: "Austria",
  59. Code: "AUT",
  60. },
  61. {
  62. Day_str: "2020-04-07T00:00:00.000000Z",
  63. "Daily hospital occupancy": 857,
  64. Entity: "Austria",
  65. Code: "AUT",
  66. },
  67. {
  68. Day_str: "2020-04-08T00:00:00.000000Z",
  69. "Daily hospital occupancy": 829,
  70. Entity: "Austria",
  71. Code: "AUT",
  72. },
  73. {
  74. Day_str: "2020-04-09T00:00:00.000000Z",
  75. "Daily hospital occupancy": 820,
  76. Entity: "Austria",
  77. Code: "AUT",
  78. },
  79. {
  80. Day_str: "2020-04-10T00:00:00.000000Z",
  81. "Daily hospital occupancy": 771,
  82. Entity: "Austria",
  83. Code: "AUT",
  84. },
  85. ],
  86. rowcount: 14477,
  87. start: 0,
  88. },
  89. };
  90. const tableColumns = JSON.stringify({
  91. Entity: { dfid: "Entity" },
  92. "Daily hospital occupancy": { dfid: "Daily hospital occupancy", type: "int64" },
  93. });
  94. const tableWidthColumns = JSON.stringify({
  95. Entity: { dfid: "Entity", width: "100px" },
  96. "Daily hospital occupancy": { dfid: "Daily hospital occupancy", type: "int64" },
  97. });
  98. const changedValue = {
  99. [valueKey]: {
  100. data: [
  101. {
  102. Day_str: "2020-04-01T00:00:00.000000Z",
  103. "Daily hospital occupancy": 856,
  104. Entity: "Australia",
  105. Code: "AUS",
  106. },
  107. ],
  108. rowcount: 1,
  109. start: 0,
  110. },
  111. };
  112. const editableValue = {
  113. "0--1-bool,int,float,Code-asc": {
  114. data: [
  115. {
  116. bool: true,
  117. int: 856,
  118. float: 1.5,
  119. Code: "AUT",
  120. },
  121. {
  122. bool: false,
  123. int: 823,
  124. float: 2.5,
  125. Code: "ZZZ",
  126. },
  127. ],
  128. rowcount: 2,
  129. start: 0,
  130. },
  131. };
  132. const editableColumns = JSON.stringify({
  133. bool: { dfid: "bool", type: "bool", index: 0 },
  134. int: { dfid: "int", type: "int", index: 1 },
  135. float: { dfid: "float", type: "float", index: 2 },
  136. Code: { dfid: "Code", type: "str", index: 3 },
  137. });
  138. const buttonImgValue = {
  139. "0--1-bool,int,float,Code-asc": {
  140. data: [
  141. {
  142. bool: true,
  143. int: 856,
  144. float: 1.5,
  145. Code: "[Button Label](button action)",
  146. },
  147. {
  148. bool: false,
  149. int: 823,
  150. float: 2.5,
  151. Code: "ZZZ",
  152. },
  153. {
  154. bool: true,
  155. int: 478,
  156. float: 3.5,
  157. Code: "![Taipy!](https://docs.taipy.io/en/latest/assets/images/favicon.png)",
  158. },
  159. ],
  160. rowcount: 2,
  161. start: 0,
  162. },
  163. };
  164. const buttonColumns = JSON.stringify({
  165. bool: { dfid: "bool", type: "bool", index: 0 },
  166. int: { dfid: "int", type: "int", index: 1 },
  167. float: { dfid: "float", type: "float", index: 2 },
  168. Code: { dfid: "Code", type: "str", index: 3 },
  169. });
  170. const styledColumns = JSON.stringify({
  171. Entity: { dfid: "Entity" },
  172. "Daily hospital occupancy": {
  173. dfid: "Daily hospital occupancy",
  174. type: "int64",
  175. style: "some style function",
  176. tooltip: "some tooltip",
  177. formatFn: "someFormat"
  178. },
  179. });
  180. describe("PaginatedTable Component", () => {
  181. it("renders", async () => {
  182. const { getByText } = render(<PaginatedTable data={undefined} defaultColumns={tableColumns} />);
  183. const elt = getByText("Entity");
  184. expect(elt.tagName).toBe("DIV");
  185. });
  186. it("displays the right info for class", async () => {
  187. const { getByText } = render(
  188. <PaginatedTable data={undefined} defaultColumns={tableColumns} className="taipy-table" />
  189. );
  190. const elt = getByText("Entity").closest("table");
  191. expect(elt?.parentElement?.parentElement?.parentElement).toHaveClass("taipy-table", "taipy-table-paginated");
  192. });
  193. it("is disabled", async () => {
  194. const { getByText } = render(<PaginatedTable data={undefined} defaultColumns={tableColumns} active={false} />);
  195. const elt = getByText("Entity");
  196. expect(elt.parentElement).toHaveClass("Mui-disabled");
  197. });
  198. it("is enabled by default", async () => {
  199. const { getByText } = render(<PaginatedTable data={undefined} defaultColumns={tableColumns} />);
  200. const elt = getByText("Entity");
  201. expect(elt.parentElement).not.toHaveClass("Mui-disabled");
  202. });
  203. it("is enabled by active", async () => {
  204. const { getByText, getAllByTestId } = render(
  205. <PaginatedTable data={undefined} defaultColumns={tableColumns} active={true} />
  206. );
  207. const elt = getByText("Entity");
  208. expect(elt.parentElement).not.toHaveClass("Mui-disabled");
  209. expect(getAllByTestId("ArrowDownwardIcon").length).toBeGreaterThan(0);
  210. });
  211. it("Hides sort icons when not active", async () => {
  212. const { queryByTestId } = render(
  213. <PaginatedTable data={undefined} defaultColumns={tableColumns} active={false} />
  214. );
  215. expect(queryByTestId("ArrowDownwardIcon")).toBeNull();
  216. });
  217. it("Hides sort icons when not sortable", async () => {
  218. const { queryByTestId } = render(
  219. <PaginatedTable data={undefined} defaultColumns={tableColumns} sortable={false} />
  220. );
  221. expect(queryByTestId("ArrowDownwardIcon")).toBeNull();
  222. });
  223. it("set width if requested", async () => {
  224. const { getByText } = render(<PaginatedTable data={undefined} defaultColumns={tableWidthColumns} />);
  225. const header = getByText("Entity").closest("tr");
  226. expect(header?.firstChild).toHaveStyle({"min-width": "100px"});
  227. expect(header?.lastChild).toHaveStyle({"width": "100%"});
  228. });
  229. it("dispatch 2 well formed messages at first render", async () => {
  230. const dispatch = jest.fn();
  231. const state: TaipyState = INITIAL_STATE;
  232. render(
  233. <TaipyContext.Provider value={{ state, dispatch }}>
  234. <PaginatedTable
  235. id="table"
  236. data={undefined}
  237. defaultColumns={tableColumns}
  238. updateVars="varname=varname"
  239. />
  240. </TaipyContext.Provider>
  241. );
  242. expect(dispatch).toHaveBeenCalledWith({
  243. name: "",
  244. payload: { id: "table", names: ["varname"], refresh: false },
  245. type: "REQUEST_UPDATE",
  246. });
  247. expect(dispatch).toHaveBeenCalledWith({
  248. name: "",
  249. payload: {
  250. columns: ["Entity", "Daily hospital occupancy"],
  251. end: 99,
  252. id: "table",
  253. orderby: "",
  254. pagekey: valueKey,
  255. handlenan: false,
  256. sort: "asc",
  257. start: 0,
  258. aggregates: [],
  259. filters: [],
  260. },
  261. type: "REQUEST_DATA_UPDATE",
  262. });
  263. });
  264. it("dispatch a well formed message on sort", async () => {
  265. const dispatch = jest.fn();
  266. const state: TaipyState = INITIAL_STATE;
  267. const { getByText } = render(
  268. <TaipyContext.Provider value={{ state, dispatch }}>
  269. <PaginatedTable data={undefined} defaultColumns={tableColumns} />
  270. </TaipyContext.Provider>
  271. );
  272. const elt = getByText("Entity");
  273. await userEvent.click(elt);
  274. expect(dispatch).toHaveBeenCalledWith({
  275. name: "",
  276. payload: {
  277. columns: ["Entity", "Daily hospital occupancy"],
  278. end: 99,
  279. orderby: "Entity",
  280. pagekey: "0-99-Entity,Daily hospital occupancy-Entity-asc",
  281. handlenan: false,
  282. sort: "asc",
  283. start: 0,
  284. aggregates: [],
  285. filters: [],
  286. },
  287. type: "REQUEST_DATA_UPDATE",
  288. });
  289. });
  290. it("dispatch a well formed message on page change", async () => {
  291. const dispatch = jest.fn();
  292. const state: TaipyState = { ...INITIAL_STATE, data: { table: undefined } };
  293. const { getByLabelText, rerender } = render(
  294. <TaipyContext.Provider value={{ state, dispatch }}>
  295. <PaginatedTable
  296. id="table"
  297. data={state.data.table as undefined}
  298. defaultColumns={tableColumns}
  299. updateVars="varname=varname"
  300. />
  301. </TaipyContext.Provider>
  302. );
  303. const newState = { ...state, data: { ...state.data, table: tableValue } };
  304. rerender(
  305. <TaipyContext.Provider value={{ state: newState, dispatch }}>
  306. <PaginatedTable
  307. id="table"
  308. data={newState.data.table as TableValueType}
  309. defaultColumns={tableColumns}
  310. updateVars="varname=varname"
  311. />
  312. </TaipyContext.Provider>
  313. );
  314. const elt = getByLabelText("Go to next page");
  315. await userEvent.click(elt);
  316. expect(dispatch).toHaveBeenCalledWith({
  317. name: "",
  318. payload: {
  319. columns: ["Entity", "Daily hospital occupancy"],
  320. end: 199,
  321. id: "table",
  322. orderby: "",
  323. pagekey: "100-199-Entity,Daily hospital occupancy-asc",
  324. handlenan: false,
  325. sort: "asc",
  326. start: 100,
  327. aggregates: [],
  328. filters: [],
  329. },
  330. type: "REQUEST_DATA_UPDATE",
  331. });
  332. });
  333. it("displays the received data", async () => {
  334. const dispatch = jest.fn();
  335. const state: TaipyState = INITIAL_STATE;
  336. const { getAllByText, rerender } = render(
  337. <TaipyContext.Provider value={{ state, dispatch }}>
  338. <PaginatedTable data={undefined} defaultColumns={tableColumns} />
  339. </TaipyContext.Provider>
  340. );
  341. rerender(
  342. <TaipyContext.Provider value={{ state, dispatch }}>
  343. <PaginatedTable data={tableValue as TableValueType} defaultColumns={tableColumns} />
  344. </TaipyContext.Provider>
  345. );
  346. const elts = getAllByText("Austria");
  347. expect(elts.length).toBeGreaterThan(1);
  348. expect(elts[0].tagName).toBe("SPAN");
  349. });
  350. it("displays the refreshed data", async () => {
  351. const dispatch = jest.fn();
  352. const state: TaipyState = INITIAL_STATE;
  353. const { getAllByText, rerender, queryByText } = render(
  354. <TaipyContext.Provider value={{ state, dispatch }}>
  355. <PaginatedTable data={undefined} defaultColumns={tableColumns} />
  356. </TaipyContext.Provider>
  357. );
  358. rerender(
  359. <TaipyContext.Provider value={{ state, dispatch }}>
  360. <PaginatedTable data={tableValue as TableValueType} defaultColumns={tableColumns} />
  361. </TaipyContext.Provider>
  362. );
  363. expect(getAllByText("Austria").length).toBeGreaterThan(1);
  364. rerender(
  365. <TaipyContext.Provider value={{ state, dispatch }}>
  366. <PaginatedTable data={changedValue as TableValueType} defaultColumns={tableColumns} />
  367. </TaipyContext.Provider>
  368. );
  369. expect(queryByText("Austria")).toBeNull();
  370. expect(getAllByText("Australia").length).toBe(1);
  371. });
  372. it("selects the rows", async () => {
  373. const dispatch = jest.fn();
  374. const state: TaipyState = INITIAL_STATE;
  375. const selected = [2, 4, 6];
  376. const { findAllByText, rerender } = render(
  377. <TaipyContext.Provider value={{ state, dispatch }}>
  378. <PaginatedTable data={undefined} defaultColumns={tableColumns} />
  379. </TaipyContext.Provider>
  380. );
  381. rerender(
  382. <TaipyContext.Provider value={{ state: { ...state }, dispatch }}>
  383. <PaginatedTable selected={selected} data={tableValue as TableValueType} defaultColumns={tableColumns} />
  384. </TaipyContext.Provider>
  385. );
  386. const elts = await waitFor(() => findAllByText("Austria"));
  387. elts.forEach((elt: HTMLElement, idx: number) =>
  388. selected.indexOf(idx) == -1
  389. ? expect(elt.parentElement?.parentElement?.parentElement?.parentElement).not.toHaveClass("Mui-selected")
  390. : expect(elt.parentElement?.parentElement?.parentElement?.parentElement).toHaveClass("Mui-selected")
  391. );
  392. expect(document.querySelectorAll(".Mui-selected")).toHaveLength(selected.length);
  393. });
  394. describe("Edit Mode", () => {
  395. it("displays the data with edit buttons", async () => {
  396. const dispatch = jest.fn();
  397. const state: TaipyState = INITIAL_STATE;
  398. const { getAllByTestId, queryAllByTestId, rerender } = render(
  399. <TaipyContext.Provider value={{ state, dispatch }}>
  400. <PaginatedTable
  401. data={undefined}
  402. defaultColumns={editableColumns}
  403. editable={true}
  404. onEdit="onEdit"
  405. showAll={true}
  406. />
  407. </TaipyContext.Provider>
  408. );
  409. rerender(
  410. <TaipyContext.Provider value={{ state: { ...state }, dispatch }}>
  411. <PaginatedTable
  412. data={editableValue as TableValueType}
  413. defaultColumns={editableColumns}
  414. editable={true}
  415. onEdit="onEdit"
  416. showAll={true}
  417. />
  418. </TaipyContext.Provider>
  419. );
  420. expect(document.querySelectorAll(".MuiSwitch-root")).not.toHaveLength(0);
  421. expect(getAllByTestId("EditIcon")).not.toHaveLength(0);
  422. expect(queryAllByTestId("CheckIcon")).toHaveLength(0);
  423. expect(queryAllByTestId("ClearIcon")).toHaveLength(0);
  424. });
  425. it("can edit", async () => {
  426. const dispatch = jest.fn();
  427. const state: TaipyState = INITIAL_STATE;
  428. const { getByTestId, queryAllByTestId, getAllByTestId, rerender } = render(
  429. <TaipyContext.Provider value={{ state, dispatch }}>
  430. <PaginatedTable
  431. data={undefined}
  432. defaultColumns={editableColumns}
  433. editable={true}
  434. onEdit="onEdit"
  435. showAll={true}
  436. />
  437. </TaipyContext.Provider>
  438. );
  439. rerender(
  440. <TaipyContext.Provider value={{ state: { ...state }, dispatch }}>
  441. <PaginatedTable
  442. data={editableValue as TableValueType}
  443. defaultColumns={editableColumns}
  444. editable={true}
  445. onEdit="onEdit"
  446. showAll={true}
  447. />
  448. </TaipyContext.Provider>
  449. );
  450. const edits = getAllByTestId("EditIcon");
  451. await userEvent.click(edits[0]);
  452. const checkButton = getByTestId("CheckIcon");
  453. getByTestId("ClearIcon");
  454. await userEvent.click(checkButton);
  455. expect(queryAllByTestId("CheckIcon")).toHaveLength(0);
  456. expect(queryAllByTestId("ClearIcon")).toHaveLength(0);
  457. await userEvent.click(edits[1]);
  458. const clearButton = getByTestId("ClearIcon");
  459. const input = document.querySelector("input");
  460. expect(input).not.toBeNull();
  461. if (input) {
  462. if (input.type == "checkbox") {
  463. await userEvent.click(input);
  464. } else {
  465. await userEvent.type(input, "1");
  466. }
  467. }
  468. await userEvent.click(clearButton);
  469. expect(queryAllByTestId("CheckIcon")).toHaveLength(0);
  470. expect(queryAllByTestId("ClearIcon")).toHaveLength(0);
  471. dispatch.mockClear();
  472. await userEvent.click(edits[2]);
  473. await userEvent.click(getByTestId("CheckIcon"));
  474. expect(dispatch).toHaveBeenCalledWith({
  475. name: "",
  476. payload: {
  477. action: "onEdit",
  478. args: [],
  479. col: "float",
  480. index: 0,
  481. user_value: 1.5,
  482. value: 1.5,
  483. },
  484. type: "SEND_ACTION_ACTION",
  485. });
  486. await userEvent.click(edits[3]);
  487. const input2 = document.querySelector("input");
  488. expect(input2).not.toBeNull();
  489. if (input2) {
  490. if (input2.type == "checkbox") {
  491. await userEvent.click(input2);
  492. await userEvent.click(getByTestId("ClearIcon"));
  493. } else {
  494. await userEvent.type(input2, "{Esc}");
  495. }
  496. }
  497. dispatch.mockClear();
  498. await userEvent.click(edits[5]);
  499. const input3 = document.querySelector("input");
  500. expect(input3).not.toBeNull();
  501. if (input3) {
  502. if (input3.type == "checkbox") {
  503. await userEvent.click(input3);
  504. await userEvent.click(getByTestId("CheckIcon"));
  505. } else {
  506. await userEvent.type(input3, "{Enter}");
  507. }
  508. }
  509. expect(dispatch).toHaveBeenCalledWith({
  510. name: "",
  511. payload: {
  512. action: "onEdit",
  513. args: [],
  514. col: "int",
  515. index: 1,
  516. user_value: 823,
  517. value: 823,
  518. },
  519. type: "SEND_ACTION_ACTION",
  520. });
  521. });
  522. });
  523. it("can add", async () => {
  524. const dispatch = jest.fn();
  525. const state: TaipyState = INITIAL_STATE;
  526. const { getByTestId } = render(
  527. <TaipyContext.Provider value={{ state, dispatch }}>
  528. <PaginatedTable
  529. data={undefined}
  530. defaultColumns={editableColumns}
  531. showAll={true}
  532. editable={true}
  533. onAdd="onAdd"
  534. />
  535. </TaipyContext.Provider>
  536. );
  537. dispatch.mockClear();
  538. const addButton = getByTestId("AddIcon");
  539. await userEvent.click(addButton);
  540. expect(dispatch).toHaveBeenCalledWith({
  541. name: "",
  542. payload: {
  543. action: "onAdd",
  544. args: [],
  545. index: 0,
  546. },
  547. type: "SEND_ACTION_ACTION",
  548. });
  549. });
  550. it("can delete", async () => {
  551. const dispatch = jest.fn();
  552. const state: TaipyState = INITIAL_STATE;
  553. const { getAllByTestId, getByTestId, queryAllByTestId, rerender } = render(
  554. <TaipyContext.Provider value={{ state, dispatch }}>
  555. <PaginatedTable
  556. data={undefined}
  557. defaultColumns={editableColumns}
  558. showAll={true}
  559. editable={true}
  560. onDelete="onDelete"
  561. />
  562. </TaipyContext.Provider>
  563. );
  564. rerender(
  565. <TaipyContext.Provider value={{ state: { ...state }, dispatch }}>
  566. <PaginatedTable
  567. data={editableValue as TableValueType}
  568. defaultColumns={editableColumns}
  569. editable={true}
  570. showAll={true}
  571. onDelete="onDelete"
  572. />
  573. </TaipyContext.Provider>
  574. );
  575. let deleteButtons = getAllByTestId("DeleteIcon");
  576. expect(deleteButtons).not.toHaveLength(0);
  577. await userEvent.click(deleteButtons[0]);
  578. const checkButton = getByTestId("CheckIcon");
  579. getByTestId("ClearIcon");
  580. await userEvent.click(checkButton);
  581. expect(queryAllByTestId("CheckIcon")).toHaveLength(0);
  582. expect(queryAllByTestId("ClearIcon")).toHaveLength(0);
  583. await userEvent.click(deleteButtons[1]);
  584. const clearButton = getByTestId("ClearIcon");
  585. const input = document.querySelector("input");
  586. expect(input).not.toBeNull();
  587. input && (await userEvent.type(input, "1"));
  588. await userEvent.click(clearButton);
  589. expect(queryAllByTestId("CheckIcon")).toHaveLength(0);
  590. expect(queryAllByTestId("ClearIcon")).toHaveLength(0);
  591. await userEvent.click(deleteButtons[2]);
  592. const input3 = document.querySelector("input");
  593. expect(input3).not.toBeNull();
  594. input3 && (await userEvent.type(input3, "{esc}"));
  595. deleteButtons = getAllByTestId("DeleteIcon");
  596. dispatch.mockClear();
  597. await userEvent.click(deleteButtons[0]);
  598. const input2 = document.querySelector("input");
  599. expect(input2).not.toBeNull();
  600. input2 && (await userEvent.type(input2, "{enter}"));
  601. expect(dispatch).toHaveBeenCalledWith({
  602. name: "",
  603. payload: {
  604. action: "onDelete",
  605. args: [],
  606. index: 0,
  607. },
  608. type: "SEND_ACTION_ACTION",
  609. });
  610. });
  611. it("can select", async () => {
  612. const dispatch = jest.fn();
  613. const state: TaipyState = INITIAL_STATE;
  614. const { getByText, rerender } = render(
  615. <TaipyContext.Provider value={{ state, dispatch }}>
  616. <PaginatedTable data={undefined} defaultColumns={editableColumns} showAll={true} onAction="onSelect" />
  617. </TaipyContext.Provider>
  618. );
  619. rerender(
  620. <TaipyContext.Provider value={{ state: { ...state }, dispatch }}>
  621. <PaginatedTable
  622. data={editableValue as TableValueType}
  623. defaultColumns={editableColumns}
  624. showAll={true}
  625. onAction="onSelect"
  626. />
  627. </TaipyContext.Provider>
  628. );
  629. dispatch.mockClear();
  630. const elt = getByText("823");
  631. await userEvent.click(elt);
  632. expect(dispatch).toHaveBeenCalledWith({
  633. name: "",
  634. payload: {
  635. action: "onSelect",
  636. args: [],
  637. col: "int",
  638. index: 1,
  639. reason: "click",
  640. value: undefined,
  641. },
  642. type: "SEND_ACTION_ACTION",
  643. });
  644. });
  645. it("can click on button", async () => {
  646. const dispatch = jest.fn();
  647. const state: TaipyState = INITIAL_STATE;
  648. const { getByText, rerender } = render(
  649. <TaipyContext.Provider value={{ state, dispatch }}>
  650. <PaginatedTable data={undefined} defaultColumns={editableColumns} showAll={true} onAction="onSelect" />
  651. </TaipyContext.Provider>
  652. );
  653. rerender(
  654. <TaipyContext.Provider value={{ state: { ...state }, dispatch }}>
  655. <PaginatedTable
  656. data={buttonImgValue as TableValueType}
  657. defaultColumns={buttonColumns}
  658. showAll={true}
  659. />
  660. </TaipyContext.Provider>
  661. );
  662. dispatch.mockClear();
  663. const elt = getByText("Button Label");
  664. expect(elt.tagName).toBe("BUTTON");
  665. expect(elt).toBeDisabled();
  666. rerender(
  667. <TaipyContext.Provider value={{ state: { ...state }, dispatch }}>
  668. <PaginatedTable
  669. data={buttonImgValue as TableValueType}
  670. defaultColumns={buttonColumns}
  671. showAll={true}
  672. onAction="onSelect"
  673. />
  674. </TaipyContext.Provider>
  675. );
  676. dispatch.mockClear();
  677. const elt2 = getByText("Button Label");
  678. expect(elt2.tagName).toBe("BUTTON");
  679. await userEvent.click(elt2);
  680. expect(dispatch).toHaveBeenCalledWith({
  681. name: "",
  682. payload: {
  683. action: "onSelect",
  684. args: [],
  685. col: "Code",
  686. index: 0,
  687. reason: "button",
  688. value: "button action",
  689. },
  690. type: "SEND_ACTION_ACTION",
  691. });
  692. });
  693. it("can show an image", async () => {
  694. const dispatch = jest.fn();
  695. const state: TaipyState = INITIAL_STATE;
  696. const { getByAltText, rerender } = render(
  697. <TaipyContext.Provider value={{ state, dispatch }}>
  698. <PaginatedTable data={undefined} defaultColumns={editableColumns} showAll={true} onAction="onSelect" />
  699. </TaipyContext.Provider>
  700. );
  701. rerender(
  702. <TaipyContext.Provider value={{ state: { ...state }, dispatch }}>
  703. <PaginatedTable
  704. data={buttonImgValue as TableValueType}
  705. defaultColumns={buttonColumns}
  706. showAll={true}
  707. />
  708. </TaipyContext.Provider>
  709. );
  710. dispatch.mockClear();
  711. const elt = getByAltText("Taipy!");
  712. expect(elt.tagName).toBe("IMG");
  713. rerender(
  714. <TaipyContext.Provider value={{ state: { ...state }, dispatch }}>
  715. <PaginatedTable
  716. data={buttonImgValue as TableValueType}
  717. defaultColumns={buttonColumns}
  718. showAll={true}
  719. onAction="onSelect"
  720. />
  721. </TaipyContext.Provider>
  722. );
  723. dispatch.mockClear();
  724. const elt2 = getByAltText("Taipy!");
  725. expect(elt2.tagName).toBe("IMG");
  726. await userEvent.click(elt2);
  727. expect(dispatch).toHaveBeenCalledWith({
  728. name: "",
  729. payload: {
  730. action: "onSelect",
  731. args: [],
  732. col: "Code",
  733. index: 2,
  734. reason: "button",
  735. value: "Taipy!",
  736. },
  737. type: "SEND_ACTION_ACTION",
  738. });
  739. });
  740. it("should render correctly when style is applied to columns", async () => {
  741. const dispatch = jest.fn();
  742. const state: TaipyState = INITIAL_STATE;
  743. await waitFor(() => {
  744. render(
  745. <TaipyContext.Provider value={{ state, dispatch }}>
  746. <PaginatedTable
  747. data={tableValue}
  748. defaultColumns={styledColumns}
  749. rowClassName={"class_name=rows-bordered"}
  750. />
  751. </TaipyContext.Provider>
  752. );
  753. });
  754. const elt = document.querySelector('table[aria-labelledby="tableTitle"]');
  755. expect(elt).toBeInTheDocument();
  756. });
  757. it("should sort the table in ascending order", async () => {
  758. await waitFor(() => {
  759. render(<PaginatedTable data={tableValue} defaultColumns={tableColumns} />);
  760. });
  761. const elt = document.querySelector('svg[data-testid="ArrowDownwardIcon"]');
  762. act(() => {
  763. fireEvent.click(elt as Element);
  764. });
  765. expect(document.querySelector('th[aria-sort="ascending"]')).toBeInTheDocument();
  766. });
  767. it("should handle rows per page change", async () => {
  768. const { getByRole, queryByRole } = render(<PaginatedTable data={tableValue} defaultColumns={tableColumns} />);
  769. const rowsPerPageDropdown = getByRole("combobox");
  770. fireEvent.mouseDown(rowsPerPageDropdown);
  771. const option = queryByRole("option", { selected: false, name: "50" });
  772. fireEvent.click(option as Element);
  773. const table = document.querySelector(
  774. 'table[aria-labelledby="tableTitle"].MuiTable-root.MuiTable-stickyHeader'
  775. );
  776. expect(table).toBeInTheDocument();
  777. });
  778. it("should allow all rows", async () => {
  779. const { getByRole, queryByRole } = render(
  780. <PaginatedTable data={tableValue} defaultColumns={tableColumns} allowAllRows={true} />
  781. );
  782. const rowsPerPageDropdown = getByRole("combobox");
  783. fireEvent.mouseDown(rowsPerPageDropdown);
  784. const option = queryByRole("option", { selected: false, name: "All" });
  785. expect(option).toBeInTheDocument();
  786. });
  787. it("should display row per page correctly", async () => {
  788. const { getByRole, queryByRole } = render(
  789. <PaginatedTable
  790. data={tableValue}
  791. defaultColumns={tableColumns}
  792. pageSizeOptions={JSON.stringify([10, 20, 30])}
  793. />
  794. );
  795. const rowsPerPageDropdown = getByRole("combobox");
  796. fireEvent.mouseDown(rowsPerPageDropdown);
  797. const option = queryByRole("option", { selected: false, name: "10" });
  798. expect(option).toBeInTheDocument();
  799. });
  800. it("logs error when pageSizeOptions prop is invalid", () => {
  801. // Create a spy on console.log
  802. const logSpy = jest.spyOn(console, "log");
  803. // Render the component with invalid pageSizeOptions prop
  804. render(<PaginatedTable data={tableValue} defaultColumns={tableColumns} pageSizeOptions={"not a valid json"} />);
  805. // Check if console.log was called with the expected arguments
  806. expect(logSpy).toHaveBeenCalledWith(
  807. "PaginatedTable pageSizeOptions is wrong ",
  808. "not a valid json",
  809. expect.any(Error)
  810. );
  811. // Clean up the spy
  812. logSpy.mockRestore();
  813. });
  814. });