Skip to content

Commit eafdd04

Browse files
committed
Add regression tests for AppRun routing bootstrap and pretty-link examples
- Introduced `apprun-routing-bootstrap.spec.ts` to cover routing bootstrap scenarios including hash/native routing, pretty-link opt-in, and initial-route suppression. - Added `pretty-link-examples.spec.ts` to validate routing intent for runnable AppRun examples, ensuring proper configuration and behavior across various examples and documentation.
1 parent bf164f3 commit eafdd04

31 files changed

Lines changed: 1009 additions & 353 deletions
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
# Plan: Configurable pretty-link routing for 6.0.0
2+
3+
## Goal
4+
5+
Align AppRun 6.0.0 with the 3.38.1 routing boundary: hash/native behavior without configuration, explicit `use_prettyLink()` or `true` for `/path` SPA navigation, and no false-mode boilerplate across ordinary examples and generated documentation.
6+
7+
## Current Context
8+
9+
- The uncommitted implementation already exposes `app.use_prettyLink(enabled = true)`, uses mutually exclusive listener wiring, removes hash-handler inference, and updates public declarations.
10+
- `src/apprun.ts` currently initializes its internal `prettyLinks` flag to `true`; this is the single framework control point that must return to the 3.38.1 value of `false`.
11+
- The default-on decision caused explicit false calls in Play iframe wrappers, complete HTML examples, `demo/main.ts`, `demo/apprun-play.html`, and 40 generated `docs/**/*.html` hosts.
12+
- `tests/apprun-routing-bootstrap.spec.ts` currently asserts default pathname routing and must be inverted to verify default hash/native routing plus explicit true.
13+
- `tests/pretty-link-examples.spec.ts` currently requires false calls in preview wrappers and generated documentation hosts; the revised policy must reject that boilerplate instead.
14+
- `README.md`, `WHATSNEW.md`, `MIGRATION.md`, routing references, and public type comments currently describe default-on behavior and must distinguish no call from the argument-free method call.
15+
- The TypeScript browser URL, sibling AppRun iframe runtime, synchronous compiled-script execution, CodeMirror selector fix, balanced invocation scanner, tracked Play bundle, and Play source map are independent fixes and remain in scope.
16+
- Verification remains Jest through `npm test`, the TypeScript/Rollup/Webpack pipeline through `npm run build`, ESLint through `npm run lint`, and the browser scenarios in `.docs/tests/test-explicit-pretty-links.md`.
17+
18+
## Decisions
19+
20+
- Initialize the internal `prettyLinks` flag to `false`; keep `app.use_prettyLink(enabled = true)` so `app.use_prettyLink()` is an explicit enable operation exactly like 3.38.1.
21+
- Install only hash initialization and `hashchange` when no call or false is selected. Install pathname initialization, `popstate`, and guarded anchor interception only after argument-free or explicit-true configuration.
22+
- Keep routing mode independent of registered `#` handlers and retain last-call-wins behavior before `DOMContentLoaded`.
23+
- Remove explicit false calls that exist only to counter the abandoned default-on policy: Play wrappers and hosts, generated documentation pages, complete non-routing examples, and the shared hash demo bootstrap.
24+
- Keep explicit routing calls where they teach or own routing: false in the hash-routing example, true in the path-routing example, CLI path SPA, add-components path SPA, and routing documentation.
25+
- Keep reusable Play bundles neutral about host routing. Under default-off, loading them must naturally preserve native host navigation without a host opt-out.
26+
- Preserve pinned TypeScript, sibling runtime resolution, classic compiled-script execution, editable CodeMirror input, `apprun-play.js` publishing, and its source map.
27+
- Do not add a feature flag, fallback detector, compatibility branch, or environment-specific routing mode.
28+
29+
## Phased Tasks
30+
31+
### Phase 1 - Reconcile the reversed default
32+
33+
- [x] Update `.docs/reqs/2026/07/13/req-explicit-pretty-links.md` so no call or false means hash/native and argument-free or true means pretty links.
34+
- [x] Update `.docs/tests/test-explicit-pretty-links.md` so browser scenarios exercise default-off behavior and explicit enablement.
35+
- [x] Review the revised REQ, AP, and E2E spec together and confirm the 3.38.1 compatibility boundary is unambiguous.
36+
37+
### Phase 2 - Restore the 3.38.1 framework default
38+
39+
- [x] Update `src/apprun.ts` so `prettyLinks` initializes to `false` while `use_prettyLink(enabled = true)` remains unchanged.
40+
- [x] Update source and declaration comments in `src/apprun.ts`, `src/router.ts`, `src/types.ts`, and `apprun.d.ts` to describe default hash/native mode and explicit pretty-link enablement.
41+
- [x] Preserve mutually exclusive listeners, last-call precedence, no-init behavior, base-path normalization, and removal of hash-handler inference.
42+
43+
### Phase 3 - Remove default-on configuration churn
44+
45+
- [x] Remove preview-level false calls from `src/apprun-code.tsx`, `src/apprun-play.tsx`, and ordinary new-tab generation in `demo/components/play.tsx` without changing TypeScript loading or compiled-script timing.
46+
- [x] Remove false calls added solely for default-on behavior from `demo/main.ts`, `demo/apprun-play.html`, complete non-routing README/What's New examples, and all tracked generated documentation hosts.
47+
- [x] Keep false only in deliberate hash-routing guidance/examples and keep true in deliberate `/path` applications and examples.
48+
- [x] Remove the generated-host opt-out explanation from `docs/about/index.html` because reusable Play now inherits the framework's native-navigation default.
49+
50+
### Phase 4 - Rewrite regression policy around opt-in pretty links
51+
52+
- [x] Update `tests/apprun-routing-bootstrap.spec.ts` to execute default hash/native listeners and explicit argument-free/true path listeners.
53+
- [x] Update `tests/pretty-link-examples.spec.ts` so generic examples, Play wrappers, hash-default hosts, and generated docs contain no unnecessary false call.
54+
- [x] Retain coverage for multiline/nested/semicolonless invocation discovery, Play runtime alignment, source-map publishing, editor input, and the two explicit routing examples.
55+
- [x] Add built-bundle assertions that loading Play without configuration leaves native host links alone and that explicit true enables path interception.
56+
57+
### Phase 5 - Align public guidance
58+
59+
- [x] Update `README.md`, `WHATSNEW.md`, and `MIGRATION.md` to state that no call or false preserves hash/native behavior while argument-free or true enables pretty links.
60+
- [x] Update `ai/building-apprun-spa.md` and `skills/apprun/references/routing-navigation.md` so `/path` SPAs explicitly call true and generic examples remain configuration-free.
61+
- [x] Confirm documentation never describes argument-free `use_prettyLink()` as equivalent to making no call.
62+
63+
### Phase 6 - Rebuild, verify, and review
64+
65+
- [x] Run `npm test -- --runInBand tests/apprun-routing-bootstrap.spec.ts tests/router.spec.ts tests/typescript-declarations.spec.ts tests/pretty-link-examples.spec.ts` and record focused evidence.
66+
- [x] Run `npm test -- --runInBand`, `npm run build`, and `npm run lint`; retain generated Pages bundles and report only actual warnings or failures.
67+
- [x] Execute the built-bundle routing and Play scenarios from `.docs/tests/test-explicit-pretty-links.md`.
68+
- [x] Run `git diff --check` and repeated code review until no default-on wording, unnecessary false calls, stale generated-host changes, or major findings remain.
69+
70+
## Validation
71+
72+
- Focused routing tests: `npm test -- --runInBand tests/apprun-routing-bootstrap.spec.ts tests/router.spec.ts tests/typescript-declarations.spec.ts tests/pretty-link-examples.spec.ts`.
73+
- Full unit suite: `npm test -- --runInBand`.
74+
- Production build: `npm run build`.
75+
- Static analysis: `npm run lint`, reporting the existing warning baseline separately from errors.
76+
- Browser behavior: execute `.docs/tests/test-explicit-pretty-links.md` against built bundles and inspect click prevention, `pushState`, `hashchange`, `popstate`, compiler availability, and editor input.
77+
- Scope: `git diff --check`, `git status --short`, and a requirement-focused review of every remaining changed file.
78+
79+
## Rollback / Risk
80+
81+
- Applications that relied on the temporary uncommitted default-on behavior must explicitly call `app.use_prettyLink(true)`; this is intentional and matches 3.38.1.
82+
- The argument-free method call enables pretty links even though making no call leaves them disabled. Documentation and tests must keep that distinction explicit.
83+
- Removing false calls must not remove deliberate hash-mode teaching examples or disturb the Play compiler/runtime/editor fixes.
84+
- Generated documentation pages should return to their prior HTML except for independently required runtime asset changes; broad host-page churn is specifically rejected.
85+
- No schema, persisted data, dependency, package version, or release publication is involved.
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
# Configurable pretty-link routing for 6.0.0
2+
3+
## Problem
4+
5+
Making pretty-link routing the 6.0.0 default forces every hash-routed, multi-page, documentation, and non-routing host to add `app.use_prettyLink(false)` merely to preserve normal browser navigation. That creates broad example and generated-page churn for an opt-in SPA feature. AppRun 3.38.1 already established the safer boundary: routing remains hash/native by default, while applications that own `/path` navigation enable pretty links explicitly.
6+
7+
## Requirement
8+
9+
AppRun 6.0.0 must expose the typed `app.use_prettyLink` startup API with the same default-off behavior as 3.38.1. Loading AppRun without a configuration call must use hash initialization and native browser links. Calling `app.use_prettyLink()` or `app.use_prettyLink(true)` before `DOMContentLoaded` must enable pathname initialization, `popstate`, and guarded same-origin anchor interception. Calling `false` must explicitly select the default hash/native mode. Play compiler, runtime-alignment, generated-asset, and editor-input fixes must remain intact without requiring false-mode boilerplate in ordinary examples or hosts.
10+
11+
## Acceptance Criteria
12+
13+
- [x] AppRun 6.0.0 initializes in hash/native mode when the application makes no `use_prettyLink` call.
14+
- [x] Default startup initializes from `location.hash`, listens for `hashchange`, and does not attach the path `popstate` listener or body-level anchor interceptor.
15+
- [x] `app.use_prettyLink()` and `app.use_prettyLink(true)` enable pathname initialization, `popstate`, and eligible same-origin anchor interception with the existing `pushState` and route behavior.
16+
- [x] `app.use_prettyLink(false)` explicitly selects hash initialization, `hashchange`, and native ordinary links.
17+
- [x] The last `use_prettyLink` call before `DOMContentLoaded` determines the mode, and later calls do not rewire installed listeners.
18+
- [x] Registering `#` or `#/` handlers does not silently override the default or an explicit routing choice.
19+
- [x] `apprun-no-init` and `app['no-init-route']` suppress only the initial route call in either mode.
20+
- [x] Public TypeScript declarations expose `use_prettyLink(enabled?: boolean): void`.
21+
- [x] Public routing and migration documentation distinguishes no call or false as hash/native mode from argument-free or true as pretty-link mode.
22+
- [x] Ordinary Play previews, reusable Play bundles, generated documentation hosts, complete non-routing HTML examples, and the main hash-routed demo require no false-mode call.
23+
- [x] The two Play routing examples remain explicit: `Routing (component event)` demonstrates `false`, and `Routing (mount options)` demonstrates `true`.
24+
- [x] Repository examples call `use_prettyLink` only when routing behavior is the subject or when a page intentionally owns `/path` navigation.
25+
- [x] Automated coverage rejects false-mode boilerplate in generic examples and generated hosts while requiring explicit true in deliberate path-routing applications.
26+
- [x] Both Play generators load `typescript@5.8.3/lib/typescript.js` before calling `ts.transpileModule`.
27+
- [x] Play previews load the AppRun HTML runtime from the same local or deployed build as the Play component.
28+
- [x] The Play page hides only its direct source textarea so CodeMirror remains editable.
29+
- [x] Tracked Pages bundles and referenced source maps are published from the current 6.0 build.
30+
- [x] Listener-level routing tests, example-policy tests, the full unit suite, and the production build pass with the default-off contract.
31+
32+
## Constraints
33+
34+
- Preserve the public name and signature: `app.use_prettyLink(enabled?: boolean)`.
35+
- Match 3.38.1 semantics exactly at the configuration boundary: internal mode starts `false`, while an omitted method argument defaults to `true`.
36+
- Treat routing mode as startup configuration selected before `DOMContentLoaded`.
37+
- Preserve direct route events, hierarchical matching, `basePath`, no-init behavior, router events, 404 behavior, and guarded anchor eligibility.
38+
- Do not restore hash-handler detection as a hidden mode switch.
39+
- Remove only configuration and documentation churn made unnecessary by the default-off decision; preserve the Play compiler, runtime, editor, test-quality, and publishing fixes.
40+
- Preserve tracked deployment outputs under `demo/` and `docs/assets/` while keeping `dist/` generated and ignored.
41+
42+
## Non-Goals
43+
44+
- Supporting routing-mode changes after AppRun initializes.
45+
- Redesigning link eligibility, route matching, or component registration.
46+
- Adding an environment variable, compatibility shim, alias, or second routing API.
47+
- Preserving the abandoned 6.0.0 default-on policy through widespread explicit-false calls.
48+
- Changing the package version or publishing a release.
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
# E2E: Configurable pretty-link routing for 6.0.0
2+
3+
## Scenario 1 - Hash and native navigation are the default
4+
5+
1. Load a page that imports the built AppRun browser bundle without calling `app.use_prettyLink`.
6+
2. Register observable hash and path route handlers plus an ordinary same-origin anchor.
7+
3. Confirm startup dispatches `location.hash`, installs `hashchange`, and does not install `popstate` or body-level click interception.
8+
4. Click the anchor and confirm AppRun does not prevent native browser navigation.
9+
5. Change the hash and confirm AppRun dispatches the hash route.
10+
11+
## Scenario 2 - Argument-free and true explicitly enable pretty links
12+
13+
1. Load one page that calls `app.use_prettyLink()` and another that calls `app.use_prettyLink(true)` before `DOMContentLoaded`.
14+
2. Register a path route and a same-origin anchor on each page.
15+
3. Confirm pathname initialization, `popstate`, body-level guarded anchor interception, `history.pushState`, and path dispatch on both pages.
16+
4. Use browser Back and confirm the restored pathname is dispatched.
17+
18+
## Scenario 3 - Explicit false matches the default
19+
20+
1. Load a page that calls `app.use_prettyLink(false)` before `DOMContentLoaded`.
21+
2. Confirm its listener set and native-link behavior match an unconfigured page.
22+
3. Confirm false remains useful as explicit documentation for a hash-routing example.
23+
24+
## Scenario 4 - The last pre-startup configuration wins
25+
26+
1. Call false and then true before `DOMContentLoaded`; confirm pretty-link path navigation.
27+
2. Repeat with true followed by false; confirm hash routing and native ordinary links.
28+
3. Call either mode after `DOMContentLoaded`; confirm installed listeners are not rewired.
29+
30+
## Scenario 5 - Hash handlers are not a hidden mode switch
31+
32+
1. Load an unconfigured page with and without `#` handlers; confirm both remain in default hash/native mode.
33+
2. Load an explicit-true page with a `#` handler; confirm it still uses pathname routing and path-link interception.
34+
35+
## Scenario 6 - Initial-route suppression is mode-independent
36+
37+
1. Load default-off and explicit-true pages with `apprun-no-init`, then repeat with `app['no-init-route']`.
38+
2. Confirm no page dispatches an initial route.
39+
3. Confirm later hash navigation still works in default mode and later path/Back navigation works in explicit-true mode.
40+
41+
## Scenario 7 - Play and examples avoid false-mode boilerplate
42+
43+
1. Load all 30 named Play entries.
44+
2. Confirm `Routing (component event)` explicitly demonstrates false and `Routing (mount options)` explicitly demonstrates true.
45+
3. Confirm the other 28 entries contain no routing-mode call.
46+
4. Confirm ordinary preview wrappers, reusable Play bundles, the main hash demo, complete non-routing documents, and generated documentation hosts contain no false call needed solely for startup.
47+
5. Confirm CLI and add-components `/path` applications explicitly call true.
48+
6. Confirm both preview documents load `typescript@5.8.3/lib/typescript.js`, expose `ts.transpileModule`, and load the sibling AppRun HTML runtime without a missing-method error.
49+
50+
## Scenario 8 - Published Play assets preserve host behavior
51+
52+
1. Build the repository and load `docs/assets/apprun-play.js` on an unconfigured host with an ordinary same-origin link.
53+
2. Confirm the link is not prevented because AppRun defaults to hash/native mode.
54+
3. Repeat on a host that calls `app.use_prettyLink(true)` before `DOMContentLoaded`; confirm path interception remains enabled after Play loads.
55+
4. Confirm Play iframes resolve `docs/assets/apprun-html.js` beside the Play bundle.
56+
5. Confirm `docs/assets/apprun-play.js.map` exists beside the bundle referenced by its `sourceMappingURL`.
57+
58+
## Scenario 9 - Play source editor accepts input
59+
60+
1. Load the root Play page and focus the left CodeMirror editor.
61+
2. Type a visible change into the current example.
62+
3. Confirm the editor content changes and the preview reruns with the edited source.
63+
4. Confirm the original source textarea remains hidden without hiding CodeMirror's internal input textarea.
64+
65+
## Verification Evidence
66+
67+
- Focused routing and example-policy tests: 4 suites, 106 tests passed.
68+
- Full unit suite: 47 suites, 620 tests passed.
69+
- Production build: TypeScript, Rollup, Webpack, and Pages asset sync passed.
70+
- Lint: 0 errors; 87 pre-existing warnings.
71+
- Browser catalog sweep: all 30 Play entries loaded one preview iframe with no TypeScript diagnostics or console errors.
72+
- Browser interaction: the left CodeMirror editor accepted input and reran the preview; explicit true intercepted a `/contact` link and rendered the Contact route.

