diff --git a/playwright/src/main/java/com/microsoft/playwright/Browser.java b/playwright/src/main/java/com/microsoft/playwright/Browser.java index c4f1f7d09..fb6fda7fa 100644 --- a/playwright/src/main/java/com/microsoft/playwright/Browser.java +++ b/playwright/src/main/java/com/microsoft/playwright/Browser.java @@ -114,9 +114,11 @@ class NewContextOptions { */ public List permissions; /** - * Network proxy settings to use with this context. Note that browser needs to be launched with the global proxy for this - * option to work. If all contexts override the proxy, global proxy will be never used and can be any string, for example - * {@code launch({ proxy: { server: 'per-context' } })}. + * Network proxy settings to use with this context. + * + *

NOTE: For Chromium on Windows the browser needs to be launched with the global proxy for this option to work. If all contexts + * override the proxy, global proxy will be never used and can be any string, for example {@code launch({ proxy: { server: + * 'http://per-context' } })}. */ public Proxy proxy; /** @@ -349,9 +351,11 @@ class NewPageOptions { */ public List permissions; /** - * Network proxy settings to use with this context. Note that browser needs to be launched with the global proxy for this - * option to work. If all contexts override the proxy, global proxy will be never used and can be any string, for example - * {@code launch({ proxy: { server: 'per-context' } })}. + * Network proxy settings to use with this context. + * + *

NOTE: For Chromium on Windows the browser needs to be launched with the global proxy for this option to work. If all contexts + * override the proxy, global proxy will be never used and can be any string, for example {@code launch({ proxy: { server: + * 'http://per-context' } })}. */ public Proxy proxy; /** @@ -525,6 +529,33 @@ public NewPageOptions setViewportSize(ViewportSize viewportSize) { return this; } } + class StartTracingOptions { + /** + * specify custom categories to use instead of default. + */ + public List categories; + /** + * A path to write the trace file to. + */ + public Path path; + /** + * captures screenshots in the trace. + */ + public Boolean screenshots; + + public StartTracingOptions setCategories(List categories) { + this.categories = categories; + return this; + } + public StartTracingOptions setPath(Path path) { + this.path = path; + return this; + } + public StartTracingOptions setScreenshots(boolean screenshots) { + this.screenshots = screenshots; + return this; + } + } /** * In case this browser is obtained using {@link BrowserType#launch BrowserType.launch()}, closes the browser and all of * its pages (if any were opened). @@ -593,6 +624,59 @@ default Page newPage() { * BrowserContext#newPage BrowserContext.newPage()} to control their exact life times. */ Page newPage(NewPageOptions options); + /** + * NOTE: Tracing is only supported on Chromium-based browsers. + * + *

You can use {@link Browser#startTracing Browser.startTracing()} and {@link Browser#stopTracing Browser.stopTracing()} to + * create a trace file that can be opened in Chrome DevTools performance panel. + *

{@code
+   * browser.startTracing(page, new Browser.StartTracingOptions()
+   *   .setPath(Paths.get("trace.json")));
+   * page.goto('https://www.google.com');
+   * browser.stopTracing();
+   * }
+ * + * @param page Optional, if specified, tracing includes screenshots of the given page. + */ + default void startTracing(Page page) { + startTracing(page, null); + } + /** + * NOTE: Tracing is only supported on Chromium-based browsers. + * + *

You can use {@link Browser#startTracing Browser.startTracing()} and {@link Browser#stopTracing Browser.stopTracing()} to + * create a trace file that can be opened in Chrome DevTools performance panel. + *

{@code
+   * browser.startTracing(page, new Browser.StartTracingOptions()
+   *   .setPath(Paths.get("trace.json")));
+   * page.goto('https://www.google.com');
+   * browser.stopTracing();
+   * }
+ */ + default void startTracing() { + startTracing(null); + } + /** + * NOTE: Tracing is only supported on Chromium-based browsers. + * + *

You can use {@link Browser#startTracing Browser.startTracing()} and {@link Browser#stopTracing Browser.stopTracing()} to + * create a trace file that can be opened in Chrome DevTools performance panel. + *

{@code
+   * browser.startTracing(page, new Browser.StartTracingOptions()
+   *   .setPath(Paths.get("trace.json")));
+   * page.goto('https://www.google.com');
+   * browser.stopTracing();
+   * }
+ * + * @param page Optional, if specified, tracing includes screenshots of the given page. + */ + void startTracing(Page page, StartTracingOptions options); + /** + * NOTE: Tracing is only supported on Chromium-based browsers. + * + *

Returns the buffer with trace data. + */ + byte[] stopTracing(); /** * Returns the browser version. */ diff --git a/playwright/src/main/java/com/microsoft/playwright/Keyboard.java b/playwright/src/main/java/com/microsoft/playwright/Keyboard.java index 4e7ddfadb..c23e2c6cc 100644 --- a/playwright/src/main/java/com/microsoft/playwright/Keyboard.java +++ b/playwright/src/main/java/com/microsoft/playwright/Keyboard.java @@ -202,6 +202,8 @@ default void press(String key) { * *

NOTE: Modifier keys DO NOT effect {@code keyboard.type}. Holding down {@code Shift} will not type the text in upper case. * + *

NOTE: For characters that are not on a US keyboard, only an {@code input} event will be sent. + * * @param text A text to type into a focused element. */ default void type(String text) { @@ -220,6 +222,8 @@ default void type(String text) { * *

NOTE: Modifier keys DO NOT effect {@code keyboard.type}. Holding down {@code Shift} will not type the text in upper case. * + *

NOTE: For characters that are not on a US keyboard, only an {@code input} event will be sent. + * * @param text A text to type into a focused element. */ void type(String text, TypeOptions options); diff --git a/playwright/src/main/java/com/microsoft/playwright/Page.java b/playwright/src/main/java/com/microsoft/playwright/Page.java index 3c6ee7cce..44e313e4d 100644 --- a/playwright/src/main/java/com/microsoft/playwright/Page.java +++ b/playwright/src/main/java/com/microsoft/playwright/Page.java @@ -2077,6 +2077,8 @@ default void dispatchEvent(String selector, String type) { */ void dispatchEvent(String selector, String type, Object eventInit, DispatchEventOptions options); /** + * This method changes the {@code CSS media type} through the {@code media} argument, and/or the {@code "prefers-colors-scheme"} media + * feature, using the {@code colorScheme} argument. *

{@code
    * page.evaluate("() => matchMedia('screen').matches");
    * // → true
@@ -2109,6 +2111,8 @@ default void emulateMedia() {
     emulateMedia(null);
   }
   /**
+   * This method changes the {@code CSS media type} through the {@code media} argument, and/or the {@code "prefers-colors-scheme"} media
+   * feature, using the {@code colorScheme} argument.
    * 
{@code
    * page.evaluate("() => matchMedia('screen').matches");
    * // → true
diff --git a/playwright/src/main/java/com/microsoft/playwright/impl/BrowserImpl.java b/playwright/src/main/java/com/microsoft/playwright/impl/BrowserImpl.java
index 9dbdbea10..2c559a718 100644
--- a/playwright/src/main/java/com/microsoft/playwright/impl/BrowserImpl.java
+++ b/playwright/src/main/java/com/microsoft/playwright/impl/BrowserImpl.java
@@ -27,10 +27,7 @@
 import java.io.IOException;
 import java.nio.charset.StandardCharsets;
 import java.nio.file.Files;
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
+import java.util.*;
 import java.util.function.Consumer;
 
 import static com.microsoft.playwright.impl.Serialization.gson;
@@ -183,6 +180,34 @@ public Page newPage(NewPageOptions options) {
     return withLogging("Browser.newPage", () -> newPageImpl(options));
   }
 
+  @Override
+  public void startTracing(Page page, StartTracingOptions options) {
+    withLogging("Browser.startTracing", () -> startTracingImpl(page, options));
+  }
+
+  private void startTracingImpl(Page page, StartTracingOptions options) {
+    if (options == null) {
+      options = new StartTracingOptions();
+    }
+    JsonObject params = gson().toJsonTree(options).getAsJsonObject();
+    if (page != null) {
+      JsonObject jsonPage = new JsonObject();
+      jsonPage.addProperty("guid", ((PageImpl) page).guid);
+      params.add("page", jsonPage);
+    }
+    sendMessage("startTracing", params);
+  }
+
+  @Override
+  public byte[] stopTracing() {
+    return withLogging("Browser.stopTracing", () -> stopTracingImpl());
+  }
+
+  private byte[] stopTracingImpl() {
+    JsonObject json = sendMessage("stopTracing").getAsJsonObject();
+    return Base64.getDecoder().decode(json.get("binary").getAsString());
+  }
+
   private Page newPageImpl(NewPageOptions options) {
     BrowserContextImpl context = newContext(convertViaJson(options, NewContextOptions.class));
     PageImpl page = context.newPage();
diff --git a/playwright/src/test/java/com/microsoft/playwright/TestBrowserContextProxy.java b/playwright/src/test/java/com/microsoft/playwright/TestBrowserContextProxy.java
index 07c8a2aa4..bf39e37f8 100644
--- a/playwright/src/test/java/com/microsoft/playwright/TestBrowserContextProxy.java
+++ b/playwright/src/test/java/com/microsoft/playwright/TestBrowserContextProxy.java
@@ -20,6 +20,7 @@
 import org.junit.jupiter.api.BeforeAll;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.condition.DisabledIf;
+import org.junit.jupiter.api.condition.EnabledIf;
 
 import java.io.OutputStreamWriter;
 import java.util.Base64;
@@ -37,16 +38,18 @@ static void launchBrowser() {
     launchBrowser(options);
   }
 
+  static boolean isChromiumWindows() {
+    return isChromium() && isWindows;
+  }
+
   @Test
-  void shouldThrowForMissingGlobalProxy() {
-    Browser browser = browserType.launch(createLaunchOptions());
-    try {
+  @EnabledIf(value="isChromiumWindows", disabledReason="Platform-specific")
+  void shouldThrowForMissingGlobalProxyOnChromiumWindows() {
+    try (Browser browser = browserType.launch(createLaunchOptions())) {
       browser.newContext(new Browser.NewContextOptions().setProxy("localhost:" + server.PORT));
       fail("did not throw");
     } catch (PlaywrightException e) {
       assertTrue(e.getMessage().contains("Browser needs to be launched with the global proxy"));
-    } finally {
-      browser.close();
     }
   }
 
diff --git a/playwright/src/test/java/com/microsoft/playwright/TestChromiumTracing.java b/playwright/src/test/java/com/microsoft/playwright/TestChromiumTracing.java
new file mode 100644
index 000000000..e2eab7640
--- /dev/null
+++ b/playwright/src/test/java/com/microsoft/playwright/TestChromiumTracing.java
@@ -0,0 +1,125 @@
+/*
+ * 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.google.gson.Gson;
+import com.google.gson.JsonObject;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.condition.EnabledIf;
+import org.junit.jupiter.api.io.TempDir;
+
+import java.io.FileReader;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+
+import static java.util.Arrays.asList;
+import static org.junit.jupiter.api.Assertions.*;
+
+@EnabledIf(value="com.microsoft.playwright.TestBase#isChromium", disabledReason="Chromium-only API")
+public class TestChromiumTracing extends TestBase {
+  @Test
+  void shouldOutputATrace(@TempDir Path tempDir) {
+    Page page = browser.newPage();
+    Path outputTraceFile = tempDir.resolve("trace.json");
+    browser.startTracing(page, new Browser.StartTracingOptions()
+      .setScreenshots(true).setPath(outputTraceFile));
+    page.navigate(server.PREFIX + "/grid.html");
+    browser.stopTracing();
+    assertTrue(Files.exists(outputTraceFile));
+    page.close();
+  }
+
+  @Test
+  void shouldCreateDirectoriesAsNeeded(@TempDir Path tempDir) {
+    Page page = browser.newPage();
+    Path filePath = tempDir.resolve("these/are/directories/trace.json");
+    browser.startTracing(page, new Browser.StartTracingOptions()
+      .setScreenshots(true).setPath(filePath));
+    page.navigate(server.PREFIX + "/grid.html");
+    browser.stopTracing();
+    assertTrue(Files.exists(filePath));
+    page.close();
+  }
+
+  @Test
+  void shouldRunWithCustomCategoriesIfProvided(@TempDir Path tempDir) throws IOException {
+    Page page = browser.newPage();
+    Path outputTraceFile = tempDir.resolve("trace.json");
+    browser.startTracing(page, new Browser.StartTracingOptions()
+      .setPath(outputTraceFile)
+      .setCategories(asList("disabled-by-default-v8.cpu_profiler.hires")));
+    browser.stopTracing();
+    byte[] data = Files.readAllBytes(outputTraceFile);
+    JsonObject traceJson = new Gson().fromJson(new FileReader(outputTraceFile.toFile()), JsonObject.class);
+    assertTrue(traceJson.getAsJsonObject("metadata").get("trace-config")
+      .getAsString().contains("disabled-by-default-v8.cpu_profiler.hires"));
+    page.close();
+  }
+
+  @Test
+  void shouldThrowIfTracingOnTwoPages(@TempDir Path tempDir) {
+    Page page = browser.newPage();
+    Path outputTraceFile = tempDir.resolve("trace.json");
+    browser.startTracing(page, new Browser.StartTracingOptions()
+        .setPath(outputTraceFile));
+    Page newPage = browser.newPage();
+    try {
+      browser.startTracing(newPage, new Browser.StartTracingOptions()
+        .setPath(outputTraceFile));
+      fail("did not throw");
+    } catch (PlaywrightException e) {
+    }
+    newPage.close();
+    browser.stopTracing();
+    page.close();
+  }
+
+  @Test
+  void shouldReturnABuffer(@TempDir Path tempDir) throws IOException {
+    Page page = browser.newPage();
+    Path outputTraceFile = tempDir.resolve("trace.json");
+    browser.startTracing(page, new Browser.StartTracingOptions()
+      .setScreenshots(true).setPath(outputTraceFile));
+    page.navigate(server.PREFIX + "/grid.html");
+    byte[] trace = browser.stopTracing();
+    byte[] buf = Files.readAllBytes(outputTraceFile);
+    assertArrayEquals(buf, trace);
+    page.close();
+  }
+
+  @Test
+  void shouldWorkWithoutOptions() {
+    Page page = browser.newPage();
+    browser.startTracing(page);
+    page.navigate(server.PREFIX + "/grid.html");
+    byte[] trace = browser.stopTracing();
+    assertNotNull(trace);
+    page.close();
+  }
+
+  @Test
+  void shouldSupportABufferWithoutAPath() {
+    Page page = browser.newPage();
+    browser.startTracing(page, new Browser.StartTracingOptions().setScreenshots(true));
+    page.navigate(server.PREFIX + "/grid.html");
+    byte[] trace = browser.stopTracing();
+    assertTrue(new String(trace, StandardCharsets.UTF_8).contains("screenshot"));
+    page.close();
+  }
+}
diff --git a/scripts/CLI_VERSION b/scripts/CLI_VERSION
index 25738e33b..c6984e89c 100644
--- a/scripts/CLI_VERSION
+++ b/scripts/CLI_VERSION
@@ -1 +1 @@
-1.11.0-next-1619459952000
+1.11.0-next-1620080583000