web-mocks-msw-47187b — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited web-mocks-msw-47187b (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
Quick Guide: MSW intercepts requests at the network layer, so application code never learns it is mocked. One handler set serves both environments —setupWorkerfrommsw/browserin development,setupServerfrommsw/nodein tests — and the two are not interchangeable. Keep response bodies in their own module so each variant (default, empty, error) is reusable and typed against your API's generated types.
Detailed Resources:
Handlers are shared; only the setup module and the lifecycle differ.
setupWorker from msw/browser, awaited before the app renders, with runtime variant switching to walk the UI through its states. Follow examples/browser.md.setupServer from msw/node, listening for the suite and reset between tests, with server.use() for one-test overrides. Follow examples/core.md.<critical_requirements>
Match the setup function to the environment. setupWorker needs service worker APIs and setupServer patches Node's request layer, so each fails in the other with an error that names a missing global rather than the swap.
Reset handlers in `afterEach` with `server.resetHandlers()`, so a server.use() override cannot decide the outcome of the next test.
Await `worker.start()` before rendering. Requests fired before the worker is ready reach the real network, which makes the first render of a suite intermittently different from the rest.
Keep response bodies in their own module, typed against your API's generated types — one fixture then serves several handlers, and a schema change fails at compile time instead of inside an assertion.
</critical_requirements>
Auto-detection: msw, setupWorker, setupServer, msw/browser, msw/node, http.get, http.all, HttpResponse.json, server.use, resetHandlers, onUnhandledRequest, mockServiceWorker.js, delay()
Applies to:
Handled elsewhere:
beforeAll and afterEach, not which runner provides them<philosophy>
MSW mocks the network, not the code that calls it. Nothing in the application is injected, wrapped or swapped, so what runs in a test is what runs in production — and the same handlers can drive a dev server, a test suite and a demo build.
That only holds while the handler set stays a description of the API rather than of one test's needs. Data lives in fixtures, handlers pick a fixture, and anything a single test needs differently arrives through an override that is thrown away afterwards.
</philosophy>
<decision_framework>
| You want | Use |
|---|---|
| A scenario several tests share | A named variant handler exported beside the default |
| One test to see something different | server.use(variant()) — discarded by resetHandlers() |
| To flip states by hand while developing | A variant map the default handler reads at request time |
| A response the code under test waits on | delay(ms) with an explicit duration |
Runtime variant switching belongs to development only. In a test it is shared mutable state that survives the test that set it, where server.use() is scoped and self-cleaning.
</decision_framework>
<patterns>
Fixtures live apart from handlers, typed against the API's generated types, so one body serves several handlers and a schema change surfaces as a type error.
// mocks/features.ts
import type { GetFeaturesResponse } from "./api-types";
export const defaultFeatures: GetFeaturesResponse = {
features: [{ id: "1", name: "Dark mode", status: "done" }],
};
export const emptyFeatures: GetFeaturesResponse = { features: [] };Data genuinely specific to one test stays inline in that test.
Full code: examples/core.md
Export the default handler and each alternative scenario from one module, so tests and the dev server pick a variant by name.
import { http, HttpResponse } from "msw";
export const getFeaturesHandlers = {
defaultHandler: () =>
http.get(ENDPOINT, () => HttpResponse.json(defaultFeatures)),
emptyHandler: () =>
http.get(ENDPOINT, () => HttpResponse.json(emptyFeatures)),
errorHandler: () =>
http.get(ENDPOINT, () => new HttpResponse("Server error", { status: 500 })),
};HttpResponse.json sets the JSON content type and answers 200 unless init.status says otherwise, so only the error variant states a code. A non-JSON body — plain text, an empty error — goes through new HttpResponse(body, init).
Full code, including a default handler that reads a variant map at request time: examples/core.md
The handler array is shared; the module that consumes it is chosen by environment.
// browser-worker.ts
import { setupWorker } from "msw/browser";
export const browserWorker = setupWorker(...handlers);
// server-worker.ts
import { setupServer } from "msw/node";
export const server = setupServer(...handlers);Full code, with app integration: examples/browser.md
Listen once, reset between tests, close at the end. server.use() prepends a handler that resetHandlers() then removes.
beforeAll(() => server.listen());
afterEach(() => server.resetHandlers());
afterAll(() => server.close());
it("renders the empty state", async () => {
server.use(getFeaturesHandlers.emptyHandler());
renderApp();
});Full code: examples/core.md
</patterns>
<red_flags>
Breaks at runtime:
setupServer in a browser bundle, or setupWorker in Node — the error names a missing global, so it reads as an environment problem rather than a swapped importworker.start() without await — the first requests race the worker and reach the real network, which shows up as an intermittent failure in whichever spec runs firsttypeof window checkresetHandlers() in afterEach — an override outlives its test, so a suite passes alone and fails in a different orderworker.start() that is not behind an environment guard — the service worker registers in the production bundle and real users are served fixturesSurprising behaviour:
delay() with no argument is a random 100–400ms wait in the browser and is negated in Node, so a test that needs a pause has to pass an explicit durationserver.use() overrides persist until resetHandlers(); they do not expire when the test that added them endshttp.all() matches every method on a path, so a request sent with the wrong verb still gets a successful responseonce: true makes a handler match a single request and then stop, which is what sequential responses need and what makes a later request fall through unnoticedonUnhandledRequest on start/listen, a request nobody wrote a handler for passes through quietly, so a missing mock looks like a backend fault — it takes "bypass", "warn" or "error", which is the difference between silent, noisy and fatal</red_flags>
~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.