Skip to content
This repository was archived by the owner on Jan 30, 2025. It is now read-only.

Commit 016a170

Browse files
authored
Merge pull request #134 from grafana/fix/132-special-chars
Fix/132 special chars
2 parents ca26b90 + 4c3a46f commit 016a170

File tree

4 files changed

+204
-178
lines changed

4 files changed

+204
-178
lines changed

common/keyboard.go

Lines changed: 131 additions & 159 deletions
Original file line numberDiff line numberDiff line change
@@ -30,44 +30,92 @@ import (
3030
"github.com/dop251/goja"
3131
"github.com/grafana/xk6-browser/api"
3232
"github.com/grafana/xk6-browser/keyboardlayout"
33-
k6common "go.k6.io/k6/js/common"
3433
)
3534

36-
// Ensure Keyboard implements the api.Keyboard interface
3735
var _ api.Keyboard = &Keyboard{}
3836

3937
const (
40-
ModifierKeyAlt int64 = 1
41-
ModifierKeyControl = 2
42-
ModifierKeyMeta = 4
43-
ModifierKeyShift = 8
38+
ModifierKeyAlt int64 = 1 << iota
39+
ModifierKeyControl
40+
ModifierKeyMeta
41+
ModifierKeyShift
4442
)
4543

46-
// Keyboard represents a keyboard input device
44+
// Keyboard represents a keyboard input device.
45+
// Each Page has a publicly accessible Keyboard.
4746
type Keyboard struct {
48-
ctx context.Context
49-
session *Session
50-
modifiers int64
51-
pressedKeys map[int64]bool
52-
layoutName string
47+
ctx context.Context
48+
session *Session
49+
50+
modifiers int64 // like shift, alt, ctrl, ...
51+
pressedKeys map[int64]bool // tracks keys through down() and up()
52+
layoutName string // us by default
53+
layout keyboardlayout.KeyboardLayout
5354
}
5455

