pickles 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +9 -0
- data/.rspec +2 -0
- data/.travis.yml +5 -0
- data/Gemfile +10 -0
- data/LICENSE.txt +21 -0
- data/README.md +196 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/lib/cucumber/pickles.rb +6 -0
- data/lib/cucumber/pickles/check_in.rb +10 -0
- data/lib/cucumber/pickles/config.rb +61 -0
- data/lib/cucumber/pickles/errors/ambigious.rb +27 -0
- data/lib/cucumber/pickles/errors/node_find_error.rb +37 -0
- data/lib/cucumber/pickles/fill_in.rb +10 -0
- data/lib/cucumber/pickles/helpers.rb +40 -0
- data/lib/cucumber/pickles/helpers/extensions/chrome/.DS_Store +0 -0
- data/lib/cucumber/pickles/helpers/extensions/chrome/compiled.crx.base64 +1 -0
- data/lib/cucumber/pickles/helpers/extensions/chrome/manifest.json +15 -0
- data/lib/cucumber/pickles/helpers/extensions/chrome/src/.DS_Store +0 -0
- data/lib/cucumber/pickles/helpers/extensions/chrome/src/inject/inject.js +35 -0
- data/lib/cucumber/pickles/helpers/main.rb +88 -0
- data/lib/cucumber/pickles/helpers/node_finders.rb +125 -0
- data/lib/cucumber/pickles/helpers/regex.rb +6 -0
- data/lib/cucumber/pickles/helpers/waiter.rb +152 -0
- data/lib/cucumber/pickles/locator/equal.rb +26 -0
- data/lib/cucumber/pickles/locator/index.rb +20 -0
- data/lib/cucumber/pickles/refinements.rb +49 -0
- data/lib/cucumber/pickles/steps.rb +73 -0
- data/lib/cucumber/pickles/steps/can_see.rb +70 -0
- data/lib/cucumber/pickles/steps/check.rb +55 -0
- data/lib/cucumber/pickles/steps/check_in/complex_input.rb +17 -0
- data/lib/cucumber/pickles/steps/check_in/factory.rb +26 -0
- data/lib/cucumber/pickles/steps/check_in/input.rb +36 -0
- data/lib/cucumber/pickles/steps/check_in/text.rb +17 -0
- data/lib/cucumber/pickles/steps/click.rb +91 -0
- data/lib/cucumber/pickles/steps/fill.rb +72 -0
- data/lib/cucumber/pickles/steps/fill_in/complex_input.rb +19 -0
- data/lib/cucumber/pickles/steps/fill_in/factory.rb +25 -0
- data/lib/cucumber/pickles/steps/fill_in/input.rb +29 -0
- data/lib/cucumber/pickles/steps/fill_in/select.rb +30 -0
- data/lib/cucumber/pickles/steps/redirect.rb +3 -0
- data/lib/cucumber/pickles/transform.rb +13 -0
- data/lib/cucumber/pickles/version.rb +3 -0
- data/lib/pickles.rb +3 -0
- data/pickles.gemspec +36 -0
- data/spec/helpers/node_finders_spec.rb +155 -0
- data/spec/helpers/waiter_spec.rb +41 -0
- data/spec/locator_spec.rb +31 -0
- data/spec/spec_helper.rb +32 -0
- data/spec/step_def_spec.rb +0 -0
- data/spec/steps/check_in/factory_spec.rb +52 -0
- data/spec/steps/fill_in/factory_spec.rb +51 -0
- metadata +153 -0
@@ -0,0 +1,26 @@
|
|
1
|
+
module Locator::Equal
|
2
|
+
|
3
|
+
EQUAL_REGEX = /\A([^\p{L}]*)(=)(.*)\Z/
|
4
|
+
|
5
|
+
module_function
|
6
|
+
|
7
|
+
def execute(locator)
|
8
|
+
matches = EQUAL_REGEX.match(locator)
|
9
|
+
|
10
|
+
if matches
|
11
|
+
captures = matches.captures
|
12
|
+
locator = "#{captures[0]}#{captures[2]}"
|
13
|
+
xpath = ".//*[text()='#{locator}']"
|
14
|
+
|
15
|
+
[locator, xpath]
|
16
|
+
else
|
17
|
+
#
|
18
|
+
# find node that contains *text* and does not have any child nodes that contain *text*
|
19
|
+
#
|
20
|
+
xpath = ".//*[contains(., '#{locator}')][not(*[contains(., '#{locator}')])]"
|
21
|
+
|
22
|
+
[locator, xpath]
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Locator::Index
|
2
|
+
|
3
|
+
INDEX_REGEX = /(.*)?\[(.*)\]\s*$/
|
4
|
+
|
5
|
+
module_function
|
6
|
+
|
7
|
+
def execute(locator)
|
8
|
+
return [nil, nil] if locator.nil?
|
9
|
+
|
10
|
+
matches = INDEX_REGEX.match(locator)
|
11
|
+
|
12
|
+
return [locator, nil] unless matches
|
13
|
+
|
14
|
+
text = matches.captures[0]
|
15
|
+
index = matches.captures[1].to_i
|
16
|
+
|
17
|
+
[text, index]
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module HashSymbolizeKeys
|
2
|
+
|
3
|
+
refine Hash do
|
4
|
+
def symbolize_keys
|
5
|
+
res = {}
|
6
|
+
|
7
|
+
each_key do |key|
|
8
|
+
res[key.to_sym] = self[key]
|
9
|
+
end
|
10
|
+
|
11
|
+
res
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
|
17
|
+
module BlankMethod
|
18
|
+
|
19
|
+
refine String do
|
20
|
+
def blank?
|
21
|
+
self == ""
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
refine NilClass do
|
26
|
+
def blank?
|
27
|
+
true
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
refine Array do
|
32
|
+
def blank?
|
33
|
+
self == []
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
refine Hash do
|
38
|
+
def blank?
|
39
|
+
self == {}
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
refine Symbol do
|
44
|
+
def blank?
|
45
|
+
self == :''
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
# cucumber transforms
|
2
|
+
_folder = 'cucumber/pickles/'
|
3
|
+
|
4
|
+
require _folder + 'transform'
|
5
|
+
require _folder + 'fill_in'
|
6
|
+
require _folder + 'check_in'
|
7
|
+
|
8
|
+
_steps_folder = _folder + 'steps/'
|
9
|
+
|
10
|
+
module Pickles::StepDef
|
11
|
+
|
12
|
+
module_function
|
13
|
+
|
14
|
+
def define_table_step(allowed_table_cols)
|
15
|
+
-> do |within, table|
|
16
|
+
|
17
|
+
Waiter.wait_for_ajax
|
18
|
+
|
19
|
+
if table.headers.length == 3 && 3.in?(allowed_table_cols)
|
20
|
+
current_within = within
|
21
|
+
|
22
|
+
rows = table.rows.unshift(table.headers)
|
23
|
+
|
24
|
+
rows.each do |(within, label, value)|
|
25
|
+
case within
|
26
|
+
when Helpers::Regex::WITHIN
|
27
|
+
current_within = Pickles.detect_node($1, $2, within: within)
|
28
|
+
when "-"
|
29
|
+
current_within = within
|
30
|
+
end
|
31
|
+
|
32
|
+
if label['pry']
|
33
|
+
label['pry'] = ''
|
34
|
+
|
35
|
+
pry binding
|
36
|
+
end
|
37
|
+
|
38
|
+
yield(label, value, current_within)
|
39
|
+
end
|
40
|
+
elsif table.headers.length == 2 && 2.in?(allowed_table_cols)
|
41
|
+
table.rows_hash.each do |label, value|
|
42
|
+
if label['pry']
|
43
|
+
label['pry'] = ''
|
44
|
+
|
45
|
+
pry binding
|
46
|
+
end
|
47
|
+
|
48
|
+
yield(label, value, within)
|
49
|
+
end
|
50
|
+
elsif table.headers.length == 1 && 1.in?(allowed_table_cols)
|
51
|
+
table.each do |label|
|
52
|
+
if label['pry']
|
53
|
+
label['pry'] = ''
|
54
|
+
|
55
|
+
pry binding
|
56
|
+
end
|
57
|
+
|
58
|
+
yield(label, nil, within)
|
59
|
+
end
|
60
|
+
else
|
61
|
+
raise(ArgumentError, "Unsupported table format. Must contain #{allowed_table_cols.join(' || ')} cols")
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
68
|
+
|
69
|
+
require _steps_folder + 'fill'
|
70
|
+
require _steps_folder + 'check'
|
71
|
+
require _steps_folder + 'can_see'
|
72
|
+
require _steps_folder + 'redirect'
|
73
|
+
require _steps_folder + 'click'
|
@@ -0,0 +1,70 @@
|
|
1
|
+
#
|
2
|
+
# Then I can see:
|
3
|
+
# | Account Number | 5002 |
|
4
|
+
# | body | 2009-11-01 |
|
5
|
+
# | .note | Nice guy |
|
6
|
+
#
|
7
|
+
|
8
|
+
Then(/^I can(not)? see:$/) do |is_not, table|
|
9
|
+
if is_not
|
10
|
+
check = -> within, content {
|
11
|
+
within = within.strip.present? ? find_node(within) : page
|
12
|
+
|
13
|
+
expect(within).not_to have_content(content)
|
14
|
+
}
|
15
|
+
else
|
16
|
+
check = -> within, content {
|
17
|
+
within = within.strip.present? ? find_node(within) : page
|
18
|
+
|
19
|
+
expect(within).to have_content(content)
|
20
|
+
}
|
21
|
+
end
|
22
|
+
|
23
|
+
case table.headers.length
|
24
|
+
when 1
|
25
|
+
check = check.curry['']
|
26
|
+
|
27
|
+
table.raw.flatten.each(&check)
|
28
|
+
when 2
|
29
|
+
table.rows_hash.each(&check)
|
30
|
+
else
|
31
|
+
raise ArgumentError, "Unsupported table format"
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
|
36
|
+
And(/^I can(not)? see video (".*?")( within (?:.*))?$/) do |is_not, video_src, within|
|
37
|
+
within ||= page
|
38
|
+
if is_not
|
39
|
+
expect(within).not_to have_selector("iframe[src=#{video_src}]")
|
40
|
+
else
|
41
|
+
expect(within).to have_selector("iframe[src=#{video_src}]")
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
And(/^I can(not)? see image (".*?")( within (?:.*))?$/) do |is_not, image_src, within|
|
46
|
+
within ||= page
|
47
|
+
if is_not
|
48
|
+
expect(within).not_to have_selector("img[src=#{image_src}]")
|
49
|
+
else
|
50
|
+
expect(within).to have_selector("img[src=#{image_src}]")
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
# Then /^focus is on "(.*?)"$/ do |locator|
|
55
|
+
# node = find_node(locator, within: page)
|
56
|
+
#
|
57
|
+
# begin
|
58
|
+
# block_scroll = page.evaluate_script("arguments[0].offsetTop", node)
|
59
|
+
# rescue
|
60
|
+
# raise ArgumentError, "Element #{locator} does not exist on page"
|
61
|
+
# end
|
62
|
+
#
|
63
|
+
# window_height = page.evaluate_script('window.innerHeight') / 2
|
64
|
+
#
|
65
|
+
# synchronize do
|
66
|
+
# scrolled = page.evaluate_script('document.body.scrollTop')
|
67
|
+
#
|
68
|
+
# (scrolled <= block_scroll && block_scroll <= scrolled + window_height) || raise(Capybara::ElementNotFound)
|
69
|
+
# end
|
70
|
+
# end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# Use this to fill in an entire form with data from a table. Example:
|
2
|
+
#
|
3
|
+
# Then fields are filled with:
|
4
|
+
# | Account Number | 5002 |
|
5
|
+
# | Expiry date | 2009-11-01 |
|
6
|
+
# | Note | Nice guy |
|
7
|
+
# | Wants Email? | true |
|
8
|
+
# | Sex | Male |
|
9
|
+
# | Accept user agrement | true |
|
10
|
+
# | Send me letters | false |
|
11
|
+
# | radio 1 | true |
|
12
|
+
# | Avatar | avatar.png |
|
13
|
+
# | Due date | 12:35 |
|
14
|
+
Then(/^fields are filled with:( within (?:.*))?$/,
|
15
|
+
&Pickles::StepDef.define_table_step([2, 3]) do |label, value, within|
|
16
|
+
|
17
|
+
CheckIn::Factory.new(label, value, within: within).call.call
|
18
|
+
|
19
|
+
end)
|
20
|
+
|
21
|
+
# do |within, fields|
|
22
|
+
#
|
23
|
+
# Waiter.wait_for_ajax
|
24
|
+
#
|
25
|
+
# if fields.headers.length == 3
|
26
|
+
# current_within = within
|
27
|
+
#
|
28
|
+
# rows = fields.rows.unshift(fields.headers)
|
29
|
+
#
|
30
|
+
# rows.each do |(within, label, value)|
|
31
|
+
# case within
|
32
|
+
# when /\A(.+?)(?: "(.*)")?\Z/
|
33
|
+
# current_within = Pickles.detect_node($1, $2, within)
|
34
|
+
# when "-"
|
35
|
+
# current_within = within
|
36
|
+
# end
|
37
|
+
#
|
38
|
+
# if label['pry']
|
39
|
+
# label['pry'] = ''
|
40
|
+
#
|
41
|
+
# pry binding
|
42
|
+
# end
|
43
|
+
#
|
44
|
+
# CheckIn::Factory.new(label, value, within: current_within).call.call
|
45
|
+
# end
|
46
|
+
# elsif fields.headers.length == 2
|
47
|
+
# fields.rows_hash.each do |label, value|
|
48
|
+
# pry binding if label['pry']
|
49
|
+
# CheckIn::Factory.new(label, value, within: within).call.call
|
50
|
+
# end
|
51
|
+
# else
|
52
|
+
# raise(ArgumentError, 'Unsupported table type. Must contain 2 or 3 columns')
|
53
|
+
# end
|
54
|
+
#
|
55
|
+
# end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
class CheckIn::ComplexInput
|
2
|
+
|
3
|
+
def initialize(label, value, within)
|
4
|
+
@label = label
|
5
|
+
@value = value
|
6
|
+
@within = within || Capybara.current_session
|
7
|
+
end
|
8
|
+
|
9
|
+
def call
|
10
|
+
@value.split(/\s*:\s*/).each.with_index do |value, index|
|
11
|
+
input_locator = "#{label}[#{index}]"
|
12
|
+
|
13
|
+
CheckIn::Input.new(input_locator, value, @within)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
class CheckIn::Factory
|
2
|
+
|
3
|
+
TAG = /^(.+\S+)\s*\((.*)\)$/
|
4
|
+
|
5
|
+
def initialize(label, value, within: nil)
|
6
|
+
@label = label
|
7
|
+
@value = value
|
8
|
+
@within = within
|
9
|
+
end
|
10
|
+
|
11
|
+
def call
|
12
|
+
if !@value.nil? && @value[':']
|
13
|
+
step = CheckIn::ComplexInput
|
14
|
+
# return if text_complex_input(label, value, within)
|
15
|
+
elsif @label =~ TAG
|
16
|
+
@label = $1
|
17
|
+
tag = $2
|
18
|
+
step = Pickles.config.check_step_by_tag(tag) || CheckIn::Input
|
19
|
+
else
|
20
|
+
step = CheckIn::Input
|
21
|
+
end
|
22
|
+
|
23
|
+
step.new(@label, @value, @within)
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
class CheckIn::Input
|
2
|
+
|
3
|
+
include RSpec::Expectations
|
4
|
+
include RSpec::Matchers
|
5
|
+
|
6
|
+
def initialize(label, value, within)
|
7
|
+
@label = label
|
8
|
+
@value = value
|
9
|
+
@within = within || Capybara.current_session
|
10
|
+
end
|
11
|
+
|
12
|
+
def call
|
13
|
+
case input.native.attribute("type")
|
14
|
+
|
15
|
+
when "text"
|
16
|
+
expect(input.value).to eq @value
|
17
|
+
when "radio", "checkbox"
|
18
|
+
case @value
|
19
|
+
|
20
|
+
when "true", true
|
21
|
+
expect(input).to be_selected
|
22
|
+
when "false", false, nil
|
23
|
+
expect(input).not_to be_selected
|
24
|
+
end
|
25
|
+
when "file"
|
26
|
+
expect(page).to have_selector("[src$='#{@value}']")
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def input
|
33
|
+
@input ||= Pickles.find_input(@label, within: @within)
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
class CheckIn::Text
|
2
|
+
|
3
|
+
include RSpec::Expectations
|
4
|
+
include RSpec::Matchers
|
5
|
+
|
6
|
+
def initialize(label, value, within)
|
7
|
+
@label = label
|
8
|
+
@value = value
|
9
|
+
@within = within || Capybara.current_session
|
10
|
+
end
|
11
|
+
|
12
|
+
def call
|
13
|
+
expect(@within).to have_content(@value)
|
14
|
+
expect(@within).to have_content(@label)
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
def wait_flags(text)
|
2
|
+
if text.starts_with?('>')
|
3
|
+
text = text[1..-1]
|
4
|
+
|
5
|
+
Waiter.wait_for_ajax
|
6
|
+
|
7
|
+
js_wait = true
|
8
|
+
end
|
9
|
+
|
10
|
+
if text.ends_with?('>')
|
11
|
+
text = text.chomp('>')
|
12
|
+
|
13
|
+
ajax_wait = true
|
14
|
+
end
|
15
|
+
|
16
|
+
[js_wait, text, ajax_wait]
|
17
|
+
end
|
18
|
+
|
19
|
+
def trigger(text, event, within)
|
20
|
+
|
21
|
+
js_wait, text, ajax_wait = wait_flags(text)
|
22
|
+
|
23
|
+
if js_wait
|
24
|
+
Waiter.wait { Pickles.find_node(text, within: within).public_send(event) }
|
25
|
+
else
|
26
|
+
Pickles.find_node(text, within: within).public_send(event)
|
27
|
+
end
|
28
|
+
|
29
|
+
Waiter.wait_for_ajax if ajax_wait
|
30
|
+
|
31
|
+
end
|
32
|
+
|
33
|
+
#
|
34
|
+
# Use this to click anything anywhere:
|
35
|
+
#
|
36
|
+
# When I click "My button" - standard click by text
|
37
|
+
#
|
38
|
+
# When I click "=Mo" - click node that has exact this text. i.e. ignore: Monday, Moth
|
39
|
+
#
|
40
|
+
# When I click ">Mo" - ajax wait requests done before clicking
|
41
|
+
# When I click "Mo>" - ajax wait requests done after clicking
|
42
|
+
#
|
43
|
+
# When I click ">Mo>" - both of the above
|
44
|
+
#
|
45
|
+
# When I click "My button,=Mo" - chain clicks ( click My button then click exact Mo )
|
46
|
+
# When I click "My button->=Mo" - same as above (-> is for chaining sequential clicks)
|
47
|
+
#
|
48
|
+
# When I click "My button>->=Mo>" - click My button, ajax wait then click Mo
|
49
|
+
#
|
50
|
+
# etc.
|
51
|
+
#
|
52
|
+
When /^I (?:click|navigate) "([^"]*)"( within (?:.*))?$/ do |click_text, within|
|
53
|
+
click_text.split(/,|->/).each do |text|
|
54
|
+
pry binding if text['pry']
|
55
|
+
|
56
|
+
trigger(text, 'click', within)
|
57
|
+
end
|
58
|
+
|
59
|
+
Waiter.wait_for_ajax
|
60
|
+
end
|
61
|
+
|
62
|
+
#
|
63
|
+
# I navigate:
|
64
|
+
# | click | My button |
|
65
|
+
# | hover | My span |
|
66
|
+
# | hover | Your span |
|
67
|
+
# | click | Your button |
|
68
|
+
#
|
69
|
+
When /^I (?:click|navigate):( within (?:.*))?$/ do |within, table|
|
70
|
+
do_click = -> (event, text) do
|
71
|
+
pry binding if text['pry']
|
72
|
+
event = 'click' if event.strip.blank?
|
73
|
+
|
74
|
+
trigger(text, event, within)
|
75
|
+
end
|
76
|
+
|
77
|
+
case table.headers.length
|
78
|
+
when 1
|
79
|
+
event = 'click'
|
80
|
+
|
81
|
+
do_click = do_click.curry[event]
|
82
|
+
|
83
|
+
table.raw.flatten.each(&do_click)
|
84
|
+
when 2
|
85
|
+
table.rows_hash.each(&do_click)
|
86
|
+
else
|
87
|
+
raise ArgumentError, "Unsupported table format"
|
88
|
+
end
|
89
|
+
|
90
|
+
Waiter.wait_for_ajax
|
91
|
+
end
|