Custom task states: improvements#9985
Conversation
There was a problem hiding this comment.
Code Review
This pull request introduces a comprehensive suite of unit tests for the CKEditor 5 multi-state todo list plugin, covering commands, HTML conversion, keystrokes, post-fixers, and schema definitions, while configuring Vitest and Happy DOM for the test environment. The review feedback correctly identifies a DOM element leak in two test files where a new domElement is created and appended without first removing the previous instance from the document body.
| await editor.destroy(); | ||
| domElement = document.createElement("div"); |
There was a problem hiding this comment.
The previous domElement is not removed from the document body before a new one is created and appended. Since afterEach only removes the element currently referenced by domElement, the first div element will leak in the DOM during test execution. Removing the old element before reassigning domElement prevents this leak.
| await editor.destroy(); | |
| domElement = document.createElement("div"); | |
| await editor.destroy(); | |
| domElement.remove(); | |
| domElement = document.createElement("div"); |
| await editor.destroy(); | ||
| domElement = document.createElement("div"); |
There was a problem hiding this comment.
The previous domElement is not removed from the document body before a new one is created and appended. Since afterEach only removes the element currently referenced by domElement, the first div element will leak in the DOM during test execution. Removing the old element before reassigning domElement prevents this leak.
| await editor.destroy(); | |
| domElement = document.createElement("div"); | |
| await editor.destroy(); | |
| domElement.remove(); | |
| domElement = document.createElement("div"); |
Greptile SummaryThis PR adds a Vitest unit-test suite for the
Confidence Score: 4/5Safe to merge — changes are test-only and do not touch production code paths. The test infrastructure and logic are sound overall, but two tests leave orphaned DOM elements when they recreate the editor inline, and tsconfig.test.json is not wired into the project reference graph so type errors in tests escape pnpm typecheck. Neither issue affects production behaviour. tsconfig.json (missing reference to tsconfig.test.json), todo-list-multistate-command.ts and todo-list-multistate-keystroke.ts (DOM element cleanup in inline editor-recreation tests). Important Files Changed
|
| it("custom isCompleted=true state ticks the native checkbox via the post-fixer", async () => { | ||
| await editor.destroy(); | ||
| domElement = document.createElement("div"); |
There was a problem hiding this comment.
Leaked DOM element when recreating the editor
editor.destroy() cleans up the CKEditor instance but the original domElement (created in beforeEach) remains attached to document.body once domElement is reassigned to the new element. domElement.remove() before the reassignment ensures the old host element is detached. The same pattern appears in todo-list-multistate-keystroke.ts.
| it("custom isCompleted=true state ticks the native checkbox via the post-fixer", async () => { | |
| await editor.destroy(); | |
| domElement = document.createElement("div"); | |
| it("custom isCompleted=true state ticks the native checkbox via the post-fixer", async () => { | |
| await editor.destroy(); | |
| domElement.remove(); | |
| domElement = document.createElement("div"); |
| it("skips hidden states in the cycle", async () => { | ||
| await editor.destroy(); | ||
| domElement = document.createElement("div"); | ||
| document.body.appendChild(domElement); |
There was a problem hiding this comment.
Leaked DOM element when recreating the editor
Same pattern as in todo-list-multistate-command.ts: the original domElement from beforeEach remains in document.body after domElement is reassigned. Add domElement.remove() before reassigning to avoid accumulating orphaned elements across test runs.
| it("skips hidden states in the cycle", async () => { | |
| await editor.destroy(); | |
| domElement = document.createElement("div"); | |
| document.body.appendChild(domElement); | |
| it("skips hidden states in the cycle", async () => { | |
| await editor.destroy(); | |
| domElement.remove(); | |
| domElement = document.createElement("div"); | |
| document.body.appendChild(domElement); |
| domElement.remove(); | ||
| return editor.destroy(); | ||
| }); | ||
|
|
There was a problem hiding this comment.
domElement.remove() called before editor.destroy() in every afterEach
All five test files remove the host element from the DOM before calling editor.destroy(). CKEditor's teardown may still need the element to be connected (e.g. to clean up MutationObserver, focus traps, or view observers). The recommended CKEditor pattern is await editor.destroy() first, then detach the element. Because destroy() is returned (not awaited), a rejection from destroy() would also be silently dropped in the happy-dom environment. This same ordering issue exists in all five test files.
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
No description provided.