From 05fae21a33b74e7768a94eb84294f6198c3cec56 Mon Sep 17 00:00:00 2001 From: mo khan Date: Sun, 11 May 2025 21:22:43 -0600 Subject: chore: update go modules --- .../github.com/deckarep/golang-set/v2/.gitignore | 23 + vendor/github.com/deckarep/golang-set/v2/LICENSE | 22 + vendor/github.com/deckarep/golang-set/v2/README.md | 190 + .../github.com/deckarep/golang-set/v2/iterator.go | 58 + .../deckarep/golang-set/v2/new_improved.jpeg | Bin 0 -> 120935 bytes vendor/github.com/deckarep/golang-set/v2/set.go | 255 ++ vendor/github.com/deckarep/golang-set/v2/sorted.go | 42 + .../deckarep/golang-set/v2/threadsafe.go | 299 ++ .../deckarep/golang-set/v2/threadunsafe.go | 332 ++ vendor/github.com/go-stack/stack/LICENSE.md | 21 + vendor/github.com/go-stack/stack/README.md | 38 + vendor/github.com/go-stack/stack/stack.go | 400 ++ .../github.com/magiconair/properties/properties.go | 112 +- .../playwright-go/.gitattributes | 3 + .../playwright-community/playwright-go/.gitignore | 34 + .../playwright-community/playwright-go/.gitmodules | 3 + .../playwright-go/.golangci.yaml | 6 + .../playwright-community/playwright-go/.nojekyll | 0 .../playwright-community/playwright-go/404.html | 25 + .../playwright-go/CONTRIBUTING.md | 39 + .../playwright-go/Dockerfile.example | 25 + .../playwright-community/playwright-go/LICENSE | 21 + .../playwright-community/playwright-go/README.md | 148 + .../playwright-community/playwright-go/_config.yml | 21 + .../playwright-go/apiresponse_assertions.go | 75 + .../playwright-community/playwright-go/artifact.go | 70 + .../playwright-go/assertions.go | 146 + .../playwright-go/binding_call.go | 87 + .../playwright-community/playwright-go/browser.go | 274 ++ .../playwright-go/browser_context.go | 914 ++++ .../playwright-go/browser_type.go | 181 + .../playwright-go/cdp_session.go | 38 + .../playwright-community/playwright-go/channel.go | 92 + .../playwright-go/channel_owner.go | 122 + .../playwright-community/playwright-go/clock.go | 111 + .../playwright-go/connection.go | 401 ++ .../playwright-go/console_message.go | 47 + .../playwright-community/playwright-go/dialog.go | 48 + .../playwright-community/playwright-go/download.go | 56 + .../playwright-go/element_handle.go | 403 ++ .../playwright-community/playwright-go/errors.go | 58 + .../playwright-go/event_emitter.go | 163 + .../playwright-community/playwright-go/fetch.go | 451 ++ .../playwright-go/file_chooser.go | 44 + .../playwright-community/playwright-go/frame.go | 792 ++++ .../playwright-go/frame_locator.go | 130 + .../playwright-go/generated-enums.go | 404 ++ .../playwright-go/generated-interfaces.go | 4658 ++++++++++++++++++++ .../playwright-go/generated-structs.go | 4364 ++++++++++++++++++ .../playwright-community/playwright-go/glob.go | 170 + .../playwright-go/har_router.go | 110 + .../playwright-community/playwright-go/helpers.go | 628 +++ .../playwright-community/playwright-go/input.go | 117 + .../playwright-go/input_files_helper.go | 202 + .../playwright-go/internal/safe/map.go | 90 + .../playwright-go/js_handle.go | 421 ++ .../playwright-community/playwright-go/jsonPipe.go | 64 + .../playwright-go/local_utils.go | 165 + .../playwright-community/playwright-go/locator.go | 914 ++++ .../playwright-go/locator_assertions.go | 568 +++ .../playwright-go/locator_helpers.go | 133 + .../playwright-community/playwright-go/network.go | 62 + .../playwright-go/objectFactory.go | 74 + .../playwright-community/playwright-go/page.go | 1384 ++++++ .../playwright-go/page_assertions.go | 70 + .../playwright-go/playwright.go | 64 + .../playwright-community/playwright-go/request.go | 274 ++ .../playwright-community/playwright-go/response.go | 162 + .../playwright-community/playwright-go/route.go | 270 ++ .../playwright-community/playwright-go/run.go | 409 ++ .../playwright-community/playwright-go/run_unix.go | 10 + .../playwright-community/playwright-go/run_win.go | 10 + .../playwright-go/selectors.go | 88 + .../playwright-community/playwright-go/stream.go | 68 + .../playwright-community/playwright-go/tracing.go | 164 + .../playwright-go/transport.go | 141 + .../playwright-go/type_helpers.go | 72 + .../playwright-community/playwright-go/video.go | 97 + .../playwright-community/playwright-go/waiter.go | 181 + .../playwright-go/web_error.go | 21 + .../playwright-go/websocket.go | 134 + .../playwright-go/websocket_route.go | 220 + .../playwright-community/playwright-go/worker.go | 78 + .../playwright-go/writable_stream.go | 44 + .../testcontainers/testcontainers-go/.golangci.yml | 149 +- .../testcontainers/testcontainers-go/Makefile | 2 +- .../testcontainers/testcontainers-go/Pipfile | 4 +- .../testcontainers/testcontainers-go/Pipfile.lock | 265 +- .../testcontainers-go/commons-test.mk | 2 +- .../testcontainers/testcontainers-go/container.go | 52 +- .../testcontainers/testcontainers-go/docker.go | 30 +- .../testcontainers-go/docker_client.go | 2 +- .../testcontainers-go/docker_mounts.go | 54 + .../testcontainers/testcontainers-go/generic.go | 31 + .../testcontainers-go/internal/core/bootstrap.go | 2 +- .../testcontainers-go/internal/version.go | 2 +- .../testcontainers/testcontainers-go/lifecycle.go | 30 +- .../testcontainers/testcontainers-go/mkdocs.yml | 10 +- .../testcontainers/testcontainers-go/mounts.go | 51 +- .../testcontainers/testcontainers-go/options.go | 125 +- .../testcontainers-go/port_forwarding.go | 2 +- .../testcontainers/testcontainers-go/reaper.go | 2 +- .../testcontainers/testcontainers-go/runtime.txt | 2 +- .../testcontainers/testcontainers-go/testing.go | 15 + .../testcontainers/testcontainers-go/validator.go | 7 + .../testcontainers/testcontainers-go/wait/all.go | 8 + .../testcontainers/testcontainers-go/wait/http.go | 4 +- .../testcontainers/testcontainers-go/wait/walk.go | 24 +- vendor/modules.txt | 14 +- 109 files changed, 24542 insertions(+), 272 deletions(-) create mode 100644 vendor/github.com/deckarep/golang-set/v2/.gitignore create mode 100644 vendor/github.com/deckarep/golang-set/v2/LICENSE create mode 100644 vendor/github.com/deckarep/golang-set/v2/README.md create mode 100644 vendor/github.com/deckarep/golang-set/v2/iterator.go create mode 100644 vendor/github.com/deckarep/golang-set/v2/new_improved.jpeg create mode 100644 vendor/github.com/deckarep/golang-set/v2/set.go create mode 100644 vendor/github.com/deckarep/golang-set/v2/sorted.go create mode 100644 vendor/github.com/deckarep/golang-set/v2/threadsafe.go create mode 100644 vendor/github.com/deckarep/golang-set/v2/threadunsafe.go create mode 100644 vendor/github.com/go-stack/stack/LICENSE.md create mode 100644 vendor/github.com/go-stack/stack/README.md create mode 100644 vendor/github.com/go-stack/stack/stack.go create mode 100644 vendor/github.com/playwright-community/playwright-go/.gitattributes create mode 100644 vendor/github.com/playwright-community/playwright-go/.gitignore create mode 100644 vendor/github.com/playwright-community/playwright-go/.gitmodules create mode 100644 vendor/github.com/playwright-community/playwright-go/.golangci.yaml create mode 100644 vendor/github.com/playwright-community/playwright-go/.nojekyll create mode 100644 vendor/github.com/playwright-community/playwright-go/404.html create mode 100644 vendor/github.com/playwright-community/playwright-go/CONTRIBUTING.md create mode 100644 vendor/github.com/playwright-community/playwright-go/Dockerfile.example create mode 100644 vendor/github.com/playwright-community/playwright-go/LICENSE create mode 100644 vendor/github.com/playwright-community/playwright-go/README.md create mode 100644 vendor/github.com/playwright-community/playwright-go/_config.yml create mode 100644 vendor/github.com/playwright-community/playwright-go/apiresponse_assertions.go create mode 100644 vendor/github.com/playwright-community/playwright-go/artifact.go create mode 100644 vendor/github.com/playwright-community/playwright-go/assertions.go create mode 100644 vendor/github.com/playwright-community/playwright-go/binding_call.go create mode 100644 vendor/github.com/playwright-community/playwright-go/browser.go create mode 100644 vendor/github.com/playwright-community/playwright-go/browser_context.go create mode 100644 vendor/github.com/playwright-community/playwright-go/browser_type.go create mode 100644 vendor/github.com/playwright-community/playwright-go/cdp_session.go create mode 100644 vendor/github.com/playwright-community/playwright-go/channel.go create mode 100644 vendor/github.com/playwright-community/playwright-go/channel_owner.go create mode 100644 vendor/github.com/playwright-community/playwright-go/clock.go create mode 100644 vendor/github.com/playwright-community/playwright-go/connection.go create mode 100644 vendor/github.com/playwright-community/playwright-go/console_message.go create mode 100644 vendor/github.com/playwright-community/playwright-go/dialog.go create mode 100644 vendor/github.com/playwright-community/playwright-go/download.go create mode 100644 vendor/github.com/playwright-community/playwright-go/element_handle.go create mode 100644 vendor/github.com/playwright-community/playwright-go/errors.go create mode 100644 vendor/github.com/playwright-community/playwright-go/event_emitter.go create mode 100644 vendor/github.com/playwright-community/playwright-go/fetch.go create mode 100644 vendor/github.com/playwright-community/playwright-go/file_chooser.go create mode 100644 vendor/github.com/playwright-community/playwright-go/frame.go create mode 100644 vendor/github.com/playwright-community/playwright-go/frame_locator.go create mode 100644 vendor/github.com/playwright-community/playwright-go/generated-enums.go create mode 100644 vendor/github.com/playwright-community/playwright-go/generated-interfaces.go create mode 100644 vendor/github.com/playwright-community/playwright-go/generated-structs.go create mode 100644 vendor/github.com/playwright-community/playwright-go/glob.go create mode 100644 vendor/github.com/playwright-community/playwright-go/har_router.go create mode 100644 vendor/github.com/playwright-community/playwright-go/helpers.go create mode 100644 vendor/github.com/playwright-community/playwright-go/input.go create mode 100644 vendor/github.com/playwright-community/playwright-go/input_files_helper.go create mode 100644 vendor/github.com/playwright-community/playwright-go/internal/safe/map.go create mode 100644 vendor/github.com/playwright-community/playwright-go/js_handle.go create mode 100644 vendor/github.com/playwright-community/playwright-go/jsonPipe.go create mode 100644 vendor/github.com/playwright-community/playwright-go/local_utils.go create mode 100644 vendor/github.com/playwright-community/playwright-go/locator.go create mode 100644 vendor/github.com/playwright-community/playwright-go/locator_assertions.go create mode 100644 vendor/github.com/playwright-community/playwright-go/locator_helpers.go create mode 100644 vendor/github.com/playwright-community/playwright-go/network.go create mode 100644 vendor/github.com/playwright-community/playwright-go/objectFactory.go create mode 100644 vendor/github.com/playwright-community/playwright-go/page.go create mode 100644 vendor/github.com/playwright-community/playwright-go/page_assertions.go create mode 100644 vendor/github.com/playwright-community/playwright-go/playwright.go create mode 100644 vendor/github.com/playwright-community/playwright-go/request.go create mode 100644 vendor/github.com/playwright-community/playwright-go/response.go create mode 100644 vendor/github.com/playwright-community/playwright-go/route.go create mode 100644 vendor/github.com/playwright-community/playwright-go/run.go create mode 100644 vendor/github.com/playwright-community/playwright-go/run_unix.go create mode 100644 vendor/github.com/playwright-community/playwright-go/run_win.go create mode 100644 vendor/github.com/playwright-community/playwright-go/selectors.go create mode 100644 vendor/github.com/playwright-community/playwright-go/stream.go create mode 100644 vendor/github.com/playwright-community/playwright-go/tracing.go create mode 100644 vendor/github.com/playwright-community/playwright-go/transport.go create mode 100644 vendor/github.com/playwright-community/playwright-go/type_helpers.go create mode 100644 vendor/github.com/playwright-community/playwright-go/video.go create mode 100644 vendor/github.com/playwright-community/playwright-go/waiter.go create mode 100644 vendor/github.com/playwright-community/playwright-go/web_error.go create mode 100644 vendor/github.com/playwright-community/playwright-go/websocket.go create mode 100644 vendor/github.com/playwright-community/playwright-go/websocket_route.go create mode 100644 vendor/github.com/playwright-community/playwright-go/worker.go create mode 100644 vendor/github.com/playwright-community/playwright-go/writable_stream.go create mode 100644 vendor/github.com/testcontainers/testcontainers-go/validator.go diff --git a/vendor/github.com/deckarep/golang-set/v2/.gitignore b/vendor/github.com/deckarep/golang-set/v2/.gitignore new file mode 100644 index 0000000..4eb156d --- /dev/null +++ b/vendor/github.com/deckarep/golang-set/v2/.gitignore @@ -0,0 +1,23 @@ +# Compiled Object files, Static and Dynamic libs (Shared Objects) +*.o +*.a +*.so + +# Folders +_obj +_test + +# Architecture specific extensions/prefixes +*.[568vq] +[568vq].out + +*.cgo1.go +*.cgo2.c +_cgo_defun.c +_cgo_gotypes.go +_cgo_export.* + +_testmain.go + +*.exe +.idea \ No newline at end of file diff --git a/vendor/github.com/deckarep/golang-set/v2/LICENSE b/vendor/github.com/deckarep/golang-set/v2/LICENSE new file mode 100644 index 0000000..efd4827 --- /dev/null +++ b/vendor/github.com/deckarep/golang-set/v2/LICENSE @@ -0,0 +1,22 @@ +Open Source Initiative OSI - The MIT License (MIT):Licensing + +The MIT License (MIT) +Copyright (c) 2013 - 2022 Ralph Caraveo (deckarep@gmail.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/vendor/github.com/deckarep/golang-set/v2/README.md b/vendor/github.com/deckarep/golang-set/v2/README.md new file mode 100644 index 0000000..bb691b1 --- /dev/null +++ b/vendor/github.com/deckarep/golang-set/v2/README.md @@ -0,0 +1,190 @@ +![example workflow](https://github.com/deckarep/golang-set/actions/workflows/ci.yml/badge.svg) +[![Go Report Card](https://goreportcard.com/badge/github.com/deckarep/golang-set/v2)](https://goreportcard.com/report/github.com/deckarep/golang-set/v2) +[![GoDoc](https://godoc.org/github.com/deckarep/golang-set/v2?status.svg)](http://godoc.org/github.com/deckarep/golang-set/v2) + +# golang-set + +The missing `generic` set collection for the Go language. Until Go has sets built-in...use this. + +## Psst +* Hi there, 👋! Do you use or have interest in the [Zig programming language](https://ziglang.org/) created by Andrew Kelley? If so, the golang-set project has a new sibling project: [ziglang-set](https://github.com/deckarep/ziglang-set)! Come check it out! + +## Update 12/3/2024 +* Packaged version: `2.7.0` fixes a long-standing bug with *JSON Unmarshaling*. A large refactor in the interest of performance +introduced this bug and there was no way around it but to revert the code back to how it was previously. The performance +difference was likely negligible to begin with. JSON Marshaling and Unmarshaling is now properly supported again without +needing to do workarounds. + +## Update 3/5/2023 +* Packaged version: `2.2.0` release includes a refactor to minimize pointer indirection, better method documentation standards and a few constructor convenience methods to increase ergonomics when appending items `Append` or creating a new set from an exist `Map`. +* supports `new generic` syntax +* Go `1.18.0` or higher +* Workflow tested on Go `1.20` + +![With Generics](new_improved.jpeg) + +Coming from Python one of the things I miss is the superbly wonderful set collection. This is my attempt to mimic the primary features of the set collection from Python. +You can of course argue that there is no need for a set in Go, otherwise the creators would have added one to the standard library. To those I say simply ignore this repository and carry-on and to the rest that find this useful please contribute in helping me make it better by contributing with suggestions or PRs. + +## Install + +Use `go get` to install this package. + +```shell +go get github.com/deckarep/golang-set/v2 +``` + +## Features + +* *NEW* [Generics](https://go.dev/doc/tutorial/generics) based implementation (requires [Go 1.18](https://go.dev/blog/go1.18beta1) or higher) +* One common *interface* to both implementations + * a **non threadsafe** implementation favoring *performance* + * a **threadsafe** implementation favoring *concurrent* use +* Feature complete set implementation modeled after [Python's set implementation](https://docs.python.org/3/library/stdtypes.html#set). +* Exhaustive unit-test and benchmark suite + +## Trusted by + +This package is trusted by many companies and thousands of open-source packages. Here are just a few sample users of this package. + +* Notable projects/companies using this package + * Ethereum + * Docker + * 1Password + * Hashicorp + +## Star History + +[![Star History Chart](https://api.star-history.com/svg?repos=deckarep/golang-set&type=Date)](https://star-history.com/#deckarep/golang-set&Date) + + +## Usage + +The code below demonstrates how a Set collection can better manage data and actually minimize boilerplate and needless loops in code. This package now fully supports *generic* syntax so you are now able to instantiate a collection for any [comparable](https://flaviocopes.com/golang-comparing-values/) type object. + +What is considered comparable in Go? +* `Booleans`, `integers`, `strings`, `floats` or basically primitive types. +* `Pointers` +* `Arrays` +* `Structs` if *all of their fields* are also comparable independently + +Using this library is as simple as creating either a threadsafe or non-threadsafe set and providing a `comparable` type for instantiation of the collection. + +```go +// Syntax example, doesn't compile. +mySet := mapset.NewSet[T]() // where T is some concrete comparable type. + +// Therefore this code creates an int set +mySet := mapset.NewSet[int]() + +// Or perhaps you want a string set +mySet := mapset.NewSet[string]() + +type myStruct struct { + name string + age uint8 +} + +// Alternatively a set of structs +mySet := mapset.NewSet[myStruct]() + +// Lastly a set that can hold anything using the any or empty interface keyword: interface{}. This is effectively removes type safety. +mySet := mapset.NewSet[any]() +``` + +## Comprehensive Example + +```go +package main + +import ( + "fmt" + mapset "github.com/deckarep/golang-set/v2" +) + +func main() { + // Create a string-based set of required classes. + required := mapset.NewSet[string]() + required.Add("cooking") + required.Add("english") + required.Add("math") + required.Add("biology") + + // Create a string-based set of science classes. + sciences := mapset.NewSet[string]() + sciences.Add("biology") + sciences.Add("chemistry") + + // Create a string-based set of electives. + electives := mapset.NewSet[string]() + electives.Add("welding") + electives.Add("music") + electives.Add("automotive") + + // Create a string-based set of bonus programming classes. + bonus := mapset.NewSet[string]() + bonus.Add("beginner go") + bonus.Add("python for dummies") +} +``` + +Create a set of all unique classes. +Sets will *automatically* deduplicate the same data. + +```go + all := required + .Union(sciences) + .Union(electives) + .Union(bonus) + + fmt.Println(all) +``` + +Output: +```sh +Set{cooking, english, math, chemistry, welding, biology, music, automotive, beginner go, python for dummies} +``` + +Is cooking considered a science class? +```go +result := sciences.Contains("cooking") +fmt.Println(result) +``` + +Output: +```false +false +``` + +Show me all classes that are not science classes, since I don't enjoy science. +```go +notScience := all.Difference(sciences) +fmt.Println(notScience) +``` + +```sh +Set{ music, automotive, beginner go, python for dummies, cooking, english, math, welding } +``` + +Which science classes are also required classes? +```go +reqScience := sciences.Intersect(required) +``` + +Output: +```sh +Set{biology} +``` + +How many bonus classes do you offer? +```go +fmt.Println(bonus.Cardinality()) +``` +Output: +```sh +2 +``` + +Thanks for visiting! + +-deckarep diff --git a/vendor/github.com/deckarep/golang-set/v2/iterator.go b/vendor/github.com/deckarep/golang-set/v2/iterator.go new file mode 100644 index 0000000..fc14e70 --- /dev/null +++ b/vendor/github.com/deckarep/golang-set/v2/iterator.go @@ -0,0 +1,58 @@ +/* +Open Source Initiative OSI - The MIT License (MIT):Licensing + +The MIT License (MIT) +Copyright (c) 2013 - 2022 Ralph Caraveo (deckarep@gmail.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +package mapset + +// Iterator defines an iterator over a Set, its C channel can be used to range over the Set's +// elements. +type Iterator[T comparable] struct { + C <-chan T + stop chan struct{} +} + +// Stop stops the Iterator, no further elements will be received on C, C will be closed. +func (i *Iterator[T]) Stop() { + // Allows for Stop() to be called multiple times + // (close() panics when called on already closed channel) + defer func() { + recover() + }() + + close(i.stop) + + // Exhaust any remaining elements. + for range i.C { + } +} + +// newIterator returns a new Iterator instance together with its item and stop channels. +func newIterator[T comparable]() (*Iterator[T], chan<- T, <-chan struct{}) { + itemChan := make(chan T) + stopChan := make(chan struct{}) + return &Iterator[T]{ + C: itemChan, + stop: stopChan, + }, itemChan, stopChan +} diff --git a/vendor/github.com/deckarep/golang-set/v2/new_improved.jpeg b/vendor/github.com/deckarep/golang-set/v2/new_improved.jpeg new file mode 100644 index 0000000..429752a Binary files /dev/null and b/vendor/github.com/deckarep/golang-set/v2/new_improved.jpeg differ diff --git a/vendor/github.com/deckarep/golang-set/v2/set.go b/vendor/github.com/deckarep/golang-set/v2/set.go new file mode 100644 index 0000000..292089d --- /dev/null +++ b/vendor/github.com/deckarep/golang-set/v2/set.go @@ -0,0 +1,255 @@ +/* +Open Source Initiative OSI - The MIT License (MIT):Licensing + +The MIT License (MIT) +Copyright (c) 2013 - 2022 Ralph Caraveo (deckarep@gmail.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +// Package mapset implements a simple and set collection. +// Items stored within it are unordered and unique. It supports +// typical set operations: membership testing, intersection, union, +// difference, symmetric difference and cloning. +// +// Package mapset provides two implementations of the Set +// interface. The default implementation is safe for concurrent +// access, but a non-thread-safe implementation is also provided for +// programs that can benefit from the slight speed improvement and +// that can enforce mutual exclusion through other means. +package mapset + +// Set is the primary interface provided by the mapset package. It +// represents an unordered set of data and a large number of +// operations that can be applied to that set. +type Set[T comparable] interface { + // Add adds an element to the set. Returns whether + // the item was added. + Add(val T) bool + + // Append multiple elements to the set. Returns + // the number of elements added. + Append(val ...T) int + + // Cardinality returns the number of elements in the set. + Cardinality() int + + // Clear removes all elements from the set, leaving + // the empty set. + Clear() + + // Clone returns a clone of the set using the same + // implementation, duplicating all keys. + Clone() Set[T] + + // Contains returns whether the given items + // are all in the set. + Contains(val ...T) bool + + // ContainsOne returns whether the given item + // is in the set. + // + // Contains may cause the argument to escape to the heap. + // See: https://github.com/deckarep/golang-set/issues/118 + ContainsOne(val T) bool + + // ContainsAny returns whether at least one of the + // given items are in the set. + ContainsAny(val ...T) bool + + // Difference returns the difference between this set + // and other. The returned set will contain + // all elements of this set that are not also + // elements of other. + // + // Note that the argument to Difference + // must be of the same type as the receiver + // of the method. Otherwise, Difference will + // panic. + Difference(other Set[T]) Set[T] + + // Equal determines if two sets are equal to each + // other. If they have the same cardinality + // and contain the same elements, they are + // considered equal. The order in which + // the elements were added is irrelevant. + // + // Note that the argument to Equal must be + // of the same type as the receiver of the + // method. Otherwise, Equal will panic. + Equal(other Set[T]) bool + + // Intersect returns a new set containing only the elements + // that exist only in both sets. + // + // Note that the argument to Intersect + // must be of the same type as the receiver + // of the method. Otherwise, Intersect will + // panic. + Intersect(other Set[T]) Set[T] + + // IsEmpty determines if there are elements in the set. + IsEmpty() bool + + // IsProperSubset determines if every element in this set is in + // the other set but the two sets are not equal. + // + // Note that the argument to IsProperSubset + // must be of the same type as the receiver + // of the method. Otherwise, IsProperSubset + // will panic. + IsProperSubset(other Set[T]) bool + + // IsProperSuperset determines if every element in the other set + // is in this set but the two sets are not + // equal. + // + // Note that the argument to IsSuperset + // must be of the same type as the receiver + // of the method. Otherwise, IsSuperset will + // panic. + IsProperSuperset(other Set[T]) bool + + // IsSubset determines if every element in this set is in + // the other set. + // + // Note that the argument to IsSubset + // must be of the same type as the receiver + // of the method. Otherwise, IsSubset will + // panic. + IsSubset(other Set[T]) bool + + // IsSuperset determines if every element in the other set + // is in this set. + // + // Note that the argument to IsSuperset + // must be of the same type as the receiver + // of the method. Otherwise, IsSuperset will + // panic. + IsSuperset(other Set[T]) bool + + // Each iterates over elements and executes the passed func against each element. + // If passed func returns true, stop iteration at the time. + Each(func(T) bool) + + // Iter returns a channel of elements that you can + // range over. + Iter() <-chan T + + // Iterator returns an Iterator object that you can + // use to range over the set. + Iterator() *Iterator[T] + + // Remove removes a single element from the set. + Remove(i T) + + // RemoveAll removes multiple elements from the set. + RemoveAll(i ...T) + + // String provides a convenient string representation + // of the current state of the set. + String() string + + // SymmetricDifference returns a new set with all elements which are + // in either this set or the other set but not in both. + // + // Note that the argument to SymmetricDifference + // must be of the same type as the receiver + // of the method. Otherwise, SymmetricDifference + // will panic. + SymmetricDifference(other Set[T]) Set[T] + + // Union returns a new set with all elements in both sets. + // + // Note that the argument to Union must be of the + // same type as the receiver of the method. + // Otherwise, Union will panic. + Union(other Set[T]) Set[T] + + // Pop removes and returns an arbitrary item from the set. + Pop() (T, bool) + + // ToSlice returns the members of the set as a slice. + ToSlice() []T + + // MarshalJSON will marshal the set into a JSON-based representation. + MarshalJSON() ([]byte, error) + + // UnmarshalJSON will unmarshal a JSON-based byte slice into a full Set datastructure. + // For this to work, set subtypes must implemented the Marshal/Unmarshal interface. + UnmarshalJSON(b []byte) error +} + +// NewSet creates and returns a new set with the given elements. +// Operations on the resulting set are thread-safe. +func NewSet[T comparable](vals ...T) Set[T] { + s := newThreadSafeSetWithSize[T](len(vals)) + for _, item := range vals { + s.Add(item) + } + return s +} + +// NewSetWithSize creates and returns a reference to an empty set with a specified +// capacity. Operations on the resulting set are thread-safe. +func NewSetWithSize[T comparable](cardinality int) Set[T] { + s := newThreadSafeSetWithSize[T](cardinality) + return s +} + +// NewThreadUnsafeSet creates and returns a new set with the given elements. +// Operations on the resulting set are not thread-safe. +func NewThreadUnsafeSet[T comparable](vals ...T) Set[T] { + s := newThreadUnsafeSetWithSize[T](len(vals)) + for _, item := range vals { + s.Add(item) + } + return s +} + +// NewThreadUnsafeSetWithSize creates and returns a reference to an empty set with +// a specified capacity. Operations on the resulting set are not thread-safe. +func NewThreadUnsafeSetWithSize[T comparable](cardinality int) Set[T] { + s := newThreadUnsafeSetWithSize[T](cardinality) + return s +} + +// NewSetFromMapKeys creates and returns a new set with the given keys of the map. +// Operations on the resulting set are thread-safe. +func NewSetFromMapKeys[T comparable, V any](val map[T]V) Set[T] { + s := NewSetWithSize[T](len(val)) + + for k := range val { + s.Add(k) + } + + return s +} + +// NewThreadUnsafeSetFromMapKeys creates and returns a new set with the given keys of the map. +// Operations on the resulting set are not thread-safe. +func NewThreadUnsafeSetFromMapKeys[T comparable, V any](val map[T]V) Set[T] { + s := NewThreadUnsafeSetWithSize[T](len(val)) + + for k := range val { + s.Add(k) + } + + return s +} diff --git a/vendor/github.com/deckarep/golang-set/v2/sorted.go b/vendor/github.com/deckarep/golang-set/v2/sorted.go new file mode 100644 index 0000000..8ee2e70 --- /dev/null +++ b/vendor/github.com/deckarep/golang-set/v2/sorted.go @@ -0,0 +1,42 @@ +//go:build go1.21 +// +build go1.21 + +/* +Open Source Initiative OSI - The MIT License (MIT):Licensing + +The MIT License (MIT) +Copyright (c) 2013 - 2023 Ralph Caraveo (deckarep@gmail.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +package mapset + +import ( + "cmp" + "slices" +) + +// Sorted returns a sorted slice of a set of any ordered type in ascending order. +// When sorting floating-point numbers, NaNs are ordered before other values. +func Sorted[E cmp.Ordered](set Set[E]) []E { + s := set.ToSlice() + slices.Sort(s) + return s +} diff --git a/vendor/github.com/deckarep/golang-set/v2/threadsafe.go b/vendor/github.com/deckarep/golang-set/v2/threadsafe.go new file mode 100644 index 0000000..93f20c8 --- /dev/null +++ b/vendor/github.com/deckarep/golang-set/v2/threadsafe.go @@ -0,0 +1,299 @@ +/* +Open Source Initiative OSI - The MIT License (MIT):Licensing + +The MIT License (MIT) +Copyright (c) 2013 - 2022 Ralph Caraveo (deckarep@gmail.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +package mapset + +import "sync" + +type threadSafeSet[T comparable] struct { + sync.RWMutex + uss *threadUnsafeSet[T] +} + +func newThreadSafeSet[T comparable]() *threadSafeSet[T] { + return &threadSafeSet[T]{ + uss: newThreadUnsafeSet[T](), + } +} + +func newThreadSafeSetWithSize[T comparable](cardinality int) *threadSafeSet[T] { + return &threadSafeSet[T]{ + uss: newThreadUnsafeSetWithSize[T](cardinality), + } +} + +func (t *threadSafeSet[T]) Add(v T) bool { + t.Lock() + ret := t.uss.Add(v) + t.Unlock() + return ret +} + +func (t *threadSafeSet[T]) Append(v ...T) int { + t.Lock() + ret := t.uss.Append(v...) + t.Unlock() + return ret +} + +func (t *threadSafeSet[T]) Contains(v ...T) bool { + t.RLock() + ret := t.uss.Contains(v...) + t.RUnlock() + + return ret +} + +func (t *threadSafeSet[T]) ContainsOne(v T) bool { + t.RLock() + ret := t.uss.ContainsOne(v) + t.RUnlock() + + return ret +} + +func (t *threadSafeSet[T]) ContainsAny(v ...T) bool { + t.RLock() + ret := t.uss.ContainsAny(v...) + t.RUnlock() + + return ret +} + +func (t *threadSafeSet[T]) IsEmpty() bool { + return t.Cardinality() == 0 +} + +func (t *threadSafeSet[T]) IsSubset(other Set[T]) bool { + o := other.(*threadSafeSet[T]) + + t.RLock() + o.RLock() + + ret := t.uss.IsSubset(o.uss) + t.RUnlock() + o.RUnlock() + return ret +} + +func (t *threadSafeSet[T]) IsProperSubset(other Set[T]) bool { + o := other.(*threadSafeSet[T]) + + t.RLock() + defer t.RUnlock() + o.RLock() + defer o.RUnlock() + + return t.uss.IsProperSubset(o.uss) +} + +func (t *threadSafeSet[T]) IsSuperset(other Set[T]) bool { + return other.IsSubset(t) +} + +func (t *threadSafeSet[T]) IsProperSuperset(other Set[T]) bool { + return other.IsProperSubset(t) +} + +func (t *threadSafeSet[T]) Union(other Set[T]) Set[T] { + o := other.(*threadSafeSet[T]) + + t.RLock() + o.RLock() + + unsafeUnion := t.uss.Union(o.uss).(*threadUnsafeSet[T]) + ret := &threadSafeSet[T]{uss: unsafeUnion} + t.RUnlock() + o.RUnlock() + return ret +} + +func (t *threadSafeSet[T]) Intersect(other Set[T]) Set[T] { + o := other.(*threadSafeSet[T]) + + t.RLock() + o.RLock() + + unsafeIntersection := t.uss.Intersect(o.uss).(*threadUnsafeSet[T]) + ret := &threadSafeSet[T]{uss: unsafeIntersection} + t.RUnlock() + o.RUnlock() + return ret +} + +func (t *threadSafeSet[T]) Difference(other Set[T]) Set[T] { + o := other.(*threadSafeSet[T]) + + t.RLock() + o.RLock() + + unsafeDifference := t.uss.Difference(o.uss).(*threadUnsafeSet[T]) + ret := &threadSafeSet[T]{uss: unsafeDifference} + t.RUnlock() + o.RUnlock() + return ret +} + +func (t *threadSafeSet[T]) SymmetricDifference(other Set[T]) Set[T] { + o := other.(*threadSafeSet[T]) + + t.RLock() + o.RLock() + + unsafeDifference := t.uss.SymmetricDifference(o.uss).(*threadUnsafeSet[T]) + ret := &threadSafeSet[T]{uss: unsafeDifference} + t.RUnlock() + o.RUnlock() + return ret +} + +func (t *threadSafeSet[T]) Clear() { + t.Lock() + t.uss.Clear() + t.Unlock() +} + +func (t *threadSafeSet[T]) Remove(v T) { + t.Lock() + delete(*t.uss, v) + t.Unlock() +} + +func (t *threadSafeSet[T]) RemoveAll(i ...T) { + t.Lock() + t.uss.RemoveAll(i...) + t.Unlock() +} + +func (t *threadSafeSet[T]) Cardinality() int { + t.RLock() + defer t.RUnlock() + return len(*t.uss) +} + +func (t *threadSafeSet[T]) Each(cb func(T) bool) { + t.RLock() + for elem := range *t.uss { + if cb(elem) { + break + } + } + t.RUnlock() +} + +func (t *threadSafeSet[T]) Iter() <-chan T { + ch := make(chan T) + go func() { + t.RLock() + + for elem := range *t.uss { + ch <- elem + } + close(ch) + t.RUnlock() + }() + + return ch +} + +func (t *threadSafeSet[T]) Iterator() *Iterator[T] { + iterator, ch, stopCh := newIterator[T]() + + go func() { + t.RLock() + L: + for elem := range *t.uss { + select { + case <-stopCh: + break L + case ch <- elem: + } + } + close(ch) + t.RUnlock() + }() + + return iterator +} + +func (t *threadSafeSet[T]) Equal(other Set[T]) bool { + o := other.(*threadSafeSet[T]) + + t.RLock() + o.RLock() + + ret := t.uss.Equal(o.uss) + t.RUnlock() + o.RUnlock() + return ret +} + +func (t *threadSafeSet[T]) Clone() Set[T] { + t.RLock() + + unsafeClone := t.uss.Clone().(*threadUnsafeSet[T]) + ret := &threadSafeSet[T]{uss: unsafeClone} + t.RUnlock() + return ret +} + +func (t *threadSafeSet[T]) String() string { + t.RLock() + ret := t.uss.String() + t.RUnlock() + return ret +} + +func (t *threadSafeSet[T]) Pop() (T, bool) { + t.Lock() + defer t.Unlock() + return t.uss.Pop() +} + +func (t *threadSafeSet[T]) ToSlice() []T { + keys := make([]T, 0, t.Cardinality()) + t.RLock() + for elem := range *t.uss { + keys = append(keys, elem) + } + t.RUnlock() + return keys +} + +func (t *threadSafeSet[T]) MarshalJSON() ([]byte, error) { + t.RLock() + b, err := t.uss.MarshalJSON() + t.RUnlock() + + return b, err +} + +func (t *threadSafeSet[T]) UnmarshalJSON(p []byte) error { + t.RLock() + err := t.uss.UnmarshalJSON(p) + t.RUnlock() + + return err +} diff --git a/vendor/github.com/deckarep/golang-set/v2/threadunsafe.go b/vendor/github.com/deckarep/golang-set/v2/threadunsafe.go new file mode 100644 index 0000000..7e3243b --- /dev/null +++ b/vendor/github.com/deckarep/golang-set/v2/threadunsafe.go @@ -0,0 +1,332 @@ +/* +Open Source Initiative OSI - The MIT License (MIT):Licensing + +The MIT License (MIT) +Copyright (c) 2013 - 2022 Ralph Caraveo (deckarep@gmail.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +package mapset + +import ( + "encoding/json" + "fmt" + "strings" +) + +type threadUnsafeSet[T comparable] map[T]struct{} + +// Assert concrete type:threadUnsafeSet adheres to Set interface. +var _ Set[string] = (*threadUnsafeSet[string])(nil) + +func newThreadUnsafeSet[T comparable]() *threadUnsafeSet[T] { + t := make(threadUnsafeSet[T]) + return &t +} + +func newThreadUnsafeSetWithSize[T comparable](cardinality int) *threadUnsafeSet[T] { + t := make(threadUnsafeSet[T], cardinality) + return &t +} + +func (s threadUnsafeSet[T]) Add(v T) bool { + prevLen := len(s) + s[v] = struct{}{} + return prevLen != len(s) +} + +func (s *threadUnsafeSet[T]) Append(v ...T) int { + prevLen := len(*s) + for _, val := range v { + (*s)[val] = struct{}{} + } + return len(*s) - prevLen +} + +// private version of Add which doesn't return a value +func (s *threadUnsafeSet[T]) add(v T) { + (*s)[v] = struct{}{} +} + +func (s *threadUnsafeSet[T]) Cardinality() int { + return len(*s) +} + +func (s *threadUnsafeSet[T]) Clear() { + // Constructions like this are optimised by compiler, and replaced by + // mapclear() function, defined in + // https://github.com/golang/go/blob/29bbca5c2c1ad41b2a9747890d183b6dd3a4ace4/src/runtime/map.go#L993) + for key := range *s { + delete(*s, key) + } +} + +func (s *threadUnsafeSet[T]) Clone() Set[T] { + clonedSet := newThreadUnsafeSetWithSize[T](s.Cardinality()) + for elem := range *s { + clonedSet.add(elem) + } + return clonedSet +} + +func (s *threadUnsafeSet[T]) Contains(v ...T) bool { + for _, val := range v { + if _, ok := (*s)[val]; !ok { + return false + } + } + return true +} + +func (s *threadUnsafeSet[T]) ContainsOne(v T) bool { + _, ok := (*s)[v] + return ok +} + +func (s *threadUnsafeSet[T]) ContainsAny(v ...T) bool { + for _, val := range v { + if _, ok := (*s)[val]; ok { + return true + } + } + return false +} + +// private version of Contains for a single element v +func (s *threadUnsafeSet[T]) contains(v T) (ok bool) { + _, ok = (*s)[v] + return ok +} + +func (s *threadUnsafeSet[T]) Difference(other Set[T]) Set[T] { + o := other.(*threadUnsafeSet[T]) + + diff := newThreadUnsafeSet[T]() + for elem := range *s { + if !o.contains(elem) { + diff.add(elem) + } + } + return diff +} + +func (s *threadUnsafeSet[T]) Each(cb func(T) bool) { + for elem := range *s { + if cb(elem) { + break + } + } +} + +func (s *threadUnsafeSet[T]) Equal(other Set[T]) bool { + o := other.(*threadUnsafeSet[T]) + + if s.Cardinality() != other.Cardinality() { + return false + } + for elem := range *s { + if !o.contains(elem) { + return false + } + } + return true +} + +func (s *threadUnsafeSet[T]) Intersect(other Set[T]) Set[T] { + o := other.(*threadUnsafeSet[T]) + + intersection := newThreadUnsafeSet[T]() + // loop over smaller set + if s.Cardinality() < other.Cardinality() { + for elem := range *s { + if o.contains(elem) { + intersection.add(elem) + } + } + } else { + for elem := range *o { + if s.contains(elem) { + intersection.add(elem) + } + } + } + return intersection +} + +func (s *threadUnsafeSet[T]) IsEmpty() bool { + return s.Cardinality() == 0 +} + +func (s *threadUnsafeSet[T]) IsProperSubset(other Set[T]) bool { + return s.Cardinality() < other.Cardinality() && s.IsSubset(other) +} + +func (s *threadUnsafeSet[T]) IsProperSuperset(other Set[T]) bool { + return s.Cardinality() > other.Cardinality() && s.IsSuperset(other) +} + +func (s *threadUnsafeSet[T]) IsSubset(other Set[T]) bool { + o := other.(*threadUnsafeSet[T]) + if s.Cardinality() > other.Cardinality() { + return false + } + for elem := range *s { + if !o.contains(elem) { + return false + } + } + return true +} + +func (s *threadUnsafeSet[T]) IsSuperset(other Set[T]) bool { + return other.IsSubset(s) +} + +func (s *threadUnsafeSet[T]) Iter() <-chan T { + ch := make(chan T) + go func() { + for elem := range *s { + ch <- elem + } + close(ch) + }() + + return ch +} + +func (s *threadUnsafeSet[T]) Iterator() *Iterator[T] { + iterator, ch, stopCh := newIterator[T]() + + go func() { + L: + for elem := range *s { + select { + case <-stopCh: + break L + case ch <- elem: + } + } + close(ch) + }() + + return iterator +} + +// Pop returns a popped item in case set is not empty, or nil-value of T +// if set is already empty +func (s *threadUnsafeSet[T]) Pop() (v T, ok bool) { + for item := range *s { + delete(*s, item) + return item, true + } + return v, false +} + +func (s threadUnsafeSet[T]) Remove(v T) { + delete(s, v) +} + +func (s threadUnsafeSet[T]) RemoveAll(i ...T) { + for _, elem := range i { + delete(s, elem) + } +} + +func (s threadUnsafeSet[T]) String() string { + items := make([]string, 0, len(s)) + + for elem := range s { + items = append(items, fmt.Sprintf("%v", elem)) + } + return fmt.Sprintf("Set{%s}", strings.Join(items, ", ")) +} + +func (s *threadUnsafeSet[T]) SymmetricDifference(other Set[T]) Set[T] { + o := other.(*threadUnsafeSet[T]) + + sd := newThreadUnsafeSet[T]() + for elem := range *s { + if !o.contains(elem) { + sd.add(elem) + } + } + for elem := range *o { + if !s.contains(elem) { + sd.add(elem) + } + } + return sd +} + +func (s threadUnsafeSet[T]) ToSlice() []T { + keys := make([]T, 0, s.Cardinality()) + for elem := range s { + keys = append(keys, elem) + } + + return keys +} + +func (s threadUnsafeSet[T]) Union(other Set[T]) Set[T] { + o := other.(*threadUnsafeSet[T]) + + n := s.Cardinality() + if o.Cardinality() > n { + n = o.Cardinality() + } + unionedSet := make(threadUnsafeSet[T], n) + + for elem := range s { + unionedSet.add(elem) + } + for elem := range *o { + unionedSet.add(elem) + } + return &unionedSet +} + +// MarshalJSON creates a JSON array from the set, it marshals all elements +func (s threadUnsafeSet[T]) MarshalJSON() ([]byte, error) { + items := make([]string, 0, s.Cardinality()) + + for elem := range s { + b, err := json.Marshal(elem) + if err != nil { + return nil, err + } + + items = append(items, string(b)) + } + + return []byte(fmt.Sprintf("[%s]", strings.Join(items, ","))), nil +} + +// UnmarshalJSON recreates a set from a JSON array, it only decodes +// primitive types. Numbers are decoded as json.Number. +func (s *threadUnsafeSet[T]) UnmarshalJSON(b []byte) error { + var i []T + err := json.Unmarshal(b, &i) + if err != nil { + return err + } + s.Append(i...) + + return nil +} diff --git a/vendor/github.com/go-stack/stack/LICENSE.md b/vendor/github.com/go-stack/stack/LICENSE.md new file mode 100644 index 0000000..2abf98e --- /dev/null +++ b/vendor/github.com/go-stack/stack/LICENSE.md @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2014 Chris Hines + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/github.com/go-stack/stack/README.md b/vendor/github.com/go-stack/stack/README.md new file mode 100644 index 0000000..f11cccc --- /dev/null +++ b/vendor/github.com/go-stack/stack/README.md @@ -0,0 +1,38 @@ +[![GoDoc](https://godoc.org/github.com/go-stack/stack?status.svg)](https://godoc.org/github.com/go-stack/stack) +[![Go Report Card](https://goreportcard.com/badge/go-stack/stack)](https://goreportcard.com/report/go-stack/stack) +[![TravisCI](https://travis-ci.org/go-stack/stack.svg?branch=master)](https://travis-ci.org/go-stack/stack) +[![Coverage Status](https://coveralls.io/repos/github/go-stack/stack/badge.svg?branch=master)](https://coveralls.io/github/go-stack/stack?branch=master) + +# stack + +Package stack implements utilities to capture, manipulate, and format call +stacks. It provides a simpler API than package runtime. + +The implementation takes care of the minutia and special cases of interpreting +the program counter (pc) values returned by runtime.Callers. + +## Versioning + +Package stack publishes releases via [semver](http://semver.org/) compatible Git +tags prefixed with a single 'v'. The master branch always contains the latest +release. The develop branch contains unreleased commits. + +## Formatting + +Package stack's types implement fmt.Formatter, which provides a simple and +flexible way to declaratively configure formatting when used with logging or +error tracking packages. + +```go +func DoTheThing() { + c := stack.Caller(0) + log.Print(c) // "source.go:10" + log.Printf("%+v", c) // "pkg/path/source.go:10" + log.Printf("%n", c) // "DoTheThing" + + s := stack.Trace().TrimRuntime() + log.Print(s) // "[source.go:15 caller.go:42 main.go:14]" +} +``` + +See the docs for all of the supported formatting options. diff --git a/vendor/github.com/go-stack/stack/stack.go b/vendor/github.com/go-stack/stack/stack.go new file mode 100644 index 0000000..ac3b93b --- /dev/null +++ b/vendor/github.com/go-stack/stack/stack.go @@ -0,0 +1,400 @@ +// +build go1.7 + +// Package stack implements utilities to capture, manipulate, and format call +// stacks. It provides a simpler API than package runtime. +// +// The implementation takes care of the minutia and special cases of +// interpreting the program counter (pc) values returned by runtime.Callers. +// +// Package stack's types implement fmt.Formatter, which provides a simple and +// flexible way to declaratively configure formatting when used with logging +// or error tracking packages. +package stack + +import ( + "bytes" + "errors" + "fmt" + "io" + "runtime" + "strconv" + "strings" +) + +// Call records a single function invocation from a goroutine stack. +type Call struct { + frame runtime.Frame +} + +// Caller returns a Call from the stack of the current goroutine. The argument +// skip is the number of stack frames to ascend, with 0 identifying the +// calling function. +func Caller(skip int) Call { + // As of Go 1.9 we need room for up to three PC entries. + // + // 0. An entry for the stack frame prior to the target to check for + // special handling needed if that prior entry is runtime.sigpanic. + // 1. A possible second entry to hold metadata about skipped inlined + // functions. If inline functions were not skipped the target frame + // PC will be here. + // 2. A third entry for the target frame PC when the second entry + // is used for skipped inline functions. + var pcs [3]uintptr + n := runtime.Callers(skip+1, pcs[:]) + frames := runtime.CallersFrames(pcs[:n]) + frame, _ := frames.Next() + frame, _ = frames.Next() + + return Call{ + frame: frame, + } +} + +// String implements fmt.Stinger. It is equivalent to fmt.Sprintf("%v", c). +func (c Call) String() string { + return fmt.Sprint(c) +} + +// MarshalText implements encoding.TextMarshaler. It formats the Call the same +// as fmt.Sprintf("%v", c). +func (c Call) MarshalText() ([]byte, error) { + if c.frame == (runtime.Frame{}) { + return nil, ErrNoFunc + } + + buf := bytes.Buffer{} + fmt.Fprint(&buf, c) + return buf.Bytes(), nil +} + +// ErrNoFunc means that the Call has a nil *runtime.Func. The most likely +// cause is a Call with the zero value. +var ErrNoFunc = errors.New("no call stack information") + +// Format implements fmt.Formatter with support for the following verbs. +// +// %s source file +// %d line number +// %n function name +// %k last segment of the package path +// %v equivalent to %s:%d +// +// It accepts the '+' and '#' flags for most of the verbs as follows. +// +// %+s path of source file relative to the compile time GOPATH, +// or the module path joined to the path of source file relative +// to module root +// %#s full path of source file +// %+n import path qualified function name +// %+k full package path +// %+v equivalent to %+s:%d +// %#v equivalent to %#s:%d +func (c Call) Format(s fmt.State, verb rune) { + if c.frame == (runtime.Frame{}) { + fmt.Fprintf(s, "%%!%c(NOFUNC)", verb) + return + } + + switch verb { + case 's', 'v': + file := c.frame.File + switch { + case s.Flag('#'): + // done + case s.Flag('+'): + file = pkgFilePath(&c.frame) + default: + const sep = "/" + if i := strings.LastIndex(file, sep); i != -1 { + file = file[i+len(sep):] + } + } + io.WriteString(s, file) + if verb == 'v' { + buf := [7]byte{':'} + s.Write(strconv.AppendInt(buf[:1], int64(c.frame.Line), 10)) + } + + case 'd': + buf := [6]byte{} + s.Write(strconv.AppendInt(buf[:0], int64(c.frame.Line), 10)) + + case 'k': + name := c.frame.Function + const pathSep = "/" + start, end := 0, len(name) + if i := strings.LastIndex(name, pathSep); i != -1 { + start = i + len(pathSep) + } + const pkgSep = "." + if i := strings.Index(name[start:], pkgSep); i != -1 { + end = start + i + } + if s.Flag('+') { + start = 0 + } + io.WriteString(s, name[start:end]) + + case 'n': + name := c.frame.Function + if !s.Flag('+') { + const pathSep = "/" + if i := strings.LastIndex(name, pathSep); i != -1 { + name = name[i+len(pathSep):] + } + const pkgSep = "." + if i := strings.Index(name, pkgSep); i != -1 { + name = name[i+len(pkgSep):] + } + } + io.WriteString(s, name) + } +} + +// Frame returns the call frame infomation for the Call. +func (c Call) Frame() runtime.Frame { + return c.frame +} + +// PC returns the program counter for this call frame; multiple frames may +// have the same PC value. +// +// Deprecated: Use Call.Frame instead. +func (c Call) PC() uintptr { + return c.frame.PC +} + +// CallStack records a sequence of function invocations from a goroutine +// stack. +type CallStack []Call + +// String implements fmt.Stinger. It is equivalent to fmt.Sprintf("%v", cs). +func (cs CallStack) String() string { + return fmt.Sprint(cs) +} + +var ( + openBracketBytes = []byte("[") + closeBracketBytes = []byte("]") + spaceBytes = []byte(" ") +) + +// MarshalText implements encoding.TextMarshaler. It formats the CallStack the +// same as fmt.Sprintf("%v", cs). +func (cs CallStack) MarshalText() ([]byte, error) { + buf := bytes.Buffer{} + buf.Write(openBracketBytes) + for i, pc := range cs { + if i > 0 { + buf.Write(spaceBytes) + } + fmt.Fprint(&buf, pc) + } + buf.Write(closeBracketBytes) + return buf.Bytes(), nil +} + +// Format implements fmt.Formatter by printing the CallStack as square brackets +// ([, ]) surrounding a space separated list of Calls each formatted with the +// supplied verb and options. +func (cs CallStack) Format(s fmt.State, verb rune) { + s.Write(openBracketBytes) + for i, pc := range cs { + if i > 0 { + s.Write(spaceBytes) + } + pc.Format(s, verb) + } + s.Write(closeBracketBytes) +} + +// Trace returns a CallStack for the current goroutine with element 0 +// identifying the calling function. +func Trace() CallStack { + var pcs [512]uintptr + n := runtime.Callers(1, pcs[:]) + + frames := runtime.CallersFrames(pcs[:n]) + cs := make(CallStack, 0, n) + + // Skip extra frame retrieved just to make sure the runtime.sigpanic + // special case is handled. + frame, more := frames.Next() + + for more { + frame, more = frames.Next() + cs = append(cs, Call{frame: frame}) + } + + return cs +} + +// TrimBelow returns a slice of the CallStack with all entries below c +// removed. +func (cs CallStack) TrimBelow(c Call) CallStack { + for len(cs) > 0 && cs[0] != c { + cs = cs[1:] + } + return cs +} + +// TrimAbove returns a slice of the CallStack with all entries above c +// removed. +func (cs CallStack) TrimAbove(c Call) CallStack { + for len(cs) > 0 && cs[len(cs)-1] != c { + cs = cs[:len(cs)-1] + } + return cs +} + +// pkgIndex returns the index that results in file[index:] being the path of +// file relative to the compile time GOPATH, and file[:index] being the +// $GOPATH/src/ portion of file. funcName must be the name of a function in +// file as returned by runtime.Func.Name. +func pkgIndex(file, funcName string) int { + // As of Go 1.6.2 there is no direct way to know the compile time GOPATH + // at runtime, but we can infer the number of path segments in the GOPATH. + // We note that runtime.Func.Name() returns the function name qualified by + // the import path, which does not include the GOPATH. Thus we can trim + // segments from the beginning of the file path until the number of path + // separators remaining is one more than the number of path separators in + // the function name. For example, given: + // + // GOPATH /home/user + // file /home/user/src/pkg/sub/file.go + // fn.Name() pkg/sub.Type.Method + // + // We want to produce: + // + // file[:idx] == /home/user/src/ + // file[idx:] == pkg/sub/file.go + // + // From this we can easily see that fn.Name() has one less path separator + // than our desired result for file[idx:]. We count separators from the + // end of the file path until it finds two more than in the function name + // and then move one character forward to preserve the initial path + // segment without a leading separator. + const sep = "/" + i := len(file) + for n := strings.Count(funcName, sep) + 2; n > 0; n-- { + i = strings.LastIndex(file[:i], sep) + if i == -1 { + i = -len(sep) + break + } + } + // get back to 0 or trim the leading separator + return i + len(sep) +} + +// pkgFilePath returns the frame's filepath relative to the compile-time GOPATH, +// or its module path joined to its path relative to the module root. +// +// As of Go 1.11 there is no direct way to know the compile time GOPATH or +// module paths at runtime, but we can piece together the desired information +// from available information. We note that runtime.Frame.Function contains the +// function name qualified by the package path, which includes the module path +// but not the GOPATH. We can extract the package path from that and append the +// last segments of the file path to arrive at the desired package qualified +// file path. For example, given: +// +// GOPATH /home/user +// import path pkg/sub +// frame.File /home/user/src/pkg/sub/file.go +// frame.Function pkg/sub.Type.Method +// Desired return pkg/sub/file.go +// +// It appears that we simply need to trim ".Type.Method" from frame.Function and +// append "/" + path.Base(file). +// +// But there are other wrinkles. Although it is idiomatic to do so, the internal +// name of a package is not required to match the last segment of its import +// path. In addition, the introduction of modules in Go 1.11 allows working +// without a GOPATH. So we also must make these work right: +// +// GOPATH /home/user +// import path pkg/go-sub +// package name sub +// frame.File /home/user/src/pkg/go-sub/file.go +// frame.Function pkg/sub.Type.Method +// Desired return pkg/go-sub/file.go +// +// Module path pkg/v2 +// import path pkg/v2/go-sub +// package name sub +// frame.File /home/user/cloned-pkg/go-sub/file.go +// frame.Function pkg/v2/sub.Type.Method +// Desired return pkg/v2/go-sub/file.go +// +// We can handle all of these situations by using the package path extracted +// from frame.Function up to, but not including, the last segment as the prefix +// and the last two segments of frame.File as the suffix of the returned path. +// This preserves the existing behavior when working in a GOPATH without modules +// and a semantically equivalent behavior when used in module aware project. +func pkgFilePath(frame *runtime.Frame) string { + pre := pkgPrefix(frame.Function) + post := pathSuffix(frame.File) + if pre == "" { + return post + } + return pre + "/" + post +} + +// pkgPrefix returns the import path of the function's package with the final +// segment removed. +func pkgPrefix(funcName string) string { + const pathSep = "/" + end := strings.LastIndex(funcName, pathSep) + if end == -1 { + return "" + } + return funcName[:end] +} + +// pathSuffix returns the last two segments of path. +func pathSuffix(path string) string { + const pathSep = "/" + lastSep := strings.LastIndex(path, pathSep) + if lastSep == -1 { + return path + } + return path[strings.LastIndex(path[:lastSep], pathSep)+1:] +} + +var runtimePath string + +func init() { + var pcs [3]uintptr + runtime.Callers(0, pcs[:]) + frames := runtime.CallersFrames(pcs[:]) + frame, _ := frames.Next() + file := frame.File + + idx := pkgIndex(frame.File, frame.Function) + + runtimePath = file[:idx] + if runtime.GOOS == "windows" { + runtimePath = strings.ToLower(runtimePath) + } +} + +func inGoroot(c Call) bool { + file := c.frame.File + if len(file) == 0 || file[0] == '?' { + return true + } + if runtime.GOOS == "windows" { + file = strings.ToLower(file) + } + return strings.HasPrefix(file, runtimePath) || strings.HasSuffix(file, "/_testmain.go") +} + +// TrimRuntime returns a slice of the CallStack with the topmost entries from +// the go runtime removed. It considers any calls originating from unknown +// files, files under GOROOT, or _testmain.go as part of the runtime. +func (cs CallStack) TrimRuntime() CallStack { + for len(cs) > 0 && inGoroot(cs[len(cs)-1]) { + cs = cs[:len(cs)-1] + } + return cs +} diff --git a/vendor/github.com/magiconair/properties/properties.go b/vendor/github.com/magiconair/properties/properties.go index fb2f7b4..5829761 100644 --- a/vendor/github.com/magiconair/properties/properties.go +++ b/vendor/github.com/magiconair/properties/properties.go @@ -18,6 +18,7 @@ import ( "strconv" "strings" "time" + "unicode" "unicode/utf8" ) @@ -306,6 +307,40 @@ func (p *Properties) getFloat64(key string) (value float64, err error) { // ---------------------------------------------------------------------------- +// GetFloat32 parses the expanded value as a float32 if the key exists. +// If key does not exist or the value cannot be parsed the default +// value is returned. +func (p *Properties) GetFloat32(key string, def float32) float32 { + v, err := p.getFloat32(key) + if err != nil { + return def + } + return v +} + +// MustGetFloat32 parses the expanded value as a float32 if the key exists. +// If key does not exist or the value cannot be parsed the function panics. +func (p *Properties) MustGetFloat32(key string) float32 { + v, err := p.getFloat32(key) + if err != nil { + ErrorHandler(err) + } + return v +} + +func (p *Properties) getFloat32(key string) (value float32, err error) { + if v, ok := p.Get(key); ok { + n, err := strconv.ParseFloat(v, 32) + if err != nil { + return 0, err + } + return float32(n), nil + } + return 0, invalidKeyError(key) +} + +// ---------------------------------------------------------------------------- + // GetInt parses the expanded value as an int if the key exists. // If key does not exist or the value cannot be parsed the default // value is returned. If the value does not fit into an int the @@ -366,6 +401,40 @@ func (p *Properties) getInt64(key string) (value int64, err error) { // ---------------------------------------------------------------------------- +// GetInt32 parses the expanded value as an int32 if the key exists. +// If key does not exist or the value cannot be parsed the default +// value is returned. +func (p *Properties) GetInt32(key string, def int32) int32 { + v, err := p.getInt32(key) + if err != nil { + return def + } + return v +} + +// MustGetInt32 parses the expanded value as an int if the key exists. +// If key does not exist or the value cannot be parsed the function panics. +func (p *Properties) MustGetInt32(key string) int32 { + v, err := p.getInt32(key) + if err != nil { + ErrorHandler(err) + } + return v +} + +func (p *Properties) getInt32(key string) (value int32, err error) { + if v, ok := p.Get(key); ok { + n, err := strconv.ParseInt(v, 10, 32) + if err != nil { + return 0, err + } + return int32(n), nil + } + return 0, invalidKeyError(key) +} + +// ---------------------------------------------------------------------------- + // GetUint parses the expanded value as an uint if the key exists. // If key does not exist or the value cannot be parsed the default // value is returned. If the value does not fit into an int the @@ -426,6 +495,40 @@ func (p *Properties) getUint64(key string) (value uint64, err error) { // ---------------------------------------------------------------------------- +// GetUint32 parses the expanded value as an uint32 if the key exists. +// If key does not exist or the value cannot be parsed the default +// value is returned. +func (p *Properties) GetUint32(key string, def uint32) uint32 { + v, err := p.getUint32(key) + if err != nil { + return def + } + return v +} + +// MustGetUint32 parses the expanded value as an int if the key exists. +// If key does not exist or the value cannot be parsed the function panics. +func (p *Properties) MustGetUint32(key string) uint32 { + v, err := p.getUint32(key) + if err != nil { + ErrorHandler(err) + } + return v +} + +func (p *Properties) getUint32(key string) (value uint32, err error) { + if v, ok := p.Get(key); ok { + n, err := strconv.ParseUint(v, 10, 32) + if err != nil { + return 0, err + } + return uint32(n), nil + } + return 0, invalidKeyError(key) +} + +// ---------------------------------------------------------------------------- + // GetString returns the expanded value for the given key if exists or // the default value otherwise. func (p *Properties) GetString(key, def string) string { @@ -799,8 +902,13 @@ func encodeUtf8(s string, special string) string { v := "" for pos := 0; pos < len(s); { r, w := utf8.DecodeRuneInString(s[pos:]) + switch { + case pos == 0 && unicode.IsSpace(r): // escape leading whitespace + v += escape(r, " ") + default: + v += escape(r, special) // escape special chars only + } pos += w - v += escape(r, special) } return v } @@ -811,6 +919,8 @@ func encodeIso(s string, special string) string { var v string for pos := 0; pos < len(s); { switch r, w = utf8.DecodeRuneInString(s[pos:]); { + case pos == 0 && unicode.IsSpace(r): // escape leading whitespace + v += escape(r, " ") case r < 1<<8: // single byte rune -> escape special chars only v += escape(r, special) case r < 1<<16: // two byte rune -> unicode literal diff --git a/vendor/github.com/playwright-community/playwright-go/.gitattributes b/vendor/github.com/playwright-community/playwright-go/.gitattributes new file mode 100644 index 0000000..c976050 --- /dev/null +++ b/vendor/github.com/playwright-community/playwright-go/.gitattributes @@ -0,0 +1,3 @@ +# text files must be lf for golden file tests to work +* text=auto eol=lf + diff --git a/vendor/github.com/playwright-community/playwright-go/.gitignore b/vendor/github.com/playwright-community/playwright-go/.gitignore new file mode 100644 index 0000000..83e2355 --- /dev/null +++ b/vendor/github.com/playwright-community/playwright-go/.gitignore @@ -0,0 +1,34 @@ +# Created by https://www.toptal.com/developers/gitignore/api/go +# Edit at https://www.toptal.com/developers/gitignore?templates=go + +### Go ### +# Binaries for programs and plugins +*.exe +*.exe~ +*.dll +*.so +*.dylib + +# Test binary, built with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out + +# Dependency directories (remove the comment below to include it) +# vendor/ + +### Go Patch ### +/vendor/ +/Godeps/ + +# End of https://www.toptal.com/developers/gitignore/api/go +covprofile +.idea/ +.DS_Store + +api.json +_site/ +.jekyll-cache/ + +.vscode/settings.json \ No newline at end of file diff --git a/vendor/github.com/playwright-community/playwright-go/.gitmodules b/vendor/github.com/playwright-community/playwright-go/.gitmodules new file mode 100644 index 0000000..9ab899d --- /dev/null +++ b/vendor/github.com/playwright-community/playwright-go/.gitmodules @@ -0,0 +1,3 @@ +[submodule "playwright"] + path = playwright + url = https://github.com/microsoft/playwright diff --git a/vendor/github.com/playwright-community/playwright-go/.golangci.yaml b/vendor/github.com/playwright-community/playwright-go/.golangci.yaml new file mode 100644 index 0000000..1557a3f --- /dev/null +++ b/vendor/github.com/playwright-community/playwright-go/.golangci.yaml @@ -0,0 +1,6 @@ +--- +linters: + enable-all: false + disable-all: false + enable: + - gofumpt \ No newline at end of file diff --git a/vendor/github.com/playwright-community/playwright-go/.nojekyll b/vendor/github.com/playwright-community/playwright-go/.nojekyll new file mode 100644 index 0000000..e69de29 diff --git a/vendor/github.com/playwright-community/playwright-go/404.html b/vendor/github.com/playwright-community/playwright-go/404.html new file mode 100644 index 0000000..086a5c9 --- /dev/null +++ b/vendor/github.com/playwright-community/playwright-go/404.html @@ -0,0 +1,25 @@ +--- +permalink: /404.html +layout: default +--- + + + +
+

