/* Copyright 2022 Mozilla Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ const { closePages, editorPrefix, getSelectedEditors, loadAndWait, } = require("./test_utils.js"); describe("Editor", () => { describe("FreeText", () => { let pages; beforeAll(async () => { pages = await loadAndWait("aboutstacks.pdf", ".annotationEditorLayer"); }); afterAll(async () => { await closePages(pages); }); it("must write a string in a FreeText editor", async () => { await Promise.all( pages.map(async ([browserName, page]) => { await page.click("#editorFreeText"); const rect = await page.$eval(".annotationEditorLayer", el => { // With Chrome something is wrong when serializing a DomRect, // hence we extract the values and just return them. const { x, y } = el.getBoundingClientRect(); return { x, y }; }); const data = "Hello PDF.js World !!"; await page.mouse.click(rect.x + 100, rect.y + 100); await page.type(`${editorPrefix}0 .internal`, data); const editorRect = await page.$eval(`${editorPrefix}0`, el => { const { x, y, width, height } = el.getBoundingClientRect(); return { x, y, width, height, }; }); // Commit. await page.mouse.click( editorRect.x, editorRect.y + 2 * editorRect.height ); const content = await page.$eval(`${editorPrefix}0`, el => el.innerText.trimEnd() ); expect(content).withContext(`In ${browserName}`).toEqual(data); }) ); }); it("must copy/paste", async () => { await Promise.all( pages.map(async ([browserName, page]) => { const editorRect = await page.$eval(`${editorPrefix}0`, el => { const { x, y, width, height } = el.getBoundingClientRect(); return { x, y, width, height }; }); // Select the editor created previously. await page.mouse.click( editorRect.x + editorRect.width / 2, editorRect.y + editorRect.height / 2 ); await page.keyboard.down("Control"); await page.keyboard.press("c"); await page.keyboard.up("Control"); await page.keyboard.down("Control"); await page.keyboard.press("v"); await page.keyboard.up("Control"); const content = await page.$eval(`${editorPrefix}0`, el => el.innerText.trimEnd() ); let pastedContent = await page.$eval(`${editorPrefix}1`, el => el.innerText.trimEnd() ); expect(pastedContent) .withContext(`In ${browserName}`) .toEqual(content); await page.keyboard.down("Control"); await page.keyboard.press("c"); await page.keyboard.up("Control"); await page.keyboard.down("Control"); await page.keyboard.press("v"); await page.keyboard.up("Control"); pastedContent = await page.$eval(`${editorPrefix}2`, el => el.innerText.trimEnd() ); expect(pastedContent) .withContext(`In ${browserName}`) .toEqual(content); }) ); }); it("must clear all", async () => { await Promise.all( pages.map(async ([browserName, page]) => { await page.keyboard.down("Control"); await page.keyboard.press("a"); await page.keyboard.up("Control"); await page.keyboard.down("Control"); await page.keyboard.press("Backspace"); await page.keyboard.up("Control"); for (const n of [0, 1, 2]) { const hasEditor = await page.evaluate(sel => { return !!document.querySelector(sel); }, `${editorPrefix}${n}`); expect(hasEditor).withContext(`In ${browserName}`).toEqual(false); } }) ); }); it("must check that a paste has been undone", async () => { await Promise.all( pages.map(async ([browserName, page]) => { const rect = await page.$eval(".annotationEditorLayer", el => { const { x, y } = el.getBoundingClientRect(); return { x, y }; }); const data = "Hello PDF.js World !!"; await page.mouse.click(rect.x + 100, rect.y + 100); await page.type(`${editorPrefix}3 .internal`, data); const editorRect = await page.$eval(`${editorPrefix}3`, el => { const { x, y, width, height } = el.getBoundingClientRect(); return { x, y, width, height }; }); // Commit. await page.mouse.click( editorRect.x, editorRect.y + 2 * editorRect.height ); // And select it again. await page.mouse.click( editorRect.x + editorRect.width / 2, editorRect.y + editorRect.height / 2 ); await page.keyboard.down("Control"); await page.keyboard.press("c"); await page.keyboard.up("Control"); await page.keyboard.down("Control"); await page.keyboard.press("v"); await page.keyboard.up("Control"); let hasEditor = await page.evaluate(sel => { return !!document.querySelector(sel); }, `${editorPrefix}4`); expect(hasEditor).withContext(`In ${browserName}`).toEqual(true); await page.keyboard.down("Control"); await page.keyboard.press("z"); await page.keyboard.up("Control"); hasEditor = await page.evaluate(sel => { return !!document.querySelector(sel); }, `${editorPrefix}4`); expect(hasEditor).withContext(`In ${browserName}`).toEqual(false); for (let i = 0; i < 2; i++) { await page.keyboard.down("Control"); await page.keyboard.press("v"); await page.keyboard.up("Control"); } let length = await page.evaluate(sel => { return document.querySelectorAll(sel).length; }, `${editorPrefix}5, ${editorPrefix}6`); expect(length).withContext(`In ${browserName}`).toEqual(2); for (let i = 0; i < 2; i++) { await page.keyboard.down("Control"); await page.keyboard.press("z"); await page.keyboard.up("Control"); } length = await page.evaluate(sel => { return document.querySelectorAll(sel).length; }, `${editorPrefix}5, ${editorPrefix}6`); expect(length).withContext(`In ${browserName}`).toEqual(0); }) ); }); it("must check that aria-owns is correct", async () => { await Promise.all( pages.map(async ([browserName, page]) => { const [stacksRect, oldAriaOwns] = await page.$eval( ".textLayer", el => { for (const span of el.querySelectorAll( `span[role="presentation"]` )) { if (span.innerText.includes("Stacks are simple to create")) { span.setAttribute("pdfjs", true); const { x, y, width, height } = span.getBoundingClientRect(); return [ { x, y, width, height }, span.getAttribute("aria-owns"), ]; } } return null; } ); expect(oldAriaOwns).withContext(`In ${browserName}`).toEqual(null); const data = "Hello PDF.js World !!"; await page.mouse.click( stacksRect.x + stacksRect.width + 1, stacksRect.y + stacksRect.height / 2 ); await page.type(`${editorPrefix}7 .internal`, data); // Commit. await page.keyboard.press("Escape"); const ariaOwns = await page.$eval(".textLayer", el => { const span = el.querySelector(`span[pdfjs="true"]`); return span?.getAttribute("aria-owns") || null; }); expect(ariaOwns) .withContext(`In ${browserName}`) .toEqual(`${editorPrefix}7-editor`.slice(1)); }) ); }); it("must check that right click doesn't select", async () => { await Promise.all( pages.map(async ([browserName, page]) => { const rect = await page.$eval(".annotationEditorLayer", el => { const { x, y } = el.getBoundingClientRect(); return { x, y }; }); await page.keyboard.down("Control"); await page.keyboard.press("a"); await page.keyboard.up("Control"); await page.keyboard.down("Control"); await page.keyboard.press("Backspace"); await page.keyboard.up("Control"); const data = "Hello PDF.js World !!"; await page.mouse.click(rect.x + 100, rect.y + 100); await page.type(`${editorPrefix}8 .internal`, data); const editorRect = await page.$eval(`${editorPrefix}8`, el => { const { x, y, width, height } = el.getBoundingClientRect(); return { x, y, width, height }; }); // Commit. await page.keyboard.press("Escape"); expect(await getSelectedEditors(page)) .withContext(`In ${browserName}`) .toEqual([8]); await page.keyboard.press("Escape"); expect(await getSelectedEditors(page)) .withContext(`In ${browserName}`) .toEqual([]); await page.mouse.click( editorRect.x + editorRect.width / 2, editorRect.y + editorRect.height / 2 ); expect(await getSelectedEditors(page)) .withContext(`In ${browserName}`) .toEqual([8]); // Escape. await page.keyboard.press("Escape"); expect(await getSelectedEditors(page)) .withContext(`In ${browserName}`) .toEqual([]); // TODO: uncomment that stuff once we've a way to dismiss // the context menu. /* await page.mouse.click( editorRect.x + editorRect.width / 2, editorRect.y + editorRect.height / 2, { button: "right" } ); */ }) ); }); }); describe("FreeText (multiselection)", () => { let pages; beforeAll(async () => { pages = await loadAndWait("aboutstacks.pdf", ".annotationEditorLayer"); }); afterAll(async () => { await closePages(pages); }); it("must select/unselect several editors and check copy, paste and delete operations", async () => { await Promise.all( pages.map(async ([browserName, page]) => { await page.click("#editorFreeText"); const rect = await page.$eval(".annotationEditorLayer", el => { // With Chrome something is wrong when serializing a DomRect, // hence we extract the values and just return them. const { x, y } = el.getBoundingClientRect(); return { x, y }; }); const editorCenters = []; for (let i = 0; i < 4; i++) { const data = `FreeText ${i}`; await page.mouse.click( rect.x + (i + 1) * 100, rect.y + (i + 1) * 100 ); await page.type(`${editorPrefix}${i} .internal`, data); const editorRect = await page.$eval(`${editorPrefix}${i}`, el => { const { x, y, width, height } = el.getBoundingClientRect(); return { x, y, width, height, }; }); editorCenters.push({ x: editorRect.x + editorRect.width / 2, y: editorRect.y + editorRect.height / 2, }); // Commit. await page.mouse.click( editorRect.x, editorRect.y + 2 * editorRect.height ); } await page.keyboard.down("Control"); await page.keyboard.press("a"); await page.keyboard.up("Control"); expect(await getSelectedEditors(page)) .withContext(`In ${browserName}`) .toEqual([0, 1, 2, 3]); await page.keyboard.down("Control"); await page.mouse.click(editorCenters[1].x, editorCenters[1].y); expect(await getSelectedEditors(page)) .withContext(`In ${browserName}`) .toEqual([0, 2, 3]); await page.mouse.click(editorCenters[2].x, editorCenters[2].y); expect(await getSelectedEditors(page)) .withContext(`In ${browserName}`) .toEqual([0, 3]); await page.mouse.click(editorCenters[1].x, editorCenters[1].y); await page.keyboard.up("Control"); expect(await getSelectedEditors(page)) .withContext(`In ${browserName}`) .toEqual([0, 1, 3]); await page.keyboard.down("Control"); await page.keyboard.press("c"); await page.keyboard.up("Control"); await page.keyboard.down("Control"); await page.keyboard.press("v"); await page.keyboard.up("Control"); // 0,1,3 are unselected and new pasted editors are selected. expect(await getSelectedEditors(page)) .withContext(`In ${browserName}`) .toEqual([4, 5, 6]); // No ctrl here, hence all are unselected and 2 is selected. await page.mouse.click(editorCenters[2].x, editorCenters[2].y); expect(await getSelectedEditors(page)) .withContext(`In ${browserName}`) .toEqual([2]); await page.mouse.click(editorCenters[1].x, editorCenters[1].y); expect(await getSelectedEditors(page)) .withContext(`In ${browserName}`) .toEqual([1]); await page.keyboard.down("Control"); await page.mouse.click(editorCenters[3].x, editorCenters[3].y); expect(await getSelectedEditors(page)) .withContext(`In ${browserName}`) .toEqual([1, 3]); await page.keyboard.up("Control"); // Delete 1 and 3. await page.keyboard.press("Backspace"); await page.keyboard.down("Control"); await page.keyboard.press("a"); await page.keyboard.up("Control"); expect(await getSelectedEditors(page)) .withContext(`In ${browserName}`) .toEqual([0, 2, 4, 5, 6]); // Create an empty editor. await page.mouse.click(rect.x + 700, rect.y + 100); expect(await getSelectedEditors(page)) .withContext(`In ${browserName}`) .toEqual([7]); // Set the focus to 2 and check that only 2 is selected. await page.mouse.click(editorCenters[2].x, editorCenters[2].y); expect(await getSelectedEditors(page)) .withContext(`In ${browserName}`) .toEqual([2]); // Create an empty editor. await page.mouse.click(rect.x + 700, rect.y + 100); expect(await getSelectedEditors(page)) .withContext(`In ${browserName}`) .toEqual([8]); // Dismiss it. await page.keyboard.press("Escape"); // Select all. await page.keyboard.down("Control"); await page.keyboard.press("a"); await page.keyboard.up("Control"); // Check that all the editors are correctly selected (and the focus // didn't move to the body when the empty editor was removed). expect(await getSelectedEditors(page)) .withContext(`In ${browserName}`) .toEqual([0, 2, 4, 5, 6]); }) ); }); }); });