Metric.spec.tsx 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462
  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 { container } = render(<Metric className="test" />);
  160. const elt = container.querySelector(".test");
  161. expect(elt?.tagName).toBe("DIV");
  162. });
  163. it("displays the right info for class", async () => {
  164. const { container } = render(<Metric className="test" />);
  165. const elt = container.querySelector(".test");
  166. expect(elt).toHaveClass("test");
  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. const { container } = render(<Metric template={JSON.stringify(template)} />);
  195. await waitFor(() => {
  196. const elt = container.querySelector(".main-svg");
  197. expect(elt).toHaveStyle({
  198. backgroundColor: "rgb(255, 0, 0)",
  199. });
  200. });
  201. });
  202. it("processes colorMap prop correctly", async () => {
  203. const { container } = render(<Metric colorMap={JSON.stringify(colorMap)} />);
  204. await waitFor(() => {
  205. const elements = container.querySelectorAll(".bg-arc");
  206. const redElt = Array.from(elements[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(elements[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. const { container } = render(<Metric delta={10} />);
  216. await waitFor(() => {
  217. const elt = container.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("applies style correctly when deltaColor is set", async () => {
  226. const { container } = render(<Metric delta={10} deltaColor="#FF4136" />);
  227. await waitFor(() => {
  228. const elt = container.querySelector(".delta");
  229. expect(elt).toHaveStyle({
  230. fill: "rgb(255, 65, 54)",
  231. });
  232. });
  233. });
  234. it("applies style correctly when deltaColor is set invert", async () => {
  235. const { container } = render(<Metric delta={10} deltaColor="invert" />);
  236. await waitFor(() => {
  237. const elt = container.querySelector(".delta");
  238. expect(elt).toHaveStyle({
  239. fill: "rgb(255, 65, 54)",
  240. });
  241. });
  242. });
  243. it("processes type and threshold props correctly when type is linear", async () => {
  244. const { container } = render(<Metric type="linear" threshold={50} />);
  245. await waitFor(() => {
  246. const elt = container.querySelector(".bullet");
  247. expect(elt).toBeInTheDocument();
  248. });
  249. });
  250. it("processes type and threshold props correctly when type is not linear", async () => {
  251. const { container } = render(<Metric type="angular" threshold={50} />);
  252. await waitFor(() => {
  253. const elt = container.querySelector(".angular");
  254. expect(elt).toBeInTheDocument();
  255. });
  256. });
  257. it("applies style correctly when height and width are undefined", async () => {
  258. const { container } = render(<Metric />);
  259. await waitFor(() => {
  260. const elt = container.querySelector(".js-plotly-plot");
  261. expect(elt).toHaveStyle({
  262. width: "20vw",
  263. height: "20vh",
  264. });
  265. });
  266. });
  267. it("applies style correctly when height is set to 100px", async () => {
  268. const { container } = render(<Metric height="100px" />);
  269. await waitFor(() => {
  270. const elt = container.querySelector(".js-plotly-plot");
  271. expect(elt).toHaveStyle({
  272. height: "100px",
  273. });
  274. });
  275. });
  276. it("applies style correctly when height is set to 30em", async () => {
  277. const { container } = render(<Metric height="30em" />);
  278. await waitFor(() => {
  279. const elt = container.querySelector(".js-plotly-plot");
  280. expect(elt).toHaveStyle({
  281. height: "30em",
  282. });
  283. });
  284. });
  285. it("applies style correctly when height is set to 30%", async () => {
  286. const { container } = render(<Metric height="30%" />);
  287. await waitFor(() => {
  288. const elt = container.querySelector(".js-plotly-plot");
  289. expect(elt).toHaveStyle({
  290. height: "30%",
  291. });
  292. });
  293. });
  294. it("applies style correctly when height is set to 30vh", async () => {
  295. const { container } = render(<Metric height="30vh" />);
  296. await waitFor(() => {
  297. const elt = container.querySelector(".js-plotly-plot");
  298. expect(elt).toHaveStyle({
  299. height: "30vh",
  300. });
  301. });
  302. });
  303. it("applies style correctly when height is set to 30vw", async () => {
  304. const { container } = render(<Metric height="30vw" />);
  305. await waitFor(() => {
  306. const elt = container.querySelector(".js-plotly-plot");
  307. expect(elt).toHaveStyle({
  308. height: "30vw",
  309. });
  310. });
  311. });
  312. it("applies style correctly when width is set to 100px", async () => {
  313. const { container } = render(<Metric width="100px" />);
  314. await waitFor(() => {
  315. const elt = container.querySelector(".js-plotly-plot");
  316. expect(elt).toHaveStyle({
  317. width: "100px",
  318. });
  319. });
  320. });
  321. it("applies style correctly when width is set to 30em", async () => {
  322. const { container } = render(<Metric width="30em" />);
  323. await waitFor(() => {
  324. const elt = container.querySelector(".js-plotly-plot");
  325. expect(elt).toHaveStyle({
  326. width: "30em",
  327. });
  328. });
  329. });
  330. it("applies style correctly when width is set to 30%", async () => {
  331. const { container } = render(<Metric width="30%" />);
  332. await waitFor(() => {
  333. const elt = container.querySelector(".js-plotly-plot");
  334. expect(elt).toHaveStyle({
  335. width: "30%",
  336. });
  337. });
  338. });
  339. it("applies style correctly when width is set to 30vh", async () => {
  340. const { container } = render(<Metric width="30vh" />);
  341. await waitFor(() => {
  342. const elt = container.querySelector(".js-plotly-plot");
  343. expect(elt).toHaveStyle({
  344. width: "30vh",
  345. });
  346. });
  347. });
  348. it("applies style correctly when width is set to 30vw", async () => {
  349. const { container } = render(<Metric width="30vw" />);
  350. await waitFor(() => {
  351. const elt = container.querySelector(".js-plotly-plot");
  352. expect(elt).toHaveStyle({
  353. width: "30vw",
  354. });
  355. });
  356. });
  357. it("processes type prop correctly when type is none (string)", async () => {
  358. const { container } = render(<Metric type="none" />);
  359. await waitFor(() => {
  360. const angularElm = container.querySelector(".angular");
  361. const angularAxis = container.querySelector(".angularaxis");
  362. expect(angularElm).not.toBeInTheDocument();
  363. expect(angularAxis).not.toBeInTheDocument();
  364. });
  365. });
  366. it("processes type prop correctly when type is None", async () => {
  367. const { container } = render(<Metric type="None" />);
  368. await waitFor(() => {
  369. const angularElm = container.querySelector(".angular");
  370. const angularAxis = container.querySelector(".angularaxis");
  371. expect(angularElm).not.toBeInTheDocument();
  372. expect(angularAxis).not.toBeInTheDocument();
  373. });
  374. });
  375. it("processes template_Dark_ prop correctly when theme is dark", async () => {
  376. const darkTheme = createTheme({
  377. palette: {
  378. mode: "dark",
  379. },
  380. });
  381. const { container } = render(
  382. <ThemeProvider theme={darkTheme}>
  383. <Metric template_Dark_={JSON.stringify(darkTemplate)} />
  384. </ThemeProvider>,
  385. );
  386. await waitFor(() => {
  387. const elt = container.querySelector(".main-svg");
  388. expect(elt).toHaveStyle({
  389. backgroundColor: "rgb(31, 47, 68)",
  390. });
  391. });
  392. });
  393. it("processes template_Light_ prop correctly when theme is not dark", async () => {
  394. const lightTheme = createTheme({
  395. palette: {
  396. mode: "light",
  397. },
  398. });
  399. const { container } = render(
  400. <ThemeProvider theme={lightTheme}>
  401. <Metric template_Light_={JSON.stringify(lightTemplate)} />
  402. </ThemeProvider>,
  403. );
  404. await waitFor(() => {
  405. const elt = container.querySelector(".main-svg");
  406. expect(elt).toHaveStyle({
  407. backgroundColor: "rgb(255, 255, 255)",
  408. });
  409. });
  410. });
  411. it.skip("logs an error when template_Dark_ prop is not a valid JSON string", () => {
  412. const consoleSpy = jest.spyOn(console, "info");
  413. render(<Metric template_Dark_="not a valid JSON string" />);
  414. expect(consoleSpy).toHaveBeenCalledWith(expect.stringContaining("Error while parsing Metric.template"));
  415. consoleSpy.mockRestore();
  416. }); // TODO: Not working at the moment, need to fix
  417. it("logs an error when template_Light_ prop is not a valid JSON string", () => {
  418. const consoleSpy = jest.spyOn(console, "info");
  419. render(<Metric template_Light_="not a valid JSON string" />);
  420. expect(consoleSpy).toHaveBeenCalledWith(expect.stringContaining("Error while parsing Metric.template"));
  421. consoleSpy.mockRestore();
  422. });
  423. });