From b540b6333f377e790eca727b684080c2efe838e3 Mon Sep 17 00:00:00 2001 From: Tim van der Meij Date: Sat, 6 Jul 2024 19:07:03 +0200 Subject: [PATCH] Introduce a `waitForScripting` helper function and use it in all scripting integration tests Code inspection uncovered that quite a few integration tests don't wait for scripting to be ready before proceeding, which is a source of intermittent failures, especially on slower machines where e.g. creating the sandbox takes longer. This commit fixes the issues by introducing a `waitForScripting` helper function and calling it consistently in all scripting integration tests. --- test/integration/scripting_spec.mjs | 201 ++++++++++++---------------- 1 file changed, 86 insertions(+), 115 deletions(-) diff --git a/test/integration/scripting_spec.mjs b/test/integration/scripting_spec.mjs index b1e08197b730b..f41c271b882ea 100644 --- a/test/integration/scripting_spec.mjs +++ b/test/integration/scripting_spec.mjs @@ -32,6 +32,12 @@ import { waitForTimeout, } from "./test_utils.mjs"; +async function waitForScripting(page) { + await page.waitForFunction( + "window.PDFViewerApplication.scriptingReady === true" + ); +} + describe("Interaction", () => { async function actAndWaitForInput(page, selector, action, clear = true) { await page.waitForSelector(selector, { @@ -61,9 +67,8 @@ describe("Interaction", () => { it("must check that first text field has focus", async () => { await Promise.all( pages.map(async ([browserName, page]) => { - await page.waitForFunction( - "window.PDFViewerApplication.scriptingReady === true" - ); + await waitForScripting(page); + await page.waitForFunction(`window.document.activeElement !== null`); // The document has an open action in order to give the focus to 401R. @@ -79,6 +84,8 @@ describe("Interaction", () => { it("must show a text field and then make in invisible when content is removed", async () => { await Promise.all( pages.map(async ([browserName, page]) => { + await waitForScripting(page); + let visibility = await page.$eval( getSelector("427R"), el => getComputedStyle(el).visibility @@ -121,6 +128,8 @@ describe("Interaction", () => { it("must format the field with 2 digits and leave field with a click", async () => { await Promise.all( pages.map(async ([browserName, page]) => { + await waitForScripting(page); + await page.type(getSelector("416R"), "3.14159", { delay: 200 }); await page.click(getSelector("419R")); @@ -139,6 +148,8 @@ describe("Interaction", () => { it("must format the field with 2 digits, leave field with a click and again", async () => { await Promise.all( pages.map(async ([browserName, page]) => { + await waitForScripting(page); + await page.type(getSelector("448R"), "61803", { delay: 200 }); await page.click(getSelector("419R")); @@ -178,6 +189,8 @@ describe("Interaction", () => { it("must format the field with 2 digits and leave field with a TAB", async () => { await Promise.all( pages.map(async ([browserName, page]) => { + await waitForScripting(page); + const prevSum = await page.$eval(getSelector("427R"), el => el.value); await page.type(getSelector("422R"), "2.7182818", { delay: 200 }); @@ -203,6 +216,8 @@ describe("Interaction", () => { it("must format the field with 2 digits and hit ESC", async () => { await Promise.all( pages.map(async ([browserName, page]) => { + await waitForScripting(page); + let sum = await page.$eval(getSelector("471R"), el => el.value); expect(sum).withContext(`In ${browserName}`).toEqual("4,24"); @@ -225,6 +240,8 @@ describe("Interaction", () => { it("must format the field with 2 digits on key ENTER", async () => { await Promise.all( pages.map(async ([browserName, page]) => { + await waitForScripting(page); + const prevSum = await page.$eval(getSelector("427R"), el => el.value); await page.type(getSelector("419R"), "0.577215", { delay: 200 }); @@ -245,6 +262,8 @@ describe("Interaction", () => { it("must reset all", async () => { await Promise.all( pages.map(async ([browserName, page]) => { + await waitForScripting(page); + // click on a radio button await page.click("[data-annotation-id='449R']"); @@ -303,9 +322,7 @@ describe("Interaction", () => { it("must show values in a text input when clicking on radio buttons", async () => { await Promise.all( pages.map(async ([browserName, page]) => { - await page.waitForFunction( - "window.PDFViewerApplication.scriptingReady === true" - ); + await waitForScripting(page); const expected = [ ["81R", "Group1=Choice1::1"], @@ -331,6 +348,8 @@ describe("Interaction", () => { it("must show values in a text input when clicking on checkboxes", async () => { await Promise.all( pages.map(async ([browserName, page]) => { + await waitForScripting(page); + const expected = [ ["85R", "Check1=Yes::5"], ["87R", "Check2=Yes::6"], @@ -359,6 +378,8 @@ describe("Interaction", () => { it("must show values in a text input when clicking on checkboxes in a group", async () => { await Promise.all( pages.map(async ([browserName, page]) => { + await waitForScripting(page); + const expected = [ ["90R", "Check5=Yes1::9"], ["91R", "Check5=Yes2::10"], @@ -384,6 +405,8 @@ describe("Interaction", () => { it("must show values in a text input when clicking on checkboxes or radio with no actions", async () => { await Promise.all( pages.map(async ([browserName, page]) => { + await waitForScripting(page); + const expected = [ ["", "Off;Off"], ["94R", "Yes;Off"], @@ -425,9 +448,7 @@ describe("Interaction", () => { // And to make sure that a printer isn't locked by a process we close the // page before running the next test. for (const [browserName, page] of pages) { - await page.waitForFunction( - "window.PDFViewerApplication.scriptingReady === true" - ); + await waitForScripting(page); await clearInput(page, getSelector("47R")); await page.evaluate(_ => { @@ -467,9 +488,7 @@ describe("Interaction", () => { it("must execute WillSave and DidSave actions", async () => { await Promise.all( pages.map(async ([browserName, page]) => { - await page.waitForFunction( - "window.PDFViewerApplication.scriptingReady === true" - ); + await waitForScripting(page); try { // Disable download in chrome @@ -516,9 +535,7 @@ describe("Interaction", () => { it("must execute PageOpen and PageClose actions", async () => { await Promise.all( pages.map(async ([browserName, page]) => { - await page.waitForFunction( - "window.PDFViewerApplication.scriptingReady === true" - ); + await waitForScripting(page); await page.waitForFunction(`${getQuerySelector("47R")}.value !== ""`); @@ -575,6 +592,8 @@ describe("Interaction", () => { it("must print authors in a text field", async () => { await Promise.all( pages.map(async ([browserName, page]) => { + await waitForScripting(page); + const text = await actAndWaitForInput( page, getSelector("25R"), @@ -604,6 +623,8 @@ describe("Interaction", () => { it("must print selected value in a text field", async () => { await Promise.all( pages.map(async ([browserName, page]) => { + await waitForScripting(page); + for (const num of [7, 6, 4, 3, 2, 1]) { await clearInput(page, getSelector("33R")); await page.click(`option[value=Export${num}]`); @@ -622,6 +643,8 @@ describe("Interaction", () => { it("must clear and restore list elements", async () => { await Promise.all( pages.map(async ([browserName, page]) => { + await waitForScripting(page); + // Click on ClearItems button. await page.click("[data-annotation-id='34R']"); await page.waitForFunction( @@ -652,6 +675,8 @@ describe("Interaction", () => { it("must insert new elements", async () => { await Promise.all( pages.map(async ([browserName, page]) => { + await waitForScripting(page); + let len = 6; for (const num of [1, 3, 5, 6, 431, -1, 0]) { ++len; @@ -690,6 +715,8 @@ describe("Interaction", () => { it("must delete some element", async () => { await Promise.all( pages.map(async ([browserName, page]) => { + await waitForScripting(page); + let len = 6; // Click on Restore button. await clearInput(page, getSelector("33R")); @@ -741,6 +768,8 @@ describe("Interaction", () => { it("must change colors", async () => { await Promise.all( pages.map(async ([browserName, page]) => { + await waitForScripting(page); + for (const [name, ref] of [ ["Text1", "34R"], ["Check1", "35R"], @@ -814,9 +843,7 @@ describe("Interaction", () => { it("must compute sum of fields", async () => { await Promise.all( pages.map(async ([browserName, page]) => { - await page.waitForFunction( - "window.PDFViewerApplication.scriptingReady === true" - ); + await waitForScripting(page); await scrollIntoView(page, getSelector("138R")); @@ -877,9 +904,7 @@ describe("Interaction", () => { it("must check page index", async () => { await Promise.all( pages.map(async ([browserName, page]) => { - await page.waitForFunction( - "window.PDFViewerApplication.scriptingReady === true" - ); + await waitForScripting(page); await clearInput(page, getSelector("55R")); await page.type( @@ -903,6 +928,8 @@ describe("Interaction", () => { it("must check display", async () => { await Promise.all( pages.map(async ([browserName, page]) => { + await waitForScripting(page); + for (const [type, vis] of [ ["hidden", "hidden"], ["noPrint", "visible"], @@ -952,9 +979,7 @@ describe("Interaction", () => { it("must update fields with the same name from JS", async () => { await Promise.all( pages.map(async ([browserName, page]) => { - await page.waitForFunction( - "window.PDFViewerApplication.scriptingReady === true" - ); + await waitForScripting(page); await page.type(getSelector("27R"), "hello"); await page.keyboard.press("Enter"); @@ -988,6 +1013,8 @@ describe("Interaction", () => { it("must print securityHandler value in a text field", async () => { await Promise.all( pages.map(async ([browserName, page]) => { + await waitForScripting(page); + const text = await actAndWaitForInput( page, getSelector("25R"), @@ -1021,9 +1048,7 @@ describe("Interaction", () => { // Run the tests sequentially to avoid any focus issues between the two // browsers when an alert is displayed. for (const [browserName, page] of pages) { - await page.waitForFunction( - "window.PDFViewerApplication.scriptingReady === true" - ); + await waitForScripting(page); await clearInput(page, getSelector("29R")); await clearInput(page, getSelector("30R")); @@ -1078,9 +1103,7 @@ describe("Interaction", () => { // Run the tests sequentially to avoid any focus issues between the two // browsers when an alert is displayed. for (const [browserName, page] of pages) { - await page.waitForFunction( - "window.PDFViewerApplication.scriptingReady === true" - ); + await waitForScripting(page); await clearInput(page, getSelector("29R")); await clearInput(page, getSelector("30R")); @@ -1135,9 +1158,7 @@ describe("Interaction", () => { // Run the tests sequentially to avoid any focus issues between the two // browsers when an alert is displayed. for (const [browserName, page] of pages) { - await page.waitForFunction( - "window.PDFViewerApplication.scriptingReady === true" - ); + await waitForScripting(page); await clearInput(page, getSelector("29R")); await clearInput(page, getSelector("30R")); @@ -1191,9 +1212,7 @@ describe("Interaction", () => { it("must convert input to uppercase", async () => { await Promise.all( pages.map(async ([browserName, page]) => { - await page.waitForFunction( - "window.PDFViewerApplication.scriptingReady === true" - ); + await waitForScripting(page); await page.type(getSelector("27R"), "Hello", { delay: 200 }); await page.waitForFunction( @@ -1257,9 +1276,7 @@ describe("Interaction", () => { it("must check that an infinite loop is not triggered", async () => { await Promise.all( pages.map(async ([browserName, page]) => { - await page.waitForFunction( - "window.PDFViewerApplication.scriptingReady === true" - ); + await waitForScripting(page); await page.click(getSelector("28R")); await page.$eval(getSelector("28R"), el => @@ -1310,9 +1327,7 @@ describe("Interaction", () => { it("must check that field value is correctly updated", async () => { await Promise.all( pages.map(async ([browserName, page]) => { - await page.waitForFunction( - "window.PDFViewerApplication.scriptingReady === true" - ); + await waitForScripting(page); await page.type(getSelector("29R"), "Hello World", { delay: 200 }); await page.click(getSelector("27R")); @@ -1351,9 +1366,7 @@ describe("Interaction", () => { it("must check that field value is correctly formatted", async () => { await Promise.all( pages.map(async ([browserName, page]) => { - await page.waitForFunction( - "window.PDFViewerApplication.scriptingReady === true" - ); + await waitForScripting(page); let text = await page.$eval(getSelector("75R"), el => el.value); expect(text).withContext(`In ${browserName}`).toEqual("150.32 €"); @@ -1385,9 +1398,7 @@ describe("Interaction", () => { it("must check that a button and text field with a border are hidden", async () => { await Promise.all( pages.map(async ([browserName, page]) => { - await page.waitForFunction( - "window.PDFViewerApplication.scriptingReady === true" - ); + await waitForScripting(page); let visibility = await page.$eval( "[data-annotation-id='35R']", @@ -1440,9 +1451,7 @@ describe("Interaction", () => { it("must check that data-main-rotation is correct", async () => { await Promise.all( pages.map(async ([browserName, page]) => { - await page.waitForFunction( - "window.PDFViewerApplication.scriptingReady === true" - ); + await waitForScripting(page); let base = 0; @@ -1488,9 +1497,7 @@ describe("Interaction", () => { it("must check that a values is correctly updated on a field and its siblings", async () => { await Promise.all( pages.map(async ([browserName, page]) => { - await page.waitForFunction( - "window.PDFViewerApplication.scriptingReady === true" - ); + await waitForScripting(page); await clearInput(page, getSelector("39R")); await page.type(getSelector("39R"), "123", { delay: 10 }); @@ -1536,9 +1543,7 @@ describe("Interaction", () => { it("must check that charLimit is correctly set", async () => { await Promise.all( pages.map(async ([, page]) => { - await page.waitForFunction( - "window.PDFViewerApplication.scriptingReady === true" - ); + await waitForScripting(page); // The default charLimit is 0, which indicates unlimited text length. await page.type(getSelector("7R"), "abcdefghij", { delay: 10 }); @@ -1583,9 +1588,7 @@ describe("Interaction", () => { it("must check field value is treated by default as a number", async () => { await Promise.all( pages.map(async ([browserName, page]) => { - await page.waitForFunction( - "window.PDFViewerApplication.scriptingReady === true" - ); + await waitForScripting(page); await page.type(getSelector("30R"), "123", { delay: 10, @@ -1619,9 +1622,7 @@ describe("Interaction", () => { it("must check field value is correctly updated when committed with ENTER key", async () => { await Promise.all( pages.map(async ([browserName, page]) => { - await page.waitForFunction( - "window.PDFViewerApplication.scriptingReady === true" - ); + await waitForScripting(page); await page.type(getSelector("27R"), "abc", { delay: 10, @@ -1670,9 +1671,7 @@ describe("Interaction", () => { it("must check field value is correctly updated when committed with ENTER key", async () => { await Promise.all( pages.map(async ([browserName, page]) => { - await page.waitForFunction( - "window.PDFViewerApplication.scriptingReady === true" - ); + await waitForScripting(page); let value = "A"; for (const [displayValue, exportValue] of [ @@ -1719,9 +1718,7 @@ describe("Interaction", () => { it("must check the field value set when the document is open", async () => { await Promise.all( pages.map(async ([browserName, page]) => { - await page.waitForFunction( - "window.PDFViewerApplication.scriptingReady === true" - ); + await waitForScripting(page); await page.waitForFunction(`${getQuerySelector("27R")}.value !== ""`); @@ -1734,9 +1731,7 @@ describe("Interaction", () => { it("must check the format action is called when setFocus is used", async () => { await Promise.all( pages.map(async ([browserName, page]) => { - await page.waitForFunction( - "window.PDFViewerApplication.scriptingReady === true" - ); + await waitForScripting(page); await page.type(getSelector("30R"), "abc", { delay: 200 }); await page.waitForFunction( @@ -1811,6 +1806,8 @@ describe("Interaction", () => { it("must check if printing is triggered when the document is open", async () => { await Promise.all( pages.map(async ([browserName, page]) => { + await waitForScripting(page); + await awaitPromise(await printHandles.get(page)); }) ); @@ -1831,9 +1828,7 @@ describe("Interaction", () => { it("must check that a field value with a number isn't changed", async () => { await Promise.all( pages.map(async ([browserName, page]) => { - await page.waitForFunction( - "window.PDFViewerApplication.scriptingReady === true" - ); + await waitForScripting(page); await page.click(getSelector("25R")); await page.type(getSelector("25R"), "00000000123", { delay: 10 }); @@ -1865,9 +1860,7 @@ describe("Interaction", () => { it("must check that a field value with a number with a comma has the correct value", async () => { await Promise.all( pages.map(async ([browserName, page]) => { - await page.waitForFunction( - "window.PDFViewerApplication.scriptingReady === true" - ); + await waitForScripting(page); let text = await page.$eval(getSelector("22R"), el => el.value); expect(text).withContext(`In ${browserName}`).toEqual("5,25"); @@ -1907,9 +1900,7 @@ describe("Interaction", () => { it("must check that a field has the correct value when a choice is changed", async () => { await Promise.all( pages.map(async ([browserName, page]) => { - await page.waitForFunction( - "window.PDFViewerApplication.scriptingReady === true" - ); + await waitForScripting(page); let text = await page.$eval(getSelector("44R"), el => el.value); expect(text).withContext(`In ${browserName}`).toEqual(""); @@ -1944,9 +1935,7 @@ describe("Interaction", () => { it("must check that a field has the correct formatted value", async () => { await Promise.all( pages.map(async ([browserName, page]) => { - await page.waitForFunction( - "window.PDFViewerApplication.scriptingReady === true" - ); + await waitForScripting(page); let text = await page.$eval(getSelector("23R"), el => el.value); expect(text) @@ -1967,9 +1956,7 @@ describe("Interaction", () => { it("must check that a field is empty", async () => { await Promise.all( pages.map(async ([browserName, page]) => { - await page.waitForFunction( - "window.PDFViewerApplication.scriptingReady === true" - ); + await waitForScripting(page); let text = await page.$eval(getSelector("26R"), el => el.value); expect(text).withContext(`In ${browserName}`).toEqual(""); @@ -2002,6 +1989,8 @@ describe("Interaction", () => { it("must check that a field has the correct formatted value", async () => { await Promise.all( pages.map(async ([browserName, page]) => { + await waitForScripting(page); + const hasVisibleCanvas = await page.$eval( `[data-annotation-id="9R"] > canvas`, elem => elem && !elem.hasAttribute("hidden") @@ -2058,9 +2047,7 @@ describe("Interaction", () => { it("must check that invisible fields are made visible", async () => { await Promise.all( pages.map(async ([browserName, page]) => { - await page.waitForFunction( - "window.PDFViewerApplication.scriptingReady === true" - ); + await waitForScripting(page); let visibility = await page.$eval( getSelector("7R"), @@ -2116,9 +2103,7 @@ describe("Interaction", () => { it("must check that checkboxes are correctly resetted", async () => { await Promise.all( pages.map(async ([browserName, page]) => { - await page.waitForFunction( - "window.PDFViewerApplication.scriptingReady === true" - ); + await waitForScripting(page); let readonly = await page.$eval( getSelector("353R"), @@ -2188,9 +2173,7 @@ describe("Interaction", () => { it("must check that focus/blur callbacks aren't called", async () => { await Promise.all( pages.map(async ([browserName, page], i) => { - await page.waitForFunction( - "window.PDFViewerApplication.scriptingReady === true" - ); + await waitForScripting(page); await page.click(getSelector("55R")); await page.type(getSelector("55R"), "Hello", { delay: 10 }); @@ -2234,9 +2217,7 @@ describe("Interaction", () => { it("must check that blur callback is called", async () => { await Promise.all( pages.map(async ([browserName, page], i) => { - await page.waitForFunction( - "window.PDFViewerApplication.scriptingReady === true" - ); + await waitForScripting(page); await page.click(getSelector("25R")); await page.click(getSelector("26R")); @@ -2275,11 +2256,9 @@ describe("Interaction", () => { it("must check that only one radio is selected", async () => { await Promise.all( pages.map(async ([browserName, page], i) => { - await page.waitForFunction( - "window.PDFViewerApplication.scriptingReady === true" - ); - await scrollIntoView(page, getSelector("22R")); + await waitForScripting(page); + await scrollIntoView(page, getSelector("22R")); await page.click(getSelector("25R")); await waitForEntryInStorage(page, "25R", { value: true }); @@ -2341,9 +2320,7 @@ describe("Interaction", () => { it("must check the number has the correct number of decimals", async () => { await Promise.all( pages.map(async ([browserName, page], i) => { - await page.waitForFunction( - "window.PDFViewerApplication.scriptingReady === true" - ); + await waitForScripting(page); await page.click(getSelector("15R")); await page.type(getSelector("15R"), "3"); @@ -2383,9 +2360,7 @@ describe("Interaction", () => { it("must check the zip code is correctly formatted", async () => { await Promise.all( pages.map(async ([browserName, page], i) => { - await page.waitForFunction( - "window.PDFViewerApplication.scriptingReady === true" - ); + await waitForScripting(page); await page.click(getSelector("24R")); await page.type(getSelector("24R"), "01234", { delay: 10 }); @@ -2420,9 +2395,7 @@ describe("Interaction", () => { it("must check the properties of the event", async () => { await Promise.all( pages.map(async ([browserName, page], i) => { - await page.waitForFunction( - "window.PDFViewerApplication.scriptingReady === true" - ); + await waitForScripting(page); for (const [value, expected] of [ ["b", "change=B,changeEx=b,value=A"], @@ -2463,9 +2436,7 @@ describe("Interaction", () => { it("must check that PageOpen/PageClose actions are correctly executed", async () => { await Promise.all( pages.map(async ([browserName, page], i) => { - await page.waitForFunction( - "window.PDFViewerApplication.scriptingReady === true" - ); + await waitForScripting(page); const buttonSelector = `[data-annotation-id="25R"`; await page.waitForSelector(buttonSelector, {