API reference
Every export, handle, action, matcher and option.
Imports
import { geowright, expect, GeowrightError } from 'geowright'import { routeVectorTiles } from 'geowright/tiles'/// <reference types="geowright/app" />App side, types only: /// <reference types="geowright/app" /> declares window.__geowright_maps (Record<string, unknown>)
Types, from 'geowright': every type a signature names, and GeowrightMatchers (the matchers, e.g. for mergeExpects). expect is typed from @playwright/test alone.
MapHandleFeatureHandleFeatureSetHandleFeatureQueryFeatureSummaryLngLatPixelPointGeometryGeometryExpectationWhereOpWhereValueLayerInfoSnapshotOptionsGeowrightOptionsActionOptionsActionPointOptionsSteadyActionOptionsDrawOptionsTwoPointDrawOptionsOverlayWhereModifierViewExpectationGeowrightMatchers
geowright()
geowright(page, options?) → MapHandle
MapHandlegeowright(page)
map.feature(query)
Lazy handles: strict single feature
map.features(query?)
A set
map.waitForIdle({ timeout? })
Map loaded, still, tiles in, nothing to repaint. Cannot see app-level async — use retrying assertions for that
map.viewState()
Camera + bounds + size + projection + terrain
map.layers()
Layer list with source types
map.snapshot(opts?)
Text snapshot (perLayer, maxProps, layers, skipLayers, filter, maxChars)
map.clickAt(lngLat, opts?)map.dblclickAt(lngLat, opts?)map.rightClickAt(lngLat, opts?)map.hoverAt(lngLat, opts?)map.tapAt(lngLat, opts?)
Real input at a location; waits until it is in view, on the visible surface, uncovered, map still
map.dragAt(from, to, { steps? })
Press-move-release
map.wheelAt(lngLat, deltaY)
Wheel with the pointer there
map.pinchAt(lngLat, { scale })map.twoFingerRotateAt(lngLat, { degrees })map.touchDragAt(from, to)map.doubleTapAt(lngLat)
Touch gestures via CDP — Chromium only
map.keyboard.press(key)
Real key press with the map focused
map.draw.polygon(vertices, { finish, vertexDelay? })map.draw.line(vertices, { finish, vertexDelay? })
finish: click-last (default) click-first dblclick enter none
map.draw.rectangle(a, b, opts?)map.draw.circle(center, edge, opts?)map.draw.cancel()
gesture: click-move-click (default) or drag; Escape
map.marker(where) → Promise<Locator>map.popup(where?) → Promise<Locator>
DOM overlays as normal Locators; where: { at?, feature?, tolerancePx? }
map.clientPoint(lngLat)map.lngLatAt({ x, y })
Lng/lat ⇄ client pixel. clientPoint waits for a pixel inside the browser window; lngLatAt answers at once, for any pixel it is given, and the lng it reports is in [-180, 180]
FeatureHandlemap.feature(q)
click(opts?)rightClick(opts?)dblclick(opts?)tap(opts?)
Full actionability + click-time verification
hover(opts?)
Full actionability, verified as the pointer arrives (a miss is aimed at again)
dragTo(lngLat, opts?)
Drag a handle to a place
dragBy({ dLng, dLat }, opts?)
Move a whole shape (no moving option)
info()
Resolved FeatureSummary (layer id properties geometryType pixel client lngLat visible invisibleBecause state; its pixel is the best geometric candidate, unverified)
lngLat(opts?)
The verified action point as lng/lat (waits and fails like click(); no moving option)
geometry()
Full geometry from the source (a tiled source: one drawn piece)
FeatureSetHandlemap.features(q)
Queries and action options
What each query field matches: Queries. What the action options relax: What an action waits for.
Matchers
All retry; .not supported
expect(map).toHaveFeature(query, { visible?, timeout? })
At least one visible feature (visible: 'any' = merely rendered)
expect(map.features(q)).toHaveFeatureCount(n)
Exactly n
expect(map.feature(q)).toBeVisibleOnMap({ pixels? })
One feature, painted by the style and contributing pixels to a patch of about 5×5 px at its action point (a ring or an outline can read as not visible: Limits). To assert absence: .not.toHaveFeature
expect(map).toHaveLayerPixels(layerId, { at, radiusPx? = 24 })
radiusPx in CSS pixels. A layer without features (raster, image, canvas, video, heatmap, hillshade) contributes pixels around a location
expect(map.feature(q)).toHaveGeometry(expected, { tolerancePx? = 2 })
GeoJSON geometry · vertex list · { circle: { center, edge } }
expect(map).toHavePopup({ at?, feature?, text?, tolerancePx? })
A popup is open and anchored there. at is the popup's tip within tolerancePx (default 8): a default Marker's tip sits about 38 px above its lng/lat, so pass a larger tolerancePx or match by text / feature
expect(map).toHaveView({ center?, zoom?, bearing?, pitch? }, { zoomTolerance? = 0.01, degreesTolerance? = 0.0005 })
Camera, with the map at rest. degreesTolerance is for the centre; bearing and pitch match within 0.1°, a bearing as an angle (270 = -90)
A matcher that cannot reach a verdict fails whichever way it is written, and says why: the map is not registered or
was removed, a named layer does not exist, several features match where one is required, the map has not settled (a
.not or a count needs it idle, or seen to be never idle), the camera is moving (toHavePopup), no pixel of the
feature's geometry hit-tests back to it (toBeVisibleOnMap; see Limits). "Could not look" never
passes as "absent". To assert that a layer was removed, check map.layers().
Errors
Failures throw GeowrightError (actions) or a normal expect failure; both carry "what is on the map" and attach text + an annotated screenshot.