Skip to content

Network

Tapsmith supports Playwright-style network interception. Route handlers let you mock, modify, or abort HTTP/HTTPS requests made by the app under test.

device.route(url, handler, options?): Promise<void>

Section titled “device.route(url, handler, options?): Promise<void>”

Intercept network requests matching a URL pattern. Routes run on Tapsmith’s network proxy, which needs no config: when the run’s test files (or the local modules they import) call route(), waitForRequest(), waitForResponse() or on('request' | 'response'), the test runner starts it as each device session starts, before the app launches, and keeps it up for the rest of the session. A call it does not find in the files (one inside an npm package, say) starts it on demand, at the first such call a test or hook makes; a trace that records network starts it with each test. A route only sees requests made after it is registered, so register routes before the app sends the requests they mock — at the top of the test or in a beforeEach, before navigating. If the proxy cannot start, route() throws Network capture disabled: … with the reason. When a test’s routes never fired and the capture shows why (HTTPS passed through uncaptured, or a proxy that started after the app had opened its connections), Tapsmith prints a warning naming the test; outside the test runner nothing starts the proxy, and the runner warns that the routes could not fire (see Network prerequisites). Route handlers only see traffic Tapsmith can decrypt; hosts that are configured for passthrough, or dynamically tunneled because an HTTP/2-capable client rejects the generated MITM certificate, cannot be matched.

See also: device.unroute(), device.unrouteAll().

  • url: string | RegExp | ((url: URL) => boolean) — URL pattern (glob), regex, or predicate
  • handler: (route: Route) => Promise<void> | void — handler that decides how to handle the request
  • options.times?: number — how many times to intercept (then auto-remove)
await device.route('**/api/posts*', async (route) => {
await route.fulfill({ json: [{ id: 1, title: 'Mocked' }] })
})

A route lasts as long as the scope that registered it: one registered in a test, beforeEach, afterEach or a test fixture is removed when the test ends; one registered in beforeAll (or afterAll) stays for every test of its describe block, or of the file at the top level, and is removed when that block ends. See Route lifetime.

device.unroute(url, handler?): Promise<void>

Section titled “device.unroute(url, handler?): Promise<void>”

Remove a previously registered route handler. If handler is omitted, all handlers for the pattern are removed.

Remove all registered route handlers, including those registered in beforeAll.

device.waitForRequest(urlOrPredicate, options?): Promise<TapsmithRequest>

Section titled “device.waitForRequest(urlOrPredicate, options?): Promise<TapsmithRequest>”

Wait for a network request matching the pattern. Needs the network proxy, which starts as it does for device.route(); start waiting before the action that sends the request. Rejects at once with Network capture disabled: … when the proxy cannot start, and a timeout says when no capture was running or the proxy only started with the wait.

  • urlOrPredicate: string | RegExp | ((request: TapsmithRequest) => boolean)
  • options.timeout?: number — timeout in ms (default: device timeout)

device.waitForResponse(urlOrPredicate, options?): Promise<NetworkResponseEventData>

Section titled “device.waitForResponse(urlOrPredicate, options?): Promise<NetworkResponseEventData>”

Wait for a network response matching the pattern. Needs the network proxy, as device.route() does, and reports a failed start, or a timeout that capture likely caused, as device.waitForRequest() does.

Resolves with a NetworkResponseEventData: method, url, status, headers, body (Buffer | null) and routeAction. body is decoded, as Playwright’s response.body() is: chunked framing is removed and a gzip, deflate or br Content-Encoding is decompressed, so JSON.parse(resp.body!.toString()) works against a compressing server. headers stay as sent, so content-encoding and transfer-encoding describe the wire bytes, not body. A body in another coding (such as zstd) is left as received. The postData of a request from device.waitForRequest() or device.on('request') is decoded the same way.

A waiter belongs to the test that creates it. If the test ends first (say, a step before await fails), the waiter ends with it and can’t time out later in another test. A waiter that times out while its test is still running, without being awaited, fails that test. Waiters created in a beforeAll hook last until the file ends.

Subscribe to network events: 'request' or 'response'. Needs the network proxy, which starts as it does for device.route(). A listener lasts as long as a route registered in the same place would (Route lifetime).

device.on('request', (req) => console.log(req.url))
device.on('response', (resp) => console.log(resp.status))

Unsubscribe from network events.

The Route object is passed to route handlers. It provides methods to decide how to handle the intercepted request.

Returns the intercepted request.

Abort the request. Optional errorCode: 'connectionrefused', 'connectionreset', 'timedout'.

Continue the request to the server with optional modifications.

  • overrides.url?: string — override the request URL. Supports both same-origin path changes (e.g. /v2/posts) and cross-origin redirection (e.g. https://staging.example.com/api/posts). When the host differs, the Host header is automatically updated.
  • overrides.method?: string — override the HTTP method
  • overrides.headers?: Record<string, string> — override headers
  • overrides.postData?: string | Buffer — override request body

Return a mock response without contacting the server.

  • options.status?: number — HTTP status code (default: 200)
  • options.headers?: Record<string, string> — response headers
  • options.body?: string | Buffer — response body
  • options.contentType?: string — content-type header
  • options.json?: unknown — convenience: JSON-serializes and sets content-type
  • options.path?: string — read body from a file

route.fetch(overrides?): Promise<FetchedAPIResponse>

Section titled “route.fetch(overrides?): Promise<FetchedAPIResponse>”

Fetch the actual response from the server. Returns a FetchedAPIResponse that you can inspect and modify before calling route.fulfill().

  • overrides.url?: string — override the URL to fetch from (supports cross-origin, same as route.continue())
  • overrides.method?: string — override the HTTP method
  • overrides.headers?: Record<string, string> — override headers
  • overrides.postData?: string | Buffer — override request body
await device.route('**/api/users/*', async (route) => {
const response = await route.fetch()
const data = response.json() as { name: string }
data.name = 'Modified'
await route.fulfill({ json: data })
})

Properties: method, url, headers, postData, isHttps.

Returned by route.fetch(). Properties: status, headers. Methods: body(), text(), json().