Skip to content

Add experimental geb-playwright driver - #330

Merged
jonnybot0 merged 9 commits into
apache:masterfrom
jamesfredley:feature/geb-direct-playwright
Oct 1, 2026
Merged

jonnybot0 merged 9 commits into
apache:masterfrom
jamesfredley:feature/geb-direct-playwright

Conversation

@jamesfredley

@jamesfredley jamesfredley commented Jul 22, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Experimental, optional Playwright-backed driver for Geb (geb-playwright). Selenium stays the default. geb-core does not depend on Playwright unless a project adds this module and configures PlaywrightDriver.

The module is a WebDriver adapter so existing Geb code (Browser, pages, the content DSL, modules, waitFor, reporting) can run on Playwright. It does not wrap Playwright's own API. Tests that need network routing, locators, tracing, or anything else Playwright already exposes use driver.page and driver.context.

The artifact, package, and configuration may change or be removed. This module is not covered by Geb's compatibility guarantees.

Configuration

import geb.playwright.PlaywrightDriver

driver = PlaywrightDriver.config {
    browserType = 'chromium' // firefox | webkit
    headless = true
}

cacheDriverPerThread = true

Install a browser once per machine and CI image:

npx playwright install chromium
# or
./gradlew :module:geb-playwright:installPlaywrightBrowsers

Skip the module's browser tests with -Dgeb.playwright.skip=true or PLAYWRIGHT_SKIP=true.

What is included

  • module/geb-playwright — WebDriver / WebElement adapter, driver factory, context options, trace reporter
  • Manual chapter: doc/manual/src/docs/asciidoc/022-geb-playwright.adoc
  • CI installs Playwright Chromium before check

Known limits

  • Not every Selenium edge case is 1:1 (window chrome and alerts are best-effort)
  • driver.manage().logs() is unsupported
  • Cookie APIs follow the current document host (WebDriver semantics)
  • Playwright Java is not thread-safe

Grails note

Re-ran the Grails 8 proof on apache/grails-core branch feature/grails-geb-playwright-local (843dc0b215) against org.apache.groovy.geb:geb-playwright and package geb.playwright. Local integration tests: 5 tests, 0 failures (PlaywrightStressSpec, PlaywrightUploadSpec, ContextPathSpec twice, GebConfigSpec). The BOM map key is geb-spock-playwright so the published POM still uses geb-spock.version.

Checklist

  • Experimental module; default Selenium path untouched
  • WebDriver adapter tests + docs
  • CI browser install hook
  • Grails proof re-run against geb-playwright

Register the optional geb-direct submodule, Playwright 1.61 dependency,
JaCoCo coverage gate, browser install task, and CI Chromium install step.
Implement PlaywrightSession, PlaywrightWebDriver/WebElement, locator and
network helpers, tracing, waits, input, storage, emulation, and related
facades so existing Geb DSL can run on Playwright without changing the
default Selenium path.
Cover WebDriver core paths, Geb Browser/Page DSL, frames, windows, alerts,
network, locators, waits, storage, emulation, and facade helpers with a
90% JaCoCo line coverage gate.
Add module README and manual chapter covering configuration, browser
install, advanced Playwright surfaces, and Selenium default path notes.
Filter cookie reads/deletes by the active page URL/host so multi-domain
BrowserContexts match Selenium WebDriver cookie semantics.
Copilot AI review requested due to automatic review settings July 22, 2026 17:57

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Not ready to approve

There are a few concrete correctness/lint concerns (e.g., selector mapping edge cases and CodeNarc-triggering dead/unused imports/fields) that should be addressed before merging.

Pull request overview

Adds a new optional geb-direct module that lets Geb run on a Playwright-backed WebDriver/WebElement adapter while keeping the default Selenium WebDriver path unchanged. This introduces a Playwright session/context abstraction plus a set of “facade” helpers to expose Playwright-only capabilities (tracing, network routing, locators, etc.) through Geb’s existing Browser/DSL surfaces, along with module docs and CI setup for installing browsers.

