web4cucumber 0.0.8 → 0.0.9

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 665d2600c206c0133279ee8f6d5a5e3b94283f34
4
- data.tar.gz: 8ffc25cafcedfdbaa5b14fdd8c8fb7119550aac0
3
+ metadata.gz: ab9093d7fc709c2f8eead84d33b4f9d9f71d883a
4
+ data.tar.gz: 9b5fa23a4b76251c413bc8694ed2bcca5d5c6bd4
5
5
  SHA512:
6
- metadata.gz: b9d51ac82df67ae8e13d4ec9822728b8abbb304aba5f9dad097bc66870cc4dde1e308e2d8b9810e691bb4af0eea6fd905eb40241ac437d40c973f1c0f7431f30
7
- data.tar.gz: ebb7890e5377b5d93153036d092c9f6aec4049cbd2efaa55213cde89eb51a245f4007c77aa51fc4e17aed092faa4b5fe53d8038b32d66642e34f723edc8e3ae4
6
+ metadata.gz: e2b5d5d15d73b6257cded9dfccbbea9eabc55c6643d0e50620d6fefd6b0addceb8d2e0c80555e3f94b0df7be08de0c052c61dd904b36e939feaa735b79a32293
7
+ data.tar.gz: 3b8b76ae963965559691b8cb5aff1ab5b95fbc2a40e777f96d209e4e611de688cddb49f3c976e1df72d9cf695459c71d63bf5bd6724d61a2a60d12acbfcffff9
data/README.md CHANGED
@@ -144,7 +144,7 @@ second_page:
144
144
  selector:
145
145
  text: 'Click me too'
146
146
  ```
147
- If you take a closer look at this yaml structure, you'll notice that it describes describes two web pages pages. The first one is accessed through relative url "/some_relative_path/testme" that is being appended to your project's base_url (used during Web initialization).
147
+ If you take a closer look at this yaml structure, you'll notice that it describes two web pages. The first one is accessed through relative url "/some_relative_path/testme" that is being appended to your project's base_url (used during Web initialization).
148
148
  The second page is accessed by clicking the "Click me" button on the first page. The yaml file describes 2 textfields on first page and one textfield and one filefield on the second page. It also describes checkpoints - elements whose presence will be asserted during the action execution. We can provide default values for textfields right inside the yaml with the *def_value* keyword. Now the most interesting question: how do we use that?
149
149
 
150
150
  - The whole workflow would look like this. You would create your step:
@@ -307,3 +307,14 @@ Different elemet types get treated differently by webdriver, textfields can not
307
307
  ```
308
308
  in the options and yo will be dropped into a shell right after page three is
309
309
  loaded in the browser window.
310
+
311
+ 4. Sometimes, a commit button is situated far below under the long multi-field
312
+ form, outside the browser viewport and therefore can not be interacted with.
313
+ Use keyword *scroll* for such commits:
314
+ ```
315
+ :commit:
316
+ :scroll: true
317
+ :type: a
318
+ :selector:
319
+ :text: "Submit"
320
+ ```
@@ -7,6 +7,7 @@ GEM
7
7
  debugger-linecache (~> 1.2)
8
8
  childprocess (0.5.5)
9
9
  ffi (~> 1.0, >= 1.0.11)
10
+ coderay (1.1.0)
10
11
  columnize (0.9.0)
11
12
  cucumber (1.3.19)
12
13
  builder (>= 2.1.2)
@@ -20,8 +21,14 @@ GEM
20
21
  gherkin (2.12.2)
21
22
  multi_json (~> 1.3)
22
23
  headless (1.0.2)
24
+ json (1.8.2)
25
+ method_source (0.8.2)
23
26
  multi_json (1.10.1)
24
27
  multi_test (0.1.2)
28
+ pry (0.9.12.6)
29
+ coderay (~> 1.0)
30
+ method_source (~> 0.8)
31
+ slop (~> 3.4)
25
32
  rspec-expectations (2.14.4)
26
33
  diff-lcs (>= 1.1.3, < 2.0)
27
34
  rubyzip (1.1.7)
@@ -30,6 +37,7 @@ GEM
30
37
  multi_json (~> 1.0)
31
38
  rubyzip (~> 1.0)
32
39
  websocket (~> 1.0)
40
+ slop (3.5.0)
33
41
  watir-webdriver (0.6.11)
34
42
  selenium-webdriver (>= 2.18.0)
35
43
  web4cucumber (0.0.2)
@@ -42,6 +50,11 @@ PLATFORMS
42
50
  ruby
43
51
 
44
52
  DEPENDENCIES
53
+ byebug
45
54
  cucumber
