Metric.spec.tsx 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332
  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, waitFor } from "@testing-library/react";
  15. import "@testing-library/jest-dom";
  16. import { ThemeProvider } from "@mui/material/styles";
  17. import { createTheme } from "@mui/material/styles";
  18. import Metric from "./Metric";
  19. const template = {
  20. layout: {
  21. colorscale: {
  22. diverging: [
  23. [0, "#8e0152"],
  24. [0.1, "#c51b7d"],
  25. [0.2, "#de77ae"],
  26. [0.3, "#f1b6da"],
  27. [0.4, "#fde0ef"],
  28. [0.5, "#f7f7f7"],
  29. [0.6, "#e6f5d0"],
  30. [0.7, "#b8e186"],
  31. [0.8, "#7fbc41"],
  32. [0.9, "#4d9221"],
  33. [1, "#276419"],
  34. ],
  35. sequential: [
  36. [0, "#0d0887"],
  37. [0.1111111111111111, "#46039f"],
  38. [0.2222222222222222, "#7201a8"],
  39. [0.3333333333333333, "#9c179e"],
  40. [0.4444444444444444, "#bd3786"],
  41. [0.5555555555555556, "#d8576b"],
  42. [0.6666666666666666, "#ed7953"],
  43. [0.7777777777777778, "#fb9f3a"],
  44. [0.8888888888888888, "#fdca26"],
  45. [1, "#f0f921"],
  46. ],
  47. sequentialminus: [
  48. [0, "#0d0887"],
  49. [0.1111111111111111, "#46039f"],
  50. [0.2222222222222222, "#7201a8"],
  51. [0.3333333333333333, "#9c179e"],
  52. [0.4444444444444444, "#bd3786"],
  53. [0.5555555555555556, "#d8576b"],
  54. [0.6666666666666666, "#ed7953"],
  55. [0.7777777777777778, "#fb9f3a"],
  56. [0.8888888888888888, "#fdca26"],
  57. [1, "#f0f921"],
  58. ],
  59. },
  60. paper_bgcolor: "rgb(255,0,0)",
  61. },
  62. };
  63. const colorMap = {
  64. 20: "red",
  65. 40: null,
  66. 60: "blue",
  67. 80: null,
  68. };
  69. const darkTemplate = {
  70. layout: {
  71. colorscale: {
  72. diverging: [
  73. [0, "#8e0152"],
  74. [0.1, "#c51b7d"],
  75. [0.2, "#de77ae"],
  76. [0.3, "#f1b6da"],
  77. [0.4, "#fde0ef"],
  78. [0.5, "#f7f7f7"],
  79. [0.6, "#e6f5d0"],
  80. [0.7, "#b8e186"],
  81. [0.8, "#7fbc41"],
  82. [0.9, "#4d9221"],
  83. [1, "#276419"],
  84. ],
  85. sequential: [
  86. [0, "#0d0887"],
  87. [0.1111111111111111, "#46039f"],
  88. [0.2222222222222222, "#7201a8"],
  89. [0.3333333333333333, "#9c179e"],
  90. [0.4444444444444444, "#bd3786"],
  91. [0.5555555555555556, "#d8576b"],
  92. [0.6666666666666666, "#ed7953"],
  93. [0.7777777777777778, "#fb9f3a"],
  94. [0.8888888888888888, "#fdca26"],
  95. [1, "#f0f921"],
  96. ],
  97. sequentialminus: [
  98. [0, "#0d0887"],
  99. [0.1111111111111111, "#46039f"],
  100. [0.2222222222222222, "#7201a8"],
  101. [0.3333333333333333, "#9c179e"],
  102. [0.4444444444444444, "#bd3786"],
  103. [0.5555555555555556, "#d8576b"],
  104. [0.6666666666666666, "#ed7953"],
  105. [0.7777777777777778, "#fb9f3a"],
  106. [0.8888888888888888, "#fdca26"],
  107. [1, "#f0f921"],
  108. ],
  109. },
  110. paper_bgcolor: "rgb(31,47,68)",
  111. },
  112. };
  113. const lightTemplate = {
  114. layout: {
  115. colorscale: {
  116. diverging: [
  117. [0, "#053061"],
  118. [0.1, "#2166ac"],
  119. [0.2, "#4393c3"],
  120. [0.3, "#92c5de"],
  121. [0.4, "#d1e5f0"],
  122. [0.5, "#f7f7f7"],
  123. [0.6, "#fddbc7"],
  124. [0.7, "#f4a582"],
  125. [0.8, "#d6604d"],
  126. [0.9, "#b2182b"],
  127. [1, "#67001f"],
  128. ],
  129. sequential: [
  130. [0, "#f7fcf5"],
  131. [0.1111111111111111, "#e5f5e0"],
  132. [0.2222222222222222, "#c7e9c0"],
  133. [0.3333333333333333, "#a1d99b"],
  134. [0.4444444444444444, "#74c476"],
  135. [0.5555555555555556, "#41ab5d"],
  136. [0.6666666666666666, "#238b45"],
  137. [0.7777777777777778, "#006d2c"],
  138. [0.8888888888888888, "#00441b"],
  139. [1, "#000000"],
  140. ],
  141. sequentialminus: [
  142. [0, "#f7fcf5"],
  143. [0.1111111111111111, "#e5f5e0"],
  144. [0.2222222222222222, "#c7e9c0"],
  145. [0.3333333333333333, "#a1d99b"],
  146. [0.4444444444444444, "#74c476"],
  147. [0.5555555555555556, "#41ab5d"],
  148. [0.6666666666666666, "#238b45"],
  149. [0.7777777777777778, "#006d2c"],
  150. [0.8888888888888888, "#00441b"],
  151. [1, "#000000"],
  152. ],
  153. },
  154. paper_bgcolor: "rgb(255,255,255)",
  155. },
  156. };
  157. describe("Metric Component", () => {
  158. it("renders", async () => {
  159. const { getByTestId } = render(<Metric testId="test-id" />);
  160. const elt = getByTestId("test-id");
  161. expect(elt.tagName).toBe("DIV");
  162. });
  163. it("displays the right info for class", async () => {
  164. const { getByTestId } = render(<Metric testId="test-id" className={"taipy-gauge"} />);
  165. const elt = getByTestId("test-id");
  166. expect(elt).toHaveClass("taipy-gauge");
  167. });
  168. it("sets the title when provided", async () => {
  169. const title = "Test Title";
  170. const { container } = render(<Metric title={title} />);
  171. await waitFor(() => {
  172. const titleElement = container.querySelector(".gtitle");
  173. if (!titleElement) {
  174. throw new Error("Title element not found");
  175. }
  176. expect(titleElement.textContent).toContain(title);
  177. });
  178. });
  179. it("logs an error when template prop is a malformed JSON string", () => {
  180. const consoleSpy = jest.spyOn(console, "info");
  181. const malformedJson = "{ key: 'value'";
  182. render(<Metric template={malformedJson} />);
  183. expect(consoleSpy).toHaveBeenCalledWith(expect.stringContaining("Error while parsing Metric.template"));
  184. consoleSpy.mockRestore();
  185. });
  186. it("logs an error when colorMap prop is a malformed JSON string", () => {
  187. const consoleSpy = jest.spyOn(console, "info");
  188. const malformedJson = "{ key: 'value'"; // missing closing brace
  189. render(<Metric colorMap={malformedJson} />);
  190. expect(consoleSpy).toHaveBeenCalledWith(expect.stringContaining("Error parsing color_map value (metric)"));
  191. consoleSpy.mockRestore();
  192. });
  193. it("sets the template when provided", async () => {
  194. render(<Metric template={JSON.stringify(template)} />);
  195. await waitFor(() => {
  196. const elt = document.querySelector(".main-svg");
  197. expect(elt).toHaveStyle({
  198. backgroundColor: "rgb(255, 0, 0)",
  199. });
  200. });
  201. });
  202. it("processes colorMap prop correctly", async () => {
  203. render(<Metric colorMap={JSON.stringify(colorMap)} />);
  204. await waitFor(() => {
  205. const elts = document.querySelectorAll(".bg-arc");
  206. const redElt = Array.from(elts[1].children);
  207. const redEltHasRedFill = redElt.some((elt) => (elt as HTMLElement).style.fill === "rgb(255, 0, 0)");
  208. expect(redEltHasRedFill).toBeTruthy();
  209. const blueElt = Array.from(elts[2].children);
  210. const blueEltHasBlueFill = blueElt.some((elt) => (elt as HTMLElement).style.fill === "rgb(0, 0, 255)");
  211. expect(blueEltHasBlueFill).toBeTruthy();
  212. });
  213. });
  214. it("processes delta prop correctly when delta is defined", async () => {
  215. render(<Metric delta={10} testId="test-id" />);
  216. await waitFor(() => {
  217. const elt = document.querySelector(".delta");
  218. if (elt) {
  219. expect(elt.textContent).toContain("10");
  220. } else {
  221. throw new Error("Element with class .delta not found");
  222. }
  223. });
  224. });
  225. it("processes type and threshold props correctly when type is linear", async () => {
  226. render(<Metric type="linear" threshold={50} testId="test-id" />);
  227. await waitFor(() => {
  228. const elt = document.querySelector(".bullet");
  229. expect(elt).toBeInTheDocument();
  230. });
  231. });
  232. it("processes type and threshold props correctly when type is not linear", async () => {
  233. render(<Metric type="angular" threshold={50} testId="test-id" />);
  234. await waitFor(() => {
  235. const elt = document.querySelector(".angular");
  236. expect(elt).toBeInTheDocument();
  237. });
  238. });
  239. it("applies style correctly when height is undefined", async () => {
  240. render(<Metric testId="test-id" />);
  241. await waitFor(() => {
  242. const elt = document.querySelector(".js-plotly-plot");
  243. expect(elt).toHaveStyle({
  244. width: "100%",
  245. position: "relative",
  246. display: "inline-block",
  247. });
  248. });
  249. });
  250. it("processes type prop correctly when type is none", async () => {
  251. render(<Metric type="none" testId="test-id" />);
  252. await waitFor(() => {
  253. const angularElm = document.querySelector(".angular");
  254. const angularAxis = document.querySelector(".angularaxis");
  255. expect(angularElm).not.toBeInTheDocument();
  256. expect(angularAxis).not.toBeInTheDocument();
  257. });
  258. });
  259. it("processes template_Dark_ prop correctly when theme is dark", async () => {
  260. const darkTheme = createTheme({
  261. palette: {
  262. mode: "dark",
  263. },
  264. });
  265. render(
  266. <ThemeProvider theme={darkTheme}>
  267. <Metric template_Dark_={JSON.stringify(darkTemplate)} testId="test-id" />
  268. </ThemeProvider>,
  269. );
  270. await waitFor(() => {
  271. const elt = document.querySelector(".main-svg");
  272. expect(elt).toHaveStyle({
  273. backgroundColor: "rgb(31, 47, 68)",
  274. });
  275. });
  276. });
  277. it("processes template_Light_ prop correctly when theme is not dark", async () => {
  278. const lightTheme = createTheme({
  279. palette: {
  280. mode: "light",
  281. },
  282. });
  283. render(
  284. <ThemeProvider theme={lightTheme}>
  285. <Metric template_Light_={JSON.stringify(lightTemplate)} testId="test-id" />
  286. </ThemeProvider>,
  287. );
  288. await waitFor(() => {
  289. const elt = document.querySelector(".main-svg");
  290. expect(elt).toHaveStyle({
  291. backgroundColor: "rgb(255, 255, 255)",
  292. });
  293. });
  294. });
  295. it.skip("logs an error when template_Dark_ prop is not a valid JSON string", () => {
  296. const consoleSpy = jest.spyOn(console, "info");
  297. render(<Metric template_Dark_="not a valid JSON string" testId="test-id" />);
  298. expect(consoleSpy).toHaveBeenCalledWith(expect.stringContaining("Error while parsing Metric.template"));
  299. consoleSpy.mockRestore();
  300. }); // TODO: Not working at the moment, need to fix
  301. it("logs an error when template_Light_ prop is not a valid JSON string", () => {
  302. const consoleSpy = jest.spyOn(console, "info");
  303. render(<Metric template_Light_="not a valid JSON string" testId="test-id" />);
  304. expect(consoleSpy).toHaveBeenCalledWith(expect.stringContaining("Error while parsing Metric.template"));
  305. consoleSpy.mockRestore();
  306. });
  307. });