refactor: replace fmt.Errorf/errors.New with coreerr.E()
All checks were successful
Security Scan / security (push) Successful in 8s
Test / test (push) Successful in 1m14s

Co-Authored-By: Virgil <virgil@lethean.io>
This commit is contained in:
Snider 2026-03-16 21:10:49 +00:00
parent 978e153615
commit ab77e922bd
6 changed files with 81 additions and 62 deletions

View file

@ -4,6 +4,8 @@ import (
"context" "context"
"fmt" "fmt"
"time" "time"
coreerr "forge.lthn.ai/core/go-log"
) )
// Action represents a browser action that can be performed. // Action represents a browser action that can be performed.
@ -43,7 +45,7 @@ func (a NavigateAction) Execute(ctx context.Context, wv *Webview) error {
"url": a.URL, "url": a.URL,
}) })
if err != nil { if err != nil {
return fmt.Errorf("failed to navigate: %w", err) return coreerr.E("NavigateAction.Execute", "failed to navigate", err)
} }
return wv.waitForLoad(ctx) return wv.waitForLoad(ctx)
} }
@ -191,7 +193,7 @@ func (a HoverAction) Execute(ctx context.Context, wv *Webview) error {
} }
if elem.BoundingBox == nil { if elem.BoundingBox == nil {
return fmt.Errorf("element has no bounding box") return coreerr.E("HoverAction.Execute", "element has no bounding box", nil)
} }
x := elem.BoundingBox.X + elem.BoundingBox.Width/2 x := elem.BoundingBox.X + elem.BoundingBox.Width/2
@ -459,7 +461,7 @@ func (s *ActionSequence) WaitForSelector(selector string) *ActionSequence {
func (s *ActionSequence) Execute(ctx context.Context, wv *Webview) error { func (s *ActionSequence) Execute(ctx context.Context, wv *Webview) error {
for i, action := range s.actions { for i, action := range s.actions {
if err := action.Execute(ctx, wv); err != nil { if err := action.Execute(ctx, wv); err != nil {
return fmt.Errorf("action %d failed: %w", i, err) return coreerr.E("ActionSequence.Execute", fmt.Sprintf("action %d failed", i), err)
} }
} }
return nil return nil
@ -492,18 +494,18 @@ func (wv *Webview) DragAndDrop(sourceSelector, targetSelector string) error {
// Get source and target elements // Get source and target elements
source, err := wv.querySelector(ctx, sourceSelector) source, err := wv.querySelector(ctx, sourceSelector)
if err != nil { if err != nil {
return fmt.Errorf("source element not found: %w", err) return coreerr.E("Webview.DragAndDrop", "source element not found", err)
} }
if source.BoundingBox == nil { if source.BoundingBox == nil {
return fmt.Errorf("source element has no bounding box") return coreerr.E("Webview.DragAndDrop", "source element has no bounding box", nil)
} }
target, err := wv.querySelector(ctx, targetSelector) target, err := wv.querySelector(ctx, targetSelector)
if err != nil { if err != nil {
return fmt.Errorf("target element not found: %w", err) return coreerr.E("Webview.DragAndDrop", "target element not found", err)
} }
if target.BoundingBox == nil { if target.BoundingBox == nil {
return fmt.Errorf("target element has no bounding box") return coreerr.E("Webview.DragAndDrop", "target element has no bounding box", nil)
} }
// Calculate center points // Calculate center points

View file

@ -5,6 +5,8 @@ import (
"fmt" "fmt"
"strings" "strings"
"time" "time"
coreerr "forge.lthn.ai/core/go-log"
) )
// AngularHelper provides Angular-specific testing utilities. // AngularHelper provides Angular-specific testing utilities.
@ -43,7 +45,7 @@ func (ah *AngularHelper) waitForAngular(ctx context.Context) error {
return err return err
} }
if !isAngular { if !isAngular {
return fmt.Errorf("not an Angular application") return coreerr.E("AngularHelper.waitForAngular", "not an Angular application", nil)
} }
// Wait for Zone.js stability // Wait for Zone.js stability
@ -238,7 +240,7 @@ func (ah *AngularHelper) NavigateByRouter(path string) error {
_, err := ah.wv.evaluate(ctx, script) _, err := ah.wv.evaluate(ctx, script)
if err != nil { if err != nil {
return fmt.Errorf("failed to navigate: %w", err) return coreerr.E("AngularHelper.NavigateByRouter", "failed to navigate", err)
} }
// Wait for navigation to complete // Wait for navigation to complete
@ -279,13 +281,13 @@ func (ah *AngularHelper) GetRouterState() (*AngularRouterState, error) {
} }
if result == nil { if result == nil {
return nil, fmt.Errorf("could not get router state") return nil, coreerr.E("AngularHelper.GetRouterState", "could not get router state", nil)
} }
// Parse result // Parse result
resultMap, ok := result.(map[string]any) resultMap, ok := result.(map[string]any)
if !ok { if !ok {
return nil, fmt.Errorf("invalid router state format") return nil, coreerr.E("AngularHelper.GetRouterState", "invalid router state format", nil)
} }
state := &AngularRouterState{ state := &AngularRouterState{

45
cdp.go
View file

@ -3,7 +3,6 @@ package webview
import ( import (
"context" "context"
"encoding/json" "encoding/json"
"fmt"
"io" "io"
"iter" "iter"
"net/http" "net/http"
@ -12,6 +11,8 @@ import (
"sync/atomic" "sync/atomic"
"github.com/gorilla/websocket" "github.com/gorilla/websocket"
coreerr "forge.lthn.ai/core/go-log"
) )
// CDPClient handles communication with Chrome DevTools Protocol via WebSocket. // CDPClient handles communication with Chrome DevTools Protocol via WebSocket.
@ -78,18 +79,18 @@ func NewCDPClient(debugURL string) (*CDPClient, error) {
// Get available targets // Get available targets
resp, err := http.Get(debugURL + "/json") resp, err := http.Get(debugURL + "/json")
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to get targets: %w", err) return nil, coreerr.E("CDPClient.New", "failed to get targets", err)
} }
defer func() { _ = resp.Body.Close() }() defer func() { _ = resp.Body.Close() }()
body, err := io.ReadAll(resp.Body) body, err := io.ReadAll(resp.Body)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to read targets: %w", err) return nil, coreerr.E("CDPClient.New", "failed to read targets", err)
} }
var targets []TargetInfo var targets []TargetInfo
if err := json.Unmarshal(body, &targets); err != nil { if err := json.Unmarshal(body, &targets); err != nil {
return nil, fmt.Errorf("failed to parse targets: %w", err) return nil, coreerr.E("CDPClient.New", "failed to parse targets", err)
} }
// Find a page target // Find a page target
@ -105,31 +106,31 @@ func NewCDPClient(debugURL string) (*CDPClient, error) {
// Try to create a new target // Try to create a new target
resp, err := http.Get(debugURL + "/json/new") resp, err := http.Get(debugURL + "/json/new")
if err != nil { if err != nil {
return nil, fmt.Errorf("no page targets found and failed to create new: %w", err) return nil, coreerr.E("CDPClient.New", "no page targets found and failed to create new", err)
} }
defer func() { _ = resp.Body.Close() }() defer func() { _ = resp.Body.Close() }()
body, err := io.ReadAll(resp.Body) body, err := io.ReadAll(resp.Body)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to read new target: %w", err) return nil, coreerr.E("CDPClient.New", "failed to read new target", err)
} }
var newTarget TargetInfo var newTarget TargetInfo
if err := json.Unmarshal(body, &newTarget); err != nil { if err := json.Unmarshal(body, &newTarget); err != nil {
return nil, fmt.Errorf("failed to parse new target: %w", err) return nil, coreerr.E("CDPClient.New", "failed to parse new target", err)
} }
wsURL = newTarget.WebSocketDebuggerURL wsURL = newTarget.WebSocketDebuggerURL
} }
if wsURL == "" { if wsURL == "" {
return nil, fmt.Errorf("no WebSocket URL available") return nil, coreerr.E("CDPClient.New", "no WebSocket URL available", nil)
} }
// Connect to WebSocket // Connect to WebSocket
conn, _, err := websocket.DefaultDialer.Dial(wsURL, nil) conn, _, err := websocket.DefaultDialer.Dial(wsURL, nil)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to connect to WebSocket: %w", err) return nil, coreerr.E("CDPClient.New", "failed to connect to WebSocket", err)
} }
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
@ -185,7 +186,7 @@ func (c *CDPClient) Call(ctx context.Context, method string, params map[string]a
err := c.conn.WriteJSON(msg) err := c.conn.WriteJSON(msg)
c.mu.Unlock() c.mu.Unlock()
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to send message: %w", err) return nil, coreerr.E("CDPClient.Call", "failed to send message", err)
} }
// Wait for response // Wait for response
@ -194,7 +195,7 @@ func (c *CDPClient) Call(ctx context.Context, method string, params map[string]a
return nil, ctx.Err() return nil, ctx.Err()
case resp := <-respCh: case resp := <-respCh:
if resp.Error != nil { if resp.Error != nil {
return nil, fmt.Errorf("CDP error %d: %s", resp.Error.Code, resp.Error.Message) return nil, coreerr.E("CDPClient.Call", resp.Error.Message, nil)
} }
return resp.Result, nil return resp.Result, nil
} }
@ -293,28 +294,28 @@ func (c *CDPClient) NewTab(url string) (*CDPClient, error) {
resp, err := http.Get(endpoint) resp, err := http.Get(endpoint)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to create new tab: %w", err) return nil, coreerr.E("CDPClient.NewTab", "failed to create new tab", err)
} }
defer func() { _ = resp.Body.Close() }() defer func() { _ = resp.Body.Close() }()
body, err := io.ReadAll(resp.Body) body, err := io.ReadAll(resp.Body)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to read response: %w", err) return nil, coreerr.E("CDPClient.NewTab", "failed to read response", err)
} }
var target TargetInfo var target TargetInfo
if err := json.Unmarshal(body, &target); err != nil { if err := json.Unmarshal(body, &target); err != nil {
return nil, fmt.Errorf("failed to parse target: %w", err) return nil, coreerr.E("CDPClient.NewTab", "failed to parse target", err)
} }
if target.WebSocketDebuggerURL == "" { if target.WebSocketDebuggerURL == "" {
return nil, fmt.Errorf("no WebSocket URL for new tab") return nil, coreerr.E("CDPClient.NewTab", "no WebSocket URL for new tab", nil)
} }
// Connect to new tab // Connect to new tab
conn, _, err := websocket.DefaultDialer.Dial(target.WebSocketDebuggerURL, nil) conn, _, err := websocket.DefaultDialer.Dial(target.WebSocketDebuggerURL, nil)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to connect to new tab: %w", err) return nil, coreerr.E("CDPClient.NewTab", "failed to connect to new tab", err)
} }
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
@ -350,18 +351,18 @@ func (c *CDPClient) CloseTab() error {
func ListTargets(debugURL string) ([]TargetInfo, error) { func ListTargets(debugURL string) ([]TargetInfo, error) {
resp, err := http.Get(debugURL + "/json") resp, err := http.Get(debugURL + "/json")
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to get targets: %w", err) return nil, coreerr.E("ListTargets", "failed to get targets", err)
} }
defer func() { _ = resp.Body.Close() }() defer func() { _ = resp.Body.Close() }()
body, err := io.ReadAll(resp.Body) body, err := io.ReadAll(resp.Body)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to read targets: %w", err) return nil, coreerr.E("ListTargets", "failed to read targets", err)
} }
var targets []TargetInfo var targets []TargetInfo
if err := json.Unmarshal(body, &targets); err != nil { if err := json.Unmarshal(body, &targets); err != nil {
return nil, fmt.Errorf("failed to parse targets: %w", err) return nil, coreerr.E("ListTargets", "failed to parse targets", err)
} }
return targets, nil return targets, nil
@ -386,18 +387,18 @@ func ListTargetsAll(debugURL string) iter.Seq[TargetInfo] {
func GetVersion(debugURL string) (map[string]string, error) { func GetVersion(debugURL string) (map[string]string, error) {
resp, err := http.Get(debugURL + "/json/version") resp, err := http.Get(debugURL + "/json/version")
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to get version: %w", err) return nil, coreerr.E("GetVersion", "failed to get version", err)
} }
defer func() { _ = resp.Body.Close() }() defer func() { _ = resp.Body.Close() }()
body, err := io.ReadAll(resp.Body) body, err := io.ReadAll(resp.Body)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to read version: %w", err) return nil, coreerr.E("GetVersion", "failed to read version", err)
} }
var version map[string]string var version map[string]string
if err := json.Unmarshal(body, &version); err != nil { if err := json.Unmarshal(body, &version); err != nil {
return nil, fmt.Errorf("failed to parse version: %w", err) return nil, coreerr.E("GetVersion", "failed to parse version", err)
} }
return version, nil return version, nil

