diff --git a/addons/web_editor/static/src/js/editor/odoo-editor/test/spec/copyPaste.test.js b/addons/web_editor/static/src/js/editor/odoo-editor/test/spec/copyPaste.test.js index 5b5c7aae742..74ddaf0bd30 100644 --- a/addons/web_editor/static/src/js/editor/odoo-editor/test/spec/copyPaste.test.js +++ b/addons/web_editor/static/src/js/editor/odoo-editor/test/spec/copyPaste.test.js @@ -3,6 +3,9 @@ import { testEditor, triggerEvent, undo, + setTestSelection, + Direction, + nextTick, } from "../utils.js"; import {CLIPBOARD_WHITELISTS} from "../../src/OdooEditor.js"; @@ -1424,6 +1427,61 @@ describe('Paste', () => { contentAfter: '

abhttps://www.xyz.xdc []cd

', }); }); + it('should paste and transform URL among text', async () => { + const url = 'https://www.odoo.com'; + const imgUrl = 'https://download.odoocdn.com/icons/website/static/description/icon.png'; + const videoUrl = 'https://www.youtube.com/watch?v=dQw4w9WgXcQ'; + await testEditor(BasicEditor, { + contentBefore: '

[]

', + stepFunction: async editor => { + await pasteText(editor, `abc ${url} def`); + // Powerbox should not open + window.chai.expect(editor.powerbox.isOpen).to.be.false; + }, + contentAfter: `

abc ${url} def[]

`, + }); + await testEditor(BasicEditor, { + contentBefore: '

[]

', + stepFunction: async editor => { + await pasteText(editor, `abc ${imgUrl} def`); + // Powerbox should not open + window.chai.expect(editor.powerbox.isOpen).to.be.false; + }, + contentAfter: `

abc ${imgUrl} def[]

`, + }); + await testEditor(BasicEditor, { + contentBefore: '

[]

', + stepFunction: async editor => { + await pasteText(editor, `abc ${videoUrl} def`); + // Powerbox should not open + window.chai.expect(editor.powerbox.isOpen).to.be.false; + }, + contentAfter: `

abc ${videoUrl} def[]

`, + }); + }); + it('should paste and transform multiple URLs', async () => { + const url = 'https://www.odoo.com'; + const imgUrl = 'https://download.odoocdn.com/icons/website/static/description/icon.png'; + const videoUrl = 'https://www.youtube.com/watch?v=dQw4w9WgXcQ'; + await testEditor(BasicEditor, { + contentBefore: '

[]

', + stepFunction: async editor => { + await pasteText(editor, `${url} ${videoUrl} ${imgUrl}`); + // Powerbox should not open + window.chai.expect(editor.powerbox.isOpen).to.be.false; + }, + contentAfter: `

${url} ${videoUrl} ${imgUrl}[]

`, + }); + await testEditor(BasicEditor, { + contentBefore: '

[]

', + stepFunction: async editor => { + await pasteText(editor, `${url} abc ${videoUrl} def ${imgUrl}`); + // Powerbox should not open + window.chai.expect(editor.powerbox.isOpen).to.be.false; + }, + contentAfter: `

${url} abc ${videoUrl} def ${imgUrl}[]

`, + }); + }); }); describe('range not collapsed', async () => { it('should paste and transform an URL in a p', async () => { @@ -1453,6 +1511,84 @@ describe('Paste', () => { contentAfter: '

abhttp://www.xyz.com[]cd

', }); }); + it('should restore selection when pasting plain text followed by UNDO', async () => { + await testEditor(BasicEditor, { + contentBefore: '

[abc]

', + stepFunction: async editor => { + await pasteText(editor, 'def'); + editor.historyUndo(); + }, + contentAfter: '

[abc]

', + }); + await testEditor(BasicEditor, { + contentBefore: '

[abc]

', + stepFunction: async editor => { + await pasteText(editor, 'www.odoo.com'); + editor.historyUndo(); + }, + contentAfter: '

[abc]

', + }); + await testEditor(BasicEditor, { + contentBefore: '

[abc]

', + stepFunction: async editor => { + await pasteText(editor, 'def www.odoo.com xyz'); + editor.historyUndo(); + }, + contentAfter: '

[abc]

', + }); + + }); + it('should restore selection after pasting HTML followed by UNDO', async () => { + await testEditor(BasicEditor, { + contentBefore: '

[abc]

', + stepFunction: async editor => { + await pasteHtml(editor, 'odoo.com
google.com'); + editor.historyUndo(); + }, + contentAfter: '

[abc]

', + }); + }); + it('should paste and transform URLs among text or multiple URLs', async () => { + const url = 'https://www.odoo.com'; + const imgUrl = 'https://download.odoocdn.com/icons/website/static/description/icon.png'; + const videoUrl = 'https://www.youtube.com/watch?v=dQw4w9WgXcQ'; + await testEditor(BasicEditor, { + contentBefore: '

[xyz]

', + stepFunction: async editor => { + await pasteText(editor, `abc ${url} def`); + // Powerbox should not open + window.chai.expect(editor.powerbox.isOpen).to.be.false; + }, + contentAfter: `

abc ${url} def[]

`, + }); + await testEditor(BasicEditor, { + contentBefore: '

[xyz]

', + stepFunction: async editor => { + await pasteText(editor, `abc ${imgUrl} def`); + // Powerbox should not open + window.chai.expect(editor.powerbox.isOpen).to.be.false; + }, + contentAfter: `

abc ${imgUrl} def[]

`, + }); + await testEditor(BasicEditor, { + contentBefore: '

[xyz]

', + stepFunction: async editor => { + await pasteText(editor, `abc ${videoUrl} def`); + // Powerbox should not open + window.chai.expect(editor.powerbox.isOpen).to.be.false; + }, + contentAfter: `

abc ${videoUrl} def[]

`, + }); + await testEditor(BasicEditor, { + contentBefore: '

[xyz]

', + stepFunction: async editor => { + await pasteText(editor, `${url} ${videoUrl} ${imgUrl}`); + // Powerbox should not open + window.chai.expect(editor.powerbox.isOpen).to.be.false; + }, + contentAfter: `

${url} ${videoUrl} ${imgUrl}[]

`, + }); + }); }); }); describe('images', () => { @@ -1488,15 +1624,45 @@ describe('Paste', () => { contentBefore: '

ab[]cd

', stepFunction: async editor => { await pasteText(editor, 'https://download.odoocdn.com/icons/website/static/description/icon.png'); - // Ensure the powerbox is active - window.chai.expect(editor.powerbox.isOpen).to.be.true; - // Force powerbox validation on the default first choice - await editor.powerbox._pickCommand(); + // Powerbox should not open + window.chai.expect(editor.powerbox.isOpen).to.be.false; }, contentAfter: '

ab[]cd

', }); }); - }); + it('should paste an image URL as a link in a p', async () => { + const url = 'https://download.odoocdn.com/icons/website/static/description/icon.png'; + await testEditor(BasicEditor, { + contentBefore: '

[]

', + stepFunction: async editor => { + await pasteText(editor, url); + // Ensure the powerbox is active + window.chai.expect(editor.powerbox.isOpen).to.be.true; + // Pick the second command (Paste as URL) + triggerEvent(editor.editable, 'keydown', { key: 'ArrowDown' }); + triggerEvent(editor.editable, 'keydown', { key: 'Enter' }); + }, + contentAfter: `

${url}[]

`, + }); + }); + it('should not revert a history step when pasting an image URL as a link', async () => { + const url = 'https://download.odoocdn.com/icons/website/static/description/icon.png'; + await testEditor(BasicEditor, { + contentBefore: '

[]

', + stepFunction: async editor => { + // paste text to have a history step recorded + await pasteText(editor, "*should not disappear*"); + await pasteText(editor, url); + // Ensure the powerbox is active + window.chai.expect(editor.powerbox.isOpen).to.be.true; + // Pick the second command (Paste as URL) + triggerEvent(editor.editable, 'keydown', { key: 'ArrowDown' }); + triggerEvent(editor.editable, 'keydown', { key: 'Enter' }); + }, + contentAfter: `

*should not disappear*${url}[]

`, + }); + }); + }); describe('range not collapsed', async () => { it('should paste and transform an image URL in a p', async () => { await testEditor(BasicEditor, { @@ -1529,14 +1695,88 @@ describe('Paste', () => { contentBefore: '

ab[qsdqsd]cd

', stepFunction: async editor => { await pasteText(editor, 'https://download.odoocdn.com/icons/website/static/description/icon.png'); - // Ensure the powerbox is active - window.chai.expect(editor.powerbox.isOpen).to.be.true; - // Force powerbox validation on the default first choice - await editor.powerbox._pickCommand(); + // Powerbox should not open + window.chai.expect(editor.powerbox.isOpen).to.be.false; }, contentAfter: '

ab[]cd

', }); }); + it('should paste an image URL as a link in a p', async () => { + const url = 'https://download.odoocdn.com/icons/website/static/description/icon.png'; + await testEditor(BasicEditor, { + contentBefore: '

ab[xxx]cd

', + stepFunction: async editor => { + await pasteText(editor, url); + // Ensure the powerbox is active + window.chai.expect(editor.powerbox.isOpen).to.be.true; + // Pick the second command (Paste as URL) + triggerEvent(editor.editable, 'keydown', { key: 'ArrowDown' }); + triggerEvent(editor.editable, 'keydown', { key: 'Enter' }); + }, + contentAfter: `

ab${url}[]cd

`, + }); + }); + it('should not revert a history step when pasting an image URL as a link', async () => { + const url = 'https://download.odoocdn.com/icons/website/static/description/icon.png'; + await testEditor(BasicEditor, { + contentBefore: '

[]

', + stepFunction: async editor => { + // paste text (to have a history step recorded) + await pasteText(editor, "abxxxcd"); + // select xxx in "

ab[xxx]cd

"" + const p = editor.editable.querySelector('p') + let selection = { + direction: Direction.FORWARD, + anchorNode: p.childNodes[1], + anchorOffset: 2, + focusNode: p.childNodes[1], + focusOffset: 5, + } + setTestSelection(selection, editor.document); + editor._computeHistorySelection(); + // paste url + await pasteText(editor, url); + // Ensure the powerbox is active + window.chai.expect(editor.powerbox.isOpen).to.be.true; + // Pick the second command (Paste as URL) + triggerEvent(editor.editable, 'keydown', { key: 'ArrowDown' }); + triggerEvent(editor.editable, 'keydown', { key: 'Enter' }); + }, + contentAfter: `

ab${url}[]cd

`, + }); + }); + it('should restore selection after pasting image URL followed by UNDO', async () => { + const url = 'https://download.odoocdn.com/icons/website/static/description/icon.png'; + await testEditor(BasicEditor, { + contentBefore: '

[abc]

', + stepFunction: async editor => { + await pasteText(editor, url); + // Ensure the powerbox is active + window.chai.expect(editor.powerbox.isOpen).to.be.true; + // Pick first command (Embed image) + triggerEvent(editor.editable, 'keydown', { key: 'Enter' }); + // Undo + await nextTick(); + editor.historyUndo(); + }, + contentAfter: '

[abc]

', + }); + await testEditor(BasicEditor, { + contentBefore: '

[abc]

', + stepFunction: async editor => { + await pasteText(editor, url); + // Ensure the powerbox is active + window.chai.expect(editor.powerbox.isOpen).to.be.true; + // Pick second command (Paste as URL) + triggerEvent(editor.editable, 'keydown', { key: 'ArrowDown' }); + triggerEvent(editor.editable, 'keydown', { key: 'Enter' }); + // Undo + await nextTick(); + editor.historyUndo(); + }, + contentAfter: '

[abc]

', + }); + }); }); }); describe('youtube video', () => { @@ -1567,7 +1807,7 @@ describe('Paste', () => { contentAfter: '

ab[]cd

', }); }); - it('should paste and not transform a youtube URL in a existing link', async () => { + it('should paste and not transform a youtube URL in a existing link', async () => { await testEditor(BasicEditor, { contentBefore: '

ab[]cd

', stepFunction: async editor => { @@ -1578,6 +1818,38 @@ describe('Paste', () => { contentAfter: '

abhttps://youtu.be/dQw4w9WgXcQ[]cd

', }); }); + it('should paste a youtube URL as a link in a p', async () => { + const url = 'https://youtu.be/dQw4w9WgXcQ'; + await testEditor(BasicEditor, { + contentBefore: '

[]

', + stepFunction: async editor => { + await pasteText(editor, url); + // Ensure the powerbox is active + window.chai.expect(editor.powerbox.isOpen).to.be.true; + // Pick the second command (Paste as URL) + triggerEvent(editor.editable, 'keydown', { key: 'ArrowDown' }); + triggerEvent(editor.editable, 'keydown', { key: 'Enter' }); + }, + contentAfter: `

${url}[]

`, + }); + }); + it('should not revert a history step when pasting a youtube URL as a link', async () => { + const url = 'https://youtu.be/dQw4w9WgXcQ'; + await testEditor(BasicEditor, { + contentBefore: '

[]

', + stepFunction: async editor => { + // paste text to have a history step recorded + await pasteText(editor, "*should not disappear*"); + await pasteText(editor, url); + // Ensure the powerbox is active + window.chai.expect(editor.powerbox.isOpen).to.be.true; + // Pick the second command (Paste as URL) + triggerEvent(editor.editable, 'keydown', { key: 'ArrowDown' }); + triggerEvent(editor.editable, 'keydown', { key: 'Enter' }); + }, + contentAfter: `

*should not disappear*${url}[]

`, + }); + }); }); describe('range not collapsed', async () => { it('should paste and transform a youtube URL in a p', async () => { @@ -1617,6 +1889,83 @@ describe('Paste', () => { contentAfter: '

abhttps://www.youtube.com/watch?v=dQw4w9WgXcQ[]cd

', }); }); + it('should paste a youtube URL as a link in a p', async () => { + const url = 'https://www.youtube.com/watch?v=dQw4w9WgXcQ'; + await testEditor(BasicEditor, { + contentBefore: '

ab[xxx]cd

', + stepFunction: async editor => { + await pasteText(editor, url); + // Ensure the powerbox is active + window.chai.expect(editor.powerbox.isOpen).to.be.true; + // Pick the second command (Paste as URL) + triggerEvent(editor.editable, 'keydown', { key: 'ArrowDown' }); + triggerEvent(editor.editable, 'keydown', { key: 'Enter' }); + }, + contentAfter: `

ab${url}[]cd

`, + }); + }); + it('should not revert a history step when pasting a youtube URL as a link', async () => { + const url = 'https://www.youtube.com/watch?v=dQw4w9WgXcQ'; + await testEditor(BasicEditor, { + contentBefore: '

[]

', + stepFunction: async editor => { + // paste text (to have a history step recorded) + await pasteText(editor, "abxxxcd"); + // select xxx in "

ab[xxx]cd

" + const p = editor.editable.querySelector('p') + let selection = { + direction: Direction.FORWARD, + anchorNode: p.childNodes[1], + anchorOffset: 2, + focusNode: p.childNodes[1], + focusOffset: 5, + } + setTestSelection(selection, editor.document); + editor._computeHistorySelection(); + + // paste url + await pasteText(editor, url); + // Ensure the powerbox is active + window.chai.expect(editor.powerbox.isOpen).to.be.true; + // Pick the second command (Paste as URL) + triggerEvent(editor.editable, 'keydown', { key: 'ArrowDown' }); + triggerEvent(editor.editable, 'keydown', { key: 'Enter' }); + }, + contentAfter: `

ab${url}[]cd

`, + }); + }); + it('should restore selection after pasting video URL followed by UNDO', async () => { + const url = 'https://www.youtube.com/watch?v=dQw4w9WgXcQ'; + await testEditor(BasicEditor, { + contentBefore: '

[abc]

', + stepFunction: async editor => { + await pasteText(editor, url); + // Ensure the powerbox is active + window.chai.expect(editor.powerbox.isOpen).to.be.true; + // Pick first command (Embed video) + triggerEvent(editor.editable, 'keydown', { key: 'Enter' }); + // Undo + await nextTick(); + editor.historyUndo(); + }, + contentAfter: '

[abc]

', + }); + await testEditor(BasicEditor, { + contentBefore: '

[abc]

', + stepFunction: async editor => { + await pasteText(editor, url); + // Ensure the powerbox is active + window.chai.expect(editor.powerbox.isOpen).to.be.true; + // Pick second command (Paste as URL) + triggerEvent(editor.editable, 'keydown', { key: 'ArrowDown' }); + triggerEvent(editor.editable, 'keydown', { key: 'Enter' }); + // Undo + await nextTick(); + editor.historyUndo(); + }, + contentAfter: '

[abc]

', + }); + }); }); }); describe('Odoo editor own html', () => {