55
+ headless
56
+ json
57
+ pry
46
58
  rspec-expectations
59
+ watir-webdriver
47
60
  web4cucumber
@@ -25,6 +25,7 @@ class Web4Cucumber
25
25
  value[:selector].each_pair do |how, what|
26
26
  options.keys.each do |optkey|
27
27
  if what.match Regexp.new("<#{optkey.to_s}>")
28
+ # TODO: make sure this does not alter global page rules
28
29
  what.gsub!("<#{optkey.to_s}>", options[optkey])
29
30
  end
30
31
  end
@@ -71,6 +72,7 @@ class Web4Cucumber
71
72
  value[:selector].each_pair do |how, what|
72
73
  options.keys.each do |optkey|
73
74
  if what.match Regexp.new("<#{optkey.to_s}>")
75
+ # TODO: make sure this does not alter global page rules
74
76
  what.gsub!("<#{optkey.to_s}>", options[optkey])
75
77
  end
76
78
  end
@@ -96,7 +98,6 @@ class Web4Cucumber
96
98
  }
97
99
  end
98
100
 
99
-
100
101
  def check_iframe(page_rules)
101
102
  if page_rules.has_key? :iframe
102
103
  return @@b.iframe(page_rules[:iframe][:selector])
@@ -117,11 +118,74 @@ class Web4Cucumber
117
118
  @counter += 1
118
119
  end
119
120
 
121
+ def find_elements(type, selector)
122
+ type = type.to_sym
123
+
124
+ possible_elements = {
125
+ :select => :select_lists,
126
+ :checkbox => :checkboxes,
127
+ :radio => :radios,
128
+ :text_field => :text_fields,
129
+ :textfield => :text_fields,
130
+ :text_area => :textareas,
131
+ :textarea => :textareas,
132
+ :filefield => :file_fields,
133
+ :file_field => :file_fields,
134
+ :a => :as,
135
+ :button => :button,
136
+ :element => :elements
137
+ }
138
+
139
+ raise "unknown type #{type}" unless possible_elements.keys.include? type
140
+
141
+ # note that this is lazily evaluated so errors may occur later
142
+ res = driver.public_send(possible_elements[type],selector)
143
+
144
+ # we want to always return an array
145
+ if res.nil?
146
+ @@logger.info("found none #{type} elements with selector #{selector}")
147
+ return []
148
+ elsif res.kind_of? Watir::ElementCollection
149
+ @@logger.info("found #{res.size} #{type} elements with selector #{selector}")
150
+ return res.to_a
151
+ else
152
+ @@logger.info("found #{type} element with selector #{selector}")
153
+ return [res]
154
+ end
155
+ end
156
+
157
+ def find_visible_elements(type, selector)
158
+ return find_elements(type, selector).select { |e| e.visible? }
159
+ end
160
+
161
+ # @return [status, [[[elements], type, selector], ..] ]
162
+ def wait_for_elements(opts)
163
+ elements = opts[:list] || [[ opts[:type], opts[:selector] ]]
164
+ only_visible = opts.has_key?(:visible) ? opts[:visible] : true
165
+ timeout = opts[:timeout] || 20 # in seconds
166
+
167
+ start = Time.now
168
+ result = nil
169
+ begin
170
+ sleep 1
171
+ result = {:list => [], :success => true}
172
+ break if elements.all? { |type, selector|
173
+ e = only_visible ? find_visible_elements(type, selector) :
174
+ find_elements(type, selector)
175
+ result[:list] << [e, opts[:type], opts[:selector]] unless e.empty?
176
+ }
177
+ result[:success] = false
178
+ end until Time.now - start > timeout
179
+
180
+ return result[:success], result[:list]
181
+ end
182
+
120
183
  def run_action(key, options)
121
184
  @result = {:result=>true,
122
185
  :failed_positive_checkpoints=>[],
123
- :failed_negative_checkpoints=>[],
186
+ :failed_negative_checkpoints=>[],
124
187
  :errors => []}
188
+ # TODO: can we do a deep freeze here?
125
189
  @@rules.freeze
126
190
  rules = Marshal.load(Marshal.dump(@@rules))
127
191
  action_rules = rules[key.to_sym]
@@ -146,12 +210,14 @@ class Web4Cucumber
146
210
  if page_rules.has_key? :url
147
211
  options.keys.each do |key|
148
212
  if page_rules[:url].match Regexp.new("<#{key.to_s}>")
213
+ # TODO: make sure this does not alter global page rules
149
214
  page_rules[:url].gsub!("<#{key.to_s}>", options[key])
150
215
  end
151
216
  end
152
217
  elsif page_rules.has_key? :base_url
153
218
  options.keys.each do |key|
