2023-07-06 15:32:11 +02:00
|
|
|
/* 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.
|
|
|
|
*/
|
|
|
|
|
2023-10-12 13:16:58 +02:00
|
|
|
import {
|
2024-06-19 17:20:42 +02:00
|
|
|
applyFunctionToEditor,
|
2023-12-06 15:27:31 +01:00
|
|
|
awaitPromise,
|
2025-02-23 18:52:19 +01:00
|
|
|
clearEditors,
|
2024-08-08 15:54:26 +02:00
|
|
|
clearInput,
|
2023-07-06 15:32:11 +02:00
|
|
|
closePages,
|
2024-06-21 20:37:01 +02:00
|
|
|
copy,
|
|
|
|
copyToClipboard,
|
2024-12-09 16:07:39 +01:00
|
|
|
dragAndDrop,
|
2024-10-02 14:36:35 +02:00
|
|
|
getAnnotationSelector,
|
2023-07-06 15:32:11 +02:00
|
|
|
getEditorDimensions,
|
2024-10-02 14:36:35 +02:00
|
|
|
getEditors,
|
2023-09-29 15:57:16 +02:00
|
|
|
getEditorSelector,
|
2023-10-03 15:02:54 +02:00
|
|
|
getFirstSerialized,
|
2024-05-23 17:53:58 +02:00
|
|
|
getRect,
|
2024-06-19 17:20:42 +02:00
|
|
|
getSerialized,
|
2025-03-06 14:59:04 +01:00
|
|
|
isCanvasMonochrome,
|
2023-10-26 22:42:41 +02:00
|
|
|
kbBigMoveDown,
|
|
|
|
kbBigMoveRight,
|
2024-03-22 19:30:56 +01:00
|
|
|
kbUndo,
|
2023-07-06 15:32:11 +02:00
|
|
|
loadAndWait,
|
2024-06-21 20:37:01 +02:00
|
|
|
paste,
|
2024-03-26 21:17:36 +01:00
|
|
|
pasteFromClipboard,
|
2024-03-22 19:30:56 +01:00
|
|
|
scrollIntoView,
|
2025-01-18 19:21:55 +01:00
|
|
|
selectEditor,
|
2023-07-06 15:32:11 +02:00
|
|
|
serializeBitmapDimensions,
|
2024-05-24 19:11:37 +02:00
|
|
|
switchToEditor,
|
2025-01-18 20:00:23 +01:00
|
|
|
unselectEditor,
|
2023-08-04 18:21:27 +02:00
|
|
|
waitForAnnotationEditorLayer,
|
2024-12-04 17:57:17 +01:00
|
|
|
waitForAnnotationModeChanged,
|
2024-05-13 16:14:50 +02:00
|
|
|
waitForEntryInStorage,
|
2025-03-06 14:59:04 +01:00
|
|
|
waitForPageRendered,
|
2023-09-29 15:57:16 +02:00
|
|
|
waitForSelectedEditor,
|
2024-03-22 19:30:56 +01:00
|
|
|
waitForSerialized,
|
2024-09-09 15:26:33 +02:00
|
|
|
waitForTimeout,
|
2023-10-12 13:16:58 +02:00
|
|
|
} from "./test_utils.mjs";
|
|
|
|
import { fileURLToPath } from "url";
|
|
|
|
import fs from "fs";
|
|
|
|
import path from "path";
|
2024-09-25 17:09:24 +02:00
|
|
|
import { PNG } from "pngjs";
|
2023-10-12 13:16:58 +02:00
|
|
|
|
|
|
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
2023-07-06 15:32:11 +02:00
|
|
|
|
2025-02-23 18:52:19 +01:00
|
|
|
const clearAll = clearEditors.bind(null, "stamp");
|
2023-09-29 15:57:16 +02:00
|
|
|
|
2023-09-29 22:36:01 +02:00
|
|
|
const waitForImage = async (page, selector) => {
|
|
|
|
await page.waitForSelector(`${selector} canvas`);
|
|
|
|
await page.waitForFunction(
|
|
|
|
sel => {
|
|
|
|
const canvas = document.querySelector(sel);
|
|
|
|
const data = canvas
|
|
|
|
.getContext("2d")
|
|
|
|
.getImageData(0, 0, canvas.width, canvas.height);
|
|
|
|
return data.data.some(x => x !== 0);
|
|
|
|
},
|
|
|
|
{},
|
|
|
|
`${selector} canvas`
|
|
|
|
);
|
|
|
|
await page.waitForSelector(`${selector} .altText`);
|
|
|
|
};
|
|
|
|
|
2025-01-19 18:59:55 +01:00
|
|
|
const copyImage = async (page, imagePath, selector) => {
|
2023-10-03 15:02:54 +02:00
|
|
|
const data = fs
|
|
|
|
.readFileSync(path.join(__dirname, imagePath))
|
|
|
|
.toString("base64");
|
2024-06-21 20:37:01 +02:00
|
|
|
|
|
|
|
await copyToClipboard(page, { "image/png": `data:image/png;base64,${data}` });
|
|
|
|
await pasteFromClipboard(page);
|
|
|
|
|
2025-01-19 18:59:55 +01:00
|
|
|
await waitForImage(page, selector);
|
2023-10-03 15:02:54 +02:00
|
|
|
};
|
|
|
|
|
2024-10-28 14:34:12 +01:00
|
|
|
async function waitForTranslation(page) {
|
|
|
|
return page.evaluate(async () => {
|
|
|
|
await new Promise(resolve => {
|
|
|
|
window.requestAnimationFrame(resolve);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2024-05-24 19:11:37 +02:00
|
|
|
const switchToStamp = switchToEditor.bind(null, "Stamp");
|
|
|
|
|
2023-07-06 15:32:11 +02:00
|
|
|
describe("Stamp Editor", () => {
|
|
|
|
describe("Basic operations", () => {
|
|
|
|
let pages;
|
|
|
|
|
2025-04-13 14:19:24 +02:00
|
|
|
beforeEach(async () => {
|
2024-10-04 17:07:25 +02:00
|
|
|
pages = await loadAndWait("empty.pdf", ".annotationEditorLayer", null, {
|
|
|
|
eventBusSetup: eventBus => {
|
|
|
|
eventBus.on("annotationeditoruimanager", ({ uiManager }) => {
|
|
|
|
window.uiManager = uiManager;
|
|
|
|
});
|
|
|
|
},
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
afterEach(async () => {
|
2023-07-06 15:32:11 +02:00
|
|
|
await closePages(pages);
|
|
|
|
});
|
|
|
|
|
|
|
|
it("must load a PNG which is bigger than a page", async () => {
|
|
|
|
await Promise.all(
|
|
|
|
pages.map(async ([browserName, page]) => {
|
2024-05-24 19:11:37 +02:00
|
|
|
await switchToStamp(page);
|
2023-08-09 14:07:15 +02:00
|
|
|
await page.click("#editorStampAddImage");
|
2023-07-06 15:32:11 +02:00
|
|
|
|
|
|
|
const input = await page.$("#stampEditorFileInput");
|
|
|
|
await input.uploadFile(
|
|
|
|
`${path.join(__dirname, "../images/firefox_logo.png")}`
|
|
|
|
);
|
2025-01-19 18:59:55 +01:00
|
|
|
const editorSelector = getEditorSelector(0);
|
|
|
|
await waitForImage(page, editorSelector);
|
2023-07-06 15:32:11 +02:00
|
|
|
|
2025-01-19 18:59:55 +01:00
|
|
|
const { width } = await getEditorDimensions(page, editorSelector);
|
2023-07-06 15:32:11 +02:00
|
|
|
|
|
|
|
// The image is bigger than the page, so it has been scaled down to
|
|
|
|
// 75% of the page width.
|
|
|
|
expect(width).toEqual("75%");
|
|
|
|
|
|
|
|
const [bitmap] = await serializeBitmapDimensions(page);
|
|
|
|
expect(bitmap.width).toEqual(512);
|
|
|
|
expect(bitmap.height).toEqual(543);
|
|
|
|
})
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
|
|
|
it("must load a SVG", async () => {
|
|
|
|
await Promise.all(
|
|
|
|
pages.map(async ([browserName, page]) => {
|
2024-10-04 17:07:25 +02:00
|
|
|
await switchToStamp(page);
|
2023-08-09 14:07:15 +02:00
|
|
|
await page.click("#editorStampAddImage");
|
2023-07-06 15:32:11 +02:00
|
|
|
const input = await page.$("#stampEditorFileInput");
|
|
|
|
await input.uploadFile(
|
|
|
|
`${path.join(__dirname, "../images/firefox_logo.svg")}`
|
|
|
|
);
|
2025-01-19 18:59:55 +01:00
|
|
|
const editorSelector = getEditorSelector(0);
|
|
|
|
await waitForImage(page, editorSelector);
|
2023-07-06 15:32:11 +02:00
|
|
|
|
2025-01-19 18:59:55 +01:00
|
|
|
const { width } = await getEditorDimensions(page, editorSelector);
|
2023-07-06 15:32:11 +02:00
|
|
|
|
|
|
|
expect(Math.round(parseFloat(width))).toEqual(40);
|
|
|
|
|
|
|
|
const [bitmap] = await serializeBitmapDimensions(page);
|
|
|
|
// The original size is 80x242 but to increase the resolution when it
|
|
|
|
// is rasterized we scale it up by 96 / 72
|
|
|
|
const ratio = await page.evaluate(
|
|
|
|
() => window.pdfjsLib.PixelsPerInch.PDF_TO_CSS_UNITS
|
|
|
|
);
|
2024-09-09 15:26:33 +02:00
|
|
|
expect(Math.abs(bitmap.width - 242 * ratio) < 1).toBeTrue();
|
|
|
|
expect(Math.abs(bitmap.height - 80 * ratio) < 1).toBeTrue();
|
2024-10-04 17:07:25 +02:00
|
|
|
})
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
|
|
|
it("must load a SVG, delete it and undo", async () => {
|
|
|
|
await Promise.all(
|
|
|
|
pages.map(async ([browserName, page]) => {
|
|
|
|
await switchToStamp(page);
|
|
|
|
await page.click("#editorStampAddImage");
|
|
|
|
const input = await page.$("#stampEditorFileInput");
|
|
|
|
await input.uploadFile(
|
|
|
|
`${path.join(__dirname, "../images/firefox_logo.svg")}`
|
|
|
|
);
|
|
|
|
const editorSelector = getEditorSelector(0);
|
|
|
|
await waitForImage(page, editorSelector);
|
|
|
|
await waitForSerialized(page, 1);
|
2024-11-18 21:25:43 +01:00
|
|
|
|
2024-10-04 17:07:25 +02:00
|
|
|
await page.waitForSelector(`${editorSelector} button.delete`);
|
|
|
|
await page.click(`${editorSelector} button.delete`);
|
|
|
|
await waitForSerialized(page, 0);
|
2023-07-06 15:32:11 +02:00
|
|
|
|
2024-10-04 17:07:25 +02:00
|
|
|
await kbUndo(page);
|
2024-11-18 21:25:43 +01:00
|
|
|
await waitForImage(page, editorSelector);
|
2024-10-04 17:07:25 +02:00
|
|
|
await waitForSerialized(page, 1);
|
|
|
|
|
|
|
|
await waitForSelectedEditor(page, editorSelector);
|
2023-07-06 15:32:11 +02:00
|
|
|
})
|
|
|
|
);
|
|
|
|
});
|
|
|
|
});
|
2023-07-13 18:31:08 +02:00
|
|
|
|
2023-08-04 18:21:27 +02:00
|
|
|
describe("Resize", () => {
|
|
|
|
let pages;
|
|
|
|
|
2025-04-13 14:19:24 +02:00
|
|
|
beforeEach(async () => {
|
2024-12-09 16:07:39 +01:00
|
|
|
pages = await loadAndWait("empty.pdf", ".annotationEditorLayer", 50, {
|
|
|
|
eventBusSetup: eventBus => {
|
|
|
|
eventBus.on("annotationeditoruimanager", ({ uiManager }) => {
|
|
|
|
window.uiManager = uiManager;
|
|
|
|
});
|
|
|
|
},
|
|
|
|
});
|
2023-08-04 18:21:27 +02:00
|
|
|
});
|
|
|
|
|
2024-12-09 16:07:39 +01:00
|
|
|
afterEach(async () => {
|
2025-04-13 14:19:24 +02:00
|
|
|
await closePages(pages);
|
2024-12-09 16:07:39 +01:00
|
|
|
});
|
|
|
|
|
2023-08-04 18:21:27 +02:00
|
|
|
it("must check that an added image stay within the page", async () => {
|
|
|
|
await Promise.all(
|
|
|
|
pages.map(async ([browserName, page]) => {
|
2024-05-24 19:11:37 +02:00
|
|
|
await switchToStamp(page);
|
2023-08-04 18:21:27 +02:00
|
|
|
const names = ["bottomLeft", "bottomRight", "topRight", "topLeft"];
|
|
|
|
|
|
|
|
for (let i = 0; i < 4; i++) {
|
|
|
|
if (i !== 0) {
|
2023-09-29 15:57:16 +02:00
|
|
|
await clearAll(page);
|
2023-08-04 18:21:27 +02:00
|
|
|
}
|
|
|
|
|
2023-08-09 14:07:15 +02:00
|
|
|
await page.click("#editorStampAddImage");
|
2023-08-04 18:21:27 +02:00
|
|
|
const input = await page.$("#stampEditorFileInput");
|
|
|
|
await input.uploadFile(
|
|
|
|
`${path.join(__dirname, "../images/firefox_logo.png")}`
|
|
|
|
);
|
2024-12-09 16:07:39 +01:00
|
|
|
const editorSelector = getEditorSelector(i);
|
|
|
|
await waitForImage(page, editorSelector);
|
|
|
|
await page.waitForSelector(`${editorSelector} .altText`);
|
2023-08-04 18:21:27 +02:00
|
|
|
|
|
|
|
for (let j = 0; j < 4; j++) {
|
|
|
|
await page.keyboard.press("Escape");
|
2024-12-09 16:07:39 +01:00
|
|
|
await page.waitForSelector(`${editorSelector} .resizers.hidden`);
|
2023-08-04 18:21:27 +02:00
|
|
|
|
2023-12-06 15:27:31 +01:00
|
|
|
const handle = await waitForAnnotationEditorLayer(page);
|
2023-08-04 18:21:27 +02:00
|
|
|
await page.evaluate(() => {
|
|
|
|
window.PDFViewerApplication.rotatePages(90);
|
|
|
|
});
|
2023-12-06 15:27:31 +01:00
|
|
|
await awaitPromise(handle);
|
2023-09-29 15:57:16 +02:00
|
|
|
|
2023-08-04 18:21:27 +02:00
|
|
|
await page.focus(".stampEditor");
|
2024-12-09 16:07:39 +01:00
|
|
|
await waitForSelectedEditor(page, editorSelector);
|
2023-08-04 18:21:27 +02:00
|
|
|
|
2023-09-29 15:57:16 +02:00
|
|
|
await page.waitForSelector(
|
2024-12-09 16:07:39 +01:00
|
|
|
`${editorSelector} .resizers:not(.hidden)`
|
2023-08-04 18:21:27 +02:00
|
|
|
);
|
|
|
|
|
2024-05-23 17:53:58 +02:00
|
|
|
const stampRect = await getRect(page, ".stampEditor");
|
|
|
|
const [name, cursor] = await page.evaluate(rect => {
|
|
|
|
const el = document.elementFromPoint(rect.x, rect.y);
|
2023-08-04 18:21:27 +02:00
|
|
|
const cornerName = Array.from(el.classList).find(
|
|
|
|
c => c !== "resizer"
|
|
|
|
);
|
|
|
|
return [cornerName, window.getComputedStyle(el).cursor];
|
2024-05-23 17:53:58 +02:00
|
|
|
}, stampRect);
|
2023-08-04 18:21:27 +02:00
|
|
|
|
|
|
|
expect(name).withContext(`In ${browserName}`).toEqual(names[j]);
|
|
|
|
expect(cursor)
|
|
|
|
.withContext(`In ${browserName}`)
|
|
|
|
.toEqual("nwse-resize");
|
|
|
|
}
|
|
|
|
|
2023-12-06 15:27:31 +01:00
|
|
|
const handle = await waitForAnnotationEditorLayer(page);
|
2023-08-04 18:21:27 +02:00
|
|
|
await page.evaluate(() => {
|
|
|
|
window.PDFViewerApplication.rotatePages(90);
|
|
|
|
});
|
2023-12-06 15:27:31 +01:00
|
|
|
await awaitPromise(handle);
|
2023-08-04 18:21:27 +02:00
|
|
|
}
|
|
|
|
})
|
|
|
|
);
|
|
|
|
});
|
2024-12-09 16:07:39 +01:00
|
|
|
|
|
|
|
it("must check that the opposite corner doesn't move", async () => {
|
|
|
|
await Promise.all(
|
|
|
|
pages.map(async ([browserName, page]) => {
|
|
|
|
await switchToStamp(page);
|
|
|
|
|
|
|
|
await page.click("#editorStampAddImage");
|
|
|
|
const input = await page.$("#stampEditorFileInput");
|
|
|
|
await input.uploadFile(
|
|
|
|
`${path.join(__dirname, "../images/firefox_logo.png")}`
|
|
|
|
);
|
|
|
|
const editorSelector = getEditorSelector(0);
|
|
|
|
await waitForImage(page, editorSelector);
|
|
|
|
await page.waitForSelector(`${editorSelector} .resizer.topLeft`);
|
|
|
|
const baseRect = await getRect(page, editorSelector);
|
|
|
|
const bRX = baseRect.x + baseRect.width;
|
|
|
|
const bRY = baseRect.y + baseRect.height;
|
|
|
|
|
|
|
|
await dragAndDrop(page, `${editorSelector} .resizer.topLeft`, [
|
|
|
|
[-10, -10],
|
|
|
|
[20, 20],
|
|
|
|
[-10, -10],
|
|
|
|
[20, 20],
|
|
|
|
]);
|
|
|
|
|
|
|
|
const newRect = await getRect(page, editorSelector);
|
|
|
|
const newBRX = newRect.x + newRect.width;
|
|
|
|
const newBRY = newRect.y + newRect.height;
|
|
|
|
|
|
|
|
expect(Math.abs(bRX - newBRX) <= 1)
|
|
|
|
.withContext(`In ${browserName}`)
|
|
|
|
.toBeTrue();
|
|
|
|
expect(Math.abs(bRY - newBRY) <= 1)
|
|
|
|
.withContext(`In ${browserName}`)
|
|
|
|
.toBeTrue();
|
|
|
|
})
|
|
|
|
);
|
|
|
|
});
|
2023-08-04 18:21:27 +02:00
|
|
|
});
|
2023-09-23 18:56:07 +02:00
|
|
|
|
|
|
|
describe("Alt text dialog", () => {
|
|
|
|
let pages;
|
|
|
|
|
2025-04-13 14:19:24 +02:00
|
|
|
beforeEach(async () => {
|
2023-09-23 18:56:07 +02:00
|
|
|
pages = await loadAndWait("empty.pdf", ".annotationEditorLayer", 50);
|
|
|
|
});
|
|
|
|
|
2025-04-13 14:19:24 +02:00
|
|
|
afterEach(async () => {
|
2023-09-23 18:56:07 +02:00
|
|
|
await closePages(pages);
|
|
|
|
});
|
|
|
|
|
|
|
|
it("must check that the alt-text flow is correctly implemented", async () => {
|
2024-06-27 18:15:45 +02:00
|
|
|
// Run sequentially to avoid clipboard issues.
|
|
|
|
for (const [browserName, page] of pages) {
|
|
|
|
await switchToStamp(page);
|
|
|
|
|
2025-01-19 18:59:55 +01:00
|
|
|
const editorSelector = getEditorSelector(0);
|
|
|
|
await copyImage(page, "../images/firefox_logo.png", editorSelector);
|
2024-06-27 18:15:45 +02:00
|
|
|
|
|
|
|
// Wait for the alt-text button to be visible.
|
2025-01-19 18:59:55 +01:00
|
|
|
const buttonSelector = `${editorSelector} button.altText`;
|
2024-06-27 18:15:45 +02:00
|
|
|
await page.waitForSelector(buttonSelector);
|
|
|
|
|
|
|
|
// Click on the alt-text button.
|
|
|
|
await page.click(buttonSelector);
|
|
|
|
|
|
|
|
// Wait for the alt-text dialog to be visible.
|
|
|
|
await page.waitForSelector("#altTextDialog", { visible: true });
|
|
|
|
|
|
|
|
// Click on the alt-text editor.
|
|
|
|
const textareaSelector = "#altTextDialog textarea";
|
|
|
|
await page.click(textareaSelector);
|
|
|
|
await page.type(textareaSelector, "Hello World");
|
|
|
|
|
|
|
|
// Click on save button.
|
|
|
|
const saveButtonSelector = "#altTextDialog #altTextSave";
|
|
|
|
await page.click(saveButtonSelector);
|
|
|
|
|
|
|
|
// Check that the canvas has an aria-describedby attribute.
|
2025-03-12 20:37:02 +01:00
|
|
|
await page.waitForSelector(`${editorSelector}[aria-describedby]`);
|
2024-06-27 18:15:45 +02:00
|
|
|
|
|
|
|
// Wait for the alt-text button to have the correct icon.
|
|
|
|
await page.waitForSelector(`${buttonSelector}.done`);
|
|
|
|
|
|
|
|
// Hover the button.
|
|
|
|
await page.hover(buttonSelector);
|
|
|
|
|
|
|
|
// Wait for the tooltip to be visible.
|
|
|
|
const tooltipSelector = `${buttonSelector} .tooltip`;
|
|
|
|
await page.waitForSelector(tooltipSelector, { visible: true });
|
|
|
|
|
|
|
|
let tooltipText = await page.evaluate(
|
|
|
|
sel => document.querySelector(`${sel}`).innerText,
|
|
|
|
tooltipSelector
|
|
|
|
);
|
|
|
|
expect(tooltipText).toEqual("Hello World");
|
|
|
|
|
|
|
|
// Now we change the alt-text and check that the tooltip is updated.
|
2024-10-27 20:24:24 +01:00
|
|
|
const longString = "a".repeat(512);
|
2024-06-27 18:15:45 +02:00
|
|
|
await page.click(buttonSelector);
|
|
|
|
await page.waitForSelector("#altTextDialog", { visible: true });
|
|
|
|
await page.evaluate(sel => {
|
|
|
|
document.querySelector(`${sel}`).value = "";
|
|
|
|
}, textareaSelector);
|
|
|
|
await page.click(textareaSelector);
|
2024-10-27 20:24:24 +01:00
|
|
|
await page.type(textareaSelector, longString);
|
2024-06-27 18:15:45 +02:00
|
|
|
await page.click(saveButtonSelector);
|
|
|
|
await page.waitForSelector(`${buttonSelector}.done`);
|
|
|
|
await page.hover(buttonSelector);
|
|
|
|
await page.waitForSelector(tooltipSelector, { visible: true });
|
|
|
|
tooltipText = await page.evaluate(
|
|
|
|
sel => document.querySelector(`${sel}`).innerText,
|
|
|
|
tooltipSelector
|
|
|
|
);
|
2024-10-27 20:24:24 +01:00
|
|
|
expect(tooltipText).toEqual(longString);
|
|
|
|
const dims = await page.evaluate(sel => {
|
|
|
|
const { width, height } = document
|
|
|
|
.querySelector(`${sel}`)
|
|
|
|
.getBoundingClientRect();
|
|
|
|
return { width, height };
|
|
|
|
}, tooltipSelector);
|
|
|
|
expect(dims.width / dims.height).toBeLessThan(2);
|
2024-06-27 18:15:45 +02:00
|
|
|
|
|
|
|
// Now we just check that cancel didn't change anything.
|
|
|
|
await page.click(buttonSelector);
|
|
|
|
await page.waitForSelector("#altTextDialog", { visible: true });
|
|
|
|
await page.evaluate(sel => {
|
|
|
|
document.querySelector(`${sel}`).value = "";
|
|
|
|
}, textareaSelector);
|
|
|
|
await page.click(textareaSelector);
|
|
|
|
await page.type(textareaSelector, "Hello PDF.js");
|
|
|
|
const cancelButtonSelector = "#altTextDialog #altTextCancel";
|
|
|
|
await page.click(cancelButtonSelector);
|
|
|
|
await page.waitForSelector(`${buttonSelector}.done`);
|
|
|
|
await page.hover(buttonSelector);
|
|
|
|
await page.waitForSelector(tooltipSelector, { visible: true });
|
|
|
|
tooltipText = await page.evaluate(
|
|
|
|
sel => document.querySelector(`${sel}`).innerText,
|
|
|
|
tooltipSelector
|
|
|
|
);
|
2024-10-27 20:24:24 +01:00
|
|
|
// The tooltip should still be longString.
|
|
|
|
expect(tooltipText).toEqual(longString);
|
2024-06-27 18:15:45 +02:00
|
|
|
|
|
|
|
// Now we switch to decorative.
|
|
|
|
await page.click(buttonSelector);
|
|
|
|
await page.waitForSelector("#altTextDialog", { visible: true });
|
|
|
|
const decorativeSelector = "#altTextDialog #decorativeButton";
|
|
|
|
await page.click(decorativeSelector);
|
|
|
|
await page.click(saveButtonSelector);
|
|
|
|
await page.waitForSelector(`${buttonSelector}.done`);
|
|
|
|
await page.hover(buttonSelector);
|
|
|
|
await page.waitForSelector(tooltipSelector, { visible: true });
|
|
|
|
tooltipText = await page.evaluate(
|
|
|
|
sel => document.querySelector(`${sel}`).innerText,
|
|
|
|
tooltipSelector
|
|
|
|
);
|
|
|
|
expect(tooltipText).toEqual("Marked as decorative");
|
|
|
|
|
|
|
|
// Now we switch back to non-decorative.
|
|
|
|
await page.click(buttonSelector);
|
|
|
|
await page.waitForSelector("#altTextDialog", { visible: true });
|
|
|
|
const descriptionSelector = "#altTextDialog #descriptionButton";
|
|
|
|
await page.click(descriptionSelector);
|
|
|
|
await page.click(saveButtonSelector);
|
|
|
|
await page.waitForSelector(`${buttonSelector}.done`);
|
|
|
|
await page.hover(buttonSelector);
|
|
|
|
await page.waitForSelector(tooltipSelector, { visible: true });
|
|
|
|
tooltipText = await page.evaluate(
|
|
|
|
sel => document.querySelector(`${sel}`).innerText,
|
|
|
|
tooltipSelector
|
|
|
|
);
|
2024-10-27 20:24:24 +01:00
|
|
|
expect(tooltipText).toEqual(longString);
|
2024-06-27 18:15:45 +02:00
|
|
|
|
|
|
|
// Now we remove the alt-text and check that the tooltip is removed.
|
|
|
|
await page.click(buttonSelector);
|
|
|
|
await page.waitForSelector("#altTextDialog", { visible: true });
|
|
|
|
await page.evaluate(sel => {
|
|
|
|
document.querySelector(`${sel}`).value = "";
|
|
|
|
}, textareaSelector);
|
|
|
|
await page.click(saveButtonSelector);
|
|
|
|
await page.waitForSelector(`${buttonSelector}:not(.done)`);
|
|
|
|
await page.hover(buttonSelector);
|
|
|
|
await page.evaluate(
|
|
|
|
sel => document.querySelector(sel) === null,
|
|
|
|
tooltipSelector
|
|
|
|
);
|
|
|
|
|
|
|
|
// We check that the alt-text button works correctly with the
|
|
|
|
// keyboard.
|
|
|
|
const handle = await page.evaluateHandle(sel => {
|
|
|
|
document.getElementById("viewerContainer").focus();
|
|
|
|
return [
|
|
|
|
new Promise(resolve => {
|
|
|
|
setTimeout(() => {
|
|
|
|
const el = document.querySelector(sel);
|
|
|
|
el.addEventListener("focus", resolve, { once: true });
|
|
|
|
el.focus({ focusVisible: true });
|
|
|
|
}, 0);
|
|
|
|
}),
|
|
|
|
];
|
|
|
|
}, buttonSelector);
|
|
|
|
await awaitPromise(handle);
|
|
|
|
await (browserName === "chrome"
|
|
|
|
? page.waitForSelector(`${buttonSelector}:focus`)
|
|
|
|
: page.waitForSelector(`${buttonSelector}:focus-visible`));
|
|
|
|
await page.keyboard.press("Enter");
|
|
|
|
await page.waitForSelector("#altTextDialog", { visible: true });
|
|
|
|
await page.keyboard.press("Escape");
|
|
|
|
await (browserName === "chrome"
|
|
|
|
? page.waitForSelector(`${buttonSelector}:focus`)
|
|
|
|
: page.waitForSelector(`${buttonSelector}:focus-visible`));
|
|
|
|
}
|
2023-09-23 18:56:07 +02:00
|
|
|
});
|
|
|
|
});
|
2023-10-03 15:02:54 +02:00
|
|
|
|
|
|
|
describe("Resize an image with the keyboard", () => {
|
|
|
|
let pages;
|
|
|
|
|
2025-04-13 14:19:24 +02:00
|
|
|
beforeEach(async () => {
|
2023-10-03 15:02:54 +02:00
|
|
|
pages = await loadAndWait("empty.pdf", ".annotationEditorLayer", 50);
|
|
|
|
});
|
|
|
|
|
2025-04-13 14:19:24 +02:00
|
|
|
afterEach(async () => {
|
2023-10-03 15:02:54 +02:00
|
|
|
await closePages(pages);
|
|
|
|
});
|
|
|
|
|
|
|
|
it("must check that the dimensions change", async () => {
|
2024-06-27 18:15:45 +02:00
|
|
|
// Run sequentially to avoid clipboard issues.
|
|
|
|
for (const [browserName, page] of pages) {
|
|
|
|
await switchToStamp(page);
|
2023-10-03 15:02:54 +02:00
|
|
|
|
2024-06-27 18:15:45 +02:00
|
|
|
const editorSelector = getEditorSelector(0);
|
2025-01-19 18:59:55 +01:00
|
|
|
await copyImage(page, "../images/firefox_logo.png", editorSelector);
|
|
|
|
|
2025-01-18 19:21:55 +01:00
|
|
|
await selectEditor(page, editorSelector);
|
2023-10-03 15:02:54 +02:00
|
|
|
|
2024-06-27 18:15:45 +02:00
|
|
|
await page.waitForSelector(
|
|
|
|
`${editorSelector} .resizer.topLeft[tabindex="-1"]`
|
|
|
|
);
|
2023-10-03 15:02:54 +02:00
|
|
|
|
2024-06-27 18:15:45 +02:00
|
|
|
const getDims = async () => {
|
|
|
|
const [blX, blY, trX, trY] = await getFirstSerialized(
|
|
|
|
page,
|
|
|
|
x => x.rect
|
2023-10-03 15:02:54 +02:00
|
|
|
);
|
2024-06-27 18:15:45 +02:00
|
|
|
return [trX - blX, trY - blY];
|
|
|
|
};
|
2023-10-03 15:02:54 +02:00
|
|
|
|
2024-06-27 18:15:45 +02:00
|
|
|
const [width, height] = await getDims();
|
2023-10-03 15:02:54 +02:00
|
|
|
|
2024-06-27 18:15:45 +02:00
|
|
|
// Press Enter to enter in resize-with-keyboard mode.
|
|
|
|
await page.keyboard.press("Enter");
|
2023-10-03 15:02:54 +02:00
|
|
|
|
2024-06-27 18:15:45 +02:00
|
|
|
// The resizer must become keyboard focusable.
|
|
|
|
await page.waitForSelector(
|
|
|
|
`${editorSelector} .resizer.topLeft[tabindex="0"]`
|
|
|
|
);
|
2023-10-03 15:02:54 +02:00
|
|
|
|
2024-06-27 18:15:45 +02:00
|
|
|
let prevWidth = width;
|
|
|
|
let prevHeight = height;
|
2023-10-03 15:02:54 +02:00
|
|
|
|
2024-06-27 18:15:45 +02:00
|
|
|
const waitForDimsChange = async (w, h) => {
|
2023-10-03 15:02:54 +02:00
|
|
|
await page.waitForFunction(
|
2024-06-27 18:15:45 +02:00
|
|
|
(prevW, prevH) => {
|
|
|
|
const [x1, y1, x2, y2] =
|
|
|
|
window.PDFViewerApplication.pdfDocument.annotationStorage.serializable.map
|
|
|
|
.values()
|
|
|
|
.next().value.rect;
|
|
|
|
const newWidth = x2 - x1;
|
|
|
|
const newHeight = y2 - y1;
|
|
|
|
return newWidth !== prevW || newHeight !== prevH;
|
|
|
|
},
|
|
|
|
{},
|
|
|
|
w,
|
|
|
|
h
|
2023-10-03 15:02:54 +02:00
|
|
|
);
|
2024-06-27 18:15:45 +02:00
|
|
|
};
|
2023-10-03 15:02:54 +02:00
|
|
|
|
2024-06-27 18:15:45 +02:00
|
|
|
for (let i = 0; i < 40; i++) {
|
|
|
|
await page.keyboard.press("ArrowLeft");
|
|
|
|
await waitForDimsChange(prevWidth, prevHeight);
|
|
|
|
[prevWidth, prevHeight] = await getDims();
|
|
|
|
}
|
2023-10-03 15:02:54 +02:00
|
|
|
|
2024-06-27 18:15:45 +02:00
|
|
|
let [newWidth, newHeight] = await getDims();
|
|
|
|
expect(newWidth > width + 30)
|
|
|
|
.withContext(`In ${browserName}`)
|
|
|
|
.toEqual(true);
|
|
|
|
expect(newHeight > height + 30)
|
|
|
|
.withContext(`In ${browserName}`)
|
|
|
|
.toEqual(true);
|
|
|
|
|
|
|
|
for (let i = 0; i < 4; i++) {
|
|
|
|
await kbBigMoveRight(page);
|
|
|
|
await waitForDimsChange(prevWidth, prevHeight);
|
|
|
|
[prevWidth, prevHeight] = await getDims();
|
|
|
|
}
|
2023-10-03 15:02:54 +02:00
|
|
|
|
2024-06-27 18:15:45 +02:00
|
|
|
[newWidth, newHeight] = await getDims();
|
|
|
|
expect(Math.abs(newWidth - width) < 2)
|
|
|
|
.withContext(`In ${browserName}`)
|
|
|
|
.toEqual(true);
|
|
|
|
expect(Math.abs(newHeight - height) < 2)
|
|
|
|
.withContext(`In ${browserName}`)
|
|
|
|
.toEqual(true);
|
|
|
|
|
|
|
|
// Move the focus to the next resizer.
|
|
|
|
await page.keyboard.press("Tab");
|
|
|
|
await page.waitForFunction(
|
|
|
|
() => !!document.activeElement?.classList.contains("topMiddle")
|
|
|
|
);
|
|
|
|
|
|
|
|
for (let i = 0; i < 40; i++) {
|
|
|
|
await page.keyboard.press("ArrowUp");
|
|
|
|
await waitForDimsChange(prevWidth, prevHeight);
|
|
|
|
[prevWidth, prevHeight] = await getDims();
|
|
|
|
}
|
2023-10-03 15:02:54 +02:00
|
|
|
|
2024-06-27 18:15:45 +02:00
|
|
|
[, newHeight] = await getDims();
|
|
|
|
expect(newHeight > height + 50)
|
|
|
|
.withContext(`In ${browserName}`)
|
|
|
|
.toEqual(true);
|
2023-10-03 15:02:54 +02:00
|
|
|
|
2024-06-27 18:15:45 +02:00
|
|
|
for (let i = 0; i < 4; i++) {
|
|
|
|
await kbBigMoveDown(page);
|
|
|
|
await waitForDimsChange(prevWidth, prevHeight);
|
|
|
|
[prevWidth, prevHeight] = await getDims();
|
|
|
|
}
|
|
|
|
|
|
|
|
[, newHeight] = await getDims();
|
|
|
|
expect(Math.abs(newHeight - height) < 2)
|
|
|
|
.withContext(`In ${browserName}`)
|
|
|
|
.toEqual(true);
|
|
|
|
|
|
|
|
// Escape should remove the focus from the resizer.
|
|
|
|
await page.keyboard.press("Escape");
|
|
|
|
await page.waitForSelector(
|
|
|
|
`${editorSelector} .resizer.topLeft[tabindex="-1"]`
|
|
|
|
);
|
|
|
|
await page.waitForFunction(
|
|
|
|
() => !document.activeElement?.classList.contains("resizer")
|
|
|
|
);
|
|
|
|
}
|
2023-10-03 15:02:54 +02:00
|
|
|
});
|
|
|
|
});
|
2024-03-07 18:24:34 +01:00
|
|
|
|
|
|
|
describe("Copy/paste from a tab to an other", () => {
|
|
|
|
let pages1, pages2;
|
|
|
|
|
2025-04-13 14:19:24 +02:00
|
|
|
beforeEach(async () => {
|
2024-03-07 18:24:34 +01:00
|
|
|
pages1 = await loadAndWait("empty.pdf", ".annotationEditorLayer");
|
|
|
|
pages2 = await loadAndWait("empty.pdf", ".annotationEditorLayer");
|
|
|
|
});
|
|
|
|
|
2025-04-13 14:19:24 +02:00
|
|
|
afterEach(async () => {
|
2024-06-23 18:26:23 +02:00
|
|
|
await closePages(pages1);
|
2024-06-25 17:23:19 +02:00
|
|
|
await closePages(pages2);
|
2024-03-07 18:24:34 +01:00
|
|
|
});
|
|
|
|
|
|
|
|
it("must check that the alt-text button is here when pasting in the second tab", async () => {
|
|
|
|
for (let i = 0; i < pages1.length; i++) {
|
|
|
|
const [, page1] = pages1[i];
|
2024-06-23 18:26:23 +02:00
|
|
|
await page1.bringToFront();
|
2024-05-21 14:41:07 +02:00
|
|
|
await switchToStamp(page1);
|
2024-03-07 18:24:34 +01:00
|
|
|
|
2025-01-19 18:59:55 +01:00
|
|
|
const editorSelector = getEditorSelector(0);
|
|
|
|
await copyImage(page1, "../images/firefox_logo.png", editorSelector);
|
2024-06-21 20:37:01 +02:00
|
|
|
await copy(page1);
|
2024-03-07 18:24:34 +01:00
|
|
|
|
|
|
|
const [, page2] = pages2[i];
|
2024-06-23 18:26:23 +02:00
|
|
|
await page2.bringToFront();
|
2024-05-21 14:41:07 +02:00
|
|
|
await switchToStamp(page2);
|
2024-03-07 18:24:34 +01:00
|
|
|
|
2024-06-21 20:37:01 +02:00
|
|
|
await paste(page2);
|
2024-03-07 18:24:34 +01:00
|
|
|
|
2025-01-19 18:59:55 +01:00
|
|
|
await waitForImage(page2, editorSelector);
|
2024-03-07 18:24:34 +01:00
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
2024-03-22 19:30:56 +01:00
|
|
|
|
|
|
|
describe("Undo a stamp", () => {
|
|
|
|
let pages;
|
|
|
|
|
2025-04-13 14:19:24 +02:00
|
|
|
beforeEach(async () => {
|
2024-03-22 19:30:56 +01:00
|
|
|
pages = await loadAndWait("tracemonkey.pdf", ".annotationEditorLayer");
|
|
|
|
});
|
|
|
|
|
2025-04-13 14:19:24 +02:00
|
|
|
afterEach(async () => {
|
2024-03-22 19:30:56 +01:00
|
|
|
await closePages(pages);
|
|
|
|
});
|
|
|
|
|
|
|
|
it("must check that a stamp can be undone", async () => {
|
2024-06-27 18:15:45 +02:00
|
|
|
// Run sequentially to avoid clipboard issues.
|
|
|
|
for (const [, page] of pages) {
|
|
|
|
await switchToStamp(page);
|
|
|
|
|
2025-01-19 18:59:55 +01:00
|
|
|
const editorSelector = getEditorSelector(0);
|
|
|
|
await copyImage(page, "../images/firefox_logo.png", editorSelector);
|
|
|
|
await page.waitForSelector(editorSelector);
|
2024-06-27 18:15:45 +02:00
|
|
|
await waitForSerialized(page, 1);
|
|
|
|
|
2025-01-19 18:59:55 +01:00
|
|
|
await page.waitForSelector(`${editorSelector} button.delete`);
|
|
|
|
await page.click(`${editorSelector} button.delete`);
|
2024-06-27 18:15:45 +02:00
|
|
|
await waitForSerialized(page, 0);
|
|
|
|
|
|
|
|
await kbUndo(page);
|
2025-01-19 18:59:55 +01:00
|
|
|
await waitForImage(page, editorSelector);
|
2024-06-27 18:15:45 +02:00
|
|
|
await waitForSerialized(page, 1);
|
|
|
|
}
|
2024-03-22 19:30:56 +01:00
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
describe("Delete a stamp and undo it on another page", () => {
|
|
|
|
let pages;
|
|
|
|
|
2025-04-13 14:19:24 +02:00
|
|
|
beforeEach(async () => {
|
2024-03-22 19:30:56 +01:00
|
|
|
pages = await loadAndWait("tracemonkey.pdf", ".annotationEditorLayer");
|
|
|
|
});
|
|
|
|
|
2025-04-13 14:19:24 +02:00
|
|
|
afterEach(async () => {
|
2024-03-22 19:30:56 +01:00
|
|
|
await closePages(pages);
|
|
|
|
});
|
|
|
|
|
|
|
|
it("must check that a stamp can be undone", async () => {
|
2024-06-27 18:15:45 +02:00
|
|
|
// Run sequentially to avoid clipboard issues.
|
|
|
|
for (const [, page] of pages) {
|
|
|
|
await switchToStamp(page);
|
|
|
|
|
2025-01-19 18:59:55 +01:00
|
|
|
const editorSelector = getEditorSelector(0);
|
|
|
|
await copyImage(page, "../images/firefox_logo.png", editorSelector);
|
|
|
|
await page.waitForSelector(editorSelector);
|
2024-06-27 18:15:45 +02:00
|
|
|
await waitForSerialized(page, 1);
|
|
|
|
|
2025-01-19 18:59:55 +01:00
|
|
|
await page.waitForSelector(`${editorSelector} button.delete`);
|
|
|
|
await page.click(`${editorSelector} button.delete`);
|
2024-06-27 18:15:45 +02:00
|
|
|
await waitForSerialized(page, 0);
|
|
|
|
|
|
|
|
const twoToFourteen = Array.from(new Array(13).keys(), n => n + 2);
|
|
|
|
for (const pageNumber of twoToFourteen) {
|
|
|
|
const pageSelector = `.page[data-page-number = "${pageNumber}"]`;
|
|
|
|
await scrollIntoView(page, pageSelector);
|
|
|
|
}
|
2024-03-22 19:30:56 +01:00
|
|
|
|
2024-06-27 18:15:45 +02:00
|
|
|
await kbUndo(page);
|
|
|
|
await waitForSerialized(page, 1);
|
2024-03-22 19:30:56 +01:00
|
|
|
|
2024-06-27 18:15:45 +02:00
|
|
|
const thirteenToOne = Array.from(new Array(13).keys(), n => 13 - n);
|
|
|
|
for (const pageNumber of thirteenToOne) {
|
|
|
|
const pageSelector = `.page[data-page-number = "${pageNumber}"]`;
|
|
|
|
await scrollIntoView(page, pageSelector);
|
|
|
|
}
|
2024-03-22 19:30:56 +01:00
|
|
|
|
2025-01-19 18:59:55 +01:00
|
|
|
await page.waitForSelector(`${editorSelector} canvas`);
|
2024-06-27 18:15:45 +02:00
|
|
|
}
|
2024-03-22 19:30:56 +01:00
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
describe("Delete a stamp, scroll and undo it", () => {
|
|
|
|
let pages;
|
|
|
|
|
2025-04-13 14:19:24 +02:00
|
|
|
beforeEach(async () => {
|
2024-03-22 19:30:56 +01:00
|
|
|
pages = await loadAndWait("tracemonkey.pdf", ".annotationEditorLayer");
|
|
|
|
});
|
|
|
|
|
2025-04-13 14:19:24 +02:00
|
|
|
afterEach(async () => {
|
2024-03-22 19:30:56 +01:00
|
|
|
await closePages(pages);
|
|
|
|
});
|
|
|
|
|
|
|
|
it("must check that a stamp can be undone", async () => {
|
2024-06-27 18:15:45 +02:00
|
|
|
// Run sequentially to avoid clipboard issues.
|
|
|
|
for (const [, page] of pages) {
|
|
|
|
await switchToStamp(page);
|
|
|
|
|
2025-01-19 18:59:55 +01:00
|
|
|
const editorSelector = getEditorSelector(0);
|
|
|
|
await copyImage(page, "../images/firefox_logo.png", editorSelector);
|
|
|
|
await page.waitForSelector(editorSelector);
|
2024-06-27 18:15:45 +02:00
|
|
|
await waitForSerialized(page, 1);
|
|
|
|
|
2025-01-19 18:59:55 +01:00
|
|
|
await page.waitForSelector(`${editorSelector} button.delete`);
|
|
|
|
await page.click(`${editorSelector} button.delete`);
|
2024-06-27 18:15:45 +02:00
|
|
|
await waitForSerialized(page, 0);
|
|
|
|
|
|
|
|
const twoToOne = Array.from(new Array(13).keys(), n => n + 2).concat(
|
|
|
|
Array.from(new Array(13).keys(), n => 13 - n)
|
|
|
|
);
|
|
|
|
for (const pageNumber of twoToOne) {
|
|
|
|
const pageSelector = `.page[data-page-number = "${pageNumber}"]`;
|
|
|
|
await scrollIntoView(page, pageSelector);
|
|
|
|
}
|
2024-03-22 19:30:56 +01:00
|
|
|
|
2024-06-27 18:15:45 +02:00
|
|
|
await kbUndo(page);
|
2025-01-19 18:59:55 +01:00
|
|
|
await waitForImage(page, editorSelector);
|
2024-06-27 18:15:45 +02:00
|
|
|
await waitForSerialized(page, 1);
|
|
|
|
}
|
2024-03-22 19:30:56 +01:00
|
|
|
});
|
|
|
|
});
|
2024-05-13 16:14:50 +02:00
|
|
|
|
|
|
|
describe("Resize a stamp", () => {
|
|
|
|
let pages;
|
|
|
|
|
2025-04-13 14:19:24 +02:00
|
|
|
beforeEach(async () => {
|
2024-05-13 16:14:50 +02:00
|
|
|
pages = await loadAndWait("empty.pdf", ".annotationEditorLayer");
|
|
|
|
});
|
|
|
|
|
2025-04-13 14:19:24 +02:00
|
|
|
afterEach(async () => {
|
2024-05-13 16:14:50 +02:00
|
|
|
await closePages(pages);
|
|
|
|
});
|
|
|
|
|
|
|
|
it("must check that a resized stamp has its canvas at the right position", async () => {
|
2024-06-27 18:15:45 +02:00
|
|
|
// Run sequentially to avoid clipboard issues.
|
|
|
|
for (const [, page] of pages) {
|
|
|
|
await switchToStamp(page);
|
|
|
|
|
2025-01-19 18:59:55 +01:00
|
|
|
const editorSelector = getEditorSelector(0);
|
|
|
|
await copyImage(page, "../images/firefox_logo.png", editorSelector);
|
|
|
|
await page.waitForSelector(editorSelector);
|
2024-06-27 18:15:45 +02:00
|
|
|
await waitForSerialized(page, 1);
|
|
|
|
|
|
|
|
const serializedRect = await getFirstSerialized(page, x => x.rect);
|
|
|
|
const rect = await getRect(page, ".resizer.bottomRight");
|
|
|
|
const centerX = rect.x + rect.width / 2;
|
|
|
|
const centerY = rect.y + rect.height / 2;
|
|
|
|
|
|
|
|
await page.mouse.move(centerX, centerY);
|
|
|
|
await page.mouse.down();
|
|
|
|
await page.mouse.move(centerX - 500, centerY - 500);
|
|
|
|
await page.mouse.up();
|
|
|
|
|
|
|
|
await waitForEntryInStorage(
|
|
|
|
page,
|
|
|
|
"rect",
|
|
|
|
serializedRect,
|
|
|
|
(x, y) => x !== y
|
|
|
|
);
|
|
|
|
|
2025-01-19 18:59:55 +01:00
|
|
|
const canvasRect = await getRect(page, `${editorSelector} canvas`);
|
|
|
|
const stampRect = await getRect(page, editorSelector);
|
2024-06-27 18:15:45 +02:00
|
|
|
|
|
|
|
expect(
|
|
|
|
["x", "y", "width", "height"].every(
|
|
|
|
key => Math.abs(canvasRect[key] - stampRect[key]) <= 10
|
|
|
|
)
|
|
|
|
).toBeTrue();
|
|
|
|
}
|
2024-05-13 16:14:50 +02:00
|
|
|
});
|
|
|
|
});
|
2024-06-18 17:55:55 +02:00
|
|
|
|
|
|
|
describe("Add a stamp in odd spread mode", () => {
|
|
|
|
let pages;
|
|
|
|
|
2025-04-13 14:19:24 +02:00
|
|
|
beforeEach(async () => {
|
2024-06-18 17:55:55 +02:00
|
|
|
pages = await loadAndWait(
|
|
|
|
"empty.pdf",
|
|
|
|
".annotationEditorLayer",
|
|
|
|
null,
|
|
|
|
null,
|
|
|
|
{
|
|
|
|
spreadModeOnLoad: 1,
|
|
|
|
}
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
2025-04-13 14:19:24 +02:00
|
|
|
afterEach(async () => {
|
2024-06-18 17:55:55 +02:00
|
|
|
await closePages(pages);
|
|
|
|
});
|
|
|
|
|
|
|
|
it("must check that the stamp has its canvas at the right position", async () => {
|
2024-06-27 18:15:45 +02:00
|
|
|
// Run sequentially to avoid clipboard issues.
|
|
|
|
for (const [, page] of pages) {
|
|
|
|
await switchToStamp(page);
|
|
|
|
|
2025-01-19 18:59:55 +01:00
|
|
|
const editorSelector = getEditorSelector(0);
|
|
|
|
await copyImage(page, "../images/firefox_logo.png", editorSelector);
|
|
|
|
await page.waitForSelector(editorSelector);
|
2024-06-27 18:15:45 +02:00
|
|
|
await waitForSerialized(page, 1);
|
|
|
|
|
2025-01-19 18:59:55 +01:00
|
|
|
const canvasRect = await getRect(page, `${editorSelector} canvas`);
|
|
|
|
const stampRect = await getRect(page, editorSelector);
|
2024-06-27 18:15:45 +02:00
|
|
|
|
|
|
|
expect(
|
|
|
|
["x", "y", "width", "height"].every(
|
|
|
|
key => Math.abs(canvasRect[key] - stampRect[key]) <= 10
|
|
|
|
)
|
|
|
|
).toBeTrue();
|
|
|
|
}
|
2024-06-18 17:55:55 +02:00
|
|
|
});
|
|
|
|
});
|
2024-06-19 17:20:42 +02:00
|
|
|
|
|
|
|
describe("Copy and paste a stamp with an alt text", () => {
|
|
|
|
let pages;
|
|
|
|
|
2025-04-13 14:19:24 +02:00
|
|
|
beforeEach(async () => {
|
2024-06-19 17:20:42 +02:00
|
|
|
pages = await loadAndWait("empty.pdf", ".annotationEditorLayer");
|
|
|
|
});
|
|
|
|
|
2025-04-13 14:19:24 +02:00
|
|
|
afterEach(async () => {
|
2024-06-19 17:20:42 +02:00
|
|
|
await closePages(pages);
|
|
|
|
});
|
|
|
|
|
|
|
|
it("must check that the pasted image has an alt text", async () => {
|
2024-06-27 18:15:45 +02:00
|
|
|
// Run sequentially to avoid clipboard issues.
|
|
|
|
for (const [browserName, page] of pages) {
|
|
|
|
await switchToStamp(page);
|
|
|
|
|
2025-01-19 18:59:55 +01:00
|
|
|
const editorSelector = getEditorSelector(0);
|
|
|
|
await copyImage(page, "../images/firefox_logo.png", editorSelector);
|
|
|
|
await page.waitForSelector(editorSelector);
|
2024-06-27 18:15:45 +02:00
|
|
|
await waitForSerialized(page, 1);
|
|
|
|
await applyFunctionToEditor(page, "pdfjs_internal_editor_0", editor => {
|
|
|
|
editor.altTextData = {
|
|
|
|
altText: "Hello World",
|
|
|
|
decorative: false,
|
|
|
|
};
|
|
|
|
});
|
2025-01-19 18:59:55 +01:00
|
|
|
await page.waitForSelector(`${editorSelector} .altText.done`);
|
2024-06-27 18:15:45 +02:00
|
|
|
|
|
|
|
await copy(page);
|
|
|
|
await paste(page);
|
|
|
|
await page.waitForSelector(`${getEditorSelector(1)} .altText.done`);
|
|
|
|
await waitForSerialized(page, 2);
|
|
|
|
|
|
|
|
const serialized = await getSerialized(
|
|
|
|
page,
|
|
|
|
x => x.accessibilityData?.alt
|
|
|
|
);
|
|
|
|
|
|
|
|
expect(serialized)
|
|
|
|
.withContext(`In ${browserName}`)
|
|
|
|
.toEqual(["Hello World", "Hello World"]);
|
|
|
|
}
|
2024-06-19 17:20:42 +02:00
|
|
|
});
|
|
|
|
});
|
2024-08-08 15:54:26 +02:00
|
|
|
|
|
|
|
describe("New alt-text flow", () => {
|
|
|
|
let pages;
|
|
|
|
|
2025-04-13 14:19:24 +02:00
|
|
|
beforeEach(async () => {
|
2024-08-08 15:54:26 +02:00
|
|
|
pages = await loadAndWait(
|
|
|
|
"empty.pdf",
|
|
|
|
".annotationEditorLayer",
|
|
|
|
null,
|
2024-08-18 15:21:02 +02:00
|
|
|
{
|
|
|
|
eventBusSetup: eventBus => {
|
|
|
|
eventBus.on("annotationeditoruimanager", ({ uiManager }) => {
|
|
|
|
window.uiManager = uiManager;
|
|
|
|
});
|
2024-12-03 16:11:41 +01:00
|
|
|
eventBus.on("reporttelemetry", ({ details }) => {
|
|
|
|
(window.telemetry ||= []).push(structuredClone(details));
|
|
|
|
});
|
2024-08-18 15:21:02 +02:00
|
|
|
},
|
|
|
|
},
|
2024-08-08 15:54:26 +02:00
|
|
|
{
|
|
|
|
enableAltText: true,
|
|
|
|
enableUpdatedAddImage: true,
|
|
|
|
enableGuessAltText: true,
|
|
|
|
}
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
|
|
|
afterEach(async () => {
|
|
|
|
await closePages(pages);
|
|
|
|
});
|
|
|
|
|
|
|
|
it("must check the new alt text flow (part 1)", async () => {
|
|
|
|
// Run sequentially to avoid clipboard issues.
|
|
|
|
for (const [browserName, page] of pages) {
|
|
|
|
await switchToStamp(page);
|
|
|
|
|
|
|
|
// Add an image.
|
|
|
|
const editorSelector = getEditorSelector(0);
|
2025-01-19 18:59:55 +01:00
|
|
|
await copyImage(page, "../images/firefox_logo.png", editorSelector);
|
2024-08-08 15:54:26 +02:00
|
|
|
await page.waitForSelector(editorSelector);
|
|
|
|
await waitForSerialized(page, 1);
|
|
|
|
|
|
|
|
// Wait for the dialog to be visible.
|
|
|
|
await page.waitForSelector("#newAltTextDialog", { visible: true });
|
|
|
|
// Wait for the spinner to be visible.
|
|
|
|
await page.waitForSelector("#newAltTextDescriptionContainer.loading");
|
|
|
|
// Check we've the disclaimer.
|
|
|
|
await page.waitForSelector("#newAltTextDisclaimer", { visible: true });
|
|
|
|
|
|
|
|
// Check that the dialog has the correct title: "Edit..."
|
|
|
|
await page.waitForFunction(
|
|
|
|
"document.getElementById('newAltTextTitle').textContent.startsWith('Edit')"
|
|
|
|
);
|
|
|
|
|
|
|
|
// Check that AI guessed the correct alt text.
|
|
|
|
await page.waitForFunction(
|
|
|
|
`document.getElementById("newAltTextDescriptionTextarea").value ===
|
2024-12-03 16:11:41 +01:00
|
|
|
"Fake alt text."`
|
2024-08-08 15:54:26 +02:00
|
|
|
);
|
|
|
|
|
|
|
|
// Check that the dialog has the correct title: "Edit..."
|
|
|
|
await page.waitForFunction(
|
|
|
|
"document.getElementById('newAltTextTitle').textContent.startsWith('Edit')"
|
|
|
|
);
|
|
|
|
|
|
|
|
// Check we've the disclaimer.
|
|
|
|
await page.waitForSelector("#newAltTextDisclaimer", { visible: true });
|
|
|
|
|
|
|
|
// Clear the input and check that the title changes to "Add..."
|
|
|
|
await clearInput(
|
|
|
|
page,
|
|
|
|
"#newAltTextDescriptionTextarea",
|
|
|
|
/* waitForInputEvent = */ true
|
|
|
|
);
|
|
|
|
await page.waitForFunction(
|
|
|
|
"document.getElementById('newAltTextTitle').textContent.startsWith('Add')"
|
|
|
|
);
|
|
|
|
|
|
|
|
// Check we haven't the disclaimer.
|
|
|
|
await page.waitForSelector("#newAltTextDisclaimer", { visible: false });
|
|
|
|
|
|
|
|
// Add a new alt text and check that the title changes to "Edit..."
|
|
|
|
await page.type("#newAltTextDescriptionTextarea", "Hello World");
|
|
|
|
await page.waitForFunction(
|
|
|
|
"document.getElementById('newAltTextTitle').textContent.startsWith('Edit')"
|
|
|
|
);
|
|
|
|
|
|
|
|
// Check we haven't the disclaimer after the modification.
|
|
|
|
await page.waitForSelector("#newAltTextDisclaimer", { visible: false });
|
|
|
|
|
|
|
|
// Click on the Not Now button.
|
|
|
|
await page.click("#newAltTextNotNow");
|
|
|
|
await page.waitForSelector("#newAltTextDialog", { visible: false });
|
|
|
|
await waitForSelectedEditor(page, editorSelector);
|
|
|
|
|
|
|
|
// Wait for the alt-text button to be visible.
|
|
|
|
const buttonSelector = `${editorSelector} button.altText.new`;
|
|
|
|
await page.waitForSelector(buttonSelector, { visible: true });
|
|
|
|
|
2024-10-28 14:34:12 +01:00
|
|
|
await waitForTranslation(page);
|
2024-08-08 15:54:26 +02:00
|
|
|
// Check the text in the button.
|
|
|
|
let text = await page.evaluate(
|
|
|
|
sel => document.querySelector(sel).textContent,
|
|
|
|
buttonSelector
|
|
|
|
);
|
|
|
|
let ariaLabel = await page.evaluate(
|
|
|
|
sel => document.querySelector(sel).getAttribute("aria-label"),
|
|
|
|
buttonSelector
|
|
|
|
);
|
|
|
|
expect(text === ariaLabel && text)
|
|
|
|
.withContext(`In ${browserName}`)
|
|
|
|
.toEqual("Review alt text");
|
|
|
|
|
|
|
|
// Unselect and select the editor and check that the badge is visible.
|
2025-01-18 20:00:23 +01:00
|
|
|
await unselectEditor(page, editorSelector);
|
2024-08-08 15:54:26 +02:00
|
|
|
await page.waitForSelector(".editToolbar", { visible: false });
|
|
|
|
await page.waitForSelector(".noAltTextBadge", { visible: true });
|
|
|
|
|
|
|
|
await page.evaluate(() => {
|
|
|
|
window.uiManager.selectAll();
|
|
|
|
});
|
|
|
|
await waitForSelectedEditor(page, editorSelector);
|
|
|
|
await page.waitForSelector(".editToolbar", { visible: true });
|
|
|
|
await page.waitForSelector(".noAltTextBadge", { visible: false });
|
|
|
|
|
|
|
|
// Click on the Review button.
|
|
|
|
await page.click(buttonSelector);
|
|
|
|
await page.waitForSelector("#newAltTextDialog", { visible: true });
|
|
|
|
|
|
|
|
// Check that the dialog has the correct title: "Edit..."
|
|
|
|
await page.waitForFunction(
|
|
|
|
"document.getElementById('newAltTextTitle').textContent.startsWith('Edit')"
|
|
|
|
);
|
|
|
|
|
|
|
|
// Click on create automatically toggle button.
|
|
|
|
await page.click("#newAltTextCreateAutomaticallyButton");
|
|
|
|
await clearInput(
|
|
|
|
page,
|
|
|
|
"#newAltTextDescriptionTextarea",
|
|
|
|
/* waitForInputEvent = */ true
|
|
|
|
);
|
|
|
|
|
|
|
|
// Save the empty text.
|
|
|
|
await page.click("#newAltTextSave");
|
|
|
|
await page.waitForSelector("#newAltTextDialog", { visible: false });
|
|
|
|
await waitForSelectedEditor(page, editorSelector);
|
|
|
|
await page.waitForSelector(buttonSelector, { visible: true });
|
|
|
|
|
2024-10-28 14:34:12 +01:00
|
|
|
await waitForTranslation(page);
|
2024-08-08 15:54:26 +02:00
|
|
|
// Check the text in the button.
|
|
|
|
text = await page.evaluate(
|
|
|
|
sel => document.querySelector(sel).textContent,
|
|
|
|
buttonSelector
|
|
|
|
);
|
|
|
|
ariaLabel = await page.evaluate(
|
|
|
|
sel => document.querySelector(sel).getAttribute("aria-label"),
|
|
|
|
buttonSelector
|
|
|
|
);
|
|
|
|
expect(text === ariaLabel && text)
|
|
|
|
.withContext(`In ${browserName}`)
|
|
|
|
.toEqual("Missing alt text");
|
|
|
|
|
|
|
|
// Unselect and select the editor and check that the badge is visible.
|
2025-01-18 20:00:23 +01:00
|
|
|
await unselectEditor(page, editorSelector);
|
2024-08-08 15:54:26 +02:00
|
|
|
await page.waitForSelector(".editToolbar", { visible: false });
|
|
|
|
await page.waitForSelector(".noAltTextBadge", { visible: true });
|
|
|
|
await page.evaluate(() => {
|
|
|
|
window.uiManager.selectAll();
|
|
|
|
});
|
|
|
|
await waitForSelectedEditor(page, editorSelector);
|
|
|
|
await page.waitForSelector(".editToolbar", { visible: true });
|
|
|
|
await page.waitForSelector(".noAltTextBadge", { visible: false });
|
|
|
|
|
|
|
|
// Click on the Review button.
|
|
|
|
await page.click(buttonSelector);
|
|
|
|
await page.waitForSelector("#newAltTextDialog", { visible: true });
|
|
|
|
|
|
|
|
await page.waitForFunction(
|
|
|
|
"document.getElementById('newAltTextTitle').textContent.startsWith('Add')"
|
|
|
|
);
|
|
|
|
// Add a new alt text and check that the title changes to "Edit..."
|
|
|
|
await page.type("#newAltTextDescriptionTextarea", "Hello World");
|
|
|
|
await page.waitForFunction(
|
|
|
|
"document.getElementById('newAltTextTitle').textContent.startsWith('Edit')"
|
|
|
|
);
|
|
|
|
|
|
|
|
// Click on the Save button.
|
|
|
|
await page.click("#newAltTextSave");
|
|
|
|
await page.waitForSelector("#newAltTextDialog", { visible: false });
|
|
|
|
|
2024-10-28 14:34:12 +01:00
|
|
|
await waitForTranslation(page);
|
2024-08-08 15:54:26 +02:00
|
|
|
// Check the text in the button.
|
|
|
|
text = await page.evaluate(
|
|
|
|
sel => document.querySelector(sel).firstChild.textContent,
|
|
|
|
buttonSelector
|
|
|
|
);
|
|
|
|
ariaLabel = await page.evaluate(
|
|
|
|
sel => document.querySelector(sel).getAttribute("aria-label"),
|
|
|
|
buttonSelector
|
|
|
|
);
|
|
|
|
expect(text === ariaLabel && text)
|
|
|
|
.withContext(`In ${browserName}`)
|
|
|
|
.toEqual("Alt text added");
|
|
|
|
|
|
|
|
await page.hover(buttonSelector);
|
|
|
|
|
|
|
|
// Wait for the tooltip to be visible.
|
|
|
|
const tooltipSelector = `${buttonSelector} .tooltip`;
|
|
|
|
await page.waitForSelector(tooltipSelector, { visible: true });
|
|
|
|
|
|
|
|
const tooltipText = await page.evaluate(
|
|
|
|
sel => document.querySelector(`${sel}`).textContent,
|
|
|
|
tooltipSelector
|
|
|
|
);
|
|
|
|
expect(tooltipText).toEqual("Hello World");
|
2024-08-30 22:19:12 +02:00
|
|
|
|
|
|
|
// Click on the Review button.
|
|
|
|
await page.click(buttonSelector);
|
|
|
|
await page.waitForSelector("#newAltTextDialog", { visible: true });
|
|
|
|
await page.click("#newAltTextCreateAutomaticallyButton");
|
|
|
|
await page.click("#newAltTextCancel");
|
|
|
|
await page.waitForSelector("#newAltTextDialog", { visible: false });
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
it("must check the new alt text flow (part 2)", async () => {
|
|
|
|
// Run sequentially to avoid clipboard issues.
|
|
|
|
for (const [, page] of pages) {
|
|
|
|
await switchToStamp(page);
|
|
|
|
|
|
|
|
// Add an image.
|
|
|
|
const editorSelector = getEditorSelector(0);
|
2025-01-19 18:59:55 +01:00
|
|
|
await copyImage(page, "../images/firefox_logo.png", editorSelector);
|
2024-08-30 22:19:12 +02:00
|
|
|
await page.waitForSelector(editorSelector);
|
|
|
|
await waitForSerialized(page, 1);
|
|
|
|
|
|
|
|
// Wait for the dialog to be visible.
|
|
|
|
await page.waitForSelector("#newAltTextDialog", { visible: true });
|
|
|
|
|
|
|
|
// Wait for the spinner to be visible.
|
|
|
|
await page.waitForSelector("#newAltTextDescriptionContainer.loading");
|
|
|
|
|
|
|
|
// Check we've the disclaimer.
|
|
|
|
await page.waitForSelector("#newAltTextDisclaimer", { visible: true });
|
|
|
|
|
|
|
|
// Click in the textarea in order to stop the guessing.
|
|
|
|
await page.click("#newAltTextDescriptionTextarea");
|
|
|
|
await page.waitForFunction(() =>
|
|
|
|
document
|
|
|
|
.getElementById("newAltTextTitle")
|
|
|
|
.textContent.startsWith("Add ")
|
|
|
|
);
|
|
|
|
|
|
|
|
// Check we haven't the disclaimer.
|
|
|
|
await page.waitForSelector("#newAltTextDisclaimer", { visible: false });
|
|
|
|
|
|
|
|
// Click on the Not Now button.
|
|
|
|
await page.click("#newAltTextNotNow");
|
|
|
|
await page.waitForSelector("#newAltTextDialog", { visible: false });
|
2024-08-08 15:54:26 +02:00
|
|
|
}
|
|
|
|
});
|
2024-09-09 21:28:59 +02:00
|
|
|
|
|
|
|
it("must check the new alt text flow (part 3)", async () => {
|
|
|
|
// Run sequentially to avoid clipboard issues.
|
|
|
|
for (const [, page] of pages) {
|
|
|
|
await page.evaluate(() => {
|
|
|
|
window.PDFViewerApplication.mlManager.enableAltTextModelDownload = false;
|
|
|
|
});
|
|
|
|
|
|
|
|
await switchToStamp(page);
|
|
|
|
|
|
|
|
// Add an image.
|
|
|
|
const editorSelector = getEditorSelector(0);
|
2025-01-19 18:59:55 +01:00
|
|
|
await copyImage(page, "../images/firefox_logo.png", editorSelector);
|
2024-09-09 21:28:59 +02:00
|
|
|
await page.waitForSelector(editorSelector);
|
|
|
|
await waitForSerialized(page, 1);
|
|
|
|
|
|
|
|
// Wait for the dialog to be visible.
|
|
|
|
await page.waitForSelector("#newAltTextDialog", { visible: true });
|
|
|
|
|
|
|
|
// Check we haven't the disclaimer.
|
|
|
|
await page.waitForSelector("#newAltTextDisclaimer[hidden]");
|
|
|
|
}
|
|
|
|
});
|
2024-12-03 16:11:41 +01:00
|
|
|
|
|
|
|
it("must check that the data in telemetry are correct", async () => {
|
|
|
|
// Run sequentially to avoid clipboard issues.
|
|
|
|
for (const [browserName, page] of pages) {
|
|
|
|
await page.evaluate(() => {
|
|
|
|
window.PDFViewerApplication.mlManager.enableAltTextModelDownload = true;
|
|
|
|
});
|
|
|
|
await switchToStamp(page);
|
|
|
|
|
|
|
|
// Add an image.
|
|
|
|
const editorSelector = getEditorSelector(0);
|
2025-01-19 18:59:55 +01:00
|
|
|
await copyImage(page, "../images/firefox_logo.png", editorSelector);
|
2024-12-03 16:11:41 +01:00
|
|
|
await page.waitForSelector(editorSelector);
|
|
|
|
await waitForSerialized(page, 1);
|
|
|
|
|
|
|
|
// Wait for the dialog to be visible.
|
|
|
|
await page.waitForSelector("#newAltTextDialog", { visible: true });
|
|
|
|
|
|
|
|
// Check that AI guessed the correct alt text.
|
|
|
|
await page.waitForFunction(
|
|
|
|
`document.getElementById("newAltTextDescriptionTextarea").value ===
|
|
|
|
"Fake alt text."`
|
|
|
|
);
|
|
|
|
// Clear the input and check that the title changes to "Add..."
|
|
|
|
await clearInput(
|
|
|
|
page,
|
|
|
|
"#newAltTextDescriptionTextarea",
|
|
|
|
/* waitForInputEvent = */ true
|
|
|
|
);
|
|
|
|
// Save the empty text.
|
|
|
|
await page.click("#newAltTextSave");
|
|
|
|
await page.waitForSelector("#newAltTextDialog", { visible: false });
|
|
|
|
|
|
|
|
// Get the telemetry data and clean.
|
|
|
|
let telemetry = await page.evaluate(() => {
|
|
|
|
const tel = window.telemetry;
|
|
|
|
window.telemetry = [];
|
|
|
|
return tel;
|
|
|
|
});
|
|
|
|
let saveTelemetry = telemetry.find(
|
|
|
|
details => details.data.action === "pdfjs.image.alt_text.user_edit"
|
|
|
|
);
|
|
|
|
expect(saveTelemetry.data.data)
|
|
|
|
.withContext(`In ${browserName}`)
|
|
|
|
.toEqual({
|
|
|
|
total_words: 3,
|
|
|
|
words_removed: 3,
|
|
|
|
words_added: 0,
|
|
|
|
});
|
|
|
|
|
|
|
|
// Click on the Review button.
|
|
|
|
const buttonSelector = `${editorSelector} button.altText.new`;
|
|
|
|
await page.waitForSelector(buttonSelector, { visible: true });
|
|
|
|
await page.click(buttonSelector);
|
|
|
|
await page.waitForSelector("#newAltTextDialog", { visible: true });
|
|
|
|
|
|
|
|
// Add a new alt text and check that the title changes to "Edit..."
|
|
|
|
await page.type("#newAltTextDescriptionTextarea", "Fake text alt foo.");
|
|
|
|
|
|
|
|
// Save the empty text.
|
|
|
|
await page.click("#newAltTextSave");
|
|
|
|
await page.waitForSelector("#newAltTextDialog", { visible: false });
|
|
|
|
|
|
|
|
telemetry = await page.evaluate(() => window.telemetry);
|
|
|
|
saveTelemetry = telemetry.find(
|
|
|
|
details => details.data.action === "pdfjs.image.alt_text.user_edit"
|
|
|
|
);
|
|
|
|
expect(saveTelemetry.data.data)
|
|
|
|
.withContext(`In ${browserName}`)
|
|
|
|
.toEqual({
|
|
|
|
total_words: 3,
|
|
|
|
words_removed: 0,
|
|
|
|
words_added: 1,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
});
|
2024-08-08 15:54:26 +02:00
|
|
|
});
|
2024-09-09 15:26:33 +02:00
|
|
|
|
2024-09-23 19:46:32 +02:00
|
|
|
describe("New alt-text flow (bug 1920515)", () => {
|
|
|
|
let pages;
|
|
|
|
|
2025-04-13 14:19:24 +02:00
|
|
|
beforeEach(async () => {
|
2024-09-23 19:46:32 +02:00
|
|
|
pages = await loadAndWait(
|
|
|
|
"empty.pdf",
|
|
|
|
".annotationEditorLayer",
|
|
|
|
null,
|
|
|
|
{
|
|
|
|
eventBusSetup: eventBus => {
|
|
|
|
eventBus.on("annotationeditoruimanager", ({ uiManager }) => {
|
|
|
|
window.uiManager = uiManager;
|
|
|
|
});
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
enableAltText: false,
|
|
|
|
enableFakeMLManager: false,
|
|
|
|
enableUpdatedAddImage: true,
|
|
|
|
enableGuessAltText: true,
|
|
|
|
}
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
|
|
|
afterEach(async () => {
|
|
|
|
await closePages(pages);
|
|
|
|
});
|
|
|
|
|
|
|
|
it("must check that the toggle button isn't displayed when there is no AI", async () => {
|
|
|
|
// Run sequentially to avoid clipboard issues.
|
|
|
|
for (const [, page] of pages) {
|
|
|
|
await switchToStamp(page);
|
|
|
|
|
|
|
|
// Add an image.
|
|
|
|
const editorSelector = getEditorSelector(0);
|
2025-01-19 18:59:55 +01:00
|
|
|
await copyImage(page, "../images/firefox_logo.png", editorSelector);
|
2024-09-23 19:46:32 +02:00
|
|
|
await page.waitForSelector(editorSelector);
|
|
|
|
await waitForSerialized(page, 1);
|
|
|
|
|
|
|
|
// Wait for the dialog to be visible.
|
|
|
|
await page.waitForSelector("#newAltTextDialog.noAi", { visible: true });
|
|
|
|
|
|
|
|
// enableFakeMLManager is false, so it means that we don't have ML but
|
|
|
|
// we're using the new flow, hence we don't want to have the toggle
|
|
|
|
// button.
|
|
|
|
await page.waitForSelector("#newAltTextCreateAutomatically", {
|
|
|
|
hidden: true,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2024-09-09 15:26:33 +02:00
|
|
|
describe("No auto-resize", () => {
|
|
|
|
let pages;
|
|
|
|
|
2025-04-13 14:19:24 +02:00
|
|
|
beforeEach(async () => {
|
2024-09-09 15:26:33 +02:00
|
|
|
pages = await loadAndWait("empty.pdf", ".annotationEditorLayer", 67);
|
|
|
|
});
|
|
|
|
|
2025-04-13 14:19:24 +02:00
|
|
|
afterEach(async () => {
|
2024-09-09 15:26:33 +02:00
|
|
|
await closePages(pages);
|
|
|
|
});
|
|
|
|
|
|
|
|
it("must check that a stamp editor isn't resizing itself", async () => {
|
|
|
|
// Run sequentially to avoid clipboard issues.
|
|
|
|
const editorSelector = getEditorSelector(0);
|
|
|
|
|
|
|
|
for (const [, page] of pages) {
|
|
|
|
await switchToStamp(page);
|
|
|
|
|
2025-01-19 18:59:55 +01:00
|
|
|
await copyImage(page, "../images/firefox_logo.png", editorSelector);
|
2024-09-09 15:26:33 +02:00
|
|
|
await page.waitForSelector(editorSelector);
|
|
|
|
await waitForSerialized(page, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
await Promise.all(
|
|
|
|
pages.map(async ([browserName, page]) => {
|
|
|
|
const getDims = () =>
|
|
|
|
page.evaluate(sel => {
|
|
|
|
const bbox = document.querySelector(sel).getBoundingClientRect();
|
|
|
|
return `${bbox.width}::${bbox.height}`;
|
|
|
|
}, editorSelector);
|
|
|
|
const initialDims = await getDims();
|
|
|
|
for (let i = 0; i < 50; i++) {
|
|
|
|
// We want to make sure that the editor doesn't resize itself, so we
|
|
|
|
// check every 10ms that the dimensions are the same.
|
|
|
|
|
|
|
|
// eslint-disable-next-line no-restricted-syntax
|
|
|
|
await waitForTimeout(10);
|
|
|
|
|
|
|
|
const dims = await getDims();
|
|
|
|
expect(dims).withContext(`In ${browserName}`).toEqual(initialDims);
|
|
|
|
}
|
|
|
|
})
|
|
|
|
);
|
|
|
|
});
|
|
|
|
});
|
2024-09-25 17:09:24 +02:00
|
|
|
|
|
|
|
describe("A stamp musn't be on top of the secondary toolbar", () => {
|
|
|
|
let pages;
|
|
|
|
|
2025-04-13 14:19:24 +02:00
|
|
|
beforeEach(async () => {
|
2024-09-25 17:09:24 +02:00
|
|
|
pages = await loadAndWait("empty.pdf", ".annotationEditorLayer", 600);
|
|
|
|
});
|
|
|
|
|
2025-04-13 14:19:24 +02:00
|
|
|
afterEach(async () => {
|
2024-09-25 17:09:24 +02:00
|
|
|
await closePages(pages);
|
|
|
|
});
|
|
|
|
|
|
|
|
it("must check that a stamp editor isn't on top of the secondary toolbar", async () => {
|
|
|
|
// Run sequentially to avoid clipboard issues.
|
|
|
|
for (const [, page] of pages) {
|
|
|
|
await switchToStamp(page);
|
|
|
|
|
2025-01-19 18:59:55 +01:00
|
|
|
const editorSelector = getEditorSelector(0);
|
|
|
|
await copyImage(page, "../images/red.png", editorSelector);
|
2024-09-25 17:09:24 +02:00
|
|
|
await page.waitForSelector(editorSelector);
|
|
|
|
await waitForSerialized(page, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
await Promise.all(
|
|
|
|
pages.map(async ([browserName, page]) => {
|
|
|
|
await page.click("#secondaryToolbarToggleButton");
|
|
|
|
await page.waitForSelector("#secondaryToolbar", { visible: true });
|
|
|
|
const secondary = await page.$("#secondaryToolbar");
|
2025-04-13 19:19:13 +02:00
|
|
|
const png = await secondary.screenshot({ type: "png" });
|
2024-09-25 17:09:24 +02:00
|
|
|
const secondaryImage = PNG.sync.read(Buffer.from(png));
|
|
|
|
const buffer = new Uint32Array(secondaryImage.data.buffer);
|
|
|
|
expect(buffer.every(x => x === 0xff0000ff))
|
|
|
|
.withContext(`In ${browserName}`)
|
|
|
|
.toBeFalse();
|
|
|
|
})
|
|
|
|
);
|
|
|
|
});
|
|
|
|
});
|
2024-10-02 14:36:35 +02:00
|
|
|
|
|
|
|
describe("Stamp (move existing)", () => {
|
|
|
|
let pages;
|
|
|
|
|
2025-04-13 14:19:24 +02:00
|
|
|
beforeEach(async () => {
|
2024-10-02 14:36:35 +02:00
|
|
|
pages = await loadAndWait("stamps.pdf", getAnnotationSelector("25R"));
|
|
|
|
});
|
|
|
|
|
2025-04-13 14:19:24 +02:00
|
|
|
afterEach(async () => {
|
2024-10-02 14:36:35 +02:00
|
|
|
await closePages(pages);
|
|
|
|
});
|
|
|
|
|
|
|
|
it("must move an annotation", async () => {
|
|
|
|
await Promise.all(
|
|
|
|
pages.map(async ([browserName, page]) => {
|
2024-12-04 17:57:17 +01:00
|
|
|
const modeChangedHandle = await waitForAnnotationModeChanged(page);
|
2024-10-02 14:36:35 +02:00
|
|
|
await page.click(getAnnotationSelector("25R"), { count: 2 });
|
2024-12-04 17:57:17 +01:00
|
|
|
await awaitPromise(modeChangedHandle);
|
2024-12-09 16:07:39 +01:00
|
|
|
const editorSelector = getEditorSelector(0);
|
|
|
|
await waitForSelectedEditor(page, editorSelector);
|
2024-10-02 14:36:35 +02:00
|
|
|
|
|
|
|
const editorIds = await getEditors(page, "stamp");
|
|
|
|
expect(editorIds.length).withContext(`In ${browserName}`).toEqual(5);
|
|
|
|
|
|
|
|
// All the current annotations should be serialized as null objects
|
|
|
|
// because they haven't been edited yet.
|
|
|
|
const serialized = await getSerialized(page);
|
|
|
|
expect(serialized).withContext(`In ${browserName}`).toEqual([]);
|
|
|
|
|
|
|
|
// Select the annotation we want to move.
|
2025-01-18 19:21:55 +01:00
|
|
|
await selectEditor(page, editorSelector);
|
2024-10-02 14:36:35 +02:00
|
|
|
|
2024-12-09 16:07:39 +01:00
|
|
|
await dragAndDrop(page, editorSelector, [[100, 100]]);
|
2024-10-02 14:36:35 +02:00
|
|
|
await waitForSerialized(page, 1);
|
|
|
|
})
|
|
|
|
);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
describe("Stamp (change alt-text)", () => {
|
|
|
|
let pages;
|
|
|
|
|
2025-04-13 14:19:24 +02:00
|
|
|
beforeEach(async () => {
|
2024-10-02 14:36:35 +02:00
|
|
|
pages = await loadAndWait("stamps.pdf", getAnnotationSelector("58R"));
|
|
|
|
});
|
|
|
|
|
2025-04-13 14:19:24 +02:00
|
|
|
afterEach(async () => {
|
2024-10-02 14:36:35 +02:00
|
|
|
await closePages(pages);
|
|
|
|
});
|
|
|
|
|
|
|
|
it("must update an existing alt-text", async () => {
|
|
|
|
await Promise.all(
|
|
|
|
pages.map(async ([browserName, page]) => {
|
2024-12-04 17:57:17 +01:00
|
|
|
const modeChangedHandle = await waitForAnnotationModeChanged(page);
|
2024-10-02 14:36:35 +02:00
|
|
|
await page.click(getAnnotationSelector("58R"), { count: 2 });
|
2024-12-04 17:57:17 +01:00
|
|
|
await awaitPromise(modeChangedHandle);
|
2025-01-19 18:59:55 +01:00
|
|
|
const editorSelector = getEditorSelector(4);
|
|
|
|
await waitForSelectedEditor(page, editorSelector);
|
2024-10-02 14:36:35 +02:00
|
|
|
|
|
|
|
const editorIds = await getEditors(page, "stamp");
|
|
|
|
expect(editorIds.length).withContext(`In ${browserName}`).toEqual(5);
|
|
|
|
|
2025-01-19 18:59:55 +01:00
|
|
|
await page.click(`${editorSelector} button.altText`);
|
2024-10-02 14:36:35 +02:00
|
|
|
await page.waitForSelector("#altTextDialog", { visible: true });
|
|
|
|
|
|
|
|
const textareaSelector = "#altTextDialog textarea";
|
|
|
|
await page.waitForFunction(
|
|
|
|
sel => document.querySelector(sel).value !== "",
|
|
|
|
{},
|
|
|
|
textareaSelector
|
|
|
|
);
|
|
|
|
|
|
|
|
const altText = await page.evaluate(
|
|
|
|
sel => document.querySelector(sel).value,
|
|
|
|
textareaSelector
|
|
|
|
);
|
|
|
|
expect(altText).toEqual("An elephant");
|
|
|
|
|
|
|
|
await page.evaluate(sel => {
|
|
|
|
document.querySelector(sel).value = "";
|
|
|
|
}, textareaSelector);
|
|
|
|
|
|
|
|
await page.click(textareaSelector);
|
|
|
|
await page.type(textareaSelector, "Hello World");
|
|
|
|
|
|
|
|
// All the current annotations should be serialized as null objects
|
|
|
|
// because they haven't been edited yet.
|
|
|
|
const serialized = await getSerialized(page);
|
|
|
|
expect(serialized).withContext(`In ${browserName}`).toEqual([]);
|
|
|
|
|
|
|
|
const saveButtonSelector = "#altTextDialog #altTextSave";
|
|
|
|
await page.click(saveButtonSelector);
|
|
|
|
|
|
|
|
await waitForSerialized(page, 1);
|
|
|
|
})
|
|
|
|
);
|
|
|
|
});
|
|
|
|
});
|
2024-10-04 17:07:25 +02:00
|
|
|
|
|
|
|
describe("Stamp (delete existing and undo)", () => {
|
|
|
|
let pages;
|
|
|
|
|
2025-04-13 14:19:24 +02:00
|
|
|
beforeEach(async () => {
|
2024-10-04 17:07:25 +02:00
|
|
|
pages = await loadAndWait("stamps.pdf", getAnnotationSelector("37R"));
|
|
|
|
});
|
|
|
|
|
2025-04-13 14:19:24 +02:00
|
|
|
afterEach(async () => {
|
2024-10-04 17:07:25 +02:00
|
|
|
await closePages(pages);
|
|
|
|
});
|
|
|
|
|
|
|
|
it("must check that the annotation is correctly restored", async () => {
|
|
|
|
await Promise.all(
|
|
|
|
pages.map(async ([browserName, page]) => {
|
2024-12-04 17:57:17 +01:00
|
|
|
const modeChangedHandle = await waitForAnnotationModeChanged(page);
|
2024-10-04 17:07:25 +02:00
|
|
|
await page.click(getAnnotationSelector("37R"), { count: 2 });
|
2024-12-04 17:57:17 +01:00
|
|
|
await awaitPromise(modeChangedHandle);
|
2024-10-04 17:07:25 +02:00
|
|
|
const editorSelector = getEditorSelector(2);
|
|
|
|
await waitForSelectedEditor(page, editorSelector);
|
|
|
|
|
|
|
|
const editorIds = await getEditors(page, "stamp");
|
|
|
|
expect(editorIds.length).withContext(`In ${browserName}`).toEqual(5);
|
|
|
|
|
|
|
|
// All the current annotations should be serialized as null objects
|
|
|
|
// because they haven't been edited yet.
|
|
|
|
let serialized = await getSerialized(page);
|
|
|
|
expect(serialized).withContext(`In ${browserName}`).toEqual([]);
|
|
|
|
|
|
|
|
await page.waitForSelector(`${editorSelector} button.delete`);
|
|
|
|
await page.click(`${editorSelector} button.delete`);
|
|
|
|
|
|
|
|
await waitForSerialized(page, 1);
|
|
|
|
serialized = await getSerialized(page);
|
|
|
|
expect(serialized)
|
|
|
|
.withContext(`In ${browserName}`)
|
|
|
|
.toEqual([
|
|
|
|
{ id: "37R", deleted: true, pageIndex: 0, popupRef: "44R" },
|
|
|
|
]);
|
|
|
|
|
|
|
|
await kbUndo(page);
|
|
|
|
await waitForSerialized(page, 0);
|
|
|
|
|
|
|
|
await waitForSelectedEditor(page, editorSelector);
|
|
|
|
})
|
|
|
|
);
|
|
|
|
});
|
|
|
|
});
|
2024-11-26 15:59:10 +01:00
|
|
|
|
|
|
|
describe("Drag a stamp annotation and click on a touchscreen", () => {
|
|
|
|
let pages;
|
|
|
|
|
2025-04-13 14:19:24 +02:00
|
|
|
beforeEach(async () => {
|
2024-11-26 15:59:10 +01:00
|
|
|
pages = await loadAndWait("empty.pdf", ".annotationEditorLayer");
|
|
|
|
});
|
|
|
|
|
2025-04-13 14:19:24 +02:00
|
|
|
afterEach(async () => {
|
2024-11-26 15:59:10 +01:00
|
|
|
await closePages(pages);
|
|
|
|
});
|
|
|
|
|
|
|
|
it("must check that the annotation isn't unselected when an other finger taps on the screen", async () => {
|
|
|
|
// Run sequentially to avoid clipboard issues.
|
2025-01-19 19:04:06 +01:00
|
|
|
for (const [, page] of pages) {
|
2024-11-26 15:59:10 +01:00
|
|
|
await switchToStamp(page);
|
|
|
|
|
|
|
|
const editorSelector = getEditorSelector(0);
|
2025-01-19 18:59:55 +01:00
|
|
|
await copyImage(page, "../images/firefox_logo.png", editorSelector);
|
2024-11-26 15:59:10 +01:00
|
|
|
const stampRect = await getRect(page, editorSelector);
|
|
|
|
|
|
|
|
await page.touchscreen.tap(stampRect.x + 10, stampRect.y + 10);
|
|
|
|
await waitForSelectedEditor(page, editorSelector);
|
|
|
|
|
|
|
|
await page.touchscreen.touchStart(stampRect.x + 10, stampRect.y + 10);
|
|
|
|
await page.touchscreen.touchMove(stampRect.x + 20, stampRect.y + 20);
|
|
|
|
await page.touchscreen.tap(stampRect.x - 10, stampRect.y - 10);
|
|
|
|
await page.touchscreen.touchEnd();
|
|
|
|
|
|
|
|
await waitForSelectedEditor(page, editorSelector);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
2024-10-19 02:19:46 +05:30
|
|
|
|
|
|
|
describe("Undo deletion popup has the expected behaviour", () => {
|
|
|
|
let pages;
|
|
|
|
|
|
|
|
beforeEach(async () => {
|
|
|
|
pages = await loadAndWait("tracemonkey.pdf", ".annotationEditorLayer");
|
|
|
|
});
|
|
|
|
|
|
|
|
afterEach(async () => {
|
|
|
|
await closePages(pages);
|
|
|
|
});
|
|
|
|
|
|
|
|
it("must check that deleting an image can be undone using the undo button", async () => {
|
|
|
|
await Promise.all(
|
|
|
|
pages.map(async ([browserName, page]) => {
|
|
|
|
await switchToStamp(page);
|
|
|
|
|
2025-01-19 18:59:55 +01:00
|
|
|
const editorSelector = getEditorSelector(0);
|
|
|
|
await copyImage(page, "../images/firefox_logo.png", editorSelector);
|
|
|
|
await page.waitForSelector(editorSelector);
|
2024-10-19 02:19:46 +05:30
|
|
|
await waitForSerialized(page, 1);
|
|
|
|
|
2025-01-19 18:59:55 +01:00
|
|
|
await page.waitForSelector(`${editorSelector} button.delete`);
|
|
|
|
await page.click(`${editorSelector} button.delete`);
|
2024-10-19 02:19:46 +05:30
|
|
|
await waitForSerialized(page, 0);
|
|
|
|
|
|
|
|
await page.waitForSelector("#editorUndoBar:not([hidden])");
|
|
|
|
|
|
|
|
await page.click("#editorUndoBarUndoButton");
|
|
|
|
await waitForSerialized(page, 1);
|
|
|
|
await page.waitForSelector(editorSelector);
|
2025-01-19 18:59:55 +01:00
|
|
|
await page.waitForSelector(`${editorSelector} canvas`);
|
2024-10-19 02:19:46 +05:30
|
|
|
})
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
|
|
|
it("must check that the undo deletion popup displays the correct message", async () => {
|
|
|
|
await Promise.all(
|
|
|
|
pages.map(async ([browserName, page]) => {
|
|
|
|
await switchToStamp(page);
|
|
|
|
|
2025-01-19 18:59:55 +01:00
|
|
|
const editorSelector = getEditorSelector(0);
|
|
|
|
await copyImage(page, "../images/firefox_logo.png", editorSelector);
|
|
|
|
await page.waitForSelector(editorSelector);
|
2024-10-19 02:19:46 +05:30
|
|
|
await waitForSerialized(page, 1);
|
|
|
|
|
2025-01-19 18:59:55 +01:00
|
|
|
await page.waitForSelector(`${editorSelector} button.delete`);
|
|
|
|
await page.click(`${editorSelector} button.delete`);
|
2024-10-19 02:19:46 +05:30
|
|
|
await waitForSerialized(page, 0);
|
|
|
|
|
|
|
|
await page.waitForFunction(() => {
|
|
|
|
const messageElement = document.querySelector(
|
|
|
|
"#editorUndoBarMessage"
|
|
|
|
);
|
|
|
|
return messageElement && messageElement.textContent.trim() !== "";
|
|
|
|
});
|
|
|
|
const message = await page.waitForSelector("#editorUndoBarMessage");
|
|
|
|
const messageText = await page.evaluate(
|
|
|
|
el => el.textContent,
|
|
|
|
message
|
|
|
|
);
|
|
|
|
expect(messageText).toContain("Image removed");
|
|
|
|
})
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
|
|
|
it("must check that the popup disappears when a new image is inserted", async () => {
|
|
|
|
await Promise.all(
|
|
|
|
pages.map(async ([browserName, page]) => {
|
|
|
|
await switchToStamp(page);
|
|
|
|
|
2025-01-19 18:59:55 +01:00
|
|
|
const editorSelector = getEditorSelector(0);
|
|
|
|
await copyImage(page, "../images/firefox_logo.png", editorSelector);
|
|
|
|
await page.waitForSelector(editorSelector);
|
2024-10-19 02:19:46 +05:30
|
|
|
await waitForSerialized(page, 1);
|
|
|
|
|
|
|
|
await page.waitForSelector(`${editorSelector} button.delete`);
|
|
|
|
await page.click(`${editorSelector} button.delete`);
|
|
|
|
await waitForSerialized(page, 0);
|
|
|
|
|
|
|
|
await page.waitForSelector("#editorUndoBar:not([hidden])");
|
|
|
|
await page.click("#editorStampAddImage");
|
|
|
|
const newInput = await page.$("#stampEditorFileInput");
|
|
|
|
await newInput.uploadFile(
|
|
|
|
`${path.join(__dirname, "../images/firefox_logo.png")}`
|
|
|
|
);
|
|
|
|
await waitForImage(page, getEditorSelector(1));
|
|
|
|
await waitForSerialized(page, 1);
|
|
|
|
await page.waitForSelector("#editorUndoBar", { hidden: true });
|
|
|
|
})
|
|
|
|
);
|
|
|
|
});
|
|
|
|
});
|
2025-01-09 18:10:47 +01:00
|
|
|
|
|
|
|
describe("Switch to edit mode a pdf with an existing stamp annotation on an invisible and rendered page", () => {
|
|
|
|
let pages;
|
|
|
|
|
2025-04-13 14:19:24 +02:00
|
|
|
beforeEach(async () => {
|
2025-01-09 18:10:47 +01:00
|
|
|
pages = await loadAndWait("issue19239.pdf", ".annotationEditorLayer");
|
|
|
|
});
|
|
|
|
|
2025-04-13 14:19:24 +02:00
|
|
|
afterEach(async () => {
|
2025-01-09 18:10:47 +01:00
|
|
|
await closePages(pages);
|
|
|
|
});
|
|
|
|
|
|
|
|
it("must move on the second page", async () => {
|
|
|
|
await Promise.all(
|
|
|
|
pages.map(async ([, page]) => {
|
|
|
|
const pageOneSelector = `.page[data-page-number = "1"]`;
|
|
|
|
const pageTwoSelector = `.page[data-page-number = "2"]`;
|
|
|
|
await scrollIntoView(page, pageTwoSelector);
|
2025-02-02 17:02:24 +01:00
|
|
|
await page.waitForSelector(pageOneSelector, { visible: false });
|
2025-01-09 18:10:47 +01:00
|
|
|
|
|
|
|
await switchToStamp(page);
|
|
|
|
await scrollIntoView(page, pageOneSelector);
|
|
|
|
await page.waitForSelector(
|
|
|
|
`${pageOneSelector} .annotationEditorLayer canvas`,
|
|
|
|
{ visible: true }
|
|
|
|
);
|
|
|
|
})
|
|
|
|
);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
describe("Switch to edit mode a pdf with an existing stamp annotation on an invisible and unrendered page", () => {
|
|
|
|
let pages;
|
|
|
|
|
2025-04-13 14:19:24 +02:00
|
|
|
beforeEach(async () => {
|
2025-01-09 18:10:47 +01:00
|
|
|
pages = await loadAndWait("issue19239.pdf", ".annotationEditorLayer");
|
|
|
|
});
|
|
|
|
|
2025-04-13 14:19:24 +02:00
|
|
|
afterEach(async () => {
|
2025-01-09 18:10:47 +01:00
|
|
|
await closePages(pages);
|
|
|
|
});
|
|
|
|
|
|
|
|
it("must move on the last page", async () => {
|
|
|
|
await Promise.all(
|
|
|
|
pages.map(async ([, page]) => {
|
|
|
|
const twoToFourteen = Array.from(new Array(13).keys(), n => n + 2);
|
|
|
|
for (const pageNumber of twoToFourteen) {
|
|
|
|
const pageSelector = `.page[data-page-number = "${pageNumber}"]`;
|
|
|
|
await scrollIntoView(page, pageSelector);
|
|
|
|
}
|
|
|
|
|
|
|
|
await switchToStamp(page);
|
|
|
|
|
|
|
|
const thirteenToOne = Array.from(new Array(13).keys(), n => 13 - n);
|
|
|
|
for (const pageNumber of thirteenToOne) {
|
|
|
|
const pageSelector = `.page[data-page-number = "${pageNumber}"]`;
|
|
|
|
await scrollIntoView(page, pageSelector);
|
|
|
|
}
|
|
|
|
|
|
|
|
await page.waitForSelector(
|
|
|
|
`.page[data-page-number = "1"] .annotationEditorLayer canvas`,
|
|
|
|
{ visible: true }
|
|
|
|
);
|
|
|
|
})
|
|
|
|
);
|
|
|
|
});
|
|
|
|
});
|
2025-01-13 11:51:57 +01:00
|
|
|
|
|
|
|
describe("Switch to edit mode by double clicking on an existing stamp annotation", () => {
|
|
|
|
const annotationSelector = getAnnotationSelector("999R");
|
|
|
|
|
|
|
|
let pages;
|
|
|
|
|
2025-04-13 14:19:24 +02:00
|
|
|
beforeEach(async () => {
|
2025-01-13 11:51:57 +01:00
|
|
|
pages = await loadAndWait("issue19239.pdf", annotationSelector);
|
|
|
|
});
|
|
|
|
|
2025-04-13 14:19:24 +02:00
|
|
|
afterEach(async () => {
|
2025-01-13 11:51:57 +01:00
|
|
|
await closePages(pages);
|
|
|
|
});
|
|
|
|
|
|
|
|
it("must switch to edit mode", async () => {
|
|
|
|
await Promise.all(
|
|
|
|
pages.map(async ([, page]) => {
|
|
|
|
await page.waitForSelector(annotationSelector);
|
|
|
|
await scrollIntoView(page, annotationSelector);
|
|
|
|
|
|
|
|
await page.click(annotationSelector, { count: 2 });
|
|
|
|
|
|
|
|
await page.waitForFunction(() =>
|
|
|
|
document
|
|
|
|
.querySelector(".annotationEditorLayer")
|
|
|
|
.classList.contains("stampEditing")
|
|
|
|
);
|
|
|
|
})
|
|
|
|
);
|
|
|
|
});
|
|
|
|
});
|
2025-03-06 14:59:04 +01:00
|
|
|
|
|
|
|
describe("Switch to edit mode, zoom and check that the non-editable stamp is still there", () => {
|
|
|
|
const annotationSelector = getAnnotationSelector("14R");
|
|
|
|
|
|
|
|
let pages;
|
|
|
|
|
2025-04-13 14:19:24 +02:00
|
|
|
beforeEach(async () => {
|
2025-03-06 14:59:04 +01:00
|
|
|
pages = await loadAndWait("red_stamp.pdf", annotationSelector, 20);
|
|
|
|
});
|
|
|
|
|
2025-04-13 14:19:24 +02:00
|
|
|
afterEach(async () => {
|
2025-03-06 14:59:04 +01:00
|
|
|
await closePages(pages);
|
|
|
|
});
|
|
|
|
|
|
|
|
it("must check if the canvas is still red", async () => {
|
|
|
|
await Promise.all(
|
|
|
|
pages.map(async ([, page]) => {
|
|
|
|
expect(
|
|
|
|
await isCanvasMonochrome(page, 1, null, 0xff0000ff)
|
|
|
|
).toBeTrue();
|
|
|
|
|
|
|
|
await switchToStamp(page);
|
|
|
|
|
|
|
|
expect(
|
|
|
|
await isCanvasMonochrome(page, 1, null, 0xff0000ff)
|
|
|
|
).toBeTrue();
|
|
|
|
|
|
|
|
const rectPage = await getRect(
|
|
|
|
page,
|
|
|
|
`.page[data-page-number = "1"] .annotationEditorLayer`
|
|
|
|
);
|
|
|
|
|
|
|
|
const handle = await waitForPageRendered(page, 1);
|
|
|
|
const originX = rectPage.x + rectPage.width / 2;
|
|
|
|
const originY = rectPage.y + rectPage.height / 2;
|
|
|
|
await page.evaluate(
|
|
|
|
origin => {
|
|
|
|
window.PDFViewerApplication.pdfViewer.increaseScale({
|
|
|
|
scaleFactor: 2,
|
|
|
|
origin,
|
|
|
|
});
|
|
|
|
},
|
|
|
|
[originX, originY]
|
|
|
|
);
|
|
|
|
await awaitPromise(handle);
|
|
|
|
|
|
|
|
expect(
|
|
|
|
await isCanvasMonochrome(page, 1, null, 0xff0000ff)
|
|
|
|
).toBeTrue();
|
|
|
|
})
|
|
|
|
);
|
|
|
|
});
|
|
|
|
});
|
2023-07-06 15:32:11 +02:00
|
|
|
});
|