diff --git a/packages/quill/test/e2e/__dev_server__/iframe.html b/packages/quill/test/e2e/__dev_server__/iframe.html new file mode 100644 index 0000000000..1b44556495 --- /dev/null +++ b/packages/quill/test/e2e/__dev_server__/iframe.html @@ -0,0 +1,95 @@ + + +
+ + + +The Whale
'); +function epicTest(mode: 'regular' | 'iframe' = 'regular') { + testWithMode(mode)( + `compose an epic (${mode})`, + async ({ page, editorPage }) => { + await editorPage.open(); + await editorPage.root.pressSequentially('The Whale'); + expect(await editorPage.root.innerHTML()).toEqual('The Whale
'); - await page.keyboard.press('Enter'); - expect(await editorPage.root.innerHTML()).toEqual( - 'The Whale
The Whale
The Whale
', - '\t${P1}
`, - '${P2}
`, - ].join(''), - ); + await page.keyboard.press('Enter'); + await page.keyboard.press('Tab'); + await page.keyboard.type(P1); + await page.keyboard.press('Enter'); + await page.keyboard.press('Enter'); + await editorPage.root.pressSequentially(P2); + expect(await editorPage.root.innerHTML()).toEqual( + [ + 'The Whale
', + '\t${P1}
`, + '${P2}
`, + ].join(''), + ); - // More than enough to get to top - await Promise.all( - Array(40) - .fill(0) - .map(() => page.keyboard.press('ArrowUp')), - ); - await page.keyboard.press('ArrowDown'); - await page.keyboard.press('Enter'); - await page.type('.ql-editor', CHAPTER); - await page.keyboard.press('Enter'); - expect(await editorPage.root.innerHTML()).toEqual( - [ - 'The Whale
', - '${CHAPTER}
`, - '\t${P1}
`, - '${P2}
`, - ].join(''), - ); + // More than enough to get to top + await Promise.all( + Array(40) + .fill(0) + .map(() => page.keyboard.press('ArrowUp')), + ); + await page.keyboard.press('ArrowDown'); + await page.keyboard.press('Enter'); + await editorPage.root.pressSequentially(CHAPTER); + await page.keyboard.press('Enter'); + expect(await editorPage.root.innerHTML()).toEqual( + [ + 'The Whale
', + '${CHAPTER}
`, + '\t${P1}
`, + '${P2}
`, + ].join(''), + ); - // More than enough to get to top - await Promise.all( - Array(20) - .fill(0) - .map(() => page.keyboard.press('ArrowUp')), - ); - await page.keyboard.press('ArrowRight'); - await page.keyboard.press('ArrowRight'); - await page.keyboard.press('ArrowRight'); - await page.keyboard.press('ArrowRight'); - await page.keyboard.press('Backspace'); - await page.keyboard.press('Backspace'); - await page.keyboard.press('Backspace'); - await page.keyboard.press('Backspace'); - expect(await editorPage.root.innerHTML()).toEqual( - [ - 'Whale
', - '${CHAPTER}
`, - '\t${P1}
`, - '${P2}
`, - ].join(''), - ); + // More than enough to get to top + await Promise.all( + Array(20) + .fill(0) + .map(() => page.keyboard.press('ArrowUp')), + ); + await page.keyboard.press('ArrowRight'); + await page.keyboard.press('ArrowRight'); + await page.keyboard.press('ArrowRight'); + await page.keyboard.press('ArrowRight'); + await page.keyboard.press('Backspace'); + await page.keyboard.press('Backspace'); + await page.keyboard.press('Backspace'); + await page.keyboard.press('Backspace'); + expect(await editorPage.root.innerHTML()).toEqual( + [ + 'Whale
', + '${CHAPTER}
`, + '\t${P1}
`, + '${P2}
`, + ].join(''), + ); - await page.keyboard.press('Delete'); - await page.keyboard.press('Delete'); - await page.keyboard.press('Delete'); - await page.keyboard.press('Delete'); - await page.keyboard.press('Delete'); - expect(await editorPage.root.innerHTML()).toEqual( - [ - '${CHAPTER}
`, - '\t${P1}
`, - '${P2}
`, - ].join(''), - ); + await page.keyboard.press('Delete'); + await page.keyboard.press('Delete'); + await page.keyboard.press('Delete'); + await page.keyboard.press('Delete'); + await page.keyboard.press('Delete'); + expect(await editorPage.root.innerHTML()).toEqual( + [ + '${CHAPTER}
`, + '\t${P1}
`, + '${P2}
`, + ].join(''), + ); - await page.keyboard.press('Delete'); - expect(await editorPage.root.innerHTML()).toEqual( - [ - '${CHAPTER}
`, - '\t${P1}
`, - '${P2}
`, - ].join(''), - ); + await page.keyboard.press('Delete'); + expect(await editorPage.root.innerHTML()).toEqual( + [ + '${CHAPTER}
`, + '\t${P1}
`, + '${P2}
`, + ].join(''), + ); - await page.click('.ql-toolbar .ql-bold'); - await page.click('.ql-toolbar .ql-italic'); - expect(await editorPage.root.innerHTML()).toEqual( - [ - '\uFEFF
', - `${CHAPTER}
`, - '\t${P1}
`, - '${P2}
`, - ].join(''), - ); - let bold = await page.$('.ql-toolbar .ql-bold.ql-active'); - let italic = await page.$('.ql-toolbar .ql-italic.ql-active'); - expect(bold).not.toBe(null); - expect(italic).not.toBe(null); + await editorPage.toolbar.locator('.ql-bold').click(); + await editorPage.toolbar.locator('.ql-italic').click(); + expect(await editorPage.root.innerHTML()).toEqual( + [ + '\uFEFF
', + `${CHAPTER}
`, + '\t${P1}
`, + '${P2}
`, + ].join(''), + ); + let bold = editorPage.toolbar.locator('.ql-bold'); + let italic = editorPage.toolbar.locator('.ql-italic'); + expect(bold).toHaveClass(/ql-active/); + expect(italic).toHaveClass(/ql-active/); - await editorPage.root.pressSequentially('Moby Dick'); - expect(await editorPage.root.innerHTML()).toEqual( - [ - 'Moby Dick
', - `${CHAPTER}
`, - '\t${P1}
`, - '${P2}
`, - ].join(''), - ); - bold = await page.$('.ql-toolbar .ql-bold.ql-active'); - italic = await page.$('.ql-toolbar .ql-italic.ql-active'); - expect(bold).not.toBe(null); - expect(italic).not.toBe(null); + await editorPage.root.pressSequentially('Moby Dick'); + expect(await editorPage.root.innerHTML()).toEqual( + [ + 'Moby Dick
', + `${CHAPTER}
`, + '\t${P1}
`, + '${P2}
`, + ].join(''), + ); + bold = editorPage.toolbar.locator('.ql-bold'); + italic = editorPage.toolbar.locator('.ql-italic'); + expect(bold).toHaveClass(/ql-active/); + expect(italic).toHaveClass(/ql-active/); - await page.keyboard.press('ArrowRight'); - await page.keyboard.down('Shift'); - await Promise.all( - Array(CHAPTER.length) - .fill(0) - .map(() => page.keyboard.press('ArrowRight')), - ); - await page.keyboard.up('Shift'); - bold = await page.$('.ql-toolbar .ql-bold.ql-active'); - italic = await page.$('.ql-toolbar .ql-italic.ql-active'); - expect(bold).toBe(null); - expect(italic).toBe(null); + await page.keyboard.press('ArrowRight'); + await page.keyboard.down('Shift'); + await Promise.all( + Array(CHAPTER.length) + .fill(0) + .map(() => page.keyboard.press('ArrowRight')), + ); + await page.keyboard.up('Shift'); + bold = editorPage.toolbar.locator('.ql-bold'); + italic = editorPage.toolbar.locator('.ql-italic'); + expect(bold).not.toHaveClass(/ql-active/); + expect(italic).not.toHaveClass(/ql-active/); - await page.keyboard.down(SHORTKEY); - await page.keyboard.press('b'); - await page.keyboard.up(SHORTKEY); - bold = await page.$('.ql-toolbar .ql-bold.ql-active'); - expect(bold).not.toBe(null); - expect(await editorPage.root.innerHTML()).toEqual( - [ - 'Moby Dick
', - `${CHAPTER}
`, - '\t${P1}
`, - '${P2}
`, - ].join(''), - ); + await page.keyboard.down(SHORTKEY); + await page.keyboard.press('b'); + await page.keyboard.up(SHORTKEY); + bold = editorPage.toolbar.locator('.ql-bold'); + expect(bold).toHaveClass(/ql-active/); + expect(await editorPage.root.innerHTML()).toEqual( + [ + 'Moby Dick
', + `${CHAPTER}
`, + '\t${P1}
`, + '${P2}
`, + ].join(''), + ); - await page.keyboard.press('ArrowLeft'); - await page.keyboard.press('ArrowUp'); - await page.click('.ql-toolbar .ql-header[value="1"]'); - expect(await editorPage.root.innerHTML()).toEqual( - [ - '${CHAPTER}
`, - '\t${P1}
`, - '${P2}
`, - ].join(''), - ); - const header = await page.$('.ql-toolbar .ql-header.ql-active[value="1"]'); - expect(header).not.toBe(null); + await page.keyboard.press('ArrowLeft'); + await page.keyboard.press('ArrowUp'); + await editorPage.toolbar.locator('.ql-header[value="1"]').click(); + expect(await editorPage.root.innerHTML()).toEqual( + [ + '${CHAPTER}
`, + '\t${P1}
`, + '${P2}
`, + ].join(''), + ); + const header = editorPage.toolbar.locator('.ql-header[value="1"]'); + expect(header).toHaveClass(/ql-active/); - await page.keyboard.press('ArrowDown'); - await page.keyboard.press('ArrowDown'); - await page.keyboard.press('Enter'); - await page.keyboard.press('Enter'); - await page.keyboard.press('ArrowUp'); - await editorPage.root.pressSequentially('AA'); - await page.keyboard.press('ArrowLeft'); - await page.keyboard.down(SHORTKEY); - await page.keyboard.press('b'); - await page.keyboard.press('b'); - await page.keyboard.up(SHORTKEY); - await editorPage.root.pressSequentially('B'); - expect(await editorPage.root.locator('p').nth(2).innerHTML()).toBe('ABA'); - await page.keyboard.down(SHORTKEY); - await page.keyboard.press('b'); - await page.keyboard.up(SHORTKEY); - await editorPage.root.pressSequentially('C'); - await page.keyboard.down(SHORTKEY); - await page.keyboard.press('b'); - await page.keyboard.up(SHORTKEY); - await editorPage.root.pressSequentially('D'); - expect(await editorPage.root.locator('p').nth(2).innerHTML()).toBe( - 'ABCDA', + await page.keyboard.press('ArrowDown'); + await page.keyboard.press('ArrowDown'); + await page.keyboard.press('Enter'); + await page.keyboard.press('Enter'); + await page.keyboard.press('ArrowUp'); + await editorPage.root.pressSequentially('AA'); + await page.keyboard.press('ArrowLeft'); + await page.keyboard.down(SHORTKEY); + await page.keyboard.press('b'); + await page.keyboard.press('b'); + await page.keyboard.up(SHORTKEY); + await editorPage.root.pressSequentially('B'); + expect(await editorPage.root.locator('p').nth(2).innerHTML()).toBe('ABA'); + await page.keyboard.down(SHORTKEY); + await page.keyboard.press('b'); + await page.keyboard.up(SHORTKEY); + await editorPage.root.pressSequentially('C'); + await page.keyboard.down(SHORTKEY); + await page.keyboard.press('b'); + await page.keyboard.up(SHORTKEY); + await editorPage.root.pressSequentially('D'); + expect(await editorPage.root.locator('p').nth(2).innerHTML()).toBe( + 'ABCDA', + ); + const selection = await page.evaluate(getSelectionInTextNode); + expect(selection).toBe('["DA",1,"DA",1]'); + }, ); - const selection = await page.evaluate(getSelectionInTextNode); - expect(selection).toBe('["DA",1,"DA",1]'); -}); +} + +epicTest(); +epicTest('iframe'); diff --git a/packages/quill/test/e2e/pageobjects/EditorPage.ts b/packages/quill/test/e2e/pageobjects/EditorPage.ts index 0e770e0891..292cfbae2d 100644 --- a/packages/quill/test/e2e/pageobjects/EditorPage.ts +++ b/packages/quill/test/e2e/pageobjects/EditorPage.ts @@ -55,15 +55,33 @@ const updateSelectionDef = [ ]; export default class EditorPage { - constructor(protected readonly page: Page) {} + constructor( + protected readonly page: Page, + protected readonly mode: 'regular' | 'iframe' = 'regular', + ) {} get root() { + if (this.mode === 'iframe') { + return this.page.frameLocator('iframe').locator('.ql-editor'); + } return this.page.locator('.ql-editor'); } + get toolbar() { + if (this.mode === 'iframe') { + return this.page.frameLocator('iframe').locator('#toolbar-container'); + } + return this.page.locator('#toolbar-container'); + } + async open() { - await this.page.goto('/'); - await this.page.waitForSelector('.ql-editor', { timeout: 10000 }); + await this.page.goto(this.mode === 'iframe' ? '/iframe.html' : '/'); + await this.page.waitForSelector( + this.mode === 'iframe' ? 'iframe' : '.ql-editor', + { + timeout: 10000, + }, + ); } async html(content: string, title = '') { diff --git a/packages/quill/test/e2e/utils/index.ts b/packages/quill/test/e2e/utils/index.ts index dc2eb0f3b0..4607938651 100644 --- a/packages/quill/test/e2e/utils/index.ts +++ b/packages/quill/test/e2e/utils/index.ts @@ -2,7 +2,11 @@ export const isMac = process.platform === 'darwin'; export const SHORTKEY = isMac ? 'Meta' : 'Control'; export function getSelectionInTextNode() { - const selection = document.getSelection(); + const doc = + (document.activeElement?.tagName === 'IFRAME' + ? (document.activeElement as HTMLIFrameElement).contentDocument + : document) ?? document; + const selection = doc.getSelection(); if (!selection) { throw new Error('Selection is null'); } diff --git a/packages/quill/test/unit/core/quill.spec.ts b/packages/quill/test/unit/core/quill.spec.ts index c128c0dc12..9fc03c3296 100644 --- a/packages/quill/test/unit/core/quill.spec.ts +++ b/packages/quill/test/unit/core/quill.spec.ts @@ -1377,4 +1377,25 @@ describe('Quill', () => { ).toEqual(0); }); }); + + describe('iframe', () => { + const createDocument = (): Document => { + const iframe = document.createElement('iframe'); + document.body.appendChild(iframe); + return iframe.contentDocument!; + }; + const createContainer = (html: string | { html: string } = '') => { + const doc = createDocument(); + const container = doc!.createElement('div'); + container.innerHTML = normalizeHTML(html); + doc!.body.appendChild(container); + return container; + }; + + test('initialize empty', () => { + const quill = new Quill(createContainer('0123')); + expect(quill.getContents()).toEqual(new Delta().insert('0123\n')); + expect(quill.root.innerHTML).toMatchInlineSnapshot('"0123
"'); + }); + }); });