rudra 1.0.15 → 1.1.2
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 +4 -4
- data/lib/rudra.rb +148 -33
- metadata +22 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f6485433a80329770ea6ce43647f1d10176c20fa1eefa825b51e222615e0b4fe
|
4
|
+
data.tar.gz: 980b66578597240fab1f2b0c68ee9fcc68be47b7466a8b5d99decf3dec6787c4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f12e6ea46bb610708c6fa9cf13cd897bbd2e19ea645d7f5e748b1daccac7efdf00470b8333f16907401c9e63968f975f2c526188513862197db8771669e7c785
|
7
|
+
data.tar.gz: 7584799d278830b5912e6eb57e6582c3b2b7bb93cafff6c2bbc331137b06181674f5152e3cf179696ff3d1c6b66d0a91546fbd5800e2329662e5bb95357a8f40
|
data/lib/rudra.rb
CHANGED
@@ -1,10 +1,5 @@
|
|
1
1
|
require 'selenium-webdriver'
|
2
|
-
require 'webdrivers
|
3
|
-
require 'webdrivers/geckodriver'
|
4
|
-
require 'webdrivers/iedriver'
|
5
|
-
|
6
|
-
# Selenium::WebDriver::Chrome::Service.driver_path = './webdrivers/chromedriver'
|
7
|
-
# Selenium::WebDriver::Firefox::Service.driver_path = './webdrivers/geckodriver'
|
2
|
+
require 'webdrivers'
|
8
3
|
|
9
4
|
# Selenium IDE-like WebDriver based upon Ruby binding
|
10
5
|
# @author Aaron Chen
|
@@ -13,8 +8,13 @@ require 'webdrivers/iedriver'
|
|
13
8
|
# of the chosen browser
|
14
9
|
# @attr_reader [String] install_dir The install directory of WebDrivers
|
15
10
|
# @attr_reader [String] locale The browser locale
|
11
|
+
# @attr_reader [Boolean] headless Headless mode for Google Chrome
|
12
|
+
# @attr_reader [String] window_size Chrome window size when headless
|
13
|
+
# @attr_reader [String] screen_dir The screenshot directory of save_screenshot
|
14
|
+
# @attr_reader [String] log_prefix Prefix for logging descriptions and methods
|
16
15
|
# @attr_reader [Integer] timeout The driver timeout
|
17
|
-
# @attr_reader [Boolean] verbose Verbose mode
|
16
|
+
# @attr_reader [Boolean] verbose Turn on/off Verbose mode
|
17
|
+
# @attr_reader [Boolean] silent Turn off Turn on/off descriptions
|
18
18
|
class Rudra
|
19
19
|
# Supported Browsers
|
20
20
|
BROWSERS = %i[chrome firefox ie safari].freeze
|
@@ -28,12 +28,13 @@ class Rudra
|
|
28
28
|
# Attributes
|
29
29
|
ATTRIBUTES = %i[
|
30
30
|
browser driver install_dir locale
|
31
|
-
headless
|
31
|
+
headless window_size screen_dir
|
32
|
+
log_prefix timeout verbose silent
|
32
33
|
].freeze
|
33
34
|
|
34
35
|
attr_reader :browser, :driver, :install_dir, :locale,
|
35
|
-
:headless, :
|
36
|
-
:timeout, :verbose
|
36
|
+
:headless, :window_size, :screen_dir,
|
37
|
+
:log_prefix, :timeout, :verbose, :silent
|
37
38
|
|
38
39
|
# Initialize an instance of Rudra
|
39
40
|
# @param [Hash] options the options to initialize Rudra
|
@@ -43,18 +44,22 @@ class Rudra
|
|
43
44
|
# directory of WebDrivers
|
44
45
|
# @option options [Symbol] :locale (:en) the browser locale
|
45
46
|
# @option options [Boolean] :headless (false) headless mode
|
46
|
-
# @option options [String] :
|
47
|
-
# @option options [String] :
|
47
|
+
# @option options [String] :window_size ('1280,720') window size when headless
|
48
|
+
# @option options [String] :screen_dir ('./screens/') the location of screenshots
|
49
|
+
# @option options [String] :log_prefix (' - ') prefix for logging descriptions and methods
|
48
50
|
# @option options [Integer] :timeout (30) implicit_wait timeout
|
49
|
-
# @option options [Boolean] :verbose (
|
51
|
+
# @option options [Boolean] :verbose (false) Turn on/off verbose mode
|
52
|
+
# @option options [Boolean] :silent (false) Turn on/off descriptions
|
50
53
|
def initialize(options = {})
|
51
54
|
self.browser = options.fetch(:browser, :chrome)
|
52
55
|
self.install_dir = options.fetch(:install_dir, './webdrivers/')
|
53
|
-
self.locale = options.fetch(:locale, :
|
56
|
+
self.locale = options.fetch(:locale, :en)
|
54
57
|
self.headless = options.fetch(:headless, false)
|
58
|
+
self.window_size = options.fetch(:window_size, '1280,720')
|
55
59
|
self.screen_dir = options.fetch(:screen_dir, './screens/')
|
56
60
|
self.log_prefix = options.fetch(:log_prefix, ' - ')
|
57
|
-
self.verbose = options.fetch(:verbose,
|
61
|
+
self.verbose = options.fetch(:verbose, false)
|
62
|
+
self.silent = options.fetch(:silent, false)
|
58
63
|
self.main_label = caller_locations(2, 1).first.label
|
59
64
|
|
60
65
|
initialize_driver
|
@@ -100,6 +105,7 @@ class Rudra
|
|
100
105
|
end
|
101
106
|
|
102
107
|
# Send keys to an alert
|
108
|
+
# @param [String] keys keystrokes to send
|
103
109
|
def alert_send_keys(keys)
|
104
110
|
switch_to_alert.send_keys(keys)
|
105
111
|
end
|
@@ -171,7 +177,7 @@ class Rudra
|
|
171
177
|
|
172
178
|
element ||= driver.find_element(how, what)
|
173
179
|
|
174
|
-
|
180
|
+
raise Selenium::WebDriver::Error::NoSuchElementError, "Failed to find element: #{locator}" unless element
|
175
181
|
|
176
182
|
wait_for { element.displayed? }
|
177
183
|
|
@@ -183,8 +189,11 @@ class Rudra
|
|
183
189
|
# @return [Array<Selenium::WebDriver::Element>] the elements found
|
184
190
|
def find_elements(locator)
|
185
191
|
how, what = parse_locator(locator)
|
186
|
-
driver.find_elements(how, what)
|
187
|
-
|
192
|
+
elements = driver.find_elements(how, what)
|
193
|
+
|
194
|
+
raise Selenium::WebDriver::Error::NoSuchElementError, "Failed to find elements: #{locator}" if elements.empty?
|
195
|
+
|
196
|
+
elements
|
188
197
|
end
|
189
198
|
|
190
199
|
# Move forward a single entry in the browser's history
|
@@ -204,7 +213,7 @@ class Rudra
|
|
204
213
|
|
205
214
|
# Maximize the current window
|
206
215
|
def maximize
|
207
|
-
driver.manage.window.maximize
|
216
|
+
driver.manage.window.maximize unless headless
|
208
217
|
end
|
209
218
|
|
210
219
|
# Maximize the current window to the size of the screen
|
@@ -262,6 +271,12 @@ class Rudra
|
|
262
271
|
driver.page_source
|
263
272
|
end
|
264
273
|
|
274
|
+
# Print description in the console
|
275
|
+
# @param [String] description description to show
|
276
|
+
def puts(description)
|
277
|
+
$stdout.puts "#{log_prefix}#{description.chomp}" unless silent
|
278
|
+
end
|
279
|
+
|
265
280
|
# Refresh the current pagef
|
266
281
|
def refresh
|
267
282
|
driver.navigate.refresh
|
@@ -277,13 +292,16 @@ class Rudra
|
|
277
292
|
# Save a PNG screenshot to file
|
278
293
|
# @param [String] filename the filename of PNG screenshot
|
279
294
|
def save_screenshot(filename)
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
@screen_dir,
|
284
|
-
filename.end_with?('.png') ? filename : "#{filename}.png"
|
285
|
-
)
|
295
|
+
file = File.join(
|
296
|
+
@screen_dir,
|
297
|
+
sanitize(filename.end_with?('.png') ? filename : "#{filename}.png")
|
286
298
|
)
|
299
|
+
|
300
|
+
dir = File.dirname(file)
|
301
|
+
|
302
|
+
mkdir(dir) unless Dir.exist?(dir)
|
303
|
+
|
304
|
+
driver.save_screenshot(file)
|
287
305
|
end
|
288
306
|
|
289
307
|
# Switch to the currently active modal dialog
|
@@ -298,6 +316,7 @@ class Rudra
|
|
298
316
|
end
|
299
317
|
|
300
318
|
# Switch to the frame with the given id
|
319
|
+
# @param [String] id the frame id
|
301
320
|
def switch_to_frame(id)
|
302
321
|
driver.switch_to.frame(id)
|
303
322
|
end
|
@@ -333,6 +352,46 @@ class Rudra
|
|
333
352
|
wait_for { find_element(locator).enabled? }
|
334
353
|
end
|
335
354
|
|
355
|
+
# Wait until the element, identified by locator, is found in frame
|
356
|
+
# @param [String] frame_id the frame id
|
357
|
+
# @param [String] locator the locator to identify the element
|
358
|
+
def wait_for_element_found_in_frame(frame_id, locator)
|
359
|
+
switch_to_frame frame_id
|
360
|
+
|
361
|
+
how, what = parse_locator(locator)
|
362
|
+
|
363
|
+
wait_for do
|
364
|
+
begin
|
365
|
+
driver.find_element(how, what)
|
366
|
+
rescue Selenium::WebDriver::Error::NoSuchWindowError
|
367
|
+
false
|
368
|
+
end
|
369
|
+
end
|
370
|
+
end
|
371
|
+
|
372
|
+
# Wait (in seconds) until the element is not displayed
|
373
|
+
# @param [String, Selenium::WebDriver::Element] locator the locator to
|
374
|
+
# identify the element or Selenium::WebDriver::Element
|
375
|
+
# @param [Integer] seconds seconds before timed out
|
376
|
+
def wait_for_not_visible(locator, seconds = 3)
|
377
|
+
how, what = parse_locator(locator)
|
378
|
+
|
379
|
+
implicit_wait(seconds)
|
380
|
+
|
381
|
+
begin
|
382
|
+
wait_for(seconds) do
|
383
|
+
elements = driver.find_elements(how, what)
|
384
|
+
elements.empty? || elements.map(&:displayed?).none?
|
385
|
+
end
|
386
|
+
rescue Selenium::WebDriver::Error::TimeoutError
|
387
|
+
true
|
388
|
+
rescue Net::ReadTimeout
|
389
|
+
true
|
390
|
+
ensure
|
391
|
+
implicit_wait(timeout)
|
392
|
+
end
|
393
|
+
end
|
394
|
+
|
336
395
|
# Wait until the title of the page including the given string
|
337
396
|
# @param [String] string the string to compare
|
338
397
|
def wait_for_title(string)
|
@@ -381,7 +440,7 @@ class Rudra
|
|
381
440
|
# @param [String] attribute the name of the attribute
|
382
441
|
# @return [String, nil] attribute value
|
383
442
|
def attribute(locator, attribute)
|
384
|
-
find_element(locator).
|
443
|
+
find_element(locator).attribute(attribute)
|
385
444
|
end
|
386
445
|
|
387
446
|
# If the element, identified by locator, has the given attribute
|
@@ -416,7 +475,13 @@ class Rudra
|
|
416
475
|
# @param [String, Selenium::WebDriver::Element] locator the locator to
|
417
476
|
# identify the element or Selenium::WebDriver::Element
|
418
477
|
def click(locator)
|
419
|
-
|
478
|
+
wait_for do
|
479
|
+
begin
|
480
|
+
find_element(locator).click.nil?
|
481
|
+
rescue Selenium::WebDriver::Error::ElementClickInterceptedError
|
482
|
+
false
|
483
|
+
end
|
484
|
+
end
|
420
485
|
end
|
421
486
|
|
422
487
|
# Click the given element, identified by locator, with an offset
|
@@ -729,6 +794,38 @@ class Rudra
|
|
729
794
|
), find_element(locator), event)
|
730
795
|
end
|
731
796
|
|
797
|
+
# Wait until the element, identified by locator, attribute has value
|
798
|
+
# @param [String, Selenium::WebDriver::Element] locator the locator to identify the element
|
799
|
+
# @param [String] attribute the name of the attribute
|
800
|
+
# @param [String] value the value of the attribute
|
801
|
+
def wait_for_attribute_to_include(locator, attribute, value)
|
802
|
+
how, what = parse_locator(locator)
|
803
|
+
|
804
|
+
wait_for do
|
805
|
+
begin
|
806
|
+
driver.find_element(how, what)&.attribute(attribute)&.downcase&.include?(value.downcase)
|
807
|
+
rescue Selenium::WebDriver::Error::StaleElementReferenceError
|
808
|
+
false
|
809
|
+
end
|
810
|
+
end
|
811
|
+
end
|
812
|
+
|
813
|
+
# Wait until the element, identified by locator, excluding string in text
|
814
|
+
# @param [String, Selenium::WebDriver::Element] locator the locator to
|
815
|
+
# identify the element or Selenium::WebDriver::Element
|
816
|
+
# @param [String] string the string to exclude
|
817
|
+
def wait_for_text_to_exclude(locator, string)
|
818
|
+
wait_for { text(locator).exclude?(string) }
|
819
|
+
end
|
820
|
+
|
821
|
+
# Wait until the element, identified by locator, including string in text
|
822
|
+
# @param [String, Selenium::WebDriver::Element] locator the locator to
|
823
|
+
# identify the element or Selenium::WebDriver::Element
|
824
|
+
# @param [String] string the string to compare
|
825
|
+
def wait_for_text_to_include(locator, string)
|
826
|
+
wait_for { text(locator).include?(string) }
|
827
|
+
end
|
828
|
+
|
732
829
|
#
|
733
830
|
# Tool Functions
|
734
831
|
#
|
@@ -1066,7 +1163,7 @@ class Rudra
|
|
1066
1163
|
end
|
1067
1164
|
|
1068
1165
|
(instance_methods - superclass.instance_methods).map do |method_name|
|
1069
|
-
next if private_method_defined?(method_name) || ATTRIBUTES.include?(method_name)
|
1166
|
+
next if private_method_defined?(method_name) || ATTRIBUTES.include?(method_name) || method_name == :puts
|
1070
1167
|
|
1071
1168
|
original_method = instance_method(method_name)
|
1072
1169
|
|
@@ -1078,7 +1175,8 @@ class Rudra
|
|
1078
1175
|
|
1079
1176
|
private
|
1080
1177
|
|
1081
|
-
|
1178
|
+
attr_accessor :main_label
|
1179
|
+
attr_writer :silent, :window_size
|
1082
1180
|
|
1083
1181
|
def browser=(brw)
|
1084
1182
|
unless BROWSERS.include?(brw)
|
@@ -1131,6 +1229,8 @@ class Rudra
|
|
1131
1229
|
def initialize_driver
|
1132
1230
|
@driver = if browser == :chrome
|
1133
1231
|
Selenium::WebDriver.for(:chrome, options: chrome_options)
|
1232
|
+
# elsif browser == :edge
|
1233
|
+
# Selenium::WebDriver.for(:edge, options: edge_options)
|
1134
1234
|
elsif browser == :firefox
|
1135
1235
|
Selenium::WebDriver.for(:firefox, options: firefox_options)
|
1136
1236
|
elsif browser == :ie
|
@@ -1143,7 +1243,10 @@ class Rudra
|
|
1143
1243
|
def chrome_options
|
1144
1244
|
options = Selenium::WebDriver::Chrome::Options.new
|
1145
1245
|
options.add_argument('--disable-notifications')
|
1146
|
-
|
1246
|
+
if headless
|
1247
|
+
options.add_argument('--headless')
|
1248
|
+
options.add_argument("--window-size=#{window_size}")
|
1249
|
+
end
|
1147
1250
|
options.add_option(
|
1148
1251
|
'excludeSwitches',
|
1149
1252
|
%w[enable-automation enable-logging]
|
@@ -1152,6 +1255,10 @@ class Rudra
|
|
1152
1255
|
options
|
1153
1256
|
end
|
1154
1257
|
|
1258
|
+
# def edge_options
|
1259
|
+
# Selenium::WebDriver::Edge::Options.new
|
1260
|
+
# end
|
1261
|
+
|
1155
1262
|
def firefox_options
|
1156
1263
|
options = Selenium::WebDriver::Firefox::Options.new
|
1157
1264
|
options.add_preference('intl.accept_languages', locale)
|
@@ -1184,21 +1291,29 @@ class Rudra
|
|
1184
1291
|
how.to_sym
|
1185
1292
|
end
|
1186
1293
|
|
1187
|
-
|
1294
|
+
raise Selenium::WebDriver::Error::InvalidSelectorError, "Cannot parse locator: #{locator}" unless HOWS.include?(how)
|
1188
1295
|
|
1189
1296
|
[how, what]
|
1190
1297
|
end
|
1191
1298
|
|
1192
1299
|
def log(method_name, *args)
|
1193
|
-
return unless
|
1300
|
+
return unless verbose && caller_locations(2, 1).first.label == main_label
|
1194
1301
|
|
1195
1302
|
arguments = args.map(&:to_s).join(', ')
|
1196
1303
|
|
1197
|
-
puts
|
1304
|
+
puts log_prefix + (
|
1198
1305
|
arguments.empty? ? method_name.to_s : "#{method_name}(#{arguments})"
|
1199
1306
|
)
|
1200
1307
|
end
|
1201
1308
|
|
1309
|
+
def sanitize(filename)
|
1310
|
+
invalid_characters = ['/', '\\', '?', '%', '*', ':', '|', '"', '<', '>']
|
1311
|
+
invalid_characters.each do |character|
|
1312
|
+
filename.gsub!(character, '')
|
1313
|
+
end
|
1314
|
+
filename
|
1315
|
+
end
|
1316
|
+
|
1202
1317
|
def random_id(length = 8)
|
1203
1318
|
charset = [(0..9), ('a'..'z')].flat_map(&:to_a)
|
1204
1319
|
id = Array.new(length) { charset.sample }.join
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rudra
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.1.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Aaron Chen
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-06-
|
11
|
+
date: 2020-06-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: yard
|
@@ -24,6 +24,26 @@ dependencies:
|
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: 0.9.25
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rspec
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 3.9.0
|
34
|
+
- - "~>"
|
35
|
+
- !ruby/object:Gem::Version
|
36
|
+
version: '3.9'
|
37
|
+
type: :runtime
|
38
|
+
prerelease: false
|
39
|
+
version_requirements: !ruby/object:Gem::Requirement
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: 3.9.0
|
44
|
+
- - "~>"
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '3.9'
|
27
47
|
- !ruby/object:Gem::Dependency
|
28
48
|
name: selenium-webdriver
|
29
49
|
requirement: !ruby/object:Gem::Requirement
|