Changes:

  • Introduces module/geb-direct with a Playwright→WebDriver adapter (PlaywrightWebDriver, PlaywrightWebElement, session + helpers).
  • Adds a comprehensive Spock test suite for adapter behavior and Playwright facades (with skip flags).
  • Updates docs, dependency catalog, Gradle wiring, and CI to install Playwright Chromium before running check.
File summaries
File Description
settings.gradle Adds the new module:geb-direct to the multi-project build.
module/geb-direct/src/test/groovy/geb/direct/support/PlaywrightSpecSupport.groovy Shared test helpers for driver creation, local server, and temp files.
module/geb-direct/src/test/groovy/geb/direct/PlaywrightWebDriverSpec.groovy Basic WebDriver surface smoke coverage (navigation/actions/JS/screenshot/locators).
module/geb-direct/src/test/groovy/geb/direct/PlaywrightWebDriverSmokeSpec.groovy Jetty-backed end-to-end WebDriver interaction smoke test.
module/geb-direct/src/test/groovy/geb/direct/PlaywrightWebDriverCoreSpec.groovy Core adapter behavior tests (timeouts, cookies, frames, actions, keys, async scripts).
module/geb-direct/src/test/groovy/geb/direct/PlaywrightWebDriverBrowserFeatureSpec.groovy Verifies Geb Browser integrations (frames/windows/uploads) against Playwright driver.
module/geb-direct/src/test/groovy/geb/direct/PlaywrightOptionsSpec.groovy Tests PlaywrightOptions defaults and copy/config behavior.
module/geb-direct/src/test/groovy/geb/direct/PlaywrightHelpersSpec.groovy Exercises helper facades (keyboard/mouse/storage/wait/emulation) on a live page.
module/geb-direct/src/test/groovy/geb/direct/PlaywrightFacadeCoverageSpec.groovy Broad facade coverage including tracing reporter, waits, routing, downloads, PDF, a11y, video.
module/geb-direct/src/test/groovy/geb/direct/PlaywrightDriverSpec.groovy Tests PlaywrightDriver.config factory behavior (independent option copies).
module/geb-direct/src/test/groovy/geb/direct/PlaywrightBySpec.groovy Tests By→Playwright selector mapping and escaping.
module/geb-direct/src/test/groovy/geb/direct/PlaywrightAlertAndWindowSpec.groovy Alert/dialog and window manager behavior tests.
module/geb-direct/src/test/groovy/geb/direct/PlaywrightAdvancedFeaturesSpec.groovy Exercises higher-level Playwright features (routing, tracing, PDF, API client, storage, console).
module/geb-direct/src/test/groovy/geb/direct/GebDirectSmokePage.groovy Simple Geb Page for verifying content DSL on the Playwright driver.
module/geb-direct/src/test/groovy/geb/direct/GebBrowserPlaywrightSpec.groovy Verifies Geb Browser/Page/content DSL works on the Playwright-backed driver.
module/geb-direct/src/main/groovy/geb/direct/report/PlaywrightTraceReporter.groovy Geb reporter that writes a trace zip and starts a new trace.
module/geb-direct/src/main/groovy/geb/direct/PlaywrightWindow.groovy WebDriver Window adapter (viewport-based best-effort).
module/geb-direct/src/main/groovy/geb/direct/PlaywrightWebElement.groovy WebElement adapter backed by Playwright Locator with Selenium-like semantics.
module/geb-direct/src/main/groovy/geb/direct/PlaywrightWebDriver.groovy Main WebDriver facade over a Playwright BrowserContext/Page plus helper facades.
module/geb-direct/src/main/groovy/geb/direct/PlaywrightWait.groovy Wait facade mapping common Playwright page/context wait primitives.
module/geb-direct/src/main/groovy/geb/direct/PlaywrightVideoSupport.groovy Small helper to access recorded video path.
module/geb-direct/src/main/groovy/geb/direct/PlaywrightTracing.groovy Tracing start/stop wrapper over session tracing state.
module/geb-direct/src/main/groovy/geb/direct/PlaywrightTouchscreen.groovy Touchscreen wrapper.
module/geb-direct/src/main/groovy/geb/direct/PlaywrightTimeouts.groovy WebDriver Timeouts adapter (implicit wait + script/page load timeouts).
module/geb-direct/src/main/groovy/geb/direct/PlaywrightTargetLocator.groovy WebDriver TargetLocator adapter (frames/windows/alerts).
module/geb-direct/src/main/groovy/geb/direct/PlaywrightStorage.groovy Local/session storage helper via page evaluation.
module/geb-direct/src/main/groovy/geb/direct/PlaywrightSession.groovy Playwright lifecycle: create/close, context options, tracing, dialog/event tracking.
module/geb-direct/src/main/groovy/geb/direct/PlaywrightPdf.groovy Chromium PDF wrapper.
module/geb-direct/src/main/groovy/geb/direct/PlaywrightOptions.groovy Options model + deep copy used by config factories and sessions.
module/geb-direct/src/main/groovy/geb/direct/PlaywrightNetwork.groovy Routing/interception wrapper plus request/response wait passthrough.
module/geb-direct/src/main/groovy/geb/direct/PlaywrightNavigation.groovy WebDriver Navigation adapter.
module/geb-direct/src/main/groovy/geb/direct/PlaywrightMouse.groovy Mouse wrapper.
module/geb-direct/src/main/groovy/geb/direct/PlaywrightLocators.groovy Semantic locator helpers returning Selenium WebElements.
module/geb-direct/src/main/groovy/geb/direct/PlaywrightKeyboard.groovy Keyboard wrapper.
module/geb-direct/src/main/groovy/geb/direct/PlaywrightHar.groovy HAR routing wrapper.
module/geb-direct/src/main/groovy/geb/direct/PlaywrightEmulation.groovy Emulation + permissions helper wrapper.
module/geb-direct/src/main/groovy/geb/direct/PlaywrightDriver.groovy Factory entrypoint: create and config {} driver factory closures.
module/geb-direct/src/main/groovy/geb/direct/PlaywrightDownloads.groovy Download helper wrapper.
module/geb-direct/src/main/groovy/geb/direct/PlaywrightCoverage.groovy Coverage facade with explicit unsupported behavior where absent.
module/geb-direct/src/main/groovy/geb/direct/PlaywrightCookieJar.groovy Selenium-semantics cookie jar adapter scoped to current document host.
module/geb-direct/src/main/groovy/geb/direct/PlaywrightConsole.groovy Console message + page error collection access/clear.
module/geb-direct/src/main/groovy/geb/direct/PlaywrightBy.groovy Selenium By→Playwright selector mapping.
module/geb-direct/src/main/groovy/geb/direct/PlaywrightBrowserSupport.groovy Entry points to access Playwright driver/page/context + helper facades from Geb Browser.
module/geb-direct/src/main/groovy/geb/direct/PlaywrightApiClient.groovy Playwright APIRequestContext wrapper for test-side HTTP calls.
module/geb-direct/src/main/groovy/geb/direct/PlaywrightAccessibility.groovy Accessibility snapshot wrapper.
module/geb-direct/README.md Module README with setup, usage, options, and limitations.
module/geb-direct/geb-direct.gradle Module build, dependencies, skip flags, Playwright browser install task, JaCoCo gate.
gradle/libs.versions.toml Adds Playwright dependency coordinates/version.
doc/manual/src/docs/asciidoc/index.adoc Includes the new manual chapter.
doc/manual/src/docs/asciidoc/022-geb-direct.adoc New manual chapter documenting Geb Direct with Playwright.
.github/workflows/build-check.yml Installs Playwright Chromium before running check in CI.