55-
// NewKeyboard creates a new keyboard
56+
// NewKeyboard returns a new keyboard with a "us" layout.
5657
func NewKeyboard(ctx context.Context, session *Session) *Keyboard {
57-
k := &Keyboard{
58+
return &Keyboard{
5859
ctx: ctx,
5960
session: session,
60-
modifiers: 0,
6161
pressedKeys: make(map[int64]bool),
6262
layoutName: "us",
63+
layout: keyboardlayout.GetKeyboardLayout("us"),
64+
}
65+
}
66+
67+
// Down sends a key down message to a session target.
68+
func (k *Keyboard) Down(key string) {
69+
if err := k.down(key); err != nil {
70+
k6Throw(k.ctx, "cannot send key down: %w", err)
71+
}
72+
}
73+
74+
// Up sends a key up message to a session target.
75+
func (k *Keyboard) Up(key string) {
76+
if err := k.up(key); err != nil {
77+
k6Throw(k.ctx, "cannot send key up: %w", err)
78+
}
79+
}
80+
81+
// Press sends a key press message to a session target.
82+
// It delays the action if `Delay` option is specified.
83+
// A press message consists of successive key down and up messages.
84+
func (k *Keyboard) Press(key string, opts goja.Value) {
85+
kbdOpts := NewKeyboardOptions()
86+
if err := kbdOpts.Parse(k.ctx, opts); err != nil {
87+
k6Throw(k.ctx, "cannot parse keyboard options: %w", err)
88+
}
89+
if err := k.press(key, kbdOpts); err != nil {
90+
k6Throw(k.ctx, "cannot press key: %w", err)
91+
}
92+
}
93+
94+
// InsertText inserts a text without dispatching key events.
95+
func (k *Keyboard) InsertText(text string) {
96+
if err := k.insertText(text); err != nil {
97+
k6Throw(k.ctx, "cannot insert text: %w", err)
98+
}
99+
}
100+
101+
// Type sends a press message to a session target for each character in text.
102+
// It delays the action if `Delay` option is specified.
103+
//
104+
// It sends an insertText message if a character is not among
105+
// valid characters in the keyboard's layout.
106+
func (k *Keyboard) Type(text string, opts goja.Value) {
107+
kbdOpts := NewKeyboardOptions()
108+
if err := kbdOpts.Parse(k.ctx, opts); err != nil {
109+
k6Throw(k.ctx, "cannot parse keyboard options: %w", err)
110+
}
111+
if err := k.typ(text, kbdOpts); err != nil {
112+
k6Throw(k.ctx, "cannot type text: %w", err)
63113
}
64-
return k
65114
}
66115

67116
func (k *Keyboard) down(key string) error {
68117
keyInput := keyboardlayout.KeyInput(key)
69-
layout := keyboardlayout.GetKeyboardLayout(k.layoutName)
70-
if _, ok := layout.ValidKeys[keyInput]; !ok {
118+
if _, ok := k.layout.ValidKeys[keyInput]; !ok {
71119
return fmt.Errorf("%q is not a valid key for layout %q", key, k.layoutName)
72120
}
73121

@@ -93,7 +141,30 @@ func (k *Keyboard) down(key string) error {
93141
WithUnmodifiedText(text).
94142
WithAutoRepeat(autoRepeat)
95143
if err := action.Do(cdp.WithExecutor(k.ctx, k.session)); err != nil {
96-
return fmt.Errorf("unable to mouse down: %w", err)
144+
return fmt.Errorf("cannot execute dispatch key event down: %w", err)
145+
}
146+
147+
return nil
148+
}
149+
150+
func (k *Keyboard) up(key string) error {
151+
keyInput := keyboardlayout.KeyInput(key)
152+
if _, ok := k.layout.ValidKeys[keyInput]; !ok {
153+
return fmt.Errorf("'%s' is not a valid key for layout '%s'", key, k.layoutName)
154+
}
155+
156+
keyDef := k.keyDefinitionFromKey(keyInput)
157+
k.modifiers &= ^k.modifierBitFromKeyName(keyDef.Key)
158+
delete(k.pressedKeys, keyDef.KeyCode)
159+
160+
action := input.DispatchKeyEvent(input.KeyUp).
161+
WithModifiers(input.Modifier(k.modifiers)).
162+
WithKey(keyDef.Key).
163+
WithWindowsVirtualKeyCode(keyDef.KeyCode).
164+
WithCode(keyDef.Code).
165+
WithLocation(keyDef.Location)
166+
if err := action.Do(cdp.WithExecutor(k.ctx, k.session)); err != nil {
167+
return fmt.Errorf("cannot execute dispatch key event up: %w", err)
97168
}
98169

99170
return nil
@@ -102,206 +173,107 @@ func (k *Keyboard) down(key string) error {
102173
func (k *Keyboard) insertText(text string) error {
103174
action := input.InsertText(text)
104175
if err := action.Do(cdp.WithExecutor(k.ctx, k.session)); err != nil {
105-
return fmt.Errorf("unable to send character: %w", err)
176+
return fmt.Errorf("cannot execute insert text: %w", err)
106177
}
107178
return nil
108179
}
109180

110-
func (k *Keyboard) keyDefinitionFromKey(keyString keyboardlayout.KeyInput) keyboardlayout.KeyDefinition {
181+
func (k *Keyboard) keyDefinitionFromKey(key keyboardlayout.KeyInput) keyboardlayout.KeyDefinition {
111182
shift := k.modifiers & ModifierKeyShift
112-
keyDef := keyboardlayout.KeyDefinition{}
113-
layout := keyboardlayout.GetKeyboardLayout(k.layoutName)
114-
code := keyString
115-
srcKeyDef, ok := layout.Keys[keyString]
116183

184+
// Find directly from the keyboard layout
185+
srcKeyDef, ok := k.layout.Keys[key]
186+
// Try to find based on key value instead of code
117187
if !ok {
118-
// Find based on key value instead of code
119-
for k, v := range layout.Keys {
120-
if v.Key == string(keyString) {
121-
code = k
122-
srcKeyDef = v
123-
break
124-
}
125-
}
188+
srcKeyDef, ok = k.layout.KeyDefinition(key)
189+
}
190+
// Try to find with the shift key value
191+
if !ok {
192+
srcKeyDef = k.layout.ShiftKeyDefinition(key)
193+
shift = k.modifiers | ModifierKeyShift
126194
}
127195

196+
var keyDef keyboardlayout.KeyDefinition
128197
if srcKeyDef.Key != "" {
129198
keyDef.Key = srcKeyDef.Key
130-
}
131-
if shift != 0 && srcKeyDef.ShiftKey != "" {
132-
keyDef.Key = srcKeyDef.ShiftKey
133-
}
134-
if srcKeyDef.KeyCode != 0 {
135-
keyDef.KeyCode = srcKeyDef.KeyCode
199+
keyDef.Text = srcKeyDef.Key
136200
}
137201
if shift != 0 && srcKeyDef.ShiftKeyCode != 0 {
138202
keyDef.KeyCode = srcKeyDef.ShiftKeyCode
139203
}
140204
if srcKeyDef.KeyCode != 0 {
141205
keyDef.KeyCode = srcKeyDef.KeyCode
142206
}
143-
if code != "" {
144-
keyDef.Code = string(code)
207+
if key != "" {
208+
keyDef.Code = string(key)
145209
}
146210
if srcKeyDef.Location != 0 {
147211
keyDef.Location = srcKeyDef.Location
148212
}
149-
if len(srcKeyDef.Key) == 1 {
150-
keyDef.Text = srcKeyDef.Key
151-
}
152213
if srcKeyDef.Text != "" {
153214
keyDef.Text = srcKeyDef.Text
154215
}
155-
if shift != 0 && keyDef.ShiftKey != "" {
156-
keyDef.Text = keyDef.ShiftKey
216+
if shift != 0 && srcKeyDef.ShiftKey != "" {
217+
keyDef.Key = srcKeyDef.ShiftKey
218+
keyDef.Text = srcKeyDef.ShiftKey
157219
}
158-
159220
// If any modifiers besides shift are pressed, no text should be sent
160221
if k.modifiers & ^ModifierKeyShift != 0 {
161222
keyDef.Text = ""
162223
}
163-
164224
return keyDef
165225
}
166226

167227
func (k *Keyboard) modifierBitFromKeyName(key string) int64 {
168-
if key == "Alt" {
228+
switch key {
229+
case "Alt":
169230
return ModifierKeyAlt
170-
}
171-
if key == "Control" {
231+
case "Control":
172232
return ModifierKeyControl
173-
}
174-
if key == "Meta" {
233+
case "Meta":
175234
return ModifierKeyMeta
176-
}
177-
if key == "Shift" {
235+
case "Shift":
178236
return ModifierKeyShift
179237
}
180238
return 0
181239
}
182240

183241
func (k *Keyboard) press(key string, opts *KeyboardOptions) error {
184-
err := k.down(key)
185-
if err != nil {
186-
return err
187-
}
188242
if opts.Delay != 0 {
189-
t := time.NewTimer(time.Duration(opts.Delay * time.Hour.Milliseconds()))
243+
t := time.NewTimer(time.Duration(opts.Delay) * time.Millisecond)
190244
select {
191245
case <-k.ctx.Done():
192246
t.Stop()
193247
case <-t.C:
194248
}
195249
}
196-
err = k.up(key)
197-
if err != nil {
198-
return err
250+
if err := k.down(key); err != nil {
251+
return fmt.Errorf("cannot do key down: %w", err)
199252
}
200-
return nil
253+
return k.up(key)
201254
}
202255

203256
func (k *Keyboard) typ(text string, opts *KeyboardOptions) error {
204257
layout := keyboardlayout.GetKeyboardLayout(k.layoutName)
205258
for _, c := range text {
259+
if opts.Delay != 0 {
260+
t := time.NewTimer(time.Duration(opts.Delay) * time.Millisecond)
261+
select {
262+
case <-k.ctx.Done():
263+
t.Stop()
264+
case <-t.C:
265+
}
266+
}
206267
keyInput := keyboardlayout.KeyInput(c)
207268
if _, ok := layout.ValidKeys[keyInput]; ok {
208-
err := k.press(string(c), opts)
209-
if err != nil {
210-
return nil
211-
}
212-
} else {
213-
if opts.Delay != 0 {
214-
t := time.NewTimer(time.Duration(opts.Delay * time.Hour.Milliseconds()))
215-
select {
216-
case <-k.ctx.Done():
217-
t.Stop()
218-
case <-t.C:
219-
}
220-
}
221-
err := k.insertText(string(c))
222-
if err != nil {
223-
return nil
269+
if err := k.press(string(c), opts); err != nil {
270+
return fmt.Errorf("cannot press key: %w", err)
224271
}
272+
continue
273+
}
274+
if err := k.insertText(string(c)); err != nil {
275+
return fmt.Errorf("cannot insert text: %w", err)
225276
}
226277
}
227278
return nil
228279
}
229-
230-
func (k *Keyboard) up(key string) error {
231-
keyInput := keyboardlayout.KeyInput(key)
232-
layout := keyboardlayout.GetKeyboardLayout(k.layoutName)
233-
if _, ok := layout.ValidKeys[keyInput]; !ok {
234-
return fmt.Errorf("'%s' is not a valid key for layout '%s'", key, k.layoutName)
235-
}
236-
237-
keyDef := k.keyDefinitionFromKey(keyInput)
238-
k.modifiers &= ^k.modifierBitFromKeyName(keyDef.Key)
239-
delete(k.pressedKeys, keyDef.KeyCode)
240-
241-
action := input.DispatchKeyEvent(input.KeyUp).
242-
WithModifiers(input.Modifier(k.modifiers)).
243-
WithKey(keyDef.Key).
244-
WithWindowsVirtualKeyCode(keyDef.KeyCode).
245-
WithCode(keyDef.Code).
246-
WithLocation(keyDef.Location)
247-
if err := action.Do(cdp.WithExecutor(k.ctx, k.session)); err != nil {
248-
return fmt.Errorf("unable to mouse down: %w", err)
249-
}
250-
251-
return nil
252-
}
253-
254-
// Down
255-
func (k *Keyboard) Down(key string) {
256-
rt := k6common.GetRuntime(k.ctx)
257-
err := k.down(key)
258-
if err != nil {
259-
k6common.Throw(rt, err)
260-
}
261-
}
262-
263-
// Press
264-
func (k *Keyboard) Press(key string, opts goja.Value) {
265-
rt := k6common.GetRuntime(k.ctx)
266-
kbdOpts := NewKeyboardOptions()
267-
if err := kbdOpts.Parse(k.ctx, opts); err != nil {
268-
k6common.Throw(rt, fmt.Errorf("failed parsing options: %w", err))
269-
}
270-
271-
err := k.press(key, kbdOpts)
272-
if err != nil {
273-
k6common.Throw(rt, err)
274-
}
275-
}
276-
277-
// InsertText
278-
func (k *Keyboard) InsertText(text string) {
279-
rt := k6common.GetRuntime(k.ctx)
280-
err := k.insertText(text)
281-
if err != nil {
282-
k6common.Throw(rt, err)
283-
}
284-
}
285-
286-
// Type
287-
func (k *Keyboard) Type(text string, opts goja.Value) {
288-
rt := k6common.GetRuntime(k.ctx)
289-
kbdOpts := NewKeyboardOptions()
290-
if err := kbdOpts.Parse(k.ctx, opts); err != nil {
291-
k6common.Throw(rt, fmt.Errorf("failed parsing options: %w", err))
292-
}
293-
294-
err := k.typ(text, kbdOpts)
295-
if err != nil {
296-
k6common.Throw(rt, err)
297-
}
298-
}
299-
300-
// Up
301-
func (k *Keyboard) Up(key string) {
302-
rt := k6common.GetRuntime(k.ctx)
303-
err := k.up(key)
304-
if err != nil {
305-
k6common.Throw(rt, err)
306-
}
307-
}

0 commit comments

Comments
 (0)