KMPwright

Capabilities

How each backend reports what it supports through PageCapabilities, how to fail fast with requireCapabilities, and the full support matrix for Android WebView, iOS WKWebView and JVM Chromium.

Not every backend can do everything. A Page reports what its engine supports through page.capabilities(), so portable code can check before it relies on a platform-dependent feature.

val caps = page.capabilities()
if (caps.supports(PageCapability.Tracing)) {
    page.tracing.start()
}

// Fail before a workflow starts instead of halfway through it.
page.requireCapabilities(
    PageCapability.RouteInterception.require(SupportLevel.Partial),
    PageCapability.Downloads.require(reason = "the export step saves a file"),
)

KMPwright drives both full browsers and embedded WebViews. Their available hooks differ, so portable workflows check the capabilities they need before starting.

Types

PageCapability

A closed enum of the features that differ per backend:

RouteInterception, TouchInput, MouseInput, KeyboardInput, Pdf, BrowserContexts, ServiceWorkers, DomainFiltering, NetworkEmulation, HarRecording, Downloads, FileChooser, Tracing, Profiling, Tabs, Cdp, InitScripts, WebMcp, AccessibilityAudit, WebVitals, ReactInspection, HttpRead, NetworkHistory, Screencast, VideoRecording, CrossOriginFrames (locating and acting inside cross-origin iframes through frameLocator).

SupportLevel

ValueMeaning
SupportedImplements the documented behavior and options for this backend.
PartialWorks with documented gaps. The gaps are described in notes.
UnsupportedCalling the API throws KmpwrightUnsupportedException.

SupportLevel.isAvailable is true for Supported and Partial. level.meets(minimum) compares levels: Supported meets everything, Partial meets Partial and Unsupported, and any level meets Unsupported.

PageCapabilities

A data class with one SupportLevel property per capability (routeInterception, touchInput, mouseInput, keyboardInput, pdf, browserContexts, serviceWorkers, domainFiltering, networkEmulation, harRecording, downloads, fileChooser, tracing, profiling, tabs, cdp, initScripts, webMcp, accessibilityAudit, webVitals, reactInspection, httpRead, networkHistory, screencast, videoRecording, crossOriginFrames), plus:

Prop

Type

Every support-level property defaults to Unsupported, so a custom Page that does not override capabilities() advertises nothing.

Extension functions on PageCapabilities:

FunctionReturns
supportLevel(capability: PageCapability)The SupportLevel of one capability.
supports(capability: PageCapability, minimum: SupportLevel = Supported)Whether that capability meets minimum.
supportLevels()Map<PageCapability, SupportLevel> for every capability.
missing(requirements: Iterable<PageCapabilityRequirement>)List<MissingPageCapability> (requirement plus actual level) for the unmet requirements, in order.
require(requirements: Iterable<PageCapabilityRequirement>)this, or throws when any requirement is unmet.
require(vararg requirements: PageCapabilityRequirement)Same, vararg form.
require(vararg capabilities: PageCapability)Same, requiring Supported for each.
requireCapabilities(vararg requirements: PageCapabilityRequirement)Alias of require(vararg requirements).
supports(level: SupportLevel)Whether every PageCapability of this backend meets level.

Requirements

public data class PageCapabilityRequirement(
    val capability: PageCapability,
    val minimum: SupportLevel = SupportLevel.Supported,
    val reason: String? = null
)

requiredLevel is an alias of minimum, and CapabilityRequirement is a type alias of PageCapabilityRequirement. Build one with PageCapability.require(minimum = Supported, reason = null).

requireCapabilities

Page has two overloads, both returning the page's PageCapabilities:

public fun requireCapabilities(vararg requirements: PageCapabilityRequirement): PageCapabilities
public fun requireCapabilities(vararg capabilities: PageCapability): PageCapabilities  // each must be Supported

When a requirement is not met they throw KmpwrightUnsupportedException (a KmpwrightException) with one entry per unmet requirement, separated by ; :

Page capability requirements are not satisfied: Downloads: requires Supported, actual Unsupported (the export step saves a file)

A Partial requirement accepts Partial or Supported. The default requirement (Supported) accepts only Supported.

// Accept Android's and iOS's partial routing, but insist on real downloads.
page.requireCapabilities(
    PageCapability.RouteInterception.require(SupportLevel.Partial),
    PageCapability.Downloads.require(),
)

The check is static: it reads the backend's advertisement and does not probe the page. Individual APIs can still fail for other reasons, for example KmpwrightConfig.inputMode disabling native input (see Configuration).

Support matrix

Transcribed from androidPageCapabilities(), iosPageCapabilities() and jvmCdpPageCapabilities().

