mirror of
https://github.com/microsoft/playwright-java.git
synced 2025-12-28 02:15:15 +00:00
feat: roll driver, implement context tracing and network APIs (#444)
This commit is contained in:
parent
bcf879b8f0
commit
02bd360319
@ -81,6 +81,55 @@ public interface BrowserContext extends AutoCloseable {
|
||||
*/
|
||||
void offPage(Consumer<Page> handler);
|
||||
|
||||
/**
|
||||
* Emitted when a request is issued from any pages created through this context. The [request] object is read-only. To only
|
||||
* listen for requests from a particular page, use {@link Page#onRequest Page.onRequest()}.
|
||||
*
|
||||
* <p> In order to intercept and mutate requests, see {@link BrowserContext#route BrowserContext.route()} or {@link Page#route
|
||||
* Page.route()}.
|
||||
*/
|
||||
void onRequest(Consumer<Request> handler);
|
||||
/**
|
||||
* Removes handler that was previously added with {@link #onRequest onRequest(handler)}.
|
||||
*/
|
||||
void offRequest(Consumer<Request> handler);
|
||||
|
||||
/**
|
||||
* Emitted when a request fails, for example by timing out. To only listen for failed requests from a particular page, use
|
||||
* {@link Page#onRequestFailed Page.onRequestFailed()}.
|
||||
*
|
||||
* <p> <strong>NOTE:</strong> HTTP Error responses, such as 404 or 503, are still successful responses from HTTP standpoint, so request will complete
|
||||
* with {@link BrowserContext#onRequestFinished BrowserContext.onRequestFinished()} event and not with {@link
|
||||
* BrowserContext#onRequestFailed BrowserContext.onRequestFailed()}.
|
||||
*/
|
||||
void onRequestFailed(Consumer<Request> handler);
|
||||
/**
|
||||
* Removes handler that was previously added with {@link #onRequestFailed onRequestFailed(handler)}.
|
||||
*/
|
||||
void offRequestFailed(Consumer<Request> handler);
|
||||
|
||||
/**
|
||||
* Emitted when a request finishes successfully after downloading the response body. For a successful response, the
|
||||
* sequence of events is {@code request}, {@code response} and {@code requestfinished}. To listen for successful requests from a particular
|
||||
* page, use {@link Page#onRequestFinished Page.onRequestFinished()}.
|
||||
*/
|
||||
void onRequestFinished(Consumer<Request> handler);
|
||||
/**
|
||||
* Removes handler that was previously added with {@link #onRequestFinished onRequestFinished(handler)}.
|
||||
*/
|
||||
void offRequestFinished(Consumer<Request> handler);
|
||||
|
||||
/**
|
||||
* Emitted when [response] status and headers are received for a request. For a successful response, the sequence of events
|
||||
* is {@code request}, {@code response} and {@code requestfinished}. To listen for response events from a particular page, use {@link
|
||||
* Page#onResponse Page.onResponse()}.
|
||||
*/
|
||||
void onResponse(Consumer<Response> handler);
|
||||
/**
|
||||
* Removes handler that was previously added with {@link #onResponse onResponse(handler)}.
|
||||
*/
|
||||
void offResponse(Consumer<Response> handler);
|
||||
|
||||
class ExposeBindingOptions {
|
||||
/**
|
||||
* Whether to pass the argument as a handle, instead of passing by value. When passing a handle, only one argument is
|
||||
@ -664,6 +713,7 @@ public interface BrowserContext extends AutoCloseable {
|
||||
* Returns storage state for this browser context, contains current cookies and local storage snapshot.
|
||||
*/
|
||||
String storageState(StorageStateOptions options);
|
||||
Tracing tracing();
|
||||
/**
|
||||
* Removes a route created with {@link BrowserContext#route BrowserContext.route()}. When {@code handler} is not specified,
|
||||
* removes all routes for the {@code url}.
|
||||
|
||||
@ -111,7 +111,7 @@ public interface BrowserType {
|
||||
*/
|
||||
public BrowserChannel channel;
|
||||
/**
|
||||
* Enable Chromium sandboxing. Defaults to {@code false}.
|
||||
* Enable Chromium sandboxing. Defaults to {@code true}.
|
||||
*/
|
||||
public Boolean chromiumSandbox;
|
||||
/**
|
||||
@ -181,6 +181,10 @@ public interface BrowserType {
|
||||
* disable timeout.
|
||||
*/
|
||||
public Double timeout;
|
||||
/**
|
||||
* If specified, traces are saved into this directory.
|
||||
*/
|
||||
public Path traceDir;
|
||||
|
||||
public LaunchOptions setArgs(List<String> args) {
|
||||
this.args = args;
|
||||
@ -253,6 +257,10 @@ public interface BrowserType {
|
||||
this.timeout = timeout;
|
||||
return this;
|
||||
}
|
||||
public LaunchOptions setTraceDir(Path traceDir) {
|
||||
this.traceDir = traceDir;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
class LaunchPersistentContextOptions {
|
||||
/**
|
||||
@ -302,8 +310,8 @@ public interface BrowserType {
|
||||
public Map<String, String> env;
|
||||
/**
|
||||
* Path to a browser executable to run instead of the bundled one. If {@code executablePath} is a relative path, then it is
|
||||
* resolved relative to the current working directory. **BEWARE**: Playwright is only guaranteed to work with the bundled
|
||||
* Chromium, Firefox or WebKit, use at your own risk.
|
||||
* resolved relative to the current working directory. Note that Playwright only works with the bundled Chromium, Firefox
|
||||
* or WebKit, use at your own risk.
|
||||
*/
|
||||
public Path executablePath;
|
||||
/**
|
||||
@ -407,7 +415,6 @@ public interface BrowserType {
|
||||
public ScreenSize screenSize;
|
||||
/**
|
||||
* Slows down Playwright operations by the specified amount of milliseconds. Useful so that you can see what is going on.
|
||||
* Defaults to 0.
|
||||
*/
|
||||
public Double slowMo;
|
||||
/**
|
||||
@ -421,6 +428,10 @@ public interface BrowserType {
|
||||
* metaZones.txt</a> for a list of supported timezone IDs.
|
||||
*/
|
||||
public String timezoneId;
|
||||
/**
|
||||
* If specified, traces are saved into this directory.
|
||||
*/
|
||||
public Path traceDir;
|
||||
/**
|
||||
* Specific user agent to use in this context.
|
||||
*/
|
||||
@ -589,6 +600,10 @@ public interface BrowserType {
|
||||
this.timezoneId = timezoneId;
|
||||
return this;
|
||||
}
|
||||
public LaunchPersistentContextOptions setTraceDir(Path traceDir) {
|
||||
this.traceDir = traceDir;
|
||||
return this;
|
||||
}
|
||||
public LaunchPersistentContextOptions setUserAgent(String userAgent) {
|
||||
this.userAgent = userAgent;
|
||||
return this;
|
||||
|
||||
@ -23,8 +23,8 @@ import java.util.*;
|
||||
/**
|
||||
* {@code Download} objects are dispatched by page via the {@link Page#onDownload Page.onDownload()} event.
|
||||
*
|
||||
* <p> All the downloaded files belonging to the browser context are deleted when the browser context is closed. All downloaded
|
||||
* files are deleted when the browser closes.
|
||||
* <p> If {@code downloadsPath} isn't specified, all the downloaded files belonging to the browser context are deleted when the
|
||||
* browser context is closed. And all downloaded files are deleted when the browser closes.
|
||||
*
|
||||
* <p> Download event is emitted once the download starts. Download path becomes available once download completes:
|
||||
* <pre>{@code
|
||||
@ -59,15 +59,20 @@ public interface Download {
|
||||
* Returns download error if any. Will wait for the download to finish if necessary.
|
||||
*/
|
||||
String failure();
|
||||
/**
|
||||
* Get the page that the download belongs to.
|
||||
*/
|
||||
Page page();
|
||||
/**
|
||||
* Returns path to the downloaded file in case of successful download. The method will wait for the download to finish if
|
||||
* necessary. The method throws when connected remotely.
|
||||
*/
|
||||
Path path();
|
||||
/**
|
||||
* Saves the download to a user-specified path. It is safe to call this method while the download is still in progress.
|
||||
* Copy the download to a user-specified path. It is safe to call this method while the download is still in progress. Will
|
||||
* wait for the download to finish if necessary.
|
||||
*
|
||||
* @param path Path where the download should be saved.
|
||||
* @param path Path where the download should be copied.
|
||||
*/
|
||||
void saveAs(Path path);
|
||||
/**
|
||||
|
||||
@ -3142,7 +3142,8 @@ public interface Page extends AutoCloseable {
|
||||
void press(String selector, String key, PressOptions options);
|
||||
/**
|
||||
* The method finds an element matching the specified selector within the page. If no elements match the selector, the
|
||||
* return value resolves to {@code null}.
|
||||
* return value resolves to {@code null}. To wait for an element on the page, use {@link Page#waitForSelector
|
||||
* Page.waitForSelector()}.
|
||||
*
|
||||
* <p> Shortcut for main frame's {@link Frame#querySelector Frame.querySelector()}.
|
||||
*
|
||||
|
||||
110
playwright/src/main/java/com/microsoft/playwright/Tracing.java
Normal file
110
playwright/src/main/java/com/microsoft/playwright/Tracing.java
Normal file
@ -0,0 +1,110 @@
|
||||
/*
|
||||
* 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 java.nio.file.Path;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* API for collecting and saving Playwright traces. Playwright traces can be opened using the Playwright CLI after
|
||||
* Playwright script runs.
|
||||
*
|
||||
* <p> Start with specifying the folder traces will be stored in:
|
||||
* <pre>{@code
|
||||
* Browser browser = chromium.launch(new BrowserType.LaunchOptions().setTraceDir("trace"));
|
||||
* BrowserContext context = browser.newContext();
|
||||
* context.tracing.start(page, new Tracing.StartOptions()
|
||||
* .setName("trace")
|
||||
* .setScreenshots(true)
|
||||
* .setSnapshots(true);
|
||||
* Page page = context.newPage();
|
||||
* page.goto("https://playwright.dev");
|
||||
* context.tracing.stop();
|
||||
* context.tracing.export(Paths.get("trace.zip")))
|
||||
* }</pre>
|
||||
*/
|
||||
public interface Tracing {
|
||||
class StartOptions {
|
||||
/**
|
||||
* If specified, the trace is going to be saved into the file with the given name inside the {@code traceDir} folder specified in
|
||||
* {@link BrowserType#launch BrowserType.launch()}.
|
||||
*/
|
||||
public String name;
|
||||
/**
|
||||
* Whether to capture screenshots during tracing. Screenshots are used to build a timeline preview.
|
||||
*/
|
||||
public Boolean screenshots;
|
||||
/**
|
||||
* Whether to capture DOM snapshot on every action.
|
||||
*/
|
||||
public Boolean snapshots;
|
||||
|
||||
public StartOptions setName(String name) {
|
||||
this.name = name;
|
||||
return this;
|
||||
}
|
||||
public StartOptions setScreenshots(boolean screenshots) {
|
||||
this.screenshots = screenshots;
|
||||
return this;
|
||||
}
|
||||
public StartOptions setSnapshots(boolean snapshots) {
|
||||
this.snapshots = snapshots;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Export trace into the file with the given name. Should be called after the tracing has stopped.
|
||||
*
|
||||
* @param path File to save the trace into.
|
||||
*/
|
||||
void export(Path path);
|
||||
/**
|
||||
* Start tracing.
|
||||
* <pre>{@code
|
||||
* context.tracing.start(page, new Tracing.StartOptions()
|
||||
* .setName("trace")
|
||||
* .setScreenshots(true)
|
||||
* .setSnapshots(true);
|
||||
* Page page = context.newPage();
|
||||
* page.goto('https://playwright.dev');
|
||||
* context.tracing.stop();
|
||||
* context.tracing.export(Paths.get("trace.zip")))
|
||||
* }</pre>
|
||||
*/
|
||||
default void start() {
|
||||
start(null);
|
||||
}
|
||||
/**
|
||||
* Start tracing.
|
||||
* <pre>{@code
|
||||
* context.tracing.start(page, new Tracing.StartOptions()
|
||||
* .setName("trace")
|
||||
* .setScreenshots(true)
|
||||
* .setSnapshots(true);
|
||||
* Page page = context.newPage();
|
||||
* page.goto('https://playwright.dev');
|
||||
* context.tracing.stop();
|
||||
* context.tracing.export(Paths.get("trace.zip")))
|
||||
* }</pre>
|
||||
*/
|
||||
void start(StartOptions options);
|
||||
/**
|
||||
* Stop tracing.
|
||||
*/
|
||||
void stop();
|
||||
}
|
||||
|
||||
@ -19,10 +19,7 @@ package com.microsoft.playwright.impl;
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.microsoft.playwright.BrowserContext;
|
||||
import com.microsoft.playwright.Page;
|
||||
import com.microsoft.playwright.PlaywrightException;
|
||||
import com.microsoft.playwright.Route;
|
||||
import com.microsoft.playwright.*;
|
||||
import com.microsoft.playwright.options.BindingCallback;
|
||||
import com.microsoft.playwright.options.Cookie;
|
||||
import com.microsoft.playwright.options.FunctionCallback;
|
||||
@ -47,6 +44,7 @@ import static java.util.Arrays.asList;
|
||||
|
||||
class BrowserContextImpl extends ChannelOwner implements BrowserContext {
|
||||
private final BrowserImpl browser;
|
||||
private final TracingImpl tracing;
|
||||
final List<PageImpl> pages = new ArrayList<>();
|
||||
final Router routes = new Router();
|
||||
private boolean isClosedOrClosing;
|
||||
@ -59,6 +57,10 @@ class BrowserContextImpl extends ChannelOwner implements BrowserContext {
|
||||
enum EventType {
|
||||
CLOSE,
|
||||
PAGE,
|
||||
REQUEST,
|
||||
REQUESTFAILED,
|
||||
REQUESTFINISHED,
|
||||
RESPONSE,
|
||||
}
|
||||
|
||||
BrowserContextImpl(ChannelOwner parent, String type, String guid, JsonObject initializer) {
|
||||
@ -68,6 +70,7 @@ class BrowserContextImpl extends ChannelOwner implements BrowserContext {
|
||||
} else {
|
||||
browser = null;
|
||||
}
|
||||
this.tracing = new TracingImpl(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -90,6 +93,46 @@ class BrowserContextImpl extends ChannelOwner implements BrowserContext {
|
||||
listeners.remove(EventType.PAGE, handler);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRequest(Consumer<Request> handler) {
|
||||
listeners.add(EventType.REQUEST, handler);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void offRequest(Consumer<Request> handler) {
|
||||
listeners.remove(EventType.REQUEST, handler);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRequestFailed(Consumer<Request> handler) {
|
||||
listeners.add(EventType.REQUESTFAILED, handler);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void offRequestFailed(Consumer<Request> handler) {
|
||||
listeners.remove(EventType.REQUESTFAILED, handler);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRequestFinished(Consumer<Request> handler) {
|
||||
listeners.add(EventType.REQUESTFINISHED, handler);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void offRequestFinished(Consumer<Request> handler) {
|
||||
listeners.remove(EventType.REQUESTFINISHED, handler);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResponse(Consumer<Response> handler) {
|
||||
listeners.add(EventType.RESPONSE, handler);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void offResponse(Consumer<Response> handler) {
|
||||
listeners.remove(EventType.RESPONSE, handler);
|
||||
}
|
||||
|
||||
private <T> T waitForEventWithTimeout(EventType eventType, Runnable code, Double timeout) {
|
||||
List<Waitable<T>> waitables = new ArrayList<>();
|
||||
waitables.add(new WaitableEvent<>(listeners, eventType));
|
||||
@ -356,6 +399,11 @@ class BrowserContextImpl extends ChannelOwner implements BrowserContext {
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public Tracing tracing() {
|
||||
return tracing;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unroute(String url, Consumer<Route> handler) {
|
||||
unroute(new UrlMatcher(url), handler);
|
||||
@ -418,6 +466,47 @@ class BrowserContextImpl extends ChannelOwner implements BrowserContext {
|
||||
if (binding != null) {
|
||||
bindingCall.call(binding);
|
||||
}
|
||||
} else if ("request".equals(event)) {
|
||||
String guid = params.getAsJsonObject("request").get("guid").getAsString();
|
||||
RequestImpl request = connection.getExistingObject(guid);
|
||||
listeners.notify(EventType.REQUEST, request);
|
||||
if (params.has("page")) {
|
||||
PageImpl page = connection.getExistingObject(params.getAsJsonObject("page").get("guid").getAsString());
|
||||
page.listeners.notify(PageImpl.EventType.REQUEST, request);
|
||||
}
|
||||
} else if ("requestFailed".equals(event)) {
|
||||
String guid = params.getAsJsonObject("request").get("guid").getAsString();
|
||||
RequestImpl request = connection.getExistingObject(guid);
|
||||
if (params.has("failureText")) {
|
||||
request.failure = params.get("failureText").getAsString();
|
||||
}
|
||||
if (request.timing != null) {
|
||||
request.timing.responseEnd = params.get("responseEndTiming").getAsDouble();
|
||||
}
|
||||
listeners.notify(EventType.REQUESTFAILED, request);
|
||||
if (params.has("page")) {
|
||||
PageImpl page = connection.getExistingObject(params.getAsJsonObject("page").get("guid").getAsString());
|
||||
page.listeners.notify(PageImpl.EventType.REQUESTFAILED, request);
|
||||
}
|
||||
} else if ("requestFinished".equals(event)) {
|
||||
String guid = params.getAsJsonObject("request").get("guid").getAsString();
|
||||
RequestImpl request = connection.getExistingObject(guid);
|
||||
if (request.timing != null) {
|
||||
request.timing.responseEnd = params.get("responseEndTiming").getAsDouble();
|
||||
}
|
||||
listeners.notify(EventType.REQUESTFINISHED, request);
|
||||
if (params.has("page")) {
|
||||
PageImpl page = connection.getExistingObject(params.getAsJsonObject("page").get("guid").getAsString());
|
||||
page.listeners.notify(PageImpl.EventType.REQUESTFINISHED, request);
|
||||
}
|
||||
} else if ("response".equals(event)) {
|
||||
String guid = params.getAsJsonObject("response").get("guid").getAsString();
|
||||
Response response = connection.getExistingObject(guid);
|
||||
listeners.notify(EventType.RESPONSE, response);
|
||||
if (params.has("page")) {
|
||||
PageImpl page = connection.getExistingObject(params.getAsJsonObject("page").get("guid").getAsString());
|
||||
page.listeners.notify(PageImpl.EventType.RESPONSE, response);
|
||||
}
|
||||
} else if ("close".equals(event)) {
|
||||
didClose();
|
||||
}
|
||||
|
||||
@ -18,15 +18,18 @@ package com.microsoft.playwright.impl;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import com.microsoft.playwright.Download;
|
||||
import com.microsoft.playwright.Page;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.Path;
|
||||
|
||||
class DownloadImpl extends LoggingSupport implements Download {
|
||||
private final PageImpl page;
|
||||
private final ArtifactImpl artifact;
|
||||
private final JsonObject initializer;
|
||||
|
||||
DownloadImpl(ArtifactImpl artifact, JsonObject initializer) {
|
||||
DownloadImpl(PageImpl page, ArtifactImpl artifact, JsonObject initializer) {
|
||||
this.page = page;
|
||||
this.artifact = artifact;
|
||||
this.initializer = initializer;
|
||||
}
|
||||
@ -56,6 +59,11 @@ class DownloadImpl extends LoggingSupport implements Download {
|
||||
return withLogging("Download.failure", () -> artifact.failure());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page page() {
|
||||
return page;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Path path() {
|
||||
return withLogging("Download.path", () -> artifact.pathAfterFinished());
|
||||
|
||||
@ -48,7 +48,7 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
private ViewportSize viewport;
|
||||
private final Router routes = new Router();
|
||||
private final Set<FrameImpl> frames = new LinkedHashSet<>();
|
||||
private final ListenerCollection<EventType> listeners = new ListenerCollection<EventType>() {
|
||||
final ListenerCollection<EventType> listeners = new ListenerCollection<EventType>() {
|
||||
@Override
|
||||
void add(EventType eventType, Consumer<?> listener) {
|
||||
if (eventType == EventType.FILECHOOSER) {
|
||||
@ -145,7 +145,7 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
String artifactGuid = params.getAsJsonObject("artifact").get("guid").getAsString();
|
||||
ArtifactImpl artifact = connection.getExistingObject(artifactGuid);
|
||||
artifact.isRemote = browserContext.browser() != null && browserContext.browser().isRemote;
|
||||
DownloadImpl download = new DownloadImpl(artifact, params);
|
||||
DownloadImpl download = new DownloadImpl(this, artifact, params);
|
||||
listeners.notify(EventType.DOWNLOAD, download);
|
||||
} else if ("fileChooser".equals(event)) {
|
||||
String guid = params.getAsJsonObject("element").get("guid").getAsString();
|
||||
@ -170,25 +170,6 @@ public class PageImpl extends ChannelOwner implements Page {
|
||||
listeners.notify(EventType.LOAD, this);
|
||||
} else if ("domcontentloaded".equals(event)) {
|
||||
listeners.notify(EventType.DOMCONTENTLOADED, this);
|
||||
} else if ("request".equals(event)) {
|
||||
String guid = params.getAsJsonObject("request").get("guid").getAsString();
|
||||
Request request = connection.getExistingObject(guid);
|
||||
listeners.notify(EventType.REQUEST, request);
|
||||
} else if ("requestFailed".equals(event)) {
|
||||
String guid = params.getAsJsonObject("request").get("guid").getAsString();
|
||||
RequestImpl request = connection.getExistingObject(guid);
|
||||
if (params.has("failureText")) {
|
||||
request.failure = params.get("failureText").getAsString();
|
||||
}
|
||||
listeners.notify(EventType.REQUESTFAILED, request);
|
||||
} else if ("requestFinished".equals(event)) {
|
||||
String guid = params.getAsJsonObject("request").get("guid").getAsString();
|
||||
Request request = connection.getExistingObject(guid);
|
||||
listeners.notify(EventType.REQUESTFINISHED, request);
|
||||
} else if ("response".equals(event)) {
|
||||
String guid = params.getAsJsonObject("response").get("guid").getAsString();
|
||||
Response response = connection.getExistingObject(guid);
|
||||
listeners.notify(EventType.RESPONSE, response);
|
||||
} else if ("frameAttached".equals(event)) {
|
||||
String guid = params.getAsJsonObject("frame").get("guid").getAsString();
|
||||
FrameImpl frame = connection.getExistingObject(guid);
|
||||
|
||||
@ -0,0 +1,66 @@
|
||||
/*
|
||||
* 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.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.microsoft.playwright.Tracing;
|
||||
|
||||
import java.nio.file.Path;
|
||||
|
||||
import static com.microsoft.playwright.impl.Serialization.gson;
|
||||
|
||||
class TracingImpl implements Tracing {
|
||||
private final BrowserContextImpl context;
|
||||
|
||||
TracingImpl(BrowserContextImpl context) {
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void export(Path path) {
|
||||
context.withLogging("Tracing.export", () -> exportImpl(path));
|
||||
}
|
||||
|
||||
private void exportImpl(Path path) {
|
||||
JsonObject json = context.sendMessage("tracingExport").getAsJsonObject();
|
||||
ArtifactImpl artifact = context.connection.getExistingObject(json.getAsJsonObject("artifact").get("guid").getAsString());
|
||||
if (context.browser().isRemote) {
|
||||
artifact.isRemote = true;
|
||||
}
|
||||
artifact.saveAs(path);
|
||||
artifact.delete();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start(StartOptions options) {
|
||||
context.withLogging("Tracing.start", () -> startImpl(options));
|
||||
}
|
||||
|
||||
private void startImpl(StartOptions options) {
|
||||
if (options == null) {
|
||||
options = new StartOptions();
|
||||
}
|
||||
JsonObject params = gson().toJsonTree(options).getAsJsonObject();
|
||||
context.sendMessage("tracingStart", params);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
context.withLogging("Tracing.stop", () -> context.sendMessage("tracingStop"));
|
||||
}
|
||||
}
|
||||
@ -25,5 +25,5 @@ public enum BrowserChannel {
|
||||
MSEDGE_BETA,
|
||||
MSEDGE_DEV,
|
||||
MSEDGE_CANARY,
|
||||
FIREFOX_STABLE
|
||||
@Deprecated FIREFOX_STABLE
|
||||
}
|
||||
@ -0,0 +1,93 @@
|
||||
/*
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.microsoft.playwright;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static java.util.Arrays.asList;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
public class TestBrowserContextNetworkEvents extends TestBase {
|
||||
@Test
|
||||
void BrowserContextEventsRequest() {
|
||||
List<String> requests = new ArrayList<>();
|
||||
context.onRequest(request -> requests.add(request.url()));
|
||||
page.navigate(getServer().EMPTY_PAGE);
|
||||
page.setContent("<a target=_blank rel=noopener href='/one-style.html'>yo</a>");
|
||||
Page page1 = context.waitForPage(() -> page.click("a"));
|
||||
page1.waitForLoadState();
|
||||
assertEquals(asList(
|
||||
getServer().EMPTY_PAGE,
|
||||
getServer().PREFIX + "/one-style.html",
|
||||
getServer().PREFIX + "/one-style.css"), requests);
|
||||
}
|
||||
|
||||
@Test
|
||||
void BrowserContextEventsResponse() {
|
||||
List<String> responses = new ArrayList<>();
|
||||
context.onResponse(response -> responses.add(response.url()));
|
||||
page.navigate(getServer().EMPTY_PAGE);
|
||||
page.setContent("<a target=_blank rel=noopener href='/one-style.html'>yo</a>");
|
||||
Page page1 = context.waitForPage(() -> page.click("a"));
|
||||
page1.waitForLoadState();
|
||||
assertEquals(asList(
|
||||
getServer().EMPTY_PAGE,
|
||||
getServer().PREFIX + "/one-style.html",
|
||||
getServer().PREFIX + "/one-style.css"), responses);
|
||||
}
|
||||
|
||||
@Test
|
||||
void BrowserContextEventsRequestFailed() {
|
||||
getServer().setRoute("/one-style.css", exchange -> exchange.getResponseBody().close());
|
||||
List<Request> failedRequests = new ArrayList<>();
|
||||
context.onRequestFailed(request -> failedRequests.add(request));
|
||||
page.navigate(getServer().PREFIX + "/one-style.html");
|
||||
assertEquals(1, failedRequests.size());
|
||||
assertTrue(failedRequests.get(0).url().contains("one-style.css"));
|
||||
assertNull(failedRequests.get(0).response());
|
||||
assertEquals("stylesheet", failedRequests.get(0).resourceType());
|
||||
assertNotNull(failedRequests.get(0).frame());
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
void BrowserContextEventsRequestFinished() {
|
||||
Request[] requestRef = {null};
|
||||
context.onRequestFinished(r -> requestRef[0] = r);
|
||||
Response response = page.navigate(getServer().EMPTY_PAGE);
|
||||
Request request = response.request();
|
||||
assertEquals(getServer().EMPTY_PAGE, request.url());
|
||||
assertNotNull(request.response());
|
||||
assertEquals(request.frame(), page.mainFrame());
|
||||
assertEquals(getServer().EMPTY_PAGE, request.frame().url());
|
||||
assertNull(request.failure());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldFireEventsInProperOrder() {
|
||||
List<String> events = new ArrayList<>();
|
||||
context.onRequest(r -> events.add("request"));
|
||||
context.onResponse(r -> events.add("response"));
|
||||
context.onRequestFinished(r -> events.add("requestfinished"));
|
||||
Response response = page.navigate(getServer().EMPTY_PAGE);
|
||||
assertNull(response.finished());
|
||||
assertEquals(asList("request", "response", "requestfinished"), events);
|
||||
}
|
||||
}
|
||||
@ -97,9 +97,9 @@ public class TestPageEventNetwork extends TestBase {
|
||||
List<String> events = new ArrayList<>();
|
||||
page.onRequest(request -> events.add("request"));
|
||||
page.onResponse(response -> events.add("response"));
|
||||
page.onRequestFinished(r -> events.add("requestfinished"));
|
||||
Response response = page.navigate(getServer().EMPTY_PAGE);
|
||||
assertNull(response.finished());
|
||||
events.add("requestfinished");
|
||||
assertEquals(asList("request", "response", "requestfinished"), events);
|
||||
}
|
||||
|
||||
|
||||
@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.microsoft.playwright;
|
||||
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.io.TempDir;
|
||||
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
public class TestTracing extends TestBase {
|
||||
|
||||
@BeforeAll
|
||||
static void launchBrowser(@TempDir Path tempDir) {
|
||||
BrowserType.LaunchOptions options = createLaunchOptions();
|
||||
options.setTraceDir(tempDir.resolve("trace-dir"));
|
||||
launchBrowser(options);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldCollectTrace1(@TempDir Path tempDir) {
|
||||
context.tracing().start(new Tracing.StartOptions().setName("test")
|
||||
.setScreenshots(true).setSnapshots(true));
|
||||
page.navigate(getServer().EMPTY_PAGE);
|
||||
page.setContent("<button>Click</button>");
|
||||
page.click("'Click'");
|
||||
page.close();
|
||||
context.tracing().stop();
|
||||
Path traceFile = tempDir.resolve("trace.zip");
|
||||
context.tracing().export(traceFile);
|
||||
|
||||
assertTrue(Files.exists(traceFile));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldCollectTwoTraces(@TempDir Path tempDir) {
|
||||
context.tracing().start(new Tracing.StartOptions().setName("test1")
|
||||
.setScreenshots(true).setSnapshots(true));
|
||||
page.navigate(getServer().EMPTY_PAGE);
|
||||
page.setContent("<button>Click</button>");
|
||||
page.click("'Click'");
|
||||
context.tracing().stop();
|
||||
Path traceFile1 = tempDir.resolve("trace1.zip");
|
||||
context.tracing().export(traceFile1);
|
||||
|
||||
context.tracing().start(new Tracing.StartOptions().setName("test2")
|
||||
.setScreenshots(true).setSnapshots(true));
|
||||
page.dblclick("'Click'");
|
||||
page.close();
|
||||
context.tracing().stop();
|
||||
Path traceFile2 = tempDir.resolve("trace2.zip");
|
||||
context.tracing().export(traceFile2);
|
||||
|
||||
assertTrue(Files.exists(traceFile1));
|
||||
assertTrue(Files.exists(traceFile2));
|
||||
}
|
||||
|
||||
}
|
||||
@ -1 +1 @@
|
||||
1.11.0-1620331022000
|
||||
1.12.0-next-1621019018000
|
||||
|
||||
@ -867,7 +867,7 @@ class Interface extends TypeDefinition {
|
||||
if ("Download".equals(jsonName)) {
|
||||
output.add("import java.io.InputStream;");
|
||||
}
|
||||
if (asList("Page", "Frame", "ElementHandle", "FileChooser", "Browser", "BrowserContext", "BrowserType", "Download", "Route", "Selectors", "Video").contains(jsonName)) {
|
||||
if (asList("Page", "Frame", "ElementHandle", "FileChooser", "Browser", "BrowserContext", "BrowserType", "Download", "Route", "Selectors", "Tracing", "Video").contains(jsonName)) {
|
||||
output.add("import java.nio.file.Path;");
|
||||
}
|
||||
output.add("import java.util.*;");
|
||||
@ -1016,6 +1016,10 @@ class Enum extends TypeDefinition {
|
||||
}
|
||||
enumValues.add(value.substring(1, value.length() - 1).replace("-", "_").toUpperCase());
|
||||
}
|
||||
if ("BrowserChannel".equals(jsonName)) {
|
||||
// Firefox stable 'channel' was removed in 1.12.0
|
||||
enumValues.add("@Deprecated FIREFOX_STABLE");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user