Limits
What geowright cannot do, or does only partly, stated plainly.
- Tested on Chromium with software WebGL (the flag set MapLibre uses in its own CI;
npx geowright initwrites it). Touch gestures beyondtapgo through CDP — Chromium only. - One line in the app is required. Auto-discovery is not possible for bundled ESM apps (
tests/discovery.spec.tsproves it). - A map container that STARTS moving on the page after an action's last check is not caught for location actions
(
clickAt,hoverAt,dragAt…) and drags: they wait while the container is already moving, but cannot re-verify at the instant of the input. Multi-point actions (dragAt,draw.*) look at each point once, at least 50 ms apart, so an earlier point can go stale if the container starts moving during a later look. Featureclick(),tap()andhover()do re-verify at the real event. waitForIdle()cannot see app-level async (a fetch that adds a layer later) — use the retrying assertions for that.- Mapbox GL JS 1.13: GeoJSON set again while its last data is still loading ("clear, then load":
setData(empty), thensetData(url)) is not waited for. 1.13 calls the source loaded, and the map idle, as soon as the first load ends, while the URL is still being fetched, so a.notor a count can pass before the new data arrives. MapLibre 2–6 are not affected. On 1.13, wait for something the app shows once its data is in before such an assertion. - Sources re-pointed faster than they load (image, vector tiles, GeoJSON URLs) count as never idle when repeated
overlapping starts continue through the wait.
.notand counts then judge the current map; the newest data may still be on its way. A single load, two replacements, or completed sequential loads still need to settle. - Tiled sources: the browser cannot know what is off-screen, and the failure messages say so.
- On a globe MapLibre's own viewport-wide query is wrong; the workaround is a grid: an extrusion narrower than 16 px whose
footprint centre is off-screen can be missed by viewport-wide queries (never mis-clicked; a query
atit finds it). - A feature drawn away from its geometry (a label under its point by
text-offset,line-offset, a*-translate) is aimed at on its geometry: the point and a few pixels around it, a line's vertices and midpoints. When it is drawn further away than that, an action on it fails withnot-found; act where it is drawn withclickAt(hoverAt,tapAt…).toBeVisibleOnMapreaches no verdict either way:{ pixels: false }checks its paint, and.not.toHaveFeatureasserts that it is gone. - A feature that draws nothing at its action point reads as not visible to the pixel probe of
toBeVisibleOnMap, which looks only at a patch of about 5×5 CSS px there (the first point the hit test gives back to the feature: a point's centre, a line's middle, a polygon's interior far from its edge). That is the case for a ring marker with a transparent fill, a polygon drawn only by its outline, a two-line label whose centre falls between its lines, a dashed line whose middle falls in a gap.toBeVisibleOnMapfails on such a feature, and.not.toBeVisibleOnMap()passes although a user sees it. Assert that a feature is gone with.not.toHaveFeature; check one of these withtoBeVisibleOnMap({ pixels: false })(its paint) andtoHaveLayerPixelswhere it is drawn (its pixels). An action reads the same patch for a pixel-only layer above its target: under a raster, even a translucent one, an action on such a feature fails withoccluded; pass{ ignoreLayers: [...] }for that layer. - An opaque overlay whose colour changes by itself (a playing video, a paint transition) can read as the target showing
through, so a click goes through to the layer below and
toBeVisibleOnMappasses: the pixel probe cannot tell an overlay's own colour drift from a target. Over such a map, assert on the feature (toHaveFeature) or pause the overlay first. - A location just past a globe's horizon is refused as not on the visible map surface, like any far-side location.
Near the limb one pixel covers many degrees, so a location whose project/unproject round trip is more than about one
degree off is refused too, rather than clicked at a different near-side point; a query
atsuch a location matches nothing for the same reason. Locations well inside the visible disc are unaffected. hoveron a moving target is only dependable when frames are short;clickholds up.- A feature whose source the app replaces on every frame is never "at rest", even one that stands still: "at rest"
is the same pixel two frames running and no data replacement of its source in between (a starved renderer draws a
moving feature at the same pixel too). Such a feature needs
moving: 'allow', whose check at the real event tells a hit from a miss. - A press that was verified can still miss at the
clickevent (adblclick: its first click only), where MapLibre decides which layer was clicked, after the release: a fastmoving: 'allow'target moved off in between, or the app moved the map on the press. The hit test is run again there, and a miss is a failure after input: the click was sent once, the app's click landed elsewhere, and it is not sent again. What cannot be done is to take the click back. - Pixel-only layers above a feature (raster, image, video, canvas, heatmap, hillshade, color-relief, background, and custom layers such as deck.gl's) are checked with a pixel probe:
a few renders per action on a map that has one over its features, and not at all for
moving: 'allow'. Pixels that change inside an overlay between the last probe and the press are not re-checked (over a target whose opacity is data-driven that time includes a reload of its source, and an overlay given new data during the first one is probed once more); over such a target, an overlay that changes colour by itself can still, rarely, pass for the target showing through; on MapLibre 2 and Mapbox GL 1 a custom layer is not checked at all; the snapshot does not probe (it marks such a featureunder-pixel-layer:<ids>): RECIPES. - The injected script is about 455 KB, not minified (99 KB gzipped; it bundles MapLibre's style-expression evaluator); test-time only, nothing ships in your app.
The full list, with costs measured on a dense map: DESIGN — Costs and known limitations.