test(xtask): assert html examples avoid reloads
Wrap html_examples browser smoke interactions with a no-navigation guard so dynamic paths fail if native form fallback or full-page reload occurs. req: test/014 req: test/006
This commit is contained in:
@@ -51,7 +51,7 @@ Keep it stable. Prefer pointers to canonical sources over copied structure, file
|
||||
- Add only durable style, ownership, gotchas, and at most a few stable commands agents should actually run.
|
||||
- Prefer links or pointers to canonical sources over copied lists.
|
||||
- Avoid project trees, architecture maps, generated inventories, current file sizes, issue lists, TODO inventories, and other snapshots that will rot.
|
||||
- Stable commands: `cargo run -p hemx-xtask -- test`, `cargo run -p hemx-xtask -- html-examples-smoke`, `cargo check --workspace`, `redgate health --strict`. Use the xtask runner for full verification so jobs are capped from local CPU and memory; use the html_examples smoke for focused repo-owned browser verification of the HTML pattern gallery, not `/tmp` scripts. req: test/004 req: test/006 req: test/012 req: test/013
|
||||
- Stable commands: `cargo run -p hemx-xtask -- test`, `cargo run -p hemx-xtask -- html-examples-smoke`, `cargo check --workspace`, `redgate health --strict`. Use the xtask runner for full verification so jobs are capped from local CPU and memory; use the html_examples smoke for focused repo-owned browser verification of the HTML pattern gallery, no-reload dynamic interactions, and no `/tmp` scripts. req: test/004 req: test/006 req: test/012 req: test/013 req: test/014
|
||||
- Example behavior tests should prefer `hemx_test` generated-resource assertions over raw slot constants or raw effect/payload matching; keep browser selector helpers as test adapters only, not authoring APIs. req: test/008 req: test/009 req: test/010
|
||||
- Run the workout product exemplar with `cargo run -p hemx-xtask -- workout dev` and open `http://127.0.0.1:3028`; set `HEMX_WORKOUT_ADDR=127.0.0.1:3030` if the default port is busy. Its durable visual direction and recovery expectations live in `examples/workout/DESIGN.md`. req: examples/008
|
||||
- Use the same Workout command surface for tests, production build, and mobile release: `cargo run -p hemx-xtask -- workout test`, `cargo run -p hemx-xtask -- workout build`, `HEMX_WORKOUT_ORIGIN=https://workout.example.com cargo run -p hemx-xtask -- workout mobile-release`, and `HEMX_WORKOUT_ORIGIN=https://workout.example.com cargo run -p hemx-xtask -- workout mobile-verify`; Android/iOS SDKs, store submission targets, and signing remain external blockers, not repo-owned secrets, and do not imply a broad `hemx-mobile` framework. req: examples/006 req: examples/011 req: examples/013
|
||||
|
||||
@@ -856,6 +856,9 @@ what a valid business email is. [north_star]
|
||||
### req: test/013
|
||||
0 013 Durable browser coverage must not rely on ad hoc `/tmp` scripts. [north_star]
|
||||
|
||||
### req: test/014
|
||||
0 014 html_examples browser smoke asserts dynamic interactions complete without full-page reload or navigation. [north_star]
|
||||
|
||||
---
|
||||
|
||||
## check
|
||||
|
||||
+27
-4
@@ -103,7 +103,10 @@ fn run_html_examples_smoke() -> ExitCode {
|
||||
if let Err(code) = cdp_wait_for_html_examples() {
|
||||
return code;
|
||||
}
|
||||
if let Err(code) = cdp_assert("revealed fallback without IntersectionObserver", REVEALED_FALLBACK_SMOKE) {
|
||||
if let Err(code) = cdp_assert(
|
||||
"revealed fallback without IntersectionObserver",
|
||||
REVEALED_FALLBACK_SMOKE,
|
||||
) {
|
||||
return code;
|
||||
}
|
||||
println!("html_examples smoke ok: revealed fallback without IntersectionObserver");
|
||||
@@ -124,7 +127,10 @@ fn pick_unused_port() -> Result<u16, ExitCode> {
|
||||
fn start_html_examples_server(addr: &str) -> Result<HtmlExamplesServer, ExitCode> {
|
||||
let mut child = Command::new("cargo")
|
||||
.args(["run", "-p", "hemx-html-examples"])
|
||||
.env("HEMX_HTML_EXAMPLES_PORT", addr.rsplit(':').next().unwrap_or("3029"))
|
||||
.env(
|
||||
"HEMX_HTML_EXAMPLES_PORT",
|
||||
addr.rsplit(':').next().unwrap_or("3029"),
|
||||
)
|
||||
.stdout(Stdio::null())
|
||||
.stderr(Stdio::null())
|
||||
.spawn()
|
||||
@@ -206,7 +212,9 @@ fn cdp_wait_for_html_examples() -> Result<(), ExitCode> {
|
||||
ExitCode::FAILURE
|
||||
})?;
|
||||
if output.status.success()
|
||||
&& String::from_utf8_lossy(&output.stdout).trim().ends_with("true")
|
||||
&& String::from_utf8_lossy(&output.stdout)
|
||||
.trim()
|
||||
.ends_with("true")
|
||||
{
|
||||
return Ok(());
|
||||
}
|
||||
@@ -217,8 +225,23 @@ fn cdp_wait_for_html_examples() -> Result<(), ExitCode> {
|
||||
}
|
||||
|
||||
fn cdp_assert(name: &str, script: &str) -> Result<(), ExitCode> {
|
||||
// Each interaction must be handled by hemx without full-page navigation or
|
||||
// reload; failures here catch native form fallback sneaking into the smoke. req: test/014
|
||||
let guarded_script = format!(
|
||||
r#"(async()=>{{
|
||||
const __hemxSmokeUrl = location.href;
|
||||
const __hemxSmokeMarker = String(Date.now()) + Math.random();
|
||||
const __hemxSmokeNavCount = performance.getEntriesByType("navigation").length;
|
||||
window.__hemxSmokeNoReload = __hemxSmokeMarker;
|
||||
const __hemxSmokeResult = await ({script});
|
||||
if (location.href !== __hemxSmokeUrl) throw new Error("page navigated during smoke interaction");
|
||||
if (window.__hemxSmokeNoReload !== __hemxSmokeMarker) throw new Error("page reloaded during smoke interaction");
|
||||
if (performance.getEntriesByType("navigation").length !== __hemxSmokeNavCount) throw new Error("navigation entry changed during smoke interaction");
|
||||
return __hemxSmokeResult;
|
||||
}})()"#
|
||||
);
|
||||
let output = Command::new("cdp-browser")
|
||||
.args(["js", script])
|
||||
.args(["js", &guarded_script])
|
||||
.output()
|
||||
.map_err(|err| {
|
||||
eprintln!("failed to run browser smoke `{name}`: {err}");
|
||||
|
||||
Reference in New Issue
Block a user