agentsclimarketplace

Playwright java e2e

Skill findscripter/everything-skills/02-engineering/playwright-java-e2e

类书式 AI Agent 技能大典 · 精选/中文化/互见成网的 500+ 开源技能,可作为 Claude Code 插件市场一键安装。A curated, cross-referenced encyclopedia of 500+ open-source agent skills.

Install
npx -y skills add findscripter/everything-skills --skill playwright-java-e2e

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

2 things to look at

  • no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
  • 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

What its author says it does

Copied from the file, not written here

当用 Java 写浏览器端到端(E2E)测试、需要并行/跨浏览器/可追溯报告时使用;做基于页面对象模型(POM)+JUnit 5+Allure 的可落地测试脚手架与页面类/用例代码;不适用于纯 API 测试(用 REST Assured)或单元测试。触发词:Playwright Java、POM、JUnit5、Allure、跨浏览器、ThreadLocal、trace、并行测试

The file declares its own license as MIT. That is the author’s claim about this one file, and it is not the same thing as the license GitHub reports for the repository, which is listed with the other numbers below.

SKILL.md

11.1 KB, as published. Nobody here has run it

何时使用

适用:

  • 从零搭建 Playwright Java 项目脚手架(Java 17+ / Playwright 1.44+)。
  • 编写页面对象(Page Object)类或 JUnit 5 用例类。
  • 跨浏览器测试、并行执行、Allure 报告、trace/视频取证。
  • 修复 flaky 测试:把 Thread.sleep() 换成显式等待。
  • 在 CI/CD(GitHub Actions、Jenkins、Docker)中接入 Playwright。
  • 单个用例内混合 API 调用与 UI 断言(hybrid 测试)。
  • 用户提到「POM」「BrowserContext」「Playwright fixtures」「traces」。

不该用(负边界):

  • 纯 API 测试套件、无任何 UI 交互 —— 用 REST Assured。
  • 普通单元测试 / 组件测试 —— 用 JUnit + Mockito,无需启动浏览器。
  • 需求、权限、成功标准不清时,先停下来澄清,别硬写。

步骤 / 指令

  1. 先选方案,再写代码。按下表对号入座:
用户诉求方案
从零新项目完整脚手架(pom.xml + 目录结构)
单个功能测试一个 POM 页面类 + 一个 JUnit5 用例类
API+UI 混合APIRequestContextPage 并用
跨浏览器@MethodSource 参数化浏览器名
修 flakysleep 换成 waitFor / waitForResponse
CI 接入流水线里跑 install --with-deps
并行执行junit-platform.properties + ThreadLocal
富报告Allure + trace + 录屏
  1. 固定目录结构(新项目一律照此布局):
src/test/java/com/company/tests/
├── base/   BaseTest.java, BasePage.java
├── pages/  LoginPage.java
├── tests/  LoginTest.java
├── utils/  TestDataFactory.java, WaitUtils.java
└── config/ ConfigReader.java
src/test/resources/
├── test.properties
├── junit-platform.properties
└── testdata/users.json
pom.xml
  1. 搭线程安全的 BaseTest:用 ThreadLocal 持有 Playwright → Browser → BrowserContext → Page 整条链;@BeforeEach 建链 + 开 tracing,@AfterEach 停 tracing 落盘后逐层关闭。浏览器由 -Dbrowser 系统属性切换:
public class BaseTest {
    protected static ThreadLocal<Playwright>     playwrightTL = new ThreadLocal<>();
    protected static ThreadLocal<Browser>        browserTL    = new ThreadLocal<>();
    protected static ThreadLocal<BrowserContext> contextTL    = new ThreadLocal<>();
    protected static ThreadLocal<Page>           pageTL       = new ThreadLocal<>();
    protected Page page() { return pageTL.get(); }

