/** * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved. * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license */ /* global document */ import ClassicTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/classictesteditor'; import { _clear as clearTranslations, add as addTranslations } from '@ckeditor/ckeditor5-utils/src/translation-service'; import testUtils from '@ckeditor/ckeditor5-core/tests/_utils/utils'; import TableEditing from '../src/tableediting'; import TableUI from '../src/tableui'; import InsertTableView from '../src/ui/inserttableview'; import SwitchButtonView from '@ckeditor/ckeditor5-ui/src/button/switchbuttonview'; import DropdownView from '@ckeditor/ckeditor5-ui/src/dropdown/dropdownview'; import ListSeparatorView from '@ckeditor/ckeditor5-ui/src/list/listseparatorview'; import SplitButtonView from '@ckeditor/ckeditor5-ui/src/dropdown/button/splitbuttonview'; describe( 'TableUI', () => { let editor, element; testUtils.createSinonSandbox(); before( () => { addTranslations( 'en', {} ); addTranslations( 'pl', {} ); } ); after( () => { clearTranslations(); } ); beforeEach( () => { element = document.createElement( 'div' ); document.body.appendChild( element ); return ClassicTestEditor .create( element, { plugins: [ TableEditing, TableUI ] } ) .then( newEditor => { editor = newEditor; } ); } ); afterEach( () => { element.remove(); return editor.destroy(); } ); describe( 'insertTable dropdown', () => { let insertTable; beforeEach( () => { insertTable = editor.ui.componentFactory.create( 'insertTable' ); insertTable.isOpen = true; // Dropdown is lazy loaded, so make sure its open (#6193). } ); it( 'should register insertTable button', () => { expect( insertTable ).to.be.instanceOf( DropdownView ); expect( insertTable.buttonView.label ).to.equal( 'Insert table' ); expect( insertTable.buttonView.icon ).to.match( / { const command = editor.commands.get( 'insertTable' ); command.isEnabled = true; expect( insertTable.buttonView.isOn ).to.be.true; expect( insertTable.buttonView.isEnabled ).to.be.true; command.isEnabled = false; expect( insertTable.buttonView.isEnabled ).to.be.false; } ); it( 'should execute insertTable command on button execute event', () => { const executeSpy = testUtils.sinon.spy( editor, 'execute' ); const tableSizeView = insertTable.panelView.children.first; tableSizeView.rows = 2; tableSizeView.columns = 7; insertTable.fire( 'execute' ); sinon.assert.calledOnce( executeSpy ); sinon.assert.calledWithExactly( executeSpy, 'insertTable', { rows: 2, columns: 7 } ); } ); it( 'should reset rows & columns on dropdown open', () => { insertTable.isOpen = true; const tableSizeView = insertTable.panelView.children.first; expect( tableSizeView.rows ).to.equal( 0 ); expect( tableSizeView.columns ).to.equal( 0 ); tableSizeView.rows = 2; tableSizeView.columns = 2; insertTable.buttonView.fire( 'open' ); expect( tableSizeView.rows ).to.equal( 0 ); expect( tableSizeView.columns ).to.equal( 0 ); } ); it( 'is not fully initialized when not open', () => { const dropdown = editor.ui.componentFactory.create( 'insertTable' ); for ( const childView of dropdown.panelView.children ) { expect( childView ).not.to.be.instanceOf( InsertTableView ); } } ); } ); describe( 'tableRow dropdown', () => { let dropdown; beforeEach( () => { dropdown = editor.ui.componentFactory.create( 'tableRow' ); } ); it( 'have button with proper properties set', () => { expect( dropdown ).to.be.instanceOf( DropdownView ); const button = dropdown.buttonView; expect( button.isOn ).to.be.false; expect( button.tooltip ).to.be.true; expect( button.label ).to.equal( 'Row' ); expect( button.icon ).to.match( / { const listView = dropdown.listView; const labels = listView.items.map( item => item instanceof ListSeparatorView ? '|' : item.children.first.label ); expect( labels ).to.deep.equal( [ 'Header row', '|', 'Insert row above', 'Insert row below', 'Delete row', 'Select row' ] ); } ); it( 'should bind items in panel to proper commands', () => { const items = dropdown.listView.items; const setRowHeaderCommand = editor.commands.get( 'setTableRowHeader' ); const insertRowBelowCommand = editor.commands.get( 'insertTableRowBelow' ); const insertRowAboveCommand = editor.commands.get( 'insertTableRowAbove' ); const removeRowCommand = editor.commands.get( 'removeTableRow' ); const selectRowCommand = editor.commands.get( 'selectTableRow' ); setRowHeaderCommand.isEnabled = true; insertRowBelowCommand.isEnabled = true; insertRowAboveCommand.isEnabled = true; removeRowCommand.isEnabled = true; selectRowCommand.isEnabled = true; expect( items.first.children.first.isEnabled ).to.be.true; expect( items.get( 2 ).children.first.isEnabled ).to.be.true; expect( items.get( 3 ).children.first.isEnabled ).to.be.true; expect( items.get( 4 ).children.first.isEnabled ).to.be.true; expect( items.get( 5 ).children.first.isEnabled ).to.be.true; expect( dropdown.buttonView.isEnabled ).to.be.true; setRowHeaderCommand.isEnabled = false; expect( items.first.children.first.isEnabled ).to.be.false; expect( dropdown.buttonView.isEnabled ).to.be.true; insertRowAboveCommand.isEnabled = false; expect( items.get( 2 ).children.first.isEnabled ).to.be.false; expect( dropdown.buttonView.isEnabled ).to.be.true; insertRowBelowCommand.isEnabled = false; expect( items.get( 3 ).children.first.isEnabled ).to.be.false; expect( dropdown.buttonView.isEnabled ).to.be.true; removeRowCommand.isEnabled = false; expect( items.get( 4 ).children.first.isEnabled ).to.be.false; expect( dropdown.buttonView.isEnabled ).to.be.true; selectRowCommand.isEnabled = false; expect( items.get( 5 ).children.first.isEnabled ).to.be.false; expect( dropdown.buttonView.isEnabled ).to.be.false; } ); it( 'should focus view after command execution', () => { const focusSpy = testUtils.sinon.spy( editor.editing.view, 'focus' ); dropdown.listView.items.first.children.first.fire( 'execute' ); sinon.assert.calledOnce( focusSpy ); } ); it( 'executes command when it\'s executed', () => { const spy = sinon.stub( editor, 'execute' ); dropdown.listView.items.first.children.first.fire( 'execute' ); expect( spy.calledOnce ).to.be.true; expect( spy.args[ 0 ][ 0 ] ).to.equal( 'setTableRowHeader' ); } ); it( 'should use a toggle switch for the setTableRowHeader item', () => { const items = dropdown.listView.items; expect( items.first.children.first ).to.be.instanceOf( SwitchButtonView ); } ); it( 'should bind set header row command value to dropdown item', () => { const items = dropdown.listView.items; const setRowHeaderCommand = editor.commands.get( 'setTableRowHeader' ); setRowHeaderCommand.value = false; expect( items.first.children.first.isOn ).to.be.false; setRowHeaderCommand.value = true; expect( items.first.children.first.isOn ).to.be.true; } ); } ); describe( 'tableColumn dropdown', () => { let dropdown; beforeEach( () => { dropdown = editor.ui.componentFactory.create( 'tableColumn' ); } ); it( 'have button with proper properties set', () => { expect( dropdown ).to.be.instanceOf( DropdownView ); const button = dropdown.buttonView; expect( button.isOn ).to.be.false; expect( button.tooltip ).to.be.true; expect( button.label ).to.equal( 'Column' ); expect( button.icon ).to.match( / { const listView = dropdown.listView; const labels = listView.items.map( item => item instanceof ListSeparatorView ? '|' : item.children.first.label ); expect( labels ).to.deep.equal( [ 'Header column', '|', 'Insert column left', 'Insert column right', 'Delete column', 'Select column' ] ); } ); it( 'should bind items in panel to proper commands (LTR content)', () => { const items = dropdown.listView.items; const setColumnHeaderCommand = editor.commands.get( 'setTableColumnHeader' ); const insertColumnLeftCommand = editor.commands.get( 'insertTableColumnLeft' ); const insertColumnRightCommand = editor.commands.get( 'insertTableColumnRight' ); const removeColumnCommand = editor.commands.get( 'removeTableColumn' ); const selectColumnCommand = editor.commands.get( 'selectTableColumn' ); setColumnHeaderCommand.isEnabled = true; insertColumnLeftCommand.isEnabled = true; insertColumnRightCommand.isEnabled = true; removeColumnCommand.isEnabled = true; selectColumnCommand.isEnabled = true; expect( items.first.children.first.isEnabled ).to.be.true; expect( items.get( 2 ).children.first.isEnabled ).to.be.true; expect( items.get( 3 ).children.first.isEnabled ).to.be.true; expect( items.get( 4 ).children.first.isEnabled ).to.be.true; expect( items.get( 5 ).children.first.isEnabled ).to.be.true; expect( dropdown.buttonView.isEnabled ).to.be.true; setColumnHeaderCommand.isEnabled = false; expect( items.first.children.first.isEnabled ).to.be.false; expect( dropdown.buttonView.isEnabled ).to.be.true; insertColumnLeftCommand.isEnabled = false; expect( items.get( 2 ).children.first.isEnabled ).to.be.false; expect( dropdown.buttonView.isEnabled ).to.be.true; insertColumnRightCommand.isEnabled = false; expect( items.get( 3 ).children.first.isEnabled ).to.be.false; removeColumnCommand.isEnabled = false; expect( items.get( 4 ).children.first.isEnabled ).to.be.false; selectColumnCommand.isEnabled = false; expect( items.get( 5 ).children.first.isEnabled ).to.be.false; expect( dropdown.buttonView.isEnabled ).to.be.false; } ); it( 'should bind items in panel to proper commands (RTL content)', () => { const element = document.createElement( 'div' ); document.body.appendChild( element ); return ClassicTestEditor .create( element, { language: { ui: 'en', content: 'ar' }, plugins: [ TableEditing, TableUI ] } ) .then( editor => { const dropdown = editor.ui.componentFactory.create( 'tableColumn' ); const items = dropdown.listView.items; expect( items.get( 2 ).children.first.label ).to.equal( 'Insert column left' ); expect( items.get( 2 ).children.first.commandName ).to.equal( 'insertTableColumnRight' ); expect( items.get( 3 ).children.first.label ).to.equal( 'Insert column right' ); expect( items.get( 3 ).children.first.commandName ).to.equal( 'insertTableColumnLeft' ); element.remove(); return editor.destroy(); } ); } ); it( 'should focus view after command execution', () => { const focusSpy = testUtils.sinon.spy( editor.editing.view, 'focus' ); dropdown.listView.items.first.children.first.fire( 'execute' ); sinon.assert.calledOnce( focusSpy ); } ); it( 'executes command when it\'s executed', () => { const spy = sinon.stub( editor, 'execute' ); dropdown.listView.items.first.children.first.fire( 'execute' ); expect( spy.calledOnce ).to.be.true; expect( spy.args[ 0 ][ 0 ] ).to.equal( 'setTableColumnHeader' ); } ); it( 'should use a toggle switch for the setTableColumnHeader item', () => { const items = dropdown.listView.items; expect( items.first.children.first ).to.be.instanceOf( SwitchButtonView ); } ); it( 'should bind set header column command value to dropdown item', () => { const items = dropdown.listView.items; const setColumnHeaderCommand = editor.commands.get( 'setTableColumnHeader' ); setColumnHeaderCommand.value = false; expect( items.first.children.first.isOn ).to.be.false; setColumnHeaderCommand.value = true; expect( items.first.children.first.isOn ).to.be.true; } ); } ); describe( 'mergeTableCell split button', () => { let dropdown, command; beforeEach( () => { dropdown = editor.ui.componentFactory.create( 'mergeTableCells' ); command = editor.commands.get( 'mergeTableCells' ); } ); it( 'have button with proper properties set', () => { expect( dropdown ).to.be.instanceOf( DropdownView ); const button = dropdown.buttonView; expect( button.isOn ).to.be.false; expect( button.tooltip ).to.be.true; expect( button.label ).to.equal( 'Merge cells' ); expect( button.icon ).to.match( / { expect( dropdown.buttonView ).to.be.instanceOf( SplitButtonView ); } ); it( 'should have #isEnabled always true regardless of the "mergeTableCells" command state', () => { command.isEnabled = false; expect( dropdown.isEnabled ).to.be.true; command.isEnabled = true; expect( dropdown.isEnabled ).to.be.true; } ); it( 'should execute the "mergeTableCells" command when the main part of the split button is clicked', () => { const spy = sinon.stub( editor, 'execute' ); dropdown.buttonView.fire( 'execute' ); sinon.assert.calledOnce( spy ); sinon.assert.calledWithExactly( spy, 'mergeTableCells' ); } ); it( 'should have the dropdown part of the split button always enabled no matter the "mergeTableCells" command state', () => { command.isEnabled = true; expect( dropdown.buttonView.arrowView.isEnabled ).to.be.true; command.isEnabled = false; expect( dropdown.buttonView.arrowView.isEnabled ).to.be.true; } ); it( 'should have proper items in panel', () => { const listView = dropdown.listView; const labels = listView.items.map( item => item instanceof ListSeparatorView ? '|' : item.children.first.label ); expect( labels ).to.deep.equal( [ 'Merge cell up', 'Merge cell right', 'Merge cell down', 'Merge cell left', '|', 'Split cell vertically', 'Split cell horizontally' ] ); } ); it( 'should bind items in panel to proper commands (LTR content)', () => { const items = dropdown.listView.items; const mergeCellUpCommand = editor.commands.get( 'mergeTableCellUp' ); const mergeCellRightCommand = editor.commands.get( 'mergeTableCellRight' ); const mergeCellDownCommand = editor.commands.get( 'mergeTableCellDown' ); const mergeCellLeftCommand = editor.commands.get( 'mergeTableCellLeft' ); const splitCellVerticallyCommand = editor.commands.get( 'splitTableCellVertically' ); const splitCellHorizontallyCommand = editor.commands.get( 'splitTableCellHorizontally' ); mergeCellUpCommand.isEnabled = true; mergeCellRightCommand.isEnabled = true; mergeCellDownCommand.isEnabled = true; mergeCellLeftCommand.isEnabled = true; splitCellVerticallyCommand.isEnabled = true; splitCellHorizontallyCommand.isEnabled = true; const mergeCellUpButton = items.first; const mergeCellRightButton = items.get( 1 ); const mergeCellDownButton = items.get( 2 ); const mergeCellLeftButton = items.get( 3 ); // separator const splitVerticallyButton = items.get( 5 ); const splitHorizontallyButton = items.get( 6 ); expect( mergeCellUpButton.children.first.isEnabled ).to.be.true; expect( mergeCellRightButton.children.first.isEnabled ).to.be.true; expect( mergeCellDownButton.children.first.isEnabled ).to.be.true; expect( mergeCellLeftButton.children.first.isEnabled ).to.be.true; expect( splitVerticallyButton.children.first.isEnabled ).to.be.true; expect( splitHorizontallyButton.children.first.isEnabled ).to.be.true; mergeCellUpCommand.isEnabled = false; expect( mergeCellUpButton.children.first.isEnabled ).to.be.false; mergeCellRightCommand.isEnabled = false; expect( mergeCellRightButton.children.first.isEnabled ).to.be.false; mergeCellDownCommand.isEnabled = false; expect( mergeCellDownButton.children.first.isEnabled ).to.be.false; mergeCellLeftCommand.isEnabled = false; expect( mergeCellLeftButton.children.first.isEnabled ).to.be.false; splitCellVerticallyCommand.isEnabled = false; expect( splitVerticallyButton.children.first.isEnabled ).to.be.false; splitCellHorizontallyCommand.isEnabled = false; expect( splitHorizontallyButton.children.first.isEnabled ).to.be.false; } ); it( 'should bind items in panel to proper commands (RTL content)', () => { const element = document.createElement( 'div' ); document.body.appendChild( element ); return ClassicTestEditor .create( element, { language: { ui: 'en', content: 'ar' }, plugins: [ TableEditing, TableUI ] } ) .then( editor => { const dropdown = editor.ui.componentFactory.create( 'mergeTableCells' ); const items = dropdown.listView.items; expect( items.get( 1 ).children.first.label ).to.equal( 'Merge cell right' ); expect( items.get( 1 ).children.first.commandName ).to.equal( 'mergeTableCellLeft' ); expect( items.get( 3 ).children.first.label ).to.equal( 'Merge cell left' ); expect( items.get( 3 ).children.first.commandName ).to.equal( 'mergeTableCellRight' ); element.remove(); return editor.destroy(); } ); } ); it( 'should focus view after command execution', () => { const focusSpy = testUtils.sinon.spy( editor.editing.view, 'focus' ); dropdown.listView.items.first.children.first.fire( 'execute' ); sinon.assert.calledOnce( focusSpy ); } ); it( 'executes command when it\'s executed', () => { const spy = sinon.stub( editor, 'execute' ); dropdown.listView.items.first.children.first.fire( 'execute' ); expect( spy.calledOnce ).to.be.true; expect( spy.args[ 0 ][ 0 ] ).to.equal( 'mergeTableCellUp' ); } ); } ); } );