web4cucumber 0.0.8 → 0.0.9

Sign up to get free protection for your applications and to get access to all the features.
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