diff --git a/playwright/e2e/ime-input.spec.ts b/playwright/e2e/ime-input.spec.ts new file mode 100644 index 00000000000..a587df0d3b2 --- /dev/null +++ b/playwright/e2e/ime-input.spec.ts @@ -0,0 +1,66 @@ +/** + * SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +// Ideas taken from https://github.com/microsoft/playwright/issues/5777 and https://github.com/slab/quill/commit/0ea789f95fc4956287b3995f9495aafa367d4190 + +import { type Locator, expect, mergeTests } from '@playwright/test' +import { test as editorTest } from '../support/fixtures/editor' +import { test as uploadFileTest } from '../support/fixtures/upload-file' + +const test = mergeTests(editorTest, uploadFileTest) + +test.beforeEach(async ({ open }) => { + await open() +}) + +let composingData = '' +const withKeyboardEvents = async ( + el: Locator, + key: string, + callback: () => Promise, +) => { + composingData += key + await el.dispatchEvent('keydown', { key }) + await callback() + await el.dispatchEvent('keyup', { key }) +} + +test('Input Chinese character via IME at beginning of paragraph works', async ({ + browserName, + editor, + page, +}) => { + test.skip( + browserName !== 'chromium', + 'IME testing is currently only implemented in Chromium API', + ) + + // Get developer tools API + const client = await page.context().newCDPSession(page) + + await editor.content.focus() + + await withKeyboardEvents(editor.content, 'w', async () => { + client.send('Input.imeSetComposition', { + selectionStart: composingData.length, + selectionEnd: composingData.length, + text: 'w', + }) + }) + await withKeyboardEvents(editor.content, 'o', async () => { + client.send('Input.imeSetComposition', { + selectionStart: composingData.length, + selectionEnd: composingData.length, + text: 'o', + }) + }) + await withKeyboardEvents(editor.content, 'Space', async () => { + client.send('Input.insertText', { + text: '我', + }) + }) + + await expect(editor.content).toHaveText('我') +}) diff --git a/src/extensions/TextDirection.ts b/src/extensions/TextDirection.ts index 2ee09b4fd69..c8151ff668b 100644 --- a/src/extensions/TextDirection.ts +++ b/src/extensions/TextDirection.ts @@ -51,8 +51,8 @@ function TextDirectionPlugin({ types }: { types: string[] }) { return new Plugin({ key: new PluginKey('textDirection'), appendTransaction: (transactions, oldState, newState) => { - const isCollabTransaction = transactions.some((tr) => - tr.getMeta('y-sync$'), + const isCollabOrCompositionTransaction = transactions.some( + (tr) => tr.getMeta('y-sync$') || tr.getMeta('composition'), ) const inputRulePlugin = newState.plugins.find( (plugin) => plugin.spec.isInputRules, @@ -60,7 +60,7 @@ function TextDirectionPlugin({ types }: { types: string[] }) { const isInputRuleTransaction = inputRulePlugin && transactions.some((tr) => tr.getMeta(inputRulePlugin)) - if (isCollabTransaction || isInputRuleTransaction) { + if (isCollabOrCompositionTransaction || isInputRuleTransaction) { return } const docChanges = transactions.some(