Skip to content

Commit

Permalink
Add tests for IME
Browse files Browse the repository at this point in the history
  • Loading branch information
luin committed Nov 3, 2023
1 parent dc303a9 commit 854a75d
Show file tree
Hide file tree
Showing 5 changed files with 174 additions and 1 deletion.
108 changes: 108 additions & 0 deletions e2e/fixtures/Composition.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import type {
CDPSession,
Page,
PlaywrightWorkerArgs,
PlaywrightWorkerOptions,
} from '@playwright/test';

abstract class CompositionSession {
abstract update(key: string): Promise<void>;
abstract commit(committedText: string): Promise<void>;

protected composingData = '';

constructor(protected page: Page) {}

protected async withKeyboardEvents(
key: string,
callback: () => Promise<void>,
) {
const activeElement = this.page.locator('*:focus');

await activeElement.dispatchEvent('keydown', { key });
await callback();
await activeElement.dispatchEvent('keyup', { key });
}
}

class ChromiumCompositionSession extends CompositionSession {
constructor(
page: Page,
private session: CDPSession,
) {
super(page);
}

async update(key: string) {
await this.withKeyboardEvents(key, async () => {
this.composingData += key;

await this.session.send('Input.imeSetComposition', {
selectionStart: this.composingData.length,
selectionEnd: this.composingData.length,
text: this.composingData,
});
});
}

async commit(committedText: string) {
await this.withKeyboardEvents('Space', async () => {
await this.session.send('Input.insertText', {
text: committedText,
});
});
}
}

class WebkitCompositionSession extends CompositionSession {
constructor(
page: Page,
private session: any,
) {
super(page);
}

async update(key: string) {
await this.withKeyboardEvents(key, async () => {
this.composingData += key;

await this.session.send('Page.setComposition', {
selectionStart: this.composingData.length,
selectionLength: 0,
text: this.composingData,
});
});
}

async commit(committedText: string) {
await this.withKeyboardEvents('Space', async () => {
await this.page.keyboard.insertText(committedText);
});
}
}

class Composition {
constructor(
private page: Page,
private browserName: PlaywrightWorkerOptions['browserName'],
private playwright: PlaywrightWorkerArgs['playwright'],
) {}

async start() {
switch (this.browserName) {
case 'chromium': {
const session = await this.page.context().newCDPSession(this.page);
return new ChromiumCompositionSession(this.page, session);
}
case 'webkit': {
const session = (await (this.playwright as any)._toImpl(this.page))
._delegate._session;
return new WebkitCompositionSession(this.page, session);
}
default:
throw new Error(`Unsupported browser: ${this.browserName}`);
}
}
}

export default Composition;
10 changes: 10 additions & 0 deletions e2e/fixtures/index.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,23 @@
import { test as base } from '@playwright/test';
import EditorPage from '../pageobjects/EditorPage';
import Composition from './Composition';

export const test = base.extend<{
editorPage: EditorPage;
clipboard: Clipboard;
composition: Composition;
}>({
editorPage: ({ page }, use) => {
use(new EditorPage(page));
},
composition: ({ page, browserName, playwright }, use) => {
test.fail(
browserName === 'firefox',
'CDPSession is not available in Firefox',
);

use(new Composition(page, browserName, playwright));
},
});

export const CHAPTER = 'Chapter 1. Loomings.';
Expand Down
24 changes: 23 additions & 1 deletion e2e/list.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { expect } from '@playwright/test';
import { test } from './fixtures';
import { isMac } from './utils';

const listTypes = ['bullet', 'ordered', 'checked'];
const listTypes = ['bullet', 'checked'];

test.describe('list', () => {
test.beforeEach(async ({ editorPage }) => {
Expand Down Expand Up @@ -75,5 +75,27 @@ test.describe('list', () => {
]);
});
});

// https://github.com/quilljs/quill/issues/3837
test(`typing at beginning with IME (${list})`, async ({
editorPage,
composition,
}) => {
await editorPage.setContents([
{ insert: 'item 1' },
{ insert: '\n', attributes: { list } },
{ insert: '' },
{ insert: '\n', attributes: { list } },
]);

await editorPage.setSelection(7, 0);
await editorPage.typeWordWithIME(composition, '我');
expect(await editorPage.getContents()).toEqual([
{ insert: 'item 1' },
{ insert: '\n', attributes: { list } },
{ insert: '我' },
{ insert: '\n', attributes: { list } },
]);
});
}
});
21 changes: 21 additions & 0 deletions e2e/pageobjects/EditorPage.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { Page } from '@playwright/test';
import type Composition from '../fixtures/Composition';

interface Op {
insert?: string | Record<string, unknown>;
Expand Down Expand Up @@ -81,6 +82,26 @@ export default class EditorPage {
});
}

async setSelection(index: number, length: number): Promise<void>;
async setSelection(range: { index: number; length: number }): Promise<void>;
async setSelection(
range: { index: number; length: number } | number,
length?: number,
) {
await this.page.evaluate(
// @ts-expect-error
(range) => window.quill.setSelection(range),
typeof range === 'number' ? { index: range, length: length || 0 } : range,
);
}

async typeWordWithIME(composition: Composition, composedWord: string) {
const ime = await composition.start();
await ime.update('w');
await ime.update('o');
await ime.commit(composedWord);
}

async cutoffHistory() {
await this.page.evaluate(() => {
// @ts-expect-error
Expand Down
12 changes: 12 additions & 0 deletions e2e/replaceSelection.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,18 @@ test.describe('replace selection', () => {
expect(await editorPage.getContents()).toEqual([{ insert: '1\n\n' }]);
});

test('with IME', async ({ editorPage, composition }) => {
await editorPage.setContents([
{ insert: '1' },
{ insert: '2', attributes: { color: 'red' } },
{ insert: '3\n' },
]);
await editorPage.selectText('2', '3');
await editorPage.typeWordWithIME(composition, '我');
expect(await editorPage.root.innerHTML()).toEqual('<p>1我</p>');
expect(await editorPage.getContents()).toEqual([{ insert: '1我\n' }]);
});

test('after a bold text', async ({ page, editorPage }) => {
await editorPage.setContents([
{ insert: '1', attributes: { bold: true } },
Expand Down

0 comments on commit 854a75d

Please sign in to comment.