PR 19828: e2e tests in full, generate and integration modes

rancher/dashboard#19828 by codyrancher, draft, "[Experiment]", no issue. Head 2cb7332 on master 55438bb. Reviewed 2026-11-12 in two passes: Cody's process from codyrancher/ai-skills at 8b419f0 (A) and ours as a fresh agent (B). Every finding was rechecked at the head commit before it got here. Nothing is posted.

Preview: none. The PR changes only Cypress support code and the workflow, so the evidence is the head CI run, its artifacts, and the PR's own server.ts run locally on CI recordings. Cypress was not run locally.

Verdict: comment now; six things to fix before it leaves draftboth passes agree

Head CI run 43218397084
ModeAttempt 1Attempt 2Failures
full (e2e-test)17 / 1817 / 18kubewarden before all, also on master
generate16 / 1818 / 18
integration12 / 1813 / 18edit-fake-cluster (admin manager): finding 1 · user-retention (std usersAndAuths): finding 2 · compliance, logging (admin charts): shell window timing, 0 misses · not diagnosed: kontainer-drivers, name-annotation (admin manager), helmop (admin fleet), banners, performance (admin generic)

The PR body's numbers (integration 10/18) come from run 43194123695 at 17482b6, three commits back. Generate adds 252 and integration 140 job minutes to full's 230.

Findings, in the order of the review to post
1

Integration runs without RANCHER_VERSION, so the docs-link test fails every time

Should fix
Pass B

What happens

Full and generate pass RANCHER_VERSION; the new integration job does not. CURRENT_RANCHER_VERSION comes from it, so edit-fake-cluster.spec.ts expects https://ranchermanager.docs.rancher.com//how-to-guides/... while the replayed app links /v2.16/. The job is the third copy of e2e-test and has drifted twice: generate also lost the "Detect Rancher license" step, so its tags never get --@noPrime (no effect on master today, CI runs community rancher/rancher:head).

Evidence

CI: admin manager integration, attempt 1 and attempt 2: "Rancher version: not provided", then the assertion with docs.rancher.com//. Rechecked in the attempt-2 log.

In the merge

Only Pass B. Pass A saw the Prime drift and dropped it as "no effect on master", and never compared the env blocks of the copied jobs key by key.

Posted as

Integration doesn't get `RANCHER_VERSION` like full and generate do, so the log says "Rancher version: not provided" and `edit-fake-cluster.spec.ts` expects a `docs.rancher.com//` link, which fails it in admin manager on both attempts. Generate also dropped the Prime check that full has, so the two would run different tests on a Prime image.
2

A logged-in before-all hook gets the next test's pre-login 401

Should fix
both passes

What happens

When a hook asks for something it didn't record, lookup borrows the next test's recordings and pickEarly takes that test's first state, whatever the hook already did. user-retention's test clears its session first, so its first GET /v3/principals is a 401 (write 0) and the later one a 200 (write 3). Replay is faster than Rancher, the app's request lands in the before all hook after its login, gets the 401, and the hook fails with "Unauthorized 401: must authenticate".

Evidence

