Skip to content

Commit c8eb4f9

Browse files
authored
feat: route chaining (#950)
1 parent e4ec9b8 commit c8eb4f9

File tree

6 files changed

+227
-21
lines changed

6 files changed

+227
-21
lines changed

playwright/src/main/java/com/microsoft/playwright/impl/BrowserContextImpl.java

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -474,12 +474,13 @@ private void maybeDisableNetworkInterception() {
474474
}
475475
}
476476

477-
void handleRoute(Route route) {
478-
boolean handled = routes.handle(route);
479-
if (handled) {
477+
void handleRoute(RouteImpl route) {
478+
Router.HandleResult handled = routes.handle(route);
479+
if (handled != Router.HandleResult.NoMatchingHandler) {
480480
maybeDisableNetworkInterception();
481-
} else {
482-
route.resume();
481+
}
482+
if (handled != Router.HandleResult.Handled){
483+
route.resume(null);
483484
}
484485
}
485486

@@ -490,7 +491,7 @@ void pause() {
490491
@Override
491492
protected void handleEvent(String event, JsonObject params) {
492493
if ("route".equals(event)) {
493-
Route route = connection.getExistingObject(params.getAsJsonObject("route").get("guid").getAsString());
494+
RouteImpl route = connection.getExistingObject(params.getAsJsonObject("route").get("guid").getAsString());
494495
handleRoute(route);
495496
} else if ("page".equals(event)) {
496497
PageImpl page = connection.getExistingObject(params.getAsJsonObject("page").get("guid").getAsString());

playwright/src/main/java/com/microsoft/playwright/impl/PageImpl.java

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -198,11 +198,12 @@ protected void handleEvent(String event, JsonObject params) {
198198
}
199199
listeners.notify(EventType.FRAMEDETACHED, frame);
200200
} else if ("route".equals(event)) {
201-
Route route = connection.getExistingObject(params.getAsJsonObject("route").get("guid").getAsString());
202-
boolean handled = routes.handle(route);
203-
if (handled) {
201+
RouteImpl route = connection.getExistingObject(params.getAsJsonObject("route").get("guid").getAsString());
202+
Router.HandleResult handled = routes.handle(route);
203+
if (handled != Router.HandleResult.NoMatchingHandler) {
204204
maybeDisableNetworkInterception();
205-
} else {
205+
}
206+
if (handled != Router.HandleResult.Handled) {
206207
browserContext.handleRoute(route);
207208
}
208209
} else if ("video".equals(event)) {

playwright/src/main/java/com/microsoft/playwright/impl/RouteImpl.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929

3030
public class RouteImpl extends ChannelOwner implements Route {
3131
private boolean handled;
32+
private boolean lastHandlerGaveUp;
3233

3334
public RouteImpl(ChannelOwner parent, String type, String guid, JsonObject initializer) {
3435
super(parent, type, guid, initializer);
@@ -44,6 +45,20 @@ public void abort(String errorCode) {
4445
});
4546
}
4647

48+
boolean takeLastHandlerGaveUp() {
49+
boolean result = lastHandlerGaveUp;
50+
lastHandlerGaveUp = false;
51+
return result;
52+
}
53+
54+
@Override
55+
public void resume() {
56+
if (lastHandlerGaveUp) {
57+
throw new PlaywrightException("Route is already handled!");
58+
}
59+
lastHandlerGaveUp = true;
60+
}
61+
4762
@Override
4863
public void resume(ResumeOptions options) {
4964
startHandling();

playwright/src/main/java/com/microsoft/playwright/impl/Router.java

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import com.microsoft.playwright.Route;
2020

2121
import java.util.ArrayList;
22+
import java.util.Iterator;
2223
import java.util.List;
2324
import java.util.function.Consumer;
2425
import java.util.stream.Collectors;
@@ -70,15 +71,22 @@ int size() {
7071
return routes.size();
7172
}
7273

73-
boolean handle(Route route) {
74-
for (RouteInfo info : routes) {
74+
enum HandleResult { NoMatchingHandler, MatchedHandlerButNotHandled, Handled }
75+
HandleResult handle(RouteImpl route) {
76+
HandleResult result = HandleResult.NoMatchingHandler;
77+
for (Iterator<RouteInfo> it = routes.iterator(); it.hasNext();) {
78+
RouteInfo info = it.next();
7579
if (info.handle(route)) {
7680
if (info.isDone()) {
77-
routes.remove(info);
81+
it.remove();
7882
}
79-
return true;
83+
if (route.takeLastHandlerGaveUp()) {
84+
result = HandleResult.MatchedHandlerButNotHandled;
85+
continue;
86+
}
87+
return HandleResult.Handled;
8088
}
8189
}
82-
return false;
90+
return result;
8391
}
8492
}

playwright/src/test/java/com/microsoft/playwright/TestBrowserContextRoute.java

Lines changed: 95 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import org.junit.jupiter.api.Disabled;
2020
import org.junit.jupiter.api.Test;
2121

22+
import java.nio.charset.StandardCharsets;
2223
import java.util.ArrayList;
2324
import java.util.List;
2425
import java.util.concurrent.ExecutionException;
@@ -78,12 +79,12 @@ void shouldUnroute() {
7879
};
7980
context.route("**/empty.html", handler4);
8081
page.navigate(server.EMPTY_PAGE);
81-
assertEquals(asList(4), intercepted);
82+
assertEquals(asList(4, 3, 2, 1), intercepted);
8283

8384
intercepted.clear();
8485
context.unroute("**/empty.html", handler4);
8586
page.navigate(server.EMPTY_PAGE);
86-
assertEquals(asList(3), intercepted);
87+
assertEquals(asList(3, 2, 1), intercepted);
8788

8889
intercepted.clear();
8990
context.unroute("**/empty.html");
@@ -207,4 +208,96 @@ void shouldNotSwallowExceptionsInResume() throws ExecutionException, Interrupted
207208
assertTrue(e.getMessage().contains("New URL must have same protocol as overridden URL"), e.getMessage());
208209
}
209210
}
211+
212+
213+
@Test
214+
void shouldChainContinue() {
215+
List<Integer> intercepted = new ArrayList<>();
216+
context.route("**/empty.html", route -> {
217+
intercepted.add(1);
218+
route.resume();
219+
});
220+
context.route("**/empty.html", route -> {
221+
intercepted.add(2);
222+
route.resume();
223+
});
224+
context.route("**/empty.html", route -> {
225+
intercepted.add(3);
226+
route.resume();
227+
});
228+
page.navigate(server.EMPTY_PAGE);
229+
assertEquals(asList(3, 2, 1), intercepted);
230+
}
231+
232+
@Test
233+
void shouldNotChainFulfill() {
234+
boolean[] failed = {false};
235+
context.route("**/empty.html", route -> {
236+
failed[0] = true;
237+
});
238+
context.route("**/empty.html", route -> {
239+
route.fulfill(new Route.FulfillOptions().setStatus(200).setBody("fulfilled"));
240+
});
241+
context.route("**/empty.html", route -> {
242+
route.resume();
243+
});
244+
Response response = page.navigate(server.EMPTY_PAGE);
245+
byte[] body = response.body();
246+
assertEquals("fulfilled", new String(body, StandardCharsets.UTF_8));
247+
assertFalse(failed[0]);
248+
}
249+
250+
@Test
251+
void shouldNotChainAbort() {
252+
boolean[] failed = {false};
253+
context.route("**/empty.html", route -> {
254+
failed[0] = true;
255+
});
256+
context.route("**/empty.html", route -> {
257+
route.abort();
258+
});
259+
context.route("**/empty.html", route -> {
260+
route.resume();
261+
});
262+
263+
try {
264+
page.navigate(server.EMPTY_PAGE);
265+
fail("did not throw");
266+
} catch (PlaywrightException e) {
267+
assertNotNull(e);
268+
}
269+
assertFalse(failed[0]);
270+
}
271+
272+
@Test
273+
void shouldChainContinueIntoPage() {
274+
List<Integer> intercepted = new ArrayList<>();
275+
context.route("**/empty.html", route -> {
276+
intercepted.add(1);
277+
route.resume();
278+
});
279+
context.route("**/empty.html", route -> {
280+
intercepted.add(2);
281+
route.resume();
282+
});
283+
context.route("**/empty.html", route -> {
284+
intercepted.add(3);
285+
route.resume();
286+
});
287+
page.route("**/empty.html", route -> {
288+
intercepted.add(4);
289+
route.resume();
290+
});
291+
page.route("**/empty.html", route -> {
292+
intercepted.add(5);
293+
route.resume();
294+
});
295+
page.route("**/empty.html", route -> {
296+
intercepted.add(6);
297+
route.resume();
298+
});
299+
page.navigate(server.EMPTY_PAGE);
300+
assertEquals(asList(6, 5, 4, 3, 2, 1), intercepted);
301+
}
302+
210303
}

playwright/src/test/java/com/microsoft/playwright/TestPageRoute.java

Lines changed: 92 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import org.junit.jupiter.api.condition.DisabledIf;
2222

2323
import java.io.OutputStreamWriter;
24+
import java.nio.charset.StandardCharsets;
2425
import java.util.ArrayList;
2526
import java.util.HashMap;
2627
import java.util.List;
@@ -83,12 +84,12 @@ void shouldUnroute() {
8384
};
8485
page.route("**/empty.html", handler4);
8586
page.navigate(server.EMPTY_PAGE);
86-
assertEquals(asList(4), intercepted);
87+
assertEquals(asList(4, 3, 2, 1), intercepted);
8788

8889
intercepted.clear();
8990
page.unroute("**/empty.html", handler4);
9091
page.navigate(server.EMPTY_PAGE);
91-
assertEquals(asList(3), intercepted);
92+
assertEquals(asList(3, 2, 1), intercepted);
9293

9394
intercepted.clear();
9495
page.unroute("**/empty.html");
@@ -115,12 +116,12 @@ void shouldUnroutePredicate() {
115116
page.route(predicate, handler3);
116117

117118
page.navigate(server.EMPTY_PAGE);
118-
assertEquals(asList(3), intercepted);
119+
assertEquals(asList(3, 2, 1), intercepted);
119120

120121
intercepted.clear();
121122
page.unroute(predicate, handler3);
122123
page.navigate(server.EMPTY_PAGE);
123-
assertEquals(asList(2), intercepted);
124+
assertEquals(asList(2, 1), intercepted);
124125

125126
intercepted.clear();
126127
page.unroute(predicate);
@@ -723,4 +724,91 @@ void shouldAddAccessControlAllowOriginByDefaultWhenFulfill() {
723724
});
724725
assertEquals(server.PREFIX, response.headerValue("Access-Control-Allow-Origin"));
725726
}
727+
728+
@Test
729+
void shouldChainContinue() {
730+
List<Integer> intercepted = new ArrayList<>();
731+
page.route("**/empty.html", route -> {
732+
intercepted.add(1);
733+
route.resume();
734+
});
735+
page.route("**/empty.html", route -> {
736+
intercepted.add(2);
737+
route.resume();
738+
});
739+
page.route("**/empty.html", route -> {
740+
intercepted.add(3);
741+
route.resume();
742+
});
743+
page.navigate(server.EMPTY_PAGE);
744+
assertEquals(asList(3, 2, 1), intercepted);
745+
}
746+
747+
@Test
748+
void shouldNotChainFulfill() {
749+
boolean[] failed = {false};
750+
page.route("**/empty.html", route -> {
751+
failed[0] = true;
752+
});
753+
page.route("**/empty.html", route -> {
754+
route.fulfill(new Route.FulfillOptions().setStatus(200).setBody("fulfilled"));
755+
});
756+
page.route("**/empty.html", route -> {
757+
route.resume();
758+
});
759+
Response response = page.navigate(server.EMPTY_PAGE);
760+
byte[] body = response.body();
761+
assertEquals("fulfilled", new String(body, StandardCharsets.UTF_8));
762+
assertFalse(failed[0]);
763+
}
764+
765+
@Test
766+
void shouldNotChainAbort() {
767+
boolean[] failed = {false};
768+
page.route("**/empty.html", route -> {
769+
failed[0] = true;
770+
});
771+
page.route("**/empty.html", route -> {
772+
route.abort();
773+
});
774+
page.route("**/empty.html", route -> {
775+
route.resume();
776+
});
777+
try {
778+
page.navigate(server.EMPTY_PAGE);
779+
fail("did not throw");
780+
} catch (PlaywrightException e) {
781+
assertNotNull(e);
782+
}
783+
assertFalse(failed[0]);
784+
}
785+
786+
// @Test
787+
// void shouldContinueAfterException() {
788+
// page.route("**/empty.html", route -> {
789+
// route.resume();
790+
// });
791+
// page.route("**/empty.html", route -> {
792+
// try {
793+
// route.fulfill(new Route.FulfillOptions().setHarPath(Paths.get("file")).setResponse(""));
794+
// } catch (PlaywrightException e) {
795+
// route.resume();
796+
// }
797+
// });
798+
// page.navigate(server.EMPTY_PAGE);
799+
// }
800+
801+
@Test
802+
void shouldChainOnce() {
803+
page.route("**/empty.html", route -> {
804+
route.fulfill(new Route.FulfillOptions().setStatus(200).setBody("fulfilled one"));
805+
}, new Page.RouteOptions().setTimes(1));
806+
page.route("**/empty.html", route -> {
807+
route.resume();
808+
}, new Page.RouteOptions().setTimes(1));
809+
Response response = page.navigate(server.EMPTY_PAGE);
810+
byte[] body = response.body();
811+
assertEquals("fulfilled one", new String(body, StandardCharsets.UTF_8));
812+
}
813+
726814
}

0 commit comments

Comments
 (0)