mirror of
https://github.com/microsoft/playwright-java.git
synced 2025-09-08 21:01:00 +00:00
feat: Request.postData (#48)
This commit is contained in:
parent
7d45a26344
commit
efa1a11c0f
@ -245,6 +245,7 @@ class Method extends Element {
|
||||
});
|
||||
// There is no standard JSON type in Java.
|
||||
customSignature.put("Response.json", new String[0]);
|
||||
customSignature.put("Request.postDataJSON", new String[0]);
|
||||
customSignature.put("Page.frame", new String[]{
|
||||
"Frame frameByName(String name);",
|
||||
"Frame frameByUrl(String glob);",
|
||||
|
@ -39,7 +39,6 @@ public interface Request {
|
||||
String method();
|
||||
String postData();
|
||||
byte[] postDataBuffer();
|
||||
RequestPostDataJSON postDataJSON();
|
||||
Request redirectedFrom();
|
||||
Request redirectedTo();
|
||||
String resourceType();
|
||||
|
@ -22,13 +22,16 @@ import com.microsoft.playwright.Frame;
|
||||
import com.microsoft.playwright.Request;
|
||||
import com.microsoft.playwright.Response;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Base64;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class RequestImpl extends ChannelOwner implements Request {
|
||||
private final byte[] postData;
|
||||
private RequestImpl redirectedFrom;
|
||||
private RequestImpl redirectedTo;
|
||||
private final Map<String, String> headers = new HashMap<>();
|
||||
final Map<String, String> headers = new HashMap<>();
|
||||
RequestFailure failure;
|
||||
|
||||
RequestImpl(ChannelOwner parent, String type, String guid, JsonObject initializer) {
|
||||
@ -42,6 +45,11 @@ public class RequestImpl extends ChannelOwner implements Request {
|
||||
JsonObject item = e.getAsJsonObject();
|
||||
headers.put(item.get("name").getAsString().toLowerCase(), item.get("value").getAsString());
|
||||
}
|
||||
if (initializer.has("postData")) {
|
||||
postData = Base64.getDecoder().decode(initializer.get("postData").getAsString());
|
||||
} else {
|
||||
postData = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -71,17 +79,15 @@ public class RequestImpl extends ChannelOwner implements Request {
|
||||
|
||||
@Override
|
||||
public String postData() {
|
||||
if (postData == null) {
|
||||
return null;
|
||||
}
|
||||
return new String(postData, StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] postDataBuffer() {
|
||||
return new byte[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
public RequestPostDataJSON postDataJSON() {
|
||||
return null;
|
||||
return postData;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -29,6 +29,7 @@ import java.util.Map;
|
||||
|
||||
public class ResponseImpl extends ChannelOwner implements Response {
|
||||
private final Map<String, String> headers = new HashMap<>();
|
||||
private final RequestImpl request;
|
||||
|
||||
ResponseImpl(ChannelOwner parent, String type, String guid, JsonObject initializer) {
|
||||
super(parent, type, guid, initializer);
|
||||
@ -37,6 +38,14 @@ public class ResponseImpl extends ChannelOwner implements Response {
|
||||
JsonObject item = e.getAsJsonObject();
|
||||
headers.put(item.get("name").getAsString().toLowerCase(), item.get("value").getAsString());
|
||||
}
|
||||
|
||||
request = connection.getExistingObject(initializer.getAsJsonObject("request").get("guid").getAsString());
|
||||
// TODO: uncomment when server changes are published.
|
||||
// request.headers.clear();
|
||||
// for (JsonElement e : initializer.getAsJsonArray("requestHeaders")) {
|
||||
// JsonObject item = e.getAsJsonObject();
|
||||
// request.headers.put(item.get("name").getAsString().toLowerCase(), item.get("value").getAsString());
|
||||
// }
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -70,8 +79,8 @@ public class ResponseImpl extends ChannelOwner implements Response {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Request request() {
|
||||
return connection.getExistingObject(initializer.getAsJsonObject("request").get("guid").getAsString());
|
||||
public RequestImpl request() {
|
||||
return request;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -0,0 +1,273 @@
|
||||
/*
|
||||
* 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.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static com.microsoft.playwright.Page.EventType.REQUEST;
|
||||
import static com.microsoft.playwright.Page.EventType.RESPONSE;
|
||||
import static com.microsoft.playwright.Utils.attachFrame;
|
||||
import static com.microsoft.playwright.Utils.mapOf;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
public class TestNetworkRequest extends TestBase {
|
||||
@Test
|
||||
void shouldWorkForMainFrameNavigationRequest() {
|
||||
List<Request> requests = new ArrayList<>();
|
||||
page.addListener(REQUEST, event -> requests.add((Request) event.data()));
|
||||
page.navigate(server.EMPTY_PAGE);
|
||||
assertEquals(1, requests.size());
|
||||
assertEquals(page.mainFrame(), requests.get(0).frame());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldWorkForSubframeNavigationRequest() {
|
||||
page.navigate(server.EMPTY_PAGE);
|
||||
List<Request> requests = new ArrayList<>();
|
||||
page.addListener(REQUEST, event -> requests.add((Request) event.data()));
|
||||
attachFrame(page, "frame1", server.EMPTY_PAGE);
|
||||
assertEquals(1, requests.size());
|
||||
assertEquals(page.frames().get(1), requests.get(0).frame());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldWorkForFetchRequests() {
|
||||
page.navigate(server.EMPTY_PAGE);
|
||||
List<Request> requests = new ArrayList<>();
|
||||
page.addListener(REQUEST, event -> requests.add((Request) event.data()));
|
||||
page.evaluate("() => fetch('/digits/1.png')");
|
||||
assertEquals(1, requests.size());
|
||||
assertEquals(page.mainFrame(), requests.get(0).frame());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldWorkForARedirect() {
|
||||
server.setRedirect("/foo.html", "/empty.html");
|
||||
List<Request> requests = new ArrayList<>();
|
||||
page.addListener(REQUEST, event -> requests.add((Request) event.data()));
|
||||
page.navigate(server.PREFIX + "/foo.html");
|
||||
|
||||
assertEquals(2, requests.size());
|
||||
assertEquals(server.PREFIX + "/foo.html", requests.get(0).url());
|
||||
assertEquals(server.PREFIX + "/empty.html", requests.get(1).url());
|
||||
}
|
||||
|
||||
// https://github.com/microsoft/playwright/issues/3993
|
||||
@Test
|
||||
void shouldNotWorkForARedirectAndInterception() {
|
||||
server.setRedirect("/foo.html", "/empty.html");
|
||||
List<Request> requests = new ArrayList<>();
|
||||
page.route("**", (route, request) -> {
|
||||
requests.add(route.request());
|
||||
route.continue_();
|
||||
});
|
||||
page.navigate(server.PREFIX + "/foo.html");
|
||||
|
||||
assertEquals(server.PREFIX + "/empty.html", page.url());
|
||||
|
||||
assertEquals(1, requests.size());
|
||||
assertEquals(server.PREFIX + "/foo.html", requests.get(0).url());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReturnHeaders() {
|
||||
Response response = page.navigate(server.EMPTY_PAGE);
|
||||
if (isChromium)
|
||||
assertTrue(response.request().headers().get("user-agent").contains("Chrome"));
|
||||
else if (isFirefox)
|
||||
assertTrue(response.request().headers().get("user-agent").contains("Firefox"));
|
||||
else if (isWebKit)
|
||||
assertTrue(response.request().headers().get("user-agent").contains("WebKit"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldGetTheSameHeadersAsTheServer() throws ExecutionException, InterruptedException {
|
||||
// TODO: test.fail(browserName === "webkit", "Provisional headers differ from those in network stack");
|
||||
Future<Server.Request> serverRequest = server.waitForRequest("/empty.html");
|
||||
server.setRoute("/empty.html", exchange -> {
|
||||
exchange.sendResponseHeaders(200, 0);
|
||||
try (OutputStreamWriter writer = new OutputStreamWriter(exchange.getResponseBody())) {
|
||||
writer.write("done");
|
||||
}
|
||||
});
|
||||
// TODO: uncomment when server changes are published
|
||||
// Response response = page.navigate(server.PREFIX + "/empty.html");
|
||||
// Map<String, String> expectedHeaders = serverRequest.get().headers.entrySet().stream().collect(
|
||||
// Collectors.toMap(Map.Entry::getKey, e -> e.getValue().get(0)));
|
||||
// assertEquals(expectedHeaders, response.request().headers());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldGetTheSameHeadersAsTheServerCORP() throws ExecutionException, InterruptedException {
|
||||
// TODO: test.fail(browserName === "webkit", "Provisional headers differ from those in network stack");
|
||||
page.navigate(server.PREFIX + "/empty.html");
|
||||
Future<Server.Request> serverRequest = server.waitForRequest("/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");
|
||||
}
|
||||
});
|
||||
Deferred<Event<Page.EventType>> responsePromise = page.waitForEvent(RESPONSE);
|
||||
Object text = page.evaluate("async url => {\n" +
|
||||
" const data = await fetch(url);\n" +
|
||||
" return data.text();\n" +
|
||||
"}", server.CROSS_PROCESS_PREFIX + "/something");
|
||||
assertEquals("done", text);
|
||||
// TODO: uncomment when server changes are published
|
||||
// Response response = (Response) responsePromise.get().data();
|
||||
// Map<String, String> expectedHeaders = serverRequest.get().headers.entrySet().stream().collect(
|
||||
// Collectors.toMap(Map.Entry::getKey, e -> e.getValue().get(0)));
|
||||
// assertEquals(expectedHeaders, response.request().headers());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReturnPostData() {
|
||||
page.navigate(server.EMPTY_PAGE);
|
||||
server.setRoute("/post", exchange -> {
|
||||
exchange.sendResponseHeaders(200, 0);
|
||||
exchange.getResponseBody().close();
|
||||
});
|
||||
Request[] request = {null};
|
||||
page.addListener(REQUEST, event -> request[0] = (Request) event.data());
|
||||
page.evaluate("() => fetch('./post', { method: 'POST', body: JSON.stringify({foo: 'bar'})})");
|
||||
assertNotNull(request[0]);
|
||||
assertEquals("{\"foo\":\"bar\"}", request[0].postData());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldWorkWithBinaryPostData() {
|
||||
page.navigate(server.EMPTY_PAGE);
|
||||
server.setRoute("/post", exchange -> {
|
||||
exchange.sendResponseHeaders(200, 0);
|
||||
exchange.getResponseBody().close();
|
||||
});
|
||||
Request[] request = {null};
|
||||
page.addListener(REQUEST, event -> request[0] = (Request) event.data());
|
||||
page.evaluate("async () => {\n" +
|
||||
" await fetch('./post', { method: 'POST', body: new Uint8Array(Array.from(Array(256).keys())) });\n" +
|
||||
"}");
|
||||
assertNotNull(request[0]);
|
||||
byte[] buffer = request[0].postDataBuffer();
|
||||
assertEquals(256, buffer.length);
|
||||
for (int i = 0; i < 256; ++i) {
|
||||
assertEquals((byte) i, buffer[i]);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldWorkWithBinaryPostDataAndInterception() {
|
||||
page.navigate(server.EMPTY_PAGE);
|
||||
server.setRoute("/post", exchange -> {
|
||||
exchange.sendResponseHeaders(200, 0);
|
||||
exchange.getResponseBody().close();
|
||||
});
|
||||
Request[] request = {null};
|
||||
page.addListener(REQUEST, event -> request[0] = (Request) event.data());
|
||||
page.route("/post", (route, req) -> route.continue_());
|
||||
page.evaluate("async () => {\n" +
|
||||
" await fetch('./post', { method: 'POST', body: new Uint8Array(Array.from(Array(256).keys())) });\n" +
|
||||
"}");
|
||||
assertNotNull(request[0]);
|
||||
byte[] buffer = request[0].postDataBuffer();
|
||||
assertEquals(256, buffer.length);
|
||||
for (int i = 0; i < 256; ++i) {
|
||||
assertEquals((byte) i, buffer[i]);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldBeUndefinedWhenThereIsNoPostData() {
|
||||
Response response = page.navigate(server.EMPTY_PAGE);
|
||||
assertNull(response.request().postData());
|
||||
}
|
||||
|
||||
void shouldParseTheJsonPostData() {
|
||||
// Not supported in Java.
|
||||
}
|
||||
|
||||
void shouldParseTheDataIfContentTypeIsApplicationXWwwFormUrlencoded() {
|
||||
// Not supported in Java.
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReturnEventSource() {
|
||||
// 1. Setup server-sent events on server that immediately sends a message to the client.
|
||||
server.setRoute("/sse", exchange -> {
|
||||
exchange.getResponseHeaders().add("Content-Type", "text/event-stream");
|
||||
exchange.getResponseHeaders().add("Connection", "keep-alive");
|
||||
exchange.getResponseHeaders().add("Cache-Control", "no-cache");
|
||||
exchange.sendResponseHeaders(200, 0);
|
||||
try (OutputStreamWriter writer = new OutputStreamWriter(exchange.getResponseBody())) {
|
||||
writer.write("data: {\"foo\":\"bar\"}\n\n");
|
||||
}
|
||||
});
|
||||
// 2. Subscribe to page request events.
|
||||
page.navigate(server.EMPTY_PAGE);
|
||||
List<Request> requests = new ArrayList<>();
|
||||
page.addListener(REQUEST, event -> requests.add((Request) event.data()));
|
||||
// 3. Connect to EventSource in browser and return first message.
|
||||
Object result = page.evaluate("() => {\n" +
|
||||
" const eventSource = new EventSource('/sse');\n" +
|
||||
" return new Promise(resolve => {\n" +
|
||||
" eventSource.onmessage = e => resolve(JSON.parse(e.data));\n" +
|
||||
" });\n" +
|
||||
"}");
|
||||
assertEquals(mapOf("foo", "bar"), result);
|
||||
assertEquals("eventsource", requests.get(0).resourceType());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReturnNavigationBit() {
|
||||
Map<String, Request> requests = new HashMap<>();
|
||||
page.addListener(REQUEST, event -> {
|
||||
Request request = (Request) event.data();
|
||||
String name = request.url();
|
||||
int lastSlash = name.lastIndexOf('/');
|
||||
if (lastSlash != -1) {
|
||||
name = name.substring(lastSlash + 1);
|
||||
}
|
||||
requests.put(name, request);
|
||||
});
|
||||
server.setRedirect("/rrredirect", "/frames/one-frame.html");
|
||||
page.navigate(server.PREFIX + "/rrredirect");
|
||||
assertTrue(requests.get("rrredirect").isNavigationRequest());
|
||||
assertTrue(requests.get("one-frame.html").isNavigationRequest());
|
||||
assertTrue(requests.get("frame.html").isNavigationRequest());
|
||||
assertFalse(requests.get("script.js").isNavigationRequest());
|
||||
assertFalse(requests.get("style.css").isNavigationRequest());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReturnNavigationBitWhenNavigatingToImage() {
|
||||
List<Request> requests = new ArrayList<>();
|
||||
page.addListener(REQUEST, event -> requests.add((Request) event.data()));
|
||||
page.navigate(server.PREFIX + "/pptr.png");
|
||||
assertTrue(requests.get(0).isNavigationRequest());
|
||||
}
|
||||
}
|
@ -32,7 +32,7 @@ class Utils {
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
static Map mapOf(Object... entries) {
|
||||
static <K,V> Map<K, V> mapOf(Object... entries) {
|
||||
Map result = new HashMap();
|
||||
for (int i = 0; i + 1 < entries.length; i += 2) {
|
||||
result.put(entries[i], entries[i + 1]);
|
||||
|
Loading…
x
Reference in New Issue
Block a user