Review details

  • Files reviewed: 51/51 changed files
  • Comments generated: 6
  • Review effort level: Low

Note

Your feedback helps us improve the quality of this feature.
Please use 👍 or 👎 to tell us whether this assessment is correct.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread module/geb-direct/src/main/groovy/geb/direct/PlaywrightBy.groovy Outdated
Comment thread module/geb-direct/src/main/groovy/geb/direct/PlaywrightHar.groovy Outdated
Comment thread module/geb-direct/src/main/groovy/geb/direct/PlaywrightWebDriver.groovy Outdated
Comment thread module/geb-direct/src/test/groovy/geb/direct/PlaywrightWebDriverCoreSpec.groovy Outdated
Comment thread module/geb-direct/src/main/groovy/geb/direct/PlaywrightNetwork.groovy Outdated
Apply valid review feedback for window minimize dead state, className
selector edge cases, HAR unroute clarity, and unused imports. Green
codenarcTest which was failing Build/test/quality check on CI.
@jamesfredley

Copy link
Copy Markdown
Contributor Author

Copilot review follow-up (39e0327)

Addressed the valid review comments and the CI failure:

Thread Action
previousSize unused in PlaywrightWindow Removed dead field; maximize/fullscreen are viewport best-effort; minimize is an explicit no-op
By.className digit identifiers Use [class~="..."] when not a valid CSS identifier; added unit test
unrouteFromHAR clears all routes Documented Playwright limitation; deprecated path overload; added explicit unrouteAll()
Redundant Selenium imports Dropped duplicates; kept org.openqa.selenium.*
Unused Locator import in core spec Removed (this was the codenarcTest CI break)
Path unused in PlaywrightNetwork Not valid - still used by routeFromHAR(Path) / Path.of(...)

