diff --git a/test/nbrowser/CopyPaste.ntest.js b/test/nbrowser/CopyPaste.ntest.js new file mode 100644 index 00000000000..a871cdb41b7 --- /dev/null +++ b/test/nbrowser/CopyPaste.ntest.js @@ -0,0 +1,1150 @@ +/* global document, window */ + +import { assert, driver } from 'mocha-webdriver'; +import { $, gu, test } from 'test/nbrowser/gristUtil-nbrowser'; + +function createDummyTextArea() { + var textArea = document.createElement('textarea'); + textArea.style.position = "absolute"; + textArea.style.top = "0"; + textArea.style.height = "2rem"; + textArea.style.width = "16rem"; + textArea.id = 'dummyText'; + window.document.body.appendChild(textArea); +} + +function removeDummyTextArea() { + var dom = document.getElementById('dummyText'); + if (dom) { + window.document.body.removeChild(dom); + } +} + +describe('CopyPaste.ntest', function() { + this.timeout(90000); + + const cleanup = test.setupTestSuite(this); + + afterEach(function() { + return gu.checkForErrors(); + }); + + async function fromGrid(cb, vs, start, end, action) { + await gu.actions.viewSection(vs).selectSection(); + await gu.selectGridArea(start, end); + if (action === 'cut') { + await cb.cut(); + } else { + await cb.copy(); + } + } + + async function copyFromGrid(cb, copyVs, copyStart, copyEnd) { + await fromGrid(cb, copyVs, copyStart, copyEnd, 'copy'); + } + + async function cutFromGrid(cb, cutVs, cutStart, cutEnd) { + await fromGrid(cb, cutVs, cutStart, cutEnd, 'cut'); + } + + async function fromDetail(cb, vs, colName, rowNum, action) { + await gu.actions.viewSection(vs).selectSection(); + await gu.clickVisibleDetailCells(colName, [rowNum]); + if (action === 'cut') { + await cb.cut(); + } else { + await cb.copy(); + } + } + + async function copyFromDetail(cb, copyVs, colName, rowNum) { + await fromDetail(cb, copyVs, colName, rowNum, 'copy'); + } + + async function cutFromDetail(cb, cutVs, colName, rowNum) { + await fromDetail(cb, cutVs, colName, rowNum, 'cut'); + } + + async function pasteToGrid(cb, pasteVs, startRow, startCol) { + await gu.actions.viewSection(pasteVs).selectSection(); + await gu.clickCellRC(startRow - 1, startCol); + await cb.paste(); + await gu.waitForServer(); + } + + async function pasteToDetail(cb, pasteVs, colName, rowNum) { + await gu.actions.viewSection(pasteVs).selectSection(); + await gu.clickVisibleDetailCells(colName, [rowNum]); + await cb.paste(); + await gu.waitForServer(); + } + + describe("when copying between viewSections", function() { + before(async function() { + await gu.supportOldTimeyTestCode(); + await gu.useFixtureDoc(cleanup, "CopyPaste.grist", true); + }); + + it("should allow copy-paste multi-character column names", async function() { + await gu.actions.selectTabView("Complex"); + + // Try copy and paste with gridview + await gu.withSafeClipboard(async (cb) => { + await copyFromGrid(cb, "Complex", [1, 0], [1, 0]); + assert.deepEqual(await gu.getGridValues({ rowNums: [1], cols: [0] }), + ['hello']); + await pasteToGrid(cb, "Complex", 1, 2); + }); + assert.deepEqual(await gu.getGridValues({ rowNums: [1], cols: [2] }), + ['hello']); + + // Try copy and paste with detailView + await gu.withSafeClipboard(async (cb) => { + await copyFromDetail(cb, "Complex Card List", "Bar", 1); + assert.equal(await gu.getVisibleDetailCells('Bar', [1]), 'world'); + await pasteToDetail(cb, "Complex Card List", "Baz", 1); + }); + assert.equal(await gu.getVisibleDetailCells('Baz', [1]), 'world'); + }); + + // GridView Multi Cell copy tests + it('should allow multi cell copying between gridviews that reference the same table', async function() { + await gu.actions.selectTabView("GridGrid"); + await gu.withSafeClipboard(async (cb) => { + await copyFromGrid(cb, "GridGrid", [1, 0], [3, 2]); + assert.deepEqual(await gu.getGridValues({ rowNums: [1, 2, 3], cols: [0, 1, 2] }), + ['A1', 'B1', 'C1', 'A2', 'B2', 'C2', 'A3', 'B3', 'C3']); + + await pasteToGrid(cb, "GridGrid record2", 4, 0); + }); + assert.deepEqual(await gu.getGridValues({ rowNums: [4, 5, 6], cols: [0, 1, 2] }), + ['A1', 'B1', 'C1', 'A2', 'B2', 'C2', 'A3', 'B3', 'C3']); + }); + + it('should allow multi cell copying between gridviews that reference a different table', async function() { + await gu.actions.selectTabView("GridGrid"); + await gu.withSafeClipboard(async (cb) => { + await copyFromGrid(cb, "GridGrid", [1, 0], [3, 2]); + assert.deepEqual(await gu.getGridValues({ rowNums: [1, 2, 3], cols: [0, 1, 2] }), + ['A1', 'B1', 'C1', 'A2', 'B2', 'C2', 'A3', 'B3', 'C3']); + await pasteToGrid(cb, "Basic", 4, 0); + }); + assert.deepEqual(await gu.getGridValues({ rowNums: [4, 5, 6], cols: [0, 1, 2] }), + ['A1', 'B1', 'C1', 'A2', 'B2', 'C2', 'A3', 'B3', 'C3']); + }); + + it('should allow multi cell copying between a gridview and a detailView that reference the same table', async function() { + await gu.actions.selectTabView("GridDetail"); + await gu.withSafeClipboard(async (cb) => { + await copyFromGrid(cb, "GridDetail", [1, 0], [3, 2]); + assert.deepEqual(await gu.getGridValues({ rowNums: [1, 2, 3], cols: [0, 1, 2] }), + ['A1', 'B1', 'C1', 'A2', 'B2', 'C2', 'A3', 'B3', 'C3']); + await pasteToDetail(cb, "GridDetail Card List", 'C', 1); + }); + assert.deepEqual(await gu.getDetailValues({ rowNums: [1, 2, 3], cols: ['C', 'D', 'E'] }), + ['A1', 'D1', 'E1', 'C2', 'D2', 'E2', 'C3', 'D3', 'E3']); + }); + + it('should allow multi cell copying between a gridview and a detailView that reference a different table', async function() { + await gu.withSafeClipboard(async (cb) => { + await copyFromGrid(cb, "GridDetail", [4, 0], [6, 2]); + assert.deepEqual(await gu.getGridValues({ rowNums: [4, 5, 6], cols: [0, 1, 2] }), + ['A4', 'B4', 'C4', 'A5', 'B5', 'C5', 'A6', 'B6', 'C6']); + await pasteToDetail(cb, "Basic Card List", 'D', 1); + }); + assert.deepEqual(await gu.getDetailValues({ rowNums: [1, 2, 3], cols: ['D', 'E', 'F'] }), + ['A4', 'E1', 'F1', 'D2', 'E2', 'F2', 'D3', 'E3', 'F3']); + }); + + // GridView single Cell cut tests + it('should allow single cell cutting between a gridView and a detailView that reference the same table', async function() { + await gu.actions.selectTabView("GridDetail"); + await gu.withSafeClipboard(async (cb) => { + await cutFromGrid(cb, "GridDetail", [7, 3], [7, 3]); + assert.deepEqual(await gu.getGridValues({ rowNums: [7], cols: [3] }), + ['D7']); + await pasteToDetail(cb, "GridDetail Card List", 'D', 1); + }); + assert.deepEqual(await gu.getVisibleDetailCells('D', [1]), ['D7']); + await gu.actions.viewSection("GridDetail").selectSection(); + assert.deepEqual(await gu.getGridValues({ rowNums: [7], cols: [3] }), + ['']); + }); + + it('should allow single cell cutting between a gridView and a detailView that reference a different table', async function() { + await gu.actions.selectTabView("GridDetail"); + await gu.withSafeClipboard(async (cb) => { + await cutFromGrid(cb, "GridDetail", [7, 2], [7, 2]); + assert.deepEqual(await gu.getGridValues({ rowNums: [7], cols: [2] }), + ['C7']); + await pasteToDetail(cb, "Basic Card List", 'C', 1); + }); + assert.deepEqual(await gu.getVisibleDetailCells('C', [1]), ['C7']); + await gu.actions.viewSection("GridDetail").selectSection(); + assert.deepEqual(await gu.getGridValues({ rowNums: [7], cols: [2] }), + ['']); + }); + + // GridView multi Cell cut tests + it('should allow multi cell cutting between gridviews that reference the same table', async function() { + await gu.actions.selectTabView("GridGrid"); + await gu.withSafeClipboard(async (cb) => { + await cutFromGrid(cb, "GridGrid", [5, 1], [7, 3]); + assert.deepEqual(await gu.getGridValues({ rowNums: [5, 6, 7], cols: [1, 2, 3] }), + ['B2', 'C2', 'D5', 'B3', 'C3', 'D6', 'B7', 'C7', 'D7']); + await pasteToGrid(cb, "GridGrid record2", 1, 0); + }); + assert.deepEqual(await gu.getGridValues({ rowNums: [1, 2, 3], cols: [0, 1, 2] }), + ['B2', 'C2', 'D5', 'B3', 'C3', 'D6', 'B7', 'C7', 'D7']); + + await gu.actions.viewSection("GridGrid").selectSection(); + assert.deepEqual(await gu.getGridValues({ rowNums: [5, 6, 7], cols: [1, 2, 3] }), + ['', '', '', '', '', '', '', '', '']); + }); + + it('should allow multi cell cutting between gridviews that reference a different table', async function() { + await gu.actions.selectTabView("GridGrid"); + await gu.sendKeys([$.MOD, $.UP], $.HOME); + await gu.withSafeClipboard(async (cb) => { + await cutFromGrid(cb, "GridGrid", [1, 0], [3, 2]); + assert.deepEqual(await gu.getGridValues({ rowNums: [1, 2, 3], cols: [0, 1, 2] }), + ['B2', 'C2', 'D5', 'B3', 'C3', 'D6', 'B7', 'C7', 'D7']); + + // scroll the top of the grid into view + await gu.actions.viewSection("Basic").selectSection(); + await gu.sendKeys([$.MOD, $.UP]); + await pasteToGrid(cb, "Basic", 1, 0); + }); + assert.deepEqual(await gu.getGridValues({ rowNums: [1, 2, 3], cols: [0, 1, 2] }), + ['B2', 'C2', 'D5', 'B3', 'C3', 'D6', 'B7', 'C7', 'D7']); + + await gu.actions.viewSection("GridGrid").selectSection(); + assert.deepEqual(await gu.getGridValues({ rowNums: [1, 2, 3], cols: [0, 1, 2] }), + ['', '', '', '', '', '', '', '', '']); + }); + + it('should allow multi cell cutting between a gridview and a detailView that reference the same table', async function() { + await gu.actions.selectTabView("GridDetail"); + await gu.sendKeys([$.MOD, $.UP], $.HOME); + await gu.withSafeClipboard(async (cb) => { + await cutFromGrid(cb, "GridDetail", [1, 0], [3, 2]); + assert.deepEqual(await gu.getGridValues({ rowNums: [1, 2, 3], cols: [0, 1, 2] }), + ['A1', 'B1', 'A1', 'A2', 'B2', 'C2', 'A3', 'B3', 'C3']); + await pasteToDetail(cb, "GridDetail Card List", 'D', 1); + }); + assert.deepEqual(await gu.getDetailValues({ rowNums: [1, 2, 3], cols: ['C', 'D', 'E'] }), + ['A1', 'A1', 'E1', 'C2', 'D2', 'E2', 'C3', 'D3', 'E3']); + await gu.actions.viewSection("GridDetail").selectSection(); + assert.deepEqual(await gu.getGridValues({ rowNums: [1, 2, 3], cols: [0, 1, 2] }), + ['A1', 'B1', 'A1', 'A2', 'B2', 'C2', 'A3', 'B3', 'C3']); + }); + + it('should allow multi cell cutting between a gridview and a detailView that reference a different table', async function() { + await gu.withSafeClipboard(async (cb) => { + await cutFromGrid(cb, "GridDetail", [1, 0], [3, 2]); + assert.deepEqual(await gu.getGridValues({ rowNums: [1, 2, 3], cols: [0, 1, 2] }), + ['A1', 'B1', 'A1', 'A2', 'B2', 'C2', 'A3', 'B3', 'C3']); + await pasteToDetail(cb, "Basic Card List", 'A', 1); + }); + assert.deepEqual(await gu.getDetailValues({ rowNums: [1, 2, 3], cols: ['A', 'B', 'C'] }), + ['A1', 'C2', 'C7', 'B3', 'C3', 'C2', 'B7', 'C7', 'C3']); + await gu.actions.viewSection("GridDetail").selectSection(); + assert.deepEqual(await gu.getGridValues({ rowNums: [1, 2, 3], cols: [0, 1, 2] }), + ['A1', 'B1', 'A1', 'A2', 'B2', 'C2', 'A3', 'B3', 'C3']); + }); + + // DetailView Single Cell copy tests + it('should allow single cell copying between detailViews that reference the same table', async function() { + await gu.actions.selectTabView("DetailDetail"); + await gu.withSafeClipboard(async (cb) => { + await copyFromDetail(cb, "DetailDetail Card List", "B", 1); + assert.equal(await gu.getVisibleDetailCells('B', [1]), 'B1'); + await pasteToDetail(cb, "DetailDetail detail2", "C", 1); + }); + assert.equal(await gu.getVisibleDetailCells('C', [1]), 'B1'); + }); + + it('should allow single cell copying between detailViews that reference a different table', async function() { + await gu.withSafeClipboard(async (cb) => { + await copyFromDetail(cb, "DetailDetail Card List", "B", 1); + assert.equal(await gu.getVisibleDetailCells('B', [1]), 'B1'); + await pasteToDetail(cb, "Basic Card List", "C", 1); + }); + assert.equal(await gu.getVisibleDetailCells('C', [1]), 'B1'); + }); + + it('should allow single cell copying between a detailView and a gridView that reference the same table', async function() { + await gu.actions.selectTabView("GridDetail"); + await gu.withSafeClipboard(async (cb) => { + await copyFromDetail(cb, "GridDetail Card List", "B", 1); + assert.equal(await gu.getVisibleDetailCells('B', [1]), 'B1'); + await pasteToGrid(cb, "GridDetail", 4, 0); + }); + assert.deepEqual(await gu.getGridValues({ rowNums: [4], cols: [0] }), + ['B1']); + }); + + it('should allow single cell copying between a detailView and a gridView that reference a different table', async function() { + await gu.withSafeClipboard(async (cb) => { + await copyFromDetail(cb, "Basic Card List", "B", 1); + assert.equal(await gu.getVisibleDetailCells('B', [1]), 'C2'); + await pasteToGrid(cb, "GridDetail", 5, 0); + }); + assert.deepEqual(await gu.getGridValues({ rowNums: [5], cols: [0] }), + ['C2']); + }); + + // DetailView Single Cell cut tests + it('should allow single cell cutting between detailViews that reference the same table', async function() { + await gu.actions.selectTabView("DetailDetail"); + await gu.withSafeClipboard(async (cb) => { + await cutFromDetail(cb, "DetailDetail Card List", "A", 1); + assert.equal(await gu.getVisibleDetailCells('A', [1]), 'A1'); + await pasteToDetail(cb, "DetailDetail detail2", "C", 1); + }); + assert.equal(await gu.getVisibleDetailCells('C', [1]), 'A1'); + await gu.actions.viewSection("DetailDetail Card List").selectSection(); + assert.equal(await gu.getVisibleDetailCells('A', [1]), ''); + }); + + it('should allow single cell cutting between detailViews that reference a different table', async function() { + await gu.withSafeClipboard(async (cb) => { + await cutFromDetail(cb, "DetailDetail Card List", "B", 1); + assert.equal(await gu.getVisibleDetailCells('B', [1]), 'B1'); + await pasteToDetail(cb, "Basic Card List", "E", 1); + }); + assert.equal(await gu.getVisibleDetailCells('E', [1]), 'B1'); + await gu.actions.viewSection("DetailDetail Card List").selectSection(); + assert.equal(await gu.getVisibleDetailCells('B', [1]), ''); + }); + + it('should allow single cell cutting between a detailView and a gridView that reference the same table', async function() { + await gu.actions.selectTabView("GridDetail"); + await gu.withSafeClipboard(async (cb) => { + await cutFromDetail(cb, "GridDetail Card List", "E", 1); + assert.equal(await gu.getVisibleDetailCells('E', [1]), 'E1'); + await pasteToGrid(cb, "GridDetail", 4, 0); + }); + assert.deepEqual(await gu.getGridValues({ rowNums: [4], cols: [0] }), + ['E1']); + await gu.actions.viewSection("GridDetail Card List").selectSection(); + assert.equal(await gu.getVisibleDetailCells('E', [1]), ''); + }); + + it('should allow single cell cutting between a detailView and a gridView that reference a different table', async function() { + await gu.withSafeClipboard(async (cb) => { + await cutFromDetail(cb, "Basic Card List", "F", 1); + assert.equal(await gu.getVisibleDetailCells('F', [1]), 'F1'); + await pasteToGrid(cb, "GridDetail", 5, 0); + }); + assert.deepEqual(await gu.getGridValues({ rowNums: [5], cols: [0] }), ['F1']); + await gu.actions.viewSection("Basic Card List").selectSection(); + assert.equal(await gu.getVisibleDetailCells('F', [1]), ''); + }); + + it('should allow undoing a cut-paste in a single step', async function() { + // Test DetailView cut/paste reversal + await gu.withSafeClipboard(async (cb) => { + await cutFromDetail(cb, "GridDetail Card List", "B", 1); + assert.equal(await gu.getVisibleDetailCells('B', [1]), 'B1'); + + // Paste and check for success + await pasteToDetail(cb, "Basic Card List", "D", 2); + }); + assert.equal(await gu.getVisibleDetailCells('D', [2]), 'B1'); + await gu.actions.viewSection("GridDetail Card List").selectSection(); + assert.equal(await gu.getVisibleDetailCells('B', [1]), ''); + + // Undo the cut/paste and check that everything is reversed + // Use the keyboard shortcut since the clipboard preview covers the top bar. + await gu.sendKeys([$.MOD, 'z']); + await gu.waitForServer(); + await gu.actions.viewSection("GridDetail Card List").selectSection(); + assert.equal(await gu.getVisibleDetailCells('B', [1]), 'B1'); + await gu.actions.viewSection("Basic Card List").selectSection(); + assert.equal(await gu.getVisibleDetailCells('D', [2]), 'D6'); + + // Test GridView cut/paste reversal + await gu.actions.selectTabView("GridGrid"); + await gu.withSafeClipboard(async (cb) => { + await cutFromGrid(cb, "Basic", [2, 1], [3, 2]); + assert.deepEqual(await gu.getGridValues({ rowNums: [2, 3], cols: [1, 2] }), + ['C3', 'D6', 'C7', 'D7']); + // Paste and check for success + await pasteToGrid(cb, "GridGrid", 1, 1); + }); + assert.deepEqual(await gu.getGridValues({ rowNums: [1, 2], cols: [1, 2] }), + ['C3', 'D6', 'C7', 'D7']); + await gu.actions.viewSection("Basic").selectSection(); + assert.deepEqual(await gu.getGridValues({ rowNums: [2, 3], cols: [1, 2] }), + ['', '', '', '']); + + // Undo the cut/paste and check that everything is reversed + // Use the keyboard shortcut since the clipboard preview covers the top bar. + await gu.sendKeys([$.MOD, 'z']); + await gu.waitForServer(); + await gu.actions.viewSection("Basic").selectSection(); + assert.deepEqual(await gu.getGridValues({ rowNums: [2, 3], cols: [1, 2] }), + ['C3', 'D6', 'C7', 'D7']); + await gu.actions.viewSection("GridGrid").selectSection(); + assert.deepEqual(await gu.getGridValues({ rowNums: [1, 2], cols: [1, 2] }), + ['', '', '', '']); + }); + }); + + for (const method of ['menu', 'keyboard']) { + const copy = (cb) => cb.copy({method}); + const cut = (cb) => cb.cut({method}); + const paste = (cb) => cb.paste({method}); + + describe(`DetailView with ${method} commands`, function() { + before(async function() { + await gu.supportOldTimeyTestCode(); + await gu.useFixtureDoc(cleanup, "CopyPaste.grist", true); + await gu.toggleSidePanel('left', 'close'); + await driver.executeScript(createDummyTextArea); + }); + + after(async function() { + await driver.executeScript(removeDummyTextArea); + }); + + it("should allow copy and paste of single cells", async function() { + await gu.actions.addNewSection('Basic', 'Card List'); + await gu.closeSidePane(); + await gu.getSection('BASIC Card List').click(); + await gu.renameActiveSection('Basic Card List'); + await gu.waitForServer(); + + await gu.actions.viewSection('Basic Card List').selectSection(); + await gu.clickVisibleDetailCells('A', [1]); + assert.equal(await gu.getVisibleDetailCells('A', [1]), 'A1'); + + await gu.withSafeClipboard(async (cb) => { + await copy(cb); + await gu.clickVisibleDetailCells('B', [1]); + assert.equal(await gu.getVisibleDetailCells('B', [1]), 'B1'); + + await paste(cb); + }); + await gu.waitForServer(); + + // Check that cell was copied + await driver.wait(async () => (await gu.getVisibleDetailCells('B', [1]))[0] === 'A1', 1000); + // Check that original is unchanged + assert.equal(await gu.getVisibleDetailCells('A', [1]), 'A1'); + }); + + it("should allow cut and paste of single cells", async function() { + await gu.actions.viewSection('Basic Card List').selectSection(); + await gu.clickVisibleDetailCells('I', [1]); + assert.equal(await gu.getVisibleDetailCells('I', [1]), 'I1'); + + await gu.withSafeClipboard(async (cb) => { + await cut(cb); + await gu.clickVisibleDetailCells('E', [1]); + assert.equal(await gu.getVisibleDetailCells('E', [1]), 'E1'); + + await paste(cb); + }); + await gu.waitForServer(); + + // Check that cell was copied + await driver.wait(async () => (await gu.getVisibleDetailCells('E', [1]))[0] === 'I1', 1000); + // Check that original removed + assert.equal(await gu.getVisibleDetailCells('I', [1]), ''); + }); + + it("should allow multiple pastes after a single copy", async function() { + await gu.clickVisibleDetailCells('A', [1]); + assert.equal(await gu.getVisibleDetailCells('A', [1]), 'A1'); + await gu.withSafeClipboard(async (cb) => { + await copy(cb); + + await gu.clickVisibleDetailCells('C', [1]); + assert.equal(await gu.getVisibleDetailCells('C', [1]), 'C1'); + await paste(cb); + await driver.wait(async () => (await gu.getVisibleDetailCells('C', [1]))[0] === 'A1', 1000); + await gu.clickVisibleDetailCells('D', [1]); + assert.equal(await gu.getVisibleDetailCells('D', [1]), 'D1'); + await paste(cb); + }); + await gu.waitForServer(); + await driver.wait(async () => (await gu.getVisibleDetailCells('D', [1]))[0] === 'A1', 1000); + }); + + it("should allow paste of single cell from an external source", async function() { + // Copy from dummy text area to simulate a copy from outside of grist + await $('#dummyText').val('barfoo').resolve(); + await $('#dummyText').sendKeys($.SELECT_ALL); + await gu.withSafeClipboard(async (cb) => { + await cb.copy(); + + // Then paste + await gu.clickVisibleDetailCells('A', [1]); + assert.equal(await gu.getVisibleDetailCells('A', [1]), 'A1'); + await paste(cb); + }); + await gu.waitForServer(); + await driver.wait(async () => (await gu.getVisibleDetailCells('A', [1]))[0] === 'barfoo', 1000); + }); + + it("should allow paste of multiple cells from an external source", async function() { + // Copy from dummy text area to simulate a copy from outside of grist + await $('#dummyText').val('topleft\ttopright\nbottomleft\tbottomright').resolve(); + await $('#dummyText').click(); + await gu.sendKeys($.SELECT_ALL_LINES(2)); + await gu.withSafeClipboard(async (cb) => { + await cb.copy(); + + // Then paste + await gu.clickVisibleDetailCells('A', [1]); + await gu.sendKeys($.ESCAPE); + assert.equal(await gu.getVisibleDetailCells('A', [1]), 'barfoo'); + assert.equal(await gu.getVisibleDetailCells('B', [1]), 'A1'); + assert.equal(await gu.getVisibleDetailCells('C', [1]), 'A1'); + assert.equal(await gu.getVisibleDetailCells('D', [1]), 'A1'); + await paste(cb); + }); + await gu.waitForServer(); + + // Only the first element of the area paste should be pasted + await driver.wait(async () => (await gu.getVisibleDetailCells('A', [1]))[0] === 'topleft', 1000); + assert.equal(await gu.getVisibleDetailCells('B', [1]), 'A1'); + assert.equal(await gu.getVisibleDetailCells('C', [1]), 'A1'); + assert.equal(await gu.getVisibleDetailCells('D', [1]), 'A1'); + }); + + it("should allow copy of single cell to an external source", async function() { + await assert.equal(await $('#dummyText').val(), 'topleft\ttopright\nbottomleft\tbottomright'); + await gu.clickVisibleDetailCells('A', [1]); + await gu.sendKeys($.ESCAPE); + assert.equal(await gu.getVisibleDetailCells('A', [1]), 'topleft'); + await gu.withSafeClipboard(async (cb) => { + await copy(cb); + await $('#dummyText').val(''); + await $('#dummyText').click(); + await cb.paste(); + }); + await gu.waitForServer(); + assert.equal(await $('#dummyText').val(), 'topleft'); + }); + + it("should skip formula columns when pasting", async function() { + await gu.clickVisibleDetailCells('A', [1]); + await gu.sendKeys($.ESCAPE); + assert.equal(await gu.getVisibleDetailCells('A', [1]), 'topleft'); + + await gu.withSafeClipboard(async (cb) => { + await copy(cb); + await gu.clickVisibleDetailCells('H', [1]); + assert.equal(await gu.getVisibleDetailCells('H', [1]), 'F1G1'); + await paste(cb); + }); + await gu.waitForServer(); + + // Check that formula cell is unchanged + await driver.wait(async () => (await gu.getVisibleDetailCells('H', [1]))[0] === 'F1G1', 1000); + }); + }); + + describe(`GridView with ${method} commands`, function() { + before(async function() { + await gu.supportOldTimeyTestCode(); + await gu.useFixtureDoc(cleanup, "CopyPaste.grist", true); + await gu.toggleSidePanel('left', 'close'); + + await driver.executeScript(createDummyTextArea); + }); + + after(async function() { + await driver.executeScript(removeDummyTextArea); + }); + + it("should allow copy and paste of single cells", async function() { + await gu.clickCellRC(0, 0); + assert.equal(await gu.getCellRC(0, 0).text(), 'A1'); + + await gu.withSafeClipboard(async (cb) => { + await copy(cb); + await gu.clickCellRC(0, 1); + assert.equal(await gu.getCellRC(0, 1).text(), 'B1'); + + await paste(cb); + }); + await gu.waitForServer(); + + // Check that cell was copied + await driver.wait(async () => await gu.getCellRC(0, 1).text() === 'A1', 1000); + // Check that original is unchanged + assert.equal(await gu.getCellRC(0, 0).text(), 'A1'); + }); + + it("should allow multiple pastes after a single copy", async function() { + await gu.clickCellRC(0, 0); + assert.equal(await gu.getCellRC(0, 1).text(), 'A1'); + await gu.withSafeClipboard(async (cb) => { + await copy(cb); + await gu.clickCellRC(6, 0); + assert.equal(await gu.getCellRC(6, 0).text(), 'A7'); + await paste(cb); + await driver.wait(async () => await gu.getCellRC(6, 0).text() === 'A1', 1000); + await gu.clickCellRC(6, 1); + assert.equal(await gu.getCellRC(6, 1).text(), 'B7'); + await paste(cb); + }); + await gu.waitForServer(); + await driver.wait(async () => await gu.getCellRC(6, 1).text() === 'A1', 1000); + }); + + it("should allow copy and paste of multiple cells", async function() { + await gu.clickCellRC(1, 0); + assert.equal(await gu.getCellRC(1, 0).text(), 'A2'); + assert.equal(await gu.getCellRC(1, 1).text(), 'B2'); + assert.equal(await gu.getCellRC(2, 0).text(), 'A3'); + assert.equal(await gu.getCellRC(2, 1).text(), 'B3'); + + await gu.sendKeys([$.SHIFT, $.RIGHT], [$.SHIFT, $.DOWN]); + await gu.withSafeClipboard(async (cb) => { + await copy(cb); + await gu.clickCellRC(3, 0); + assert.equal(await gu.getCellRC(3, 0).text(), 'A4'); + assert.equal(await gu.getCellRC(3, 1).text(), 'B4'); + assert.equal(await gu.getCellRC(4, 0).text(), 'A5'); + assert.equal(await gu.getCellRC(4, 1).text(), 'B5'); + + await paste(cb); + }); + await gu.waitForServer(); + + // Check that cells were copied + await driver.wait(async () => await gu.getCellRC(3, 0).text() === 'A2', 1000); + assert.equal(await gu.getCellRC(3, 1).text(), 'B2'); + assert.equal(await gu.getCellRC(4, 0).text(), 'A3'); + assert.equal(await gu.getCellRC(4, 1).text(), 'B3'); + // Check that original cells are unchanged + assert.equal(await gu.getCellRC(1, 0).text(), 'A2'); + assert.equal(await gu.getCellRC(1, 1).text(), 'B2'); + assert.equal(await gu.getCellRC(2, 0).text(), 'A3'); + assert.equal(await gu.getCellRC(2, 1).text(), 'B3'); + }); + + it("should allow paste of single cell from an external source", async function() { + // Copy from dummy text area to simulate a copy from outside of grist + await $('#dummyText').val('foobar').resolve(); + await $('#dummyText').sendKeys($.SELECT_ALL); + await gu.withSafeClipboard(async (cb) => { + await cb.copy(); + + // Then paste + await gu.clickCellRC(0, 0); + assert.equal(await gu.getCellRC(0, 0).text(), 'A1'); + await paste(cb); + }); + await gu.waitForServer(); + await driver.wait(async () => await gu.getCellRC(0, 0).text() === 'foobar', 1000); + }); + + it("should allow paste of multiple cells from an external source", async function() { + // Copy from dummy text area to simulate a copy from outside of grist + await $('#dummyText').val('topleft\ttopright\nbottomleft\tbottomright').resolve(); + await $('#dummyText').click(); + await gu.sendKeys($.SELECT_ALL_LINES(2)); + await gu.withSafeClipboard(async (cb) => { + await cb.copy(); + + // Then paste + await gu.clickCellRC(0, 2); + assert.equal(await gu.getCellRC(0, 2).text(), 'C1'); + assert.equal(await gu.getCellRC(0, 3).text(), 'D1'); + assert.equal(await gu.getCellRC(1, 2).text(), 'C2'); + assert.equal(await gu.getCellRC(1, 3).text(), 'D2'); + await paste(cb); + }); + await gu.waitForServer(); + + await driver.wait(async () => await gu.getCellRC(0, 2).text() === 'topleft', 1000); + assert.equal(await gu.getCellRC(0, 3).text(), 'topright'); + assert.equal(await gu.getCellRC(1, 2).text(), 'bottomleft'); + assert.equal(await gu.getCellRC(1, 3).text(), 'bottomright'); + }); + + it("should allow copy of single cell to an external source", async function() { + assert.equal(await $('#dummyText').val(), 'topleft\ttopright\nbottomleft\tbottomright'); + await gu.clickCellRC(0, 0); + assert.equal(await gu.getCellRC(0, 0).text(), 'foobar'); + await gu.withSafeClipboard(async (cb) => { + await copy(cb); + await $('#dummyText').val(''); + await $('#dummyText').click(); + await cb.paste(); + }); + await gu.waitForServer(); + assert.equal(await $('#dummyText').val(), 'foobar'); + }); + + it("should allow copy of multiple cells to an external source", async function() { + assert.equal(await $('#dummyText').val(), 'foobar'); + await gu.clickCellRC(1, 0); + await gu.sendKeys([$.SHIFT, $.RIGHT], [$.SHIFT, $.DOWN]); + await gu.withSafeClipboard(async (cb) => { + await copy(cb); + assert.equal(await gu.getCellRC(1, 0).text(), 'A2'); + assert.equal(await gu.getCellRC(1, 1).text(), 'B2'); + assert.equal(await gu.getCellRC(2, 0).text(), 'A3'); + assert.equal(await gu.getCellRC(2, 1).text(), 'B3'); + + await $('#dummyText').val(''); + await $('#dummyText').click(); + await cb.paste(); + }); + await gu.waitForServer(); + assert.equal(await $('#dummyText').val(), 'A2\tB2\nA3\tB3'); + }); + + it.skip("should correctly copy and paste between differently typed columns", async function() { + //TODO: write tests for copy and pasting between differently typed columns + }); + + it("should correctly paste cells containing new-lines", async function() { + const mapper = e => e.getAttribute('textContent'); // preserve whitespace + // try to paste some external text with a new line in a cell + await $('#dummyText').val('A\nB\rC\r\n"D\tE""F\'G\nH\n"').resolve(); + await $('#dummyText').click(); + await gu.sendKeys($.SELECT_ALL_LINES(5)); + await gu.withSafeClipboard(async (cb) => { + await cb.copy(); + assert.deepEqual(await gu.getGridValues({ rowNums: [4, 5, 6, 7, 8], cols: [1], mapper}), + ['B2', 'B3', 'B6', 'A1', '']); + await gu.clickCellRC(3, 1); + await paste(cb); + }); + await gu.waitForServer(); + + await driver.wait(async () => await gu.getCellRC(3, 1).text() === 'A', 1000); + assert.deepEqual(await gu.getGridValues({ rowNums: [4, 5, 6, 7, 8], cols: [1], mapper }), + ['A', 'B', 'C', "D\tE\"F'G\nH\n", '']); + + // Also try a to paste a new line containing cell within grist + await gu.clickCellRC(3, 1); + await gu.selectGridArea([4, 1], [7, 1]); + await gu.withSafeClipboard(async (cb) => { + await copy(cb); + await gu.clickCellRC(3, 2); + assert.deepEqual(await gu.getGridValues({ rowNums: [4, 5, 6, 7, 8], cols: [2], mapper }), + ['C4', 'C5', 'C6', 'C7', '']); + await paste(cb); + }); + await gu.waitForServer(); + + await driver.wait(async () => await gu.getCellRC(3, 2).text() === 'A', 1000); + assert.deepEqual(await gu.getGridValues({ rowNums: [4, 5, 6, 7, 8], cols: [2], mapper }), + ['A', 'B', 'C', "D\tE\"F'G\nH\n", '']); + }); + + it("should allow cut and paste of single cells", async function() { + await gu.clickCellRC(2, 2); + assert.equal(await gu.getCellRC(2, 2).text(), 'C3'); + + await gu.withSafeClipboard(async (cb) => { + await cut(cb); + await gu.clickCellRC(2, 3); + assert.equal(await gu.getCellRC(2, 3).text(), 'D3'); + await paste(cb); + }); + await gu.waitForServer(); + + // Check that cell was copied + await driver.wait(async () => await gu.getCellRC(2, 3).text() === 'C3', 1000); + // Check that original is now blank + assert.equal(await gu.getCellRC(2, 2).text(), ''); + // Check that cut works with a blank cell + await gu.clickCellRC(2, 2); + await gu.withSafeClipboard(async (cb) => { + await cut(cb); + await gu.clickCellRC(5, 3); + assert.equal(await gu.getCellRC(5, 3).text(), 'D6'); + await paste(cb); + }); + await gu.waitForServer(); + + await driver.wait(async () => await gu.getCellRC(5, 3).text() === '', 1000); + assert.equal(await gu.getCellRC(2, 2).text(), ''); + }); + + it("should allow cut and paste of multiple cells", async function() { + const mapper = e => e.getAttribute('textContent'); + await gu.clickCellRC(3, 2); + assert.deepEqual(await gu.getGridValues({ rowNums: [4, 5], cols: [2, 3] }), + ['A', 'D4', 'B', 'D5']); + + await gu.sendKeys([$.SHIFT, $.RIGHT], [$.SHIFT, $.DOWN]); + await gu.withSafeClipboard(async (cb) => { + await cut(cb); + await gu.clickCellRC(5, 2); + assert.deepEqual(await gu.getGridValues({ rowNums: [6, 7], cols: [2, 3], mapper}), + ['C', '', "D\tE\"F'G\nH\n", "D7"]); + await paste(cb); + }); + await gu.waitForServer(); + + // Check that cells were copied + await driver.wait(async () => await gu.getCellRC(5, 2).text() === 'A', 1000); + assert.deepEqual(await gu.getGridValues({ rowNums: [6, 7], cols: [2, 3] }), + ['A', 'D4', 'B', "D5"]); + // Check that original cells are now blank + assert.deepEqual(await gu.getGridValues({ rowNums: [4, 5], cols: [2, 3] }), + ['', '', '', '']); + }); + + it("should not allow multiple pastes after a single cut", async function() { + await gu.clickCellRC(5, 2); + assert.equal(await gu.getCellRC(5, 2).text(), 'A'); + await gu.withSafeClipboard(async (cb) => { + await cut(cb); + await gu.clickCellRC(4, 2); + assert.equal(await gu.getCellRC(4, 2).text(), ''); + await paste(cb); + await gu.waitForServer(); + await driver.wait(async () => await gu.getCellRC(4, 2).text() === 'A', 1000); + // There should be no paste the second time + await gu.clickCellRC(3, 2); + assert.equal(await gu.getCellRC(3, 2).text(), ''); + await paste(cb); + }); + await gu.waitForServer(); + await driver.wait(async () => await gu.getCellRC(3, 2).text() === '', 1000); + }); + + it("should allow copy and paste of single cells into a multi cell selection", async function() { + // Copy one cell + await gu.clickCellRC(0, 0); + assert.equal(await gu.getCellRC(0, 0).text(), 'foobar'); + await gu.withSafeClipboard(async (cb) => { + await copy(cb); + assert.equal(await gu.getCellRC(1, 0).text(), 'A2'); + assert.equal(await gu.getCellRC(1, 1).text(), 'B2'); + assert.equal(await gu.getCellRC(2, 0).text(), 'A3'); + assert.equal(await gu.getCellRC(2, 1).text(), 'B3'); + // Select an area and paste + await gu.clickCellRC(1, 0); + await gu.sendKeys([$.SHIFT, $.RIGHT], [$.SHIFT, $.DOWN]); + await paste(cb); + await gu.waitForServer(); + + // Make sure all cells are replaced by copied cell + await driver.wait(async () => await gu.getCellRC(1, 0).text() === 'foobar', 1000); + assert.equal(await gu.getCellRC(1, 1).text(), 'foobar'); + assert.equal(await gu.getCellRC(2, 0).text(), 'foobar'); + assert.equal(await gu.getCellRC(2, 1).text(), 'foobar'); + + // Finally check that the direction the cells are selected does not affect thet outcome + await gu.clickCellRC(6, 5); + assert.equal(await gu.getCellRC(5, 4).text(), 'E6'); + assert.equal(await gu.getCellRC(5, 5).text(), 'F6'); + assert.equal(await gu.getCellRC(6, 4).text(), 'E7'); + assert.equal(await gu.getCellRC(6, 5).text(), 'F7'); + await gu.sendKeys([$.SHIFT, $.LEFT], [$.SHIFT, $.UP]); + await paste(cb); + }); + await gu.waitForServer(); + + await driver.wait(async () => await gu.getCellRC(5, 4).text() === 'foobar', 1000); + assert.equal(await gu.getCellRC(5, 5).text(), 'foobar'); + assert.equal(await gu.getCellRC(6, 4).text(), 'foobar'); + assert.equal(await gu.getCellRC(6, 5).text(), 'foobar'); + }); + + it("should allow copy and paste of multiple cells into a multi cell selection", async function() { + // Copy an 2x2 area + await gu.getCellRC(0, 2).click(); + assert.equal(await gu.getCellRC(0, 2).text(), 'topleft'); + assert.equal(await gu.getCellRC(0, 3).text(), 'topright'); + assert.equal(await gu.getCellRC(1, 2).text(), 'bottomleft'); + assert.equal(await gu.getCellRC(1, 3).text(), 'bottomright'); + await gu.sendKeys([$.SHIFT, $.RIGHT], [$.SHIFT, $.DOWN]); + await gu.withSafeClipboard(async (cb) => { + await copy(cb); + await gu.clickCellRC(1, 0); + // Select an 4x4 area, expect that a paste will result in 4 duplications of the copied area + await gu.sendKeys([$.SHIFT, $.RIGHT], [$.SHIFT, $.RIGHT], [$.SHIFT, $.RIGHT], + [$.SHIFT, $.DOWN], [$.SHIFT, $.DOWN], [$.SHIFT, $.DOWN]); + await paste(cb); + await gu.waitForServer(); + // First pasted row + await driver.wait(async () => await gu.getCellRC(1, 0).text() === 'topleft', 1000); + assert.equal(await gu.getCellRC(1, 1).text(), 'topright'); + assert.equal(await gu.getCellRC(1, 2).text(), 'topleft'); + assert.equal(await gu.getCellRC(1, 3).text(), 'topright'); + // Second pasted row + assert.equal(await gu.getCellRC(2, 0).text(), 'bottomleft'); + assert.equal(await gu.getCellRC(2, 1).text(), 'bottomright'); + assert.equal(await gu.getCellRC(2, 2).text(), 'bottomleft'); + assert.equal(await gu.getCellRC(2, 3).text(), 'bottomright'); + // Third pasted row + assert.equal(await gu.getCellRC(3, 0).text(), 'topleft'); + assert.equal(await gu.getCellRC(3, 1).text(), 'topright'); + assert.equal(await gu.getCellRC(3, 2).text(), 'topleft'); + assert.equal(await gu.getCellRC(3, 3).text(), 'topright'); + // Fourth pasted row + assert.equal(await gu.getCellRC(4, 0).text(), 'bottomleft'); + assert.equal(await gu.getCellRC(4, 1).text(), 'bottomright'); + assert.equal(await gu.getCellRC(4, 2).text(), 'bottomleft'); + assert.equal(await gu.getCellRC(4, 3).text(), 'bottomright'); + + //Now check that if a paste pattern does not fit in the selected area it fits as much as it can + await gu.clickCellRC(0, 2); + // Select an 5x5 area, expect that a paste will result in 4 duplications of the copied area + await gu.sendKeys([$.SHIFT, $.RIGHT], [$.SHIFT, $.RIGHT], [$.SHIFT, $.RIGHT], [$.SHIFT, $.RIGHT], + [$.SHIFT, $.DOWN], [$.SHIFT, $.DOWN], [$.SHIFT, $.DOWN], [$.SHIFT, $.DOWN]); + await paste(cb); + }); + await gu.waitForServer(); + + // First pasted row + await driver.wait(async () => await gu.getCellRC(0, 2).text() === 'topleft', 1000); + assert.equal(await gu.getCellRC(0, 3).text(), 'topright'); + assert.equal(await gu.getCellRC(0, 4).text(), 'topleft'); + assert.equal(await gu.getCellRC(0, 5).text(), 'topright'); + assert.equal(await gu.getCellRC(0, 6).text(), 'G1'); + // Second pasted row + assert.equal(await gu.getCellRC(1, 2).text(), 'bottomleft'); + assert.equal(await gu.getCellRC(1, 3).text(), 'bottomright'); + assert.equal(await gu.getCellRC(1, 4).text(), 'bottomleft'); + assert.equal(await gu.getCellRC(1, 5).text(), 'bottomright'); + assert.equal(await gu.getCellRC(1, 6).text(), 'G2'); + // Third pasted row + assert.equal(await gu.getCellRC(2, 2).text(), 'topleft'); + assert.equal(await gu.getCellRC(2, 3).text(), 'topright'); + assert.equal(await gu.getCellRC(2, 4).text(), 'topleft'); + assert.equal(await gu.getCellRC(2, 5).text(), 'topright'); + assert.equal(await gu.getCellRC(2, 6).text(), 'G3'); + // Fourth pasted row + assert.equal(await gu.getCellRC(3, 2).text(), 'bottomleft'); + assert.equal(await gu.getCellRC(3, 3).text(), 'bottomright'); + assert.equal(await gu.getCellRC(3, 4).text(), 'bottomleft'); + assert.equal(await gu.getCellRC(3, 5).text(), 'bottomright'); + assert.equal(await gu.getCellRC(3, 6).text(), 'G4'); + // Last unchanged row + assert.equal(await gu.getCellRC(4, 2).text(), 'bottomleft'); + assert.equal(await gu.getCellRC(4, 3).text(), 'bottomright'); + assert.equal(await gu.getCellRC(4, 4).text(), 'E5'); + assert.equal(await gu.getCellRC(4, 5).text(), 'F5'); + assert.equal(await gu.getCellRC(4, 6).text(), 'G5'); + }); + + it("should allow fill down in a single column selection", async function() { + await gu.clickCellRC(0, 6); + assert.equal(await gu.getCellRC(0, 6).text(), 'G1'); + assert.equal(await gu.getCellRC(1, 6).text(), 'G2'); + assert.equal(await gu.getCellRC(2, 6).text(), 'G3'); + assert.equal(await gu.getCellRC(3, 6).text(), 'G4'); + assert.equal(await gu.getCellRC(4, 6).text(), 'G5'); + assert.equal(await gu.getCellRC(5, 6).text(), 'G6'); + assert.equal(await gu.getCellRC(6, 6).text(), 'G7'); + + await $('.column_name.selected').click(); + await gu.sendKeys([$.MOD, 'd']); + await gu.waitForServer(); + assert.equal(await gu.getCellRC(0, 6).text(), 'G1'); + assert.equal(await gu.getCellRC(1, 6).text(), 'G1'); + assert.equal(await gu.getCellRC(2, 6).text(), 'G1'); + assert.equal(await gu.getCellRC(3, 6).text(), 'G1'); + assert.equal(await gu.getCellRC(4, 6).text(), 'G1'); + assert.equal(await gu.getCellRC(5, 6).text(), 'G1'); + assert.equal(await gu.getCellRC(6, 6).text(), 'G1'); + }); + + it("should allow fill down in a multiple column selection", async function() { + await gu.clickCellRC(4, 4); + assert.equal(await gu.getCellRC(4, 4).text(), 'E5'); + assert.equal(await gu.getCellRC(5, 4).text(), 'foobar'); + assert.equal(await gu.getCellRC(6, 4).text(), 'foobar'); + assert.equal(await gu.getCellRC(4, 5).text(), 'F5'); + assert.equal(await gu.getCellRC(5, 5).text(), 'foobar'); + assert.equal(await gu.getCellRC(6, 5).text(), 'foobar'); + + await gu.sendKeys([$.SHIFT, $.RIGHT], [$.SHIFT, $.DOWN], [$.SHIFT, $.DOWN], [$.MOD, 'd']); + await gu.waitForServer(); + assert.equal(await gu.getCellRC(4, 4).text(), 'E5'); + assert.equal(await gu.getCellRC(5, 4).text(), 'E5'); + assert.equal(await gu.getCellRC(6, 4).text(), 'E5'); + assert.equal(await gu.getCellRC(4, 5).text(), 'F5'); + assert.equal(await gu.getCellRC(5, 5).text(), 'F5'); + assert.equal(await gu.getCellRC(6, 5).text(), 'F5'); + }); + + it("should not fill down in formula columns", async function() { + await gu.clickCellRC(0, 8); + assert.equal(await gu.getCellRC(0, 8).text(), 'I1'); + assert.equal(await gu.getCellRC(1, 8).text(), 'I2'); + assert.equal(await gu.getCellRC(2, 8).text(), 'I3'); + assert.equal(await gu.getCellRC(0, 7).text(), 'toprightG1'); + assert.equal(await gu.getCellRC(1, 7).text(), 'bottomrightG1'); + assert.equal(await gu.getCellRC(2, 7).text(), 'toprightG1'); + + await gu.sendKeys([$.SHIFT, $.LEFT], [$.SHIFT, $.DOWN], [$.SHIFT, $.DOWN], [$.MOD, 'd']); + await gu.waitForServer(); + + assert.equal(await gu.getCellRC(0, 8).text(), 'I1'); + assert.equal(await gu.getCellRC(1, 8).text(), 'I1'); + assert.equal(await gu.getCellRC(2, 8).text(), 'I1'); + assert.equal(await gu.getCellRC(0, 7).text(), 'toprightG1'); + assert.equal(await gu.getCellRC(1, 7).text(), 'bottomrightG1'); + assert.equal(await gu.getCellRC(2, 7).text(), 'toprightG1'); + }); + + it.skip("should add columns when pasted data is larger than available space", async function() { + // TODO: Currently new columns are not added after a paste, instead there is an unhandled error + await gu.clickCellRC(0, 0); + await gu.sendKeys([$.SHIFT, $.RIGHT], [$.SHIFT, $.RIGHT]); + await gu.withSafeClipboard(async (cb) => { + await copy(cb); + await gu.clickCellRC(0, 3); + assert.lengthOf(await $('.column_names .column_name').array(), 4); + await paste(cb); + }); + await gu.waitForServer(); + + await driver.wait(async () => await $('.column_names .column_name').array().length === 7, 1000); + }); + + it("should add rows when pasted data is larger than available space", async function() { + await gu.getCellRC(3, 0).scrollIntoView({inline: 'end'}); + await gu.clickCellRC(3, 0); + await gu.sendKeys([$.SHIFT, $.DOWN], [$.SHIFT, $.DOWN]); + await gu.withSafeClipboard(async (cb) => { + await copy(cb); + assert.equal(await gu.getGridRowCount(), 8); // includes "Add Row" + await gu.clickCellRC(6, 0); + await paste(cb); + }); + await gu.waitForServer(); + + await driver.wait(async () => await gu.getGridRowCount() === 10, 1000); + + assert.equal(await gu.getCellRC(6, 0).text(), 'topleft'); + assert.equal(await gu.getCellRC(7, 0).text(), 'bottomleft'); + assert.equal(await gu.getCellRC(8, 0).text(), 'A6'); + }); + + it("should skip formula columns when pasting", async function() { + // Single Cell Pasting + await gu.clickCellRC(0, 0); + assert.equal(await gu.getCellRC(0, 0).text(), 'foobar'); + await gu.withSafeClipboard(async (cb) => { + await copy(cb); + // Paste into formula row + await gu.clickCellRC(0, 7); + assert.equal(await gu.getCellRC(0, 7).text(), 'toprightG1'); + await paste(cb); + }); + await gu.waitForServer(); + + // Nothing should change + await driver.wait(async () => await gu.getCellRC(0, 7).text() === 'toprightG1', 1000); + + await gu.clickCellRC(0, 0); + await gu.sendKeys([$.SHIFT, $.DOWN], [$.SHIFT, $.RIGHT], [$.SHIFT, $.RIGHT]); + await gu.withSafeClipboard(async (cb) => { + await copy(cb); + + // Copy a large area + assert.deepEqual(await gu.getGridValues({ rowNums: [1, 2], cols: [0, 1, 2] }), [ + 'foobar', 'A1', 'topleft', + 'topleft', 'topright', 'bottomleft' + ]); + + await gu.clickCellRC(6, 6); + assert.deepEqual(await gu.getGridValues({ rowNums: [7, 8], cols: [6, 7, 8] }), [ + 'G1', 'F5G1', 'I7', + '', '', '' + ]); + + // Now paste, we expect the columns on either side of the formula column + // to reflect the pasted values + await paste(cb); + }); + await gu.waitForServer(); + + // The contents of the middle formula column should be $F+$G not the value of what was pasted. + await driver.wait(async () => await gu.getCellRC(6, 6).text() === 'foobar', 1000); + assert.deepEqual(await gu.getGridValues({ rowNums: [7, 8], cols: [6, 7, 8] }), [ + 'foobar', 'F5foobar', 'topleft', + 'topleft', 'topleft', 'bottomleft' + ]); + + // make sure cut and paste works too + await gu.clickCellRC(5, 4); + await gu.sendKeys([$.SHIFT, $.DOWN], [$.SHIFT, $.RIGHT], [$.SHIFT, $.RIGHT]); + await gu.withSafeClipboard(async (cb) => { + await cut(cb); + + assert.deepEqual(await gu.getGridValues({ rowNums: [6, 7], cols: [4, 5, 6] }), + ['E5', 'F5', 'G1', 'E5', 'F5', 'foobar']); + await gu.clickCellRC(7, 6); + assert.deepEqual(await gu.getGridValues({ rowNums: [8, 9], cols: [6, 7, 8] }), + ['topleft', 'topleft', 'bottomleft', '', '', '']); + await paste(cb); + }); + await gu.waitForServer(); + + await driver.wait(async () => await gu.getCellRC(7, 6).text() === 'E5', 1000); + assert.deepEqual(await gu.getGridValues({ rowNums: [6, 7], cols: [4, 5, 6] }), + ['', '', '', '', '', '']); + assert.deepEqual(await gu.getGridValues({ rowNums: [8, 9], cols: [6, 7, 8] }), + ['E5', 'E5', 'G1', 'E5', 'E5', 'foobar']); + }); + + it("should not cut cells that are the target of a paste", async function() { + // Cut and Paste in place shouldn't change anything + await gu.clickCellRC(4, 6); + assert.equal(await gu.getCellRC(4, 6).text(), 'G1'); + await gu.withSafeClipboard(async (cb) => { + await cut(cb); + await paste(cb); + }); + await gu.waitForServer(); + await driver.wait(async () => await gu.getCellRC(4, 6).text() === 'G1', 1000); + + // A paste that overlaps a cut should only cut non-conflicting cells + assert.deepEqual(await gu.getGridValues({ rowNums: [4, 5], cols: [5, 6] }), + ['bottomright', 'G1', 'F5', 'G1']); + await gu.selectGridArea([4, 5], [5, 6]); + await gu.withSafeClipboard(async (cb) => { + await cut(cb); + await gu.clickCellRC(4, 4); + await paste(cb); + }); + await gu.waitForServer(); + + await driver.wait(async () => await gu.getCellRC(4, 4).text() === 'bottomright', 1000); + assert.deepEqual(await gu.getGridValues({ rowNums: [4, 5, 6], cols: [4, 5, 6] }), + ['bottomleft', '', '', 'bottomright', 'G1', '', 'F5', 'G1', '']); + }); + + it("should be possible to copy and paste entire tables", async function() { + assert.equal(await gu.getGridRowCount(), 10); + // This click is a work-around for bug T282. + await gu.clickCellRC(9, 1); + await gu.sendKeys($.SELECT_ALL); + await gu.withSafeClipboard(async (cb) => { + await cb.copy(); + await gu.clickCellRC(9, 0); + await paste(cb); + }); + await gu.waitForServer(); + await driver.wait(async () => await gu.getGridRowCount() === 19, 1000); + + // spot check to make sure it is an accurate copy + assert.equal(await gu.getCellRC(0, 7).text(), 'toprightG1'); + assert.equal(await gu.getCellRC(4, 4).text(), 'bottomright'); + assert.equal(await gu.getCellRC(8, 0).text(), 'A6'); + + assert.equal(await gu.getCellRC(9, 7).text(), 'toprightG1'); + assert.equal(await gu.getCellRC(13, 4).text(), 'bottomright'); + assert.equal(await gu.getCellRC(17, 0).text(), 'A6'); + }); + }); + } +});