Skip to content

208 add tests for a richeditor#308

Open
katsyuta wants to merge 27 commits into
DeepinkApp:masterfrom
katsyuta:208-add-tests-for-a-richeditor
Open

208 add tests for a richeditor#308
katsyuta wants to merge 27 commits into
DeepinkApp:masterfrom
katsyuta:208-add-tests-for-a-richeditor

Conversation

@katsyuta

@katsyuta katsyuta commented May 30, 2026

Copy link
Copy Markdown
Collaborator

Closes #208

I added Vitest projects to separate tests by environment. Projects allow us to use different setup files and test settings for different groups of tests. To distinguish tests that require a DOM environment, I suggest using the .dom.test.ts suffix.

Summary by CodeRabbit

  • Tests

    • Added a comprehensive DOM test suite for the Rich Text editor covering markdown rendering, read-only mode, image handling, inline formatting, task lists, and list conversions.
    • Removed file-level JS DOM directives so DOM tests run under the new project config.
  • Chores

    • Split node and DOM test projects and improved DOM setup with browser-like mocks for more reliable testing.
    • Added test utilities to render the editor and simulate cursor/selection and formatting actions.
    • Updated dev test dependencies and expanded word list (added "contenteditable").

@coderabbitai

coderabbitai Bot commented May 30, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@katsyuta, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 44 minutes and 25 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: c3a2e270-db3e-4ac9-9545-89bb4bda01ea

📥 Commits

Reviewing files that changed from the base of the PR and between b31cb56 and fa800ff.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (2)
  • packages/app/src/features/NoteEditor/tests/utils/renderRichEditor.tsx
  • packages/app/src/features/NoteEditor/tests/utils/utils.ts
📝 Walkthrough

Walkthrough

Adds jsdom/test dependencies and setup, moves DOM setup into a jsdom Vitest project, installs browser-layout mocks, provides RichEditor test utilities, and adds extensive DOM tests for markdown rendering, formatting, images, and list behaviors.

Changes

RichEditor Testing Setup and Coverage

Layer / File(s) Summary
Testing infrastructure and environment setup
package.json, packages/app/vitest.config.mts, packages/app/scripts/vitest.setup.ts, packages/app/src/core/storage/interop/export/NotesExporter.dom.test.ts, words.txt
Adds @testing-library/react and jsdom devDependencies; moves DOM setup into a jsdom project in Vitest config; unconditionally loads @testing-library/jest-dom and blob-polyfill; stubs Image, Element.prototype.getClientRects, and Range.prototype.getBoundingClientRect; removes file-level @vitest-environment jsdom; adds contenteditable to wordlist.
Test helper utilities for RichEditor interaction
packages/app/src/features/NoteEditor/tests/utils/renderRichEditor.tsx, packages/app/src/features/NoteEditor/tests/utils/utils.tsx
Adds renderRichEditor helper that renders RichEditor with a test Redux store and editor/context events and returns rerender, insert, and format helpers. Adds getFirstTextNode, selectContent(startText, endText?), and setCursorPosition(node).
Comprehensive RichEditor DOM test suite
packages/app/src/features/NoteEditor/tests/richEditor.dom.test.ts
Adds DOM-level tests verifying markdown rendering (headings, paragraphs, blockquotes, HR, lists, code blocks, links), rerender behavior, read-only mode, image rendering from markdown and via insertion actions (including DOM placement), heading insertion/level toggles, inline formatting toggles and composition, nested checklist rendering, and unordered→ordered list conversion.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 I hopped into jsdom's night,
MockImage blinked, the cursor bright,
Helpers nudged the selection true,
Markdown petals opened new,
Tests now hum — the editor's right!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title '208 add tests for a richeditor' directly corresponds to the main objective of adding test coverage for the RichEditor component, which is the primary focus of the changeset.
Linked Issues check ✅ Passed The PR implements comprehensive test infrastructure and test cases for the RichEditor component, addressing all coding requirements from issue #208: Vitest setup with projects, dom.test.ts test files, and test utilities.
Out of Scope Changes check ✅ Passed All changes directly support RichEditor test implementation. The vitest configuration, test utilities, setup files, and test cases are all within scope. The words.txt addition of 'contenteditable' is a minor supporting change for spell-checking test code.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@@ -1,5 +1,3 @@
// @vitest-environment jsdom

@katsyuta katsyuta May 31, 2026

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After added projects in the vitest configuration, this annotation is no needed.
vitest ignores environment annotations when projects are used, so the file was renamed to indicate the required test environment

