1
0
Fork 0
mirror of https://github.com/mozilla/pdf.js.git synced 2025-04-19 14:48:08 +02:00
pdf.js/test/integration/highlight_editor_spec.mjs

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

2698 lines
88 KiB
JavaScript
Raw Permalink Normal View History

/* 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.
*/
import {
awaitPromise,
closePages,
getEditorSelector,
getFirstSerialized,
getRect,
getSerialized,
Fix flickering on text selection When seleciting on a touch screen device, whenever the finger moves to a blank area (so over `div.textLayer` directly rather than on a `<span>`), the selection jumps to include all the text between the beginning of the .textLayer and the selection side that is not being moved. The existing selection flickering fix when using the mouse cannot be trivially re-used on mobile, because when modifying a selection on a touchscreen device Firefox will not emit any pointer event (and Chrome will emit them inconsistently). Instead, we have to listen to the 'selectionchange' event. The fix is different in Firefox and Chrome: - on Firefox, we have to make sure that, when modifying the selection, hovering on blank areas will hover on the .endOfContent element rather than on the .textLayer element. This is done by adjusting the z-indexes so that .endOfContent is above .textLayer. - on Chrome, hovering on blank areas needs to trigger hovering on an element that is either immediately after (or immediately before, depending on which side of the selection the user is moving) the currently selected text. This is done by moving the .endOfContent element around between the correct `<span>`s in the text layer. The new anti-flickering code is also used when selecting using a mouse: the improvement in Firefox is only observable on multi-page selection, while in Chrome it also affects selection within a single page. After this commit, the `z-index`es inside .textLayer are as follows: - .endOfContent has `z-index: 0` - everything else has `z-index: 1` - except for .markedContent, which have `z-index: 0` and their contents have `z-index: 1`. `.textLayer` has an explicit `z-index: 0` to introduce a new stacking context, so that its contents are not drawn on top of `.annotationLayer`.
2024-04-11 12:50:57 +02:00
getSpanRectFromText,
getXY,
kbBigMoveLeft,
kbBigMoveUp,
kbFocusNext,
kbFocusPrevious,
2024-10-19 02:19:46 +05:30
kbSave,
kbUndo,
loadAndWait,
scrollIntoView,
selectEditors,
setCaretAt,
switchToEditor,
unselectEditor,
waitAndClick,
waitForAnnotationModeChanged,
waitForPointerUp,
waitForSelectedEditor,
waitForSerialized,
waitForTimeout,
} from "./test_utils.mjs";
2024-10-19 02:19:46 +05:30
import { fileURLToPath } from "url";
import fs from "fs";
import path from "path";
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const selectAll = selectEditors.bind(null, "highlight");
const switchToHighlight = switchToEditor.bind(null, "Highlight");
describe("Highlight Editor", () => {
describe("Editor must be removed without exception", () => {
let pages;
beforeEach(async () => {
pages = await loadAndWait("tracemonkey.pdf", ".annotationEditorLayer");
});
afterEach(async () => {
await closePages(pages);
});
it("must scroll and check that the draw layer is there", async () => {
await Promise.all(
pages.map(async ([browserName, page]) => {
await switchToHighlight(page);
const rect = await getSpanRectFromText(page, 1, "Abstract");
const x = rect.x + rect.width / 2;
const y = rect.y + rect.height / 2;
// Here and elsewhere, we add a small delay between press and release
// to make sure that a pointerup event is triggered after
// selectionchange.
// It works with a value of 1ms, but we use 100ms to be sure.
await page.mouse.click(x, y, { count: 2, delay: 100 });
await page.waitForSelector(`${getEditorSelector(0)}`);
const oneToOne = Array.from(new Array(13).keys(), n => n + 2).concat(
Array.from(new Array(13).keys(), n => 13 - n)
);
for (const pageNumber of oneToOne) {
await scrollIntoView(
page,
`.page[data-page-number = "${pageNumber}"]`
);
}
await page.waitForSelector(
`.page[data-page-number = "1"] svg.highlight`,
{ visible: true }
);
})
);
});
});
describe("Editor must keep selected", () => {
let pages;
beforeEach(async () => {
pages = await loadAndWait("tracemonkey.pdf", ".annotationEditorLayer");
});
afterEach(async () => {
await closePages(pages);
});
it("must scroll and check that the highlight is selected", async () => {
await Promise.all(
pages.map(async ([browserName, page]) => {
await switchToHighlight(page);
const rect = await getSpanRectFromText(page, 1, "Abstract");
const x = rect.x + rect.width / 2;
const y = rect.y + rect.height / 2;
await page.mouse.click(x, y, { count: 2, delay: 100 });
await page.waitForSelector(`${getEditorSelector(0)}`);
await page.waitForSelector(
`.page[data-page-number = "1"] svg.highlightOutline.selected`
);
const oneToOne = Array.from(new Array(13).keys(), n => n + 2).concat(
Array.from(new Array(13).keys(), n => 13 - n)
);
for (const pageNumber of oneToOne) {
await scrollIntoView(
page,
`.page[data-page-number = "${pageNumber}"]`
);
}
await page.waitForSelector(
`.page[data-page-number = "1"] svg.highlightOutline.selected`
);
})
);
});
});
describe("The default color must have the correct value", () => {
let pages;
beforeEach(async () => {
pages = await loadAndWait(
"tracemonkey.pdf",
".annotationEditorLayer",
null,
null,
{ highlightEditorColors: "red=#AB0000" }
);
});
afterEach(async () => {
await closePages(pages);
});
it("must highlight with red color", async () => {
await Promise.all(
pages.map(async ([browserName, page]) => {
await switchToHighlight(page);
const rect = await getSpanRectFromText(page, 1, "Abstract");
const x = rect.x + rect.width / 2;
const y = rect.y + rect.height / 2;
await page.mouse.click(x, y, { count: 2, delay: 100 });
await page.waitForSelector(`${getEditorSelector(0)}`);
await page.waitForSelector(
`.page[data-page-number = "1"] svg.highlightOutline.selected`
);
const usedColor = await page.evaluate(() => {
const highlight = document.querySelector(
`.page[data-page-number = "1"] .canvasWrapper > svg.highlight`
);
return highlight.getAttribute("fill");
});
expect(usedColor).withContext(`In ${browserName}`).toEqual("#AB0000");
})
);
});
});
describe("Invisible editor must change its color", () => {
let pages;
beforeEach(async () => {
pages = await loadAndWait(
"tracemonkey.pdf",
".annotationEditorLayer",
null,
null,
{ highlightEditorColors: "yellow=#FFFF98,green=#53FFBC" }
);
});
afterEach(async () => {
await closePages(pages);
});
it("must scroll and change the color without exceptions", async () => {
await Promise.all(
pages.map(async ([browserName, page]) => {
await switchToHighlight(page);
let rect = await getSpanRectFromText(page, 1, "Abstract");
let x = rect.x + rect.width / 2;
let y = rect.y + rect.height / 2;
await page.mouse.click(x, y, { count: 2, delay: 100 });
await page.waitForSelector(`${getEditorSelector(0)}`);
await page.waitForSelector(
`.page[data-page-number = "1"] svg.highlightOutline.selected`
);
for (const pageNumber of Array.from(
new Array(13).keys(),
n => n + 2
)) {
await scrollIntoView(
page,
`.page[data-page-number = "${pageNumber}"]`
);
}
await page.waitForSelector(
`.page[data-page-number = "14"] .textLayer .endOfContent`
);
rect = await getSpanRectFromText(page, 14, "References");
x = rect.x + rect.width / 2;
y = rect.y + rect.height / 2;
await page.mouse.click(x, y, { count: 2, delay: 100 });
const editorSelector = getEditorSelector(1);
await page.waitForSelector(editorSelector);
await page.waitForSelector(
`.page[data-page-number = "14"] svg.highlightOutline.selected`
);
await selectAll(page);
await page.waitForSelector(
`${editorSelector} .editToolbar button.colorPicker`
);
await page.click(`${editorSelector} .editToolbar button.colorPicker`);
await page.waitForSelector(
`${editorSelector} .editToolbar button[title = "Green"]`
);
await page.click(
`${editorSelector} .editToolbar button[title = "Green"]`
);
await page.waitForSelector(
`.page[data-page-number = "14"] svg.highlight[fill = "#53FFBC"]`
);
for (const pageNumber of Array.from(
new Array(13).keys(),
n => 13 - n
)) {
await scrollIntoView(
page,
`.page[data-page-number = "${pageNumber}"]`
);
}
await page.waitForSelector(
`.page[data-page-number = "1"] svg.highlight[fill = "#53FFBC"]`
);
})
);
});
});
describe("Highlight data serialization", () => {
let pages;
beforeEach(async () => {
pages = await loadAndWait(
"tracemonkey.pdf",
".annotationEditorLayer",
null,
null,
{ highlightEditorColors: "red=#AB0000" }
);
});
afterEach(async () => {
await closePages(pages);
});
it("must be correctly serialized", async () => {
await Promise.all(
pages.map(async ([browserName, page]) => {
await switchToHighlight(page);
const rect = await getSpanRectFromText(page, 1, "Abstract");
const x = rect.x + rect.width / 2;
const y = rect.y + rect.height / 2;
await page.mouse.click(x, y, { count: 2, delay: 100 });
await page.waitForSelector(`${getEditorSelector(0)}`);
await page.waitForSelector(
`.page[data-page-number = "1"] svg.highlightOutline.selected`
);
await waitForSerialized(page, 1);
const serialized = (await getSerialized(page))[0];
expect(serialized.annotationType)
.withContext(`In ${browserName}`)
.toEqual(9);
expect(serialized.color)
.withContext(`In ${browserName}`)
.toEqual([0xab, 0, 0]);
// We don't check the quadPoints and outlines values because they're
// dependent on the font used in the text layer.
expect(serialized.quadPoints.length)
.withContext(`In ${browserName}`)
.toEqual(8);
expect(serialized.outlines.length)
.withContext(`In ${browserName}`)
.toEqual(1);
expect(serialized.outlines[0].length)
.withContext(`In ${browserName}`)
.toEqual(8);
})
);
});
});
describe("Color picker and keyboard", () => {
let pages;
beforeEach(async () => {
pages = await loadAndWait(
"tracemonkey.pdf",
".annotationEditorLayer",
null,
null,
{
highlightEditorColors:
"yellow=#FFFF00,green=#00FF00,blue=#0000FF,pink=#FF00FF,red=#FF0000",
}
);
});
afterEach(async () => {
await closePages(pages);
});
it("must check that we can use the keyboard to select a color", async () => {
await Promise.all(
pages.map(async ([browserName, page]) => {
await switchToHighlight(page);
const sel = getEditorSelector(0);
const rect = await getSpanRectFromText(page, 1, "Abstract");
const x = rect.x + rect.width / 2;
const y = rect.y + rect.height / 2;
await page.mouse.click(x, y, { count: 2, delay: 100 });
await page.waitForSelector(sel);
await page.waitForSelector(
`.page[data-page-number = "1"] svg.highlightOutline.selected`
);
await page.waitForSelector(`${sel} .editToolbar button.colorPicker`);
await page.click(`${sel} .editToolbar button.colorPicker`);
await page.waitForSelector(
`${sel} .editToolbar button[title = "Red"]`
);
await page.click(`${sel} .editToolbar button[title = "Red"]`);
await page.waitForSelector(
`.page[data-page-number = "1"] svg.highlight[fill = "#FF0000"]`
);
await page.keyboard.press("ArrowUp");
await page.waitForSelector(
`${sel} .editToolbar button[title = "Pink"]:focus`
);
await page.keyboard.press("Enter");
await page.waitForSelector(
`.page[data-page-number = "1"] svg.highlight[fill = "#FF00FF"]`
);
await page.keyboard.press("ArrowUp");
await page.waitForSelector(
`${sel} .editToolbar button[title = "Blue"]:focus`
);
await page.keyboard.press(" ");
await page.waitForSelector(
`.page[data-page-number = "1"] svg.highlight[fill = "#0000FF"]`
);
await page.keyboard.press("ArrowLeft");
await page.waitForSelector(
`${sel} .editToolbar button[title = "Green"]:focus`
);
await page.keyboard.press("Enter");
await page.waitForSelector(
`.page[data-page-number = "1"] svg.highlight[fill = "#00FF00"]`
);
await page.keyboard.press("ArrowRight");
await page.waitForSelector(
`${sel} .editToolbar button[title = "Blue"]:focus`
);
await page.keyboard.press("Enter");
await page.waitForSelector(
`.page[data-page-number = "1"] svg.highlight[fill = "#0000FF"]`
);
await page.keyboard.press("ArrowDown");
await page.waitForSelector(
`${sel} .editToolbar button[title = "Pink"]:focus`
);
await page.keyboard.press(" ");
await page.waitForSelector(
`.page[data-page-number = "1"] svg.highlight[fill = "#FF00FF"]`
);
for (let i = 0; i < 4; i++) {
await page.keyboard.press("ArrowUp");
}
await page.waitForSelector(
`${sel} .editToolbar button.colorPicker .dropdown.hidden`
);
await page.keyboard.press("ArrowDown");
await page.waitForSelector(
`${sel} .editToolbar button.colorPicker .dropdown:not(.hidden)`
);
await page.keyboard.press("ArrowUp");
await page.waitForSelector(
`${sel} .editToolbar button.colorPicker .dropdown.hidden`
);
await page.keyboard.press(" ");
await page.waitForSelector(
`${sel} .editToolbar button.colorPicker .dropdown:not(.hidden)`
);
})
);
});
});
describe("Text highlights aren't draggable", () => {
let pages;
beforeEach(async () => {
pages = await loadAndWait(
"tracemonkey.pdf",
".annotationEditorLayer",
null,
null,
{ highlightEditorColors: "red=#AB0000" }
);
});
afterEach(async () => {
await closePages(pages);
});
it("must check that a text highlight don't move when arrows are pressed", async () => {
await Promise.all(
pages.map(async ([browserName, page]) => {
await switchToHighlight(page);
const rect = await getSpanRectFromText(page, 1, "Abstract");
const x = rect.x + rect.width / 2;
const y = rect.y + rect.height / 2;
await page.mouse.click(x, y, { count: 2, delay: 100 });
const editorSelector = getEditorSelector(0);
await page.waitForSelector(editorSelector);
await page.waitForSelector(
`.page[data-page-number = "1"] svg.highlightOutline.selected`
);
await page.focus(editorSelector);
const xy = await getXY(page, editorSelector);
for (let i = 0; i < 5; i++) {
await kbBigMoveLeft(page);
}
expect(await getXY(page, editorSelector))
.withContext(`In ${browserName}`)
.toEqual(xy);
for (let i = 0; i < 5; i++) {
await kbBigMoveUp(page);
}
expect(await getXY(page, editorSelector))
.withContext(`In ${browserName}`)
.toEqual(xy);
})
);
});
});
describe("Color picker and click outside", () => {
let pages;
beforeEach(async () => {
pages = await loadAndWait("tracemonkey.pdf", ".annotationEditorLayer");
});
afterEach(async () => {
await closePages(pages);
});
it("must check that the dropdown is hidden", async () => {
await Promise.all(
pages.map(async ([browserName, page]) => {
await switchToHighlight(page);
const sel = getEditorSelector(0);
const rect = await getSpanRectFromText(page, 1, "Abstract");
const x = rect.x + rect.width / 2;
const y = rect.y + rect.height / 2;
await page.mouse.click(x, y, { count: 2, delay: 100 });
await page.waitForSelector(sel);
await page.waitForSelector(
`.page[data-page-number = "1"] svg.highlightOutline.selected`
);
await page.waitForSelector(`${sel} .editToolbar button.colorPicker`);
await page.click(`${sel} .editToolbar button.colorPicker`);
await page.waitForSelector(
`${sel} .editToolbar button[title = "Red"]`
);
await page.mouse.click(x, y - rect.height);
await page.waitForSelector(
`${sel} .editToolbar button.colorPicker .dropdown.hidden`
);
})
);
});
});
describe("Color picker can annoy the user when selecting some text", () => {
let pages;
beforeEach(async () => {
pages = await loadAndWait("tracemonkey.pdf", ".annotationEditorLayer");
});
afterEach(async () => {
await closePages(pages);
});
it("must check that editor is unselected when the mouse is down on the text layer", async () => {
await Promise.all(
pages.map(async ([browserName, page]) => {
await switchToHighlight(page);
const sel = getEditorSelector(0);
const rect = await getSpanRectFromText(page, 1, "Abstract");
const x = rect.x + rect.width / 2;
const y = rect.y + rect.height / 2;
await page.mouse.click(x, y, { count: 2, delay: 100 });
await page.waitForSelector(sel);
await page.waitForSelector(
`.page[data-page-number = "1"] svg.highlightOutline.selected`
);
await page.waitForSelector(`${sel} .editToolbar button.colorPicker`);
await page.mouse.click(x, y - rect.height);
await page.waitForSelector(
`.page[data-page-number = "1"] svg.highlightOutline:not(.selected)`
);
})
);
});
});
describe("Free highlight thickness can be changed", () => {
let pages;
beforeEach(async () => {
pages = await loadAndWait(
"empty.pdf",
".annotationEditorLayer",
null,
null,
{ highlightEditorColors: "yellow=#000000" }
);
});
afterEach(async () => {
await closePages(pages);
});
it("must check that the thickness is correctly updated", async () => {
await Promise.all(
pages.map(async ([browserName, page]) => {
await switchToHighlight(page);
const rect = await getRect(page, ".annotationEditorLayer");
for (let i = 0; i < 3; i++) {
const x = rect.x + 120 + i * 120;
const y = rect.y + 120 + i * 120;
const clickHandle = await waitForPointerUp(page);
await page.mouse.move(x, y);
await page.mouse.down();
await page.mouse.move(x + 100, y + 100);
await page.mouse.up();
await awaitPromise(clickHandle);
await page.waitForSelector(getEditorSelector(i));
}
let value = 12;
await waitForSerialized(page, 3);
let serialized = await getSerialized(page);
expect(serialized.map(x => x.thickness))
.withContext(`In ${browserName}`)
.toEqual([value, value, value]);
await selectAll(page);
const editorSelector = getEditorSelector(0);
const { width: prevWidth } = await getRect(page, editorSelector);
value = 24;
page.evaluate(val => {
window.PDFViewerApplication.eventBus.dispatch(
"switchannotationeditorparams",
{
source: null,
type: window.pdfjsLib.AnnotationEditorParamsType
.HIGHLIGHT_THICKNESS,
value: val,
}
);
}, value);
await page.waitForFunction(
(w, sel) =>
document.querySelector(sel).getBoundingClientRect().width !== w,
{},
prevWidth,
editorSelector
);
await waitForSerialized(page, 3);
serialized = await getSerialized(page);
expect(serialized.map(x => x.thickness))
.withContext(`In ${browserName}`)
.toEqual([value, value, value]);
})
);
});
});
describe("Highlight with the keyboard", () => {
let pages;
beforeEach(async () => {
pages = await loadAndWait("tracemonkey.pdf", ".annotationEditorLayer");
});
afterEach(async () => {
await closePages(pages);
});
it("must check that some text has been highlighted", async () => {
await Promise.all(
pages.map(async ([browserName, page]) => {
await switchToHighlight(page);
const sel = getEditorSelector(0);
const spanRect = await getRect(
page,
`.page[data-page-number="1"] > .textLayer > span`
);
await page.keyboard.down("Shift");
await page.mouse.click(
spanRect.x + 1,
spanRect.y + spanRect.height / 2,
{ count: 2 }
);
for (let i = 0; i < 6; i++) {
await page.keyboard.press("ArrowRight");
}
await page.keyboard.press("ArrowDown");
// Here and elsewhere, we add a small delay between press and release
// to make sure that a keyup event for Shift is triggered after
// selectionchange (it's why adding the delay on the last before
// releasing shift is enough).
// It works with a value of 10ms, but we use 100ms to be sure.
await page.keyboard.press("ArrowDown", { delay: 100 });
await page.keyboard.up("Shift");
await page.waitForSelector(sel);
const [w, h] = await page.evaluate(s => {
const {
style: { width, height },
} = document.querySelector(s);
return [parseFloat(width), parseFloat(height)];
}, sel);
// w & h are the width and height of the highlight in percent.
// We expect the highlight to be around 73% wide and 9% high.
// We allow a 2% margin of error because of the font used in the text
// layer we can't be sure of the dimensions.
expect(Math.abs(w - 73) <= 2)
.withContext(`In ${browserName}`)
.toBe(true);
expect(Math.abs(h - 9) <= 2)
.withContext(`In ${browserName}`)
.toBe(true);
})
);
});
});
describe("Free highlight is drawn at the right place after having been rotated (bug 1879108)", () => {
let pages;
beforeEach(async () => {
pages = await loadAndWait(
"empty.pdf",
".annotationEditorLayer",
null,
null,
{ highlightEditorColors: "yellow=#000000" }
);
});
afterEach(async () => {
await closePages(pages);
});
it("must check that highlight is at the correct position", async () => {
await Promise.all(
pages.map(async ([browserName, page]) => {
await switchToHighlight(page);
const rect = await getRect(page, ".annotationEditorLayer");
const clickHandle = await waitForPointerUp(page);
await page.mouse.move(rect.x + 120, rect.y + 120);
await page.mouse.down();
await page.mouse.move(rect.x + 220, rect.y + 220);
await page.mouse.up();
await awaitPromise(clickHandle);
const editorSelector = getEditorSelector(0);
await page.waitForSelector(editorSelector);
await page.evaluate(() => {
window.PDFViewerApplication.rotatePages(90);
});
await page.waitForSelector(
".annotationEditorLayer[data-main-rotation='90']"
);
await selectAll(page);
const { width: prevWidth } = await getRect(page, editorSelector);
page.evaluate(val => {
window.PDFViewerApplication.eventBus.dispatch(
"switchannotationeditorparams",
{
source: null,
type: window.pdfjsLib.AnnotationEditorParamsType
.HIGHLIGHT_THICKNESS,
value: val,
}
);
}, 24);
await page.waitForFunction(
(w, sel) =>
document.querySelector(sel).getBoundingClientRect().width !== w,
{},
prevWidth,
editorSelector
);
const rectDiv = await getRect(page, editorSelector);
const rectSVG = await getRect(page, "svg.highlight.free");
expect(Math.abs(rectDiv.x - rectSVG.x) <= 2)
.withContext(`In ${browserName}`)
.toBe(true);
expect(Math.abs(rectDiv.y - rectSVG.y) <= 2)
.withContext(`In ${browserName}`)
.toBe(true);
expect(Math.abs(rectDiv.height - rectSVG.height) <= 2)
.withContext(`In ${browserName}`)
.toBe(true);
expect(Math.abs(rectDiv.width - rectSVG.width) <= 2)
.withContext(`In ${browserName}`)
.toBe(true);
})
);
});
});
describe("Highlight links", () => {
let pages;
beforeEach(async () => {
pages = await loadAndWait(
"bug1868759.pdf",
".annotationEditorLayer",
null,
null,
{ highlightEditorColors: "red=#AB0000" }
);
});
afterEach(async () => {
await closePages(pages);
});
it("must check that it's possible to highlight a part of a link", async () => {
await Promise.all(
pages.map(async ([browserName, page]) => {
await switchToHighlight(page);
const rect = await getSpanRectFromText(
page,
1,
"Questions courantes"
);
const x = rect.x + 0.75 * rect.width;
const y = rect.y + rect.height / 2;
await page.mouse.click(x, y, { count: 2, delay: 100 });
await page.waitForSelector(`${getEditorSelector(0)}`);
const usedColor = await page.evaluate(() => {
const highlight = document.querySelector(
`.page[data-page-number = "1"] .canvasWrapper > svg.highlight`
);
return highlight.getAttribute("fill");
});
expect(usedColor).withContext(`In ${browserName}`).toEqual("#AB0000");
})
);
});
});
describe("Highlight forms", () => {
let pages;
beforeEach(async () => {
pages = await loadAndWait(
"issue12233.pdf",
".annotationEditorLayer",
null,
null,
{ highlightEditorColors: "red=#AB0000" }
);
});
afterEach(async () => {
await closePages(pages);
});
it("must check that it's possible to highlight a part of a form", async () => {
await Promise.all(
pages.map(async ([browserName, page]) => {
await switchToHighlight(page);
const rect1 = await getRect(page, "#pdfjs_internal_id_5R");
const rect2 = await getRect(page, "#pdfjs_internal_id_16R");
const x1 = rect1.x + rect1.width / 2;
const y1 = rect1.y + rect1.height / 2;
const x2 = rect2.x + rect2.width / 2;
const y2 = rect2.y + rect2.height / 2;
const clickHandle = await waitForPointerUp(page);
await page.mouse.move(x1, y1);
await page.mouse.down();
await page.mouse.move(x2, y2);
await page.mouse.up();
await awaitPromise(clickHandle);
await page.waitForSelector(getEditorSelector(0));
const usedColor = await page.evaluate(() => {
const highlight = document.querySelector(
`.page[data-page-number = "1"] .canvasWrapper > svg.highlight`
);
return highlight.getAttribute("fill");
});
expect(usedColor).withContext(`In ${browserName}`).toEqual("#AB0000");
})
);
});
});
describe("Send a message when some text is selected", () => {
let pages;
beforeEach(async () => {
pages = await loadAndWait(
"tracemonkey.pdf",
`.page[data-page-number = "1"] .endOfContent`,
null,
{
eventBusSetup: eventBus => {
window.editingEvents = [];
eventBus.on("annotationeditorstateschanged", ({ details }) => {
window.editingEvents.push(details);
});
},
},
{ highlightEditorColors: "red=#AB0000" }
);
});
afterEach(async () => {
await closePages(pages);
});
it("must check that a message is sent on selection", async () => {
await Promise.all(
pages.map(async ([browserName, page]) => {
const rect = await getSpanRectFromText(page, 1, "Abstract");
const x = rect.x + rect.width / 2;
const y = rect.y + rect.height / 2;
await page.mouse.click(x, y, { count: 2, delay: 100 });
await page.waitForFunction(() => window.editingEvents.length > 0);
let editingEvent = await page.evaluate(() => {
const e = window.editingEvents[0];
window.editingEvents.length = 0;
return e;
});
expect(editingEvent.isEditing)
.withContext(`In ${browserName}`)
.toBe(false);
expect(editingEvent.hasSelectedText)
.withContext(`In ${browserName}`)
.toBe(true);
// Click somewhere to unselect the current selection.
await page.mouse.click(rect.x + rect.width + 10, y, { count: 1 });
await page.waitForFunction(() => window.editingEvents.length > 0);
editingEvent = await page.evaluate(() => {
const e = window.editingEvents[0];
window.editingEvents.length = 0;
return e;
});
expect(editingEvent.hasSelectedText)
.withContext(`In ${browserName}`)
.toBe(false);
await page.mouse.click(x, y, { count: 2, delay: 100 });
await page.waitForFunction(() => window.editingEvents.length > 0);
await page.evaluate(() => {
window.PDFViewerApplication.eventBus.dispatch("editingaction", {
name: "highlightSelection",
});
});
await page.waitForSelector(getEditorSelector(0));
const usedColor = await page.evaluate(() => {
const highlight = document.querySelector(
`.page[data-page-number = "1"] .canvasWrapper > svg.highlight`
);
return highlight.getAttribute("fill");
});
expect(usedColor).withContext(`In ${browserName}`).toEqual("#AB0000");
})
);
});
});
describe("Highlight and caret browsing", () => {
let pages;
beforeEach(async () => {
pages = await loadAndWait(
"tracemonkey.pdf",
".annotationEditorLayer",
null,
{
eventBusSetup: eventBus => {
eventBus.on("annotationeditoruimanager", ({ uiManager }) => {
window.uiManager = uiManager;
});
},
},
{
highlightEditorColors: "red=#AB0000",
supportsCaretBrowsingMode: true,
}
);
});
afterEach(async () => {
await closePages(pages);
});
it("must check that the caret can move a highlighted text", async () => {
await Promise.all(
pages.map(async ([browserName, page]) => {
await switchToHighlight(page);
const rect = await getSpanRectFromText(page, 1, "Abstract");
const x = rect.x + rect.width / 2;
const y = rect.y + rect.height / 2;
await page.mouse.click(x, y, { count: 2, delay: 100 });
const editorSelector = getEditorSelector(0);
await page.waitForSelector(editorSelector);
await unselectEditor(page, editorSelector);
await setCaretAt(
page,
1,
"Dynamic languages such as JavaScript are more difficult to com-",
1
);
await page.keyboard.press("ArrowUp");
const [text, offset] = await page.evaluate(() => {
const selection = window.getSelection();
return [selection.anchorNode.textContent, selection.anchorOffset];
});
expect(text).withContext(`In ${browserName}`).toEqual("Abstract");
expect(offset).withContext(`In ${browserName}`).toEqual(1);
})
);
});
it("must check that selection is correctly highlighted on arrow down key pressed", async () => {
await Promise.all(
pages.map(async ([browserName, page]) => {
await switchToHighlight(page);
await setCaretAt(
page,
1,
"Dynamic languages such as JavaScript are more difficult to com-",
15
);
await page.keyboard.down("Shift");
await page.keyboard.press("ArrowDown", { delay: 100 });
await page.keyboard.up("Shift");
await page.waitForSelector(getEditorSelector(0));
const usedColor = await page.evaluate(() => {
const highlight = document.querySelector(
`.page[data-page-number = "1"] .canvasWrapper > svg.highlight`
);
return highlight.getAttribute("fill");
});
expect(usedColor).withContext(`In ${browserName}`).toEqual("#AB0000");
})
);
});
it("must check that an highlight can be left with the keyboard", async () => {
await Promise.all(
pages.map(async ([browserName, page]) => {
await switchToHighlight(page);
if (browserName === "chrome") {
// Unfortunately, we can't test this on Chrome because we can't set
// the caret browsing mode to true.
return;
}
let rect = await getSpanRectFromText(
page,
1,
"Dynamic languages such as JavaScript are more difficult to com-"
);
await page.mouse.click(rect.x + 5, rect.y + rect.height / 2);
await page.keyboard.down("Shift");
for (let i = 0; i < 10; i++) {
await page.keyboard.press("ArrowRight");
}
const focusOffset = await page.evaluate(
() => window.getSelection().focusOffset
);
await page.keyboard.up("Shift");
await page.waitForSelector(getEditorSelector(0));
for (let i = 0; i < 5; i++) {
await page.keyboard.press("ArrowRight");
}
let offset = await page.evaluate(
() => window.getSelection().anchorOffset
);
expect(offset)
.withContext(`In ${browserName}`)
.toEqual(focusOffset + 4);
rect = await getSpanRectFromText(
page,
1,
"experience and enable a new generation of applications, virtual ma-"
);
await page.mouse.click(
rect.x + rect.width / 2,
rect.y + rect.height / 2
);
await page.keyboard.down("Shift");
for (let i = 0; i < 10; i++) {
await page.keyboard.press("ArrowRight");
}
const anchorOffset = await page.evaluate(
() => window.getSelection().anchorOffset
);
await page.keyboard.up("Shift");
await page.waitForSelector(getEditorSelector(1));
for (let i = 0; i < 5; i++) {
await page.keyboard.press("ArrowLeft");
}
offset = await page.evaluate(
() => window.getSelection().anchorOffset
);
expect(offset)
.withContext(`In ${browserName}`)
.toEqual(anchorOffset - 4);
})
);
});
});
describe("Editor must be removed in using the space key on the delete button", () => {
let pages;
beforeEach(async () => {
pages = await loadAndWait("tracemonkey.pdf", ".annotationEditorLayer");
});
afterEach(async () => {
await closePages(pages);
});
it("must check that the highlight has been deleted", async () => {
await Promise.all(
pages.map(async ([browserName, page]) => {
await switchToHighlight(page);
const rect = await getSpanRectFromText(page, 1, "Abstract");
const x = rect.x + rect.width / 2;
const y = rect.y + rect.height / 2;
await page.mouse.click(x, y, { count: 2, delay: 100 });
const editorSelector = getEditorSelector(0);
await page.waitForSelector(editorSelector);
await waitForSerialized(page, 1);
await page.waitForSelector(`${editorSelector} button.delete`);
await page.focus(`${editorSelector} button.delete`);
await page.keyboard.press(" ");
await waitForSerialized(page, 0);
})
);
});
});
describe("Thickness must be enabled when there's no selected highlights", () => {
let pages;
beforeEach(async () => {
pages = await loadAndWait("tracemonkey.pdf", ".annotationEditorLayer");
});
afterEach(async () => {
await closePages(pages);
});
it("must check the thickness input state", async () => {
await Promise.all(
pages.map(async ([browserName, page]) => {
await switchToHighlight(page);
let rect = await getSpanRectFromText(page, 1, "Abstract");
await page.mouse.click(
rect.x + rect.width / 2,
rect.y + rect.height / 2,
{ count: 2, delay: 100 }
);
const editorSelector = getEditorSelector(0);
await page.waitForSelector(editorSelector);
rect = await getRect(page, ".annotationEditorLayer");
const clickHandle = await waitForPointerUp(page);
await page.mouse.move(rect.x + 5, rect.y + 5);
await page.mouse.down();
await page.mouse.move(rect.x + 100, rect.y + 100);
await page.mouse.up();
await awaitPromise(clickHandle);
await page.waitForSelector(getEditorSelector(1));
await page.waitForSelector(
"#editorFreeHighlightThickness:not([disabled])"
);
await page.click(editorSelector);
await page.waitForSelector(editorSelector);
await page.waitForSelector("#editorFreeHighlightThickness[disabled]");
await switchToHighlight(page, /* disable */ true);
await switchToHighlight(page);
await page.waitForSelector(
"#editorFreeHighlightThickness:not([disabled])"
);
})
);
});
});
describe("Quadpoints must be correct", () => {
let pages;
beforeEach(async () => {
pages = await loadAndWait("tracemonkey.pdf", ".annotationEditorLayer");
});
afterEach(async () => {
await closePages(pages);
});
it("must check that the quadpoints for an highlight are almost correct", async () => {
await Promise.all(
pages.map(async ([browserName, page]) => {
await switchToHighlight(page);
const rect = await getSpanRectFromText(page, 1, "Languages");
await page.mouse.click(
rect.x + rect.width / 2,
rect.y + rect.height / 2,
{ count: 2, delay: 100 }
);
await page.waitForSelector(getEditorSelector(0));
await waitForSerialized(page, 1);
const quadPoints = await getFirstSerialized(page, e => e.quadPoints);
// Expected quadPoints tL, tR, bL, bR with bL coordinate.
const expected = [263, 696, 346, 696, 263, 674, 346, 674];
expect(quadPoints.every((x, i) => Math.abs(x - expected[i]) <= 5))
.withContext(`In ${browserName}`)
.toBeTrue();
})
);
});
});
describe("Quadpoints must be correct when they're in a translated page", () => {
let pages;
beforeEach(async () => {
pages = await loadAndWait("issue18360.pdf", ".annotationEditorLayer");
});
afterEach(async () => {
await closePages(pages);
});
it("must check that the quadpoints for an highlight are almost correct", async () => {
await Promise.all(
pages.map(async ([browserName, page]) => {
await switchToHighlight(page);
const rect = await getSpanRectFromText(page, 1, "Hello World");
await page.mouse.click(
rect.x + rect.width / 4,
rect.y + rect.height / 2,
{ count: 2, delay: 100 }
);
await page.waitForSelector(getEditorSelector(0));
await waitForSerialized(page, 1);
const quadPoints = await getFirstSerialized(page, e => e.quadPoints);
// Expected quadPoints tL, tR, bL, bR with bL coordinate.
const expected = [148, 637, 176, 637, 148, 624, 176, 624];
expect(quadPoints.every((x, i) => Math.abs(x - expected[i]) <= 5))
.withContext(`In ${browserName} (got ${quadPoints})`)
.toBeTrue();
})
);
});
});
describe("Editor must be unselected when the color picker is Escaped", () => {
let pages;
beforeEach(async () => {
pages = await loadAndWait("tracemonkey.pdf", ".annotationEditorLayer");
});
afterEach(async () => {
await closePages(pages);
});
it("must check that the highlight editor is unselected", async () => {
await Promise.all(
pages.map(async ([browserName, page]) => {
await switchToHighlight(page);
const rect = await getSpanRectFromText(page, 1, "Abstract");
const x = rect.x + rect.width / 2;
const y = rect.y + rect.height / 2;
await page.mouse.click(x, y, { count: 2, delay: 100 });
const editorSelector = getEditorSelector(0);
await page.waitForSelector(editorSelector);
await page.focus(`${editorSelector} button.colorPicker`);
await page.keyboard.press("Escape");
await page.focus(`${editorSelector}:not(selectedEditor)`);
})
);
});
});
describe("Highlight editor mustn't be moved when close to the page limits", () => {
let pages;
beforeEach(async () => {
pages = await loadAndWait("empty.pdf", ".annotationEditorLayer");
});
afterEach(async () => {
await closePages(pages);
});
it("must check the editor coordinates", async () => {
await Promise.all(
pages.map(async ([browserName, page]) => {
await switchToHighlight(page);
const rect = await getRect(page, ".annotationEditorLayer");
const clickHandle = await waitForPointerUp(page);
await page.mouse.move(rect.x + 1, rect.y + 50);
await page.mouse.down();
await page.mouse.move(rect.x + 1, rect.y + 100);
await page.mouse.up();
await awaitPromise(clickHandle);
const editorSelector = getEditorSelector(0);
await page.waitForSelector(editorSelector);
const { x: editorX } = await getRect(page, editorSelector);
expect(editorX < rect.x)
.withContext(`In ${browserName}`)
.toBeTrue();
})
);
});
});
describe("Show/hide all the highlights", () => {
let pages;
beforeEach(async () => {
pages = await loadAndWait("tracemonkey.pdf", ".annotationEditorLayer");
});
afterEach(async () => {
await closePages(pages);
});
it("must check that the highlights are correctly hidden/shown", async () => {
await Promise.all(
pages.map(async ([browserName, page]) => {
await switchToHighlight(page);
let rect = await getRect(page, ".annotationEditorLayer");
const clickHandle = await waitForPointerUp(page);
await page.mouse.move(rect.x + 20, rect.y + 20);
await page.mouse.down();
await page.mouse.move(rect.x + 20, rect.y + 120);
await page.mouse.up();
await awaitPromise(clickHandle);
const firstEditorSelector = getEditorSelector(0);
await page.waitForSelector(firstEditorSelector);
rect = await getSpanRectFromText(page, 1, "Languages");
await page.mouse.click(
rect.x + rect.width / 2,
rect.y + rect.height / 2,
{ count: 2, delay: 100 }
);
const secondEditorSelector = getEditorSelector(1);
await page.waitForSelector(secondEditorSelector);
await page.click("#editorHighlightShowAll");
await page.waitForSelector(`${firstEditorSelector}.hidden`);
await page.waitForSelector(`${secondEditorSelector}.hidden`);
await page.click("#editorHighlightShowAll");
await page.waitForSelector(`${firstEditorSelector}:not(.hidden)`);
await page.waitForSelector(`${secondEditorSelector}:not(.hidden)`);
await page.click("#editorHighlightShowAll");
await page.waitForSelector(`${firstEditorSelector}.hidden`);
await page.waitForSelector(`${secondEditorSelector}.hidden`);
const oneToOne = Array.from(new Array(13).keys(), n => n + 2).concat(
Array.from(new Array(13).keys(), n => 13 - n)
);
for (const pageNumber of oneToOne) {
await scrollIntoView(
page,
`.page[data-page-number = "${pageNumber}"]`
);
if (pageNumber === 14) {
await page.click("#editorHighlightShowAll");
}
}
await page.waitForSelector(`${firstEditorSelector}:not(.hidden)`);
await page.waitForSelector(`${secondEditorSelector}:not(.hidden)`);
})
);
});
});
describe("Highlight from floating highlight button", () => {
let pages;
beforeEach(async () => {
pages = await loadAndWait(
"tracemonkey.pdf",
".annotationEditorLayer",
null,
null,
{ highlightEditorColors: "red=#AB0000" }
);
});
afterEach(async () => {
await closePages(pages);
});
it("must check that clicking on the highlight floating button triggers an highlight", async () => {
await Promise.all(
pages.map(async ([browserName, page]) => {
async function floatingHighlight(text, editorId) {
const rect = await getSpanRectFromText(page, 1, text);
const x = rect.x + rect.width / 2;
const y = rect.y + rect.height / 2;
await page.mouse.click(x, y, { count: 2, delay: 100 });
await page.waitForSelector(".textLayer .highlightButton");
await page.click(".textLayer .highlightButton");
await page.waitForSelector(getEditorSelector(editorId));
const usedColor = await page.evaluate(() => {
const highlight = document.querySelector(
`.page[data-page-number = "1"] .canvasWrapper > svg.highlight`
);
return highlight.getAttribute("fill");
});
expect(usedColor)
.withContext(`In ${browserName}`)
.toEqual("#AB0000");
}
await floatingHighlight("Abstract", 0);
// Disable editing mode, and highlight another string (issue 19369).
await switchToHighlight(page, /* disable */ true);
await floatingHighlight("Introduction", 1);
})
);
});
});
describe("Text layer must have the focus before highlights", () => {
let pages;
beforeEach(async () => {
pages = await loadAndWait("tracemonkey.pdf", ".annotationEditorLayer");
});
afterEach(async () => {
await closePages(pages);
});
it("must check the focus order", async () => {
await Promise.all(
pages.map(async ([browserName, page]) => {
await switchToHighlight(page);
let rect = await getSpanRectFromText(page, 1, "Abstract");
let x = rect.x + rect.width / 2;
let y = rect.y + rect.height / 2;
await page.mouse.click(x, y, { count: 2, delay: 100 });
await page.waitForSelector(getEditorSelector(0));
rect = await getSpanRectFromText(page, 1, "Languages");
x = rect.x + rect.width / 2;
y = rect.y + rect.height / 2;
await page.mouse.click(x, y, { count: 2, delay: 100 });
const editorSelector = getEditorSelector(1);
await page.waitForSelector(editorSelector);
await page.focus(editorSelector);
await kbFocusPrevious(page);
await page.waitForSelector(
`.page[data-page-number="1"] > .textLayer:focus`
);
await kbFocusNext(page);
await page.waitForSelector(`${editorSelector}:focus`);
})
);
});
});
describe("Undo a highlight", () => {
let pages;
beforeEach(async () => {
pages = await loadAndWait("tracemonkey.pdf", ".annotationEditorLayer");
});
afterEach(async () => {
await closePages(pages);
});
it("must check that a highlight can be undone", async () => {
await Promise.all(
pages.map(async ([browserName, page]) => {
await switchToHighlight(page);
const rect = await getSpanRectFromText(page, 1, "Abstract");
const x = rect.x + rect.width / 2;
const y = rect.y + rect.height / 2;
await page.mouse.click(x, y, { count: 2, delay: 100 });
const editorSelector = getEditorSelector(0);
await page.waitForSelector(editorSelector);
await waitForSerialized(page, 1);
await page.waitForSelector(`${editorSelector} button.delete`);
await page.click(`${editorSelector} button.delete`);
await waitForSerialized(page, 0);
await kbUndo(page);
await waitForSerialized(page, 1);
await page.waitForSelector(editorSelector);
})
);
});
});
describe("Delete a highlight and undo it an other page", () => {
let pages;
beforeEach(async () => {
pages = await loadAndWait(
"tracemonkey.pdf",
".annotationEditorLayer",
null,
null,
{
highlightEditorColors:
"yellow=#FFFF00,green=#00FF00,blue=#0000FF,pink=#FF00FF,red=#FF0000",
}
);
});
afterEach(async () => {
await closePages(pages);
});
it("must check that a highlight can be undone", async () => {
await Promise.all(
pages.map(async ([browserName, page]) => {
await switchToHighlight(page);
const rect = await getSpanRectFromText(page, 1, "Abstract");
const x = rect.x + rect.width / 2;
const y = rect.y + rect.height / 2;
await page.mouse.click(x, y, { count: 2, delay: 100 });
const editorSelector = getEditorSelector(0);
await page.waitForSelector(editorSelector);
await waitForSerialized(page, 1);
await page.waitForSelector(`${editorSelector} button.delete`);
await page.click(`${editorSelector} button.delete`);
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);
}
await kbUndo(page);
await waitForSerialized(page, 1);
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(editorSelector);
await page.waitForSelector(
`.page[data-page-number = "1"] svg.highlight[fill = "#FFFF00"]`
);
})
);
});
});
describe("Delete a highlight, scroll and undo it", () => {
let pages;
beforeEach(async () => {
pages = await loadAndWait(
"tracemonkey.pdf",
".annotationEditorLayer",
null,
null,
{
highlightEditorColors:
"yellow=#FFFF00,green=#00FF00,blue=#0000FF,pink=#FF00FF,red=#FF0000",
}
);
});
afterEach(async () => {
await closePages(pages);
});
it("must check that a highlight can be undone", async () => {
await Promise.all(
pages.map(async ([browserName, page]) => {
await switchToHighlight(page);
const rect = await getSpanRectFromText(page, 1, "Abstract");
const x = rect.x + rect.width / 2;
const y = rect.y + rect.height / 2;
await page.mouse.click(x, y, { count: 2, delay: 100 });
const editorSelector = getEditorSelector(0);
await page.waitForSelector(editorSelector);
await waitForSerialized(page, 1);
await page.waitForSelector(`${editorSelector} button.delete`);
await page.click(`${editorSelector} button.delete`);
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);
}
await kbUndo(page);
await waitForSerialized(page, 1);
await page.waitForSelector(editorSelector);
await page.waitForSelector(
`.page[data-page-number = "1"] svg.highlight[fill = "#FFFF00"]`
);
})
);
});
});
describe("Use a toolbar overlapping an other highlight", () => {
let pages;
beforeEach(async () => {
pages = await loadAndWait(
"tracemonkey.pdf",
".annotationEditorLayer",
null,
null,
{
highlightEditorColors:
"yellow=#FFFF00,green=#00FF00,blue=#0000FF,pink=#FF00FF,red=#FF0000",
}
);
});
afterEach(async () => {
await closePages(pages);
});
it("must check that the toolbar is usable", async () => {
await Promise.all(
pages.map(async ([browserName, page]) => {
await switchToHighlight(page);
await setCaretAt(
page,
1,
"Dynamic languages such as JavaScript are more difficult to com-",
0
);
await page.keyboard.down("Shift");
for (let i = 0; i < 3; i++) {
await page.keyboard.press("ArrowDown", { delay: 100 });
}
await page.keyboard.up("Shift");
const editorSelector = getEditorSelector(0);
await page.waitForSelector(editorSelector);
await setCaretAt(
page,
1,
"handle all possible type combinations at runtime. We present an al-",
0
);
await page.keyboard.down("Shift");
for (let i = 0; i < 3; i++) {
await page.keyboard.press("ArrowDown", { delay: 100 });
}
await page.keyboard.up("Shift");
await page.waitForSelector(getEditorSelector(1));
const rect = await getRect(page, editorSelector);
const x = rect.x + rect.width / 2;
const y = rect.y + rect.height / 2;
await page.mouse.click(x, y);
await page.waitForSelector(
`${editorSelector} .editToolbar button.colorPicker`
);
await page.click(`${editorSelector} .editToolbar button.colorPicker`);
await page.waitForSelector(
`${editorSelector} .editToolbar button[title = "Green"]`
);
await page.click(
`${editorSelector} .editToolbar button[title = "Green"]`
);
await page.waitForSelector(
`.page[data-page-number = "1"] svg.highlight[fill = "#00FF00"]`
);
})
);
});
});
describe("Draw a free highlight with the pointer hovering an existing highlight", () => {
let pages;
beforeEach(async () => {
pages = await loadAndWait("tracemonkey.pdf", ".annotationEditorLayer");
});
afterEach(async () => {
await closePages(pages);
});
it("must check that an existing highlight is ignored on hovering", async () => {
await Promise.all(
pages.map(async ([browserName, page]) => {
await switchToHighlight(page);
const rect = await getSpanRectFromText(page, 1, "Abstract");
const editorSelector = getEditorSelector(0);
const x = rect.x + rect.width / 2;
let y = rect.y + rect.height / 2;
await page.mouse.click(x, y, { count: 2, delay: 100 });
await page.waitForSelector(editorSelector);
await waitForSerialized(page, 1);
await unselectEditor(page, editorSelector);
const clickHandle = await waitForPointerUp(page);
y = rect.y - rect.height;
await page.mouse.move(x, y);
const counterHandle = await page.evaluateHandle(sel => {
const el = document.querySelector(sel);
const counter = { count: 0 };
el.addEventListener(
"pointerover",
() => {
counter.count += 1;
},
{ capture: true }
);
return counter;
}, editorSelector);
await page.mouse.down();
await page.waitForSelector(
`.page[data-page-number = "1"] .annotationEditorLayer.drawing`
);
for (
const endY = rect.y + 2 * rect.height;
y <= endY;
y += rect.height / 10
) {
await page.mouse.move(x, Math.round(y));
}
await page.mouse.up();
await awaitPromise(clickHandle);
await page.waitForSelector(
`.page[data-page-number = "1"] .annotationEditorLayer:not(.drawing)`
);
const { count } = await counterHandle.jsonValue();
expect(count).withContext(`In ${browserName}`).toEqual(0);
})
);
});
});
describe("Select text with the pointer hovering an existing highlight", () => {
let pages;
beforeEach(async () => {
pages = await loadAndWait("tracemonkey.pdf", ".annotationEditorLayer");
});
afterEach(async () => {
await closePages(pages);
});
it("must check that an existing highlight is ignored on hovering", async () => {
await Promise.all(
pages.map(async ([browserName, page]) => {
await switchToHighlight(page);
const rect = await getSpanRectFromText(
page,
1,
"ternative compilation technique for dynamically-typed languages"
);
const editorSelector = getEditorSelector(0);
const x = Math.round(rect.x + rect.width / 2);
let y = Math.round(rect.y + rect.height / 2);
await page.mouse.click(x, y, { count: 3, delay: 100 });
await page.waitForSelector(editorSelector);
await waitForSerialized(page, 1);
await unselectEditor(page, editorSelector);
const clickHandle = await waitForPointerUp(page);
y = rect.y - 3 * rect.height;
await page.mouse.move(x, y);
const counterHandle = await page.evaluateHandle(sel => {
const el = document.querySelector(sel);
const counter = { count: 0 };
el.addEventListener(
"pointerover",
() => {
counter.count += 1;
},
{ capture: true }
);
return counter;
}, editorSelector);
await page.mouse.down();
await page.waitForSelector(
`.page[data-page-number = "1"] .textLayer.selecting`
);
for (
const endY = rect.y + 3 * rect.height;
y <= endY;
y += rect.height / 10
) {
await page.mouse.move(x, Math.round(y));
}
await page.mouse.up();
await awaitPromise(clickHandle);
await page.waitForSelector(
`.page[data-page-number = "1"] .textLayer:not(.selecting)`
);
const { count } = await counterHandle.jsonValue();
expect(count).withContext(`In ${browserName}`).toEqual(0);
})
);
});
});
describe("Highlight with the floating button in a pdf containing a FreeText", () => {
let pages;
beforeEach(async () => {
pages = await loadAndWait(
"file_pdfjs_test.pdf",
".annotationEditorLayer",
null,
null,
{ highlightEditorColors: "red=#AB0000" }
);
});
afterEach(async () => {
await closePages(pages);
});
it("must check that the highlight is created", async () => {
await Promise.all(
pages.map(async ([browserName, page]) => {
const rect = await getSpanRectFromText(page, 1, "In production");
const x = rect.x + rect.width / 2;
const y = rect.y + rect.height / 2;
await page.mouse.click(x, y, { count: 3, delay: 100 });
await page.waitForSelector(".textLayer .highlightButton");
await page.click(".textLayer .highlightButton");
await page.waitForSelector(getEditorSelector(0));
const usedColor = await page.evaluate(() => {
const highlight = document.querySelector(
`.page[data-page-number = "1"] .canvasWrapper > svg.highlight`
);
return highlight.getAttribute("fill");
});
expect(usedColor).withContext(`In ${browserName}`).toEqual("#AB0000");
})
);
});
});
describe("Highlight (edit existing in double clicking on it)", () => {
let pages;
beforeEach(async () => {
pages = await loadAndWait(
"highlights.pdf",
".annotationEditorLayer",
null,
null,
{
highlightEditorColors:
"yellow=#FFFF00,green=#00FF00,blue=#0000FF,pink=#FF00FF,red=#FF0102",
}
);
});
afterEach(async () => {
await closePages(pages);
});
it("must change the color of an highlight", async () => {
await Promise.all(
pages.map(async ([browserName, page]) => {
const modeChangedHandle = await waitForAnnotationModeChanged(page);
await waitAndClick(page, "[data-annotation-id='687R']", { count: 2 });
await awaitPromise(modeChangedHandle);
await page.waitForSelector("#highlightParamsToolbarContainer");
const editorSelector = getEditorSelector(5);
await page.waitForSelector(editorSelector);
await waitAndClick(
page,
`${editorSelector} .editToolbar button.colorPicker`
);
await waitAndClick(
page,
`${editorSelector} .editToolbar button[title = "Red"]`
);
await page.waitForSelector(
`.page[data-page-number = "1"] svg.highlight[fill = "#FF0102"]`
);
})
);
});
});
describe("Highlight (delete an existing annotation)", () => {
let pages;
beforeEach(async () => {
pages = await loadAndWait(
"highlight_popup.pdf",
".annotationEditorLayer"
);
});
afterEach(async () => {
await closePages(pages);
});
it("must delete an existing annotation and its popup", async () => {
await Promise.all(
pages.map(async ([browserName, page]) => {
const modeChangedHandle = await waitForAnnotationModeChanged(page);
await waitAndClick(page, "[data-annotation-id='24R']", { count: 2 });
await awaitPromise(modeChangedHandle);
await page.waitForSelector("#highlightParamsToolbarContainer");
const editorSelector = getEditorSelector(0);
await page.waitForSelector(editorSelector);
await page.waitForSelector(`${editorSelector} button.delete`);
await page.click(`${editorSelector} button.delete`);
await waitForSerialized(page, 1);
const serialized = await getSerialized(page);
expect(serialized)
.withContext(`In ${browserName}`)
.toEqual([
{
pageIndex: 0,
id: "24R",
deleted: true,
popupRef: "25R",
},
]);
})
);
});
});
describe("Free Highlight (edit existing in double clicking on it)", () => {
let pages;
beforeEach(async () => {
pages = await loadAndWait(
"highlights.pdf",
".annotationEditorLayer",
null,
null,
{
highlightEditorColors:
"yellow=#FFFF00,green=#00FF00,blue=#0000FF,pink=#FF00FF,red=#FF0102",
}
);
});
afterEach(async () => {
await closePages(pages);
});
it("must change the color of a free highlight", async () => {
await Promise.all(
pages.map(async ([browserName, page]) => {
const modeChangedHandle = await waitForAnnotationModeChanged(page);
await page.click("[data-annotation-id='693R']", { count: 2 });
await awaitPromise(modeChangedHandle);
await page.waitForSelector("#highlightParamsToolbarContainer");
const editorSelector = getEditorSelector(6);
await page.waitForSelector(editorSelector);
await page.focus(editorSelector);
await waitForSelectedEditor(page, editorSelector);
await waitAndClick(
page,
`${editorSelector} .editToolbar button.colorPicker`
);
await waitAndClick(
page,
`${editorSelector} .editToolbar button[title = "Red"]`
);
await page.waitForSelector(
`.page[data-page-number = "1"] svg.highlight[fill = "#FF0102"]`
);
})
);
});
});
describe("Highlight editor mustn't throw when disabled", () => {
let pages;
beforeEach(async () => {
pages = await loadAndWait(
"annotation-highlight.pdf",
".annotationEditorLayer"
);
});
afterEach(async () => {
await closePages(pages);
});
it("must enable & disable highlight mode successfully", async () => {
await Promise.all(
pages.map(async ([browserName, page]) => {
const modeChangedHandle = await waitForAnnotationModeChanged(page);
await switchToHighlight(page);
await awaitPromise(modeChangedHandle);
await page.waitForSelector("#highlightParamsToolbarContainer", {
visible: true,
});
await switchToHighlight(page, /* disable */ true);
await page.waitForSelector("#highlightParamsToolbarContainer", {
visible: false,
});
})
);
});
});
describe("Free Highlight with an image in the struct tree", () => {
let pages;
beforeEach(async () => {
pages = await loadAndWait(
"bug1708040.pdf",
".annotationEditorLayer",
null,
null,
{ highlightEditorColors: "red=#AB0000" }
);
});
afterEach(async () => {
await closePages(pages);
});
it("must check that it's possible to draw on an image in a struct tree", async () => {
await Promise.all(
pages.map(async ([browserName, page]) => {
await switchToHighlight(page);
const rect = await getRect(page, `.textLayer span[role="img"]`);
const x = rect.x + rect.width / 2;
const y = rect.y + rect.height / 2;
const clickHandle = await waitForPointerUp(page);
await page.mouse.move(x, y);
await page.mouse.down();
await page.mouse.move(rect.x - 1, rect.y - 1);
await page.mouse.up();
await awaitPromise(clickHandle);
await page.waitForSelector(getEditorSelector(0));
const usedColor = await page.evaluate(() => {
const highlight = document.querySelector(
`.page[data-page-number = "1"] .canvasWrapper > svg.highlight`
);
return highlight.getAttribute("fill");
});
expect(usedColor).withContext(`In ${browserName}`).toEqual("#AB0000");
})
);
});
});
2024-10-19 02:19:46 +05:30
describe("Undo deletion popup has the expected behaviour", () => {
let pages;
const editorSelector = getEditorSelector(0);
beforeEach(async () => {
pages = await loadAndWait(
"tracemonkey.pdf",
".annotationEditorLayer",
null,
null,
{
highlightEditorColors:
"yellow=#FFFF00,green=#00FF00,blue=#0000FF,pink=#FF00FF,red=#FF0000",
}
);
});
afterEach(async () => {
await closePages(pages);
});
it("must check that deleting a highlight can be undone using the undo button", async () => {
await Promise.all(
pages.map(async ([browserName, page]) => {
await switchToHighlight(page);
const rect = await getSpanRectFromText(page, 1, "Abstract");
const x = rect.x + rect.width / 2;
const y = rect.y + rect.height / 2;
await page.mouse.click(x, y, { count: 2, delay: 100 });
await page.waitForSelector(editorSelector);
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("#editorUndoBarUndoButton");
await waitForSerialized(page, 1);
await page.waitForSelector(editorSelector);
await page.waitForSelector(
`.page[data-page-number = "1"] svg.highlight[fill = "#FFFF00"]`
);
})
);
});
it("must check that the popup disappears when the undo button is clicked", async () => {
await Promise.all(
pages.map(async ([browserName, page]) => {
await switchToHighlight(page);
const rect = await getSpanRectFromText(page, 1, "Abstract");
const x = rect.x + rect.width / 2;
const y = rect.y + rect.height / 2;
await page.mouse.click(x, y, { count: 2, delay: 100 });
await page.waitForSelector(editorSelector);
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("#editorUndoBarUndoButton");
await page.waitForSelector("#editorUndoBar", { hidden: true });
})
);
});
it("must check that the popup disappears when the close button is clicked", async () => {
await Promise.all(
pages.map(async ([browserName, page]) => {
await switchToHighlight(page);
const rect = await getSpanRectFromText(page, 1, "Abstract");
const x = rect.x + rect.width / 2;
const y = rect.y + rect.height / 2;
await page.mouse.click(x, y, { count: 2, delay: 100 });
await page.waitForSelector(editorSelector);
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.waitForSelector("#editorUndoBarCloseButton");
await page.click("#editorUndoBarCloseButton");
await page.waitForSelector("#editorUndoBar", { hidden: true });
})
);
});
it("must check that the popup disappears when a new annotation is created", async () => {
await Promise.all(
pages.map(async ([browserName, page]) => {
await switchToHighlight(page);
const rect = await getSpanRectFromText(page, 1, "Abstract");
const x = rect.x + rect.width / 2;
const y = rect.y + rect.height / 2;
await page.mouse.click(x, y, { count: 2, delay: 100 });
await page.waitForSelector(editorSelector);
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])");
const newRect = await getSpanRectFromText(page, 1, "Introduction");
const newX = newRect.x + newRect.width / 2;
const newY = newRect.y + newRect.height / 2;
await page.mouse.click(newX, newY, { count: 2, delay: 100 });
await page.waitForSelector(getEditorSelector(1));
await page.waitForSelector("#editorUndoBar", { hidden: true });
})
);
});
it("must check that the popup disappears when the print dialog is opened", async () => {
await Promise.all(
pages.map(async ([browserName, page]) => {
await switchToHighlight(page);
const rect = await getSpanRectFromText(page, 1, "Abstract");
const x = rect.x + rect.width / 2;
const y = rect.y + rect.height / 2;
await page.mouse.click(x, y, { count: 2, delay: 100 });
await page.waitForSelector(editorSelector);
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.evaluate(() => window.print());
await page.waitForSelector("#editorUndoBar", { hidden: true });
})
);
});
it("must check that the popup disappears when the user clicks on the print button", async () => {
await Promise.all(
pages.map(async ([browserName, page]) => {
await switchToHighlight(page);
const rect = await getSpanRectFromText(page, 1, "Abstract");
const x = rect.x + rect.width / 2;
const y = rect.y + rect.height / 2;
await page.mouse.click(x, y, { count: 2, delay: 100 });
await page.waitForSelector(editorSelector);
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("#printButton");
await page.waitForSelector("#editorUndoBar", { hidden: true });
})
);
});
it("must check that the popup disappears when the save dialog is opened", async () => {
await Promise.all(
pages.map(async ([browserName, page]) => {
await switchToHighlight(page);
const rect = await getSpanRectFromText(page, 1, "Abstract");
const x = rect.x + rect.width / 2;
const y = rect.y + rect.height / 2;
await page.mouse.click(x, y, { count: 2, delay: 100 });
await page.waitForSelector(editorSelector);
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 kbSave(page);
await page.waitForSelector("#editorUndoBar", { hidden: true });
})
);
});
it("must check that the popup disappears when an option from the secondaryToolbar is used", async () => {
await Promise.all(
pages.map(async ([browserName, page]) => {
await switchToHighlight(page);
const rect = await getSpanRectFromText(page, 1, "Abstract");
const x = rect.x + rect.width / 2;
const y = rect.y + rect.height / 2;
await page.mouse.click(x, y, { count: 2, delay: 100 });
await page.waitForSelector(editorSelector);
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("#secondaryToolbarToggleButton");
await page.click("#lastPage");
await page.waitForSelector("#editorUndoBar", { hidden: true });
})
);
});
it("must check that the popup disappears when highlight mode is disabled", async () => {
await Promise.all(
pages.map(async ([browserName, page]) => {
await switchToHighlight(page);
const rect = await getSpanRectFromText(page, 1, "Abstract");
const x = rect.x + rect.width / 2;
const y = rect.y + rect.height / 2;
await page.mouse.click(x, y, { count: 2, delay: 100 });
await page.waitForSelector(editorSelector);
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 switchToHighlight(page, /* disable */ true);
await page.waitForSelector("#editorUndoBar", { hidden: true });
})
);
});
it("must check that the popup disappears when a PDF is drag-and-dropped", async () => {
await Promise.all(
pages.map(async ([browserName, page]) => {
await switchToHighlight(page);
const rect = await getSpanRectFromText(page, 1, "Abstract");
const x = rect.x + rect.width / 2;
const y = rect.y + rect.height / 2;
await page.mouse.click(x, y, { count: 2, delay: 100 });
await page.waitForSelector(editorSelector);
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])");
const pdfPath = path.join(__dirname, "../pdfs/basicapi.pdf");
const pdfData = fs.readFileSync(pdfPath).toString("base64");
const dataTransfer = await page.evaluateHandle(data => {
const transfer = new DataTransfer();
const view = Uint8Array.from(atob(data), code =>
code.charCodeAt(0)
);
const file = new File([view], "basicapi.pdf", {
type: "application/pdf",
});
transfer.items.add(file);
return transfer;
}, pdfData);
const dropSelector = "#viewer";
await page.evaluate(
(transfer, selector) => {
const dropTarget = document.querySelector(selector);
const event = new DragEvent("dragstart", {
dataTransfer: transfer,
});
dropTarget.dispatchEvent(event);
},
dataTransfer,
dropSelector
);
await page.evaluate(
(transfer, selector) => {
const dropTarget = document.querySelector(selector);
const event = new DragEvent("drop", {
dataTransfer: transfer,
bubbles: true,
});
dropTarget.dispatchEvent(event);
},
dataTransfer,
dropSelector
);
await page.waitForSelector("#editorUndoBar", { hidden: true });
})
);
});
it("must check that the undo deletion popup displays the correct message", async () => {
await Promise.all(
pages.map(async ([browserName, page]) => {
await switchToHighlight(page);
const rect = await getSpanRectFromText(page, 1, "Abstract");
const x = rect.x + rect.width / 2;
const y = rect.y + rect.height / 2;
await page.mouse.click(x, y, { count: 2, delay: 100 });
await page.waitForSelector(editorSelector);
await waitForSerialized(page, 1);
await page.waitForSelector(`${editorSelector} button.delete`);
await page.click(`${editorSelector} button.delete`);
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("Highlight removed");
})
);
});
it("must display correct message for multiple highlights", async () => {
await Promise.all(
pages.map(async ([browserName, page]) => {
await switchToHighlight(page);
let rect = await getSpanRectFromText(page, 1, "Abstract");
let x = rect.x + rect.width / 2;
let y = rect.y + rect.height / 2;
await page.mouse.click(x, y, { count: 2, delay: 100 });
await page.waitForSelector(editorSelector);
rect = await getSpanRectFromText(page, 1, "Languages");
x = rect.x + rect.width / 2;
y = rect.y + rect.height / 2;
await page.mouse.click(x, y, { count: 2, delay: 100 });
await page.waitForSelector(getEditorSelector(1));
await selectAll(page);
await page.waitForSelector(`${editorSelector} button.delete`);
await page.click(`${editorSelector} button.delete`);
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
);
// Cleans the message text by removing all non-ASCII characters.
// It eliminates any invisible characters such as directional marks
// that interfere with string comparisons
const cleanMessage = messageText.replaceAll(/\P{ASCII}/gu, "");
expect(cleanMessage).toContain(`2 annotations removed`);
})
);
});
it("must work properly when selecting undo by keyboard", async () => {
await Promise.all(
pages.map(async ([browserName, page]) => {
await switchToHighlight(page);
const rect = await getSpanRectFromText(page, 1, "Abstract");
const x = rect.x + rect.width / 2;
const y = rect.y + rect.height / 2;
await page.mouse.click(x, y, { count: 2, delay: 100 });
await page.waitForSelector(editorSelector);
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.focus("#editorUndoBarUndoButton"); // we have to simulate focus like this to avoid the wait
await page.keyboard.press("Enter");
await waitForSerialized(page, 1);
await page.waitForSelector(editorSelector);
await page.waitForSelector(
`.page[data-page-number = "1"] svg.highlight[fill = "#FFFF00"]`
);
await page.waitForSelector(`${editorSelector} button.delete`);
await page.click(`${editorSelector} button.delete`);
await waitForSerialized(page, 0);
await page.waitForSelector("#editorUndoBar:not([hidden])");
await page.focus("#editorUndoBarUndoButton"); // we have to simulate focus like this to avoid the wait
await page.keyboard.press(" ");
await waitForSerialized(page, 1);
await page.waitForSelector(editorSelector);
await page.waitForSelector(
`.page[data-page-number = "1"] svg.highlight[fill = "#FFFF00"]`
);
})
);
});
it("must dismiss itself when user presses space/enter key and undo key isn't focused", async () => {
await Promise.all(
pages.map(async ([browserName, page]) => {
await switchToHighlight(page);
const rect = await getSpanRectFromText(page, 1, "Abstract");
const x = rect.x + rect.width / 2;
const y = rect.y + rect.height / 2;
await page.mouse.click(x, y, { count: 2, delay: 100 });
await page.waitForSelector(editorSelector);
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.focus("#editorUndoBar");
await page.keyboard.press("Enter");
await page.waitForSelector("#editorUndoBar", { hidden: true });
})
);
});
});
describe("Highlight mustn't trigger a scroll when edited", () => {
let pages;
beforeEach(async () => {
pages = await loadAndWait("issue18911.pdf", ".annotationEditorLayer");
});
afterEach(async () => {
await closePages(pages);
});
it("must check that there is no scroll because of focus", async () => {
await Promise.all(
pages.map(async ([browserName, page]) => {
await switchToHighlight(page);
const page4Selector = ".page[data-page-number = '4']";
const page5Selector = ".page[data-page-number = '5']";
await scrollIntoView(page, page4Selector);
await page.waitForSelector(`${page5Selector} .annotationEditorLayer`);
// When moving to page 4, the highlight editor mustn't be focused (it
// was causing a scroll to page 5).
// So here we're waiting a bit and checking that the page is still 4.
// eslint-disable-next-line no-restricted-syntax
await waitForTimeout(100);
// Get the length of the intersection between two ranges.
const inter = ([a, b], [c, d]) =>
d < a || b < c ? 0 : Math.min(b, d) - Math.max(a, c);
const page4Rect = await getRect(page, page4Selector);
const page5Rect = await getRect(page, page5Selector);
const viewportRect = await getRect(page, "#viewerContainer");
const viewportRange = [
viewportRect.y,
viewportRect.y + viewportRect.height,
];
const interPage4 = inter(
[page4Rect.y, page4Rect.y + page4Rect.height],
viewportRange
);
const interPage5 = inter(
[page5Rect.y, page5Rect.y + page5Rect.height],
viewportRange
);
expect(interPage4)
.withContext(`In ${browserName}`)
.toBeGreaterThan(0.5 * interPage5);
})
);
});
});
describe("Highlight must be rotated when existing in the pdf", () => {
let pages;
beforeEach(async () => {
pages = await loadAndWait("issue19424.pdf", ".annotationEditorLayer");
});
afterEach(async () => {
await closePages(pages);
});
it("must check that there is no scroll because of focus", async () => {
await Promise.all(
pages.map(async ([browserName, page]) => {
await page.evaluate(() => {
window.PDFViewerApplication.rotatePages(90);
});
await page.waitForSelector(
".annotationEditorLayer[data-main-rotation='90']"
);
await switchToHighlight(page);
await page.waitForSelector(
".canvasWrapper svg[data-main-rotation='90']"
);
})
);
});
});
});