1
0
mirror of synced 2026-08-06 07:26:55 +00:00

feat: route.fallback with overrides (#958)

This commit is contained in:
Yury Semikhatsky
2022-06-23 16:48:32 -07:00
committed by GitHub
parent edf0e45fb4
commit 7e285ffe44
12 changed files with 528 additions and 134 deletions
@@ -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));
}
}