@katsyuta katsyuta marked this pull request as ready for review June 7, 2026 10:36
@katsyuta

katsyuta commented Jun 7, 2026

Copy link
Copy Markdown
Collaborator Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Jun 7, 2026

Copy link
Copy Markdown
✅ Action performed

Full review finished.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
packages/app/src/features/NoteEditor/tests/utils/renderRichEditor.tsx (1)

44-46: 💤 Low value

Consider simplifying the initial render.

React Testing Library's render function already wraps updates in act internally, so the explicit await act(async () => render(...)) wrapper is redundant. The async callback wrapper serves no purpose here since render is synchronous.

♻️ Simplified version
-	// Use `act` to wait for all editor updates to complete before making assertions;
-	// otherwise, some asynchronous updates may not have been applied to the DOM yet.
-	const result = await act(async () => render(renderEditor(props)));
+	const result = render(renderEditor(props));

Note: The custom rerender, insert, and format methods correctly use act since they trigger state updates outside of RTL's automatic wrapping.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/app/src/features/NoteEditor/tests/utils/renderRichEditor.tsx` around
lines 44 - 46, The initial render in renderRichEditor.tsx wraps
render(renderEditor(props)) in an unnecessary await act(...) call; replace the
explicit await act(async () => render(renderEditor(props))) with a direct call
to render(renderEditor(props)) and assign its return to result (since RTL's
render already handles act), while leaving the custom rerender, insert, and
format helpers that explicitly use act unchanged; locate the call around
renderEditor and act imports and update accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/app/scripts/vitest.setup.ts`:
- Around line 17-22: Element.prototype.getClientRects currently sets
Symbol.iterator to an empty function which returns undefined and breaks
iteration; update the mock returned by Element.prototype.getClientRects (the
DOMRectList stub) so its [Symbol.iterator] property is a function that returns
an iterator object with a next() method (e.g., returning { done: true }
immediately or iterating over an internal array), ensuring the returned object
conforms to the iterator protocol and matches DOMRectList behavior for for...of
and spread usage.

---

Nitpick comments:
In `@packages/app/src/features/NoteEditor/tests/utils/renderRichEditor.tsx`:
- Around line 44-46: The initial render in renderRichEditor.tsx wraps
render(renderEditor(props)) in an unnecessary await act(...) call; replace the
explicit await act(async () => render(renderEditor(props))) with a direct call
to render(renderEditor(props)) and assign its return to result (since RTL's
render already handles act), while leaving the custom rerender, insert, and
format helpers that explicitly use act unchanged; locate the call around
renderEditor and act imports and update accordingly.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: b7501d73-29c8-475e-96a6-79c0b93d9557

📥 Commits

Reviewing files that changed from the base of the PR and between c0a5387 and 78c1602.

⛔ Files ignored due to path filters (2)
  • package-lock.json is excluded by !**/package-lock.json
  • packages/app/src/core/storage/interop/export/__snapshots__/NotesExporter.dom.test.ts.snap is excluded by !**/*.snap
📒 Files selected for processing (8)
  • package.json
  • packages/app/scripts/vitest.setup.ts
  • packages/app/src/core/storage/interop/export/NotesExporter.dom.test.ts
  • packages/app/src/features/NoteEditor/tests/richEditor.dom.test.ts
  • packages/app/src/features/NoteEditor/tests/utils/renderRichEditor.tsx
  • packages/app/src/features/NoteEditor/tests/utils/utils.tsx
  • packages/app/vitest.config.mts
  • words.txt
💤 Files with no reviewable changes (1)
  • packages/app/src/core/storage/interop/export/NotesExporter.dom.test.ts

Comment thread packages/app/scripts/vitest.setup.ts
Comment on lines +17 to +18
const onFormatting = createEvent<TextFormat>();
const onInserting = createEvent<InsertingPayload>();

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of simulating clicks on editor panel buttons, i suggests directly call the APIs that those buttons use under the hood. So the tests is not depends on DOM structure in editor panel ans we can change the behavior of the panel without changing the tests

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/app/src/features/NoteEditor/tests/utils/renderRichEditor.tsx`:
- Around line 51-60: The test helper functions insert and format are declared
async but do not await anything, misleading callers who use await
editor.insert(...) / await editor.format(...); update the helper implementations
in renderRichEditor.tsx by removing the async keyword from the insert and format
functions (the ones calling onInserting(payload) and onFormatting(format)) so
they are plain synchronous helpers, ensuring tests do not falsely rely on
awaiting them for Lexical/React DOM flushes.

