diff --git a/.golangci.yml b/.golangci.yml index ead4236c..475e5722 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -2,19 +2,70 @@ run: skip-dirs-use-default: false linters: - enable: - - gofumpt - - revive - - gocyclo - - bodyclose + enable-all: true + disable: + - gochecknoinits + - paralleltest + - wrapcheck + - gosec + - gochecknoglobals + - musttag + - varnamelen + - wsl + - nonamedreturns + - tagliatelle + - nlreturn + - nakedret + - gomnd + - goerr113 + - exhaustruct + - godox + - depguard + - testpackage + - exhaustive + - containedctx + - prealloc + - perfsprint + - ireturn + - contextcheck -gocyclo: - min-complexity: 15 + # Deprecated ones: + - structcheck + - interfacer + - deadcode + - varcheck + - ifshort + - exhaustivestruct + - golint + - maligned + - nosnakecase + - scopelint + +linters-settings: + cyclop: + max-complexity: 15 + gocyclo: + min-complexity: 15 + nestif: + min-complexity: 6 + funlen: + lines: 120 issues: exclude-use-default: false exclude-rules: + - path: _test.go$ + linters: + - lll + - funlen + - dupword + - goconst + - contextcheck + - errorlint + - testableexamples + - forcetypeassert + # To support old golang version - path: lib/launcher/os_unix.go source: "// \\+build !windows" @@ -30,3 +81,44 @@ issues: - source: 'io/ioutil' linters: - staticcheck + + # Generated code + - path: lib/proto/ + linters: + - lll + - gocritic + - dupword + - forcetypeassert + - path: lib/devices/list.go + linters: + - lll + - path: lib/js/helper.go + linters: + - lll + + - path: /fixtures/ + linters: + - forbidigo + + - path: lib/examples/ + linters: + - forbidigo + - noctx + - gocritic + + - path: examples?_test.go$ + linters: + - forbidigo + - noctx + - gocritic + + - path: main.go$ + linters: + - forbidigo + - noctx + - forcetypeassert + - lll + + - path: lib/assets/ + linters: + - lll diff --git a/browser.go b/browser.go index 9af92c15..bd2045f5 100644 --- a/browser.go +++ b/browser.go @@ -23,7 +23,7 @@ import ( "github.com/ysmood/goob" ) -// Browser implements these interfaces +// Browser implements these interfaces. var ( _ proto.Client = &Browser{} _ proto.Contextable = &Browser{} @@ -81,7 +81,7 @@ func New() *Browser { }).WithPanic(utils.Panic) } -// Incognito creates a new incognito browser +// Incognito creates a new incognito browser. func (b *Browser) Incognito() (*Browser, error) { res, err := proto.TargetCreateBrowserContext{}.Call(b) if err != nil { @@ -100,31 +100,31 @@ func (b *Browser) ControlURL(url string) *Browser { return b } -// SlowMotion set the delay for each control action, such as the simulation of the human inputs +// SlowMotion set the delay for each control action, such as the simulation of the human inputs. func (b *Browser) SlowMotion(delay time.Duration) *Browser { b.slowMotion = delay return b } -// Trace enables/disables the visual tracing of the input actions on the page +// Trace enables/disables the visual tracing of the input actions on the page. func (b *Browser) Trace(enable bool) *Browser { b.trace = enable return b } -// Monitor address to listen if not empty. Shortcut for [Browser.ServeMonitor] +// Monitor address to listen if not empty. Shortcut for [Browser.ServeMonitor]. func (b *Browser) Monitor(url string) *Browser { b.monitor = url return b } -// Logger overrides the default log functions for tracing +// Logger overrides the default log functions for tracing. func (b *Browser) Logger(l utils.Logger) *Browser { b.logger = l return b } -// Client set the cdp client +// Client set the cdp client. func (b *Browser) Client(c CDPClient) *Browser { b.client = c return b @@ -138,7 +138,7 @@ func (b *Browser) DefaultDevice(d devices.Device) *Browser { return b } -// NoDefaultDevice is the same as [Browser.DefaultDevice](devices.Clear) +// NoDefaultDevice is the same as [Browser.DefaultDevice](devices.Clear). func (b *Browser) NoDefaultDevice() *Browser { return b.DefaultDevice(devices.Clear) } @@ -174,7 +174,7 @@ func (b *Browser) Connect() error { return proto.TargetSetDiscoverTargets{Discover: true}.Call(b) } -// Close the browser +// Close the browser. func (b *Browser) Close() error { if b.BrowserContextID == "" { return proto.BrowserClose{}.Call(b) @@ -213,7 +213,7 @@ func (b *Browser) Page(opts proto.TargetCreateTarget) (p *Page, err error) { return } -// Pages retrieves all visible pages +// Pages retrieves all visible pages. func (b *Browser) Pages() (Pages, error) { list, err := proto.TargetGetTargets{}.Call(b) if err != nil { @@ -247,7 +247,7 @@ func (b *Browser) Call(ctx context.Context, sessionID, methodName string, params return } -// PageFromSession is used for low-level debugging +// PageFromSession is used for low-level debugging. func (b *Browser) PageFromSession(sessionID proto.TargetSessionID) *Page { sessionCtx, cancel := context.WithCancel(b.ctx) return &Page{ @@ -358,7 +358,7 @@ func (b *Browser) eachEvent(sessionID proto.TargetSessionID, callbacks ...interf for _, cb := range callbacks { cbVal := reflect.ValueOf(cb) eType := cbVal.Type().In(0) - name := reflect.New(eType.Elem()).Interface().(proto.Event).ProtoEvent() + name := reflect.New(eType.Elem()).Interface().(proto.Event).ProtoEvent() //nolint: forcetypeassert cbMap[name] = cbVal // Only enabled domains will emit events to cdp client. @@ -366,7 +366,7 @@ func (b *Browser) eachEvent(sessionID proto.TargetSessionID, callbacks ...interf // We restore the domains to their previous states after the wait ends. domain, _ := proto.ParseMethodName(name) if req := proto.GetType(domain + ".enable"); req != nil { - enable := reflect.New(req).Interface().(proto.Request) + enable := reflect.New(req).Interface().(proto.Request) //nolint: forcetypeassert restores = append(restores, b.EnableDomain(sessionID, enable)) } } @@ -394,7 +394,7 @@ func (b *Browser) eachEvent(sessionID proto.TargetSessionID, callbacks ...interf if cbVal, has := cbMap[msg.Method]; has { e := reflect.New(proto.GetType(msg.Method)) - msg.Load(e.Interface().(proto.Event)) + msg.Load(e.Interface().(proto.Event)) //nolint: forcetypeassert args := []reflect.Value{e} if cbVal.Type().NumIn() == 2 { args = append(args, reflect.ValueOf(msg.SessionID)) @@ -410,7 +410,7 @@ func (b *Browser) eachEvent(sessionID proto.TargetSessionID, callbacks ...interf } } -// Event of the browser +// Event of the browser. func (b *Browser) Event() <-chan *Message { src := b.event.Subscribe(b.ctx) dst := make(chan *Message) @@ -427,7 +427,7 @@ func (b *Browser) Event() <-chan *Message { select { case <-b.ctx.Done(): return - case dst <- e.(*Message): + case dst <- e.(*Message): //nolint: forcetypeassert } } } @@ -476,7 +476,7 @@ func (b *Browser) IgnoreCertErrors(enable bool) error { return proto.SecuritySetIgnoreCertificateErrors{Ignore: enable}.Call(b) } -// GetCookies from the browser +// GetCookies from the browser. func (b *Browser) GetCookies() ([]*proto.NetworkCookie, error) { res, err := proto.StorageGetCookies{BrowserContextID: b.BrowserContextID}.Call(b) if err != nil { @@ -537,7 +537,7 @@ func (b *Browser) WaitDownload(dir string) func() (info *proto.PageDownloadWillB } } -// Version info of the browser +// Version info of the browser. func (b *Browser) Version() (*proto.BrowserGetVersionResult, error) { return proto.BrowserGetVersion{}.Call(b) } diff --git a/browser_test.go b/browser_test.go index 6c20f5d7..38aecfd4 100644 --- a/browser_test.go +++ b/browser_test.go @@ -50,7 +50,7 @@ func TestDefaultDevice(t *testing.T) { ua := "" s := g.Serve() - s.Mux.HandleFunc("/t", func(rw http.ResponseWriter, r *http.Request) { + s.Mux.HandleFunc("/t", func(_ http.ResponseWriter, r *http.Request) { ua = r.Header.Get("User-Agent") }) @@ -153,7 +153,7 @@ func TestBrowserWaitEvent(t *testing.T) { g.page.MustNavigate(g.blank()) wait() - wait = g.browser.EachEvent(func(e *proto.PageFrameNavigated, id proto.TargetSessionID) bool { + wait = g.browser.EachEvent(func(_ *proto.PageFrameNavigated, _ proto.TargetSessionID) bool { return true }) g.page.MustNavigate(g.blank()) @@ -201,7 +201,7 @@ func TestBlockingNavigation(t *testing.T) { s := g.Serve() pause := g.Context() - s.Mux.HandleFunc("/a", func(w http.ResponseWriter, r *http.Request) { + s.Mux.HandleFunc("/a", func(_ http.ResponseWriter, _ *http.Request) { <-pause.Done() }) s.Route("/b", ".html", `ok`) @@ -226,7 +226,7 @@ func TestResolveBlocking(t *testing.T) { pause := g.Context() - s.Mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + s.Mux.HandleFunc("/", func(_ http.ResponseWriter, _ *http.Request) { <-pause.Done() }) @@ -248,14 +248,14 @@ func TestTestTry(t *testing.T) { g.Nil(rod.Try(func() {})) err := rod.Try(func() { panic(1) }) - var errVal *rod.ErrTry + var errVal *rod.TryError g.True(errors.As(err, &errVal)) - g.Is(err, &rod.ErrTry{}) + g.Is(err, &rod.TryError{}) g.Eq(errVal.Unwrap().Error(), "1") g.Eq(1, errVal.Value) g.Has(errVal.Error(), "error value: 1\ngoroutine") - errVal = rod.Try(func() { panic(errors.New("t")) }).(*rod.ErrTry) + errVal = rod.Try(func() { panic(errors.New("t")) }).(*rod.TryError) g.Eq(errVal.Unwrap().Error(), "t") } @@ -405,7 +405,7 @@ func TestStreamReader(t *testing.T) { r := rod.NewStreamReader(g.page, "") - g.mc.stub(1, proto.IORead{}, func(send StubSend) (gson.JSON, error) { + g.mc.stub(1, proto.IORead{}, func(_ StubSend) (gson.JSON, error) { return gson.New(proto.IOReadResult{ Data: "test", }), nil @@ -418,7 +418,7 @@ func TestStreamReader(t *testing.T) { _, err := r.Read(nil) g.Err(err) - g.mc.stub(1, proto.IORead{}, func(send StubSend) (gson.JSON, error) { + g.mc.stub(1, proto.IORead{}, func(_ StubSend) (gson.JSON, error) { return gson.New(proto.IOReadResult{ Base64Encoded: true, Data: "@", diff --git a/context.go b/context.go index 39dadbe6..b7706954 100644 --- a/context.go +++ b/context.go @@ -15,45 +15,45 @@ type ( } ) -// Context returns a clone with the specified ctx for chained sub-operations +// Context returns a clone with the specified ctx for chained sub-operations. func (b *Browser) Context(ctx context.Context) *Browser { newObj := *b newObj.ctx = ctx return &newObj } -// GetContext of current instance +// GetContext of current instance. func (b *Browser) GetContext() context.Context { return b.ctx } -// Timeout returns a clone with the specified total timeout of all chained sub-operations +// Timeout returns a clone with the specified total timeout of all chained sub-operations. func (b *Browser) Timeout(d time.Duration) *Browser { ctx, cancel := context.WithTimeout(b.ctx, d) return b.Context(context.WithValue(ctx, timeoutContextKey{}, &timeoutContextVal{b.ctx, cancel})) } -// CancelTimeout cancels the current timeout context and returns a clone with the parent context +// CancelTimeout cancels the current timeout context and returns a clone with the parent context. func (b *Browser) CancelTimeout() *Browser { - val := b.ctx.Value(timeoutContextKey{}).(*timeoutContextVal) + val := b.ctx.Value(timeoutContextKey{}).(*timeoutContextVal) //nolint:forcetypeassert val.cancel() return b.Context(val.parent) } -// WithCancel returns a clone with a context cancel function +// WithCancel returns a clone with a context cancel function. func (b *Browser) WithCancel() (*Browser, func()) { ctx, cancel := context.WithCancel(b.ctx) return b.Context(ctx), cancel } -// Sleeper returns a clone with the specified sleeper for chained sub-operations +// Sleeper returns a clone with the specified sleeper for chained sub-operations. func (b *Browser) Sleeper(sleeper func() utils.Sleeper) *Browser { newObj := *b newObj.sleeper = sleeper return &newObj } -// Context returns a clone with the specified ctx for chained sub-operations +// Context returns a clone with the specified ctx for chained sub-operations. func (p *Page) Context(ctx context.Context) *Page { p.helpersLock.Lock() newObj := *p @@ -62,69 +62,69 @@ func (p *Page) Context(ctx context.Context) *Page { return &newObj } -// GetContext of current instance +// GetContext of current instance. func (p *Page) GetContext() context.Context { return p.ctx } -// Timeout returns a clone with the specified total timeout of all chained sub-operations +// Timeout returns a clone with the specified total timeout of all chained sub-operations. func (p *Page) Timeout(d time.Duration) *Page { ctx, cancel := context.WithTimeout(p.ctx, d) return p.Context(context.WithValue(ctx, timeoutContextKey{}, &timeoutContextVal{p.ctx, cancel})) } -// CancelTimeout cancels the current timeout context and returns a clone with the parent context +// CancelTimeout cancels the current timeout context and returns a clone with the parent context. func (p *Page) CancelTimeout() *Page { - val := p.ctx.Value(timeoutContextKey{}).(*timeoutContextVal) + val := p.ctx.Value(timeoutContextKey{}).(*timeoutContextVal) //nolint: forcetypeassert val.cancel() return p.Context(val.parent) } -// WithCancel returns a clone with a context cancel function +// WithCancel returns a clone with a context cancel function. func (p *Page) WithCancel() (*Page, func()) { ctx, cancel := context.WithCancel(p.ctx) return p.Context(ctx), cancel } -// Sleeper returns a clone with the specified sleeper for chained sub-operations +// Sleeper returns a clone with the specified sleeper for chained sub-operations. func (p *Page) Sleeper(sleeper func() utils.Sleeper) *Page { newObj := *p newObj.sleeper = sleeper return &newObj } -// Context returns a clone with the specified ctx for chained sub-operations +// Context returns a clone with the specified ctx for chained sub-operations. func (el *Element) Context(ctx context.Context) *Element { newObj := *el newObj.ctx = ctx return &newObj } -// GetContext of current instance +// GetContext of current instance. func (el *Element) GetContext() context.Context { return el.ctx } -// Timeout returns a clone with the specified total timeout of all chained sub-operations +// Timeout returns a clone with the specified total timeout of all chained sub-operations. func (el *Element) Timeout(d time.Duration) *Element { ctx, cancel := context.WithTimeout(el.ctx, d) return el.Context(context.WithValue(ctx, timeoutContextKey{}, &timeoutContextVal{el.ctx, cancel})) } -// CancelTimeout cancels the current timeout context and returns a clone with the parent context +// CancelTimeout cancels the current timeout context and returns a clone with the parent context. func (el *Element) CancelTimeout() *Element { - val := el.ctx.Value(timeoutContextKey{}).(*timeoutContextVal) + val := el.ctx.Value(timeoutContextKey{}).(*timeoutContextVal) //nolint: forcetypeassert val.cancel() return el.Context(val.parent) } -// WithCancel returns a clone with a context cancel function +// WithCancel returns a clone with a context cancel function. func (el *Element) WithCancel() (*Element, func()) { ctx, cancel := context.WithCancel(el.ctx) return el.Context(ctx), cancel } -// Sleeper returns a clone with the specified sleeper for chained sub-operations +// Sleeper returns a clone with the specified sleeper for chained sub-operations. func (el *Element) Sleeper(sleeper func() utils.Sleeper) *Element { newObj := *el newObj.sleeper = sleeper diff --git a/cspell.json b/cspell.json index ba806af8..210251b5 100644 --- a/cspell.json +++ b/cspell.json @@ -1,129 +1,138 @@ // cSpell Settings { - // Version of the setting file. Always 0.2 - "version": "0.2", - // language - current active spelling language - "language": "en", - "ignorePaths": [ - "**/*.{out,sketch,svg}", - "fixtures/fonts.html", - "**/tmp/**", - "lib/devices/list.go", - "lib/js/helper.go", - "lib/proto/!(a_*)", - "**/go.{mod,sum}" - ], - // words - list of words to be always considered correct - "words": [ - "APPDATA", - "Arraybuffer", - "backgrounding", - "backoff", - "Backquote", - "beforeunload", - "bodyclose", - "breakpad", - "Chromedp", - "codesearch", - "commandline", - "COMSPEC", - "containerenv", - "contenteditable", - "Contentful", - "Contextable", - "coverprofile", - "Dataview", - "datetime", - "dockerenv", - "dropzone", - "duckduckgo", - "enctype", - "errcheck", - "evenodd", - "excludesfile", - "fetchup", - "fontconfig", - "Fullscreen", - "Geolocation", - "getent", - "gobwas", - "gocyclo", - "GODEBUG", - "gofmt", - "gofumpt", - "goimports", - "golangci", - "goob", - "gopls", - "goproxy", - "gotrace", - "gson", - "headful", - "iframe", - "iframes", - "Interactable", - "ioutil", - "keychain", - "KHTML", - "ldflags", - "leakless", - "libasound", - "libcairo", - "libgbm", - "libgobject", - "libgtk", - "libnss", - "libxss", - "libxtst", - "Lmsgprefix", - "loglevel", - "MDPI", - "MITM", - "mitmproxy", - "mvdan", - "Noto", - "Numpad", - "onbeforeunload", - "onclick", - "onmouseenter", - "onmouseout", - "opencontainers", - "osversion", - "progresser", - "proto", - "proxyauth", - "Rects", - "repost", - "sattributes", - "schildren", - "Sessionable", - "Smood", - "Socketable", - "spki", - "spkis", - "srgb", - "staticcheck", - "stdlib", - "termux", - "tlid", - "touchend", - "touchstart", - "tracebackancestors", - "trimpath", - "Typedarray", - "tzdata", - "Unserializable", - "Wasmvalue", - "Weakmap", - "Weakset", - "Webassemblymemory", - "wsutil", - "xlink", - "XVFB", - "ysmood" - ], - // flagWords - list of words to be always considered incorrect - // This is useful for offensive words and common spelling errors. - // For example "hte" should be "the" - "flagWords": [] + // Version of the setting file. Always 0.2 + "version": "0.2", + // language - current active spelling language + "language": "en", + "ignorePaths": [ + "**/*.{out,sketch,svg}", + "fixtures/fonts.html", + "**/tmp/**", + "lib/devices/list.go", + "lib/js/helper.go", + "lib/proto/!(a_*)", + "**/go.{mod,sum}", + ".golangci.yml" + ], + // words - list of words to be always considered correct + "words": [ + "APPDATA", + "Arraybuffer", + "backgrounding", + "backoff", + "Backquote", + "beforeunload", + "bodyclose", + "breakpad", + "Chromedp", + "codesearch", + "commandline", + "COMSPEC", + "containerenv", + "contenteditable", + "Contentful", + "Contextable", + "contextcheck", + "coverprofile", + "Dataview", + "datetime", + "dockerenv", + "dropzone", + "duckduckgo", + "enctype", + "errcheck", + "evenodd", + "excludesfile", + "fetchup", + "fontconfig", + "forbidigo", + "forcetypeassert", + "Fullscreen", + "Geolocation", + "getent", + "gobwas", + "gocognit", + "gocyclo", + "GODEBUG", + "gofmt", + "gofumpt", + "goimports", + "golangci", + "goob", + "gopls", + "goproxy", + "gotrace", + "gson", + "headful", + "iframe", + "iframes", + "Interactable", + "ioutil", + "keychain", + "KHTML", + "ldflags", + "leakless", + "libasound", + "libcairo", + "libgbm", + "libgobject", + "libgtk", + "libnss", + "libxss", + "libxtst", + "Lmsgprefix", + "loglevel", + "MDPI", + "MITM", + "mitmproxy", + "mvdan", + "nilnil", + "noctx", + "nolint", + "Noto", + "Numpad", + "onbeforeunload", + "onclick", + "onmouseenter", + "onmouseout", + "opencontainers", + "osversion", + "progresser", + "proto", + "proxyauth", + "Rects", + "repost", + "sattributes", + "schildren", + "Sessionable", + "Smood", + "Socketable", + "spki", + "spkis", + "srgb", + "staticcheck", + "stdlib", + "termux", + "tlid", + "touchend", + "touchstart", + "tparallel", + "tracebackancestors", + "trimpath", + "Typedarray", + "tzdata", + "Unserializable", + "Wasmvalue", + "Weakmap", + "Weakset", + "Webassemblymemory", + "wsutil", + "xlink", + "XVFB", + "ysmood" + ], + // flagWords - list of words to be always considered incorrect + // This is useful for offensive words and common spelling errors. + // For example "hte" should be "the" + "flagWords": [] } diff --git a/dev_helpers.go b/dev_helpers.go index c0942627..c4527563 100644 --- a/dev_helpers.go +++ b/dev_helpers.go @@ -19,28 +19,28 @@ import ( "github.com/go-rod/rod/lib/utils" ) -// TraceType for logger +// TraceType for logger. type TraceType string -// String interface +// String interface. func (t TraceType) String() string { return fmt.Sprintf("[%s]", string(t)) } const ( - // TraceTypeWaitRequestsIdle type + // TraceTypeWaitRequestsIdle type. TraceTypeWaitRequestsIdle TraceType = "wait requests idle" - // TraceTypeWaitRequests type + // TraceTypeWaitRequests type. TraceTypeWaitRequests TraceType = "wait requests" - // TraceTypeQuery type + // TraceTypeQuery type. TraceTypeQuery TraceType = "query" - // TraceTypeWait type + // TraceTypeWait type. TraceTypeWait TraceType = "wait" - // TraceTypeInput type + // TraceTypeInput type. TraceTypeInput TraceType = "input" ) @@ -53,11 +53,11 @@ func (b *Browser) ServeMonitor(host string) string { utils.E(closeSvr()) }() - mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + mux.HandleFunc("/", func(w http.ResponseWriter, _ *http.Request) { httHTML(w, assets.Monitor) }) - mux.HandleFunc("/api/pages", func(w http.ResponseWriter, r *http.Request) { - res, err := proto.TargetGetTargets{}.Call(b) + mux.HandleFunc("/api/pages", func(w http.ResponseWriter, _ *http.Request) { + res, err := proto.TargetGetTargets{}.Call(b) //nolint: contextcheck utils.E(err) list := []*proto.TargetTargetInfo{} @@ -70,12 +70,12 @@ func (b *Browser) ServeMonitor(host string) string { w.WriteHeader(http.StatusOK) utils.E(w.Write(utils.MustToJSONBytes(list))) }) - mux.HandleFunc("/page/", func(w http.ResponseWriter, r *http.Request) { + mux.HandleFunc("/page/", func(w http.ResponseWriter, _ *http.Request) { httHTML(w, assets.MonitorPage) }) mux.HandleFunc("/api/page/", func(w http.ResponseWriter, r *http.Request) { id := r.URL.Path[strings.LastIndex(r.URL.Path, "/")+1:] - info, err := b.pageInfo(proto.TargetTargetID(id)) + info, err := b.pageInfo(proto.TargetTargetID(id)) //nolint: contextcheck utils.E(err) w.WriteHeader(http.StatusOK) utils.E(w.Write(utils.MustToJSONBytes(info))) @@ -86,13 +86,13 @@ func (b *Browser) ServeMonitor(host string) string { p := b.MustPageFromTargetID(target) w.Header().Add("Content-Type", "image/png;") - utils.E(w.Write(p.MustScreenshot())) + utils.E(w.Write(p.MustScreenshot())) //nolint: contextcheck }) return u } -// check method and sleep if needed +// check method and sleep if needed. func (b *Browser) trySlowMotion() { if b.slowMotion == 0 { return @@ -110,7 +110,7 @@ func (p *Page) ExposeHelpers(list ...*js.Function) { })) } -// Overlay a rectangle on the main frame with specified message +// Overlay a rectangle on the main frame with specified message. func (p *Page) Overlay(left, top, width, height float64, msg string) (remove func()) { id := utils.RandString(8) @@ -194,7 +194,7 @@ func (p *Page) tryTraceReq(includes, excludes []string) func(map[proto.NetworkRe return update } -// Overlay msg on the element +// Overlay msg on the element. func (el *Element) Overlay(msg string) (removeOverlay func()) { id := utils.RandString(8) diff --git a/element.go b/element.go index c78de541..97fceed0 100644 --- a/element.go +++ b/element.go @@ -8,23 +8,22 @@ import ( "strings" "time" - "github.com/ysmood/gson" - "github.com/go-rod/rod/lib/cdp" "github.com/go-rod/rod/lib/input" "github.com/go-rod/rod/lib/js" "github.com/go-rod/rod/lib/proto" "github.com/go-rod/rod/lib/utils" + "github.com/ysmood/gson" ) -// Element implements these interfaces +// Element implements these interfaces. var ( _ proto.Client = &Element{} _ proto.Contextable = &Element{} _ proto.Sessionable = &Element{} ) -// Element represents the DOM element +// Element represents the DOM element. type Element struct { Object *proto.RuntimeRemoteObject @@ -37,17 +36,17 @@ type Element struct { page *Page } -// GetSessionID interface +// GetSessionID interface. func (el *Element) GetSessionID() proto.TargetSessionID { return el.page.SessionID } -// String interface +// String interface. func (el *Element) String() string { return fmt.Sprintf("<%s>", el.Object.Description) } -// Page of the element +// Page of the element. func (el *Element) Page() *Page { return el.page } @@ -89,7 +88,7 @@ func (el *Element) Hover() error { return el.page.Context(el.ctx).Mouse.MoveTo(*pt) } -// MoveMouseOut of the current element +// MoveMouseOut of the current element. func (el *Element) MoveMouseOut() error { shape, err := el.Shape() if err != nil { @@ -143,7 +142,7 @@ func (el *Element) Tap() error { // Interactable checks if the element is interactable with cursor. // The cursor can be mouse, finger, stylus, etc. -// If not interactable err will be ErrNotInteractable, such as when covered by a modal, +// If not interactable err will be ErrNotInteractable, such as when covered by a modal,. func (el *Element) Interactable() (pt *proto.Point, err error) { noPointerEvents, err := el.Eval(`() => getComputedStyle(this).pointerEvents === 'none'`) if err != nil { @@ -151,7 +150,7 @@ func (el *Element) Interactable() (pt *proto.Point, err error) { } if noPointerEvents.Value.Bool() { - return nil, &ErrNoPointerEvents{el} + return nil, &NoPointerEventsError{el} } shape, err := el.Shape() @@ -161,7 +160,7 @@ func (el *Element) Interactable() (pt *proto.Point, err error) { pt = shape.OnePointInside() if pt == nil { - err = &ErrInvisibleShape{el} + err = &InvisibleShapeError{el} return } @@ -176,7 +175,7 @@ func (el *Element) Interactable() (pt *proto.Point, err error) { ) if err != nil { if errors.Is(err, cdp.ErrNodeNotFoundAtPos) { - err = &ErrInvisibleShape{el} + err = &InvisibleShapeError{el} } return } @@ -187,7 +186,7 @@ func (el *Element) Interactable() (pt *proto.Point, err error) { } if !isParent { - err = &ErrCovered{elAtPoint} + err = &CoveredError{elAtPoint} } return } @@ -352,12 +351,12 @@ func (el *Element) Select(selectors []string, selected bool, t SelectorType) err return err } if !res.Value.Bool() { - return &ErrElementNotFound{} + return &ElementNotFoundError{} } return nil } -// Matches checks if the element can be selected by the css selector +// Matches checks if the element can be selected by the css selector. func (el *Element) Matches(selector string) (bool, error) { res, err := el.Eval(`s => this.matches(s)`, selector) if err != nil { @@ -367,7 +366,8 @@ func (el *Element) Matches(selector string) (bool, error) { } // Attribute of the DOM object. -// Attribute vs Property: https://stackoverflow.com/questions/6003819/what-is-the-difference-between-properties-and-attributes-in-html +// Attribute vs Property: +// https://stackoverflow.com/questions/6003819/what-is-the-difference-between-properties-and-attributes-in-html func (el *Element) Attribute(name string) (*string, error) { attr, err := el.Eval("(n) => this.getAttribute(n)", name) if err != nil { @@ -375,7 +375,7 @@ func (el *Element) Attribute(name string) (*string, error) { } if attr.Value.Nil() { - return nil, nil + return nil, nil //nolint: nilnil } s := attr.Value.Str() @@ -383,7 +383,8 @@ func (el *Element) Attribute(name string) (*string, error) { } // Property of the DOM object. -// Property vs Attribute: https://stackoverflow.com/questions/6003819/what-is-the-difference-between-properties-and-attributes-in-html +// Property vs Attribute: +// https://stackoverflow.com/questions/6003819/what-is-the-difference-between-properties-and-attributes-in-html func (el *Element) Property(name string) (gson.JSON, error) { prop, err := el.Eval("(n) => this[n]", name) if err != nil { @@ -402,7 +403,7 @@ func (el *Element) Disabled() (bool, error) { return prop.Bool(), nil } -// SetFiles of the current file input element +// SetFiles of the current file input element. func (el *Element) SetFiles(paths []string) error { absPaths := utils.AbsolutePaths(paths) @@ -420,7 +421,8 @@ func (el *Element) SetFiles(paths []string) error { // Describe the current element. The depth is the maximum depth at which children should be retrieved, defaults to 1, // use -1 for the entire subtree or provide an integer larger than 0. // The pierce decides whether or not iframes and shadow roots should be traversed when returning the subtree. -// The returned [proto.DOMNode.NodeID] will always be empty, because NodeID is not stable (when [proto.DOMDocumentUpdated] +// The returned [proto.DOMNode.NodeID] will always be empty, +// because NodeID is not stable (when [proto.DOMDocumentUpdated] // is fired all NodeID on the page will be reassigned to another value) // we don't recommend using the NodeID, instead, use the [proto.DOMBackendNodeID] to identify the element. func (el *Element) Describe(depth int, pierce bool) (*proto.DOMNode, error) { @@ -431,7 +433,7 @@ func (el *Element) Describe(depth int, pierce bool) (*proto.DOMNode, error) { return val.Node, nil } -// ShadowRoot returns the shadow root of this element +// ShadowRoot returns the shadow root of this element. func (el *Element) ShadowRoot() (*Element, error) { node, err := el.Describe(1, false) if err != nil { @@ -440,7 +442,7 @@ func (el *Element) ShadowRoot() (*Element, error) { // though now it's an array, w3c changed the spec of it to be a single. if len(node.ShadowRoots) == 0 { - return nil, &ErrNoShadowRoot{el} + return nil, &NoShadowRootError{el} } id := node.ShadowRoots[0].BackendNodeID @@ -452,7 +454,7 @@ func (el *Element) ShadowRoot() (*Element, error) { return el.page.Context(el.ctx).ElementFromObject(shadowNode.Object) } -// Frame creates a page instance that represents the iframe +// Frame creates a page instance that represents the iframe. func (el *Element) Frame() (*Page, error) { node, err := el.Describe(1, false) if err != nil { @@ -477,7 +479,7 @@ func (el *Element) ContainsElement(target *Element) (bool, error) { return res.Value.Bool(), nil } -// Text that the element displays +// Text that the element displays. func (el *Element) Text() (string, error) { str, err := el.Evaluate(evalHelper(js.Text)) if err != nil { @@ -486,7 +488,7 @@ func (el *Element) Text() (string, error) { return str.Value.String(), nil } -// HTML of the element +// HTML of the element. func (el *Element) HTML() (string, error) { res, err := proto.DOMGetOuterHTML{ObjectID: el.Object.ObjectID}.Call(el) if err != nil { @@ -495,7 +497,7 @@ func (el *Element) HTML() (string, error) { return res.OuterHTML, nil } -// Visible returns true if the element is visible on the page +// Visible returns true if the element is visible on the page. func (el *Element) Visible() (bool, error) { res, err := el.Evaluate(evalHelper(js.Visible)) if err != nil { @@ -504,7 +506,7 @@ func (el *Element) Visible() (bool, error) { return res.Value.Bool(), nil } -// WaitLoad for element like +// WaitLoad for element like . func (el *Element) WaitLoad() error { defer el.tryTrace(TraceTypeWait, "load")() _, err := el.Evaluate(evalHelper(js.WaitLoad).ByPromise()) @@ -594,7 +596,7 @@ func (el *Element) WaitInteractable() (pt *proto.Point, err error) { } pt, err = el.Interactable() - if errors.Is(err, &ErrCovered{}) { + if errors.Is(err, &CoveredError{}) { return false, nil } return true, err @@ -602,12 +604,12 @@ func (el *Element) WaitInteractable() (pt *proto.Point, err error) { return } -// Wait until the js returns true +// Wait until the js returns true. func (el *Element) Wait(opts *EvalOptions) error { return el.page.Context(el.ctx).Sleeper(el.sleeper).Wait(opts.This(el.Object)) } -// WaitVisible until the element is visible +// WaitVisible until the element is visible. func (el *Element) WaitVisible() error { defer el.tryTrace(TraceTypeWait, "visible")() return el.Wait(evalHelper(js.Visible)) @@ -627,7 +629,7 @@ func (el *Element) WaitWritable() error { return el.Wait(Eval(`() => !this.readonly`)) } -// WaitInvisible until the element invisible +// WaitInvisible until the element invisible. func (el *Element) WaitInvisible() error { defer el.tryTrace(TraceTypeWait, "invisible")() return el.Wait(evalHelper(js.Invisible)) @@ -647,7 +649,7 @@ func (el *Element) CanvasToImage(format string, quality float64) ([]byte, error) return bin, nil } -// Resource returns the "src" content of current element. Such as the jpg of +// Resource returns the "src" content of current element. Such as the jpg of . func (el *Element) Resource() ([]byte, error) { src, err := el.Evaluate(evalHelper(js.Resource).ByPromise()) if err != nil { @@ -657,7 +659,7 @@ func (el *Element) Resource() ([]byte, error) { return el.page.Context(el.ctx).GetResource(src.Value.String()) } -// BackgroundImage returns the css background-image of the element +// BackgroundImage returns the css background-image of the element. func (el *Element) BackgroundImage() ([]byte, error) { res, err := el.Eval(`() => window.getComputedStyle(this).backgroundImage.replace(/^url\("/, '').replace(/"\)$/, '')`) if err != nil { @@ -669,7 +671,7 @@ func (el *Element) BackgroundImage() ([]byte, error) { return el.page.Context(el.ctx).GetResource(u) } -// Screenshot of the area of the element +// Screenshot of the area of the element. func (el *Element) Screenshot(format proto.PageCaptureScreenshotFormat, quality int) ([]byte, error) { err := el.ScrollIntoView() if err != nil { @@ -708,7 +710,7 @@ func (el *Element) Release() error { return el.page.Context(el.ctx).Release(el.Object) } -// Remove the element from the page +// Remove the element from the page. func (el *Element) Remove() error { _, err := el.Eval(`() => this.remove()`) if err != nil { @@ -717,7 +719,7 @@ func (el *Element) Remove() error { return el.Release() } -// Call implements the [proto.Client] +// Call implements the [proto.Client]. func (el *Element) Call(ctx context.Context, sessionID, methodName string, params interface{}) (res []byte, err error) { return el.page.Call(ctx, sessionID, methodName, params) } @@ -742,7 +744,7 @@ func (el *Element) id() proto.RuntimeRemoteObjectID { return el.Object.ObjectID } -// GetXPath returns the xpath of the element +// GetXPath returns the xpath of the element. func (el *Element) GetXPath(optimized bool) (string, error) { str, err := el.Evaluate(evalHelper(js.GetXPath, optimized)) if err != nil { diff --git a/element_test.go b/element_test.go index 23ce9fa5..506bbd95 100644 --- a/element_test.go +++ b/element_test.go @@ -12,14 +12,13 @@ import ( "testing" "time" - "github.com/ysmood/gson" - "github.com/go-rod/rod" "github.com/go-rod/rod/lib/cdp" "github.com/go-rod/rod/lib/devices" "github.com/go-rod/rod/lib/input" "github.com/go-rod/rod/lib/proto" "github.com/go-rod/rod/lib/utils" + "github.com/ysmood/gson" ) func TestGetElementPage(t *testing.T) { @@ -118,10 +117,10 @@ func TestNotInteractable(t *testing.T) { }`) _, err := el.Interactable() g.Has(err.Error(), "element covered by: