From 741649c31ddb40e4111e5518fdf2af79ca7faf6d Mon Sep 17 00:00:00 2001 From: calixteman Date: Sat, 21 Mar 2026 21:44:44 +0100 Subject: [PATCH] Add an integration test for the simple viewer --- test/components/simple-viewer.html | 70 ++++++++++++++++ test/components/simple-viewer.js | 106 ++++++++++++++++++++++++ test/integration/jasmine-boot.js | 1 + test/integration/simple_viewer_spec.mjs | 58 +++++++++++++ 4 files changed, 235 insertions(+) create mode 100644 test/components/simple-viewer.html create mode 100644 test/components/simple-viewer.js create mode 100644 test/integration/simple_viewer_spec.mjs diff --git a/test/components/simple-viewer.html b/test/components/simple-viewer.html new file mode 100644 index 000000000..49703137d --- /dev/null +++ b/test/components/simple-viewer.html @@ -0,0 +1,70 @@ + + + + + + + + PDF.js — Simple viewer + + + + + + + + + + +
+
+
+ + diff --git a/test/components/simple-viewer.js b/test/components/simple-viewer.js new file mode 100644 index 000000000..a8db16b0a --- /dev/null +++ b/test/components/simple-viewer.js @@ -0,0 +1,106 @@ +/* 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 { getDocument, GlobalWorkerOptions } from "pdfjs-lib"; +import { EventBus } from "../../web/event_utils.js"; +import { GenericL10n } from "../../web/genericl10n.js"; +import { PDFFindController } from "../../web/pdf_find_controller.js"; +import { PDFLinkService } from "../../web/pdf_link_service.js"; +import { PDFScriptingManager } from "../../web/pdf_scripting_manager.js"; +import { PDFViewer } from "../../web/pdf_viewer.js"; + +// The workerSrc property shall be specified. +// +GlobalWorkerOptions.workerSrc = + typeof PDFJSDev === "undefined" + ? "../../src/pdf.worker.js" + : "../../build/pdf.worker.mjs"; + +// Some PDFs need external cmaps. +// +const CMAP_URL = + typeof PDFJSDev === "undefined" + ? "../../external/bcmaps/" + : "../../web/cmaps/"; + +const DEFAULT_URL = "../../web/compressed.tracemonkey-pldi-09.pdf"; + +const ENABLE_XFA = true; +const SEARCH_FOR = ""; // try "Mozilla"; + +const SANDBOX_BUNDLE_SRC = new URL( + typeof PDFJSDev === "undefined" + ? "../../src/pdf.sandbox.js" + : "../../build/pdf.sandbox.mjs", + window.location +); + +const fileUrl = new URLSearchParams(location.search).get("file") ?? DEFAULT_URL; + +const container = document.getElementById("viewerContainer"); + +const eventBus = new EventBus(); + +// (Optionally) enable hyperlinks within PDF files. +const pdfLinkService = new PDFLinkService({ + eventBus, +}); + +// (Optionally) enable find controller. +const pdfFindController = new PDFFindController({ + eventBus, + linkService: pdfLinkService, +}); + +// (Optionally) enable scripting support. +const pdfScriptingManager = new PDFScriptingManager({ + eventBus, + sandboxBundleSrc: SANDBOX_BUNDLE_SRC, +}); + +const pdfViewer = new PDFViewer({ + container, + eventBus, + l10n: new GenericL10n(navigator.language), + linkService: pdfLinkService, + findController: pdfFindController, + scriptingManager: pdfScriptingManager, +}); +pdfLinkService.setViewer(pdfViewer); +pdfScriptingManager.setViewer(pdfViewer); + +eventBus.on("pagesinit", function () { + // We can use pdfViewer now, e.g. let's change default scale. + pdfViewer.currentScaleValue = "page-width"; + + // We can try searching for things. + if (SEARCH_FOR) { + eventBus.dispatch("find", { type: "", query: SEARCH_FOR }); + } +}); + +// Loading document. +const loadingTask = getDocument({ + url: fileUrl, + cMapUrl: CMAP_URL, + enableXfa: ENABLE_XFA, +}); + +const pdfDocument = await loadingTask.promise; +// Document loaded, specifying document for the viewer and +// the (optional) linkService. +pdfViewer.setDocument(pdfDocument); + +pdfLinkService.setDocument(pdfDocument, null); diff --git a/test/integration/jasmine-boot.js b/test/integration/jasmine-boot.js index 59e599ee8..26e1ce134 100644 --- a/test/integration/jasmine-boot.js +++ b/test/integration/jasmine-boot.js @@ -40,6 +40,7 @@ async function runTests(results) { "reorganize_pages_spec.mjs", "scripting_spec.mjs", "signature_editor_spec.mjs", + "simple_viewer_spec.mjs", "stamp_editor_spec.mjs", "text_field_spec.mjs", "text_layer_spec.mjs", diff --git a/test/integration/simple_viewer_spec.mjs b/test/integration/simple_viewer_spec.mjs new file mode 100644 index 000000000..34dc6e293 --- /dev/null +++ b/test/integration/simple_viewer_spec.mjs @@ -0,0 +1,58 @@ +/* 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. + */ + +// Integration tests for the simple viewer (test/components/). + +describe("Simple viewer", () => { + describe("TextLayerBuilder without abortSignal", () => { + let pages; + + beforeEach(async () => { + const origin = new URL(global.integrationBaseUrl).origin; + pages = await Promise.all( + global.integrationSessions.map(async session => { + const page = await session.browser.newPage(); + await page.goto( + `${origin}/test/components/simple-viewer.html` + + `?file=/test/pdfs/tracemonkey.pdf` + ); + await page.bringToFront(); + await page.waitForSelector( + "[data-page-number='1'] .textLayer .endOfContent" + ); + await page.waitForSelector( + "[data-page-number='2'] .textLayer .endOfContent" + ); + return [session.name, page]; + }) + ); + }); + + afterEach(async () => { + await Promise.all(pages.map(([, page]) => page.close())); + }); + + it("must produce text spans in the text layer", async () => { + await Promise.all( + pages.map(async ([browserName, page]) => { + const count = await page.evaluate( + () => document.querySelectorAll(".textLayer span").length + ); + expect(count).withContext(`In ${browserName}`).toBeGreaterThan(0); + }) + ); + }); + }); +});