What “visible” means
Why a feature MapLibre renders is not always one a person can see, and how geowright tells the two apart.
MapLibre's queryRenderedFeatures returns features the style paints invisibly: an opacity of 0, a colour with zero alpha, a zero radius and text-opacity: 0 all come back from a viewport query as if they were drawn. A plain “does this feature exist” check can pass on something no user could see, so geowright's queries match only what is visible, by default.
Paint, evaluated per feature
MapLibre's own style-expression evaluator, published as @maplibre/maplibre-gl-style-spec, is bundled into the script geowright injects. The public getPaintProperty and getLayoutProperty give the style's raw values; the evaluator resolves them for each feature as the renderer does, with data-driven expressions, zoom, feature state and global state. A zoom-and-data expression is evaluated at the tile's zoom, as MapLibre draws it, and a layer's visibility, minzoom and maxzoom count too. It costs microseconds per feature and needs no rendering.
When it cannot tell, the answer is visible
An expression newer than the bundled evaluator, or one that uses within or distance, cannot be evaluated here. The verdict is then “visible”, with a caveat in the failure message and the snapshot, and never a false “invisible”: reporting a real feature as hidden would do more damage to a test suite than the reverse.
Pixels as the ground truth
Paint cannot see a transparent icon image, or a colour that exactly matches what is drawn under it. So toBeVisibleOnMap() adds a pixel probe. Using MapLibre's public, synchronous redraw(), it draws the map with the layer, reads the pixels, hides the layer, draws and reads again, and restores it, all in one task, so nothing reaches the screen. A difference means the layer contributed pixels there. A probe costs about 25 ms; { pixels: false } skips it.
Two things confound it: a second feature of the same layer under the same pixel, and the probe's own styledata event, which an app listening for it will see. Mapbox GL JS 1.13 has no redraw(), so there the probe uses real frames and may flicker once.
Layers without features
A raster, image, video, canvas, hillshade or heatmap layer has no features to query. toHaveLayerPixels(layerId, { at, radiusPx }) runs the same probe over a radius around a location instead, 24 pixels by default, because imagery is transparent at any single spot often enough. Heatmap layers are always “visible” to feature queries; toHaveLayerPixels is the check for a heatmap.
A failure says why
When a feature is not visible, the failure names the style property, its value, the expression branch that produced it and the zoom:
-> parks#7 is not visible at zoom 11.2: fill-opacity is 0 because the match on ["get","kind"] took its fallback for kind="park"Choosing what counts
visible: true, the default: only features the style paints visibly.visible: 'any': MapLibre's own “rendered”, opacity 0 included.visible: false: only the invisible ones.
Actions refuse an invisible feature unless { force: true }: a person cannot click what they cannot see.
The query options and matchers, with examples, are in the Visible by default guide.
Read more
- Visible by defaultThe query options and matchers, with examples.
- Matchers
toBeVisibleOnMap,toHaveLayerPixelsand the rest. - LimitsWhat the paint check and the pixel probe cannot see.