MIGRATION.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,30 @@ import { ROUTER_EVENT } from 'apprun';
5555

5656
AppRun no longer aliases itself to `window.React` or stores the previous React value under `window._React`. That avoids React collisions while preserving the script-tag workflow existing examples use.
5757

58+
### Routing Mode Is Explicit
59+
60+
AppRun 6.0 keeps the 3.38.1 default: hash routing and native browser links unless the application explicitly enables pretty links.
61+
62+
The startup API has two modes:
63+
64+
- No call or `app.use_prettyLink(false)`: hash routing and native browser handling for ordinary links.
65+
- `app.use_prettyLink()` or `app.use_prettyLink(true)`: pretty-link path routing.
66+
67+
Registering a `#` or `#/` handler no longer switches modes. Hash applications need no configuration, but can state the default explicitly:
68+
69+
```ts
70+
import { app } from 'apprun';
71+
72+
app.use_prettyLink(false);
73+
app.on('#home', () => {
74+
// render home
75+
});
76+
```
77+
78+
The last call before `DOMContentLoaded` determines the mode. Calls after that event do not remove or replace installed listeners. Put the selection in one startup entry point, immediately after loading or importing AppRun.
79+
80+
Use argument-free or explicit `true` when an application owns `/path` navigation. Generic examples and Play previews need no call.
81+
5882
### `query()` Removed
5983

6084
`app.query()` and `component.query()` have been removed. Use `runAsync()`.

0 commit comments

Comments
 (0)