Skip to content

Commit

Permalink
make the eval consistent with function definition
Browse files Browse the repository at this point in the history
  • Loading branch information
ysmood committed Mar 20, 2022
1 parent 3e6eca4 commit edc1d6d
Show file tree
Hide file tree
Showing 13 changed files with 104 additions and 170 deletions.
2 changes: 1 addition & 1 deletion browser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ func (t T) BrowserWaitEvent() {
func (t T) BrowserCrash() {
browser := rod.New().Context(t.Context()).MustConnect()
page := browser.MustPage()
js := `new Promise(r => setTimeout(r, 10000))`
js := `() => new Promise(r => setTimeout(r, 10000))`

go t.Panic(func() {
page.MustEval(js)
Expand Down
2 changes: 1 addition & 1 deletion dev_helpers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,5 +93,5 @@ func (t T) ExposeHelpers() {
p := t.newPage(t.srcFile("fixtures/click.html"))
p.ExposeHelpers(js.ElementR)

t.Eq(p.MustElementByJS(`rod.elementR('button', 'click me')`).MustText(), "click me")
t.Eq(p.MustElementByJS(`() => rod.elementR('button', 'click me')`).MustText(), "click me")
}
20 changes: 10 additions & 10 deletions element.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ func (el *Element) Focus() error {
return err
}

_, err = el.Evaluate(Eval(`this.focus()`).ByUser())
_, err = el.Evaluate(Eval(`() => this.focus()`).ByUser())
return err
}

Expand Down Expand Up @@ -143,7 +143,7 @@ func (el *Element) Tap() error {
// The cursor can be mouse, finger, stylus, etc.
// If not interactable err will be ErrNotInteractable, such as when covered by a modal,
func (el *Element) Interactable() (pt *proto.Point, err error) {
noPointerEvents, err := el.Eval(`getComputedStyle(this).pointerEvents === 'none'`)
noPointerEvents, err := el.Eval(`() => getComputedStyle(this).pointerEvents === 'none'`)
if err != nil {
return nil, err
}
Expand All @@ -167,7 +167,7 @@ func (el *Element) Interactable() (pt *proto.Point, err error) {
return
}

scroll, err := el.page.root.Eval(`{ x: window.scrollX, y: window.scrollY }`)
scroll, err := el.page.root.Eval(`() => ({ x: window.scrollX, y: window.scrollY })`)
if err != nil {
return
}
Expand Down Expand Up @@ -302,7 +302,7 @@ func (el *Element) InputTime(t time.Time) error {

// Blur is similar to the method Blur
func (el *Element) Blur() error {
_, err := el.Evaluate(Eval("this.blur()").ByUser())
_, err := el.Evaluate(Eval("() => this.blur()").ByUser())
return err
}

Expand Down Expand Up @@ -591,14 +591,14 @@ func (el *Element) WaitVisible() error {
// Doc for readonly: https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/readonly
func (el *Element) WaitEnabled() error {
defer el.tryTrace(TraceTypeWait, "enabled")()
return el.Wait(Eval(`!this.disabled`))
return el.Wait(Eval(`() => !this.disabled`))
}

// WaitWritable until the element is not readonly.
// Doc for disabled: https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/disabled
func (el *Element) WaitWritable() error {
defer el.tryTrace(TraceTypeWait, "writable")()
return el.Wait(Eval(`!this.readonly`))
return el.Wait(Eval(`() => !this.readonly`))
}

// WaitInvisible until the element invisible
Expand Down Expand Up @@ -633,7 +633,7 @@ func (el *Element) Resource() ([]byte, error) {

// BackgroundImage returns the css background-image of the element
func (el *Element) BackgroundImage() ([]byte, error) {
res, err := el.Eval(`window.getComputedStyle(this).backgroundImage.replace(/^url\("/, '').replace(/"\)$/, '')`)
res, err := el.Eval(`() => window.getComputedStyle(this).backgroundImage.replace(/^url\("/, '').replace(/"\)$/, '')`)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -684,7 +684,7 @@ func (el *Element) Release() error {

// Remove the element from the page
func (el *Element) Remove() error {
_, err := el.Eval(`this.remove()`)
_, err := el.Eval(`() => this.remove()`)
if err != nil {
return err
}
Expand All @@ -696,9 +696,9 @@ func (el *Element) Call(ctx context.Context, sessionID, methodName string, param
return el.page.Call(ctx, sessionID, methodName, params)
}

// Eval js on the page. For more info check the Element.Evaluate
// Eval is a shortcut for Element.Evaluate with AwaitPromise, ByValue and AutoExp set to true.
func (el *Element) Eval(js string, params ...interface{}) (*proto.RuntimeRemoteObject, error) {
return el.Evaluate(Eval(js, params...))
return el.Evaluate(Eval(js, params...).ByPromise())
}

// Evaluate is just a shortcut of Page.Evaluate with This set to current element.
Expand Down
28 changes: 14 additions & 14 deletions element_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -177,9 +177,9 @@ func (t T) WaitInteractable() {
func (t T) Hover() {
p := t.page.MustNavigate(t.srcFile("fixtures/click.html"))
el := p.MustElement("button")
el.MustEval(`this.onmouseenter = () => this.dataset['a'] = 1`)
el.MustEval(`() => this.onmouseenter = () => this.dataset['a'] = 1`)
el.MustHover()
t.Eq("1", el.MustEval(`this.dataset['a']`).String())
t.Eq("1", el.MustEval(`() => this.dataset['a']`).String())

t.mc.stubErr(1, proto.DOMScrollIntoViewIfNeeded{})
t.Err(el.Hover())
Expand All @@ -197,7 +197,7 @@ func (t T) Hover() {
func (t T) ElementMoveMouseOut() {
p := t.page.MustNavigate(t.srcFile("fixtures/click.html"))
btn := p.MustElement("button")
btn.MustEval(`this.onmouseout = () => this.setAttribute('name', 'mouse moved.')`)
btn.MustEval(`() => this.onmouseout = () => this.setAttribute('name', 'mouse moved.')`)
t.Eq("mouse moved.", *btn.MustHover().MustMoveMouseOut().MustAttribute("name"))

t.mc.stubErr(1, proto.DOMGetContentQuads{})
Expand Down Expand Up @@ -227,7 +227,7 @@ func (t T) Iframes() {
el := frame02.MustElement("button")
el.MustClick()

t.Eq(frame01.MustEval(`testIsolation()`).Str(), "ok")
t.Eq(frame01.MustEval(`() => testIsolation()`).Str(), "ok")
t.True(frame02.MustHas("[a=ok]"))
}

Expand Down Expand Up @@ -429,7 +429,7 @@ func (t T) SelectQuery() {
err := el.Select([]string{`[value="c"]`}, true, rod.SelectorTypeCSSSector)
t.E(err)

t.Eq(2, el.MustEval("this.selectedIndex").Int())
t.Eq(2, el.MustEval("() => this.selectedIndex").Int())
}

func (t T) SelectOptions() {
Expand Down Expand Up @@ -520,7 +520,7 @@ func (t T) SetFiles() {
slash("fixtures/alert.html"),
)

list := el.MustEval("Array.from(this.files).map(f => f.name)").Arr()
list := el.MustEval("() => Array.from(this.files).map(f => f.name)").Arr()
t.Len(list, 2)
t.Eq("alert.html", list[1].String())
}
Expand All @@ -544,9 +544,9 @@ func (t T) WaitInvisible() {

go func() {
utils.Sleep(0.03)
h4.MustEval(`this.remove()`)
h4.MustEval(`() => this.remove()`)
utils.Sleep(0.03)
btn.MustEval(`this.style.visibility = 'hidden'`)
btn.MustEval(`() => this.style.visibility = 'hidden'`)
}()

h4.MustWaitInvisible()
Expand All @@ -568,7 +568,7 @@ func (t T) WaitWritable() {
func (t T) WaitStable() {
p := t.page.MustNavigate(t.srcFile("fixtures/wait-stable.html"))
el := p.MustElement("button")
el.MustEval(`this.classList.add("play")`)
el.MustEval(`() => this.classList.add("play")`)
start := time.Now()
el.MustWaitStable()
t.Gt(time.Since(start), time.Second)
Expand Down Expand Up @@ -596,7 +596,7 @@ func (t T) WaitStable() {
func (t T) WaitStableRAP() {
p := t.page.MustNavigate(t.srcFile("fixtures/wait-stable.html"))
el := p.MustElement("button")
el.MustEval(`this.classList.add("play")`)
el.MustEval(`() => this.classList.add("play")`)
start := time.Now()
t.E(el.WaitStableRAF())
t.Gt(time.Since(start), time.Second)
Expand Down Expand Up @@ -723,7 +723,7 @@ func (t T) FnErr() {
t.True(errors.As(err, &e))
t.Eq(proto.RuntimeRemoteObjectSubtypeError, e.Exception.Subtype)

_, err = el.ElementByJS(rod.Eval("foo()"))
_, err = el.ElementByJS(rod.Eval("() => foo()"))
t.Err(err)
t.Has(err.Error(), "ReferenceError: foo is not defined")
t.True(errors.Is(err, &rod.ErrEval{}))
Expand All @@ -749,9 +749,9 @@ func (t T) ElementOthers() {
el.MustScrollIntoView()
t.Eq("submit", el.MustElement("[type=submit]").MustText())
t.Eq("<input type=\"submit\" value=\"submit\">", el.MustElement("[type=submit]").MustHTML())
el.MustWait(`true`)
t.Eq("form", el.MustElementByJS(`this`).MustDescribe().LocalName)
t.Len(el.MustElementsByJS(`[]`), 0)
el.MustWait(`() => true`)
t.Eq("form", el.MustElementByJS(`() => this`).MustDescribe().LocalName)
t.Len(el.MustElementsByJS(`() => []`), 0)
}

func (t T) ElementEqual() {
Expand Down
12 changes: 6 additions & 6 deletions examples_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,13 +43,13 @@ func Example() {
fmt.Println("Found", len(page.MustElements("input")), "input elements")

// Eval js on the page
page.MustEval(`console.log("hello world")`)
page.MustEval(`() => console.log("hello world")`)

// Pass parameters as json objects to the js function. This MustEval will result 3
fmt.Println("1 + 2 =", page.MustEval(`(a, b) => a + b`, 1, 2).Int())

// When eval on an element, "this" in the js is the current DOM element.
fmt.Println(page.MustElement("title").MustEval(`this.innerText`).String())
fmt.Println(page.MustElement("title").MustEval(`() => this.innerText`).String())

// Output:
// Git is the most widely used version control system.
Expand Down Expand Up @@ -433,7 +433,7 @@ func Example_handle_events() {
}
}

page.MustEval(`console.log("hello", "world")`)
page.MustEval(`() => console.log("hello", "world")`)

<-done

Expand Down Expand Up @@ -483,7 +483,7 @@ func Example_hijack_requests() {

go router.Run()

browser.MustPage("https://go-rod.github.io").MustWait(`document.title === 'hi'`)
browser.MustPage("https://go-rod.github.io").MustWait(`() => document.title === 'hi'`)

fmt.Println("done")

Expand All @@ -494,7 +494,7 @@ func Example_hijack_requests() {
func Example_eval_reuse_remote_object() {
page := rod.New().MustConnect().MustPage()

fn := page.MustEvaluate(rod.Eval(`Math.random`).ByObject())
fn := page.MustEvaluate(rod.Eval(`() => Math.random`).ByObject())

res := page.MustEval(`f => f()`, fn)

Expand Down Expand Up @@ -580,7 +580,7 @@ func Example_load_extension() {

page := rod.New().ControlURL(u).MustConnect().MustPage("http://mdn.dev")

page.MustWait(`document.title === 'test-extension'`)
page.MustWait(`() => document.title === 'test-extension'`)

fmt.Println("ok")

Expand Down
2 changes: 1 addition & 1 deletion hijack_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,7 @@ func (t T) HijackFailRequest() {

t.page.MustNavigate(s.URL("/page")).MustWaitLoad()

t.page.MustWait(`document.title === 'Failed to fetch'`)
t.page.MustWait(`() => document.title === 'Failed to fetch'`)

{ // test error log
t.mc.stub(1, proto.FetchFailRequest{}, func(send StubSend) (gson.JSON, error) {
Expand Down
8 changes: 4 additions & 4 deletions page.go
Original file line number Diff line number Diff line change
Expand Up @@ -173,14 +173,14 @@ func (p *Page) Navigate(url string) error {
// NavigateBack history.
func (p *Page) NavigateBack() error {
// Not using cdp API because it doesn't work for iframe
_, err := p.Evaluate(Eval(`history.back()`).ByUser())
_, err := p.Evaluate(Eval(`() => history.back()`).ByUser())
return err
}

// NavigateForward history.
func (p *Page) NavigateForward() error {
// Not using cdp API because it doesn't work for iframe
_, err := p.Evaluate(Eval(`history.forward()`).ByUser())
_, err := p.Evaluate(Eval(`() => history.forward()`).ByUser())
return err
}

Expand All @@ -194,7 +194,7 @@ func (p *Page) Reload() error {
})

// Not using cdp API because it doesn't work for iframe
_, err := p.Evaluate(Eval(`location.reload()`).ByUser())
_, err := p.Evaluate(Eval(`() => location.reload()`).ByUser())
if err != nil {
return err
}
Expand Down Expand Up @@ -542,7 +542,7 @@ func (p *Page) WaitIdle(timeout time.Duration) (err error) {
// Doc: https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame
func (p *Page) WaitRepaint() error {
// we use root here because iframe doesn't trigger requestAnimationFrame
_, err := p.root.Eval(`new Promise(r => requestAnimationFrame(r))`)
_, err := p.root.Eval(`() => new Promise(r => requestAnimationFrame(r))`)
return err
}

Expand Down
37 changes: 12 additions & 25 deletions page_eval.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,31 +26,22 @@ type EvalOptions struct {
// ThisObj represents the "this" object in the JS
ThisObj *proto.RuntimeRemoteObject

// JS code to eval. It can be an expression or function definition. If it's a function definition
// the function will be executed with the JSArgs. Such as
// 1 + 2
// is the same as
// () => 1 + 2
// or
// function() {
// return 1 + 2
// }
// JS function definition to execute.
JS string

// JSArgs represents the arguments in the JS if the JS is a function definition.
// JSArgs represents the arguments that will be passed to JS.
// If an argument is *proto.RuntimeRemoteObject type, the corresponding remote object will be used.
// Or it will be passed as a plain JSON value.
// When an arg in the args is a *js.Function, the arg will be cached on the page's js context.
// When the arg.Name exists in the page's cache, it reuse the cache without sending the definition to the browser again.
// Useful when you need to eval a huge js expression many times.
JSArgs []interface{}

// Whether execution should be treated as initiated by user in the UI.
UserGesture bool
}

// Eval creates a EvalOptions with ByValue set to true.
//
// When an arg in the args is a *js.Function, the arg will be cached on the page's js context.
// When the arg.Name exists in the page's cache, it reuse the cache without sending the definition to the browser again.
// Useful when you need to eval a huge js expression many times.
func Eval(js string, args ...interface{}) *EvalOptions {
return &EvalOptions{
ByValue: true,
Expand All @@ -66,7 +57,7 @@ func evalHelper(fn *js.Function, args ...interface{}) *EvalOptions {
return &EvalOptions{
ByValue: true,
JSArgs: append([]interface{}{fn}, args...),
JS: `(f, ...args) => f.apply(this, args)`,
JS: `function (f, ...args) { return f.apply(this, args) }`,
}
}

Expand Down Expand Up @@ -119,15 +110,12 @@ func (e *EvalOptions) ByPromise() *EvalOptions {

func (e *EvalOptions) formatToJSFunc() string {
js := strings.TrimSpace(e.JS)
if detectJSFunction(js) {
return fmt.Sprintf(`function() { return (%s).apply(this, arguments) }`, js)
}
return fmt.Sprintf(`function() { return %s }`, js)
return fmt.Sprintf(`function() { return (%s).apply(this, arguments) }`, js)
}

// Eval is just a shortcut for Page.Evaluate with AwaitPromise set true.
func (p *Page) Eval(js string, jsArgs ...interface{}) (*proto.RuntimeRemoteObject, error) {
return p.Evaluate(Eval(js, jsArgs...).ByPromise())
// Eval is a shortcut for Page.Evaluate with AwaitPromise, ByValue set to true.
func (p *Page) Eval(js string, args ...interface{}) (*proto.RuntimeRemoteObject, error) {
return p.Evaluate(Eval(js, args...).ByPromise())
}

// Evaluate js on the page.
Expand Down Expand Up @@ -202,13 +190,12 @@ func (p *Page) Expose(name string, fn func(gson.JSON) (interface{}, error)) (sto
return
}

code := fmt.Sprintf(`(%s)("%s", "%s")`, js.ExposeFunc.Definition, name, bind)

_, err = p.Evaluate(Eval(code))
_, err = p.Evaluate(Eval(js.ExposeFunc.Definition, name, bind))
if err != nil {
return
}

code := fmt.Sprintf(`(%s)("%s", "%s")`, js.ExposeFunc.Definition, name, bind)
remove, err := p.EvalOnNewDocument(code)
if err != nil {
return
Expand Down
Loading

0 comments on commit edc1d6d

Please sign in to comment.