mirror of
https://github.com/microsoft/playwright-java.git
synced 2025-09-08 21:01:00 +00:00
feat: implement remaining Response methods (#46)
This commit is contained in:
parent
24b101d071
commit
c134a6355f
@ -243,6 +243,8 @@ class Method extends Element {
|
||||
"void route(Pattern url, BiConsumer<Route, Request> handler);",
|
||||
"void route(Predicate<String> url, BiConsumer<Route, Request> handler);",
|
||||
});
|
||||
// There is no standard JSON type in Java.
|
||||
customSignature.put("Response.json", new String[0]);
|
||||
customSignature.put("Page.frame", new String[]{
|
||||
"Frame frameByName(String name);",
|
||||
"Frame frameByUrl(String glob);",
|
||||
|
@ -273,6 +273,7 @@ class Types {
|
||||
add("ElementHandle.screenshot", "Promise<Buffer>", "byte[]");
|
||||
add("Request.postDataBuffer", "null|Buffer", "byte[]");
|
||||
add("Response.body", "Promise<Buffer>", "byte[]");
|
||||
add("Response.finished", "Promise<null|Error>", "String");
|
||||
add("ChromiumBrowser.stopTracing", "Promise<Buffer>", "byte[]");
|
||||
|
||||
// JSON type
|
||||
|
@ -20,10 +20,9 @@ import java.util.*;
|
||||
|
||||
public interface Response {
|
||||
byte[] body();
|
||||
Error finished();
|
||||
String finished();
|
||||
Frame frame();
|
||||
Map<String, String> headers();
|
||||
Object json();
|
||||
boolean ok();
|
||||
Request request();
|
||||
int status();
|
||||
|
@ -16,20 +16,27 @@
|
||||
|
||||
package com.microsoft.playwright.impl;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
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;
|
||||
|
||||
import static com.microsoft.playwright.impl.Serialization.deserialize;
|
||||
|
||||
public class ResponseImpl extends ChannelOwner implements Response {
|
||||
private final Map<String, String> headers = new HashMap<>();
|
||||
|
||||
ResponseImpl(ChannelOwner parent, String type, String guid, JsonObject initializer) {
|
||||
super(parent, type, guid, initializer);
|
||||
|
||||
for (JsonElement e : initializer.getAsJsonArray("headers")) {
|
||||
JsonObject item = e.getAsJsonObject();
|
||||
headers.put(item.get("name").getAsString().toLowerCase(), item.get("value").getAsString());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -39,7 +46,11 @@ public class ResponseImpl extends ChannelOwner implements Response {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Error finished() {
|
||||
public String finished() {
|
||||
JsonObject json = sendMessage("finished").getAsJsonObject();
|
||||
if (json.has("error")) {
|
||||
return json.get("error").getAsString();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -50,12 +61,7 @@ public class ResponseImpl extends ChannelOwner implements Response {
|
||||
|
||||
@Override
|
||||
public Map<String, String> headers() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object json() {
|
||||
return null;
|
||||
return headers;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -80,7 +86,7 @@ public class ResponseImpl extends ChannelOwner implements Response {
|
||||
|
||||
@Override
|
||||
public String text() {
|
||||
return null;
|
||||
return new String(body(), StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -25,6 +25,7 @@ import com.microsoft.playwright.*;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.PrintStream;
|
||||
import java.lang.reflect.Array;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.StreamSupport;
|
||||
@ -38,7 +39,7 @@ class Serialization {
|
||||
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
e.printStackTrace(new PrintStream(out));
|
||||
result.error.stack = new String(out.toByteArray());
|
||||
result.error.stack = new String(out.toByteArray(), StandardCharsets.UTF_8);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -24,8 +24,7 @@ import java.nio.file.FileSystems;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
import static java.util.Collections.singletonList;
|
||||
import java.util.zip.GZIPOutputStream;
|
||||
|
||||
public class Server implements HttpHandler {
|
||||
private final HttpServer server;
|
||||
@ -40,6 +39,7 @@ public class Server implements HttpHandler {
|
||||
private final Map<String, Auth> auths = Collections.synchronizedMap(new HashMap<>());
|
||||
private final Map<String, String> csp = Collections.synchronizedMap(new HashMap<>());
|
||||
private final Map<String, HttpHandler> routes = Collections.synchronizedMap(new HashMap<>());
|
||||
private final Set<String> gzipRoutes = Collections.synchronizedSet(new HashSet<>());
|
||||
|
||||
private static class Auth {
|
||||
public final String user;
|
||||
@ -93,6 +93,10 @@ public class Server implements HttpHandler {
|
||||
this.csp.put(path, csp);
|
||||
}
|
||||
|
||||
void enableGzip(String path) {
|
||||
gzipRoutes.add(path);
|
||||
}
|
||||
|
||||
static class Request {
|
||||
public final String method;
|
||||
// TODO: make a copy to ensure thread safety?
|
||||
@ -135,6 +139,7 @@ public class Server implements HttpHandler {
|
||||
auths.clear();
|
||||
csp.clear();
|
||||
routes.clear();
|
||||
gzipRoutes.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -154,7 +159,7 @@ public class Server implements HttpHandler {
|
||||
}
|
||||
}
|
||||
if (!authorized) {
|
||||
exchange.getResponseHeaders().put("WWW-Authenticate", Arrays.asList("Basic realm=\"Secure Area\""));
|
||||
exchange.getResponseHeaders().add("WWW-Authenticate", "Basic realm=\"Secure Area\"");
|
||||
exchange.sendResponseHeaders(401, 0);
|
||||
try (Writer writer = new OutputStreamWriter(exchange.getResponseBody())) {
|
||||
writer.write("HTTP Error 401 Unauthorized: Access is denied");
|
||||
@ -180,20 +185,27 @@ public class Server implements HttpHandler {
|
||||
}
|
||||
|
||||
if (csp.containsKey(path)) {
|
||||
exchange.getResponseHeaders().put("Content-Security-Policy", singletonList(csp.get(path)));
|
||||
exchange.getResponseHeaders().add("Content-Security-Policy", csp.get(path));
|
||||
}
|
||||
File file = new File(resourcesDir, path.substring(1));
|
||||
exchange.getResponseHeaders().put("Content-Type", singletonList(mimeType(file)));
|
||||
exchange.getResponseHeaders().add("Content-Type", mimeType(file));
|
||||
OutputStream output = exchange.getResponseBody();
|
||||
if (gzipRoutes.contains(path)) {
|
||||
exchange.getResponseHeaders().add("Content-Encoding", "gzip");
|
||||
}
|
||||
try (FileInputStream input = new FileInputStream(file)) {
|
||||
exchange.sendResponseHeaders(200, 0);
|
||||
copy(input, exchange.getResponseBody());
|
||||
if (gzipRoutes.contains(path)) {
|
||||
output = new GZIPOutputStream(output);
|
||||
}
|
||||
copy(input, output);
|
||||
} catch (IOException e) {
|
||||
exchange.sendResponseHeaders(404, 0);
|
||||
try (Writer writer = new OutputStreamWriter(exchange.getResponseBody())) {
|
||||
writer.write("File not found: " + file.getCanonicalPath());
|
||||
}
|
||||
}
|
||||
exchange.getResponseBody().close();
|
||||
output.close();
|
||||
}
|
||||
|
||||
private static void copy(InputStream in, OutputStream out) throws IOException {
|
||||
|
@ -0,0 +1,135 @@
|
||||
/*
|
||||
* 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.*;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Arrays;
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
import static com.microsoft.playwright.Page.EventType.REQUESTFINISHED;
|
||||
import static com.microsoft.playwright.Page.EventType.RESPONSE;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
public class TestNetworkResponse extends TestBase {
|
||||
@Test
|
||||
void shouldWork() {
|
||||
server.setRoute("/empty.html", exchange -> {
|
||||
exchange.getResponseHeaders().add("foo", "bar");
|
||||
exchange.getResponseHeaders().add("BaZ", "bAz");
|
||||
exchange.sendResponseHeaders(200, 0);
|
||||
exchange.getResponseBody().close();
|
||||
});
|
||||
Response response = page.navigate(server.EMPTY_PAGE);
|
||||
assertEquals("bar", response.headers().get("foo"));
|
||||
assertEquals("bAz", response.headers().get("baz"));
|
||||
assertNull(response.headers().get("BaZ"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReturnText() {
|
||||
Response response = page.navigate(server.PREFIX + "/simple.json");
|
||||
assertEquals("{\"foo\": \"bar\"}\n", response.text());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReturnUncompressedText() {
|
||||
server.enableGzip("/simple.json");
|
||||
Response response = page.navigate(server.PREFIX + "/simple.json");
|
||||
assertEquals("gzip", response.headers().get("content-encoding"));
|
||||
assertEquals("{\"foo\": \"bar\"}\n", response.text());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldThrowWhenRequestingBodyOfRedirectedResponse() {
|
||||
server.setRedirect("/foo.html", "/empty.html");
|
||||
Response response = page.navigate(server.PREFIX + "/foo.html");
|
||||
Request redirectedFrom = response.request().redirectedFrom();
|
||||
assertNotNull(redirectedFrom);
|
||||
Response redirected = redirectedFrom.response();
|
||||
assertEquals(302, redirected.status());
|
||||
try {
|
||||
redirected.text();
|
||||
fail("did not throw");
|
||||
} catch (PlaywrightException e) {
|
||||
assertTrue(e.getMessage().contains("Response body is unavailable for redirect responses"));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldWaitUntilResponseCompletes() {
|
||||
page.navigate(server.EMPTY_PAGE);
|
||||
server.setRoute("/get", exchange -> {
|
||||
// In Firefox, |fetch| will be hanging until it receives |Content-Type| header
|
||||
// from server.
|
||||
exchange.getResponseHeaders().add("Content-Type", "text/plain; charset=utf-8");
|
||||
exchange.sendResponseHeaders(200, 0);
|
||||
try (OutputStreamWriter writer = new OutputStreamWriter(exchange.getResponseBody())) {
|
||||
writer.write("hello ");
|
||||
writer.flush();
|
||||
writer.write("wor");
|
||||
writer.flush();
|
||||
writer.write("ld!");
|
||||
}
|
||||
});
|
||||
// Setup page to trap response.
|
||||
boolean[] requestFinished = {false};
|
||||
page.addListener(REQUESTFINISHED, event -> {
|
||||
requestFinished[0] |= ((Request) event.data()).url().contains("/get");
|
||||
});
|
||||
// send request and wait for server response
|
||||
Deferred<Event<Page.EventType>> responseEvent = page.waitForEvent(RESPONSE);
|
||||
Future<Server.Request> request = server.waitForRequest("/get");
|
||||
page.evaluate("() => fetch('./get', { method: 'GET'})");
|
||||
assertNotNull(responseEvent.get());
|
||||
Response pageResponse = (Response) responseEvent.get().data();
|
||||
assertEquals(200, pageResponse.status());
|
||||
assertEquals(false, requestFinished[0]);
|
||||
assertEquals("hello world!", pageResponse.text());
|
||||
}
|
||||
|
||||
void shouldReturnJson() {
|
||||
// Not exposed in Java.
|
||||
}
|
||||
@Test
|
||||
void shouldReturnBody() throws IOException {
|
||||
Response response = page.navigate(server.PREFIX + "/pptr.png");
|
||||
byte[] expected = Files.readAllBytes(new File("src/test/resources/pptr.png").toPath());
|
||||
assertTrue(Arrays.equals(expected, response.body()));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReturnBodyWithCompression() throws IOException {
|
||||
server.enableGzip("/pptr.png");
|
||||
Response response = page.navigate(server.PREFIX + "/pptr.png");
|
||||
byte[] expected = Files.readAllBytes(new File("src/test/resources/pptr.png").toPath());
|
||||
assertTrue(Arrays.equals(expected, response.body()));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReturnStatusText() {
|
||||
server.setRoute("/cool", exchange -> {
|
||||
exchange.sendResponseHeaders(200, 0);
|
||||
exchange.getResponseBody().close();
|
||||
});
|
||||
Response response = page.navigate(server.PREFIX + "/cool");
|
||||
assertEquals("OK", response.statusText());
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user