.ai/principles/distilled/testing-msw.md
spec/frontend/msw_integration/) when the test covers multi-component interaction on a single page, backend responses can be represented with auto-generated fixtures, and you do not need to verify database state, authorization, server-side validations, or real-time updates.spec/features/) instead when the test requires a real backend (database writes, authorization checks, server-side validations), navigation across multiple server-rendered pages, backend state not representable with fixtures, or behavior that depends on multiple Vue applications on the same page.yarn jest:msw-integration; DO NOT run with the default yarn jest.spec/frontend/msw_integration/ in a subdirectory mirroring the feature area (e.g. work_items/work_item_spec.js).handlers.js, server.js, test_setup.js, polyfills.js, test_helpers.js) configured automatically through jest.config.msw_integration.js; DO NOT duplicate their setup in individual test files.test_helpers.js so they are available globally in all MSW integration tests (auto-imported via Object.assign(global, testHelpers) in test_setup.js).work_items/handlers.js) and register them in the top-level handlers.js via featureHandlers/restEndpoints.rest.post handler for http://test.host/api/graphql in handlers.js as a thin GraphQL router that delegates to feature-specific resolver functions in order; DO NOT split a single GraphQL endpoint across multiple MSW handlers.ServerParseError: Unexpected end of JSON input, add the missing operation to the relevant feature handler file.{ operationName, variables, res, ctx } and return an MSW response if it handles the operation, or null to pass to the next resolver.handlers/ that uses loadFixturesMap to auto-load fixtures and build the handler.handlers.js by importing it and adding it to graphqlFeatureHandlers.ee/spec/frontend/fixtures/ and running it (see Generate Fixtures).bundle exec rspec ee/spec/frontend/fixtures/work_items_integration.rb); DO NOT hand-write JSON fixture files.it block to the fixture generator spec — the test name determines the output file path (e.g. "graphql/work_items/integration/my_query.query.graphql.json").snake_case matching the GraphQL operation name after camelCase conversion (e.g. get_work_item_state_counts.query.graphql.json maps to operation getWorkItemStateCounts).loadFixturesMap from fixture_utils.js to automatically load all JSON fixtures from a directory and map them to camelCase operation name keys; DO NOT manually import each fixture file.OPERATION_NAME_OVERRIDES in the handler file for any operation name that does not match the derived camelCase filename (e.g. EE-suffixed operations like getWorkItemsFullEE).fixtures and OPERATION_NAME_OVERRIDES into FIXTURE_RESPONSES; use MUTATION_OPERATION_HANDLERS for mutations that need dynamic responses based on input variables.OPERATION_HANDLERS map and look up the operation in the resolver function.snapshotRequests() before an action and expectGraphQLCalls(baseline, { expect, forbid }) inside waitFor after the action to verify that mutations update the Apollo cache without triggering unwanted network calls.snapshotRequests calls without using expectGraphQLCalls — expectGraphQLCalls throws a Jest diff on unexpected calls, making debugging easier.capturedRequests manually in your own test suite if stray operations fire after the global afterEach reset has already been called.assignRouter from test_helpers.js instead of calling the router factory directly, so test_setup.js can reset it between tests; DO NOT push routes manually.fullMount from test_helpers.js and the real apolloProvider; DO NOT use shallowMountExtended or mountExtended in MSW integration tests.waitFor from @testing-library/dom after actions that trigger API calls.beforeEach with apolloProvider.defaultClient.cache.reset() to prevent state leaking between tests.afterEach cleanup for wrapper destruction or Apollo client teardown — the global test_setup.js handles router resets, wrapper destroy, and metadata cleanup.server.listen, server.resetHandlers, or server.close calls in individual test files — server lifecycle is handled globally by test_setup.js.@testing-library/vue queries to locate elements.wrapper.find(), wrapper.findComponent(), wrapper.trigger(), wrapper.text(), wrapper.exists()) in MSW integration tests.vm.$emit(), vm.$data, or any component instance property; DO NOT use el.__vue__ or createWrapper() to obtain a VTU wrapper from a DOM element..querySelector(selector) instead of .find(selector), .click() instead of .trigger('click'), getText(el) from test_helpers.js instead of .text(), .getAttribute('name') instead of .attributes('name'), !== null instead of .exists().For the full picture, see: