Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions playwright/src/test/java/com/microsoft/playwright/Server.java
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -555,7 +555,9 @@ void shouldReportTheWholeFrameTreeWhenConnectingOverCDPToNestedOOPIFs(BrowserTyp
}

private static List<String> 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<String> result = new ArrayList<>();
result.add(indent + frame.url());
for (Frame child : frame.childFrames())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -223,12 +223,7 @@ void shouldSelectTheTextByTripleClicking(Page page, TestServer server) {
void shouldClickOffscreenButtons(Page page, TestServer server) {
page.navigate(server.PREFIX + "/offscreenbuttons.html");
List<String> 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)");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -572,7 +572,6 @@ void shouldHandleCustomDataTransfer(Page page, BrowserName browserName) {
assumeFalse(browserName == BrowserName.WEBKIT && OS.WINDOWS.isCurrentOs(), "fixme");
page.setContent("<button draggable=\"true\">Draggable</button>");

// 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" +
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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" +
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<TestServer.Request> 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
Expand All @@ -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" +
Expand All @@ -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
Expand Down
Loading