CI

Build, test, and quality check failed on :module:geb-direct:codenarcTest (unused import), not on the Playwright tests themselves. Local verification after the fix:

  • :module:geb-direct:codenarcMain / codenarcTest green
  • :module:geb-direct:test green
  • JaCoCo gate green

All six Copilot review threads marked resolved.

ManualAnchorsSpec failed in CI because auto-generated header ids for
Installation and other sections collided with existing chapters.
@jamesfredley

Copy link
Copy Markdown
Contributor Author

CI failure root cause: ManualAnchorsSpec duplicate header ids from the new geb-direct chapter (e.g. Installation colliding with Installation & usage). Fixed in the latest commit by giving every section an explicit unique AsciiDoc anchor ([[geb-direct-...]]). Verified locally with :doc:site:test --tests geb.ManualAnchorsSpec.

@matrei

matrei commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

@jamesfredley This is really cool!
Why the name geb-direct? Would not geb-playwright be more suitable?

@jamesfredley

Copy link
Copy Markdown
Contributor Author

I did start at geb-playwright and then landed at geb-direct

Other options I considered:

Name Pros Cons Verdict
geb-direct Clear, short, highlights the protocol advantage Slightly vague on first read Best
geb-native Emphasizes native browser protocols Could be confused with "native mobile" Good
geb-engine Sounds technical Too generic Okay
geb-playwright Most discoverable could be confusing Fallback
geb-cdp Technically accurate for Chromium Too narrow (ignores Firefox/WebKit) Not great

  • It clearly signals the key architectural difference: direct communication with the browser engine (via CDP and equivalent protocols) instead of going through the Selenium/WebDriver abstraction layer.
  • Short, Groovy-style, and easy to say.
  • "Direct" tells people exactly what they're getting — lower-level, more powerful browser control without the extra translation hop.
  • It doesn't tie the module name too tightly to the Playwright library itself (even though Playwright would be the implementation underneath). This leaves room later if someone wanted to add other direct-protocol backends.

Playwright communicates directly with the browser engines using their native low-level debugging/automation protocols:

Chromium → Chrome DevTools Protocol (CDP)
Firefox → Firefox Remote Debugging Protocol
WebKit → WebKit Remote Debugging Protocol

Playwright skips the WebDriver server entirely. Its Java (or Node) client talks more directly to the browser process using the engine’s own protocol.

@jamesfredley

Copy link
Copy Markdown
Contributor Author

@matrei Thank you, I am hopeful this resolves our flaky test issues.

@jdaugherty

jdaugherty commented Aug 5, 2026 •

Copy link
Copy Markdown
Contributor

Wouldn't reactive as a name be better? it's only direct because it doesn't use polling. it uses push?

@jamesfredley

Copy link
Copy Markdown
Contributor Author

I still lean towards geb-direct.

If that gets voted down strongly, geb-playwright would be my next vote.

@sbglasius

Copy link
Copy Markdown

I think the name is less important, more important is to get it into Geb

