diff --git a/CHANGELOG.md b/CHANGELOG.md index d547674e..a7807945 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,9 @@ - `Ferrum::Frame#lifecycle_events` provides a list of frame's events like init, networkIdle, firstPaint, etc. [#583] - `Ferrum::Frame#idle?` whether frame was loaded [#583] +### Fixed +- `Ferrum::Node#type` / `Ferrum::Keyboard` now trigger the select-all editing shortcut (`Ctrl`/`Cmd`+`A`) by naming the CDP `commands` field, so selecting and replacing text in inputs and contenteditables works. + ### Changed - `Ferrum::PendingConnectionsError` and `Ferrum::TimeoutError` were swallowed even though happening when traffic iterator results in empty array. [#583] diff --git a/lib/ferrum/keyboard.rb b/lib/ferrum/keyboard.rb index 9245f84a..83c3b496 100644 --- a/lib/ferrum/keyboard.rb +++ b/lib/ferrum/keyboard.rb @@ -136,6 +136,13 @@ def normalize_keys(keys, pressed_keys = [], memo = []) modifiers: pressed.map { |k| MODIFIERS[k] }.reduce(0, :|) ) + # Synthetic key events alone do not trigger the browser's editing + # shortcuts (e.g. select-all). When the chord matches a platform + # accelerator, name the editing command explicitly so Chrome + # executes it via the CDP `commands` field. + command = editing_command(pressed, char) + key = key.merge(commands: [command]) if command + modifiers = pressed.map { |k| to_options(KEYS.fetch(KEYS_MAPPING[k.to_sym])) } modifiers + [to_options(key)] end.flatten @@ -157,5 +164,17 @@ def combine_strings(keys) def to_options(hash) hash.inject({}) { |memo, (k, v)| memo.merge(k.to_sym => v) } end + + # Names the CDP editing command for a modifier chord, or nil if the chord + # is not an editing accelerator. The accelerator modifier is Cmd on macOS + # and Ctrl elsewhere, matching Chrome's own key bindings. Currently only + # select-all is mapped; extend the guard for further commands as needed. + def editing_command(pressed, char) + return unless char.casecmp?("a") + return if pressed.include?("shift") + + accelerator = Utils::Platform.mac? ? MODIFIERS["meta"] : MODIFIERS["ctrl"] + "selectAll" if pressed.map { |k| MODIFIERS[k] }.compact.uniq == [accelerator] + end end end diff --git a/spec/node_spec.rb b/spec/node_spec.rb index 5e5562a6..9938b59d 100644 --- a/spec/node_spec.rb +++ b/spec/node_spec.rb @@ -516,6 +516,15 @@ expect(input.value).to eq("Text appended") end + it "selects all in an input with the command modifier then clears" do + input = browser.at_css("#filled_input") + modifier = Ferrum::Utils::Platform.mac? ? :meta : :ctrl + + input.focus.type([modifier, "a"], :backspace) + + expect(input.value).to eq("") + end + it "sends keys to empty textarea" do input = browser.at_css("#empty_textarea") @@ -643,6 +652,15 @@ expect(input.text).to eq("Content appended") end + it "selects all with the command modifier then clears" do + input = browser.at_css("#filled_div") + modifier = Ferrum::Utils::Platform.mac? ? :meta : :ctrl + + input.focus.type([modifier, "a"], :backspace) + + expect(input.text).to eq("") + end + it "sets content" do input = browser.at_css("#filled_div")