PaginatedTable.spec.tsx 29 KB

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