PaginatedTable.spec.tsx 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770
  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 changedValue = {
  95. [valueKey]: {
  96. data: [
  97. {
  98. Day_str: "2020-04-01T00:00:00.000000Z",
  99. "Daily hospital occupancy": 856,
  100. Entity: "Australia",
  101. Code: "AUS",
  102. },
  103. ],
  104. rowcount: 1,
  105. start: 0,
  106. },
  107. };
  108. const editableValue = {
  109. "0--1-bool,int,float,Code--asc": {
  110. data: [
  111. {
  112. bool: true,
  113. int: 856,
  114. float: 1.5,
  115. Code: "AUT",
  116. },
  117. {
  118. bool: false,
  119. int: 823,
  120. float: 2.5,
  121. Code: "ZZZ",
  122. },
  123. ],
  124. rowcount: 2,
  125. start: 0,
  126. },
  127. };
  128. const editableColumns = JSON.stringify({
  129. bool: { dfid: "bool", type: "bool", index: 0 },
  130. int: { dfid: "int", type: "int", index: 1 },
  131. float: { dfid: "float", type: "float", index: 2 },
  132. Code: { dfid: "Code", type: "str", index: 3 },
  133. });
  134. const buttonValue = {
  135. "0--1-bool,int,float,Code--asc": {
  136. data: [
  137. {
  138. bool: true,
  139. int: 856,
  140. float: 1.5,
  141. Code: "[Button Label](button action)",
  142. },
  143. {
  144. bool: false,
  145. int: 823,
  146. float: 2.5,
  147. Code: "ZZZ",
  148. },
  149. ],
  150. rowcount: 2,
  151. start: 0,
  152. },
  153. };
  154. const buttonColumns = JSON.stringify({
  155. bool: { dfid: "bool", type: "bool", index: 0 },
  156. int: { dfid: "int", type: "int", index: 1 },
  157. float: { dfid: "float", type: "float", index: 2 },
  158. Code: { dfid: "Code", type: "str", index: 3 },
  159. });
  160. const styledColumns = JSON.stringify({
  161. Entity: { dfid: "Entity" },
  162. "Daily hospital occupancy": {
  163. dfid: "Daily hospital occupancy",
  164. type: "int64",
  165. style: "some style function",
  166. tooltip: "some tooltip",
  167. },
  168. });
  169. describe("PaginatedTable Component", () => {
  170. it("renders", async () => {
  171. const { getByText } = render(<PaginatedTable data={undefined} defaultColumns={tableColumns} />);
  172. const elt = getByText("Entity");
  173. expect(elt.tagName).toBe("DIV");
  174. });
  175. it("displays the right info for class", async () => {
  176. const { getByText } = render(
  177. <PaginatedTable data={undefined} defaultColumns={tableColumns} className="taipy-table" />
  178. );
  179. const elt = getByText("Entity").closest("table");
  180. expect(elt?.parentElement?.parentElement?.parentElement).toHaveClass("taipy-table", "taipy-table-paginated");
  181. });
  182. it("is disabled", async () => {
  183. const { getByText } = render(<PaginatedTable data={undefined} defaultColumns={tableColumns} active={false} />);
  184. const elt = getByText("Entity");
  185. expect(elt.parentElement).toHaveClass("Mui-disabled");
  186. });
  187. it("is enabled by default", async () => {
  188. const { getByText } = render(<PaginatedTable data={undefined} defaultColumns={tableColumns} />);
  189. const elt = getByText("Entity");
  190. expect(elt.parentElement).not.toHaveClass("Mui-disabled");
  191. });
  192. it("is enabled by active", async () => {
  193. const { getByText, getAllByTestId } = render(
  194. <PaginatedTable data={undefined} defaultColumns={tableColumns} active={true} />
  195. );
  196. const elt = getByText("Entity");
  197. expect(elt.parentElement).not.toHaveClass("Mui-disabled");
  198. expect(getAllByTestId("ArrowDownwardIcon").length).toBeGreaterThan(0);
  199. });
  200. it("Hides sort icons when not active", async () => {
  201. const { queryByTestId } = render(
  202. <PaginatedTable data={undefined} defaultColumns={tableColumns} active={false} />
  203. );
  204. expect(queryByTestId("ArrowDownwardIcon")).toBeNull();
  205. });
  206. it("dispatch 2 well formed messages at first render", async () => {
  207. const dispatch = jest.fn();
  208. const state: TaipyState = INITIAL_STATE;
  209. render(
  210. <TaipyContext.Provider value={{ state, dispatch }}>
  211. <PaginatedTable
  212. id="table"
  213. data={undefined}
  214. defaultColumns={tableColumns}
  215. updateVars="varname=varname"
  216. />
  217. </TaipyContext.Provider>
  218. );
  219. expect(dispatch).toHaveBeenCalledWith({
  220. name: "",
  221. payload: { id: "table", names: ["varname"], refresh: false },
  222. type: "REQUEST_UPDATE",
  223. });
  224. expect(dispatch).toHaveBeenCalledWith({
  225. name: "",
  226. payload: {
  227. columns: ["Entity", "Daily hospital occupancy"],
  228. end: 99,
  229. id: "table",
  230. orderby: "",
  231. pagekey: valueKey,
  232. handlenan: false,
  233. sort: "asc",
  234. start: 0,
  235. aggregates: [],
  236. applies: undefined,
  237. styles: undefined,
  238. tooltips: undefined,
  239. filters: [],
  240. },
  241. type: "REQUEST_DATA_UPDATE",
  242. });
  243. });
  244. it("dispatch a well formed message on sort", async () => {
  245. const dispatch = jest.fn();
  246. const state: TaipyState = INITIAL_STATE;
  247. const { getByText } = render(
  248. <TaipyContext.Provider value={{ state, dispatch }}>
  249. <PaginatedTable data={undefined} defaultColumns={tableColumns} />
  250. </TaipyContext.Provider>
  251. );
  252. const elt = getByText("Entity");
  253. await userEvent.click(elt);
  254. expect(dispatch).toHaveBeenCalledWith({
  255. name: "",
  256. payload: {
  257. columns: ["Entity", "Daily hospital occupancy"],
  258. end: 99,
  259. id: undefined,
  260. orderby: "Entity",
  261. pagekey: "0-99-Entity,Daily hospital occupancy-Entity-asc",
  262. handlenan: false,
  263. sort: "asc",
  264. start: 0,
  265. aggregates: [],
  266. applies: undefined,
  267. styles: undefined,
  268. tooltips: undefined,
  269. filters: [],
  270. },
  271. type: "REQUEST_DATA_UPDATE",
  272. });
  273. });
  274. it("dispatch a well formed message on page change", async () => {
  275. const dispatch = jest.fn();
  276. const state: TaipyState = { ...INITIAL_STATE, data: { table: undefined } };
  277. const { getByLabelText, rerender } = render(
  278. <TaipyContext.Provider value={{ state, dispatch }}>
  279. <PaginatedTable
  280. id="table"
  281. data={state.data.table as undefined}
  282. defaultColumns={tableColumns}
  283. updateVars="varname=varname"
  284. />
  285. </TaipyContext.Provider>
  286. );
  287. const newState = { ...state, data: { ...state.data, table: tableValue } };
  288. rerender(
  289. <TaipyContext.Provider value={{ state: newState, dispatch }}>
  290. <PaginatedTable
  291. id="table"
  292. data={newState.data.table as TableValueType}
  293. defaultColumns={tableColumns}
  294. updateVars="varname=varname"
  295. />
  296. </TaipyContext.Provider>
  297. );
  298. const elt = getByLabelText("Go to next page");
  299. await userEvent.click(elt);
  300. expect(dispatch).toHaveBeenCalledWith({
  301. name: "",
  302. payload: {
  303. columns: ["Entity", "Daily hospital occupancy"],
  304. end: 199,
  305. id: "table",
  306. orderby: "",
  307. pagekey: "100-199-Entity,Daily hospital occupancy--asc",
  308. handlenan: false,
  309. sort: "asc",
  310. start: 100,
  311. aggregates: [],
  312. applies: undefined,
  313. styles: undefined,
  314. tooltips: undefined,
  315. filters: [],
  316. },
  317. type: "REQUEST_DATA_UPDATE",
  318. });
  319. });
  320. it("displays the received data", async () => {
  321. const dispatch = jest.fn();
  322. const state: TaipyState = INITIAL_STATE;
  323. const { getAllByText, rerender } = render(
  324. <TaipyContext.Provider value={{ state, dispatch }}>
  325. <PaginatedTable data={undefined} defaultColumns={tableColumns} />
  326. </TaipyContext.Provider>
  327. );
  328. rerender(
  329. <TaipyContext.Provider value={{ state, dispatch }}>
  330. <PaginatedTable data={tableValue as TableValueType} defaultColumns={tableColumns} />
  331. </TaipyContext.Provider>
  332. );
  333. const elts = getAllByText("Austria");
  334. expect(elts.length).toBeGreaterThan(1);
  335. expect(elts[0].tagName).toBe("SPAN");
  336. });
  337. it("displays the refreshed data", async () => {
  338. const dispatch = jest.fn();
  339. const state: TaipyState = INITIAL_STATE;
  340. const { getAllByText, rerender, queryByText } = render(
  341. <TaipyContext.Provider value={{ state, dispatch }}>
  342. <PaginatedTable data={undefined} defaultColumns={tableColumns} />
  343. </TaipyContext.Provider>
  344. );
  345. rerender(
  346. <TaipyContext.Provider value={{ state, dispatch }}>
  347. <PaginatedTable data={tableValue as TableValueType} defaultColumns={tableColumns} />
  348. </TaipyContext.Provider>
  349. );
  350. expect(getAllByText("Austria").length).toBeGreaterThan(1);
  351. rerender(
  352. <TaipyContext.Provider value={{ state, dispatch }}>
  353. <PaginatedTable data={changedValue as TableValueType} defaultColumns={tableColumns} />
  354. </TaipyContext.Provider>
  355. );
  356. expect(queryByText("Austria")).toBeNull();
  357. expect(getAllByText("Australia").length).toBe(1);
  358. });
  359. it("selects the rows", async () => {
  360. const dispatch = jest.fn();
  361. const state: TaipyState = INITIAL_STATE;
  362. const selected = [2, 4, 6];
  363. const { findAllByText, rerender } = render(
  364. <TaipyContext.Provider value={{ state, dispatch }}>
  365. <PaginatedTable data={undefined} defaultColumns={tableColumns} />
  366. </TaipyContext.Provider>
  367. );
  368. rerender(
  369. <TaipyContext.Provider value={{ state: { ...state }, dispatch }}>
  370. <PaginatedTable selected={selected} data={tableValue as TableValueType} defaultColumns={tableColumns} />
  371. </TaipyContext.Provider>
  372. );
  373. const elts = await waitFor(() => findAllByText("Austria"));
  374. elts.forEach((elt: HTMLElement, idx: number) =>
  375. selected.indexOf(idx) == -1
  376. ? expect(elt.parentElement?.parentElement?.parentElement?.parentElement).not.toHaveClass("Mui-selected")
  377. : expect(elt.parentElement?.parentElement?.parentElement?.parentElement).toHaveClass("Mui-selected")
  378. );
  379. expect(document.querySelectorAll(".Mui-selected")).toHaveLength(selected.length);
  380. });
  381. describe("Edit Mode", () => {
  382. it("displays the data with edit buttons", async () => {
  383. const dispatch = jest.fn();
  384. const state: TaipyState = INITIAL_STATE;
  385. const { getAllByTestId, queryAllByTestId, rerender } = render(
  386. <TaipyContext.Provider value={{ state, dispatch }}>
  387. <PaginatedTable
  388. data={undefined}
  389. defaultColumns={editableColumns}
  390. editable={true}
  391. onEdit="onEdit"
  392. showAll={true}
  393. />
  394. </TaipyContext.Provider>
  395. );
  396. rerender(
  397. <TaipyContext.Provider value={{ state: { ...state }, dispatch }}>
  398. <PaginatedTable
  399. data={editableValue as TableValueType}
  400. defaultColumns={editableColumns}
  401. editable={true}
  402. onEdit="onEdit"
  403. showAll={true}
  404. />
  405. </TaipyContext.Provider>
  406. );
  407. expect(document.querySelectorAll(".MuiSwitch-root")).not.toHaveLength(0);
  408. expect(getAllByTestId("EditIcon")).not.toHaveLength(0);
  409. expect(queryAllByTestId("CheckIcon")).toHaveLength(0);
  410. expect(queryAllByTestId("ClearIcon")).toHaveLength(0);
  411. });
  412. it("can edit", async () => {
  413. const dispatch = jest.fn();
  414. const state: TaipyState = INITIAL_STATE;
  415. const { getByTestId, queryAllByTestId, getAllByTestId, rerender } = render(
  416. <TaipyContext.Provider value={{ state, dispatch }}>
  417. <PaginatedTable
  418. data={undefined}
  419. defaultColumns={editableColumns}
  420. editable={true}
  421. onEdit="onEdit"
  422. showAll={true}
  423. />
  424. </TaipyContext.Provider>
  425. );
  426. rerender(
  427. <TaipyContext.Provider value={{ state: { ...state }, dispatch }}>
  428. <PaginatedTable
  429. data={editableValue as TableValueType}
  430. defaultColumns={editableColumns}
  431. editable={true}
  432. onEdit="onEdit"
  433. showAll={true}
  434. />
  435. </TaipyContext.Provider>
  436. );
  437. const edits = getAllByTestId("EditIcon");
  438. await userEvent.click(edits[0]);
  439. const checkButton = getByTestId("CheckIcon");
  440. getByTestId("ClearIcon");
  441. await userEvent.click(checkButton);
  442. expect(queryAllByTestId("CheckIcon")).toHaveLength(0);
  443. expect(queryAllByTestId("ClearIcon")).toHaveLength(0);
  444. await userEvent.click(edits[1]);
  445. const clearButton = getByTestId("ClearIcon");
  446. const input = document.querySelector("input");
  447. expect(input).not.toBeNull();
  448. if (input) {
  449. if (input.type == "checkbox") {
  450. await userEvent.click(input);
  451. } else {
  452. await userEvent.type(input, "1");
  453. }
  454. }
  455. await userEvent.click(clearButton);
  456. expect(queryAllByTestId("CheckIcon")).toHaveLength(0);
  457. expect(queryAllByTestId("ClearIcon")).toHaveLength(0);
  458. dispatch.mockClear();
  459. await userEvent.click(edits[2]);
  460. await userEvent.click(getByTestId("CheckIcon"));
  461. expect(dispatch).toHaveBeenCalledWith({
  462. name: "",
  463. payload: {
  464. action: "onEdit",
  465. args: [],
  466. col: "float",
  467. index: 0,
  468. user_value: 1.5,
  469. value: 1.5,
  470. },
  471. type: "SEND_ACTION_ACTION",
  472. });
  473. await userEvent.click(edits[3]);
  474. const input2 = document.querySelector("input");
  475. expect(input2).not.toBeNull();
  476. if (input2) {
  477. if (input2.type == "checkbox") {
  478. await userEvent.click(input2);
  479. await userEvent.click(getByTestId("ClearIcon"));
  480. } else {
  481. await userEvent.type(input2, "{Esc}");
  482. }
  483. }
  484. dispatch.mockClear();
  485. await userEvent.click(edits[5]);
  486. const input3 = document.querySelector("input");
  487. expect(input3).not.toBeNull();
  488. if (input3) {
  489. if (input3.type == "checkbox") {
  490. await userEvent.click(input3);
  491. await userEvent.click(getByTestId("CheckIcon"));
  492. } else {
  493. await userEvent.type(input3, "{Enter}");
  494. }
  495. }
  496. expect(dispatch).toHaveBeenCalledWith({
  497. name: "",
  498. payload: {
  499. action: "onEdit",
  500. args: [],
  501. col: "int",
  502. index: 1,
  503. user_value: 823,
  504. value: 823,
  505. },
  506. type: "SEND_ACTION_ACTION",
  507. });
  508. });
  509. });
  510. it("can add", async () => {
  511. const dispatch = jest.fn();
  512. const state: TaipyState = INITIAL_STATE;
  513. const { getByTestId } = render(
  514. <TaipyContext.Provider value={{ state, dispatch }}>
  515. <PaginatedTable
  516. data={undefined}
  517. defaultColumns={editableColumns}
  518. showAll={true}
  519. editable={true}
  520. onAdd="onAdd"
  521. />
  522. </TaipyContext.Provider>
  523. );
  524. dispatch.mockClear();
  525. const addButton = getByTestId("AddIcon");
  526. await userEvent.click(addButton);
  527. expect(dispatch).toHaveBeenCalledWith({
  528. name: "",
  529. payload: {
  530. action: "onAdd",
  531. args: [],
  532. index: 0,
  533. },
  534. type: "SEND_ACTION_ACTION",
  535. });
  536. });
  537. it("can delete", async () => {
  538. const dispatch = jest.fn();
  539. const state: TaipyState = INITIAL_STATE;
  540. const { getAllByTestId, getByTestId, queryAllByTestId, rerender } = render(
  541. <TaipyContext.Provider value={{ state, dispatch }}>
  542. <PaginatedTable
  543. data={undefined}
  544. defaultColumns={editableColumns}
  545. showAll={true}
  546. editable={true}
  547. onDelete="onDelete"
  548. />
  549. </TaipyContext.Provider>
  550. );
  551. rerender(
  552. <TaipyContext.Provider value={{ state: { ...state }, dispatch }}>
  553. <PaginatedTable
  554. data={editableValue as TableValueType}
  555. defaultColumns={editableColumns}
  556. editable={true}
  557. showAll={true}
  558. onDelete="onDelete"
  559. />
  560. </TaipyContext.Provider>
  561. );
  562. let deleteButtons = getAllByTestId("DeleteIcon");
  563. expect(deleteButtons).not.toHaveLength(0);
  564. await userEvent.click(deleteButtons[0]);
  565. const checkButton = getByTestId("CheckIcon");
  566. getByTestId("ClearIcon");
  567. await userEvent.click(checkButton);
  568. expect(queryAllByTestId("CheckIcon")).toHaveLength(0);
  569. expect(queryAllByTestId("ClearIcon")).toHaveLength(0);
  570. await userEvent.click(deleteButtons[1]);
  571. const clearButton = getByTestId("ClearIcon");
  572. const input = document.querySelector("input");
  573. expect(input).not.toBeNull();
  574. input && (await userEvent.type(input, "1"));
  575. await userEvent.click(clearButton);
  576. expect(queryAllByTestId("CheckIcon")).toHaveLength(0);
  577. expect(queryAllByTestId("ClearIcon")).toHaveLength(0);
  578. await userEvent.click(deleteButtons[2]);
  579. const input3 = document.querySelector("input");
  580. expect(input3).not.toBeNull();
  581. input3 && (await userEvent.type(input3, "{esc}"));
  582. deleteButtons = getAllByTestId("DeleteIcon");
  583. dispatch.mockClear();
  584. await userEvent.click(deleteButtons[0]);
  585. const input2 = document.querySelector("input");
  586. expect(input2).not.toBeNull();
  587. input2 && (await userEvent.type(input2, "{enter}"));
  588. expect(dispatch).toHaveBeenCalledWith({
  589. name: "",
  590. payload: {
  591. action: "onDelete",
  592. args: [],
  593. index: 0,
  594. },
  595. type: "SEND_ACTION_ACTION",
  596. });
  597. });
  598. it("can select", async () => {
  599. const dispatch = jest.fn();
  600. const state: TaipyState = INITIAL_STATE;
  601. const { getByText, rerender } = render(
  602. <TaipyContext.Provider value={{ state, dispatch }}>
  603. <PaginatedTable data={undefined} defaultColumns={editableColumns} showAll={true} onAction="onSelect" />
  604. </TaipyContext.Provider>
  605. );
  606. rerender(
  607. <TaipyContext.Provider value={{ state: { ...state }, dispatch }}>
  608. <PaginatedTable
  609. data={editableValue as TableValueType}
  610. defaultColumns={editableColumns}
  611. showAll={true}
  612. onAction="onSelect"
  613. />
  614. </TaipyContext.Provider>
  615. );
  616. dispatch.mockClear();
  617. const elt = getByText("823");
  618. await userEvent.click(elt);
  619. expect(dispatch).toHaveBeenCalledWith({
  620. name: "",
  621. payload: {
  622. action: "onSelect",
  623. args: [],
  624. col: "int",
  625. index: 1,
  626. reason: "click",
  627. value: undefined,
  628. },
  629. type: "SEND_ACTION_ACTION",
  630. });
  631. });
  632. it("can click on button", async () => {
  633. const dispatch = jest.fn();
  634. const state: TaipyState = INITIAL_STATE;
  635. const { getByText, rerender } = render(
  636. <TaipyContext.Provider value={{ state, dispatch }}>
  637. <PaginatedTable data={undefined} defaultColumns={editableColumns} showAll={true} onAction="onSelect" />
  638. </TaipyContext.Provider>
  639. );
  640. rerender(
  641. <TaipyContext.Provider value={{ state: { ...state }, dispatch }}>
  642. <PaginatedTable
  643. data={buttonValue as TableValueType}
  644. defaultColumns={buttonColumns}
  645. showAll={true}
  646. onAction="onSelect"
  647. />
  648. </TaipyContext.Provider>
  649. );
  650. dispatch.mockClear();
  651. const elt = getByText("Button Label");
  652. expect(elt.tagName).toBe("BUTTON");
  653. await userEvent.click(elt);
  654. expect(dispatch).toHaveBeenCalledWith({
  655. name: "",
  656. payload: {
  657. action: "onSelect",
  658. args: [],
  659. col: "Code",
  660. index: 0,
  661. reason: "button",
  662. value: "button action",
  663. },
  664. type: "SEND_ACTION_ACTION",
  665. });
  666. });
  667. it("should render correctly when style is applied to columns", async () => {
  668. const dispatch = jest.fn();
  669. const state: TaipyState = INITIAL_STATE;
  670. await waitFor(() => {
  671. render(
  672. <TaipyContext.Provider value={{ state, dispatch }}>
  673. <PaginatedTable
  674. data={tableValue}
  675. defaultColumns={styledColumns}
  676. lineStyle={"class_name=rows-bordered"}
  677. />
  678. </TaipyContext.Provider>
  679. );
  680. });
  681. const elt = document.querySelector('table[aria-labelledby="tableTitle"]');
  682. expect(elt).toBeInTheDocument();
  683. });
  684. it("should sort the table in ascending order", async () => {
  685. await waitFor(() => {
  686. render(<PaginatedTable data={tableValue} defaultColumns={tableColumns} />);
  687. });
  688. const elt = document.querySelector('svg[data-testid="ArrowDownwardIcon"]');
  689. act(() => {
  690. fireEvent.click(elt as Element);
  691. });
  692. expect(document.querySelector('th[aria-sort="ascending"]')).toBeInTheDocument();
  693. });
  694. it("should handle rows per page change", async () => {
  695. const { getByRole, queryByRole } = render(<PaginatedTable data={tableValue} defaultColumns={tableColumns} />);
  696. const rowsPerPageDropdown = getByRole("combobox");
  697. fireEvent.mouseDown(rowsPerPageDropdown);
  698. const option = queryByRole("option", { selected: false, name: "50" });
  699. fireEvent.click(option as Element);
  700. const table = document.querySelector(
  701. 'table[aria-labelledby="tableTitle"].MuiTable-root.MuiTable-stickyHeader.css-cz602z-MuiTable-root'
  702. );
  703. expect(table).toBeInTheDocument();
  704. });
  705. it("should allow all rows", async () => {
  706. const { getByRole, queryByRole } = render(
  707. <PaginatedTable data={tableValue} defaultColumns={tableColumns} allowAllRows={true} />
  708. );
  709. const rowsPerPageDropdown = getByRole("combobox");
  710. fireEvent.mouseDown(rowsPerPageDropdown);
  711. const option = queryByRole("option", { selected: false, name: "All" });
  712. expect(option).toBeInTheDocument();
  713. });
  714. it("should display row per page correctly", async () => {
  715. const { getByRole, queryByRole } = render(
  716. <PaginatedTable
  717. data={tableValue}
  718. defaultColumns={tableColumns}
  719. pageSizeOptions={JSON.stringify([10, 20, 30])}
  720. />
  721. );
  722. const rowsPerPageDropdown = getByRole("combobox");
  723. fireEvent.mouseDown(rowsPerPageDropdown);
  724. const option = queryByRole("option", { selected: false, name: "10" });
  725. expect(option).toBeInTheDocument();
  726. });
  727. it("logs error when pageSizeOptions prop is invalid", () => {
  728. // Create a spy on console.log
  729. const logSpy = jest.spyOn(console, "log");
  730. // Render the component with invalid pageSizeOptions prop
  731. render(<PaginatedTable data={tableValue} defaultColumns={tableColumns} pageSizeOptions={"not a valid json"} />);
  732. // Check if console.log was called with the expected arguments
  733. expect(logSpy).toHaveBeenCalledWith(
  734. "PaginatedTable pageSizeOptions is wrong ",
  735. "not a valid json",
  736. expect.any(Error)
  737. );
  738. // Clean up the spy
  739. logSpy.mockRestore();
  740. });
  741. });