DocsLimits

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 init writes it). Touch gestures beyond tap go through CDP — Chromium only.
  • One line in the app is required. Auto-discovery is not possible for bundled ESM apps (tests/discovery.spec.ts proves 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. Feature click(), tap() and hover() 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), then setData(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 .not or 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. .not and 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 at it 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 with not-found; act where it is drawn with clickAt (hoverAt, tapAt…). toBeVisibleOnMap reaches no verdict either way: { pixels: false } checks its paint, and .not.toHaveFeature asserts 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. toBeVisibleOnMap fails 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 with toBeVisibleOnMap({ pixels: false }) (its paint) and toHaveLayerPixels where 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 with occluded; 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 toBeVisibleOnMap passes: 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 at such a location matches nothing for the same reason. Locations well inside the visible disc are unaffected.
  • hover on a moving target is only dependable when frames are short; click holds 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 click event (a dblclick: its first click only), where MapLibre decides which layer was clicked, after the release: a fast moving: '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 feature under-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.