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.
|
// There is no standard JSON type in Java.
|
||||||
customSignature.put("Response.json", new String[0]);
|
customSignature.put("Response.json", new String[0]);
|
||||||
|
customSignature.put("Request.postDataJSON", new String[0]);
|
||||||
customSignature.put("Page.frame", new String[]{
|
customSignature.put("Page.frame", new String[]{
|
||||||
"Frame frameByName(String name);",
|
"Frame frameByName(String name);",
|
||||||
"Frame frameByUrl(String glob);",
|
"Frame frameByUrl(String glob);",
|
||||||
|
@ -39,7 +39,6 @@ public interface Request {
|
|||||||
String method();
|
String method();
|
||||||
String postData();
|
String postData();
|
||||||
byte[] postDataBuffer();
|
byte[] postDataBuffer();
|
||||||
RequestPostDataJSON postDataJSON();
|
|
||||||
Request redirectedFrom();
|
Request redirectedFrom();
|
||||||
Request redirectedTo();
|
Request redirectedTo();
|
||||||
String resourceType();
|
String resourceType();
|
||||||
|
@ -22,13 +22,16 @@ import com.microsoft.playwright.Frame;
|
|||||||
import com.microsoft.playwright.Request;
|
import com.microsoft.playwright.Request;
|
||||||
import com.microsoft.playwright.Response;
|
import com.microsoft.playwright.Response;
|
||||||
|
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.Base64;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
public class RequestImpl extends ChannelOwner implements Request {
|
public class RequestImpl extends ChannelOwner implements Request {
|
||||||
|
private final byte[] postData;
|
||||||
private RequestImpl redirectedFrom;
|
private RequestImpl redirectedFrom;
|
||||||
private RequestImpl redirectedTo;
|
private RequestImpl redirectedTo;
|
||||||
private final Map<String, String> headers = new HashMap<>();
|
final Map<String, String> headers = new HashMap<>();
|
||||||
RequestFailure failure;
|
RequestFailure failure;
|
||||||
|
|
||||||
RequestImpl(ChannelOwner parent, String type, String guid, JsonObject initializer) {
|
RequestImpl(ChannelOwner parent, String type, String guid, JsonObject initializer) {
|
||||||
@ -42,6 +45,11 @@ public class RequestImpl extends ChannelOwner implements Request {
|
|||||||
JsonObject item = e.getAsJsonObject();
|
JsonObject item = e.getAsJsonObject();
|
||||||
headers.put(item.get("name").getAsString().toLowerCase(), item.get("value").getAsString());
|
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
|
@Override
|
||||||
@ -71,17 +79,15 @@ public class RequestImpl extends ChannelOwner implements Request {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String postData() {
|
public String postData() {
|
||||||
|
if (postData == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
return new String(postData, StandardCharsets.UTF_8);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public byte[] postDataBuffer() {
|
public byte[] postDataBuffer() {
|
||||||
return new byte[0];
|
return postData;
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public RequestPostDataJSON postDataJSON() {
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -29,6 +29,7 @@ import java.util.Map;
|
|||||||
|
|
||||||
public class ResponseImpl extends ChannelOwner implements Response {
|
public class ResponseImpl extends ChannelOwner implements Response {
|
||||||
private final Map<String, String> headers = new HashMap<>();
|
private final Map<String, String> headers = new HashMap<>();
|
||||||
|
private final RequestImpl request;
|
||||||
|
|
||||||
ResponseImpl(ChannelOwner parent, String type, String guid, JsonObject initializer) {
|
ResponseImpl(ChannelOwner parent, String type, String guid, JsonObject initializer) {
|
||||||
super(parent, type, guid, initializer);
|
super(parent, type, guid, initializer);
|
||||||
@ -37,6 +38,14 @@ public class ResponseImpl extends ChannelOwner implements Response {
|
|||||||
JsonObject item = e.getAsJsonObject();
|
JsonObject item = e.getAsJsonObject();
|
||||||
headers.put(item.get("name").getAsString().toLowerCase(), item.get("value").getAsString());
|
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
|
@Override
|
||||||
@ -70,8 +79,8 @@ public class ResponseImpl extends ChannelOwner implements Response {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Request request() {
|
public RequestImpl request() {
|
||||||
return connection.getExistingObject(initializer.getAsJsonObject("request").get("guid").getAsString());
|
return request;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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")
|
@SuppressWarnings("unchecked")
|
||||||
static Map mapOf(Object... entries) {
|
static <K,V> Map<K, V> mapOf(Object... entries) {
|
||||||
Map result = new HashMap();
|
Map result = new HashMap();
|
||||||
for (int i = 0; i + 1 < entries.length; i += 2) {
|
for (int i = 0; i + 1 < entries.length; i += 2) {
|
||||||
result.put(entries[i], entries[i + 1]);
|
result.put(entries[i], entries[i + 1]);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user