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