    @BeforeEach
    void setUp() {
        Playwright pw = Playwright.create();
        playwrightTL.set(pw);
        Browser browser = resolveBrowser(pw).launch(
            new BrowserType.LaunchOptions().setHeadless(ConfigReader.isHeadless()));
        browserTL.set(browser);
        BrowserContext ctx = browser.newContext(new Browser.NewContextOptions()
            .setViewportSize(1920, 1080)
            .setRecordVideoDir(Paths.get("target/videos/"))
            .setLocale("en-US"));
        ctx.tracing().start(new Tracing.StartOptions().setScreenshots(true).setSnapshots(true));
        contextTL.set(ctx);
        pageTL.set(ctx.newPage());
    }

    @AfterEach
    void tearDown(TestInfo info) {
        String name = info.getDisplayName().replaceAll("[^a-zA-Z0-9]", "_");
        contextTL.get().tracing().stop(new Tracing.StopOptions()
            .setPath(Paths.get("target/traces/" + name + ".zip")));
        pageTL.get().close(); contextTL.get().close();
        browserTL.get().close(); playwrightTL.get().close();
    }

    private BrowserType resolveBrowser(Playwright pw) {
        return switch (System.getProperty("browser", "chromium").toLowerCase()) {
            case "firefox" -> pw.firefox();
            case "webkit"  -> pw.webkit();
            default        -> pw.chromium();
        };
    }
}
  1. 写页面对象类:所有 Locator 声明为字段(不在动作方法里内联),优先 getByRole / getByLabel / getByTestId;导航方法返回下一个页面对象以支持链式调用:
public class LoginPage extends BasePage {
    private final Locator emailInput, passwordInput, loginButton, errorMessage;
    public LoginPage(Page page) {
        super(page);
        emailInput    = page.getByLabel("Email address");
        passwordInput = page.getByLabel("Password");
        loginButton   = page.getByRole(AriaRole.BUTTON,
                            new Page.GetByRoleOptions().setName("Sign in"));
        errorMessage  = page.getByTestId("login-error");
    }
    @Override protected String getUrl() { return "/login"; }

    public DashboardPage loginAs(String email, String password) {
        fill(emailInput, email); fill(passwordInput, password);
        clickAndWaitForNav(loginButton);
        return new DashboardPage(page);
    }
    public LoginPage loginExpectingError(String email, String password) {
        fill(emailInput, email); fill(passwordInput, password);
        loginButton.click(); errorMessage.waitFor();
        return this;
    }
    public String getErrorMessage() { return errorMessage.textContent(); }
}
  1. 写带 Allure 注解的用例:@ExtendWith(AllureJunit5.class),用 @Severity / @DisplayName 标注,多字段校验用 SoftAssertions,多场景用 @ParameterizedTest + @MethodSource
@ExtendWith(AllureJunit5.class)
class LoginTest extends BaseTest {
    private LoginPage loginPage;
    @BeforeEach void open() { loginPage = new LoginPage(page()); loginPage.navigate(); }

    @Test @Severity(SeverityLevel.BLOCKER)
    @DisplayName("有效凭据应跳转到 dashboard")
    void shouldLoginWithValidCredentials() {
        User u = TestDataFactory.getDefaultUser();
        DashboardPage dash = loginPage.loginAs(u.email(), u.password());
        assertThat(page()).hasURL(Pattern.compile(".*/dashboard"));
        assertThat(dash.getWelcomeBanner()).containsText("Welcome, " + u.firstName());
    }
}
  1. 并行执行配置 src/test/resources/junit-platform.properties
junit.jupiter.execution.parallel.enabled=true
junit.jupiter.execution.parallel.mode.default=concurrent
junit.jupiter.execution.parallel.config.strategy=fixed
junit.jupiter.execution.parallel.config.fixed.parallelism=4

示例

示例一 · API+UI 混合(用 API 准备数据,比走 UI 快):

APIRequestContext api = page().context().request();
APIResponse resp = api.post("/api/orders", RequestOptions.create()
    .setHeader("Authorization", "Bearer " + authToken)
    .setData(Map.of("productId", "SKU-001", "quantity", 2)));