404

+ +

Page not found :(

+

The requested page could not be found.

+
diff --git a/vendor/github.com/playwright-community/playwright-go/CONTRIBUTING.md b/vendor/github.com/playwright-community/playwright-go/CONTRIBUTING.md new file mode 100644 index 0000000..3b11995 --- /dev/null +++ b/vendor/github.com/playwright-community/playwright-go/CONTRIBUTING.md @@ -0,0 +1,39 @@ +# Contributing + +## Code style +The Go code is linted with [golangci-lint](https://golangci-lint.run/) and formatted with [gofumpt](https://github.com/mvdan/gofumpt). Please configure your editor to run the tools while developing and make sure to run the tools before committing any code. + +## Tests + +### Test coverage + +For every Pull Request on GitHub and on the main branch the coverage data will get sent over to [Coveralls](https://coveralls.io/github/playwright-community/playwright-go), this is helpful for finding functions that aren't covered by tests. + +### Running tests + +You can use the `BROWSER` environment variable to use a different browser than Chromium for the tests and use the `HEADLESS` environment variable which is useful for debugging. + +``` +BROWSER=chromium HEADLESS=1 go test -v --race ./... +``` + +### Roll + +1. Find out to which upstream version you want to roll, and change the value of `playwrightCliVersion` in the **run.go** to the new version. +1. Download current version of Playwright driver `go run scripts/install-browsers/main.go` +1. Apply patch `bash scripts/apply-patch.sh` +1. Fix merge conflicts if any, otherwise ignore this step. Once you are happy you can commit the changes `cd playwright; git commit -am "apply patch" && cd ..` +1. Regenerate a new patch `bash scripts/update-patch.sh` +1. Generate go code `go generate ./...` + +To adapt to the new version of Playwright's protocol and feature updates, you may need to modify the patch. Refer to the following steps: + +1. Apply patch `bash scripts/apply-patch.sh` +1. `cd playwright` +1. Revert the patch`git reset HEAD~1` +1. Modify the files under `docs/src/api`, etc. as needed. Available references: + - Protocol `packages/protocol/src/protocol.yml` + - [Playwright python](https://github.com/microsoft/playwright-python) +1. Commit the changes `git commit -am "apply patch"` +1. Regenerate a new patch `bash scripts/update-patch.sh` +1. Generate go code `go generate ./...`. diff --git a/vendor/github.com/playwright-community/playwright-go/Dockerfile.example b/vendor/github.com/playwright-community/playwright-go/Dockerfile.example new file mode 100644 index 0000000..3077cf3 --- /dev/null +++ b/vendor/github.com/playwright-community/playwright-go/Dockerfile.example @@ -0,0 +1,25 @@ +# Stage 1: Modules caching +FROM golang:1.22 as modules +COPY go.mod go.sum /modules/ +WORKDIR /modules +RUN go mod download + +# Stage 2: Build +FROM golang:1.22 as builder +COPY --from=modules /go/pkg /go/pkg +COPY . /workdir +WORKDIR /workdir +# Install playwright cli with right version for later use +RUN PWGO_VER=$(grep -oE "playwright-go v\S+" /workdir/go.mod | sed 's/playwright-go //g') \ + && go install github.com/playwright-community/playwright-go/cmd/playwright@${PWGO_VER} +# Build your app +RUN GOOS=linux GOARCH=amd64 go build -o /bin/myapp + +# Stage 3: Final +FROM ubuntu:noble +COPY --from=builder /go/bin/playwright /bin/myapp / +RUN apt-get update && apt-get install -y ca-certificates tzdata \ + # Install dependencies and all browsers (or specify one) + && /playwright install --with-deps \ + && rm -rf /var/lib/apt/lists/* +CMD ["/myapp"] \ No newline at end of file diff --git a/vendor/github.com/playwright-community/playwright-go/LICENSE b/vendor/github.com/playwright-community/playwright-go/LICENSE new file mode 100644 index 0000000..d4f29b3 --- /dev/null +++ b/vendor/github.com/playwright-community/playwright-go/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 Max Schmitt + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/github.com/playwright-community/playwright-go/README.md b/vendor/github.com/playwright-community/playwright-go/README.md new file mode 100644 index 0000000..a9e3dd7 --- /dev/null +++ b/vendor/github.com/playwright-community/playwright-go/README.md @@ -0,0 +1,148 @@ +# 🎭 [Playwright](https://github.com/microsoft/playwright#readme) for + +## Looking for maintainers and see [here](https://github.com/playwright-community/playwright-go/issues/122). Thanks! + +[![PkgGoDev](https://pkg.go.dev/badge/github.com/playwright-community/playwright-go)](https://pkg.go.dev/github.com/playwright-community/playwright-go) +[![License](https://img.shields.io/badge/License-MIT-blue.svg)](http://opensource.org/licenses/MIT) +[![Go Report Card](https://goreportcard.com/badge/github.com/playwright-community/playwright-go)](https://goreportcard.com/report/github.com/playwright-community/playwright-go) ![Build Status](https://github.com/playwright-community/playwright-go/workflows/Go/badge.svg) +[![Join Slack](https://img.shields.io/badge/join-slack-infomational)](https://aka.ms/playwright-slack) [![Coverage Status](https://coveralls.io/repos/github/playwright-community/playwright-go/badge.svg?branch=main)](https://coveralls.io/github/playwright-community/playwright-go?branch=main) [![Chromium version](https://img.shields.io/badge/chromium-136.0.7103.25-blue.svg?logo=google-chrome)](https://www.chromium.org/Home) [![Firefox version](https://img.shields.io/badge/firefox-137.0-blue.svg?logo=mozilla-firefox)](https://www.mozilla.org/en-US/firefox/new/) [![WebKit version](https://img.shields.io/badge/webkit-18.4-blue.svg?logo=safari)](https://webkit.org/) + +[API reference](https://playwright.dev/docs/api/class-playwright) | [Example recipes](https://github.com/playwright-community/playwright-go/tree/main/examples) + +Playwright is a Go library to automate [Chromium](https://www.chromium.org/Home), [Firefox](https://www.mozilla.org/en-US/firefox/new/) and [WebKit](https://webkit.org/) with a single API. Playwright is built to enable cross-browser web automation that is **ever-green**, **capable**, **reliable** and **fast**. + +| | Linux | macOS | Windows | +| :--- | :---: | :---: | :---: | +| Chromium 136.0.7103.25 | ✅ | ✅ | ✅ | +| WebKit 18.4 | ✅ | ✅ | ✅ | +| Firefox 137.0 | ✅ | ✅ | ✅ | + +Headless execution is supported for all the browsers on all platforms. + +## Installation + +```txt +go get -u github.com/playwright-community/playwright-go +``` + +Install the browsers and OS dependencies: + +```bash +go run github.com/playwright-community/playwright-go/cmd/playwright@latest install --with-deps +# Or +go install github.com/playwright-community/playwright-go/cmd/playwright@latest +playwright install --with-deps +``` + +Alternatively you can do it inside your program via which downloads the driver and browsers: + +```go +err := playwright.Install() +``` + +## Capabilities + +Playwright is built to automate the broad and growing set of web browser capabilities used by Single Page Apps and Progressive Web Apps. + +* Scenarios that span multiple page, domains and iframes +* Auto-wait for elements to be ready before executing actions (like click, fill) +* Intercept network activity for stubbing and mocking network requests +* Emulate mobile devices, geolocation, permissions +* Support for web components via shadow-piercing selectors +* Native input events for mouse and keyboard +* Upload and download files + +## Example + +The following example crawls the current top voted items from [Hacker News](https://news.ycombinator.com). + +```go + +package main + +import ( + "fmt" + "log" + + "github.com/playwright-community/playwright-go" +) + +func main() { + pw, err := playwright.Run() + if err != nil { + log.Fatalf("could not start playwright: %v", err) + } + browser, err := pw.Chromium.Launch() + if err != nil { + log.Fatalf("could not launch browser: %v", err) + } + page, err := browser.NewPage() + if err != nil { + log.Fatalf("could not create page: %v", err) + } + if _, err = page.Goto("https://news.ycombinator.com"); err != nil { + log.Fatalf("could not goto: %v", err) + } + entries, err := page.Locator(".athing").All() + if err != nil { + log.Fatalf("could not get entries: %v", err) + } + for i, entry := range entries { + title, err := entry.Locator("td.title > span > a").TextContent() + if err != nil { + log.Fatalf("could not get text content: %v", err) + } + fmt.Printf("%d: %s\n", i+1, title) + } + if err = browser.Close(); err != nil { + log.Fatalf("could not close browser: %v", err) + } + if err = pw.Stop(); err != nil { + log.Fatalf("could not stop Playwright: %v", err) + } +} +``` + +## Docker +Refer to the [Dockerfile.example](./Dockerfile.example) to build your own docker image. + +## More examples + +* Refer to [helper_test.go](./tests/helper_test.go) for End-To-End testing +* [Downloading files](./examples/download/main.go) +* [End-To-End testing a website](./examples/end-to-end-testing/main.go) +* [Executing JavaScript in the browser](./examples/javascript/main.go) +* [Emulate mobile and geolocation](./examples/mobile-and-geolocation/main.go) +* [Parallel scraping using a WaitGroup](./examples/parallel-scraping/main.go) +* [Rendering a PDF of a website](./examples/pdf/main.go) +* [Scraping HackerNews](./examples/scraping/main.go) +* [Take a screenshot](./examples/screenshot/main.go) +* [Record a video](./examples/video/main.go) +* [Monitor network activity](./examples/network-monitoring/main.go) + +## How does it work? + +Playwright is a Node.js library which uses: + +* Chrome DevTools Protocol to communicate with Chromium +* Patched Firefox to communicate with Firefox +* Patched WebKit to communicate with WebKit + +These patches are based on the original sources of the browsers and don't modify the browser behaviour so the browsers are basically the same (see [here](https://github.com/microsoft/playwright/tree/main/browser_patches)) as you see them in the wild. The support for different programming languages is based on exposing a RPC server in the Node.js land which can be used to allow other languages to use Playwright without implementing all the custom logic: + +* [Playwright for Python](https://github.com/microsoft/playwright-python) +* [Playwright for .NET](https://github.com/microsoft/playwright-sharp) +* [Playwright for Java](https://github.com/microsoft/playwright-java) +* [Playwright for Go](https://github.com/playwright-community/playwright-go) + +The bridge between Node.js and the other languages is basically a Node.js runtime combined with Playwright which gets shipped for each of these languages (around 50MB) and then communicates over stdio to send the relevant commands. This will also download the pre-compiled browsers. + +## Is Playwright for Go ready? + +We are ready for your feedback, but we are still covering Playwright Go with the tests. + +## Resources + +* [Playwright for Go Documentation](https://pkg.go.dev/github.com/playwright-community/playwright-go) +* [Playwright Documentation](https://playwright.dev/docs/api/class-playwright) +* [Example recipes](https://github.com/playwright-community/playwright-go/tree/main/examples) diff --git a/vendor/github.com/playwright-community/playwright-go/_config.yml b/vendor/github.com/playwright-community/playwright-go/_config.yml new file mode 100644 index 0000000..15ec3b2 --- /dev/null +++ b/vendor/github.com/playwright-community/playwright-go/_config.yml @@ -0,0 +1,21 @@ +title: Playwright for Go +email: max@schmitt.mx +description: >- # this means to ignore newlines until "baseurl:" + Playwright is a Node.js library to automate Chromium, Firefox and WebKit with a single API. + Playwright is built to enable cross-browser web automation that is ever-green, capable, reliable and fast. +baseurl: "/playwright-go" +url: "https://playwright-community.github.io/" +twitter_username: maxibanki +github_username: playwright-community +remote_theme: pages-themes/cayman@v0.2.0 +plugins: + - jekyll-remote-theme + - jekyll-optional-front-matter + - jekyll-readme-index +exclude: + - playwright/ +defaults: + - scope: + path: "" # an empty string here means all files in the project + values: + layout: "default" diff --git a/vendor/github.com/playwright-community/playwright-go/apiresponse_assertions.go b/vendor/github.com/playwright-community/playwright-go/apiresponse_assertions.go new file mode 100644 index 0000000..187618e --- /dev/null +++ b/vendor/github.com/playwright-community/playwright-go/apiresponse_assertions.go @@ -0,0 +1,75 @@ +package playwright + +import ( + "errors" + "fmt" + "regexp" + "strings" +) + +type apiResponseAssertionsImpl struct { + actual APIResponse + isNot bool +} + +func newAPIResponseAssertions(actual APIResponse, isNot bool) *apiResponseAssertionsImpl { + return &apiResponseAssertionsImpl{ + actual: actual, + isNot: isNot, + } +} + +func (ar *apiResponseAssertionsImpl) Not() APIResponseAssertions { + return newAPIResponseAssertions(ar.actual, true) +} + +func (ar *apiResponseAssertionsImpl) ToBeOK() error { + if ar.isNot != ar.actual.Ok() { + return nil + } + message := fmt.Sprintf(`Response status expected to be within [200..299] range, was %v`, ar.actual.Status()) + if ar.isNot { + message = strings.ReplaceAll(message, "expected to", "expected not to") + } + logList, err := ar.actual.(*apiResponseImpl).fetchLog() + if err != nil { + return err + } + log := strings.Join(logList, "\n") + if log != "" { + message += "\nCall log:\n" + log + } + + isTextEncoding := false + contentType, ok := ar.actual.Headers()["content-type"] + if ok { + isTextEncoding = isTexualMimeType(contentType) + } + if isTextEncoding { + text, err := ar.actual.Text() + if err == nil { + message += fmt.Sprintf(`\n Response Text:\n %s`, subString(text, 0, 1000)) + } + } + return errors.New(message) +} + +func isTexualMimeType(mimeType string) bool { + re := regexp.MustCompile(`^(text\/.*?|application\/(json|(x-)?javascript|xml.*?|ecmascript|graphql|x-www-form-urlencoded)|image\/svg(\+xml)?|application\/.*?(\+json|\+xml))(;\s*charset=.*)?$`) + return re.MatchString(mimeType) +} + +func subString(s string, start, length int) string { + if start < 0 { + start = 0 + } + if length < 0 { + length = 0 + } + rs := []rune(s) + end := start + length + if end > len(rs) { + end = len(rs) + } + return string(rs[start:end]) +} diff --git a/vendor/github.com/playwright-community/playwright-go/artifact.go b/vendor/github.com/playwright-community/playwright-go/artifact.go new file mode 100644 index 0000000..c76b892 --- /dev/null +++ b/vendor/github.com/playwright-community/playwright-go/artifact.go @@ -0,0 +1,70 @@ +package playwright + +import ( + "errors" + "fmt" +) + +type artifactImpl struct { + channelOwner +} + +func (a *artifactImpl) AbsolutePath() string { + return a.initializer["absolutePath"].(string) +} + +func (a *artifactImpl) PathAfterFinished() (string, error) { + if a.connection.isRemote { + return "", errors.New("Path is not available when connecting remotely. Use SaveAs() to save a local copy") + } + path, err := a.channel.Send("pathAfterFinished") + return path.(string), err +} + +func (a *artifactImpl) SaveAs(path string) error { + if !a.connection.isRemote { + _, err := a.channel.Send("saveAs", map[string]interface{}{ + "path": path, + }) + return err + } + streamChannel, err := a.channel.Send("saveAsStream") + if err != nil { + return err + } + stream := fromChannel(streamChannel).(*streamImpl) + return stream.SaveAs(path) +} + +func (a *artifactImpl) Failure() error { + reason, err := a.channel.Send("failure") + if reason == nil { + return err + } + return fmt.Errorf("%w: %v", ErrPlaywright, reason) +} + +func (a *artifactImpl) Delete() error { + _, err := a.channel.Send("delete") + return err +} + +func (a *artifactImpl) Cancel() error { + _, err := a.channel.Send("cancel") + return err +} + +func (a *artifactImpl) ReadIntoBuffer() ([]byte, error) { + streamChannel, err := a.channel.Send("stream") + if err != nil { + return nil, err + } + stream := fromChannel(streamChannel) + return stream.(*streamImpl).ReadAll() +} + +func newArtifact(parent *channelOwner, objectType string, guid string, initializer map[string]interface{}) *artifactImpl { + artifact := &artifactImpl{} + artifact.createChannelOwner(artifact, parent, objectType, guid, initializer) + return artifact +} diff --git a/vendor/github.com/playwright-community/playwright-go/assertions.go b/vendor/github.com/playwright-community/playwright-go/assertions.go new file mode 100644 index 0000000..5e0e710 --- /dev/null +++ b/vendor/github.com/playwright-community/playwright-go/assertions.go @@ -0,0 +1,146 @@ +package playwright + +import ( + "errors" + "fmt" + "reflect" + "regexp" + "strings" +) + +const assertionsDefaultTimeout = 5000 // 5s + +type playwrightAssertionsImpl struct { + defaultTimeout *float64 +} + +// NewPlaywrightAssertions creates a new instance of PlaywrightAssertions +// - timeout: default value is 5000 (ms) +func NewPlaywrightAssertions(timeout ...float64) PlaywrightAssertions { + if len(timeout) > 0 { + return &playwrightAssertionsImpl{Float(timeout[0])} + } + return &playwrightAssertionsImpl{Float(assertionsDefaultTimeout)} +} + +func (pa *playwrightAssertionsImpl) APIResponse(response APIResponse) APIResponseAssertions { + return newAPIResponseAssertions(response, false) +} + +func (pa *playwrightAssertionsImpl) Locator(locator Locator) LocatorAssertions { + return newLocatorAssertions(locator, false, pa.defaultTimeout) +} + +func (pa *playwrightAssertionsImpl) Page(page Page) PageAssertions { + return newPageAssertions(page, false, pa.defaultTimeout) +} + +type expectedTextValue struct { + Str *string `json:"string,omitempty"` + RegexSource *string `json:"regexSource,omitempty"` + RegexFlags *string `json:"regexFlags,omitempty"` + MatchSubstring *bool `json:"matchSubstring,omitempty"` + IgnoreCase *bool `json:"ignoreCase,omitempty"` + NormalizeWhiteSpace *bool `json:"normalizeWhiteSpace,omitempty"` +} + +type frameExpectOptions struct { + ExpressionArg interface{} `json:"expressionArg,omitempty"` + ExpectedText []expectedTextValue `json:"expectedText,omitempty"` + ExpectedNumber *float64 `json:"expectedNumber,omitempty"` + ExpectedValue interface{} `json:"expectedValue,omitempty"` + UseInnerText *bool `json:"useInnerText,omitempty"` + IsNot bool `json:"isNot"` + Timeout *float64 `json:"timeout"` +} + +type frameExpectResult struct { + Matches bool `json:"matches"` + Received interface{} `json:"received,omitempty"` + TimedOut *bool `json:"timedOut,omitempty"` + Log []string `json:"log,omitempty"` +} + +type assertionsBase struct { + actualLocator Locator + isNot bool + defaultTimeout *float64 +} + +func (b *assertionsBase) expect( + expression string, + options frameExpectOptions, + expected interface{}, + message string, +) error { + options.IsNot = b.isNot + if options.Timeout == nil { + options.Timeout = b.defaultTimeout + } + if options.IsNot { + message = strings.ReplaceAll(message, "expected to", "expected not to") + } + result, err := b.actualLocator.(*locatorImpl).expect(expression, options) + if err != nil { + return err + } + + if result.Matches == b.isNot { + actual := result.Received + log := strings.Join(result.Log, "\n") + if log != "" { + log = "\nCall log:\n" + log + } + if expected != nil { + return fmt.Errorf("%s '%v'\nActual value: %v %s", message, expected, actual, log) + } + return fmt.Errorf("%s\nActual value: %v %s", message, actual, log) + } + + return nil +} + +func toExpectedTextValues( + items []interface{}, + matchSubstring bool, + normalizeWhiteSpace bool, + ignoreCase *bool, +) ([]expectedTextValue, error) { + var out []expectedTextValue + for _, item := range items { + switch item := item.(type) { + case string: + out = append(out, expectedTextValue{ + Str: String(item), + MatchSubstring: Bool(matchSubstring), + NormalizeWhiteSpace: Bool(normalizeWhiteSpace), + IgnoreCase: ignoreCase, + }) + case *regexp.Regexp: + pattern, flags := convertRegexp(item) + out = append(out, expectedTextValue{ + RegexSource: String(pattern), + RegexFlags: String(flags), + MatchSubstring: Bool(matchSubstring), + NormalizeWhiteSpace: Bool(normalizeWhiteSpace), + IgnoreCase: ignoreCase, + }) + default: + return nil, errors.New("value must be a string or regexp") + } + } + return out, nil +} + +func convertToInterfaceList(v interface{}) []interface{} { + rv := reflect.ValueOf(v) + if rv.Kind() != reflect.Slice { + return []interface{}{v} + } + + list := make([]interface{}, rv.Len()) + for i := 0; i < rv.Len(); i++ { + list[i] = rv.Index(i).Interface() + } + return list +} diff --git a/vendor/github.com/playwright-community/playwright-go/binding_call.go b/vendor/github.com/playwright-community/playwright-go/binding_call.go new file mode 100644 index 0000000..8468992 --- /dev/null +++ b/vendor/github.com/playwright-community/playwright-go/binding_call.go @@ -0,0 +1,87 @@ +package playwright + +import ( + "fmt" + "strings" + + "github.com/go-stack/stack" +) + +type BindingCall interface { + Call(f BindingCallFunction) +} + +type bindingCallImpl struct { + channelOwner +} + +// BindingSource is the value passed to a binding call execution +type BindingSource struct { + Context BrowserContext + Page Page + Frame Frame +} + +// ExposedFunction represents the func signature of an exposed function +type ExposedFunction = func(args ...interface{}) interface{} + +// BindingCallFunction represents the func signature of an exposed binding call func +type BindingCallFunction func(source *BindingSource, args ...interface{}) interface{} + +func (b *bindingCallImpl) Call(f BindingCallFunction) { + defer func() { + if r := recover(); r != nil { + if _, err := b.channel.Send("reject", map[string]interface{}{ + "error": serializeError(r.(error)), + }); err != nil { + logger.Error("could not reject BindingCall", "error", err) + } + } + }() + + frame := fromChannel(b.initializer["frame"]).(*frameImpl) + source := &BindingSource{ + Context: frame.Page().Context(), + Page: frame.Page(), + Frame: frame, + } + var result interface{} + if handle, ok := b.initializer["handle"]; ok { + result = f(source, fromChannel(handle)) + } else { + initializerArgs := b.initializer["args"].([]interface{}) + funcArgs := []interface{}{} + for i := 0; i < len(initializerArgs); i++ { + funcArgs = append(funcArgs, parseResult(initializerArgs[i])) + } + result = f(source, funcArgs...) + } + _, err := b.channel.Send("resolve", map[string]interface{}{ + "result": serializeArgument(result), + }) + if err != nil { + logger.Error("could not resolve BindingCall", "error", err) + } +} + +func serializeError(err error) map[string]interface{} { + st := stack.Trace().TrimRuntime() + if len(st) == 0 { // https://github.com/go-stack/stack/issues/27 + st = stack.Trace() + } + return map[string]interface{}{ + "error": &Error{ + Name: "Playwright for Go Error", + Message: err.Error(), + Stack: strings.ReplaceAll(strings.TrimFunc(fmt.Sprintf("%+v", st), func(r rune) bool { + return r == '[' || r == ']' + }), " ", "\n"), + }, + } +} + +func newBindingCall(parent *channelOwner, objectType string, guid string, initializer map[string]interface{}) *bindingCallImpl { + bt := &bindingCallImpl{} + bt.createChannelOwner(bt, parent, objectType, guid, initializer) + return bt +} diff --git a/vendor/github.com/playwright-community/playwright-go/browser.go b/vendor/github.com/playwright-community/playwright-go/browser.go new file mode 100644 index 0000000..c87540a --- /dev/null +++ b/vendor/github.com/playwright-community/playwright-go/browser.go @@ -0,0 +1,274 @@ +package playwright + +import ( + "encoding/base64" + "encoding/json" + "errors" + "fmt" + "os" + "path/filepath" +) + +type browserImpl struct { + channelOwner + isConnected bool + shouldCloseConnectionOnClose bool + contexts []BrowserContext + browserType BrowserType + chromiumTracingPath *string + closeReason *string +} + +func (b *browserImpl) BrowserType() BrowserType { + return b.browserType +} + +func (b *browserImpl) IsConnected() bool { + b.RLock() + defer b.RUnlock() + return b.isConnected +} + +func (b *browserImpl) NewContext(options ...BrowserNewContextOptions) (BrowserContext, error) { + overrides := map[string]interface{}{} + option := BrowserNewContextOptions{} + if len(options) == 1 { + option = options[0] + } + if option.AcceptDownloads != nil { + if *option.AcceptDownloads { + overrides["acceptDownloads"] = "accept" + } else { + overrides["acceptDownloads"] = "deny" + } + options[0].AcceptDownloads = nil + } + if option.ExtraHttpHeaders != nil { + overrides["extraHTTPHeaders"] = serializeMapToNameAndValue(options[0].ExtraHttpHeaders) + options[0].ExtraHttpHeaders = nil + } + if option.ClientCertificates != nil { + certs, err := transformClientCertificate(option.ClientCertificates) + if err != nil { + return nil, err + } + overrides["clientCertificates"] = certs + options[0].ClientCertificates = nil + } + if option.StorageStatePath != nil { + var storageState *OptionalStorageState + storageString, err := os.ReadFile(*options[0].StorageStatePath) + if err != nil { + return nil, fmt.Errorf("could not read storage state file: %w", err) + } + err = json.Unmarshal(storageString, &storageState) + if err != nil { + return nil, fmt.Errorf("could not parse storage state file: %w", err) + } + options[0].StorageState = storageState + options[0].StorageStatePath = nil + } + if option.NoViewport != nil && *options[0].NoViewport { + overrides["noDefaultViewport"] = true + options[0].NoViewport = nil + } + if option.RecordHarPath != nil { + overrides["recordHar"] = prepareRecordHarOptions(recordHarInputOptions{ + Path: *options[0].RecordHarPath, + URL: options[0].RecordHarURLFilter, + Mode: options[0].RecordHarMode, + Content: options[0].RecordHarContent, + OmitContent: options[0].RecordHarOmitContent, + }) + options[0].RecordHarPath = nil + options[0].RecordHarURLFilter = nil + options[0].RecordHarMode = nil + options[0].RecordHarContent = nil + options[0].RecordHarOmitContent = nil + } + channel, err := b.channel.Send("newContext", options, overrides) + if err != nil { + return nil, err + } + context := fromChannel(channel).(*browserContextImpl) + context.browser = b + b.browserType.(*browserTypeImpl).didCreateContext(context, &option, nil) + return context, nil +} + +func (b *browserImpl) NewPage(options ...BrowserNewPageOptions) (Page, error) { + opts := make([]BrowserNewContextOptions, 0) + if len(options) == 1 { + opts = append(opts, BrowserNewContextOptions(options[0])) + } + context, err := b.NewContext(opts...) + if err != nil { + return nil, err + } + page, err := context.NewPage() + if err != nil { + return nil, err + } + page.(*pageImpl).ownedContext = context + context.(*browserContextImpl).ownedPage = page + return page, nil +} + +func (b *browserImpl) NewBrowserCDPSession() (CDPSession, error) { + channel, err := b.channel.Send("newBrowserCDPSession") + if err != nil { + return nil, err + } + + cdpSession := fromChannel(channel).(*cdpSessionImpl) + + return cdpSession, nil +} + +func (b *browserImpl) Contexts() []BrowserContext { + b.Lock() + defer b.Unlock() + return b.contexts +} + +func (b *browserImpl) Close(options ...BrowserCloseOptions) (err error) { + if len(options) == 1 { + b.closeReason = options[0].Reason + } + + if b.shouldCloseConnectionOnClose { + err = b.connection.Stop() + } else if b.closeReason != nil { + _, err = b.channel.Send("close", map[string]interface{}{ + "reason": b.closeReason, + }) + } else { + _, err = b.channel.Send("close") + } + if err != nil && !errors.Is(err, ErrTargetClosed) { + return fmt.Errorf("close browser failed: %w", err) + } + return nil +} + +func (b *browserImpl) Version() string { + return b.initializer["version"].(string) +} + +func (b *browserImpl) StartTracing(options ...BrowserStartTracingOptions) error { + overrides := map[string]interface{}{} + option := BrowserStartTracingOptions{} + if len(options) == 1 { + option = options[0] + } + if option.Page != nil { + overrides["page"] = option.Page.(*pageImpl).channel + option.Page = nil + } + if option.Path != nil { + b.chromiumTracingPath = option.Path + option.Path = nil + } + _, err := b.channel.Send("startTracing", option, overrides) + return err +} + +func (b *browserImpl) StopTracing() ([]byte, error) { + channel, err := b.channel.Send("stopTracing") + if err != nil { + return nil, err + } + artifact := fromChannel(channel).(*artifactImpl) + binary, err := artifact.ReadIntoBuffer() + if err != nil { + return nil, err + } + err = artifact.Delete() + if err != nil { + return binary, err + } + if b.chromiumTracingPath != nil { + err := os.MkdirAll(filepath.Dir(*b.chromiumTracingPath), 0o777) + if err != nil { + return binary, err + } + err = os.WriteFile(*b.chromiumTracingPath, binary, 0o644) + if err != nil { + return binary, err + } + } + return binary, nil +} + +func (b *browserImpl) onClose() { + b.Lock() + if b.isConnected { + b.isConnected = false + b.Unlock() + b.Emit("disconnected", b) + return + } + b.Unlock() +} + +func (b *browserImpl) OnDisconnected(fn func(Browser)) { + b.On("disconnected", fn) +} + +func newBrowser(parent *channelOwner, objectType string, guid string, initializer map[string]interface{}) *browserImpl { + b := &browserImpl{ + isConnected: true, + contexts: make([]BrowserContext, 0), + } + b.createChannelOwner(b, parent, objectType, guid, initializer) + // convert parent to *browserTypeImpl + b.browserType = newBrowserType(parent.parent, parent.objectType, parent.guid, parent.initializer) + b.channel.On("close", b.onClose) + return b +} + +func transformClientCertificate(clientCertificates []ClientCertificate) ([]map[string]interface{}, error) { + results := make([]map[string]interface{}, 0) + + for _, cert := range clientCertificates { + data := map[string]interface{}{ + "origin": cert.Origin, + "passphrase": cert.Passphrase, + } + if len(cert.Cert) > 0 { + data["cert"] = base64.StdEncoding.EncodeToString(cert.Cert) + } else if cert.CertPath != nil { + content, err := os.ReadFile(*cert.CertPath) + if err != nil { + return nil, err + } + data["cert"] = base64.StdEncoding.EncodeToString(content) + } + + if len(cert.Key) > 0 { + data["key"] = base64.StdEncoding.EncodeToString(cert.Key) + } else if cert.KeyPath != nil { + content, err := os.ReadFile(*cert.KeyPath) + if err != nil { + return nil, err + } + data["key"] = base64.StdEncoding.EncodeToString(content) + } + + if len(cert.Pfx) > 0 { + data["pfx"] = base64.StdEncoding.EncodeToString(cert.Pfx) + } else if cert.PfxPath != nil { + content, err := os.ReadFile(*cert.PfxPath) + if err != nil { + return nil, err + } + data["pfx"] = base64.StdEncoding.EncodeToString(content) + } + + results = append(results, data) + } + if len(results) == 0 { + return nil, nil + } + return results, nil +} diff --git a/vendor/github.com/playwright-community/playwright-go/browser_context.go b/vendor/github.com/playwright-community/playwright-go/browser_context.go new file mode 100644 index 0000000..1d420d3 --- /dev/null +++ b/vendor/github.com/playwright-community/playwright-go/browser_context.go @@ -0,0 +1,914 @@ +package playwright + +import ( + "encoding/json" + "errors" + "fmt" + "os" + "regexp" + "slices" + "strings" + "sync" + + "github.com/playwright-community/playwright-go/internal/safe" +) + +type browserContextImpl struct { + channelOwner + timeoutSettings *timeoutSettings + closeWasCalled bool + options *BrowserNewContextOptions + pages []Page + routes []*routeHandlerEntry + webSocketRoutes []*webSocketRouteHandler + ownedPage Page + browser *browserImpl + serviceWorkers []Worker + backgroundPages []Page + bindings *safe.SyncMap[string, BindingCallFunction] + tracing *tracingImpl + request *apiRequestContextImpl + harRecorders map[string]harRecordingMetadata + closed chan struct{} + closeReason *string + harRouters []*harRouter + clock Clock +} + +func (b *browserContextImpl) Clock() Clock { + return b.clock +} + +func (b *browserContextImpl) SetDefaultNavigationTimeout(timeout float64) { + b.setDefaultNavigationTimeoutImpl(&timeout) +} + +func (b *browserContextImpl) setDefaultNavigationTimeoutImpl(timeout *float64) { + b.timeoutSettings.SetDefaultNavigationTimeout(timeout) + b.channel.SendNoReplyInternal("setDefaultNavigationTimeoutNoReply", map[string]interface{}{ + "timeout": timeout, + }) +} + +func (b *browserContextImpl) SetDefaultTimeout(timeout float64) { + b.setDefaultTimeoutImpl(&timeout) +} + +func (b *browserContextImpl) setDefaultTimeoutImpl(timeout *float64) { + b.timeoutSettings.SetDefaultTimeout(timeout) + b.channel.SendNoReplyInternal("setDefaultTimeoutNoReply", map[string]interface{}{ + "timeout": timeout, + }) +} + +func (b *browserContextImpl) Pages() []Page { + b.Lock() + defer b.Unlock() + return b.pages +} + +func (b *browserContextImpl) Browser() Browser { + return b.browser +} + +func (b *browserContextImpl) Tracing() Tracing { + return b.tracing +} + +func (b *browserContextImpl) NewCDPSession(page interface{}) (CDPSession, error) { + params := map[string]interface{}{} + + if p, ok := page.(*pageImpl); ok { + params["page"] = p.channel + } else if f, ok := page.(*frameImpl); ok { + params["frame"] = f.channel + } else { + return nil, fmt.Errorf("not page or frame: %v", page) + } + + channel, err := b.channel.Send("newCDPSession", params) + if err != nil { + return nil, err + } + + cdpSession := fromChannel(channel).(*cdpSessionImpl) + + return cdpSession, nil +} + +func (b *browserContextImpl) NewPage() (Page, error) { + if b.ownedPage != nil { + return nil, errors.New("Please use browser.NewContext()") + } + channel, err := b.channel.Send("newPage") + if err != nil { + return nil, err + } + return fromChannel(channel).(*pageImpl), nil +} + +func (b *browserContextImpl) Cookies(urls ...string) ([]Cookie, error) { + result, err := b.channel.Send("cookies", map[string]interface{}{ + "urls": urls, + }) + if err != nil { + return nil, err + } + cookies := make([]Cookie, len(result.([]interface{}))) + for i, item := range result.([]interface{}) { + cookie := &Cookie{} + remapMapToStruct(item, cookie) + cookies[i] = *cookie + } + return cookies, nil +} + +func (b *browserContextImpl) AddCookies(cookies []OptionalCookie) error { + _, err := b.channel.Send("addCookies", map[string]interface{}{ + "cookies": cookies, + }) + return err +} + +func (b *browserContextImpl) ClearCookies(options ...BrowserContextClearCookiesOptions) error { + params := map[string]interface{}{} + if len(options) == 1 { + if options[0].Domain != nil { + switch t := options[0].Domain.(type) { + case string: + params["domain"] = t + case *string: + params["domain"] = t + case *regexp.Regexp: + pattern, flag := convertRegexp(t) + params["domainRegexSource"] = pattern + params["domainRegexFlags"] = flag + default: + return errors.New("invalid type for domain, expected string or *regexp.Regexp") + } + } + if options[0].Name != nil { + switch t := options[0].Name.(type) { + case string: + params["name"] = t + case *string: + params["name"] = t + case *regexp.Regexp: + pattern, flag := convertRegexp(t) + params["nameRegexSource"] = pattern + params["nameRegexFlags"] = flag + default: + return errors.New("invalid type for name, expected string or *regexp.Regexp") + } + } + if options[0].Path != nil { + switch t := options[0].Path.(type) { + case string: + params["path"] = t + case *string: + params["path"] = t + case *regexp.Regexp: + pattern, flag := convertRegexp(t) + params["pathRegexSource"] = pattern + params["pathRegexFlags"] = flag + default: + return errors.New("invalid type for path, expected string or *regexp.Regexp") + } + } + } + _, err := b.channel.Send("clearCookies", params) + return err +} + +func (b *browserContextImpl) GrantPermissions(permissions []string, options ...BrowserContextGrantPermissionsOptions) error { + _, err := b.channel.Send("grantPermissions", map[string]interface{}{ + "permissions": permissions, + }, options) + return err +} + +func (b *browserContextImpl) ClearPermissions() error { + _, err := b.channel.Send("clearPermissions") + return err +} + +func (b *browserContextImpl) SetGeolocation(geolocation *Geolocation) error { + _, err := b.channel.Send("setGeolocation", map[string]interface{}{ + "geolocation": geolocation, + }) + return err +} + +func (b *browserContextImpl) ResetGeolocation() error { + _, err := b.channel.Send("setGeolocation", map[string]interface{}{}) + return err +} + +func (b *browserContextImpl) SetExtraHTTPHeaders(headers map[string]string) error { + _, err := b.channel.Send("setExtraHTTPHeaders", map[string]interface{}{ + "headers": serializeMapToNameAndValue(headers), + }) + return err +} + +func (b *browserContextImpl) SetOffline(offline bool) error { + _, err := b.channel.Send("setOffline", map[string]interface{}{ + "offline": offline, + }) + return err +} + +func (b *browserContextImpl) AddInitScript(script Script) error { + var source string + if script.Content != nil { + source = *script.Content + } + if script.Path != nil { + content, err := os.ReadFile(*script.Path) + if err != nil { + return err + } + source = string(content) + } + _, err := b.channel.Send("addInitScript", map[string]interface{}{ + "source": source, + }) + return err +} + +func (b *browserContextImpl) ExposeBinding(name string, binding BindingCallFunction, handle ...bool) error { + needsHandle := false + if len(handle) == 1 { + needsHandle = handle[0] + } + for _, page := range b.Pages() { + if _, ok := page.(*pageImpl).bindings.Load(name); ok { + return fmt.Errorf("Function '%s' has been already registered in one of the pages", name) + } + } + if _, ok := b.bindings.Load(name); ok { + return fmt.Errorf("Function '%s' has been already registered", name) + } + _, err := b.channel.Send("exposeBinding", map[string]interface{}{ + "name": name, + "needsHandle": needsHandle, + }) + if err != nil { + return err + } + b.bindings.Store(name, binding) + return err +} + +func (b *browserContextImpl) ExposeFunction(name string, binding ExposedFunction) error { + return b.ExposeBinding(name, func(source *BindingSource, args ...interface{}) interface{} { + return binding(args...) + }) +} + +func (b *browserContextImpl) Route(url interface{}, handler routeHandler, times ...int) error { + b.Lock() + defer b.Unlock() + b.routes = slices.Insert(b.routes, 0, newRouteHandlerEntry(newURLMatcher(url, b.options.BaseURL), handler, times...)) + return b.updateInterceptionPatterns() +} + +func (b *browserContextImpl) Unroute(url interface{}, handlers ...routeHandler) error { + removed, remaining, err := unroute(b.routes, url, handlers...) + if err != nil { + return err + } + return b.unrouteInternal(removed, remaining, UnrouteBehaviorDefault) +} + +func (b *browserContextImpl) unrouteInternal(removed []*routeHandlerEntry, remaining []*routeHandlerEntry, behavior *UnrouteBehavior) error { + b.Lock() + defer b.Unlock() + b.routes = remaining + if err := b.updateInterceptionPatterns(); err != nil { + return err + } + if behavior == nil || behavior == UnrouteBehaviorDefault { + return nil + } + wg := &sync.WaitGroup{} + for _, entry := range removed { + wg.Add(1) + go func(entry *routeHandlerEntry) { + defer wg.Done() + entry.Stop(string(*behavior)) + }(entry) + } + wg.Wait() + return nil +} + +func (b *browserContextImpl) UnrouteAll(options ...BrowserContextUnrouteAllOptions) error { + var behavior *UnrouteBehavior + if len(options) == 1 { + behavior = options[0].Behavior + } + defer b.disposeHarRouters() + return b.unrouteInternal(b.routes, []*routeHandlerEntry{}, behavior) +} + +func (b *browserContextImpl) disposeHarRouters() { + for _, router := range b.harRouters { + router.dispose() + } + b.harRouters = make([]*harRouter, 0) +} + +func (b *browserContextImpl) Request() APIRequestContext { + return b.request +} + +func (b *browserContextImpl) RouteFromHAR(har string, options ...BrowserContextRouteFromHAROptions) error { + opt := BrowserContextRouteFromHAROptions{} + if len(options) == 1 { + opt = options[0] + } + if opt.Update != nil && *opt.Update { + var updateContent *HarContentPolicy + switch opt.UpdateContent { + case RouteFromHarUpdateContentPolicyAttach: + updateContent = HarContentPolicyAttach + case RouteFromHarUpdateContentPolicyEmbed: + updateContent = HarContentPolicyEmbed + } + return b.recordIntoHar(har, browserContextRecordIntoHarOptions{ + URL: opt.URL, + UpdateContent: updateContent, + UpdateMode: opt.UpdateMode, + }) + } + notFound := opt.NotFound + if notFound == nil { + notFound = HarNotFoundAbort + } + router := newHarRouter(b.connection.localUtils, har, *notFound, opt.URL) + b.harRouters = append(b.harRouters, router) + return router.addContextRoute(b) +} + +func (b *browserContextImpl) WaitForEvent(event string, options ...BrowserContextWaitForEventOptions) (interface{}, error) { + return b.waiterForEvent(event, options...).Wait() +} + +func (b *browserContextImpl) waiterForEvent(event string, options ...BrowserContextWaitForEventOptions) *waiter { + timeout := b.timeoutSettings.Timeout() + var predicate interface{} = nil + if len(options) == 1 { + if options[0].Timeout != nil { + timeout = *options[0].Timeout + } + predicate = options[0].Predicate + } + waiter := newWaiter().WithTimeout(timeout) + waiter.RejectOnEvent(b, "close", ErrTargetClosed) + return waiter.WaitForEvent(b, event, predicate) +} + +func (b *browserContextImpl) ExpectConsoleMessage(cb func() error, options ...BrowserContextExpectConsoleMessageOptions) (ConsoleMessage, error) { + var w *waiter + if len(options) == 1 { + w = b.waiterForEvent("console", BrowserContextWaitForEventOptions{ + Predicate: options[0].Predicate, + Timeout: options[0].Timeout, + }) + } else { + w = b.waiterForEvent("console") + } + ret, err := w.RunAndWait(cb) + if err != nil { + return nil, err + } + return ret.(ConsoleMessage), nil +} + +func (b *browserContextImpl) ExpectEvent(event string, cb func() error, options ...BrowserContextExpectEventOptions) (interface{}, error) { + if len(options) == 1 { + return b.waiterForEvent(event, BrowserContextWaitForEventOptions(options[0])).RunAndWait(cb) + } + return b.waiterForEvent(event).RunAndWait(cb) +} + +func (b *browserContextImpl) ExpectPage(cb func() error, options ...BrowserContextExpectPageOptions) (Page, error) { + var w *waiter + if len(options) == 1 { + w = b.waiterForEvent("page", BrowserContextWaitForEventOptions{ + Predicate: options[0].Predicate, + Timeout: options[0].Timeout, + }) + } else { + w = b.waiterForEvent("page") + } + ret, err := w.RunAndWait(cb) + if err != nil { + return nil, err + } + return ret.(Page), nil +} + +func (b *browserContextImpl) Close(options ...BrowserContextCloseOptions) error { + if b.closeWasCalled { + return nil + } + if len(options) == 1 { + b.closeReason = options[0].Reason + } + b.closeWasCalled = true + + _, err := b.channel.connection.WrapAPICall(func() (interface{}, error) { + return nil, b.request.Dispose(APIRequestContextDisposeOptions{ + Reason: b.closeReason, + }) + }, true) + if err != nil { + return err + } + + innerClose := func() (interface{}, error) { + for harId, harMetaData := range b.harRecorders { + overrides := map[string]interface{}{} + if harId != "" { + overrides["harId"] = harId + } + response, err := b.channel.Send("harExport", overrides) + if err != nil { + return nil, err + } + artifact := fromChannel(response).(*artifactImpl) + // Server side will compress artifact if content is attach or if file is .zip. + needCompressed := strings.HasSuffix(strings.ToLower(harMetaData.Path), ".zip") + if !needCompressed && harMetaData.Content == HarContentPolicyAttach { + tmpPath := harMetaData.Path + ".tmp" + if err := artifact.SaveAs(tmpPath); err != nil { + return nil, err + } + err = b.connection.localUtils.HarUnzip(tmpPath, harMetaData.Path) + if err != nil { + return nil, err + } + } else { + if err := artifact.SaveAs(harMetaData.Path); err != nil { + return nil, err + } + } + if err := artifact.Delete(); err != nil { + return nil, err + } + } + return nil, nil + } + + _, err = b.channel.connection.WrapAPICall(innerClose, true) + if err != nil { + return err + } + + _, err = b.channel.Send("close", map[string]interface{}{ + "reason": b.closeReason, + }) + if err != nil { + return err + } + <-b.closed + return err +} + +type browserContextRecordIntoHarOptions struct { + Page Page + URL interface{} + UpdateContent *HarContentPolicy + UpdateMode *HarMode +} + +func (b *browserContextImpl) recordIntoHar(har string, options ...browserContextRecordIntoHarOptions) error { + overrides := map[string]interface{}{} + harOptions := recordHarInputOptions{ + Path: har, + Content: HarContentPolicyAttach, + Mode: HarModeMinimal, + } + if len(options) == 1 { + if options[0].UpdateContent != nil { + harOptions.Content = options[0].UpdateContent + } + if options[0].UpdateMode != nil { + harOptions.Mode = options[0].UpdateMode + } + harOptions.URL = options[0].URL + overrides["options"] = prepareRecordHarOptions(harOptions) + if options[0].Page != nil { + overrides["page"] = options[0].Page.(*pageImpl).channel + } + } + harId, err := b.channel.Send("harStart", overrides) + if err != nil { + return err + } + b.harRecorders[harId.(string)] = harRecordingMetadata{ + Path: har, + Content: harOptions.Content, + } + return nil +} + +func (b *browserContextImpl) StorageState(paths ...string) (*StorageState, error) { + result, err := b.channel.SendReturnAsDict("storageState") + if err != nil { + return nil, err + } + if len(paths) == 1 { + file, err := os.Create(paths[0]) + if err != nil { + return nil, err + } + if err := json.NewEncoder(file).Encode(result); err != nil { + return nil, err + } + if err := file.Close(); err != nil { + return nil, err + } + } + var storageState StorageState + remapMapToStruct(result, &storageState) + return &storageState, nil +} + +func (b *browserContextImpl) onBinding(binding *bindingCallImpl) { + function, ok := b.bindings.Load(binding.initializer["name"].(string)) + if !ok || function == nil { + return + } + go binding.Call(function) +} + +func (b *browserContextImpl) onClose() { + if b.browser != nil { + contexts := make([]BrowserContext, 0) + b.browser.Lock() + for _, context := range b.browser.contexts { + if context != b { + contexts = append(contexts, context) + } + } + b.browser.contexts = contexts + b.browser.Unlock() + } + b.disposeHarRouters() + b.Emit("close", b) +} + +func (b *browserContextImpl) onPage(page Page) { + b.Lock() + b.pages = append(b.pages, page) + b.Unlock() + b.Emit("page", page) + opener, _ := page.Opener() + if opener != nil && !opener.IsClosed() { + opener.Emit("popup", page) + } +} + +func (b *browserContextImpl) onRoute(route *routeImpl) { + b.Lock() + route.context = b + page := route.Request().(*requestImpl).safePage() + routes := make([]*routeHandlerEntry, len(b.routes)) + copy(routes, b.routes) + b.Unlock() + + checkInterceptionIfNeeded := func() { + b.Lock() + defer b.Unlock() + if len(b.routes) == 0 { + _, err := b.connection.WrapAPICall(func() (interface{}, error) { + err := b.updateInterceptionPatterns() + return nil, err + }, true) + if err != nil { + logger.Error("could not update interception patterns", "error", err) + } + } + } + + url := route.Request().URL() + for _, handlerEntry := range routes { + // If the page or the context was closed we stall all requests right away. + if (page != nil && page.closeWasCalled) || b.closeWasCalled { + return + } + if !handlerEntry.Matches(url) { + continue + } + if !slices.ContainsFunc(b.routes, func(entry *routeHandlerEntry) bool { + return entry == handlerEntry + }) { + continue + } + if handlerEntry.WillExceed() { + b.routes = slices.DeleteFunc(b.routes, func(rhe *routeHandlerEntry) bool { + return rhe == handlerEntry + }) + } + handled := handlerEntry.Handle(route) + checkInterceptionIfNeeded() + yes := <-handled + if yes { + return + } + } + // If the page is closed or unrouteAll() was called without waiting and interception disabled, + // the method will throw an error - silence it. + _ = route.internalContinue(true) +} + +func (b *browserContextImpl) updateInterceptionPatterns() error { + patterns := prepareInterceptionPatterns(b.routes) + _, err := b.channel.Send("setNetworkInterceptionPatterns", map[string]interface{}{ + "patterns": patterns, + }) + return err +} + +func (b *browserContextImpl) pause() <-chan error { + ret := make(chan error, 1) + go func() { + _, err := b.channel.Send("pause") + ret <- err + }() + return ret +} + +func (b *browserContextImpl) onBackgroundPage(ev map[string]interface{}) { + b.Lock() + p := fromChannel(ev["page"]).(*pageImpl) + p.browserContext = b + b.backgroundPages = append(b.backgroundPages, p) + b.Unlock() + b.Emit("backgroundpage", p) +} + +func (b *browserContextImpl) onServiceWorker(worker *workerImpl) { + worker.context = b + b.serviceWorkers = append(b.serviceWorkers, worker) + b.Emit("serviceworker", worker) +} + +func (b *browserContextImpl) setOptions(options *BrowserNewContextOptions, tracesDir *string) { + if options == nil { + options = &BrowserNewContextOptions{} + } + b.options = options + if b.options != nil && b.options.RecordHarPath != nil { + b.harRecorders[""] = harRecordingMetadata{ + Path: *b.options.RecordHarPath, + Content: b.options.RecordHarContent, + } + } + if tracesDir != nil { + b.tracing.tracesDir = *tracesDir + } +} + +func (b *browserContextImpl) BackgroundPages() []Page { + b.Lock() + defer b.Unlock() + return b.backgroundPages +} + +func (b *browserContextImpl) ServiceWorkers() []Worker { + b.Lock() + defer b.Unlock() + return b.serviceWorkers +} + +func (b *browserContextImpl) OnBackgroundPage(fn func(Page)) { + b.On("backgroundpage", fn) +} + +func (b *browserContextImpl) OnClose(fn func(BrowserContext)) { + b.On("close", fn) +} + +func (b *browserContextImpl) OnConsole(fn func(ConsoleMessage)) { + b.On("console", fn) +} + +func (b *browserContextImpl) OnDialog(fn func(Dialog)) { + b.On("dialog", fn) +} + +func (b *browserContextImpl) OnPage(fn func(Page)) { + b.On("page", fn) +} + +func (b *browserContextImpl) OnRequest(fn func(Request)) { + b.On("request", fn) +} + +func (b *browserContextImpl) OnRequestFailed(fn func(Request)) { + b.On("requestfailed", fn) +} + +func (b *browserContextImpl) OnRequestFinished(fn func(Request)) { + b.On("requestfinished", fn) +} + +func (b *browserContextImpl) OnResponse(fn func(Response)) { + b.On("response", fn) +} + +func (b *browserContextImpl) OnWebError(fn func(WebError)) { + b.On("weberror", fn) +} + +func (b *browserContextImpl) RouteWebSocket(url interface{}, handler func(WebSocketRoute)) error { + b.Lock() + defer b.Unlock() + b.webSocketRoutes = slices.Insert(b.webSocketRoutes, 0, newWebSocketRouteHandler(newURLMatcher(url, b.options.BaseURL), handler)) + + return b.updateWebSocketInterceptionPatterns() +} + +func (b *browserContextImpl) onWebSocketRoute(wr WebSocketRoute) { + b.Lock() + index := slices.IndexFunc(b.webSocketRoutes, func(r *webSocketRouteHandler) bool { + return r.Matches(wr.URL()) + }) + if index == -1 { + b.Unlock() + _, err := wr.ConnectToServer() + if err != nil { + logger.Error("could not connect to WebSocket server", "error", err) + } + return + } + handler := b.webSocketRoutes[index] + b.Unlock() + handler.Handle(wr) +} + +func (b *browserContextImpl) updateWebSocketInterceptionPatterns() error { + patterns := prepareWebSocketRouteHandlerInterceptionPatterns(b.webSocketRoutes) + _, err := b.channel.Send("setWebSocketInterceptionPatterns", map[string]interface{}{ + "patterns": patterns, + }) + return err +} + +func (b *browserContextImpl) effectiveCloseReason() *string { + b.Lock() + defer b.Unlock() + if b.closeReason != nil { + return b.closeReason + } + if b.browser != nil { + return b.browser.closeReason + } + return nil +} + +func newBrowserContext(parent *channelOwner, objectType string, guid string, initializer map[string]interface{}) *browserContextImpl { + bt := &browserContextImpl{ + timeoutSettings: newTimeoutSettings(nil), + pages: make([]Page, 0), + backgroundPages: make([]Page, 0), + routes: make([]*routeHandlerEntry, 0), + bindings: safe.NewSyncMap[string, BindingCallFunction](), + harRecorders: make(map[string]harRecordingMetadata), + closed: make(chan struct{}, 1), + harRouters: make([]*harRouter, 0), + } + bt.createChannelOwner(bt, parent, objectType, guid, initializer) + if parent.objectType == "Browser" { + bt.browser = fromChannel(parent.channel).(*browserImpl) + bt.browser.contexts = append(bt.browser.contexts, bt) + } + bt.tracing = fromChannel(initializer["tracing"]).(*tracingImpl) + bt.request = fromChannel(initializer["requestContext"]).(*apiRequestContextImpl) + bt.clock = newClock(bt) + bt.channel.On("bindingCall", func(params map[string]interface{}) { + bt.onBinding(fromChannel(params["binding"]).(*bindingCallImpl)) + }) + + bt.channel.On("close", bt.onClose) + bt.channel.On("page", func(payload map[string]interface{}) { + bt.onPage(fromChannel(payload["page"]).(*pageImpl)) + }) + bt.channel.On("route", func(params map[string]interface{}) { + bt.channel.CreateTask(func() { + bt.onRoute(fromChannel(params["route"]).(*routeImpl)) + }) + }) + bt.channel.On("webSocketRoute", func(params map[string]interface{}) { + bt.channel.CreateTask(func() { + bt.onWebSocketRoute(fromChannel(params["webSocketRoute"]).(*webSocketRouteImpl)) + }) + }) + bt.channel.On("backgroundPage", bt.onBackgroundPage) + bt.channel.On("serviceWorker", func(params map[string]interface{}) { + bt.onServiceWorker(fromChannel(params["worker"]).(*workerImpl)) + }) + bt.channel.On("console", func(ev map[string]interface{}) { + message := newConsoleMessage(ev) + bt.Emit("console", message) + if message.page != nil { + message.page.Emit("console", message) + } + }) + bt.channel.On("dialog", func(params map[string]interface{}) { + dialog := fromChannel(params["dialog"]).(*dialogImpl) + go func() { + hasListeners := bt.Emit("dialog", dialog) + page := dialog.page + if page != nil { + if page.Emit("dialog", dialog) { + hasListeners = true + } + } + if !hasListeners { + // Although we do similar handling on the server side, we still need this logic + // on the client side due to a possible race condition between two async calls: + // a) removing "dialog" listener subscription (client->server) + // b) actual "dialog" event (server->client) + if dialog.Type() == "beforeunload" { + _ = dialog.Accept() + } else { + _ = dialog.Dismiss() + } + } + }() + }) + bt.channel.On( + "pageError", func(ev map[string]interface{}) { + pwErr := &Error{} + remapMapToStruct(ev["error"].(map[string]interface{})["error"], pwErr) + err := parseError(*pwErr) + page := fromNullableChannel(ev["page"]) + if page != nil { + bt.Emit("weberror", newWebError(page.(*pageImpl), err)) + page.(*pageImpl).Emit("pageerror", err) + } else { + bt.Emit("weberror", newWebError(nil, err)) + } + }, + ) + bt.channel.On("request", func(ev map[string]interface{}) { + request := fromChannel(ev["request"]).(*requestImpl) + page := fromNullableChannel(ev["page"]) + bt.Emit("request", request) + if page != nil { + page.(*pageImpl).Emit("request", request) + } + }) + bt.channel.On("requestFailed", func(ev map[string]interface{}) { + request := fromChannel(ev["request"]).(*requestImpl) + failureText := ev["failureText"] + if failureText != nil { + request.failureText = failureText.(string) + } + page := fromNullableChannel(ev["page"]) + request.setResponseEndTiming(ev["responseEndTiming"].(float64)) + bt.Emit("requestfailed", request) + if page != nil { + page.(*pageImpl).Emit("requestfailed", request) + } + }) + + bt.channel.On("requestFinished", func(ev map[string]interface{}) { + request := fromChannel(ev["request"]).(*requestImpl) + response := fromNullableChannel(ev["response"]) + page := fromNullableChannel(ev["page"]) + request.setResponseEndTiming(ev["responseEndTiming"].(float64)) + bt.Emit("requestfinished", request) + if page != nil { + page.(*pageImpl).Emit("requestfinished", request) + } + if response != nil { + close(response.(*responseImpl).finished) + } + }) + bt.channel.On("response", func(ev map[string]interface{}) { + response := fromChannel(ev["response"]).(*responseImpl) + page := fromNullableChannel(ev["page"]) + bt.Emit("response", response) + if page != nil { + page.(*pageImpl).Emit("response", response) + } + }) + bt.Once("close", func() { + bt.closed <- struct{}{} + }) + bt.setEventSubscriptionMapping(map[string]string{ + "console": "console", + "dialog": "dialog", + "request": "request", + "response": "response", + "requestfinished": "requestFinished", + "responsefailed": "responseFailed", + }) + return bt +} diff --git a/vendor/github.com/playwright-community/playwright-go/browser_type.go b/vendor/github.com/playwright-community/playwright-go/browser_type.go new file mode 100644 index 0000000..41a8b18 --- /dev/null +++ b/vendor/github.com/playwright-community/playwright-go/browser_type.go @@ -0,0 +1,181 @@ +package playwright + +import ( + "fmt" +) + +type browserTypeImpl struct { + channelOwner + playwright *Playwright +} + +func (b *browserTypeImpl) Name() string { + return b.initializer["name"].(string) +} + +func (b *browserTypeImpl) ExecutablePath() string { + return b.initializer["executablePath"].(string) +} + +func (b *browserTypeImpl) Launch(options ...BrowserTypeLaunchOptions) (Browser, error) { + overrides := map[string]interface{}{} + if len(options) == 1 && options[0].Env != nil { + overrides["env"] = serializeMapToNameAndValue(options[0].Env) + options[0].Env = nil + } + channel, err := b.channel.Send("launch", options, overrides) + if err != nil { + return nil, err + } + browser := fromChannel(channel).(*browserImpl) + b.didLaunchBrowser(browser) + return browser, nil +} + +func (b *browserTypeImpl) LaunchPersistentContext(userDataDir string, options ...BrowserTypeLaunchPersistentContextOptions) (BrowserContext, error) { + overrides := map[string]interface{}{ + "userDataDir": userDataDir, + } + option := &BrowserNewContextOptions{} + var tracesDir *string = nil + if len(options) == 1 { + tracesDir = options[0].TracesDir + err := assignStructFields(option, options[0], true) + if err != nil { + return nil, fmt.Errorf("can not convert options: %w", err) + } + if options[0].AcceptDownloads != nil { + if *options[0].AcceptDownloads { + overrides["acceptDownloads"] = "accept" + } else { + overrides["acceptDownloads"] = "deny" + } + options[0].AcceptDownloads = nil + } + if options[0].ClientCertificates != nil { + certs, err := transformClientCertificate(options[0].ClientCertificates) + if err != nil { + return nil, err + } + overrides["clientCertificates"] = certs + options[0].ClientCertificates = nil + } + if options[0].ExtraHttpHeaders != nil { + overrides["extraHTTPHeaders"] = serializeMapToNameAndValue(options[0].ExtraHttpHeaders) + options[0].ExtraHttpHeaders = nil + } + if options[0].Env != nil { + overrides["env"] = serializeMapToNameAndValue(options[0].Env) + options[0].Env = nil + } + if options[0].NoViewport != nil && *options[0].NoViewport { + overrides["noDefaultViewport"] = true + options[0].NoViewport = nil + } + if options[0].RecordHarPath != nil { + overrides["recordHar"] = prepareRecordHarOptions(recordHarInputOptions{ + Path: *options[0].RecordHarPath, + URL: options[0].RecordHarURLFilter, + Mode: options[0].RecordHarMode, + Content: options[0].RecordHarContent, + OmitContent: options[0].RecordHarOmitContent, + }) + options[0].RecordHarPath = nil + options[0].RecordHarURLFilter = nil + options[0].RecordHarMode = nil + options[0].RecordHarContent = nil + options[0].RecordHarOmitContent = nil + } + } + channel, err := b.channel.Send("launchPersistentContext", options, overrides) + if err != nil { + return nil, err + } + context := fromChannel(channel).(*browserContextImpl) + b.didCreateContext(context, option, tracesDir) + return context, nil +} + +func (b *browserTypeImpl) Connect(wsEndpoint string, options ...BrowserTypeConnectOptions) (Browser, error) { + overrides := map[string]interface{}{ + "wsEndpoint": wsEndpoint, + "headers": map[string]string{ + "x-playwright-browser": b.Name(), + }, + } + if len(options) == 1 { + if options[0].Headers != nil { + for k, v := range options[0].Headers { + overrides["headers"].(map[string]string)[k] = v + } + options[0].Headers = nil + } + } + localUtils := b.connection.LocalUtils() + pipe, err := localUtils.channel.SendReturnAsDict("connect", options, overrides) + if err != nil { + return nil, err + } + jsonPipe := fromChannel(pipe["pipe"]).(*jsonPipe) + connection := newConnection(jsonPipe, localUtils) + + playwright, err := connection.Start() + if err != nil { + return nil, err + } + playwright.setSelectors(b.playwright.Selectors) + browser := fromChannel(playwright.initializer["preLaunchedBrowser"]).(*browserImpl) + browser.shouldCloseConnectionOnClose = true + pipeClosed := func() { + for _, context := range browser.Contexts() { + pages := context.Pages() + for _, page := range pages { + page.(*pageImpl).onClose() + } + context.(*browserContextImpl).onClose() + } + browser.onClose() + connection.cleanup() + } + jsonPipe.On("closed", pipeClosed) + + b.didLaunchBrowser(browser) + return browser, nil +} + +func (b *browserTypeImpl) ConnectOverCDP(endpointURL string, options ...BrowserTypeConnectOverCDPOptions) (Browser, error) { + overrides := map[string]interface{}{ + "endpointURL": endpointURL, + } + if len(options) == 1 { + if options[0].Headers != nil { + overrides["headers"] = serializeMapToNameAndValue(options[0].Headers) + options[0].Headers = nil + } + } + response, err := b.channel.SendReturnAsDict("connectOverCDP", options, overrides) + if err != nil { + return nil, err + } + browser := fromChannel(response["browser"]).(*browserImpl) + b.didLaunchBrowser(browser) + if defaultContext, ok := response["defaultContext"]; ok { + context := fromChannel(defaultContext).(*browserContextImpl) + b.didCreateContext(context, nil, nil) + } + return browser, nil +} + +func (b *browserTypeImpl) didCreateContext(context *browserContextImpl, contextOptions *BrowserNewContextOptions, tracesDir *string) { + context.setOptions(contextOptions, tracesDir) +} + +func (b *browserTypeImpl) didLaunchBrowser(browser *browserImpl) { + browser.browserType = b +} + +func newBrowserType(parent *channelOwner, objectType string, guid string, initializer map[string]interface{}) *browserTypeImpl { + bt := &browserTypeImpl{} + bt.createChannelOwner(bt, parent, objectType, guid, initializer) + return bt +} diff --git a/vendor/github.com/playwright-community/playwright-go/cdp_session.go b/vendor/github.com/playwright-community/playwright-go/cdp_session.go new file mode 100644 index 0000000..e9bba82 --- /dev/null +++ b/vendor/github.com/playwright-community/playwright-go/cdp_session.go @@ -0,0 +1,38 @@ +package playwright + +type cdpSessionImpl struct { + channelOwner +} + +func (c *cdpSessionImpl) Detach() error { + _, err := c.channel.Send("detach") + return err +} + +func (c *cdpSessionImpl) Send(method string, params map[string]interface{}) (interface{}, error) { + result, err := c.channel.Send("send", map[string]interface{}{ + "method": method, + "params": params, + }) + if err != nil { + return nil, err + } + + return result, err +} + +func (c *cdpSessionImpl) onEvent(params map[string]interface{}) { + c.Emit(params["method"].(string), params["params"]) +} + +func newCDPSession(parent *channelOwner, objectType string, guid string, initializer map[string]interface{}) *cdpSessionImpl { + bt := &cdpSessionImpl{} + + bt.createChannelOwner(bt, parent, objectType, guid, initializer) + + bt.channel.On("event", func(params map[string]interface{}) { + bt.onEvent(params) + }) + + return bt +} diff --git a/vendor/github.com/playwright-community/playwright-go/channel.go b/vendor/github.com/playwright-community/playwright-go/channel.go new file mode 100644 index 0000000..b0bded4 --- /dev/null +++ b/vendor/github.com/playwright-community/playwright-go/channel.go @@ -0,0 +1,92 @@ +package playwright + +import ( + "encoding/json" + "fmt" +) + +type channel struct { + eventEmitter + guid string + connection *connection + owner *channelOwner // to avoid type conversion + object interface{} // retain type info (for fromChannel needed) +} + +func (c *channel) MarshalJSON() ([]byte, error) { + return json.Marshal(map[string]string{ + "guid": c.guid, + }) +} + +// for catch errors of route handlers etc. +func (c *channel) CreateTask(fn func()) { + go func() { + defer func() { + if e := recover(); e != nil { + err, ok := e.(error) + if ok { + c.connection.err.Set(err) + } else { + c.connection.err.Set(fmt.Errorf("%v", e)) + } + } + }() + fn() + }() +} + +func (c *channel) Send(method string, options ...interface{}) (interface{}, error) { + return c.connection.WrapAPICall(func() (interface{}, error) { + return c.innerSend(method, options...).GetResultValue() + }, c.owner.isInternalType) +} + +func (c *channel) SendReturnAsDict(method string, options ...interface{}) (map[string]interface{}, error) { + ret, err := c.connection.WrapAPICall(func() (interface{}, error) { + return c.innerSend(method, options...).GetResult() + }, c.owner.isInternalType) + return ret.(map[string]interface{}), err +} + +func (c *channel) innerSend(method string, options ...interface{}) *protocolCallback { + if err := c.connection.err.Get(); err != nil { + c.connection.err.Set(nil) + pc := newProtocolCallback(false, c.connection.abort) + pc.SetError(err) + return pc + } + params := transformOptions(options...) + return c.connection.sendMessageToServer(c.owner, method, params, false) +} + +// SendNoReply ignores return value and errors +// almost equivalent to `send(...).catch(() => {})` +func (c *channel) SendNoReply(method string, options ...interface{}) { + c.innerSendNoReply(method, c.owner.isInternalType, options...) +} + +func (c *channel) SendNoReplyInternal(method string, options ...interface{}) { + c.innerSendNoReply(method, true, options...) +} + +func (c *channel) innerSendNoReply(method string, isInternal bool, options ...interface{}) { + params := transformOptions(options...) + _, err := c.connection.WrapAPICall(func() (interface{}, error) { + return c.connection.sendMessageToServer(c.owner, method, params, true).GetResult() + }, isInternal) + if err != nil { + // ignore error actively, log only for debug + logger.Error("SendNoReply failed", "error", err) + } +} + +func newChannel(owner *channelOwner, object interface{}) *channel { + channel := &channel{ + connection: owner.connection, + guid: owner.guid, + owner: owner, + object: object, + } + return channel +} diff --git a/vendor/github.com/playwright-community/playwright-go/channel_owner.go b/vendor/github.com/playwright-community/playwright-go/channel_owner.go new file mode 100644 index 0000000..5159eb2 --- /dev/null +++ b/vendor/github.com/playwright-community/playwright-go/channel_owner.go @@ -0,0 +1,122 @@ +package playwright + +import ( + "sync" +) + +type channelOwner struct { + sync.RWMutex + eventEmitter + objectType string + guid string + channel *channel + objects map[string]*channelOwner + eventToSubscriptionMapping map[string]string + connection *connection + initializer map[string]interface{} + parent *channelOwner + wasCollected bool + isInternalType bool +} + +func (c *channelOwner) dispose(reason ...string) { + // Clean up from parent and connection. + if c.parent != nil { + delete(c.parent.objects, c.guid) + } + c.connection.objects.Delete(c.guid) + if len(reason) > 0 { + c.wasCollected = reason[0] == "gc" + } + + // Dispose all children. + for _, object := range c.objects { + object.dispose(reason...) + } + c.objects = make(map[string]*channelOwner) +} + +func (c *channelOwner) adopt(child *channelOwner) { + delete(child.parent.objects, child.guid) + c.objects[child.guid] = child + child.parent = c +} + +func (c *channelOwner) setEventSubscriptionMapping(mapping map[string]string) { + c.eventToSubscriptionMapping = mapping +} + +func (c *channelOwner) updateSubscription(event string, enabled bool) { + protocolEvent, ok := c.eventToSubscriptionMapping[event] + if ok { + c.channel.SendNoReplyInternal("updateSubscription", map[string]interface{}{ + "event": protocolEvent, + "enabled": enabled, + }) + } +} + +func (c *channelOwner) Once(name string, handler interface{}) { + c.addEvent(name, handler, true) +} + +func (c *channelOwner) On(name string, handler interface{}) { + c.addEvent(name, handler, false) +} + +func (c *channelOwner) addEvent(name string, handler interface{}, once bool) { + if c.ListenerCount(name) == 0 { + c.updateSubscription(name, true) + } + c.eventEmitter.addEvent(name, handler, once) +} + +func (c *channelOwner) RemoveListener(name string, handler interface{}) { + c.eventEmitter.RemoveListener(name, handler) + if c.ListenerCount(name) == 0 { + c.updateSubscription(name, false) + } +} + +func (c *channelOwner) createChannelOwner(self interface{}, parent *channelOwner, objectType string, guid string, initializer map[string]interface{}) { + c.objectType = objectType + c.guid = guid + c.wasCollected = false + c.parent = parent + c.objects = make(map[string]*channelOwner) + c.initializer = initializer + if c.parent != nil { + c.connection = parent.connection + c.parent.objects[guid] = c + } + if c.connection != nil { + c.connection.objects.Store(guid, c) + } + c.channel = newChannel(c, self) + c.eventToSubscriptionMapping = map[string]string{} +} + +func (c *channelOwner) markAsInternalType() { + c.isInternalType = true +} + +type rootChannelOwner struct { + channelOwner +} + +func (r *rootChannelOwner) initialize() (*Playwright, error) { + ret, err := r.channel.SendReturnAsDict("initialize", map[string]interface{}{ + "sdkLanguage": "javascript", + }) + if err != nil { + return nil, err + } + return fromChannel(ret["playwright"]).(*Playwright), nil +} + +func newRootChannelOwner(connection *connection) *rootChannelOwner { + c := &rootChannelOwner{} + c.connection = connection + c.createChannelOwner(c, nil, "Root", "", make(map[string]interface{})) + return c +} diff --git a/vendor/github.com/playwright-community/playwright-go/clock.go b/vendor/github.com/playwright-community/playwright-go/clock.go new file mode 100644 index 0000000..8bab037 --- /dev/null +++ b/vendor/github.com/playwright-community/playwright-go/clock.go @@ -0,0 +1,111 @@ +package playwright + +import ( + "errors" + "time" +) + +type clockImpl struct { + browserCtx *browserContextImpl +} + +func newClock(bCtx *browserContextImpl) Clock { + return &clockImpl{ + browserCtx: bCtx, + } +} + +func (c *clockImpl) FastForward(ticks interface{}) error { + params, err := parseTicks(ticks) + if err != nil { + return err + } + + _, err = c.browserCtx.channel.Send("clockFastForward", params) + return err +} + +func (c *clockImpl) Install(options ...ClockInstallOptions) (err error) { + params := map[string]any{} + if len(options) == 1 { + if options[0].Time != nil { + params, err = parseTime(options[0].Time) + if err != nil { + return err + } + } + } + + _, err = c.browserCtx.channel.Send("clockInstall", params) + + return err +} + +func (c *clockImpl) PauseAt(time interface{}) error { + params, err := parseTime(time) + if err != nil { + return err + } + + _, err = c.browserCtx.channel.Send("clockPauseAt", params) + return err +} + +func (c *clockImpl) Resume() error { + _, err := c.browserCtx.channel.Send("clockResume") + return err +} + +func (c *clockImpl) RunFor(ticks interface{}) error { + params, err := parseTicks(ticks) + if err != nil { + return err + } + + _, err = c.browserCtx.channel.Send("clockRunFor", params) + return err +} + +func (c *clockImpl) SetFixedTime(time interface{}) error { + params, err := parseTime(time) + if err != nil { + return err + } + + _, err = c.browserCtx.channel.Send("clockSetFixedTime", params) + return err +} + +func (c *clockImpl) SetSystemTime(time interface{}) error { + params, err := parseTime(time) + if err != nil { + return err + } + + _, err = c.browserCtx.channel.Send("clockSetSystemTime", params) + return err +} + +func parseTime(t interface{}) (map[string]any, error) { + switch v := t.(type) { + case int, int64: + return map[string]any{"timeNumber": v}, nil + case string: + return map[string]any{"timeString": v}, nil + case time.Time: + return map[string]any{"timeNumber": v.UnixMilli()}, nil + default: + return nil, errors.New("time should be one of: int, int64, string, time.Time") + } +} + +func parseTicks(ticks interface{}) (map[string]any, error) { + switch v := ticks.(type) { + case int, int64: + return map[string]any{"ticksNumber": v}, nil + case string: + return map[string]any{"ticksString": v}, nil + default: + return nil, errors.New("ticks should be one of: int, int64, string") + } +} diff --git a/vendor/github.com/playwright-community/playwright-go/connection.go b/vendor/github.com/playwright-community/playwright-go/connection.go new file mode 100644 index 0000000..ba1e365 --- /dev/null +++ b/vendor/github.com/playwright-community/playwright-go/connection.go @@ -0,0 +1,401 @@ +package playwright + +import ( + "errors" + "fmt" + "reflect" + "regexp" + "strconv" + "strings" + "sync" + "sync/atomic" + "time" + + "github.com/go-stack/stack" + "github.com/playwright-community/playwright-go/internal/safe" +) + +var ( + pkgSourcePathPattern = regexp.MustCompile(`.+[\\/]playwright-go[\\/][^\\/]+\.go`) + apiNameTransform = regexp.MustCompile(`(?U)\(\*(.+)(Impl)?\)`) +) + +type connection struct { + transport transport + apiZone sync.Map + objects *safe.SyncMap[string, *channelOwner] + lastID atomic.Uint32 + rootObject *rootChannelOwner + callbacks *safe.SyncMap[uint32, *protocolCallback] + afterClose func() + onClose func() error + isRemote bool + localUtils *localUtilsImpl + tracingCount atomic.Int32 + abort chan struct{} + abortOnce sync.Once + err *safeValue[error] // for event listener error + closedError *safeValue[error] +} + +func (c *connection) Start() (*Playwright, error) { + go func() { + for { + msg, err := c.transport.Poll() + if err != nil { + _ = c.transport.Close() + c.cleanup(err) + return + } + c.Dispatch(msg) + } + }() + + c.onClose = func() error { + if err := c.transport.Close(); err != nil { + return err + } + return nil + } + + return c.rootObject.initialize() +} + +func (c *connection) Stop() error { + if err := c.onClose(); err != nil { + return err + } + c.cleanup() + return nil +} + +func (c *connection) cleanup(cause ...error) { + if len(cause) > 0 { + c.closedError.Set(fmt.Errorf("%w: %w", ErrTargetClosed, cause[0])) + } else { + c.closedError.Set(ErrTargetClosed) + } + if c.afterClose != nil { + c.afterClose() + } + c.abortOnce.Do(func() { + select { + case <-c.abort: + default: + close(c.abort) + } + }) +} + +func (c *connection) Dispatch(msg *message) { + if c.closedError.Get() != nil { + return + } + method := msg.Method + if msg.ID != 0 { + cb, _ := c.callbacks.LoadAndDelete(uint32(msg.ID)) + if cb.noReply { + return + } + if msg.Error != nil { + cb.SetError(parseError(msg.Error.Error)) + } else { + cb.SetResult(c.replaceGuidsWithChannels(msg.Result).(map[string]interface{})) + } + return + } + object, _ := c.objects.Load(msg.GUID) + if method == "__create__" { + c.createRemoteObject( + object, msg.Params["type"].(string), msg.Params["guid"].(string), msg.Params["initializer"], + ) + return + } + if object == nil { + return + } + if method == "__adopt__" { + child, ok := c.objects.Load(msg.Params["guid"].(string)) + if !ok { + return + } + object.adopt(child) + return + } + if method == "__dispose__" { + reason, ok := msg.Params["reason"] + if ok { + object.dispose(reason.(string)) + } else { + object.dispose() + } + return + } + if object.objectType == "JsonPipe" { + object.channel.Emit(method, msg.Params) + } else { + object.channel.Emit(method, c.replaceGuidsWithChannels(msg.Params)) + } +} + +func (c *connection) LocalUtils() *localUtilsImpl { + return c.localUtils +} + +func (c *connection) createRemoteObject(parent *channelOwner, objectType string, guid string, initializer interface{}) interface{} { + initializer = c.replaceGuidsWithChannels(initializer) + result := createObjectFactory(parent, objectType, guid, initializer.(map[string]interface{})) + return result +} + +func (c *connection) WrapAPICall(cb func() (interface{}, error), isInternal bool) (interface{}, error) { + if _, ok := c.apiZone.Load("apiZone"); ok { + return cb() + } + c.apiZone.Store("apiZone", serializeCallStack(isInternal)) + return cb() +} + +func (c *connection) replaceGuidsWithChannels(payload interface{}) interface{} { + if payload == nil { + return nil + } + v := reflect.ValueOf(payload) + if v.Kind() == reflect.Slice { + listV := payload.([]interface{}) + for i := 0; i < len(listV); i++ { + listV[i] = c.replaceGuidsWithChannels(listV[i]) + } + return listV + } + if v.Kind() == reflect.Map { + mapV := payload.(map[string]interface{}) + if guid, hasGUID := mapV["guid"]; hasGUID { + if channelOwner, ok := c.objects.Load(guid.(string)); ok { + return channelOwner.channel + } + } + for key := range mapV { + mapV[key] = c.replaceGuidsWithChannels(mapV[key]) + } + return mapV + } + return payload +} + +func (c *connection) sendMessageToServer(object *channelOwner, method string, params interface{}, noReply bool) (cb *protocolCallback) { + cb = newProtocolCallback(noReply, c.abort) + + if err := c.closedError.Get(); err != nil { + cb.SetError(err) + return + } + if object.wasCollected { + cb.SetError(errors.New("The object has been collected to prevent unbounded heap growth.")) + return + } + + id := c.lastID.Add(1) + c.callbacks.Store(id, cb) + var ( + metadata = make(map[string]interface{}, 0) + stack = make([]map[string]interface{}, 0) + ) + apiZone, ok := c.apiZone.LoadAndDelete("apiZone") + if ok { + for k, v := range apiZone.(parsedStackTrace).metadata { + metadata[k] = v + } + stack = append(stack, apiZone.(parsedStackTrace).frames...) + } + metadata["wallTime"] = time.Now().UnixMilli() + message := map[string]interface{}{ + "id": id, + "guid": object.guid, + "method": method, + "params": params, // channel.MarshalJSON will replace channel with guid + "metadata": metadata, + } + if c.tracingCount.Load() > 0 && len(stack) > 0 && object.guid != "localUtils" { + c.LocalUtils().AddStackToTracingNoReply(id, stack) + } + + if err := c.transport.Send(message); err != nil { + cb.SetError(fmt.Errorf("could not send message: %w", err)) + return + } + + return +} + +func (c *connection) setInTracing(isTracing bool) { + if isTracing { + c.tracingCount.Add(1) + } else { + c.tracingCount.Add(-1) + } +} + +type parsedStackTrace struct { + frames []map[string]interface{} + metadata map[string]interface{} +} + +func serializeCallStack(isInternal bool) parsedStackTrace { + st := stack.Trace().TrimRuntime() + if len(st) == 0 { // https://github.com/go-stack/stack/issues/27 + st = stack.Trace() + } + + lastInternalIndex := 0 + for i, s := range st { + if pkgSourcePathPattern.MatchString(s.Frame().File) { + lastInternalIndex = i + } + } + apiName := "" + if !isInternal { + apiName = fmt.Sprintf("%n", st[lastInternalIndex]) + } + st = st.TrimBelow(st[lastInternalIndex]) + + callStack := make([]map[string]interface{}, 0) + for i, s := range st { + if i == 0 { + continue + } + callStack = append(callStack, map[string]interface{}{ + "file": s.Frame().File, + "line": s.Frame().Line, + "column": 0, + "function": s.Frame().Function, + }) + } + metadata := make(map[string]interface{}) + if len(st) > 1 { + metadata["location"] = serializeCallLocation(st[1]) + } + apiName = apiNameTransform.ReplaceAllString(apiName, "$1") + if len(apiName) > 1 { + apiName = strings.ToUpper(apiName[:1]) + apiName[1:] + } + metadata["apiName"] = apiName + metadata["isInternal"] = isInternal + return parsedStackTrace{ + metadata: metadata, + frames: callStack, + } +} + +func serializeCallLocation(caller stack.Call) map[string]interface{} { + line, _ := strconv.Atoi(fmt.Sprintf("%d", caller)) + return map[string]interface{}{ + "file": fmt.Sprintf("%s", caller), + "line": line, + } +} + +func newConnection(transport transport, localUtils ...*localUtilsImpl) *connection { + connection := &connection{ + abort: make(chan struct{}, 1), + callbacks: safe.NewSyncMap[uint32, *protocolCallback](), + objects: safe.NewSyncMap[string, *channelOwner](), + transport: transport, + isRemote: false, + err: &safeValue[error]{}, + closedError: &safeValue[error]{}, + } + if len(localUtils) > 0 { + connection.localUtils = localUtils[0] + connection.isRemote = true + } + connection.rootObject = newRootChannelOwner(connection) + return connection +} + +func fromChannel(v interface{}) interface{} { + return v.(*channel).object +} + +func fromNullableChannel(v interface{}) interface{} { + if v == nil { + return nil + } + return fromChannel(v) +} + +type protocolCallback struct { + done chan struct{} + noReply bool + abort <-chan struct{} + once sync.Once + value map[string]interface{} + err error +} + +func (pc *protocolCallback) setResultOnce(result map[string]interface{}, err error) { + pc.once.Do(func() { + pc.value = result + pc.err = err + close(pc.done) + }) +} + +func (pc *protocolCallback) waitResult() { + if pc.noReply { + return + } + select { + case <-pc.done: // wait for result + return + case <-pc.abort: + select { + case <-pc.done: + return + default: + pc.err = errors.New("Connection closed") + return + } + } +} + +func (pc *protocolCallback) SetError(err error) { + pc.setResultOnce(nil, err) +} + +func (pc *protocolCallback) SetResult(result map[string]interface{}) { + pc.setResultOnce(result, nil) +} + +func (pc *protocolCallback) GetResult() (map[string]interface{}, error) { + pc.waitResult() + return pc.value, pc.err +} + +// GetResultValue returns value if the map has only one element +func (pc *protocolCallback) GetResultValue() (interface{}, error) { + pc.waitResult() + if len(pc.value) == 0 { // empty map treated as nil + return nil, pc.err + } + if len(pc.value) == 1 { + for key := range pc.value { + return pc.value[key], pc.err + } + } + + return pc.value, pc.err +} + +func newProtocolCallback(noReply bool, abort <-chan struct{}) *protocolCallback { + if noReply { + return &protocolCallback{ + noReply: true, + abort: abort, + } + } + return &protocolCallback{ + done: make(chan struct{}, 1), + abort: abort, + } +} diff --git a/vendor/github.com/playwright-community/playwright-go/console_message.go b/vendor/github.com/playwright-community/playwright-go/console_message.go new file mode 100644 index 0000000..4baf3f1 --- /dev/null +++ b/vendor/github.com/playwright-community/playwright-go/console_message.go @@ -0,0 +1,47 @@ +package playwright + +type consoleMessageImpl struct { + event map[string]interface{} + page Page +} + +func (c *consoleMessageImpl) Type() string { + return c.event["type"].(string) +} + +func (c *consoleMessageImpl) Text() string { + return c.event["text"].(string) +} + +func (c *consoleMessageImpl) String() string { + return c.Text() +} + +func (c *consoleMessageImpl) Args() []JSHandle { + args := c.event["args"].([]interface{}) + out := []JSHandle{} + for idx := range args { + out = append(out, fromChannel(args[idx]).(*jsHandleImpl)) + } + return out +} + +func (c *consoleMessageImpl) Location() *ConsoleMessageLocation { + location := &ConsoleMessageLocation{} + remapMapToStruct(c.event["location"], location) + return location +} + +func (c *consoleMessageImpl) Page() Page { + return c.page +} + +func newConsoleMessage(event map[string]interface{}) *consoleMessageImpl { + bt := &consoleMessageImpl{} + bt.event = event + page := fromNullableChannel(event["page"]) + if page != nil { + bt.page = page.(*pageImpl) + } + return bt +} diff --git a/vendor/github.com/playwright-community/playwright-go/dialog.go b/vendor/github.com/playwright-community/playwright-go/dialog.go new file mode 100644 index 0000000..8d13234 --- /dev/null +++ b/vendor/github.com/playwright-community/playwright-go/dialog.go @@ -0,0 +1,48 @@ +package playwright + +type dialogImpl struct { + channelOwner + page Page +} + +func (d *dialogImpl) Type() string { + return d.initializer["type"].(string) +} + +func (d *dialogImpl) Message() string { + return d.initializer["message"].(string) +} + +func (d *dialogImpl) DefaultValue() string { + return d.initializer["defaultValue"].(string) +} + +func (d *dialogImpl) Accept(promptTextInput ...string) error { + var promptText *string + if len(promptTextInput) == 1 { + promptText = &promptTextInput[0] + } + _, err := d.channel.Send("accept", map[string]interface{}{ + "promptText": promptText, + }) + return err +} + +func (d *dialogImpl) Dismiss() error { + _, err := d.channel.Send("dismiss") + return err +} + +func (d *dialogImpl) Page() Page { + return d.page +} + +func newDialog(parent *channelOwner, objectType string, guid string, initializer map[string]interface{}) *dialogImpl { + bt := &dialogImpl{} + bt.createChannelOwner(bt, parent, objectType, guid, initializer) + page := fromNullableChannel(initializer["page"]) + if page != nil { + bt.page = page.(*pageImpl) + } + return bt +} diff --git a/vendor/github.com/playwright-community/playwright-go/download.go b/vendor/github.com/playwright-community/playwright-go/download.go new file mode 100644 index 0000000..b9d2024 --- /dev/null +++ b/vendor/github.com/playwright-community/playwright-go/download.go @@ -0,0 +1,56 @@ +package playwright + +type downloadImpl struct { + page *pageImpl + url string + suggestedFilename string + artifact *artifactImpl +} + +func (d *downloadImpl) String() string { + return d.SuggestedFilename() +} + +func (d *downloadImpl) Page() Page { + return d.page +} + +func (d *downloadImpl) URL() string { + return d.url +} + +func (d *downloadImpl) SuggestedFilename() string { + return d.suggestedFilename +} + +func (d *downloadImpl) Delete() error { + err := d.artifact.Delete() + return err +} + +func (d *downloadImpl) Failure() error { + return d.artifact.Failure() +} + +func (d *downloadImpl) Path() (string, error) { + path, err := d.artifact.PathAfterFinished() + return path, err +} + +func (d *downloadImpl) SaveAs(path string) error { + err := d.artifact.SaveAs(path) + return err +} + +func (d *downloadImpl) Cancel() error { + return d.artifact.Cancel() +} + +func newDownload(page *pageImpl, url string, suggestedFilename string, artifact *artifactImpl) *downloadImpl { + return &downloadImpl{ + page: page, + url: url, + suggestedFilename: suggestedFilename, + artifact: artifact, + } +} diff --git a/vendor/github.com/playwright-community/playwright-go/element_handle.go b/vendor/github.com/playwright-community/playwright-go/element_handle.go new file mode 100644 index 0000000..62c41ba --- /dev/null +++ b/vendor/github.com/playwright-community/playwright-go/element_handle.go @@ -0,0 +1,403 @@ +package playwright + +import ( + "encoding/base64" + "errors" + "fmt" + "os" +) + +type elementHandleImpl struct { + jsHandleImpl +} + +func (e *elementHandleImpl) AsElement() ElementHandle { + return e +} + +func (e *elementHandleImpl) OwnerFrame() (Frame, error) { + channel, err := e.channel.Send("ownerFrame") + if err != nil { + return nil, err + } + channelOwner := fromNullableChannel(channel) + if channelOwner == nil { + return nil, nil + } + return channelOwner.(*frameImpl), nil +} + +func (e *elementHandleImpl) ContentFrame() (Frame, error) { + channel, err := e.channel.Send("contentFrame") + if err != nil { + return nil, err + } + channelOwner := fromNullableChannel(channel) + if channelOwner == nil { + return nil, nil + } + return channelOwner.(*frameImpl), nil +} + +func (e *elementHandleImpl) GetAttribute(name string) (string, error) { + attribute, err := e.channel.Send("getAttribute", map[string]interface{}{ + "name": name, + }) + if attribute == nil { + return "", err + } + return attribute.(string), err +} + +func (e *elementHandleImpl) TextContent() (string, error) { + textContent, err := e.channel.Send("textContent") + if textContent == nil { + return "", err + } + return textContent.(string), err +} + +func (e *elementHandleImpl) InnerText() (string, error) { + innerText, err := e.channel.Send("innerText") + if innerText == nil { + return "", err + } + return innerText.(string), err +} + +func (e *elementHandleImpl) InnerHTML() (string, error) { + innerHTML, err := e.channel.Send("innerHTML") + if innerHTML == nil { + return "", err + } + return innerHTML.(string), err +} + +func (e *elementHandleImpl) DispatchEvent(typ string, initObjects ...interface{}) error { + var initObject interface{} + if len(initObjects) == 1 { + initObject = initObjects[0] + } + _, err := e.channel.Send("dispatchEvent", map[string]interface{}{ + "type": typ, + "eventInit": serializeArgument(initObject), + }) + return err +} + +func (e *elementHandleImpl) Hover(options ...ElementHandleHoverOptions) error { + _, err := e.channel.Send("hover", options) + return err +} + +func (e *elementHandleImpl) Click(options ...ElementHandleClickOptions) error { + _, err := e.channel.Send("click", options) + return err +} + +func (e *elementHandleImpl) Dblclick(options ...ElementHandleDblclickOptions) error { + _, err := e.channel.Send("dblclick", options) + return err +} + +func (e *elementHandleImpl) QuerySelector(selector string) (ElementHandle, error) { + channel, err := e.channel.Send("querySelector", map[string]interface{}{ + "selector": selector, + }) + if err != nil { + return nil, err + } + if channel == nil { + return nil, nil + } + return fromChannel(channel).(*elementHandleImpl), nil +} + +func (e *elementHandleImpl) QuerySelectorAll(selector string) ([]ElementHandle, error) { + channels, err := e.channel.Send("querySelectorAll", map[string]interface{}{ + "selector": selector, + }) + if err != nil { + return nil, err + } + elements := make([]ElementHandle, 0) + for _, channel := range channels.([]interface{}) { + elements = append(elements, fromChannel(channel).(*elementHandleImpl)) + } + return elements, nil +} + +func (e *elementHandleImpl) EvalOnSelector(selector string, expression string, options ...interface{}) (interface{}, error) { + var arg interface{} + if len(options) == 1 { + arg = options[0] + } + result, err := e.channel.Send("evalOnSelector", map[string]interface{}{ + "selector": selector, + "expression": expression, + "arg": serializeArgument(arg), + }) + if err != nil { + return nil, err + } + return parseResult(result), nil +} + +func (e *elementHandleImpl) EvalOnSelectorAll(selector string, expression string, options ...interface{}) (interface{}, error) { + var arg interface{} + if len(options) == 1 { + arg = options[0] + } + result, err := e.channel.Send("evalOnSelectorAll", map[string]interface{}{ + "selector": selector, + "expression": expression, + "arg": serializeArgument(arg), + }) + if err != nil { + return nil, err + } + return parseResult(result), nil +} + +func (e *elementHandleImpl) ScrollIntoViewIfNeeded(options ...ElementHandleScrollIntoViewIfNeededOptions) error { + _, err := e.channel.Send("scrollIntoViewIfNeeded", options) + if err != nil { + return err + } + return err +} + +func (e *elementHandleImpl) SetInputFiles(files interface{}, options ...ElementHandleSetInputFilesOptions) error { + frame, err := e.OwnerFrame() + if err != nil { + return err + } + if frame == nil { + return errors.New("Cannot set input files to detached element") + } + + params, err := convertInputFiles(files, frame.(*frameImpl).page.browserContext) + if err != nil { + return err + } + _, err = e.channel.Send("setInputFiles", params, options) + return err +} + +func (e *elementHandleImpl) BoundingBox() (*Rect, error) { + boundingBox, err := e.channel.Send("boundingBox") + if err != nil { + return nil, err + } + + if boundingBox == nil { + return nil, nil + } + + out := &Rect{} + remapMapToStruct(boundingBox, out) + return out, nil +} + +func (e *elementHandleImpl) Check(options ...ElementHandleCheckOptions) error { + _, err := e.channel.Send("check", options) + return err +} + +func (e *elementHandleImpl) Uncheck(options ...ElementHandleUncheckOptions) error { + _, err := e.channel.Send("uncheck", options) + return err +} + +func (e *elementHandleImpl) Press(key string, options ...ElementHandlePressOptions) error { + _, err := e.channel.Send("press", map[string]interface{}{ + "key": key, + }, options) + return err +} + +func (e *elementHandleImpl) Fill(value string, options ...ElementHandleFillOptions) error { + _, err := e.channel.Send("fill", map[string]interface{}{ + "value": value, + }, options) + return err +} + +func (e *elementHandleImpl) Type(value string, options ...ElementHandleTypeOptions) error { + _, err := e.channel.Send("type", map[string]interface{}{ + "text": value, + }, options) + return err +} + +func (e *elementHandleImpl) Focus() error { + _, err := e.channel.Send("focus") + return err +} + +func (e *elementHandleImpl) SelectText(options ...ElementHandleSelectTextOptions) error { + _, err := e.channel.Send("selectText", options) + return err +} + +func (e *elementHandleImpl) Screenshot(options ...ElementHandleScreenshotOptions) ([]byte, error) { + var path *string + overrides := map[string]interface{}{} + if len(options) == 1 { + path = options[0].Path + options[0].Path = nil + if options[0].Mask != nil { + masks := make([]map[string]interface{}, 0) + for _, m := range options[0].Mask { + if m.Err() != nil { // ErrLocatorNotSameFrame + return nil, m.Err() + } + l, ok := m.(*locatorImpl) + if ok { + masks = append(masks, map[string]interface{}{ + "selector": l.selector, + "frame": l.frame.channel, + }) + } + } + overrides["mask"] = masks + options[0].Mask = nil + } + } + data, err := e.channel.Send("screenshot", options, overrides) + if err != nil { + return nil, err + } + image, err := base64.StdEncoding.DecodeString(data.(string)) + if err != nil { + return nil, fmt.Errorf("could not decode base64 :%w", err) + } + if path != nil { + if err := os.WriteFile(*path, image, 0o644); err != nil { + return nil, err + } + } + return image, nil +} + +func (e *elementHandleImpl) Tap(options ...ElementHandleTapOptions) error { + _, err := e.channel.Send("tap", options) + return err +} + +func (e *elementHandleImpl) SelectOption(values SelectOptionValues, options ...ElementHandleSelectOptionOptions) ([]string, error) { + opts := convertSelectOptionSet(values) + selected, err := e.channel.Send("selectOption", opts, options) + if err != nil { + return nil, err + } + + return transformToStringList(selected), nil +} + +func (e *elementHandleImpl) IsChecked() (bool, error) { + checked, err := e.channel.Send("isChecked") + if err != nil { + return false, err + } + return checked.(bool), nil +} + +func (e *elementHandleImpl) IsDisabled() (bool, error) { + disabled, err := e.channel.Send("isDisabled") + if err != nil { + return false, err + } + return disabled.(bool), nil +} + +func (e *elementHandleImpl) IsEditable() (bool, error) { + editable, err := e.channel.Send("isEditable") + if err != nil { + return false, err + } + return editable.(bool), nil +} + +func (e *elementHandleImpl) IsEnabled() (bool, error) { + enabled, err := e.channel.Send("isEnabled") + if err != nil { + return false, err + } + return enabled.(bool), nil +} + +func (e *elementHandleImpl) IsHidden() (bool, error) { + hidden, err := e.channel.Send("isHidden") + if err != nil { + return false, err + } + return hidden.(bool), nil +} + +func (e *elementHandleImpl) IsVisible() (bool, error) { + visible, err := e.channel.Send("isVisible") + if err != nil { + return false, err + } + return visible.(bool), nil +} + +func (e *elementHandleImpl) WaitForElementState(state ElementState, options ...ElementHandleWaitForElementStateOptions) error { + _, err := e.channel.Send("waitForElementState", map[string]interface{}{ + "state": state, + }, options) + if err != nil { + return err + } + return nil +} + +func (e *elementHandleImpl) WaitForSelector(selector string, options ...ElementHandleWaitForSelectorOptions) (ElementHandle, error) { + ch, err := e.channel.Send("waitForSelector", map[string]interface{}{ + "selector": selector, + }, options) + if err != nil { + return nil, err + } + + channelOwner := fromNullableChannel(ch) + if channelOwner == nil { + return nil, nil + } + return channelOwner.(*elementHandleImpl), nil +} + +func (e *elementHandleImpl) InputValue(options ...ElementHandleInputValueOptions) (string, error) { + result, err := e.channel.Send("inputValue", options) + if result == nil { + return "", err + } + return result.(string), err +} + +func (e *elementHandleImpl) SetChecked(checked bool, options ...ElementHandleSetCheckedOptions) error { + if checked { + _, err := e.channel.Send("check", options) + return err + } else { + _, err := e.channel.Send("uncheck", options) + return err + } +} + +func newElementHandle(parent *channelOwner, objectType string, guid string, initializer map[string]interface{}) *elementHandleImpl { + bt := &elementHandleImpl{} + bt.createChannelOwner(bt, parent, objectType, guid, initializer) + return bt +} + +func transformToStringList(in interface{}) []string { + s := in.([]interface{}) + + var out []string + for _, v := range s { + out = append(out, v.(string)) + } + return out +} diff --git a/vendor/github.com/playwright-community/playwright-go/errors.go b/vendor/github.com/playwright-community/playwright-go/errors.go new file mode 100644 index 0000000..36f7396 --- /dev/null +++ b/vendor/github.com/playwright-community/playwright-go/errors.go @@ -0,0 +1,58 @@ +package playwright + +import ( + "errors" + "fmt" +) + +var ( + // ErrPlaywright wraps all Playwright errors. + // - Use errors.Is to check if the error is a Playwright error. + // - Use errors.As to cast an error to [Error] if you want to access "Stack". + ErrPlaywright = errors.New("playwright") + // ErrTargetClosed usually wraps a reason. + ErrTargetClosed = errors.New("target closed") + // ErrTimeout wraps timeout errors. It can be either Playwright TimeoutError or client timeout. + ErrTimeout = errors.New("timeout") +) + +// Error represents a Playwright error +type Error struct { + Name string `json:"name"` + Message string `json:"message"` + Stack string `json:"stack"` +} + +func (e *Error) Error() string { + return e.Message +} + +func (e *Error) Is(target error) bool { + err, ok := target.(*Error) + if !ok { + return false + } + if err.Name != e.Name { + return false + } + if e.Name != "Error" { + return true // same name and not normal error + } + return e.Message == err.Message +} + +func parseError(err Error) error { + if err.Name == "TimeoutError" { + return fmt.Errorf("%w: %w: %w", ErrPlaywright, ErrTimeout, &err) + } else if err.Name == "TargetClosedError" { + return fmt.Errorf("%w: %w: %w", ErrPlaywright, ErrTargetClosed, &err) + } + return fmt.Errorf("%w: %w", ErrPlaywright, &err) +} + +func targetClosedError(reason *string) error { + if reason == nil { + return ErrTargetClosed + } + return fmt.Errorf("%w: %s", ErrTargetClosed, *reason) +} diff --git a/vendor/github.com/playwright-community/playwright-go/event_emitter.go b/vendor/github.com/playwright-community/playwright-go/event_emitter.go new file mode 100644 index 0000000..d4d62ef --- /dev/null +++ b/vendor/github.com/playwright-community/playwright-go/event_emitter.go @@ -0,0 +1,163 @@ +package playwright + +import ( + "math" + "reflect" + "slices" + "sync" +) + +type EventEmitter interface { + Emit(name string, payload ...interface{}) bool + ListenerCount(name string) int + On(name string, handler interface{}) + Once(name string, handler interface{}) + RemoveListener(name string, handler interface{}) + RemoveListeners(name string) +} + +type ( + eventEmitter struct { + eventsMutex sync.Mutex + events map[string]*eventRegister + hasInit bool + } + eventRegister struct { + sync.Mutex + listeners []listener + } + listener struct { + handler interface{} + once bool + } +) + +func NewEventEmitter() EventEmitter { + return &eventEmitter{} +} + +func (e *eventEmitter) Emit(name string, payload ...interface{}) (hasListener bool) { + e.eventsMutex.Lock() + e.init() + + evt, ok := e.events[name] + if !ok { + e.eventsMutex.Unlock() + return + } + e.eventsMutex.Unlock() + return evt.callHandlers(payload...) > 0 +} + +func (e *eventEmitter) Once(name string, handler interface{}) { + e.addEvent(name, handler, true) +} + +func (e *eventEmitter) On(name string, handler interface{}) { + e.addEvent(name, handler, false) +} + +func (e *eventEmitter) RemoveListener(name string, handler interface{}) { + e.eventsMutex.Lock() + defer e.eventsMutex.Unlock() + e.init() + + if evt, ok := e.events[name]; ok { + evt.Lock() + defer evt.Unlock() + evt.removeHandler(handler) + } +} + +func (e *eventEmitter) RemoveListeners(name string) { + e.eventsMutex.Lock() + defer e.eventsMutex.Unlock() + e.init() + delete(e.events, name) +} + +// ListenerCount count the listeners by name, count all if name is empty +func (e *eventEmitter) ListenerCount(name string) int { + e.eventsMutex.Lock() + defer e.eventsMutex.Unlock() + e.init() + + if name != "" { + evt, ok := e.events[name] + if !ok { + return 0 + } + return evt.count() + } + + count := 0 + for key := range e.events { + count += e.events[key].count() + } + + return count +} + +func (e *eventEmitter) addEvent(name string, handler interface{}, once bool) { + e.eventsMutex.Lock() + defer e.eventsMutex.Unlock() + e.init() + + if _, ok := e.events[name]; !ok { + e.events[name] = &eventRegister{ + listeners: make([]listener, 0), + } + } + e.events[name].addHandler(handler, once) +} + +func (e *eventEmitter) init() { + if !e.hasInit { + e.events = make(map[string]*eventRegister, 0) + e.hasInit = true + } +} + +func (er *eventRegister) addHandler(handler interface{}, once bool) { + er.Lock() + defer er.Unlock() + er.listeners = append(er.listeners, listener{handler: handler, once: once}) +} + +func (er *eventRegister) count() int { + er.Lock() + defer er.Unlock() + return len(er.listeners) +} + +func (er *eventRegister) removeHandler(handler interface{}) { + handlerPtr := reflect.ValueOf(handler).Pointer() + + er.listeners = slices.DeleteFunc(er.listeners, func(l listener) bool { + return reflect.ValueOf(l.handler).Pointer() == handlerPtr + }) +} + +func (er *eventRegister) callHandlers(payloads ...interface{}) int { + payloadV := make([]reflect.Value, 0) + + for _, p := range payloads { + payloadV = append(payloadV, reflect.ValueOf(p)) + } + + handle := func(l listener) { + handlerV := reflect.ValueOf(l.handler) + handlerV.Call(payloadV[:int(math.Min(float64(handlerV.Type().NumIn()), float64(len(payloadV))))]) + } + + er.Lock() + defer er.Unlock() + count := len(er.listeners) + for _, l := range er.listeners { + if l.once { + defer er.removeHandler(l.handler) + } + handle(l) + } + return count +} diff --git a/vendor/github.com/playwright-community/playwright-go/fetch.go b/vendor/github.com/playwright-community/playwright-go/fetch.go new file mode 100644 index 0000000..fc7f79f --- /dev/null +++ b/vendor/github.com/playwright-community/playwright-go/fetch.go @@ -0,0 +1,451 @@ +package playwright + +import ( + "encoding/base64" + "encoding/json" + "errors" + "fmt" + "os" + "strings" +) + +type apiRequestImpl struct { + *Playwright +} + +func (r *apiRequestImpl) NewContext(options ...APIRequestNewContextOptions) (APIRequestContext, error) { + overrides := map[string]interface{}{} + if len(options) == 1 { + if options[0].ClientCertificates != nil { + certs, err := transformClientCertificate(options[0].ClientCertificates) + if err != nil { + return nil, err + } + overrides["clientCertificates"] = certs + options[0].ClientCertificates = nil + } + if options[0].ExtraHttpHeaders != nil { + overrides["extraHTTPHeaders"] = serializeMapToNameAndValue(options[0].ExtraHttpHeaders) + options[0].ExtraHttpHeaders = nil + } + if options[0].StorageStatePath != nil { + var storageState *StorageState + storageString, err := os.ReadFile(*options[0].StorageStatePath) + if err != nil { + return nil, fmt.Errorf("could not read storage state file: %w", err) + } + err = json.Unmarshal(storageString, &storageState) + if err != nil { + return nil, fmt.Errorf("could not parse storage state file: %w", err) + } + options[0].StorageState = storageState + options[0].StorageStatePath = nil + } + } + + channel, err := r.channel.Send("newRequest", options, overrides) + if err != nil { + return nil, err + } + return fromChannel(channel).(*apiRequestContextImpl), nil +} + +func newApiRequestImpl(pw *Playwright) *apiRequestImpl { + return &apiRequestImpl{pw} +} + +type apiRequestContextImpl struct { + channelOwner + tracing *tracingImpl + closeReason *string +} + +func (r *apiRequestContextImpl) Dispose(options ...APIRequestContextDisposeOptions) error { + if len(options) == 1 { + r.closeReason = options[0].Reason + } + _, err := r.channel.Send("dispose", map[string]interface{}{ + "reason": r.closeReason, + }) + if errors.Is(err, ErrTargetClosed) { + return nil + } + return err +} + +func (r *apiRequestContextImpl) Delete(url string, options ...APIRequestContextDeleteOptions) (APIResponse, error) { + opts := APIRequestContextFetchOptions{ + Method: String("DELETE"), + } + if len(options) == 1 { + err := assignStructFields(&opts, options[0], false) + if err != nil { + return nil, err + } + } + + return r.Fetch(url, opts) +} + +func (r *apiRequestContextImpl) Fetch(urlOrRequest interface{}, options ...APIRequestContextFetchOptions) (APIResponse, error) { + switch v := urlOrRequest.(type) { + case string: + return r.innerFetch(v, nil, options...) + case Request: + return r.innerFetch("", v, options...) + default: + return nil, fmt.Errorf("urlOrRequest has unsupported type: %T", urlOrRequest) + } +} + +func (r *apiRequestContextImpl) innerFetch(url string, request Request, options ...APIRequestContextFetchOptions) (APIResponse, error) { + if r.closeReason != nil { + return nil, fmt.Errorf("%w: %s", ErrTargetClosed, *r.closeReason) + } + overrides := map[string]interface{}{} + if url != "" { + overrides["url"] = url + } else if request != nil { + overrides["url"] = request.URL() + } + + if len(options) == 1 { + if options[0].MaxRedirects != nil && *options[0].MaxRedirects < 0 { + return nil, errors.New("maxRedirects must be non-negative") + } + if options[0].MaxRetries != nil && *options[0].MaxRetries < 0 { + return nil, errors.New("maxRetries must be non-negative") + } + // only one of them can be specified + if countNonNil(options[0].Data, options[0].Form, options[0].Multipart) > 1 { + return nil, errors.New("only one of 'data', 'form' or 'multipart' can be specified") + } + if options[0].Method == nil { + if request != nil { + options[0].Method = String(request.Method()) + } else { + options[0].Method = String("GET") + } + } + if options[0].Headers == nil { + if request != nil { + overrides["headers"] = serializeMapToNameAndValue(request.Headers()) + } + } else { + overrides["headers"] = serializeMapToNameAndValue(options[0].Headers) + options[0].Headers = nil + } + if options[0].Data != nil { + switch v := options[0].Data.(type) { + case string: + headersArray, ok := overrides["headers"].([]map[string]string) + if ok && isJsonContentType(headersArray) { + if json.Valid([]byte(v)) { + overrides["jsonData"] = v + } else { + data, err := json.Marshal(v) + if err != nil { + return nil, fmt.Errorf("could not marshal data: %w", err) + } + overrides["jsonData"] = string(data) + } + } else { + overrides["postData"] = base64.StdEncoding.EncodeToString([]byte(v)) + } + case []byte: + overrides["postData"] = base64.StdEncoding.EncodeToString(v) + case interface{}: + data, err := json.Marshal(v) + if err != nil { + return nil, fmt.Errorf("could not marshal data: %w", err) + } + overrides["jsonData"] = string(data) + default: + return nil, errors.New("data must be a string, []byte, or interface{} that can marshal to json") + } + options[0].Data = nil + } else if options[0].Form != nil { + form, ok := options[0].Form.(map[string]interface{}) + if !ok { + return nil, errors.New("form must be a map") + } + overrides["formData"] = serializeMapToNameValue(form) + options[0].Form = nil + } else if options[0].Multipart != nil { + _, ok := options[0].Multipart.(map[string]interface{}) + if !ok { + return nil, errors.New("multipart must be a map") + } + multipartData := []map[string]interface{}{} + for name, value := range options[0].Multipart.(map[string]interface{}) { + switch v := value.(type) { + case InputFile: + multipartData = append(multipartData, map[string]interface{}{ + "name": name, + "file": map[string]string{ + "name": v.Name, + "mimeType": v.MimeType, + "buffer": base64.StdEncoding.EncodeToString(v.Buffer), + }, + }) + default: + multipartData = append(multipartData, map[string]interface{}{ + "name": name, + "value": String(fmt.Sprintf("%v", v)), + }) + } + } + overrides["multipartData"] = multipartData + options[0].Multipart = nil + } else if request != nil { + postDataBuf, err := request.PostDataBuffer() + if err == nil { + overrides["postData"] = base64.StdEncoding.EncodeToString(postDataBuf) + } + } + if options[0].Params != nil { + overrides["params"] = serializeMapToNameValue(options[0].Params) + options[0].Params = nil + } + } + + response, err := r.channel.Send("fetch", options, overrides) + if err != nil { + return nil, err + } + + return newAPIResponse(r, response.(map[string]interface{})), nil +} + +func (r *apiRequestContextImpl) Get(url string, options ...APIRequestContextGetOptions) (APIResponse, error) { + opts := APIRequestContextFetchOptions{ + Method: String("GET"), + } + if len(options) == 1 { + err := assignStructFields(&opts, options[0], false) + if err != nil { + return nil, err + } + } + + return r.Fetch(url, opts) +} + +func (r *apiRequestContextImpl) Head(url string, options ...APIRequestContextHeadOptions) (APIResponse, error) { + opts := APIRequestContextFetchOptions{ + Method: String("HEAD"), + } + if len(options) == 1 { + err := assignStructFields(&opts, options[0], false) + if err != nil { + return nil, err + } + } + + return r.Fetch(url, opts) +} + +func (r *apiRequestContextImpl) Patch(url string, options ...APIRequestContextPatchOptions) (APIResponse, error) { + opts := APIRequestContextFetchOptions{ + Method: String("PATCH"), + } + if len(options) == 1 { + err := assignStructFields(&opts, options[0], false) + if err != nil { + return nil, err + } + } + + return r.Fetch(url, opts) +} + +func (r *apiRequestContextImpl) Put(url string, options ...APIRequestContextPutOptions) (APIResponse, error) { + opts := APIRequestContextFetchOptions{ + Method: String("PUT"), + } + if len(options) == 1 { + err := assignStructFields(&opts, options[0], false) + if err != nil { + return nil, err + } + } + + return r.Fetch(url, opts) +} + +func (r *apiRequestContextImpl) Post(url string, options ...APIRequestContextPostOptions) (APIResponse, error) { + opts := APIRequestContextFetchOptions{ + Method: String("POST"), + } + if len(options) == 1 { + err := assignStructFields(&opts, options[0], false) + if err != nil { + return nil, err + } + } + + return r.Fetch(url, opts) +} + +func (r *apiRequestContextImpl) StorageState(path ...string) (*StorageState, error) { + result, err := r.channel.SendReturnAsDict("storageState") + if err != nil { + return nil, err + } + if len(path) == 1 { + file, err := os.Create(path[0]) + if err != nil { + return nil, err + } + if err := json.NewEncoder(file).Encode(result); err != nil { + return nil, err + } + if err := file.Close(); err != nil { + return nil, err + } + } + var storageState StorageState + remapMapToStruct(result, &storageState) + return &storageState, nil +} + +func newAPIRequestContext(parent *channelOwner, objectType string, guid string, initializer map[string]interface{}) *apiRequestContextImpl { + rc := &apiRequestContextImpl{} + rc.createChannelOwner(rc, parent, objectType, guid, initializer) + rc.tracing = fromChannel(initializer["tracing"]).(*tracingImpl) + return rc +} + +type apiResponseImpl struct { + request *apiRequestContextImpl + initializer map[string]interface{} + headers *rawHeaders +} + +func (r *apiResponseImpl) Body() ([]byte, error) { + result, err := r.request.channel.SendReturnAsDict("fetchResponseBody", []map[string]interface{}{ + { + "fetchUid": r.fetchUid(), + }, + }) + if err != nil { + if errors.Is(err, ErrTargetClosed) { + return nil, errors.New("response has been disposed") + } + return nil, err + } + body := result["binary"] + if body == nil { + return nil, errors.New("response has been disposed") + } + return base64.StdEncoding.DecodeString(body.(string)) +} + +func (r *apiResponseImpl) Dispose() error { + _, err := r.request.channel.Send("disposeAPIResponse", []map[string]interface{}{ + { + "fetchUid": r.fetchUid(), + }, + }) + return err +} + +func (r *apiResponseImpl) Headers() map[string]string { + return r.headers.Headers() +} + +func (r *apiResponseImpl) HeadersArray() []NameValue { + return r.headers.HeadersArray() +} + +func (r *apiResponseImpl) JSON(v interface{}) error { + body, err := r.Body() + if err != nil { + return err + } + return json.Unmarshal(body, &v) +} + +func (r *apiResponseImpl) Ok() bool { + return r.Status() == 0 || (r.Status() >= 200 && r.Status() <= 299) +} + +func (r *apiResponseImpl) Status() int { + return int(r.initializer["status"].(float64)) +} + +func (r *apiResponseImpl) StatusText() string { + return r.initializer["statusText"].(string) +} + +func (r *apiResponseImpl) Text() (string, error) { + body, err := r.Body() + if err != nil { + return "", err + } + return string(body), nil +} + +func (r *apiResponseImpl) URL() string { + return r.initializer["url"].(string) +} + +func (r *apiResponseImpl) fetchUid() string { + return r.initializer["fetchUid"].(string) +} + +func (r *apiResponseImpl) fetchLog() ([]string, error) { + ret, err := r.request.channel.Send("fetchLog", map[string]interface{}{ + "fetchUid": r.fetchUid(), + }) + if err != nil { + return nil, err + } + result := make([]string, len(ret.([]interface{}))) + for i, v := range ret.([]interface{}) { + result[i] = v.(string) + } + return result, nil +} + +func newAPIResponse(context *apiRequestContextImpl, initializer map[string]interface{}) *apiResponseImpl { + return &apiResponseImpl{ + request: context, + initializer: initializer, + headers: newRawHeaders(initializer["headers"]), + } +} + +func countNonNil(args ...interface{}) int { + count := 0 + for _, v := range args { + if v != nil { + count++ + } + } + return count +} + +func isJsonContentType(headers []map[string]string) bool { + if len(headers) > 0 { + for _, v := range headers { + if strings.ToLower(v["name"]) == "content-type" { + if v["value"] == "application/json" { + return true + } + } + } + } + return false +} + +func serializeMapToNameValue(data map[string]interface{}) []map[string]string { + serialized := make([]map[string]string, 0, len(data)) + for k, v := range data { + serialized = append(serialized, map[string]string{ + "name": k, + "value": fmt.Sprintf("%v", v), + }) + } + return serialized +} diff --git a/vendor/github.com/playwright-community/playwright-go/file_chooser.go b/vendor/github.com/playwright-community/playwright-go/file_chooser.go new file mode 100644 index 0000000..119e885 --- /dev/null +++ b/vendor/github.com/playwright-community/playwright-go/file_chooser.go @@ -0,0 +1,44 @@ +package playwright + +type fileChooserImpl struct { + page Page + elementHandle ElementHandle + isMultiple bool +} + +func (f *fileChooserImpl) Page() Page { + return f.page +} + +func (f *fileChooserImpl) Element() ElementHandle { + return f.elementHandle +} + +func (f *fileChooserImpl) IsMultiple() bool { + return f.isMultiple +} + +// InputFile represents the input file for: +// - FileChooser.SetFiles() +// - ElementHandle.SetInputFiles() +// - Page.SetInputFiles() +type InputFile struct { + Name string `json:"name"` + MimeType string `json:"mimeType,omitempty"` + Buffer []byte `json:"buffer"` +} + +func (f *fileChooserImpl) SetFiles(files interface{}, options ...FileChooserSetFilesOptions) error { + if len(options) == 1 { + return f.elementHandle.SetInputFiles(files, ElementHandleSetInputFilesOptions(options[0])) + } + return f.elementHandle.SetInputFiles(files) +} + +func newFileChooser(page Page, elementHandle ElementHandle, isMultiple bool) *fileChooserImpl { + return &fileChooserImpl{ + page: page, + elementHandle: elementHandle, + isMultiple: isMultiple, + } +} diff --git a/vendor/github.com/playwright-community/playwright-go/frame.go b/vendor/github.com/playwright-community/playwright-go/frame.go new file mode 100644 index 0000000..b571c8e --- /dev/null +++ b/vendor/github.com/playwright-community/playwright-go/frame.go @@ -0,0 +1,792 @@ +package playwright + +import ( + "errors" + "fmt" + "os" + "time" + + mapset "github.com/deckarep/golang-set/v2" +) + +type frameImpl struct { + channelOwner + detached bool + page *pageImpl + name string + url string + parentFrame Frame + childFrames []Frame + loadStates mapset.Set[string] +} + +func newFrame(parent *channelOwner, objectType string, guid string, initializer map[string]interface{}) *frameImpl { + var loadStates mapset.Set[string] + + if ls, ok := initializer["loadStates"].([]string); ok { + loadStates = mapset.NewSet[string](ls...) + } else { + loadStates = mapset.NewSet[string]() + } + f := &frameImpl{ + name: initializer["name"].(string), + url: initializer["url"].(string), + loadStates: loadStates, + childFrames: make([]Frame, 0), + } + f.createChannelOwner(f, parent, objectType, guid, initializer) + + channelOwner := fromNullableChannel(initializer["parentFrame"]) + if channelOwner != nil { + f.parentFrame = channelOwner.(*frameImpl) + f.parentFrame.(*frameImpl).childFrames = append(f.parentFrame.(*frameImpl).childFrames, f) + } + + f.channel.On("navigated", f.onFrameNavigated) + f.channel.On("loadstate", f.onLoadState) + return f +} + +func (f *frameImpl) URL() string { + f.RLock() + defer f.RUnlock() + return f.url +} + +func (f *frameImpl) Name() string { + f.RLock() + defer f.RUnlock() + return f.name +} + +func (f *frameImpl) SetContent(content string, options ...FrameSetContentOptions) error { + _, err := f.channel.Send("setContent", map[string]interface{}{ + "html": content, + }, options) + return err +} + +func (f *frameImpl) Content() (string, error) { + content, err := f.channel.Send("content") + if content == nil { + return "", err + } + return content.(string), err +} + +func (f *frameImpl) Goto(url string, options ...FrameGotoOptions) (Response, error) { + channel, err := f.channel.Send("goto", map[string]interface{}{ + "url": url, + }, options) + if err != nil { + return nil, fmt.Errorf("Frame.Goto %s: %w", url, err) + } + channelOwner := fromNullableChannel(channel) + if channelOwner == nil { + // navigation to about:blank or navigation to the same URL with a different hash + return nil, nil + } + return channelOwner.(*responseImpl), nil +} + +func (f *frameImpl) AddScriptTag(options FrameAddScriptTagOptions) (ElementHandle, error) { + if options.Path != nil { + file, err := os.ReadFile(*options.Path) + if err != nil { + return nil, err + } + options.Content = String(string(file)) + options.Path = nil + } + channel, err := f.channel.Send("addScriptTag", options) + if err != nil { + return nil, err + } + return fromChannel(channel).(*elementHandleImpl), nil +} + +func (f *frameImpl) AddStyleTag(options FrameAddStyleTagOptions) (ElementHandle, error) { + if options.Path != nil { + file, err := os.ReadFile(*options.Path) + if err != nil { + return nil, err + } + options.Content = String(string(file)) + options.Path = nil + } + channel, err := f.channel.Send("addStyleTag", options) + if err != nil { + return nil, err + } + return fromChannel(channel).(*elementHandleImpl), nil +} + +func (f *frameImpl) Page() Page { + return f.page +} + +func (f *frameImpl) WaitForLoadState(options ...FrameWaitForLoadStateOptions) error { + option := FrameWaitForLoadStateOptions{} + if len(options) == 1 { + option = options[0] + } + if option.State == nil { + option.State = LoadStateLoad + } + return f.waitForLoadStateImpl(string(*option.State), option.Timeout, nil) +} + +func (f *frameImpl) waitForLoadStateImpl(state string, timeout *float64, cb func() error) error { + if f.loadStates.ContainsOne(state) { + return nil + } + waiter, err := f.setNavigationWaiter(timeout) + if err != nil { + return err + } + waiter.WaitForEvent(f, "loadstate", func(payload interface{}) bool { + gotState := payload.(string) + return gotState == state + }) + if cb == nil { + _, err := waiter.Wait() + return err + } else { + _, err := waiter.RunAndWait(cb) + return err + } +} + +func (f *frameImpl) WaitForURL(url interface{}, options ...FrameWaitForURLOptions) error { + if f.page == nil { + return errors.New("frame is detached") + } + matcher := newURLMatcher(url, f.page.browserContext.options.BaseURL) + if matcher.Matches(f.URL()) { + state := "load" + timeout := Float(f.page.timeoutSettings.NavigationTimeout()) + if len(options) == 1 { + if options[0].WaitUntil != nil { + state = string(*options[0].WaitUntil) + } + if options[0].Timeout != nil { + timeout = options[0].Timeout + } + } + return f.waitForLoadStateImpl(state, timeout, nil) + } + navigationOptions := FrameExpectNavigationOptions{URL: url} + if len(options) > 0 { + navigationOptions.Timeout = options[0].Timeout + navigationOptions.WaitUntil = options[0].WaitUntil + } + if _, err := f.ExpectNavigation(nil, navigationOptions); err != nil { + return err + } + return nil +} + +func (f *frameImpl) ExpectNavigation(cb func() error, options ...FrameExpectNavigationOptions) (Response, error) { + if f.page == nil { + return nil, errors.New("frame is detached") + } + option := FrameExpectNavigationOptions{} + if len(options) == 1 { + option = options[0] + } + if option.WaitUntil == nil { + option.WaitUntil = WaitUntilStateLoad + } + if option.Timeout == nil { + option.Timeout = Float(f.page.timeoutSettings.NavigationTimeout()) + } + deadline := time.Now().Add(time.Duration(*option.Timeout) * time.Millisecond) + var matcher *urlMatcher + if option.URL != nil { + matcher = newURLMatcher(option.URL, f.page.browserContext.options.BaseURL) + } + predicate := func(events ...interface{}) bool { + ev := events[0].(map[string]interface{}) + err, ok := ev["error"] + if ok { + // Any failed navigation results in a rejection. + logger.Error("navigation error", "url", ev["url"].(string), "error", err) + return true + } + return matcher == nil || matcher.Matches(ev["url"].(string)) + } + waiter, err := f.setNavigationWaiter(option.Timeout) + if err != nil { + return nil, err + } + + eventData, err := waiter.WaitForEvent(f, "navigated", predicate).RunAndWait(cb) + if err != nil || eventData == nil { + return nil, err + } + + t := time.Until(deadline).Milliseconds() + if t > 0 { + err = f.waitForLoadStateImpl(string(*option.WaitUntil), Float(float64(t)), nil) + if err != nil { + return nil, err + } + } + event := eventData.(map[string]interface{}) + if event["newDocument"] != nil && event["newDocument"].(map[string]interface{})["request"] != nil { + request := fromChannel(event["newDocument"].(map[string]interface{})["request"]).(*requestImpl) + return request.Response() + } + return nil, nil +} + +func (f *frameImpl) setNavigationWaiter(timeout *float64) (*waiter, error) { + if f.page == nil { + return nil, errors.New("page does not exist") + } + waiter := newWaiter() + if timeout != nil { + waiter.WithTimeout(*timeout) + } else { + waiter.WithTimeout(f.page.timeoutSettings.NavigationTimeout()) + } + waiter.RejectOnEvent(f.page, "close", f.page.closeErrorWithReason()) + waiter.RejectOnEvent(f.page, "crash", fmt.Errorf("Navigation failed because page crashed!")) + waiter.RejectOnEvent(f.page, "framedetached", fmt.Errorf("Navigating frame was detached!"), func(payload interface{}) bool { + frame, ok := payload.(*frameImpl) + if ok && frame == f { + return true + } + return false + }) + return waiter, nil +} + +func (f *frameImpl) onFrameNavigated(ev map[string]interface{}) { + f.Lock() + f.url = ev["url"].(string) + f.name = ev["name"].(string) + f.Unlock() + f.Emit("navigated", ev) + _, ok := ev["error"] + if !ok && f.page != nil { + f.page.Emit("framenavigated", f) + } +} + +func (f *frameImpl) onLoadState(ev map[string]interface{}) { + if ev["add"] != nil { + add := ev["add"].(string) + f.loadStates.Add(add) + f.Emit("loadstate", add) + if f.parentFrame == nil && f.page != nil { + if add == "load" || add == "domcontentloaded" { + f.Page().Emit(add, f.page) + } + } + } else if ev["remove"] != nil { + remove := ev["remove"].(string) + f.loadStates.Remove(remove) + } +} + +func (f *frameImpl) QuerySelector(selector string, options ...FrameQuerySelectorOptions) (ElementHandle, error) { + params := map[string]interface{}{ + "selector": selector, + } + if len(options) == 1 { + params["strict"] = options[0].Strict + } + channel, err := f.channel.Send("querySelector", params) + if err != nil { + return nil, err + } + if channel == nil { + return nil, nil + } + return fromChannel(channel).(*elementHandleImpl), nil +} + +func (f *frameImpl) QuerySelectorAll(selector string) ([]ElementHandle, error) { + channels, err := f.channel.Send("querySelectorAll", map[string]interface{}{ + "selector": selector, + }) + if err != nil { + return nil, err + } + elements := make([]ElementHandle, 0) + for _, channel := range channels.([]interface{}) { + elements = append(elements, fromChannel(channel).(*elementHandleImpl)) + } + return elements, nil +} + +func (f *frameImpl) Evaluate(expression string, options ...interface{}) (interface{}, error) { + var arg interface{} + if len(options) == 1 { + arg = options[0] + } + result, err := f.channel.Send("evaluateExpression", map[string]interface{}{ + "expression": expression, + "arg": serializeArgument(arg), + }) + if err != nil { + return nil, err + } + return parseResult(result), nil +} + +func (f *frameImpl) EvalOnSelector(selector string, expression string, arg interface{}, options ...FrameEvalOnSelectorOptions) (interface{}, error) { + params := map[string]interface{}{ + "selector": selector, + "expression": expression, + "arg": serializeArgument(arg), + } + if len(options) == 1 && options[0].Strict != nil { + params["strict"] = *options[0].Strict + } + + result, err := f.channel.Send("evalOnSelector", params) + if err != nil { + return nil, err + } + return parseResult(result), nil +} + +func (f *frameImpl) EvalOnSelectorAll(selector string, expression string, options ...interface{}) (interface{}, error) { + var arg interface{} + if len(options) == 1 { + arg = options[0] + } + result, err := f.channel.Send("evalOnSelectorAll", map[string]interface{}{ + "selector": selector, + "expression": expression, + "arg": serializeArgument(arg), + }) + if err != nil { + return nil, err + } + return parseResult(result), nil +} + +func (f *frameImpl) EvaluateHandle(expression string, options ...interface{}) (JSHandle, error) { + var arg interface{} + if len(options) == 1 { + arg = options[0] + } + result, err := f.channel.Send("evaluateExpressionHandle", map[string]interface{}{ + "expression": expression, + "arg": serializeArgument(arg), + }) + if err != nil { + return nil, err + } + channelOwner := fromChannel(result) + if channelOwner == nil { + return nil, nil + } + return channelOwner.(JSHandle), nil +} + +func (f *frameImpl) Click(selector string, options ...FrameClickOptions) error { + _, err := f.channel.Send("click", map[string]interface{}{ + "selector": selector, + }, options) + return err +} + +func (f *frameImpl) WaitForSelector(selector string, options ...FrameWaitForSelectorOptions) (ElementHandle, error) { + channel, err := f.channel.Send("waitForSelector", map[string]interface{}{ + "selector": selector, + }, options) + if err != nil { + return nil, err + } + channelOwner := fromNullableChannel(channel) + if channelOwner == nil { + return nil, nil + } + return channelOwner.(*elementHandleImpl), nil +} + +func (f *frameImpl) DispatchEvent(selector, typ string, eventInit interface{}, options ...FrameDispatchEventOptions) error { + _, err := f.channel.Send("dispatchEvent", map[string]interface{}{ + "selector": selector, + "type": typ, + "eventInit": serializeArgument(eventInit), + }) + return err +} + +func (f *frameImpl) InnerText(selector string, options ...FrameInnerTextOptions) (string, error) { + innerText, err := f.channel.Send("innerText", map[string]interface{}{ + "selector": selector, + }, options) + if innerText == nil { + return "", err + } + return innerText.(string), err +} + +func (f *frameImpl) InnerHTML(selector string, options ...FrameInnerHTMLOptions) (string, error) { + innerHTML, err := f.channel.Send("innerHTML", map[string]interface{}{ + "selector": selector, + }, options) + if innerHTML == nil { + return "", err + } + return innerHTML.(string), err +} + +func (f *frameImpl) GetAttribute(selector string, name string, options ...FrameGetAttributeOptions) (string, error) { + attribute, err := f.channel.Send("getAttribute", map[string]interface{}{ + "selector": selector, + "name": name, + }, options) + if attribute == nil { + return "", err + } + return attribute.(string), err +} + +func (f *frameImpl) Hover(selector string, options ...FrameHoverOptions) error { + _, err := f.channel.Send("hover", map[string]interface{}{ + "selector": selector, + }, options) + return err +} + +func (f *frameImpl) SetInputFiles(selector string, files interface{}, options ...FrameSetInputFilesOptions) error { + params, err := convertInputFiles(files, f.page.browserContext) + if err != nil { + return err + } + params.Selector = &selector + _, err = f.channel.Send("setInputFiles", params, options) + return err +} + +func (f *frameImpl) Type(selector, text string, options ...FrameTypeOptions) error { + _, err := f.channel.Send("type", map[string]interface{}{ + "selector": selector, + "text": text, + }, options) + return err +} + +func (f *frameImpl) Press(selector, key string, options ...FramePressOptions) error { + _, err := f.channel.Send("press", map[string]interface{}{ + "selector": selector, + "key": key, + }, options) + return err +} + +func (f *frameImpl) Check(selector string, options ...FrameCheckOptions) error { + _, err := f.channel.Send("check", map[string]interface{}{ + "selector": selector, + }, options) + return err +} + +func (f *frameImpl) Uncheck(selector string, options ...FrameUncheckOptions) error { + _, err := f.channel.Send("uncheck", map[string]interface{}{ + "selector": selector, + }, options) + return err +} + +func (f *frameImpl) WaitForTimeout(timeout float64) { + time.Sleep(time.Duration(timeout) * time.Millisecond) +} + +func (f *frameImpl) WaitForFunction(expression string, arg interface{}, options ...FrameWaitForFunctionOptions) (JSHandle, error) { + var option FrameWaitForFunctionOptions + if len(options) == 1 { + option = options[0] + } + result, err := f.channel.Send("waitForFunction", map[string]interface{}{ + "expression": expression, + "arg": serializeArgument(arg), + "timeout": option.Timeout, + "polling": option.Polling, + }) + if err != nil { + return nil, err + } + handle := fromChannel(result) + if handle == nil { + return nil, nil + } + return handle.(*jsHandleImpl), nil +} + +func (f *frameImpl) Title() (string, error) { + title, err := f.channel.Send("title") + if title == nil { + return "", err + } + return title.(string), err +} + +func (f *frameImpl) ChildFrames() []Frame { + return f.childFrames +} + +func (f *frameImpl) Dblclick(selector string, options ...FrameDblclickOptions) error { + _, err := f.channel.Send("dblclick", map[string]interface{}{ + "selector": selector, + }, options) + return err +} + +func (f *frameImpl) Fill(selector string, value string, options ...FrameFillOptions) error { + _, err := f.channel.Send("fill", map[string]interface{}{ + "selector": selector, + "value": value, + }, options) + return err +} + +func (f *frameImpl) Focus(selector string, options ...FrameFocusOptions) error { + _, err := f.channel.Send("focus", map[string]interface{}{ + "selector": selector, + }, options) + return err +} + +func (f *frameImpl) FrameElement() (ElementHandle, error) { + channel, err := f.channel.Send("frameElement") + if err != nil { + return nil, err + } + return fromChannel(channel).(*elementHandleImpl), nil +} + +func (f *frameImpl) IsDetached() bool { + return f.detached +} + +func (f *frameImpl) ParentFrame() Frame { + return f.parentFrame +} + +func (f *frameImpl) TextContent(selector string, options ...FrameTextContentOptions) (string, error) { + textContent, err := f.channel.Send("textContent", map[string]interface{}{ + "selector": selector, + }, options) + if textContent == nil { + return "", err + } + return textContent.(string), err +} + +func (f *frameImpl) Tap(selector string, options ...FrameTapOptions) error { + _, err := f.channel.Send("tap", map[string]interface{}{ + "selector": selector, + }, options) + return err +} + +func (f *frameImpl) SelectOption(selector string, values SelectOptionValues, options ...FrameSelectOptionOptions) ([]string, error) { + opts := convertSelectOptionSet(values) + + m := make(map[string]interface{}) + m["selector"] = selector + for k, v := range opts { + m[k] = v + } + selected, err := f.channel.Send("selectOption", m, options) + if err != nil { + return nil, err + } + + return transformToStringList(selected), nil +} + +func (f *frameImpl) IsChecked(selector string, options ...FrameIsCheckedOptions) (bool, error) { + checked, err := f.channel.Send("isChecked", map[string]interface{}{ + "selector": selector, + }, options) + if err != nil { + return false, err + } + return checked.(bool), nil +} + +func (f *frameImpl) IsDisabled(selector string, options ...FrameIsDisabledOptions) (bool, error) { + disabled, err := f.channel.Send("isDisabled", map[string]interface{}{ + "selector": selector, + }, options) + if err != nil { + return false, err + } + return disabled.(bool), nil +} + +func (f *frameImpl) IsEditable(selector string, options ...FrameIsEditableOptions) (bool, error) { + editable, err := f.channel.Send("isEditable", map[string]interface{}{ + "selector": selector, + }, options) + if err != nil { + return false, err + } + return editable.(bool), nil +} + +func (f *frameImpl) IsEnabled(selector string, options ...FrameIsEnabledOptions) (bool, error) { + enabled, err := f.channel.Send("isEnabled", map[string]interface{}{ + "selector": selector, + }, options) + if err != nil { + return false, err + } + return enabled.(bool), nil +} + +func (f *frameImpl) IsHidden(selector string, options ...FrameIsHiddenOptions) (bool, error) { + hidden, err := f.channel.Send("isHidden", map[string]interface{}{ + "selector": selector, + }, options) + if err != nil { + return false, err + } + return hidden.(bool), nil +} + +func (f *frameImpl) IsVisible(selector string, options ...FrameIsVisibleOptions) (bool, error) { + visible, err := f.channel.Send("isVisible", map[string]interface{}{ + "selector": selector, + }, options) + if err != nil { + return false, err + } + return visible.(bool), nil +} + +func (f *frameImpl) InputValue(selector string, options ...FrameInputValueOptions) (string, error) { + value, err := f.channel.Send("inputValue", map[string]interface{}{ + "selector": selector, + }, options) + if value == nil { + return "", err + } + return value.(string), err +} + +func (f *frameImpl) DragAndDrop(source, target string, options ...FrameDragAndDropOptions) error { + _, err := f.channel.Send("dragAndDrop", map[string]interface{}{ + "source": source, + "target": target, + }, options) + return err +} + +func (f *frameImpl) SetChecked(selector string, checked bool, options ...FrameSetCheckedOptions) error { + if checked { + _, err := f.channel.Send("check", map[string]interface{}{ + "selector": selector, + }, options) + return err + } else { + _, err := f.channel.Send("uncheck", map[string]interface{}{ + "selector": selector, + }, options) + return err + } +} + +func (f *frameImpl) Locator(selector string, options ...FrameLocatorOptions) Locator { + var option LocatorOptions + if len(options) == 1 { + option = LocatorOptions{ + Has: options[0].Has, + HasNot: options[0].HasNot, + HasText: options[0].HasText, + HasNotText: options[0].HasNotText, + } + } + return newLocator(f, selector, option) +} + +func (f *frameImpl) GetByAltText(text interface{}, options ...FrameGetByAltTextOptions) Locator { + exact := false + if len(options) == 1 { + if *options[0].Exact { + exact = true + } + } + return f.Locator(getByAltTextSelector(text, exact)) +} + +func (f *frameImpl) GetByLabel(text interface{}, options ...FrameGetByLabelOptions) Locator { + exact := false + if len(options) == 1 { + if *options[0].Exact { + exact = true + } + } + return f.Locator(getByLabelSelector(text, exact)) +} + +func (f *frameImpl) GetByPlaceholder(text interface{}, options ...FrameGetByPlaceholderOptions) Locator { + exact := false + if len(options) == 1 { + if *options[0].Exact { + exact = true + } + } + return f.Locator(getByPlaceholderSelector(text, exact)) +} + +func (f *frameImpl) GetByRole(role AriaRole, options ...FrameGetByRoleOptions) Locator { + if len(options) == 1 { + return f.Locator(getByRoleSelector(role, LocatorGetByRoleOptions(options[0]))) + } + return f.Locator(getByRoleSelector(role)) +} + +func (f *frameImpl) GetByTestId(testId interface{}) Locator { + return f.Locator(getByTestIdSelector(getTestIdAttributeName(), testId)) +} + +func (f *frameImpl) GetByText(text interface{}, options ...FrameGetByTextOptions) Locator { + exact := false + if len(options) == 1 { + if *options[0].Exact { + exact = true + } + } + return f.Locator(getByTextSelector(text, exact)) +} + +func (f *frameImpl) GetByTitle(text interface{}, options ...FrameGetByTitleOptions) Locator { + exact := false + if len(options) == 1 { + if *options[0].Exact { + exact = true + } + } + return f.Locator(getByTitleSelector(text, exact)) +} + +func (f *frameImpl) FrameLocator(selector string) FrameLocator { + return newFrameLocator(f, selector) +} + +func (f *frameImpl) highlight(selector string) error { + _, err := f.channel.Send("highlight", map[string]interface{}{ + "selector": selector, + }) + return err +} + +func (f *frameImpl) queryCount(selector string) (int, error) { + response, err := f.channel.Send("queryCount", map[string]interface{}{ + "selector": selector, + }) + if err != nil { + return 0, err + } + return int(response.(float64)), nil +} diff --git a/vendor/github.com/playwright-community/playwright-go/frame_locator.go b/vendor/github.com/playwright-community/playwright-go/frame_locator.go new file mode 100644 index 0000000..d4b8fd0 --- /dev/null +++ b/vendor/github.com/playwright-community/playwright-go/frame_locator.go @@ -0,0 +1,130 @@ +package playwright + +import ( + "errors" + "fmt" + "strconv" +) + +type frameLocatorImpl struct { + frame *frameImpl + frameSelector string +} + +func newFrameLocator(frame *frameImpl, frameSelector string) *frameLocatorImpl { + return &frameLocatorImpl{frame: frame, frameSelector: frameSelector} +} + +func (fl *frameLocatorImpl) First() FrameLocator { + return newFrameLocator(fl.frame, fl.frameSelector+" >> nth=0") +} + +func (fl *frameLocatorImpl) FrameLocator(selector string) FrameLocator { + return newFrameLocator(fl.frame, fl.frameSelector+" >> internal:control=enter-frame >> "+selector) +} + +func (fl *frameLocatorImpl) GetByAltText(text interface{}, options ...FrameLocatorGetByAltTextOptions) Locator { + exact := false + if len(options) == 1 { + if *options[0].Exact { + exact = true + } + } + return fl.Locator(getByAltTextSelector(text, exact)) +} + +func (fl *frameLocatorImpl) GetByLabel(text interface{}, options ...FrameLocatorGetByLabelOptions) Locator { + exact := false + if len(options) == 1 { + if *options[0].Exact { + exact = true + } + } + return fl.Locator(getByLabelSelector(text, exact)) +} + +func (fl *frameLocatorImpl) GetByPlaceholder(text interface{}, options ...FrameLocatorGetByPlaceholderOptions) Locator { + exact := false + if len(options) == 1 { + if *options[0].Exact { + exact = true + } + } + return fl.Locator(getByPlaceholderSelector(text, exact)) +} + +func (fl *frameLocatorImpl) GetByRole(role AriaRole, options ...FrameLocatorGetByRoleOptions) Locator { + if len(options) == 1 { + return fl.Locator(getByRoleSelector(role, LocatorGetByRoleOptions(options[0]))) + } + return fl.Locator(getByRoleSelector(role)) +} + +func (fl *frameLocatorImpl) GetByTestId(testId interface{}) Locator { + return fl.Locator(getByTestIdSelector(getTestIdAttributeName(), testId)) +} + +func (fl *frameLocatorImpl) GetByText(text interface{}, options ...FrameLocatorGetByTextOptions) Locator { + exact := false + if len(options) == 1 { + if *options[0].Exact { + exact = true + } + } + return fl.Locator(getByTextSelector(text, exact)) +} + +func (fl *frameLocatorImpl) GetByTitle(text interface{}, options ...FrameLocatorGetByTitleOptions) Locator { + exact := false + if len(options) == 1 { + if *options[0].Exact { + exact = true + } + } + return fl.Locator(getByTitleSelector(text, exact)) +} + +func (fl *frameLocatorImpl) Last() FrameLocator { + return newFrameLocator(fl.frame, fl.frameSelector+" >> nth=-1") +} + +func (fl *frameLocatorImpl) Locator(selectorOrLocator interface{}, options ...FrameLocatorLocatorOptions) Locator { + var option LocatorOptions + if len(options) == 1 { + option = LocatorOptions{ + Has: options[0].Has, + HasNot: options[0].HasNot, + HasText: options[0].HasText, + HasNotText: options[0].HasNotText, + } + } + + selector, ok := selectorOrLocator.(string) + if ok { + return newLocator(fl.frame, fl.frameSelector+" >> internal:control=enter-frame >> "+selector, option) + } + locator, ok := selectorOrLocator.(*locatorImpl) + if ok { + if fl.frame != locator.frame { + locator.err = errors.Join(locator.err, ErrLocatorNotSameFrame) + return locator + } + return newLocator(locator.frame, + fmt.Sprintf("%s >> internal:control=enter-frame >> %s", fl.frameSelector, locator.selector), + option, + ) + } + return &locatorImpl{ + frame: fl.frame, + selector: fl.frameSelector, + err: fmt.Errorf("invalid locator parameter: %v", selectorOrLocator), + } +} + +func (fl *frameLocatorImpl) Nth(index int) FrameLocator { + return newFrameLocator(fl.frame, fl.frameSelector+" >> nth="+strconv.Itoa(index)) +} + +func (fl *frameLocatorImpl) Owner() Locator { + return newLocator(fl.frame, fl.frameSelector) +} diff --git a/vendor/github.com/playwright-community/playwright-go/generated-enums.go b/vendor/github.com/playwright-community/playwright-go/generated-enums.go new file mode 100644 index 0000000..92c20c6 --- /dev/null +++ b/vendor/github.com/playwright-community/playwright-go/generated-enums.go @@ -0,0 +1,404 @@ +package playwright + +func getMixedState(in string) *MixedState { + v := MixedState(in) + return &v +} + +type MixedState string + +var ( + MixedStateOn *MixedState = getMixedState("On") + MixedStateOff = getMixedState("Off") + MixedStateMixed = getMixedState("Mixed") +) + +func getElementState(in string) *ElementState { + v := ElementState(in) + return &v +} + +type ElementState string + +var ( + ElementStateVisible *ElementState = getElementState("visible") + ElementStateHidden = getElementState("hidden") + ElementStateStable = getElementState("stable") + ElementStateEnabled = getElementState("enabled") + ElementStateDisabled = getElementState("disabled") + ElementStateEditable = getElementState("editable") +) + +func getAriaRole(in string) *AriaRole { + v := AriaRole(in) + return &v +} + +type AriaRole string + +var ( + AriaRoleAlert *AriaRole = getAriaRole("alert") + AriaRoleAlertdialog = getAriaRole("alertdialog") + AriaRoleApplication = getAriaRole("application") + AriaRoleArticle = getAriaRole("article") + AriaRoleBanner = getAriaRole("banner") + AriaRoleBlockquote = getAriaRole("blockquote") + AriaRoleButton = getAriaRole("button") + AriaRoleCaption = getAriaRole("caption") + AriaRoleCell = getAriaRole("cell") + AriaRoleCheckbox = getAriaRole("checkbox") + AriaRoleCode = getAriaRole("code") + AriaRoleColumnheader = getAriaRole("columnheader") + AriaRoleCombobox = getAriaRole("combobox") + AriaRoleComplementary = getAriaRole("complementary") + AriaRoleContentinfo = getAriaRole("contentinfo") + AriaRoleDefinition = getAriaRole("definition") + AriaRoleDeletion = getAriaRole("deletion") + AriaRoleDialog = getAriaRole("dialog") + AriaRoleDirectory = getAriaRole("directory") + AriaRoleDocument = getAriaRole("document") + AriaRoleEmphasis = getAriaRole("emphasis") + AriaRoleFeed = getAriaRole("feed") + AriaRoleFigure = getAriaRole("figure") + AriaRoleForm = getAriaRole("form") + AriaRoleGeneric = getAriaRole("generic") + AriaRoleGrid = getAriaRole("grid") + AriaRoleGridcell = getAriaRole("gridcell") + AriaRoleGroup = getAriaRole("group") + AriaRoleHeading = getAriaRole("heading") + AriaRoleImg = getAriaRole("img") + AriaRoleInsertion = getAriaRole("insertion") + AriaRoleLink = getAriaRole("link") + AriaRoleList = getAriaRole("list") + AriaRoleListbox = getAriaRole("listbox") + AriaRoleListitem = getAriaRole("listitem") + AriaRoleLog = getAriaRole("log") + AriaRoleMain = getAriaRole("main") + AriaRoleMarquee = getAriaRole("marquee") + AriaRoleMath = getAriaRole("math") + AriaRoleMeter = getAriaRole("meter") + AriaRoleMenu = getAriaRole("menu") + AriaRoleMenubar = getAriaRole("menubar") + AriaRoleMenuitem = getAriaRole("menuitem") + AriaRoleMenuitemcheckbox = getAriaRole("menuitemcheckbox") + AriaRoleMenuitemradio = getAriaRole("menuitemradio") + AriaRoleNavigation = getAriaRole("navigation") + AriaRoleNone = getAriaRole("none") + AriaRoleNote = getAriaRole("note") + AriaRoleOption = getAriaRole("option") + AriaRoleParagraph = getAriaRole("paragraph") + AriaRolePresentation = getAriaRole("presentation") + AriaRoleProgressbar = getAriaRole("progressbar") + AriaRoleRadio = getAriaRole("radio") + AriaRoleRadiogroup = getAriaRole("radiogroup") + AriaRoleRegion = getAriaRole("region") + AriaRoleRow = getAriaRole("row") + AriaRoleRowgroup = getAriaRole("rowgroup") + AriaRoleRowheader = getAriaRole("rowheader") + AriaRoleScrollbar = getAriaRole("scrollbar") + AriaRoleSearch = getAriaRole("search") + AriaRoleSearchbox = getAriaRole("searchbox") + AriaRoleSeparator = getAriaRole("separator") + AriaRoleSlider = getAriaRole("slider") + AriaRoleSpinbutton = getAriaRole("spinbutton") + AriaRoleStatus = getAriaRole("status") + AriaRoleStrong = getAriaRole("strong") + AriaRoleSubscript = getAriaRole("subscript") + AriaRoleSuperscript = getAriaRole("superscript") + AriaRoleSwitch = getAriaRole("switch") + AriaRoleTab = getAriaRole("tab") + AriaRoleTable = getAriaRole("table") + AriaRoleTablist = getAriaRole("tablist") + AriaRoleTabpanel = getAriaRole("tabpanel") + AriaRoleTerm = getAriaRole("term") + AriaRoleTextbox = getAriaRole("textbox") + AriaRoleTime = getAriaRole("time") + AriaRoleTimer = getAriaRole("timer") + AriaRoleToolbar = getAriaRole("toolbar") + AriaRoleTooltip = getAriaRole("tooltip") + AriaRoleTree = getAriaRole("tree") + AriaRoleTreegrid = getAriaRole("treegrid") + AriaRoleTreeitem = getAriaRole("treeitem") +) + +func getColorScheme(in string) *ColorScheme { + v := ColorScheme(in) + return &v +} + +type ColorScheme string + +var ( + ColorSchemeLight *ColorScheme = getColorScheme("light") + ColorSchemeDark = getColorScheme("dark") + ColorSchemeNoPreference = getColorScheme("no-preference") + ColorSchemeNoOverride = getColorScheme("no-override") +) + +func getForcedColors(in string) *ForcedColors { + v := ForcedColors(in) + return &v +} + +type ForcedColors string + +var ( + ForcedColorsActive *ForcedColors = getForcedColors("active") + ForcedColorsNone = getForcedColors("none") + ForcedColorsNoOverride = getForcedColors("no-override") +) + +func getHarContentPolicy(in string) *HarContentPolicy { + v := HarContentPolicy(in) + return &v +} + +type HarContentPolicy string + +var ( + HarContentPolicyOmit *HarContentPolicy = getHarContentPolicy("omit") + HarContentPolicyEmbed = getHarContentPolicy("embed") + HarContentPolicyAttach = getHarContentPolicy("attach") +) + +func getHarMode(in string) *HarMode { + v := HarMode(in) + return &v +} + +type HarMode string + +var ( + HarModeFull *HarMode = getHarMode("full") + HarModeMinimal = getHarMode("minimal") +) + +func getReducedMotion(in string) *ReducedMotion { + v := ReducedMotion(in) + return &v +} + +type ReducedMotion string + +var ( + ReducedMotionReduce *ReducedMotion = getReducedMotion("reduce") + ReducedMotionNoPreference = getReducedMotion("no-preference") + ReducedMotionNoOverride = getReducedMotion("no-override") +) + +func getServiceWorkerPolicy(in string) *ServiceWorkerPolicy { + v := ServiceWorkerPolicy(in) + return &v +} + +type ServiceWorkerPolicy string + +var ( + ServiceWorkerPolicyAllow *ServiceWorkerPolicy = getServiceWorkerPolicy("allow") + ServiceWorkerPolicyBlock = getServiceWorkerPolicy("block") +) + +func getSameSiteAttribute(in string) *SameSiteAttribute { + v := SameSiteAttribute(in) + return &v +} + +type SameSiteAttribute string + +var ( + SameSiteAttributeStrict *SameSiteAttribute = getSameSiteAttribute("Strict") + SameSiteAttributeLax = getSameSiteAttribute("Lax") + SameSiteAttributeNone = getSameSiteAttribute("None") +) + +func getHarNotFound(in string) *HarNotFound { + v := HarNotFound(in) + return &v +} + +type HarNotFound string + +var ( + HarNotFoundAbort *HarNotFound = getHarNotFound("abort") + HarNotFoundFallback = getHarNotFound("fallback") +) + +func getRouteFromHarUpdateContentPolicy(in string) *RouteFromHarUpdateContentPolicy { + v := RouteFromHarUpdateContentPolicy(in) + return &v +} + +type RouteFromHarUpdateContentPolicy string + +var ( + RouteFromHarUpdateContentPolicyEmbed *RouteFromHarUpdateContentPolicy = getRouteFromHarUpdateContentPolicy("embed") + RouteFromHarUpdateContentPolicyAttach = getRouteFromHarUpdateContentPolicy("attach") +) + +func getUnrouteBehavior(in string) *UnrouteBehavior { + v := UnrouteBehavior(in) + return &v +} + +type UnrouteBehavior string + +var ( + UnrouteBehaviorWait *UnrouteBehavior = getUnrouteBehavior("wait") + UnrouteBehaviorIgnoreErrors = getUnrouteBehavior("ignoreErrors") + UnrouteBehaviorDefault = getUnrouteBehavior("default") +) + +func getMouseButton(in string) *MouseButton { + v := MouseButton(in) + return &v +} + +type MouseButton string + +var ( + MouseButtonLeft *MouseButton = getMouseButton("left") + MouseButtonRight = getMouseButton("right") + MouseButtonMiddle = getMouseButton("middle") +) + +func getKeyboardModifier(in string) *KeyboardModifier { + v := KeyboardModifier(in) + return &v +} + +type KeyboardModifier string + +var ( + KeyboardModifierAlt *KeyboardModifier = getKeyboardModifier("Alt") + KeyboardModifierControl = getKeyboardModifier("Control") + KeyboardModifierControlOrMeta = getKeyboardModifier("ControlOrMeta") + KeyboardModifierMeta = getKeyboardModifier("Meta") + KeyboardModifierShift = getKeyboardModifier("Shift") +) + +func getScreenshotAnimations(in string) *ScreenshotAnimations { + v := ScreenshotAnimations(in) + return &v +} + +type ScreenshotAnimations string + +var ( + ScreenshotAnimationsDisabled *ScreenshotAnimations = getScreenshotAnimations("disabled") + ScreenshotAnimationsAllow = getScreenshotAnimations("allow") +) + +func getScreenshotCaret(in string) *ScreenshotCaret { + v := ScreenshotCaret(in) + return &v +} + +type ScreenshotCaret string + +var ( + ScreenshotCaretHide *ScreenshotCaret = getScreenshotCaret("hide") + ScreenshotCaretInitial = getScreenshotCaret("initial") +) + +func getScreenshotScale(in string) *ScreenshotScale { + v := ScreenshotScale(in) + return &v +} + +type ScreenshotScale string + +var ( + ScreenshotScaleCss *ScreenshotScale = getScreenshotScale("css") + ScreenshotScaleDevice = getScreenshotScale("device") +) + +func getScreenshotType(in string) *ScreenshotType { + v := ScreenshotType(in) + return &v +} + +type ScreenshotType string + +var ( + ScreenshotTypePng *ScreenshotType = getScreenshotType("png") + ScreenshotTypeJpeg = getScreenshotType("jpeg") +) + +func getWaitForSelectorState(in string) *WaitForSelectorState { + v := WaitForSelectorState(in) + return &v +} + +type WaitForSelectorState string + +var ( + WaitForSelectorStateAttached *WaitForSelectorState = getWaitForSelectorState("attached") + WaitForSelectorStateDetached = getWaitForSelectorState("detached") + WaitForSelectorStateVisible = getWaitForSelectorState("visible") + WaitForSelectorStateHidden = getWaitForSelectorState("hidden") +) + +func getWaitUntilState(in string) *WaitUntilState { + v := WaitUntilState(in) + return &v +} + +type WaitUntilState string + +var ( + WaitUntilStateLoad *WaitUntilState = getWaitUntilState("load") + WaitUntilStateDomcontentloaded = getWaitUntilState("domcontentloaded") + WaitUntilStateNetworkidle = getWaitUntilState("networkidle") + WaitUntilStateCommit = getWaitUntilState("commit") +) + +func getLoadState(in string) *LoadState { + v := LoadState(in) + return &v +} + +type LoadState string + +var ( + LoadStateLoad *LoadState = getLoadState("load") + LoadStateDomcontentloaded = getLoadState("domcontentloaded") + LoadStateNetworkidle = getLoadState("networkidle") +) + +func getContrast(in string) *Contrast { + v := Contrast(in) + return &v +} + +type Contrast string + +var ( + ContrastNoPreference *Contrast = getContrast("no-preference") + ContrastMore = getContrast("more") + ContrastNoOverride = getContrast("no-override") +) + +func getMedia(in string) *Media { + v := Media(in) + return &v +} + +type Media string + +var ( + MediaScreen *Media = getMedia("screen") + MediaPrint = getMedia("print") + MediaNoOverride = getMedia("no-override") +) + +func getHttpCredentialsSend(in string) *HttpCredentialsSend { + v := HttpCredentialsSend(in) + return &v +} + +type HttpCredentialsSend string + +var ( + HttpCredentialsSendUnauthorized *HttpCredentialsSend = getHttpCredentialsSend("unauthorized") + HttpCredentialsSendAlways = getHttpCredentialsSend("always") +) diff --git a/vendor/github.com/playwright-community/playwright-go/generated-interfaces.go b/vendor/github.com/playwright-community/playwright-go/generated-interfaces.go new file mode 100644 index 0000000..187dc91 --- /dev/null +++ b/vendor/github.com/playwright-community/playwright-go/generated-interfaces.go @@ -0,0 +1,4658 @@ +package playwright + +// Exposes API that can be used for the Web API testing. This class is used for creating [APIRequestContext] instance +// which in turn can be used for sending web requests. An instance of this class can be obtained via +// [Playwright.Request]. For more information see [APIRequestContext]. +type APIRequest interface { + // Creates new instances of [APIRequestContext]. + NewContext(options ...APIRequestNewContextOptions) (APIRequestContext, error) +} + +// This API is used for the Web API testing. You can use it to trigger API endpoints, configure micro-services, +// prepare environment or the service to your e2e test. +// Each Playwright browser context has associated with it [APIRequestContext] instance which shares cookie storage +// with the browser context and can be accessed via [BrowserContext.Request] or [Page.Request]. It is also possible to +// create a new APIRequestContext instance manually by calling [APIRequest.NewContext]. +// **Cookie management** +// [APIRequestContext] returned by [BrowserContext.Request] and [Page.Request] shares cookie storage with the +// corresponding [BrowserContext]. Each API request will have `Cookie` header populated with the values from the +// browser context. If the API response contains `Set-Cookie` header it will automatically update [BrowserContext] +// cookies and requests made from the page will pick them up. This means that if you log in using this API, your e2e +// test will be logged in and vice versa. +// If you want API requests to not interfere with the browser cookies you should create a new [APIRequestContext] by +// calling [APIRequest.NewContext]. Such `APIRequestContext` object will have its own isolated cookie storage. +type APIRequestContext interface { + // Sends HTTP(S) [DELETE] request and returns its + // response. The method will populate request cookies from the context and update context cookies from the response. + // The method will automatically follow redirects. + // + // url: Target URL. + // + // [DELETE]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/DELETE + Delete(url string, options ...APIRequestContextDeleteOptions) (APIResponse, error) + + // All responses returned by [APIRequestContext.Get] and similar methods are stored in the memory, so that you can + // later call [APIResponse.Body].This method discards all its resources, calling any method on disposed + // [APIRequestContext] will throw an exception. + Dispose(options ...APIRequestContextDisposeOptions) error + + // Sends HTTP(S) request and returns its response. The method will populate request cookies from the context and + // update context cookies from the response. The method will automatically follow redirects. + // + // urlOrRequest: Target URL or Request to get all parameters from. + Fetch(urlOrRequest interface{}, options ...APIRequestContextFetchOptions) (APIResponse, error) + + // Sends HTTP(S) [GET] request and returns its + // response. The method will populate request cookies from the context and update context cookies from the response. + // The method will automatically follow redirects. + // + // url: Target URL. + // + // [GET]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/GET + Get(url string, options ...APIRequestContextGetOptions) (APIResponse, error) + + // Sends HTTP(S) [HEAD] request and returns its + // response. The method will populate request cookies from the context and update context cookies from the response. + // The method will automatically follow redirects. + // + // url: Target URL. + // + // [HEAD]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/HEAD + Head(url string, options ...APIRequestContextHeadOptions) (APIResponse, error) + + // Sends HTTP(S) [PATCH] request and returns its + // response. The method will populate request cookies from the context and update context cookies from the response. + // The method will automatically follow redirects. + // + // url: Target URL. + // + // [PATCH]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/PATCH + Patch(url string, options ...APIRequestContextPatchOptions) (APIResponse, error) + + // Sends HTTP(S) [POST] request and returns its + // response. The method will populate request cookies from the context and update context cookies from the response. + // The method will automatically follow redirects. + // + // url: Target URL. + // + // [POST]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/POST + Post(url string, options ...APIRequestContextPostOptions) (APIResponse, error) + + // Sends HTTP(S) [PUT] request and returns its + // response. The method will populate request cookies from the context and update context cookies from the response. + // The method will automatically follow redirects. + // + // url: Target URL. + // + // [PUT]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/PUT + Put(url string, options ...APIRequestContextPutOptions) (APIResponse, error) + + // Returns storage state for this request context, contains current cookies and local storage snapshot if it was + // passed to the constructor. + StorageState(path ...string) (*StorageState, error) +} + +// [APIResponse] class represents responses returned by [APIRequestContext.Get] and similar methods. +type APIResponse interface { + // Returns the buffer with response body. + Body() ([]byte, error) + + // Disposes the body of this response. If not called then the body will stay in memory until the context closes. + Dispose() error + + // An object with all the response HTTP headers associated with this response. + Headers() map[string]string + + // An array with all the response HTTP headers associated with this response. Header names are not lower-cased. + // Headers with multiple entries, such as `Set-Cookie`, appear in the array multiple times. + HeadersArray() []NameValue + + // Returns the JSON representation of response body. + // This method will throw if the response body is not parsable via `JSON.parse`. + JSON(v interface{}) error + + // Contains a boolean stating whether the response was successful (status in the range 200-299) or not. + Ok() bool + + // Contains the status code of the response (e.g., 200 for a success). + Status() int + + // Contains the status text of the response (e.g. usually an "OK" for a success). + StatusText() string + + // Returns the text representation of response body. + Text() (string, error) + + // Contains the URL of the response. + URL() string +} + +// The [APIResponseAssertions] class provides assertion methods that can be used to make assertions about the +// [APIResponse] in the tests. +type APIResponseAssertions interface { + // Makes the assertion check for the opposite condition. For example, this code tests that the response status is not + // successful: + Not() APIResponseAssertions + + // Ensures the response status code is within `200..299` range. + ToBeOK() error +} + +// A Browser is created via [BrowserType.Launch]. An example of using a [Browser] to create a [Page]: +type Browser interface { + EventEmitter + // Emitted when Browser gets disconnected from the browser application. This might happen because of one of the + // following: + // - Browser application is closed or crashed. + // - The [Browser.Close] method was called. + OnDisconnected(fn func(Browser)) + + // Get the browser type (chromium, firefox or webkit) that the browser belongs to. + BrowserType() BrowserType + + // In case this browser is obtained using [BrowserType.Launch], closes the browser and all of its pages (if any were + // opened). + // In case this browser is connected to, clears all created contexts belonging to this browser and disconnects from + // the browser server. + // **NOTE** This is similar to force-quitting the browser. To close pages gracefully and ensure you receive page close + // events, call [BrowserContext.Close] on any [BrowserContext] instances you explicitly created earlier using + // [Browser.NewContext] **before** calling [Browser.Close]. + // The [Browser] object itself is considered to be disposed and cannot be used anymore. + Close(options ...BrowserCloseOptions) error + + // Returns an array of all open browser contexts. In a newly created browser, this will return zero browser contexts. + Contexts() []BrowserContext + + // Indicates that the browser is connected. + IsConnected() bool + + // **NOTE** CDP Sessions are only supported on Chromium-based browsers. + // Returns the newly created browser session. + NewBrowserCDPSession() (CDPSession, error) + + // Creates a new browser context. It won't share cookies/cache with other browser contexts. + // **NOTE** If directly using this method to create [BrowserContext]s, it is best practice to explicitly close the + // returned context via [BrowserContext.Close] when your code is done with the [BrowserContext], and before calling + // [Browser.Close]. This will ensure the `context` is closed gracefully and any artifacts—like HARs and videos—are + // fully flushed and saved. + NewContext(options ...BrowserNewContextOptions) (BrowserContext, error) + + // Creates a new page in a new browser context. Closing this page will close the context as well. + // This is a convenience API that should only be used for the single-page scenarios and short snippets. Production + // code and testing frameworks should explicitly create [Browser.NewContext] followed by the [BrowserContext.NewPage] + // to control their exact life times. + NewPage(options ...BrowserNewPageOptions) (Page, error) + + // **NOTE** This API controls + // [Chromium Tracing] which is a low-level + // chromium-specific debugging tool. API to control [Playwright Tracing] could be found + // [here]. + // You can use [Browser.StartTracing] and [Browser.StopTracing] to create a trace file that can be opened in Chrome + // DevTools performance panel. + // + // [Chromium Tracing]: https://www.chromium.org/developers/how-tos/trace-event-profiling-tool + // [Playwright Tracing]: ../trace-viewer + // [here]: ./class-tracing + StartTracing(options ...BrowserStartTracingOptions) error + + // **NOTE** This API controls + // [Chromium Tracing] which is a low-level + // chromium-specific debugging tool. API to control [Playwright Tracing] could be found + // [here]. + // Returns the buffer with trace data. + // + // [Chromium Tracing]: https://www.chromium.org/developers/how-tos/trace-event-profiling-tool + // [Playwright Tracing]: ../trace-viewer + // [here]: ./class-tracing + StopTracing() ([]byte, error) + + // Returns the browser version. + Version() string +} + +// BrowserContexts provide a way to operate multiple independent browser sessions. +// If a page opens another page, e.g. with a `window.open` call, the popup will belong to the parent page's browser +// context. +// Playwright allows creating isolated non-persistent browser contexts with [Browser.NewContext] method. +// Non-persistent browser contexts don't write any browsing data to disk. +type BrowserContext interface { + EventEmitter + // **NOTE** Only works with Chromium browser's persistent context. + // Emitted when new background page is created in the context. + OnBackgroundPage(fn func(Page)) + + // Playwright has ability to mock clock and passage of time. + Clock() Clock + + // Emitted when Browser context gets closed. This might happen because of one of the following: + // - Browser context is closed. + // - Browser application is closed or crashed. + // - The [Browser.Close] method was called. + OnClose(fn func(BrowserContext)) + + // Emitted when JavaScript within the page calls one of console API methods, e.g. `console.log` or `console.dir`. + // The arguments passed into `console.log` and the page are available on the [ConsoleMessage] event handler argument. + OnConsole(fn func(ConsoleMessage)) + + // Emitted when a JavaScript dialog appears, such as `alert`, `prompt`, `confirm` or `beforeunload`. Listener **must** + // either [Dialog.Accept] or [Dialog.Dismiss] the dialog - otherwise the page will + // [freeze] waiting for the dialog, + // and actions like click will never finish. + // + // [freeze]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/EventLoop#never_blocking + OnDialog(fn func(Dialog)) + + // The event is emitted when a new Page is created in the BrowserContext. The page may still be loading. The event + // will also fire for popup pages. See also [Page.OnPopup] to receive events about popups relevant to a specific page. + // The earliest moment that page is available is when it has navigated to the initial url. For example, when opening a + // popup with `window.open('http://example.com')`, this event will fire when the network request to + // "http://example.com" is done and its response has started loading in the popup. If you would like to route/listen + // to this network request, use [BrowserContext.Route] and [BrowserContext.OnRequest] respectively instead of similar + // methods on the [Page]. + // **NOTE** Use [Page.WaitForLoadState] to wait until the page gets to a particular state (you should not need it in + // most cases). + OnPage(fn func(Page)) + + // Emitted when exception is unhandled in any of the pages in this context. To listen for errors from a particular + // page, use [Page.OnPageError] instead. + OnWebError(fn func(WebError)) + + // Emitted when a request is issued from any pages created through this context. The [request] object is read-only. To + // only listen for requests from a particular page, use [Page.OnRequest]. + // In order to intercept and mutate requests, see [BrowserContext.Route] or [Page.Route]. + OnRequest(fn func(Request)) + + // Emitted when a request fails, for example by timing out. To only listen for failed requests from a particular page, + // use [Page.OnRequestFailed]. + // **NOTE** HTTP Error responses, such as 404 or 503, are still successful responses from HTTP standpoint, so request + // will complete with [BrowserContext.OnRequestFinished] event and not with [BrowserContext.OnRequestFailed]. + OnRequestFailed(fn func(Request)) + + // Emitted when a request finishes successfully after downloading the response body. For a successful response, the + // sequence of events is `request`, `response` and `requestfinished`. To listen for successful requests from a + // particular page, use [Page.OnRequestFinished]. + OnRequestFinished(fn func(Request)) + + // Emitted when [response] status and headers are received for a request. For a successful response, the sequence of + // events is `request`, `response` and `requestfinished`. To listen for response events from a particular page, use + // [Page.OnResponse]. + OnResponse(fn func(Response)) + + // Adds cookies into this browser context. All pages within this context will have these cookies installed. Cookies + // can be obtained via [BrowserContext.Cookies]. + AddCookies(cookies []OptionalCookie) error + + // Adds a script which would be evaluated in one of the following scenarios: + // - Whenever a page is created in the browser context or is navigated. + // - Whenever a child frame is attached or navigated in any page in the browser context. In this case, the script is + // evaluated in the context of the newly attached frame. + // The script is evaluated after the document was created but before any of its scripts were run. This is useful to + // amend the JavaScript environment, e.g. to seed `Math.random`. + // + // script: Script to be evaluated in all pages in the browser context. + AddInitScript(script Script) error + + // **NOTE** Background pages are only supported on Chromium-based browsers. + // All existing background pages in the context. + BackgroundPages() []Page + + // Returns the browser instance of the context. If it was launched as a persistent context null gets returned. + Browser() Browser + + // Removes cookies from context. Accepts optional filter. + ClearCookies(options ...BrowserContextClearCookiesOptions) error + + // Clears all permission overrides for the browser context. + ClearPermissions() error + + // Closes the browser context. All the pages that belong to the browser context will be closed. + // **NOTE** The default browser context cannot be closed. + Close(options ...BrowserContextCloseOptions) error + + // If no URLs are specified, this method returns all cookies. If URLs are specified, only cookies that affect those + // URLs are returned. + Cookies(urls ...string) ([]Cookie, error) + + // The method adds a function called “[object Object]” on the `window` object of every frame in every page in the + // context. When called, the function executes “[object Object]” and returns a [Promise] which resolves to the return + // value of “[object Object]”. If the “[object Object]” returns a [Promise], it will be awaited. + // The first argument of the “[object Object]” function contains information about the caller: `{ browserContext: + // BrowserContext, page: Page, frame: Frame }`. + // See [Page.ExposeBinding] for page-only version. + // + // 1. name: Name of the function on the window object. + // 2. binding: Callback function that will be called in the Playwright's context. + ExposeBinding(name string, binding BindingCallFunction, handle ...bool) error + + // The method adds a function called “[object Object]” on the `window` object of every frame in every page in the + // context. When called, the function executes “[object Object]” and returns a [Promise] which resolves to the return + // value of “[object Object]”. + // If the “[object Object]” returns a [Promise], it will be awaited. + // See [Page.ExposeFunction] for page-only version. + // + // 1. name: Name of the function on the window object. + // 2. binding: Callback function that will be called in the Playwright's context. + ExposeFunction(name string, binding ExposedFunction) error + + // Grants specified permissions to the browser context. Only grants corresponding permissions to the given origin if + // specified. + // + // permissions: A list of permissions to grant. + // + // **NOTE** Supported permissions differ between browsers, and even between different versions of the same browser. + // Any permission may stop working after an update. + // + // Here are some permissions that may be supported by some browsers: + // - `'accelerometer'` + // - `'ambient-light-sensor'` + // - `'background-sync'` + // - `'camera'` + // - `'clipboard-read'` + // - `'clipboard-write'` + // - `'geolocation'` + // - `'gyroscope'` + // - `'magnetometer'` + // - `'microphone'` + // - `'midi-sysex'` (system-exclusive midi) + // - `'midi'` + // - `'notifications'` + // - `'payment-handler'` + // - `'storage-access'` + GrantPermissions(permissions []string, options ...BrowserContextGrantPermissionsOptions) error + + // **NOTE** CDP sessions are only supported on Chromium-based browsers. + // Returns the newly created session. + // + // page: Target to create new session for. For backwards-compatibility, this parameter is named `page`, but it can be a + // `Page` or `Frame` type. + NewCDPSession(page interface{}) (CDPSession, error) + + // Creates a new page in the browser context. + NewPage() (Page, error) + + // Returns all open pages in the context. + Pages() []Page + + // API testing helper associated with this context. Requests made with this API will use context cookies. + Request() APIRequestContext + + // Routing provides the capability to modify network requests that are made by any page in the browser context. Once + // route is enabled, every request matching the url pattern will stall unless it's continued, fulfilled or aborted. + // **NOTE** [BrowserContext.Route] will not intercept requests intercepted by Service Worker. See + // [this] issue. We recommend disabling Service Workers when + // using request interception by setting “[object Object]” to `block`. + // + // 1. url: A glob pattern, regex pattern, or predicate that receives a [URL] to match during routing. If “[object Object]” is + // set in the context options and the provided URL is a string that does not start with `*`, it is resolved using the + // [`new URL()`](https://developer.mozilla.org/en-US/docs/Web/API/URL/URL) constructor. + // 2. handler: handler function to route the request. + // + // [this]: https://github.com/microsoft/playwright/issues/1090 + Route(url interface{}, handler routeHandler, times ...int) error + + // If specified the network requests that are made in the context will be served from the HAR file. Read more about + // [Replaying from HAR]. + // Playwright will not serve requests intercepted by Service Worker from the HAR file. See + // [this] issue. We recommend disabling Service Workers when + // using request interception by setting “[object Object]” to `block`. + // + // har: Path to a [HAR](http://www.softwareishard.com/blog/har-12-spec) file with prerecorded network data. If `path` is a + // relative path, then it is resolved relative to the current working directory. + // + // [Replaying from HAR]: https://playwright.dev/docs/mock#replaying-from-har + // [this]: https://github.com/microsoft/playwright/issues/1090 + RouteFromHAR(har string, options ...BrowserContextRouteFromHAROptions) error + + // This method allows to modify websocket connections that are made by any page in the browser context. + // Note that only `WebSocket`s created after this method was called will be routed. It is recommended to call this + // method before creating any pages. + // + // 1. url: Only WebSockets with the url matching this pattern will be routed. A string pattern can be relative to the + // “[object Object]” context option. + // 2. handler: Handler function to route the WebSocket. + RouteWebSocket(url interface{}, handler func(WebSocketRoute)) error + + // **NOTE** Service workers are only supported on Chromium-based browsers. + // All existing service workers in the context. + ServiceWorkers() []Worker + + // This setting will change the default maximum navigation time for the following methods and related shortcuts: + // - [Page.GoBack] + // - [Page.GoForward] + // - [Page.Goto] + // - [Page.Reload] + // - [Page.SetContent] + // - [Page.ExpectNavigation] + // **NOTE** [Page.SetDefaultNavigationTimeout] and [Page.SetDefaultTimeout] take priority over + // [BrowserContext.SetDefaultNavigationTimeout]. + // + // timeout: Maximum navigation time in milliseconds + SetDefaultNavigationTimeout(timeout float64) + + // This setting will change the default maximum time for all the methods accepting “[object Object]” option. + // **NOTE** [Page.SetDefaultNavigationTimeout], [Page.SetDefaultTimeout] and + // [BrowserContext.SetDefaultNavigationTimeout] take priority over [BrowserContext.SetDefaultTimeout]. + // + // timeout: Maximum time in milliseconds. Pass `0` to disable timeout. + SetDefaultTimeout(timeout float64) + + // The extra HTTP headers will be sent with every request initiated by any page in the context. These headers are + // merged with page-specific extra HTTP headers set with [Page.SetExtraHTTPHeaders]. If page overrides a particular + // header, page-specific header value will be used instead of the browser context header value. + // **NOTE** [BrowserContext.SetExtraHTTPHeaders] does not guarantee the order of headers in the outgoing requests. + // + // headers: An object containing additional HTTP headers to be sent with every request. All header values must be strings. + SetExtraHTTPHeaders(headers map[string]string) error + + // Sets the context's geolocation. Passing `null` or `undefined` emulates position unavailable. + SetGeolocation(geolocation *Geolocation) error + + // + // offline: Whether to emulate network being offline for the browser context. + SetOffline(offline bool) error + + // Returns storage state for this browser context, contains current cookies, local storage snapshot and IndexedDB + // snapshot. + StorageState(path ...string) (*StorageState, error) + + Tracing() Tracing + + // Removes all routes created with [BrowserContext.Route] and [BrowserContext.RouteFromHAR]. + UnrouteAll(options ...BrowserContextUnrouteAllOptions) error + + // Removes a route created with [BrowserContext.Route]. When “[object Object]” is not specified, removes all routes + // for the “[object Object]”. + // + // 1. url: A glob pattern, regex pattern or predicate receiving [URL] used to register a routing with [BrowserContext.Route]. + // 2. handler: Optional handler function used to register a routing with [BrowserContext.Route]. + Unroute(url interface{}, handler ...routeHandler) error + + // Performs action and waits for a [ConsoleMessage] to be logged by in the pages in the context. If predicate is + // provided, it passes [ConsoleMessage] value into the `predicate` function and waits for `predicate(message)` to + // return a truthy value. Will throw an error if the page is closed before the [BrowserContext.OnConsole] event is + // fired. + ExpectConsoleMessage(cb func() error, options ...BrowserContextExpectConsoleMessageOptions) (ConsoleMessage, error) + + // Waits for event to fire and passes its value into the predicate function. Returns when the predicate returns truthy + // value. Will throw an error if the context closes before the event is fired. Returns the event data value. + // + // event: Event name, same one would pass into `browserContext.on(event)`. + ExpectEvent(event string, cb func() error, options ...BrowserContextExpectEventOptions) (interface{}, error) + + // Performs action and waits for a new [Page] to be created in the context. If predicate is provided, it passes [Page] + // value into the `predicate` function and waits for `predicate(event)` to return a truthy value. Will throw an error + // if the context closes before new [Page] is created. + ExpectPage(cb func() error, options ...BrowserContextExpectPageOptions) (Page, error) + + // **NOTE** In most cases, you should use [BrowserContext.ExpectEvent]. + // Waits for given `event` to fire. If predicate is provided, it passes event's value into the `predicate` function + // and waits for `predicate(event)` to return a truthy value. Will throw an error if the browser context is closed + // before the `event` is fired. + // + // event: Event name, same one typically passed into `*.on(event)`. + WaitForEvent(event string, options ...BrowserContextWaitForEventOptions) (interface{}, error) +} + +// BrowserType provides methods to launch a specific browser instance or connect to an existing one. The following is +// a typical example of using Playwright to drive automation: +type BrowserType interface { + // This method attaches Playwright to an existing browser instance created via `BrowserType.launchServer` in Node.js. + // **NOTE** The major and minor version of the Playwright instance that connects needs to match the version of + // Playwright that launches the browser (1.2.3 → is compatible with 1.2.x). + // + // wsEndpoint: A Playwright browser websocket endpoint to connect to. You obtain this endpoint via `BrowserServer.wsEndpoint`. + Connect(wsEndpoint string, options ...BrowserTypeConnectOptions) (Browser, error) + + // This method attaches Playwright to an existing browser instance using the Chrome DevTools Protocol. + // The default browser context is accessible via [Browser.Contexts]. + // **NOTE** Connecting over the Chrome DevTools Protocol is only supported for Chromium-based browsers. + // **NOTE** This connection is significantly lower fidelity than the Playwright protocol connection via + // [BrowserType.Connect]. If you are experiencing issues or attempting to use advanced functionality, you probably + // want to use [BrowserType.Connect]. + // + // endpointURL: A CDP websocket endpoint or http url to connect to. For example `http://localhost:9222/` or + // `ws://127.0.0.1:9222/devtools/browser/387adf4c-243f-4051-a181-46798f4a46f4`. + ConnectOverCDP(endpointURL string, options ...BrowserTypeConnectOverCDPOptions) (Browser, error) + + // A path where Playwright expects to find a bundled browser executable. + ExecutablePath() string + + // Returns the browser instance. + // + // [Chrome Canary]: https://www.google.com/chrome/browser/canary.html + // [Dev Channel]: https://www.chromium.org/getting-involved/dev-channel + // [this article]: https://www.howtogeek.com/202825/what%E2%80%99s-the-difference-between-chromium-and-chrome/ + // [This article]: https://chromium.googlesource.com/chromium/src/+/lkgr/docs/chromium_browser_vs_google_chrome.md + Launch(options ...BrowserTypeLaunchOptions) (Browser, error) + + // Returns the persistent browser context instance. + // Launches browser that uses persistent storage located at “[object Object]” and returns the only context. Closing + // this context will automatically close the browser. + // + // userDataDir: Path to a User Data Directory, which stores browser session data like cookies and local storage. Pass an empty + // string to create a temporary directory. + // + // More details for + // [Chromium](https://chromium.googlesource.com/chromium/src/+/master/docs/user_data_dir.md#introduction) and + // [Firefox](https://wiki.mozilla.org/Firefox/CommandLineOptions#User_profile). Chromium's user data directory is the + // **parent** directory of the "Profile Path" seen at `chrome://version`. + // + // Note that browsers do not allow launching multiple instances with the same User Data Directory. + LaunchPersistentContext(userDataDir string, options ...BrowserTypeLaunchPersistentContextOptions) (BrowserContext, error) + + // Returns browser name. For example: `chromium`, `webkit` or `firefox`. + Name() string +} + +// The `CDPSession` instances are used to talk raw Chrome Devtools Protocol: +// - protocol methods can be called with `session.send` method. +// - protocol events can be subscribed to with `session.on` method. +// +// Useful links: +// - Documentation on DevTools Protocol can be found here: +// [DevTools Protocol Viewer]. +// - Getting Started with DevTools Protocol: +// https://github.com/aslushnikov/getting-started-with-cdp/blob/master/README.md +// +// [DevTools Protocol Viewer]: https://chromedevtools.github.io/devtools-protocol/ +type CDPSession interface { + EventEmitter + // Detaches the CDPSession from the target. Once detached, the CDPSession object won't emit any events and can't be + // used to send messages. + Detach() error + + // + // 1. method: Protocol method name. + // 2. params: Optional method parameters. + Send(method string, params map[string]interface{}) (interface{}, error) +} + +// Accurately simulating time-dependent behavior is essential for verifying the correctness of applications. Learn +// more about [clock emulation]. +// Note that clock is installed for the entire [BrowserContext], so the time in all the pages and iframes is +// controlled by the same clock. +// +// [clock emulation]: https://playwright.dev/docs/clock +type Clock interface { + // Advance the clock by jumping forward in time. Only fires due timers at most once. This is equivalent to user + // closing the laptop lid for a while and reopening it later, after given time. + // + // ticks: Time may be the number of milliseconds to advance the clock by or a human-readable string. Valid string formats are + // "08" for eight seconds, "01:00" for one minute and "02:34:10" for two hours, 34 minutes and ten seconds. + FastForward(ticks interface{}) error + + // Install fake implementations for the following time-related functions: + // - `Date` + // - `setTimeout` + // - `clearTimeout` + // - `setInterval` + // - `clearInterval` + // - `requestAnimationFrame` + // - `cancelAnimationFrame` + // - `requestIdleCallback` + // - `cancelIdleCallback` + // - `performance` + // Fake timers are used to manually control the flow of time in tests. They allow you to advance time, fire timers, + // and control the behavior of time-dependent functions. See [Clock.RunFor] and [Clock.FastForward] for more + // information. + Install(options ...ClockInstallOptions) error + + // Advance the clock, firing all the time-related callbacks. + // + // ticks: Time may be the number of milliseconds to advance the clock by or a human-readable string. Valid string formats are + // "08" for eight seconds, "01:00" for one minute and "02:34:10" for two hours, 34 minutes and ten seconds. + RunFor(ticks interface{}) error + + // Advance the clock by jumping forward in time and pause the time. Once this method is called, no timers are fired + // unless [Clock.RunFor], [Clock.FastForward], [Clock.PauseAt] or [Clock.Resume] is called. + // Only fires due timers at most once. This is equivalent to user closing the laptop lid for a while and reopening it + // at the specified time and pausing. + // + // time: Time to pause at. + PauseAt(time interface{}) error + + // Resumes timers. Once this method is called, time resumes flowing, timers are fired as usual. + Resume() error + + // Makes `Date.now` and `new Date()` return fixed fake time at all times, keeps all the timers running. + // Use this method for simple scenarios where you only need to test with a predefined time. For more advanced + // scenarios, use [Clock.Install] instead. Read docs on [clock emulation] to learn more. + // + // time: Time to be set. + // + // [clock emulation]: https://playwright.dev/docs/clock + SetFixedTime(time interface{}) error + + // Sets system time, but does not trigger any timers. Use this to test how the web page reacts to a time shift, for + // example switching from summer to winter time, or changing time zones. + // + // time: Time to be set. + SetSystemTime(time interface{}) error +} + +// [ConsoleMessage] objects are dispatched by page via the [Page.OnConsole] event. For each console message logged in +// the page there will be corresponding event in the Playwright context. +type ConsoleMessage interface { + // List of arguments passed to a `console` function call. See also [Page.OnConsole]. + Args() []JSHandle + + Location() *ConsoleMessageLocation + + // The page that produced this console message, if any. + Page() Page + + // The text of the console message. + Text() string + + // The text of the console message. + String() string + + // One of the following values: `log`, `debug`, `info`, `error`, `warning`, `dir`, `dirxml`, `table`, + // `trace`, `clear`, `startGroup`, `startGroupCollapsed`, `endGroup`, `assert`, `profile`, + // `profileEnd`, `count`, `timeEnd`. + Type() string +} + +// [Dialog] objects are dispatched by page via the [Page.OnDialog] event. +// An example of using `Dialog` class: +// **NOTE** Dialogs are dismissed automatically, unless there is a [Page.OnDialog] listener. When listener is present, +// it **must** either [Dialog.Accept] or [Dialog.Dismiss] the dialog - otherwise the page will +// [freeze] waiting for the dialog, +// and actions like click will never finish. +// +// [freeze]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/EventLoop#never_blocking +type Dialog interface { + // Returns when the dialog has been accepted. + Accept(promptText ...string) error + + // If dialog is prompt, returns default prompt value. Otherwise, returns empty string. + DefaultValue() string + + // Returns when the dialog has been dismissed. + Dismiss() error + + // A message displayed in the dialog. + Message() string + + // The page that initiated this dialog, if available. + Page() Page + + // Returns dialog's type, can be one of `alert`, `beforeunload`, `confirm` or `prompt`. + Type() string +} + +// [Download] objects are dispatched by page via the [Page.OnDownload] event. +// All the downloaded files belonging to the browser context are deleted when the browser context is closed. +// Download event is emitted once the download starts. Download path becomes available once download completes. +type Download interface { + // Cancels a download. Will not fail if the download is already finished or canceled. Upon successful cancellations, + // `download.failure()` would resolve to `canceled`. + Cancel() error + + // Deletes the downloaded file. Will wait for the download to finish if necessary. + Delete() error + + // Returns download error if any. Will wait for the download to finish if necessary. + Failure() error + + // Get the page that the download belongs to. + Page() Page + + // Returns path to the downloaded file for a successful download, or throws for a failed/canceled download. The method + // will wait for the download to finish if necessary. The method throws when connected remotely. + // Note that the download's file name is a random GUID, use [Download.SuggestedFilename] to get suggested file name. + Path() (string, error) + + // Copy the download to a user-specified path. It is safe to call this method while the download is still in progress. + // Will wait for the download to finish if necessary. + // + // path: Path where the download should be copied. + SaveAs(path string) error + + // Returns suggested filename for this download. It is typically computed by the browser from the + // [`Content-Disposition`] response + // header or the `download` attribute. See the spec on [whatwg]. + // Different browsers can use different logic for computing it. + // + // [`Content-Disposition`]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Disposition + // [whatwg]: https://html.spec.whatwg.org/#downloading-resources + SuggestedFilename() string + + // Returns downloaded url. + URL() string + + String() string +} + +// ElementHandle represents an in-page DOM element. ElementHandles can be created with the [Page.QuerySelector] +// +// method. +// **NOTE** The use of ElementHandle is discouraged, use [Locator] objects and web-first assertions instead. +// ElementHandle prevents DOM element from garbage collection unless the handle is disposed with [JSHandle.Dispose]. +// ElementHandles are auto-disposed when their origin frame gets navigated. +// ElementHandle instances can be used as an argument in [Page.EvalOnSelector] and [Page.Evaluate] methods. +// The difference between the [Locator] and ElementHandle is that the ElementHandle points to a particular element, +// while [Locator] captures the logic of how to retrieve an element. +// In the example below, handle points to a particular DOM element on page. If that element changes text or is used by +// React to render an entirely different component, handle is still pointing to that very DOM element. This can lead +// to unexpected behaviors. +// With the locator, every time the `element` is used, up-to-date DOM element is located in the page using the +// selector. So in the snippet below, underlying DOM element is going to be located twice. +type ElementHandle interface { + JSHandle + // This method returns the bounding box of the element, or `null` if the element is not visible. The bounding box is + // calculated relative to the main frame viewport - which is usually the same as the browser window. + // Scrolling affects the returned bounding box, similarly to + // [Element.GetBoundingClientRect]. + // That means `x` and/or `y` may be negative. + // Elements from child frames return the bounding box relative to the main frame, unlike the + // [Element.GetBoundingClientRect]. + // Assuming the page is static, it is safe to use bounding box coordinates to perform input. For example, the + // following snippet should click the center of the element. + // + // [Element.GetBoundingClientRect]: https://developer.mozilla.org/en-US/docs/Web/API/Element/getBoundingClientRect + // [Element.GetBoundingClientRect]: https://developer.mozilla.org/en-US/docs/Web/API/Element/getBoundingClientRect + BoundingBox() (*Rect, error) + + // This method checks the element by performing the following steps: + // 1. Ensure that element is a checkbox or a radio input. If not, this method throws. If the element is already + // checked, this method returns immediately. + // 2. Wait for [actionability] checks on the element, unless “[object Object]” option is set. + // 3. Scroll the element into view if needed. + // 4. Use [Page.Mouse] to click in the center of the element. + // 5. Ensure that the element is now checked. If not, this method throws. + // If the element is detached from the DOM at any moment during the action, this method throws. + // When all steps combined have not finished during the specified “[object Object]”, this method throws a + // [TimeoutError]. Passing zero timeout disables this. + // + // Deprecated: Use locator-based [Locator.Check] instead. Read more about [locators]. + // + // [actionability]: https://playwright.dev/docs/actionability + // [locators]: https://playwright.dev/docs/locators + Check(options ...ElementHandleCheckOptions) error + + // This method clicks the element by performing the following steps: + // 1. Wait for [actionability] checks on the element, unless “[object Object]” option is set. + // 2. Scroll the element into view if needed. + // 3. Use [Page.Mouse] to click in the center of the element, or the specified “[object Object]”. + // 4. Wait for initiated navigations to either succeed or fail, unless “[object Object]” option is set. + // If the element is detached from the DOM at any moment during the action, this method throws. + // When all steps combined have not finished during the specified “[object Object]”, this method throws a + // [TimeoutError]. Passing zero timeout disables this. + // + // Deprecated: Use locator-based [Locator.Click] instead. Read more about [locators]. + // + // [actionability]: https://playwright.dev/docs/actionability + // [locators]: https://playwright.dev/docs/locators + Click(options ...ElementHandleClickOptions) error + + // Returns the content frame for element handles referencing iframe nodes, or `null` otherwise + ContentFrame() (Frame, error) + + // This method double clicks the element by performing the following steps: + // 1. Wait for [actionability] checks on the element, unless “[object Object]” option is set. + // 2. Scroll the element into view if needed. + // 3. Use [Page.Mouse] to double click in the center of the element, or the specified “[object Object]”. + // If the element is detached from the DOM at any moment during the action, this method throws. + // When all steps combined have not finished during the specified “[object Object]”, this method throws a + // [TimeoutError]. Passing zero timeout disables this. + // **NOTE** `elementHandle.dblclick()` dispatches two `click` events and a single `dblclick` event. + // + // Deprecated: Use locator-based [Locator.Dblclick] instead. Read more about [locators]. + // + // [actionability]: https://playwright.dev/docs/actionability + // [locators]: https://playwright.dev/docs/locators + Dblclick(options ...ElementHandleDblclickOptions) error + + // The snippet below dispatches the `click` event on the element. Regardless of the visibility state of the element, + // `click` is dispatched. This is equivalent to calling + // [element.Click()]. + // + // Deprecated: Use locator-based [Locator.DispatchEvent] instead. Read more about [locators]. + // + // 1. typ: DOM event type: `"click"`, `"dragstart"`, etc. + // 2. eventInit: Optional event-specific initialization properties. + // + // [element.Click()]: https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/click + // [DeviceMotionEvent]: https://developer.mozilla.org/en-US/docs/Web/API/DeviceMotionEvent/DeviceMotionEvent + // [DeviceOrientationEvent]: https://developer.mozilla.org/en-US/docs/Web/API/DeviceOrientationEvent/DeviceOrientationEvent + // [DragEvent]: https://developer.mozilla.org/en-US/docs/Web/API/DragEvent/DragEvent + // [Event]: https://developer.mozilla.org/en-US/docs/Web/API/Event/Event + // [FocusEvent]: https://developer.mozilla.org/en-US/docs/Web/API/FocusEvent/FocusEvent + // [KeyboardEvent]: https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/KeyboardEvent + // [MouseEvent]: https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/MouseEvent + // [PointerEvent]: https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/PointerEvent + // [TouchEvent]: https://developer.mozilla.org/en-US/docs/Web/API/TouchEvent/TouchEvent + // [WheelEvent]: https://developer.mozilla.org/en-US/docs/Web/API/WheelEvent/WheelEvent + // [locators]: https://playwright.dev/docs/locators + DispatchEvent(typ string, eventInit ...interface{}) error + + // Returns the return value of “[object Object]”. + // The method finds an element matching the specified selector in the `ElementHandle`s subtree and passes it as a + // first argument to “[object Object]”. If no elements match the selector, the method throws an error. + // If “[object Object]” returns a [Promise], then [ElementHandle.EvalOnSelector] would wait for the promise to resolve + // and return its value. + // + // Deprecated: This method does not wait for the element to pass actionability checks and therefore can lead to the flaky tests. Use [Locator.Evaluate], other [Locator] helper methods or web-first assertions instead. + // + // 1. selector: A selector to query for. + // 2. expression: JavaScript expression to be evaluated in the browser context. If the expression evaluates to a function, the + // function is automatically invoked. + // 3. arg: Optional argument to pass to “[object Object]”. + EvalOnSelector(selector string, expression string, arg ...interface{}) (interface{}, error) + + // Returns the return value of “[object Object]”. + // The method finds all elements matching the specified selector in the `ElementHandle`'s subtree and passes an array + // of matched elements as a first argument to “[object Object]”. + // If “[object Object]” returns a [Promise], then [ElementHandle.EvalOnSelectorAll] would wait for the promise to + // resolve and return its value. + // + // Deprecated: In most cases, [Locator.EvaluateAll], other [Locator] helper methods and web-first assertions do a better job. + // + // 1. selector: A selector to query for. + // 2. expression: JavaScript expression to be evaluated in the browser context. If the expression evaluates to a function, the + // function is automatically invoked. + // 3. arg: Optional argument to pass to “[object Object]”. + EvalOnSelectorAll(selector string, expression string, arg ...interface{}) (interface{}, error) + + // This method waits for [actionability] checks, focuses the element, fills it and triggers an + // `input` event after filling. Note that you can pass an empty string to clear the input field. + // If the target element is not an ``, `