sx-http: fix AJAX fragment extraction + proper back button test
AJAX fragment: extract #main-panel with matching close tag (depth tracking) instead of taking everything to end of file. Prevents shell closing tags from breaking the DOM swap. Back button test: verifies content actually changes — checks for "Geography" and "Rendering Pipeline" after going back, not just that body has >100 chars. Tests forward nav content change too. 7/7 navigation tests pass including back button content verification. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -2117,7 +2117,35 @@ let http_mode port =
|
|||||||
with _ -> None in
|
with _ -> None in
|
||||||
match panel_start with
|
match panel_start with
|
||||||
| Some start ->
|
| Some start ->
|
||||||
let fragment = String.sub html start (String.length html - start) in
|
(* Find the tag name (section, div, etc.) *)
|
||||||
|
let tag_end = ref (start + 1) in
|
||||||
|
while !tag_end < String.length html && html.[!tag_end] <> ' ' && html.[!tag_end] <> '>' do
|
||||||
|
tag_end := !tag_end + 1
|
||||||
|
done;
|
||||||
|
let tag = String.sub html (start + 1) (!tag_end - start - 1) in
|
||||||
|
(* Find matching close tag by counting depth *)
|
||||||
|
let open_tag = "<" ^ tag in
|
||||||
|
let close_tag = "</" ^ tag ^ ">" in
|
||||||
|
let depth = ref 1 in
|
||||||
|
let pos = ref (!tag_end) in
|
||||||
|
let found_end = ref (String.length html) in
|
||||||
|
while !depth > 0 && !pos < String.length html - String.length close_tag do
|
||||||
|
if String.sub html !pos (String.length close_tag) = close_tag then begin
|
||||||
|
depth := !depth - 1;
|
||||||
|
if !depth = 0 then
|
||||||
|
found_end := !pos + String.length close_tag
|
||||||
|
else
|
||||||
|
pos := !pos + 1
|
||||||
|
end else if !pos + String.length open_tag < String.length html
|
||||||
|
&& String.sub html !pos (String.length open_tag) = open_tag
|
||||||
|
&& (html.[!pos + String.length open_tag] = ' '
|
||||||
|
|| html.[!pos + String.length open_tag] = '>') then begin
|
||||||
|
depth := !depth + 1;
|
||||||
|
pos := !pos + 1
|
||||||
|
end else
|
||||||
|
pos := !pos + 1
|
||||||
|
done;
|
||||||
|
let fragment = String.sub html start (!found_end - start) in
|
||||||
http_response ~content_type:"text/html; charset=utf-8" fragment
|
http_response ~content_type:"text/html; charset=utf-8" fragment
|
||||||
| None -> http_response html
|
| None -> http_response html
|
||||||
end else begin
|
end else begin
|
||||||
|
|||||||
@@ -93,35 +93,30 @@ test.describe('Client-side Navigation', () => {
|
|||||||
await page.goto(BASE_URL + '/sx/(geography)', { waitUntil: 'networkidle' });
|
await page.goto(BASE_URL + '/sx/(geography)', { waitUntil: 'networkidle' });
|
||||||
await page.waitForTimeout(2000);
|
await page.waitForTimeout(2000);
|
||||||
|
|
||||||
// Note the initial URL and content
|
// Get initial content
|
||||||
const initialUrl = page.url();
|
const initialContent = await page.locator('#main-panel').textContent();
|
||||||
const initialText = await page.locator('h1, h2').first().textContent();
|
expect(initialContent).toContain('Geography');
|
||||||
|
|
||||||
// Navigate forward
|
// Navigate forward to Hypermedia
|
||||||
const cekLink = page.locator('a:has-text("CEK Machine")');
|
await page.click('a[href*="geography.(hypermedia"]:not([href*="example"])');
|
||||||
if (await cekLink.count() > 0) {
|
await page.waitForTimeout(3000);
|
||||||
await cekLink.first().click();
|
|
||||||
await page.waitForTimeout(3000);
|
|
||||||
|
|
||||||
// Should be on a different page
|
// Verify we navigated — content should be different
|
||||||
const navUrl = page.url();
|
const navContent = await page.locator('#main-panel').textContent();
|
||||||
expect(navUrl).not.toBe(initialUrl);
|
expect(navContent).toContain('Hypermedia');
|
||||||
|
expect(page.url()).toContain('hypermedia');
|
||||||
|
|
||||||
// Go back
|
// Go back
|
||||||
await page.goBack();
|
await page.goBack();
|
||||||
await page.waitForTimeout(3000);
|
await page.waitForTimeout(3000);
|
||||||
|
|
||||||
// URL should return to original
|
// Content should return to Geography — not stay on Hypermedia
|
||||||
expect(page.url()).toContain('geography');
|
const backContent = await page.locator('#main-panel').textContent();
|
||||||
|
expect(backContent).toContain('Geography');
|
||||||
// Content should show Geography page content (not blank, not broken)
|
expect(backContent).toContain('Rendering Pipeline');
|
||||||
const bodyText = await page.textContent('body');
|
// Should NOT still show Hypermedia content
|
||||||
expect(bodyText.length).toBeGreaterThan(100);
|
expect(page.url()).toContain('geography');
|
||||||
|
expect(page.url()).not.toContain('hypermedia');
|
||||||
// Should not show raw SX
|
|
||||||
const mainContent = await page.locator('#main-panel, main').first().textContent();
|
|
||||||
expect(mainContent).not.toContain('(~shared:layout');
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test('back button preserves layout (no side-by-side)', async ({ page }) => {
|
test('back button preserves layout (no side-by-side)', async ({ page }) => {
|
||||||
|
|||||||
Reference in New Issue
Block a user