type
PathParams
export type PathParams = { readonly [string]: string };Parameters captured from a path, by name.
API reference
MSW-compatible request mocking over the platform's own fetch, part of the Unified Toolchain for Flow.
Written from the source by uf doc when this site was built: the signature and the comment above each export, grouped by the specifier a program imports it from.
@uniflowed/mocktype
PathParamsexport type PathParams = { readonly [string]: string };Parameters captured from a path, by name.
type
RecordedRequestexport type RecordedRequest = {|
readonly method: string,
/** The absolute URL, after a relative one was resolved. */
readonly url: string,
readonly pathname: string,
/** Header names lower-cased, as the platform gives them. */
readonly headers: { readonly [string]: string },
/** The body as text, or `""` for a request that cannot have one. */
readonly body: string,
/** Whether a handler answered it. `false` is an unhandled request. */
readonly handled: boolean,
/** The body parsed as JSON. Throws for a body that is not JSON. */
readonly json: () => mixed,
|};One request the registry saw, with its body already captured.
@uniflowed/mock/handlertype
ResolverInfoexport type ResolverInfo = {|
/** The request itself, with its body unread. */
readonly request: Request,
/** What the path pattern captured. */
readonly params: PathParams,
/** The query string, already parsed. */
readonly query: URLSearchParams,
|};What a resolver is told about the request it is answering.
type
ResolverResultexport type ResolverResult = Response | void | null;What a resolver may return.
void and null both mean "not this one after all": the next matching handler is tried, and if there is none the request is unhandled. That is how a handler makes its decision on the *body* rather than the path — answering only the requests whose payload it recognises and leaving the rest alone.
passthrough() is also a Response, which is why this union has three members and not four; response.js says why it is spelled that way.
type
Resolverexport type Resolver = (info: ResolverInfo) => ResolverResult | Promise<ResolverResult>;The function a handler runs when its method and path match.
type
HandlerOptionsexport type HandlerOptions = {|
/**
* Answer at most one request, then step aside.
*
* The reason it exists is the retry test: two handlers for the same path, the
* first failing once and the second succeeding, is how a suite states "it
* retries" without a counter in a closure.
*/
readonly once?: boolean,
|};Options a handler may be declared with.
type
MockHandlerexport type MockHandler = {|
/** An upper-cased HTTP method, or `"ALL"` for every method. */
readonly method: string,
/** The path as it was written, for a failure message. */
readonly path: string,
readonly pattern: PathPattern,
readonly resolve: Resolver,
readonly once: boolean,
|};One declared answer: a method, a path, and what to do about it.
Frozen and inert. Nothing here knows about interception, and the same handler may be in two registries at once.
type
Httpexport type Http = {|
readonly all: Route,
readonly get: Route,
readonly post: Route,
readonly put: Route,
readonly patch: Route,
readonly delete: Route,
readonly head: Route,
readonly options: Route,
|};The methods http covers.
variable
httpexport const http: Http = {
all: (path, resolver, options) => declare("ALL", path, resolver, options),
get: (path, resolver, options) => declare("GET", path, resolver, options),
post: (path, resolver, options) => declare("POST", path, resolver, options),
put: (path, resolver, options) => declare("PUT", path, resolver, options),
patch: (path, resolver, options) => declare("PATCH", path, resolver, options),
delete: (path, resolver, options) => declare("DELETE", path, resolver, options),
head: (path, resolver, options) => declare("HEAD", path, resolver, options),
options: (path, resolver, options) => declare("OPTIONS", path, resolver, options),
};Handlers by HTTP method.
http.all matches any method, which is the right tool for a passthrough rule or a catch-all that fails loudly; everything else names one method, because a GET handler quietly answering a POST is a test that passes for the wrong reason.
function
matchHandlerexport function matchHandler(handler: MockHandler, method: string, url: URL): PathParams | null { ... }Whether this handler is the one for a request, and what its path captured.
null for a miss. Method first because it is a string comparison and the path walk is not, and because most misses in a real suite are the wrong method on a path that exists.
@uniflowed/mock/registrytype
UnhandledPolicyexport type UnhandledPolicy =
/** Reject the caller's `fetch`. The default. */
| "error"
/** Warn once and let it reach the network. */
| "warn"
/** Let it reach the network, silently. */
| "bypass";What happens to a request no handler claimed.
type
MockOptionsexport type MockOptions = {|
readonly onUnhandledRequest?: UnhandledPolicy,
/**
* What a relative URL is resolved against.
*
* Defaults to the document's origin when a DOM is installed — which is what
* `@uniflowed/react-testing` does — and to `http://localhost` otherwise, so
* a component calling `fetch("/api/users")` works under `uf test` without
* being rewritten to an absolute URL it would never use in production.
*/
readonly origin?: string,
|};How a registry behaves while it is listening.
type
MockRegistryexport type MockRegistry = {|
/** Start intercepting. Throws if something already is. */
readonly listen: (options?: MockOptions) => void,
/** Stop, and put the platform's `fetch` back. */
readonly close: () => void,
/** Add handlers that win over the declared set, until the next reset. */
readonly use: (...handlers: $ReadOnlyArray<MockHandler>) => void,
/**
* Drop every override, restoring the set `mock()` was given — or, when
* handlers are passed, replace that set with them.
*/
readonly resetHandlers: (...next: $ReadOnlyArray<MockHandler>) => void,
/** Every request seen since the last `clearRequests`, in request order. */
readonly requests: $ReadOnlyArray<RecordedRequest>,
readonly clearRequests: () => void,
|};A suite's mock server.
class
UnhandledRequestErrorexport class UnhandledRequestError extends Error { ... }A request nobody claimed, under the default policy.
Carries the method and URL as fields as well as in the message, so a test that means to provoke one can assert on them rather than on prose.
function
mockexport function mock(...handlers: $ReadOnlyArray<MockHandler>): MockRegistry { ... }A registry over these handlers.
Nothing is intercepted until listen(). The handlers given here are the *declared* set: use() layers over it and resetHandlers() takes those layers away, which is what makes a per-test override actually disappear between tests rather than at the end of the file.
@uniflowed/mock/responsetype
HttpResponseInitexport type HttpResponseInit = {|
readonly status?: number,
readonly statusText?: string,
readonly headers?: { readonly [string]: string },
|};The subset of ResponseInit a mocked response needs.
class
HttpResponseexport class HttpResponse extends Response { ... }A Response, with the constructors a test actually writes.
Subclassing rather than wrapping is the whole point: a handler returns one of these and the code under test receives something that passes instanceof Response, streams, clones and reads exactly like the response it will get from the real endpoint. A mock that hands back a plain object shaped like a response is a mock that stops agreeing with production the first time somebody calls .clone().
function
isNetworkErrorexport function isNetworkError(response: Response): boolean { ... }Whether a response stands for a network error rather than an HTTP one.
Lives here, next to HttpResponse.error, so the registry does not have to know how a network error is spelled — only that it must reject instead of resolving.
function
delayexport function delay(ms: number | "infinite" = 0): Promise<void> { ... }Wait, inside a resolver, before answering.
delay("infinite") never settles, which is how a test observes a loading state: the component renders its spinner, the assertion runs, and the request is still in flight. Nothing is scheduled for it, so a pending one holds nothing open and the runner exits normally.
globalThis.setTimeout is read at call time rather than captured at import, so uft.useFakeTimers() reaches a delay declared before the clock was installed. A delay that quietly ignored the fake clock would be a test that waits for real milliseconds while claiming not to.
function
passthroughexport function passthrough(): Response { ... }Let this request reach the real network after all.
Returned from a resolver that matched but decided not to answer — an endpoint a test wants to hit for real while everything around it is mocked. Distinct from returning nothing, which falls through to the next handler.
function
isPassthroughexport function isPassthrough(response: Response): boolean { ... }Whether a resolver's return value was passthrough().