Skip to content

Commit e930de7

Browse files
authored
feat: add scroll shortcuts for top and bottom targets (#21)
1 parent 8fe5b0f commit e930de7

8 files changed

Lines changed: 151 additions & 42 deletions

File tree

go.work.sum

Lines changed: 6 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,36 @@
1+
github.com/JohannesKaufmann/dom v0.2.0 h1:1bragmEb19K8lHAqgFgqCpiPCFEZMTXzOIEjuxkUfLQ=
2+
github.com/JohannesKaufmann/html-to-markdown/v2 v2.5.0 h1:mklaPbT4f/EiDr1Q+zPrEt9lgKAkVrIBtWf33d9GpVA=
13
github.com/MontFerret/contrib/modules/web/html v0.0.0-20260330220155-415840fb6708/go.mod h1:rJWNq8u8V5sjmyki45ggebIuKwaSNOMtrpzdVeG4GRk=
24
github.com/MontFerret/contrib/modules/xml v0.0.0-20260330180809-8a916f8c063b/go.mod h1:0uZg18mEPfdJ7kHDx0/ESYwdeBxytLyBUicz9pRATl0=
3-
github.com/MontFerret/contrib/modules/xml v1.0.0-rc.2/go.mod h1:gVKhCOSXmIjTATqbeirxyR2b5yZZRcOEvZDi8B5Ws4U=
45
github.com/MontFerret/ferret/v2 v2.0.0-alpha.11/go.mod h1:5xHHTp/uR5N/Nm+gA8gSdTiEj4XXhTxQPYFSh722Phw=
56
github.com/MontFerret/ferret/v2 v2.0.0-alpha.12/go.mod h1:xnhO/65Sa17LSRC8GLIO51JZ4Wm08JjEtmowGVut4z0=
67
github.com/MontFerret/ferret/v2 v2.0.0-alpha.15/go.mod h1:xnhO/65Sa17LSRC8GLIO51JZ4Wm08JjEtmowGVut4z0=
78
github.com/agnivade/levenshtein v1.2.1 h1:EHBY3UOn1gwdy/VbFwgo4cxecRznFk7fKWN1KOX7eoM=
89
github.com/agnivade/levenshtein v1.2.1/go.mod h1:QVVI16kDrtSuwcpd0p1+xMC6Z/VfhtCyDIjcwga4/DU=
10+
github.com/antlr4-go/antlr/v4 v4.13.1 h1:SqQKkuVZ+zWkMMNkjy5FZe5mr5WURWnlpmOuzYWrPrQ=
911
github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
1012
github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
13+
github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=
1114
github.com/bmatcuk/doublestar/v4 v4.9.1 h1:X8jg9rRZmJd4yRy7ZeNDRnM+T3ZfHv15JiBJ/avrEXE=
1215
github.com/bmatcuk/doublestar/v4 v4.9.1/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc=
1316
github.com/client9/misspell v0.3.4 h1:ta993UF76GwbvJcIo3Y68y/M3WxlpEHPWIGDkJYwzJI=
14-
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
15-
github.com/coder/websocket v1.8.12/go.mod h1:LNVeNrXQZfe5qhS9ALED3uA+l5pPqvwXg3CKoDBB2gs=
1617
github.com/coreos/go-systemd/v22 v22.7.0 h1:LAEzFkke61DFROc7zNLX/WA2i5J8gYqe0rSj9KI28KA=
1718
github.com/coreos/go-systemd/v22 v22.7.0/go.mod h1:xNUYtjHu2EDXbsxz1i41wouACIwT7Ybq9o0BQhMwD0w=
1819
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
19-
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
2020
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
2121
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
2222
github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk=
2323
github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
2424
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
25-
github.com/gopherjs/gopherjs v1.17.2/go.mod h1:pRRIvn/QzFLrKfvEz3qUuEhtE/zLCWfreZ6J5gM2i+k=
26-
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
2725
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
2826
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
29-
github.com/jarcoal/httpmock v1.4.1/go.mod h1:ftW1xULwo+j0R0JJkJIIi7UKigZUXCLLanykgjwBXL0=
3027
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
31-
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
3228
github.com/lucasb-eyer/go-colorful v1.3.0 h1:2/yBRLdWBZKrf7gB40FoiKfAWYQ0lqNcbuQwVHXptag=
3329
github.com/lucasb-eyer/go-colorful v1.3.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
34-
github.com/mafredri/cdp v0.35.0/go.mod h1:xS8dVzwKfYswsOHG05SfDCbhNrO89kWVJyMj5vD+zYo=
3530
github.com/mafredri/go-lint v0.0.0-20180911205320-920981dfc79e h1:nmYwSlsAjQ6WYj3FklYlGy3rRfkmsv+PcUa2YWZJLmg=
36-
github.com/mafredri/go-lint v0.0.0-20180911205320-920981dfc79e/go.mod h1:k/zdyxI3q6dup24o8xpYjJKTCf2F7rfxLp6w/efTiWs=
3731
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
3832
github.com/maxatome/go-testdeep v1.14.0/go.mod h1:lPZc/HAcJMP92l7yI6TRz1aZN5URwUBUAfUNvrclaNM=
33+
github.com/microcosm-cc/bluemonday v1.0.27 h1:MpEUotklkwCSLeH+Qdx1VJgNqLlpY2KXwXFM08ygZfk=
3934
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
4035
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
4136
github.com/muesli/termenv v0.16.0 h1:S5AlUN9dENB57rsbnkPyfdGuWIlkmzJjbFf0Tf5FWUc=
@@ -44,7 +39,6 @@ github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86 h1:D6paGObi5Wu
4439
github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo=
4540
github.com/neelance/sourcemap v0.0.0-20200213170602-2833bce08e4c h1:bY6ktFuJkt+ZXkX0RChQch2FtHpWQLVS8Qo1YasiIVk=
4641
github.com/neelance/sourcemap v0.0.0-20200213170602-2833bce08e4c/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM=
47-
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
4842
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
4943
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
5044
github.com/rs/xid v1.6.0 h1:fV591PaemRlL6JfRxGDEPl69wICngIQ3shQtzfy2gxU=
@@ -62,28 +56,23 @@ github.com/shurcooL/vfsgen v0.0.0-20200824052919-0d455de96546 h1:pXY9qYc/MP5zdvq
6256
github.com/shurcooL/vfsgen v0.0.0-20200824052919-0d455de96546/go.mod h1:TrYk7fJVaAttu97ZZKrO9UbRa8izdowaMIZcxYMbVaw=
6357
github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
6458
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
65-
github.com/smarty/assertions v1.16.0/go.mod h1:duaaFdCS0K9dnoM50iyek/eYINOZ64gbh1Xlf6LG7AI=
66-
github.com/smartystreets/goconvey v1.8.1/go.mod h1:+/u4qLyY6x1jReYOp7GOM2FSt8aP9CzCZL03bI28W60=
6759
github.com/spf13/cobra v1.2.1 h1:+KmjbUw1hriSNMF55oPrkZcb27aECyrj8V2ytv7kWDw=
6860
github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk=
6961
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
7062
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
7163
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
72-
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
7364
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
7465
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
75-
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
7666
github.com/yuin/goldmark v1.7.13/go.mod h1:ip/1k0VRfGynBgxOz0yCqHrbZXhcjxyuS66Brc7iBKg=
7767
golang.org/x/crypto v0.49.0 h1:+Ng2ULVvLHnJ/ZFEq4KdcDd/cfjrrjjNSXNzxg0Y4U4=
7868
golang.org/x/crypto v0.49.0/go.mod h1:ErX4dUh2UM+CFYiXZRTcMpEcN8b/1gxEuv3nODoYtCA=
7969
golang.org/x/crypto v0.51.0 h1:IBPXwPfKxY7cWQZ38ZCIRPI50YLeevDLlLnyC5wRGTI=
8070
golang.org/x/crypto v0.51.0/go.mod h1:8AdwkbraGNABw2kOX6YFPs3WM22XqI4EXEd8g+x7Oc8=
71+
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 h1:vr/HnozRka3pE4EsMEg1lgkXJkTFJCVUX+S/ZT6wYzM=
8172
golang.org/x/mod v0.33.0 h1:tHFzIWbBifEmbwtGz65eaWyGiGZatSrT9prnU8DbVL8=
8273
golang.org/x/mod v0.33.0/go.mod h1:swjeQEj+6r7fODbD2cqrnje9PnziFuw4bmLbBZFrQ5w=
8374
golang.org/x/mod v0.35.0 h1:Ww1D637e6Pg+Zb2KrWfHQUnH2dQRLBQyAtpr/haaJeM=
8475
golang.org/x/mod v0.35.0/go.mod h1:+GwiRhIInF8wPm+4AoT6L0FA1QWAad3OMdTRx4tFYlU=
85-
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
86-
golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
8776
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
8877
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
8978
golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
@@ -94,15 +83,12 @@ golang.org/x/term v0.41.0 h1:QCgPso/Q3RTJx2Th4bDLqML4W6iJiaXFq2/ftQF13YU=
9483
golang.org/x/term v0.41.0/go.mod h1:3pfBgksrReYfZ5lvYM0kSO0LIkAl4Yl2bXOkKP7Ec2A=
9584
golang.org/x/term v0.43.0 h1:S4RLU2sB31O/NCl+zFN9Aru9A/Cq2aqKpTZJ6B+DwT4=
9685
golang.org/x/term v0.43.0/go.mod h1:lrhlHNdQJHO+1qVYiHfFKVuVioJIheAc3fBSMFYEIsk=
97-
golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
9886
golang.org/x/tools v0.42.0 h1:uNgphsn75Tdz5Ji2q36v/nsFSfR/9BRFvqhGBaJGd5k=
9987
golang.org/x/tools v0.42.0/go.mod h1:Ma6lCIwGZvHK6XtgbswSoWroEkhugApmsXyrUmBhfr0=
10088
golang.org/x/tools v0.44.0 h1:UP4ajHPIcuMjT1GqzDWRlalUEoY+uzoZKnhOjbIPD2c=
10189
golang.org/x/tools v0.44.0/go.mod h1:KA0AfVErSdxRZIsOVipbv3rQhVXTnlU6UhKxHd1seDI=
10290
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
10391
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
104-
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
10592
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
10693
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
10794
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
108-
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

modules/web/html/README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -413,6 +413,7 @@ DISPATCH "input" IN searchBox WITH { value: "macbook" }
413413
DISPATCH "keydown" IN input WITH { key: "Enter" }
414414
DISPATCH "type" IN input WITH { text: "macbook pro", delay: 50 }
415415
DISPATCH "scroll" IN doc WITH { y: 1200 }
416+
DISPATCH "scroll" IN doc WITH { to: "bottom" }
416417
DISPATCH "scroll" IN container WITH { by: { y: 800 } }
417418
DISPATCH "scroll" IN item WITH { intoView: true }
418419
```
@@ -426,7 +427,7 @@ Supported CDP dispatch event names are:
426427
| Forms | `input`, `change`, `submit`, `reset`, `focus`, `blur`, `check`, `uncheck`, `toggle` |
427428
| Scroll | `scroll` |
428429

429-
Mouse payloads may include `button`, `count`, `x`, and `y`. Keyboard `press` accepts `key` or `keys`; `type` accepts `text`, optional `delay`, and optional `clear`. Form `input` requires `value`; `change` may include `value`. Scroll accepts absolute coordinates via `x`/`y` or `to`, relative coordinates via `by`, or `intoView: true`.
430+
Mouse payloads may include `button`, `count`, `x`, and `y`. Keyboard `press` accepts `key` or `keys`; `type` accepts `text`, optional `delay`, and optional `clear`. Form `input` requires `value`; `change` may include `value`. Scroll accepts absolute coordinates via `x`/`y` or `to`, named absolute targets via `to: "top"` or `to: "bottom"`, relative coordinates via `by`, or `intoView: true`.
430431

431432
Navigation and scrolling module functions:
432433

modules/web/html/drivers/cdp/dom/dispatch.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,10 @@ func dispatchDocumentScroll(ctx context.Context, doc *HTMLDocument, payload runt
151151
return doc.element.ScrollIntoView(ctx, params.Options)
152152
case dispatchScrollModeBy:
153153
return doc.input.ScrollByDelta(ctx, params.Options)
154+
case dispatchScrollModeTop:
155+
return doc.ScrollTop(ctx, params.Options)
156+
case dispatchScrollModeBottom:
157+
return doc.ScrollBottom(ctx, params.Options)
154158
default:
155159
return doc.Scroll(ctx, params.Options)
156160
}

modules/web/html/drivers/cdp/dom/dispatch_payload.go

Lines changed: 51 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -10,32 +10,36 @@ import (
1010
"github.com/MontFerret/ferret/v2/pkg/sdk"
1111
)
1212

13-
type dispatchPayload struct {
14-
fields runtime.Map
15-
}
13+
type (
14+
dispatchPayload struct {
15+
fields runtime.Map
16+
}
1617

17-
type dispatchKeyboardParams struct {
18-
Keys []runtime.String
19-
Count runtime.Int
20-
}
18+
dispatchKeyboardParams struct {
19+
Keys []runtime.String
20+
Count runtime.Int
21+
}
2122

22-
type dispatchTypeParams struct {
23-
Text string
24-
Clear bool
25-
Delay runtime.Int
26-
}
23+
dispatchTypeParams struct {
24+
Text string
25+
Clear bool
26+
Delay runtime.Int
27+
}
2728

28-
type dispatchScrollMode string
29+
dispatchScrollMode string
2930

30-
type dispatchScrollParams struct {
31-
Mode dispatchScrollMode
32-
Options drivers.ScrollOptions
33-
}
31+
dispatchScrollParams struct {
32+
Mode dispatchScrollMode
33+
Options drivers.ScrollOptions
34+
}
35+
)
3436

3537
const (
3638
dispatchScrollModeTo dispatchScrollMode = "to"
3739
dispatchScrollModeBy dispatchScrollMode = "by"
3840
dispatchScrollModeIntoView dispatchScrollMode = "intoView"
41+
dispatchScrollModeTop dispatchScrollMode = "top"
42+
dispatchScrollModeBottom dispatchScrollMode = "bottom"
3943
)
4044

4145
func newDispatchPayload(value runtime.Value) (dispatchPayload, error) {
@@ -284,12 +288,12 @@ func parseDispatchScrollPayload(ctx context.Context, value runtime.Value) (dispa
284288
if toValue, found, err := dispatchLookup(ctx, payload, "to"); err != nil {
285289
return dispatchScrollParams{}, err
286290
} else if found && toValue != runtime.None {
287-
options, err = parseDispatchScrollCoordinates(ctx, toValue, options)
291+
params, err := parseDispatchScrollTarget(ctx, toValue, options)
288292
if err != nil {
289293
return dispatchScrollParams{}, err
290294
}
291295

292-
return dispatchScrollParams{Mode: dispatchScrollModeTo, Options: options}, nil
296+
return params, nil
293297
}
294298

295299
if byValue, found, err := dispatchLookup(ctx, payload, "by"); err != nil {
@@ -339,6 +343,34 @@ func parseDispatchScrollOptions(ctx context.Context, payload dispatchPayload) (d
339343
return options, nil
340344
}
341345

346+
func parseDispatchScrollTarget(
347+
ctx context.Context,
348+
value runtime.Value,
349+
options drivers.ScrollOptions,
350+
) (dispatchScrollParams, error) {
351+
if target, err := runtime.CastString(value); err == nil {
352+
switch target.String() {
353+
case string(dispatchScrollModeTop):
354+
return dispatchScrollParams{Mode: dispatchScrollModeTop, Options: options}, nil
355+
case string(dispatchScrollModeBottom):
356+
return dispatchScrollParams{Mode: dispatchScrollModeBottom, Options: options}, nil
357+
default:
358+
return dispatchScrollParams{}, runtime.Errorf(
359+
runtime.ErrInvalidOperation,
360+
"unsupported scroll target %q; supported targets: top, bottom",
361+
target.String(),
362+
)
363+
}
364+
}
365+
366+
options, err := parseDispatchScrollCoordinates(ctx, value, options)
367+
if err != nil {
368+
return dispatchScrollParams{}, err
369+
}
370+
371+
return dispatchScrollParams{Mode: dispatchScrollModeTo, Options: options}, nil
372+
}
373+
342374
func parseDispatchScrollCoordinates(ctx context.Context, value runtime.Value, options drivers.ScrollOptions) (drivers.ScrollOptions, error) {
343375
payload, err := newDispatchPayload(value)
344376
if err != nil {

modules/web/html/drivers/cdp/dom/dispatch_payload_test.go

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,50 @@ func TestParseDispatchScrollPayload(t *testing.T) {
148148
t.Fatalf("unexpected direct scroll params: %#v", params)
149149
}
150150

151+
params, err = parseDispatchScrollPayload(ctx, runtime.NewObjectWith(map[string]runtime.Value{
152+
"to": runtime.NewObjectWith(map[string]runtime.Value{
153+
"x": runtime.NewInt(5),
154+
"y": runtime.NewInt(10),
155+
}),
156+
}))
157+
if err != nil {
158+
t.Fatalf("unexpected coordinate target scroll error: %v", err)
159+
}
160+
161+
if params.Mode != dispatchScrollModeTo || params.Options.Left != 5 || params.Options.Top != 10 {
162+
t.Fatalf("unexpected coordinate target scroll params: %#v", params)
163+
}
164+
165+
params, err = parseDispatchScrollPayload(ctx, runtime.NewObjectWith(map[string]runtime.Value{
166+
"to": runtime.NewString("top"),
167+
}))
168+
if err != nil {
169+
t.Fatalf("unexpected top target scroll error: %v", err)
170+
}
171+
172+
if params.Mode != dispatchScrollModeTop {
173+
t.Fatalf("unexpected top target scroll params: %#v", params)
174+
}
175+
176+
params, err = parseDispatchScrollPayload(ctx, runtime.NewObjectWith(map[string]runtime.Value{
177+
"to": runtime.NewString("bottom"),
178+
"behavior": runtime.NewString("smooth"),
179+
}))
180+
if err != nil {
181+
t.Fatalf("unexpected bottom target scroll error: %v", err)
182+
}
183+
184+
if params.Mode != dispatchScrollModeBottom || params.Options.Behavior != drivers.ScrollBehaviorSmooth {
185+
t.Fatalf("unexpected bottom target scroll params: %#v", params)
186+
}
187+
188+
_, err = parseDispatchScrollPayload(ctx, runtime.NewObjectWith(map[string]runtime.Value{
189+
"to": runtime.NewString("middle"),
190+
}))
191+
if !errors.Is(err, runtime.ErrInvalidOperation) || !strings.Contains(err.Error(), "supported targets: top, bottom") {
192+
t.Fatalf("expected invalid target error, got %v", err)
193+
}
194+
151195
params, err = parseDispatchScrollPayload(ctx, runtime.NewObjectWith(map[string]runtime.Value{
152196
"by": runtime.NewObjectWith(map[string]runtime.Value{
153197
"x": runtime.NewInt(10),

modules/web/html/drivers/cdp/templates/dispatch.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,24 @@ const elementScroll = `(el, mode, opts) => {
167167
behavior: opts.behavior
168168
};
169169
170+
if (mode === "top") {
171+
el.scrollTo({
172+
left: 0,
173+
top: 0,
174+
behavior: opts.behavior
175+
});
176+
return true;
177+
}
178+
179+
if (mode === "bottom") {
180+
el.scrollTo({
181+
left: 0,
182+
top: el.scrollHeight,
183+
behavior: opts.behavior
184+
});
185+
return true;
186+
}
187+
170188
if (mode === "by") {
171189
el.scrollBy(args);
172190
return true;

modules/web/html/drivers/memory/element_removal_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,10 @@ func TestHTMLElementRemoveAtRemovesChildAndRefreshesChildren(t *testing.T) {
2020

2121
for _, tc := range []struct {
2222
name string
23-
idx runtime.Int
2423
removedText string
25-
nextIdx runtime.Int
2624
nextIndexAttr string
25+
idx runtime.Int
26+
nextIdx runtime.Int
2727
}{
2828
{name: "first child", idx: 0, removedText: "one", nextIdx: 0, nextIndexAttr: "1"},
2929
{name: "middle child", idx: 1, removedText: "two", nextIdx: 1, nextIndexAttr: "2"},

tests/modules/web/html/dynamic/element/dispatch/scroll.fql

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,35 @@ DISPATCH "scroll" IN doc WITH { y: 300 }
77
WAIT(100)
88
T::NOT::EQ(INNER_TEXT(doc, "#scroll_output"), "0")
99

10+
DISPATCH "scroll" IN doc WITH { to: "top" }
11+
WAIT(100)
12+
T::EQ(INNER_TEXT(doc, "#scroll_output"), "0")
13+
14+
DISPATCH "scroll" IN doc WITH { to: "bottom" }
15+
WAIT(100)
16+
T::NOT::EQ(INNER_TEXT(doc, "#scroll_output"), "0")
17+
18+
DISPATCH "scroll" IN doc WITH { to: "top" }
19+
WAIT(100)
20+
T::EQ(INNER_TEXT(doc, "#scroll_output"), "0")
21+
1022
LET container = ELEMENT(doc, "#scroll_container")
1123
DISPATCH "scroll" IN container WITH { by: { y: 120 } }
1224
WAIT(100)
1325
T::NOT::EQ(INNER_TEXT(doc, "#scroll_container_output"), "0")
1426

27+
DISPATCH "scroll" IN container WITH { to: "top" }
28+
WAIT(100)
29+
T::EQ(INNER_TEXT(doc, "#scroll_container_output"), "0")
30+
31+
DISPATCH "scroll" IN container WITH { to: "bottom" }
32+
WAIT(100)
33+
T::NOT::EQ(INNER_TEXT(doc, "#scroll_container_output"), "0")
34+
35+
DISPATCH "scroll" IN container WITH { to: "top" }
36+
WAIT(100)
37+
T::EQ(INNER_TEXT(doc, "#scroll_container_output"), "0")
38+
1539
DISPATCH "scroll" IN container WITH { intoView: true }
1640

1741
RETURN NONE

0 commit comments

Comments
 (0)