fix(dev): WP-37 — dev-switcher resets scenario/role instead of sticking
currentScenario()/currentRole() read the URL param before sessionStorage, so a
stale ?scenario=/?role= in the address bar overrode the switcher on reload
("stuck on slow"). The switcher now strips both dev params from the URL
(pure stripDevParams + history.replaceState) before reloading, so the stored
value wins.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,31 @@
|
|||||||
|
# WP-37 — Dev-switcher reset fix (scenario/role)
|
||||||
|
|
||||||
|
Status: done
|
||||||
|
Phase: 8 — platform/DX/showcase
|
||||||
|
|
||||||
|
## Why
|
||||||
|
|
||||||
|
The WP-33 dev switcher can't reset scenario/role back to `default`/`drafter` — it gets "stuck"
|
||||||
|
(e.g. on `slow`). Cause: `currentScenario()`/`currentRole()` read the URL `?scenario=`/`?role=`
|
||||||
|
param **before** sessionStorage, so once a param is in the address bar, `location.reload()` (same
|
||||||
|
URL) re-reads the stale value and overrides what the switcher just stored.
|
||||||
|
|
||||||
|
## Decisions
|
||||||
|
|
||||||
|
- Once the switcher is used, **sessionStorage is authoritative**; a leftover URL param must not
|
||||||
|
win. Strip both dev params from the URL (`history.replaceState`) before reloading.
|
||||||
|
- Extract the URL rewrite as a **pure** `stripDevParams(href)` so it's unit-testable without
|
||||||
|
touching `location.reload()`.
|
||||||
|
|
||||||
|
## Files
|
||||||
|
|
||||||
|
- `src/app/shared/infrastructure/dev-params.ts` (+ `dev-params.spec.ts`) — pure `stripDevParams`.
|
||||||
|
- `src/app/shared/ui/debug-state/debug-state.component.ts` — `switchRole`/`switchScenario` call
|
||||||
|
`applyAndReload()` (replaceState with stripped URL, then reload).
|
||||||
|
|
||||||
|
## Acceptance criteria
|
||||||
|
|
||||||
|
- [x] Switching scenario/role to any value (incl. default/drafter) sticks after reload, even when
|
||||||
|
a `?scenario=`/`?role=` param was in the URL.
|
||||||
|
- [x] `stripDevParams` removes both params, keeps other params + path/hash (spec).
|
||||||
|
- [x] `npm run ci` green.
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
import { describe, it, expect } from 'vitest';
|
||||||
|
import { stripDevParams } from './dev-params';
|
||||||
|
|
||||||
|
describe('stripDevParams (WP-37)', () => {
|
||||||
|
it('removes ?scenario and ?role so the stored dev value wins on reload', () => {
|
||||||
|
expect(stripDevParams('http://localhost:4200/dashboard?scenario=slow&role=admin')).toBe(
|
||||||
|
'http://localhost:4200/dashboard',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('keeps unrelated query params and the path/hash', () => {
|
||||||
|
expect(stripDevParams('http://localhost:4200/beheer/zaken?scenario=error&tab=2#top')).toBe(
|
||||||
|
'http://localhost:4200/beheer/zaken?tab=2#top',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('is a no-op when neither param is present', () => {
|
||||||
|
expect(stripDevParams('http://localhost:4200/dashboard')).toBe(
|
||||||
|
'http://localhost:4200/dashboard',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
/**
|
||||||
|
* Remove the dev-only `?scenario=` and `?role=` params from a URL (WP-37). Once the
|
||||||
|
* dev switcher (debug-state) has been used, sessionStorage is the authoritative source
|
||||||
|
* for both — `currentScenario()`/`currentRole()` read the URL FIRST, so a stale param
|
||||||
|
* left in the address bar would override the switcher on reload (the "stuck on slow"
|
||||||
|
* bug). Stripping the params before reload lets the stored value win. Pure: returns the
|
||||||
|
* rewritten href, mutates nothing.
|
||||||
|
*/
|
||||||
|
export function stripDevParams(href: string): string {
|
||||||
|
const url = new URL(href);
|
||||||
|
url.searchParams.delete('scenario');
|
||||||
|
url.searchParams.delete('role');
|
||||||
|
return url.toString();
|
||||||
|
}
|
||||||
@@ -7,6 +7,7 @@ import { map } from '@shared/application/remote-data';
|
|||||||
import { Role } from '@shared/domain/role';
|
import { Role } from '@shared/domain/role';
|
||||||
import { ROLES, currentRole, setRole } from '@shared/infrastructure/role';
|
import { ROLES, currentRole, setRole } from '@shared/infrastructure/role';
|
||||||
import { Scenario, SCENARIOS, currentScenario, setScenario } from '@shared/infrastructure/scenario';
|
import { Scenario, SCENARIOS, currentScenario, setScenario } from '@shared/infrastructure/scenario';
|
||||||
|
import { stripDevParams } from '@shared/infrastructure/dev-params';
|
||||||
import { maskBsn, redactProfile } from './mask';
|
import { maskBsn, redactProfile } from './mask';
|
||||||
|
|
||||||
// CIBG-GAP EXTENSION: n/a — devtool, no corresponding CIBG concept; deliberately
|
// CIBG-GAP EXTENSION: n/a — devtool, no corresponding CIBG concept; deliberately
|
||||||
@@ -143,10 +144,18 @@ export class DebugStateComponent {
|
|||||||
|
|
||||||
switchRole(r: Role): void {
|
switchRole(r: Role): void {
|
||||||
setRole(r);
|
setRole(r);
|
||||||
location.reload();
|
this.applyAndReload();
|
||||||
}
|
}
|
||||||
switchScenario(s: Scenario): void {
|
switchScenario(s: Scenario): void {
|
||||||
setScenario(s);
|
setScenario(s);
|
||||||
|
this.applyAndReload();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Strip the dev params from the URL before reloading (WP-37) so a stale ?scenario=/?role=
|
||||||
|
// in the address bar can't override the value the switcher just stored (currentScenario/
|
||||||
|
// currentRole read the URL first) — otherwise a switch to "default"/"drafter" gets stuck.
|
||||||
|
private applyAndReload(): void {
|
||||||
|
history.replaceState({}, '', stripDevParams(window.location.href));
|
||||||
location.reload();
|
location.reload();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user