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.
Files changed (3) hide show
  1. checksums.yaml +4 -4
  2. data/lib/rudra.rb +148 -33
  3. metadata +22 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 18f80bbed760fbf34d622b60e647507bab5ddfea167a6d144430dae84ed35a98
4
- data.tar.gz: '04965c6ea61ea288861f22c6fabbb61240f384faf99b9a9402e51273d201caa4'
3
+ metadata.gz: f6485433a80329770ea6ce43647f1d10176c20fa1eefa825b51e222615e0b4fe
4
+ data.tar.gz: 980b66578597240fab1f2b0c68ee9fcc68be47b7466a8b5d99decf3dec6787c4
5
5
  SHA512:
6
- metadata.gz: 55a6791c2d89ecb33b3833ac41ef6f1514b482ac13525070aa246166bc5d530c7dc23bf09d353630f4205ab5959dc8202a49cee271e96554e485e19a5ca24f6e
7
- data.tar.gz: 1e9d5d7c2fd2cfa29aa3baa80145647e3088096a3af625f5e49d7baf931d2a72082d73abfeb7e12e44995a8d29607d08693e0533bbffdb5a34995e1182b7ee44
6
+ metadata.gz: f12e6ea46bb610708c6fa9cf13cd897bbd2e19ea645d7f5e748b1daccac7efdf00470b8333f16907401c9e63968f975f2c526188513862197db8771669e7c785
7
+ data.tar.gz: 7584799d278830b5912e6eb57e6582c3b2b7bb93cafff6c2bbc331137b06181674f5152e3cf179696ff3d1c6b66d0a91546fbd5800e2329662e5bb95357a8f40
@@ -1,10 +1,5 @@
1
1
  require 'selenium-webdriver'
2
- require 'webdrivers/chromedriver'
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 log_prefix timeout verbose
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, :screen_dir, :log_prefix,
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] :screen_dir ('.//') the location of screenshots
47
- # @option options [String] :log_prefix (' - ') log prefix
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 (true) verbose mode
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, :enscreens)
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, true)
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
- abort("Failed to find element: #{locator}") unless element
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
- abort("Failed to find elements: #{locator}")
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
- mkdir(@screen_dir) unless Dir.exist?(@screen_dir)
281
- driver.save_screenshot(
282
- File.join(
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).property(attribute)
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
- find_element(locator).click
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
- attr_writer :main_label
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
- options.add_argument('--headless') if headless
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
- abort("Cannot parse locator: #{locator}") unless HOWS.include?(how)
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 @verbose && caller_locations(2, 1).first.label == @main_label
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 @log_prefix + (
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.0.15
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-02 00:00:00.000000000 Z
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