Align webview behavior with RFC
This commit is contained in:
parent
d6fcd6a2f4
commit
f6e235d83e
5 changed files with 57 additions and 80 deletions
10
angular.go
10
angular.go
|
|
@ -290,8 +290,8 @@ func (ah *AngularHelper) GetRouterState() (*AngularRouterState, error) {
|
|||
state.Fragment = core.Sprint(fragment)
|
||||
}
|
||||
|
||||
state.Params = stringifyMap(resultMap["params"])
|
||||
state.QueryParams = stringifyMap(resultMap["queryParams"])
|
||||
state.Params = copyStringOnlyMap(resultMap["params"])
|
||||
state.QueryParams = copyStringOnlyMap(resultMap["queryParams"])
|
||||
|
||||
return state, nil
|
||||
}
|
||||
|
|
@ -598,12 +598,14 @@ func getString(m map[string]any, key string) string {
|
|||
return ""
|
||||
}
|
||||
|
||||
func stringifyMap(value any) map[string]string {
|
||||
func copyStringOnlyMap(value any) map[string]string {
|
||||
switch typed := value.(type) {
|
||||
case map[string]any:
|
||||
result := make(map[string]string, len(typed))
|
||||
for key, item := range typed {
|
||||
result[key] = core.Sprint(item)
|
||||
if text, ok := item.(string); ok {
|
||||
result[key] = text
|
||||
}
|
||||
}
|
||||
return result
|
||||
case map[string]string:
|
||||
|
|
|
|||
|
|
@ -672,7 +672,7 @@ func TestExceptionWatcherWaitForException_Good_PreservesExistingHandlers(t *test
|
|||
}
|
||||
}
|
||||
|
||||
func TestWebviewGoBack_Good_UsesNavigationHistoryAndWaitsForLoad(t *testing.T) {
|
||||
func TestWebviewGoBack_Good_UsesGoBackOrForwardAndWaitsForLoad(t *testing.T) {
|
||||
server := newFakeCDPServer(t)
|
||||
target := server.primaryTarget()
|
||||
|
||||
|
|
@ -681,17 +681,9 @@ func TestWebviewGoBack_Good_UsesNavigationHistoryAndWaitsForLoad(t *testing.T) {
|
|||
methods = append(methods, msg.Method)
|
||||
|
||||
switch msg.Method {
|
||||
case "Page.getNavigationHistory":
|
||||
target.reply(msg.ID, map[string]any{
|
||||
"currentIndex": float64(1),
|
||||
"entries": []map[string]any{
|
||||
{"id": float64(101), "url": "https://example.com/one"},
|
||||
{"id": float64(202), "url": "https://example.com/two"},
|
||||
},
|
||||
})
|
||||
case "Page.navigateToHistoryEntry":
|
||||
if got, ok := msg.Params["entryId"].(float64); !ok || got != 101 {
|
||||
t.Fatalf("navigateToHistoryEntry entryId = %v, want 101", msg.Params["entryId"])
|
||||
case "Page.goBackOrForward":
|
||||
if got, ok := msg.Params["delta"].(float64); !ok || got != -1 {
|
||||
t.Fatalf("goBackOrForward delta = %v, want -1", msg.Params["delta"])
|
||||
}
|
||||
target.reply(msg.ID, map[string]any{})
|
||||
case "Runtime.evaluate":
|
||||
|
|
@ -717,27 +709,29 @@ func TestWebviewGoBack_Good_UsesNavigationHistoryAndWaitsForLoad(t *testing.T) {
|
|||
t.Fatalf("GoBack returned error: %v", err)
|
||||
}
|
||||
|
||||
if len(methods) != 3 {
|
||||
t.Fatalf("expected 3 CDP calls, got %d (%v)", len(methods), methods)
|
||||
if len(methods) != 2 {
|
||||
t.Fatalf("expected 2 CDP calls, got %d (%v)", len(methods), methods)
|
||||
}
|
||||
if methods[0] != "Page.getNavigationHistory" || methods[1] != "Page.navigateToHistoryEntry" || methods[2] != "Runtime.evaluate" {
|
||||
if methods[0] != "Page.goBackOrForward" || methods[1] != "Runtime.evaluate" {
|
||||
t.Fatalf("unexpected call sequence: %v", methods)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWebviewGoForward_Bad_NoHistoryEntry(t *testing.T) {
|
||||
func TestWebviewGoForward_Good_UsesGoBackOrForwardAndWaitsForLoad(t *testing.T) {
|
||||
server := newFakeCDPServer(t)
|
||||
target := server.primaryTarget()
|
||||
target.onMessage = func(target *fakeCDPTarget, msg cdpMessage) {
|
||||
if msg.Method != "Page.getNavigationHistory" {
|
||||
switch msg.Method {
|
||||
case "Page.goBackOrForward":
|
||||
if got, ok := msg.Params["delta"].(float64); !ok || got != 1 {
|
||||
t.Fatalf("goBackOrForward delta = %v, want 1", msg.Params["delta"])
|
||||
}
|
||||
target.reply(msg.ID, map[string]any{})
|
||||
case "Runtime.evaluate":
|
||||
target.replyValue(msg.ID, "complete")
|
||||
default:
|
||||
t.Fatalf("unexpected method %q", msg.Method)
|
||||
}
|
||||
target.reply(msg.ID, map[string]any{
|
||||
"currentIndex": float64(0),
|
||||
"entries": []map[string]any{
|
||||
{"id": float64(101), "url": "https://example.com/one"},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
client, err := NewCDPClient(server.DebugURL())
|
||||
|
|
@ -752,8 +746,8 @@ func TestWebviewGoForward_Bad_NoHistoryEntry(t *testing.T) {
|
|||
timeout: time.Second,
|
||||
}
|
||||
|
||||
if err := wv.GoForward(); err == nil {
|
||||
t.Fatal("GoForward succeeded without a forward history entry")
|
||||
if err := wv.GoForward(); err != nil {
|
||||
t.Fatalf("GoForward returned error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -791,7 +785,7 @@ func TestWebviewEvaluate_Bad_UsesExceptionText(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestAngularHelperGetRouterState_Good_StringifiesParams(t *testing.T) {
|
||||
func TestAngularHelperGetRouterState_Good_KeepsOnlyStringParams(t *testing.T) {
|
||||
server := newFakeCDPServer(t)
|
||||
target := server.primaryTarget()
|
||||
target.onMessage = func(target *fakeCDPTarget, msg cdpMessage) {
|
||||
|
|
@ -802,11 +796,12 @@ func TestAngularHelperGetRouterState_Good_StringifiesParams(t *testing.T) {
|
|||
"url": "/items/123",
|
||||
"fragment": "details",
|
||||
"params": map[string]any{
|
||||
"id": float64(123),
|
||||
"id": "123",
|
||||
"active": true,
|
||||
},
|
||||
"queryParams": map[string]any{
|
||||
"page": float64(2),
|
||||
"page": "2",
|
||||
"debug": float64(1),
|
||||
},
|
||||
})
|
||||
}
|
||||
|
|
@ -828,10 +823,16 @@ func TestAngularHelperGetRouterState_Good_StringifiesParams(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Fatalf("GetRouterState returned error: %v", err)
|
||||
}
|
||||
if state.Params["id"] != "123" || state.Params["active"] != "true" {
|
||||
if state.Params["id"] != "123" {
|
||||
t.Fatalf("unexpected params: %#v", state.Params)
|
||||
}
|
||||
if _, ok := state.Params["active"]; ok {
|
||||
t.Fatalf("expected non-string params to be omitted, got %#v", state.Params)
|
||||
}
|
||||
if state.QueryParams["page"] != "2" {
|
||||
t.Fatalf("unexpected query params: %#v", state.QueryParams)
|
||||
}
|
||||
if _, ok := state.QueryParams["debug"]; ok {
|
||||
t.Fatalf("expected non-string query params to be omitted, got %#v", state.QueryParams)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
21
console.go
21
console.go
|
|
@ -27,7 +27,7 @@ type ConsoleWatcher struct {
|
|||
|
||||
// ConsoleFilter filters console messages.
|
||||
type ConsoleFilter struct {
|
||||
Type string // Filter by type (log, warn, error, info, debug), empty for all
|
||||
Type string // Exact message type match, empty for all
|
||||
Pattern string // Filter by text pattern (substring match)
|
||||
}
|
||||
|
||||
|
|
@ -60,12 +60,7 @@ func NewConsoleWatcher(wv *Webview) *ConsoleWatcher {
|
|||
// normalizeConsoleType converts CDP event types to package-level values.
|
||||
func normalizeConsoleType(raw string) string {
|
||||
normalized := strings.ToLower(core.Trim(core.Sprint(raw)))
|
||||
switch normalized {
|
||||
case "warning":
|
||||
return "warn"
|
||||
default:
|
||||
return normalized
|
||||
}
|
||||
return normalized
|
||||
}
|
||||
|
||||
// consoleTextFromArgs extracts message text from Runtime.consoleAPICalled args.
|
||||
|
|
@ -279,7 +274,7 @@ func (cw *ConsoleWatcher) ErrorsAll() iter.Seq[ConsoleMessage] {
|
|||
defer cw.mu.RUnlock()
|
||||
|
||||
for _, msg := range cw.messages {
|
||||
if normalizeConsoleType(msg.Type) == "error" {
|
||||
if msg.Type == "error" {
|
||||
if !yield(msg) {
|
||||
return
|
||||
}
|
||||
|
|
@ -300,7 +295,7 @@ func (cw *ConsoleWatcher) WarningsAll() iter.Seq[ConsoleMessage] {
|
|||
defer cw.mu.RUnlock()
|
||||
|
||||
for _, msg := range cw.messages {
|
||||
if normalizeConsoleType(msg.Type) == "warn" {
|
||||
if msg.Type == "warning" {
|
||||
if !yield(msg) {
|
||||
return
|
||||
}
|
||||
|
|
@ -361,7 +356,7 @@ func (cw *ConsoleWatcher) HasErrors() bool {
|
|||
defer cw.mu.RUnlock()
|
||||
|
||||
for _, msg := range cw.messages {
|
||||
if normalizeConsoleType(msg.Type) == "error" {
|
||||
if msg.Type == "error" {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
|
@ -382,7 +377,7 @@ func (cw *ConsoleWatcher) ErrorCount() int {
|
|||
|
||||
count := 0
|
||||
for _, msg := range cw.messages {
|
||||
if normalizeConsoleType(msg.Type) == "error" {
|
||||
if msg.Type == "error" {
|
||||
count++
|
||||
}
|
||||
}
|
||||
|
|
@ -459,7 +454,7 @@ func (cw *ConsoleWatcher) matchesFilter(msg ConsoleMessage) bool {
|
|||
|
||||
// matchesSingleFilter checks if a message matches a specific filter.
|
||||
func (cw *ConsoleWatcher) matchesSingleFilter(msg ConsoleMessage, filter ConsoleFilter) bool {
|
||||
if filter.Type != "" && msg.Type != normalizeConsoleType(filter.Type) {
|
||||
if filter.Type != "" && msg.Type != filter.Type {
|
||||
return false
|
||||
}
|
||||
if filter.Pattern != "" {
|
||||
|
|
@ -684,7 +679,7 @@ func FormatConsoleOutput(messages []ConsoleMessage) string {
|
|||
switch normalizeConsoleType(msg.Type) {
|
||||
case "error":
|
||||
prefix = "[ERROR]"
|
||||
case "warn":
|
||||
case "warning", "warn":
|
||||
prefix = "[WARN]"
|
||||
case "info":
|
||||
prefix = "[INFO]"
|
||||
|
|
|
|||
38
webview.go
38
webview.go
|
|
@ -47,7 +47,7 @@ type Webview struct {
|
|||
|
||||
// ConsoleMessage represents a captured console log message.
|
||||
type ConsoleMessage struct {
|
||||
Type string `json:"type"` // log, warn, error, info, debug
|
||||
Type string `json:"type"` // log, warning, error, info, debug
|
||||
Text string `json:"text"` // Message text
|
||||
Timestamp time.Time `json:"timestamp"` // When the message was logged
|
||||
URL string `json:"url"` // Source URL
|
||||
|
|
@ -415,41 +415,11 @@ func (wv *Webview) navigateHistory(delta int, scope string) error {
|
|||
ctx, cancel := context.WithTimeout(wv.ctx, wv.timeout)
|
||||
defer cancel()
|
||||
|
||||
result, err := wv.client.Call(ctx, "Page.getNavigationHistory", nil)
|
||||
if err != nil {
|
||||
return coreerr.E(scope, "failed to get navigation history", err)
|
||||
}
|
||||
|
||||
currentIndex, ok := result["currentIndex"].(float64)
|
||||
if !ok {
|
||||
return coreerr.E(scope, "invalid navigation history index", nil)
|
||||
}
|
||||
|
||||
entries, ok := result["entries"].([]any)
|
||||
if !ok {
|
||||
return coreerr.E(scope, "invalid navigation history entries", nil)
|
||||
}
|
||||
|
||||
targetIndex := int(currentIndex) + delta
|
||||
if targetIndex < 0 || targetIndex >= len(entries) {
|
||||
return coreerr.E(scope, "no history entry available", nil)
|
||||
}
|
||||
|
||||
entry, ok := entries[targetIndex].(map[string]any)
|
||||
if !ok {
|
||||
return coreerr.E(scope, "invalid navigation history entry", nil)
|
||||
}
|
||||
|
||||
entryID, ok := entry["id"].(float64)
|
||||
if !ok {
|
||||
return coreerr.E(scope, "invalid navigation history entry ID", nil)
|
||||
}
|
||||
|
||||
_, err = wv.client.Call(ctx, "Page.navigateToHistoryEntry", map[string]any{
|
||||
"entryId": int(entryID),
|
||||
_, err := wv.client.Call(ctx, "Page.goBackOrForward", map[string]any{
|
||||
"delta": delta,
|
||||
})
|
||||
if err != nil {
|
||||
return coreerr.E(scope, "failed to navigate to history entry", err)
|
||||
return coreerr.E(scope, "failed to navigate history", err)
|
||||
}
|
||||
|
||||
return wv.waitForLoad(ctx)
|
||||
|
|
|
|||
|
|
@ -579,6 +579,15 @@ func TestConsoleWatcherFilter_Good(t *testing.T) {
|
|||
if cw.matchesFilter(msg) {
|
||||
t.Error("Expected 'test error' NOT to match pattern 'hello'")
|
||||
}
|
||||
|
||||
cw.ClearFilters()
|
||||
cw.AddFilter(ConsoleFilter{Type: "warning"})
|
||||
if !cw.matchesFilter(ConsoleMessage{Type: "warning", Text: "deprecated"}) {
|
||||
t.Error("Expected warning message to match warning filter")
|
||||
}
|
||||
if cw.matchesFilter(ConsoleMessage{Type: "warn", Text: "deprecated"}) {
|
||||
t.Error("Expected warn message not to match warning filter")
|
||||
}
|
||||
}
|
||||
|
||||
// TestConsoleWatcherCounts_Good verifies console watcher counting methods.
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue