UNPKG

@quantlab/handsontable

Version:

Spreadsheet-like data grid editor that provides copy/paste functionality compatible with Excel/Google Docs

719 lines (541 loc) 22.5 kB
describe('CopyPaste', () => { const id = 'testContainer'; beforeEach(function() { this.$container = $(`<div id="${id}"></div>`).appendTo('body'); }); afterEach(function() { if (this.$container) { destroy(); this.$container.remove(); } }); const arrayOfArrays = function() { return [ ['', 'Kia', 'Nissan', 'Toyota', 'Honda'], ['2008', 10, 11, 12, 13], ['2009', 20, 11, 14, 13], ['2010', 30, 15, 12, 13] ]; }; describe('enabling/disabing plugin', () => { it('should copyPaste be set enabled as default', () => { const hot = handsontable(); expect(hot.getSettings().copyPaste).toBeTruthy(); expect(hot.getPlugin('CopyPaste').textarea).toBeDefined(); }); it('should do not create textarea element if copyPaste is disabled on initialization', () => { handsontable({ copyPaste: false }); expect($('#HandsontableCopyPaste').length).toEqual(0); }); }); describe('working with multiple tables', () => { beforeEach(function() { this.$container2 = $(`<div id="${id}2"></div>`).appendTo('body'); }); afterEach(function() { if (this.$container2) { this.$container2.handsontable('destroy'); this.$container2.remove(); } }); it('should disable copyPaste only in particular table', function() { const hot1 = handsontable(); const hot2 = spec().$container2.handsontable({ copyPaste: false }).handsontable('getInstance'); expect(hot1.getPlugin('CopyPaste').textarea).toBeDefined(); expect(hot2.getPlugin('CopyPaste').textarea).toBeUndefined(); }); it('should create only one HandsontableCopyPaste regardless of the number of tables', function() { handsontable(); spec().$container2.handsontable(); expect($('#HandsontableCopyPaste').length).toEqual(1); }); it('should leave HandsontableCopyPaste as long as at least one table has copyPaste enabled', function() { const hot1 = handsontable(); const hot2 = spec().$container2.handsontable().handsontable('getInstance'); expect($('#HandsontableCopyPaste').length).toEqual(1); hot1.updateSettings({ copyPaste: false }); expect($('#HandsontableCopyPaste').length).toEqual(1); hot2.updateSettings({ copyPaste: false }); expect($('#HandsontableCopyPaste').length).toEqual(0); }); }); describe('setting values copyable', () => { it('should set copyable text when selecting a single cell and hitting ctrl', () => { handsontable({ data: Handsontable.helper.createSpreadsheetData(2, 2) }); const copyPasteTextarea = $('#HandsontableCopyPaste')[0]; expect(copyPasteTextarea.value.length).toEqual(0); selectCell(0, 0); keyDownUp(Handsontable.helper.KEY_CODES.CONTROL_LEFT); expect(copyPasteTextarea.value).toEqual('A1'); }); it('should set copyable text when selecting a single cell and hitting left command', () => { handsontable({ data: Handsontable.helper.createSpreadsheetData(2, 2) }); const copyPasteTextarea = $('#HandsontableCopyPaste')[0]; expect(copyPasteTextarea.value.length).toEqual(0); selectCell(0, 0); keyDownUp(Handsontable.helper.KEY_CODES.COMMAND_LEFT); expect(copyPasteTextarea.value).toEqual('A1'); }); it('should set copyable text when selecting a single cell and hitting right command', () => { handsontable({ data: Handsontable.helper.createSpreadsheetData(2, 2) }); const copyPasteTextarea = $('#HandsontableCopyPaste')[0]; expect(copyPasteTextarea.value.length).toEqual(0); selectCell(0, 0); keyDownUp(Handsontable.helper.KEY_CODES.COMMAND_RIGHT); expect(copyPasteTextarea.value).toEqual('A1'); }); it('should set copyable text when selecting multiple cells and hitting ctrl', () => { handsontable({ data: Handsontable.helper.createSpreadsheetData(2, 2) }); const copyPasteTextarea = $('#HandsontableCopyPaste')[0]; expect(copyPasteTextarea.value.length).toEqual(0); selectCell(0, 0, 1, 0); keyDownUp(Handsontable.helper.KEY_CODES.CONTROL_LEFT); expect(copyPasteTextarea.value).toEqual('A1\nA2'); }); it('should set copyable text when selecting all cells with CTRL+A', (done) => { handsontable({ data: Handsontable.helper.createSpreadsheetData(2, 2) }); const copyPasteTextarea = $('#HandsontableCopyPaste')[0]; expect(copyPasteTextarea.value.length).toEqual(0); selectCell(0, 0); $(document.activeElement).simulate('keydown', {keyCode: Handsontable.helper.KEY_CODES.A, ctrlKey: true}); setTimeout(() => { expect(getSelected()).toEqual([0, 0, 1, 1]); expect(copyPasteTextarea.value).toEqual('A1\tB1\nA2\tB2'); done(); }, 10); }); it('should not throw error when no cell is selected (#1221)', () => { handsontable({ data: Handsontable.helper.createSpreadsheetData(2, 2) }); selectCell(0, 0); deselectCell(); function keydownCtrl() { $(document).simulate('keydown', { keyCode: Handsontable.helper.KEY_CODES.COMMAND_LEFT }); } // expect no to throw any exception expect(keydownCtrl).not.toThrow(); }); it('should set copyable text when selecting a single cell with specified type and hitting ctrl (#1300)', () => { handsontable({ data: [['A', 1], ['B', 2]], columns: [ { type: 'text' }, { type: 'numeric' } ] }); const copyPasteTextarea = $('#HandsontableCopyPaste')[0]; expect(copyPasteTextarea.value.length).toEqual(0); selectCell(0, 0, 1, 1); keyDownUp(Handsontable.helper.KEY_CODES.CONTROL_LEFT); expect(copyPasteTextarea.value).toEqual('A\t1\nB\t2'); }); it('should set copyable text when selecting a single cell with editor type as false (#2574)', () => { handsontable({ data: [['A', 1], ['B', 2]], columns: [ { type: 'text' }, { editor: false } ] }); const copyPasteTextarea = $('#HandsontableCopyPaste')[0]; expect(copyPasteTextarea.value.length).toEqual(0); selectCell(1, 1, 1, 1); keyDownUp(Handsontable.helper.KEY_CODES.CONTROL_LEFT); expect(copyPasteTextarea.value).toEqual('2'); }); it('should set copyable text until copyRowsLimit is reached', () => { handsontable({ data: arrayOfArrays(), copyPaste: { rowsLimit: 2 }, }); selectCell(0, 0, countRows() - 1, countCols() - 1); // selectAll keyDownUp('ctrl'); // should prepare 2 rows for copying expect($('#HandsontableCopyPaste')[0].value).toEqual('\tKia\tNissan\tToyota\tHonda\n2008\t10\t11\t12\t13'); }); it('should set copyable text until copyColsLimit is reached', () => { handsontable({ data: arrayOfArrays(), copyPaste: { columnsLimit: 2 }, }); selectCell(0, 0, countRows() - 1, countCols() - 1); // selectAll keyDownUp('ctrl'); // should prepare 2 columns for copying expect($('#HandsontableCopyPaste')[0].value).toEqual('\tKia\n2008\t10\n2009\t20\n2010\t30'); }); it('should call onCopyLimit callback when copy limit was reached', () => { let result; handsontable({ data: arrayOfArrays(), copyPaste: { rowsLimit: 2, columnsLimit: 2 }, afterCopyLimit(selectedRowsCount, selectedColsCount, copyRowsLimit, copyColsLimit) { result = [selectedRowsCount, selectedColsCount, copyRowsLimit, copyColsLimit]; } }); selectCell(0, 0, countRows() - 1, countCols() - 1); // selectAll keyDownUp('ctrl'); expect(result).toEqual([4, 5, 2, 2]); }); }); describe('copy', () => { it('should be possible to copy data by keyboard shortcut', () => { handsontable({ data: Handsontable.helper.createSpreadsheetData(2, 2), }); selectCell(1, 1); keyDown('ctrl'); keyDown('ctrl+c'); expect($('#HandsontableCopyPaste')[0]).toBe(document.activeElement); // unfortunately we have not access to read data from the system clipboard }); it('should be possible to copy data by API', () => { const hot = handsontable({ data: Handsontable.helper.createSpreadsheetData(2, 2), }); selectCell(1, 0); hot.getPlugin('CopyPaste').setCopyableText(); // below line is cause of console warning in FF about execCommand hot.getPlugin('CopyPaste').copy(true); expect($('#HandsontableCopyPaste')[0]).toBe(document.activeElement); // unfortunately we have not access to read data from the system clipboard }); it('should be possible to copy data by contextMenu option', () => { const beforeCopySpy = jasmine.createSpy('beforeCopy'); handsontable({ data: Handsontable.helper.createSpreadsheetData(2, 2), beforeCopy: beforeCopySpy, contextMenu: ['copy'] }); selectCell(0, 1); contextMenu(); const items = $('.htContextMenu tbody td'); const actions = items.not('.htSeparator'); actions.simulate('mousedown'); expect(beforeCopySpy).toHaveBeenCalledTimes(1); }); it('should call beforeCopy and afterCopy during copying operation', () => { const beforeCopySpy = jasmine.createSpy('beforeCopy'); const afterCopySpy = jasmine.createSpy('afterCopy'); handsontable({ data: Handsontable.helper.createSpreadsheetData(2, 2), beforeCopy: beforeCopySpy, afterCopy: afterCopySpy }); selectCell(0, 0); keyDown('ctrl'); keyDown('ctrl+c'); expect(beforeCopySpy.calls.count()).toEqual(1); expect(beforeCopySpy).toHaveBeenCalledWith([['A1']], [{startRow: 0, startCol: 0, endRow: 0, endCol: 0}], void 0, void 0, void 0, void 0); expect(afterCopySpy.calls.count()).toEqual(1); expect(afterCopySpy).toHaveBeenCalledWith([['A1']], [{startRow: 0, startCol: 0, endRow: 0, endCol: 0}], void 0, void 0, void 0, void 0); }); it('should be possible to block copying', () => { const afterCopySpy = jasmine.createSpy('afterCopy'); handsontable({ data: Handsontable.helper.createSpreadsheetData(2, 2), beforeCopy() { return false; }, afterCopy: afterCopySpy }); selectCell(0, 0); keyDown('ctrl'); keyDown('ctrl+c'); expect(afterCopySpy.calls.count()).toEqual(0); }); it('should be possible modification of changes during copying', async () => { handsontable({ data: Handsontable.helper.createSpreadsheetData(2, 2), beforeCopy(changes) { changes.splice(0, 1); } }); selectCell(0, 0, 1, 0); keyDown('ctrl'); keyDown('ctrl+c'); await sleep(60); expect($('#HandsontableCopyPaste')[0].value).toEqual('A2'); }); }); describe('cut', () => { it('should be possible to cut data by keyboard shortcut', async () => { const hot = handsontable({ data: Handsontable.helper.createSpreadsheetData(2, 2), }); selectCell(1, 1); keyDown('ctrl'); keyDown('ctrl+x'); expect($('#HandsontableCopyPaste')[0]).toBe(document.activeElement); await sleep(100); expect(hot.getDataAtCell(1, 1)).toBe(''); // unfortunately we have not access to read data from the system clipboard }); it('should be possible to cut data by API', async () => { const hot = handsontable({ data: Handsontable.helper.createSpreadsheetData(2, 2), }); selectCell(1, 0); hot.getPlugin('CopyPaste').setCopyableText(); // below line is cause of console warning in FF about execCommand hot.getPlugin('CopyPaste').cut(true); expect($('#HandsontableCopyPaste')[0]).toBe(document.activeElement); await sleep(100); expect(hot.getDataAtCell(1, 0)).toBe(''); // unfortunately we have not access to read data from the system clipboard }); it('should be possible to cut data by contextMenu option', () => { const beforeCutSpy = jasmine.createSpy('beforeCopy'); handsontable({ data: Handsontable.helper.createSpreadsheetData(2, 2), beforeCut: beforeCutSpy, contextMenu: ['cut'] }); selectCell(0, 1); contextMenu(); const items = $('.htContextMenu tbody td'); const actions = items.not('.htSeparator'); actions.simulate('mousedown'); expect(beforeCutSpy).toHaveBeenCalledTimes(1); }); it('should call beforeCut and afterCut during cutting out operation', () => { const beforeCutSpy = jasmine.createSpy('beforeCut'); const afterCutSpy = jasmine.createSpy('afterCut'); handsontable({ data: Handsontable.helper.createSpreadsheetData(2, 2), beforeCut: beforeCutSpy, afterCut: afterCutSpy }); selectCell(0, 0); keyDown('ctrl'); keyDown('ctrl+x'); expect(beforeCutSpy.calls.count()).toEqual(1); expect(beforeCutSpy).toHaveBeenCalledWith([['A1']], [{startRow: 0, startCol: 0, endRow: 0, endCol: 0}], void 0, void 0, void 0, void 0); expect(afterCutSpy.calls.count()).toEqual(1); expect(afterCutSpy).toHaveBeenCalledWith([['A1']], [{startRow: 0, startCol: 0, endRow: 0, endCol: 0}], void 0, void 0, void 0, void 0); }); it('should be possible to block cutting out', () => { const afterCutSpy = jasmine.createSpy('afterCut'); handsontable({ data: Handsontable.helper.createSpreadsheetData(2, 2), beforeCut() { return false; }, afterCut: afterCutSpy }); selectCell(0, 0); keyDown('ctrl'); keyDown('ctrl+x'); expect(afterCutSpy.calls.count()).toEqual(0); }); }); describe('paste', () => { it('should not create new rows or columns when allowInsertRow and allowInsertColumn equal false', async () => { handsontable({ data: arrayOfArrays(), copyPaste: { pasteMode: 'shift_down', }, allowInsertRow: false, allowInsertColumn: false }); selectCell(3, 4); // selectAll triggerPaste('Kia\tNissan\tToyota'); await sleep(60); let expected = arrayOfArrays(); expected[3][4] = 'Kia'; expect(getData()).toEqual(expected); }); it('should shift data down instead of overwrite when paste (when allowInsertRow = false)', async () => { handsontable({ data: arrayOfArrays(), copyPaste: { pasteMode: 'shift_down', }, allowInsertRow: false }); selectCell(1, 0); // selectAll triggerPaste('Kia\tNissan\tToyota'); await sleep(60); expect(getData().length).toEqual(4); expect(getData(0, 0, 2, 4)).toEqual([['', 'Kia', 'Nissan', 'Toyota', 'Honda'], ['Kia', 'Nissan', 'Toyota', 12, 13], ['2008', 10, 11, 14, 13]]); }); it('should shift data down instead of overwrite when paste (minSpareRows > 0)', async () => { handsontable({ data: arrayOfArrays(), copyPaste: { pasteMode: 'shift_down' }, minSpareRows: 1 }); selectCell(1, 0); // selectAll triggerPaste('Kia\tNissan\tToyota'); await sleep(60); expect(getData().length).toEqual(6); expect(getData(0, 0, 2, 4)).toEqual([['', 'Kia', 'Nissan', 'Toyota', 'Honda'], ['Kia', 'Nissan', 'Toyota', 12, 13], ['2008', 10, 11, 14, 13]]); }); it('should shift right insert instead of overwrite when paste', async () => { handsontable({ data: arrayOfArrays(), copyPaste: { pasteMode: 'shift_right' }, allowInsertColumn: false }); selectCell(1, 0); // selectAll triggerPaste('Kia\tNissan\tToyota'); await sleep(60); expect(getData()[0].length).toEqual(5); expect(getDataAtRow(1)).toEqual(['Kia', 'Nissan', 'Toyota', '2008', 10]); }); it('should shift right insert instead of overwrite when paste (minSpareCols > 0)', (done) => { handsontable({ data: arrayOfArrays(), copyPaste: { pasteMode: 'shift_right' }, minSpareCols: 1 }); selectCell(1, 0); // selectAll triggerPaste('Kia\tNissan\tToyota'); setTimeout(() => { expect(getData()[0].length).toEqual(9); expect(getDataAtRow(1)).toEqual(['Kia', 'Nissan', 'Toyota', '2008', 10, 11, 12, 13, null]); done(); }, 60); }); it('should not throw an error when changes are null in `once` hook', async () => { let errors = 0; try { handsontable({ data: arrayOfArrays(), afterChange(changes, source) { if (source === 'loadData') { return; } loadData(arrayOfArrays()); } }); selectCell(1, 0); // selectAll triggerPaste('Kia\tNissan\tToyota'); } catch (e) { errors++; } await sleep(60); expect(errors).toEqual(0); }); it('should not paste any data, if no cell is selected', (done) => { const copiedData1 = 'foo'; const copiedData2 = 'bar'; handsontable({ data: Handsontable.helper.createSpreadsheetData(3, 1) }); expect(spec().$container.find('tbody tr:eq(0) td:eq(0)').text()).toEqual('A1'); expect(spec().$container.find('tbody tr:eq(1) td:eq(0)').text()).toEqual('A2'); expect(spec().$container.find('tbody tr:eq(2) td:eq(0)').text()).toEqual('A3'); expect(getSelected()).toBeUndefined(); triggerPaste(copiedData1); setTimeout(() => { expect(spec().$container.find('tbody tr:eq(0) td:eq(0)').text()).toEqual('A1'); expect(spec().$container.find('tbody tr:eq(1) td:eq(0)').text()).toEqual('A2'); expect(spec().$container.find('tbody tr:eq(2) td:eq(0)').text()).toEqual('A3'); }, 100); setTimeout(() => { selectCell(1, 0, 2, 0); triggerPaste(copiedData2); }, 200); setTimeout(() => { expect(spec().$container.find('tbody tr:eq(0) td:eq(0)').text()).toEqual('A1'); expect(spec().$container.find('tbody tr:eq(1) td:eq(0)').text()).toEqual(copiedData2); expect(spec().$container.find('tbody tr:eq(2) td:eq(0)').text()).toEqual(copiedData2); done(); }, 300); }); it('should not paste any data, if no cell is selected (select/deselect cell using mouse)', async () => { const copiedData = 'foo'; handsontable({ data: Handsontable.helper.createSpreadsheetData(3, 1) }); expect(spec().$container.find('tbody tr:eq(0) td:eq(0)').text()).toEqual('A1'); expect(spec().$container.find('tbody tr:eq(1) td:eq(0)').text()).toEqual('A2'); expect(spec().$container.find('tbody tr:eq(2) td:eq(0)').text()).toEqual('A3'); spec().$container.find('tbody tr:eq(1) td:eq(0)').simulate('mousedown'); spec().$container.find('tbody tr:eq(1) td:eq(0)').simulate('mouseup'); expect(getSelected()).toEqual([1, 0, 1, 0]); $('html').simulate('mousedown').simulate('mouseup'); expect(getSelected()).toBeUndefined(); triggerPaste(copiedData); await sleep(100); expect(spec().$container.find('tbody tr:eq(0) td:eq(0)').text()).toEqual('A1'); expect(spec().$container.find('tbody tr:eq(1) td:eq(0)').text()).toEqual('A2'); expect(spec().$container.find('tbody tr:eq(2) td:eq(0)').text()).toEqual('A3'); }); it('should call beforePaste and afterPaste during pasting operation', async () => { const beforePasteSpy = jasmine.createSpy('beforePaste'); const afterPasteSpy = jasmine.createSpy('afterPaste'); handsontable({ data: Handsontable.helper.createSpreadsheetData(2, 2), beforePaste: beforePasteSpy, afterPaste: afterPasteSpy }); selectCell(0, 0); keyDown('ctrl'); triggerPaste('Kia'); await sleep(60); expect(beforePasteSpy.calls.count()).toEqual(1); expect(beforePasteSpy).toHaveBeenCalledWith([['Kia']], [{startRow: 0, startCol: 0, endRow: 0, endCol: 0}], void 0, void 0, void 0, void 0); expect(afterPasteSpy.calls.count()).toEqual(1); expect(afterPasteSpy).toHaveBeenCalledWith([['Kia']], [{startRow: 0, startCol: 0, endRow: 0, endCol: 0}], void 0, void 0, void 0, void 0); }); it('should be possible to block pasting', async () => { const afterPasteSpy = jasmine.createSpy('afterPaste'); handsontable({ data: Handsontable.helper.createSpreadsheetData(2, 2), beforePaste() { return false; }, afterPaste: afterPasteSpy }); selectCell(0, 0); keyDown('ctrl'); triggerPaste('Kia'); await sleep(60); expect(afterPasteSpy.calls.count()).toEqual(0); }); it('should be possible modification of changes', async () => { handsontable({ data: Handsontable.helper.createSpreadsheetData(2, 2), beforePaste(changes) { changes.splice(0, 1); } }); selectCell(0, 0); keyDown('ctrl'); triggerPaste('Kia\nToyota'); await sleep(60); expect(spec().$container.find('tbody tr:eq(0) td:eq(0)').text()).toEqual('Toyota'); expect(spec().$container.find('tbody tr:eq(1) td:eq(0)').text()).toEqual('A2'); }); }); });