2
go.mod
View file

@ -3,3 +3,5 @@ module forge.lthn.ai/core/go-webview
go 1.26.0 go 1.26.0
require github.com/gorilla/websocket v1.5.3 require github.com/gorilla/websocket v1.5.3
require forge.lthn.ai/core/go-log v0.0.4

10
go.sum
View file

@ -1,2 +1,12 @@
forge.lthn.ai/core/go-log v0.0.4 h1:KTuCEPgFmuM8KJfnyQ8vPOU1Jg654W74h8IJvfQMfv0=
forge.lthn.ai/core/go-log v0.0.4/go.mod h1:r14MXKOD3LF/sI8XUJQhRk/SZHBE7jAFVuCfgkXoZPw=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View file

@ -30,6 +30,8 @@ import (
"strings" "strings"
"sync" "sync"
"time" "time"
coreerr "forge.lthn.ai/core/go-log"
) )
// Webview represents a connection to a Chrome DevTools Protocol endpoint. // Webview represents a connection to a Chrome DevTools Protocol endpoint.
@ -80,7 +82,7 @@ func WithDebugURL(url string) Option {
return func(wv *Webview) error { return func(wv *Webview) error {
client, err := NewCDPClient(url) client, err := NewCDPClient(url)
if err != nil { if err != nil {
return fmt.Errorf("failed to connect to Chrome DevTools: %w", err) return coreerr.E("Webview.WithDebugURL", "failed to connect to Chrome DevTools", err)
} }
wv.client = client wv.client = client
return nil return nil
@ -125,13 +127,13 @@ func New(opts ...Option) (*Webview, error) {
if wv.client == nil { if wv.client == nil {
cancel() cancel()
return nil, fmt.Errorf("no debug URL provided; use WithDebugURL option") return nil, coreerr.E("Webview.New", "no debug URL provided; use WithDebugURL option", nil)
} }
// Enable console capture // Enable console capture
if err := wv.enableConsole(); err != nil { if err := wv.enableConsole(); err != nil {
cancel() cancel()
return nil, fmt.Errorf("failed to enable console capture: %w", err) return nil, coreerr.E("Webview.New", "failed to enable console capture", err)
} }
return wv, nil return wv, nil
@ -155,7 +157,7 @@ func (wv *Webview) Navigate(url string) error {
"url": url, "url": url,
}) })
if err != nil { if err != nil {
return fmt.Errorf("failed to navigate: %w", err) return coreerr.E("Webview.Navigate", "failed to navigate", err)
} }
// Wait for page load // Wait for page load
@ -248,17 +250,17 @@ func (wv *Webview) Screenshot() ([]byte, error) {
"format": "png", "format": "png",
}) })
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to capture screenshot: %w", err) return nil, coreerr.E("Webview.Screenshot", "failed to capture screenshot", err)
} }
dataStr, ok := result["data"].(string) dataStr, ok := result["data"].(string)
if !ok { if !ok {
return nil, fmt.Errorf("invalid screenshot data") return nil, coreerr.E("Webview.Screenshot", "invalid screenshot data", nil)
} }
data, err := base64.StdEncoding.DecodeString(dataStr) data, err := base64.StdEncoding.DecodeString(dataStr)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to decode screenshot: %w", err) return nil, coreerr.E("Webview.Screenshot", "failed to decode screenshot", err)
} }
return data, nil return data, nil
@ -294,7 +296,7 @@ func (wv *Webview) GetURL() (string, error) {
url, ok := result.(string) url, ok := result.(string)
if !ok { if !ok {
return "", fmt.Errorf("invalid URL result") return "", coreerr.E("Webview.GetURL", "invalid URL result", nil)
} }
return url, nil return url, nil
@ -312,7 +314,7 @@ func (wv *Webview) GetTitle() (string, error) {
title, ok := result.(string) title, ok := result.(string)
if !ok { if !ok {
return "", fmt.Errorf("invalid title result") return "", coreerr.E("Webview.GetTitle", "invalid title result", nil)
} }
return title, nil return title, nil
@ -337,7 +339,7 @@ func (wv *Webview) GetHTML(selector string) (string, error) {
html, ok := result.(string) html, ok := result.(string)
if !ok { if !ok {
return "", fmt.Errorf("invalid HTML result") return "", coreerr.E("Webview.GetHTML", "invalid HTML result", nil)
} }
return html, nil return html, nil
@ -375,7 +377,7 @@ func (wv *Webview) Reload() error {
_, err := wv.client.Call(ctx, "Page.reload", nil) _, err := wv.client.Call(ctx, "Page.reload", nil)
if err != nil { if err != nil {
return fmt.Errorf("failed to reload: %w", err) return coreerr.E("Webview.Reload", "failed to reload", err)
} }
return wv.waitForLoad(ctx) return wv.waitForLoad(ctx)
@ -541,17 +543,17 @@ func (wv *Webview) evaluate(ctx context.Context, script string) (any, error) {
"returnByValue": true, "returnByValue": true,
}) })
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to evaluate script: %w", err) return nil, coreerr.E("Webview.evaluate", "failed to evaluate script", err)
} }
// Check for exception // Check for exception
if exceptionDetails, ok := result["exceptionDetails"].(map[string]any); ok { if exceptionDetails, ok := result["exceptionDetails"].(map[string]any); ok {
if exception, ok := exceptionDetails["exception"].(map[string]any); ok { if exception, ok := exceptionDetails["exception"].(map[string]any); ok {
if description, ok := exception["description"].(string); ok { if description, ok := exception["description"].(string); ok {
return nil, fmt.Errorf("JavaScript error: %s", description) return nil, coreerr.E("Webview.evaluate", description, nil)
} }
} }
return nil, fmt.Errorf("JavaScript error") return nil, coreerr.E("Webview.evaluate", "JavaScript error", nil)
} }
// Extract result value // Extract result value
@ -567,17 +569,17 @@ func (wv *Webview) querySelector(ctx context.Context, selector string) (*Element
// Get document root // Get document root
docResult, err := wv.client.Call(ctx, "DOM.getDocument", nil) docResult, err := wv.client.Call(ctx, "DOM.getDocument", nil)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to get document: %w", err) return nil, coreerr.E("Webview.querySelector", "failed to get document", err)
} }
root, ok := docResult["root"].(map[string]any) root, ok := docResult["root"].(map[string]any)
if !ok { if !ok {
return nil, fmt.Errorf("invalid document root") return nil, coreerr.E("Webview.querySelector", "invalid document root", nil)
} }
rootID, ok := root["nodeId"].(float64) rootID, ok := root["nodeId"].(float64)
if !ok { if !ok {
return nil, fmt.Errorf("invalid root node ID") return nil, coreerr.E("Webview.querySelector", "invalid root node ID", nil)
} }
// Query selector // Query selector
@ -586,12 +588,12 @@ func (wv *Webview) querySelector(ctx context.Context, selector string) (*Element
"selector": selector, "selector": selector,
}) })
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to query selector: %w", err) return nil, coreerr.E("Webview.querySelector", "failed to query selector", err)
} }
nodeID, ok := queryResult["nodeId"].(float64) nodeID, ok := queryResult["nodeId"].(float64)
if !ok || nodeID == 0 { if !ok || nodeID == 0 {
return nil, fmt.Errorf("element not found: %s", selector) return nil, coreerr.E("Webview.querySelector", "element not found: "+selector, nil)
} }
return wv.getElementInfo(ctx, int(nodeID)) return wv.getElementInfo(ctx, int(nodeID))
@ -602,17 +604,17 @@ func (wv *Webview) querySelectorAll(ctx context.Context, selector string) ([]*El
// Get document root // Get document root
docResult, err := wv.client.Call(ctx, "DOM.getDocument", nil) docResult, err := wv.client.Call(ctx, "DOM.getDocument", nil)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to get document: %w", err) return nil, coreerr.E("Webview.querySelectorAll", "failed to get document", err)
} }
root, ok := docResult["root"].(map[string]any) root, ok := docResult["root"].(map[string]any)
if !ok { if !ok {
return nil, fmt.Errorf("invalid document root") return nil, coreerr.E("Webview.querySelectorAll", "invalid document root", nil)
} }
rootID, ok := root["nodeId"].(float64) rootID, ok := root["nodeId"].(float64)
if !ok { if !ok {
return nil, fmt.Errorf("invalid root node ID") return nil, coreerr.E("Webview.querySelectorAll", "invalid root node ID", nil)
} }
// Query selector all // Query selector all
@ -621,12 +623,12 @@ func (wv *Webview) querySelectorAll(ctx context.Context, selector string) ([]*El
"selector": selector, "selector": selector,
}) })
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to query selector all: %w", err) return nil, coreerr.E("Webview.querySelectorAll", "failed to query selector all", err)
} }
nodeIDs, ok := queryResult["nodeIds"].([]any) nodeIDs, ok := queryResult["nodeIds"].([]any)
if !ok { if !ok {
return nil, fmt.Errorf("invalid node IDs") return nil, coreerr.E("Webview.querySelectorAll", "invalid node IDs", nil)
} }
elements := make([]*ElementInfo, 0, len(nodeIDs)) elements := make([]*ElementInfo, 0, len(nodeIDs))
@ -653,7 +655,7 @@ func (wv *Webview) getElementInfo(ctx context.Context, nodeID int) (*ElementInfo
node, ok := descResult["node"].(map[string]any) node, ok := descResult["node"].(map[string]any)
if !ok { if !ok {
return nil, fmt.Errorf("invalid node description") return nil, coreerr.E("Webview.getElementInfo", "invalid node description", nil)
} }
tagName, _ := node["nodeName"].(string) tagName, _ := node["nodeName"].(string)
@ -726,7 +728,7 @@ func (wv *Webview) click(ctx context.Context, selector string) error {
"clickCount": 1, "clickCount": 1,
}) })
if err != nil { if err != nil {
return fmt.Errorf("failed to dispatch %s: %w", eventType, err) return coreerr.E("Webview.click", "failed to dispatch "+eventType, err)
} }
} }
@ -739,7 +741,7 @@ func (wv *Webview) typeText(ctx context.Context, selector, text string) error {
script := fmt.Sprintf("document.querySelector(%q)?.focus()", selector) script := fmt.Sprintf("document.querySelector(%q)?.focus()", selector)
_, err := wv.evaluate(ctx, script) _, err := wv.evaluate(ctx, script)
if err != nil { if err != nil {
return fmt.Errorf("failed to focus element: %w", err) return coreerr.E("Webview.typeText", "failed to focus element", err)
} }
// Type each character // Type each character
@ -749,14 +751,14 @@ func (wv *Webview) typeText(ctx context.Context, selector, text string) error {
"text": string(char), "text": string(char),
}) })
if err != nil { if err != nil {
return fmt.Errorf("failed to dispatch keyDown: %w", err) return coreerr.E("Webview.typeText", "failed to dispatch keyDown", err)
} }
_, err = wv.client.Call(ctx, "Input.dispatchKeyEvent", map[string]any{ _, err = wv.client.Call(ctx, "Input.dispatchKeyEvent", map[string]any{
"type": "keyUp", "type": "keyUp",
}) })
if err != nil { if err != nil {
return fmt.Errorf("failed to dispatch keyUp: %w", err) return coreerr.E("Webview.typeText", "failed to dispatch keyUp", err)
} }
} }