onlyoffice_webdriver_wrapper 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. checksums.yaml +7 -0
  2. data/lib/onlyoffice_webdriver_wrapper.rb +7 -0
  3. data/lib/onlyoffice_webdriver_wrapper/dimensions.rb +22 -0
  4. data/lib/onlyoffice_webdriver_wrapper/helpers/bin/chromedriver +0 -0
  5. data/lib/onlyoffice_webdriver_wrapper/helpers/bin/chromedriver_mac +0 -0
  6. data/lib/onlyoffice_webdriver_wrapper/helpers/bin/geckodriver +0 -0
  7. data/lib/onlyoffice_webdriver_wrapper/helpers/chrome_helper.rb +66 -0
  8. data/lib/onlyoffice_webdriver_wrapper/helpers/firefox_helper.rb +37 -0
  9. data/lib/onlyoffice_webdriver_wrapper/helpers/firefox_helper/save_to_disk_files.list +35 -0
  10. data/lib/onlyoffice_webdriver_wrapper/helpers/headless_helper.rb +86 -0
  11. data/lib/onlyoffice_webdriver_wrapper/helpers/headless_helper/headless_screenshot_patch.rb +18 -0
  12. data/lib/onlyoffice_webdriver_wrapper/helpers/headless_helper/headless_video_recorder.rb +36 -0
  13. data/lib/onlyoffice_webdriver_wrapper/helpers/headless_helper/real_display_tools.rb +21 -0
  14. data/lib/onlyoffice_webdriver_wrapper/helpers/headless_helper/ruby_helper.rb +16 -0
  15. data/lib/onlyoffice_webdriver_wrapper/helpers/os_helper.rb +11 -0
  16. data/lib/onlyoffice_webdriver_wrapper/name.rb +7 -0
  17. data/lib/onlyoffice_webdriver_wrapper/version.rb +7 -0
  18. data/lib/onlyoffice_webdriver_wrapper/webdriver.rb +424 -0
  19. data/lib/onlyoffice_webdriver_wrapper/webdriver/click_methods.rb +125 -0
  20. data/lib/onlyoffice_webdriver_wrapper/webdriver/wait_until_methods.rb +71 -0
  21. data/lib/onlyoffice_webdriver_wrapper/webdriver/webdriver_alert_helper.rb +25 -0
  22. data/lib/onlyoffice_webdriver_wrapper/webdriver/webdriver_attributes_helper.rb +63 -0
  23. data/lib/onlyoffice_webdriver_wrapper/webdriver/webdriver_browser_info_helper.rb +22 -0
  24. data/lib/onlyoffice_webdriver_wrapper/webdriver/webdriver_browser_log_helper.rb +12 -0
  25. data/lib/onlyoffice_webdriver_wrapper/webdriver/webdriver_exceptions.rb +5 -0
  26. data/lib/onlyoffice_webdriver_wrapper/webdriver/webdriver_helper.rb +37 -0
  27. data/lib/onlyoffice_webdriver_wrapper/webdriver/webdriver_js_methods.rb +100 -0
  28. data/lib/onlyoffice_webdriver_wrapper/webdriver/webdriver_screenshot_helper.rb +68 -0
  29. data/lib/onlyoffice_webdriver_wrapper/webdriver/webdriver_style_helper.rb +32 -0
  30. data/lib/onlyoffice_webdriver_wrapper/webdriver/webdriver_tab_helper.rb +70 -0
  31. data/lib/onlyoffice_webdriver_wrapper/webdriver/webdriver_type_helper.rb +121 -0
  32. data/lib/onlyoffice_webdriver_wrapper/webdriver/webdriver_user_agent_helper.rb +51 -0
  33. metadata +278 -0
