feat: route.fallback with overrides (#958)
This commit is contained in:
@@ -485,7 +485,7 @@ class BrowserContextImpl extends ChannelOwner implements BrowserContext {
|
||||
maybeDisableNetworkInterception();
|
||||
}
|
||||
if (handled != Router.HandleResult.Handled){
|
||||
route.resume(null);
|
||||
route.resume();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -23,7 +23,6 @@ import com.microsoft.playwright.*;
|
||||
import com.microsoft.playwright.options.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
@@ -1048,6 +1047,13 @@ public class FrameImpl extends ChannelOwner implements Frame {
|
||||
if (add != null) {
|
||||
WaitUntilState state = loadStateFromProtocol(add.getAsString());
|
||||
loadStates.add(state);
|
||||
if (parentFrame == null && page != null) {
|
||||
if (state == LOAD) {
|
||||
page.listeners.notify(PageImpl.EventType.LOAD, page);
|
||||
} else if (state == DOMCONTENTLOADED) {
|
||||
page.listeners.notify(PageImpl.EventType.DOMCONTENTLOADED, page);
|
||||
}
|
||||
}
|
||||
internalListeners.notify(InternalEventType.LOADSTATE, state);
|
||||
}
|
||||
JsonElement remove = params.get("remove");
|
||||
|
||||
@@ -175,10 +175,6 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if ("load".equals(event)) {
|
||||
listeners.notify(EventType.LOAD, this);
|
||||
} else if ("domcontentloaded".equals(event)) {
|
||||
listeners.notify(EventType.DOMCONTENTLOADED, this);
|
||||
} else if ("frameAttached".equals(event)) {
|
||||
String guid = params.getAsJsonObject("frame").get("guid").getAsString();
|
||||
FrameImpl frame = connection.getExistingObject(guid);
|
||||
|
||||
@@ -42,6 +42,14 @@ public class RequestImpl extends ChannelOwner implements Request {
|
||||
String failure;
|
||||
Timing timing;
|
||||
boolean didFailOrFinish;
|
||||
private FallbackOverrides fallbackOverrides;
|
||||
|
||||
static class FallbackOverrides {
|
||||
String url;
|
||||
String method;
|
||||
byte[] postData;
|
||||
Map<String, String> headers;
|
||||
}
|
||||
|
||||
RequestImpl(ChannelOwner parent, String type, String guid, JsonObject initializer) {
|
||||
super(parent, type, guid, initializer);
|
||||
@@ -75,6 +83,9 @@ public class RequestImpl extends ChannelOwner implements Request {
|
||||
|
||||
@Override
|
||||
public Map<String, String> headers() {
|
||||
if (fallbackOverrides != null && fallbackOverrides.headers != null) {
|
||||
return new RawHeaders(Utils.toHeadersList(fallbackOverrides.headers)).headers();
|
||||
}
|
||||
return headers.headers();
|
||||
}
|
||||
|
||||
@@ -95,19 +106,26 @@ public class RequestImpl extends ChannelOwner implements Request {
|
||||
|
||||
@Override
|
||||
public String method() {
|
||||
if (fallbackOverrides != null && fallbackOverrides.method != null) {
|
||||
return fallbackOverrides.method;
|
||||
}
|
||||
return initializer.get("method").getAsString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String postData() {
|
||||
if (postData == null) {
|
||||
byte[] buffer = postDataBuffer();
|
||||
if (buffer == null) {
|
||||
return null;
|
||||
}
|
||||
return new String(postData, StandardCharsets.UTF_8);
|
||||
return new String(buffer, StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] postDataBuffer() {
|
||||
if (fallbackOverrides != null && fallbackOverrides.postData != null) {
|
||||
return fallbackOverrides.postData;
|
||||
}
|
||||
return postData;
|
||||
}
|
||||
|
||||
@@ -156,6 +174,13 @@ public class RequestImpl extends ChannelOwner implements Request {
|
||||
|
||||
@Override
|
||||
public String url() {
|
||||
if (fallbackOverrides != null && fallbackOverrides.url != null) {
|
||||
return fallbackOverrides.url;
|
||||
}
|
||||
return originalUrl();
|
||||
}
|
||||
|
||||
String originalUrl() {
|
||||
return initializer.get("url").getAsString();
|
||||
}
|
||||
|
||||
@@ -164,6 +189,9 @@ public class RequestImpl extends ChannelOwner implements Request {
|
||||
}
|
||||
|
||||
private RawHeaders getRawHeaders() {
|
||||
if (fallbackOverrides != null && fallbackOverrides.headers != null) {
|
||||
return new RawHeaders(Utils.toHeadersList(fallbackOverrides.headers));
|
||||
}
|
||||
if (rawHeaders != null) {
|
||||
return rawHeaders;
|
||||
}
|
||||
@@ -176,4 +204,26 @@ public class RequestImpl extends ChannelOwner implements Request {
|
||||
rawHeaders = new RawHeaders(asList(gson().fromJson(rawHeadersJson, HttpHeader[].class)));
|
||||
return rawHeaders;
|
||||
}
|
||||
|
||||
void applyFallbackOverrides(FallbackOverrides overrides) {
|
||||
if (fallbackOverrides == null) {
|
||||
fallbackOverrides = new FallbackOverrides();
|
||||
}
|
||||
if (overrides.url != null) {
|
||||
fallbackOverrides.url = overrides.url;
|
||||
}
|
||||
if (overrides.method != null) {
|
||||
fallbackOverrides.method = overrides.method;
|
||||
}
|
||||
if (overrides.headers != null) {
|
||||
fallbackOverrides.headers = overrides.headers;
|
||||
}
|
||||
if (overrides.postData != null) {
|
||||
fallbackOverrides.postData = overrides.postData;
|
||||
}
|
||||
}
|
||||
|
||||
FallbackOverrides fallbackOverridesForResume() {
|
||||
return fallbackOverrides;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package com.microsoft.playwright.impl;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import com.microsoft.playwright.Frame;
|
||||
import com.microsoft.playwright.PlaywrightException;
|
||||
import com.microsoft.playwright.Route;
|
||||
|
||||
@@ -27,9 +28,10 @@ import java.util.Base64;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import static com.microsoft.playwright.impl.Utils.convertType;
|
||||
|
||||
public class RouteImpl extends ChannelOwner implements Route {
|
||||
private boolean handled;
|
||||
private boolean lastHandlerGaveUp;
|
||||
|
||||
public RouteImpl(ChannelOwner parent, String type, String guid, JsonObject initializer) {
|
||||
super(parent, type, guid, initializer);
|
||||
@@ -45,55 +47,69 @@ public class RouteImpl extends ChannelOwner implements Route {
|
||||
});
|
||||
}
|
||||
|
||||
boolean takeLastHandlerGaveUp() {
|
||||
boolean result = lastHandlerGaveUp;
|
||||
lastHandlerGaveUp = false;
|
||||
return result;
|
||||
boolean isHandled() {
|
||||
return handled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resume(ResumeOptions options) {
|
||||
startHandling();
|
||||
withLogging("Route.resume", () -> resumeImpl(options));
|
||||
applyOverrides(convertType(options, FallbackOptions.class));
|
||||
withLogging("Route.resume", () -> resumeImpl(request().fallbackOverridesForResume()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fallback(FallbackOptions options) {
|
||||
if (lastHandlerGaveUp) {
|
||||
if (handled) {
|
||||
throw new PlaywrightException("Route is already handled!");
|
||||
}
|
||||
lastHandlerGaveUp = true;
|
||||
applyOverrides(options);
|
||||
}
|
||||
|
||||
private void resumeImpl(ResumeOptions options) {
|
||||
private void applyOverrides(FallbackOptions options) {
|
||||
if (options == null) {
|
||||
options = new ResumeOptions();
|
||||
}
|
||||
JsonObject params = new JsonObject();
|
||||
if (options.url != null) {
|
||||
params.addProperty("url", options.url);
|
||||
}
|
||||
if (options.method != null) {
|
||||
params.addProperty("method", options.method);
|
||||
}
|
||||
if (options.headers != null) {
|
||||
params.add("headers", Serialization.toProtocol(options.headers));
|
||||
return;
|
||||
}
|
||||
RequestImpl.FallbackOverrides overrides = new RequestImpl.FallbackOverrides();
|
||||
overrides.url = options.url;
|
||||
overrides.method = options.method;
|
||||
overrides.headers = options.headers;
|
||||
if (options.postData != null) {
|
||||
byte[] bytes = null;
|
||||
if (options.postData instanceof byte[]) {
|
||||
bytes = (byte[]) options.postData;
|
||||
} else if (options.postData instanceof String) {
|
||||
bytes = ((String) options.postData).getBytes(StandardCharsets.UTF_8);
|
||||
} else {
|
||||
throw new PlaywrightException("postData must be either String or byte[], found: " + options.postData.getClass().getName());
|
||||
overrides.postData = getPostDataBytes(options.postData);
|
||||
}
|
||||
request().applyFallbackOverrides(overrides);
|
||||
}
|
||||
|
||||
private void resumeImpl(RequestImpl.FallbackOverrides options) {
|
||||
JsonObject params = new JsonObject();
|
||||
if (options != null) {
|
||||
if (options.url != null) {
|
||||
params.addProperty("url", options.url);
|
||||
}
|
||||
if (options.method != null) {
|
||||
params.addProperty("method", options.method);
|
||||
}
|
||||
if (options.headers != null) {
|
||||
params.add("headers", Serialization.toProtocol(options.headers));
|
||||
}
|
||||
if (options.postData != null) {
|
||||
String base64 = Base64.getEncoder().encodeToString(options.postData);
|
||||
params.addProperty("postData", base64);
|
||||
}
|
||||
String base64 = Base64.getEncoder().encodeToString(bytes);
|
||||
params.addProperty("postData", base64);
|
||||
}
|
||||
sendMessageAsync("continue", params);
|
||||
}
|
||||
|
||||
private static byte[] getPostDataBytes(Object postData) {
|
||||
if (postData instanceof byte[]) {
|
||||
return (byte[]) postData;
|
||||
}
|
||||
if (postData instanceof String) {
|
||||
return ((String) postData).getBytes(StandardCharsets.UTF_8);
|
||||
}
|
||||
throw new PlaywrightException("postData must be either String or byte[], found: " + postData.getClass().getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fulfill(FulfillOptions options) {
|
||||
startHandling();
|
||||
|
||||
@@ -38,11 +38,11 @@ class Router {
|
||||
this.times = times;
|
||||
}
|
||||
|
||||
boolean handle(Route route) {
|
||||
boolean handle(RouteImpl route) {
|
||||
if (times != null && times <= 0) {
|
||||
return false;
|
||||
}
|
||||
if (!matcher.test(route.request().url())) {
|
||||
if (!matcher.test(route.request().originalUrl())) {
|
||||
return false;
|
||||
}
|
||||
if (times != null) {
|
||||
@@ -80,11 +80,10 @@ class Router {
|
||||
if (info.isDone()) {
|
||||
it.remove();
|
||||
}
|
||||
if (route.takeLastHandlerGaveUp()) {
|
||||
result = HandleResult.MatchedHandlerButNotHandled;
|
||||
continue;
|
||||
if (route.isHandled()) {
|
||||
return HandleResult.Handled;
|
||||
}
|
||||
return HandleResult.Handled;
|
||||
result = HandleResult.MatchedHandlerButNotHandled;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
|
||||
@@ -277,6 +277,17 @@ class Utils {
|
||||
return map;
|
||||
}
|
||||
|
||||
static List<HttpHeader> toHeadersList(Map<String, String> headers) {
|
||||
List<HttpHeader> list = new ArrayList<>();
|
||||
for (Map.Entry<String, String> entry: headers.entrySet()) {
|
||||
HttpHeader header = new HttpHeader();
|
||||
header.name = entry.getKey();
|
||||
header.value = entry.getValue();
|
||||
list.add(header);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
static String toJsRegexFlags(Pattern pattern) {
|
||||
String regexFlags = "";
|
||||
if ((pattern.flags() & Pattern.CASE_INSENSITIVE) != 0) {
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package com.microsoft.playwright;
|
||||
|
||||
import com.microsoft.playwright.options.LoadState;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static java.util.Arrays.asList;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
public class TestPageAutowaitingBasic extends TestBase {
|
||||
@Test
|
||||
void shouldWorkWithNoWaitAfterTrue() {
|
||||
server.setRoute("/empty.html", exchange -> {});
|
||||
page.setContent("<a id='anchor' href='${server.EMPTY_PAGE}'>empty.html</a>");
|
||||
page.click("a", new Page.ClickOptions().setNoWaitAfter(true));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldWorkWithDblclickNoWaitAfterTrue() {
|
||||
server.setRoute("/empty.html", exchange -> {});
|
||||
page.setContent("<a id='anchor' href='${server.EMPTY_PAGE}'>empty.html</a>");
|
||||
page.dblclick("a", new Page.DblclickOptions().setNoWaitAfter(true));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldWorkWithWaitForLoadStateLoad() {
|
||||
List<String> messages = new ArrayList<>();
|
||||
server.setRoute("/empty.html", exchange -> {
|
||||
messages.add("route");
|
||||
exchange.getResponseHeaders().add("content-type", "text/html");
|
||||
exchange.sendResponseHeaders(200, 0);
|
||||
try (OutputStreamWriter writer = new OutputStreamWriter(exchange.getResponseBody())) {
|
||||
writer.write("<link rel='stylesheet' href='./one-style.css'>");
|
||||
} catch (RuntimeException e) {
|
||||
e.printStackTrace();
|
||||
throw e;
|
||||
}
|
||||
});
|
||||
page.setContent("<a id='anchor' href='" + server.EMPTY_PAGE + "'>empty.html</a>");
|
||||
|
||||
page.onLoad(p -> messages.add("clickload"));
|
||||
page.click("a");
|
||||
page.waitForLoadState(LoadState.LOAD);
|
||||
messages.add("load");
|
||||
assertEquals(asList("route", "clickload", "load"), messages);
|
||||
}
|
||||
}
|
||||
@@ -164,6 +164,24 @@ public class TestPageBasic extends TestBase {
|
||||
page.waitForLoadState(DOMCONTENTLOADED);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldPassSelfAsArgumentToDomcontentloadedEvent() {
|
||||
Page[] eventPage = {null};
|
||||
page.onDOMContentLoaded(p -> eventPage[0] = p);
|
||||
page.navigate("about:blank");
|
||||
page.waitForLoadState(DOMCONTENTLOADED);
|
||||
assertEquals(page, eventPage[0]);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldPassSelfAsArgumentToLoadEvent() {
|
||||
Page[] eventPage = {null};
|
||||
page.onLoad(p -> eventPage[0] = p);
|
||||
page.navigate("about:blank");
|
||||
page.waitForLoadState(LOAD);
|
||||
assertEquals(page, eventPage[0]);
|
||||
}
|
||||
|
||||
// TODO: downloads
|
||||
void shouldFailWithErrorUponDisconnect() {
|
||||
}
|
||||
|
||||
@@ -18,10 +18,52 @@ package com.microsoft.playwright;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.junit.jupiter.api.Assertions.fail;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
import static java.util.Arrays.asList;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
public class TestPageRequestContinue extends TestBase {
|
||||
@Test
|
||||
void shouldDeleteHeaderWithUndefinedValue() throws ExecutionException, InterruptedException {
|
||||
// https://github.com/microsoft/playwright/issues/13106
|
||||
page.navigate(server.PREFIX + "/empty.html");
|
||||
Future<Server.Request> serverRequest = server.futureRequest("/something");
|
||||
server.setRoute("/something", exchange -> {
|
||||
exchange.getResponseHeaders().add("Access-Control-Allow-Origin", "*");
|
||||
exchange.sendResponseHeaders(200, 0);
|
||||
try (OutputStreamWriter writer = new OutputStreamWriter(exchange.getResponseBody())) {
|
||||
writer.write("done");
|
||||
}
|
||||
});
|
||||
|
||||
Request[] interceptedRequest = {null};
|
||||
page.route(server.PREFIX + "/something", route -> {
|
||||
interceptedRequest[0] = route.request();
|
||||
Map<String, String> headers = route.request().allHeaders();
|
||||
headers.remove("foo");
|
||||
route.resume(new Route.ResumeOptions().setHeaders(headers));
|
||||
});
|
||||
|
||||
Object text = page.evaluate("async url => {\n" +
|
||||
" const data = await fetch(url, {\n" +
|
||||
" headers: {\n" +
|
||||
" foo: 'a',\n" +
|
||||
" bar: 'b',\n" +
|
||||
" }\n" +
|
||||
" });\n" +
|
||||
" return data.text();\n" +
|
||||
" }", server.PREFIX + "/something");
|
||||
|
||||
assertEquals("done", text);
|
||||
assertNull(interceptedRequest[0].headers().get("foo"));
|
||||
assertNull(serverRequest.get().headers.get("foo"));
|
||||
assertEquals(asList("b"), serverRequest.get().headers.get("bar"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldNotThrowWhenContinuingAfterPageIsClosed() {
|
||||
boolean[] done = {false};
|
||||
|
||||
@@ -0,0 +1,276 @@
|
||||
/*
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package com.microsoft.playwright;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
import static java.util.Arrays.asList;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
public class TestPageRequestFallback extends TestBase {
|
||||
@Test
|
||||
void shouldWork() {
|
||||
page.route("**/*", route -> route.fallback());
|
||||
page.navigate(server.EMPTY_PAGE);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldFallBack() {
|
||||
List<Integer> intercepted = new ArrayList<>();
|
||||
page.route("**/empty.html", route -> {
|
||||
intercepted.add(1);
|
||||
route.fallback();
|
||||
});
|
||||
page.route("**/empty.html", route -> {
|
||||
intercepted.add(2);
|
||||
route.fallback();
|
||||
});
|
||||
page.route("**/empty.html", route -> {
|
||||
intercepted.add(3);
|
||||
route.fallback();
|
||||
});
|
||||
page.navigate(server.EMPTY_PAGE);
|
||||
assertEquals(asList(3, 2, 1), intercepted);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldFallBackAsync() {
|
||||
List<Integer> intercepted = new ArrayList<>();
|
||||
page.route("**/empty.html", route -> {
|
||||
intercepted.add(1);
|
||||
page.waitForTimeout(50);
|
||||
route.fallback();
|
||||
});
|
||||
page.route("**/empty.html", route -> {
|
||||
intercepted.add(2);
|
||||
page.waitForTimeout(100);
|
||||
route.fallback();
|
||||
});
|
||||
page.route("**/empty.html", route -> {
|
||||
intercepted.add(3);
|
||||
page.waitForTimeout(150);
|
||||
route.fallback();
|
||||
});
|
||||
page.navigate(server.EMPTY_PAGE);
|
||||
assertEquals(asList(3, 2, 1), intercepted);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldNotChainFulfill() {
|
||||
boolean[] failed = {false};
|
||||
page.route("**/empty.html", route -> {
|
||||
failed[0] = true;
|
||||
});
|
||||
page.route("**/empty.html", route -> {
|
||||
route.fulfill(new Route.FulfillOptions().setStatus(200).setBody("fulfilled"));
|
||||
});
|
||||
page.route("**/empty.html", route -> {
|
||||
route.fallback();
|
||||
});
|
||||
Response response = page.navigate(server.EMPTY_PAGE);
|
||||
assertEquals("fulfilled", response.text());
|
||||
assertFalse(failed[0]);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldNotChainAbort() {
|
||||
boolean[] failed = {false};
|
||||
page.route("**/empty.html", route -> {
|
||||
failed[0] = true;
|
||||
});
|
||||
page.route("**/empty.html", route -> {
|
||||
route.abort();
|
||||
});
|
||||
page.route("**/empty.html", route -> {
|
||||
route.fallback();
|
||||
});
|
||||
try {
|
||||
Response response = page.navigate(server.EMPTY_PAGE);
|
||||
fail("did not throw");
|
||||
assertEquals("fulfilled", response.text());
|
||||
} catch (PlaywrightException e) {
|
||||
}
|
||||
assertFalse(failed[0]);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldFallBackAfterException() {
|
||||
page.route("**/empty.html", route -> {
|
||||
route.resume();
|
||||
});
|
||||
page.route("**/empty.html", route -> {
|
||||
try {
|
||||
route.fulfill(new Route.FulfillOptions());
|
||||
} catch (PlaywrightException e) {
|
||||
route.fallback();
|
||||
}
|
||||
});
|
||||
page.navigate(server.EMPTY_PAGE);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldChainOnce() {
|
||||
page.route("**/empty.html", route -> {
|
||||
route.fulfill(new Route.FulfillOptions().setStatus(200).setBody("fulfilled one"));
|
||||
}, new Page.RouteOptions().setTimes(1));
|
||||
page.route("**/empty.html", route -> {
|
||||
route.fallback();
|
||||
}, new Page.RouteOptions().setTimes(1));
|
||||
Response response = page.navigate(server.EMPTY_PAGE);
|
||||
assertEquals("fulfilled one", response.text());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldAmendHTTPHeaders() throws ExecutionException, InterruptedException {
|
||||
List<String> values = new ArrayList<>();
|
||||
page.route("**/sleep.zzz", route -> {
|
||||
values.add(route.request().headers().get("foo"));
|
||||
values.add(route.request().headerValue("FOO"));
|
||||
route.resume();
|
||||
});
|
||||
page.route("**/*", route -> {
|
||||
Map<String, String> headers = route.request().headers();
|
||||
headers.put("FOO", "bar");
|
||||
route.fallback(new Route.FallbackOptions().setHeaders(headers));
|
||||
});
|
||||
page.navigate(server.EMPTY_PAGE);
|
||||
Future<Server.Request> request = server.futureRequest("/sleep.zzz");
|
||||
page.evaluate("() => fetch('/sleep.zzz')");
|
||||
values.addAll(request.get().headers.get("foo"));
|
||||
assertEquals(asList("bar", "bar", "bar"), values);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldDeleteHeaderWithUndefinedValue() throws ExecutionException, InterruptedException {
|
||||
// https://github.com/microsoft/playwright/issues/13106
|
||||
page.navigate(server.PREFIX + "/empty.html");
|
||||
Future<Server.Request> serverRequest = server.futureRequest("/something");
|
||||
server.setRoute("/something", exchange -> {
|
||||
exchange.getResponseHeaders().add("Access-Control-Allow-Origin", "*");
|
||||
exchange.sendResponseHeaders(200, 0);
|
||||
try (OutputStreamWriter writer = new OutputStreamWriter(exchange.getResponseBody())) {
|
||||
writer.write("done");
|
||||
}
|
||||
});
|
||||
|
||||
Request[] interceptedRequest = {null};
|
||||
page.route(server.PREFIX + "/something", route -> {
|
||||
interceptedRequest[0] = route.request();
|
||||
route.resume();
|
||||
});
|
||||
page.route(server.PREFIX + "/something", route -> {
|
||||
Map<String, String> headers = route.request().allHeaders();
|
||||
headers.remove("foo");
|
||||
route.fallback(new Route.FallbackOptions().setHeaders(headers));
|
||||
});
|
||||
|
||||
Object text = page.evaluate("async url => {\n" +
|
||||
" const data = await fetch(url, {\n" +
|
||||
" headers: {\n" +
|
||||
" foo: 'a',\n" +
|
||||
" bar: 'b',\n" +
|
||||
" }\n" +
|
||||
" });\n" +
|
||||
" return data.text();\n" +
|
||||
" }", server.PREFIX + "/something");
|
||||
|
||||
assertEquals("done", text);
|
||||
assertNull(interceptedRequest[0].headers().get("foo"));
|
||||
assertNull(serverRequest.get().headers.get("foo"));
|
||||
assertEquals(asList("b"), serverRequest.get().headers.get("bar"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldAmendMethod() throws ExecutionException, InterruptedException {
|
||||
Future<Server.Request> sRequest = server.futureRequest("/sleep.zzz");
|
||||
page.navigate(server.EMPTY_PAGE);
|
||||
String[] method = {null};
|
||||
page.route("**/*", route -> {
|
||||
method[0] = route.request().method();
|
||||
route.resume();
|
||||
});
|
||||
page.route("**/*", route -> route.fallback(new Route.FallbackOptions().setMethod("POST")));
|
||||
Request request = page.waitForRequest("**/sleep.zzz", () -> page.evaluate("() => fetch('/sleep.zzz')"));
|
||||
assertEquals("POST", method[0]);
|
||||
assertEquals("POST", request.method());
|
||||
assertEquals("POST", sRequest.get().method);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldOverrideRequestUrl() throws ExecutionException, InterruptedException {
|
||||
Future<Server.Request> request = server.futureRequest("/global-var.html");
|
||||
String[] url = {null};
|
||||
page.route("**/foo", route -> {
|
||||
url[0] = route.request().url();
|
||||
route.resume();
|
||||
});
|
||||
page.route("**/foo", route -> route.fallback(new Route.FallbackOptions().setUrl(server.PREFIX + "/global-var.html")));
|
||||
Response response = page.waitForResponse("**/*", () -> page.navigate(server.PREFIX + "/foo"));
|
||||
assertEquals(server.PREFIX + "/global-var.html", url[0]);
|
||||
assertEquals(server.PREFIX + "/foo", response.url());
|
||||
assertEquals(123, page.evaluate("() => window['globalVar']"));
|
||||
assertEquals("GET", request.get().method);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldAmendPostData() throws ExecutionException, InterruptedException {
|
||||
page.navigate(server.EMPTY_PAGE);
|
||||
String[] postData = {null};
|
||||
page.route("**/*", route -> {
|
||||
postData[0] = route.request().postData();
|
||||
route.resume();
|
||||
});
|
||||
page.route("**/*", route -> {
|
||||
route.fallback(new Route.FallbackOptions().setPostData("doggo"));
|
||||
});
|
||||
Future<Server.Request> serverRequest = server.futureRequest("/sleep.zzz");
|
||||
page.evaluate("() => fetch('/sleep.zzz', { method: 'POST', body: 'birdy' })");
|
||||
assertEquals("doggo", postData[0]);
|
||||
assertEquals("doggo", new String(serverRequest.get().postBody, StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldAmendBinaryPostData() throws ExecutionException, InterruptedException {
|
||||
page.navigate(server.EMPTY_PAGE);
|
||||
byte[] arr = new byte[256];
|
||||
for (int i = 0; i < arr.length; i++) {
|
||||
arr[i] = (byte) i;
|
||||
}
|
||||
byte[][] postDataBuffer = {null};
|
||||
page.route("**/*", route -> {
|
||||
postDataBuffer[0] = route.request().postDataBuffer();
|
||||
route.resume();
|
||||
});
|
||||
page.route("**/*", route -> {
|
||||
route.fallback(new Route.FallbackOptions().setPostData(arr));
|
||||
});
|
||||
Future<Server.Request> serverRequest = server.futureRequest("/sleep.zzz");
|
||||
page.evaluate("() => fetch('/sleep.zzz', { method: 'POST', body: 'birdy' })");
|
||||
byte[] buffer = serverRequest.get().postBody;
|
||||
assertArrayEquals(arr, buffer);
|
||||
assertArrayEquals(arr, postDataBuffer[0]);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -476,7 +476,7 @@ public class TestPageRoute extends TestBase {
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldNotThrowInvalidInterceptionIdIfTheRequestWasCancelled() {
|
||||
void shouldThrowIfResumeIsCalledAfterRouteHandlerFinished() {
|
||||
page.setContent("<iframe></iframe>");
|
||||
Route[] route = {null};
|
||||
page.route("**/*", r -> route[0] = r);
|
||||
@@ -486,8 +486,9 @@ public class TestPageRoute extends TestBase {
|
||||
page.evalOnSelector("iframe", "frame => frame.remove()");
|
||||
try {
|
||||
route[0].resume();
|
||||
fail("did not throw");
|
||||
} catch (PlaywrightException e) {
|
||||
fail("Should not throw");
|
||||
assertTrue(e.getMessage().contains("Route is already handled!"), e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -724,91 +725,4 @@ public class TestPageRoute extends TestBase {
|
||||
});
|
||||
assertEquals(server.PREFIX, response.headerValue("Access-Control-Allow-Origin"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldChainFallback() {
|
||||
List<Integer> intercepted = new ArrayList<>();
|
||||
page.route("**/empty.html", route -> {
|
||||
intercepted.add(1);
|
||||
route.fallback();
|
||||
});
|
||||
page.route("**/empty.html", route -> {
|
||||
intercepted.add(2);
|
||||
route.fallback();
|
||||
});
|
||||
page.route("**/empty.html", route -> {
|
||||
intercepted.add(3);
|
||||
route.fallback();
|
||||
});
|
||||
page.navigate(server.EMPTY_PAGE);
|
||||
assertEquals(asList(3, 2, 1), intercepted);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldNotChainFulfill() {
|
||||
boolean[] failed = {false};
|
||||
page.route("**/empty.html", route -> {
|
||||
failed[0] = true;
|
||||
});
|
||||
page.route("**/empty.html", route -> {
|
||||
route.fulfill(new Route.FulfillOptions().setStatus(200).setBody("fulfilled"));
|
||||
});
|
||||
page.route("**/empty.html", route -> {
|
||||
route.fallback();
|
||||
});
|
||||
Response response = page.navigate(server.EMPTY_PAGE);
|
||||
byte[] body = response.body();
|
||||
assertEquals("fulfilled", new String(body, StandardCharsets.UTF_8));
|
||||
assertFalse(failed[0]);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldNotChainAbort() {
|
||||
boolean[] failed = {false};
|
||||
page.route("**/empty.html", route -> {
|
||||
failed[0] = true;
|
||||
});
|
||||
page.route("**/empty.html", route -> {
|
||||
route.abort();
|
||||
});
|
||||
page.route("**/empty.html", route -> {
|
||||
route.fallback();
|
||||
});
|
||||
try {
|
||||
page.navigate(server.EMPTY_PAGE);
|
||||
fail("did not throw");
|
||||
} catch (PlaywrightException e) {
|
||||
assertNotNull(e);
|
||||
}
|
||||
assertFalse(failed[0]);
|
||||
}
|
||||
|
||||
// @Test
|
||||
// void shouldContinueAfterException() {
|
||||
// page.route("**/empty.html", route -> {
|
||||
// route.resume();
|
||||
// });
|
||||
// page.route("**/empty.html", route -> {
|
||||
// try {
|
||||
// route.fulfill(new Route.FulfillOptions().setHarPath(Paths.get("file")).setResponse(""));
|
||||
// } catch (PlaywrightException e) {
|
||||
// route.resume();
|
||||
// }
|
||||
// });
|
||||
// page.navigate(server.EMPTY_PAGE);
|
||||
// }
|
||||
|
||||
@Test
|
||||
void shouldChainOnce() {
|
||||
page.route("**/empty.html", route -> {
|
||||
route.fulfill(new Route.FulfillOptions().setStatus(200).setBody("fulfilled one"));
|
||||
}, new Page.RouteOptions().setTimes(1));
|
||||
page.route("**/empty.html", route -> {
|
||||
route.fallback();
|
||||
}, new Page.RouteOptions().setTimes(1));
|
||||
Response response = page.navigate(server.EMPTY_PAGE);
|
||||
byte[] body = response.body();
|
||||
assertEquals("fulfilled one", new String(body, StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user