test-utils.ts 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149
  1. import "pepjs";
  2. import {
  3. render,
  4. queries,
  5. RenderResult,
  6. RenderOptions,
  7. waitFor,
  8. } from "@testing-library/react";
  9. import * as toolQueries from "./queries/toolQueries";
  10. import { ImportedDataState } from "../data/types";
  11. import { STORAGE_KEYS } from "../excalidraw-app/data/localStorage";
  12. import { SceneData } from "../types";
  13. import { getSelectedElements } from "../scene/selection";
  14. import { ExcalidrawElement } from "../element/types";
  15. require("fake-indexeddb/auto");
  16. const customQueries = {
  17. ...queries,
  18. ...toolQueries,
  19. };
  20. type TestRenderFn = (
  21. ui: React.ReactElement,
  22. options?: Omit<
  23. RenderOptions & { localStorageData?: ImportedDataState },
  24. "queries"
  25. >,
  26. ) => Promise<RenderResult<typeof customQueries>>;
  27. const renderApp: TestRenderFn = async (ui, options) => {
  28. if (options?.localStorageData) {
  29. initLocalStorage(options.localStorageData);
  30. delete options.localStorageData;
  31. }
  32. const renderResult = render(ui, {
  33. queries: customQueries,
  34. ...options,
  35. });
  36. GlobalTestState.renderResult = renderResult;
  37. Object.defineProperty(GlobalTestState, "canvas", {
  38. // must be a getter because at the time of ExcalidrawApp render the
  39. // child App component isn't likely mounted yet (and thus canvas not
  40. // present in DOM)
  41. get() {
  42. return renderResult.container.querySelector("canvas")!;
  43. },
  44. });
  45. await waitFor(() => {
  46. const canvas = renderResult.container.querySelector("canvas");
  47. if (!canvas) {
  48. throw new Error("not initialized yet");
  49. }
  50. });
  51. return renderResult;
  52. };
  53. // re-export everything
  54. export * from "@testing-library/react";
  55. // override render method
  56. export { renderApp as render };
  57. /**
  58. * For state-sharing across test helpers.
  59. * NOTE: there shouldn't be concurrency issues as each test is running in its
  60. * own process and thus gets its own instance of this module when running
  61. * tests in parallel.
  62. */
  63. export class GlobalTestState {
  64. /**
  65. * automatically updated on each call to render()
  66. */
  67. static renderResult: RenderResult<typeof customQueries> = null!;
  68. /**
  69. * retrieves canvas for currently rendered app instance
  70. */
  71. static get canvas(): HTMLCanvasElement {
  72. return null!;
  73. }
  74. }
  75. const initLocalStorage = (data: ImportedDataState) => {
  76. if (data.elements) {
  77. localStorage.setItem(
  78. STORAGE_KEYS.LOCAL_STORAGE_ELEMENTS,
  79. JSON.stringify(data.elements),
  80. );
  81. }
  82. if (data.appState) {
  83. localStorage.setItem(
  84. STORAGE_KEYS.LOCAL_STORAGE_APP_STATE,
  85. JSON.stringify(data.appState),
  86. );
  87. }
  88. };
  89. export const updateSceneData = (data: SceneData) => {
  90. (window.collab as any).excalidrawAPI.updateScene(data);
  91. };
  92. const originalGetBoundingClientRect =
  93. global.window.HTMLDivElement.prototype.getBoundingClientRect;
  94. export const mockBoundingClientRect = () => {
  95. // override getBoundingClientRect as by default it will always return all values as 0 even if customized in html
  96. global.window.HTMLDivElement.prototype.getBoundingClientRect = () => ({
  97. top: 10,
  98. left: 20,
  99. bottom: 10,
  100. right: 10,
  101. width: 200,
  102. x: 10,
  103. y: 20,
  104. height: 100,
  105. toJSON: () => {},
  106. });
  107. };
  108. export const restoreOriginalGetBoundingClientRect = () => {
  109. global.window.HTMLDivElement.prototype.getBoundingClientRect = originalGetBoundingClientRect;
  110. };
  111. export const assertSelectedElements = (
  112. ...elements: (
  113. | (ExcalidrawElement["id"] | ExcalidrawElement)[]
  114. | ExcalidrawElement["id"]
  115. | ExcalidrawElement
  116. )[]
  117. ) => {
  118. const { h } = window;
  119. const selectedElementIds = getSelectedElements(
  120. h.app.getSceneElements(),
  121. h.state,
  122. ).map((el) => el.id);
  123. const ids = elements
  124. .flat()
  125. .map((item) => (typeof item === "string" ? item : item.id));
  126. expect(selectedElementIds.length).toBe(ids.length);
  127. expect(selectedElementIds).toEqual(expect.arrayContaining(ids));
  128. };