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 +4 -4
- data/README.md +12 -1
- data/examples/testproject/Gemfile.lock +13 -0
- data/lib/web4cucumber.rb +112 -56
- data/lib/web4cucumber/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ab9093d7fc709c2f8eead84d33b4f9d9f71d883a
|
4
|
+
data.tar.gz: 9b5fa23a4b76251c413bc8694ed2bcca5d5c6bd4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
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
|
data/lib/web4cucumber.rb
CHANGED
@@ -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
|
-
|
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
|
-
#
|
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.
|
209
|
-
|
210
|
-
|
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
|
246
|
-
|
247
|
-
|
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
|
-
|
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
|
-
|
276
|
-
|
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:
|
data/lib/web4cucumber/version.rb
CHANGED