TreeView.spec.tsx 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  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 from "react";
  14. import { render } from "@testing-library/react";
  15. import "@testing-library/jest-dom";
  16. import userEvent from "@testing-library/user-event";
  17. import TreeView from "./TreeView";
  18. import { LoV } from "./lovUtils";
  19. import { INITIAL_STATE, TaipyState } from "../../context/taipyReducers";
  20. import { TaipyContext } from "../../context/taipyContext";
  21. import { stringIcon } from "../../utils/icon";
  22. const lov: LoV = [
  23. [
  24. "id1",
  25. "Item 1",
  26. [
  27. ["id1.1", "Item 1.1"],
  28. ["id1.2", "Item 1.2"],
  29. ],
  30. ],
  31. ["id2", "Item 2"],
  32. ["id3", "Item 3"],
  33. ["id4", "Item 4"],
  34. ];
  35. const defLov = '[["id10","Default Item"]]';
  36. const imageItem: [string, stringIcon] = ["ii1", { path: "/img/fred.png", text: "Image" }];
  37. describe("TreeView Component", () => {
  38. it("renders", async () => {
  39. const { getByText } = render(<TreeView lov={lov} />);
  40. const elt = getByText("Item 1");
  41. expect(elt.tagName).toBe("DIV");
  42. });
  43. it("uses the class", async () => {
  44. const { getByText } = render(<TreeView lov={lov} className="taipy-tree" />);
  45. const elt = getByText("Item 1");
  46. expect(elt.parentElement?.parentElement?.parentElement?.parentElement?.parentElement).toHaveClass("taipy-tree");
  47. });
  48. it("can display an image", async () => {
  49. const lovWithImage = [...lov, imageItem];
  50. const { getByAltText } = render(<TreeView lov={lovWithImage} />);
  51. const elt = getByAltText("Image");
  52. expect(elt.tagName).toBe("IMG");
  53. });
  54. it("shows a tree", async () => {
  55. const { getByText, queryAllByText } = render(<TreeView lov={lov} filter={true} />);
  56. const elt = getByText("Item 1");
  57. expect(queryAllByText(/Item 1\./)).toHaveLength(0);
  58. const icon = elt.parentElement?.querySelector(".MuiTreeItem-iconContainer") || elt;
  59. expect(icon).toBeInTheDocument();
  60. await userEvent.click(icon);
  61. getByText("Item 1.2");
  62. expect(queryAllByText(/Item 1\./)).toHaveLength(2);
  63. });
  64. it("displays the right info for lov vs defaultLov", async () => {
  65. const { getByText, queryAllByText } = render(<TreeView lov={lov} defaultLov={defLov} />);
  66. getByText("Item 1");
  67. expect(queryAllByText("Default Item")).toHaveLength(0);
  68. });
  69. it("displays the default LoV", async () => {
  70. const { getByText } = render(<TreeView lov={undefined as unknown as []} defaultLov={defLov} />);
  71. getByText("Default Item");
  72. });
  73. it("shows a selection at start", async () => {
  74. const { getByText } = render(<TreeView defaultValue="id1" lov={lov} />);
  75. const elt = getByText("Item 1");
  76. expect(elt.parentElement).toHaveClass("Mui-selected");
  77. });
  78. it("shows a selection at start through value", async () => {
  79. const { getByText } = render(<TreeView defaultValue="id1" value="id2" lov={lov} />);
  80. const elt = getByText("Item 1");
  81. expect(elt.parentElement).not.toHaveClass("Mui-selected");
  82. const elt2 = getByText("Item 2");
  83. expect(elt2.parentElement).toHaveClass("Mui-selected");
  84. });
  85. it("is disabled", async () => {
  86. const { getAllByRole } = render(<TreeView lov={lov} active={false} />);
  87. const elts = getAllByRole("treeitem");
  88. elts.forEach((elt) => expect(elt.firstElementChild).toHaveClass("Mui-disabled"));
  89. });
  90. it("is enabled by default", async () => {
  91. const { getAllByRole } = render(<TreeView lov={lov} />);
  92. const elts = getAllByRole("treeitem");
  93. elts.forEach((elt) => expect(elt.firstElementChild).not.toHaveClass("Mui-disabled"));
  94. });
  95. it("is enabled by active", async () => {
  96. const { getAllByRole } = render(<TreeView lov={lov} active={true} />);
  97. const elts = getAllByRole("treeitem");
  98. elts.forEach((elt) => expect(elt.firstElementChild).not.toHaveClass("Mui-disabled"));
  99. });
  100. it("dispatch a well formed message base", async () => {
  101. const dispatch = jest.fn();
  102. const state: TaipyState = INITIAL_STATE;
  103. const { getByText } = render(
  104. <TaipyContext.Provider value={{ state, dispatch }}>
  105. <TreeView lov={lov} updateVarName="varname" />
  106. </TaipyContext.Provider>
  107. );
  108. const elt = getByText("Item 1");
  109. await userEvent.click(elt);
  110. expect(dispatch).toHaveBeenCalledWith({
  111. name: "varname",
  112. payload: { value: ["id1"] },
  113. propagate: true,
  114. type: "SEND_UPDATE_ACTION",
  115. });
  116. });
  117. //multiple
  118. it("selects 2 items", async () => {
  119. const { queryAllByRole } = render(<TreeView lov={lov} multiple={true} value={["id1", "id2"]} />);
  120. expect(document.querySelectorAll(".Mui-selected")).toHaveLength(2);
  121. });
  122. it("dispatch a well formed message for multiple", async () => {
  123. const user = userEvent.setup()
  124. const dispatch = jest.fn();
  125. const state: TaipyState = INITIAL_STATE;
  126. const { getByText } = render(
  127. <TaipyContext.Provider value={{ state, dispatch }}>
  128. <TreeView lov={lov} updateVarName="varname" multiple={true} />
  129. </TaipyContext.Provider>
  130. );
  131. const elt = getByText("Item 1");
  132. await user.click(elt);
  133. const elt2 = getByText("Item 2");
  134. const elt3 = getByText("Item 3");
  135. await user.keyboard('{Control>}')
  136. await user.click(elt2)
  137. await user.click(elt3)
  138. await user.click(elt2)
  139. await user.keyboard('{/Control}')
  140. expect(dispatch).toHaveBeenLastCalledWith({
  141. name: "varname",
  142. payload: { value: ["id3", "id1"] },
  143. propagate: true,
  144. type: "SEND_UPDATE_ACTION",
  145. });
  146. });
  147. //filter
  148. it("displays an input when filter", async () => {
  149. const { getByPlaceholderText } = render(<TreeView lov={lov} filter={true} />);
  150. getByPlaceholderText("Search field");
  151. });
  152. it("does not display an input when filter is off", async () => {
  153. const { queryAllByPlaceholderText } = render(<TreeView lov={lov} filter={false} />);
  154. expect(queryAllByPlaceholderText("Search field")).toHaveLength(0);
  155. });
  156. it("filters items by name", async () => {
  157. const { getByPlaceholderText, queryAllByText } = render(<TreeView lov={lov} filter={true} />);
  158. expect(queryAllByText(/Item /)).toHaveLength(4);
  159. const search = getByPlaceholderText("Search field");
  160. await userEvent.type(search, "m 3");
  161. expect(queryAllByText(/Item /)).toHaveLength(1);
  162. await userEvent.clear(search);
  163. expect(queryAllByText(/Item /)).toHaveLength(4);
  164. });
  165. // expanded
  166. it("does not dispatch update message when expanded is boolean", async () => {
  167. const dispatch = jest.fn();
  168. const state: TaipyState = INITIAL_STATE;
  169. const { getByText } = render(<TaipyContext.Provider value={{ state, dispatch }}><TreeView lov={lov} expanded={true} updateVars="expanded=tree_expanded" /></TaipyContext.Provider>);
  170. const elt = getByText("Item 1");
  171. await userEvent.click(elt);
  172. expect(dispatch).toHaveBeenCalledTimes(1);
  173. expect(dispatch).toHaveBeenCalledWith({name: "", payload: {id: undefined, names:["tree_expanded"], refresh: false}, type: "REQUEST_UPDATE"});
  174. });
  175. it("does dispatch update message when expanded is not boolean", async () => {
  176. const dispatch = jest.fn();
  177. const state: TaipyState = INITIAL_STATE;
  178. const { getByText } = render(<TaipyContext.Provider value={{ state, dispatch }}><TreeView lov={lov} expanded={[]} updateVars="expanded=tree_expanded" /></TaipyContext.Provider>);
  179. const elt = getByText("Item 1");
  180. const icon = elt.parentElement?.querySelector(".MuiTreeItem-iconContainer") || elt;
  181. await userEvent.click(icon);
  182. //expect(dispatch).toHaveBeenCalledTimes(2);
  183. expect(dispatch).toHaveBeenCalledWith({name: "", payload: {id: undefined, names:["tree_expanded"], refresh: false}, type: "REQUEST_UPDATE"});
  184. expect(dispatch).toHaveBeenCalledWith({name:"tree_expanded", payload: {value: ["id1"]}, type: "SEND_UPDATE_ACTION", propagate: true});
  185. });
  186. });