assertThat(resp).isOK();
String orderId = new JsonParser().parse(resp.text())
    .getAsJsonObject().get("id").getAsString();
OrdersPage orders = new OrdersPage(page()); orders.navigate();
assertThat(orders.getOrderRowById(orderId)).isVisible();

示例二 · 网络打桩(mock 后端故障):

page().route("**/api/products", route -> route.fulfill(new Route.FulfillOptions()
    .setStatus(503).setBody("{\"error\":\"Service Unavailable\"}")
    .setContentType("application/json")));
new ProductsPage(page()).navigate();
assertThat(products.getErrorBanner())
    .hasText("We're having trouble loading products. Please try again.");

示例三 · 跨浏览器并行:

@ParameterizedTest @MethodSource("browsers")
void shouldRenderCheckoutOnAllBrowsers(String browserName) {
    System.setProperty("browser", browserName);
    new CheckoutPage(page()).navigate();
    assertThat(page().locator(".checkout-form")).isVisible();
}
static Stream<String> browsers() { return Stream.of("chromium", "firefox", "webkit"); }

示例四 · GitHub Actions 流水线关键步骤:

- name: Install Playwright browsers
  run: mvn exec:java -e -Dexec.mainClass=com.microsoft.playwright.CLI -Dexec.args="install --with-deps"
- name: Run tests
  run: mvn test -Dbrowser=${{ matrix.browser }} -Dheadless=true
- name: Upload traces on failure
  uses: actions/upload-artifact@v4
  if: failure()
  with: { name: playwright-traces, path: target/traces/ }
- name: Upload Allure results
  uses: actions/upload-artifact@v4
  if: always()
  with: { name: allure-results, path: target/allure-results/ }

注意事项

要做(✅):

  • 并行套件每个用例都用 ThreadLocal<Page>,绝不跨线程共享 Page
  • Locator 字段全部声明在页面类顶部。
  • 导航方法返回下一个页面对象(链式调用)。
  • assertThat(locator) —— 它会自动重试直到超时。
  • 优先 getByRole / getByLabel / getByTestId 定位。
  • tracing 在 @BeforeEach 开、@AfterEach 停并落盘(不是 @AfterAll)。
  • 同页多字段校验用 SoftAssertions
  • 跨类复用登录态:保存 storageState 跳过重复登录。

不要做(❌):

  • 不用 Thread.sleep(),换 waitFor() / waitForResponse()
  • 不硬编码 baseURL,统一 ConfigReader.getBaseUrl()
  • 不在页面对象内部 Playwright.create()
  • 动态/频繁变更的元素不用 XPath。

常见坑:

  • 并行随机失败 → 检查是否每个用例独立建 Playwright→Browser→Context→Page 链。
  • isVisible() 超时 → .setTimeout(10_000) 或在 BaseTestcontext.setDefaultTimeout()
  • 加了 sleep 仍 flaky → 换 waitForResponse("**/api/endpoint", () -> action()) 或断言自动轮询。
  • trace zip 为空 → 确认 start() 在动作前、stop()@AfterEach
  • Allure 报告空白/缺步骤 → 在 maven-surefire-plugin<argLine> 里加 AspectJ agent。
  • storageState 过期跳登录页 → 重跑 AuthSetup 重生成 target/auth/user-state.json

互见

  • REST Assured(Java)—— 纯 API 测试套件,无 UI 交互时用。
  • Selenium(Java)—— 旧方案;新项目一律优先 Playwright。
  • Allure 报告 —— 注解、分类、历史趋势深入。
  • Testcontainers(Java)—— 测试需要真实数据库/服务时配合本技能。
  • GitHub Actions CI —— 构建多浏览器矩阵流水线。

采编自 sickn33/antigravity-awesome-skills(MIT 许可),原作者 amalsam18,已按本仓库 SCHEMA 适配重写。

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.