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 +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