Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a127bfefb3 | |||
| 06082438fe | |||
| b8bd59a55c | |||
| e42d7bdc9a |
@@ -31,18 +31,14 @@ jobs:
|
|||||||
- name: Build & Install
|
- name: Build & Install
|
||||||
run: mvn -B install -D skipTests --no-transfer-progress
|
run: mvn -B install -D skipTests --no-transfer-progress
|
||||||
- name: Run tests
|
- name: Run tests
|
||||||
run: mvn test -DexcludedGroups=isolated --no-transfer-progress --fail-at-end
|
run: mvn test --no-transfer-progress --fail-at-end
|
||||||
env:
|
env:
|
||||||
BROWSER: ${{ matrix.browser }}
|
BROWSER: ${{ matrix.browser }}
|
||||||
- name: Run tracing tests w/ sources
|
- name: Run tracing tests w/ sources
|
||||||
run: mvn test -DexcludedGroups=isolated --no-transfer-progress --fail-at-end -D test=*TestTracing*
|
run: mvn test --no-transfer-progress --fail-at-end -D test=*TestTracing*
|
||||||
env:
|
env:
|
||||||
BROWSER: ${{ matrix.browser }}
|
BROWSER: ${{ matrix.browser }}
|
||||||
PLAYWRIGHT_JAVA_SRC: src/test/java
|
PLAYWRIGHT_JAVA_SRC: src/test/java
|
||||||
- name: Run driver throw tests
|
|
||||||
run: mvn test -Dgroups=driverThrowTest --no-transfer-progress --fail-at-end
|
|
||||||
env:
|
|
||||||
BROWSER: ${{ matrix.browser }}
|
|
||||||
- name: Test Spring Boot Starter
|
- name: Test Spring Boot Starter
|
||||||
shell: bash
|
shell: bash
|
||||||
env:
|
env:
|
||||||
@@ -83,12 +79,7 @@ jobs:
|
|||||||
- name: Build & Install
|
- name: Build & Install
|
||||||
run: mvn -B install -D skipTests --no-transfer-progress
|
run: mvn -B install -D skipTests --no-transfer-progress
|
||||||
- name: Run tests
|
- name: Run tests
|
||||||
run: mvn test -DexcludedGroups=isolated --no-transfer-progress --fail-at-end
|
run: mvn test --no-transfer-progress --fail-at-end
|
||||||
env:
|
|
||||||
BROWSER: chromium
|
|
||||||
BROWSER_CHANNEL: ${{ matrix.browser-channel }}
|
|
||||||
- name: Run driver throw tests
|
|
||||||
run: mvn test -Dgroups=driverThrowTest --no-transfer-progress --fail-at-end
|
|
||||||
env:
|
env:
|
||||||
BROWSER: chromium
|
BROWSER: chromium
|
||||||
BROWSER_CHANNEL: ${{ matrix.browser-channel }}
|
BROWSER_CHANNEL: ${{ matrix.browser-channel }}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>com.microsoft.playwright</groupId>
|
<groupId>com.microsoft.playwright</groupId>
|
||||||
<artifactId>parent-pom</artifactId>
|
<artifactId>parent-pom</artifactId>
|
||||||
<version>1.23.0-SNAPSHOT</version>
|
<version>1.23.0</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>driver-bundle</artifactId>
|
<artifactId>driver-bundle</artifactId>
|
||||||
|
|||||||
@@ -18,9 +18,14 @@ package com.microsoft.playwright;
|
|||||||
|
|
||||||
import com.microsoft.playwright.impl.Driver;
|
import com.microsoft.playwright.impl.Driver;
|
||||||
import com.microsoft.playwright.impl.DriverJar;
|
import com.microsoft.playwright.impl.DriverJar;
|
||||||
import org.junit.jupiter.api.*;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.api.io.TempDir;
|
import org.junit.jupiter.api.io.TempDir;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.net.MalformedURLException;
|
||||||
|
import java.net.ServerSocket;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
@@ -28,12 +33,26 @@ import java.util.HashMap;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
|
||||||
|
|
||||||
public class TestInstall {
|
public class TestInstall {
|
||||||
|
private static boolean isPortAvailable(int port) {
|
||||||
|
try (ServerSocket ignored = new ServerSocket(port)) {
|
||||||
|
return true;
|
||||||
|
} catch (IOException ignored) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int unusedPort() {
|
||||||
|
for (int i = 10000; i < 11000; i++) {
|
||||||
|
if (isPortAvailable(i)) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new RuntimeException("Cannot find unused local port");
|
||||||
|
}
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
void clearSystemProperties() {
|
void clearSystemProperties() {
|
||||||
// Clear system property to ensure that the driver is loaded from jar.
|
// Clear system property to ensure that the driver is loaded from jar.
|
||||||
@@ -44,16 +63,29 @@ public class TestInstall {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Tags({@Tag("isolated"), @Tag("driverThrowTest")})
|
void shouldThrowWhenBrowserPathIsInvalid(@TempDir Path tmpDir) throws MalformedURLException, ClassNotFoundException, NoSuchMethodException, NoSuchFieldException, IllegalAccessException {
|
||||||
void shouldThrowWhenBrowserPathIsInvalid(@TempDir Path tmpDir) {
|
|
||||||
Map<String,String> env = new HashMap<>();
|
Map<String,String> env = new HashMap<>();
|
||||||
env.put("PLAYWRIGHT_DOWNLOAD_HOST", "https://127.0.0.127");
|
|
||||||
|
// On macOS we can only use 127.0.0.1, so pick unused port instead.
|
||||||
|
// https://superuser.com/questions/458875/how-do-you-get-loopback-addresses-other-than-127-0-0-1-to-work-on-os-x
|
||||||
|
env.put("PLAYWRIGHT_DOWNLOAD_HOST", "https://127.0.0.1:" + unusedPort());
|
||||||
// Make sure the browsers are not installed yet by pointing at an empty dir.
|
// Make sure the browsers are not installed yet by pointing at an empty dir.
|
||||||
env.put("PLAYWRIGHT_BROWSERS_PATH", tmpDir.toString());
|
env.put("PLAYWRIGHT_BROWSERS_PATH", tmpDir.toString());
|
||||||
env.put("PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD", "false");
|
env.put("PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD", "false");
|
||||||
|
|
||||||
assertThrows(RuntimeException.class, () -> Driver.ensureDriverInstalled(env, true));
|
// Reset instance field value to null for the test.
|
||||||
assertThrows(RuntimeException.class, () -> Driver.ensureDriverInstalled(env, true));
|
Field field = Driver.class.getDeclaredField("instance");
|
||||||
|
field.setAccessible(true);
|
||||||
|
Object value = field.get(Driver.class);
|
||||||
|
field.set(Driver.class, null);
|
||||||
|
|
||||||
|
for (int i = 0; i < 2; i++){
|
||||||
|
RuntimeException exception = assertThrows(RuntimeException.class, () -> Driver.ensureDriverInstalled(env, true));
|
||||||
|
String message = exception.getMessage();
|
||||||
|
assertTrue(message.contains("Failed to create driver"), message);
|
||||||
|
}
|
||||||
|
|
||||||
|
field.set(Driver.class, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
+1
-1
@@ -6,7 +6,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>com.microsoft.playwright</groupId>
|
<groupId>com.microsoft.playwright</groupId>
|
||||||
<artifactId>parent-pom</artifactId>
|
<artifactId>parent-pom</artifactId>
|
||||||
<version>1.23.0-SNAPSHOT</version>
|
<version>1.23.0</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>driver</artifactId>
|
<artifactId>driver</artifactId>
|
||||||
|
|||||||
+1
-1
@@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
<groupId>org.example</groupId>
|
<groupId>org.example</groupId>
|
||||||
<artifactId>examples</artifactId>
|
<artifactId>examples</artifactId>
|
||||||
<version>1.23.0-SNAPSHOT</version>
|
<version>1.23.0</version>
|
||||||
<name>Playwright Client Examples</name>
|
<name>Playwright Client Examples</name>
|
||||||
<properties>
|
<properties>
|
||||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
|
|||||||
+1
-1
@@ -7,7 +7,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>com.microsoft.playwright</groupId>
|
<groupId>com.microsoft.playwright</groupId>
|
||||||
<artifactId>parent-pom</artifactId>
|
<artifactId>parent-pom</artifactId>
|
||||||
<version>1.23.0-SNAPSHOT</version>
|
<version>1.23.0</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>playwright</artifactId>
|
<artifactId>playwright</artifactId>
|
||||||
|
|||||||
@@ -184,6 +184,10 @@ public interface BrowserContext extends AutoCloseable {
|
|||||||
* <p> Defaults to abort.
|
* <p> Defaults to abort.
|
||||||
*/
|
*/
|
||||||
public HarNotFound notFound;
|
public HarNotFound notFound;
|
||||||
|
/**
|
||||||
|
* If specified, updates the given HAR with the actual network information instead of serving from file.
|
||||||
|
*/
|
||||||
|
public Boolean update;
|
||||||
/**
|
/**
|
||||||
* A glob pattern, regular expression or predicate to match the request URL. Only requests with URL matching the pattern
|
* A glob pattern, regular expression or predicate to match the request URL. Only requests with URL matching the pattern
|
||||||
* will be surved from the HAR file. If not specified, all requests are served from the HAR file.
|
* will be surved from the HAR file. If not specified, all requests are served from the HAR file.
|
||||||
@@ -202,6 +206,13 @@ public interface BrowserContext extends AutoCloseable {
|
|||||||
this.notFound = notFound;
|
this.notFound = notFound;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* If specified, updates the given HAR with the actual network information instead of serving from file.
|
||||||
|
*/
|
||||||
|
public RouteFromHAROptions setUpdate(boolean update) {
|
||||||
|
this.update = update;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* A glob pattern, regular expression or predicate to match the request URL. Only requests with URL matching the pattern
|
* A glob pattern, regular expression or predicate to match the request URL. Only requests with URL matching the pattern
|
||||||
* will be surved from the HAR file. If not specified, all requests are served from the HAR file.
|
* will be surved from the HAR file. If not specified, all requests are served from the HAR file.
|
||||||
@@ -218,14 +229,6 @@ public interface BrowserContext extends AutoCloseable {
|
|||||||
this.url = url;
|
this.url = url;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
/**
|
|
||||||
* A glob pattern, regular expression or predicate to match the request URL. Only requests with URL matching the pattern
|
|
||||||
* will be surved from the HAR file. If not specified, all requests are served from the HAR file.
|
|
||||||
*/
|
|
||||||
public RouteFromHAROptions setUrl(Predicate<String> url) {
|
|
||||||
this.url = url;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
class StorageStateOptions {
|
class StorageStateOptions {
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -2007,6 +2007,10 @@ public interface Page extends AutoCloseable {
|
|||||||
* <p> Defaults to abort.
|
* <p> Defaults to abort.
|
||||||
*/
|
*/
|
||||||
public HarNotFound notFound;
|
public HarNotFound notFound;
|
||||||
|
/**
|
||||||
|
* If specified, updates the given HAR with the actual network information instead of serving from file.
|
||||||
|
*/
|
||||||
|
public Boolean update;
|
||||||
/**
|
/**
|
||||||
* A glob pattern, regular expression or predicate to match the request URL. Only requests with URL matching the pattern
|
* A glob pattern, regular expression or predicate to match the request URL. Only requests with URL matching the pattern
|
||||||
* will be surved from the HAR file. If not specified, all requests are served from the HAR file.
|
* will be surved from the HAR file. If not specified, all requests are served from the HAR file.
|
||||||
@@ -2025,6 +2029,13 @@ public interface Page extends AutoCloseable {
|
|||||||
this.notFound = notFound;
|
this.notFound = notFound;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* If specified, updates the given HAR with the actual network information instead of serving from file.
|
||||||
|
*/
|
||||||
|
public RouteFromHAROptions setUpdate(boolean update) {
|
||||||
|
this.update = update;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* A glob pattern, regular expression or predicate to match the request URL. Only requests with URL matching the pattern
|
* A glob pattern, regular expression or predicate to match the request URL. Only requests with URL matching the pattern
|
||||||
* will be surved from the HAR file. If not specified, all requests are served from the HAR file.
|
* will be surved from the HAR file. If not specified, all requests are served from the HAR file.
|
||||||
@@ -2041,14 +2052,6 @@ public interface Page extends AutoCloseable {
|
|||||||
this.url = url;
|
this.url = url;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
/**
|
|
||||||
* A glob pattern, regular expression or predicate to match the request URL. Only requests with URL matching the pattern
|
|
||||||
* will be surved from the HAR file. If not specified, all requests are served from the HAR file.
|
|
||||||
*/
|
|
||||||
public RouteFromHAROptions setUrl(Predicate<String> url) {
|
|
||||||
this.url = url;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
class ScreenshotOptions {
|
class ScreenshotOptions {
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -27,13 +27,16 @@ import java.net.MalformedURLException;
|
|||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
import static com.microsoft.playwright.impl.Serialization.addHarUrlFilter;
|
||||||
import static com.microsoft.playwright.impl.Serialization.gson;
|
import static com.microsoft.playwright.impl.Serialization.gson;
|
||||||
import static com.microsoft.playwright.impl.Utils.isSafeCloseError;
|
import static com.microsoft.playwright.impl.Utils.isSafeCloseError;
|
||||||
|
import static com.microsoft.playwright.impl.Utils.toJsRegexFlags;
|
||||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||||
import static java.nio.file.Files.readAllBytes;
|
import static java.nio.file.Files.readAllBytes;
|
||||||
import static java.util.Arrays.asList;
|
import static java.util.Arrays.asList;
|
||||||
@@ -51,7 +54,17 @@ class BrowserContextImpl extends ChannelOwner implements BrowserContext {
|
|||||||
final TimeoutSettings timeoutSettings = new TimeoutSettings();
|
final TimeoutSettings timeoutSettings = new TimeoutSettings();
|
||||||
Path videosDir;
|
Path videosDir;
|
||||||
URL baseUrl;
|
URL baseUrl;
|
||||||
Path recordHarPath;
|
final Map<String, HarRecorder> harRecorders = new HashMap<>();
|
||||||
|
|
||||||
|
static class HarRecorder {
|
||||||
|
final Path path;
|
||||||
|
final HarContentPolicy contentPolicy;
|
||||||
|
|
||||||
|
HarRecorder(Path har, HarContentPolicy policy) {
|
||||||
|
path = har;
|
||||||
|
contentPolicy = policy;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
enum EventType {
|
enum EventType {
|
||||||
CLOSE,
|
CLOSE,
|
||||||
@@ -74,6 +87,12 @@ class BrowserContextImpl extends ChannelOwner implements BrowserContext {
|
|||||||
this.request = connection.getExistingObject(initializer.getAsJsonObject("APIRequestContext").get("guid").getAsString());
|
this.request = connection.getExistingObject(initializer.getAsJsonObject("APIRequestContext").get("guid").getAsString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void setRecordHar(Path path, HarContentPolicy policy) {
|
||||||
|
if (path != null) {
|
||||||
|
harRecorders.put("", new HarRecorder(path, policy));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void setBaseUrl(String spec) {
|
void setBaseUrl(String spec) {
|
||||||
try {
|
try {
|
||||||
this.baseUrl = new URL(spec);
|
this.baseUrl = new URL(spec);
|
||||||
@@ -178,15 +197,31 @@ class BrowserContextImpl extends ChannelOwner implements BrowserContext {
|
|||||||
}
|
}
|
||||||
isClosedOrClosing = true;
|
isClosedOrClosing = true;
|
||||||
try {
|
try {
|
||||||
if (recordHarPath != null) {
|
for (Map.Entry<String, HarRecorder> entry : harRecorders.entrySet()) {
|
||||||
JsonObject json = sendMessage("harExport").getAsJsonObject();
|
JsonObject params = new JsonObject();
|
||||||
|
params.addProperty("harId", entry.getKey());
|
||||||
|
JsonObject json = sendMessage("harExport", params).getAsJsonObject();
|
||||||
ArtifactImpl artifact = connection.getExistingObject(json.getAsJsonObject("artifact").get("guid").getAsString());
|
ArtifactImpl artifact = connection.getExistingObject(json.getAsJsonObject("artifact").get("guid").getAsString());
|
||||||
// In case of CDP connection browser is null but since the connection is established by
|
// In case of CDP connection browser is null but since the connection is established by
|
||||||
// the driver it is safe to consider the artifact local.
|
// the driver it is safe to consider the artifact local.
|
||||||
if (browser() != null && browser().isRemote) {
|
if (browser() != null && browser().isRemote) {
|
||||||
artifact.isRemote = true;
|
artifact.isRemote = true;
|
||||||
}
|
}
|
||||||
artifact.saveAs(recordHarPath);
|
|
||||||
|
// Server side will compress artifact if content is attach or if file is .zip.
|
||||||
|
HarRecorder harParams = entry.getValue();
|
||||||
|
boolean isCompressed = harParams.contentPolicy == HarContentPolicy.ATTACH || harParams.path.toString().endsWith(".zip");
|
||||||
|
boolean needCompressed = harParams.path.toString().endsWith(".zip");
|
||||||
|
if (isCompressed && !needCompressed) {
|
||||||
|
String tmpPath = harParams.path + ".tmp";
|
||||||
|
artifact.saveAs(Paths.get(tmpPath));
|
||||||
|
JsonObject unzipParams = new JsonObject();
|
||||||
|
unzipParams.addProperty("zipFile", tmpPath);
|
||||||
|
unzipParams.addProperty("harFile", harParams.path.toString());
|
||||||
|
connection.localUtils.sendMessage("harUnzip", unzipParams);
|
||||||
|
} else {
|
||||||
|
artifact.saveAs(harParams.path);
|
||||||
|
}
|
||||||
artifact.delete();
|
artifact.delete();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -351,6 +386,10 @@ class BrowserContextImpl extends ChannelOwner implements BrowserContext {
|
|||||||
if (options == null) {
|
if (options == null) {
|
||||||
options = new RouteFromHAROptions();
|
options = new RouteFromHAROptions();
|
||||||
}
|
}
|
||||||
|
if (options.update != null && options.update) {
|
||||||
|
recordIntoHar(null, har, options);
|
||||||
|
return;
|
||||||
|
}
|
||||||
UrlMatcher matcher = UrlMatcher.forOneOf(baseUrl, options.url);
|
UrlMatcher matcher = UrlMatcher.forOneOf(baseUrl, options.url);
|
||||||
HARRouter harRouter = new HARRouter(connection.localUtils, har, options.notFound);
|
HARRouter harRouter = new HARRouter(connection.localUtils, har, options.notFound);
|
||||||
onClose(context -> harRouter.dispose());
|
onClose(context -> harRouter.dispose());
|
||||||
@@ -368,6 +407,22 @@ class BrowserContextImpl extends ChannelOwner implements BrowserContext {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void recordIntoHar(PageImpl page, Path har, RouteFromHAROptions options) {
|
||||||
|
JsonObject params = new JsonObject();
|
||||||
|
if (page != null) {
|
||||||
|
params.add("page", page.toProtocolRef());
|
||||||
|
}
|
||||||
|
JsonObject jsonOptions = new JsonObject();
|
||||||
|
jsonOptions.addProperty("path", har.toAbsolutePath().toString());
|
||||||
|
jsonOptions.addProperty("content", HarContentPolicy.ATTACH.name().toLowerCase());
|
||||||
|
jsonOptions.addProperty("mode", HarMode.MINIMAL.name().toLowerCase());
|
||||||
|
addHarUrlFilter(jsonOptions, options.url);
|
||||||
|
params.add("options", jsonOptions);
|
||||||
|
JsonObject json = sendMessage("harStart", params).getAsJsonObject();
|
||||||
|
String harId = json.get("harId").getAsString();
|
||||||
|
harRecorders.put(harId, new HarRecorder(har, HarContentPolicy.ATTACH));
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setDefaultNavigationTimeout(double timeout) {
|
public void setDefaultNavigationTimeout(double timeout) {
|
||||||
withLogging("BrowserContext.setDefaultNavigationTimeout", () -> {
|
withLogging("BrowserContext.setDefaultNavigationTimeout", () -> {
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ import java.util.*;
|
|||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
import static com.microsoft.playwright.impl.Serialization.addHarUrlFilter;
|
||||||
import static com.microsoft.playwright.impl.Serialization.gson;
|
import static com.microsoft.playwright.impl.Serialization.gson;
|
||||||
import static com.microsoft.playwright.impl.Utils.*;
|
import static com.microsoft.playwright.impl.Utils.*;
|
||||||
import static com.microsoft.playwright.impl.Utils.convertType;
|
import static com.microsoft.playwright.impl.Utils.convertType;
|
||||||
@@ -137,24 +138,22 @@ class BrowserImpl extends ChannelOwner implements Browser {
|
|||||||
}
|
}
|
||||||
JsonObject recordHar = null;
|
JsonObject recordHar = null;
|
||||||
Path recordHarPath = options.recordHarPath;
|
Path recordHarPath = options.recordHarPath;
|
||||||
|
HarContentPolicy harContentPolicy = null;
|
||||||
if (options.recordHarPath != null) {
|
if (options.recordHarPath != null) {
|
||||||
recordHar = new JsonObject();
|
recordHar = new JsonObject();
|
||||||
recordHar.addProperty("path", options.recordHarPath.toString());
|
recordHar.addProperty("path", options.recordHarPath.toString());
|
||||||
if (options.recordHarContent != null) {
|
if (options.recordHarContent != null) {
|
||||||
recordHar.addProperty("content", options.recordHarContent.toString().toLowerCase());
|
harContentPolicy = options.recordHarContent;
|
||||||
} else if (options.recordHarOmitContent != null && options.recordHarOmitContent) {
|
} else if (options.recordHarOmitContent != null && options.recordHarOmitContent) {
|
||||||
recordHar.addProperty("content", HarContentPolicy.OMIT.toString().toLowerCase());
|
harContentPolicy = HarContentPolicy.OMIT;
|
||||||
|
}
|
||||||
|
if (harContentPolicy != null) {
|
||||||
|
recordHar.addProperty("content", harContentPolicy.name().toLowerCase());
|
||||||
}
|
}
|
||||||
if (options.recordHarMode != null) {
|
if (options.recordHarMode != null) {
|
||||||
recordHar.addProperty("mode", options.recordHarMode.toString().toLowerCase());
|
recordHar.addProperty("mode", options.recordHarMode.name().toLowerCase());
|
||||||
}
|
|
||||||
if (options.recordHarUrlFilter instanceof String) {
|
|
||||||
recordHar.addProperty("urlGlob", (String) options.recordHarUrlFilter);
|
|
||||||
} else if (options.recordHarUrlFilter instanceof Pattern) {
|
|
||||||
Pattern pattern = (Pattern) options.recordHarUrlFilter;
|
|
||||||
recordHar.addProperty("urlRegexSource", pattern.pattern());
|
|
||||||
recordHar.addProperty("urlRegexFlags", toJsRegexFlags(pattern));
|
|
||||||
}
|
}
|
||||||
|
addHarUrlFilter(recordHar, options.recordHarUrlFilter);
|
||||||
options.recordHarPath = null;
|
options.recordHarPath = null;
|
||||||
options.recordHarMode = null;
|
options.recordHarMode = null;
|
||||||
options.recordHarOmitContent = null;
|
options.recordHarOmitContent = null;
|
||||||
@@ -210,7 +209,7 @@ class BrowserImpl extends ChannelOwner implements Browser {
|
|||||||
if (options.baseURL != null) {
|
if (options.baseURL != null) {
|
||||||
context.setBaseUrl(options.baseURL);
|
context.setBaseUrl(options.baseURL);
|
||||||
}
|
}
|
||||||
context.recordHarPath = recordHarPath;
|
context.setRecordHar(recordHarPath, harContentPolicy);
|
||||||
contexts.add(context);
|
contexts.add(context);
|
||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
@@ -231,9 +230,7 @@ class BrowserImpl extends ChannelOwner implements Browser {
|
|||||||
}
|
}
|
||||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||||
if (page != null) {
|
if (page != null) {
|
||||||
JsonObject jsonPage = new JsonObject();
|
params.add("page", ((PageImpl) page).toProtocolRef());
|
||||||
jsonPage.addProperty("guid", ((PageImpl) page).guid);
|
|
||||||
params.add("page", jsonPage);
|
|
||||||
}
|
}
|
||||||
sendMessage("startTracing", params);
|
sendMessage("startTracing", params);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,12 +22,15 @@ import com.google.gson.JsonObject;
|
|||||||
import com.microsoft.playwright.Browser;
|
import com.microsoft.playwright.Browser;
|
||||||
import com.microsoft.playwright.BrowserType;
|
import com.microsoft.playwright.BrowserType;
|
||||||
import com.microsoft.playwright.PlaywrightException;
|
import com.microsoft.playwright.PlaywrightException;
|
||||||
|
import com.microsoft.playwright.options.HarContentPolicy;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
import static com.microsoft.playwright.impl.Serialization.addHarUrlFilter;
|
||||||
import static com.microsoft.playwright.impl.Serialization.gson;
|
import static com.microsoft.playwright.impl.Serialization.gson;
|
||||||
|
import static com.microsoft.playwright.impl.Utils.convertType;
|
||||||
|
|
||||||
class BrowserTypeImpl extends ChannelOwner implements BrowserType {
|
class BrowserTypeImpl extends ChannelOwner implements BrowserType {
|
||||||
LocalUtils localUtils;
|
LocalUtils localUtils;
|
||||||
@@ -152,20 +155,52 @@ class BrowserTypeImpl extends ChannelOwner implements BrowserType {
|
|||||||
private BrowserContextImpl launchPersistentContextImpl(Path userDataDir, LaunchPersistentContextOptions options) {
|
private BrowserContextImpl launchPersistentContextImpl(Path userDataDir, LaunchPersistentContextOptions options) {
|
||||||
if (options == null) {
|
if (options == null) {
|
||||||
options = new LaunchPersistentContextOptions();
|
options = new LaunchPersistentContextOptions();
|
||||||
|
} else {
|
||||||
|
// Make a copy so that we can nullify some fields below.
|
||||||
|
options = convertType(options, LaunchPersistentContextOptions.class);
|
||||||
}
|
}
|
||||||
|
JsonObject recordHar = null;
|
||||||
|
Path recordHarPath = options.recordHarPath;
|
||||||
|
HarContentPolicy harContentPolicy = null;
|
||||||
|
if (options.recordHarPath != null) {
|
||||||
|
recordHar = new JsonObject();
|
||||||
|
recordHar.addProperty("path", options.recordHarPath.toString());
|
||||||
|
if (options.recordHarContent != null) {
|
||||||
|
harContentPolicy = options.recordHarContent;
|
||||||
|
} else if (options.recordHarOmitContent != null && options.recordHarOmitContent) {
|
||||||
|
harContentPolicy = HarContentPolicy.OMIT;
|
||||||
|
}
|
||||||
|
if (harContentPolicy != null) {
|
||||||
|
recordHar.addProperty("content", harContentPolicy.name().toLowerCase());
|
||||||
|
}
|
||||||
|
if (options.recordHarMode != null) {
|
||||||
|
recordHar.addProperty("mode", options.recordHarMode.toString().toLowerCase());
|
||||||
|
}
|
||||||
|
addHarUrlFilter(recordHar, options.recordHarUrlFilter);
|
||||||
|
options.recordHarPath = null;
|
||||||
|
options.recordHarMode = null;
|
||||||
|
options.recordHarOmitContent = null;
|
||||||
|
options.recordHarContent = null;
|
||||||
|
options.recordHarUrlFilter = null;
|
||||||
|
} else {
|
||||||
|
if (options.recordHarOmitContent != null) {
|
||||||
|
throw new PlaywrightException("recordHarOmitContent is set but recordHarPath is null");
|
||||||
|
}
|
||||||
|
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();
|
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||||
params.addProperty("userDataDir", userDataDir.toString());
|
params.addProperty("userDataDir", userDataDir.toString());
|
||||||
if (options.recordHarPath != null) {
|
if (recordHar != null) {
|
||||||
JsonObject recordHar = new JsonObject();
|
|
||||||
recordHar.addProperty("path", options.recordHarPath.toString());
|
|
||||||
if (options.recordHarOmitContent != null) {
|
|
||||||
recordHar.addProperty("omitContent", true);
|
|
||||||
}
|
|
||||||
params.remove("recordHarPath");
|
|
||||||
params.remove("recordHarOmitContent");
|
|
||||||
params.add("recordHar", recordHar);
|
params.add("recordHar", recordHar);
|
||||||
} else if (options.recordHarOmitContent != null) {
|
|
||||||
throw new PlaywrightException("recordHarOmitContent is set but recordHarPath is null");
|
|
||||||
}
|
}
|
||||||
if (options.recordVideoDir != null) {
|
if (options.recordVideoDir != null) {
|
||||||
JsonObject recordVideo = new JsonObject();
|
JsonObject recordVideo = new JsonObject();
|
||||||
@@ -195,7 +230,7 @@ class BrowserTypeImpl extends ChannelOwner implements BrowserType {
|
|||||||
if (options.baseURL != null) {
|
if (options.baseURL != null) {
|
||||||
context.setBaseUrl(options.baseURL);
|
context.setBaseUrl(options.baseURL);
|
||||||
}
|
}
|
||||||
context.recordHarPath = options.recordHarPath;
|
context.setRecordHar(recordHarPath, harContentPolicy);
|
||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -108,4 +108,10 @@ class ChannelOwner extends LoggingSupport {
|
|||||||
|
|
||||||
void handleEvent(String event, JsonObject parameters) {
|
void handleEvent(String event, JsonObject parameters) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
JsonObject toProtocolRef() {
|
||||||
|
JsonObject json = new JsonObject();
|
||||||
|
json.addProperty("guid", guid);
|
||||||
|
return json;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ package com.microsoft.playwright.impl;
|
|||||||
import com.microsoft.playwright.Locator;
|
import com.microsoft.playwright.Locator;
|
||||||
import com.microsoft.playwright.assertions.LocatorAssertions;
|
import com.microsoft.playwright.assertions.LocatorAssertions;
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
@@ -39,6 +40,7 @@ public class LocatorAssertionsImpl extends AssertionsBase implements LocatorAsse
|
|||||||
public void containsText(String text, ContainsTextOptions options) {
|
public void containsText(String text, ContainsTextOptions options) {
|
||||||
ExpectedTextValue expected = new ExpectedTextValue();
|
ExpectedTextValue expected = new ExpectedTextValue();
|
||||||
expected.string = text;
|
expected.string = text;
|
||||||
|
expected.ignoreCase = shouldIgnoreCase(options);
|
||||||
expected.matchSubstring = true;
|
expected.matchSubstring = true;
|
||||||
expected.normalizeWhiteSpace = true;
|
expected.normalizeWhiteSpace = true;
|
||||||
expectImpl("to.have.text", expected, text, "Locator expected to contain text", convertType(options, FrameExpectOptions.class));
|
expectImpl("to.have.text", expected, text, "Locator expected to contain text", convertType(options, FrameExpectOptions.class));
|
||||||
@@ -47,6 +49,7 @@ public class LocatorAssertionsImpl extends AssertionsBase implements LocatorAsse
|
|||||||
@Override
|
@Override
|
||||||
public void containsText(Pattern pattern, ContainsTextOptions options) {
|
public void containsText(Pattern pattern, ContainsTextOptions options) {
|
||||||
ExpectedTextValue expected = expectedRegex(pattern);
|
ExpectedTextValue expected = expectedRegex(pattern);
|
||||||
|
expected.ignoreCase = shouldIgnoreCase(options);
|
||||||
expected.matchSubstring = true;
|
expected.matchSubstring = true;
|
||||||
expected.normalizeWhiteSpace = true;
|
expected.normalizeWhiteSpace = true;
|
||||||
expectImpl("to.have.text", expected, pattern, "Locator expected to contain regex", convertType(options, FrameExpectOptions.class));
|
expectImpl("to.have.text", expected, pattern, "Locator expected to contain regex", convertType(options, FrameExpectOptions.class));
|
||||||
@@ -58,6 +61,7 @@ public class LocatorAssertionsImpl extends AssertionsBase implements LocatorAsse
|
|||||||
for (String text : strings) {
|
for (String text : strings) {
|
||||||
ExpectedTextValue expected = new ExpectedTextValue();
|
ExpectedTextValue expected = new ExpectedTextValue();
|
||||||
expected.string = text;
|
expected.string = text;
|
||||||
|
expected.ignoreCase = shouldIgnoreCase(options);
|
||||||
expected.matchSubstring = true;
|
expected.matchSubstring = true;
|
||||||
expected.normalizeWhiteSpace = true;
|
expected.normalizeWhiteSpace = true;
|
||||||
list.add(expected);
|
list.add(expected);
|
||||||
@@ -70,6 +74,7 @@ public class LocatorAssertionsImpl extends AssertionsBase implements LocatorAsse
|
|||||||
List<ExpectedTextValue> list = new ArrayList<>();
|
List<ExpectedTextValue> list = new ArrayList<>();
|
||||||
for (Pattern pattern : patterns) {
|
for (Pattern pattern : patterns) {
|
||||||
ExpectedTextValue expected = expectedRegex(pattern);
|
ExpectedTextValue expected = expectedRegex(pattern);
|
||||||
|
expected.ignoreCase = shouldIgnoreCase(options);
|
||||||
expected.matchSubstring = true;
|
expected.matchSubstring = true;
|
||||||
expected.normalizeWhiteSpace = true;
|
expected.normalizeWhiteSpace = true;
|
||||||
list.add(expected);
|
list.add(expected);
|
||||||
@@ -203,6 +208,7 @@ public class LocatorAssertionsImpl extends AssertionsBase implements LocatorAsse
|
|||||||
public void hasText(String text, HasTextOptions options) {
|
public void hasText(String text, HasTextOptions options) {
|
||||||
ExpectedTextValue expected = new ExpectedTextValue();
|
ExpectedTextValue expected = new ExpectedTextValue();
|
||||||
expected.string = text;
|
expected.string = text;
|
||||||
|
expected.ignoreCase = shouldIgnoreCase(options);
|
||||||
expected.matchSubstring = false;
|
expected.matchSubstring = false;
|
||||||
expected.normalizeWhiteSpace = true;
|
expected.normalizeWhiteSpace = true;
|
||||||
expectImpl("to.have.text", expected, text, "Locator expected to have text", convertType(options, FrameExpectOptions.class));
|
expectImpl("to.have.text", expected, text, "Locator expected to have text", convertType(options, FrameExpectOptions.class));
|
||||||
@@ -211,6 +217,7 @@ public class LocatorAssertionsImpl extends AssertionsBase implements LocatorAsse
|
|||||||
@Override
|
@Override
|
||||||
public void hasText(Pattern pattern, HasTextOptions options) {
|
public void hasText(Pattern pattern, HasTextOptions options) {
|
||||||
ExpectedTextValue expected = expectedRegex(pattern);
|
ExpectedTextValue expected = expectedRegex(pattern);
|
||||||
|
expected.ignoreCase = shouldIgnoreCase(options);
|
||||||
// Just match substring, same as containsText.
|
// Just match substring, same as containsText.
|
||||||
expected.matchSubstring = true;
|
expected.matchSubstring = true;
|
||||||
expected.normalizeWhiteSpace = true;
|
expected.normalizeWhiteSpace = true;
|
||||||
@@ -223,6 +230,7 @@ public class LocatorAssertionsImpl extends AssertionsBase implements LocatorAsse
|
|||||||
for (String text : strings) {
|
for (String text : strings) {
|
||||||
ExpectedTextValue expected = new ExpectedTextValue();
|
ExpectedTextValue expected = new ExpectedTextValue();
|
||||||
expected.string = text;
|
expected.string = text;
|
||||||
|
expected.ignoreCase = shouldIgnoreCase(options);
|
||||||
expected.matchSubstring = false;
|
expected.matchSubstring = false;
|
||||||
expected.normalizeWhiteSpace = true;
|
expected.normalizeWhiteSpace = true;
|
||||||
list.add(expected);
|
list.add(expected);
|
||||||
@@ -235,6 +243,7 @@ public class LocatorAssertionsImpl extends AssertionsBase implements LocatorAsse
|
|||||||
List<ExpectedTextValue> list = new ArrayList<>();
|
List<ExpectedTextValue> list = new ArrayList<>();
|
||||||
for (Pattern pattern : patterns) {
|
for (Pattern pattern : patterns) {
|
||||||
ExpectedTextValue expected = expectedRegex(pattern);
|
ExpectedTextValue expected = expectedRegex(pattern);
|
||||||
|
expected.ignoreCase = shouldIgnoreCase(options);
|
||||||
expected.matchSubstring = true;
|
expected.matchSubstring = true;
|
||||||
expected.normalizeWhiteSpace = true;
|
expected.normalizeWhiteSpace = true;
|
||||||
list.add(expected);
|
list.add(expected);
|
||||||
@@ -327,5 +336,17 @@ public class LocatorAssertionsImpl extends AssertionsBase implements LocatorAsse
|
|||||||
public LocatorAssertions not() {
|
public LocatorAssertions not() {
|
||||||
return new LocatorAssertionsImpl(actualLocator, !isNot);
|
return new LocatorAssertionsImpl(actualLocator, !isNot);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
private static Boolean shouldIgnoreCase(Object options) {
|
||||||
|
if (options == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
Field fromField = options.getClass().getDeclaredField("ignoreCase");
|
||||||
|
Object value = fromField.get(options);
|
||||||
|
return (Boolean) value;
|
||||||
|
} catch (NoSuchFieldException | IllegalAccessException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -510,9 +510,7 @@ class LocatorImpl implements Locator {
|
|||||||
|
|
||||||
JsonObject toProtocol() {
|
JsonObject toProtocol() {
|
||||||
JsonObject result = new JsonObject();
|
JsonObject result = new JsonObject();
|
||||||
JsonObject frameJson = new JsonObject();
|
result.add("frame", frame.toProtocolRef());
|
||||||
frameJson.addProperty("guid", frame.guid);
|
|
||||||
result.add("frame", frameJson);
|
|
||||||
result.addProperty("selector", selector);
|
result.addProperty("selector", selector);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -974,6 +974,10 @@ public class PageImpl extends ChannelOwner implements Page {
|
|||||||
if (options == null) {
|
if (options == null) {
|
||||||
options = new RouteFromHAROptions();
|
options = new RouteFromHAROptions();
|
||||||
}
|
}
|
||||||
|
if (options.update != null && options.update) {
|
||||||
|
browserContext.recordIntoHar(this, har, convertType(options, BrowserContext.RouteFromHAROptions.class));
|
||||||
|
return;
|
||||||
|
}
|
||||||
UrlMatcher matcher = UrlMatcher.forOneOf(browserContext.baseUrl, options.url);
|
UrlMatcher matcher = UrlMatcher.forOneOf(browserContext.baseUrl, options.url);
|
||||||
HARRouter harRouter = new HARRouter(connection.localUtils, har, options.notFound);
|
HARRouter harRouter = new HARRouter(connection.localUtils, har, options.notFound);
|
||||||
onClose(context -> harRouter.dispose());
|
onClose(context -> harRouter.dispose());
|
||||||
|
|||||||
@@ -85,6 +85,7 @@ class ExpectedTextValue {
|
|||||||
String string;
|
String string;
|
||||||
String regexSource;
|
String regexSource;
|
||||||
String regexFlags;
|
String regexFlags;
|
||||||
|
Boolean ignoreCase;
|
||||||
Boolean matchSubstring;
|
Boolean matchSubstring;
|
||||||
Boolean normalizeWhiteSpace;
|
Boolean normalizeWhiteSpace;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,6 +31,9 @@ import java.lang.reflect.Type;
|
|||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
import static com.microsoft.playwright.impl.Utils.toJsRegexFlags;
|
||||||
|
|
||||||
class Serialization {
|
class Serialization {
|
||||||
private static final Gson gson = new GsonBuilder()
|
private static final Gson gson = new GsonBuilder()
|
||||||
@@ -288,9 +291,7 @@ class Serialization {
|
|||||||
static JsonArray toProtocol(ElementHandle[] handles) {
|
static JsonArray toProtocol(ElementHandle[] handles) {
|
||||||
JsonArray jsonElements = new JsonArray();
|
JsonArray jsonElements = new JsonArray();
|
||||||
for (ElementHandle handle : handles) {
|
for (ElementHandle handle : handles) {
|
||||||
JsonObject jsonHandle = new JsonObject();
|
jsonElements.add(((ElementHandleImpl) handle).toProtocolRef());
|
||||||
jsonHandle.addProperty("guid", ((ElementHandleImpl) handle).guid);
|
|
||||||
jsonElements.add(jsonHandle);
|
|
||||||
}
|
}
|
||||||
return jsonElements;
|
return jsonElements;
|
||||||
}
|
}
|
||||||
@@ -299,6 +300,16 @@ class Serialization {
|
|||||||
return toNameValueArray(map);
|
return toNameValueArray(map);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void addHarUrlFilter(JsonObject options, Object urlFilter) {
|
||||||
|
if (urlFilter instanceof String) {
|
||||||
|
options.addProperty("urlGlob", (String) urlFilter);
|
||||||
|
} else if (urlFilter instanceof Pattern) {
|
||||||
|
Pattern pattern = (Pattern) urlFilter;
|
||||||
|
options.addProperty("urlRegexSource", pattern.pattern());
|
||||||
|
options.addProperty("urlRegexFlags", toJsRegexFlags(pattern));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static JsonArray toNameValueArray(Map<String, ?> map) {
|
static JsonArray toNameValueArray(Map<String, ?> map) {
|
||||||
JsonArray array = new JsonArray();
|
JsonArray array = new JsonArray();
|
||||||
for (Map.Entry<String, ?> e : map.entrySet()) {
|
for (Map.Entry<String, ?> e : map.entrySet()) {
|
||||||
@@ -349,9 +360,7 @@ class Serialization {
|
|||||||
private static class HandleSerializer implements JsonSerializer<JSHandleImpl> {
|
private static class HandleSerializer implements JsonSerializer<JSHandleImpl> {
|
||||||
@Override
|
@Override
|
||||||
public JsonElement serialize(JSHandleImpl src, Type typeOfSrc, JsonSerializationContext context) {
|
public JsonElement serialize(JSHandleImpl src, Type typeOfSrc, JsonSerializationContext context) {
|
||||||
JsonObject json = new JsonObject();
|
return src.toProtocolRef();
|
||||||
json.addProperty("guid", src.guid);
|
|
||||||
return json;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -176,7 +176,7 @@ class Utils {
|
|||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new PlaywrightException("Failed to copy file to remote server.", e);
|
throw new PlaywrightException("Failed to copy file to remote server.", e);
|
||||||
}
|
}
|
||||||
jsonStreams.add(temp.toProtocol());
|
jsonStreams.add(temp.toProtocolRef());
|
||||||
}
|
}
|
||||||
params.add("streams", jsonStreams);
|
params.add("streams", jsonStreams);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -30,10 +30,4 @@ class WritableStream extends ChannelOwner {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
JsonObject toProtocol() {
|
|
||||||
JsonObject json = new JsonObject();
|
|
||||||
json.addProperty("guid", guid);
|
|
||||||
return json;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package com.microsoft.playwright;
|
package com.microsoft.playwright;
|
||||||
|
|
||||||
|
import com.microsoft.playwright.options.HarContentPolicy;
|
||||||
import com.microsoft.playwright.options.HarMode;
|
import com.microsoft.playwright.options.HarMode;
|
||||||
import com.microsoft.playwright.options.HarNotFound;
|
import com.microsoft.playwright.options.HarNotFound;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
@@ -26,6 +27,7 @@ import java.io.IOException;
|
|||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.io.OutputStreamWriter;
|
import java.io.OutputStreamWriter;
|
||||||
import java.io.Writer;
|
import java.io.Writer;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
@@ -35,6 +37,7 @@ import java.util.regex.Pattern;
|
|||||||
import static com.microsoft.playwright.Utils.copy;
|
import static com.microsoft.playwright.Utils.copy;
|
||||||
import static com.microsoft.playwright.Utils.extractZip;
|
import static com.microsoft.playwright.Utils.extractZip;
|
||||||
import static com.microsoft.playwright.assertions.PlaywrightAssertions.assertThat;
|
import static com.microsoft.playwright.assertions.PlaywrightAssertions.assertThat;
|
||||||
|
import static com.microsoft.playwright.options.HarContentPolicy.ATTACH;
|
||||||
import static org.junit.jupiter.api.Assertions.*;
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
public class TestBrowserContextHar extends TestBase {
|
public class TestBrowserContextHar extends TestBase {
|
||||||
@@ -264,7 +267,29 @@ public class TestBrowserContextHar extends TestBase {
|
|||||||
}
|
}
|
||||||
try (BrowserContext context2 = browser.newContext()) {
|
try (BrowserContext context2 = browser.newContext()) {
|
||||||
context2.routeFromHAR(harPath, new BrowserContext.RouteFromHAROptions().setNotFound(HarNotFound.ABORT));
|
context2.routeFromHAR(harPath, new BrowserContext.RouteFromHAROptions().setNotFound(HarNotFound.ABORT));
|
||||||
Page page2 = context.newPage();
|
Page page2 = context2.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 shouldProduceExtractedZip(@TempDir Path tmpDir) throws IOException {
|
||||||
|
Path harPath = tmpDir.resolve("har.har");
|
||||||
|
try (BrowserContext context1 = browser.newContext(new Browser.NewContextOptions()
|
||||||
|
.setRecordHarPath(harPath)
|
||||||
|
.setRecordHarMode(HarMode.MINIMAL)
|
||||||
|
.setRecordHarContent(ATTACH))) {
|
||||||
|
Page page1 = context1.newPage();
|
||||||
|
page1.navigate(server.PREFIX + "/one-style.html");
|
||||||
|
}
|
||||||
|
assertTrue(Files.exists(harPath));
|
||||||
|
String har = new String(Files.readAllBytes(harPath), StandardCharsets.UTF_8);
|
||||||
|
assertFalse(har.contains("background-color"));
|
||||||
|
try (BrowserContext context2 = browser.newContext()) {
|
||||||
|
context2.routeFromHAR(harPath, new BrowserContext.RouteFromHAROptions().setNotFound(HarNotFound.ABORT));
|
||||||
|
Page page2 = context2.newPage();
|
||||||
page2.navigate(server.PREFIX + "/one-style.html");
|
page2.navigate(server.PREFIX + "/one-style.html");
|
||||||
assertTrue(page2.content().contains("hello, world!"));
|
assertTrue(page2.content().contains("hello, world!"));
|
||||||
assertThat(page2.locator("body")).hasCSS("background-color", "rgb(255, 192, 203)");
|
assertThat(page2.locator("body")).hasCSS("background-color", "rgb(255, 192, 203)");
|
||||||
@@ -286,7 +311,7 @@ public class TestBrowserContextHar extends TestBase {
|
|||||||
|
|
||||||
try (BrowserContext context2 = browser.newContext()) {
|
try (BrowserContext context2 = browser.newContext()) {
|
||||||
context2.routeFromHAR(harDir.resolve("har.har"));
|
context2.routeFromHAR(harDir.resolve("har.har"));
|
||||||
Page page2 = context.newPage();
|
Page page2 = context2.newPage();
|
||||||
page2.navigate(server.PREFIX + "/one-style.html");
|
page2.navigate(server.PREFIX + "/one-style.html");
|
||||||
assertTrue(page2.content().contains("hello, world!"));
|
assertTrue(page2.content().contains("hello, world!"));
|
||||||
assertThat(page2.locator("body")).hasCSS("background-color", "rgb(255, 192, 203)");
|
assertThat(page2.locator("body")).hasCSS("background-color", "rgb(255, 192, 203)");
|
||||||
@@ -380,4 +405,54 @@ public class TestBrowserContextHar extends TestBase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldUpdateHarZipForContext(@TempDir Path tmpDir) {
|
||||||
|
Path harPath = tmpDir.resolve("har.zip");
|
||||||
|
try (BrowserContext context1 = browser.newContext()) {
|
||||||
|
context1.routeFromHAR(harPath, new BrowserContext.RouteFromHAROptions().setUpdate(true));
|
||||||
|
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 = context2.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 shouldUpdateHarZipForPage(@TempDir Path tmpDir) {
|
||||||
|
Path harPath = tmpDir.resolve("har.zip");
|
||||||
|
try (BrowserContext context1 = browser.newContext()) {
|
||||||
|
Page page1 = context1.newPage();
|
||||||
|
page1.routeFromHAR(harPath, new Page.RouteFromHAROptions().setUpdate(true));
|
||||||
|
page1.navigate(server.PREFIX + "/one-style.html");
|
||||||
|
}
|
||||||
|
try (BrowserContext context2 = browser.newContext()) {
|
||||||
|
Page page2 = context2.newPage();
|
||||||
|
page2.routeFromHAR(harPath, new Page.RouteFromHAROptions().setNotFound(HarNotFound.ABORT));
|
||||||
|
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 shouldUpdateExtractedHarZipForPage(@TempDir Path tmpDir) {
|
||||||
|
Path harPath = tmpDir.resolve("har.har");
|
||||||
|
try (BrowserContext context1 = browser.newContext()) {
|
||||||
|
Page page1 = context1.newPage();
|
||||||
|
page1.routeFromHAR(harPath, new Page.RouteFromHAROptions().setUpdate(true));
|
||||||
|
page1.navigate(server.PREFIX + "/one-style.html");
|
||||||
|
}
|
||||||
|
try (BrowserContext context2 = browser.newContext()) {
|
||||||
|
Page page2 = context2.newPage();
|
||||||
|
page2.routeFromHAR(harPath, new Page.RouteFromHAROptions().setNotFound(HarNotFound.ABORT));
|
||||||
|
page2.navigate(server.PREFIX + "/one-style.html");
|
||||||
|
assertTrue(page2.content().contains("hello, world!"));
|
||||||
|
assertThat(page2.locator("body")).hasCSS("background-color", "rgb(255, 192, 203)");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -72,6 +72,28 @@ public class TestLocatorAssertions extends TestBase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void containsTextWTextPass() {
|
||||||
|
page.setContent("<div id=node>Text content</div>");
|
||||||
|
Locator locator = page.locator("#node");
|
||||||
|
assertThat(locator).containsText("Text");
|
||||||
|
// Should normalize whitespace.
|
||||||
|
assertThat(locator).containsText(" ext cont\n ");
|
||||||
|
// Should support ignoreCase.
|
||||||
|
assertThat(locator).containsText("EXT", new LocatorAssertions.ContainsTextOptions().setIgnoreCase(true));
|
||||||
|
// Should support falsy ignoreCase.
|
||||||
|
assertThat(locator).not().containsText("TEXT", new LocatorAssertions.ContainsTextOptions().setIgnoreCase(false));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void containsTextWTextArrayPass() {
|
||||||
|
page.setContent("<div>Text \n1</div><div>Text2</div><div>Text3</div>");
|
||||||
|
Locator locator = page.locator("div");
|
||||||
|
assertThat(locator).containsText(new String[] {"ext 1", "ext3"});
|
||||||
|
// Should support ignoreCase.
|
||||||
|
assertThat(locator).containsText(new String[] {"EXT 1", "eXt3"}, new LocatorAssertions.ContainsTextOptions().setIgnoreCase(true));
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void hasTextWRegexPass() {
|
void hasTextWRegexPass() {
|
||||||
page.setContent("<div id=node>Text content</div>");
|
page.setContent("<div id=node>Text content</div>");
|
||||||
@@ -79,6 +101,10 @@ public class TestLocatorAssertions extends TestBase {
|
|||||||
assertThat(locator).hasText(Pattern.compile("Te.t"));
|
assertThat(locator).hasText(Pattern.compile("Te.t"));
|
||||||
// Should not normalize whitespace.
|
// Should not normalize whitespace.
|
||||||
assertThat(locator).hasText(Pattern.compile("Text.+content"));
|
assertThat(locator).hasText(Pattern.compile("Text.+content"));
|
||||||
|
// Should respect ignoreCase.
|
||||||
|
assertThat(locator).hasText(Pattern.compile("text content"), new LocatorAssertions.HasTextOptions().setIgnoreCase(true));
|
||||||
|
// Should override regex flag with ignoreCase.
|
||||||
|
assertThat(locator).not().hasText(Pattern.compile("text content", Pattern.CASE_INSENSITIVE), new LocatorAssertions.HasTextOptions().setIgnoreCase(false));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -101,6 +127,10 @@ public class TestLocatorAssertions extends TestBase {
|
|||||||
Locator locator = page.locator("#node");
|
Locator locator = page.locator("#node");
|
||||||
// Should normalize whitespace.
|
// Should normalize whitespace.
|
||||||
assertThat(locator).hasText("Text content");
|
assertThat(locator).hasText("Text content");
|
||||||
|
// Should support ignoreCase.
|
||||||
|
assertThat(locator).hasText("text CONTENT", new LocatorAssertions.HasTextOptions().setIgnoreCase(true));
|
||||||
|
// Should support falsy ignoreCase.
|
||||||
|
assertThat(locator).not().hasText("TEXT", new LocatorAssertions.HasTextOptions().setIgnoreCase(false));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -131,6 +161,8 @@ public class TestLocatorAssertions extends TestBase {
|
|||||||
Locator locator = page.locator("div");
|
Locator locator = page.locator("div");
|
||||||
// Should normalize whitespace.
|
// Should normalize whitespace.
|
||||||
assertThat(locator).hasText(new String[] {"Text 1", "Text 2a"});
|
assertThat(locator).hasText(new String[] {"Text 1", "Text 2a"});
|
||||||
|
// Should support ignoreCase.
|
||||||
|
assertThat(locator).hasText(new String[] {"tEXT 1", "TExt 2A"}, new LocatorAssertions.HasTextOptions().setIgnoreCase(true));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
<groupId>com.microsoft.playwright</groupId>
|
<groupId>com.microsoft.playwright</groupId>
|
||||||
<artifactId>parent-pom</artifactId>
|
<artifactId>parent-pom</artifactId>
|
||||||
<version>1.23.0-SNAPSHOT</version>
|
<version>1.23.0</version>
|
||||||
<packaging>pom</packaging>
|
<packaging>pom</packaging>
|
||||||
<name>Playwright Parent Project</name>
|
<name>Playwright Parent Project</name>
|
||||||
<description>Java library to automate Chromium, Firefox and WebKit with a single API.
|
<description>Java library to automate Chromium, Firefox and WebKit with a single API.
|
||||||
|
|||||||
+1
-1
@@ -1 +1 @@
|
|||||||
1.23.0
|
1.23.1
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
<groupId>com.microsoft.playwright</groupId>
|
<groupId>com.microsoft.playwright</groupId>
|
||||||
<artifactId>api-generator</artifactId>
|
<artifactId>api-generator</artifactId>
|
||||||
<version>1.23.0-SNAPSHOT</version>
|
<version>1.23.0</version>
|
||||||
<name>Playwright - API Generator</name>
|
<name>Playwright - API Generator</name>
|
||||||
<description>
|
<description>
|
||||||
This is an internal module used to generate Java API from the upstream Playwright
|
This is an internal module used to generate Java API from the upstream Playwright
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
<groupId>com.microsoft.playwright</groupId>
|
<groupId>com.microsoft.playwright</groupId>
|
||||||
<artifactId>test-cli-version</artifactId>
|
<artifactId>test-cli-version</artifactId>
|
||||||
<version>1.23.0-SNAPSHOT</version>
|
<version>1.23.0</version>
|
||||||
<name>Test Playwright Command Line Version</name>
|
<name>Test Playwright Command Line Version</name>
|
||||||
<properties>
|
<properties>
|
||||||
<compiler.version>1.8</compiler.version>
|
<compiler.version>1.8</compiler.version>
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
<groupId>com.microsoft.playwright</groupId>
|
<groupId>com.microsoft.playwright</groupId>
|
||||||
<artifactId>test-local-installation</artifactId>
|
<artifactId>test-local-installation</artifactId>
|
||||||
<version>1.23.0-SNAPSHOT</version>
|
<version>1.23.0</version>
|
||||||
<name>Test local installation</name>
|
<name>Test local installation</name>
|
||||||
<description>Runs Playwright test suite (copied from playwright module) against locally cached Playwright</description>
|
<description>Runs Playwright test suite (copied from playwright module) against locally cached Playwright</description>
|
||||||
<properties>
|
<properties>
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
</parent>
|
</parent>
|
||||||
<groupId>com.microsoft.playwright</groupId>
|
<groupId>com.microsoft.playwright</groupId>
|
||||||
<artifactId>test-spring-boot-starter</artifactId>
|
<artifactId>test-spring-boot-starter</artifactId>
|
||||||
<version>1.23.0-SNAPSHOT</version>
|
<version>1.23.0</version>
|
||||||
<name>Test Playwright With Spring Boot</name>
|
<name>Test Playwright With Spring Boot</name>
|
||||||
<properties>
|
<properties>
|
||||||
<spring.version>2.4.3</spring.version>
|
<spring.version>2.4.3</spring.version>
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
<groupId>com.microsoft.playwright</groupId>
|
<groupId>com.microsoft.playwright</groupId>
|
||||||
<artifactId>update-version</artifactId>
|
<artifactId>update-version</artifactId>
|
||||||
<version>1.23.0-SNAPSHOT</version>
|
<version>1.23.0</version>
|
||||||
<name>Playwright - Update Version in Documentation</name>
|
<name>Playwright - Update Version in Documentation</name>
|
||||||
<description>
|
<description>
|
||||||
This is an internal module used to update versions in the documentation based on
|
This is an internal module used to update versions in the documentation based on
|
||||||
|
|||||||
Reference in New Issue
Block a user