feat: route chaining (#950)
This commit is contained in:
@@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user