diff --git a/test/integration/reorganize_pages_spec.mjs b/test/integration/reorganize_pages_spec.mjs index 3b4be61ff..018a92d30 100644 --- a/test/integration/reorganize_pages_spec.mjs +++ b/test/integration/reorganize_pages_spec.mjs @@ -23,6 +23,9 @@ import { getAnnotationSelector, getRect, getThumbnailSelector, + kbCopy, + kbCut, + kbDelete, loadAndWait, scrollIntoView, waitAndClick, @@ -841,4 +844,144 @@ describe("Reorganize Pages View", () => { ); }); }); + + describe("Keyboard shortcuts for cut and copy (bug 2018139)", () => { + let pages; + + beforeEach(async () => { + pages = await loadAndWait( + "page_with_number.pdf", + "#viewsManagerToggleButton", + "1", + null, + { enableSplitMerge: true } + ); + }); + + afterEach(async () => { + await closePages(pages); + }); + + it("should cut pages with Ctrl+X and paste them", async () => { + await Promise.all( + pages.map(async ([browserName, page]) => { + await waitForThumbnailVisible(page, 1); + await waitAndClick( + page, + `.thumbnail:has(${getThumbnailSelector(1)}) input` + ); + await waitAndClick( + page, + `.thumbnail:has(${getThumbnailSelector(3)}) input` + ); + + let handlePagesEdited = await waitForPagesEdited(page, "cut"); + await kbCut(page); + + let pageIndices = await awaitPromise(handlePagesEdited); + let expected = [2, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17]; + expect(pageIndices) + .withContext(`In ${browserName}`) + .toEqual(expected); + await waitForHavingContents(page, expected); + + handlePagesEdited = await waitForPagesEdited(page); + await waitAndClick(page, `${getThumbnailSelector(1)}+button`); + pageIndices = await awaitPromise(handlePagesEdited); + expected = [ + 2, 1, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, + ]; + expect(pageIndices) + .withContext(`In ${browserName}`) + .toEqual(expected); + await waitForHavingContents(page, expected); + }) + ); + }); + + it("should copy pages with Ctrl+C and paste them", async () => { + await Promise.all( + pages.map(async ([browserName, page]) => { + await waitForThumbnailVisible(page, 1); + await waitAndClick( + page, + `.thumbnail:has(${getThumbnailSelector(1)}) input` + ); + await waitAndClick( + page, + `.thumbnail:has(${getThumbnailSelector(3)}) input` + ); + + let handlePagesEdited = await waitForPagesEdited(page, "copy"); + await kbCopy(page); + + let pageIndices = await awaitPromise(handlePagesEdited); + let expected = [ + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, + ]; + expect(pageIndices) + .withContext(`In ${browserName}`) + .toEqual(expected); + await waitForHavingContents(page, expected); + + handlePagesEdited = await waitForPagesEdited(page); + await waitAndClick(page, `${getThumbnailSelector(2)}+button`); + pageIndices = await awaitPromise(handlePagesEdited); + expected = [ + 1, 2, 1, 3, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, + ]; + expect(pageIndices) + .withContext(`In ${browserName}`) + .toEqual(expected); + await waitForHavingContents(page, expected); + }) + ); + }); + }); + + describe("Keyboard shortcuts for delete (bug 2010831)", () => { + let pages; + + beforeEach(async () => { + pages = await loadAndWait( + "page_with_number.pdf", + "#viewsManagerToggleButton", + "1", + null, + { enableSplitMerge: true } + ); + }); + + afterEach(async () => { + await closePages(pages); + }); + + it("should delete pages with the Delete key", async () => { + await Promise.all( + pages.map(async ([browserName, page]) => { + await waitForThumbnailVisible(page, 1); + await waitAndClick( + page, + `.thumbnail:has(${getThumbnailSelector(1)}) input` + ); + await waitAndClick( + page, + `.thumbnail:has(${getThumbnailSelector(3)}) input` + ); + + const handlePagesEdited = await waitForPagesEdited(page); + await kbDelete(page); + + const pageIndices = await awaitPromise(handlePagesEdited); + const expected = [ + 2, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, + ]; + expect(pageIndices) + .withContext(`In ${browserName}`) + .toEqual(expected); + await waitForHavingContents(page, expected); + }) + ); + }); + }); }); diff --git a/test/integration/test_utils.mjs b/test/integration/test_utils.mjs index e571d2f63..6e561ac1a 100644 --- a/test/integration/test_utils.mjs +++ b/test/integration/test_utils.mjs @@ -702,6 +702,14 @@ async function kbCopy(page) { await page.keyboard.press("c", { commands: ["Copy"] }); await page.keyboard.up(modifier); } +async function kbCut(page) { + await page.keyboard.down(modifier); + await page.keyboard.press("x", { commands: ["Cut"] }); + await page.keyboard.up(modifier); +} +async function kbDelete(page) { + await page.keyboard.press("Delete"); +} async function kbPaste(page) { await page.keyboard.down(modifier); await page.keyboard.press("v", { commands: ["Paste"] }); @@ -997,6 +1005,9 @@ export { kbBigMoveLeft, kbBigMoveRight, kbBigMoveUp, + kbCopy, + kbCut, + kbDelete, kbDeleteLastWord, kbFocusNext, kbFocusPrevious, diff --git a/web/pdf_thumbnail_viewer.js b/web/pdf_thumbnail_viewer.js index 5619a0eef..e823979cb 100644 --- a/web/pdf_thumbnail_viewer.js +++ b/web/pdf_thumbnail_viewer.js @@ -884,6 +884,33 @@ class PDFThumbnailViewer { } // For checkboxes, let the default behavior handle toggling break; + case "c": + if ( + this.#enableSplitMerge && + (e.ctrlKey || e.metaKey) && + this.#selectedPages?.size + ) { + this.#copyPages(); + stopEvent(e); + } + break; + case "x": + if ( + this.#enableSplitMerge && + (e.ctrlKey || e.metaKey) && + this.#selectedPages?.size + ) { + this.#cutPages(); + stopEvent(e); + } + break; + case "Delete": + case "Backspace": + if (this.#enableSplitMerge && this.#selectedPages?.size) { + this.#deletePages(); + stopEvent(e); + } + break; } }); this.container.addEventListener("click", e => {