老板让搞下线上业务的 ui 监控,之前是写 selenium 然后引入一个代理工具来搞,在测 ui 自动化的同时,来监测接口的信息,
也可以达到现在使用的工具:playwright 差不多的效果,但使用下来,明显 playwright 更好用些
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>examples</artifactId>
<version>0.1-SNAPSHOT</version>
<name>Playwright Client Examples</name>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>com.microsoft.playwright</groupId>
<artifactId>playwright</artifactId>
<version>1.28.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.10.1</version>
</plugin>
</plugins>
</build>
</project>
package org.example;
import com.microsoft.playwright.*;
public class App {
public static void main(String[] args) {
try (Playwright playwright = Playwright.create()) {
Browser browser = playwright.chromium().launch();
Page page = browser.newPage();
page.navigate("http://playwright.dev");
System.out.println(page.title());
}
}
}
运行这个 maven 工程的时候,会自动安装 playwright 的,如果没有,也可以使用命令来运行 maven 工程
mvn exec:java -e -Dexec.mainClass=com.microsoft.playwright.CLI -Dexec.args="install"
# 一般不需要执行这个指令,至少我这边的环境没有用到下面个命令,可能在linux的机器上会需要吧
mvn exec:java -e -Dexec.mainClass=com.microsoft.playwright.CLI -Dexec.args="install-deps"
mvn exec:java -e -Dexec.mainClass=com.microsoft.playwright.CLI
# 个人感觉这个录制工具还挺好用的,就是debug java文件好像没那么好使,官方有例子是js的
mvn exec:java -e -Dexec.mainClass=com.microsoft.playwright.CLI -Dexec.args="codegen"
Playwright playwright = null;
Browser browser = null;
BrowserContext context = null;
Page page = null;
Browser.NewContextOptions newContextOptions = new Browser.NewContextOptions().setViewportSize(1440, 875);
try {
playwright = Playwright.create();
browser = playwright.chromium().launch(new BrowserType.LaunchOptions()
// .setHeadless(false)
// .setDevtools(true)
.setSlowMo(1800));
// 不同的context的配置,理论上可能是一样的,例如浏览器的尺寸
context = browser.newContext(newContextOptions);
page = context.newPage();
page.setDefaultTimeout(60000);
public enum WaitUntilState {
LOAD,
DOMCONTENTLOADED,
NETWORKIDLE,
COMMIT;
private WaitUntilState() {
}
}
页面加载分 4 个阶段,依次是 COMMIT, DOMCONTENTLOADED, LOAD, NETWORKIDLE
设置方法:
Page.NavigateOptions options = new Page.NavigateOptions();
options.setWaitUntil(WaitUntilState.LOAD);
page.navigate("https://flowmore.pingpongx.com/entrance/signin", options);
page.screenshot(new Page.ScreenshotOptions().setPath(Paths.get("/tmp/ui-monitor/" + getFormattedTime() + ".png")));
BrowserContext context = browser.newContext(new Browser.NewContextOptions()
.setRecordVideoDir(Paths.get("videos/"))
.setRecordVideoSize(640, 480));
page.locator("#payment-link").getByText("外贸收款").click();
page.getByRole(AriaRole.TEXTBOX, new Page.GetByRoleOptions().setName("付款人名称/收款账户")).click();
page.getByText("提现", new Page.GetByTextOptions().setExact(true)).first().hover();
// Select one file
page.getByLabel("Upload file").setInputFiles(Paths.get("myfile.pdf"));
// Select multiple files
page.getByLabel("Upload files").setInputFiles(new Path[] {Paths.get("file1.txt"), Paths.get("file2.txt")});
// Remove all the selected files
page.getByLabel("Upload file").setInputFiles(new Path[0]);
// Upload buffer from memory
page.getByLabel("Upload file").setInputFiles(new FilePayload(
"file.txt", "text/plain", "this is test".getBytes(StandardCharsets.UTF_8)));
Download download = page.waitForDownload(() -> {
// Perform the action that initiates download
page.locator("button#delayed-download").click();
});
// Wait for the download process to complete
Path path = download.path();
System.out.println(download.path());
// Save downloaded file somewhere
download.saveAs(Paths.get("/path/to/save/download/at.txt"));
page.onDownload(download -> System.out.println(download.path()));
// A primitive value.
page.evaluate("num => num", 42);
// An array.
page.evaluate("array => array.length", Arrays.asList(1, 2, 3));
// An object.
Map<String, Object> obj = new HashMap<>();
obj.put("foo", "bar");
page.evaluate("object => object.foo", obj);
// A single handle.
ElementHandle button = page.evaluate("window.button");
page.evaluate("button => button.textContent", button);
// Alternative notation using elementHandle.evaluate.
button.evaluate("(button, from) => button.textContent.substring(from)", 5);
// Object with multiple handles.
ElementHandle button1 = page.evaluate("window.button1");
ElementHandle button2 = page.evaluate("window.button2");
Map<String, ElementHandle> arg = new HashMap<>();
arg.put("button1", button1);
arg.put("button2", button2);
page.evaluate("o => o.button1.textContent + o.button2.textContent", arg);
// Object destructuring works. Note that property names must match
// between the destructured object and the argument.
// Also note the required parenthesis.
Map<String, ElementHandle> arg = new HashMap<>();
arg.put("button1", button1);
arg.put("button2", button2);
page.evaluate("({ button1, button2 }) => button1.textContent + button2.textContent", arg);
// Array works as well. Arbitrary names can be used for destructuring.
// Note the required parenthesis.
page.evaluate(
"([b1, b2]) => b1.textContent + b2.textContent",
Arrays.asList(button1, button2));
// Any non-cyclic mix of serializables and handles works.
Map<String, Object> arg = new HashMap<>();
arg.put("button1", button1);
arg.put("list", Arrays.asList(button2));
arg.put("foo", 0);
page.evaluate(
"x => x.button1.textContent + x.list[0].textContent + String(x.foo)",
arg);
Locator username = page.frameLocator(".frame-class").getByLabel("User Name");
BrowserContext userContext = browser.newContext();
BrowserContext adminContext = browser.newContext();
apiRequestContext = playwright.request().newContext(new APIRequest.NewContextOptions()
// // All requests we send go to this API endpoint.
.setBaseURL("https://xxx.com"));
Map<String, String> data = new HashMap<>();
data.put("appName", "ui-monitor");
apiRequestContext.post("/v2/alert/***", RequestOptions.create().setData(data));
page.route("**/*", route -> {
if ("image".equals(route.request().resourceType()))
route.abort();
else
route.resume();
});
page.route("**/api/fetch_data", route -> route.fulfill(new Route.FulfillOptions()
.setStatus(200)
.setBody(testData)));
page.navigate("https://example.com");
page.route("**/title.html", route -> {
// Fetch original response.
APIResponse response = page.request().fetch(route.request());
// Add a prefix to the title.
String body = response.text();
body = body.replace("<title>", "<title>My prefix:");
Map<String, String> headers = response.headers();
headers.put("content-type": "text/html");
route.fulfill(new Route.FulfillOptions()
// Pass all fields from the response.
.setResponse(response)
// Override response body.
.setBody(body)
// Force content type to be html.
.setHeaders(headers));
});
Consumer<Request> listener = request -> {
if (request.url().contains("https://flowmore.pingpongx.com") && request.url().contains("api") && request.response() != null) {
System.out.println("Request "+ request.url() + " finished, res: " + new String(request.response().body()));
}
};
page.onRequestFinished(listener);
page.onWebSocket(ws -> {
log("WebSocket opened: " + ws.url());
ws.onFrameSent(frameData -> log(frameData.text()));
ws.onFrameReceived(frameData -> log(frameData.text()));
ws.onClose(ws1 -> log("WebSocket closed"));
});
Browser browser = chromium.launch();
BrowserContext context = browser.newContext();
context.tracing().start(new Tracing.StartOptions()
.setScreenshots(true)
.setSnapshots(true));
Page page = context.newPage();
page.navigate("https://playwright.dev");
context.tracing().stop(new Tracing.StopOptions()
.setPath(Paths.get("trace.zip")));
官方文档之 java 版本: https://playwright.dev/java/docs/cli