@@ -0,0 +1,125 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OnlyofficeWebdriverWrapper
4
+ # Method to perform different clicks
5
+ module ClickMethods
6
+ # Click on specified element
7
+ # @param element [Selenium::WebDriver::Element] element to click
8
+ # @return [nil] nothing
9
+ def click(element)
10
+ element.click
11
+ end
12
+
13
+ # Click on locator
14
+ # @param xpath_name [String] xpath to click
15
+ # @param by_javascript [True, False] should be clicked by javascript
16
+ # @param count [Integer] count of clicks
17
+ def click_on_locator(xpath_name, by_javascript = false, count: 1)
18
+ element = get_element(xpath_name)
19
+ return webdriver_error("Element with xpath: #{xpath_name} not found") if element.nil?
20
+
21
+ if by_javascript
22
+ execute_javascript("document.evaluate(\"#{xpath_name}\", document, null, XPathResult.ANY_TYPE, null).iterateNext().click();")
23
+ else
24
+ begin
25
+ count.times { element.click }
26
+ rescue Selenium::WebDriver::Error::ElementNotVisibleError => e
27
+ webdriver_error(e.class, "Selenium::WebDriver::Error::ElementNotVisibleError: element not visible for xpath: #{xpath_name}")
28
+ rescue Exception => e
29
+ webdriver_error(e.class, "UnknownError #{e.message} #{xpath_name}")
30
+ end
31
+ end
32
+ end
33
+
34
+ # Click on one of several which displayed
35
+ # @param xpath_name [String] xpath to find element
36
+ # @return [nil]
37
+ def click_on_displayed(xpath_name)
38
+ element = get_element_by_display(xpath_name)
39
+ begin
40
+ element.is_a?(Array) ? element.first.click : element.click
41
+ rescue Exception => e
42
+ webdriver_error(e.class, "Exception #{e} in click_on_displayed(#{xpath_name})")
43
+ end
44
+ end
45
+
46
+ # Click on locator by coordinates
47
+ # @param xpath_name [String] xpath to click
48
+ # @param right_by [Integer] shift to right
49
+ # @param down_by [Integer] shift to bottom
50
+ # @return [nil]
51
+ def click_on_locator_coordinates(xpath_name, right_by, down_by)
52
+ wait_until_element_visible(xpath_name)
53
+ element = @driver.find_element(:xpath, xpath_name)
54
+ @driver.action.move_to(element, right_by.to_i, down_by.to_i).perform
55
+ @driver.action.move_to(element, right_by.to_i, down_by.to_i).click.perform
56
+ end
57
+
58
+ # Click on one of several which displayed
59
+ # @param xpath_several_elements [String] xpath to find element
60
+ # @return [True, False] true if click successful, false if not found
61
+ def click_on_one_of_several_by_display(xpath_several_elements)
62
+ @driver.find_elements(:xpath, xpath_several_elements).each do |current_element|
63
+ if current_element.displayed?
64
+ current_element.click
65
+ return true
66
+ end
67
+ end
68
+ false
69
+ end
70
+
71
+ # Click on one of several xpath filtered by parameter and value
72
+ # @param xpath_several_elements [String] xpath to select
73
+ # @param parameter_name [String] parameter name
74
+ # @param parameter_value [String] parameter value
75
+ # @return [True, False] true if click successful, false if not found
76
+ def click_on_one_of_several_by_parameter(xpath_several_elements, parameter_name, parameter_value)
77
+ @driver.find_elements(:xpath, xpath_several_elements).each do |current_element|
78
+ if current_element.attribute(parameter_name).include? parameter_value
79
+ current_element.click
80
+ return true
81
+ end
82
+ end
83
+ false
84
+ end
85
+
86
+ # Perform right click on xpath
87
+ # @param xpath_name [String] xpath to click
88
+ # @return [nil]
89
+ def right_click(xpath_name)
90
+ wait_until_element_visible(xpath_name)
91
+
92
+ @driver.action.context_click(@driver.find_element(:xpath, xpath_name)).perform
93
+ end
94
+
95
+ # Perform right click on locator with specified coordinates
96
+ # @param xpath_name [String] xpath to click
97
+ # @param right_by [Integer] shift to right
98
+ # @param down_by [Integer] shift to bottom
99
+ # @return [nil]
100
+ def right_click_on_locator_coordinates(xpath_name, right_by = nil, down_by = nil)
101
+ wait_until_element_visible(xpath_name)
102
+ element = @driver.find_element(:xpath, xpath_name)
103
+ @driver.action.move_to(element, right_by.to_i, down_by.to_i).perform
104
+ @driver.action.move_to(element, right_by.to_i, down_by.to_i).context_click.perform
105
+ end
106
+
107
+ # Perform double_click on element
108
+ # @param xpath_name [String] xpath to click
109
+ # @return [nil]
110
+ def double_click(xpath_name)
111
+ wait_until_element_visible(xpath_name)
112
+ @driver.action.move_to(@driver.find_element(:xpath, xpath_name)).double_click.perform
113
+ end
114
+
115
+ # Perform double_click on specified coordinates
116
+ # @param xpath_name [String] xpath to click
117
+ # @param right_by [Integer] shift to right
118
+ # @param down_by [Integer] shift to bottom
119
+ # @return [nil]
120
+ def double_click_on_locator_coordinates(xpath_name, right_by, down_by)
121
+ wait_until_element_visible(xpath_name)
122
+ @driver.action.move_to(@driver.find_element(:xpath, xpath_name), right_by.to_i, down_by.to_i).double_click.perform
123
+ end
124
+ end
125
+ end
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OnlyofficeWebdriverWrapper
4
+ # Method to different wait_until methods
5
+ module WaitUntilMethods
6
+ # @return [Integer] default timeout for wait element
7
+ TIMEOUT_WAIT_ELEMENT = 15
8
+
9
+ def wait_until(timeout = ::PageObject.default_page_wait, message = nil, wait_js: true, &block)
10
+ tries ||= 3
11
+ wait = Object::Selenium::WebDriver::Wait.new(timeout: timeout, message: message)
12
+ wait.until(&block)
13
+ if wait_js
14
+ wait.until { document_ready? }
15
+ wait.until { jquery_finished? }
16
+ end
17
+ rescue Selenium::WebDriver::Error::TimeOutError
18
+ webdriver_error("Wait until timeout: #{timeout} seconds in")
19
+ rescue Selenium::WebDriver::Error::StaleElementReferenceError
20
+ OnlyofficeLoggerHelper.log("Wait until: rescuing from Stale Element error, #{tries} attempts remaining")
21
+ retry unless (tries -= 1).zero?
22
+ webdriver_error('Wait until: rescuing from Stale Element error failed after 3 tries')
23
+ end
24
+
25
+ def wait_until_element_visible(xpath_name, timeout = 15)
26
+ wait_until_element_present(xpath_name)
27
+ time = 0
28
+ while !element_visible?(xpath_name) && time < timeout
29
+ sleep(1)
30
+ time += 1
31
+ end
32
+ return unless time >= timeout
33
+
34
+ webdriver_error("Element #{xpath_name} not visible for #{timeout} seconds")
35
+ end
36
+
37
+ # Wait until some element present
38
+ # If timeout exceeded - raise an error
39
+ # @param xpath_name [String] xpath of element
40
+ # @param timeout [Integer] timeout to wait
41
+ # @return [Void]
42
+ def wait_until_element_present(xpath_name, timeout: TIMEOUT_WAIT_ELEMENT)
43
+ wait = Selenium::WebDriver::Wait.new(timeout: timeout) # seconds
44
+ begin
45
+ wait.until { get_element(xpath_name) }
46
+ rescue Selenium::WebDriver::Error::TimeOutError => e
47
+ timeout_message = "wait_until_element_present(#{xpath_name}) "\
48
+ 'Selenium::WebDriver::Error::TimeOutError: '\
49
+ "timed out after #{timeout} seconds"
50
+ webdriver_error(e.class, timeout_message)
51
+ end
52
+ end
53
+
54
+ # Wait until some element disappear
55
+ # If timeout exceeded - raise an error
56
+ # @param xpath_name [String] xpath of element
57
+ # @param timeout [Integer] timeout to wait
58
+ # @return [Void]
59
+ def wait_until_element_disappear(xpath_name, timeout: TIMEOUT_WAIT_ELEMENT)
60
+ wait = Selenium::WebDriver::Wait.new(timeout: timeout) # seconds
61
+ begin
62
+ wait.until { get_element(xpath_name) ? false : true }
63
+ rescue Selenium::WebDriver::Error::TimeOutError => e
64
+ timeout_message = "wait_until_element_present(#{xpath_name}) "\
65
+ 'Selenium::WebDriver::Error::TimeOutError: '\
66
+ "timed out after #{timeout} seconds"
67
+ webdriver_error(e.class, timeout_message)
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OnlyofficeWebdriverWrapper
4
+ # Methods for working with alerts
5
+ module WebdriverAlertHelper
6
+ def alert_confirm
7
+ @driver.switch_to.alert.accept if alert_exists?
8
+ end
9
+
10
+ # Check if alert exists
11
+ # @return [True, false]
12
+ def alert_exists?
13
+ @driver.switch_to.alert.text
14
+ true
15
+ rescue Selenium::WebDriver::Error::NoSuchAlertError, Errno::ECONNREFUSED
16
+ false
17
+ end
18
+
19
+ # Get alert text
20
+ # @return [String] text inside alert
21
+ def alert_text
22
+ @driver.switch_to.alert.text
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OnlyofficeWebdriverWrapper
4
+ # Module with methods to work with attributes
5
+ module WebdriverAttributesHelper
6
+ def attribute_exist?(xpath_name, attribute)
7
+ exist = false
8
+
9
+ element = xpath_name.is_a?(String) ? get_element(xpath_name) : xpath_name
10
+ begin
11
+ attribute_value = element.attribute(attribute)
12
+ exist = attribute_value.empty? || attribute_value.nil? ? false : true
13
+ rescue Exception
14
+ exist = false
15
+ end
16
+ exist
17
+ end
18
+
19
+ def get_attribute(xpath_name, attribute)
20
+ element = xpath_name.is_a?(Selenium::WebDriver::Element) ? xpath_name : get_element(xpath_name)
21
+
22
+ if element.nil?
23
+ webdriver_error("Webdriver.get_attribute(#{xpath_name}, #{attribute}) failed because element not found")
24
+ else
25
+ element.attribute(attribute)
26
+ end
27
+ end
28
+
29
+ def get_attributes_of_several_elements(xpath_several_elements, attribute)
30
+ elements = @driver.find_elements(:xpath, xpath_several_elements)
31
+
32
+ elements.map do |element|
33
+ element.attribute(attribute)
34
+ end
35
+ end
36
+
37
+ def get_index_of_elements_with_attribute(xpath, attribute, value, only_visible = true)
38
+ get_elements(xpath, only_visible).each_with_index do |element, index|
39
+ return (index + 1) if get_attribute(element, attribute).include?(value)
40
+ end
41
+ 0
42
+ end
43
+
44
+ # Set element attribute
45
+ # @param xpath [String] element to select
46
+ # @param attribute [String] attribute to set
47
+ # @param attribute_value [String] value of attribute
48
+ # @return [String] result of execution
49
+ def set_attribute(xpath, attribute, attribute_value)
50
+ execute_javascript("#{dom_element_by_xpath(xpath)}.#{attribute}=\"#{attribute_value}\";")
51
+ end
52
+
53
+ alias set_parameter set_attribute
54
+
55
+ # Remove attribute of element
56
+ # @param xpath [String] xpath of element
57
+ # @param attribute [String] attribute to remove
58
+ # @return [String] result of execution
59
+ def remove_attribute(xpath, attribute)
60
+ execute_javascript("#{dom_element_by_xpath(xpath)}.removeAttribute('#{attribute}');")
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OnlyofficeWebdriverWrapper
4
+ # Module for working with browser metadata
5
+ module WebdriverBrowserInfo
6
+ # @return [Dimensions] Size of browser window
7
+ def browser_size
8
+ size_struct = @driver.manage.window.size
9
+ size = Dimensions.new(size_struct.width, size_struct.height)
10
+ OnlyofficeLoggerHelper.log("browser_size: #{size}")
11
+ size
12
+ end
13
+
14
+ # @return [String] info about platform
15
+ def browser_metadata
16
+ caps = @driver.capabilities
17
+ platform = caps[:platform_name] || caps[:platform]
18
+ version = caps[:browser_version] || caps[:version]
19
+ "Platform: #{platform}, Browser: #{caps[:browser_name]}, Version: #{version}"
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OnlyofficeWebdriverWrapper
4
+ # Methods for working with browser console logs
5
+ module WebdriverBrowserLogHelper
6
+ def browser_logs
7
+ return [] if browser == :firefox # not implemented, see https://github.com/mozilla/geckodriver/issues/284
8
+
9
+ @driver.manage.logs.get(:browser)
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OnlyofficeWebdriverWrapper
4
+ class WebdriverSystemNotSupported < StandardError; end
5
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'open-uri'
4
+ require 'tempfile'
5
+
6
+ module OnlyofficeWebdriverWrapper
7
+ # Some additional methods for webdriver
8
+ module WebdriverHelper
9
+ def system_screenshot(file_name)
10
+ `import -window root #{file_name}`
11
+ end
12
+
13
+ # Download temp file and return it location
14
+ # @param file [String] url
15
+ # @return [String] path to file
16
+ def download(file_url)
17
+ data = Kernel.open(file_url, &:read)
18
+ file = Tempfile.new('onlyoffice-downloaded-file')
19
+ file.write(data.force_encoding('UTF-8'))
20
+ file.close
21
+ file.path
22
+ end
23
+
24
+ # Perform safe cleanup of download folder
25
+ # @return [Nothing]
26
+ def cleanup_download_folder
27
+ return unless Dir.exist?(@download_directory)
28
+
29
+ if @download_directory.start_with?(Dir.tmpdir)
30
+ FileUtils.remove_dir(@download_directory)
31
+ else
32
+ OnlyofficeLoggerHelper.log("Download directory #{@download_directory} is not at tmp dir. "\
33
+ 'It will be not deleted')
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,100 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OnlyofficeWebdriverWrapper
4
+ # Methods for webdriver for calling Javascript
5
+ module WebdriverJsMethods
6
+ # Execute javascript
7
+ # @param script [String] code to execute
8
+ # @param wait_timeout [Integer] wait after JS is executed.
9
+ # Some code require some time to execute
10
+ def execute_javascript(script, wait_timeout: 0)
11
+ result = @driver.execute_script(script)
12
+ OnlyofficeLoggerHelper.log("Executed js: `#{script}` with result: `#{result}`")
13
+ sleep(wait_timeout)
14
+ result
15
+ rescue Exception => e
16
+ webdriver_error("Exception #{e} in execute_javascript: #{script}")
17
+ end
18
+
19
+ # @param [String] xpath element to select
20
+ # @return [String] string to select dom by xpath
21
+ def dom_element_by_xpath(xpath)
22
+ escaped_xpath = xpath.tr('"', "'")
23
+ "document.evaluate(\"#{escaped_xpath}\", document, null, XPathResult.ANY_TYPE, null).iterateNext()"
24
+ end
25
+
26
+ def type_to_locator_by_javascript(xpath_name, text)
27
+ escaped_text = text.gsub('\\', '\\\\\\\\').gsub('"', '\\"').gsub("\n", '\\n')
28
+ execute_javascript("document.evaluate('#{xpath_name}', document, null, XPathResult.ANY_TYPE, null).iterateNext().value=\"#{escaped_text}\";")
29
+ end
30
+
31
+ # Get text in object by xpath
32
+ # @param xpath [String] xpath to get text
33
+ # @return [String] text in xpath
34
+ def get_text_by_js(xpath)
35
+ text = execute_javascript("return #{dom_element_by_xpath(xpath)}.textContent")
36
+ text = execute_javascript("return #{dom_element_by_xpath(xpath)}.value") if text.empty?
37
+ text
38
+ end
39
+
40
+ # Calculate object size using Javascript
41
+ # @param xpath [Sting] xpath of object
42
+ # @return [Dimensions] size of element
43
+ def element_size_by_js(xpath)
44
+ width = execute_javascript("return #{dom_element_by_xpath(xpath)}.offsetWidth")
45
+ height = execute_javascript("return #{dom_element_by_xpath(xpath)}.offsetHeight")
46
+ Dimensions.new(width, height)
47
+ end
48
+
49
+ # Get object absolute postion from top left edge of screen
50
+ # @param xpath [Sting] xpath of object
51
+ # @return [CursorPoint] position of element
52
+ def object_absolute_position(xpath)
53
+ bounding = "#{dom_element_by_xpath(xpath)}.getBoundingClientRect()"
54
+ left = execute_javascript("return #{bounding}.left")
55
+ top = execute_javascript("return #{bounding}.top")
56
+ Dimensions.new(left, top)
57
+ end
58
+
59
+ # @return [True, False] is page have jquery loaded
60
+ def jquery_loaded?
61
+ loaded = execute_javascript('return !!window.jQuery')
62
+ OnlyofficeLoggerHelper.log("jquery_loaded? # #{loaded}")
63
+ loaded
64
+ end
65
+
66
+ # Checks for jQuery finished its job or not present
67
+ def jquery_finished?
68
+ return true unless jquery_loaded?
69
+
70
+ execute_javascript('return window.jQuery.active;').zero?
71
+ end
72
+
73
+ def document_ready?
74
+ execute_javascript('return document.readyState;') == 'complete'
75
+ end
76
+
77
+ # Get ComputedStyle property value
78
+ # @param xpath [String] xpath of element
79
+ # @param pseudo_element [String] pseudo element to compute
80
+ # @param property [String] property to get
81
+ # @return [String] value of property
82
+ def computed_style(xpath, pseudo_element = 'null', property = nil)
83
+ element_by_xpath = "document.evaluate(\"#{xpath.tr('"', "'")}\",document, null, XPathResult.ANY_TYPE, null ).iterateNext()"
84
+ style = "window.getComputedStyle(#{element_by_xpath}, '#{pseudo_element}')"
85
+ full_command = "#{style}.getPropertyValue('#{property}')"
86
+ result = execute_javascript("return #{full_command}")
87
+ result.delete('"')
88
+ end
89
+
90
+ # Remove element by its xpath
91
+ # @param [String] xpath of element to remove
92
+ # @return [String] result of javascript execution
93
+ def remove_element(xpath)
94
+ script = "element = #{dom_element_by_xpath(xpath)};"\
95
+ 'if (element !== null) '\
96
+ '{element.parentNode.removeChild(element);};'
97
+ execute_javascript(script)
98
+ end
99
+ end
100
+ end