Skip to content

Commit 7e285ff

Browse files
authored
feat: route.fallback with overrides (#958)
1 parent edf0e45 commit 7e285ff

File tree

12 files changed

+528
-134
lines changed

12 files changed

+528
-134
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -485,7 +485,7 @@ void handleRoute(RouteImpl route) {
485485
maybeDisableNetworkInterception();
486486
}
487487
if (handled != Router.HandleResult.Handled){
488-
route.resume(null);
488+
route.resume();
489489
}
490490
}
491491

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

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@
2323
import com.microsoft.playwright.options.*;
2424

2525
import java.io.IOException;
26-
import java.io.OutputStream;
2726
import java.nio.charset.StandardCharsets;
2827
import java.nio.file.Files;
2928
import java.nio.file.Path;
@@ -1048,6 +1047,13 @@ protected void handleEvent(String event, JsonObject params) {
10481047
if (add != null) {
10491048
WaitUntilState state = loadStateFromProtocol(add.getAsString());
10501049
loadStates.add(state);
1050+
if (parentFrame == null && page != null) {
1051+
if (state == LOAD) {
1052+
page.listeners.notify(PageImpl.EventType.LOAD, page);
1053+
} else if (state == DOMCONTENTLOADED) {
1054+
page.listeners.notify(PageImpl.EventType.DOMCONTENTLOADED, page);
1055+
}
1056+
}
10511057
internalListeners.notify(InternalEventType.LOADSTATE, state);
10521058
}
10531059
JsonElement remove = params.get("remove");

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

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -175,10 +175,6 @@ protected void handleEvent(String event, JsonObject params) {
175175
}
176176
}
177177
}
178-
} else if ("load".equals(event)) {
179-
listeners.notify(EventType.LOAD, this);
180-
} else if ("domcontentloaded".equals(event)) {
181-
listeners.notify(EventType.DOMCONTENTLOADED, this);
182178
} else if ("frameAttached".equals(event)) {
183179
String guid = params.getAsJsonObject("frame").get("guid").getAsString();
184180
FrameImpl frame = connection.getExistingObject(guid);

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

Lines changed: 52 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,14 @@ public class RequestImpl extends ChannelOwner implements Request {
4242
String failure;
4343
Timing timing;
4444
boolean didFailOrFinish;
45+
private FallbackOverrides fallbackOverrides;
46+
47+
static class FallbackOverrides {
48+
String url;
49+
String method;
50+
byte[] postData;
51+
Map<String, String> headers;
52+
}
4553

4654
RequestImpl(ChannelOwner parent, String type, String guid, JsonObject initializer) {
4755
super(parent, type, guid, initializer);
@@ -75,6 +83,9 @@ public FrameImpl frame() {
7583

7684
@Override
7785
public Map<String, String> headers() {
86+
if (fallbackOverrides != null && fallbackOverrides.headers != null) {
87+
return new RawHeaders(Utils.toHeadersList(fallbackOverrides.headers)).headers();
88+
}
7889
return headers.headers();
7990
}
8091

@@ -95,19 +106,26 @@ public boolean isNavigationRequest() {
95106

96107
@Override
97108
public String method() {
109+
if (fallbackOverrides != null && fallbackOverrides.method != null) {
110+
return fallbackOverrides.method;
111+
}
98112
return initializer.get("method").getAsString();
99113
}
100114

101115
@Override
102116
public String postData() {
103-
if (postData == null) {
117+
byte[] buffer = postDataBuffer();
118+
if (buffer == null) {
104119
return null;
105120
}
106-
return new String(postData, StandardCharsets.UTF_8);
121+
return new String(buffer, StandardCharsets.UTF_8);
107122
}
108123

109124
@Override
110125
public byte[] postDataBuffer() {
126+
if (fallbackOverrides != null && fallbackOverrides.postData != null) {
127+
return fallbackOverrides.postData;
128+
}
111129
return postData;
112130
}
113131

@@ -156,6 +174,13 @@ public Timing timing() {
156174

157175
@Override
158176
public String url() {
177+
if (fallbackOverrides != null && fallbackOverrides.url != null) {
178+
return fallbackOverrides.url;
179+
}
180+
return originalUrl();
181+
}
182+
183+
String originalUrl() {
159184
return initializer.get("url").getAsString();
160185
}
161186

@@ -164,6 +189,9 @@ Request finalRequest() {
164189
}
165190

166191
private RawHeaders getRawHeaders() {
192+
if (fallbackOverrides != null && fallbackOverrides.headers != null) {
193+
return new RawHeaders(Utils.toHeadersList(fallbackOverrides.headers));
194+
}
167195
if (rawHeaders != null) {
168196
return rawHeaders;
169197
}
@@ -176,4 +204,26 @@ private RawHeaders getRawHeaders() {
176204
rawHeaders = new RawHeaders(asList(gson().fromJson(rawHeadersJson, HttpHeader[].class)));
177205
return rawHeaders;
178206
}
207+
208+
void applyFallbackOverrides(FallbackOverrides overrides) {
209+
if (fallbackOverrides == null) {
210+
fallbackOverrides = new FallbackOverrides();
211+
}
212+
if (overrides.url != null) {
213+
fallbackOverrides.url = overrides.url;
214+
}
215+
if (overrides.method != null) {
216+
fallbackOverrides.method = overrides.method;
217+
}
218+
if (overrides.headers != null) {
219+
fallbackOverrides.headers = overrides.headers;
220+
}
221+
if (overrides.postData != null) {
222+
fallbackOverrides.postData = overrides.postData;
223+
}
224+
}
225+
226+
FallbackOverrides fallbackOverridesForResume() {
227+
return fallbackOverrides;
228+
}
179229
}

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

Lines changed: 45 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
package com.microsoft.playwright.impl;
1818

1919
import com.google.gson.JsonObject;
20+
import com.microsoft.playwright.Frame;
2021
import com.microsoft.playwright.PlaywrightException;
2122
import com.microsoft.playwright.Route;
2223

@@ -27,9 +28,10 @@
2728
import java.util.LinkedHashMap;
2829
import java.util.Map;
2930

31+
import static com.microsoft.playwright.impl.Utils.convertType;
32+
3033
public class RouteImpl extends ChannelOwner implements Route {
3134
private boolean handled;
32-
private boolean lastHandlerGaveUp;
3335

3436
public RouteImpl(ChannelOwner parent, String type, String guid, JsonObject initializer) {
3537
super(parent, type, guid, initializer);
@@ -45,55 +47,69 @@ public void abort(String errorCode) {
4547
});
4648
}
4749

48-
boolean takeLastHandlerGaveUp() {
49-
boolean result = lastHandlerGaveUp;
50-
lastHandlerGaveUp = false;
51-
return result;
50+
boolean isHandled() {
51+
return handled;
5252
}
5353

5454
@Override
5555
public void resume(ResumeOptions options) {
5656
startHandling();
57-
withLogging("Route.resume", () -> resumeImpl(options));
57+
applyOverrides(convertType(options, FallbackOptions.class));
58+
withLogging("Route.resume", () -> resumeImpl(request().fallbackOverridesForResume()));
5859
}
5960

6061
@Override
6162
public void fallback(FallbackOptions options) {
62-
if (lastHandlerGaveUp) {
63+
if (handled) {
6364
throw new PlaywrightException("Route is already handled!");
6465
}
65-
lastHandlerGaveUp = true;
66+
applyOverrides(options);
6667
}
6768

68-
private void resumeImpl(ResumeOptions options) {
69+
private void applyOverrides(FallbackOptions options) {
6970
if (options == null) {
70-
options = new ResumeOptions();
71-
}
72-
JsonObject params = new JsonObject();
73-
if (options.url != null) {
74-
params.addProperty("url", options.url);
75-
}
76-
if (options.method != null) {
77-
params.addProperty("method", options.method);
78-
}
79-
if (options.headers != null) {
80-
params.add("headers", Serialization.toProtocol(options.headers));
71+
return;
8172
}
73+
RequestImpl.FallbackOverrides overrides = new RequestImpl.FallbackOverrides();
74+
overrides.url = options.url;
75+
overrides.method = options.method;
76+
overrides.headers = options.headers;
8277
if (options.postData != null) {
83-
byte[] bytes = null;
84-
if (options.postData instanceof byte[]) {
85-
bytes = (byte[]) options.postData;
86-
} else if (options.postData instanceof String) {
87-
bytes = ((String) options.postData).getBytes(StandardCharsets.UTF_8);
88-
} else {
89-
throw new PlaywrightException("postData must be either String or byte[], found: " + options.postData.getClass().getName());
78+
overrides.postData = getPostDataBytes(options.postData);
79+
}
80+
request().applyFallbackOverrides(overrides);
81+
}
82+
83+
private void resumeImpl(RequestImpl.FallbackOverrides options) {
84+
JsonObject params = new JsonObject();
85+
if (options != null) {
86+
if (options.url != null) {
87+
params.addProperty("url", options.url);
88+
}
89+
if (options.method != null) {
90+
params.addProperty("method", options.method);
91+
}
92+
if (options.headers != null) {
93+
params.add("headers", Serialization.toProtocol(options.headers));
94+
}
95+
if (options.postData != null) {
96+
String base64 = Base64.getEncoder().encodeToString(options.postData);
97+
params.addProperty("postData", base64);
9098
}
91-
String base64 = Base64.getEncoder().encodeToString(bytes);
92-
params.addProperty("postData", base64);
9399
}
94100
sendMessageAsync("continue", params);
95101
}
96102

103+
private static byte[] getPostDataBytes(Object postData) {
104+
if (postData instanceof byte[]) {
105+
return (byte[]) postData;
106+
}
107+
if (postData instanceof String) {
108+
return ((String) postData).getBytes(StandardCharsets.UTF_8);
109+
}
110+
throw new PlaywrightException("postData must be either String or byte[], found: " + postData.getClass().getName());
111+
}
112+
97113
@Override
98114
public void fulfill(FulfillOptions options) {
99115
startHandling();

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

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,11 @@ private static class RouteInfo {
3838
this.times = times;
3939
}
4040

41-
boolean handle(Route route) {
41+
boolean handle(RouteImpl route) {
4242
if (times != null && times <= 0) {
4343
return false;
4444
}
45-
if (!matcher.test(route.request().url())) {
45+
if (!matcher.test(route.request().originalUrl())) {
4646
return false;
4747
}
4848
if (times != null) {
@@ -80,11 +80,10 @@ HandleResult handle(RouteImpl route) {
8080
if (info.isDone()) {
8181
it.remove();
8282
}
83-
if (route.takeLastHandlerGaveUp()) {
84-
result = HandleResult.MatchedHandlerButNotHandled;
85-
continue;
83+
if (route.isHandled()) {
84+
return HandleResult.Handled;
8685
}
87-
return HandleResult.Handled;
86+
result = HandleResult.MatchedHandlerButNotHandled;
8887
}
8988
}
9089
return result;

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

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,17 @@ static Map<String, String> toHeadersMap(List<HttpHeader> headers) {
277277
return map;
278278
}
279279

280+
static List<HttpHeader> toHeadersList(Map<String, String> headers) {
281+
List<HttpHeader> list = new ArrayList<>();
282+
for (Map.Entry<String, String> entry: headers.entrySet()) {
283+
HttpHeader header = new HttpHeader();
284+
header.name = entry.getKey();
285+
header.value = entry.getValue();
286+
list.add(header);
287+
}
288+
return list;
289+
}
290+
280291
static String toJsRegexFlags(Pattern pattern) {
281292
String regexFlags = "";
282293
if ((pattern.flags() & Pattern.CASE_INSENSITIVE) != 0) {
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
/*
2+
* Copyright (c) Microsoft Corporation.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.microsoft.playwright;
18+
19+
import com.microsoft.playwright.options.LoadState;
20+
import org.junit.jupiter.api.Test;
21+
22+
import java.io.OutputStreamWriter;
23+
import java.util.ArrayList;
24+
import java.util.List;
25+
26+
import static java.util.Arrays.asList;
27+
import static org.junit.jupiter.api.Assertions.assertEquals;
28+
29+
public class TestPageAutowaitingBasic extends TestBase {
30+
@Test
31+
void shouldWorkWithNoWaitAfterTrue() {
32+
server.setRoute("/empty.html", exchange -> {});
33+
page.setContent("<a id='anchor' href='${server.EMPTY_PAGE}'>empty.html</a>");
34+
page.click("a", new Page.ClickOptions().setNoWaitAfter(true));
35+
}
36+
37+
@Test
38+
void shouldWorkWithDblclickNoWaitAfterTrue() {
39+
server.setRoute("/empty.html", exchange -> {});
40+
page.setContent("<a id='anchor' href='${server.EMPTY_PAGE}'>empty.html</a>");
41+
page.dblclick("a", new Page.DblclickOptions().setNoWaitAfter(true));
42+
}
43+
44+
@Test
45+
void shouldWorkWithWaitForLoadStateLoad() {
46+
List<String> messages = new ArrayList<>();
47+
server.setRoute("/empty.html", exchange -> {
48+
messages.add("route");
49+
exchange.getResponseHeaders().add("content-type", "text/html");
50+
exchange.sendResponseHeaders(200, 0);
51+
try (OutputStreamWriter writer = new OutputStreamWriter(exchange.getResponseBody())) {
52+
writer.write("<link rel='stylesheet' href='./one-style.css'>");
53+
} catch (RuntimeException e) {
54+
e.printStackTrace();
55+
throw e;
56+
}
57+
});
58+
page.setContent("<a id='anchor' href='" + server.EMPTY_PAGE + "'>empty.html</a>");
59+
60+
page.onLoad(p -> messages.add("clickload"));
61+
page.click("a");
62+
page.waitForLoadState(LoadState.LOAD);
63+
messages.add("load");
64+
assertEquals(asList("route", "clickload", "load"), messages);
65+
}
66+
}

0 commit comments

Comments
 (0)