setupTests.ts 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122
  1. // vitest.setup.ts
  2. import "vitest-canvas-mock";
  3. import "@testing-library/jest-dom";
  4. import fs from "fs";
  5. import { vi } from "vitest";
  6. import polyfill from "./packages/excalidraw/polyfill";
  7. import { testPolyfills } from "./packages/excalidraw/tests/helpers/polyfills";
  8. import { yellow } from "./packages/excalidraw/tests/helpers/colorize";
  9. // mock for pep.js not working with setPointerCapture()
  10. HTMLElement.prototype.setPointerCapture = vi.fn();
  11. Object.assign(globalThis, testPolyfills);
  12. require("fake-indexeddb/auto");
  13. polyfill();
  14. Object.defineProperty(window, "matchMedia", {
  15. writable: true,
  16. value: vi.fn().mockImplementation((query) => ({
  17. matches: false,
  18. media: query,
  19. onchange: null,
  20. addListener: vi.fn(), // deprecated
  21. removeListener: vi.fn(), // deprecated
  22. addEventListener: vi.fn(),
  23. removeEventListener: vi.fn(),
  24. dispatchEvent: vi.fn(),
  25. })),
  26. });
  27. Object.defineProperty(window, "FontFace", {
  28. enumerable: true,
  29. value: class {
  30. private family: string;
  31. private source: string;
  32. private descriptors: any;
  33. private status: string;
  34. private unicodeRange: string;
  35. constructor(family, source, descriptors) {
  36. this.family = family;
  37. this.source = source;
  38. this.descriptors = descriptors;
  39. this.status = "unloaded";
  40. this.unicodeRange = "U+0000-00FF";
  41. }
  42. load() {
  43. this.status = "loaded";
  44. }
  45. },
  46. });
  47. Object.defineProperty(document, "fonts", {
  48. value: {
  49. load: vi.fn().mockResolvedValue([]),
  50. check: vi.fn().mockResolvedValue(true),
  51. has: vi.fn().mockResolvedValue(true),
  52. add: vi.fn(),
  53. },
  54. });
  55. Object.defineProperty(window, "EXCALIDRAW_ASSET_PATH", {
  56. value: `file://${__dirname}/`,
  57. });
  58. // mock the font fetch only, so that everything else, as font subsetting, can run inside of the (snapshot) tests
  59. vi.mock(
  60. "./packages/excalidraw/fonts/ExcalidrawFontFace",
  61. async (importOriginal) => {
  62. const mod = await importOriginal<
  63. typeof import("./packages/excalidraw/fonts/ExcalidrawFontFace")
  64. >();
  65. const ExcalidrawFontFaceImpl = mod.ExcalidrawFontFace;
  66. return {
  67. ...mod,
  68. ExcalidrawFontFace: class extends ExcalidrawFontFaceImpl {
  69. public async fetchFont(url: URL): Promise<ArrayBuffer> {
  70. if (!url.toString().startsWith("file://")) {
  71. return super.fetchFont(url);
  72. }
  73. // read local assets directly, without running a server
  74. const content = await fs.promises.readFile(url);
  75. return content.buffer;
  76. }
  77. },
  78. };
  79. },
  80. );
  81. vi.mock("nanoid", () => {
  82. return {
  83. nanoid: vi.fn(() => "test-id"),
  84. };
  85. });
  86. // ReactDOM is located inside index.tsx file
  87. // as a result, we need a place for it to render into
  88. const element = document.createElement("div");
  89. element.id = "root";
  90. document.body.appendChild(element);
  91. const _consoleError = console.error.bind(console);
  92. console.error = (...args) => {
  93. // the react's act() warning usually doesn't contain any useful stack trace
  94. // so we're catching the log and re-logging the message with the test name,
  95. // also stripping the actual component stack trace as it's not useful
  96. if (args[0]?.includes?.("act(")) {
  97. _consoleError(
  98. yellow(
  99. `<<< WARNING: test "${
  100. expect.getState().currentTestName
  101. }" does not wrap some state update in act() >>>`,
  102. ),
  103. );
  104. } else {
  105. _consoleError(...args);
  106. }
  107. };