DateSelector.spec.tsx 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305
  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 userEvent from "@testing-library/user-event";
  16. import "@testing-library/jest-dom";
  17. import { LocalizationProvider } from "@mui/x-date-pickers";
  18. import { AdapterDateFns } from "@mui/x-date-pickers/AdapterDateFnsV3";
  19. import DateSelector from "./DateSelector";
  20. import { TaipyContext } from "../../context/taipyContext";
  21. import { TaipyState, INITIAL_STATE } from "../../context/taipyReducers";
  22. import { getClientServerTimeZoneOffset } from "../../utils";
  23. jest.mock("../../utils", () => {
  24. const originalModule = jest.requireActual("../../utils");
  25. //Mock getClientServerTimeZoneOffset
  26. return {
  27. __esModule: true,
  28. ...originalModule,
  29. getClientServerTimeZoneOffset: () => 0,
  30. };
  31. });
  32. beforeEach(() => {
  33. // add window.matchMedia
  34. // this is necessary for the date picker to be rendered in desktop mode.
  35. // if this is not provided, the mobile mode is rendered, which might lead to unexpected behavior
  36. Object.defineProperty(window, "matchMedia", {
  37. writable: true,
  38. value: (query: string): MediaQueryList => ({
  39. media: query,
  40. // this is the media query that @material-ui/pickers uses to determine if a device is a desktop device
  41. matches: query === "(pointer: fine)",
  42. onchange: () => {},
  43. addEventListener: () => {},
  44. removeEventListener: () => {},
  45. addListener: () => {},
  46. removeListener: () => {},
  47. dispatchEvent: () => false,
  48. }),
  49. });
  50. });
  51. afterEach(() => {
  52. // @ts-ignore
  53. delete window.matchMedia;
  54. });
  55. const curDate = new Date();
  56. curDate.setHours(1, 1, 1, 1);
  57. const curDateStr = curDate.toISOString();
  58. const cleanText = (val: string) => val.replace(/\u200e|\u2066|\u2067|\u2068|\u2069/g, "");
  59. describe("DateSelector Component", () => {
  60. it("renders", async () => {
  61. const { getByTestId } = render(
  62. <LocalizationProvider dateAdapter={AdapterDateFns}>
  63. <DateSelector date={curDateStr} />
  64. </LocalizationProvider>
  65. );
  66. const elt = getByTestId("CalendarIcon");
  67. expect(elt.parentElement?.tagName).toBe("BUTTON");
  68. });
  69. it("displays the right info for string", async () => {
  70. const { getByTestId } = render(
  71. <LocalizationProvider dateAdapter={AdapterDateFns}>
  72. <DateSelector date={curDateStr} defaultDate="2001-01-01T00:00:01.001Z" className="taipy-date" />
  73. </LocalizationProvider>
  74. );
  75. const elt = getByTestId("CalendarIcon");
  76. expect(elt.parentElement?.parentElement?.parentElement?.parentElement).toHaveClass("taipy-date-picker");
  77. expect(elt.parentElement?.parentElement?.parentElement?.parentElement?.parentElement).toHaveClass("taipy-date");
  78. });
  79. it("displays the default value", async () => {
  80. render(
  81. <LocalizationProvider dateAdapter={AdapterDateFns}>
  82. <DateSelector defaultDate="2001-01-01T00:00:01.001Z" date={undefined as unknown as string} />
  83. </LocalizationProvider>
  84. );
  85. const input = document.querySelector("input");
  86. expect(input).toBeInTheDocument();
  87. expect(cleanText(input?.value || "")).toEqual("01/01/2001");
  88. });
  89. it("displays the default value with format", async () => {
  90. render(
  91. <LocalizationProvider dateAdapter={AdapterDateFns}>
  92. <DateSelector
  93. defaultDate="2011-01-01T00:00:01.001Z"
  94. date={undefined as unknown as string}
  95. format="yy-MM-dd"
  96. />
  97. </LocalizationProvider>
  98. );
  99. const input = document.querySelector("input");
  100. expect(input).toBeInTheDocument();
  101. expect(cleanText(input?.value || "")).toEqual("11-01-01");
  102. });
  103. it("shows label", async () => {
  104. const { getByLabelText } = render(
  105. <LocalizationProvider dateAdapter={AdapterDateFns}>
  106. <DateSelector
  107. defaultDate="2001-01-01T00:00:01.001Z"
  108. date={undefined as unknown as string}
  109. className="taipy-date"
  110. label="a label"
  111. />
  112. </LocalizationProvider>
  113. );
  114. const input = getByLabelText("a label") as HTMLInputElement;
  115. expect(input.value).toBe("01/01/2001");
  116. });
  117. it("displays with width=70%", async () => {
  118. render(
  119. <LocalizationProvider dateAdapter={AdapterDateFns}>
  120. <DateSelector date={curDateStr} width="70%" />
  121. </LocalizationProvider>
  122. );
  123. const elt = document.querySelector(".MuiFormControl-root");
  124. expect(elt).toHaveStyle("max-width: 70%");
  125. });
  126. it("displays with width=500", async () => {
  127. render(
  128. <LocalizationProvider dateAdapter={AdapterDateFns}>
  129. <DateSelector date={curDateStr} width={500} />
  130. </LocalizationProvider>
  131. );
  132. const elt = document.querySelector(".MuiFormControl-root");
  133. expect(elt).toHaveStyle("max-width: 500px");
  134. });
  135. it("is disabled", async () => {
  136. render(
  137. <LocalizationProvider dateAdapter={AdapterDateFns}>
  138. <DateSelector date={curDateStr} active={false} />
  139. </LocalizationProvider>
  140. );
  141. const input = document.querySelector("input");
  142. expect(input).toBeInTheDocument();
  143. expect(input).toBeDisabled();
  144. });
  145. it("is enabled by default", async () => {
  146. render(
  147. <LocalizationProvider dateAdapter={AdapterDateFns}>
  148. <DateSelector date={curDateStr} />
  149. </LocalizationProvider>
  150. );
  151. const input = document.querySelector("input");
  152. expect(input).toBeInTheDocument();
  153. expect(input).not.toBeDisabled();
  154. });
  155. it("is enabled by active", async () => {
  156. render(
  157. <LocalizationProvider dateAdapter={AdapterDateFns}>
  158. <DateSelector date={curDateStr} active={true} />
  159. </LocalizationProvider>
  160. );
  161. const input = document.querySelector("input");
  162. expect(input).toBeInTheDocument();
  163. expect(input).not.toBeDisabled();
  164. });
  165. it("dispatch a well formed message", async () => {
  166. const dispatch = jest.fn();
  167. const state: TaipyState = INITIAL_STATE;
  168. render(
  169. <TaipyContext.Provider value={{ state, dispatch }}>
  170. <LocalizationProvider dateAdapter={AdapterDateFns}>
  171. <DateSelector date={curDateStr} />
  172. </LocalizationProvider>
  173. </TaipyContext.Provider>
  174. );
  175. const input = document.querySelector("input");
  176. expect(input).toBeInTheDocument();
  177. if (input) {
  178. // await userEvent.clear(input);
  179. await userEvent.type(input, "{ArrowLeft}{ArrowLeft}{ArrowLeft}01012001", { delay: 1 });
  180. expect(dispatch).toHaveBeenLastCalledWith({
  181. name: "",
  182. payload: { value: "Mon Jan 01 2001" },
  183. propagate: true,
  184. type: "SEND_UPDATE_ACTION",
  185. });
  186. }
  187. });
  188. });
  189. describe("DateSelector with time Component", () => {
  190. it("renders", async () => {
  191. const { getByTestId } = render(
  192. <LocalizationProvider dateAdapter={AdapterDateFns}>
  193. <DateSelector date={curDateStr} withTime={true} />
  194. </LocalizationProvider>
  195. );
  196. const elt = getByTestId("CalendarIcon");
  197. expect(elt.parentElement?.tagName).toBe("BUTTON");
  198. });
  199. it("displays the right info for string", async () => {
  200. const { getByTestId } = render(
  201. <LocalizationProvider dateAdapter={AdapterDateFns}>
  202. <DateSelector date={curDateStr} withTime={true} className="taipy-time" />
  203. </LocalizationProvider>
  204. );
  205. const elt = getByTestId("CalendarIcon");
  206. expect(elt.parentElement?.parentElement?.parentElement?.parentElement).toHaveClass("taipy-time-picker");
  207. expect(elt.parentElement?.parentElement?.parentElement?.parentElement?.parentElement).toHaveClass("taipy-time");
  208. });
  209. it("displays the default value", async () => {
  210. render(
  211. <LocalizationProvider dateAdapter={AdapterDateFns}>
  212. <DateSelector
  213. defaultDate="2001-01-01T01:01:01.001Z"
  214. withTime={true}
  215. date={undefined as unknown as string}
  216. />
  217. </LocalizationProvider>
  218. );
  219. const input = document.querySelector("input");
  220. expect(input).toBeInTheDocument();
  221. expect(cleanText(input?.value || "").toLocaleLowerCase()).toEqual("01/01/2001 01:01 am");
  222. });
  223. it("displays the default value with format", async () => {
  224. render(
  225. <LocalizationProvider dateAdapter={AdapterDateFns}>
  226. <DateSelector
  227. defaultDate="2011-01-01T00:10:01.001Z"
  228. date={undefined as unknown as string}
  229. format="yy-MM-dd mm"
  230. />
  231. </LocalizationProvider>
  232. );
  233. const input = document.querySelector("input");
  234. expect(input).toBeInTheDocument();
  235. expect(cleanText(input?.value || "")).toEqual("11-01-01 10");
  236. });
  237. it("is disabled", async () => {
  238. render(
  239. <LocalizationProvider dateAdapter={AdapterDateFns}>
  240. <DateSelector date={curDateStr} withTime={true} active={false} />
  241. </LocalizationProvider>
  242. );
  243. const input = document.querySelector("input");
  244. expect(input).toBeInTheDocument();
  245. expect(input).toBeDisabled();
  246. });
  247. it("is enabled by default", async () => {
  248. render(
  249. <LocalizationProvider dateAdapter={AdapterDateFns}>
  250. <DateSelector date={curDateStr} withTime={true} />
  251. </LocalizationProvider>
  252. );
  253. const input = document.querySelector("input");
  254. expect(input).toBeInTheDocument();
  255. expect(input).not.toBeDisabled();
  256. });
  257. it("is enabled by active", async () => {
  258. render(
  259. <LocalizationProvider dateAdapter={AdapterDateFns}>
  260. <DateSelector date={curDateStr} withTime={true} active={true} />
  261. </LocalizationProvider>
  262. );
  263. const input = document.querySelector("input");
  264. expect(input).toBeInTheDocument();
  265. expect(input).not.toBeDisabled();
  266. });
  267. it("dispatch a well formed message", async () => {
  268. const dispatch = jest.fn();
  269. const state: TaipyState = INITIAL_STATE;
  270. render(
  271. <TaipyContext.Provider value={{ state, dispatch }}>
  272. <LocalizationProvider dateAdapter={AdapterDateFns}>
  273. <DateSelector date={curDateStr} withTime={true} updateVarName="varname" />
  274. </LocalizationProvider>
  275. </TaipyContext.Provider>
  276. );
  277. const input = document.querySelector("input");
  278. expect(input).toBeInTheDocument();
  279. if (input) {
  280. // await userEvent.clear(input);
  281. await userEvent.type(
  282. input,
  283. "{ArrowLeft}{ArrowLeft}{ArrowLeft}{ArrowLeft}{ArrowLeft}{ArrowLeft}010120010101am",
  284. { delay: 1 }
  285. );
  286. expect(dispatch).toHaveBeenLastCalledWith({
  287. name: "varname",
  288. payload: { value: "2001-01-01T01:01:00.000Z" },
  289. propagate: true,
  290. type: "SEND_UPDATE_ACTION",
  291. });
  292. }
  293. });
  294. });