Skip to content

Commit

Permalink
refactor the way Rod simulates keyboard inputs
Browse files Browse the repository at this point in the history
fix #610
  • Loading branch information
ysmood committed May 26, 2022
1 parent 386ee20 commit 938f47b
Show file tree
Hide file tree
Showing 19 changed files with 1,022 additions and 804 deletions.
4 changes: 1 addition & 3 deletions browser.go
Original file line number Diff line number Diff line change
Expand Up @@ -289,9 +289,7 @@ func (b *Browser) PageFromTarget(targetID proto.TargetTargetID) (*Page, error) {
}

page.root = page
page.Mouse = &Mouse{page: page, id: utils.RandString(8)}
page.Keyboard = &Keyboard{page: page}
page.Touch = &Touch{page: page}
page.newKeyboard().newMouse().newTouch()

if !b.defaultDevice.IsClear() {
err = page.Emulate(b.defaultDevice)
Expand Down
19 changes: 15 additions & 4 deletions element.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"github.com/ysmood/gson"

"github.com/go-rod/rod/lib/cdp"
"github.com/go-rod/rod/lib/input"
"github.com/go-rod/rod/lib/js"
"github.com/go-rod/rod/lib/proto"
"github.com/go-rod/rod/lib/utils"
Expand Down Expand Up @@ -206,15 +207,25 @@ func (el *Element) Shape() (*proto.DOMGetContentQuadsResult, error) {
return proto.DOMGetContentQuads{ObjectID: el.id()}.Call(el)
}

// Press is similar with Keyboard.Press.
// Type is similar with Keyboard.Type.
// Before the action, it will try to scroll to the element and focus on it.
func (el *Element) Press(keys ...rune) error {
func (el *Element) Type(keys ...input.Key) error {
err := el.Focus()
if err != nil {
return err
}
return el.page.Keyboard.Type(keys...)
}

// KeyActions is similar with Page.KeyActions.
// Before the action, it will try to scroll to the element and focus on it.
func (el *Element) KeyActions() (*KeyActions, error) {
err := el.Focus()
if err != nil {
return nil, err
}

return el.page.Keyboard.Press(keys...)
return el.page.KeyActions(), nil
}

// SelectText selects the text that matches the regular expression.
Expand Down Expand Up @@ -266,7 +277,7 @@ func (el *Element) Input(text string) error {
return err
}

err = el.page.Keyboard.InsertText(text)
err = el.page.InsertText(text)
_, _ = el.Evaluate(evalHelper(js.InputEvent).ByUser())
return err
}
Expand Down
100 changes: 9 additions & 91 deletions element_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -227,14 +227,6 @@ func TestElementMoveMouseOut(t *testing.T) {
g.Err(btn.MoveMouseOut())
}

func TestMouseMoveErr(t *testing.T) {
g := setup(t)

p := g.page.MustNavigate(g.srcFile("fixtures/click.html"))
g.mc.stubErr(1, proto.InputDispatchMouseEvent{})
g.Err(p.Mouse.Move(10, 10, 1))
}

func TestElementContext(t *testing.T) {
g := setup(t)

Expand Down Expand Up @@ -294,87 +286,6 @@ func TestShadowDOM(t *testing.T) {
})
}

func TestPress(t *testing.T) {
g := setup(t)

p := g.page.MustNavigate(g.srcFile("fixtures/input.html"))
el := p.MustElement("[type=text]")

el.MustPress('1', '2', input.Backspace, ' ')
el.MustPress([]rune("A b")...)

g.Eq("1 A b", el.MustText())

g.Panic(func() {
g.mc.stubErr(1, proto.DOMScrollIntoViewIfNeeded{})
el.MustPress(' ')
})
g.Panic(func() {
g.mc.stubErr(1, proto.DOMScrollIntoViewIfNeeded{})
el.MustSelectAllText()
})
}

func TestKeyDown(t *testing.T) {
g := setup(t)

p := g.page.MustNavigate(g.srcFile("fixtures/keys.html"))
p.MustElement("body")
p.Keyboard.MustDown('j')

g.True(p.MustHas("body[event=key-down-j]"))
}

func TestKeyUp(t *testing.T) {
g := setup(t)

p := g.page.MustNavigate(g.srcFile("fixtures/keys.html"))
p.MustElement("body")
p.Keyboard.MustUp('x')

g.True(p.MustHas("body[event=key-up-x]"))
}

