diff --git a/addons/web_editor/static/src/js/editor/odoo-editor/test/editor-test.js b/addons/web_editor/static/src/js/editor/odoo-editor/test/editor-test.js index b081311b8e2..a708c96a551 100644 --- a/addons/web_editor/static/src/js/editor/odoo-editor/test/editor-test.js +++ b/addons/web_editor/static/src/js/editor/odoo-editor/test/editor-test.js @@ -12,6 +12,7 @@ import './spec/fontAwesome.test.js'; import './spec/autostep.test.js'; import './spec/urlRegex.test.js'; import './spec/collab.test.js'; +import './spec/powerbox.test.js'; /* global mocha */ mocha.run(failures => { diff --git a/addons/web_editor/static/src/js/editor/odoo-editor/test/spec/powerbox.test.js b/addons/web_editor/static/src/js/editor/odoo-editor/test/spec/powerbox.test.js new file mode 100644 index 00000000000..a0d79ae9b81 --- /dev/null +++ b/addons/web_editor/static/src/js/editor/odoo-editor/test/spec/powerbox.test.js @@ -0,0 +1,284 @@ +import { setSelection } from '../../src/OdooEditor.js'; +import { Powerbox } from '../../src/powerbox/Powerbox.js'; +import { BasicEditor, insertText, testEditor, triggerEvent } from '../utils.js'; + +const getCurrentCommandNames = powerbox => { + return [...powerbox.el.querySelectorAll('.oe-powerbox-commandName')].map(c => c.innerText); +} + +describe('Powerbox', () => { + describe('integration', () => { + it('should open the Powerbox on type `/`', async () => { + await testEditor(BasicEditor, { + contentBefore: '

ab[]

', + stepFunction: async editor => { + window.chai.expect(editor.powerbox.isOpen).to.eql(false); + window.chai.expect(editor.powerbox.el.style.display).to.eql('none'); + await insertText(editor, '/'); + window.chai.expect(editor.powerbox.isOpen).to.eql(true); + window.chai.expect(editor.powerbox.el.style.display).not.to.eql('none'); + }, + }); + }); + it('should filter the Powerbox contents with term', async () => { + await testEditor(BasicEditor, { + contentBefore: '

ab[]

', + stepFunction: async editor => { + await insertText(editor, '/'); + await insertText(editor, 'head'); + triggerEvent(editor.editable, 'keyup'); + window.chai.expect(getCurrentCommandNames(editor.powerbox)).to.eql(['Heading 1', 'Heading 2', 'Heading 3']); + }, + }); + }); + it('should execute command and remove term and hot character on Enter', async () => { + await testEditor(BasicEditor, { + contentBefore: '

ab[]

', + stepFunction: async editor => { + editor.powerbox.el.classList.add('yo'); + await insertText(editor, '/'); + await insertText(editor, 'head'); + triggerEvent(editor.editable, 'keyup'); + triggerEvent(editor.editable, 'keydown', { key: 'Enter' }); + }, + contentAfter: '

ab[]

', + }); + }); + }); + describe('class', () => { + it('should properly order default commands and categories', async () => { + const editable = document.createElement('div'); + document.body.append(editable); + const powerbox = new Powerbox({ + categories: [ + {name: 'a', priority: 2}, + {name: 'b', priority: 4}, + {name: 'c', priority: 1}, // redefined lower -> ignore + {name: 'd', priority: 4}, // same as b -> alphabetical + {name: 'c', priority: 3}, + ], + commands: [ + {category: 'f', name: 'f1'}, // category doesn't exist -> end + {category: 'e', name: 'e1'}, // category doesn't exist -> end + {category: 'a', name: 'a1', priority: 1}, + {category: 'a', name: 'a4', priority: 3}, + {category: 'a', name: 'a2', priority: 3}, + {category: 'a', name: 'a3', priority: 2}, + {category: 'b', name: 'b3'}, // no priority -> priority 1 + {category: 'b', name: 'b1'}, + {category: 'b', name: 'b2'}, + {category: 'c', name: 'c1'}, + {category: 'd', name: 'd1'}, + ], + editable, + }); + powerbox.open(); + window.chai.expect(getCurrentCommandNames(powerbox)).to.eql( + ['b1', 'b2', 'b3', 'd1', 'c1', 'a2', 'a4', 'a3', 'a1', 'e1', 'f1'] + ); + powerbox.destroy(); + editable.remove(); + }); + it('should navigate through commands with arrow keys', async () => { + const editable = document.createElement('div'); + document.body.append(editable); + const powerbox = new Powerbox({ + categories: [], + commands: [ + {category: 'a', name: '2'}, + {category: 'a', name: '3'}, + {category: 'a', name: '1'}, + ], + editable, + }); + powerbox.open(); + window.chai.expect(powerbox._context.selectedCommand.name).to.eql('1'); + triggerEvent(editable, 'keydown', { key: 'ArrowDown'}); + window.chai.expect(powerbox._context.selectedCommand.name).to.eql('2'); + triggerEvent(editable, 'keydown', { key: 'ArrowDown'}); + window.chai.expect(powerbox._context.selectedCommand.name).to.eql('3'); + triggerEvent(editable, 'keydown', { key: 'ArrowUp'}); + window.chai.expect(powerbox._context.selectedCommand.name).to.eql('2'); + triggerEvent(editable, 'keydown', { key: 'ArrowUp'}); + window.chai.expect(powerbox._context.selectedCommand.name).to.eql('1'); + powerbox.destroy(); + editable.remove(); + }); + it('should execute command on press Enter', async () => { + const editable = document.createElement('div'); + document.body.append(editable); + const powerbox = new Powerbox({ + categories: [], + commands: [ + {category: 'a', name: '2', callback: () => editable.innerText = '2'}, + {category: 'a', name: '3', callback: () => editable.innerText = '3'}, + {category: 'a', name: '1', callback: () => editable.innerText = '1'}, + ], + editable, + }); + powerbox.open(); + window.chai.expect(editable.innerText).to.eql(''); + triggerEvent(editable, 'keydown', { key: 'Enter'}); + window.chai.expect(editable.innerText).to.eql('1'); + triggerEvent(editable, 'keydown', { key: 'ArrowDown'}); + triggerEvent(editable, 'keydown', { key: 'Enter'}); + window.chai.expect(editable.innerText).to.eql('2'); + powerbox.destroy(); + editable.remove(); + }); + it('should filter commands with `commandFilters`', async () => { + const editable = document.createElement('div'); + document.body.append(editable); + const powerbox = new Powerbox({ + categories: [], + commands: [ + {category: 'a', name: 'a2'}, + {category: 'a', name: 'a3'}, + {category: 'a', name: 'a1'}, + {category: 'b', name: 'b2x'}, + {category: 'b', name: 'b3x'}, + {category: 'b', name: 'b1y'}, + ], + editable, + commandFilters: [commands => commands.filter(command => command.category === 'b')], + }); + powerbox.open(); + window.chai.expect(getCurrentCommandNames(powerbox)).to.eql(['b1y', 'b2x', 'b3x']); + powerbox.close(); + powerbox.commandFilters.push(commands => commands.filter(command => command.name.includes('x'))) + powerbox.open(); + window.chai.expect(getCurrentCommandNames(powerbox)).to.eql(['b2x', 'b3x']); + powerbox.destroy(); + editable.remove(); + }); + it('should filter commands with `isDisabled`', async () => { + const editable = document.createElement('div'); + document.body.append(editable); + let disableCommands = false; + const powerbox = new Powerbox({ + categories: [], + commands: [ + {category: 'a', name: 'a2', isDisabled: () => disableCommands}, + {category: 'a', name: 'a3', isDisabled: () => disableCommands}, + {category: 'a', name: 'a1', isDisabled: () => disableCommands}, + {category: 'b', name: 'b2x'}, + {category: 'b', name: 'b3x'}, + {category: 'b', name: 'b1y'}, + ], + editable, + }); + powerbox.open(); + window.chai.expect(getCurrentCommandNames(powerbox)).to.eql(['a1', 'a2', 'a3', 'b1y', 'b2x', 'b3x']); + powerbox.close(); + disableCommands = true; + powerbox.open(); + window.chai.expect(getCurrentCommandNames(powerbox)).to.eql(['b1y', 'b2x', 'b3x']); + powerbox.close(); + disableCommands = false; + powerbox.open(); + window.chai.expect(getCurrentCommandNames(powerbox)).to.eql(['a1', 'a2', 'a3', 'b1y', 'b2x', 'b3x']); + powerbox.destroy(); + editable.remove(); + }); + it('should filter commands with filter text', async () => { + const editable = document.createElement('div'); + document.body.append(editable); + editable.append(document.createTextNode('original text')); + setSelection(editable.firstChild, 13); + const powerbox = new Powerbox({ + categories: [], + commands: [ + {category: 'a', name: 'a2'}, + {category: 'a', name: 'a3'}, + {category: 'a', name: 'a1'}, + {category: 'b', name: 'b2x'}, + {category: 'b', name: 'b3x'}, + {category: 'b', name: 'b1y'}, + ], + editable, + }); + powerbox.open(); + window.chai.expect(powerbox._context.initialValue).to.eql('original text'); + window.chai.expect(getCurrentCommandNames(powerbox)).to.eql(['a1', 'a2', 'a3', 'b1y', 'b2x', 'b3x']); + // filter: '1' + editable.append(document.createTextNode('1')); + triggerEvent(editable, 'keyup'); + window.chai.expect(getCurrentCommandNames(powerbox)).to.eql(['a1', 'b1y']); + // filter: '' + editable.lastChild.remove(); + triggerEvent(editable, 'keyup'); + window.chai.expect(getCurrentCommandNames(powerbox)).to.eql(['a1', 'a2', 'a3', 'b1y', 'b2x', 'b3x']); + // filter: 'a' + editable.append(document.createTextNode('a')); + triggerEvent(editable, 'keyup'); // filter: 'a'. + window.chai.expect(getCurrentCommandNames(powerbox)).to.eql(['a1', 'a2', 'a3']); + editable.append(document.createTextNode('1')); + triggerEvent(editable, 'keyup'); // filter: 'a1'. + window.chai.expect(getCurrentCommandNames(powerbox)).to.eql(['a1']); + powerbox.destroy(); + editable.remove(); + }); + it('should close the Powerbox on remove last filter text with Backspace', async () => { + const editable = document.createElement('div'); + document.body.append(editable); + editable.append(document.createTextNode('1')); + setSelection(editable.firstChild, 13); + const powerbox = new Powerbox({ + categories: [], + commands: [ + {category: 'a', name: 'a2'}, + {category: 'a', name: 'a3'}, + {category: 'a', name: 'a1'}, + {category: 'b', name: 'b2x'}, + {category: 'b', name: 'b3x'}, + {category: 'b', name: 'b1y'}, + ], + editable, + }); + powerbox.open(); + // Text: '1' + window.chai.expect(powerbox._context.initialValue).to.eql('1'); + window.chai.expect(getCurrentCommandNames(powerbox)).to.eql(['a1', 'a2', 'a3', 'b1y', 'b2x', 'b3x']); + // Text: '1y' -> filter: 'y' + editable.append(document.createTextNode('x')); + triggerEvent(editable, 'keyup'); + window.chai.expect(getCurrentCommandNames(powerbox)).to.eql(['b2x', 'b3x']); + // Text: '1' + editable.lastChild.remove(); + triggerEvent(editable, 'keydown', { key: 'Backspace' }); + triggerEvent(editable, 'keyup'); + window.chai.expect(getCurrentCommandNames(powerbox)).to.eql(['a1', 'a2', 'a3', 'b1y', 'b2x', 'b3x']); + window.chai.expect(powerbox.isOpen).to.eql(true); + window.chai.expect(powerbox.el.style.display).not.to.eql('none'); + // Text: '' + editable.lastChild.remove(); + triggerEvent(editable, 'keydown', { key: 'Backspace' }); + triggerEvent(editable, 'keyup'); + window.chai.expect(powerbox.isOpen).to.eql(false); + window.chai.expect(powerbox.el.style.display).to.eql('none'); + powerbox.destroy(); + editable.remove(); + }); + it('should close the Powerbox on press Escape', async () => { + const editable = document.createElement('div'); + document.body.append(editable); + const powerbox = new Powerbox({ + categories: [], + commands: [ + {category: 'a', name: '2'}, + {category: 'a', name: '3'}, + {category: 'a', name: '1'}, + ], + editable, + }); + powerbox.open(); + window.chai.expect(powerbox.isOpen).to.eql(true); + window.chai.expect(powerbox.el.style.display).not.to.eql('none'); + triggerEvent(editable, 'keydown', { key: 'Escape' }); + window.chai.expect(powerbox.isOpen).to.eql(false); + window.chai.expect(powerbox.el.style.display).to.eql('none'); + powerbox.destroy(); + editable.remove(); + }); + }); +});