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.
- checksums.yaml +7 -0
- data/lib/onlyoffice_webdriver_wrapper.rb +7 -0
- data/lib/onlyoffice_webdriver_wrapper/dimensions.rb +22 -0
- data/lib/onlyoffice_webdriver_wrapper/helpers/bin/chromedriver +0 -0
- data/lib/onlyoffice_webdriver_wrapper/helpers/bin/chromedriver_mac +0 -0
- data/lib/onlyoffice_webdriver_wrapper/helpers/bin/geckodriver +0 -0
- data/lib/onlyoffice_webdriver_wrapper/helpers/chrome_helper.rb +66 -0
- data/lib/onlyoffice_webdriver_wrapper/helpers/firefox_helper.rb +37 -0
- data/lib/onlyoffice_webdriver_wrapper/helpers/firefox_helper/save_to_disk_files.list +35 -0
- data/lib/onlyoffice_webdriver_wrapper/helpers/headless_helper.rb +86 -0
- data/lib/onlyoffice_webdriver_wrapper/helpers/headless_helper/headless_screenshot_patch.rb +18 -0
- data/lib/onlyoffice_webdriver_wrapper/helpers/headless_helper/headless_video_recorder.rb +36 -0
- data/lib/onlyoffice_webdriver_wrapper/helpers/headless_helper/real_display_tools.rb +21 -0
- data/lib/onlyoffice_webdriver_wrapper/helpers/headless_helper/ruby_helper.rb +16 -0
- data/lib/onlyoffice_webdriver_wrapper/helpers/os_helper.rb +11 -0
- data/lib/onlyoffice_webdriver_wrapper/name.rb +7 -0
- data/lib/onlyoffice_webdriver_wrapper/version.rb +7 -0
- data/lib/onlyoffice_webdriver_wrapper/webdriver.rb +424 -0
- data/lib/onlyoffice_webdriver_wrapper/webdriver/click_methods.rb +125 -0
- data/lib/onlyoffice_webdriver_wrapper/webdriver/wait_until_methods.rb +71 -0
- data/lib/onlyoffice_webdriver_wrapper/webdriver/webdriver_alert_helper.rb +25 -0
- data/lib/onlyoffice_webdriver_wrapper/webdriver/webdriver_attributes_helper.rb +63 -0
- data/lib/onlyoffice_webdriver_wrapper/webdriver/webdriver_browser_info_helper.rb +22 -0
- data/lib/onlyoffice_webdriver_wrapper/webdriver/webdriver_browser_log_helper.rb +12 -0
- data/lib/onlyoffice_webdriver_wrapper/webdriver/webdriver_exceptions.rb +5 -0
- data/lib/onlyoffice_webdriver_wrapper/webdriver/webdriver_helper.rb +37 -0
- data/lib/onlyoffice_webdriver_wrapper/webdriver/webdriver_js_methods.rb +100 -0
- data/lib/onlyoffice_webdriver_wrapper/webdriver/webdriver_screenshot_helper.rb +68 -0
- data/lib/onlyoffice_webdriver_wrapper/webdriver/webdriver_style_helper.rb +32 -0
- data/lib/onlyoffice_webdriver_wrapper/webdriver/webdriver_tab_helper.rb +70 -0
- data/lib/onlyoffice_webdriver_wrapper/webdriver/webdriver_type_helper.rb +121 -0
- data/lib/onlyoffice_webdriver_wrapper/webdriver/webdriver_user_agent_helper.rb +51 -0
- metadata +278 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 01e7c8a03fdec74828c4f660d287be5729f56f29ce49b91f74005e29a666942e
|
4
|
+
data.tar.gz: bd3f969c07b3c5043b6329c07fff8e02d9576dfa373eef527af6738db1be9acd
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: e5a02e944fd3b617209598f54274dcec25776695ed050576e9f7b92371de8c8bb72632ef8460c1bf0390a338f0e910f8610d590b59b449f29c08a93618440e99
|
7
|
+
data.tar.gz: dd7151ef729280ba654f37a5c4842f62b10c3d6c4b47f64df0e02e9a0d9658ad1a8c7c6c8ad4e44b2b9a87ea2a1e4092913ee290165006dd5ad00745f7bbd755
|
@@ -0,0 +1,7 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'onlyoffice_logger_helper'
|
4
|
+
require_relative 'onlyoffice_webdriver_wrapper/helpers/headless_helper'
|
5
|
+
require_relative 'onlyoffice_webdriver_wrapper/helpers/os_helper'
|
6
|
+
require_relative 'onlyoffice_webdriver_wrapper/dimensions'
|
7
|
+
require_relative 'onlyoffice_webdriver_wrapper/webdriver'
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module OnlyofficeWebdriverWrapper
|
4
|
+
# Class for working with cursor coordinates
|
5
|
+
class Dimensions
|
6
|
+
attr_accessor :left, :top
|
7
|
+
|
8
|
+
def initialize(left, top)
|
9
|
+
@left = left
|
10
|
+
@top = top
|
11
|
+
end
|
12
|
+
|
13
|
+
alias width left
|
14
|
+
alias height top
|
15
|
+
alias x left
|
16
|
+
alias y top
|
17
|
+
|
18
|
+
def to_s
|
19
|
+
"Dimensions(left: #{@left}, top: #{@top})"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
Binary file
|
Binary file
|
Binary file
|
@@ -0,0 +1,66 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module OnlyofficeWebdriverWrapper
|
4
|
+
# Class for working with Chrome
|
5
|
+
module ChromeHelper
|
6
|
+
DEFAULT_CHROME_SWITCHES = %w[--kiosk-printing
|
7
|
+
--disable-popup-blocking
|
8
|
+
--disable-infobars
|
9
|
+
--no-sandbox
|
10
|
+
test-type].freeze
|
11
|
+
|
12
|
+
# @return [String] path to chromedriver
|
13
|
+
def chromedriver_path
|
14
|
+
driver_name = 'bin/chromedriver'
|
15
|
+
driver_name = 'bin/chromedriver_mac' if OSHelper.mac?
|
16
|
+
File.join(File.dirname(__FILE__), driver_name)
|
17
|
+
end
|
18
|
+
|
19
|
+
def chrome_service
|
20
|
+
@chrome_service ||= Selenium::WebDriver::Chrome::Service.new(path: chromedriver_path)
|
21
|
+
end
|
22
|
+
|
23
|
+
# @return [Webdriver::Chrome] Chrome webdriver
|
24
|
+
def start_chrome_driver
|
25
|
+
prefs = {
|
26
|
+
download: {
|
27
|
+
'prompt_for_download' => false,
|
28
|
+
'default_directory' => download_directory
|
29
|
+
},
|
30
|
+
profile: {
|
31
|
+
'default_content_setting_values' => {
|
32
|
+
'automatic_downloads' => 1
|
33
|
+
}
|
34
|
+
},
|
35
|
+
credentials_enable_service: false
|
36
|
+
}
|
37
|
+
caps = Selenium::WebDriver::Remote::Capabilities.chrome
|
38
|
+
caps[:logging_prefs] = { browser: 'ALL' }
|
39
|
+
caps[:proxy] = Selenium::WebDriver::Proxy.new(ssl: "#{@proxy.proxy_address}:#{@proxy.proxy_port}") if @proxy
|
40
|
+
switches = add_useragent_to_switches(DEFAULT_CHROME_SWITCHES)
|
41
|
+
options = Selenium::WebDriver::Chrome::Options.new(args: switches,
|
42
|
+
prefs: prefs)
|
43
|
+
webdriver_options = { options: options,
|
44
|
+
desired_capabilities: caps,
|
45
|
+
service: chrome_service }
|
46
|
+
driver = Selenium::WebDriver.for :chrome, webdriver_options
|
47
|
+
maximize_chrome(driver)
|
48
|
+
driver
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
# Maximize chrome
|
54
|
+
# @param driver [Selenium::WebDriver] driver to use
|
55
|
+
# @return [Void]
|
56
|
+
def maximize_chrome(driver)
|
57
|
+
if headless.running?
|
58
|
+
# Cannot use `driver.manage.window.maximize` in xvfb session
|
59
|
+
# according to https://bugs.chromium.org/p/chromedriver/issues/detail?id=1901#c16
|
60
|
+
driver.manage.window.size = Selenium::WebDriver::Dimension.new(headless.resolution_x, headless.resolution_y)
|
61
|
+
else
|
62
|
+
driver.manage.window.maximize
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module OnlyofficeWebdriverWrapper
|
4
|
+
# Module for working with firefox
|
5
|
+
module FirefoxHelper
|
6
|
+
def firefox_service
|
7
|
+
geckodriver = File.join(File.dirname(__FILE__), 'bin/geckodriver')
|
8
|
+
@firefox_service ||= Selenium::WebDriver::Firefox::Service.new(path: geckodriver)
|
9
|
+
end
|
10
|
+
|
11
|
+
# @return [Webdriver::Firefox] firefox webdriver
|
12
|
+
def start_firefox_driver
|
13
|
+
profile = Selenium::WebDriver::Firefox::Profile.new
|
14
|
+
profile['browser.download.folderList'] = 2
|
15
|
+
profile['browser.helperApps.neverAsk.saveToDisk'] = read_firefox_files_to_save
|
16
|
+
profile['browser.download.dir'] = @download_directory
|
17
|
+
profile['browser.download.manager.showWhenStarting'] = false
|
18
|
+
profile['dom.disable_window_move_resize'] = false
|
19
|
+
options = Selenium::WebDriver::Firefox::Options.new(profile: profile)
|
20
|
+
caps = Selenium::WebDriver::Remote::Capabilities.firefox
|
21
|
+
caps[:proxy] = Selenium::WebDriver::Proxy.new(ssl: "#{@proxy.proxy_address}:#{@proxy.proxy_port}") if @proxy
|
22
|
+
driver = Selenium::WebDriver.for :firefox, options: options, service: firefox_service, desired_capabilities: caps
|
23
|
+
driver.manage.window.size = Selenium::WebDriver::Dimension.new(headless.resolution_x, headless.resolution_y) if headless.running?
|
24
|
+
driver
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
# @return [Array<String>] list of formats to save
|
30
|
+
def read_firefox_files_to_save
|
31
|
+
path_to_file = "#{Dir.pwd}/lib/onlyoffice_webdriver_wrapper/"\
|
32
|
+
'helpers/firefox_helper/save_to_disk_files.list'
|
33
|
+
OnlyofficeFileHelper::FileHelper.read_array_from_file(path_to_file)
|
34
|
+
.join(', ')
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
application/doct
|
2
|
+
application/mspowerpoint
|
3
|
+
application/msword
|
4
|
+
application/octet-stream
|
5
|
+
application/oleobject
|
6
|
+
application/pdf
|
7
|
+
application/powerpoint
|
8
|
+
application/pptt
|
9
|
+
application/rtf
|
10
|
+
application/vnd.ms-excel
|
11
|
+
application/vnd.ms-powerpoint
|
12
|
+
application/vnd.oasis.opendocument.spreadsheet
|
13
|
+
application/vnd.oasis.opendocument.text
|
14
|
+
application/vnd.openxmlformats-officedocument.presentationml.presentation
|
15
|
+
application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
|
16
|
+
application/vnd.openxmlformats-officedocument.wordprocessingml.document
|
17
|
+
application/x-compressed
|
18
|
+
application/x-excel
|
19
|
+
application/xlst
|
20
|
+
application/x-msexcel
|
21
|
+
application/x-mspowerpoint
|
22
|
+
application/x-rtf
|
23
|
+
application/x-zip-compressed
|
24
|
+
application/zip
|
25
|
+
image/jpeg
|
26
|
+
image/pjpeg
|
27
|
+
image/pjpeg
|
28
|
+
image/x-jps
|
29
|
+
message/rfc822
|
30
|
+
multipart/x-zip
|
31
|
+
text/csv
|
32
|
+
text/html
|
33
|
+
text/html
|
34
|
+
text/plain
|
35
|
+
text/richtext
|
@@ -0,0 +1,86 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'headless_helper/real_display_tools'
|
4
|
+
require_relative 'headless_helper/ruby_helper'
|
5
|
+
require 'headless'
|
6
|
+
require_relative 'headless_helper/headless_screenshot_patch'
|
7
|
+
require_relative 'headless_helper/headless_video_recorder'
|
8
|
+
|
9
|
+
module OnlyofficeWebdriverWrapper
|
10
|
+
# Class for using headless gem
|
11
|
+
class HeadlessHelper
|
12
|
+
include HeadlessVideoRecorder
|
13
|
+
include RealDisplayTools
|
14
|
+
include RubyHelper
|
15
|
+
# @return [Headless] instance of headless object
|
16
|
+
attr_accessor :headless_instance
|
17
|
+
# @return [Integer] x resolution of virtual screen
|
18
|
+
attr_accessor :resolution_x
|
19
|
+
# @return [Integer] y resolution of virtual screen
|
20
|
+
attr_accessor :resolution_y
|
21
|
+
# @return [True, False] is video should be recorded
|
22
|
+
attr_reader :record_video
|
23
|
+
|
24
|
+
def initialize(resolution_x = 1680,
|
25
|
+
resolution_y = 1050,
|
26
|
+
record_video: true)
|
27
|
+
@resolution_x = resolution_x
|
28
|
+
@resolution_y = resolution_y
|
29
|
+
@record_video = record_video
|
30
|
+
end
|
31
|
+
|
32
|
+
# Check if should start headless
|
33
|
+
# @return [True, False] result
|
34
|
+
def should_start?
|
35
|
+
return false if debug?
|
36
|
+
return false if OSHelper.mac?
|
37
|
+
|
38
|
+
true
|
39
|
+
end
|
40
|
+
|
41
|
+
def start
|
42
|
+
create_session = if real_display_connected?
|
43
|
+
should_start?
|
44
|
+
else
|
45
|
+
true
|
46
|
+
end
|
47
|
+
return unless create_session
|
48
|
+
|
49
|
+
OnlyofficeLoggerHelper.log('Starting Headless Session')
|
50
|
+
begin
|
51
|
+
@headless_instance = Headless.new(reuse: false,
|
52
|
+
destroy_at_exit: true,
|
53
|
+
dimensions: "#{@resolution_x + 1}x#{@resolution_y + 1}x24",
|
54
|
+
video: { provider: :ffmpeg })
|
55
|
+
rescue Exception => e
|
56
|
+
OnlyofficeLoggerHelper.log("xvfb not started with problem #{e}")
|
57
|
+
WebDriver.clean_up(true)
|
58
|
+
@headless_instance = Headless.new(reuse: false,
|
59
|
+
destroy_at_exit: true,
|
60
|
+
dimensions: "#{@resolution_x + 1}x#{@resolution_y + 1}x24",
|
61
|
+
video: { provider: :ffmpeg })
|
62
|
+
end
|
63
|
+
headless_instance.start
|
64
|
+
start_capture
|
65
|
+
end
|
66
|
+
|
67
|
+
def stop
|
68
|
+
return unless running?
|
69
|
+
|
70
|
+
OnlyofficeLoggerHelper.log('Stopping Headless Session')
|
71
|
+
stop_capture
|
72
|
+
headless_instance.destroy
|
73
|
+
end
|
74
|
+
|
75
|
+
def running?
|
76
|
+
!headless_instance.nil?
|
77
|
+
end
|
78
|
+
|
79
|
+
def take_screenshot(scr_path = '/tmp/screenshot.png')
|
80
|
+
return unless running?
|
81
|
+
|
82
|
+
headless_instance.take_screenshot(scr_path)
|
83
|
+
OnlyofficeLoggerHelper.log("Took Screenshot to file: #{scr_path}")
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
class Headless
|
2
|
+
def take_screenshot(file_path, options={})
|
3
|
+
using = options.fetch(:using, :imagemagick)
|
4
|
+
case using
|
5
|
+
when :imagemagick
|
6
|
+
CliUtil.ensure_application_exists!('import', "imagemagick is not found on your system. Please install it using sudo apt-get install imagemagick")
|
7
|
+
system "#{CliUtil.path_to('import')} -display :#{display} -window root #{file_path}"
|
8
|
+
when :xwd
|
9
|
+
CliUtil.ensure_application_exists!('xwd', "xwd is not found on your system. Please install it using sudo apt-get install X11-apps")
|
10
|
+
system "#{CliUtil.path_to('xwd')} -display localhost:#{display} -silent -root -out #{file_path}"
|
11
|
+
when :graphicsmagick, :gm
|
12
|
+
CliUtil.ensure_application_exists!('gm', "graphicsmagick is not found on your system. Please install it.")
|
13
|
+
system "#{CliUtil.path_to('gm')} import -display localhost:#{display} -window root #{file_path}"
|
14
|
+
else
|
15
|
+
raise Headless::Exception.new('Unknown :using option value')
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module OnlyofficeWebdriverWrapper
|
4
|
+
# Methods to record video
|
5
|
+
module HeadlessVideoRecorder
|
6
|
+
# @return [String] uniq file path to recorded file
|
7
|
+
def recorded_video_file
|
8
|
+
return @recorded_video_file if @recorded_video_file
|
9
|
+
|
10
|
+
file_pattern = 'onlyoffice_webdriver_wrapper_video_file'
|
11
|
+
temp_file = Tempfile.new([file_pattern, '.mp4'])
|
12
|
+
@recorded_video_file = temp_file.path
|
13
|
+
temp_file.unlink
|
14
|
+
@recorded_video_file
|
15
|
+
end
|
16
|
+
|
17
|
+
# @return [nil] start capture of file
|
18
|
+
def start_capture
|
19
|
+
headless_instance.video.start_capture if record_video
|
20
|
+
rescue Headless::Exception => e
|
21
|
+
OnlyofficeLoggerHelper.log("Cannot start video capture: #{e}")
|
22
|
+
@record_video = false
|
23
|
+
end
|
24
|
+
|
25
|
+
# @return [nil] stop catpure of file
|
26
|
+
def stop_capture
|
27
|
+
return unless record_video
|
28
|
+
|
29
|
+
headless_instance.video.stop_and_save(recorded_video_file)
|
30
|
+
OnlyofficeLoggerHelper.log("Video is saved to #{recorded_video_file}")
|
31
|
+
rescue Headless::Exception => e
|
32
|
+
OnlyofficeLoggerHelper.log("Saving recorded video failed: #{e}")
|
33
|
+
@record_video = false
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module OnlyofficeWebdriverWrapper
|
4
|
+
# module for getting info about real display
|
5
|
+
module RealDisplayTools
|
6
|
+
def xrandr_result
|
7
|
+
result = `xrandr 2>&1`
|
8
|
+
OnlyofficeLoggerHelper.log("xrandr answer: #{result}".delete("\n"))
|
9
|
+
result
|
10
|
+
end
|
11
|
+
|
12
|
+
def real_display_connected?
|
13
|
+
return true if OSHelper.mac?
|
14
|
+
|
15
|
+
result = xrandr_result
|
16
|
+
exists = result.include?(' connected') && !result.include?('Failed')
|
17
|
+
OnlyofficeLoggerHelper.log("Real Display Exists: #{exists}")
|
18
|
+
exists
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module OnlyofficeWebdriverWrapper
|
4
|
+
# Module for check ruby info
|
5
|
+
module RubyHelper
|
6
|
+
def debug?
|
7
|
+
ENV['RUBYLIB'].to_s.include?('ruby-debug')
|
8
|
+
end
|
9
|
+
|
10
|
+
# Check if current os is 64 bit
|
11
|
+
# @return [True, False] result of comparision
|
12
|
+
def os_64_bit?
|
13
|
+
RUBY_PLATFORM.include?('_64')
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,424 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'onlyoffice_file_helper'
|
4
|
+
require 'page-object'
|
5
|
+
require 'securerandom'
|
6
|
+
require 'selenium-webdriver'
|
7
|
+
require 'uri'
|
8
|
+
require_relative 'helpers/chrome_helper'
|
9
|
+
require_relative 'helpers/firefox_helper'
|
10
|
+
require_relative 'webdriver/click_methods'
|
11
|
+
require_relative 'webdriver/wait_until_methods'
|
12
|
+
require_relative 'webdriver/webdriver_alert_helper'
|
13
|
+
require_relative 'webdriver/webdriver_attributes_helper'
|
14
|
+
require_relative 'webdriver/webdriver_browser_info_helper'
|
15
|
+
require_relative 'webdriver/webdriver_type_helper'
|
16
|
+
require_relative 'webdriver/webdriver_exceptions'
|
17
|
+
require_relative 'webdriver/webdriver_helper'
|
18
|
+
require_relative 'webdriver/webdriver_js_methods'
|
19
|
+
require_relative 'webdriver/webdriver_screenshot_helper'
|
20
|
+
require_relative 'webdriver/webdriver_style_helper'
|
21
|
+
require_relative 'webdriver/webdriver_tab_helper'
|
22
|
+
require_relative 'webdriver/webdriver_user_agent_helper'
|
23
|
+
require_relative 'webdriver/webdriver_browser_log_helper'
|
24
|
+
|
25
|
+
module OnlyofficeWebdriverWrapper
|
26
|
+
# noinspection RubyTooManyMethodsInspection, RubyInstanceMethodNamingConvention, RubyParameterNamingConvention
|
27
|
+
class WebDriver
|
28
|
+
include ChromeHelper
|
29
|
+
include ClickMethods
|
30
|
+
include FirefoxHelper
|
31
|
+
include RubyHelper
|
32
|
+
include WaitUntilMethods
|
33
|
+
include WebdriverAlertHelper
|
34
|
+
include WebdriverAttributesHelper
|
35
|
+
include WebdriverBrowserInfo
|
36
|
+
include WebdriverTypeHelper
|
37
|
+
include WebdriverHelper
|
38
|
+
include WebdriverJsMethods
|
39
|
+
include WebdriverScreenshotHelper
|
40
|
+
include WebdriverStyleHelper
|
41
|
+
include WebdriverTabHelper
|
42
|
+
include WebdriverUserAgentHelper
|
43
|
+
include WebdriverBrowserLogHelper
|
44
|
+
TIMEOUT_FILE_DOWNLOAD = 100
|
45
|
+
# @return [Array, String] default switches for chrome
|
46
|
+
attr_accessor :driver
|
47
|
+
# @return [Symbol] browser to use
|
48
|
+
attr_accessor :browser
|
49
|
+
# @return [True, False] is browser currently running
|
50
|
+
attr_reader :browser_running
|
51
|
+
# @return [Symbol] device of which we try to simulate, default - :desktop_linux
|
52
|
+
attr_accessor :device
|
53
|
+
# @return [String] directory to which file is downloaded
|
54
|
+
attr_accessor :download_directory
|
55
|
+
# @return [HeadlessHelper] headless wrapper
|
56
|
+
attr_accessor :headless
|
57
|
+
# @return [Net::HTTP::Proxy] connection proxy
|
58
|
+
attr_accessor :proxy
|
59
|
+
|
60
|
+
def initialize(browser = :firefox,
|
61
|
+
_remote_server = nil,
|
62
|
+
device: :desktop_linux,
|
63
|
+
proxy: nil)
|
64
|
+
raise WebdriverSystemNotSupported, 'Your OS is not 64 bit. It is not supported' unless os_64_bit?
|
65
|
+
|
66
|
+
@device = device
|
67
|
+
@headless = HeadlessHelper.new
|
68
|
+
@headless.start
|
69
|
+
|
70
|
+
@download_directory = Dir.mktmpdir('webdriver-download')
|
71
|
+
@browser = browser
|
72
|
+
@proxy = proxy
|
73
|
+
case browser
|
74
|
+
when :firefox
|
75
|
+
@driver = start_firefox_driver
|
76
|
+
when :chrome
|
77
|
+
@driver = start_chrome_driver
|
78
|
+
else
|
79
|
+
raise("Unknown Browser: #{browser}")
|
80
|
+
end
|
81
|
+
@browser_running = true
|
82
|
+
end
|
83
|
+
|
84
|
+
def open(url)
|
85
|
+
url = "http://#{url}" unless url.include?('http') || url.include?('file://')
|
86
|
+
loop do
|
87
|
+
@driver.navigate.to url
|
88
|
+
break
|
89
|
+
rescue Timeout::Error
|
90
|
+
@driver.navigate.refresh
|
91
|
+
end
|
92
|
+
sleep(1) # Correct wait for Page to init focus
|
93
|
+
OnlyofficeLoggerHelper.log("Opened page: #{url}")
|
94
|
+
end
|
95
|
+
|
96
|
+
def quit
|
97
|
+
return unless browser_running
|
98
|
+
|
99
|
+
begin
|
100
|
+
@driver.execute_script('window.onbeforeunload = null') # off popup window
|
101
|
+
rescue StandardError
|
102
|
+
Exception
|
103
|
+
end
|
104
|
+
begin
|
105
|
+
@driver.quit
|
106
|
+
rescue Exception => e
|
107
|
+
OnlyofficeLoggerHelper.log("Some error happened on webdriver.quit #{e.backtrace}")
|
108
|
+
end
|
109
|
+
alert_confirm
|
110
|
+
@headless.stop
|
111
|
+
cleanup_download_folder
|
112
|
+
@browser_running = false
|
113
|
+
end
|
114
|
+
|
115
|
+
def get_element(object_identification)
|
116
|
+
return object_identification unless object_identification.is_a?(String)
|
117
|
+
|
118
|
+
@driver.find_element(:xpath, object_identification)
|
119
|
+
rescue StandardError
|
120
|
+
nil
|
121
|
+
end
|
122
|
+
|
123
|
+
# Get text from all elements with specified xpath
|
124
|
+
# @param array_elements [String] xpath of elements
|
125
|
+
# @return [Array<String>] values of elements
|
126
|
+
def get_text_array(array_elements)
|
127
|
+
get_elements(array_elements).map { |current_element| get_text(current_element) }
|
128
|
+
end
|
129
|
+
|
130
|
+
def select_from_list_elements(value, elements_value)
|
131
|
+
index = get_element_index(value, elements_value)
|
132
|
+
elements_value[index].click
|
133
|
+
end
|
134
|
+
|
135
|
+
def get_element_index(title, list_elements)
|
136
|
+
list_elements.each_with_index do |current, i|
|
137
|
+
return i if get_text(current) == title
|
138
|
+
end
|
139
|
+
nil
|
140
|
+
end
|
141
|
+
|
142
|
+
def get_all_combo_box_values(xpath_name)
|
143
|
+
@driver.find_element(:xpath, xpath_name).find_elements(tag_name: 'option').map { |el| el.attribute('value') }
|
144
|
+
end
|
145
|
+
|
146
|
+
# @return [String] url of current frame, or browser url if
|
147
|
+
# it is a root frame
|
148
|
+
def get_url
|
149
|
+
execute_javascript('return window.location.href')
|
150
|
+
rescue Selenium::WebDriver::Error::NoSuchDriverError
|
151
|
+
raise 'Browser is crushed or hangup'
|
152
|
+
end
|
153
|
+
|
154
|
+
def refresh
|
155
|
+
@driver.navigate.refresh
|
156
|
+
OnlyofficeLoggerHelper.log('Refresh page')
|
157
|
+
end
|
158
|
+
|
159
|
+
def go_back
|
160
|
+
@driver.navigate.back
|
161
|
+
OnlyofficeLoggerHelper.log('Go back to previous page')
|
162
|
+
end
|
163
|
+
|
164
|
+
# Perform drag'n'drop action in one element (for example on big canvas area)
|
165
|
+
# for drag'n'drop one whole element use 'drag_and_drop_by'
|
166
|
+
# ==== Attributes
|
167
|
+
#
|
168
|
+
# * +xpath+ - xpath of element on which drag and drop performed
|
169
|
+
# * +x1+ - x coordinate on element to start drag'n'drop
|
170
|
+
# * +y1+ - y coordinate on element to start drag'n'drop
|
171
|
+
# * +x2+ - shift vector x coordinate
|
172
|
+
# * +y2+ - shift vector y coordinate
|
173
|
+
# * +mouse_release+ - release mouse after move
|
174
|
+
def drag_and_drop(xpath, x1, y1, x2, y2, mouse_release: true)
|
175
|
+
canvas = get_element(xpath)
|
176
|
+
|
177
|
+
move_action = @driver.action
|
178
|
+
.move_to(canvas, x1.to_i, y1.to_i)
|
179
|
+
.click_and_hold
|
180
|
+
.move_by(x2, y2)
|
181
|
+
move_action = move_action.release if mouse_release
|
182
|
+
|
183
|
+
move_action.perform
|
184
|
+
end
|
185
|
+
|
186
|
+
# Perform drag'n'drop one whole element
|
187
|
+
# for drag'n'drop inside one element (f.e. canvas) use drag_and_drop
|
188
|
+
# ==== Attributes
|
189
|
+
#
|
190
|
+
# * +source+ - xpath of element on which drag and drop performed
|
191
|
+
# * +right_by+ - shift vector x coordinate
|
192
|
+
# * +down_by+ - shift vector y coordinate
|
193
|
+
def drag_and_drop_by(source, right_by, down_by = 0)
|
194
|
+
@driver.action.drag_and_drop_by(get_element(source), right_by, down_by).perform
|
195
|
+
end
|
196
|
+
|
197
|
+
def scroll_list_to_element(list_xpath, element_xpath)
|
198
|
+
execute_javascript("$(document.evaluate(\"#{list_xpath}\", document, null, XPathResult.ANY_TYPE, null).
|
199
|
+
iterateNext()).jScrollPane().data('jsp').scrollToElement(document.evaluate(\"#{element_xpath}\",
|
200
|
+
document, null, XPathResult.ANY_TYPE, null).iterateNext());")
|
201
|
+
end
|
202
|
+
|
203
|
+
def scroll_list_by_pixels(list_xpath, pixels)
|
204
|
+
execute_javascript("$(document.evaluate(\"#{list_xpath.tr('"', "'")}\", document, null, XPathResult.ANY_TYPE, null).iterateNext()).scrollTop(#{pixels})")
|
205
|
+
end
|
206
|
+
|
207
|
+
# Open dropdown selector, like 'Color Selector', which has no element id
|
208
|
+
# @param [String] xpath_name name of dropdown list
|
209
|
+
def open_dropdown_selector(xpath_name, horizontal_shift = 30, vertical_shift = 0)
|
210
|
+
element = get_element(xpath_name)
|
211
|
+
if @browser == :firefox || @browser == :safari
|
212
|
+
set_style_attribute("#{xpath_name}/button", 'display', 'none')
|
213
|
+
set_style_attribute(xpath_name, 'display', 'inline')
|
214
|
+
element.click
|
215
|
+
set_style_attribute("#{xpath_name}/button", 'display', 'inline-block')
|
216
|
+
set_style_attribute(xpath_name, 'display', 'block')
|
217
|
+
else
|
218
|
+
@driver.action.move_to(element, horizontal_shift, vertical_shift).click.perform
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|
222
|
+
def action_on_locator_coordinates(xpath_name, right_by, down_by, action = :click, times = 1)
|
223
|
+
wait_until_element_visible(xpath_name)
|
224
|
+
element = @driver.find_element(:xpath, xpath_name)
|
225
|
+
(0...times).inject(@driver.action.move_to(element, right_by.to_i, down_by.to_i)) { |acc, _elem| acc.send(action) }.perform
|
226
|
+
end
|
227
|
+
|
228
|
+
def move_to_element(element)
|
229
|
+
element = get_element(element) if element.is_a?(String)
|
230
|
+
@driver.action.move_to(element).perform
|
231
|
+
end
|
232
|
+
|
233
|
+
def move_to_element_by_locator(xpath_name)
|
234
|
+
element = get_element(xpath_name)
|
235
|
+
@driver.action.move_to(element).perform
|
236
|
+
OnlyofficeLoggerHelper.log("Moved mouse to element: #{xpath_name}")
|
237
|
+
end
|
238
|
+
|
239
|
+
def mouse_over(xpath_name, x_coordinate = 0, y_coordinate = 0)
|
240
|
+
wait_until_element_present(xpath_name)
|
241
|
+
@driver.action.move_to(@driver.find_element(:xpath, xpath_name), x_coordinate.to_i, y_coordinate.to_i).perform
|
242
|
+
end
|
243
|
+
|
244
|
+
def element_present?(xpath_name)
|
245
|
+
case xpath_name
|
246
|
+
when PageObject::Elements::Element
|
247
|
+
xpath_name.present?
|
248
|
+
when Selenium::WebDriver::Element
|
249
|
+
xpath_name.displayed?
|
250
|
+
else
|
251
|
+
@driver.find_element(:xpath, xpath_name)
|
252
|
+
true
|
253
|
+
end
|
254
|
+
rescue Exception
|
255
|
+
false
|
256
|
+
end
|
257
|
+
|
258
|
+
def get_element_by_display(xpath_name)
|
259
|
+
@driver.find_elements(:xpath, xpath_name).each do |element|
|
260
|
+
return element if element.displayed?
|
261
|
+
end
|
262
|
+
rescue Selenium::WebDriver::Error::InvalidSelectorError
|
263
|
+
webdriver_error("get_element_by_display(#{xpath_name}): invalid selector: Unable to locate an element with the xpath expression")
|
264
|
+
end
|
265
|
+
|
266
|
+
# Return count of elements (visible and not visible)
|
267
|
+
# @param xpath_name [String] xpath to find
|
268
|
+
# @param only_visible [True, False] count only visible elements?
|
269
|
+
# @return [Integer] element count
|
270
|
+
def get_element_count(xpath_name, only_visible = true)
|
271
|
+
if element_present?(xpath_name)
|
272
|
+
elements = @driver.find_elements(:xpath, xpath_name)
|
273
|
+
only_visible ? elements.delete_if { |element| !element.displayed? }.length : elements.length
|
274
|
+
else
|
275
|
+
0
|
276
|
+
end
|
277
|
+
end
|
278
|
+
|
279
|
+
def get_elements(objects_identification, only_visible = true)
|
280
|
+
return objects_identification if objects_identification.is_a?(Array)
|
281
|
+
|
282
|
+
elements = @driver.find_elements(:xpath, objects_identification)
|
283
|
+
if only_visible
|
284
|
+
elements.each do |current|
|
285
|
+
elements.delete(current) unless @browser == :firefox || current.displayed?
|
286
|
+
end
|
287
|
+
end
|
288
|
+
elements
|
289
|
+
end
|
290
|
+
|
291
|
+
def element_visible?(xpath_name)
|
292
|
+
if xpath_name.is_a?(PageObject::Elements::Element) # PageObject always visible
|
293
|
+
true
|
294
|
+
elsif element_present?(xpath_name)
|
295
|
+
element = get_element(xpath_name)
|
296
|
+
return false if element.nil?
|
297
|
+
|
298
|
+
begin
|
299
|
+
visible = element.displayed?
|
300
|
+
rescue Exception
|
301
|
+
visible = false
|
302
|
+
end
|
303
|
+
visible
|
304
|
+
else
|
305
|
+
false
|
306
|
+
end
|
307
|
+
end
|
308
|
+
|
309
|
+
# Check if any element of xpath is displayed
|
310
|
+
# @param xpath_several_elements [String] xpath to check
|
311
|
+
# @return [True, False] result of visibility
|
312
|
+
def one_of_several_elements_displayed?(xpath_several_elements)
|
313
|
+
@driver.find_elements(:xpath, xpath_several_elements).each do |current_element|
|
314
|
+
return true if current_element.displayed?
|
315
|
+
end
|
316
|
+
false
|
317
|
+
rescue Exception => e
|
318
|
+
webdriver_error("Raise unkwnown exception: #{e}")
|
319
|
+
end
|
320
|
+
|
321
|
+
def wait_element(xpath_name, period_of_wait = 1, critical_time = 3)
|
322
|
+
wait_until_element_present(xpath_name)
|
323
|
+
time = 0
|
324
|
+
until element_visible?(xpath_name)
|
325
|
+
sleep(period_of_wait)
|
326
|
+
time += 1
|
327
|
+
return if time == critical_time
|
328
|
+
end
|
329
|
+
end
|
330
|
+
|
331
|
+
# Select frame as current
|
332
|
+
# @param [String] xpath_name name of current xpath
|
333
|
+
def select_frame(xpath_name = '//iframe', count_of_frames = 1)
|
334
|
+
(0...count_of_frames).each do
|
335
|
+
frame = @driver.find_element(:xpath, xpath_name)
|
336
|
+
@driver.switch_to.frame frame
|
337
|
+
rescue Selenium::WebDriver::Error::NoSuchElementError
|
338
|
+
OnlyofficeLoggerHelper.log('Raise NoSuchElementError in the select_frame method')
|
339
|
+
rescue Exception => e
|
340
|
+
webdriver_error("Raise unkwnown exception: #{e}")
|
341
|
+
end
|
342
|
+
end
|
343
|
+
|
344
|
+
# Select top frame of browser (even if several subframes exists)
|
345
|
+
def select_top_frame
|
346
|
+
@driver.switch_to.default_content
|
347
|
+
rescue Timeout::Error
|
348
|
+
OnlyofficeLoggerHelper.log('Raise TimeoutError in the select_top_frame method')
|
349
|
+
rescue Exception => e
|
350
|
+
raise "Browser is crushed or hangup with error: #{e}"
|
351
|
+
end
|
352
|
+
|
353
|
+
# Get text of current element
|
354
|
+
# @param [String] xpath_name name of xpath
|
355
|
+
# @param [true, false] wait_until_visible wait until element visible [@default = true]
|
356
|
+
def get_text(xpath_name, wait_until_visible = true)
|
357
|
+
wait_until_element_visible(xpath_name) if wait_until_visible
|
358
|
+
|
359
|
+
element = get_element(xpath_name)
|
360
|
+
webdriver_error("get_text(#{xpath_name}, #{wait_until_visible}) not found element by xpath") if element.nil?
|
361
|
+
if element.tag_name == 'input' || element.tag_name == 'textarea'
|
362
|
+
element.attribute('value')
|
363
|
+
else
|
364
|
+
element.text
|
365
|
+
end
|
366
|
+
end
|
367
|
+
|
368
|
+
def get_text_of_several_elements(xpath_several_elements)
|
369
|
+
@driver.find_elements(:xpath, xpath_several_elements).map { |element| element.text unless element.text == '' }.compact
|
370
|
+
end
|
371
|
+
|
372
|
+
def select_combo_box(xpath_name, select_value, select_by = :value)
|
373
|
+
wait_until_element_visible(xpath_name)
|
374
|
+
option = Selenium::WebDriver::Support::Select.new(get_element(xpath_name))
|
375
|
+
begin
|
376
|
+
option.select_by(select_by, select_value)
|
377
|
+
rescue StandardError
|
378
|
+
option.select_by(:text, select_value)
|
379
|
+
end
|
380
|
+
end
|
381
|
+
|
382
|
+
# Get page source
|
383
|
+
# @return [String] all page source
|
384
|
+
def get_page_source
|
385
|
+
@driver.execute_script('return document.documentElement.innerHTML;')
|
386
|
+
end
|
387
|
+
|
388
|
+
def webdriver_error(exception, error_message = nil)
|
389
|
+
if exception.is_a?(String) # If there is no error_message
|
390
|
+
error_message = exception
|
391
|
+
exception = RuntimeError
|
392
|
+
end
|
393
|
+
select_top_frame
|
394
|
+
current_url = get_url
|
395
|
+
raise exception, "#{error_message}\n\nPage address: #{current_url}\n\nError #{webdriver_screenshot}"
|
396
|
+
end
|
397
|
+
|
398
|
+
def wait_file_for_download(file_name, timeout = TIMEOUT_FILE_DOWNLOAD)
|
399
|
+
full_file_name = "#{@download_directory}/#{file_name}"
|
400
|
+
full_file_name = file_name if file_name[0] == '/'
|
401
|
+
counter = 0
|
402
|
+
while !File.exist?(full_file_name) && counter < timeout
|
403
|
+
OnlyofficeLoggerHelper.log("Waiting for download file #{full_file_name} for #{counter} of #{timeout}")
|
404
|
+
sleep 1
|
405
|
+
counter += 1
|
406
|
+
end
|
407
|
+
webdriver_error("File #{full_file_name} not download for #{timeout} seconds") if counter >= timeout
|
408
|
+
full_file_name
|
409
|
+
end
|
410
|
+
|
411
|
+
# Perform cleanup if something went wrong during tests
|
412
|
+
# @param forced [True, False] should cleanup run in all cases
|
413
|
+
def self.clean_up(forced = false)
|
414
|
+
return unless OnlyofficeFileHelper::LinuxHelper.user_name.include?('nct-at') ||
|
415
|
+
OnlyofficeFileHelper::LinuxHelper.user_name.include?('ubuntu') ||
|
416
|
+
forced == true
|
417
|
+
|
418
|
+
OnlyofficeFileHelper::LinuxHelper.kill_all('chromedriver')
|
419
|
+
OnlyofficeFileHelper::LinuxHelper.kill_all('geckodriver')
|
420
|
+
OnlyofficeFileHelper::LinuxHelper.kill_all('Xvfb')
|
421
|
+
OnlyofficeFileHelper::LinuxHelper.kill_all_browsers
|
422
|
+
end
|
423
|
+
end
|
424
|
+
end
|