Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 87 additions & 0 deletions .claude/skills/playwright-triage/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
---
name: playwright-triage
description: Triage and fix one microsoft/playwright-java issue, or do a maintainer's task, unattended inside the Issue triage GitHub Actions workflow (.github/workflows/triage.yml), which a needs-triage, needs-triage-mac or needs-triage-win label starts, or a maintainer starts by hand with an issue, a task or both. Reproduces the issue on the runner, then leaves a verified fix proposal for the workflow to open as a draft PR, an investigation note for it to post on the issue, or an upstream bug report for a maintainer to file in microsoft/playwright. Only for that workflow.
---

# Playwright Java Issue Triage

You run unattended in the [Issue triage](../../../.github/workflows/triage.yml) workflow of
`microsoft/playwright-java`. First read the shared guide, `$AGENT_GUIDES_DIR/triage.md`, a copy of
[microsoft/playwright/.github/actions/run-agent/guides/triage.md](https://github.com/microsoft/playwright/blob/main/.github/actions/run-agent/guides/triage.md),
and then [AGENTS.md](../../../AGENTS.md), both in full. The guide says how to triage an issue in any
Playwright repo. AGENTS.md says how to build and test this one, and where a regression test goes.
This file covers what neither has: the checkouts, how to run a published version, and the outcome
that this repo adds, an upstream bug report.

## The checkouts

`$REPO_DIR` is the checkout of `microsoft/playwright-java` that the guide describes. The driver is
downloaded (`scripts/download_driver.sh`), the modules are built and installed
(`mvn -B install -D skipTests --no-transfer-progress`), and Chromium, Firefox and WebKit are
installed (`mvn exec:java -e -D exec.mainClass=com.microsoft.playwright.CLI -D exec.args="install --with-deps" -f playwright/pom.xml --no-transfer-progress`).
It is the working directory, and the checkout that the guides refer to. The JDK is JDK 8 (Zulu),
the JDK of the `dev` job in `.github/workflows/test.yml`. The project targets Java 8, so do not use
a newer language feature or library API. The clone is shallow: run
`git fetch --unshallow --tags origin` in it before you bisect or use `git tag --contains`.

`$PLAYWRIGHT_DIR` is a shallow clone of `microsoft/playwright` at `main`. Read there the JavaScript
client that this repo ports, `packages/playwright-core/src/client/`, the API docs in
`docs/src/api/`, and the upstream tests in `tests/page/` and `tests/library/`. It is not at the
commit of the driver: `scripts/DRIVER_VERSION` in `$REPO_DIR` names the driver version, and
`npm view playwright@<version> gitHead` gives its upstream commit; `main` is close to it. The clone
is not built. Before you run an upstream test there, run `npm ci`, `npm run build` and
`npx playwright install` in it. Then `npm run ctest <spec>` runs one spec in Chromium,
`npm run ftest <spec>` in Firefox and `npm run wtest <spec>` in WebKit.

## Published versions

The Maven artifact is `com.microsoft.playwright:playwright`. Release `1.X.0` bundles the driver
named in `scripts/DRIVER_VERSION` at tag `v1.X.0`: `git show v1.64.0:scripts/DRIVER_VERSION` prints
`1.64.1`. Name both versions in the note. The version in `pom.xml` on `main` is a placeholder
snapshot; `scripts/DRIVER_VERSION` says what tip-of-tree runs.

To run a published version, create `$SCRATCH_DIR/playwright-java-<n>/<version>/` with a `pom.xml`
like `examples/pom.xml`: the dependency at `<version>`, and `maven-compiler-plugin` with source and
target `1.8`. Put the repro in a class with a `main` method under `src/main/java/`, and run
`mvn -q compile exec:java -D exec.mainClass=<class>` there. The first `Playwright.create()`
downloads the browsers of that version. To match a change between two releases, compare the driver
versions of the two tags first. When they differ, `git log v<good>..v<bad> -- <path>` in
`$PLAYWRIGHT_DIR` finds the change, after you unshallow that clone too; otherwise
`git log v<good>..v<bad> -- playwright/src/main` in `$REPO_DIR` finds it.

## Upstream bugs

The Java client talks to the Node.js driver that `microsoft/playwright` publishes: the
`playwright-core` npm package that `scripts/download_driver.sh` unpacks into
`driver/src/main/resources/driver/package/`. After you reproduced a bug in Java, write the same
calls as an upstream test in `$PLAYWRIGHT_DIR` and run it there. When the bug reproduces in Node.js
too, the root cause is in the driver or in a browser, and the fix lands in `microsoft/playwright`,
which the family treats as one team. When Node.js passes, the bug is in the Java client: compare
the `*Impl.java` with its JavaScript counterpart, and fix it here.

For an upstream bug, create `$OUTPUT_DIR/upstream/` with:

- `report.md`: the report for a maintainer to file in `microsoft/playwright`, for an engineer who
did not read the Java issue: what fails, expected versus actual, the driver version, the browsers
and the platform, the root cause when you found it, and the condensed Node.js test as the repro.
- `report.json`: `{"title": "..."}`, at most 256 characters, for example
`route.fulfill() ignores the charset in Content-Type`.

The comment job appends the title and the body to your comment, for a maintainer to file. The bot
never files it. Still write the comment as the note: the verdict, the evidence, and that the bug is
upstream. Do not paste the report into it. Write `report.json` last, when `report.md` and the
comment are done. Like `proposal.json`, it marks the report as ready.

The public API is generated from the upstream docs: `scripts/generate_api.sh` runs upstream's
`utils/doclint/generateApiJson.js` at the commit of the driver, and `tools/api-generator` writes
every interface and options class in `playwright/src/main/java/com/microsoft/playwright/`,
`options/` and `assertions/`; only `CLI.java`, `PlaywrightException.java`, `TimeoutError.java`,
`assertions/PlaywrightAssertions.java` and the `package-info.java` files there are written by hand.
Never edit a generated file: the next roll reverts it. A missing method or option is a change to
`docs/src/api/` upstream, so it ends in a report or a note, not in a fix here.

## PRs from third parties

Do not run a PR that changes a `pom.xml` of any module, `scripts/`, `.github/`, `.azure-pipelines/`,
`.devcontainer/`, `utils/`, `tools/`, the `driver/` or `driver-bundle/` modules, or a generated API
file.
35 changes: 35 additions & 0 deletions .github/actions/setup-triage-runner/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
name: 'Set up the triage runner'
description: >
Prepares the environment that the triage and revision agents work in: the checkout built as CI
builds it, with the browsers and the Copilot CLI installed, and a checkout of microsoft/playwright
to read the client that this repo ports. Exports PLAYWRIGHT_DIR, the path of that checkout, and
CI_ENV_EXEC, the wrapper from microsoft/playwright that runs a command under xvfb on Linux and at
normal I/O priority on Windows.
runs:
using: composite
steps:
- uses: microsoft/playwright/.github/actions/ci-env@main
- uses: actions/setup-java@03ad4de0992f5dab5e18fcb136590ce7c4a0ac95 # v5.6.0
with:
distribution: zulu
java-version: 8
- uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0
with:
node-version: '24'
- name: Download the driver, build and install
shell: bash
run: |
scripts/download_driver.sh
mvn -B install -D skipTests --no-transfer-progress
- name: Install the browsers
shell: bash
run: mvn exec:java -e -D exec.mainClass=com.microsoft.playwright.CLI -D exec.args="install --with-deps" -f playwright/pom.xml --no-transfer-progress
- name: Check out microsoft/playwright
shell: bash
run: |
dir="${RUNNER_TEMP//\\//}/playwright"
git clone --quiet --depth 1 https://github.com/microsoft/playwright "$dir"
echo "PLAYWRIGHT_DIR=$dir" >> "$GITHUB_ENV"
- name: Install the Copilot CLI
shell: bash
run: npm install -g @github/copilot
132 changes: 132 additions & 0 deletions .github/workflows/revision.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
# Copilot addresses the maintainers' review comments on a draft PR that the triage workflow opened,
# without write credentials. A fresh job pushes the new commit to the PR's branch using the app, so
# CI runs on it. The steps that are the same for every Playwright repo are actions in
# microsoft/playwright, and the shared guide they carry says how to revise; the prompt below has
# what is specific to this repo.
name: PR revision

on:
# Apply needs-revision, needs-revision-mac or needs-revision-win; re-apply it to trigger another pass.
# pull_request_target always runs this file from main; the jobs only accept the triage app's own PRs.
pull_request_target:
types: [labeled]

permissions: {}

jobs:
prepare:
name: Collect the maintainers' comments
if: >-
github.repository == 'microsoft/playwright-java' &&
contains(fromJSON('["needs-revision","needs-revision-mac","needs-revision-win"]'), github.event.label.name) &&
github.event.pull_request.state == 'open' &&
github.event.pull_request.user.login == 'microsoft-playwright-automation[bot]' &&
github.event.pull_request.head.repo.full_name == github.repository
runs-on: ubuntu-latest
permissions:
pull-requests: write
env:
PLATFORM: ${{ fromJSON('{"needs-revision":"linux","needs-revision-mac":"mac","needs-revision-win":"windows"}')[github.event.label.name] }}
outputs:
head: ${{ steps.collect.outputs.head }}
base: ${{ steps.collect.outputs.base }}
branch: ${{ steps.collect.outputs.branch }}
platform: ${{ env.PLATFORM }}
steps:
- name: Collect the PR and the maintainers' comments
id: collect
uses: microsoft/playwright/.github/actions/collect-review@main
with:
pull-request: ${{ github.event.pull_request.number }}
- name: Post the run link
env:
GH_TOKEN: ${{ github.token }}
PR: ${{ github.event.pull_request.number }}
PLATFORM_NAME: ${{ fromJSON('{"linux":"Linux","mac":"macOS","windows":"Windows"}')[env.PLATFORM] }}
RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
run: |
body=$(printf "Hi, I'm the Playwright bot. I started to address the review comments on %s: [agent run](%s)\n<!-- playwright-revision-started -->" "$PLATFORM_NAME" "$RUN_URL")
gh pr comment "$PR" --body "$body" || echo "::warning::Could not post the run link"

revise:
name: Revise PR ${{ github.event.pull_request.number }} (${{ needs.prepare.outputs.platform }})
needs: prepare
runs-on: ${{ fromJSON('{"linux":"playwright-x64-ubuntu24-64-core","mac":"macos-15-xlarge","windows":"playwright-x64-server2025-64-core"}')[needs.prepare.outputs.platform] }}
timeout-minutes: 240
concurrency:
group: revision-${{ github.event.pull_request.number }}
cancel-in-progress: false
defaults:
run:
shell: bash
env:
PR: ${{ github.event.pull_request.number }}
HEAD_SHA: ${{ needs.prepare.outputs.head }}
BASE_SHA: ${{ needs.prepare.outputs.base }}
TRIAGE_PLATFORM: ${{ needs.prepare.outputs.platform }}
permissions:
copilot-requests: write
outputs:
has_revision: ${{ steps.agent.outputs.has-result }}
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
ref: ${{ needs.prepare.outputs.head }}
persist-credentials: false
- name: Fetch the base and record the directories
run: |
git fetch --no-tags --depth=1 origin "$BASE_SHA"
echo "OUTPUT_DIR=${RUNNER_TEMP//\\//}/revision" >> "$GITHUB_ENV"
echo "REVIEW_DIR=${RUNNER_TEMP//\\//}/review" >> "$GITHUB_ENV"
echo "SCRATCH_DIR=${RUNNER_TEMP//\\//}/scratch" >> "$GITHUB_ENV"
echo "REPO_DIR=${GITHUB_WORKSPACE//\\//}" >> "$GITHUB_ENV"
mkdir -p "${RUNNER_TEMP//\\//}/revision" "${RUNNER_TEMP//\\//}/scratch"
- uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
with:
name: review
path: ${{ runner.temp }}/review
- name: Set up the runner
uses: ./.github/actions/setup-triage-runner
- name: Revise with Copilot CLI
id: agent
# No GitHub tools and no URL access: the agent reads the PR only from the review the prepare job filtered.
uses: microsoft/playwright/.github/actions/run-agent@main
with:
prompt: |
Follow the shared guide revision.md and this repo's AGENTS.md. Read both in full before starting. $PLAYWRIGHT_DIR is a shallow clone of microsoft/playwright at main, for reading the JavaScript client that this repo ports.

Pull request: https://github.com/microsoft/playwright-java/pull/${{ github.event.pull_request.number }}
output-dir: ${{ env.OUTPUT_DIR }}
result-file: commit/commit.json
artifact-name: revision
exec: ${{ env.CI_ENV_EXEC }}

push:
name: Push the revision
needs: [prepare, revise]
# commit.json is written last, so a finished revision is pushed even when the job failed later.
if: ${{ !cancelled() && needs.revise.outputs.has_revision == 'true' }}
runs-on: ubuntu-latest
steps:
- uses: actions/create-github-app-token@64ab729a6294000f5aa4a4bc0b73d1f32bc45510 # v3.3.0
id: app-token
with:
app-id: ${{ vars.PLAYWRIGHT_APP_ID }}
private-key: ${{ secrets.PLAYWRIGHT_PRIVATE_KEY }}
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
ref: ${{ needs.prepare.outputs.head }}
token: ${{ steps.app-token.outputs.token }}
- uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
with:
name: revision
path: ${{ runner.temp }}/revision
# Never execute code or scripts from the proposed changes in the app job.
- name: Validate and push
uses: microsoft/playwright/.github/actions/publish-patch@main
with:
token: ${{ steps.app-token.outputs.token }}
dir: ${{ runner.temp }}/revision/commit
patch: revision.patch
metadata: commit.json
branch: ${{ needs.prepare.outputs.branch }}
Loading
Loading