154
219
  if page_rules[:base_url].match Regexp.new("<#{key.to_s}>")
220
+ # TODO: make sure this does not alter global page rules
155
221
  page_rules[:base_url].gsub!("<#{key.to_s}>", options[key])
156
222
  end
157
223
  end
@@ -170,8 +236,10 @@ class Web4Cucumber
170
236
  end
171
237
  if page_rules[:url].match /^\:\d+/
172
238
  if url.match /\:\d+/
239
+ # TODO: make sure this does not alter global page rules
173
240
  url.gsub!(/\:\d+\//, "")
174
241
  else
242
+ # TODO: make sure this does not alter global page rules
175
243
  url.gsub!(/\/$/, "")
176
244
  end
177
245
  end
@@ -183,10 +251,10 @@ class Web4Cucumber
183
251
  if page_rules.has_key? :sleep
184
252
  sleep page_rules[:sleep]
185
253
  end
186
- driver = check_iframe(page_rules) # substitute browser operating with main html
254
+ set_driver(page_rules) # substitute browser operating with main html
187
255
  # sometimes also it is pretty handy to be able to stick the debugger at
188
- # some point to have a human control over the webdriver instance. Then
189
- # in the same way stick the :debug_at keyword with the page name as a value
256
+ # some point to have a human control over the webdriver instance. Then in
257
+ # the same way stick the :debug_at keyword with the page name as a value
190
258
  # into the options hash. The webdriver is available via @@b clas variable
191
259
  if options.has_key?(:debug_at) and options[:debug_at] == page
192
260
  require "byebug"
@@ -205,29 +273,9 @@ class Web4Cucumber
205
273
  # :selector:
206
274
  # :text: 'Some text <passme>'
207
275
  # Do not forget to pass :passme key with value in options hash
208
- options.keys.each do |optkey|
209
- if prop[:selector].values[0].include? "<#{optkey}>"
210
- prop[:selector].values[0].gsub!("<#{optkey}>", options[optkey])
211
- end
212
- end
213
- possible_elements = {
214
- # There could be more than one element with the same
215
- # properties and only one would be visible. As an example:
216
- # try adding more than one member to the same domain
217
- 'select' => driver.select_lists(prop[:selector]),
218
- 'checkbox' => driver.checkboxes(prop[:selector]),
219
- 'radio' => driver.radios(prop[:selector]),
220
- 'text_field' => driver.text_fields(prop[:selector]),
221
- 'textfield' => driver.text_fields(prop[:selector]),
222
- 'text_area' => driver.textareas(prop[:selector]),
223
- 'textarea' => driver.textareas(prop[:selector]),
224
- 'filefield' => driver.file_fields(prop[:selector]),
225
- 'file_field' => driver.file_fields(prop[:selector]),
226
- 'a' => driver.as(prop[:selector]),
227
- 'element' => driver.elements(prop[:selector])
228
- }
229
- if prop.has_key?(:type) and not possible_elements.keys.include? prop[:type]
230
- @@logger.error("Unsupported element #{prop[:type]} for cucushift, so type error?")
276
+ options.each do |opt_key, opt_value|
277
+ # TODO: this likely alters rules so we need to dup first
278
+ prop[:selector].values[0].gsub!("<#{opt_key}>", opt_value)
231
279
  end
232
280
  result ||= true
233
281
  @counter = 0
@@ -242,20 +290,15 @@ class Web4Cucumber
242
290
  end
243
291
  end
244
292
  options.each do |key,value|
245
- if value.is_a? String and prop[:selector].values[0].match Regexp.new("<#{key.to_s}>")
246
- prop[:selector].values[0].gsub!("<#{key.to_s}>", options[key])
247
- end
248
- end
249
- elements = possible_elements[prop[:type]]
250
- element = nil
251
- elements.each do |elem|
252
- if elem.visible?
253
- element = elem
293
+ if value.is_a? String
294
+ # TODO: make sure this does not alter global page rules
295
+ prop[:selector].values[0].gsub!("<#{key.to_s}>", value)
254
296
  end
255
297
  end
256
298
  if (options.has_key? name and options[name]) or prop.has_key? :def_value
257
299
  options.keys.each do |key|
258
300
  if prop[:selector].values[0].match Regexp.new("<#{key.to_s}>")
301
+ # TODO: make sure this does not alter global page rules
259
302
  prop[:selector].values[0].gsub!("<#{key.to_s}>", options[key])
260
303
  end
261
304
  end
@@ -266,20 +309,22 @@ class Web4Cucumber
266
309
  prop[:selector][key] = options[name.to_sym]
267
310
  end
268
311
  end
269
- if not element
312
+ found, elements = wait_for_elements( :type => prop[:type],
313
+ :selector => prop[:selector],
314
+ :visible => true
315
+ )
316
+ if ! found
270
317
  if not prop[:may_absent] and not page_rules.has_key? :may_absent
318
+ screenshot_save
319
+ @result[:failed_positive_checkpoints] << prop[:selector]
271
320
  @result[:result] = false
272
321
  @result[:errors] << "Unable to find element #{name.to_s} by the following #{prop[:selector].keys[0].to_s}: #{prop[:selector].values[0]}"
322
+ # historically we avoided raising on missing element
323
+ # we may revisit that here
273
324
  end
274
325
  else
275
- until element.exists? do
276
- begin
277
- wait_for_element(prop[:selector], prop[:may_absent])
278
- rescue Exception => e
279
- result = false
280
- break
281
- end
282
- end
326
+ # first element searched for, first field [the actual element list], last found element [this must be most inner element]
327
+ element = elements.first.first.last
283
328
  if result # continue if nothing failed
284
329
  begin
285
330
  if prop[:type] == 'select'
@@ -350,6 +395,7 @@ class Web4Cucumber
350
395
  if page_rules[:commit].has_key?(:selector)
351
396
  options.keys.each do |optkey|
352
397
  if page_rules[:commit][:selector].values[0].include? "<#{optkey}>"
398
+ # TODO: make sure this does not alter global page rules
353
399
  page_rules[:commit][:selector].values[0].gsub!("<#{optkey}>", options[optkey])
354
400
  end
355
401
  end
@@ -482,11 +528,21 @@ class Web4Cucumber
482
528
  @@b.goto @@base_url
483
529
  end
484
530
 
531
+ # @return current driver
532
+ # @note that is usually the browser we have but can be an iframe if set_driver was called
533
+ def driver
534
+ return @current_driver || @@b
535
+ end
536
+
537
+ def set_driver(page_rules)
538
+ @current_driver = check_iframe(page_rules)
539
+ end
540
+
485
541
  def goto(params) # params should be a hash
486
542
  if params[:relative]
487
543
  # if provided relative url and current url contain port nuumbers - take
488
544
  # the explicitly provided one
489
- if params[:url].match /^:\d+/
545
+ if params[:url].match /^:\d+/
490
546
  base_url.gsub!(/\/$/, "")
491
547
  end
492
548
  @@b.goto @@base_url + params[:url]
@@ -499,15 +555,15 @@ class Web4Cucumber
499
555
  def get_url
500
556
  return @@b.url
501
557
  end
502
-
558
+
503
559
  def cookie_option(cookies,opt = nil)
504
560
  @result = {:result => true, :failed_positive_checkpoints => nil,:message => nil}
505
561
  @cookies= @@b.cookies
506
- if opt == 'show'
562
+ if opt == 'show'
507
563
  return @cookies.to_a
508
564
  end
509
565
  if opt == 'select'
510
- new_cookies = []
566
+ new_cookies = []
511
567
  cookies.each do |cookie|
512
568
  if @cookies[cookie[:name].to_sym]
513
569
  cookie[:value] = @cookies[cookie[:name].to_sym][:value]
@@ -517,9 +573,9 @@ class Web4Cucumber
517
573
  @result[:falied_negative_checkpoints] =[cookie]
518
574
  return @result
519
575
  end
520
- end
576
+ end
521
577
  return new_cookies
522
- end
578
+ end
523
579
  if opt == 'delete_all'
524
580
  unless @cookies.clear
525
581
  @result[:result]=false
@@ -528,7 +584,7 @@ class Web4Cucumber
528
584
  end
529
585
  cookies.each do |cookie|
530
586
  if opt == 'add'
531
- if @cookies.add cookie[:name],cookie[:value]
587
+ if @cookies.add cookie[:name],cookie[:value]
532
588
  @result[:result]=true
533
589
  else
534
590
  @result[:result]=false
@@ -541,9 +597,9 @@ class Web4Cucumber
541
597
  @result[:falied_negative_checkpoints] = [cookie]
542
598
  end
543
599
  end
544
- end
545
- return @result
546
- end
600
+ end
601
+ return @result
602
+ end
547
603
 
548
604
  def check_elements(elements, negate=nil, click=nil)
549
605
  # elements should be an array of hashes, for example:
@@ -1,3 +1,3 @@
1
1
  module Web4Cucumber
2
- VERSION = "0.0.8"
2
+ VERSION = "0.0.9"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: web4cucumber
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.8
4
+ version: 0.0.9
5
5
  platform: ruby
6
6
  authors:
7
7
  - Oleg Fayans