mirror of
https://github.com/mozilla/pdf.js.git
synced 2026-04-09 14:54:04 +02:00
552 lines
16 KiB
JavaScript
552 lines
16 KiB
JavaScript
/* Copyright 2026 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,
|
|
FSI,
|
|
getThumbnailSelector,
|
|
kbFocusNext,
|
|
loadAndWait,
|
|
PDI,
|
|
showViewsManager,
|
|
} from "./test_utils.mjs";
|
|
|
|
function waitForThumbnailVisible(page, pageNum) {
|
|
return page.waitForSelector(getThumbnailSelector(pageNum), { visible: true });
|
|
}
|
|
|
|
async function waitForMenu(page, buttonSelector, visible = true) {
|
|
return page.waitForFunction(
|
|
(selector, vis) => {
|
|
const button = document.querySelector(selector);
|
|
if (!button) {
|
|
return false;
|
|
}
|
|
return button.getAttribute("aria-expanded") === (vis ? "true" : "false");
|
|
},
|
|
{},
|
|
buttonSelector,
|
|
visible
|
|
);
|
|
}
|
|
|
|
describe("PDF Thumbnail View", () => {
|
|
describe("Works without errors", () => {
|
|
let pages;
|
|
|
|
beforeEach(async () => {
|
|
pages = await loadAndWait("tracemonkey.pdf", "#viewsManagerToggleButton");
|
|
});
|
|
|
|
afterEach(async () => {
|
|
await closePages(pages);
|
|
});
|
|
|
|
it("should render thumbnails without errors", async () => {
|
|
await Promise.all(
|
|
pages.map(async ([browserName, page]) => {
|
|
await showViewsManager(page);
|
|
|
|
const thumbSelector =
|
|
"#thumbnailsView .thumbnailImageContainer > img";
|
|
await page.waitForSelector(thumbSelector, { visible: true });
|
|
|
|
await waitForThumbnailVisible(page, 1);
|
|
|
|
await page.waitForSelector(`${thumbSelector}[src^="blob:http:"]`, {
|
|
visible: true,
|
|
});
|
|
|
|
const title = await page.$eval(
|
|
getThumbnailSelector(1),
|
|
el => el.title
|
|
);
|
|
expect(title)
|
|
.withContext(`In ${browserName}`)
|
|
.toBe(`Page ${FSI}1${PDI} of ${FSI}14${PDI}`);
|
|
})
|
|
);
|
|
});
|
|
|
|
it("should have accessible label on resizer", async () => {
|
|
await Promise.all(
|
|
pages.map(async ([browserName, page]) => {
|
|
await showViewsManager(page);
|
|
|
|
const ariaLabel = await page.$eval("#viewsManagerResizer", el =>
|
|
el.getAttribute("aria-label")
|
|
);
|
|
expect(ariaLabel)
|
|
.withContext(`In ${browserName}`)
|
|
.toBe("Sidebar resizer");
|
|
})
|
|
);
|
|
});
|
|
});
|
|
|
|
describe("The view is scrolled correctly", () => {
|
|
let pages;
|
|
|
|
beforeEach(async () => {
|
|
pages = await loadAndWait("tracemonkey.pdf", "#viewsManagerToggleButton");
|
|
});
|
|
|
|
afterEach(async () => {
|
|
await closePages(pages);
|
|
});
|
|
|
|
async function goToPage(page, number) {
|
|
const handle = await page.evaluateHandle(
|
|
num => [
|
|
new Promise(resolve => {
|
|
const container = document.getElementById("viewsManagerContent");
|
|
container.addEventListener("scrollend", resolve, { once: true });
|
|
// eslint-disable-next-line no-undef
|
|
PDFViewerApplication.pdfLinkService.goToPage(num);
|
|
}),
|
|
],
|
|
number
|
|
);
|
|
return awaitPromise(handle);
|
|
}
|
|
|
|
it("should scroll the view", async () => {
|
|
await Promise.all(
|
|
pages.map(async ([browserName, page]) => {
|
|
await showViewsManager(page);
|
|
await waitForThumbnailVisible(page, 1);
|
|
|
|
for (const pageNum of [14, 1, 13, 2]) {
|
|
await goToPage(page, pageNum);
|
|
const thumbSelector = getThumbnailSelector(pageNum);
|
|
await page.waitForSelector(
|
|
`.thumbnail ${thumbSelector}[aria-current="page"]`,
|
|
{ visible: true }
|
|
);
|
|
await page.waitForSelector(
|
|
`${thumbSelector} > img[src^="blob:http:"]`,
|
|
{
|
|
visible: true,
|
|
}
|
|
);
|
|
}
|
|
})
|
|
);
|
|
});
|
|
});
|
|
|
|
describe("The view is accessible with the keyboard", () => {
|
|
let pages;
|
|
|
|
beforeEach(async () => {
|
|
pages = await loadAndWait("tracemonkey.pdf", "#viewsManagerToggleButton");
|
|
});
|
|
|
|
afterEach(async () => {
|
|
await closePages(pages);
|
|
});
|
|
|
|
it("should navigate with the keyboard", async () => {
|
|
await Promise.all(
|
|
pages.map(async ([browserName, page]) => {
|
|
await showViewsManager(page);
|
|
await waitForThumbnailVisible(page, 1);
|
|
await waitForThumbnailVisible(page, 2);
|
|
await waitForThumbnailVisible(page, 3);
|
|
|
|
await kbFocusNext(page);
|
|
await page.waitForSelector("#viewsManagerSelectorButton:focus", {
|
|
visible: true,
|
|
});
|
|
|
|
await kbFocusNext(page);
|
|
await page.waitForSelector("#viewsManagerStatusActionButton:focus", {
|
|
visible: true,
|
|
});
|
|
|
|
await kbFocusNext(page);
|
|
await page.waitForSelector(
|
|
`#thumbnailsView ${getThumbnailSelector(1)}:focus`,
|
|
{ visible: true }
|
|
);
|
|
|
|
await page.keyboard.press("ArrowDown");
|
|
await page.waitForSelector(
|
|
`#thumbnailsView ${getThumbnailSelector(2)}:focus`,
|
|
{ visible: true }
|
|
);
|
|
|
|
await page.keyboard.press("ArrowUp");
|
|
await page.waitForSelector(`${getThumbnailSelector(1)}:focus`, {
|
|
visible: true,
|
|
});
|
|
|
|
await page.keyboard.press("ArrowDown");
|
|
await page.keyboard.press("ArrowDown");
|
|
await page.waitForSelector(
|
|
`#thumbnailsView ${getThumbnailSelector(3)}:focus`,
|
|
{ visible: true }
|
|
);
|
|
|
|
await page.keyboard.press("Enter");
|
|
const currentPage = await page.$eval(
|
|
"#pageNumber",
|
|
el => el.valueAsNumber
|
|
);
|
|
expect(currentPage).withContext(`In ${browserName}`).toBe(3);
|
|
|
|
await page.keyboard.press("End");
|
|
await page.waitForSelector(
|
|
`#thumbnailsView ${getThumbnailSelector(14)}:focus`,
|
|
{ visible: true }
|
|
);
|
|
|
|
await page.keyboard.press("Home");
|
|
await page.waitForSelector(
|
|
`#thumbnailsView ${getThumbnailSelector(1)}:focus`,
|
|
{ visible: true }
|
|
);
|
|
})
|
|
);
|
|
});
|
|
});
|
|
|
|
describe("The manage dropdown menu", () => {
|
|
let pages;
|
|
|
|
beforeEach(async () => {
|
|
pages = await loadAndWait(
|
|
"tracemonkey.pdf",
|
|
"#viewsManagerToggleButton",
|
|
null,
|
|
null,
|
|
{ enableSplitMerge: true }
|
|
);
|
|
});
|
|
|
|
afterEach(async () => {
|
|
await closePages(pages);
|
|
});
|
|
|
|
async function enableMenuItems(page) {
|
|
await page.evaluate(() => {
|
|
document
|
|
.querySelectorAll("#viewsManagerStatusActionOptions button")
|
|
.forEach(button => {
|
|
button.disabled = false;
|
|
});
|
|
});
|
|
}
|
|
|
|
it("should open with Enter key and remain open", async () => {
|
|
await Promise.all(
|
|
pages.map(async ([browserName, page]) => {
|
|
await showViewsManager(page);
|
|
await waitForThumbnailVisible(page, 1);
|
|
|
|
await enableMenuItems(page);
|
|
|
|
// Focus the manage button
|
|
await kbFocusNext(page);
|
|
await kbFocusNext(page);
|
|
await page.waitForSelector("#viewsManagerStatusActionButton:focus", {
|
|
visible: true,
|
|
});
|
|
|
|
// Press Enter to open the menu
|
|
await page.keyboard.press("Enter");
|
|
|
|
await waitForMenu(page, "#viewsManagerStatusActionButton");
|
|
|
|
// Verify first menu item can be focused
|
|
await page.waitForSelector("#viewsManagerStatusActionCopy:focus", {
|
|
visible: true,
|
|
});
|
|
|
|
// Close menu with Escape
|
|
await page.keyboard.press("Escape");
|
|
await waitForMenu(page, "#viewsManagerStatusActionButton", false);
|
|
})
|
|
);
|
|
});
|
|
|
|
it("should open with Space key and remain open", async () => {
|
|
await Promise.all(
|
|
pages.map(async ([browserName, page]) => {
|
|
await showViewsManager(page);
|
|
await waitForThumbnailVisible(page, 1);
|
|
|
|
await enableMenuItems(page);
|
|
|
|
// Focus the manage button
|
|
await kbFocusNext(page);
|
|
await kbFocusNext(page);
|
|
await page.waitForSelector("#viewsManagerStatusActionButton:focus", {
|
|
visible: true,
|
|
});
|
|
|
|
// Press Space to open the menu
|
|
await page.keyboard.press(" ");
|
|
|
|
await waitForMenu(page, "#viewsManagerStatusActionButton");
|
|
|
|
// Verify first menu item can be focused
|
|
await page.waitForSelector("#viewsManagerStatusActionCopy:focus", {
|
|
visible: true,
|
|
});
|
|
|
|
// Navigate menu items with arrow keys
|
|
await page.keyboard.press("ArrowDown");
|
|
await page.waitForSelector("#viewsManagerStatusActionCut:focus", {
|
|
visible: true,
|
|
});
|
|
|
|
// Menu should still be open
|
|
await waitForMenu(page, "#viewsManagerStatusActionButton");
|
|
|
|
// Close menu with Escape
|
|
await page.keyboard.press("Escape");
|
|
await waitForMenu(page, "#viewsManagerStatusActionButton", false);
|
|
})
|
|
);
|
|
});
|
|
});
|
|
|
|
describe("Checkbox accessibility", () => {
|
|
let pages;
|
|
|
|
beforeEach(async () => {
|
|
pages = await loadAndWait(
|
|
"tracemonkey.pdf",
|
|
"#viewsManagerToggleButton",
|
|
null,
|
|
null,
|
|
{ enableSplitMerge: true }
|
|
);
|
|
});
|
|
|
|
afterEach(async () => {
|
|
await closePages(pages);
|
|
});
|
|
|
|
it("should have a title on the checkbox", async () => {
|
|
await Promise.all(
|
|
pages.map(async ([browserName, page]) => {
|
|
await showViewsManager(page);
|
|
await waitForThumbnailVisible(page, 1);
|
|
|
|
const title = await page.$eval(
|
|
`.thumbnail[page-number="1"] input[type="checkbox"]`,
|
|
el => el.title
|
|
);
|
|
expect(title)
|
|
.withContext(`In ${browserName}`)
|
|
.toBe(`Select page ${FSI}1${PDI}`);
|
|
})
|
|
);
|
|
});
|
|
});
|
|
|
|
describe("Menu keyboard navigation with multi-character keys (bug 2016212)", () => {
|
|
let pages;
|
|
|
|
beforeEach(async () => {
|
|
pages = await loadAndWait(
|
|
"page_with_number_and_link.pdf",
|
|
"#viewsManagerSelectorButton",
|
|
null,
|
|
null,
|
|
{ enableSplitMerge: true }
|
|
);
|
|
});
|
|
|
|
afterEach(async () => {
|
|
await closePages(pages);
|
|
});
|
|
|
|
it("must navigate menus with ArrowDown and Tab keys", async () => {
|
|
await Promise.all(
|
|
pages.map(async ([browserName, page]) => {
|
|
await showViewsManager(page);
|
|
await waitForThumbnailVisible(page, 1);
|
|
|
|
// Focus the views manager selector button
|
|
await page.waitForSelector("#viewsManagerSelectorButton", {
|
|
visible: true,
|
|
});
|
|
await page.focus("#viewsManagerSelectorButton");
|
|
|
|
// Open menu with Enter key
|
|
await page.keyboard.press("Enter");
|
|
|
|
// Wait for menu to be expanded
|
|
await waitForMenu(page, "#viewsManagerSelectorButton");
|
|
|
|
// Check that focus moved to the first menu button (pages)
|
|
await page.waitForSelector("#thumbnailsViewMenu:focus", {
|
|
visible: true,
|
|
});
|
|
|
|
// Press ArrowDown to navigate to second item
|
|
await page.keyboard.press("ArrowDown");
|
|
|
|
// Should now be on outlines button
|
|
await page.waitForSelector("#outlinesViewMenu:focus", {
|
|
visible: true,
|
|
});
|
|
|
|
// Press Tab to move to the manage button (should close views menu)
|
|
await page.keyboard.press("Tab");
|
|
|
|
// Wait for views manager menu to be collapsed
|
|
await waitForMenu(page, "#viewsManagerSelectorButton", false);
|
|
|
|
// Focus should be on manage button
|
|
await page.waitForSelector("#viewsManagerStatusActionButton:focus", {
|
|
visible: true,
|
|
});
|
|
|
|
// Open manage menu with Space key
|
|
await page.keyboard.press(" ");
|
|
|
|
// Wait for manage menu to be expanded
|
|
await waitForMenu(page, "#viewsManagerStatusActionButton");
|
|
})
|
|
);
|
|
});
|
|
});
|
|
|
|
describe("Views manager status visibility (bug 2016656)", () => {
|
|
let pages;
|
|
|
|
beforeEach(async () => {
|
|
pages = await loadAndWait(
|
|
"page_with_number_and_link.pdf",
|
|
"#viewsManagerToggleButton",
|
|
null,
|
|
null,
|
|
{ enableSplitMerge: true }
|
|
);
|
|
});
|
|
|
|
afterEach(async () => {
|
|
await closePages(pages);
|
|
});
|
|
|
|
it("should show the manage button in thumbnail view and hide it in outline view", async () => {
|
|
await Promise.all(
|
|
pages.map(async ([browserName, page]) => {
|
|
await showViewsManager(page);
|
|
await waitForThumbnailVisible(page, 1);
|
|
|
|
// The status bar (Select pages + Manage button) must be visible in
|
|
// thumbnail view.
|
|
await page.waitForSelector("#viewsManagerStatus", { visible: true });
|
|
|
|
// Switch to outline view.
|
|
await page.click("#viewsManagerSelectorButton");
|
|
await page.waitForSelector("#outlinesViewMenu", { visible: true });
|
|
await page.click("#outlinesViewMenu");
|
|
await page.waitForSelector("#outlinesView", { visible: true });
|
|
|
|
// The status bar must no longer be visible in outline view.
|
|
await page.waitForSelector("#viewsManagerStatus", { hidden: true });
|
|
})
|
|
);
|
|
});
|
|
});
|
|
|
|
describe("Checkbox keyboard navigation", () => {
|
|
let pages;
|
|
|
|
beforeEach(async () => {
|
|
pages = await loadAndWait(
|
|
"tracemonkey.pdf",
|
|
"#viewsManagerToggleButton",
|
|
null,
|
|
null,
|
|
{ enableSplitMerge: true }
|
|
);
|
|
});
|
|
|
|
afterEach(async () => {
|
|
await closePages(pages);
|
|
});
|
|
|
|
it("should focus checkboxes with Tab key", async () => {
|
|
await Promise.all(
|
|
pages.map(async ([browserName, page]) => {
|
|
await showViewsManager(page);
|
|
await waitForThumbnailVisible(page, 1);
|
|
|
|
// Focus the first thumbnail button
|
|
await kbFocusNext(page);
|
|
await kbFocusNext(page);
|
|
await kbFocusNext(page);
|
|
|
|
// Verify we're on the first thumbnail
|
|
await page.waitForSelector(`${getThumbnailSelector(1)}:focus`, {
|
|
visible: true,
|
|
});
|
|
|
|
// Tab to checkbox
|
|
await kbFocusNext(page);
|
|
await page.waitForSelector(
|
|
`.thumbnail[page-number="1"] input[type="checkbox"]:focus`,
|
|
{ visible: true }
|
|
);
|
|
})
|
|
);
|
|
});
|
|
|
|
it("should navigate checkboxes with arrow keys", async () => {
|
|
await Promise.all(
|
|
pages.map(async ([browserName, page]) => {
|
|
await showViewsManager(page);
|
|
await waitForThumbnailVisible(page, 1);
|
|
await waitForThumbnailVisible(page, 2);
|
|
|
|
// Navigate to first checkbox
|
|
await kbFocusNext(page);
|
|
await kbFocusNext(page);
|
|
await kbFocusNext(page);
|
|
await kbFocusNext(page);
|
|
|
|
// Verify first checkbox is focused
|
|
await page.waitForSelector(
|
|
`.thumbnail[page-number="1"] input[type="checkbox"]:focus`,
|
|
{ visible: true }
|
|
);
|
|
|
|
// Navigate to next checkbox with ArrowDown
|
|
await page.keyboard.press("ArrowDown");
|
|
await page.waitForSelector(
|
|
`.thumbnail[page-number="2"] input[type="checkbox"]:focus`,
|
|
{ visible: true }
|
|
);
|
|
|
|
// Navigate back with ArrowUp
|
|
await page.keyboard.press("ArrowUp");
|
|
await page.waitForSelector(
|
|
`.thumbnail[page-number="1"] input[type="checkbox"]:focus`,
|
|
{ visible: true }
|
|
);
|
|
})
|
|
);
|
|
});
|
|
});
|
|
});
|