CapabilityAndroid WebViewiOS WKWebViewJVM (Chromium over CDP)
RouteInterceptionPartialPartialSupported
TouchInputSupportedPartialPartial
MouseInputPartialPartialSupported
KeyboardInputSupportedPartialSupported
PdfPartialPartialPartial
BrowserContextsUnsupportedPartialSupported
ServiceWorkersUnsupportedUnsupportedSupported
DomainFilteringPartialSupportedSupported
NetworkEmulationPartialPartialSupported
HarRecordingPartialPartialSupported
DownloadsUnsupportedUnsupportedSupported
FileChooserUnsupportedUnsupportedPartial
TracingUnsupportedUnsupportedSupported
ProfilingUnsupportedUnsupportedSupported
TabsSupportedSupportedSupported
CdpUnsupportedUnsupportedSupported
InitScriptsPartialSupportedSupported
WebMcpUnsupportedUnsupportedPartial
AccessibilityAuditPartialPartialSupported
WebVitalsPartialPartialSupported
ReactInspectionSupportedSupportedSupported
HttpReadSupportedSupportedSupported
NetworkHistoryPartialPartialPartial
ScreencastUnsupportedUnsupportedSupported
VideoRecordingUnsupportedUnsupportedSupported
CrossOriginFramesUnsupportedUnsupportedSupported

backend is BackendKind.AndroidWebView, BackendKind.IOSWebView and BackendKind.JvmCdp respectively.

Notes per backend

These are the exact notes strings each backend returns.

  1. Android touch and keyboard automation dispatch native WebView events.
  2. Mouse automation is still partial and may fall back to hover/tap approximations.
  3. Routing works at the WebView interception layer. Fulfill and abort are supported; continue overrides are applied only for requests WebView exposes to shouldInterceptRequest.
  4. Network emulation is offline-only and implemented in JavaScript (navigator.onLine, fetch, XMLHttpRequest, sendBeacon); WebView has no native latency/throughput throttling, so those parameters are rejected.
  5. Color scheme emulation is a JavaScript fallback (matchMedia and the root color-scheme property): with targetSdk 33+ WebView derives the CSS prefers-color-scheme media feature from the app theme, so stylesheet @media rules are not affected.
  6. Geolocation emulation is a JavaScript fallback that patches navigator.geolocation in every document.
  7. The domain filter is enforced natively: requests to disallowed hosts fail as network errors in shouldInterceptRequest (fetch, XHR, images, iframes) and navigations, including main-frame server redirects, are cancelled in loadUrl/shouldOverrideUrlLoading. It is Partial because WebView exposes no hook for server redirects of subresource requests (an open redirect on an allowed host reaches the target), WebSocket connections, or requests made by service workers.
  8. Cookie enumeration is limited to origins observed by this WebView and does not expose full browser-cookie metadata.
  9. Download and file chooser events are not currently surfaced from Android WebView.
  10. Dialogs reach automation only while a Page.onDialog handler or a waitForDialog is active; otherwise WebView hands them to the host WebChromeClient (or its native UI) and no dialog event is emitted.
  11. WebView reports no response for requests it loads itself: the main-frame document gets its status when the page finishes, any request gets status and headers from onReceivedHttpError (4xx/5xx), network errors fail the record, and other subresources finish without a response. resourceType is "document" for the main frame and "other" otherwise. Response bodies are unavailable.
  12. Screenshots support PNG, JPEG and WEBP; omitBackground is unsupported because WebView always paints its background.
  13. emulateDevice applies the viewport size and user agent; the device scale factor, mobile layout and touch support come from the device and are not emulated.
  14. frameLocator reaches same-origin frames only: page JavaScript cannot enter cross-origin iframes.

The browser backend (live demo)

The interactive demo on this site's home page runs KMPwright compiled to JavaScript (BackendKind.Browser). It drives a same-origin <iframe> with the same Page and runCommand() code, so snapshots, refs, locators, actions, assertions, evaluation and console capture behave as on the other backends. Browsers keep page JavaScript out of other origins, so it can only open setContent documents and pages of the hosting site. Input is synthesized as DOM events, and routing, network history, screenshots, PDF and downloads are unavailable. LoadState.NetworkIdle also throws KmpwrightUnsupportedException: page JavaScript cannot observe all browser traffic, so wait for a locator, assertion or explicit page result instead. It exists to power the demo: build apps on Android, iOS or the JVM.

What "Unsupported" looks like at runtime

Unsupported features throw KmpwrightUnsupportedException rather than silently doing nothing. Some examples from the source:

CallAndroidiOSJVM
page.waitForDownload()"Android WebView does not currently surface download events.""WKWebView does not currently surface download events."Works
browser.newContext()"Android WebView cannot provide isolated BrowserContext semantics in-process. …"Works (non-persistent data store)Works
browser.targets(), attachTarget, newWindow"Browser target discovery requires CDP" (and similar)SameWorks with a browser-scoped endpoint
page.emulateNetworkConditions(NetworkConditions.Slow3G)"This backend cannot throttle latency or throughput; only offline emulation is available (see PageCapabilities.networkEmulation)."SameWorks
page.setDeviceScaleFactor(2.0)"Android WebView device scale is determined by the host display""WKWebView device scale is determined by the host screen"Works
page.screenshot(omitBackground = true)"omitBackground is not supported on Android WebView: …""omitBackground is not supported on iOS WKWebView: …"Works
launchBrowser(...)"Desktop browser process launch requires the JVM backend"SameWorks

page.emulateDevice(device) does not throw on WebViews: it applies the viewport size and user agent and leaves the scale factor, mobile layout and touch support to the device.

See Errors for the full list of messages.

On this page