Repository navigation
journey-runner.js has no browser-Back action and no 5xx check: a Mendix page that fails its data sources after Back still passes its landing guard (patch offered: { "do": "back" } and "ui": { "no5xx": true }) #564
Description
File: project-tests/e2e/journey-runner.js lines 162-172 (describeAct), 174-319 (act, default: throw new Error('unknown action …') at 319), ~740-830 (runJourney step loop: actions, then the ui.ready landing guard at 812-823) · Toolkit: 92331e4 (origin/master, 23 Oct) · mxcli: v0.27.0 · Mendix: 11.14.0
Summary
Two gaps, found together on one defect:
- No browser Back. The runner's actions are
nav,click,fill,combobox,dismissModal,wait. There is
no way to press the browser's Back button. Back is a path no in-app click exercises: the Mendix client rebuilds
the previous page from its own history and re-sends the client state it kept. Defects that live only there (a
new, unsaved object in the client state that the role may not create, for example) cannot be expressed as a
journey, so they can only be found by the monkey's randombackAfterSubmitand cannot be pinned by a journey or
a named control after the fix. - No 5xx check. When a page's data sources fail (HTTP 560 on
/xas/), Mendix can still render the page frame:
the layout, the headings and the grid container are there, the rows are not. Theui.readylanding guard sees
its selector and records PASS. Only the monkey's oracle looks at HTTP status, so a journey walk reads green over a
page whose every server call failed.
Local patch (about 36 lines, one commit, in use since 23 Oct)
a. { "do": "back" } in act() (and a describeAct line, verb "Browser Back"):
case 'back': {
await page.goBack({ waitUntil: 'networkidle', timeout: a.timeout || 15000 }).catch(e => {
if (!/timeout/i.test(e.message)) throw e;
console.log(` WARN back: networkidle not reached in ${a.timeout || 15000} ms; settling as usual`);
});
const url = page.url();
const base = String(cfg.baseUrl).replace(/\/+$/, '');
if (!url || (url !== base && !url.startsWith(base + '/')) || /\/login\.html(?:[?#]|$)/.test(url))
throw new Error(`Back left the app (now at ${url}): the previous action pushed no history entry`);
break;
}A networkidle timeout alone is not a failure (a polling page never goes idle; the history move has happened). Back
leaving the app is a failure, because every later check would measure the login page or another site. The same
"left the app" test is monkey.js's leftApp().
b. Opt-in "ui": { "no5xx": true } on a step: a page.on('response') listener collects every answer >= 500
from the step's first action until its landing guard, and the step gets one more row:
<step>: no HTTP 5xx during the step PASS
<step>: no HTTP 5xx during the step FAIL 2 answer(s) >= 500: 560 POST <base>/xas/; 560 POST <base>/xas/
The row is recorded before the not-reached bail-out, so when a 5xx is why the page never landed, the walk shows
the cause next to the landing FAIL. The listener is removed on every exit path (the step's seal() finaliser).
Opt-in, so no existing journey changes result.
Evidence it works
A Back journey written for the defect (click an action that starts a background job, press Back, land on the page
again, no5xx: true) went 60 PASS / 2 FAIL on the defective build (both FAILs the no5xx rows, 560 on /xas/;
the landing guard PASSED on the same steps) and 62 / 62 PASS after the fix. A named control for it was PROVEN.
Suggested fix
- Port
backandui.no5xx(or take the patch as a PR). - Consider making
no5xxthe default for every step, with"no5xx": falseto opt out. A 5xx during a user step is
almost never intended; the opt-in was chosen locally only to keep existing results stable. - Add both to
--positive-control: a mutant that setsui.no5xxon a step whose action is pointed at a request
that 500s (or that asserts no5xx after an action known to fail), so the check has been seen going red. examples/validate-journeys.js: addbacktoACTIONSand acceptui.no5xxas a boolean (comment on validate-journeys.js rejects the runner's offline/online verbs and does not check the notMeasurableWhen step tag #522).- Document both in
skills/journey-proof.md(the action table and theuirung). - Self-test: a fixture page that answers 560 on load must give
reachedPASS andno HTTP 5xxFAIL;backafter an
action that pushed no history must throw "Back left the app".
How to retest
A journey: open a page, click a button, { "do": "back" }, land on the page again with ui: { ready, no5xx: true }.
On a page with a client-state defect after Back: the no5xx row FAILs while reached PASSes. On a clean page: all PASS.
Activity
MendixMau commentedon Oct 23, 2026
Triage: DISCUSS track. It adds a new journey JSON action (
back) and a new field (ui.no5xx), and other tools read the journey format (examples/validate-journeys.js:32,--positive-control,skills/journey-proof.md), verifiedProblem:
act()knows only nav/click/fill/combobox/dismissModal/wait and throws on anything else (project-tests/e2e/journey-runner.js:174-319, throw at:319). The runner has nopage.on('response')listener, and the landing guard (:812-823) records PASS as soon as its selector appears. A page whose/xas/calls all return 560 still walks green. Today onlymonkey.jschecks for 5xx (:99) and Back (backAfterSubmit,leftApp:202). This affects anyone proving role-restricted pages; #565 is a real defect it misses.Value: High. It closes a false-green path on the main proof rung, and the reporter has field evidence (60/2 FAIL on the broken build, 62/62 after the fix).
Options:
backandui.no5xxas opt-in, using the reporter's patch. Update the validator'sACTIONSset and itsuichecks, document both in journey-proof.md, and add a fixture for each. No existing journey changes result, and the happy path costs nothing extra.no5xxis on by default. Synced projects may see new FAIL rows and their verify-module journey grade may flip. That is the right behaviour, but it is a regression to them.no5xxcontrol mutant. If it goes inCONTROL_RUNGS, every journey withoutno5xxgets an UNPROVEN rung, its control exits 1 and verify-module reports FINDING. It also adds one more walk per journey.Recommendation: Option 1 now. Default-on (option 2) after one release of field data. Add the mutant only as conditional, so a journey without
no5xxgets no rung for it.Decision needed from you: Should
backandno5xxship opt-in only (option 1), or withno5xxon by default (option 2)? Reply "go option N" to build it.Generated by Claude Code
test(journeys): drive back and no5xx in a real Chromium (#564)
Bug sweep 2026-10-26: gates, runners, doctor, lint receipt, git hooks…
MendixMau commentedon Oct 26, 2026
Fixed in #578 (squash a088b4e): your patch is ported. It adds
{ "do": "back" }and an opt-in"ui": { "no5xx": true }, driven in a real Chromium in test-journey-back-no5xx. Turning no5xx on by default is on the roadmap (#579), since it changes results for existing journeys.Generated by Claude Code