Mock Server with Playwright
Mocked API + Playwright Cookbook
This cookbook demonstrates testing a Fusion Framework app end-to-end with a real browser,
against a real HTTP mock backend, using ffc mock-server and @playwright/test.
Overview
Most cookbooks show a framework feature in isolation. This one shows the integration between two
pieces of Fusion Framework tooling: @equinor/fusion-framework-cli-plugin-mock-server'sffc mock-server command (a standalone mock HTTP server, seepackages/cli-plugins/mock-server) and Playwright'swebServer option, which builds the application, starts both the mock server and the preview
server, and tears the servers down after the test run.
How it works
mocks/people.mock.tsoverrides an existing service-discovery entry
withserviceDiscovery: 'merge', inheriting the bundledpeopleschema while replacing itsgetPersonresponse.mocks/aurora-api.mock.tstemporarily adds the fictional
pre-productionaurora-apiservice withserviceDiscovery: 'new', its ownaurora-api.openapi.jsonschema, and deterministic component
values. It is absent from real service discovery during development, but must be registered
there before release.mocks/my-api.mock.tsdefinesmy-api, imports its OpenAPI schema,
and usesserviceDiscovery: falseto keep this custom service out of discovery.app.config.local.tsconfiguresmy-apidirectly athttp://my-api.localhost:4010for both normal and--mockdevelopment.ffc app serve --mock http://localhost:4010serves the production build, resolvesapp.config.local.ts, and points service discovery at the mock server. The
preview server automatically creates local proxy routes for the bundled services.src/routes/index.tsx,src/routes/people/index.tsx, andsrc/routes/aurora/index.tsxgive each service lifecycle
scenario its own page. The direct-only scenario is the root index; named pages live in their own
directories. Each page callsuseHttpClient()directly so its explanation and the framework
integration agents should reproduce live together in one retrieval-friendly file.docs/articles/holds one help article per page, anddocs/faqs/holds FAQs that link to those articles.ffc mock-server
auto-detects the folder and serves it as thehelpservice. Each page heading has aHelpInfoButton— an info icon that callsuseHelpCenter().openArticle(slug)— so the dev portal opens the article explaining that page.src/routes/help/index.tsxalso opens a missing article and the
FAQs page.playwright/help-articles.spec.tsclicks every
info icon and asserts the dev portal's article, not-found, and not-supported states. Renaming an
article'sslugfails its test.src/analytics/tracks apage-viewedfeature with the route on every page
(usePageViewTracking) and reads the app's own usage
back through the Apps service'sPOST /apps/feature-events/query
(useAppFeatureEvents).src/routes/analytics/index.tsxtracks a demo feature and
lists the events. With--mock, the dev portal sends the events toffc mock-server, which reads
them like Fusion's analytics pipeline, records them to.fusion-mock/analytics.jsonl, and answers
the query with them plus the history inmocks/analytics.seed.jsonl(--analytics-seed).playwright/analytics.spec.tswaits for tracked features withcreateMockAnalytics, checks their data, reads them through the query, and keeps parallel
browser contexts apart.playwright.config.tsstartsffc mock-serverand runsffc app build
followed byffc app serve --mockas PlaywrightwebServerentries, then runs the
specs underplaywright/against the built app.playwright/mock-auth-personas.spec.tsusescreateMockAuthto select normal-user and administrator personas in isolated browser contexts.
The mock server issues a token for the scope requested by Fusion HTTP, andmy-apiechoes itsoidclaim so the test verifies the selected identity reached the backend.
Running it
pnpm --filter @equinor/fusion-framework-cookbook-app-react-mock-playwright testTo run the pieces individually while developing:
pnpm mock:server # ffc mock-server ./mocks --port 4010 --analytics-seed mocks/analytics.seed.jsonl
pnpm mock:dev # ffc app dev --mock http://localhost:4010, in another terminal
ffc app build
ffc app serve --mock http://localhost:4010 # in another terminalOr start both processes together with pnpm dev:mock.
For normal development with real service discovery plus selected local services, manually startpnpm mock:server, then run pnpm dev in another terminal. Plain ffc app dev discovers*.mock.ts files under mockServer.path (default mocks) and merges visible defineService
entries into real discovery by key.
It does not start ffc mock-server; unreachable local service URIs remain unreachable.
Key concepts
ffc mock-serverserves any directory of<name>.mock.tsservice modules over HTTP,
independent of Vite or the dev server — see the plugin's own
README for the full command reference.- Loopback browser origins can use credentialed mock auth on any port. Use
--allow-origin
only when tests serve apps or portals from a non-loopback origin. ffc app dev --mockandffc app serve --mockuse the mock server's discovery endpoint and
generate proxy routes automatically, so the app needs no customdev-server.config.ts. These
modes ignore normal discovery and use only mock-server presets plus localdefineService
modules.app serverequires an existing build; Playwright runsffc app buildbefore starting
the preview server.defineServicecontrols mock-server behavior and whether a service is advertised. Plainffc app devpoints visible definitions at the manually started mock server;'merge'overrides an existing entry,'new'adds a pre-production service and rejects key
collisions,'replace'deliberately replaces a complete definition, andfalsekeeps a
genuinely custom endpoint likemy-apidirect-only becauseapp.config.local.tssupplies its URL.defineServicekeeps a service's schema,components, declarativeroutes, andmiddlewarein one module.serviceDiscovery: 'replace'defines a complete service;serviceDiscovery: 'merge'inherits an earlier service schema. A declarative route remains
overridable at runtime via/@fusion-mock/:service/:operationId
(seeplaywright/playwright-override.spec.ts), whilemiddlewarealways wins over the generated mock and runtime override.- Local help articles: help docs use the same frontmatter markdown files
fhelpsyncs to
production. Edit an article whilepnpm mock:serverruns and open help again to see the change.
See Test help articles locally. - Usage analytics: give each test its own mock-auth user, so its analytics session is isolated,
and usecreateMockAnalytics().waitFor()rather than reading once, because the framework sends
analytics in batches. Tracked data arrives asdata_body_dataJSON text, exactly as the Apps
service returns it. A recording from one run can be loaded with--analytics-seedin the next.
See Test analytics locally. - Playwright's
webServerarray starts and stops each process for the whole test run — do
not startffc mock-serveryourself in the background; it is designed to run in the
foreground and shut down onSIGINT/SIGTERM. createMockAuthselects a user per browser context rather than accepting a bearer token.
Configure scopes on the app endpoint; the mock server mints an unsigned OBO-style token whenever
Fusion MSAL requests those scopes. CallingsetUseragain and reloading switches persona;
callingresetand reloading restores the default mock identity. Seemock-auth-personas.spec.tsfor the complete flow.
Persona claims here are placeholder identity data (userId, optionalclaims.roles); an
authorization-aware app instead reads its access roles through the Roles V2 module — see the
Roles V2 end-to-end adoption guide
for that persona-to-policy flow and the Roles V2 cookbook for a
runnable example.