diff --git a/playwright/src/test/java/com/microsoft/playwright/Server.java b/playwright/src/test/java/com/microsoft/playwright/Server.java index 3c1946068..64867a1ea 100644 --- a/playwright/src/test/java/com/microsoft/playwright/Server.java +++ b/playwright/src/test/java/com/microsoft/playwright/Server.java @@ -196,6 +196,12 @@ public void handle(HttpExchange exchange) throws IOException { handler.handle(exchange); return; } + // Headless Chrome requests the favicon after a load and logs a 404 to the console of the page. + if ("/favicon.ico".equals(path)) { + exchange.sendResponseHeaders(204, -1); + exchange.close(); + return; + } if (csp.containsKey(path)) { exchange.getResponseHeaders().add("Content-Security-Policy", csp.get(path)); diff --git a/playwright/src/test/java/com/microsoft/playwright/fixtures/TestServer.java b/playwright/src/test/java/com/microsoft/playwright/fixtures/TestServer.java index d0d0c0732..e9c09cc6d 100644 --- a/playwright/src/test/java/com/microsoft/playwright/fixtures/TestServer.java +++ b/playwright/src/test/java/com/microsoft/playwright/fixtures/TestServer.java @@ -414,6 +414,10 @@ private void onRequest(Request request, Response response) { try { if (handler != null) { handler.handle(request, response); + } else if (pathWithSearch.equals("/favicon.ico")) { + // Java: headless Chrome requests the favicon after a load and logs a 404 to the console of the page. + response.statusCode = 204; + response.end(); } else { serveFile(request, response); } diff --git a/playwright/src/test/java/com/microsoft/playwright/tests/library/TestBrowsercontextTimezoneId.java b/playwright/src/test/java/com/microsoft/playwright/tests/library/TestBrowsercontextTimezoneId.java index 877ec93f9..32d352721 100644 --- a/playwright/src/test/java/com/microsoft/playwright/tests/library/TestBrowsercontextTimezoneId.java +++ b/playwright/src/test/java/com/microsoft/playwright/tests/library/TestBrowsercontextTimezoneId.java @@ -147,8 +147,7 @@ void shouldPropagateTimezoneToWorkers(Browser browser, TestServer server) { BrowserContext context = browser.newContext(new Browser.NewContextOptions().setTimezoneId("America/Jamaica")); Page page = context.newPage(); page.navigate(server.EMPTY_PAGE); - // Java: only a log message, Chrome on Windows can log the 404 error of /favicon.ico first. - ConsoleMessage msg = page.waitForConsoleMessage(new Page.WaitForConsoleMessageOptions().setPredicate(m -> "log".equals(m.type())), () -> + ConsoleMessage msg = page.waitForConsoleMessage(() -> page.evaluate("() => new Worker(URL.createObjectURL(new Blob(['console.log(Intl.DateTimeFormat().resolvedOptions().timeZone)'], { type: 'application/javascript' })))")); assertEquals("America/Jamaica", msg.text()); context.close(); diff --git a/playwright/src/test/java/com/microsoft/playwright/tests/library/TestCapabilities.java b/playwright/src/test/java/com/microsoft/playwright/tests/library/TestCapabilities.java index 818cd1da7..fb4cec0c3 100644 --- a/playwright/src/test/java/com/microsoft/playwright/tests/library/TestCapabilities.java +++ b/playwright/src/test/java/com/microsoft/playwright/tests/library/TestCapabilities.java @@ -190,17 +190,15 @@ void shouldNotCrashOnShowDirectoryPicker(Page page, TestServer server) { page.navigate(server.EMPTY_PAGE); // "User activation is required to show a file picker." - so we click first. page.locator("body").click(); - // Java: upstream does not await the evaluate; the picker is opened from the page without returning its promise. page.evaluate("() => {\n" + - " window.__picker = (async () => {\n" + - " const dir = await window.showDirectoryPicker();\n" + - " return dir.name;\n" + - " // In headless it throws (aborted), in headed it stalls (Test ended) and waits for the picker to be accepted.\n" + - " })().catch(e => e.message);\n" + + " window.showDirectoryPicker().catch(e => { window.__pickerError = String(e); });\n" + "}"); // The dialog will not be accepted, so we just wait for some time to // to give the browser a chance to crash. page.waitForTimeout(3_000); + // In headless it throws (aborted), in headed it stalls and waits for the picker to be accepted. + String error = (String) page.evaluate("() => window.__pickerError ?? 'stalled'"); + assertTrue(Pattern.compile("((DOMException|AbortError): .*The user aborted a request|stalled)").matcher(error).find(), error); } @PlaywrightTest diff --git a/playwright/src/test/java/com/microsoft/playwright/tests/library/chromium/TestOopif.java b/playwright/src/test/java/com/microsoft/playwright/tests/library/chromium/TestOopif.java index 6c2821cb7..80a8deeb7 100644 --- a/playwright/src/test/java/com/microsoft/playwright/tests/library/chromium/TestOopif.java +++ b/playwright/src/test/java/com/microsoft/playwright/tests/library/chromium/TestOopif.java @@ -555,7 +555,9 @@ void shouldReportTheWholeFrameTreeWhenConnectingOverCDPToNestedOOPIFs(BrowserTyp } private static List dumpFrames(Frame frame, String indent) { - assertEquals(frame.url(), frame.evaluate("() => '' + location.href")); + // Java: the URL is read after the evaluate, as upstream does; the frame of a new OOPIF gets it with its context. + Object href = frame.evaluate("() => '' + location.href"); + assertEquals(frame.url(), href); List result = new ArrayList<>(); result.add(indent + frame.url()); for (Frame child : frame.childFrames()) diff --git a/playwright/src/test/java/com/microsoft/playwright/tests/page/TestPageClick.java b/playwright/src/test/java/com/microsoft/playwright/tests/page/TestPageClick.java index 2ee8dad81..bf4849a89 100644 --- a/playwright/src/test/java/com/microsoft/playwright/tests/page/TestPageClick.java +++ b/playwright/src/test/java/com/microsoft/playwright/tests/page/TestPageClick.java @@ -223,12 +223,7 @@ void shouldSelectTheTextByTripleClicking(Page page, TestServer server) { void shouldClickOffscreenButtons(Page page, TestServer server) { page.navigate(server.PREFIX + "/offscreenbuttons.html"); List messages = new ArrayList<>(); - // Java: only log messages, Edge on macOS can log the 404 error of /favicon.ico first. - page.onConsoleMessage(msg -> { - if ("log".equals(msg.type())) { - messages.add(msg.text()); - } - }); + page.onConsoleMessage(msg -> messages.add(msg.text())); for (int i = 0; i < 11; ++i) { // We might've scrolled to click a button - reset to (0, 0). page.evaluate("() => window.scrollTo(0, 0)"); diff --git a/playwright/src/test/java/com/microsoft/playwright/tests/page/TestPageDrag.java b/playwright/src/test/java/com/microsoft/playwright/tests/page/TestPageDrag.java index 22c36b2f3..101ae809e 100644 --- a/playwright/src/test/java/com/microsoft/playwright/tests/page/TestPageDrag.java +++ b/playwright/src/test/java/com/microsoft/playwright/tests/page/TestPageDrag.java @@ -572,7 +572,6 @@ void shouldHandleCustomDataTransfer(Page page, BrowserName browserName) { assumeFalse(browserName == BrowserName.WEBKIT && OS.WINDOWS.isCurrentOs(), "fixme"); page.setContent(""); - // Java: the promise is kept on window and awaited after the drag, so that the evaluate returns at once. page.evaluate("() => {\n" + " window.__result = new Promise(resolve => {\n" + " document.addEventListener('dragstart', event => {\n" + diff --git a/playwright/src/test/java/com/microsoft/playwright/tests/page/TestPageEvaluate.java b/playwright/src/test/java/com/microsoft/playwright/tests/page/TestPageEvaluate.java index f69d6cb00..89866ac66 100644 --- a/playwright/src/test/java/com/microsoft/playwright/tests/page/TestPageEvaluate.java +++ b/playwright/src/test/java/com/microsoft/playwright/tests/page/TestPageEvaluate.java @@ -632,16 +632,14 @@ void shouldSimulateAUserGesture(Page page) { @PlaywrightTest @DisplayName("EV-063 should throw a nice error after a navigation") void shouldThrowANiceErrorAfterANavigation(Page page) { - // Java: the first evaluate blocks, so the reload that upstream triggers with a second evaluate - // is scheduled from the page before the promise is returned. - PlaywrightException error = assertThrows(PlaywrightException.class, () -> page.evaluate("() => {\n" + - " setTimeout(() => {\n" + - " window.location.reload();\n" + - " setTimeout(() => window['__resolve'](42), 1000);\n" + - " }, 0);\n" + - " return new Promise(f => window['__resolve'] = f);\n" + - "}")); - assertTrue(error.getMessage().contains("navigation"), error.getMessage()); + PlaywrightException[] error = {null}; + page.waitForNavigation(() -> { + error[0] = assertThrows(PlaywrightException.class, () -> page.evaluate("() => new Promise(f => setTimeout(() => {\n" + + " window.location.reload();\n" + + " setTimeout(() => f(42), 1000);\n" + + "}, 0))")); + }); + assertTrue(error[0].getMessage().contains("navigation"), error[0].getMessage()); } @PlaywrightTest diff --git a/playwright/src/test/java/com/microsoft/playwright/tests/page/TestPageRequestFulfill.java b/playwright/src/test/java/com/microsoft/playwright/tests/page/TestPageRequestFulfill.java index a527f4817..371e8c29b 100644 --- a/playwright/src/test/java/com/microsoft/playwright/tests/page/TestPageRequestFulfill.java +++ b/playwright/src/test/java/com/microsoft/playwright/tests/page/TestPageRequestFulfill.java @@ -142,8 +142,6 @@ void shouldNotThrowIfRequestWasCancelledByThePage(Page page, TestServer server) Route[] interceptedRoute = {null}; page.route("**/data.json", route -> interceptedRoute[0] = route); page.navigate(server.EMPTY_PAGE); - // Java: upstream does not await the evaluate; the fetch is started in the page without returning - // its promise, so that evaluate returns while the route is pending. page.evaluate("url => {\n" + " globalThis.controller = new AbortController();\n" + " fetch(url, { signal: globalThis.controller.signal }).catch(() => {});\n" + diff --git a/playwright/src/test/java/com/microsoft/playwright/tests/page/TestPageRoute.java b/playwright/src/test/java/com/microsoft/playwright/tests/page/TestPageRoute.java index e17592ed8..a5f24bbe9 100644 --- a/playwright/src/test/java/com/microsoft/playwright/tests/page/TestPageRoute.java +++ b/playwright/src/test/java/com/microsoft/playwright/tests/page/TestPageRoute.java @@ -291,26 +291,26 @@ void shouldPauseInterceptedXHRUntilContinue(Page page, TestServer server) { page.navigate(server.EMPTY_PAGE); Route[] routePromise = {null}; page.route("**/global-var.html", route -> routePromise[0] = route); - // Java: the synchronous request blocks the page, so it is started from a page timer and its status - // is kept on window; the evaluate returns at once and the route stays pending. - CompletableFuture serverRequest = server.waitForRequest("/global-var.html"); + boolean[] xhrFinished = {false}; + page.onRequestFinished(request -> { + if (request.url().endsWith("/global-var.html")) + xhrFinished[0] = true; + }); + // The synchronous request blocks the page, so it is started from a timer. page.evaluate("() => {\n" + - " window.__status = undefined;\n" + - " setTimeout(() => {\n" + + " window.__statusPromise = new Promise(resolve => setTimeout(() => {\n" + " const request = new XMLHttpRequest();\n" + " request.open('GET', '/global-var.html', false); // `false` makes the request synchronous\n" + " request.send(null);\n" + - " window.__status = request.status;\n" + - " }, 0);\n" + + " resolve(request.status);\n" + + " }, 0));\n" + "}"); page.waitForCondition(() -> routePromise[0] != null); // Check that intercepted request is actually paused. - // Java: the page is blocked by the synchronous request, so the check is that the server has not seen it. page.waitForTimeout(500); - assertFalse(serverRequest.isDone()); + assertFalse(xhrFinished[0]); routePromise[0].resume(); - page.waitForFunction("() => window.__status !== undefined"); - assertEquals(200, page.evaluate("() => window.__status")); + assertEquals(200, page.evaluate("() => window.__statusPromise")); } @PlaywrightTest @@ -319,8 +319,6 @@ void shouldPauseInterceptedFetchRequestUntilContinue(Page page, TestServer serve page.navigate(server.EMPTY_PAGE); Route[] routePromise = {null}; page.route("**/global-var.html", route -> routePromise[0] = route); - // Java: the fetch is started in the page and its promise kept on window, so that the evaluate returns - // while the route is pending; the promise is awaited by a second evaluate. page.evaluate("() => {\n" + " window.__fetchFinished = false;\n" + " window.__statusPromise = fetch('/global-var.html').then(response => {\n" + @@ -333,8 +331,7 @@ void shouldPauseInterceptedFetchRequestUntilContinue(Page page, TestServer serve page.waitForTimeout(500); assertEquals(false, page.evaluate("() => window.__fetchFinished")); routePromise[0].resume(); - Object status = page.evaluate("() => window.__statusPromise"); - assertEquals(200, status); + assertEquals(200, page.evaluate("() => window.__statusPromise")); } @PlaywrightTest