CI: standard_user usersAndAuths integration, attempt 1 and attempt 2: "13 replayed, 0 without a recording", then the 401. Same spec passes in full and generate. Test: both passes replayed that job's downloaded recording through the PR's own server.ts and got the 401; Pass B's one-line guard (no borrowing once the hook has written) returned the 200.
Harness output
before all  GET /rancherversion                                                                                -> 200
before all  GET /rancherversion                                                                                -> 200
before all  GET /v1/uiplugins                                                                                  -> 200
before all  GET /version                                                                                       -> 200
before all  GET /v1/management.cattle.io.settings?pagesize=100000&exclude=metadata.managedFields               -> 200
before all  GET /v1-public/authproviders?pagesize=100000&exclude=metadata.managedFields                        -> 200
before all  POST /v1-public/login                                                                              -> 200
before all  POST /v1/ext.cattle.io.selfuser                                                                    -> 201
before all  GET /v1/management.cattle.io.users?pagesize=1                                                      -> 200
before all  GET /v1/management.cattle.io.user/u-fzw97                                                          -> 200
before all  GET /v1/uiplugins                                                                                  -> 200
before all  GET /v3/principals (made inside the hook in CI)                                                    -> 401 {"type":"error","status":"401","message":"Unauthoriz

In the merge

Both passes, independently, with the same root cause.

Posted as

When a `before all` hook asks for something it didn't record, this borrows the next test's first recording, whatever the hook already did. user-retention's test clears its session first, so the hook's logged-in `GET /v3/principals` gets that test's pre-login 401, and the standard_user usersAndAuths job fails on both attempts. I got the same 401 replaying that job's recording through this server locally.
3

Answers from a fallback never count as a miss, so integration can pass a changed request

Suggestion
both passes

What happens

Only a request with no recording anywhere is a miss. A write whose body matches no recording gets a recorded write's success, a GET whose query matches none gets the closest recording of that path (even with zero matching params), and failing that the spec's other tests, other users' recordings and a global index answer. None of it reaches misses, the afterEach log or the spec summary, so the PR's own check ("logs no no recording: lines") passes while different data is served.

Evidence

Test (Pass A, PR's server.ts on a hand-made recording): a PUT with replicas: 0 got the recorded replicas: 3 200; a team-b filter got team-a's rows; a request only another spec recorded as standard_user was served to admin; the only miss was a path recorded nowhere. Test (Pass B, CI recording): a metadata.name=nobody filter got the full user list. CI: admin charts logged 45 "replaying a similar one" lines and 0 misses while 2 tests failed.
Harness output
GET recorded query                   GET /v1/things?filter=metadata.namespace=team-a
                                     -> 200 {"data":[{"id":"team-a/one"}]}
GET a different filter               GET /v1/things?filter=metadata.namespace=team-b
                                     -> 200 {"data":[{"id":"team-a/one"}]}
PUT a different body                 PUT /v1/things/team-a/one {"spec":{"replicas":0}}
                                     -> 200 {"id":"team-a/one","spec":{"replicas":3}}
GET only another spec/user recorded  GET /v1/secrets
                                     -> 200 {"data":[{"id":"recorded-by-another-spec-and-user"}]}
GET nothing recorded                 GET /v1/nothing
                                     -> 404 {"type":"error","status":404,"code":"NotFound","message":"e2e integration mode has no recording of GET /v1/nothing"}

test-end misses: {"misses":["GET /v1/nothing"]}

server console:
  [e2e integration] listening on https://127.0.0.1.sslip.io:18443
  [e2e integration] recordings: /var/folders/6v/k6yp126x6dd36kv4g1xtv1vr0000gn/T/rec-WLyQf6/admin
  [e2e integration] indexed 3 distinct requests across 2 recording sets
  [e2e integration] no exact recording, replaying a similar one: GET /v1/things?filter=metadata.namespace=team-b (demo > saves a thing)
  [e2e integration] no recording: GET /v1/nothing (demo > saves a thing)

In the merge

Both. Pass A tested writes and other users; Pass B tested filters and other scopes, and asked about other users as a question. Folded into one comment.

Posted as

Only a request with no recording anywhere counts as a miss. A save with a different body still gets the recorded success, a GET with a different filter gets the other filter's rows, and another spec's or user's recording can answer too, so integration can pass a change to what the UI sends, and a failing spec still logs "0 without a recording".
4

The new jobs also run on master, can't fail softly, and add about 390 job minutes

Suggestion
both passes

What happens

test.yaml runs on push to master and release-*, and generate and integration are gated only on the ci/skip-e2e label. So master would run all three modes, while the description says it keeps running against a real Rancher, and any replay failure turns the run red. Auto-retry already re-ran every run of this PR.

Evidence

CI, head run: attempt 1 full 17/18, generate 16/18, integration 12/18; attempt 2 full 17/18, generate 18/18, integration 13/18. Job minutes per attempt: full 230, generate 252, integration 140 (attempt 2: 229, 246, 142). The master-push part is from reading the workflow; nothing has run on master.

In the merge

Both: Pass A the master trigger, Pass B the soft-fail, retry and cost numbers. Rechecked the minutes from the jobs API.

Posted as

These jobs have no PR condition and can't fail softly, so every push to master would also run generate and integration (the description says master keeps running only full), and with 13 of 18 integration jobs green every run goes red and auto-retry runs it again. They also add about 390 job minutes per run.
5

Recordings keep the password and session cookie, in a folder git doesn't ignore

Suggestion
Pass B

What happens

Every POST /v1-public/login is stored with its body, and its response keeps Set-Cookie: R_SESS=token-.... Without TEST_RECORDINGS_DIR they go to cypress/recordings/, which .gitignore doesn't cover. CI only has the throwaway password on a k3d Rancher that dies with the job, so nothing real leaks here. The PR's test steps run generate against API=https://<rancher>, and the code ships in @rancher/cypress.

Evidence

CI artifact e2e-recordings-standard_user@userMenu@usersAndAuths: three HARs with the login body and R_SESS. Test: git check-ignore --no-index cypress/recordings/... exits 1, while cypress/videos matches .gitignore:95 (rechecked).

In the merge

Pass B kept it; Pass A saw the same data and dropped it as CI-only. Kept: the local case is real and the PR's own steps lead there.

Posted as

Recordings keep the login body with the password and the `R_SESS` cookie, and `cypress/recordings` isn't in `.gitignore`. In CI that's only the throwaway `password`, but following the PR's test steps against a real Rancher leaves a real password and a live token in untracked files. Could those be left out and the folder ignored?
6

A socket frame for a type the UI watches later than in the recording is lost

Suggestion
Pass B

What happens

pump moves past a Steve frame whose resourceType isn't watched yet instead of holding it. Frames are timed from the recording (capped at 1.5s), so a watch sent a little later than against Rancher means the resource.change never arrives and the UI waits for a state that never comes.

Evidence

Test, synthetic: one socket, watch at 50ms and a change at 200ms. Watching at 400ms the client gets only resource.start; at 50ms it gets both. Rerun after checking Pass B's harness copies server.ts unchanged. Not tied to a CI failure.

In the merge

Only Pass B. Posted with "made-up recording" in the text, so it doesn't read as a CI failure.

Posted as

A frame for a resource type the UI hasn't watched yet is skipped for good, so if the browser sends its watch a little later than it did against Rancher, that change never arrives. On a small made-up recording, watching at 400ms instead of 50ms lost it.
Q1

What is the plan for PRs?

Question
Integration replays the same run's generate artifact, so on a PR it always starts after a real-Rancher run, and the PR is slower, not faster. If PRs later replay master's recordings, a PR that changes a request has none for it. Both passes asked it.

Posted as

What's the plan for PRs? Integration replays this run's generate artifact, so it always waits for a real Rancher run. If PRs replay master's recordings later, how does a PR that changes requests get new ones?
Q2

The HAR_FILE claim in the description

Question
The description says the recordings are "the format the repo's HAR_FILE dev replay already reads". har-file.js only keeps responses with content.text, and these keep bodies in _file, so it kept 0 of 32 responses from user-retention.har. Pass B ran it; I rechecked the code.

Posted as

The description says the `HAR_FILE` dev replay already reads this format, but `har-file.js` only keeps responses with `content.text`, so it dropped all 32 from `user-retention.har`. Did you mean it's HAR in general?
N1

Stale docstring (optional)

Nit
Says it returns "the Cypress tasks it needs"; it returns the server. Kept because it misleads.

Posted as

This says it returns the Cypress tasks it needs, but it returns the server.
The two passes side by side
Pass A: Cody's processPass B: ours
Kept findings3 comments3 blockers, 4 suggestions, 4 nits, 4 questions
Before-all hook gets a 401 (2)✓ reproduced on the CI recording✓ reproduced, plus a tested one-line guard
Silent fallbacks (3)✓ writes, filters, other spec and user✓ filters, other scopes; other users as a question
Workflow on master, red, cost (4)✓ master trigger✓ plus soft-fail, auto-retry, +390 job minutes
Missing RANCHER_VERSION (1)✗ missed; saw only the Prime drift and dropped it✓ from the admin manager log
Credentials in recordings (5)seen, dropped as CI-only✓ kept for local runs
Socket frames lost (6)–✓ synthetic
HAR_FILE claim (Q2)–✓ ran the loader
Nits– (his rule: never post one)4, one kept
Acceptance table✓ 10 criteria from the description–
Full mode and consumers✓ extendConfig, the Jenkins config in qa-infra-automation✓ virtual-clusters-ui, rancher-ai-ui
Outputacceptance files, 3 condensed comments, 2 harnessesreview, 7-dimension grid, checked list, draft, 6 harnesses
Costabout 45 min in this session, about 45 tool calls20 min, 99 tool calls, 258k tokens

Lesson for both: when a PR adds a job that copies an existing one, diff the two jobs' env and steps key by key. Pass A noticed one dropped step and stopped there.

Pass A: Cody's my-pr-full-review
CriterionVerdictHow
AC1full unchangedMetServer returns early, hook gated on e2eMode; full 17/18, the failure is on master too
AC2generate records each passing testMetArtifact: one HAR page per test and hook; only passing tests committed
AC3integration without RancherPartly metNo Rancher step; 13/18 legs pass (findings 1 and 2)
AC4a write replays the same bodyPartly metPreferred, not required (finding 3)
AC5a read recorded after that writeMet (reading)priorWrite / lastWrites
AC6a socket frame once its write is madeMet (reading)Gives up after 5s and sends anyway
AC7CI runs all three and charts timingsMete2e-timings job; the PR body table
AC8master keeps running against RancherNot metMaster would also run generate and integration (finding 4)
AC9@rancher/cypress consumersMetws already a dependency; extendConfig doesn't call setupNodeEvents
AC10the manual test stepsCould not checkCypress isn't run locally; "no no recording: lines" can't show exact replay
Pass B: our checklist, fresh agent
Dropped in the merge
Kubewarden failure in full admin navigation (Pass B question)Same before all failure on master, run 42312559879; master's last 6 test.yaml runs are red. Not this PR.
No unit tests for the matcher (Pass B suggestion)True, and Jest ignores cypress/. Left out while it's an experiment whose matching changes every commit; worth raising once the design settles.
stop() never called, any for parsed JSON, new env vars not documented (Pass B nits)Look odd, mislead no one, and the last is premature for an experiment.
scripts/e2e-recordings copies serialize (Pass A)Five lines, commented as a copy. Taste.
What I'd post

Comment, body:

Nice experiment, and full mode looks untouched. I dug into the red integration jobs on the latest run, and a few of them come from the replay or the workflow rather than flaky tests.

Inline, 9 comments

  1. .github/workflows/test.yaml:252
    Integration doesn't get RANCHER_VERSION like full and generate do, so the log says "Rancher version: not provided" and edit-fake-cluster.spec.ts expects a docs.rancher.com// link, which fails it in admin manager on both attempts. Generate also dropped the Prime check that full has, so the two would run different tests on a Prime image.
  2. cypress/support/e2e-modes/server.ts:963
    When a before all hook asks for something it didn't record, this borrows the next test's first recording, whatever the hook already did. user-retention's test clears its session first, so the hook's logged-in GET /v3/principals gets that test's pre-login 401, and the standard_user usersAndAuths job fails on both attempts. I got the same 401 replaying that job's recording through this server locally.
  3. cypress/support/e2e-modes/server.ts:903
    Only a request with no recording anywhere counts as a miss. A save with a different body still gets the recorded success, a GET with a different filter gets the other filter's rows, and another spec's or user's recording can answer too, so integration can pass a change to what the UI sends, and a failing spec still logs "0 without a recording".
  4. .github/workflows/test.yaml:203
    These jobs have no PR condition and can't fail softly, so every push to master would also run generate and integration (the description says master keeps running only full), and with 13 of 18 integration jobs green every run goes red and auto-retry runs it again. They also add about 390 job minutes per run.
  5. cypress/support/e2e-modes/server.ts:48
    Recordings keep the login body with the password and the R_SESS cookie, and cypress/recordings isn't in .gitignore. In CI that's only the throwaway password, but following the PR's test steps against a real Rancher leaves a real password and a live token in untracked files. Could those be left out and the folder ignored?
  6. cypress/support/e2e-modes/server.ts:371
    A frame for a resource type the UI hasn't watched yet is skipped for good, so if the browser sends its watch a little later than it did against Rancher, that change never arrives. On a small made-up recording, watching at 400ms instead of 50ms lost it.
  7. .github/workflows/test.yaml:205
    What's the plan for PRs? Integration replays this run's generate artifact, so it always waits for a real Rancher run. If PRs replay master's recordings later, how does a PR that changes requests get new ones?
  8. cypress/support/e2e-modes/recordings.ts:11
    The description says the HAR_FILE dev replay already reads this format, but har-file.js only keeps responses with content.text, so it dropped all 32 from user-retention.har. Did you mean it's HAR in general?
  9. cypress/support/e2e-modes/config.ts:60 (optional)
    This says it returns the Cypress tasks it needs, but it returns the server.