In `@packages/app/src/features/NoteEditor/tests/utils/utils.tsx`:
- Line 34: The error message in utils.tsx is using the wrong variable: when
endNode is null the thrown Error interpolates endNode (null) instead of the
search string endText; update the throw in the code that checks if (!endNode) to
reference endText (or the correct search variable) so the Error reads something
like "Text node not found for \"<searched text>\"" to show what was being looked
for (locate the check that throws when endNode is falsy and replace the
interpolated variable).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: b06610c8-eaf5-45e1-a4c2-29930f203051

📥 Commits

Reviewing files that changed from the base of the PR and between 78c1602 and 7deb02e.

📒 Files selected for processing (4)
  • packages/app/scripts/vitest.setup.ts
  • packages/app/src/features/NoteEditor/tests/richEditor.dom.test.ts
  • packages/app/src/features/NoteEditor/tests/utils/renderRichEditor.tsx
  • packages/app/src/features/NoteEditor/tests/utils/utils.tsx
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/app/scripts/vitest.setup.ts
  • packages/app/src/features/NoteEditor/tests/richEditor.dom.test.ts

Comment thread packages/app/src/features/NoteEditor/tests/utils/utils.tsx Outdated
@katsyuta katsyuta force-pushed the 208-add-tests-for-a-richeditor branch from e145c11 to b31cb56 Compare June 8, 2026 12:30
@katsyuta katsyuta requested a review from vitonsky June 8, 2026 12:55
Comment on lines +1 to +2
require('@testing-library/jest-dom');
require('blob-polyfill');

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why this code runs with no conditions? How it will work in node environment?

"/[REDACTED-UUID].md",
"/[REDACTED-UUID].md",
"/_resources/[REDACTED-UUID]-test.txt",

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why snapshot is changed?
It must not be changed. Because it means something have been changed, but it would be out of scope of that PR.

We need in reproducible tests.

Comment on lines +25 to +32
notesApi={{} as any}
filesRegistry={{} as any}
filesController={{} as any}
attachmentsController={{} as any}
tagsRegistry={{} as any}
notesRegistry={{} as any}
notesHistory={{} as any}
notesIndex={{} as any}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why we provide nothing?
We have to provide only minimal contexts that is actually used by rich editor component.
The WorkspaceProvider component is just an aggregate helper.
Everything that is actually used, must be mocked, not to be an empty object

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The directory with stuff for testing must be named as __tests__, that is a special name to distinguish the actual code and the tests code

* Selects everything from the beginning of `startText` to the end of `endText`;
* If `endText` is omitted, selects the entire `startText`.
*/
export const selectContent = (startText: string, endText?: string) => {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Incorrect abstraction.
We may potentially have many instances of the editors. So it is technically not possible to select the text in specific editor with current api.

The util must receive the container node.
Also it is unclear what happens when i pass only startText

isReadOnly: true,
});

expect(screen.getByRole('textbox')).toHaveAttribute('contenteditable', 'false');

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That expectation proves nothing. What if user will click that editor and will enter text?

value: `My favorite image\n\n I love cat`,
});

selectContent('My favorite image');

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why we select the text to insert image? It is not how actual user does act.
The real human being will click text to place cursor on some position. That is not a selection.

Comment on lines +93 to +94
const firstText = screen.getByText('My favorite image');
const secondText = screen.getByText('I love cat');

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why we define it here and don't use immediately? Move that

expect(screen.getByText(content)).toBeInTheDocument();
});

test('Toggles text formatting', async () => {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need in test that would work with a multi line text and to format its parts, not whole line.

Like that

That **text** is *partially* formatted.

Only _that_ word is selected. **Not a whole line**

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. The tests is incomplete. We need to test a user interactions that includes clicks and keyboard inputs. Add such tests. Use only DOM events for that interactions like real user does.
  2. We must split tests into few files. We will have a hundreds tests for rich editor eventually. So we must invent the rules how to organize them. Each file must have not very much tests, but we have to support very large amount of test files. We could break the tests at least for "interactive" and non interactive. The interactive ones is where user actively interact with editor (via DOM events) and we check that editor behaves as expected. Also we could breakdown them by scope like formatting, editing, etc. Think about that, maybe consult with ChatGPT
  3. We have to support the tests with many editors. I don't see such tests that proves that 2 instances will work fine together in one document
  4. I don't see any cleanups before each test. How it does work? We must be able to control the DOM.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add tests for a RichEditor

2 participants