func TestInput(t *testing.T) {
g := setup(t)

text := "雲の上は\nいつも晴れ"

p := g.page.MustNavigate(g.srcFile("fixtures/input.html"))

{
el := p.MustElement("[contenteditable=true]").MustInput(text)
g.Eq(text, el.MustText())
}

el := p.MustElement("textarea")
el.MustInput(text)

g.Eq(text, el.MustText())
g.True(p.MustHas("[event=textarea-change]"))

g.Panic(func() {
g.mc.stubErr(1, proto.RuntimeCallFunctionOn{})
el.MustText()
})
g.Panic(func() {
g.mc.stubErr(4, proto.RuntimeCallFunctionOn{})
el.MustInput("")
})
g.Panic(func() {
g.mc.stubErr(5, proto.RuntimeCallFunctionOn{})
el.MustInput("")
})
g.Panic(func() {
g.mc.stubErr(6, proto.RuntimeCallFunctionOn{})
el.MustInput("")
})
g.Panic(func() {
g.mc.stubErr(1, proto.InputInsertText{})
el.MustInput("")
})
}

func TestInputTime(t *testing.T) {
g := setup(t)

Expand Down Expand Up @@ -417,6 +328,13 @@ func TestInputTime(t *testing.T) {
})
}

func TestElementInputDate(t *testing.T) {
g := setup(t)

p := g.page.MustNavigate(g.srcFile("fixtures/input.html"))
p.MustElement("[type=date]").MustInput("12")
}

func TestCheckbox(t *testing.T) {
g := setup(t)

Expand Down Expand Up @@ -587,7 +505,7 @@ func TestEnter(t *testing.T) {

p := g.page.MustNavigate(g.srcFile("fixtures/input.html"))
el := p.MustElement("[type=submit]")
el.MustPress(input.Enter)
el.MustType(input.Enter)

g.True(p.MustHas("[event=submit]"))
}
Expand Down Expand Up @@ -897,7 +815,7 @@ func TestElementErrors(t *testing.T) {
err = el.Context(ctx).Focus()
g.Err(err)

err = el.Context(ctx).Press('a')
_, err = el.Context(ctx).KeyActions()
g.Err(err)

err = el.Context(ctx).Input("a")
Expand Down
6 changes: 3 additions & 3 deletions examples_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ func Example() {
page := browser.MustPage("https://github.com")

// We use css selector to get the search input element and input "git"
page.MustElement("input").MustInput("git").MustPress(input.Enter)
page.MustElement("input").MustInput("git").MustType(input.Enter)

// Wait until css selector get the element then get the text content of it.
text := page.MustElement(".codesearch-results p").MustText()
Expand Down Expand Up @@ -91,7 +91,7 @@ func Example_disable_headless_to_debug() {

page := browser.MustPage("https://github.com/")

page.MustElement("input").MustInput("git").MustPress(input.Enter)
page.MustElement("input").MustInput("git").MustType(input.Enter)

text := page.MustElement(".codesearch-results p").MustText()

Expand Down Expand Up @@ -253,7 +253,7 @@ func Example_race_selectors() {
page := browser.MustPage("https://leetcode.com/accounts/login/")

page.MustElement("#id_login").MustInput(username)
page.MustElement("#id_password").MustInput(password).MustPress(input.Enter)
page.MustElement("#id_password").MustInput(password).MustType(input.Enter)

// It will keep retrying until one selector has found a match
elm := page.Race().Element(".nav-user-icon-base").MustHandle(func(e *rod.Element) {
Expand Down
19 changes: 13 additions & 6 deletions fixtures/keys.html
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@
<html>
<body></body>
<body style="font-family: monospace"></body>
<script>
window.onkeydown = (e) => {
document.body.setAttribute('event', 'key-down-' + e.key)
}
window.onkeyup = (e) => {
document.body.setAttribute('event', 'key-up-' + e.key)
let rec = (name) => (e) => {
let list = []
e.ctrlKey ? list.push('ctrl') : 0
e.altKey ? list.push('alt') : 0
e.shiftKey ? list.push('shift') : 0
e.metaKey ? list.push('meta') : 0
document.body.innerText += `${name} ${JSON.stringify(e.key)} ${e.code} ${
e.keyCode
} modifiers(${list.join(',')})\n`
}

window.onkeydown = rec('↓')
window.onkeyup = rec('↑')
</script>
</html>
3 changes: 3 additions & 0 deletions hijack_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,9 @@ func TestHijackMockWholeResponseEmptyBody(t *testing.T) {
}

func TestHijackMockWholeResponseNoBody(t *testing.T) {
// TODO: remove the skip
t.Skip("Because of flaky test result")

g := setup(t)

router := g.page.HijackRequests()
Expand Down

0 comments on commit 938f47b

Please sign in to comment.