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

feat: route chaining (#950)

This commit is contained in:
Yury Semikhatsky
2022-06-11 11:24:30 -07:00
committed by GitHub
parent e4ec9b8dbe
commit c8eb4f9eeb
6 changed files with 227 additions and 21 deletions
@@ -474,12 +474,13 @@ class BrowserContextImpl extends ChannelOwner implements BrowserContext {
}
}
void handleRoute(Route route) {
boolean handled = routes.handle(route);
if (handled) {
void handleRoute(RouteImpl route) {
Router.HandleResult handled = routes.handle(route);
if (handled != Router.HandleResult.NoMatchingHandler) {
maybeDisableNetworkInterception();
} else {
route.resume();
}
if (handled != Router.HandleResult.Handled){
route.resume(null);
}
}
@@ -490,7 +491,7 @@ class BrowserContextImpl extends ChannelOwner implements BrowserContext {
@Override
protected void handleEvent(String event, JsonObject params) {
if ("route".equals(event)) {
Route route = connection.getExistingObject(params.getAsJsonObject("route").get("guid").getAsString());
RouteImpl route = connection.getExistingObject(params.getAsJsonObject("route").get("guid").getAsString());
handleRoute(route);
} else if ("page".equals(event)) {
PageImpl page = connection.getExistingObject(params.getAsJsonObject("page").get("guid").getAsString());
@@ -198,11 +198,12 @@ public class PageImpl extends ChannelOwner implements Page {
}
listeners.notify(EventType.FRAMEDETACHED, frame);
} else if ("route".equals(event)) {
Route route = connection.getExistingObject(params.getAsJsonObject("route").get("guid").getAsString());
boolean handled = routes.handle(route);
if (handled) {
RouteImpl route = connection.getExistingObject(params.getAsJsonObject("route").get("guid").getAsString());
Router.HandleResult handled = routes.handle(route);
if (handled != Router.HandleResult.NoMatchingHandler) {
maybeDisableNetworkInterception();
} else {
}
if (handled != Router.HandleResult.Handled) {
browserContext.handleRoute(route);
}
} else if ("video".equals(event)) {
@@ -29,6 +29,7 @@ import java.util.Map;
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);
@@ -44,6 +45,20 @@ public class RouteImpl extends ChannelOwner implements Route {
});
}
boolean takeLastHandlerGaveUp() {
boolean result = lastHandlerGaveUp;
lastHandlerGaveUp = false;
return result;
}
@Override
public void resume() {
if (lastHandlerGaveUp) {
throw new PlaywrightException("Route is already handled!");
}
lastHandlerGaveUp = true;
}
@Override
public void resume(ResumeOptions options) {
startHandling();
@@ -19,6 +19,7 @@ package com.microsoft.playwright.impl;
import com.microsoft.playwright.Route;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.function.Consumer;
import java.util.stream.Collectors;
@@ -70,15 +71,22 @@ class Router {
return routes.size();
}
boolean handle(Route route) {
for (RouteInfo info : routes) {
enum HandleResult { NoMatchingHandler, MatchedHandlerButNotHandled, Handled }
HandleResult handle(RouteImpl route) {
HandleResult result = HandleResult.NoMatchingHandler;
for (Iterator<RouteInfo> it = routes.iterator(); it.hasNext();) {
RouteInfo info = it.next();
if (info.handle(route)) {
if (info.isDone()) {
routes.remove(info);
it.remove();
}
return true;
if (route.takeLastHandlerGaveUp()) {
result = HandleResult.MatchedHandlerButNotHandled;
continue;
}
return HandleResult.Handled;
}
}
return false;
return result;
}
}
@@ -19,6 +19,7 @@ package com.microsoft.playwright;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutionException;
@@ -78,12 +79,12 @@ public class TestBrowserContextRoute extends TestBase {
};
context.route("**/empty.html", handler4);
page.navigate(server.EMPTY_PAGE);
assertEquals(asList(4), intercepted);
assertEquals(asList(4, 3, 2, 1), intercepted);
intercepted.clear();
context.unroute("**/empty.html", handler4);
page.navigate(server.EMPTY_PAGE);
assertEquals(asList(3), intercepted);
assertEquals(asList(3, 2, 1), intercepted);
intercepted.clear();
context.unroute("**/empty.html");
@@ -207,4 +208,96 @@ public class TestBrowserContextRoute extends TestBase {
assertTrue(e.getMessage().contains("New URL must have same protocol as overridden URL"), e.getMessage());
}
}
@Test
void shouldChainContinue() {
List<Integer> intercepted = new ArrayList<>();
context.route("**/empty.html", route -> {
intercepted.add(1);
route.resume();
});
context.route("**/empty.html", route -> {
intercepted.add(2);
route.resume();
});
context.route("**/empty.html", route -> {
intercepted.add(3);
route.resume();
});
page.navigate(server.EMPTY_PAGE);
assertEquals(asList(3, 2, 1), intercepted);
}
@Test
void shouldNotChainFulfill() {
boolean[] failed = {false};
context.route("**/empty.html", route -> {
failed[0] = true;
});
context.route("**/empty.html", route -> {
route.fulfill(new Route.FulfillOptions().setStatus(200).setBody("fulfilled"));
});
context.route("**/empty.html", route -> {
route.resume();
});
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};
context.route("**/empty.html", route -> {
failed[0] = true;
});
context.route("**/empty.html", route -> {
route.abort();
});
context.route("**/empty.html", route -> {
route.resume();
});
try {
page.navigate(server.EMPTY_PAGE);
fail("did not throw");
} catch (PlaywrightException e) {
assertNotNull(e);
}
assertFalse(failed[0]);
}
@Test
void shouldChainContinueIntoPage() {
List<Integer> intercepted = new ArrayList<>();
context.route("**/empty.html", route -> {
intercepted.add(1);
route.resume();
});
context.route("**/empty.html", route -> {
intercepted.add(2);
route.resume();
});
context.route("**/empty.html", route -> {
intercepted.add(3);
route.resume();
});
page.route("**/empty.html", route -> {
intercepted.add(4);
route.resume();
});
page.route("**/empty.html", route -> {
intercepted.add(5);
route.resume();
});
page.route("**/empty.html", route -> {
intercepted.add(6);
route.resume();
});
page.navigate(server.EMPTY_PAGE);
assertEquals(asList(6, 5, 4, 3, 2, 1), intercepted);
}
}
@@ -21,6 +21,7 @@ import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.DisabledIf;
import java.io.OutputStreamWriter;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@@ -83,12 +84,12 @@ public class TestPageRoute extends TestBase {
};
page.route("**/empty.html", handler4);
page.navigate(server.EMPTY_PAGE);
assertEquals(asList(4), intercepted);
assertEquals(asList(4, 3, 2, 1), intercepted);
intercepted.clear();
page.unroute("**/empty.html", handler4);
page.navigate(server.EMPTY_PAGE);
assertEquals(asList(3), intercepted);
assertEquals(asList(3, 2, 1), intercepted);
intercepted.clear();
page.unroute("**/empty.html");
@@ -115,12 +116,12 @@ public class TestPageRoute extends TestBase {
page.route(predicate, handler3);
page.navigate(server.EMPTY_PAGE);
assertEquals(asList(3), intercepted);
assertEquals(asList(3, 2, 1), intercepted);
intercepted.clear();
page.unroute(predicate, handler3);
page.navigate(server.EMPTY_PAGE);
assertEquals(asList(2), intercepted);
assertEquals(asList(2, 1), intercepted);
intercepted.clear();
page.unroute(predicate);
@@ -723,4 +724,91 @@ public class TestPageRoute extends TestBase {
});
assertEquals(server.PREFIX, response.headerValue("Access-Control-Allow-Origin"));
}
@Test
void shouldChainContinue() {
List<Integer> intercepted = new ArrayList<>();
page.route("**/empty.html", route -> {
intercepted.add(1);
route.resume();
});
page.route("**/empty.html", route -> {
intercepted.add(2);
route.resume();
});
page.route("**/empty.html", route -> {
intercepted.add(3);
route.resume();
});
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.resume();
});
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.resume();
});
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.resume();
}, 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));
}
}