@matrei

matrei commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

I think the name is less important, more important is to get it into Geb

I disagree, naming is extremely important, and hard to change afterwards.
It's one of the two hard things in computer science you know 😉

@jonnybot0

Copy link
Copy Markdown
Contributor

Hey! Late to the party, but I'm on team geb-playwright, as I noted on the mailing list. Do that, and you get a ✅ from me.

…pers

The Playwright-backed driver only needs a WebDriver adapter. Call Page and BrowserContext directly for Playwright features that have no WebDriver equivalent.
@jamesfredley jamesfredley changed the title Add optional geb-direct Playwright backend Add experimental geb-playwright driver Sep 30, 2026
@jamesfredley

jamesfredley commented Sep 30, 2026 •

Copy link
Copy Markdown
Contributor Author

Simplification pass

Question for this change: is a Playwright backend for Geb the smallest thing that still lets existing Geb code run? The previous draft answered that by re-implementing Playwright.

geb-direct is now geb-playwright (org.apache.groovy.geb:geb-playwright, package geb.playwright) and is marked experimental in the manual, README, and POM. The API is not a compatibility commitment.

Removed

These types only forwarded to Page or BrowserContext. Call those objects on the driver instead.

  • PlaywrightBrowserSupport
  • PlaywrightNetwork, PlaywrightTracing, PlaywrightLocators, PlaywrightWait
  • PlaywrightKeyboard, PlaywrightMouse, PlaywrightTouchscreen
  • PlaywrightStorage, PlaywrightEmulation, PlaywrightDownloads, PlaywrightHar
  • PlaywrightConsole, PlaywrightApiClient, PlaywrightCoverage, PlaywrightPdf
  • PlaywrightAccessibility, PlaywrightVideoSupport

Also removed the tests that existed to walk those wrappers (PlaywrightFacadeCoverageSpec, PlaywrightAdvancedFeaturesSpec, PlaywrightHelpersSpec), plus PlaywrightWebDriverSmokeSpec and PlaywrightWebDriverSpec, which repeated the core adapter specs and pulled in Jetty for one page. The Jetty test dependencies went with them.

PlaywrightWebElement no longer adds Playwright methods that are not part of WebElement (hover, selectOption, dragTo, and the rest). PlaywrightOptions.copy() no longer lists every field by hand. PlaywrightDriver.config and create share one closure applicator. The session no longer records console messages and page errors that only the deleted console wrapper read.

Kept

The WebDriver surface Geb actually calls:

  • PlaywrightDriver, PlaywrightOptions, PlaywrightSession
  • PlaywrightWebDriver, PlaywrightWebElement, PlaywrightBy
  • navigation, cookies, timeouts, frames, windows, alerts
  • PlaywrightTraceReporter, which is a Geb reporter, not a Playwright wrapper

Playwright-only work:

def playwrightDriver = browser.driver as PlaywrightWebDriver
playwrightDriver.page.getByTestId('submit').click()
playwrightDriver.context.route('**/api/**') { route -> route.abort() }

Tests

:module:geb-playwright:check passed locally (CodeNarc, module tests, JaCoCo line coverage at 85%, RAT). Cookie add/read/HttpOnly and each delete path are asserted separately. Viewport size is asserted before maximize/fullscreen overwrite it.

Diff against the previous tip: 53 files, +414 / −2053.

The Grails 8 proof on apache/grails-core branch feature/grails-geb-playwright-local now depends on org.apache.groovy.geb:geb-playwright and package geb.playwright (843dc0b215). Re-ran the local Playwright integration tests after that rename: 5 tests, 0 failures (PlaywrightStressSpec, PlaywrightUploadSpec, ContextPathSpec twice, GebConfigSpec). The BOM map key is geb-spock-playwright so publication still resolves geb-spock.version.

@jonnybot0

Copy link
Copy Markdown
Contributor

Looks good. I just resolved the merge conflict. Once the checks pass, we should be good to merge. May need to make some fix-forward changes later, but this seems as good a place to start as any.

@jonnybot0
jonnybot0 merged commit a55d4db into apache:master Oct 1, 2026
12 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants