fix(web): report the pathname as the screen when there is no hash route

This commit is contained in:
pj committed 2026-08-13 00:46:56 +05:30
1 parent 871a56d148
commit f839f23af9
2 files changed
+51 -1

No files matched your search

+5 -1
View File
@@ -381,7 +381,11 @@ func (d *Driver) Hierarchy(ctx context.Context) (string, error) {
defer cancel()
script := `
(function() {
const route = window.location.hash.replace(/^#/, '').split('?')[0] || '/';
// Hash first (a HashRouter names the screen there), then the pathname, which
// is where a path-routed SPA keeps it. Reporting '/' for every step of a
// BrowserRouter app made every screen look like the same screen.
const route = window.location.hash.replace(/^#/, '').split('?')[0] ||
window.location.pathname || '/';
// clickable and editable are resolved through the SAME selector sets
// pkg/spec/src/web-runtime.ts uses, so the goja host (which reads this dump)
// and the V8 host (which reads the DOM directly) cannot mean different things
+46
View File
@@ -544,3 +544,49 @@ func TestInputText_ReplacesTextInsideAShadowRoot(t *testing.T) {
t.Errorf("field holds %q after EraseText, want empty", shown)
}
}
// TestHierarchy_ScreenFallsBackToThePathname pins the route the goja host reads
// off the dump. Reading location.hash alone reported "/" on every step of a
// path-routed SPA (react-router's BrowserRouter, which the replay UI itself
// uses), so every screen looked like the same screen and no route-scoped
// property or action could tell them apart.
func TestHierarchy_ScreenFallsBackToThePathname(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
w.Header().Set("Content-Type", "text/html")
_, _ = w.Write([]byte(`<body><div id="app">app</div></body>`))
}))
defer server.Close()
for _, testCase := range []struct {
name string
path string
want string
}{
{"path-routed", "/runs/20260101-120000/steps/7", "/runs/20260101-120000/steps/7"},
{"hash wins when present", "/runs/1#/detail", "/detail"},
{"root", "/", "/"},
} {
t.Run(testCase.name, func(t *testing.T) {
d := New()
defer d.Terminate(context.Background())
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
if err := d.Launch(ctx, server.URL+testCase.path, false, nil); err != nil {
t.Fatalf("Launch: %v", err)
}
dump, err := d.Hierarchy(ctx)
if err != nil {
t.Fatalf("Hierarchy: %v", err)
}
var root struct {
Attributes map[string]string `json:"attributes"`
}
if err := json.Unmarshal([]byte(dump), &root); err != nil {
t.Fatalf("unmarshal hierarchy: %v", err)
}
if got := root.Attributes["sanderling-screen"]; got != testCase.want {
t.Errorf("sanderling-screen: got %q, want %q", got, testCase.want)
}
})
}
}