Skip to content

Commit cdcbc35

Browse files
committed
add unit tests
1 parent 17bff63 commit cdcbc35

File tree

3 files changed

+88
-2
lines changed

3 files changed

+88
-2
lines changed
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
import { BlockNoteEditor } from "@blocknote/core";
2+
import { BlockNoteView } from "@blocknote/mantine";
3+
import "@blocknote/mantine/style.css";
4+
import { FormattingToolbar } from "@blocknote/react";
5+
import { flushSync } from "react-dom";
6+
import { createRoot } from "react-dom/client";
7+
import { afterEach, beforeEach, describe, expect, it } from "vitest";
8+
9+
describe("FormattingToolbar unmount", () => {
10+
let div: HTMLDivElement;
11+
12+
beforeEach(() => {
13+
div = document.createElement("div");
14+
document.body.appendChild(div);
15+
});
16+
17+
afterEach(() => {
18+
document.body.removeChild(div);
19+
});
20+
21+
it("should not throw error when unmounting editor with FormattingToolbar", () => {
22+
const editor = BlockNoteEditor.create();
23+
24+
const root = createRoot(div);
25+
26+
// Mount the editor with FormattingToolbar
27+
// This will cause CreateLinkButton to mount and register its event listeners
28+
flushSync(() => {
29+
root.render(
30+
<BlockNoteView editor={editor}>
31+
<FormattingToolbar />
32+
</BlockNoteView>,
33+
);
34+
});
35+
36+
// Unmount should not throw error
37+
// Before the fix in commit 17bff6362, this would throw:
38+
// "Cannot read properties of undefined (reading 'removeEventListener')"
39+
// because CreateLinkButton's useEffect cleanup tries to access
40+
// editor.prosemirrorView.dom.removeEventListener()
41+
// but prosemirrorView.dom is undefined when the editor is being destroyed
42+
expect(() => {
43+
root.unmount();
44+
}).not.toThrow();
45+
46+
// Cleanup
47+
editor._tiptapEditor.destroy();
48+
});
49+
50+
it("should handle rapid mount/unmount cycles", () => {
51+
// Test multiple mount/unmount cycles
52+
for (let i = 0; i < 3; i++) {
53+
const editor = BlockNoteEditor.create();
54+
const root = createRoot(div);
55+
56+
flushSync(() => {
57+
root.render(
58+
<BlockNoteView editor={editor}>
59+
<FormattingToolbar />
60+
</BlockNoteView>,
61+
);
62+
});
63+
64+
// Should not throw on unmount
65+
expect(() => {
66+
root.unmount();
67+
}).not.toThrow();
68+
69+
editor._tiptapEditor.destroy();
70+
}
71+
});
72+
});

tests/vite.config.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
11
import * as path from "path";
22
import { defineConfig } from "vite";
3-
import eslintPlugin from "vite-plugin-eslint";
43

54
// https://vitejs.dev/config/
65
export default defineConfig((conf) => ({
76
test: {
87
environment: "jsdom",
98
setupFiles: ["./vitestSetup.ts"],
10-
include: ["./src/unit/**/*.test.ts"],
9+
include: ["./src/unit/**/*.test.ts", "./src/unit/**/*.test.tsx"],
1110
},
1211
resolve: {
1312
alias:

tests/vitestSetup.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,3 +33,18 @@ class DragEventMock extends Event {
3333
};
3434
}
3535
(global as any).DragEvent = DragEventMock;
36+
37+
// Mock matchMedia for Mantine
38+
Object.defineProperty(window, "matchMedia", {
39+
writable: true,
40+
value: (query: string) => ({
41+
matches: false,
42+
media: query,
43+
onchange: null,
44+
addListener: () => {}, // deprecated
45+
removeListener: () => {}, // deprecated
46+
addEventListener: () => {},
47+
removeEventListener: () => {},
48+
dispatchEvent: () => {},
49+
}),
50+
});

0 commit comments

Comments
 (0)