File: firefox_node.rb

package info (click to toggle)
ruby-capybara 3.40.0%2Bds-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 2,368 kB
  • sloc: ruby: 23,988; javascript: 752; makefile: 11
file content (136 lines) | stat: -rw-r--r-- 4,457 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
# frozen_string_literal: true

require 'capybara/selenium/extensions/html5_drag'
require 'capybara/selenium/extensions/file_input_click_emulation'

class Capybara::Selenium::FirefoxNode < Capybara::Selenium::Node
  include Html5Drag
  include FileInputClickEmulation

  def click(keys = [], **options)
    super
  rescue ::Selenium::WebDriver::Error::ElementNotInteractableError
    if tag_name == 'tr'
      warn 'You are attempting to click a table row which has issues in geckodriver/marionette - ' \
           'see https://github.com/mozilla/geckodriver/issues/1228 - Your test should probably be ' \
           'clicking on a table cell like a user would. Clicking the first cell in the row instead.'
      return find_css('th:first-child,td:first-child')[0].click(keys, **options)
    end
    raise
  end

  def disabled?
    driver.evaluate_script("arguments[0].matches(':disabled, select:disabled *')", self)
  end

  def set_file(value) # rubocop:disable Naming/AccessorMethodName
    # By default files are appended so we have to clear here if its multiple and already set
    driver.execute_script(<<~JS, self)
      if (arguments[0].multiple && arguments[0].files.length){
        arguments[0].value = null;
      }
    JS
    return super if browser_version >= 62.0

    # Workaround lack of support for multiple upload by uploading one at a time
    path_names = value.to_s.empty? ? [] : Array(value)
    if (fd = bridge.file_detector) && !driver.browser.respond_to?(:upload)
      path_names.map! { |path| upload(fd.call([path])) || path }
    end
    path_names.each { |path| native.send_keys(path) }
  end

  def focused?
    driver.evaluate_script('arguments[0] == document.activeElement', self)
  end

  def send_keys(*args)
    # https://github.com/mozilla/geckodriver/issues/846
    return super(*args.map { |arg| arg == :space ? ' ' : arg }) if args.none?(Array)

    native.click unless focused?

    _send_keys(args).perform
  end

  def drop(*args)
    html5_drop(*args)
  end

  def hover
    return super unless browser_version >= 65.0

    # work around issue 2156 - https://github.com/teamcapybara/capybara/issues/2156
    scroll_if_needed { browser_action.move_to(native, 0, 0).move_to(native).perform }
  end

  def select_option
    # To optimize to only one check and then click
    selected_or_disabled = driver.evaluate_script(<<~JS, self)
      arguments[0].matches(':disabled, select:disabled *, :checked')
    JS
    click unless selected_or_disabled
  end

  def visible?
    return super unless native_displayed?

    begin
      bridge.send(:execute, :is_element_displayed, id: native_id)
    rescue Selenium::WebDriver::Error::UnknownCommandError
      # If the is_element_displayed command is unknown, no point in trying again
      driver.options[:native_displayed] = false
      super
    end
  end

private

  def native_displayed?
    (driver.options[:native_displayed] != false) && !ENV['DISABLE_CAPYBARA_SELENIUM_OPTIMIZATIONS']
  end

  def perform_with_options(click_options)
    # Firefox/marionette has an issue clicking with offset near viewport edge
    # scroll element to middle just in case
    scroll_to_center if click_options.coords?
    super
  end

  def _send_keys(keys, actions = browser_action, down_keys = ModifierKeysStack.new)
    case keys
    when :control, :left_control, :right_control,
         :alt, :left_alt, :right_alt,
         :shift, :left_shift, :right_shift,
         :meta, :left_meta, :right_meta,
         :command
      down_keys.press(keys)
      actions.key_down(keys)
    when String
      # https://bugzilla.mozilla.org/show_bug.cgi?id=1405370
      keys = keys.upcase if (browser_version < 64.0) && down_keys&.include?(:shift)
      actions.send_keys(keys)
    when Symbol
      actions.send_keys(keys)
    when Array
      down_keys.push
      keys.each { |sub_keys| _send_keys(sub_keys, actions, down_keys) }
      down_keys.pop.reverse_each { |key| actions.key_up(key) }
    else
      raise ArgumentError, 'Unknown keys type'
    end
    actions
  end

  def upload(local_file)
    return nil unless local_file
    raise ArgumentError, "You may only upload files: #{local_file.inspect}" unless File.file?(local_file)

    file = ::Selenium::WebDriver::Zipper.zip_file(local_file)
    bridge.http.call(:post, "session/#{bridge.session_id}/file", file: file)['value']
  end

  def browser_version
    driver.browser.capabilities[:browser_version].to_f
  end
end