mirror of
https://github.com/microsoft/playwright-java.git
synced 2025-12-27 01:44:01 +00:00
feat: implement routeFromHAR (#960)
This commit is contained in:
parent
fdec32c650
commit
efb281e016
@ -144,6 +144,17 @@ public interface Browser extends AutoCloseable {
|
||||
* 'http://per-context' } })}.
|
||||
*/
|
||||
public Proxy proxy;
|
||||
/**
|
||||
* Optional setting to control resource content management. If {@code omit} is specified, content is not persisted. If {@code attach}
|
||||
* is specified, resources are persistet as separate files and all of these files are archived along with the HAR file.
|
||||
* Defaults to {@code embed}, which stores content inline the HAR file as per HAR specification.
|
||||
*/
|
||||
public HarContentPolicy recordHarContent;
|
||||
/**
|
||||
* When set to {@code minimal}, only record information necessary for routing from HAR. This omits sizes, timing, page, cookies,
|
||||
* security and other types of HAR information that are not used when replaying from HAR. Defaults to {@code full}.
|
||||
*/
|
||||
public HarMode recordHarMode;
|
||||
/**
|
||||
* Optional setting to control whether to omit request content from the HAR. Defaults to {@code false}.
|
||||
*/
|
||||
@ -374,6 +385,23 @@ public interface Browser extends AutoCloseable {
|
||||
this.proxy = proxy;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Optional setting to control resource content management. If {@code omit} is specified, content is not persisted. If {@code attach}
|
||||
* is specified, resources are persistet as separate files and all of these files are archived along with the HAR file.
|
||||
* Defaults to {@code embed}, which stores content inline the HAR file as per HAR specification.
|
||||
*/
|
||||
public NewContextOptions setRecordHarContent(HarContentPolicy recordHarContent) {
|
||||
this.recordHarContent = recordHarContent;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* When set to {@code minimal}, only record information necessary for routing from HAR. This omits sizes, timing, page, cookies,
|
||||
* security and other types of HAR information that are not used when replaying from HAR. Defaults to {@code full}.
|
||||
*/
|
||||
public NewContextOptions setRecordHarMode(HarMode recordHarMode) {
|
||||
this.recordHarMode = recordHarMode;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Optional setting to control whether to omit request content from the HAR. Defaults to {@code false}.
|
||||
*/
|
||||
@ -602,6 +630,17 @@ public interface Browser extends AutoCloseable {
|
||||
* 'http://per-context' } })}.
|
||||
*/
|
||||
public Proxy proxy;
|
||||
/**
|
||||
* Optional setting to control resource content management. If {@code omit} is specified, content is not persisted. If {@code attach}
|
||||
* is specified, resources are persistet as separate files and all of these files are archived along with the HAR file.
|
||||
* Defaults to {@code embed}, which stores content inline the HAR file as per HAR specification.
|
||||
*/
|
||||
public HarContentPolicy recordHarContent;
|
||||
/**
|
||||
* When set to {@code minimal}, only record information necessary for routing from HAR. This omits sizes, timing, page, cookies,
|
||||
* security and other types of HAR information that are not used when replaying from HAR. Defaults to {@code full}.
|
||||
*/
|
||||
public HarMode recordHarMode;
|
||||
/**
|
||||
* Optional setting to control whether to omit request content from the HAR. Defaults to {@code false}.
|
||||
*/
|
||||
@ -832,6 +871,23 @@ public interface Browser extends AutoCloseable {
|
||||
this.proxy = proxy;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Optional setting to control resource content management. If {@code omit} is specified, content is not persisted. If {@code attach}
|
||||
* is specified, resources are persistet as separate files and all of these files are archived along with the HAR file.
|
||||
* Defaults to {@code embed}, which stores content inline the HAR file as per HAR specification.
|
||||
*/
|
||||
public NewPageOptions setRecordHarContent(HarContentPolicy recordHarContent) {
|
||||
this.recordHarContent = recordHarContent;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* When set to {@code minimal}, only record information necessary for routing from HAR. This omits sizes, timing, page, cookies,
|
||||
* security and other types of HAR information that are not used when replaying from HAR. Defaults to {@code full}.
|
||||
*/
|
||||
public NewPageOptions setRecordHarMode(HarMode recordHarMode) {
|
||||
this.recordHarMode = recordHarMode;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Optional setting to control whether to omit request content from the HAR. Defaults to {@code false}.
|
||||
*/
|
||||
|
||||
@ -516,6 +516,17 @@ public interface BrowserType {
|
||||
* Network proxy settings.
|
||||
*/
|
||||
public Proxy proxy;
|
||||
/**
|
||||
* Optional setting to control resource content management. If {@code omit} is specified, content is not persisted. If {@code attach}
|
||||
* is specified, resources are persistet as separate files and all of these files are archived along with the HAR file.
|
||||
* Defaults to {@code embed}, which stores content inline the HAR file as per HAR specification.
|
||||
*/
|
||||
public HarContentPolicy recordHarContent;
|
||||
/**
|
||||
* When set to {@code minimal}, only record information necessary for routing from HAR. This omits sizes, timing, page, cookies,
|
||||
* security and other types of HAR information that are not used when replaying from HAR. Defaults to {@code full}.
|
||||
*/
|
||||
public HarMode recordHarMode;
|
||||
/**
|
||||
* Optional setting to control whether to omit request content from the HAR. Defaults to {@code false}.
|
||||
*/
|
||||
@ -854,6 +865,23 @@ public interface BrowserType {
|
||||
this.proxy = proxy;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Optional setting to control resource content management. If {@code omit} is specified, content is not persisted. If {@code attach}
|
||||
* is specified, resources are persistet as separate files and all of these files are archived along with the HAR file.
|
||||
* Defaults to {@code embed}, which stores content inline the HAR file as per HAR specification.
|
||||
*/
|
||||
public LaunchPersistentContextOptions setRecordHarContent(HarContentPolicy recordHarContent) {
|
||||
this.recordHarContent = recordHarContent;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* When set to {@code minimal}, only record information necessary for routing from HAR. This omits sizes, timing, page, cookies,
|
||||
* security and other types of HAR information that are not used when replaying from HAR. Defaults to {@code full}.
|
||||
*/
|
||||
public LaunchPersistentContextOptions setRecordHarMode(HarMode recordHarMode) {
|
||||
this.recordHarMode = recordHarMode;
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Optional setting to control whether to omit request content from the HAR. Defaults to {@code false}.
|
||||
*/
|
||||
|
||||
@ -20,10 +20,7 @@ import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.microsoft.playwright.*;
|
||||
import com.microsoft.playwright.options.BindingCallback;
|
||||
import com.microsoft.playwright.options.Cookie;
|
||||
import com.microsoft.playwright.options.FunctionCallback;
|
||||
import com.microsoft.playwright.options.Geolocation;
|
||||
import com.microsoft.playwright.options.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.MalformedURLException;
|
||||
@ -336,7 +333,7 @@ class BrowserContextImpl extends ChannelOwner implements BrowserContext {
|
||||
|
||||
@Override
|
||||
public void route(String url, Consumer<Route> handler, RouteOptions options) {
|
||||
route(new UrlMatcher(this.baseUrl, url), handler, options);
|
||||
route(new UrlMatcher(baseUrl, url), handler, options);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -351,7 +348,13 @@ class BrowserContextImpl extends ChannelOwner implements BrowserContext {
|
||||
|
||||
@Override
|
||||
public void routeFromHAR(Path har, RouteFromHAROptions options) {
|
||||
// TODO:
|
||||
if (options == null) {
|
||||
options = new RouteFromHAROptions();
|
||||
}
|
||||
UrlMatcher matcher = UrlMatcher.forOneOf(baseUrl, options.url);
|
||||
HARRouter harRouter = new HARRouter(browser.localUtils, har, options.notFound);
|
||||
onClose(context -> harRouter.dispose());
|
||||
route(matcher, route -> harRouter.handle(route), null);
|
||||
}
|
||||
|
||||
private void route(UrlMatcher matcher, Consumer<Route> handler, RouteOptions options) {
|
||||
|
||||
@ -20,6 +20,7 @@ import com.google.gson.Gson;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.microsoft.playwright.*;
|
||||
import com.microsoft.playwright.options.HarContentPolicy;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
@ -140,8 +141,13 @@ class BrowserImpl extends ChannelOwner implements Browser {
|
||||
if (options.recordHarPath != null) {
|
||||
recordHar = new JsonObject();
|
||||
recordHar.addProperty("path", options.recordHarPath.toString());
|
||||
if (options.recordHarOmitContent != null) {
|
||||
recordHar.addProperty("omitContent", true);
|
||||
if (options.recordHarContent != null) {
|
||||
recordHar.addProperty("content", options.recordHarContent.toString().toLowerCase());
|
||||
} else if (options.recordHarOmitContent != null && options.recordHarOmitContent) {
|
||||
recordHar.addProperty("content", HarContentPolicy.OMIT.toString().toLowerCase());
|
||||
}
|
||||
if (options.recordHarMode != null) {
|
||||
recordHar.addProperty("mode", options.recordHarMode.toString().toLowerCase());
|
||||
}
|
||||
if (options.recordHarUrlFilter instanceof String) {
|
||||
recordHar.addProperty("urlGlob", (String) options.recordHarUrlFilter);
|
||||
@ -151,7 +157,9 @@ class BrowserImpl extends ChannelOwner implements Browser {
|
||||
recordHar.addProperty("urlRegexFlags", toJsRegexFlags(pattern));
|
||||
}
|
||||
options.recordHarPath = null;
|
||||
options.recordHarMode = null;
|
||||
options.recordHarOmitContent = null;
|
||||
options.recordHarContent = null;
|
||||
options.recordHarUrlFilter = null;
|
||||
} else {
|
||||
if (options.recordHarOmitContent != null) {
|
||||
@ -160,6 +168,12 @@ class BrowserImpl extends ChannelOwner implements Browser {
|
||||
if (options.recordHarUrlFilter != null) {
|
||||
throw new PlaywrightException("recordHarUrlFilter is set but recordHarPath is null");
|
||||
}
|
||||
if (options.recordHarMode != null) {
|
||||
throw new PlaywrightException("recordHarMode is set but recordHarPath is null");
|
||||
}
|
||||
if (options.recordHarContent != null) {
|
||||
throw new PlaywrightException("recordHarContent is set but recordHarPath is null");
|
||||
}
|
||||
}
|
||||
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
|
||||
@ -0,0 +1,104 @@
|
||||
/*
|
||||
* 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.impl;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import com.microsoft.playwright.PlaywrightException;
|
||||
import com.microsoft.playwright.Request;
|
||||
import com.microsoft.playwright.Route;
|
||||
import com.microsoft.playwright.options.HarNotFound;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.Base64;
|
||||
import java.util.Map;
|
||||
|
||||
import static com.microsoft.playwright.impl.LoggingSupport.logApi;
|
||||
import static com.microsoft.playwright.impl.Serialization.fromNameValues;
|
||||
import static com.microsoft.playwright.impl.Serialization.gson;
|
||||
|
||||
public class HARRouter {
|
||||
private final LocalUtils localUtils;
|
||||
private final HarNotFound defaultAction;
|
||||
private final String harId;
|
||||
|
||||
HARRouter(LocalUtils localUtils, Path harFile, HarNotFound defaultAction) {
|
||||
this.localUtils = localUtils;
|
||||
this.defaultAction = defaultAction;
|
||||
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("file", harFile.toString());
|
||||
JsonObject json = localUtils.sendMessage("harOpen", params).getAsJsonObject();
|
||||
if (json.has("error")) {
|
||||
throw new PlaywrightException(json.get("error").getAsString());
|
||||
}
|
||||
harId = json.get("harId").getAsString();
|
||||
}
|
||||
|
||||
void handle(Route route) {
|
||||
Request request = route.request();
|
||||
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("harId", harId);
|
||||
params.addProperty("url", request.url());
|
||||
params.addProperty("method", request.method());
|
||||
params.add("headers", gson().toJsonTree(request.headersArray()));
|
||||
if (request.postDataBuffer() != null) {
|
||||
String base64 = Base64.getEncoder().encodeToString(request.postDataBuffer());
|
||||
params.addProperty("postData", base64);
|
||||
}
|
||||
params.addProperty("isNavigationRequest", request.isNavigationRequest());
|
||||
JsonObject response = localUtils.sendMessage("harLookup", params).getAsJsonObject();
|
||||
|
||||
String action = response.get("action").getAsString();
|
||||
if ("redirect".equals(action)) {
|
||||
String redirectURL = response.get("redirectURL").getAsString();
|
||||
logApi("HAR: " + route.request().url() + " redirected to " + redirectURL);
|
||||
((RouteImpl) route).redirectNavigationRequest(redirectURL);
|
||||
return;
|
||||
}
|
||||
|
||||
if ("fulfill".equals(action)) {
|
||||
int status = response.get("status").getAsInt();
|
||||
Map<String, String> headers = fromNameValues(response.getAsJsonArray("headers"));
|
||||
byte[] buffer = Base64.getDecoder().decode(response.get("body").getAsString());
|
||||
route.fulfill(new Route.FulfillOptions()
|
||||
.setStatus(status)
|
||||
.setHeaders(headers)
|
||||
.setBodyBytes(buffer));
|
||||
return;
|
||||
}
|
||||
|
||||
if ("error".equals(action)) {
|
||||
logApi("HAR: " + response.get("message").getAsString());
|
||||
// Report the error, but fall through to the default handler.
|
||||
}
|
||||
|
||||
if (defaultAction == HarNotFound.FALLBACK) {
|
||||
route.fallback();
|
||||
return;
|
||||
}
|
||||
|
||||
// By default abort not matching requests.
|
||||
route.abort();
|
||||
}
|
||||
|
||||
void dispose() {
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("harId", harId);
|
||||
localUtils.sendMessageAsync("harClose", params);
|
||||
}
|
||||
}
|
||||
@ -60,7 +60,7 @@ class LoggingSupport {
|
||||
System.err.println(timestamp + " " + message);
|
||||
}
|
||||
|
||||
private void logApi(String message) {
|
||||
static void logApi(String message) {
|
||||
// This matches log format produced by the server.
|
||||
logWithTimestamp("pw:api " + message);
|
||||
}
|
||||
|
||||
@ -971,7 +971,13 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
|
||||
@Override
|
||||
public void routeFromHAR(Path har, RouteFromHAROptions options) {
|
||||
// TODO:
|
||||
if (options == null) {
|
||||
options = new RouteFromHAROptions();
|
||||
}
|
||||
UrlMatcher matcher = UrlMatcher.forOneOf(browserContext.baseUrl, options.url);
|
||||
HARRouter harRouter = new HARRouter(browserContext.browser().localUtils, har, options.notFound);
|
||||
onClose(context -> harRouter.dispose());
|
||||
route(matcher, route -> harRouter.handle(route), null);
|
||||
}
|
||||
|
||||
private void route(UrlMatcher matcher, Consumer<Route> handler, RouteOptions options) {
|
||||
|
||||
@ -199,6 +199,14 @@ public class RouteImpl extends ChannelOwner implements Route {
|
||||
return connection.getExistingObject(initializer.getAsJsonObject("request").get("guid").getAsString());
|
||||
}
|
||||
|
||||
void redirectNavigationRequest(String redirectURL) {
|
||||
startHandling();
|
||||
JsonObject params = new JsonObject();
|
||||
params.addProperty("url", redirectURL);
|
||||
// TODO: _raceWithPageClose ?
|
||||
sendMessageAsync("redirectNavigationRequest", params);
|
||||
}
|
||||
|
||||
private void startHandling() {
|
||||
if (handled) {
|
||||
throw new PlaywrightException("Route is already handled!");
|
||||
|
||||
@ -31,7 +31,6 @@ import java.lang.reflect.Type;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Path;
|
||||
import java.util.*;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
class Serialization {
|
||||
private static final Gson gson = new GsonBuilder()
|
||||
@ -311,6 +310,15 @@ class Serialization {
|
||||
return array;
|
||||
}
|
||||
|
||||
static Map<String, String> fromNameValues(JsonArray array) {
|
||||
Map<String, String> map = new LinkedHashMap<>();
|
||||
for (JsonElement element : array) {
|
||||
JsonObject pair = element.getAsJsonObject();
|
||||
map.put(pair.get("name").getAsString(), pair.get("value").getAsString());
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
static List<String> parseStringList(JsonArray array) {
|
||||
List<String> result = new ArrayList<>();
|
||||
for (JsonElement e : array) {
|
||||
|
||||
@ -0,0 +1,23 @@
|
||||
/*
|
||||
* 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.options;
|
||||
|
||||
public enum HarContentPolicy {
|
||||
OMIT,
|
||||
EMBED,
|
||||
ATTACH
|
||||
}
|
||||
@ -0,0 +1,22 @@
|
||||
/*
|
||||
* 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.options;
|
||||
|
||||
public enum HarMode {
|
||||
FULL,
|
||||
MINIMAL
|
||||
}
|
||||
@ -0,0 +1,381 @@
|
||||
/*
|
||||
* 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 com.microsoft.playwright.options.HarMode;
|
||||
import com.microsoft.playwright.options.HarNotFound;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.io.TempDir;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.Writer;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.List;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import static com.microsoft.playwright.Utils.copy;
|
||||
import static com.microsoft.playwright.Utils.extractZip;
|
||||
import static com.microsoft.playwright.assertions.PlaywrightAssertions.assertThat;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
public class TestBrowserContextHar extends TestBase {
|
||||
@Test
|
||||
void shouldContextRouteFromHARMatchingTheMethodAndFollowingRedirects() {
|
||||
Path path = Paths.get("src/test/resources/har-fulfill.har");
|
||||
context.routeFromHAR(path);
|
||||
Page page = context.newPage();
|
||||
page.navigate("http://no.playwright/");
|
||||
// HAR contains a redirect for the script that should be followed automatically.
|
||||
assertEquals("foo", page.evaluate("window.value"));
|
||||
// HAR contains a POST for the css file that should not be used.
|
||||
assertThat(page.locator("body")).hasCSS("background-color", "rgb(255, 0, 0)");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldPageRouteFromHARMatchingTheMethodAndFollowingRedirects() {
|
||||
Path path = Paths.get("src/test/resources/har-fulfill.har");
|
||||
Page page = context.newPage();
|
||||
page.routeFromHAR(path);
|
||||
page.navigate("http://no.playwright/");
|
||||
// HAR contains a redirect for the script that should be followed automatically.
|
||||
assertEquals("foo", page.evaluate("window.value"));
|
||||
// HAR contains a POST for the css file that should not be used.
|
||||
assertThat(page.locator("body")).hasCSS("background-color", "rgb(255, 0, 0)");
|
||||
}
|
||||
|
||||
@Test
|
||||
void fallbackContinueShouldContinueWhenNotFoundInHar() {
|
||||
Path path = Paths.get("src/test/resources/har-fulfill.har");
|
||||
context.routeFromHAR(path, new BrowserContext.RouteFromHAROptions().setNotFound(HarNotFound.FALLBACK));
|
||||
Page page = context.newPage();
|
||||
page.navigate(server.PREFIX + "/one-style.html");
|
||||
assertThat(page.locator("body")).hasCSS("background-color", "rgb(255, 192, 203)");
|
||||
}
|
||||
|
||||
@Test
|
||||
void byDefaultShouldAbortRequestsNotFoundInHar() {
|
||||
Path path = Paths.get("src/test/resources/har-fulfill.har");
|
||||
context.routeFromHAR(path);
|
||||
Page page = context.newPage();
|
||||
try {
|
||||
page.navigate(server.EMPTY_PAGE);
|
||||
fail("did not throw");
|
||||
} catch (PlaywrightException e) {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void fallbackContinueShouldContinueRequestsOnBadHar(@TempDir Path tmpDir) throws IOException {
|
||||
Path path = tmpDir.resolve("test.har");
|
||||
try (Writer stream = new OutputStreamWriter(Files.newOutputStream(path))) {
|
||||
stream.write("{ \"log\" : {} }");
|
||||
}
|
||||
context.routeFromHAR(path, new BrowserContext.RouteFromHAROptions().setNotFound(HarNotFound.FALLBACK));
|
||||
Page page = context.newPage();
|
||||
page.navigate(server.PREFIX + "/one-style.html");
|
||||
assertThat(page.locator("body")).hasCSS("background-color", "rgb(255, 192, 203)");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldOnlyHandleRequestsMatchingUrlFilter() {
|
||||
Path path = Paths.get("src/test/resources/har-fulfill.har");
|
||||
context.routeFromHAR(path, new BrowserContext.RouteFromHAROptions().setNotFound(HarNotFound.FALLBACK).setUrl("**/*.js"));
|
||||
Page page = context.newPage();
|
||||
context.route("http://no.playwright/", route -> {
|
||||
assertEquals("http://no.playwright/", route.request().url());
|
||||
route.fulfill(new Route.FulfillOptions()
|
||||
.setStatus(200)
|
||||
.setContentType("text/html")
|
||||
.setBody("<script src='./script.js'></script><div>hello</div>"));
|
||||
});
|
||||
page.navigate("http://no.playwright/");
|
||||
// HAR contains a redirect for the script that should be followed automatically.
|
||||
assertEquals("foo", page.evaluate("window.value"));
|
||||
assertThat(page.locator("body")).hasCSS("background-color", "rgba(0, 0, 0, 0)");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldOnlyContextRouteFromHARRequestsMatchingUrlFilter() {
|
||||
Path path = Paths.get("src/test/resources/har-fulfill.har");
|
||||
context.routeFromHAR(path, new BrowserContext.RouteFromHAROptions().setUrl("**/*.js"));
|
||||
Page page = context.newPage();
|
||||
context.route("http://no.playwright/", route -> {
|
||||
assertEquals("http://no.playwright/", route.request().url());
|
||||
route.fulfill(new Route.FulfillOptions()
|
||||
.setStatus(200)
|
||||
.setContentType("text/html")
|
||||
.setBody("<script src='./script.js'></script><div>hello</div>"));
|
||||
});
|
||||
page.navigate("http://no.playwright/");
|
||||
// HAR contains a redirect for the script that should be followed automatically.
|
||||
assertEquals("foo", page.evaluate("window.value"));
|
||||
assertThat(page.locator("body")).hasCSS("background-color", "rgba(0, 0, 0, 0)");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldOnlyPageRouteFromHARRequestsMatchingUrlFilter() {
|
||||
Path path = Paths.get("src/test/resources/har-fulfill.har");
|
||||
Page page = context.newPage();
|
||||
page.routeFromHAR(path, new Page.RouteFromHAROptions().setUrl("**/*.js"));
|
||||
context.route("http://no.playwright/", route -> {
|
||||
assertEquals("http://no.playwright/", route.request().url());
|
||||
route.fulfill(new Route.FulfillOptions()
|
||||
.setStatus(200)
|
||||
.setContentType("text/html")
|
||||
.setBody("<script src='./script.js'></script><div>hello</div>"));
|
||||
});
|
||||
page.navigate("http://no.playwright/");
|
||||
// HAR contains a redirect for the script that should be followed automatically.
|
||||
assertEquals("foo", page.evaluate("window.value"));
|
||||
assertThat(page.locator("body")).hasCSS("background-color", "rgba(0, 0, 0, 0)");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldSupportRegexFilter() {
|
||||
Path path = Paths.get("src/test/resources/har-fulfill.har");
|
||||
context.routeFromHAR(path, new BrowserContext.RouteFromHAROptions().setUrl(Pattern.compile(".*(\\.js|.*\\.css|no.playwright\\/)$")));
|
||||
Page page = context.newPage();
|
||||
page.navigate("http://no.playwright/");
|
||||
assertEquals("foo", page.evaluate("window.value"));
|
||||
assertThat(page.locator("body")).hasCSS("background-color", "rgb(255, 0, 0)");
|
||||
}
|
||||
|
||||
@Test
|
||||
void newPageShouldFulfillFromHarMatchingTheMethodAndFollowingRedirects() {
|
||||
Path path = Paths.get("src/test/resources/har-fulfill.har");
|
||||
Page page = browser.newPage();
|
||||
page.routeFromHAR(path);
|
||||
page.navigate("http://no.playwright/");
|
||||
// HAR contains a redirect for the script that should be followed automatically.
|
||||
assertEquals("foo", page.evaluate("window.value"));
|
||||
// HAR contains a POST for the css file that should not be used.
|
||||
assertThat(page.locator("body")).hasCSS("background-color", "rgb(255, 0, 0)");
|
||||
page.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldChangeDocumentURLAfterRedirectedNavigation() {
|
||||
Path path = Paths.get("src/test/resources/har-redirect.har");
|
||||
context.routeFromHAR(path);
|
||||
Page page = context.newPage();
|
||||
Response response = page.waitForNavigation(() -> {
|
||||
page.navigate("https://theverge.com/");
|
||||
page.waitForURL("https://www.theverge.com/");
|
||||
});
|
||||
assertThat(page).hasURL("https://www.theverge.com/");
|
||||
assertEquals("https://www.theverge.com/", response.request().url());
|
||||
assertEquals("https://www.theverge.com/", page.evaluate("location.href"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldChangeDocumentURLAfterRedirectedNavigationOnClick() {
|
||||
Path path = Paths.get("src/test/resources/har-redirect.har");
|
||||
context.routeFromHAR(path, new BrowserContext.RouteFromHAROptions().setUrl(Pattern.compile(".*theverge.*")));
|
||||
Page page = context.newPage();
|
||||
page.navigate(server.EMPTY_PAGE);
|
||||
page.setContent("<a href='https://theverge.com/'>click me</a>");
|
||||
Response response = page.waitForNavigation(() -> page.click("text=click me"));
|
||||
assertThat(page).hasURL("https://www.theverge.com/");
|
||||
assertEquals("https://www.theverge.com/", response.request().url());
|
||||
assertEquals("https://www.theverge.com/", page.evaluate("location.href"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldGoBackToRedirectedNavigation() {
|
||||
Path path = Paths.get("src/test/resources/har-redirect.har");
|
||||
context.routeFromHAR(path, new BrowserContext.RouteFromHAROptions().setUrl(Pattern.compile(".*theverge.*")));
|
||||
Page page = context.newPage();
|
||||
page.navigate("https://theverge.com/");
|
||||
page.navigate(server.EMPTY_PAGE);
|
||||
assertThat(page).hasURL(server.EMPTY_PAGE);
|
||||
Response response = page.goBack();
|
||||
assertThat(page).hasURL("https://www.theverge.com/");
|
||||
assertEquals("https://www.theverge.com/", response.request().url());
|
||||
assertEquals("https://www.theverge.com/", page.evaluate("location.href"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldGoForwardToRedirectedNavigation() {
|
||||
Path path = Paths.get("src/test/resources/har-redirect.har");
|
||||
context.routeFromHAR(path, new BrowserContext.RouteFromHAROptions().setUrl(Pattern.compile(".*theverge.*")));
|
||||
Page page = context.newPage();
|
||||
page.navigate(server.EMPTY_PAGE);
|
||||
assertThat(page).hasURL(server.EMPTY_PAGE);
|
||||
page.navigate("https://theverge.com/");
|
||||
assertThat(page).hasURL("https://www.theverge.com/");
|
||||
page.goBack();
|
||||
assertThat(page).hasURL(server.EMPTY_PAGE);
|
||||
Response response = page.goForward();
|
||||
assertThat(page).hasURL("https://www.theverge.com/");
|
||||
assertEquals("https://www.theverge.com/", response.request().url());
|
||||
assertEquals("https://www.theverge.com/", page.evaluate("() => location.href"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReloadRedirectedNavigation() {
|
||||
Path path = Paths.get("src/test/resources/har-redirect.har");
|
||||
context.routeFromHAR(path, new BrowserContext.RouteFromHAROptions().setUrl(Pattern.compile(".*theverge.*")));
|
||||
Page page = context.newPage();
|
||||
page.navigate("https://theverge.com/");
|
||||
assertThat(page).hasURL("https://www.theverge.com/");
|
||||
Response response = page.reload();
|
||||
assertThat(page).hasURL("https://www.theverge.com/");
|
||||
assertEquals("https://www.theverge.com/", response.request().url());
|
||||
assertEquals("https://www.theverge.com/", page.evaluate("() => location.href"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldFulfillFromHarWithContentInAFile() {
|
||||
Path path = Paths.get("src/test/resources/har-sha1.har");
|
||||
context.routeFromHAR(path);
|
||||
Page page = context.newPage();
|
||||
page.navigate("http://no.playwright/");
|
||||
assertEquals("<html><head></head><body>Hello, world</body></html>", page.content());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldRoundTripHarZip(@TempDir Path tmpDir) {
|
||||
Path harPath = tmpDir.resolve("har.zip");
|
||||
try (BrowserContext context1 = browser.newContext(new Browser.NewContextOptions()
|
||||
.setRecordHarPath(harPath)
|
||||
.setRecordHarMode(HarMode.MINIMAL))) {
|
||||
Page page1 = context1.newPage();
|
||||
page1.navigate(server.PREFIX + "/one-style.html");
|
||||
}
|
||||
try (BrowserContext context2 = browser.newContext()) {
|
||||
context2.routeFromHAR(harPath, new BrowserContext.RouteFromHAROptions().setNotFound(HarNotFound.ABORT));
|
||||
Page page2 = context.newPage();
|
||||
page2.navigate(server.PREFIX + "/one-style.html");
|
||||
assertTrue(page2.content().contains("hello, world!"));
|
||||
assertThat(page2.locator("body")).hasCSS("background-color", "rgb(255, 192, 203)");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldRoundTripExtractedHarZip(@TempDir Path tmpDir) throws IOException {
|
||||
Path harPath = tmpDir.resolve("har.zip");
|
||||
try (BrowserContext context1 = browser.newContext(new Browser.NewContextOptions()
|
||||
.setRecordHarPath(harPath)
|
||||
.setRecordHarMode(HarMode.MINIMAL))) {
|
||||
Page page1 = context1.newPage();
|
||||
page1.navigate(server.PREFIX + "/one-style.html");
|
||||
}
|
||||
|
||||
Path harDir = tmpDir.resolve("hardir");
|
||||
extractZip(harPath, harDir);
|
||||
|
||||
try (BrowserContext context2 = browser.newContext()) {
|
||||
context2.routeFromHAR(harDir.resolve("har.har"));
|
||||
Page page2 = context.newPage();
|
||||
page2.navigate(server.PREFIX + "/one-style.html");
|
||||
assertTrue(page2.content().contains("hello, world!"));
|
||||
assertThat(page2.locator("body")).hasCSS("background-color", "rgb(255, 192, 203)");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldRoundTripHarWithPostData(@TempDir Path tmpDir) {
|
||||
server.setRoute("/echo", exchange -> {
|
||||
exchange.sendResponseHeaders(200, 0);
|
||||
try (OutputStream out = exchange.getResponseBody()) {
|
||||
copy(exchange.getRequestBody(), out);
|
||||
}
|
||||
});
|
||||
|
||||
String fetchFunction = "async body => {\n" +
|
||||
" const response = await fetch('/echo', { method: 'POST', body });\n" +
|
||||
" return await response.text();\n" +
|
||||
" }\n";
|
||||
Path harPath = tmpDir.resolve("har.zip");
|
||||
try (BrowserContext context1 = browser.newContext(new Browser.NewContextOptions()
|
||||
.setRecordHarPath(harPath)
|
||||
.setRecordHarMode(HarMode.MINIMAL))) {
|
||||
Page page1 = context1.newPage();
|
||||
page1.navigate(server.EMPTY_PAGE);
|
||||
assertEquals("1", page1.evaluate(fetchFunction, "1"));
|
||||
assertEquals("2", page1.evaluate(fetchFunction, "2"));
|
||||
assertEquals("3", page1.evaluate(fetchFunction, "3"));
|
||||
}
|
||||
server.reset();
|
||||
try (BrowserContext context2 = browser.newContext()) {
|
||||
context2.routeFromHAR(harPath);
|
||||
Page page2 = context2.newPage();
|
||||
page2.navigate(server.EMPTY_PAGE);
|
||||
assertEquals("1", page2.evaluate(fetchFunction, "1"));
|
||||
assertEquals("2", page2.evaluate(fetchFunction, "2"));
|
||||
assertEquals("3", page2.evaluate(fetchFunction, "3"));
|
||||
assertEquals("3", page2.evaluate(fetchFunction, "3"));
|
||||
try {
|
||||
Object result = page2.evaluate(fetchFunction, "4");
|
||||
System.out.println(result);
|
||||
fail("did not throw");
|
||||
} catch (PlaywrightException e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldDisambiguateByHeader(@TempDir Path tmpDir) {
|
||||
server.setRoute("/echo", exchange -> {
|
||||
exchange.sendResponseHeaders(200, 0);
|
||||
try (OutputStream out = exchange.getResponseBody()) {
|
||||
List<String> values = exchange.getRequestHeaders().get("baz");
|
||||
try (OutputStreamWriter writer = new OutputStreamWriter(exchange.getResponseBody())) {
|
||||
writer.write(values == null ? "<no header>" : String.join(", ", values));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
String fetchFunction = "async bazValue => {\n" +
|
||||
" const response = await fetch('/echo', {\n" +
|
||||
" method: 'POST',\n" +
|
||||
" body: '',\n" +
|
||||
" headers: {\n" +
|
||||
" foo: 'foo-value',\n" +
|
||||
" bar: 'bar-value',\n" +
|
||||
" baz: bazValue,\n" +
|
||||
" }\n" +
|
||||
" });\n" +
|
||||
" return await response.text();\n" +
|
||||
" }\n";
|
||||
Path harPath = tmpDir.resolve("har.zip");
|
||||
try (BrowserContext context1 = browser.newContext(new Browser.NewContextOptions()
|
||||
.setRecordHarPath(harPath)
|
||||
.setRecordHarMode(HarMode.MINIMAL))) {
|
||||
Page page1 = context1.newPage();
|
||||
page1.navigate(server.EMPTY_PAGE);
|
||||
assertEquals("baz1", page1.evaluate(fetchFunction, "baz1"));
|
||||
assertEquals("baz2", page1.evaluate(fetchFunction, "baz2"));
|
||||
assertEquals("baz3", page1.evaluate(fetchFunction, "baz3"));
|
||||
}
|
||||
server.reset();
|
||||
try (BrowserContext context2 = browser.newContext()) {
|
||||
context2.routeFromHAR(harPath);
|
||||
Page page2 = context2.newPage();
|
||||
page2.navigate(server.EMPTY_PAGE);
|
||||
assertEquals("baz1", page2.evaluate(fetchFunction, "baz1"));
|
||||
assertEquals("baz2", page2.evaluate(fetchFunction, "baz2"));
|
||||
assertEquals("baz3", page2.evaluate(fetchFunction, "baz3"));
|
||||
assertEquals("baz1", page2.evaluate(fetchFunction, "baz4"));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -17,7 +17,6 @@
|
||||
package com.microsoft.playwright;
|
||||
|
||||
import com.microsoft.playwright.impl.Driver;
|
||||
import com.microsoft.playwright.options.WaitForSelectorState;
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.Assumptions;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
@ -499,7 +498,7 @@ public class TestBrowserTypeConnect extends TestBase {
|
||||
Path trace = tmpDir.resolve("trace1.zip");
|
||||
context.tracing().stop(new Tracing.StopOptions().setPath(trace));
|
||||
|
||||
Map<String, byte[]> entries = parseTrace(trace);
|
||||
Map<String, byte[]> entries = parseZip(trace);
|
||||
Map<String, byte[]> sources = entries.entrySet().stream().filter(e -> e.getKey().endsWith(".txt")).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
|
||||
assertEquals(1, sources.size());
|
||||
|
||||
|
||||
@ -20,16 +20,17 @@ import com.google.gson.Gson;
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.microsoft.playwright.options.HarContentPolicy;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.io.TempDir;
|
||||
|
||||
import java.io.FileReader;
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
import java.io.*;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import static com.microsoft.playwright.options.LoadState.DOMCONTENTLOADED;
|
||||
@ -50,8 +51,12 @@ public class TestHar extends TestBase {
|
||||
final Page page;
|
||||
|
||||
PageWithHar() throws IOException {
|
||||
harFile = Files.createTempFile("test-", ".har");
|
||||
context = browser.newContext(new Browser.NewContextOptions()
|
||||
this(new Browser.NewContextOptions(), null);
|
||||
}
|
||||
|
||||
PageWithHar(Browser.NewContextOptions options, Path harFilePath) throws IOException {
|
||||
harFile = harFilePath == null ? Files.createTempFile("test-", ".har") : harFilePath;
|
||||
context = browser.newContext(options
|
||||
.setRecordHarPath(harFile).setIgnoreHTTPSErrors(true));
|
||||
page = context.newPage();
|
||||
}
|
||||
@ -61,6 +66,11 @@ public class TestHar extends TestBase {
|
||||
return parseHar(harFile);
|
||||
}
|
||||
|
||||
Map<String, byte[]> parseZip() throws IOException {
|
||||
context.close();
|
||||
return Utils.parseZip(harFile);
|
||||
}
|
||||
|
||||
void dispose() throws IOException {
|
||||
context.close();
|
||||
Files.deleteIfExists(harFile);
|
||||
@ -217,4 +227,94 @@ public class TestHar extends TestBase {
|
||||
assertEquals(1, entries.size());
|
||||
assertTrue(entries.get(0).getAsJsonObject().getAsJsonObject("request").get("url").getAsString().endsWith("har.html"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldOmitContent(@TempDir Path tmpDir) throws IOException {
|
||||
Path harPath = tmpDir.resolve("test.har");
|
||||
PageWithHar pageWithHar = new PageWithHar(new Browser.NewContextOptions()
|
||||
.setRecordHarContent(HarContentPolicy.OMIT), harPath);
|
||||
pageWithHar.page.navigate(server.PREFIX + "/har.html");
|
||||
pageWithHar.page.evaluate("() => fetch('/pptr.png').then(r => r.arrayBuffer())");
|
||||
JsonObject log = pageWithHar.log();
|
||||
pageWithHar.dispose();
|
||||
JsonArray entries = log.getAsJsonArray("entries");
|
||||
assertFalse(entries.get(0).getAsJsonObject()
|
||||
.getAsJsonObject("response")
|
||||
.getAsJsonObject("content")
|
||||
.has("text"));
|
||||
assertFalse(entries.get(0).getAsJsonObject()
|
||||
.getAsJsonObject("response")
|
||||
.getAsJsonObject("content")
|
||||
.has("_file"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldOmitContentLegacy(@TempDir Path tmpDir) throws IOException {
|
||||
Path harPath = tmpDir.resolve("test.har");
|
||||
PageWithHar pageWithHar = new PageWithHar(new Browser.NewContextOptions()
|
||||
.setRecordHarOmitContent(true), harPath);
|
||||
pageWithHar.page.navigate(server.PREFIX + "/har.html");
|
||||
pageWithHar.page.evaluate("() => fetch('/pptr.png').then(r => r.arrayBuffer())");
|
||||
JsonObject log = pageWithHar.log();
|
||||
pageWithHar.dispose();
|
||||
JsonArray entries = log.getAsJsonArray("entries");
|
||||
assertFalse(entries.get(0).getAsJsonObject()
|
||||
.getAsJsonObject("response")
|
||||
.getAsJsonObject("content")
|
||||
.has("text"));
|
||||
assertFalse(entries.get(0).getAsJsonObject()
|
||||
.getAsJsonObject("response")
|
||||
.getAsJsonObject("content")
|
||||
.has("_file"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldAttachContent(@TempDir Path tmpDir) throws IOException {
|
||||
Path harPath = tmpDir.resolve("test.har.zip");
|
||||
PageWithHar pageWithHar = new PageWithHar(new Browser.NewContextOptions()
|
||||
.setRecordHarContent(HarContentPolicy.ATTACH), harPath);
|
||||
pageWithHar.page.navigate(server.PREFIX + "/har.html");
|
||||
pageWithHar.page.evaluate("() => fetch('/pptr.png').then(r => r.arrayBuffer())");
|
||||
Map<String, byte[]> zip = pageWithHar.parseZip();
|
||||
JsonObject log = new Gson().fromJson(new InputStreamReader(new ByteArrayInputStream(zip.get("har.har"))), JsonObject.class).getAsJsonObject("log");
|
||||
pageWithHar.dispose();
|
||||
|
||||
JsonArray entries = log.getAsJsonArray("entries");
|
||||
{
|
||||
JsonObject content = entries.get(0).getAsJsonObject()
|
||||
.getAsJsonObject("response")
|
||||
.getAsJsonObject("content");
|
||||
assertFalse(content.has("encoding"));
|
||||
assertEquals("text/html", content.get("mimeType").getAsString());
|
||||
assertTrue(content.get("_file").getAsString().contains("75841480e2606c03389077304342fac2c58ccb1b"));
|
||||
assertTrue(content.get("size").getAsInt() >= 96);
|
||||
assertEquals(0, content.get("compression").getAsInt());
|
||||
}
|
||||
{
|
||||
JsonObject content = entries.get(1).getAsJsonObject()
|
||||
.getAsJsonObject("response")
|
||||
.getAsJsonObject("content");
|
||||
assertFalse(content.has("encoding"));
|
||||
assertEquals("text/css", content.get("mimeType").getAsString());
|
||||
assertTrue(content.get("_file").getAsString().contains("79f739d7bc88e80f55b9891a22bf13a2b4e18adb"));
|
||||
assertTrue(content.get("size").getAsInt() >= 37);
|
||||
assertEquals(0, content.get("compression").getAsInt());
|
||||
}
|
||||
{
|
||||
JsonObject content = entries.get(2).getAsJsonObject()
|
||||
.getAsJsonObject("response")
|
||||
.getAsJsonObject("content");
|
||||
assertFalse(content.has("encoding"));
|
||||
assertEquals("image/png", content.get("mimeType").getAsString());
|
||||
assertTrue(content.get("_file").getAsString().contains("a4c3a18f0bb83f5d9fe7ce561e065c36205762fa"));
|
||||
assertTrue(content.get("size").getAsInt() >= 6000);
|
||||
assertEquals(0, content.get("compression").getAsInt());
|
||||
}
|
||||
assertTrue(new String(zip.get("75841480e2606c03389077304342fac2c58ccb1b.html"), StandardCharsets.UTF_8).contains("HAR Page"));
|
||||
assertTrue(new String(zip.get("79f739d7bc88e80f55b9891a22bf13a2b4e18adb.css"), StandardCharsets.UTF_8).contains("pink"));
|
||||
assertEquals(entries.get(2).getAsJsonObject()
|
||||
.getAsJsonObject("response")
|
||||
.getAsJsonObject("content")
|
||||
.get("size").getAsInt(), zip.get("a4c3a18f0bb83f5d9fe7ce561e065c36205762fa.png").length);
|
||||
}
|
||||
}
|
||||
|
||||
@ -113,7 +113,7 @@ public class TestTracing extends TestBase {
|
||||
Path trace = tmpDir.resolve("trace1.zip");
|
||||
context.tracing().stop(new Tracing.StopOptions().setPath(trace));
|
||||
|
||||
Map<String, byte[]> entries = Utils.parseTrace(trace);
|
||||
Map<String, byte[]> entries = Utils.parseZip(trace);
|
||||
Map<String, byte[]> sources = entries.entrySet().stream().filter(e -> e.getKey().endsWith(".txt")).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
|
||||
assertEquals(1, sources.size());
|
||||
|
||||
|
||||
@ -22,7 +22,7 @@ import com.google.gson.JsonParser;
|
||||
|
||||
import java.io.*;
|
||||
import java.net.ServerSocket;
|
||||
import java.net.Socket;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
@ -89,7 +89,7 @@ class Utils {
|
||||
}
|
||||
}
|
||||
|
||||
static Map<String, byte[]> parseTrace(Path trace) throws IOException {
|
||||
static Map<String, byte[]> parseZip(Path trace) throws IOException {
|
||||
Map<String, byte[]> entries = new HashMap<>();
|
||||
try (ZipInputStream zis = new ZipInputStream(new FileInputStream(trace.toFile()))) {
|
||||
for (ZipEntry zipEntry = zis.getNextEntry(); zipEntry != null; zipEntry = zis.getNextEntry()) {
|
||||
@ -104,6 +104,25 @@ class Utils {
|
||||
return entries;
|
||||
}
|
||||
|
||||
static Map<String, byte[]> extractZip(Path zipPath, Path toDir) throws IOException {
|
||||
Map<String, byte[]> entries = new HashMap<>();
|
||||
Files.createDirectories(toDir);
|
||||
try (ZipInputStream zis = new ZipInputStream(new FileInputStream(zipPath.toFile()))) {
|
||||
for (ZipEntry zipEntry = zis.getNextEntry(); zipEntry != null; zipEntry = zis.getNextEntry()) {
|
||||
System.out.println(zipEntry.getName());
|
||||
Path toPath = toDir.resolve(zipEntry.getName());
|
||||
if (zipEntry.isDirectory()) {
|
||||
Files.createDirectories(toPath);
|
||||
} else {
|
||||
Files.copy(zis, toPath);
|
||||
}
|
||||
zis.closeEntry();
|
||||
}
|
||||
}
|
||||
return entries;
|
||||
}
|
||||
|
||||
|
||||
enum OS { WINDOWS, MAC, LINUX, UNKNOWN }
|
||||
static OS getOS() {
|
||||
String name = System.getProperty("os.name").toLowerCase();
|
||||
|
||||
366
playwright/src/test/resources/har-fulfill.har
Normal file
366
playwright/src/test/resources/har-fulfill.har
Normal file
@ -0,0 +1,366 @@
|
||||
{
|
||||
"log": {
|
||||
"version": "1.2",
|
||||
"creator": {
|
||||
"name": "Playwright",
|
||||
"version": "1.23.0-next"
|
||||
},
|
||||
"browser": {
|
||||
"name": "chromium",
|
||||
"version": "103.0.5060.33"
|
||||
},
|
||||
"pages": [
|
||||
{
|
||||
"startedDateTime": "2022-06-10T04:27:32.125Z",
|
||||
"id": "page@b17b177f1c2e66459db3dcbe44636ffd",
|
||||
"title": "Hey",
|
||||
"pageTimings": {
|
||||
"onContentLoad": 70,
|
||||
"onLoad": 70
|
||||
}
|
||||
}
|
||||
],
|
||||
"entries": [
|
||||
{
|
||||
"_frameref": "frame@c7467fc0f1f86f09fc3b0d727a3862ea",
|
||||
"_monotonicTime": 270572145.898,
|
||||
"startedDateTime": "2022-06-10T04:27:32.146Z",
|
||||
"time": 8.286,
|
||||
"request": {
|
||||
"method": "GET",
|
||||
"url": "http://no.playwright/",
|
||||
"httpVersion": "HTTP/1.1",
|
||||
"cookies": [],
|
||||
"headers": [
|
||||
{
|
||||
"name": "Accept",
|
||||
"value": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9"
|
||||
},
|
||||
{
|
||||
"name": "Upgrade-Insecure-Requests",
|
||||
"value": "1"
|
||||
},
|
||||
{
|
||||
"name": "User-Agent",
|
||||
"value": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.5060.33 Safari/537.36"
|
||||
}
|
||||
],
|
||||
"queryString": [],
|
||||
"headersSize": 326,
|
||||
"bodySize": 0
|
||||
},
|
||||
"response": {
|
||||
"status": 200,
|
||||
"statusText": "OK",
|
||||
"httpVersion": "HTTP/1.1",
|
||||
"cookies": [],
|
||||
"headers": [
|
||||
{
|
||||
"name": "content-length",
|
||||
"value": "111"
|
||||
},
|
||||
{
|
||||
"name": "content-type",
|
||||
"value": "text/html"
|
||||
}
|
||||
],
|
||||
"content": {
|
||||
"size": 111,
|
||||
"mimeType": "text/html",
|
||||
"compression": 0,
|
||||
"text": "<title>Hey</title><link rel='stylesheet' href='./style.css'><script src='./script.js'></script><div>hello</div>"
|
||||
},
|
||||
"headersSize": 65,
|
||||
"bodySize": 170,
|
||||
"redirectURL": "",
|
||||
"_transferSize": 170
|
||||
},
|
||||
"cache": {
|
||||
"beforeRequest": null,
|
||||
"afterRequest": null
|
||||
},
|
||||
"timings": {
|
||||
"dns": -1,
|
||||
"connect": -1,
|
||||
"ssl": -1,
|
||||
"send": 0,
|
||||
"wait": 8.286,
|
||||
"receive": -1
|
||||
},
|
||||
"pageref": "page@b17b177f1c2e66459db3dcbe44636ffd",
|
||||
"_securityDetails": {}
|
||||
},
|
||||
{
|
||||
"_frameref": "frame@c7467fc0f1f86f09fc3b0d727a3862ea",
|
||||
"_monotonicTime": 270572174.683,
|
||||
"startedDateTime": "2022-06-10T04:27:32.172Z",
|
||||
"time": 7.132,
|
||||
"request": {
|
||||
"method": "POST",
|
||||
"url": "http://no.playwright/style.css",
|
||||
"httpVersion": "HTTP/1.1",
|
||||
"cookies": [],
|
||||
"headers": [
|
||||
{
|
||||
"name": "Accept",
|
||||
"value": "text/css,*/*;q=0.1"
|
||||
},
|
||||
{
|
||||
"name": "Referer",
|
||||
"value": "http://no.playwright/"
|
||||
},
|
||||
{
|
||||
"name": "User-Agent",
|
||||
"value": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.5060.33 Safari/537.36"
|
||||
}
|
||||
],
|
||||
"queryString": [],
|
||||
"headersSize": 220,
|
||||
"bodySize": 0
|
||||
},
|
||||
"response": {
|
||||
"status": 200,
|
||||
"statusText": "OK",
|
||||
"httpVersion": "HTTP/1.1",
|
||||
"cookies": [],
|
||||
"headers": [
|
||||
{
|
||||
"name": "content-length",
|
||||
"value": "24"
|
||||
},
|
||||
{
|
||||
"name": "content-type",
|
||||
"value": "text/css"
|
||||
}
|
||||
],
|
||||
"content": {
|
||||
"size": 24,
|
||||
"mimeType": "text/css",
|
||||
"compression": 0,
|
||||
"text": "body { background:cyan }"
|
||||
},
|
||||
"headersSize": 63,
|
||||
"bodySize": 81,
|
||||
"redirectURL": "",
|
||||
"_transferSize": 81
|
||||
},
|
||||
"cache": {
|
||||
"beforeRequest": null,
|
||||
"afterRequest": null
|
||||
},
|
||||
"timings": {
|
||||
"dns": -1,
|
||||
"connect": -1,
|
||||
"ssl": -1,
|
||||
"send": 0,
|
||||
"wait": 8.132,
|
||||
"receive": -1
|
||||
},
|
||||
"pageref": "page@b17b177f1c2e66459db3dcbe44636ffd",
|
||||
"_securityDetails": {}
|
||||
},
|
||||
{
|
||||
"_frameref": "frame@c7467fc0f1f86f09fc3b0d727a3862ea",
|
||||
"_monotonicTime": 270572174.683,
|
||||
"startedDateTime": "2022-06-10T04:27:32.174Z",
|
||||
"time": 8.132,
|
||||
"request": {
|
||||
"method": "GET",
|
||||
"url": "http://no.playwright/style.css",
|
||||
"httpVersion": "HTTP/1.1",
|
||||
"cookies": [],
|
||||
"headers": [
|
||||
{
|
||||
"name": "Accept",
|
||||
"value": "text/css,*/*;q=0.1"
|
||||
},
|
||||
{
|
||||
"name": "Referer",
|
||||
"value": "http://no.playwright/"
|
||||
},
|
||||
{
|
||||
"name": "User-Agent",
|
||||
"value": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.5060.33 Safari/537.36"
|
||||
}
|
||||
],
|
||||
"queryString": [],
|
||||
"headersSize": 220,
|
||||
"bodySize": 0
|
||||
},
|
||||
"response": {
|
||||
"status": 200,
|
||||
"statusText": "OK",
|
||||
"httpVersion": "HTTP/1.1",
|
||||
"cookies": [],
|
||||
"headers": [
|
||||
{
|
||||
"name": "content-length",
|
||||
"value": "24"
|
||||
},
|
||||
{
|
||||
"name": "content-type",
|
||||
"value": "text/css"
|
||||
}
|
||||
],
|
||||
"content": {
|
||||
"size": 24,
|
||||
"mimeType": "text/css",
|
||||
"compression": 0,
|
||||
"text": "body { background: red }"
|
||||
},
|
||||
"headersSize": 63,
|
||||
"bodySize": 81,
|
||||
"redirectURL": "",
|
||||
"_transferSize": 81
|
||||
},
|
||||
"cache": {
|
||||
"beforeRequest": null,
|
||||
"afterRequest": null
|
||||
},
|
||||
"timings": {
|
||||
"dns": -1,
|
||||
"connect": -1,
|
||||
"ssl": -1,
|
||||
"send": 0,
|
||||
"wait": 8.132,
|
||||
"receive": -1
|
||||
},
|
||||
"pageref": "page@b17b177f1c2e66459db3dcbe44636ffd",
|
||||
"_securityDetails": {}
|
||||
},
|
||||
{
|
||||
"_frameref": "frame@c7467fc0f1f86f09fc3b0d727a3862ea",
|
||||
"_monotonicTime": 270572175.042,
|
||||
"startedDateTime": "2022-06-10T04:27:32.175Z",
|
||||
"time": 15.997,
|
||||
"request": {
|
||||
"method": "GET",
|
||||
"url": "http://no.playwright/script.js",
|
||||
"httpVersion": "HTTP/1.1",
|
||||
"cookies": [],
|
||||
"headers": [
|
||||
{
|
||||
"name": "Accept",
|
||||
"value": "*/*"
|
||||
},
|
||||
{
|
||||
"name": "Referer",
|
||||
"value": "http://no.playwright/"
|
||||
},
|
||||
{
|
||||
"name": "User-Agent",
|
||||
"value": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.5060.33 Safari/537.36"
|
||||
}
|
||||
],
|
||||
"queryString": [],
|
||||
"headersSize": 205,
|
||||
"bodySize": 0
|
||||
},
|
||||
"response": {
|
||||
"status": 301,
|
||||
"statusText": "Moved Permanently",
|
||||
"httpVersion": "HTTP/1.1",
|
||||
"cookies": [],
|
||||
"headers": [
|
||||
{
|
||||
"name": "location",
|
||||
"value": "http://no.playwright/script2.js"
|
||||
}
|
||||
],
|
||||
"content": {
|
||||
"size": -1,
|
||||
"mimeType": "x-unknown",
|
||||
"compression": 0
|
||||
},
|
||||
"headersSize": 77,
|
||||
"bodySize": 0,
|
||||
"redirectURL": "http://no.playwright/script2.js",
|
||||
"_transferSize": 77
|
||||
},
|
||||
"cache": {
|
||||
"beforeRequest": null,
|
||||
"afterRequest": null
|
||||
},
|
||||
"timings": {
|
||||
"dns": -1,
|
||||
"connect": -1,
|
||||
"ssl": -1,
|
||||
"send": 0,
|
||||
"wait": 7.673,
|
||||
"receive": 8.324
|
||||
},
|
||||
"pageref": "page@b17b177f1c2e66459db3dcbe44636ffd",
|
||||
"_securityDetails": {}
|
||||
},
|
||||
{
|
||||
"_frameref": "frame@c7467fc0f1f86f09fc3b0d727a3862ea",
|
||||
"_monotonicTime": 270572181.822,
|
||||
"startedDateTime": "2022-06-10T04:27:32.182Z",
|
||||
"time": 6.735,
|
||||
"request": {
|
||||
"method": "GET",
|
||||
"url": "http://no.playwright/script2.js",
|
||||
"httpVersion": "HTTP/1.1",
|
||||
"cookies": [],
|
||||
"headers": [
|
||||
{
|
||||
"name": "Accept",
|
||||
"value": "*/*"
|
||||
},
|
||||
{
|
||||
"name": "Referer",
|
||||
"value": "http://no.playwright/"
|
||||
},
|
||||
{
|
||||
"name": "User-Agent",
|
||||
"value": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.5060.33 Safari/537.36"
|
||||
}
|
||||
],
|
||||
"queryString": [],
|
||||
"headersSize": 206,
|
||||
"bodySize": 0
|
||||
},
|
||||
"response": {
|
||||
"status": 200,
|
||||
"statusText": "OK",
|
||||
"httpVersion": "HTTP/1.1",
|
||||
"cookies": [],
|
||||
"headers": [
|
||||
{
|
||||
"name": "content-length",
|
||||
"value": "18"
|
||||
},
|
||||
{
|
||||
"name": "content-type",
|
||||
"value": "text/javascript"
|
||||
}
|
||||
],
|
||||
"content": {
|
||||
"size": 18,
|
||||
"mimeType": "text/javascript",
|
||||
"compression": 0,
|
||||
"text": "window.value='foo'"
|
||||
},
|
||||
"headersSize": 70,
|
||||
"bodySize": 82,
|
||||
"redirectURL": "",
|
||||
"_transferSize": 82
|
||||
},
|
||||
"cache": {
|
||||
"beforeRequest": null,
|
||||
"afterRequest": null
|
||||
},
|
||||
"timings": {
|
||||
"dns": -1,
|
||||
"connect": -1,
|
||||
"ssl": -1,
|
||||
"send": 0,
|
||||
"wait": 6.735,
|
||||
"receive": -1
|
||||
},
|
||||
"pageref": "page@b17b177f1c2e66459db3dcbe44636ffd",
|
||||
"_securityDetails": {}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
620
playwright/src/test/resources/har-redirect.har
Normal file
620
playwright/src/test/resources/har-redirect.har
Normal file
@ -0,0 +1,620 @@
|
||||
{
|
||||
"log": {
|
||||
"version": "1.2",
|
||||
"creator": {
|
||||
"name": "Playwright",
|
||||
"version": "1.23.0-next"
|
||||
},
|
||||
"browser": {
|
||||
"name": "chromium",
|
||||
"version": "103.0.5060.42"
|
||||
},
|
||||
"pages": [
|
||||
{
|
||||
"startedDateTime": "2022-06-16T21:41:23.901Z",
|
||||
"id": "page@8f314969edc000996eb5c2ab22f0e6b3",
|
||||
"title": "Microsoft",
|
||||
"pageTimings": {
|
||||
"onContentLoad": 8363,
|
||||
"onLoad": 8896
|
||||
}
|
||||
}
|
||||
],
|
||||
"entries": [
|
||||
{
|
||||
"_frameref": "frame@3767e074ecde4cb8372abba2f6f9bb4f",
|
||||
"_monotonicTime": 110928357.437,
|
||||
"startedDateTime": "2022-06-16T21:41:23.951Z",
|
||||
"time": 93.99,
|
||||
"request": {
|
||||
"method": "GET",
|
||||
"url": "https://theverge.com/",
|
||||
"httpVersion": "HTTP/2.0",
|
||||
"cookies": [],
|
||||
"headers": [
|
||||
{
|
||||
"name": ":authority",
|
||||
"value": "theverge.com"
|
||||
},
|
||||
{
|
||||
"name": ":method",
|
||||
"value": "GET"
|
||||
},
|
||||
{
|
||||
"name": ":path",
|
||||
"value": "/"
|
||||
},
|
||||
{
|
||||
"name": ":scheme",
|
||||
"value": "https"
|
||||
},
|
||||
{
|
||||
"name": "accept",
|
||||
"value": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9"
|
||||
},
|
||||
{
|
||||
"name": "accept-encoding",
|
||||
"value": "gzip, deflate, br"
|
||||
},
|
||||
{
|
||||
"name": "accept-language",
|
||||
"value": "en-US,en;q=0.9"
|
||||
},
|
||||
{
|
||||
"name": "sec-ch-ua",
|
||||
"value": "\"Chromium\";v=\"103\", \".Not/A)Brand\";v=\"99\""
|
||||
},
|
||||
{
|
||||
"name": "sec-ch-ua-mobile",
|
||||
"value": "?0"
|
||||
},
|
||||
{
|
||||
"name": "sec-ch-ua-platform",
|
||||
"value": "\"Linux\""
|
||||
},
|
||||
{
|
||||
"name": "sec-fetch-dest",
|
||||
"value": "document"
|
||||
},
|
||||
{
|
||||
"name": "sec-fetch-mode",
|
||||
"value": "navigate"
|
||||
},
|
||||
{
|
||||
"name": "sec-fetch-site",
|
||||
"value": "none"
|
||||
},
|
||||
{
|
||||
"name": "sec-fetch-user",
|
||||
"value": "?1"
|
||||
},
|
||||
{
|
||||
"name": "upgrade-insecure-requests",
|
||||
"value": "1"
|
||||
},
|
||||
{
|
||||
"name": "user-agent",
|
||||
"value": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.5060.42 Safari/537.36"
|
||||
}
|
||||
],
|
||||
"queryString": [],
|
||||
"headersSize": 644,
|
||||
"bodySize": 0
|
||||
},
|
||||
"response": {
|
||||
"status": 301,
|
||||
"statusText": "",
|
||||
"httpVersion": "HTTP/2.0",
|
||||
"cookies": [
|
||||
{
|
||||
"name": "vmidv1",
|
||||
"value": "9faf31ab-1415-4b90-b367-24b670205f41",
|
||||
"expires": "2027-06-15T21:41:24.000Z",
|
||||
"domain": "theverge.com",
|
||||
"path": "/",
|
||||
"sameSite": "Lax",
|
||||
"secure": true
|
||||
}
|
||||
],
|
||||
"headers": [
|
||||
{
|
||||
"name": "accept-ranges",
|
||||
"value": "bytes"
|
||||
},
|
||||
{
|
||||
"name": "content-length",
|
||||
"value": "0"
|
||||
},
|
||||
{
|
||||
"name": "date",
|
||||
"value": "Thu, 16 Jun 2022 21:41:24 GMT"
|
||||
},
|
||||
{
|
||||
"name": "location",
|
||||
"value": "http://www.theverge.com/"
|
||||
},
|
||||
{
|
||||
"name": "retry-after",
|
||||
"value": "0"
|
||||
},
|
||||
{
|
||||
"name": "server",
|
||||
"value": "Varnish"
|
||||
},
|
||||
{
|
||||
"name": "set-cookie",
|
||||
"value": "vmidv1=9faf31ab-1415-4b90-b367-24b670205f41;Expires=Tue, 15 Jun 2027 21:41:24 GMT;Domain=theverge.com;Path=/;SameSite=Lax;Secure"
|
||||
},
|
||||
{
|
||||
"name": "via",
|
||||
"value": "1.1 varnish"
|
||||
},
|
||||
{
|
||||
"name": "x-cache",
|
||||
"value": "HIT"
|
||||
},
|
||||
{
|
||||
"name": "x-cache-hits",
|
||||
"value": "0"
|
||||
},
|
||||
{
|
||||
"name": "x-served-by",
|
||||
"value": "cache-pao17442-PAO"
|
||||
},
|
||||
{
|
||||
"name": "x-timer",
|
||||
"value": "S1655415684.005867,VS0,VE0"
|
||||
}
|
||||
],
|
||||
"content": {
|
||||
"size": -1,
|
||||
"mimeType": "x-unknown",
|
||||
"compression": 0
|
||||
},
|
||||
"headersSize": 425,
|
||||
"bodySize": 0,
|
||||
"redirectURL": "http://www.theverge.com/",
|
||||
"_transferSize": 425
|
||||
},
|
||||
"cache": {
|
||||
"beforeRequest": null,
|
||||
"afterRequest": null
|
||||
},
|
||||
"timings": {
|
||||
"dns": 0,
|
||||
"connect": 34.151,
|
||||
"ssl": 28.074,
|
||||
"send": 0,
|
||||
"wait": 27.549,
|
||||
"receive": 4.216
|
||||
},
|
||||
"pageref": "page@8f314969edc000996eb5c2ab22f0e6b3",
|
||||
"serverIPAddress": "151.101.65.52",
|
||||
"_serverPort": 443,
|
||||
"_securityDetails": {
|
||||
"protocol": "TLS 1.2",
|
||||
"subjectName": "*.americanninjawarriornation.com",
|
||||
"issuer": "GlobalSign Atlas R3 DV TLS CA 2022 Q1",
|
||||
"validFrom": 1644853133,
|
||||
"validTo": 1679153932
|
||||
}
|
||||
},
|
||||
{
|
||||
"_frameref": "frame@3767e074ecde4cb8372abba2f6f9bb4f",
|
||||
"_monotonicTime": 110928427.603,
|
||||
"startedDateTime": "2022-06-16T21:41:24.022Z",
|
||||
"time": 44.39499999999999,
|
||||
"request": {
|
||||
"method": "GET",
|
||||
"url": "http://www.theverge.com/",
|
||||
"httpVersion": "HTTP/1.1",
|
||||
"cookies": [],
|
||||
"headers": [
|
||||
{
|
||||
"name": "Accept",
|
||||
"value": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9"
|
||||
},
|
||||
{
|
||||
"name": "Accept-Encoding",
|
||||
"value": "gzip, deflate"
|
||||
},
|
||||
{
|
||||
"name": "Accept-Language",
|
||||
"value": "en-US,en;q=0.9"
|
||||
},
|
||||
{
|
||||
"name": "Connection",
|
||||
"value": "keep-alive"
|
||||
},
|
||||
{
|
||||
"name": "Host",
|
||||
"value": "www.theverge.com"
|
||||
},
|
||||
{
|
||||
"name": "Upgrade-Insecure-Requests",
|
||||
"value": "1"
|
||||
},
|
||||
{
|
||||
"name": "User-Agent",
|
||||
"value": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.5060.42 Safari/537.36"
|
||||
}
|
||||
],
|
||||
"queryString": [],
|
||||
"headersSize": 423,
|
||||
"bodySize": 0
|
||||
},
|
||||
"response": {
|
||||
"status": 301,
|
||||
"statusText": "Moved Permanently",
|
||||
"httpVersion": "HTTP/1.1",
|
||||
"cookies": [
|
||||
{
|
||||
"name": "_chorus_geoip_continent",
|
||||
"value": "NA"
|
||||
},
|
||||
{
|
||||
"name": "vmidv1",
|
||||
"value": "4e0c1265-10f8-4cb1-a5de-1c3cf70b531c",
|
||||
"expires": "2027-06-15T21:41:24.000Z",
|
||||
"domain": "www.theverge.com",
|
||||
"path": "/",
|
||||
"sameSite": "Lax",
|
||||
"secure": true
|
||||
}
|
||||
],
|
||||
"headers": [
|
||||
{
|
||||
"name": "Accept-Ranges",
|
||||
"value": "bytes"
|
||||
},
|
||||
{
|
||||
"name": "Age",
|
||||
"value": "2615"
|
||||
},
|
||||
{
|
||||
"name": "Connection",
|
||||
"value": "keep-alive"
|
||||
},
|
||||
{
|
||||
"name": "Content-Length",
|
||||
"value": "0"
|
||||
},
|
||||
{
|
||||
"name": "Content-Type",
|
||||
"value": "text/html"
|
||||
},
|
||||
{
|
||||
"name": "Date",
|
||||
"value": "Thu, 16 Jun 2022 21:41:24 GMT"
|
||||
},
|
||||
{
|
||||
"name": "Location",
|
||||
"value": "https://www.theverge.com/"
|
||||
},
|
||||
{
|
||||
"name": "Server",
|
||||
"value": "nginx"
|
||||
},
|
||||
{
|
||||
"name": "Set-Cookie",
|
||||
"value": "_chorus_geoip_continent=NA; expires=Fri, 17 Jun 2022 21:41:24 GMT; path=/;"
|
||||
},
|
||||
{
|
||||
"name": "Set-Cookie",
|
||||
"value": "vmidv1=4e0c1265-10f8-4cb1-a5de-1c3cf70b531c;Expires=Tue, 15 Jun 2027 21:41:24 GMT;Domain=www.theverge.com;Path=/;SameSite=Lax;Secure"
|
||||
},
|
||||
{
|
||||
"name": "Vary",
|
||||
"value": "X-Forwarded-Proto, Cookie, X-Chorus-Unison-Testing, X-Chorus-Require-Privacy-Consent, X-Chorus-Restrict-In-Privacy-Consent-Region, Accept-Encoding"
|
||||
},
|
||||
{
|
||||
"name": "Via",
|
||||
"value": "1.1 varnish"
|
||||
},
|
||||
{
|
||||
"name": "X-Cache",
|
||||
"value": "HIT"
|
||||
},
|
||||
{
|
||||
"name": "X-Cache-Hits",
|
||||
"value": "2"
|
||||
},
|
||||
{
|
||||
"name": "X-Served-By",
|
||||
"value": "cache-pao17450-PAO"
|
||||
},
|
||||
{
|
||||
"name": "X-Timer",
|
||||
"value": "S1655415684.035748,VS0,VE0"
|
||||
}
|
||||
],
|
||||
"content": {
|
||||
"size": -1,
|
||||
"mimeType": "text/html",
|
||||
"compression": 0
|
||||
},
|
||||
"headersSize": 731,
|
||||
"bodySize": 0,
|
||||
"redirectURL": "https://www.theverge.com/",
|
||||
"_transferSize": 731
|
||||
},
|
||||
"cache": {
|
||||
"beforeRequest": null,
|
||||
"afterRequest": null
|
||||
},
|
||||
"timings": {
|
||||
"dns": 2.742,
|
||||
"connect": 10.03,
|
||||
"ssl": 14.123,
|
||||
"send": 0,
|
||||
"wait": 15.023,
|
||||
"receive": 2.477
|
||||
},
|
||||
"pageref": "page@8f314969edc000996eb5c2ab22f0e6b3",
|
||||
"serverIPAddress": "151.101.189.52",
|
||||
"_serverPort": 80,
|
||||
"_securityDetails": {}
|
||||
},
|
||||
{
|
||||
"_frameref": "frame@3767e074ecde4cb8372abba2f6f9bb4f",
|
||||
"_monotonicTime": 110928455.901,
|
||||
"startedDateTime": "2022-06-16T21:41:24.050Z",
|
||||
"time": 50.29199999999999,
|
||||
"request": {
|
||||
"method": "GET",
|
||||
"url": "https://www.theverge.com/",
|
||||
"httpVersion": "HTTP/2.0",
|
||||
"cookies": [
|
||||
{
|
||||
"name": "vmidv1",
|
||||
"value": "9faf31ab-1415-4b90-b367-24b670205f41"
|
||||
},
|
||||
{
|
||||
"name": "_chorus_geoip_continent",
|
||||
"value": "NA"
|
||||
}
|
||||
],
|
||||
"headers": [
|
||||
{
|
||||
"name": ":authority",
|
||||
"value": "www.theverge.com"
|
||||
},
|
||||
{
|
||||
"name": ":method",
|
||||
"value": "GET"
|
||||
},
|
||||
{
|
||||
"name": ":path",
|
||||
"value": "/"
|
||||
},
|
||||
{
|
||||
"name": ":scheme",
|
||||
"value": "https"
|
||||
},
|
||||
{
|
||||
"name": "accept",
|
||||
"value": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9"
|
||||
},
|
||||
{
|
||||
"name": "accept-encoding",
|
||||
"value": "gzip, deflate, br"
|
||||
},
|
||||
{
|
||||
"name": "accept-language",
|
||||
"value": "en-US,en;q=0.9"
|
||||
},
|
||||
{
|
||||
"name": "cookie",
|
||||
"value": "vmidv1=9faf31ab-1415-4b90-b367-24b670205f41; _chorus_geoip_continent=NA"
|
||||
},
|
||||
{
|
||||
"name": "sec-ch-ua",
|
||||
"value": "\"Chromium\";v=\"103\", \".Not/A)Brand\";v=\"99\""
|
||||
},
|
||||
{
|
||||
"name": "sec-ch-ua-mobile",
|
||||
"value": "?0"
|
||||
},
|
||||
{
|
||||
"name": "sec-ch-ua-platform",
|
||||
"value": "\"Linux\""
|
||||
},
|
||||
{
|
||||
"name": "sec-fetch-dest",
|
||||
"value": "document"
|
||||
},
|
||||
{
|
||||
"name": "sec-fetch-mode",
|
||||
"value": "navigate"
|
||||
},
|
||||
{
|
||||
"name": "sec-fetch-site",
|
||||
"value": "none"
|
||||
},
|
||||
{
|
||||
"name": "sec-fetch-user",
|
||||
"value": "?1"
|
||||
},
|
||||
{
|
||||
"name": "upgrade-insecure-requests",
|
||||
"value": "1"
|
||||
},
|
||||
{
|
||||
"name": "user-agent",
|
||||
"value": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.5060.42 Safari/537.36"
|
||||
}
|
||||
],
|
||||
"queryString": [],
|
||||
"headersSize": 729,
|
||||
"bodySize": 0
|
||||
},
|
||||
"response": {
|
||||
"status": 200,
|
||||
"statusText": "",
|
||||
"httpVersion": "HTTP/2.0",
|
||||
"cookies": [
|
||||
{
|
||||
"name": "_chorus_geoip_continent",
|
||||
"value": "NA"
|
||||
},
|
||||
{
|
||||
"name": "vmidv1",
|
||||
"value": "40d8fd14-5ac3-4757-9e9c-efb106e82d3a",
|
||||
"expires": "2027-06-15T21:41:24.000Z",
|
||||
"domain": "www.theverge.com",
|
||||
"path": "/",
|
||||
"sameSite": "Lax",
|
||||
"secure": true
|
||||
}
|
||||
],
|
||||
"headers": [
|
||||
{
|
||||
"name": "accept-ranges",
|
||||
"value": "bytes"
|
||||
},
|
||||
{
|
||||
"name": "age",
|
||||
"value": "263"
|
||||
},
|
||||
{
|
||||
"name": "cache-control",
|
||||
"value": "max-age=0, public, must-revalidate"
|
||||
},
|
||||
{
|
||||
"name": "content-encoding",
|
||||
"value": "br"
|
||||
},
|
||||
{
|
||||
"name": "content-length",
|
||||
"value": "14"
|
||||
},
|
||||
{
|
||||
"name": "content-security-policy",
|
||||
"value": "default-src https: data: 'unsafe-inline' 'unsafe-eval'; child-src https: data: blob:; connect-src https: data: blob: ; font-src https: data:; img-src https: data: blob:; media-src https: data: blob:; object-src https:; script-src https: data: blob: 'unsafe-inline' 'unsafe-eval'; style-src https: 'unsafe-inline'; block-all-mixed-content; upgrade-insecure-requests"
|
||||
},
|
||||
{
|
||||
"name": "content-type",
|
||||
"value": "text/html; charset=utf-8"
|
||||
},
|
||||
{
|
||||
"name": "date",
|
||||
"value": "Thu, 16 Jun 2022 21:41:24 GMT"
|
||||
},
|
||||
{
|
||||
"name": "etag",
|
||||
"value": "W/\"d498ef668223d015000070a66a181e85\""
|
||||
},
|
||||
{
|
||||
"name": "link",
|
||||
"value": "<https://concertads-configs.vox-cdn.com/sbn/verge/config.json>; rel=preload; as=fetch; crossorigin"
|
||||
},
|
||||
{
|
||||
"name": "referrer-policy",
|
||||
"value": "strict-origin-when-cross-origin"
|
||||
},
|
||||
{
|
||||
"name": "server",
|
||||
"value": "nginx"
|
||||
},
|
||||
{
|
||||
"name": "set-cookie",
|
||||
"value": "_chorus_geoip_continent=NA; expires=Fri, 17 Jun 2022 21:41:24 GMT; path=/;"
|
||||
},
|
||||
{
|
||||
"name": "set-cookie",
|
||||
"value": "vmidv1=40d8fd14-5ac3-4757-9e9c-efb106e82d3a;Expires=Tue, 15 Jun 2027 21:41:24 GMT;Domain=www.theverge.com;Path=/;SameSite=Lax;Secure"
|
||||
},
|
||||
{
|
||||
"name": "strict-transport-security",
|
||||
"value": "max-age=31556952; preload"
|
||||
},
|
||||
{
|
||||
"name": "vary",
|
||||
"value": "Accept-Encoding, X-Chorus-Unison-Testing, X-Chorus-Require-Privacy-Consent, X-Chorus-Restrict-In-Privacy-Consent-Region, Origin, X-Forwarded-Proto, Cookie, X-Chorus-Unison-Testing, X-Chorus-Require-Privacy-Consent, X-Chorus-Restrict-In-Privacy-Consent-Region"
|
||||
},
|
||||
{
|
||||
"name": "via",
|
||||
"value": "1.1 varnish"
|
||||
},
|
||||
{
|
||||
"name": "x-cache",
|
||||
"value": "HIT"
|
||||
},
|
||||
{
|
||||
"name": "x-cache-hits",
|
||||
"value": "1"
|
||||
},
|
||||
{
|
||||
"name": "x-content-type-options",
|
||||
"value": "nosniff"
|
||||
},
|
||||
{
|
||||
"name": "x-download-options",
|
||||
"value": "noopen"
|
||||
},
|
||||
{
|
||||
"name": "x-frame-options",
|
||||
"value": "SAMEORIGIN"
|
||||
},
|
||||
{
|
||||
"name": "x-permitted-cross-domain-policies",
|
||||
"value": "none"
|
||||
},
|
||||
{
|
||||
"name": "x-request-id",
|
||||
"value": "97363ad70e272e63641c0bb784fa06a01b848dfd"
|
||||
},
|
||||
{
|
||||
"name": "x-runtime",
|
||||
"value": "0.257911"
|
||||
},
|
||||
{
|
||||
"name": "x-served-by",
|
||||
"value": "cache-pao17436-PAO"
|
||||
},
|
||||
{
|
||||
"name": "x-timer",
|
||||
"value": "S1655415684.075077,VS0,VE1"
|
||||
},
|
||||
{
|
||||
"name": "x-xss-protection",
|
||||
"value": "1; mode=block"
|
||||
}
|
||||
],
|
||||
"content": {
|
||||
"size": 14,
|
||||
"mimeType": "text/html",
|
||||
"compression": 0,
|
||||
"text": "<h1>hello</h1>"
|
||||
},
|
||||
"headersSize": 1742,
|
||||
"bodySize": 48716,
|
||||
"redirectURL": "",
|
||||
"_transferSize": 48716
|
||||
},
|
||||
"cache": {
|
||||
"beforeRequest": null,
|
||||
"afterRequest": null
|
||||
},
|
||||
"timings": {
|
||||
"dns": 0.016,
|
||||
"connect": 24.487,
|
||||
"ssl": 17.406,
|
||||
"send": 0,
|
||||
"wait": 8.383,
|
||||
"receive": -1
|
||||
},
|
||||
"pageref": "page@8f314969edc000996eb5c2ab22f0e6b3",
|
||||
"serverIPAddress": "151.101.189.52",
|
||||
"_serverPort": 443,
|
||||
"_securityDetails": {
|
||||
"protocol": "TLS 1.2",
|
||||
"subjectName": "*.americanninjawarriornation.com",
|
||||
"issuer": "GlobalSign Atlas R3 DV TLS CA 2022 Q1",
|
||||
"validFrom": 1644853133,
|
||||
"validTo": 1679153932
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
1
playwright/src/test/resources/har-sha1-main-response.txt
Normal file
1
playwright/src/test/resources/har-sha1-main-response.txt
Normal file
@ -0,0 +1 @@
|
||||
Hello, world
|
||||
95
playwright/src/test/resources/har-sha1.har
Normal file
95
playwright/src/test/resources/har-sha1.har
Normal file
@ -0,0 +1,95 @@
|
||||
{
|
||||
"log": {
|
||||
"version": "1.2",
|
||||
"creator": {
|
||||
"name": "Playwright",
|
||||
"version": "1.23.0-next"
|
||||
},
|
||||
"browser": {
|
||||
"name": "chromium",
|
||||
"version": "103.0.5060.33"
|
||||
},
|
||||
"pages": [
|
||||
{
|
||||
"startedDateTime": "2022-06-10T04:27:32.125Z",
|
||||
"id": "page@b17b177f1c2e66459db3dcbe44636ffd",
|
||||
"title": "Hey",
|
||||
"pageTimings": {
|
||||
"onContentLoad": 70,
|
||||
"onLoad": 70
|
||||
}
|
||||
}
|
||||
],
|
||||
"entries": [
|
||||
{
|
||||
"_frameref": "frame@c7467fc0f1f86f09fc3b0d727a3862ea",
|
||||
"_monotonicTime": 270572145.898,
|
||||
"startedDateTime": "2022-06-10T04:27:32.146Z",
|
||||
"time": 8.286,
|
||||
"request": {
|
||||
"method": "GET",
|
||||
"url": "http://no.playwright/",
|
||||
"httpVersion": "HTTP/1.1",
|
||||
"cookies": [],
|
||||
"headers": [
|
||||
{
|
||||
"name": "Accept",
|
||||
"value": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9"
|
||||
},
|
||||
{
|
||||
"name": "Upgrade-Insecure-Requests",
|
||||
"value": "1"
|
||||
},
|
||||
{
|
||||
"name": "User-Agent",
|
||||
"value": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.5060.33 Safari/537.36"
|
||||
}
|
||||
],
|
||||
"queryString": [],
|
||||
"headersSize": 326,
|
||||
"bodySize": 0
|
||||
},
|
||||
"response": {
|
||||
"status": 200,
|
||||
"statusText": "OK",
|
||||
"httpVersion": "HTTP/1.1",
|
||||
"cookies": [],
|
||||
"headers": [
|
||||
{
|
||||
"name": "content-length",
|
||||
"value": "12"
|
||||
},
|
||||
{
|
||||
"name": "content-type",
|
||||
"value": "text/html"
|
||||
}
|
||||
],
|
||||
"content": {
|
||||
"size": 12,
|
||||
"mimeType": "text/html",
|
||||
"compression": 0,
|
||||
"_file": "har-sha1-main-response.txt"
|
||||
},
|
||||
"headersSize": 64,
|
||||
"bodySize": 71,
|
||||
"redirectURL": "",
|
||||
"_transferSize": 71
|
||||
},
|
||||
"cache": {
|
||||
"beforeRequest": null,
|
||||
"afterRequest": null
|
||||
},
|
||||
"timings": {
|
||||
"dns": -1,
|
||||
"connect": -1,
|
||||
"ssl": -1,
|
||||
"send": 0,
|
||||
"wait": 8.286,
|
||||
"receive": -1
|
||||
},
|
||||
"pageref": "page@b17b177f1c2e66459db3dcbe44636ffd",
|
||||
"_securityDetails": {}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@ -1 +1 @@
|
||||
1.23.0-beta-1655926399000
|
||||
1.23.0-beta-1656035897000
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user