From 4993ea2bbe720cd7a372d456386500de9eb448c2 Mon Sep 17 00:00:00 2001 From: kolaente Date: Tue, 16 Dec 2025 23:56:05 +0100 Subject: [PATCH] fix(editor): prevent TypeError when typing mentions in comments (#1997) Fix TypeError when typing `@` mentions in task comments, add null checks for `component` variable in `mentionSuggestion.ts` to handle race conditions where `onUpdate` or `onKeyDown` fire before `onStart` completes --- .../input/editor/mention/mentionSuggestion.ts | 4 +- .../tests/e2e/task/mention-in-comment.spec.ts | 79 +++++++++++++++++++ 2 files changed, 81 insertions(+), 2 deletions(-) create mode 100644 frontend/tests/e2e/task/mention-in-comment.spec.ts diff --git a/frontend/src/components/input/editor/mention/mentionSuggestion.ts b/frontend/src/components/input/editor/mention/mentionSuggestion.ts index 55a60abae..0aaf756a8 100644 --- a/frontend/src/components/input/editor/mention/mentionSuggestion.ts +++ b/frontend/src/components/input/editor/mention/mentionSuggestion.ts @@ -145,7 +145,7 @@ export default function mentionSuggestionSetup(projectId: number) { items: MentionItem[] command: (item: MentionItem) => void }) { - component.updateProps(props) + component?.updateProps(props) if (!props.clientRect || !popupElement) { return @@ -171,7 +171,7 @@ export default function mentionSuggestionSetup(projectId: number) { return true } - return component.ref?.onKeyDown(props) + return component?.ref?.onKeyDown(props) }, onExit() { diff --git a/frontend/tests/e2e/task/mention-in-comment.spec.ts b/frontend/tests/e2e/task/mention-in-comment.spec.ts new file mode 100644 index 000000000..21c558e08 --- /dev/null +++ b/frontend/tests/e2e/task/mention-in-comment.spec.ts @@ -0,0 +1,79 @@ +import {test, expect} from '../../support/fixtures' +import {ProjectFactory} from '../../factories/project' +import {TaskFactory} from '../../factories/task' +import {TaskCommentFactory} from '../../factories/task_comment' +import {createDefaultViews} from '../project/prepareProjects' + +test.describe('Mention in task comment', () => { + test.beforeEach(async ({authenticatedPage: page}) => { + await ProjectFactory.create(1) + await createDefaultViews(1) + await TaskFactory.create(1, {id: 1}) + await TaskCommentFactory.truncate() + }) + + test('typing @ in comment editor does not throw TypeError', async ({authenticatedPage: page}) => { + // Collect console errors + const consoleErrors: string[] = [] + page.on('console', (msg) => { + if (msg.type() === 'error') { + consoleErrors.push(msg.text()) + } + }) + + await page.goto('/tasks/1') + await page.waitForLoadState('networkidle') + + // Wait for comment input editor to be visible (the editable one) + const commentEditor = page.locator('.task-view .comments .media.comment .tiptap__editor .tiptap.ProseMirror[contenteditable="true"]') + await expect(commentEditor).toBeVisible({timeout: 10000}) + + // Click to focus the editor + await commentEditor.click() + + // Type @ to trigger mention suggestion + await commentEditor.pressSequentially('@', {delay: 50}) + + // Wait a bit for any async operations + await page.waitForTimeout(500) + + // Type more characters to trigger updates + await commentEditor.pressSequentially('test', {delay: 50}) + + // Wait for debounce and any potential errors + await page.waitForTimeout(500) + + // Press Escape to close any mention popup + await page.keyboard.press('Escape') + + // Verify no TypeErrors related to mention component were logged + const mentionErrors = consoleErrors.filter(err => + err.includes('TypeError') && + (err.includes('updateProps') || err.includes('onKeyDown') || err.includes('ref')), + ) + + expect(mentionErrors).toHaveLength(0) + }) + + test('can type mention without error notifications appearing', async ({authenticatedPage: page}) => { + await page.goto('/tasks/1') + await page.waitForLoadState('networkidle') + + // Wait for comment input editor to be visible (the editable one) + const commentEditor = page.locator('.task-view .comments .media.comment .tiptap__editor .tiptap.ProseMirror[contenteditable="true"]') + await expect(commentEditor).toBeVisible({timeout: 10000}) + + // Click to focus the editor + await commentEditor.click() + + // Type @ to trigger mention suggestion + await commentEditor.pressSequentially('@user', {delay: 50}) + + // Wait for potential error notifications to appear + await page.waitForTimeout(1000) + + // Verify no error notification appeared + const errorNotification = page.locator('.global-notification.is-danger, .global-notification.error') + await expect(errorNotification).not.toBeVisible() + }) +})