Set up MSW foundations for migrating Duo Agentic Chat specs

Why

Duo Agentic Chat's Capybara feature specs are among the worst flakiness offenders in CI. They drive a real browser against a real duo-workflow-service and assert on a streaming UI, so they fail on timing rather than on behaviour:

This MR is part of an MR chain to migrate duo agentic chat Capybara specs coverage to MSW integration tests, which run in jsdom with no browser and no timing races.

This MR adds the test infrastructure that migration needs. It does not migrate anything yet — the specs follow in five separate MRs, and the Capybara removals come last. See the chain below.

How it works

Duo Chat streams over a websocket, and MSW (pinned at 1.3.5) cannot intercept websockets. So the fake has to be the transport itself.

websocket_mock.js replaces global.WebSocket with a controllable double. That is the lowest available seam, which means everything above it is real code under test rather than a stub:

Runs for real What that gives us
stream_worker.js framing, binary decoding, the open/close/error protocol, flushing the initial startRequest
stream_manager.js subscriber fan-out, message buffering, error escalation, the stall threshold
duo_agentic_chat_state_manager.vue checkpoint parsing, message reconciliation, cancel and tool-approval paths

What's in this MR

File Purpose
duo_agentic_chat/websocket_mock.js The WebSocket double: push checkpoints, close codes and errors; assert on what the client sent
duo_agentic_chat/test_setup.js Mounting helpers and DOM finders, mirroring how init_duo_panel.js wires the panel
handlers/duo_agentic_chat.js Runtime GraphQL handler for createAiDuoWorkflow, which has to mint an id per test
fixtures/ai/duo_agentic_chat.rb Three new generated fixtures
glql_spec.js, hydrate_thread_spec.js The two existing MSW specs, ported onto the harness with their assertions unchanged

Porting those two specs is deliberate: they were already green, so they are the cheapest proof the harness behaves.

Response shapes come from the backend

Every GraphQL response the handler serves takes its shape from a fixture generated against the real resolver, and overrides only the values that vary per test. So the field set, enum casing and __typenames are the API's, not a literal maintained by hand — there are zero hand-written __typenames in the handler. A schema change reaches the tests when fixtures are regenerated instead of drifting silently.

create_ai_duo_workflow.mutation.graphql.json is new for this reason: it is a real mutation against the real resolver, so the payload is backend-validated even though the id must be minted per test.

Note

Two fixtures here (get_rule_content, get_ai_chat_available_models_unpinned) are not used until later MRs in the chain. They live here because both it blocks sit in the same hunk of the generator, and test_setup.js couples to the generator by string (PROJECT_PATH must equal rules_project.full_path).

The MR chain

Seven MRs. Each targets the previous one, so they merge in order. Every MR adds only the helpers its own specs use, which is why the chain is linear rather than parallel — no MR asks a reviewer to accept an unused export.

# MR What it adds Files
1 this MR The harness, plus the two existing specs ported onto it 6
2 !248782 (merged) Navigation rail and Duo-disabled empty state 4
3 !248783 (merged) Send/stream/history round trip, plus cancel and retry 4
4 !248785 (merged) Tool-call rendering and the tool-approval round trip 3
5 !248787 (merged) Agent picker and model picker 4
6 !248791 (merged) Page and repository-rule context injection 3
7 !244590 (merged) Deletes the Capybara coverage MRs 2–6 replace 9

Verification

bundle exec rspec ee/spec/frontend/fixtures/ai/duo_agentic_chat.rb
yarn jest:msw-integration ee/spec/frontend/msw_integration/
VUE_VERSION=3 yarn jest:msw-integration ee/spec/frontend/msw_integration/

Green on both Vue 2 and Vue 3. The two ported specs keep one it each, so there is no net change in test count.

Notes

Test-only change, so no changelog entry.

Part of #603605 (closed)

Edited by Enrique Alcántara

Merge request reports

Loading
Loading