What each test covers

Notes on the individual test files in /tests/. The hazards that apply to writing any of them — Web Crypto’s secure context, --headless=new, waiting on content, the @noble pin — are in tests/CLAUDE.md, which is loaded whenever you open this directory; this file is the lookup.

Where an entry below says a job “skips” for a reason, read it against the rule in tests/CLAUDE.md — A test that does not run is not a passing test — which changed what half of those sentences mean on 2026-09-02. A prerequisite that is genuinely ABSENT (an unset variable, MIT Kerberos not installed, a layout that is the tests image rather than a checkout) is still a skip, and is now a RECORDED one that reaches <skipped> in report.xml rather than exiting 0 as a pass. A prerequisite that is PRESENT and cannot do what the job needs — a mock that answered and does not publish an attribute, an endpoint that 404s, a stack the launcher already decided was up — is a FAILURE now. That second half is there because six probes reading a moved URL turned fifty-four jobs off on 2026-09-01 under a report that said failures="0". tests/expectation.js is the mechanism.

This is 139 of the 157 test files, not the suite. The entries below are the ones with something non-obvious to record; the rest are named at the foot of this file with no note, and their absence means nothing about their importance. To recheck the split:

ls tests/*.js | xargs -n1 basename | sort > /tmp/actual
grep -oE '^- .[a-z0-9_]+\.js.' docs/test-suite-map.md | sed 's/^- .//; s/.$//' | sort > /tmp/listed
comm -23 /tmp/actual /tmp/listed   # test files with no entry, plus the five helpers

Several tests are named for what they check rather than for the module they check, and that is not stylistic: tests/Dockerfile copies test scripts and the modules they exercise FLAT into one directory, so a test sharing a basename with a module silently overwrites it or is overwritten by it. jwk_pem.js is checked by jwk_pem_encoding.js, krb5_crypto.js by krb5_crypto_vectors.js, krb5_pac.js by krb5_pac_layout.js, dpop.js by dpop_vectors.js, jose_jwe.js by jose_jwe_encryption.js, url_safety.js by url_safety_schemes.js. The module keeps its own name because other code imports it. testsImageHasNoCollidingFilenames() in jwk_pem_encoding.js now fails the suite if a new pair ever collides — three tests had been reporting green in ~30ms while executing a module.

Individual test files in /tests/:

Test files with no note here

Helpers rather than jobs: browser_flags.js, wait_for.js, page_load.js, module_paths.js, run-report.js (the runner). page_load.js is the only one of those with a test of its own — page_load_retry.js — because what it promises is a negative (a page that loaded is not retried) that nothing else would notice losing. Since 2026-09-10 it has two entry points: loadPage(), which takes the id the page must hold, and loadUrl(), the same retry with no id for a caller whose own next line is the readiness check — which is what ~180 navigations across 65 files were swept onto, and what everyTargetNavigationGoesThroughPageLoad() in jwk_pem_encoding.js now holds every test to.

consent_screen.js is a sixth, added 2026-09-01, and it is the one worth reading before editing any OAuth or OIDC job here. The mock STS asks for consent now: the first time a given username signs in to a given client_id for a given scope, /oauth2/consent is drawn and nothing is issued until somebody presses Allow — oauth2.consentRequired, which is ON by default over there and is the one policy in that service that is. So every flow in this suite that signs a person in to the mock has one more hop in it than it had the day before, in the middle of a redirect chain that several of these jobs walk by hand.

It has two surfaces because this suite drives that service two ways. settleAuthorization() is for the jobs that follow redirects themselves with redirect: "manual" — it walks the hops that stay on the mock’s own origin and hands back the first one that leaves, which is the client’s redirect_uri and the thing those jobs were already reading. passInBrowser(driver, By) is for the Selenium jobs: it presses Allow if the button is there and returns quietly if it is not, and it returns IMMEDIATELY once the browser’s URL is no longer on a path the identity service owns, which is what keeps calling it after every sign-in from costing anything.

Neither of them asserts that the screen appeared, and that is deliberate rather than lax: a scope under oauthGlobalConsent, a username that has consented earlier in the same run and the setting turned off are all states in which the screen correctly does not appear, and a helper that insisted on it would make every job here also a test of the consent feature — failing in fifteen places for one reason. What tests the screen is sts/tests/vendored/sts_consent.js, in the mock’s own repository, which asserts that it appears, what is on it, that a GET of it records nothing, that a consent id is spendable once, that it cannot be answered by somebody else’s session, and that a global consent on an application’s entry stops everybody being asked without writing anything about anybody.

Two jobs are the exception to the sweep and each for its own reason. krb5_mit_client.js drives its flow with curl rather than a browser and reads one Location header, so its application is registered with oauthGlobalConsent: ["openid"] instead — which is configuration rather than a workaround, and makes its assertion STRONGER: the only screen that could now appear is the sign-in screen it already forbids. oauth2_device_authorization.js is the Keycloak half and meets Keycloak’s own consent page, which its existing page-walking loop already handles.

Jobs, in the same directory, carrying no entry above: